pgstattuple: Add pgstathashindex.

Since pgstattuple v1.5 hasn't been released yet, no need for a new
extension version.  The new function exposes statistics about hash
indexes similar to what other pgstatindex functions return for other
index types.

Ashutosh Sharma, reviewed by Kuntal Ghosh.  Substantial further
revisions by me.
This commit is contained in:
Robert Haas 2017-02-03 14:35:25 -05:00
parent 39b8cc991f
commit e759854a09
5 changed files with 318 additions and 0 deletions

View File

@ -130,3 +130,11 @@ select * from pgstatginindex('test_ginidx');
2 | 0 | 0
(1 row)
create index test_hashidx on test using hash (b);
WARNING: hash indexes are not WAL-logged and their use is discouraged
select * from pgstathashindex('test_hashidx');
version | bucket_pages | overflow_pages | bitmap_pages | zero_pages | live_items | dead_items | free_percent
---------+--------------+----------------+--------------+------------+------------+------------+--------------
2 | 4 | 0 | 1 | 0 | 0 | 0 | 100
(1 row)

View File

@ -29,6 +29,7 @@
#include "access/gin_private.h"
#include "access/heapam.h"
#include "access/hash.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "catalog/namespace.h"
@ -36,6 +37,7 @@
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/rel.h"
#include "utils/varlena.h"
@ -54,6 +56,7 @@ PG_FUNCTION_INFO_V1(pgstatindexbyid);
PG_FUNCTION_INFO_V1(pg_relpages);
PG_FUNCTION_INFO_V1(pg_relpagesbyid);
PG_FUNCTION_INFO_V1(pgstatginindex);
PG_FUNCTION_INFO_V1(pgstathashindex);
PG_FUNCTION_INFO_V1(pgstatindex_v1_5);
PG_FUNCTION_INFO_V1(pgstatindexbyid_v1_5);
@ -66,6 +69,7 @@ Datum pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo);
#define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
#define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
#define IS_GIN(r) ((r)->rd_rel->relam == GIN_AM_OID)
#define IS_HASH(r) ((r)->rd_rel->relam == HASH_AM_OID)
/* ------------------------------------------------
* A structure for a whole btree index statistics
@ -102,7 +106,29 @@ typedef struct GinIndexStat
int64 pending_tuples;
} GinIndexStat;
/* ------------------------------------------------
* A structure for a whole HASH index statistics
* used by pgstathashindex().
* ------------------------------------------------
*/
typedef struct HashIndexStat
{
int32 version;
int32 space_per_page;
BlockNumber bucket_pages;
BlockNumber overflow_pages;
BlockNumber bitmap_pages;
BlockNumber zero_pages;
int64 live_items;
int64 dead_items;
uint64 free_space;
} HashIndexStat;
static Datum pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo);
static void GetHashPageStats(Page page, HashIndexStat *stats);
/* ------------------------------------------------------
* pgstatindex()
@ -528,3 +554,172 @@ pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo)
return (result);
}
/* ------------------------------------------------------
* pgstathashindex()
*
* Usage: SELECT * FROM pgstathashindex('hashindex');
* ------------------------------------------------------
*/
Datum
pgstathashindex(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
BlockNumber nblocks;
BlockNumber blkno;
Relation rel;
HashIndexStat stats;
BufferAccessStrategy bstrategy;
HeapTuple tuple;
TupleDesc tupleDesc;
Datum values[8];
bool nulls[8];
Buffer metabuf;
HashMetaPage metap;
float8 free_percent;
uint64 total_space;
rel = index_open(relid, AccessShareLock);
if (!IS_HASH(rel))
elog(ERROR, "relation \"%s\" is not a HASH index",
RelationGetRelationName(rel));
/*
* Reject attempts to read non-local temporary relations; we would be
* likely to get wrong data since we have no visibility into the owning
* session's local buffers.
*/
if (RELATION_IS_OTHER_TEMP(rel))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot access temporary indexes of other sessions")));
/* Get the information we need from the metapage. */
memset(&stats, 0, sizeof(stats));
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
metap = HashPageGetMeta(BufferGetPage(metabuf));
stats.version = metap->hashm_version;
stats.space_per_page = metap->hashm_bsize;
_hash_relbuf(rel, metabuf);
/* Get the current relation length */
nblocks = RelationGetNumberOfBlocks(rel);
/* prepare access strategy for this index */
bstrategy = GetAccessStrategy(BAS_BULKREAD);
/* Start from blkno 1 as 0th block is metapage */
for (blkno = 1; blkno < nblocks; blkno++)
{
Buffer buf;
Page page;
HashPageOpaque opaque;
CHECK_FOR_INTERRUPTS();
buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
bstrategy);
LockBuffer(buf, BUFFER_LOCK_SHARE);
page = (Page) BufferGetPage(buf);
if (PageIsNew(page))
stats.zero_pages++;
else if (PageGetSpecialSize(page) !=
MAXALIGN(sizeof(HashPageOpaqueData)))
ereport(ERROR,
(errcode(ERRCODE_INDEX_CORRUPTED),
errmsg("index \"%s\" contains corrupted page at block %u",
RelationGetRelationName(rel),
BufferGetBlockNumber(buf))));
else
{
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
if (opaque->hasho_flag & LH_BUCKET_PAGE)
{
stats.bucket_pages++;
GetHashPageStats(page, &stats);
}
else if (opaque->hasho_flag & LH_OVERFLOW_PAGE)
{
stats.overflow_pages++;
GetHashPageStats(page, &stats);
}
else if (opaque->hasho_flag & LH_BITMAP_PAGE)
stats.bitmap_pages++;
else
ereport(ERROR,
(errcode(ERRCODE_INDEX_CORRUPTED),
errmsg("unexpected page type 0x%04X in HASH index \"%s\" block %u",
opaque->hasho_flag, RelationGetRelationName(rel),
BufferGetBlockNumber(buf))));
}
UnlockReleaseBuffer(buf);
}
/* Done accessing the index */
index_close(rel, AccessShareLock);
/* Count zero pages as free space. */
stats.free_space += stats.zero_pages * stats.space_per_page;
/*
* Total space available for tuples excludes the metapage and the bitmap
* pages.
*/
total_space = (nblocks - (stats.bitmap_pages + 1)) * stats.space_per_page;
if (total_space == 0)
free_percent = 0.0;
else
free_percent = 100.0 * stats.free_space / total_space;
/*
* 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);
/*
* Build and return the tuple
*/
MemSet(nulls, 0, sizeof(nulls));
values[0] = Int32GetDatum(stats.version);
values[1] = Int64GetDatum((int64) stats.bucket_pages);
values[2] = Int64GetDatum((int64) stats.overflow_pages);
values[3] = Int64GetDatum((int64) stats.bitmap_pages);
values[4] = Int64GetDatum((int64) stats.zero_pages);
values[5] = Int64GetDatum(stats.live_items);
values[6] = Int64GetDatum(stats.dead_items);
values[7] = Float8GetDatum(free_percent);
tuple = heap_form_tuple(tupleDesc, values, nulls);
PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
}
/* -------------------------------------------------
* GetHashPageStatis()
*
* Collect statistics of single hash page
* -------------------------------------------------
*/
static void
GetHashPageStats(Page page, HashIndexStat *stats)
{
OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
int off;
/* count live and dead tuples, and free space */
for (off = FirstOffsetNumber; off <= maxoff; off++)
{
ItemId id = PageGetItemId(page, off);
if (!ItemIdIsDead(id))
stats->live_items++;
else
stats->dead_items++;
}
stats->free_space += PageGetExactFreeSpace(page);
}

