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, TransactionId OldestXmin, bool report_stats, TransactionId *latestRemovedXid)
 
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 ss_report_location (Relation rel, BlockNumber location)
 
BlockNumber ss_get_location (Relation rel, BlockNumber relnblocks)
 
void SyncScanShmemInit (void)
 
Size SyncScanShmemSize (void)
 
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)
 
void HeapTupleSetHintBits (HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid)
 
bool HeapTupleHeaderIsOnlyLocked (HeapTupleHeader tuple)
 
bool XidInMVCCSnapshot (TransactionId xid, Snapshot snapshot)
 
bool HeapTupleIsSurelyDead (HeapTuple htup, TransactionId OldestXmin)
 
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 1792 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().

1793 {
1794  if (bistate->current_buf != InvalidBuffer)
1795  ReleaseBuffer(bistate->current_buf);
1796  FreeAccessStrategy(bistate->strategy);
1797  pfree(bistate);
1798 }
#define InvalidBuffer
Definition: buf.h:25
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3483
void pfree(void *pointer)
Definition: mcxt.c:1056
void FreeAccessStrategy(BufferAccessStrategy strategy)
Definition: freelist.c:597

◆ GetBulkInsertState()

BulkInsertState GetBulkInsertState ( void  )

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

1779 {
1780  BulkInsertState bistate;
1781 
1782  bistate = (BulkInsertState) palloc(sizeof(BulkInsertStateData));
1784  bistate->current_buf = InvalidBuffer;
1785  return bistate;
1786 }
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:949
Buffer current_buf
Definition: hio.h:32

◆ heap_abort_speculative()

void heap_abort_speculative ( Relation  relation,
ItemPointer  tid 
)

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

5538 {
5540  ItemId lp;
5541  HeapTupleData tp;
5542  Page page;
5543  BlockNumber block;
5544  Buffer buffer;
5545  TransactionId prune_xid;
5546 
5547  Assert(ItemPointerIsValid(tid));
5548 
5549  block = ItemPointerGetBlockNumber(tid);
5550  buffer = ReadBuffer(relation, block);
5551  page = BufferGetPage(buffer);
5552 
5554 
5555  /*
5556  * Page can't be all visible, we just inserted into it, and are still
5557  * running.
5558  */
5559  Assert(!PageIsAllVisible(page));
5560 
5561  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
5562  Assert(ItemIdIsNormal(lp));
5563 
5564  tp.t_tableOid = RelationGetRelid(relation);
5565  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
5566  tp.t_len = ItemIdGetLength(lp);
5567  tp.t_self = *tid;
5568 
5569  /*
5570  * Sanity check that the tuple really is a speculatively inserted tuple,
5571  * inserted by us.
5572  */
5573  if (tp.t_data->t_choice.t_heap.t_xmin != xid)
5574  elog(ERROR, "attempted to kill a tuple inserted by another transaction");
5575  if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
5576  elog(ERROR, "attempted to kill a non-speculative tuple");
5578 
5579  /*
5580  * No need to check for serializable conflicts here. There is never a
5581  * need for a combocid, either. No need to extract replica identity, or
5582  * do anything special with infomask bits.
5583  */
5584 
5586 
5587  /*
5588  * The tuple will become DEAD immediately. Flag that this page is a
5589  * candidate for pruning by setting xmin to TransactionXmin. While not
5590  * immediately prunable, it is the oldest xid we can cheaply determine
5591  * that's safe against wraparound / being older than the table's
5592  * relfrozenxid. To defend against the unlikely case of a new relation
5593  * having a newer relfrozenxid than our TransactionXmin, use relfrozenxid
5594  * if so (vacuum can't subsequently move relfrozenxid to beyond
5595  * TransactionXmin, so there's no race here).
5596  */
5598  if (TransactionIdPrecedes(TransactionXmin, relation->rd_rel->relfrozenxid))
5599  prune_xid = relation->rd_rel->relfrozenxid;
5600  else
5601  prune_xid = TransactionXmin;
5602  PageSetPrunable(page, prune_xid);
5603 
5604  /* store transaction information of xact deleting the tuple */
5607 
5608  /*
5609  * Set the tuple header xmin to InvalidTransactionId. This makes the
5610  * tuple immediately invisible everyone. (In particular, to any
5611  * transactions waiting on the speculative token, woken up later.)
5612  */
5614 
5615  /* Clear the speculative insertion token too */
5616  tp.t_data->t_ctid = tp.t_self;
5617 
5618  MarkBufferDirty(buffer);
5619 
5620  /*
5621  * XLOG stuff
5622  *
5623  * The WAL records generated here match heap_delete(). The same recovery
5624  * routines are used.
5625  */
5626  if (RelationNeedsWAL(relation))
5627  {
5628  xl_heap_delete xlrec;
5629  XLogRecPtr recptr;
5630 
5631  xlrec.flags = XLH_DELETE_IS_SUPER;
5633  tp.t_data->t_infomask2);
5635  xlrec.xmax = xid;
5636 
5637  XLogBeginInsert();
5638  XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
5639  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
5640 
5641  /* No replica identity & replication origin logged */
5642 
5643  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
5644 
5645  PageSetLSN(page, recptr);
5646  }
5647 
5648  END_CRIT_SECTION();
5649 
5650  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
5651 
5652  if (HeapTupleHasExternal(&tp))
5653  {
5654  Assert(!IsToastRelation(relation));
5655  heap_toast_delete(relation, &tp, true);
5656  }
5657 
5658  /*
5659  * Never need to mark tuple for invalidation, since catalogs don't support
5660  * speculative insertion
5661  */
5662 
5663  /* Now we can release the buffer */
5664  ReleaseBuffer(buffer);
5665 
5666  /* count deletion, as we counted the insertion too */
5667  pgstat_count_heap_delete(relation);
5668 }
#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:95
static uint8 compute_infobits(uint16 infomask, uint16 infomask2)
Definition: heapam.c:2368
HeapTupleFields t_heap
Definition: htup_details.h:156
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:513
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1468
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:214
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:398
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3483
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
Form_pg_class rd_rel
Definition: rel.h:109
OffsetNumber offnum
Definition: heapam_xlog.h:106
TransactionId TransactionXmin
Definition: snapmgr.c:166
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:105
void heap_toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
Definition: heaptoast.c:43
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:423
uint32 t_len
Definition: htup.h:64
#define SizeOfHeapDelete
Definition: heapam_xlog.h:111
#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:324
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:416
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3722
#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:738
uint8 infobits_set
Definition: heapam_xlog.h:107
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:606
#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:2043
#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:121
#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 1132 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_rd, TableScanDescData::rs_snapshot, HeapScanDescData::rs_strategy, SO_ALLOW_PAGEMODE, SO_TYPE_SAMPLESCAN, SO_TYPE_SEQSCAN, and HeapTupleData::t_tableOid.

Referenced by SampleHeapTupleVisible().

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

6968 {
6969  TransactionId latestRemovedXid = InvalidTransactionId;
6970  BlockNumber hblkno;
6972  Page hpage;
6973 #ifdef USE_PREFETCH
6974  XidHorizonPrefetchState prefetch_state;
6975  int prefetch_distance;
6976 #endif
6977 
6978  /*
6979  * Sort to avoid repeated lookups for the same page, and to make it more
6980  * likely to access items in an efficient order. In particular, this
6981  * ensures that if there are multiple pointers to the same page, they all
6982  * get processed looking up and locking the page just once.
6983  */
6984  qsort((void *) tids, nitems, sizeof(ItemPointerData),
6985  (int (*) (const void *, const void *)) ItemPointerCompare);
6986 
6987 #ifdef USE_PREFETCH
6988  /* Initialize prefetch state. */
6989  prefetch_state.cur_hblkno = InvalidBlockNumber;
6990  prefetch_state.next_item = 0;
6991  prefetch_state.nitems = nitems;
6992  prefetch_state.tids = tids;
6993 
6994  /*
6995  * Compute the prefetch distance that we will attempt to maintain.
6996  *
6997  * Since the caller holds a buffer lock somewhere in rel, we'd better make
6998  * sure that isn't a catalog relation before we call code that does
6999  * syscache lookups, to avoid risk of deadlock.
7000  */
7001  if (IsCatalogRelation(rel))
7002  prefetch_distance = maintenance_io_concurrency;
7003  else
7004  prefetch_distance =
7006 
7007  /* Start prefetching. */
7008  xid_horizon_prefetch_buffer(rel, &prefetch_state, prefetch_distance);
7009 #endif
7010 
7011  /* Iterate over all tids, and check their horizon */
7012  hblkno = InvalidBlockNumber;
7013  hpage = NULL;
7014  for (int i = 0; i < nitems; i++)
7015  {
7016  ItemPointer htid = &tids[i];
7017  ItemId hitemid;
7018  OffsetNumber hoffnum;
7019 
7020  /*
7021  * Read heap buffer, but avoid refetching if it's the same block as
7022  * required for the last tid.
7023  */
7024  if (hblkno == InvalidBlockNumber ||
7025  ItemPointerGetBlockNumber(htid) != hblkno)
7026  {
7027  /* release old buffer */
7028  if (BufferIsValid(buf))
7029  {
7031  ReleaseBuffer(buf);
7032  }
7033 
7034  hblkno = ItemPointerGetBlockNumber(htid);
7035 
7036  buf = ReadBuffer(rel, hblkno);
7037 
7038 #ifdef USE_PREFETCH
7039 
7040  /*
7041  * To maintain the prefetch distance, prefetch one more page for
7042  * each page we read.
7043  */
7044  xid_horizon_prefetch_buffer(rel, &prefetch_state, 1);
7045 #endif
7046 
7047  hpage = BufferGetPage(buf);
7048 
7050  }
7051 
7052  hoffnum = ItemPointerGetOffsetNumber(htid);
7053  hitemid = PageGetItemId(hpage, hoffnum);
7054 
7055  /*
7056  * Follow any redirections until we find something useful.
7057  */
7058  while (ItemIdIsRedirected(hitemid))
7059  {
7060  hoffnum = ItemIdGetRedirect(hitemid);
7061  hitemid = PageGetItemId(hpage, hoffnum);
7063  }
7064 
7065  /*
7066  * If the heap item has storage, then read the header and use that to
7067  * set latestRemovedXid.
7068  *
7069  * Some LP_DEAD items may not be accessible, so we ignore them.
7070  */
7071  if (ItemIdHasStorage(hitemid))
7072  {
7073  HeapTupleHeader htuphdr;
7074 
7075  htuphdr = (HeapTupleHeader) PageGetItem(hpage, hitemid);
7076 
7077  HeapTupleHeaderAdvanceLatestRemovedXid(htuphdr, &latestRemovedXid);
7078  }
7079  else if (ItemIdIsDead(hitemid))
7080  {
7081  /*
7082  * Conjecture: if hitemid is dead then it had xids before the xids
7083  * marked on LP_NORMAL items. So we just ignore this item and move
7084  * onto the next, for the purposes of calculating
7085  * latestRemovedXid.
7086  */
7087  }
7088  else
7089  Assert(!ItemIdIsUsed(hitemid));
7090 
7091  }
7092 
7093  if (BufferIsValid(buf))
7094  {
7096  ReleaseBuffer(buf);
7097  }
7098 
7099  /*
7100  * If all heap tuples were LP_DEAD then we will be returning
7101  * InvalidTransactionId here, which avoids conflicts. This matches
7102  * existing logic which assumes that LP_DEAD tuples must already be older
7103  * than the latestRemovedXid on the cleanup record that set them as
7104  * LP_DEAD, hence must already have generated a conflict.
7105  */
7106 
7107  return latestRemovedXid;
7108 }
int32 ItemPointerCompare(ItemPointer arg1, ItemPointer arg2)
Definition: itemptr.c:52
void HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple, TransactionId *latestRemovedXid)
Definition: heapam.c:6874
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
bool IsCatalogRelation(Relation relation)
Definition: catalog.c:98
int maintenance_io_concurrency
Definition: bufmgr.c:141
#define ItemIdIsRedirected(itemId)
Definition: itemid.h:106
uint32 TransactionId
Definition: c.h:513
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:3483
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:67
#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:3722
#define ItemIdHasStorage(itemId)
Definition: itemid.h:120
#define Assert(condition)
Definition: c.h:738
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:606
#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:479
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 2413 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().

