В письме от 3 августа 2015 15:35:23 пользователь Alvaro Herrera написал:
> Nikolay Shaplov wrote:
> > This patch adds several new functions, available from SQL queries. All
> > these functions are based on heap_page_items, but accept slightly
> > different arguments and has one additional column at the result set:
> > 
> > heap_page_tuples - accepts relation name, and bulkno, and returns usual
> > heap_page_items set with additional column that contain snapshot of tuple
> > data area stored in bytea.
> 
> I think the API around get_raw_page is more useful generally.  You might
> have table blocks stored in a bytea column for instance, because you
> extracted from some remote server and inserted into a local table for
> examination.  If you only accept relname/blkno, there's no way to
> examine data other than blocks directly in the database dir, which is
> limiting.
> 
> > There is also one strange function: _heap_page_items it is useless for
> > practical purposes. As heap_page_items it accepts page data as bytea, but
> > also get a relation name. It tries to apply tuple descriptor of that
> > relation to that page data.
> 
> For BRIN, I added something similar (brin_page_items), but it receives
> the index OID rather than name, and constructs a tuple descriptor to
> read the data.  I think OID is better than name generally.  (You can
> cast the relation name to regclass).
> 
> It's easy to misuse, but these functions are superuser-only, so there
> should be no security issue at least.  The possibility of a crash
> remains real, though, so maybe we should try to come up with a way to
> harden that.

I've done as you've said: Now patch adds two functions heap_page_item_attrs 
and heap_page_item_detoast_attrs and these function takes raw_page and 
relation OID as arguments. They also have third optional parameter that allows 
to change error mode from ERROR to WARNING.

Is it ok now?


-- 
Nikolay Shaplov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile
index aec5258..e4bc1af 100644
--- a/contrib/pageinspect/Makefile
+++ b/contrib/pageinspect/Makefile
@@ -5,7 +5,7 @@ OBJS		= rawpage.o heapfuncs.o btreefuncs.o fsmfuncs.o \
 		  brinfuncs.o ginfuncs.o $(WIN32RES)
 
 EXTENSION = pageinspect
-DATA = pageinspect--1.3.sql pageinspect--1.2--1.3.sql \
+DATA = pageinspect--1.4.sql pageinspect--1.3--1.4.sql pageinspect--1.2--1.3.sql \
 	pageinspect--1.1--1.2.sql pageinspect--1.0--1.1.sql \
 	pageinspect--unpackaged--1.0.sql
 PGFILEDESC = "pageinspect - functions to inspect contents of database pages"
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 8d1666c..59eae0f 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -29,7 +29,12 @@
 #include "funcapi.h"
 #include "utils/builtins.h"
 #include "miscadmin.h"
+#include "utils/array.h"
+#include "utils/rel.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
 
