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
 

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 HeapScanIsValid(scan)   PointerIsValid(scan)
 

Typedefs

typedef struct BulkInsertStateDataBulkInsertState
 
typedef struct HeapScanDescData HeapScanDescData
 
typedef struct HeapScanDescDataHeapScanDesc
 
typedef struct IndexFetchHeapData IndexFetchHeapData
 

Enumerations

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

Functions

TableScanDesc heap_beginscan (Relation relation, Snapshot snapshot, int nkeys, ScanKey key, ParallelTableScanDesc parallel_scan, uint32 flags)
 
void heap_setscanlimits (TableScanDesc scan, BlockNumber startBlk, BlockNumber numBlks)
 
void heapgetpage (TableScanDesc scan, BlockNumber page)
 
void heap_rescan (TableScanDesc scan, ScanKey key, bool set_params, bool allow_strat, bool allow_sync, bool allow_pagemode)
 
void heap_endscan (TableScanDesc scan)
 
HeapTuple heap_getnext (TableScanDesc scan, ScanDirection direction)
 
bool heap_getnextslot (TableScanDesc sscan, ScanDirection direction, struct TupleTableSlot *slot)
 
bool heap_fetch (Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf)
 
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 scan, 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)
 
TM_Result heap_lock_tuple (Relation relation, HeapTuple tuple, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, bool follow_update, Buffer *buffer, struct TM_FailureData *tmfd)
 
void heap_inplace_update (Relation relation, HeapTuple tuple)
 
bool heap_freeze_tuple (HeapTupleHeader tuple, TransactionId relfrozenxid, TransactionId relminmxid, TransactionId cutoff_xid, TransactionId cutoff_multi)
 
bool heap_tuple_needs_freeze (HeapTupleHeader tuple, TransactionId cutoff_xid, MultiXactId cutoff_multi, Buffer buf)
 
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)
 
TransactionId heap_index_delete_tuples (Relation rel, TM_IndexDeleteOp *delstate)
 
void heap_page_prune_opt (Relation relation, Buffer buffer)
 
int heap_page_prune (Relation relation, Buffer buffer, struct GlobalVisState *vistest, TransactionId limited_oldest_xmin, TimestampTz limited_oldest_ts, bool report_stats, TransactionId *latestRemovedXid, 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 onerel, struct VacuumParams *params, BufferAccessStrategy bstrategy)
 
void parallel_vacuum_main (dsm_segment *seg, shm_toc *toc)
 
bool HeapTupleSatisfiesVisibility (HeapTuple stup, Snapshot snapshot, Buffer buffer)
 
TM_Result HeapTupleSatisfiesUpdate (HeapTuple stup, CommandId curcid, Buffer buffer)
 
HTSV_Result HeapTupleSatisfiesVacuum (HeapTuple stup, TransactionId OldestXmin, Buffer buffer)
 
HTSV_Result HeapTupleSatisfiesVacuumHorizon (HeapTuple stup, Buffer buffer, TransactionId *dead_after)
 
void HeapTupleSetHintBits (HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid)
 
bool HeapTupleHeaderIsOnlyLocked (HeapTupleHeader tuple)
 
bool XidInMVCCSnapshot (TransactionId xid, Snapshot snapshot)
 
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 valid, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
 

Macro Definition Documentation

◆ HEAP_INSERT_FROZEN

#define HEAP_INSERT_FROZEN   TABLE_INSERT_FROZEN

Definition at line 35 of file heapam.h.

Referenced by heap_multi_insert(), heap_prepare_insert(), and RelationGetBufferForTuple().

◆ HEAP_INSERT_NO_LOGICAL

#define HEAP_INSERT_NO_LOGICAL   TABLE_INSERT_NO_LOGICAL

Definition at line 36 of file heapam.h.

Referenced by heap_insert(), heap_multi_insert(), and raw_heap_insert().

◆ HEAP_INSERT_SKIP_FSM

#define HEAP_INSERT_SKIP_FSM   TABLE_INSERT_SKIP_FSM

Definition at line 34 of file heapam.h.

Referenced by raw_heap_insert(), and RelationGetBufferForTuple().

◆ HEAP_INSERT_SPECULATIVE

#define HEAP_INSERT_SPECULATIVE   0x0010

◆ HeapScanIsValid

#define HeapScanIsValid (   scan)    PointerIsValid(scan)

Definition at line 109 of file heapam.h.

◆ MaxLockTupleMode

#define MaxLockTupleMode   LockTupleExclusive

Definition at line 42 of file heapam.h.

Typedef Documentation

◆ BulkInsertState

Definition at line 39 of file heapam.h.

◆ HeapScanDesc

typedef struct HeapScanDescData* HeapScanDesc

Definition at line 73 of file heapam.h.

◆ HeapScanDescData

◆ IndexFetchHeapData

Enumeration Type Documentation

◆ HTSV_Result

Enumerator
HEAPTUPLE_DEAD 
HEAPTUPLE_LIVE 
HEAPTUPLE_RECENTLY_DEAD 
HEAPTUPLE_INSERT_IN_PROGRESS 
HEAPTUPLE_DELETE_IN_PROGRESS 

Definition at line 87 of file heapam.h.

88 {
89  HEAPTUPLE_DEAD, /* tuple is dead and deletable */
90  HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
91  HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
92  HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
93  HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
94 } HTSV_Result;
HTSV_Result
Definition: heapam.h:87

Function Documentation

◆ FreeBulkInsertState()

void FreeBulkInsertState ( BulkInsertState  )

Definition at line 1838 of file heapam.c.

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

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

1839 {
1840  if (bistate->current_buf != InvalidBuffer)
1841  ReleaseBuffer(bistate->current_buf);
1842  FreeAccessStrategy(bistate->strategy);
1843  pfree(bistate);
1844 }
#define InvalidBuffer
Definition: buf.h:25
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3700
void pfree(void *pointer)
Definition: mcxt.c:1057
void FreeAccessStrategy(BufferAccessStrategy strategy)
Definition: freelist.c:597

◆ GetBulkInsertState()

BulkInsertState GetBulkInsertState ( void  )

Definition at line 1824 of file heapam.c.

References BAS_BULKWRITE, BulkInsertStateData::current_buf, GetAccessStrategy(), InvalidBuffer, palloc(), and BulkInsertStateData::strategy.

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

1825 {
1826  BulkInsertState bistate;
1827 
1828  bistate = (BulkInsertState) palloc(sizeof(BulkInsertStateData));
1830  bistate->current_buf = InvalidBuffer;
1831  return bistate;
1832 }
BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype)
Definition: freelist.c:542
#define InvalidBuffer
Definition: buf.h:25
struct BulkInsertStateData * BulkInsertState
Definition: heapam.h:39
BufferAccessStrategy strategy
Definition: hio.h:31
void * palloc(Size size)
Definition: mcxt.c:950
Buffer current_buf
Definition: hio.h:32

◆ heap_abort_speculative()

void heap_abort_speculative ( Relation  relation,
ItemPointer  tid 
)

Definition at line 5637 of file heapam.c.

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().

5638 {
5640  ItemId lp;
5641  HeapTupleData tp;
5642  Page page;
5643  BlockNumber block;
5644  Buffer buffer;
5645  TransactionId prune_xid;
5646 
5647  Assert(ItemPointerIsValid(tid));
5648 
5649  block = ItemPointerGetBlockNumber(tid);
5650  buffer = ReadBuffer(relation, block);
5651  page = BufferGetPage(buffer);
5652 
5654 
5655  /*
5656  * Page can't be all visible, we just inserted into it, and are still
5657  * running.
5658  */
5659  Assert(!PageIsAllVisible(page));
5660 
5661  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
5662  Assert(ItemIdIsNormal(lp));
5663 
5664  tp.t_tableOid = RelationGetRelid(relation);
5665  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
5666  tp.t_len = ItemIdGetLength(lp);
5667  tp.t_self = *tid;
5668 
5669  /*
5670  * Sanity check that the tuple really is a speculatively inserted tuple,
5671  * inserted by us.
5672  */
5673  if (tp.t_data->t_choice.t_heap.t_xmin != xid)
5674  elog(ERROR, "attempted to kill a tuple inserted by another transaction");
5675  if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
5676  elog(ERROR, "attempted to kill a non-speculative tuple");
5678 
5679  /*
5680  * No need to check for serializable conflicts here. There is never a
5681  * need for a combocid, either. No need to extract replica identity, or
5682  * do anything special with infomask bits.
5683  */
5684 
5686 
5687  /*
5688  * The tuple will become DEAD immediately. Flag that this page is a
5689  * candidate for pruning by setting xmin to TransactionXmin. While not
5690  * immediately prunable, it is the oldest xid we can cheaply determine
5691  * that's safe against wraparound / being older than the table's
5692  * relfrozenxid. To defend against the unlikely case of a new relation
5693  * having a newer relfrozenxid than our TransactionXmin, use relfrozenxid
5694  * if so (vacuum can't subsequently move relfrozenxid to beyond
5695  * TransactionXmin, so there's no race here).
5696  */
5698  if (TransactionIdPrecedes(TransactionXmin, relation->rd_rel->relfrozenxid))
5699  prune_xid = relation->rd_rel->relfrozenxid;
5700  else
5701  prune_xid = TransactionXmin;
5702  PageSetPrunable(page, prune_xid);
5703 
5704  /* store transaction information of xact deleting the tuple */
5707 
5708  /*
5709  * Set the tuple header xmin to InvalidTransactionId. This makes the
5710  * tuple immediately invisible everyone. (In particular, to any
5711  * transactions waiting on the speculative token, woken up later.)
5712  */
5714 
5715  /* Clear the speculative insertion token too */
5716  tp.t_data->t_ctid = tp.t_self;
5717 
5718  MarkBufferDirty(buffer);
5719 
5720  /*
5721  * XLOG stuff
5722  *
5723  * The WAL records generated here match heap_delete(). The same recovery
5724  * routines are used.
5725  */
5726  if (RelationNeedsWAL(relation))
5727  {
5728  xl_heap_delete xlrec;
5729  XLogRecPtr recptr;
5730 
5731  xlrec.flags = XLH_DELETE_IS_SUPER;
5733  tp.t_data->t_infomask2);
5735  xlrec.xmax = xid;
5736 
5737  XLogBeginInsert();
5738  XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
5739  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
5740 
5741  /* No replica identity & replication origin logged */
5742 
5743  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
5744 
5745  PageSetLSN(page, recptr);
5746  }
5747 
5748  END_CRIT_SECTION();
5749 
5750  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
5751 
5752  if (HeapTupleHasExternal(&tp))
5753  {
5754  Assert(!IsToastRelation(relation));
5755  heap_toast_delete(relation, &tp, true);
5756  }
5757 
5758  /*
5759  * Never need to mark tuple for invalidation, since catalogs don't support
5760  * speculative insertion
5761  */
5762 
5763  /* Now we can release the buffer */
5764  ReleaseBuffer(buffer);
5765 
5766  /* count deletion, as we counted the insertion too */
5767  pgstat_count_heap_delete(relation);
5768 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
bool IsToastRelation(Relation relation)
Definition: catalog.c:138
#define HEAP_XMAX_BITS
Definition: htup_details.h:270
union HeapTupleHeaderData::@45 t_choice
#define XLH_DELETE_IS_SUPER
Definition: heapam_xlog.h:99
static uint8 compute_infobits(uint16 infomask, uint16 infomask2)
Definition: heapam.c:2468
HeapTupleFields t_heap
Definition: htup_details.h:156
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:575
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
#define HeapTupleHeaderIsSpeculative(tup)
Definition: htup_details.h:429
#define PageSetPrunable(page, xid)
Definition: bufpage.h:392
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3700
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
Form_pg_class rd_rel
Definition: rel.h:110
OffsetNumber offnum
Definition: heapam_xlog.h:110
TransactionId TransactionXmin
Definition: snapmgr.c:112
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderIsHeapOnly(tup)
Definition: htup_details.h:501
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
#define ERROR
Definition: elog.h:45
ItemPointerData t_ctid
Definition: htup_details.h:160
ItemPointerData t_self
Definition: htup.h:65
TransactionId xmax
Definition: heapam_xlog.h:109
void heap_toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
Definition: heaptoast.c:43
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:438
uint32 t_len
Definition: htup.h:64
#define SizeOfHeapDelete
Definition: heapam_xlog.h:115
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define InvalidTransactionId
Definition: transam.h:31
Oid t_tableOid
Definition: htup.h:66
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
TransactionId t_xmin
Definition: htup_details.h:123
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3939
#define HEAP_KEYS_UPDATED
Definition: htup_details.h:278
#define HEAP_MOVED
Definition: htup_details.h:216
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:792
uint8 infobits_set
Definition: heapam_xlog.h:111
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:619
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define RelationNeedsWAL(relation)
Definition: rel.h:563
void pgstat_count_heap_delete(Relation rel)
Definition: pgstat.c:2221
#define HeapTupleHasExternal(tuple)
Definition: htup_details.h:673
#define elog(elevel,...)
Definition: elog.h:228
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define TransactionIdIsValid(xid)
Definition: transam.h:41
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define XLOG_HEAP_DELETE
Definition: heapam_xlog.h:33
#define RelationGetRelid(relation)
Definition: rel.h:457
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:319

◆ heap_beginscan()

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

Definition at line 1159 of file heapam.c.

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

Referenced by SampleHeapTupleVisible().

1163 {
1164  HeapScanDesc scan;
1165 
1166  /*
1167  * increment relation ref count while scanning relation
1168  *
1169  * This is just to make really sure the relcache entry won't go away while
1170  * the scan has a pointer to it. Caller should be holding the rel open
1171  * anyway, so this is redundant in all normal scenarios...
1172  */
1174 
1175  /*
1176  * allocate and initialize scan descriptor
1177  */
1178  scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
1179 
1180  scan->rs_base.rs_rd = relation;
1181  scan->rs_base.rs_snapshot = snapshot;
1182  scan->rs_base.rs_nkeys = nkeys;
1183  scan->rs_base.rs_flags = flags;
1184  scan->rs_base.rs_parallel = parallel_scan;
1185  scan->rs_base.rs_private =
1187  scan->rs_strategy = NULL; /* set in initscan */
1188 
1189  /*
1190  * Disable page-at-a-time mode if it's not a MVCC-safe snapshot.
1191  */
1192  if (!(snapshot && IsMVCCSnapshot(snapshot)))
1194 
1195  /*
1196  * For seqscan and sample scans in a serializable transaction, acquire a
1197  * predicate lock on the entire relation. This is required not only to
1198  * lock all the matching tuples, but also to conflict with new insertions
1199  * into the table. In an indexscan, we take page locks on the index pages
1200  * covering the range specified in the scan qual, but in a heap scan there
1201  * is nothing more fine-grained to lock. A bitmap scan is a different
1202  * story, there we have already scanned the index and locked the index
1203  * pages covering the predicate. But in that case we still have to lock
1204  * any matching heap tuples. For sample scan we could optimize the locking
1205  * to be at least page-level granularity, but we'd need to add per-tuple
1206  * locking for that.
1207  */
1209  {
1210  /*
1211  * Ensure a missing snapshot is noticed reliably, even if the
1212  * isolation mode means predicate locking isn't performed (and
1213  * therefore the snapshot isn't used here).
1214  */
1215  Assert(snapshot);
1216  PredicateLockRelation(relation, snapshot);
1217  }
1218 
1219  /* we only need to set this up once */
1220  scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
1221 
1222  /*
1223  * we do this here instead of in initscan() because heap_rescan also calls
1224  * initscan() and we don't want to allocate memory again
1225  */
1226  if (nkeys > 0)
1227  scan->rs_base.rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
1228  else
1229  scan->rs_base.rs_key = NULL;
1230 
1231  initscan(scan, key, false);
1232 
1233  return (TableScanDesc) scan;
1234 }
TableScanDescData rs_base
Definition: heapam.h:49
void PredicateLockRelation(Relation relation, Snapshot snapshot)
Definition: predicate.c:2569
uint32 rs_flags
Definition: relscan.h:43
struct HeapScanDescData * HeapScanDesc
Definition: heapam.h:73
HeapTupleData rs_ctup
Definition: heapam.h:66
ScanKeyData * ScanKey
Definition: skey.h:75
Oid t_tableOid
Definition: htup.h:66
struct ScanKeyData * rs_key
Definition: relscan.h:37
void * rs_private
Definition: relscan.h:45
void RelationIncrementReferenceCount(Relation rel)
Definition: relcache.c:2090
BufferAccessStrategy rs_strategy
Definition: heapam.h:64
#define IsMVCCSnapshot(snapshot)
Definition: snapmgr.h:97
#define Assert(condition)
Definition: c.h:792
Relation rs_rd
Definition: relscan.h:34
struct SnapshotData * rs_snapshot
Definition: relscan.h:35
void * palloc(Size size)
Definition: mcxt.c:950
struct ParallelTableScanDescData * rs_parallel
Definition: relscan.h:46
static void initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
Definition: heapam.c:227
#define RelationGetRelid(relation)
Definition: rel.h:457

