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