Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e759854

Browse files
committed
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.
1 parent 39b8cc9 commit e759854

File tree

5 files changed

+318
-0
lines changed

5 files changed

+318
-0
lines changed

contrib/pgstattuple/expected/pgstattuple.out

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,11 @@ select * from pgstatginindex('test_ginidx');
130130
2 | 0 | 0
131131
(1 row)
132132

133+
create index test_hashidx on test using hash (b);
134+
WARNING: hash indexes are not WAL-logged and their use is discouraged
135+
select * from pgstathashindex('test_hashidx');
136+
version | bucket_pages | overflow_pages | bitmap_pages | zero_pages | live_items | dead_items | free_percent
137+
---------+--------------+----------------+--------------+------------+------------+------------+--------------
138+
2 | 4 | 0 | 1 | 0 | 0 | 0 | 100
139+
(1 row)
140+

contrib/pgstattuple/pgstatindex.c

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@
2929

3030
#include "access/gin_private.h"
3131
#include "access/heapam.h"
32+
#include "access/hash.h"
3233
#include "access/htup_details.h"
3334
#include "access/nbtree.h"
3435
#include "catalog/namespace.h"
3536
#include "catalog/pg_am.h"
3637
#include "funcapi.h"
3738
#include "miscadmin.h"
3839
#include "storage/bufmgr.h"
40+
#include "storage/lmgr.h"
3941
#include "utils/builtins.h"
4042
#include "utils/rel.h"
4143
#include "utils/varlena.h"
@@ -54,6 +56,7 @@ PG_FUNCTION_INFO_V1(pgstatindexbyid);
5456
PG_FUNCTION_INFO_V1(pg_relpages);
5557
PG_FUNCTION_INFO_V1(pg_relpagesbyid);
5658
PG_FUNCTION_INFO_V1(pgstatginindex);
59+
PG_FUNCTION_INFO_V1(pgstathashindex);
5760

5861
PG_FUNCTION_INFO_V1(pgstatindex_v1_5);
5962
PG_FUNCTION_INFO_V1(pgstatindexbyid_v1_5);
@@ -66,6 +69,7 @@ Datum pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo);
6669
#define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
6770
#define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
6871
#define IS_GIN(r) ((r)->rd_rel->relam == GIN_AM_OID)
72+
#define IS_HASH(r) ((r)->rd_rel->relam == HASH_AM_OID)
6973

7074
/* ------------------------------------------------
7175
* A structure for a whole btree index statistics
@@ -102,7 +106,29 @@ typedef struct GinIndexStat
102106
int64 pending_tuples;
103107
} GinIndexStat;
104108

109+
/* ------------------------------------------------
110+
* A structure for a whole HASH index statistics
111+
* used by pgstathashindex().
112+
* ------------------------------------------------
113+
*/
114+
typedef struct HashIndexStat
115+
{
116+
int32 version;
117+
int32 space_per_page;
118+
119+
BlockNumber bucket_pages;
120+
BlockNumber overflow_pages;
121+
BlockNumber bitmap_pages;
122+
BlockNumber zero_pages;
123+
124+
int64 live_items;
125+
int64 dead_items;
126+
uint64 free_space;
127+
} HashIndexStat;
128+
105129
static Datum pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo);
130+
static void GetHashPageStats(Page page, HashIndexStat *stats);
131+
106132

