static bool parse_page(const DataPage *page, XLogRecPtr *lsn, uint16 *offset, uint16 *length) { const PageHeaderData *page_data = &page->page_data; /* Get lsn from page header */ *lsn = PageXLogRecPtrGet(page_data->pd_lsn); if (PageGetPageSize(page_data) == BLCKSZ && PageGetPageLayoutVersion(page_data) == PG_PAGE_LAYOUT_VERSION && (page_data->pd_flags & ~PD_VALID_FLAG_BITS) == 0 && page_data->pd_lower >= SizeOfPageHeaderData && page_data->pd_lower <= page_data->pd_upper && page_data->pd_upper <= page_data->pd_special && page_data->pd_special <= BLCKSZ && page_data->pd_special == MAXALIGN(page_data->pd_special) && !XLogRecPtrIsInvalid(*lsn)) { *offset = page_data->pd_lower; *length = page_data->pd_upper - page_data->pd_lower; return true; } *offset = *length = 0; return false; }
/* * PageHeaderIsValid * Check that the header fields of a page appear valid. * * This is called when a page has just been read in from disk. The idea is * to cheaply detect trashed pages before we go nuts following bogus item * pointers, testing invalid transaction identifiers, etc. * * It turns out to be necessary to allow zeroed pages here too. Even though * this routine is *not* called when deliberately adding a page to a relation, * there are scenarios in which a zeroed page might be found in a table. * (Example: a backend extends a relation, then crashes before it can write * any WAL entry about the new page. The kernel will already have the * zeroed page in the file, and it will stay that way after restart.) So we * allow zeroed pages here, and are careful that the page access macros * treat such a page as empty and without free space. Eventually, VACUUM * will clean up such a page and make it usable. */ bool PageHeaderIsValid(PageHeader page) { char *pagebytes; int i; /* Check normal case */ if (PageGetPageSize(page) == BLCKSZ && PageGetPageLayoutVersion(page) == PG_PAGE_LAYOUT_VERSION && (page->pd_flags & ~PD_VALID_FLAG_BITS) == 0 && page->pd_lower >= SizeOfPageHeaderData && page->pd_lower <= page->pd_upper && page->pd_upper <= page->pd_special && page->pd_special <= BLCKSZ && page->pd_special == MAXALIGN(page->pd_special)) return true; /* Check all-zeroes case */ pagebytes = (char *) page; for (i = 0; i < BLCKSZ; i++) { if (pagebytes[i] != 0) return false; } return true; }
Datum page_header(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); int raw_page_size; TupleDesc tupdesc; Datum result; HeapTuple tuple; Datum values[9]; bool nulls[9]; PageHeader page; XLogRecPtr lsn; char lsnchar[64]; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); raw_page_size = VARSIZE(raw_page) - VARHDRSZ; /* * Check that enough data was supplied, so that we don't try to access * fields outside the supplied buffer. */ if (raw_page_size < sizeof(PageHeaderData)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("input page too small (%d bytes)", raw_page_size))); page = (PageHeader) VARDATA(raw_page); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); /* Extract information from the page header */ lsn = PageGetLSN(page); snprintf(lsnchar, sizeof(lsnchar), "%X/%X", lsn.xlogid, lsn.xrecoff); values[0] = CStringGetTextDatum(lsnchar); values[1] = UInt16GetDatum(PageGetTLI(page)); values[2] = UInt16GetDatum(page->pd_flags); values[3] = UInt16GetDatum(page->pd_lower); values[4] = UInt16GetDatum(page->pd_upper); values[5] = UInt16GetDatum(page->pd_special); values[6] = UInt16GetDatum(PageGetPageSize(page)); values[7] = UInt16GetDatum(PageGetPageLayoutVersion(page)); values[8] = TransactionIdGetDatum(page->pd_prune_xid); /* Build and return the tuple. */ memset(nulls, 0, sizeof(nulls)); tuple = heap_form_tuple(tupdesc, values, nulls); result = HeapTupleGetDatum(tuple); PG_RETURN_DATUM(result); }
static const char * convert_gpdb4_heap_page(char *page) { VERSION4_PageHeaderData *oldhdr; PageHeader newhdr; OffsetNumber off; OffsetNumber maxoff; if (PageGetPageSize(page) != BLCKSZ) return "invalid block size on page"; /* Can only convert from GPDB4 format */ if (PageGetPageLayoutVersion(page) != 4) return "invalid page version"; oldhdr = (VERSION4_PageHeaderData *) page; /* Other checks that PageHeaderIsValid() normally performs */ if (!((oldhdr->pd_flags & ~PD_VALID_FLAG_BITS) == 0 && oldhdr->pd_lower >= SizeOfPageHeaderData && oldhdr->pd_lower <= oldhdr->pd_upper && oldhdr->pd_upper <= oldhdr->pd_special && oldhdr->pd_special <= BLCKSZ && oldhdr->pd_special == MAXALIGN(oldhdr->pd_special))) return "invalid header"; /* * Ok, it's a valid heap page, from GPDB4. We know how to convert that! */ /* 1. pd_prune_xid was added to page header */ /* 2. Line pointer flags were changed */ /* 3. HEAP_COMPRESSED flag was removed */ /* 4. On-disk representation of numerics was changed */ /* * Also, money datatype was widened from 32 to 64 bits. (pg_upgrade * should've refused the upgrade) */ /* * First, check if there is enough space on the page, after we expand the header. */ oldhdr = (VERSION4_PageHeaderData *) page; newhdr = (PageHeader) page; /* * If there isn't enough space on this page for the new header field, relocate a tuple */ make_room(page); /* * There is space. Move the line pointers. We also convert the line pointer flags * while we're at it. Begin from end to beginning, so that we don't overwrite items * we haven't processed yet. */ maxoff = (oldhdr->pd_lower - VERSION4_SizeOfPageHeaderData) / sizeof(ItemIdData); /* PageGetMaxOffsetNumber */ for (off = maxoff; off >= 1; off--) { ItemIdData iid = oldhdr->pd_linp[off - 1]; /* PageGetItemId */ if (iid.lp_flags == VERSION4_LP_UNUSED) iid.lp_flags = LP_UNUSED; else if (iid.lp_flags == VERSION4_LP_USED) iid.lp_flags = LP_NORMAL; else { /* LP_DELETE and LP_USED were never used on heap pages. */ return "unexpected LP_DELETE or LP_DEAD line pointer on old-format heap page"; } newhdr->pd_linp[off - 1] = iid; } newhdr->pd_lower = (char *) &newhdr->pd_linp[maxoff] - (char *) page; /* Initialize the field that was added after version 4 format */ newhdr->pd_prune_xid = 0; /* * Ok, the page header and line pointers are in the new format now. Mangle * the tuples themselves */ for (off = 1; off <= maxoff; off++) { ItemId iid = PageGetItemId(page, off); /* we can use PageGetItemId now */ HeapTupleHeader htup; if (!ItemIdIsNormal(iid)) continue; htup = (HeapTupleHeader) PageGetItem(page, iid); convert_heaptuple(htup); } /* * Finally, change the version number. */ PageSetPageSizeAndVersion(page, BLCKSZ, TARGET_PAGE_VERSION); return NULL; }