2416 {
2417  TM_Result result;
2419  ItemId lp;
2420  HeapTupleData tp;
2421  Page page;
2422  BlockNumber block;
2423  Buffer buffer;
2424  Buffer vmbuffer = InvalidBuffer;
2425  TransactionId new_xmax;
2426  uint16 new_infomask,
2427  new_infomask2;
2428  bool have_tuple_lock = false;
2429  bool iscombo;
2430  bool all_visible_cleared = false;
2431  HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
2432  bool old_key_copied = false;
2433 
2434  Assert(ItemPointerIsValid(tid));
2435 
2436  /*
2437  * Forbid this during a parallel operation, lest it allocate a combocid.
2438  * Other workers might need that combocid for visibility checks, and we
2439  * have no provision for broadcasting it to them.
2440  */
2441  if (IsInParallelMode())
2442  ereport(ERROR,
2443  (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
2444  errmsg("cannot delete tuples during a parallel operation")));
2445 
2446  block = ItemPointerGetBlockNumber(tid);
2447  buffer = ReadBuffer(relation, block);
2448  page = BufferGetPage(buffer);
2449 
2450  /*
2451  * Before locking the buffer, pin the visibility map page if it appears to
2452  * be necessary. Since we haven't got the lock yet, someone else might be
2453  * in the middle of changing this, so we'll need to recheck after we have
2454  * the lock.
2455  */
2456  if (PageIsAllVisible(page))
2457  visibilitymap_pin(relation, block, &vmbuffer);
2458 
2460 
2461  /*
2462  * If we didn't pin the visibility map page and the page has become all
2463  * visible while we were busy locking the buffer, we'll have to unlock and
2464  * re-lock, to avoid holding the buffer lock across an I/O. That's a bit
2465  * unfortunate, but hopefully shouldn't happen often.
2466  */
2467  if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
2468  {
2469  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2470  visibilitymap_pin(relation, block, &vmbuffer);
2472  }
2473 
2474  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
2475  Assert(ItemIdIsNormal(lp));
2476 
2477  tp.t_tableOid = RelationGetRelid(relation);
2478  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2479  tp.t_len = ItemIdGetLength(lp);
2480  tp.t_self = *tid;
2481 
2482 l1:
2483  result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
2484 
2485  if (result == TM_Invisible)
2486  {
2487  UnlockReleaseBuffer(buffer);
2488  ereport(ERROR,
2489  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2490  errmsg("attempted to delete invisible tuple")));
2491  }
2492  else if (result == TM_BeingModified && wait)
2493  {
2494  TransactionId xwait;
2495  uint16 infomask;
2496 
2497  /* must copy state data before unlocking buffer */
2498  xwait = HeapTupleHeaderGetRawXmax(tp.t_data);
2499  infomask = tp.t_data->t_infomask;
2500 
2501  /*
2502  * Sleep until concurrent transaction ends -- except when there's a
2503  * single locker and it's our own transaction. Note we don't care
2504  * which lock mode the locker has, because we need the strongest one.
2505  *
2506  * Before sleeping, we need to acquire tuple lock to establish our
2507  * priority for the tuple (see heap_lock_tuple). LockTuple will
2508  * release us when we are next-in-line for the tuple.
2509  *
2510  * If we are forced to "start over" below, we keep the tuple lock;
2511  * this arranges that we stay at the head of the line while rechecking
2512  * tuple state.
2513  */
2514  if (infomask & HEAP_XMAX_IS_MULTI)
2515  {
2516  bool current_is_member = false;
2517 
2518  if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
2519  LockTupleExclusive, &current_is_member))
2520  {
2521  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2522 
2523  /*
2524  * Acquire the lock, if necessary (but skip it when we're
2525  * requesting a lock and already have one; avoids deadlock).
2526  */
2527  if (!current_is_member)
2529  LockWaitBlock, &have_tuple_lock);
2530 
2531  /* wait for multixact */
2533  relation, &(tp.t_self), XLTW_Delete,
2534  NULL);
2536 
2537  /*
2538  * If xwait had just locked the tuple then some other xact
2539  * could update this tuple before we get to this point. Check
2540  * for xmax change, and start over if so.
2541  */
2542  if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
2544  xwait))
2545  goto l1;
2546  }
2547 
2548  /*
2549  * You might think the multixact is necessarily done here, but not
2550  * so: it could have surviving members, namely our own xact or
2551  * other subxacts of this backend. It is legal for us to delete
2552  * the tuple in either case, however (the latter case is
2553  * essentially a situation of upgrading our former shared lock to
2554  * exclusive). We don't bother changing the on-disk hint bits
2555  * since we are about to overwrite the xmax altogether.
2556  */
2557  }
2558  else if (!TransactionIdIsCurrentTransactionId(xwait))
2559  {
2560  /*
2561  * Wait for regular transaction to end; but first, acquire tuple
2562  * lock.
2563  */
2564  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2566  LockWaitBlock, &have_tuple_lock);
2567  XactLockTableWait(xwait, relation, &(tp.t_self), XLTW_Delete);
2569 
2570  /*
2571  * xwait is done, but if xwait had just locked the tuple then some
2572  * other xact could update this tuple before we get to this point.
2573  * Check for xmax change, and start over if so.
2574  */
2575  if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
2577  xwait))
2578  goto l1;
2579 
2580  /* Otherwise check if it committed or aborted */
2581  UpdateXmaxHintBits(tp.t_data, buffer, xwait);
2582  }
2583 
2584  /*
2585  * We may overwrite if previous xmax aborted, or if it committed but
2586  * only locked the tuple without updating it.
2587  */
2588  if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
2591  result = TM_Ok;
2592  else if (!ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid) ||
2594  result = TM_Updated;
2595  else
2596  result = TM_Deleted;
2597  }
2598 
2599  if (crosscheck != InvalidSnapshot && result == TM_Ok)
2600  {
2601  /* Perform additional check for transaction-snapshot mode RI updates */
2602  if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
2603  result = TM_Updated;
2604  }
2605 
2606  if (result != TM_Ok)
2607  {
2608  Assert(result == TM_SelfModified ||
2609  result == TM_Updated ||
2610  result == TM_Deleted ||
2611  result == TM_BeingModified);
2613  Assert(result != TM_Updated ||
2614  !ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid));
2615  tmfd->ctid = tp.t_data->t_ctid;
2617  if (result == TM_SelfModified)
2618  tmfd->cmax = HeapTupleHeaderGetCmax(tp.t_data);
2619  else
2620  tmfd->cmax = InvalidCommandId;
2621  UnlockReleaseBuffer(buffer);
2622  if (have_tuple_lock)
2623  UnlockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive);
2624  if (vmbuffer != InvalidBuffer)
2625  ReleaseBuffer(vmbuffer);
2626  return result;
2627  }
2628 
2629  /*
2630  * We're about to do the actual delete -- check for conflict first, to
2631  * avoid possibly having to roll back work we've just done.
2632  *
2633  * This is safe without a recheck as long as there is no possibility of
2634  * another process scanning the page between this check and the delete
2635  * being visible to the scan (i.e., an exclusive buffer content lock is
2636  * continuously held from this point until the tuple delete is visible).
2637  */
2638  CheckForSerializableConflictIn(relation, tid, BufferGetBlockNumber(buffer));
2639 
2640  /* replace cid with a combo cid if necessary */
2641  HeapTupleHeaderAdjustCmax(tp.t_data, &cid, &iscombo);
2642 
2643  /*
2644  * Compute replica identity tuple before entering the critical section so
2645  * we don't PANIC upon a memory allocation failure.
2646  */
2647  old_key_tuple = ExtractReplicaIdentity(relation, &tp, true, &old_key_copied);
2648 
2649  /*
2650  * If this is the first possibly-multixact-able operation in the current
2651  * transaction, set my per-backend OldestMemberMXactId setting. We can be
2652  * certain that the transaction will never become a member of any older
2653  * MultiXactIds than that. (We have to do this even if we end up just
2654  * using our own TransactionId below, since some other backend could
2655  * incorporate our XID into a MultiXact immediately afterwards.)
2656  */
2658 
2661  xid, LockTupleExclusive, true,
2662  &new_xmax, &new_infomask, &new_infomask2);
2663 
2665 
2666  /*
2667  * If this transaction commits, the tuple will become DEAD sooner or
2668  * later. Set flag that this page is a candidate for pruning once our xid
2669  * falls below the OldestXmin horizon. If the transaction finally aborts,
2670  * the subsequent page pruning will be a no-op and the hint will be
2671  * cleared.
2672  */
2673  PageSetPrunable(page, xid);
2674 
2675  if (PageIsAllVisible(page))
2676  {
2677  all_visible_cleared = true;
2678  PageClearAllVisible(page);
2679  visibilitymap_clear(relation, BufferGetBlockNumber(buffer),
2680  vmbuffer, VISIBILITYMAP_VALID_BITS);
2681  }
2682 
2683  /* store transaction information of xact deleting the tuple */
2686  tp.t_data->t_infomask |= new_infomask;
2687  tp.t_data->t_infomask2 |= new_infomask2;
2689  HeapTupleHeaderSetXmax(tp.t_data, new_xmax);
2690  HeapTupleHeaderSetCmax(tp.t_data, cid, iscombo);
2691  /* Make sure there is no forward chain link in t_ctid */
2692  tp.t_data->t_ctid = tp.t_self;
2693 
2694  /* Signal that this is actually a move into another partition */
2695  if (changingPart)
2697 
2698  MarkBufferDirty(buffer);
2699 
2700  /*
2701  * XLOG stuff
2702  *
2703  * NB: heap_abort_speculative() uses the same xlog record and replay
2704  * routines.
2705  */
2706  if (RelationNeedsWAL(relation))
2707  {
2708  xl_heap_delete xlrec;
2709  xl_heap_header xlhdr;
2710  XLogRecPtr recptr;
2711 
2712  /* For logical decode we need combocids to properly decode the catalog */
2714  log_heap_new_cid(relation, &tp);
2715 
2716  xlrec.flags = 0;
2717  if (all_visible_cleared)
2719  if (changingPart)
2722  tp.t_data->t_infomask2);
2724  xlrec.xmax = new_xmax;
2725 
2726  if (old_key_tuple != NULL)
2727  {
2728  if (relation->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
2730  else
2732  }
2733 
2734  XLogBeginInsert();
2735  XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
2736 
2737  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
2738 
2739  /*
2740  * Log replica identity of the deleted tuple if there is one
2741  */
2742  if (old_key_tuple != NULL)
2743  {
2744  xlhdr.t_infomask2 = old_key_tuple->t_data->t_infomask2;
2745  xlhdr.t_infomask = old_key_tuple->t_data->t_infomask;
2746  xlhdr.t_hoff = old_key_tuple->t_data->t_hoff;
2747 
2748  XLogRegisterData((char *) &xlhdr, SizeOfHeapHeader);
2749  XLogRegisterData((char *) old_key_tuple->t_data
2751  old_key_tuple->t_len
2753  }
2754 
2755  /* filtering by origin on a row level is much more efficient */
2757 
2758  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
2759 
2760  PageSetLSN(page, recptr);
2761  }
2762 
2763  END_CRIT_SECTION();
2764 
2765  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2766 
2767  if (vmbuffer != InvalidBuffer)
2768  ReleaseBuffer(vmbuffer);
2769 
2770  /*
2771  * If the tuple has toasted out-of-line attributes, we need to delete
2772  * those items too. We have to do this before releasing the buffer
2773  * because we need to look at the contents of the tuple, but it's OK to
2774  * release the content lock on the buffer first.
2775  */
2776  if (relation->rd_rel->relkind != RELKIND_RELATION &&
2777  relation->rd_rel->relkind != RELKIND_MATVIEW)
2778  {
2779  /* toast table entries should never be recursively toasted */
2781  }
2782  else if (HeapTupleHasExternal(&tp))
2783  heap_toast_delete(relation, &tp, false);
2784 
2785  /*
2786  * Mark tuple for invalidation from system caches at next command
2787  * boundary. We have to do this before releasing the buffer because we
2788  * need to look at the contents of the tuple.
2789  */
2790  CacheInvalidateHeapTuple(relation, &tp, NULL);
2791 
2792  /* Now we can release the buffer */
2793  ReleaseBuffer(buffer);
2794 
2795  /*
2796  * Release the lmgr tuple lock, if we had it.
2797  */
2798  if (have_tuple_lock)
2799  UnlockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive);
2800 
2801  pgstat_count_heap_delete(relation);
2802 
2803  if (old_key_tuple != NULL && old_key_copied)
2804  heap_freetuple(old_key_tuple);
2805 
2806  return TM_Ok;
2807 }
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:365
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
ItemPointerData ctid
Definition: tableam.h:124
#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:7492
#define HEAP_XMAX_BITS
Definition: htup_details.h:270
static uint8 compute_infobits(uint16 infomask, uint16 infomask2)
Definition: heapam.c:2368
void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple)
Definition: inval.c:1114
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:513
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:854
void visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *buf)
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1468
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:214
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_changed, bool *copy)
Definition: heapam.c:7574
static bool xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
Definition: heapam.c:2390
#define HeapTupleHeaderClearHotUpdated(tup)
Definition: htup_details.h:496
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
CommandId cmax
Definition: tableam.h:126
bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
#define InvalidBuffer
Definition: buf.h:25
uint16 t_infomask2
Definition: heapam_xlog.h:144
#define PageSetPrunable(page, xid)
Definition: bufpage.h:398
#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:3483
#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:161
OffsetNumber offnum
Definition: heapam_xlog.h:106
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:366
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
bool IsInParallelMode(void)
Definition: xact.c:997
bool visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer buf, uint8 flags)
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3506
TransactionId xmax
Definition: tableam.h:125
#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:105
static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, ItemPointer ctid, XLTW_Oper oper, int *remaining)
Definition: heapam.c:6704
void heap_toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
Definition: heaptoast.c:43
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:423
uint32 t_len
Definition: htup.h:64
#define SizeOfHeapDelete
Definition: heapam_xlog.h:111
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define XLH_DELETE_CONTAINS_OLD_KEY
Definition: heapam_xlog.h:94
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:380
Oid t_tableOid
Definition: htup.h:66
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:398
#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:4683
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
#define InvalidSnapshot
Definition: snapshot.h:123
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:324
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:416
TM_Result
Definition: tableam.h:69
#define RelationIsAccessibleInLogicalDecoding(relation)
Definition: rel.h:619
#define InvalidCommandId
Definition: c.h:530
#define HEAP_XMAX_IS_LOCKED_ONLY(infomask)
Definition: htup_details.h:230
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3722
#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:4377
static void UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
Definition: heapam.c:1756
#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:4634
#define ereport(elevel,...)
Definition: elog.h:144
TransactionId MultiXactId
Definition: c.h:523
#define PageClearAllVisible(page)
Definition: bufpage.h:389
void XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, XLTW_Oper oper)
Definition: lmgr.c:624
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:738
uint8 infobits_set
Definition: heapam_xlog.h:107
CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup)
Definition: combocid.c:118
Definition: tableam.h:75
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:606
uint16 t_infomask
Definition: heapam_xlog.h:145
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask, LockTupleMode lockmode, bool *current_is_member)
Definition: heapam.c:6527
#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:2043
void HeapTupleHeaderAdjustCmax(HeapTupleHeader tup, CommandId *cmax, bool *iscombo)
Definition: combocid.c:153
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2633
#define HeapTupleHasExternal(tuple)
Definition: htup_details.h:673
int errmsg(const char *fmt,...)
Definition: elog.c:824
#define XLH_DELETE_ALL_VISIBLE_CLEARED
Definition: heapam_xlog.h:92
#define XLH_DELETE_IS_PARTITION_MOVE
Definition: heapam_xlog.h:96
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
void XLogBeginInsert(void)
Definition: xloginsert.c:121
#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:149
Pointer Page
Definition: bufpage.h:78
bool HeapTupleSatisfiesVisibility(HeapTuple tup, Snapshot snapshot, Buffer buffer)
#define XLH_DELETE_CONTAINS_OLD_TUPLE
Definition: heapam_xlog.h:93

