В письме от 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