◆ 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.

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, HeapTupleHeaderIndicatesMovedPartitions, 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().

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 combocid.
2538  * Other workers might need that combocid 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  /*
2562  * If we didn't pin the visibility map page and the page has become all
2563  * visible while we were busy locking the buffer, we'll have to unlock and
2564  * re-lock, to avoid holding the buffer lock across an I/O. That's a bit
2565  * unfortunate, but hopefully shouldn't happen often.
2566  */
2567  if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
2568  {
2569  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2570  visibilitymap_pin(relation, block, &vmbuffer);
2572  }
2573 
2574  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
2575  Assert(ItemIdIsNormal(lp));
2576 
2577  tp.t_tableOid = RelationGetRelid(relation);
2578  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2579  tp.t_len = ItemIdGetLength(lp);
2580  tp.t_self = *tid;
2581 
2582 l1:
2583  result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
2584 
2585  if (result == TM_Invisible)
2586  {
2587  UnlockReleaseBuffer(buffer);
2588  ereport(ERROR,
2589  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2590  errmsg("attempted to delete invisible tuple")));
2591  }
2592  else if (result == TM_BeingModified && wait)
2593  {
2594  TransactionId xwait;
2595  uint16 infomask;
2596 
2597  /* must copy state data before unlocking buffer */
2598  xwait = HeapTupleHeaderGetRawXmax(tp.t_data);
2599  infomask = tp.t_data->t_infomask;
2600 
2601  /*
2602  * Sleep until concurrent transaction ends -- except when there's a
2603  * single locker and it's our own transaction. Note we don't care
2604  * which lock mode the locker has, because we need the strongest one.
2605  *
2606  * Before sleeping, we need to acquire tuple lock to establish our
2607  * priority for the tuple (see heap_lock_tuple). LockTuple will
2608  * release us when we are next-in-line for the tuple.
2609  *
2610  * If we are forced to "start over" below, we keep the tuple lock;
2611  * this arranges that we stay at the head of the line while rechecking
2612  * tuple state.
2613  */
2614  if (infomask & HEAP_XMAX_IS_MULTI)
2615  {
2616  bool current_is_member = false;
2617 
2618  if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
2619  LockTupleExclusive, &current_is_member))
2620  {
2621  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2622 
2623  /*
2624  * Acquire the lock, if necessary (but skip it when we're
2625  * requesting a lock and already have one; avoids deadlock).
2626  */
2627  if (!current_is_member)
2629  LockWaitBlock, &have_tuple_lock);
2630 
2631  /* wait for multixact */
2633  relation, &(tp.t_self), XLTW_Delete,
2634  NULL);
2636 
2637  /*
2638  * If xwait had just locked the tuple then some other xact
2639  * could update this tuple before we get to this point. Check
2640  * for xmax change, and start over if so.
2641  */
2642  if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
2644  xwait))
2645  goto l1;
2646  }
2647 
2648  /*
2649  * You might think the multixact is necessarily done here, but not
2650  * so: it could have surviving members, namely our own xact or
2651  * other subxacts of this backend. It is legal for us to delete
2652  * the tuple in either case, however (the latter case is
2653  * essentially a situation of upgrading our former shared lock to
2654  * exclusive). We don't bother changing the on-disk hint bits
2655  * since we are about to overwrite the xmax altogether.
2656  */
2657  }
2658  else if (!TransactionIdIsCurrentTransactionId(xwait))
2659  {
2660  /*
2661  * Wait for regular transaction to end; but first, acquire tuple
2662  * lock.
2663  */
2664  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2666  LockWaitBlock, &have_tuple_lock);
2667  XactLockTableWait(xwait, relation, &(tp.t_self), XLTW_Delete);
2669 
2670  /*
2671  * xwait is done, but if xwait had just locked the tuple then some
2672  * other xact could update this tuple before we get to this point.
2673  * Check for xmax change, and start over if so.
2674  */
2675  if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
2677  xwait))
2678  goto l1;
2679 
2680  /* Otherwise check if it committed or aborted */
2681  UpdateXmaxHintBits(tp.t_data, buffer, xwait);
2682  }
2683 
2684  /*
2685  * We may overwrite if previous xmax aborted, or if it committed but
2686  * only locked the tuple without updating it.
2687  */
2688  if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
2691  result = TM_Ok;
2692  else if (!ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid) ||
2694  result = TM_Updated;
2695  else
2696  result = TM_Deleted;
2697  }
2698 
2699  if (crosscheck != InvalidSnapshot && result == TM_Ok)
2700  {
2701  /* Perform additional check for transaction-snapshot mode RI updates */
2702  if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
2703  result = TM_Updated;
2704  }
2705 
2706  if (result != TM_Ok)
2707  {
2708  Assert(result == TM_SelfModified ||
2709  result == TM_Updated ||
2710  result == TM_Deleted ||
2711  result == TM_BeingModified);
2713  Assert(result != TM_Updated ||
2714  !ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid));
2715  tmfd->ctid = tp.t_data->t_ctid;
2717  if (result == TM_SelfModified)
2718  tmfd->cmax = HeapTupleHeaderGetCmax(tp.t_data);
2719  else
2720  tmfd->cmax = InvalidCommandId;
2721  UnlockReleaseBuffer(buffer);
2722  if (have_tuple_lock)
2723  UnlockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive);
2724  if (vmbuffer != InvalidBuffer)
2725  ReleaseBuffer(vmbuffer);
2726  return result;
2727  }
2728 
2729  /*
2730  * We're about to do the actual delete -- check for conflict first, to
2731  * avoid possibly having to roll back work we've just done.
2732  *
2733  * This is safe without a recheck as long as there is no possibility of
2734  * another process scanning the page between this check and the delete
2735  * being visible to the scan (i.e., an exclusive buffer content lock is
2736  * continuously held from this point until the tuple delete is visible).
2737  */
2738  CheckForSerializableConflictIn(relation, tid, BufferGetBlockNumber(buffer));
2739 
2740  /* replace cid with a combo cid if necessary */
2741  HeapTupleHeaderAdjustCmax(tp.t_data, &cid, &iscombo);
2742 
2743  /*
2744  * Compute replica identity tuple before entering the critical section so
2745  * we don't PANIC upon a memory allocation failure.
2746  */
2747  old_key_tuple = ExtractReplicaIdentity(relation, &tp, true, &old_key_copied);
2748 
2749  /*
2750  * If this is the first possibly-multixact-able operation in the current
2751  * transaction, set my per-backend OldestMemberMXactId setting. We can be
2752  * certain that the transaction will never become a member of any older
2753  * MultiXactIds than that. (We have to do this even if we end up just
2754  * using our own TransactionId below, since some other backend could
2755  * incorporate our XID into a MultiXact immediately afterwards.)
2756  */
2758 
2761  xid, LockTupleExclusive, true,
2762  &new_xmax, &new_infomask, &new_infomask2);
2763 
2765 
2766  /*
2767  * If this transaction commits, the tuple will become DEAD sooner or
2768  * later. Set flag that this page is a candidate for pruning once our xid
2769  * falls below the OldestXmin horizon. If the transaction finally aborts,
2770  * the subsequent page pruning will be a no-op and the hint will be
2771  * cleared.
2772  */
2773  PageSetPrunable(page, xid);
2774 
2775  if (PageIsAllVisible(page))
2776  {
2777  all_visible_cleared = true;
2778  PageClearAllVisible(page);
2779  visibilitymap_clear(relation, BufferGetBlockNumber(buffer),
2780  vmbuffer, VISIBILITYMAP_VALID_BITS);
2781  }
2782 
2783  /* store transaction information of xact deleting the tuple */
2786  tp.t_data->t_infomask |= new_infomask;
2787  tp.t_data->t_infomask2 |= new_infomask2;
2789  HeapTupleHeaderSetXmax(tp.t_data, new_xmax);
2790  HeapTupleHeaderSetCmax(tp.t_data, cid, iscombo);
2791  /* Make sure there is no forward chain link in t_ctid */
2792  tp.t_data->t_ctid = tp.t_self;
2793 
2794  /* Signal that this is actually a move into another partition */
2795  if (changingPart)
2797 
2798  MarkBufferDirty(buffer);
2799 
2800  /*
2801  * XLOG stuff
2802  *
2803  * NB: heap_abort_speculative() uses the same xlog record and replay
2804  * routines.
2805  */
2806  if (RelationNeedsWAL(relation))
2807  {
2808  xl_heap_delete xlrec;
2809  xl_heap_header xlhdr;
2810  XLogRecPtr recptr;
2811 
2812  /* For logical decode we need combocids to properly decode the catalog */
2814  log_heap_new_cid(relation, &tp);
2815 
2816  xlrec.flags = 0;
2817  if (all_visible_cleared)
2819  if (changingPart)
2822  tp.t_data->t_infomask2);
2824  xlrec.xmax = new_xmax;
2825 
2826  if (old_key_tuple != NULL)
2827  {
2828  if (relation->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
2830  else
2832  }
2833 
2834  XLogBeginInsert();
2835  XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
2836 
2837  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
2838 
2839  /*
2840  * Log replica identity of the deleted tuple if there is one
2841  */
2842  if (old_key_tuple != NULL)
2843  {
2844  xlhdr.t_infomask2 = old_key_tuple->t_data->t_infomask2;
2845  xlhdr.t_infomask = old_key_tuple->t_data->t_infomask;
2846  xlhdr.t_hoff = old_key_tuple->t_data->t_hoff;
2847 
2848  XLogRegisterData((char *) &xlhdr, SizeOfHeapHeader);
2849  XLogRegisterData((char *) old_key_tuple->t_data
2851  old_key_tuple->t_len
2853  }
2854 
2855  /* filtering by origin on a row level is much more efficient */
2857 
2858  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
2859 
2860  PageSetLSN(page, recptr);
2861  }
2862 
2863  END_CRIT_SECTION();
2864 
2865  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2866 
2867  if (vmbuffer != InvalidBuffer)
2868  ReleaseBuffer(vmbuffer);
2869 
2870  /*
2871  * If the tuple has toasted out-of-line attributes, we need to delete
2872  * those items too. We have to do this before releasing the buffer
2873  * because we need to look at the contents of the tuple, but it's OK to
2874  * release the content lock on the buffer first.
2875  */
2876  if (relation->rd_rel->relkind != RELKIND_RELATION &&
2877  relation->rd_rel->relkind != RELKIND_MATVIEW)
2878  {
2879  /* toast table entries should never be recursively toasted */
2881  }
2882  else if (HeapTupleHasExternal(&tp))
2883  heap_toast_delete(relation, &tp, false);
2884 
2885  /*
2886  * Mark tuple for invalidation from system caches at next command
2887  * boundary. We have to do this before releasing the buffer because we
2888  * need to look at the contents of the tuple.
2889  */
2890  CacheInvalidateHeapTuple(relation, &tp, NULL);
2891 
2892  /* Now we can release the buffer */
2893  ReleaseBuffer(buffer);
2894 
2895  /*
2896  * Release the lmgr tuple lock, if we had it.
2897  */
2898  if (have_tuple_lock)
2899  UnlockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive);
2900 
2901  pgstat_count_heap_delete(relation);
2902 
2903  if (old_key_tuple != NULL && old_key_copied)
2904  heap_freetuple(old_key_tuple);
2905 
2906  return TM_Ok;
2907 }
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:365
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
ItemPointerData ctid
Definition: tableam.h:125
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
#define SizeofHeapTupleHeader
Definition: htup_details.h:184
static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup)
Definition: heapam.c:8107
#define HEAP_XMAX_BITS
Definition: htup_details.h:270
static uint8 compute_infobits(uint16 infomask, uint16 infomask2)
Definition: heapam.c:2468
void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple)
Definition: inval.c:1122
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:575
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:869
void visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *buf)
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_changed, bool *copy)
Definition: heapam.c:8189
static bool xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
Definition: heapam.c:2490
#define HeapTupleHeaderClearHotUpdated(tup)
Definition: htup_details.h:496
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
CommandId cmax
Definition: tableam.h:127
bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
#define InvalidBuffer
Definition: buf.h:25
uint16 t_infomask2
Definition: heapam_xlog.h:146
#define PageSetPrunable(page, xid)
Definition: bufpage.h:392
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
int errcode(int sqlerrcode)
Definition: elog.c:704
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:238
#define HeapTupleHeaderIndicatesMovedPartitions(tup)
Definition: htup_details.h:445
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3700
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
Form_pg_class rd_rel
Definition: rel.h:110
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
TM_Result HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, Buffer buffer)
#define UnlockTupleTuplock(rel, tup, mode)
Definition: heapam.c:165
OffsetNumber offnum
Definition: heapam_xlog.h:110
void MultiXactIdSetOldestMember(void)
Definition: multixact.c:625
#define VISIBILITYMAP_VALID_BITS
Definition: visibilitymap.h:28
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderGetRawXmax(tup)
Definition: htup_details.h:375
unsigned short uint16
Definition: c.h:428
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
bool IsInParallelMode(void)
Definition: xact.c:1012
bool visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer buf, uint8 flags)
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3723
TransactionId xmax
Definition: tableam.h:126
#define ERROR
Definition: elog.h:45
#define HEAP_XMAX_INVALID
Definition: htup_details.h:207
ItemPointerData t_ctid
Definition: htup_details.h:160
#define HeapTupleHeaderSetMovedPartitions(tup)
Definition: htup_details.h:449
ItemPointerData t_self
Definition: htup.h:65
TransactionId xmax
Definition: heapam_xlog.h:109
static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, ItemPointer ctid, XLTW_Oper oper, int *remaining)
Definition: heapam.c:6808
void heap_toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
Definition: heaptoast.c:43
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:438
uint32 t_len
Definition: htup.h:64
#define SizeOfHeapDelete
Definition: heapam_xlog.h:115
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define XLH_DELETE_CONTAINS_OLD_KEY
Definition: heapam_xlog.h:98
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:380
Oid t_tableOid
Definition: htup.h:66
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:404
#define HeapTupleHeaderSetCmax(tup, cid, iscombo)
Definition: htup_details.h:405
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
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:4783
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
#define InvalidSnapshot
Definition: snapshot.h:123
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
TM_Result
Definition: tableam.h:70
#define RelationIsAccessibleInLogicalDecoding(relation)
Definition: rel.h:620
#define InvalidCommandId
Definition: c.h:592
#define HEAP_XMAX_IS_LOCKED_ONLY(infomask)
Definition: htup_details.h:230
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3939
#define HEAP_KEYS_UPDATED
Definition: htup_details.h:278
#define HEAP_XMAX_IS_MULTI
Definition: htup_details.h:208
void CheckForSerializableConflictIn(Relation relation, ItemPointer tid, BlockNumber blkno)
Definition: predicate.c:4446
static void UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
Definition: heapam.c:1802
#define HEAP_MOVED
Definition: htup_details.h:216
static bool heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode, LockWaitPolicy wait_policy, bool *have_tuple_lock)
Definition: heapam.c:4734
#define ereport(elevel,...)
Definition: elog.h:155
TransactionId MultiXactId
Definition: c.h:585
#define PageClearAllVisible(page)
Definition: bufpage.h:389
void XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, XLTW_Oper oper)
Definition: lmgr.c:639
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:792
uint8 infobits_set
Definition: heapam_xlog.h:111
CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup)
Definition: combocid.c:118
Definition: tableam.h:76
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:619
uint16 t_infomask
Definition: heapam_xlog.h:147
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask, LockTupleMode lockmode, bool *current_is_member)
Definition: heapam.c:6631
#define RelationNeedsWAL(relation)
Definition: rel.h:563
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
void pgstat_count_heap_delete(Relation rel)
Definition: pgstat.c:2221
void HeapTupleHeaderAdjustCmax(HeapTupleHeader tup, CommandId *cmax, bool *iscombo)
Definition: combocid.c:153
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2674
#define HeapTupleHasExternal(tuple)
Definition: htup_details.h:673
int errmsg(const char *fmt,...)
Definition: elog.c:915
#define XLH_DELETE_ALL_VISIBLE_CLEARED
Definition: heapam_xlog.h:96
#define XLH_DELETE_IS_PARTITION_MOVE
Definition: heapam_xlog.h:100
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define XLOG_HEAP_DELETE
Definition: heapam_xlog.h:33
#define RelationGetRelid(relation)
Definition: rel.h:457
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
#define SizeOfHeapHeader
Definition: heapam_xlog.h:151
Pointer Page
Definition: bufpage.h:78
bool HeapTupleSatisfiesVisibility(HeapTuple tup, Snapshot snapshot, Buffer buffer)
#define XLH_DELETE_CONTAINS_OLD_TUPLE
Definition: heapam_xlog.h:97

◆ heap_endscan()

void heap_endscan ( TableScanDesc  scan)

Definition at line 1274 of file heapam.c.

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

Referenced by SampleHeapTupleVisible().

