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)
 
void setLastTid (const 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_compute_xid_horizon_for_tuples (Relation rel, ItemPointerData *items, int nitems)
 
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_prepare_insert().

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

1821 {
1822  if (bistate->current_buf != InvalidBuffer)
1823  ReleaseBuffer(bistate->current_buf);
1824  FreeAccessStrategy(bistate->strategy);
1825  pfree(bistate);
1826 }
#define InvalidBuffer
Definition: buf.h:25
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3513
void pfree(void *pointer)
Definition: mcxt.c:1057
void FreeAccessStrategy(BufferAccessStrategy strategy)
Definition: freelist.c:597

◆ GetBulkInsertState()

BulkInsertState GetBulkInsertState ( void  )

Definition at line 1806 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().

1807 {
1808  BulkInsertState bistate;
1809 
1810  bistate = (BulkInsertState) palloc(sizeof(BulkInsertStateData));
1812  bistate->current_buf = InvalidBuffer;
1813  return bistate;
1814 }
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 5566 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().

5567 {
5569  ItemId lp;
5570  HeapTupleData tp;
5571  Page page;
5572  BlockNumber block;
5573  Buffer buffer;
5574  TransactionId prune_xid;
5575 
5576  Assert(ItemPointerIsValid(tid));
5577 
5578  block = ItemPointerGetBlockNumber(tid);
5579  buffer = ReadBuffer(relation, block);
5580  page = BufferGetPage(buffer);
5581 
5583 
5584  /*
5585  * Page can't be all visible, we just inserted into it, and are still
5586  * running.
5587  */
5588  Assert(!PageIsAllVisible(page));
5589 
5590  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
5591  Assert(ItemIdIsNormal(lp));
5592 
5593  tp.t_tableOid = RelationGetRelid(relation);
5594  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
5595  tp.t_len = ItemIdGetLength(lp);
5596  tp.t_self = *tid;
5597 
5598  /*
5599  * Sanity check that the tuple really is a speculatively inserted tuple,
5600  * inserted by us.
5601  */
5602  if (tp.t_data->t_choice.t_heap.t_xmin != xid)
5603  elog(ERROR, "attempted to kill a tuple inserted by another transaction");
5604  if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
5605  elog(ERROR, "attempted to kill a non-speculative tuple");
5607 
5608  /*
5609  * No need to check for serializable conflicts here. There is never a
5610  * need for a combocid, either. No need to extract replica identity, or
5611  * do anything special with infomask bits.
5612  */
5613 
5615 
5616  /*
5617  * The tuple will become DEAD immediately. Flag that this page is a
5618  * candidate for pruning by setting xmin to TransactionXmin. While not
5619  * immediately prunable, it is the oldest xid we can cheaply determine
5620  * that's safe against wraparound / being older than the table's
5621  * relfrozenxid. To defend against the unlikely case of a new relation
5622  * having a newer relfrozenxid than our TransactionXmin, use relfrozenxid
5623  * if so (vacuum can't subsequently move relfrozenxid to beyond
5624  * TransactionXmin, so there's no race here).
5625  */
5627  if (TransactionIdPrecedes(TransactionXmin, relation->rd_rel->relfrozenxid))
5628  prune_xid = relation->rd_rel->relfrozenxid;
5629  else
5630  prune_xid = TransactionXmin;
5631  PageSetPrunable(page, prune_xid);
5632 
5633  /* store transaction information of xact deleting the tuple */
5636 
5637  /*
5638  * Set the tuple header xmin to InvalidTransactionId. This makes the
5639  * tuple immediately invisible everyone. (In particular, to any
5640  * transactions waiting on the speculative token, woken up later.)
5641  */
5643 
5644  /* Clear the speculative insertion token too */
5645  tp.t_data->t_ctid = tp.t_self;
5646 
5647  MarkBufferDirty(buffer);
5648 
5649  /*
5650  * XLOG stuff
5651  *
5652  * The WAL records generated here match heap_delete(). The same recovery
5653  * routines are used.
5654  */
5655  if (RelationNeedsWAL(relation))
5656  {
5657  xl_heap_delete xlrec;
5658  XLogRecPtr recptr;
5659 
5660  xlrec.flags = XLH_DELETE_IS_SUPER;
5662  tp.t_data->t_infomask2);
5664  xlrec.xmax = xid;
5665 
5666  XLogBeginInsert();
5667  XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
5668  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
5669 
5670  /* No replica identity & replication origin logged */
5671 
5672  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
5673 
5674  PageSetLSN(page, recptr);
5675  }
5676 
5677  END_CRIT_SECTION();
5678 
5679  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
5680 
5681  if (HeapTupleHasExternal(&tp))
5682  {
5683  Assert(!IsToastRelation(relation));
5684  heap_toast_delete(relation, &tp, true);
5685  }
5686 
5687  /*
5688  * Never need to mark tuple for invalidation, since catalogs don't support
5689  * speculative insertion
5690  */
5691 
5692  /* Now we can release the buffer */
5693  ReleaseBuffer(buffer);
5694 
5695  /* count deletion, as we counted the insertion too */
5696  pgstat_count_heap_delete(relation);
5697 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
bool IsToastRelation(Relation relation)
Definition: catalog.c:140
#define HEAP_XMAX_BITS
Definition: htup_details.h:270
#define XLH_DELETE_IS_SUPER
Definition: heapam_xlog.h:96
static uint8 compute_infobits(uint16 infomask, uint16 infomask2)
Definition: heapam.c:2397
HeapTupleFields t_heap
Definition: htup_details.h:156
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:521
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1471
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:134
#define HeapTupleHeaderIsSpeculative(tup)
Definition: htup_details.h:429
#define PageSetPrunable(page, xid)
Definition: bufpage.h:392
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3513
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
Form_pg_class rd_rel
Definition: rel.h:109
OffsetNumber offnum
Definition: heapam_xlog.h:107
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:43
ItemPointerData t_ctid
Definition: htup_details.h:160
ItemPointerData t_self
Definition: htup.h:65
TransactionId xmax
Definition: heapam_xlog.h:106
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:112
#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:3752
#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:746
uint8 infobits_set
Definition: heapam_xlog.h:108
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:607
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
union HeapTupleHeaderData::@44 t_choice
#define RelationNeedsWAL(relation)
Definition: rel.h:562
void pgstat_count_heap_delete(Relation rel)
Definition: pgstat.c:2157
#define HeapTupleHasExternal(tuple)
Definition: htup_details.h:673
#define elog(elevel,...)
Definition: elog.h:214
#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:456
#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 1141 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().

1145 {
1146  HeapScanDesc scan;
1147 
1148  /*
1149  * increment relation ref count while scanning relation
1150  *
1151  * This is just to make really sure the relcache entry won't go away while
1152  * the scan has a pointer to it. Caller should be holding the rel open
1153  * anyway, so this is redundant in all normal scenarios...
1154  */
1156 
1157  /*
1158  * allocate and initialize scan descriptor
1159  */
1160  scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
1161 
1162  scan->rs_base.rs_rd = relation;
1163  scan->rs_base.rs_snapshot = snapshot;
1164  scan->rs_base.rs_nkeys = nkeys;
1165  scan->rs_base.rs_flags = flags;
1166  scan->rs_base.rs_parallel = parallel_scan;
1167  scan->rs_base.rs_private =
1169  scan->rs_strategy = NULL; /* set in initscan */
1170 
1171  /*
1172  * Disable page-at-a-time mode if it's not a MVCC-safe snapshot.
1173  */
1174  if (!(snapshot && IsMVCCSnapshot(snapshot)))
1176 
1177  /*
1178  * For seqscan and sample scans in a serializable transaction, acquire a
1179  * predicate lock on the entire relation. This is required not only to
1180  * lock all the matching tuples, but also to conflict with new insertions
1181  * into the table. In an indexscan, we take page locks on the index pages
1182  * covering the range specified in the scan qual, but in a heap scan there
1183  * is nothing more fine-grained to lock. A bitmap scan is a different
1184  * story, there we have already scanned the index and locked the index
1185  * pages covering the predicate. But in that case we still have to lock
1186  * any matching heap tuples. For sample scan we could optimize the locking
1187  * to be at least page-level granularity, but we'd need to add per-tuple
1188  * locking for that.
1189  */
1191  {
1192  /*
1193  * Ensure a missing snapshot is noticed reliably, even if the
1194  * isolation mode means predicate locking isn't performed (and
1195  * therefore the snapshot isn't used here).
1196  */
1197  Assert(snapshot);
1198  PredicateLockRelation(relation, snapshot);
1199  }
1200 
1201  /* we only need to set this up once */
1202  scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
1203 
1204  /*
1205  * we do this here instead of in initscan() because heap_rescan also calls
1206  * initscan() and we don't want to allocate memory again
1207  */
1208  if (nkeys > 0)
1209  scan->rs_base.rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
1210  else
1211  scan->rs_base.rs_key = NULL;
1212 
1213  initscan(scan, key, false);
1214 
1215  return (TableScanDesc) scan;
1216 }
TableScanDescData rs_base
Definition: heapam.h:49
void PredicateLockRelation(Relation relation, Snapshot snapshot)
Definition: predicate.c:2498
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:2077
BufferAccessStrategy rs_strategy
Definition: heapam.h:64
#define IsMVCCSnapshot(snapshot)
Definition: snapmgr.h:97
#define Assert(condition)
Definition: c.h:746
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:209
#define RelationGetRelid(relation)
Definition: rel.h:456

◆ heap_compute_xid_horizon_for_tuples()

TransactionId heap_compute_xid_horizon_for_tuples ( Relation  rel,
ItemPointerData items,
int  nitems 
)

Definition at line 6992 of file heapam.c.

References Assert, buf, BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetPage, BufferIsValid, CHECK_FOR_INTERRUPTS, get_tablespace_maintenance_io_concurrency(), HeapTupleHeaderAdvanceLatestRemovedXid(), i, InvalidBlockNumber, InvalidBuffer, InvalidTransactionId, IsCatalogRelation(), ItemIdGetRedirect, ItemIdHasStorage, ItemIdIsDead, ItemIdIsRedirected, ItemIdIsUsed, ItemPointerCompare(), ItemPointerGetBlockNumber, ItemPointerGetOffsetNumber, LockBuffer(), maintenance_io_concurrency, PageGetItem, PageGetItemId, qsort, RelationData::rd_rel, ReadBuffer(), and ReleaseBuffer().

Referenced by SampleHeapTupleVisible().

6995 {
6996  TransactionId latestRemovedXid = InvalidTransactionId;
6997  BlockNumber hblkno;
6999  Page hpage;
7000 #ifdef USE_PREFETCH
7001  XidHorizonPrefetchState prefetch_state;
7002  int prefetch_distance;
7003 #endif
7004 
7005  /*
7006  * Sort to avoid repeated lookups for the same page, and to make it more
7007  * likely to access items in an efficient order. In particular, this
7008  * ensures that if there are multiple pointers to the same page, they all
7009  * get processed looking up and locking the page just once.
7010  */
7011  qsort((void *) tids, nitems, sizeof(ItemPointerData),
7012  (int (*) (const void *, const void *)) ItemPointerCompare);
7013 
7014 #ifdef USE_PREFETCH
7015  /* Initialize prefetch state. */
7016  prefetch_state.cur_hblkno = InvalidBlockNumber;
7017  prefetch_state.next_item = 0;
7018  prefetch_state.nitems = nitems;
7019  prefetch_state.tids = tids;
7020 
7021  /*
7022  * Compute the prefetch distance that we will attempt to maintain.
7023  *
7024  * Since the caller holds a buffer lock somewhere in rel, we'd better make
7025  * sure that isn't a catalog relation before we call code that does
7026  * syscache lookups, to avoid risk of deadlock.
7027  */
7028  if (IsCatalogRelation(rel))
7029  prefetch_distance = maintenance_io_concurrency;
7030  else
7031  prefetch_distance =
7033 
7034  /* Start prefetching. */
7035  xid_horizon_prefetch_buffer(rel, &prefetch_state, prefetch_distance);
7036 #endif
7037 
7038  /* Iterate over all tids, and check their horizon */
7039  hblkno = InvalidBlockNumber;
7040  hpage = NULL;
7041  for (int i = 0; i < nitems; i++)
7042  {
7043  ItemPointer htid = &tids[i];
7044  ItemId hitemid;
7045  OffsetNumber hoffnum;
7046 
7047  /*
7048  * Read heap buffer, but avoid refetching if it's the same block as
7049  * required for the last tid.
7050  */
7051  if (hblkno == InvalidBlockNumber ||
7052  ItemPointerGetBlockNumber(htid) != hblkno)
7053  {
7054  /* release old buffer */
7055  if (BufferIsValid(buf))
7056  {
7058  ReleaseBuffer(buf);
7059  }
7060 
7061  hblkno = ItemPointerGetBlockNumber(htid);
7062 
7063  buf = ReadBuffer(rel, hblkno);
7064 
7065 #ifdef USE_PREFETCH
7066 
7067  /*
7068  * To maintain the prefetch distance, prefetch one more page for
7069  * each page we read.
7070  */
7071  xid_horizon_prefetch_buffer(rel, &prefetch_state, 1);
7072 #endif
7073 
7074  hpage = BufferGetPage(buf);
7075 
7077  }
7078 
7079  hoffnum = ItemPointerGetOffsetNumber(htid);
7080  hitemid = PageGetItemId(hpage, hoffnum);
7081 
7082  /*
7083  * Follow any redirections until we find something useful.
7084  */
7085  while (ItemIdIsRedirected(hitemid))
7086  {
7087  hoffnum = ItemIdGetRedirect(hitemid);
7088  hitemid = PageGetItemId(hpage, hoffnum);
7090  }
7091 
7092  /*
7093  * If the heap item has storage, then read the header and use that to
7094  * set latestRemovedXid.
7095  *
7096  * Some LP_DEAD items may not be accessible, so we ignore them.
7097  */
7098  if (ItemIdHasStorage(hitemid))
7099  {
7100  HeapTupleHeader htuphdr;
7101 
7102  htuphdr = (HeapTupleHeader) PageGetItem(hpage, hitemid);
7103 
7104  HeapTupleHeaderAdvanceLatestRemovedXid(htuphdr, &latestRemovedXid);
7105  }
7106  else if (ItemIdIsDead(hitemid))
7107  {
7108  /*
7109  * Conjecture: if hitemid is dead then it had xids before the xids
7110  * marked on LP_NORMAL items. So we just ignore this item and move
7111  * onto the next, for the purposes of calculating
7112  * latestRemovedXid.
7113  */
7114  }
7115  else
7116  Assert(!ItemIdIsUsed(hitemid));
7117 
7118  }
7119 
7120  if (BufferIsValid(buf))
7121  {
7123  ReleaseBuffer(buf);
7124  }
7125 
7126  /*
7127  * If all heap tuples were LP_DEAD then we will be returning
7128  * InvalidTransactionId here, which avoids conflicts. This matches
7129  * existing logic which assumes that LP_DEAD tuples must already be older
7130  * than the latestRemovedXid on the cleanup record that set them as
7131  * LP_DEAD, hence must already have generated a conflict.
7132  */
7133 
7134  return latestRemovedXid;
7135 }
int32 ItemPointerCompare(ItemPointer arg1, ItemPointer arg2)
Definition: itemptr.c:52
void HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple, TransactionId *latestRemovedXid)
Definition: heapam.c:6903
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
bool IsCatalogRelation(Relation relation)
Definition: catalog.c:98
int maintenance_io_concurrency
Definition: bufmgr.c:142
#define ItemIdIsRedirected(itemId)
Definition: itemid.h:106
uint32 TransactionId
Definition: c.h:521
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define ItemIdGetRedirect(itemId)
Definition: itemid.h:78
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
#define InvalidBuffer
Definition: buf.h:25
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3513
Form_pg_class rd_rel
Definition: rel.h:109
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
uint16 OffsetNumber
Definition: off.h:24
int get_tablespace_maintenance_io_concurrency(Oid spcid)
Definition: spccache.c:229
static char * buf
Definition: pg_test_fsync.c:68
#define InvalidTransactionId
Definition: transam.h:31
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3752
#define ItemIdHasStorage(itemId)
Definition: itemid.h:120
#define Assert(condition)
Definition: c.h:746
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:607
#define InvalidBlockNumber
Definition: block.h:33
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
int i
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:97
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define qsort(a, b, c, d)
Definition: port.h:497
int Buffer
Definition: buf.h:23
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78

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

