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

Generated by: LCOV version 1.14