On 09/29/2016 11:58 AM, Peter Eisentraut wrote:
On 9/27/16 10:10 AM, Jesper Pedersen wrote:
contrib/pageinspect/pageinspect--1.5--1.6.sql |  59 ++++
 contrib/pageinspect/pageinspect--1.5.sql      | 279 ------------------
 contrib/pageinspect/pageinspect--1.6.sql      | 346 ++++++++++++++++++++++

I think there is still a misunderstanding about these extension files.
You only need to supply pageinspect--1.5--1.6.sql and leave all the
other ones alone.


Ok.

Left the 'DATA' section in the Makefile, and the control file as is.

Best regards,
 Jesper

>From b48623beda6aad43116d46ff44de55bdc7547325 Mon Sep 17 00:00:00 2001
From: jesperpedersen <jesper.peder...@redhat.com>
Date: Fri, 5 Aug 2016 10:16:32 -0400
Subject: [PATCH] pageinspect: Hash index support

---
 contrib/pageinspect/Makefile                  |  10 +-
 contrib/pageinspect/hashfuncs.c               | 408 ++++++++++++++++++++++++++
 contrib/pageinspect/pageinspect--1.5--1.6.sql |  59 ++++
 contrib/pageinspect/pageinspect.control       |   2 +-
 doc/src/sgml/pageinspect.sgml                 | 146 ++++++++-
 5 files changed, 609 insertions(+), 16 deletions(-)
 create mode 100644 contrib/pageinspect/hashfuncs.c
 create mode 100644 contrib/pageinspect/pageinspect--1.5--1.6.sql

diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile
index a98237e..c73d728 100644
--- a/contrib/pageinspect/Makefile
+++ b/contrib/pageinspect/Makefile
@@ -2,13 +2,13 @@
 
 MODULE_big	= pageinspect
 OBJS		= rawpage.o heapfuncs.o btreefuncs.o fsmfuncs.o \
-		  brinfuncs.o ginfuncs.o $(WIN32RES)
+		  brinfuncs.o ginfuncs.o hashfuncs.o $(WIN32RES)
 
 EXTENSION = pageinspect
-DATA = pageinspect--1.5.sql pageinspect--1.4--1.5.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
+DATA = pageinspect--1.6.sql pageinspect--1.5--1.6.sql \
+	pageinspect--1.4--1.5.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"
 
 ifdef USE_PGXS
