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

Generated by: LCOV version 1.14