New functions to examine the contents of heap pages, as discussed
recently on -hackers. These are fully integrated into backend.

Designed to be extended for other page layouts/contents. (Heikki has
some similar functions for index pages).

Docs included, applies cleanly, tests good.

I'll maintain this with immediate fixes/additions as we go up to 8.3 and
beyond, to assist review process of various patches that alter page
contents.

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

Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/func.sgml,v
retrieving revision 1.369
diff -c -r1.369 func.sgml
*** doc/src/sgml/func.sgml	20 Feb 2007 19:59:04 -0000	1.369
--- doc/src/sgml/func.sgml	8 Mar 2007 15:34:26 -0000
***************
*** 10854,10859 ****
--- 10854,11064 ----
      at session end, even if the client disconnects ungracefully.)
     </para>
  
+    <table id="functions-diagnostic">
+     <title>Diagnostic Functions</title>
+     <tgroup cols="3">
+      <thead>
+       <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry>
+       </row>
+      </thead>
+ 
+      <tbody>
+       <row>
+        <entry>
+         <literal><function>heap_get_raw_page</function>(<parameter>RelName</> <type>text</>, <parameter>blockId</> <type>int</>)</literal>
+        </entry>
+        <entry><type>bytea(BLCKSZ)</type></entry>
+        <entry>Get binary copy of a shared buffer page</entry>
+       </row>
+ 
+       <row>
+        <entry>
+         <literal><function>raw_page_header</function>(<parameter>rawpage</> <type>bytea</>)</literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>Show page header fields of a raw page</entry>
+       </row>
+ 
+       <row>
+        <entry>
+         <literal><function>raw_page_heap_tuple_headers</function>(<parameter>rawpage</> <type>bytea</>)</literal>
+        </entry>
+        <entry><type>setof record</type></entry>
+        <entry>Show all tuple headers on heap raw page</entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
+ 
+    <indexterm zone="functions-admin">
+     <primary>heap_get_raw_page</primary>
+    </indexterm>
+    <para>
+     <function>heap_get_raw_page</> reads one block of the named table and
+ 	returns a copy as a bytea field. This allows a single time-consistent
+ 	copy of the block to be made for diagnostic purposes. The block
+ 	is read from shared memory, so the block is already known Valid
+ 	when it is read in from disk. Use of this functions is restricted to
+ 	superusers.
+    </para>
+ 
+    <indexterm zone="functions-admin">
+     <primary>raw_page_header</primary>
+    </indexterm>
+    <para>
+     <function>raw_page_header</> shows fields which are common to all types
+ 	of PostgreSQL page, so can be used with both heap and index pages. Use of
+ 	this function is not restricted to superusers, though a raw page must be 
+ 	provided as input, which can only be gained by a superuser executing
+ 	<function>heap_get_raw_page</>.
+ <programlisting>
+ postgres=# select * from raw_page_header(heap_get_raw_page('foo',0));
+ 
+    lsn    | tli | pd_lower | pd_upper | pd_special | version
+ ----------+-----+----------+----------+------------+---------
+  0/47FC08 |   1 |      420 |     5392 |       8192 |       4
+ (1 row)
+ </programlisting>
+ 	The fields are defined in <filename>src/include/storage/bufpage.h</>
+    </para>
+ 
+    <indexterm zone="functions-admin">
+     <primary>raw_page_heap_tuple_headers</primary>
+    </indexterm>
+    <para>
+     <function>raw_page_heap_tuple_headers</> shows all line pointers on a heap page.
+ 	Most or all of these will be filled with tuples and, when present,
+ 	tuple headers are shown. All tuples are shown, whether or not
+ 	the tuples were visible to an MVCC snapshot at the time the raw page
+ 	was copied. Use of this function is not restricted to superusers, though
+ 	a raw page must be provided as input, which can only be gained by a 
+ 	superuser executing <function>heap_get_raw_page</>.
+ <programlisting>
+ postgres=# select * from raw_page_heap_tuple_headers(heap_get_raw_page('foo',0)) limit 1;
+ 
+  id  | ok | lpoff | len |  tctid  | xmn  | cb | xmx  | cx | cid | natts | toid | info  | hdrsz
+ -----+----+-------+-----+---------+------+----+------+----+-----+-------+------+-------+-------
+    1 | t  |  5420 |  28 | (0,1)   | 2223 | c  |    0 | i  |   0 |     1 |      | 10496 |    24
+    2 | t  |  5364 |  28 | (0,2)   | 2464 |    |    0 | i  |   0 |     1 |      | 10240 |    24
+    3 | f  |       |     |         |      |    |      |    |     |       |      |       |
+    4 | t  |  8164 |  28 | (0,2)   |  602 | c  | 2464 |    |   0 |     1 |      |   256 |    24
+    5 | t  |  8136 |  28 | (0,5)   |  602 | c  |    0 | i  |   0 |     1 |      |  2304 |    24
+ </programlisting>
+ 	The fields shown are defined in detail in 
+ 	<filename>src/include/storage/bufpage.h</>, some of which are formatted
+ 	for easier interpretation as shown in the table below.
+    </para>
+ 
+    <table id="functions-heap-tuple-headers">
+     <title>Heap Tuple Header Fields</title>
+     <tgroup cols="3">
+      <thead>
+       <row><entry>Field</entry> <entry>Type</entry> <entry>Description</entry>
+       </row>
+      </thead>
+ 
+      <tbody>
+       <row>
+        <entry>id</entry>
+        <entry><type>int</type></entry>
+        <entry>Line pointer</entry>
+       </row>
+       <row>
+        <entry>ok</entry>
+        <entry><type>boolean</type></entry>
+        <entry>is item and tuple valid?</entry>
+       </row>
+       <row>
+        <entry>lpoff</entry>
+        <entry><type>int</type></entry>
+        <entry>Byte offset within page to start of tuple</entry>
+       </row>
+       <row>
+        <entry>len</entry>
+        <entry><type>int</type></entry>
+        <entry>Length of tuple</entry>
+       </row>
+       <row>
+        <entry>tctid</entry>
+        <entry><type>text</type></entry>
+        <entry>tuple identifier (tid) in (blockid, itemid) format</entry>
+       </row>
+       <row>
+        <entry>xmn</entry>
+        <entry><type>int</type></entry>
+        <entry>xmin of tuple</entry>
+       </row>
+       <row>
+        <entry>cn</entry>
+        <entry><type>char(1)</type></entry>
+        <entry>xmn commit status: 
+ 			<literal>c</> = known committed,
+ 			<literal>i</> = known invalid,
+ 			<literal>blank</> = unknown
+ 		</entry>
+       </row>
+       <row>
+        <entry>xmx</entry>
+        <entry><type>int</type></entry>
+        <entry>xmax of tuple or 0 if not set</entry>
+       </row>
+       <row>
+        <entry>cx</entry>
+        <entry><type>char(1)</type></entry>
+        <entry>xmx status: 
+ 			<literal>c</> = known committed,
+ 			<literal>i</> = known invalid,
+ 			<literal>x</> = exclusive locked,
+ 			<literal>s</> = share locked,
+ 			<literal>m</> = multixact locked,
+ 			<literal>blank</> = unknown
+ 		</entry>
+       </row>
+       <row>
+        <entry>cid</entry>
+        <entry><type>int</type></entry>
+        <entry>raw CommandId or ComboId</entry>
+       </row>
+       <row>
+        <entry>natts</entry>
+        <entry><type>int</type></entry>
+        <entry>Number of attributes on this tuple</entry>
+       </row>
+       <row>
+        <entry>toid</entry>
+        <entry><type>int</type></entry>
+        <entry>Oid of tuple, or NULL if not present</entry>
+       </row>
+       <row>
+        <entry>info</entry>
+        <entry><type>int</type></entry>
+        <entry>Infomask bits. t_infomask2 bits are high order bits, t_infomask are low order</entry>
+       </row>
+       <row>
+        <entry>hdrsz</entry>
+        <entry><type>int</type></entry>
+        <entry>Tuple header size</entry>
+       </row>	
+      </tbody>
+     </tgroup>
+    </table>
+ 
+    <para>
+     The infomask field can be accessed using bitwise operators. Fields
+ 	from t_infomask can be accessed directly using the values shown in
+ 	<filename>src/include/storage/bufpage.h</>, as shown below:
+ <programlisting>
+ postgres=# select ((~info & 256) = 0) as HEAP_XMIN_COMMITTED from raw_page_heap_tuple_headers(heap_get_raw_page('foo',0)) limit 1;
+  heap_xmin_committed
+ ---------------------
+  t
+ (1 row)
+ </programlisting>
+ 	Values of infomask2 will be 65536 higher than shown in
+ 	<filename>src/include/storage/bufpage.h</>, since they have are
+ 	stored in the high order bits of the info result field.
+    </para>
+ 
    </sect1>
  
   <sect1 id="functions-xml">