2445 {
2446  TM_Result result;
2448  ItemId lp;
2449  HeapTupleData tp;
2450  Page page;
2451  BlockNumber block;
2452  Buffer buffer;
2453  Buffer vmbuffer = InvalidBuffer;
2454  TransactionId new_xmax;
2455  uint16 new_infomask,
2456  new_infomask2;
2457  bool have_tuple_lock = false;
2458  bool iscombo;
2459  bool all_visible_cleared = false;
2460  HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
2461  bool old_key_copied = false;
2462 
2463  Assert(ItemPointerIsValid(tid));
2464 
2465  /*
2466  * Forbid this during a parallel operation, lest it allocate a combocid.
2467  * Other workers might need that combocid for visibility checks, and we
2468  * have no provision for broadcasting it to them.
2469  */
2470  if (IsInParallelMode())
2471  ereport(ERROR,
2472  (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
2473  errmsg("cannot delete tuples during a parallel operation")));
2474 
2475  block = ItemPointerGetBlockNumber(tid);
2476  buffer = ReadBuffer(relation, block);
2477  page = BufferGetPage(buffer);
2478 
2479  /*
2480  * Before locking the buffer, pin the visibility map page if it appears to
2481  * be necessary. Since we haven't got the lock yet, someone else might be
2482  * in the middle of changing this, so we'll need to recheck after we have
2483  * the lock.
2484  */
2485  if (PageIsAllVisible(page))
2486  visibilitymap_pin(relation, block, &vmbuffer);
2487 
2489 
2490  /*
2491  * If we didn't pin the visibility map page and the page has become all
2492  * visible while we were busy locking the buffer, we'll have to unlock and
2493  * re-lock, to avoid holding the buffer lock across an I/O. That's a bit
2494  * unfortunate, but hopefully shouldn't happen often.
2495  */
2496  if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
2497  {
2498  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2499  visibilitymap_pin(relation, block, &vmbuffer);
2501  }
2502 
2503  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
2504  Assert(ItemIdIsNormal(lp));
2505 
2506  tp.t_tableOid = RelationGetRelid(relation);
2507  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2508  tp.t_len = ItemIdGetLength(lp);
2509  tp.t_self = *tid;
2510 
2511 l1:
2512  result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
2513 
2514  if (result == TM_Invisible)
2515  {
2516  UnlockReleaseBuffer(buffer);
2517  ereport(ERROR,
2518  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2519  errmsg("attempted to delete invisible tuple")));
2520  }
2521  else if (result == TM_BeingModified && wait)
2522  {
2523  TransactionId xwait;
2524  uint16 infomask;
2525 
2526  /* must copy state data before unlocking buffer */
2527  xwait = HeapTupleHeaderGetRawXmax(tp.t_data);
2528  infomask = tp.t_data->t_infomask;
2529 
2530  /*
2531  * Sleep until concurrent transaction ends -- except when there's a
2532  * single locker and it's our own transaction. Note we don't care
2533  * which lock mode the locker has, because we need the strongest one.
2534  *
2535  * Before sleeping, we need to acquire tuple lock to establish our
2536  * priority for the tuple (see heap_lock_tuple). LockTuple will
2537  * release us when we are next-in-line for the tuple.
2538  *
2539  * If we are forced to "start over" below, we keep the tuple lock;
2540  * this arranges that we stay at the head of the line while rechecking
2541  * tuple state.
2542  */
2543  if (infomask & HEAP_XMAX_IS_MULTI)
2544  {
2545  bool current_is_member = false;
2546 
2547  if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
2548  LockTupleExclusive, &current_is_member))
2549  {
2550  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2551 
2552  /*
2553  * Acquire the lock, if necessary (but skip it when we're
2554  * requesting a lock and already have one; avoids deadlock).
2555  */
2556  if (!current_is_member)
2558  LockWaitBlock, &have_tuple_lock);
2559 
2560  /* wait for multixact */
2562  relation, &(tp.t_self), XLTW_Delete,
2563  NULL);
2565 
2566  /*
2567  * If xwait had just locked the tuple then some other xact
2568  * could update this tuple before we get to this point. Check
2569  * for xmax change, and start over if so.
2570  */
2571  if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
2573  xwait))
2574  goto l1;
2575  }
2576 
2577  /*
2578  * You might think the multixact is necessarily done here, but not
2579  * so: it could have surviving members, namely our own xact or
2580  * other subxacts of this backend. It is legal for us to delete
2581  * the tuple in either case, however (the latter case is
2582  * essentially a situation of upgrading our former shared lock to
2583  * exclusive). We don't bother changing the on-disk hint bits
2584  * since we are about to overwrite the xmax altogether.
2585  */
2586  }
2587  else if (!TransactionIdIsCurrentTransactionId(xwait))
2588  {
2589  /*
2590  * Wait for regular transaction to end; but first, acquire tuple
2591  * lock.
2592  */
2593  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2595  LockWaitBlock, &have_tuple_lock);
2596  XactLockTableWait(xwait, relation, &(tp.t_self), XLTW_Delete);
2598 
2599  /*
2600  * xwait is done, but if xwait had just locked the tuple then some
2601  * other xact could update this tuple before we get to this point.
2602  * Check for xmax change, and start over if so.
2603  */
2604  if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
2606  xwait))
2607  goto l1;
2608 
2609  /* Otherwise check if it committed or aborted */
2610  UpdateXmaxHintBits(tp.t_data, buffer, xwait);
2611  }
2612 
2613  /*
2614  * We may overwrite if previous xmax aborted, or if it committed but
2615  * only locked the tuple without updating it.
2616  */
2617  if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
2620  result = TM_Ok;
2621  else if (!ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid) ||
2623  result = TM_Updated;
2624  else
2625  result = TM_Deleted;
2626  }
2627 
2628  if (crosscheck != InvalidSnapshot && result == TM_Ok)
2629  {
2630  /* Perform additional check for transaction-snapshot mode RI updates */
2631  if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
2632  result = TM_Updated;
2633  }
2634 
2635  if (result != TM_Ok)
2636  {
2637  Assert(result == TM_SelfModified ||
2638  result == TM_Updated ||
2639  result == TM_Deleted ||
2640  result == TM_BeingModified);
2642  Assert(result != TM_Updated ||
2643  !ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid));
2644  tmfd->ctid = tp.t_data->t_ctid;
2646  if (result == TM_SelfModified)
2647  tmfd->cmax = HeapTupleHeaderGetCmax(tp.t_data);
2648  else
2649  tmfd->cmax = InvalidCommandId;
2650  UnlockReleaseBuffer(buffer);
2651  if (have_tuple_lock)
2652  UnlockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive);
2653  if (vmbuffer != InvalidBuffer)
2654  ReleaseBuffer(vmbuffer);
2655  return result;
2656  }
2657 
2658  /*
2659  * We're about to do the actual delete -- check for conflict first, to
2660  * avoid possibly having to roll back work we've just done.
2661  *
2662  * This is safe without a recheck as long as there is no possibility of
2663  * another process scanning the page between this check and the delete
2664  * being visible to the scan (i.e., an exclusive buffer content lock is
2665  * continuously held from this point until the tuple delete is visible).
2666  */
2667  CheckForSerializableConflictIn(relation, tid, BufferGetBlockNumber(buffer));
2668 
2669  /* replace cid with a combo cid if necessary */
2670  HeapTupleHeaderAdjustCmax(tp.t_data, &cid, &iscombo);
2671 
2672  /*
2673  * Compute replica identity tuple before entering the critical section so
2674  * we don't PANIC upon a memory allocation failure.
2675  */
2676  old_key_tuple = ExtractReplicaIdentity(relation, &tp, true, &old_key_copied);
2677 
2678  /*
2679  * If this is the first possibly-multixact-able operation in the current
2680  * transaction, set my per-backend OldestMemberMXactId setting. We can be
2681  * certain that the transaction will never become a member of any older
2682  * MultiXactIds than that. (We have to do this even if we end up just
2683  * using our own TransactionId below, since some other backend could
2684  * incorporate our XID into a MultiXact immediately afterwards.)
2685  */
2687 
2690  xid, LockTupleExclusive, true,
2691  &new_xmax, &new_infomask, &new_infomask2);
2692 
2694 
2695  /*
2696  * If this transaction commits, the tuple will become DEAD sooner or
2697  * later. Set flag that this page is a candidate for pruning once our xid
2698  * falls below the OldestXmin horizon. If the transaction finally aborts,
2699  * the subsequent page pruning will be a no-op and the hint will be
2700  * cleared.
2701  */
2702  PageSetPrunable(page, xid);
2703 
2704  if (PageIsAllVisible(page))
2705  {
2706  all_visible_cleared = true;
2707  PageClearAllVisible(page);
2708  visibilitymap_clear(relation, BufferGetBlockNumber(buffer),
2709  vmbuffer, VISIBILITYMAP_VALID_BITS);
2710  }
2711 
2712  /* store transaction information of xact deleting the tuple */
2715  tp.t_data->t_infomask |= new_infomask;
2716  tp.t_data->t_infomask2 |= new_infomask2;
2718  HeapTupleHeaderSetXmax(tp.t_data, new_xmax);
2719  HeapTupleHeaderSetCmax(tp.t_data, cid, iscombo);
2720  /* Make sure there is no forward chain link in t_ctid */
2721  tp.t_data->t_ctid = tp.t_self;
2722 
2723  /* Signal that this is actually a move into another partition */
2724  if (changingPart)
2726 
2727  MarkBufferDirty(buffer);
2728 
2729  /*
2730  * XLOG stuff
2731  *
2732  * NB: heap_abort_speculative() uses the same xlog record and replay
2733  * routines.
2734  */
2735  if (RelationNeedsWAL(relation))
2736  {
2737  xl_heap_delete xlrec;
2738  xl_heap_header xlhdr;
2739  XLogRecPtr recptr;
2740 
2741  /* For logical decode we need combocids to properly decode the catalog */
2743  log_heap_new_cid(relation, &tp);
2744 
2745  xlrec.flags = 0;
2746  if (all_visible_cleared)
2748  if (changingPart)
2751  tp.t_data->t_infomask2);
2753  xlrec.xmax = new_xmax;
2754 
2755  if (old_key_tuple != NULL)
2756  {
2757  if (relation->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
2759  else
2761  }
2762 
2763  XLogBeginInsert();
2764  XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
2765 
2766  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
2767 
2768  /*
2769  * Log replica identity of the deleted tuple if there is one
2770  */
2771  if (old_key_tuple != NULL)
2772  {
2773  xlhdr.t_infomask2 = old_key_tuple->t_data->t_infomask2;
2774  xlhdr.t_infomask = old_key_tuple->t_data->t_infomask;
2775  xlhdr.t_hoff = old_key_tuple->t_data->t_hoff;
2776 
2777  XLogRegisterData((char *) &xlhdr, SizeOfHeapHeader);
2778  XLogRegisterData((char *) old_key_tuple->t_data
2780  old_key_tuple->t_len
2782  }
2783 
2784  /* filtering by origin on a row level is much more efficient */
2786 
2787  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
2788 
2789  PageSetLSN(page, recptr);
2790  }
2791 
2792  END_CRIT_SECTION();
2793 
2794  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2795 
2796  if (vmbuffer != InvalidBuffer)
2797  ReleaseBuffer(vmbuffer);
2798 
2799  /*
2800  * If the tuple has toasted out-of-line attributes, we need to delete
2801  * those items too. We have to do this before releasing the buffer
2802  * because we need to look at the contents of the tuple, but it's OK to
2803  * release the content lock on the buffer first.
2804  */
2805  if (relation->rd_rel->relkind != RELKIND_RELATION &&
2806  relation->rd_rel->relkind != RELKIND_MATVIEW)
2807  {
2808  /* toast table entries should never be recursively toasted */
2810  }
2811  else if (HeapTupleHasExternal(&tp))
2812  heap_toast_delete(relation, &tp, false);
2813 
2814  /*
2815  * Mark tuple for invalidation from system caches at next command
2816  * boundary. We have to do this before releasing the buffer because we
2817  * need to look at the contents of the tuple.
2818  */
2819  CacheInvalidateHeapTuple(relation, &tp, NULL);
2820 
2821  /* Now we can release the buffer */
2822  ReleaseBuffer(buffer);
2823 
2824  /*
2825  * Release the lmgr tuple lock, if we had it.
2826  */
2827  if (have_tuple_lock)
2828  UnlockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive);
2829 
2830  pgstat_count_heap_delete(relation);
2831 
2832  if (old_key_tuple != NULL && old_key_copied)
2833  heap_freetuple(old_key_tuple);
2834 
2835  return TM_Ok;
2836 }
#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:7519
#define HEAP_XMAX_BITS
Definition: htup_details.h:270
static uint8 compute_infobits(uint16 infomask, uint16 infomask2)
Definition: heapam.c:2397
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:521
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:869
void visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *buf)
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1471
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:7601
static bool xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
Definition: heapam.c:2419
#define HeapTupleHeaderClearHotUpdated(tup)
Definition: htup_details.h:496
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
CommandId cmax
Definition: tableam.h:127
bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
#define InvalidBuffer
Definition: buf.h:25
uint16 t_infomask2
Definition: heapam_xlog.h:143
#define PageSetPrunable(page, xid)
Definition: bufpage.h:392
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
int errcode(int sqlerrcode)
Definition: elog.c:610
#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:3513
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
Form_pg_class rd_rel
Definition: rel.h:109
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:162
OffsetNumber offnum
Definition: heapam_xlog.h:107
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:374
#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:3536
TransactionId xmax
Definition: tableam.h:126
#define ERROR
Definition: elog.h:43
#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:106
static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, ItemPointer ctid, XLTW_Oper oper, int *remaining)
Definition: heapam.c:6733
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:112
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define XLH_DELETE_CONTAINS_OLD_KEY
Definition: heapam_xlog.h:95
#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:4712
#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:619
#define InvalidCommandId
Definition: c.h:538
#define HEAP_XMAX_IS_LOCKED_ONLY(infomask)
Definition: htup_details.h:230
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3752
#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:4375
static void UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
Definition: heapam.c:1784
#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:4663
#define ereport(elevel,...)
Definition: elog.h:144
TransactionId MultiXactId
Definition: c.h:531
#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:746
uint8 infobits_set
Definition: heapam_xlog.h:108
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:607
uint16 t_infomask
Definition: heapam_xlog.h:144
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask, LockTupleMode lockmode, bool *current_is_member)
Definition: heapam.c:6556
#define RelationNeedsWAL(relation)
Definition: rel.h:562
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
void pgstat_count_heap_delete(Relation rel)
Definition: pgstat.c:2157
void HeapTupleHeaderAdjustCmax(HeapTupleHeader tup, CommandId *cmax, bool *iscombo)
Definition: combocid.c:153
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2663
#define HeapTupleHasExternal(tuple)
Definition: htup_details.h:673
int errmsg(const char *fmt,...)
Definition: elog.c:821
#define XLH_DELETE_ALL_VISIBLE_CLEARED
Definition: heapam_xlog.h:93
#define XLH_DELETE_IS_PARTITION_MOVE
Definition: heapam_xlog.h:97
#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:456
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
#define SizeOfHeapHeader
Definition: heapam_xlog.h:148
Pointer Page
Definition: bufpage.h:78
bool HeapTupleSatisfiesVisibility(HeapTuple tup, Snapshot snapshot, Buffer buffer)
#define XLH_DELETE_CONTAINS_OLD_TUPLE
Definition: heapam_xlog.h:94