diff --git a/contrib/pageinspect/hashfuncs.c b/contrib/pageinspect/hashfuncs.c
new file mode 100644
index 0000000..a76b683
--- /dev/null
+++ b/contrib/pageinspect/hashfuncs.c
@@ -0,0 +1,408 @@
+/*
+ * hashfuncs.c
+ *		Functions to investigate the content of HASH indexes
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		contrib/pageinspect/hashfuncs.c
+ */
+
+#include "postgres.h"
+
+#include "access/hash.h"
+#include "access/htup_details.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+
+PG_FUNCTION_INFO_V1(hash_metapage_info);
+PG_FUNCTION_INFO_V1(hash_page_items);
+PG_FUNCTION_INFO_V1(hash_page_stats);
+
+/* ------------------------------------------------
+ * structure for single hash page statistics
+ * ------------------------------------------------
+ */
+typedef struct HashPageStat
+{
+	uint32		live_items;
+	uint32		dead_items;
+	uint32		page_size;
+	uint32		free_size;
+	char	   *type;
+
+	/* opaque data */
+	BlockNumber hasho_prevblkno;
+	BlockNumber hasho_nextblkno;
+	Bucket		hasho_bucket;
+	uint16		hasho_flag;
+	uint16		hasho_page_id;
+} HashPageStat;
+
+
+/*
+ * Verify that the given bytea contains a HASH page, or die in the attempt.
+ * A pointer to the page is returned.
+ */
+static Page
+verify_hash_page(bytea *raw_page, bool metap)
+{
+	Page		page;
+	int			raw_page_size;
+	HashPageOpaque pageopaque;
+
+	raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
+
+	if (raw_page_size != BLCKSZ)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("input page too small"),
+				 errdetail("Expected size %d, got %d",
+						   BLCKSZ, raw_page_size)));
+
+	page = VARDATA(raw_page);
+	pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
+	if (pageopaque->hasho_page_id != HASHO_PAGE_ID)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("page is not a HASH page"),
+				 errdetail("Expected %08x, got %08x.",
+						   HASHO_PAGE_ID, pageopaque->hasho_page_id)));
+
+	if (metap)
+	{
+		if (pageopaque->hasho_flag != LH_META_PAGE)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("page is not a metadata page"),
+					 errdetail("Expected %08x, got %08x.",
+							   LH_META_PAGE, pageopaque->hasho_flag)));
+	}
+	else
+	{
+		if (pageopaque->hasho_flag == LH_META_PAGE)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("page is a metadata page"),
+					 errdetail("Flags %08x.",
+							   pageopaque->hasho_flag)));
+	}
+
+	return page;
+}
+
+/* -------------------------------------------------
+ * GetHashPageStatistics()
+ *
+ * Collect statistics of single hash page
+ * -------------------------------------------------
+ */
+static void
+GetHashPageStatistics(Page page, HashPageStat *stat)
+{
+	OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
+	HashPageOpaque opaque = (HashPageOpaque) PageGetSpecialPointer(page);
+	int			off;
+
+	stat->dead_items = stat->live_items = 0;
+	stat->page_size = PageGetPageSize(page);
+
+	/* page type (flags) */
+	if (opaque->hasho_flag & LH_META_PAGE)
+		stat->type = "metapage";
+	else if (opaque->hasho_flag & LH_OVERFLOW_PAGE)
+		stat->type = "overflow";
+	else if (opaque->hasho_flag & LH_BUCKET_PAGE)
+		stat->type = "bucket";
+	else if (opaque->hasho_flag & LH_BITMAP_PAGE)
+		stat->type = "bitmap";
+	else
+		stat->type = "unused";
+
+	/* hash page opaque data */
+	stat->hasho_prevblkno = opaque->hasho_prevblkno;
+	stat->hasho_nextblkno = opaque->hasho_nextblkno;
+	stat->hasho_bucket = opaque->hasho_bucket;
+	stat->hasho_flag = opaque->hasho_flag;
+	stat->hasho_page_id = opaque->hasho_page_id;
+
+	/* count live and dead tuples, and free space */
+	for (off = FirstOffsetNumber; off <= maxoff; off++)
+	{
+		ItemId		id = PageGetItemId(page, off);
+
+		if (!ItemIdIsDead(id))
+			stat->live_items++;
+		else
+			stat->dead_items++;
+	}
+	stat->free_size = PageGetFreeSpace(page);
+}
+
+/* ---------------------------------------------------
+ * hash_page_stats()
+ *
+ * Usage: SELECT * FROM hash_page_stats(get_raw_page('t1_hash', 1));
+ * ---------------------------------------------------
+ */
+Datum
+hash_page_stats(PG_FUNCTION_ARGS)
+{
+	bytea	   *raw_page = PG_GETARG_BYTEA_P(0);
+	Page		page;
+	int			j;
+	Datum       values[10];
+	bool        nulls[10];
+	HashPageStat	stat;
+	HeapTuple	tuple;
+	TupleDesc	tupleDesc;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to use pageinspect functions"))));
+
+	page = verify_hash_page(raw_page, false);
+
+	/* keep compiler quiet */
+	stat.hasho_prevblkno = stat.hasho_nextblkno = InvalidBlockNumber;
+	stat.hasho_flag = stat.hasho_page_id = stat.free_size = 0;
+
+	GetHashPageStatistics(page, &stat);
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+	tupleDesc = BlessTupleDesc(tupleDesc);
+
+	MemSet(nulls, 0, sizeof(nulls));
+
+	j = 0;
+	values[j++] = CStringGetTextDatum(stat.type);
+	values[j++] = UInt32GetDatum(stat.live_items);
+	values[j++] = UInt32GetDatum(stat.dead_items);
+	values[j++] = UInt32GetDatum(stat.page_size);
+	values[j++] = UInt32GetDatum(stat.free_size);
+	values[j++] = UInt32GetDatum(stat.hasho_prevblkno);
+	values[j++] = UInt32GetDatum(stat.hasho_nextblkno);
+	values[j++] = UInt32GetDatum(stat.hasho_bucket);
+	values[j++] = UInt16GetDatum(stat.hasho_flag);
+	values[j++] = UInt16GetDatum(stat.hasho_page_id);
+
+	tuple = heap_form_tuple(tupleDesc, values, nulls);
+
+	PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+}
+
+/*
+ * cross-call data structure for SRF
+ */
+struct user_args
+{
+	Page		page;
+	OffsetNumber offset;
+};
+
+/*-------------------------------------------------------
+ * hash_page_items()
+ *
+ * Get IndexTupleData set in a hash page
+ *
+ * Usage: SELECT * FROM hash_page_items(get_raw_page('t1_hash', 1));
+ *-------------------------------------------------------
+ */
+Datum
+hash_page_items(PG_FUNCTION_ARGS)
+{
+	bytea	   *raw_page = PG_GETARG_BYTEA_P(0);
+	Page		page;
+	Datum		result;
+	Datum	    values[3];
+	bool	    nulls[3];
+	HeapTuple	tuple;
+	FuncCallContext *fctx;
+	MemoryContext mctx;
+	struct user_args *uargs;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to use pageinspect functions"))));
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		TupleDesc	tupleDesc;
+
+		fctx = SRF_FIRSTCALL_INIT();
+
+		page = verify_hash_page(raw_page, false);
+
+		/*
+		 * We copy the page into local storage to avoid holding pin on the
+		 * buffer longer than we must, and possibly failing to release it at
+		 * all if the calling query doesn't fetch all rows.
+		 */
+		mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
+
+		uargs = palloc(sizeof(struct user_args));
+
+		uargs->page = palloc(BLCKSZ);
+		memcpy(uargs->page, page, BLCKSZ);
+
+		uargs->offset = FirstOffsetNumber;
+
+		fctx->max_calls = PageGetMaxOffsetNumber(uargs->page);
+
+		/* Build a tuple descriptor for our result type */
+		if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
+			elog(ERROR, "return type must be a row type");
+		tupleDesc = BlessTupleDesc(tupleDesc);
+
+		fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc);
+
+		fctx->user_fctx = uargs;
+
+		MemoryContextSwitchTo(mctx);
+	}
+
+	fctx = SRF_PERCALL_SETUP();
+	uargs = fctx->user_fctx;
+
+	if (fctx->call_cntr < fctx->max_calls)
+	{
+		ItemId		id;
+		IndexTuple	itup;
+		int			j;
+		int			off;
+		int			dlen;
+		char	   *dump;
+		char	   *s;
+		char	   *ptr;
+
+		id = PageGetItemId(uargs->page, uargs->offset);
+
+		if (!ItemIdIsValid(id))
+			elog(ERROR, "invalid ItemId");
+
+		itup = (IndexTuple) PageGetItem(uargs->page, id);
+
+		MemSet(nulls, 0, sizeof(nulls));
+
+		j = 0;
+		values[j++] = UInt16GetDatum(uargs->offset);
+		values[j++] = CStringGetTextDatum(psprintf("(%u,%u)",
+												   BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)),
+												   itup->t_tid.ip_posid));
+
+		ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
+		dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
+		dump = palloc0(dlen * 3 + 1);
+		s = dump;
+		for (off = 0; off < dlen; off++)
+		{
+			if (off > 0)
+				*dump++ = ' ';
+			sprintf(dump, "%02x", *(ptr + off) & 0xff);
+			dump += 2;
+		}
+		values[j] = CStringGetTextDatum(s);
+
+		tuple = heap_form_tuple(fctx->attinmeta->tupdesc, values, nulls);
+		result = HeapTupleGetDatum(tuple);
+
+		uargs->offset = uargs->offset + 1;
+
+		SRF_RETURN_NEXT(fctx, result);
+	}
+	else
+	{
+		pfree(uargs->page);
+		pfree(uargs);
+		SRF_RETURN_DONE(fctx);
+	}
+}
+
+/* ------------------------------------------------
+ * hash_metapage_info()
+ *
+ * Get a hash's meta-page information
+ *
+ * Usage: SELECT * FROM hash_metapage_info(get_raw_page('t1_hash', 0))
+ * ------------------------------------------------
+ */
+Datum
+hash_metapage_info(PG_FUNCTION_ARGS)
+{
+	bytea	   *raw_page = PG_GETARG_BYTEA_P(0);
+	Page		page;
+	HashMetaPageData *metad;
+	TupleDesc	tupleDesc;
+	HeapTuple	tuple;
+	int			i,j;
+	Datum	    values[16];
+	bool	    nulls[16];
+	char       *spares;
+	char       *mapp;
+	char       *s;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to use pageinspect functions"))));
+
+	page = verify_hash_page(raw_page, true);
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+	tupleDesc = BlessTupleDesc(tupleDesc);
+
+	metad = HashPageGetMeta(page);
+
+	MemSet(nulls, 0, sizeof(nulls));
+
+	j = 0;
+	values[j++] = CStringGetTextDatum(psprintf("0x%08X", metad->hashm_magic));
+	values[j++] = UInt32GetDatum(metad->hashm_version);
+	values[j++] = Float8GetDatum(metad->hashm_ntuples);
+	values[j++] = UInt16GetDatum(metad->hashm_ffactor);
+	values[j++] = UInt16GetDatum(metad->hashm_bsize);
+	values[j++] = UInt16GetDatum(metad->hashm_bmsize);
+	values[j++] = UInt16GetDatum(metad->hashm_bmshift);
+	values[j++] = UInt32GetDatum(metad->hashm_maxbucket);
+	values[j++] = UInt32GetDatum(metad->hashm_highmask);
+	values[j++] = UInt32GetDatum(metad->hashm_lowmask);
+	values[j++] = UInt32GetDatum(metad->hashm_ovflpoint);
+	values[j++] = UInt32GetDatum(metad->hashm_firstfree);
+	values[j++] = UInt32GetDatum(metad->hashm_nmaps);
+	values[j++] = UInt16GetDatum(metad->hashm_procid);
+
+	spares = palloc0(HASH_MAX_SPLITPOINTS * 5 + 1);
+	s = spares;
+	for (i = 0; i < HASH_MAX_SPLITPOINTS; i++)
+	{
+		if (i > 0)
+			*spares++ = ' ';
+
+		sprintf(spares, "%04x", metad->hashm_spares[i]);
+		spares += 4;
+	}
+	values[j++] = CStringGetTextDatum(s);
+
+	mapp = palloc0(HASH_MAX_BITMAPS * 5 + 1);
+	s = mapp;
+	for (i = 0; i < HASH_MAX_BITMAPS; i++)
+	{
+		if (i > 0)
+			*mapp++ = ' ';
+
+		sprintf(mapp, "%04x", metad->hashm_mapp[i]);
+		mapp += 4;
+	}
+	values[j++] = CStringGetTextDatum(s);
+
+	tuple = heap_form_tuple(tupleDesc, values, nulls);
+
+	PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+}
diff --git a/contrib/pageinspect/pageinspect--1.5--1.6.sql b/contrib/pageinspect/pageinspect--1.5--1.6.sql
new file mode 100644
index 0000000..7f8c2d2
--- /dev/null
+++ b/contrib/pageinspect/pageinspect--1.5--1.6.sql
@@ -0,0 +1,59 @@
+/* contrib/pageinspect/pageinspect--1.5--1.6.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pageinspect UPDATE TO '1.6'" to load this file. \quit
+
+--
+-- HASH functions
+--
+
+--
+-- hash_metapage_info()
+--
+CREATE FUNCTION hash_metapage_info(IN page bytea,
+    OUT magic text,
+    OUT version int4,
+    OUT ntuples double precision,
+    OUT ffactor int2,
+    OUT bsize int2,
+    OUT bmsize int2,
+    OUT bmshift int2,
+    OUT maxbucket int4,
+    OUT highmask int4,
+    OUT lowmask int4,
+    OUT ovflpoint int4,
+    OUT firstfree int4,
+    OUT nmaps int4,
+    OUT procid int4,
+    OUT spares text,
+    OUT mapp text)
+AS 'MODULE_PATHNAME', 'hash_metapage_info'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- hash_page_stats()
+--
+CREATE FUNCTION hash_page_stats(IN page bytea,
+    OUT type text,
+    OUT live_items int4,
+    OUT dead_items int4,
+    OUT page_size int4,
+    OUT free_size int4,
+    OUT hasho_prevblkno int4,
+    OUT hasho_nextblkno int4,
+    OUT hasho_bucket int4,
+    OUT hasho_flag int4,
+    OUT hasho_page_id int4)
+AS 'MODULE_PATHNAME', 'hash_page_stats'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- hash_page_items()
+--
+CREATE FUNCTION hash_page_items(IN page bytea,
+    OUT itemoffset smallint,
+    OUT ctid tid,
+    OUT data text)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'hash_page_items'
+LANGUAGE C STRICT PARALLEL SAFE;
diff --git a/contrib/pageinspect/pageinspect.control b/contrib/pageinspect/pageinspect.control
index 23c8eff..1a61c9f 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.5'
+default_version = '1.6'
 module_pathname = '$libdir/pageinspect'
 relocatable = true
diff --git a/doc/src/sgml/pageinspect.sgml b/doc/src/sgml/pageinspect.sgml
index 5d187ed..626feba 100644
--- a/doc/src/sgml/pageinspect.sgml
+++ b/doc/src/sgml/pageinspect.sgml
@@ -14,7 +14,7 @@
  </para>
 
  <sect2>
-  <title>Functions</title>
+  <title>General functions</title>
 
   <variablelist>
    <varlistentry>
@@ -160,7 +160,36 @@ test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term>
+     <function>fsm_page_contents(page bytea) returns text</function>
+     <indexterm>
+      <primary>fsm_page_contents</primary>
+     </indexterm>
+    </term>
+
+    <listitem>
+     <para>
+      <function>fsm_page_contents</function> shows the internal node structure
+      of a FSM page. The output is a multiline string, with one line per
+      node in the binary tree within the page. Only those nodes that are not
+      zero are printed. The so-called "next" pointer, which points to the
+      next slot to be returned from the page, is also printed.
+     </para>
+     <para>
+      See <filename>src/backend/storage/freespace/README</> for more
+      information on the structure of an FSM page.
+     </para>
+    </listitem>
+   </varlistentry>
+</variablelist>
+</sect2>
    
+ <sect2>
+  <title>B-tree functions</title>
+
+  <variablelist>
    <varlistentry>
     <term>
      <function>bt_metap(relname text) returns record</function>
@@ -261,7 +290,13 @@ test=# SELECT * FROM bt_page_items('pg_cast_oid_index', 1);
      </para>
     </listitem>
    </varlistentry>
+  </variablelist>
+ </sect2>
+   
+ <sect2>
+  <title>BRIN functions</title>
 
+  <variablelist>
    <varlistentry>
     <term>
      <function>brin_page_type(page bytea) returns text</function>
@@ -365,7 +400,13 @@ test=# SELECT * FROM brin_page_items(get_raw_page('brinidx', 5),
      </para>
     </listitem>
    </varlistentry>
+  </variablelist>
+ </sect2>
+   
+ <sect2>
+  <title>GIN functions</title>
 
+  <variablelist>
    <varlistentry>
     <term>
      <function>gin_metapage_info(page bytea) returns record</function>
@@ -449,26 +490,111 @@ test=# SELECT first_tid, nbytes, tids[0:5] as some_tids
      </para>
     </listitem>
    </varlistentry>
+  </variablelist>
+ </sect2>
+   
+ <sect2>
+  <title>Hash functions</title>
 
+  <para>
+    The hash index data structures are defined in <filename>src/include/access/hash.h</>.
+  </para>
+
+  <variablelist>
    <varlistentry>
     <term>
-     <function>fsm_page_contents(page bytea) returns text</function>
+     <function>hash_metapage_info(page bytes) returns record</function>
      <indexterm>
-      <primary>fsm_page_contents</primary>
+      <primary>hash_metapage_info</primary>
      </indexterm>
     </term>
 
     <listitem>
      <para>
-      <function>fsm_page_contents</function> shows the internal node structure
-      of a FSM page. The output is a multiline string, with one line per
-      node in the binary tree within the page. Only those nodes that are not
-      zero are printed. The so-called "next" pointer, which points to the
-      next slot to be returned from the page, is also printed.
+      <function>hash_metapage_info</function> returns information about a hash
+      index's metapage.  For example:
+<screen>
+test=# SELECT * FROM hash_metapage_info(get_raw_page('mytab_index', 0));
+-[ RECORD 1 ]-----
+magic     | 0x06440640
+version   | 2
+ntuples   | 1000000
+ffactor   | 307
+bsize     | 8152
+bmsize    | 4096
+bmshift   | 15
+maxbucket | 4095
+highmask  | 8191
+lowmask   | 4095
+ovflpoint | 12
+firstfree | 0
+nmaps     | 1
+procid    | 400
+spares    | 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 ...
+mapp      | 1001 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 ...
+</screen>
      </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
+     <function>hash_page_stats(page bytea) returns record</function>
+     <indexterm>
+      <primary>hash_page_stats</primary>
+     </indexterm>
+    </term>
+
+    <listitem>
      <para>
-      See <filename>src/backend/storage/freespace/README</> for more
-      information on the structure of an FSM page.
+      <function>hash_page_stats</function> returns summary information about
+      single pages of hash indexes.  For example:
+<screen>
+test=# SELECT * FROM hash_page_stats(get_raw_page('mytab_index', 1));
+-[ RECORD 1 ]---+-----
+type            | bucket
+live_items      | 211
+dead_items      | 0
+page_size       | 8192
+free_size       | 3928
+hasho_prevblkno | -1
+hasho_nextblkno | -1
+hasho_bucket    | 0
+hasho_flag      | 2
+hasho_page_id   | 65408
+</screen>
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
+     <function>hash_page_items(page bytea) returns setof record</function>
+     <indexterm>
+      <primary>hash_page_items</primary>
+     </indexterm>
+    </term>
+
+    <listitem>
+     <para>
+      <function>hash_page_items</function> returns detailed information about
+      all of the items on a hash index page.  For example:
+<screen>
+test=# SELECT * FROM hash_page_items(get_raw_page('mytab_index', 1));     
+ itemoffset |    ctid    |          data           
+------------+------------+-------------------------
+          1 | (2057,114) | 00 a0 25 02 00 00 00 00
+          2 | (4033,38)  | 00 20 8d 02 00 00 00 00
+          3 | (4223,130) | 00 80 f7 02 00 00 00 00
+          4 | (1524,126) | 00 70 de 04 00 00 00 00
+          5 | (5850,86)  | 00 70 1a 07 00 00 00 00
+...
+        210 | (5541,140) | 00 90 a9 ff 00 00 00 00
+        211 | (4654,93)  | 00 d0 fb ff 00 00 00 00
+
+</screen>
+      The <structfield>ctid</> column points to a heap tuple 
+      (BlockIdData,OffsetNumber).
      </para>
     </listitem>
    </varlistentry>
-- 
2.7.4

-- 
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