View File

@ -109,3 +109,19 @@ AS 'MODULE_PATHNAME', 'pgstattuple_approx_v1_5'
LANGUAGE C STRICT PARALLEL SAFE;
REVOKE EXECUTE ON FUNCTION pgstattuple_approx(regclass) FROM PUBLIC;
/* New stuff in 1.5 begins here */
CREATE OR REPLACE FUNCTION pgstathashindex(IN relname regclass,
OUT version INTEGER,
OUT bucket_pages BIGINT,
OUT overflow_pages BIGINT,
OUT bitmap_pages BIGINT,
OUT zero_pages BIGINT,
OUT live_items BIGINT,
OUT dead_items BIGINT,
OUT free_percent FLOAT8)
AS 'MODULE_PATHNAME', 'pgstathashindex'
LANGUAGE C STRICT PARALLEL SAFE;
REVOKE EXECUTE ON FUNCTION pgstathashindex(regclass) FROM PUBLIC;

View File

@ -47,3 +47,7 @@ select pg_relpages(relname) from pg_class where relname = 'test_pkey';
create index test_ginidx on test using gin (b);
select * from pgstatginindex('test_ginidx');
create index test_hashidx on test using hash (b);
select * from pgstathashindex('test_hashidx');

View File

@ -352,6 +352,101 @@ pending_tuples | 0
</listitem>
</varlistentry>
<varlistentry>
<term>
<indexterm>
<primary>pgstathashindex</primary>
</indexterm>
<function>pgstathashindex(regclass) returns record</>
</term>
<listitem>
<para>
<function>pgstathashindex</function> returns a record showing information
about a HASH index. For example:
<programlisting>
test=&gt; select * from pgstathashindex('con_hash_index');
-[ RECORD 1 ]--+-----------------
version | 2
bucket_pages | 33081
overflow_pages | 0
bitmap_pages | 1
zero_pages | 32455
live_items | 10204006
dead_items | 0
free_percent | 61.8005949100872
</programlisting>
</para>
<para>
The output columns are:
<informaltable>
<tgroup cols="3">
<thead>
<row>
<entry>Column</entry>
<entry>Type</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><structfield>version</structfield></entry>
<entry><type>integer</type></entry>
<entry>HASH version number</entry>
</row>
<row>
<entry><structfield>bucket_pages</structfield></entry>
<entry><type>bigint</type></entry>
<entry>Number of bucket pages</entry>
</row>
<row>
<entry><structfield>overflow_pages</structfield></entry>
<entry><type>bigint</type></entry>
<entry>Number of overflow pages</entry>
</row>
<row>
<entry><structfield>bitmap_pages</structfield></entry>
<entry><type>bigint</type></entry>
<entry>Number of bitmap pages</entry>
</row>
<row>
<entry><structfield>zero_pages</structfield></entry>
<entry><type>bigint</type></entry>
<entry>Number of new or zero pages</entry>
</row>
<row>
<entry><structfield>live_items</structfield></entry>
<entry><type>bigint</type></entry>
<entry>Number of live tuples</entry>
</row>
<row>
<entry><structfield>dead_tuples</structfield></entry>
<entry><type>bigint</type></entry>
<entry>Number of dead tuples</entry>
</row>
<row>
<entry><structfield>free_percent</structfield></entry>
<entry><type>float</type></entry>
<entry>Percentage of free space</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<indexterm>