LCOV - code coverage report
Current view: top level - contrib/pgstattuple - pgstattuple.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 248 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 17 0
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            0 : PG_MODULE_MAGIC_EXT(
      42              :                                         .name = "pgstattuple",
      43              :                                         .version = PG_VERSION
      44              : );
      45              : 
      46            0 : PG_FUNCTION_INFO_V1(pgstattuple);
      47            0 : PG_FUNCTION_INFO_V1(pgstattuple_v1_5);
      48            0 : PG_FUNCTION_INFO_V1(pgstattuplebyid);
      49            0 : 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            0 : build_pgstattuple_type(pgstattuple_type *stat, FunctionCallInfo fcinfo)
      93              : {
      94              : #define NCOLUMNS        9
      95              : #define NCHARS          314
      96              : 
      97            0 :         HeapTuple       tuple;
      98            0 :         char       *values[NCOLUMNS];
      99            0 :         char            values_buf[NCOLUMNS][NCHARS];
     100            0 :         int                     i;
     101            0 :         double          tuple_percent;
     102            0 :         double          dead_tuple_percent;
     103            0 :         double          free_percent;   /* free/reusable space in % */
     104            0 :         TupleDesc       tupdesc;
     105            0 :         AttInMetadata *attinmeta;
     106              : 
     107              :         /* Build a tuple descriptor for our result type */
     108            0 :         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            0 :         attinmeta = TupleDescGetAttInMetadata(tupdesc);
     116              : 
     117            0 :         if (stat->table_len == 0)
     118              :         {
     119            0 :                 tuple_percent = 0.0;
     120            0 :                 dead_tuple_percent = 0.0;
     121            0 :                 free_percent = 0.0;
     122            0 :         }
     123              :         else
     124              :         {
     125            0 :                 tuple_percent = 100.0 * stat->tuple_len / stat->table_len;
     126            0 :                 dead_tuple_percent = 100.0 * stat->dead_tuple_len / stat->table_len;
     127            0 :                 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            0 :         for (i = 0; i < NCOLUMNS; i++)
     136            0 :                 values[i] = values_buf[i];
     137            0 :         i = 0;
     138            0 :         snprintf(values[i++], NCHARS, INT64_FORMAT, stat->table_len);
     139            0 :         snprintf(values[i++], NCHARS, INT64_FORMAT, stat->tuple_count);
     140            0 :         snprintf(values[i++], NCHARS, INT64_FORMAT, stat->tuple_len);
     141            0 :         snprintf(values[i++], NCHARS, "%.2f", tuple_percent);
     142            0 :         snprintf(values[i++], NCHARS, INT64_FORMAT, stat->dead_tuple_count);
     143            0 :         snprintf(values[i++], NCHARS, INT64_FORMAT, stat->dead_tuple_len);
     144            0 :         snprintf(values[i++], NCHARS, "%.2f", dead_tuple_percent);
     145            0 :         snprintf(values[i++], NCHARS, INT64_FORMAT, stat->free_space);
     146            0 :         snprintf(values[i++], NCHARS, "%.2f", free_percent);
     147              : 
     148              :         /* build a tuple */
     149            0 :         tuple = BuildTupleFromCStrings(attinmeta, values);
     150              : 
     151              :         /* make the tuple into a datum */
     152            0 :         return HeapTupleGetDatum(tuple);
     153            0 : }
     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            0 :         RangeVar   *relrv;
     173            0 :         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            0 : }
     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            0 : pgstattuple_v1_5(PG_FUNCTION_ARGS)
     196              : {
     197            0 :         text       *relname = PG_GETARG_TEXT_PP(0);
     198            0 :         RangeVar   *relrv;
     199            0 :         Relation        rel;
     200              : 
     201              :         /* open relation */
     202            0 :         relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     203            0 :         rel = relation_openrv(relrv, AccessShareLock);
     204              : 
     205            0 :         PG_RETURN_DATUM(pgstat_relation(rel, fcinfo));
     206            0 : }
     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            0 :         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            0 : }
     225              : 
     226              : /* Remove superuser() check for 1.5 version, see above */
     227              : Datum
     228            0 : pgstattuplebyid_v1_5(PG_FUNCTION_ARGS)
     229              : {
     230            0 :         Oid                     relid = PG_GETARG_OID(0);
     231            0 :         Relation        rel;
     232              : 
     233              :         /* open relation */
     234            0 :         rel = relation_open(relid, AccessShareLock);
     235              : 
     236            0 :         PG_RETURN_DATUM(pgstat_relation(rel, fcinfo));
     237            0 : }
     238              : 
     239              : /*
     240              :  * pgstat_relation
     241              :  */
     242              : static Datum
     243            0 : pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
     244              : {
     245            0 :         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            0 :         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            0 :         if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind) ||
     258            0 :                 rel->rd_rel->relkind == RELKIND_SEQUENCE)
     259              :         {
     260            0 :                 return pgstat_heap(rel, fcinfo);
     261              :         }
     262            0 :         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              :                         case BTREE_AM_OID:
     274            0 :                                 return pgstat_index(rel, BTREE_METAPAGE + 1,
     275            0 :                                                                         pgstat_btree_page, fcinfo);
     276              :                         case HASH_AM_OID:
     277            0 :                                 return pgstat_index(rel, HASH_METAPAGE + 1,
     278            0 :                                                                         pgstat_hash_page, fcinfo);
     279              :                         case GIST_AM_OID:
     280            0 :                                 return pgstat_index(rel, GIST_ROOT_BLKNO + 1,
     281            0 :                                                                         pgstat_gist_page, fcinfo);
     282              :                         case GIN_AM_OID:
     283            0 :                                 err = "gin index";
     284            0 :                                 break;
     285              :                         case SPGIST_AM_OID:
     286            0 :                                 err = "spgist index";
     287            0 :                                 break;
     288              :                         case BRIN_AM_OID:
     289            0 :                                 err = "brin index";
     290            0 :                                 break;
     291              :                         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            0 :         }
     300              :         else
     301              :         {
     302            0 :                 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            0 :         return 0;                                       /* should not happen */
     310            0 : }
     311              : 
     312              : /*
     313              :  * pgstat_heap -- returns live/dead tuples info in a heap
     314              :  */
     315              : static Datum
     316            0 : pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
     317              : {
     318            0 :         TableScanDesc scan;
     319            0 :         HeapScanDesc hscan;
     320            0 :         HeapTuple       tuple;
     321            0 :         BlockNumber nblocks;
     322            0 :         BlockNumber block = 0;          /* next block to count free space in */
     323            0 :         BlockNumber tupblock;
     324            0 :         Buffer          buffer;
     325            0 :         pgstattuple_type stat = {0};
     326            0 :         SnapshotData SnapshotDirty;
     327              : 
     328              :         /*
     329              :          * Sequences always use heap AM, but they don't show that in the catalogs.
     330              :          */
     331            0 :         if (rel->rd_rel->relkind != RELKIND_SEQUENCE &&
     332            0 :                 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            0 :         scan = table_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
     339            0 :         hscan = (HeapScanDesc) scan;
     340              : 
     341            0 :         InitDirtySnapshot(SnapshotDirty);
     342              : 
     343            0 :         nblocks = hscan->rs_nblocks; /* # blocks to be scanned */
     344              : 
     345              :         /* scan the relation */
     346            0 :         while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
     347              :         {
     348            0 :                 CHECK_FOR_INTERRUPTS();
     349              : 
     350              :                 /* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
     351            0 :                 LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
     352              : 
     353            0 :                 if (HeapTupleSatisfiesVisibility(tuple, &SnapshotDirty, hscan->rs_cbuf))
     354              :                 {
     355            0 :                         stat.tuple_len += tuple->t_len;
     356            0 :                         stat.tuple_count++;
     357            0 :                 }
     358              :                 else
     359              :                 {
     360            0 :                         stat.dead_tuple_len += tuple->t_len;
     361            0 :                         stat.dead_tuple_count++;
     362              :                 }
     363              : 
     364            0 :                 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            0 :                 tupblock = ItemPointerGetBlockNumber(&tuple->t_self);
     373              : 
     374            0 :                 while (block <= tupblock)
     375              :                 {
     376            0 :                         CHECK_FOR_INTERRUPTS();
     377              : 
     378            0 :                         buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
     379            0 :                                                                                 RBM_NORMAL, hscan->rs_strategy);
     380            0 :                         LockBuffer(buffer, BUFFER_LOCK_SHARE);
     381            0 :                         stat.free_space += PageGetExactFreeSpace(BufferGetPage(buffer));
     382            0 :                         UnlockReleaseBuffer(buffer);
     383            0 :                         block++;
     384              :                 }
     385              :         }
     386              : 
     387            0 :         while (block < nblocks)
     388              :         {
     389            0 :                 CHECK_FOR_INTERRUPTS();
     390              : 
     391            0 :                 buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
     392            0 :                                                                         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            0 :         table_endscan(scan);
     400            0 :         relation_close(rel, AccessShareLock);
     401              : 
     402            0 :         stat.table_len = (uint64) nblocks * BLCKSZ;
     403              : 
     404            0 :         return build_pgstattuple_type(&stat, fcinfo);
     405            0 : }
     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            0 :         Buffer          buf;
     415            0 :         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            0 :         }
     427            0 :         else if (PageGetSpecialSize(page) == MAXALIGN(sizeof(BTPageOpaqueData)))
     428              :         {
     429            0 :                 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            0 :                 }
     437            0 :                 else if (P_ISLEAF(opaque))
     438              :                 {
     439            0 :                         pgstat_index_page(stat, page, P_FIRSTDATAKEY(opaque),
     440            0 :                                                           PageGetMaxOffsetNumber(page));
     441            0 :                 }
     442              :                 else
     443              :                 {
     444              :                         /* internal page */
     445              :                 }
     446            0 :         }
     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            0 :         Buffer          buf;
     459            0 :         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            0 :         }
     470            0 :         else if (PageGetSpecialSize(page) == MAXALIGN(sizeof(HashPageOpaqueData)))
     471              :         {
     472            0 :                 HashPageOpaque opaque;
     473              : 
     474            0 :                 opaque = HashPageGetOpaque(page);
     475            0 :                 switch (opaque->hasho_flag & LH_PAGE_TYPE)
     476              :                 {
     477              :                         case LH_UNUSED_PAGE:
     478            0 :                                 stat->free_space += BLCKSZ;
     479            0 :                                 break;
     480              :                         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              :                         case LH_BITMAP_PAGE:
     486            0 :                         case LH_META_PAGE:
     487              :                         default:
     488            0 :                                 break;
     489              :                 }
     490            0 :         }
     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            0 :         Buffer          buf;
     507            0 :         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            0 :         }
     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            0 :                 }
     524              :                 else
     525              :                 {
     526              :                         /* root or node */
     527              :                 }
     528            0 :         }
     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            0 :         BlockNumber nblocks;
     541            0 :         BlockNumber blkno;
     542            0 :         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            0 :         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            0 :                 }
     570              :         }
     571              : 
     572            0 :         relation_close(rel, AccessShareLock);
     573              : 
     574            0 :         return build_pgstattuple_type(&stat, fcinfo);
     575            0 : }
     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            0 :         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            0 :                 }
     597              :                 else
     598              :                 {
     599            0 :                         stat->tuple_count++;
     600            0 :                         stat->tuple_len += ItemIdGetLength(itemid);
     601              :                 }
     602            0 :         }
     603            0 : }
        

Generated by: LCOV version 2.3.2-1