◆ heap_endscan()

void heap_endscan ( TableScanDesc  scan)

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

1246 {
1247  HeapScanDesc scan = (HeapScanDesc) sscan;
1248 
1249  /* Note: no locking manipulations needed */
1250 
1251  /*
1252  * unpin scan buffers
1253  */
1254  if (BufferIsValid(scan->rs_cbuf))
1255  ReleaseBuffer(scan->rs_cbuf);
1256 
1257  /*
1258  * decrement relation reference count and free scan descriptor storage
1259  */
1261 
1262  if (scan->rs_base.rs_key)
1263  pfree(scan->rs_base.rs_key);
1264 
1265  if (scan->rs_strategy != NULL)
1267 
1268  if (scan->rs_base.rs_flags & SO_TEMP_SNAPSHOT)
1270 
1271  pfree(scan);
1272 }
TableScanDescData rs_base
Definition: heapam.h:49
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3483
uint32 rs_flags
Definition: relscan.h:43
struct HeapScanDescData * HeapScanDesc
Definition: heapam.h:73
void pfree(void *pointer)
Definition: mcxt.c:1056
void RelationDecrementReferenceCount(Relation rel)
Definition: relcache.c:2083
struct ScanKeyData * rs_key
Definition: relscan.h:37
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:907
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 1373 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().

1377 {
1378  ItemPointer tid = &(tuple->t_self);
1379  ItemId lp;
1380  Buffer buffer;
1381  Page page;
1382  OffsetNumber offnum;
1383  bool valid;
1384 
1385  /*
1386  * Fetch and pin the appropriate page of the relation.
1387  */
1388  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
1389 
1390  /*
1391  * Need share lock on buffer to examine tuple commit status.
1392  */
1393  LockBuffer(buffer, BUFFER_LOCK_SHARE);
1394  page = BufferGetPage(buffer);
1395  TestForOldSnapshot(snapshot, relation, page);
1396 
1397  /*
1398  * We'd better check for out-of-range offnum in case of VACUUM since the
1399  * TID was obtained.
1400  */
1401  offnum = ItemPointerGetOffsetNumber(tid);
1402  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
1403  {
1404  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1405  ReleaseBuffer(buffer);
1406  *userbuf = InvalidBuffer;
1407  tuple->t_data = NULL;
1408  return false;
1409  }
1410 
1411  /*
1412  * get the item line pointer corresponding to the requested tid
1413  */
1414  lp = PageGetItemId(page, offnum);
1415 
1416  /*
1417  * Must check for deleted tuple.
1418  */
1419  if (!ItemIdIsNormal(lp))
1420  {
1421  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1422  ReleaseBuffer(buffer);
1423  *userbuf = InvalidBuffer;
1424  tuple->t_data = NULL;
1425  return false;
1426  }
1427 
1428  /*
1429  * fill in *tuple fields
1430  */
1431  tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
1432  tuple->t_len = ItemIdGetLength(lp);
1433  tuple->t_tableOid = RelationGetRelid(relation);
1434 
1435  /*
1436  * check tuple visibility, then release lock
1437  */
1438  valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
1439 
1440  if (valid)
1441  PredicateLockTID(relation, &(tuple->t_self), snapshot,
1442  HeapTupleHeaderGetXmin(tuple->t_data));
1443 
1444  HeapCheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
1445 
1446  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1447 
1448  if (valid)
1449  {
1450  /*
1451  * All checks passed, so return the tuple as valid. Caller is now
1452  * responsible for releasing the buffer.
1453  */
1454  *userbuf = buffer;
1455 
1456  return true;
1457  }
1458 
1459  /* Tuple failed time qual */
1460  ReleaseBuffer(buffer);
1461  *userbuf = InvalidBuffer;
1462 
1463  return false;
1464 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
static void TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page)
Definition: bufmgr.h:277
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define InvalidBuffer
Definition: buf.h:25
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3483
#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:3722
void PredicateLockTID(Relation relation, ItemPointer tid, Snapshot snapshot, TransactionId tuple_xid)
Definition: predicate.c:2545
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition: heapam.c:8992
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:606
#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 5446 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().

5447 {
5448  Buffer buffer;
5449  Page page;
5450  OffsetNumber offnum;
5451  ItemId lp = NULL;
5452  HeapTupleHeader htup;
5453 
5454  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
5456  page = (Page) BufferGetPage(buffer);
5457 
5458  offnum = ItemPointerGetOffsetNumber(tid);
5459  if (PageGetMaxOffsetNumber(page) >= offnum)
5460  lp = PageGetItemId(page, offnum);
5461 
5462  if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
5463  elog(ERROR, "invalid lp");
5464 
5465  htup = (HeapTupleHeader) PageGetItem(page, lp);
5466 
5467  /* SpecTokenOffsetNumber should be distinguishable from any real offset */
5469  "invalid speculative token constant");
5470 
5471  /* NO EREPORT(ERROR) from here till changes are logged */
5473 
5475 
5476  MarkBufferDirty(buffer);
5477 
5478  /*
5479  * Replace the speculative insertion token with a real t_ctid, pointing to
5480  * itself like it does on regular tuples.
5481  */
5482  htup->t_ctid = *tid;
5483 
5484  /* XLOG stuff */
5485  if (RelationNeedsWAL(relation))
5486  {
5487  xl_heap_confirm xlrec;
5488  XLogRecPtr recptr;
5489 
5490  xlrec.offnum = ItemPointerGetOffsetNumber(tid);
5491 
5492  XLogBeginInsert();
5493 
5494  /* We want the same filtering on this as on a plain insert */
5496 
5497  XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
5498  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
5499 
5500  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
5501 
5502  PageSetLSN(page, recptr);
5503  }
5504 
5505  END_CRIT_SECTION();
5506 
5507  UnlockReleaseBuffer(buffer);
5508 }
OffsetNumber offnum
Definition: heapam_xlog.h:296
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1468
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:214
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:852
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3506
#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:398
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define SizeOfHeapConfirm
Definition: heapam_xlog.h:299
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:324
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:416
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3722
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:738
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:606
#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:121
#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 6347 of file heapam.c.

References heap_execute_freeze_tuple(), and heap_prepare_freeze_tuple().

