LCOV - code coverage report
Current view: top level - contrib/pageinspect - brinfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 146 169 86.4 %
Date: 2024-04-23 08:11:33 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * brinfuncs.c
       3             :  *      Functions to investigate BRIN indexes
       4             :  *
       5             :  * Copyright (c) 2014-2024, PostgreSQL Global Development Group
       6             :  *
       7             :  * IDENTIFICATION
       8             :  *      contrib/pageinspect/brinfuncs.c
       9             :  */
      10             : #include "postgres.h"
      11             : 
      12             : #include "access/brin.h"
      13             : #include "access/brin_internal.h"
      14             : #include "access/brin_page.h"
      15             : #include "access/brin_revmap.h"
      16             : #include "access/brin_tuple.h"
      17             : #include "access/htup_details.h"
      18             : #include "catalog/index.h"
      19             : #include "catalog/pg_am_d.h"
      20             : #include "catalog/pg_type.h"
      21             : #include "funcapi.h"
      22             : #include "lib/stringinfo.h"
      23             : #include "miscadmin.h"
      24             : #include "pageinspect.h"
      25             : #include "utils/array.h"
      26             : #include "utils/builtins.h"
      27             : #include "utils/lsyscache.h"
      28             : #include "utils/rel.h"
      29             : 
      30          14 : PG_FUNCTION_INFO_V1(brin_page_type);
      31          48 : PG_FUNCTION_INFO_V1(brin_page_items);
      32          16 : PG_FUNCTION_INFO_V1(brin_metapage_info);
      33          14 : PG_FUNCTION_INFO_V1(brin_revmap_data);
      34             : 
      35             : #define IS_BRIN(r) ((r)->rd_rel->relam == BRIN_AM_OID)
      36             : 
      37             : typedef struct brin_column_state
      38             : {
      39             :     int         nstored;
      40             :     FmgrInfo    outputFn[FLEXIBLE_ARRAY_MEMBER];
      41             : } brin_column_state;
      42             : 
      43             : 
      44             : static Page verify_brin_page(bytea *raw_page, uint16 type,
      45             :                              const char *strtype);
      46             : 
      47             : Datum
      48          10 : brin_page_type(PG_FUNCTION_ARGS)
      49             : {
      50          10 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
      51             :     Page        page;
      52             :     char       *type;
      53             : 
      54          10 :     if (!superuser())
      55           0 :         ereport(ERROR,
      56             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
      57             :                  errmsg("must be superuser to use raw page functions")));
      58             : 
      59          10 :     page = get_page_from_raw(raw_page);
      60             : 
      61          10 :     if (PageIsNew(page))
      62           2 :         PG_RETURN_NULL();
      63             : 
      64             :     /* verify the special space has the expected size */
      65           8 :     if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BrinSpecialSpace)))
      66           2 :         ereport(ERROR,
      67             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      68             :                  errmsg("input page is not a valid %s page", "BRIN"),
      69             :                  errdetail("Expected special size %d, got %d.",
      70             :                            (int) MAXALIGN(sizeof(BrinSpecialSpace)),
      71             :                            (int) PageGetSpecialSize(page))));
      72             : 
      73           6 :     switch (BrinPageType(page))
      74             :     {
      75           2 :         case BRIN_PAGETYPE_META:
      76           2 :             type = "meta";
      77           2 :             break;
      78           2 :         case BRIN_PAGETYPE_REVMAP:
      79           2 :             type = "revmap";
      80           2 :             break;
      81           2 :         case BRIN_PAGETYPE_REGULAR:
      82           2 :             type = "regular";
      83           2 :             break;
      84           0 :         default:
      85           0 :             type = psprintf("unknown (%02x)", BrinPageType(page));
      86           0 :             break;
      87             :     }
      88             : 
      89           6 :     PG_RETURN_TEXT_P(cstring_to_text(type));
      90             : }
      91             : 
      92             : /*
      93             :  * Verify that the given bytea contains a BRIN page of the indicated page
      94             :  * type, or die in the attempt.  A pointer to the page is returned.
      95             :  */
      96             : static Page
      97          80 : verify_brin_page(bytea *raw_page, uint16 type, const char *strtype)
      98             : {
      99          80 :     Page        page = get_page_from_raw(raw_page);
     100             : 
     101          80 :     if (PageIsNew(page))
     102           6 :         return page;
     103             : 
     104             :     /* verify the special space has the expected size */
     105          74 :     if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BrinSpecialSpace)))
     106           6 :         ereport(ERROR,
     107             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     108             :                  errmsg("input page is not a valid %s page", "BRIN"),
     109             :                  errdetail("Expected special size %d, got %d.",
     110             :                            (int) MAXALIGN(sizeof(BrinSpecialSpace)),
     111             :                            (int) PageGetSpecialSize(page))));
     112             : 
     113             :     /* verify the special space says this page is what we want */
     114          68 :     if (BrinPageType(page) != type)
     115           4 :         ereport(ERROR,
     116             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     117             :                  errmsg("page is not a BRIN page of type \"%s\"", strtype),
     118             :                  errdetail("Expected special type %08x, got %08x.",
     119             :                            type, BrinPageType(page))));
     120             : 
     121          64 :     return page;
     122             : }
     123             : 
     124             : 
     125             : /*
     126             :  * Extract all item values from a BRIN index page
     127             :  *
     128             :  * Usage: SELECT * FROM brin_page_items(get_raw_page('idx', 1), 'idx'::regclass);
     129             :  */
     130             : Datum
     131          44 : brin_page_items(PG_FUNCTION_ARGS)
     132             : {
     133          44 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
     134          44 :     Oid         indexRelid = PG_GETARG_OID(1);
     135          44 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     136             :     Relation    indexRel;
     137             :     brin_column_state **columns;
     138             :     BrinDesc   *bdesc;
     139             :     BrinMemTuple *dtup;
     140             :     Page        page;
     141             :     OffsetNumber offset;
     142             :     AttrNumber  attno;
     143             :     bool        unusedItem;
     144             : 
     145          44 :     if (!superuser())
     146           0 :         ereport(ERROR,
     147             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     148             :                  errmsg("must be superuser to use raw page functions")));
     149             : 
     150          44 :     InitMaterializedSRF(fcinfo, 0);
     151             : 
     152          44 :     indexRel = index_open(indexRelid, AccessShareLock);
     153             : 
     154          44 :     if (!IS_BRIN(indexRel))
     155           2 :         ereport(ERROR,
     156             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     157             :                  errmsg("\"%s\" is not a %s index",
     158             :                         RelationGetRelationName(indexRel), "BRIN")));
     159             : 
     160          42 :     bdesc = brin_build_desc(indexRel);
     161             : 
     162             :     /* minimally verify the page we got */
     163          42 :     page = verify_brin_page(raw_page, BRIN_PAGETYPE_REGULAR, "regular");
     164             : 
     165          40 :     if (PageIsNew(page))
     166             :     {
     167           2 :         brin_free_desc(bdesc);
     168           2 :         index_close(indexRel, AccessShareLock);
     169           2 :         PG_RETURN_NULL();
     170             :     }
     171             : 
     172             :     /*
     173             :      * Initialize output functions for all indexed datatypes; simplifies
     174             :      * calling them later.
     175             :      */
     176          38 :     columns = palloc(sizeof(brin_column_state *) * RelationGetDescr(indexRel)->natts);
     177         124 :     for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++)
     178             :     {
     179             :         Oid         output;
     180             :         bool        isVarlena;
     181             :         BrinOpcInfo *opcinfo;
     182             :         int         i;
     183             :         brin_column_state *column;
     184             : 
     185          86 :         opcinfo = bdesc->bd_info[attno - 1];
     186          86 :         column = palloc(offsetof(brin_column_state, outputFn) +
     187          86 :                         sizeof(FmgrInfo) * opcinfo->oi_nstored);
     188             : 
     189          86 :         column->nstored = opcinfo->oi_nstored;
     190         226 :         for (i = 0; i < opcinfo->oi_nstored; i++)
     191             :         {
     192         140 :             getTypeOutputInfo(opcinfo->oi_typcache[i]->type_id, &output, &isVarlena);
     193         140 :             fmgr_info(output, &column->outputFn[i]);
     194             :         }
     195             : 
     196          86 :         columns[attno - 1] = column;
     197             :     }
     198             : 
     199          38 :     offset = FirstOffsetNumber;
     200          38 :     unusedItem = false;
     201          38 :     dtup = NULL;
     202             :     for (;;)
     203        1032 :     {
     204             :         Datum       values[8];
     205        1070 :         bool        nulls[8] = {0};
     206             : 
     207             :         /*
     208             :          * This loop is called once for every attribute of every tuple in the
     209             :          * page.  At the start of a tuple, we get a NULL dtup; that's our
     210             :          * signal for obtaining and decoding the next one.  If that's not the
     211             :          * case, we output the next attribute.
     212             :          */
     213        1070 :         if (dtup == NULL)
     214             :         {
     215             :             ItemId      itemId;
     216             : 
     217             :             /* verify item status: if there's no data, we can't decode */
     218         302 :             itemId = PageGetItemId(page, offset);
     219         302 :             if (ItemIdIsUsed(itemId))
     220             :             {
     221         302 :                 dtup = brin_deform_tuple(bdesc,
     222         302 :                                          (BrinTuple *) PageGetItem(page, itemId),
     223             :                                          NULL);
     224         302 :                 attno = 1;
     225         302 :                 unusedItem = false;
     226             :             }
     227             :             else
     228           0 :                 unusedItem = true;
     229             :         }
     230             :         else
     231         768 :             attno++;
     232             : 
     233        1070 :         if (unusedItem)
     234             :         {
     235           0 :             values[0] = UInt16GetDatum(offset);
     236           0 :             nulls[1] = true;
     237           0 :             nulls[2] = true;
     238           0 :             nulls[3] = true;
     239           0 :             nulls[4] = true;
     240           0 :             nulls[5] = true;
     241           0 :             nulls[6] = true;
     242           0 :             nulls[7] = true;
     243             :         }
     244             :         else
     245             :         {
     246        1070 :             int         att = attno - 1;
     247             : 
     248        1070 :             values[0] = UInt16GetDatum(offset);
     249        1070 :             switch (TupleDescAttr(rsinfo->setDesc, 1)->atttypid)
     250             :             {
     251        1070 :                 case INT8OID:
     252        1070 :                     values[1] = Int64GetDatum((int64) dtup->bt_blkno);
     253        1070 :                     break;
     254           0 :                 case INT4OID:
     255             :                     /* support for old extension version */
     256           0 :                     values[1] = UInt32GetDatum(dtup->bt_blkno);
     257           0 :                     break;
     258           0 :                 default:
     259           0 :                     elog(ERROR, "incorrect output types");
     260             :             }
     261        1070 :             values[2] = UInt16GetDatum(attno);
     262        1070 :             values[3] = BoolGetDatum(dtup->bt_columns[att].bv_allnulls);
     263        1070 :             values[4] = BoolGetDatum(dtup->bt_columns[att].bv_hasnulls);
     264        1070 :             values[5] = BoolGetDatum(dtup->bt_placeholder);
     265        1070 :             values[6] = BoolGetDatum(dtup->bt_empty_range);
     266        1070 :             if (!dtup->bt_columns[att].bv_allnulls)
     267             :             {
     268         946 :                 BrinValues *bvalues = &dtup->bt_columns[att];
     269             :                 StringInfoData s;
     270             :                 bool        first;
     271             :                 int         i;
     272             : 
     273         946 :                 initStringInfo(&s);
     274         946 :                 appendStringInfoChar(&s, '{');
     275             : 
     276         946 :                 first = true;
     277        2390 :                 for (i = 0; i < columns[att]->nstored; i++)
     278             :                 {
     279             :                     char       *val;
     280             : 
     281        1444 :                     if (!first)
     282         498 :                         appendStringInfoString(&s, " .. ");
     283        1444 :                     first = false;
     284        1444 :                     val = OutputFunctionCall(&columns[att]->outputFn[i],
     285        1444 :                                              bvalues->bv_values[i]);
     286        1444 :                     appendStringInfoString(&s, val);
     287        1444 :                     pfree(val);
     288             :                 }
     289         946 :                 appendStringInfoChar(&s, '}');
     290             : 
     291         946 :                 values[7] = CStringGetTextDatum(s.data);
     292         946 :                 pfree(s.data);
     293             :             }
     294             :             else
     295             :             {
     296         124 :                 nulls[7] = true;
     297             :             }
     298             :         }
     299             : 
     300        1070 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
     301             : 
     302             :         /*
     303             :          * If the item was unused, jump straight to the next one; otherwise,
     304             :          * the only cleanup needed here is to set our signal to go to the next
     305             :          * tuple in the following iteration, by freeing the current one.
     306             :          */
     307        1070 :         if (unusedItem)
     308           0 :             offset = OffsetNumberNext(offset);
     309        1070 :         else if (attno >= bdesc->bd_tupdesc->natts)
     310             :         {
     311         302 :             pfree(dtup);
     312         302 :             dtup = NULL;
     313         302 :             offset = OffsetNumberNext(offset);
     314             :         }
     315             : 
     316             :         /*
     317             :          * If we're beyond the end of the page, we're done.
     318             :          */
     319        1070 :         if (offset > PageGetMaxOffsetNumber(page))
     320          38 :             break;
     321             :     }
     322             : 
     323          38 :     brin_free_desc(bdesc);
     324          38 :     index_close(indexRel, AccessShareLock);
     325             : 
     326          38 :     return (Datum) 0;
     327             : }
     328             : 
     329             : Datum
     330          30 : brin_metapage_info(PG_FUNCTION_ARGS)
     331             : {
     332          30 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
     333             :     Page        page;
     334             :     BrinMetaPageData *meta;
     335             :     TupleDesc   tupdesc;
     336             :     Datum       values[4];
     337          30 :     bool        nulls[4] = {0};
     338             :     HeapTuple   htup;
     339             : 
     340          30 :     if (!superuser())
     341           0 :         ereport(ERROR,
     342             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     343             :                  errmsg("must be superuser to use raw page functions")));
     344             : 
     345          30 :     page = verify_brin_page(raw_page, BRIN_PAGETYPE_META, "metapage");
     346             : 
     347          26 :     if (PageIsNew(page))
     348           2 :         PG_RETURN_NULL();
     349             : 
     350             :     /* Build a tuple descriptor for our result type */
     351          24 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     352           0 :         elog(ERROR, "return type must be a row type");
     353          24 :     tupdesc = BlessTupleDesc(tupdesc);
     354             : 
     355             :     /* Extract values from the metapage */
     356          24 :     meta = (BrinMetaPageData *) PageGetContents(page);
     357          24 :     values[0] = CStringGetTextDatum(psprintf("0x%08X", meta->brinMagic));
     358          24 :     values[1] = Int32GetDatum(meta->brinVersion);
     359          24 :     values[2] = Int32GetDatum(meta->pagesPerRange);
     360          24 :     values[3] = Int64GetDatum(meta->lastRevmapPage);
     361             : 
     362          24 :     htup = heap_form_tuple(tupdesc, values, nulls);
     363             : 
     364          24 :     PG_RETURN_DATUM(HeapTupleGetDatum(htup));
     365             : }
     366             : 
     367             : /*
     368             :  * Return the TID array stored in a BRIN revmap page
     369             :  */
     370             : Datum
     371        2728 : brin_revmap_data(PG_FUNCTION_ARGS)
     372             : {
     373             :     struct
     374             :     {
     375             :         ItemPointerData *tids;
     376             :         int         idx;
     377             :     }          *state;
     378             :     FuncCallContext *fctx;
     379             : 
     380        2728 :     if (!superuser())
     381           0 :         ereport(ERROR,
     382             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     383             :                  errmsg("must be superuser to use raw page functions")));
     384             : 
     385        2728 :     if (SRF_IS_FIRSTCALL())
     386             :     {
     387           8 :         bytea      *raw_page = PG_GETARG_BYTEA_P(0);
     388             :         MemoryContext mctx;
     389             :         Page        page;
     390             : 
     391             :         /* create a function context for cross-call persistence */
     392           8 :         fctx = SRF_FIRSTCALL_INIT();
     393             : 
     394             :         /* switch to memory context appropriate for multiple function calls */
     395           8 :         mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
     396             : 
     397             :         /* minimally verify the page we got */
     398           8 :         page = verify_brin_page(raw_page, BRIN_PAGETYPE_REVMAP, "revmap");
     399             : 
     400           4 :         if (PageIsNew(page))
     401             :         {
     402           2 :             MemoryContextSwitchTo(mctx);
     403           2 :             PG_RETURN_NULL();
     404             :         }
     405             : 
     406           2 :         state = palloc(sizeof(*state));
     407           2 :         state->tids = ((RevmapContents *) PageGetContents(page))->rm_tids;
     408           2 :         state->idx = 0;
     409             : 
     410           2 :         fctx->user_fctx = state;
     411             : 
     412           2 :         MemoryContextSwitchTo(mctx);
     413             :     }
     414             : 
     415        2722 :     fctx = SRF_PERCALL_SETUP();
     416        2722 :     state = fctx->user_fctx;
     417             : 
     418        2722 :     if (state->idx < REVMAP_PAGE_MAXITEMS)
     419        2720 :         SRF_RETURN_NEXT(fctx, PointerGetDatum(&state->tids[state->idx++]));
     420             : 
     421           2 :     SRF_RETURN_DONE(fctx);
     422             : }

Generated by: LCOV version 1.14