PostgreSQL Source Code git master
gistfuncs.c
Go to the documentation of this file.
1/*
2 * gistfuncs.c
3 * Functions to investigate the content of GiST indexes
4 *
5 * Copyright (c) 2014-2025, PostgreSQL Global Development Group
6 *
7 * IDENTIFICATION
8 * contrib/pageinspect/gistfuncs.c
9 */
10#include "postgres.h"
11
12#include "access/gist.h"
13#include "access/htup.h"
14#include "access/relation.h"
15#include "catalog/pg_am_d.h"
16#include "funcapi.h"
17#include "miscadmin.h"
18#include "pageinspect.h"
19#include "storage/itemptr.h"
20#include "utils/array.h"
21#include "utils/builtins.h"
22#include "utils/lsyscache.h"
23#include "utils/pg_lsn.h"
24#include "utils/rel.h"
25#include "utils/ruleutils.h"
26
30
31#define IS_GIST(r) ((r)->rd_rel->relam == GIST_AM_OID)
32
33
34static Page verify_gist_page(bytea *raw_page);
35
36/*
37 * Verify that the given bytea contains a GIST page or die in the attempt.
38 * A pointer to the page is returned.
39 */
40static Page
42{
43 Page page = get_page_from_raw(raw_page);
44 GISTPageOpaque opaq;
45
46 if (PageIsNew(page))
47 return page;
48
49 /* verify the special space has the expected size */
52 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
53 errmsg("input page is not a valid %s page", "GiST"),
54 errdetail("Expected special size %d, got %d.",
55 (int) MAXALIGN(sizeof(GISTPageOpaqueData)),
56 (int) PageGetSpecialSize(page))));
57
58 opaq = GistPageGetOpaque(page);
59 if (opaq->gist_page_id != GIST_PAGE_ID)
61 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
62 errmsg("input page is not a valid %s page", "GiST"),
63 errdetail("Expected %08x, got %08x.",
65 opaq->gist_page_id)));
66
67 return page;
68}
69
72{
73 bytea *raw_page = PG_GETARG_BYTEA_P(0);
74 TupleDesc tupdesc;
75 Page page;
76 HeapTuple resultTuple;
77 Datum values[4];
78 bool nulls[4];
79 Datum flags[16];
80 int nflags = 0;
81 uint16 flagbits;
82
83 if (!superuser())
85 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
86 errmsg("must be superuser to use raw page functions")));
87
88 page = verify_gist_page(raw_page);
89
90 if (PageIsNew(page))
92
93 /* Build a tuple descriptor for our result type */
94 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
95 elog(ERROR, "return type must be a row type");
96
97 /* Convert the flags bitmask to an array of human-readable names */
98 flagbits = GistPageGetOpaque(page)->flags;
99 if (flagbits & F_LEAF)
100 flags[nflags++] = CStringGetTextDatum("leaf");
101 if (flagbits & F_DELETED)
102 flags[nflags++] = CStringGetTextDatum("deleted");
103 if (flagbits & F_TUPLES_DELETED)
104 flags[nflags++] = CStringGetTextDatum("tuples_deleted");
105 if (flagbits & F_FOLLOW_RIGHT)
106 flags[nflags++] = CStringGetTextDatum("follow_right");
107 if (flagbits & F_HAS_GARBAGE)
108 flags[nflags++] = CStringGetTextDatum("has_garbage");
110 if (flagbits)
111 {
112 /* any flags we don't recognize are printed in hex */
113 flags[nflags++] = DirectFunctionCall1(to_hex32, Int32GetDatum(flagbits));
114 }
115
116 memset(nulls, 0, sizeof(nulls));
117
118 values[0] = LSNGetDatum(PageGetLSN(page));
120 values[2] = Int64GetDatum(GistPageGetOpaque(page)->rightlink);
121 values[3] = PointerGetDatum(construct_array_builtin(flags, nflags, TEXTOID));
122
123 /* Build and return the result tuple. */
124 resultTuple = heap_form_tuple(tupdesc, values, nulls);
125
126 return HeapTupleGetDatum(resultTuple);
127}
128
129Datum
131{
132 bytea *raw_page = PG_GETARG_BYTEA_P(0);
133 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
134 Page page;
135 OffsetNumber offset;
137
138 if (!superuser())
140 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
141 errmsg("must be superuser to use raw page functions")));
142
143 InitMaterializedSRF(fcinfo, 0);
144
145 page = verify_gist_page(raw_page);
146
147 if (PageIsNew(page))
149
150 /* Avoid bogus PageGetMaxOffsetNumber() call with deleted pages */
151 if (GistPageIsDeleted(page))
152 elog(NOTICE, "page is deleted");
153 else
154 maxoff = PageGetMaxOffsetNumber(page);
155
156 for (offset = FirstOffsetNumber;
157 offset <= maxoff;
158 offset++)
159 {
160 Datum values[5];
161 bool nulls[5];
162 ItemId id;
163 IndexTuple itup;
164 bytea *tuple_bytea;
165 int tuple_len;
166
167 id = PageGetItemId(page, offset);
168
169 if (!ItemIdIsValid(id))
170 elog(ERROR, "invalid ItemId");
171
172 itup = (IndexTuple) PageGetItem(page, id);
173 tuple_len = IndexTupleSize(itup);
174
175 memset(nulls, 0, sizeof(nulls));
176
177 values[0] = DatumGetInt16(offset);
178 values[1] = ItemPointerGetDatum(&itup->t_tid);
179 values[2] = Int32GetDatum((int) IndexTupleSize(itup));
180
181 tuple_bytea = (bytea *) palloc(tuple_len + VARHDRSZ);
182 SET_VARSIZE(tuple_bytea, tuple_len + VARHDRSZ);
183 memcpy(VARDATA(tuple_bytea), itup, tuple_len);
185 values[4] = PointerGetDatum(tuple_bytea);
186
187 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
188 }
189
190 return (Datum) 0;
191}
192
193Datum
195{
196 bytea *raw_page = PG_GETARG_BYTEA_P(0);
197 Oid indexRelid = PG_GETARG_OID(1);
198 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
199 Relation indexRel;
200 TupleDesc tupdesc;
201 Page page;
202 uint16 flagbits;
203 bits16 printflags = 0;
204 OffsetNumber offset;
206 char *index_columns;
207
208 if (!superuser())
210 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
211 errmsg("must be superuser to use raw page functions")));
212
213 InitMaterializedSRF(fcinfo, 0);
214
215 /* Open the relation */
216 indexRel = index_open(indexRelid, AccessShareLock);
217
218 if (!IS_GIST(indexRel))
220 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
221 errmsg("\"%s\" is not a %s index",
222 RelationGetRelationName(indexRel), "GiST")));
223
224 page = verify_gist_page(raw_page);
225
226 if (PageIsNew(page))
227 {
228 index_close(indexRel, AccessShareLock);
230 }
231
232 flagbits = GistPageGetOpaque(page)->flags;
233
234 /*
235 * Included attributes are added when dealing with leaf pages, discarded
236 * for non-leaf pages as these include only data for key attributes.
237 */
238 printflags |= RULE_INDEXDEF_PRETTY;
239 if (flagbits & F_LEAF)
240 {
241 tupdesc = RelationGetDescr(indexRel);
242 }
243 else
244 {
247 printflags |= RULE_INDEXDEF_KEYS_ONLY;
248 }
249
250 index_columns = pg_get_indexdef_columns_extended(indexRelid,
251 printflags);
252
253 /* Avoid bogus PageGetMaxOffsetNumber() call with deleted pages */
254 if (GistPageIsDeleted(page))
255 elog(NOTICE, "page is deleted");
256 else
257 maxoff = PageGetMaxOffsetNumber(page);
258
259 for (offset = FirstOffsetNumber;
260 offset <= maxoff;
261 offset++)
262 {
263 Datum values[5];
264 bool nulls[5];
265 ItemId id;
266 IndexTuple itup;
267 Datum itup_values[INDEX_MAX_KEYS];
268 bool itup_isnull[INDEX_MAX_KEYS];
270 int i;
271
272 id = PageGetItemId(page, offset);
273
274 if (!ItemIdIsValid(id))
275 elog(ERROR, "invalid ItemId");
276
277 itup = (IndexTuple) PageGetItem(page, id);
278
279 index_deform_tuple(itup, tupdesc,
280 itup_values, itup_isnull);
281
282 memset(nulls, 0, sizeof(nulls));
283
284 values[0] = DatumGetInt16(offset);
285 values[1] = ItemPointerGetDatum(&itup->t_tid);
286 values[2] = Int32GetDatum((int) IndexTupleSize(itup));
288
289 if (index_columns)
290 {
292 appendStringInfo(&buf, "(%s)=(", index_columns);
293
294 /* Most of this is copied from record_out(). */
295 for (i = 0; i < tupdesc->natts; i++)
296 {
297 char *value;
298 char *tmp;
299 bool nq = false;
300
301 if (itup_isnull[i])
302 value = "null";
303 else
304 {
305 Oid foutoid;
306 bool typisvarlena;
307 Oid typoid;
308
309 typoid = TupleDescAttr(tupdesc, i)->atttypid;
310 getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
311 value = OidOutputFunctionCall(foutoid, itup_values[i]);
312 }
313
315 appendStringInfoString(&buf, ") INCLUDE (");
316 else if (i > 0)
318
319 /* Check whether we need double quotes for this value */
320 nq = (value[0] == '\0'); /* force quotes for empty string */
321 for (tmp = value; *tmp; tmp++)
322 {
323 char ch = *tmp;
324
325 if (ch == '"' || ch == '\\' ||
326 ch == '(' || ch == ')' || ch == ',' ||
327 isspace((unsigned char) ch))
328 {
329 nq = true;
330 break;
331 }
332 }
333
334 /* And emit the string */
335 if (nq)
337 for (tmp = value; *tmp; tmp++)
338 {
339 char ch = *tmp;
340
341 if (ch == '"' || ch == '\\')
344 }
345 if (nq)
347 }
348
350
351 values[4] = CStringGetTextDatum(buf.data);
352 nulls[4] = false;
353 }
354 else
355 {
356 values[4] = (Datum) 0;
357 nulls[4] = true;
358 }
359
360 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
361 }
362
364
365 return (Datum) 0;
366}
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
Definition: arrayfuncs.c:3381
static Datum values[MAXATTR]
Definition: bootstrap.c:151
static uint16 PageGetSpecialSize(const PageData *page)
Definition: bufpage.h:317
static Item PageGetItem(const PageData *page, const ItemIdData *itemId)
Definition: bufpage.h:354
static bool PageIsNew(const PageData *page)
Definition: bufpage.h:234
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:244
PageData * Page
Definition: bufpage.h:82
static XLogRecPtr PageGetLSN(const PageData *page)
Definition: bufpage.h:386
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition: bufpage.h:372
#define CStringGetTextDatum(s)
Definition: builtins.h:97
uint16 bits16
Definition: c.h:496
#define MAXALIGN(LEN)
Definition: c.h:768
#define VARHDRSZ
Definition: c.h:649
uint16_t uint16
Definition: c.h:487
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define NOTICE
Definition: elog.h:35
#define ereport(elevel,...)
Definition: elog.h:149
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:1763
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1807
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:641
#define PG_RETURN_NULL()
Definition: fmgr.h:345
#define PG_GETARG_BYTEA_P(n)
Definition: fmgr.h:335
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
Definition: funcapi.c:76
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:276
@ TYPEFUNC_COMPOSITE
Definition: funcapi.h:149
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
Definition: funcapi.h:230
#define F_TUPLES_DELETED
Definition: gist.h:51
#define GIST_PAGE_ID
Definition: gist.h:112
#define F_LEAF
Definition: gist.h:49
#define F_FOLLOW_RIGHT
Definition: gist.h:52
#define GistPageIsDeleted(page)
Definition: gist.h:173
#define GistPageGetOpaque(page)
Definition: gist.h:168
#define F_HAS_GARBAGE
Definition: gist.h:53
#define F_DELETED
Definition: gist.h:50
#define GistPageGetNSN(page)
Definition: gist.h:187
PG_FUNCTION_INFO_V1(gist_page_opaque_info)
Datum gist_page_items_bytea(PG_FUNCTION_ARGS)
Definition: gistfuncs.c:130
static Page verify_gist_page(bytea *raw_page)
Definition: gistfuncs.c:41
#define IS_GIST(r)
Definition: gistfuncs.c:31
Datum gist_page_items(PG_FUNCTION_ARGS)
Definition: gistfuncs.c:194
Datum gist_page_opaque_info(PG_FUNCTION_ARGS)
Definition: gistfuncs.c:71
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1117
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
void index_deform_tuple(IndexTuple tup, TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: indextuple.c:456
static struct @162 value
int i
Definition: isn.c:72
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
#define ItemIdIsValid(itemId)
Definition: itemid.h:86
static Datum ItemPointerGetDatum(const ItemPointerData *X)
Definition: itemptr.h:237
IndexTupleData * IndexTuple
Definition: itup.h:53
static Size IndexTupleSize(const IndexTupleData *itup)
Definition: itup.h:71
#define AccessShareLock
Definition: lockdefs.h:36
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2907
void * palloc(Size size)
Definition: mcxt.c:1317
#define InvalidOffsetNumber
Definition: off.h:26
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
Page get_page_from_raw(bytea *raw_page)
Definition: rawpage.c:215
#define INDEX_MAX_KEYS
static Datum LSNGetDatum(XLogRecPtr X)
Definition: pg_lsn.h:28
static char * buf
Definition: pg_test_fsync.c:72
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
uintptr_t Datum
Definition: postgres.h:69
static Datum BoolGetDatum(bool X)
Definition: postgres.h:107
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:217
static int16 DatumGetInt16(Datum X)
Definition: postgres.h:167
unsigned int Oid
Definition: postgres_ext.h:32
#define RelationGetDescr(relation)
Definition: rel.h:531
#define RelationGetRelationName(relation)
Definition: rel.h:539
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:524
char * pg_get_indexdef_columns_extended(Oid indexrelid, bits16 flags)
Definition: ruleutils.c:1246
#define RULE_INDEXDEF_PRETTY
Definition: ruleutils.h:24
#define RULE_INDEXDEF_KEYS_ONLY
Definition: ruleutils.h:25
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:145
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:230
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:242
void initStringInfo(StringInfo str)
Definition: stringinfo.c:97
#define appendStringInfoCharMacro(str, ch)
Definition: stringinfo.h:231
uint16 gist_page_id
Definition: gist.h:83
ItemPointerData t_tid
Definition: itup.h:37
TupleDesc setDesc
Definition: execnodes.h:358
Tuplestorestate * setResult
Definition: execnodes.h:357
Definition: c.h:644
bool superuser(void)
Definition: superuser.c:46
TupleDesc CreateTupleDescTruncatedCopy(TupleDesc tupdesc, int natts)
Definition: tupdesc.c:278
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:153
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition: tuplestore.c:784
#define VARDATA(PTR)
Definition: varatt.h:278
#define SET_VARSIZE(PTR, len)
Definition: varatt.h:305
Datum to_hex32(PG_FUNCTION_ARGS)
Definition: varlena.c:5007