LCOV - code coverage report
Current view: top level - contrib/pgstattuple - pgstattuple.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 38.5 % 192 74
Test Date: 2026-03-03 14:15:12 Functions: 58.8 % 17 10
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * contrib/pgstattuple/pgstattuple.c
       3              :  *
       4              :  * Copyright (c) 2001,2002  Tatsuo Ishii
       5              :  *
       6              :  * Permission to use, copy, modify, and distribute this software and
       7              :  * its documentation for any purpose, without fee, and without a
       8              :  * written agreement is hereby granted, provided that the above
       9              :  * copyright notice and this paragraph and the following two
      10              :  * paragraphs appear in all copies.
      11              :  *
      12              :  * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
      13              :  * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
      14              :  * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
      15              :  * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED
      16              :  * OF THE POSSIBILITY OF SUCH DAMAGE.
      17              :  *
      18              :  * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
      19              :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      20              :  * A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS
      21              :  * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
      22              :  * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
      23              :  */
      24              : 
      25              : #include "postgres.h"
      26              : 
      27              : #include "access/gist_private.h"
      28              : #include "access/hash.h"
      29              : #include "access/heapam.h"
      30              : #include "access/nbtree.h"
      31              : #include "access/relscan.h"
      32              : #include "access/tableam.h"
      33              : #include "catalog/namespace.h"
      34              : #include "catalog/pg_am_d.h"
      35              : #include "funcapi.h"
      36              : #include "miscadmin.h"
      37              : #include "storage/bufmgr.h"
      38              : #include "storage/lmgr.h"
      39              : #include "utils/varlena.h"
      40              : 
      41            1 : PG_MODULE_MAGIC_EXT(
      42              :                     .name = "pgstattuple",
      43              :                     .version = PG_VERSION
      44              : );
      45              : 
      46            1 : PG_FUNCTION_INFO_V1(pgstattuple);
      47            2 : PG_FUNCTION_INFO_V1(pgstattuple_v1_5);
      48            1 : PG_FUNCTION_INFO_V1(pgstattuplebyid);
      49            2 : PG_FUNCTION_INFO_V1(pgstattuplebyid_v1_5);
      50              : 
      51              : /*
      52              :  * struct pgstattuple_type
      53              :  *
      54              :  * tuple_percent, dead_tuple_percent and free_percent are computable,
      55              :  * so not defined here.
      56              :  */
      57              : typedef struct pgstattuple_type
      58              : {
      59              :     uint64      table_len;
      60              :     uint64      tuple_count;
      61              :     uint64      tuple_len;
      62              :     uint64      dead_tuple_count;
      63              :     uint64      dead_tuple_len;
      64              :     uint64      free_space;     /* free/reusable space in bytes */
      65              : } pgstattuple_type;
      66              : 
      67              : typedef void (*pgstat_page) (pgstattuple_type *, Relation, BlockNumber,
      68              :                              BufferAccessStrategy);
      69              : 
      70              : static Datum build_pgstattuple_type(pgstattuple_type *stat,
      71              :                                     FunctionCallInfo fcinfo);
      72              : static Datum pgstat_relation(Relation rel, FunctionCallInfo fcinfo);
      73              : static Datum pgstat_heap(Relation rel, FunctionCallInfo fcinfo);
      74              : static void pgstat_btree_page(pgstattuple_type *stat,
      75              :                               Relation rel, BlockNumber blkno,
      76              :                               BufferAccessStrategy bstrategy);
      77              : static void pgstat_hash_page(pgstattuple_type *stat,
      78              :                              Relation rel, BlockNumber blkno,
      79              :                              BufferAccessStrategy bstrategy);
      80              : static void pgstat_gist_page(pgstattuple_type *stat,
      81              :                              Relation rel, BlockNumber blkno,
      82              :                              BufferAccessStrategy bstrategy);
      83              : static Datum pgstat_index(Relation rel, BlockNumber start,
      84              :                           pgstat_page pagefn, FunctionCallInfo fcinfo);
      85              : static void pgstat_index_page(pgstattuple_type *stat, Page page,
      86              :                               OffsetNumber minoff, OffsetNumber maxoff);
      87              : 
      88              : /*
      89              :  * build_pgstattuple_type -- build a pgstattuple_type tuple
      90              :  */
      91              : static Datum
      92            9 : build_pgstattuple_type(pgstattuple_type *stat, FunctionCallInfo fcinfo)
      93              : {
      94              : #define NCOLUMNS    9
      95              : #define NCHARS      314
      96              : 
      97              :     HeapTuple   tuple;
      98              :     char       *values[NCOLUMNS];
      99              :     char        values_buf[NCOLUMNS][NCHARS];
     100              :     int         i;
     101              :     double      tuple_percent;
     102              :     double      dead_tuple_percent;
     103              :     double      free_percent;   /* free/reusable space in % */
     104              :     TupleDesc   tupdesc;
     105              :     AttInMetadata *attinmeta;
     106              : 
     107              :     /* Build a tuple descriptor for our result type */
     108            9 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     109            0 :         elog(ERROR, "return type must be a row type");
     110              : 
     111              :     /*
     112              :      * Generate attribute metadata needed later to produce tuples from raw C
     113              :      * strings
     114              :      */
     115            9 :     attinmeta = TupleDescGetAttInMetadata(tupdesc);
     116              : 
     117            9 :     if (stat->table_len == 0)
     118              :     {
     119            8 :         tuple_percent = 0.0;
     120            8 :         dead_tuple_percent = 0.0;
     121            8 :         free_percent = 0.0;
     122              :     }
     123              :     else
     124              :     {
     125            1 :         tuple_percent = 100.0 * stat->tuple_len / stat->table_len;
     126            1 :         dead_tuple_percent = 100.0 * stat->dead_tuple_len / stat->table_len;
     127            1 :         free_percent = 100.0 * stat->free_space / stat->table_len;
     128              :     }
     129              : 
     130              :     /*
     131              :      * Prepare a values array for constructing the tuple. This should be an
     132              :      * array of C strings which will be processed later by the appropriate
     133              :      * "in" functions.
     134              :      */
     135           90 :     for (i = 0; i < NCOLUMNS; i++)
     136           81 :         values[i] = values_buf[i];
     137            9 :     i = 0;
     138            9 :     snprintf(values[i++], NCHARS, INT64_FORMAT, stat->table_len);
     139            9 :     snprintf(values[i++], NCHARS, INT64_FORMAT, stat->tuple_count);
     140            9 :     snprintf(values[i++], NCHARS, INT64_FORMAT, stat->tuple_len);
     141            9 :     snprintf(values[i++], NCHARS, "%.2f", tuple_percent);
     142            9 :     snprintf(values[i++], NCHARS, INT64_FORMAT, stat->dead_tuple_count);
     143            9 :     snprintf(values[i++], NCHARS, INT64_FORMAT, stat->dead_tuple_len);
     144            9 :     snprintf(values[i++], NCHARS, "%.2f", dead_tuple_percent);
     145            9 :     snprintf(values[i++], NCHARS, INT64_FORMAT, stat->free_space);
     146            9 :     snprintf(values[i++], NCHARS, "%.2f", free_percent);
     147              : 
     148              :     /* build a tuple */
     149            9 :     tuple = BuildTupleFromCStrings(attinmeta, values);
     150              : 
     151              :     /* make the tuple into a datum */
     152            9 :     return HeapTupleGetDatum(tuple);
     153              : }
     154              : 
     155              : /* ----------
     156              :  * pgstattuple:
     157              :  * returns live/dead tuples info
     158              :  *
     159              :  * C FUNCTION definition
     160              :  * pgstattuple(text) returns pgstattuple_type
     161              :  *
     162              :  * The superuser() check here must be kept as the library might be upgraded
     163              :  * without the extension being upgraded, meaning that in pre-1.5 installations
     164              :  * these functions could be called by any user.
     165              :  * ----------
     166              :  */
     167              : 
     168              : Datum
     169            0 : pgstattuple(PG_FUNCTION_ARGS)
     170              : {
     171            0 :     text       *relname = PG_GETARG_TEXT_PP(0);
     172              :     RangeVar   *relrv;
     173              :     Relation    rel;
     174              : 
     175            0 :     if (!superuser())
     176            0 :         ereport(ERROR,
     177              :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     178              :                  errmsg("must be superuser to use pgstattuple functions")));
     179              : 
     180              :     /* open relation */
     181            0 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     182            0 :     rel = relation_openrv(relrv, AccessShareLock);
     183              : 
     184            0 :     PG_RETURN_DATUM(pgstat_relation(rel, fcinfo));
     185              : }
     186              : 
     187              : /*
     188              :  * As of pgstattuple version 1.5, we no longer need to check if the user
     189              :  * is a superuser because we REVOKE EXECUTE on the function from PUBLIC.
     190              :  * Users can then grant access to it based on their policies.
     191              :  *
     192              :  * Otherwise identical to pgstattuple (above).
     193              :  */
     194              : Datum
     195           10 : pgstattuple_v1_5(PG_FUNCTION_ARGS)
     196              : {
     197           10 :     text       *relname = PG_GETARG_TEXT_PP(0);
     198              :     RangeVar   *relrv;
     199              :     Relation    rel;
     200              : 
     201              :     /* open relation */
     202           10 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     203           10 :     rel = relation_openrv(relrv, AccessShareLock);
     204              : 
     205           10 :     PG_RETURN_DATUM(pgstat_relation(rel, fcinfo));
     206              : }
     207              : 
     208              : /* Must keep superuser() check, see above. */
     209              : Datum
     210            0 : pgstattuplebyid(PG_FUNCTION_ARGS)
     211              : {
     212            0 :     Oid         relid = PG_GETARG_OID(0);
     213              :     Relation    rel;
     214              : 
     215            0 :     if (!superuser())
     216            0 :         ereport(ERROR,
     217              :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     218              :                  errmsg("must be superuser to use pgstattuple functions")));
     219              : 
     220              :     /* open relation */
     221            0 :     rel = relation_open(relid, AccessShareLock);
     222              : 
     223            0 :     PG_RETURN_DATUM(pgstat_relation(rel, fcinfo));
     224              : }
     225              : 
     226              : /* Remove superuser() check for 1.5 version, see above */
     227              : Datum
     228            3 : pgstattuplebyid_v1_5(PG_FUNCTION_ARGS)
     229              : {
     230            3 :     Oid         relid = PG_GETARG_OID(0);
     231              :     Relation    rel;
     232              : 
     233              :     /* open relation */
     234            3 :     rel = relation_open(relid, AccessShareLock);
     235              : 
     236            3 :     PG_RETURN_DATUM(pgstat_relation(rel, fcinfo));
     237              : }
     238              : 
     239              : /*
     240              :  * pgstat_relation
     241              :  */
     242              : static Datum
     243           13 : pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
     244              : {
     245              :     const char *err;
     246              : 
     247              :     /*
     248              :      * Reject attempts to read non-local temporary relations; we would be
     249              :      * likely to get wrong data since we have no visibility into the owning
     250              :      * session's local buffers.
     251              :      */
     252           13 :     if (RELATION_IS_OTHER_TEMP(rel))
     253            0 :         ereport(ERROR,
     254              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     255              :                  errmsg("cannot access temporary tables of other sessions")));
     256              : 
     257           13 :     if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind) ||
     258            5 :         rel->rd_rel->relkind == RELKIND_SEQUENCE)
     259              :     {
     260            9 :         return pgstat_heap(rel, fcinfo);
     261              :     }
     262            4 :     else if (rel->rd_rel->relkind == RELKIND_INDEX)
     263              :     {
     264              :         /* see pgstatindex_impl */
     265            0 :         if (!rel->rd_index->indisvalid)
     266            0 :             ereport(ERROR,
     267              :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     268              :                      errmsg("index \"%s\" is not valid",
     269              :                             RelationGetRelationName(rel))));
     270              : 
     271            0 :         switch (rel->rd_rel->relam)
     272              :         {
     273            0 :             case BTREE_AM_OID:
     274            0 :                 return pgstat_index(rel, BTREE_METAPAGE + 1,
     275              :                                     pgstat_btree_page, fcinfo);
     276            0 :             case HASH_AM_OID:
     277            0 :                 return pgstat_index(rel, HASH_METAPAGE + 1,
     278              :                                     pgstat_hash_page, fcinfo);
     279            0 :             case GIST_AM_OID:
     280            0 :                 return pgstat_index(rel, GIST_ROOT_BLKNO + 1,
     281              :                                     pgstat_gist_page, fcinfo);
     282            0 :             case GIN_AM_OID:
     283            0 :                 err = "gin index";
     284            0 :                 break;
     285            0 :             case SPGIST_AM_OID:
     286            0 :                 err = "spgist index";
     287            0 :                 break;
     288            0 :             case BRIN_AM_OID:
     289            0 :                 err = "brin index";
     290            0 :                 break;
     291            0 :             default:
     292            0 :                 err = "unknown index";
     293            0 :                 break;
     294              :         }
     295            0 :         ereport(ERROR,
     296              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     297              :                  errmsg("index \"%s\" (%s) is not supported",
     298              :                         RelationGetRelationName(rel), err)));
     299              :     }
     300              :     else
     301              :     {
     302            4 :         ereport(ERROR,
     303              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     304              :                  errmsg("cannot get tuple-level statistics for relation \"%s\"",
     305              :                         RelationGetRelationName(rel)),
     306              :                  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
     307              :     }
     308              : 
     309              :     return 0;                   /* should not happen */
     310              : }
     311              : 
     312              : /*
     313              :  * pgstat_heap -- returns live/dead tuples info in a heap
     314              :  */
     315              : static Datum
     316            9 : pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
     317              : {
     318              :     TableScanDesc scan;
     319              :     HeapScanDesc hscan;
     320              :     HeapTuple   tuple;
     321              :     BlockNumber nblocks;
     322            9 :     BlockNumber block = 0;      /* next block to count free space in */
     323              :     BlockNumber tupblock;
     324              :     Buffer      buffer;
     325            9 :     pgstattuple_type stat = {0};
     326              :     SnapshotData SnapshotDirty;
     327              : 
     328              :     /*
     329              :      * Sequences always use heap AM, but they don't show that in the catalogs.
     330              :      */
     331            9 :     if (rel->rd_rel->relkind != RELKIND_SEQUENCE &&
     332            8 :         rel->rd_rel->relam != HEAP_TABLE_AM_OID)
     333            0 :         ereport(ERROR,
     334              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     335              :                  errmsg("only heap AM is supported")));
     336              : 
     337              :     /* Disable syncscan because we assume we scan from block zero upwards */
     338            9 :     scan = table_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
     339            9 :     hscan = (HeapScanDesc) scan;
     340              : 
     341            9 :     InitDirtySnapshot(SnapshotDirty);
     342              : 
     343            9 :     nblocks = hscan->rs_nblocks; /* # blocks to be scanned */
     344              : 
     345              :     /* scan the relation */
     346           10 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
     347              :     {
     348            1 :         CHECK_FOR_INTERRUPTS();
     349              : 
     350              :         /* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
     351            1 :         LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
     352              : 
     353            1 :         if (HeapTupleSatisfiesVisibility(tuple, &SnapshotDirty, hscan->rs_cbuf))
     354              :         {
     355            1 :             stat.tuple_len += tuple->t_len;
     356            1 :             stat.tuple_count++;
     357              :         }
     358              :         else
     359              :         {
     360            0 :             stat.dead_tuple_len += tuple->t_len;
     361            0 :             stat.dead_tuple_count++;
     362              :         }
     363              : 
     364            1 :         LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
     365              : 
     366              :         /*
     367              :          * To avoid physically reading the table twice, try to do the
     368              :          * free-space scan in parallel with the heap scan.  However,
     369              :          * heap_getnext may find no tuples on a given page, so we cannot
     370              :          * simply examine the pages returned by the heap scan.
     371              :          */
     372            1 :         tupblock = ItemPointerGetBlockNumber(&tuple->t_self);
     373              : 
     374            2 :         while (block <= tupblock)
     375              :         {
     376            1 :             CHECK_FOR_INTERRUPTS();
     377              : 
     378            1 :             buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
     379              :                                         RBM_NORMAL, hscan->rs_strategy);
     380            1 :             LockBuffer(buffer, BUFFER_LOCK_SHARE);
     381            1 :             stat.free_space += PageGetExactFreeSpace(BufferGetPage(buffer));
     382            1 :             UnlockReleaseBuffer(buffer);
     383            1 :             block++;
     384              :         }
     385              :     }
     386              : 
     387            9 :     while (block < nblocks)
     388              :     {
     389            0 :         CHECK_FOR_INTERRUPTS();
     390              : 
     391            0 :         buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
     392              :                                     RBM_NORMAL, hscan->rs_strategy);
     393            0 :         LockBuffer(buffer, BUFFER_LOCK_SHARE);
     394            0 :         stat.free_space += PageGetExactFreeSpace(BufferGetPage(buffer));
     395            0 :         UnlockReleaseBuffer(buffer);
     396            0 :         block++;
     397              :     }
     398              : 
     399            9 :     table_endscan(scan);
     400            9 :     relation_close(rel, AccessShareLock);
     401              : 
     402            9 :     stat.table_len = (uint64) nblocks * BLCKSZ;
     403              : 
     404            9 :     return build_pgstattuple_type(&stat, fcinfo);
     405              : }
     406              : 
     407              : /*
     408              :  * pgstat_btree_page -- check tuples in a btree page
     409              :  */
     410              : static void
     411            0 : pgstat_btree_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
     412              :                   BufferAccessStrategy bstrategy)
     413              : {
     414              :     Buffer      buf;
     415              :     Page        page;
     416              : 
     417            0 :     buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy);
     418            0 :     LockBuffer(buf, BT_READ);
     419            0 :     page = BufferGetPage(buf);
     420              : 
     421              :     /* Page is valid, see what to do with it */
     422            0 :     if (PageIsNew(page))
     423              :     {
     424              :         /* fully empty page */
     425            0 :         stat->free_space += BLCKSZ;
     426              :     }
     427            0 :     else if (PageGetSpecialSize(page) == MAXALIGN(sizeof(BTPageOpaqueData)))
     428              :     {
     429              :         BTPageOpaque opaque;
     430              : 
     431            0 :         opaque = BTPageGetOpaque(page);
     432            0 :         if (P_IGNORE(opaque))
     433              :         {
     434              :             /* deleted or half-dead page */
     435            0 :             stat->free_space += BLCKSZ;
     436              :         }
     437            0 :         else if (P_ISLEAF(opaque))
     438              :         {
     439            0 :             pgstat_index_page(stat, page, P_FIRSTDATAKEY(opaque),
     440            0 :                               PageGetMaxOffsetNumber(page));
     441              :         }
     442              :         else
     443              :         {
     444              :             /* internal page */
     445              :         }
     446              :     }
     447              : 
     448            0 :     _bt_relbuf(rel, buf);
     449            0 : }
     450              : 
     451              : /*
     452              :  * pgstat_hash_page -- check tuples in a hash page
     453              :  */
     454              : static void
     455            0 : pgstat_hash_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
     456              :                  BufferAccessStrategy bstrategy)
     457              : {
     458              :     Buffer      buf;
     459              :     Page        page;
     460              : 
     461            0 :     buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy);
     462            0 :     LockBuffer(buf, HASH_READ);
     463            0 :     page = BufferGetPage(buf);
     464              : 
     465            0 :     if (PageIsNew(page))
     466              :     {
     467              :         /* fully empty page */
     468            0 :         stat->free_space += BLCKSZ;
     469              :     }
     470            0 :     else if (PageGetSpecialSize(page) == MAXALIGN(sizeof(HashPageOpaqueData)))
     471              :     {
     472              :         HashPageOpaque opaque;
     473              : 
     474            0 :         opaque = HashPageGetOpaque(page);
     475            0 :         switch (opaque->hasho_flag & LH_PAGE_TYPE)
     476              :         {
     477            0 :             case LH_UNUSED_PAGE:
     478            0 :                 stat->free_space += BLCKSZ;
     479            0 :                 break;
     480            0 :             case LH_BUCKET_PAGE:
     481              :             case LH_OVERFLOW_PAGE:
     482            0 :                 pgstat_index_page(stat, page, FirstOffsetNumber,
     483            0 :                                   PageGetMaxOffsetNumber(page));
     484            0 :                 break;
     485            0 :             case LH_BITMAP_PAGE:
     486              :             case LH_META_PAGE:
     487              :             default:
     488            0 :                 break;
     489              :         }
     490              :     }
     491              :     else
     492              :     {
     493              :         /* maybe corrupted */
     494              :     }
     495              : 
     496            0 :     _hash_relbuf(rel, buf);
     497            0 : }
     498              : 
     499              : /*
     500              :  * pgstat_gist_page -- check tuples in a gist page
     501              :  */
     502              : static void
     503            0 : pgstat_gist_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
     504              :                  BufferAccessStrategy bstrategy)
     505              : {
     506              :     Buffer      buf;
     507              :     Page        page;
     508              : 
     509            0 :     buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy);
     510            0 :     LockBuffer(buf, GIST_SHARE);
     511            0 :     page = BufferGetPage(buf);
     512            0 :     if (PageIsNew(page))
     513              :     {
     514              :         /* fully empty page */
     515            0 :         stat->free_space += BLCKSZ;
     516              :     }
     517            0 :     else if (PageGetSpecialSize(page) == MAXALIGN(sizeof(GISTPageOpaqueData)))
     518              :     {
     519            0 :         if (GistPageIsLeaf(page))
     520              :         {
     521            0 :             pgstat_index_page(stat, page, FirstOffsetNumber,
     522            0 :                               PageGetMaxOffsetNumber(page));
     523              :         }
     524              :         else
     525              :         {
     526              :             /* root or node */
     527              :         }
     528              :     }
     529              : 
     530            0 :     UnlockReleaseBuffer(buf);
     531            0 : }
     532              : 
     533              : /*
     534              :  * pgstat_index -- returns live/dead tuples info in a generic index
     535              :  */
     536              : static Datum
     537            0 : pgstat_index(Relation rel, BlockNumber start, pgstat_page pagefn,
     538              :              FunctionCallInfo fcinfo)
     539              : {
     540              :     BlockNumber nblocks;
     541              :     BlockNumber blkno;
     542              :     BufferAccessStrategy bstrategy;
     543            0 :     pgstattuple_type stat = {0};
     544              : 
     545              :     /* prepare access strategy for this index */
     546            0 :     bstrategy = GetAccessStrategy(BAS_BULKREAD);
     547              : 
     548            0 :     blkno = start;
     549              :     for (;;)
     550              :     {
     551              :         /* Get the current relation length */
     552            0 :         LockRelationForExtension(rel, ExclusiveLock);
     553            0 :         nblocks = RelationGetNumberOfBlocks(rel);
     554            0 :         UnlockRelationForExtension(rel, ExclusiveLock);
     555              : 
     556              :         /* Quit if we've scanned the whole relation */
     557            0 :         if (blkno >= nblocks)
     558              :         {
     559            0 :             stat.table_len = (uint64) nblocks * BLCKSZ;
     560              : 
     561            0 :             break;
     562              :         }
     563              : 
     564            0 :         for (; blkno < nblocks; blkno++)
     565              :         {
     566            0 :             CHECK_FOR_INTERRUPTS();
     567              : 
     568            0 :             pagefn(&stat, rel, blkno, bstrategy);
     569              :         }
     570              :     }
     571              : 
     572            0 :     relation_close(rel, AccessShareLock);
     573              : 
     574            0 :     return build_pgstattuple_type(&stat, fcinfo);
     575              : }
     576              : 
     577              : /*
     578              :  * pgstat_index_page -- for generic index page
     579              :  */
     580              : static void
     581            0 : pgstat_index_page(pgstattuple_type *stat, Page page,
     582              :                   OffsetNumber minoff, OffsetNumber maxoff)
     583              : {
     584              :     OffsetNumber i;
     585              : 
     586            0 :     stat->free_space += PageGetExactFreeSpace(page);
     587              : 
     588            0 :     for (i = minoff; i <= maxoff; i = OffsetNumberNext(i))
     589              :     {
     590            0 :         ItemId      itemid = PageGetItemId(page, i);
     591              : 
     592            0 :         if (ItemIdIsDead(itemid))
     593              :         {
     594            0 :             stat->dead_tuple_count++;
     595            0 :             stat->dead_tuple_len += ItemIdGetLength(itemid);
     596              :         }
     597              :         else
     598              :         {
     599            0 :             stat->tuple_count++;
     600            0 :             stat->tuple_len += ItemIdGetLength(itemid);
     601              :         }
     602              :     }
     603            0 : }
        

Generated by: LCOV version 2.0-1