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.3 % 792 731
Test Date: 2026-03-11 06:14:41 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     14566193 : heapam_slot_callbacks(Relation relation)
      71              : {
      72     14566193 :     return &TTSOpsBufferHeapTuple;
      73              : }
      74              : 
      75              : 
      76              : /* ------------------------------------------------------------------------
      77              :  * Index Scan Callbacks for heap AM
      78              :  * ------------------------------------------------------------------------
      79              :  */
      80              : 
      81              : static IndexFetchTableData *
      82     13946401 : heapam_index_fetch_begin(Relation rel)
      83              : {
      84     13946401 :     IndexFetchHeapData *hscan = palloc0_object(IndexFetchHeapData);
      85              : 
      86     13946401 :     hscan->xs_base.rel = rel;
      87     13946401 :     hscan->xs_cbuf = InvalidBuffer;
      88              : 
      89     13946401 :     return &hscan->xs_base;
      90              : }
      91              : 
      92              : static void
      93     26336399 : heapam_index_fetch_reset(IndexFetchTableData *scan)
      94              : {
      95     26336399 :     IndexFetchHeapData *hscan = (IndexFetchHeapData *) scan;
      96              : 
      97     26336399 :     if (BufferIsValid(hscan->xs_cbuf))
      98              :     {
      99     11860690 :         ReleaseBuffer(hscan->xs_cbuf);
     100     11860690 :         hscan->xs_cbuf = InvalidBuffer;
     101              :     }
     102     26336399 : }
     103              : 
     104              : static void
     105     13945498 : heapam_index_fetch_end(IndexFetchTableData *scan)
     106              : {
     107     13945498 :     IndexFetchHeapData *hscan = (IndexFetchHeapData *) scan;
     108              : 
     109     13945498 :     heapam_index_fetch_reset(scan);
     110              : 
     111     13945498 :     pfree(hscan);
     112     13945498 : }
     113              : 
     114              : static bool
     115     20006544 : 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     20006544 :     IndexFetchHeapData *hscan = (IndexFetchHeapData *) scan;
     122     20006544 :     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     20006544 :     if (!*call_again)
     129              :     {
     130              :         /* Switch to correct buffer if we don't have it already */
     131     19919978 :         Buffer      prev_buf = hscan->xs_cbuf;
     132              : 
     133     19919978 :         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     19919975 :         if (prev_buf != hscan->xs_cbuf)
     141     13807395 :             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     20006541 :     LockBuffer(hscan->xs_cbuf, BUFFER_LOCK_SHARE);
     146     20006541 :     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     20006541 :                                             !*call_again);
     153     20006539 :     bslot->base.tupdata.t_self = *tid;
     154     20006539 :     LockBuffer(hscan->xs_cbuf, BUFFER_LOCK_UNLOCK);
     155              : 
     156     20006539 :     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     13697663 :         *call_again = !IsMVCCSnapshot(snapshot);
     163              : 
     164     13697663 :         slot->tts_tableOid = RelationGetRelid(scan->rel);
     165     13697663 :         ExecStoreBufferHeapTuple(&bslot->base.tupdata, slot, hscan->xs_cbuf);
     166              :     }
     167              :     else
     168              :     {
     169              :         /* We've reached the end of the HOT chain. */
     170      6308876 :         *call_again = false;
     171              :     }
     172              : 
     173     20006539 :     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       180076 : heapam_fetch_row_version(Relation relation,
     184              :                          ItemPointer tid,
     185              :                          Snapshot snapshot,
     186              :                          TupleTableSlot *slot)
     187              : {
     188       180076 :     BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
     189              :     Buffer      buffer;
     190              : 
     191              :     Assert(TTS_IS_BUFFERTUPLE(slot));
     192              : 
     193       180076 :     bslot->base.tupdata.t_self = *tid;
     194       180076 :     if (heap_fetch(relation, snapshot, &bslot->base.tupdata, &buffer, false))
     195              :     {
     196              :         /* store in slot, transferring existing pin */
     197       179734 :         ExecStorePinnedBufferHeapTuple(&bslot->base.tupdata, slot, buffer);
     198       179734 :         slot->tts_tableOid = RelationGetRelid(relation);
     199              : 
     200       179734 :         return true;
     201              :     }
     202              : 
     203          334 :     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       128802 : heapam_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot,
     217              :                                 Snapshot snapshot)
     218              : {
     219       128802 :     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       128802 :     LockBuffer(bslot->buffer, BUFFER_LOCK_SHARE);
     230       128802 :     res = HeapTupleSatisfiesVisibility(bslot->base.tuple, snapshot,
     231              :                                        bslot->buffer);
     232       128802 :     LockBuffer(bslot->buffer, BUFFER_LOCK_UNLOCK);
     233              : 
     234       128802 :     return res;
     235              : }
     236              : 
     237              : 
     238              : /* ----------------------------------------------------------------------------
     239              :  *  Functions for manipulations of physical tuples for heap AM.
     240              :  * ----------------------------------------------------------------------------
     241              :  */
     242              : 
     243              : static void
     244      7431889 : heapam_tuple_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
     245              :                     int options, BulkInsertState bistate)
     246              : {
     247      7431889 :     bool        shouldFree = true;
     248      7431889 :     HeapTuple   tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
     249              : 
     250              :     /* Update the tuple with table oid */
     251      7431889 :     slot->tts_tableOid = RelationGetRelid(relation);
     252      7431889 :     tuple->t_tableOid = slot->tts_tableOid;
     253              : 
     254              :     /* Perform the insertion, and copy the resulting ItemPointer */
     255      7431889 :     heap_insert(relation, tuple, cid, options, bistate);
     256      7431871 :     ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
     257              : 
     258      7431871 :     if (shouldFree)
     259      1487086 :         pfree(tuple);
     260      7431871 : }
     261              : 
     262              : static void
     263         2133 : heapam_tuple_insert_speculative(Relation relation, TupleTableSlot *slot,
     264              :                                 CommandId cid, int options,
     265              :                                 BulkInsertState bistate, uint32 specToken)
     266              : {
     267         2133 :     bool        shouldFree = true;
     268         2133 :     HeapTuple   tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
     269              : 
     270              :     /* Update the tuple with table oid */
     271         2133 :     slot->tts_tableOid = RelationGetRelid(relation);
     272         2133 :     tuple->t_tableOid = slot->tts_tableOid;
     273              : 
     274         2133 :     HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
     275         2133 :     options |= HEAP_INSERT_SPECULATIVE;
     276              : 
     277              :     /* Perform the insertion, and copy the resulting ItemPointer */
     278         2133 :     heap_insert(relation, tuple, cid, options, bistate);
     279         2133 :     ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
     280              : 
     281         2133 :     if (shouldFree)
     282           45 :         pfree(tuple);
     283         2133 : }
     284              : 
     285              : static void
     286         2130 : heapam_tuple_complete_speculative(Relation relation, TupleTableSlot *slot,
     287              :                                   uint32 specToken, bool succeeded)
     288              : {
     289         2130 :     bool        shouldFree = true;
     290         2130 :     HeapTuple   tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
     291              : 
     292              :     /* adjust the tuple's state accordingly */
     293         2130 :     if (succeeded)
     294         2119 :         heap_finish_speculative(relation, &slot->tts_tid);
     295              :     else
     296           11 :         heap_abort_speculative(relation, &slot->tts_tid);
     297              : 
     298         2130 :     if (shouldFree)
     299           45 :         pfree(tuple);
     300         2130 : }
     301              : 
     302              : static TM_Result
     303       865983 : 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       865983 :     return heap_delete(relation, tid, cid, crosscheck, wait, tmfd, changingPart);
     313              : }
     314              : 
     315              : 
     316              : static TM_Result
     317       195860 : 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       195860 :     bool        shouldFree = true;
     323       195860 :     HeapTuple   tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
     324              :     TM_Result   result;
     325              : 
     326              :     /* Update the tuple with table oid */
     327       195860 :     slot->tts_tableOid = RelationGetRelid(relation);
     328       195860 :     tuple->t_tableOid = slot->tts_tableOid;
     329              : 
     330       195860 :     result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
     331              :                          tmfd, lockmode, update_indexes);
     332       195848 :     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       195848 :     if (result != TM_Ok)
     345              :     {
     346              :         Assert(*update_indexes == TU_None);
     347          164 :         *update_indexes = TU_None;
     348              :     }
     349       195684 :     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       195848 :     if (shouldFree)
     356        31946 :         pfree(tuple);
     357              : 
     358       195848 :     return result;
     359              : }
     360              : 
     361              : static TM_Result
     362       158338 : 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       158338 :     BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
     368              :     TM_Result   result;
     369              :     Buffer      buffer;
     370       158338 :     HeapTuple   tuple = &bslot->base.tupdata;
     371              :     bool        follow_updates;
     372              : 
     373       158338 :     follow_updates = (flags & TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS) != 0;
     374       158338 :     tmfd->traversed = false;
     375              : 
     376              :     Assert(TTS_IS_BUFFERTUPLE(slot));
     377              : 
     378       158513 : tuple_lock_retry:
     379       158513 :     tuple->t_self = *tid;
     380       158513 :     result = heap_lock_tuple(relation, tuple, cid, mode, wait_policy,
     381              :                              follow_updates, &buffer, tmfd);
     382              : 
     383       158500 :     if (result == TM_Updated &&
     384          217 :         (flags & TUPLE_LOCK_FLAG_FIND_LAST_VERSION))
     385              :     {
     386              :         /* Should not encounter speculative tuple on recheck */
     387              :         Assert(!HeapTupleHeaderIsSpeculative(tuple->t_data));
     388              : 
     389          198 :         ReleaseBuffer(buffer);
     390              : 
     391          198 :         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          198 :             *tid = tmfd->ctid;
     398              :             /* updated row should have xmin matching this xmax */
     399          198 :             priorXmax = tmfd->xmax;
     400              : 
     401              :             /* signal that a tuple later in the chain is getting locked */
     402          198 :             tmfd->traversed = true;
     403              : 
     404              :             /*
     405              :              * fetch target tuple
     406              :              *
     407              :              * Loop here to deal with updated or busy tuples
     408              :              */
     409          198 :             InitDirtySnapshot(SnapshotDirty);
     410              :             for (;;)
     411              :             {
     412          229 :                 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          218 :                 tuple->t_self = *tid;
     418          218 :                 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          184 :                     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          184 :                     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          184 :                     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          189 :                     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          175 :                     ReleaseBuffer(buffer);
     507          175 :                     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       158302 :     slot->tts_tableOid = RelationGetRelid(relation);
     568       158302 :     tuple->t_tableOid = slot->tts_tableOid;
     569              : 
     570              :     /* store in slot, transferring existing pin */
     571       158302 :     ExecStorePinnedBufferHeapTuple(tuple, slot, buffer);
     572              : 
     573       158302 :     return result;
     574              : }
     575              : 
     576              : 
     577              : /* ------------------------------------------------------------------------
     578              :  * DDL related callbacks for heap AM.
     579              :  * ------------------------------------------------------------------------
     580              :  */
     581              : 
     582              : static void
     583        34064 : 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        34064 :     *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        34064 :     *minmulti = GetOldestMultiXactId();
     607              : 
     608        34064 :     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        34064 :     if (persistence == RELPERSISTENCE_UNLOGGED)
     615              :     {
     616              :         Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
     617              :                rel->rd_rel->relkind == RELKIND_TOASTVALUE);
     618          136 :         smgrcreate(srel, INIT_FORKNUM, false);
     619          136 :         log_smgrcreate(newrlocator, INIT_FORKNUM);
     620              :     }
     621              : 
     622        34064 :     smgrclose(srel);
     623        34064 : }
     624              : 
     625              : static void
     626          314 : heapam_relation_nontransactional_truncate(Relation rel)
     627              : {
     628          314 :     RelationTruncate(rel, 0);
     629          314 : }
     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          317 : 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          317 :     TupleDesc   oldTupDesc = RelationGetDescr(OldHeap);
     701          317 :     TupleDesc   newTupDesc = RelationGetDescr(NewHeap);
     702              :     TupleTableSlot *slot;
     703              :     int         natts;
     704              :     Datum      *values;
     705              :     bool       *isnull;
     706              :     BufferHeapTupleTableSlot *hslot;
     707          317 :     BlockNumber prev_cblock = InvalidBlockNumber;
     708              : 
     709              :     /* Remember if it's a system catalog */
     710          317 :     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          317 :     natts = newTupDesc->natts;
     720          317 :     values = palloc_array(Datum, natts);
     721          317 :     isnull = palloc_array(bool, natts);
     722              : 
     723              :     /* Initialize the rewrite operation */
     724          317 :     rwstate = begin_heap_rewrite(OldHeap, NewHeap, OldestXmin, *xid_cutoff,
     725              :                                  *multi_cutoff);
     726              : 
     727              : 
     728              :     /* Set up sorting if wanted */
     729          317 :     if (use_sort)
     730           67 :         tuplesort = tuplesort_begin_cluster(oldTupDesc, OldIndex,
     731              :                                             maintenance_work_mem,
     732              :                                             NULL, TUPLESORT_NONE);
     733              :     else
     734          250 :         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          317 :     if (OldIndex != NULL && !use_sort)
     742           39 :     {
     743           39 :         const int   ci_index[] = {
     744              :             PROGRESS_REPACK_PHASE,
     745              :             PROGRESS_REPACK_INDEX_RELID
     746              :         };
     747              :         int64       ci_val[2];
     748              : 
     749              :         /* Set phase and OIDOldIndex to columns */
     750           39 :         ci_val[0] = PROGRESS_REPACK_PHASE_INDEX_SCAN_HEAP;
     751           39 :         ci_val[1] = RelationGetRelid(OldIndex);
     752           39 :         pgstat_progress_update_multi_param(2, ci_index, ci_val);
     753              : 
     754           39 :         tableScan = NULL;
     755           39 :         heapScan = NULL;
     756           39 :         indexScan = index_beginscan(OldHeap, OldIndex, SnapshotAny, NULL, 0, 0);
     757           39 :         index_rescan(indexScan, NULL, 0, NULL, 0);
     758              :     }
     759              :     else
     760              :     {
     761              :         /* In scan-and-sort mode and also VACUUM FULL, set phase */
     762          278 :         pgstat_progress_update_param(PROGRESS_REPACK_PHASE,
     763              :                                      PROGRESS_REPACK_PHASE_SEQ_SCAN_HEAP);
     764              : 
     765          278 :         tableScan = table_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
     766          278 :         heapScan = (HeapScanDesc) tableScan;
     767          278 :         indexScan = NULL;
     768              : 
     769              :         /* Set total heap blocks */
     770          278 :         pgstat_progress_update_param(PROGRESS_REPACK_TOTAL_HEAP_BLKS,
     771          278 :                                      heapScan->rs_nblocks);
     772              :     }
     773              : 
     774          317 :     slot = table_slot_create(OldHeap, NULL);
     775          317 :     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       366989 :     {
     785              :         HeapTuple   tuple;
     786              :         Buffer      buf;
     787              :         bool        isdead;
     788              : 
     789       367306 :         CHECK_FOR_INTERRUPTS();
     790              : 
     791       367306 :         if (indexScan != NULL)
     792              :         {
     793           93 :             if (!index_getnext_slot(indexScan, ForwardScanDirection, slot))
     794           39 :                 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       367213 :             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          278 :                 pgstat_progress_update_param(PROGRESS_REPACK_HEAP_BLKS_SCANNED,
     813          278 :                                              heapScan->rs_nblocks);
     814          278 :                 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       366935 :             if (prev_cblock != heapScan->rs_cblock)
     827              :             {
     828         5520 :                 pgstat_progress_update_param(PROGRESS_REPACK_HEAP_BLKS_SCANNED,
     829         5520 :                                              (heapScan->rs_cblock +
     830         5520 :                                               heapScan->rs_nblocks -
     831         5520 :                                               heapScan->rs_startblock
     832         5520 :                                               ) % heapScan->rs_nblocks + 1);
     833         5520 :                 prev_cblock = heapScan->rs_cblock;
     834              :             }
     835              :         }
     836              : 
     837       366989 :         tuple = ExecFetchSlotHeapTuple(slot, false, NULL);
     838       366989 :         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       366989 :         LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
     855              : 
     856       366989 :         switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf))
     857              :         {
     858        12330 :             case HEAPTUPLE_DEAD:
     859              :                 /* Definitely dead */
     860        12330 :                 isdead = true;
     861        12330 :                 break;
     862         6686 :             case HEAPTUPLE_RECENTLY_DEAD:
     863         6686 :                 *tups_recently_dead += 1;
     864              :                 pg_fallthrough;
     865       354533 :             case HEAPTUPLE_LIVE:
     866              :                 /* Live or recently dead, must copy it */
     867       354533 :                 isdead = false;
     868       354533 :                 break;
     869          103 :             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          103 :                 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          103 :                 isdead = false;
     885          103 :                 break;
     886           23 :             case HEAPTUPLE_DELETE_IN_PROGRESS:
     887              : 
     888              :                 /*
     889              :                  * Similar situation to INSERT_IN_PROGRESS case.
     890              :                  */
     891           23 :                 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           23 :                 *tups_recently_dead += 1;
     897           23 :                 isdead = false;
     898           23 :                 break;
     899            0 :             default:
     900            0 :                 elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
     901              :                 isdead = false; /* keep compiler quiet */
     902              :                 break;
     903              :         }
     904              : 
     905       366989 :         LockBuffer(buf, BUFFER_LOCK_UNLOCK);
     906              : 
     907       366989 :         if (isdead)
     908              :         {
     909        12330 :             *tups_vacuumed += 1;
     910              :             /* heap rewrite module still needs to see it... */
     911        12330 :             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        12330 :             continue;
     918              :         }
     919              : 
     920       354659 :         *num_tuples += 1;
     921       354659 :         if (tuplesort != NULL)
     922              :         {
     923       273813 :             tuplesort_putheaptuple(tuplesort, tuple);
     924              : 
     925              :             /*
     926              :              * In scan-and-sort mode, report increase in number of tuples
     927              :              * scanned
     928              :              */
     929       273813 :             pgstat_progress_update_param(PROGRESS_REPACK_HEAP_TUPLES_SCANNED,
     930       273813 :                                          *num_tuples);
     931              :         }
     932              :         else
     933              :         {
     934        80846 :             const int   ct_index[] = {
     935              :                 PROGRESS_REPACK_HEAP_TUPLES_SCANNED,
     936              :                 PROGRESS_REPACK_HEAP_TUPLES_WRITTEN
     937              :             };
     938              :             int64       ct_val[2];
     939              : 
     940        80846 :             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        80846 :             ct_val[0] = *num_tuples;
     948        80846 :             ct_val[1] = *num_tuples;
     949        80846 :             pgstat_progress_update_multi_param(2, ct_index, ct_val);
     950              :         }
     951              :     }
     952              : 
     953          317 :     if (indexScan != NULL)
     954           39 :         index_endscan(indexScan);
     955          317 :     if (tableScan != NULL)
     956          278 :         table_endscan(tableScan);
     957          317 :     if (slot)
     958          317 :         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          317 :     if (tuplesort != NULL)
     965              :     {
     966           67 :         double      n_tuples = 0;
     967              : 
     968              :         /* Report that we are now sorting tuples */
     969           67 :         pgstat_progress_update_param(PROGRESS_REPACK_PHASE,
     970              :                                      PROGRESS_REPACK_PHASE_SORT_TUPLES);
     971              : 
     972           67 :         tuplesort_performsort(tuplesort);
     973              : 
     974              :         /* Report that we are now writing new heap */
     975           67 :         pgstat_progress_update_param(PROGRESS_REPACK_PHASE,
     976              :                                      PROGRESS_REPACK_PHASE_WRITE_NEW_HEAP);
     977              : 
     978              :         for (;;)
     979       273813 :         {
     980              :             HeapTuple   tuple;
     981              : 
     982       273880 :             CHECK_FOR_INTERRUPTS();
     983              : 
     984       273880 :             tuple = tuplesort_getheaptuple(tuplesort, true);
     985       273880 :             if (tuple == NULL)
     986           67 :                 break;
     987              : 
     988       273813 :             n_tuples += 1;
     989       273813 :             reform_and_rewrite_tuple(tuple,
     990              :                                      OldHeap, NewHeap,
     991              :                                      values, isnull,
     992              :                                      rwstate);
     993              :             /* Report n_tuples */
     994       273813 :             pgstat_progress_update_param(PROGRESS_REPACK_HEAP_TUPLES_WRITTEN,
     995              :                                          n_tuples);
     996              :         }
     997              : 
     998           67 :         tuplesort_end(tuplesort);
     999              :     }
    1000              : 
    1001              :     /* Write out any remaining tuples, and fsync if needed */
    1002          317 :     end_heap_rewrite(rwstate);
    1003              : 
    1004              :     /* Clean up */
    1005          317 :     pfree(values);
    1006          317 :     pfree(isnull);
    1007          317 : }
    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        78119 : heapam_scan_analyze_next_block(TableScanDesc scan, ReadStream *stream)
    1020              : {
    1021        78119 :     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        78119 :     hscan->rs_cbuf = read_stream_next_buffer(stream, NULL);
    1032        78119 :     if (!BufferIsValid(hscan->rs_cbuf))
    1033         8912 :         return false;
    1034              : 
    1035        69207 :     LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
    1036              : 
    1037        69207 :     hscan->rs_cblock = BufferGetBlockNumber(hscan->rs_cbuf);
    1038        69207 :     hscan->rs_cindex = FirstOffsetNumber;
    1039        69207 :     return true;
    1040              : }
    1041              : 
    1042              : static bool
    1043      5487596 : heapam_scan_analyze_next_tuple(TableScanDesc scan,
    1044              :                                double *liverows, double *deadrows,
    1045              :                                TupleTableSlot *slot)
    1046              : {
    1047      5487596 :     HeapScanDesc hscan = (HeapScanDesc) scan;
    1048              :     Page        targpage;
    1049              :     OffsetNumber maxoffset;
    1050              :     BufferHeapTupleTableSlot *hslot;
    1051              : 
    1052              :     Assert(TTS_IS_BUFFERTUPLE(slot));
    1053              : 
    1054      5487596 :     hslot = (BufferHeapTupleTableSlot *) slot;
    1055      5487596 :     targpage = BufferGetPage(hscan->rs_cbuf);
    1056      5487596 :     maxoffset = PageGetMaxOffsetNumber(targpage);
    1057              : 
    1058              :     /* Inner loop over all tuples on the selected page */
    1059      5736538 :     for (; hscan->rs_cindex <= maxoffset; hscan->rs_cindex++)
    1060              :     {
    1061              :         ItemId      itemid;
    1062      5667331 :         HeapTuple   targtuple = &hslot->base.tupdata;
    1063      5667331 :         bool        sample_it = false;
    1064              :         TransactionId dead_after;
    1065              : 
    1066      5667331 :         itemid = PageGetItemId(targpage, hscan->rs_cindex);
    1067              : 
    1068              :         /*
    1069              :          * We ignore unused and redirect line pointers.  DEAD line pointers
    1070              :          * should be counted as dead, because we need vacuum to run to get rid
    1071              :          * of them.  Note that this rule agrees with the way that
    1072              :          * heap_page_prune_and_freeze() counts things.
    1073              :          */
    1074      5667331 :         if (!ItemIdIsNormal(itemid))
    1075              :         {
    1076       167380 :             if (ItemIdIsDead(itemid))
    1077        76293 :                 *deadrows += 1;
    1078       167380 :             continue;
    1079              :         }
    1080              : 
    1081      5499951 :         ItemPointerSet(&targtuple->t_self, hscan->rs_cblock, hscan->rs_cindex);
    1082              : 
    1083      5499951 :         targtuple->t_tableOid = RelationGetRelid(scan->rs_rd);
    1084      5499951 :         targtuple->t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
    1085      5499951 :         targtuple->t_len = ItemIdGetLength(itemid);
    1086              : 
    1087      5499951 :         switch (HeapTupleSatisfiesVacuumHorizon(targtuple,
    1088              :                                                 hscan->rs_cbuf,
    1089              :                                                 &dead_after))
    1090              :         {
    1091      5188744 :             case HEAPTUPLE_LIVE:
    1092      5188744 :                 sample_it = true;
    1093      5188744 :                 *liverows += 1;
    1094      5188744 :                 break;
    1095              : 
    1096        80704 :             case HEAPTUPLE_DEAD:
    1097              :             case HEAPTUPLE_RECENTLY_DEAD:
    1098              :                 /* Count dead and recently-dead rows */
    1099        80704 :                 *deadrows += 1;
    1100        80704 :                 break;
    1101              : 
    1102       173145 :             case HEAPTUPLE_INSERT_IN_PROGRESS:
    1103              : 
    1104              :                 /*
    1105              :                  * Insert-in-progress rows are not counted.  We assume that
    1106              :                  * when the inserting transaction commits or aborts, it will
    1107              :                  * send a stats message to increment the proper count.  This
    1108              :                  * works right only if that transaction ends after we finish
    1109              :                  * analyzing the table; if things happen in the other order,
    1110              :                  * its stats update will be overwritten by ours.  However, the
    1111              :                  * error will be large only if the other transaction runs long
    1112              :                  * enough to insert many tuples, so assuming it will finish
    1113              :                  * after us is the safer option.
    1114              :                  *
    1115              :                  * A special case is that the inserting transaction might be
    1116              :                  * our own.  In this case we should count and sample the row,
    1117              :                  * to accommodate users who load a table and analyze it in one
    1118              :                  * transaction.  (pgstat_report_analyze has to adjust the
    1119              :                  * numbers we report to the cumulative stats system to make
    1120              :                  * this come out right.)
    1121              :                  */
    1122       173145 :                 if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(targtuple->t_data)))
    1123              :                 {
    1124       173145 :                     sample_it = true;
    1125       173145 :                     *liverows += 1;
    1126              :                 }
    1127       173145 :                 break;
    1128              : 
    1129        57358 :             case HEAPTUPLE_DELETE_IN_PROGRESS:
    1130              : 
    1131              :                 /*
    1132              :                  * We count and sample delete-in-progress rows the same as
    1133              :                  * live ones, so that the stats counters come out right if the
    1134              :                  * deleting transaction commits after us, per the same
    1135              :                  * reasoning given above.
    1136              :                  *
    1137              :                  * If the delete was done by our own transaction, however, we
    1138              :                  * must count the row as dead to make pgstat_report_analyze's
    1139              :                  * stats adjustments come out right.  (Note: this works out
    1140              :                  * properly when the row was both inserted and deleted in our
    1141              :                  * xact.)
    1142              :                  *
    1143              :                  * The net effect of these choices is that we act as though an
    1144              :                  * IN_PROGRESS transaction hasn't happened yet, except if it
    1145              :                  * is our own transaction, which we assume has happened.
    1146              :                  *
    1147              :                  * This approach ensures that we behave sanely if we see both
    1148              :                  * the pre-image and post-image rows for a row being updated
    1149              :                  * by a concurrent transaction: we will sample the pre-image
    1150              :                  * but not the post-image.  We also get sane results if the
    1151              :                  * concurrent transaction never commits.
    1152              :                  */
    1153        57358 :                 if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(targtuple->t_data)))
    1154          858 :                     *deadrows += 1;
    1155              :                 else
    1156              :                 {
    1157        56500 :                     sample_it = true;
    1158        56500 :                     *liverows += 1;
    1159              :                 }
    1160        57358 :                 break;
    1161              : 
    1162            0 :             default:
    1163            0 :                 elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
    1164              :                 break;
    1165              :         }
    1166              : 
    1167      5499951 :         if (sample_it)
    1168              :         {
    1169      5418389 :             ExecStoreBufferHeapTuple(targtuple, slot, hscan->rs_cbuf);
    1170      5418389 :             hscan->rs_cindex++;
    1171              : 
    1172              :             /* note that we leave the buffer locked here! */
    1173      5418389 :             return true;
    1174              :         }
    1175              :     }
    1176              : 
    1177              :     /* Now release the lock and pin on the page */
    1178        69207 :     UnlockReleaseBuffer(hscan->rs_cbuf);
    1179        69207 :     hscan->rs_cbuf = InvalidBuffer;
    1180              : 
    1181              :     /* also prevent old slot contents from having pin on page */
    1182        69207 :     ExecClearTuple(slot);
    1183              : 
    1184        69207 :     return false;
    1185              : }
    1186              : 
    1187              : static double
    1188        29357 : heapam_index_build_range_scan(Relation heapRelation,
    1189              :                               Relation indexRelation,
    1190              :                               IndexInfo *indexInfo,
    1191              :                               bool allow_sync,
    1192              :                               bool anyvisible,
    1193              :                               bool progress,
    1194              :                               BlockNumber start_blockno,
    1195              :                               BlockNumber numblocks,
    1196              :                               IndexBuildCallback callback,
    1197              :                               void *callback_state,
    1198              :                               TableScanDesc scan)
    1199              : {
    1200              :     HeapScanDesc hscan;
    1201              :     bool        is_system_catalog;
    1202              :     bool        checking_uniqueness;
    1203              :     HeapTuple   heapTuple;
    1204              :     Datum       values[INDEX_MAX_KEYS];
    1205              :     bool        isnull[INDEX_MAX_KEYS];
    1206              :     double      reltuples;
    1207              :     ExprState  *predicate;
    1208              :     TupleTableSlot *slot;
    1209              :     EState     *estate;
    1210              :     ExprContext *econtext;
    1211              :     Snapshot    snapshot;
    1212        29357 :     bool        need_unregister_snapshot = false;
    1213              :     TransactionId OldestXmin;
    1214        29357 :     BlockNumber previous_blkno = InvalidBlockNumber;
    1215        29357 :     BlockNumber root_blkno = InvalidBlockNumber;
    1216              :     OffsetNumber root_offsets[MaxHeapTuplesPerPage];
    1217              : 
    1218              :     /*
    1219              :      * sanity checks
    1220              :      */
    1221              :     Assert(OidIsValid(indexRelation->rd_rel->relam));
    1222              : 
    1223              :     /* Remember if it's a system catalog */
    1224        29357 :     is_system_catalog = IsSystemRelation(heapRelation);
    1225              : 
    1226              :     /* See whether we're verifying uniqueness/exclusion properties */
    1227        37112 :     checking_uniqueness = (indexInfo->ii_Unique ||
    1228         7755 :                            indexInfo->ii_ExclusionOps != NULL);
    1229              : 
    1230              :     /*
    1231              :      * "Any visible" mode is not compatible with uniqueness checks; make sure
    1232              :      * only one of those is requested.
    1233              :      */
    1234              :     Assert(!(anyvisible && checking_uniqueness));
    1235              : 
    1236              :     /*
    1237              :      * Need an EState for evaluation of index expressions and partial-index
    1238              :      * predicates.  Also a slot to hold the current tuple.
    1239              :      */
    1240        29357 :     estate = CreateExecutorState();
    1241        29357 :     econtext = GetPerTupleExprContext(estate);
    1242        29357 :     slot = table_slot_create(heapRelation, NULL);
    1243              : 
    1244              :     /* Arrange for econtext's scan tuple to be the tuple under test */
    1245        29357 :     econtext->ecxt_scantuple = slot;
    1246              : 
    1247              :     /* Set up execution state for predicate, if any. */
    1248        29357 :     predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
    1249              : 
    1250              :     /*
    1251              :      * Prepare for scan of the base relation.  In a normal index build, we use
    1252              :      * SnapshotAny because we must retrieve all tuples and do our own time
    1253              :      * qual checks (because we have to index RECENTLY_DEAD tuples). In a
    1254              :      * concurrent build, or during bootstrap, we take a regular MVCC snapshot
    1255              :      * and index whatever's live according to that.
    1256              :      */
    1257        29357 :     OldestXmin = InvalidTransactionId;
    1258              : 
    1259              :     /* okay to ignore lazy VACUUMs here */
    1260        29357 :     if (!IsBootstrapProcessingMode() && !indexInfo->ii_Concurrent)
    1261        20790 :         OldestXmin = GetOldestNonRemovableTransactionId(heapRelation);
    1262              : 
    1263        29357 :     if (!scan)
    1264              :     {
    1265              :         /*
    1266              :          * Serial index build.
    1267              :          *
    1268              :          * Must begin our own heap scan in this case.  We may also need to
    1269              :          * register a snapshot whose lifetime is under our direct control.
    1270              :          */
    1271        29074 :         if (!TransactionIdIsValid(OldestXmin))
    1272              :         {
    1273         8487 :             snapshot = RegisterSnapshot(GetTransactionSnapshot());
    1274         8487 :             need_unregister_snapshot = true;
    1275              :         }
    1276              :         else
    1277        20587 :             snapshot = SnapshotAny;
    1278              : 
    1279        29074 :         scan = table_beginscan_strat(heapRelation,  /* relation */
    1280              :                                      snapshot,  /* snapshot */
    1281              :                                      0, /* number of keys */
    1282              :                                      NULL,  /* scan key */
    1283              :                                      true,  /* buffer access strategy OK */
    1284              :                                      allow_sync);   /* syncscan OK? */
    1285              :     }
    1286              :     else
    1287              :     {
    1288              :         /*
    1289              :          * Parallel index build.
    1290              :          *
    1291              :          * Parallel case never registers/unregisters own snapshot.  Snapshot
    1292              :          * is taken from parallel heap scan, and is SnapshotAny or an MVCC
    1293              :          * snapshot, based on same criteria as serial case.
    1294              :          */
    1295              :         Assert(!IsBootstrapProcessingMode());
    1296              :         Assert(allow_sync);
    1297          283 :         snapshot = scan->rs_snapshot;
    1298              :     }
    1299              : 
    1300        29357 :     hscan = (HeapScanDesc) scan;
    1301              : 
    1302              :     /*
    1303              :      * Must have called GetOldestNonRemovableTransactionId() if using
    1304              :      * SnapshotAny.  Shouldn't have for an MVCC snapshot. (It's especially
    1305              :      * worth checking this for parallel builds, since ambuild routines that
    1306              :      * support parallel builds must work these details out for themselves.)
    1307              :      */
    1308              :     Assert(snapshot == SnapshotAny || IsMVCCSnapshot(snapshot));
    1309              :     Assert(snapshot == SnapshotAny ? TransactionIdIsValid(OldestXmin) :
    1310              :            !TransactionIdIsValid(OldestXmin));
    1311              :     Assert(snapshot == SnapshotAny || !anyvisible);
    1312              : 
    1313              :     /* Publish number of blocks to scan */
    1314        29357 :     if (progress)
    1315              :     {
    1316              :         BlockNumber nblocks;
    1317              : 
    1318        27705 :         if (hscan->rs_base.rs_parallel != NULL)
    1319              :         {
    1320              :             ParallelBlockTableScanDesc pbscan;
    1321              : 
    1322          103 :             pbscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
    1323          103 :             nblocks = pbscan->phs_nblocks;
    1324              :         }
    1325              :         else
    1326        27602 :             nblocks = hscan->rs_nblocks;
    1327              : 
    1328        27705 :         pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_TOTAL,
    1329              :                                      nblocks);
    1330              :     }
    1331              : 
    1332              :     /* set our scan endpoints */
    1333        29357 :     if (!allow_sync)
    1334         1846 :         heap_setscanlimits(scan, start_blockno, numblocks);
    1335              :     else
    1336              :     {
    1337              :         /* syncscan can only be requested on whole relation */
    1338              :         Assert(start_blockno == 0);
    1339              :         Assert(numblocks == InvalidBlockNumber);
    1340              :     }
    1341              : 
    1342        29357 :     reltuples = 0;
    1343              : 
    1344              :     /*
    1345              :      * Scan all tuples in the base relation.
    1346              :      */
    1347      8878165 :     while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    1348              :     {
    1349              :         bool        tupleIsAlive;
    1350              : 
    1351      8848814 :         CHECK_FOR_INTERRUPTS();
    1352              : 
    1353              :         /* Report scan progress, if asked to. */
    1354      8848814 :         if (progress)
    1355              :         {
    1356      7418021 :             BlockNumber blocks_done = heapam_scan_get_blocks_done(hscan);
    1357              : 
    1358      7418021 :             if (blocks_done != previous_blkno)
    1359              :             {
    1360        96175 :                 pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE,
    1361              :                                              blocks_done);
    1362        96175 :                 previous_blkno = blocks_done;
    1363              :             }
    1364              :         }
    1365              : 
    1366              :         /*
    1367              :          * When dealing with a HOT-chain of updated tuples, we want to index
    1368              :          * the values of the live tuple (if any), but index it under the TID
    1369              :          * of the chain's root tuple.  This approach is necessary to preserve
    1370              :          * the HOT-chain structure in the heap. So we need to be able to find
    1371              :          * the root item offset for every tuple that's in a HOT-chain.  When
    1372              :          * first reaching a new page of the relation, call
    1373              :          * heap_get_root_tuples() to build a map of root item offsets on the
    1374              :          * page.
    1375              :          *
    1376              :          * It might look unsafe to use this information across buffer
    1377              :          * lock/unlock.  However, we hold ShareLock on the table so no
    1378              :          * ordinary insert/update/delete should occur; and we hold pin on the
    1379              :          * buffer continuously while visiting the page, so no pruning
    1380              :          * operation can occur either.
    1381              :          *
    1382              :          * In cases with only ShareUpdateExclusiveLock on the table, it's
    1383              :          * possible for some HOT tuples to appear that we didn't know about
    1384              :          * when we first read the page.  To handle that case, we re-obtain the
    1385              :          * list of root offsets when a HOT tuple points to a root item that we
    1386              :          * don't know about.
    1387              :          *
    1388              :          * Also, although our opinions about tuple liveness could change while
    1389              :          * we scan the page (due to concurrent transaction commits/aborts),
    1390              :          * the chain root locations won't, so this info doesn't need to be
    1391              :          * rebuilt after waiting for another transaction.
    1392              :          *
    1393              :          * Note the implied assumption that there is no more than one live
    1394              :          * tuple per HOT-chain --- else we could create more than one index
    1395              :          * entry pointing to the same root tuple.
    1396              :          */
    1397      8848814 :         if (hscan->rs_cblock != root_blkno)
    1398              :         {
    1399       110675 :             Page        page = BufferGetPage(hscan->rs_cbuf);
    1400              : 
    1401       110675 :             LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
    1402       110675 :             heap_get_root_tuples(page, root_offsets);
    1403       110675 :             LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
    1404              : 
    1405       110675 :             root_blkno = hscan->rs_cblock;
    1406              :         }
    1407              : 
    1408      8848814 :         if (snapshot == SnapshotAny)
    1409              :         {
    1410              :             /* do our own time qual check */
    1411              :             bool        indexIt;
    1412              :             TransactionId xwait;
    1413              : 
    1414      7007764 :     recheck:
    1415              : 
    1416              :             /*
    1417              :              * We could possibly get away with not locking the buffer here,
    1418              :              * since caller should hold ShareLock on the relation, but let's
    1419              :              * be conservative about it.  (This remark is still correct even
    1420              :              * with HOT-pruning: our pin on the buffer prevents pruning.)
    1421              :              */
    1422      7007764 :             LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
    1423              : 
    1424              :             /*
    1425              :              * The criteria for counting a tuple as live in this block need to
    1426              :              * match what analyze.c's heapam_scan_analyze_next_tuple() does,
    1427              :              * otherwise CREATE INDEX and ANALYZE may produce wildly different
    1428              :              * reltuples values, e.g. when there are many recently-dead
    1429              :              * tuples.
    1430              :              */
    1431      7007764 :             switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin,
    1432              :                                              hscan->rs_cbuf))
    1433              :             {
    1434         1099 :                 case HEAPTUPLE_DEAD:
    1435              :                     /* Definitely dead, we can ignore it */
    1436         1099 :                     indexIt = false;
    1437         1099 :                     tupleIsAlive = false;
    1438         1099 :                     break;
    1439      5116910 :                 case HEAPTUPLE_LIVE:
    1440              :                     /* Normal case, index and unique-check it */
    1441      5116910 :                     indexIt = true;
    1442      5116910 :                     tupleIsAlive = true;
    1443              :                     /* Count it as live, too */
    1444      5116910 :                     reltuples += 1;
    1445      5116910 :                     break;
    1446       116073 :                 case HEAPTUPLE_RECENTLY_DEAD:
    1447              : 
    1448              :                     /*
    1449              :                      * If tuple is recently deleted then we must index it
    1450              :                      * anyway to preserve MVCC semantics.  (Pre-existing
    1451              :                      * transactions could try to use the index after we finish
    1452              :                      * building it, and may need to see such tuples.)
    1453              :                      *
    1454              :                      * However, if it was HOT-updated then we must only index
    1455              :                      * the live tuple at the end of the HOT-chain.  Since this
    1456              :                      * breaks semantics for pre-existing snapshots, mark the
    1457              :                      * index as unusable for them.
    1458              :                      *
    1459              :                      * We don't count recently-dead tuples in reltuples, even
    1460              :                      * if we index them; see heapam_scan_analyze_next_tuple().
    1461              :                      */
    1462       116073 :                     if (HeapTupleIsHotUpdated(heapTuple))
    1463              :                     {
    1464           92 :                         indexIt = false;
    1465              :                         /* mark the index as unsafe for old snapshots */
    1466           92 :                         indexInfo->ii_BrokenHotChain = true;
    1467              :                     }
    1468              :                     else
    1469       115981 :                         indexIt = true;
    1470              :                     /* In any case, exclude the tuple from unique-checking */
    1471       116073 :                     tupleIsAlive = false;
    1472       116073 :                     break;
    1473      1773643 :                 case HEAPTUPLE_INSERT_IN_PROGRESS:
    1474              : 
    1475              :                     /*
    1476              :                      * In "anyvisible" mode, this tuple is visible and we
    1477              :                      * don't need any further checks.
    1478              :                      */
    1479      1773643 :                     if (anyvisible)
    1480              :                     {
    1481        30736 :                         indexIt = true;
    1482        30736 :                         tupleIsAlive = true;
    1483        30736 :                         reltuples += 1;
    1484        30736 :                         break;
    1485              :                     }
    1486              : 
    1487              :                     /*
    1488              :                      * Since caller should hold ShareLock or better, normally
    1489              :                      * the only way to see this is if it was inserted earlier
    1490              :                      * in our own transaction.  However, it can happen in
    1491              :                      * system catalogs, since we tend to release write lock
    1492              :                      * before commit there.  Give a warning if neither case
    1493              :                      * applies.
    1494              :                      */
    1495      1742907 :                     xwait = HeapTupleHeaderGetXmin(heapTuple->t_data);
    1496      1742907 :                     if (!TransactionIdIsCurrentTransactionId(xwait))
    1497              :                     {
    1498           84 :                         if (!is_system_catalog)
    1499            0 :                             elog(WARNING, "concurrent insert in progress within table \"%s\"",
    1500              :                                  RelationGetRelationName(heapRelation));
    1501              : 
    1502              :                         /*
    1503              :                          * If we are performing uniqueness checks, indexing
    1504              :                          * such a tuple could lead to a bogus uniqueness
    1505              :                          * failure.  In that case we wait for the inserting
    1506              :                          * transaction to finish and check again.
    1507              :                          */
    1508           84 :                         if (checking_uniqueness)
    1509              :                         {
    1510              :                             /*
    1511              :                              * Must drop the lock on the buffer before we wait
    1512              :                              */
    1513            0 :                             LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
    1514            0 :                             XactLockTableWait(xwait, heapRelation,
    1515            0 :                                               &heapTuple->t_self,
    1516              :                                               XLTW_InsertIndexUnique);
    1517            0 :                             CHECK_FOR_INTERRUPTS();
    1518            0 :                             goto recheck;
    1519              :                         }
    1520              :                     }
    1521              :                     else
    1522              :                     {
    1523              :                         /*
    1524              :                          * For consistency with
    1525              :                          * heapam_scan_analyze_next_tuple(), count
    1526              :                          * HEAPTUPLE_INSERT_IN_PROGRESS tuples as live only
    1527              :                          * when inserted by our own transaction.
    1528              :                          */
    1529      1742823 :                         reltuples += 1;
    1530              :                     }
    1531              : 
    1532              :                     /*
    1533              :                      * We must index such tuples, since if the index build
    1534              :                      * commits then they're good.
    1535              :                      */
    1536      1742907 :                     indexIt = true;
    1537      1742907 :                     tupleIsAlive = true;
    1538      1742907 :                     break;
    1539           39 :                 case HEAPTUPLE_DELETE_IN_PROGRESS:
    1540              : 
    1541              :                     /*
    1542              :                      * As with INSERT_IN_PROGRESS case, this is unexpected
    1543              :                      * unless it's our own deletion or a system catalog; but
    1544              :                      * in anyvisible mode, this tuple is visible.
    1545              :                      */
    1546           39 :                     if (anyvisible)
    1547              :                     {
    1548            0 :                         indexIt = true;
    1549            0 :                         tupleIsAlive = false;
    1550            0 :                         reltuples += 1;
    1551            0 :                         break;
    1552              :                     }
    1553              : 
    1554           39 :                     xwait = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
    1555           39 :                     if (!TransactionIdIsCurrentTransactionId(xwait))
    1556              :                     {
    1557            0 :                         if (!is_system_catalog)
    1558            0 :                             elog(WARNING, "concurrent delete in progress within table \"%s\"",
    1559              :                                  RelationGetRelationName(heapRelation));
    1560              : 
    1561              :                         /*
    1562              :                          * If we are performing uniqueness checks, assuming
    1563              :                          * the tuple is dead could lead to missing a
    1564              :                          * uniqueness violation.  In that case we wait for the
    1565              :                          * deleting transaction to finish and check again.
    1566              :                          *
    1567              :                          * Also, if it's a HOT-updated tuple, we should not
    1568              :                          * index it but rather the live tuple at the end of
    1569              :                          * the HOT-chain.  However, the deleting transaction
    1570              :                          * could abort, possibly leaving this tuple as live
    1571              :                          * after all, in which case it has to be indexed. The
    1572              :                          * only way to know what to do is to wait for the
    1573              :                          * deleting transaction to finish and check again.
    1574              :                          */
    1575            0 :                         if (checking_uniqueness ||
    1576            0 :                             HeapTupleIsHotUpdated(heapTuple))
    1577              :                         {
    1578              :                             /*
    1579              :                              * Must drop the lock on the buffer before we wait
    1580              :                              */
    1581            0 :                             LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
    1582            0 :                             XactLockTableWait(xwait, heapRelation,
    1583            0 :                                               &heapTuple->t_self,
    1584              :                                               XLTW_InsertIndexUnique);
    1585            0 :                             CHECK_FOR_INTERRUPTS();
    1586            0 :                             goto recheck;
    1587              :                         }
    1588              : 
    1589              :                         /*
    1590              :                          * Otherwise index it but don't check for uniqueness,
    1591              :                          * the same as a RECENTLY_DEAD tuple.
    1592              :                          */
    1593            0 :                         indexIt = true;
    1594              : 
    1595              :                         /*
    1596              :                          * Count HEAPTUPLE_DELETE_IN_PROGRESS tuples as live,
    1597              :                          * if they were not deleted by the current
    1598              :                          * transaction.  That's what
    1599              :                          * heapam_scan_analyze_next_tuple() does, and we want
    1600              :                          * the behavior to be consistent.
    1601              :                          */
    1602            0 :                         reltuples += 1;
    1603              :                     }
    1604           39 :                     else if (HeapTupleIsHotUpdated(heapTuple))
    1605              :                     {
    1606              :                         /*
    1607              :                          * It's a HOT-updated tuple deleted by our own xact.
    1608              :                          * We can assume the deletion will commit (else the
    1609              :                          * index contents don't matter), so treat the same as
    1610              :                          * RECENTLY_DEAD HOT-updated tuples.
    1611              :                          */
    1612            0 :                         indexIt = false;
    1613              :                         /* mark the index as unsafe for old snapshots */
    1614            0 :                         indexInfo->ii_BrokenHotChain = true;
    1615              :                     }
    1616              :                     else
    1617              :                     {
    1618              :                         /*
    1619              :                          * It's a regular tuple deleted by our own xact. Index
    1620              :                          * it, but don't check for uniqueness nor count in
    1621              :                          * reltuples, the same as a RECENTLY_DEAD tuple.
    1622              :                          */
    1623           39 :                         indexIt = true;
    1624              :                     }
    1625              :                     /* In any case, exclude the tuple from unique-checking */
    1626           39 :                     tupleIsAlive = false;
    1627           39 :                     break;
    1628            0 :                 default:
    1629            0 :                     elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
    1630              :                     indexIt = tupleIsAlive = false; /* keep compiler quiet */
    1631              :                     break;
    1632              :             }
    1633              : 
    1634      7007764 :             LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
    1635              : 
    1636      7007764 :             if (!indexIt)
    1637         1191 :                 continue;
    1638              :         }
    1639              :         else
    1640              :         {
    1641              :             /* heap_getnext did the time qual check */
    1642      1841050 :             tupleIsAlive = true;
    1643      1841050 :             reltuples += 1;
    1644              :         }
    1645              : 
    1646      8847623 :         MemoryContextReset(econtext->ecxt_per_tuple_memory);
    1647              : 
    1648              :         /* Set up for predicate or expression evaluation */
    1649      8847623 :         ExecStoreBufferHeapTuple(heapTuple, slot, hscan->rs_cbuf);
    1650              : 
    1651              :         /*
    1652              :          * In a partial index, discard tuples that don't satisfy the
    1653              :          * predicate.
    1654              :          */
    1655      8847623 :         if (predicate != NULL)
    1656              :         {
    1657       102277 :             if (!ExecQual(predicate, econtext))
    1658        54829 :                 continue;
    1659              :         }
    1660              : 
    1661              :         /*
    1662              :          * For the current heap tuple, extract all the attributes we use in
    1663              :          * this index, and note which are null.  This also performs evaluation
    1664              :          * of any expressions needed.
    1665              :          */
    1666      8792794 :         FormIndexDatum(indexInfo,
    1667              :                        slot,
    1668              :                        estate,
    1669              :                        values,
    1670              :                        isnull);
    1671              : 
    1672              :         /*
    1673              :          * You'd think we should go ahead and build the index tuple here, but
    1674              :          * some index AMs want to do further processing on the data first.  So
    1675              :          * pass the values[] and isnull[] arrays, instead.
    1676              :          */
    1677              : 
    1678      8792788 :         if (HeapTupleIsHeapOnly(heapTuple))
    1679              :         {
    1680              :             /*
    1681              :              * For a heap-only tuple, pretend its TID is that of the root. See
    1682              :              * src/backend/access/heap/README.HOT for discussion.
    1683              :              */
    1684              :             ItemPointerData tid;
    1685              :             OffsetNumber offnum;
    1686              : 
    1687         3412 :             offnum = ItemPointerGetOffsetNumber(&heapTuple->t_self);
    1688              : 
    1689              :             /*
    1690              :              * If a HOT tuple points to a root that we don't know about,
    1691              :              * obtain root items afresh.  If that still fails, report it as
    1692              :              * corruption.
    1693              :              */
    1694         3412 :             if (root_offsets[offnum - 1] == InvalidOffsetNumber)
    1695              :             {
    1696            0 :                 Page        page = BufferGetPage(hscan->rs_cbuf);
    1697              : 
    1698            0 :                 LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
    1699            0 :                 heap_get_root_tuples(page, root_offsets);
    1700            0 :                 LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
    1701              :             }
    1702              : 
    1703         3412 :             if (!OffsetNumberIsValid(root_offsets[offnum - 1]))
    1704            0 :                 ereport(ERROR,
    1705              :                         (errcode(ERRCODE_DATA_CORRUPTED),
    1706              :                          errmsg_internal("failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
    1707              :                                          ItemPointerGetBlockNumber(&heapTuple->t_self),
    1708              :                                          offnum,
    1709              :                                          RelationGetRelationName(heapRelation))));
    1710              : 
    1711         3412 :             ItemPointerSet(&tid, ItemPointerGetBlockNumber(&heapTuple->t_self),
    1712         3412 :                            root_offsets[offnum - 1]);
    1713              : 
    1714              :             /* Call the AM's callback routine to process the tuple */
    1715         3412 :             callback(indexRelation, &tid, values, isnull, tupleIsAlive,
    1716              :                      callback_state);
    1717              :         }
    1718              :         else
    1719              :         {
    1720              :             /* Call the AM's callback routine to process the tuple */
    1721      8789376 :             callback(indexRelation, &heapTuple->t_self, values, isnull,
    1722              :                      tupleIsAlive, callback_state);
    1723              :         }
    1724              :     }
    1725              : 
    1726              :     /* Report scan progress one last time. */
    1727        29351 :     if (progress)
    1728              :     {
    1729              :         BlockNumber blks_done;
    1730              : 
    1731        27699 :         if (hscan->rs_base.rs_parallel != NULL)
    1732              :         {
    1733              :             ParallelBlockTableScanDesc pbscan;
    1734              : 
    1735          103 :             pbscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
    1736          103 :             blks_done = pbscan->phs_nblocks;
    1737              :         }
    1738              :         else
    1739        27596 :             blks_done = hscan->rs_nblocks;
    1740              : 
    1741        27699 :         pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE,
    1742              :                                      blks_done);
    1743              :     }
    1744              : 
    1745        29351 :     table_endscan(scan);
    1746              : 
    1747              :     /* we can now forget our snapshot, if set and registered by us */
    1748        29351 :     if (need_unregister_snapshot)
    1749         8484 :         UnregisterSnapshot(snapshot);
    1750              : 
    1751        29351 :     ExecDropSingleTupleTableSlot(slot);
    1752              : 
    1753        29351 :     FreeExecutorState(estate);
    1754              : 
    1755              :     /* These may have been pointing to the now-gone estate */
    1756        29351 :     indexInfo->ii_ExpressionsState = NIL;
    1757        29351 :     indexInfo->ii_PredicateState = NULL;
    1758              : 
    1759        29351 :     return reltuples;
    1760              : }
    1761              : 
    1762              : static void
    1763          369 : heapam_index_validate_scan(Relation heapRelation,
    1764              :                            Relation indexRelation,
    1765              :                            IndexInfo *indexInfo,
    1766              :                            Snapshot snapshot,
    1767              :                            ValidateIndexState *state)
    1768              : {
    1769              :     TableScanDesc scan;
    1770              :     HeapScanDesc hscan;
    1771              :     HeapTuple   heapTuple;
    1772              :     Datum       values[INDEX_MAX_KEYS];
    1773              :     bool        isnull[INDEX_MAX_KEYS];
    1774              :     ExprState  *predicate;
    1775              :     TupleTableSlot *slot;
    1776              :     EState     *estate;
    1777              :     ExprContext *econtext;
    1778          369 :     BlockNumber root_blkno = InvalidBlockNumber;
    1779              :     OffsetNumber root_offsets[MaxHeapTuplesPerPage];
    1780              :     bool        in_index[MaxHeapTuplesPerPage];
    1781          369 :     BlockNumber previous_blkno = InvalidBlockNumber;
    1782              : 
    1783              :     /* state variables for the merge */
    1784          369 :     ItemPointer indexcursor = NULL;
    1785              :     ItemPointerData decoded;
    1786          369 :     bool        tuplesort_empty = false;
    1787              : 
    1788              :     /*
    1789              :      * sanity checks
    1790              :      */
    1791              :     Assert(OidIsValid(indexRelation->rd_rel->relam));
    1792              : 
    1793              :     /*
    1794              :      * Need an EState for evaluation of index expressions and partial-index
    1795              :      * predicates.  Also a slot to hold the current tuple.
    1796              :      */
    1797          369 :     estate = CreateExecutorState();
    1798          369 :     econtext = GetPerTupleExprContext(estate);
    1799          369 :     slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation),
    1800              :                                     &TTSOpsHeapTuple);
    1801              : 
    1802              :     /* Arrange for econtext's scan tuple to be the tuple under test */
    1803          369 :     econtext->ecxt_scantuple = slot;
    1804              : 
    1805              :     /* Set up execution state for predicate, if any. */
    1806          369 :     predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
    1807              : 
    1808              :     /*
    1809              :      * Prepare for scan of the base relation.  We need just those tuples
    1810              :      * satisfying the passed-in reference snapshot.  We must disable syncscan
    1811              :      * here, because it's critical that we read from block zero forward to
    1812              :      * match the sorted TIDs.
    1813              :      */
    1814          369 :     scan = table_beginscan_strat(heapRelation,  /* relation */
    1815              :                                  snapshot,  /* snapshot */
    1816              :                                  0, /* number of keys */
    1817              :                                  NULL,  /* scan key */
    1818              :                                  true,  /* buffer access strategy OK */
    1819              :                                  false);    /* syncscan not OK */
    1820          369 :     hscan = (HeapScanDesc) scan;
    1821              : 
    1822          369 :     pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_TOTAL,
    1823          369 :                                  hscan->rs_nblocks);
    1824              : 
    1825              :     /*
    1826              :      * Scan all tuples matching the snapshot.
    1827              :      */
    1828       124324 :     while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    1829              :     {
    1830       123955 :         ItemPointer heapcursor = &heapTuple->t_self;
    1831              :         ItemPointerData rootTuple;
    1832              :         OffsetNumber root_offnum;
    1833              : 
    1834       123955 :         CHECK_FOR_INTERRUPTS();
    1835              : 
    1836       123955 :         state->htups += 1;
    1837              : 
    1838       123955 :         if ((previous_blkno == InvalidBlockNumber) ||
    1839       123735 :             (hscan->rs_cblock != previous_blkno))
    1840              :         {
    1841         2209 :             pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE,
    1842         2209 :                                          hscan->rs_cblock);
    1843         2209 :             previous_blkno = hscan->rs_cblock;
    1844              :         }
    1845              : 
    1846              :         /*
    1847              :          * As commented in table_index_build_scan, we should index heap-only
    1848              :          * tuples under the TIDs of their root tuples; so when we advance onto
    1849              :          * a new heap page, build a map of root item offsets on the page.
    1850              :          *
    1851              :          * This complicates merging against the tuplesort output: we will
    1852              :          * visit the live tuples in order by their offsets, but the root
    1853              :          * offsets that we need to compare against the index contents might be
    1854              :          * ordered differently.  So we might have to "look back" within the
    1855              :          * tuplesort output, but only within the current page.  We handle that
    1856              :          * by keeping a bool array in_index[] showing all the
    1857              :          * already-passed-over tuplesort output TIDs of the current page. We
    1858              :          * clear that array here, when advancing onto a new heap page.
    1859              :          */
    1860       123955 :         if (hscan->rs_cblock != root_blkno)
    1861              :         {
    1862         2209 :             Page        page = BufferGetPage(hscan->rs_cbuf);
    1863              : 
    1864         2209 :             LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
    1865         2209 :             heap_get_root_tuples(page, root_offsets);
    1866         2209 :             LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
    1867              : 
    1868         2209 :             memset(in_index, 0, sizeof(in_index));
    1869              : 
    1870         2209 :             root_blkno = hscan->rs_cblock;
    1871              :         }
    1872              : 
    1873              :         /* Convert actual tuple TID to root TID */
    1874       123955 :         rootTuple = *heapcursor;
    1875       123955 :         root_offnum = ItemPointerGetOffsetNumber(heapcursor);
    1876              : 
    1877       123955 :         if (HeapTupleIsHeapOnly(heapTuple))
    1878              :         {
    1879           12 :             root_offnum = root_offsets[root_offnum - 1];
    1880           12 :             if (!OffsetNumberIsValid(root_offnum))
    1881            0 :                 ereport(ERROR,
    1882              :                         (errcode(ERRCODE_DATA_CORRUPTED),
    1883              :                          errmsg_internal("failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
    1884              :                                          ItemPointerGetBlockNumber(heapcursor),
    1885              :                                          ItemPointerGetOffsetNumber(heapcursor),
    1886              :                                          RelationGetRelationName(heapRelation))));
    1887           12 :             ItemPointerSetOffsetNumber(&rootTuple, root_offnum);
    1888              :         }
    1889              : 
    1890              :         /*
    1891              :          * "merge" by skipping through the index tuples until we find or pass
    1892              :          * the current root tuple.
    1893              :          */
    1894       281017 :         while (!tuplesort_empty &&
    1895       280767 :                (!indexcursor ||
    1896       280767 :                 ItemPointerCompare(indexcursor, &rootTuple) < 0))
    1897              :         {
    1898              :             Datum       ts_val;
    1899              :             bool        ts_isnull;
    1900              : 
    1901       157062 :             if (indexcursor)
    1902              :             {
    1903              :                 /*
    1904              :                  * Remember index items seen earlier on the current heap page
    1905              :                  */
    1906       156842 :                 if (ItemPointerGetBlockNumber(indexcursor) == root_blkno)
    1907       154391 :                     in_index[ItemPointerGetOffsetNumber(indexcursor) - 1] = true;
    1908              :             }
    1909              : 
    1910       157062 :             tuplesort_empty = !tuplesort_getdatum(state->tuplesort, true,
    1911              :                                                   false, &ts_val, &ts_isnull,
    1912       157062 :                                                   NULL);
    1913              :             Assert(tuplesort_empty || !ts_isnull);
    1914       157062 :             if (!tuplesort_empty)
    1915              :             {
    1916       157041 :                 itemptr_decode(&decoded, DatumGetInt64(ts_val));
    1917       157041 :                 indexcursor = &decoded;
    1918              :             }
    1919              :             else
    1920              :             {
    1921              :                 /* Be tidy */
    1922           21 :                 indexcursor = NULL;
    1923              :             }
    1924              :         }
    1925              : 
    1926              :         /*
    1927              :          * If the tuplesort has overshot *and* we didn't see a match earlier,
    1928              :          * then this tuple is missing from the index, so insert it.
    1929              :          */
    1930       247880 :         if ((tuplesort_empty ||
    1931       123925 :              ItemPointerCompare(indexcursor, &rootTuple) > 0) &&
    1932           65 :             !in_index[root_offnum - 1])
    1933              :         {
    1934           56 :             MemoryContextReset(econtext->ecxt_per_tuple_memory);
    1935              : 
    1936              :             /* Set up for predicate or expression evaluation */
    1937           56 :             ExecStoreHeapTuple(heapTuple, slot, false);
    1938              : 
    1939              :             /*
    1940              :              * In a partial index, discard tuples that don't satisfy the
    1941              :              * predicate.
    1942              :              */
    1943           56 :             if (predicate != NULL)
    1944              :             {
    1945           24 :                 if (!ExecQual(predicate, econtext))
    1946           24 :                     continue;
    1947              :             }
    1948              : 
    1949              :             /*
    1950              :              * For the current heap tuple, extract all the attributes we use
    1951              :              * in this index, and note which are null.  This also performs
    1952              :              * evaluation of any expressions needed.
    1953              :              */
    1954           32 :             FormIndexDatum(indexInfo,
    1955              :                            slot,
    1956              :                            estate,
    1957              :                            values,
    1958              :                            isnull);
    1959              : 
    1960              :             /*
    1961              :              * You'd think we should go ahead and build the index tuple here,
    1962              :              * but some index AMs want to do further processing on the data
    1963              :              * first. So pass the values[] and isnull[] arrays, instead.
    1964              :              */
    1965              : 
    1966              :             /*
    1967              :              * If the tuple is already committed dead, you might think we
    1968              :              * could suppress uniqueness checking, but this is no longer true
    1969              :              * in the presence of HOT, because the insert is actually a proxy
    1970              :              * for a uniqueness check on the whole HOT-chain.  That is, the
    1971              :              * tuple we have here could be dead because it was already
    1972              :              * HOT-updated, and if so the updating transaction will not have
    1973              :              * thought it should insert index entries.  The index AM will
    1974              :              * check the whole HOT-chain and correctly detect a conflict if
    1975              :              * there is one.
    1976              :              */
    1977              : 
    1978           32 :             index_insert(indexRelation,
    1979              :                          values,
    1980              :                          isnull,
    1981              :                          &rootTuple,
    1982              :                          heapRelation,
    1983           32 :                          indexInfo->ii_Unique ?
    1984              :                          UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
    1985              :                          false,
    1986              :                          indexInfo);
    1987              : 
    1988           32 :             state->tups_inserted += 1;
    1989              :         }
    1990              :     }
    1991              : 
    1992          369 :     table_endscan(scan);
    1993              : 
    1994          369 :     ExecDropSingleTupleTableSlot(slot);
    1995              : 
    1996          369 :     FreeExecutorState(estate);
    1997              : 
    1998              :     /* These may have been pointing to the now-gone estate */
    1999          369 :     indexInfo->ii_ExpressionsState = NIL;
    2000          369 :     indexInfo->ii_PredicateState = NULL;
    2001          369 : }
    2002              : 
    2003              : /*
    2004              :  * Return the number of blocks that have been read by this scan since
    2005              :  * starting.  This is meant for progress reporting rather than be fully
    2006              :  * accurate: in a parallel scan, workers can be concurrently reading blocks
    2007              :  * further ahead than what we report.
    2008              :  */
    2009              : static BlockNumber
    2010      7418021 : heapam_scan_get_blocks_done(HeapScanDesc hscan)
    2011              : {
    2012      7418021 :     ParallelBlockTableScanDesc bpscan = NULL;
    2013              :     BlockNumber startblock;
    2014              :     BlockNumber blocks_done;
    2015              : 
    2016      7418021 :     if (hscan->rs_base.rs_parallel != NULL)
    2017              :     {
    2018      1001318 :         bpscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
    2019      1001318 :         startblock = bpscan->phs_startblock;
    2020              :     }
    2021              :     else
    2022      6416703 :         startblock = hscan->rs_startblock;
    2023              : 
    2024              :     /*
    2025              :      * Might have wrapped around the end of the relation, if startblock was
    2026              :      * not zero.
    2027              :      */
    2028      7418021 :     if (hscan->rs_cblock > startblock)
    2029      7127642 :         blocks_done = hscan->rs_cblock - startblock;
    2030              :     else
    2031              :     {
    2032              :         BlockNumber nblocks;
    2033              : 
    2034       290379 :         nblocks = bpscan != NULL ? bpscan->phs_nblocks : hscan->rs_nblocks;
    2035       290379 :         blocks_done = nblocks - startblock +
    2036       290379 :             hscan->rs_cblock;
    2037              :     }
    2038              : 
    2039      7418021 :     return blocks_done;
    2040              : }
    2041              : 
    2042              : 
    2043              : /* ------------------------------------------------------------------------
    2044              :  * Miscellaneous callbacks for the heap AM
    2045              :  * ------------------------------------------------------------------------
    2046              :  */
    2047              : 
    2048              : /*
    2049              :  * Check to see whether the table needs a TOAST table.  It does only if
    2050              :  * (1) there are any toastable attributes, and (2) the maximum length
    2051              :  * of a tuple could exceed TOAST_TUPLE_THRESHOLD.  (We don't want to
    2052              :  * create a toast table for something like "f1 varchar(20)".)
    2053              :  */
    2054              : static bool
    2055        23379 : heapam_relation_needs_toast_table(Relation rel)
    2056              : {
    2057        23379 :     int32       data_length = 0;
    2058        23379 :     bool        maxlength_unknown = false;
    2059        23379 :     bool        has_toastable_attrs = false;
    2060        23379 :     TupleDesc   tupdesc = rel->rd_att;
    2061              :     int32       tuple_length;
    2062              :     int         i;
    2063              : 
    2064        93526 :     for (i = 0; i < tupdesc->natts; i++)
    2065              :     {
    2066        70147 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
    2067              : 
    2068        70147 :         if (att->attisdropped)
    2069          567 :             continue;
    2070        69580 :         if (att->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
    2071          478 :             continue;
    2072        69102 :         data_length = att_align_nominal(data_length, att->attalign);
    2073        69102 :         if (att->attlen > 0)
    2074              :         {
    2075              :             /* Fixed-length types are never toastable */
    2076        51609 :             data_length += att->attlen;
    2077              :         }
    2078              :         else
    2079              :         {
    2080        17493 :             int32       maxlen = type_maximum_size(att->atttypid,
    2081              :                                                    att->atttypmod);
    2082              : 
    2083        17493 :             if (maxlen < 0)
    2084        15909 :                 maxlength_unknown = true;
    2085              :             else
    2086         1584 :                 data_length += maxlen;
    2087        17493 :             if (att->attstorage != TYPSTORAGE_PLAIN)
    2088        16873 :                 has_toastable_attrs = true;
    2089              :         }
    2090              :     }
    2091        23379 :     if (!has_toastable_attrs)
    2092        13440 :         return false;           /* nothing to toast? */
    2093         9939 :     if (maxlength_unknown)
    2094         8765 :         return true;            /* any unlimited-length attrs? */
    2095         1174 :     tuple_length = MAXALIGN(SizeofHeapTupleHeader +
    2096         1174 :                             BITMAPLEN(tupdesc->natts)) +
    2097         1174 :         MAXALIGN(data_length);
    2098         1174 :     return (tuple_length > TOAST_TUPLE_THRESHOLD);
    2099              : }
    2100              : 
    2101              : /*
    2102              :  * TOAST tables for heap relations are just heap relations.
    2103              :  */
    2104              : static Oid
    2105         9045 : heapam_relation_toast_am(Relation rel)
    2106              : {
    2107         9045 :     return rel->rd_rel->relam;
    2108              : }
    2109              : 
    2110              : 
    2111              : /* ------------------------------------------------------------------------
    2112              :  * Planner related callbacks for the heap AM
    2113              :  * ------------------------------------------------------------------------
    2114              :  */
    2115              : 
    2116              : #define HEAP_OVERHEAD_BYTES_PER_TUPLE \
    2117              :     (MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))
    2118              : #define HEAP_USABLE_BYTES_PER_PAGE \
    2119              :     (BLCKSZ - SizeOfPageHeaderData)
    2120              : 
    2121              : static void
    2122       251523 : heapam_estimate_rel_size(Relation rel, int32 *attr_widths,
    2123              :                          BlockNumber *pages, double *tuples,
    2124              :                          double *allvisfrac)
    2125              : {
    2126       251523 :     table_block_relation_estimate_size(rel, attr_widths, pages,
    2127              :                                        tuples, allvisfrac,
    2128              :                                        HEAP_OVERHEAD_BYTES_PER_TUPLE,
    2129              :                                        HEAP_USABLE_BYTES_PER_PAGE);
    2130       251523 : }
    2131              : 
    2132              : 
    2133              : /* ------------------------------------------------------------------------
    2134              :  * Executor related callbacks for the heap AM
    2135              :  * ------------------------------------------------------------------------
    2136              :  */
    2137              : 
    2138              : static bool
    2139      3469162 : heapam_scan_bitmap_next_tuple(TableScanDesc scan,
    2140              :                               TupleTableSlot *slot,
    2141              :                               bool *recheck,
    2142              :                               uint64 *lossy_pages,
    2143              :                               uint64 *exact_pages)
    2144              : {
    2145      3469162 :     BitmapHeapScanDesc bscan = (BitmapHeapScanDesc) scan;
    2146      3469162 :     HeapScanDesc hscan = (HeapScanDesc) bscan;
    2147              :     OffsetNumber targoffset;
    2148              :     Page        page;
    2149              :     ItemId      lp;
    2150              : 
    2151              :     /*
    2152              :      * Out of range?  If so, nothing more to look at on this page
    2153              :      */
    2154      3673723 :     while (hscan->rs_cindex >= hscan->rs_ntuples)
    2155              :     {
    2156              :         /*
    2157              :          * Returns false if the bitmap is exhausted and there are no further
    2158              :          * blocks we need to scan.
    2159              :          */
    2160       217833 :         if (!BitmapHeapScanNextBlock(scan, recheck, lossy_pages, exact_pages))
    2161        13269 :             return false;
    2162              :     }
    2163              : 
    2164      3455890 :     targoffset = hscan->rs_vistuples[hscan->rs_cindex];
    2165      3455890 :     page = BufferGetPage(hscan->rs_cbuf);
    2166      3455890 :     lp = PageGetItemId(page, targoffset);
    2167              :     Assert(ItemIdIsNormal(lp));
    2168              : 
    2169      3455890 :     hscan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
    2170      3455890 :     hscan->rs_ctup.t_len = ItemIdGetLength(lp);
    2171      3455890 :     hscan->rs_ctup.t_tableOid = scan->rs_rd->rd_id;
    2172      3455890 :     ItemPointerSet(&hscan->rs_ctup.t_self, hscan->rs_cblock, targoffset);
    2173              : 
    2174      3455890 :     pgstat_count_heap_fetch(scan->rs_rd);
    2175              : 
    2176              :     /*
    2177              :      * Set up the result slot to point to this tuple.  Note that the slot
    2178              :      * acquires a pin on the buffer.
    2179              :      */
    2180      3455890 :     ExecStoreBufferHeapTuple(&hscan->rs_ctup,
    2181              :                              slot,
    2182              :                              hscan->rs_cbuf);
    2183              : 
    2184      3455890 :     hscan->rs_cindex++;
    2185              : 
    2186      3455890 :     return true;
    2187              : }
    2188              : 
    2189              : static bool
    2190         6456 : heapam_scan_sample_next_block(TableScanDesc scan, SampleScanState *scanstate)
    2191              : {
    2192         6456 :     HeapScanDesc hscan = (HeapScanDesc) scan;
    2193         6456 :     TsmRoutine *tsm = scanstate->tsmroutine;
    2194              :     BlockNumber blockno;
    2195              : 
    2196              :     /* return false immediately if relation is empty */
    2197         6456 :     if (hscan->rs_nblocks == 0)
    2198            0 :         return false;
    2199              : 
    2200              :     /* release previous scan buffer, if any */
    2201         6456 :     if (BufferIsValid(hscan->rs_cbuf))
    2202              :     {
    2203         6368 :         ReleaseBuffer(hscan->rs_cbuf);
    2204         6368 :         hscan->rs_cbuf = InvalidBuffer;
    2205              :     }
    2206              : 
    2207         6456 :     if (tsm->NextSampleBlock)
    2208         2223 :         blockno = tsm->NextSampleBlock(scanstate, hscan->rs_nblocks);
    2209              :     else
    2210              :     {
    2211              :         /* scanning table sequentially */
    2212              : 
    2213         4233 :         if (hscan->rs_cblock == InvalidBlockNumber)
    2214              :         {
    2215              :             Assert(!hscan->rs_inited);
    2216           39 :             blockno = hscan->rs_startblock;
    2217              :         }
    2218              :         else
    2219              :         {
    2220              :             Assert(hscan->rs_inited);
    2221              : 
    2222         4194 :             blockno = hscan->rs_cblock + 1;
    2223              : 
    2224         4194 :             if (blockno >= hscan->rs_nblocks)
    2225              :             {
    2226              :                 /* wrap to beginning of rel, might not have started at 0 */
    2227           39 :                 blockno = 0;
    2228              :             }
    2229              : 
    2230              :             /*
    2231              :              * Report our new scan position for synchronization purposes.
    2232              :              *
    2233              :              * Note: we do this before checking for end of scan so that the
    2234              :              * final state of the position hint is back at the start of the
    2235              :              * rel.  That's not strictly necessary, but otherwise when you run
    2236              :              * the same query multiple times the starting position would shift
    2237              :              * a little bit backwards on every invocation, which is confusing.
    2238              :              * We don't guarantee any specific ordering in general, though.
    2239              :              */
    2240         4194 :             if (scan->rs_flags & SO_ALLOW_SYNC)
    2241            0 :                 ss_report_location(scan->rs_rd, blockno);
    2242              : 
    2243         4194 :             if (blockno == hscan->rs_startblock)
    2244              :             {
    2245           39 :                 blockno = InvalidBlockNumber;
    2246              :             }
    2247              :         }
    2248              :     }
    2249              : 
    2250         6456 :     hscan->rs_cblock = blockno;
    2251              : 
    2252         6456 :     if (!BlockNumberIsValid(blockno))
    2253              :     {
    2254           85 :         hscan->rs_inited = false;
    2255           85 :         return false;
    2256              :     }
    2257              : 
    2258              :     Assert(hscan->rs_cblock < hscan->rs_nblocks);
    2259              : 
    2260              :     /*
    2261              :      * Be sure to check for interrupts at least once per page.  Checks at
    2262              :      * higher code levels won't be able to stop a sample scan that encounters
    2263              :      * many pages' worth of consecutive dead tuples.
    2264              :      */
    2265         6371 :     CHECK_FOR_INTERRUPTS();
    2266              : 
    2267              :     /* Read page using selected strategy */
    2268         6371 :     hscan->rs_cbuf = ReadBufferExtended(hscan->rs_base.rs_rd, MAIN_FORKNUM,
    2269              :                                         blockno, RBM_NORMAL, hscan->rs_strategy);
    2270              : 
    2271              :     /* in pagemode, prune the page and determine visible tuple offsets */
    2272         6371 :     if (hscan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
    2273         4277 :         heap_prepare_pagescan(scan);
    2274              : 
    2275         6371 :     hscan->rs_inited = true;
    2276         6371 :     return true;
    2277              : }
    2278              : 
    2279              : static bool
    2280       126948 : heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate,
    2281              :                               TupleTableSlot *slot)
    2282              : {
    2283       126948 :     HeapScanDesc hscan = (HeapScanDesc) scan;
    2284       126948 :     TsmRoutine *tsm = scanstate->tsmroutine;
    2285       126948 :     BlockNumber blockno = hscan->rs_cblock;
    2286       126948 :     bool        pagemode = (scan->rs_flags & SO_ALLOW_PAGEMODE) != 0;
    2287              : 
    2288              :     Page        page;
    2289              :     bool        all_visible;
    2290              :     OffsetNumber maxoffset;
    2291              : 
    2292              :     /*
    2293              :      * When not using pagemode, we must lock the buffer during tuple
    2294              :      * visibility checks.
    2295              :      */
    2296       126948 :     if (!pagemode)
    2297         2097 :         LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
    2298              : 
    2299       126948 :     page = BufferGetPage(hscan->rs_cbuf);
    2300       253344 :     all_visible = PageIsAllVisible(page) &&
    2301       126396 :         !scan->rs_snapshot->takenDuringRecovery;
    2302       126948 :     maxoffset = PageGetMaxOffsetNumber(page);
    2303              : 
    2304              :     for (;;)
    2305            0 :     {
    2306              :         OffsetNumber tupoffset;
    2307              : 
    2308       126948 :         CHECK_FOR_INTERRUPTS();
    2309              : 
    2310              :         /* Ask the tablesample method which tuples to check on this page. */
    2311       126948 :         tupoffset = tsm->NextSampleTuple(scanstate,
    2312              :                                          blockno,
    2313              :                                          maxoffset);
    2314              : 
    2315       126948 :         if (OffsetNumberIsValid(tupoffset))
    2316              :         {
    2317              :             ItemId      itemid;
    2318              :             bool        visible;
    2319       120580 :             HeapTuple   tuple = &(hscan->rs_ctup);
    2320              : 
    2321              :             /* Skip invalid tuple pointers. */
    2322       120580 :             itemid = PageGetItemId(page, tupoffset);
    2323       120580 :             if (!ItemIdIsNormal(itemid))
    2324            0 :                 continue;
    2325              : 
    2326       120580 :             tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
    2327       120580 :             tuple->t_len = ItemIdGetLength(itemid);
    2328       120580 :             ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
    2329              : 
    2330              : 
    2331       120580 :             if (all_visible)
    2332       120174 :                 visible = true;
    2333              :             else
    2334          406 :                 visible = SampleHeapTupleVisible(scan, hscan->rs_cbuf,
    2335              :                                                  tuple, tupoffset);
    2336              : 
    2337              :             /* in pagemode, heap_prepare_pagescan did this for us */
    2338       120580 :             if (!pagemode)
    2339            3 :                 HeapCheckForSerializableConflictOut(visible, scan->rs_rd, tuple,
    2340              :                                                     hscan->rs_cbuf, scan->rs_snapshot);
    2341              : 
    2342              :             /* Try next tuple from same page. */
    2343       120580 :             if (!visible)
    2344            0 :                 continue;
    2345              : 
    2346              :             /* Found visible tuple, return it. */
    2347       120580 :             if (!pagemode)
    2348            3 :                 LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
    2349              : 
    2350       120580 :             ExecStoreBufferHeapTuple(tuple, slot, hscan->rs_cbuf);
    2351              : 
    2352              :             /* Count successfully-fetched tuples as heap fetches */
    2353       120580 :             pgstat_count_heap_getnext(scan->rs_rd);
    2354              : 
    2355       120580 :             return true;
    2356              :         }
    2357              :         else
    2358              :         {
    2359              :             /*
    2360              :              * If we get here, it means we've exhausted the items on this page
    2361              :              * and it's time to move to the next.
    2362              :              */
    2363         6368 :             if (!pagemode)
    2364         2094 :                 LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
    2365              : 
    2366         6368 :             ExecClearTuple(slot);
    2367         6368 :             return false;
    2368              :         }
    2369              :     }
    2370              : 
    2371              :     Assert(0);
    2372              : }
    2373              : 
    2374              : 
    2375              : /* ----------------------------------------------------------------------------
    2376              :  *  Helper functions for the above.
    2377              :  * ----------------------------------------------------------------------------
    2378              :  */
    2379              : 
    2380              : /*
    2381              :  * Reconstruct and rewrite the given tuple
    2382              :  *
    2383              :  * We cannot simply copy the tuple as-is, for several reasons:
    2384              :  *
    2385              :  * 1. We'd like to squeeze out the values of any dropped columns, both
    2386              :  * to save space and to ensure we have no corner-case failures. (It's
    2387              :  * possible for example that the new table hasn't got a TOAST table
    2388              :  * and so is unable to store any large values of dropped cols.)
    2389              :  *
    2390              :  * 2. The tuple might not even be legal for the new table; this is
    2391              :  * currently only known to happen as an after-effect of ALTER TABLE
    2392              :  * SET WITHOUT OIDS.
    2393              :  *
    2394              :  * So, we must reconstruct the tuple from component Datums.
    2395              :  */
    2396              : static void
    2397       354659 : reform_and_rewrite_tuple(HeapTuple tuple,
    2398              :                          Relation OldHeap, Relation NewHeap,
    2399              :                          Datum *values, bool *isnull, RewriteState rwstate)
    2400              : {
    2401       354659 :     TupleDesc   oldTupDesc = RelationGetDescr(OldHeap);
    2402       354659 :     TupleDesc   newTupDesc = RelationGetDescr(NewHeap);
    2403              :     HeapTuple   copiedTuple;
    2404              :     int         i;
    2405              : 
    2406       354659 :     heap_deform_tuple(tuple, oldTupDesc, values, isnull);
    2407              : 
    2408              :     /* Be sure to null out any dropped columns */
    2409      3174416 :     for (i = 0; i < newTupDesc->natts; i++)
    2410              :     {
    2411      2819757 :         if (TupleDescCompactAttr(newTupDesc, i)->attisdropped)
    2412            0 :             isnull[i] = true;
    2413              :     }
    2414              : 
    2415       354659 :     copiedTuple = heap_form_tuple(newTupDesc, values, isnull);
    2416              : 
    2417              :     /* The heap rewrite module does the rest */
    2418       354659 :     rewrite_heap_tuple(rwstate, tuple, copiedTuple);
    2419              : 
    2420       354659 :     heap_freetuple(copiedTuple);
    2421       354659 : }
    2422              : 
    2423              : /*
    2424              :  * Check visibility of the tuple.
    2425              :  */
    2426              : static bool
    2427          406 : SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer,
    2428              :                        HeapTuple tuple,
    2429              :                        OffsetNumber tupoffset)
    2430              : {
    2431          406 :     HeapScanDesc hscan = (HeapScanDesc) scan;
    2432              : 
    2433          406 :     if (scan->rs_flags & SO_ALLOW_PAGEMODE)
    2434              :     {
    2435          403 :         uint32      start = 0,
    2436          403 :                     end = hscan->rs_ntuples;
    2437              : 
    2438              :         /*
    2439              :          * In pageatatime mode, heap_prepare_pagescan() already did visibility
    2440              :          * checks, so just look at the info it left in rs_vistuples[].
    2441              :          *
    2442              :          * We use a binary search over the known-sorted array.  Note: we could
    2443              :          * save some effort if we insisted that NextSampleTuple select tuples
    2444              :          * in increasing order, but it's not clear that there would be enough
    2445              :          * gain to justify the restriction.
    2446              :          */
    2447          775 :         while (start < end)
    2448              :         {
    2449          775 :             uint32      mid = start + (end - start) / 2;
    2450          775 :             OffsetNumber curoffset = hscan->rs_vistuples[mid];
    2451              : 
    2452          775 :             if (tupoffset == curoffset)
    2453          403 :                 return true;
    2454          372 :             else if (tupoffset < curoffset)
    2455          220 :                 end = mid;
    2456              :             else
    2457          152 :                 start = mid + 1;
    2458              :         }
    2459              : 
    2460            0 :         return false;
    2461              :     }
    2462              :     else
    2463              :     {
    2464              :         /* Otherwise, we have to check the tuple individually. */
    2465            3 :         return HeapTupleSatisfiesVisibility(tuple, scan->rs_snapshot,
    2466              :                                             buffer);
    2467              :     }
    2468              : }
    2469              : 
    2470              : /*
    2471              :  * Helper function get the next block of a bitmap heap scan. Returns true when
    2472              :  * it got the next block and saved it in the scan descriptor and false when
    2473              :  * the bitmap and or relation are exhausted.
    2474              :  */
    2475              : static bool
    2476       217833 : BitmapHeapScanNextBlock(TableScanDesc scan,
    2477              :                         bool *recheck,
    2478              :                         uint64 *lossy_pages, uint64 *exact_pages)
    2479              : {
    2480       217833 :     BitmapHeapScanDesc bscan = (BitmapHeapScanDesc) scan;
    2481       217833 :     HeapScanDesc hscan = (HeapScanDesc) bscan;
    2482              :     BlockNumber block;
    2483              :     void       *per_buffer_data;
    2484              :     Buffer      buffer;
    2485              :     Snapshot    snapshot;
    2486              :     int         ntup;
    2487              :     TBMIterateResult *tbmres;
    2488              :     OffsetNumber offsets[TBM_MAX_TUPLES_PER_PAGE];
    2489       217833 :     int         noffsets = -1;
    2490              : 
    2491              :     Assert(scan->rs_flags & SO_TYPE_BITMAPSCAN);
    2492              :     Assert(hscan->rs_read_stream);
    2493              : 
    2494       217833 :     hscan->rs_cindex = 0;
    2495       217833 :     hscan->rs_ntuples = 0;
    2496              : 
    2497              :     /* Release buffer containing previous block. */
    2498       217833 :     if (BufferIsValid(hscan->rs_cbuf))
    2499              :     {
    2500       204367 :         ReleaseBuffer(hscan->rs_cbuf);
    2501       204367 :         hscan->rs_cbuf = InvalidBuffer;
    2502              :     }
    2503              : 
    2504       217833 :     hscan->rs_cbuf = read_stream_next_buffer(hscan->rs_read_stream,
    2505              :                                              &per_buffer_data);
    2506              : 
    2507       217833 :     if (BufferIsInvalid(hscan->rs_cbuf))
    2508              :     {
    2509              :         /* the bitmap is exhausted */
    2510        13269 :         return false;
    2511              :     }
    2512              : 
    2513              :     Assert(per_buffer_data);
    2514              : 
    2515       204564 :     tbmres = per_buffer_data;
    2516              : 
    2517              :     Assert(BlockNumberIsValid(tbmres->blockno));
    2518              :     Assert(BufferGetBlockNumber(hscan->rs_cbuf) == tbmres->blockno);
    2519              : 
    2520              :     /* Exact pages need their tuple offsets extracted. */
    2521       204564 :     if (!tbmres->lossy)
    2522       124364 :         noffsets = tbm_extract_page_tuple(tbmres, offsets,
    2523              :                                           TBM_MAX_TUPLES_PER_PAGE);
    2524              : 
    2525       204564 :     *recheck = tbmres->recheck;
    2526              : 
    2527       204564 :     block = hscan->rs_cblock = tbmres->blockno;
    2528       204564 :     buffer = hscan->rs_cbuf;
    2529       204564 :     snapshot = scan->rs_snapshot;
    2530              : 
    2531       204564 :     ntup = 0;
    2532              : 
    2533              :     /*
    2534              :      * Prune and repair fragmentation for the whole page, if possible.
    2535              :      */
    2536       204564 :     heap_page_prune_opt(scan->rs_rd, buffer);
    2537              : 
    2538              :     /*
    2539              :      * We must hold share lock on the buffer content while examining tuple
    2540              :      * visibility.  Afterwards, however, the tuples we have found to be
    2541              :      * visible are guaranteed good as long as we hold the buffer pin.
    2542              :      */
    2543       204564 :     LockBuffer(buffer, BUFFER_LOCK_SHARE);
    2544              : 
    2545              :     /*
    2546              :      * We need two separate strategies for lossy and non-lossy cases.
    2547              :      */
    2548       204564 :     if (!tbmres->lossy)
    2549              :     {
    2550              :         /*
    2551              :          * Bitmap is non-lossy, so we just look through the offsets listed in
    2552              :          * tbmres; but we have to follow any HOT chain starting at each such
    2553              :          * offset.
    2554              :          */
    2555              :         int         curslot;
    2556              : 
    2557              :         /* We must have extracted the tuple offsets by now */
    2558              :         Assert(noffsets > -1);
    2559              : 
    2560      3095738 :         for (curslot = 0; curslot < noffsets; curslot++)
    2561              :         {
    2562      2971377 :             OffsetNumber offnum = offsets[curslot];
    2563              :             ItemPointerData tid;
    2564              :             HeapTupleData heapTuple;
    2565              : 
    2566      2971377 :             ItemPointerSet(&tid, block, offnum);
    2567      2971377 :             if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
    2568              :                                        &heapTuple, NULL, true))
    2569      2845426 :                 hscan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
    2570              :         }
    2571              :     }
    2572              :     else
    2573              :     {
    2574              :         /*
    2575              :          * Bitmap is lossy, so we must examine each line pointer on the page.
    2576              :          * But we can ignore HOT chains, since we'll check each tuple anyway.
    2577              :          */
    2578        80200 :         Page        page = BufferGetPage(buffer);
    2579        80200 :         OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
    2580              :         OffsetNumber offnum;
    2581              : 
    2582       692425 :         for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
    2583              :         {
    2584              :             ItemId      lp;
    2585              :             HeapTupleData loctup;
    2586              :             bool        valid;
    2587              : 
    2588       612225 :             lp = PageGetItemId(page, offnum);
    2589       612225 :             if (!ItemIdIsNormal(lp))
    2590            0 :                 continue;
    2591       612225 :             loctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
    2592       612225 :             loctup.t_len = ItemIdGetLength(lp);
    2593       612225 :             loctup.t_tableOid = scan->rs_rd->rd_id;
    2594       612225 :             ItemPointerSet(&loctup.t_self, block, offnum);
    2595       612225 :             valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
    2596       612225 :             if (valid)
    2597              :             {
    2598       612162 :                 hscan->rs_vistuples[ntup++] = offnum;
    2599       612162 :                 PredicateLockTID(scan->rs_rd, &loctup.t_self, snapshot,
    2600       612162 :                                  HeapTupleHeaderGetXmin(loctup.t_data));
    2601              :             }
    2602       612225 :             HeapCheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
    2603              :                                                 buffer, snapshot);
    2604              :         }
    2605              :     }
    2606              : 
    2607       204561 :     LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
    2608              : 
    2609              :     Assert(ntup <= MaxHeapTuplesPerPage);
    2610       204561 :     hscan->rs_ntuples = ntup;
    2611              : 
    2612       204561 :     if (tbmres->lossy)
    2613        80200 :         (*lossy_pages)++;
    2614              :     else
    2615       124361 :         (*exact_pages)++;
    2616              : 
    2617              :     /*
    2618              :      * Return true to indicate that a valid block was found and the bitmap is
    2619              :      * not exhausted. If there are no visible tuples on this page,
    2620              :      * hscan->rs_ntuples will be 0 and heapam_scan_bitmap_next_tuple() will
    2621              :      * return false returning control to this function to advance to the next
    2622              :      * block in the bitmap.
    2623              :      */
    2624       204561 :     return true;
    2625              : }
    2626              : 
    2627              : /* ------------------------------------------------------------------------
    2628              :  * Definition of the heap table access method.
    2629              :  * ------------------------------------------------------------------------
    2630              :  */
    2631              : 
    2632              : static const TableAmRoutine heapam_methods = {
    2633              :     .type = T_TableAmRoutine,
    2634              : 
    2635              :     .slot_callbacks = heapam_slot_callbacks,
    2636              : 
    2637              :     .scan_begin = heap_beginscan,
    2638              :     .scan_end = heap_endscan,
    2639              :     .scan_rescan = heap_rescan,
    2640              :     .scan_getnextslot = heap_getnextslot,
    2641              : 
    2642              :     .scan_set_tidrange = heap_set_tidrange,
    2643              :     .scan_getnextslot_tidrange = heap_getnextslot_tidrange,
    2644              : 
    2645              :     .parallelscan_estimate = table_block_parallelscan_estimate,
    2646              :     .parallelscan_initialize = table_block_parallelscan_initialize,
    2647              :     .parallelscan_reinitialize = table_block_parallelscan_reinitialize,
    2648              : 
    2649              :     .index_fetch_begin = heapam_index_fetch_begin,
    2650              :     .index_fetch_reset = heapam_index_fetch_reset,
    2651              :     .index_fetch_end = heapam_index_fetch_end,
    2652              :     .index_fetch_tuple = heapam_index_fetch_tuple,
    2653              : 
    2654              :     .tuple_insert = heapam_tuple_insert,
    2655              :     .tuple_insert_speculative = heapam_tuple_insert_speculative,
    2656              :     .tuple_complete_speculative = heapam_tuple_complete_speculative,
    2657              :     .multi_insert = heap_multi_insert,
    2658              :     .tuple_delete = heapam_tuple_delete,
    2659              :     .tuple_update = heapam_tuple_update,
    2660              :     .tuple_lock = heapam_tuple_lock,
    2661              : 
    2662              :     .tuple_fetch_row_version = heapam_fetch_row_version,
    2663              :     .tuple_get_latest_tid = heap_get_latest_tid,
    2664              :     .tuple_tid_valid = heapam_tuple_tid_valid,
    2665              :     .tuple_satisfies_snapshot = heapam_tuple_satisfies_snapshot,
    2666              :     .index_delete_tuples = heap_index_delete_tuples,
    2667              : 
    2668              :     .relation_set_new_filelocator = heapam_relation_set_new_filelocator,
    2669              :     .relation_nontransactional_truncate = heapam_relation_nontransactional_truncate,
    2670              :     .relation_copy_data = heapam_relation_copy_data,
    2671              :     .relation_copy_for_cluster = heapam_relation_copy_for_cluster,
    2672              :     .relation_vacuum = heap_vacuum_rel,
    2673              :     .scan_analyze_next_block = heapam_scan_analyze_next_block,
    2674              :     .scan_analyze_next_tuple = heapam_scan_analyze_next_tuple,
    2675              :     .index_build_range_scan = heapam_index_build_range_scan,
    2676              :     .index_validate_scan = heapam_index_validate_scan,
    2677              : 
    2678              :     .relation_size = table_block_relation_size,
    2679              :     .relation_needs_toast_table = heapam_relation_needs_toast_table,
    2680              :     .relation_toast_am = heapam_relation_toast_am,
    2681              :     .relation_fetch_toast_slice = heap_fetch_toast_slice,
    2682              : 
    2683              :     .relation_estimate_size = heapam_estimate_rel_size,
    2684              : 
    2685              :     .scan_bitmap_next_tuple = heapam_scan_bitmap_next_tuple,
    2686              :     .scan_sample_next_block = heapam_scan_sample_next_block,
    2687              :     .scan_sample_next_tuple = heapam_scan_sample_next_tuple
    2688              : };
    2689              : 
    2690              : 
    2691              : const TableAmRoutine *
    2692      9967216 : GetHeapamTableAmRoutine(void)
    2693              : {
    2694      9967216 :     return &heapam_methods;
    2695              : }
    2696              : 
    2697              : Datum
    2698      1274900 : heap_tableam_handler(PG_FUNCTION_ARGS)
    2699              : {
    2700      1274900 :     PG_RETURN_POINTER(&heapam_methods);
    2701              : }
        

Generated by: LCOV version 2.0-1