Referenced by rewrite_heap_tuple().

6350 {
6352  bool do_freeze;
6353  bool tuple_totally_frozen;
6354 
6355  do_freeze = heap_prepare_freeze_tuple(tuple,
6356  relfrozenxid, relminmxid,
6357  cutoff_xid, cutoff_multi,
6358  &frz, &tuple_totally_frozen);
6359 
6360  /*
6361  * Note that because this is not a WAL-logged operation, we don't need to
6362  * fill in the offset in the freeze record.
6363  */
6364 
6365  if (do_freeze)
6366  heap_execute_freeze_tuple(tuple, &frz);
6367  return do_freeze;
6368 }
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:6097
void heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
Definition: heapam.c:6326

◆ heap_get_latest_tid()

void heap_get_latest_tid ( TableScanDesc  scan,
ItemPointer  tid 
)

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

1635 {
1636  Relation relation = sscan->rs_rd;
1637  Snapshot snapshot = sscan->rs_snapshot;
1638  ItemPointerData ctid;
1639  TransactionId priorXmax;
1640 
1641  /*
1642  * table_get_latest_tid verified that the passed in tid is valid. Assume
1643  * that t_ctid links are valid however - there shouldn't be invalid ones
1644  * in the table.
1645  */
1646  Assert(ItemPointerIsValid(tid));
1647 
1648  /*
1649  * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
1650  * need to examine, and *tid is the TID we will return if ctid turns out
1651  * to be bogus.
1652  *
1653  * Note that we will loop until we reach the end of the t_ctid chain.
1654  * Depending on the snapshot passed, there might be at most one visible
1655  * version of the row, but we don't try to optimize for that.
1656  */
1657  ctid = *tid;
1658  priorXmax = InvalidTransactionId; /* cannot check first XMIN */
1659  for (;;)
1660  {
1661  Buffer buffer;
1662  Page page;
1663  OffsetNumber offnum;
1664  ItemId lp;
1665  HeapTupleData tp;
1666  bool valid;
1667 
1668  /*
1669  * Read, pin, and lock the page.
1670  */
1671  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
1672  LockBuffer(buffer, BUFFER_LOCK_SHARE);
1673  page = BufferGetPage(buffer);
1674  TestForOldSnapshot(snapshot, relation, page);
1675 
1676  /*
1677  * Check for bogus item number. This is not treated as an error
1678  * condition because it can happen while following a t_ctid link. We
1679  * just assume that the prior tid is OK and return it unchanged.
1680  */
1681  offnum = ItemPointerGetOffsetNumber(&ctid);
1682  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
1683  {
1684  UnlockReleaseBuffer(buffer);
1685  break;
1686  }
1687  lp = PageGetItemId(page, offnum);
1688  if (!ItemIdIsNormal(lp))
1689  {
1690  UnlockReleaseBuffer(buffer);
1691  break;
1692  }
1693 
1694  /* OK to access the tuple */
1695  tp.t_self = ctid;
1696  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
1697  tp.t_len = ItemIdGetLength(lp);
1698  tp.t_tableOid = RelationGetRelid(relation);
1699 
1700  /*
1701  * After following a t_ctid link, we might arrive at an unrelated
1702  * tuple. Check for XMIN match.
1703  */
1704  if (TransactionIdIsValid(priorXmax) &&
1706  {
1707  UnlockReleaseBuffer(buffer);
1708  break;
1709  }
1710 
1711  /*
1712  * Check tuple visibility; if visible, set it as the new result
1713  * candidate.
1714  */
1715  valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
1716  HeapCheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
1717  if (valid)
1718  *tid = ctid;
1719 
1720  /*
1721  * If there's a valid t_ctid link, follow it, else we're done.
1722  */
1723  if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
1727  {
1728  UnlockReleaseBuffer(buffer);
1729  break;
1730  }
1731 
1732  ctid = tp.t_data->t_ctid;
1733  priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
1734  UnlockReleaseBuffer(buffer);
1735  } /* end of loop */
1736 }
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:365
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
static void TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page)
Definition: bufmgr.h:277
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
uint32 TransactionId
Definition: c.h:513
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:3506
#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:3722
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition: heapam.c:8992
#define Assert(condition)
Definition: c.h:738
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:606
#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 745 of file pruneheap.c.

References Assert, FirstOffsetNumber, HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleHeaderIndicatesMovedPartitions, HeapTupleHeaderIsHeapOnly, HeapTupleHeaderIsHotUpdated, 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().

746 {
747  OffsetNumber offnum,
748  maxoff;
749 
750  MemSet(root_offsets, 0, MaxHeapTuplesPerPage * sizeof(OffsetNumber));
751 
752  maxoff = PageGetMaxOffsetNumber(page);
753  for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
754  {
755  ItemId lp = PageGetItemId(page, offnum);
756  HeapTupleHeader htup;
757  OffsetNumber nextoffnum;
758  TransactionId priorXmax;
759 
760  /* skip unused and dead items */
761  if (!ItemIdIsUsed(lp) || ItemIdIsDead(lp))
762  continue;
763 
764  if (ItemIdIsNormal(lp))
765  {
766  htup = (HeapTupleHeader) PageGetItem(page, lp);
767 
768  /*
769  * Check if this tuple is part of a HOT-chain rooted at some other
770  * tuple. If so, skip it for now; we'll process it when we find
771  * its root.
772  */
773  if (HeapTupleHeaderIsHeapOnly(htup))
774  continue;
775 
776  /*
777  * This is either a plain tuple or the root of a HOT-chain.
778  * Remember it in the mapping.
779  */
780  root_offsets[offnum - 1] = offnum;
781 
782  /* If it's not the start of a HOT-chain, we're done with it */
783  if (!HeapTupleHeaderIsHotUpdated(htup))
784  continue;
785 
786  /* Set up to scan the HOT-chain */
787  nextoffnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
788  priorXmax = HeapTupleHeaderGetUpdateXid(htup);
789  }
790  else
791  {
792  /* Must be a redirect item. We do not set its root_offsets entry */
794  /* Set up to scan the HOT-chain */
795  nextoffnum = ItemIdGetRedirect(lp);
796  priorXmax = InvalidTransactionId;
797  }
798 
799  /*
800  * Now follow the HOT-chain and collect other tuples in the chain.
801  *
802  * Note: Even though this is a nested loop, the complexity of the
803  * function is O(N) because a tuple in the page should be visited not
804  * more than twice, once in the outer loop and once in HOT-chain
805  * chases.
806  */
807  for (;;)
808  {
809  lp = PageGetItemId(page, nextoffnum);
810 
811  /* Check for broken chains */
812  if (!ItemIdIsNormal(lp))
813  break;
814 
815  htup = (HeapTupleHeader) PageGetItem(page, lp);
816 
817  if (TransactionIdIsValid(priorXmax) &&
818  !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(htup)))
819  break;
820 
821  /* Remember the root line pointer for this item */
822  root_offsets[nextoffnum - 1] = offnum;
823 
824  /* Advance to next chain member, if any */
825  if (!HeapTupleHeaderIsHotUpdated(htup))
826  break;
827 
828  /* HOT implies it can't have moved to different partition */
830 
831  nextoffnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
832  priorXmax = HeapTupleHeaderGetUpdateXid(htup);
833  }
834  }
835 }
#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:513
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:971
#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 Assert(condition)
Definition: c.h:738
#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 1275 of file heapam.c.

References 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, and unlikely.

Referenced by AlterTableMoveAll(), AlterTableSpaceOptions(), boot_openrel(), 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(), gettype(), heapam_index_build_range_scan(), heapam_index_validate_scan(), index_update_stats(), objectsInSchemaToOids(), pgrowlocks(), pgstat_collect_oids(), pgstat_heap(), ReindexMultipleTables(), remove_dbtablespaces(), RemoveSubscriptionRel(), RenameTableSpace(), ThereIsAtLeastOneRole(), and vac_truncate_clog().

