PostgreSQL Source Code  git master
heapam.h File Reference
#include "access/relation.h"
#include "access/relscan.h"
#include "access/sdir.h"
#include "access/skey.h"
#include "access/table.h"
#include "access/tableam.h"
#include "nodes/lockoptions.h"
#include "nodes/primnodes.h"
#include "storage/bufpage.h"
#include "storage/dsm.h"
#include "storage/lockdefs.h"
#include "storage/shm_toc.h"
#include "utils/relcache.h"
#include "utils/snapshot.h"
Include dependency graph for heapam.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  HeapScanDescData
 
struct  IndexFetchHeapData
 
struct  HeapTupleFreeze
 
struct  HeapPageFreeze
 
struct  PruneResult
 

Macros

#define HEAP_INSERT_SKIP_FSM   TABLE_INSERT_SKIP_FSM
 
#define HEAP_INSERT_FROZEN   TABLE_INSERT_FROZEN
 
#define HEAP_INSERT_NO_LOGICAL   TABLE_INSERT_NO_LOGICAL
 
#define HEAP_INSERT_SPECULATIVE   0x0010
 
#define MaxLockTupleMode   LockTupleExclusive
 
#define HEAP_FREEZE_CHECK_XMIN_COMMITTED   0x01
 
#define HEAP_FREEZE_CHECK_XMAX_ABORTED   0x02
 
#define HeapScanIsValid(scan)   PointerIsValid(scan)
 

Typedefs

typedef struct BulkInsertStateDataBulkInsertState
 
typedef struct HeapScanDescData HeapScanDescData
 
typedef struct HeapScanDescDataHeapScanDesc
 
typedef struct IndexFetchHeapData IndexFetchHeapData
 
typedef struct HeapTupleFreeze HeapTupleFreeze
 
typedef struct HeapPageFreeze HeapPageFreeze
 
typedef struct PruneResult PruneResult
 

Enumerations

enum  HTSV_Result {
  HEAPTUPLE_DEAD , HEAPTUPLE_LIVE , HEAPTUPLE_RECENTLY_DEAD , HEAPTUPLE_INSERT_IN_PROGRESS ,
  HEAPTUPLE_DELETE_IN_PROGRESS
}
 

Functions

static HTSV_Result htsv_get_valid_status (int status)
 
TableScanDesc heap_beginscan (Relation relation, Snapshot snapshot, int nkeys, ScanKey key, ParallelTableScanDesc parallel_scan, uint32 flags)
 
void heap_setscanlimits (TableScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
 
void heapgetpage (TableScanDesc sscan, BlockNumber block)
 
void heap_rescan (TableScanDesc sscan, ScanKey key, bool set_params, bool allow_strat, bool allow_sync, bool allow_pagemode)
 
void heap_endscan (TableScanDesc sscan)
 
HeapTuple heap_getnext (TableScanDesc sscan, ScanDirection direction)
 
bool heap_getnextslot (TableScanDesc sscan, ScanDirection direction, struct TupleTableSlot *slot)
 
void heap_set_tidrange (TableScanDesc sscan, ItemPointer mintid, ItemPointer maxtid)
 
bool heap_getnextslot_tidrange (TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
 
bool heap_fetch (Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf)
 
bool heap_hot_search_buffer (ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, bool *all_dead, bool first_call)
 
void heap_get_latest_tid (TableScanDesc sscan, ItemPointer tid)
 
BulkInsertState GetBulkInsertState (void)
 
void FreeBulkInsertState (BulkInsertState)
 
void ReleaseBulkInsertStatePin (BulkInsertState bistate)
 
void heap_insert (Relation relation, HeapTuple tup, CommandId cid, int options, BulkInsertState bistate)
 
void heap_multi_insert (Relation relation, struct TupleTableSlot **slots, int ntuples, CommandId cid, int options, BulkInsertState bistate)
 
TM_Result heap_delete (Relation relation, ItemPointer tid, CommandId cid, Snapshot crosscheck, bool wait, struct TM_FailureData *tmfd, bool changingPart)
 
void heap_finish_speculative (Relation relation, ItemPointer tid)
 
void heap_abort_speculative (Relation relation, ItemPointer tid)
 
TM_Result heap_update (Relation relation, ItemPointer otid, HeapTuple newtup, CommandId cid, Snapshot crosscheck, bool wait, struct TM_FailureData *tmfd, LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
 
TM_Result heap_lock_tuple (Relation relation, HeapTuple tuple, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, bool follow_updates, Buffer *buffer, struct TM_FailureData *tmfd)
 
void heap_inplace_update (Relation relation, HeapTuple tuple)
 
bool heap_prepare_freeze_tuple (HeapTupleHeader tuple, const struct VacuumCutoffs *cutoffs, HeapPageFreeze *pagefrz, HeapTupleFreeze *frz, bool *totally_frozen)
 
void heap_freeze_execute_prepared (Relation rel, Buffer buffer, TransactionId snapshotConflictHorizon, HeapTupleFreeze *tuples, int ntuples)
 
bool heap_freeze_tuple (HeapTupleHeader tuple, TransactionId relfrozenxid, TransactionId relminmxid, TransactionId FreezeLimit, TransactionId MultiXactCutoff)
 
bool heap_tuple_should_freeze (HeapTupleHeader tuple, const struct VacuumCutoffs *cutoffs, TransactionId *NoFreezePageRelfrozenXid, MultiXactId *NoFreezePageRelminMxid)
 
bool heap_tuple_needs_eventual_freeze (HeapTupleHeader tuple)
 
void simple_heap_insert (Relation relation, HeapTuple tup)
 
void simple_heap_delete (Relation relation, ItemPointer tid)
 
void simple_heap_update (Relation relation, ItemPointer otid, HeapTuple tup, TU_UpdateIndexes *update_indexes)
 
TransactionId heap_index_delete_tuples (Relation rel, TM_IndexDeleteOp *delstate)
 
void heap_page_prune_opt (Relation relation, Buffer buffer)
 
void heap_page_prune (Relation relation, Buffer buffer, struct GlobalVisState *vistest, bool mark_unused_now, PruneResult *presult, OffsetNumber *off_loc)
 
void heap_page_prune_execute (Buffer buffer, OffsetNumber *redirected, int nredirected, OffsetNumber *nowdead, int ndead, OffsetNumber *nowunused, int nunused)
 
void heap_get_root_tuples (Page page, OffsetNumber *root_offsets)
 
void heap_vacuum_rel (Relation rel, struct VacuumParams *params, BufferAccessStrategy bstrategy)
 
bool HeapTupleSatisfiesVisibility (HeapTuple htup, Snapshot snapshot, Buffer buffer)
 
TM_Result HeapTupleSatisfiesUpdate (HeapTuple htup, CommandId curcid, Buffer buffer)
 
HTSV_Result HeapTupleSatisfiesVacuum (HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
 
HTSV_Result HeapTupleSatisfiesVacuumHorizon (HeapTuple htup, Buffer buffer, TransactionId *dead_after)
 
void HeapTupleSetHintBits (HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid)
 
bool HeapTupleHeaderIsOnlyLocked (HeapTupleHeader tuple)
 
bool HeapTupleIsSurelyDead (HeapTuple htup, struct GlobalVisState *vistest)
 
bool ResolveCminCmaxDuringDecoding (struct HTAB *tuplecid_data, Snapshot snapshot, HeapTuple htup, Buffer buffer, CommandId *cmin, CommandId *cmax)
 
void HeapCheckForSerializableConflictOut (bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
 

Macro Definition Documentation

◆ HEAP_FREEZE_CHECK_XMAX_ABORTED

#define HEAP_FREEZE_CHECK_XMAX_ABORTED   0x02

Definition at line 108 of file heapam.h.

◆ HEAP_FREEZE_CHECK_XMIN_COMMITTED

#define HEAP_FREEZE_CHECK_XMIN_COMMITTED   0x01

Definition at line 107 of file heapam.h.

◆ HEAP_INSERT_FROZEN

#define HEAP_INSERT_FROZEN   TABLE_INSERT_FROZEN

Definition at line 35 of file heapam.h.

◆ HEAP_INSERT_NO_LOGICAL

#define HEAP_INSERT_NO_LOGICAL   TABLE_INSERT_NO_LOGICAL

Definition at line 36 of file heapam.h.

◆ HEAP_INSERT_SKIP_FSM

#define HEAP_INSERT_SKIP_FSM   TABLE_INSERT_SKIP_FSM

Definition at line 34 of file heapam.h.

◆ HEAP_INSERT_SPECULATIVE

#define HEAP_INSERT_SPECULATIVE   0x0010

Definition at line 37 of file heapam.h.

◆ HeapScanIsValid

#define HeapScanIsValid (   scan)    PointerIsValid(scan)

Definition at line 241 of file heapam.h.

◆ MaxLockTupleMode

#define MaxLockTupleMode   LockTupleExclusive

Definition at line 43 of file heapam.h.

Typedef Documentation

◆ BulkInsertState

Definition at line 39 of file heapam.h.

◆ HeapPageFreeze

◆ HeapScanDesc

typedef struct HeapScanDescData* HeapScanDesc

Definition at line 80 of file heapam.h.

◆ HeapScanDescData

◆ HeapTupleFreeze

◆ IndexFetchHeapData

◆ PruneResult

typedef struct PruneResult PruneResult

Enumeration Type Documentation

◆ HTSV_Result

Enumerator
HEAPTUPLE_DEAD 
HEAPTUPLE_LIVE 
HEAPTUPLE_RECENTLY_DEAD 
HEAPTUPLE_INSERT_IN_PROGRESS 
HEAPTUPLE_DELETE_IN_PROGRESS 

Definition at line 94 of file heapam.h.

95 {
96  HEAPTUPLE_DEAD, /* tuple is dead and deletable */
97  HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
98  HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
99  HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
100  HEAPTUPLE_DELETE_IN_PROGRESS, /* deleting xact is still in progress */
101 } HTSV_Result;
HTSV_Result
Definition: heapam.h:95
@ HEAPTUPLE_RECENTLY_DEAD
Definition: heapam.h:98
@ HEAPTUPLE_INSERT_IN_PROGRESS
Definition: heapam.h:99
@ HEAPTUPLE_LIVE
Definition: heapam.h:97
@ HEAPTUPLE_DELETE_IN_PROGRESS
Definition: heapam.h:100
@ HEAPTUPLE_DEAD
Definition: heapam.h:96

Function Documentation

◆ FreeBulkInsertState()

void FreeBulkInsertState ( BulkInsertState  bistate)

Definition at line 1774 of file heapam.c.

1775 {
1776  if (bistate->current_buf != InvalidBuffer)
1777  ReleaseBuffer(bistate->current_buf);
1778  FreeAccessStrategy(bistate->strategy);
1779  pfree(bistate);
1780 }
#define InvalidBuffer
Definition: buf.h:25
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4560
void FreeAccessStrategy(BufferAccessStrategy strategy)
Definition: freelist.c:639
void pfree(void *pointer)
Definition: mcxt.c:1508
BufferAccessStrategy strategy
Definition: hio.h:31
Buffer current_buf
Definition: hio.h:32

References BulkInsertStateData::current_buf, FreeAccessStrategy(), InvalidBuffer, pfree(), ReleaseBuffer(), and BulkInsertStateData::strategy.

Referenced by ATRewriteTable(), CopyFrom(), CopyMultiInsertBufferCleanup(), intorel_shutdown(), and transientrel_shutdown().

◆ GetBulkInsertState()

BulkInsertState GetBulkInsertState ( void  )

Definition at line 1757 of file heapam.c.

1758 {
1759  BulkInsertState bistate;
1760 
1761  bistate = (BulkInsertState) palloc(sizeof(BulkInsertStateData));
1763  bistate->current_buf = InvalidBuffer;
1764  bistate->next_free = InvalidBlockNumber;
1765  bistate->last_free = InvalidBlockNumber;
1766  bistate->already_extended_by = 0;
1767  return bistate;
1768 }
#define InvalidBlockNumber
Definition: block.h:33
@ BAS_BULKWRITE
Definition: bufmgr.h:37
BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype)
Definition: freelist.c:541
struct BulkInsertStateData * BulkInsertState
Definition: heapam.h:39
void * palloc(Size size)
Definition: mcxt.c:1304
BlockNumber last_free
Definition: hio.h:49
uint32 already_extended_by
Definition: hio.h:50
BlockNumber next_free
Definition: hio.h:48

References BulkInsertStateData::already_extended_by, BAS_BULKWRITE, BulkInsertStateData::current_buf, GetAccessStrategy(), InvalidBlockNumber, InvalidBuffer, BulkInsertStateData::last_free, BulkInsertStateData::next_free, palloc(), and BulkInsertStateData::strategy.

Referenced by ATRewriteTable(), CopyFrom(), CopyMultiInsertBufferInit(), intorel_startup(), and transientrel_startup().

◆ heap_abort_speculative()

void heap_abort_speculative ( Relation  relation,
ItemPointer  tid 
)

Definition at line 5736 of file heapam.c.

5737 {
5739  ItemId lp;
5740  HeapTupleData tp;
5741  Page page;
5742  BlockNumber block;
5743  Buffer buffer;
5744  TransactionId prune_xid;
5745 
5746  Assert(ItemPointerIsValid(tid));
5747 
5748  block = ItemPointerGetBlockNumber(tid);
5749  buffer = ReadBuffer(relation, block);
5750  page = BufferGetPage(buffer);
5751 
5753 
5754  /*
5755  * Page can't be all visible, we just inserted into it, and are still
5756  * running.
5757  */
5758  Assert(!PageIsAllVisible(page));
5759 
5760  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
5761  Assert(ItemIdIsNormal(lp));
5762 
5763  tp.t_tableOid = RelationGetRelid(relation);
5764  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
5765  tp.t_len = ItemIdGetLength(lp);
5766  tp.t_self = *tid;
5767 
5768  /*
5769  * Sanity check that the tuple really is a speculatively inserted tuple,
5770  * inserted by us.
5771  */
5772  if (tp.t_data->t_choice.t_heap.t_xmin != xid)
5773  elog(ERROR, "attempted to kill a tuple inserted by another transaction");
5774  if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
5775  elog(ERROR, "attempted to kill a non-speculative tuple");
5777 
5778  /*
5779  * No need to check for serializable conflicts here. There is never a
5780  * need for a combo CID, either. No need to extract replica identity, or
5781  * do anything special with infomask bits.
5782  */
5783 
5785 
5786  /*
5787  * The tuple will become DEAD immediately. Flag that this page is a
5788  * candidate for pruning by setting xmin to TransactionXmin. While not
5789  * immediately prunable, it is the oldest xid we can cheaply determine
5790  * that's safe against wraparound / being older than the table's
5791  * relfrozenxid. To defend against the unlikely case of a new relation
5792  * having a newer relfrozenxid than our TransactionXmin, use relfrozenxid
5793  * if so (vacuum can't subsequently move relfrozenxid to beyond
5794  * TransactionXmin, so there's no race here).
5795  */
5797  if (TransactionIdPrecedes(TransactionXmin, relation->rd_rel->relfrozenxid))
5798  prune_xid = relation->rd_rel->relfrozenxid;
5799  else
5800  prune_xid = TransactionXmin;
5801  PageSetPrunable(page, prune_xid);
5802 
5803  /* store transaction information of xact deleting the tuple */
5806 
5807  /*
5808  * Set the tuple header xmin to InvalidTransactionId. This makes the
5809  * tuple immediately invisible everyone. (In particular, to any
5810  * transactions waiting on the speculative token, woken up later.)
5811  */
5813 
5814  /* Clear the speculative insertion token too */
5815  tp.t_data->t_ctid = tp.t_self;
5816 
5817  MarkBufferDirty(buffer);
5818 
5819  /*
5820  * XLOG stuff
5821  *
5822  * The WAL records generated here match heap_delete(). The same recovery
5823  * routines are used.
5824  */
5825  if (RelationNeedsWAL(relation))
5826  {
5827  xl_heap_delete xlrec;
5828  XLogRecPtr recptr;
5829 
5830  xlrec.flags = XLH_DELETE_IS_SUPER;
5832  tp.t_data->t_infomask2);
5834  xlrec.xmax = xid;
5835 
5836  XLogBeginInsert();
5837  XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
5838  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
5839 
5840  /* No replica identity & replication origin logged */
5841 
5842  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
5843 
5844  PageSetLSN(page, recptr);
5845  }
5846 
5847  END_CRIT_SECTION();
5848 
5849  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
5850 
5851  if (HeapTupleHasExternal(&tp))
5852  {
5853  Assert(!IsToastRelation(relation));
5854  heap_toast_delete(relation, &tp, true);
5855  }
5856 
5857  /*
5858  * Never need to mark tuple for invalidation, since catalogs don't support
5859  * speculative insertion
5860  */
5861 
5862  /* Now we can release the buffer */
5863  ReleaseBuffer(buffer);
5864 
5865  /* count deletion, as we counted the insertion too */
5866  pgstat_count_heap_delete(relation);
5867 }
uint32 BlockNumber
Definition: block.h:31
int Buffer
Definition: buf.h:23
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2189
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4795
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:734
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:157
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:350
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:159
Pointer Page
Definition: bufpage.h:78
static Item PageGetItem(Page page, ItemId itemId)
Definition: bufpage.h:351
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:240
static bool PageIsAllVisible(Page page)
Definition: bufpage.h:426
static void PageSetLSN(Page page, XLogRecPtr lsn)
Definition: bufpage.h:388
#define PageSetPrunable(page, xid)
Definition: bufpage.h:444
uint32 TransactionId
Definition: c.h:639
bool IsToastRelation(Relation relation)
Definition: catalog.c:145
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
static uint8 compute_infobits(uint16 infomask, uint16 infomask2)
Definition: heapam.c:2468
#define XLOG_HEAP_DELETE
Definition: heapam_xlog.h:33
#define SizeOfHeapDelete
Definition: heapam_xlog.h:115
#define XLH_DELETE_IS_SUPER
Definition: heapam_xlog.h:99
void heap_toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
Definition: heaptoast.c:43
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define HEAP_KEYS_UPDATED
Definition: htup_details.h:275
#define HeapTupleHeaderIsHeapOnly(tup)
Definition: htup_details.h:499
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:315
#define HEAP_XMAX_BITS
Definition: htup_details.h:267
#define HeapTupleHasExternal(tuple)
Definition: htup_details.h:671
#define HEAP_MOVED
Definition: htup_details.h:213
#define HeapTupleHeaderIsSpeculative(tup)
Definition: htup_details.h:428
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
Definition: itemptr.h:124
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition: itemptr.h:103
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition: itemptr.h:83
Assert(fmt[strlen(fmt) - 1] !='\n')
#define START_CRIT_SECTION()
Definition: miscadmin.h:149
#define END_CRIT_SECTION()
Definition: miscadmin.h:151
void pgstat_count_heap_delete(Relation rel)
#define RelationGetRelid(relation)
Definition: rel.h:505
#define RelationNeedsWAL(relation)
Definition: rel.h:628
TransactionId TransactionXmin
Definition: snapmgr.c:98
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
HeapTupleHeader t_data
Definition: htup.h:68
Oid t_tableOid
Definition: htup.h:66
TransactionId t_xmin
Definition: htup_details.h:124
union HeapTupleHeaderData::@45 t_choice
ItemPointerData t_ctid
Definition: htup_details.h:161
HeapTupleFields t_heap
Definition: htup_details.h:157
Form_pg_class rd_rel
Definition: rel.h:111
TransactionId xmax
Definition: heapam_xlog.h:109
OffsetNumber offnum
Definition: heapam_xlog.h:110
uint8 infobits_set
Definition: heapam_xlog.h:111
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:280
#define InvalidTransactionId
Definition: transam.h:31
#define TransactionIdIsValid(xid)
Definition: transam.h:41
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:446
uint64 XLogRecPtr
Definition: xlogdefs.h:21
void XLogRegisterData(char *data, uint32 len)
Definition: xloginsert.c:364
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:474
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:242
void XLogBeginInsert(void)
Definition: xloginsert.c:149
#define REGBUF_STANDARD
Definition: xloginsert.h:34

References Assert(), BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferGetPage(), compute_infobits(), elog, END_CRIT_SECTION, ERROR, xl_heap_delete::flags, GetCurrentTransactionId(), HEAP_KEYS_UPDATED, HEAP_MOVED, heap_toast_delete(), HEAP_XMAX_BITS, HeapTupleHasExternal, HeapTupleHeaderIsHeapOnly, HeapTupleHeaderIsSpeculative, HeapTupleHeaderSetXmin, xl_heap_delete::infobits_set, InvalidTransactionId, IsToastRelation(), ItemIdGetLength, ItemIdIsNormal, ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), ItemPointerIsValid(), LockBuffer(), MarkBufferDirty(), xl_heap_delete::offnum, PageGetItem(), PageGetItemId(), PageIsAllVisible(), PageSetLSN(), PageSetPrunable, pgstat_count_heap_delete(), RelationData::rd_rel, ReadBuffer(), REGBUF_STANDARD, RelationGetRelid, RelationNeedsWAL, ReleaseBuffer(), SizeOfHeapDelete, START_CRIT_SECTION, HeapTupleHeaderData::t_choice, HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleHeaderData::t_heap, HeapTupleHeaderData::t_infomask, HeapTupleHeaderData::t_infomask2, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, HeapTupleFields::t_xmin, TransactionIdIsValid, TransactionIdPrecedes(), TransactionXmin, XLH_DELETE_IS_SUPER, XLOG_HEAP_DELETE, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), XLogRegisterData(), and xl_heap_delete::xmax.

Referenced by heapam_tuple_complete_speculative(), and toast_delete_datum().

◆ heap_beginscan()

TableScanDesc heap_beginscan ( Relation  relation,
Snapshot  snapshot,
int  nkeys,
ScanKey  key,
ParallelTableScanDesc  parallel_scan,
uint32  flags 
)

Definition at line 927 of file heapam.c.

931 {
932  HeapScanDesc scan;
933 
934  /*
935  * increment relation ref count while scanning relation
936  *
937  * This is just to make really sure the relcache entry won't go away while
938  * the scan has a pointer to it. Caller should be holding the rel open
939  * anyway, so this is redundant in all normal scenarios...
940  */
942 
943  /*
944  * allocate and initialize scan descriptor
945  */
946  scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
947 
948  scan->rs_base.rs_rd = relation;
949  scan->rs_base.rs_snapshot = snapshot;
950  scan->rs_base.rs_nkeys = nkeys;
951  scan->rs_base.rs_flags = flags;
952  scan->rs_base.rs_parallel = parallel_scan;
953  scan->rs_strategy = NULL; /* set in initscan */
954 
955  /*
956  * Disable page-at-a-time mode if it's not a MVCC-safe snapshot.
957  */
958  if (!(snapshot && IsMVCCSnapshot(snapshot)))
960 
961  /*
962  * For seqscan and sample scans in a serializable transaction, acquire a
963  * predicate lock on the entire relation. This is required not only to
964  * lock all the matching tuples, but also to conflict with new insertions
965  * into the table. In an indexscan, we take page locks on the index pages
966  * covering the range specified in the scan qual, but in a heap scan there
967  * is nothing more fine-grained to lock. A bitmap scan is a different
968  * story, there we have already scanned the index and locked the index
969  * pages covering the predicate. But in that case we still have to lock
970  * any matching heap tuples. For sample scan we could optimize the locking
971  * to be at least page-level granularity, but we'd need to add per-tuple
972  * locking for that.
973  */
975  {
976  /*
977  * Ensure a missing snapshot is noticed reliably, even if the
978  * isolation mode means predicate locking isn't performed (and
979  * therefore the snapshot isn't used here).
980  */
981  Assert(snapshot);
982  PredicateLockRelation(relation, snapshot);
983  }
984 
985  /* we only need to set this up once */
986  scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
987 
988  /*
989  * Allocate memory to keep track of page allocation for parallel workers
990  * when doing a parallel scan.
991  */
992  if (parallel_scan != NULL)
994  else
995  scan->rs_parallelworkerdata = NULL;
996 
997  /*
998  * we do this here instead of in initscan() because heap_rescan also calls
999  * initscan() and we don't want to allocate memory again
1000  */
1001  if (nkeys > 0)
1002  scan->rs_base.rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
1003  else
1004  scan->rs_base.rs_key = NULL;
1005 
1006  initscan(scan, key, false);
1007 
1008  return (TableScanDesc) scan;
1009 }
static void initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
Definition: heapam.c:229
struct HeapScanDescData * HeapScanDesc
Definition: heapam.h:80
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
void PredicateLockRelation(Relation relation, Snapshot snapshot)
Definition: predicate.c:2556
void RelationIncrementReferenceCount(Relation rel)
Definition: relcache.c:2153
ScanKeyData * ScanKey
Definition: skey.h:75
#define IsMVCCSnapshot(snapshot)
Definition: snapmgr.h:62
BufferAccessStrategy rs_strategy
Definition: heapam.h:65
ParallelBlockTableScanWorkerData * rs_parallelworkerdata
Definition: heapam.h:73
HeapTupleData rs_ctup
Definition: heapam.h:67
TableScanDescData rs_base
Definition: heapam.h:50
Relation rs_rd
Definition: relscan.h:34
uint32 rs_flags
Definition: relscan.h:47
struct ScanKeyData * rs_key
Definition: relscan.h:37
struct SnapshotData * rs_snapshot
Definition: relscan.h:35
struct ParallelTableScanDescData * rs_parallel
Definition: relscan.h:49
@ SO_ALLOW_PAGEMODE
Definition: tableam.h:61
@ SO_TYPE_SAMPLESCAN
Definition: tableam.h:50
@ SO_TYPE_SEQSCAN
Definition: tableam.h:48

References Assert(), if(), initscan(), IsMVCCSnapshot, sort-test::key, palloc(), PredicateLockRelation(), RelationGetRelid, RelationIncrementReferenceCount(), HeapScanDescData::rs_base, HeapScanDescData::rs_ctup, TableScanDescData::rs_flags, TableScanDescData::rs_key, TableScanDescData::rs_nkeys, TableScanDescData::rs_parallel, HeapScanDescData::rs_parallelworkerdata, TableScanDescData::rs_rd, TableScanDescData::rs_snapshot, HeapScanDescData::rs_strategy, SO_ALLOW_PAGEMODE, SO_TYPE_SAMPLESCAN, SO_TYPE_SEQSCAN, and HeapTupleData::t_tableOid.

◆ heap_delete()

TM_Result heap_delete ( Relation  relation,
ItemPointer  tid,
CommandId  cid,
Snapshot  crosscheck,
bool  wait,
struct TM_FailureData tmfd,
bool  changingPart 
)

Definition at line 2513 of file heapam.c.

2516 {
2517  TM_Result result;
2519  ItemId lp;
2520  HeapTupleData tp;
2521  Page page;
2522  BlockNumber block;
2523  Buffer buffer;
2524  Buffer vmbuffer = InvalidBuffer;
2525  TransactionId new_xmax;
2526  uint16 new_infomask,
2527  new_infomask2;
2528  bool have_tuple_lock = false;
2529  bool iscombo;
2530  bool all_visible_cleared = false;
2531  HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
2532  bool old_key_copied = false;
2533 
2534  Assert(ItemPointerIsValid(tid));
2535 
2536  /*
2537  * Forbid this during a parallel operation, lest it allocate a combo CID.
2538  * Other workers might need that combo CID for visibility checks, and we
2539  * have no provision for broadcasting it to them.
2540  */
2541  if (IsInParallelMode())
2542  ereport(ERROR,
2543  (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
2544  errmsg("cannot delete tuples during a parallel operation")));
2545 
2546  block = ItemPointerGetBlockNumber(tid);
2547  buffer = ReadBuffer(relation, block);
2548  page = BufferGetPage(buffer);
2549 
2550  /*
2551  * Before locking the buffer, pin the visibility map page if it appears to
2552  * be necessary. Since we haven't got the lock yet, someone else might be
2553  * in the middle of changing this, so we'll need to recheck after we have
2554  * the lock.
2555  */
2556  if (PageIsAllVisible(page))
2557  visibilitymap_pin(relation, block, &vmbuffer);
2558 
2560 
2561  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
2562  Assert(ItemIdIsNormal(lp));
2563 
2564  tp.t_tableOid = RelationGetRelid(relation);
2565  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2566  tp.t_len = ItemIdGetLength(lp);
2567  tp.t_self = *tid;
2568 
2569 l1:
2570 
2571  /*
2572  * If we didn't pin the visibility map page and the page has become all
2573  * visible while we were busy locking the buffer, we'll have to unlock and
2574  * re-lock, to avoid holding the buffer lock across an I/O. That's a bit
2575  * unfortunate, but hopefully shouldn't happen often.
2576  */
2577  if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
2578  {
2579  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2580  visibilitymap_pin(relation, block, &vmbuffer);
2582  }
2583 
2584  result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
2585 
2586  if (result == TM_Invisible)
2587  {
2588  UnlockReleaseBuffer(buffer);
2589  ereport(ERROR,
2590  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2591  errmsg("attempted to delete invisible tuple")));
2592  }
2593  else if (result == TM_BeingModified && wait)
2594  {
2595  TransactionId xwait;
2596  uint16 infomask;
2597 
2598  /* must copy state data before unlocking buffer */
2599  xwait = HeapTupleHeaderGetRawXmax(tp.t_data);
2600  infomask = tp.t_data->t_infomask;
2601 
2602  /*
2603  * Sleep until concurrent transaction ends -- except when there's a
2604  * single locker and it's our own transaction. Note we don't care
2605  * which lock mode the locker has, because we need the strongest one.
2606  *
2607  * Before sleeping, we need to acquire tuple lock to establish our
2608  * priority for the tuple (see heap_lock_tuple). LockTuple will
2609  * release us when we are next-in-line for the tuple.
2610  *
2611  * If we are forced to "start over" below, we keep the tuple lock;
2612  * this arranges that we stay at the head of the line while rechecking
2613  * tuple state.
2614  */
2615  if (infomask & HEAP_XMAX_IS_MULTI)
2616  {
2617  bool current_is_member = false;
2618 
2619  if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
2620  LockTupleExclusive, &current_is_member))
2621  {
2622  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2623 
2624  /*
2625  * Acquire the lock, if necessary (but skip it when we're
2626  * requesting a lock and already have one; avoids deadlock).
2627  */
2628  if (!current_is_member)
2630  LockWaitBlock, &have_tuple_lock);
2631 
2632  /* wait for multixact */
2634  relation, &(tp.t_self), XLTW_Delete,
2635  NULL);
2637 
2638  /*
2639  * If xwait had just locked the tuple then some other xact
2640  * could update this tuple before we get to this point. Check
2641  * for xmax change, and start over if so.
2642  *
2643  * We also must start over if we didn't pin the VM page, and
2644  * the page has become all visible.
2645  */
2646  if ((vmbuffer == InvalidBuffer && PageIsAllVisible(page)) ||
2647  xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
2649  xwait))
2650  goto l1;
2651  }
2652 
2653  /*
2654  * You might think the multixact is necessarily done here, but not
2655  * so: it could have surviving members, namely our own xact or
2656  * other subxacts of this backend. It is legal for us to delete
2657  * the tuple in either case, however (the latter case is
2658  * essentially a situation of upgrading our former shared lock to
2659  * exclusive). We don't bother changing the on-disk hint bits
2660  * since we are about to overwrite the xmax altogether.
2661  */
2662  }
2663  else if (!TransactionIdIsCurrentTransactionId(xwait))
2664  {
2665  /*
2666  * Wait for regular transaction to end; but first, acquire tuple
2667  * lock.
2668  */
2669  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2671  LockWaitBlock, &have_tuple_lock);
2672  XactLockTableWait(xwait, relation, &(tp.t_self), XLTW_Delete);
2674 
2675  /*
2676  * xwait is done, but if xwait had just locked the tuple then some
2677  * other xact could update this tuple before we get to this point.
2678  * Check for xmax change, and start over if so.
2679  *
2680  * We also must start over if we didn't pin the VM page, and the
2681  * page has become all visible.
2682  */
2683  if ((vmbuffer == InvalidBuffer && PageIsAllVisible(page)) ||
2684  xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
2686  xwait))
2687  goto l1;
2688 
2689  /* Otherwise check if it committed or aborted */
2690  UpdateXmaxHintBits(tp.t_data, buffer, xwait);
2691  }
2692 
2693  /*
2694  * We may overwrite if previous xmax aborted, or if it committed but
2695  * only locked the tuple without updating it.
2696  */
2697  if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
2700  result = TM_Ok;
2701  else if (!ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
2702  result = TM_Updated;
2703  else
2704  result = TM_Deleted;
2705  }
2706 
2707  /* sanity check the result HeapTupleSatisfiesUpdate() and the logic above */
2708  if (result != TM_Ok)
2709  {
2710  Assert(result == TM_SelfModified ||
2711  result == TM_Updated ||
2712  result == TM_Deleted ||
2713  result == TM_BeingModified);
2715  Assert(result != TM_Updated ||
2716  !ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid));
2717  }
2718 
2719  if (crosscheck != InvalidSnapshot && result == TM_Ok)
2720  {
2721  /* Perform additional check for transaction-snapshot mode RI updates */
2722  if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
2723  result = TM_Updated;
2724  }
2725 
2726  if (result != TM_Ok)
2727  {
2728  tmfd->ctid = tp.t_data->t_ctid;
2730  if (result == TM_SelfModified)
2731  tmfd->cmax = HeapTupleHeaderGetCmax(tp.t_data);
2732  else
2733  tmfd->cmax = InvalidCommandId;
2734  UnlockReleaseBuffer(buffer);
2735  if (have_tuple_lock)
2736  UnlockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive);
2737  if (vmbuffer != InvalidBuffer)
2738  ReleaseBuffer(vmbuffer);
2739  return result;
2740  }
2741 
2742  /*
2743  * We're about to do the actual delete -- check for conflict first, to
2744  * avoid possibly having to roll back work we've just done.
2745  *
2746  * This is safe without a recheck as long as there is no possibility of
2747  * another process scanning the page between this check and the delete
2748  * being visible to the scan (i.e., an exclusive buffer content lock is
2749  * continuously held from this point until the tuple delete is visible).
2750  */
2751  CheckForSerializableConflictIn(relation, tid, BufferGetBlockNumber(buffer));
2752 
2753  /* replace cid with a combo CID if necessary */
2754  HeapTupleHeaderAdjustCmax(tp.t_data, &cid, &iscombo);
2755 
2756  /*
2757  * Compute replica identity tuple before entering the critical section so
2758  * we don't PANIC upon a memory allocation failure.
2759  */
2760  old_key_tuple = ExtractReplicaIdentity(relation, &tp, true, &old_key_copied);
2761 
2762  /*
2763  * If this is the first possibly-multixact-able operation in the current
2764  * transaction, set my per-backend OldestMemberMXactId setting. We can be
2765  * certain that the transaction will never become a member of any older
2766  * MultiXactIds than that. (We have to do this even if we end up just
2767  * using our own TransactionId below, since some other backend could
2768  * incorporate our XID into a MultiXact immediately afterwards.)
2769  */
2771 
2774  xid, LockTupleExclusive, true,
2775  &new_xmax, &new_infomask, &new_infomask2);
2776 
2778 
2779  /*
2780  * If this transaction commits, the tuple will become DEAD sooner or
2781  * later. Set flag that this page is a candidate for pruning once our xid
2782  * falls below the OldestXmin horizon. If the transaction finally aborts,
2783  * the subsequent page pruning will be a no-op and the hint will be
2784  * cleared.
2785  */
2786  PageSetPrunable(page, xid);
2787 
2788  if (PageIsAllVisible(page))
2789  {
2790  all_visible_cleared = true;
2791  PageClearAllVisible(page);
2792  visibilitymap_clear(relation, BufferGetBlockNumber(buffer),
2793  vmbuffer, VISIBILITYMAP_VALID_BITS);
2794  }
2795 
2796  /* store transaction information of xact deleting the tuple */
2799  tp.t_data->t_infomask |= new_infomask;
2800  tp.t_data->t_infomask2 |= new_infomask2;
2802  HeapTupleHeaderSetXmax(tp.t_data, new_xmax);
2803  HeapTupleHeaderSetCmax(tp.t_data, cid, iscombo);
2804  /* Make sure there is no forward chain link in t_ctid */
2805  tp.t_data->t_ctid = tp.t_self;
2806 
2807  /* Signal that this is actually a move into another partition */
2808  if (changingPart)
2810 
2811  MarkBufferDirty(buffer);
2812 
2813  /*
2814  * XLOG stuff
2815  *
2816  * NB: heap_abort_speculative() uses the same xlog record and replay
2817  * routines.
2818  */
2819  if (RelationNeedsWAL(relation))
2820  {
2821  xl_heap_delete xlrec;
2822  xl_heap_header xlhdr;
2823  XLogRecPtr recptr;
2824 
2825  /*
2826  * For logical decode we need combo CIDs to properly decode the
2827  * catalog
2828  */
2830  log_heap_new_cid(relation, &tp);
2831 
2832  xlrec.flags = 0;
2833  if (all_visible_cleared)
2835  if (changingPart)
2838  tp.t_data->t_infomask2);
2840  xlrec.xmax = new_xmax;
2841 
2842  if (old_key_tuple != NULL)
2843  {
2844  if (relation->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
2846  else
2848  }
2849 
2850  XLogBeginInsert();
2851  XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
2852 
2853  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
2854 
2855  /*
2856  * Log replica identity of the deleted tuple if there is one
2857  */
2858  if (old_key_tuple != NULL)
2859  {
2860  xlhdr.t_infomask2 = old_key_tuple->t_data->t_infomask2;
2861  xlhdr.t_infomask = old_key_tuple->t_data->t_infomask;
2862  xlhdr.t_hoff = old_key_tuple->t_data->t_hoff;
2863 
2864  XLogRegisterData((char *) &xlhdr, SizeOfHeapHeader);
2865  XLogRegisterData((char *) old_key_tuple->t_data
2867  old_key_tuple->t_len
2869  }
2870 
2871  /* filtering by origin on a row level is much more efficient */
2873 
2874  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
2875 
2876  PageSetLSN(page, recptr);
2877  }
2878 
2879  END_CRIT_SECTION();
2880 
2881  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2882 
2883  if (vmbuffer != InvalidBuffer)
2884  ReleaseBuffer(vmbuffer);
2885 
2886  /*
2887  * If the tuple has toasted out-of-line attributes, we need to delete
2888  * those items too. We have to do this before releasing the buffer
2889  * because we need to look at the contents of the tuple, but it's OK to
2890  * release the content lock on the buffer first.
2891  */
2892  if (relation->rd_rel->relkind != RELKIND_RELATION &&
2893  relation->rd_rel->relkind != RELKIND_MATVIEW)
2894  {
2895  /* toast table entries should never be recursively toasted */
2897  }
2898  else if (HeapTupleHasExternal(&tp))
2899  heap_toast_delete(relation, &tp, false);
2900 
2901  /*
2902  * Mark tuple for invalidation from system caches at next command
2903  * boundary. We have to do this before releasing the buffer because we
2904  * need to look at the contents of the tuple.
2905  */
2906  CacheInvalidateHeapTuple(relation, &tp, NULL);
2907 
2908  /* Now we can release the buffer */
2909  ReleaseBuffer(buffer);
2910 
2911  /*
2912  * Release the lmgr tuple lock, if we had it.
2913  */
2914  if (have_tuple_lock)
2915  UnlockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive);
2916 
2917  pgstat_count_heap_delete(relation);
2918 
2919  if (old_key_tuple != NULL && old_key_copied)
2920  heap_freetuple(old_key_tuple);
2921 
2922  return TM_Ok;
2923 }
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:3377
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4577
static void PageClearAllVisible(Page page)
Definition: bufpage.h:436
#define InvalidCommandId
Definition: c.h:656
unsigned short uint16
Definition: c.h:492
TransactionId MultiXactId
Definition: c.h:649
void HeapTupleHeaderAdjustCmax(HeapTupleHeader tup, CommandId *cmax, bool *iscombo)
Definition: combocid.c:153
CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup)
Definition: combocid.c:118
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ereport(elevel,...)
Definition: elog.h:149
static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask, LockTupleMode lockmode, bool *current_is_member)
Definition: heapam.c:7122
static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup)
Definition: heapam.c:8586
static void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask, uint16 old_infomask2, TransactionId add_to_xmax, LockTupleMode mode, bool is_update, TransactionId *result_xmax, uint16 *result_infomask, uint16 *result_infomask2)
Definition: heapam.c:4887
static HeapTuple ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool key_required, bool *copy)
Definition: heapam.c:8667
static bool heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode, LockWaitPolicy wait_policy, bool *have_tuple_lock)
Definition: heapam.c:4838
static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, ItemPointer ctid, XLTW_Oper oper, int *remaining)
Definition: heapam.c:7299
static bool xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
Definition: heapam.c:2490
#define UnlockTupleTuplock(rel, tup, mode)
Definition: heapam.c:167
static void UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
Definition: heapam.c:1735
bool HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
TM_Result HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, Buffer buffer)
#define XLH_DELETE_CONTAINS_OLD_KEY
Definition: heapam_xlog.h:98
#define XLH_DELETE_ALL_VISIBLE_CLEARED
Definition: heapam_xlog.h:96
#define SizeOfHeapHeader
Definition: heapam_xlog.h:151
#define XLH_DELETE_IS_PARTITION_MOVE
Definition: heapam_xlog.h:100
#define XLH_DELETE_CONTAINS_OLD_TUPLE
Definition: heapam_xlog.h:97
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
#define HEAP_XMAX_IS_LOCKED_ONLY(infomask)
Definition: htup_details.h:227
#define SizeofHeapTupleHeader
Definition: htup_details.h:185
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:376
#define HeapTupleHeaderClearHotUpdated(tup)
Definition: htup_details.h:494
#define HEAP_XMAX_IS_MULTI
Definition: htup_details.h:209
#define HEAP_XMAX_INVALID
Definition: htup_details.h:208
#define HeapTupleHeaderSetMovedPartitions(tup)
Definition: htup_details.h:447
#define HeapTupleHeaderGetRawXmax(tup)
Definition: htup_details.h:371
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:361
#define HeapTupleHeaderSetCmax(tup, cid, iscombo)
Definition: htup_details.h:401
void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple)
Definition: inval.c:1204
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:35
void XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, XLTW_Oper oper)
Definition: lmgr.c:667
@ XLTW_Delete
Definition: lmgr.h:28
@ LockWaitBlock
Definition: lockoptions.h:39
@ LockTupleExclusive
Definition: lockoptions.h:58
void MultiXactIdSetOldestMember(void)
Definition: multixact.c:624
@ MultiXactStatusUpdate
Definition: multixact.h:46
void CheckForSerializableConflictIn(Relation relation, ItemPointer tid, BlockNumber blkno)
Definition: predicate.c:4316
#define RelationIsAccessibleInLogicalDecoding(relation)
Definition: rel.h:684
#define InvalidSnapshot
Definition: snapshot.h:123
TransactionId xmax
Definition: tableam.h:143
CommandId cmax
Definition: tableam.h:144
ItemPointerData ctid
Definition: tableam.h:142
uint16 t_infomask
Definition: heapam_xlog.h:147
uint16 t_infomask2
Definition: heapam_xlog.h:146
TM_Result
Definition: tableam.h:72
@ TM_Ok
Definition: tableam.h:77
@ TM_BeingModified
Definition: tableam.h:99
@ TM_Deleted
Definition: tableam.h:92
@ TM_Updated
Definition: tableam.h:89
@ TM_SelfModified
Definition: tableam.h:83
@ TM_Invisible
Definition: tableam.h:80
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
bool visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer vmbuf, uint8 flags)
void visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *vmbuf)
#define VISIBILITYMAP_VALID_BITS
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:927
bool IsInParallelMode(void)
Definition: xact.c:1070
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:152
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:456

References Assert(), BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage(), CacheInvalidateHeapTuple(), CheckForSerializableConflictIn(), TM_FailureData::cmax, compute_infobits(), compute_new_xmax_infomask(), TM_FailureData::ctid, DoesMultiXactIdConflict(), END_CRIT_SECTION, ereport, errcode(), errmsg(), ERROR, ExtractReplicaIdentity(), xl_heap_delete::flags, GetCurrentTransactionId(), heap_acquire_tuplock(), heap_freetuple(), HEAP_KEYS_UPDATED, HEAP_MOVED, heap_toast_delete(), HEAP_XMAX_BITS, HEAP_XMAX_INVALID, HEAP_XMAX_IS_LOCKED_ONLY, HEAP_XMAX_IS_MULTI, HeapTupleHasExternal, HeapTupleHeaderAdjustCmax(), HeapTupleHeaderClearHotUpdated, HeapTupleHeaderGetCmax(), HeapTupleHeaderGetRawXmax, HeapTupleHeaderGetUpdateXid, HeapTupleHeaderIsOnlyLocked(), HeapTupleHeaderSetCmax, HeapTupleHeaderSetMovedPartitions, HeapTupleHeaderSetXmax, HeapTupleSatisfiesUpdate(), HeapTupleSatisfiesVisibility(), xl_heap_delete::infobits_set, InvalidBuffer, InvalidCommandId, InvalidSnapshot, IsInParallelMode(), ItemIdGetLength, ItemIdIsNormal, ItemPointerEquals(), ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), ItemPointerIsValid(), LockBuffer(), LockTupleExclusive, LockWaitBlock, log_heap_new_cid(), MarkBufferDirty(), MultiXactIdSetOldestMember(), MultiXactIdWait(), MultiXactStatusUpdate, xl_heap_delete::offnum, PageClearAllVisible(), PageGetItem(), PageGetItemId(), PageIsAllVisible(), PageSetLSN(), PageSetPrunable, pgstat_count_heap_delete(), RelationData::rd_rel, ReadBuffer(), REGBUF_STANDARD, RelationGetRelid, RelationIsAccessibleInLogicalDecoding, RelationNeedsWAL, ReleaseBuffer(), SizeOfHeapDelete, SizeOfHeapHeader, SizeofHeapTupleHeader, START_CRIT_SECTION, HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, xl_heap_header::t_hoff, HeapTupleHeaderData::t_hoff, xl_heap_header::t_infomask, HeapTupleHeaderData::t_infomask, xl_heap_header::t_infomask2, HeapTupleHeaderData::t_infomask2, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TM_BeingModified, TM_Deleted, TM_Invisible, TM_Ok, TM_SelfModified, TM_Updated, TransactionIdEquals, TransactionIdIsCurrentTransactionId(), UnlockReleaseBuffer(), UnlockTupleTuplock, UpdateXmaxHintBits(), visibilitymap_clear(), visibilitymap_pin(), VISIBILITYMAP_VALID_BITS, XactLockTableWait(), XLH_DELETE_ALL_VISIBLE_CLEARED, XLH_DELETE_CONTAINS_OLD_KEY, XLH_DELETE_CONTAINS_OLD_TUPLE, XLH_DELETE_IS_PARTITION_MOVE, XLOG_HEAP_DELETE, XLOG_INCLUDE_ORIGIN, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), XLogRegisterData(), XLogSetRecordFlags(), XLTW_Delete, xl_heap_delete::xmax, TM_FailureData::xmax, and xmax_infomask_changed().

Referenced by heapam_tuple_delete(), and simple_heap_delete().

◆ heap_endscan()

void heap_endscan ( TableScanDesc  sscan)

Definition at line 1049 of file heapam.c.

1050 {
1051  HeapScanDesc scan = (HeapScanDesc) sscan;
1052 
1053  /* Note: no locking manipulations needed */
1054 
1055  /*
1056  * unpin scan buffers
1057  */
1058  if (BufferIsValid(scan->rs_cbuf))
1059  ReleaseBuffer(scan->rs_cbuf);
1060 
1061  /*
1062  * decrement relation reference count and free scan descriptor storage
1063  */
1065 
1066  if (scan->rs_base.rs_key)
1067  pfree(scan->rs_base.rs_key);
1068 
1069  if (scan->rs_strategy != NULL)
1071 
1072  if (scan->rs_parallelworkerdata != NULL)
1074 
1075  if (scan->rs_base.rs_flags & SO_TEMP_SNAPSHOT)
1077 
1078  pfree(scan);
1079 }
static bool BufferIsValid(Buffer bufnum)
Definition: bufmgr.h:301
void RelationDecrementReferenceCount(Relation rel)
Definition: relcache.c:2166
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:836
Buffer rs_cbuf
Definition: heapam.h:62
@ SO_TEMP_SNAPSHOT
Definition: tableam.h:64

References BufferIsValid(), FreeAccessStrategy(), pfree(), RelationDecrementReferenceCount(), ReleaseBuffer(), HeapScanDescData::rs_base, HeapScanDescData::rs_cbuf, TableScanDescData::rs_flags, TableScanDescData::rs_key, HeapScanDescData::rs_parallelworkerdata, TableScanDescData::rs_rd, TableScanDescData::rs_snapshot, HeapScanDescData::rs_strategy, SO_TEMP_SNAPSHOT, and UnregisterSnapshot().

◆ heap_fetch()

bool heap_fetch ( Relation  relation,
Snapshot  snapshot,
HeapTuple  tuple,
Buffer userbuf,
bool  keep_buf 
)

Definition at line 1341 of file heapam.c.

1346 {
1347  ItemPointer tid = &(tuple->t_self);
1348  ItemId lp;
1349  Buffer buffer;
1350  Page page;
1351  OffsetNumber offnum;
1352  bool valid;
1353 
1354  /*
1355  * Fetch and pin the appropriate page of the relation.
1356  */
1357  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
1358 
1359  /*
1360  * Need share lock on buffer to examine tuple commit status.
1361  */
1362  LockBuffer(buffer, BUFFER_LOCK_SHARE);
1363  page = BufferGetPage(buffer);
1364 
1365  /*
1366  * We'd better check for out-of-range offnum in case of VACUUM since the
1367  * TID was obtained.
1368  */
1369  offnum = ItemPointerGetOffsetNumber(tid);
1370  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
1371  {
1372  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1373  ReleaseBuffer(buffer);
1374  *userbuf = InvalidBuffer;
1375  tuple->t_data = NULL;
1376  return false;
1377  }
1378 
1379  /*
1380  * get the item line pointer corresponding to the requested tid
1381  */
1382  lp = PageGetItemId(page, offnum);
1383 
1384  /*
1385  * Must check for deleted tuple.
1386  */
1387  if (!ItemIdIsNormal(lp))
1388  {
1389  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1390  ReleaseBuffer(buffer);
1391  *userbuf = InvalidBuffer;
1392  tuple->t_data = NULL;
1393  return false;
1394  }
1395 
1396  /*
1397  * fill in *tuple fields
1398  */
1399  tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
1400  tuple->t_len = ItemIdGetLength(lp);
1401  tuple->t_tableOid = RelationGetRelid(relation);
1402 
1403  /*
1404  * check tuple visibility, then release lock
1405  */
1406  valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
1407 
1408  if (valid)
1409  PredicateLockTID(relation, &(tuple->t_self), snapshot,
1410  HeapTupleHeaderGetXmin(tuple->t_data));
1411 
1412  HeapCheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
1413 
1414  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1415 
1416  if (valid)
1417  {
1418  /*
1419  * All checks passed, so return the tuple as valid. Caller is now
1420  * responsible for releasing the buffer.
1421  */
1422  *userbuf = buffer;
1423 
1424  return true;
1425  }
1426 
1427  /* Tuple failed time qual, but maybe caller wants to see it anyway. */
1428  if (keep_buf)
1429  *userbuf = buffer;
1430  else
1431  {
1432  ReleaseBuffer(buffer);
1433  *userbuf = InvalidBuffer;
1434  tuple->t_data = NULL;
1435  }
1436 
1437  return false;
1438 }
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:158
static OffsetNumber PageGetMaxOffsetNumber(Page page)
Definition: bufpage.h:369
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition: heapam.c:10153
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:309
uint16 OffsetNumber
Definition: off.h:24
void PredicateLockTID(Relation relation, ItemPointer tid, Snapshot snapshot, TransactionId tuple_xid)
Definition: predicate.c:2601

References BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetPage(), HeapCheckForSerializableConflictOut(), HeapTupleHeaderGetXmin, HeapTupleSatisfiesVisibility(), InvalidBuffer, ItemIdGetLength, ItemIdIsNormal, ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), LockBuffer(), PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), PredicateLockTID(), ReadBuffer(), RelationGetRelid, ReleaseBuffer(), HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, and HeapTupleData::t_tableOid.

Referenced by heap_lock_updated_tuple_rec(), heapam_fetch_row_version(), and heapam_tuple_lock().

◆ heap_finish_speculative()

void heap_finish_speculative ( Relation  relation,
ItemPointer  tid 
)

Definition at line 5649 of file heapam.c.

5650 {
5651  Buffer buffer;
5652  Page page;
5653  OffsetNumber offnum;
5654  ItemId lp = NULL;
5655  HeapTupleHeader htup;
5656 
5657  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
5659  page = (Page) BufferGetPage(buffer);
5660 
5661  offnum = ItemPointerGetOffsetNumber(tid);
5662  if (PageGetMaxOffsetNumber(page) >= offnum)
5663  lp = PageGetItemId(page, offnum);
5664 
5665  if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
5666  elog(ERROR, "invalid lp");
5667 
5668  htup = (HeapTupleHeader) PageGetItem(page, lp);
5669 
5670  /* NO EREPORT(ERROR) from here till changes are logged */
5672 
5674 
5675  MarkBufferDirty(buffer);
5676 
5677  /*
5678  * Replace the speculative insertion token with a real t_ctid, pointing to
5679  * itself like it does on regular tuples.
5680  */
5681  htup->t_ctid = *tid;
5682 
5683  /* XLOG stuff */
5684  if (RelationNeedsWAL(relation))
5685  {
5686  xl_heap_confirm xlrec;
5687  XLogRecPtr recptr;
5688 
5689  xlrec.offnum = ItemPointerGetOffsetNumber(tid);
5690 
5691  XLogBeginInsert();
5692 
5693  /* We want the same filtering on this as on a plain insert */
5695 
5696  XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
5697  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
5698 
5699  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
5700 
5701  PageSetLSN(page, recptr);
5702  }
5703 
5704  END_CRIT_SECTION();
5705 
5706  UnlockReleaseBuffer(buffer);
5707 }
#define SizeOfHeapConfirm
Definition: heapam_xlog.h:307
#define XLOG_HEAP_CONFIRM
Definition: heapam_xlog.h:37
OffsetNumber offnum
Definition: heapam_xlog.h:304

References Assert(), BUFFER_LOCK_EXCLUSIVE, BufferGetPage(), elog, END_CRIT_SECTION, ERROR, HeapTupleHeaderIsSpeculative, ItemIdIsNormal, ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), LockBuffer(), MarkBufferDirty(), xl_heap_confirm::offnum, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), PageSetLSN(), ReadBuffer(), REGBUF_STANDARD, RelationNeedsWAL, SizeOfHeapConfirm, START_CRIT_SECTION, HeapTupleHeaderData::t_ctid, UnlockReleaseBuffer(), XLOG_HEAP_CONFIRM, XLOG_INCLUDE_ORIGIN, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), XLogRegisterData(), and XLogSetRecordFlags().

Referenced by heapam_tuple_complete_speculative().

◆ heap_freeze_execute_prepared()

void heap_freeze_execute_prepared ( Relation  rel,
Buffer  buffer,
TransactionId  snapshotConflictHorizon,
HeapTupleFreeze tuples,
int  ntuples 
)

Definition at line 6678 of file heapam.c.

6681 {
6682  Page page = BufferGetPage(buffer);
6683 
6684  Assert(ntuples > 0);
6685 
6686  /*
6687  * Perform xmin/xmax XID status sanity checks before critical section.
6688  *
6689  * heap_prepare_freeze_tuple doesn't perform these checks directly because
6690  * pg_xact lookups are relatively expensive. They shouldn't be repeated
6691  * by successive VACUUMs that each decide against freezing the same page.
6692  */
6693  for (int i = 0; i < ntuples; i++)
6694  {
6695  HeapTupleFreeze *frz = tuples + i;
6696  ItemId itemid = PageGetItemId(page, frz->offset);
6697  HeapTupleHeader htup;
6698 
6699  htup = (HeapTupleHeader) PageGetItem(page, itemid);
6700 
6701  /* Deliberately avoid relying on tuple hint bits here */
6703  {
6705 
6707  if (unlikely(!TransactionIdDidCommit(xmin)))
6708  ereport(ERROR,
6710  errmsg_internal("uncommitted xmin %u needs to be frozen",
6711  xmin)));
6712  }
6713 
6714  /*
6715  * TransactionIdDidAbort won't work reliably in the presence of XIDs
6716  * left behind by transactions that were in progress during a crash,
6717  * so we can only check that xmax didn't commit
6718  */
6720  {
6722 
6724  if (unlikely(TransactionIdDidCommit(xmax)))
6725  ereport(ERROR,
6727  errmsg_internal("cannot freeze committed xmax %u",
6728  xmax)));
6729  }
6730  }
6731 
6733 
6734  for (int i = 0; i < ntuples; i++)
6735  {
6736  HeapTupleFreeze *frz = tuples + i;
6737  ItemId itemid = PageGetItemId(page, frz->offset);
6738  HeapTupleHeader htup;
6739 
6740  htup = (HeapTupleHeader) PageGetItem(page, itemid);
6741  heap_execute_freeze_tuple(htup, frz);
6742  }
6743 
6744  MarkBufferDirty(buffer);
6745 
6746  /* Now WAL-log freezing if necessary */
6747  if (RelationNeedsWAL(rel))
6748  {
6751  int nplans;
6752  xl_heap_freeze_page xlrec;
6753  XLogRecPtr recptr;
6754 
6755  /* Prepare deduplicated representation for use in WAL record */
6756  nplans = heap_log_freeze_plan(tuples, ntuples, plans, offsets);
6757 
6758  xlrec.snapshotConflictHorizon = snapshotConflictHorizon;
6760  xlrec.nplans = nplans;
6761 
6762  XLogBeginInsert();
6763  XLogRegisterData((char *) &xlrec, SizeOfHeapFreezePage);
6764 
6765  /*
6766  * The freeze plan array and offset array are not actually in the
6767  * buffer, but pretend that they are. When XLogInsert stores the
6768  * whole buffer, the arrays need not be stored too.
6769  */
6770  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
6771  XLogRegisterBufData(0, (char *) plans,
6772  nplans * sizeof(xl_heap_freeze_plan));
6773  XLogRegisterBufData(0, (char *) offsets,
6774  ntuples * sizeof(OffsetNumber));
6775 
6776  recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_FREEZE_PAGE);
6777 
6778  PageSetLSN(page, recptr);
6779  }
6780 
6781  END_CRIT_SECTION();
6782 }
#define unlikely(x)
Definition: c.h:298
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1159
static int heap_log_freeze_plan(HeapTupleFreeze *tuples, int ntuples, xl_heap_freeze_plan *plans_out, OffsetNumber *offsets_out)
Definition: heapam.c:6872
static void heap_execute_freeze_tuple(HeapTupleHeader tuple, HeapTupleFreeze *frz)
Definition: heapam.c:6649
#define HEAP_FREEZE_CHECK_XMAX_ABORTED
Definition: heapam.h:108
#define HEAP_FREEZE_CHECK_XMIN_COMMITTED
Definition: heapam.h:107
#define SizeOfHeapFreezePage
Definition: heapam_xlog.h:357
#define XLOG_HEAP2_FREEZE_PAGE
Definition: heapam_xlog.h:56
#define HeapTupleHeaderGetRawXmin(tup)
Definition: htup_details.h:304
#define HeapTupleHeaderXminFrozen(tup)
Definition: htup_details.h:331
#define MaxHeapTuplesPerPage
Definition: htup_details.h:572
int i
Definition: isn.c:73
#define ERRCODE_DATA_CORRUPTED
Definition: pg_basebackup.c:41
OffsetNumber offset
Definition: heapam.h:122
uint8 checkflags
Definition: heapam.h:120
TransactionId snapshotConflictHorizon
Definition: heapam_xlog.h:347
bool TransactionIdDidCommit(TransactionId transactionId)
Definition: transam.c:126
#define TransactionIdIsNormal(xid)
Definition: transam.h:42
void XLogRegisterBufData(uint8 block_id, char *data, uint32 len)
Definition: xloginsert.c:405

References Assert(), BufferGetPage(), HeapTupleFreeze::checkflags, END_CRIT_SECTION, ereport, errcode(), ERRCODE_DATA_CORRUPTED, errmsg_internal(), ERROR, heap_execute_freeze_tuple(), HEAP_FREEZE_CHECK_XMAX_ABORTED, HEAP_FREEZE_CHECK_XMIN_COMMITTED, heap_log_freeze_plan(), HeapTupleHeaderGetRawXmax, HeapTupleHeaderGetRawXmin, HeapTupleHeaderXminFrozen, i, xl_heap_freeze_page::isCatalogRel, MarkBufferDirty(), MaxHeapTuplesPerPage, xl_heap_freeze_page::nplans, HeapTupleFreeze::offset, PageGetItem(), PageGetItemId(), PageSetLSN(), REGBUF_STANDARD, RelationIsAccessibleInLogicalDecoding, RelationNeedsWAL, SizeOfHeapFreezePage, xl_heap_freeze_page::snapshotConflictHorizon, START_CRIT_SECTION, TransactionIdDidCommit(), TransactionIdIsNormal, unlikely, XLOG_HEAP2_FREEZE_PAGE, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by lazy_scan_prune().

◆ heap_freeze_tuple()

bool heap_freeze_tuple ( HeapTupleHeader  tuple,
TransactionId  relfrozenxid,
TransactionId  relminmxid,
TransactionId  FreezeLimit,
TransactionId  MultiXactCutoff 
)

Definition at line 6929 of file heapam.c.

6932 {
6933  HeapTupleFreeze frz;
6934  bool do_freeze;
6935  bool totally_frozen;
6936  struct VacuumCutoffs cutoffs;
6937  HeapPageFreeze pagefrz;
6938 
6939  cutoffs.relfrozenxid = relfrozenxid;
6940  cutoffs.relminmxid = relminmxid;
6941  cutoffs.OldestXmin = FreezeLimit;
6942  cutoffs.OldestMxact = MultiXactCutoff;
6943  cutoffs.FreezeLimit = FreezeLimit;
6944  cutoffs.MultiXactCutoff = MultiXactCutoff;
6945 
6946  pagefrz.freeze_required = true;
6947  pagefrz.FreezePageRelfrozenXid = FreezeLimit;
6948  pagefrz.FreezePageRelminMxid = MultiXactCutoff;
6949  pagefrz.NoFreezePageRelfrozenXid = FreezeLimit;
6950  pagefrz.NoFreezePageRelminMxid = MultiXactCutoff;
6951 
6952  do_freeze = heap_prepare_freeze_tuple(tuple, &cutoffs,
6953  &pagefrz, &frz, &totally_frozen);
6954 
6955  /*
6956  * Note that because this is not a WAL-logged operation, we don't need to
6957  * fill in the offset in the freeze record.
6958  */
6959 
6960  if (do_freeze)
6961  heap_execute_freeze_tuple(tuple, &frz);
6962  return do_freeze;
6963 }
bool heap_prepare_freeze_tuple(HeapTupleHeader tuple, const struct VacuumCutoffs *cutoffs, HeapPageFreeze *pagefrz, HeapTupleFreeze *frz, bool *totally_frozen)
Definition: heapam.c:6375
TransactionId FreezeLimit
Definition: vacuum.h:276
TransactionId relfrozenxid
Definition: vacuum.h:250
MultiXactId relminmxid
Definition: vacuum.h:251
MultiXactId MultiXactCutoff
Definition: vacuum.h:277

References VacuumCutoffs::FreezeLimit, heap_execute_freeze_tuple(), heap_prepare_freeze_tuple(), VacuumCutoffs::MultiXactCutoff, VacuumCutoffs::OldestMxact, VacuumCutoffs::OldestXmin, VacuumCutoffs::relfrozenxid, and VacuumCutoffs::relminmxid.

Referenced by rewrite_heap_tuple().

◆ heap_get_latest_tid()

void heap_get_latest_tid ( TableScanDesc  sscan,
ItemPointer  tid 
)

Definition at line 1613 of file heapam.c.

1615 {
1616  Relation relation = sscan->rs_rd;
1617  Snapshot snapshot = sscan->rs_snapshot;
1618  ItemPointerData ctid;
1619  TransactionId priorXmax;
1620 
1621  /*
1622  * table_tuple_get_latest_tid() verified that the passed in tid is valid.
1623  * Assume that t_ctid links are valid however - there shouldn't be invalid
1624  * ones in the table.
1625  */
1626  Assert(ItemPointerIsValid(tid));
1627 
1628  /*
1629  * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
1630  * need to examine, and *tid is the TID we will return if ctid turns out
1631  * to be bogus.
1632  *
1633  * Note that we will loop until we reach the end of the t_ctid chain.
1634  * Depending on the snapshot passed, there might be at most one visible
1635  * version of the row, but we don't try to optimize for that.
1636  */
1637  ctid = *tid;
1638  priorXmax = InvalidTransactionId; /* cannot check first XMIN */
1639  for (;;)
1640  {
1641  Buffer buffer;
1642  Page page;
1643  OffsetNumber offnum;
1644  ItemId lp;
1645  HeapTupleData tp;
1646  bool valid;
1647 
1648  /*
1649  * Read, pin, and lock the page.
1650  */
1651  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
1652  LockBuffer(buffer, BUFFER_LOCK_SHARE);
1653  page = BufferGetPage(buffer);
1654 
1655  /*
1656  * Check for bogus item number. This is not treated as an error
1657  * condition because it can happen while following a t_ctid link. We
1658  * just assume that the prior tid is OK and return it unchanged.
1659  */
1660  offnum = ItemPointerGetOffsetNumber(&ctid);
1661  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
1662  {
1663  UnlockReleaseBuffer(buffer);
1664  break;
1665  }
1666  lp = PageGetItemId(page, offnum);
1667  if (!ItemIdIsNormal(lp))
1668  {
1669  UnlockReleaseBuffer(buffer);
1670  break;
1671  }
1672 
1673  /* OK to access the tuple */
1674  tp.t_self = ctid;
1675  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
1676  tp.t_len = ItemIdGetLength(lp);
1677  tp.t_tableOid = RelationGetRelid(relation);
1678 
1679  /*
1680  * After following a t_ctid link, we might arrive at an unrelated
1681  * tuple. Check for XMIN match.
1682  */
1683  if (TransactionIdIsValid(priorXmax) &&
1685  {
1686  UnlockReleaseBuffer(buffer);
1687  break;
1688  }
1689 
1690  /*
1691  * Check tuple visibility; if visible, set it as the new result
1692  * candidate.
1693  */
1694  valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
1695  HeapCheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
1696  if (valid)
1697  *tid = ctid;
1698 
1699  /*
1700  * If there's a valid t_ctid link, follow it, else we're done.
1701  */
1702  if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
1706  {
1707  UnlockReleaseBuffer(buffer);
1708  break;
1709  }
1710 
1711  ctid = tp.t_data->t_ctid;
1712  priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
1713  UnlockReleaseBuffer(buffer);
1714  } /* end of loop */
1715 }
#define HeapTupleHeaderIndicatesMovedPartitions(tup)
Definition: htup_details.h:444

References Assert(), BUFFER_LOCK_SHARE, BufferGetPage(), HEAP_XMAX_INVALID, HeapCheckForSerializableConflictOut(), HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleHeaderIndicatesMovedPartitions, HeapTupleHeaderIsOnlyLocked(), HeapTupleSatisfiesVisibility(), InvalidTransactionId, ItemIdGetLength, ItemIdIsNormal, ItemPointerEquals(), ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), ItemPointerIsValid(), LockBuffer(), PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), ReadBuffer(), RelationGetRelid, TableScanDescData::rs_rd, TableScanDescData::rs_snapshot, HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleHeaderData::t_infomask, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TransactionIdEquals, TransactionIdIsValid, and UnlockReleaseBuffer().

◆ heap_get_root_tuples()

void heap_get_root_tuples ( Page  page,
OffsetNumber root_offsets 
)

Definition at line 1048 of file pruneheap.c.

1049 {
1050  OffsetNumber offnum,
1051  maxoff;
1052 
1053  MemSet(root_offsets, InvalidOffsetNumber,
1055 
1056  maxoff = PageGetMaxOffsetNumber(page);
1057  for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
1058  {
1059  ItemId lp = PageGetItemId(page, offnum);
1060  HeapTupleHeader htup;
1061  OffsetNumber nextoffnum;
1062  TransactionId priorXmax;
1063 
1064  /* skip unused and dead items */
1065  if (!ItemIdIsUsed(lp) || ItemIdIsDead(lp))
1066  continue;
1067 
1068  if (ItemIdIsNormal(lp))
1069  {
1070  htup = (HeapTupleHeader) PageGetItem(page, lp);
1071 
1072  /*
1073  * Check if this tuple is part of a HOT-chain rooted at some other
1074  * tuple. If so, skip it for now; we'll process it when we find
1075  * its root.
1076  */
1077  if (HeapTupleHeaderIsHeapOnly(htup))
1078  continue;
1079 
1080  /*
1081  * This is either a plain tuple or the root of a HOT-chain.
1082  * Remember it in the mapping.
1083  */
1084  root_offsets[offnum - 1] = offnum;
1085 
1086  /* If it's not the start of a HOT-chain, we're done with it */
1087  if (!HeapTupleHeaderIsHotUpdated(htup))
1088  continue;
1089 
1090  /* Set up to scan the HOT-chain */
1091  nextoffnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
1092  priorXmax = HeapTupleHeaderGetUpdateXid(htup);
1093  }
1094  else
1095  {
1096  /* Must be a redirect item. We do not set its root_offsets entry */
1098  /* Set up to scan the HOT-chain */
1099  nextoffnum = ItemIdGetRedirect(lp);
1100  priorXmax = InvalidTransactionId;
1101  }
1102 
1103  /*
1104  * Now follow the HOT-chain and collect other tuples in the chain.
1105  *
1106  * Note: Even though this is a nested loop, the complexity of the
1107  * function is O(N) because a tuple in the page should be visited not
1108  * more than twice, once in the outer loop and once in HOT-chain
1109  * chases.
1110  */
1111  for (;;)
1112  {
1113  /* Sanity check (pure paranoia) */
1114  if (offnum < FirstOffsetNumber)
1115  break;
1116 
1117  /*
1118  * An offset past the end of page's line pointer array is possible
1119  * when the array was truncated
1120  */
1121  if (offnum > maxoff)
1122  break;
1123 
1124  lp = PageGetItemId(page, nextoffnum);
1125 
1126  /* Check for broken chains */
1127  if (!ItemIdIsNormal(lp))
1128  break;
1129 
1130  htup = (HeapTupleHeader) PageGetItem(page, lp);
1131 
1132  if (TransactionIdIsValid(priorXmax) &&
1133  !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(htup)))
1134  break;
1135 
1136  /* Remember the root line pointer for this item */
1137  root_offsets[nextoffnum - 1] = offnum;
1138 
1139  /* Advance to next chain member, if any */
1140  if (!HeapTupleHeaderIsHotUpdated(htup))
1141  break;
1142 
1143  /* HOT implies it can't have moved to different partition */
1145 
1146  nextoffnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
1147  priorXmax = HeapTupleHeaderGetUpdateXid(htup);
1148  }
1149  }
1150 }
#define MemSet(start, val, len)
Definition: c.h:1007
#define HeapTupleHeaderIsHotUpdated(tup)
Definition: htup_details.h:482
#define ItemIdGetRedirect(itemId)
Definition: itemid.h:78
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
#define ItemIdIsRedirected(itemId)
Definition: itemid.h:106
#define InvalidOffsetNumber
Definition: off.h:26
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
#define FirstOffsetNumber
Definition: off.h:27

References Assert(), FirstOffsetNumber, HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleHeaderIndicatesMovedPartitions, HeapTupleHeaderIsHeapOnly, HeapTupleHeaderIsHotUpdated, InvalidOffsetNumber, InvalidTransactionId, ItemIdGetRedirect, ItemIdIsDead, ItemIdIsNormal, ItemIdIsRedirected, ItemIdIsUsed, ItemPointerGetOffsetNumber(), MaxHeapTuplesPerPage, MemSet, OffsetNumberNext, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), HeapTupleHeaderData::t_ctid, TransactionIdEquals, and TransactionIdIsValid.

Referenced by heapam_index_build_range_scan(), and heapam_index_validate_scan().

◆ heap_getnext()

HeapTuple heap_getnext ( TableScanDesc  sscan,
ScanDirection  direction 
)

Definition at line 1082 of file heapam.c.

1083 {
1084  HeapScanDesc scan = (HeapScanDesc) sscan;
1085 
1086  /*
1087  * This is still widely used directly, without going through table AM, so
1088  * add a safety check. It's possible we should, at a later point,
1089  * downgrade this to an assert. The reason for checking the AM routine,
1090  * rather than the AM oid, is that this allows to write regression tests
1091  * that create another AM reusing the heap handler.
1092  */
1094  ereport(ERROR,
1095  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1096  errmsg_internal("only heap AM is supported")));
1097 
1098  /*
1099  * We don't expect direct calls to heap_getnext with valid CheckXidAlive
1100  * for catalog or regular tables. See detailed comments in xact.c where
1101  * these variables are declared. Normally we have such a check at tableam
1102  * level API but this is called from many places so we need to ensure it
1103  * here.
1104  */
1106  elog(ERROR, "unexpected heap_getnext call during logical decoding");
1107 
1108  /* Note: no locking manipulations needed */
1109 
1110  if (scan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
1111  heapgettup_pagemode(scan, direction,
1112  scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
1113  else
1114  heapgettup(scan, direction,
1115  scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
1116 
1117  if (scan->rs_ctup.t_data == NULL)
1118  return NULL;
1119 
1120  /*
1121  * if we get here it means we have a new current scan tuple, so point to
1122  * the proper return buffer and return the tuple.
1123  */
1124 
1126 
1127  return &scan->rs_ctup;
1128 }
static void heapgettup(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:720
static void heapgettup_pagemode(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:835
const TableAmRoutine * GetHeapamTableAmRoutine(void)
#define pgstat_count_heap_getnext(rel)
Definition: pgstat.h:615
const struct TableAmRoutine * rd_tableam
Definition: rel.h:189
bool bsysscan
Definition: xact.c:98
TransactionId CheckXidAlive
Definition: xact.c:97

References bsysscan, CheckXidAlive, elog, ereport, errcode(), errmsg_internal(), ERROR, GetHeapamTableAmRoutine(), heapgettup(), heapgettup_pagemode(), pgstat_count_heap_getnext, RelationData::rd_tableam, HeapScanDescData::rs_base, HeapScanDescData::rs_ctup, TableScanDescData::rs_flags, TableScanDescData::rs_key, TableScanDescData::rs_nkeys, TableScanDescData::rs_rd, SO_ALLOW_PAGEMODE, HeapTupleData::t_data, TransactionIdIsValid, and unlikely.

Referenced by AlterTableMoveAll(), AlterTableSpaceOptions(), check_db_file_conflict(), CreateDatabaseUsingFileCopy(), do_autovacuum(), DropSetting(), DropTableSpace(), find_typed_table_dependencies(), get_all_vacuum_rels(), get_database_list(), get_subscription_list(), get_tables_to_cluster(), get_tablespace_name(), get_tablespace_oid(), GetAllTablesPublicationRelations(), getRelationsInNamespace(), GetSchemaPublicationRelations(), heapam_index_build_range_scan(), heapam_index_validate_scan(), index_update_stats(), objectsInSchemaToOids(), pgrowlocks(), pgstat_heap(), populate_typ_list(), ReindexMultipleTables(), remove_dbtablespaces(), RemoveSubscriptionRel(), RenameTableSpace(), ThereIsAtLeastOneRole(), and vac_truncate_clog().

◆ heap_getnextslot()

bool heap_getnextslot ( TableScanDesc  sscan,
ScanDirection  direction,
struct TupleTableSlot slot 
)

Definition at line 1131 of file heapam.c.

1132 {
1133  HeapScanDesc scan = (HeapScanDesc) sscan;
1134 
1135  /* Note: no locking manipulations needed */
1136 
1137  if (sscan->rs_flags & SO_ALLOW_PAGEMODE)
1138  heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1139  else
1140  heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1141 
1142  if (scan->rs_ctup.t_data == NULL)
1143  {
1144  ExecClearTuple(slot);
1145  return false;
1146  }
1147 
1148  /*
1149  * if we get here it means we have a new current scan tuple, so point to
1150  * the proper return buffer and return the tuple.
1151  */
1152 
1154 
1155  ExecStoreBufferHeapTuple(&scan->rs_ctup, slot,
1156  scan->rs_cbuf);
1157  return true;
1158 }
TupleTableSlot * ExecStoreBufferHeapTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer)
Definition: execTuples.c:1391
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:433

References ExecClearTuple(), ExecStoreBufferHeapTuple(), heapgettup(), heapgettup_pagemode(), pgstat_count_heap_getnext, HeapScanDescData::rs_base, HeapScanDescData::rs_cbuf, HeapScanDescData::rs_ctup, TableScanDescData::rs_flags, TableScanDescData::rs_key, TableScanDescData::rs_nkeys, TableScanDescData::rs_rd, SO_ALLOW_PAGEMODE, and HeapTupleData::t_data.

◆ heap_getnextslot_tidrange()

bool heap_getnextslot_tidrange ( TableScanDesc  sscan,
ScanDirection  direction,
TupleTableSlot slot 
)

Definition at line 1234 of file heapam.c.

1236 {
1237  HeapScanDesc scan = (HeapScanDesc) sscan;
1238  ItemPointer mintid = &sscan->rs_mintid;
1239  ItemPointer maxtid = &sscan->rs_maxtid;
1240 
1241  /* Note: no locking manipulations needed */
1242  for (;;)
1243  {
1244  if (sscan->rs_flags & SO_ALLOW_PAGEMODE)
1245  heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1246  else
1247  heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1248 
1249  if (scan->rs_ctup.t_data == NULL)
1250  {
1251  ExecClearTuple(slot);
1252  return false;
1253  }
1254 
1255  /*
1256  * heap_set_tidrange will have used heap_setscanlimits to limit the
1257  * range of pages we scan to only ones that can contain the TID range
1258  * we're scanning for. Here we must filter out any tuples from these
1259  * pages that are outside of that range.
1260  */
1261  if (ItemPointerCompare(&scan->rs_ctup.t_self, mintid) < 0)
1262  {
1263  ExecClearTuple(slot);
1264 
1265  /*
1266  * When scanning backwards, the TIDs will be in descending order.
1267  * Future tuples in this direction will be lower still, so we can
1268  * just return false to indicate there will be no more tuples.
1269  */
1270  if (ScanDirectionIsBackward(direction))
1271  return false;
1272 
1273  continue;
1274  }
1275 
1276  /*
1277  * Likewise for the final page, we must filter out TIDs greater than
1278  * maxtid.
1279  */
1280  if (ItemPointerCompare(&scan->rs_ctup.t_self, maxtid) > 0)
1281  {
1282  ExecClearTuple(slot);
1283 
1284  /*
1285  * When scanning forward, the TIDs will be in ascending order.
1286  * Future tuples in this direction will be higher still, so we can
1287  * just return false to indicate there will be no more tuples.
1288  */
1289  if (ScanDirectionIsForward(direction))
1290  return false;
1291  continue;
1292  }
1293 
1294  break;
1295  }
1296 
1297  /*
1298  * if we get here it means we have a new current scan tuple, so point to
1299  * the proper return buffer and return the tuple.
1300  */
1302 
1303  ExecStoreBufferHeapTuple(&scan->rs_ctup, slot, scan->rs_cbuf);
1304  return true;
1305 }
int32 ItemPointerCompare(ItemPointer arg1, ItemPointer arg2)
Definition: itemptr.c:51
#define ScanDirectionIsForward(direction)
Definition: sdir.h:64
#define ScanDirectionIsBackward(direction)
Definition: sdir.h:50
ItemPointerData rs_mintid
Definition: relscan.h:40
ItemPointerData rs_maxtid
Definition: relscan.h:41

References ExecClearTuple(), ExecStoreBufferHeapTuple(), heapgettup(), heapgettup_pagemode(), ItemPointerCompare(), pgstat_count_heap_getnext, HeapScanDescData::rs_base, HeapScanDescData::rs_cbuf, HeapScanDescData::rs_ctup, TableScanDescData::rs_flags, TableScanDescData::rs_key, TableScanDescData::rs_maxtid, TableScanDescData::rs_mintid, TableScanDescData::rs_nkeys, TableScanDescData::rs_rd, ScanDirectionIsBackward, ScanDirectionIsForward, SO_ALLOW_PAGEMODE, HeapTupleData::t_data, and HeapTupleData::t_self.

◆ heap_hot_search_buffer()

bool heap_hot_search_buffer ( ItemPointer  tid,
Relation  relation,
Buffer  buffer,
Snapshot  snapshot,
HeapTuple  heapTuple,
bool all_dead,
bool  first_call 
)

Definition at line 1461 of file heapam.c.

1464 {
1465  Page page = BufferGetPage(buffer);
1466  TransactionId prev_xmax = InvalidTransactionId;
1467  BlockNumber blkno;
1468  OffsetNumber offnum;
1469  bool at_chain_start;
1470  bool valid;
1471  bool skip;
1472  GlobalVisState *vistest = NULL;
1473 
1474  /* If this is not the first call, previous call returned a (live!) tuple */
1475  if (all_dead)
1476  *all_dead = first_call;
1477 
1478  blkno = ItemPointerGetBlockNumber(tid);
1479  offnum = ItemPointerGetOffsetNumber(tid);
1480  at_chain_start = first_call;
1481  skip = !first_call;
1482 
1483  /* XXX: we should assert that a snapshot is pushed or registered */
1485  Assert(BufferGetBlockNumber(buffer) == blkno);
1486 
1487  /* Scan through possible multiple members of HOT-chain */
1488  for (;;)
1489  {
1490  ItemId lp;
1491 
1492  /* check for bogus TID */
1493  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
1494  break;
1495 
1496  lp = PageGetItemId(page, offnum);
1497 
1498  /* check for unused, dead, or redirected items */
1499  if (!ItemIdIsNormal(lp))
1500  {
1501  /* We should only see a redirect at start of chain */
1502  if (ItemIdIsRedirected(lp) && at_chain_start)
1503  {
1504  /* Follow the redirect */
1505  offnum = ItemIdGetRedirect(lp);
1506  at_chain_start = false;
1507  continue;
1508  }
1509  /* else must be end of chain */
1510  break;
1511  }
1512 
1513  /*
1514  * Update heapTuple to point to the element of the HOT chain we're
1515  * currently investigating. Having t_self set correctly is important
1516  * because the SSI checks and the *Satisfies routine for historical
1517  * MVCC snapshots need the correct tid to decide about the visibility.
1518  */
1519  heapTuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
1520  heapTuple->t_len = ItemIdGetLength(lp);
1521  heapTuple->t_tableOid = RelationGetRelid(relation);
1522  ItemPointerSet(&heapTuple->t_self, blkno, offnum);
1523 
1524  /*
1525  * Shouldn't see a HEAP_ONLY tuple at chain start.
1526  */
1527  if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
1528  break;
1529 
1530  /*
1531  * The xmin should match the previous xmax value, else chain is
1532  * broken.
1533  */
1534  if (TransactionIdIsValid(prev_xmax) &&
1535  !TransactionIdEquals(prev_xmax,
1536  HeapTupleHeaderGetXmin(heapTuple->t_data)))
1537  break;
1538 
1539  /*
1540  * When first_call is true (and thus, skip is initially false) we'll
1541  * return the first tuple we find. But on later passes, heapTuple
1542  * will initially be pointing to the tuple we returned last time.
1543  * Returning it again would be incorrect (and would loop forever), so
1544  * we skip it and return the next match we find.
1545  */
1546  if (!skip)
1547  {
1548  /* If it's visible per the snapshot, we must return it */
1549  valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
1550  HeapCheckForSerializableConflictOut(valid, relation, heapTuple,
1551  buffer, snapshot);
1552 
1553  if (valid)
1554  {
1555  ItemPointerSetOffsetNumber(tid, offnum);
1556  PredicateLockTID(relation, &heapTuple->t_self, snapshot,
1557  HeapTupleHeaderGetXmin(heapTuple->t_data));
1558  if (all_dead)
1559  *all_dead = false;
1560  return true;
1561  }
1562  }
1563  skip = false;
1564 
1565  /*
1566  * If we can't see it, maybe no one else can either. At caller
1567  * request, check whether all chain members are dead to all
1568  * transactions.
1569  *
1570  * Note: if you change the criterion here for what is "dead", fix the
1571  * planner's get_actual_variable_range() function to match.
1572  */
1573  if (all_dead && *all_dead)
1574  {
1575  if (!vistest)
1576  vistest = GlobalVisTestFor(relation);
1577 
1578  if (!HeapTupleIsSurelyDead(heapTuple, vistest))
1579  *all_dead = false;
1580  }
1581 
1582  /*
1583  * Check to see if HOT chain continues past this tuple; if so fetch
1584  * the next offnum and loop around.
1585  */
1586  if (HeapTupleIsHotUpdated(heapTuple))
1587  {
1589  blkno);
1590  offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
1591  at_chain_start = false;
1592  prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
1593  }
1594  else
1595  break; /* end of chain */
1596  }
1597 
1598  return false;
1599 }
bool HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
#define HeapTupleIsHeapOnly(tuple)
Definition: htup_details.h:683
#define HeapTupleIsHotUpdated(tuple)
Definition: htup_details.h:674
static void ItemPointerSet(ItemPointerData *pointer, BlockNumber blockNumber, OffsetNumber offNum)
Definition: itemptr.h:135
static void ItemPointerSetOffsetNumber(ItemPointerData *pointer, OffsetNumber offsetNumber)
Definition: itemptr.h:158
static const struct exclude_list_item skip[]
Definition: pg_checksums.c:108
GlobalVisState * GlobalVisTestFor(Relation rel)
Definition: procarray.c:4091
TransactionId RecentXmin
Definition: snapmgr.c:99

References Assert(), BufferGetBlockNumber(), BufferGetPage(), GlobalVisTestFor(), HeapCheckForSerializableConflictOut(), HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleIsHeapOnly, HeapTupleIsHotUpdated, HeapTupleIsSurelyDead(), HeapTupleSatisfiesVisibility(), InvalidTransactionId, ItemIdGetLength, ItemIdGetRedirect, ItemIdIsNormal, ItemIdIsRedirected, ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), ItemPointerSet(), ItemPointerSetOffsetNumber(), PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), PredicateLockTID(), RecentXmin, RelationGetRelid, skip, HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TransactionIdEquals, and TransactionIdIsValid.

Referenced by heap_index_delete_tuples(), heapam_index_fetch_tuple(), and heapam_scan_bitmap_next_block().

◆ heap_index_delete_tuples()

TransactionId heap_index_delete_tuples ( Relation  rel,
TM_IndexDeleteOp delstate 
)

Definition at line 7643 of file heapam.c.

7644 {
7645  /* Initial assumption is that earlier pruning took care of conflict */
7646  TransactionId snapshotConflictHorizon = InvalidTransactionId;
7649  Page page = NULL;
7651  TransactionId priorXmax;
7652 #ifdef USE_PREFETCH
7653  IndexDeletePrefetchState prefetch_state;
7654  int prefetch_distance;
7655 #endif
7656  SnapshotData SnapshotNonVacuumable;
7657  int finalndeltids = 0,
7658  nblocksaccessed = 0;
7659 
7660  /* State that's only used in bottom-up index deletion case */
7661  int nblocksfavorable = 0;
7662  int curtargetfreespace = delstate->bottomupfreespace,
7663  lastfreespace = 0,
7664  actualfreespace = 0;
7665  bool bottomup_final_block = false;
7666 
7667  InitNonVacuumableSnapshot(SnapshotNonVacuumable, GlobalVisTestFor(rel));
7668 
7669  /* Sort caller's deltids array by TID for further processing */
7670  index_delete_sort(delstate);
7671 
7672  /*
7673  * Bottom-up case: resort deltids array in an order attuned to where the
7674  * greatest number of promising TIDs are to be found, and determine how
7675  * many blocks from the start of sorted array should be considered
7676  * favorable. This will also shrink the deltids array in order to
7677  * eliminate completely unfavorable blocks up front.
7678  */
7679  if (delstate->bottomup)
7680  nblocksfavorable = bottomup_sort_and_shrink(delstate);
7681 
7682 #ifdef USE_PREFETCH
7683  /* Initialize prefetch state. */
7684  prefetch_state.cur_hblkno = InvalidBlockNumber;
7685  prefetch_state.next_item = 0;
7686  prefetch_state.ndeltids = delstate->ndeltids;
7687  prefetch_state.deltids = delstate->deltids;
7688 
7689  /*
7690  * Determine the prefetch distance that we will attempt to maintain.
7691  *
7692  * Since the caller holds a buffer lock somewhere in rel, we'd better make
7693  * sure that isn't a catalog relation before we call code that does
7694  * syscache lookups, to avoid risk of deadlock.
7695  */
7696  if (IsCatalogRelation(rel))
7697  prefetch_distance = maintenance_io_concurrency;
7698  else
7699  prefetch_distance =
7701 
7702  /* Cap initial prefetch distance for bottom-up deletion caller */
7703  if (delstate->bottomup)
7704  {
7705  Assert(nblocksfavorable >= 1);
7706  Assert(nblocksfavorable <= BOTTOMUP_MAX_NBLOCKS);
7707  prefetch_distance = Min(prefetch_distance, nblocksfavorable);
7708  }
7709 
7710  /* Start prefetching. */
7711  index_delete_prefetch_buffer(rel, &prefetch_state, prefetch_distance);
7712 #endif
7713 
7714  /* Iterate over deltids, determine which to delete, check their horizon */
7715  Assert(delstate->ndeltids > 0);
7716  for (int i = 0; i < delstate->ndeltids; i++)
7717  {
7718  TM_IndexDelete *ideltid = &delstate->deltids[i];
7719  TM_IndexStatus *istatus = delstate->status + ideltid->id;
7720  ItemPointer htid = &ideltid->tid;
7721  OffsetNumber offnum;
7722 
7723  /*
7724  * Read buffer, and perform required extra steps each time a new block
7725  * is encountered. Avoid refetching if it's the same block as the one
7726  * from the last htid.
7727  */
7728  if (blkno == InvalidBlockNumber ||
7729  ItemPointerGetBlockNumber(htid) != blkno)
7730  {
7731  /*
7732  * Consider giving up early for bottom-up index deletion caller
7733  * first. (Only prefetch next-next block afterwards, when it
7734  * becomes clear that we're at least going to access the next
7735  * block in line.)
7736  *
7737  * Sometimes the first block frees so much space for bottom-up
7738  * caller that the deletion process can end without accessing any
7739  * more blocks. It is usually necessary to access 2 or 3 blocks
7740  * per bottom-up deletion operation, though.
7741  */
7742  if (delstate->bottomup)
7743  {
7744  /*
7745  * We often allow caller to delete a few additional items
7746  * whose entries we reached after the point that space target
7747  * from caller was satisfied. The cost of accessing the page
7748  * was already paid at that point, so it made sense to finish
7749  * it off. When that happened, we finalize everything here
7750  * (by finishing off the whole bottom-up deletion operation
7751  * without needlessly paying the cost of accessing any more
7752  * blocks).
7753  */
7754  if (bottomup_final_block)
7755  break;
7756 
7757  /*
7758  * Give up when we didn't enable our caller to free any
7759  * additional space as a result of processing the page that we
7760  * just finished up with. This rule is the main way in which
7761  * we keep the cost of bottom-up deletion under control.
7762  */
7763  if (nblocksaccessed >= 1 && actualfreespace == lastfreespace)
7764  break;
7765  lastfreespace = actualfreespace; /* for next time */
7766 
7767  /*
7768  * Deletion operation (which is bottom-up) will definitely
7769  * access the next block in line. Prepare for that now.
7770  *
7771  * Decay target free space so that we don't hang on for too
7772  * long with a marginal case. (Space target is only truly
7773  * helpful when it allows us to recognize that we don't need
7774  * to access more than 1 or 2 blocks to satisfy caller due to
7775  * agreeable workload characteristics.)
7776  *
7777  * We are a bit more patient when we encounter contiguous
7778  * blocks, though: these are treated as favorable blocks. The
7779  * decay process is only applied when the next block in line
7780  * is not a favorable/contiguous block. This is not an
7781  * exception to the general rule; we still insist on finding
7782  * at least one deletable item per block accessed. See
7783  * bottomup_nblocksfavorable() for full details of the theory
7784  * behind favorable blocks and heap block locality in general.
7785  *
7786  * Note: The first block in line is always treated as a
7787  * favorable block, so the earliest possible point that the
7788  * decay can be applied is just before we access the second
7789  * block in line. The Assert() verifies this for us.
7790  */
7791  Assert(nblocksaccessed > 0 || nblocksfavorable > 0);
7792  if (nblocksfavorable > 0)
7793  nblocksfavorable--;
7794  else
7795  curtargetfreespace /= 2;
7796  }
7797 
7798  /* release old buffer */
7799  if (BufferIsValid(buf))
7801 
7802  blkno = ItemPointerGetBlockNumber(htid);
7803  buf = ReadBuffer(rel, blkno);
7804  nblocksaccessed++;
7805  Assert(!delstate->bottomup ||
7806  nblocksaccessed <= BOTTOMUP_MAX_NBLOCKS);
7807 
7808 #ifdef USE_PREFETCH
7809 
7810  /*
7811  * To maintain the prefetch distance, prefetch one more page for
7812  * each page we read.
7813  */
7814  index_delete_prefetch_buffer(rel, &prefetch_state, 1);
7815 #endif
7816 
7818 
7819  page = BufferGetPage(buf);
7820  maxoff = PageGetMaxOffsetNumber(page);
7821  }
7822 
7823  /*
7824  * In passing, detect index corruption involving an index page with a
7825  * TID that points to a location in the heap that couldn't possibly be
7826  * correct. We only do this with actual TIDs from caller's index page
7827  * (not items reached by traversing through a HOT chain).
7828  */
7829  index_delete_check_htid(delstate, page, maxoff, htid, istatus);
7830 
7831  if (istatus->knowndeletable)
7832  Assert(!delstate->bottomup && !istatus->promising);
7833  else
7834  {
7835  ItemPointerData tmp = *htid;
7836  HeapTupleData heapTuple;
7837 
7838  /* Are any tuples from this HOT chain non-vacuumable? */
7839  if (heap_hot_search_buffer(&tmp, rel, buf, &SnapshotNonVacuumable,
7840  &heapTuple, NULL, true))
7841  continue; /* can't delete entry */
7842 
7843  /* Caller will delete, since whole HOT chain is vacuumable */
7844  istatus->knowndeletable = true;
7845 
7846  /* Maintain index free space info for bottom-up deletion case */
7847  if (delstate->bottomup)
7848  {
7849  Assert(istatus->freespace > 0);
7850  actualfreespace += istatus->freespace;
7851  if (actualfreespace >= curtargetfreespace)
7852  bottomup_final_block = true;
7853  }
7854  }
7855 
7856  /*
7857  * Maintain snapshotConflictHorizon value for deletion operation as a
7858  * whole by advancing current value using heap tuple headers. This is
7859  * loosely based on the logic for pruning a HOT chain.
7860  */
7861  offnum = ItemPointerGetOffsetNumber(htid);
7862  priorXmax = InvalidTransactionId; /* cannot check first XMIN */
7863  for (;;)
7864  {
7865  ItemId lp;
7866  HeapTupleHeader htup;
7867 
7868  /* Sanity check (pure paranoia) */
7869  if (offnum < FirstOffsetNumber)
7870  break;
7871 
7872  /*
7873  * An offset past the end of page's line pointer array is possible
7874  * when the array was truncated
7875  */
7876  if (offnum > maxoff)
7877  break;
7878 
7879  lp = PageGetItemId(page, offnum);
7880  if (ItemIdIsRedirected(lp))
7881  {
7882  offnum = ItemIdGetRedirect(lp);
7883  continue;
7884  }
7885 
7886  /*
7887  * We'll often encounter LP_DEAD line pointers (especially with an
7888  * entry marked knowndeletable by our caller up front). No heap
7889  * tuple headers get examined for an htid that leads us to an
7890  * LP_DEAD item. This is okay because the earlier pruning
7891  * operation that made the line pointer LP_DEAD in the first place
7892  * must have considered the original tuple header as part of
7893  * generating its own snapshotConflictHorizon value.
7894  *
7895  * Relying on XLOG_HEAP2_PRUNE records like this is the same
7896  * strategy that index vacuuming uses in all cases. Index VACUUM
7897  * WAL records don't even have a snapshotConflictHorizon field of
7898  * their own for this reason.
7899  */
7900  if (!ItemIdIsNormal(lp))
7901  break;
7902 
7903  htup = (HeapTupleHeader) PageGetItem(page, lp);
7904 
7905  /*
7906  * Check the tuple XMIN against prior XMAX, if any
7907  */
7908  if (TransactionIdIsValid(priorXmax) &&
7909  !TransactionIdEquals(HeapTupleHeaderGetXmin(htup), priorXmax))
7910  break;
7911 
7913  &snapshotConflictHorizon);
7914 
7915  /*
7916  * If the tuple is not HOT-updated, then we are at the end of this
7917  * HOT-chain. No need to visit later tuples from the same update
7918  * chain (they get their own index entries) -- just move on to
7919  * next htid from index AM caller.
7920  */
7921  if (!HeapTupleHeaderIsHotUpdated(htup))
7922  break;
7923 
7924  /* Advance to next HOT chain member */
7925  Assert(ItemPointerGetBlockNumber(&htup->t_ctid) == blkno);
7926  offnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
7927  priorXmax = HeapTupleHeaderGetUpdateXid(htup);
7928  }
7929 
7930  /* Enable further/final shrinking of deltids for caller */
7931  finalndeltids = i + 1;
7932  }
7933 
7935 
7936  /*
7937  * Shrink deltids array to exclude non-deletable entries at the end. This
7938  * is not just a minor optimization. Final deltids array size might be
7939  * zero for a bottom-up caller. Index AM is explicitly allowed to rely on
7940  * ndeltids being zero in all cases with zero total deletable entries.
7941  */
7942  Assert(finalndeltids > 0 || delstate->bottomup);
7943  delstate->ndeltids = finalndeltids;
7944 
7945  return snapshotConflictHorizon;
7946 }
int maintenance_io_concurrency
Definition: bufmgr.c:153
#define Min(x, y)
Definition: c.h:991
bool IsCatalogRelation(Relation relation)
Definition: catalog.c:103
static int bottomup_sort_and_shrink(TM_IndexDeleteOp *delstate)
Definition: heapam.c:8201
void HeapTupleHeaderAdvanceConflictHorizon(HeapTupleHeader tuple, TransactionId *snapshotConflictHorizon)
Definition: heapam.c:7498
static void index_delete_check_htid(TM_IndexDeleteOp *delstate, Page page, OffsetNumber maxoff, ItemPointer htid, TM_IndexStatus *istatus)
Definition: heapam.c:7583
#define BOTTOMUP_MAX_NBLOCKS
Definition: heapam.c:187
bool heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, bool *all_dead, bool first_call)
Definition: heapam.c:1461
static void index_delete_sort(TM_IndexDeleteOp *delstate)
Definition: heapam.c:7988
static char * buf
Definition: pg_test_fsync.c:73
#define InitNonVacuumableSnapshot(snapshotdata, vistestp)
Definition: snapmgr.h:48
int get_tablespace_maintenance_io_concurrency(Oid spcid)
Definition: spccache.c:229
TM_IndexStatus * status
Definition: tableam.h:247
int bottomupfreespace
Definition: tableam.h:242
TM_IndexDelete * deltids
Definition: tableam.h:246
ItemPointerData tid
Definition: tableam.h:205
bool knowndeletable
Definition: tableam.h:212
bool promising
Definition: tableam.h:215
int16 freespace
Definition: tableam.h:216

References Assert(), TM_IndexDeleteOp::bottomup, BOTTOMUP_MAX_NBLOCKS, bottomup_sort_and_shrink(), TM_IndexDeleteOp::bottomupfreespace, buf, BUFFER_LOCK_SHARE, BufferGetPage(), BufferIsValid(), TM_IndexDeleteOp::deltids, FirstOffsetNumber, TM_IndexStatus::freespace, get_tablespace_maintenance_io_concurrency(), GlobalVisTestFor(), heap_hot_search_buffer(), HeapTupleHeaderAdvanceConflictHorizon(), HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleHeaderIsHotUpdated, i, TM_IndexDelete::id, index_delete_check_htid(), index_delete_sort(), InitNonVacuumableSnapshot, InvalidBlockNumber, InvalidBuffer, InvalidOffsetNumber, InvalidTransactionId, IsCatalogRelation(), ItemIdGetRedirect, ItemIdIsNormal, ItemIdIsRedirected, ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), TM_IndexStatus::knowndeletable, LockBuffer(), maintenance_io_concurrency, Min, TM_IndexDeleteOp::ndeltids, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), TM_IndexStatus::promising, RelationData::rd_rel, ReadBuffer(), TM_IndexDeleteOp::status, HeapTupleHeaderData::t_ctid, TM_IndexDelete::tid, TransactionIdEquals, TransactionIdIsValid, and UnlockReleaseBuffer().

◆ heap_inplace_update()

void heap_inplace_update ( Relation  relation,
HeapTuple  tuple 
)

Definition at line 5889 of file heapam.c.

5890 {
5891  Buffer buffer;
5892  Page page;
5893  OffsetNumber offnum;
5894  ItemId lp = NULL;
5895  HeapTupleHeader htup;
5896  uint32 oldlen;
5897  uint32 newlen;
5898 
5899  /*
5900  * For now, we don't allow parallel updates. Unlike a regular update,
5901  * this should never create a combo CID, so it might be possible to relax
5902  * this restriction, but not without more thought and testing. It's not
5903  * clear that it would be useful, anyway.
5904  */
5905  if (IsInParallelMode())
5906  ereport(ERROR,
5907  (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
5908  errmsg("cannot update tuples during a parallel operation")));
5909 
5910  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
5912  page = (Page) BufferGetPage(buffer);
5913 
5914  offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
5915  if (PageGetMaxOffsetNumber(page) >= offnum)
5916  lp = PageGetItemId(page, offnum);
5917 
5918  if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
5919  elog(ERROR, "invalid lp");
5920 
5921  htup = (HeapTupleHeader) PageGetItem(page, lp);
5922 
5923  oldlen = ItemIdGetLength(lp) - htup->t_hoff;
5924  newlen = tuple->t_len - tuple->t_data->t_hoff;
5925  if (oldlen != newlen || htup->t_hoff != tuple->t_data->t_hoff)
5926  elog(ERROR, "wrong tuple length");
5927 
5928  /* NO EREPORT(ERROR) from here till changes are logged */
5930 
5931  memcpy((char *) htup + htup->t_hoff,
5932  (char *) tuple->t_data + tuple->t_data->t_hoff,
5933  newlen);
5934 
5935  MarkBufferDirty(buffer);
5936 
5937  /* XLOG stuff */
5938  if (RelationNeedsWAL(relation))
5939  {
5940  xl_heap_inplace xlrec;
5941  XLogRecPtr recptr;
5942 
5943  xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
5944 
5945  XLogBeginInsert();
5946  XLogRegisterData((char *) &xlrec, SizeOfHeapInplace);
5947 
5948  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
5949  XLogRegisterBufData(0, (char *) htup + htup->t_hoff, newlen);
5950 
5951  /* inplace updates aren't decoded atm, don't log the origin */
5952 
5953  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_INPLACE);
5954 
5955  PageSetLSN(page, recptr);
5956  }
5957 
5958  END_CRIT_SECTION();
5959 
5960  UnlockReleaseBuffer(buffer);
5961 
5962  /*
5963  * Send out shared cache inval if necessary. Note that because we only
5964  * pass the new version of the tuple, this mustn't be used for any
5965  * operations that could change catcache lookup keys. But we aren't
5966  * bothering with index updates either, so that's true a fortiori.
5967  */
5969  CacheInvalidateHeapTuple(relation, tuple, NULL);
5970 }
unsigned int uint32
Definition: c.h:493
#define SizeOfHeapInplace
Definition: heapam_xlog.h:316
#define XLOG_HEAP_INPLACE
Definition: heapam_xlog.h:39
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:451
OffsetNumber offnum
Definition: heapam_xlog.h:312

References BUFFER_LOCK_EXCLUSIVE, BufferGetPage(), CacheInvalidateHeapTuple(), elog, END_CRIT_SECTION, ereport, errcode(), errmsg(), ERROR, IsBootstrapProcessingMode, IsInParallelMode(), ItemIdGetLength, ItemIdIsNormal, ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), LockBuffer(), MarkBufferDirty(), xl_heap_inplace::offnum, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), PageSetLSN(), ReadBuffer(), REGBUF_STANDARD, RelationNeedsWAL, SizeOfHeapInplace, START_CRIT_SECTION, HeapTupleData::t_data, HeapTupleHeaderData::t_hoff, HeapTupleData::t_len, HeapTupleData::t_self, UnlockReleaseBuffer(), XLOG_HEAP_INPLACE, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by create_toast_table(), dropdb(), EventTriggerOnLogin(), index_update_stats(), vac_update_datfrozenxid(), and vac_update_relstats().

◆ heap_insert()

void heap_insert ( Relation  relation,
HeapTuple  tup,
CommandId  cid,
int  options,
BulkInsertState  bistate 
)

Definition at line 1824 of file heapam.c.

1826 {
1828  HeapTuple heaptup;
1829  Buffer buffer;
1830  Buffer vmbuffer = InvalidBuffer;
1831  bool all_visible_cleared = false;
1832 
1833  /* Cheap, simplistic check that the tuple matches the rel's rowtype. */
1835  RelationGetNumberOfAttributes(relation));
1836 
1837  /*
1838  * Fill in tuple header fields and toast the tuple if necessary.
1839  *
1840  * Note: below this point, heaptup is the data we actually intend to store
1841  * into the relation; tup is the caller's original untoasted data.
1842  */
1843  heaptup = heap_prepare_insert(relation, tup, xid, cid, options);
1844 
1845  /*
1846  * Find buffer to insert this tuple into. If the page is all visible,
1847  * this will also pin the requisite visibility map page.
1848  */
1849  buffer = RelationGetBufferForTuple(relation, heaptup->t_len,
1850  InvalidBuffer, options, bistate,
1851  &vmbuffer, NULL,
1852  0);
1853 
1854  /*
1855  * We're about to do the actual insert -- but check for conflict first, to
1856  * avoid possibly having to roll back work we've just done.
1857  *
1858  * This is safe without a recheck as long as there is no possibility of
1859  * another process scanning the page between this check and the insert
1860  * being visible to the scan (i.e., an exclusive buffer content lock is
1861  * continuously held from this point until the tuple insert is visible).
1862  *
1863  * For a heap insert, we only need to check for table-level SSI locks. Our
1864  * new tuple can't possibly conflict with existing tuple locks, and heap
1865  * page locks are only consolidated versions of tuple locks; they do not
1866  * lock "gaps" as index page locks do. So we don't need to specify a
1867  * buffer when making the call, which makes for a faster check.
1868  */
1870 
1871  /* NO EREPORT(ERROR) from here till changes are logged */
1873 
1874  RelationPutHeapTuple(relation, buffer, heaptup,
1875  (options & HEAP_INSERT_SPECULATIVE) != 0);
1876 
1877  if (PageIsAllVisible(BufferGetPage(buffer)))
1878  {
1879  all_visible_cleared = true;
1881  visibilitymap_clear(relation,
1882  ItemPointerGetBlockNumber(&(heaptup->t_self)),
1883  vmbuffer, VISIBILITYMAP_VALID_BITS);
1884  }
1885 
1886  /*
1887  * XXX Should we set PageSetPrunable on this page ?
1888  *
1889  * The inserting transaction may eventually abort thus making this tuple
1890  * DEAD and hence available for pruning. Though we don't want to optimize
1891  * for aborts, if no other tuple in this page is UPDATEd/DELETEd, the
1892  * aborted tuple will never be pruned until next vacuum is triggered.
1893  *
1894  * If you do add PageSetPrunable here, add it in heap_xlog_insert too.
1895  */
1896 
1897  MarkBufferDirty(buffer);
1898 
1899  /* XLOG stuff */
1900  if (RelationNeedsWAL(relation))
1901  {
1902  xl_heap_insert xlrec;
1903  xl_heap_header xlhdr;
1904  XLogRecPtr recptr;
1905  Page page = BufferGetPage(buffer);
1906  uint8 info = XLOG_HEAP_INSERT;
1907  int bufflags = 0;
1908 
1909  /*
1910  * If this is a catalog, we need to transmit combo CIDs to properly
1911  * decode, so log that as well.
1912  */
1914  log_heap_new_cid(relation, heaptup);
1915 
1916  /*
1917  * If this is the single and first tuple on page, we can reinit the
1918  * page instead of restoring the whole thing. Set flag, and hide
1919  * buffer references from XLogInsert.
1920  */
1921  if (ItemPointerGetOffsetNumber(&(heaptup->t_self)) == FirstOffsetNumber &&
1923  {
1924  info |= XLOG_HEAP_INIT_PAGE;
1925  bufflags |= REGBUF_WILL_INIT;
1926  }
1927 
1928  xlrec.offnum = ItemPointerGetOffsetNumber(&heaptup->t_self);
1929  xlrec.flags = 0;
1930  if (all_visible_cleared)
1935 
1936  /*
1937  * For logical decoding, we need the tuple even if we're doing a full
1938  * page write, so make sure it's included even if we take a full-page
1939  * image. (XXX We could alternatively store a pointer into the FPW).
1940  */
1941  if (RelationIsLogicallyLogged(relation) &&
1943  {
1945  bufflags |= REGBUF_KEEP_DATA;
1946 
1947  if (IsToastRelation(relation))
1949  }
1950 
1951  XLogBeginInsert();
1952  XLogRegisterData((char *) &xlrec, SizeOfHeapInsert);
1953 
1954  xlhdr.t_infomask2 = heaptup->t_data->t_infomask2;
1955  xlhdr.t_infomask = heaptup->t_data->t_infomask;
1956  xlhdr.t_hoff = heaptup->t_data->t_hoff;
1957 
1958  /*
1959  * note we mark xlhdr as belonging to buffer; if XLogInsert decides to
1960  * write the whole page to the xlog, we don't need to store
1961  * xl_heap_header in the xlog.
1962  */
1963  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
1964  XLogRegisterBufData(0, (char *) &xlhdr, SizeOfHeapHeader);
1965  /* PG73FORMAT: write bitmap [+ padding] [+ oid] + data */
1967  (char *) heaptup->t_data + SizeofHeapTupleHeader,
1968  heaptup->t_len - SizeofHeapTupleHeader);
1969 
1970  /* filtering by origin on a row level is much more efficient */
1972 
1973  recptr = XLogInsert(RM_HEAP_ID, info);
1974 
1975  PageSetLSN(page, recptr);
1976  }
1977 
1978  END_CRIT_SECTION();
1979 
1980  UnlockReleaseBuffer(buffer);
1981  if (vmbuffer != InvalidBuffer)
1982  ReleaseBuffer(vmbuffer);
1983 
1984  /*
1985  * If tuple is cachable, mark it for invalidation from the caches in case
1986  * we abort. Note it is OK to do this after releasing the buffer, because
1987  * the heaptup data structure is all in local memory, not in the shared
1988  * buffer.
1989  */
1990  CacheInvalidateHeapTuple(relation, heaptup, NULL);
1991 
1992  /* Note: speculative insertions are counted too, even if aborted later */
1993  pgstat_count_heap_insert(relation, 1);
1994 
1995  /*
1996  * If heaptup is a private copy, release it. Don't forget to copy t_self
1997  * back to the caller's image, too.
1998  */
1999  if (heaptup != tup)
2000  {
2001  tup->t_self = heaptup->t_self;
2002  heap_freetuple(heaptup);
2003  }
2004 }
unsigned char uint8
Definition: c.h:491
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid, CommandId cid, int options)
Definition: heapam.c:2013
#define HEAP_INSERT_SPECULATIVE
Definition: heapam.h:37
#define HEAP_INSERT_NO_LOGICAL
Definition: heapam.h:36
#define XLH_INSERT_ON_TOAST_RELATION
Definition: heapam_xlog.h:70
#define XLH_INSERT_IS_SPECULATIVE
Definition: heapam_xlog.h:68
#define XLH_INSERT_ALL_VISIBLE_CLEARED
Definition: heapam_xlog.h:66
#define XLOG_HEAP_INSERT
Definition: heapam_xlog.h:32
#define SizeOfHeapInsert
Definition: heapam_xlog.h:162
#define XLH_INSERT_CONTAINS_NEW_TUPLE
Definition: heapam_xlog.h:69
#define XLOG_HEAP_INIT_PAGE
Definition: heapam_xlog.h:46
void RelationPutHeapTuple(Relation relation, Buffer buffer, HeapTuple tuple, bool token)
Definition: hio.c:35
Buffer RelationGetBufferForTuple(Relation relation, Size len, Buffer otherBuffer, int options, BulkInsertState bistate, Buffer *vmbuffer, Buffer *vmbuffer_other, int num_pages)
Definition: hio.c:502
#define HeapTupleHeaderGetNatts(tup)
Definition: htup_details.h:529
void pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:701
#define RelationGetNumberOfAttributes(relation)
Definition: rel.h:511
OffsetNumber offnum
Definition: heapam_xlog.h:156
#define REGBUF_KEEP_DATA
Definition: xloginsert.h:35
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33

References Assert(), BufferGetBlockNumber(), BufferGetPage(), CacheInvalidateHeapTuple(), CheckForSerializableConflictIn(), END_CRIT_SECTION, FirstOffsetNumber, xl_heap_insert::flags, GetCurrentTransactionId(), heap_freetuple(), HEAP_INSERT_NO_LOGICAL, HEAP_INSERT_SPECULATIVE, heap_prepare_insert(), HeapTupleHeaderGetNatts, InvalidBlockNumber, InvalidBuffer, IsToastRelation(), ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), log_heap_new_cid(), MarkBufferDirty(), xl_heap_insert::offnum, PageClearAllVisible(), PageGetMaxOffsetNumber(), PageIsAllVisible(), PageSetLSN(), pgstat_count_heap_insert(), REGBUF_KEEP_DATA, REGBUF_STANDARD, REGBUF_WILL_INIT, RelationGetBufferForTuple(), RelationGetNumberOfAttributes, RelationIsAccessibleInLogicalDecoding, RelationIsLogicallyLogged, RelationNeedsWAL, RelationPutHeapTuple(), ReleaseBuffer(), SizeOfHeapHeader, SizeOfHeapInsert, SizeofHeapTupleHeader, START_CRIT_SECTION, HeapTupleData::t_data, xl_heap_header::t_hoff, HeapTupleHeaderData::t_hoff, xl_heap_header::t_infomask, HeapTupleHeaderData::t_infomask, xl_heap_header::t_infomask2, HeapTupleHeaderData::t_infomask2, HeapTupleData::t_len, HeapTupleData::t_self, UnlockReleaseBuffer(), visibilitymap_clear(), VISIBILITYMAP_VALID_BITS, XLH_INSERT_ALL_VISIBLE_CLEARED, XLH_INSERT_CONTAINS_NEW_TUPLE, XLH_INSERT_IS_SPECULATIVE, XLH_INSERT_ON_TOAST_RELATION, XLOG_HEAP_INIT_PAGE, XLOG_HEAP_INSERT, XLOG_INCLUDE_ORIGIN, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), XLogRegisterData(), and XLogSetRecordFlags().

Referenced by heapam_tuple_insert(), heapam_tuple_insert_speculative(), simple_heap_insert(), and toast_save_datum().

◆ heap_lock_tuple()

TM_Result heap_lock_tuple ( Relation  relation,
HeapTuple  tuple,
CommandId  cid,
LockTupleMode  mode,
LockWaitPolicy  wait_policy,
bool  follow_updates,
Buffer buffer,
struct TM_FailureData tmfd 
)

Definition at line 4140 of file heapam.c.

4144 {
4145  TM_Result result;
4146  ItemPointer tid = &(tuple->t_self);
4147  ItemId lp;
4148  Page page;
4149  Buffer vmbuffer = InvalidBuffer;
4150  BlockNumber block;
4151  TransactionId xid,
4152  xmax;
4153  uint16 old_infomask,
4154  new_infomask,
4155  new_infomask2;
4156  bool first_time = true;
4157  bool skip_tuple_lock = false;
4158  bool have_tuple_lock = false;
4159  bool cleared_all_frozen = false;
4160 
4161  *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
4162  block = ItemPointerGetBlockNumber(tid);
4163 
4164  /*
4165  * Before locking the buffer, pin the visibility map page if it appears to
4166  * be necessary. Since we haven't got the lock yet, someone else might be
4167  * in the middle of changing this, so we'll need to recheck after we have
4168  * the lock.
4169  */
4170  if (PageIsAllVisible(BufferGetPage(*buffer)))
4171  visibilitymap_pin(relation, block, &vmbuffer);
4172 
4174 
4175  page = BufferGetPage(*buffer);
4176  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
4177  Assert(ItemIdIsNormal(lp));
4178 
4179  tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
4180  tuple->t_len = ItemIdGetLength(lp);
4181  tuple->t_tableOid = RelationGetRelid(relation);
4182 
4183 l3:
4184  result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
4185 
4186  if (result == TM_Invisible)
4187  {
4188  /*
4189  * This is possible, but only when locking a tuple for ON CONFLICT
4190  * UPDATE. We return this value here rather than throwing an error in
4191  * order to give that case the opportunity to throw a more specific
4192  * error.
4193  */
4194  result = TM_Invisible;
4195  goto out_locked;
4196  }
4197  else if (result == TM_BeingModified ||
4198  result == TM_Updated ||
4199  result == TM_Deleted)
4200  {
4201  TransactionId xwait;
4202  uint16 infomask;
4203  uint16 infomask2;
4204  bool require_sleep;
4205  ItemPointerData t_ctid;
4206 
4207  /* must copy state data before unlocking buffer */
4208  xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
4209  infomask = tuple->t_data->t_infomask;
4210  infomask2 = tuple->t_data->t_infomask2;
4211  ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
4212 
4213  LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
4214 
4215  /*
4216  * If any subtransaction of the current top transaction already holds
4217  * a lock as strong as or stronger than what we're requesting, we
4218  * effectively hold the desired lock already. We *must* succeed
4219  * without trying to take the tuple lock, else we will deadlock
4220  * against anyone wanting to acquire a stronger lock.
4221  *
4222  * Note we only do this the first time we loop on the HTSU result;
4223  * there is no point in testing in subsequent passes, because
4224  * evidently our own transaction cannot have acquired a new lock after
4225  * the first time we checked.
4226  */
4227  if (first_time)
4228  {
4229  first_time = false;
4230 
4231  if (infomask & HEAP_XMAX_IS_MULTI)
4232  {
4233  int i;
4234  int nmembers;
4235  MultiXactMember *members;
4236 
4237  /*
4238  * We don't need to allow old multixacts here; if that had
4239  * been the case, HeapTupleSatisfiesUpdate would have returned
4240  * MayBeUpdated and we wouldn't be here.
4241  */
4242  nmembers =
4243  GetMultiXactIdMembers(xwait, &members, false,
4244  HEAP_XMAX_IS_LOCKED_ONLY(infomask));
4245 
4246  for (i = 0; i < nmembers; i++)
4247  {
4248  /* only consider members of our own transaction */
4249  if (!TransactionIdIsCurrentTransactionId(members[i].xid))
4250  continue;
4251 
4252  if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
4253  {
4254  pfree(members);
4255  result = TM_Ok;
4256  goto out_unlocked;
4257  }
4258  else
4259  {
4260  /*
4261  * Disable acquisition of the heavyweight tuple lock.
4262  * Otherwise, when promoting a weaker lock, we might
4263  * deadlock with another locker that has acquired the
4264  * heavyweight tuple lock and is waiting for our
4265  * transaction to finish.
4266  *
4267  * Note that in this case we still need to wait for
4268  * the multixact if required, to avoid acquiring
4269  * conflicting locks.
4270  */
4271  skip_tuple_lock = true;
4272  }
4273  }
4274 
4275  if (members)
4276  pfree(members);
4277  }
4278  else if (TransactionIdIsCurrentTransactionId(xwait))
4279  {
4280  switch (mode)
4281  {
4282  case LockTupleKeyShare:
4283  Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
4284  HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
4285  HEAP_XMAX_IS_EXCL_LOCKED(infomask));
4286  result = TM_Ok;
4287  goto out_unlocked;
4288  case LockTupleShare:
4289  if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
4290  HEAP_XMAX_IS_EXCL_LOCKED(infomask))
4291  {
4292  result = TM_Ok;
4293  goto out_unlocked;
4294  }
4295  break;
4297  if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
4298  {
4299  result = TM_Ok;
4300  goto out_unlocked;
4301  }
4302  break;
4303  case LockTupleExclusive:
4304  if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
4305  infomask2 & HEAP_KEYS_UPDATED)
4306  {
4307  result = TM_Ok;
4308  goto out_unlocked;
4309  }
4310  break;
4311  }
4312  }
4313  }
4314 
4315  /*
4316  * Initially assume that we will have to wait for the locking
4317  * transaction(s) to finish. We check various cases below in which
4318  * this can be turned off.
4319  */
4320  require_sleep = true;
4321  if (mode == LockTupleKeyShare)
4322  {
4323  /*
4324  * If we're requesting KeyShare, and there's no update present, we
4325  * don't need to wait. Even if there is an update, we can still
4326  * continue if the key hasn't been modified.
4327  *
4328  * However, if there are updates, we need to walk the update chain
4329  * to mark future versions of the row as locked, too. That way,
4330  * if somebody deletes that future version, we're protected
4331  * against the key going away. This locking of future versions
4332  * could block momentarily, if a concurrent transaction is
4333  * deleting a key; or it could return a value to the effect that
4334  * the transaction deleting the key has already committed. So we
4335  * do this before re-locking the buffer; otherwise this would be
4336  * prone to deadlocks.
4337  *
4338  * Note that the TID we're locking was grabbed before we unlocked
4339  * the buffer. For it to change while we're not looking, the
4340  * other properties we're testing for below after re-locking the
4341  * buffer would also change, in which case we would restart this
4342  * loop above.
4343  */
4344  if (!(infomask2 & HEAP_KEYS_UPDATED))
4345  {
4346  bool updated;
4347 
4348  updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
4349 
4350  /*
4351  * If there are updates, follow the update chain; bail out if
4352  * that cannot be done.
4353  */
4354  if (follow_updates && updated)
4355  {
4356  TM_Result res;
4357 
4358  res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
4360  mode);
4361  if (res != TM_Ok)
4362  {
4363  result = res;
4364  /* recovery code expects to have buffer lock held */
4366  goto failed;
4367  }
4368  }
4369 
4371 
4372  /*
4373  * Make sure it's still an appropriate lock, else start over.
4374  * Also, if it wasn't updated before we released the lock, but
4375  * is updated now, we start over too; the reason is that we
4376  * now need to follow the update chain to lock the new
4377  * versions.
4378  */
4379  if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
4380  ((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
4381  !updated))
4382  goto l3;
4383 
4384  /* Things look okay, so we can skip sleeping */
4385  require_sleep = false;
4386 
4387  /*
4388  * Note we allow Xmax to change here; other updaters/lockers
4389  * could have modified it before we grabbed the buffer lock.
4390  * However, this is not a problem, because with the recheck we
4391  * just did we ensure that they still don't conflict with the
4392  * lock we want.
4393  */
4394  }
4395  }
4396  else if (mode == LockTupleShare)
4397  {
4398  /*
4399  * If we're requesting Share, we can similarly avoid sleeping if
4400  * there's no update and no exclusive lock present.
4401  */
4402  if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
4403  !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
4404  {
4406 
4407  /*
4408  * Make sure it's still an appropriate lock, else start over.
4409  * See above about allowing xmax to change.
4410  */
4411  if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
4413  goto l3;
4414  require_sleep = false;
4415  }
4416  }
4417  else if (mode == LockTupleNoKeyExclusive)
4418  {
4419  /*
4420  * If we're requesting NoKeyExclusive, we might also be able to
4421  * avoid sleeping; just ensure that there no conflicting lock
4422  * already acquired.
4423  */
4424  if (infomask & HEAP_XMAX_IS_MULTI)
4425  {
4426  if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
4427  mode, NULL))
4428  {
4429  /*
4430  * No conflict, but if the xmax changed under us in the
4431  * meantime, start over.
4432  */
4434  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4436  xwait))
4437  goto l3;
4438 
4439  /* otherwise, we're good */
4440  require_sleep = false;
4441  }
4442  }
4443  else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
4444  {
4446 
4447  /* if the xmax changed in the meantime, start over */
4448  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4450  xwait))
4451  goto l3;
4452  /* otherwise, we're good */
4453  require_sleep = false;
4454  }
4455  }
4456 
4457  /*
4458  * As a check independent from those above, we can also avoid sleeping
4459  * if the current transaction is the sole locker of the tuple. Note
4460  * that the strength of the lock already held is irrelevant; this is
4461  * not about recording the lock in Xmax (which will be done regardless
4462  * of this optimization, below). Also, note that the cases where we
4463  * hold a lock stronger than we are requesting are already handled
4464  * above by not doing anything.
4465  *
4466  * Note we only deal with the non-multixact case here; MultiXactIdWait
4467  * is well equipped to deal with this situation on its own.
4468  */
4469  if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
4471  {
4472  /* ... but if the xmax changed in the meantime, start over */
4474  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4476  xwait))
4477  goto l3;
4479  require_sleep = false;
4480  }
4481 
4482  /*
4483  * Time to sleep on the other transaction/multixact, if necessary.
4484  *
4485  * If the other transaction is an update/delete that's already
4486  * committed, then sleeping cannot possibly do any good: if we're
4487  * required to sleep, get out to raise an error instead.
4488  *
4489  * By here, we either have already acquired the buffer exclusive lock,
4490  * or we must wait for the locking transaction or multixact; so below
4491  * we ensure that we grab buffer lock after the sleep.
4492  */
4493  if (require_sleep && (result == TM_Updated || result == TM_Deleted))
4494  {
4496  goto failed;
4497  }
4498  else if (require_sleep)
4499  {
4500  /*
4501  * Acquire tuple lock to establish our priority for the tuple, or
4502  * die trying. LockTuple will release us when we are next-in-line
4503  * for the tuple. We must do this even if we are share-locking,
4504  * but not if we already have a weaker lock on the tuple.
4505  *
4506  * If we are forced to "start over" below, we keep the tuple lock;
4507  * this arranges that we stay at the head of the line while
4508  * rechecking tuple state.
4509  */
4510  if (!skip_tuple_lock &&
4511  !heap_acquire_tuplock(relation, tid, mode, wait_policy,
4512  &have_tuple_lock))
4513  {
4514  /*
4515  * This can only happen if wait_policy is Skip and the lock
4516  * couldn't be obtained.
4517  */
4518  result = TM_WouldBlock;
4519  /* recovery code expects to have buffer lock held */
4521  goto failed;
4522  }
4523 
4524  if (infomask & HEAP_XMAX_IS_MULTI)
4525  {
4527 
4528  /* We only ever lock tuples, never update them */
4529  if (status >= MultiXactStatusNoKeyUpdate)
4530  elog(ERROR, "invalid lock mode in heap_lock_tuple");
4531 
4532  /* wait for multixact to end, or die trying */
4533  switch (wait_policy)
4534  {
4535  case LockWaitBlock:
4536  MultiXactIdWait((MultiXactId) xwait, status, infomask,
4537  relation, &tuple->t_self, XLTW_Lock, NULL);
4538  break;
4539  case LockWaitSkip:
4541  status, infomask, relation,
4542  NULL))
4543  {
4544  result = TM_WouldBlock;
4545  /* recovery code expects to have buffer lock held */
4547  goto failed;
4548  }
4549  break;
4550  case LockWaitError:
4552  status, infomask, relation,
4553  NULL))
4554  ereport(ERROR,
4555  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
4556  errmsg("could not obtain lock on row in relation \"%s\"",
4557  RelationGetRelationName(relation))));
4558 
4559  break;
4560  }
4561 
4562  /*
4563  * Of course, the multixact might not be done here: if we're
4564  * requesting a light lock mode, other transactions with light
4565  * locks could still be alive, as well as locks owned by our
4566  * own xact or other subxacts of this backend. We need to
4567  * preserve the surviving MultiXact members. Note that it
4568  * isn't absolutely necessary in the latter case, but doing so
4569  * is simpler.
4570  */
4571  }
4572  else
4573  {
4574  /* wait for regular transaction to end, or die trying */
4575  switch (wait_policy)
4576  {
4577  case LockWaitBlock:
4578  XactLockTableWait(xwait, relation, &tuple->t_self,
4579  XLTW_Lock);
4580  break;
4581  case LockWaitSkip:
4582  if (!ConditionalXactLockTableWait(xwait))
4583  {
4584  result = TM_WouldBlock;
4585  /* recovery code expects to have buffer lock held */
4587  goto failed;
4588  }
4589  break;
4590  case LockWaitError:
4591  if (!ConditionalXactLockTableWait(xwait))
4592  ereport(ERROR,
4593  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
4594  errmsg("could not obtain lock on row in relation \"%s\"",
4595  RelationGetRelationName(relation))));
4596  break;
4597  }
4598  }
4599 
4600  /* if there are updates, follow the update chain */
4601  if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
4602  {
4603  TM_Result res;
4604 
4605  res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
4607  mode);
4608  if (res != TM_Ok)
4609  {
4610  result = res;
4611  /* recovery code expects to have buffer lock held */
4613  goto failed;
4614  }
4615  }
4616 
4618 
4619  /*
4620  * xwait is done, but if xwait had just locked the tuple then some
4621  * other xact could update this tuple before we get to this point.
4622  * Check for xmax change, and start over if so.
4623  */
4624  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4626  xwait))
4627  goto l3;
4628 
4629  if (!(infomask & HEAP_XMAX_IS_MULTI))
4630  {
4631  /*
4632  * Otherwise check if it committed or aborted. Note we cannot
4633  * be here if the tuple was only locked by somebody who didn't
4634  * conflict with us; that would have been handled above. So
4635  * that transaction must necessarily be gone by now. But
4636  * don't check for this in the multixact case, because some
4637  * locker transactions might still be running.
4638  */
4639  UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
4640  }
4641  }
4642 
4643  /* By here, we're certain that we hold buffer exclusive lock again */
4644 
4645  /*
4646  * We may lock if previous xmax aborted, or if it committed but only
4647  * locked the tuple without updating it; or if we didn't have to wait
4648  * at all for whatever reason.
4649  */
4650  if (!require_sleep ||
4651  (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
4654  result = TM_Ok;
4655  else if (!ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid))
4656  result = TM_Updated;
4657  else
4658  result = TM_Deleted;
4659  }
4660 
4661 failed:
4662  if (result != TM_Ok)
4663  {
4664  Assert(result == TM_SelfModified || result == TM_Updated ||
4665  result == TM_Deleted || result == TM_WouldBlock);
4666 
4667  /*
4668  * When locking a tuple under LockWaitSkip semantics and we fail with
4669  * TM_WouldBlock above, it's possible for concurrent transactions to
4670  * release the lock and set HEAP_XMAX_INVALID in the meantime. So
4671  * this assert is slightly different from the equivalent one in
4672  * heap_delete and heap_update.
4673  */
4674  Assert((result == TM_WouldBlock) ||
4675  !(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
4676  Assert(result != TM_Updated ||
4677  !ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid));
4678  tmfd->ctid = tuple->t_data->t_ctid;
4679  tmfd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
4680  if (result == TM_SelfModified)
4681  tmfd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
4682  else
4683  tmfd->cmax = InvalidCommandId;
4684  goto out_locked;
4685  }
4686 
4687  /*
4688  * If we didn't pin the visibility map page and the page has become all
4689  * visible while we were busy locking the buffer, or during some
4690  * subsequent window during which we had it unlocked, we'll have to unlock
4691  * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
4692  * unfortunate, especially since we'll now have to recheck whether the
4693  * tuple has been locked or updated under us, but hopefully it won't
4694  * happen very often.
4695  */
4696  if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
4697  {
4698  LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
4699  visibilitymap_pin(relation, block, &vmbuffer);
4701  goto l3;
4702  }
4703 
4704  xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
4705  old_infomask = tuple->t_data->t_infomask;
4706 
4707  /*
4708  * If this is the first possibly-multixact-able operation in the current
4709  * transaction, set my per-backend OldestMemberMXactId setting. We can be
4710  * certain that the transaction will never become a member of any older
4711  * MultiXactIds than that. (We have to do this even if we end up just
4712  * using our own TransactionId below, since some other backend could
4713  * incorporate our XID into a MultiXact immediately afterwards.)
4714  */
4716 
4717  /*
4718  * Compute the new xmax and infomask to store into the tuple. Note we do
4719  * not modify the tuple just yet, because that would leave it in the wrong
4720  * state if multixact.c elogs.
4721  */
4722  compute_new_xmax_infomask(xmax, old_infomask, tuple->t_data->t_infomask2,
4723  GetCurrentTransactionId(), mode, false,
4724  &xid, &new_infomask, &new_infomask2);
4725 
4727 
4728  /*
4729  * Store transaction information of xact locking the tuple.
4730  *
4731  * Note: Cmax is meaningless in this context, so don't set it; this avoids
4732  * possibly generating a useless combo CID. Moreover, if we're locking a
4733  * previously updated tuple, it's important to preserve the Cmax.
4734  *
4735  * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
4736  * we would break the HOT chain.
4737  */
4738  tuple->t_data->t_infomask &= ~HEAP_XMAX_BITS;
4739  tuple->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
4740  tuple->t_data->t_infomask |= new_infomask;
4741  tuple->t_data->t_infomask2 |= new_infomask2;
4742  if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
4744  HeapTupleHeaderSetXmax(tuple->t_data, xid);
4745 
4746  /*
4747  * Make sure there is no forward chain link in t_ctid. Note that in the
4748  * cases where the tuple has been updated, we must not overwrite t_ctid,
4749  * because it was set by the updater. Moreover, if the tuple has been
4750  * updated, we need to follow the update chain to lock the new versions of
4751  * the tuple as well.
4752  */
4753  if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
4754  tuple->t_data->t_ctid = *tid;
4755 
4756  /* Clear only the all-frozen bit on visibility map if needed */
4757  if (PageIsAllVisible(page) &&
4758  visibilitymap_clear(relation, block, vmbuffer,
4760  cleared_all_frozen = true;
4761 
4762 
4763  MarkBufferDirty(*buffer);
4764 
4765  /*
4766  * XLOG stuff. You might think that we don't need an XLOG record because
4767  * there is no state change worth restoring after a crash. You would be
4768  * wrong however: we have just written either a TransactionId or a
4769  * MultiXactId that may never have been seen on disk before, and we need
4770  * to make sure that there are XLOG entries covering those ID numbers.
4771  * Else the same IDs might be re-used after a crash, which would be
4772  * disastrous if this page made it to disk before the crash. Essentially
4773  * we have to enforce the WAL log-before-data rule even in this case.
4774  * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
4775  * entries for everything anyway.)
4776  */
4777  if (RelationNeedsWAL(relation))
4778  {
4779  xl_heap_lock xlrec;
4780  XLogRecPtr recptr;
4781 
4782  XLogBeginInsert();
4783  XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD);
4784 
4785  xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
4786  xlrec.xmax = xid;
4787  xlrec.infobits_set = compute_infobits(new_infomask,
4788  tuple->t_data->t_infomask2);
4789  xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
4790  XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
4791 
4792  /* we don't decode row locks atm, so no need to log the origin */
4793 
4794  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
4795 
4796  PageSetLSN(page, recptr);
4797  }
4798 
4799  END_CRIT_SECTION();
4800 
4801  result = TM_Ok;
4802 
4803 out_locked:
4804  LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
4805 
4806 out_unlocked:
4807  if (BufferIsValid(vmbuffer))
4808  ReleaseBuffer(vmbuffer);
4809 
4810  /*
4811  * Don't update the visibility map here. Locking a tuple doesn't change
4812  * visibility info.
4813  */
4814 
4815  /*
4816  * Now that we have successfully marked the tuple as locked, we can
4817  * release the lmgr tuple lock, if we had it.
4818  */
4819  if (have_tuple_lock)
4820  UnlockTupleTuplock(relation, tid, mode);
4821 
4822  return result;
4823 }
#define TUPLOCK_from_mxstatus(status)
Definition: heapam.c:216
static TM_Result heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid, TransactionId xid, LockTupleMode mode)
Definition: heapam.c:5604
static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, int *remaining)
Definition: heapam.c:7321
static MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
Definition: heapam.c:4092
#define XLH_LOCK_ALL_FROZEN_CLEARED
Definition: heapam_xlog.h:277
#define XLOG_HEAP_LOCK
Definition: heapam_xlog.h:38
#define SizeOfHeapLock
Definition: heapam_xlog.h:288
#define HEAP_XMAX_IS_EXCL_LOCKED(infomask)
Definition: htup_details.h:261
#define HEAP_XMAX_IS_KEYSHR_LOCKED(infomask)
Definition: htup_details.h:263
#define HEAP_XMAX_IS_SHR_LOCKED(infomask)
Definition: htup_details.h:259
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
Definition: itemptr.h:172
bool ConditionalXactLockTableWait(TransactionId xid)
Definition: lmgr.c:740
@ XLTW_Lock
Definition: lmgr.h:29
@ LockWaitSkip
Definition: lockoptions.h:41
@ LockWaitError
Definition: lockoptions.h:43
@ LockTupleNoKeyExclusive
Definition: lockoptions.h:56
@ LockTupleShare
Definition: lockoptions.h:54
@ LockTupleKeyShare
Definition: lockoptions.h:52
int GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members, bool from_pgupgrade, bool isLockOnly)
Definition: multixact.c:1239
MultiXactStatus
Definition: multixact.h:38
@ MultiXactStatusNoKeyUpdate
Definition: multixact.h:44
static PgChecksumMode mode
Definition: pg_checksums.c:56
#define RelationGetRelationName(relation)
Definition: rel.h:539
uint8 infobits_set
Definition: heapam_xlog.h:284
OffsetNumber offnum
Definition: heapam_xlog.h:283
TransactionId xmax
Definition: heapam_xlog.h:282
@ TM_WouldBlock
Definition: tableam.h:102
#define VISIBILITYMAP_ALL_FROZEN

References Assert(), BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferGetPage(), BufferIsValid(), TM_FailureData::cmax, compute_infobits(), compute_new_xmax_infomask(), ConditionalMultiXactIdWait(), ConditionalXactLockTableWait(), TM_FailureData::ctid, DoesMultiXactIdConflict(), elog, END_CRIT_SECTION, ereport, errcode(), errmsg(), ERROR, xl_heap_lock::flags, get_mxact_status_for_lock(), GetCurrentTransactionId(), GetMultiXactIdMembers(), heap_acquire_tuplock(), HEAP_KEYS_UPDATED, heap_lock_updated_tuple(), HEAP_XMAX_BITS, HEAP_XMAX_INVALID, HEAP_XMAX_IS_EXCL_LOCKED, HEAP_XMAX_IS_KEYSHR_LOCKED, HEAP_XMAX_IS_LOCKED_ONLY, HEAP_XMAX_IS_MULTI, HEAP_XMAX_IS_SHR_LOCKED, HeapTupleHeaderClearHotUpdated, HeapTupleHeaderGetCmax(), HeapTupleHeaderGetRawXmax, HeapTupleHeaderGetUpdateXid, HeapTupleHeaderIsOnlyLocked(), HeapTupleHeaderSetXmax, HeapTupleSatisfiesUpdate(), i, xl_heap_lock::infobits_set, InvalidBuffer, InvalidCommandId, ItemIdGetLength, ItemIdIsNormal, ItemPointerCopy(), ItemPointerEquals(), ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), LockBuffer(), LockTupleExclusive, LockTupleKeyShare, LockTupleNoKeyExclusive, LockTupleShare, LockWaitBlock, LockWaitError, LockWaitSkip, MarkBufferDirty(), mode, MultiXactIdSetOldestMember(), MultiXactIdWait(), MultiXactStatusNoKeyUpdate, xl_heap_lock::offnum, PageGetItem(), PageGetItemId(), PageIsAllVisible(), PageSetLSN(), pfree(), ReadBuffer(), REGBUF_STANDARD, RelationGetRelationName, RelationGetRelid, RelationNeedsWAL, ReleaseBuffer(), res, SizeOfHeapLock, START_CRIT_SECTION, HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleHeaderData::t_infomask, HeapTupleHeaderData::t_infomask2, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TM_BeingModified, TM_Deleted, TM_Invisible, TM_Ok, TM_SelfModified, TM_Updated, TM_WouldBlock, TransactionIdEquals, TransactionIdIsCurrentTransactionId(), TUPLOCK_from_mxstatus, UnlockTupleTuplock, UpdateXmaxHintBits(), VISIBILITYMAP_ALL_FROZEN, visibilitymap_clear(), visibilitymap_pin(), XactLockTableWait(), XLH_LOCK_ALL_FROZEN_CLEARED, XLOG_HEAP_LOCK, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), XLogRegisterData(), XLTW_Lock, xl_heap_lock::xmax, TM_FailureData::xmax, and xmax_infomask_changed().

Referenced by heapam_tuple_lock().

◆ heap_multi_insert()

void heap_multi_insert ( Relation  relation,
struct TupleTableSlot **  slots,
int  ntuples,
CommandId  cid,
int  options,
BulkInsertState  bistate 
)

Definition at line 2093 of file heapam.c.

2095 {
2097  HeapTuple *heaptuples;
2098  int i;
2099  int ndone;
2100  PGAlignedBlock scratch;
2101  Page page;
2102  Buffer vmbuffer = InvalidBuffer;
2103  bool needwal;
2104  Size saveFreeSpace;
2105  bool need_tuple_data = RelationIsLogicallyLogged(relation);
2106  bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
2107  bool starting_with_empty_page = false;
2108  int npages = 0;
2109  int npages_used = 0;
2110 
2111  /* currently not needed (thus unsupported) for heap_multi_insert() */
2113 
2114  needwal = RelationNeedsWAL(relation);
2115  saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
2117 
2118  /* Toast and set header data in all the slots */
2119  heaptuples = palloc(ntuples * sizeof(HeapTuple));
2120  for (i = 0; i < ntuples; i++)
2121  {
2122  HeapTuple tuple;
2123 
2124  tuple = ExecFetchSlotHeapTuple(slots[i], true, NULL);
2125  slots[i]->tts_tableOid = RelationGetRelid(relation);
2126  tuple->t_tableOid = slots[i]->tts_tableOid;
2127  heaptuples[i] = heap_prepare_insert(relation, tuple, xid, cid,
2128  options);
2129  }
2130 
2131  /*
2132  * We're about to do the actual inserts -- but check for conflict first,
2133  * to minimize the possibility of having to roll back work we've just
2134  * done.
2135  *
2136  * A check here does not definitively prevent a serialization anomaly;
2137  * that check MUST be done at least past the point of acquiring an
2138  * exclusive buffer content lock on every buffer that will be affected,
2139  * and MAY be done after all inserts are reflected in the buffers and
2140  * those locks are released; otherwise there is a race condition. Since
2141  * multiple buffers can be locked and unlocked in the loop below, and it
2142  * would not be feasible to identify and lock all of those buffers before
2143  * the loop, we must do a final check at the end.
2144  *
2145  * The check here could be omitted with no loss of correctness; it is
2146  * present strictly as an optimization.
2147  *
2148  * For heap inserts, we only need to check for table-level SSI locks. Our
2149  * new tuples can't possibly conflict with existing tuple locks, and heap
2150  * page locks are only consolidated versions of tuple locks; they do not
2151  * lock "gaps" as index page locks do. So we don't need to specify a
2152  * buffer when making the call, which makes for a faster check.
2153  */
2155 
2156  ndone = 0;
2157  while (ndone < ntuples)
2158  {
2159  Buffer buffer;
2160  bool all_visible_cleared = false;
2161  bool all_frozen_set = false;
2162  int nthispage;
2163 
2165 
2166  /*
2167  * Compute number of pages needed to fit the to-be-inserted tuples in
2168  * the worst case. This will be used to determine how much to extend
2169  * the relation by in RelationGetBufferForTuple(), if needed. If we
2170  * filled a prior page from scratch, we can just update our last
2171  * computation, but if we started with a partially filled page,
2172  * recompute from scratch, the number of potentially required pages
2173  * can vary due to tuples needing to fit onto the page, page headers
2174  * etc.
2175  */
2176  if (ndone == 0 || !starting_with_empty_page)
2177  {
2178  npages = heap_multi_insert_pages(heaptuples, ndone, ntuples,
2179  saveFreeSpace);
2180  npages_used = 0;
2181  }
2182  else
2183  npages_used++;
2184 
2185  /*
2186  * Find buffer where at least the next tuple will fit. If the page is
2187  * all-visible, this will also pin the requisite visibility map page.
2188  *
2189  * Also pin visibility map page if COPY FREEZE inserts tuples into an
2190  * empty page. See all_frozen_set below.
2191  */
2192  buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
2193  InvalidBuffer, options, bistate,
2194  &vmbuffer, NULL,
2195  npages - npages_used);
2196  page = BufferGetPage(buffer);
2197 
2198  starting_with_empty_page = PageGetMaxOffsetNumber(page) == 0;
2199 
2200  if (starting_with_empty_page && (options & HEAP_INSERT_FROZEN))
2201  all_frozen_set = true;
2202 
2203  /* NO EREPORT(ERROR) from here till changes are logged */
2205 
2206  /*
2207  * RelationGetBufferForTuple has ensured that the first tuple fits.
2208  * Put that on the page, and then as many other tuples as fit.
2209  */
2210  RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
2211 
2212  /*
2213  * For logical decoding we need combo CIDs to properly decode the
2214  * catalog.
2215  */
2216  if (needwal && need_cids)
2217  log_heap_new_cid(relation, heaptuples[ndone]);
2218 
2219  for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
2220  {
2221  HeapTuple heaptup = heaptuples[ndone + nthispage];
2222 
2223  if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
2224  break;
2225 
2226  RelationPutHeapTuple(relation, buffer, heaptup, false);
2227 
2228  /*
2229  * For logical decoding we need combo CIDs to properly decode the
2230  * catalog.
2231  */
2232  if (needwal && need_cids)
2233  log_heap_new_cid(relation, heaptup);
2234  }
2235 
2236  /*
2237  * If the page is all visible, need to clear that, unless we're only
2238  * going to add further frozen rows to it.
2239  *
2240  * If we're only adding already frozen rows to a previously empty
2241  * page, mark it as all-visible.
2242  */
2243  if (PageIsAllVisible(page) && !(options & HEAP_INSERT_FROZEN))
2244  {
2245  all_visible_cleared = true;
2246  PageClearAllVisible(page);
2247  visibilitymap_clear(relation,
2248  BufferGetBlockNumber(buffer),
2249  vmbuffer, VISIBILITYMAP_VALID_BITS);
2250  }
2251  else if (all_frozen_set)
2252  PageSetAllVisible(page);
2253 
2254  /*
2255  * XXX Should we set PageSetPrunable on this page ? See heap_insert()
2256  */
2257 
2258  MarkBufferDirty(buffer);
2259 
2260  /* XLOG stuff */
2261  if (needwal)
2262  {
2263  XLogRecPtr recptr;
2264  xl_heap_multi_insert *xlrec;
2266  char *tupledata;
2267  int totaldatalen;
2268  char *scratchptr = scratch.data;
2269  bool init;
2270  int bufflags = 0;
2271 
2272  /*
2273  * If the page was previously empty, we can reinit the page
2274  * instead of restoring the whole thing.
2275  */
2276  init = starting_with_empty_page;
2277 
2278  /* allocate xl_heap_multi_insert struct from the scratch area */
2279  xlrec = (xl_heap_multi_insert *) scratchptr;
2280  scratchptr += SizeOfHeapMultiInsert;
2281 
2282  /*
2283  * Allocate offsets array. Unless we're reinitializing the page,
2284  * in that case the tuples are stored in order starting at
2285  * FirstOffsetNumber and we don't need to store the offsets
2286  * explicitly.
2287  */
2288  if (!init)
2289  scratchptr += nthispage * sizeof(OffsetNumber);
2290 
2291  /* the rest of the scratch space is used for tuple data */
2292  tupledata = scratchptr;
2293 
2294  /* check that the mutually exclusive flags are not both set */
2295  Assert(!(all_visible_cleared && all_frozen_set));
2296 
2297  xlrec->flags = 0;
2298  if (all_visible_cleared)
2300  if (all_frozen_set)
2302 
2303  xlrec->ntuples = nthispage;
2304 
2305  /*
2306  * Write out an xl_multi_insert_tuple and the tuple data itself
2307  * for each tuple.
2308  */
2309  for (i = 0; i < nthispage; i++)
2310  {
2311  HeapTuple heaptup = heaptuples[ndone + i];
2312  xl_multi_insert_tuple *tuphdr;
2313  int datalen;
2314 
2315  if (!init)
2316  xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
2317  /* xl_multi_insert_tuple needs two-byte alignment. */
2318  tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
2319  scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
2320 
2321  tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
2322  tuphdr->t_infomask = heaptup->t_data->t_infomask;
2323  tuphdr->t_hoff = heaptup->t_data->t_hoff;
2324 
2325  /* write bitmap [+ padding] [+ oid] + data */
2326  datalen = heaptup->t_len - SizeofHeapTupleHeader;
2327  memcpy(scratchptr,
2328  (char *) heaptup->t_data + SizeofHeapTupleHeader,
2329  datalen);
2330  tuphdr->datalen = datalen;
2331  scratchptr += datalen;
2332  }
2333  totaldatalen = scratchptr - tupledata;
2334  Assert((scratchptr - scratch.data) < BLCKSZ);
2335 
2336  if (need_tuple_data)
2338 
2339  /*
2340  * Signal that this is the last xl_heap_multi_insert record
2341  * emitted by this call to heap_multi_insert(). Needed for logical
2342  * decoding so it knows when to cleanup temporary data.
2343  */
2344  if (ndone + nthispage == ntuples)
2345  xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
2346 
2347  if (init)
2348  {
2349  info |= XLOG_HEAP_INIT_PAGE;
2350  bufflags |= REGBUF_WILL_INIT;
2351  }
2352 
2353  /*
2354  * If we're doing logical decoding, include the new tuple data
2355  * even if we take a full-page image of the page.
2356  */
2357  if (need_tuple_data)
2358  bufflags |= REGBUF_KEEP_DATA;
2359 
2360  XLogBeginInsert();
2361  XLogRegisterData((char *) xlrec, tupledata - scratch.data);
2362  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
2363 
2364  XLogRegisterBufData(0, tupledata, totaldatalen);
2365 
2366  /* filtering by origin on a row level is much more efficient */
2368 
2369  recptr = XLogInsert(RM_HEAP2_ID, info);
2370 
2371  PageSetLSN(page, recptr);
2372  }
2373 
2374  END_CRIT_SECTION();
2375 
2376  /*
2377  * If we've frozen everything on the page, update the visibilitymap.
2378  * We're already holding pin on the vmbuffer.
2379  */
2380  if (all_frozen_set)
2381  {
2382  Assert(PageIsAllVisible(page));
2383  Assert(visibilitymap_pin_ok(BufferGetBlockNumber(buffer), vmbuffer));
2384 
2385  /*
2386  * It's fine to use InvalidTransactionId here - this is only used
2387  * when HEAP_INSERT_FROZEN is specified, which intentionally
2388  * violates visibility rules.
2389  */
2390  visibilitymap_set(relation, BufferGetBlockNumber(buffer), buffer,
2391  InvalidXLogRecPtr, vmbuffer,
2394  }
2395 
2396  UnlockReleaseBuffer(buffer);
2397  ndone += nthispage;
2398 
2399  /*
2400  * NB: Only release vmbuffer after inserting all tuples - it's fairly
2401  * likely that we'll insert into subsequent heap pages that are likely
2402  * to use the same vm page.
2403  */
2404  }
2405 
2406  /* We're done with inserting all tuples, so release the last vmbuffer. */
2407  if (vmbuffer != InvalidBuffer)
2408  ReleaseBuffer(vmbuffer);
2409 
2410  /*
2411  * We're done with the actual inserts. Check for conflicts again, to
2412  * ensure that all rw-conflicts in to these inserts are detected. Without
2413  * this final check, a sequential scan of the heap may have locked the
2414  * table after the "before" check, missing one opportunity to detect the
2415  * conflict, and then scanned the table before the new tuples were there,
2416  * missing the other chance to detect the conflict.
2417  *
2418  * For heap inserts, we only need to check for table-level SSI locks. Our
2419  * new tuples can't possibly conflict with existing tuple locks, and heap
2420  * page locks are only consolidated versions of tuple locks; they do not
2421  * lock "gaps" as index page locks do. So we don't need to specify a
2422  * buffer when making the call.
2423  */
2425 
2426  /*
2427  * If tuples are cachable, mark them for invalidation from the caches in
2428  * case we abort. Note it is OK to do this after releasing the buffer,
2429  * because the heaptuples data structure is all in local memory, not in
2430  * the shared buffer.
2431  */
2432  if (IsCatalogRelation(relation))
2433  {
2434  for (i = 0; i < ntuples; i++)
2435  CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
2436  }
2437 
2438  /* copy t_self fields back to the caller's slots */
2439  for (i = 0; i < ntuples; i++)
2440  slots[i]->tts_tid = heaptuples[i]->t_self;
2441 
2442  pgstat_count_heap_insert(relation, ntuples);
2443 }
Size PageGetHeapFreeSpace(Page page)
Definition: bufpage.c:991
static void PageSetAllVisible(Page page)
Definition: bufpage.h:431
#define MAXALIGN(LEN)
Definition: c.h:798
#define SHORTALIGN(LEN)
Definition: c.h:794
size_t Size
Definition: c.h:592
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1643
static int heap_multi_insert_pages(HeapTuple *heaptuples, int done, int ntuples, Size saveFreeSpace)
Definition: heapam.c:2061
#define HEAP_INSERT_FROZEN
Definition: heapam.h:35
#define SizeOfHeapMultiInsert
Definition: heapam_xlog.h:182
#define XLOG_HEAP2_MULTI_INSERT
Definition: heapam_xlog.h:58
#define XLH_INSERT_LAST_IN_MULTI
Definition: heapam_xlog.h:67
#define XLH_INSERT_ALL_FROZEN_SET
Definition: heapam_xlog.h:73
#define SizeOfMultiInsertTuple
Definition: heapam_xlog.h:193
int init
Definition: isn.c:75
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
#define RelationGetTargetPageFreeSpace(relation, defaultff)
Definition: rel.h:378
#define HEAP_DEFAULT_FILLFACTOR
Definition: rel.h:349
Oid tts_tableOid
Definition: tuptable.h:130
OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER]
Definition: heapam_xlog.h:179
char data[BLCKSZ]
Definition: c.h:1106
void visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf, XLogRecPtr recptr, Buffer vmBuf, TransactionId cutoff_xid, uint8 flags)
bool visibilitymap_pin_ok(BlockNumber heapBlk, Buffer vmbuf)
#define VISIBILITYMAP_ALL_VISIBLE
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28

References Assert(), BufferGetBlockNumber(), BufferGetPage(), CacheInvalidateHeapTuple(), CHECK_FOR_INTERRUPTS, CheckForSerializableConflictIn(), PGAlignedBlock::data, xl_multi_insert_tuple::datalen, END_CRIT_SECTION, ExecFetchSlotHeapTuple(), xl_heap_multi_insert::flags, GetCurrentTransactionId(), HEAP_DEFAULT_FILLFACTOR, HEAP_INSERT_FROZEN, HEAP_INSERT_NO_LOGICAL, heap_multi_insert_pages(), heap_prepare_insert(), i, init, InvalidBlockNumber, InvalidBuffer, InvalidTransactionId, InvalidXLogRecPtr, IsCatalogRelation(), ItemPointerGetOffsetNumber(), log_heap_new_cid(), MarkBufferDirty(), MAXALIGN, xl_heap_multi_insert::ntuples, xl_heap_multi_insert::offsets, PageClearAllVisible(), PageGetHeapFreeSpace(), PageGetMaxOffsetNumber(), PageIsAllVisible(), PageSetAllVisible(), PageSetLSN(), palloc(), pgstat_count_heap_insert(), REGBUF_KEEP_DATA, REGBUF_STANDARD, REGBUF_WILL_INIT, RelationGetBufferForTuple(), RelationGetRelid, RelationGetTargetPageFreeSpace, RelationIsAccessibleInLogicalDecoding, RelationIsLogicallyLogged, RelationNeedsWAL, RelationPutHeapTuple(), ReleaseBuffer(), SHORTALIGN, SizeOfHeapMultiInsert, SizeofHeapTupleHeader, SizeOfMultiInsertTuple, START_CRIT_SECTION, HeapTupleData::t_data, xl_multi_insert_tuple::t_hoff, HeapTupleHeaderData::t_hoff, xl_multi_insert_tuple::t_infomask, HeapTupleHeaderData::t_infomask, xl_multi_insert_tuple::t_infomask2, HeapTupleHeaderData::t_infomask2, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TupleTableSlot::tts_tableOid, UnlockReleaseBuffer(), VISIBILITYMAP_ALL_FROZEN, VISIBILITYMAP_ALL_VISIBLE, visibilitymap_clear(), visibilitymap_pin_ok(), visibilitymap_set(), VISIBILITYMAP_VALID_BITS, XLH_INSERT_ALL_FROZEN_SET, XLH_INSERT_ALL_VISIBLE_CLEARED, XLH_INSERT_CONTAINS_NEW_TUPLE, XLH_INSERT_LAST_IN_MULTI, XLOG_HEAP2_MULTI_INSERT, XLOG_HEAP_INIT_PAGE, XLOG_INCLUDE_ORIGIN, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), XLogRegisterData(), and XLogSetRecordFlags().

Referenced by CatalogTuplesMultiInsertWithInfo().

◆ heap_page_prune()

void heap_page_prune ( Relation  relation,
Buffer  buffer,
struct GlobalVisState vistest,
bool  mark_unused_now,
PruneResult presult,
OffsetNumber off_loc 
)

Definition at line 214 of file pruneheap.c.

219 {
220  Page page = BufferGetPage(buffer);
221  BlockNumber blockno = BufferGetBlockNumber(buffer);
222  OffsetNumber offnum,
223  maxoff;
224  PruneState prstate;
225  HeapTupleData tup;
226 
227  /*
228  * Our strategy is to scan the page and make lists of items to change,
229  * then apply the changes within a critical section. This keeps as much
230  * logic as possible out of the critical section, and also ensures that
231  * WAL replay will work the same as the normal case.
232  *
233  * First, initialize the new pd_prune_xid value to zero (indicating no
234  * prunable tuples). If we find any tuples which may soon become
235  * prunable, we will save the lowest relevant XID in new_prune_xid. Also
236  * initialize the rest of our working state.
237  */
239  prstate.rel = relation;
240  prstate.vistest = vistest;
241  prstate.mark_unused_now = mark_unused_now;
243  prstate.nredirected = prstate.ndead = prstate.nunused = 0;
244  memset(prstate.marked, 0, sizeof(prstate.marked));
245 
246  /*
247  * presult->htsv is not initialized here because all ntuple spots in the
248  * array will be set either to a valid HTSV_Result value or -1.
249  */
250  presult->ndeleted = 0;
251  presult->nnewlpdead = 0;
252 
253  maxoff = PageGetMaxOffsetNumber(page);
254  tup.t_tableOid = RelationGetRelid(prstate.rel);
255 
256  /*
257  * Determine HTSV for all tuples.
258  *
259  * This is required for correctness to deal with cases where running HTSV
260  * twice could result in different results (e.g. RECENTLY_DEAD can turn to
261  * DEAD if another checked item causes GlobalVisTestIsRemovableFullXid()
262  * to update the horizon, INSERT_IN_PROGRESS can change to DEAD if the
263  * inserting transaction aborts, ...). That in turn could cause
264  * heap_prune_chain() to behave incorrectly if a tuple is reached twice,
265  * once directly via a heap_prune_chain() and once following a HOT chain.
266  *
267  * It's also good for performance. Most commonly tuples within a page are
268  * stored at decreasing offsets (while the items are stored at increasing
269  * offsets). When processing all tuples on a page this leads to reading
270  * memory at decreasing offsets within a page, with a variable stride.
271  * That's hard for CPU prefetchers to deal with. Processing the items in
272  * reverse order (and thus the tuples in increasing order) increases
273  * prefetching efficiency significantly / decreases the number of cache
274  * misses.
275  */
276  for (offnum = maxoff;
277  offnum >= FirstOffsetNumber;
278  offnum = OffsetNumberPrev(offnum))
279  {
280  ItemId itemid = PageGetItemId(page, offnum);
281  HeapTupleHeader htup;
282 
283  /* Nothing to do if slot doesn't contain a tuple */
284  if (!ItemIdIsNormal(itemid))
285  {
286  presult->htsv[offnum] = -1;
287  continue;
288  }
289 
290  htup = (HeapTupleHeader) PageGetItem(page, itemid);
291  tup.t_data = htup;
292  tup.t_len = ItemIdGetLength(itemid);
293  ItemPointerSet(&(tup.t_self), blockno, offnum);
294 
295  /*
296  * Set the offset number so that we can display it along with any
297  * error that occurred while processing this tuple.
298  */
299  if (off_loc)
300  *off_loc = offnum;
301 
302  presult->htsv[offnum] = heap_prune_satisfies_vacuum(&prstate, &tup,
303  buffer);
304  }
305 
306  /* Scan the page */
307  for (offnum = FirstOffsetNumber;
308  offnum <= maxoff;
309  offnum = OffsetNumberNext(offnum))
310  {
311  ItemId itemid;
312 
313  /* Ignore items already processed as part of an earlier chain */
314  if (prstate.marked[offnum])
315  continue;
316 
317  /* see preceding loop */
318  if (off_loc)
319  *off_loc = offnum;
320 
321  /* Nothing to do if slot is empty */
322  itemid = PageGetItemId(page, offnum);
323  if (!ItemIdIsUsed(itemid))
324  continue;
325 
326  /* Process this item or chain of items */
327  presult->ndeleted += heap_prune_chain(buffer, offnum,
328  presult->htsv, &prstate);
329  }
330 
331  /* Clear the offset information once we have processed the given page. */
332  if (off_loc)
333  *off_loc = InvalidOffsetNumber;
334 
335  /* Any error while applying the changes is critical */
337 
338  /* Have we found any prunable items? */
339  if (prstate.nredirected > 0 || prstate.ndead > 0 || prstate.nunused > 0)
340  {
341  /*
342  * Apply the planned item changes, then repair page fragmentation, and
343  * update the page's hint bit about whether it has free line pointers.
344  */
346  prstate.redirected, prstate.nredirected,
347  prstate.nowdead, prstate.ndead,
348  prstate.nowunused, prstate.nunused);
349 
350  /*
351  * Update the page's pd_prune_xid field to either zero, or the lowest
352  * XID of any soon-prunable tuple.
353  */
354  ((PageHeader) page)->pd_prune_xid = prstate.new_prune_xid;
355 
356  /*
357  * Also clear the "page is full" flag, since there's no point in
358  * repeating the prune/defrag process until something else happens to
359  * the page.
360  */
361  PageClearFull(page);
362 
363  MarkBufferDirty(buffer);
364 
365  /*
366  * Emit a WAL XLOG_HEAP2_PRUNE record showing what we did
367  */
368  if (RelationNeedsWAL(relation))
369  {
370  xl_heap_prune xlrec;
371  XLogRecPtr recptr;
372 
375  xlrec.nredirected = prstate.nredirected;
376  xlrec.ndead = prstate.ndead;
377 
378  XLogBeginInsert();
379  XLogRegisterData((char *) &xlrec, SizeOfHeapPrune);
380 
382 
383  /*
384  * The OffsetNumber arrays are not actually in the buffer, but we
385  * pretend that they are. When XLogInsert stores the whole
386  * buffer, the offset arrays need not be stored too.
387  */
388  if (prstate.nredirected > 0)
389  XLogRegisterBufData(0, (char *) prstate.redirected,
390  prstate.nredirected *
391  sizeof(OffsetNumber) * 2);
392 
393  if (prstate.ndead > 0)
394  XLogRegisterBufData(0, (char *) prstate.nowdead,
395  prstate.ndead * sizeof(OffsetNumber));
396 
397  if (prstate.nunused > 0)
398  XLogRegisterBufData(0, (char *) prstate.nowunused,
399  prstate.nunused * sizeof(OffsetNumber));
400 
401  recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_PRUNE);
402 
403  PageSetLSN(BufferGetPage(buffer), recptr);
404  }
405  }
406  else
407  {
408  /*
409  * If we didn't prune anything, but have found a new value for the
410  * pd_prune_xid field, update it and mark the buffer dirty. This is
411  * treated as a non-WAL-logged hint.
412  *
413  * Also clear the "page is full" flag if it is set, since there's no
414  * point in repeating the prune/defrag process until something else
415  * happens to the page.
416  */
417  if (((PageHeader) page)->pd_prune_xid != prstate.new_prune_xid ||
418  PageIsFull(page))
419  {
420  ((PageHeader) page)->pd_prune_xid = prstate.new_prune_xid;
421  PageClearFull(page);
422  MarkBufferDirtyHint(buffer, true);
423  }
424  }
425 
427 
428  /* Record number of newly-set-LP_DEAD items for caller */
429  presult->nnewlpdead = prstate.ndead;
430 }
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:4624
PageHeaderData * PageHeader
Definition: bufpage.h:170
static void PageClearFull(Page page)
Definition: bufpage.h:420
static bool PageIsFull(Page page)
Definition: bufpage.h:410
#define XLOG_HEAP2_PRUNE
Definition: heapam_xlog.h:54
#define SizeOfHeapPrune
Definition: heapam_xlog.h:253
#define OffsetNumberPrev(offsetNumber)
Definition: off.h:54
static int heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, int8 *htsv, PruneState *prstate)
Definition: pruneheap.c:486
static HTSV_Result heap_prune_satisfies_vacuum(PruneState *prstate, HeapTuple tup, Buffer buffer)
Definition: pruneheap.c:437
void heap_page_prune_execute(Buffer buffer, OffsetNumber *redirected, int nredirected, OffsetNumber *nowdead, int ndead, OffsetNumber *nowunused, int nunused)
Definition: pruneheap.c:838
int nnewlpdead
Definition: heapam.h:200
int ndeleted
Definition: heapam.h:199
int8 htsv[MaxHeapTuplesPerPage+1]
Definition: heapam.h:211
int ndead
Definition: pruneheap.c:42
TransactionId new_prune_xid
Definition: pruneheap.c:39
OffsetNumber nowdead[MaxHeapTuplesPerPage]
Definition: pruneheap.c:46
bool marked[MaxHeapTuplesPerPage+1]
Definition: pruneheap.c:55
OffsetNumber nowunused[MaxHeapTuplesPerPage]
Definition: pruneheap.c:47
bool mark_unused_now
Definition: pruneheap.c:37
GlobalVisState * vistest
Definition: pruneheap.c:35
Relation rel
Definition: pruneheap.c:32
OffsetNumber redirected[MaxHeapTuplesPerPage *2]
Definition: pruneheap.c:45
int nredirected
Definition: pruneheap.c:41
int nunused
Definition: pruneheap.c:43
TransactionId snapshotConflictHorizon
Definition: pruneheap.c:40
TransactionId snapshotConflictHorizon
Definition: heapam_xlog.h:245
uint16 nredirected
Definition: heapam_xlog.h:246

References BufferGetBlockNumber(), BufferGetPage(), END_CRIT_SECTION, FirstOffsetNumber, heap_page_prune_execute(), heap_prune_chain(), heap_prune_satisfies_vacuum(), PruneResult::htsv, InvalidOffsetNumber, InvalidTransactionId, xl_heap_prune::isCatalogRel, ItemIdGetLength, ItemIdIsNormal, ItemIdIsUsed, ItemPointerSet(), PruneState::mark_unused_now, MarkBufferDirty(), MarkBufferDirtyHint(), PruneState::marked, PruneState::ndead, xl_heap_prune::ndead, PruneResult::ndeleted, PruneState::new_prune_xid, PruneResult::nnewlpdead, PruneState::nowdead, PruneState::nowunused, PruneState::nredirected, xl_heap_prune::nredirected, PruneState::nunused, OffsetNumberNext, OffsetNumberPrev, PageClearFull(), PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), PageIsFull(), PageSetLSN(), PruneState::redirected, REGBUF_STANDARD, PruneState::rel, RelationGetRelid, RelationIsAccessibleInLogicalDecoding, RelationNeedsWAL, SizeOfHeapPrune, PruneState::snapshotConflictHorizon, xl_heap_prune::snapshotConflictHorizon, START_CRIT_SECTION, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, PruneState::vistest, XLOG_HEAP2_PRUNE, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by heap_page_prune_opt(), and lazy_scan_prune().

◆ heap_page_prune_execute()

void heap_page_prune_execute ( Buffer  buffer,
OffsetNumber redirected,
int  nredirected,
OffsetNumber nowdead,
int  ndead,
OffsetNumber nowunused,
int  nunused 
)

Definition at line 838 of file pruneheap.c.

842 {
843  Page page = (Page) BufferGetPage(buffer);
844  OffsetNumber *offnum;
846 
847  /* Shouldn't be called unless there's something to do */
848  Assert(nredirected > 0 || ndead > 0 || nunused > 0);
849 
850  /* Update all redirected line pointers */
851  offnum = redirected;
852  for (int i = 0; i < nredirected; i++)
853  {
854  OffsetNumber fromoff = *offnum++;
855  OffsetNumber tooff = *offnum++;
856  ItemId fromlp = PageGetItemId(page, fromoff);
858 
859 #ifdef USE_ASSERT_CHECKING
860 
861  /*
862  * Any existing item that we set as an LP_REDIRECT (any 'from' item)
863  * must be the first item from a HOT chain. If the item has tuple
864  * storage then it can't be a heap-only tuple. Otherwise we are just
865  * maintaining an existing LP_REDIRECT from an existing HOT chain that
866  * has been pruned at least once before now.
867  */
868  if (!ItemIdIsRedirected(fromlp))
869  {
870  Assert(ItemIdHasStorage(fromlp) && ItemIdIsNormal(fromlp));
871 
872  htup = (HeapTupleHeader) PageGetItem(page, fromlp);
874  }
875  else
876  {
877  /* We shouldn't need to redundantly set the redirect */
878  Assert(ItemIdGetRedirect(fromlp) != tooff);
879  }
880 
881  /*
882  * The item that we're about to set as an LP_REDIRECT (the 'from'
883  * item) will point to an existing item (the 'to' item) that is
884  * already a heap-only tuple. There can be at most one LP_REDIRECT
885  * item per HOT chain.
886  *
887  * We need to keep around an LP_REDIRECT item (after original
888  * non-heap-only root tuple gets pruned away) so that it's always
889  * possible for VACUUM to easily figure out what TID to delete from
890  * indexes when an entire HOT chain becomes dead. A heap-only tuple
891  * can never become LP_DEAD; an LP_REDIRECT item or a regular heap
892  * tuple can.
893  *
894  * This check may miss problems, e.g. the target of a redirect could
895  * be marked as unused subsequently. The page_verify_redirects() check
896  * below will catch such problems.
897  */
898  tolp = PageGetItemId(page, tooff);
899  Assert(ItemIdHasStorage(tolp) && ItemIdIsNormal(tolp));
900  htup = (HeapTupleHeader) PageGetItem(page, tolp);
902 #endif
903 
904  ItemIdSetRedirect(fromlp, tooff);
905  }
906 
907  /* Update all now-dead line pointers */
908  offnum = nowdead;
909  for (int i = 0; i < ndead; i++)
910  {
911  OffsetNumber off = *offnum++;
912  ItemId lp = PageGetItemId(page, off);
913 
914 #ifdef USE_ASSERT_CHECKING
915 
916  /*
917  * An LP_DEAD line pointer must be left behind when the original item
918  * (which is dead to everybody) could still be referenced by a TID in
919  * an index. This should never be necessary with any individual
920  * heap-only tuple item, though. (It's not clear how much of a problem
921  * that would be, but there is no reason to allow it.)
922  */
923  if (ItemIdHasStorage(lp))
924  {
925  Assert(ItemIdIsNormal(lp));
926  htup = (HeapTupleHeader) PageGetItem(page, lp);
928  }
929  else
930  {
931  /* Whole HOT chain becomes dead */
933  }
934 #endif
935 
936  ItemIdSetDead(lp);
937  }
938 
939  /* Update all now-unused line pointers */
940  offnum = nowunused;
941  for (int i = 0; i < nunused; i++)
942  {
943  OffsetNumber off = *offnum++;
944  ItemId lp = PageGetItemId(page, off);
945 
946 #ifdef USE_ASSERT_CHECKING
947 
948  /*
949  * When heap_page_prune() was called, mark_unused_now may have been
950  * passed as true, which allows would-be LP_DEAD items to be made
951  * LP_UNUSED instead. This is only possible if the relation has no
952  * indexes. If there are any dead items, then mark_unused_now was not
953  * true and every item being marked LP_UNUSED must refer to a
954  * heap-only tuple.
955  */
956  if (ndead > 0)
957  {
959  htup = (HeapTupleHeader) PageGetItem(page, lp);
961  }
962  else
963  {
964  Assert(ItemIdIsUsed(lp));
965  }
966 
967 #endif
968 
969  ItemIdSetUnused(lp);
970  }
971 
972  /*
973  * Finally, repair any fragmentation, and update the page's hint bit about
974  * whether it has free pointers.
975  */
977 
978  /*
979  * Now that the page has been modified, assert that redirect items still
980  * point to valid targets.
981  */
982  page_verify_redirects(page);
983 }
void PageRepairFragmentation(Page page)
Definition: bufpage.c:699
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:169
#define ItemIdSetRedirect(itemId, link)
Definition: itemid.h:152
#define ItemIdSetDead(itemId)
Definition: itemid.h:164
#define ItemIdSetUnused(itemId)
Definition: itemid.h:128
#define ItemIdHasStorage(itemId)
Definition: itemid.h:120
static void page_verify_redirects(Page page)
Definition: pruneheap.c:1000

References Assert(), BufferGetPage(), HeapTupleHeaderIsHeapOnly, i, ItemIdGetRedirect, ItemIdHasStorage, ItemIdIsNormal, ItemIdIsRedirected, ItemIdIsUsed, ItemIdSetDead, ItemIdSetRedirect, ItemIdSetUnused, page_verify_redirects(), PageGetItem(), PageGetItemId(), PageRepairFragmentation(), and PG_USED_FOR_ASSERTS_ONLY.

Referenced by heap_page_prune(), and heap_xlog_prune().

◆ heap_page_prune_opt()

void heap_page_prune_opt ( Relation  relation,
Buffer  buffer 
)

Definition at line 88 of file pruneheap.c.

89 {
90  Page page = BufferGetPage(buffer);
91  TransactionId prune_xid;
92  GlobalVisState *vistest;
93  Size minfree;
94 
95  /*
96  * We can't write WAL in recovery mode, so there's no point trying to
97  * clean the page. The primary will likely issue a cleaning WAL record
98  * soon anyway, so this is no particular loss.
99  */
100  if (RecoveryInProgress())
101  return;
102 
103  /*
104  * First check whether there's any chance there's something to prune,
105  * determining the appropriate horizon is a waste if there's no prune_xid
106  * (i.e. no updates/deletes left potentially dead tuples around).
107  */
108  prune_xid = ((PageHeader) page)->pd_prune_xid;
109  if (!TransactionIdIsValid(prune_xid))
110  return;
111 
112  /*
113  * Check whether prune_xid indicates that there may be dead rows that can
114  * be cleaned up.
115  */
116  vistest = GlobalVisTestFor(relation);
117 
118  if (!GlobalVisTestIsRemovableXid(vistest, prune_xid))
119  return;
120 
121  /*
122  * We prune when a previous UPDATE failed to find enough space on the page
123  * for a new tuple version, or when free space falls below the relation's
124  * fill-factor target (but not less than 10%).
125  *
126  * Checking free space here is questionable since we aren't holding any
127  * lock on the buffer; in the worst case we could get a bogus answer. It's
128  * unlikely to be *seriously* wrong, though, since reading either pd_lower
129  * or pd_upper is probably atomic. Avoiding taking a lock seems more
130  * important than sometimes getting a wrong answer in what is after all
131  * just a heuristic estimate.
132  */
133  minfree = RelationGetTargetPageFreeSpace(relation,
135  minfree = Max(minfree, BLCKSZ / 10);
136 
137  if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
138  {
139  /* OK, try to get exclusive buffer lock */
140  if (!ConditionalLockBufferForCleanup(buffer))
141  return;
142 
143  /*
144  * Now that we have buffer lock, get accurate information about the
145  * page's free space, and recheck the heuristic about whether to
146  * prune.
147  */
148  if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
149  {
150  PruneResult presult;
151 
152  /*
153  * For now, pass mark_unused_now as false regardless of whether or
154  * not the relation has indexes, since we cannot safely determine
155  * that during on-access pruning with the current implementation.
156  */
157  heap_page_prune(relation, buffer, vistest, false,
158  &presult, NULL);
159 
160  /*
161  * Report the number of tuples reclaimed to pgstats. This is
162  * presult.ndeleted minus the number of newly-LP_DEAD-set items.
163  *
164  * We derive the number of dead tuples like this to avoid totally
165  * forgetting about items that were set to LP_DEAD, since they
166  * still need to be cleaned up by VACUUM. We only want to count
167  * heap-only tuples that just became LP_UNUSED in our report,
168  * which don't.
169  *
170  * VACUUM doesn't have to compensate in the same way when it
171  * tracks ndeleted, since it will set the same LP_DEAD items to
172  * LP_UNUSED separately.
173  */
174  if (presult.ndeleted > presult.nnewlpdead)
176  presult.ndeleted - presult.nnewlpdead);
177  }
178 
179  /* And release buffer lock */
181 
182  /*
183  * We avoid reuse of any free space created on the page by unrelated
184  * UPDATEs/INSERTs by opting to not update the FSM at this point. The
185  * free space should be reused by UPDATEs to *this* page.
186  */
187  }
188 }
bool ConditionalLockBufferForCleanup(Buffer buffer)
Definition: bufmgr.c:5036
#define Max(x, y)
Definition: c.h:985
void pgstat_update_heap_dead_tuples(Relation rel, int delta)
bool GlobalVisTestIsRemovableXid(GlobalVisState *state, TransactionId xid)
Definition: procarray.c:4248
void heap_page_prune(Relation relation, Buffer buffer, GlobalVisState *vistest, bool mark_unused_now, PruneResult *presult, OffsetNumber *off_loc)
Definition: pruneheap.c:214
bool RecoveryInProgress(void)
Definition: xlog.c:6201

References BUFFER_LOCK_UNLOCK, BufferGetPage(), ConditionalLockBufferForCleanup(), GlobalVisTestFor(), GlobalVisTestIsRemovableXid(), HEAP_DEFAULT_FILLFACTOR, heap_page_prune(), LockBuffer(), Max, PruneResult::ndeleted, PruneResult::nnewlpdead, PageGetHeapFreeSpace(), PageIsFull(), pgstat_update_heap_dead_tuples(), RecoveryInProgress(), RelationGetTargetPageFreeSpace, and TransactionIdIsValid.

Referenced by heapam_index_fetch_tuple(), heapam_scan_bitmap_next_block(), and heapgetpage().

◆ heap_prepare_freeze_tuple()

bool heap_prepare_freeze_tuple ( HeapTupleHeader  tuple,
const struct VacuumCutoffs cutoffs,
HeapPageFreeze pagefrz,
HeapTupleFreeze frz,
bool totally_frozen 
)

Definition at line 6375 of file heapam.c.

6379 {
6380  bool xmin_already_frozen = false,
6381  xmax_already_frozen = false;
6382  bool freeze_xmin = false,
6383  replace_xvac = false,
6384  replace_xmax = false,
6385  freeze_xmax = false;
6386  TransactionId xid;
6387 
6388  frz->xmax = HeapTupleHeaderGetRawXmax(tuple);
6389  frz->t_infomask2 = tuple->t_infomask2;
6390  frz->t_infomask = tuple->t_infomask;
6391  frz->frzflags = 0;
6392  frz->checkflags = 0;
6393 
6394  /*
6395  * Process xmin, while keeping track of whether it's already frozen, or
6396  * will become frozen iff our freeze plan is executed by caller (could be
6397  * neither).
6398  */
6399  xid = HeapTupleHeaderGetXmin(tuple);
6400  if (!TransactionIdIsNormal(xid))
6401  xmin_already_frozen = true;
6402  else
6403  {
6404  if (TransactionIdPrecedes(xid, cutoffs->relfrozenxid))
6405  ereport(ERROR,
6407  errmsg_internal("found xmin %u from before relfrozenxid %u",
6408  xid, cutoffs->relfrozenxid)));
6409 
6410  /* Will set freeze_xmin flags in freeze plan below */
6411  freeze_xmin = TransactionIdPrecedes(xid, cutoffs->OldestXmin);
6412 
6413  /* Verify that xmin committed if and when freeze plan is executed */
6414  if (freeze_xmin)
6416  }
6417 
6418  /*
6419  * Old-style VACUUM FULL is gone, but we have to process xvac for as long
6420  * as we support having MOVED_OFF/MOVED_IN tuples in the database
6421  */
6422  xid = HeapTupleHeaderGetXvac(tuple);
6423  if (TransactionIdIsNormal(xid))
6424  {
6426  Assert(TransactionIdPrecedes(xid, cutoffs->OldestXmin));
6427 
6428  /*
6429  * For Xvac, we always freeze proactively. This allows totally_frozen
6430  * tracking to ignore xvac.
6431  */
6432  replace_xvac = pagefrz->freeze_required = true;
6433 
6434  /* Will set replace_xvac flags in freeze plan below */
6435  }
6436 
6437  /* Now process xmax */
6438  xid = frz->xmax;
6439  if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
6440  {
6441  /* Raw xmax is a MultiXactId */
6442  TransactionId newxmax;
6443  uint16 flags;
6444 
6445  /*
6446  * We will either remove xmax completely (in the "freeze_xmax" path),
6447  * process xmax by replacing it (in the "replace_xmax" path), or
6448  * perform no-op xmax processing. The only constraint is that the
6449  * FreezeLimit/MultiXactCutoff postcondition must never be violated.
6450  */
6451  newxmax = FreezeMultiXactId(xid, tuple->t_infomask, cutoffs,
6452  &flags, pagefrz);
6453 
6454  if (flags & FRM_NOOP)
6455  {
6456  /*
6457  * xmax is a MultiXactId, and nothing about it changes for now.
6458  * This is the only case where 'freeze_required' won't have been
6459  * set for us by FreezeMultiXactId, as well as the only case where
6460  * neither freeze_xmax nor replace_xmax are set (given a multi).
6461  *
6462  * This is a no-op, but the call to FreezeMultiXactId might have
6463  * ratcheted back NewRelfrozenXid and/or NewRelminMxid trackers
6464  * for us (the "freeze page" variants, specifically). That'll
6465  * make it safe for our caller to freeze the page later on, while
6466  * leaving this particular xmax undisturbed.
6467  *
6468  * FreezeMultiXactId is _not_ responsible for the "no freeze"
6469  * NewRelfrozenXid/NewRelminMxid trackers, though -- that's our
6470  * job. A call to heap_tuple_should_freeze for this same tuple
6471  * will take place below if 'freeze_required' isn't set already.
6472  * (This repeats work from FreezeMultiXactId, but allows "no
6473  * freeze" tracker maintenance to happen in only one place.)
6474  */
6475  Assert(!MultiXactIdPrecedes(newxmax, cutoffs->MultiXactCutoff));
6476  Assert(MultiXactIdIsValid(newxmax) && xid == newxmax);
6477  }
6478  else if (flags & FRM_RETURN_IS_XID)
6479  {
6480  /*
6481  * xmax will become an updater Xid (original MultiXact's updater
6482  * member Xid will be carried forward as a simple Xid in Xmax).
6483  */
6484  Assert(!TransactionIdPrecedes(newxmax, cutoffs->OldestXmin));
6485 
6486  /*
6487  * NB -- some of these transformations are only valid because we
6488  * know the return Xid is a tuple updater (i.e. not merely a
6489  * locker.) Also note that the only reason we don't explicitly
6490  * worry about HEAP_KEYS_UPDATED is because it lives in
6491  * t_infomask2 rather than t_infomask.
6492  */
6493  frz->t_infomask &= ~HEAP_XMAX_BITS;
6494  frz->xmax = newxmax;
6495  if (flags & FRM_MARK_COMMITTED)
6497  replace_xmax = true;
6498  }
6499  else if (flags & FRM_RETURN_IS_MULTI)
6500  {
6501  uint16 newbits;
6502  uint16 newbits2;
6503 
6504  /*
6505  * xmax is an old MultiXactId that we have to replace with a new
6506  * MultiXactId, to carry forward two or more original member XIDs.
6507  */
6508  Assert(!MultiXactIdPrecedes(newxmax, cutoffs->OldestMxact));
6509 
6510  /*
6511  * We can't use GetMultiXactIdHintBits directly on the new multi
6512  * here; that routine initializes the masks to all zeroes, which
6513  * would lose other bits we need. Doing it this way ensures all
6514  * unrelated bits remain untouched.
6515  */
6516  frz->t_infomask &= ~HEAP_XMAX_BITS;
6517  frz->t_infomask2 &= ~HEAP_KEYS_UPDATED;
6518  GetMultiXactIdHintBits(newxmax, &newbits, &newbits2);
6519  frz->t_infomask |= newbits;
6520  frz->t_infomask2 |= newbits2;
6521  frz->xmax = newxmax;
6522  replace_xmax = true;
6523  }
6524  else
6525  {
6526  /*
6527  * Freeze plan for tuple "freezes xmax" in the strictest sense:
6528  * it'll leave nothing in xmax (neither an Xid nor a MultiXactId).
6529  */
6530  Assert(flags & FRM_INVALIDATE_XMAX);
6531  Assert(!TransactionIdIsValid(newxmax));
6532 
6533  /* Will set freeze_xmax flags in freeze plan below */
6534  freeze_xmax = true;
6535  }
6536 
6537  /* MultiXactId processing forces freezing (barring FRM_NOOP case) */
6538  Assert(pagefrz->freeze_required || (!freeze_xmax && !replace_xmax));
6539  }
6540  else if (TransactionIdIsNormal(xid))
6541  {
6542  /* Raw xmax is normal XID */
6543  if (TransactionIdPrecedes(xid, cutoffs->relfrozenxid))
6544  ereport(ERROR,
6546  errmsg_internal("found xmax %u from before relfrozenxid %u",
6547  xid, cutoffs->relfrozenxid)));
6548 
6549  /* Will set freeze_xmax flags in freeze plan below */
6550  freeze_xmax = TransactionIdPrecedes(xid, cutoffs->OldestXmin);
6551 
6552  /*
6553  * Verify that xmax aborted if and when freeze plan is executed,
6554  * provided it's from an update. (A lock-only xmax can be removed
6555  * independent of this, since the lock is released at xact end.)
6556  */
6557  if (freeze_xmax && !HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
6559  }
6560  else if (!TransactionIdIsValid(xid))
6561  {
6562  /* Raw xmax is InvalidTransactionId XID */
6563  Assert((tuple->t_infomask & HEAP_XMAX_IS_MULTI) == 0);
6564  xmax_already_frozen = true;
6565  }
6566  else
6567  ereport(ERROR,
6569  errmsg_internal("found raw xmax %u (infomask 0x%04x) not invalid and not multi",
6570  xid, tuple->t_infomask)));
6571 
6572  if (freeze_xmin)
6573  {
6574  Assert(!xmin_already_frozen);
6575 
6576  frz->t_infomask |= HEAP_XMIN_FROZEN;
6577  }
6578  if (replace_xvac)
6579  {
6580  /*
6581  * If a MOVED_OFF tuple is not dead, the xvac transaction must have
6582  * failed; whereas a non-dead MOVED_IN tuple must mean the xvac
6583  * transaction succeeded.
6584  */
6585  Assert(pagefrz->freeze_required);
6586  if (tuple->t_infomask & HEAP_MOVED_OFF)
6587  frz->frzflags |= XLH_INVALID_XVAC;
6588  else
6589  frz->frzflags |= XLH_FREEZE_XVAC;
6590  }
6591  if (replace_xmax)
6592  {
6593  Assert(!xmax_already_frozen && !freeze_xmax);
6594  Assert(pagefrz->freeze_required);
6595 
6596  /* Already set replace_xmax flags in freeze plan earlier */
6597  }
6598  if (freeze_xmax)
6599  {
6600  Assert(!xmax_already_frozen && !replace_xmax);
6601 
6602  frz->xmax = InvalidTransactionId;
6603 
6604  /*
6605  * The tuple might be marked either XMAX_INVALID or XMAX_COMMITTED +
6606  * LOCKED. Normalize to INVALID just to be sure no one gets confused.
6607  * Also get rid of the HEAP_KEYS_UPDATED bit.
6608  */
6609  frz->t_infomask &= ~HEAP_XMAX_BITS;
6610  frz->t_infomask |= HEAP_XMAX_INVALID;
6611  frz->t_infomask2 &= ~HEAP_HOT_UPDATED;
6612  frz->t_infomask2 &= ~HEAP_KEYS_UPDATED;
6613  }
6614 
6615  /*
6616  * Determine if this tuple is already totally frozen, or will become
6617  * totally frozen (provided caller executes freeze plans for the page)
6618  */
6619  *totally_frozen = ((freeze_xmin || xmin_already_frozen) &&
6620  (freeze_xmax || xmax_already_frozen));
6621 
6622  if (!pagefrz->freeze_required && !(xmin_already_frozen &&
6623  xmax_already_frozen))
6624  {
6625  /*
6626  * So far no previous tuple from the page made freezing mandatory.
6627  * Does this tuple force caller to freeze the entire page?
6628  */
6629  pagefrz->freeze_required =
6630  heap_tuple_should_freeze(tuple, cutoffs,
6631  &pagefrz->NoFreezePageRelfrozenXid,
6632  &pagefrz->NoFreezePageRelminMxid);
6633  }
6634 
6635  /* Tell caller if this tuple has a usable freeze plan set in *frz */
6636  return freeze_xmin || replace_xvac || replace_xmax || freeze_xmax;
6637 }
static void GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask, uint16 *new_infomask2)
Definition: heapam.c:6973
#define FRM_RETURN_IS_XID
Definition: heapam.c:5974
static TransactionId FreezeMultiXactId(MultiXactId multi, uint16 t_infomask, const struct VacuumCutoffs *cutoffs, uint16 *flags, HeapPageFreeze *pagefrz)
Definition: heapam.c:6025
bool heap_tuple_should_freeze(HeapTupleHeader tuple, const struct VacuumCutoffs *cutoffs, TransactionId *NoFreezePageRelfrozenXid, MultiXactId *NoFreezePageRelminMxid)
Definition: heapam.c:7390
#define FRM_MARK_COMMITTED
Definition: heapam.c:5976
#define FRM_NOOP
Definition: heapam.c:5972
#define FRM_RETURN_IS_MULTI
Definition: heapam.c:5975
#define FRM_INVALIDATE_XMAX
Definition: heapam.c:5973
#define XLH_INVALID_XVAC
Definition: heapam_xlog.h:324
#define XLH_FREEZE_XVAC
Definition: heapam_xlog.h:323
#define HEAP_MOVED_OFF
Definition: htup_details.h:211
#define HEAP_XMIN_FROZEN
Definition: htup_details.h:206
#define HEAP_HOT_UPDATED
Definition: htup_details.h:276
#define HeapTupleHeaderGetXvac(tup)
Definition: htup_details.h:411
#define HEAP_XMAX_COMMITTED
Definition: htup_details.h:207
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3234
#define MultiXactIdIsValid(multi)
Definition: multixact.h:28
MultiXactId NoFreezePageRelminMxid
Definition: heapam.h:190
bool freeze_required
Definition: heapam.h:152
TransactionId NoFreezePageRelfrozenXid
Definition: heapam.h:189
uint8 frzflags
Definition: heapam.h:117
uint16 t_infomask2
Definition: heapam.h:115
TransactionId xmax
Definition: heapam.h:114
uint16 t_infomask
Definition: heapam.h:116
TransactionId OldestXmin
Definition: vacuum.h:266
MultiXactId OldestMxact
Definition: vacuum.h:267
bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:299

References Assert(), HeapTupleFreeze::checkflags, ereport, errcode(), ERRCODE_DATA_CORRUPTED, errmsg_internal(), ERROR, HeapPageFreeze::freeze_required, FreezeMultiXactId(), FRM_INVALIDATE_XMAX, FRM_MARK_COMMITTED, FRM_NOOP, FRM_RETURN_IS_MULTI, FRM_RETURN_IS_XID, HeapTupleFreeze::frzflags, GetMultiXactIdHintBits(), HEAP_FREEZE_CHECK_XMAX_ABORTED, HEAP_FREEZE_CHECK_XMIN_COMMITTED, HEAP_HOT_UPDATED, HEAP_KEYS_UPDATED, HEAP_MOVED_OFF, heap_tuple_should_freeze(), HEAP_XMAX_BITS, HEAP_XMAX_COMMITTED, HEAP_XMAX_INVALID, HEAP_XMAX_IS_LOCKED_ONLY, HEAP_XMAX_IS_MULTI, HEAP_XMIN_FROZEN, HeapTupleHeaderGetRawXmax, HeapTupleHeaderGetXmin, HeapTupleHeaderGetXvac, InvalidTransactionId, VacuumCutoffs::MultiXactCutoff, MultiXactIdIsValid, MultiXactIdPrecedes(), HeapPageFreeze::NoFreezePageRelfrozenXid, HeapPageFreeze::NoFreezePageRelminMxid, VacuumCutoffs::OldestMxact, VacuumCutoffs::OldestXmin, VacuumCutoffs::relfrozenxid, HeapTupleFreeze::t_infomask, HeapTupleHeaderData::t_infomask, HeapTupleFreeze::t_infomask2, HeapTupleHeaderData::t_infomask2, TransactionIdIsNormal, TransactionIdIsValid, TransactionIdPrecedes(), TransactionIdPrecedesOrEquals(), XLH_FREEZE_XVAC, XLH_INVALID_XVAC, and HeapTupleFreeze::xmax.

Referenced by heap_freeze_tuple(), and lazy_scan_prune().

◆ heap_rescan()

void heap_rescan ( TableScanDesc  sscan,
ScanKey  key,
bool  set_params,
bool  allow_strat,
bool  allow_sync,
bool  allow_pagemode 
)

Definition at line 1012 of file heapam.c.

1014 {
1015  HeapScanDesc scan = (HeapScanDesc) sscan;
1016 
1017  if (set_params)
1018  {
1019  if (allow_strat)
1020  scan->rs_base.rs_flags |= SO_ALLOW_STRAT;
1021  else
1022  scan->rs_base.rs_flags &= ~SO_ALLOW_STRAT;
1023 
1024  if (allow_sync)
1025  scan->rs_base.rs_flags |= SO_ALLOW_SYNC;
1026  else
1027  scan->rs_base.rs_flags &= ~SO_ALLOW_SYNC;
1028 
1029  if (allow_pagemode && scan->rs_base.rs_snapshot &&
1032  else
1034  }
1035 
1036  /*
1037  * unpin scan buffers
1038  */
1039  if (BufferIsValid(scan->rs_cbuf))
1040  ReleaseBuffer(scan->rs_cbuf);
1041 
1042  /*
1043  * reinitialize scan descriptor
1044  */
1045  initscan(scan, key, true);
1046 }
@ SO_ALLOW_STRAT
Definition: tableam.h:57
@ SO_ALLOW_SYNC
Definition: tableam.h:59

References BufferIsValid(), initscan(), IsMVCCSnapshot, sort-test::key, ReleaseBuffer(), HeapScanDescData::rs_base, HeapScanDescData::rs_cbuf, TableScanDescData::rs_flags, TableScanDescData::rs_snapshot, SO_ALLOW_PAGEMODE, SO_ALLOW_STRAT, and SO_ALLOW_SYNC.

◆ heap_set_tidrange()

void heap_set_tidrange ( TableScanDesc  sscan,
ItemPointer  mintid,
ItemPointer  maxtid 
)

Definition at line 1161 of file heapam.c.

1163 {
1164  HeapScanDesc scan = (HeapScanDesc) sscan;
1165  BlockNumber startBlk;
1166  BlockNumber numBlks;
1167  ItemPointerData highestItem;
1168  ItemPointerData lowestItem;
1169 
1170  /*
1171  * For relations without any pages, we can simply leave the TID range
1172  * unset. There will be no tuples to scan, therefore no tuples outside
1173  * the given TID range.
1174  */
1175  if (scan->rs_nblocks == 0)
1176  return;
1177 
1178  /*
1179  * Set up some ItemPointers which point to the first and last possible
1180  * tuples in the heap.
1181  */
1182  ItemPointerSet(&highestItem, scan->rs_nblocks - 1, MaxOffsetNumber);
1183  ItemPointerSet(&lowestItem, 0, FirstOffsetNumber);
1184 
1185  /*
1186  * If the given maximum TID is below the highest possible TID in the
1187  * relation, then restrict the range to that, otherwise we scan to the end
1188  * of the relation.
1189  */
1190  if (ItemPointerCompare(maxtid, &highestItem) < 0)
1191  ItemPointerCopy(maxtid, &highestItem);
1192 
1193  /*
1194  * If the given minimum TID is above the lowest possible TID in the
1195  * relation, then restrict the range to only scan for TIDs above that.
1196  */
1197  if (ItemPointerCompare(mintid, &lowestItem) > 0)
1198  ItemPointerCopy(mintid, &lowestItem);
1199 
1200  /*
1201  * Check for an empty range and protect from would be negative results
1202  * from the numBlks calculation below.
1203  */
1204  if (ItemPointerCompare(&highestItem, &lowestItem) < 0)
1205  {
1206  /* Set an empty range of blocks to scan */
1207  heap_setscanlimits(sscan, 0, 0);
1208  return;
1209  }
1210 
1211  /*
1212  * Calculate the first block and the number of blocks we must scan. We
1213  * could be more aggressive here and perform some more validation to try
1214  * and further narrow the scope of blocks to scan by checking if the
1215  * lowestItem has an offset above MaxOffsetNumber. In this case, we could
1216  * advance startBlk by one. Likewise, if highestItem has an offset of 0
1217  * we could scan one fewer blocks. However, such an optimization does not
1218  * seem worth troubling over, currently.
1219  */
1220  startBlk = ItemPointerGetBlockNumberNoCheck(&lowestItem);
1221 
1222  numBlks = ItemPointerGetBlockNumberNoCheck(&highestItem) -
1223  ItemPointerGetBlockNumberNoCheck(&lowestItem) + 1;
1224 
1225  /* Set the start block and number of blocks to scan */
1226  heap_setscanlimits(sscan, startBlk, numBlks);
1227 
1228  /* Finally, set the TID range in sscan */
1229  ItemPointerCopy(&lowestItem, &sscan->rs_mintid);
1230  ItemPointerCopy(&highestItem, &sscan->rs_maxtid);
1231 }
void heap_setscanlimits(TableScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
Definition: heapam.c:350
static BlockNumber ItemPointerGetBlockNumberNoCheck(const ItemPointerData *pointer)
Definition: itemptr.h:93
#define MaxOffsetNumber
Definition: off.h:28
BlockNumber rs_nblocks
Definition: heapam.h:53

References FirstOffsetNumber, heap_setscanlimits(), ItemPointerCompare(), ItemPointerCopy(), ItemPointerGetBlockNumberNoCheck(), ItemPointerSet(), MaxOffsetNumber, TableScanDescData::rs_maxtid, TableScanDescData::rs_mintid, and HeapScanDescData::rs_nblocks.

◆ heap_setscanlimits()

void heap_setscanlimits ( TableScanDesc  sscan,
BlockNumber  startBlk,
BlockNumber  numBlks 
)

Definition at line 350 of file heapam.c.

351 {
352  HeapScanDesc scan = (HeapScanDesc) sscan;
353 
354  Assert(!scan->rs_inited); /* else too late to change */
355  /* else rs_startblock is significant */
356  Assert(!(scan->rs_base.rs_flags & SO_ALLOW_SYNC));
357 
358  /* Check startBlk is valid (but allow case of zero blocks...) */
359  Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
360 
361  scan->rs_startblock = startBlk;
362  scan->rs_numblocks = numBlks;
363 }
bool rs_inited
Definition: heapam.h:59
BlockNumber rs_startblock
Definition: heapam.h:54
BlockNumber rs_numblocks
Definition: heapam.h:55

References Assert(), HeapScanDescData::rs_base, TableScanDescData::rs_flags, HeapScanDescData::rs_inited, HeapScanDescData::rs_numblocks, HeapScanDescData::rs_startblock, and SO_ALLOW_SYNC.

Referenced by heap_set_tidrange(), and heapam_index_build_range_scan().

◆ heap_tuple_needs_eventual_freeze()

bool heap_tuple_needs_eventual_freeze ( HeapTupleHeader  tuple)

Definition at line 7335 of file heapam.c.

7336 {
7337  TransactionId xid;
7338 
7339  /*
7340  * If xmin is a normal transaction ID, this tuple is definitely not
7341  * frozen.
7342  */
7343  xid = HeapTupleHeaderGetXmin(tuple);
7344  if (TransactionIdIsNormal(xid))
7345  return true;
7346 
7347  /*
7348  * If xmax is a valid xact or multixact, this tuple is also not frozen.
7349  */
7350  if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
7351  {
7352  MultiXactId multi;
7353 
7354  multi = HeapTupleHeaderGetRawXmax(tuple);
7355  if (MultiXactIdIsValid(multi))
7356  return true;
7357  }
7358  else
7359  {
7360  xid = HeapTupleHeaderGetRawXmax(tuple);
7361  if (TransactionIdIsNormal(xid))
7362  return true;
7363  }
7364 
7365  if (tuple->t_infomask & HEAP_MOVED)
7366  {
7367  xid = HeapTupleHeaderGetXvac(tuple);
7368  if (TransactionIdIsNormal(xid))
7369  return true;
7370  }
7371 
7372  return false;
7373 }

References HEAP_MOVED, HEAP_XMAX_IS_MULTI, HeapTupleHeaderGetRawXmax, HeapTupleHeaderGetXmin, HeapTupleHeaderGetXvac, MultiXactIdIsValid, HeapTupleHeaderData::t_infomask, and TransactionIdIsNormal.

Referenced by collect_corrupt_items(), and heap_page_is_all_visible().

◆ heap_tuple_should_freeze()

bool heap_tuple_should_freeze ( HeapTupleHeader  tuple,
const struct VacuumCutoffs cutoffs,
TransactionId NoFreezePageRelfrozenXid,
MultiXactId NoFreezePageRelminMxid 
)

Definition at line 7390 of file heapam.c.

7394 {
7395  TransactionId xid;
7396  MultiXactId multi;
7397  bool freeze = false;
7398 
7399  /* First deal with xmin */
7400  xid = HeapTupleHeaderGetXmin(tuple);
7401  if (TransactionIdIsNormal(xid))
7402  {
7404  if (TransactionIdPrecedes(xid, *NoFreezePageRelfrozenXid))
7405  *NoFreezePageRelfrozenXid = xid;
7406  if (TransactionIdPrecedes(xid, cutoffs->FreezeLimit))
7407  freeze = true;
7408  }
7409 
7410  /* Now deal with xmax */
7411  xid = InvalidTransactionId;
7412  multi = InvalidMultiXactId;
7413  if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
7414  multi = HeapTupleHeaderGetRawXmax(tuple);
7415  else
7416  xid = HeapTupleHeaderGetRawXmax(tuple);
7417 
7418  if (TransactionIdIsNormal(xid))
7419  {
7421  /* xmax is a non-permanent XID */
7422  if (TransactionIdPrecedes(xid, *NoFreezePageRelfrozenXid))
7423  *NoFreezePageRelfrozenXid = xid;
7424  if (TransactionIdPrecedes(xid, cutoffs->FreezeLimit))
7425  freeze = true;
7426  }
7427  else if (!MultiXactIdIsValid(multi))
7428  {
7429  /* xmax is a permanent XID or invalid MultiXactId/XID */
7430  }
7431  else if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
7432  {
7433  /* xmax is a pg_upgrade'd MultiXact, which can't have updater XID */
7434  if (MultiXactIdPrecedes(multi, *NoFreezePageRelminMxid))
7435  *NoFreezePageRelminMxid = multi;
7436  /* heap_prepare_freeze_tuple always freezes pg_upgrade'd xmax */
7437  freeze = true;
7438  }
7439  else
7440  {
7441  /* xmax is a MultiXactId that may have an updater XID */
7442  MultiXactMember *members;
7443  int nmembers;
7444 
7445  Assert(MultiXactIdPrecedesOrEquals(cutoffs->relminmxid, multi));
7446  if (MultiXactIdPrecedes(multi, *NoFreezePageRelminMxid))
7447  *NoFreezePageRelminMxid = multi;
7448  if (MultiXactIdPrecedes(multi, cutoffs->MultiXactCutoff))
7449  freeze = true;
7450 
7451  /* need to check whether any member of the mxact is old */
7452  nmembers = GetMultiXactIdMembers(multi, &members, false,
7454 
7455  for (int i = 0; i < nmembers; i++)
7456  {
7457  xid = members[i].xid;
7459  if (TransactionIdPrecedes(xid, *NoFreezePageRelfrozenXid))
7460  *NoFreezePageRelfrozenXid = xid;
7461  if (TransactionIdPrecedes(xid, cutoffs->FreezeLimit))
7462  freeze = true;
7463  }
7464  if (nmembers > 0)
7465  pfree(members);
7466  }
7467 
7468  if (tuple->t_infomask & HEAP_MOVED)
7469  {
7470  xid = HeapTupleHeaderGetXvac(tuple);
7471  if (TransactionIdIsNormal(xid))
7472  {
7474  if (TransactionIdPrecedes(xid, *NoFreezePageRelfrozenXid))
7475  *NoFreezePageRelfrozenXid = xid;
7476  /* heap_prepare_freeze_tuple forces xvac freezing */
7477  freeze = true;
7478  }
7479  }
7480 
7481  return freeze;
7482 }
#define HEAP_LOCKED_UPGRADED(infomask)
Definition: htup_details.h:249
bool MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3248
#define InvalidMultiXactId
Definition: multixact.h:24
TransactionId xid
Definition: multixact.h:58

References Assert(), VacuumCutoffs::FreezeLimit, GetMultiXactIdMembers(), HEAP_LOCKED_UPGRADED, HEAP_MOVED, HEAP_XMAX_IS_LOCKED_ONLY, HEAP_XMAX_IS_MULTI, HeapTupleHeaderGetRawXmax, HeapTupleHeaderGetXmin, HeapTupleHeaderGetXvac, i, InvalidMultiXactId, InvalidTransactionId, VacuumCutoffs::MultiXactCutoff, MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactIdPrecedesOrEquals(), pfree(), VacuumCutoffs::relfrozenxid, VacuumCutoffs::relminmxid, HeapTupleHeaderData::t_infomask, TransactionIdIsNormal, TransactionIdPrecedes(), TransactionIdPrecedesOrEquals(), and MultiXactMember::xid.

Referenced by heap_prepare_freeze_tuple(), and lazy_scan_noprune().

◆ heap_update()

TM_Result heap_update ( Relation  relation,
ItemPointer  otid,
HeapTuple  newtup,
CommandId  cid,
Snapshot  crosscheck,
bool  wait,
struct TM_FailureData tmfd,
LockTupleMode lockmode,
TU_UpdateIndexes update_indexes 
)

Definition at line 2980 of file heapam.c.

2984 {
2985  TM_Result result;
2987  Bitmapset *hot_attrs;
2988  Bitmapset *sum_attrs;
2989  Bitmapset *key_attrs;
2990  Bitmapset *id_attrs;
2991  Bitmapset *interesting_attrs;
2992  Bitmapset *modified_attrs;
2993  ItemId lp;
2994  HeapTupleData oldtup;
2995  HeapTuple heaptup;
2996  HeapTuple old_key_tuple = NULL;
2997  bool old_key_copied = false;
2998  Page page;
2999  BlockNumber block;
3000  MultiXactStatus mxact_status;
3001  Buffer buffer,
3002  newbuf,
3003  vmbuffer = InvalidBuffer,
3004  vmbuffer_new = InvalidBuffer;
3005  bool need_toast;
3006  Size newtupsize,
3007  pagefree;
3008  bool have_tuple_lock = false;
3009  bool iscombo;
3010  bool use_hot_update = false;
3011  bool summarized_update = false;
3012  bool key_intact;
3013  bool all_visible_cleared = false;
3014  bool all_visible_cleared_new = false;
3015  bool checked_lockers;
3016  bool locker_remains;
3017  bool id_has_external = false;
3018  TransactionId xmax_new_tuple,
3019  xmax_old_tuple;
3020  uint16 infomask_old_tuple,
3021  infomask2_old_tuple,
3022  infomask_new_tuple,
3023  infomask2_new_tuple;
3024 
3025  Assert(ItemPointerIsValid(otid));
3026 
3027  /* Cheap, simplistic check that the tuple matches the rel's rowtype. */
3029  RelationGetNumberOfAttributes(relation));
3030 
3031  /*
3032  * Forbid this during a parallel operation, lest it allocate a combo CID.
3033  * Other workers might need that combo CID for visibility checks, and we
3034  * have no provision for broadcasting it to them.
3035  */
3036  if (IsInParallelMode())
3037  ereport(ERROR,
3038  (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
3039  errmsg("cannot update tuples during a parallel operation")));
3040 
3041  /*
3042  * Fetch the list of attributes to be checked for various operations.
3043  *
3044  * For HOT considerations, this is wasted effort if we fail to update or
3045  * have to put the new tuple on a different page. But we must compute the
3046  * list before obtaining buffer lock --- in the worst case, if we are
3047  * doing an update on one of the relevant system catalogs, we could
3048  * deadlock if we try to fetch the list later. In any case, the relcache
3049  * caches the data so this is usually pretty cheap.
3050  *
3051  * We also need columns used by the replica identity and columns that are
3052  * considered the "key" of rows in the table.
3053  *
3054  * Note that we get copies of each bitmap, so we need not worry about
3055  * relcache flush happening midway through.
3056  */
3057  hot_attrs = RelationGetIndexAttrBitmap(relation,
3059  sum_attrs = RelationGetIndexAttrBitmap(relation,
3061  key_attrs = RelationGetIndexAttrBitmap(relation, INDEX_ATTR_BITMAP_KEY);
3062  id_attrs = RelationGetIndexAttrBitmap(relation,
3064  interesting_attrs = NULL;
3065  interesting_attrs = bms_add_members(interesting_attrs, hot_attrs);
3066  interesting_attrs = bms_add_members(interesting_attrs, sum_attrs);
3067  interesting_attrs = bms_add_members(interesting_attrs, key_attrs);
3068  interesting_attrs = bms_add_members(interesting_attrs, id_attrs);
3069 
3070  block = ItemPointerGetBlockNumber(otid);
3071  buffer = ReadBuffer(relation, block);
3072  page = BufferGetPage(buffer);
3073 
3074  /*
3075  * Before locking the buffer, pin the visibility map page if it appears to
3076  * be necessary. Since we haven't got the lock yet, someone else might be
3077  * in the middle of changing this, so we'll need to recheck after we have
3078  * the lock.
3079  */
3080  if (PageIsAllVisible(page))
3081  visibilitymap_pin(relation, block, &vmbuffer);
3082 
3084 
3085  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(otid));
3086  Assert(ItemIdIsNormal(lp));
3087 
3088  /*
3089  * Fill in enough data in oldtup for HeapDetermineColumnsInfo to work
3090  * properly.
3091  */
3092  oldtup.t_tableOid = RelationGetRelid(relation);
3093  oldtup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
3094  oldtup.t_len = ItemIdGetLength(lp);
3095  oldtup.t_self = *otid;
3096 
3097  /* the new tuple is ready, except for this: */
3098  newtup->t_tableOid = RelationGetRelid(relation);
3099 
3100  /*
3101  * Determine columns modified by the update. Additionally, identify
3102  * whether any of the unmodified replica identity key attributes in the
3103  * old tuple is externally stored or not. This is required because for
3104  * such attributes the flattened value won't be WAL logged as part of the
3105  * new tuple so we must include it as part of the old_key_tuple. See
3106  * ExtractReplicaIdentity.
3107  */
3108  modified_attrs = HeapDetermineColumnsInfo(relation, interesting_attrs,
3109  id_attrs, &oldtup,
3110  newtup, &id_has_external);
3111 
3112  /*
3113  * If we're not updating any "key" column, we can grab a weaker lock type.
3114  * This allows for more concurrency when we are running simultaneously
3115  * with foreign key checks.
3116  *
3117  * Note that if a column gets detoasted while executing the update, but
3118  * the value ends up being the same, this test will fail and we will use
3119  * the stronger lock. This is acceptable; the important case to optimize
3120  * is updates that don't manipulate key columns, not those that
3121  * serendipitously arrive at the same key values.
3122  */
3123  if (!bms_overlap(modified_attrs, key_attrs))
3124  {
3125  *lockmode = LockTupleNoKeyExclusive;
3126  mxact_status = MultiXactStatusNoKeyUpdate;
3127  key_intact = true;
3128 
3129  /*
3130  * If this is the first possibly-multixact-able operation in the
3131  * current transaction, set my per-backend OldestMemberMXactId
3132  * setting. We can be certain that the transaction will never become a
3133  * member of any older MultiXactIds than that. (We have to do this
3134  * even if we end up just using our own TransactionId below, since
3135  * some other backend could incorporate our XID into a MultiXact
3136  * immediately afterwards.)
3137  */
3139  }
3140  else
3141  {
3142  *lockmode = LockTupleExclusive;
3143  mxact_status = MultiXactStatusUpdate;
3144  key_intact = false;
3145  }
3146 
3147  /*
3148  * Note: beyond this point, use oldtup not otid to refer to old tuple.
3149  * otid may very well point at newtup->t_self, which we will overwrite
3150  * with the new tuple's location, so there's great risk of confusion if we
3151  * use otid anymore.
3152  */
3153 
3154 l2:
3155  checked_lockers = false;
3156  locker_remains = false;
3157  result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer);
3158 
3159  /* see below about the "no wait" case */
3160  Assert(result != TM_BeingModified || wait);
3161 
3162  if (result == TM_Invisible)
3163  {
3164  UnlockReleaseBuffer(buffer);
3165  ereport(ERROR,
3166  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3167  errmsg("attempted to update invisible tuple")));
3168  }
3169  else if (result == TM_BeingModified && wait)
3170  {
3171  TransactionId xwait;
3172  uint16 infomask;
3173  bool can_continue = false;
3174 
3175  /*
3176  * XXX note that we don't consider the "no wait" case here. This
3177  * isn't a problem currently because no caller uses that case, but it
3178  * should be fixed if such a caller is introduced. It wasn't a
3179  * problem previously because this code would always wait, but now
3180  * that some tuple locks do not conflict with one of the lock modes we
3181  * use, it is possible that this case is interesting to handle
3182  * specially.
3183  *
3184  * This may cause failures with third-party code that calls
3185  * heap_update directly.
3186  */
3187 
3188  /* must copy state data before unlocking buffer */
3189  xwait = HeapTupleHeaderGetRawXmax(oldtup.t_data);
3190  infomask = oldtup.t_data->t_infomask;
3191 
3192  /*
3193  * Now we have to do something about the existing locker. If it's a
3194  * multi, sleep on it; we might be awakened before it is completely
3195  * gone (or even not sleep at all in some cases); we need to preserve
3196  * it as locker, unless it is gone completely.
3197  *
3198  * If it's not a multi, we need to check for sleeping conditions
3199  * before actually going to sleep. If the update doesn't conflict
3200  * with the locks, we just continue without sleeping (but making sure
3201  * it is preserved).
3202  *
3203  * Before sleeping, we need to acquire tuple lock to establish our
3204  * priority for the tuple (see heap_lock_tuple). LockTuple will
3205  * release us when we are next-in-line for the tuple. Note we must
3206  * not acquire the tuple lock until we're sure we're going to sleep;
3207  * otherwise we're open for race conditions with other transactions
3208  * holding the tuple lock which sleep on us.
3209  *
3210  * If we are forced to "start over" below, we keep the tuple lock;
3211  * this arranges that we stay at the head of the line while rechecking
3212  * tuple state.
3213  */
3214  if (infomask & HEAP_XMAX_IS_MULTI)
3215  {
3216  TransactionId update_xact;
3217  int remain;
3218  bool current_is_member = false;
3219 
3220  if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
3221  *lockmode, &current_is_member))
3222  {
3223  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
3224 
3225  /*
3226  * Acquire the lock, if necessary (but skip it when we're
3227  * requesting a lock and already have one; avoids deadlock).
3228  */
3229  if (!current_is_member)
3230  heap_acquire_tuplock(relation, &(oldtup.t_self), *lockmode,
3231  LockWaitBlock, &have_tuple_lock);
3232 
3233  /* wait for multixact */
3234  MultiXactIdWait((MultiXactId) xwait, mxact_status, infomask,
3235  relation, &oldtup.t_self, XLTW_Update,
3236  &remain);
3237  checked_lockers = true;
3238  locker_remains = remain != 0;
3240 
3241  /*
3242  * If xwait had just locked the tuple then some other xact
3243  * could update this tuple before we get to this point. Check
3244  * for xmax change, and start over if so.
3245  */
3247  infomask) ||
3249  xwait))
3250  goto l2;
3251  }
3252 
3253  /*
3254  * Note that the multixact may not be done by now. It could have
3255  * surviving members; our own xact or other subxacts of this
3256  * backend, and also any other concurrent transaction that locked
3257  * the tuple with LockTupleKeyShare if we only got
3258  * LockTupleNoKeyExclusive. If this is the case, we have to be
3259  * careful to mark the updated tuple with the surviving members in
3260  * Xmax.
3261  *
3262  * Note that there could have been another update in the
3263  * MultiXact. In that case, we need to check whether it committed
3264  * or aborted. If it aborted we are safe to update it again;
3265  * otherwise there is an update conflict, and we have to return
3266  * TableTuple{Deleted, Updated} below.
3267  *
3268  * In the LockTupleExclusive case, we still need to preserve the
3269  * surviving members: those would include the tuple locks we had
3270  * before this one, which are important to keep in case this
3271  * subxact aborts.
3272  */
3274  update_xact = HeapTupleGetUpdateXid(oldtup.t_data);
3275  else
3276  update_xact = InvalidTransactionId;
3277 
3278  /*
3279  * There was no UPDATE in the MultiXact; or it aborted. No
3280  * TransactionIdIsInProgress() call needed here, since we called
3281  * MultiXactIdWait() above.
3282  */
3283  if (!TransactionIdIsValid(update_xact) ||
3284  TransactionIdDidAbort(update_xact))
3285  can_continue = true;
3286  }
3287  else if (TransactionIdIsCurrentTransactionId(xwait))
3288  {
3289  /*
3290  * The only locker is ourselves; we can avoid grabbing the tuple
3291  * lock here, but must preserve our locking information.
3292  */
3293  checked_lockers = true;
3294  locker_remains = true;
3295  can_continue = true;
3296  }
3297  else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) && key_intact)
3298  {
3299  /*
3300  * If it's just a key-share locker, and we're not changing the key
3301  * columns, we don't need to wait for it to end; but we need to
3302  * preserve it as locker.
3303  */
3304  checked_lockers = true;
3305  locker_remains = true;
3306  can_continue = true;
3307  }
3308  else
3309  {
3310  /*
3311  * Wait for regular transaction to end; but first, acquire tuple
3312  * lock.
3313  */
3314  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
3315  heap_acquire_tuplock(relation, &(oldtup.t_self), *lockmode,
3316  LockWaitBlock, &have_tuple_lock);
3317  XactLockTableWait(xwait, relation, &oldtup.t_self,
3318  XLTW_Update);
3319  checked_lockers = true;
3321 
3322  /*
3323  * xwait is done, but if xwait had just locked the tuple then some
3324  * other xact could update this tuple before we get to this point.
3325  * Check for xmax change, and start over if so.
3326  */
3327  if (xmax_infomask_changed(oldtup.t_data->t_infomask, infomask) ||
3328  !TransactionIdEquals(xwait,
3330  goto l2;
3331 
3332  /* Otherwise check if it committed or aborted */
3333  UpdateXmaxHintBits(oldtup.t_data, buffer, xwait);
3334  if (oldtup.t_data->t_infomask & HEAP_XMAX_INVALID)
3335  can_continue = true;
3336  }
3337 
3338  if (can_continue)
3339  result = TM_Ok;
3340  else if (!ItemPointerEquals(&oldtup.t_self, &oldtup.t_data->t_ctid))
3341  result = TM_Updated;
3342  else
3343  result = TM_Deleted;
3344  }
3345 
3346  /* Sanity check the result HeapTupleSatisfiesUpdate() and the logic above */
3347  if (result != TM_Ok)
3348  {
3349  Assert(result == TM_SelfModified ||
3350  result == TM_Updated ||
3351  result == TM_Deleted ||
3352  result == TM_BeingModified);
3353  Assert(!(oldtup.t_data->t_infomask & HEAP_XMAX_INVALID));
3354  Assert(result != TM_Updated ||
3355  !ItemPointerEquals(&oldtup.t_self, &oldtup.t_data->t_ctid));
3356  }
3357 
3358  if (crosscheck != InvalidSnapshot && result == TM_Ok)
3359  {
3360  /* Perform additional check for transaction-snapshot mode RI updates */
3361  if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer))
3362  result = TM_Updated;
3363  }
3364 
3365  if (result != TM_Ok)
3366  {
3367  tmfd->ctid = oldtup.t_data->t_ctid;
3368  tmfd->xmax = HeapTupleHeaderGetUpdateXid(oldtup.t_data);
3369  if (result == TM_SelfModified)
3370  tmfd->cmax = HeapTupleHeaderGetCmax(oldtup.t_data);
3371  else
3372  tmfd->cmax = InvalidCommandId;
3373  UnlockReleaseBuffer(buffer);
3374  if (have_tuple_lock)
3375  UnlockTupleTuplock(relation, &(oldtup.t_self), *lockmode);
3376  if (vmbuffer != InvalidBuffer)
3377  ReleaseBuffer(vmbuffer);
3378  *update_indexes = TU_None;
3379 
3380  bms_free(hot_attrs);
3381  bms_free(sum_attrs);
3382  bms_free(key_attrs);
3383  bms_free(id_attrs);
3384  bms_free(modified_attrs);
3385  bms_free(interesting_attrs);
3386  return result;
3387  }
3388 
3389  /*
3390  * If we didn't pin the visibility map page and the page has become all
3391  * visible while we were busy locking the buffer, or during some
3392  * subsequent window during which we had it unlocked, we'll have to unlock
3393  * and re-lock, to avoid holding the buffer lock across an I/O. That's a
3394  * bit unfortunate, especially since we'll now have to recheck whether the
3395  * tuple has been locked or updated under us, but hopefully it won't
3396  * happen very often.
3397  */
3398  if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
3399  {
3400  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
3401  visibilitymap_pin(relation, block, &vmbuffer);
3403  goto l2;
3404  }
3405 
3406  /* Fill in transaction status data */
3407 
3408  /*
3409  * If the tuple we're updating is locked, we need to preserve the locking
3410  * info in the old tuple's Xmax. Prepare a new Xmax value for this.
3411  */
3413  oldtup.t_data->t_infomask,
3414  oldtup.t_data->t_infomask2,
3415  xid, *lockmode, true,
3416  &xmax_old_tuple, &infomask_old_tuple,
3417  &infomask2_old_tuple);
3418 
3419  /*
3420  * And also prepare an Xmax value for the new copy of the tuple. If there
3421  * was no xmax previously, or there was one but all lockers are now gone,
3422  * then use InvalidTransactionId; otherwise, get the xmax from the old
3423  * tuple. (In rare cases that might also be InvalidTransactionId and yet
3424  * not have the HEAP_XMAX_INVALID bit set; that's fine.)
3425  */
3426  if ((oldtup.t_data->t_infomask & HEAP_XMAX_INVALID) ||
3428  (checked_lockers && !locker_remains))
3429  xmax_new_tuple = InvalidTransactionId;
3430  else
3431  xmax_new_tuple = HeapTupleHeaderGetRawXmax(oldtup.t_data);
3432 
3433  if (!TransactionIdIsValid(xmax_new_tuple))
3434  {
3435  infomask_new_tuple = HEAP_XMAX_INVALID;
3436  infomask2_new_tuple = 0;
3437  }
3438  else
3439  {
3440  /*
3441  * If we found a valid Xmax for the new tuple, then the infomask bits
3442  * to use on the new tuple depend on what was there on the old one.
3443  * Note that since we're doing an update, the only possibility is that
3444  * the lockers had FOR KEY SHARE lock.
3445  */
3446  if (oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI)
3447  {
3448  GetMultiXactIdHintBits(xmax_new_tuple, &infomask_new_tuple,
3449  &infomask2_new_tuple);
3450  }
3451  else
3452  {
3453  infomask_new_tuple = HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_LOCK_ONLY;
3454  infomask2_new_tuple = 0;
3455  }
3456  }
3457 
3458  /*
3459  * Prepare the new tuple with the appropriate initial values of Xmin and
3460  * Xmax, as well as initial infomask bits as computed above.
3461  */
3462  newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
3463  newtup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
3464  HeapTupleHeaderSetXmin(newtup->t_data, xid);
3465  HeapTupleHeaderSetCmin(newtup->t_data, cid);
3466  newtup->t_data->t_infomask |= HEAP_UPDATED | infomask_new_tuple;
3467  newtup->t_data->t_infomask2 |= infomask2_new_tuple;
3468  HeapTupleHeaderSetXmax(newtup->t_data, xmax_new_tuple);
3469 
3470  /*
3471  * Replace cid with a combo CID if necessary. Note that we already put
3472  * the plain cid into the new tuple.
3473  */
3474  HeapTupleHeaderAdjustCmax(oldtup.t_data, &cid, &iscombo);
3475 
3476  /*
3477  * If the toaster needs to be activated, OR if the new tuple will not fit
3478  * on the same page as the old, then we need to release the content lock
3479  * (but not the pin!) on the old tuple's buffer while we are off doing
3480  * TOAST and/or table-file-extension work. We must mark the old tuple to
3481  * show that it's locked, else other processes may try to update it
3482  * themselves.
3483  *
3484  * We need to invoke the toaster if there are already any out-of-line
3485  * toasted values present, or if the new tuple is over-threshold.
3486  */
3487  if (relation->rd_rel->relkind != RELKIND_RELATION &&
3488  relation->rd_rel->relkind != RELKIND_MATVIEW)
3489  {
3490  /* toast table entries should never be recursively toasted */
3491  Assert(!HeapTupleHasExternal(&oldtup));
3492  Assert(!HeapTupleHasExternal(newtup));
3493  need_toast = false;
3494  }
3495  else
3496  need_toast = (HeapTupleHasExternal(&oldtup) ||
3497  HeapTupleHasExternal(newtup) ||
3498  newtup->t_len > TOAST_TUPLE_THRESHOLD);
3499 
3500  pagefree = PageGetHeapFreeSpace(page);
3501 
3502  newtupsize = MAXALIGN(newtup->t_len);
3503 
3504  if (need_toast || newtupsize > pagefree)
3505  {
3506  TransactionId xmax_lock_old_tuple;
3507  uint16 infomask_lock_old_tuple,
3508  infomask2_lock_old_tuple;
3509  bool cleared_all_frozen = false;
3510 
3511  /*
3512  * To prevent concurrent sessions from updating the tuple, we have to
3513  * temporarily mark it locked, while we release the page-level lock.
3514  *
3515  * To satisfy the rule that any xid potentially appearing in a buffer
3516  * written out to disk, we unfortunately have to WAL log this
3517  * temporary modification. We can reuse xl_heap_lock for this
3518  * purpose. If we crash/error before following through with the
3519  * actual update, xmax will be of an aborted transaction, allowing
3520  * other sessions to proceed.
3521  */
3522 
3523  /*
3524  * Compute xmax / infomask appropriate for locking the tuple. This has
3525  * to be done separately from the combo that's going to be used for
3526  * updating, because the potentially created multixact would otherwise
3527  * be wrong.
3528  */
3530  oldtup.t_data->t_infomask,
3531  oldtup.t_data->t_infomask2,
3532  xid, *lockmode, false,
3533  &xmax_lock_old_tuple, &infomask_lock_old_tuple,
3534  &infomask2_lock_old_tuple);
3535 
3536  Assert(HEAP_XMAX_IS_LOCKED_ONLY(infomask_lock_old_tuple));
3537 
3539 
3540  /* Clear obsolete visibility flags ... */
3541  oldtup.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
3542  oldtup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
3543  HeapTupleClearHotUpdated(&oldtup);
3544  /* ... and store info about transaction updating this tuple */
3545  Assert(TransactionIdIsValid(xmax_lock_old_tuple));
3546  HeapTupleHeaderSetXmax(oldtup.t_data, xmax_lock_old_tuple);
3547  oldtup.t_data->t_infomask |= infomask_lock_old_tuple;
3548  oldtup.t_data->t_infomask2 |= infomask2_lock_old_tuple;
3549  HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
3550 
3551  /* temporarily make it look not-updated, but locked */
3552  oldtup.t_data->t_ctid = oldtup.t_self;
3553 
3554  /*
3555  * Clear all-frozen bit on visibility map if needed. We could
3556  * immediately reset ALL_VISIBLE, but given that the WAL logging
3557  * overhead would be unchanged, that doesn't seem necessarily
3558  * worthwhile.
3559  */
3560  if (PageIsAllVisible(page) &&
3561  visibilitymap_clear(relation, block, vmbuffer,
3563  cleared_all_frozen = true;
3564 
3565  MarkBufferDirty(buffer);
3566 
3567  if (RelationNeedsWAL(relation))
3568  {
3569  xl_heap_lock xlrec;
3570  XLogRecPtr recptr;
3571 
3572  XLogBeginInsert();
3573  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
3574 
3575  xlrec.offnum = ItemPointerGetOffsetNumber(&oldtup.t_self);
3576  xlrec.xmax = xmax_lock_old_tuple;
3578  oldtup.t_data->t_infomask2);
3579  xlrec.flags =
3580  cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
3581  XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
3582  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
3583  PageSetLSN(page, recptr);
3584  }
3585 
3586  END_CRIT_SECTION();
3587 
3588  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
3589 
3590  /*
3591  * Let the toaster do its thing, if needed.
3592  *
3593  * Note: below this point, heaptup is the data we actually intend to
3594  * store into the relation; newtup is the caller's original untoasted
3595  * data.
3596  */
3597  if (need_toast)
3598  {
3599  /* Note we always use WAL and FSM during updates */
3600  heaptup = heap_toast_insert_or_update(relation, newtup, &oldtup, 0);
3601  newtupsize = MAXALIGN(heaptup->t_len);
3602  }
3603  else
3604  heaptup = newtup;
3605 
3606  /*
3607  * Now, do we need a new page for the tuple, or not? This is a bit
3608  * tricky since someone else could have added tuples to the page while
3609  * we weren't looking. We have to recheck the available space after
3610  * reacquiring the buffer lock. But don't bother to do that if the
3611  * former amount of free space is still not enough; it's unlikely
3612  * there's more free now than before.
3613  *
3614  * What's more, if we need to get a new page, we will need to acquire
3615  * buffer locks on both old and new pages. To avoid deadlock against
3616  * some other backend trying to get the same two locks in the other
3617  * order, we must be consistent about the order we get the locks in.
3618  * We use the rule "lock the lower-numbered page of the relation
3619  * first". To implement this, we must do RelationGetBufferForTuple
3620  * while not holding the lock on the old page, and we must rely on it
3621  * to get the locks on both pages in the correct order.
3622  *
3623  * Another consideration is that we need visibility map page pin(s) if
3624  * we will have to clear the all-visible flag on either page. If we
3625  * call RelationGetBufferForTuple, we rely on it to acquire any such
3626  * pins; but if we don't, we have to handle that here. Hence we need
3627  * a loop.
3628  */
3629  for (;;)
3630  {
3631  if (newtupsize > pagefree)
3632  {
3633  /* It doesn't fit, must use RelationGetBufferForTuple. */
3634  newbuf = RelationGetBufferForTuple(relation, heaptup->t_len,
3635  buffer, 0, NULL,
3636  &vmbuffer_new, &vmbuffer,
3637  0);
3638  /* We're all done. */
3639  break;
3640  }
3641  /* Acquire VM page pin if needed and we don't have it. */
3642  if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
3643  visibilitymap_pin(relation, block, &vmbuffer);
3644  /* Re-acquire the lock on the old tuple's page. */
3646  /* Re-check using the up-to-date free space */
3647  pagefree = PageGetHeapFreeSpace(page);
3648  if (newtupsize > pagefree ||
3649  (vmbuffer == InvalidBuffer && PageIsAllVisible(page)))
3650  {
3651  /*
3652  * Rats, it doesn't fit anymore, or somebody just now set the
3653  * all-visible flag. We must now unlock and loop to avoid
3654  * deadlock. Fortunately, this path should seldom be taken.
3655  */
3656  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
3657  }
3658  else
3659  {
3660  /* We're all done. */
3661  newbuf = buffer;
3662  break;
3663  }
3664  }
3665  }
3666  else
3667  {
3668  /* No TOAST work needed, and it'll fit on same page */
3669  newbuf = buffer;
3670  heaptup = newtup;
3671  }
3672 
3673  /*
3674  * We're about to do the actual update -- check for conflict first, to
3675  * avoid possibly having to roll back work we've just done.
3676  *
3677  * This is safe without a recheck as long as there is no possibility of
3678  * another process scanning the pages between this check and the update
3679  * being visible to the scan (i.e., exclusive buffer content lock(s) are
3680  * continuously held from this point until the tuple update is visible).
3681  *
3682  * For the new tuple the only check needed is at the relation level, but
3683  * since both tuples are in the same relation and the check for oldtup
3684  * will include checking the relation level, there is no benefit to a
3685  * separate check for the new tuple.
3686  */
3687  CheckForSerializableConflictIn(relation, &oldtup.t_self,
3688  BufferGetBlockNumber(buffer));
3689 
3690  /*
3691  * At this point newbuf and buffer are both pinned and locked, and newbuf
3692  * has enough space for the new tuple. If they are the same buffer, only
3693  * one pin is held.
3694  */
3695 
3696  if (newbuf == buffer)
3697  {
3698  /*
3699  * Since the new tuple is going into the same page, we might be able
3700  * to do a HOT update. Check if any of the index columns have been
3701  * changed.
3702  */
3703  if (!bms_overlap(modified_attrs, hot_attrs))
3704  {
3705  use_hot_update = true;
3706 
3707  /*
3708  * If none of the columns that are used in hot-blocking indexes
3709  * were updated, we can apply HOT, but we do still need to check
3710  * if we need to update the summarizing indexes, and update those
3711  * indexes if the columns were updated, or we may fail to detect
3712  * e.g. value bound changes in BRIN minmax indexes.
3713  */
3714  if (bms_overlap(modified_attrs, sum_attrs))
3715  summarized_update = true;
3716  }
3717  }
3718  else
3719  {
3720  /* Set a hint that the old page could use prune/defrag */
3721  PageSetFull(page);
3722  }
3723 
3724  /*
3725  * Compute replica identity tuple before entering the critical section so
3726  * we don't PANIC upon a memory allocation failure.
3727  * ExtractReplicaIdentity() will return NULL if nothing needs to be
3728  * logged. Pass old key required as true only if the replica identity key
3729  * columns are modified or it has external data.
3730  */
3731  old_key_tuple = ExtractReplicaIdentity(relation, &oldtup,
3732  bms_overlap(modified_attrs, id_attrs) ||
3733  id_has_external,
3734  &old_key_copied);
3735 
3736  /* NO EREPORT(ERROR) from here till changes are logged */
3738 
3739  /*
3740  * If this transaction commits, the old tuple will become DEAD sooner or
3741  * later. Set flag that this page is a candidate for pruning once our xid
3742  * falls below the OldestXmin horizon. If the transaction finally aborts,
3743  * the subsequent page pruning will be a no-op and the hint will be
3744  * cleared.
3745  *
3746  * XXX Should we set hint on newbuf as well? If the transaction aborts,
3747  * there would be a prunable tuple in the newbuf; but for now we choose
3748  * not to optimize for aborts. Note that heap_xlog_update must be kept in
3749  * sync if this decision changes.
3750  */
3751  PageSetPrunable(page, xid);
3752 
3753  if (use_hot_update)
3754  {
3755  /* Mark the old tuple as HOT-updated */
3756  HeapTupleSetHotUpdated(&oldtup);
3757  /* And mark the new tuple as heap-only */
3758  HeapTupleSetHeapOnly(heaptup);
3759  /* Mark the caller's copy too, in case different from heaptup */
3760  HeapTupleSetHeapOnly(newtup);
3761  }
3762  else
3763  {
3764  /* Make sure tuples are correctly marked as not-HOT */
3765  HeapTupleClearHotUpdated(&oldtup);
3766  HeapTupleClearHeapOnly(heaptup);
3767  HeapTupleClearHeapOnly(newtup);
3768  }
3769 
3770  RelationPutHeapTuple(relation, newbuf, heaptup, false); /* insert new tuple */
3771 
3772 
3773  /* Clear obsolete visibility flags, possibly set by ourselves above... */
3774  oldtup.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
3775  oldtup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
3776  /* ... and store info about transaction updating this tuple */
3777  Assert(TransactionIdIsValid(xmax_old_tuple));
3778  HeapTupleHeaderSetXmax(oldtup.t_data, xmax_old_tuple);
3779  oldtup.t_data->t_infomask |= infomask_old_tuple;
3780  oldtup.t_data->t_infomask2 |= infomask2_old_tuple;
3781  HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
3782 
3783  /* record address of new tuple in t_ctid of old one */
3784  oldtup.t_data->t_ctid = heaptup->t_self;
3785 
3786  /* clear PD_ALL_VISIBLE flags, reset all visibilitymap bits */
3787  if (PageIsAllVisible(BufferGetPage(buffer)))
3788  {
3789  all_visible_cleared = true;
3791  visibilitymap_clear(relation, BufferGetBlockNumber(buffer),
3792  vmbuffer, VISIBILITYMAP_VALID_BITS);
3793  }
3794  if (newbuf != buffer && PageIsAllVisible(BufferGetPage(newbuf)))
3795  {
3796  all_visible_cleared_new = true;
3798  visibilitymap_clear(relation, BufferGetBlockNumber(newbuf),
3799  vmbuffer_new, VISIBILITYMAP_VALID_BITS);
3800  }
3801 
3802  if (newbuf != buffer)
3803  MarkBufferDirty(newbuf);
3804  MarkBufferDirty(buffer);
3805 
3806  /* XLOG stuff */
3807  if (RelationNeedsWAL(relation))
3808  {
3809  XLogRecPtr recptr;
3810 
3811  /*
3812  * For logical decoding we need combo CIDs to properly decode the
3813  * catalog.
3814  */
3816  {
3817  log_heap_new_cid(relation, &oldtup);
3818  log_heap_new_cid(relation, heaptup);
3819  }
3820 
3821  recptr = log_heap_update(relation, buffer,
3822  newbuf, &oldtup, heaptup,
3823  old_key_tuple,
3824  all_visible_cleared,
3825  all_visible_cleared_new);
3826  if (newbuf != buffer)
3827  {
3828  PageSetLSN(BufferGetPage(newbuf), recptr);
3829  }
3830  PageSetLSN(BufferGetPage(buffer), recptr);
3831  }
3832 
3833  END_CRIT_SECTION();
3834 
3835  if (newbuf != buffer)
3836  LockBuffer(newbuf, BUFFER_LOCK_UNLOCK);
3837  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
3838 
3839  /*
3840  * Mark old tuple for invalidation from system caches at next command
3841  * boundary, and mark the new tuple for invalidation in case we abort. We
3842  * have to do this before releasing the buffer because oldtup is in the
3843  * buffer. (heaptup is all in local memory, but it's necessary to process
3844  * both tuple versions in one call to inval.c so we can avoid redundant
3845  * sinval messages.)
3846  */
3847  CacheInvalidateHeapTuple(relation, &oldtup, heaptup);
3848 
3849  /* Now we can release the buffer(s) */
3850  if (newbuf != buffer)
3851  ReleaseBuffer(newbuf);
3852  ReleaseBuffer(buffer);
3853  if (BufferIsValid(vmbuffer_new))
3854  ReleaseBuffer(vmbuffer_new);
3855  if (BufferIsValid(vmbuffer))
3856  ReleaseBuffer(vmbuffer);
3857 
3858  /*
3859  * Release the lmgr tuple lock, if we had it.
3860  */
3861  if (have_tuple_lock)
3862  UnlockTupleTuplock(relation, &(oldtup.t_self), *lockmode);
3863 
3864  pgstat_count_heap_update(relation, use_hot_update, newbuf != buffer);
3865 
3866  /*
3867  * If heaptup is a private copy, release it. Don't forget to copy t_self
3868  * back to the caller's image, too.
3869  */
3870  if (heaptup != newtup)
3871  {
3872  newtup->t_self = heaptup->t_self;
3873  heap_freetuple(heaptup);
3874  }
3875 
3876  /*
3877  * If it is a HOT update, the update may still need to update summarized
3878  * indexes, lest we fail to update those summaries and get incorrect
3879  * results (for example, minmax bounds of the block may change with this
3880  * update).
3881  */
3882  if (use_hot_update)
3883  {
3884  if (summarized_update)
3885  *update_indexes = TU_Summarizing;
3886  else
3887  *update_indexes = TU_None;
3888  }
3889  else
3890  *update_indexes = TU_All;
3891 
3892  if (old_key_tuple != NULL && old_key_copied)
3893  heap_freetuple(old_key_tuple);
3894 
3895  bms_free(hot_attrs);
3896  bms_free(sum_attrs);
3897  bms_free(key_attrs);
3898  bms_free(id_attrs);
3899  bms_free(modified_attrs);
3900  bms_free(interesting_attrs);
3901 
3902  return TM_Ok;
3903 }
void bms_free(Bitmapset *a)
Definition: bitmapset.c:239
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:917
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:582
static void PageSetFull(Page page)
Definition: bufpage.h:415
TransactionId HeapTupleGetUpdateXid(HeapTupleHeader tuple)
Definition: heapam.c:7106
static Bitmapset * HeapDetermineColumnsInfo(Relation relation, Bitmapset *interesting_cols, Bitmapset *external_cols, HeapTuple oldtup, HeapTuple newtup, bool *has_external)
Definition: heapam.c:3961
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf, Buffer newbuf, HeapTuple oldtup, HeapTuple newtup, HeapTuple old_key_tuple, bool all_visible_cleared, bool new_all_visible_cleared)
Definition: heapam.c:8364
HeapTuple heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, int options)
Definition: heaptoast.c:96
#define TOAST_TUPLE_THRESHOLD
Definition: heaptoast.h:48
#define HeapTupleSetHotUpdated(tuple)
Definition: htup_details.h:677
#define HEAP2_XACT_MASK
Definition: htup_details.h:279
#define HEAP_XMAX_LOCK_ONLY
Definition: htup_details.h:197
#define HeapTupleHeaderSetCmin(tup, cid)
Definition: htup_details.h:393
#define HEAP_XACT_MASK
Definition: htup_details.h:215
#define HeapTupleSetHeapOnly(tuple)
Definition: htup_details.h:686
#define HeapTupleClearHeapOnly(tuple)
Definition: htup_details.h:689
#define HEAP_UPDATED
Definition: htup_details.h:210
#define HEAP_XMAX_KEYSHR_LOCK
Definition: htup_details.h:194
#define HeapTupleClearHotUpdated(tuple)
Definition: htup_details.h:680
@ XLTW_Update
Definition: lmgr.h:27
void pgstat_count_heap_update(Relation rel, bool hot, bool newpage)
Bitmapset * RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
Definition: relcache.c:5220
@ INDEX_ATTR_BITMAP_KEY
Definition: relcache.h:61
@ INDEX_ATTR_BITMAP_HOT_BLOCKING
Definition: relcache.h:64
@ INDEX_ATTR_BITMAP_SUMMARIZED
Definition: relcache.h:65
@ INDEX_ATTR_BITMAP_IDENTITY_KEY
Definition: relcache.h:63
@ TU_Summarizing
Definition: tableam.h:118
@ TU_All
Definition: tableam.h:115
@ TU_None
Definition: tableam.h:112
bool TransactionIdDidAbort(TransactionId transactionId)
Definition: transam.c:188

References Assert(), bms_add_members(), bms_free(), bms_overlap(), BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage(), BufferIsValid(), CacheInvalidateHeapTuple(), CheckForSerializableConflictIn(), TM_FailureData::cmax, compute_infobits(), compute_new_xmax_infomask(), TM_FailureData::ctid, DoesMultiXactIdConflict(), END_CRIT_SECTION, ereport, errcode(), errmsg(), ERROR, ExtractReplicaIdentity(), xl_heap_lock::flags, GetCurrentTransactionId(), GetMultiXactIdHintBits(), HEAP2_XACT_MASK, heap_acquire_tuplock(), heap_freetuple(), HEAP_KEYS_UPDATED, HEAP_LOCKED_UPGRADED, HEAP_MOVED, heap_toast_insert_or_update(), HEAP_UPDATED, HEAP_XACT_MASK, HEAP_XMAX_BITS, HEAP_XMAX_INVALID, HEAP_XMAX_IS_KEYSHR_LOCKED, HEAP_XMAX_IS_LOCKED_ONLY, HEAP_XMAX_IS_MULTI, HEAP_XMAX_KEYSHR_LOCK, HEAP_XMAX_LOCK_ONLY, HeapDetermineColumnsInfo(), HeapTupleClearHeapOnly, HeapTupleClearHotUpdated, HeapTupleGetUpdateXid(), HeapTupleHasExternal, HeapTupleHeaderAdjustCmax(), HeapTupleHeaderGetCmax(), HeapTupleHeaderGetNatts, HeapTupleHeaderGetRawXmax, HeapTupleHeaderGetUpdateXid, HeapTupleHeaderSetCmax, HeapTupleHeaderSetCmin, HeapTupleHeaderSetXmax, HeapTupleHeaderSetXmin, HeapTupleSatisfiesUpdate(), HeapTupleSatisfiesVisibility(), HeapTupleSetHeapOnly, HeapTupleSetHotUpdated, INDEX_ATTR_BITMAP_HOT_BLOCKING, INDEX_ATTR_BITMAP_IDENTITY_KEY, INDEX_ATTR_BITMAP_KEY, INDEX_ATTR_BITMAP_SUMMARIZED, xl_heap_lock::infobits_set, InvalidBuffer, InvalidCommandId, InvalidSnapshot, InvalidTransactionId, IsInParallelMode(), ItemIdGetLength, ItemIdIsNormal, ItemPointerEquals(), ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), ItemPointerIsValid(), LockBuffer(), LockTupleExclusive, LockTupleNoKeyExclusive, LockWaitBlock, log_heap_new_cid(), log_heap_update(), MarkBufferDirty(), MAXALIGN, MultiXactIdSetOldestMember(), MultiXactIdWait(), MultiXactStatusNoKeyUpdate, MultiXactStatusUpdate, xl_heap_lock::offnum, PageClearAllVisible(), PageGetHeapFreeSpace(), PageGetItem(), PageGetItemId(), PageIsAllVisible(), PageSetFull(), PageSetLSN(), PageSetPrunable, pgstat_count_heap_update(), RelationData::rd_rel, ReadBuffer(), REGBUF_STANDARD, RelationGetBufferForTuple(), RelationGetIndexAttrBitmap(), RelationGetNumberOfAttributes, RelationGetRelid, RelationIsAccessibleInLogicalDecoding, RelationNeedsWAL, RelationPutHeapTuple(), ReleaseBuffer(), SizeOfHeapLock, START_CRIT_SECTION, HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleHeaderData::t_infomask, HeapTupleHeaderData::t_infomask2, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TM_BeingModified, TM_Deleted, TM_Invisible, TM_Ok, TM_SelfModified, TM_Updated, TOAST_TUPLE_THRESHOLD, TransactionIdDidAbort(), TransactionIdEquals, TransactionIdIsCurrentTransactionId(), TransactionIdIsValid, TU_All, TU_None, TU_Summarizing, UnlockReleaseBuffer(), UnlockTupleTuplock, UpdateXmaxHintBits(), VISIBILITYMAP_ALL_FROZEN, visibilitymap_clear(), visibilitymap_pin(), VISIBILITYMAP_VALID_BITS, XactLockTableWait(), XLH_LOCK_ALL_FROZEN_CLEARED, XLOG_HEAP_LOCK, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), XLogRegisterData(), XLTW_Update, xl_heap_lock::xmax, TM_FailureData::xmax, and xmax_infomask_changed().

Referenced by heapam_tuple_update(), and simple_heap_update().

◆ heap_vacuum_rel()

void heap_vacuum_rel ( Relation  rel,
struct VacuumParams params,
BufferAccessStrategy  bstrategy 
)

Definition at line 285 of file vacuumlazy.c.

287 {
288  LVRelState *vacrel;
289  bool verbose,
290  instrument,
291  skipwithvm,
292  frozenxid_updated,
293  minmulti_updated;
294  BlockNumber orig_rel_pages,
295  new_rel_pages,
296  new_rel_allvisible;
297  PGRUsage ru0;
298  TimestampTz starttime = 0;
299  PgStat_Counter startreadtime = 0,
300  startwritetime = 0;
301  WalUsage startwalusage = pgWalUsage;
302  int64 StartPageHit = VacuumPageHit,
303  StartPageMiss = VacuumPageMiss,
304  StartPageDirty = VacuumPageDirty;
305  ErrorContextCallback errcallback;
306  char **indnames = NULL;
307 
308  verbose = (params->options & VACOPT_VERBOSE) != 0;
309  instrument = (verbose || (AmAutoVacuumWorkerProcess() &&
310  params->log_min_duration >= 0));
311  if (instrument)
312  {
313  pg_rusage_init(&ru0);
314  starttime = GetCurrentTimestamp();
315  if (track_io_timing)
316  {
317  startreadtime = pgStatBlockReadTime;
318  startwritetime = pgStatBlockWriteTime;
319  }
320  }
321 
323  RelationGetRelid(rel));
324 
325  /*
326  * Setup error traceback support for ereport() first. The idea is to set
327  * up an error context callback to display additional information on any
328  * error during a vacuum. During different phases of vacuum, we update
329  * the state so that the error context callback always display current
330  * information.
331  *
332  * Copy the names of heap rel into local memory for error reporting
333  * purposes, too. It isn't always safe to assume that we can get the name
334  * of each rel. It's convenient for code in lazy_scan_heap to always use
335  * these temp copies.
336  */
337  vacrel = (LVRelState *) palloc0(sizeof(LVRelState));
340  vacrel->relname = pstrdup(RelationGetRelationName(rel));
341  vacrel->indname = NULL;
343  vacrel->verbose = verbose;
344  errcallback.callback = vacuum_error_callback;
345  errcallback.arg = vacrel;
346  errcallback.previous = error_context_stack;
347  error_context_stack = &errcallback;
348 
349  /* Set up high level stuff about rel and its indexes */
350  vacrel->rel = rel;
351  vac_open_indexes(vacrel->rel, RowExclusiveLock, &vacrel->nindexes,
352  &vacrel->indrels);
353  vacrel->bstrategy = bstrategy;
354  if (instrument && vacrel->nindexes > 0)
355  {
356  /* Copy index names used by instrumentation (not error reporting) */
357  indnames = palloc(sizeof(char *) * vacrel->nindexes);
358  for (int i = 0; i < vacrel->nindexes; i++)
359  indnames[i] = pstrdup(RelationGetRelationName(vacrel->indrels[i]));
360  }
361 
362  /*
363  * The index_cleanup param either disables index vacuuming and cleanup or
364  * forces it to go ahead when we would otherwise apply the index bypass
365  * optimization. The default is 'auto', which leaves the final decision
366  * up to lazy_vacuum().
367  *
368  * The truncate param allows user to avoid attempting relation truncation,
369  * though it can't force truncation to happen.
370  */
373  params->truncate != VACOPTVALUE_AUTO);
374 
375  /*
376  * While VacuumFailSafeActive is reset to false before calling this, we
377  * still need to reset it here due to recursive calls.
378  */
379  VacuumFailsafeActive = false;
380  vacrel->consider_bypass_optimization = true;
381  vacrel->do_index_vacuuming = true;
382  vacrel->do_index_cleanup = true;
383  vacrel->do_rel_truncate = (params->truncate != VACOPTVALUE_DISABLED);
384  if (params->index_cleanup == VACOPTVALUE_DISABLED)
385  {
386  /* Force disable index vacuuming up-front */
387  vacrel->do_index_vacuuming = false;
388  vacrel->do_index_cleanup = false;
389  }
390  else if (params->index_cleanup == VACOPTVALUE_ENABLED)
391  {
392  /* Force index vacuuming. Note that failsafe can still bypass. */
393  vacrel->consider_bypass_optimization = false;
394  }
395  else
396  {
397  /* Default/auto, make all decisions dynamically */
399  }
400 
401  /* Initialize page counters explicitly (be tidy) */
402  vacrel->scanned_pages = 0;
403  vacrel->removed_pages = 0;
404  vacrel->frozen_pages = 0;
405  vacrel->lpdead_item_pages = 0;
406  vacrel->missed_dead_pages = 0;
407  vacrel->nonempty_pages = 0;
408  /* dead_items_alloc allocates vacrel->dead_items later on */
409 
410  /* Allocate/initialize output statistics state */
411  vacrel->new_rel_tuples = 0;
412  vacrel->new_live_tuples = 0;
413  vacrel->indstats = (IndexBulkDeleteResult **)
414  palloc0(vacrel->nindexes * sizeof(IndexBulkDeleteResult *));
415 
416  /* Initialize remaining counters (be tidy) */
417  vacrel->num_index_scans = 0;
418  vacrel->tuples_deleted = 0;
419  vacrel->tuples_frozen = 0;
420  vacrel->lpdead_items = 0;
421  vacrel->live_tuples = 0;
422  vacrel->recently_dead_tuples = 0;
423  vacrel->missed_dead_tuples = 0;
424 
425  /*
426  * Get cutoffs that determine which deleted tuples are considered DEAD,
427  * not just RECENTLY_DEAD, and which XIDs/MXIDs to freeze. Then determine
428  * the extent of the blocks that we'll scan in lazy_scan_heap. It has to
429  * happen in this order to ensure that the OldestXmin cutoff field works
430  * as an upper bound on the XIDs stored in the pages we'll actually scan
431  * (NewRelfrozenXid tracking must never be allowed to miss unfrozen XIDs).
432  *
433  * Next acquire vistest, a related cutoff that's used in heap_page_prune.
434  * We expect vistest will always make heap_page_prune remove any deleted
435  * tuple whose xmax is < OldestXmin. lazy_scan_prune must never become
436  * confused about whether a tuple should be frozen or removed. (In the
437  * future we might want to teach lazy_scan_prune to recompute vistest from
438  * time to time, to increase the number of dead tuples it can prune away.)
439  */
440  vacrel->aggressive = vacuum_get_cutoffs(rel, params, &vacrel->cutoffs);
441  vacrel->rel_pages = orig_rel_pages = RelationGetNumberOfBlocks(rel);
442  vacrel->vistest = GlobalVisTestFor(rel);
443  /* Initialize state used to track oldest extant XID/MXID */
444  vacrel->NewRelfrozenXid = vacrel->cutoffs.OldestXmin;
445  vacrel->NewRelminMxid = vacrel->cutoffs.OldestMxact;
446  vacrel->skippedallvis = false;
447  skipwithvm = true;
449  {
450  /*
451  * Force aggressive mode, and disable skipping blocks using the
452  * visibility map (even those set all-frozen)
453  */
454  vacrel->aggressive = true;
455  skipwithvm = false;
456  }
457 
458  vacrel->skipwithvm = skipwithvm;
459 
460  if (verbose)
461  {
462  if (vacrel->aggressive)
463  ereport(INFO,
464  (errmsg("aggressively vacuuming \"%s.%s.%s\"",
465  vacrel->dbname, vacrel->relnamespace,
466  vacrel->relname)));
467  else
468  ereport(INFO,
469  (errmsg("vacuuming \"%s.%s.%s\"",
470  vacrel->dbname, vacrel->relnamespace,
471  vacrel->relname)));
472  }
473 
474  /*
475  * Allocate dead_items array memory using dead_items_alloc. This handles
476  * parallel VACUUM initialization as part of allocating shared memory
477  * space used for dead_items. (But do a failsafe precheck first, to
478  * ensure that parallel VACUUM won't be attempted at all when relfrozenxid
479  * is already dangerously old.)
480  */
482  dead_items_alloc(vacrel, params->nworkers);
483 
484  /*
485  * Call lazy_scan_heap to perform all required heap pruning, index
486  * vacuuming, and heap vacuuming (plus related processing)
487  */
488  lazy_scan_heap(vacrel);
489 
490  /*
491  * Free resources managed by dead_items_alloc. This ends parallel mode in
492  * passing when necessary.
493  */
494  dead_items_cleanup(vacrel);
496 
497  /*
498  * Update pg_class entries for each of rel's indexes where appropriate.
499  *
500  * Unlike the later update to rel's pg_class entry, this is not critical.
501  * Maintains relpages/reltuples statistics used by the planner only.
502  */
503  if (vacrel->do_index_cleanup)
505 
506  /* Done with rel's indexes */
507  vac_close_indexes(vacrel->nindexes, vacrel->indrels, NoLock);
508 
509  /* Optionally truncate rel */
510  if (should_attempt_truncation(vacrel))
511  lazy_truncate_heap(vacrel);
512 
513  /* Pop the error context stack */
514  error_context_stack = errcallback.previous;
515 
516  /* Report that we are now doing final cleanup */
519 
520  /*
521  * Prepare to update rel's pg_class entry.
522  *
523  * Aggressive VACUUMs must always be able to advance relfrozenxid to a
524  * value >= FreezeLimit, and relminmxid to a value >= MultiXactCutoff.
525  * Non-aggressive VACUUMs may advance them by any amount, or not at all.
526  */
527  Assert(vacrel->NewRelfrozenXid == vacrel->cutoffs.OldestXmin ||
529  vacrel->cutoffs.relfrozenxid,
530  vacrel->NewRelfrozenXid));
531  Assert(vacrel->NewRelminMxid == vacrel->cutoffs.OldestMxact ||
533  vacrel->cutoffs.relminmxid,
534  vacrel->NewRelminMxid));
535  if (vacrel->skippedallvis)
536  {
537  /*
538  * Must keep original relfrozenxid in a non-aggressive VACUUM that
539  * chose to skip an all-visible page range. The state that tracks new
540  * values will have missed unfrozen XIDs from the pages we skipped.
541  */
542  Assert(!vacrel->aggressive);
545  }
546 
547  /*
548  * For safety, clamp relallvisible to be not more than what we're setting
549  * pg_class.relpages to
550  */
551  new_rel_pages = vacrel->rel_pages; /* After possible rel truncation */
552  visibilitymap_count(rel, &new_rel_allvisible, NULL);
553  if (new_rel_allvisible > new_rel_pages)
554  new_rel_allvisible = new_rel_pages;
555 
556  /*
557  * Now actually update rel's pg_class entry.
558  *
559  * In principle new_live_tuples could be -1 indicating that we (still)
560  * don't know the tuple count. In practice that can't happen, since we
561  * scan every page that isn't skipped using the visibility map.
562  */
563  vac_update_relstats(rel, new_rel_pages, vacrel->new_live_tuples,
564  new_rel_allvisible, vacrel->nindexes > 0,
565  vacrel->NewRelfrozenXid, vacrel->NewRelminMxid,
566  &frozenxid_updated, &minmulti_updated, false);
567 
568  /*
569  * Report results to the cumulative stats system, too.
570  *
571  * Deliberately avoid telling the stats system about LP_DEAD items that
572  * remain in the table due to VACUUM bypassing index and heap vacuuming.
573  * ANALYZE will consider the remaining LP_DEAD items to be dead "tuples".
574  * It seems like a good idea to err on the side of not vacuuming again too
575  * soon in cases where the failsafe prevented significant amounts of heap
576  * vacuuming.
577  */
579  rel->rd_rel->relisshared,
580  Max(vacrel->new_live_tuples, 0),
581  vacrel->recently_dead_tuples +
582  vacrel->missed_dead_tuples);
584 
585  if (instrument)
586  {
587  TimestampTz endtime = GetCurrentTimestamp();
588 
589  if (verbose || params->log_min_duration == 0 ||
590  TimestampDifferenceExceeds(starttime, endtime,
591  params->log_min_duration))
592  {
593  long secs_dur;
594  int usecs_dur;
595  WalUsage walusage;
597  char *msgfmt;
598  int32 diff;
599  int64 PageHitOp = VacuumPageHit - StartPageHit,
600  PageMissOp = VacuumPageMiss - StartPageMiss,
601  PageDirtyOp = VacuumPageDirty - StartPageDirty;
602  double read_rate = 0,
603  write_rate = 0;
604 
605  TimestampDifference(starttime, endtime, &secs_dur, &usecs_dur);
606  memset(&walusage, 0, sizeof(WalUsage));
607  WalUsageAccumDiff(&walusage, &pgWalUsage, &startwalusage);
608 
610  if (verbose)
611  {
612  /*
613  * Aggressiveness already reported earlier, in dedicated
614  * VACUUM VERBOSE ereport
615  */
616  Assert(!params->is_wraparound);
617  msgfmt = _("finished vacuuming \"%s.%s.%s\": index scans: %d\n");
618  }
619  else if (params->is_wraparound)
620  {
621  /*
622  * While it's possible for a VACUUM to be both is_wraparound
623  * and !aggressive, that's just a corner-case -- is_wraparound
624  * implies aggressive. Produce distinct output for the corner
625  * case all the same, just in case.
626  */
627  if (vacrel->aggressive)
628  msgfmt = _("automatic aggressive vacuum to prevent wraparound of table \"%s.%s.%s\": index scans: %d\n");
629  else
630  msgfmt = _("automatic vacuum to prevent wraparound of table \"%s.%s.%s\": index scans: %d\n");
631  }
632  else
633  {
634  if (vacrel->aggressive)
635  msgfmt = _("automatic aggressive vacuum of table \"%s.%s.%s\": index scans: %d\n");
636  else
637  msgfmt = _("automatic vacuum of table \"%s.%s.%s\": index scans: %d\n");
638  }
639  appendStringInfo(&buf, msgfmt,
640  vacrel->dbname,
641  vacrel->relnamespace,
642  vacrel->relname,
643  vacrel->num_index_scans);
644  appendStringInfo(&buf, _("pages: %u removed, %u remain, %u scanned (%.2f%% of total)\n"),
645  vacrel->removed_pages,
646  new_rel_pages,
647  vacrel->scanned_pages,
648  orig_rel_pages == 0 ? 100.0 :
649  100.0 * vacrel->scanned_pages / orig_rel_pages);
651  _("tuples: %lld removed, %lld remain, %lld are dead but not yet removable\n"),
652  (long long) vacrel->tuples_deleted,
653  (long long) vacrel->new_rel_tuples,
654  (long long) vacrel->recently_dead_tuples);
655  if (vacrel->missed_dead_tuples > 0)
657  _("tuples missed: %lld dead from %u pages not removed due to cleanup lock contention\n"),
658  (long long) vacrel->missed_dead_tuples,
659  vacrel->missed_dead_pages);
660  diff = (int32) (ReadNextTransactionId() -
661  vacrel->cutoffs.OldestXmin);
663  _("removable cutoff: %u, which was %d XIDs old when operation ended\n"),
664  vacrel->cutoffs.OldestXmin, diff);
665  if (frozenxid_updated)
666  {
667  diff = (int32) (vacrel->NewRelfrozenXid -
668  vacrel->cutoffs.relfrozenxid);
670  _("new relfrozenxid: %u, which is %d XIDs ahead of previous value\n"),
671  vacrel->NewRelfrozenXid, diff);
672  }
673  if (minmulti_updated)
674  {
675  diff = (int32) (vacrel->NewRelminMxid -
676  vacrel->cutoffs.relminmxid);
678  _("new relminmxid: %u, which is %d MXIDs ahead of previous value\n"),
679  vacrel->NewRelminMxid, diff);
680  }
681  appendStringInfo(&buf, _("frozen: %u pages from table (%.2f%% of total) had %lld tuples frozen\n"),
682  vacrel->frozen_pages,
683  orig_rel_pages == 0 ? 100.0 :
684  100.0 * vacrel->frozen_pages / orig_rel_pages,
685  (long long) vacrel->tuples_frozen);
686  if (vacrel->do_index_vacuuming)
687  {
688  if (vacrel->nindexes == 0 || vacrel->num_index_scans == 0)
689  appendStringInfoString(&buf, _("index scan not needed: "));
690  else
691  appendStringInfoString(&buf, _("index scan needed: "));
692 
693  msgfmt = _("%u pages from table (%.2f%% of total) had %lld dead item identifiers removed\n");
694  }
695  else
696  {
698  appendStringInfoString(&buf, _("index scan bypassed: "));
699  else
700  appendStringInfoString(&buf, _("index scan bypassed by failsafe: "));
701 
702  msgfmt = _("%u pages from table (%.2f%% of total) have %lld dead item identifiers\n");
703  }
704  appendStringInfo(&buf, msgfmt,
705  vacrel->lpdead_item_pages,
706  orig_rel_pages == 0 ? 100.0 :
707  100.0 * vacrel->lpdead_item_pages / orig_rel_pages,
708  (long long) vacrel->lpdead_items);
709  for (int i = 0; i < vacrel->nindexes; i++)
710  {
711  IndexBulkDeleteResult *istat = vacrel->indstats[i];
712 
713  if (!istat)
714  continue;
715 
717  _("index \"%s\": pages: %u in total, %u newly deleted, %u currently deleted, %u reusable\n"),
718  indnames[i],
719  istat->num_pages,
720  istat->pages_newly_deleted,
721  istat->pages_deleted,
722  istat->pages_free);
723  }
724  if (track_io_timing)
725  {
726  double read_ms = (double) (pgStatBlockReadTime - startreadtime) / 1000;
727  double write_ms = (double) (pgStatBlockWriteTime - startwritetime) / 1000;
728 
729  appendStringInfo(&buf, _("I/O timings: read: %.3f ms, write: %.3f ms\n"),
730  read_ms, write_ms);
731  }
732  if (secs_dur > 0 || usecs_dur > 0)
733  {
734  read_rate = (double) BLCKSZ * PageMissOp / (1024 * 1024) /
735  (secs_dur + usecs_dur / 1000000.0);
736  write_rate = (double) BLCKSZ * PageDirtyOp / (1024 * 1024) /
737  (secs_dur + usecs_dur / 1000000.0);
738  }
739  appendStringInfo(&buf, _("avg read rate: %.3f MB/s, avg write rate: %.3f MB/s\n"),
740  read_rate, write_rate);
742  _("buffer usage: %lld hits, %lld misses, %lld dirtied\n"),
743  (long long) PageHitOp,
744  (long long) PageMissOp,
745  (long long) PageDirtyOp);
747  _("WAL usage: %lld records, %lld full page images, %llu bytes\n"),
748  (long long) walusage.wal_records,
749  (long long) walusage.wal_fpi,
750  (unsigned long long) walusage.wal_bytes);
751  appendStringInfo(&buf, _("system usage: %s"), pg_rusage_show(&ru0));
752 
753  ereport(verbose ? INFO : LOG,
754  (errmsg_internal("%s", buf.data)));
755  pfree(buf.data);
756  }
757  }
758 
759  /* Cleanup index statistics and index names */
760  for (int i = 0; i < vacrel->nindexes; i++)
761  {
762  if (vacrel->indstats[i])
763  pfree(vacrel->indstats[i]);
764 
765  if (instrument)
766  pfree(indnames[i]);
767  }
768 }
void TimestampDifference(TimestampTz start_time, TimestampTz stop_time, long *secs, int *microsecs)
Definition: timestamp.c:1730
bool TimestampDifferenceExceeds(TimestampTz start_time, TimestampTz stop_time, int msec)
Definition: timestamp.c:1790
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1654
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
void pgstat_progress_update_param(int index, int64 val)
void pgstat_progress_end_command(void)
@ PROGRESS_COMMAND_VACUUM
bool track_io_timing
Definition: bufmgr.c:138
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:229
signed int int32
Definition: c.h:481
int64 TimestampTz
Definition: timestamp.h:39
char * get_database_name(Oid dbid)
Definition: dbcommands.c:3153
ErrorContextCallback * error_context_stack
Definition: elog.c:94
#define _(x)
Definition: elog.c:90
#define LOG
Definition: elog.h:31
#define INFO
Definition: elog.h:34
int64 VacuumPageHit
Definition: globals.c:154
int64 VacuumPageMiss
Definition: globals.c:155
int64 VacuumPageDirty
Definition: globals.c:156
Oid MyDatabaseId
Definition: globals.c:91
int verbose
WalUsage pgWalUsage
Definition: instrument.c:22
void WalUsageAccumDiff(WalUsage *dst, const WalUsage *add, const WalUsage *sub)
Definition: instrument.c:286
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3322
char * pstrdup(const char *in)
Definition: mcxt.c:1683
void * palloc0(Size size)
Definition: mcxt.c:1334
#define AmAutoVacuumWorkerProcess()
Definition: miscadmin.h:372
const char * pg_rusage_show(const PGRUsage *ru0)
Definition: pg_rusage.c:40
void pg_rusage_init(PGRUsage *ru0)
Definition: pg_rusage.c:27
int64 PgStat_Counter
Definition: pgstat.h:89
PgStat_Counter pgStatBlockReadTime
PgStat_Counter pgStatBlockWriteTime
void pgstat_report_vacuum(Oid tableoid, bool shared, PgStat_Counter livetuples, PgStat_Counter deadtuples)
#define PROGRESS_VACUUM_PHASE_FINAL_CLEANUP
Definition: progress.h:37
#define PROGRESS_VACUUM_PHASE
Definition: progress.h:21
#define RelationGetNamespace(relation)
Definition: rel.h:546
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:97
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:182
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
struct ErrorContextCallback * previous
Definition: elog.h:295
void(* callback)(void *arg)
Definition: elog.h:296
BlockNumber pages_deleted
Definition: genam.h:82
BlockNumber pages_newly_deleted
Definition: genam.h:81
BlockNumber pages_free
Definition: genam.h:83
BlockNumber num_pages
Definition: genam.h:77
bool verbose
Definition: vacuumlazy.c:174
int nindexes
Definition: vacuumlazy.c:140
int64 tuples_deleted
Definition: vacuumlazy.c:201
BlockNumber nonempty_pages
Definition: vacuumlazy.c:190
bool do_rel_truncate
Definition: vacuumlazy.c:156
BlockNumber scanned_pages
Definition: vacuumlazy.c:185
bool aggressive
Definition: vacuumlazy.c:147
GlobalVisState * vistest
Definition: vacuumlazy.c:160
BlockNumber removed_pages
Definition: vacuumlazy.c:186
int num_index_scans
Definition: vacuumlazy.c:199
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:196
double new_live_tuples
Definition: vacuumlazy.c:194
double new_rel_tuples
Definition: vacuumlazy.c:193
TransactionId NewRelfrozenXid
Definition: vacuumlazy.c:162
Relation rel
Definition: vacuumlazy.c:138
bool consider_bypass_optimization
Definition: vacuumlazy.c:151
BlockNumber rel_pages
Definition: vacuumlazy.c:184
int64 recently_dead_tuples
Definition: vacuumlazy.c:205
int64 tuples_frozen
Definition: vacuumlazy.c:202
BlockNumber frozen_pages
Definition: vacuumlazy.c:187
char * dbname
Definition: vacuumlazy.c:167
BlockNumber missed_dead_pages
Definition: vacuumlazy.c:189
char * relnamespace
Definition: vacuumlazy.c:168
int64 live_tuples
Definition: vacuumlazy.c:204
int64 lpdead_items
Definition: vacuumlazy.c:203
BufferAccessStrategy bstrategy
Definition: vacuumlazy.c:143
bool skippedallvis
Definition: vacuumlazy.c:164
BlockNumber lpdead_item_pages
Definition: vacuumlazy.c:188
Relation * indrels
Definition: vacuumlazy.c:139
bool skipwithvm
Definition: vacuumlazy.c:149
bool do_index_cleanup
Definition: vacuumlazy.c:155
MultiXactId NewRelminMxid
Definition: vacuumlazy.c:163
int64 missed_dead_tuples
Definition: vacuumlazy.c:206
struct VacuumCutoffs cutoffs
Definition: vacuumlazy.c:159
char * relname
Definition: vacuumlazy.c:169
VacErrPhase phase
Definition: vacuumlazy.c:173
char * indname
Definition: vacuumlazy.c:170
bool do_index_vacuuming
Definition: vacuumlazy.c:154
int nworkers
Definition: vacuum.h:238
VacOptValue truncate
Definition: vacuum.h:230
bits32 options
Definition: vacuum.h:218
bool is_wraparound
Definition: vacuum.h:225
int log_min_duration
Definition: vacuum.h:226
VacOptValue index_cleanup
Definition: vacuum.h:229
uint64 wal_bytes
Definition: instrument.h:55
int64 wal_fpi
Definition: instrument.h:54
int64 wal_records
Definition: instrument.h:53
static TransactionId ReadNextTransactionId(void)
Definition: transam.h:315
void vac_open_indexes(Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
Definition: vacuum.c:2273
void vac_update_relstats(Relation relation, BlockNumber num_pages, double num_tuples, BlockNumber num_all_visible_pages, bool hasindex, TransactionId frozenxid, MultiXactId minmulti, bool *frozenxid_updated, bool *minmulti_updated, bool in_outer_xact)
Definition: vacuum.c:1399
void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode)
Definition: vacuum.c:2316
bool vacuum_get_cutoffs(Relation rel, const VacuumParams *params, struct VacuumCutoffs *cutoffs)
Definition: vacuum.c:1073
bool VacuumFailsafeActive
Definition: vacuum.c:96
#define VACOPT_VERBOSE
Definition: vacuum.h:181
@ VACOPTVALUE_AUTO
Definition: vacuum.h:202
@ VACOPTVALUE_ENABLED
Definition: vacuum.h:204
@ VACOPTVALUE_UNSPECIFIED
Definition: vacuum.h:201
@ VACOPTVALUE_DISABLED
Definition: vacuum.h:203
#define VACOPT_DISABLE_PAGE_SKIPPING
Definition: vacuum.h:187
static void dead_items_cleanup(LVRelState *vacrel)
Definition: vacuumlazy.c:3233
static void update_relstats_all_indexes(LVRelState *vacrel)
Definition: vacuumlazy.c:3374
static void vacuum_error_callback(void *arg)
Definition: vacuumlazy.c:3409
static void lazy_truncate_heap(LVRelState *vacrel)
Definition: vacuumlazy.c:2863
static bool should_attempt_truncation(LVRelState *vacrel)
Definition: vacuumlazy.c:2843
@ VACUUM_ERRCB_PHASE_UNKNOWN
Definition: vacuumlazy.c:127
static void lazy_scan_heap(LVRelState *vacrel)
Definition: vacuumlazy.c:807
static bool lazy_check_wraparound_failsafe(LVRelState *vacrel)
Definition: vacuumlazy.c:2614
static void dead_items_alloc(LVRelState *vacrel, int nworkers)
Definition: vacuumlazy.c:3176
void visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_frozen)

References _, LVRelState::aggressive, AmAutoVacuumWorkerProcess, appendStringInfo(), appendStringInfoString(), ErrorContextCallback::arg, Assert(), LVRelState::bstrategy, buf, ErrorContextCallback::callback, LVRelState::consider_bypass_optimization, LVRelState::cutoffs, LVRelState::dbname, dead_items_alloc(), dead_items_cleanup(), LVRelState::do_index_cleanup, LVRelState::do_index_vacuuming, LVRelState::do_rel_truncate, ereport, errmsg(), errmsg_internal(), error_context_stack, VacuumCutoffs::FreezeLimit, LVRelState::frozen_pages, get_database_name(), get_namespace_name(), GetCurrentTimestamp(), GlobalVisTestFor(), i, VacuumParams::index_cleanup, LVRelState::indname, LVRelState::indrels, LVRelState::indstats, INFO, initStringInfo(), InvalidMultiXactId, InvalidTransactionId, VacuumParams::is_wraparound, IsInParallelMode(), lazy_check_wraparound_failsafe(), lazy_scan_heap(), lazy_truncate_heap(), LVRelState::live_tuples, LOG, VacuumParams::log_min_duration, LVRelState::lpdead_item_pages, LVRelState::lpdead_items, Max, LVRelState::missed_dead_pages, LVRelState::missed_dead_tuples, VacuumCutoffs::MultiXactCutoff, MultiXactIdPrecedesOrEquals(), MyDatabaseId, LVRelState::new_live_tuples, LVRelState::new_rel_tuples, LVRelState::NewRelfrozenXid, LVRelState::NewRelminMxid, LVRelState::nindexes, NoLock, LVRelState::nonempty_pages, LVRelState::num_index_scans, IndexBulkDeleteResult::num_pages, VacuumParams::nworkers, VacuumCutoffs::OldestMxact, VacuumCutoffs::OldestXmin, VacuumParams::options, IndexBulkDeleteResult::pages_deleted, IndexBulkDeleteResult::pages_free, IndexBulkDeleteResult::pages_newly_deleted, palloc(), palloc0(), pfree(), pg_rusage_init(), pg_rusage_show(), pgstat_progress_end_command(), pgstat_progress_start_command(), pgstat_progress_update_param(), pgstat_report_vacuum(), pgStatBlockReadTime, pgStatBlockWriteTime, pgWalUsage, LVRelState::phase, ErrorContextCallback::previous, PROGRESS_COMMAND_VACUUM, PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE_FINAL_CLEANUP, pstrdup(), RelationData::rd_rel, ReadNextTransactionId(), LVRelState::recently_dead_tuples, LVRelState::rel, LVRelState::rel_pages, RelationGetNamespace, RelationGetNumberOfBlocks, RelationGetRelationName, RelationGetRelid, VacuumCutoffs::relfrozenxid, VacuumCutoffs::relminmxid, LVRelState::relname, LVRelState::relnamespace, LVRelState::removed_pages, RowExclusiveLock, LVRelState::scanned_pages, should_attempt_truncation(), LVRelState::skippedallvis, LVRelState::skipwithvm, TimestampDifference(), TimestampDifferenceExceeds(), track_io_timing, TransactionIdPrecedesOrEquals(), VacuumParams::truncate, LVRelState::tuples_deleted, LVRelState::tuples_frozen, update_relstats_all_indexes(), vac_close_indexes(), vac_open_indexes(), vac_update_relstats(), VACOPT_DISABLE_PAGE_SKIPPING, VACOPT_VERBOSE, VACOPTVALUE_AUTO, VACOPTVALUE_DISABLED, VACOPTVALUE_ENABLED, VACOPTVALUE_UNSPECIFIED, VACUUM_ERRCB_PHASE_UNKNOWN, vacuum_error_callback(), vacuum_get_cutoffs(), VacuumFailsafeActive, VacuumPageDirty, VacuumPageHit, VacuumPageMiss, LVRelState::verbose, verbose, visibilitymap_count(), LVRelState::vistest, WalUsage::wal_bytes, WalUsage::wal_fpi, WalUsage::wal_records, and WalUsageAccumDiff().

◆ HeapCheckForSerializableConflictOut()

void HeapCheckForSerializableConflictOut ( bool  visible,
Relation  relation,
HeapTuple  tuple,
Buffer  buffer,
Snapshot  snapshot 
)

Definition at line 10153 of file heapam.c.

10156 {
10157  TransactionId xid;
10158  HTSV_Result htsvResult;
10159 
10160  if (!CheckForSerializableConflictOutNeeded(relation, snapshot))
10161  return;
10162 
10163  /*
10164  * Check to see whether the tuple has been written to by a concurrent
10165  * transaction, either to create it not visible to us, or to delete it
10166  * while it is visible to us. The "visible" bool indicates whether the
10167  * tuple is visible to us, while HeapTupleSatisfiesVacuum checks what else
10168  * is going on with it.
10169  *
10170  * In the event of a concurrently inserted tuple that also happens to have
10171  * been concurrently updated (by a separate transaction), the xmin of the
10172  * tuple will be used -- not the updater's xid.
10173  */
10174  htsvResult = HeapTupleSatisfiesVacuum(tuple, TransactionXmin, buffer);
10175  switch (htsvResult)
10176  {
10177  case HEAPTUPLE_LIVE:
10178  if (visible)
10179  return;
10180  xid = HeapTupleHeaderGetXmin(tuple->t_data);
10181  break;
10184  if (visible)
10185  xid = HeapTupleHeaderGetUpdateXid(tuple->t_data);
10186  else
10187  xid = HeapTupleHeaderGetXmin(tuple->t_data);
10188 
10190  {
10191  /* This is like the HEAPTUPLE_DEAD case */
10192  Assert(!visible);
10193  return;
10194  }
10195  break;
10197  xid = HeapTupleHeaderGetXmin(tuple->t_data);
10198  break;
10199  case HEAPTUPLE_DEAD:
10200  Assert(!visible);
10201  return;
10202  default:
10203 
10204  /*
10205  * The only way to get to this default clause is if a new value is
10206  * added to the enum type without adding it to this switch
10207  * statement. That's a bug, so elog.
10208  */
10209  elog(ERROR, "unrecognized return value from HeapTupleSatisfiesVacuum: %u", htsvResult);
10210 
10211  /*
10212  * In spite of having all enum values covered and calling elog on
10213  * this default, some compilers think this is a code path which
10214  * allows xid to be used below without initialization. Silence
10215  * that warning.
10216  */
10217  xid = InvalidTransactionId;
10218  }
10219 
10222 
10223  /*
10224  * Find top level xid. Bail out if xid is too early to be a conflict, or
10225  * if it's our own xid.
10226  */
10228  return;
10229  xid = SubTransGetTopmostTransaction(xid);
10231  return;
10232 
10233  CheckForSerializableConflictOut(relation, xid, snapshot);
10234 }
HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
void CheckForSerializableConflictOut(Relation relation, TransactionId xid, Snapshot snapshot)
Definition: predicate.c:4003
bool CheckForSerializableConflictOutNeeded(Relation relation, Snapshot snapshot)
Definition: predicate.c:3971
TransactionId SubTransGetTopmostTransaction(TransactionId xid)
Definition: subtrans.c:163
bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:329
TransactionId GetTopTransactionIdIfAny(void)
Definition: xact.c:433

References Assert(), CheckForSerializableConflictOut(), CheckForSerializableConflictOutNeeded(), elog, ERROR, GetTopTransactionIdIfAny(), HEAPTUPLE_DEAD, HEAPTUPLE_DELETE_IN_PROGRESS, HEAPTUPLE_INSERT_IN_PROGRESS, HEAPTUPLE_LIVE, HEAPTUPLE_RECENTLY_DEAD, HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleSatisfiesVacuum(), InvalidTransactionId, SubTransGetTopmostTransaction(), HeapTupleData::t_data, TransactionIdEquals, TransactionIdFollowsOrEquals(), TransactionIdIsValid, TransactionIdPrecedes(), and TransactionXmin.

Referenced by heap_fetch(), heap_get_latest_tid(), heap_hot_search_buffer(), heapam_scan_bitmap_next_block(), heapam_scan_sample_next_tuple(), heapgetpage(), and heapgettup().

◆ heapgetpage()

void heapgetpage ( TableScanDesc  sscan,
BlockNumber  block 
)

Definition at line 373 of file heapam.c.

374 {
375  HeapScanDesc scan = (HeapScanDesc) sscan;
376  Buffer buffer;
377  Snapshot snapshot;
378  Page page;
379  int lines;
380  int ntup;
381  OffsetNumber lineoff;
382  bool all_visible;
383 
384  Assert(block < scan->rs_nblocks);
385 
386  /* release previous scan buffer, if any */
387  if (BufferIsValid(scan->rs_cbuf))
388  {
389  ReleaseBuffer(scan->rs_cbuf);
390  scan->rs_cbuf = InvalidBuffer;
391  }
392 
393  /*
394  * Be sure to check for interrupts at least once per page. Checks at
395  * higher code levels won't be able to stop a seqscan that encounters many
396  * pages' worth of consecutive dead tuples.
397  */
399 
400  /* read page using selected strategy */
401  scan->rs_cbuf = ReadBufferExtended(scan->rs_base.rs_rd, MAIN_FORKNUM, block,
402  RBM_NORMAL, scan->rs_strategy);
403  scan->rs_cblock = block;
404 
405  if (!(scan->rs_base.rs_flags & SO_ALLOW_PAGEMODE))
406  return;
407 
408  buffer = scan->rs_cbuf;
409  snapshot = scan->rs_base.rs_snapshot;
410 
411  /*
412  * Prune and repair fragmentation for the whole page, if possible.
413  */
414  heap_page_prune_opt(scan->rs_base.rs_rd, buffer);
415 
416  /*
417  * We must hold share lock on the buffer content while examining tuple
418  * visibility. Afterwards, however, the tuples we have found to be
419  * visible are guaranteed good as long as we hold the buffer pin.
420  */
421  LockBuffer(buffer, BUFFER_LOCK_SHARE);
422 
423  page = BufferGetPage(buffer);
424  lines = PageGetMaxOffsetNumber(page);
425  ntup = 0;
426 
427  /*
428  * If the all-visible flag indicates that all tuples on the page are
429  * visible to everyone, we can skip the per-tuple visibility tests.
430  *
431  * Note: In hot standby, a tuple that's already visible to all
432  * transactions on the primary might still be invisible to a read-only
433  * transaction in the standby. We partly handle this problem by tracking
434  * the minimum xmin of visible tuples as the cut-off XID while marking a
435  * page all-visible on the primary and WAL log that along with the
436  * visibility map SET operation. In hot standby, we wait for (or abort)
437  * all transactions that can potentially may not see one or more tuples on
438  * the page. That's how index-only scans work fine in hot standby. A
439  * crucial difference between index-only scans and heap scans is that the
440  * index-only scan completely relies on the visibility map where as heap
441  * scan looks at the page-level PD_ALL_VISIBLE flag. We are not sure if
442  * the page-level flag can be trusted in the same way, because it might
443  * get propagated somehow without being explicitly WAL-logged, e.g. via a
444  * full page write. Until we can prove that beyond doubt, let's check each
445  * tuple for visibility the hard way.
446  */
447  all_visible = PageIsAllVisible(page) && !snapshot->takenDuringRecovery;
448 
449  for (lineoff = FirstOffsetNumber; lineoff <= lines; lineoff++)
450  {
451  ItemId lpp = PageGetItemId(page, lineoff);
452  HeapTupleData loctup;
453  bool valid;
454 
455  if (!ItemIdIsNormal(lpp))
456  continue;
457 
458  loctup.t_tableOid = RelationGetRelid(scan->rs_base.rs_rd);
459  loctup.t_data = (HeapTupleHeader) PageGetItem(page, lpp);
460  loctup.t_len = ItemIdGetLength(lpp);
461  ItemPointerSet(&(loctup.t_self), block, lineoff);
462 
463  if (all_visible)
464  valid = true;
465  else
466  valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
467 
469  &loctup, buffer, snapshot);
470 
471  if (valid)
472  scan->rs_vistuples[ntup++] = lineoff;
473  }
474 
476 
477  Assert(ntup <= MaxHeapTuplesPerPage);
478  scan->rs_ntuples = ntup;
479 }
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:781
@ RBM_NORMAL
Definition: bufmgr.h:44
void heap_page_prune_opt(Relation relation, Buffer buffer)
Definition: pruneheap.c:88
@ MAIN_FORKNUM
Definition: relpath.h:50
int rs_ntuples
Definition: heapam.h:77
OffsetNumber rs_vistuples[MaxHeapTuplesPerPage]
Definition: heapam.h:78
BlockNumber rs_cblock
Definition: heapam.h:61
bool takenDuringRecovery
Definition: snapshot.h:184

References Assert(), BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetPage(), BufferIsValid(), CHECK_FOR_INTERRUPTS, FirstOffsetNumber, heap_page_prune_opt(), HeapCheckForSerializableConflictOut(), HeapTupleSatisfiesVisibility(), InvalidBuffer, ItemIdGetLength, ItemIdIsNormal, ItemPointerSet(), LockBuffer(), MAIN_FORKNUM, MaxHeapTuplesPerPage, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), PageIsAllVisible(), RBM_NORMAL, ReadBufferExtended(), RelationGetRelid, ReleaseBuffer(), HeapScanDescData::rs_base, HeapScanDescData::rs_cblock, HeapScanDescData::rs_cbuf, TableScanDescData::rs_flags, HeapScanDescData::rs_ntuples, TableScanDescData::rs_rd, TableScanDescData::rs_snapshot, HeapScanDescData::rs_strategy, HeapScanDescData::rs_vistuples, SO_ALLOW_PAGEMODE, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, and SnapshotData::takenDuringRecovery.

Referenced by heapam_scan_sample_next_block(), heapgettup(), and heapgettup_pagemode().

◆ HeapTupleHeaderIsOnlyLocked()

bool HeapTupleHeaderIsOnlyLocked ( HeapTupleHeader  tuple)

Definition at line 1520 of file heapam_visibility.c.

1521 {
1522  TransactionId xmax;
1523 
1524  /* if there's no valid Xmax, then there's obviously no update either */
1525  if (tuple->t_infomask & HEAP_XMAX_INVALID)
1526  return true;
1527 
1528  if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
1529  return true;
1530 
1531  /* invalid xmax means no update */
1533  return true;
1534 
1535  /*
1536  * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
1537  * necessarily have been updated
1538  */
1539  if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
1540  return false;
1541 
1542  /* ... but if it's a multi, then perhaps the updating Xid aborted. */
1543  xmax = HeapTupleGetUpdateXid(tuple);
1544 
1545  /* not LOCKED_ONLY, so it has to have an xmax */
1547 
1549  return false;
1550  if (TransactionIdIsInProgress(xmax))
1551  return false;
1552  if (TransactionIdDidCommit(xmax))
1553  return false;
1554 
1555  /*
1556  * not current, not in progress, not committed -- must have aborted or
1557  * crashed
1558  */
1559  return true;
1560 }
bool TransactionIdIsInProgress(TransactionId xid)
Definition: procarray.c:1390

References Assert(), HEAP_XMAX_INVALID, HEAP_XMAX_IS_MULTI, HEAP_XMAX_LOCK_ONLY, HeapTupleGetUpdateXid(), HeapTupleHeaderGetRawXmax, HeapTupleHeaderData::t_infomask, TransactionIdDidCommit(), TransactionIdIsCurrentTransactionId(), TransactionIdIsInProgress(), and TransactionIdIsValid.

Referenced by heap_delete(), heap_get_latest_tid(), heap_lock_tuple(), heap_lock_updated_tuple_rec(), HeapTupleSatisfiesVacuumHorizon(), and rewrite_heap_tuple().

◆ HeapTupleIsSurelyDead()

bool HeapTupleIsSurelyDead ( HeapTuple  htup,
struct GlobalVisState vistest 
)

Definition at line 1465 of file heapam_visibility.c.

1466 {
1467  HeapTupleHeader tuple = htup->t_data;
1468 
1469  Assert(ItemPointerIsValid(&htup->t_self));
1470  Assert(htup->t_tableOid != InvalidOid);
1471 
1472  /*
1473  * If the inserting transaction is marked invalid, then it aborted, and
1474  * the tuple is definitely dead. If it's marked neither committed nor
1475  * invalid, then we assume it's still alive (since the presumption is that
1476  * all relevant hint bits were just set moments ago).
1477  */
1478  if (!HeapTupleHeaderXminCommitted(tuple))
1479  return HeapTupleHeaderXminInvalid(tuple);
1480 
1481  /*
1482  * If the inserting transaction committed, but any deleting transaction
1483  * aborted, the tuple is still alive.
1484  */
1485  if (tuple->t_infomask & HEAP_XMAX_INVALID)
1486  return false;
1487 
1488  /*
1489  * If the XMAX is just a lock, the tuple is still alive.
1490  */
1492  return false;
1493 
1494  /*
1495  * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
1496  * know without checking pg_multixact.
1497  */
1498  if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1499  return false;
1500 
1501  /* If deleter isn't known to have committed, assume it's still running. */
1502  if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1503  return false;
1504 
1505  /* Deleter committed, so tuple is dead if the XID is old enough. */
1506  return GlobalVisTestIsRemovableXid(vistest,
1507  HeapTupleHeaderGetRawXmax(tuple));
1508 }
#define HeapTupleHeaderXminCommitted(tup)
Definition: htup_details.h:320
#define HeapTupleHeaderXminInvalid(tup)
Definition: htup_details.h:325
#define InvalidOid
Definition: postgres_ext.h:36

References Assert(), GlobalVisTestIsRemovableXid(), HEAP_XMAX_COMMITTED, HEAP_XMAX_INVALID, HEAP_XMAX_IS_LOCKED_ONLY, HEAP_XMAX_IS_MULTI, HeapTupleHeaderGetRawXmax, HeapTupleHeaderXminCommitted, HeapTupleHeaderXminInvalid, InvalidOid, ItemPointerIsValid(), HeapTupleData::t_data, HeapTupleHeaderData::t_infomask, HeapTupleData::t_self, and HeapTupleData::t_tableOid.

Referenced by heap_hot_search_buffer().

◆ HeapTupleSatisfiesUpdate()

TM_Result HeapTupleSatisfiesUpdate ( HeapTuple  htup,
CommandId  curcid,
Buffer  buffer 
)

Definition at line 458 of file heapam_visibility.c.

460 {
461  HeapTupleHeader tuple = htup->t_data;
462 
464  Assert(htup->t_tableOid != InvalidOid);
465 
466  if (!HeapTupleHeaderXminCommitted(tuple))
467  {
468  if (HeapTupleHeaderXminInvalid(tuple))
469  return TM_Invisible;
470 
471  /* Used by pre-9.0 binary upgrades */
472  if (tuple->t_infomask & HEAP_MOVED_OFF)
473  {
475 
477  return TM_Invisible;
478  if (!TransactionIdIsInProgress(xvac))
479  {
480  if (TransactionIdDidCommit(xvac))
481  {
482  SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
484  return TM_Invisible;
485  }
486  SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
488  }
489  }
490  /* Used by pre-9.0 binary upgrades */
491  else if (tuple->t_infomask & HEAP_MOVED_IN)
492  {
494 
496  {
497  if (TransactionIdIsInProgress(xvac))
498  return TM_Invisible;
499  if (TransactionIdDidCommit(xvac))
500  SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
502  else
503  {
504  SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
506  return TM_Invisible;
507  }
508  }
509  }
511  {
512  if (HeapTupleHeaderGetCmin(tuple) >= curcid)
513  return TM_Invisible; /* inserted after scan started */
514 
515  if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
516  return TM_Ok;
517 
519  {
520  TransactionId xmax;
521 
522  xmax = HeapTupleHeaderGetRawXmax(tuple);
523 
524  /*
525  * Careful here: even though this tuple was created by our own
526  * transaction, it might be locked by other transactions, if
527  * the original version was key-share locked when we updated
528  * it.
529  */
530 
531  if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
532  {
533  if (MultiXactIdIsRunning(xmax, true))
534  return TM_BeingModified;
535  else
536  return TM_Ok;
537  }
538 
539  /*
540  * If the locker is gone, then there is nothing of interest
541  * left in this Xmax; otherwise, report the tuple as
542  * locked/updated.
543  */
544  if (!TransactionIdIsInProgress(xmax))
545  return TM_Ok;
546  return TM_BeingModified;
547  }
548 
549  if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
550  {
551  TransactionId xmax;
552 
553  xmax = HeapTupleGetUpdateXid(tuple);
554 
555  /* not LOCKED_ONLY, so it has to have an xmax */
557 
558  /* deleting subtransaction must have aborted */
560  {
562  false))
563  return TM_BeingModified;
564  return TM_Ok;
565  }
566  else
567  {
568  if (HeapTupleHeaderGetCmax(tuple) >= curcid)
569  return TM_SelfModified; /* updated after scan started */
570  else
571  return TM_Invisible; /* updated before scan started */
572  }
573  }
574 
576  {
577  /* deleting subtransaction must have aborted */
578  SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
580  return TM_Ok;
581  }
582 
583  if (HeapTupleHeaderGetCmax(tuple) >= curcid)
584  return TM_SelfModified; /* updated after scan started */
585  else
586  return TM_Invisible; /* updated before scan started */
587  }
589  return TM_Invisible;
591  SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
593  else
594  {
595  /* it must have aborted or crashed */
596  SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
598  return TM_Invisible;
599  }
600  }
601 
602  /* by here, the inserting transaction has committed */
603 
604  if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
605  return TM_Ok;
606 
607  if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
608  {
610  return TM_Ok;
611  if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
612  return TM_Updated; /* updated by other */
613  else
614  return TM_Deleted; /* deleted by other */
615  }
616 
617  if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
618  {
619  TransactionId xmax;
620 
621  if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
622  return TM_Ok;
623 
625  {
627  return TM_BeingModified;
628 
630  return TM_Ok;
631  }
632 
633  xmax = HeapTupleGetUpdateXid(tuple);
634  if (!TransactionIdIsValid(xmax))
635  {
637  return TM_BeingModified;
638  }
639 
640  /* not LOCKED_ONLY, so it has to have an xmax */
642 
644  {
645  if (HeapTupleHeaderGetCmax(tuple) >= curcid)
646  return TM_SelfModified; /* updated after scan started */
647  else
648  return TM_Invisible; /* updated before scan started */
649  }
650 
652  return TM_BeingModified;
653 
654  if (TransactionIdDidCommit(xmax))
655  {
656  if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
657  return TM_Updated;
658  else
659  return TM_Deleted;
660  }
661 
662  /*
663  * By here, the update in the Xmax is either aborted or crashed, but
664  * what about the other members?
665  */
666 
668  {
669  /*
670  * There's no member, even just a locker, alive anymore, so we can
671  * mark the Xmax as invalid.
672  */
673  SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
675  return TM_Ok;
676  }
677  else
678  {
679  /* There are lockers running */
680  return TM_BeingModified;
681  }
682  }
683 
685  {
687  return TM_BeingModified;
688  if (HeapTupleHeaderGetCmax(tuple) >= curcid)
689  return TM_SelfModified; /* updated after scan started */
690  else
691  return TM_Invisible; /* updated before scan started */
692  }
693 
695  return TM_BeingModified;
696 
698  {
699  /* it must have aborted or crashed */
700  SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
702  return TM_Ok;
703  }
704 
705  /* xmax transaction committed */
706 
708  {
709  SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
711  return TM_Ok;
712  }
713 
714  SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
716  if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
717  return TM_Updated; /* updated by other */
718  else
719  return TM_Deleted; /* deleted by other */
720 }
CommandId HeapTupleHeaderGetCmin(HeapTupleHeader tup)
Definition: combocid.c:104
static void SetHintBits(HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid)
#define HEAP_XMIN_COMMITTED
Definition: htup_details.h:204
#define HEAP_MOVED_IN
Definition: htup_details.h:212
#define HEAP_XMIN_INVALID
Definition: htup_details.h:205
bool MultiXactIdIsRunning(MultiXactId multi, bool isLockOnly)
Definition: multixact.c:550

References Assert(), HEAP_LOCKED_UPGRADED, HEAP_MOVED_IN, HEAP_MOVED_OFF, HEAP_XMAX_COMMITTED, HEAP_XMAX_INVALID, HEAP_XMAX_IS_LOCKED_ONLY, HEAP_XMAX_IS_MULTI, HEAP_XMIN_COMMITTED, HEAP_XMIN_INVALID, HeapTupleGetUpdateXid(), HeapTupleHeaderGetCmax(), HeapTupleHeaderGetCmin(), HeapTupleHeaderGetRawXmax, HeapTupleHeaderGetRawXmin, HeapTupleHeaderGetXvac, HeapTupleHeaderXminCommitted, HeapTupleHeaderXminInvalid, InvalidOid, InvalidTransactionId, ItemPointerEquals(), ItemPointerIsValid(), MultiXactIdIsRunning(), SetHintBits(), HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleHeaderData::t_infomask, HeapTupleData::t_self, HeapTupleData::t_tableOid, TM_BeingModified, TM_Deleted, TM_Invisible, TM_Ok, TM_SelfModified, TM_Updated, TransactionIdDidCommit(), TransactionIdIsCurrentTransactionId(), TransactionIdIsInProgress(), and TransactionIdIsValid.

Referenced by heap_delete(), heap_lock_tuple(), heap_update(), and pgrowlocks().

◆ HeapTupleSatisfiesVacuum()

HTSV_Result HeapTupleSatisfiesVacuum ( HeapTuple  htup,
TransactionId  OldestXmin,
Buffer  buffer 
)

Definition at line 1162 of file heapam_visibility.c.

1164 {
1165  TransactionId dead_after = InvalidTransactionId;
1166  HTSV_Result res;
1167 
1168  res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1169 
1171  {
1172  Assert(TransactionIdIsValid(dead_after));
1173 
1174  if (TransactionIdPrecedes(dead_after, OldestXmin))
1175  res = HEAPTUPLE_DEAD;
1176  }
1177  else
1178  Assert(!TransactionIdIsValid(dead_after));
1179 
1180  return res;
1181 }
HTSV_Result HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *dead_after)

References Assert(), HEAPTUPLE_DEAD, HEAPTUPLE_RECENTLY_DEAD, HeapTupleSatisfiesVacuumHorizon(), InvalidTransactionId, res, TransactionIdIsValid, and TransactionIdPrecedes().

Referenced by heap_page_is_all_visible(), heapam_index_build_range_scan(), heapam_relation_copy_for_cluster(), heapam_scan_analyze_next_tuple(), HeapCheckForSerializableConflictOut(), lazy_scan_noprune(), statapprox_heap(), and tuple_all_visible().

◆ HeapTupleSatisfiesVacuumHorizon()

HTSV_Result HeapTupleSatisfiesVacuumHorizon ( HeapTuple  htup,
Buffer  buffer,
TransactionId dead_after 
)

Definition at line 1196 of file heapam_visibility.c.

1197 {
1198  HeapTupleHeader tuple = htup->t_data;
1199 
1200  Assert(ItemPointerIsValid(&htup->t_self));
1201  Assert(htup->t_tableOid != InvalidOid);
1202  Assert(dead_after != NULL);
1203 
1204  *dead_after = InvalidTransactionId;
1205 
1206  /*
1207  * Has inserting transaction committed?
1208  *
1209  * If the inserting transaction aborted, then the tuple was never visible
1210  * to any other transaction, so we can delete it immediately.
1211  */
1212  if (!HeapTupleHeaderXminCommitted(tuple))
1213  {
1214  if (HeapTupleHeaderXminInvalid(tuple))
1215  return HEAPTUPLE_DEAD;
1216  /* Used by pre-9.0 binary upgrades */
1217  else if (tuple->t_infomask & HEAP_MOVED_OFF)
1218  {
1219  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
1220 
1223  if (TransactionIdIsInProgress(xvac))
1225  if (TransactionIdDidCommit(xvac))
1226  {
1227  SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1229  return HEAPTUPLE_DEAD;
1230  }
1231  SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1233  }
1234  /* Used by pre-9.0 binary upgrades */
1235  else if (tuple->t_infomask & HEAP_MOVED_IN)
1236  {
1237  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
1238 
1241  if (TransactionIdIsInProgress(xvac))
1243  if (TransactionIdDidCommit(xvac))
1244  SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1246  else
1247  {
1248  SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1250  return HEAPTUPLE_DEAD;
1251  }
1252  }
1254  {
1255  if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1257  /* only locked? run infomask-only check first, for performance */
1258  if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
1261  /* inserted and then deleted by same xact */
1264  /* deleting subtransaction must have aborted */
1266  }
1268  {
1269  /*
1270  * It'd be possible to discern between INSERT/DELETE in progress
1271  * here by looking at xmax - but that doesn't seem beneficial for
1272  * the majority of callers and even detrimental for some. We'd
1273  * rather have callers look at/wait for xmin than xmax. It's
1274  * always correct to return INSERT_IN_PROGRESS because that's
1275  * what's happening from the view of other backends.
1276  */
1278  }
1280  SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1281  HeapTupleHeaderGetRawXmin(tuple));
1282  else
1283  {
1284  /*
1285  * Not in Progress, Not Committed, so either Aborted or crashed
1286  */
1287  SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1289  return HEAPTUPLE_DEAD;
1290  }
1291 
1292  /*
1293  * At this point the xmin is known committed, but we might not have
1294  * been able to set the hint bit yet; so we can no longer Assert that
1295  * it's set.
1296  */
1297  }
1298 
1299  /*
1300  * Okay, the inserter committed, so it was good at some point. Now what
1301  * about the deleting transaction?
1302  */
1303  if (tuple->t_infomask & HEAP_XMAX_INVALID)
1304  return HEAPTUPLE_LIVE;
1305 
1307  {
1308  /*
1309  * "Deleting" xact really only locked it, so the tuple is live in any
1310  * case. However, we should make sure that either XMAX_COMMITTED or
1311  * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
1312  * examining the tuple for future xacts.
1313  */
1314  if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1315  {
1316  if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1317  {
1318  /*
1319  * If it's a pre-pg_upgrade tuple, the multixact cannot
1320  * possibly be running; otherwise have to check.
1321  */
1322  if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
1324  true))
1325  return HEAPTUPLE_LIVE;
1327  }
1328  else
1329  {
1331  return HEAPTUPLE_LIVE;
1332  SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1334  }
1335  }
1336 
1337  /*
1338  * We don't really care whether xmax did commit, abort or crash. We
1339  * know that xmax did lock the tuple, but it did not and will never
1340  * actually update it.
1341  */
1342 
1343  return HEAPTUPLE_LIVE;
1344  }
1345 
1346  if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1347  {
1348  TransactionId xmax = HeapTupleGetUpdateXid(tuple);
1349 
1350  /* already checked above */
1352 
1353  /* not LOCKED_ONLY, so it has to have an xmax */
1355 
1356  if (TransactionIdIsInProgress(xmax))
1358  else if (TransactionIdDidCommit(xmax))
1359  {
1360  /*
1361  * The multixact might still be running due to lockers. Need to
1362  * allow for pruning if below the xid horizon regardless --
1363  * otherwise we could end up with a tuple where the updater has to
1364  * be removed due to the horizon, but is not pruned away. It's
1365  * not a problem to prune that tuple, because any remaining
1366  * lockers will also be present in newer tuple versions.
1367  */
1368  *dead_after = xmax;
1369  return HEAPTUPLE_RECENTLY_DEAD;
1370  }
1371  else if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
1372  {
1373  /*
1374  * Not in Progress, Not Committed, so either Aborted or crashed.
1375  * Mark the Xmax as invalid.
1376  */
1378  }
1379 
1380  return HEAPTUPLE_LIVE;
1381  }
1382 
1383  if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1384  {
1388  SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1389  HeapTupleHeaderGetRawXmax(tuple));
1390  else
1391  {
1392  /*
1393  * Not in Progress, Not Committed, so either Aborted or crashed
1394  */
1395  SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1397  return HEAPTUPLE_LIVE;
1398  }
1399 
1400  /*
1401  * At this point the xmax is known committed, but we might not have
1402  * been able to set the hint bit yet; so we can no longer Assert that
1403  * it's set.
1404  */
1405  }
1406 
1407  /*
1408  * Deleter committed, allow caller to check if it was recent enough that
1409  * some open transactions could still see the tuple.
1410  */
1411  *dead_after = HeapTupleHeaderGetRawXmax(tuple);
1412  return HEAPTUPLE_RECENTLY_DEAD;
1413 }

References Assert(), HEAP_LOCKED_UPGRADED, HEAP_MOVED_IN, HEAP_MOVED_OFF, HEAP_XMAX_COMMITTED, HEAP_XMAX_INVALID, HEAP_XMAX_IS_LOCKED_ONLY, HEAP_XMAX_IS_MULTI, HEAP_XMIN_COMMITTED, HEAP_XMIN_INVALID, HEAPTUPLE_DEAD, HEAPTUPLE_DELETE_IN_PROGRESS, HEAPTUPLE_INSERT_IN_PROGRESS, HEAPTUPLE_LIVE, HEAPTUPLE_RECENTLY_DEAD, HeapTupleGetUpdateXid(), HeapTupleHeaderGetRawXmax, HeapTupleHeaderGetRawXmin, HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXvac, HeapTupleHeaderIsOnlyLocked(), HeapTupleHeaderXminCommitted, HeapTupleHeaderXminInvalid, InvalidOid, InvalidTransactionId, ItemPointerIsValid(), MultiXactIdIsRunning(), SetHintBits(), HeapTupleData::t_data, HeapTupleHeaderData::t_infomask, HeapTupleData::t_self, HeapTupleData::t_tableOid, TransactionIdDidCommit(), TransactionIdIsCurrentTransactionId(), TransactionIdIsInProgress(), and TransactionIdIsValid.

Referenced by heap_prune_satisfies_vacuum(), HeapTupleSatisfiesNonVacuumable(), and HeapTupleSatisfiesVacuum().

◆ HeapTupleSatisfiesVisibility()

bool HeapTupleSatisfiesVisibility ( HeapTuple  htup,
Snapshot  snapshot,
Buffer  buffer 
)

Definition at line 1767 of file heapam_visibility.c.

1768 {
1769  switch (snapshot->snapshot_type)
1770  {
1771  case SNAPSHOT_MVCC:
1772  return HeapTupleSatisfiesMVCC(htup, snapshot, buffer);
1773  case SNAPSHOT_SELF:
1774  return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
1775  case SNAPSHOT_ANY:
1776  return HeapTupleSatisfiesAny(htup, snapshot, buffer);
1777  case SNAPSHOT_TOAST:
1778  return HeapTupleSatisfiesToast(htup, snapshot, buffer);
1779  case SNAPSHOT_DIRTY:
1780  return HeapTupleSatisfiesDirty(htup, snapshot, buffer);
1782  return HeapTupleSatisfiesHistoricMVCC(htup, snapshot, buffer);
1784  return HeapTupleSatisfiesNonVacuumable(htup, snapshot, buffer);
1785  }
1786 
1787  return false; /* keep compiler quiet */
1788 }
static bool HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot, Buffer buffer)
@ SNAPSHOT_TOAST
Definition: snapshot.h:74
@ SNAPSHOT_SELF
Definition: snapshot.h:64
@ SNAPSHOT_NON_VACUUMABLE
Definition: snapshot.h:118
@ SNAPSHOT_MVCC
Definition: snapshot.h:50
@ SNAPSHOT_ANY
Definition: snapshot.h:69
@ SNAPSHOT_HISTORIC_MVCC
Definition: snapshot.h:109
@ SNAPSHOT_DIRTY
Definition: snapshot.h:102
SnapshotType snapshot_type
Definition: snapshot.h:144

References HeapTupleSatisfiesAny(), HeapTupleSatisfiesDirty(), HeapTupleSatisfiesHistoricMVCC(), HeapTupleSatisfiesMVCC(), HeapTupleSatisfiesNonVacuumable(), HeapTupleSatisfiesSelf(), HeapTupleSatisfiesToast(), SNAPSHOT_ANY, SNAPSHOT_DIRTY, SNAPSHOT_HISTORIC_MVCC, SNAPSHOT_MVCC, SNAPSHOT_NON_VACUUMABLE, SNAPSHOT_SELF, SNAPSHOT_TOAST, and SnapshotData::snapshot_type.

Referenced by heap_delete(), heap_fetch(), heap_get_latest_tid(), heap_hot_search_buffer(), heap_update(), heapam_scan_bitmap_next_block(), heapam_tuple_satisfies_snapshot(), heapgetpage(), heapgettup(), pgstat_heap(), SampleHeapTupleVisible(), and ScanSourceDatabasePgClassPage().

◆ HeapTupleSetHintBits()

void HeapTupleSetHintBits ( HeapTupleHeader  tuple,
Buffer  buffer,
uint16  infomask,
TransactionId  xid 
)

Definition at line 141 of file heapam_visibility.c.

143 {
144  SetHintBits(tuple, buffer, infomask, xid);
145 }

References SetHintBits().

Referenced by UpdateXmaxHintBits().

◆ htsv_get_valid_status()

static HTSV_Result htsv_get_valid_status ( int  status)
inlinestatic

Definition at line 221 of file heapam.h.

222 {
223  Assert(status >= HEAPTUPLE_DEAD &&
224  status <= HEAPTUPLE_DELETE_IN_PROGRESS);
225  return (HTSV_Result) status;
226 }

References Assert(), HEAPTUPLE_DEAD, and HEAPTUPLE_DELETE_IN_PROGRESS.

Referenced by heap_prune_chain(), and lazy_scan_prune().

◆ ReleaseBulkInsertStatePin()

void ReleaseBulkInsertStatePin ( BulkInsertState  bistate)

Definition at line 1786 of file heapam.c.

1787 {
1788  if (bistate->current_buf != InvalidBuffer)
1789  ReleaseBuffer(bistate->current_buf);
1790  bistate->current_buf = InvalidBuffer;
1791 
1792  /*
1793  * Despite the name, we also reset bulk relation extension state.
1794  * Otherwise we can end up erroring out due to looking for free space in
1795  * ->next_free of one partition, even though ->next_free was set when
1796  * extending another partition. It could obviously also be bad for
1797  * efficiency to look at existing blocks at offsets from another
1798  * partition, even if we don't error out.
1799  */
1800  bistate->next_free = InvalidBlockNumber;
1801  bistate->last_free = InvalidBlockNumber;
1802 }

References BulkInsertStateData::current_buf, InvalidBlockNumber, InvalidBuffer, BulkInsertStateData::last_free, BulkInsertStateData::next_free, and ReleaseBuffer().

Referenced by CopyFrom().

◆ ResolveCminCmaxDuringDecoding()

bool ResolveCminCmaxDuringDecoding ( struct HTAB tuplecid_data,
Snapshot  snapshot,
HeapTuple  htup,
Buffer  buffer,
CommandId cmin,
CommandId cmax 
)

Definition at line 5207 of file reorderbuffer.c.

5211 {
5214  ForkNumber forkno;
5215  BlockNumber blockno;
5216  bool updated_mapping = false;
5217 
5218  /*
5219  * Return unresolved if tuplecid_data is not valid. That's because when
5220  * streaming in-progress transactions we may run into tuples with the CID
5221  * before actually decoding them. Think e.g. about INSERT followed by
5222  * TRUNCATE, where the TRUNCATE may not be decoded yet when applying the
5223  * INSERT. So in such cases, we assume the CID is from the future
5224  * command.
5225  */
5226  if (tuplecid_data == NULL)
5227  return false;
5228 
5229  /* be careful about padding */
5230  memset(&key, 0, sizeof(key));
5231 
5232  Assert(!BufferIsLocal(buffer));
5233 
5234  /*
5235  * get relfilelocator from the buffer, no convenient way to access it
5236  * other than that.
5237  */
5238  BufferGetTag(buffer, &key.rlocator, &forkno, &blockno);
5239 
5240  /* tuples can only be in the main fork */
5241  Assert(forkno == MAIN_FORKNUM);
5242  Assert(blockno == ItemPointerGetBlockNumber(&htup->t_self));
5243 
5244  ItemPointerCopy(&htup->t_self,
5245  &key.tid);
5246 
5247 restart:
5248  ent = (ReorderBufferTupleCidEnt *)
5250 
5251  /*
5252  * failed to find a mapping, check whether the table was rewritten and
5253  * apply mapping if so, but only do that once - there can be no new
5254  * mappings while we are in here since we have to hold a lock on the
5255  * relation.
5256  */
5257  if (ent == NULL && !updated_mapping)
5258  {
5260  /* now check but don't update for a mapping again */
5261  updated_mapping = true;
5262  goto restart;
5263  }
5264  else if (ent == NULL)
5265  return false;
5266 
5267  if (cmin)
5268  *cmin = ent->cmin;
5269  if (cmax)
5270  *cmax = ent->cmax;
5271  return true;
5272 }
#define BufferIsLocal(buffer)
Definition: buf.h:37
void BufferGetTag(Buffer buffer, RelFileLocator *rlocator, ForkNumber *forknum, BlockNumber *blknum)
Definition: bufmgr.c:3398
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
@ HASH_FIND
Definition: hsearch.h:113
ForkNumber
Definition: relpath.h:48
static void UpdateLogicalMappings(HTAB *tuplecid_data, Oid relid, Snapshot snapshot)
static HTAB * tuplecid_data
Definition: snapmgr.c:102

References Assert(), BufferGetTag(), BufferIsLocal, ReorderBufferTupleCidEnt::cmax, ReorderBufferTupleCidEnt::cmin, HASH_FIND, hash_search(), ItemPointerCopy(), ItemPointerGetBlockNumber(), sort-test::key, MAIN_FORKNUM, HeapTupleData::t_self, HeapTupleData::t_tableOid, tuplecid_data, and UpdateLogicalMappings().

Referenced by HeapTupleSatisfiesHistoricMVCC().

◆ simple_heap_delete()

void simple_heap_delete ( Relation  relation,
ItemPointer  tid 
)

Definition at line 2934 of file heapam.c.

2935 {
2936  TM_Result result;
2937  TM_FailureData tmfd;
2938 
2939  result = heap_delete(relation, tid,
2941  true /* wait for commit */ ,
2942  &tmfd, false /* changingPart */ );
2943  switch (result)
2944  {
2945  case TM_SelfModified:
2946  /* Tuple was already updated in current command? */
2947  elog(ERROR, "tuple already updated by self");
2948  break;
2949 
2950  case TM_Ok:
2951  /* done successfully */
2952  break;
2953 
2954  case TM_Updated:
2955  elog(ERROR, "tuple concurrently updated");
2956  break;
2957 
2958  case TM_Deleted:
2959  elog(ERROR, "tuple concurrently deleted");
2960  break;
2961 
2962  default:
2963  elog(ERROR, "unrecognized heap_delete status: %u", result);
2964  break;
2965  }
2966 }
TM_Result heap_delete(Relation relation, ItemPointer tid, CommandId cid, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, bool changingPart)
Definition: heapam.c:2513
CommandId GetCurrentCommandId(bool used)
Definition: xact.c:819

References elog, ERROR, GetCurrentCommandId(), heap_delete(), InvalidSnapshot, TM_Deleted, TM_Ok, TM_SelfModified, and TM_Updated.

Referenced by CatalogTupleDelete(), and toast_delete_datum().

◆ simple_heap_insert()

void simple_heap_insert ( Relation  relation,
HeapTuple  tup 
)

Definition at line 2455 of file heapam.c.

2456 {
2457  heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
2458 }
void heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, BulkInsertState bistate)
Definition: heapam.c:1824

References GetCurrentCommandId(), and heap_insert().

Referenced by CatalogTupleInsert(), CatalogTupleInsertWithInfo(), and InsertOneTuple().

◆ simple_heap_update()

void simple_heap_update ( Relation  relation,
ItemPointer  otid,
HeapTuple  tup,
TU_UpdateIndexes update_indexes 
)

Definition at line 4051 of file heapam.c.

4053 {
4054  TM_Result result;
4055  TM_FailureData tmfd;
4056  LockTupleMode lockmode;
4057 
4058  result = heap_update(relation, otid, tup,
4060  true /* wait for commit */ ,
4061  &tmfd, &lockmode, update_indexes);
4062  switch (result)
4063  {
4064  case TM_SelfModified:
4065  /* Tuple was already updated in current command? */
4066  elog(ERROR, "tuple already updated by self");
4067  break;
4068 
4069  case TM_Ok:
4070  /* done successfully */
4071  break;
4072 
4073  case TM_Updated:
4074  elog(ERROR, "tuple concurrently updated");
4075  break;
4076 
4077  case TM_Deleted:
4078  elog(ERROR, "tuple concurrently deleted");
4079  break;
4080 
4081  default:
4082  elog(ERROR, "unrecognized heap_update status: %u", result);
4083  break;
4084  }
4085 }
TM_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, CommandId cid, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
Definition: heapam.c:2980
LockTupleMode
Definition: lockoptions.h:50

References elog, ERROR, GetCurrentCommandId(), heap_update(), InvalidSnapshot, TM_Deleted, TM_Ok, TM_SelfModified, and TM_Updated.

Referenced by CatalogTupleUpdate(), and CatalogTupleUpdateWithInfo().