LCOV - code coverage report
Current view: top level - contrib/pageinspect - ginfuncs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 87.8 % 115 101
Test Date: 2026-03-05 04:15:12 Functions: 100.0 % 6 6
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * ginfuncs.c
       3              :  *      Functions to investigate the content of GIN indexes
       4              :  *
       5              :  * Copyright (c) 2014-2026, PostgreSQL Global Development Group
       6              :  *
       7              :  * IDENTIFICATION
       8              :  *      contrib/pageinspect/ginfuncs.c
       9              :  */
      10              : #include "postgres.h"
      11              : 
      12              : #include "access/gin_private.h"
      13              : #include "access/htup_details.h"
      14              : #include "catalog/pg_type.h"
      15              : #include "funcapi.h"
      16              : #include "miscadmin.h"
      17              : #include "pageinspect.h"
      18              : #include "utils/array.h"
      19              : #include "utils/builtins.h"
      20              : 
      21              : 
      22            8 : PG_FUNCTION_INFO_V1(gin_metapage_info);
      23            8 : PG_FUNCTION_INFO_V1(gin_page_opaque_info);
      24            8 : PG_FUNCTION_INFO_V1(gin_leafpage_items);
      25              : 
      26              : 
      27              : Datum
      28            5 : gin_metapage_info(PG_FUNCTION_ARGS)
      29              : {
      30            5 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
      31              :     TupleDesc   tupdesc;
      32              :     Page        page;
      33              :     GinPageOpaque opaq;
      34              :     GinMetaPageData *metadata;
      35              :     HeapTuple   resultTuple;
      36              :     Datum       values[10];
      37              :     bool        nulls[10];
      38              : 
      39            5 :     if (!superuser())
      40            0 :         ereport(ERROR,
      41              :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
      42              :                  errmsg("must be superuser to use raw page functions")));
      43              : 
      44            5 :     page = get_page_from_raw(raw_page);
      45              : 
      46            4 :     if (PageIsNew(page))
      47            1 :         PG_RETURN_NULL();
      48              : 
      49            3 :     if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData)))
      50            1 :         ereport(ERROR,
      51              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      52              :                  errmsg("input page is not a valid GIN metapage"),
      53              :                  errdetail("Expected special size %d, got %d.",
      54              :                            (int) MAXALIGN(sizeof(GinPageOpaqueData)),
      55              :                            (int) PageGetSpecialSize(page))));
      56              : 
      57            2 :     opaq = GinPageGetOpaque(page);
      58              : 
      59            2 :     if (opaq->flags != GIN_META)
      60            1 :         ereport(ERROR,
      61              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      62              :                  errmsg("input page is not a GIN metapage"),
      63              :                  errdetail("Flags %04X, expected %04X",
      64              :                            opaq->flags, GIN_META)));
      65              : 
      66              :     /* Build a tuple descriptor for our result type */
      67            1 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
      68            0 :         elog(ERROR, "return type must be a row type");
      69              : 
      70            1 :     metadata = GinPageGetMeta(page);
      71              : 
      72            1 :     memset(nulls, 0, sizeof(nulls));
      73              : 
      74            1 :     values[0] = Int64GetDatum(metadata->head);
      75            1 :     values[1] = Int64GetDatum(metadata->tail);
      76            1 :     values[2] = UInt32GetDatum(metadata->tailFreeSize);
      77            1 :     values[3] = Int64GetDatum(metadata->nPendingPages);
      78            1 :     values[4] = Int64GetDatum(metadata->nPendingHeapTuples);
      79              : 
      80              :     /* statistics, updated by VACUUM */
      81            1 :     values[5] = Int64GetDatum(metadata->nTotalPages);
      82            1 :     values[6] = Int64GetDatum(metadata->nEntryPages);
      83            1 :     values[7] = Int64GetDatum(metadata->nDataPages);
      84            1 :     values[8] = Int64GetDatum(metadata->nEntries);
      85              : 
      86            1 :     values[9] = Int32GetDatum(metadata->ginVersion);
      87              : 
      88              :     /* Build and return the result tuple. */
      89            1 :     resultTuple = heap_form_tuple(tupdesc, values, nulls);
      90              : 
      91            1 :     return HeapTupleGetDatum(resultTuple);
      92              : }
      93              : 
      94              : 
      95              : Datum
      96            4 : gin_page_opaque_info(PG_FUNCTION_ARGS)
      97              : {
      98            4 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
      99              :     TupleDesc   tupdesc;
     100              :     Page        page;
     101              :     GinPageOpaque opaq;
     102              :     HeapTuple   resultTuple;
     103              :     Datum       values[3];
     104              :     bool        nulls[3];
     105              :     Datum       flags[16];
     106            4 :     int         nflags = 0;
     107              :     uint16      flagbits;
     108              : 
     109            4 :     if (!superuser())
     110            0 :         ereport(ERROR,
     111              :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     112              :                  errmsg("must be superuser to use raw page functions")));
     113              : 
     114            4 :     page = get_page_from_raw(raw_page);
     115              : 
     116            3 :     if (PageIsNew(page))
     117            1 :         PG_RETURN_NULL();
     118              : 
     119            2 :     if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData)))
     120            1 :         ereport(ERROR,
     121              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     122              :                  errmsg("input page is not a valid GIN data leaf page"),
     123              :                  errdetail("Expected special size %d, got %d.",
     124              :                            (int) MAXALIGN(sizeof(GinPageOpaqueData)),
     125              :                            (int) PageGetSpecialSize(page))));
     126              : 
     127            1 :     opaq = GinPageGetOpaque(page);
     128              : 
     129              :     /* Build a tuple descriptor for our result type */
     130            1 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     131            0 :         elog(ERROR, "return type must be a row type");
     132              : 
     133              :     /* Convert the flags bitmask to an array of human-readable names */
     134            1 :     flagbits = opaq->flags;
     135            1 :     if (flagbits & GIN_DATA)
     136            0 :         flags[nflags++] = CStringGetTextDatum("data");
     137            1 :     if (flagbits & GIN_LEAF)
     138            1 :         flags[nflags++] = CStringGetTextDatum("leaf");
     139            1 :     if (flagbits & GIN_DELETED)
     140            0 :         flags[nflags++] = CStringGetTextDatum("deleted");
     141            1 :     if (flagbits & GIN_META)
     142            0 :         flags[nflags++] = CStringGetTextDatum("meta");
     143            1 :     if (flagbits & GIN_LIST)
     144            0 :         flags[nflags++] = CStringGetTextDatum("list");
     145            1 :     if (flagbits & GIN_LIST_FULLROW)
     146            0 :         flags[nflags++] = CStringGetTextDatum("list_fullrow");
     147            1 :     if (flagbits & GIN_INCOMPLETE_SPLIT)
     148            0 :         flags[nflags++] = CStringGetTextDatum("incomplete_split");
     149            1 :     if (flagbits & GIN_COMPRESSED)
     150            0 :         flags[nflags++] = CStringGetTextDatum("compressed");
     151            1 :     flagbits &= ~(GIN_DATA | GIN_LEAF | GIN_DELETED | GIN_META | GIN_LIST |
     152              :                   GIN_LIST_FULLROW | GIN_INCOMPLETE_SPLIT | GIN_COMPRESSED);
     153            1 :     if (flagbits)
     154              :     {
     155              :         /* any flags we don't recognize are printed in hex */
     156            0 :         flags[nflags++] = DirectFunctionCall1(to_hex32, Int32GetDatum(flagbits));
     157              :     }
     158              : 
     159            1 :     memset(nulls, 0, sizeof(nulls));
     160              : 
     161            1 :     values[0] = Int64GetDatum(opaq->rightlink);
     162            1 :     values[1] = Int32GetDatum(opaq->maxoff);
     163            1 :     values[2] = PointerGetDatum(construct_array_builtin(flags, nflags, TEXTOID));
     164              : 
     165              :     /* Build and return the result tuple. */
     166            1 :     resultTuple = heap_form_tuple(tupdesc, values, nulls);
     167              : 
     168            1 :     return HeapTupleGetDatum(resultTuple);
     169              : }
     170              : 
     171              : typedef struct gin_leafpage_items_state
     172              : {
     173              :     TupleDesc   tupd;
     174              :     GinPostingList *seg;
     175              :     GinPostingList *lastseg;
     176              : } gin_leafpage_items_state;
     177              : 
     178              : Datum
     179           26 : gin_leafpage_items(PG_FUNCTION_ARGS)
     180              : {
     181           26 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
     182              :     FuncCallContext *fctx;
     183              :     gin_leafpage_items_state *inter_call_data;
     184              : 
     185           26 :     if (!superuser())
     186            0 :         ereport(ERROR,
     187              :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     188              :                  errmsg("must be superuser to use raw page functions")));
     189              : 
     190           26 :     if (SRF_IS_FIRSTCALL())
     191              :     {
     192              :         TupleDesc   tupdesc;
     193              :         MemoryContext mctx;
     194              :         Page        page;
     195              :         GinPageOpaque opaq;
     196              : 
     197            5 :         fctx = SRF_FIRSTCALL_INIT();
     198            5 :         mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
     199              : 
     200            5 :         page = get_page_from_raw(raw_page);
     201              : 
     202            4 :         if (PageIsNew(page))
     203              :         {
     204            1 :             MemoryContextSwitchTo(mctx);
     205            1 :             PG_RETURN_NULL();
     206              :         }
     207              : 
     208            3 :         if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData)))
     209            1 :             ereport(ERROR,
     210              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     211              :                      errmsg("input page is not a valid GIN data leaf page"),
     212              :                      errdetail("Expected special size %d, got %d.",
     213              :                                (int) MAXALIGN(sizeof(GinPageOpaqueData)),
     214              :                                (int) PageGetSpecialSize(page))));
     215              : 
     216            2 :         opaq = GinPageGetOpaque(page);
     217            2 :         if (opaq->flags != (GIN_DATA | GIN_LEAF | GIN_COMPRESSED))
     218            1 :             ereport(ERROR,
     219              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     220              :                      errmsg("input page is not a compressed GIN data leaf page"),
     221              :                      errdetail("Flags %04X, expected %04X",
     222              :                                opaq->flags,
     223              :                                (GIN_DATA | GIN_LEAF | GIN_COMPRESSED))));
     224              : 
     225            1 :         inter_call_data = palloc_object(gin_leafpage_items_state);
     226              : 
     227              :         /* Build a tuple descriptor for our result type */
     228            1 :         if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     229            0 :             elog(ERROR, "return type must be a row type");
     230              : 
     231            1 :         inter_call_data->tupd = tupdesc;
     232              : 
     233            1 :         inter_call_data->seg = GinDataLeafPageGetPostingList(page);
     234            1 :         inter_call_data->lastseg = (GinPostingList *)
     235            1 :             (((char *) inter_call_data->seg) +
     236            1 :              GinDataLeafPageGetPostingListSize(page));
     237              : 
     238            1 :         fctx->user_fctx = inter_call_data;
     239              : 
     240            1 :         MemoryContextSwitchTo(mctx);
     241              :     }
     242              : 
     243           22 :     fctx = SRF_PERCALL_SETUP();
     244           22 :     inter_call_data = fctx->user_fctx;
     245              : 
     246           22 :     if (inter_call_data->seg != inter_call_data->lastseg)
     247              :     {
     248           21 :         GinPostingList *cur = inter_call_data->seg;
     249              :         HeapTuple   resultTuple;
     250              :         Datum       result;
     251              :         Datum       values[3];
     252              :         bool        nulls[3];
     253              :         int         ndecoded,
     254              :                     i;
     255              :         ItemPointer tids;
     256              :         Datum      *tids_datum;
     257              : 
     258           21 :         memset(nulls, 0, sizeof(nulls));
     259              : 
     260           21 :         values[0] = ItemPointerGetDatum(&cur->first);
     261           21 :         values[1] = UInt16GetDatum(cur->nbytes);
     262              : 
     263              :         /* build an array of decoded item pointers */
     264           21 :         tids = ginPostingListDecode(cur, &ndecoded);
     265           21 :         tids_datum = (Datum *) palloc(ndecoded * sizeof(Datum));
     266         6082 :         for (i = 0; i < ndecoded; i++)
     267         6061 :             tids_datum[i] = ItemPointerGetDatum(&tids[i]);
     268           21 :         values[2] = PointerGetDatum(construct_array_builtin(tids_datum, ndecoded, TIDOID));
     269           21 :         pfree(tids_datum);
     270           21 :         pfree(tids);
     271              : 
     272              :         /* Build and return the result tuple. */
     273           21 :         resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls);
     274           21 :         result = HeapTupleGetDatum(resultTuple);
     275              : 
     276           21 :         inter_call_data->seg = GinNextPostingListSegment(cur);
     277              : 
     278           21 :         SRF_RETURN_NEXT(fctx, result);
     279              :     }
     280              : 
     281            1 :     SRF_RETURN_DONE(fctx);
     282              : }
        

Generated by: LCOV version 2.0-1