◆ heap_endscan()

void heap_endscan ( TableScanDesc  scan)

Definition at line 1256 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().

1257 {
1258  HeapScanDesc scan = (HeapScanDesc) sscan;
1259 
1260  /* Note: no locking manipulations needed */
1261 
1262  /*
1263  * unpin scan buffers
1264  */
1265  if (BufferIsValid(scan->rs_cbuf))
1266  ReleaseBuffer(scan->rs_cbuf);
1267 
1268  /*
1269  * decrement relation reference count and free scan descriptor storage
1270  */
1272 
1273  if (scan->rs_base.rs_key)
1274  pfree(scan->rs_base.rs_key);
1275 
1276  if (scan->rs_strategy != NULL)
1278 
1279  if (scan->rs_base.rs_flags & SO_TEMP_SNAPSHOT)
1281 
1282  pfree(scan);
1283 }
TableScanDescData rs_base
Definition: heapam.h:49
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3513
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:2090
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 1394 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().

1398 {
1399  ItemPointer tid = &(tuple->t_self);
1400  ItemId lp;
1401  Buffer buffer;
1402  Page page;
1403  OffsetNumber offnum;
1404  bool valid;
1405 
1406  /*
1407  * Fetch and pin the appropriate page of the relation.
1408  */
1409  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
1410 
1411  /*
1412  * Need share lock on buffer to examine tuple commit status.
1413  */
1414  LockBuffer(buffer, BUFFER_LOCK_SHARE);
1415  page = BufferGetPage(buffer);
1416  TestForOldSnapshot(snapshot, relation, page);
1417 
1418  /*
1419  * We'd better check for out-of-range offnum in case of VACUUM since the
1420  * TID was obtained.
1421  */
1422  offnum = ItemPointerGetOffsetNumber(tid);
1423  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
1424  {
1425  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1426  ReleaseBuffer(buffer);
1427  *userbuf = InvalidBuffer;
1428  tuple->t_data = NULL;
1429  return false;
1430  }
1431 
1432  /*
1433  * get the item line pointer corresponding to the requested tid
1434  */
1435  lp = PageGetItemId(page, offnum);
1436 
1437  /*
1438  * Must check for deleted tuple.
1439  */
1440  if (!ItemIdIsNormal(lp))
1441  {
1442  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1443  ReleaseBuffer(buffer);
1444  *userbuf = InvalidBuffer;
1445  tuple->t_data = NULL;
1446  return false;
1447  }
1448 
1449  /*
1450  * fill in *tuple fields
1451  */
1452  tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
1453  tuple->t_len = ItemIdGetLength(lp);
1454  tuple->t_tableOid = RelationGetRelid(relation);
1455 
1456  /*
1457  * check tuple visibility, then release lock
1458  */
1459  valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
1460 
1461  if (valid)
1462  PredicateLockTID(relation, &(tuple->t_self), snapshot,
1463  HeapTupleHeaderGetXmin(tuple->t_data));
1464 
1465  HeapCheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
1466 
1467  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1468 
1469  if (valid)
1470  {
1471  /*
1472  * All checks passed, so return the tuple as valid. Caller is now
1473  * responsible for releasing the buffer.
1474  */
1475  *userbuf = buffer;
1476 
1477  return true;
1478  }
1479 
1480  /* Tuple failed time qual */
1481  ReleaseBuffer(buffer);
1482  *userbuf = InvalidBuffer;
1483 
1484  return false;
1485 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
static void TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page)
Definition: bufmgr.h:280
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define InvalidBuffer
Definition: buf.h:25
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3513
#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:3752
void PredicateLockTID(Relation relation, ItemPointer tid, Snapshot snapshot, TransactionId tuple_xid)
Definition: predicate.c:2543
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition: heapam.c:9019
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:607
#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:456
#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 5475 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().

5476 {
5477  Buffer buffer;
5478  Page page;
5479  OffsetNumber offnum;
5480  ItemId lp = NULL;
5481  HeapTupleHeader htup;
5482 
5483  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
5485  page = (Page) BufferGetPage(buffer);
5486 
5487  offnum = ItemPointerGetOffsetNumber(tid);
5488  if (PageGetMaxOffsetNumber(page) >= offnum)
5489  lp = PageGetItemId(page, offnum);
5490 
5491  if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
5492  elog(ERROR, "invalid lp");
5493 
5494  htup = (HeapTupleHeader) PageGetItem(page, lp);
5495 
5496  /* SpecTokenOffsetNumber should be distinguishable from any real offset */
5498  "invalid speculative token constant");
5499 
5500  /* NO EREPORT(ERROR) from here till changes are logged */
5502 
5504 
5505  MarkBufferDirty(buffer);
5506 
5507  /*
5508  * Replace the speculative insertion token with a real t_ctid, pointing to
5509  * itself like it does on regular tuples.
5510  */
5511  htup->t_ctid = *tid;
5512 
5513  /* XLOG stuff */
5514  if (RelationNeedsWAL(relation))
5515  {
5516  xl_heap_confirm xlrec;
5517  XLogRecPtr recptr;
5518 
5519  xlrec.offnum = ItemPointerGetOffsetNumber(tid);
5520 
5521  XLogBeginInsert();
5522 
5523  /* We want the same filtering on this as on a plain insert */
5525 
5526  XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
5527  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
5528 
5529  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
5530 
5531  PageSetLSN(page, recptr);
5532  }
5533 
5534  END_CRIT_SECTION();
5535 
5536  UnlockReleaseBuffer(buffer);
5537 }
OffsetNumber offnum
Definition: heapam_xlog.h:295
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1471
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:134
#define HeapTupleHeaderIsSpeculative(tup)
Definition: htup_details.h:429
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
#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:860
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3536
#define ERROR
Definition: elog.h:43
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:298
#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:3752
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:746
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:607
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define RelationNeedsWAL(relation)
Definition: rel.h:562
#define elog(elevel,...)
Definition: elog.h:214
#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 6376 of file heapam.c.

References heap_execute_freeze_tuple(), and heap_prepare_freeze_tuple().

Referenced by rewrite_heap_tuple().

6379 {
6381  bool do_freeze;
6382  bool tuple_totally_frozen;
6383 
6384  do_freeze = heap_prepare_freeze_tuple(tuple,
6385  relfrozenxid, relminmxid,
6386  cutoff_xid, cutoff_multi,
6387  &frz, &tuple_totally_frozen);
6388 
6389  /*
6390  * Note that because this is not a WAL-logged operation, we don't need to
6391  * fill in the offset in the freeze record.
6392  */
6393 
6394  if (do_freeze)
6395  heap_execute_freeze_tuple(tuple, &frz);
6396  return do_freeze;
6397 }
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:6126
void heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
Definition: heapam.c:6355

◆ heap_get_latest_tid()

