postgresql/contrib/pageinspect/ginfuncs.c
Peter Eisentraut bd944884e9 Consolidate ItemPointer to Datum conversion functions
Instead of defining the same set of macros several times, define it
once in an appropriate header file.  In passing, convert to inline
functions.

Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi>
Discussion: https://www.postgresql.org/message-id/flat/844dd4c5-e5a1-3df1-bfaf-d1e1c2a16e45%40enterprisedb.com
2023-02-13 09:57:15 +01:00

286 lines
8.0 KiB
C

/*
* ginfuncs.c
* Functions to investigate the content of GIN indexes
*
* Copyright (c) 2014-2023, PostgreSQL Global Development Group
*
* IDENTIFICATION
* contrib/pageinspect/ginfuncs.c
*/
#include "postgres.h"
#include "access/gin.h"
#include "access/gin_private.h"
#include "access/htup_details.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "pageinspect.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rel.h"
PG_FUNCTION_INFO_V1(gin_metapage_info);
PG_FUNCTION_INFO_V1(gin_page_opaque_info);
PG_FUNCTION_INFO_V1(gin_leafpage_items);
Datum
gin_metapage_info(PG_FUNCTION_ARGS)
{
bytea *raw_page = PG_GETARG_BYTEA_P(0);
TupleDesc tupdesc;
Page page;
GinPageOpaque opaq;
GinMetaPageData *metadata;
HeapTuple resultTuple;
Datum values[10];
bool nulls[10];
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to use raw page functions")));
page = get_page_from_raw(raw_page);
if (PageIsNew(page))
PG_RETURN_NULL();
if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("input page is not a valid GIN metapage"),
errdetail("Expected special size %d, got %d.",
(int) MAXALIGN(sizeof(GinPageOpaqueData)),
(int) PageGetSpecialSize(page))));
opaq = GinPageGetOpaque(page);
if (opaq->flags != GIN_META)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("input page is not a GIN metapage"),
errdetail("Flags %04X, expected %04X",
opaq->flags, GIN_META)));
/* Build a tuple descriptor for our result type */
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
elog(ERROR, "return type must be a row type");
metadata = GinPageGetMeta(page);
memset(nulls, 0, sizeof(nulls));
values[0] = Int64GetDatum(metadata->head);
values[1] = Int64GetDatum(metadata->tail);
values[2] = Int32GetDatum(metadata->tailFreeSize);
values[3] = Int64GetDatum(metadata->nPendingPages);
values[4] = Int64GetDatum(metadata->nPendingHeapTuples);
/* statistics, updated by VACUUM */
values[5] = Int64GetDatum(metadata->nTotalPages);
values[6] = Int64GetDatum(metadata->nEntryPages);
values[7] = Int64GetDatum(metadata->nDataPages);
values[8] = Int64GetDatum(metadata->nEntries);
values[9] = Int32GetDatum(metadata->ginVersion);
/* Build and return the result tuple. */
resultTuple = heap_form_tuple(tupdesc, values, nulls);
return HeapTupleGetDatum(resultTuple);
}
Datum
gin_page_opaque_info(PG_FUNCTION_ARGS)
{
bytea *raw_page = PG_GETARG_BYTEA_P(0);
TupleDesc tupdesc;
Page page;
GinPageOpaque opaq;
HeapTuple resultTuple;
Datum values[3];
bool nulls[3];
Datum flags[16];
int nflags = 0;
uint16 flagbits;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to use raw page functions")));
page = get_page_from_raw(raw_page);
if (PageIsNew(page))
PG_RETURN_NULL();
if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("input page is not a valid GIN data leaf page"),
errdetail("Expected special size %d, got %d.",
(int) MAXALIGN(sizeof(GinPageOpaqueData)),
(int) PageGetSpecialSize(page))));
opaq = GinPageGetOpaque(page);
/* Build a tuple descriptor for our result type */
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
elog(ERROR, "return type must be a row type");
/* Convert the flags bitmask to an array of human-readable names */
flagbits = opaq->flags;
if (flagbits & GIN_DATA)
flags[nflags++] = CStringGetTextDatum("data");
if (flagbits & GIN_LEAF)
flags[nflags++] = CStringGetTextDatum("leaf");
if (flagbits & GIN_DELETED)
flags[nflags++] = CStringGetTextDatum("deleted");
if (flagbits & GIN_META)
flags[nflags++] = CStringGetTextDatum("meta");
if (flagbits & GIN_LIST)
flags[nflags++] = CStringGetTextDatum("list");
if (flagbits & GIN_LIST_FULLROW)
flags[nflags++] = CStringGetTextDatum("list_fullrow");
if (flagbits & GIN_INCOMPLETE_SPLIT)
flags[nflags++] = CStringGetTextDatum("incomplete_split");
if (flagbits & GIN_COMPRESSED)
flags[nflags++] = CStringGetTextDatum("compressed");
flagbits &= ~(GIN_DATA | GIN_LEAF | GIN_DELETED | GIN_META | GIN_LIST |
GIN_LIST_FULLROW | GIN_INCOMPLETE_SPLIT | GIN_COMPRESSED);
if (flagbits)
{
/* any flags we don't recognize are printed in hex */
flags[nflags++] = DirectFunctionCall1(to_hex32, Int32GetDatum(flagbits));
}
memset(nulls, 0, sizeof(nulls));
values[0] = Int64GetDatum(opaq->rightlink);
values[1] = Int32GetDatum(opaq->maxoff);
values[2] = PointerGetDatum(construct_array_builtin(flags, nflags, TEXTOID));
/* Build and return the result tuple. */
resultTuple = heap_form_tuple(tupdesc, values, nulls);
return HeapTupleGetDatum(resultTuple);
}
typedef struct gin_leafpage_items_state
{
TupleDesc tupd;
GinPostingList *seg;
GinPostingList *lastseg;
} gin_leafpage_items_state;
Datum
gin_leafpage_items(PG_FUNCTION_ARGS)
{
bytea *raw_page = PG_GETARG_BYTEA_P(0);
FuncCallContext *fctx;
gin_leafpage_items_state *inter_call_data;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to use raw page functions")));
if (SRF_IS_FIRSTCALL())
{
TupleDesc tupdesc;
MemoryContext mctx;
Page page;
GinPageOpaque opaq;
fctx = SRF_FIRSTCALL_INIT();
mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
page = get_page_from_raw(raw_page);
if (PageIsNew(page))
{
MemoryContextSwitchTo(mctx);
PG_RETURN_NULL();
}
if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("input page is not a valid GIN data leaf page"),
errdetail("Expected special size %d, got %d.",
(int) MAXALIGN(sizeof(GinPageOpaqueData)),
(int) PageGetSpecialSize(page))));
opaq = GinPageGetOpaque(page);
if (opaq->flags != (GIN_DATA | GIN_LEAF | GIN_COMPRESSED))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("input page is not a compressed GIN data leaf page"),
errdetail("Flags %04X, expected %04X",
opaq->flags,
(GIN_DATA | GIN_LEAF | GIN_COMPRESSED))));
inter_call_data = palloc(sizeof(gin_leafpage_items_state));
/* Build a tuple descriptor for our result type */
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
elog(ERROR, "return type must be a row type");
inter_call_data->tupd = tupdesc;
inter_call_data->seg = GinDataLeafPageGetPostingList(page);
inter_call_data->lastseg = (GinPostingList *)
(((char *) inter_call_data->seg) +
GinDataLeafPageGetPostingListSize(page));
fctx->user_fctx = inter_call_data;
MemoryContextSwitchTo(mctx);
}
fctx = SRF_PERCALL_SETUP();
inter_call_data = fctx->user_fctx;
if (inter_call_data->seg != inter_call_data->lastseg)
{
GinPostingList *cur = inter_call_data->seg;
HeapTuple resultTuple;
Datum result;
Datum values[3];
bool nulls[3];
int ndecoded,
i;
ItemPointer tids;
Datum *tids_datum;
memset(nulls, 0, sizeof(nulls));
values[0] = ItemPointerGetDatum(&cur->first);
values[1] = UInt16GetDatum(cur->nbytes);
/* build an array of decoded item pointers */
tids = ginPostingListDecode(cur, &ndecoded);
tids_datum = (Datum *) palloc(ndecoded * sizeof(Datum));
for (i = 0; i < ndecoded; i++)
tids_datum[i] = ItemPointerGetDatum(&tids[i]);
values[2] = PointerGetDatum(construct_array_builtin(tids_datum, ndecoded, TIDOID));
pfree(tids_datum);
pfree(tids);
/* Build and return the result tuple. */
resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls);
result = HeapTupleGetDatum(resultTuple);
inter_call_data->seg = GinNextPostingListSegment(cur);
SRF_RETURN_NEXT(fctx, result);
}
SRF_RETURN_DONE(fctx);
}