1276 {
1277  HeapScanDesc scan = (HeapScanDesc) sscan;
1278 
1279  /*
1280  * This is still widely used directly, without going through table AM, so
1281  * add a safety check. It's possible we should, at a later point,
1282  * downgrade this to an assert. The reason for checking the AM routine,
1283  * rather than the AM oid, is that this allows to write regression tests
1284  * that create another AM reusing the heap handler.
1285  */
1286  if (unlikely(sscan->rs_rd->rd_tableam != GetHeapamTableAmRoutine()))
1287  ereport(ERROR,
1288  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1289  errmsg_internal("only heap AM is supported")));
1290 
1291  /* Note: no locking manipulations needed */
1292 
1293  if (scan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
1294  heapgettup_pagemode(scan, direction,
1295  scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
1296  else
1297  heapgettup(scan, direction,
1298  scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
1299 
1300  if (scan->rs_ctup.t_data == NULL)
1301  return NULL;
1302 
1303  /*
1304  * if we get here it means we have a new current scan tuple, so point to
1305  * the proper return buffer and return the tuple.
1306  */
1307 
1309 
1310  return &scan->rs_ctup;
1311 }
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
struct ScanKeyData * rs_key
Definition: relscan.h:37
static void heapgettup(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:487
#define ereport(elevel,...)
Definition: elog.h:144
int errmsg_internal(const char *fmt,...)
Definition: elog.c:911
Relation rs_rd
Definition: relscan.h:34
#define unlikely(x)
Definition: c.h:206
#define pgstat_count_heap_getnext(rel)
Definition: pgstat.h:1402
static void heapgettup_pagemode(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:801
const TableAmRoutine * GetHeapamTableAmRoutine(void)

◆ heap_getnextslot()

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

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

1315 {
1316  HeapScanDesc scan = (HeapScanDesc) sscan;
1317 
1318  /* Note: no locking manipulations needed */
1319 
1320  if (sscan->rs_flags & SO_ALLOW_PAGEMODE)
1321  heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1322  else
1323  heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1324 
1325  if (scan->rs_ctup.t_data == NULL)
1326  {
1327  ExecClearTuple(slot);
1328  return false;
1329  }
1330 
1331  /*
1332  * if we get here it means we have a new current scan tuple, so point to
1333  * the proper return buffer and return the tuple.
1334  */
1335 
1337 
1338  ExecStoreBufferHeapTuple(&scan->rs_ctup, slot,
1339  scan->rs_cbuf);
1340  return true;
1341 }
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:487
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:1402
static void heapgettup_pagemode(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:801

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

References Assert, BufferGetBlockNumber(), BufferGetPage, HeapCheckForSerializableConflictOut(), HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleIsHeapOnly, HeapTupleIsHotUpdated, HeapTupleIsSurelyDead(), HeapTupleSatisfiesVisibility(), InvalidTransactionId, ItemIdGetLength, ItemIdGetRedirect, ItemIdIsNormal, ItemIdIsRedirected, ItemPointerGetBlockNumber, ItemPointerGetOffsetNumber, ItemPointerSet, ItemPointerSetOffsetNumber, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PredicateLockTID(), RecentGlobalXmin, 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().

1491 {
1492  Page dp = (Page) BufferGetPage(buffer);
1493  TransactionId prev_xmax = InvalidTransactionId;
1494  BlockNumber blkno;
1495  OffsetNumber offnum;
1496  bool at_chain_start;
1497  bool valid;
1498  bool skip;
1499 
1500  /* If this is not the first call, previous call returned a (live!) tuple */
1501  if (all_dead)
1502  *all_dead = first_call;
1503 
1504  blkno = ItemPointerGetBlockNumber(tid);
1505  offnum = ItemPointerGetOffsetNumber(tid);
1506  at_chain_start = first_call;
1507  skip = !first_call;
1508 
1510  Assert(BufferGetBlockNumber(buffer) == blkno);
1511 
1512  /* Scan through possible multiple members of HOT-chain */
1513  for (;;)
1514  {
1515  ItemId lp;
1516 
1517  /* check for bogus TID */
1518  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
1519  break;
1520 
1521  lp = PageGetItemId(dp, offnum);
1522 
1523  /* check for unused, dead, or redirected items */
1524  if (!ItemIdIsNormal(lp))
1525  {
1526  /* We should only see a redirect at start of chain */
1527  if (ItemIdIsRedirected(lp) && at_chain_start)
1528  {
1529  /* Follow the redirect */
1530  offnum = ItemIdGetRedirect(lp);
1531  at_chain_start = false;
1532  continue;
1533  }
1534  /* else must be end of chain */
1535  break;
1536  }
1537 
1538  /*
1539  * Update heapTuple to point to the element of the HOT chain we're
1540  * currently investigating. Having t_self set correctly is important
1541  * because the SSI checks and the *Satisfies routine for historical
1542  * MVCC snapshots need the correct tid to decide about the visibility.
1543  */
1544  heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
1545  heapTuple->t_len = ItemIdGetLength(lp);
1546  heapTuple->t_tableOid = RelationGetRelid(relation);
1547  ItemPointerSet(&heapTuple->t_self, blkno, offnum);
1548 
1549  /*
1550  * Shouldn't see a HEAP_ONLY tuple at chain start.
1551  */
1552  if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
1553  break;
1554 
1555  /*
1556  * The xmin should match the previous xmax value, else chain is
1557  * broken.
1558  */
1559  if (TransactionIdIsValid(prev_xmax) &&
1560  !TransactionIdEquals(prev_xmax,
1561  HeapTupleHeaderGetXmin(heapTuple->t_data)))
1562  break;
1563 
1564  /*
1565  * When first_call is true (and thus, skip is initially false) we'll
1566  * return the first tuple we find. But on later passes, heapTuple
1567  * will initially be pointing to the tuple we returned last time.
1568  * Returning it again would be incorrect (and would loop forever), so
1569  * we skip it and return the next match we find.
1570  */
1571  if (!skip)
1572  {
1573  /* If it's visible per the snapshot, we must return it */
1574  valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
1575  HeapCheckForSerializableConflictOut(valid, relation, heapTuple,
1576  buffer, snapshot);
1577 
1578  if (valid)
1579  {
1580  ItemPointerSetOffsetNumber(tid, offnum);
1581  PredicateLockTID(relation, &heapTuple->t_self, snapshot,
1582  HeapTupleHeaderGetXmin(heapTuple->t_data));
1583  if (all_dead)
1584  *all_dead = false;
1585  return true;
1586  }
1587  }
1588  skip = false;
1589 
1590  /*
1591  * If we can't see it, maybe no one else can either. At caller
1592  * request, check whether all chain members are dead to all
1593  * transactions.
1594  *
1595  * Note: if you change the criterion here for what is "dead", fix the
1596  * planner's get_actual_variable_range() function to match.
1597  */
1598  if (all_dead && *all_dead &&
1600  *all_dead = false;
1601 
1602  /*
1603  * Check to see if HOT chain continues past this tuple; if so fetch
1604  * the next offnum and loop around.
1605  */
1606  if (HeapTupleIsHotUpdated(heapTuple))
1607  {
1609  blkno);
1610  offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
1611  at_chain_start = false;
1612  prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
1613  }
1614  else
1615  break; /* end of chain */
1616  }
1617 
1618  return false;
1619 }
#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:513
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
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
#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
TransactionId RecentGlobalXmin
Definition: snapmgr.c:168
#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
bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
void PredicateLockTID(Relation relation, ItemPointer tid, Snapshot snapshot, TransactionId tuple_xid)
Definition: predicate.c:2545
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition: heapam.c:8992
#define HeapTupleIsHeapOnly(tuple)
Definition: htup_details.h:685
#define Assert(condition)
Definition: c.h:738
#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:2633
#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 5686 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_set_state_flags(), index_update_stats(), vac_update_datfrozenxid(), and vac_update_relstats().

5687 {
5688  Buffer buffer;
5689  Page page;
5690  OffsetNumber offnum;
5691  ItemId lp = NULL;
5692  HeapTupleHeader htup;
5693  uint32 oldlen;
5694  uint32 newlen;
5695 
5696  /*
5697  * For now, parallel operations are required to be strictly read-only.
5698  * Unlike a regular update, this should never create a combo CID, so it
5699  * might be possible to relax this restriction, but not without more
5700  * thought and testing. It's not clear that it would be useful, anyway.
5701  */
5702  if (IsInParallelMode())
5703  ereport(ERROR,
5704  (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
5705  errmsg("cannot update tuples during a parallel operation")));
5706 
5707  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
5709  page = (Page) BufferGetPage(buffer);
5710 
5711  offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
5712  if (PageGetMaxOffsetNumber(page) >= offnum)
5713  lp = PageGetItemId(page, offnum);
5714 
5715  if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
5716  elog(ERROR, "invalid lp");
5717 
5718  htup = (HeapTupleHeader) PageGetItem(page, lp);
5719 
5720  oldlen = ItemIdGetLength(lp) - htup->t_hoff;
5721  newlen = tuple->t_len - tuple->t_data->t_hoff;
5722  if (oldlen != newlen || htup->t_hoff != tuple->t_data->t_hoff)
5723  elog(ERROR, "wrong tuple length");
5724 
5725  /* NO EREPORT(ERROR) from here till changes are logged */
5727 
5728  memcpy((char *) htup + htup->t_hoff,
5729  (char *) tuple->t_data + tuple->t_data->t_hoff,
5730  newlen);
5731 
5732  MarkBufferDirty(buffer);
5733 
5734  /* XLOG stuff */
5735  if (RelationNeedsWAL(relation))
5736  {
5737  xl_heap_inplace xlrec;
5738  XLogRecPtr recptr;
5739 
5740  xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
5741 
5742  XLogBeginInsert();
5743  XLogRegisterData((char *) &xlrec, SizeOfHeapInplace);
5744 
5745  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
5746  XLogRegisterBufData(0, (char *) htup + htup->t_hoff, newlen);
5747 
5748  /* inplace updates aren't decoded atm, don't log the origin */
5749 
5750  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_INPLACE);
5751 
5752  PageSetLSN(page, recptr);
5753  }
5754 
5755  END_CRIT_SECTION();
5756 
5757  UnlockReleaseBuffer(buffer);
5758 
5759  /*
5760  * Send out shared cache inval if necessary. Note that because we only
5761  * pass the new version of the tuple, this mustn't be used for any
5762  * operations that could change catcache lookup keys. But we aren't
5763  * bothering with index updates either, so that's true a fortiori.
5764  */
5766  CacheInvalidateHeapTuple(relation, tuple, NULL);
5767 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:362
void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple)
Definition: inval.c:1114
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1468
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:214
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
#define SizeOfHeapInplace
Definition: heapam_xlog.h:308
#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:997
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3506
#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:367
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:324
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:416
OffsetNumber offnum
Definition: heapam_xlog.h:304
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3722
#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:606
#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:392
int errmsg(const char *fmt,...)
Definition: elog.c:824
#define elog(elevel,...)
Definition: elog.h:214
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
void XLogBeginInsert(void)
Definition: xloginsert.c:121
#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 1831 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, 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, 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().

1833 {
1835  HeapTuple heaptup;
1836  Buffer buffer;
1837  Buffer vmbuffer = InvalidBuffer;
1838  bool all_visible_cleared = false;
1839 
1840  /*
1841  * Fill in tuple header fields and toast the tuple if necessary.
1842  *
1843  * Note: below this point, heaptup is the data we actually intend to store
1844  * into the relation; tup is the caller's original untoasted data.
1845  */
1846  heaptup = heap_prepare_insert(relation, tup, xid, cid, options);
1847 
1848  /*
1849  * Find buffer to insert this tuple into. If the page is all visible,
1850  * this will also pin the requisite visibility map page.
1851  */
1852  buffer = RelationGetBufferForTuple(relation, heaptup->t_len,
1853  InvalidBuffer, options, bistate,
1854  &vmbuffer, NULL);
1855 
1856  /*
1857  * We're about to do the actual insert -- but check for conflict first, to
1858  * avoid possibly having to roll back work we've just done.
1859  *
1860  * This is safe without a recheck as long as there is no possibility of
1861  * another process scanning the page between this check and the insert
1862  * being visible to the scan (i.e., an exclusive buffer content lock is
1863  * continuously held from this point until the tuple insert is visible).
1864  *
1865  * For a heap insert, we only need to check for table-level SSI locks. Our
1866  * new tuple can't possibly conflict with existing tuple locks, and heap
1867  * page locks are only consolidated versions of tuple locks; they do not
1868  * lock "gaps" as index page locks do. So we don't need to specify a
1869  * buffer when making the call, which makes for a faster check.
1870  */
1872 
1873  /* NO EREPORT(ERROR) from here till changes are logged */
1875 
1876  RelationPutHeapTuple(relation, buffer, heaptup,
1877  (options & HEAP_INSERT_SPECULATIVE) != 0);
1878 
1879  if (PageIsAllVisible(BufferGetPage(buffer)))
1880  {
1881  all_visible_cleared = true;
1883  visibilitymap_clear(relation,
1884  ItemPointerGetBlockNumber(&(heaptup->t_self)),
1885  vmbuffer, VISIBILITYMAP_VALID_BITS);
1886  }
1887 
1888  /*
1889  * XXX Should we set PageSetPrunable on this page ?
1890  *
1891  * The inserting transaction may eventually abort thus making this tuple
1892  * DEAD and hence available for pruning. Though we don't want to optimize
1893  * for aborts, if no other tuple in this page is UPDATEd/DELETEd, the
1894  * aborted tuple will never be pruned until next vacuum is triggered.
1895  *
1896  * If you do add PageSetPrunable here, add it in heap_xlog_insert too.
1897  */
1898 
1899  MarkBufferDirty(buffer);
1900 
1901  /* XLOG stuff */
1902  if (RelationNeedsWAL(relation))
1903  {
1904  xl_heap_insert xlrec;
1905  xl_heap_header xlhdr;
1906  XLogRecPtr recptr;
1907  Page page = BufferGetPage(buffer);
1908  uint8 info = XLOG_HEAP_INSERT;
1909  int bufflags = 0;
1910 
1911  /*
1912  * If this is a catalog, we need to transmit combocids to properly
1913  * decode, so log that as well.
1914  */
1916  log_heap_new_cid(relation, heaptup);
1917 
1918  /*
1919  * If this is the single and first tuple on page, we can reinit the
1920  * page instead of restoring the whole thing. Set flag, and hide
1921  * buffer references from XLogInsert.
1922  */
1923  if (ItemPointerGetOffsetNumber(&(heaptup->t_self)) == FirstOffsetNumber &&
1925  {
1926  info |= XLOG_HEAP_INIT_PAGE;
1927  bufflags |= REGBUF_WILL_INIT;
1928  }
1929 
1930  xlrec.offnum = ItemPointerGetOffsetNumber(&heaptup->t_self);
1931  xlrec.flags = 0;
1932  if (all_visible_cleared)
1937 
1938  /*
1939  * For logical decoding, we need the tuple even if we're doing a full
1940  * page write, so make sure it's included even if we take a full-page
1941  * image. (XXX We could alternatively store a pointer into the FPW).
1942  */
1943  if (RelationIsLogicallyLogged(relation) &&
1945  {
1947  bufflags |= REGBUF_KEEP_DATA;
1948  }
1949 
1950  XLogBeginInsert();
1951  XLogRegisterData((char *) &xlrec, SizeOfHeapInsert);
1952 
1953  xlhdr.t_infomask2 = heaptup->t_data->t_infomask2;
1954  xlhdr.t_infomask = heaptup->t_data->t_infomask;
1955  xlhdr.t_hoff = heaptup->t_data->t_hoff;
1956 
1957  /*
1958  * note we mark xlhdr as belonging to buffer; if XLogInsert decides to
1959  * write the whole page to the xlog, we don't need to store
1960  * xl_heap_header in the xlog.
1961  */
1962  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
1963  XLogRegisterBufData(0, (char *) &xlhdr, SizeOfHeapHeader);
1964  /* PG73FORMAT: write bitmap [+ padding] [+ oid] + data */
1966  (char *) heaptup->t_data + SizeofHeapTupleHeader,
1967  heaptup->t_len - SizeofHeapTupleHeader);
1968 
1969  /* filtering by origin on a row level is much more efficient */
1971 
1972  recptr = XLogInsert(RM_HEAP_ID, info);
1973 
1974  PageSetLSN(page, recptr);
1975  }
1976 
1977  END_CRIT_SECTION();
1978 
1979  UnlockReleaseBuffer(buffer);
1980  if (vmbuffer != InvalidBuffer)
1981  ReleaseBuffer(vmbuffer);
1982 
1983  /*
1984  * If tuple is cachable, mark it for invalidation from the caches in case
1985  * we abort. Note it is OK to do this after releasing the buffer, because
1986  * the heaptup data structure is all in local memory, not in the shared
1987  * buffer.
1988  */
1989  CacheInvalidateHeapTuple(relation, heaptup, NULL);
1990 
1991  /* Note: speculative insertions are counted too, even if aborted later */
1992  pgstat_count_heap_insert(relation, 1);
1993 
1994  /*
1995  * If heaptup is a private copy, release it. Don't forget to copy t_self
1996  * back to the caller's image, too.
1997  */
1998  if (heaptup != tup)
1999  {
2000  tup->t_self = heaptup->t_self;
2001  heap_freetuple(heaptup);
2002  }
2003 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:362
#define SizeofHeapTupleHeader
Definition: htup_details.h:184
#define XLOG_HEAP_INSERT
Definition: heapam_xlog.h:32
static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup)
Definition: heapam.c:7492
void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple)
Definition: inval.c:1114
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid, CommandId cid, int options)
Definition: heapam.c:2012
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:513
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1468
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:214
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
unsigned char uint8
Definition: c.h:365
#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:144
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:238
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3483
#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:3506
#define XLH_INSERT_CONTAINS_NEW_TUPLE
Definition: heapam_xlog.h:69
ItemPointerData t_self
Definition: htup.h:65
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:423
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:320
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:398
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:324
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:416
#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:4377
#define PageClearAllVisible(page)
Definition: bufpage.h:389
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:738
uint16 t_infomask
Definition: heapam_xlog.h:145
#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:160
#define XLH_INSERT_ALL_VISIBLE_CLEARED
Definition: heapam_xlog.h:66
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2633
void pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
Definition: pgstat.c:1997
#define HEAP_INSERT_NO_LOGICAL
Definition: heapam.h:36
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
void XLogBeginInsert(void)
Definition: xloginsert.c:121
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
OffsetNumber offnum
Definition: heapam_xlog.h:154
#define SizeOfHeapHeader
Definition: heapam_xlog.h:149
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 3944 of file heapam.c.

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

Referenced by heapam_tuple_lock().

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

2070 {
2072  HeapTuple *heaptuples;
2073  int i;
2074  int ndone;
2075  PGAlignedBlock scratch;
2076  Page page;
2077  bool needwal;
2078  Size saveFreeSpace;
2079  bool need_tuple_data = RelationIsLogicallyLogged(relation);
2080  bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
2081 
2082  /* currently not needed (thus unsupported) for heap_multi_insert() */
2084 
2085  needwal = RelationNeedsWAL(relation);
2086  saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
2088 
2089  /* Toast and set header data in all the slots */
2090  heaptuples = palloc(ntuples * sizeof(HeapTuple));
2091  for (i = 0; i < ntuples; i++)
2092  {
2093  HeapTuple tuple;
2094 
2095  tuple = ExecFetchSlotHeapTuple(slots[i], true, NULL);
2096  slots[i]->tts_tableOid = RelationGetRelid(relation);
2097  tuple->t_tableOid = slots[i]->tts_tableOid;
2098  heaptuples[i] = heap_prepare_insert(relation, tuple, xid, cid,
2099  options);
2100  }
2101 
2102  /*
2103  * We're about to do the actual inserts -- but check for conflict first,
2104  * to minimize the possibility of having to roll back work we've just
2105  * done.
2106  *
2107  * A check here does not definitively prevent a serialization anomaly;
2108  * that check MUST be done at least past the point of acquiring an
2109  * exclusive buffer content lock on every buffer that will be affected,
2110  * and MAY be done after all inserts are reflected in the buffers and
2111  * those locks are released; otherwise there is a race condition. Since
2112  * multiple buffers can be locked and unlocked in the loop below, and it
2113  * would not be feasible to identify and lock all of those buffers before
2114  * the loop, we must do a final check at the end.
2115  *
2116  * The check here could be omitted with no loss of correctness; it is
2117  * present strictly as an optimization.
2118  *
2119  * For heap inserts, we only need to check for table-level SSI locks. Our
2120  * new tuples can't possibly conflict with existing tuple locks, and heap
2121  * page locks are only consolidated versions of tuple locks; they do not
2122  * lock "gaps" as index page locks do. So we don't need to specify a
2123  * buffer when making the call, which makes for a faster check.
2124  */
2126 
2127  ndone = 0;
2128  while (ndone < ntuples)
2129  {
2130  Buffer buffer;
2131  Buffer vmbuffer = InvalidBuffer;
2132  bool all_visible_cleared = false;
2133  int nthispage;
2134 
2136 
2137  /*
2138  * Find buffer where at least the next tuple will fit. If the page is
2139  * all-visible, this will also pin the requisite visibility map page.
2140  */
2141  buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
2142  InvalidBuffer, options, bistate,
2143  &vmbuffer, NULL);
2144  page = BufferGetPage(buffer);
2145 
2146  /* NO EREPORT(ERROR) from here till changes are logged */
2148 
2149  /*
2150  * RelationGetBufferForTuple has ensured that the first tuple fits.
2151  * Put that on the page, and then as many other tuples as fit.
2152  */
2153  RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
2154 
2155  /*
2156  * Note that heap_multi_insert is not used for catalog tuples yet, but
2157  * this will cover the gap once that is the case.
2158  */
2159  if (needwal && need_cids)
2160  log_heap_new_cid(relation, heaptuples[ndone]);
2161 
2162  for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
2163  {
2164  HeapTuple heaptup = heaptuples[ndone + nthispage];
2165 
2166  if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
2167  break;
2168 
2169  RelationPutHeapTuple(relation, buffer, heaptup, false);
2170 
2171  /*
2172  * We don't use heap_multi_insert for catalog tuples yet, but
2173  * better be prepared...
2174  */
2175  if (needwal && need_cids)
2176  log_heap_new_cid(relation, heaptup);
2177  }
2178 
2179  if (PageIsAllVisible(page))
2180  {
2181  all_visible_cleared = true;
2182  PageClearAllVisible(page);
2183  visibilitymap_clear(relation,
2184  BufferGetBlockNumber(buffer),
2185  vmbuffer, VISIBILITYMAP_VALID_BITS);
2186  }
2187 
2188  /*
2189  * XXX Should we set PageSetPrunable on this page ? See heap_insert()
2190  */
2191 
2192  MarkBufferDirty(buffer);
2193 
2194  /* XLOG stuff */
2195  if (needwal)
2196  {
2197  XLogRecPtr recptr;
2198  xl_heap_multi_insert *xlrec;
2200  char *tupledata;
2201  int totaldatalen;
2202  char *scratchptr = scratch.data;
2203  bool init;
2204  int bufflags = 0;
2205 
2206  /*
2207  * If the page was previously empty, we can reinit the page
2208  * instead of restoring the whole thing.
2209  */
2210  init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
2211  PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
2212 
2213  /* allocate xl_heap_multi_insert struct from the scratch area */
2214  xlrec = (xl_heap_multi_insert *) scratchptr;
2215  scratchptr += SizeOfHeapMultiInsert;
2216 
2217  /*
2218  * Allocate offsets array. Unless we're reinitializing the page,
2219  * in that case the tuples are stored in order starting at
2220  * FirstOffsetNumber and we don't need to store the offsets
2221  * explicitly.
2222  */
2223  if (!init)
2224  scratchptr += nthispage * sizeof(OffsetNumber);
2225 
2226  /* the rest of the scratch space is used for tuple data */
2227  tupledata = scratchptr;
2228 
2229  xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
2230  xlrec->ntuples = nthispage;
2231 
2232  /*
2233  * Write out an xl_multi_insert_tuple and the tuple data itself
2234  * for each tuple.
2235  */
2236  for (i = 0; i < nthispage; i++)
2237  {
2238  HeapTuple heaptup = heaptuples[ndone + i];
2239  xl_multi_insert_tuple *tuphdr;
2240  int datalen;
2241 
2242  if (!init)
2243  xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
2244  /* xl_multi_insert_tuple needs two-byte alignment. */
2245  tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
2246  scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
2247 
2248  tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
2249  tuphdr->t_infomask = heaptup->t_data->t_infomask;
2250  tuphdr->t_hoff = heaptup->t_data->t_hoff;
2251 
2252  /* write bitmap [+ padding] [+ oid] + data */
2253  datalen = heaptup->t_len - SizeofHeapTupleHeader;
2254  memcpy(scratchptr,
2255  (char *) heaptup->t_data + SizeofHeapTupleHeader,
2256  datalen);
2257  tuphdr->datalen = datalen;
2258  scratchptr += datalen;
2259  }
2260  totaldatalen = scratchptr - tupledata;
2261  Assert((scratchptr - scratch.data) < BLCKSZ);
2262 
2263  if (need_tuple_data)
2265 
2266  /*
2267  * Signal that this is the last xl_heap_multi_insert record
2268  * emitted by this call to heap_multi_insert(). Needed for logical
2269  * decoding so it knows when to cleanup temporary data.
2270  */
2271  if (ndone + nthispage == ntuples)
2272  xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
2273 
2274  if (init)
2275  {
2276  info |= XLOG_HEAP_INIT_PAGE;
2277  bufflags |= REGBUF_WILL_INIT;
2278  }
2279 
2280  /*
2281  * If we're doing logical decoding, include the new tuple data
2282  * even if we take a full-page image of the page.
2283  */
2284  if (need_tuple_data)
2285  bufflags |= REGBUF_KEEP_DATA;
2286 
2287  XLogBeginInsert();
2288  XLogRegisterData((char *) xlrec, tupledata - scratch.data);
2289  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
2290 
2291  XLogRegisterBufData(0, tupledata, totaldatalen);
2292 
2293  /* filtering by origin on a row level is much more efficient */
2295 
2296  recptr = XLogInsert(RM_HEAP2_ID, info);
2297 
2298  PageSetLSN(page, recptr);
2299  }
2300 
2301  END_CRIT_SECTION();
2302 
2303  UnlockReleaseBuffer(buffer);
2304  if (vmbuffer != InvalidBuffer)
2305  ReleaseBuffer(vmbuffer);
2306 
2307  ndone += nthispage;
2308  }
2309 
2310  /*
2311  * We're done with the actual inserts. Check for conflicts again, to
2312  * ensure that all rw-conflicts in to these inserts are detected. Without
2313  * this final check, a sequential scan of the heap may have locked the
2314  * table after the "before" check, missing one opportunity to detect the
2315  * conflict, and then scanned the table before the new tuples were there,
2316  * missing the other chance to detect the conflict.
2317  *
2318  * For heap inserts, we only need to check for table-level SSI locks. Our
2319  * new tuples can't possibly conflict with existing tuple locks, and heap
2320  * page locks are only consolidated versions of tuple locks; they do not
2321  * lock "gaps" as index page locks do. So we don't need to specify a
2322  * buffer when making the call.
2323  */
2325 
2326  /*
2327  * If tuples are cachable, mark them for invalidation from the caches in
2328  * case we abort. Note it is OK to do this after releasing the buffer,
2329  * because the heaptuples data structure is all in local memory, not in
2330  * the shared buffer.
2331  */
2332  if (IsCatalogRelation(relation))
2333  {
2334  for (i = 0; i < ntuples; i++)
2335  CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
2336  }
2337 
2338  /* copy t_self fields back to the caller's slots */
2339  for (i = 0; i < ntuples; i++)
2340  slots[i]->tts_tid = heaptuples[i]->t_self;
2341 
2342  pgstat_count_heap_insert(relation, ntuples);
2343 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:362
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:7492
void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple)
Definition: inval.c:1114
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid, CommandId cid, int options)
Definition: heapam.c:2012
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:513
OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER]
Definition: heapam_xlog.h:177
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1468
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:214
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
unsigned char uint8
Definition: c.h:365
#define InvalidBuffer
Definition: buf.h:25
#define SizeOfHeapMultiInsert
Definition: heapam_xlog.h:180
#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:3483
#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:1104
bool visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer buf, uint8 flags)
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3506
Size PageGetHeapFreeSpace(Page page)
Definition: bufpage.c:658
#define XLH_INSERT_CONTAINS_NEW_TUPLE
Definition: heapam_xlog.h:69
ItemPointerData t_self
Definition: htup.h:65
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:423
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:320
Oid t_tableOid
Definition: htup.h:66
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:398
#define init()
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define AssertArg(condition)
Definition: c.h:740
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:324
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:416
#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:4377
#define PageClearAllVisible(page)
Definition: bufpage.h:389
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:738
size_t Size
Definition: c.h:466
#define InvalidBlockNumber
Definition: block.h:33
#define MAXALIGN(LEN)
Definition: c.h:691
#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:2633
void pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
Definition: pgstat.c:1997
void * palloc(Size size)
Definition: mcxt.c:949
#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:687
void XLogBeginInsert(void)
Definition: xloginsert.c:121
#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:191

