PostgreSQL Source Code git master
Loading...
Searching...
No Matches
heapam_handler.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * heapam_handler.c
4 * heap table access method code
5 *
6 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/access/heap/heapam_handler.c
12 *
13 *
14 * NOTES
15 * This files wires up the lower level heapam.c et al routines with the
16 * tableam abstraction.
17 *
18 *-------------------------------------------------------------------------
19 */
20#include "postgres.h"
21
22#include "access/genam.h"
23#include "access/heapam.h"
24#include "access/heaptoast.h"
25#include "access/multixact.h"
26#include "access/rewriteheap.h"
27#include "access/syncscan.h"
28#include "access/tableam.h"
29#include "access/tsmapi.h"
31#include "access/xact.h"
32#include "catalog/catalog.h"
33#include "catalog/index.h"
34#include "catalog/storage.h"
36#include "commands/progress.h"
37#include "executor/executor.h"
38#include "miscadmin.h"
39#include "pgstat.h"
40#include "storage/bufmgr.h"
41#include "storage/bufpage.h"
42#include "storage/lmgr.h"
43#include "storage/predicate.h"
44#include "storage/procarray.h"
45#include "storage/smgr.h"
46#include "utils/builtins.h"
47#include "utils/rel.h"
48
49static void reform_and_rewrite_tuple(HeapTuple tuple,
51 Datum *values, bool *isnull, RewriteState rwstate);
52
53static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer,
54 HeapTuple tuple,
56
58
60 bool *recheck,
61 uint64 *lossy_pages, uint64 *exact_pages);
62
63
64/* ------------------------------------------------------------------------
65 * Slot related callbacks for heap AM
66 * ------------------------------------------------------------------------
67 */
68
69static const TupleTableSlotOps *
74
75
76/* ------------------------------------------------------------------------
77 * Index Scan Callbacks for heap AM
78 * ------------------------------------------------------------------------
79 */
80
83{
85
86 hscan->xs_base.rel = rel;
87 hscan->xs_cbuf = InvalidBuffer;
88
89 return &hscan->xs_base;
90}
91
92static void
94{
96
97 if (BufferIsValid(hscan->xs_cbuf))
98 {
99 ReleaseBuffer(hscan->xs_cbuf);
100 hscan->xs_cbuf = InvalidBuffer;
101 }
102}
103
104static void
113
114static bool
116 ItemPointer tid,
117 Snapshot snapshot,
118 TupleTableSlot *slot,
119 bool *call_again, bool *all_dead)
120{
123 bool got_heap_tuple;
124
126
127 /* We can skip the buffer-switching logic if we're in mid-HOT chain. */
128 if (!*call_again)
129 {
130 /* Switch to correct buffer if we don't have it already */
131 Buffer prev_buf = hscan->xs_cbuf;
132
133 hscan->xs_cbuf = ReleaseAndReadBuffer(hscan->xs_cbuf,
134 hscan->xs_base.rel,
136
137 /*
138 * Prune page, but only if we weren't already on this page
139 */
140 if (prev_buf != hscan->xs_cbuf)
141 heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf);
142 }
143
144 /* Obtain share-lock on the buffer so we can examine visibility */
147 hscan->xs_base.rel,
148 hscan->xs_cbuf,
149 snapshot,
150 &bslot->base.tupdata,
151 all_dead,
152 !*call_again);
153 bslot->base.tupdata.t_self = *tid;
155
156 if (got_heap_tuple)
157 {
158 /*
159 * Only in a non-MVCC snapshot can more than one member of the HOT
160 * chain be visible.
161 */
162 *call_again = !IsMVCCSnapshot(snapshot);
163
164 slot->tts_tableOid = RelationGetRelid(scan->rel);
165 ExecStoreBufferHeapTuple(&bslot->base.tupdata, slot, hscan->xs_cbuf);
166 }
167 else
168 {
169 /* We've reached the end of the HOT chain. */
170 *call_again = false;
171 }
172
173 return got_heap_tuple;
174}
175
176
177/* ------------------------------------------------------------------------
178 * Callbacks for non-modifying operations on individual tuples for heap AM
179 * ------------------------------------------------------------------------
180 */
181
182static bool
184 ItemPointer tid,
185 Snapshot snapshot,
186 TupleTableSlot *slot)
187{
189 Buffer buffer;
190
192
193 bslot->base.tupdata.t_self = *tid;
194 if (heap_fetch(relation, snapshot, &bslot->base.tupdata, &buffer, false))
195 {
196 /* store in slot, transferring existing pin */
197 ExecStorePinnedBufferHeapTuple(&bslot->base.tupdata, slot, buffer);
198 slot->tts_tableOid = RelationGetRelid(relation);
199
200 return true;
201 }
202
203 return false;
204}
205
206static bool
208{
210
211 return ItemPointerIsValid(tid) &&
212 ItemPointerGetBlockNumber(tid) < hscan->rs_nblocks;
213}
214
215static bool
217 Snapshot snapshot)
218{
220 bool res;
221
223 Assert(BufferIsValid(bslot->buffer));
224
225 /*
226 * We need buffer pin and lock to call HeapTupleSatisfiesVisibility.
227 * Caller should be holding pin, but not lock.
228 */
230 res = HeapTupleSatisfiesVisibility(bslot->base.tuple, snapshot,
231 bslot->buffer);
233
234 return res;
235}
236
237
238/* ----------------------------------------------------------------------------
239 * Functions for manipulations of physical tuples for heap AM.
240 * ----------------------------------------------------------------------------
241 */
242
243static void
245 int options, BulkInsertState bistate)
246{
247 bool shouldFree = true;
248 HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
249
250 /* Update the tuple with table oid */
251 slot->tts_tableOid = RelationGetRelid(relation);
252 tuple->t_tableOid = slot->tts_tableOid;
253
254 /* Perform the insertion, and copy the resulting ItemPointer */
255 heap_insert(relation, tuple, cid, options, bistate);
256 ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
257
258 if (shouldFree)
259 pfree(tuple);
260}
261
262static void
264 CommandId cid, int options,
266{
267 bool shouldFree = true;
268 HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
269
270 /* Update the tuple with table oid */
271 slot->tts_tableOid = RelationGetRelid(relation);
272 tuple->t_tableOid = slot->tts_tableOid;
273
276
277 /* Perform the insertion, and copy the resulting ItemPointer */
278 heap_insert(relation, tuple, cid, options, bistate);
279 ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
280
281 if (shouldFree)
282 pfree(tuple);
283}
284
285static void
288{
289 bool shouldFree = true;
290 HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
291
292 /* adjust the tuple's state accordingly */
293 if (succeeded)
294 heap_finish_speculative(relation, &slot->tts_tid);
295 else
296 heap_abort_speculative(relation, &slot->tts_tid);
297
298 if (shouldFree)
299 pfree(tuple);
300}
301
302static TM_Result
304 Snapshot snapshot, Snapshot crosscheck, bool wait,
305 TM_FailureData *tmfd, bool changingPart)
306{
307 /*
308 * Currently Deleting of index tuples are handled at vacuum, in case if
309 * the storage itself is cleaning the dead tuples by itself, it is the
310 * time to call the index tuple deletion also.
311 */
312 return heap_delete(relation, tid, cid, crosscheck, wait, tmfd, changingPart);
313}
314
315
316static TM_Result
319 bool wait, TM_FailureData *tmfd,
321{
322 bool shouldFree = true;
323 HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
324 TM_Result result;
325
326 /* Update the tuple with table oid */
327 slot->tts_tableOid = RelationGetRelid(relation);
328 tuple->t_tableOid = slot->tts_tableOid;
329
330 result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
331 tmfd, lockmode, update_indexes);
332 ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
333
334 /*
335 * Decide whether new index entries are needed for the tuple
336 *
337 * Note: heap_update returns the tid (location) of the new tuple in the
338 * t_self field.
339 *
340 * If the update is not HOT, we must update all indexes. If the update is
341 * HOT, it could be that we updated summarized columns, so we either
342 * update only summarized indexes, or none at all.
343 */
344 if (result != TM_Ok)
345 {
348 }
349 else if (!HeapTupleIsHeapOnly(tuple))
351 else
353 (*update_indexes == TU_None));
354
355 if (shouldFree)
356 pfree(tuple);
357
358 return result;
359}
360
361static TM_Result
365 TM_FailureData *tmfd)
366{
368 TM_Result result;
369 Buffer buffer;
370 HeapTuple tuple = &bslot->base.tupdata;
371 bool follow_updates;
372
374 tmfd->traversed = false;
375
377
379 tuple->t_self = *tid;
380 result = heap_lock_tuple(relation, tuple, cid, mode, wait_policy,
381 follow_updates, &buffer, tmfd);
382
383 if (result == TM_Updated &&
385 {
386 /* Should not encounter speculative tuple on recheck */
388
389 ReleaseBuffer(buffer);
390
391 if (!ItemPointerEquals(&tmfd->ctid, &tuple->t_self))
392 {
395
396 /* it was updated, so look at the updated version */
397 *tid = tmfd->ctid;
398 /* updated row should have xmin matching this xmax */
399 priorXmax = tmfd->xmax;
400
401 /* signal that a tuple later in the chain is getting locked */
402 tmfd->traversed = true;
403
404 /*
405 * fetch target tuple
406 *
407 * Loop here to deal with updated or busy tuples
408 */
410 for (;;)
411 {
415 errmsg("tuple to be locked was already moved to another partition due to concurrent update")));
416
417 tuple->t_self = *tid;
418 if (heap_fetch(relation, &SnapshotDirty, tuple, &buffer, true))
419 {
420 /*
421 * If xmin isn't what we're expecting, the slot must have
422 * been recycled and reused for an unrelated tuple. This
423 * implies that the latest version of the row was deleted,
424 * so we need do nothing. (Should be safe to examine xmin
425 * without getting buffer's content lock. We assume
426 * reading a TransactionId to be atomic, and Xmin never
427 * changes in an existing tuple, except to invalid or
428 * frozen, and neither of those can match priorXmax.)
429 */
431 priorXmax))
432 {
433 ReleaseBuffer(buffer);
434 return TM_Deleted;
435 }
436
437 /* otherwise xmin should not be dirty... */
441 errmsg_internal("t_xmin %u is uncommitted in tuple (%u,%u) to be updated in table \"%s\"",
442 SnapshotDirty.xmin,
445 RelationGetRelationName(relation))));
446
447 /*
448 * If tuple is being updated by other transaction then we
449 * have to wait for its commit/abort, or die trying.
450 */
452 {
453 ReleaseBuffer(buffer);
454 switch (wait_policy)
455 {
456 case LockWaitBlock:
458 relation, &tuple->t_self,
460 break;
461 case LockWaitSkip:
463 /* skip instead of waiting */
464 return TM_WouldBlock;
465 break;
466 case LockWaitError:
470 errmsg("could not obtain lock on row in relation \"%s\"",
471 RelationGetRelationName(relation))));
472 break;
473 }
474 continue; /* loop back to repeat heap_fetch */
475 }
476
477 /*
478 * If tuple was inserted by our own transaction, we have
479 * to check cmin against cid: cmin >= current CID means
480 * our command cannot see the tuple, so we should ignore
481 * it. Otherwise heap_lock_tuple() will throw an error,
482 * and so would any later attempt to update or delete the
483 * tuple. (We need not check cmax because
484 * HeapTupleSatisfiesDirty will consider a tuple deleted
485 * by our transaction dead, regardless of cmax.) We just
486 * checked that priorXmax == xmin, so we can test that
487 * variable instead of doing HeapTupleHeaderGetXmin again.
488 */
491 {
492 tmfd->xmax = priorXmax;
493
494 /*
495 * Cmin is the problematic value, so store that. See
496 * above.
497 */
498 tmfd->cmax = HeapTupleHeaderGetCmin(tuple->t_data);
499 ReleaseBuffer(buffer);
500 return TM_SelfModified;
501 }
502
503 /*
504 * This is a live tuple, so try to lock it again.
505 */
506 ReleaseBuffer(buffer);
507 goto tuple_lock_retry;
508 }
509
510 /*
511 * If the referenced slot was actually empty, the latest
512 * version of the row must have been deleted, so we need do
513 * nothing.
514 */
515 if (tuple->t_data == NULL)
516 {
517 Assert(!BufferIsValid(buffer));
518 return TM_Deleted;
519 }
520
521 /*
522 * As above, if xmin isn't what we're expecting, do nothing.
523 */
525 priorXmax))
526 {
527 ReleaseBuffer(buffer);
528 return TM_Deleted;
529 }
530
531 /*
532 * If we get here, the tuple was found but failed
533 * SnapshotDirty. Assuming the xmin is either a committed xact
534 * or our own xact (as it certainly should be if we're trying
535 * to modify the tuple), this must mean that the row was
536 * updated or deleted by either a committed xact or our own
537 * xact. If it was deleted, we can ignore it; if it was
538 * updated then chain up to the next version and repeat the
539 * whole process.
540 *
541 * As above, it should be safe to examine xmax and t_ctid
542 * without the buffer content lock, because they can't be
543 * changing. We'd better hold a buffer pin though.
544 */
545 if (ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid))
546 {
547 /* deleted, so forget about it */
548 ReleaseBuffer(buffer);
549 return TM_Deleted;
550 }
551
552 /* updated, so look at the updated row */
553 *tid = tuple->t_data->t_ctid;
554 /* updated row should have xmin matching this xmax */
556 ReleaseBuffer(buffer);
557 /* loop back to fetch next in chain */
558 }
559 }
560 else
561 {
562 /* tuple was deleted, so give up */
563 return TM_Deleted;
564 }
565 }
566
567 slot->tts_tableOid = RelationGetRelid(relation);
568 tuple->t_tableOid = slot->tts_tableOid;
569
570 /* store in slot, transferring existing pin */
571 ExecStorePinnedBufferHeapTuple(tuple, slot, buffer);
572
573 return result;
574}
575
576
577/* ------------------------------------------------------------------------
578 * DDL related callbacks for heap AM.
579 * ------------------------------------------------------------------------
580 */
581
582static void
585 char persistence,
588{
589 SMgrRelation srel;
590
591 /*
592 * Initialize to the minimum XID that could put tuples in the table. We
593 * know that no xacts older than RecentXmin are still running, so that
594 * will do.
595 */
597
598 /*
599 * Similarly, initialize the minimum Multixact to the first value that
600 * could possibly be stored in tuples in the table. Running transactions
601 * could reuse values from their local cache, so we are careful to
602 * consider all currently running multis.
603 *
604 * XXX this could be refined further, but is it worth the hassle?
605 */
607
608 srel = RelationCreateStorage(*newrlocator, persistence, true);
609
610 /*
611 * If required, set up an init fork for an unlogged table so that it can
612 * be correctly reinitialized on restart.
613 */
614 if (persistence == RELPERSISTENCE_UNLOGGED)
615 {
616 Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
617 rel->rd_rel->relkind == RELKIND_TOASTVALUE);
618 smgrcreate(srel, INIT_FORKNUM, false);
620 }
621
622 smgrclose(srel);
623}
624
625static void
630
631static void
633{
635
636 /*
637 * Since we copy the file directly without looking at the shared buffers,
638 * we'd better first flush out any pages of the source relation that are
639 * in shared buffers. We assume no new changes will be made while we are
640 * holding exclusive lock on the rel.
641 */
643
644 /*
645 * Create and copy all forks of the relation, and schedule unlinking of
646 * old physical files.
647 *
648 * NOTE: any conflict in relfilenumber value will be caught in
649 * RelationCreateStorage().
650 */
651 dstrel = RelationCreateStorage(*newrlocator, rel->rd_rel->relpersistence, true);
652
653 /* copy main fork */
655 rel->rd_rel->relpersistence);
656
657 /* copy those extra forks that exist */
658 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
659 forkNum <= MAX_FORKNUM; forkNum++)
660 {
661 if (smgrexists(RelationGetSmgr(rel), forkNum))
662 {
663 smgrcreate(dstrel, forkNum, false);
664
665 /*
666 * WAL log creation if the relation is persistent, or this is the
667 * init fork of an unlogged relation.
668 */
669 if (RelationIsPermanent(rel) ||
670 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
671 forkNum == INIT_FORKNUM))
672 log_smgrcreate(newrlocator, forkNum);
674 rel->rd_rel->relpersistence);
675 }
676 }
677
678
679 /* drop old relation, and close new one */
682}
683
684static void
687 TransactionId OldestXmin,
690 double *num_tuples,
691 double *tups_vacuumed,
692 double *tups_recently_dead)
693{
699 Tuplesortstate *tuplesort;
702 TupleTableSlot *slot;
703 int natts;
704 Datum *values;
705 bool *isnull;
708
709 /* Remember if it's a system catalog */
711
712 /*
713 * Valid smgr_targblock implies something already wrote to the relation.
714 * This may be harmless, but this function hasn't planned for it.
715 */
717
718 /* Preallocate values/isnull arrays */
719 natts = newTupDesc->natts;
720 values = palloc_array(Datum, natts);
721 isnull = palloc_array(bool, natts);
722
723 /* Initialize the rewrite operation */
725 *multi_cutoff);
726
727
728 /* Set up sorting if wanted */
729 if (use_sort)
733 else
734 tuplesort = NULL;
735
736 /*
737 * Prepare to scan the OldHeap. To ensure we see recently-dead tuples
738 * that still need to be copied, we scan with SnapshotAny and use
739 * HeapTupleSatisfiesVacuum for the visibility test.
740 */
741 if (OldIndex != NULL && !use_sort)
742 {
743 const int ci_index[] = {
746 };
747 int64 ci_val[2];
748
749 /* Set phase and OIDOldIndex to columns */
753
754 tableScan = NULL;
755 heapScan = NULL;
758 }
759 else
760 {
761 /* In scan-and-sort mode and also VACUUM FULL, set phase */
764
767 indexScan = NULL;
768
769 /* Set total heap blocks */
771 heapScan->rs_nblocks);
772 }
773
776
777 /*
778 * Scan through the OldHeap, either in OldIndex order or sequentially;
779 * copy each tuple into the NewHeap, or transiently to the tuplesort
780 * module. Note that we don't bother sorting dead tuples (they won't get
781 * to the new table anyway).
782 */
783 for (;;)
784 {
785 HeapTuple tuple;
786 Buffer buf;
787 bool isdead;
788
790
791 if (indexScan != NULL)
792 {
794 break;
795
796 /* Since we used no scan keys, should never need to recheck */
797 if (indexScan->xs_recheck)
798 elog(ERROR, "CLUSTER does not support lossy index conditions");
799 }
800 else
801 {
803 {
804 /*
805 * If the last pages of the scan were empty, we would go to
806 * the next phase while heap_blks_scanned != heap_blks_total.
807 * Instead, to ensure that heap_blks_scanned is equivalent to
808 * heap_blks_total after the table scan phase, this parameter
809 * is manually updated to the correct value when the table
810 * scan finishes.
811 */
813 heapScan->rs_nblocks);
814 break;
815 }
816
817 /*
818 * In scan-and-sort mode and also VACUUM FULL, set heap blocks
819 * scanned
820 *
821 * Note that heapScan may start at an offset and wrap around, i.e.
822 * rs_startblock may be >0, and rs_cblock may end with a number
823 * below rs_startblock. To prevent showing this wraparound to the
824 * user, we offset rs_cblock by rs_startblock (modulo rs_nblocks).
825 */
826 if (prev_cblock != heapScan->rs_cblock)
827 {
829 (heapScan->rs_cblock +
830 heapScan->rs_nblocks -
831 heapScan->rs_startblock
832 ) % heapScan->rs_nblocks + 1);
833 prev_cblock = heapScan->rs_cblock;
834 }
835 }
836
837 tuple = ExecFetchSlotHeapTuple(slot, false, NULL);
838 buf = hslot->buffer;
839
840 /*
841 * To be able to guarantee that we can set the hint bit, acquire an
842 * exclusive lock on the old buffer. We need the hint bits, set in
843 * heapam_relation_copy_for_cluster() -> HeapTupleSatisfiesVacuum(),
844 * to be set, as otherwise reform_and_rewrite_tuple() ->
845 * rewrite_heap_tuple() will get confused. Specifically,
846 * rewrite_heap_tuple() checks for HEAP_XMAX_INVALID in the old tuple
847 * to determine whether to check the old-to-new mapping hash table.
848 *
849 * It'd be better if we somehow could avoid setting hint bits on the
850 * old page. One reason to use VACUUM FULL are very bloated tables -
851 * rewriting most of the old table during VACUUM FULL doesn't exactly
852 * help...
853 */
855
856 switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf))
857 {
858 case HEAPTUPLE_DEAD:
859 /* Definitely dead */
860 isdead = true;
861 break;
863 *tups_recently_dead += 1;
865 case HEAPTUPLE_LIVE:
866 /* Live or recently dead, must copy it */
867 isdead = false;
868 break;
870
871 /*
872 * Since we hold exclusive lock on the relation, normally the
873 * only way to see this is if it was inserted earlier in our
874 * own transaction. However, it can happen in system
875 * catalogs, since we tend to release write lock before commit
876 * there. Give a warning if neither case applies; but in any
877 * case we had better copy it.
878 */
879 if (!is_system_catalog &&
881 elog(WARNING, "concurrent insert in progress within table \"%s\"",
883 /* treat as live */
884 isdead = false;
885 break;
887
888 /*
889 * Similar situation to INSERT_IN_PROGRESS case.
890 */
891 if (!is_system_catalog &&
893 elog(WARNING, "concurrent delete in progress within table \"%s\"",
895 /* treat as recently dead */
896 *tups_recently_dead += 1;
897 isdead = false;
898 break;
899 default:
900 elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
901 isdead = false; /* keep compiler quiet */
902 break;
903 }
904
906
907 if (isdead)
908 {
909 *tups_vacuumed += 1;
910 /* heap rewrite module still needs to see it... */
912 {
913 /* A previous recently-dead tuple is now known dead */
914 *tups_vacuumed += 1;
915 *tups_recently_dead -= 1;
916 }
917 continue;
918 }
919
920 *num_tuples += 1;
921 if (tuplesort != NULL)
922 {
923 tuplesort_putheaptuple(tuplesort, tuple);
924
925 /*
926 * In scan-and-sort mode, report increase in number of tuples
927 * scanned
928 */
930 *num_tuples);
931 }
932 else
933 {
934 const int ct_index[] = {
937 };
938 int64 ct_val[2];
939
941 values, isnull, rwstate);
942
943 /*
944 * In indexscan mode and also VACUUM FULL, report increase in
945 * number of tuples scanned and written
946 */
947 ct_val[0] = *num_tuples;
948 ct_val[1] = *num_tuples;
950 }
951 }
952
953 if (indexScan != NULL)
955 if (tableScan != NULL)
957 if (slot)
959
960 /*
961 * In scan-and-sort mode, complete the sort, then read out all live tuples
962 * from the tuplestore and write them to the new relation.
963 */
964 if (tuplesort != NULL)
965 {
966 double n_tuples = 0;
967
968 /* Report that we are now sorting tuples */
971
972 tuplesort_performsort(tuplesort);
973
974 /* Report that we are now writing new heap */
977
978 for (;;)
979 {
980 HeapTuple tuple;
981
983
984 tuple = tuplesort_getheaptuple(tuplesort, true);
985 if (tuple == NULL)
986 break;
987
988 n_tuples += 1;
991 values, isnull,
992 rwstate);
993 /* Report n_tuples */
995 n_tuples);
996 }
997
998 tuplesort_end(tuplesort);
999 }
1000
1001 /* Write out any remaining tuples, and fsync if needed */
1003
1004 /* Clean up */
1005 pfree(values);
1006 pfree(isnull);
1007}
1008
1009/*
1010 * Prepare to analyze the next block in the read stream. Returns false if
1011 * the stream is exhausted and true otherwise. The scan must have been started
1012 * with SO_TYPE_ANALYZE option.
1013 *
1014 * This routine holds a buffer pin and lock on the heap page. They are held
1015 * until heapam_scan_analyze_next_tuple() returns false. That is until all the
1016 * items of the heap page are analyzed.
1017 */
1018static bool
1020{
1022
1023 /*
1024 * We must maintain a pin on the target page's buffer to ensure that
1025 * concurrent activity - e.g. HOT pruning - doesn't delete tuples out from
1026 * under us. It comes from the stream already pinned. We also choose to
1027 * hold sharelock on the buffer throughout --- we could release and
1028 * re-acquire sharelock for each tuple, but since we aren't doing much
1029 * work per tuple, the extra lock traffic is probably better avoided.
1030 */
1031 hscan->rs_cbuf = read_stream_next_buffer(stream, NULL);
1032 if (!BufferIsValid(hscan->rs_cbuf))
1033 return false;
1034
1036
1037 hscan->rs_cblock = BufferGetBlockNumber(hscan->rs_cbuf);
1038 hscan->rs_cindex = FirstOffsetNumber;
1039 return true;
1040}
1041
1042static bool
1044 double *liverows, double *deadrows,
1045 TupleTableSlot *slot)
1046{
1048 Page targpage;
1051
1053
1055 targpage = BufferGetPage(hscan->rs_cbuf);
1057
1058 /* Inner loop over all tuples on the selected page */
1059 for (; hscan->rs_cindex <= maxoffset; hscan->rs_cindex++)
1060 {
1061 ItemId itemid;
1062 HeapTuple targtuple = &hslot->base.tupdata;
1063 bool sample_it = false;
1065
1066 itemid = PageGetItemId(targpage, hscan->rs_cindex);
1067
1068 /*
1069 * We ignore unused and redirect line pointers. DEAD line pointers
1070 * should be counted as dead, because we need vacuum to run to get rid
1071 * of them. Note that this rule agrees with the way that
1072 * heap_page_prune_and_freeze() counts things.
1073 */
1074 if (!ItemIdIsNormal(itemid))
1075 {
1076 if (ItemIdIsDead(itemid))
1077 *deadrows += 1;
1078 continue;
1079 }
1080
1081 ItemPointerSet(&targtuple->t_self, hscan->rs_cblock, hscan->rs_cindex);
1082
1083 targtuple->t_tableOid = RelationGetRelid(scan->rs_rd);
1084 targtuple->t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
1085 targtuple->t_len = ItemIdGetLength(itemid);
1086
1088 hscan->rs_cbuf,
1089 &dead_after))
1090 {
1091 case HEAPTUPLE_LIVE:
1092 sample_it = true;
1093 *liverows += 1;
1094 break;
1095
1096 case HEAPTUPLE_DEAD:
1098 /* Count dead and recently-dead rows */
1099 *deadrows += 1;
1100 break;
1101
1103
1104 /*
1105 * Insert-in-progress rows are not counted. We assume that
1106 * when the inserting transaction commits or aborts, it will
1107 * send a stats message to increment the proper count. This
1108 * works right only if that transaction ends after we finish
1109 * analyzing the table; if things happen in the other order,
1110 * its stats update will be overwritten by ours. However, the
1111 * error will be large only if the other transaction runs long
1112 * enough to insert many tuples, so assuming it will finish
1113 * after us is the safer option.
1114 *
1115 * A special case is that the inserting transaction might be
1116 * our own. In this case we should count and sample the row,
1117 * to accommodate users who load a table and analyze it in one
1118 * transaction. (pgstat_report_analyze has to adjust the
1119 * numbers we report to the cumulative stats system to make
1120 * this come out right.)
1121 */
1123 {
1124 sample_it = true;
1125 *liverows += 1;
1126 }
1127 break;
1128
1130
1131 /*
1132 * We count and sample delete-in-progress rows the same as
1133 * live ones, so that the stats counters come out right if the
1134 * deleting transaction commits after us, per the same
1135 * reasoning given above.
1136 *
1137 * If the delete was done by our own transaction, however, we
1138 * must count the row as dead to make pgstat_report_analyze's
1139 * stats adjustments come out right. (Note: this works out
1140 * properly when the row was both inserted and deleted in our
1141 * xact.)
1142 *
1143 * The net effect of these choices is that we act as though an
1144 * IN_PROGRESS transaction hasn't happened yet, except if it
1145 * is our own transaction, which we assume has happened.
1146 *
1147 * This approach ensures that we behave sanely if we see both
1148 * the pre-image and post-image rows for a row being updated
1149 * by a concurrent transaction: we will sample the pre-image
1150 * but not the post-image. We also get sane results if the
1151 * concurrent transaction never commits.
1152 */
1154 *deadrows += 1;
1155 else
1156 {
1157 sample_it = true;
1158 *liverows += 1;
1159 }
1160 break;
1161
1162 default:
1163 elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
1164 break;
1165 }
1166
1167 if (sample_it)
1168 {
1169 ExecStoreBufferHeapTuple(targtuple, slot, hscan->rs_cbuf);
1170 hscan->rs_cindex++;
1171
1172 /* note that we leave the buffer locked here! */
1173 return true;
1174 }
1175 }
1176
1177 /* Now release the lock and pin on the page */
1178 UnlockReleaseBuffer(hscan->rs_cbuf);
1179 hscan->rs_cbuf = InvalidBuffer;
1180
1181 /* also prevent old slot contents from having pin on page */
1182 ExecClearTuple(slot);
1183
1184 return false;
1185}
1186
1187static double
1189 Relation indexRelation,
1190 IndexInfo *indexInfo,
1191 bool allow_sync,
1192 bool anyvisible,
1193 bool progress,
1197 void *callback_state,
1198 TableScanDesc scan)
1199{
1201 bool is_system_catalog;
1205 bool isnull[INDEX_MAX_KEYS];
1206 double reltuples;
1208 TupleTableSlot *slot;
1209 EState *estate;
1210 ExprContext *econtext;
1211 Snapshot snapshot;
1212 bool need_unregister_snapshot = false;
1213 TransactionId OldestXmin;
1215 BlockNumber root_blkno = InvalidBlockNumber;
1217
1218 /*
1219 * sanity checks
1220 */
1221 Assert(OidIsValid(indexRelation->rd_rel->relam));
1222
1223 /* Remember if it's a system catalog */
1224 is_system_catalog = IsSystemRelation(heapRelation);
1225
1226 /* See whether we're verifying uniqueness/exclusion properties */
1227 checking_uniqueness = (indexInfo->ii_Unique ||
1228 indexInfo->ii_ExclusionOps != NULL);
1229
1230 /*
1231 * "Any visible" mode is not compatible with uniqueness checks; make sure
1232 * only one of those is requested.
1233 */
1235
1236 /*
1237 * Need an EState for evaluation of index expressions and partial-index
1238 * predicates. Also a slot to hold the current tuple.
1239 */
1240 estate = CreateExecutorState();
1241 econtext = GetPerTupleExprContext(estate);
1242 slot = table_slot_create(heapRelation, NULL);
1243
1244 /* Arrange for econtext's scan tuple to be the tuple under test */
1245 econtext->ecxt_scantuple = slot;
1246
1247 /* Set up execution state for predicate, if any. */
1248 predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
1249
1250 /*
1251 * Prepare for scan of the base relation. In a normal index build, we use
1252 * SnapshotAny because we must retrieve all tuples and do our own time
1253 * qual checks (because we have to index RECENTLY_DEAD tuples). In a
1254 * concurrent build, or during bootstrap, we take a regular MVCC snapshot
1255 * and index whatever's live according to that.
1256 */
1257 OldestXmin = InvalidTransactionId;
1258
1259 /* okay to ignore lazy VACUUMs here */
1260 if (!IsBootstrapProcessingMode() && !indexInfo->ii_Concurrent)
1261 OldestXmin = GetOldestNonRemovableTransactionId(heapRelation);
1262
1263 if (!scan)
1264 {
1265 /*
1266 * Serial index build.
1267 *
1268 * Must begin our own heap scan in this case. We may also need to
1269 * register a snapshot whose lifetime is under our direct control.
1270 */
1271 if (!TransactionIdIsValid(OldestXmin))
1272 {
1275 }
1276 else
1277 snapshot = SnapshotAny;
1278
1279 scan = table_beginscan_strat(heapRelation, /* relation */
1280 snapshot, /* snapshot */
1281 0, /* number of keys */
1282 NULL, /* scan key */
1283 true, /* buffer access strategy OK */
1284 allow_sync); /* syncscan OK? */
1285 }
1286 else
1287 {
1288 /*
1289 * Parallel index build.
1290 *
1291 * Parallel case never registers/unregisters own snapshot. Snapshot
1292 * is taken from parallel heap scan, and is SnapshotAny or an MVCC
1293 * snapshot, based on same criteria as serial case.
1294 */
1297 snapshot = scan->rs_snapshot;
1298 }
1299
1300 hscan = (HeapScanDesc) scan;
1301
1302 /*
1303 * Must have called GetOldestNonRemovableTransactionId() if using
1304 * SnapshotAny. Shouldn't have for an MVCC snapshot. (It's especially
1305 * worth checking this for parallel builds, since ambuild routines that
1306 * support parallel builds must work these details out for themselves.)
1307 */
1308 Assert(snapshot == SnapshotAny || IsMVCCSnapshot(snapshot));
1309 Assert(snapshot == SnapshotAny ? TransactionIdIsValid(OldestXmin) :
1310 !TransactionIdIsValid(OldestXmin));
1311 Assert(snapshot == SnapshotAny || !anyvisible);
1312
1313 /* Publish number of blocks to scan */
1314 if (progress)
1315 {
1316 BlockNumber nblocks;
1317
1318 if (hscan->rs_base.rs_parallel != NULL)
1319 {
1321
1322 pbscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
1323 nblocks = pbscan->phs_nblocks;
1324 }
1325 else
1326 nblocks = hscan->rs_nblocks;
1327
1329 nblocks);
1330 }
1331
1332 /* set our scan endpoints */
1333 if (!allow_sync)
1335 else
1336 {
1337 /* syncscan can only be requested on whole relation */
1338 Assert(start_blockno == 0);
1340 }
1341
1342 reltuples = 0;
1343
1344 /*
1345 * Scan all tuples in the base relation.
1346 */
1347 while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1348 {
1349 bool tupleIsAlive;
1350
1352
1353 /* Report scan progress, if asked to. */
1354 if (progress)
1355 {
1357
1359 {
1361 blocks_done);
1363 }
1364 }
1365
1366 /*
1367 * When dealing with a HOT-chain of updated tuples, we want to index
1368 * the values of the live tuple (if any), but index it under the TID
1369 * of the chain's root tuple. This approach is necessary to preserve
1370 * the HOT-chain structure in the heap. So we need to be able to find
1371 * the root item offset for every tuple that's in a HOT-chain. When
1372 * first reaching a new page of the relation, call
1373 * heap_get_root_tuples() to build a map of root item offsets on the
1374 * page.
1375 *
1376 * It might look unsafe to use this information across buffer
1377 * lock/unlock. However, we hold ShareLock on the table so no
1378 * ordinary insert/update/delete should occur; and we hold pin on the
1379 * buffer continuously while visiting the page, so no pruning
1380 * operation can occur either.
1381 *
1382 * In cases with only ShareUpdateExclusiveLock on the table, it's
1383 * possible for some HOT tuples to appear that we didn't know about
1384 * when we first read the page. To handle that case, we re-obtain the
1385 * list of root offsets when a HOT tuple points to a root item that we
1386 * don't know about.
1387 *
1388 * Also, although our opinions about tuple liveness could change while
1389 * we scan the page (due to concurrent transaction commits/aborts),
1390 * the chain root locations won't, so this info doesn't need to be
1391 * rebuilt after waiting for another transaction.
1392 *
1393 * Note the implied assumption that there is no more than one live
1394 * tuple per HOT-chain --- else we could create more than one index
1395 * entry pointing to the same root tuple.
1396 */
1397 if (hscan->rs_cblock != root_blkno)
1398 {
1399 Page page = BufferGetPage(hscan->rs_cbuf);
1400
1404
1405 root_blkno = hscan->rs_cblock;
1406 }
1407
1408 if (snapshot == SnapshotAny)
1409 {
1410 /* do our own time qual check */
1411 bool indexIt;
1413
1414 recheck:
1415
1416 /*
1417 * We could possibly get away with not locking the buffer here,
1418 * since caller should hold ShareLock on the relation, but let's
1419 * be conservative about it. (This remark is still correct even
1420 * with HOT-pruning: our pin on the buffer prevents pruning.)
1421 */
1423
1424 /*
1425 * The criteria for counting a tuple as live in this block need to
1426 * match what analyze.c's heapam_scan_analyze_next_tuple() does,
1427 * otherwise CREATE INDEX and ANALYZE may produce wildly different
1428 * reltuples values, e.g. when there are many recently-dead
1429 * tuples.
1430 */
1431 switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin,
1432 hscan->rs_cbuf))
1433 {
1434 case HEAPTUPLE_DEAD:
1435 /* Definitely dead, we can ignore it */
1436 indexIt = false;
1437 tupleIsAlive = false;
1438 break;
1439 case HEAPTUPLE_LIVE:
1440 /* Normal case, index and unique-check it */
1441 indexIt = true;
1442 tupleIsAlive = true;
1443 /* Count it as live, too */
1444 reltuples += 1;
1445 break;
1447
1448 /*
1449 * If tuple is recently deleted then we must index it
1450 * anyway to preserve MVCC semantics. (Pre-existing
1451 * transactions could try to use the index after we finish
1452 * building it, and may need to see such tuples.)
1453 *
1454 * However, if it was HOT-updated then we must only index
1455 * the live tuple at the end of the HOT-chain. Since this
1456 * breaks semantics for pre-existing snapshots, mark the
1457 * index as unusable for them.
1458 *
1459 * We don't count recently-dead tuples in reltuples, even
1460 * if we index them; see heapam_scan_analyze_next_tuple().
1461 */
1463 {
1464 indexIt = false;
1465 /* mark the index as unsafe for old snapshots */
1466 indexInfo->ii_BrokenHotChain = true;
1467 }
1468 else
1469 indexIt = true;
1470 /* In any case, exclude the tuple from unique-checking */
1471 tupleIsAlive = false;
1472 break;
1474
1475 /*
1476 * In "anyvisible" mode, this tuple is visible and we
1477 * don't need any further checks.
1478 */
1479 if (anyvisible)
1480 {
1481 indexIt = true;
1482 tupleIsAlive = true;
1483 reltuples += 1;
1484 break;
1485 }
1486
1487 /*
1488 * Since caller should hold ShareLock or better, normally
1489 * the only way to see this is if it was inserted earlier
1490 * in our own transaction. However, it can happen in
1491 * system catalogs, since we tend to release write lock
1492 * before commit there. Give a warning if neither case
1493 * applies.
1494 */
1497 {
1498 if (!is_system_catalog)
1499 elog(WARNING, "concurrent insert in progress within table \"%s\"",
1500 RelationGetRelationName(heapRelation));
1501
1502 /*
1503 * If we are performing uniqueness checks, indexing
1504 * such a tuple could lead to a bogus uniqueness
1505 * failure. In that case we wait for the inserting
1506 * transaction to finish and check again.
1507 */
1509 {
1510 /*
1511 * Must drop the lock on the buffer before we wait
1512 */
1514 XactLockTableWait(xwait, heapRelation,
1515 &heapTuple->t_self,
1518 goto recheck;
1519 }
1520 }
1521 else
1522 {
1523 /*
1524 * For consistency with
1525 * heapam_scan_analyze_next_tuple(), count
1526 * HEAPTUPLE_INSERT_IN_PROGRESS tuples as live only
1527 * when inserted by our own transaction.
1528 */
1529 reltuples += 1;
1530 }
1531
1532 /*
1533 * We must index such tuples, since if the index build
1534 * commits then they're good.
1535 */
1536 indexIt = true;
1537 tupleIsAlive = true;
1538 break;
1540
1541 /*
1542 * As with INSERT_IN_PROGRESS case, this is unexpected
1543 * unless it's our own deletion or a system catalog; but
1544 * in anyvisible mode, this tuple is visible.
1545 */
1546 if (anyvisible)
1547 {
1548 indexIt = true;
1549 tupleIsAlive = false;
1550 reltuples += 1;
1551 break;
1552 }
1553
1556 {
1557 if (!is_system_catalog)
1558 elog(WARNING, "concurrent delete in progress within table \"%s\"",
1559 RelationGetRelationName(heapRelation));
1560
1561 /*
1562 * If we are performing uniqueness checks, assuming
1563 * the tuple is dead could lead to missing a
1564 * uniqueness violation. In that case we wait for the
1565 * deleting transaction to finish and check again.
1566 *
1567 * Also, if it's a HOT-updated tuple, we should not
1568 * index it but rather the live tuple at the end of
1569 * the HOT-chain. However, the deleting transaction
1570 * could abort, possibly leaving this tuple as live
1571 * after all, in which case it has to be indexed. The
1572 * only way to know what to do is to wait for the
1573 * deleting transaction to finish and check again.
1574 */
1575 if (checking_uniqueness ||
1577 {
1578 /*
1579 * Must drop the lock on the buffer before we wait
1580 */
1582 XactLockTableWait(xwait, heapRelation,
1583 &heapTuple->t_self,
1586 goto recheck;
1587 }
1588
1589 /*
1590 * Otherwise index it but don't check for uniqueness,
1591 * the same as a RECENTLY_DEAD tuple.
1592 */
1593 indexIt = true;
1594
1595 /*
1596 * Count HEAPTUPLE_DELETE_IN_PROGRESS tuples as live,
1597 * if they were not deleted by the current
1598 * transaction. That's what
1599 * heapam_scan_analyze_next_tuple() does, and we want
1600 * the behavior to be consistent.
1601 */
1602 reltuples += 1;
1603 }
1605 {
1606 /*
1607 * It's a HOT-updated tuple deleted by our own xact.
1608 * We can assume the deletion will commit (else the
1609 * index contents don't matter), so treat the same as
1610 * RECENTLY_DEAD HOT-updated tuples.
1611 */
1612 indexIt = false;
1613 /* mark the index as unsafe for old snapshots */
1614 indexInfo->ii_BrokenHotChain = true;
1615 }
1616 else
1617 {
1618 /*
1619 * It's a regular tuple deleted by our own xact. Index
1620 * it, but don't check for uniqueness nor count in
1621 * reltuples, the same as a RECENTLY_DEAD tuple.
1622 */
1623 indexIt = true;
1624 }
1625 /* In any case, exclude the tuple from unique-checking */
1626 tupleIsAlive = false;
1627 break;
1628 default:
1629 elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
1630 indexIt = tupleIsAlive = false; /* keep compiler quiet */
1631 break;
1632 }
1633
1635
1636 if (!indexIt)
1637 continue;
1638 }
1639 else
1640 {
1641 /* heap_getnext did the time qual check */
1642 tupleIsAlive = true;
1643 reltuples += 1;
1644 }
1645
1647
1648 /* Set up for predicate or expression evaluation */
1649 ExecStoreBufferHeapTuple(heapTuple, slot, hscan->rs_cbuf);
1650
1651 /*
1652 * In a partial index, discard tuples that don't satisfy the
1653 * predicate.
1654 */
1655 if (predicate != NULL)
1656 {
1657 if (!ExecQual(predicate, econtext))
1658 continue;
1659 }
1660
1661 /*
1662 * For the current heap tuple, extract all the attributes we use in
1663 * this index, and note which are null. This also performs evaluation
1664 * of any expressions needed.
1665 */
1666 FormIndexDatum(indexInfo,
1667 slot,
1668 estate,
1669 values,
1670 isnull);
1671
1672 /*
1673 * You'd think we should go ahead and build the index tuple here, but
1674 * some index AMs want to do further processing on the data first. So
1675 * pass the values[] and isnull[] arrays, instead.
1676 */
1677
1679 {
1680 /*
1681 * For a heap-only tuple, pretend its TID is that of the root. See
1682 * src/backend/access/heap/README.HOT for discussion.
1683 */
1684 ItemPointerData tid;
1685 OffsetNumber offnum;
1686
1687 offnum = ItemPointerGetOffsetNumber(&heapTuple->t_self);
1688
1689 /*
1690 * If a HOT tuple points to a root that we don't know about,
1691 * obtain root items afresh. If that still fails, report it as
1692 * corruption.
1693 */
1694 if (root_offsets[offnum - 1] == InvalidOffsetNumber)
1695 {
1696 Page page = BufferGetPage(hscan->rs_cbuf);
1697
1701 }
1702
1703 if (!OffsetNumberIsValid(root_offsets[offnum - 1]))
1704 ereport(ERROR,
1706 errmsg_internal("failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
1708 offnum,
1709 RelationGetRelationName(heapRelation))));
1710
1712 root_offsets[offnum - 1]);
1713
1714 /* Call the AM's callback routine to process the tuple */
1715 callback(indexRelation, &tid, values, isnull, tupleIsAlive,
1716 callback_state);
1717 }
1718 else
1719 {
1720 /* Call the AM's callback routine to process the tuple */
1721 callback(indexRelation, &heapTuple->t_self, values, isnull,
1722 tupleIsAlive, callback_state);
1723 }
1724 }
1725
1726 /* Report scan progress one last time. */
1727 if (progress)
1728 {
1730
1731 if (hscan->rs_base.rs_parallel != NULL)
1732 {
1734
1735 pbscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
1736 blks_done = pbscan->phs_nblocks;
1737 }
1738 else
1739 blks_done = hscan->rs_nblocks;
1740
1742 blks_done);
1743 }
1744
1745 table_endscan(scan);
1746
1747 /* we can now forget our snapshot, if set and registered by us */
1749 UnregisterSnapshot(snapshot);
1750
1752
1753 FreeExecutorState(estate);
1754
1755 /* These may have been pointing to the now-gone estate */
1756 indexInfo->ii_ExpressionsState = NIL;
1757 indexInfo->ii_PredicateState = NULL;
1758
1759 return reltuples;
1760}
1761
1762static void
1764 Relation indexRelation,
1765 IndexInfo *indexInfo,
1766 Snapshot snapshot,
1768{
1769 TableScanDesc scan;
1773 bool isnull[INDEX_MAX_KEYS];
1775 TupleTableSlot *slot;
1776 EState *estate;
1777 ExprContext *econtext;
1778 BlockNumber root_blkno = InvalidBlockNumber;
1782
1783 /* state variables for the merge */
1786 bool tuplesort_empty = false;
1787
1788 /*
1789 * sanity checks
1790 */
1791 Assert(OidIsValid(indexRelation->rd_rel->relam));
1792
1793 /*
1794 * Need an EState for evaluation of index expressions and partial-index
1795 * predicates. Also a slot to hold the current tuple.
1796 */
1797 estate = CreateExecutorState();
1798 econtext = GetPerTupleExprContext(estate);
1799 slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation),
1801
1802 /* Arrange for econtext's scan tuple to be the tuple under test */
1803 econtext->ecxt_scantuple = slot;
1804
1805 /* Set up execution state for predicate, if any. */
1806 predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
1807
1808 /*
1809 * Prepare for scan of the base relation. We need just those tuples
1810 * satisfying the passed-in reference snapshot. We must disable syncscan
1811 * here, because it's critical that we read from block zero forward to
1812 * match the sorted TIDs.
1813 */
1814 scan = table_beginscan_strat(heapRelation, /* relation */
1815 snapshot, /* snapshot */
1816 0, /* number of keys */
1817 NULL, /* scan key */
1818 true, /* buffer access strategy OK */
1819 false); /* syncscan not OK */
1820 hscan = (HeapScanDesc) scan;
1821
1823 hscan->rs_nblocks);
1824
1825 /*
1826 * Scan all tuples matching the snapshot.
1827 */
1828 while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1829 {
1830 ItemPointer heapcursor = &heapTuple->t_self;
1833
1835
1836 state->htups += 1;
1837
1839 (hscan->rs_cblock != previous_blkno))
1840 {
1842 hscan->rs_cblock);
1843 previous_blkno = hscan->rs_cblock;
1844 }
1845
1846 /*
1847 * As commented in table_index_build_scan, we should index heap-only
1848 * tuples under the TIDs of their root tuples; so when we advance onto
1849 * a new heap page, build a map of root item offsets on the page.
1850 *
1851 * This complicates merging against the tuplesort output: we will
1852 * visit the live tuples in order by their offsets, but the root
1853 * offsets that we need to compare against the index contents might be
1854 * ordered differently. So we might have to "look back" within the
1855 * tuplesort output, but only within the current page. We handle that
1856 * by keeping a bool array in_index[] showing all the
1857 * already-passed-over tuplesort output TIDs of the current page. We
1858 * clear that array here, when advancing onto a new heap page.
1859 */
1860 if (hscan->rs_cblock != root_blkno)
1861 {
1862 Page page = BufferGetPage(hscan->rs_cbuf);
1863
1867
1868 memset(in_index, 0, sizeof(in_index));
1869
1870 root_blkno = hscan->rs_cblock;
1871 }
1872
1873 /* Convert actual tuple TID to root TID */
1876
1878 {
1881 ereport(ERROR,
1883 errmsg_internal("failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
1886 RelationGetRelationName(heapRelation))));
1888 }
1889
1890 /*
1891 * "merge" by skipping through the index tuples until we find or pass
1892 * the current root tuple.
1893 */
1894 while (!tuplesort_empty &&
1895 (!indexcursor ||
1897 {
1898 Datum ts_val;
1899 bool ts_isnull;
1900
1901 if (indexcursor)
1902 {
1903 /*
1904 * Remember index items seen earlier on the current heap page
1905 */
1906 if (ItemPointerGetBlockNumber(indexcursor) == root_blkno)
1908 }
1909
1910 tuplesort_empty = !tuplesort_getdatum(state->tuplesort, true,
1911 false, &ts_val, &ts_isnull,
1912 NULL);
1914 if (!tuplesort_empty)
1915 {
1918 }
1919 else
1920 {
1921 /* Be tidy */
1922 indexcursor = NULL;
1923 }
1924 }
1925
1926 /*
1927 * If the tuplesort has overshot *and* we didn't see a match earlier,
1928 * then this tuple is missing from the index, so insert it.
1929 */
1930 if ((tuplesort_empty ||
1932 !in_index[root_offnum - 1])
1933 {
1935
1936 /* Set up for predicate or expression evaluation */
1937 ExecStoreHeapTuple(heapTuple, slot, false);
1938
1939 /*
1940 * In a partial index, discard tuples that don't satisfy the
1941 * predicate.
1942 */
1943 if (predicate != NULL)
1944 {
1945 if (!ExecQual(predicate, econtext))
1946 continue;
1947 }
1948
1949 /*
1950 * For the current heap tuple, extract all the attributes we use
1951 * in this index, and note which are null. This also performs
1952 * evaluation of any expressions needed.
1953 */
1954 FormIndexDatum(indexInfo,
1955 slot,
1956 estate,
1957 values,
1958 isnull);
1959
1960 /*
1961 * You'd think we should go ahead and build the index tuple here,
1962 * but some index AMs want to do further processing on the data
1963 * first. So pass the values[] and isnull[] arrays, instead.
1964 */
1965
1966 /*
1967 * If the tuple is already committed dead, you might think we
1968 * could suppress uniqueness checking, but this is no longer true
1969 * in the presence of HOT, because the insert is actually a proxy
1970 * for a uniqueness check on the whole HOT-chain. That is, the
1971 * tuple we have here could be dead because it was already
1972 * HOT-updated, and if so the updating transaction will not have
1973 * thought it should insert index entries. The index AM will
1974 * check the whole HOT-chain and correctly detect a conflict if
1975 * there is one.
1976 */
1977
1978 index_insert(indexRelation,
1979 values,
1980 isnull,
1981 &rootTuple,
1982 heapRelation,
1983 indexInfo->ii_Unique ?
1985 false,
1986 indexInfo);
1987
1988 state->tups_inserted += 1;
1989 }
1990 }
1991
1992 table_endscan(scan);
1993
1995
1996 FreeExecutorState(estate);
1997
1998 /* These may have been pointing to the now-gone estate */
1999 indexInfo->ii_ExpressionsState = NIL;
2000 indexInfo->ii_PredicateState = NULL;
2001}
2002
2003/*
2004 * Return the number of blocks that have been read by this scan since
2005 * starting. This is meant for progress reporting rather than be fully
2006 * accurate: in a parallel scan, workers can be concurrently reading blocks
2007 * further ahead than what we report.
2008 */
2009static BlockNumber
2011{
2013 BlockNumber startblock;
2015
2016 if (hscan->rs_base.rs_parallel != NULL)
2017 {
2018 bpscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
2019 startblock = bpscan->phs_startblock;
2020 }
2021 else
2022 startblock = hscan->rs_startblock;
2023
2024 /*
2025 * Might have wrapped around the end of the relation, if startblock was
2026 * not zero.
2027 */
2028 if (hscan->rs_cblock > startblock)
2029 blocks_done = hscan->rs_cblock - startblock;
2030 else
2031 {
2032 BlockNumber nblocks;
2033
2034 nblocks = bpscan != NULL ? bpscan->phs_nblocks : hscan->rs_nblocks;
2035 blocks_done = nblocks - startblock +
2036 hscan->rs_cblock;
2037 }
2038
2039 return blocks_done;
2040}
2041
2042
2043/* ------------------------------------------------------------------------
2044 * Miscellaneous callbacks for the heap AM
2045 * ------------------------------------------------------------------------
2046 */
2047
2048/*
2049 * Check to see whether the table needs a TOAST table. It does only if
2050 * (1) there are any toastable attributes, and (2) the maximum length
2051 * of a tuple could exceed TOAST_TUPLE_THRESHOLD. (We don't want to
2052 * create a toast table for something like "f1 varchar(20)".)
2053 */
2054static bool
2056{
2057 int32 data_length = 0;
2058 bool maxlength_unknown = false;
2059 bool has_toastable_attrs = false;
2060 TupleDesc tupdesc = rel->rd_att;
2062 int i;
2063
2064 for (i = 0; i < tupdesc->natts; i++)
2065 {
2067
2068 if (att->attisdropped)
2069 continue;
2070 if (att->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
2071 continue;
2072 data_length = att_align_nominal(data_length, att->attalign);
2073 if (att->attlen > 0)
2074 {
2075 /* Fixed-length types are never toastable */
2076 data_length += att->attlen;
2077 }
2078 else
2079 {
2080 int32 maxlen = type_maximum_size(att->atttypid,
2081 att->atttypmod);
2082
2083 if (maxlen < 0)
2084 maxlength_unknown = true;
2085 else
2086 data_length += maxlen;
2087 if (att->attstorage != TYPSTORAGE_PLAIN)
2088 has_toastable_attrs = true;
2089 }
2090 }
2092 return false; /* nothing to toast? */
2094 return true; /* any unlimited-length attrs? */
2096 BITMAPLEN(tupdesc->natts)) +
2097 MAXALIGN(data_length);
2099}
2100
2101/*
2102 * TOAST tables for heap relations are just heap relations.
2103 */
2104static Oid
2106{
2107 return rel->rd_rel->relam;
2108}
2109
2110
2111/* ------------------------------------------------------------------------
2112 * Planner related callbacks for the heap AM
2113 * ------------------------------------------------------------------------
2114 */
2115
2116#define HEAP_OVERHEAD_BYTES_PER_TUPLE \
2117 (MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))
2118#define HEAP_USABLE_BYTES_PER_PAGE \
2119 (BLCKSZ - SizeOfPageHeaderData)
2120
2121static void
2123 BlockNumber *pages, double *tuples,
2124 double *allvisfrac)
2125{
2127 tuples, allvisfrac,
2130}
2131
2132
2133/* ------------------------------------------------------------------------
2134 * Executor related callbacks for the heap AM
2135 * ------------------------------------------------------------------------
2136 */
2137
2138static bool
2140 TupleTableSlot *slot,
2141 bool *recheck,
2142 uint64 *lossy_pages,
2143 uint64 *exact_pages)
2144{
2148 Page page;
2149 ItemId lp;
2150
2151 /*
2152 * Out of range? If so, nothing more to look at on this page
2153 */
2154 while (hscan->rs_cindex >= hscan->rs_ntuples)
2155 {
2156 /*
2157 * Returns false if the bitmap is exhausted and there are no further
2158 * blocks we need to scan.
2159 */
2160 if (!BitmapHeapScanNextBlock(scan, recheck, lossy_pages, exact_pages))
2161 return false;
2162 }
2163
2164 targoffset = hscan->rs_vistuples[hscan->rs_cindex];
2165 page = BufferGetPage(hscan->rs_cbuf);
2166 lp = PageGetItemId(page, targoffset);
2168
2169 hscan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2170 hscan->rs_ctup.t_len = ItemIdGetLength(lp);
2171 hscan->rs_ctup.t_tableOid = scan->rs_rd->rd_id;
2172 ItemPointerSet(&hscan->rs_ctup.t_self, hscan->rs_cblock, targoffset);
2173
2175
2176 /*
2177 * Set up the result slot to point to this tuple. Note that the slot
2178 * acquires a pin on the buffer.
2179 */
2181 slot,
2182 hscan->rs_cbuf);
2183
2184 hscan->rs_cindex++;
2185
2186 return true;
2187}
2188
2189static bool
2191{
2193 TsmRoutine *tsm = scanstate->tsmroutine;
2194 BlockNumber blockno;
2195
2196 /* return false immediately if relation is empty */
2197 if (hscan->rs_nblocks == 0)
2198 return false;
2199
2200 /* release previous scan buffer, if any */
2201 if (BufferIsValid(hscan->rs_cbuf))
2202 {
2203 ReleaseBuffer(hscan->rs_cbuf);
2204 hscan->rs_cbuf = InvalidBuffer;
2205 }
2206
2207 if (tsm->NextSampleBlock)
2208 blockno = tsm->NextSampleBlock(scanstate, hscan->rs_nblocks);
2209 else
2210 {
2211 /* scanning table sequentially */
2212
2213 if (hscan->rs_cblock == InvalidBlockNumber)
2214 {
2215 Assert(!hscan->rs_inited);
2216 blockno = hscan->rs_startblock;
2217 }
2218 else
2219 {
2220 Assert(hscan->rs_inited);
2221
2222 blockno = hscan->rs_cblock + 1;
2223
2224 if (blockno >= hscan->rs_nblocks)
2225 {
2226 /* wrap to beginning of rel, might not have started at 0 */
2227 blockno = 0;
2228 }
2229
2230 /*
2231 * Report our new scan position for synchronization purposes.
2232 *
2233 * Note: we do this before checking for end of scan so that the
2234 * final state of the position hint is back at the start of the
2235 * rel. That's not strictly necessary, but otherwise when you run
2236 * the same query multiple times the starting position would shift
2237 * a little bit backwards on every invocation, which is confusing.
2238 * We don't guarantee any specific ordering in general, though.
2239 */
2240 if (scan->rs_flags & SO_ALLOW_SYNC)
2241 ss_report_location(scan->rs_rd, blockno);
2242
2243 if (blockno == hscan->rs_startblock)
2244 {
2245 blockno = InvalidBlockNumber;
2246 }
2247 }
2248 }
2249
2250 hscan->rs_cblock = blockno;
2251
2252 if (!BlockNumberIsValid(blockno))
2253 {
2254 hscan->rs_inited = false;
2255 return false;
2256 }
2257
2258 Assert(hscan->rs_cblock < hscan->rs_nblocks);
2259
2260 /*
2261 * Be sure to check for interrupts at least once per page. Checks at
2262 * higher code levels won't be able to stop a sample scan that encounters
2263 * many pages' worth of consecutive dead tuples.
2264 */
2266
2267 /* Read page using selected strategy */
2268 hscan->rs_cbuf = ReadBufferExtended(hscan->rs_base.rs_rd, MAIN_FORKNUM,
2269 blockno, RBM_NORMAL, hscan->rs_strategy);
2270
2271 /* in pagemode, prune the page and determine visible tuple offsets */
2272 if (hscan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
2274
2275 hscan->rs_inited = true;
2276 return true;
2277}
2278
2279static bool
2281 TupleTableSlot *slot)
2282{
2284 TsmRoutine *tsm = scanstate->tsmroutine;
2285 BlockNumber blockno = hscan->rs_cblock;
2286 bool pagemode = (scan->rs_flags & SO_ALLOW_PAGEMODE) != 0;
2287
2288 Page page;
2289 bool all_visible;
2291
2292 /*
2293 * When not using pagemode, we must lock the buffer during tuple
2294 * visibility checks.
2295 */
2296 if (!pagemode)
2298
2299 page = BufferGetPage(hscan->rs_cbuf);
2300 all_visible = PageIsAllVisible(page) &&
2303
2304 for (;;)
2305 {
2307
2309
2310 /* Ask the tablesample method which tuples to check on this page. */
2311 tupoffset = tsm->NextSampleTuple(scanstate,
2312 blockno,
2313 maxoffset);
2314
2316 {
2317 ItemId itemid;
2318 bool visible;
2319 HeapTuple tuple = &(hscan->rs_ctup);
2320
2321 /* Skip invalid tuple pointers. */
2322 itemid = PageGetItemId(page, tupoffset);
2323 if (!ItemIdIsNormal(itemid))
2324 continue;
2325
2326 tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
2327 tuple->t_len = ItemIdGetLength(itemid);
2328 ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
2329
2330
2331 if (all_visible)
2332 visible = true;
2333 else
2334 visible = SampleHeapTupleVisible(scan, hscan->rs_cbuf,
2335 tuple, tupoffset);
2336
2337 /* in pagemode, heap_prepare_pagescan did this for us */
2338 if (!pagemode)
2339 HeapCheckForSerializableConflictOut(visible, scan->rs_rd, tuple,
2340 hscan->rs_cbuf, scan->rs_snapshot);
2341
2342 /* Try next tuple from same page. */
2343 if (!visible)
2344 continue;
2345
2346 /* Found visible tuple, return it. */
2347 if (!pagemode)
2349
2350 ExecStoreBufferHeapTuple(tuple, slot, hscan->rs_cbuf);
2351
2352 /* Count successfully-fetched tuples as heap fetches */
2354
2355 return true;
2356 }
2357 else
2358 {
2359 /*
2360 * If we get here, it means we've exhausted the items on this page
2361 * and it's time to move to the next.
2362 */
2363 if (!pagemode)
2365
2366 ExecClearTuple(slot);
2367 return false;
2368 }
2369 }
2370
2371 Assert(0);
2372}
2373
2374
2375/* ----------------------------------------------------------------------------
2376 * Helper functions for the above.
2377 * ----------------------------------------------------------------------------
2378 */
2379
2380/*
2381 * Reconstruct and rewrite the given tuple
2382 *
2383 * We cannot simply copy the tuple as-is, for several reasons:
2384 *
2385 * 1. We'd like to squeeze out the values of any dropped columns, both
2386 * to save space and to ensure we have no corner-case failures. (It's
2387 * possible for example that the new table hasn't got a TOAST table
2388 * and so is unable to store any large values of dropped cols.)
2389 *
2390 * 2. The tuple might not even be legal for the new table; this is
2391 * currently only known to happen as an after-effect of ALTER TABLE
2392 * SET WITHOUT OIDS.
2393 *
2394 * So, we must reconstruct the tuple from component Datums.
2395 */
2396static void
2399 Datum *values, bool *isnull, RewriteState rwstate)
2400{
2404 int i;
2405
2406 heap_deform_tuple(tuple, oldTupDesc, values, isnull);
2407
2408 /* Be sure to null out any dropped columns */
2409 for (i = 0; i < newTupDesc->natts; i++)
2410 {
2411 if (TupleDescCompactAttr(newTupDesc, i)->attisdropped)
2412 isnull[i] = true;
2413 }
2414
2416
2417 /* The heap rewrite module does the rest */
2419
2421}
2422
2423/*
2424 * Check visibility of the tuple.
2425 */
2426static bool
2428 HeapTuple tuple,
2430{
2432
2433 if (scan->rs_flags & SO_ALLOW_PAGEMODE)
2434 {
2435 uint32 start = 0,
2436 end = hscan->rs_ntuples;
2437
2438 /*
2439 * In pageatatime mode, heap_prepare_pagescan() already did visibility
2440 * checks, so just look at the info it left in rs_vistuples[].
2441 *
2442 * We use a binary search over the known-sorted array. Note: we could
2443 * save some effort if we insisted that NextSampleTuple select tuples
2444 * in increasing order, but it's not clear that there would be enough
2445 * gain to justify the restriction.
2446 */
2447 while (start < end)
2448 {
2449 uint32 mid = start + (end - start) / 2;
2450 OffsetNumber curoffset = hscan->rs_vistuples[mid];
2451
2452 if (tupoffset == curoffset)
2453 return true;
2454 else if (tupoffset < curoffset)
2455 end = mid;
2456 else
2457 start = mid + 1;
2458 }
2459
2460 return false;
2461 }
2462 else
2463 {
2464 /* Otherwise, we have to check the tuple individually. */
2465 return HeapTupleSatisfiesVisibility(tuple, scan->rs_snapshot,
2466 buffer);
2467 }
2468}
2469
2470/*
2471 * Helper function get the next block of a bitmap heap scan. Returns true when
2472 * it got the next block and saved it in the scan descriptor and false when
2473 * the bitmap and or relation are exhausted.
2474 */
2475static bool
2477 bool *recheck,
2478 uint64 *lossy_pages, uint64 *exact_pages)
2479{
2482 BlockNumber block;
2483 void *per_buffer_data;
2484 Buffer buffer;
2485 Snapshot snapshot;
2486 int ntup;
2489 int noffsets = -1;
2490
2492 Assert(hscan->rs_read_stream);
2493
2494 hscan->rs_cindex = 0;
2495 hscan->rs_ntuples = 0;
2496
2497 /* Release buffer containing previous block. */
2498 if (BufferIsValid(hscan->rs_cbuf))
2499 {
2500 ReleaseBuffer(hscan->rs_cbuf);
2501 hscan->rs_cbuf = InvalidBuffer;
2502 }
2503
2504 hscan->rs_cbuf = read_stream_next_buffer(hscan->rs_read_stream,
2505 &per_buffer_data);
2506
2507 if (BufferIsInvalid(hscan->rs_cbuf))
2508 {
2509 /* the bitmap is exhausted */
2510 return false;
2511 }
2512
2513 Assert(per_buffer_data);
2514
2515 tbmres = per_buffer_data;
2516
2517 Assert(BlockNumberIsValid(tbmres->blockno));
2518 Assert(BufferGetBlockNumber(hscan->rs_cbuf) == tbmres->blockno);
2519
2520 /* Exact pages need their tuple offsets extracted. */
2521 if (!tbmres->lossy)
2524
2525 *recheck = tbmres->recheck;
2526
2527 block = hscan->rs_cblock = tbmres->blockno;
2528 buffer = hscan->rs_cbuf;
2529 snapshot = scan->rs_snapshot;
2530
2531 ntup = 0;
2532
2533 /*
2534 * Prune and repair fragmentation for the whole page, if possible.
2535 */
2536 heap_page_prune_opt(scan->rs_rd, buffer);
2537
2538 /*
2539 * We must hold share lock on the buffer content while examining tuple
2540 * visibility. Afterwards, however, the tuples we have found to be
2541 * visible are guaranteed good as long as we hold the buffer pin.
2542 */
2544
2545 /*
2546 * We need two separate strategies for lossy and non-lossy cases.
2547 */
2548 if (!tbmres->lossy)
2549 {
2550 /*
2551 * Bitmap is non-lossy, so we just look through the offsets listed in
2552 * tbmres; but we have to follow any HOT chain starting at each such
2553 * offset.
2554 */
2555 int curslot;
2556
2557 /* We must have extracted the tuple offsets by now */
2558 Assert(noffsets > -1);
2559
2560 for (curslot = 0; curslot < noffsets; curslot++)
2561 {
2562 OffsetNumber offnum = offsets[curslot];
2563 ItemPointerData tid;
2565
2566 ItemPointerSet(&tid, block, offnum);
2567 if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
2568 &heapTuple, NULL, true))
2569 hscan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
2570 }
2571 }
2572 else
2573 {
2574 /*
2575 * Bitmap is lossy, so we must examine each line pointer on the page.
2576 * But we can ignore HOT chains, since we'll check each tuple anyway.
2577 */
2578 Page page = BufferGetPage(buffer);
2579 OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
2580 OffsetNumber offnum;
2581
2582 for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
2583 {
2584 ItemId lp;
2586 bool valid;
2587
2588 lp = PageGetItemId(page, offnum);
2589 if (!ItemIdIsNormal(lp))
2590 continue;
2591 loctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2592 loctup.t_len = ItemIdGetLength(lp);
2593 loctup.t_tableOid = scan->rs_rd->rd_id;
2594 ItemPointerSet(&loctup.t_self, block, offnum);
2595 valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
2596 if (valid)
2597 {
2598 hscan->rs_vistuples[ntup++] = offnum;
2599 PredicateLockTID(scan->rs_rd, &loctup.t_self, snapshot,
2601 }
2603 buffer, snapshot);
2604 }
2605 }
2606
2608
2610 hscan->rs_ntuples = ntup;
2611
2612 if (tbmres->lossy)
2613 (*lossy_pages)++;
2614 else
2615 (*exact_pages)++;
2616
2617 /*
2618 * Return true to indicate that a valid block was found and the bitmap is
2619 * not exhausted. If there are no visible tuples on this page,
2620 * hscan->rs_ntuples will be 0 and heapam_scan_bitmap_next_tuple() will
2621 * return false returning control to this function to advance to the next
2622 * block in the bitmap.
2623 */
2624 return true;
2625}
2626
2627/* ------------------------------------------------------------------------
2628 * Definition of the heap table access method.
2629 * ------------------------------------------------------------------------
2630 */
2631
2634
2635 .slot_callbacks = heapam_slot_callbacks,
2636
2637 .scan_begin = heap_beginscan,
2638 .scan_end = heap_endscan,
2639 .scan_rescan = heap_rescan,
2640 .scan_getnextslot = heap_getnextslot,
2641
2642 .scan_set_tidrange = heap_set_tidrange,
2643 .scan_getnextslot_tidrange = heap_getnextslot_tidrange,
2644
2645 .parallelscan_estimate = table_block_parallelscan_estimate,
2646 .parallelscan_initialize = table_block_parallelscan_initialize,
2647 .parallelscan_reinitialize = table_block_parallelscan_reinitialize,
2648
2649 .index_fetch_begin = heapam_index_fetch_begin,
2650 .index_fetch_reset = heapam_index_fetch_reset,
2651 .index_fetch_end = heapam_index_fetch_end,
2652 .index_fetch_tuple = heapam_index_fetch_tuple,
2653
2654 .tuple_insert = heapam_tuple_insert,
2655 .tuple_insert_speculative = heapam_tuple_insert_speculative,
2656 .tuple_complete_speculative = heapam_tuple_complete_speculative,
2657 .multi_insert = heap_multi_insert,
2658 .tuple_delete = heapam_tuple_delete,
2659 .tuple_update = heapam_tuple_update,
2660 .tuple_lock = heapam_tuple_lock,
2661
2662 .tuple_fetch_row_version = heapam_fetch_row_version,
2663 .tuple_get_latest_tid = heap_get_latest_tid,
2664 .tuple_tid_valid = heapam_tuple_tid_valid,
2665 .tuple_satisfies_snapshot = heapam_tuple_satisfies_snapshot,
2666 .index_delete_tuples = heap_index_delete_tuples,
2667
2668 .relation_set_new_filelocator = heapam_relation_set_new_filelocator,
2669 .relation_nontransactional_truncate = heapam_relation_nontransactional_truncate,
2670 .relation_copy_data = heapam_relation_copy_data,
2671 .relation_copy_for_cluster = heapam_relation_copy_for_cluster,
2672 .relation_vacuum = heap_vacuum_rel,
2673 .scan_analyze_next_block = heapam_scan_analyze_next_block,
2674 .scan_analyze_next_tuple = heapam_scan_analyze_next_tuple,
2675 .index_build_range_scan = heapam_index_build_range_scan,
2676 .index_validate_scan = heapam_index_validate_scan,
2677
2678 .relation_size = table_block_relation_size,
2679 .relation_needs_toast_table = heapam_relation_needs_toast_table,
2680 .relation_toast_am = heapam_relation_toast_am,
2681 .relation_fetch_toast_slice = heap_fetch_toast_slice,
2682
2683 .relation_estimate_size = heapam_estimate_rel_size,
2684
2685 .scan_bitmap_next_tuple = heapam_scan_bitmap_next_tuple,
2686 .scan_sample_next_block = heapam_scan_sample_next_block,
2687 .scan_sample_next_tuple = heapam_scan_sample_next_tuple
2688};
2689
2690
2691const TableAmRoutine *
2693{
2694 return &heapam_methods;
2695}
2696
2697Datum
void pgstat_progress_update_param(int index, int64 val)
void pgstat_progress_update_multi_param(int nparam, const int *index, const int64 *val)
uint32 BlockNumber
Definition block.h:31
#define InvalidBlockNumber
Definition block.h:33
static bool BlockNumberIsValid(BlockNumber blockNumber)
Definition block.h:71
static Datum values[MAXATTR]
Definition bootstrap.c:147
int Buffer
Definition buf.h:23
#define BufferIsInvalid(buffer)
Definition buf.h:31
#define InvalidBuffer
Definition buf.h:25
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition bufmgr.c:4357
Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation, BlockNumber blockNum)
Definition bufmgr.c:3122
void ReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5502
void UnlockReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5519
void FlushRelationBuffers(Relation rel)
Definition bufmgr.c:5078
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition bufmgr.c:912
static Page BufferGetPage(Buffer buffer)
Definition bufmgr.h:466
@ BUFFER_LOCK_SHARE
Definition bufmgr.h:210
@ BUFFER_LOCK_EXCLUSIVE
Definition bufmgr.h:220
@ BUFFER_LOCK_UNLOCK
Definition bufmgr.h:205
static void LockBuffer(Buffer buffer, BufferLockMode mode)
Definition bufmgr.h:328
@ RBM_NORMAL
Definition bufmgr.h:46
static bool BufferIsValid(Buffer bufnum)
Definition bufmgr.h:417
static bool PageIsAllVisible(const PageData *page)
Definition bufpage.h:428
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition bufpage.h:243
static void * PageGetItem(PageData *page, const ItemIdData *itemId)
Definition bufpage.h:353
PageData * Page
Definition bufpage.h:81
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition bufpage.h:371
#define MAXALIGN(LEN)
Definition c.h:838
uint8_t uint8
Definition c.h:556
#define Assert(condition)
Definition c.h:885
int64_t int64
Definition c.h:555
TransactionId MultiXactId
Definition c.h:688
int32_t int32
Definition c.h:554
uint64_t uint64
Definition c.h:559
uint32_t uint32
Definition c.h:558
#define pg_fallthrough
Definition c.h:144
uint32 CommandId
Definition c.h:692
uint32 TransactionId
Definition c.h:678
#define OidIsValid(objectId)
Definition c.h:800
bool IsSystemRelation(Relation relation)
Definition catalog.c:74
CommandId HeapTupleHeaderGetCmin(const HeapTupleHeaderData *tup)
Definition combocid.c:104
int errcode(int sqlerrcode)
Definition elog.c:874
int errmsg(const char *fmt,...)
Definition elog.c:1093
int int errmsg_internal(const char *fmt,...) pg_attribute_printf(1
#define WARNING
Definition elog.h:36
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define ereport(elevel,...)
Definition elog.h:150
ExprState * ExecPrepareQual(List *qual, EState *estate)
Definition execExpr.c:793
TupleTableSlot * ExecStorePinnedBufferHeapTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer)
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
const TupleTableSlotOps TTSOpsBufferHeapTuple
Definition execTuples.c:87
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
TupleTableSlot * ExecStoreBufferHeapTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer)
const TupleTableSlotOps TTSOpsHeapTuple
Definition execTuples.c:85
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
void FreeExecutorState(EState *estate)
Definition execUtils.c:192
EState * CreateExecutorState(void)
Definition execUtils.c:88
#define GetPerTupleExprContext(estate)
Definition executor.h:656
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition executor.h:519
#define palloc_array(type, count)
Definition fe_memutils.h:76
#define palloc0_object(type)
Definition fe_memutils.h:75
#define PG_RETURN_POINTER(x)
Definition fmgr.h:363
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
int32 type_maximum_size(Oid type_oid, int32 typemod)
@ UNIQUE_CHECK_NO
Definition genam.h:123
@ UNIQUE_CHECK_YES
Definition genam.h:124
int maintenance_work_mem
Definition globals.c:133
return str start
void heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, BulkInsertState bistate)
Definition heapam.c:2142
bool heap_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf)
Definition heapam.c:1659
void heap_finish_speculative(Relation relation, const ItemPointerData *tid)
Definition heapam.c:6168
bool heap_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition heapam.c:1449
void heap_endscan(TableScanDesc sscan)
Definition heapam.c:1371
void heap_rescan(TableScanDesc sscan, ScanKey key, bool set_params, bool allow_strat, bool allow_sync, bool allow_pagemode)
Definition heapam.c:1318
TM_Result heap_update(Relation relation, const ItemPointerData *otid, HeapTuple newtup, CommandId cid, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
Definition heapam.c:3312
TM_Result heap_delete(Relation relation, const ItemPointerData *tid, CommandId cid, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, bool changingPart)
Definition heapam.c:2843
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition heapam.c:1410
bool heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, bool *all_dead, bool first_call)
Definition heapam.c:1779
bool heap_getnextslot_tidrange(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition heapam.c:1552
void heap_set_tidrange(TableScanDesc sscan, ItemPointer mintid, ItemPointer maxtid)
Definition heapam.c:1479
void heap_abort_speculative(Relation relation, const ItemPointerData *tid)
Definition heapam.c:6255
TableScanDesc heap_beginscan(Relation relation, Snapshot snapshot, int nkeys, ScanKey key, ParallelTableScanDesc parallel_scan, uint32 flags)
Definition heapam.c:1164
void heap_prepare_pagescan(TableScanDesc sscan)
Definition heapam.c:616
TransactionId heap_index_delete_tuples(Relation rel, TM_IndexDeleteOp *delstate)
Definition heapam.c:8199
void heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples, CommandId cid, int options, BulkInsertState bistate)
Definition heapam.c:2413
TM_Result heap_lock_tuple(Relation relation, HeapTuple tuple, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, bool follow_updates, Buffer *buffer, TM_FailureData *tmfd)
Definition heapam.c:4644
void heap_get_latest_tid(TableScanDesc sscan, ItemPointer tid)
Definition heapam.c:1931
void heap_setscanlimits(TableScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
Definition heapam.c:500
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition heapam.c:9326
#define HEAP_INSERT_SPECULATIVE
Definition heapam.h:40
struct HeapScanDescData * HeapScanDesc
Definition heapam.h:102
@ HEAPTUPLE_RECENTLY_DEAD
Definition heapam.h:128
@ HEAPTUPLE_INSERT_IN_PROGRESS
Definition heapam.h:129
@ HEAPTUPLE_LIVE
Definition heapam.h:127
@ HEAPTUPLE_DELETE_IN_PROGRESS
Definition heapam.h:130
@ HEAPTUPLE_DEAD
Definition heapam.h:126
struct BitmapHeapScanDescData * BitmapHeapScanDesc
Definition heapam.h:110
static double heapam_index_build_range_scan(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo, bool allow_sync, bool anyvisible, bool progress, BlockNumber start_blockno, BlockNumber numblocks, IndexBuildCallback callback, void *callback_state, TableScanDesc scan)
#define HEAP_OVERHEAD_BYTES_PER_TUPLE
static void heapam_estimate_rel_size(Relation rel, int32 *attr_widths, BlockNumber *pages, double *tuples, double *allvisfrac)
static const TableAmRoutine heapam_methods
static bool BitmapHeapScanNextBlock(TableScanDesc scan, bool *recheck, uint64 *lossy_pages, uint64 *exact_pages)
static void heapam_index_validate_scan(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo, Snapshot snapshot, ValidateIndexState *state)
static bool heapam_index_fetch_tuple(struct IndexFetchTableData *scan, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, bool *call_again, bool *all_dead)
static void heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap, Relation OldIndex, bool use_sort, TransactionId OldestXmin, TransactionId *xid_cutoff, MultiXactId *multi_cutoff, double *num_tuples, double *tups_vacuumed, double *tups_recently_dead)
static void heapam_tuple_insert_speculative(Relation relation, TupleTableSlot *slot, CommandId cid, int options, BulkInsertState bistate, uint32 specToken)
static bool heapam_scan_sample_next_block(TableScanDesc scan, SampleScanState *scanstate)
static void heapam_tuple_insert(Relation relation, TupleTableSlot *slot, CommandId cid, int options, BulkInsertState bistate)
static bool heapam_scan_analyze_next_block(TableScanDesc scan, ReadStream *stream)
static TM_Result heapam_tuple_delete(Relation relation, ItemPointer tid, CommandId cid, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, bool changingPart)
static void heapam_index_fetch_reset(IndexFetchTableData *scan)
static Oid heapam_relation_toast_am(Relation rel)
const TableAmRoutine * GetHeapamTableAmRoutine(void)
static TM_Result heapam_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot, CommandId cid, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
static bool heapam_relation_needs_toast_table(Relation rel)
static BlockNumber heapam_scan_get_blocks_done(HeapScanDesc hscan)
static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, OffsetNumber tupoffset)
static bool heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate, TupleTableSlot *slot)
static IndexFetchTableData * heapam_index_fetch_begin(Relation rel)
static bool heapam_tuple_tid_valid(TableScanDesc scan, ItemPointer tid)
#define HEAP_USABLE_BYTES_PER_PAGE
static TM_Result heapam_tuple_lock(Relation relation, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, uint8 flags, TM_FailureData *tmfd)
static void heapam_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
static void heapam_index_fetch_end(IndexFetchTableData *scan)
static void heapam_tuple_complete_speculative(Relation relation, TupleTableSlot *slot, uint32 specToken, bool succeeded)
static void reform_and_rewrite_tuple(HeapTuple tuple, Relation OldHeap, Relation NewHeap, Datum *values, bool *isnull, RewriteState rwstate)
static void heapam_relation_set_new_filelocator(Relation rel, const RelFileLocator *newrlocator, char persistence, TransactionId *freezeXid, MultiXactId *minmulti)
static bool heapam_fetch_row_version(Relation relation, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
static bool heapam_scan_analyze_next_tuple(TableScanDesc scan, double *liverows, double *deadrows, TupleTableSlot *slot)
static bool heapam_scan_bitmap_next_tuple(TableScanDesc scan, TupleTableSlot *slot, bool *recheck, uint64 *lossy_pages, uint64 *exact_pages)
static void heapam_relation_nontransactional_truncate(Relation rel)
static bool heapam_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot, Snapshot snapshot)
Datum heap_tableam_handler(PG_FUNCTION_ARGS)
static const TupleTableSlotOps * heapam_slot_callbacks(Relation relation)
bool HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
HTSV_Result HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *dead_after)
HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
void heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize, int32 sliceoffset, int32 slicelength, varlena *result)
Definition heaptoast.c:626
#define TOAST_TUPLE_THRESHOLD
Definition heaptoast.h:48
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1117
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition heaptuple.c:1346
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1435
HeapTupleHeaderData * HeapTupleHeader
Definition htup.h:23
static bool HeapTupleIsHotUpdated(const HeapTupleData *tuple)
#define SizeofHeapTupleHeader
static void HeapTupleHeaderSetSpeculativeToken(HeapTupleHeaderData *tup, BlockNumber token)
static int BITMAPLEN(int NATTS)
static bool HeapTupleIsHeapOnly(const HeapTupleData *tuple)
static TransactionId HeapTupleHeaderGetXmin(const HeapTupleHeaderData *tup)
static bool HeapTupleHeaderIsSpeculative(const HeapTupleHeaderData *tup)
static TransactionId HeapTupleHeaderGetUpdateXid(const HeapTupleHeaderData *tup)
#define MaxHeapTuplesPerPage
void FormIndexDatum(IndexInfo *indexInfo, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
Definition index.c:2728
static void itemptr_decode(ItemPointer itemptr, int64 encoded)
Definition index.h:211
bool index_getnext_slot(IndexScanDesc scan, ScanDirection direction, TupleTableSlot *slot)
Definition indexam.c:730
bool index_insert(Relation indexRelation, Datum *values, bool *isnull, ItemPointer heap_t_ctid, Relation heapRelation, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
Definition indexam.c:213
IndexScanDesc index_beginscan(Relation heapRelation, Relation indexRelation, Snapshot snapshot, IndexScanInstrumentation *instrument, int nkeys, int norderbys)
Definition indexam.c:256
void index_endscan(IndexScanDesc scan)
Definition indexam.c:392
void index_rescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys)
Definition indexam.c:366
int i
Definition isn.c:77
#define ItemIdGetLength(itemId)
Definition itemid.h:59
#define ItemIdIsNormal(itemId)
Definition itemid.h:99
#define ItemIdIsDead(itemId)
Definition itemid.h:113
int32 ItemPointerCompare(const ItemPointerData *arg1, const ItemPointerData *arg2)
Definition itemptr.c:51
bool ItemPointerEquals(const ItemPointerData *pointer1, const ItemPointerData *pointer2)
Definition itemptr.c:35
static void ItemPointerSet(ItemPointerData *pointer, BlockNumber blockNumber, OffsetNumber offNum)
Definition itemptr.h:135
static void ItemPointerSetOffsetNumber(ItemPointerData *pointer, OffsetNumber offsetNumber)
Definition itemptr.h:158
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
Definition itemptr.h:124
static bool ItemPointerIndicatesMovedPartitions(const ItemPointerData *pointer)
Definition itemptr.h:197
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition itemptr.h:103
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
Definition itemptr.h:172
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition itemptr.h:83
bool ConditionalXactLockTableWait(TransactionId xid, bool logLockFailure)
Definition lmgr.c:739
void XactLockTableWait(TransactionId xid, Relation rel, const ItemPointerData *ctid, XLTW_Oper oper)
Definition lmgr.c:663
@ XLTW_FetchUpdated
Definition lmgr.h:33
@ XLTW_InsertIndexUnique
Definition lmgr.h:32
bool log_lock_failures
Definition lock.c:54
LockWaitPolicy
Definition lockoptions.h:38
@ LockWaitSkip
Definition lockoptions.h:42
@ LockWaitBlock
Definition lockoptions.h:40
@ LockWaitError
Definition lockoptions.h:44
LockTupleMode
Definition lockoptions.h:51
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:403
void pfree(void *pointer)
Definition mcxt.c:1616
#define IsBootstrapProcessingMode()
Definition miscadmin.h:477
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:123
MultiXactId GetOldestMultiXactId(void)
Definition multixact.c:2303
#define InvalidOffsetNumber
Definition off.h:26
#define OffsetNumberIsValid(offsetNumber)
Definition off.h:39
#define OffsetNumberNext(offsetNumber)
Definition off.h:52
uint16 OffsetNumber
Definition off.h:24
#define FirstOffsetNumber
Definition off.h:27
FormData_pg_attribute * Form_pg_attribute
#define ERRCODE_DATA_CORRUPTED
static PgChecksumMode mode
#define INDEX_MAX_KEYS
#define NIL
Definition pg_list.h:68
static char buf[DEFAULT_XLOG_SEG_SIZE]
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition pgbench.c:77
static int progress
Definition pgbench.c:262
#define pgstat_count_heap_fetch(rel)
Definition pgstat.h:704
#define pgstat_count_heap_getnext(rel)
Definition pgstat.h:699
static int64 DatumGetInt64(Datum X)
Definition postgres.h:413
uint64_t Datum
Definition postgres.h:70
unsigned int Oid
void PredicateLockTID(Relation relation, const ItemPointerData *tid, Snapshot snapshot, TransactionId tuple_xid)
Definition predicate.c:2619
static int fb(int x)
TransactionId GetOldestNonRemovableTransactionId(Relation rel)
Definition procarray.c:1956
#define PROGRESS_CLUSTER_INDEX_RELID
Definition progress.h:79
#define PROGRESS_CLUSTER_HEAP_BLKS_SCANNED
Definition progress.h:83
#define PROGRESS_CLUSTER_PHASE_SORT_TUPLES
Definition progress.h:89
#define PROGRESS_CLUSTER_PHASE_SEQ_SCAN_HEAP
Definition progress.h:87
#define PROGRESS_SCAN_BLOCKS_DONE
Definition progress.h:142
#define PROGRESS_CLUSTER_PHASE
Definition progress.h:78
#define PROGRESS_CLUSTER_HEAP_TUPLES_SCANNED
Definition progress.h:80
#define PROGRESS_CLUSTER_TOTAL_HEAP_BLKS
Definition progress.h:82
#define PROGRESS_CLUSTER_HEAP_TUPLES_WRITTEN
Definition progress.h:81
#define PROGRESS_CLUSTER_PHASE_INDEX_SCAN_HEAP
Definition progress.h:88
#define PROGRESS_SCAN_BLOCKS_TOTAL
Definition progress.h:141
#define PROGRESS_CLUSTER_PHASE_WRITE_NEW_HEAP
Definition progress.h:90
void heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
Definition pruneheap.c:1895
void heap_page_prune_opt(Relation relation, Buffer buffer)
Definition pruneheap.c:209
Buffer read_stream_next_buffer(ReadStream *stream, void **per_buffer_data)
#define RelationGetRelid(relation)
Definition rel.h:514
static SMgrRelation RelationGetSmgr(Relation rel)
Definition rel.h:576
#define RelationGetDescr(relation)
Definition rel.h:540
#define RelationGetRelationName(relation)
Definition rel.h:548
#define RelationGetTargetBlock(relation)
Definition rel.h:610
#define RelationIsPermanent(relation)
Definition rel.h:626
ForkNumber
Definition relpath.h:56
@ MAIN_FORKNUM
Definition relpath.h:58
@ INIT_FORKNUM
Definition relpath.h:61
#define MAX_FORKNUM
Definition relpath.h:70
struct ParallelBlockTableScanDescData * ParallelBlockTableScanDesc
Definition relscan.h:103
void end_heap_rewrite(RewriteState state)
bool rewrite_heap_dead_tuple(RewriteState state, HeapTuple old_tuple)
RewriteState begin_heap_rewrite(Relation old_heap, Relation new_heap, TransactionId oldest_xmin, TransactionId freeze_xid, MultiXactId cutoff_multi)
void rewrite_heap_tuple(RewriteState state, HeapTuple old_tuple, HeapTuple new_tuple)
@ ForwardScanDirection
Definition sdir.h:28
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
Definition smgr.c:481
void smgrclose(SMgrRelation reln)
Definition smgr.c:374
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition smgr.c:462
TransactionId RecentXmin
Definition snapmgr.c:160
Snapshot GetTransactionSnapshot(void)
Definition snapmgr.c:272
void UnregisterSnapshot(Snapshot snapshot)
Definition snapmgr.c:866
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition snapmgr.c:824
#define SnapshotAny
Definition snapmgr.h:33
#define InitDirtySnapshot(snapshotdata)
Definition snapmgr.h:42
#define IsMVCCSnapshot(snapshot)
Definition snapmgr.h:55
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
Definition storage.c:478
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
Definition storage.c:122
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
Definition storage.c:187
void RelationDropStorage(Relation rel)
Definition storage.c:207
void RelationTruncate(Relation rel, BlockNumber nblocks)
Definition storage.c:289
MemoryContext ecxt_per_tuple_memory
Definition execnodes.h:283
TupleTableSlot * ecxt_scantuple
Definition execnodes.h:275
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
ItemPointerData t_ctid
bool ii_Unique
Definition execnodes.h:202
bool ii_BrokenHotChain
Definition execnodes.h:214
ExprState * ii_PredicateState
Definition execnodes.h:187
Oid * ii_ExclusionOps
Definition execnodes.h:190
bool ii_Concurrent
Definition execnodes.h:212
List * ii_ExpressionsState
Definition execnodes.h:182
List * ii_Predicate
Definition execnodes.h:185
TupleDesc rd_att
Definition rel.h:112
Oid rd_id
Definition rel.h:113
Form_pg_class rd_rel
Definition rel.h:111
bool takenDuringRecovery
Definition snapshot.h:180
TransactionId xmax
Definition tableam.h:150
CommandId cmax
Definition tableam.h:151
ItemPointerData ctid
Definition tableam.h:149
NodeTag type
Definition tableam.h:292
Relation rs_rd
Definition relscan.h:35
uint32 rs_flags
Definition relscan.h:63
struct SnapshotData * rs_snapshot
Definition relscan.h:36
ItemPointerData tts_tid
Definition tuptable.h:128
void ss_report_location(Relation rel, BlockNumber location)
Definition syncscan.c:289
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition tableam.c:92
Size table_block_parallelscan_initialize(Relation rel, ParallelTableScanDesc pscan)
Definition tableam.c:412
void table_block_parallelscan_reinitialize(Relation rel, ParallelTableScanDesc pscan)
Definition tableam.c:431
uint64 table_block_relation_size(Relation rel, ForkNumber forkNumber)
Definition tableam.c:679
Size table_block_parallelscan_estimate(Relation rel)
Definition tableam.c:406
void table_block_relation_estimate_size(Relation rel, int32 *attr_widths, BlockNumber *pages, double *tuples, double *allvisfrac, Size overhead_bytes_per_tuple, Size usable_bytes_per_page)
Definition tableam.c:716
@ SO_ALLOW_PAGEMODE
Definition tableam.h:62
@ SO_ALLOW_SYNC
Definition tableam.h:60
@ SO_TYPE_BITMAPSCAN
Definition tableam.h:50
TU_UpdateIndexes
Definition tableam.h:111
@ TU_Summarizing
Definition tableam.h:119
@ TU_All
Definition tableam.h:116
@ TU_None
Definition tableam.h:113
static void table_endscan(TableScanDesc scan)
Definition tableam.h:1004
TM_Result
Definition tableam.h:73
@ TM_Ok
Definition tableam.h:78
@ TM_Deleted
Definition tableam.h:93
@ TM_WouldBlock
Definition tableam.h:103
@ TM_Updated
Definition tableam.h:90
@ TM_SelfModified
Definition tableam.h:84
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition tableam.h:267
void(* IndexBuildCallback)(Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition tableam.h:271
static TableScanDesc table_beginscan_strat(Relation rel, Snapshot snapshot, int nkeys, ScanKeyData *key, bool allow_strat, bool allow_sync)
Definition tableam.h:920
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition tableam.h:1039
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, ScanKeyData *key)
Definition tableam.h:896
#define TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS
Definition tableam.h:265
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
int tbm_extract_page_tuple(TBMIterateResult *iteritem, OffsetNumber *offsets, uint32 max_offsets)
Definition tidbitmap.c:899
#define TBM_MAX_TUPLES_PER_PAGE
Definition tidbitmap.h:34
#define InvalidTransactionId
Definition transam.h:31
#define TransactionIdEquals(id1, id2)
Definition transam.h:43
#define TransactionIdIsValid(xid)
Definition transam.h:41
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:160
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:175
void tuplesort_performsort(Tuplesortstate *state)
Definition tuplesort.c:1259
void tuplesort_end(Tuplesortstate *state)
Definition tuplesort.c:847
#define TUPLESORT_NONE
Definition tuplesort.h:67
HeapTuple tuplesort_getheaptuple(Tuplesortstate *state, bool forward)
Tuplesortstate * tuplesort_begin_cluster(TupleDesc tupDesc, Relation indexRel, int workMem, SortCoordinate coordinate, int sortopt)
void tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
bool tuplesort_getdatum(Tuplesortstate *state, bool forward, bool copy, Datum *val, bool *isNull, Datum *abbrev)
#define att_align_nominal(cur_offset, attalign)
Definition tupmacs.h:182
#define TTS_IS_BUFFERTUPLE(slot)
Definition tuptable.h:240
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:457
void heap_vacuum_rel(Relation rel, const VacuumParams params, BufferAccessStrategy bstrategy)
Definition vacuumlazy.c:626
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition xact.c:942