+#include "rawpage.h"
 
 /*
  * bits_to_text
@@ -53,6 +58,9 @@ bits_to_text(bits8 *bits, int len)
 	return str;
 }
 
+Datum heap_page_items_internal(FunctionCallInfo fcinfo, bytea *raw_page, Oid rel_oid, int error_level, bool do_detoast);
+void fill_header_data(FuncCallContext *fctx, Datum *values, bool *nulls, bool do_detoast);
+void split_tuple_data(FuncCallContext *fctx, HeapTupleHeader tuphdr, Datum *values, bool *nulls, bool do_detoast);
 
 /*
  * heap_page_items
@@ -66,38 +74,80 @@ typedef struct heap_page_items_state
 	TupleDesc	tupd;
 	Page		page;
 	uint16		offset;
+	TupleDesc	page_tuple_desc;
+	int		raw_page_size;
+	int		error_level;
 } heap_page_items_state;
 
 Datum
 heap_page_items(PG_FUNCTION_ARGS)
 {
 	bytea	   *raw_page = PG_GETARG_BYTEA_P(0);
+	return heap_page_items_internal(fcinfo, raw_page, InvalidOid, ERROR, false);
+}
+
+PG_FUNCTION_INFO_V1(heap_page_item_attrs);
+Datum
+heap_page_item_attrs(PG_FUNCTION_ARGS)
+{
+	bytea		*raw_page = PG_GETARG_BYTEA_P(0);
+	Oid		rel_oid = PG_GETARG_OID(1);
+	int		error_mode = PG_NARGS()==3 ? PG_GETARG_BOOL(2) :NULL;
+	error_mode = error_mode?WARNING:ERROR;
+	if (SRF_IS_FIRSTCALL() && (error_mode == WARNING))
+	{
+		ereport(WARNING,
+			(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				(errmsg("Runnung heap_page_item_attrs in WARNING mode."))));
+	}
+
+	return heap_page_items_internal(fcinfo, raw_page, rel_oid, error_mode, false);
+}
+
+PG_FUNCTION_INFO_V1(heap_page_item_detoast_attrs);
+Datum
+heap_page_item_detoast_attrs(PG_FUNCTION_ARGS)
+{
+	bytea		*raw_page = PG_GETARG_BYTEA_P(0);
+	Oid		rel_oid = PG_GETARG_OID(1);
+	int		error_mode = PG_NARGS()==3 ? PG_GETARG_BOOL(2) :NULL;
+	error_mode = error_mode?WARNING:ERROR;
+	if (SRF_IS_FIRSTCALL() && (error_mode == WARNING))
+	{
+		ereport(WARNING,
+			(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				(errmsg("Runnung heap_page_item_detoast_attrs in WARNING mode."))));
+	}
+	return heap_page_items_internal(fcinfo, raw_page, rel_oid, error_mode, true);
+}
+
+Datum
+heap_page_items_internal(FunctionCallInfo fcinfo, bytea *raw_page, Oid rel_oid, int error_level, bool do_detoast)
+{
 	heap_page_items_state *inter_call_data = NULL;
 	FuncCallContext *fctx;
-	int			raw_page_size;
 
 	if (!superuser())
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to use raw page functions"))));
-
-	raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
+					(errmsg("must be superuser to use raw page functions"))));
 
 	if (SRF_IS_FIRSTCALL())
 	{
 		TupleDesc	tupdesc;
-		MemoryContext mctx;
-
-		if (raw_page_size < SizeOfPageHeaderData)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				  errmsg("input page too small (%d bytes)", raw_page_size)));
+		MemoryContext	mctx;
 
 		fctx = SRF_FIRSTCALL_INIT();
 		mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
 
 		inter_call_data = palloc(sizeof(heap_page_items_state));
 
+		inter_call_data->raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
+		if (inter_call_data->raw_page_size < SizeOfPageHeaderData)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("input page too small (%d bytes)", inter_call_data->raw_page_size)));
+
 		/* 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");
@@ -105,7 +155,19 @@ heap_page_items(PG_FUNCTION_ARGS)
 		inter_call_data->tupd = tupdesc;
 
 		inter_call_data->offset = FirstOffsetNumber;
-		inter_call_data->page = VARDATA(raw_page);
+
+		inter_call_data->page = palloc(BLCKSZ);
+		memcpy(inter_call_data->page, VARDATA(raw_page), BLCKSZ);
+
+		inter_call_data->page_tuple_desc = NULL;
+		inter_call_data->error_level = error_level;
+
+		if (rel_oid != InvalidOid)
+		{
+			RelationData * rel = relation_open(rel_oid, NoLock);
+			inter_call_data->page_tuple_desc = CreateTupleDescCopyConstr(rel->rd_att);
+			relation_close(rel, NoLock);
+		}
 
 		fctx->max_calls = PageGetMaxOffsetNumber(inter_call_data->page);
 		fctx->user_fctx = inter_call_data;
@@ -114,112 +176,255 @@ heap_page_items(PG_FUNCTION_ARGS)
 	}
 
 	fctx = SRF_PERCALL_SETUP();
+
 	inter_call_data = fctx->user_fctx;
 
 	if (fctx->call_cntr < fctx->max_calls)
 	{
-		Page		page = inter_call_data->page;
+		Datum		values[14];
+		bool		nulls[14];
 		HeapTuple	resultTuple;
 		Datum		result;
-		ItemId		id;
-		Datum		values[13];
-		bool		nulls[13];
-		uint16		lp_offset;
-		uint16		lp_flags;
-		uint16		lp_len;
 
 		memset(nulls, 0, sizeof(nulls));
 
-		/* Extract information from the line pointer */
+		fill_header_data(fctx, values, nulls, do_detoast);
+
+		/* Build and return the result tuple. */
+		resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls);
+		result = HeapTupleGetDatum(resultTuple);
+
+		inter_call_data->offset++;
+
+		SRF_RETURN_NEXT(fctx, result);
+	}
+	else
+		SRF_RETURN_DONE(fctx);
+}
 
-		id = PageGetItemId(page, inter_call_data->offset);
 
-		lp_offset = ItemIdGetOffset(id);
-		lp_flags = ItemIdGetFlags(id);
-		lp_len = ItemIdGetLength(id);
 