***************
*** 11262,11268 ****
  ...
  ]]></screen>
  
!     If no table name is avaible, that is, when mapping a query or a
      cursor, the string <literal>table</literal> is used in the first
      format, <literal>row</literal> in the second format.
     </para>
--- 11467,11473 ----
  ...
  ]]></screen>
  
!     If no table name is available, that is, when mapping a query or a
      cursor, the string <literal>table</literal> is used in the first
      format, <literal>row</literal> in the second format.
     </para>
Index: src/backend/access/heap/heapam.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/access/heap/heapam.c,v
retrieving revision 1.228
diff -c -r1.228 heapam.c
*** src/backend/access/heap/heapam.c	9 Feb 2007 03:35:33 -0000	1.228
--- src/backend/access/heap/heapam.c	8 Mar 2007 15:34:30 -0000
***************
*** 39,44 ****
--- 39,45 ----
   */
  #include "postgres.h"
  
+ #include "funcapi.h"
  #include "access/heapam.h"
  #include "access/hio.h"
  #include "access/multixact.h"
***************
*** 48,57 ****
--- 49,61 ----
  #include "access/xact.h"
  #include "catalog/catalog.h"
  #include "catalog/namespace.h"
+ #include "catalog/pg_type.h"
  #include "miscadmin.h"
  #include "pgstat.h"
