PostgreSQL Source Code git master
brinfuncs.c
Go to the documentation of this file.
1/*
2 * brinfuncs.c
3 * Functions to investigate BRIN indexes
4 *
5 * Copyright (c) 2014-2025, PostgreSQL Global Development Group
6 *
7 * IDENTIFICATION
8 * contrib/pageinspect/brinfuncs.c
9 */
10#include "postgres.h"
11
13#include "access/brin_page.h"
14#include "access/brin_tuple.h"
15#include "access/htup_details.h"
16#include "catalog/pg_am_d.h"
17#include "catalog/pg_type.h"
18#include "funcapi.h"
19#include "lib/stringinfo.h"
20#include "miscadmin.h"
21#include "pageinspect.h"
22#include "utils/builtins.h"
23#include "utils/lsyscache.h"
24#include "utils/rel.h"
25
30
31#define IS_BRIN(r) ((r)->rd_rel->relam == BRIN_AM_OID)
32
33typedef struct brin_column_state
34{
38
39
40static Page verify_brin_page(bytea *raw_page, uint16 type,
41 const char *strtype);
42
45{
46 bytea *raw_page = PG_GETARG_BYTEA_P(0);
47 Page page;
48 char *type;
49
50 if (!superuser())
52 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
53 errmsg("must be superuser to use raw page functions")));
54
55 page = get_page_from_raw(raw_page);
56
57 if (PageIsNew(page))
59
60 /* verify the special space has the expected size */
61 if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BrinSpecialSpace)))
63 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
64 errmsg("input page is not a valid %s page", "BRIN"),
65 errdetail("Expected special size %d, got %d.",
66 (int) MAXALIGN(sizeof(BrinSpecialSpace)),
67 (int) PageGetSpecialSize(page))));
68
69 switch (BrinPageType(page))
70 {
72 type = "meta";
73 break;
75 type = "revmap";
76 break;
78 type = "regular";
79 break;
80 default:
81 type = psprintf("unknown (%02x)", BrinPageType(page));
82 break;
83 }
84
86}
87
88/*
89 * Verify that the given bytea contains a BRIN page of the indicated page
90 * type, or die in the attempt. A pointer to the page is returned.
91 */
92static Page
93verify_brin_page(bytea *raw_page, uint16 type, const char *strtype)
94{
95 Page page = get_page_from_raw(raw_page);
96
97 if (PageIsNew(page))
98 return page;
99
100 /* verify the special space has the expected size */
101 if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BrinSpecialSpace)))
103 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
104 errmsg("input page is not a valid %s page", "BRIN"),
105 errdetail("Expected special size %d, got %d.",
106 (int) MAXALIGN(sizeof(BrinSpecialSpace)),
107 (int) PageGetSpecialSize(page))));
108
109 /* verify the special space says this page is what we want */
110 if (BrinPageType(page) != type)
112 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
113 errmsg("page is not a BRIN page of type \"%s\"", strtype),
114 errdetail("Expected special type %08x, got %08x.",
115 type, BrinPageType(page))));
116
117 return page;
118}
119
120/* Number of output arguments (columns) for brin_page_items() */
121#define BRIN_PAGE_ITEMS_V1_12 8
122
123/*
124 * Extract all item values from a BRIN index page
125 *
126 * Usage: SELECT * FROM brin_page_items(get_raw_page('idx', 1), 'idx'::regclass);
127 */
128Datum
130{
131 bytea *raw_page = PG_GETARG_BYTEA_P(0);
132 Oid indexRelid = PG_GETARG_OID(1);
133 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
134 Relation indexRel;
135 brin_column_state **columns;
136 BrinDesc *bdesc;
137 BrinMemTuple *dtup;
138 Page page;
139 OffsetNumber offset;
140 AttrNumber attno;
141 bool unusedItem;
142
143 if (!superuser())
145 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
146 errmsg("must be superuser to use raw page functions")));
147
148 InitMaterializedSRF(fcinfo, 0);
149
150 /*
151 * Version 1.12 added a new output column for the empty range flag. But as
152 * it was added in the middle, it may cause crashes with function
153 * definitions from older versions of the extension.
154 *
155 * There is no way to reliably avoid the problems created by the old
156 * function definition at this point, so insist that the user update the
157 * extension.
158 */
159 if (rsinfo->setDesc->natts < BRIN_PAGE_ITEMS_V1_12)
161 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
162 errmsg("function has wrong number of declared columns"),
163 errhint("To resolve the problem, update the \"pageinspect\" extension to the latest version.")));
164
165 indexRel = index_open(indexRelid, AccessShareLock);
166
167 if (!IS_BRIN(indexRel))
169 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
170 errmsg("\"%s\" is not a %s index",
171 RelationGetRelationName(indexRel), "BRIN")));
172
173 bdesc = brin_build_desc(indexRel);
174
175 /* minimally verify the page we got */
176 page = verify_brin_page(raw_page, BRIN_PAGETYPE_REGULAR, "regular");
177
178 if (PageIsNew(page))
179 {
180 brin_free_desc(bdesc);
181 index_close(indexRel, AccessShareLock);
183 }
184
185 /*
186 * Initialize output functions for all indexed datatypes; simplifies
187 * calling them later.
188 */
189 columns = palloc(sizeof(brin_column_state *) * RelationGetDescr(indexRel)->natts);
190 for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++)
191 {
192 Oid output;
193 bool isVarlena;
194 BrinOpcInfo *opcinfo;
195 int i;
196 brin_column_state *column;
197
198 opcinfo = bdesc->bd_info[attno - 1];
199 column = palloc(offsetof(brin_column_state, outputFn) +
200 sizeof(FmgrInfo) * opcinfo->oi_nstored);
201
202 column->nstored = opcinfo->oi_nstored;
203 for (i = 0; i < opcinfo->oi_nstored; i++)
204 {
205 getTypeOutputInfo(opcinfo->oi_typcache[i]->type_id, &output, &isVarlena);
206 fmgr_info(output, &column->outputFn[i]);
207 }
208
209 columns[attno - 1] = column;
210 }
211
212 offset = FirstOffsetNumber;
213 unusedItem = false;
214 dtup = NULL;
215 for (;;)
216 {
217 Datum values[8];
218 bool nulls[8] = {0};
219
220 /*
221 * This loop is called once for every attribute of every tuple in the
222 * page. At the start of a tuple, we get a NULL dtup; that's our
223 * signal for obtaining and decoding the next one. If that's not the
224 * case, we output the next attribute.
225 */
226 if (dtup == NULL)
227 {
228 ItemId itemId;
229
230 /* verify item status: if there's no data, we can't decode */
231 itemId = PageGetItemId(page, offset);
232 if (ItemIdIsUsed(itemId))
233 {
234 dtup = brin_deform_tuple(bdesc,
235 (BrinTuple *) PageGetItem(page, itemId),
236 NULL);
237 attno = 1;
238 unusedItem = false;
239 }
240 else
241 unusedItem = true;
242 }
243 else
244 attno++;
245
246 if (unusedItem)
247 {
248 values[0] = UInt16GetDatum(offset);
249 nulls[1] = true;
250 nulls[2] = true;
251 nulls[3] = true;
252 nulls[4] = true;
253 nulls[5] = true;
254 nulls[6] = true;
255 nulls[7] = true;
256 }
257 else
258 {
259 int att = attno - 1;
260
261 values[0] = UInt16GetDatum(offset);
262 switch (TupleDescAttr(rsinfo->setDesc, 1)->atttypid)
263 {
264 case INT8OID:
265 values[1] = Int64GetDatum((int64) dtup->bt_blkno);
266 break;
267 case INT4OID:
268 /* support for old extension version */
269 values[1] = UInt32GetDatum(dtup->bt_blkno);
270 break;
271 default:
272 elog(ERROR, "incorrect output types");
273 }
274 values[2] = UInt16GetDatum(attno);
275 values[3] = BoolGetDatum(dtup->bt_columns[att].bv_allnulls);
276 values[4] = BoolGetDatum(dtup->bt_columns[att].bv_hasnulls);
277 values[5] = BoolGetDatum(dtup->bt_placeholder);
278 values[6] = BoolGetDatum(dtup->bt_empty_range);
279 if (!dtup->bt_columns[att].bv_allnulls)
280 {
281 BrinValues *bvalues = &dtup->bt_columns[att];
283 bool first;
284 int i;
285
286 initStringInfo(&s);
287 appendStringInfoChar(&s, '{');
288
289 first = true;
290 for (i = 0; i < columns[att]->nstored; i++)
291 {
292 char *val;
293
294 if (!first)
295 appendStringInfoString(&s, " .. ");
296 first = false;
297 val = OutputFunctionCall(&columns[att]->outputFn[i],
298 bvalues->bv_values[i]);
300 pfree(val);
301 }
302 appendStringInfoChar(&s, '}');
303
305 pfree(s.data);
306 }
307 else
308 {
309 nulls[7] = true;
310 }
311 }
312
313 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
314
315 /*
316 * If the item was unused, jump straight to the next one; otherwise,
317 * the only cleanup needed here is to set our signal to go to the next
318 * tuple in the following iteration, by freeing the current one.
319 */
320 if (unusedItem)
321 offset = OffsetNumberNext(offset);
322 else if (attno >= bdesc->bd_tupdesc->natts)
323 {
324 pfree(dtup);
325 dtup = NULL;
326 offset = OffsetNumberNext(offset);
327 }
328
329 /*
330 * If we're beyond the end of the page, we're done.
331 */
332 if (offset > PageGetMaxOffsetNumber(page))
333 break;
334 }
335
336 brin_free_desc(bdesc);
337 index_close(indexRel, AccessShareLock);
338
339 return (Datum) 0;
340}
341
342Datum
344{
345 bytea *raw_page = PG_GETARG_BYTEA_P(0);
346 Page page;
347 BrinMetaPageData *meta;
348 TupleDesc tupdesc;
349 Datum values[4];
350 bool nulls[4] = {0};
351 HeapTuple htup;
352
353 if (!superuser())
355 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
356 errmsg("must be superuser to use raw page functions")));
357
358 page = verify_brin_page(raw_page, BRIN_PAGETYPE_META, "metapage");
359
360 if (PageIsNew(page))
362
363 /* Build a tuple descriptor for our result type */
364 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
365 elog(ERROR, "return type must be a row type");
366 tupdesc = BlessTupleDesc(tupdesc);
367
368 /* Extract values from the metapage */
369 meta = (BrinMetaPageData *) PageGetContents(page);
370 values[0] = CStringGetTextDatum(psprintf("0x%08X", meta->brinMagic));
371 values[1] = Int32GetDatum(meta->brinVersion);
374
375 htup = heap_form_tuple(tupdesc, values, nulls);
376
378}
379
380/*
381 * Return the TID array stored in a BRIN revmap page
382 */
383Datum
385{
386 struct
387 {
388 ItemPointerData *tids;
389 int idx;
390 } *state;
391 FuncCallContext *fctx;
392
393 if (!superuser())
395 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
396 errmsg("must be superuser to use raw page functions")));
397
398 if (SRF_IS_FIRSTCALL())
399 {
400 bytea *raw_page = PG_GETARG_BYTEA_P(0);
401 MemoryContext mctx;
402 Page page;
403
404 /* create a function context for cross-call persistence */
405 fctx = SRF_FIRSTCALL_INIT();
406
407 /* switch to memory context appropriate for multiple function calls */
409
410 /* minimally verify the page we got */
411 page = verify_brin_page(raw_page, BRIN_PAGETYPE_REVMAP, "revmap");
412
413 if (PageIsNew(page))
414 {
417 }
418
419 state = palloc(sizeof(*state));
420 state->tids = ((RevmapContents *) PageGetContents(page))->rm_tids;
421 state->idx = 0;
422
423 fctx->user_fctx = state;
424
426 }
427
428 fctx = SRF_PERCALL_SETUP();
429 state = fctx->user_fctx;
430
431 if (state->idx < REVMAP_PAGE_MAXITEMS)
432 SRF_RETURN_NEXT(fctx, PointerGetDatum(&state->tids[state->idx++]));
433
434 SRF_RETURN_DONE(fctx);
435}
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
int16 AttrNumber
Definition: attnum.h:21
static Datum values[MAXATTR]
Definition: bootstrap.c:151
void brin_free_desc(BrinDesc *bdesc)
Definition: brin.c:1628
BrinDesc * brin_build_desc(Relation rel)
Definition: brin.c:1573
#define BRIN_PAGETYPE_REVMAP
Definition: brin_page.h:52
#define BRIN_PAGETYPE_REGULAR
Definition: brin_page.h:53
#define BRIN_PAGETYPE_META
Definition: brin_page.h:51
#define BrinPageType(page)
Definition: brin_page.h:42
#define REVMAP_PAGE_MAXITEMS
Definition: brin_page.h:93
BrinMemTuple * brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple)
Definition: brin_tuple.c:553
Datum brin_revmap_data(PG_FUNCTION_ARGS)
Definition: brinfuncs.c:384
PG_FUNCTION_INFO_V1(brin_page_type)
struct brin_column_state brin_column_state
Datum brin_metapage_info(PG_FUNCTION_ARGS)
Definition: brinfuncs.c:343
#define BRIN_PAGE_ITEMS_V1_12
Definition: brinfuncs.c:121
#define IS_BRIN(r)
Definition: brinfuncs.c:31
Datum brin_page_type(PG_FUNCTION_ARGS)
Definition: brinfuncs.c:44
static Page verify_brin_page(bytea *raw_page, uint16 type, const char *strtype)
Definition: brinfuncs.c:93
Datum brin_page_items(PG_FUNCTION_ARGS)
Definition: brinfuncs.c:129
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 char * PageGetContents(Page page)
Definition: bufpage.h:258
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:244
PageData * Page
Definition: bufpage.h:82
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition: bufpage.h:372
#define CStringGetTextDatum(s)
Definition: builtins.h:97
#define MAXALIGN(LEN)
Definition: c.h:768
int64_t int64
Definition: c.h:485
#define FLEXIBLE_ARRAY_MEMBER
Definition: c.h:420
uint16_t uint16
Definition: c.h:487
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errhint(const char *fmt,...)
Definition: elog.c:1317
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 ereport(elevel,...)
Definition: elog.h:149
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
Definition: execTuples.c:2258
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:127
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1807
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1683
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_RETURN_NULL()
Definition: fmgr.h:345
#define PG_RETURN_TEXT_P(x)
Definition: fmgr.h:372
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
#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
#define SRF_IS_FIRSTCALL()
Definition: funcapi.h:304
#define SRF_PERCALL_SETUP()
Definition: funcapi.h:308
@ TYPEFUNC_COMPOSITE
Definition: funcapi.h:149
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition: funcapi.h:310
#define SRF_FIRSTCALL_INIT()
Definition: funcapi.h:306
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
Definition: funcapi.h:230
#define SRF_RETURN_DONE(_funcctx)
Definition: funcapi.h:328
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1117
FILE * output
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
long val
Definition: informix.c:689
int i
Definition: isn.c:72
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
#define AccessShareLock
Definition: lockdefs.h:36
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2907
void pfree(void *pointer)
Definition: mcxt.c:1521
void * palloc(Size size)
Definition: mcxt.c:1317
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
Page get_page_from_raw(bytea *raw_page)
Definition: rawpage.c:215
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
uintptr_t Datum
Definition: postgres.h:69
static Datum UInt16GetDatum(uint16 X)
Definition: postgres.h:197
static Datum BoolGetDatum(bool X)
Definition: postgres.h:107
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:217
static Datum UInt32GetDatum(uint32 X)
Definition: postgres.h:237
unsigned int Oid
Definition: postgres_ext.h:32
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
#define RelationGetDescr(relation)
Definition: rel.h:531
#define RelationGetRelationName(relation)
Definition: rel.h:539
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
uint32 brinVersion
Definition: brin_page.h:67
uint32 brinMagic
Definition: brin_page.h:66
BlockNumber lastRevmapPage
Definition: brin_page.h:69
BlockNumber pagesPerRange
Definition: brin_page.h:68
TypeCacheEntry * oi_typcache[FLEXIBLE_ARRAY_MEMBER]
Definition: brin_internal.h:37
uint16 oi_nstored
Definition: brin_internal.h:28
Datum * bv_values
Definition: brin_tuple.h:34
Definition: fmgr.h:57
void * user_fctx
Definition: funcapi.h:82
MemoryContext multi_call_memory_ctx
Definition: funcapi.h:101
TupleDesc setDesc
Definition: execnodes.h:358
Tuplestorestate * setResult
Definition: execnodes.h:357
FmgrInfo outputFn[FLEXIBLE_ARRAY_MEMBER]
Definition: brinfuncs.c:36
Definition: regguts.h:323
Definition: c.h:644
bool superuser(void)
Definition: superuser.c:46
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
text * cstring_to_text(const char *s)
Definition: varlena.c:184
const char * type