void heap_get_latest_tid ( TableScanDesc  scan,
ItemPointer  tid 
)

Definition at line 1661 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().

1663 {
1664  Relation relation = sscan->rs_rd;
1665  Snapshot snapshot = sscan->rs_snapshot;
1666  ItemPointerData ctid;
1667  TransactionId priorXmax;
1668 
1669  /*
1670  * table_tuple_get_latest_tid() verified that the passed in tid is valid.
1671  * Assume that t_ctid links are valid however - there shouldn't be invalid
1672  * ones in the table.
1673  */
1674  Assert(ItemPointerIsValid(tid));
1675 
1676  /*
1677  * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
1678  * need to examine, and *tid is the TID we will return if ctid turns out
1679  * to be bogus.
1680  *
1681  * Note that we will loop until we reach the end of the t_ctid chain.
1682  * Depending on the snapshot passed, there might be at most one visible
1683  * version of the row, but we don't try to optimize for that.
1684  */
1685  ctid = *tid;
1686  priorXmax = InvalidTransactionId; /* cannot check first XMIN */
1687  for (;;)
1688  {
1689  Buffer buffer;
1690  Page page;
1691  OffsetNumber offnum;
1692  ItemId lp;
1693  HeapTupleData tp;
1694  bool valid;
1695 
1696  /*
1697  * Read, pin, and lock the page.
1698  */
1699  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
1700  LockBuffer(buffer, BUFFER_LOCK_SHARE);
1701  page = BufferGetPage(buffer);
1702  TestForOldSnapshot(snapshot, relation, page);
1703 
1704  /*
1705  * Check for bogus item number. This is not treated as an error
1706  * condition because it can happen while following a t_ctid link. We
1707  * just assume that the prior tid is OK and return it unchanged.
1708  */
1709  offnum = ItemPointerGetOffsetNumber(&ctid);
1710  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
1711  {
1712  UnlockReleaseBuffer(buffer);
1713  break;
1714  }
1715  lp = PageGetItemId(page, offnum);
1716  if (!ItemIdIsNormal(lp))
1717  {
1718  UnlockReleaseBuffer(buffer);
1719  break;
1720  }
1721 
1722  /* OK to access the tuple */
1723  tp.t_self = ctid;
1724  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
1725  tp.t_len = ItemIdGetLength(lp);
1726  tp.t_tableOid = RelationGetRelid(relation);
1727 
1728  /*
1729  * After following a t_ctid link, we might arrive at an unrelated
1730  * tuple. Check for XMIN match.
1731  */
1732  if (TransactionIdIsValid(priorXmax) &&
1734  {
1735  UnlockReleaseBuffer(buffer);
1736  break;
1737  }
1738 
1739  /*
1740  * Check tuple visibility; if visible, set it as the new result
1741  * candidate.
1742  */
1743  valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
1744  HeapCheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
1745  if (valid)
1746  *tid = ctid;
1747 
1748  /*
1749  * If there's a valid t_ctid link, follow it, else we're done.
1750  */
1751  if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
1755  {
1756  UnlockReleaseBuffer(buffer);
1757  break;
1758  }
1759 
1760  ctid = tp.t_data->t_ctid;
1761  priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
1762  UnlockReleaseBuffer(buffer);
1763  } /* end of loop */
1764 }
#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:280
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
uint32 TransactionId
Definition: c.h:521
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:3536
#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:3752
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition: heapam.c:9019
#define Assert(condition)
Definition: c.h:746
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:607
#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:456
#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:521
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:950
#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:746
#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 1286 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().

1287 {
1288  HeapScanDesc scan = (HeapScanDesc) sscan;
1289 
1290  /*
1291  * This is still widely used directly, without going through table AM, so
1292  * add a safety check. It's possible we should, at a later point,
1293  * downgrade this to an assert. The reason for checking the AM routine,
1294  * rather than the AM oid, is that this allows to write regression tests
1295  * that create another AM reusing the heap handler.
1296  */
1297  if (unlikely(sscan->rs_rd->rd_tableam != GetHeapamTableAmRoutine()))
1298  ereport(ERROR,
1299  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1300  errmsg_internal("only heap AM is supported")));
1301 
1302  /*
1303  * We don't expect direct calls to heap_getnext with valid CheckXidAlive
1304  * for catalog or regular tables. See detailed comments in xact.c where
1305  * these variables are declared. Normally we have such a check at tableam
1306  * level API but this is called from many places so we need to ensure it
1307  * here.
1308  */
1310  elog(ERROR, "unexpected heap_getnext call during logical decoding");
1311 
1312  /* Note: no locking manipulations needed */
1313 
1314  if (scan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
1315  heapgettup_pagemode(scan, direction,
1316  scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
1317  else
1318  heapgettup(scan, direction,
1319  scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
1320 
1321  if (scan->rs_ctup.t_data == NULL)
1322  return NULL;
1323 
1324  /*
1325  * if we get here it means we have a new current scan tuple, so point to
1326  * the proper return buffer and return the tuple.
1327  */
1328 
1330 
1331  return &scan->rs_ctup;
1332 }
TableScanDescData rs_base
Definition: heapam.h:49
int errcode(int sqlerrcode)
Definition: elog.c:610
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:43
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:488
#define ereport(elevel,...)
Definition: elog.h:144
int errmsg_internal(const char *fmt,...)
Definition: elog.c:908
Relation rs_rd
Definition: relscan.h:34
#define elog(elevel,...)
Definition: elog.h:214
#define unlikely(x)
Definition: c.h:207
#define pgstat_count_heap_getnext(rel)
Definition: pgstat.h:1487
#define TransactionIdIsValid(xid)
Definition: transam.h:41
static void heapgettup_pagemode(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:806
const TableAmRoutine * GetHeapamTableAmRoutine(void)

◆ heap_getnextslot()

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

Definition at line 1335 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().

1336 {
1337  HeapScanDesc scan = (HeapScanDesc) sscan;
1338 
1339  /* Note: no locking manipulations needed */
1340 
1341  if (sscan->rs_flags & SO_ALLOW_PAGEMODE)
1342  heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1343  else
1344  heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1345 
1346  if (scan->rs_ctup.t_data == NULL)
1347  {
1348  ExecClearTuple(slot);
1349  return false;
1350  }
1351 
1352  /*
1353  * if we get here it means we have a new current scan tuple, so point to
1354  * the proper return buffer and return the tuple.
1355  */
1356 
1358 
1359  ExecStoreBufferHeapTuple(&scan->rs_ctup, slot,
1360  scan->rs_cbuf);
1361  return true;
1362 }
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:488
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:1487
static void heapgettup_pagemode(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:806

◆ 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 1509 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 heapam_index_fetch_tuple(), and heapam_scan_bitmap_next_block().

1512 {
1513  Page dp = (Page) BufferGetPage(buffer);
1514  TransactionId prev_xmax = InvalidTransactionId;
1515  BlockNumber blkno;
1516  OffsetNumber offnum;
1517  bool at_chain_start;
1518  bool valid;
1519  bool skip;
1520  GlobalVisState *vistest = NULL;
1521 
1522  /* If this is not the first call, previous call returned a (live!) tuple */
1523  if (all_dead)
1524  *all_dead = first_call;
1525 
1526  blkno = ItemPointerGetBlockNumber(tid);
1527  offnum = ItemPointerGetOffsetNumber(tid);
1528  at_chain_start = first_call;
1529  skip = !first_call;
1530 
1531  /* XXX: we should assert that a snapshot is pushed or registered */
1533  Assert(BufferGetBlockNumber(buffer) == blkno);
1534 
1535  /* Scan through possible multiple members of HOT-chain */
1536  for (;;)
1537  {
1538  ItemId lp;
1539 
1540  /* check for bogus TID */
1541  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
1542  break;
1543 
1544  lp = PageGetItemId(dp, offnum);
1545 
1546  /* check for unused, dead, or redirected items */
1547  if (!ItemIdIsNormal(lp))
1548  {
1549  /* We should only see a redirect at start of chain */
1550  if (ItemIdIsRedirected(lp) && at_chain_start)
1551  {
1552  /* Follow the redirect */
1553  offnum = ItemIdGetRedirect(lp);
1554  at_chain_start = false;
1555  continue;
1556  }
1557  /* else must be end of chain */
1558  break;
1559  }
1560 
1561  /*
1562  * Update heapTuple to point to the element of the HOT chain we're
1563  * currently investigating. Having t_self set correctly is important
1564  * because the SSI checks and the *Satisfies routine for historical
1565  * MVCC snapshots need the correct tid to decide about the visibility.
1566  */
1567  heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
1568  heapTuple->t_len = ItemIdGetLength(lp);
1569  heapTuple->t_tableOid = RelationGetRelid(relation);
1570  ItemPointerSet(&heapTuple->t_self, blkno, offnum);
1571 
1572  /*
1573  * Shouldn't see a HEAP_ONLY tuple at chain start.
1574  */
1575  if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
1576  break;
1577 
1578  /*
1579  * The xmin should match the previous xmax value, else chain is
1580  * broken.
1581  */
1582  if (TransactionIdIsValid(prev_xmax) &&
1583  !TransactionIdEquals(prev_xmax,
1584  HeapTupleHeaderGetXmin(heapTuple->t_data)))
1585  break;
1586 
1587  /*
1588  * When first_call is true (and thus, skip is initially false) we'll
1589  * return the first tuple we find. But on later passes, heapTuple
1590  * will initially be pointing to the tuple we returned last time.
1591  * Returning it again would be incorrect (and would loop forever), so
1592  * we skip it and return the next match we find.
1593  */
1594  if (!skip)
1595  {
1596  /* If it's visible per the snapshot, we must return it */
1597  valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
1598  HeapCheckForSerializableConflictOut(valid, relation, heapTuple,
1599  buffer, snapshot);
1600 
1601  if (valid)
1602  {
1603  ItemPointerSetOffsetNumber(tid, offnum);
1604  PredicateLockTID(relation, &heapTuple->t_self, snapshot,
1605  HeapTupleHeaderGetXmin(heapTuple->t_data));
1606  if (all_dead)
1607  *all_dead = false;
1608  return true;
1609  }
1610  }
1611  skip = false;
1612 
1613  /*
1614  * If we can't see it, maybe no one else can either. At caller
1615  * request, check whether all chain members are dead to all
1616  * transactions.
1617  *
1618  * Note: if you change the criterion here for what is "dead", fix the
1619  * planner's get_actual_variable_range() function to match.
1620  */
1621  if (all_dead && *all_dead)
1622  {
1623  if (!vistest)
1624  vistest = GlobalVisTestFor(relation);
1625 
1626  if (!HeapTupleIsSurelyDead(heapTuple, vistest))
1627  *all_dead = false;
1628  }
1629 
1630  /*
1631  * Check to see if HOT chain continues past this tuple; if so fetch
1632  * the next offnum and loop around.
1633  */
1634  if (HeapTupleIsHotUpdated(heapTuple))
1635  {
1637  blkno);
1638  offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
1639  at_chain_start = false;
1640  prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
1641  }
1642  else
1643  break; /* end of chain */
1644  }
1645 
1646  return false;
1647 }
#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:521
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:3918
#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:2543
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition: heapam.c:9019
#define HeapTupleIsHeapOnly(tuple)
Definition: htup_details.h:685
#define Assert(condition)
Definition: c.h:746
#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:2663
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:456
#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_inplace_update()

void heap_inplace_update ( Relation  relation,
HeapTuple  tuple 
)

Definition at line 5715 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().

5716 {
5717  Buffer buffer;
5718  Page page;
5719  OffsetNumber offnum;
5720  ItemId lp = NULL;
5721  HeapTupleHeader htup;
5722  uint32 oldlen;
5723  uint32 newlen;
5724 
5725  /*
5726  * For now, we don't allow parallel updates. Unlike a regular update,
5727  * this should never create a combo CID, so it might be possible to relax
5728  * this restriction, but not without more thought and testing. It's not
5729  * clear that it would be useful, anyway.
5730  */
5731  if (IsInParallelMode())
5732  ereport(ERROR,
5733  (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
5734  errmsg("cannot update tuples during a parallel operation")));
5735 
5736  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
5738  page = (Page) BufferGetPage(buffer);
5739 
5740  offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
5741  if (PageGetMaxOffsetNumber(page) >= offnum)
5742  lp = PageGetItemId(page, offnum);
5743 
5744  if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
5745  elog(ERROR, "invalid lp");
5746 
5747  htup = (HeapTupleHeader) PageGetItem(page, lp);
5748 
5749  oldlen = ItemIdGetLength(lp) - htup->t_hoff;
5750  newlen = tuple->t_len - tuple->t_data->t_hoff;
5751  if (oldlen != newlen || htup->t_hoff != tuple->t_data->t_hoff)
5752  elog(ERROR, "wrong tuple length");
5753 
5754  /* NO EREPORT(ERROR) from here till changes are logged */
5756 
5757  memcpy((char *) htup + htup->t_hoff,
5758  (char *) tuple->t_data + tuple->t_data->t_hoff,
5759  newlen);
5760 
5761  MarkBufferDirty(buffer);
5762 
5763  /* XLOG stuff */
5764  if (RelationNeedsWAL(relation))
5765  {
5766  xl_heap_inplace xlrec;
5767  XLogRecPtr recptr;
5768 
5769  xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
5770 
5771  XLogBeginInsert();
5772  XLogRegisterData((char *) &xlrec, SizeOfHeapInplace);
5773 
5774  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
5775  XLogRegisterBufData(0, (char *) htup + htup->t_hoff, newlen);
5776 
5777  /* inplace updates aren't decoded atm, don't log the origin */
5778 
5779  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_INPLACE);
5780 
5781  PageSetLSN(page, recptr);
5782  }
5783 
5784  END_CRIT_SECTION();
5785 
5786  UnlockReleaseBuffer(buffer);
5787 
5788  /*
5789  * Send out shared cache inval if necessary. Note that because we only
5790  * pass the new version of the tuple, this mustn't be used for any
5791  * operations that could change catcache lookup keys. But we aren't
5792  * bothering with index updates either, so that's true a fortiori.
5793  */
5795  CacheInvalidateHeapTuple(relation, tuple, NULL);
5796 }
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:1471
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:134
#define SizeOfHeapInplace
Definition: heapam_xlog.h:307
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
int errcode(int sqlerrcode)
Definition: elog.c:610
#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:3536
#define ERROR
Definition: elog.h:43
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:375
#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:303
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3752
#define ereport(elevel,...)
Definition: elog.h:144
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:607
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define XLOG_HEAP_INPLACE
Definition: heapam_xlog.h:39
#define RelationNeedsWAL(relation)
Definition: rel.h:562
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:393
int errmsg(const char *fmt,...)
Definition: elog.c:821
#define elog(elevel,...)
Definition: elog.h:214
#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 1859 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().

1861 {
1863  HeapTuple heaptup;
1864  Buffer buffer;
1865  Buffer vmbuffer = InvalidBuffer;
1866  bool all_visible_cleared = false;
1867 
1868  /*
1869  * Fill in tuple header fields and toast the tuple if necessary.
1870  *
1871  * Note: below this point, heaptup is the data we actually intend to store
1872  * into the relation; tup is the caller's original untoasted data.
1873  */
1874  heaptup = heap_prepare_insert(relation, tup, xid, cid, options);
1875 
1876  /*
1877  * Find buffer to insert this tuple into. If the page is all visible,
1878  * this will also pin the requisite visibility map page.
1879  */
1880  buffer = RelationGetBufferForTuple(relation, heaptup->t_len,
1881  InvalidBuffer, options, bistate,
1882  &vmbuffer, NULL);
1883 
1884  /*
1885  * We're about to do the actual insert -- but check for conflict first, to
1886  * avoid possibly having to roll back work we've just done.
1887  *
1888  * This is safe without a recheck as long as there is no possibility of
1889  * another process scanning the page between this check and the insert
1890  * being visible to the scan (i.e., an exclusive buffer content lock is
1891  * continuously held from this point until the tuple insert is visible).
1892  *
1893  * For a heap insert, we only need to check for table-level SSI locks. Our
1894  * new tuple can't possibly conflict with existing tuple locks, and heap
1895  * page locks are only consolidated versions of tuple locks; they do not
1896  * lock "gaps" as index page locks do. So we don't need to specify a
1897  * buffer when making the call, which makes for a faster check.
1898  */
1900 
1901  /* NO EREPORT(ERROR) from here till changes are logged */
1903 
1904  RelationPutHeapTuple(relation, buffer, heaptup,
1905  (options & HEAP_INSERT_SPECULATIVE) != 0);
1906 
1907  if (PageIsAllVisible(BufferGetPage(buffer)))
1908  {
1909  all_visible_cleared = true;
1911  visibilitymap_clear(relation,
1912  ItemPointerGetBlockNumber(&(heaptup->t_self)),
1913  vmbuffer, VISIBILITYMAP_VALID_BITS);
1914  }
1915 
1916  /*
1917  * XXX Should we set PageSetPrunable on this page ?
1918  *
1919  * The inserting transaction may eventually abort thus making this tuple
1920  * DEAD and hence available for pruning. Though we don't want to optimize
1921  * for aborts, if no other tuple in this page is UPDATEd/DELETEd, the
1922  * aborted tuple will never be pruned until next vacuum is triggered.
1923  *
1924  * If you do add PageSetPrunable here, add it in heap_xlog_insert too.
1925  */
1926 
1927  MarkBufferDirty(buffer);
1928 
1929  /* XLOG stuff */
1930  if (RelationNeedsWAL(relation))
1931  {
1932  xl_heap_insert xlrec;
1933  xl_heap_header xlhdr;
1934  XLogRecPtr recptr;
1935  Page page = BufferGetPage(buffer);
1936  uint8 info = XLOG_HEAP_INSERT;
1937  int bufflags = 0;
1938 
1939  /*
1940  * If this is a catalog, we need to transmit combocids to properly
1941  * decode, so log that as well.
1942  */
1944  log_heap_new_cid(relation, heaptup);
1945 
1946  /*
1947  * If this is the single and first tuple on page, we can reinit the
1948  * page instead of restoring the whole thing. Set flag, and hide
1949  * buffer references from XLogInsert.
1950  */
1951  if (ItemPointerGetOffsetNumber(&(heaptup->t_self)) == FirstOffsetNumber &&
1953  {
1954  info |= XLOG_HEAP_INIT_PAGE;
1955  bufflags |= REGBUF_WILL_INIT;
1956  }
1957 
1958  xlrec.offnum = ItemPointerGetOffsetNumber(&heaptup->t_self);
1959  xlrec.flags = 0;
1960  if (all_visible_cleared)
1965 
1966  /*
1967  * For logical decoding, we need the tuple even if we're doing a full
1968  * page write, so make sure it's included even if we take a full-page
1969  * image. (XXX We could alternatively store a pointer into the FPW).
1970  */
1971  if (RelationIsLogicallyLogged(relation) &&
1973  {
1975  bufflags |= REGBUF_KEEP_DATA;
1976 
1977  if (IsToastRelation(relation))
1979  }
1980 
1981  XLogBeginInsert();
1982  XLogRegisterData((char *) &xlrec, SizeOfHeapInsert);
1983 
1984  xlhdr.t_infomask2 = heaptup->t_data->t_infomask2;
1985  xlhdr.t_infomask = heaptup->t_data->t_infomask;
1986  xlhdr.t_hoff = heaptup->t_data->t_hoff;
1987 
1988  /*
1989  * note we mark xlhdr as belonging to buffer; if XLogInsert decides to
1990  * write the whole page to the xlog, we don't need to store
1991  * xl_heap_header in the xlog.
1992  */
1993  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
1994  XLogRegisterBufData(0, (char *) &xlhdr, SizeOfHeapHeader);
1995  /* PG73FORMAT: write bitmap [+ padding] [+ oid] + data */
1997  (char *) heaptup->t_data + SizeofHeapTupleHeader,
1998  heaptup->t_len - SizeofHeapTupleHeader);
1999 
2000  /* filtering by origin on a row level is much more efficient */
2002 
2003  recptr = XLogInsert(RM_HEAP_ID, info);
2004 
2005  PageSetLSN(page, recptr);
2006  }
2007 
2008  END_CRIT_SECTION();
2009 
2010  UnlockReleaseBuffer(buffer);
2011  if (vmbuffer != InvalidBuffer)
2012  ReleaseBuffer(vmbuffer);
2013 
2014  /*
2015  * If tuple is cachable, mark it for invalidation from the caches in case
2016  * we abort. Note it is OK to do this after releasing the buffer, because
2017  * the heaptup data structure is all in local memory, not in the shared
2018  * buffer.
2019  */
2020  CacheInvalidateHeapTuple(relation, heaptup, NULL);
2021 
2022  /* Note: speculative insertions are counted too, even if aborted later */
2023  pgstat_count_heap_insert(relation, 1);
2024 
2025  /*
2026  * If heaptup is a private copy, release it. Don't forget to copy t_self
2027  * back to the caller's image, too.
2028  */
2029  if (heaptup != tup)
2030  {
2031  tup->t_self = heaptup->t_self;
2032  heap_freetuple(heaptup);
2033  }
2034 }
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:140
#define XLOG_HEAP_INSERT
Definition: heapam_xlog.h:32
static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup)
Definition: heapam.c:7519
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:2043
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:521
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1471
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
unsigned char uint8
Definition: c.h:373
#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:143
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
#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:3513
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:635
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:3536
#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:619
#define REGBUF_KEEP_DATA
Definition: xloginsert.h:38
void CheckForSerializableConflictIn(Relation relation, ItemPointer tid, BlockNumber blkno)
Definition: predicate.c:4375
#define PageClearAllVisible(page)
Definition: bufpage.h:389
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:746
uint16 t_infomask
Definition: heapam_xlog.h:144
#define InvalidBlockNumber
Definition: block.h:33
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define RelationNeedsWAL(relation)
Definition: rel.h:562
#define SizeOfHeapInsert
Definition: heapam_xlog.h:159
#define XLH_INSERT_ALL_VISIBLE_CLEARED
Definition: heapam_xlog.h:66
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2663
void pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
Definition: pgstat.c:2111
#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:153
#define SizeOfHeapHeader
Definition: heapam_xlog.h:148
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 3973 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(), ERRCODE_LOCK_NOT_AVAILABLE, 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().

3977 {
3978  TM_Result result;
3979  ItemPointer tid = &(tuple->t_self);
3980  ItemId lp;
3981  Page page;
3982  Buffer vmbuffer = InvalidBuffer;
3983  BlockNumber block;
3984  TransactionId xid,
3985  xmax;
3986  uint16 old_infomask,
3987  new_infomask,
3988  new_infomask2;
3989  bool first_time = true;
3990  bool skip_tuple_lock = false;
3991  bool have_tuple_lock = false;
3992  bool cleared_all_frozen = false;
3993 
3994  *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
3995  block = ItemPointerGetBlockNumber(tid);
3996 
3997  /*
3998  * Before locking the buffer, pin the visibility map page if it appears to
3999  * be necessary. Since we haven't got the lock yet, someone else might be
4000  * in the middle of changing this, so we'll need to recheck after we have
4001  * the lock.
4002  */
4003  if (PageIsAllVisible(BufferGetPage(*buffer)))
4004  visibilitymap_pin(relation, block, &vmbuffer);
4005 
4007 
4008  page = BufferGetPage(*buffer);
4009  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
4010  Assert(ItemIdIsNormal(lp));
4011 
4012  tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
4013  tuple->t_len = ItemIdGetLength(lp);
4014  tuple->t_tableOid = RelationGetRelid(relation);
4015 
4016 l3:
4017  result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
4018 
4019  if (result == TM_Invisible)
4020  {
4021  /*
4022  * This is possible, but only when locking a tuple for ON CONFLICT
4023  * UPDATE. We return this value here rather than throwing an error in
4024  * order to give that case the opportunity to throw a more specific
4025  * error.
4026  */
4027  result = TM_Invisible;
4028  goto out_locked;
4029  }
4030  else if (result == TM_BeingModified ||
4031  result == TM_Updated ||
4032  result == TM_Deleted)
4033  {
4034  TransactionId xwait;
4035  uint16 infomask;
4036  uint16 infomask2;
4037  bool require_sleep;
4038  ItemPointerData t_ctid;
4039 
4040  /* must copy state data before unlocking buffer */
4041  xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
4042  infomask = tuple->t_data->t_infomask;
4043  infomask2 = tuple->t_data->t_infomask2;
4044  ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
4045 
4046  LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
4047 
4048  /*
4049  * If any subtransaction of the current top transaction already holds
4050  * a lock as strong as or stronger than what we're requesting, we
4051  * effectively hold the desired lock already. We *must* succeed
4052  * without trying to take the tuple lock, else we will deadlock
4053  * against anyone wanting to acquire a stronger lock.
4054  *
4055  * Note we only do this the first time we loop on the HTSU result;
4056  * there is no point in testing in subsequent passes, because
4057  * evidently our own transaction cannot have acquired a new lock after
4058  * the first time we checked.
4059  */
4060  if (first_time)
4061  {
4062  first_time = false;
4063 
4064  if (infomask & HEAP_XMAX_IS_MULTI)
4065  {
4066  int i;
4067  int nmembers;
4068  MultiXactMember *members;
4069 
4070  /*
4071  * We don't need to allow old multixacts here; if that had
4072  * been the case, HeapTupleSatisfiesUpdate would have returned
4073  * MayBeUpdated and we wouldn't be here.
4074  */
4075  nmembers =
4076  GetMultiXactIdMembers(xwait, &members, false,
4077  HEAP_XMAX_IS_LOCKED_ONLY(infomask));
4078 
4079  for (i = 0; i < nmembers; i++)
4080  {
4081  /* only consider members of our own transaction */
4082  if (!TransactionIdIsCurrentTransactionId(members[i].xid))
4083  continue;
4084 
4085  if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
4086  {
4087  pfree(members);
4088  result = TM_Ok;
4089  goto out_unlocked;
4090  }
4091  else
4092  {
4093  /*
4094  * Disable acquisition of the heavyweight tuple lock.
4095  * Otherwise, when promoting a weaker lock, we might
4096  * deadlock with another locker that has acquired the
4097  * heavyweight tuple lock and is waiting for our
4098  * transaction to finish.
4099  *
4100  * Note that in this case we still need to wait for
4101  * the multixact if required, to avoid acquiring
4102  * conflicting locks.
4103  */
4104  skip_tuple_lock = true;
4105  }
4106  }
4107 
4108  if (members)
4109  pfree(members);
4110  }
4111  else if (TransactionIdIsCurrentTransactionId(xwait))
4112  {
4113  switch (mode)
4114  {
4115  case LockTupleKeyShare:
4116  Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
4117  HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
4118  HEAP_XMAX_IS_EXCL_LOCKED(infomask));
4119  result = TM_Ok;
4120  goto out_unlocked;
4121  case LockTupleShare:
4122  if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
4123  HEAP_XMAX_IS_EXCL_LOCKED(infomask))
4124  {
4125  result = TM_Ok;
4126  goto out_unlocked;
4127  }
4128  break;
4130  if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
4131  {
4132  result = TM_Ok;
4133  goto out_unlocked;
4134  }
4135  break;
4136  case LockTupleExclusive:
4137  if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
4138  infomask2 & HEAP_KEYS_UPDATED)
4139  {
4140  result = TM_Ok;
4141  goto out_unlocked;
4142  }
4143  break;
4144  }
4145  }
4146  }
4147 
4148  /*
4149  * Initially assume that we will have to wait for the locking
4150  * transaction(s) to finish. We check various cases below in which
4151  * this can be turned off.
4152  */
4153  require_sleep = true;
4154  if (mode == LockTupleKeyShare)
4155  {
4156  /*
4157  * If we're requesting KeyShare, and there's no update present, we
4158  * don't need to wait. Even if there is an update, we can still
4159  * continue if the key hasn't been modified.
4160  *
4161  * However, if there are updates, we need to walk the update chain
4162  * to mark future versions of the row as locked, too. That way,
4163  * if somebody deletes that future version, we're protected
4164  * against the key going away. This locking of future versions
4165  * could block momentarily, if a concurrent transaction is
4166  * deleting a key; or it could return a value to the effect that
4167  * the transaction deleting the key has already committed. So we
4168  * do this before re-locking the buffer; otherwise this would be
4169  * prone to deadlocks.
4170  *
4171  * Note that the TID we're locking was grabbed before we unlocked
4172  * the buffer. For it to change while we're not looking, the
4173  * other properties we're testing for below after re-locking the
4174  * buffer would also change, in which case we would restart this
4175  * loop above.
4176  */
4177  if (!(infomask2 & HEAP_KEYS_UPDATED))
4178  {
4179  bool updated;
4180 
4181  updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
4182 
4183  /*
4184  * If there are updates, follow the update chain; bail out if
4185  * that cannot be done.
4186  */
4187  if (follow_updates && updated)
4188  {
4189  TM_Result res;
4190 
4191  res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
4193  mode);
4194  if (res != TM_Ok)
4195  {
4196  result = res;
4197  /* recovery code expects to have buffer lock held */
4199  goto failed;
4200  }
4201  }
4202 
4204 
4205  /*
4206  * Make sure it's still an appropriate lock, else start over.
4207  * Also, if it wasn't updated before we released the lock, but
4208  * is updated now, we start over too; the reason is that we
4209  * now need to follow the update chain to lock the new
4210  * versions.
4211  */
4212  if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
4213  ((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
4214  !updated))
4215  goto l3;
4216 
4217  /* Things look okay, so we can skip sleeping */
4218  require_sleep = false;
4219 
4220  /*
4221  * Note we allow Xmax to change here; other updaters/lockers
4222  * could have modified it before we grabbed the buffer lock.
4223  * However, this is not a problem, because with the recheck we
4224  * just did we ensure that they still don't conflict with the
4225  * lock we want.
4226  */
4227  }
4228  }
4229  else if (mode == LockTupleShare)
4230  {
4231  /*
4232  * If we're requesting Share, we can similarly avoid sleeping if
4233  * there's no update and no exclusive lock present.
4234  */
4235  if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
4236  !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
4237  {
4239 
4240  /*
4241  * Make sure it's still an appropriate lock, else start over.
4242  * See above about allowing xmax to change.
4243  */
4244  if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
4246  goto l3;
4247  require_sleep = false;
4248  }
4249  }
4250  else if (mode == LockTupleNoKeyExclusive)
4251  {
4252  /*
4253  * If we're requesting NoKeyExclusive, we might also be able to
4254  * avoid sleeping; just ensure that there no conflicting lock
4255  * already acquired.
4256  */
4257  if (infomask & HEAP_XMAX_IS_MULTI)
4258  {
4259  if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
4260  mode, NULL))
4261  {
4262  /*
4263  * No conflict, but if the xmax changed under us in the
4264  * meantime, start over.
4265  */
4267  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4269  xwait))
4270  goto l3;
4271 
4272  /* otherwise, we're good */
4273  require_sleep = false;
4274  }
4275  }
4276  else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
4277  {
4279 
4280  /* if the xmax changed in the meantime, start over */
4281  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4283  xwait))
4284  goto l3;
4285  /* otherwise, we're good */
4286  require_sleep = false;
4287  }
4288  }
4289 
4290  /*
4291  * As a check independent from those above, we can also avoid sleeping
4292  * if the current transaction is the sole locker of the tuple. Note
4293  * that the strength of the lock already held is irrelevant; this is
4294  * not about recording the lock in Xmax (which will be done regardless
4295  * of this optimization, below). Also, note that the cases where we
4296  * hold a lock stronger than we are requesting are already handled
4297  * above by not doing anything.
4298  *
4299  * Note we only deal with the non-multixact case here; MultiXactIdWait
4300  * is well equipped to deal with this situation on its own.
4301  */
4302  if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
4304  {
4305  /* ... but if the xmax changed in the meantime, start over */
4307  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4309  xwait))
4310  goto l3;
4312  require_sleep = false;
4313  }
4314 
4315  /*
4316  * Time to sleep on the other transaction/multixact, if necessary.
4317  *
4318  * If the other transaction is an update/delete that's already
4319  * committed, then sleeping cannot possibly do any good: if we're
4320  * required to sleep, get out to raise an error instead.
4321  *
4322  * By here, we either have already acquired the buffer exclusive lock,
4323  * or we must wait for the locking transaction or multixact; so below
4324  * we ensure that we grab buffer lock after the sleep.
4325  */
4326  if (require_sleep && (result == TM_Updated || result == TM_Deleted))
4327  {
4329  goto failed;
4330  }
4331  else if (require_sleep)
4332  {
4333  /*
4334  * Acquire tuple lock to establish our priority for the tuple, or
4335  * die trying. LockTuple will release us when we are next-in-line
4336  * for the tuple. We must do this even if we are share-locking,
4337  * but not if we already have a weaker lock on the tuple.
4338  *
4339  * If we are forced to "start over" below, we keep the tuple lock;
4340  * this arranges that we stay at the head of the line while
4341  * rechecking tuple state.
4342  */
4343  if (!skip_tuple_lock &&
4344  !heap_acquire_tuplock(relation, tid, mode, wait_policy,
4345  &have_tuple_lock))
4346  {
4347  /*
4348  * This can only happen if wait_policy is Skip and the lock
4349  * couldn't be obtained.
4350  */
4351  result = TM_WouldBlock;
4352  /* recovery code expects to have buffer lock held */
4354  goto failed;
4355  }
4356 
4357  if (infomask & HEAP_XMAX_IS_MULTI)
4358  {
4360 
4361  /* We only ever lock tuples, never update them */
4362  if (status >= MultiXactStatusNoKeyUpdate)
4363  elog(ERROR, "invalid lock mode in heap_lock_tuple");
4364 
4365  /* wait for multixact to end, or die trying */
4366  switch (wait_policy)
4367  {
4368  case LockWaitBlock:
4369  MultiXactIdWait((MultiXactId) xwait, status, infomask,
4370  relation, &tuple->t_self, XLTW_Lock, NULL);
4371  break;
4372  case LockWaitSkip:
4374  status, infomask, relation,
4375  NULL))
4376  {
4377  result = TM_WouldBlock;
4378  /* recovery code expects to have buffer lock held */
4380  goto failed;
4381  }
4382  break;
4383  case LockWaitError:
4385  status, infomask, relation,
4386  NULL))
4387  ereport(ERROR,
4389  errmsg("could not obtain lock on row in relation \"%s\"",
4390  RelationGetRelationName(relation))));
4391 
4392  break;
4393  }
4394 
4395  /*
4396  * Of course, the multixact might not be done here: if we're
4397  * requesting a light lock mode, other transactions with light
4398  * locks could still be alive, as well as locks owned by our
4399  * own xact or other subxacts of this backend. We need to
4400  * preserve the surviving MultiXact members. Note that it
4401  * isn't absolutely necessary in the latter case, but doing so
4402  * is simpler.
4403  */
4404  }
4405  else
4406  {
4407  /* wait for regular transaction to end, or die trying */
4408  switch (wait_policy)
4409  {
4410  case LockWaitBlock:
4411  XactLockTableWait(xwait, relation, &tuple->t_self,
4412  XLTW_Lock);
4413  break;
4414  case LockWaitSkip:
4415  if (!ConditionalXactLockTableWait(xwait))
4416  {
4417  result = TM_WouldBlock;
4418  /* recovery code expects to have buffer lock held */
4420  goto failed;
4421  }
4422  break;
4423  case LockWaitError:
4424  if (!ConditionalXactLockTableWait(xwait))
4425  ereport(ERROR,
4427  errmsg("could not obtain lock on row in relation \"%s\"",
4428  RelationGetRelationName(relation))));
4429  break;
4430  }
4431  }
4432 
4433  /* if there are updates, follow the update chain */
4434  if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
4435  {
4436  TM_Result res;
4437 
4438  res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
4440  mode);
4441  if (res != TM_Ok)
4442  {
4443  result = res;
4444  /* recovery code expects to have buffer lock held */
4446  goto failed;
4447  }
4448  }
4449 
4451 
4452  /*
4453  * xwait is done, but if xwait had just locked the tuple then some
4454  * other xact could update this tuple before we get to this point.
4455  * Check for xmax change, and start over if so.
4456  */
4457  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4459  xwait))
4460  goto l3;
4461 
4462  if (!(infomask & HEAP_XMAX_IS_MULTI))
4463  {
4464  /*
4465  * Otherwise check if it committed or aborted. Note we cannot
4466  * be here if the tuple was only locked by somebody who didn't
4467  * conflict with us; that would have been handled above. So
4468  * that transaction must necessarily be gone by now. But
4469  * don't check for this in the multixact case, because some
4470  * locker transactions might still be running.
4471  */
4472  UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
4473  }
4474  }
4475 
4476  /* By here, we're certain that we hold buffer exclusive lock again */
4477 
4478  /*
4479  * We may lock if previous xmax aborted, or if it committed but only
4480  * locked the tuple without updating it; or if we didn't have to wait
4481  * at all for whatever reason.
4482  */
4483  if (!require_sleep ||
4484  (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
4487  result = TM_Ok;
4488  else if (!ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid) ||
4490  result = TM_Updated;
4491  else
4492  result = TM_Deleted;
4493  }
4494 
4495 failed:
4496  if (result != TM_Ok)
4497  {
4498  Assert(result == TM_SelfModified || result == TM_Updated ||
4499  result == TM_Deleted || result == TM_WouldBlock);
4500  Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
4501  Assert(result != TM_Updated ||
4502  !ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid));
4503  tmfd->ctid = tuple->t_data->t_ctid;
4504  tmfd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
4505  if (result == TM_SelfModified)
4506  tmfd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
4507  else
4508  tmfd->cmax = InvalidCommandId;
4509  goto out_locked;
4510  }
4511 
4512  /*
4513  * If we didn't pin the visibility map page and the page has become all
4514  * visible while we were busy locking the buffer, or during some
4515  * subsequent window during which we had it unlocked, we'll have to unlock
4516  * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
4517  * unfortunate, especially since we'll now have to recheck whether the
4518  * tuple has been locked or updated under us, but hopefully it won't
4519  * happen very often.
4520  */
4521  if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
4522  {
4523  LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
4524  visibilitymap_pin(relation, block, &vmbuffer);
4526  goto l3;
4527  }
4528 
4529  xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
4530  old_infomask = tuple->t_data->t_infomask;
4531 
4532  /*
4533  * If this is the first possibly-multixact-able operation in the current
4534  * transaction, set my per-backend OldestMemberMXactId setting. We can be
4535  * certain that the transaction will never become a member of any older
4536  * MultiXactIds than that. (We have to do this even if we end up just
4537  * using our own TransactionId below, since some other backend could
4538  * incorporate our XID into a MultiXact immediately afterwards.)
4539  */
4541 
4542  /*
4543  * Compute the new xmax and infomask to store into the tuple. Note we do
4544  * not modify the tuple just yet, because that would leave it in the wrong
4545  * state if multixact.c elogs.
4546  */
4547  compute_new_xmax_infomask(xmax, old_infomask, tuple->t_data->t_infomask2,
4548  GetCurrentTransactionId(), mode, false,
4549  &xid, &new_infomask, &new_infomask2);
4550 
4552 
4553  /*
4554  * Store transaction information of xact locking the tuple.
4555  *
4556  * Note: Cmax is meaningless in this context, so don't set it; this avoids
4557  * possibly generating a useless combo CID. Moreover, if we're locking a
4558  * previously updated tuple, it's important to preserve the Cmax.
4559  *
4560  * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
4561  * we would break the HOT chain.
4562  */
4563  tuple->t_data->t_infomask &= ~HEAP_XMAX_BITS;
4564  tuple->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
4565  tuple->t_data->t_infomask |= new_infomask;
4566  tuple->t_data->t_infomask2 |= new_infomask2;
4567  if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
4569  HeapTupleHeaderSetXmax(tuple->t_data, xid);
4570 
4571  /*
4572  * Make sure there is no forward chain link in t_ctid. Note that in the
4573  * cases where the tuple has been updated, we must not overwrite t_ctid,
4574  * because it was set by the updater. Moreover, if the tuple has been
4575  * updated, we need to follow the update chain to lock the new versions of
4576  * the tuple as well.
4577  */
4578  if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
4579  tuple->t_data->t_ctid = *tid;
4580 
4581  /* Clear only the all-frozen bit on visibility map if needed */
4582  if (PageIsAllVisible(page) &&
4583  visibilitymap_clear(relation, block, vmbuffer,
4585  cleared_all_frozen = true;
4586 
4587 
4588  MarkBufferDirty(*buffer);
4589 
4590  /*
4591  * XLOG stuff. You might think that we don't need an XLOG record because
4592  * there is no state change worth restoring after a crash. You would be
4593  * wrong however: we have just written either a TransactionId or a
4594  * MultiXactId that may never have been seen on disk before, and we need
4595  * to make sure that there are XLOG entries covering those ID numbers.
4596  * Else the same IDs might be re-used after a crash, which would be
4597  * disastrous if this page made it to disk before the crash. Essentially
4598  * we have to enforce the WAL log-before-data rule even in this case.
4599  * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
4600  * entries for everything anyway.)
4601  */
4602  if (RelationNeedsWAL(relation))
4603  {
4604  xl_heap_lock xlrec;
4605  XLogRecPtr recptr;
4606 
4607  XLogBeginInsert();
4608  XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD);
4609 
4610  xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
4611  xlrec.locking_xid = xid;
4612  xlrec.infobits_set = compute_infobits(new_infomask,
4613  tuple->t_data->t_infomask2);
4614  xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
4615  XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
4616 
4617  /* we don't decode row locks atm, so no need to log the origin */
4618 
4619  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
4620 
4621  PageSetLSN(page, recptr);
4622  }
4623 
4624  END_CRIT_SECTION();
4625 
4626  result = TM_Ok;
4627 
4628 out_locked:
4629  LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
4630 
4631 out_unlocked:
4632  if (BufferIsValid(vmbuffer))
4633  ReleaseBuffer(vmbuffer);
4634 
4635  /*
4636  * Don't update the visibility map here. Locking a tuple doesn't change
4637  * visibility info.
4638  */
4639 
4640  /*
4641  * Now that we have successfully marked the tuple as locked, we can
4642  * release the lmgr tuple lock, if we had it.
4643  */
4644  if (have_tuple_lock)
4645  UnlockTupleTuplock(relation, tid, mode);
4646 
4647  return result;
4648 }
#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:274
static uint8 compute_infobits(uint16 infomask, uint16 infomask2)
Definition: heapam.c:2397
#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:268
TransactionId locking_xid
Definition: heapam_xlog.h:273
uint32 TransactionId
Definition: c.h:521
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:869
void visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *buf)
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1471
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:2419
#define HeapTupleHeaderClearHotUpdated(tup)
Definition: htup_details.h:496
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
CommandId cmax
Definition: tableam.h:127
bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
#define InvalidBuffer
Definition: buf.h:25
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
int errcode(int sqlerrcode)
Definition: elog.c:610
#define HeapTupleHeaderIndicatesMovedPartitions(tup)
Definition: htup_details.h:445
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3513
#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:162
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
#define ERRCODE_LOCK_NOT_AVAILABLE
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderGetRawXmax(tup)
Definition: htup_details.h:375
unsigned short uint16
Definition: c.h:374
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:43
#define HEAP_XMAX_INVALID
Definition: htup_details.h:207
int8 infobits_set
Definition: heapam_xlog.h:275
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:6733
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:490
Oid t_tableOid
Definition: htup.h:66
#define SizeOfHeapLock
Definition: heapam_xlog.h:279
#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:4712
#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:538
#define HEAP_XMAX_IS_LOCKED_ONLY(infomask)
Definition: htup_details.h:230
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3752
#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:1784
static bool heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode, LockWaitPolicy wait_policy, bool *have_tuple_lock)
Definition: heapam.c:4663
#define ereport(elevel,...)
Definition: elog.h:144
TransactionId MultiXactId
Definition: c.h:531
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:746
static TM_Result heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid, TransactionId xid, LockTupleMode mode)
Definition: heapam.c:5430
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:607
#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:6556
#define RelationNeedsWAL(relation)
Definition: rel.h:562
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
#define TUPLOCK_from_mxstatus(status)
Definition: heapam.c:196
int errmsg(const char *fmt,...)
Definition: elog.c:821
#define XLOG_HEAP_LOCK
Definition: heapam_xlog.h:38
#define elog(elevel,...)
Definition: elog.h:214
int i
static MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
Definition: heapam.c:3925
#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:456
static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, int *remaining)
Definition: heapam.c:6755
#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 2097 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(), FirstOffsetNumber, xl_heap_multi_insert::flags, GetCurrentTransactionId(), HEAP_DEFAULT_FILLFACTOR, HEAP_INSERT_NO_LOGICAL, heap_prepare_insert(), i, init, InvalidBlockNumber, InvalidBuffer, IsCatalogRelation(), ItemPointerGetOffsetNumber, log_heap_new_cid(), MarkBufferDirty(), MAXALIGN, xl_heap_multi_insert::ntuples, xl_heap_multi_insert::offsets, PageClearAllVisible, PageGetHeapFreeSpace(), PageGetMaxOffsetNumber, PageIsAllVisible, 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_clear(), VISIBILITYMAP_VALID_BITS, 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().

