Hi,

On 01/18/2017 04:54 AM, Ashutosh Sharma wrote:
Is there a reason for keeping the input arguments for
hash_bitmap_info() different from hash_page_items()?


Yes, there are two reasons behind it.

Firstly, we need metapage to identify the bitmap page that holds the
information about the overflow page passed as an input to this
function.

Secondly, we will have to input overflow block number as an input to
this function so as to determine the overflow bit number which can be
used further to identify the bitmap page.

+   /* Read the metapage so we can determine which bitmap page to use */
+   metabuf = _hash_getbuf(indexRel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
+   metap = HashPageGetMeta(BufferGetPage(metabuf));
+
+   /* Identify overflow bit number */
+   ovflbitno = _hash_ovflblkno_to_bitno(metap, ovflblkno);
+
+   bitmappage = ovflbitno >> BMPG_SHIFT(metap);
+   bitmapbit = ovflbitno & BMPG_MASK(metap);
+
+   if (bitmappage >= metap->hashm_nmaps)
+       ereport(ERROR,
+               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                errmsg("invalid overflow bit number %u", ovflbitno)));
+
+   bitmapblkno = metap->hashm_mapp[bitmappage];



As discussed off-list (along with my patch that included some of these fixes), it would require calculation from the user to be able to provide the necessary pages through get_raw_page().

Other ideas on how to improve this are most welcome.

Apart from above comments, the attached patch also handles all the
comments from Mithun.


Please, include a version number for your patch files in the future.

Fixed in this version:

* verify_hash_page: Display magic in hex, like hash_metapage_info
* Update header for hash_page_type

Moving the patch back to 'Needs Review'.

Best regards,
 Jesper

>From 8e5a68cfaf979bcab88fb9358b88a89bc780a277 Mon Sep 17 00:00:00 2001
From: jesperpedersen <jesper.peder...@redhat.com>
Date: Wed, 18 Jan 2017 08:42:02 -0500
Subject: [PATCH] Add support for hash index in pageinspect contrib module v15

Authors: Ashutosh Sharma and Jesper Pedersen.
---
 contrib/pageinspect/Makefile                  |  10 +-
 contrib/pageinspect/hashfuncs.c               | 574 ++++++++++++++++++++++++++
 contrib/pageinspect/pageinspect--1.5--1.6.sql |  76 ++++
 contrib/pageinspect/pageinspect.control       |   2 +-
 doc/src/sgml/pageinspect.sgml                 | 148 +++++++
 src/backend/access/hash/hashovfl.c            |   8 +-
 src/include/access/hash.h                     |   1 +
 7 files changed, 810 insertions(+), 9 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 87a28e9..052a8e1 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.5.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"
 
 REGRESS = page btree brin gin
