WIP patch for diagnostic/test functions for heap pages. (Linked to
discussion thread on -hackers "HOT - Whats Next?")

Specifically designed to allow test cases to be written that prove that
HOT works, as well as allowing diagnosis of general heap page content
errors.

Patch, plus additional file: /contrib/pgstattuple/pgstatheap.c

-- 
  Simon Riggs             
  EnterpriseDB   http://www.enterprisedb.com

Index: contrib/pgstattuple/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/pgstattuple/Makefile,v
retrieving revision 1.7
diff -c -r1.7 Makefile
*** contrib/pgstattuple/Makefile	19 Oct 2006 17:40:03 -0000	1.7
--- contrib/pgstattuple/Makefile	5 Mar 2007 15:47:59 -0000
***************
*** 7,13 ****
  #-------------------------------------------------------------------------
  
  MODULE_big	= pgstattuple
! OBJS		= pgstattuple.o pgstatindex.o
  DOCS		= README.pgstattuple README.pgstattuple.euc_jp
  DATA_built	= pgstattuple.sql
  DATA      	= uninstall_pgstattuple.sql
--- 7,13 ----
  #-------------------------------------------------------------------------
  
  MODULE_big	= pgstattuple
! OBJS		= pgstattuple.o pgstatindex.o pgstatheap.o
  DOCS		= README.pgstattuple README.pgstattuple.euc_jp
  DATA_built	= pgstattuple.sql
  DATA      	= uninstall_pgstattuple.sql
Index: contrib/pgstattuple/pgstattuple.sql.in
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/pgstattuple/pgstattuple.sql.in,v
retrieving revision 1.12
diff -c -r1.12 pgstattuple.sql.in
*** contrib/pgstattuple/pgstattuple.sql.in	4 Sep 2006 02:03:04 -0000	1.12
--- contrib/pgstattuple/pgstattuple.sql.in	5 Mar 2007 15:47:59 -0000
***************
*** 107,109 ****
--- 107,139 ----
  RETURNS int
  AS 'MODULE_PATHNAME', 'pg_relpages'
  LANGUAGE 'C' STRICT;
+ 
+ CREATE OR REPLACE FUNCTION bufpage_get_raw_page(text, int4)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'bufpage_get_raw_page'
+ LANGUAGE 'C' STRICT;
+ 
+ CREATE OR REPLACE FUNCTION heap_raw_page_header_is_valid(bytea)
+ RETURNS boolean
+ AS 'MODULE_PATHNAME', 'heap_raw_page_header_is_valid'
+ LANGUAGE 'C' STRICT;
+ 
+ CREATE TYPE heap_page_tuple_header_type AS (
+   itemid			int4,
+   ok				boolean,
+   len				int4,
+   tctid				text,
+   xmn				int4,
+   cn				char(1),
+   xmx				int4,
+   cx				char(1),
+   cid				int4,
+   natts				int2,
+   toid				int4,
+   info_flag_text	text
+ );
+ 
+ CREATE OR REPLACE FUNCTION heap_raw_page_tuple_headers(bytea)
+ RETURNS SETOF heap_page_tuple_header_type
+ AS 'MODULE_PATHNAME', 'heap_raw_page_tuple_headers'
+ LANGUAGE 'C' STRICT;
/*
 * pgstatheap
 *
 */

#include "postgres.h"

#include "fmgr.h"
#include "funcapi.h"
#include "access/heapam.h"
#include "access/transam.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "storage/bufpage.h"
#include "utils/builtins.h"
#include "utils/inval.h"

PG_FUNCTION_INFO_V1(bufpage_get_raw_page);
PG_FUNCTION_INFO_V1(heap_raw_page_header_is_valid);
PG_FUNCTION_INFO_V1(heap_raw_page_tuple_headers);

extern Datum bufpage_get_raw_page(PG_FUNCTION_ARGS);
extern Datum heap_raw_page_header_is_valid(PG_FUNCTION_ARGS);
extern Datum heap_raw_page_tuple_headers(PG_FUNCTION_ARGS);

static void validate_raw_page(bytea	*raw_page);

#define CHECK_RELATION_BLOCK_RANGE(rel, blkno) { \
		if ( (blkno)<0 && RelationGetNumberOfBlocks((rel))<=(blkno) ) \
			 elog(ERROR, "Block number out of range."); }

