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