diff --git a/contrib/pageinspect/hashfuncs.c b/contrib/pageinspect/hashfuncs.c
new file mode 100644
index 0000000..f157fcc
--- /dev/null
+++ b/contrib/pageinspect/hashfuncs.c
@@ -0,0 +1,574 @@
+/*
+ * hashfuncs.c
+ *		Functions to investigate the content of HASH indexes
+ *
+ * Copyright (c) 2017, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		contrib/pageinspect/hashfuncs.c
+ */
+
+#include "postgres.h"
+
+#include "access/hash.h"
+#include "access/htup_details.h"
+#include "catalog/pg_am.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+
+PG_FUNCTION_INFO_V1(hash_page_type);
+PG_FUNCTION_INFO_V1(hash_page_stats);
+PG_FUNCTION_INFO_V1(hash_page_items);
+PG_FUNCTION_INFO_V1(hash_bitmap_info);
+PG_FUNCTION_INFO_V1(hash_metapage_info);
+
+#define IS_HASH(r) ((r)->rd_rel->relam == HASH_AM_OID)
+
+/* ------------------------------------------------
+ * structure for single hash page statistics
+ * ------------------------------------------------
+ */
+typedef struct HashPageStat
+{
+	uint32		live_items;
+	uint32		dead_items;
+	uint32		page_size;
+	uint32		free_size;
+
+	/* 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, int flags)
+{
+	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("invalid page size"),
+				 errdetail("Expected size %d, got %d",
+						   BLCKSZ, raw_page_size)));
+
+	page = VARDATA(raw_page);
+
+	if (PageIsNew(page))
+		ereport(ERROR,
+				(errcode(ERRCODE_INDEX_CORRUPTED),
+				 errmsg("index table contains zero page")));
+
+	if (PageGetSpecialSize(page) != MAXALIGN(sizeof(HashPageOpaqueData)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INDEX_CORRUPTED),
+				 errmsg("index table contains corrupted 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 (!(pageopaque->hasho_flag & flags))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("page is not a hash page of expected type"),
+							errdetail("Expected hash page type: %08x got: %08x.",
+								flags, pageopaque->hasho_flag)));
+
+	/*
+	 * If it is metapage, also verify magic number and version.
+	 */
+	if (flags == LH_META_PAGE)
+	{
+		HashMetaPage metap = HashPageGetMeta(page);
+
+		if (metap->hashm_magic != HASH_MAGIC)
+			ereport(ERROR,
+					(errcode(ERRCODE_INDEX_CORRUPTED),
+					 errmsg("invalid magic number for metadata"),
+					 errdetail("Expected 0x%08X, got 0x%08X",
+							   HASH_MAGIC, metap->hashm_magic)));
+
+		if (metap->hashm_version != HASH_VERSION)
+			ereport(ERROR,
+					(errcode(ERRCODE_INDEX_CORRUPTED),
+					 errmsg("invalid version for metadata"),
+					 errdetail("Expected %d, got %d",
+							   HASH_VERSION, metap->hashm_version)));
+	}
+
+	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);
+
+	/* hash page opaque data */
+	if (opaque->hasho_flag & LH_BUCKET_PAGE)
+		stat->hasho_prevblkno = InvalidBlockNumber;
+	else
+		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_type()
+ *
+ * Usage: SELECT hash_page_type(get_raw_page('con_hash_index', 1));
+ * ---------------------------------------------------
+ */
+Datum
+hash_page_type(PG_FUNCTION_ARGS)
+{
+	bytea	*raw_page = PG_GETARG_BYTEA_P(0);
+	Page	page;
+	HashPageOpaque	opaque;
+	char	*type;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to use raw page functions"))));
+
+	page = verify_hash_page(raw_page, LH_META_PAGE | LH_BUCKET_PAGE |
+							LH_OVERFLOW_PAGE | LH_BITMAP_PAGE);
+	opaque = (HashPageOpaque) PageGetSpecialPointer(page);
+
+	/* page type (flags) */
+	if (opaque->hasho_flag & LH_META_PAGE)
+		type = "metapage";
+	else if (opaque->hasho_flag & LH_OVERFLOW_PAGE)
+		type = "overflow";
+	else if (opaque->hasho_flag & LH_BUCKET_PAGE)
+		type = "bucket";
+	else if (opaque->hasho_flag & LH_BITMAP_PAGE)
+		type = "bitmap";
+	else
+		type = "unused";
+
+	PG_RETURN_TEXT_P(cstring_to_text(type));
+}
+
+/* ---------------------------------------------------
+ * hash_page_stats()
+ *
+ * Usage: SELECT * FROM hash_page_stats(get_raw_page('con_hash_index', 1));
+ * ---------------------------------------------------
+ */
+Datum
+hash_page_stats(PG_FUNCTION_ARGS)
+{
+	bytea	   *raw_page = PG_GETARG_BYTEA_P(0);
+	Page		page;
+	int			j;
+	Datum       values[9];
+	bool        nulls[9];
+	HashPageStat	stat;
+	HeapTuple	tuple;
+	TupleDesc	tupleDesc;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to use raw page functions"))));
+
+	page = verify_hash_page(raw_page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
+
+	/* 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++] = 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('con_hash_index', 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 raw page functions"))));
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		TupleDesc	tupleDesc;
+
+		fctx = SRF_FIRSTCALL_INIT();
+
+		page = verify_hash_page(raw_page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
+
+		mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
+
+		uargs = palloc(sizeof(struct user_args));
+
+		uargs->page = page;
+
+		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	   *s;
+		char	   *ptr;
+		char       *dump;
+		int			number;
+
+		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);
+		Assert(ptr <= uargs->page + BLCKSZ);
+		dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
+		dump = palloc(dlen * 2 + 1);
+		s = dump;
+		for (off = (dlen - 1); off >= 0; off--)
+		{
+			sprintf(dump, "%02x", ptr[off] & 0xff);
+			dump += 2;
+		}
+		*dump++ = '\0';
+
+		number = (int) strtol(s, NULL, 16);
+
+		values[j] = Int32GetDatum(number & 0xFFFFFFFF);
+		pfree(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);
+		SRF_RETURN_DONE(fctx);
+	}
+}
+
+/* ------------------------------------------------
+ * hash_bitmap_info()
+ *
+ * Get bitmap information for a particular overflow page
+ *
+ * Usage: SELECT * FROM hash_bitmap_info('con_hash_index'::regclass, 5);
+ * ------------------------------------------------
+ */
+Datum
+hash_bitmap_info(PG_FUNCTION_ARGS)
+{
+	Oid		indexRelid = PG_GETARG_OID(0);
+	uint32	ovflblkno = PG_GETARG_UINT32(1);
+	bytea	*raw_page;
+	char	*raw_page_data;
+	HashMetaPage	metap;
+	Buffer	buf, metabuf, mapbuf;
+	BlockNumber	bitmapblkno;
+	Page	mappage;
+	TupleDesc	tupleDesc;
+	Relation	indexRel;
+	uint32		ovflbitno, bit = 0;
+	int32		bitmappage,bitmapbit;
+	HeapTuple	tuple;
+	int			j;
+	Datum		values[2];
+	bool		nulls[2];
+	uint32		*freep = NULL;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to use raw page functions"))));
+
+	indexRel = index_open(indexRelid, AccessShareLock);
+
+	if (!IS_HASH(indexRel))
+		elog(ERROR, "relation \"%s\" is not a hash index",
+				RelationGetRelationName(indexRel));
+
+	if (RELATION_IS_OTHER_TEMP(indexRel))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					errmsg("cannot access temporary tables of other sessions")));
+
+	if (RelationGetNumberOfBlocks(indexRel) <= (BlockNumber) (ovflblkno))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("block number %u is out of range for relation \"%s\"",
+						ovflblkno, RelationGetRelationName(indexRel))));
+
+	/* Create a raw page */
+	raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
+	SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
+	raw_page_data = VARDATA(raw_page);
+
+	buf = ReadBufferExtended(indexRel, MAIN_FORKNUM, ovflblkno, RBM_NORMAL, NULL);
+	LockBuffer(buf, BUFFER_LOCK_SHARE);
+
+	memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
+
+	LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+	ReleaseBuffer(buf);
+
+	verify_hash_page(raw_page, LH_OVERFLOW_PAGE);
+
+	/* Read the metapage so we can determine which bitmap page to use */
+	metabuf = _hash_getbuf(indexRel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
+	metap = HashPageGetMeta(BufferGetPage(metabuf));
+
+	/* Identify overflow bit number */
+	ovflbitno = _hash_ovflblkno_to_bitno(metap, ovflblkno);
+
+	bitmappage = ovflbitno >> BMPG_SHIFT(metap);
+	bitmapbit = ovflbitno & BMPG_MASK(metap);
+
+	if (bitmappage >= metap->hashm_nmaps)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid overflow bit number %u", ovflbitno)));
+
+	bitmapblkno = metap->hashm_mapp[bitmappage];
+
+	_hash_relbuf(indexRel, metabuf);
+
+	/* Check the status of bitmap bit for overflow page */
+	mapbuf = _hash_getbuf(indexRel, bitmapblkno, HASH_READ, LH_BITMAP_PAGE);
+	mappage = BufferGetPage(mapbuf);
+	freep = HashPageGetBitmap(mappage);
+
+	if (ISSET(freep, bitmapbit))
+		bit = 1;
+
+	_hash_relbuf(indexRel, mapbuf);
+
+	index_close(indexRel, AccessShareLock);
+
+	/* 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++] = UInt32GetDatum(bitmapblkno);
+	values[j++] = UInt32GetDatum(bit);
+
+	tuple = heap_form_tuple(tupleDesc, values, nulls);
+
+	PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+}
+
+/* ------------------------------------------------
+ * hash_metapage_info()
+ *
+ * Get the meta-page information for a hash index
+ *
+ * Usage: SELECT * FROM hash_metapage_info(get_raw_page('con_hash_index', 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 raw page functions"))));
+
+	page = verify_hash_page(raw_page, LH_META_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);
+
+	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);
+	pfree(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);
+	pfree(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..e159099
--- /dev/null
+++ b/contrib/pageinspect/pageinspect--1.5--1.6.sql
@@ -0,0 +1,76 @@
+/* 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_page_type()
+--
+CREATE FUNCTION hash_page_type(IN page bytea)
+RETURNS text
+AS 'MODULE_PATHNAME', 'hash_page_type'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- hash_page_stats()
+--
+CREATE FUNCTION hash_page_stats(IN page bytea,
+    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 int8)
+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 int8)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'hash_page_items'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- hash_bitmap_info()
+--
+CREATE FUNCTION hash_bitmap_info(IN index_oid regclass, IN blkno int4,
+    OUT bitmapblkno int4,
+    OUT bitmapbit int4)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'hash_bitmap_info'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- 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;
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 d12dbac..6022f27 100644
--- a/doc/src/sgml/pageinspect.sgml
+++ b/doc/src/sgml/pageinspect.sgml
@@ -493,4 +493,152 @@ test=# SELECT first_tid, nbytes, tids[0:5] AS some_tids
   </variablelist>
  </sect2>
 
+ <sect2>
+  <title>Hash Functions</title>
+
+  <variablelist>
+   <varlistentry>
+    <term>
+     <function>hash_page_type(page bytea) returns text</function>
+     <indexterm>
+      <primary>hash_page_type</primary>
+     </indexterm>
+    </term>
+
+    <listitem>
+     <para>
+      <function>hash_page_type</function> returns page type of
+      the given <acronym>HASH</acronym> index page.  For example:
+<screen>
+test=# SELECT hash_page_type(get_raw_page('con_hash_index', 0));
+ hash_page_type 
+----------------
+ metapage
+</screen>
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
+     <function>hash_page_stats(page bytea) returns setof record</function>
+     <indexterm>
+      <primary>hash_page_stats</primary>
+     </indexterm>
+    </term>
+
+    <listitem>
+     <para>
+      <function>hash_page_stats</function> returns information about
+      a bucket or overflow page of a <acronym>HASH</acronym> index.
+      For example:
+<screen>
+test=# SELECT * FROM hash_page_stats(get_raw_page('con_hash_index', 1));
+-[ RECORD 1 ]---+------
+live_items      | 407
+dead_items      | 0
+page_size       | 8192
+free_size       | 8
+hasho_prevblkno | -1
+hasho_nextblkno | 8474
+hasho_bucket    | 0
+hasho_flag      | 66
+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 information about
+      the data stored in a bucket or overflow page of a <acronym>HASH</acronym>
+      index page.  For example:
+<screen>
+test=# SELECT * FROM hash_page_items(get_raw_page('con_hash_index', 1));
+ itemoffset |      ctid       |    data    
+------------+-----------------+------------
+          1 | (3407872,12584) | 1053474816
+          2 | (3407872,12584) | 1053474816
+          3 | (3145728,12584) | 1053474816
+          4 | (3145728,12584) | 1053474816
+          5 | (3407872,12584) | 1053474816
+...
+        131 | (2883584,13352) | 2778469888
+        132 | (2883584,12840) | 2778469888
+        133 | (2883584,12328) | 2778469888
+</screen>
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
+     <function>hash_bitmap_info(index oid, blkno int) returns record</function>
+     <indexterm>
+      <primary>hash_bitmap_info</primary>
+     </indexterm>
+    </term>
+
+    <listitem>
+     <para>
+      <function>hash_bitmap_info</function> shows the status of a bit
+      in the bitmap page for a particular overflow page of <acronym>HASH</acronym>
+      index. For example:
+<screen>
+test=# SELECT * FROM hash_bitmap_info('con_hash_index', 2050);
+ bitmapblkno | bitmapbit 
+-------------+-----------
+          65 |         1
+</screen>
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
+     <function>hash_metapage_info(page bytea) returns record</function>
+     <indexterm>
+      <primary>hash_metapage_info</primary>
+     </indexterm>
+    </term>
+
+    <listitem>
+     <para>
+      <function>hash_metapage_info</function> returns information stored
+      in meta page of a <acronym>HASH</acronym> index.  For example:
+<screen>
+test=# SELECT * FROM hash_metapage_info(get_raw_page('con_hash_index', 0));
+-[ RECORD 1 ]-----------
+magic     | 0x06440640
+version   | 2
+ntuples   | 686000
+ffactor   | 40
+bsize     | 8152
+bmsize    | 4096
+bmshift   | 15
+maxbucket | 17149
+highmask  | 32767
+lowmask   | 16383
+ovflpoint | 15
+firstfree | 2012
+nmaps     | 1
+procid    | 450
+spares    | 0000 0000 0000 0000 0000 0000 0001 0001 0001 0001 0001 0004 003b 02c0 07c3 07dc 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
+mapp      | 0041 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
+</screen>
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </sect2>
+
 </sect1>
diff --git a/src/backend/access/hash/hashovfl.c b/src/backend/access/hash/hashovfl.c
index 504670b..b0ab3d3 100644
--- a/src/backend/access/hash/hashovfl.c
+++ b/src/backend/access/hash/hashovfl.c
@@ -53,10 +53,12 @@ bitno_to_blkno(HashMetaPage metap, uint32 ovflbitnum)
 }
 
 /*
+ * _hash_ovflblkno_to_bitno
+ *
  * Convert overflow page block number to bit number for free-page bitmap.
  */
-static uint32
-blkno_to_bitno(HashMetaPage metap, BlockNumber ovflblkno)
+uint32
+_hash_ovflblkno_to_bitno(HashMetaPage metap, BlockNumber ovflblkno)
 {
 	uint32		splitnum = metap->hashm_ovflpoint;
 	uint32		i;
@@ -558,7 +560,7 @@ _hash_freeovflpage(Relation rel, Buffer bucketbuf, Buffer ovflbuf,
 	metap = HashPageGetMeta(BufferGetPage(metabuf));
 
 	/* Identify which bit to set */
-	ovflbitno = blkno_to_bitno(metap, ovflblkno);
+	ovflbitno = _hash_ovflblkno_to_bitno(metap, ovflblkno);
 
 	bitmappage = ovflbitno >> BMPG_SHIFT(metap);
 	bitmapbit = ovflbitno & BMPG_MASK(metap);
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index b43d30c..7ec6ec5 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -301,6 +301,7 @@ extern void _hash_squeezebucket(Relation rel,
 					Bucket bucket, BlockNumber bucket_blkno,
 					Buffer bucket_buf,
 					BufferAccessStrategy bstrategy);
+extern uint32 _hash_ovflblkno_to_bitno(HashMetaPage metap, BlockNumber ovflblkno);
 
 /* hashpage.c */
 extern Buffer _hash_getbuf(Relation rel, BlockNumber blkno,
-- 
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