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-2021, PostgreSQL Global Development Group
6  *
7  * IDENTIFICATION
8  * contrib/pageinspect/brinfuncs.c
9  */
10 #include "postgres.h"
11 
12 #include "access/brin.h"
13 #include "access/brin_internal.h"
14 #include "access/brin_page.h"
15 #include "access/brin_revmap.h"
16 #include "access/brin_tuple.h"
17 #include "access/htup_details.h"
18 #include "catalog/index.h"
19 #include "catalog/pg_type.h"
20 #include "funcapi.h"
21 #include "lib/stringinfo.h"
22 #include "miscadmin.h"
23 #include "pageinspect.h"
24 #include "utils/array.h"
25 #include "utils/builtins.h"
26 #include "utils/lsyscache.h"
27 #include "utils/rel.h"
28 
33 
34 typedef struct brin_column_state
35 {
36  int nstored;
39 
40 
41 static Page verify_brin_page(bytea *raw_page, uint16 type,
42  const char *strtype);
43 
44 Datum
46 {
47  bytea *raw_page = PG_GETARG_BYTEA_P(0);
48  Page page = VARDATA(raw_page);
49  int raw_page_size;
50  char *type;
51 
52  if (!superuser())
53  ereport(ERROR,
54  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
55  errmsg("must be superuser to use raw page functions")));
56 
57  raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
58 
59  if (raw_page_size != BLCKSZ)
60  ereport(ERROR,
61  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
62  errmsg("input page too small"),
63  errdetail("Expected size %d, got %d",
64  BLCKSZ, raw_page_size)));
65 
66  switch (BrinPageType(page))
67  {
68  case BRIN_PAGETYPE_META:
69  type = "meta";
70  break;
72  type = "revmap";
73  break;
75  type = "regular";
76  break;
77  default:
78  type = psprintf("unknown (%02x)", BrinPageType(page));
79  break;
80  }
81 
83 }
84 
85 /*
86  * Verify that the given bytea contains a BRIN page of the indicated page
87  * type, or die in the attempt. A pointer to the page is returned.
88  */
89 static Page
90 verify_brin_page(bytea *raw_page, uint16 type, const char *strtype)
91 {
92  Page page;
93  int raw_page_size;
94 
95  raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
96 
97  if (raw_page_size != BLCKSZ)
98  ereport(ERROR,
99  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
100  errmsg("input page too small"),
101  errdetail("Expected size %d, got %d",
102  BLCKSZ, raw_page_size)));
103 
104  page = VARDATA(raw_page);
105 
106  /* verify the special space says this page is what we want */
107  if (BrinPageType(page) != type)
108  ereport(ERROR,
109  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
110  errmsg("page is not a BRIN page of type \"%s\"", strtype),
111  errdetail("Expected special type %08x, got %08x.",
112  type, BrinPageType(page))));
113 
114  return page;
115 }
116 
117 
118 /*
119  * Extract all item values from a BRIN index page
120  *
121  * Usage: SELECT * FROM brin_page_items(get_raw_page('idx', 1), 'idx'::regclass);
122  */
123 Datum
125 {
126  bytea *raw_page = PG_GETARG_BYTEA_P(0);
127  Oid indexRelid = PG_GETARG_OID(1);
128  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
129  TupleDesc tupdesc;
130  MemoryContext oldcontext;
131  Tuplestorestate *tupstore;
132  Relation indexRel;
133  brin_column_state **columns;
134  BrinDesc *bdesc;
135  BrinMemTuple *dtup;
136  Page page;
137  OffsetNumber offset;
138  AttrNumber attno;
139  bool unusedItem;
140 
141  if (!superuser())
142  ereport(ERROR,
143  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
144  errmsg("must be superuser to use raw page functions")));
145 
146  /* check to see if caller supports us returning a tuplestore */
147  if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
148  ereport(ERROR,
149  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
150  errmsg("set-valued function called in context that cannot accept a set")));
151  if (!(rsinfo->allowedModes & SFRM_Materialize) ||
152  rsinfo->expectedDesc == NULL)
153  ereport(ERROR,
154  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
155  errmsg("materialize mode required, but it is not allowed in this context")));
156 
157  /* Build a tuple descriptor for our result type */
158  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
159  elog(ERROR, "return type must be a row type");
160 
161  /* Build tuplestore to hold the result rows */
163 
164  tupstore = tuplestore_begin_heap(true, false, work_mem);
165  rsinfo->returnMode = SFRM_Materialize;
166  rsinfo->setResult = tupstore;
167  rsinfo->setDesc = tupdesc;
168 
169  MemoryContextSwitchTo(oldcontext);
170 
171  indexRel = index_open(indexRelid, AccessShareLock);
172  bdesc = brin_build_desc(indexRel);
173 
174  /* minimally verify the page we got */
175  page = verify_brin_page(raw_page, BRIN_PAGETYPE_REGULAR, "regular");
176 
177  /*
178  * Initialize output functions for all indexed datatypes; simplifies
179  * calling them later.
180  */
181  columns = palloc(sizeof(brin_column_state *) * RelationGetDescr(indexRel)->natts);
182  for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++)
183  {
184  Oid output;
185  bool isVarlena;
186  BrinOpcInfo *opcinfo;
187  int i;
188  brin_column_state *column;
189 
190  opcinfo = bdesc->bd_info[attno - 1];
192  sizeof(FmgrInfo) * opcinfo->oi_nstored);
193 
194  column->nstored = opcinfo->oi_nstored;
195  for (i = 0; i < opcinfo->oi_nstored; i++)
196  {
197  getTypeOutputInfo(opcinfo->oi_typcache[i]->type_id, &output, &isVarlena);
198  fmgr_info(output, &column->outputFn[i]);
199  }
200 
201  columns[attno - 1] = column;
202  }
203 
204  offset = FirstOffsetNumber;
205  unusedItem = false;
206  dtup = NULL;
207  for (;;)
208  {
209  Datum values[7];
210  bool nulls[7];
211 
212  /*
213  * This loop is called once for every attribute of every tuple in the
214  * page. At the start of a tuple, we get a NULL dtup; that's our
215  * signal for obtaining and decoding the next one. If that's not the
216  * case, we output the next attribute.
217  */
218  if (dtup == NULL)
219  {
220  ItemId itemId;
221 
222  /* verify item status: if there's no data, we can't decode */
223  itemId = PageGetItemId(page, offset);
224  if (ItemIdIsUsed(itemId))
225  {
226  dtup = brin_deform_tuple(bdesc,
227  (BrinTuple *) PageGetItem(page, itemId),
228  NULL);
229  attno = 1;
230  unusedItem = false;
231  }
232  else
233  unusedItem = true;
234  }
235  else
236  attno++;
237 
238  MemSet(nulls, 0, sizeof(nulls));
239 
240  if (unusedItem)
241  {
242  values[0] = UInt16GetDatum(offset);
243  nulls[1] = true;
244  nulls[2] = true;
245  nulls[3] = true;
246  nulls[4] = true;
247  nulls[5] = true;
248  nulls[6] = true;
249  }
250  else
251  {
252  int att = attno - 1;
253 
254  values[0] = UInt16GetDatum(offset);
255  switch (TupleDescAttr(tupdesc, 1)->atttypid)
256  {
257  case INT8OID:
258  values[1] = Int64GetDatum((int64) dtup->bt_blkno);
259  break;
260  case INT4OID:
261  /* support for old extension version */
262  values[1] = UInt32GetDatum(dtup->bt_blkno);
263  break;
264  default:
265  elog(ERROR, "incorrect output types");
266  }
267  values[2] = UInt16GetDatum(attno);
268  values[3] = BoolGetDatum(dtup->bt_columns[att].bv_allnulls);
269  values[4] = BoolGetDatum(dtup->bt_columns[att].bv_hasnulls);
270  values[5] = BoolGetDatum(dtup->bt_placeholder);
271  if (!dtup->bt_columns[att].bv_allnulls)
272  {
273  BrinValues *bvalues = &dtup->bt_columns[att];
274  StringInfoData s;
275  bool first;
276  int i;
277 
278  initStringInfo(&s);
279  appendStringInfoChar(&s, '{');
280 
281  first = true;
282  for (i = 0; i < columns[att]->nstored; i++)
283  {
284  char *val;
285 
286  if (!first)
287  appendStringInfoString(&s, " .. ");
288  first = false;
289  val = OutputFunctionCall(&columns[att]->outputFn[i],
290  bvalues->bv_values[i]);
291  appendStringInfoString(&s, val);
292  pfree(val);
293  }
294  appendStringInfoChar(&s, '}');
295 
296  values[6] = CStringGetTextDatum(s.data);
297  pfree(s.data);
298  }
299  else
300  {
301  nulls[6] = true;
302  }
303  }
304 
305  tuplestore_putvalues(tupstore, tupdesc, values, nulls);
306 
307  /*
308  * If the item was unused, jump straight to the next one; otherwise,
309  * the only cleanup needed here is to set our signal to go to the next
310  * tuple in the following iteration, by freeing the current one.
311  */
312  if (unusedItem)
313  offset = OffsetNumberNext(offset);
314  else if (attno >= bdesc->bd_tupdesc->natts)
315  {
316  pfree(dtup);
317  dtup = NULL;
318  offset = OffsetNumberNext(offset);
319  }
320 
321  /*
322  * If we're beyond the end of the page, we're done.
323  */
324  if (offset > PageGetMaxOffsetNumber(page))
325  break;
326  }
327 
328  /* clean up and return the tuplestore */
329  brin_free_desc(bdesc);
330  tuplestore_donestoring(tupstore);
331  index_close(indexRel, AccessShareLock);
332 
333  return (Datum) 0;
334 }
335 
336 Datum
338 {
339  bytea *raw_page = PG_GETARG_BYTEA_P(0);
340  Page page;
341  BrinMetaPageData *meta;
342  TupleDesc tupdesc;
343  Datum values[4];
344  bool nulls[4];
345  HeapTuple htup;
346 
347  if (!superuser())
348  ereport(ERROR,
349  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
350  errmsg("must be superuser to use raw page functions")));
351 
352  page = verify_brin_page(raw_page, BRIN_PAGETYPE_META, "metapage");
353 
354  /* Build a tuple descriptor for our result type */
355  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
356  elog(ERROR, "return type must be a row type");
357  tupdesc = BlessTupleDesc(tupdesc);
358 
359  /* Extract values from the metapage */
360  meta = (BrinMetaPageData *) PageGetContents(page);
361  MemSet(nulls, 0, sizeof(nulls));
362  values[0] = CStringGetTextDatum(psprintf("0x%08X", meta->brinMagic));
363  values[1] = Int32GetDatum(meta->brinVersion);
364  values[2] = Int32GetDatum(meta->pagesPerRange);
365  values[3] = Int64GetDatum(meta->lastRevmapPage);
366 
367  htup = heap_form_tuple(tupdesc, values, nulls);
368 
370 }
371 
372 /*
373  * Return the TID array stored in a BRIN revmap page
374  */
375 Datum
377 {
378  struct
379  {
380  ItemPointerData *tids;
381  int idx;
382  } *state;
383  FuncCallContext *fctx;
384 
385  if (!superuser())
386  ereport(ERROR,
387  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
388  errmsg("must be superuser to use raw page functions")));
389 
390  if (SRF_IS_FIRSTCALL())
391  {
392  bytea *raw_page = PG_GETARG_BYTEA_P(0);
393  MemoryContext mctx;
394  Page page;
395 
396  /* minimally verify the page we got */
397  page = verify_brin_page(raw_page, BRIN_PAGETYPE_REVMAP, "revmap");
398 
399  /* create a function context for cross-call persistence */
400  fctx = SRF_FIRSTCALL_INIT();
401 
402  /* switch to memory context appropriate for multiple function calls */
404 
405  state = palloc(sizeof(*state));
406  state->tids = ((RevmapContents *) PageGetContents(page))->rm_tids;
407  state->idx = 0;
408 
409  fctx->user_fctx = state;
410 
411  MemoryContextSwitchTo(mctx);
412  }
413 
414  fctx = SRF_PERCALL_SETUP();
415  state = fctx->user_fctx;
416 
417  if (state->idx < REVMAP_PAGE_MAXITEMS)
418  SRF_RETURN_NEXT(fctx, PointerGetDatum(&state->tids[state->idx++]));
419 
420  SRF_RETURN_DONE(fctx);
421 }
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, Datum *values, bool *isnull)
Definition: tuplestore.c:750
void brin_free_desc(BrinDesc *bdesc)
Definition: brin.c:1075
uint32 brinVersion
Definition: brin_page.h:67
Definition: fmgr.h:56
#define IsA(nodeptr, _type_)
Definition: nodes.h:584
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:207
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2854
#define VARDATA(PTR)
Definition: postgres.h:302
#define RelationGetDescr(relation)
Definition: rel.h:483
#define VARSIZE(PTR)
Definition: postgres.h:303
static void output(uint64 loop_count)
#define SRF_IS_FIRSTCALL()
Definition: funcapi.h:294
#define PointerGetDatum(X)
Definition: postgres.h:556
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define VARHDRSZ
Definition: c.h:627
Datum brin_page_type(PG_FUNCTION_ARGS)
Definition: brinfuncs.c:45
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
FmgrInfo outputFn[FLEXIBLE_ARRAY_MEMBER]
Definition: brinfuncs.c:37
#define tuplestore_donestoring(state)
Definition: tuplestore.h:60
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define AccessShareLock
Definition: lockdefs.h:36
#define FLEXIBLE_ARRAY_MEMBER
Definition: c.h:350
int errcode(int sqlerrcode)
Definition: elog.c:704
bool superuser(void)
Definition: superuser.c:46
#define MemSet(start, val, len)
Definition: c.h:1008
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
uint32 brinMagic
Definition: brin_page.h:66
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
unsigned int Oid
Definition: postgres_ext.h:31
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
#define SRF_PERCALL_SETUP()
Definition: funcapi.h:298
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1576
uint16 OffsetNumber
Definition: off.h:24
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition: funcapi.h:300
BrinMemTuple * brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple)
Definition: brin_tuple.c:517
#define PG_GETARG_BYTEA_P(n)
Definition: fmgr.h:335
TupleDesc expectedDesc
Definition: execnodes.h:303
unsigned short uint16
Definition: c.h:440
void pfree(void *pointer)
Definition: mcxt.c:1057
#define ERROR
Definition: elog.h:45
TypeCacheEntry * oi_typcache[FLEXIBLE_ARRAY_MEMBER]
Definition: brin_internal.h:34
PG_FUNCTION_INFO_V1(brin_page_type)
#define BRIN_PAGETYPE_META
Definition: brin_page.h:51
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:126
#define BRIN_PAGETYPE_REGULAR
Definition: brin_page.h:53
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
Definition: execTuples.c:2052
uint16 oi_nstored
Definition: brin_internal.h:28
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:176
Datum brin_metapage_info(PG_FUNCTION_ARGS)
Definition: brinfuncs.c:337
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define FirstOffsetNumber
Definition: off.h:27
int errdetail(const char *fmt,...)
Definition: elog.c:1048
Datum brin_page_items(PG_FUNCTION_ARGS)
Definition: brinfuncs.c:124
#define UInt32GetDatum(X)
Definition: postgres.h:493
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1700
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:188
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
BlockNumber lastRevmapPage
Definition: brin_page.h:69
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:318
#define PageGetContents(page)
Definition: bufpage.h:246
uintptr_t Datum
Definition: postgres.h:367
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
int work_mem
Definition: globals.c:122
#define REVMAP_PAGE_MAXITEMS
Definition: brin_page.h:93
#define BoolGetDatum(X)
Definition: postgres.h:402
#define ereport(elevel,...)
Definition: elog.h:155
int allowedModes
Definition: execnodes.h:304
BlockNumber pagesPerRange
Definition: brin_page.h:68
#define PG_RETURN_TEXT_P(x)
Definition: fmgr.h:372
SetFunctionReturnMode returnMode
Definition: execnodes.h:306
text * cstring_to_text(const char *s)
Definition: varlena.c:189
BrinDesc * brin_build_desc(Relation rel)
Definition: brin.c:1020
Definition: regguts.h:317
MemoryContext multi_call_memory_ctx
Definition: funcapi.h:101
#define BRIN_PAGETYPE_REVMAP
Definition: brin_page.h:52
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
Datum brin_revmap_data(PG_FUNCTION_ARGS)
Definition: brinfuncs.c:376
#define HeapTupleGetDatum(tuple)
Definition: funcapi.h:221
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:232
#define BrinPageType(page)
Definition: brin_page.h:42
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:158
Tuplestorestate * setResult
Definition: execnodes.h:309
static Datum values[MAXATTR]
Definition: bootstrap.c:165
ExprContext * econtext
Definition: execnodes.h:302
#define Int32GetDatum(X)
Definition: postgres.h:479
void * user_fctx
Definition: funcapi.h:82
TupleDesc setDesc
Definition: execnodes.h:310
void * palloc(Size size)
Definition: mcxt.c:950
int errmsg(const char *fmt,...)
Definition: elog.c:915
#define elog(elevel,...)
Definition: elog.h:228
int i
#define CStringGetTextDatum(s)
Definition: builtins.h:82
Definition: c.h:621
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
static Page verify_brin_page(bytea *raw_page, uint16 type, const char *strtype)
Definition: brinfuncs.c:90
Datum * bv_values
Definition: brin_tuple.h:29
#define UInt16GetDatum(X)
Definition: postgres.h:465
int16 AttrNumber
Definition: attnum.h:21
struct brin_column_state brin_column_state
long val
Definition: informix.c:664
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:132
#define offsetof(type, field)
Definition: c.h:727
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
#define SRF_RETURN_DONE(_funcctx)
Definition: funcapi.h:318
#define SRF_FIRSTCALL_INIT()
Definition: funcapi.h:296