◆ heap_page_prune()

int heap_page_prune ( Relation  relation,
Buffer  buffer,
TransactionId  OldestXmin,
bool  report_stats,
TransactionId latestRemovedXid 
)

Definition at line 180 of file pruneheap.c.

References BufferGetPage, END_CRIT_SECTION, FirstOffsetNumber, heap_page_prune_execute(), heap_prune_chain(), 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, PageClearFull, PageGetItemId, PageGetMaxOffsetNumber, PageIsFull, PageSetLSN, pgstat_update_heap_dead_tuples(), PruneState::redirected, RelationNeedsWAL, and START_CRIT_SECTION.

Referenced by heap_page_prune_opt(), and lazy_scan_heap().

182 {
183  int ndeleted = 0;
184  Page page = BufferGetPage(buffer);
185  OffsetNumber offnum,
186  maxoff;
187  PruneState prstate;
188 
189  /*
190  * Our strategy is to scan the page and make lists of items to change,
191  * then apply the changes within a critical section. This keeps as much
192  * logic as possible out of the critical section, and also ensures that
193  * WAL replay will work the same as the normal case.
194  *
195  * First, initialize the new pd_prune_xid value to zero (indicating no
196  * prunable tuples). If we find any tuples which may soon become
197  * prunable, we will save the lowest relevant XID in new_prune_xid. Also
198  * initialize the rest of our working state.
199  */
201  prstate.latestRemovedXid = *latestRemovedXid;
202  prstate.nredirected = prstate.ndead = prstate.nunused = 0;
203  memset(prstate.marked, 0, sizeof(prstate.marked));
204 
205  /* Scan the page */
206  maxoff = PageGetMaxOffsetNumber(page);
207  for (offnum = FirstOffsetNumber;
208  offnum <= maxoff;
209  offnum = OffsetNumberNext(offnum))
210  {
211  ItemId itemid;
212 
213  /* Ignore items already processed as part of an earlier chain */
214  if (prstate.marked[offnum])
215  continue;
216 
217  /* Nothing to do if slot is empty or already dead */
218  itemid = PageGetItemId(page, offnum);
219  if (!ItemIdIsUsed(itemid) || ItemIdIsDead(itemid))
220  continue;
221 
222  /* Process this item or chain of items */
223  ndeleted += heap_prune_chain(relation, buffer, offnum,
224  OldestXmin,
225  &prstate);
226  }
227 
228  /* Any error while applying the changes is critical */
230 
231  /* Have we found any prunable items? */
232  if (prstate.nredirected > 0 || prstate.ndead > 0 || prstate.nunused > 0)
233  {
234  /*
235  * Apply the planned item changes, then repair page fragmentation, and
236  * update the page's hint bit about whether it has free line pointers.
237  */
239  prstate.redirected, prstate.nredirected,
240  prstate.nowdead, prstate.ndead,
241  prstate.nowunused, prstate.nunused);
242 
243  /*
244  * Update the page's pd_prune_xid field to either zero, or the lowest
245  * XID of any soon-prunable tuple.
246  */
247  ((PageHeader) page)->pd_prune_xid = prstate.new_prune_xid;
248 
249  /*
250  * Also clear the "page is full" flag, since there's no point in
251  * repeating the prune/defrag process until something else happens to
252  * the page.
253  */
254  PageClearFull(page);
255 
256  MarkBufferDirty(buffer);
257 
258  /*
259  * Emit a WAL XLOG_HEAP2_CLEAN record showing what we did
260  */
261  if (RelationNeedsWAL(relation))
262  {
263  XLogRecPtr recptr;
264 
265  recptr = log_heap_clean(relation, buffer,
266  prstate.redirected, prstate.nredirected,
267  prstate.nowdead, prstate.ndead,
268  prstate.nowunused, prstate.nunused,
269  prstate.latestRemovedXid);
270 
271  PageSetLSN(BufferGetPage(buffer), recptr);
272  }
273  }
274  else
275  {
276  /*
277  * If we didn't prune anything, but have found a new value for the
278  * pd_prune_xid field, update it and mark the buffer dirty. This is
279  * treated as a non-WAL-logged hint.
280  *
281  * Also clear the "page is full" flag if it is set, since there's no
282  * point in repeating the prune/defrag process until something else
283  * happens to the page.
284  */
285  if (((PageHeader) page)->pd_prune_xid != prstate.new_prune_xid ||
286  PageIsFull(page))
287  {
288  ((PageHeader) page)->pd_prune_xid = prstate.new_prune_xid;
289  PageClearFull(page);
290  MarkBufferDirtyHint(buffer, true);
291  }
292  }
293 
295 
296  /*
297  * If requested, report the number of tuples reclaimed to pgstats. This is
298  * ndeleted minus ndead, because we don't want to count a now-DEAD root
299  * item as a deletion for this purpose.
300  */
301  if (report_stats && ndeleted > prstate.ndead)
302  pgstat_update_heap_dead_tuples(relation, ndeleted - prstate.ndead);
303 
304  *latestRemovedXid = prstate.latestRemovedXid;
305 
306  /*
307  * XXX Should we update the FSM information of this page ?
308  *
309  * There are two schools of thought here. We may not want to update FSM
310  * information so that the page is not used for unrelated UPDATEs/INSERTs
311  * and any free space in this page will remain available for further
312  * UPDATEs in *this* page, thus improving chances for doing HOT updates.
313  *
314  * But for a large table and where a page does not receive further UPDATEs
315  * for a long time, we might waste this space by not updating the FSM
316  * information. The relation may get extended and fragmented further.
317  *
318  * One possibility is to leave "fillfactor" worth of space in this page
319  * and update FSM with the remaining space.
320  */
321 
322  return ndeleted;
323 }
static int heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum, TransactionId OldestXmin, PruneState *prstate)
Definition: pruneheap.c:352
int nredirected
Definition: pruneheap.c:34
void pgstat_update_heap_dead_tuples(Relation rel, int delta)
Definition: pgstat.c:2127
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:3553
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1468
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
OffsetNumber nowdead[MaxHeapTuplesPerPage]
Definition: pruneheap.c:39
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
bool marked[MaxHeapTuplesPerPage+1]
Definition: pruneheap.c:42
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
TransactionId new_prune_xid
Definition: pruneheap.c:32
#define PageIsFull(page)
Definition: bufpage.h:378
int nunused
Definition: pruneheap.c:36
uint16 OffsetNumber
Definition: off.h:24
TransactionId latestRemovedXid
Definition: pruneheap.c:33
#define FirstOffsetNumber
Definition: off.h:27
#define InvalidTransactionId
Definition: transam.h:31
static TransactionId OldestXmin
Definition: vacuumlazy.c:332
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageClearFull(page)
Definition: bufpage.h:382
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void heap_page_prune_execute(Buffer buffer, OffsetNumber *redirected, int nredirected, OffsetNumber *nowdead, int ndead, OffsetNumber *nowunused, int nunused)
Definition: pruneheap.c:681
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:40
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:7146
#define RelationNeedsWAL(relation)
Definition: rel.h:562
OffsetNumber redirected[MaxHeapTuplesPerPage *2]
Definition: pruneheap.c:38
int ndead
Definition: pruneheap.c:35
#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 681 of file pruneheap.c.

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