1275 {
1276  HeapScanDesc scan = (HeapScanDesc) sscan;
1277 
1278  /* Note: no locking manipulations needed */
1279 
1280  /*
1281  * unpin scan buffers
1282  */
1283  if (BufferIsValid(scan->rs_cbuf))
1284  ReleaseBuffer(scan->rs_cbuf);
1285 
1286  /*
1287  * decrement relation reference count and free scan descriptor storage
1288  */
1290 
1291  if (scan->rs_base.rs_key)
1292  pfree(scan->rs_base.rs_key);
1293 
1294  if (scan->rs_strategy != NULL)
1296 
1297  if (scan->rs_base.rs_flags & SO_TEMP_SNAPSHOT)
1299 
1300  pfree(scan);
1301 }
TableScanDescData rs_base
Definition: heapam.h:49
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3700
uint32 rs_flags
Definition: relscan.h:43
struct HeapScanDescData * HeapScanDesc
Definition: heapam.h:73
void pfree(void *pointer)
Definition: mcxt.c:1057
void RelationDecrementReferenceCount(Relation rel)
Definition: relcache.c:2103
struct ScanKeyData * rs_key
Definition: relscan.h:37
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:852
BufferAccessStrategy rs_strategy
Definition: heapam.h:64
Buffer rs_cbuf
Definition: heapam.h:60
void FreeAccessStrategy(BufferAccessStrategy strategy)
Definition: freelist.c:597
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
Relation rs_rd
Definition: relscan.h:34
struct SnapshotData * rs_snapshot
Definition: relscan.h:35

◆ heap_fetch()

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

Definition at line 1412 of file heapam.c.

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, HeapTupleData::t_tableOid, and TestForOldSnapshot().

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

1416 {
1417  ItemPointer tid = &(tuple->t_self);
1418  ItemId lp;
1419  Buffer buffer;
1420  Page page;
1421  OffsetNumber offnum;
1422  bool valid;
1423 
1424  /*
1425  * Fetch and pin the appropriate page of the relation.
1426  */
1427  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
1428 
1429  /*
1430  * Need share lock on buffer to examine tuple commit status.
1431  */
1432  LockBuffer(buffer, BUFFER_LOCK_SHARE);
1433  page = BufferGetPage(buffer);
1434  TestForOldSnapshot(snapshot, relation, page);
1435 
1436  /*
1437  * We'd better check for out-of-range offnum in case of VACUUM since the
1438  * TID was obtained.
1439  */
1440  offnum = ItemPointerGetOffsetNumber(tid);
1441  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
1442  {
1443  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1444  ReleaseBuffer(buffer);
1445  *userbuf = InvalidBuffer;
1446  tuple->t_data = NULL;
1447  return false;
1448  }
1449 
1450  /*
1451  * get the item line pointer corresponding to the requested tid
1452  */
1453  lp = PageGetItemId(page, offnum);
1454 
1455  /*
1456  * Must check for deleted tuple.
1457  */
1458  if (!ItemIdIsNormal(lp))
1459  {
1460  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1461  ReleaseBuffer(buffer);
1462  *userbuf = InvalidBuffer;
1463  tuple->t_data = NULL;
1464  return false;
1465  }
1466 
1467  /*
1468  * fill in *tuple fields
1469  */
1470  tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
1471  tuple->t_len = ItemIdGetLength(lp);
1472  tuple->t_tableOid = RelationGetRelid(relation);
1473 
1474  /*
1475  * check tuple visibility, then release lock
1476  */
1477  valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
1478 
1479  if (valid)
1480  PredicateLockTID(relation, &(tuple->t_self), snapshot,
1481  HeapTupleHeaderGetXmin(tuple->t_data));
1482 
1483  HeapCheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
1484 
1485  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1486 
1487  if (valid)
1488  {
1489  /*
1490  * All checks passed, so return the tuple as valid. Caller is now
1491  * responsible for releasing the buffer.
1492  */
1493  *userbuf = buffer;
1494 
1495  return true;
1496  }
1497 
1498  /* Tuple failed time qual */
1499  ReleaseBuffer(buffer);
1500  *userbuf = InvalidBuffer;
1501 
1502  return false;
1503 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
static void TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page)
Definition: bufmgr.h:277
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define InvalidBuffer
Definition: buf.h:25
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3700
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
HeapTupleHeader t_data
Definition: htup.h:68
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
Oid t_tableOid
Definition: htup.h:66
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3939
void PredicateLockTID(Relation relation, ItemPointer tid, Snapshot snapshot, TransactionId tuple_xid)
Definition: predicate.c:2614
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition: heapam.c:9615
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:619
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:97
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
int Buffer
Definition: buf.h:23
#define RelationGetRelid(relation)
Definition: rel.h:457
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
bool HeapTupleSatisfiesVisibility(HeapTuple tup, Snapshot snapshot, Buffer buffer)

◆ heap_finish_speculative()

void heap_finish_speculative ( Relation  relation,
ItemPointer  tid 
)

Definition at line 5546 of file heapam.c.

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

Referenced by heapam_tuple_complete_speculative().

5547 {
5548  Buffer buffer;
5549  Page page;
5550  OffsetNumber offnum;
5551  ItemId lp = NULL;
5552  HeapTupleHeader htup;
5553 
5554  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
5556  page = (Page) BufferGetPage(buffer);
5557 
5558  offnum = ItemPointerGetOffsetNumber(tid);
5559  if (PageGetMaxOffsetNumber(page) >= offnum)
5560  lp = PageGetItemId(page, offnum);
5561 
5562  if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
5563  elog(ERROR, "invalid lp");
5564 
5565  htup = (HeapTupleHeader) PageGetItem(page, lp);
5566 
5567  /* SpecTokenOffsetNumber should be distinguishable from any real offset */
5569  "invalid speculative token constant");
5570 
5571  /* NO EREPORT(ERROR) from here till changes are logged */
5573 
5575 
5576  MarkBufferDirty(buffer);
5577 
5578  /*
5579  * Replace the speculative insertion token with a real t_ctid, pointing to
5580  * itself like it does on regular tuples.
5581  */
5582  htup->t_ctid = *tid;
5583 
5584  /* XLOG stuff */
5585  if (RelationNeedsWAL(relation))
5586  {
5587  xl_heap_confirm xlrec;
5588  XLogRecPtr recptr;
5589 
5590  xlrec.offnum = ItemPointerGetOffsetNumber(tid);
5591 
5592  XLogBeginInsert();
5593 
5594  /* We want the same filtering on this as on a plain insert */
5596 
5597  XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
5598  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
5599 
5600  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
5601 
5602  PageSetLSN(page, recptr);
5603  }
5604 
5605  END_CRIT_SECTION();
5606 
5607  UnlockReleaseBuffer(buffer);
5608 }
OffsetNumber offnum
Definition: heapam_xlog.h:298
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define MaxOffsetNumber
Definition: off.h:28
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
#define HeapTupleHeaderIsSpeculative(tup)
Definition: htup_details.h:429
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:238
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
#define SpecTokenOffsetNumber
Definition: itemptr.h:63
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
#define StaticAssertStmt(condition, errmessage)
Definition: c.h:906
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3723
#define ERROR
Definition: elog.h:45
ItemPointerData t_ctid
Definition: htup_details.h:160
#define REGBUF_STANDARD
Definition: xloginsert.h:35
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:404
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define SizeOfHeapConfirm
Definition: heapam_xlog.h:301
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3939
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:792
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:619
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define RelationNeedsWAL(relation)
Definition: rel.h:563
#define elog(elevel,...)
Definition: elog.h:228
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
#define XLOG_HEAP_CONFIRM
Definition: heapam_xlog.h:37

◆ heap_freeze_tuple()

bool heap_freeze_tuple ( HeapTupleHeader  tuple,
TransactionId  relfrozenxid,
TransactionId  relminmxid,
TransactionId  cutoff_xid,
TransactionId  cutoff_multi 
)

Definition at line 6451 of file heapam.c.

References heap_execute_freeze_tuple(), and heap_prepare_freeze_tuple().

Referenced by rewrite_heap_tuple().

6454 {
6456  bool do_freeze;
6457  bool tuple_totally_frozen;
6458 
6459  do_freeze = heap_prepare_freeze_tuple(tuple,
6460  relfrozenxid, relminmxid,
6461  cutoff_xid, cutoff_multi,
6462  &frz, &tuple_totally_frozen);
6463 
6464  /*
6465  * Note that because this is not a WAL-logged operation, we don't need to
6466  * fill in the offset in the freeze record.
6467  */
6468 
6469  if (do_freeze)
6470  heap_execute_freeze_tuple(tuple, &frz);
6471  return do_freeze;
6472 }
bool heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId relfrozenxid, TransactionId relminmxid, TransactionId cutoff_xid, TransactionId cutoff_multi, xl_heap_freeze_tuple *frz, bool *totally_frozen_p)
Definition: heapam.c:6201
void heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
Definition: heapam.c:6430

◆ heap_get_latest_tid()

void heap_get_latest_tid ( TableScanDesc  scan,
ItemPointer  tid 
)

Definition at line 1679 of file heapam.c.

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, TestForOldSnapshot(), TransactionIdEquals, TransactionIdIsValid, and UnlockReleaseBuffer().

Referenced by SampleHeapTupleVisible().

1681 {
1682  Relation relation = sscan->rs_rd;
1683  Snapshot snapshot = sscan->rs_snapshot;
1684  ItemPointerData ctid;
1685  TransactionId priorXmax;
1686 
1687  /*
1688  * table_tuple_get_latest_tid() verified that the passed in tid is valid.
1689  * Assume that t_ctid links are valid however - there shouldn't be invalid
1690  * ones in the table.
1691  */
1692  Assert(ItemPointerIsValid(tid));
1693 
1694  /*
1695  * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
1696  * need to examine, and *tid is the TID we will return if ctid turns out
1697  * to be bogus.
1698  *
1699  * Note that we will loop until we reach the end of the t_ctid chain.
1700  * Depending on the snapshot passed, there might be at most one visible
1701  * version of the row, but we don't try to optimize for that.
1702  */
1703  ctid = *tid;
1704  priorXmax = InvalidTransactionId; /* cannot check first XMIN */
1705  for (;;)
1706  {
1707  Buffer buffer;
1708  Page page;
1709  OffsetNumber offnum;
1710  ItemId lp;
1711  HeapTupleData tp;
1712  bool valid;
1713 
1714  /*
1715  * Read, pin, and lock the page.
1716  */
1717  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
1718  LockBuffer(buffer, BUFFER_LOCK_SHARE);
1719  page = BufferGetPage(buffer);
1720  TestForOldSnapshot(snapshot, relation, page);
1721 
1722  /*
1723  * Check for bogus item number. This is not treated as an error
1724  * condition because it can happen while following a t_ctid link. We
1725  * just assume that the prior tid is OK and return it unchanged.
1726  */
1727  offnum = ItemPointerGetOffsetNumber(&ctid);
1728  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
1729  {
1730  UnlockReleaseBuffer(buffer);
1731  break;
1732  }
1733  lp = PageGetItemId(page, offnum);
1734  if (!ItemIdIsNormal(lp))
1735  {
1736  UnlockReleaseBuffer(buffer);
1737  break;
1738  }
1739 
1740  /* OK to access the tuple */
1741  tp.t_self = ctid;
1742  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
1743  tp.t_len = ItemIdGetLength(lp);
1744  tp.t_tableOid = RelationGetRelid(relation);
1745 
1746  /*
1747  * After following a t_ctid link, we might arrive at an unrelated
1748  * tuple. Check for XMIN match.
1749  */
1750  if (TransactionIdIsValid(priorXmax) &&
1752  {
1753  UnlockReleaseBuffer(buffer);
1754  break;
1755  }
1756 
1757  /*
1758  * Check tuple visibility; if visible, set it as the new result
1759  * candidate.
1760  */
1761  valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
1762  HeapCheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
1763  if (valid)
1764  *tid = ctid;
1765 
1766  /*
1767  * If there's a valid t_ctid link, follow it, else we're done.
1768  */
1769  if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
1773  {
1774  UnlockReleaseBuffer(buffer);
1775  break;
1776  }
1777 
1778  ctid = tp.t_data->t_ctid;
1779  priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
1780  UnlockReleaseBuffer(buffer);
1781  } /* end of loop */
1782 }
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:365
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
static void TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page)
Definition: bufmgr.h:277
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
uint32 TransactionId
Definition: c.h:575
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
#define HeapTupleHeaderIndicatesMovedPartitions(tup)
Definition: htup_details.h:445
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
HeapTupleHeader t_data
Definition: htup.h:68
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3723
#define HEAP_XMAX_INVALID
Definition: htup_details.h:207
ItemPointerData t_ctid
Definition: htup_details.h:160
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
#define InvalidTransactionId
Definition: transam.h:31
Oid t_tableOid
Definition: htup.h:66
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3939
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition: heapam.c:9615
#define Assert(condition)
Definition: c.h:792
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:619
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:97
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define TransactionIdIsValid(xid)
Definition: transam.h:41
int Buffer
Definition: buf.h:23
#define RelationGetRelid(relation)
Definition: rel.h:457
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
bool HeapTupleSatisfiesVisibility(HeapTuple tup, Snapshot snapshot, Buffer buffer)

◆ heap_get_root_tuples()

void heap_get_root_tuples ( Page  page,
OffsetNumber root_offsets 
)

Definition at line 883 of file pruneheap.c.

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().

884 {
885  OffsetNumber offnum,
886  maxoff;
887 
888  MemSet(root_offsets, InvalidOffsetNumber,
890 
891  maxoff = PageGetMaxOffsetNumber(page);
892  for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
893  {
894  ItemId lp = PageGetItemId(page, offnum);
895  HeapTupleHeader htup;
896  OffsetNumber nextoffnum;
897  TransactionId priorXmax;
898 
899  /* skip unused and dead items */
900  if (!ItemIdIsUsed(lp) || ItemIdIsDead(lp))
901  continue;
902 
903  if (ItemIdIsNormal(lp))
904  {
905  htup = (HeapTupleHeader) PageGetItem(page, lp);
906 
907  /*
908  * Check if this tuple is part of a HOT-chain rooted at some other
909  * tuple. If so, skip it for now; we'll process it when we find
910  * its root.
911  */
912  if (HeapTupleHeaderIsHeapOnly(htup))
913  continue;
914 
915  /*
916  * This is either a plain tuple or the root of a HOT-chain.
917  * Remember it in the mapping.
918  */
919  root_offsets[offnum - 1] = offnum;
920 
921  /* If it's not the start of a HOT-chain, we're done with it */
922  if (!HeapTupleHeaderIsHotUpdated(htup))
923  continue;
924 
925  /* Set up to scan the HOT-chain */
926  nextoffnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
927  priorXmax = HeapTupleHeaderGetUpdateXid(htup);
928  }
929  else
930  {
931  /* Must be a redirect item. We do not set its root_offsets entry */
933  /* Set up to scan the HOT-chain */
934  nextoffnum = ItemIdGetRedirect(lp);
935  priorXmax = InvalidTransactionId;
936  }
937 
938  /*
939  * Now follow the HOT-chain and collect other tuples in the chain.
940  *
941  * Note: Even though this is a nested loop, the complexity of the
942  * function is O(N) because a tuple in the page should be visited not
943  * more than twice, once in the outer loop and once in HOT-chain
944  * chases.
945  */
946  for (;;)
947  {
948  lp = PageGetItemId(page, nextoffnum);
949 
950  /* Check for broken chains */
951  if (!ItemIdIsNormal(lp))
952  break;
953 
954  htup = (HeapTupleHeader) PageGetItem(page, lp);
955 
956  if (TransactionIdIsValid(priorXmax) &&
957  !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(htup)))
958  break;
959 
960  /* Remember the root line pointer for this item */
961  root_offsets[nextoffnum - 1] = offnum;
962 
963  /* Advance to next chain member, if any */
964  if (!HeapTupleHeaderIsHotUpdated(htup))
965  break;
966 
967  /* HOT implies it can't have moved to different partition */
969 
970  nextoffnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
971  priorXmax = HeapTupleHeaderGetUpdateXid(htup);
972  }
973  }
974 }
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:365
#define ItemIdIsRedirected(itemId)
Definition: itemid.h:106
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
uint32 TransactionId
Definition: c.h:575
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define ItemIdGetRedirect(itemId)
Definition: itemid.h:78
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
#define MaxHeapTuplesPerPage
Definition: htup_details.h:574
#define MemSet(start, val, len)
Definition: c.h:996
#define HeapTupleHeaderIndicatesMovedPartitions(tup)
Definition: htup_details.h:445
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
#define HeapTupleHeaderIsHeapOnly(tup)
Definition: htup_details.h:501
ItemPointerData t_ctid
Definition: htup_details.h:160
#define FirstOffsetNumber
Definition: off.h:27
#define InvalidTransactionId
Definition: transam.h:31
#define HeapTupleHeaderIsHotUpdated(tup)
Definition: htup_details.h:484
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
#define InvalidOffsetNumber
Definition: off.h:26
#define Assert(condition)
Definition: c.h:792
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define TransactionIdIsValid(xid)
Definition: transam.h:41
#define PageGetItem(page, itemId)
Definition: bufpage.h:340

◆ heap_getnext()

HeapTuple heap_getnext ( TableScanDesc  scan,
ScanDirection  direction 
)

Definition at line 1304 of file heapam.c.

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(), createdb(), 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(), heapam_index_build_range_scan(), heapam_index_validate_scan(), index_update_stats(), objectsInSchemaToOids(), pgrowlocks(), pgstat_collect_oids(), pgstat_heap(), populate_typ_array(), ReindexMultipleTables(), remove_dbtablespaces(), RemoveSubscriptionRel(), RenameTableSpace(), ThereIsAtLeastOneRole(), and vac_truncate_clog().