-		values[0] = UInt16GetDatum(inter_call_data->offset);
-		values[1] = UInt16GetDatum(lp_offset);
-		values[2] = UInt16GetDatum(lp_flags);
-		values[3] = UInt16GetDatum(lp_len);
+void
+fill_header_data(FuncCallContext *fctx, Datum *values, bool *nulls, bool do_detoast)
+{
+	Page		page;
+	ItemId		id;
+	uint16		lp_offset;
+	uint16		lp_flags;
+	uint16		lp_len;
+	heap_page_items_state *inter_call_data = NULL;
 
+	inter_call_data = fctx->user_fctx;
+	page = inter_call_data->page;
+
+	/* Extract information from the line pointer */
+
+	id = PageGetItemId(page, inter_call_data->offset);
+
+	lp_offset = ItemIdGetOffset(id);
+	lp_flags = ItemIdGetFlags(id);
+	lp_len = ItemIdGetLength(id);
+
+	values[0] = UInt16GetDatum(inter_call_data->offset);
+	values[1] = UInt16GetDatum(lp_offset);
+	values[2] = UInt16GetDatum(lp_flags);
+	values[3] = UInt16GetDatum(lp_len);
+
+	/*
+	 * We do just enough validity checking to make sure we don't reference
+	 * data outside the page passed to us. The page could be corrupt in
+	 * many other ways, but at least we won't crash.
+	 */
+	if (ItemIdHasStorage(id) &&
+		lp_len >= MinHeapTupleSize &&
+		lp_offset == MAXALIGN(lp_offset) &&
+		lp_offset + lp_len <= inter_call_data->raw_page_size)
+	{
+		HeapTupleHeader tuphdr;
+		int			bits_len;
+		bytea			*tuple_data_bytea;
+		int			tuple_data_len;
+		
+		/* Extract information from the tuple header */
+
+		tuphdr = (HeapTupleHeader) PageGetItem(page, id);
+
+		values[4] = UInt32GetDatum(HeapTupleHeaderGetRawXmin(tuphdr));
+		values[5] = UInt32GetDatum(HeapTupleHeaderGetRawXmax(tuphdr));
+		values[6] = UInt32GetDatum(HeapTupleHeaderGetRawCommandId(tuphdr)); /* shared with xvac */
+		values[7] = PointerGetDatum(&tuphdr->t_ctid);
+		values[8] = UInt32GetDatum(tuphdr->t_infomask2);
+		values[9] = UInt32GetDatum(tuphdr->t_infomask);
+		values[10] = UInt8GetDatum(tuphdr->t_hoff);
+
+		/* Copy raw tuple data into bytea attribute */
+		tuple_data_len = lp_len - tuphdr->t_hoff;
+		tuple_data_bytea = (bytea *) palloc(tuple_data_len + VARHDRSZ);
+		SET_VARSIZE(tuple_data_bytea, tuple_data_len + VARHDRSZ);
+		memcpy(VARDATA(tuple_data_bytea), (char *) tuphdr + tuphdr->t_hoff, tuple_data_len);
+		values[13] = PointerGetDatum(tuple_data_bytea);
+
+		if (inter_call_data->page_tuple_desc)
+		{
+			split_tuple_data(fctx, tuphdr, values, nulls, do_detoast);
+		}
 		/*
-		 * We do just enough validity checking to make sure we don't reference
-		 * data outside the page passed to us. The page could be corrupt in
-		 * many other ways, but at least we won't crash.
+		 * We already checked that the item is completely within the raw
+		 * page passed to us, with the length given in the line pointer.
+		 * Let's check that t_hoff doesn't point over lp_len, before using
+		 * it to access t_bits and oid.
 		 */
-		if (ItemIdHasStorage(id) &&
-			lp_len >= MinHeapTupleSize &&
-			lp_offset == MAXALIGN(lp_offset) &&
-			lp_offset + lp_len <= raw_page_size)
+		if (tuphdr->t_hoff >= SizeofHeapTupleHeader &&
+			tuphdr->t_hoff <= lp_len &&
+			tuphdr->t_hoff == MAXALIGN(tuphdr->t_hoff))
 		{
-			HeapTupleHeader tuphdr;
-			int			bits_len;
-
-			/* Extract information from the tuple header */
-
-			tuphdr = (HeapTupleHeader) PageGetItem(page, id);
-
-			values[4] = UInt32GetDatum(HeapTupleHeaderGetRawXmin(tuphdr));
-			values[5] = UInt32GetDatum(HeapTupleHeaderGetRawXmax(tuphdr));
-			values[6] = UInt32GetDatum(HeapTupleHeaderGetRawCommandId(tuphdr)); /* shared with xvac */
-			values[7] = PointerGetDatum(&tuphdr->t_ctid);
-			values[8] = UInt32GetDatum(tuphdr->t_infomask2);
-			values[9] = UInt32GetDatum(tuphdr->t_infomask);
-			values[10] = UInt8GetDatum(tuphdr->t_hoff);
-
-			/*
-			 * We already checked that the item is completely within the raw
-			 * page passed to us, with the length given in the line pointer.
-			 * Let's check that t_hoff doesn't point over lp_len, before using
-			 * it to access t_bits and oid.
-			 */
-			if (tuphdr->t_hoff >= SizeofHeapTupleHeader &&
-				tuphdr->t_hoff <= lp_len &&
-				tuphdr->t_hoff == MAXALIGN(tuphdr->t_hoff))
+			if (tuphdr->t_infomask & HEAP_HASNULL)
 			{
-				if (tuphdr->t_infomask & HEAP_HASNULL)
-				{
-					bits_len = tuphdr->t_hoff -
-						offsetof(HeapTupleHeaderData, t_bits);
-
-					values[11] = CStringGetTextDatum(
-								 bits_to_text(tuphdr->t_bits, bits_len * 8));
-				}
-				else
-					nulls[11] = true;
+				bits_len = tuphdr->t_hoff -
+					offsetof(HeapTupleHeaderData, t_bits);
 
-				if (tuphdr->t_infomask & HEAP_HASOID)
-					values[12] = HeapTupleHeaderGetOid(tuphdr);
-				else
-					nulls[12] = true;
+				values[11] = CStringGetTextDatum(
+							bits_to_text(tuphdr->t_bits, bits_len * 8));
 			}
 			else
-			{
 				nulls[11] = true;
+
+			if (tuphdr->t_infomask & HEAP_HASOID)
+				values[12] = HeapTupleHeaderGetOid(tuphdr);
+			else
 				nulls[12] = true;
-			}
 		}
 		else
 		{
-			/*
-			 * The line pointer is not used, or it's invalid. Set the rest of
-			 * the fields to NULL
-			 */
-			int			i;
-
-			for (i = 4; i <= 12; i++)
-				nulls[i] = true;
+			nulls[11] = true;
+			nulls[12] = true;
 		}
+	}
+	else
+	{
+		/*
+		 * The line pointer is not used, or it's invalid. Set the rest of
+		 * the fields to NULL
+		 */
+		int			i;
 
-		/* Build and return the result tuple. */
-		resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls);
-		result = HeapTupleGetDatum(resultTuple);
+		for (i = 4; i <= 13; i++)
+			nulls[i] = true;
+	}
+}
 
