PostgreSQL Source Code  git master
btreefuncs.c
Go to the documentation of this file.
1 /*
2  * contrib/pageinspect/btreefuncs.c
3  *
4  *
5  * btreefuncs.c
6  *
7  * Copyright (c) 2006 Satoshi Nagayasu <nagayasus@nttdata.co.jp>
8  *
9  * Permission to use, copy, modify, and distribute this software and
10  * its documentation for any purpose, without fee, and without a
11  * written agreement is hereby granted, provided that the above
12  * copyright notice and this paragraph and the following two
13  * paragraphs appear in all copies.
14  *
15  * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
16  * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
17  * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
18  * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED
19  * OF THE POSSIBILITY OF SUCH DAMAGE.
20  *
21  * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS
24  * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
25  * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
26  */
27 
28 #include "postgres.h"
29 
30 #include "access/nbtree.h"
31 #include "access/relation.h"
32 #include "catalog/namespace.h"
33 #include "catalog/pg_am.h"
34 #include "catalog/pg_type.h"
35 #include "funcapi.h"
36 #include "miscadmin.h"
37 #include "pageinspect.h"
38 #include "utils/array.h"
39 #include "utils/builtins.h"
40 #include "utils/rel.h"
41 #include "utils/varlena.h"
42 
49 
50 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
51 #define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
52 #define DatumGetItemPointer(X) ((ItemPointer) DatumGetPointer(X))
53 #define ItemPointerGetDatum(X) PointerGetDatum(X)
54 
55 /* note: BlockNumber is unsigned, hence can't be negative */
56 #define CHECK_RELATION_BLOCK_RANGE(rel, blkno) { \
57  if ( RelationGetNumberOfBlocks(rel) <= (BlockNumber) (blkno) ) \
58  elog(ERROR, "block number out of range"); }
59 
60 /* ------------------------------------------------
61  * structure for single btree page statistics
62  * ------------------------------------------------
63  */
64 typedef struct BTPageStat
65 {
73  char type;
74 
75  /* opaque data */
82 
83 
84 /* -------------------------------------------------
85  * GetBTPageStatistics()
86  *
87  * Collect statistics of single b-tree page
88  * -------------------------------------------------
89  */
90 static void
92 {
93  Page page = BufferGetPage(buffer);
94  PageHeader phdr = (PageHeader) page;
95  OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
96  BTPageOpaque opaque = BTPageGetOpaque(page);
97  int item_size = 0;
98  int off;
99 
100  stat->blkno = blkno;
101 
102  stat->max_avail = BLCKSZ - (BLCKSZ - phdr->pd_special + SizeOfPageHeaderData);
103 
104  stat->dead_items = stat->live_items = 0;
105 
106  stat->page_size = PageGetPageSize(page);
107 
108  /* page type (flags) */
109  if (P_ISDELETED(opaque))
110  {
111  /* We divide deleted pages into leaf ('d') or internal ('D') */
112  if (P_ISLEAF(opaque) || !P_HAS_FULLXID(opaque))
113  stat->type = 'd';
114  else
115  stat->type = 'D';
116 
117  /*
118  * Report safexid in a deleted page.
119  *
120  * Handle pg_upgrade'd deleted pages that used the previous safexid
121  * representation in btpo_level field (this used to be a union type
122  * called "bpto").
123  */
124  if (P_HAS_FULLXID(opaque))
125  {
126  FullTransactionId safexid = BTPageGetDeleteXid(page);
127 
128  elog(DEBUG2, "deleted page from block %u has safexid %u:%u",
129  blkno, EpochFromFullTransactionId(safexid),
130  XidFromFullTransactionId(safexid));
131  }
132  else
133  elog(DEBUG2, "deleted page from block %u has safexid %u",
134  blkno, opaque->btpo_level);
135 
136  /* Don't interpret BTDeletedPageData as index tuples */
137  maxoff = InvalidOffsetNumber;
138  }
139  else if (P_IGNORE(opaque))
140  stat->type = 'e';
141  else if (P_ISLEAF(opaque))
142  stat->type = 'l';
143  else if (P_ISROOT(opaque))
144  stat->type = 'r';
145  else
146  stat->type = 'i';
147 
148  /* btpage opaque data */
149  stat->btpo_prev = opaque->btpo_prev;
150  stat->btpo_next = opaque->btpo_next;
151  stat->btpo_level = opaque->btpo_level;
152  stat->btpo_flags = opaque->btpo_flags;
153  stat->btpo_cycleid = opaque->btpo_cycleid;
154 
155  /* count live and dead tuples, and free space */
156  for (off = FirstOffsetNumber; off <= maxoff; off++)
157  {
158  IndexTuple itup;
159 
160  ItemId id = PageGetItemId(page, off);
161 
162  itup = (IndexTuple) PageGetItem(page, id);
163 
164  item_size += IndexTupleSize(itup);
165 
166  if (!ItemIdIsDead(id))
167  stat->live_items++;
168  else
169  stat->dead_items++;
170  }
171  stat->free_size = PageGetFreeSpace(page);
172 
173  if ((stat->live_items + stat->dead_items) > 0)
174  stat->avg_item_size = item_size / (stat->live_items + stat->dead_items);
175  else
176  stat->avg_item_size = 0;
177 }
178 
179 /* -----------------------------------------------
180  * bt_page_stats()
181  *
182  * Usage: SELECT * FROM bt_page_stats('t1_pkey', 1);
183  * -----------------------------------------------
184  */
185 static Datum
187 {
189  int64 blkno = (ext_version == PAGEINSPECT_V1_8 ? PG_GETARG_UINT32(1) : PG_GETARG_INT64(1));
190  Buffer buffer;
191  Relation rel;
192  RangeVar *relrv;
193  Datum result;
194  HeapTuple tuple;
195  TupleDesc tupleDesc;
196  int j;
197  char *values[11];
199 
200  if (!superuser())
201  ereport(ERROR,
202  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
203  errmsg("must be superuser to use pageinspect functions")));
204 
206  rel = relation_openrv(relrv, AccessShareLock);
207 
208  if (!IS_INDEX(rel) || !IS_BTREE(rel))
209  ereport(ERROR,
210  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
211  errmsg("\"%s\" is not a %s index",
212  RelationGetRelationName(rel), "btree")));
213 
214  /*
215  * Reject attempts to read non-local temporary relations; we would be
216  * likely to get wrong data since we have no visibility into the owning
217  * session's local buffers.
218  */
219  if (RELATION_IS_OTHER_TEMP(rel))
220  ereport(ERROR,
221  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
222  errmsg("cannot access temporary tables of other sessions")));
223 
224  if (blkno < 0 || blkno > MaxBlockNumber)
225  ereport(ERROR,
226  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
227  errmsg("invalid block number")));
228 
229  if (blkno == 0)
230  ereport(ERROR,
231  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
232  errmsg("block 0 is a meta page")));
233 
234  CHECK_RELATION_BLOCK_RANGE(rel, blkno);
235 
236  buffer = ReadBuffer(rel, blkno);
237  LockBuffer(buffer, BUFFER_LOCK_SHARE);
238 
239  /* keep compiler quiet */
240  stat.btpo_prev = stat.btpo_next = InvalidBlockNumber;
241  stat.btpo_flags = stat.free_size = stat.avg_item_size = 0;
242 
243  GetBTPageStatistics(blkno, buffer, &stat);
244 
245  UnlockReleaseBuffer(buffer);
247 
248  /* Build a tuple descriptor for our result type */
249  if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
250  elog(ERROR, "return type must be a row type");
251 
252  j = 0;
253  values[j++] = psprintf("%u", stat.blkno);
254  values[j++] = psprintf("%c", stat.type);
255  values[j++] = psprintf("%u", stat.live_items);
256  values[j++] = psprintf("%u", stat.dead_items);
257  values[j++] = psprintf("%u", stat.avg_item_size);
258  values[j++] = psprintf("%u", stat.page_size);
259  values[j++] = psprintf("%u", stat.free_size);
260  values[j++] = psprintf("%u", stat.btpo_prev);
261  values[j++] = psprintf("%u", stat.btpo_next);
262  values[j++] = psprintf("%u", stat.btpo_level);
263  values[j++] = psprintf("%d", stat.btpo_flags);
264 
266  values);
267 
268  result = HeapTupleGetDatum(tuple);
269 
270  PG_RETURN_DATUM(result);
271 }
272 
273 Datum
275 {
277 }
278 
279 /* entry point for old extension version */
280 Datum
282 {
284 }
285 
286 
287 /*
288  * cross-call data structure for SRF
289  */
290 struct user_args
291 {
294  bool leafpage;
295  bool rightmost;
297 };
298 
299 /*-------------------------------------------------------
300  * bt_page_print_tuples()
301  *
302  * Form a tuple describing index tuple at a given offset
303  * ------------------------------------------------------
304  */
305 static Datum
307 {
308  Page page = uargs->page;
309  OffsetNumber offset = uargs->offset;
310  bool leafpage = uargs->leafpage;
311  bool rightmost = uargs->rightmost;
312  bool ispivottuple;
313  Datum values[9];
314  bool nulls[9];
315  HeapTuple tuple;
316  ItemId id;
317  IndexTuple itup;
318  int j;
319  int off;
320  int dlen;
321  char *dump,
322  *datacstring;
323  char *ptr;
324  ItemPointer htid;
325 
326  id = PageGetItemId(page, offset);
327 
328  if (!ItemIdIsValid(id))
329  elog(ERROR, "invalid ItemId");
330 
331  itup = (IndexTuple) PageGetItem(page, id);
332 
333  j = 0;
334  memset(nulls, 0, sizeof(nulls));
335  values[j++] = DatumGetInt16(offset);
336  values[j++] = ItemPointerGetDatum(&itup->t_tid);
337  values[j++] = Int32GetDatum((int) IndexTupleSize(itup));
340 
341  ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
342  dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
343 
344  /*
345  * Make sure that "data" column does not include posting list or pivot
346  * tuple representation of heap TID(s).
347  *
348  * Note: BTreeTupleIsPivot() won't work reliably on !heapkeyspace indexes
349  * (those built before BTREE_VERSION 4), but we have no way of determining
350  * if this page came from a !heapkeyspace index. We may only have a bytea
351  * nbtree page image to go on, so in general there is no metapage that we
352  * can check.
353  *
354  * That's okay here because BTreeTupleIsPivot() can only return false for
355  * a !heapkeyspace pivot, never true for a !heapkeyspace non-pivot. Since
356  * heap TID isn't part of the keyspace in a !heapkeyspace index anyway,
357  * there cannot possibly be a pivot tuple heap TID representation that we
358  * fail to make an adjustment for. A !heapkeyspace index can have
359  * BTreeTupleIsPivot() return true (due to things like suffix truncation
360  * for INCLUDE indexes in Postgres v11), but when that happens
361  * BTreeTupleGetHeapTID() can be trusted to work reliably (i.e. return
362  * NULL).
363  *
364  * Note: BTreeTupleIsPosting() always works reliably, even with
365  * !heapkeyspace indexes.
366  */
367  if (BTreeTupleIsPosting(itup))
368  dlen -= IndexTupleSize(itup) - BTreeTupleGetPostingOffset(itup);
369  else if (BTreeTupleIsPivot(itup) && BTreeTupleGetHeapTID(itup) != NULL)
370  dlen -= MAXALIGN(sizeof(ItemPointerData));
371 
372  if (dlen < 0 || dlen > INDEX_SIZE_MASK)
373  elog(ERROR, "invalid tuple length %d for tuple at offset number %u",
374  dlen, offset);
375  dump = palloc0(dlen * 3 + 1);
376  datacstring = dump;
377  for (off = 0; off < dlen; off++)
378  {
379  if (off > 0)
380  *dump++ = ' ';
381  sprintf(dump, "%02x", *(ptr + off) & 0xff);
382  dump += 2;
383  }
384  values[j++] = CStringGetTextDatum(datacstring);
385  pfree(datacstring);
386 
387  /*
388  * We need to work around the BTreeTupleIsPivot() !heapkeyspace limitation
389  * again. Deduce whether or not tuple must be a pivot tuple based on
390  * whether or not the page is a leaf page, as well as the page offset
391  * number of the tuple.
392  */
393  ispivottuple = (!leafpage || (!rightmost && offset == P_HIKEY));
394 
395  /* LP_DEAD bit can never be set for pivot tuples, so show a NULL there */
396  if (!ispivottuple)
397  values[j++] = BoolGetDatum(ItemIdIsDead(id));
398  else
399  {
400  Assert(!ItemIdIsDead(id));
401  nulls[j++] = true;
402  }
403 
404  htid = BTreeTupleGetHeapTID(itup);
405  if (ispivottuple && !BTreeTupleIsPivot(itup))
406  {
407  /* Don't show bogus heap TID in !heapkeyspace pivot tuple */
408  htid = NULL;
409  }
410 
411  if (htid)
412  values[j++] = ItemPointerGetDatum(htid);
413  else
414  nulls[j++] = true;
415 
416  if (BTreeTupleIsPosting(itup))
417  {
418  /* Build an array of item pointers */
419  ItemPointer tids;
420  Datum *tids_datum;
421  int nposting;
422 
423  tids = BTreeTupleGetPosting(itup);
424  nposting = BTreeTupleGetNPosting(itup);
425  tids_datum = (Datum *) palloc(nposting * sizeof(Datum));
426  for (int i = 0; i < nposting; i++)
427  tids_datum[i] = ItemPointerGetDatum(&tids[i]);
428  values[j++] = PointerGetDatum(construct_array(tids_datum,
429  nposting,
430  TIDOID,
431  sizeof(ItemPointerData),
432  false, TYPALIGN_SHORT));
433  pfree(tids_datum);
434  }
435  else
436  nulls[j++] = true;
437 
438  /* Build and return the result tuple */
439  tuple = heap_form_tuple(uargs->tupd, values, nulls);
440 
441  return HeapTupleGetDatum(tuple);
442 }
443 
444 /*-------------------------------------------------------
445  * bt_page_items()
446  *
447  * Get IndexTupleData set in a btree page
448  *
449  * Usage: SELECT * FROM bt_page_items('t1_pkey', 1);
450  *-------------------------------------------------------
451  */
452 static Datum
454 {
456  int64 blkno = (ext_version == PAGEINSPECT_V1_8 ? PG_GETARG_UINT32(1) : PG_GETARG_INT64(1));
457  Datum result;
458  FuncCallContext *fctx;
459  MemoryContext mctx;
460  struct user_args *uargs;
461 
462  if (!superuser())
463  ereport(ERROR,
464  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
465  errmsg("must be superuser to use pageinspect functions")));
466 
467  if (SRF_IS_FIRSTCALL())
468  {
469  RangeVar *relrv;
470  Relation rel;
471  Buffer buffer;
472  BTPageOpaque opaque;
473  TupleDesc tupleDesc;
474 
475  fctx = SRF_FIRSTCALL_INIT();
476 
478  rel = relation_openrv(relrv, AccessShareLock);
479 
480  if (!IS_INDEX(rel) || !IS_BTREE(rel))
481  ereport(ERROR,
482  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
483  errmsg("\"%s\" is not a %s index",
484  RelationGetRelationName(rel), "btree")));
485 
486  /*
487  * Reject attempts to read non-local temporary relations; we would be
488  * likely to get wrong data since we have no visibility into the
489  * owning session's local buffers.
490  */
491  if (RELATION_IS_OTHER_TEMP(rel))
492  ereport(ERROR,
493  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
494  errmsg("cannot access temporary tables of other sessions")));
495 
496  if (blkno < 0 || blkno > MaxBlockNumber)
497  ereport(ERROR,
498  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
499  errmsg("invalid block number")));
500 
501  if (blkno == 0)
502  ereport(ERROR,
503  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
504  errmsg("block 0 is a meta page")));
505 
506  CHECK_RELATION_BLOCK_RANGE(rel, blkno);
507 
508  buffer = ReadBuffer(rel, blkno);
509  LockBuffer(buffer, BUFFER_LOCK_SHARE);
510 
511  /*
512  * We copy the page into local storage to avoid holding pin on the
513  * buffer longer than we must, and possibly failing to release it at
514  * all if the calling query doesn't fetch all rows.
515  */
517 
518  uargs = palloc(sizeof(struct user_args));
519 
520  uargs->page = palloc(BLCKSZ);
521  memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ);
522 
523  UnlockReleaseBuffer(buffer);
525 
526  uargs->offset = FirstOffsetNumber;
527 
528  opaque = BTPageGetOpaque(uargs->page);
529 
530  if (!P_ISDELETED(opaque))
531  fctx->max_calls = PageGetMaxOffsetNumber(uargs->page);
532  else
533  {
534  /* Don't interpret BTDeletedPageData as index tuples */
535  elog(NOTICE, "page from block " INT64_FORMAT " is deleted", blkno);
536  fctx->max_calls = 0;
537  }
538  uargs->leafpage = P_ISLEAF(opaque);
539  uargs->rightmost = P_RIGHTMOST(opaque);
540 
541  /* Build a tuple descriptor for our result type */
542  if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
543  elog(ERROR, "return type must be a row type");
544  tupleDesc = BlessTupleDesc(tupleDesc);
545 
546  uargs->tupd = tupleDesc;
547 
548  fctx->user_fctx = uargs;
549 
550  MemoryContextSwitchTo(mctx);
551  }
552 
553  fctx = SRF_PERCALL_SETUP();
554  uargs = fctx->user_fctx;
555 
556  if (fctx->call_cntr < fctx->max_calls)
557  {
558  result = bt_page_print_tuples(uargs);
559  uargs->offset++;
560  SRF_RETURN_NEXT(fctx, result);
561  }
562 
563  SRF_RETURN_DONE(fctx);
564 }
565 
566 Datum
568 {
570 }
571 
572 /* entry point for old extension version */
573 Datum
575 {
577 }
578 
579 /*-------------------------------------------------------
580  * bt_page_items_bytea()
581  *
582  * Get IndexTupleData set in a btree page
583  *
584  * Usage: SELECT * FROM bt_page_items(get_raw_page('t1_pkey', 1));
585  *-------------------------------------------------------
586  */
587 
588 Datum
590 {
591  bytea *raw_page = PG_GETARG_BYTEA_P(0);
592  Datum result;
593  FuncCallContext *fctx;
594  struct user_args *uargs;
595 
596  if (!superuser())
597  ereport(ERROR,
598  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
599  errmsg("must be superuser to use raw page functions")));
600 
601  if (SRF_IS_FIRSTCALL())
602  {
603  BTPageOpaque opaque;
604  MemoryContext mctx;
605  TupleDesc tupleDesc;
606 
607  fctx = SRF_FIRSTCALL_INIT();
609 
610  uargs = palloc(sizeof(struct user_args));
611 
612  uargs->page = get_page_from_raw(raw_page);
613 
614  if (PageIsNew(uargs->page))
615  {
616  MemoryContextSwitchTo(mctx);
617  PG_RETURN_NULL();
618  }
619 
620  uargs->offset = FirstOffsetNumber;
621 
622  /* verify the special space has the expected size */
623  if (PageGetSpecialSize(uargs->page) != MAXALIGN(sizeof(BTPageOpaqueData)))
624  ereport(ERROR,
625  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
626  errmsg("input page is not a valid %s page", "btree"),
627  errdetail("Expected special size %d, got %d.",
628  (int) MAXALIGN(sizeof(BTPageOpaqueData)),
629  (int) PageGetSpecialSize(uargs->page))));
630 
631  opaque = BTPageGetOpaque(uargs->page);
632 
633  if (P_ISMETA(opaque))
634  ereport(ERROR,
635  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
636  errmsg("block is a meta page")));
637 
638  if (P_ISLEAF(opaque) && opaque->btpo_level != 0)
639  ereport(ERROR,
640  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
641  errmsg("block is not a valid btree leaf page")));
642 
643  if (P_ISDELETED(opaque))
644  elog(NOTICE, "page is deleted");
645 
646  if (!P_ISDELETED(opaque))
647  fctx->max_calls = PageGetMaxOffsetNumber(uargs->page);
648  else
649  {
650  /* Don't interpret BTDeletedPageData as index tuples */
651  elog(NOTICE, "page from block is deleted");
652  fctx->max_calls = 0;
653  }
654  uargs->leafpage = P_ISLEAF(opaque);
655  uargs->rightmost = P_RIGHTMOST(opaque);
656 
657  /* Build a tuple descriptor for our result type */
658  if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
659  elog(ERROR, "return type must be a row type");
660  tupleDesc = BlessTupleDesc(tupleDesc);
661 
662  uargs->tupd = tupleDesc;
663 
664  fctx->user_fctx = uargs;
665 
666  MemoryContextSwitchTo(mctx);
667  }
668 
669  fctx = SRF_PERCALL_SETUP();
670  uargs = fctx->user_fctx;
671 
672  if (fctx->call_cntr < fctx->max_calls)
673  {
674  result = bt_page_print_tuples(uargs);
675  uargs->offset++;
676  SRF_RETURN_NEXT(fctx, result);
677  }
678 
679  SRF_RETURN_DONE(fctx);
680 }
681 
682 /* Number of output arguments (columns) for bt_metap() */
683 #define BT_METAP_COLS_V1_8 9
684 
685 /* ------------------------------------------------
686  * bt_metap()
687  *
688  * Get a btree's meta-page information
689  *
690  * Usage: SELECT * FROM bt_metap('t1_pkey')
691  * ------------------------------------------------
692  */
693 Datum
695 {
697  Datum result;
698  Relation rel;
699  RangeVar *relrv;
700  BTMetaPageData *metad;
701  TupleDesc tupleDesc;
702  int j;
703  char *values[9];
704  Buffer buffer;
705  Page page;
706  HeapTuple tuple;
707 
708  if (!superuser())
709  ereport(ERROR,
710  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
711  errmsg("must be superuser to use pageinspect functions")));
712 
714  rel = relation_openrv(relrv, AccessShareLock);
715 
716  if (!IS_INDEX(rel) || !IS_BTREE(rel))
717  ereport(ERROR,
718  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
719  errmsg("\"%s\" is not a %s index",
720  RelationGetRelationName(rel), "btree")));
721 
722  /*
723  * Reject attempts to read non-local temporary relations; we would be
724  * likely to get wrong data since we have no visibility into the owning
725  * session's local buffers.
726  */
727  if (RELATION_IS_OTHER_TEMP(rel))
728  ereport(ERROR,
729  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
730  errmsg("cannot access temporary tables of other sessions")));
731 
732  buffer = ReadBuffer(rel, 0);
733  LockBuffer(buffer, BUFFER_LOCK_SHARE);
734 
735  page = BufferGetPage(buffer);
736  metad = BTPageGetMeta(page);
737 
738  /* Build a tuple descriptor for our result type */
739  if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
740  elog(ERROR, "return type must be a row type");
741 
742  /*
743  * We need a kluge here to detect API versions prior to 1.8. Earlier
744  * versions incorrectly used int4 for certain columns.
745  *
746  * There is no way to reliably avoid the problems created by the old
747  * function definition at this point, so insist that the user update the
748  * extension.
749  */
750  if (tupleDesc->natts < BT_METAP_COLS_V1_8)
751  ereport(ERROR,
752  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
753  errmsg("function has wrong number of declared columns"),
754  errhint("To resolve the problem, update the \"pageinspect\" extension to the latest version.")));
755 
756  j = 0;
757  values[j++] = psprintf("%d", metad->btm_magic);
758  values[j++] = psprintf("%d", metad->btm_version);
759  values[j++] = psprintf(INT64_FORMAT, (int64) metad->btm_root);
760  values[j++] = psprintf(INT64_FORMAT, (int64) metad->btm_level);
761  values[j++] = psprintf(INT64_FORMAT, (int64) metad->btm_fastroot);
762  values[j++] = psprintf(INT64_FORMAT, (int64) metad->btm_fastlevel);
763 
764  /*
765  * Get values of extended metadata if available, use default values
766  * otherwise. Note that we rely on the assumption that btm_allequalimage
767  * is initialized to zero with indexes that were built on versions prior
768  * to Postgres 13 (just like _bt_metaversion()).
769  */
770  if (metad->btm_version >= BTREE_NOVAC_VERSION)
771  {
773  (int64) metad->btm_last_cleanup_num_delpages);
775  values[j++] = metad->btm_allequalimage ? "t" : "f";
776  }
777  else
778  {
779  values[j++] = "0";
780  values[j++] = "-1";
781  values[j++] = "f";
782  }
783 
785  values);
786 
787  result = HeapTupleGetDatum(tuple);
788 
789  UnlockReleaseBuffer(buffer);
791 
792  PG_RETURN_DATUM(result);
793 }
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3319
uint32 BlockNumber
Definition: block.h:31
#define InvalidBlockNumber
Definition: block.h:33
#define MaxBlockNumber
Definition: block.h:35
static Datum values[MAXATTR]
Definition: bootstrap.c:156
struct BTPageStat BTPageStat
Datum bt_page_stats(PG_FUNCTION_ARGS)
Definition: btreefuncs.c:281
Datum bt_page_items(PG_FUNCTION_ARGS)
Definition: btreefuncs.c:574
#define IS_BTREE(r)
Definition: btreefuncs.c:51
Datum bt_page_stats_1_9(PG_FUNCTION_ARGS)
Definition: btreefuncs.c:274
Datum bt_page_items_1_9(PG_FUNCTION_ARGS)
Definition: btreefuncs.c:567
Datum bt_page_items_bytea(PG_FUNCTION_ARGS)
Definition: btreefuncs.c:589
#define BT_METAP_COLS_V1_8
Definition: btreefuncs.c:683
static Datum bt_page_stats_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
Definition: btreefuncs.c:186
Datum bt_metap(PG_FUNCTION_ARGS)
Definition: btreefuncs.c:694
PG_FUNCTION_INFO_V1(bt_metap)
static Datum bt_page_print_tuples(struct user_args *uargs)
Definition: btreefuncs.c:306
#define IS_INDEX(r)
Definition: btreefuncs.c:50
static void GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat *stat)
Definition: btreefuncs.c:91
#define ItemPointerGetDatum(X)
Definition: btreefuncs.c:53
#define CHECK_RELATION_BLOCK_RANGE(rel, blkno)
Definition: btreefuncs.c:56
static Datum bt_page_items_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
Definition: btreefuncs.c:453
int Buffer
Definition: buf.h:23
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3938
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4156
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:702
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:97
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
Size PageGetFreeSpace(Page page)
Definition: bufpage.c:907
PageHeaderData * PageHeader
Definition: bufpage.h:166
Pointer Page
Definition: bufpage.h:78
#define PageGetSpecialSize(page)
Definition: bufpage.h:299
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:356
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:234
#define PageGetItem(page, itemId)
Definition: bufpage.h:339
#define SizeOfPageHeaderData
Definition: bufpage.h:215
#define PageIsNew(page)
Definition: bufpage.h:228
#define PageGetPageSize(page)
Definition: bufpage.h:267
#define CStringGetTextDatum(s)
Definition: builtins.h:85
unsigned short uint16
Definition: c.h:440
unsigned int uint32
Definition: c.h:441
#define MAXALIGN(LEN)
Definition: c.h:757
#define INT64_FORMAT
Definition: c.h:483
int errdetail(const char *fmt,...)
Definition: elog.c:1037
int errhint(const char *fmt,...)
Definition: elog.c:1151
int errcode(int sqlerrcode)
Definition: elog.c:693
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define DEBUG2
Definition: elog.h:23
#define ERROR
Definition: elog.h:33
#define elog(elevel,...)
Definition: elog.h:218
#define NOTICE
Definition: elog.h:29
#define ereport(elevel,...)
Definition: elog.h:143
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
Definition: execTuples.c:2071
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Definition: execTuples.c:2135
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:2086
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
#define PG_GETARG_UINT32(n)
Definition: fmgr.h:270
#define PG_RETURN_NULL()
Definition: fmgr.h:345
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283
#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
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:276
#define SRF_IS_FIRSTCALL()
Definition: funcapi.h:299
#define SRF_PERCALL_SETUP()
Definition: funcapi.h:303
@ TYPEFUNC_COMPOSITE
Definition: funcapi.h:149
#define HeapTupleGetDatum(tuple)
Definition: funcapi.h:220
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition: funcapi.h:305
#define SRF_FIRSTCALL_INIT()
Definition: funcapi.h:301
#define SRF_RETURN_DONE(_funcctx)
Definition: funcapi.h:323
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
int j
Definition: isn.c:74
int i
Definition: isn.c:73
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
#define ItemIdIsValid(itemId)
Definition: itemid.h:86
#define IndexTupleHasVarwidths(itup)
Definition: itup.h:72
#define IndexTupleHasNulls(itup)
Definition: itup.h:71
#define IndexInfoFindDataOffset(t_info)
Definition: itup.h:79
IndexTupleData * IndexTuple
Definition: itup.h:53
#define IndexTupleSize(itup)
Definition: itup.h:70
#define INDEX_SIZE_MASK
Definition: itup.h:65
Assert(fmt[strlen(fmt) - 1] !='\n')
#define AccessShareLock
Definition: lockdefs.h:36
void pfree(void *pointer)
Definition: mcxt.c:1175
void * palloc0(Size size)
Definition: mcxt.c:1099
void * palloc(Size size)
Definition: mcxt.c:1068
RangeVar * makeRangeVarFromNameList(List *names)
Definition: namespace.c:3108
#define P_HAS_FULLXID(opaque)
Definition: nbtree.h:229
static uint16 BTreeTupleGetNPosting(IndexTuple posting)
Definition: nbtree.h:513
static bool BTreeTupleIsPivot(IndexTuple itup)
Definition: nbtree.h:475
#define BTPageGetMeta(p)
Definition: nbtree.h:121
#define P_ISLEAF(opaque)
Definition: nbtree.h:221
static FullTransactionId BTPageGetDeleteXid(Page page)
Definition: nbtree.h:261
#define P_HIKEY
Definition: nbtree.h:369
#define P_ISMETA(opaque)
Definition: nbtree.h:224
#define BTPageGetOpaque(page)
Definition: nbtree.h:73
#define P_ISDELETED(opaque)
Definition: nbtree.h:223
static ItemPointer BTreeTupleGetPosting(IndexTuple posting)
Definition: nbtree.h:532
uint16 BTCycleId
Definition: nbtree.h:29
static uint32 BTreeTupleGetPostingOffset(IndexTuple posting)
Definition: nbtree.h:524
#define P_ISROOT(opaque)
Definition: nbtree.h:222
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:220
#define P_IGNORE(opaque)
Definition: nbtree.h:226
static bool BTreeTupleIsPosting(IndexTuple itup)
Definition: nbtree.h:487
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:152
static ItemPointer BTreeTupleGetHeapTID(IndexTuple itup)
Definition: nbtree.h:633
#define InvalidOffsetNumber
Definition: off.h:26
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
Page get_page_from_raw(bytea *raw_page)
Definition: rawpage.c:215
pageinspect_version
Definition: pageinspect.h:22
@ PAGEINSPECT_V1_9
Definition: pageinspect.h:24
@ PAGEINSPECT_V1_8
Definition: pageinspect.h:23
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
NameData relname
Definition: pg_class.h:38
#define sprintf
Definition: port.h:227
uintptr_t Datum
Definition: postgres.h:411
#define BoolGetDatum(X)
Definition: postgres.h:446
#define Int32GetDatum(X)
Definition: postgres.h:523
#define DatumGetInt16(X)
Definition: postgres.h:488
#define PointerGetDatum(X)
Definition: postgres.h:600
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
#define RelationGetRelationName(relation)
Definition: rel.h:523
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:643
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
Relation relation_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: relation.c:138
uint32 btm_last_cleanup_num_delpages
Definition: nbtree.h:114
uint32 btm_level
Definition: nbtree.h:108
float8 btm_last_cleanup_num_heap_tuples
Definition: nbtree.h:116
BlockNumber btm_fastroot
Definition: nbtree.h:109
uint32 btm_version
Definition: nbtree.h:106
uint32 btm_magic
Definition: nbtree.h:105
BlockNumber btm_root
Definition: nbtree.h:107
bool btm_allequalimage
Definition: nbtree.h:118
uint32 btm_fastlevel
Definition: nbtree.h:110
BlockNumber btpo_next
Definition: nbtree.h:65
BlockNumber btpo_prev
Definition: nbtree.h:64
uint16 btpo_flags
Definition: nbtree.h:67
uint32 btpo_level
Definition: nbtree.h:66
BTCycleId btpo_cycleid
Definition: nbtree.h:68
uint32 dead_items
Definition: btreefuncs.c:68
uint32 free_size
Definition: btreefuncs.c:71
uint32 btpo_level
Definition: btreefuncs.c:78
char type
Definition: btreefuncs.c:73
BlockNumber btpo_next
Definition: btreefuncs.c:77
uint32 page_size
Definition: btreefuncs.c:69
uint32 live_items
Definition: btreefuncs.c:67
uint32 max_avail
Definition: btreefuncs.c:70
uint32 blkno
Definition: btreefuncs.c:66
uint32 avg_item_size
Definition: btreefuncs.c:72
uint16 btpo_flags
Definition: btreefuncs.c:79
BTCycleId btpo_cycleid
Definition: btreefuncs.c:80
BlockNumber btpo_prev
Definition: btreefuncs.c:76
void * user_fctx
Definition: funcapi.h:82
uint64 max_calls
Definition: funcapi.h:74
uint64 call_cntr
Definition: funcapi.h:65
MemoryContext multi_call_memory_ctx
Definition: funcapi.h:101
ItemPointerData t_tid
Definition: itup.h:37
unsigned short t_info
Definition: itup.h:49
LocationIndex pd_special
Definition: bufpage.h:160
OffsetNumber offset
Definition: btreefuncs.c:293
TupleDesc tupd
Definition: btreefuncs.c:296
bool leafpage
Definition: btreefuncs.c:294
Page page
Definition: btreefuncs.c:292
bool rightmost
Definition: btreefuncs.c:295
Definition: c.h:622
bool superuser(void)
Definition: superuser.c:46
#define EpochFromFullTransactionId(x)
Definition: transam.h:47
#define XidFromFullTransactionId(x)
Definition: transam.h:48
List * textToQualifiedNameList(text *textval)
Definition: varlena.c:3657
#define stat
Definition: win32_port.h:283