Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
/*
 * 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;
}
Ejemplo n.º 3
0
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);
}
Ejemplo n.º 4
0
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;
}