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