-		inter_call_data->offset++;
 
-		SRF_RETURN_NEXT(fctx, result);
+void
+split_tuple_data(FuncCallContext *fctx, HeapTupleHeader tuphdr, Datum *values, bool *nulls, bool do_detoast)
+{
+	TupleDesc tuple_desc;
+	ArrayBuildState *raw_attrs;
+	uint16 lp_len;
+	char *tuple_data_p;
+	int nattrs;
+	int i;
+	int off;
+	heap_page_items_state *inter_call_data;
+
+	inter_call_data = fctx->user_fctx;
+	lp_len =  DatumGetUInt16(values[3]);
+
+	/*
+	 * Here we reimplement the basic functionality of nocachegetattr from
+	 * backend/access/common/heaptuple.c whitch is basically used for fetching
+	 * attributes from tuple when it is not cached. We can not use nocachegetattr here
+	 * directly, because we should ignore all cache optimisations and other stuff
+	 * just get binary data as it is.
+	 */
+
+	tuple_desc = inter_call_data->page_tuple_desc;
+	raw_attrs = initArrayResult(BYTEAOID,fctx->multi_call_memory_ctx,0);
+	tuple_data_p = (char *) tuphdr + tuphdr->t_hoff;
+
+	off = 0;
+	nattrs = tuple_desc->natts;
+	if (inter_call_data->error_level &&
+			nattrs < (tuphdr->t_infomask2 & HEAP_NATTS_MASK))
+	{
+		ereport(inter_call_data->error_level,
+				(errcode(ERRCODE_DATA_CORRUPTED),
+					errmsg("Data corruption: number of attributes in tuple header is greater than number of attributes in tuple descripor")));
 	}
-	else
-		SRF_RETURN_DONE(fctx);
+
+	for(i=0; i < nattrs; i++)
+	{
+		Form_pg_attribute attr;
+		Datum raw_attr;
+		bool is_null;
+
+		attr = tuple_desc->attrs[i];
+		is_null = (tuphdr->t_infomask & HEAP_HASNULL) && att_isnull(i, tuphdr->t_bits);
+		/*
+		 * Tuple header can specify less attributes then tuple descriptor
+		 * as ALTER TABLE ADD COLUMN without DEFAULT keyword does not
+		 * actualy change tuples in pages, so attributes with numbers greater
+		 * than tuphdr->t_infomask2 & HEAP_NATTS_MASK should be treated as NULL
+		 */
+		if (i >= (tuphdr->t_infomask2 & HEAP_NATTS_MASK) )
+			is_null = 1;
+		if (!is_null)
+		{
+			int len;
+			bytea * attr_data;
+			if (attr->attlen == -1)
+			{
+				off = att_align_pointer(off, tuple_desc->attrs[i]->attalign, -1, tuple_data_p + off);
+				/*
+				 * As VARSIZE_ANY throws an exeption if it can't properly detect
+				 * type of external storage in macros VARTAG_SIZE, so we repeat
+				 * this check here to preform nicer error handling
+				 */
+				if ( inter_call_data->error_level &&
+				     VARATT_IS_1B_E(tuple_data_p + off) &&
+				     VARTAG_EXTERNAL(tuple_data_p + off) != VARTAG_INDIRECT &&
+				     VARTAG_EXTERNAL(tuple_data_p + off) != VARTAG_ONDISK)
+				{
+					ereport(inter_call_data->error_level,
+						(errcode(ERRCODE_DATA_CORRUPTED),
+							errmsg("Data corruption: First byte of varlen attr seems to be corrupted")));
+				}
+				len = VARSIZE_ANY(tuple_data_p + off);
+			}
+			else
+			{
+				off = att_align_nominal(off, tuple_desc->attrs[i]->attalign);
+				len = attr->attlen;
+			}
+			if ( inter_call_data->error_level &&
+					lp_len < tuphdr->t_hoff + off + len)
+			{
+				ereport(inter_call_data->error_level,
+						(errcode(ERRCODE_DATA_CORRUPTED),
+							errmsg("Data corruption: Iterating over tuple data reached out of actual tuple size")));
+			}
+			attr_data = (bytea *) palloc(len + VARHDRSZ);
+			SET_VARSIZE(attr_data, len + VARHDRSZ);
+			memcpy(VARDATA(attr_data), tuple_data_p + off, len);
+			raw_attr = PointerGetDatum(attr_data);
+
+			if ( attr->attlen == -1 && do_detoast)
+			{
+				Datum raw_attr_copy;
+				raw_attr_copy = PointerGetDatum(PG_DETOAST_DATUM_COPY(tuple_data_p + off));
+				pfree(attr_data);
+				raw_attr = raw_attr_copy;
+			}
+			off = att_addlength_pointer(off, tuple_desc->attrs[i]->attlen, tuple_data_p + off);
+		}
+		raw_attrs = accumArrayResult(raw_attrs, raw_attr, is_null, BYTEAOID, fctx->multi_call_memory_ctx);
+	}
+	if (inter_call_data->error_level &&
+			lp_len != tuphdr->t_hoff + off)
+	{
+		ereport(inter_call_data->error_level,
+				(errcode(ERRCODE_DATA_CORRUPTED),
+					errmsg("Data corruption: Iterating over tuple data did not actualy reach tuple end")));
+	}
+	pfree(DatumGetPointer(values[13]));
+	values[13] = makeArrayResult(raw_attrs, fctx->multi_call_memory_ctx);
 }
