LCOV - code coverage report
Current view: top level - contrib/pageinspect - ginfuncs.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 150 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 6 0
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            0 : PG_FUNCTION_INFO_V1(gin_metapage_info);
      23            0 : PG_FUNCTION_INFO_V1(gin_page_opaque_info);
      24            0 : PG_FUNCTION_INFO_V1(gin_leafpage_items);
      25              : 
      26              : 
      27              : Datum
      28            0 : gin_metapage_info(PG_FUNCTION_ARGS)
      29              : {
      30            0 :         bytea      *raw_page = PG_GETARG_BYTEA_P(0);
      31            0 :         TupleDesc       tupdesc;
      32            0 :         Page            page;
      33            0 :         GinPageOpaque opaq;
      34            0 :         GinMetaPageData *metadata;
      35            0 :         HeapTuple       resultTuple;
      36            0 :         Datum           values[10];
      37            0 :         bool            nulls[10];
      38              : 
      39            0 :         if (!superuser())
      40            0 :                 ereport(ERROR,
      41              :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
      42              :                                  errmsg("must be superuser to use raw page functions")));
      43              : 
      44            0 :         page = get_page_from_raw(raw_page);
      45              : 
      46            0 :         if (PageIsNew(page))
      47            0 :                 PG_RETURN_NULL();
      48              : 
      49            0 :         if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData)))
      50            0 :                 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            0 :         opaq = GinPageGetOpaque(page);
      58              : 
      59            0 :         if (opaq->flags != GIN_META)
      60            0 :                 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            0 :         if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
      68            0 :                 elog(ERROR, "return type must be a row type");
      69              : 
      70            0 :         metadata = GinPageGetMeta(page);
      71              : 
      72            0 :         memset(nulls, 0, sizeof(nulls));
      73              : 
      74            0 :         values[0] = Int64GetDatum(metadata->head);
      75            0 :         values[1] = Int64GetDatum(metadata->tail);
      76            0 :         values[2] = UInt32GetDatum(metadata->tailFreeSize);
      77            0 :         values[3] = Int64GetDatum(metadata->nPendingPages);
      78            0 :         values[4] = Int64GetDatum(metadata->nPendingHeapTuples);
      79              : 
      80              :         /* statistics, updated by VACUUM */
      81            0 :         values[5] = Int64GetDatum(metadata->nTotalPages);
      82            0 :         values[6] = Int64GetDatum(metadata->nEntryPages);
      83            0 :         values[7] = Int64GetDatum(metadata->nDataPages);
      84            0 :         values[8] = Int64GetDatum(metadata->nEntries);
      85              : 
      86            0 :         values[9] = Int32GetDatum(metadata->ginVersion);
      87              : 
      88              :         /* Build and return the result tuple. */
      89            0 :         resultTuple = heap_form_tuple(tupdesc, values, nulls);
      90              : 
      91            0 :         return HeapTupleGetDatum(resultTuple);
      92            0 : }
      93              : 
      94              : 
      95              : Datum
      96            0 : gin_page_opaque_info(PG_FUNCTION_ARGS)
      97              : {
      98            0 :         bytea      *raw_page = PG_GETARG_BYTEA_P(0);
      99            0 :         TupleDesc       tupdesc;
     100            0 :         Page            page;
     101            0 :         GinPageOpaque opaq;
     102            0 :         HeapTuple       resultTuple;
     103            0 :         Datum           values[3];
     104            0 :         bool            nulls[3];
     105            0 :         Datum           flags[16];
     106            0 :         int                     nflags = 0;
     107            0 :         uint16          flagbits;
     108              : 
     109            0 :         if (!superuser())
     110            0 :                 ereport(ERROR,
     111              :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     112              :                                  errmsg("must be superuser to use raw page functions")));
     113              : 
     114            0 :         page = get_page_from_raw(raw_page);
     115              : 
     116            0 :         if (PageIsNew(page))
     117            0 :                 PG_RETURN_NULL();
     118              : 
     119            0 :         if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData)))
     120            0 :                 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            0 :         opaq = GinPageGetOpaque(page);
     128              : 
     129              :         /* Build a tuple descriptor for our result type */
     130            0 :         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            0 :         flagbits = opaq->flags;
     135            0 :         if (flagbits & GIN_DATA)
     136            0 :                 flags[nflags++] = CStringGetTextDatum("data");
     137            0 :         if (flagbits & GIN_LEAF)
     138            0 :                 flags[nflags++] = CStringGetTextDatum("leaf");
     139            0 :         if (flagbits & GIN_DELETED)
     140            0 :                 flags[nflags++] = CStringGetTextDatum("deleted");
     141            0 :         if (flagbits & GIN_META)
     142            0 :                 flags[nflags++] = CStringGetTextDatum("meta");
     143            0 :         if (flagbits & GIN_LIST)
     144            0 :                 flags[nflags++] = CStringGetTextDatum("list");
     145            0 :         if (flagbits & GIN_LIST_FULLROW)
     146            0 :                 flags[nflags++] = CStringGetTextDatum("list_fullrow");
     147            0 :         if (flagbits & GIN_INCOMPLETE_SPLIT)
     148            0 :                 flags[nflags++] = CStringGetTextDatum("incomplete_split");
     149            0 :         if (flagbits & GIN_COMPRESSED)
     150            0 :                 flags[nflags++] = CStringGetTextDatum("compressed");
     151            0 :         flagbits &= ~(GIN_DATA | GIN_LEAF | GIN_DELETED | GIN_META | GIN_LIST |
     152              :                                   GIN_LIST_FULLROW | GIN_INCOMPLETE_SPLIT | GIN_COMPRESSED);
     153            0 :         if (flagbits)
     154              :         {
     155              :                 /* any flags we don't recognize are printed in hex */
     156            0 :                 flags[nflags++] = DirectFunctionCall1(to_hex32, Int32GetDatum(flagbits));
     157            0 :         }
     158              : 
     159            0 :         memset(nulls, 0, sizeof(nulls));
     160              : 
     161            0 :         values[0] = Int64GetDatum(opaq->rightlink);
     162            0 :         values[1] = Int32GetDatum(opaq->maxoff);
     163            0 :         values[2] = PointerGetDatum(construct_array_builtin(flags, nflags, TEXTOID));
     164              : 
     165              :         /* Build and return the result tuple. */
     166            0 :         resultTuple = heap_form_tuple(tupdesc, values, nulls);
     167              : 
     168            0 :         return HeapTupleGetDatum(resultTuple);
     169            0 : }
     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            0 : gin_leafpage_items(PG_FUNCTION_ARGS)
     180              : {
     181            0 :         bytea      *raw_page = PG_GETARG_BYTEA_P(0);
     182            0 :         FuncCallContext *fctx;
     183            0 :         gin_leafpage_items_state *inter_call_data;
     184              : 
     185            0 :         if (!superuser())
     186            0 :                 ereport(ERROR,
     187              :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     188              :                                  errmsg("must be superuser to use raw page functions")));
     189              : 
     190            0 :         if (SRF_IS_FIRSTCALL())
     191              :         {
     192            0 :                 TupleDesc       tupdesc;
     193            0 :                 MemoryContext mctx;
     194            0 :                 Page            page;
     195            0 :                 GinPageOpaque opaq;
     196              : 
     197            0 :                 fctx = SRF_FIRSTCALL_INIT();
     198            0 :                 mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
     199              : 
     200            0 :                 page = get_page_from_raw(raw_page);
     201              : 
     202            0 :                 if (PageIsNew(page))
     203              :                 {
     204            0 :                         MemoryContextSwitchTo(mctx);
     205            0 :                         PG_RETURN_NULL();
     206            0 :                 }
     207              : 
     208            0 :                 if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData)))
     209            0 :                         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            0 :                 opaq = GinPageGetOpaque(page);
     217            0 :                 if (opaq->flags != (GIN_DATA | GIN_LEAF | GIN_COMPRESSED))
     218            0 :                         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            0 :                 inter_call_data = palloc_object(gin_leafpage_items_state);
     226              : 
     227              :                 /* Build a tuple descriptor for our result type */
     228            0 :                 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     229            0 :                         elog(ERROR, "return type must be a row type");
     230              : 
     231            0 :                 inter_call_data->tupd = tupdesc;
     232              : 
     233            0 :                 inter_call_data->seg = GinDataLeafPageGetPostingList(page);
     234            0 :                 inter_call_data->lastseg = (GinPostingList *)
     235            0 :                         (((char *) inter_call_data->seg) +
     236            0 :                          GinDataLeafPageGetPostingListSize(page));
     237              : 
     238            0 :                 fctx->user_fctx = inter_call_data;
     239              : 
     240            0 :                 MemoryContextSwitchTo(mctx);
     241            0 :         }
     242              : 
     243            0 :         fctx = SRF_PERCALL_SETUP();
     244            0 :         inter_call_data = fctx->user_fctx;
     245              : 
     246            0 :         if (inter_call_data->seg != inter_call_data->lastseg)
     247              :         {
     248            0 :                 GinPostingList *cur = inter_call_data->seg;
     249            0 :                 HeapTuple       resultTuple;
     250            0 :                 Datum           result;
     251            0 :                 Datum           values[3];
     252            0 :                 bool            nulls[3];
     253            0 :                 int                     ndecoded,
     254              :                                         i;
     255            0 :                 ItemPointer tids;
     256            0 :                 Datum      *tids_datum;
     257              : 
     258            0 :                 memset(nulls, 0, sizeof(nulls));
     259              : 
     260            0 :                 values[0] = ItemPointerGetDatum(&cur->first);
     261            0 :                 values[1] = UInt16GetDatum(cur->nbytes);
     262              : 
     263              :                 /* build an array of decoded item pointers */
     264            0 :                 tids = ginPostingListDecode(cur, &ndecoded);
     265            0 :                 tids_datum = (Datum *) palloc(ndecoded * sizeof(Datum));
     266            0 :                 for (i = 0; i < ndecoded; i++)
     267            0 :                         tids_datum[i] = ItemPointerGetDatum(&tids[i]);
     268            0 :                 values[2] = PointerGetDatum(construct_array_builtin(tids_datum, ndecoded, TIDOID));
     269            0 :                 pfree(tids_datum);
     270            0 :                 pfree(tids);
     271              : 
     272              :                 /* Build and return the result tuple. */
     273            0 :                 resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls);
     274            0 :                 result = HeapTupleGetDatum(resultTuple);
     275              : 
     276            0 :                 inter_call_data->seg = GinNextPostingListSegment(cur);
     277              : 
     278            0 :                 SRF_RETURN_NEXT(fctx, result);
     279            0 :         }
     280              : 
     281            0 :         SRF_RETURN_DONE(fctx);
     282            0 : }
        

Generated by: LCOV version 2.3.2-1