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