LCOV - differential code coverage report
Current view: top level - contrib/pgstattuple - pgstatindex.c (source / functions) Coverage Total Hit UIC UBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 68.6 % 229 157 3 69 6 1 150 3 5 2
Current Date: 2023-04-08 15:15:32 Functions: 80.8 % 26 21 5 1 1 19 1
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*
       2                 :  * contrib/pgstattuple/pgstatindex.c
       3                 :  *
       4                 :  *
       5                 :  * pgstatindex
       6                 :  *
       7                 :  * Copyright (c) 2006 Satoshi Nagayasu <nagayasus@nttdata.co.jp>
       8                 :  *
       9                 :  * Permission to use, copy, modify, and distribute this software and
      10                 :  * its documentation for any purpose, without fee, and without a
      11                 :  * written agreement is hereby granted, provided that the above
      12                 :  * copyright notice and this paragraph and the following two
      13                 :  * paragraphs appear in all copies.
      14                 :  *
      15                 :  * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
      16                 :  * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
      17                 :  * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
      18                 :  * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED
      19                 :  * OF THE POSSIBILITY OF SUCH DAMAGE.
      20                 :  *
      21                 :  * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
      22                 :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      23                 :  * A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS
      24                 :  * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
      25                 :  * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
      26                 :  */
      27                 : 
      28                 : #include "postgres.h"
      29                 : 
      30                 : #include "access/gin_private.h"
      31                 : #include "access/hash.h"
      32                 : #include "access/htup_details.h"
      33                 : #include "access/nbtree.h"
      34                 : #include "access/relation.h"
      35                 : #include "access/table.h"
      36                 : #include "catalog/namespace.h"
      37                 : #include "catalog/pg_am.h"
      38                 : #include "funcapi.h"
      39                 : #include "miscadmin.h"
      40                 : #include "storage/bufmgr.h"
      41                 : #include "storage/lmgr.h"
      42                 : #include "utils/builtins.h"
      43                 : #include "utils/rel.h"
      44                 : #include "utils/varlena.h"
      45                 : 
      46                 : 
      47                 : /*
      48                 :  * Because of backward-compatibility issue, we have decided to have
      49                 :  * two types of interfaces, with regclass-type input arg and text-type
      50                 :  * input arg, for each function.
      51                 :  *
      52                 :  * Those functions which have text-type input arg will be deprecated
      53                 :  * in the future release.
      54                 :  */
      55 CBC           1 : PG_FUNCTION_INFO_V1(pgstatindex);
      56               1 : PG_FUNCTION_INFO_V1(pgstatindexbyid);
      57               1 : PG_FUNCTION_INFO_V1(pg_relpages);
      58               1 : PG_FUNCTION_INFO_V1(pg_relpagesbyid);
      59               1 : PG_FUNCTION_INFO_V1(pgstatginindex);
      60               2 : PG_FUNCTION_INFO_V1(pgstathashindex);
      61                 : 
      62               2 : PG_FUNCTION_INFO_V1(pgstatindex_v1_5);
      63               2 : PG_FUNCTION_INFO_V1(pgstatindexbyid_v1_5);
      64               2 : PG_FUNCTION_INFO_V1(pg_relpages_v1_5);
      65               2 : PG_FUNCTION_INFO_V1(pg_relpagesbyid_v1_5);
      66               2 : PG_FUNCTION_INFO_V1(pgstatginindex_v1_5);
      67                 : 
      68                 : Datum       pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo);
      69                 : 
      70                 : #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
      71                 : #define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
      72                 : #define IS_GIN(r) ((r)->rd_rel->relam == GIN_AM_OID)
      73                 : #define IS_HASH(r) ((r)->rd_rel->relam == HASH_AM_OID)
      74                 : 
      75                 : /* ------------------------------------------------
      76                 :  * A structure for a whole btree index statistics
      77                 :  * used by pgstatindex().
      78                 :  * ------------------------------------------------
      79                 :  */
      80                 : typedef struct BTIndexStat
      81                 : {
      82                 :     uint32      version;
      83                 :     uint32      level;
      84                 :     BlockNumber root_blkno;
      85                 : 
      86                 :     uint64      internal_pages;
      87                 :     uint64      leaf_pages;
      88                 :     uint64      empty_pages;
      89                 :     uint64      deleted_pages;
      90                 : 
      91                 :     uint64      max_avail;
      92                 :     uint64      free_space;
      93                 : 
      94                 :     uint64      fragments;
      95                 : } BTIndexStat;
      96                 : 
      97                 : /* ------------------------------------------------
      98                 :  * A structure for a whole GIN index statistics
      99                 :  * used by pgstatginindex().
     100                 :  * ------------------------------------------------
     101                 :  */
     102                 : typedef struct GinIndexStat
     103                 : {
     104                 :     int32       version;
     105                 : 
     106                 :     BlockNumber pending_pages;
     107                 :     int64       pending_tuples;
     108                 : } GinIndexStat;
     109                 : 
     110                 : /* ------------------------------------------------
     111                 :  * A structure for a whole HASH index statistics
     112                 :  * used by pgstathashindex().
     113                 :  * ------------------------------------------------
     114                 :  */
     115                 : typedef struct HashIndexStat
     116                 : {
     117                 :     int32       version;
     118                 :     int32       space_per_page;
     119                 : 
     120                 :     BlockNumber bucket_pages;
     121                 :     BlockNumber overflow_pages;
     122                 :     BlockNumber bitmap_pages;
     123                 :     BlockNumber unused_pages;
     124                 : 
     125                 :     int64       live_items;
     126                 :     int64       dead_items;
     127                 :     uint64      free_space;
     128                 : } HashIndexStat;
     129                 : 
     130                 : static Datum pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo);
     131                 : static int64 pg_relpages_impl(Relation rel);
     132                 : static void GetHashPageStats(Page page, HashIndexStat *stats);
     133                 : 
     134                 : /* ------------------------------------------------------
     135                 :  * pgstatindex()
     136                 :  *
     137                 :  * Usage: SELECT * FROM pgstatindex('t1_pkey');
     138                 :  *
     139                 :  * The superuser() check here must be kept as the library might be upgraded
     140                 :  * without the extension being upgraded, meaning that in pre-1.5 installations
     141                 :  * these functions could be called by any user.
     142                 :  * ------------------------------------------------------
     143                 :  */
     144                 : Datum
     145 UBC           0 : pgstatindex(PG_FUNCTION_ARGS)
     146                 : {
     147               0 :     text       *relname = PG_GETARG_TEXT_PP(0);
     148                 :     Relation    rel;
     149                 :     RangeVar   *relrv;
     150                 : 
     151               0 :     if (!superuser())
     152               0 :         ereport(ERROR,
     153                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     154                 :                  errmsg("must be superuser to use pgstattuple functions")));
     155                 : 
     156               0 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     157               0 :     rel = relation_openrv(relrv, AccessShareLock);
     158                 : 
     159               0 :     PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo));
     160                 : }
     161                 : 
     162                 : /*
     163                 :  * As of pgstattuple version 1.5, we no longer need to check if the user
     164                 :  * is a superuser because we REVOKE EXECUTE on the function from PUBLIC.
     165                 :  * Users can then grant access to it based on their policies.
     166                 :  *
     167                 :  * Otherwise identical to pgstatindex (above).
     168                 :  */
     169                 : Datum
     170 CBC          10 : pgstatindex_v1_5(PG_FUNCTION_ARGS)
     171                 : {
     172              10 :     text       *relname = PG_GETARG_TEXT_PP(0);
     173                 :     Relation    rel;
     174                 :     RangeVar   *relrv;
     175                 : 
     176              10 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     177              10 :     rel = relation_openrv(relrv, AccessShareLock);
     178                 : 
     179              10 :     PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo));
     180                 : }
     181                 : 
     182                 : /*
     183                 :  * The superuser() check here must be kept as the library might be upgraded
     184                 :  * without the extension being upgraded, meaning that in pre-1.5 installations
     185                 :  * these functions could be called by any user.
     186                 :  */
     187                 : Datum
     188 UBC           0 : pgstatindexbyid(PG_FUNCTION_ARGS)
     189                 : {
     190               0 :     Oid         relid = PG_GETARG_OID(0);
     191                 :     Relation    rel;
     192                 : 
     193               0 :     if (!superuser())
     194               0 :         ereport(ERROR,
     195                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     196                 :                  errmsg("must be superuser to use pgstattuple functions")));
     197                 : 
     198               0 :     rel = relation_open(relid, AccessShareLock);
     199                 : 
     200               0 :     PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo));
     201                 : }
     202                 : 
     203                 : /* No need for superuser checks in v1.5, see above */
     204                 : Datum
     205 CBC           1 : pgstatindexbyid_v1_5(PG_FUNCTION_ARGS)
     206                 : {
     207               1 :     Oid         relid = PG_GETARG_OID(0);
     208                 :     Relation    rel;
     209                 : 
     210               1 :     rel = relation_open(relid, AccessShareLock);
     211                 : 
     212               1 :     PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo));
     213                 : }
     214                 : 
     215                 : static Datum
     216              11 : pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
     217                 : {
     218                 :     Datum       result;
     219                 :     BlockNumber nblocks;
     220                 :     BlockNumber blkno;
     221                 :     BTIndexStat indexStat;
     222              11 :     BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD);
     223                 : 
     224              11 :     if (!IS_INDEX(rel) || !IS_BTREE(rel))
     225               6 :         ereport(ERROR,
     226                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     227                 :                  errmsg("relation \"%s\" is not a btree index",
     228                 :                         RelationGetRelationName(rel))));
     229                 : 
     230                 :     /*
     231                 :      * Reject attempts to read non-local temporary relations; we would be
     232                 :      * likely to get wrong data since we have no visibility into the owning
     233                 :      * session's local buffers.
     234                 :      */
     235               5 :     if (RELATION_IS_OTHER_TEMP(rel))
     236 UBC           0 :         ereport(ERROR,
     237                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     238                 :                  errmsg("cannot access temporary tables of other sessions")));
     239                 : 
     240                 :     /*
     241                 :      * Read metapage
     242                 :      */
     243                 :     {
     244 CBC           5 :         Buffer      buffer = ReadBufferExtended(rel, MAIN_FORKNUM, 0, RBM_NORMAL, bstrategy);
     245               5 :         Page        page = BufferGetPage(buffer);
     246               5 :         BTMetaPageData *metad = BTPageGetMeta(page);
     247                 : 
     248               5 :         indexStat.version = metad->btm_version;
     249               5 :         indexStat.level = metad->btm_level;
     250               5 :         indexStat.root_blkno = metad->btm_root;
     251                 : 
     252               5 :         ReleaseBuffer(buffer);
     253                 :     }
     254                 : 
     255                 :     /* -- init counters -- */
     256               5 :     indexStat.internal_pages = 0;
     257               5 :     indexStat.leaf_pages = 0;
     258               5 :     indexStat.empty_pages = 0;
     259               5 :     indexStat.deleted_pages = 0;
     260                 : 
     261               5 :     indexStat.max_avail = 0;
     262               5 :     indexStat.free_space = 0;
     263                 : 
     264               5 :     indexStat.fragments = 0;
     265                 : 
     266                 :     /*
     267                 :      * Scan all blocks except the metapage
     268                 :      */
     269               5 :     nblocks = RelationGetNumberOfBlocks(rel);
     270                 : 
     271               5 :     for (blkno = 1; blkno < nblocks; blkno++)
     272                 :     {
     273                 :         Buffer      buffer;
     274                 :         Page        page;
     275                 :         BTPageOpaque opaque;
     276                 : 
     277 UBC           0 :         CHECK_FOR_INTERRUPTS();
     278                 : 
     279                 :         /* Read and lock buffer */
     280               0 :         buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy);
     281               0 :         LockBuffer(buffer, BUFFER_LOCK_SHARE);
     282                 : 
     283               0 :         page = BufferGetPage(buffer);
     284               0 :         opaque = BTPageGetOpaque(page);
     285                 : 
     286                 :         /*
     287                 :          * Determine page type, and update totals.
     288                 :          *
     289                 :          * Note that we arbitrarily bucket deleted pages together without
     290                 :          * considering if they're leaf pages or internal pages.
     291                 :          */
     292               0 :         if (P_ISDELETED(opaque))
     293               0 :             indexStat.deleted_pages++;
     294               0 :         else if (P_IGNORE(opaque))
     295               0 :             indexStat.empty_pages++;    /* this is the "half dead" state */
     296               0 :         else if (P_ISLEAF(opaque))
     297                 :         {
     298                 :             int         max_avail;
     299                 : 
     300               0 :             max_avail = BLCKSZ - (BLCKSZ - ((PageHeader) page)->pd_special + SizeOfPageHeaderData);
     301               0 :             indexStat.max_avail += max_avail;
     302               0 :             indexStat.free_space += PageGetFreeSpace(page);
     303                 : 
     304               0 :             indexStat.leaf_pages++;
     305                 : 
     306                 :             /*
     307                 :              * If the next leaf is on an earlier block, it means a
     308                 :              * fragmentation.
     309                 :              */
     310               0 :             if (opaque->btpo_next != P_NONE && opaque->btpo_next < blkno)
     311               0 :                 indexStat.fragments++;
     312                 :         }
     313                 :         else
     314               0 :             indexStat.internal_pages++;
     315                 : 
     316                 :         /* Unlock and release buffer */
     317               0 :         LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
     318               0 :         ReleaseBuffer(buffer);
     319                 :     }
     320                 : 
     321 CBC           5 :     relation_close(rel, AccessShareLock);
     322                 : 
     323                 :     /*----------------------------
     324                 :      * Build a result tuple
     325                 :      *----------------------------
     326                 :      */
     327                 :     {
     328                 :         TupleDesc   tupleDesc;
     329                 :         int         j;
     330                 :         char       *values[10];
     331                 :         HeapTuple   tuple;
     332                 : 
     333                 :         /* Build a tuple descriptor for our result type */
     334               5 :         if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
     335 UBC           0 :             elog(ERROR, "return type must be a row type");
     336                 : 
     337 CBC           5 :         j = 0;
     338               5 :         values[j++] = psprintf("%d", indexStat.version);
     339               5 :         values[j++] = psprintf("%d", indexStat.level);
     340              10 :         values[j++] = psprintf(INT64_FORMAT,
     341                 :                                (1 + /* include the metapage in index_size */
     342               5 :                                 indexStat.leaf_pages +
     343               5 :                                 indexStat.internal_pages +
     344               5 :                                 indexStat.deleted_pages +
     345               5 :                                 indexStat.empty_pages) * BLCKSZ);
     346               5 :         values[j++] = psprintf("%u", indexStat.root_blkno);
     347               5 :         values[j++] = psprintf(INT64_FORMAT, indexStat.internal_pages);
     348               5 :         values[j++] = psprintf(INT64_FORMAT, indexStat.leaf_pages);
     349               5 :         values[j++] = psprintf(INT64_FORMAT, indexStat.empty_pages);
     350               5 :         values[j++] = psprintf(INT64_FORMAT, indexStat.deleted_pages);
     351               5 :         if (indexStat.max_avail > 0)
     352 UBC           0 :             values[j++] = psprintf("%.2f",
     353               0 :                                    100.0 - (double) indexStat.free_space / (double) indexStat.max_avail * 100.0);
     354                 :         else
     355 CBC           5 :             values[j++] = pstrdup("NaN");
     356               5 :         if (indexStat.leaf_pages > 0)
     357 UBC           0 :             values[j++] = psprintf("%.2f",
     358               0 :                                    (double) indexStat.fragments / (double) indexStat.leaf_pages * 100.0);
     359                 :         else
     360 CBC           5 :             values[j++] = pstrdup("NaN");
     361                 : 
     362               5 :         tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
     363                 :                                        values);
     364                 : 
     365               5 :         result = HeapTupleGetDatum(tuple);
     366                 :     }
     367                 : 
     368               5 :     return result;
     369                 : }
     370                 : 
     371                 : /* --------------------------------------------------------
     372                 :  * pg_relpages()
     373                 :  *
     374                 :  * Get the number of pages of the table/index.
     375                 :  *
     376                 :  * Usage: SELECT pg_relpages('t1');
     377                 :  *        SELECT pg_relpages('t1_pkey');
     378                 :  *
     379                 :  * Must keep superuser() check, see above.
     380                 :  * --------------------------------------------------------
     381                 :  */
     382                 : Datum
     383 UBC           0 : pg_relpages(PG_FUNCTION_ARGS)
     384                 : {
     385               0 :     text       *relname = PG_GETARG_TEXT_PP(0);
     386                 :     Relation    rel;
     387                 :     RangeVar   *relrv;
     388                 : 
     389               0 :     if (!superuser())
     390               0 :         ereport(ERROR,
     391                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     392                 :                  errmsg("must be superuser to use pgstattuple functions")));
     393                 : 
     394               0 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     395               0 :     rel = relation_openrv(relrv, AccessShareLock);
     396                 : 
     397               0 :     PG_RETURN_INT64(pg_relpages_impl(rel));
     398                 : }
     399                 : 
     400                 : /* No need for superuser checks in v1.5, see above */
     401                 : Datum
     402 CBC           9 : pg_relpages_v1_5(PG_FUNCTION_ARGS)
     403                 : {
     404               9 :     text       *relname = PG_GETARG_TEXT_PP(0);
     405                 :     Relation    rel;
     406                 :     RangeVar   *relrv;
     407                 : 
     408               9 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     409               9 :     rel = relation_openrv(relrv, AccessShareLock);
     410                 : 
     411               9 :     PG_RETURN_INT64(pg_relpages_impl(rel));
     412                 : }
     413                 : 
     414                 : /* Must keep superuser() check, see above. */
     415                 : Datum
     416 UBC           0 : pg_relpagesbyid(PG_FUNCTION_ARGS)
     417                 : {
     418               0 :     Oid         relid = PG_GETARG_OID(0);
     419                 :     Relation    rel;
     420                 : 
     421               0 :     if (!superuser())
     422               0 :         ereport(ERROR,
     423                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     424                 :                  errmsg("must be superuser to use pgstattuple functions")));
     425                 : 
     426               0 :     rel = relation_open(relid, AccessShareLock);
     427                 : 
     428               0 :     PG_RETURN_INT64(pg_relpages_impl(rel));
     429                 : }
     430                 : 
     431                 : /* No need for superuser checks in v1.5, see above */
     432                 : Datum
     433 CBC           3 : pg_relpagesbyid_v1_5(PG_FUNCTION_ARGS)
     434                 : {
     435               3 :     Oid         relid = PG_GETARG_OID(0);
     436                 :     Relation    rel;
     437                 : 
     438               3 :     rel = relation_open(relid, AccessShareLock);
     439                 : 
     440               3 :     PG_RETURN_INT64(pg_relpages_impl(rel));
     441                 : }
     442                 : 
     443                 : static int64
     444              12 : pg_relpages_impl(Relation rel)
     445                 : {
     446                 :     int64       relpages;
     447                 : 
     448              12 :     if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
     449               3 :         ereport(ERROR,
     450                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     451                 :                  errmsg("cannot get page count of relation \"%s\"",
     452                 :                         RelationGetRelationName(rel)),
     453                 :                  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
     454                 : 
     455                 :     /* note: this will work OK on non-local temp tables */
     456                 : 
     457               9 :     relpages = RelationGetNumberOfBlocks(rel);
     458                 : 
     459               9 :     relation_close(rel, AccessShareLock);
     460                 : 
     461               9 :     return relpages;
     462                 : }
     463                 : 
     464                 : /* ------------------------------------------------------
     465                 :  * pgstatginindex()
     466                 :  *
     467                 :  * Usage: SELECT * FROM pgstatginindex('ginindex');
     468                 :  *
     469                 :  * Must keep superuser() check, see above.
     470                 :  * ------------------------------------------------------
     471                 :  */
     472                 : Datum
     473 UBC           0 : pgstatginindex(PG_FUNCTION_ARGS)
     474                 : {
     475               0 :     Oid         relid = PG_GETARG_OID(0);
     476                 : 
     477               0 :     if (!superuser())
     478               0 :         ereport(ERROR,
     479                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     480                 :                  errmsg("must be superuser to use pgstattuple functions")));
     481                 : 
     482               0 :     PG_RETURN_DATUM(pgstatginindex_internal(relid, fcinfo));
     483                 : }
     484                 : 
     485                 : /* No need for superuser checks in v1.5, see above */
     486                 : Datum
     487 CBC           7 : pgstatginindex_v1_5(PG_FUNCTION_ARGS)
     488                 : {
     489               7 :     Oid         relid = PG_GETARG_OID(0);
     490                 : 
     491               7 :     PG_RETURN_DATUM(pgstatginindex_internal(relid, fcinfo));
     492                 : }
     493                 : 
     494                 : Datum
     495               7 : pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo)
     496                 : {
     497                 :     Relation    rel;
     498                 :     Buffer      buffer;
     499                 :     Page        page;
     500                 :     GinMetaPageData *metadata;
     501                 :     GinIndexStat stats;
     502                 :     HeapTuple   tuple;
     503                 :     TupleDesc   tupleDesc;
     504                 :     Datum       values[3];
     505               7 :     bool        nulls[3] = {false, false, false};
     506                 :     Datum       result;
     507                 : 
     508               7 :     rel = relation_open(relid, AccessShareLock);
     509                 : 
     510               7 :     if (!IS_INDEX(rel) || !IS_GIN(rel))
     511               6 :         ereport(ERROR,
     512                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     513                 :                  errmsg("relation \"%s\" is not a GIN index",
     514                 :                         RelationGetRelationName(rel))));
     515                 : 
     516                 :     /*
     517                 :      * Reject attempts to read non-local temporary relations; we would be
     518                 :      * likely to get wrong data since we have no visibility into the owning
     519                 :      * session's local buffers.
     520                 :      */
     521               1 :     if (RELATION_IS_OTHER_TEMP(rel))
     522 UBC           0 :         ereport(ERROR,
     523                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     524                 :                  errmsg("cannot access temporary indexes of other sessions")));
     525                 : 
     526                 :     /*
     527                 :      * Read metapage
     528                 :      */
     529 CBC           1 :     buffer = ReadBuffer(rel, GIN_METAPAGE_BLKNO);
     530               1 :     LockBuffer(buffer, GIN_SHARE);
     531               1 :     page = BufferGetPage(buffer);
     532               1 :     metadata = GinPageGetMeta(page);
     533                 : 
     534               1 :     stats.version = metadata->ginVersion;
     535               1 :     stats.pending_pages = metadata->nPendingPages;
     536               1 :     stats.pending_tuples = metadata->nPendingHeapTuples;
     537                 : 
     538               1 :     UnlockReleaseBuffer(buffer);
     539               1 :     relation_close(rel, AccessShareLock);
     540                 : 
     541                 :     /*
     542                 :      * Build a tuple descriptor for our result type
     543                 :      */
     544               1 :     if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
     545 UBC           0 :         elog(ERROR, "return type must be a row type");
     546                 : 
     547 CBC           1 :     values[0] = Int32GetDatum(stats.version);
     548               1 :     values[1] = UInt32GetDatum(stats.pending_pages);
     549               1 :     values[2] = Int64GetDatum(stats.pending_tuples);
     550                 : 
     551                 :     /*
     552                 :      * Build and return the tuple
     553                 :      */
     554               1 :     tuple = heap_form_tuple(tupleDesc, values, nulls);
     555               1 :     result = HeapTupleGetDatum(tuple);
     556                 : 
     557               1 :     return result;
     558                 : }
     559                 : 
     560                 : /* ------------------------------------------------------
     561                 :  * pgstathashindex()
     562                 :  *
     563                 :  * Usage: SELECT * FROM pgstathashindex('hashindex');
     564                 :  * ------------------------------------------------------
     565                 :  */
     566                 : Datum
     567               8 : pgstathashindex(PG_FUNCTION_ARGS)
     568                 : {
     569               8 :     Oid         relid = PG_GETARG_OID(0);
     570                 :     BlockNumber nblocks;
     571                 :     BlockNumber blkno;
     572                 :     Relation    rel;
     573                 :     HashIndexStat stats;
     574                 :     BufferAccessStrategy bstrategy;
     575                 :     HeapTuple   tuple;
     576                 :     TupleDesc   tupleDesc;
     577                 :     Datum       values[8];
     578 GNC           8 :     bool        nulls[8] = {0};
     579                 :     Buffer      metabuf;
     580                 :     HashMetaPage metap;
     581                 :     float8      free_percent;
     582                 :     uint64      total_space;
     583                 : 
     584 CBC           8 :     rel = index_open(relid, AccessShareLock);
     585                 : 
     586                 :     /* index_open() checks that it's an index */
     587               4 :     if (!IS_HASH(rel))
     588               2 :         ereport(ERROR,
     589                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     590                 :                  errmsg("relation \"%s\" is not a hash index",
     591                 :                         RelationGetRelationName(rel))));
     592                 : 
     593                 :     /*
     594                 :      * Reject attempts to read non-local temporary relations; we would be
     595                 :      * likely to get wrong data since we have no visibility into the owning
     596                 :      * session's local buffers.
     597                 :      */
     598               2 :     if (RELATION_IS_OTHER_TEMP(rel))
     599 UBC           0 :         ereport(ERROR,
     600                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     601                 :                  errmsg("cannot access temporary indexes of other sessions")));
     602                 : 
     603                 :     /* Get the information we need from the metapage. */
     604 CBC           2 :     memset(&stats, 0, sizeof(stats));
     605               2 :     metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
     606               2 :     metap = HashPageGetMeta(BufferGetPage(metabuf));
     607               2 :     stats.version = metap->hashm_version;
     608               2 :     stats.space_per_page = metap->hashm_bsize;
     609               2 :     _hash_relbuf(rel, metabuf);
     610                 : 
     611                 :     /* Get the current relation length */
     612               2 :     nblocks = RelationGetNumberOfBlocks(rel);
     613                 : 
     614                 :     /* prepare access strategy for this index */
     615               2 :     bstrategy = GetAccessStrategy(BAS_BULKREAD);
     616                 : 
     617                 :     /* Start from blkno 1 as 0th block is metapage */
     618              16 :     for (blkno = 1; blkno < nblocks; blkno++)
     619                 :     {
     620                 :         Buffer      buf;
     621                 :         Page        page;
     622                 : 
     623              14 :         CHECK_FOR_INTERRUPTS();
     624                 : 
     625              14 :         buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
     626                 :                                  bstrategy);
     627              14 :         LockBuffer(buf, BUFFER_LOCK_SHARE);
     628              14 :         page = (Page) BufferGetPage(buf);
     629                 : 
     630              14 :         if (PageIsNew(page))
     631 UBC           0 :             stats.unused_pages++;
     632 CBC          14 :         else if (PageGetSpecialSize(page) !=
     633                 :                  MAXALIGN(sizeof(HashPageOpaqueData)))
     634 UBC           0 :             ereport(ERROR,
     635                 :                     (errcode(ERRCODE_INDEX_CORRUPTED),
     636                 :                      errmsg("index \"%s\" contains corrupted page at block %u",
     637                 :                             RelationGetRelationName(rel),
     638                 :                             BufferGetBlockNumber(buf))));
     639                 :         else
     640                 :         {
     641                 :             HashPageOpaque opaque;
     642                 :             int         pagetype;
     643                 : 
     644 CBC          14 :             opaque = HashPageGetOpaque(page);
     645              14 :             pagetype = opaque->hasho_flag & LH_PAGE_TYPE;
     646                 : 
     647              14 :             if (pagetype == LH_BUCKET_PAGE)
     648                 :             {
     649              12 :                 stats.bucket_pages++;
     650              12 :                 GetHashPageStats(page, &stats);
     651                 :             }
     652               2 :             else if (pagetype == LH_OVERFLOW_PAGE)
     653                 :             {
     654 UBC           0 :                 stats.overflow_pages++;
     655               0 :                 GetHashPageStats(page, &stats);
     656                 :             }
     657 CBC           2 :             else if (pagetype == LH_BITMAP_PAGE)
     658               2 :                 stats.bitmap_pages++;
     659 UBC           0 :             else if (pagetype == LH_UNUSED_PAGE)
     660               0 :                 stats.unused_pages++;
     661                 :             else
     662               0 :                 ereport(ERROR,
     663                 :                         (errcode(ERRCODE_INDEX_CORRUPTED),
     664                 :                          errmsg("unexpected page type 0x%04X in HASH index \"%s\" block %u",
     665                 :                                 opaque->hasho_flag, RelationGetRelationName(rel),
     666                 :                                 BufferGetBlockNumber(buf))));
     667                 :         }
     668 CBC          14 :         UnlockReleaseBuffer(buf);
     669                 :     }
     670                 : 
     671                 :     /* Done accessing the index */
     672               2 :     index_close(rel, AccessShareLock);
     673                 : 
     674                 :     /* Count unused pages as free space. */
     675               2 :     stats.free_space += (uint64) stats.unused_pages * stats.space_per_page;
     676                 : 
     677                 :     /*
     678                 :      * Total space available for tuples excludes the metapage and the bitmap
     679                 :      * pages.
     680                 :      */
     681               2 :     total_space = (uint64) (nblocks - (stats.bitmap_pages + 1)) *
     682               2 :         stats.space_per_page;
     683                 : 
     684               2 :     if (total_space == 0)
     685 UBC           0 :         free_percent = 0.0;
     686                 :     else
     687 CBC           2 :         free_percent = 100.0 * stats.free_space / total_space;
     688                 : 
     689                 :     /*
     690                 :      * Build a tuple descriptor for our result type
     691                 :      */
     692               2 :     if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
     693 UBC           0 :         elog(ERROR, "return type must be a row type");
     694                 : 
     695 CBC           2 :     tupleDesc = BlessTupleDesc(tupleDesc);
     696                 : 
     697                 :     /*
     698                 :      * Build and return the tuple
     699                 :      */
     700               2 :     values[0] = Int32GetDatum(stats.version);
     701               2 :     values[1] = Int64GetDatum((int64) stats.bucket_pages);
     702               2 :     values[2] = Int64GetDatum((int64) stats.overflow_pages);
     703               2 :     values[3] = Int64GetDatum((int64) stats.bitmap_pages);
     704               2 :     values[4] = Int64GetDatum((int64) stats.unused_pages);
     705               2 :     values[5] = Int64GetDatum(stats.live_items);
     706               2 :     values[6] = Int64GetDatum(stats.dead_items);
     707               2 :     values[7] = Float8GetDatum(free_percent);
     708 GIC           2 :     tuple = heap_form_tuple(tupleDesc, values, nulls);
     709 ECB             : 
     710 GIC           2 :     PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
     711                 : }
     712                 : 
     713                 : /* -------------------------------------------------
     714                 :  * GetHashPageStats()
     715                 :  *
     716                 :  * Collect statistics of single hash page
     717                 :  * -------------------------------------------------
     718                 :  */
     719 ECB             : static void
     720 GIC          12 : GetHashPageStats(Page page, HashIndexStat *stats)
     721 ECB             : {
     722 GIC          12 :     OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
     723                 :     int         off;
     724                 : 
     725 ECB             :     /* count live and dead tuples, and free space */
     726 GIC          12 :     for (off = FirstOffsetNumber; off <= maxoff; off++)
     727 EUB             :     {
     728 UIC           0 :         ItemId      id = PageGetItemId(page, off);
     729 EUB             : 
     730 UBC           0 :         if (!ItemIdIsDead(id))
     731 UIC           0 :             stats->live_items++;
     732 EUB             :         else
     733 UIC           0 :             stats->dead_items++;
     734 ECB             :     }
     735 CBC          12 :     stats->free_space += PageGetExactFreeSpace(page);
     736 GIC          12 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a