1
0
mirror of https://github.com/postgres/postgres.git synced 2025-12-07 12:02:30 +03:00
Files
postgres/contrib/pageinspect/ginfuncs.c
Michael Paquier 31d3847a37 Use more palloc_object() and palloc_array() in contrib/
The idea is to encourage more the use of these new routines across the
tree, as these offer stronger type safety guarantees than palloc().  In
an ideal world, palloc() would then act as an internal routine of these
flavors, whose footprint in the tree is minimal.

The patch sent by the author is very large, and this chunk of changes
represents something like 10% of the overall patch submitted.

The code compiled is the same before and after this commit, using
objdump to do some validation with a difference taken in-between.  There
are some diffs, which are caused by changes in line numbers because some
of the new allocation formulas are shorter, for the following files:
trgm_regexp.c, xpath.c and pg_walinspect.c.

Author: David Geier <geidav.pg@gmail.com>
Discussion: https://postgr.es/m/ad0748d4-3080-436e-b0bc-ac8f86a3466a@gmail.com
2025-12-05 16:40:26 +09:00

283 lines
7.9 KiB
C

/*
* ginfuncs.c
* Functions to investigate the content of GIN indexes
*
* Copyright (c) 2014-2025, PostgreSQL Global Development Group
*
* IDENTIFICATION
* contrib/pageinspect/ginfuncs.c
*/
#include "postgres.h"
#include "access/gin_private.h"
#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "pageinspect.h"
#include "utils/array.h"
#include "utils/builtins.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_object(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);
}