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