LCOV - code coverage report
Current view: top level - src/backend/access/heap - heapam_handler.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 92.9 % 792 736
Test Date: 2026-02-17 17:20:33 Functions: 100.0 % 34 34
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1