diff --git a/contrib/pageinspect/pageinspect--1.3--1.4.sql b/contrib/pageinspect/pageinspect--1.3--1.4.sql
new file mode 100644
index 0000000..3966045
--- /dev/null
+++ b/contrib/pageinspect/pageinspect--1.3--1.4.sql
@@ -0,0 +1,110 @@
+/* contrib/pageinspect/pageinspect--1.3--1.4.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pageinspect UPDATE TO '1.4'" to load this file. \quit
+
+--
+-- heap_page_items()
+--
+DROP FUNCTION heap_page_items(bytea);
+CREATE FUNCTION heap_page_items(IN page bytea,
+    OUT lp smallint,
+    OUT lp_off smallint,
+    OUT lp_flags smallint,
+    OUT lp_len smallint,
+    OUT t_xmin xid,
+    OUT t_xmax xid,
+    OUT t_field3 int4,
+    OUT t_ctid tid,
+    OUT t_infomask2 integer,
+    OUT t_infomask integer,
+    OUT t_hoff smallint,
+    OUT t_bits text,
+    OUT t_oid oid,
+    OUT t_data bytea)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'heap_page_items'
+LANGUAGE C STRICT;
+
+--
+-- heap_page_item_attrs()
+--
+CREATE FUNCTION heap_page_item_attrs(IN page bytea, IN heap_oid regclass,
+    OUT lp smallint,
+    OUT lp_off smallint,
+    OUT lp_flags smallint,
+    OUT lp_len smallint,
+    OUT t_xmin xid,
+    OUT t_xmax xid,
+    OUT t_field3 int4,
+    OUT t_ctid tid,
+    OUT t_infomask2 integer,
+    OUT t_infomask integer,
+    OUT t_hoff smallint,
+    OUT t_bits text,
+    OUT t_oid oid,
+    OUT t_attrs bytea[])
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'heap_page_item_attrs'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION heap_page_item_attrs(IN page bytea, IN heap_oid regclass, IN warning_mode bool,
+    OUT lp smallint,
+    OUT lp_off smallint,
+    OUT lp_flags smallint,
+    OUT lp_len smallint,
+    OUT t_xmin xid,
+    OUT t_xmax xid,
+    OUT t_field3 int4,
+    OUT t_ctid tid,
+    OUT t_infomask2 integer,
+    OUT t_infomask integer,
+    OUT t_hoff smallint,
+    OUT t_bits text,
+    OUT t_oid oid,
+    OUT t_attrs bytea[])
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'heap_page_item_attrs'
+LANGUAGE C STRICT;
+
+--
+-- heap_page_item_detoast_attrs()
+--
+
+CREATE FUNCTION heap_page_item_detoast_attrs(IN page bytea, IN heap_oid regclass,
+    OUT lp smallint,
+    OUT lp_off smallint,
+    OUT lp_flags smallint,
+    OUT lp_len smallint,
+    OUT t_xmin xid,
+    OUT t_xmax xid,
+    OUT t_field3 int4,
+    OUT t_ctid tid,
+    OUT t_infomask2 integer,
+    OUT t_infomask integer,
+    OUT t_hoff smallint,
+    OUT t_bits text,
+    OUT t_oid oid,
+    OUT t_attrs bytea[])
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'heap_page_item_detoast_attrs'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION heap_page_item_detoast_attrs(IN page bytea, IN heap_oid regclass, IN warning_mode bool,
+    OUT lp smallint,
+    OUT lp_off smallint,
+    OUT lp_flags smallint,
+    OUT lp_len smallint,
+    OUT t_xmin xid,
+    OUT t_xmax xid,
+    OUT t_field3 int4,
+    OUT t_ctid tid,
+    OUT t_infomask2 integer,
+    OUT t_infomask integer,
+    OUT t_hoff smallint,
+    OUT t_bits text,
+    OUT t_oid oid,
+    OUT t_attrs bytea[])
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'heap_page_item_detoast_attrs'
+LANGUAGE C STRICT;
\ No newline at end of file
diff --git a/contrib/pageinspect/pageinspect--1.3.sql b/contrib/pageinspect/pageinspect--1.3.sql
deleted file mode 100644
index a99e058..0000000
--- a/contrib/pageinspect/pageinspect--1.3.sql
+++ /dev/null
@@ -1,189 +0,0 @@
-/* contrib/pageinspect/pageinspect--1.3.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION pageinspect" to load this file. \quit
-
---
--- get_raw_page()
---
-CREATE FUNCTION get_raw_page(text, int4)
-RETURNS bytea
-AS 'MODULE_PATHNAME', 'get_raw_page'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION get_raw_page(text, text, int4)
-RETURNS bytea
-AS 'MODULE_PATHNAME', 'get_raw_page_fork'
-LANGUAGE C STRICT;
-
---
--- page_header()
---
-CREATE FUNCTION page_header(IN page bytea,
-    OUT lsn pg_lsn,
-    OUT checksum smallint,
-    OUT flags smallint,
-    OUT lower smallint,
-    OUT upper smallint,
-    OUT special smallint,
-    OUT pagesize smallint,
-    OUT version smallint,
-    OUT prune_xid xid)
-AS 'MODULE_PATHNAME', 'page_header'
-LANGUAGE C STRICT;
-
---
--- heap_page_items()
---
-CREATE FUNCTION heap_page_items(IN page bytea,
-    OUT lp smallint,
-    OUT lp_off smallint,
-    OUT lp_flags smallint,
-    OUT lp_len smallint,
-    OUT t_xmin xid,
-    OUT t_xmax xid,
-    OUT t_field3 int4,
-    OUT t_ctid tid,
-    OUT t_infomask2 integer,
-    OUT t_infomask integer,
-    OUT t_hoff smallint,
-    OUT t_bits text,
-    OUT t_oid oid)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME', 'heap_page_items'
-LANGUAGE C STRICT;
-
---
--- bt_metap()
---
-CREATE FUNCTION bt_metap(IN relname text,
-    OUT magic int4,
-    OUT version int4,
-    OUT root int4,
-    OUT level int4,
-    OUT fastroot int4,
-    OUT fastlevel int4)
-AS 'MODULE_PATHNAME', 'bt_metap'
-LANGUAGE C STRICT;
-
---
--- bt_page_stats()
---
-CREATE FUNCTION bt_page_stats(IN relname text, IN blkno int4,
-    OUT blkno int4,
-    OUT type "char",
-    OUT live_items int4,
-    OUT dead_items int4,
-    OUT avg_item_size int4,
-    OUT page_size int4,
-    OUT free_size int4,
-    OUT btpo_prev int4,
-    OUT btpo_next int4,
-    OUT btpo int4,
-    OUT btpo_flags int4)
-AS 'MODULE_PATHNAME', 'bt_page_stats'
-LANGUAGE C STRICT;
-
---
--- bt_page_items()
---
-CREATE FUNCTION bt_page_items(IN relname text, IN blkno int4,
-    OUT itemoffset smallint,
-    OUT ctid tid,
-    OUT itemlen smallint,
-    OUT nulls bool,
-    OUT vars bool,
-    OUT data text)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME', 'bt_page_items'
-LANGUAGE C STRICT;
-
---
--- brin_page_type()
---
-CREATE FUNCTION brin_page_type(IN page bytea)
-RETURNS text
-AS 'MODULE_PATHNAME', 'brin_page_type'
-LANGUAGE C STRICT;
-
---
--- brin_metapage_info()
---
-CREATE FUNCTION brin_metapage_info(IN page bytea, OUT magic text,
-	OUT version integer, OUT pagesperrange integer, OUT lastrevmappage bigint)
-AS 'MODULE_PATHNAME', 'brin_metapage_info'
-LANGUAGE C STRICT;
-
---
--- brin_revmap_data()
---
-CREATE FUNCTION brin_revmap_data(IN page bytea,
-	OUT pages tid)
-RETURNS SETOF tid
-AS 'MODULE_PATHNAME', 'brin_revmap_data'
-LANGUAGE C STRICT;
-
---
--- brin_page_items()
---
-CREATE FUNCTION brin_page_items(IN page bytea, IN index_oid regclass,
-	OUT itemoffset int,
-	OUT blknum int,
-	OUT attnum int,
-	OUT allnulls bool,
-	OUT hasnulls bool,
-	OUT placeholder bool,
-	OUT value text)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME', 'brin_page_items'
-LANGUAGE C STRICT;
-
---
--- fsm_page_contents()
---
-CREATE FUNCTION fsm_page_contents(IN page bytea)
-RETURNS text
-AS 'MODULE_PATHNAME', 'fsm_page_contents'
-LANGUAGE C STRICT;
-
---
--- GIN functions
---
-
---
--- gin_metapage_info()
---
-CREATE FUNCTION gin_metapage_info(IN page bytea,
-    OUT pending_head bigint,
-    OUT pending_tail bigint,
-    OUT tail_free_size int4,
-    OUT n_pending_pages bigint,
-    OUT n_pending_tuples bigint,
-    OUT n_total_pages bigint,
-    OUT n_entry_pages bigint,
-    OUT n_data_pages bigint,
-    OUT n_entries bigint,
-    OUT version int4)
-AS 'MODULE_PATHNAME', 'gin_metapage_info'
-LANGUAGE C STRICT;
-
---
--- gin_page_opaque_info()
---
-CREATE FUNCTION gin_page_opaque_info(IN page bytea,
-    OUT rightlink bigint,
-    OUT maxoff int4,
-    OUT flags text[])
-AS 'MODULE_PATHNAME', 'gin_page_opaque_info'
-LANGUAGE C STRICT;
-
---
--- gin_leafpage_items()
---
-CREATE FUNCTION gin_leafpage_items(IN page bytea,
-    OUT first_tid tid,
-    OUT nbytes int2,
-    OUT tids tid[])
-RETURNS SETOF record
-AS 'MODULE_PATHNAME', 'gin_leafpage_items'
-LANGUAGE C STRICT;
diff --git a/contrib/pageinspect/pageinspect--1.4.sql b/contrib/pageinspect/pageinspect--1.4.sql
new file mode 100644
index 0000000..67bf545
--- /dev/null
+++ b/contrib/pageinspect/pageinspect--1.4.sql
@@ -0,0 +1,296 @@
+/* contrib/pageinspect/pageinspect--1.4.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION pageinspect" to load this file. \quit
+
+--
+-- get_raw_page()
+--
+CREATE FUNCTION get_raw_page(text, int4)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'get_raw_page'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION get_raw_page(text, text, int4)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'get_raw_page_fork'
+LANGUAGE C STRICT;
+
+--
+-- page_header()
+--
+CREATE FUNCTION page_header(IN page bytea,
+    OUT lsn pg_lsn,
+    OUT checksum smallint,
+    OUT flags smallint,
+    OUT lower smallint,
+    OUT upper smallint,
+    OUT special smallint,
+    OUT pagesize smallint,
+    OUT version smallint,
+    OUT prune_xid xid)
+AS 'MODULE_PATHNAME', 'page_header'
+LANGUAGE C STRICT;
+
+--
+-- heap_page_items()
+--
+CREATE FUNCTION heap_page_items(IN page bytea,
+    OUT lp smallint,
+    OUT lp_off smallint,
+    OUT lp_flags smallint,
+    OUT lp_len smallint,
+    OUT t_xmin xid,
+    OUT t_xmax xid,
+    OUT t_field3 int4,
+    OUT t_ctid tid,
+    OUT t_infomask2 integer,
+    OUT t_infomask integer,
+    OUT t_hoff smallint,
+    OUT t_bits text,
+    OUT t_oid oid,
+    OUT t_data bytea)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'heap_page_items'
+LANGUAGE C STRICT;
+
+--
+-- heap_page_item_attrs()
+--
+CREATE FUNCTION heap_page_item_attrs(IN page bytea, IN heap_oid regclass,
+    OUT lp smallint,
+    OUT lp_off smallint,
+    OUT lp_flags smallint,
+    OUT lp_len smallint,
+    OUT t_xmin xid,
+    OUT t_xmax xid,
+    OUT t_field3 int4,
+    OUT t_ctid tid,
+    OUT t_infomask2 integer,
+    OUT t_infomask integer,
+    OUT t_hoff smallint,
+    OUT t_bits text,
+    OUT t_oid oid,
+    OUT t_attrs bytea[])
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'heap_page_item_attrs'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION heap_page_item_attrs(IN page bytea, IN heap_oid regclass, IN warning_mode bool,
+    OUT lp smallint,
+    OUT lp_off smallint,
+    OUT lp_flags smallint,
+    OUT lp_len smallint,
+    OUT t_xmin xid,
+    OUT t_xmax xid,
+    OUT t_field3 int4,
+    OUT t_ctid tid,
+    OUT t_infomask2 integer,
+    OUT t_infomask integer,
+    OUT t_hoff smallint,
+    OUT t_bits text,
+    OUT t_oid oid,
+    OUT t_attrs bytea[])
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'heap_page_item_attrs'
+LANGUAGE C STRICT;
+
+--
+-- heap_page_item_detoast_attrs()
+--
+
+CREATE FUNCTION heap_page_item_detoast_attrs(IN page bytea, IN heap_oid regclass, IN warning_mode bool,
+    OUT lp smallint,
+    OUT lp_off smallint,
+    OUT lp_flags smallint,
+    OUT lp_len smallint,
+    OUT t_xmin xid,
+    OUT t_xmax xid,
+    OUT t_field3 int4,
+    OUT t_ctid tid,
+    OUT t_infomask2 integer,
+    OUT t_infomask integer,
+    OUT t_hoff smallint,
+    OUT t_bits text,
+    OUT t_oid oid,
+    OUT t_attrs bytea[])
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'heap_page_item_detoast_attrs'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION heap_page_item_detoast_attrs(IN page bytea, IN heap_oid regclass,
+    OUT lp smallint,
+    OUT lp_off smallint,
+    OUT lp_flags smallint,
+    OUT lp_len smallint,
+    OUT t_xmin xid,
+    OUT t_xmax xid,
+    OUT t_field3 int4,
+    OUT t_ctid tid,
+    OUT t_infomask2 integer,
+    OUT t_infomask integer,
+    OUT t_hoff smallint,
+    OUT t_bits text,
+    OUT t_oid oid,
+    OUT t_attrs bytea[])
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'heap_page_item_detoast_attrs'
+LANGUAGE C STRICT;
+
+--
+-- _heap_page_items()
+--
+CREATE FUNCTION _heap_page_items(IN page bytea, IN relname text,
+    OUT lp smallint,
+    OUT lp_off smallint,
+    OUT lp_flags smallint,
+    OUT lp_len smallint,
+    OUT t_xmin xid,
+    OUT t_xmax xid,
+    OUT t_field3 int4,
+    OUT t_ctid tid,
+    OUT t_infomask2 integer,
+    OUT t_infomask integer,
+    OUT t_hoff smallint,
+    OUT t_bits text,
+    OUT t_oid oid,
+    OUT t_attrs bytea[]
+    )
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'heap_page_items'
+LANGUAGE C STRICT;
+
+--
+-- bt_metap()
+--
+CREATE FUNCTION bt_metap(IN relname text,
+    OUT magic int4,
+    OUT version int4,
+    OUT root int4,
+    OUT level int4,
+    OUT fastroot int4,
+    OUT fastlevel int4)
+AS 'MODULE_PATHNAME', 'bt_metap'
+LANGUAGE C STRICT;
+
+--
+-- bt_page_stats()
+--
+CREATE FUNCTION bt_page_stats(IN relname text, IN blkno int4,
+    OUT blkno int4,
+    OUT type "char",
+    OUT live_items int4,
+    OUT dead_items int4,
+    OUT avg_item_size int4,
+    OUT page_size int4,
+    OUT free_size int4,
+    OUT btpo_prev int4,
+    OUT btpo_next int4,
+    OUT btpo int4,
+    OUT btpo_flags int4)
+AS 'MODULE_PATHNAME', 'bt_page_stats'
+LANGUAGE C STRICT;
+
+--
+-- bt_page_items()
+--
+CREATE FUNCTION bt_page_items(IN relname text, IN blkno int4,
+    OUT itemoffset smallint,
+    OUT ctid tid,
+    OUT itemlen smallint,
+    OUT nulls bool,
+    OUT vars bool,
+    OUT data text)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'bt_page_items'
+LANGUAGE C STRICT;
+
+--
+-- brin_page_type()
+--
+CREATE FUNCTION brin_page_type(IN page bytea)
+RETURNS text
+AS 'MODULE_PATHNAME', 'brin_page_type'
+LANGUAGE C STRICT;
+
+--
+-- brin_metapage_info()
+--
+CREATE FUNCTION brin_metapage_info(IN page bytea, OUT magic text,
+	OUT version integer, OUT pagesperrange integer, OUT lastrevmappage bigint)
+AS 'MODULE_PATHNAME', 'brin_metapage_info'
+LANGUAGE C STRICT;
+
+--
+-- brin_revmap_data()
+--
+CREATE FUNCTION brin_revmap_data(IN page bytea,
+	OUT pages tid)
+RETURNS SETOF tid
+AS 'MODULE_PATHNAME', 'brin_revmap_data'
+LANGUAGE C STRICT;
+
+--
+-- brin_page_items()
+--
+CREATE FUNCTION brin_page_items(IN page bytea, IN index_oid regclass,
+	OUT itemoffset int,
+	OUT blknum int,
+	OUT attnum int,
+	OUT allnulls bool,
+	OUT hasnulls bool,
+	OUT placeholder bool,
+	OUT value text)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'brin_page_items'
+LANGUAGE C STRICT;
+
+--
+-- fsm_page_contents()
+--
+CREATE FUNCTION fsm_page_contents(IN page bytea)
+RETURNS text
+AS 'MODULE_PATHNAME', 'fsm_page_contents'
+LANGUAGE C STRICT;
+
+--
+-- GIN functions
+--
+
+--
+-- gin_metapage_info()
+--
+CREATE FUNCTION gin_metapage_info(IN page bytea,
+    OUT pending_head bigint,
+    OUT pending_tail bigint,
+    OUT tail_free_size int4,
+    OUT n_pending_pages bigint,
+    OUT n_pending_tuples bigint,
+    OUT n_total_pages bigint,
+    OUT n_entry_pages bigint,
+    OUT n_data_pages bigint,
+    OUT n_entries bigint,
+    OUT version int4)
+AS 'MODULE_PATHNAME', 'gin_metapage_info'
+LANGUAGE C STRICT;
+
+--
+-- gin_page_opaque_info()
+--
+CREATE FUNCTION gin_page_opaque_info(IN page bytea,
+    OUT rightlink bigint,
+    OUT maxoff int4,
+    OUT flags text[])
+AS 'MODULE_PATHNAME', 'gin_page_opaque_info'
+LANGUAGE C STRICT;
+
+--
+-- gin_leafpage_items()
+--
+CREATE FUNCTION gin_leafpage_items(IN page bytea,
+    OUT first_tid tid,
+    OUT nbytes int2,
+    OUT tids tid[])
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'gin_leafpage_items'
+LANGUAGE C STRICT;
diff --git a/contrib/pageinspect/pageinspect.control b/contrib/pageinspect/pageinspect.control
index a9dab33..68c7d61 100644
--- a/contrib/pageinspect/pageinspect.control
+++ b/contrib/pageinspect/pageinspect.control
@@ -1,5 +1,5 @@
 # pageinspect extension
 comment = 'inspect the contents of database pages at a low level'
-default_version = '1.3'
+default_version = '1.4'
 module_pathname = '$libdir/pageinspect'
 relocatable = true
diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c
index 38c136f..6659ee5 100644
--- a/contrib/pageinspect/rawpage.c
+++ b/contrib/pageinspect/rawpage.c
@@ -26,11 +26,9 @@
 #include "utils/pg_lsn.h"
 #include "utils/rel.h"
 
-PG_MODULE_MAGIC;
-
-static bytea *get_raw_page_internal(text *relname, ForkNumber forknum,
-					  BlockNumber blkno);
+#include "rawpage.h"
 
+PG_MODULE_MAGIC;
 
 /*
  * get_raw_page
@@ -87,7 +85,7 @@ get_raw_page_fork(PG_FUNCTION_ARGS)
 /*
  * workhorse
  */
-static bytea *
+bytea *
 get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
 {
 	bytea	   *raw_page;
diff --git a/contrib/pageinspect/rawpage.h b/contrib/pageinspect/rawpage.h
new file mode 100644
index 0000000..d9f2678
--- /dev/null
+++ b/contrib/pageinspect/rawpage.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * rawpage.h
+ *
+ * Copyright (c) 2007-2015, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  contrib/pageinspect/rawpage.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef __RAWPAGE_H__
+#define __RAWPAGE_H__
+
+
+bytea *get_raw_page_internal(text *relname, ForkNumber forknum,
+					  BlockNumber blkno);
+
+#endif /*__RAWPAGE_H__*/
\ No newline at end of file
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to