1305 {
1306  HeapScanDesc scan = (HeapScanDesc) sscan;
1307 
1308  /*
1309  * This is still widely used directly, without going through table AM, so
1310  * add a safety check. It's possible we should, at a later point,
1311  * downgrade this to an assert. The reason for checking the AM routine,
1312  * rather than the AM oid, is that this allows to write regression tests
1313  * that create another AM reusing the heap handler.
1314  */
1315  if (unlikely(sscan->rs_rd->rd_tableam != GetHeapamTableAmRoutine()))
1316  ereport(ERROR,
1317  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1318  errmsg_internal("only heap AM is supported")));
1319 
1320  /*
1321  * We don't expect direct calls to heap_getnext with valid CheckXidAlive
1322  * for catalog or regular tables. See detailed comments in xact.c where
1323  * these variables are declared. Normally we have such a check at tableam
1324  * level API but this is called from many places so we need to ensure it
1325  * here.
1326  */
1328  elog(ERROR, "unexpected heap_getnext call during logical decoding");
1329 
1330  /* Note: no locking manipulations needed */
1331 
1332  if (scan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
1333  heapgettup_pagemode(scan, direction,
1334  scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
1335  else
1336  heapgettup(scan, direction,
1337  scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
1338 
1339  if (scan->rs_ctup.t_data == NULL)
1340  return NULL;
1341 
1342  /*
1343  * if we get here it means we have a new current scan tuple, so point to
1344  * the proper return buffer and return the tuple.
1345  */
1346 
1348 
1349  return &scan->rs_ctup;
1350 }
TableScanDescData rs_base
Definition: heapam.h:49
int errcode(int sqlerrcode)
Definition: elog.c:704
uint32 rs_flags
Definition: relscan.h:43
struct HeapScanDescData * HeapScanDesc
Definition: heapam.h:73
HeapTupleData rs_ctup
Definition: heapam.h:66
HeapTupleHeader t_data
Definition: htup.h:68
#define ERROR
Definition: elog.h:45
bool bsysscan
Definition: xact.c:96
struct ScanKeyData * rs_key
Definition: relscan.h:37
TransactionId CheckXidAlive
Definition: xact.c:95
static void heapgettup(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:506
#define ereport(elevel,...)
Definition: elog.h:155
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1002
Relation rs_rd
Definition: relscan.h:34
#define elog(elevel,...)
Definition: elog.h:228
#define unlikely(x)
Definition: c.h:261
#define pgstat_count_heap_getnext(rel)
Definition: pgstat.h:1533
#define TransactionIdIsValid(xid)
Definition: transam.h:41
static void heapgettup_pagemode(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:824
const TableAmRoutine * GetHeapamTableAmRoutine(void)

◆ heap_getnextslot()

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

Definition at line 1353 of file heapam.c.

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.

Referenced by SampleHeapTupleVisible().

1354 {
1355  HeapScanDesc scan = (HeapScanDesc) sscan;
1356 
1357  /* Note: no locking manipulations needed */
1358 
1359  if (sscan->rs_flags & SO_ALLOW_PAGEMODE)
1360  heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1361  else
1362  heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1363 
1364  if (scan->rs_ctup.t_data == NULL)
1365  {
1366  ExecClearTuple(slot);
1367  return false;
1368  }
1369 
1370  /*
1371  * if we get here it means we have a new current scan tuple, so point to
1372  * the proper return buffer and return the tuple.
1373  */
1374 
1376 
1377  ExecStoreBufferHeapTuple(&scan->rs_ctup, slot,
1378  scan->rs_cbuf);
1379  return true;
1380 }
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
TableScanDescData rs_base
Definition: heapam.h:49
uint32 rs_flags
Definition: relscan.h:43
struct HeapScanDescData * HeapScanDesc
Definition: heapam.h:73
HeapTupleData rs_ctup
Definition: heapam.h:66
HeapTupleHeader t_data
Definition: htup.h:68
struct ScanKeyData * rs_key
Definition: relscan.h:37
static void heapgettup(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:506
TupleTableSlot * ExecStoreBufferHeapTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer)
Definition: execTuples.c:1362
Buffer rs_cbuf
Definition: heapam.h:60
Relation rs_rd
Definition: relscan.h:34
#define pgstat_count_heap_getnext(rel)
Definition: pgstat.h:1533
static void heapgettup_pagemode(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:824

◆ 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 1527 of file heapam.c.

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().

1530 {
1531  Page dp = (Page) BufferGetPage(buffer);
1532  TransactionId prev_xmax = InvalidTransactionId;
1533  BlockNumber blkno;
1534  OffsetNumber offnum;
1535  bool at_chain_start;
1536  bool valid;
1537  bool skip;
1538  GlobalVisState *vistest = NULL;
1539 
1540  /* If this is not the first call, previous call returned a (live!) tuple */
1541  if (all_dead)
1542  *all_dead = first_call;
1543 
1544  blkno = ItemPointerGetBlockNumber(tid);
1545  offnum = ItemPointerGetOffsetNumber(tid);
1546  at_chain_start = first_call;
1547  skip = !first_call;
1548 
1549  /* XXX: we should assert that a snapshot is pushed or registered */
1551  Assert(BufferGetBlockNumber(buffer) == blkno);
1552 
1553  /* Scan through possible multiple members of HOT-chain */
1554  for (;;)
1555  {
1556  ItemId lp;
1557 
1558  /* check for bogus TID */
1559  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
1560  break;
1561 
1562  lp = PageGetItemId(dp, offnum);
1563 
1564  /* check for unused, dead, or redirected items */
1565  if (!ItemIdIsNormal(lp))
1566  {
1567  /* We should only see a redirect at start of chain */
1568  if (ItemIdIsRedirected(lp) && at_chain_start)
1569  {
1570  /* Follow the redirect */
1571  offnum = ItemIdGetRedirect(lp);
1572  at_chain_start = false;
1573  continue;
1574  }
1575  /* else must be end of chain */
1576  break;
1577  }
1578 
1579  /*
1580  * Update heapTuple to point to the element of the HOT chain we're
1581  * currently investigating. Having t_self set correctly is important
1582  * because the SSI checks and the *Satisfies routine for historical
1583  * MVCC snapshots need the correct tid to decide about the visibility.
1584  */
1585  heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
1586  heapTuple->t_len = ItemIdGetLength(lp);
1587  heapTuple->t_tableOid = RelationGetRelid(relation);
1588  ItemPointerSet(&heapTuple->t_self, blkno, offnum);
1589 
1590  /*
1591  * Shouldn't see a HEAP_ONLY tuple at chain start.
1592  */
1593  if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
1594  break;
1595 
1596  /*
1597  * The xmin should match the previous xmax value, else chain is
1598  * broken.
1599  */
1600  if (TransactionIdIsValid(prev_xmax) &&
1601  !TransactionIdEquals(prev_xmax,
1602  HeapTupleHeaderGetXmin(heapTuple->t_data)))
1603  break;
1604 
1605  /*
1606  * When first_call is true (and thus, skip is initially false) we'll
1607  * return the first tuple we find. But on later passes, heapTuple
1608  * will initially be pointing to the tuple we returned last time.
1609  * Returning it again would be incorrect (and would loop forever), so
1610  * we skip it and return the next match we find.
1611  */
1612  if (!skip)
1613  {
1614  /* If it's visible per the snapshot, we must return it */
1615  valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
1616  HeapCheckForSerializableConflictOut(valid, relation, heapTuple,
1617  buffer, snapshot);
1618 
1619  if (valid)
1620  {
1621  ItemPointerSetOffsetNumber(tid, offnum);
1622  PredicateLockTID(relation, &heapTuple->t_self, snapshot,
1623  HeapTupleHeaderGetXmin(heapTuple->t_data));
1624  if (all_dead)
1625  *all_dead = false;
1626  return true;
1627  }
1628  }
1629  skip = false;
1630 
1631  /*
1632  * If we can't see it, maybe no one else can either. At caller
1633  * request, check whether all chain members are dead to all
1634  * transactions.
1635  *
1636  * Note: if you change the criterion here for what is "dead", fix the
1637  * planner's get_actual_variable_range() function to match.
1638  */
1639  if (all_dead && *all_dead)
1640  {
1641  if (!vistest)
1642  vistest = GlobalVisTestFor(relation);
1643 
1644  if (!HeapTupleIsSurelyDead(heapTuple, vistest))
1645  *all_dead = false;
1646  }
1647 
1648  /*
1649  * Check to see if HOT chain continues past this tuple; if so fetch
1650  * the next offnum and loop around.
1651  */
1652  if (HeapTupleIsHotUpdated(heapTuple))
1653  {
1655  blkno);
1656  offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
1657  at_chain_start = false;
1658  prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
1659  }
1660  else
1661  break; /* end of chain */
1662  }
1663 
1664  return false;
1665 }
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:365
#define ItemIdIsRedirected(itemId)
Definition: itemid.h:106
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
uint32 TransactionId
Definition: c.h:575
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define ItemIdGetRedirect(itemId)
Definition: itemid.h:78
static const struct exclude_list_item skip[]
Definition: pg_checksums.c:112
TransactionId RecentXmin
Definition: snapmgr.c:113
uint32 BlockNumber
Definition: block.h:31
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleIsHotUpdated(tuple)
Definition: htup_details.h:676
GlobalVisState * GlobalVisTestFor(Relation rel)
Definition: procarray.c:3929
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
ItemPointerData t_ctid
Definition: htup_details.h:160
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
#define InvalidTransactionId
Definition: transam.h:31
Oid t_tableOid
Definition: htup.h:66
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void PredicateLockTID(Relation relation, ItemPointer tid, Snapshot snapshot, TransactionId tuple_xid)
Definition: predicate.c:2614
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition: heapam.c:9615
#define HeapTupleIsHeapOnly(tuple)
Definition: htup_details.h:685
#define Assert(condition)
Definition: c.h:792
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define ItemPointerSetOffsetNumber(pointer, offsetNumber)
Definition: itemptr.h:148
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2674
bool HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define TransactionIdIsValid(xid)
Definition: transam.h:41
#define RelationGetRelid(relation)
Definition: rel.h:457
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
#define ItemPointerSet(pointer, blockNumber, offNum)
Definition: itemptr.h:127
bool HeapTupleSatisfiesVisibility(HeapTuple tup, Snapshot snapshot, Buffer buffer)

◆ heap_index_delete_tuples()

TransactionId heap_index_delete_tuples ( Relation  rel,
TM_IndexDeleteOp delstate 
)

Definition at line 7068 of file heapam.c.

References Assert, TM_IndexDeleteOp::bottomup, BOTTOMUP_MAX_NBLOCKS, bottomup_sort_and_shrink(), TM_IndexDeleteOp::bottomupfreespace, buf, BUFFER_LOCK_SHARE, BufferGetPage, BufferIsValid, TM_IndexDeleteOp::deltids, TM_IndexStatus::freespace, get_tablespace_maintenance_io_concurrency(), GlobalVisTestFor(), heap_hot_search_buffer(), HeapTupleHeaderAdvanceLatestRemovedXid(), HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleHeaderIsHotUpdated, i, TM_IndexDelete::id, 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().

Referenced by SampleHeapTupleVisible().

7069 {
7070  /* Initial assumption is that earlier pruning took care of conflict */
7071  TransactionId latestRemovedXid = InvalidTransactionId;
7074  Page page = NULL;
7076  TransactionId priorXmax;
7077 #ifdef USE_PREFETCH
7078  IndexDeletePrefetchState prefetch_state;
7079  int prefetch_distance;
7080 #endif
7081  SnapshotData SnapshotNonVacuumable;
7082  int finalndeltids = 0,
7083  nblocksaccessed = 0;
7084 
7085  /* State that's only used in bottom-up index deletion case */
7086  int nblocksfavorable = 0;
7087  int curtargetfreespace = delstate->bottomupfreespace,
7088  lastfreespace = 0,
7089  actualfreespace = 0;
7090  bool bottomup_final_block = false;
7091 
7092  InitNonVacuumableSnapshot(SnapshotNonVacuumable, GlobalVisTestFor(rel));
7093 
7094  /* Sort caller's deltids array by TID for further processing */
7095  index_delete_sort(delstate);
7096 
7097  /*
7098  * Bottom-up case: resort deltids array in an order attuned to where the
7099  * greatest number of promising TIDs are to be found, and determine how
7100  * many blocks from the start of sorted array should be considered
7101  * favorable. This will also shrink the deltids array in order to
7102  * eliminate completely unfavorable blocks up front.
7103  */
7104  if (delstate->bottomup)
7105  nblocksfavorable = bottomup_sort_and_shrink(delstate);
7106 
7107 #ifdef USE_PREFETCH
7108  /* Initialize prefetch state. */
7109  prefetch_state.cur_hblkno = InvalidBlockNumber;
7110  prefetch_state.next_item = 0;
7111  prefetch_state.ndeltids = delstate->ndeltids;
7112  prefetch_state.deltids = delstate->deltids;
7113 
7114  /*
7115  * Determine the prefetch distance that we will attempt to maintain.
7116  *
7117  * Since the caller holds a buffer lock somewhere in rel, we'd better make
7118  * sure that isn't a catalog relation before we call code that does
7119  * syscache lookups, to avoid risk of deadlock.
7120  */
7121  if (IsCatalogRelation(rel))
7122  prefetch_distance = maintenance_io_concurrency;
7123  else
7124  prefetch_distance =
7126 
7127  /* Cap initial prefetch distance for bottom-up deletion caller */
7128  if (delstate->bottomup)
7129  {
7130  Assert(nblocksfavorable >= 1);
7131  Assert(nblocksfavorable <= BOTTOMUP_MAX_NBLOCKS);
7132  prefetch_distance = Min(prefetch_distance, nblocksfavorable);
7133  }
7134 
7135  /* Start prefetching. */
7136  index_delete_prefetch_buffer(rel, &prefetch_state, prefetch_distance);
7137 #endif
7138 
7139  /* Iterate over deltids, determine which to delete, check their horizon */
7140  Assert(delstate->ndeltids > 0);
7141  for (int i = 0; i < delstate->ndeltids; i++)
7142  {
7143  TM_IndexDelete *ideltid = &delstate->deltids[i];
7144  TM_IndexStatus *istatus = delstate->status + ideltid->id;
7145  ItemPointer htid = &ideltid->tid;
7146  OffsetNumber offnum;
7147 
7148  /*
7149  * Read buffer, and perform required extra steps each time a new block
7150  * is encountered. Avoid refetching if it's the same block as the one
7151  * from the last htid.
7152  */
7153  if (blkno == InvalidBlockNumber ||
7154  ItemPointerGetBlockNumber(htid) != blkno)
7155  {
7156  /*
7157  * Consider giving up early for bottom-up index deletion caller
7158  * first. (Only prefetch next-next block afterwards, when it
7159  * becomes clear that we're at least going to access the next
7160  * block in line.)
7161  *
7162  * Sometimes the first block frees so much space for bottom-up
7163  * caller that the deletion process can end without accessing any
7164  * more blocks. It is usually necessary to access 2 or 3 blocks
7165  * per bottom-up deletion operation, though.
7166  */
7167  if (delstate->bottomup)
7168  {
7169  /*
7170  * We often allow caller to delete a few additional items
7171  * whose entries we reached after the point that space target
7172  * from caller was satisfied. The cost of accessing the page
7173  * was already paid at that point, so it made sense to finish
7174  * it off. When that happened, we finalize everything here
7175  * (by finishing off the whole bottom-up deletion operation
7176  * without needlessly paying the cost of accessing any more
7177  * blocks).
7178  */
7179  if (bottomup_final_block)
7180  break;
7181 
7182  /*
7183  * Give up when we didn't enable our caller to free any
7184  * additional space as a result of processing the page that we
7185  * just finished up with. This rule is the main way in which
7186  * we keep the cost of bottom-up deletion under control.
7187  */
7188  if (nblocksaccessed >= 1 && actualfreespace == lastfreespace)
7189  break;
7190  lastfreespace = actualfreespace; /* for next time */
7191 
7192  /*
7193  * Deletion operation (which is bottom-up) will definitely
7194  * access the next block in line. Prepare for that now.
7195  *
7196  * Decay target free space so that we don't hang on for too
7197  * long with a marginal case. (Space target is only truly
7198  * helpful when it allows us to recognize that we don't need
7199  * to access more than 1 or 2 blocks to satisfy caller due to
7200  * agreeable workload characteristics.)
7201  *
7202  * We are a bit more patient when we encounter contiguous
7203  * blocks, though: these are treated as favorable blocks. The
7204  * decay process is only applied when the next block in line
7205  * is not a favorable/contiguous block. This is not an
7206  * exception to the general rule; we still insist on finding
7207  * at least one deletable item per block accessed. See
7208  * bottomup_nblocksfavorable() for full details of the theory
7209  * behind favorable blocks and heap block locality in general.
7210  *
7211  * Note: The first block in line is always treated as a
7212  * favorable block, so the earliest possible point that the
7213  * decay can be applied is just before we access the second
7214  * block in line. The Assert() verifies this for us.
7215  */
7216  Assert(nblocksaccessed > 0 || nblocksfavorable > 0);
7217  if (nblocksfavorable > 0)
7218  nblocksfavorable--;
7219  else
7220  curtargetfreespace /= 2;
7221  }
7222 
7223  /* release old buffer */
7224  if (BufferIsValid(buf))
7225  UnlockReleaseBuffer(buf);
7226 
7227  blkno = ItemPointerGetBlockNumber(htid);
7228  buf = ReadBuffer(rel, blkno);
7229  nblocksaccessed++;
7230  Assert(!delstate->bottomup ||
7231  nblocksaccessed <= BOTTOMUP_MAX_NBLOCKS);
7232 
7233 #ifdef USE_PREFETCH
7234 
7235  /*
7236  * To maintain the prefetch distance, prefetch one more page for
7237  * each page we read.
7238  */
7239  index_delete_prefetch_buffer(rel, &prefetch_state, 1);
7240 #endif
7241 
7243 
7244  page = BufferGetPage(buf);
7245  maxoff = PageGetMaxOffsetNumber(page);
7246  }
7247 
7248  if (istatus->knowndeletable)
7249  Assert(!delstate->bottomup && !istatus->promising);
7250  else
7251  {
7252  ItemPointerData tmp = *htid;
7253  HeapTupleData heapTuple;
7254 
7255  /* Are any tuples from this HOT chain non-vacuumable? */
7256  if (heap_hot_search_buffer(&tmp, rel, buf, &SnapshotNonVacuumable,
7257  &heapTuple, NULL, true))
7258  continue; /* can't delete entry */
7259 
7260  /* Caller will delete, since whole HOT chain is vacuumable */
7261  istatus->knowndeletable = true;
7262 
7263  /* Maintain index free space info for bottom-up deletion case */
7264  if (delstate->bottomup)
7265  {
7266  Assert(istatus->freespace > 0);
7267  actualfreespace += istatus->freespace;
7268  if (actualfreespace >= curtargetfreespace)
7269  bottomup_final_block = true;
7270  }
7271  }
7272 
7273  /*
7274  * Maintain latestRemovedXid value for deletion operation as a whole
7275  * by advancing current value using heap tuple headers. This is
7276  * loosely based on the logic for pruning a HOT chain.
7277  */
7278  offnum = ItemPointerGetOffsetNumber(htid);
7279  priorXmax = InvalidTransactionId; /* cannot check first XMIN */
7280  for (;;)
7281  {
7282  ItemId lp;
7283  HeapTupleHeader htup;
7284 
7285  /* Some sanity checks */
7286  if (offnum < FirstOffsetNumber || offnum > maxoff)
7287  {
7288  Assert(false);
7289  break;
7290  }
7291 
7292  lp = PageGetItemId(page, offnum);
7293  if (ItemIdIsRedirected(lp))
7294  {
7295  offnum = ItemIdGetRedirect(lp);
7296  continue;
7297  }
7298 
7299  /*
7300  * We'll often encounter LP_DEAD line pointers (especially with an
7301  * entry marked knowndeletable by our caller up front). No heap
7302  * tuple headers get examined for an htid that leads us to an
7303  * LP_DEAD item. This is okay because the earlier pruning
7304  * operation that made the line pointer LP_DEAD in the first place
7305  * must have considered the original tuple header as part of
7306  * generating its own latestRemovedXid value.
7307  *
7308  * Relying on XLOG_HEAP2_CLEAN records like this is the same
7309  * strategy that index vacuuming uses in all cases. Index VACUUM
7310  * WAL records don't even have a latestRemovedXid field of their
7311  * own for this reason.
7312  */
7313  if (!ItemIdIsNormal(lp))
7314  break;
7315 
7316  htup = (HeapTupleHeader) PageGetItem(page, lp);
7317 
7318  /*
7319  * Check the tuple XMIN against prior XMAX, if any
7320  */
7321  if (TransactionIdIsValid(priorXmax) &&
7322  !TransactionIdEquals(HeapTupleHeaderGetXmin(htup), priorXmax))
7323  break;
7324 
7325  HeapTupleHeaderAdvanceLatestRemovedXid(htup, &latestRemovedXid);
7326 
7327  /*
7328  * If the tuple is not HOT-updated, then we are at the end of this
7329  * HOT-chain. No need to visit later tuples from the same update
7330  * chain (they get their own index entries) -- just move on to
7331  * next htid from index AM caller.
7332  */
7333  if (!HeapTupleHeaderIsHotUpdated(htup))
7334  break;
7335 
7336  /* Advance to next HOT chain member */
7337  Assert(ItemPointerGetBlockNumber(&htup->t_ctid) == blkno);
7338  offnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
7339  priorXmax = HeapTupleHeaderGetUpdateXid(htup);
7340  }
7341 
7342  /* Enable further/final shrinking of deltids for caller */
7343  finalndeltids = i + 1;
7344  }
7345 
7346  UnlockReleaseBuffer(buf);
7347 
7348  /*
7349  * Shrink deltids array to exclude non-deletable entries at the end. This
7350  * is not just a minor optimization. Final deltids array size might be
7351  * zero for a bottom-up caller. Index AM is explicitly allowed to rely on
7352  * ndeltids being zero in all cases with zero total deletable entries.
7353  */
7354  Assert(finalndeltids > 0 || delstate->bottomup);
7355  delstate->ndeltids = finalndeltids;
7356 
7357  return latestRemovedXid;
7358 }
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:365
void HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple, TransactionId *latestRemovedXid)
Definition: heapam.c:6978
TM_IndexDelete * deltids
Definition: tableam.h:227
bool IsCatalogRelation(Relation relation)
Definition: catalog.c:96
int maintenance_io_concurrency
Definition: bufmgr.c:150
#define ItemIdIsRedirected(itemId)
Definition: itemid.h:106
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
uint32 TransactionId
Definition: c.h:575
static int bottomup_sort_and_shrink(TM_IndexDeleteOp *delstate)
Definition: heapam.c:7613
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define ItemIdGetRedirect(itemId)
Definition: itemid.h:78
#define Min(x, y)
Definition: c.h:974
#define InvalidBuffer
Definition: buf.h:25
bool knowndeletable
Definition: tableam.h:195
#define InitNonVacuumableSnapshot(snapshotdata, vistestp)
Definition: snapmgr.h:83
uint32 BlockNumber
Definition: block.h:31
Form_pg_class rd_rel
Definition: rel.h:110
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
bool heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, bool *all_dead, bool first_call)
Definition: heapam.c:1527
GlobalVisState * GlobalVisTestFor(Relation rel)
Definition: procarray.c:3929
int get_tablespace_maintenance_io_concurrency(Oid spcid)
Definition: spccache.c:228
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3723
ItemPointerData t_ctid
Definition: htup_details.h:160
static char * buf
Definition: pg_test_fsync.c:68
#define BOTTOMUP_MAX_NBLOCKS
Definition: heapam.c:185
#define InvalidTransactionId
Definition: transam.h:31
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
bool promising
Definition: tableam.h:198
#define HeapTupleHeaderIsHotUpdated(tup)
Definition: htup_details.h:484
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
TM_IndexStatus * status
Definition: tableam.h:228
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3939
ItemPointerData tid
Definition: tableam.h:188
#define InvalidOffsetNumber
Definition: off.h:26
static void index_delete_sort(TM_IndexDeleteOp *delstate)
Definition: heapam.c:7400
#define Assert(condition)
Definition: c.h:792
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:619
#define InvalidBlockNumber
Definition: block.h:33
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
int16 freespace
Definition: tableam.h:199
int i
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:97
int bottomupfreespace
Definition: tableam.h:223
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define TransactionIdIsValid(xid)
Definition: transam.h:41
int Buffer
Definition: buf.h:23
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78

◆ heap_inplace_update()

void heap_inplace_update ( Relation  relation,
HeapTuple  tuple 
)

Definition at line 5790 of file heapam.c.

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(), index_update_stats(), vac_update_datfrozenxid(), and vac_update_relstats().

5791 {
5792  Buffer buffer;
5793  Page page;
5794  OffsetNumber offnum;
5795  ItemId lp = NULL;
5796  HeapTupleHeader htup;
5797  uint32 oldlen;
5798  uint32 newlen;
5799 
5800  /*
5801  * For now, we don't allow parallel updates. Unlike a regular update,
5802  * this should never create a combo CID, so it might be possible to relax
5803  * this restriction, but not without more thought and testing. It's not
5804  * clear that it would be useful, anyway.
5805  */
5806  if (IsInParallelMode())
5807  ereport(ERROR,
5808  (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
5809  errmsg("cannot update tuples during a parallel operation")));
5810 
5811  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
5813  page = (Page) BufferGetPage(buffer);
5814 
5815  offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
5816  if (PageGetMaxOffsetNumber(page) >= offnum)
5817  lp = PageGetItemId(page, offnum);
5818 
5819  if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
5820  elog(ERROR, "invalid lp");
5821 
5822  htup = (HeapTupleHeader) PageGetItem(page, lp);
5823 
5824  oldlen = ItemIdGetLength(lp) - htup->t_hoff;
5825  newlen = tuple->t_len - tuple->t_data->t_hoff;
5826  if (oldlen != newlen || htup->t_hoff != tuple->t_data->t_hoff)
5827  elog(ERROR, "wrong tuple length");
5828 
5829  /* NO EREPORT(ERROR) from here till changes are logged */
5831 
5832  memcpy((char *) htup + htup->t_hoff,
5833  (char *) tuple->t_data + tuple->t_data->t_hoff,
5834  newlen);
5835 
5836  MarkBufferDirty(buffer);
5837 
5838  /* XLOG stuff */
5839  if (RelationNeedsWAL(relation))
5840  {
5841  xl_heap_inplace xlrec;
5842  XLogRecPtr recptr;
5843 
5844  xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
5845 
5846  XLogBeginInsert();
5847  XLogRegisterData((char *) &xlrec, SizeOfHeapInplace);
5848 
5849  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
5850  XLogRegisterBufData(0, (char *) htup + htup->t_hoff, newlen);
5851 
5852  /* inplace updates aren't decoded atm, don't log the origin */
5853 
5854  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_INPLACE);
5855 
5856  PageSetLSN(page, recptr);
5857  }
5858 
5859  END_CRIT_SECTION();
5860 
5861  UnlockReleaseBuffer(buffer);
5862 
5863  /*
5864  * Send out shared cache inval if necessary. Note that because we only
5865  * pass the new version of the tuple, this mustn't be used for any
5866  * operations that could change catcache lookup keys. But we aren't
5867  * bothering with index updates either, so that's true a fortiori.
5868  */
5870  CacheInvalidateHeapTuple(relation, tuple, NULL);
5871 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:368
void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple)
Definition: inval.c:1122
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
#define SizeOfHeapInplace
Definition: heapam_xlog.h:310
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
int errcode(int sqlerrcode)
Definition: elog.c:704
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
HeapTupleHeader t_data
Definition: htup.h:68
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
bool IsInParallelMode(void)
Definition: xact.c:1012
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3723
#define ERROR
Definition: elog.h:45
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
#define REGBUF_STANDARD
Definition: xloginsert.h:35
unsigned int uint32
Definition: c.h:429
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
OffsetNumber offnum
Definition: heapam_xlog.h:306
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3939
#define ereport(elevel,...)
Definition: elog.h:155
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:619
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define XLOG_HEAP_INPLACE
Definition: heapam_xlog.h:39
#define RelationNeedsWAL(relation)
Definition: rel.h:563
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:394
int errmsg(const char *fmt,...)
Definition: elog.c:915
#define elog(elevel,...)
Definition: elog.h:228
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78

◆ heap_insert()

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

Definition at line 1877 of file heapam.c.

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(), 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(), 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().

1879 {
1881  HeapTuple heaptup;
1882  Buffer buffer;
1883  Buffer vmbuffer = InvalidBuffer;
1884  bool all_visible_cleared = false;
1885 
1886  /*
1887  * Fill in tuple header fields and toast the tuple if necessary.
1888  *
1889  * Note: below this point, heaptup is the data we actually intend to store
1890  * into the relation; tup is the caller's original untoasted data.
1891  */
1892  heaptup = heap_prepare_insert(relation, tup, xid, cid, options);
1893 
1894  /*
1895  * Find buffer to insert this tuple into. If the page is all visible,
1896  * this will also pin the requisite visibility map page.
1897  */
1898  buffer = RelationGetBufferForTuple(relation, heaptup->t_len,
1899  InvalidBuffer, options, bistate,
1900  &vmbuffer, NULL);
1901 
1902  /*
1903  * We're about to do the actual insert -- but check for conflict first, to
1904  * avoid possibly having to roll back work we've just done.
1905  *
1906  * This is safe without a recheck as long as there is no possibility of
1907  * another process scanning the page between this check and the insert
1908  * being visible to the scan (i.e., an exclusive buffer content lock is
1909  * continuously held from this point until the tuple insert is visible).
1910  *
1911  * For a heap insert, we only need to check for table-level SSI locks. Our
1912  * new tuple can't possibly conflict with existing tuple locks, and heap
1913  * page locks are only consolidated versions of tuple locks; they do not
1914  * lock "gaps" as index page locks do. So we don't need to specify a
1915  * buffer when making the call, which makes for a faster check.
1916  */
1918 
1919  /* NO EREPORT(ERROR) from here till changes are logged */
1921 
1922  RelationPutHeapTuple(relation, buffer, heaptup,
1923  (options & HEAP_INSERT_SPECULATIVE) != 0);
1924 
1925  if (PageIsAllVisible(BufferGetPage(buffer)))
1926  {
1927  all_visible_cleared = true;
1929  visibilitymap_clear(relation,
1930  ItemPointerGetBlockNumber(&(heaptup->t_self)),
1931  vmbuffer, VISIBILITYMAP_VALID_BITS);
1932  }
1933 
1934  /*
1935  * XXX Should we set PageSetPrunable on this page ?
1936  *
1937  * The inserting transaction may eventually abort thus making this tuple
1938  * DEAD and hence available for pruning. Though we don't want to optimize
1939  * for aborts, if no other tuple in this page is UPDATEd/DELETEd, the
1940  * aborted tuple will never be pruned until next vacuum is triggered.
1941  *
1942  * If you do add PageSetPrunable here, add it in heap_xlog_insert too.
1943  */
1944 
1945  MarkBufferDirty(buffer);
1946 
1947  /* XLOG stuff */
1948  if (RelationNeedsWAL(relation))
1949  {
1950  xl_heap_insert xlrec;
1951  xl_heap_header xlhdr;
1952  XLogRecPtr recptr;
1953  Page page = BufferGetPage(buffer);
1954  uint8 info = XLOG_HEAP_INSERT;
1955  int bufflags = 0;
1956 
1957  /*
1958  * If this is a catalog, we need to transmit combocids to properly
1959  * decode, so log that as well.
1960  */
1962  log_heap_new_cid(relation, heaptup);
1963 
1964  /*
1965  * If this is the single and first tuple on page, we can reinit the
1966  * page instead of restoring the whole thing. Set flag, and hide
1967  * buffer references from XLogInsert.
1968  */
1969  if (ItemPointerGetOffsetNumber(&(heaptup->t_self)) == FirstOffsetNumber &&
1971  {
1972  info |= XLOG_HEAP_INIT_PAGE;
1973  bufflags |= REGBUF_WILL_INIT;
1974  }
1975 
1976  xlrec.offnum = ItemPointerGetOffsetNumber(&heaptup->t_self);
1977  xlrec.flags = 0;
1978  if (all_visible_cleared)
1983 
1984  /*
1985  * For logical decoding, we need the tuple even if we're doing a full
1986  * page write, so make sure it's included even if we take a full-page
1987  * image. (XXX We could alternatively store a pointer into the FPW).
1988  */
1989  if (RelationIsLogicallyLogged(relation) &&
1991  {
1993  bufflags |= REGBUF_KEEP_DATA;
1994 
1995  if (IsToastRelation(relation))
1997  }
1998 
1999  XLogBeginInsert();
2000  XLogRegisterData((char *) &xlrec, SizeOfHeapInsert);
2001 
2002  xlhdr.t_infomask2 = heaptup->t_data->t_infomask2;
2003  xlhdr.t_infomask = heaptup->t_data->t_infomask;
2004  xlhdr.t_hoff = heaptup->t_data->t_hoff;
2005 
2006  /*
2007  * note we mark xlhdr as belonging to buffer; if XLogInsert decides to
2008  * write the whole page to the xlog, we don't need to store
2009  * xl_heap_header in the xlog.
2010  */
2011  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
2012  XLogRegisterBufData(0, (char *) &xlhdr, SizeOfHeapHeader);
2013  /* PG73FORMAT: write bitmap [+ padding] [+ oid] + data */
2015  (char *) heaptup->t_data + SizeofHeapTupleHeader,
2016  heaptup->t_len - SizeofHeapTupleHeader);
2017 
2018  /* filtering by origin on a row level is much more efficient */
2020 
2021  recptr = XLogInsert(RM_HEAP_ID, info);
2022 
2023  PageSetLSN(page, recptr);
2024  }
2025 
2026  END_CRIT_SECTION();
2027 
2028  UnlockReleaseBuffer(buffer);
2029  if (vmbuffer != InvalidBuffer)
2030  ReleaseBuffer(vmbuffer);
2031 
2032  /*
2033  * If tuple is cachable, mark it for invalidation from the caches in case
2034  * we abort. Note it is OK to do this after releasing the buffer, because
2035  * the heaptup data structure is all in local memory, not in the shared
2036  * buffer.
2037  */
2038  CacheInvalidateHeapTuple(relation, heaptup, NULL);
2039 
2040  /* Note: speculative insertions are counted too, even if aborted later */
2041  pgstat_count_heap_insert(relation, 1);
2042 
2043  /*
2044  * If heaptup is a private copy, release it. Don't forget to copy t_self
2045  * back to the caller's image, too.
2046  */
2047  if (heaptup != tup)
2048  {
2049  tup->t_self = heaptup->t_self;
2050  heap_freetuple(heaptup);
2051  }
2052 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:368
#define SizeofHeapTupleHeader
Definition: htup_details.h:184
bool IsToastRelation(Relation relation)
Definition: catalog.c:138
#define XLOG_HEAP_INSERT
Definition: heapam_xlog.h:32
static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup)
Definition: heapam.c:8107
void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple)
Definition: inval.c:1122
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid, CommandId cid, int options)
Definition: heapam.c:2061
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:575
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
unsigned char uint8
Definition: c.h:427
#define XLH_INSERT_IS_SPECULATIVE
Definition: heapam_xlog.h:68
#define InvalidBuffer
Definition: buf.h:25
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33
uint16 t_infomask2
Definition: heapam_xlog.h:146
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
#define XLH_INSERT_ON_TOAST_RELATION
Definition: heapam_xlog.h:70
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:238
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3700
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:636
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
void RelationPutHeapTuple(Relation relation, Buffer buffer, HeapTuple tuple, bool token)
Definition: hio.c:36
#define XLOG_HEAP_INIT_PAGE
Definition: heapam_xlog.h:46
#define HEAP_INSERT_SPECULATIVE
Definition: heapam.h:37
#define VISIBILITYMAP_VALID_BITS
Definition: visibilitymap.h:28
HeapTupleHeader t_data
Definition: htup.h:68
bool visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer buf, uint8 flags)
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3723
#define XLH_INSERT_CONTAINS_NEW_TUPLE
Definition: heapam_xlog.h:69
ItemPointerData t_self
Definition: htup.h:65
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:438
uint32 t_len
Definition: htup.h:64
#define FirstOffsetNumber
Definition: off.h:27
#define REGBUF_STANDARD
Definition: xloginsert.h:35
Buffer RelationGetBufferForTuple(Relation relation, Size len, Buffer otherBuffer, int options, BulkInsertState bistate, Buffer *vmbuffer, Buffer *vmbuffer_other)
Definition: hio.c:331
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:404
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
#define RelationIsAccessibleInLogicalDecoding(relation)
Definition: rel.h:620
#define REGBUF_KEEP_DATA
Definition: xloginsert.h:38
void CheckForSerializableConflictIn(Relation relation, ItemPointer tid, BlockNumber blkno)
Definition: predicate.c:4446
#define PageClearAllVisible(page)
Definition: bufpage.h:389
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:792
uint16 t_infomask
Definition: heapam_xlog.h:147
#define InvalidBlockNumber
Definition: block.h:33
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define RelationNeedsWAL(relation)
Definition: rel.h:563
#define SizeOfHeapInsert
Definition: heapam_xlog.h:162
#define XLH_INSERT_ALL_VISIBLE_CLEARED
Definition: heapam_xlog.h:66
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2674
void pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
Definition: pgstat.c:2175
#define HEAP_INSERT_NO_LOGICAL
Definition: heapam.h:36
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
OffsetNumber offnum
Definition: heapam_xlog.h:156
#define SizeOfHeapHeader
Definition: heapam_xlog.h:151
Pointer Page
Definition: bufpage.h:78