Referenced by heap_page_prune(), and heap_xlog_clean().

685 {
686  Page page = (Page) BufferGetPage(buffer);
687  OffsetNumber *offnum;
688  int i;
689 
690  /* Update all redirected line pointers */
691  offnum = redirected;
692  for (i = 0; i < nredirected; i++)
693  {
694  OffsetNumber fromoff = *offnum++;
695  OffsetNumber tooff = *offnum++;
696  ItemId fromlp = PageGetItemId(page, fromoff);
697 
698  ItemIdSetRedirect(fromlp, tooff);
699  }
700 
701  /* Update all now-dead line pointers */
702  offnum = nowdead;
703  for (i = 0; i < ndead; i++)
704  {
705  OffsetNumber off = *offnum++;
706  ItemId lp = PageGetItemId(page, off);
707 
708  ItemIdSetDead(lp);
709  }
710 
711  /* Update all now-unused line pointers */
712  offnum = nowunused;
713  for (i = 0; i < nunused; i++)
714  {
715  OffsetNumber off = *offnum++;
716  ItemId lp = PageGetItemId(page, off);
717 
718  ItemIdSetUnused(lp);
719  }
720 
721  /*
722  * Finally, repair any fragmentation, and update the page's hint bit about
723  * whether it has free pointers.
724  */
726 }
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:475
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 73 of file pruneheap.c.