#define GET_TEXT(str_) \
        DirectFunctionCall1(textin, CStringGetDatum(str_))

Datum
bufpage_get_raw_page(PG_FUNCTION_ARGS)
{
	text	   *relname = PG_GETARG_TEXT_P(0);
	uint32		blkno = PG_GETARG_UINT32(1);

	Relation	rel;
	RangeVar	*relrv;
	bytea		*raw_page;
	char		*raw_page_data;
	Page		bufpage;
	Buffer		buf;

	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));

	rel = relation_openrv(relrv, AccessShareLock);

	/* work on relation */
	{
		CHECK_RELATION_BLOCK_RANGE(rel, blkno);

		buf = ReadBuffer(rel, blkno);

		/* work on buffer */
		{
			bufpage = BufferGetPage(buf);

			raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
			SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
			raw_page_data = VARDATA(raw_page);

			LockBuffer(buf, BUFFER_LOCK_SHARE);

			/* SnapshotEverything */
			{
				memcpy(raw_page_data, bufpage, BLCKSZ);
			}	
			LockBuffer(buf, BUFFER_LOCK_UNLOCK);
		}
		ReleaseBuffer(buf);
	}
	relation_close(rel, AccessShareLock);

	PG_RETURN_BYTEA_P(raw_page);
}

Datum
heap_raw_page_header_is_valid(PG_FUNCTION_ARGS)
{
	bytea	   *raw_page = PG_GETARG_BYTEA_P(0);
	bool		result;

	validate_raw_page(raw_page);
		
	result = PageHeaderIsValid((PageHeader)VARDATA(raw_page));

	PG_RETURN_BOOL(result);
}

static void
validate_raw_page(bytea	*raw_page)
{
	if ((VARSIZE(raw_page) - VARHDRSZ) != BLCKSZ)
		elog(ERROR,"Invalid raw_page: data length %d does not match BLCKSZ", 
						VARSIZE(raw_page) - VARHDRSZ);
}

struct user_args
{
	TupleDesc	tupd;
	Page		page;
	uint16		offset;
};

