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