3 * Functions to investigate the content of GiST indexes
5 * Copyright (c) 2014-2022, PostgreSQL Global Development Group
8 * contrib/pageinspect/gistfuncs.c
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"
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)
35 gist_page_opaque_info(PG_FUNCTION_ARGS
)
37 bytea
*raw_page
= PG_GETARG_BYTEA_P(0);
41 HeapTuple resultTuple
;
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
);
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
,
87 -1, false, TYPALIGN_INT
));
89 /* Build and return the result tuple. */
90 resultTuple
= heap_form_tuple(tupdesc
, values
, nulls
);
92 return HeapTupleGetDatum(resultTuple
);
96 gist_page_items_bytea(PG_FUNCTION_ARGS
)
98 bytea
*raw_page
= PG_GETARG_BYTEA_P(0);
99 ReturnSetInfo
*rsinfo
= (ReturnSetInfo
*) fcinfo
->resultinfo
;
102 Tuplestorestate
*tupstore
;
103 MemoryContext oldcontext
;
106 OffsetNumber maxoff
= InvalidOffsetNumber
;
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
))
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
))
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");
143 maxoff
= PageGetMaxOffsetNumber(page
);
145 for (offset
= FirstOffsetNumber
;
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
);
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
;
191 Tuplestorestate
*tupstore
;
192 MemoryContext oldcontext
;
195 OffsetNumber maxoff
= InvalidOffsetNumber
;
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
))
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
))
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");
235 maxoff
= PageGetMaxOffsetNumber(page
);
237 for (offset
= FirstOffsetNumber
;
245 Datum itup_values
[INDEX_MAX_KEYS
];
246 bool itup_isnull
[INDEX_MAX_KEYS
];
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
);
268 values
[4] = CStringGetTextDatum(key_desc
);
271 values
[4] = (Datum
) 0;
275 tuplestore_putvalues(tupstore
, tupdesc
, values
, nulls
);
278 relation_close(indexRel
, AccessShareLock
);