Datum
heap_raw_page_tuple_headers(PG_FUNCTION_ARGS)
{
	bytea	   *raw_page = PG_GETARG_BYTEA_P(0);

	HeapTuple	tuple;
	ItemId		id;
	Datum		result;

	FuncCallContext *fctx;
	MemoryContext mctx;
	struct user_args *uargs = NULL;

	validate_raw_page(raw_page);

	if (SRF_IS_FIRSTCALL())
	{
		fctx = SRF_FIRSTCALL_INIT();
		mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);

		uargs = palloc(sizeof(struct user_args));

		uargs->tupd = RelationNameGetTupleDesc("public.heap_page_tuple_header_type");
		uargs->offset = FirstOffsetNumber;

		uargs->page = VARDATA(raw_page);

		fctx->max_calls = PageGetMaxOffsetNumber(uargs->page);
		fctx->user_fctx = uargs;

		MemoryContextSwitchTo(mctx);
	}

	fctx = SRF_PERCALL_SETUP();
	uargs = fctx->user_fctx;

	if (fctx->call_cntr < fctx->max_calls)
	{
		bool		id_IsValid;
        Datum		values[12];
        bool		nulls[12];

		id = PageGetItemId(uargs->page, uargs->offset);

		id_IsValid = ItemIdIsValid(id);

		if (id_IsValid)
		{
			HeapTupleData 		tup;
			HeapTupleHeader		tuphdr;
			char				*t_ctid = palloc(16);
			BlockNumber			blkno;

			tuphdr = (HeapTupleHeader) PageGetItem((Page) uargs->page, id);
			tup.t_len = ItemIdGetLength(id);

			blkno = BlockIdGetBlockNumber(&(tuphdr->t_ctid.ip_blkid));

			snprintf(t_ctid, 16, "(%u,%u)", blkno, tuphdr->t_ctid.ip_posid);

			/* ItemOffset */
			values[0] = UInt32GetDatum(uargs->offset);
			nulls[0] = false;
			if (tup.t_len > 0)
			{
				/* Valid? */
				values[1] = BoolGetDatum(true);
				nulls[1] = false;
				/* Length */
				values[2] = UInt32GetDatum(tup.t_len);
				nulls[2] = false;
				/* t_ctid */
				values[3] = GET_TEXT(t_ctid);
				nulls[3] = false;
				/* t_xmin */
				values[4] = UInt32GetDatum(HeapTupleHeaderGetXmin(tuphdr));
				nulls[4] = false;
				/* cn */
				if (tuphdr->t_infomask & HEAP_XMIN_COMMITTED) 
					values[5] = GET_TEXT("c");
				else if (tuphdr->t_infomask & HEAP_XMIN_INVALID)
						values[5] = GET_TEXT("i");
				else
						values[5] = GET_TEXT(" ");
				nulls[5] = false;
				/* t_xmax */
				values[6] = UInt32GetDatum(HeapTupleHeaderGetXmax(tuphdr));
				nulls[6] = false;
				/* cx */
				if (tuphdr->t_infomask & HEAP_XMAX_COMMITTED) 
						values[7] = GET_TEXT("c");
				else if (tuphdr->t_infomask & HEAP_XMAX_INVALID)
						values[7] = GET_TEXT("i");
				else if (tuphdr->t_infomask & HEAP_XMAX_IS_MULTI)
						values[7] = GET_TEXT("m");
				else if (tuphdr->t_infomask & HEAP_XMAX_EXCL_LOCK)
						values[7] = GET_TEXT("x");
				else if (tuphdr->t_infomask & HEAP_XMAX_SHARED_LOCK)
						values[7] = GET_TEXT("s");
				else
						values[7] = GET_TEXT(" ");
				nulls[7] = false;
				/* t_cid */
				values[8] = UInt32GetDatum(HeapTupleHeaderGetRawCommandId(tuphdr));
				nulls[8] = false;
				/* natts */
				values[9] = UInt16GetDatum(HeapTupleHeaderGetNatts(tuphdr));
				nulls[9] = false;
				/* oid */
				values[10] = UInt32GetDatum(HeapTupleHeaderGetOid(tuphdr));
				nulls[10] = false;
				/* info_flag_text */
				if (tuphdr->t_infomask & HEAP_UPDATED) 
					values[11] = GET_TEXT("HEAP_UPDATED");
				else
					values[11] = GET_TEXT(" ");
				nulls[11] = false;
			}
			else
			{
				/* Valid? */
				values[1] = BoolGetDatum(false);
				nulls[1] = false;
				/* Length */
				nulls[2] = true;
				/* t_ctid */
				nulls[3] = true;
				/* t_xmin */
				nulls[4] = true;
				/* cn */
				nulls[5] = true;
				/* t_xmax */
				nulls[6] = true;
				/* cx */
				nulls[7] = true;
				/* t_cid */
				nulls[8] = true;
				/* natts */
				nulls[9] = true;
				/* oid */
				nulls[10] = true;
				/* info_flag_text */
				nulls[11] = true;
			}
		}
		else
		{
			/* ItemOffset */
			values[0] = UInt32GetDatum(uargs->offset);
			nulls[0] = false;
			/* Valid? */
			values[1] = BoolGetDatum(false);
			nulls[1] = false;
			/* Length */
			nulls[2] = true;
			/* t_ctid */
			nulls[3] = true;
			/* t_xmin */
			nulls[4] = true;
			/* cn */
			nulls[5] = true;
			/* t_xmax */
			nulls[6] = true;
			/* cx */
			nulls[7] = true;
			/* t_cid */
			nulls[8] = true;
			/* natts */
			nulls[9] = true;
			/* oid */
			nulls[10] = true;
			/* info_flag_text */
			nulls[11] = true;
		}

        /* Build and return the tuple. */
        tuple = heap_form_tuple(uargs->tupd, values, nulls);
        result = HeapTupleGetDatum(tuple);

		uargs->offset += 1;

		SRF_RETURN_NEXT(fctx, result);
	}
	else
		SRF_RETURN_DONE(fctx);
}
---------------------------(end of broadcast)---------------------------
TIP 9: In versions below 8.0, the planner will ignore your desire to
       choose an index scan if your joining column's datatypes do not
       match

Reply via email to