107133
/* ------------------------------------------------------
108134
* pgstatindex()
@@ -528,3 +554,172 @@ pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo)
528554

529555
return (result);
530556
}
557+
558+
/* ------------------------------------------------------
559+
* pgstathashindex()
560+
*
561+
* Usage: SELECT * FROM pgstathashindex('hashindex');
562+
* ------------------------------------------------------
563+
*/
564+
Datum
565+
pgstathashindex(PG_FUNCTION_ARGS)
566+
{
567+
Oid relid = PG_GETARG_OID(0);
568+
BlockNumber nblocks;
569+
BlockNumber blkno;
570+
Relation rel;
571+
HashIndexStat stats;
572+
BufferAccessStrategy bstrategy;
573+
HeapTuple tuple;
574+
TupleDesc tupleDesc;
575+
Datum values[8];
576+
bool nulls[8];
577+
Buffer metabuf;
578+
HashMetaPage metap;
579+
float8 free_percent;
580+
uint64 total_space;
581+
582+
rel = index_open(relid, AccessShareLock);
583+
584+
if (!IS_HASH(rel))
585+
elog(ERROR, "relation \"%s\" is not a HASH index",
586+
RelationGetRelationName(rel));
587+
588+
/*
589+
* Reject attempts to read non-local temporary relations; we would be
590+
* likely to get wrong data since we have no visibility into the owning
591+
* session's local buffers.
592+
*/
593+
if (RELATION_IS_OTHER_TEMP(rel))
594+
ereport(ERROR,
595+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
596+
errmsg("cannot access temporary indexes of other sessions")));
597+
598+
/* Get the information we need from the metapage. */
599+
memset(&stats, 0, sizeof(stats));
600+
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
601+
metap = HashPageGetMeta(BufferGetPage(metabuf));
602+
stats.version = metap->hashm_version;
603+
stats.space_per_page = metap->hashm_bsize;
604+
_hash_relbuf(rel, metabuf);
605+
606+
/* Get the current relation length */
607+
nblocks = RelationGetNumberOfBlocks(rel);
608+
609+
/* prepare access strategy for this index */
610+
bstrategy = GetAccessStrategy(BAS_BULKREAD);
611+
612+
/* Start from blkno 1 as 0th block is metapage */
613+
for (blkno = 1; blkno < nblocks; blkno++)
614+
{
615+
Buffer buf;
616+
Page page;
617+
HashPageOpaque opaque;
618+
619+
CHECK_FOR_INTERRUPTS();
620+
621+
buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
622+
bstrategy);
623+
LockBuffer(buf, BUFFER_LOCK_SHARE);
624+
page = (Page) BufferGetPage(buf);
625+
626+
if (PageIsNew(page))
627+
stats.zero_pages++;
628+
else if (PageGetSpecialSize(page) !=
629+
MAXALIGN(sizeof(HashPageOpaqueData)))
630+
ereport(ERROR,
631+
(errcode(ERRCODE_INDEX_CORRUPTED),
632+
errmsg("index \"%s\" contains corrupted page at block %u",
633+
RelationGetRelationName(rel),
634+
BufferGetBlockNumber(buf))));
635+
else
636+
{
637+
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
638+
if (opaque->hasho_flag & LH_BUCKET_PAGE)
639+
{
640+
stats.bucket_pages++;
641+
GetHashPageStats(page, &stats);
642+
}
643+
else if (opaque->hasho_flag & LH_OVERFLOW_PAGE)
644+
{
645+
stats.overflow_pages++;
646+
GetHashPageStats(page, &stats);
647+
}
648+
else if (opaque->hasho_flag & LH_BITMAP_PAGE)
649+
stats.bitmap_pages++;
650+
else
651+
ereport(ERROR,
652+
(errcode(ERRCODE_INDEX_CORRUPTED),
653+
errmsg("unexpected page type 0x%04X in HASH index \"%s\" block %u",
654+
opaque->hasho_flag, RelationGetRelationName(rel),
655+
BufferGetBlockNumber(buf))));
656+
}
657+
UnlockReleaseBuffer(buf);
658+
}
659+
660+
/* Done accessing the index */
661+
index_close(rel, AccessShareLock);
662+
663+
/* Count zero pages as free space. */
664+
stats.free_space += stats.zero_pages * stats.space_per_page;
665+
666+
/*
667+
* Total space available for tuples excludes the metapage and the bitmap
668+
* pages.
669+
*/
670+
total_space = (nblocks - (stats.bitmap_pages + 1)) * stats.space_per_page;
671+
672+
if (total_space == 0)
673+
free_percent = 0.0;
674+
else
675+
free_percent = 100.0 * stats.free_space / total_space;
676+
677+
/*
678+
* Build a tuple descriptor for our result type
679+
*/
680+
if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
681+
elog(ERROR, "return type must be a row type");
682+
683+
tupleDesc = BlessTupleDesc(tupleDesc);
684+
685+
/*
686+
* Build and return the tuple
687+
*/
688+
MemSet(nulls, 0, sizeof(nulls));
689+
values[0] = Int32GetDatum(stats.version);
690+
values[1] = Int64GetDatum((int64) stats.bucket_pages);
691+
values[2] = Int64GetDatum((int64) stats.overflow_pages);
692+
values[3] = Int64GetDatum((int64) stats.bitmap_pages);
693+
values[4] = Int64GetDatum((int64) stats.zero_pages);
694+
values[5] = Int64GetDatum(stats.live_items);
695+
values[6] = Int64GetDatum(stats.dead_items);
696+
values[7] = Float8GetDatum(free_percent);
697+
tuple = heap_form_tuple(tupleDesc, values, nulls);
698+
699+
PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
700+
}
701+
702+
/* -------------------------------------------------
703+
* GetHashPageStatis()
704+
*
705+
* Collect statistics of single hash page
706+
* -------------------------------------------------
707+
*/
708+
static void
709+
GetHashPageStats(Page page, HashIndexStat *stats)
710+
{
711+
OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
712+
int off;
713+
714+
/* count live and dead tuples, and free space */
715+
for (off = FirstOffsetNumber; off <= maxoff; off++)
716+
{
717+
ItemId id = PageGetItemId(page, off);
718+
719+
if (!ItemIdIsDead(id))
720+
stats->live_items++;
721+
else
722+
stats->dead_items++;
723+
}
724+
stats->free_space += PageGetExactFreeSpace(page);
725+
}