References Assert, BUFFER_LOCK_UNLOCK, BufferGetPage, ConditionalLockBufferForCleanup(), HEAP_DEFAULT_FILLFACTOR, heap_page_prune(), InvalidTransactionId, IsCatalogRelation(), LockBuffer(), Max, OldestXmin, PageGetHeapFreeSpace(), PageIsFull, PageIsPrunable, RecentGlobalDataXmin, RecentGlobalXmin, RecoveryInProgress(), RelationGetTargetPageFreeSpace, RelationIsAccessibleInLogicalDecoding, TransactionIdIsValid, and TransactionIdLimitedForOldSnapshots().

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

74 {
75  Page page = BufferGetPage(buffer);
76  Size minfree;
78 
79  /*
80  * We can't write WAL in recovery mode, so there's no point trying to
81  * clean the page. The master will likely issue a cleaning WAL record soon
82  * anyway, so this is no particular loss.
83  */
84  if (RecoveryInProgress())
85  return;
86 
87  /*
88  * Use the appropriate xmin horizon for this relation. If it's a proper
89  * catalog relation or a user defined, additional, catalog relation, we
90  * need to use the horizon that includes slots, otherwise the data-only
91  * horizon can be used. Note that the toast relation of user defined
92  * relations are *not* considered catalog relations.
93  *
94  * It is OK to apply the old snapshot limit before acquiring the cleanup
95  * lock because the worst that can happen is that we are not quite as
96  * aggressive about the cleanup (by however many transaction IDs are
97  * consumed between this point and acquiring the lock). This allows us to
98  * save significant overhead in the case where the page is found not to be
99  * prunable.
100  */
101  if (IsCatalogRelation(relation) ||
103  OldestXmin = RecentGlobalXmin;
104  else
105  OldestXmin =
107  relation);
108 
109  Assert(TransactionIdIsValid(OldestXmin));
110 
111  /*
112  * Let's see if we really need pruning.
113  *
114  * Forget it if page is not hinted to contain something prunable that's
115  * older than OldestXmin.
116  */
117  if (!PageIsPrunable(page, OldestXmin))
118  return;
119 
120  /*
121  * We prune when a previous UPDATE failed to find enough space on the page
122  * for a new tuple version, or when free space falls below the relation's
123  * fill-factor target (but not less than 10%).
124  *
125  * Checking free space here is questionable since we aren't holding any
126  * lock on the buffer; in the worst case we could get a bogus answer. It's
127  * unlikely to be *seriously* wrong, though, since reading either pd_lower
128  * or pd_upper is probably atomic. Avoiding taking a lock seems more
129  * important than sometimes getting a wrong answer in what is after all
130  * just a heuristic estimate.
131  */
132  minfree = RelationGetTargetPageFreeSpace(relation,
134  minfree = Max(minfree, BLCKSZ / 10);
135 
136  if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
137  {
138  /* OK, try to get exclusive buffer lock */
139  if (!ConditionalLockBufferForCleanup(buffer))
140  return;
141 
142  /*
143  * Now that we have buffer lock, get accurate information about the
144  * page's free space, and recheck the heuristic about whether to
145  * prune. (We needn't recheck PageIsPrunable, since no one else could
146  * have pruned while we hold pin.)
147  */
148  if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
149  {
150  TransactionId ignore = InvalidTransactionId; /* return value not
151  * needed */
152 
153  /* OK to prune */
154  (void) heap_page_prune(relation, buffer, OldestXmin, true, &ignore);
155  }
156 
157  /* And release buffer lock */
159  }
160 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
int heap_page_prune(Relation relation, Buffer buffer, TransactionId OldestXmin, bool report_stats, TransactionId *latestRemovedXid)
Definition: pruneheap.c:180
bool IsCatalogRelation(Relation relation)
Definition: catalog.c:98
uint32 TransactionId
Definition: c.h:513
TransactionId TransactionIdLimitedForOldSnapshots(TransactionId recentXmin, Relation relation)
Definition: snapmgr.c:1775
bool RecoveryInProgress(void)
Definition: xlog.c:8071
#define PageIsFull(page)
Definition: bufpage.h:378
bool ConditionalLockBufferForCleanup(Buffer buffer)
Definition: bufmgr.c:3916
Size PageGetHeapFreeSpace(Page page)
Definition: bufpage.c:658
TransactionId RecentGlobalXmin
Definition: snapmgr.c:168
#define InvalidTransactionId
Definition: transam.h:31
static TransactionId OldestXmin
Definition: vacuumlazy.c:332
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
TransactionId RecentGlobalDataXmin
Definition: snapmgr.c:169
#define RelationGetTargetPageFreeSpace(relation, defaultff)
Definition: rel.h:340
#define RelationIsAccessibleInLogicalDecoding(relation)
Definition: rel.h:619
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3722
#define Max(x, y)
Definition: c.h:914
#define Assert(condition)
Definition: c.h:738
size_t Size
Definition: c.h:466
#define PageIsPrunable(page, oldestxmin)
Definition: bufpage.h:392
#define HEAP_DEFAULT_FILLFACTOR
Definition: rel.h:311
#define TransactionIdIsValid(xid)
Definition: transam.h:41
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 1208 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().

1210 {
1211  HeapScanDesc scan = (HeapScanDesc) sscan;
1212 
1213  if (set_params)
1214  {
1215  if (allow_strat)
1216  scan->rs_base.rs_flags |= SO_ALLOW_STRAT;
1217  else
1218  scan->rs_base.rs_flags &= ~SO_ALLOW_STRAT;
1219 
1220  if (allow_sync)
1221  scan->rs_base.rs_flags |= SO_ALLOW_SYNC;
1222  else
1223  scan->rs_base.rs_flags &= ~SO_ALLOW_SYNC;
1224 
1225  if (allow_pagemode && scan->rs_base.rs_snapshot &&
1228  else
1230  }
1231 
1232  /*
1233  * unpin scan buffers
1234  */
1235  if (BufferIsValid(scan->rs_cbuf))
1236  ReleaseBuffer(scan->rs_cbuf);
1237 
1238  /*
1239  * reinitialize scan descriptor
1240  */
1241  initscan(scan, key, true);
1242 }
TableScanDescData rs_base
Definition: heapam.h:49
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3483
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:208

◆ heap_setscanlimits()

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

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

330 {
331  HeapScanDesc scan = (HeapScanDesc) sscan;
332 
333  Assert(!scan->rs_inited); /* else too late to change */
334  /* else rs_startblock is significant */
335  Assert(!(scan->rs_base.rs_flags & SO_ALLOW_SYNC));
336 
337  /* Check startBlk is valid (but allow case of zero blocks...) */
338  Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
339 
340  scan->rs_startblock = startBlk;
341  scan->rs_numblocks = numBlks;
342 }
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:738

◆ heap_tuple_needs_eventual_freeze()

bool heap_tuple_needs_eventual_freeze ( HeapTupleHeader  tuple)

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

6743 {
6744  TransactionId xid;
6745 
6746  /*
6747  * If xmin is a normal transaction ID, this tuple is definitely not
6748  * frozen.
6749  */
6750  xid = HeapTupleHeaderGetXmin(tuple);
6751  if (TransactionIdIsNormal(xid))
6752  return true;
6753 
6754  /*
6755  * If xmax is a valid xact or multixact, this tuple is also not frozen.
6756  */
6757  if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
6758  {
6759  MultiXactId multi;
6760 
6761  multi = HeapTupleHeaderGetRawXmax(tuple);
6762  if (MultiXactIdIsValid(multi))
6763  return true;
6764  }
6765  else
6766  {
6767  xid = HeapTupleHeaderGetRawXmax(tuple);
6768  if (TransactionIdIsNormal(xid))
6769  return true;
6770  }
6771 
6772  if (tuple->t_infomask & HEAP_MOVED)
6773  {
6774  xid = HeapTupleHeaderGetXvac(tuple);
6775  if (TransactionIdIsNormal(xid))
6776  return true;
6777  }
6778 
6779  return false;
6780 }
uint32 TransactionId
Definition: c.h:513
#define HeapTupleHeaderGetRawXmax(tup)
Definition: htup_details.h:375
#define HeapTupleHeaderGetXvac(tup)
Definition: htup_details.h:415
#define MultiXactIdIsValid(multi)
Definition: multixact.h:27
#define HEAP_XMAX_IS_MULTI
Definition: htup_details.h:208
#define HEAP_MOVED
Definition: htup_details.h:216
TransactionId MultiXactId
Definition: c.h:523
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
#define TransactionIdIsNormal(xid)
Definition: transam.h:42

◆ heap_tuple_needs_freeze()

bool heap_tuple_needs_freeze ( HeapTupleHeader  tuple,
TransactionId  cutoff_xid,
MultiXactId  cutoff_multi,
Buffer  buf 
)

Definition at line 6795 of file heapam.c.

References GetMultiXactIdMembers(), HEAP_LOCKED_UPGRADED, HEAP_MOVED, HEAP_XMAX_IS_LOCKED_ONLY, HEAP_XMAX_IS_MULTI, HeapTupleHeaderGetRawXmax, HeapTupleHeaderGetXmin, HeapTupleHeaderGetXvac, i, MultiXactIdIsValid, MultiXactIdPrecedes(), pfree(), HeapTupleHeaderData::t_infomask, TransactionIdIsNormal, and TransactionIdPrecedes().

Referenced by lazy_check_needs_freeze().

6797 {
6798  TransactionId xid;
6799 
6800  xid = HeapTupleHeaderGetXmin(tuple);
6801  if (TransactionIdIsNormal(xid) &&
6802  TransactionIdPrecedes(xid, cutoff_xid))
6803  return true;
6804 
6805  /*
6806  * The considerations for multixacts are complicated; look at
6807  * heap_prepare_freeze_tuple for justifications. This routine had better
6808  * be in sync with that one!
6809  */
6810  if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
6811  {
6812  MultiXactId multi;
6813 
6814  multi = HeapTupleHeaderGetRawXmax(tuple);
6815  if (!MultiXactIdIsValid(multi))
6816  {
6817  /* no xmax set, ignore */
6818  ;
6819  }
6820  else if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
6821  return true;
6822  else if (MultiXactIdPrecedes(multi, cutoff_multi))
6823  return true;
6824  else
6825  {
6826  MultiXactMember *members;
6827  int nmembers;
6828  int i;
6829 
6830  /* need to check whether any member of the mxact is too old */
6831 
6832  nmembers = GetMultiXactIdMembers(multi, &members, false,
6834 
6835  for (i = 0; i < nmembers; i++)
6836  {
6837  if (TransactionIdPrecedes(members[i].xid, cutoff_xid))
6838  {
6839  pfree(members);
6840  return true;
6841  }
6842  }
6843  if (nmembers > 0)
6844  pfree(members);
6845  }
6846  }
6847  else
6848  {
6849  xid = HeapTupleHeaderGetRawXmax(tuple);
6850  if (TransactionIdIsNormal(xid) &&
6851  TransactionIdPrecedes(xid, cutoff_xid))
6852  return true;
6853  }
6854 
6855  if (tuple->t_infomask & HEAP_MOVED)
6856  {
6857  xid = HeapTupleHeaderGetXvac(tuple);
6858  if (TransactionIdIsNormal(xid) &&
6859  TransactionIdPrecedes(xid, cutoff_xid))
6860  return true;
6861  }
6862 
6863  return false;
6864 }
uint32 TransactionId
Definition: c.h:513
#define HEAP_LOCKED_UPGRADED(infomask)
Definition: htup_details.h:252
#define HeapTupleHeaderGetRawXmax(tup)
Definition: htup_details.h:375
void pfree(void *pointer)
Definition: mcxt.c:1056