◆ heap_lock_tuple()

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

Definition at line 4044 of file heapam.c.

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, HeapTupleHeaderIndicatesMovedPartitions, HeapTupleHeaderIsOnlyLocked(), HeapTupleHeaderSetXmax, HeapTupleSatisfiesUpdate(), i, xl_heap_lock::infobits_set, InvalidBuffer, InvalidCommandId, ItemIdGetLength, ItemIdIsNormal, ItemPointerCopy, ItemPointerEquals(), ItemPointerGetBlockNumber, ItemPointerGetOffsetNumber, LockBuffer(), xl_heap_lock::locking_xid, LockTupleExclusive, LockTupleKeyShare, LockTupleNoKeyExclusive, LockTupleShare, LockWaitBlock, LockWaitError, LockWaitSkip, MarkBufferDirty(), MultiXactIdSetOldestMember(), MultiXactIdWait(), MultiXactStatusNoKeyUpdate, xl_heap_lock::offnum, PageGetItem, PageGetItemId, PageIsAllVisible, PageSetLSN, pfree(), ReadBuffer(), REGBUF_STANDARD, RelationGetRelationName, RelationGetRelid, RelationNeedsWAL, ReleaseBuffer(), SizeOfHeapLock, START_CRIT_SECTION, status(), 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, TM_FailureData::xmax, and xmax_infomask_changed().

