29
29
30
30
#include "access/gin_private.h"
31
31
#include "access/heapam.h"
32
+ #include "access/hash.h"
32
33
#include "access/htup_details.h"
33
34
#include "access/nbtree.h"
34
35
#include "catalog/namespace.h"
35
36
#include "catalog/pg_am.h"
36
37
#include "funcapi.h"
37
38
#include "miscadmin.h"
38
39
#include "storage/bufmgr.h"
40
+ #include "storage/lmgr.h"
39
41
#include "utils/builtins.h"
40
42
#include "utils/rel.h"
41
43
#include "utils/varlena.h"
@@ -54,6 +56,7 @@ PG_FUNCTION_INFO_V1(pgstatindexbyid);
54
56
PG_FUNCTION_INFO_V1 (pg_relpages );
55
57
PG_FUNCTION_INFO_V1 (pg_relpagesbyid );
56
58
PG_FUNCTION_INFO_V1 (pgstatginindex );
59
+ PG_FUNCTION_INFO_V1 (pgstathashindex );
57
60
58
61
PG_FUNCTION_INFO_V1 (pgstatindex_v1_5 );
59
62
PG_FUNCTION_INFO_V1 (pgstatindexbyid_v1_5 );
@@ -66,6 +69,7 @@ Datum pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo);
66
69
#define IS_INDEX (r ) ((r)->rd_rel->relkind == RELKIND_INDEX)
67
70
#define IS_BTREE (r ) ((r)->rd_rel->relam == BTREE_AM_OID)
68
71
#define IS_GIN (r ) ((r)->rd_rel->relam == GIN_AM_OID)
72
+ #define IS_HASH (r ) ((r)->rd_rel->relam == HASH_AM_OID)
69
73
70
74
/* ------------------------------------------------
71
75
* A structure for a whole btree index statistics
@@ -102,7 +106,29 @@ typedef struct GinIndexStat
102
106
int64 pending_tuples ;
103
107
} GinIndexStat ;
104
108
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
+
105
129
static Datum pgstatindex_impl (Relation rel , FunctionCallInfo fcinfo );
130
+ static void GetHashPageStats (Page page , HashIndexStat * stats );
131
+
106
132
107
133
/* ------------------------------------------------------
108
134
* pgstatindex()
@@ -528,3 +554,172 @@ pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo)
528
554
529
555
return (result );
530
556
}
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
+ }
0 commit comments