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