Referenced by heapam_tuple_lock().

4048 {
4049  TM_Result result;
4050  ItemPointer tid = &(tuple->t_self);
4051  ItemId lp;
4052  Page page;
4053  Buffer vmbuffer = InvalidBuffer;
4054  BlockNumber block;
4055  TransactionId xid,
4056  xmax;
4057  uint16 old_infomask,
4058  new_infomask,
4059  new_infomask2;
4060  bool first_time = true;
4061  bool skip_tuple_lock = false;
4062  bool have_tuple_lock = false;
4063  bool cleared_all_frozen = false;
4064 
4065  *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
4066  block = ItemPointerGetBlockNumber(tid);
4067 
4068  /*
4069  * Before locking the buffer, pin the visibility map page if it appears to
4070  * be necessary. Since we haven't got the lock yet, someone else might be
4071  * in the middle of changing this, so we'll need to recheck after we have
4072  * the lock.
4073  */
4074  if (PageIsAllVisible(BufferGetPage(*buffer)))
4075  visibilitymap_pin(relation, block, &vmbuffer);
4076 
4078 
4079  page = BufferGetPage(*buffer);
4080  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
4081  Assert(ItemIdIsNormal(lp));
4082 
4083  tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
4084  tuple->t_len = ItemIdGetLength(lp);
4085  tuple->t_tableOid = RelationGetRelid(relation);
4086 
4087 l3:
4088  result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
4089 
4090  if (result == TM_Invisible)
4091  {
4092  /*
4093  * This is possible, but only when locking a tuple for ON CONFLICT
4094  * UPDATE. We return this value here rather than throwing an error in
4095  * order to give that case the opportunity to throw a more specific
4096  * error.
4097  */
4098  result = TM_Invisible;
4099  goto out_locked;
4100  }
4101  else if (result == TM_BeingModified ||
4102  result == TM_Updated ||
4103  result == TM_Deleted)
4104  {
4105  TransactionId xwait;
4106  uint16 infomask;
4107  uint16 infomask2;
4108  bool require_sleep;
4109  ItemPointerData t_ctid;
4110 
4111  /* must copy state data before unlocking buffer */
4112  xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
4113  infomask = tuple->t_data->t_infomask;
4114  infomask2 = tuple->t_data->t_infomask2;
4115  ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
4116 
4117  LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
4118 
4119  /*
4120  * If any subtransaction of the current top transaction already holds
4121  * a lock as strong as or stronger than what we're requesting, we
4122  * effectively hold the desired lock already. We *must* succeed
4123  * without trying to take the tuple lock, else we will deadlock
4124  * against anyone wanting to acquire a stronger lock.
4125  *
4126  * Note we only do this the first time we loop on the HTSU result;
4127  * there is no point in testing in subsequent passes, because
4128  * evidently our own transaction cannot have acquired a new lock after
4129  * the first time we checked.
4130  */
4131  if (first_time)
4132  {
4133  first_time = false;
4134 
4135  if (infomask & HEAP_XMAX_IS_MULTI)
4136  {
4137  int i;
4138  int nmembers;
4139  MultiXactMember *members;
4140 
4141  /*
4142  * We don't need to allow old multixacts here; if that had
4143  * been the case, HeapTupleSatisfiesUpdate would have returned
4144  * MayBeUpdated and we wouldn't be here.
4145  */
4146  nmembers =
4147  GetMultiXactIdMembers(xwait, &members, false,
4148  HEAP_XMAX_IS_LOCKED_ONLY(infomask));
4149 
4150  for (i = 0; i < nmembers; i++)
4151  {
4152  /* only consider members of our own transaction */
4153  if (!TransactionIdIsCurrentTransactionId(members[i].xid))
4154  continue;
4155 
4156  if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
4157  {
4158  pfree(members);
4159  result = TM_Ok;
4160  goto out_unlocked;
4161  }
4162  else
4163  {
4164  /*
4165  * Disable acquisition of the heavyweight tuple lock.
4166  * Otherwise, when promoting a weaker lock, we might
4167  * deadlock with another locker that has acquired the
4168  * heavyweight tuple lock and is waiting for our
4169  * transaction to finish.
4170  *
4171  * Note that in this case we still need to wait for
4172  * the multixact if required, to avoid acquiring
4173  * conflicting locks.
4174  */
4175  skip_tuple_lock = true;
4176  }
4177  }
4178 
4179  if (members)
4180  pfree(members);
4181  }
4182  else if (TransactionIdIsCurrentTransactionId(xwait))
4183  {
4184  switch (mode)
4185  {
4186  case LockTupleKeyShare:
4187  Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
4188  HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
4189  HEAP_XMAX_IS_EXCL_LOCKED(infomask));
4190  result = TM_Ok;
4191  goto out_unlocked;
4192  case LockTupleShare:
4193  if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
4194  HEAP_XMAX_IS_EXCL_LOCKED(infomask))
4195  {
4196  result = TM_Ok;
4197  goto out_unlocked;
4198  }
4199  break;
4201  if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
4202  {
4203  result = TM_Ok;
4204  goto out_unlocked;
4205  }
4206  break;
4207  case LockTupleExclusive:
4208  if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
4209  infomask2 & HEAP_KEYS_UPDATED)
4210  {
4211  result = TM_Ok;
4212  goto out_unlocked;
4213  }
4214  break;
4215  }
4216  }
4217  }
4218 
4219  /*
4220  * Initially assume that we will have to wait for the locking
4221  * transaction(s) to finish. We check various cases below in which
4222  * this can be turned off.
4223  */
4224  require_sleep = true;
4225  if (mode == LockTupleKeyShare)
4226  {
4227  /*
4228  * If we're requesting KeyShare, and there's no update present, we
4229  * don't need to wait. Even if there is an update, we can still
4230  * continue if the key hasn't been modified.
4231  *
4232  * However, if there are updates, we need to walk the update chain
4233  * to mark future versions of the row as locked, too. That way,
4234  * if somebody deletes that future version, we're protected
4235  * against the key going away. This locking of future versions
4236  * could block momentarily, if a concurrent transaction is
4237  * deleting a key; or it could return a value to the effect that
4238  * the transaction deleting the key has already committed. So we
4239  * do this before re-locking the buffer; otherwise this would be
4240  * prone to deadlocks.
4241  *
4242  * Note that the TID we're locking was grabbed before we unlocked
4243  * the buffer. For it to change while we're not looking, the
4244  * other properties we're testing for below after re-locking the
4245  * buffer would also change, in which case we would restart this
4246  * loop above.
4247  */
4248  if (!(infomask2 & HEAP_KEYS_UPDATED))
4249  {
4250  bool updated;
4251 
4252  updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
4253 
4254  /*
4255  * If there are updates, follow the update chain; bail out if
4256  * that cannot be done.
4257  */
4258  if (follow_updates && updated)
4259  {
4260  TM_Result res;
4261 
4262  res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
4264  mode);
4265  if (res != TM_Ok)
4266  {
4267  result = res;
4268  /* recovery code expects to have buffer lock held */
4270  goto failed;
4271  }
4272  }
4273 
4275 
4276  /*
4277  * Make sure it's still an appropriate lock, else start over.
4278  * Also, if it wasn't updated before we released the lock, but
4279  * is updated now, we start over too; the reason is that we
4280  * now need to follow the update chain to lock the new
4281  * versions.
4282  */
4283  if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
4284  ((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
4285  !updated))
4286  goto l3;
4287 
4288  /* Things look okay, so we can skip sleeping */
4289  require_sleep = false;
4290 
4291  /*
4292  * Note we allow Xmax to change here; other updaters/lockers
4293  * could have modified it before we grabbed the buffer lock.
4294  * However, this is not a problem, because with the recheck we
4295  * just did we ensure that they still don't conflict with the
4296  * lock we want.
4297  */
4298  }
4299  }
4300  else if (mode == LockTupleShare)
4301  {
4302  /*
4303  * If we're requesting Share, we can similarly avoid sleeping if
4304  * there's no update and no exclusive lock present.
4305  */
4306  if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
4307  !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
4308  {
4310 
4311  /*
4312  * Make sure it's still an appropriate lock, else start over.
4313  * See above about allowing xmax to change.
4314  */
4315  if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
4317  goto l3;
4318  require_sleep = false;
4319  }
4320  }
4321  else if (mode == LockTupleNoKeyExclusive)
4322  {
4323  /*
4324  * If we're requesting NoKeyExclusive, we might also be able to
4325  * avoid sleeping; just ensure that there no conflicting lock
4326  * already acquired.
4327  */
4328  if (infomask & HEAP_XMAX_IS_MULTI)
4329  {
4330  if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
4331  mode, NULL))
4332  {
4333  /*
4334  * No conflict, but if the xmax changed under us in the
4335  * meantime, start over.
4336  */
4338  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4340  xwait))
4341  goto l3;
4342 
4343  /* otherwise, we're good */
4344  require_sleep = false;
4345  }
4346  }
4347  else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
4348  {
4350 
4351  /* if the xmax changed in the meantime, start over */
4352  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4354  xwait))
4355  goto l3;
4356  /* otherwise, we're good */
4357  require_sleep = false;
4358  }
4359  }
4360 
4361  /*
4362  * As a check independent from those above, we can also avoid sleeping
4363  * if the current transaction is the sole locker of the tuple. Note
4364  * that the strength of the lock already held is irrelevant; this is
4365  * not about recording the lock in Xmax (which will be done regardless
4366  * of this optimization, below). Also, note that the cases where we
4367  * hold a lock stronger than we are requesting are already handled
4368  * above by not doing anything.
4369  *
4370  * Note we only deal with the non-multixact case here; MultiXactIdWait
4371  * is well equipped to deal with this situation on its own.
4372  */
4373  if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
4375  {
4376  /* ... but if the xmax changed in the meantime, start over */
4378  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4380  xwait))
4381  goto l3;
4383  require_sleep = false;
4384  }
4385 
4386  /*
4387  * Time to sleep on the other transaction/multixact, if necessary.
4388  *
4389  * If the other transaction is an update/delete that's already
4390  * committed, then sleeping cannot possibly do any good: if we're
4391  * required to sleep, get out to raise an error instead.
4392  *
4393  * By here, we either have already acquired the buffer exclusive lock,
4394  * or we must wait for the locking transaction or multixact; so below
4395  * we ensure that we grab buffer lock after the sleep.
4396  */
4397  if (require_sleep && (result == TM_Updated || result == TM_Deleted))
4398  {
4400  goto failed;
4401  }
4402  else if (require_sleep)
4403  {
4404  /*
4405  * Acquire tuple lock to establish our priority for the tuple, or
4406  * die trying. LockTuple will release us when we are next-in-line
4407  * for the tuple. We must do this even if we are share-locking,
4408  * but not if we already have a weaker lock on the tuple.
4409  *
4410  * If we are forced to "start over" below, we keep the tuple lock;
4411  * this arranges that we stay at the head of the line while
4412  * rechecking tuple state.
4413  */
4414  if (!skip_tuple_lock &&
4415  !heap_acquire_tuplock(relation, tid, mode, wait_policy,
4416  &have_tuple_lock))
4417  {
4418  /*
4419  * This can only happen if wait_policy is Skip and the lock
4420  * couldn't be obtained.
4421  */
4422  result = TM_WouldBlock;
4423  /* recovery code expects to have buffer lock held */
4425  goto failed;
4426  }
4427 
4428  if (infomask & HEAP_XMAX_IS_MULTI)
4429  {
4431 
4432  /* We only ever lock tuples, never update them */
4433  if (status >= MultiXactStatusNoKeyUpdate)
4434  elog(ERROR, "invalid lock mode in heap_lock_tuple");
4435 
4436  /* wait for multixact to end, or die trying */
4437  switch (wait_policy)
4438  {
4439  case LockWaitBlock:
4440  MultiXactIdWait((MultiXactId) xwait, status, infomask,
4441  relation, &tuple->t_self, XLTW_Lock, NULL);
4442  break;
4443  case LockWaitSkip:
4445  status, infomask, relation,
4446  NULL))
4447  {
4448  result = TM_WouldBlock;
4449  /* recovery code expects to have buffer lock held */
4451  goto failed;
4452  }
4453  break;
4454  case LockWaitError:
4456  status, infomask, relation,
4457  NULL))
4458  ereport(ERROR,
4459  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
4460  errmsg("could not obtain lock on row in relation \"%s\"",
4461  RelationGetRelationName(relation))));
4462 
4463  break;
4464  }
4465 
4466  /*
4467  * Of course, the multixact might not be done here: if we're
4468  * requesting a light lock mode, other transactions with light
4469  * locks could still be alive, as well as locks owned by our
4470  * own xact or other subxacts of this backend. We need to
4471  * preserve the surviving MultiXact members. Note that it
4472  * isn't absolutely necessary in the latter case, but doing so
4473  * is simpler.
4474  */
4475  }
4476  else
4477  {
4478  /* wait for regular transaction to end, or die trying */
4479  switch (wait_policy)
4480  {
4481  case LockWaitBlock:
4482  XactLockTableWait(xwait, relation, &tuple->t_self,
4483  XLTW_Lock);
4484  break;
4485  case LockWaitSkip:
4486  if (!ConditionalXactLockTableWait(xwait))
4487  {
4488  result = TM_WouldBlock;
4489  /* recovery code expects to have buffer lock held */
4491  goto failed;
4492  }
4493  break;
4494  case LockWaitError:
4495  if (!ConditionalXactLockTableWait(xwait))
4496  ereport(ERROR,
4497  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
4498  errmsg("could not obtain lock on row in relation \"%s\"",
4499  RelationGetRelationName(relation))));
4500  break;
4501  }
4502  }
4503 
4504  /* if there are updates, follow the update chain */
4505  if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
4506  {
4507  TM_Result res;
4508 
4509  res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
4511  mode);
4512  if (res != TM_Ok)
4513  {
4514  result = res;
4515  /* recovery code expects to have buffer lock held */
4517  goto failed;
4518  }
4519  }
4520 
4522 
4523  /*
4524  * xwait is done, but if xwait had just locked the tuple then some
4525  * other xact could update this tuple before we get to this point.
4526  * Check for xmax change, and start over if so.
4527  */
4528  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4530  xwait))
4531  goto l3;
4532 
4533  if (!(infomask & HEAP_XMAX_IS_MULTI))
4534  {
4535  /*
4536  * Otherwise check if it committed or aborted. Note we cannot
4537  * be here if the tuple was only locked by somebody who didn't
4538  * conflict with us; that would have been handled above. So
4539  * that transaction must necessarily be gone by now. But
4540  * don't check for this in the multixact case, because some
4541  * locker transactions might still be running.
4542  */
4543  UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
4544  }
4545  }
4546 
4547  /* By here, we're certain that we hold buffer exclusive lock again */
4548 
4549  /*
4550  * We may lock if previous xmax aborted, or if it committed but only
4551  * locked the tuple without updating it; or if we didn't have to wait
4552  * at all for whatever reason.
4553  */
4554  if (!require_sleep ||
4555  (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
4558  result = TM_Ok;
4559  else if (!ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid) ||
4561  result = TM_Updated;
4562  else
4563  result = TM_Deleted;
4564  }
4565 
4566 failed:
4567  if (result != TM_Ok)
4568  {
4569  Assert(result == TM_SelfModified || result == TM_Updated ||
4570  result == TM_Deleted || result == TM_WouldBlock);
4571  Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
4572  Assert(result != TM_Updated ||
4573  !ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid));
4574  tmfd->ctid = tuple->t_data->t_ctid;
4575  tmfd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
4576  if (result == TM_SelfModified)
4577  tmfd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
4578  else
4579  tmfd->cmax = InvalidCommandId;
4580  goto out_locked;
4581  }
4582 
4583  /*
4584  * If we didn't pin the visibility map page and the page has become all
4585  * visible while we were busy locking the buffer, or during some
4586  * subsequent window during which we had it unlocked, we'll have to unlock
4587  * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
4588  * unfortunate, especially since we'll now have to recheck whether the
4589  * tuple has been locked or updated under us, but hopefully it won't
4590  * happen very often.
4591  */
4592  if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
4593  {
4594  LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
4595  visibilitymap_pin(relation, block, &vmbuffer);
4597  goto l3;
4598  }
4599 
4600  xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
4601  old_infomask = tuple->t_data->t_infomask;
4602 
4603  /*
4604  * If this is the first possibly-multixact-able operation in the current
4605  * transaction, set my per-backend OldestMemberMXactId setting. We can be
4606  * certain that the transaction will never become a member of any older
4607  * MultiXactIds than that. (We have to do this even if we end up just
4608  * using our own TransactionId below, since some other backend could
4609  * incorporate our XID into a MultiXact immediately afterwards.)
4610  */
4612 
4613  /*
4614  * Compute the new xmax and infomask to store into the tuple. Note we do
4615  * not modify the tuple just yet, because that would leave it in the wrong
4616  * state if multixact.c elogs.
4617  */
4618  compute_new_xmax_infomask(xmax, old_infomask, tuple->t_data->t_infomask2,
4619  GetCurrentTransactionId(), mode, false,
4620  &xid, &new_infomask, &new_infomask2);
4621 
4623 
4624  /*
4625  * Store transaction information of xact locking the tuple.
4626  *
4627  * Note: Cmax is meaningless in this context, so don't set it; this avoids
4628  * possibly generating a useless combo CID. Moreover, if we're locking a
4629  * previously updated tuple, it's important to preserve the Cmax.
4630  *
4631  * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
4632  * we would break the HOT chain.
4633  */
4634  tuple->t_data->t_infomask &= ~HEAP_XMAX_BITS;
4635  tuple->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
4636  tuple->t_data->t_infomask |= new_infomask;
4637  tuple->t_data->t_infomask2 |= new_infomask2;
4638  if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
4640  HeapTupleHeaderSetXmax(tuple->t_data, xid);
4641 
4642  /*
4643  * Make sure there is no forward chain link in t_ctid. Note that in the
4644  * cases where the tuple has been updated, we must not overwrite t_ctid,
4645  * because it was set by the updater. Moreover, if the tuple has been
4646  * updated, we need to follow the update chain to lock the new versions of
4647  * the tuple as well.
4648  */
4649  if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
4650  tuple->t_data->t_ctid = *tid;
4651 
4652  /* Clear only the all-frozen bit on visibility map if needed */
4653  if (PageIsAllVisible(page) &&
4654  visibilitymap_clear(relation, block, vmbuffer,
4656  cleared_all_frozen = true;
4657 
4658 
4659  MarkBufferDirty(*buffer);
4660 
4661  /*
4662  * XLOG stuff. You might think that we don't need an XLOG record because
4663  * there is no state change worth restoring after a crash. You would be
4664  * wrong however: we have just written either a TransactionId or a
4665  * MultiXactId that may never have been seen on disk before, and we need
4666  * to make sure that there are XLOG entries covering those ID numbers.
4667  * Else the same IDs might be re-used after a crash, which would be
4668  * disastrous if this page made it to disk before the crash. Essentially
4669  * we have to enforce the WAL log-before-data rule even in this case.
4670  * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
4671  * entries for everything anyway.)
4672  */
4673  if (RelationNeedsWAL(relation))
4674  {
4675  xl_heap_lock xlrec;
4676  XLogRecPtr recptr;
4677 
4678  XLogBeginInsert();
4679  XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD);
4680 
4681  xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
4682  xlrec.locking_xid = xid;
4683  xlrec.infobits_set = compute_infobits(new_infomask,
4684  tuple->t_data->t_infomask2);
4685  xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
4686  XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
4687 
4688  /* we don't decode row locks atm, so no need to log the origin */
4689 
4690  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
4691 
4692  PageSetLSN(page, recptr);
4693  }
4694 
4695  END_CRIT_SECTION();
4696 
4697  result = TM_Ok;
4698 
4699 out_locked:
4700  LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
4701 
4702 out_unlocked:
4703  if (BufferIsValid(vmbuffer))
4704  ReleaseBuffer(vmbuffer);
4705 
4706  /*
4707  * Don't update the visibility map here. Locking a tuple doesn't change
4708  * visibility info.
4709  */
4710 
4711  /*
4712  * Now that we have successfully marked the tuple as locked, we can
4713  * release the lmgr tuple lock, if we had it.
4714  */
4715  if (have_tuple_lock)
4716  UnlockTupleTuplock(relation, tid, mode);
4717 
4718  return result;
4719 }
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:365
ItemPointerData ctid
Definition: tableam.h:125
static PgChecksumMode mode
Definition: pg_checksums.c:61
MultiXactStatus
Definition: multixact.h:41
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
#define HEAP_XMAX_BITS
Definition: htup_details.h:270
OffsetNumber offnum
Definition: heapam_xlog.h:277
static uint8 compute_infobits(uint16 infomask, uint16 infomask2)
Definition: heapam.c:2468
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
#define PageIsAllVisible(page)
Definition: bufpage.h:385
#define XLH_LOCK_ALL_FROZEN_CLEARED
Definition: heapam_xlog.h:271
TransactionId locking_xid
Definition: heapam_xlog.h:276
uint32 TransactionId
Definition: c.h:575
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:869
void visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *buf)
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define VISIBILITYMAP_ALL_FROZEN
Definition: visibilitymap.h:27
static bool xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
Definition: heapam.c:2490
#define HeapTupleHeaderClearHotUpdated(tup)
Definition: htup_details.h:496
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
CommandId cmax
Definition: tableam.h:127
bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
#define InvalidBuffer
Definition: buf.h:25
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
int errcode(int sqlerrcode)
Definition: elog.c:704
#define HeapTupleHeaderIndicatesMovedPartitions(tup)
Definition: htup_details.h:445
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3700
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
TM_Result HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, Buffer buffer)
#define UnlockTupleTuplock(rel, tup, mode)
Definition: heapam.c:165
bool ConditionalXactLockTableWait(TransactionId xid)
Definition: lmgr.c:712
#define HEAP_XMAX_IS_SHR_LOCKED(infomask)
Definition: htup_details.h:262
void MultiXactIdSetOldestMember(void)
Definition: multixact.c:625
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderGetRawXmax(tup)
Definition: htup_details.h:375
unsigned short uint16
Definition: c.h:428
void pfree(void *pointer)
Definition: mcxt.c:1057
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
bool visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer buf, uint8 flags)
TransactionId xmax
Definition: tableam.h:126
#define ERROR
Definition: elog.h:45
#define HEAP_XMAX_INVALID
Definition: htup_details.h:207
int8 infobits_set
Definition: heapam_xlog.h:278
ItemPointerData t_ctid
Definition: htup_details.h:160
ItemPointerData t_self
Definition: htup.h:65
static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, ItemPointer ctid, XLTW_Oper oper, int *remaining)
Definition: heapam.c:6808
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:438
uint32 t_len
Definition: htup.h:64
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:380
#define RelationGetRelationName(relation)
Definition: rel.h:491
Oid t_tableOid
Definition: htup.h:66
#define SizeOfHeapLock
Definition: heapam_xlog.h:282
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
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:4783
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
TM_Result
Definition: tableam.h:70
#define InvalidCommandId
Definition: c.h:592
#define HEAP_XMAX_IS_LOCKED_ONLY(infomask)
Definition: htup_details.h:230
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3939
#define HEAP_KEYS_UPDATED
Definition: htup_details.h:278
#define HEAP_XMAX_IS_MULTI
Definition: htup_details.h:208
static void UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
Definition: heapam.c:1802
static bool heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode, LockWaitPolicy wait_policy, bool *have_tuple_lock)
Definition: heapam.c:4734
#define ereport(elevel,...)
Definition: elog.h:155
TransactionId MultiXactId
Definition: c.h:585
void XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, XLTW_Oper oper)
Definition: lmgr.c:639
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:792
static TM_Result heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid, TransactionId xid, LockTupleMode mode)
Definition: heapam.c:5501
Definition: lmgr.h:29
CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup)
Definition: combocid.c:118
Definition: tableam.h:76
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:619
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask, LockTupleMode lockmode, bool *current_is_member)
Definition: heapam.c:6631
#define RelationNeedsWAL(relation)
Definition: rel.h:563
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
#define TUPLOCK_from_mxstatus(status)
Definition: heapam.c:214
int errmsg(const char *fmt,...)
Definition: elog.c:915
#define XLOG_HEAP_LOCK
Definition: heapam_xlog.h:38
#define elog(elevel,...)
Definition: elog.h:228
int i
static MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
Definition: heapam.c:3996
#define HEAP_XMAX_IS_EXCL_LOCKED(infomask)
Definition: htup_details.h:264
int GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members, bool from_pgupgrade, bool onlyLock)
Definition: multixact.c:1223
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define HEAP_XMAX_IS_KEYSHR_LOCKED(infomask)
Definition: htup_details.h:266
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:227
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define RelationGetRelid(relation)
Definition: rel.h:457
static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, int *remaining)
Definition: heapam.c:6830
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
#define ItemPointerCopy(fromPointer, toPointer)
Definition: itemptr.h:161

