LCOV - code coverage report
Current view: top level - contrib/pageinspect - btreefuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 182 211 86.3 %
Date: 2019-11-15 22:06:47 Functions: 10 10 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * contrib/pageinspect/btreefuncs.c
       3             :  *
       4             :  *
       5             :  * btreefuncs.c
       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/nbtree.h"
      31             : #include "access/relation.h"
      32             : #include "catalog/namespace.h"
      33             : #include "catalog/pg_am.h"
      34             : #include "funcapi.h"
      35             : #include "miscadmin.h"
      36             : #include "pageinspect.h"
      37             : #include "utils/builtins.h"
      38             : #include "utils/rel.h"
      39             : #include "utils/varlena.h"
      40             : 
      41          14 : PG_FUNCTION_INFO_V1(bt_metap);
      42           8 : PG_FUNCTION_INFO_V1(bt_page_items);
      43           8 : PG_FUNCTION_INFO_V1(bt_page_items_bytea);
      44           8 : PG_FUNCTION_INFO_V1(bt_page_stats);
      45             : 
      46             : #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
      47             : #define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
      48             : 
      49             : /* note: BlockNumber is unsigned, hence can't be negative */
      50             : #define CHECK_RELATION_BLOCK_RANGE(rel, blkno) { \
      51             :         if ( RelationGetNumberOfBlocks(rel) <= (BlockNumber) (blkno) ) \
      52             :              elog(ERROR, "block number out of range"); }
      53             : 
      54             : /* ------------------------------------------------
      55             :  * structure for single btree page statistics
      56             :  * ------------------------------------------------
      57             :  */
      58             : typedef struct BTPageStat
      59             : {
      60             :     uint32      blkno;
      61             :     uint32      live_items;
      62             :     uint32      dead_items;
      63             :     uint32      page_size;
      64             :     uint32      max_avail;
      65             :     uint32      free_size;
      66             :     uint32      avg_item_size;
      67             :     char        type;
      68             : 
      69             :     /* opaque data */
      70             :     BlockNumber btpo_prev;
      71             :     BlockNumber btpo_next;
      72             :     union
      73             :     {
      74             :         uint32      level;
      75             :         TransactionId xact;
      76             :     }           btpo;
      77             :     uint16      btpo_flags;
      78             :     BTCycleId   btpo_cycleid;
      79             : } BTPageStat;
      80             : 
      81             : 
      82             : /* -------------------------------------------------
      83             :  * GetBTPageStatistics()
      84             :  *
      85             :  * Collect statistics of single b-tree page
      86             :  * -------------------------------------------------
      87             :  */
      88             : static void
      89           2 : GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat *stat)
      90             : {
      91           2 :     Page        page = BufferGetPage(buffer);
      92           2 :     PageHeader  phdr = (PageHeader) page;
      93           2 :     OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
      94           2 :     BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page);
      95           2 :     int         item_size = 0;
      96             :     int         off;
      97             : 
      98           2 :     stat->blkno = blkno;
      99             : 
     100           2 :     stat->max_avail = BLCKSZ - (BLCKSZ - phdr->pd_special + SizeOfPageHeaderData);
     101             : 
     102           2 :     stat->dead_items = stat->live_items = 0;
     103             : 
     104           2 :     stat->page_size = PageGetPageSize(page);
     105             : 
     106             :     /* page type (flags) */
     107           2 :     if (P_ISDELETED(opaque))
     108             :     {
     109           0 :         stat->type = 'd';
     110           0 :         stat->btpo.xact = opaque->btpo.xact;
     111           0 :         return;
     112             :     }
     113           2 :     else if (P_IGNORE(opaque))
     114           0 :         stat->type = 'e';
     115           2 :     else if (P_ISLEAF(opaque))
     116           2 :         stat->type = 'l';
     117           0 :     else if (P_ISROOT(opaque))
     118           0 :         stat->type = 'r';
     119             :     else
     120           0 :         stat->type = 'i';
     121             : 
     122             :     /* btpage opaque data */
     123           2 :     stat->btpo_prev = opaque->btpo_prev;
     124           2 :     stat->btpo_next = opaque->btpo_next;
     125           2 :     stat->btpo.level = opaque->btpo.level;
     126           2 :     stat->btpo_flags = opaque->btpo_flags;
     127           2 :     stat->btpo_cycleid = opaque->btpo_cycleid;
     128             : 
     129             :     /* count live and dead tuples, and free space */
     130           4 :     for (off = FirstOffsetNumber; off <= maxoff; off++)
     131             :     {
     132             :         IndexTuple  itup;
     133             : 
     134           2 :         ItemId      id = PageGetItemId(page, off);
     135             : 
     136           2 :         itup = (IndexTuple) PageGetItem(page, id);
     137             : 
     138           2 :         item_size += IndexTupleSize(itup);
     139             : 
     140           2 :         if (!ItemIdIsDead(id))
     141           2 :             stat->live_items++;
     142             :         else
     143           0 :             stat->dead_items++;
     144             :     }
     145           2 :     stat->free_size = PageGetFreeSpace(page);
     146             : 
     147           2 :     if ((stat->live_items + stat->dead_items) > 0)
     148           2 :         stat->avg_item_size = item_size / (stat->live_items + stat->dead_items);
     149             :     else
     150           0 :         stat->avg_item_size = 0;
     151             : }
     152             : 
     153             : /* -----------------------------------------------
     154             :  * bt_page_stats()
     155             :  *
     156             :  * Usage: SELECT * FROM bt_page_stats('t1_pkey', 1);
     157             :  * -----------------------------------------------
     158             :  */
     159             : Datum
     160           6 : bt_page_stats(PG_FUNCTION_ARGS)
     161             : {
     162           6 :     text       *relname = PG_GETARG_TEXT_PP(0);
     163           6 :     uint32      blkno = PG_GETARG_UINT32(1);
     164             :     Buffer      buffer;
     165             :     Relation    rel;
     166             :     RangeVar   *relrv;
     167             :     Datum       result;
     168             :     HeapTuple   tuple;
     169             :     TupleDesc   tupleDesc;
     170             :     int         j;
     171             :     char       *values[11];
     172             :     BTPageStat  stat;
     173             : 
     174           6 :     if (!superuser())
     175           0 :         ereport(ERROR,
     176             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     177             :                  (errmsg("must be superuser to use pageinspect functions"))));
     178             : 
     179           6 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     180           6 :     rel = relation_openrv(relrv, AccessShareLock);
     181             : 
     182           6 :     if (!IS_INDEX(rel) || !IS_BTREE(rel))
     183           0 :         elog(ERROR, "relation \"%s\" is not a btree index",
     184             :              RelationGetRelationName(rel));
     185             : 
     186             :     /*
     187             :      * Reject attempts to read non-local temporary relations; we would be
     188             :      * likely to get wrong data since we have no visibility into the owning
     189             :      * session's local buffers.
     190             :      */
     191           6 :     if (RELATION_IS_OTHER_TEMP(rel))
     192           0 :         ereport(ERROR,
     193             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     194             :                  errmsg("cannot access temporary tables of other sessions")));
     195             : 
     196           6 :     if (blkno == 0)
     197           2 :         elog(ERROR, "block 0 is a meta page");
     198             : 
     199           4 :     CHECK_RELATION_BLOCK_RANGE(rel, blkno);
     200             : 
     201           2 :     buffer = ReadBuffer(rel, blkno);
     202           2 :     LockBuffer(buffer, BUFFER_LOCK_SHARE);
     203             : 
     204             :     /* keep compiler quiet */
     205           2 :     stat.btpo_prev = stat.btpo_next = InvalidBlockNumber;
     206           2 :     stat.btpo_flags = stat.free_size = stat.avg_item_size = 0;
     207             : 
     208           2 :     GetBTPageStatistics(blkno, buffer, &stat);
     209             : 
     210           2 :     UnlockReleaseBuffer(buffer);
     211           2 :     relation_close(rel, AccessShareLock);
     212             : 
     213             :     /* Build a tuple descriptor for our result type */
     214           2 :     if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
     215           0 :         elog(ERROR, "return type must be a row type");
     216             : 
     217           2 :     j = 0;
     218           2 :     values[j++] = psprintf("%d", stat.blkno);
     219           2 :     values[j++] = psprintf("%c", stat.type);
     220           2 :     values[j++] = psprintf("%d", stat.live_items);
     221           2 :     values[j++] = psprintf("%d", stat.dead_items);
     222           2 :     values[j++] = psprintf("%d", stat.avg_item_size);
     223           2 :     values[j++] = psprintf("%d", stat.page_size);
     224           2 :     values[j++] = psprintf("%d", stat.free_size);
     225           2 :     values[j++] = psprintf("%d", stat.btpo_prev);
     226           2 :     values[j++] = psprintf("%d", stat.btpo_next);
     227           2 :     values[j++] = psprintf("%d", (stat.type == 'd') ? stat.btpo.xact : stat.btpo.level);
     228           2 :     values[j++] = psprintf("%d", stat.btpo_flags);
     229             : 
     230           2 :     tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
     231             :                                    values);
     232             : 
     233           2 :     result = HeapTupleGetDatum(tuple);
     234             : 
     235           2 :     PG_RETURN_DATUM(result);
     236             : }
     237             : 
     238             : 
     239             : /*
     240             :  * cross-call data structure for SRF
     241             :  */
     242             : struct user_args
     243             : {
     244             :     Page        page;
     245             :     OffsetNumber offset;
     246             : };
     247             : 
     248             : /*-------------------------------------------------------
     249             :  * bt_page_print_tuples()
     250             :  *
     251             :  * Form a tuple describing index tuple at a given offset
     252             :  * ------------------------------------------------------
     253             :  */
     254             : static Datum
     255           4 : bt_page_print_tuples(FuncCallContext *fctx, Page page, OffsetNumber offset)
     256             : {
     257             :     char       *values[6];
     258             :     HeapTuple   tuple;
     259             :     ItemId      id;
     260             :     IndexTuple  itup;
     261             :     int         j;
     262             :     int         off;
     263             :     int         dlen;
     264             :     char       *dump;
     265             :     char       *ptr;
     266             : 
     267           4 :     id = PageGetItemId(page, offset);
     268             : 
     269           4 :     if (!ItemIdIsValid(id))
     270           0 :         elog(ERROR, "invalid ItemId");
     271             : 
     272           4 :     itup = (IndexTuple) PageGetItem(page, id);
     273             : 
     274           4 :     j = 0;
     275           4 :     values[j++] = psprintf("%d", offset);
     276          12 :     values[j++] = psprintf("(%u,%u)",
     277           4 :                            ItemPointerGetBlockNumberNoCheck(&itup->t_tid),
     278           4 :                            ItemPointerGetOffsetNumberNoCheck(&itup->t_tid));
     279           4 :     values[j++] = psprintf("%d", (int) IndexTupleSize(itup));
     280           4 :     values[j++] = psprintf("%c", IndexTupleHasNulls(itup) ? 't' : 'f');
     281           4 :     values[j++] = psprintf("%c", IndexTupleHasVarwidths(itup) ? 't' : 'f');
     282             : 
     283           4 :     ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
     284           4 :     dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
     285           4 :     dump = palloc0(dlen * 3 + 1);
     286           4 :     values[j] = dump;
     287          36 :     for (off = 0; off < dlen; off++)
     288             :     {
     289          32 :         if (off > 0)
     290          28 :             *dump++ = ' ';
     291          32 :         sprintf(dump, "%02x", *(ptr + off) & 0xff);
     292          32 :         dump += 2;
     293             :     }
     294             : 
     295           4 :     tuple = BuildTupleFromCStrings(fctx->attinmeta, values);
     296             : 
     297           4 :     return HeapTupleGetDatum(tuple);
     298             : }
     299             : 
     300             : /*-------------------------------------------------------
     301             :  * bt_page_items()
     302             :  *
     303             :  * Get IndexTupleData set in a btree page
     304             :  *
     305             :  * Usage: SELECT * FROM bt_page_items('t1_pkey', 1);
     306             :  *-------------------------------------------------------
     307             :  */
     308             : Datum
     309           8 : bt_page_items(PG_FUNCTION_ARGS)
     310             : {
     311           8 :     text       *relname = PG_GETARG_TEXT_PP(0);
     312           8 :     uint32      blkno = PG_GETARG_UINT32(1);
     313             :     Datum       result;
     314             :     FuncCallContext *fctx;
     315             :     MemoryContext mctx;
     316             :     struct user_args *uargs;
     317             : 
     318           8 :     if (!superuser())
     319           0 :         ereport(ERROR,
     320             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     321             :                  (errmsg("must be superuser to use pageinspect functions"))));
     322             : 
     323           8 :     if (SRF_IS_FIRSTCALL())
     324             :     {
     325             :         RangeVar   *relrv;
     326             :         Relation    rel;
     327             :         Buffer      buffer;
     328             :         BTPageOpaque opaque;
     329             :         TupleDesc   tupleDesc;
     330             : 
     331           6 :         fctx = SRF_FIRSTCALL_INIT();
     332             : 
     333           6 :         relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     334           6 :         rel = relation_openrv(relrv, AccessShareLock);
     335             : 
     336           6 :         if (!IS_INDEX(rel) || !IS_BTREE(rel))
     337           0 :             elog(ERROR, "relation \"%s\" is not a btree index",
     338             :                  RelationGetRelationName(rel));
     339             : 
     340             :         /*
     341             :          * Reject attempts to read non-local temporary relations; we would be
     342             :          * likely to get wrong data since we have no visibility into the
     343             :          * owning session's local buffers.
     344             :          */
     345           6 :         if (RELATION_IS_OTHER_TEMP(rel))
     346           0 :             ereport(ERROR,
     347             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     348             :                      errmsg("cannot access temporary tables of other sessions")));
     349             : 
     350           6 :         if (blkno == 0)
     351           2 :             elog(ERROR, "block 0 is a meta page");
     352             : 
     353           4 :         CHECK_RELATION_BLOCK_RANGE(rel, blkno);
     354             : 
     355           2 :         buffer = ReadBuffer(rel, blkno);
     356           2 :         LockBuffer(buffer, BUFFER_LOCK_SHARE);
     357             : 
     358             :         /*
     359             :          * We copy the page into local storage to avoid holding pin on the
     360             :          * buffer longer than we must, and possibly failing to release it at
     361             :          * all if the calling query doesn't fetch all rows.
     362             :          */
     363           2 :         mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
     364             : 
     365           2 :         uargs = palloc(sizeof(struct user_args));
     366             : 
     367           2 :         uargs->page = palloc(BLCKSZ);
     368           2 :         memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ);
     369             : 
     370           2 :         UnlockReleaseBuffer(buffer);
     371           2 :         relation_close(rel, AccessShareLock);
     372             : 
     373           2 :         uargs->offset = FirstOffsetNumber;
     374             : 
     375           2 :         opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page);
     376             : 
     377           2 :         if (P_ISDELETED(opaque))
     378           0 :             elog(NOTICE, "page is deleted");
     379             : 
     380           2 :         fctx->max_calls = PageGetMaxOffsetNumber(uargs->page);
     381             : 
     382             :         /* Build a tuple descriptor for our result type */
     383           2 :         if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
     384           0 :             elog(ERROR, "return type must be a row type");
     385             : 
     386           2 :         fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc);
     387             : 
     388           2 :         fctx->user_fctx = uargs;
     389             : 
     390           2 :         MemoryContextSwitchTo(mctx);
     391             :     }
     392             : 
     393           4 :     fctx = SRF_PERCALL_SETUP();
     394           4 :     uargs = fctx->user_fctx;
     395             : 
     396           4 :     if (fctx->call_cntr < fctx->max_calls)
     397             :     {
     398           2 :         result = bt_page_print_tuples(fctx, uargs->page, uargs->offset);
     399           2 :         uargs->offset++;
     400           2 :         SRF_RETURN_NEXT(fctx, result);
     401             :     }
     402             :     else
     403             :     {
     404           2 :         pfree(uargs->page);
     405           2 :         pfree(uargs);
     406           2 :         SRF_RETURN_DONE(fctx);
     407             :     }
     408             : }
     409             : 
     410             : /*-------------------------------------------------------
     411             :  * bt_page_items_bytea()
     412             :  *
     413             :  * Get IndexTupleData set in a btree page
     414             :  *
     415             :  * Usage: SELECT * FROM bt_page_items(get_raw_page('t1_pkey', 1));
     416             :  *-------------------------------------------------------
     417             :  */
     418             : 
     419             : Datum
     420           6 : bt_page_items_bytea(PG_FUNCTION_ARGS)
     421             : {
     422           6 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
     423             :     Datum       result;
     424             :     FuncCallContext *fctx;
     425             :     struct user_args *uargs;
     426             :     int         raw_page_size;
     427             : 
     428           6 :     if (!superuser())
     429           0 :         ereport(ERROR,
     430             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     431             :                  (errmsg("must be superuser to use raw page functions"))));
     432             : 
     433           6 :     if (SRF_IS_FIRSTCALL())
     434             :     {
     435             :         BTPageOpaque opaque;
     436             :         MemoryContext mctx;
     437             :         TupleDesc   tupleDesc;
     438             : 
     439           4 :         raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
     440             : 
     441           4 :         if (raw_page_size < SizeOfPageHeaderData)
     442           0 :             ereport(ERROR,
     443             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     444             :                      errmsg("input page too small (%d bytes)", raw_page_size)));
     445             : 
     446           4 :         fctx = SRF_FIRSTCALL_INIT();
     447           4 :         mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
     448             : 
     449           4 :         uargs = palloc(sizeof(struct user_args));
     450             : 
     451           4 :         uargs->page = VARDATA(raw_page);
     452             : 
     453           4 :         uargs->offset = FirstOffsetNumber;
     454             : 
     455           4 :         opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page);
     456             : 
     457           4 :         if (P_ISMETA(opaque))
     458           2 :             ereport(ERROR,
     459             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     460             :                      errmsg("block is a meta page")));
     461             : 
     462           2 :         if (P_ISDELETED(opaque))
     463           0 :             elog(NOTICE, "page is deleted");
     464             : 
     465           2 :         fctx->max_calls = PageGetMaxOffsetNumber(uargs->page);
     466             : 
     467             :         /* Build a tuple descriptor for our result type */
     468           2 :         if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
     469           0 :             elog(ERROR, "return type must be a row type");
     470             : 
     471           2 :         fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc);
     472             : 
     473           2 :         fctx->user_fctx = uargs;
     474             : 
     475           2 :         MemoryContextSwitchTo(mctx);
     476             :     }
     477             : 
     478           4 :     fctx = SRF_PERCALL_SETUP();
     479           4 :     uargs = fctx->user_fctx;
     480             : 
     481           4 :     if (fctx->call_cntr < fctx->max_calls)
     482             :     {
     483           2 :         result = bt_page_print_tuples(fctx, uargs->page, uargs->offset);
     484           2 :         uargs->offset++;
     485           2 :         SRF_RETURN_NEXT(fctx, result);
     486             :     }
     487             :     else
     488             :     {
     489           2 :         pfree(uargs);
     490           2 :         SRF_RETURN_DONE(fctx);
     491             :     }
     492             : }
     493             : 
     494             : 
     495             : /* ------------------------------------------------
     496             :  * bt_metap()
     497             :  *
     498             :  * Get a btree's meta-page information
     499             :  *
     500             :  * Usage: SELECT * FROM bt_metap('t1_pkey')
     501             :  * ------------------------------------------------
     502             :  */
     503             : Datum
     504           2 : bt_metap(PG_FUNCTION_ARGS)
     505             : {
     506           2 :     text       *relname = PG_GETARG_TEXT_PP(0);
     507             :     Datum       result;
     508             :     Relation    rel;
     509             :     RangeVar   *relrv;
     510             :     BTMetaPageData *metad;
     511             :     TupleDesc   tupleDesc;
     512             :     int         j;
     513             :     char       *values[8];
     514             :     Buffer      buffer;
     515             :     Page        page;
     516             :     HeapTuple   tuple;
     517             : 
     518           2 :     if (!superuser())
     519           0 :         ereport(ERROR,
     520             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     521             :                  (errmsg("must be superuser to use pageinspect functions"))));
     522             : 
     523           2 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     524           2 :     rel = relation_openrv(relrv, AccessShareLock);
     525             : 
     526           2 :     if (!IS_INDEX(rel) || !IS_BTREE(rel))
     527           0 :         elog(ERROR, "relation \"%s\" is not a btree index",
     528             :              RelationGetRelationName(rel));
     529             : 
     530             :     /*
     531             :      * Reject attempts to read non-local temporary relations; we would be
     532             :      * likely to get wrong data since we have no visibility into the owning
     533             :      * session's local buffers.
     534             :      */
     535           2 :     if (RELATION_IS_OTHER_TEMP(rel))
     536           0 :         ereport(ERROR,
     537             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     538             :                  errmsg("cannot access temporary tables of other sessions")));
     539             : 
     540           2 :     buffer = ReadBuffer(rel, 0);
     541           2 :     LockBuffer(buffer, BUFFER_LOCK_SHARE);
     542             : 
     543           2 :     page = BufferGetPage(buffer);
     544           2 :     metad = BTPageGetMeta(page);
     545             : 
     546             :     /* Build a tuple descriptor for our result type */
     547           2 :     if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
     548           0 :         elog(ERROR, "return type must be a row type");
     549             : 
     550           2 :     j = 0;
     551           2 :     values[j++] = psprintf("%d", metad->btm_magic);
     552           2 :     values[j++] = psprintf("%d", metad->btm_version);
     553           2 :     values[j++] = psprintf("%d", metad->btm_root);
     554           2 :     values[j++] = psprintf("%d", metad->btm_level);
     555           2 :     values[j++] = psprintf("%d", metad->btm_fastroot);
     556           2 :     values[j++] = psprintf("%d", metad->btm_fastlevel);
     557             : 
     558             :     /*
     559             :      * Get values of extended metadata if available, use default values
     560             :      * otherwise.
     561             :      */
     562           2 :     if (metad->btm_version >= BTREE_NOVAC_VERSION)
     563             :     {
     564           2 :         values[j++] = psprintf("%u", metad->btm_oldest_btpo_xact);
     565           2 :         values[j++] = psprintf("%f", metad->btm_last_cleanup_num_heap_tuples);
     566             :     }
     567             :     else
     568             :     {
     569           0 :         values[j++] = "0";
     570           0 :         values[j++] = "-1";
     571             :     }
     572             : 
     573           2 :     tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
     574             :                                    values);
     575             : 
     576           2 :     result = HeapTupleGetDatum(tuple);
     577             : 
     578           2 :     UnlockReleaseBuffer(buffer);
     579           2 :     relation_close(rel, AccessShareLock);
     580             : 
     581           2 :     PG_RETURN_DATUM(result);
     582             : }

Generated by: LCOV version 1.13