contrib/pgstattuple/pgstattuple--1.4--1.5.sql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,19 @@ AS 'MODULE_PATHNAME', 'pgstattuple_approx_v1_5'
109109
LANGUAGE C STRICT PARALLEL SAFE;
110110

111111
REVOKE EXECUTE ON FUNCTION pgstattuple_approx(regclass) FROM PUBLIC;
112+
113+
/* New stuff in 1.5 begins here */
114+
115+
CREATE OR REPLACE FUNCTION pgstathashindex(IN relname regclass,
116+
OUT version INTEGER,
117+
OUT bucket_pages BIGINT,
118+
OUT overflow_pages BIGINT,
119+
OUT bitmap_pages BIGINT,
120+
OUT zero_pages BIGINT,
121+
OUT live_items BIGINT,
122+
OUT dead_items BIGINT,
123+
OUT free_percent FLOAT8)
124+
AS 'MODULE_PATHNAME', 'pgstathashindex'
125+
LANGUAGE C STRICT PARALLEL SAFE;
126+
127+
REVOKE EXECUTE ON FUNCTION pgstathashindex(regclass) FROM PUBLIC;

contrib/pgstattuple/sql/pgstattuple.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,7 @@ select pg_relpages(relname) from pg_class where relname = 'test_pkey';
4747
create index test_ginidx on test using gin (b);
4848

4949
select * from pgstatginindex('test_ginidx');
50+
51+
create index test_hashidx on test using hash (b);
52+
53+
select * from pgstathashindex('test_hashidx');

doc/src/sgml/pgstattuple.sgml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,101 @@ pending_tuples | 0
352352
</listitem>
353353
</varlistentry>
354354

355+
<varlistentry>
356+
<term>
357+
<indexterm>
358+
<primary>pgstathashindex</primary>
359+
</indexterm>
360+
<function>pgstathashindex(regclass) returns record</>
361+
</term>
362+
363+
<listitem>
364+
<para>
365+
<function>pgstathashindex</function> returns a record showing information
366+
about a HASH index. For example:
367+
<programlisting>
368+
test=&gt; select * from pgstathashindex('con_hash_index');
369+
-[ RECORD 1 ]--+-----------------
370+
version | 2
371+
bucket_pages | 33081
372+
overflow_pages | 0
373+
bitmap_pages | 1
374+
zero_pages | 32455
375+
live_items | 10204006
376+
dead_items | 0
377+
free_percent | 61.8005949100872
378+
</programlisting>
379+
</para>
380+
381+
<para>
382+
The output columns are:
383+
384+
<informaltable>
385+
<tgroup cols="3">
386+
<thead>
387+
<row>
388+
<entry>Column</entry>
389+
<entry>Type</entry>
390+
<entry>Description</entry>
391+
</row>
392+
</thead>
393+
394+
<tbody>
395+
<row>
396+
<entry><structfield>version</structfield></entry>
397+
<entry><type>integer</type></entry>
398+
<entry>HASH version number</entry>
399+
</row>
400+
401+
<row>
402+
<entry><structfield>bucket_pages</structfield></entry>
403+
<entry><type>bigint</type></entry>
404+
<entry>Number of bucket pages</entry>
405+
</row>
406+
407+
<row>
408+
<entry><structfield>overflow_pages</structfield></entry>
409+
<entry><type>bigint</type></entry>
410+
<entry>Number of overflow pages</entry>
411+
</row>
412+
413+
<row>
414+
<entry><structfield>bitmap_pages</structfield></entry>
415+
<entry><type>bigint</type></entry>
416+
<entry>Number of bitmap pages</entry>
417+
</row>
418+
419+
<row>
420+
<entry><structfield>zero_pages</structfield></entry>
421+
<entry><type>bigint</type></entry>
422+
<entry>Number of new or zero pages</entry>
423+
</row>
424+
425+
<row>
426+
<entry><structfield>live_items</structfield></entry>
427+
<entry><type>bigint</type></entry>
428+
<entry>Number of live tuples</entry>
429+
</row>
430+
431+
<row>
432+
<entry><structfield>dead_tuples</structfield></entry>
433+
<entry><type>bigint</type></entry>
434+
<entry>Number of dead tuples</entry>
435+
</row>
436+
437+
<row>
438+
<entry><structfield>free_percent</structfield></entry>
439+
<entry><type>float</type></entry>
440+
<entry>Percentage of free space</entry>
441+
</row>
442+
443+
</tbody>
444+
</tgroup>
445+
</informaltable>
446+
</para>
447+
</listitem>
448+
</varlistentry>
449+
355450
<varlistentry>
356451
<term>
357452
<indexterm>

0 commit comments

Comments
 (0)