◆ heap_multi_insert()

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

Definition at line 2115 of file heapam.c.

References Assert, AssertArg, 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_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, HeapTupleHeaderData::t_hoff, xl_multi_insert_tuple::t_hoff, HeapTupleHeaderData::t_infomask, xl_multi_insert_tuple::t_infomask, HeapTupleHeaderData::t_infomask2, xl_multi_insert_tuple::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(), and SampleHeapTupleVisible().

2117 {
2119  HeapTuple *heaptuples;
2120  int i;
2121  int ndone;
2122  PGAlignedBlock scratch;
2123  Page page;
2124  Buffer vmbuffer = InvalidBuffer;
2125  bool needwal;
2126  Size saveFreeSpace;
2127  bool need_tuple_data = RelationIsLogicallyLogged(relation);
2128  bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
2129 
2130  /* currently not needed (thus unsupported) for heap_multi_insert() */
2132 
2133  needwal = RelationNeedsWAL(relation);
2134  saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
2136 
2137  /* Toast and set header data in all the slots */
2138  heaptuples = palloc(ntuples * sizeof(HeapTuple));
2139  for (i = 0; i < ntuples; i++)
2140  {
2141  HeapTuple tuple;
2142 
2143  tuple = ExecFetchSlotHeapTuple(slots[i], true, NULL);
2144  slots[i]->tts_tableOid = RelationGetRelid(relation);
2145  tuple->t_tableOid = slots[i]->tts_tableOid;
2146  heaptuples[i] = heap_prepare_insert(relation, tuple, xid, cid,
2147  options);
2148  }
2149 
2150  /*
2151  * We're about to do the actual inserts -- but check for conflict first,
2152  * to minimize the possibility of having to roll back work we've just
2153  * done.
2154  *
2155  * A check here does not definitively prevent a serialization anomaly;
2156  * that check MUST be done at least past the point of acquiring an
2157  * exclusive buffer content lock on every buffer that will be affected,
2158  * and MAY be done after all inserts are reflected in the buffers and
2159  * those locks are released; otherwise there is a race condition. Since
2160  * multiple buffers can be locked and unlocked in the loop below, and it
2161  * would not be feasible to identify and lock all of those buffers before
2162  * the loop, we must do a final check at the end.
2163  *
2164  * The check here could be omitted with no loss of correctness; it is
2165  * present strictly as an optimization.
2166  *
2167  * For heap inserts, we only need to check for table-level SSI locks. Our
2168  * new tuples can't possibly conflict with existing tuple locks, and heap
2169  * page locks are only consolidated versions of tuple locks; they do not
2170  * lock "gaps" as index page locks do. So we don't need to specify a
2171  * buffer when making the call, which makes for a faster check.
2172  */
2174 
2175  ndone = 0;
2176  while (ndone < ntuples)
2177  {
2178  Buffer buffer;
2179  bool starting_with_empty_page;
2180  bool all_visible_cleared = false;
2181  bool all_frozen_set = false;
2182  int nthispage;
2183 
2185 
2186  /*
2187  * Find buffer where at least the next tuple will fit. If the page is
2188  * all-visible, this will also pin the requisite visibility map page.
2189  *
2190  * Also pin visibility map page if COPY FREEZE inserts tuples into an
2191  * empty page. See all_frozen_set below.
2192  */
2193  buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
2194  InvalidBuffer, options, bistate,
2195  &vmbuffer, NULL);
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 combocids 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 combocids 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 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:368
Oid tts_tableOid
Definition: tuptable.h:131
#define SizeofHeapTupleHeader
Definition: htup_details.h:184
bool IsCatalogRelation(Relation relation)
Definition: catalog.c:96
static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup)
Definition: heapam.c:8107
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple)
Definition: inval.c:1122
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid, CommandId cid, int options)
Definition: heapam.c:2061
#define XLH_INSERT_ALL_FROZEN_SET
Definition: heapam_xlog.h:73
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:575
#define HEAP_INSERT_FROZEN
Definition: heapam.h:35
void visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf, XLogRecPtr recptr, Buffer vmBuf, TransactionId cutoff_xid, uint8 flags)
OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER]
Definition: heapam_xlog.h:179
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
#define VISIBILITYMAP_ALL_FROZEN
Definition: visibilitymap.h:27
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
unsigned char uint8
Definition: c.h:427
#define InvalidBuffer
Definition: buf.h:25
#define SizeOfHeapMultiInsert
Definition: heapam_xlog.h:182
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:238
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3700
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:636
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
void RelationPutHeapTuple(Relation relation, Buffer buffer, HeapTuple tuple, bool token)
Definition: hio.c:36
#define XLOG_HEAP_INIT_PAGE
Definition: heapam_xlog.h:46
#define XLOG_HEAP2_MULTI_INSERT
Definition: heapam_xlog.h:58
uint16 OffsetNumber
Definition: off.h:24
#define VISIBILITYMAP_VALID_BITS
Definition: visibilitymap.h:28
HeapTupleHeader t_data
Definition: htup.h:68
char data[BLCKSZ]
Definition: c.h:1129
bool visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer buf, uint8 flags)
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3723
Size PageGetHeapFreeSpace(Page page)
Definition: bufpage.c:874
#define XLH_INSERT_CONTAINS_NEW_TUPLE
Definition: heapam_xlog.h:69
ItemPointerData t_self
Definition: htup.h:65
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:438
uint32 t_len
Definition: htup.h:64
#define PageSetAllVisible(page)
Definition: bufpage.h:387
#define REGBUF_STANDARD
Definition: xloginsert.h:35
Buffer RelationGetBufferForTuple(Relation relation, Size len, Buffer otherBuffer, int options, BulkInsertState bistate, Buffer *vmbuffer, Buffer *vmbuffer_other)
Definition: hio.c:331
#define InvalidTransactionId
Definition: transam.h:31
Oid t_tableOid
Definition: htup.h:66
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:404
#define init()
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define AssertArg(condition)
Definition: c.h:794
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1614
#define RelationGetTargetPageFreeSpace(relation, defaultff)
Definition: rel.h:341
#define XLH_INSERT_LAST_IN_MULTI
Definition: heapam_xlog.h:67
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
#define RelationIsAccessibleInLogicalDecoding(relation)
Definition: rel.h:620
#define REGBUF_KEEP_DATA
Definition: xloginsert.h:38
void CheckForSerializableConflictIn(Relation relation, ItemPointer tid, BlockNumber blkno)
Definition: predicate.c:4446
#define PageClearAllVisible(page)
Definition: bufpage.h:389
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:792
size_t Size
Definition: c.h:528
#define InvalidBlockNumber
Definition: block.h:33
#define MAXALIGN(LEN)
Definition: c.h:745
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define RelationNeedsWAL(relation)
Definition: rel.h:563
#define VISIBILITYMAP_ALL_VISIBLE
Definition: visibilitymap.h:26
#define XLH_INSERT_ALL_VISIBLE_CLEARED
Definition: heapam_xlog.h:66
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2674
void pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
Definition: pgstat.c:2175
void * palloc(Size size)
Definition: mcxt.c:950
#define HEAP_INSERT_NO_LOGICAL
Definition: heapam.h:36
int i
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:100
bool visibilitymap_pin_ok(BlockNumber heapBlk, Buffer buf)
#define HEAP_DEFAULT_FILLFACTOR
Definition: rel.h:312
#define SHORTALIGN(LEN)
Definition: c.h:741
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define RelationGetRelid(relation)
Definition: rel.h:457
Pointer Page
Definition: bufpage.h:78
#define SizeOfMultiInsertTuple
Definition: heapam_xlog.h:193