2099 {
2101  HeapTuple *heaptuples;
2102  int i;
2103  int ndone;
2104  PGAlignedBlock scratch;
2105  Page page;
2106  bool needwal;
2107  Size saveFreeSpace;
2108  bool need_tuple_data = RelationIsLogicallyLogged(relation);
2109  bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
2110 
2111  /* currently not needed (thus unsupported) for heap_multi_insert() */
2113 
2114  needwal = RelationNeedsWAL(relation);
2115  saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
2117 
2118  /* Toast and set header data in all the slots */
2119  heaptuples = palloc(ntuples * sizeof(HeapTuple));
2120  for (i = 0; i < ntuples; i++)
2121  {
2122  HeapTuple tuple;
2123 
2124  tuple = ExecFetchSlotHeapTuple(slots[i], true, NULL);
2125  slots[i]->tts_tableOid = RelationGetRelid(relation);
2126  tuple->t_tableOid = slots[i]->tts_tableOid;
2127  heaptuples[i] = heap_prepare_insert(relation, tuple, xid, cid,
2128  options);
2129  }
2130 
2131  /*
2132  * We're about to do the actual inserts -- but check for conflict first,
2133  * to minimize the possibility of having to roll back work we've just
2134  * done.
2135  *
2136  * A check here does not definitively prevent a serialization anomaly;
2137  * that check MUST be done at least past the point of acquiring an
2138  * exclusive buffer content lock on every buffer that will be affected,
2139  * and MAY be done after all inserts are reflected in the buffers and
2140  * those locks are released; otherwise there is a race condition. Since
2141  * multiple buffers can be locked and unlocked in the loop below, and it
2142  * would not be feasible to identify and lock all of those buffers before
2143  * the loop, we must do a final check at the end.
2144  *
2145  * The check here could be omitted with no loss of correctness; it is
2146  * present strictly as an optimization.
2147  *
2148  * For heap inserts, we only need to check for table-level SSI locks. Our
2149  * new tuples can't possibly conflict with existing tuple locks, and heap
2150  * page locks are only consolidated versions of tuple locks; they do not
2151  * lock "gaps" as index page locks do. So we don't need to specify a
2152  * buffer when making the call, which makes for a faster check.
2153  */
2155 
2156  ndone = 0;
2157  while (ndone < ntuples)
2158  {
2159  Buffer buffer;
2160  Buffer vmbuffer = InvalidBuffer;
2161  bool all_visible_cleared = false;
2162  int nthispage;
2163 
2165 
2166  /*
2167  * Find buffer where at least the next tuple will fit. If the page is
2168  * all-visible, this will also pin the requisite visibility map page.
2169  */
2170  buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
2171  InvalidBuffer, options, bistate,
2172  &vmbuffer, NULL);
2173  page = BufferGetPage(buffer);
2174 
2175  /* NO EREPORT(ERROR) from here till changes are logged */
2177 
2178  /*
2179  * RelationGetBufferForTuple has ensured that the first tuple fits.
2180  * Put that on the page, and then as many other tuples as fit.
2181  */
2182  RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
2183 
2184  /*
2185  * For logical decoding we need combocids to properly decode the
2186  * catalog.
2187  */
2188  if (needwal && need_cids)
2189  log_heap_new_cid(relation, heaptuples[ndone]);
2190 
2191  for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
2192  {
2193  HeapTuple heaptup = heaptuples[ndone + nthispage];
2194 
2195  if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
2196  break;
2197 
2198  RelationPutHeapTuple(relation, buffer, heaptup, false);
2199 
2200  /*
2201  * For logical decoding we need combocids to properly decode the
2202  * catalog.
2203  */
2204  if (needwal && need_cids)
2205  log_heap_new_cid(relation, heaptup);
2206  }
2207 
2208  if (PageIsAllVisible(page))
2209  {
2210  all_visible_cleared = true;
2211  PageClearAllVisible(page);
2212  visibilitymap_clear(relation,
2213  BufferGetBlockNumber(buffer),
2214  vmbuffer, VISIBILITYMAP_VALID_BITS);
2215  }
2216 
2217  /*
2218  * XXX Should we set PageSetPrunable on this page ? See heap_insert()
2219  */
2220 
2221  MarkBufferDirty(buffer);
2222 
2223  /* XLOG stuff */
2224  if (needwal)
2225  {
2226  XLogRecPtr recptr;
2227  xl_heap_multi_insert *xlrec;
2229  char *tupledata;
2230  int totaldatalen;
2231  char *scratchptr = scratch.data;
2232  bool init;
2233  int bufflags = 0;
2234 
2235  /*
2236  * If the page was previously empty, we can reinit the page
2237  * instead of restoring the whole thing.
2238  */
2239  init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
2240  PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
2241 
2242  /* allocate xl_heap_multi_insert struct from the scratch area */
2243  xlrec = (xl_heap_multi_insert *) scratchptr;
2244  scratchptr += SizeOfHeapMultiInsert;
2245 
2246  /*
2247  * Allocate offsets array. Unless we're reinitializing the page,
2248  * in that case the tuples are stored in order starting at
2249  * FirstOffsetNumber and we don't need to store the offsets
2250  * explicitly.
2251  */
2252  if (!init)
2253  scratchptr += nthispage * sizeof(OffsetNumber);
2254 
2255  /* the rest of the scratch space is used for tuple data */
2256  tupledata = scratchptr;
2257 
2258  xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
2259  xlrec->ntuples = nthispage;
2260 
2261  /*
2262  * Write out an xl_multi_insert_tuple and the tuple data itself
2263  * for each tuple.
2264  */
2265  for (i = 0; i < nthispage; i++)
2266  {
2267  HeapTuple heaptup = heaptuples[ndone + i];
2268  xl_multi_insert_tuple *tuphdr;
2269  int datalen;
2270 
2271  if (!init)
2272  xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
2273  /* xl_multi_insert_tuple needs two-byte alignment. */
2274  tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
2275  scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
2276 
2277  tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
2278  tuphdr->t_infomask = heaptup->t_data->t_infomask;
2279  tuphdr->t_hoff = heaptup->t_data->t_hoff;
2280 
2281  /* write bitmap [+ padding] [+ oid] + data */
2282  datalen = heaptup->t_len - SizeofHeapTupleHeader;
2283  memcpy(scratchptr,
2284  (char *) heaptup->t_data + SizeofHeapTupleHeader,
2285  datalen);
2286  tuphdr->datalen = datalen;
2287  scratchptr += datalen;
2288  }
2289  totaldatalen = scratchptr - tupledata;
2290  Assert((scratchptr - scratch.data) < BLCKSZ);
2291 
2292  if (need_tuple_data)
2294 
2295  /*
2296  * Signal that this is the last xl_heap_multi_insert record
2297  * emitted by this call to heap_multi_insert(). Needed for logical
2298  * decoding so it knows when to cleanup temporary data.
2299  */
2300  if (ndone + nthispage == ntuples)
2301  xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
2302 
2303  if (init)
2304  {
2305  info |= XLOG_HEAP_INIT_PAGE;
2306  bufflags |= REGBUF_WILL_INIT;
2307  }
2308 
2309  /*
2310  * If we're doing logical decoding, include the new tuple data
2311  * even if we take a full-page image of the page.
2312  */
2313  if (need_tuple_data)
2314  bufflags |= REGBUF_KEEP_DATA;
2315 
2316  XLogBeginInsert();
2317  XLogRegisterData((char *) xlrec, tupledata - scratch.data);
2318  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
2319 
2320  XLogRegisterBufData(0, tupledata, totaldatalen);
2321 
2322  /* filtering by origin on a row level is much more efficient */
2324 
2325  recptr = XLogInsert(RM_HEAP2_ID, info);
2326 
2327  PageSetLSN(page, recptr);
2328  }
2329 
2330  END_CRIT_SECTION();
2331 
2332  UnlockReleaseBuffer(buffer);
2333  if (vmbuffer != InvalidBuffer)
2334  ReleaseBuffer(vmbuffer);
2335 
2336  ndone += nthispage;
2337  }
2338 
2339  /*
2340  * We're done with the actual inserts. Check for conflicts again, to
2341  * ensure that all rw-conflicts in to these inserts are detected. Without
2342  * this final check, a sequential scan of the heap may have locked the
2343  * table after the "before" check, missing one opportunity to detect the
2344  * conflict, and then scanned the table before the new tuples were there,
2345  * missing the other chance to detect the conflict.
2346  *
2347  * For heap inserts, we only need to check for table-level SSI locks. Our
2348  * new tuples can't possibly conflict with existing tuple locks, and heap
2349  * page locks are only consolidated versions of tuple locks; they do not
2350  * lock "gaps" as index page locks do. So we don't need to specify a
2351  * buffer when making the call.
2352  */
2354 
2355  /*
2356  * If tuples are cachable, mark them for invalidation from the caches in
2357  * case we abort. Note it is OK to do this after releasing the buffer,
2358  * because the heaptuples data structure is all in local memory, not in
2359  * the shared buffer.
2360  */
2361  if (IsCatalogRelation(relation))
2362  {
2363  for (i = 0; i < ntuples; i++)
2364  CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
2365  }
2366 
2367  /* copy t_self fields back to the caller's slots */
2368  for (i = 0; i < ntuples; i++)
2369  slots[i]->tts_tid = heaptuples[i]->t_self;
2370 
2371  pgstat_count_heap_insert(relation, ntuples);
2372 }
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:98
static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup)
Definition: heapam.c:7519
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:2043
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:521
OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER]
Definition: heapam_xlog.h:176
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1471
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
unsigned char uint8
Definition: c.h:373
#define InvalidBuffer
Definition: buf.h:25
#define SizeOfHeapMultiInsert
Definition: heapam_xlog.h:179
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:238
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3513
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:635
#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:1083
bool visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer buf, uint8 flags)
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3536
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 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
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:748
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1614
#define RelationGetTargetPageFreeSpace(relation, defaultff)
Definition: rel.h:340
#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:619
#define REGBUF_KEEP_DATA
Definition: xloginsert.h:38
void CheckForSerializableConflictIn(Relation relation, ItemPointer tid, BlockNumber blkno)
Definition: predicate.c:4375
#define PageClearAllVisible(page)
Definition: bufpage.h:389
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:746
size_t Size
Definition: c.h:474
#define InvalidBlockNumber
Definition: block.h:33
#define MAXALIGN(LEN)
Definition: c.h:699
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define RelationNeedsWAL(relation)
Definition: rel.h:562
#define XLH_INSERT_ALL_VISIBLE_CLEARED
Definition: heapam_xlog.h:66
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2663
void pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
Definition: pgstat.c:2111
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:99
#define HEAP_DEFAULT_FILLFACTOR
Definition: rel.h:311
#define SHORTALIGN(LEN)
Definition: c.h:695
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:456
Pointer Page
Definition: bufpage.h:78
#define SizeOfMultiInsertTuple
Definition: heapam_xlog.h:190

