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;
864 /* fall through */
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;
1064
1065 itemid = PageGetItemId(targpage, hscan->rs_cindex);
1066
1067 /*
1068 * We ignore unused and redirect line pointers. DEAD line pointers
1069 * should be counted as dead, because we need vacuum to run to get rid
1070 * of them. Note that this rule agrees with the way that
1071 * heap_page_prune_and_freeze() counts things.
1072 */
1073 if (!ItemIdIsNormal(itemid))
1074 {
1075 if (ItemIdIsDead(itemid))
1076 *deadrows += 1;
1077 continue;
1078 }
1079
1080 ItemPointerSet(&targtuple->t_self, hscan->rs_cblock, hscan->rs_cindex);
1081
1082 targtuple->t_tableOid = RelationGetRelid(scan->rs_rd);
1083 targtuple->t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
1084 targtuple->t_len = ItemIdGetLength(itemid);
1085
1086 switch (HeapTupleSatisfiesVacuum(targtuple, OldestXmin,
1087 hscan->rs_cbuf))
1088 {
1089 case HEAPTUPLE_LIVE:
1090 sample_it = true;
1091 *liverows += 1;
1092 break;
1093
1094 case HEAPTUPLE_DEAD:
1096 /* Count dead and recently-dead rows */
1097 *deadrows += 1;
1098 break;
1099
1101
1102 /*
1103 * Insert-in-progress rows are not counted. We assume that
1104 * when the inserting transaction commits or aborts, it will
1105 * send a stats message to increment the proper count. This
1106 * works right only if that transaction ends after we finish
1107 * analyzing the table; if things happen in the other order,
1108 * its stats update will be overwritten by ours. However, the
1109 * error will be large only if the other transaction runs long
1110 * enough to insert many tuples, so assuming it will finish
1111 * after us is the safer option.
1112 *
1113 * A special case is that the inserting transaction might be
1114 * our own. In this case we should count and sample the row,
1115 * to accommodate users who load a table and analyze it in one
1116 * transaction. (pgstat_report_analyze has to adjust the
1117 * numbers we report to the cumulative stats system to make
1118 * this come out right.)
1119 */
1121 {
1122 sample_it = true;
1123 *liverows += 1;
1124 }
1125 break;
1126
1128
1129 /*
1130 * We count and sample delete-in-progress rows the same as
1131 * live ones, so that the stats counters come out right if the
1132 * deleting transaction commits after us, per the same
1133 * reasoning given above.
1134 *
1135 * If the delete was done by our own transaction, however, we
1136 * must count the row as dead to make pgstat_report_analyze's
1137 * stats adjustments come out right. (Note: this works out
1138 * properly when the row was both inserted and deleted in our
1139 * xact.)
1140 *
1141 * The net effect of these choices is that we act as though an
1142 * IN_PROGRESS transaction hasn't happened yet, except if it
1143 * is our own transaction, which we assume has happened.
1144 *
1145 * This approach ensures that we behave sanely if we see both
1146 * the pre-image and post-image rows for a row being updated
1147 * by a concurrent transaction: we will sample the pre-image
1148 * but not the post-image. We also get sane results if the
1149 * concurrent transaction never commits.
1150 */
1152 *deadrows += 1;
1153 else
1154 {
1155 sample_it = true;
1156 *liverows += 1;
1157 }
1158 break;
1159
1160 default:
1161 elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
1162 break;
1163 }
1164
1165 if (sample_it)
1166 {
1167 ExecStoreBufferHeapTuple(targtuple, slot, hscan->rs_cbuf);
1168 hscan->rs_cindex++;
1169
1170 /* note that we leave the buffer locked here! */
1171 return true;
1172 }
1173 }
1174
1175 /* Now release the lock and pin on the page */
1176 UnlockReleaseBuffer(hscan->rs_cbuf);
1177 hscan->rs_cbuf = InvalidBuffer;
1178
1179 /* also prevent old slot contents from having pin on page */
1180 ExecClearTuple(slot);
1181
1182 return false;
1183}
1184
1185static double
1187 Relation indexRelation,
1188 IndexInfo *indexInfo,
1189 bool allow_sync,
1190 bool anyvisible,
1191 bool progress,
1195 void *callback_state,
1196 TableScanDesc scan)
1197{
1199 bool is_system_catalog;
1203 bool isnull[INDEX_MAX_KEYS];
1204 double reltuples;
1206 TupleTableSlot *slot;
1207 EState *estate;
1208 ExprContext *econtext;
1209 Snapshot snapshot;
1210 bool need_unregister_snapshot = false;
1211 TransactionId OldestXmin;
1213 BlockNumber root_blkno = InvalidBlockNumber;
1215
1216 /*
1217 * sanity checks
1218 */
1219 Assert(OidIsValid(indexRelation->rd_rel->relam));
1220
1221 /* Remember if it's a system catalog */
1222 is_system_catalog = IsSystemRelation(heapRelation);
1223
1224 /* See whether we're verifying uniqueness/exclusion properties */
1225 checking_uniqueness = (indexInfo->ii_Unique ||
1226 indexInfo->ii_ExclusionOps != NULL);
1227
1228 /*
1229 * "Any visible" mode is not compatible with uniqueness checks; make sure
1230 * only one of those is requested.
1231 */
1233
1234 /*
1235 * Need an EState for evaluation of index expressions and partial-index
1236 * predicates. Also a slot to hold the current tuple.
1237 */
1238 estate = CreateExecutorState();
1239 econtext = GetPerTupleExprContext(estate);
1240 slot = table_slot_create(heapRelation, NULL);
1241
1242 /* Arrange for econtext's scan tuple to be the tuple under test */
1243 econtext->ecxt_scantuple = slot;
1244
1245 /* Set up execution state for predicate, if any. */
1246 predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
1247
1248 /*
1249 * Prepare for scan of the base relation. In a normal index build, we use
1250 * SnapshotAny because we must retrieve all tuples and do our own time
1251 * qual checks (because we have to index RECENTLY_DEAD tuples). In a
1252 * concurrent build, or during bootstrap, we take a regular MVCC snapshot
1253 * and index whatever's live according to that.
1254 */
1255 OldestXmin = InvalidTransactionId;
1256
1257 /* okay to ignore lazy VACUUMs here */
1258 if (!IsBootstrapProcessingMode() && !indexInfo->ii_Concurrent)
1259 OldestXmin = GetOldestNonRemovableTransactionId(heapRelation);
1260
1261 if (!scan)
1262 {
1263 /*
1264 * Serial index build.
1265 *
1266 * Must begin our own heap scan in this case. We may also need to
1267 * register a snapshot whose lifetime is under our direct control.
1268 */
1269 if (!TransactionIdIsValid(OldestXmin))
1270 {
1273 }
1274 else
1275 snapshot = SnapshotAny;
1276
1277 scan = table_beginscan_strat(heapRelation, /* relation */
1278 snapshot, /* snapshot */
1279 0, /* number of keys */
1280 NULL, /* scan key */
1281 true, /* buffer access strategy OK */
1282 allow_sync); /* syncscan OK? */
1283 }
1284 else
1285 {
1286 /*
1287 * Parallel index build.
1288 *
1289 * Parallel case never registers/unregisters own snapshot. Snapshot
1290 * is taken from parallel heap scan, and is SnapshotAny or an MVCC
1291 * snapshot, based on same criteria as serial case.
1292 */
1295 snapshot = scan->rs_snapshot;
1296 }
1297
1298 hscan = (HeapScanDesc) scan;
1299
1300 /*
1301 * Must have called GetOldestNonRemovableTransactionId() if using
1302 * SnapshotAny. Shouldn't have for an MVCC snapshot. (It's especially
1303 * worth checking this for parallel builds, since ambuild routines that
1304 * support parallel builds must work these details out for themselves.)
1305 */
1306 Assert(snapshot == SnapshotAny || IsMVCCSnapshot(snapshot));
1307 Assert(snapshot == SnapshotAny ? TransactionIdIsValid(OldestXmin) :
1308 !TransactionIdIsValid(OldestXmin));
1309 Assert(snapshot == SnapshotAny || !anyvisible);
1310
1311 /* Publish number of blocks to scan */
1312 if (progress)
1313 {
1314 BlockNumber nblocks;
1315
1316 if (hscan->rs_base.rs_parallel != NULL)
1317 {
1319
1320 pbscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
1321 nblocks = pbscan->phs_nblocks;
1322 }
1323 else
1324 nblocks = hscan->rs_nblocks;
1325
1327 nblocks);
1328 }
1329
1330 /* set our scan endpoints */
1331 if (!allow_sync)
1333 else
1334 {
1335 /* syncscan can only be requested on whole relation */
1336 Assert(start_blockno == 0);
1338 }
1339
1340 reltuples = 0;
1341
1342 /*
1343 * Scan all tuples in the base relation.
1344 */
1345 while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1346 {
1347 bool tupleIsAlive;
1348
1350
1351 /* Report scan progress, if asked to. */
1352 if (progress)
1353 {
1355
1357 {
1359 blocks_done);
1361 }
1362 }
1363
1364 /*
1365 * When dealing with a HOT-chain of updated tuples, we want to index
1366 * the values of the live tuple (if any), but index it under the TID
1367 * of the chain's root tuple. This approach is necessary to preserve
1368 * the HOT-chain structure in the heap. So we need to be able to find
1369 * the root item offset for every tuple that's in a HOT-chain. When
1370 * first reaching a new page of the relation, call
1371 * heap_get_root_tuples() to build a map of root item offsets on the
1372 * page.
1373 *
1374 * It might look unsafe to use this information across buffer
1375 * lock/unlock. However, we hold ShareLock on the table so no
1376 * ordinary insert/update/delete should occur; and we hold pin on the
1377 * buffer continuously while visiting the page, so no pruning
1378 * operation can occur either.
1379 *
1380 * In cases with only ShareUpdateExclusiveLock on the table, it's
1381 * possible for some HOT tuples to appear that we didn't know about
1382 * when we first read the page. To handle that case, we re-obtain the
1383 * list of root offsets when a HOT tuple points to a root item that we
1384 * don't know about.
1385 *
1386 * Also, although our opinions about tuple liveness could change while
1387 * we scan the page (due to concurrent transaction commits/aborts),
1388 * the chain root locations won't, so this info doesn't need to be
1389 * rebuilt after waiting for another transaction.
1390 *
1391 * Note the implied assumption that there is no more than one live
1392 * tuple per HOT-chain --- else we could create more than one index
1393 * entry pointing to the same root tuple.
1394 */
1395 if (hscan->rs_cblock != root_blkno)
1396 {
1397 Page page = BufferGetPage(hscan->rs_cbuf);
1398
1402
1403 root_blkno = hscan->rs_cblock;
1404 }
1405
1406 if (snapshot == SnapshotAny)
1407 {
1408 /* do our own time qual check */
1409 bool indexIt;
1411
1412 recheck:
1413
1414 /*
1415 * We could possibly get away with not locking the buffer here,
1416 * since caller should hold ShareLock on the relation, but let's
1417 * be conservative about it. (This remark is still correct even
1418 * with HOT-pruning: our pin on the buffer prevents pruning.)
1419 */
1421
1422 /*
1423 * The criteria for counting a tuple as live in this block need to
1424 * match what analyze.c's heapam_scan_analyze_next_tuple() does,
1425 * otherwise CREATE INDEX and ANALYZE may produce wildly different
1426 * reltuples values, e.g. when there are many recently-dead
1427 * tuples.
1428 */
1429 switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin,
1430 hscan->rs_cbuf))
1431 {
1432 case HEAPTUPLE_DEAD:
1433 /* Definitely dead, we can ignore it */
1434 indexIt = false;
1435 tupleIsAlive = false;
1436 break;
1437 case HEAPTUPLE_LIVE:
1438 /* Normal case, index and unique-check it */
1439 indexIt = true;
1440 tupleIsAlive = true;
1441 /* Count it as live, too */
1442 reltuples += 1;
1443 break;
1445
1446 /*
1447 * If tuple is recently deleted then we must index it
1448 * anyway to preserve MVCC semantics. (Pre-existing
1449 * transactions could try to use the index after we finish
1450 * building it, and may need to see such tuples.)
1451 *
1452 * However, if it was HOT-updated then we must only index
1453 * the live tuple at the end of the HOT-chain. Since this
1454 * breaks semantics for pre-existing snapshots, mark the
1455 * index as unusable for them.
1456 *
1457 * We don't count recently-dead tuples in reltuples, even
1458 * if we index them; see heapam_scan_analyze_next_tuple().
1459 */
1461 {
1462 indexIt = false;
1463 /* mark the index as unsafe for old snapshots */
1464 indexInfo->ii_BrokenHotChain = true;
1465 }
1466 else
1467 indexIt = true;
1468 /* In any case, exclude the tuple from unique-checking */
1469 tupleIsAlive = false;
1470 break;
1472
1473 /*
1474 * In "anyvisible" mode, this tuple is visible and we
1475 * don't need any further checks.
1476 */
1477 if (anyvisible)
1478 {
1479 indexIt = true;
1480 tupleIsAlive = true;
1481 reltuples += 1;
1482 break;
1483 }
1484
1485 /*
1486 * Since caller should hold ShareLock or better, normally
1487 * the only way to see this is if it was inserted earlier
1488 * in our own transaction. However, it can happen in
1489 * system catalogs, since we tend to release write lock
1490 * before commit there. Give a warning if neither case
1491 * applies.
1492 */
1495 {
1496 if (!is_system_catalog)
1497 elog(WARNING, "concurrent insert in progress within table \"%s\"",
1498 RelationGetRelationName(heapRelation));
1499
1500 /*
1501 * If we are performing uniqueness checks, indexing
1502 * such a tuple could lead to a bogus uniqueness
1503 * failure. In that case we wait for the inserting
1504 * transaction to finish and check again.
1505 */
1507 {
1508 /*
1509 * Must drop the lock on the buffer before we wait
1510 */
1512 XactLockTableWait(xwait, heapRelation,
1513 &heapTuple->t_self,
1516 goto recheck;
1517 }
1518 }
1519 else
1520 {
1521 /*
1522 * For consistency with
1523 * heapam_scan_analyze_next_tuple(), count
1524 * HEAPTUPLE_INSERT_IN_PROGRESS tuples as live only
1525 * when inserted by our own transaction.
1526 */
1527 reltuples += 1;
1528 }
1529
1530 /*
1531 * We must index such tuples, since if the index build
1532 * commits then they're good.
1533 */
1534 indexIt = true;
1535 tupleIsAlive = true;
1536 break;
1538
1539 /*
1540 * As with INSERT_IN_PROGRESS case, this is unexpected
1541 * unless it's our own deletion or a system catalog; but
1542 * in anyvisible mode, this tuple is visible.
1543 */
1544 if (anyvisible)
1545 {
1546 indexIt = true;
1547 tupleIsAlive = false;
1548 reltuples += 1;
1549 break;
1550 }
1551
1554 {
1555 if (!is_system_catalog)
1556 elog(WARNING, "concurrent delete in progress within table \"%s\"",
1557 RelationGetRelationName(heapRelation));
1558
1559 /*
1560 * If we are performing uniqueness checks, assuming
1561 * the tuple is dead could lead to missing a
1562 * uniqueness violation. In that case we wait for the
1563 * deleting transaction to finish and check again.
1564 *
1565 * Also, if it's a HOT-updated tuple, we should not
1566 * index it but rather the live tuple at the end of
1567 * the HOT-chain. However, the deleting transaction
1568 * could abort, possibly leaving this tuple as live
1569 * after all, in which case it has to be indexed. The
1570 * only way to know what to do is to wait for the
1571 * deleting transaction to finish and check again.
1572 */
1573 if (checking_uniqueness ||
1575 {
1576 /*
1577 * Must drop the lock on the buffer before we wait
1578 */
1580 XactLockTableWait(xwait, heapRelation,
1581 &heapTuple->t_self,
1584 goto recheck;
1585 }
1586
1587 /*
1588 * Otherwise index it but don't check for uniqueness,
1589 * the same as a RECENTLY_DEAD tuple.
1590 */
1591 indexIt = true;
1592
1593 /*
1594 * Count HEAPTUPLE_DELETE_IN_PROGRESS tuples as live,
1595 * if they were not deleted by the current
1596 * transaction. That's what
1597 * heapam_scan_analyze_next_tuple() does, and we want
1598 * the behavior to be consistent.
1599 */
1600 reltuples += 1;
1601 }
1603 {
1604 /*
1605 * It's a HOT-updated tuple deleted by our own xact.
1606 * We can assume the deletion will commit (else the
1607 * index contents don't matter), so treat the same as
1608 * RECENTLY_DEAD HOT-updated tuples.
1609 */
1610 indexIt = false;
1611 /* mark the index as unsafe for old snapshots */
1612 indexInfo->ii_BrokenHotChain = true;
1613 }
1614 else
1615 {
1616 /*
1617 * It's a regular tuple deleted by our own xact. Index
1618 * it, but don't check for uniqueness nor count in
1619 * reltuples, the same as a RECENTLY_DEAD tuple.
1620 */
1621 indexIt = true;
1622 }
1623 /* In any case, exclude the tuple from unique-checking */
1624 tupleIsAlive = false;
1625 break;
1626 default:
1627 elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
1628 indexIt = tupleIsAlive = false; /* keep compiler quiet */
1629 break;
1630 }
1631
1633
1634 if (!indexIt)
1635 continue;
1636 }
1637 else
1638 {
1639 /* heap_getnext did the time qual check */
1640 tupleIsAlive = true;
1641 reltuples += 1;
1642 }
1643
1645
1646 /* Set up for predicate or expression evaluation */
1647 ExecStoreBufferHeapTuple(heapTuple, slot, hscan->rs_cbuf);
1648
1649 /*
1650 * In a partial index, discard tuples that don't satisfy the
1651 * predicate.
1652 */
1653 if (predicate != NULL)
1654 {
1655 if (!ExecQual(predicate, econtext))
1656 continue;
1657 }
1658
1659 /*
1660 * For the current heap tuple, extract all the attributes we use in
1661 * this index, and note which are null. This also performs evaluation
1662 * of any expressions needed.
1663 */
1664 FormIndexDatum(indexInfo,
1665 slot,
1666 estate,
1667 values,
1668 isnull);
1669
1670 /*
1671 * You'd think we should go ahead and build the index tuple here, but
1672 * some index AMs want to do further processing on the data first. So
1673 * pass the values[] and isnull[] arrays, instead.
1674 */
1675
1677 {
1678 /*
1679 * For a heap-only tuple, pretend its TID is that of the root. See
1680 * src/backend/access/heap/README.HOT for discussion.
1681 */
1682 ItemPointerData tid;
1683 OffsetNumber offnum;
1684
1685 offnum = ItemPointerGetOffsetNumber(&heapTuple->t_self);
1686
1687 /*
1688 * If a HOT tuple points to a root that we don't know about,
1689 * obtain root items afresh. If that still fails, report it as
1690 * corruption.
1691 */
1692 if (root_offsets[offnum - 1] == InvalidOffsetNumber)
1693 {
1694 Page page = BufferGetPage(hscan->rs_cbuf);
1695
1699 }
1700
1701 if (!OffsetNumberIsValid(root_offsets[offnum - 1]))
1702 ereport(ERROR,
1704 errmsg_internal("failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
1706 offnum,
1707 RelationGetRelationName(heapRelation))));
1708
1710 root_offsets[offnum - 1]);
1711
1712 /* Call the AM's callback routine to process the tuple */
1713 callback(indexRelation, &tid, values, isnull, tupleIsAlive,
1714 callback_state);
1715 }
1716 else
1717 {
1718 /* Call the AM's callback routine to process the tuple */
1719 callback(indexRelation, &heapTuple->t_self, values, isnull,
1720 tupleIsAlive, callback_state);
1721 }
1722 }
1723
1724 /* Report scan progress one last time. */
1725 if (progress)
1726 {
1728
1729 if (hscan->rs_base.rs_parallel != NULL)
1730 {
1732
1733 pbscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
1734 blks_done = pbscan->phs_nblocks;
1735 }
1736 else
1737 blks_done = hscan->rs_nblocks;
1738
1740 blks_done);
1741 }
1742
1743 table_endscan(scan);
1744
1745 /* we can now forget our snapshot, if set and registered by us */
1747 UnregisterSnapshot(snapshot);
1748
1750
1751 FreeExecutorState(estate);
1752
1753 /* These may have been pointing to the now-gone estate */
1754 indexInfo->ii_ExpressionsState = NIL;
1755 indexInfo->ii_PredicateState = NULL;
1756
1757 return reltuples;
1758}
1759
1760static void
1762 Relation indexRelation,
1763 IndexInfo *indexInfo,
1764 Snapshot snapshot,
1766{
1767 TableScanDesc scan;
1771 bool isnull[INDEX_MAX_KEYS];
1773 TupleTableSlot *slot;
1774 EState *estate;
1775 ExprContext *econtext;
1776 BlockNumber root_blkno = InvalidBlockNumber;
1780
1781 /* state variables for the merge */
1784 bool tuplesort_empty = false;
1785
1786 /*
1787 * sanity checks
1788 */
1789 Assert(OidIsValid(indexRelation->rd_rel->relam));
1790
1791 /*
1792 * Need an EState for evaluation of index expressions and partial-index
1793 * predicates. Also a slot to hold the current tuple.
1794 */
1795 estate = CreateExecutorState();
1796 econtext = GetPerTupleExprContext(estate);
1797 slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation),
1799
1800 /* Arrange for econtext's scan tuple to be the tuple under test */
1801 econtext->ecxt_scantuple = slot;
1802
1803 /* Set up execution state for predicate, if any. */
1804 predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
1805
1806 /*
1807 * Prepare for scan of the base relation. We need just those tuples
1808 * satisfying the passed-in reference snapshot. We must disable syncscan
1809 * here, because it's critical that we read from block zero forward to
1810 * match the sorted TIDs.
1811 */
1812 scan = table_beginscan_strat(heapRelation, /* relation */
1813 snapshot, /* snapshot */
1814 0, /* number of keys */
1815 NULL, /* scan key */
1816 true, /* buffer access strategy OK */
1817 false); /* syncscan not OK */
1818 hscan = (HeapScanDesc) scan;
1819
1821 hscan->rs_nblocks);
1822
1823 /*
1824 * Scan all tuples matching the snapshot.
1825 */
1826 while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1827 {
1828 ItemPointer heapcursor = &heapTuple->t_self;
1831
1833
1834 state->htups += 1;
1835
1837 (hscan->rs_cblock != previous_blkno))
1838 {
1840 hscan->rs_cblock);
1841 previous_blkno = hscan->rs_cblock;
1842 }
1843
1844 /*
1845 * As commented in table_index_build_scan, we should index heap-only
1846 * tuples under the TIDs of their root tuples; so when we advance onto
1847 * a new heap page, build a map of root item offsets on the page.
1848 *
1849 * This complicates merging against the tuplesort output: we will
1850 * visit the live tuples in order by their offsets, but the root
1851 * offsets that we need to compare against the index contents might be
1852 * ordered differently. So we might have to "look back" within the
1853 * tuplesort output, but only within the current page. We handle that
1854 * by keeping a bool array in_index[] showing all the
1855 * already-passed-over tuplesort output TIDs of the current page. We
1856 * clear that array here, when advancing onto a new heap page.
1857 */
1858 if (hscan->rs_cblock != root_blkno)
1859 {
1860 Page page = BufferGetPage(hscan->rs_cbuf);
1861
1865
1866 memset(in_index, 0, sizeof(in_index));
1867
1868 root_blkno = hscan->rs_cblock;
1869 }
1870
1871 /* Convert actual tuple TID to root TID */
1874
1876 {
1879 ereport(ERROR,
1881 errmsg_internal("failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
1884 RelationGetRelationName(heapRelation))));
1886 }
1887
1888 /*
1889 * "merge" by skipping through the index tuples until we find or pass
1890 * the current root tuple.
1891 */
1892 while (!tuplesort_empty &&
1893 (!indexcursor ||
1895 {
1896 Datum ts_val;
1897 bool ts_isnull;
1898
1899 if (indexcursor)
1900 {
1901 /*
1902 * Remember index items seen earlier on the current heap page
1903 */
1904 if (ItemPointerGetBlockNumber(indexcursor) == root_blkno)
1906 }
1907
1908 tuplesort_empty = !tuplesort_getdatum(state->tuplesort, true,
1909 false, &ts_val, &ts_isnull,
1910 NULL);
1912 if (!tuplesort_empty)
1913 {
1916 }
1917 else
1918 {
1919 /* Be tidy */
1920 indexcursor = NULL;
1921 }
1922 }
1923
1924 /*
1925 * If the tuplesort has overshot *and* we didn't see a match earlier,
1926 * then this tuple is missing from the index, so insert it.
1927 */
1928 if ((tuplesort_empty ||
1930 !in_index[root_offnum - 1])
1931 {
1933
1934 /* Set up for predicate or expression evaluation */
1935 ExecStoreHeapTuple(heapTuple, slot, false);
1936
1937 /*
1938 * In a partial index, discard tuples that don't satisfy the
1939 * predicate.
1940 */
1941 if (predicate != NULL)
1942 {
1943 if (!ExecQual(predicate, econtext))
1944 continue;
1945 }
1946
1947 /*
1948 * For the current heap tuple, extract all the attributes we use
1949 * in this index, and note which are null. This also performs
1950 * evaluation of any expressions needed.
1951 */
1952 FormIndexDatum(indexInfo,
1953 slot,
1954 estate,
1955 values,
1956 isnull);
1957
1958 /*
1959 * You'd think we should go ahead and build the index tuple here,
1960 * but some index AMs want to do further processing on the data
1961 * first. So pass the values[] and isnull[] arrays, instead.
1962 */
1963
1964 /*
1965 * If the tuple is already committed dead, you might think we
1966 * could suppress uniqueness checking, but this is no longer true
1967 * in the presence of HOT, because the insert is actually a proxy
1968 * for a uniqueness check on the whole HOT-chain. That is, the
1969 * tuple we have here could be dead because it was already
1970 * HOT-updated, and if so the updating transaction will not have
1971 * thought it should insert index entries. The index AM will
1972 * check the whole HOT-chain and correctly detect a conflict if
1973 * there is one.
1974 */
1975
1976 index_insert(indexRelation,
1977 values,
1978 isnull,
1979 &rootTuple,
1980 heapRelation,
1981 indexInfo->ii_Unique ?
1983 false,
1984 indexInfo);
1985
1986 state->tups_inserted += 1;
1987 }
1988 }
1989
1990 table_endscan(scan);
1991
1993
1994 FreeExecutorState(estate);
1995
1996 /* These may have been pointing to the now-gone estate */
1997 indexInfo->ii_ExpressionsState = NIL;
1998 indexInfo->ii_PredicateState = NULL;
1999}
2000
2001/*
2002 * Return the number of blocks that have been read by this scan since
2003 * starting. This is meant for progress reporting rather than be fully
2004 * accurate: in a parallel scan, workers can be concurrently reading blocks
2005 * further ahead than what we report.
2006 */
2007static BlockNumber
2009{
2011 BlockNumber startblock;
2013
2014 if (hscan->rs_base.rs_parallel != NULL)
2015 {
2016 bpscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
2017 startblock = bpscan->phs_startblock;
2018 }
2019 else
2020 startblock = hscan->rs_startblock;
2021
2022 /*
2023 * Might have wrapped around the end of the relation, if startblock was
2024 * not zero.
2025 */
2026 if (hscan->rs_cblock > startblock)
2027 blocks_done = hscan->rs_cblock - startblock;
2028 else
2029 {
2030 BlockNumber nblocks;
2031
2032 nblocks = bpscan != NULL ? bpscan->phs_nblocks : hscan->rs_nblocks;
2033 blocks_done = nblocks - startblock +
2034 hscan->rs_cblock;
2035 }
2036
2037 return blocks_done;
2038}
2039
2040
2041/* ------------------------------------------------------------------------
2042 * Miscellaneous callbacks for the heap AM
2043 * ------------------------------------------------------------------------
2044 */
2045
2046/*
2047 * Check to see whether the table needs a TOAST table. It does only if
2048 * (1) there are any toastable attributes, and (2) the maximum length
2049 * of a tuple could exceed TOAST_TUPLE_THRESHOLD. (We don't want to
2050 * create a toast table for something like "f1 varchar(20)".)
2051 */
2052static bool
2054{
2055 int32 data_length = 0;
2056 bool maxlength_unknown = false;
2057 bool has_toastable_attrs = false;
2058 TupleDesc tupdesc = rel->rd_att;
2060 int i;
2061
2062 for (i = 0; i < tupdesc->natts; i++)
2063 {
2065
2066 if (att->attisdropped)
2067 continue;
2068 if (att->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
2069 continue;
2070 data_length = att_align_nominal(data_length, att->attalign);
2071 if (att->attlen > 0)
2072 {
2073 /* Fixed-length types are never toastable */
2074 data_length += att->attlen;
2075 }
2076 else
2077 {
2078 int32 maxlen = type_maximum_size(att->atttypid,
2079 att->atttypmod);
2080
2081 if (maxlen < 0)
2082 maxlength_unknown = true;
2083 else
2084 data_length += maxlen;
2085 if (att->attstorage != TYPSTORAGE_PLAIN)
2086 has_toastable_attrs = true;
2087 }
2088 }
2090 return false; /* nothing to toast? */
2092 return true; /* any unlimited-length attrs? */
2094 BITMAPLEN(tupdesc->natts)) +
2095 MAXALIGN(data_length);
2097}
2098
2099/*
2100 * TOAST tables for heap relations are just heap relations.
2101 */
2102static Oid
2104{
2105 return rel->rd_rel->relam;
2106}
2107
2108
2109/* ------------------------------------------------------------------------
2110 * Planner related callbacks for the heap AM
2111 * ------------------------------------------------------------------------
2112 */
2113
2114#define HEAP_OVERHEAD_BYTES_PER_TUPLE \
2115 (MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))
2116#define HEAP_USABLE_BYTES_PER_PAGE \
2117 (BLCKSZ - SizeOfPageHeaderData)
2118
2119static void
2121 BlockNumber *pages, double *tuples,
2122 double *allvisfrac)
2123{
2125 tuples, allvisfrac,
2128}
2129
2130
2131/* ------------------------------------------------------------------------
2132 * Executor related callbacks for the heap AM
2133 * ------------------------------------------------------------------------
2134 */
2135
2136static bool
2138 TupleTableSlot *slot,
2139 bool *recheck,
2140 uint64 *lossy_pages,
2141 uint64 *exact_pages)
2142{
2146 Page page;
2147 ItemId lp;
2148
2149 /*
2150 * Out of range? If so, nothing more to look at on this page
2151 */
2152 while (hscan->rs_cindex >= hscan->rs_ntuples)
2153 {
2154 /*
2155 * Returns false if the bitmap is exhausted and there are no further
2156 * blocks we need to scan.
2157 */
2158 if (!BitmapHeapScanNextBlock(scan, recheck, lossy_pages, exact_pages))
2159 return false;
2160 }
2161
2162 targoffset = hscan->rs_vistuples[hscan->rs_cindex];
2163 page = BufferGetPage(hscan->rs_cbuf);
2164 lp = PageGetItemId(page, targoffset);
2166
2167 hscan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2168 hscan->rs_ctup.t_len = ItemIdGetLength(lp);
2169 hscan->rs_ctup.t_tableOid = scan->rs_rd->rd_id;
2170 ItemPointerSet(&hscan->rs_ctup.t_self, hscan->rs_cblock, targoffset);
2171
2173
2174 /*
2175 * Set up the result slot to point to this tuple. Note that the slot
2176 * acquires a pin on the buffer.
2177 */
2179 slot,
2180 hscan->rs_cbuf);
2181
2182 hscan->rs_cindex++;
2183
2184 return true;
2185}
2186
2187static bool
2189{
2191 TsmRoutine *tsm = scanstate->tsmroutine;
2192 BlockNumber blockno;
2193
2194 /* return false immediately if relation is empty */
2195 if (hscan->rs_nblocks == 0)
2196 return false;
2197
2198 /* release previous scan buffer, if any */
2199 if (BufferIsValid(hscan->rs_cbuf))
2200 {
2201 ReleaseBuffer(hscan->rs_cbuf);
2202 hscan->rs_cbuf = InvalidBuffer;
2203 }
2204
2205 if (tsm->NextSampleBlock)
2206 blockno = tsm->NextSampleBlock(scanstate, hscan->rs_nblocks);
2207 else
2208 {
2209 /* scanning table sequentially */
2210
2211 if (hscan->rs_cblock == InvalidBlockNumber)
2212 {
2213 Assert(!hscan->rs_inited);
2214 blockno = hscan->rs_startblock;
2215 }
2216 else
2217 {
2218 Assert(hscan->rs_inited);
2219
2220 blockno = hscan->rs_cblock + 1;
2221
2222 if (blockno >= hscan->rs_nblocks)
2223 {
2224 /* wrap to beginning of rel, might not have started at 0 */
2225 blockno = 0;
2226 }
2227
2228 /*
2229 * Report our new scan position for synchronization purposes.
2230 *
2231 * Note: we do this before checking for end of scan so that the
2232 * final state of the position hint is back at the start of the
2233 * rel. That's not strictly necessary, but otherwise when you run
2234 * the same query multiple times the starting position would shift
2235 * a little bit backwards on every invocation, which is confusing.
2236 * We don't guarantee any specific ordering in general, though.
2237 */
2238 if (scan->rs_flags & SO_ALLOW_SYNC)
2239 ss_report_location(scan->rs_rd, blockno);
2240
2241 if (blockno == hscan->rs_startblock)
2242 {
2243 blockno = InvalidBlockNumber;
2244 }
2245 }
2246 }
2247
2248 hscan->rs_cblock = blockno;
2249
2250 if (!BlockNumberIsValid(blockno))
2251 {
2252 hscan->rs_inited = false;
2253 return false;
2254 }
2255
2256 Assert(hscan->rs_cblock < hscan->rs_nblocks);
2257
2258 /*
2259 * Be sure to check for interrupts at least once per page. Checks at
2260 * higher code levels won't be able to stop a sample scan that encounters
2261 * many pages' worth of consecutive dead tuples.
2262 */
2264
2265 /* Read page using selected strategy */
2266 hscan->rs_cbuf = ReadBufferExtended(hscan->rs_base.rs_rd, MAIN_FORKNUM,
2267 blockno, RBM_NORMAL, hscan->rs_strategy);
2268
2269 /* in pagemode, prune the page and determine visible tuple offsets */
2270 if (hscan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
2272
2273 hscan->rs_inited = true;
2274 return true;
2275}
2276
2277static bool
2279 TupleTableSlot *slot)
2280{
2282 TsmRoutine *tsm = scanstate->tsmroutine;
2283 BlockNumber blockno = hscan->rs_cblock;
2284 bool pagemode = (scan->rs_flags & SO_ALLOW_PAGEMODE) != 0;
2285
2286 Page page;
2287 bool all_visible;
2289
2290 /*
2291 * When not using pagemode, we must lock the buffer during tuple
2292 * visibility checks.
2293 */
2294 if (!pagemode)
2296
2297 page = BufferGetPage(hscan->rs_cbuf);
2298 all_visible = PageIsAllVisible(page) &&
2301
2302 for (;;)
2303 {
2305
2307
2308 /* Ask the tablesample method which tuples to check on this page. */
2309 tupoffset = tsm->NextSampleTuple(scanstate,
2310 blockno,
2311 maxoffset);
2312
2314 {
2315 ItemId itemid;
2316 bool visible;
2317 HeapTuple tuple = &(hscan->rs_ctup);
2318
2319 /* Skip invalid tuple pointers. */
2320 itemid = PageGetItemId(page, tupoffset);
2321 if (!ItemIdIsNormal(itemid))
2322 continue;
2323
2324 tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
2325 tuple->t_len = ItemIdGetLength(itemid);
2326 ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
2327
2328
2329 if (all_visible)
2330 visible = true;
2331 else
2332 visible = SampleHeapTupleVisible(scan, hscan->rs_cbuf,
2333 tuple, tupoffset);
2334
2335 /* in pagemode, heap_prepare_pagescan did this for us */
2336 if (!pagemode)
2337 HeapCheckForSerializableConflictOut(visible, scan->rs_rd, tuple,
2338 hscan->rs_cbuf, scan->rs_snapshot);
2339
2340 /* Try next tuple from same page. */
2341 if (!visible)
2342 continue;
2343
2344 /* Found visible tuple, return it. */
2345 if (!pagemode)
2347
2348 ExecStoreBufferHeapTuple(tuple, slot, hscan->rs_cbuf);
2349
2350 /* Count successfully-fetched tuples as heap fetches */
2352
2353 return true;
2354 }
2355 else
2356 {
2357 /*
2358 * If we get here, it means we've exhausted the items on this page
2359 * and it's time to move to the next.
2360 */
2361 if (!pagemode)
2363
2364 ExecClearTuple(slot);
2365 return false;
2366 }
2367 }
2368
2369 Assert(0);
2370}
2371
2372
2373/* ----------------------------------------------------------------------------
2374 * Helper functions for the above.
2375 * ----------------------------------------------------------------------------
2376 */
2377
2378/*
2379 * Reconstruct and rewrite the given tuple
2380 *
2381 * We cannot simply copy the tuple as-is, for several reasons:
2382 *
2383 * 1. We'd like to squeeze out the values of any dropped columns, both
2384 * to save space and to ensure we have no corner-case failures. (It's
2385 * possible for example that the new table hasn't got a TOAST table
2386 * and so is unable to store any large values of dropped cols.)
2387 *
2388 * 2. The tuple might not even be legal for the new table; this is
2389 * currently only known to happen as an after-effect of ALTER TABLE
2390 * SET WITHOUT OIDS.
2391 *
2392 * So, we must reconstruct the tuple from component Datums.
2393 */
2394static void
2397 Datum *values, bool *isnull, RewriteState rwstate)
2398{
2402 int i;
2403
2404 heap_deform_tuple(tuple, oldTupDesc, values, isnull);
2405
2406 /* Be sure to null out any dropped columns */
2407 for (i = 0; i < newTupDesc->natts; i++)
2408 {
2409 if (TupleDescCompactAttr(newTupDesc, i)->attisdropped)
2410 isnull[i] = true;
2411 }
2412
2414
2415 /* The heap rewrite module does the rest */
2417
2419}
2420
2421/*
2422 * Check visibility of the tuple.
2423 */
2424static bool
2426 HeapTuple tuple,
2428{
2430
2431 if (scan->rs_flags & SO_ALLOW_PAGEMODE)
2432 {
2433 uint32 start = 0,
2434 end = hscan->rs_ntuples;
2435
2436 /*
2437 * In pageatatime mode, heap_prepare_pagescan() already did visibility
2438 * checks, so just look at the info it left in rs_vistuples[].
2439 *
2440 * We use a binary search over the known-sorted array. Note: we could
2441 * save some effort if we insisted that NextSampleTuple select tuples
2442 * in increasing order, but it's not clear that there would be enough
2443 * gain to justify the restriction.
2444 */
2445 while (start < end)
2446 {
2447 uint32 mid = start + (end - start) / 2;
2448 OffsetNumber curoffset = hscan->rs_vistuples[mid];
2449
2450 if (tupoffset == curoffset)
2451 return true;
2452 else if (tupoffset < curoffset)
2453 end = mid;
2454 else
2455 start = mid + 1;
2456 }
2457
2458 return false;
2459 }
2460 else
2461 {
2462 /* Otherwise, we have to check the tuple individually. */
2463 return HeapTupleSatisfiesVisibility(tuple, scan->rs_snapshot,
2464 buffer);
2465 }
2466}
2467
2468/*
2469 * Helper function get the next block of a bitmap heap scan. Returns true when
2470 * it got the next block and saved it in the scan descriptor and false when
2471 * the bitmap and or relation are exhausted.
2472 */
2473static bool
2475 bool *recheck,
2476 uint64 *lossy_pages, uint64 *exact_pages)
2477{
2480 BlockNumber block;
2481 void *per_buffer_data;
2482 Buffer buffer;
2483 Snapshot snapshot;
2484 int ntup;
2487 int noffsets = -1;
2488
2490 Assert(hscan->rs_read_stream);
2491
2492 hscan->rs_cindex = 0;
2493 hscan->rs_ntuples = 0;
2494
2495 /* Release buffer containing previous block. */
2496 if (BufferIsValid(hscan->rs_cbuf))
2497 {
2498 ReleaseBuffer(hscan->rs_cbuf);
2499 hscan->rs_cbuf = InvalidBuffer;
2500 }
2501
2502 hscan->rs_cbuf = read_stream_next_buffer(hscan->rs_read_stream,
2503 &per_buffer_data);
2504
2505 if (BufferIsInvalid(hscan->rs_cbuf))
2506 {
2507 /* the bitmap is exhausted */
2508 return false;
2509 }
2510
2511 Assert(per_buffer_data);
2512
2513 tbmres = per_buffer_data;
2514
2515 Assert(BlockNumberIsValid(tbmres->blockno));
2516 Assert(BufferGetBlockNumber(hscan->rs_cbuf) == tbmres->blockno);
2517
2518 /* Exact pages need their tuple offsets extracted. */
2519 if (!tbmres->lossy)
2522
2523 *recheck = tbmres->recheck;
2524
2525 block = hscan->rs_cblock = tbmres->blockno;
2526 buffer = hscan->rs_cbuf;
2527 snapshot = scan->rs_snapshot;
2528
2529 ntup = 0;
2530
2531 /*
2532 * Prune and repair fragmentation for the whole page, if possible.
2533 */
2534 heap_page_prune_opt(scan->rs_rd, buffer);
2535
2536 /*
2537 * We must hold share lock on the buffer content while examining tuple
2538 * visibility. Afterwards, however, the tuples we have found to be
2539 * visible are guaranteed good as long as we hold the buffer pin.
2540 */
2542
2543 /*
2544 * We need two separate strategies for lossy and non-lossy cases.
2545 */
2546 if (!tbmres->lossy)
2547 {
2548 /*
2549 * Bitmap is non-lossy, so we just look through the offsets listed in
2550 * tbmres; but we have to follow any HOT chain starting at each such
2551 * offset.
2552 */
2553 int curslot;
2554
2555 /* We must have extracted the tuple offsets by now */
2556 Assert(noffsets > -1);
2557
2558 for (curslot = 0; curslot < noffsets; curslot++)
2559 {
2560 OffsetNumber offnum = offsets[curslot];
2561 ItemPointerData tid;
2563
2564 ItemPointerSet(&tid, block, offnum);
2565 if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
2566 &heapTuple, NULL, true))
2567 hscan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
2568 }
2569 }
2570 else
2571 {
2572 /*
2573 * Bitmap is lossy, so we must examine each line pointer on the page.
2574 * But we can ignore HOT chains, since we'll check each tuple anyway.
2575 */
2576 Page page = BufferGetPage(buffer);
2577 OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
2578 OffsetNumber offnum;
2579
2580 for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
2581 {
2582 ItemId lp;
2584 bool valid;
2585
2586 lp = PageGetItemId(page, offnum);
2587 if (!ItemIdIsNormal(lp))
2588 continue;
2589 loctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2590 loctup.t_len = ItemIdGetLength(lp);
2591 loctup.t_tableOid = scan->rs_rd->rd_id;
2592 ItemPointerSet(&loctup.t_self, block, offnum);
2593 valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
2594 if (valid)
2595 {
2596 hscan->rs_vistuples[ntup++] = offnum;
2597 PredicateLockTID(scan->rs_rd, &loctup.t_self, snapshot,
2599 }
2601 buffer, snapshot);
2602 }
2603 }
2604
2606
2608 hscan->rs_ntuples = ntup;
2609
2610 if (tbmres->lossy)
2611 (*lossy_pages)++;
2612 else
2613 (*exact_pages)++;
2614
2615 /*
2616 * Return true to indicate that a valid block was found and the bitmap is
2617 * not exhausted. If there are no visible tuples on this page,
2618 * hscan->rs_ntuples will be 0 and heapam_scan_bitmap_next_tuple() will
2619 * return false returning control to this function to advance to the next
2620 * block in the bitmap.
2621 */
2622 return true;
2623}
2624
2625/* ------------------------------------------------------------------------
2626 * Definition of the heap table access method.
2627 * ------------------------------------------------------------------------
2628 */
2629
2632
2633 .slot_callbacks = heapam_slot_callbacks,
2634
2635 .scan_begin = heap_beginscan,
2636 .scan_end = heap_endscan,
2637 .scan_rescan = heap_rescan,
2638 .scan_getnextslot = heap_getnextslot,
2639
2640 .scan_set_tidrange = heap_set_tidrange,
2641 .scan_getnextslot_tidrange = heap_getnextslot_tidrange,
2642
2643 .parallelscan_estimate = table_block_parallelscan_estimate,
2644 .parallelscan_initialize = table_block_parallelscan_initialize,
2645 .parallelscan_reinitialize = table_block_parallelscan_reinitialize,
2646
2647 .index_fetch_begin = heapam_index_fetch_begin,
2648 .index_fetch_reset = heapam_index_fetch_reset,
2649 .index_fetch_end = heapam_index_fetch_end,
2650 .index_fetch_tuple = heapam_index_fetch_tuple,
2651
2652 .tuple_insert = heapam_tuple_insert,
2653 .tuple_insert_speculative = heapam_tuple_insert_speculative,
2654 .tuple_complete_speculative = heapam_tuple_complete_speculative,
2655 .multi_insert = heap_multi_insert,
2656 .tuple_delete = heapam_tuple_delete,
2657 .tuple_update = heapam_tuple_update,
2658 .tuple_lock = heapam_tuple_lock,
2659
2660 .tuple_fetch_row_version = heapam_fetch_row_version,
2661 .tuple_get_latest_tid = heap_get_latest_tid,
2662 .tuple_tid_valid = heapam_tuple_tid_valid,
2663 .tuple_satisfies_snapshot = heapam_tuple_satisfies_snapshot,
2664 .index_delete_tuples = heap_index_delete_tuples,
2665
2666 .relation_set_new_filelocator = heapam_relation_set_new_filelocator,
2667 .relation_nontransactional_truncate = heapam_relation_nontransactional_truncate,
2668 .relation_copy_data = heapam_relation_copy_data,
2669 .relation_copy_for_cluster = heapam_relation_copy_for_cluster,
2670 .relation_vacuum = heap_vacuum_rel,
2671 .scan_analyze_next_block = heapam_scan_analyze_next_block,
2672 .scan_analyze_next_tuple = heapam_scan_analyze_next_tuple,
2673 .index_build_range_scan = heapam_index_build_range_scan,
2674 .index_validate_scan = heapam_index_validate_scan,
2675
2676 .relation_size = table_block_relation_size,
2677 .relation_needs_toast_table = heapam_relation_needs_toast_table,
2678 .relation_toast_am = heapam_relation_toast_am,
2679 .relation_fetch_toast_slice = heap_fetch_toast_slice,
2680
2681 .relation_estimate_size = heapam_estimate_rel_size,
2682
2683 .scan_bitmap_next_tuple = heapam_scan_bitmap_next_tuple,
2684 .scan_sample_next_block = heapam_scan_sample_next_block,
2685 .scan_sample_next_tuple = heapam_scan_sample_next_tuple
2686};
2687
2688
2689const TableAmRoutine *
2691{
2692 return &heapam_methods;
2693}
2694
2695Datum
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:155
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:4356
Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation, BlockNumber blockNum)
Definition bufmgr.c:3121
void ReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5501
void UnlockReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5518
void FlushRelationBuffers(Relation rel)
Definition bufmgr.c:5077
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition bufmgr.c:911
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:826
uint8_t uint8
Definition c.h:544
#define Assert(condition)
Definition c.h:873
int64_t int64
Definition c.h:543
TransactionId MultiXactId
Definition c.h:676
int32_t int32
Definition c.h:542
uint64_t uint64
Definition c.h:547
uint32_t uint32
Definition c.h:546
uint32 CommandId
Definition c.h:680
uint32 TransactionId
Definition c.h:666
#define OidIsValid(objectId)
Definition c.h:788
bool IsSystemRelation(Relation relation)
Definition catalog.c:74
CommandId HeapTupleHeaderGetCmin(const HeapTupleHeaderData *tup)
Definition combocid.c:104
int errmsg_internal(const char *fmt,...)
Definition elog.c:1170
int errcode(int sqlerrcode)
Definition elog.c:863
int errmsg(const char *fmt,...)
Definition elog.c:1080
#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:2150
bool heap_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf)
Definition heapam.c:1667
void heap_finish_speculative(Relation relation, const ItemPointerData *tid)
Definition heapam.c:6176
bool heap_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition heapam.c:1457
void heap_endscan(TableScanDesc sscan)
Definition heapam.c:1369
void heap_rescan(TableScanDesc sscan, ScanKey key, bool set_params, bool allow_strat, bool allow_sync, bool allow_pagemode)
Definition heapam.c:1316
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:3320
TM_Result heap_delete(Relation relation, const ItemPointerData *tid, CommandId cid, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, bool changingPart)
Definition heapam.c:2851
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition heapam.c:1408
bool heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, bool *all_dead, bool first_call)
Definition heapam.c:1787
bool heap_getnextslot_tidrange(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition heapam.c:1560
void heap_set_tidrange(TableScanDesc sscan, ItemPointer mintid, ItemPointer maxtid)
Definition heapam.c:1487
void heap_abort_speculative(Relation relation, const ItemPointerData *tid)
Definition heapam.c:6263
TableScanDesc heap_beginscan(Relation relation, Snapshot snapshot, int nkeys, ScanKey key, ParallelTableScanDesc parallel_scan, uint32 flags)
Definition heapam.c:1162
void heap_prepare_pagescan(TableScanDesc sscan)
Definition heapam.c:614
TransactionId heap_index_delete_tuples(Relation rel, TM_IndexDeleteOp *delstate)
Definition heapam.c:8207
void heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples, CommandId cid, int options, BulkInsertState bistate)
Definition heapam.c:2421
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:4652
void heap_get_latest_tid(TableScanDesc sscan, ItemPointer tid)
Definition heapam.c:1939
void heap_setscanlimits(TableScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
Definition heapam.c:498
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition heapam.c:9334
#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 heapam_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin, double *liverows, double *deadrows, TupleTableSlot *slot)
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_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 HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
void heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize, int32 sliceoffset, int32 slicelength, struct 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:37
@ LockWaitSkip
Definition lockoptions.h:41
@ LockWaitBlock
Definition lockoptions.h:39
@ LockWaitError
Definition lockoptions.h:43
LockTupleMode
Definition lockoptions.h:50
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:700
#define pgstat_count_heap_getnext(rel)
Definition pgstat.h:695
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:1955
#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:420
void table_block_parallelscan_reinitialize(Relation rel, ParallelTableScanDesc pscan)
Definition tableam.c:439
uint64 table_block_relation_size(Relation rel, ForkNumber forkNumber)
Definition tableam.c:687
Size table_block_parallelscan_estimate(Relation rel)
Definition tableam.c:414
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:724
@ 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:985
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:900
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition tableam.h:1020
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, ScanKeyData *key)
Definition tableam.h:876
#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:1348
void tuplesort_end(Tuplesortstate *state)
Definition tuplesort.c:936
#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:145
#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:624
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition xact.c:942