◆ heap_page_prune()

int heap_page_prune ( Relation  relation,
Buffer  buffer,
struct GlobalVisState vistest,
TransactionId  limited_oldest_xmin,
TimestampTz  limited_oldest_ts,
bool  report_stats,
TransactionId latestRemovedXid,
OffsetNumber off_loc 
)

Definition at line 223 of file pruneheap.c.

References BufferGetPage, END_CRIT_SECTION, FirstOffsetNumber, heap_page_prune_execute(), heap_prune_chain(), InvalidOffsetNumber, InvalidTransactionId, ItemIdIsDead, ItemIdIsUsed, PruneState::latestRemovedXid, log_heap_clean(), MarkBufferDirty(), MarkBufferDirtyHint(), PruneState::marked, PruneState::ndead, PruneState::new_prune_xid, PruneState::nowdead, PruneState::nowunused, PruneState::nredirected, PruneState::nunused, OffsetNumberNext, PruneState::old_snap_ts, PruneState::old_snap_used, PruneState::old_snap_xmin, PageClearFull, PageGetItemId, PageGetMaxOffsetNumber, PageIsFull, PageSetLSN, pgstat_update_heap_dead_tuples(), PruneState::redirected, PruneState::rel, RelationNeedsWAL, START_CRIT_SECTION, and PruneState::vistest.

Referenced by heap_page_prune_opt(), and lazy_scan_heap().

229 {
230  int ndeleted = 0;
231  Page page = BufferGetPage(buffer);
232  OffsetNumber offnum,
233  maxoff;
234  PruneState prstate;
235 
236  /*
237  * Our strategy is to scan the page and make lists of items to change,
238  * then apply the changes within a critical section. This keeps as much
239  * logic as possible out of the critical section, and also ensures that
240  * WAL replay will work the same as the normal case.
241  *
242  * First, initialize the new pd_prune_xid value to zero (indicating no
243  * prunable tuples). If we find any tuples which may soon become
244  * prunable, we will save the lowest relevant XID in new_prune_xid. Also
245  * initialize the rest of our working state.
246  */
248  prstate.rel = relation;
249  prstate.vistest = vistest;
250  prstate.old_snap_xmin = old_snap_xmin;
251  prstate.old_snap_ts = old_snap_ts;
252  prstate.old_snap_used = false;
253  prstate.latestRemovedXid = *latestRemovedXid;
254  prstate.nredirected = prstate.ndead = prstate.nunused = 0;
255  memset(prstate.marked, 0, sizeof(prstate.marked));
256 
257  /* Scan the page */
258  maxoff = PageGetMaxOffsetNumber(page);
259  for (offnum = FirstOffsetNumber;
260  offnum <= maxoff;
261  offnum = OffsetNumberNext(offnum))
262  {
263  ItemId itemid;
264 
265  /* Ignore items already processed as part of an earlier chain */
266  if (prstate.marked[offnum])
267  continue;
268 
269  /*
270  * Set the offset number so that we can display it along with any
271  * error that occurred while processing this tuple.
272  */
273  if (off_loc)
274  *off_loc = offnum;
275 
276  /* Nothing to do if slot is empty or already dead */
277  itemid = PageGetItemId(page, offnum);
278  if (!ItemIdIsUsed(itemid) || ItemIdIsDead(itemid))
279  continue;
280 
281  /* Process this item or chain of items */
282  ndeleted += heap_prune_chain(buffer, offnum, &prstate);
283  }
284 
285  /* Clear the offset information once we have processed the given page. */
286  if (off_loc)
287  *off_loc = InvalidOffsetNumber;
288 
289  /* Any error while applying the changes is critical */
291 
292  /* Have we found any prunable items? */
293  if (prstate.nredirected > 0 || prstate.ndead > 0 || prstate.nunused > 0)
294  {
295  /*
296  * Apply the planned item changes, then repair page fragmentation, and
297  * update the page's hint bit about whether it has free line pointers.
298  */
300  prstate.redirected, prstate.nredirected,
301  prstate.nowdead, prstate.ndead,
302  prstate.nowunused, prstate.nunused);
303 
304  /*
305  * Update the page's pd_prune_xid field to either zero, or the lowest
306  * XID of any soon-prunable tuple.
307  */
308  ((PageHeader) page)->pd_prune_xid = prstate.new_prune_xid;
309 
310  /*
311  * Also clear the "page is full" flag, since there's no point in
312  * repeating the prune/defrag process until something else happens to
313  * the page.
314  */
315  PageClearFull(page);
316 
317  MarkBufferDirty(buffer);
318 
319  /*
320  * Emit a WAL XLOG_HEAP2_CLEAN record showing what we did
321  */
322  if (RelationNeedsWAL(relation))
323  {
324  XLogRecPtr recptr;
325 
326  recptr = log_heap_clean(relation, buffer,
327  prstate.redirected, prstate.nredirected,
328  prstate.nowdead, prstate.ndead,
329  prstate.nowunused, prstate.nunused,
330  prstate.latestRemovedXid);
331 
332  PageSetLSN(BufferGetPage(buffer), recptr);
333  }
334  }
335  else
336  {
337  /*
338  * If we didn't prune anything, but have found a new value for the
339  * pd_prune_xid field, update it and mark the buffer dirty. This is
340  * treated as a non-WAL-logged hint.
341  *
342  * Also clear the "page is full" flag if it is set, since there's no
343  * point in repeating the prune/defrag process until something else
344  * happens to the page.
345  */
346  if (((PageHeader) page)->pd_prune_xid != prstate.new_prune_xid ||
347  PageIsFull(page))
348  {
349  ((PageHeader) page)->pd_prune_xid = prstate.new_prune_xid;
350  PageClearFull(page);
351  MarkBufferDirtyHint(buffer, true);
352  }
353  }
354 
356 
357  /*
358  * If requested, report the number of tuples reclaimed to pgstats. This is
359  * ndeleted minus ndead, because we don't want to count a now-DEAD root
360  * item as a deletion for this purpose.
361  */
362  if (report_stats && ndeleted > prstate.ndead)
363  pgstat_update_heap_dead_tuples(relation, ndeleted - prstate.ndead);
364 
365  *latestRemovedXid = prstate.latestRemovedXid;
366 
367  /*
368  * XXX Should we update the FSM information of this page ?
369  *
370  * There are two schools of thought here. We may not want to update FSM
371  * information so that the page is not used for unrelated UPDATEs/INSERTs
372  * and any free space in this page will remain available for further
373  * UPDATEs in *this* page, thus improving chances for doing HOT updates.
374  *
375  * But for a large table and where a page does not receive further UPDATEs
376  * for a long time, we might waste this space by not updating the FSM
377  * information. The relation may get extended and fragmented further.
378  *
379  * One possibility is to leave "fillfactor" worth of space in this page
380  * and update FSM with the remaining space.
381  */
382 
383  return ndeleted;
384 }
int nredirected
Definition: pruneheap.c:52
static int heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
Definition: pruneheap.c:492
void pgstat_update_heap_dead_tuples(Relation rel, int delta)
Definition: pgstat.c:2305
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:3770
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
bool old_snap_used
Definition: pruneheap.c:48
Relation rel
Definition: pruneheap.c:33
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
TimestampTz old_snap_ts
Definition: pruneheap.c:46
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
OffsetNumber nowdead[MaxHeapTuplesPerPage]
Definition: pruneheap.c:57
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
bool marked[MaxHeapTuplesPerPage+1]
Definition: pruneheap.c:60
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
TransactionId new_prune_xid
Definition: pruneheap.c:50
#define PageIsFull(page)
Definition: bufpage.h:378
int nunused
Definition: pruneheap.c:54
uint16 OffsetNumber
Definition: off.h:24
TransactionId latestRemovedXid
Definition: pruneheap.c:51
#define FirstOffsetNumber
Definition: off.h:27
#define InvalidTransactionId
Definition: transam.h:31
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageClearFull(page)
Definition: bufpage.h:382
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
TransactionId old_snap_xmin
Definition: pruneheap.c:47
#define InvalidOffsetNumber
Definition: off.h:26
GlobalVisState * vistest
Definition: pruneheap.c:36
void heap_page_prune_execute(Buffer buffer, OffsetNumber *redirected, int nredirected, OffsetNumber *nowdead, int ndead, OffsetNumber *nowunused, int nunused)
Definition: pruneheap.c:819
PageHeaderData * PageHeader
Definition: bufpage.h:166
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
OffsetNumber nowunused[MaxHeapTuplesPerPage]
Definition: pruneheap.c:58
XLogRecPtr log_heap_clean(Relation reln, Buffer buffer, OffsetNumber *redirected, int nredirected, OffsetNumber *nowdead, int ndead, OffsetNumber *nowunused, int nunused, TransactionId latestRemovedXid)
Definition: heapam.c:7761
#define RelationNeedsWAL(relation)
Definition: rel.h:563
OffsetNumber redirected[MaxHeapTuplesPerPage *2]
Definition: pruneheap.c:56
int ndead
Definition: pruneheap.c:53
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
Pointer Page
Definition: bufpage.h:78

◆ 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 819 of file pruneheap.c.

References BufferGetPage, i, ItemIdSetDead, ItemIdSetRedirect, ItemIdSetUnused, PageGetItemId, and PageRepairFragmentation().

Referenced by heap_page_prune(), and heap_xlog_clean().

823 {
824  Page page = (Page) BufferGetPage(buffer);
825  OffsetNumber *offnum;
826  int i;
827 
828  /* Update all redirected line pointers */
829  offnum = redirected;
830  for (i = 0; i < nredirected; i++)
831  {
832  OffsetNumber fromoff = *offnum++;
833  OffsetNumber tooff = *offnum++;
834  ItemId fromlp = PageGetItemId(page, fromoff);
835 
836  ItemIdSetRedirect(fromlp, tooff);
837  }
838 
839  /* Update all now-dead line pointers */
840  offnum = nowdead;
841  for (i = 0; i < ndead; i++)
842  {
843  OffsetNumber off = *offnum++;
844  ItemId lp = PageGetItemId(page, off);
845 
846  ItemIdSetDead(lp);
847  }
848 
849  /* Update all now-unused line pointers */
850  offnum = nowunused;
851  for (i = 0; i < nunused; i++)
852  {
853  OffsetNumber off = *offnum++;
854  ItemId lp = PageGetItemId(page, off);
855 
856  ItemIdSetUnused(lp);
857  }
858 
859  /*
860  * Finally, repair any fragmentation, and update the page's hint bit about
861  * whether it has free pointers.
862  */
864 }
uint16 OffsetNumber
Definition: off.h:24
#define ItemIdSetRedirect(itemId, link)
Definition: itemid.h:152
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void PageRepairFragmentation(Page page)
Definition: bufpage.c:682
int i
#define ItemIdSetDead(itemId)
Definition: itemid.h:164
#define ItemIdSetUnused(itemId)
Definition: itemid.h:128
Pointer Page
Definition: bufpage.h:78

◆ heap_page_prune_opt()

void heap_page_prune_opt ( Relation  relation,
Buffer  buffer 
)

Definition at line 87 of file pruneheap.c.

References BUFFER_LOCK_UNLOCK, BufferGetPage, ConditionalLockBufferForCleanup(), GlobalVisTestFor(), GlobalVisTestIsRemovableXid(), GlobalVisTestNonRemovableHorizon(), HEAP_DEFAULT_FILLFACTOR, heap_page_prune(), InvalidTransactionId, LockBuffer(), Max, old_snapshot_threshold, OldSnapshotThresholdActive(), PageGetHeapFreeSpace(), PageIsFull, RecoveryInProgress(), RelationGetTargetPageFreeSpace, SnapshotTooOldMagicForTest(), TransactionIdIsValid, TransactionIdLimitedForOldSnapshots(), and TransactionIdPrecedes().

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

88 {
89  Page page = BufferGetPage(buffer);
90  TransactionId prune_xid;
91  GlobalVisState *vistest;
92  TransactionId limited_xmin = InvalidTransactionId;
93  TimestampTz limited_ts = 0;
94  Size minfree;
95 
96  /*
97  * We can't write WAL in recovery mode, so there's no point trying to
98  * clean the page. The primary will likely issue a cleaning WAL record soon
99  * anyway, so this is no particular loss.
100  */
101  if (RecoveryInProgress())
102  return;
103 
104  /*
105  * XXX: Magic to keep old_snapshot_threshold tests appear "working". They
106  * currently are broken, and discussion of what to do about them is
107  * ongoing. See
108  * https://www.postgresql.org/message-id/20200403001235.e6jfdll3gh2ygbuc%40alap3.anarazel.de
109  */
110  if (old_snapshot_threshold == 0)
112 
113  /*
114  * First check whether there's any chance there's something to prune,
115  * determining the appropriate horizon is a waste if there's no prune_xid
116  * (i.e. no updates/deletes left potentially dead tuples around).
117  */
118  prune_xid = ((PageHeader) page)->pd_prune_xid;
119  if (!TransactionIdIsValid(prune_xid))
120  return;
121 
122  /*
123  * Check whether prune_xid indicates that there may be dead rows that can
124  * be cleaned up.
125  *
126  * It is OK to check the old snapshot limit before acquiring the cleanup
127  * lock because the worst that can happen is that we are not quite as
128  * aggressive about the cleanup (by however many transaction IDs are
129  * consumed between this point and acquiring the lock). This allows us to
130  * save significant overhead in the case where the page is found not to be
131  * prunable.
132  *
133  * Even if old_snapshot_threshold is set, we first check whether the page
134  * can be pruned without. Both because
135  * TransactionIdLimitedForOldSnapshots() is not cheap, and because not
136  * unnecessarily relying on old_snapshot_threshold avoids causing
137  * conflicts.
138  */
139  vistest = GlobalVisTestFor(relation);
140 
141  if (!GlobalVisTestIsRemovableXid(vistest, prune_xid))
142  {
144  return;
145 
147  relation,
148  &limited_xmin, &limited_ts))
149  return;
150 
151  if