PostgreSQL Source Code  git master
pg_visibility.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * pg_visibility.c
4  * display visibility map information and page-level visibility bits
5  *
6  * Copyright (c) 2016-2024, PostgreSQL Global Development Group
7  *
8  * contrib/pg_visibility/pg_visibility.c
9  *-------------------------------------------------------------------------
10  */
11 #include "postgres.h"
12 
13 #include "access/heapam.h"
14 #include "access/htup_details.h"
15 #include "access/visibilitymap.h"
16 #include "access/xloginsert.h"
17 #include "catalog/pg_type.h"
18 #include "catalog/storage_xlog.h"
19 #include "funcapi.h"
20 #include "miscadmin.h"
21 #include "storage/bufmgr.h"
22 #include "storage/procarray.h"
23 #include "storage/smgr.h"
24 #include "utils/rel.h"
25 #include "utils/snapmgr.h"
26 
28 
29 typedef struct vbits
30 {
35 
36 typedef struct corrupt_items
37 {
42 
51 
52 static TupleDesc pg_visibility_tupdesc(bool include_blkno, bool include_pd);
53 static vbits *collect_visibility_data(Oid relid, bool include_pd);
54 static corrupt_items *collect_corrupt_items(Oid relid, bool all_visible,
55  bool all_frozen);
56 static void record_corrupt_item(corrupt_items *items, ItemPointer tid);
57 static bool tuple_all_visible(HeapTuple tup, TransactionId OldestXmin,
58  Buffer buffer);
59 static void check_relation_relkind(Relation rel);
60 
61 /*
62  * Visibility map information for a single block of a relation.
63  *
64  * Note: the VM code will silently return zeroes for pages past the end
65  * of the map, so we allow probes up to MaxBlockNumber regardless of the
66  * actual relation size.
67  */
68 Datum
70 {
71  Oid relid = PG_GETARG_OID(0);
72  int64 blkno = PG_GETARG_INT64(1);
73  int32 mapbits;
74  Relation rel;
75  Buffer vmbuffer = InvalidBuffer;
76  TupleDesc tupdesc;
77  Datum values[2];
78  bool nulls[2] = {0};
79 
80  rel = relation_open(relid, AccessShareLock);
81 
82  /* Only some relkinds have a visibility map */
84 
85  if (blkno < 0 || blkno > MaxBlockNumber)
86  ereport(ERROR,
87  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
88  errmsg("invalid block number")));
89 
90  tupdesc = pg_visibility_tupdesc(false, false);
91 
92  mapbits = (int32) visibilitymap_get_status(rel, blkno, &vmbuffer);
93  if (vmbuffer != InvalidBuffer)
94  ReleaseBuffer(vmbuffer);
95  values[0] = BoolGetDatum((mapbits & VISIBILITYMAP_ALL_VISIBLE) != 0);
96  values[1] = BoolGetDatum((mapbits & VISIBILITYMAP_ALL_FROZEN) != 0);
97 
99 
101 }
102 
103 /*
104  * Visibility map information for a single block of a relation, plus the
105  * page-level information for the same block.
106  */
107 Datum
109 {
110  Oid relid = PG_GETARG_OID(0);
111  int64 blkno = PG_GETARG_INT64(1);
112  int32 mapbits;
113  Relation rel;
114  Buffer vmbuffer = InvalidBuffer;
115  Buffer buffer;
116  Page page;
117  TupleDesc tupdesc;
118  Datum values[3];
119  bool nulls[3] = {0};
120 
121  rel = relation_open(relid, AccessShareLock);
122 
123  /* Only some relkinds have a visibility map */
125 
126  if (blkno < 0 || blkno > MaxBlockNumber)
127  ereport(ERROR,
128  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
129  errmsg("invalid block number")));
130 
131  tupdesc = pg_visibility_tupdesc(false, true);
132 
133  mapbits = (int32) visibilitymap_get_status(rel, blkno, &vmbuffer);
134  if (vmbuffer != InvalidBuffer)
135  ReleaseBuffer(vmbuffer);
136  values[0] = BoolGetDatum((mapbits & VISIBILITYMAP_ALL_VISIBLE) != 0);
137  values[1] = BoolGetDatum((mapbits & VISIBILITYMAP_ALL_FROZEN) != 0);
138 
139  /* Here we have to explicitly check rel size ... */
140  if (blkno < RelationGetNumberOfBlocks(rel))
141  {
142  buffer = ReadBuffer(rel, blkno);
143  LockBuffer(buffer, BUFFER_LOCK_SHARE);
144 
145  page = BufferGetPage(buffer);
147 
148  UnlockReleaseBuffer(buffer);
149  }
150  else
151  {
152  /* As with the vismap, silently return 0 for pages past EOF */
153  values[2] = BoolGetDatum(false);
154  }
155 
157 
159 }
160 
161 /*
162  * Visibility map information for every block in a relation.
163  */
164 Datum
166 {
167  FuncCallContext *funcctx;
168  vbits *info;
169 
170  if (SRF_IS_FIRSTCALL())
171  {
172  Oid relid = PG_GETARG_OID(0);
173  MemoryContext oldcontext;
174 
175  funcctx = SRF_FIRSTCALL_INIT();
176  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
177  funcctx->tuple_desc = pg_visibility_tupdesc(true, false);
178  /* collect_visibility_data will verify the relkind */
179  funcctx->user_fctx = collect_visibility_data(relid, false);
180  MemoryContextSwitchTo(oldcontext);
181  }
182 
183  funcctx = SRF_PERCALL_SETUP();
184  info = (vbits *) funcctx->user_fctx;
185 
186  if (info->next < info->count)
187  {
188  Datum values[3];
189  bool nulls[3] = {0};
190  HeapTuple tuple;
191 
192  values[0] = Int64GetDatum(info->next);
193  values[1] = BoolGetDatum((info->bits[info->next] & (1 << 0)) != 0);
194  values[2] = BoolGetDatum((info->bits[info->next] & (1 << 1)) != 0);
195  info->next++;
196 
197  tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
198  SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
199  }
200 
201  SRF_RETURN_DONE(funcctx);
202 }
203 
204 /*
205  * Visibility map information for every block in a relation, plus the page
206  * level information for each block.
207  */
208 Datum
210 {
211  FuncCallContext *funcctx;
212  vbits *info;
213 
214  if (SRF_IS_FIRSTCALL())
215  {
216  Oid relid = PG_GETARG_OID(0);
217  MemoryContext oldcontext;
218 
219  funcctx = SRF_FIRSTCALL_INIT();
220  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
221  funcctx->tuple_desc = pg_visibility_tupdesc(true, true);
222  /* collect_visibility_data will verify the relkind */
223  funcctx->user_fctx = collect_visibility_data(relid, true);
224  MemoryContextSwitchTo(oldcontext);
225  }
226 
227  funcctx = SRF_PERCALL_SETUP();
228  info = (vbits *) funcctx->user_fctx;
229 
230  if (info->next < info->count)
231  {
232  Datum values[4];
233  bool nulls[4] = {0};
234  HeapTuple tuple;
235 
236  values[0] = Int64GetDatum(info->next);
237  values[1] = BoolGetDatum((info->bits[info->next] & (1 << 0)) != 0);
238  values[2] = BoolGetDatum((info->bits[info->next] & (1 << 1)) != 0);
239  values[3] = BoolGetDatum((info->bits[info->next] & (1 << 2)) != 0);
240  info->next++;
241 
242  tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
243  SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
244  }
245 
246  SRF_RETURN_DONE(funcctx);
247 }
248 
249 /*
250  * Count the number of all-visible and all-frozen pages in the visibility
251  * map for a particular relation.
252  */
253 Datum
255 {
256  Oid relid = PG_GETARG_OID(0);
257  Relation rel;
258  BlockNumber nblocks;
259  BlockNumber blkno;
260  Buffer vmbuffer = InvalidBuffer;
261  int64 all_visible = 0;
262  int64 all_frozen = 0;
263  TupleDesc tupdesc;
264  Datum values[2];
265  bool nulls[2] = {0};
266 
267  rel = relation_open(relid, AccessShareLock);
268 
269  /* Only some relkinds have a visibility map */
271 
272  nblocks = RelationGetNumberOfBlocks(rel);
273 
274  for (blkno = 0; blkno < nblocks; ++blkno)
275  {
276  int32 mapbits;
277 
278  /* Make sure we are interruptible. */
280 
281  /* Get map info. */
282  mapbits = (int32) visibilitymap_get_status(rel, blkno, &vmbuffer);
283  if ((mapbits & VISIBILITYMAP_ALL_VISIBLE) != 0)
284  ++all_visible;
285  if ((mapbits & VISIBILITYMAP_ALL_FROZEN) != 0)
286  ++all_frozen;
287  }
288 
289  /* Clean up. */
290  if (vmbuffer != InvalidBuffer)
291  ReleaseBuffer(vmbuffer);
293 
294  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
295  elog(ERROR, "return type must be a row type");
296 
297  values[0] = Int64GetDatum(all_visible);
298  values[1] = Int64GetDatum(all_frozen);
299 
301 }
302 
303 /*
304  * Return the TIDs of non-frozen tuples present in pages marked all-frozen
305  * in the visibility map. We hope no one will ever find any, but there could
306  * be bugs, database corruption, etc.
307  */
308 Datum
310 {
311  FuncCallContext *funcctx;
312  corrupt_items *items;
313 
314  if (SRF_IS_FIRSTCALL())
315  {
316  Oid relid = PG_GETARG_OID(0);
317  MemoryContext oldcontext;
318 
319  funcctx = SRF_FIRSTCALL_INIT();
320  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
321  /* collect_corrupt_items will verify the relkind */
322  funcctx->user_fctx = collect_corrupt_items(relid, false, true);
323  MemoryContextSwitchTo(oldcontext);
324  }
325 
326  funcctx = SRF_PERCALL_SETUP();
327  items = (corrupt_items *) funcctx->user_fctx;
328 
329  if (items->next < items->count)
330  SRF_RETURN_NEXT(funcctx, PointerGetDatum(&items->tids[items->next++]));
331 
332  SRF_RETURN_DONE(funcctx);
333 }
334 
335 /*
336  * Return the TIDs of not-all-visible tuples in pages marked all-visible
337  * in the visibility map. We hope no one will ever find any, but there could
338  * be bugs, database corruption, etc.
339  */
340 Datum
342 {
343  FuncCallContext *funcctx;
344  corrupt_items *items;
345 
346  if (SRF_IS_FIRSTCALL())
347  {
348  Oid relid = PG_GETARG_OID(0);
349  MemoryContext oldcontext;
350 
351  funcctx = SRF_FIRSTCALL_INIT();
352  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
353  /* collect_corrupt_items will verify the relkind */
354  funcctx->user_fctx = collect_corrupt_items(relid, true, false);
355  MemoryContextSwitchTo(oldcontext);
356  }
357 
358  funcctx = SRF_PERCALL_SETUP();
359  items = (corrupt_items *) funcctx->user_fctx;
360 
361  if (items->next < items->count)
362  SRF_RETURN_NEXT(funcctx, PointerGetDatum(&items->tids[items->next++]));
363 
364  SRF_RETURN_DONE(funcctx);
365 }
366 
367 /*
368  * Remove the visibility map fork for a relation. If there turn out to be
369  * any bugs in the visibility map code that require rebuilding the VM, this
370  * provides users with a way to do it that is cleaner than shutting down the
371  * server and removing files by hand.
372  *
373  * This is a cut-down version of RelationTruncate.
374  */
375 Datum
377 {
378  Oid relid = PG_GETARG_OID(0);
379  Relation rel;
380  ForkNumber fork;
381  BlockNumber block;
382 
383  rel = relation_open(relid, AccessExclusiveLock);
384 
385  /* Only some relkinds have a visibility map */
387 
388  /* Forcibly reset cached file size */
390 
391  block = visibilitymap_prepare_truncate(rel, 0);
392  if (BlockNumberIsValid(block))
393  {
394  fork = VISIBILITYMAP_FORKNUM;
395  smgrtruncate(RelationGetSmgr(rel), &fork, 1, &block);
396  }
397 
398  if (RelationNeedsWAL(rel))
399  {
400  xl_smgr_truncate xlrec;
401 
402  xlrec.blkno = 0;
403  xlrec.rlocator = rel->rd_locator;
404  xlrec.flags = SMGR_TRUNCATE_VM;
405 
406  XLogBeginInsert();
407  XLogRegisterData((char *) &xlrec, sizeof(xlrec));
408 
410  }
411 
412  /*
413  * Release the lock right away, not at commit time.
414  *
415  * It would be a problem to release the lock prior to commit if this
416  * truncate operation sends any transactional invalidation messages. Other
417  * backends would potentially be able to lock the relation without
418  * processing them in the window of time between when we release the lock
419  * here and when we sent the messages at our eventual commit. However,
420  * we're currently only sending a non-transactional smgr invalidation,
421  * which will have been posted to shared memory immediately from within
422  * smgr_truncate. Therefore, there should be no race here.
423  *
424  * The reason why it's desirable to release the lock early here is because
425  * of the possibility that someone will need to use this to blow away many
426  * visibility map forks at once. If we can't release the lock until
427  * commit time, the transaction doing this will accumulate
428  * AccessExclusiveLocks on all of those relations at the same time, which
429  * is undesirable. However, if this turns out to be unsafe we may have no
430  * choice...
431  */
433 
434  /* Nothing to return. */
435  PG_RETURN_VOID();
436 }
437 
438 /*
439  * Helper function to construct whichever TupleDesc we need for a particular
440  * call.
441  */
442 static TupleDesc
443 pg_visibility_tupdesc(bool include_blkno, bool include_pd)
444 {
445  TupleDesc tupdesc;
446  AttrNumber maxattr = 2;
447  AttrNumber a = 0;
448 
449  if (include_blkno)
450  ++maxattr;
451  if (include_pd)
452  ++maxattr;
453  tupdesc = CreateTemplateTupleDesc(maxattr);
454  if (include_blkno)
455  TupleDescInitEntry(tupdesc, ++a, "blkno", INT8OID, -1, 0);
456  TupleDescInitEntry(tupdesc, ++a, "all_visible", BOOLOID, -1, 0);
457  TupleDescInitEntry(tupdesc, ++a, "all_frozen", BOOLOID, -1, 0);
458  if (include_pd)
459  TupleDescInitEntry(tupdesc, ++a, "pd_all_visible", BOOLOID, -1, 0);
460  Assert(a == maxattr);
461 
462  return BlessTupleDesc(tupdesc);
463 }
464 
465 /*
466  * Collect visibility data about a relation.
467  *
468  * Checks relkind of relid and will throw an error if the relation does not
469  * have a VM.
470  */
471 static vbits *
472 collect_visibility_data(Oid relid, bool include_pd)
473 {
474  Relation rel;
475  BlockNumber nblocks;
476  vbits *info;
477  BlockNumber blkno;
478  Buffer vmbuffer = InvalidBuffer;
480 
481  rel = relation_open(relid, AccessShareLock);
482 
483  /* Only some relkinds have a visibility map */
485 
486  nblocks = RelationGetNumberOfBlocks(rel);
487  info = palloc0(offsetof(vbits, bits) + nblocks);
488  info->next = 0;
489  info->count = nblocks;
490 
491  for (blkno = 0; blkno < nblocks; ++blkno)
492  {
493  int32 mapbits;
494 
495  /* Make sure we are interruptible. */
497 
498  /* Get map info. */
499  mapbits = (int32) visibilitymap_get_status(rel, blkno, &vmbuffer);
500  if ((mapbits & VISIBILITYMAP_ALL_VISIBLE) != 0)
501  info->bits[blkno] |= (1 << 0);
502  if ((mapbits & VISIBILITYMAP_ALL_FROZEN) != 0)
503  info->bits[blkno] |= (1 << 1);
504 
505  /*
506  * Page-level data requires reading every block, so only get it if the
507  * caller needs it. Use a buffer access strategy, too, to prevent
508  * cache-trashing.
509  */
510  if (include_pd)
511  {
512  Buffer buffer;
513  Page page;
514 
515  buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
516  bstrategy);
517  LockBuffer(buffer, BUFFER_LOCK_SHARE);
518 
519  page = BufferGetPage(buffer);
520  if (PageIsAllVisible(page))
521  info->bits[blkno] |= (1 << 2);
522 
523  UnlockReleaseBuffer(buffer);
524  }
525  }
526 
527  /* Clean up. */
528  if (vmbuffer != InvalidBuffer)
529  ReleaseBuffer(vmbuffer);
531 
532  return info;
533 }
534 
535 /*
536  * Returns a list of items whose visibility map information does not match
537  * the status of the tuples on the page.
538  *
539  * If all_visible is passed as true, this will include all items which are
540  * on pages marked as all-visible in the visibility map but which do not
541  * seem to in fact be all-visible.
542  *
543  * If all_frozen is passed as true, this will include all items which are
544  * on pages marked as all-frozen but which do not seem to in fact be frozen.
545  *
546  * Checks relkind of relid and will throw an error if the relation does not
547  * have a VM.
548  */
549 static corrupt_items *
550 collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
551 {
552  Relation rel;
553  BlockNumber nblocks;
554  corrupt_items *items;
555  BlockNumber blkno;
556  Buffer vmbuffer = InvalidBuffer;
558  TransactionId OldestXmin = InvalidTransactionId;
559 
560  rel = relation_open(relid, AccessShareLock);
561 
562  /* Only some relkinds have a visibility map */
564 
565  if (all_visible)
566  OldestXmin = GetOldestNonRemovableTransactionId(rel);
567 
568  nblocks = RelationGetNumberOfBlocks(rel);
569 
570  /*
571  * Guess an initial array size. We don't expect many corrupted tuples, so
572  * start with a small array. This function uses the "next" field to track
573  * the next offset where we can store an item (which is the same thing as
574  * the number of items found so far) and the "count" field to track the
575  * number of entries allocated. We'll repurpose these fields before
576  * returning.
577  */
578  items = palloc0(sizeof(corrupt_items));
579  items->next = 0;
580  items->count = 64;
581  items->tids = palloc(items->count * sizeof(ItemPointerData));
582 
583  /* Loop over every block in the relation. */
584  for (blkno = 0; blkno < nblocks; ++blkno)
585  {
586  bool check_frozen = false;
587  bool check_visible = false;
588  Buffer buffer;
589  Page page;
590  OffsetNumber offnum,
591  maxoff;
592 
593  /* Make sure we are interruptible. */
595 
596  /* Use the visibility map to decide whether to check this page. */
597  if (all_frozen && VM_ALL_FROZEN(rel, blkno, &vmbuffer))
598  check_frozen = true;
599  if (all_visible && VM_ALL_VISIBLE(rel, blkno, &vmbuffer))
600  check_visible = true;
601  if (!check_visible && !check_frozen)
602  continue;
603 
604  /* Read and lock the page. */
605  buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
606  bstrategy);
607  LockBuffer(buffer, BUFFER_LOCK_SHARE);
608 
609  page = BufferGetPage(buffer);
610  maxoff = PageGetMaxOffsetNumber(page);
611 
612  /*
613  * The visibility map bits might have changed while we were acquiring
614  * the page lock. Recheck to avoid returning spurious results.
615  */
616  if (check_frozen && !VM_ALL_FROZEN(rel, blkno, &vmbuffer))
617  check_frozen = false;
618  if (check_visible && !VM_ALL_VISIBLE(rel, blkno, &vmbuffer))
619  check_visible = false;
620  if (!check_visible && !check_frozen)
621  {
622  UnlockReleaseBuffer(buffer);
623  continue;
624  }
625 
626  /* Iterate over each tuple on the page. */
627  for (offnum = FirstOffsetNumber;
628  offnum <= maxoff;
629  offnum = OffsetNumberNext(offnum))
630  {
631  HeapTupleData tuple;
632  ItemId itemid;
633 
634  itemid = PageGetItemId(page, offnum);
635 
636  /* Unused or redirect line pointers are of no interest. */
637  if (!ItemIdIsUsed(itemid) || ItemIdIsRedirected(itemid))
638  continue;
639 
640  /* Dead line pointers are neither all-visible nor frozen. */
641  if (ItemIdIsDead(itemid))
642  {
643  ItemPointerSet(&(tuple.t_self), blkno, offnum);
644  record_corrupt_item(items, &tuple.t_self);
645  continue;
646  }
647 
648  /* Initialize a HeapTupleData structure for checks below. */
649  ItemPointerSet(&(tuple.t_self), blkno, offnum);
650  tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
651  tuple.t_len = ItemIdGetLength(itemid);
652  tuple.t_tableOid = relid;
653 
654  /*
655  * If we're checking whether the page is all-visible, we expect
656  * the tuple to be all-visible.
657  */
658  if (check_visible &&
659  !tuple_all_visible(&tuple, OldestXmin, buffer))
660  {
661  TransactionId RecomputedOldestXmin;
662 
663  /*
664  * Time has passed since we computed OldestXmin, so it's
665  * possible that this tuple is all-visible in reality even
666  * though it doesn't appear so based on our
667  * previously-computed value. Let's compute a new value so we
668  * can be certain whether there is a problem.
669  *
670  * From a concurrency point of view, it sort of sucks to
671  * retake ProcArrayLock here while we're holding the buffer
672  * exclusively locked, but it should be safe against
673  * deadlocks, because surely
674  * GetOldestNonRemovableTransactionId() should never take a
675  * buffer lock. And this shouldn't happen often, so it's worth
676  * being careful so as to avoid false positives.
677  */
678  RecomputedOldestXmin = GetOldestNonRemovableTransactionId(rel);
679 
680  if (!TransactionIdPrecedes(OldestXmin, RecomputedOldestXmin))
681  record_corrupt_item(items, &tuple.t_self);
682  else
683  {
684  OldestXmin = RecomputedOldestXmin;
685  if (!tuple_all_visible(&tuple, OldestXmin, buffer))
686  record_corrupt_item(items, &tuple.t_self);
687  }
688  }
689 
690  /*
691  * If we're checking whether the page is all-frozen, we expect the
692  * tuple to be in a state where it will never need freezing.
693  */
694  if (check_frozen)
695  {
697  record_corrupt_item(items, &tuple.t_self);
698  }
699  }
700 
701  UnlockReleaseBuffer(buffer);
702  }
703 
704  /* Clean up. */
705  if (vmbuffer != InvalidBuffer)
706  ReleaseBuffer(vmbuffer);
708 
709  /*
710  * Before returning, repurpose the fields to match caller's expectations.
711  * next is now the next item that should be read (rather than written) and
712  * count is now the number of items we wrote (rather than the number we
713  * allocated).
714  */
715  items->count = items->next;
716  items->next = 0;
717 
718  return items;
719 }
720 
721 /*
722  * Remember one corrupt item.
723  */
724 static void
726 {
727  /* enlarge output array if needed. */
728  if (items->next >= items->count)
729  {
730  items->count *= 2;
731  items->tids = repalloc(items->tids,
732  items->count * sizeof(ItemPointerData));
733  }
734  /* and add the new item */
735  items->tids[items->next++] = *tid;
736 }
737 
738 /*
739  * Check whether a tuple is all-visible relative to a given OldestXmin value.
740  * The buffer should contain the tuple and should be locked and pinned.
741  */
742 static bool
744 {
746  TransactionId xmin;
747 
748  state = HeapTupleSatisfiesVacuum(tup, OldestXmin, buffer);
749  if (state != HEAPTUPLE_LIVE)
750  return false; /* all-visible implies live */
751 
752  /*
753  * Neither lazy_scan_heap nor heap_page_is_all_visible will mark a page
754  * all-visible unless every tuple is hinted committed. However, those hint
755  * bits could be lost after a crash, so we can't be certain that they'll
756  * be set here. So just check the xmin.
757  */
758 
759  xmin = HeapTupleHeaderGetXmin(tup->t_data);
760  if (!TransactionIdPrecedes(xmin, OldestXmin))
761  return false; /* xmin not old enough for all to see */
762 
763  return true;
764 }
765 
766 /*
767  * check_relation_relkind - convenience routine to check that relation
768  * is of the relkind supported by the callers
769  */
770 static void
772 {
773  if (!RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind))
774  ereport(ERROR,
775  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
776  errmsg("relation \"%s\" is of wrong relation kind",
778  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
779 }
int16 AttrNumber
Definition: attnum.h:21
uint32 BlockNumber
Definition: block.h:31
#define InvalidBlockNumber
Definition: block.h:33
static bool BlockNumberIsValid(BlockNumber blockNumber)
Definition: block.h:71
#define MaxBlockNumber
Definition: block.h:35
static Datum values[MAXATTR]
Definition: bootstrap.c:152
int Buffer
Definition: buf.h:23
#define InvalidBuffer
Definition: buf.h:25
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4560
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4577
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4795
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:781
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:734
@ BAS_BULKREAD
Definition: bufmgr.h:35
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:158
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:229
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:350
@ RBM_NORMAL
Definition: bufmgr.h:44
Pointer Page
Definition: bufpage.h:78
static Item PageGetItem(Page page, ItemId itemId)
Definition: bufpage.h:351
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:240
static bool PageIsAllVisible(Page page)
Definition: bufpage.h:426
static OffsetNumber PageGetMaxOffsetNumber(Page page)
Definition: bufpage.h:369
signed int int32
Definition: c.h:481
#define FLEXIBLE_ARRAY_MEMBER
Definition: c.h:385
unsigned char uint8
Definition: c.h:491
uint32 TransactionId
Definition: c.h:639
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
Definition: execTuples.c:2070
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1807
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype)
Definition: freelist.c:541
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
bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple)
Definition: heapam.c:7335
HTSV_Result
Definition: heapam.h:95
@ HEAPTUPLE_LIVE
Definition: heapam.h:97
HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:309
int a
Definition: isn.c:69
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
#define ItemIdIsRedirected(itemId)
Definition: itemid.h:106
static void ItemPointerSet(ItemPointerData *pointer, BlockNumber blockNumber, OffsetNumber offNum)
Definition: itemptr.h:135
Assert(fmt[strlen(fmt) - 1] !='\n')
#define AccessExclusiveLock
Definition: lockdefs.h:43
#define AccessShareLock
Definition: lockdefs.h:36
void * palloc0(Size size)
Definition: mcxt.c:1227
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1421
void * palloc(Size size)
Definition: mcxt.c:1197
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
static corrupt_items * collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
Datum pg_visibility_map_summary(PG_FUNCTION_ARGS)
PG_MODULE_MAGIC
Definition: pg_visibility.c:27
Datum pg_visibility_rel(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(pg_visibility_map)
struct corrupt_items corrupt_items
static TupleDesc pg_visibility_tupdesc(bool include_blkno, bool include_pd)
static void check_relation_relkind(Relation rel)
static void record_corrupt_item(corrupt_items *items, ItemPointer tid)
Datum pg_visibility_map(PG_FUNCTION_ARGS)
Definition: pg_visibility.c:69
struct vbits vbits
static vbits * collect_visibility_data(Oid relid, bool include_pd)
Datum pg_visibility_map_rel(PG_FUNCTION_ARGS)
Datum pg_check_visible(PG_FUNCTION_ARGS)
Datum pg_check_frozen(PG_FUNCTION_ARGS)
Datum pg_visibility(PG_FUNCTION_ARGS)
static bool tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
Datum pg_truncate_visibility_map(PG_FUNCTION_ARGS)
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
unsigned int Oid
Definition: postgres_ext.h:31
TransactionId GetOldestNonRemovableTransactionId(Relation rel)
Definition: procarray.c:1993
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:566
#define RelationGetRelationName(relation)
Definition: rel.h:538
#define RelationNeedsWAL(relation)
Definition: rel.h:627
ForkNumber
Definition: relpath.h:48
@ VISIBILITYMAP_FORKNUM
Definition: relpath.h:52
@ MAIN_FORKNUM
Definition: relpath.h:50
void smgrtruncate(SMgrRelation reln, ForkNumber *forknum, int nforks, BlockNumber *nblocks)
Definition: smgr.c:704
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:47
#define SMGR_TRUNCATE_VM
Definition: storage_xlog.h:41
#define XLOG_SMGR_TRUNCATE
Definition: storage_xlog.h:31
void * user_fctx
Definition: funcapi.h:82
MemoryContext multi_call_memory_ctx
Definition: funcapi.h:101
TupleDesc tuple_desc
Definition: funcapi.h:112
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
HeapTupleHeader t_data
Definition: htup.h:68
Oid t_tableOid
Definition: htup.h:66
RelFileLocator rd_locator
Definition: rel.h:57
Form_pg_class rd_rel
Definition: rel.h:111
BlockNumber smgr_cached_nblocks[MAX_FORKNUM+1]
Definition: smgr.h:46
ItemPointer tids
Definition: pg_visibility.c:40
BlockNumber count
Definition: pg_visibility.c:39
BlockNumber next
Definition: pg_visibility.c:38
Definition: regguts.h:323
BlockNumber next
Definition: pg_visibility.c:31
uint8 bits[FLEXIBLE_ARRAY_MEMBER]
Definition: pg_visibility.c:33
BlockNumber count
Definition: pg_visibility.c:32
RelFileLocator rlocator
Definition: storage_xlog.h:49
BlockNumber blkno
Definition: storage_xlog.h:48
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:280
#define InvalidTransactionId
Definition: transam.h:31
TupleDesc CreateTemplateTupleDesc(int natts)
Definition: tupdesc.c:67
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:603
uint8 visibilitymap_get_status(Relation rel, BlockNumber heapBlk, Buffer *vmbuf)
BlockNumber visibilitymap_prepare_truncate(Relation rel, BlockNumber nheapblocks)
#define VM_ALL_VISIBLE(r, b, v)
Definition: visibilitymap.h:24
#define VM_ALL_FROZEN(r, b, v)
Definition: visibilitymap.h:26
#define VISIBILITYMAP_ALL_FROZEN
#define VISIBILITYMAP_ALL_VISIBLE
void XLogRegisterData(char *data, uint32 len)
Definition: xloginsert.c:364
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:474
void XLogBeginInsert(void)
Definition: xloginsert.c:149
#define XLR_SPECIAL_REL_UPDATE
Definition: xlogrecord.h:82