◆ 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:2241
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:3583
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1471
bool old_snap_used
Definition: pruneheap.c:48
Relation rel
Definition: pruneheap.c:33
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
TimestampTz old_snap_ts
Definition: pruneheap.c:46
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
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:7173
#define RelationNeedsWAL(relation)
Definition: rel.h:562
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 (!TransactionIdPrecedes(prune_xid, limited_xmin))
152  return;
153  }
154 
155  /*
156  * We prune when a previous UPDATE failed to find enough space on the page
157  * for a new tuple version, or when free space falls below the relation's
158  * fill-factor target (but not less than 10%).
159  *
160  * Checking free space here is questionable since we aren't holding any
161  * lock on the buffer; in the worst case we could get a bogus answer. It's
162  * unlikely to be *seriously* wrong, though, since reading either pd_lower
163  * or pd_upper is probably atomic. Avoiding taking a lock seems more
164  * important than sometimes getting a wrong answer in what is after all
165  * just a heuristic estimate.
166  */
167  minfree = RelationGetTargetPageFreeSpace(relation,
169  minfree = Max(minfree, BLCKSZ / 10);
170 
171  if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
172  {
173  /* OK, try to get exclusive buffer lock */
174  if (!ConditionalLockBufferForCleanup(buffer))
175  return;
176 
177  /*
178  * Now that we have buffer lock, get accurate information about the
179  * page's free space, and recheck the heuristic about whether to
180  * prune. (We needn't recheck PageIsPrunable, since no one else could
181  * have pruned while we hold pin.)
182  */
183  if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
184  {
185  TransactionId ignore = InvalidTransactionId; /* return value not
186  * needed */
187 
188  /* OK to prune */
189  (void) heap_page_prune(relation, buffer, vistest,
190  limited_xmin, limited_ts,
191  true, &ignore, NULL);
192  }
193 
194  /* And release buffer lock */
196  }
197 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
int heap_page_prune(Relation relation, Buffer buffer, GlobalVisState *vistest, TransactionId old_snap_xmin, TimestampTz old_snap_ts, bool report_stats, TransactionId *latestRemovedXid, OffsetNumber *off_loc)
Definition: pruneheap.c:223
uint32 TransactionId
Definition: c.h:521
int64 TimestampTz
Definition: timestamp.h:39
static bool OldSnapshotThresholdActive(void)
Definition: snapmgr.h:102
bool TransactionIdLimitedForOldSnapshots(TransactionId recentXmin, Relation relation, TransactionId *limit_xid, TimestampTz *limit_ts)
Definition: snapmgr.c:1751
bool RecoveryInProgress(void)
Definition: xlog.c:8076
bool GlobalVisTestIsRemovableXid(GlobalVisState *state, TransactionId xid)
Definition: procarray.c:4086
#define PageIsFull(page)
Definition: bufpage.h:378
GlobalVisState * GlobalVisTestFor(Relation rel)
Definition: procarray.c:3918
bool ConditionalLockBufferForCleanup(Buffer buffer)
Definition: bufmgr.c:3946
Size PageGetHeapFreeSpace(Page page)
Definition: bufpage.c:874
void SnapshotTooOldMagicForTest(void)
Definition: snapmgr.c:1689
#define InvalidTransactionId
Definition: transam.h:31
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
#define RelationGetTargetPageFreeSpace(relation, defaultff)
Definition: rel.h:340
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3752
#define Max(x, y)
Definition: c.h:922
PageHeaderData * PageHeader
Definition: bufpage.h:166
size_t Size
Definition: c.h:474
int old_snapshot_threshold
Definition: snapmgr.c:78
#define HEAP_DEFAULT_FILLFACTOR
Definition: rel.h:311
#define TransactionIdIsValid(xid)
Definition: transam.h:41
TransactionId GlobalVisTestNonRemovableHorizon(GlobalVisState *state)
Definition: procarray.c:4124
Pointer Page
Definition: bufpage.h:78

◆ heap_rescan()

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

Definition at line 1219 of file heapam.c.

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

Referenced by SampleHeapTupleVisible().

1221 {
1222  HeapScanDesc scan = (HeapScanDesc) sscan;
1223 
1224  if (set_params)
1225  {
1226  if (allow_strat)
1227  scan->rs_base.rs_flags |= SO_ALLOW_STRAT;
1228  else
1229  scan->rs_base.rs_flags &= ~SO_ALLOW_STRAT;
1230 
1231  if (allow_sync)
1232  scan->rs_base.rs_flags |= SO_ALLOW_SYNC;
1233  else
1234  scan->rs_base.rs_flags &= ~SO_ALLOW_SYNC;
1235 
1236  if (allow_pagemode && scan->rs_base.rs_snapshot &&
1239  else
1241  }
1242 
1243  /*
1244  * unpin scan buffers
1245  */
1246  if (BufferIsValid(scan->rs_cbuf))
1247  ReleaseBuffer(scan->rs_cbuf);
1248 
1249  /*
1250  * reinitialize scan descriptor
1251  */
1252  initscan(scan, key, true);
1253 }
TableScanDescData rs_base
Definition: heapam.h:49
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3513
uint32 rs_flags
Definition: relscan.h:43
struct HeapScanDescData * HeapScanDesc
Definition: heapam.h:73
#define IsMVCCSnapshot(snapshot)
Definition: snapmgr.h:97
Buffer rs_cbuf
Definition: heapam.h:60
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
struct SnapshotData * rs_snapshot
Definition: relscan.h:35
static void initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
Definition: heapam.c:209

◆ heap_setscanlimits()

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

Definition at line 330 of file heapam.c.

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

Referenced by heapam_index_build_range_scan().

331 {
332  HeapScanDesc scan = (HeapScanDesc) sscan;
333 
334  Assert(!scan->rs_inited); /* else too late to change */
335  /* else rs_startblock is significant */
336  Assert(!(scan->rs_base.rs_flags & SO_ALLOW_SYNC));
337 
338  /* Check startBlk is valid (but allow case of zero blocks...) */
339  Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
340 
341  scan->rs_startblock = startBlk;
342  scan->rs_numblocks = numBlks;
343 }
TableScanDescData rs_base
Definition: heapam.h:49
uint32 rs_flags
Definition: relscan.h:43
struct HeapScanDescData * HeapScanDesc
Definition: heapam.h:73
BlockNumber rs_numblocks
Definition: heapam.h:54
bool rs_inited
Definition: heapam.h:58
BlockNumber rs_startblock
Definition: heapam.h:53
#define Assert(condition)
Definition: c.h:746

◆ heap_tuple_needs_eventual_freeze()

bool heap_tuple_needs_eventual_freeze ( HeapTupleHeader  tuple)

Definition at line 6771 of file heapam.c.

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

Referenced by collect_corrupt_items(), and heap_page_is_all_visible().

6772 {
6773  TransactionId xid;
6774 
6775  /*
6776  * If xmin is a normal transaction ID, this tuple is definitely not
6777  * frozen.
6778  */
6779  xid = HeapTupleHeaderGetXmin(tuple);
6780  if (TransactionIdIsNormal(xid))
6781  return true;
6782 
6783  /*
6784  * If xmax is a valid xact or multixact, this tuple is also not frozen.
6785  */
6786  if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
6787  {
6788  MultiXactId multi;
6789 
6790  multi = HeapTupleHeaderGetRawXmax(tuple);
6791  if (MultiXactIdIsValid(multi))
6792  return true;
6793  }
6794  else
6795  {
6796  xid = HeapTupleHeaderGetRawXmax(tuple);
6797  if (TransactionIdIsNormal(xid))
6798  return true;
6799  }
6800 
6801  if (tuple->t_infomask & HEAP_MOVED)
6802  {
6803  xid = HeapTupleHeaderGetXvac(tuple);
6804  if (TransactionIdIsNormal(xid))
6805  return true;
6806  }
6807 
6808  return false;
6809 }
uint32 TransactionId
Definition: c.h:521
#define HeapTupleHeaderGetRawXmax(tup)
Definition: htup_details.h:375