Update copyright for 2022
[pgsql.git] / contrib / pageinspect / gistfuncs.c
blob96e3cab1cc346d8046fd413b4e48eb5f8c068f12
1 /*
2 * gistfuncs.c
3 * Functions to investigate the content of GiST indexes
5 * Copyright (c) 2014-2022, PostgreSQL Global Development Group
7 * IDENTIFICATION
8 * contrib/pageinspect/gistfuncs.c
9 */
10 #include "postgres.h"
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 "funcapi.h"
18 #include "miscadmin.h"
19 #include "pageinspect.h"
20 #include "storage/itemptr.h"
21 #include "utils/array.h"
22 #include "utils/builtins.h"
23 #include "utils/rel.h"
24 #include "utils/pg_lsn.h"
25 #include "utils/varlena.h"
27 PG_FUNCTION_INFO_V1(gist_page_opaque_info);
28 PG_FUNCTION_INFO_V1(gist_page_items);
29 PG_FUNCTION_INFO_V1(gist_page_items_bytea);
31 #define ItemPointerGetDatum(X) PointerGetDatum(X)
34 Datum
35 gist_page_opaque_info(PG_FUNCTION_ARGS)
37 bytea *raw_page = PG_GETARG_BYTEA_P(0);
38 TupleDesc tupdesc;
39 Page page;
40 GISTPageOpaque opaq;
41 HeapTuple resultTuple;
42 Datum values[4];
43 bool nulls[4];
44 Datum flags[16];
45 int nflags = 0;
46 uint16 flagbits;
48 if (!superuser())
49 ereport(ERROR,
50 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
51 errmsg("must be superuser to use raw page functions")));
53 page = get_page_from_raw(raw_page);
55 opaq = (GISTPageOpaque) PageGetSpecialPointer(page);
57 /* Build a tuple descriptor for our result type */
58 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
59 elog(ERROR, "return type must be a row type");
61 /* Convert the flags bitmask to an array of human-readable names */
62 flagbits = opaq->flags;
63 if (flagbits & F_LEAF)
64 flags[nflags++] = CStringGetTextDatum("leaf");
65 if (flagbits & F_DELETED)
66 flags[nflags++] = CStringGetTextDatum("deleted");
67 if (flagbits & F_TUPLES_DELETED)
68 flags[nflags++] = CStringGetTextDatum("tuples_deleted");
69 if (flagbits & F_FOLLOW_RIGHT)
70 flags[nflags++] = CStringGetTextDatum("follow_right");
71 if (flagbits & F_HAS_GARBAGE)
72 flags[nflags++] = CStringGetTextDatum("has_garbage");
73 flagbits &= ~(F_LEAF | F_DELETED | F_TUPLES_DELETED | F_FOLLOW_RIGHT | F_HAS_GARBAGE);
74 if (flagbits)
76 /* any flags we don't recognize are printed in hex */
77 flags[nflags++] = DirectFunctionCall1(to_hex32, Int32GetDatum(flagbits));
80 memset(nulls, 0, sizeof(nulls));
82 values[0] = LSNGetDatum(PageGetLSN(page));
83 values[1] = LSNGetDatum(GistPageGetNSN(page));
84 values[2] = Int64GetDatum(opaq->rightlink);
85 values[3] = PointerGetDatum(construct_array(flags, nflags,
86 TEXTOID,
87 -1, false, TYPALIGN_INT));
89 /* Build and return the result tuple. */
90 resultTuple = heap_form_tuple(tupdesc, values, nulls);
92 return HeapTupleGetDatum(resultTuple);
95 Datum
96 gist_page_items_bytea(PG_FUNCTION_ARGS)
98 bytea *raw_page = PG_GETARG_BYTEA_P(0);
99 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
100 bool randomAccess;
101 TupleDesc tupdesc;
102 Tuplestorestate *tupstore;
103 MemoryContext oldcontext;
104 Page page;
105 OffsetNumber offset;
106 OffsetNumber maxoff = InvalidOffsetNumber;
108 if (!superuser())
109 ereport(ERROR,
110 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
111 errmsg("must be superuser to use raw page functions")));
113 /* check to see if caller supports us returning a tuplestore */
114 if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
115 ereport(ERROR,
116 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
117 errmsg("set-valued function called in context that cannot accept a set")));
118 if (!(rsinfo->allowedModes & SFRM_Materialize))
119 ereport(ERROR,
120 (errcode(ERRCODE_SYNTAX_ERROR),
121 errmsg("materialize mode required, but it is not allowed in this context")));
123 /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
124 oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
126 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
127 elog(ERROR, "return type must be a row type");
129 randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
130 tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
131 rsinfo->returnMode = SFRM_Materialize;
132 rsinfo->setResult = tupstore;
133 rsinfo->setDesc = tupdesc;
135 MemoryContextSwitchTo(oldcontext);
137 page = get_page_from_raw(raw_page);
139 /* Avoid bogus PageGetMaxOffsetNumber() call with deleted pages */
140 if (GistPageIsDeleted(page))
141 elog(NOTICE, "page is deleted");
142 else
143 maxoff = PageGetMaxOffsetNumber(page);
145 for (offset = FirstOffsetNumber;
146 offset <= maxoff;
147 offset++)
149 Datum values[5];
150 bool nulls[5];
151 ItemId id;
152 IndexTuple itup;
153 bytea *tuple_bytea;
154 int tuple_len;
156 id = PageGetItemId(page, offset);
158 if (!ItemIdIsValid(id))
159 elog(ERROR, "invalid ItemId");
161 itup = (IndexTuple) PageGetItem(page, id);
162 tuple_len = IndexTupleSize(itup);
164 memset(nulls, 0, sizeof(nulls));
166 values[0] = DatumGetInt16(offset);
167 values[1] = ItemPointerGetDatum(&itup->t_tid);
168 values[2] = Int32GetDatum((int) IndexTupleSize(itup));
170 tuple_bytea = (bytea *) palloc(tuple_len + VARHDRSZ);
171 SET_VARSIZE(tuple_bytea, tuple_len + VARHDRSZ);
172 memcpy(VARDATA(tuple_bytea), itup, tuple_len);
173 values[3] = BoolGetDatum(ItemIdIsDead(id));
174 values[4] = PointerGetDatum(tuple_bytea);
176 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
179 return (Datum) 0;
182 Datum
183 gist_page_items(PG_FUNCTION_ARGS)
185 bytea *raw_page = PG_GETARG_BYTEA_P(0);
186 Oid indexRelid = PG_GETARG_OID(1);
187 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
188 bool randomAccess;
189 Relation indexRel;
190 TupleDesc tupdesc;
191 Tuplestorestate *tupstore;
192 MemoryContext oldcontext;
193 Page page;
194 OffsetNumber offset;
195 OffsetNumber maxoff = InvalidOffsetNumber;
197 if (!superuser())
198 ereport(ERROR,
199 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
200 errmsg("must be superuser to use raw page functions")));
202 /* check to see if caller supports us returning a tuplestore */
203 if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
204 ereport(ERROR,
205 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
206 errmsg("set-valued function called in context that cannot accept a set")));
207 if (!(rsinfo->allowedModes & SFRM_Materialize))
208 ereport(ERROR,
209 (errcode(ERRCODE_SYNTAX_ERROR),
210 errmsg("materialize mode required, but it is not allowed in this context")));
212 /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
213 oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
215 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
216 elog(ERROR, "return type must be a row type");
218 randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
219 tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
220 rsinfo->returnMode = SFRM_Materialize;
221 rsinfo->setResult = tupstore;
222 rsinfo->setDesc = tupdesc;
224 MemoryContextSwitchTo(oldcontext);
226 /* Open the relation */
227 indexRel = index_open(indexRelid, AccessShareLock);
229 page = get_page_from_raw(raw_page);
231 /* Avoid bogus PageGetMaxOffsetNumber() call with deleted pages */
232 if (GistPageIsDeleted(page))
233 elog(NOTICE, "page is deleted");
234 else
235 maxoff = PageGetMaxOffsetNumber(page);
237 for (offset = FirstOffsetNumber;
238 offset <= maxoff;
239 offset++)
241 Datum values[5];
242 bool nulls[5];
243 ItemId id;
244 IndexTuple itup;
245 Datum itup_values[INDEX_MAX_KEYS];
246 bool itup_isnull[INDEX_MAX_KEYS];
247 char *key_desc;
249 id = PageGetItemId(page, offset);
251 if (!ItemIdIsValid(id))
252 elog(ERROR, "invalid ItemId");
254 itup = (IndexTuple) PageGetItem(page, id);
256 index_deform_tuple(itup, RelationGetDescr(indexRel),
257 itup_values, itup_isnull);
259 memset(nulls, 0, sizeof(nulls));
261 values[0] = DatumGetInt16(offset);
262 values[1] = ItemPointerGetDatum(&itup->t_tid);
263 values[2] = Int32GetDatum((int) IndexTupleSize(itup));
264 values[3] = BoolGetDatum(ItemIdIsDead(id));
266 key_desc = BuildIndexValueDescription(indexRel, itup_values, itup_isnull);
267 if (key_desc)
268 values[4] = CStringGetTextDatum(key_desc);
269 else
271 values[4] = (Datum) 0;
272 nulls[4] = true;
275 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
278 relation_close(indexRel, AccessShareLock);
280 return (Datum) 0;