+ #include "storage/bufpage.h"
  #include "storage/procarray.h"
  #include "storage/smgr.h"
+ #include "utils/builtins.h"
  #include "utils/inval.h"
  #include "utils/lsyscache.h"
  #include "utils/relcache.h"
***************
*** 4008,4010 ****
--- 4012,4448 ----
  		smgrimmedsync(rel->rd_smgr);
  	}
  }
+ 
+ /* ----------------------------------------------------------------
+  *						Diagnostic functions
+  * ----------------------------------------------------------------
+  */
+ 
+ /*
+  * heap_get_raw_page
+  *
+  * reads a page from shared buffers into a bytea data blob
+  *
+  * This then allows that page to be analysed in various ways
+  * secure in the knowledge that this is a fully consistent
+  * snapshot of the page.
+  */
+ PG_FUNCTION_INFO_V1(heap_get_raw_page);
+ 
+ Datum
+ heap_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;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be superuser to get raw page"))));
+ 
+ 	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
+ 	rel = heap_openrv(relrv, AccessShareLock);
+ 
+ 	/* check that this relation has storage */
+ 	if (rel->rd_rel->relkind == RELKIND_VIEW)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 				 errmsg("cannot get raw page from view \"%s\"",
+ 							RelationGetRelationName(rel))));
+ 
+ 	if (blkno >= RelationGetNumberOfBlocks(rel))
+ 		elog(ERROR, "block number %u is out of range for relation \"%s\"",
+ 			 blkno, RelationGetRelationName(rel));
+ 
+ 	/* work on relation */
+ 	{
+ 		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);
+ }
+ 
+ 
+ struct page_item_srf_args
+ {
+ 	TupleDesc	tupd;
+ 	Page		page;
+ 	uint16		offset;
+ };
+ 
+ /*
+  * raw_page_heap_tuple_headers
+  *
+  * Allow inspection of heap page tuple header fields of a raw page
+  *
+  * raw page must be valid
+  */
+ PG_FUNCTION_INFO_V1(raw_page_heap_tuple_headers);
+ 
+ 
+ #define	TUPLE_HEADER_ID				0
+ #define	TUPLE_HEADER_OK				1
+ #define TUPLE_HEADER_LPOFF			2
+ #define	TUPLE_HEADER_LEN			3
+ #define	TUPLE_HEADER_TCTID			4
+ #define	TUPLE_HEADER_XMN			5
+ #define	TUPLE_HEADER_CN				6
+ #define	TUPLE_HEADER_XMX			7
+ #define	TUPLE_HEADER_CX				8
+ #define	TUPLE_HEADER_CID			9
+ #define	TUPLE_HEADER_NATTS			10
+ #define	TUPLE_HEADER_TOID			11
+ #define	TUPLE_HEADER_INFO			12
+ #define	TUPLE_HEADER_HDRSZ			13
+ #define	TUPLE_HEADER_NUM_FIELDS		14
+ 
+ #define GET_TEXT(str_) \
+         DirectFunctionCall1(textin, CStringGetDatum(str_))
+ 
+ Datum
+ raw_page_heap_tuple_headers(PG_FUNCTION_ARGS)
+ {
+ 	bytea	   *raw_page = PG_GETARG_BYTEA_P(0);
+ 
+ 	FuncCallContext *fctx;
+ 	MemoryContext mctx;
+ 	struct page_item_srf_args *inter_call_data = NULL;
+ 
+ 	validate_raw_page(raw_page);
+ 
+ 	if (SRF_IS_FIRSTCALL())
+ 	{
+ 		TupleDesc	resultTupleDesc;
+ 
+ 		fctx = SRF_FIRSTCALL_INIT();
+ 		mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
+ 
+ 		inter_call_data = palloc(sizeof(struct page_item_srf_args));
+ 
+ 		/*
+ 		 * Construct a tuple descriptor for the result row.  This must match this
+ 		 * function's pg_proc entry!
+ 		 */
+ 		resultTupleDesc = CreateTemplateTupleDesc(TUPLE_HEADER_NUM_FIELDS, false);
+ 
+ 		TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "id",
+ 						   INT2OID, -1, 0);
+ 		TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "ok",
+ 						   BOOLOID, -1, 0);
+ 		TupleDescInitEntry(resultTupleDesc, (AttrNumber) 3, "lpoff",
+ 						   INT2OID, -1, 0);
+ 		TupleDescInitEntry(resultTupleDesc, (AttrNumber) 4, "len",
+ 						   INT2OID, -1, 0);
+ 		TupleDescInitEntry(resultTupleDesc, (AttrNumber) 5, "tctid",
+ 						   TEXTOID, -1, 0);
+ 		TupleDescInitEntry(resultTupleDesc, (AttrNumber) 6, "xmn",
+ 						   INT4OID, -1, 0);
+ 		TupleDescInitEntry(resultTupleDesc, (AttrNumber) 7, "cn",
+ 						   TEXTOID, -1, 0);
+ 		TupleDescInitEntry(resultTupleDesc, (AttrNumber) 8, "xmx",
+ 						   INT4OID, -1, 0);
+ 		TupleDescInitEntry(resultTupleDesc, (AttrNumber) 9, "cx",
+ 						   TEXTOID, -1, 0);
+ 		TupleDescInitEntry(resultTupleDesc, (AttrNumber) 10, "cid",
+ 						   INT4OID, -1, 0);
+ 		TupleDescInitEntry(resultTupleDesc, (AttrNumber) 11, "natts",
+ 						   INT2OID, -1, 0);
+ 		TupleDescInitEntry(resultTupleDesc, (AttrNumber) 12, "toid",
+ 						   INT4OID, -1, 0);
+ 		TupleDescInitEntry(resultTupleDesc, (AttrNumber) 13, "info",
+ 						   INT4OID, -1, 0);
+ 		TupleDescInitEntry(resultTupleDesc, (AttrNumber) 14, "hdrsz",
+ 						   INT2OID, -1, 0);
+ 
+ 		inter_call_data->tupd = BlessTupleDesc(resultTupleDesc);
+ 
+ 		inter_call_data->offset = FirstOffsetNumber;
+ 
+ 		inter_call_data->page = VARDATA(raw_page);
+ 
+ 		fctx->max_calls = PageGetMaxOffsetNumber(inter_call_data->page);
+ 		fctx->user_fctx = inter_call_data;
+ 
+ 		MemoryContextSwitchTo(mctx);
+ 	}
+ 
+ 	fctx = SRF_PERCALL_SETUP();
+ 	inter_call_data = fctx->user_fctx;
+ 
+ 	if (fctx->call_cntr < fctx->max_calls)
+ 	{
+ 		HeapTuple	tuple;
+ 		Datum		result;
+ 		ItemId		id;
+ 		bool		id_IsValid;
+         Datum		values[TUPLE_HEADER_NUM_FIELDS];
+         bool		nulls[TUPLE_HEADER_NUM_FIELDS];
+ 
+ 		id = PageGetItemId(inter_call_data->page, inter_call_data->offset);
+ 
+ 		id_IsValid = ItemIdIsValid(id);
+ 
+ 		if (id_IsValid)
+ 		{
+ 			HeapTupleData 		tup;
+ 			HeapTupleHeader		tuphdr;
+ 			char				t_ctid[20];
+ 			BlockNumber			blkno;
+ 			uint32				infomask;
+ 
+ 			tuphdr = (HeapTupleHeader) PageGetItem((Page) inter_call_data->page, id);
+ 			tup.t_len = ItemIdGetLength(id);
+ 
+ 			values[TUPLE_HEADER_ID] = UInt32GetDatum(inter_call_data->offset);
+ 			nulls[TUPLE_HEADER_ID] = false;
+ 			if (tup.t_len > 0)
+ 			{
+ 				values[TUPLE_HEADER_OK] = BoolGetDatum(true);
+ 				nulls[TUPLE_HEADER_OK] = false;
+ 
+ 				values[TUPLE_HEADER_LPOFF] = UInt16GetDatum(ItemIdGetOffset(id));
+ 				nulls[TUPLE_HEADER_LPOFF] = false;
+ 
+ 				values[TUPLE_HEADER_LEN] = UInt32GetDatum(tup.t_len);
+ 				nulls[TUPLE_HEADER_LEN] = false;
+ 
+ 				blkno = BlockIdGetBlockNumber(&(tuphdr->t_ctid.ip_blkid));
+ 				snprintf(t_ctid, sizeof(t_ctid), "(%u,%u)", blkno, tuphdr->t_ctid.ip_posid);
+ 				values[TUPLE_HEADER_TCTID] = GET_TEXT(t_ctid);
+ 				nulls[TUPLE_HEADER_TCTID] = false;
+ 
+ 				values[TUPLE_HEADER_XMN] = UInt32GetDatum(HeapTupleHeaderGetXmin(tuphdr));
+ 				nulls[TUPLE_HEADER_XMN] = false;
+ 
+ 				if (tuphdr->t_infomask & HEAP_XMIN_COMMITTED) 
+ 						values[TUPLE_HEADER_CN] = GET_TEXT("c");
+ 				else if (tuphdr->t_infomask & HEAP_XMIN_INVALID)
+ 						values[TUPLE_HEADER_CN] = GET_TEXT("i");
+ 				else
+ 						values[TUPLE_HEADER_CN] = GET_TEXT(" ");	/* blank, rather than NULL */
+ 				nulls[TUPLE_HEADER_CN] = false;
+ 
+ 				values[TUPLE_HEADER_XMX] = UInt32GetDatum(HeapTupleHeaderGetXmax(tuphdr));
+ 				nulls[TUPLE_HEADER_XMX] = false;
+ 
+ 				if (tuphdr->t_infomask & HEAP_XMAX_COMMITTED) 
+ 						values[TUPLE_HEADER_CX] = GET_TEXT("c");
+ 				else if (tuphdr->t_infomask & HEAP_XMAX_INVALID)
+ 						values[TUPLE_HEADER_CX] = GET_TEXT("i");
+ 				else if (tuphdr->t_infomask & HEAP_XMAX_IS_MULTI)
+ 						values[TUPLE_HEADER_CX] = GET_TEXT("m");
+ 				else if (tuphdr->t_infomask & HEAP_XMAX_EXCL_LOCK)
+ 						values[TUPLE_HEADER_CX] = GET_TEXT("x");
+ 				else if (tuphdr->t_infomask & HEAP_XMAX_SHARED_LOCK)
+ 						values[TUPLE_HEADER_CX] = GET_TEXT("s");
+ 				else
+ 						values[TUPLE_HEADER_CX] = GET_TEXT(" ");	/* blank, rather than NULL */
+ 				nulls[TUPLE_HEADER_CX] = false;
+ 
+ 				values[TUPLE_HEADER_CID] = UInt32GetDatum(HeapTupleHeaderGetRawCommandId(tuphdr));
+ 				nulls[TUPLE_HEADER_CID] = false;
+ 
+ 				values[TUPLE_HEADER_NATTS] = UInt16GetDatum(HeapTupleHeaderGetNatts(tuphdr));
+ 				nulls[TUPLE_HEADER_NATTS] = false;
+ 
+ 				values[TUPLE_HEADER_TOID] = UInt32GetDatum(HeapTupleHeaderGetOid(tuphdr));
+ 				if (values[TUPLE_HEADER_TOID] != InvalidOid) 
+ 					nulls[TUPLE_HEADER_TOID] = false;
+ 				else
+ 					nulls[TUPLE_HEADER_TOID] = true;
+ 
+ 				infomask = (uint32) tuphdr->t_infomask2;
+ 				infomask = infomask << 16;
+ 				infomask = infomask | ((uint32) tuphdr->t_infomask);
+ 				values[TUPLE_HEADER_INFO] = UInt16GetDatum(infomask);
+ 				nulls[TUPLE_HEADER_INFO] = false;
+ 
+ 				values[TUPLE_HEADER_HDRSZ] = UInt8GetDatum(tuphdr->t_hoff);
+ 				nulls[TUPLE_HEADER_HDRSZ] = false;
+ 			}
+ 			else
+ 			{
+ 				/* ItemId is valid, but tuple is zero length so show as invalid */
+ 				values[TUPLE_HEADER_OK] = BoolGetDatum(false);
+ 				nulls[TUPLE_HEADER_OK] = false;
+ 
+ 				/* Remainder are NULL */
+ 				nulls[TUPLE_HEADER_LPOFF] = true;
+ 				nulls[TUPLE_HEADER_LEN] = true;
+ 				nulls[TUPLE_HEADER_TCTID] = true;
+ 				nulls[TUPLE_HEADER_XMN] = true;
+ 				nulls[TUPLE_HEADER_CN] = true;
+ 				nulls[TUPLE_HEADER_XMX] = true;
+ 				nulls[TUPLE_HEADER_CX] = true;
+ 				nulls[TUPLE_HEADER_CID] = true;
+ 				nulls[TUPLE_HEADER_NATTS] = true;
+ 				nulls[TUPLE_HEADER_TOID] = true;
+ 				nulls[TUPLE_HEADER_INFO] = true;
+ 				nulls[TUPLE_HEADER_HDRSZ] = true;
+ 			}
+ 		}
+ 		else
+ 		{
+ 			values[TUPLE_HEADER_ID] = UInt32GetDatum(inter_call_data->offset);
+ 			nulls[TUPLE_HEADER_ID] = false;
+ 
+ 			/* ItemId is invalid */
+ 			values[TUPLE_HEADER_OK] = BoolGetDatum(false);
+ 			nulls[TUPLE_HEADER_OK] = false;
+ 
+ 			/* Remainder are NULL */
+ 			nulls[TUPLE_HEADER_LPOFF] = true;
+ 			nulls[TUPLE_HEADER_LEN] = true;
+ 			nulls[TUPLE_HEADER_TCTID] = true;
+ 			nulls[TUPLE_HEADER_XMN] = true;
+ 			nulls[TUPLE_HEADER_CN] = true;
+ 			nulls[TUPLE_HEADER_XMX] = true;
+ 			nulls[TUPLE_HEADER_CX] = true;
+ 			nulls[TUPLE_HEADER_CID] = true;
+ 			nulls[TUPLE_HEADER_NATTS] = true;
+ 			nulls[TUPLE_HEADER_TOID] = true;
+ 			nulls[TUPLE_HEADER_INFO] = true;
+ 			nulls[TUPLE_HEADER_HDRSZ] = true;
+ 		}
+ 
+         /* Build and return the tuple. */
+         tuple = heap_form_tuple(inter_call_data->tupd, values, nulls);
+         result = HeapTupleGetDatum(tuple);
+ 
+ 		inter_call_data->offset += 1;
+ 
+ 		SRF_RETURN_NEXT(fctx, result);
+ 	}
+ 	else
+ 		SRF_RETURN_DONE(fctx);
+ }
+ 
+ /* ----------------------------------------------------------------
+  *						Generic page diagnostic functions
+  * ----------------------------------------------------------------
+  */
+ 
+ /*
+  * validate_raw_page
+  *
+  * validates a raw page - shouldnt be necessary, but somebody may try and
+  * run the raw page functions on arbitrary data, so all raw page
+  * functions should call this to ensure they have good input
+  */
+ 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);
+ }
+ 
+ /*
+  * raw_page_header
+  *
+  * Allow inspection of page header fields of a raw page
+  *
+  * raw page must be valid
+  */
+ 
+ #define	  PAGE_HEADER_LSN				0
+ #define	  PAGE_HEADER_TLI				1
+ #define	  PAGE_HEADER_OFFSET_LO			2
+ #define	  PAGE_HEADER_OFFSET_HI			3
+ #define	  PAGE_HEADER_OFFSET_SPECIAL	4
+ #define	  PAGE_HEADER_VERSION			5
+ #define	  PAGE_HEADER_NUM_FIELDS		6
+ 
+ PG_FUNCTION_INFO_V1(raw_page_header);
+ 
+ Datum
+ raw_page_header(PG_FUNCTION_ARGS)
+ {
+ 	bytea	   *raw_page = PG_GETARG_BYTEA_P(0);
+ 
+ 	Page		page;
+ 
+ 	Datum		values[PAGE_HEADER_NUM_FIELDS];
+ 	bool		nulls[PAGE_HEADER_NUM_FIELDS];
+ 
+ 	TupleDesc	resultTupleDesc;
+ 
+ 	HeapTuple	tuple;
+ 	Datum		result;
+ 	XLogRecPtr	lsn;
+ 	char		lsnchar[20];
+ 
+ 	validate_raw_page(raw_page);
+ 
+ 	page = VARDATA(raw_page);
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	resultTupleDesc = CreateTemplateTupleDesc(PAGE_HEADER_NUM_FIELDS, false);
+ 
+ 	TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "lsn",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "tli",
+ 					   INT4OID, -1, 0);
+   	TupleDescInitEntry(resultTupleDesc, (AttrNumber) 3, "pd_lower",
+ 					   INT2OID, -1, 0);
+ 	TupleDescInitEntry(resultTupleDesc, (AttrNumber) 4, "pd_upper",
+ 					   INT2OID, -1, 0);
+ 	TupleDescInitEntry(resultTupleDesc, (AttrNumber) 5, "pd_special",
+ 					   INT2OID, -1, 0);
+ 	TupleDescInitEntry(resultTupleDesc, (AttrNumber) 6, "version",
+ 					   INT2OID, -1, 0);
+ 
+ 	resultTupleDesc = BlessTupleDesc(resultTupleDesc);
+ 	
+ 	lsn = PageGetLSN(page);
+ 	snprintf(lsnchar, sizeof(lsnchar), "%X/%X", lsn.xlogid, lsn.xrecoff);
+ 
+ 	values[PAGE_HEADER_LSN] = DirectFunctionCall1(textin, CStringGetDatum(lsnchar));
+ 	values[PAGE_HEADER_TLI] = UInt32GetDatum(PageGetTLI(page));
+ 	values[PAGE_HEADER_OFFSET_LO] = UInt16GetDatum(((PageHeader) (page))->pd_lower);
+ 	values[PAGE_HEADER_OFFSET_HI] = UInt16GetDatum(((PageHeader) (page))->pd_upper);
+ 	values[PAGE_HEADER_OFFSET_SPECIAL] = UInt16GetDatum(((PageHeader) (page))->pd_special);
+ 	values[PAGE_HEADER_VERSION] = UInt16GetDatum(PageGetPageLayoutVersion(page));
+ 
+ 	nulls[PAGE_HEADER_LSN] = false;
+ 	nulls[PAGE_HEADER_TLI] = false;
+ 	nulls[PAGE_HEADER_OFFSET_LO] = false;
+ 	nulls[PAGE_HEADER_OFFSET_HI] = false;
+ 	nulls[PAGE_HEADER_OFFSET_SPECIAL] = false;
+ 	nulls[PAGE_HEADER_VERSION] = false;
+ 
+     /* Build and return the tuple. */
+     tuple = heap_form_tuple(resultTupleDesc, values, nulls);
+     result = HeapTupleGetDatum(tuple);
+ 
+ 	PG_RETURN_DATUM(result);
+ }
Index: src/include/access/heapam.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/access/heapam.h,v
retrieving revision 1.120
diff -c -r1.120 heapam.h
*** src/include/access/heapam.h	25 Jan 2007 02:17:26 -0000	1.120
--- src/include/access/heapam.h	8 Mar 2007 15:34:31 -0000
***************
*** 241,244 ****
--- 241,254 ----
  
  extern void heap_sync(Relation relation);
  
