LCOV - differential code coverage report
Current view: top level - contrib/pageinspect - brinfuncs.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 86.2 % 167 144 1 7 15 2 38 2 102 6 38 3
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 9 9 2 1 6 2
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*
       2                 :  * brinfuncs.c
       3                 :  *      Functions to investigate BRIN indexes
       4                 :  *
       5                 :  * Copyright (c) 2014-2023, 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 CBC           7 : PG_FUNCTION_INFO_V1(brin_page_type);
      31              18 : PG_FUNCTION_INFO_V1(brin_page_items);
      32               8 : PG_FUNCTION_INFO_V1(brin_metapage_info);
      33               7 : 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               5 : brin_page_type(PG_FUNCTION_ARGS)
      49                 : {
      50               5 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
      51                 :     Page        page;
      52                 :     char       *type;
      53                 : 
      54               5 :     if (!superuser())
      55 UBC           0 :         ereport(ERROR,
      56                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
      57                 :                  errmsg("must be superuser to use raw page functions")));
      58                 : 
      59 CBC           5 :     page = get_page_from_raw(raw_page);
      60                 : 
      61               5 :     if (PageIsNew(page))
      62               1 :         PG_RETURN_NULL();
      63                 : 
      64                 :     /* verify the special space has the expected size */
      65               4 :     if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BrinSpecialSpace)))
      66               1 :         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               3 :     switch (BrinPageType(page))
      74                 :     {
      75               1 :         case BRIN_PAGETYPE_META:
      76               1 :             type = "meta";
      77               1 :             break;
      78               1 :         case BRIN_PAGETYPE_REVMAP:
      79               1 :             type = "revmap";
      80               1 :             break;
      81               1 :         case BRIN_PAGETYPE_REGULAR:
      82               1 :             type = "regular";
      83               1 :             break;
      84 UBC           0 :         default:
      85               0 :             type = psprintf("unknown (%02x)", BrinPageType(page));
      86               0 :             break;
      87                 :     }
      88                 : 
      89 CBC           3 :     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              29 : verify_brin_page(bytea *raw_page, uint16 type, const char *strtype)
      98                 : {
      99              29 :     Page        page = get_page_from_raw(raw_page);
     100                 : 
     101              29 :     if (PageIsNew(page))
     102               3 :         return page;
     103                 : 
     104                 :     /* verify the special space has the expected size */
     105              26 :     if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BrinSpecialSpace)))
     106               3 :         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              23 :     if (BrinPageType(page) != type)
     115               2 :         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              21 :     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              12 : brin_page_items(PG_FUNCTION_ARGS)
     132                 : {
     133              12 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
     134              12 :     Oid         indexRelid = PG_GETARG_OID(1);
     135              12 :     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              12 :     if (!superuser())
     146 UBC           0 :         ereport(ERROR,
     147                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     148                 :                  errmsg("must be superuser to use raw page functions")));
     149                 : 
     150 CBC          12 :     InitMaterializedSRF(fcinfo, 0);
     151                 : 
     152              12 :     indexRel = index_open(indexRelid, AccessShareLock);
     153                 : 
     154              12 :     if (!IS_BRIN(indexRel))
     155               1 :         ereport(ERROR,
     156                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     157                 :                  errmsg("\"%s\" is not a %s index",
     158                 :                         RelationGetRelationName(indexRel), "BRIN")));
     159                 : 
     160              11 :     bdesc = brin_build_desc(indexRel);
     161                 : 
     162                 :     /* minimally verify the page we got */
     163              11 :     page = verify_brin_page(raw_page, BRIN_PAGETYPE_REGULAR, "regular");
     164                 : 
     165              10 :     if (PageIsNew(page))
     166                 :     {
     167               1 :         brin_free_desc(bdesc);
     168               1 :         index_close(indexRel, AccessShareLock);
     169               1 :         PG_RETURN_NULL();
     170                 :     }
     171                 : 
     172                 :     /*
     173                 :      * Initialize output functions for all indexed datatypes; simplifies
     174                 :      * calling them later.
     175                 :      */
     176               9 :     columns = palloc(sizeof(brin_column_state *) * RelationGetDescr(indexRel)->natts);
     177              18 :     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               9 :         opcinfo = bdesc->bd_info[attno - 1];
     186               9 :         column = palloc(offsetof(brin_column_state, outputFn) +
     187               9 :                         sizeof(FmgrInfo) * opcinfo->oi_nstored);
     188                 : 
     189               9 :         column->nstored = opcinfo->oi_nstored;
     190              27 :         for (i = 0; i < opcinfo->oi_nstored; i++)
     191                 :         {
     192              18 :             getTypeOutputInfo(opcinfo->oi_typcache[i]->type_id, &output, &isVarlena);
     193              18 :             fmgr_info(output, &column->outputFn[i]);
     194                 :         }
     195                 : 
     196               9 :         columns[attno - 1] = column;
     197                 :     }
     198                 : 
     199               9 :     offset = FirstOffsetNumber;
     200               9 :     unusedItem = false;
     201               9 :     dtup = NULL;
     202                 :     for (;;)
     203               8 :     {
     204                 :         Datum       values[7];
     205 GNC          17 :         bool        nulls[7] = {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 CBC          17 :         if (dtup == NULL)
     214                 :         {
     215                 :             ItemId      itemId;
     216                 : 
     217                 :             /* verify item status: if there's no data, we can't decode */
     218              17 :             itemId = PageGetItemId(page, offset);
     219              17 :             if (ItemIdIsUsed(itemId))
     220                 :             {
     221              17 :                 dtup = brin_deform_tuple(bdesc,
     222              17 :                                          (BrinTuple *) PageGetItem(page, itemId),
     223                 :                                          NULL);
     224              17 :                 attno = 1;
     225              17 :                 unusedItem = false;
     226                 :             }
     227                 :             else
     228 UBC           0 :                 unusedItem = true;
     229                 :         }
     230                 :         else
     231               0 :             attno++;
     232                 : 
     233 GBC          17 :         if (unusedItem)
     234 EUB             :         {
     235 UBC           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 UIC           0 :             nulls[5] = true;
     241               0 :             nulls[6] = true;
     242                 :         }
     243 ECB             :         else
     244                 :         {
     245 CBC          17 :             int         att = attno - 1;
     246 ECB             : 
     247 GIC          17 :             values[0] = UInt16GetDatum(offset);
     248 CBC          17 :             switch (TupleDescAttr(rsinfo->setDesc, 1)->atttypid)
     249 ECB             :             {
     250 CBC          17 :                 case INT8OID:
     251 GBC          17 :                     values[1] = Int64GetDatum((int64) dtup->bt_blkno);
     252 GIC          17 :                     break;
     253 UBC           0 :                 case INT4OID:
     254 EUB             :                     /* support for old extension version */
     255 UBC           0 :                     values[1] = UInt32GetDatum(dtup->bt_blkno);
     256               0 :                     break;
     257 UIC           0 :                 default:
     258 LBC           0 :                     elog(ERROR, "incorrect output types");
     259 ECB             :             }
     260 CBC          17 :             values[2] = UInt16GetDatum(attno);
     261              17 :             values[3] = BoolGetDatum(dtup->bt_columns[att].bv_allnulls);
     262              17 :             values[4] = BoolGetDatum(dtup->bt_columns[att].bv_hasnulls);
     263 GIC          17 :             values[5] = BoolGetDatum(dtup->bt_placeholder);
     264 CBC          17 :             if (!dtup->bt_columns[att].bv_allnulls)
     265                 :             {
     266 GIC          16 :                 BrinValues *bvalues = &dtup->bt_columns[att];
     267                 :                 StringInfoData s;
     268                 :                 bool        first;
     269 ECB             :                 int         i;
     270                 : 
     271 GIC          16 :                 initStringInfo(&s);
     272 CBC          16 :                 appendStringInfoChar(&s, '{');
     273 ECB             : 
     274 GIC          16 :                 first = true;
     275              48 :                 for (i = 0; i < columns[att]->nstored; i++)
     276                 :                 {
     277 ECB             :                     char       *val;
     278                 : 
     279 CBC          32 :                     if (!first)
     280              16 :                         appendStringInfoString(&s, " .. ");
     281              32 :                     first = false;
     282              32 :                     val = OutputFunctionCall(&columns[att]->outputFn[i],
     283              32 :                                              bvalues->bv_values[i]);
     284 GIC          32 :                     appendStringInfoString(&s, val);
     285 CBC          32 :                     pfree(val);
     286                 :                 }
     287              16 :                 appendStringInfoChar(&s, '}');
     288 ECB             : 
     289 GIC          16 :                 values[6] = CStringGetTextDatum(s.data);
     290              16 :                 pfree(s.data);
     291                 :             }
     292 ECB             :             else
     293                 :             {
     294 GIC           1 :                 nulls[6] = true;
     295                 :             }
     296 ECB             :         }
     297                 : 
     298 GIC          17 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
     299                 : 
     300                 :         /*
     301                 :          * If the item was unused, jump straight to the next one; otherwise,
     302                 :          * the only cleanup needed here is to set our signal to go to the next
     303 ECB             :          * tuple in the following iteration, by freeing the current one.
     304 EUB             :          */
     305 CBC          17 :         if (unusedItem)
     306 UIC           0 :             offset = OffsetNumberNext(offset);
     307 CBC          17 :         else if (attno >= bdesc->bd_tupdesc->natts)
     308 ECB             :         {
     309 CBC          17 :             pfree(dtup);
     310 GIC          17 :             dtup = NULL;
     311              17 :             offset = OffsetNumberNext(offset);
     312                 :         }
     313                 : 
     314                 :         /*
     315 ECB             :          * If we're beyond the end of the page, we're done.
     316                 :          */
     317 GIC          17 :         if (offset > PageGetMaxOffsetNumber(page))
     318               9 :             break;
     319 ECB             :     }
     320                 : 
     321 GIC           9 :     brin_free_desc(bdesc);
     322 CBC           9 :     index_close(indexRel, AccessShareLock);
     323                 : 
     324 GIC           9 :     return (Datum) 0;
     325                 : }
     326 ECB             : 
     327                 : Datum
     328 CBC          14 : brin_metapage_info(PG_FUNCTION_ARGS)
     329                 : {
     330 GIC          14 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
     331                 :     Page        page;
     332                 :     BrinMetaPageData *meta;
     333 ECB             :     TupleDesc   tupdesc;
     334                 :     Datum       values[4];
     335 GNC          14 :     bool        nulls[4] = {0};
     336 ECB             :     HeapTuple   htup;
     337 EUB             : 
     338 GIC          14 :     if (!superuser())
     339 UIC           0 :         ereport(ERROR,
     340                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     341 ECB             :                  errmsg("must be superuser to use raw page functions")));
     342                 : 
     343 CBC          14 :     page = verify_brin_page(raw_page, BRIN_PAGETYPE_META, "metapage");
     344 ECB             : 
     345 GIC          12 :     if (PageIsNew(page))
     346               1 :         PG_RETURN_NULL();
     347 ECB             : 
     348 EUB             :     /* Build a tuple descriptor for our result type */
     349 CBC          11 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     350 UIC           0 :         elog(ERROR, "return type must be a row type");
     351 GIC          11 :     tupdesc = BlessTupleDesc(tupdesc);
     352 ECB             : 
     353                 :     /* Extract values from the metapage */
     354 CBC          11 :     meta = (BrinMetaPageData *) PageGetContents(page);
     355              11 :     values[0] = CStringGetTextDatum(psprintf("0x%08X", meta->brinMagic));
     356 GIC          11 :     values[1] = Int32GetDatum(meta->brinVersion);
     357 CBC          11 :     values[2] = Int32GetDatum(meta->pagesPerRange);
     358 GIC          11 :     values[3] = Int64GetDatum(meta->lastRevmapPage);
     359 ECB             : 
     360 GIC          11 :     htup = heap_form_tuple(tupdesc, values, nulls);
     361                 : 
     362              11 :     PG_RETURN_DATUM(HeapTupleGetDatum(htup));
     363                 : }
     364                 : 
     365                 : /*
     366 ECB             :  * Return the TID array stored in a BRIN revmap page
     367                 :  */
     368                 : Datum
     369 GIC        1364 : brin_revmap_data(PG_FUNCTION_ARGS)
     370                 : {
     371                 :     struct
     372                 :     {
     373                 :         ItemPointerData *tids;
     374                 :         int         idx;
     375 ECB             :     }          *state;
     376 EUB             :     FuncCallContext *fctx;
     377                 : 
     378 GIC        1364 :     if (!superuser())
     379 UIC           0 :         ereport(ERROR,
     380 ECB             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     381                 :                  errmsg("must be superuser to use raw page functions")));
     382                 : 
     383 GIC        1364 :     if (SRF_IS_FIRSTCALL())
     384                 :     {
     385               4 :         bytea      *raw_page = PG_GETARG_BYTEA_P(0);
     386                 :         MemoryContext mctx;
     387 ECB             :         Page        page;
     388                 : 
     389                 :         /* create a function context for cross-call persistence */
     390 CBC           4 :         fctx = SRF_FIRSTCALL_INIT();
     391                 : 
     392                 :         /* switch to memory context appropriate for multiple function calls */
     393               4 :         mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
     394                 : 
     395 ECB             :         /* minimally verify the page we got */
     396 GIC           4 :         page = verify_brin_page(raw_page, BRIN_PAGETYPE_REVMAP, "revmap");
     397 ECB             : 
     398 CBC           2 :         if (PageIsNew(page))
     399                 :         {
     400 GIC           1 :             MemoryContextSwitchTo(mctx);
     401 CBC           1 :             PG_RETURN_NULL();
     402 ECB             :         }
     403                 : 
     404 GIC           1 :         state = palloc(sizeof(*state));
     405 CBC           1 :         state->tids = ((RevmapContents *) PageGetContents(page))->rm_tids;
     406 GIC           1 :         state->idx = 0;
     407 ECB             : 
     408 GIC           1 :         fctx->user_fctx = state;
     409                 : 
     410 CBC           1 :         MemoryContextSwitchTo(mctx);
     411 ECB             :     }
     412                 : 
     413 CBC        1361 :     fctx = SRF_PERCALL_SETUP();
     414            1361 :     state = fctx->user_fctx;
     415                 : 
     416            1361 :     if (state->idx < REVMAP_PAGE_MAXITEMS)
     417 GIC        1360 :         SRF_RETURN_NEXT(fctx, PointerGetDatum(&state->tids[state->idx++]));
     418                 : 
     419               1 :     SRF_RETURN_DONE(fctx);
     420                 : }
        

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