+ /* generic raw page functions */
+ extern void validate_raw_page(bytea	*raw_page);
+ extern Datum raw_page_header(PG_FUNCTION_ARGS);
+ 
+ /* raw page functions in other access methods */
+ 
+ /* heap */
+ extern Datum heap_get_raw_page(PG_FUNCTION_ARGS);
+ extern Datum raw_page_heap_tuple_headers(PG_FUNCTION_ARGS);
+ 
  #endif   /* HEAPAM_H */
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/catalog/pg_proc.h,v
retrieving revision 1.447
diff -c -r1.447 pg_proc.h
*** src/include/catalog/pg_proc.h	3 Mar 2007 19:52:46 -0000	1.447
--- src/include/catalog/pg_proc.h	8 Mar 2007 15:34:35 -0000
***************
*** 3178,3183 ****
--- 3178,3190 ----
  DATA(insert OID = 2851 ( pg_xlogfile_name			PGNSP PGUID 12 1 0 f f t f i 1 25 "25" _null_ _null_ _null_ pg_xlogfile_name - _null_ ));
  DESCR("xlog filename, given an xlog location");
  
+ DATA(insert OID = 2931 ( heap_get_raw_page			PGNSP PGUID 12 1 0 f f t f v 2 17 "25 23" _null_ _null_ _null_ heap_get_raw_page - _null_ ));
+ DESCR("Get a raw page from shared buffers");
+ DATA(insert OID = 2932 ( raw_page_header	PGNSP PGUID 12 1 100 f f t f i 1 2249 "17" "{17,25,23,21,21,21,21}" "{i,o,o,o,o,o,o}" "{rawpage,lsn,tli,pd_lower,pd_upper,pd_special,version}" raw_page_header - _null_ ));
+ DESCR("page header, given a raw page");
+ DATA(insert OID = 2933 ( raw_page_heap_tuple_headers	PGNSP PGUID 12 1 100 f f t t i 1 2249 "17" "{17,21,16,21,21,25,23,25,23,25,23,21,23,23,21}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{rawpage,id,ok,lpoff,len,tctid,xmn,cn,xmx,cx,cid,natts,toid,info,hdrsz}" raw_page_heap_tuple_headers - _null_ ));
+ DESCR("page header, given a raw page");
+ 
  DATA(insert OID = 2621 ( pg_reload_conf			PGNSP PGUID 12 1 0 f f t f v 0 16 "" _null_ _null_ _null_ pg_reload_conf - _null_ ));
  DESCR("Reload configuration files");
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 f f t f v 0 16 "" _null_ _null_ _null_ pg_rotate_logfile - _null_ ));
---------------------------(end of broadcast)---------------------------
TIP 4: Have you searched our list archives?

               http://archives.postgresql.org

Reply via email to