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)
 
void heap_set_tidrange (TableScanDesc sscan, ItemPointer mintid, ItemPointer maxtid)
 
bool heap_getnextslot_tidrange (TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
 
bool heap_fetch (Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf)
 
bool heap_hot_search_buffer (ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, bool *all_dead, bool first_call)
 
void heap_get_latest_tid (TableScanDesc scan, ItemPointer tid)
 
BulkInsertState GetBulkInsertState (void)
 
void FreeBulkInsertState (BulkInsertState)
 
void ReleaseBulkInsertStatePin (BulkInsertState bistate)
 
void heap_insert (Relation relation, HeapTuple tup, CommandId cid, int options, BulkInsertState bistate)
 
void heap_multi_insert (Relation relation, struct TupleTableSlot **slots, int ntuples, CommandId cid, int options, BulkInsertState bistate)
 
TM_Result heap_delete (Relation relation, ItemPointer tid, CommandId cid, Snapshot crosscheck, bool wait, struct TM_FailureData *tmfd, bool changingPart)
 
void heap_finish_speculative (Relation relation, ItemPointer tid)
 
void heap_abort_speculative (Relation relation, ItemPointer tid)
 
TM_Result heap_update (Relation relation, ItemPointer otid, HeapTuple newtup, CommandId cid, Snapshot crosscheck, bool wait, struct TM_FailureData *tmfd, LockTupleMode *lockmode)
 
TM_Result heap_lock_tuple (Relation relation, HeapTuple tuple, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, bool follow_update, Buffer *buffer, struct TM_FailureData *tmfd)
 
void heap_inplace_update (Relation relation, HeapTuple tuple)
 
bool heap_freeze_tuple (HeapTupleHeader tuple, TransactionId relfrozenxid, TransactionId relminmxid, TransactionId cutoff_xid, TransactionId cutoff_multi)
 
bool heap_tuple_needs_freeze (HeapTupleHeader tuple, TransactionId cutoff_xid, MultiXactId cutoff_multi, Buffer buf)
 
bool heap_tuple_needs_eventual_freeze (HeapTupleHeader tuple)
 
void simple_heap_insert (Relation relation, HeapTuple tup)
 
void simple_heap_delete (Relation relation, ItemPointer tid)
 
void simple_heap_update (Relation relation, ItemPointer otid, HeapTuple tup)
 
TransactionId heap_index_delete_tuples (Relation rel, TM_IndexDeleteOp *delstate)
 
void heap_page_prune_opt (Relation relation, Buffer buffer)
 
int heap_page_prune (Relation relation, Buffer buffer, struct GlobalVisState *vistest, TransactionId old_snap_xmin, TimestampTz old_snap_ts_ts, bool report_stats, OffsetNumber *off_loc)
 
void heap_page_prune_execute (Buffer buffer, OffsetNumber *redirected, int nredirected, OffsetNumber *nowdead, int ndead, OffsetNumber *nowunused, int nunused)
 
void heap_get_root_tuples (Page page, OffsetNumber *root_offsets)
 
void heap_vacuum_rel (Relation rel, struct VacuumParams *params, BufferAccessStrategy bstrategy)
 
void parallel_vacuum_main (dsm_segment *seg, shm_toc *toc)
 
bool HeapTupleSatisfiesVisibility (HeapTuple stup, Snapshot snapshot, Buffer buffer)
 
TM_Result HeapTupleSatisfiesUpdate (HeapTuple stup, CommandId curcid, Buffer buffer)
 
HTSV_Result HeapTupleSatisfiesVacuum (HeapTuple stup, TransactionId OldestXmin, Buffer buffer)
 
HTSV_Result HeapTupleSatisfiesVacuumHorizon (HeapTuple stup, Buffer buffer, TransactionId *dead_after)
 
void HeapTupleSetHintBits (HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid)
 
bool HeapTupleHeaderIsOnlyLocked (HeapTupleHeader tuple)
 
bool XidInMVCCSnapshot (TransactionId xid, Snapshot snapshot)
 
bool HeapTupleIsSurelyDead (HeapTuple htup, struct GlobalVisState *vistest)
 
bool ResolveCminCmaxDuringDecoding (struct HTAB *tuplecid_data, Snapshot snapshot, HeapTuple htup, Buffer buffer, CommandId *cmin, CommandId *cmax)
 
void HeapCheckForSerializableConflictOut (bool valid, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
 

Macro Definition Documentation

◆ HEAP_INSERT_FROZEN

#define HEAP_INSERT_FROZEN   TABLE_INSERT_FROZEN

◆ 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 115 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 79 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 93 of file heapam.h.

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

Function Documentation

◆ FreeBulkInsertState()

void FreeBulkInsertState ( BulkInsertState  )

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

2022 {
2023  if (bistate->current_buf != InvalidBuffer)
2024  ReleaseBuffer(bistate->current_buf);
2025  FreeAccessStrategy(bistate->strategy);
2026  pfree(bistate);
2027 }
#define InvalidBuffer
Definition: buf.h:25
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3784
void pfree(void *pointer)
Definition: mcxt.c:1169
void FreeAccessStrategy(BufferAccessStrategy strategy)
Definition: freelist.c:597

◆ GetBulkInsertState()

BulkInsertState GetBulkInsertState ( void  )

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

2008 {
2009  BulkInsertState bistate;
2010 
2011  bistate = (BulkInsertState) palloc(sizeof(BulkInsertStateData));
2013  bistate->current_buf = InvalidBuffer;
2014  return bistate;
2015 }
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:1062
Buffer current_buf
Definition: hio.h:32

◆ heap_abort_speculative()

void heap_abort_speculative ( Relation  relation,
ItemPointer  tid 
)

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

5896 {
5898  ItemId lp;
5899  HeapTupleData tp;
5900  Page page;
5901  BlockNumber block;
5902  Buffer buffer;
5903  TransactionId prune_xid;
5904 
5905  Assert(ItemPointerIsValid(tid));
5906 
5907  block = ItemPointerGetBlockNumber(tid);
5908  buffer = ReadBuffer(relation, block);
5909  page = BufferGetPage(buffer);
5910 
5912 
5913  /*
5914  * Page can't be all visible, we just inserted into it, and are still
5915  * running.
5916  */
5917  Assert(!PageIsAllVisible(page));
5918 
5919  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
5920  Assert(ItemIdIsNormal(lp));
5921 
5922  tp.t_tableOid = RelationGetRelid(relation);
5923  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
5924  tp.t_len = ItemIdGetLength(lp);
5925  tp.t_self = *tid;
5926 
5927  /*
5928  * Sanity check that the tuple really is a speculatively inserted tuple,
5929  * inserted by us.
5930  */
5931  if (tp.t_data->t_choice.t_heap.t_xmin != xid)
5932  elog(ERROR, "attempted to kill a tuple inserted by another transaction");
5933  if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
5934  elog(ERROR, "attempted to kill a non-speculative tuple");
5936 
5937  /*
5938  * No need to check for serializable conflicts here. There is never a
5939  * need for a combo CID, either. No need to extract replica identity, or
5940  * do anything special with infomask bits.
5941  */
5942 
5944 
5945  /*
5946  * The tuple will become DEAD immediately. Flag that this page is a
5947  * candidate for pruning by setting xmin to TransactionXmin. While not
5948  * immediately prunable, it is the oldest xid we can cheaply determine
5949  * that's safe against wraparound / being older than the table's
5950  * relfrozenxid. To defend against the unlikely case of a new relation
5951  * having a newer relfrozenxid than our TransactionXmin, use relfrozenxid
5952  * if so (vacuum can't subsequently move relfrozenxid to beyond
5953  * TransactionXmin, so there's no race here).
5954  */
5956  if (TransactionIdPrecedes(TransactionXmin, relation->rd_rel->relfrozenxid))
5957  prune_xid = relation->rd_rel->relfrozenxid;
5958  else
5959  prune_xid = TransactionXmin;
5960  PageSetPrunable(page, prune_xid);
5961 
5962  /* store transaction information of xact deleting the tuple */
5965 
5966  /*
5967  * Set the tuple header xmin to InvalidTransactionId. This makes the
5968  * tuple immediately invisible everyone. (In particular, to any
5969  * transactions waiting on the speculative token, woken up later.)
5970  */
5972 
5973  /* Clear the speculative insertion token too */
5974  tp.t_data->t_ctid = tp.t_self;
5975 
5976  MarkBufferDirty(buffer);
5977 
5978  /*
5979  * XLOG stuff
5980  *
5981  * The WAL records generated here match heap_delete(). The same recovery
5982  * routines are used.
5983  */
5984  if (RelationNeedsWAL(relation))
5985  {
5986  xl_heap_delete xlrec;
5987  XLogRecPtr recptr;
5988 
5989  xlrec.flags = XLH_DELETE_IS_SUPER;
5991  tp.t_data->t_infomask2);
5993  xlrec.xmax = xid;
5994 
5995  XLogBeginInsert();
5996  XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
5997  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
5998 
5999  /* No replica identity & replication origin logged */
6000 
6001  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
6002 
6003  PageSetLSN(page, recptr);
6004  }
6005 
6006  END_CRIT_SECTION();
6007 
6008  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
6009 
6010  if (HeapTupleHasExternal(&tp))
6011  {
6012  Assert(!IsToastRelation(relation));
6013  heap_toast_delete(relation, &tp, true);
6014  }
6015 
6016  /*
6017  * Never need to mark tuple for invalidation, since catalogs don't support
6018  * speculative insertion
6019  */
6020 
6021  /* Now we can release the buffer */
6022  ReleaseBuffer(buffer);
6023 
6024  /* count deletion, as we counted the insertion too */
6025  pgstat_count_heap_delete(relation);
6026 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
bool IsToastRelation(Relation relation)
Definition: catalog.c:145
#define HEAP_XMAX_BITS
Definition: htup_details.h:270
#define XLH_DELETE_IS_SUPER
Definition: heapam_xlog.h:99
static uint8 compute_infobits(uint16 infomask, uint16 infomask2)
Definition: heapam.c:2719
HeapTupleFields t_heap
Definition: htup_details.h:156
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:587
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1562
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define END_CRIT_SECTION()
Definition: miscadmin.h:137
#define HeapTupleHeaderIsSpeculative(tup)
Definition: htup_details.h:429
#define PageSetPrunable(page, xid)
Definition: bufpage.h:392
#define START_CRIT_SECTION()
Definition: miscadmin.h:135
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3784
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
Form_pg_class rd_rel
Definition: rel.h:110
union HeapTupleHeaderData::@43 t_choice
OffsetNumber offnum
Definition: heapam_xlog.h:110
TransactionId TransactionXmin
Definition: snapmgr.c:112
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderIsHeapOnly(tup)
Definition: htup_details.h:500
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
#define ERROR
Definition: elog.h:46
ItemPointerData t_ctid
Definition: htup_details.h:160
ItemPointerData t_self
Definition: htup.h:65
TransactionId xmax
Definition: heapam_xlog.h:109
void heap_toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
Definition: heaptoast.c:43
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:438
uint32 t_len
Definition: htup.h:64
#define SizeOfHeapDelete
Definition: heapam_xlog.h:115
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define InvalidTransactionId
Definition: transam.h:31
Oid t_tableOid
Definition: htup.h:66
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
TransactionId t_xmin
Definition: htup_details.h:123
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4023
#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:804
uint8 infobits_set
Definition: heapam_xlog.h:111
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:697
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define RelationNeedsWAL(relation)
Definition: rel.h:570
void pgstat_count_heap_delete(Relation rel)
Definition: pgstat.c:2263
#define HeapTupleHasExternal(tuple)
Definition: htup_details.h:672
#define elog(elevel,...)
Definition: elog.h:232
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define TransactionIdIsValid(xid)
Definition: transam.h:41
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define XLOG_HEAP_DELETE
Definition: heapam_xlog.h:33
#define RelationGetRelid(relation)
Definition: rel.h:457
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:319

◆ heap_beginscan()

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

Definition at line 1185 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, HeapScanDescData::rs_parallelworkerdata, TableScanDescData::rs_rd, TableScanDescData::rs_snapshot, HeapScanDescData::rs_strategy, SO_ALLOW_PAGEMODE, SO_TYPE_SAMPLESCAN, SO_TYPE_SEQSCAN, and HeapTupleData::t_tableOid.

Referenced by SampleHeapTupleVisible().

1189 {
1190  HeapScanDesc scan;
1191 
1192  /*
1193  * increment relation ref count while scanning relation
1194  *
1195  * This is just to make really sure the relcache entry won't go away while
1196  * the scan has a pointer to it. Caller should be holding the rel open
1197  * anyway, so this is redundant in all normal scenarios...
1198  */
1200 
1201  /*
1202  * allocate and initialize scan descriptor
1203  */
1204  scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
1205 
1206  scan->rs_base.rs_rd = relation;
1207  scan->rs_base.rs_snapshot = snapshot;
1208  scan->rs_base.rs_nkeys = nkeys;
1209  scan->rs_base.rs_flags = flags;
1210  scan->rs_base.rs_parallel = parallel_scan;
1211  scan->rs_strategy = NULL; /* set in initscan */
1212 
1213  /*
1214  * Disable page-at-a-time mode if it's not a MVCC-safe snapshot.
1215  */
1216  if (!(snapshot && IsMVCCSnapshot(snapshot)))
1218 
1219  /*
1220  * For seqscan and sample scans in a serializable transaction, acquire a
1221  * predicate lock on the entire relation. This is required not only to
1222  * lock all the matching tuples, but also to conflict with new insertions
1223  * into the table. In an indexscan, we take page locks on the index pages
1224  * covering the range specified in the scan qual, but in a heap scan there
1225  * is nothing more fine-grained to lock. A bitmap scan is a different
1226  * story, there we have already scanned the index and locked the index
1227  * pages covering the predicate. But in that case we still have to lock
1228  * any matching heap tuples. For sample scan we could optimize the locking
1229  * to be at least page-level granularity, but we'd need to add per-tuple
1230  * locking for that.
1231  */
1233  {
1234  /*
1235  * Ensure a missing snapshot is noticed reliably, even if the
1236  * isolation mode means predicate locking isn't performed (and
1237  * therefore the snapshot isn't used here).
1238  */
1239  Assert(snapshot);
1240  PredicateLockRelation(relation, snapshot);
1241  }
1242 
1243  /* we only need to set this up once */
1244  scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
1245 
1246  /*
1247  * Allocate memory to keep track of page allocation for parallel workers
1248  * when doing a parallel scan.
1249  */
1250  if (parallel_scan != NULL)
1252  else
1253  scan->rs_parallelworkerdata = NULL;
1254 
1255  /*
1256  * we do this here instead of in initscan() because heap_rescan also calls
1257  * initscan() and we don't want to allocate memory again
1258  */
1259  if (nkeys > 0)
1260  scan->rs_base.rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
1261  else
1262  scan->rs_base.rs_key = NULL;
1263 
1264  initscan(scan, key, false);
1265 
1266  return (TableScanDesc) scan;
1267 }
TableScanDescData rs_base
Definition: heapam.h:49
void PredicateLockRelation(Relation relation, Snapshot snapshot)
Definition: predicate.c:2569
uint32 rs_flags
Definition: relscan.h:47
struct HeapScanDescData * HeapScanDesc
Definition: heapam.h:79
HeapTupleData rs_ctup
Definition: heapam.h:66
ScanKeyData * ScanKey
Definition: skey.h:75
ParallelBlockTableScanWorkerData * rs_parallelworkerdata
Definition: heapam.h:72
Oid t_tableOid
Definition: htup.h:66
struct ScanKeyData * rs_key
Definition: relscan.h:37
void RelationIncrementReferenceCount(Relation rel)
Definition: relcache.c:2063
BufferAccessStrategy rs_strategy
Definition: heapam.h:64
#define IsMVCCSnapshot(snapshot)
Definition: snapmgr.h:96
#define Assert(condition)
Definition: c.h:804
Relation rs_rd
Definition: relscan.h:34
struct SnapshotData * rs_snapshot
Definition: relscan.h:35
void * palloc(Size size)
Definition: mcxt.c:1062
struct ParallelTableScanDescData * rs_parallel
Definition: relscan.h:49
static void initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
Definition: heapam.c:227
#define RelationGetRelid(relation)
Definition: rel.h:457

◆ heap_delete()

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

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

2767 {
2768  TM_Result result;
2770  ItemId lp;
2771  HeapTupleData tp;
2772  Page page;
2773  BlockNumber block;
2774  Buffer buffer;
2775  Buffer vmbuffer = InvalidBuffer;
2776  TransactionId new_xmax;
2777  uint16 new_infomask,
2778  new_infomask2;
2779  bool have_tuple_lock = false;
2780  bool iscombo;
2781  bool all_visible_cleared = false;
2782  HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
2783  bool old_key_copied = false;
2784 
2785  Assert(ItemPointerIsValid(tid));
2786 
2787  /*
2788  * Forbid this during a parallel operation, lest it allocate a combo CID.
2789  * Other workers might need that combo CID for visibility checks, and we
2790  * have no provision for broadcasting it to them.
2791  */
2792  if (IsInParallelMode())
2793  ereport(ERROR,
2794  (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
2795  errmsg("cannot delete tuples during a parallel operation")));
2796 
2797  block = ItemPointerGetBlockNumber(tid);
2798  buffer = ReadBuffer(relation, block);
2799  page = BufferGetPage(buffer);
2800 
2801  /*
2802  * Before locking the buffer, pin the visibility map page if it appears to
2803  * be necessary. Since we haven't got the lock yet, someone else might be
2804  * in the middle of changing this, so we'll need to recheck after we have
2805  * the lock.
2806  */
2807  if (PageIsAllVisible(page))
2808  visibilitymap_pin(relation, block, &vmbuffer);
2809 
2811 
2812  /*
2813  * If we didn't pin the visibility map page and the page has become all
2814  * visible while we were busy locking the buffer, we'll have to unlock and
2815  * re-lock, to avoid holding the buffer lock across an I/O. That's a bit
2816  * unfortunate, but hopefully shouldn't happen often.
2817  */
2818  if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
2819  {
2820  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2821  visibilitymap_pin(relation, block, &vmbuffer);
2823  }
2824 
2825  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
2826  Assert(ItemIdIsNormal(lp));
2827 
2828  tp.t_tableOid = RelationGetRelid(relation);
2829  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2830  tp.t_len = ItemIdGetLength(lp);
2831  tp.t_self = *tid;
2832 
2833 l1:
2834  result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
2835 
2836  if (result == TM_Invisible)
2837  {
2838  UnlockReleaseBuffer(buffer);
2839  ereport(ERROR,
2840  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2841  errmsg("attempted to delete invisible tuple")));
2842  }
2843  else if (result == TM_BeingModified && wait)
2844  {
2845  TransactionId xwait;
2846  uint16 infomask;
2847 
2848  /* must copy state data before unlocking buffer */
2849  xwait = HeapTupleHeaderGetRawXmax(tp.t_data);
2850  infomask = tp.t_data->t_infomask;
2851 
2852  /*
2853  * Sleep until concurrent transaction ends -- except when there's a
2854  * single locker and it's our own transaction. Note we don't care
2855  * which lock mode the locker has, because we need the strongest one.
2856  *
2857  * Before sleeping, we need to acquire tuple lock to establish our
2858  * priority for the tuple (see heap_lock_tuple). LockTuple will
2859  * release us when we are next-in-line for the tuple.
2860  *
2861  * If we are forced to "start over" below, we keep the tuple lock;
2862  * this arranges that we stay at the head of the line while rechecking
2863  * tuple state.
2864  */
2865  if (infomask & HEAP_XMAX_IS_MULTI)
2866  {
2867  bool current_is_member = false;
2868 
2869  if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
2870  LockTupleExclusive, &current_is_member))
2871  {
2872  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2873 
2874  /*
2875  * Acquire the lock, if necessary (but skip it when we're
2876  * requesting a lock and already have one; avoids deadlock).
2877  */
2878  if (!current_is_member)
2880  LockWaitBlock, &have_tuple_lock);
2881 
2882  /* wait for multixact */
2884  relation, &(tp.t_self), XLTW_Delete,
2885  NULL);
2887 
2888  /*
2889  * If xwait had just locked the tuple then some other xact
2890  * could update this tuple before we get to this point. Check
2891  * for xmax change, and start over if so.
2892  */
2893  if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
2895  xwait))
2896  goto l1;
2897  }
2898 
2899  /*
2900  * You might think the multixact is necessarily done here, but not
2901  * so: it could have surviving members, namely our own xact or
2902  * other subxacts of this backend. It is legal for us to delete
2903  * the tuple in either case, however (the latter case is
2904  * essentially a situation of upgrading our former shared lock to
2905  * exclusive). We don't bother changing the on-disk hint bits
2906  * since we are about to overwrite the xmax altogether.
2907  */
2908  }
2909  else if (!TransactionIdIsCurrentTransactionId(xwait))
2910  {
2911  /*
2912  * Wait for regular transaction to end; but first, acquire tuple
2913  * lock.
2914  */
2915  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2917  LockWaitBlock, &have_tuple_lock);
2918  XactLockTableWait(xwait, relation, &(tp.t_self), XLTW_Delete);
2920 
2921  /*
2922  * xwait is done, but if xwait had just locked the tuple then some
2923  * other xact could update this tuple before we get to this point.
2924  * Check for xmax change, and start over if so.
2925  */
2926  if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
2928  xwait))
2929  goto l1;
2930 
2931  /* Otherwise check if it committed or aborted */
2932  UpdateXmaxHintBits(tp.t_data, buffer, xwait);
2933  }
2934 
2935  /*
2936  * We may overwrite if previous xmax aborted, or if it committed but
2937  * only locked the tuple without updating it.
2938  */
2939  if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
2942  result = TM_Ok;
2943  else if (!ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
2944  result = TM_Updated;
2945  else
2946  result = TM_Deleted;
2947  }
2948 
2949  if (crosscheck != InvalidSnapshot && result == TM_Ok)
2950  {
2951  /* Perform additional check for transaction-snapshot mode RI updates */
2952  if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
2953  result = TM_Updated;
2954  }
2955 
2956  if (result != TM_Ok)
2957  {
2958  Assert(result == TM_SelfModified ||
2959  result == TM_Updated ||
2960  result == TM_Deleted ||
2961  result == TM_BeingModified);
2963  Assert(result != TM_Updated ||
2964  !ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid));
2965  tmfd->ctid = tp.t_data->t_ctid;
2967  if (result == TM_SelfModified)
2968  tmfd->cmax = HeapTupleHeaderGetCmax(tp.t_data);
2969  else
2970  tmfd->cmax = InvalidCommandId;
2971  UnlockReleaseBuffer(buffer);
2972  if (have_tuple_lock)
2973  UnlockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive);
2974  if (vmbuffer != InvalidBuffer)
2975  ReleaseBuffer(vmbuffer);
2976  return result;
2977  }
2978 
2979  /*
2980  * We're about to do the actual delete -- check for conflict first, to
2981  * avoid possibly having to roll back work we've just done.
2982  *
2983  * This is safe without a recheck as long as there is no possibility of
2984  * another process scanning the page between this check and the delete
2985  * being visible to the scan (i.e., an exclusive buffer content lock is
2986  * continuously held from this point until the tuple delete is visible).
2987  */
2988  CheckForSerializableConflictIn(relation, tid, BufferGetBlockNumber(buffer));
2989 
2990  /* replace cid with a combo CID if necessary */
2991  HeapTupleHeaderAdjustCmax(tp.t_data, &cid, &iscombo);
2992 
2993  /*
2994  * Compute replica identity tuple before entering the critical section so
2995  * we don't PANIC upon a memory allocation failure.
2996  */
2997  old_key_tuple = ExtractReplicaIdentity(relation, &tp, true, &old_key_copied);
2998 
2999  /*
3000  * If this is the first possibly-multixact-able operation in the current
3001  * transaction, set my per-backend OldestMemberMXactId setting. We can be
3002  * certain that the transaction will never become a member of any older
3003  * MultiXactIds than that. (We have to do this even if we end up just
3004  * using our own TransactionId below, since some other backend could
3005  * incorporate our XID into a MultiXact immediately afterwards.)
3006  */
3008 
3011  xid, LockTupleExclusive, true,
3012  &new_xmax, &new_infomask, &new_infomask2);
3013 
3015 
3016  /*
3017  * If this transaction commits, the tuple will become DEAD sooner or
3018  * later. Set flag that this page is a candidate for pruning once our xid
3019  * falls below the OldestXmin horizon. If the transaction finally aborts,
3020  * the subsequent page pruning will be a no-op and the hint will be
3021  * cleared.
3022  */
3023  PageSetPrunable(page, xid);
3024 
3025  if (PageIsAllVisible(page))
3026  {
3027  all_visible_cleared = true;
3028  PageClearAllVisible(page);
3029  visibilitymap_clear(relation, BufferGetBlockNumber(buffer),
3030  vmbuffer, VISIBILITYMAP_VALID_BITS);
3031  }
3032 
3033  /* store transaction information of xact deleting the tuple */
3036  tp.t_data->t_infomask |= new_infomask;
3037  tp.t_data->t_infomask2 |= new_infomask2;
3039  HeapTupleHeaderSetXmax(tp.t_data, new_xmax);
3040  HeapTupleHeaderSetCmax(tp.t_data, cid, iscombo);
3041  /* Make sure there is no forward chain link in t_ctid */
3042  tp.t_data->t_ctid = tp.t_self;
3043 
3044  /* Signal that this is actually a move into another partition */
3045  if (changingPart)
3047 
3048  MarkBufferDirty(buffer);
3049 
3050  /*
3051  * XLOG stuff
3052  *
3053  * NB: heap_abort_speculative() uses the same xlog record and replay
3054  * routines.
3055  */
3056  if (RelationNeedsWAL(relation))
3057  {
3058  xl_heap_delete xlrec;
3059  xl_heap_header xlhdr;
3060  XLogRecPtr recptr;
3061 
3062  /* For logical decode we need combo CIDs to properly decode the catalog */
3064  log_heap_new_cid(relation, &tp);
3065 
3066  xlrec.flags = 0;
3067  if (all_visible_cleared)
3069  if (changingPart)
3072  tp.t_data->t_infomask2);
3074  xlrec.xmax = new_xmax;
3075 
3076  if (old_key_tuple != NULL)
3077  {
3078  if (relation->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
3080  else
3082  }
3083 
3084  XLogBeginInsert();
3085  XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
3086 
3087  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
3088 
3089  /*
3090  * Log replica identity of the deleted tuple if there is one
3091  */
3092  if (old_key_tuple != NULL)
3093  {
3094  xlhdr.t_infomask2 = old_key_tuple->t_data->t_infomask2;
3095  xlhdr.t_infomask = old_key_tuple->t_data->t_infomask;
3096  xlhdr.t_hoff = old_key_tuple->t_data->t_hoff;
3097 
3098  XLogRegisterData((char *) &xlhdr, SizeOfHeapHeader);
3099  XLogRegisterData((char *) old_key_tuple->t_data
3101  old_key_tuple->t_len
3103  }
3104 
3105  /* filtering by origin on a row level is much more efficient */
3107 
3108  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
3109 
3110  PageSetLSN(page, recptr);
3111  }
3112 
3113  END_CRIT_SECTION();
3114 
3115  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
3116 
3117  if (vmbuffer != InvalidBuffer)
3118  ReleaseBuffer(vmbuffer);
3119 
3120  /*
3121  * If the tuple has toasted out-of-line attributes, we need to delete
3122  * those items too. We have to do this before releasing the buffer
3123  * because we need to look at the contents of the tuple, but it's OK to
3124  * release the content lock on the buffer first.
3125  */
3126  if (relation->rd_rel->relkind != RELKIND_RELATION &&
3127  relation->rd_rel->relkind != RELKIND_MATVIEW)
3128  {
3129  /* toast table entries should never be recursively toasted */
3131  }
3132  else if (HeapTupleHasExternal(&tp))
3133  heap_toast_delete(relation, &tp, false);
3134 
3135  /*
3136  * Mark tuple for invalidation from system caches at next command
3137  * boundary. We have to do this before releasing the buffer because we
3138  * need to look at the contents of the tuple.
3139  */
3140  CacheInvalidateHeapTuple(relation, &tp, NULL);
3141 
3142  /* Now we can release the buffer */
3143  ReleaseBuffer(buffer);
3144 
3145  /*
3146  * Release the lmgr tuple lock, if we had it.
3147  */
3148  if (have_tuple_lock)
3149  UnlockTupleTuplock(relation, &(tp.t_self), LockTupleExclusive);
3150 
3151  pgstat_count_heap_delete(relation);
3152 
3153  if (old_key_tuple != NULL && old_key_copied)
3154  heap_freetuple(old_key_tuple);
3155 
3156  return TM_Ok;
3157 }
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:365
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
ItemPointerData ctid
Definition: tableam.h:126
#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:8285
#define HEAP_XMAX_BITS
Definition: htup_details.h:270
static uint8 compute_infobits(uint16 infomask, uint16 infomask2)
Definition: heapam.c:2719
void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple)
Definition: inval.c:1122
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:587
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:869
void visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *buf)
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1562
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_changed, bool *copy)
Definition: heapam.c:8367
static bool xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
Definition: heapam.c:2741
#define HeapTupleHeaderClearHotUpdated(tup)
Definition: htup_details.h:495
#define END_CRIT_SECTION()
Definition: miscadmin.h:137
CommandId cmax
Definition: tableam.h:128
bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
#define InvalidBuffer
Definition: buf.h:25
uint16 t_infomask2
Definition: heapam_xlog.h:146
#define PageSetPrunable(page, xid)
Definition: bufpage.h:392
#define START_CRIT_SECTION()
Definition: miscadmin.h:135
int errcode(int sqlerrcode)
Definition: elog.c:698
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:248
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3784
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
Form_pg_class rd_rel
Definition: rel.h:110
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
TM_Result HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, Buffer buffer)
#define UnlockTupleTuplock(rel, tup, mode)
Definition: heapam.c:165
OffsetNumber offnum
Definition: heapam_xlog.h:110
void MultiXactIdSetOldestMember(void)
Definition: multixact.c:625
#define VISIBILITYMAP_VALID_BITS
Definition: visibilitymap.h:28
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderGetRawXmax(tup)
Definition: htup_details.h:375
unsigned short uint16
Definition: c.h:440
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
bool IsInParallelMode(void)
Definition: xact.c:1012
bool visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer buf, uint8 flags)
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3807
TransactionId xmax
Definition: tableam.h:127
#define ERROR
Definition: elog.h:46
#define HEAP_XMAX_INVALID
Definition: htup_details.h:207
ItemPointerData t_ctid
Definition: htup_details.h:160
#define HeapTupleHeaderSetMovedPartitions(tup)
Definition: htup_details.h:448
ItemPointerData t_self
Definition: htup.h:65
TransactionId xmax
Definition: heapam_xlog.h:109
static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, ItemPointer ctid, XLTW_Oper oper, int *remaining)
Definition: heapam.c:7066
void heap_toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
Definition: heaptoast.c:43
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:438
uint32 t_len
Definition: htup.h:64
#define SizeOfHeapDelete
Definition: heapam_xlog.h:115
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define XLH_DELETE_CONTAINS_OLD_KEY
Definition: heapam_xlog.h:98
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:380
Oid t_tableOid
Definition: htup.h:66
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:404
#define HeapTupleHeaderSetCmax(tup, cid, iscombo)
Definition: htup_details.h:405
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
static void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask, uint16 old_infomask2, TransactionId add_to_xmax, LockTupleMode mode, bool is_update, TransactionId *result_xmax, uint16 *result_infomask, uint16 *result_infomask2)
Definition: heapam.c:5042
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
#define InvalidSnapshot
Definition: snapshot.h:123
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
TM_Result
Definition: tableam.h:71
#define RelationIsAccessibleInLogicalDecoding(relation)
Definition: rel.h:626
#define InvalidCommandId
Definition: c.h:604
#define HEAP_XMAX_IS_LOCKED_ONLY(infomask)
Definition: htup_details.h:230
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4023
#define HEAP_KEYS_UPDATED
Definition: htup_details.h:278
#define HEAP_XMAX_IS_MULTI
Definition: htup_details.h:208
void CheckForSerializableConflictIn(Relation relation, ItemPointer tid, BlockNumber blkno)
Definition: predicate.c:4446
static void UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
Definition: heapam.c:1985
#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:4993
#define ereport(elevel,...)
Definition: elog.h:157
TransactionId MultiXactId
Definition: c.h:597
#define PageClearAllVisible(page)
Definition: bufpage.h:389
void XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, XLTW_Oper oper)
Definition: lmgr.c:640
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
uint8 infobits_set
Definition: heapam_xlog.h:111
CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup)
Definition: combocid.c:118
Definition: tableam.h:77
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:697
uint16 t_infomask
Definition: heapam_xlog.h:147
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask, LockTupleMode lockmode, bool *current_is_member)
Definition: heapam.c:6889
#define RelationNeedsWAL(relation)
Definition: rel.h:570
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
void pgstat_count_heap_delete(Relation rel)
Definition: pgstat.c:2263
void HeapTupleHeaderAdjustCmax(HeapTupleHeader tup, CommandId *cmax, bool *iscombo)
Definition: combocid.c:153
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2758
#define HeapTupleHasExternal(tuple)
Definition: htup_details.h:672
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define XLH_DELETE_ALL_VISIBLE_CLEARED
Definition: heapam_xlog.h:96
#define XLH_DELETE_IS_PARTITION_MOVE
Definition: heapam_xlog.h:100
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define XLOG_HEAP_DELETE
Definition: heapam_xlog.h:33
#define RelationGetRelid(relation)
Definition: rel.h:457
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
#define SizeOfHeapHeader
Definition: heapam_xlog.h:151
Pointer Page
Definition: bufpage.h:78
bool HeapTupleSatisfiesVisibility(HeapTuple tup, Snapshot snapshot, Buffer buffer)
#define XLH_DELETE_CONTAINS_OLD_TUPLE
Definition: heapam_xlog.h:97

◆ heap_endscan()

void heap_endscan ( TableScanDesc  scan)

Definition at line 1307 of file heapam.c.

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

Referenced by SampleHeapTupleVisible().

1308 {
1309  HeapScanDesc scan = (HeapScanDesc) sscan;
1310 
1311  /* Note: no locking manipulations needed */
1312 
1313  /*
1314  * unpin scan buffers
1315  */
1316  if (BufferIsValid(scan->rs_cbuf))
1317  ReleaseBuffer(scan->rs_cbuf);
1318 
1319  /*
1320  * decrement relation reference count and free scan descriptor storage
1321  */
1323 
1324  if (scan->rs_base.rs_key)
1325  pfree(scan->rs_base.rs_key);
1326 
1327  if (scan->rs_strategy != NULL)
1329 
1330  if (scan->rs_parallelworkerdata != NULL)
1332 
1333  if (scan->rs_base.rs_flags & SO_TEMP_SNAPSHOT)
1335 
1336  pfree(scan);
1337 }
TableScanDescData rs_base
Definition: heapam.h:49
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3784
uint32 rs_flags
Definition: relscan.h:47
struct HeapScanDescData * HeapScanDesc
Definition: heapam.h:79
void pfree(void *pointer)
Definition: mcxt.c:1169
void RelationDecrementReferenceCount(Relation rel)
Definition: relcache.c:2076
ParallelBlockTableScanWorkerData * rs_parallelworkerdata
Definition: heapam.h:72
struct ScanKeyData * rs_key
Definition: relscan.h:37
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:852
BufferAccessStrategy rs_strategy
Definition: heapam.h:64
Buffer rs_cbuf
Definition: heapam.h:60
void FreeAccessStrategy(BufferAccessStrategy strategy)
Definition: freelist.c:597
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
Relation rs_rd
Definition: relscan.h:34
struct SnapshotData * rs_snapshot
Definition: relscan.h:35

◆ heap_fetch()

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

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

1599 {
1600  ItemPointer tid = &(tuple->t_self);
1601  ItemId lp;
1602  Buffer buffer;
1603  Page page;
1604  OffsetNumber offnum;
1605  bool valid;
1606 
1607  /*
1608  * Fetch and pin the appropriate page of the relation.
1609  */
1610  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
1611 
1612  /*
1613  * Need share lock on buffer to examine tuple commit status.
1614  */
1615  LockBuffer(buffer, BUFFER_LOCK_SHARE);
1616  page = BufferGetPage(buffer);
1617  TestForOldSnapshot(snapshot, relation, page);
1618 
1619  /*
1620  * We'd better check for out-of-range offnum in case of VACUUM since the
1621  * TID was obtained.
1622  */
1623  offnum = ItemPointerGetOffsetNumber(tid);
1624  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
1625  {
1626  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1627  ReleaseBuffer(buffer);
1628  *userbuf = InvalidBuffer;
1629  tuple->t_data = NULL;
1630  return false;
1631  }
1632 
1633  /*
1634  * get the item line pointer corresponding to the requested tid
1635  */
1636  lp = PageGetItemId(page, offnum);
1637 
1638  /*
1639  * Must check for deleted tuple.
1640  */
1641  if (!ItemIdIsNormal(lp))
1642  {
1643  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1644  ReleaseBuffer(buffer);
1645  *userbuf = InvalidBuffer;
1646  tuple->t_data = NULL;
1647  return false;
1648  }
1649 
1650  /*
1651  * fill in *tuple fields
1652  */
1653  tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
1654  tuple->t_len = ItemIdGetLength(lp);
1655  tuple->t_tableOid = RelationGetRelid(relation);
1656 
1657  /*
1658  * check tuple visibility, then release lock
1659  */
1660  valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
1661 
1662  if (valid)
1663  PredicateLockTID(relation, &(tuple->t_self), snapshot,
1664  HeapTupleHeaderGetXmin(tuple->t_data));
1665 
1666  HeapCheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
1667 
1668  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1669 
1670  if (valid)
1671  {
1672  /*
1673  * All checks passed, so return the tuple as valid. Caller is now
1674  * responsible for releasing the buffer.
1675  */
1676  *userbuf = buffer;
1677 
1678  return true;
1679  }
1680 
1681  /* Tuple failed time qual */
1682  ReleaseBuffer(buffer);
1683  *userbuf = InvalidBuffer;
1684 
1685  return false;
1686 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
static void TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page)
Definition: bufmgr.h:279
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define InvalidBuffer
Definition: buf.h:25
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3784
#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:4023
void PredicateLockTID(Relation relation, ItemPointer tid, Snapshot snapshot, TransactionId tuple_xid)
Definition: predicate.c:2614
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition: heapam.c:9850
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:697
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:97
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
int Buffer
Definition: buf.h:23
#define RelationGetRelid(relation)
Definition: rel.h:457
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
bool HeapTupleSatisfiesVisibility(HeapTuple tup, Snapshot snapshot, Buffer buffer)

◆ heap_finish_speculative()

void heap_finish_speculative ( Relation  relation,
ItemPointer  tid 
)

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

5805 {
5806  Buffer buffer;
5807  Page page;
5808  OffsetNumber offnum;
5809  ItemId lp = NULL;
5810  HeapTupleHeader htup;
5811 
5812  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
5814  page = (Page) BufferGetPage(buffer);
5815 
5816  offnum = ItemPointerGetOffsetNumber(tid);
5817  if (PageGetMaxOffsetNumber(page) >= offnum)
5818  lp = PageGetItemId(page, offnum);
5819 
5820  if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
5821  elog(ERROR, "invalid lp");
5822 
5823  htup = (HeapTupleHeader) PageGetItem(page, lp);
5824 
5825  /* SpecTokenOffsetNumber should be distinguishable from any real offset */
5827  "invalid speculative token constant");
5828 
5829  /* NO EREPORT(ERROR) from here till changes are logged */
5831 
5833 
5834  MarkBufferDirty(buffer);
5835 
5836  /*
5837  * Replace the speculative insertion token with a real t_ctid, pointing to
5838  * itself like it does on regular tuples.
5839  */
5840  htup->t_ctid = *tid;
5841 
5842  /* XLOG stuff */
5843  if (RelationNeedsWAL(relation))
5844  {
5845  xl_heap_confirm xlrec;
5846  XLogRecPtr recptr;
5847 
5848  xlrec.offnum = ItemPointerGetOffsetNumber(tid);
5849 
5850  XLogBeginInsert();
5851 
5852  /* We want the same filtering on this as on a plain insert */
5854 
5855  XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
5856  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
5857 
5858  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
5859 
5860  PageSetLSN(page, recptr);
5861  }
5862 
5863  END_CRIT_SECTION();
5864 
5865  UnlockReleaseBuffer(buffer);
5866 }
OffsetNumber offnum
Definition: heapam_xlog.h:302
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1562
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define MaxOffsetNumber
Definition: off.h:28
#define END_CRIT_SECTION()
Definition: miscadmin.h:137
#define HeapTupleHeaderIsSpeculative(tup)
Definition: htup_details.h:429
#define START_CRIT_SECTION()
Definition: miscadmin.h:135
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:248
#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:918
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3807
#define ERROR
Definition: elog.h:46
ItemPointerData t_ctid
Definition: htup_details.h:160
#define REGBUF_STANDARD
Definition: xloginsert.h:35
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:404
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define SizeOfHeapConfirm
Definition: heapam_xlog.h:305
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4023
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:697
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define RelationNeedsWAL(relation)
Definition: rel.h:570
#define elog(elevel,...)
Definition: elog.h:232
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
#define XLOG_HEAP_CONFIRM
Definition: heapam_xlog.h:37

◆ heap_freeze_tuple()

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

Definition at line 6709 of file heapam.c.

References heap_execute_freeze_tuple(), and heap_prepare_freeze_tuple().

Referenced by rewrite_heap_tuple().

6712 {
6714  bool do_freeze;
6715  bool tuple_totally_frozen;
6716 
6717  do_freeze = heap_prepare_freeze_tuple(tuple,
6718  relfrozenxid, relminmxid,
6719  cutoff_xid, cutoff_multi,
6720  &frz, &tuple_totally_frozen);
6721 
6722  /*
6723  * Note that because this is not a WAL-logged operation, we don't need to
6724  * fill in the offset in the freeze record.
6725  */
6726 
6727  if (do_freeze)
6728  heap_execute_freeze_tuple(tuple, &frz);
6729  return do_freeze;
6730 }
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:6459
void heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
Definition: heapam.c:6688

◆ heap_get_latest_tid()

void heap_get_latest_tid ( TableScanDesc  scan,
ItemPointer  tid 
)

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

1864 {
1865  Relation relation = sscan->rs_rd;
1866  Snapshot snapshot = sscan->rs_snapshot;
1867  ItemPointerData ctid;
1868  TransactionId priorXmax;
1869 
1870  /*
1871  * table_tuple_get_latest_tid() verified that the passed in tid is valid.
1872  * Assume that t_ctid links are valid however - there shouldn't be invalid
1873  * ones in the table.
1874  */
1875  Assert(ItemPointerIsValid(tid));
1876 
1877  /*
1878  * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
1879  * need to examine, and *tid is the TID we will return if ctid turns out
1880  * to be bogus.
1881  *
1882  * Note that we will loop until we reach the end of the t_ctid chain.
1883  * Depending on the snapshot passed, there might be at most one visible
1884  * version of the row, but we don't try to optimize for that.
1885  */
1886  ctid = *tid;
1887  priorXmax = InvalidTransactionId; /* cannot check first XMIN */
1888  for (;;)
1889  {
1890  Buffer buffer;
1891  Page page;
1892  OffsetNumber offnum;
1893  ItemId lp;
1894  HeapTupleData tp;
1895  bool valid;
1896 
1897  /*
1898  * Read, pin, and lock the page.
1899  */
1900  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
1901  LockBuffer(buffer, BUFFER_LOCK_SHARE);
1902  page = BufferGetPage(buffer);
1903  TestForOldSnapshot(snapshot, relation, page);
1904 
1905  /*
1906  * Check for bogus item number. This is not treated as an error
1907  * condition because it can happen while following a t_ctid link. We
1908  * just assume that the prior tid is OK and return it unchanged.
1909  */
1910  offnum = ItemPointerGetOffsetNumber(&ctid);
1911  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
1912  {
1913  UnlockReleaseBuffer(buffer);
1914  break;
1915  }
1916  lp = PageGetItemId(page, offnum);
1917  if (!ItemIdIsNormal(lp))
1918  {
1919  UnlockReleaseBuffer(buffer);
1920  break;
1921  }
1922 
1923  /* OK to access the tuple */
1924  tp.t_self = ctid;
1925  tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
1926  tp.t_len = ItemIdGetLength(lp);
1927  tp.t_tableOid = RelationGetRelid(relation);
1928 
1929  /*
1930  * After following a t_ctid link, we might arrive at an unrelated
1931  * tuple. Check for XMIN match.
1932  */
1933  if (TransactionIdIsValid(priorXmax) &&
1935  {
1936  UnlockReleaseBuffer(buffer);
1937  break;
1938  }
1939 
1940  /*
1941  * Check tuple visibility; if visible, set it as the new result
1942  * candidate.
1943  */
1944  valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
1945  HeapCheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
1946  if (valid)
1947  *tid = ctid;
1948 
1949  /*
1950  * If there's a valid t_ctid link, follow it, else we're done.
1951  */
1952  if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
1956  {
1957  UnlockReleaseBuffer(buffer);
1958  break;
1959  }
1960 
1961  ctid = tp.t_data->t_ctid;
1962  priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
1963  UnlockReleaseBuffer(buffer);
1964  } /* end of loop */
1965 }
#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:279
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
uint32 TransactionId
Definition: c.h:587
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:3807
#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:4023
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition: heapam.c:9850
#define Assert(condition)
Definition: c.h:804
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:697
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:97
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define TransactionIdIsValid(xid)
Definition: transam.h:41
int Buffer
Definition: buf.h:23
#define RelationGetRelid(relation)
Definition: rel.h:457
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
bool HeapTupleSatisfiesVisibility(HeapTuple tup, Snapshot snapshot, Buffer buffer)

◆ heap_get_root_tuples()

void heap_get_root_tuples ( Page  page,
OffsetNumber root_offsets 
)

Definition at line 900 of file pruneheap.c.

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

Referenced by heapam_index_build_range_scan(), and heapam_index_validate_scan().

901 {
902  OffsetNumber offnum,
903  maxoff;
904 
905  MemSet(root_offsets, InvalidOffsetNumber,
907 
908  maxoff = PageGetMaxOffsetNumber(page);
909  for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
910  {
911  ItemId lp = PageGetItemId(page, offnum);
912  HeapTupleHeader htup;
913  OffsetNumber nextoffnum;
914  TransactionId priorXmax;
915 
916  /* skip unused and dead items */
917  if (!ItemIdIsUsed(lp) || ItemIdIsDead(lp))
918  continue;
919 
920  if (ItemIdIsNormal(lp))
921  {
922  htup = (HeapTupleHeader) PageGetItem(page, lp);
923 
924  /*
925  * Check if this tuple is part of a HOT-chain rooted at some other
926  * tuple. If so, skip it for now; we'll process it when we find
927  * its root.
928  */
929  if (HeapTupleHeaderIsHeapOnly(htup))
930  continue;
931 
932  /*
933  * This is either a plain tuple or the root of a HOT-chain.
934  * Remember it in the mapping.
935  */
936  root_offsets[offnum - 1] = offnum;
937 
938  /* If it's not the start of a HOT-chain, we're done with it */
939  if (!HeapTupleHeaderIsHotUpdated(htup))
940  continue;
941 
942  /* Set up to scan the HOT-chain */
943  nextoffnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
944  priorXmax = HeapTupleHeaderGetUpdateXid(htup);
945  }
946  else
947  {
948  /* Must be a redirect item. We do not set its root_offsets entry */
950  /* Set up to scan the HOT-chain */
951  nextoffnum = ItemIdGetRedirect(lp);
952  priorXmax = InvalidTransactionId;
953  }
954 
955  /*
956  * Now follow the HOT-chain and collect other tuples in the chain.
957  *
958  * Note: Even though this is a nested loop, the complexity of the
959  * function is O(N) because a tuple in the page should be visited not
960  * more than twice, once in the outer loop and once in HOT-chain
961  * chases.
962  */
963  for (;;)
964  {
965  /* Sanity check */
966  if (nextoffnum < FirstOffsetNumber || nextoffnum > maxoff)
967  break;
968 
969  lp = PageGetItemId(page, nextoffnum);
970 
971  /* Check for broken chains */
972  if (!ItemIdIsNormal(lp))
973  break;
974 
975  htup = (HeapTupleHeader) PageGetItem(page, lp);
976 
977  if (TransactionIdIsValid(priorXmax) &&
978  !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(htup)))
979  break;
980 
981  /* Remember the root line pointer for this item */
982  root_offsets[nextoffnum - 1] = offnum;
983 
984  /* Advance to next chain member, if any */
985  if (!HeapTupleHeaderIsHotUpdated(htup))
986  break;
987 
988  /* HOT implies it can't have moved to different partition */
990 
991  nextoffnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
992  priorXmax = HeapTupleHeaderGetUpdateXid(htup);
993  }
994  }
995 }
#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:587
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:573
#define MemSet(start, val, len)
Definition: c.h:1008
#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:500
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:483
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
#define InvalidOffsetNumber
Definition: off.h:26
#define Assert(condition)
Definition: c.h:804
#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 1340 of file heapam.c.

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

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

1341 {
1342  HeapScanDesc scan = (HeapScanDesc) sscan;
1343 
1344  /*
1345  * This is still widely used directly, without going through table AM, so
1346  * add a safety check. It's possible we should, at a later point,
1347  * downgrade this to an assert. The reason for checking the AM routine,
1348  * rather than the AM oid, is that this allows to write regression tests
1349  * that create another AM reusing the heap handler.
1350  */
1351  if (unlikely(sscan->rs_rd->rd_tableam != GetHeapamTableAmRoutine()))
1352  ereport(ERROR,
1353  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1354  errmsg_internal("only heap AM is supported")));
1355 
1356  /*
1357  * We don't expect direct calls to heap_getnext with valid CheckXidAlive
1358  * for catalog or regular tables. See detailed comments in xact.c where
1359  * these variables are declared. Normally we have such a check at tableam
1360  * level API but this is called from many places so we need to ensure it
1361  * here.
1362  */
1364  elog(ERROR, "unexpected heap_getnext call during logical decoding");
1365 
1366  /* Note: no locking manipulations needed */
1367 
1368  if (scan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
1369  heapgettup_pagemode(scan, direction,
1370  scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
1371  else
1372  heapgettup(scan, direction,
1373  scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
1374 
1375  if (scan->rs_ctup.t_data == NULL)
1376  return NULL;
1377 
1378  /*
1379  * if we get here it means we have a new current scan tuple, so point to
1380  * the proper return buffer and return the tuple.
1381  */
1382 
1384 
1385  return &scan->rs_ctup;
1386 }
TableScanDescData rs_base
Definition: heapam.h:49
int errcode(int sqlerrcode)
Definition: elog.c:698
uint32 rs_flags
Definition: relscan.h:47
struct HeapScanDescData * HeapScanDesc
Definition: heapam.h:79
HeapTupleData rs_ctup
Definition: heapam.h:66
HeapTupleHeader t_data
Definition: htup.h:68
#define ERROR
Definition: elog.h:46
bool bsysscan
Definition: xact.c:96
struct ScanKeyData * rs_key
Definition: relscan.h:37
TransactionId CheckXidAlive
Definition: xact.c:95
static void heapgettup(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:506
#define ereport(elevel,...)
Definition: elog.h:157
int errmsg_internal(const char *fmt,...)
Definition: elog.c:996
Relation rs_rd
Definition: relscan.h:34
#define elog(elevel,...)
Definition: elog.h:232
#define unlikely(x)
Definition: c.h:273
#define pgstat_count_heap_getnext(rel)
Definition: pgstat.h:1052
#define TransactionIdIsValid(xid)
Definition: transam.h:41
static void heapgettup_pagemode(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:844
const TableAmRoutine * GetHeapamTableAmRoutine(void)

◆ heap_getnextslot()

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

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

1390 {
1391  HeapScanDesc scan = (HeapScanDesc) sscan;
1392 
1393  /* Note: no locking manipulations needed */
1394 
1395  if (sscan->rs_flags & SO_ALLOW_PAGEMODE)
1396  heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1397  else
1398  heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1399 
1400  if (scan->rs_ctup.t_data == NULL)
1401  {
1402  ExecClearTuple(slot);
1403  return false;
1404  }
1405 
1406  /*
1407  * if we get here it means we have a new current scan tuple, so point to
1408  * the proper return buffer and return the tuple.
1409  */
1410 
1412 
1413  ExecStoreBufferHeapTuple(&scan->rs_ctup, slot,
1414  scan->rs_cbuf);
1415  return true;
1416 }
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
TableScanDescData rs_base
Definition: heapam.h:49
uint32 rs_flags
Definition: relscan.h:47
struct HeapScanDescData * HeapScanDesc
Definition: heapam.h:79
HeapTupleData rs_ctup
Definition: heapam.h:66
HeapTupleHeader t_data
Definition: htup.h:68
struct ScanKeyData * rs_key
Definition: relscan.h:37
static void heapgettup(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:506
TupleTableSlot * ExecStoreBufferHeapTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer)
Definition: execTuples.c:1362
Buffer rs_cbuf
Definition: heapam.h:60
Relation rs_rd
Definition: relscan.h:34
#define pgstat_count_heap_getnext(rel)
Definition: pgstat.h:1052
static void heapgettup_pagemode(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:844

◆ heap_getnextslot_tidrange()

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

Definition at line 1492 of file heapam.c.

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

Referenced by SampleHeapTupleVisible().

1494 {
1495  HeapScanDesc scan = (HeapScanDesc) sscan;
1496  ItemPointer mintid = &sscan->rs_mintid;
1497  ItemPointer maxtid = &sscan->rs_maxtid;
1498 
1499  /* Note: no locking manipulations needed */
1500  for (;;)
1501  {
1502  if (sscan->rs_flags & SO_ALLOW_PAGEMODE)
1503  heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1504  else
1505  heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1506 
1507  if (scan->rs_ctup.t_data == NULL)
1508  {
1509  ExecClearTuple(slot);
1510  return false;
1511  }
1512 
1513  /*
1514  * heap_set_tidrange will have used heap_setscanlimits to limit the
1515  * range of pages we scan to only ones that can contain the TID range
1516  * we're scanning for. Here we must filter out any tuples from these
1517  * pages that are outwith that range.
1518  */
1519  if (ItemPointerCompare(&scan->rs_ctup.t_self, mintid) < 0)
1520  {
1521  ExecClearTuple(slot);
1522 
1523  /*
1524  * When scanning backwards, the TIDs will be in descending order.
1525  * Future tuples in this direction will be lower still, so we can
1526  * just return false to indicate there will be no more tuples.
1527  */
1528  if (ScanDirectionIsBackward(direction))
1529  return false;
1530 
1531  continue;
1532  }
1533 
1534  /*
1535  * Likewise for the final page, we must filter out TIDs greater than
1536  * maxtid.
1537  */
1538  if (ItemPointerCompare(&scan->rs_ctup.t_self, maxtid) > 0)
1539  {
1540  ExecClearTuple(slot);
1541 
1542  /*
1543  * When scanning forward, the TIDs will be in ascending order.
1544  * Future tuples in this direction will be higher still, so we can
1545  * just return false to indicate there will be no more tuples.
1546  */
1547  if (ScanDirectionIsForward(direction))
1548  return false;
1549  continue;
1550  }
1551 
1552  break;
1553  }
1554 
1555  /*
1556  * if we get here it means we have a new current scan tuple, so point to
1557  * the proper return buffer and return the tuple.
1558  */
1560 
1561  ExecStoreBufferHeapTuple(&scan->rs_ctup, slot, scan->rs_cbuf);
1562  return true;
1563 }
int32 ItemPointerCompare(ItemPointer arg1, ItemPointer arg2)
Definition: itemptr.c:52
ItemPointerData rs_mintid
Definition: relscan.h:40
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
#define ScanDirectionIsForward(direction)
Definition: sdir.h:55
TableScanDescData rs_base
Definition: heapam.h:49
uint32 rs_flags
Definition: relscan.h:47
struct HeapScanDescData * HeapScanDesc
Definition: heapam.h:79
HeapTupleData rs_ctup
Definition: heapam.h:66
HeapTupleHeader t_data
Definition: htup.h:68
#define ScanDirectionIsBackward(direction)
Definition: sdir.h:41
ItemPointerData t_self
Definition: htup.h:65
struct ScanKeyData * rs_key
Definition: relscan.h:37
ItemPointerData rs_maxtid
Definition: relscan.h:41
static void heapgettup(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:506
TupleTableSlot * ExecStoreBufferHeapTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer)
Definition: execTuples.c:1362
Buffer rs_cbuf
Definition: heapam.h:60
Relation rs_rd
Definition: relscan.h:34
#define pgstat_count_heap_getnext(rel)
Definition: pgstat.h:1052
static void heapgettup_pagemode(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition: heapam.c:844

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

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

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

1713 {
1714  Page dp = (Page) BufferGetPage(buffer);
1715  TransactionId prev_xmax = InvalidTransactionId;
1716  BlockNumber blkno;
1717  OffsetNumber offnum;
1718  bool at_chain_start;
1719  bool valid;
1720  bool skip;
1721  GlobalVisState *vistest = NULL;
1722 
1723  /* If this is not the first call, previous call returned a (live!) tuple */
1724  if (all_dead)
1725  *all_dead = first_call;
1726 
1727  blkno = ItemPointerGetBlockNumber(tid);
1728  offnum = ItemPointerGetOffsetNumber(tid);
1729  at_chain_start = first_call;
1730  skip = !first_call;
1731 
1732  /* XXX: we should assert that a snapshot is pushed or registered */
1734  Assert(BufferGetBlockNumber(buffer) == blkno);
1735 
1736  /* Scan through possible multiple members of HOT-chain */
1737  for (;;)
1738  {
1739  ItemId lp;
1740 
1741  /* check for bogus TID */
1742  if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
1743  break;
1744 
1745  lp = PageGetItemId(dp, offnum);
1746 
1747  /* check for unused, dead, or redirected items */
1748  if (!ItemIdIsNormal(lp))
1749  {
1750  /* We should only see a redirect at start of chain */
1751  if (ItemIdIsRedirected(lp) && at_chain_start)
1752  {
1753  /* Follow the redirect */
1754  offnum = ItemIdGetRedirect(lp);
1755  at_chain_start = false;
1756  continue;
1757  }
1758  /* else must be end of chain */
1759  break;
1760  }
1761 
1762  /*
1763  * Update heapTuple to point to the element of the HOT chain we're
1764  * currently investigating. Having t_self set correctly is important
1765  * because the SSI checks and the *Satisfies routine for historical
1766  * MVCC snapshots need the correct tid to decide about the visibility.
1767  */
1768  heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
1769  heapTuple->t_len = ItemIdGetLength(lp);
1770  heapTuple->t_tableOid = RelationGetRelid(relation);
1771  ItemPointerSet(&heapTuple->t_self, blkno, offnum);
1772 
1773  /*
1774  * Shouldn't see a HEAP_ONLY tuple at chain start.
1775  */
1776  if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
1777  break;
1778 
1779  /*
1780  * The xmin should match the previous xmax value, else chain is
1781  * broken.
1782  */
1783  if (TransactionIdIsValid(prev_xmax) &&
1784  !TransactionIdEquals(prev_xmax,
1785  HeapTupleHeaderGetXmin(heapTuple->t_data)))
1786  break;
1787 
1788  /*
1789  * When first_call is true (and thus, skip is initially false) we'll
1790  * return the first tuple we find. But on later passes, heapTuple
1791  * will initially be pointing to the tuple we returned last time.
1792  * Returning it again would be incorrect (and would loop forever), so
1793  * we skip it and return the next match we find.
1794  */
1795  if (!skip)
1796  {
1797  /* If it's visible per the snapshot, we must return it */
1798  valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
1799  HeapCheckForSerializableConflictOut(valid, relation, heapTuple,
1800  buffer, snapshot);
1801 
1802  if (valid)
1803  {
1804  ItemPointerSetOffsetNumber(tid, offnum);
1805  PredicateLockTID(relation, &heapTuple->t_self, snapshot,
1806  HeapTupleHeaderGetXmin(heapTuple->t_data));
1807  if (all_dead)
1808  *all_dead = false;
1809  return true;
1810  }
1811  }
1812  skip = false;
1813 
1814  /*
1815  * If we can't see it, maybe no one else can either. At caller
1816  * request, check whether all chain members are dead to all
1817  * transactions.
1818  *
1819  * Note: if you change the criterion here for what is "dead", fix the
1820  * planner's get_actual_variable_range() function to match.
1821  */
1822  if (all_dead && *all_dead)
1823  {
1824  if (!vistest)
1825  vistest = GlobalVisTestFor(relation);
1826 
1827  if (!HeapTupleIsSurelyDead(heapTuple, vistest))
1828  *all_dead = false;
1829  }
1830 
1831  /*
1832  * Check to see if HOT chain continues past this tuple; if so fetch
1833  * the next offnum and loop around.
1834  */
1835  if (HeapTupleIsHotUpdated(heapTuple))
1836  {
1838  blkno);
1839  offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
1840  at_chain_start = false;
1841  prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
1842  }
1843  else
1844  break; /* end of chain */
1845  }
1846 
1847  return false;
1848 }
#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:587
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define ItemIdGetRedirect(itemId)
Definition: itemid.h:78
static const struct exclude_list_item skip[]
Definition: pg_checksums.c:112
TransactionId RecentXmin
Definition: snapmgr.c:113
uint32 BlockNumber
Definition: block.h:31
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleIsHotUpdated(tuple)
Definition: htup_details.h:675
GlobalVisState * GlobalVisTestFor(Relation rel)
Definition: procarray.c:3963
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
ItemPointerData t_ctid
Definition: htup_details.h:160
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
#define InvalidTransactionId
Definition: transam.h:31
Oid t_tableOid
Definition: htup.h:66
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void PredicateLockTID(Relation relation, ItemPointer tid, Snapshot snapshot, TransactionId tuple_xid)
Definition: predicate.c:2614
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition: heapam.c:9850
#define HeapTupleIsHeapOnly(tuple)
Definition: htup_details.h:684
#define Assert(condition)
Definition: c.h:804
#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:2758
bool HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define TransactionIdIsValid(xid)
Definition: transam.h:41
#define RelationGetRelid(relation)
Definition: rel.h:457
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
#define ItemPointerSet(pointer, blockNumber, offNum)
Definition: itemptr.h:127
bool HeapTupleSatisfiesVisibility(HeapTuple tup, Snapshot snapshot, Buffer buffer)

◆ heap_index_delete_tuples()

TransactionId heap_index_delete_tuples ( Relation  rel,
TM_IndexDeleteOp delstate 
)

Definition at line 7326 of file heapam.c.

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

Referenced by SampleHeapTupleVisible().

7327 {
7328  /* Initial assumption is that earlier pruning took care of conflict */
7329  TransactionId latestRemovedXid = InvalidTransactionId;
7332  Page page = NULL;
7334  TransactionId priorXmax;
7335 #ifdef USE_PREFETCH
7336  IndexDeletePrefetchState prefetch_state;
7337  int prefetch_distance;
7338 #endif
7339  SnapshotData SnapshotNonVacuumable;
7340  int finalndeltids = 0,
7341  nblocksaccessed = 0;
7342 
7343  /* State that's only used in bottom-up index deletion case */
7344  int nblocksfavorable = 0;
7345  int curtargetfreespace = delstate->bottomupfreespace,
7346  lastfreespace = 0,
7347  actualfreespace = 0;
7348  bool bottomup_final_block = false;
7349 
7350  InitNonVacuumableSnapshot(SnapshotNonVacuumable, GlobalVisTestFor(rel));
7351 
7352  /* Sort caller's deltids array by TID for further processing */
7353  index_delete_sort(delstate);
7354 
7355  /*
7356  * Bottom-up case: resort deltids array in an order attuned to where the
7357  * greatest number of promising TIDs are to be found, and determine how
7358  * many blocks from the start of sorted array should be considered
7359  * favorable. This will also shrink the deltids array in order to
7360  * eliminate completely unfavorable blocks up front.
7361  */
7362  if (delstate->bottomup)
7363  nblocksfavorable = bottomup_sort_and_shrink(delstate);
7364 
7365 #ifdef USE_PREFETCH
7366  /* Initialize prefetch state. */
7367  prefetch_state.cur_hblkno = InvalidBlockNumber;
7368  prefetch_state.next_item = 0;
7369  prefetch_state.ndeltids = delstate->ndeltids;
7370  prefetch_state.deltids = delstate->deltids;
7371 
7372  /*
7373  * Determine the prefetch distance that we will attempt to maintain.
7374  *
7375  * Since the caller holds a buffer lock somewhere in rel, we'd better make
7376  * sure that isn't a catalog relation before we call code that does
7377  * syscache lookups, to avoid risk of deadlock.
7378  */
7379  if (IsCatalogRelation(rel))
7380  prefetch_distance = maintenance_io_concurrency;
7381  else
7382  prefetch_distance =
7384 
7385  /* Cap initial prefetch distance for bottom-up deletion caller */
7386  if (delstate->bottomup)
7387  {
7388  Assert(nblocksfavorable >= 1);
7389  Assert(nblocksfavorable <= BOTTOMUP_MAX_NBLOCKS);
7390  prefetch_distance = Min(prefetch_distance, nblocksfavorable);
7391  }
7392 
7393  /* Start prefetching. */
7394  index_delete_prefetch_buffer(rel, &prefetch_state, prefetch_distance);
7395 #endif
7396 
7397  /* Iterate over deltids, determine which to delete, check their horizon */
7398  Assert(delstate->ndeltids > 0);
7399  for (int i = 0; i < delstate->ndeltids; i++)
7400  {
7401  TM_IndexDelete *ideltid = &delstate->deltids[i];
7402  TM_IndexStatus *istatus = delstate->status + ideltid->id;
7403  ItemPointer htid = &ideltid->tid;
7404  OffsetNumber offnum;
7405 
7406  /*
7407  * Read buffer, and perform required extra steps each time a new block
7408  * is encountered. Avoid refetching if it's the same block as the one
7409  * from the last htid.
7410  */
7411  if (blkno == InvalidBlockNumber ||
7412  ItemPointerGetBlockNumber(htid) != blkno)
7413  {
7414  /*
7415  * Consider giving up early for bottom-up index deletion caller
7416  * first. (Only prefetch next-next block afterwards, when it
7417  * becomes clear that we're at least going to access the next
7418  * block in line.)
7419  *
7420  * Sometimes the first block frees so much space for bottom-up
7421  * caller that the deletion process can end without accessing any
7422  * more blocks. It is usually necessary to access 2 or 3 blocks
7423  * per bottom-up deletion operation, though.
7424  */
7425  if (delstate->bottomup)
7426  {
7427  /*
7428  * We often allow caller to delete a few additional items
7429  * whose entries we reached after the point that space target
7430  * from caller was satisfied. The cost of accessing the page
7431  * was already paid at that point, so it made sense to finish
7432  * it off. When that happened, we finalize everything here
7433  * (by finishing off the whole bottom-up deletion operation
7434  * without needlessly paying the cost of accessing any more
7435  * blocks).
7436  */
7437  if (bottomup_final_block)
7438  break;
7439 
7440  /*
7441  * Give up when we didn't enable our caller to free any
7442  * additional space as a result of processing the page that we
7443  * just finished up with. This rule is the main way in which
7444  * we keep the cost of bottom-up deletion under control.
7445  */
7446  if (nblocksaccessed >= 1 && actualfreespace == lastfreespace)
7447  break;
7448  lastfreespace = actualfreespace; /* for next time */
7449 
7450  /*
7451  * Deletion operation (which is bottom-up) will definitely
7452  * access the next block in line. Prepare for that now.
7453  *
7454  * Decay target free space so that we don't hang on for too
7455  * long with a marginal case. (Space target is only truly
7456  * helpful when it allows us to recognize that we don't need
7457  * to access more than 1 or 2 blocks to satisfy caller due to
7458  * agreeable workload characteristics.)
7459  *
7460  * We are a bit more patient when we encounter contiguous
7461  * blocks, though: these are treated as favorable blocks. The
7462  * decay process is only applied when the next block in line
7463  * is not a favorable/contiguous block. This is not an
7464  * exception to the general rule; we still insist on finding
7465  * at least one deletable item per block accessed. See
7466  * bottomup_nblocksfavorable() for full details of the theory
7467  * behind favorable blocks and heap block locality in general.
7468  *
7469  * Note: The first block in line is always treated as a
7470  * favorable block, so the earliest possible point that the
7471  * decay can be applied is just before we access the second
7472  * block in line. The Assert() verifies this for us.
7473  */
7474  Assert(nblocksaccessed > 0 || nblocksfavorable > 0);
7475  if (nblocksfavorable > 0)
7476  nblocksfavorable--;
7477  else
7478  curtargetfreespace /= 2;
7479  }
7480 
7481  /* release old buffer */
7482  if (BufferIsValid(buf))
7483  UnlockReleaseBuffer(buf);
7484 
7485  blkno = ItemPointerGetBlockNumber(htid);
7486  buf = ReadBuffer(rel, blkno);
7487  nblocksaccessed++;
7488  Assert(!delstate->bottomup ||
7489  nblocksaccessed <= BOTTOMUP_MAX_NBLOCKS);
7490 
7491 #ifdef USE_PREFETCH
7492 
7493  /*
7494  * To maintain the prefetch distance, prefetch one more page for
7495  * each page we read.
7496  */
7497  index_delete_prefetch_buffer(rel, &prefetch_state, 1);
7498 #endif
7499 
7501 
7502  page = BufferGetPage(buf);
7503  maxoff = PageGetMaxOffsetNumber(page);
7504  }
7505 
7506  if (istatus->knowndeletable)
7507  Assert(!delstate->bottomup && !istatus->promising);
7508  else
7509  {
7510  ItemPointerData tmp = *htid;
7511  HeapTupleData heapTuple;
7512 
7513  /* Are any tuples from this HOT chain non-vacuumable? */
7514  if (heap_hot_search_buffer(&tmp, rel, buf, &SnapshotNonVacuumable,
7515  &heapTuple, NULL, true))
7516  continue; /* can't delete entry */
7517 
7518  /* Caller will delete, since whole HOT chain is vacuumable */
7519  istatus->knowndeletable = true;
7520 
7521  /* Maintain index free space info for bottom-up deletion case */
7522  if (delstate->bottomup)
7523  {
7524  Assert(istatus->freespace > 0);
7525  actualfreespace += istatus->freespace;
7526  if (actualfreespace >= curtargetfreespace)
7527  bottomup_final_block = true;
7528  }
7529  }
7530 
7531  /*
7532  * Maintain latestRemovedXid value for deletion operation as a whole
7533  * by advancing current value using heap tuple headers. This is
7534  * loosely based on the logic for pruning a HOT chain.
7535  */
7536  offnum = ItemPointerGetOffsetNumber(htid);
7537  priorXmax = InvalidTransactionId; /* cannot check first XMIN */
7538  for (;;)
7539  {
7540  ItemId lp;
7541  HeapTupleHeader htup;
7542 
7543  /* Some sanity checks */
7544  if (offnum < FirstOffsetNumber || offnum > maxoff)
7545  {
7546  Assert(false);
7547  break;
7548  }
7549 
7550  lp = PageGetItemId(page, offnum);
7551  if (ItemIdIsRedirected(lp))
7552  {
7553  offnum = ItemIdGetRedirect(lp);
7554  continue;
7555  }
7556 
7557  /*
7558  * We'll often encounter LP_DEAD line pointers (especially with an
7559  * entry marked knowndeletable by our caller up front). No heap
7560  * tuple headers get examined for an htid that leads us to an
7561  * LP_DEAD item. This is okay because the earlier pruning
7562  * operation that made the line pointer LP_DEAD in the first place
7563  * must have considered the original tuple header as part of
7564  * generating its own latestRemovedXid value.
7565  *
7566  * Relying on XLOG_HEAP2_PRUNE records like this is the same
7567  * strategy that index vacuuming uses in all cases. Index VACUUM
7568  * WAL records don't even have a latestRemovedXid field of their
7569  * own for this reason.
7570  */
7571  if (!ItemIdIsNormal(lp))
7572  break;
7573 
7574  htup = (HeapTupleHeader) PageGetItem(page, lp);
7575 
7576  /*
7577  * Check the tuple XMIN against prior XMAX, if any
7578  */
7579  if (TransactionIdIsValid(priorXmax) &&
7580  !TransactionIdEquals(HeapTupleHeaderGetXmin(htup), priorXmax))
7581  break;
7582 
7583  HeapTupleHeaderAdvanceLatestRemovedXid(htup, &latestRemovedXid);
7584 
7585  /*
7586  * If the tuple is not HOT-updated, then we are at the end of this
7587  * HOT-chain. No need to visit later tuples from the same update
7588  * chain (they get their own index entries) -- just move on to
7589  * next htid from index AM caller.
7590  */
7591  if (!HeapTupleHeaderIsHotUpdated(htup))
7592  break;
7593 
7594  /* Advance to next HOT chain member */
7595  Assert(ItemPointerGetBlockNumber(&htup->t_ctid) == blkno);
7596  offnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
7597  priorXmax = HeapTupleHeaderGetUpdateXid(htup);
7598  }
7599 
7600  /* Enable further/final shrinking of deltids for caller */
7601  finalndeltids = i + 1;
7602  }
7603 
7604  UnlockReleaseBuffer(buf);
7605 
7606  /*
7607  * Shrink deltids array to exclude non-deletable entries at the end. This
7608  * is not just a minor optimization. Final deltids array size might be
7609  * zero for a bottom-up caller. Index AM is explicitly allowed to rely on
7610  * ndeltids being zero in all cases with zero total deletable entries.
7611  */
7612  Assert(finalndeltids > 0 || delstate->bottomup);
7613  delstate->ndeltids = finalndeltids;
7614 
7615  return latestRemovedXid;
7616 }
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:365
void HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple, TransactionId *latestRemovedXid)
Definition: heapam.c:7236
TM_IndexDelete * deltids
Definition: tableam.h:228
bool IsCatalogRelation(Relation relation)
Definition: catalog.c:103
int maintenance_io_concurrency
Definition: bufmgr.c:150
#define ItemIdIsRedirected(itemId)
Definition: itemid.h:106
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
uint32 TransactionId
Definition: c.h:587
static int bottomup_sort_and_shrink(TM_IndexDeleteOp *delstate)
Definition: heapam.c:7871
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define ItemIdGetRedirect(itemId)
Definition: itemid.h:78
#define Min(x, y)
Definition: c.h:986
#define InvalidBuffer
Definition: buf.h:25
bool knowndeletable
Definition: tableam.h:196
#define InitNonVacuumableSnapshot(snapshotdata, vistestp)
Definition: snapmgr.h:82
uint32 BlockNumber
Definition: block.h:31
Form_pg_class rd_rel
Definition: rel.h:110
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
bool heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, bool *all_dead, bool first_call)
Definition: heapam.c:1710
GlobalVisState * GlobalVisTestFor(Relation rel)
Definition: procarray.c:3963
int get_tablespace_maintenance_io_concurrency(Oid spcid)
Definition: spccache.c:228
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3807
ItemPointerData t_ctid
Definition: htup_details.h:160
static char * buf
Definition: pg_test_fsync.c:68
#define BOTTOMUP_MAX_NBLOCKS
Definition: heapam.c:185
#define InvalidTransactionId
Definition: transam.h:31
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
bool promising
Definition: tableam.h:199
#define HeapTupleHeaderIsHotUpdated(tup)
Definition: htup_details.h:483
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
TM_IndexStatus * status
Definition: tableam.h:229
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4023
ItemPointerData tid
Definition: tableam.h:189
#define InvalidOffsetNumber
Definition: off.h:26
static void index_delete_sort(TM_IndexDeleteOp *delstate)
Definition: heapam.c:7658
#define Assert(condition)
Definition: c.h:804
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:697
#define InvalidBlockNumber
Definition: block.h:33
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
int16 freespace
Definition: tableam.h:200
int i
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:97
int bottomupfreespace
Definition: tableam.h:224
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define TransactionIdIsValid(xid)
Definition: transam.h:41
int Buffer
Definition: buf.h:23
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78

◆ heap_inplace_update()

void heap_inplace_update ( Relation  relation,
HeapTuple  tuple 
)

Definition at line 6048 of file heapam.c.

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

Referenced by create_toast_table(), index_update_stats(), truncate_update_partedrel_stats(), vac_update_datfrozenxid(), and vac_update_relstats().

6049 {
6050  Buffer buffer;
6051  Page page;
6052  OffsetNumber offnum;
6053  ItemId lp = NULL;
6054  HeapTupleHeader htup;
6055  uint32 oldlen;
6056  uint32 newlen;
6057 
6058  /*
6059  * For now, we don't allow parallel updates. Unlike a regular update,
6060  * this should never create a combo CID, so it might be possible to relax
6061  * this restriction, but not without more thought and testing. It's not
6062  * clear that it would be useful, anyway.
6063  */
6064  if (IsInParallelMode())
6065  ereport(ERROR,
6066  (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
6067  errmsg("cannot update tuples during a parallel operation")));
6068 
6069  buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
6071  page = (Page) BufferGetPage(buffer);
6072 
6073  offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
6074  if (PageGetMaxOffsetNumber(page) >= offnum)
6075  lp = PageGetItemId(page, offnum);
6076 
6077  if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
6078  elog(ERROR, "invalid lp");
6079 
6080  htup = (HeapTupleHeader) PageGetItem(page, lp);
6081 
6082  oldlen = ItemIdGetLength(lp) - htup->t_hoff;
6083  newlen = tuple->t_len - tuple->t_data->t_hoff;
6084  if (oldlen != newlen || htup->t_hoff != tuple->t_data->t_hoff)
6085  elog(ERROR, "wrong tuple length");
6086 
6087  /* NO EREPORT(ERROR) from here till changes are logged */
6089 
6090  memcpy((char *) htup + htup->t_hoff,
6091  (char *) tuple->t_data + tuple->t_data->t_hoff,
6092  newlen);
6093 
6094  MarkBufferDirty(buffer);
6095 
6096  /* XLOG stuff */
6097  if (RelationNeedsWAL(relation))
6098  {
6099  xl_heap_inplace xlrec;
6100  XLogRecPtr recptr;
6101 
6102  xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
6103 
6104  XLogBeginInsert();
6105  XLogRegisterData((char *) &xlrec, SizeOfHeapInplace);
6106 
6107  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
6108  XLogRegisterBufData(0, (char *) htup + htup->t_hoff, newlen);
6109 
6110  /* inplace updates aren't decoded atm, don't log the origin */
6111 
6112  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_INPLACE);
6113 
6114  PageSetLSN(page, recptr);
6115  }
6116 
6117  END_CRIT_SECTION();
6118 
6119  UnlockReleaseBuffer(buffer);
6120 
6121  /*
6122  * Send out shared cache inval if necessary. Note that because we only
6123  * pass the new version of the tuple, this mustn't be used for any
6124  * operations that could change catcache lookup keys. But we aren't
6125  * bothering with index updates either, so that's true a fortiori.
6126  */
6128  CacheInvalidateHeapTuple(relation, tuple, NULL);
6129 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:368
void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple)
Definition: inval.c:1122
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1562
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define END_CRIT_SECTION()
Definition: miscadmin.h:137
#define SizeOfHeapInplace
Definition: heapam_xlog.h:314
#define START_CRIT_SECTION()
Definition: miscadmin.h:135
int errcode(int sqlerrcode)
Definition: elog.c:698
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
HeapTupleHeader t_data
Definition: htup.h:68
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
bool IsInParallelMode(void)
Definition: xact.c:1012
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3807
#define ERROR
Definition: elog.h:46
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:441
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
OffsetNumber offnum
Definition: heapam_xlog.h:310
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4023
#define ereport(elevel,...)
Definition: elog.h:157
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:697
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define XLOG_HEAP_INPLACE
Definition: heapam_xlog.h:39
#define RelationNeedsWAL(relation)
Definition: rel.h:570
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:394
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define elog(elevel,...)
Definition: elog.h:232
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78

◆ heap_insert()

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

Definition at line 2060 of file heapam.c.

References Assert, BufferGetBlockNumber(), BufferGetPage, CacheInvalidateHeapTuple(), CheckForSerializableConflictIn(), END_CRIT_SECTION, FirstOffsetNumber, xl_heap_insert::flags, GetCurrentTransactionId(), heap_freetuple(), HEAP_INSERT_FROZEN, HEAP_INSERT_NO_LOGICAL, HEAP_INSERT_SPECULATIVE, heap_prepare_insert(), InvalidBlockNumber, InvalidBuffer, InvalidTransactionId, InvalidXLogRecPtr, IsToastRelation(), ItemPointerGetBlockNumber, ItemPointerGetOffsetNumber, log_heap_new_cid(), MarkBufferDirty(), xl_heap_insert::offnum, PageClearAllVisible, PageGetMaxOffsetNumber, PageIsAllVisible, PageSetAllVisible, 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_ALL_FROZEN, VISIBILITYMAP_ALL_VISIBLE, visibilitymap_clear(), visibilitymap_get_status(), visibilitymap_pin_ok(), visibilitymap_set(), VISIBILITYMAP_VALID_BITS, XLH_INSERT_ALL_FROZEN_SET, XLH_INSERT_ALL_VISIBLE_CLEARED, XLH_INSERT_CONTAINS_NEW_TUPLE, XLH_INSERT_IS_SPECULATIVE, XLH_INSERT_ON_TOAST_RELATION, XLOG_HEAP_INIT_PAGE, XLOG_HEAP_INSERT, XLOG_INCLUDE_ORIGIN, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), XLogRegisterData(), and XLogSetRecordFlags().

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

2062 {
2064  HeapTuple heaptup;
2065  Buffer buffer;
2066  Page page = NULL;
2067  Buffer vmbuffer = InvalidBuffer;
2068  bool starting_with_empty_page;
2069  bool all_visible_cleared = false;
2070  bool all_frozen_set = false;
2071  uint8 vmstatus = 0;
2072 
2073  /*
2074  * Fill in tuple header fields and toast the tuple if necessary.
2075  *
2076  * Note: below this point, heaptup is the data we actually intend to store
2077  * into the relation; tup is the caller's original untoasted data.
2078  */
2079  heaptup = heap_prepare_insert(relation, tup, xid, cid, options);
2080 
2081  /*
2082  * Find buffer to insert this tuple into. If the page is all visible,
2083  * this will also pin the requisite visibility map page.
2084  *
2085  * Also pin visibility map page if COPY FREEZE inserts tuples into an
2086  * empty page. See all_frozen_set below.
2087  */
2088  buffer = RelationGetBufferForTuple(relation, heaptup->t_len,
2089  InvalidBuffer, options, bistate,
2090  &vmbuffer, NULL);
2091 
2092 
2093  /*
2094  * If we're inserting frozen entry into an empty page,
2095  * set visibility map bits and PageAllVisible() hint.
2096  *
2097  * If we're inserting frozen entry into already all_frozen page,
2098  * preserve this state.
2099  */
2101  {
2102  page = BufferGetPage(buffer);
2103 
2104  starting_with_empty_page = PageGetMaxOffsetNumber(page) == 0;
2105 
2106  if (visibilitymap_pin_ok(BufferGetBlockNumber(buffer), vmbuffer))
2107  vmstatus = visibilitymap_get_status(relation,
2108  BufferGetBlockNumber(buffer), &vmbuffer);
2109 
2110  if ((starting_with_empty_page || vmstatus & VISIBILITYMAP_ALL_FROZEN))
2111  all_frozen_set = true;
2112  }
2113 
2114  /*
2115  * We're about to do the actual insert -- but check for conflict first, to
2116  * avoid possibly having to roll back work we've just done.
2117  *
2118  * This is safe without a recheck as long as there is no possibility of
2119  * another process scanning the page between this check and the insert
2120  * being visible to the scan (i.e., an exclusive buffer content lock is
2121  * continuously held from this point until the tuple insert is visible).
2122  *
2123  * For a heap insert, we only need to check for table-level SSI locks. Our
2124  * new tuple can't possibly conflict with existing tuple locks, and heap
2125  * page locks are only consolidated versions of tuple locks; they do not
2126  * lock "gaps" as index page locks do. So we don't need to specify a
2127  * buffer when making the call, which makes for a faster check.
2128  */
2130 
2131  /* NO EREPORT(ERROR) from here till changes are logged */
2133 
2134  RelationPutHeapTuple(relation, buffer, heaptup,
2135  (options & HEAP_INSERT_SPECULATIVE) != 0);
2136 
2137  /*
2138  * If the page is all visible, need to clear that, unless we're only
2139  * going to add further frozen rows to it.
2140  *
2141  * If we're only adding already frozen rows to a page that was empty or
2142  * marked as all visible, mark it as all-visible.
2143  */
2144  if (PageIsAllVisible(BufferGetPage(buffer)) && !(options & HEAP_INSERT_FROZEN))
2145  {
2146  all_visible_cleared = true;
2148  visibilitymap_clear(relation,
2149  ItemPointerGetBlockNumber(&(heaptup->t_self)),
2150  vmbuffer, VISIBILITYMAP_VALID_BITS);
2151  }
2152  else if (all_frozen_set)
2153  {
2154  /* We only ever set all_frozen_set after reading the page. */
2155  Assert(page);
2156 
2157  PageSetAllVisible(page);
2158  }
2159 
2160  /*
2161  * XXX Should we set PageSetPrunable on this page ?
2162  *
2163  * The inserting transaction may eventually abort thus making this tuple
2164  * DEAD and hence available for pruning. Though we don't want to optimize
2165  * for aborts, if no other tuple in this page is UPDATEd/DELETEd, the
2166  * aborted tuple will never be pruned until next vacuum is triggered.
2167  *
2168  * If you do add PageSetPrunable here, add it in heap_xlog_insert too.
2169  */
2170 
2171  MarkBufferDirty(buffer);
2172 
2173  /* XLOG stuff */
2174  if (RelationNeedsWAL(relation))
2175  {
2176  xl_heap_insert xlrec;
2177  xl_heap_header xlhdr;
2178  XLogRecPtr recptr;
2179  Page page = BufferGetPage(buffer);
2180  uint8 info = XLOG_HEAP_INSERT;
2181  int bufflags = 0;
2182 
2183  /*
2184  * If this is a catalog, we need to transmit combo CIDs to properly
2185  * decode, so log that as well.
2186  */
2188  log_heap_new_cid(relation, heaptup);
2189 
2190  /*
2191  * If this is the single and first tuple on page, we can reinit the
2192  * page instead of restoring the whole thing. Set flag, and hide
2193  * buffer references from XLogInsert.
2194  */
2195  if (ItemPointerGetOffsetNumber(&(heaptup->t_self)) == FirstOffsetNumber &&
2197  {
2198  info |= XLOG_HEAP_INIT_PAGE;
2199  bufflags |= REGBUF_WILL_INIT;
2200  }
2201 
2202  xlrec.offnum = ItemPointerGetOffsetNumber(&heaptup->t_self);
2203  xlrec.flags = 0;
2204  if (all_visible_cleared)
2206  if (all_frozen_set)
2211 
2212  /*
2213  * For logical decoding, we need the tuple even if we're doing a full
2214  * page write, so make sure it's included even if we take a full-page
2215  * image. (XXX We could alternatively store a pointer into the FPW).
2216  */
2217  if (RelationIsLogicallyLogged(relation) &&
2219  {
2221  bufflags |= REGBUF_KEEP_DATA;
2222 
2223  if (IsToastRelation(relation))
2225  }
2226 
2227  XLogBeginInsert();
2228  XLogRegisterData((char *) &xlrec, SizeOfHeapInsert);
2229 
2230  xlhdr.t_infomask2 = heaptup->t_data->t_infomask2;
2231  xlhdr.t_infomask = heaptup->t_data->t_infomask;
2232  xlhdr.t_hoff = heaptup->t_data->t_hoff;
2233 
2234  /*
2235  * note we mark xlhdr as belonging to buffer; if XLogInsert decides to
2236  * write the whole page to the xlog, we don't need to store
2237  * xl_heap_header in the xlog.
2238  */
2239  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
2240  XLogRegisterBufData(0, (char *) &xlhdr, SizeOfHeapHeader);
2241  /* PG73FORMAT: write bitmap [+ padding] [+ oid] + data */
2243  (char *) heaptup->t_data + SizeofHeapTupleHeader,
2244  heaptup->t_len - SizeofHeapTupleHeader);
2245 
2246  /* filtering by origin on a row level is much more efficient */
2248 
2249  recptr = XLogInsert(RM_HEAP_ID, info);
2250 
2251  PageSetLSN(page, recptr);
2252  }
2253 
2254  END_CRIT_SECTION();
2255 
2256  /*
2257  * If we've frozen everything on the page, update the visibilitymap.
2258  * We're already holding pin on the vmbuffer.
2259  *
2260  * No need to update the visibilitymap if it had all_frozen bit set
2261  * before this insertion.
2262  */
2263  if (all_frozen_set && ((vmstatus & VISIBILITYMAP_ALL_FROZEN) == 0))
2264  {
2265  Assert(PageIsAllVisible(page));
2266  Assert(visibilitymap_pin_ok(BufferGetBlockNumber(buffer), vmbuffer));
2267 
2268  /*
2269  * It's fine to use InvalidTransactionId here - this is only used
2270  * when HEAP_INSERT_FROZEN is specified, which intentionally
2271  * violates visibility rules.
2272  */
2273  visibilitymap_set(relation, BufferGetBlockNumber(buffer), buffer,
2274  InvalidXLogRecPtr, vmbuffer,
2276  VISIBILITYMAP_ALL_VISIBLE | VISIBILITYMAP_ALL_FROZEN);
2277  }
2278 
2279  UnlockReleaseBuffer(buffer);
2280  if (vmbuffer != InvalidBuffer)
2281  ReleaseBuffer(vmbuffer);
2282 
2283  /*
2284  * If tuple is cachable, mark it for invalidation from the caches in case
2285  * we abort. Note it is OK to do this after releasing the buffer, because
2286  * the heaptup data structure is all in local memory, not in the shared
2287  * buffer.
2288  */
2289  CacheInvalidateHeapTuple(relation, heaptup, NULL);
2290 
2291  /* Note: speculative insertions are counted too, even if aborted later */
2292  pgstat_count_heap_insert(relation, 1);
2293 
2294  /*
2295  * If heaptup is a private copy, release it. Don't forget to copy t_self
2296  * back to the caller's image, too.
2297  */
2298  if (heaptup != tup)
2299  {
2300  tup->t_self = heaptup->t_self;
2301  heap_freetuple(heaptup);
2302  }
2303 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:368
#define SizeofHeapTupleHeader
Definition: htup_details.h:184
bool IsToastRelation(Relation relation)
Definition: catalog.c:145
#define XLOG_HEAP_INSERT
Definition: heapam_xlog.h:32
static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup)
Definition: heapam.c:8285
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple)
Definition: inval.c:1122
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid, CommandId cid, int options)
Definition: heapam.c:2312
#define XLH_INSERT_ALL_FROZEN_SET
Definition: heapam_xlog.h:73
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:587
#define HEAP_INSERT_FROZEN
Definition: heapam.h:35
void visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf, XLogRecPtr recptr, Buffer vmBuf, TransactionId cutoff_xid, uint8 flags)
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1562
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
#define VISIBILITYMAP_ALL_FROZEN
Definition: visibilitymap.h:27
#define END_CRIT_SECTION()
Definition: miscadmin.h:137
unsigned char uint8
Definition: c.h:439
#define XLH_INSERT_IS_SPECULATIVE
Definition: heapam_xlog.h:68
#define InvalidBuffer
Definition: buf.h:25
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33
uint16 t_infomask2
Definition: heapam_xlog.h:146
#define START_CRIT_SECTION()
Definition: miscadmin.h:135
#define XLH_INSERT_ON_TOAST_RELATION
Definition: heapam_xlog.h:70
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:248
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3784
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:643
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:3807
#define XLH_INSERT_CONTAINS_NEW_TUPLE
Definition: heapam_xlog.h:69
ItemPointerData t_self
Definition: htup.h:65
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:438
uint32 t_len
Definition: htup.h:64
#define PageSetAllVisible(page)
Definition: bufpage.h:387
#define 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:333
#define InvalidTransactionId
Definition: transam.h:31
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:404
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
#define RelationIsAccessibleInLogicalDecoding(relation)
Definition: rel.h:626
#define REGBUF_KEEP_DATA
Definition: xloginsert.h:38
void CheckForSerializableConflictIn(Relation relation, ItemPointer tid, BlockNumber blkno)
Definition: predicate.c:4446
#define PageClearAllVisible(page)
Definition: bufpage.h:389
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
uint16 t_infomask
Definition: heapam_xlog.h:147
#define InvalidBlockNumber
Definition: block.h:33
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define RelationNeedsWAL(relation)
Definition: rel.h:570
#define VISIBILITYMAP_ALL_VISIBLE
Definition: visibilitymap.h:26
#define SizeOfHeapInsert
Definition: heapam_xlog.h:162
#define XLH_INSERT_ALL_VISIBLE_CLEARED
Definition: heapam_xlog.h:66
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2758
void pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
Definition: pgstat.c:2217
#define HEAP_INSERT_NO_LOGICAL
Definition: heapam.h:36
uint8 visibilitymap_get_status(Relation rel, BlockNumber heapBlk, Buffer *buf)
bool visibilitymap_pin_ok(BlockNumber heapBlk, Buffer buf)
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
OffsetNumber offnum
Definition: heapam_xlog.h:156
#define SizeOfHeapHeader
Definition: heapam_xlog.h:151
Pointer Page
Definition: bufpage.h:78

◆ heap_lock_tuple()

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

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

4308 {
4309  TM_Result result;
4310  ItemPointer tid = &(tuple->t_self);
4311  ItemId lp;
4312  Page page;
4313  Buffer vmbuffer = InvalidBuffer;
4314  BlockNumber block;
4315  TransactionId xid,
4316  xmax;
4317  uint16 old_infomask,
4318  new_infomask,
4319  new_infomask2;
4320  bool first_time = true;
4321  bool skip_tuple_lock = false;
4322  bool have_tuple_lock = false;
4323  bool cleared_all_frozen = false;
4324 
4325  *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
4326  block = ItemPointerGetBlockNumber(tid);
4327 
4328  /*
4329  * Before locking the buffer, pin the visibility map page if it appears to
4330  * be necessary. Since we haven't got the lock yet, someone else might be
4331  * in the middle of changing this, so we'll need to recheck after we have
4332  * the lock.
4333  */
4334  if (PageIsAllVisible(BufferGetPage(*buffer)))
4335  visibilitymap_pin(relation, block, &vmbuffer);
4336 
4338 
4339  page = BufferGetPage(*buffer);
4340  lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
4341  Assert(ItemIdIsNormal(lp));
4342 
4343  tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
4344  tuple->t_len = ItemIdGetLength(lp);
4345  tuple->t_tableOid = RelationGetRelid(relation);
4346 
4347 l3:
4348  result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
4349 
4350  if (result == TM_Invisible)
4351  {
4352  /*
4353  * This is possible, but only when locking a tuple for ON CONFLICT
4354  * UPDATE. We return this value here rather than throwing an error in
4355  * order to give that case the opportunity to throw a more specific
4356  * error.
4357  */
4358  result = TM_Invisible;
4359  goto out_locked;
4360  }
4361  else if (result == TM_BeingModified ||
4362  result == TM_Updated ||
4363  result == TM_Deleted)
4364  {
4365  TransactionId xwait;
4366  uint16 infomask;
4367  uint16 infomask2;
4368  bool require_sleep;
4369  ItemPointerData t_ctid;
4370 
4371  /* must copy state data before unlocking buffer */
4372  xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
4373  infomask = tuple->t_data->t_infomask;
4374  infomask2 = tuple->t_data->t_infomask2;
4375  ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
4376 
4377  LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
4378 
4379  /*
4380  * If any subtransaction of the current top transaction already holds
4381  * a lock as strong as or stronger than what we're requesting, we
4382  * effectively hold the desired lock already. We *must* succeed
4383  * without trying to take the tuple lock, else we will deadlock
4384  * against anyone wanting to acquire a stronger lock.
4385  *
4386  * Note we only do this the first time we loop on the HTSU result;
4387  * there is no point in testing in subsequent passes, because
4388  * evidently our own transaction cannot have acquired a new lock after
4389  * the first time we checked.
4390  */
4391  if (first_time)
4392  {
4393  first_time = false;
4394 
4395  if (infomask & HEAP_XMAX_IS_MULTI)
4396  {
4397  int i;
4398  int nmembers;
4399  MultiXactMember *members;
4400 
4401  /*
4402  * We don't need to allow old multixacts here; if that had
4403  * been the case, HeapTupleSatisfiesUpdate would have returned
4404  * MayBeUpdated and we wouldn't be here.
4405  */
4406  nmembers =
4407  GetMultiXactIdMembers(xwait, &members, false,
4408  HEAP_XMAX_IS_LOCKED_ONLY(infomask));
4409 
4410  for (i = 0; i < nmembers; i++)
4411  {
4412  /* only consider members of our own transaction */
4413  if (!TransactionIdIsCurrentTransactionId(members[i].xid))
4414  continue;
4415 
4416  if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
4417  {
4418  pfree(members);
4419  result = TM_Ok;
4420  goto out_unlocked;
4421  }
4422  else
4423  {
4424  /*
4425  * Disable acquisition of the heavyweight tuple lock.
4426  * Otherwise, when promoting a weaker lock, we might
4427  * deadlock with another locker that has acquired the
4428  * heavyweight tuple lock and is waiting for our
4429  * transaction to finish.
4430  *
4431  * Note that in this case we still need to wait for
4432  * the multixact if required, to avoid acquiring
4433  * conflicting locks.
4434  */
4435  skip_tuple_lock = true;
4436  }
4437  }
4438 
4439  if (members)
4440  pfree(members);
4441  }
4442  else if (TransactionIdIsCurrentTransactionId(xwait))
4443  {
4444  switch (mode)
4445  {
4446  case LockTupleKeyShare:
4447  Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
4448  HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
4449  HEAP_XMAX_IS_EXCL_LOCKED(infomask));
4450  result = TM_Ok;
4451  goto out_unlocked;
4452  case LockTupleShare:
4453  if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
4454  HEAP_XMAX_IS_EXCL_LOCKED(infomask))
4455  {
4456  result = TM_Ok;
4457  goto out_unlocked;
4458  }
4459  break;
4461  if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
4462  {
4463  result = TM_Ok;
4464  goto out_unlocked;
4465  }
4466  break;
4467  case LockTupleExclusive:
4468  if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
4469  infomask2 & HEAP_KEYS_UPDATED)
4470  {
4471  result = TM_Ok;
4472  goto out_unlocked;
4473  }
4474  break;
4475  }
4476  }
4477  }
4478 
4479  /*
4480  * Initially assume that we will have to wait for the locking
4481  * transaction(s) to finish. We check various cases below in which
4482  * this can be turned off.
4483  */
4484  require_sleep = true;
4485  if (mode == LockTupleKeyShare)
4486  {
4487  /*
4488  * If we're requesting KeyShare, and there's no update present, we
4489  * don't need to wait. Even if there is an update, we can still
4490  * continue if the key hasn't been modified.
4491  *
4492  * However, if there are updates, we need to walk the update chain
4493  * to mark future versions of the row as locked, too. That way,
4494  * if somebody deletes that future version, we're protected
4495  * against the key going away. This locking of future versions
4496  * could block momentarily, if a concurrent transaction is
4497  * deleting a key; or it could return a value to the effect that
4498  * the transaction deleting the key has already committed. So we
4499  * do this before re-locking the buffer; otherwise this would be
4500  * prone to deadlocks.
4501  *
4502  * Note that the TID we're locking was grabbed before we unlocked
4503  * the buffer. For it to change while we're not looking, the
4504  * other properties we're testing for below after re-locking the
4505  * buffer would also change, in which case we would restart this
4506  * loop above.
4507  */
4508  if (!(infomask2 & HEAP_KEYS_UPDATED))
4509  {
4510  bool updated;
4511 
4512  updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
4513 
4514  /*
4515  * If there are updates, follow the update chain; bail out if
4516  * that cannot be done.
4517  */
4518  if (follow_updates && updated)
4519  {
4520  TM_Result res;
4521 
4522  res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
4524  mode);
4525  if (res != TM_Ok)
4526  {
4527  result = res;
4528  /* recovery code expects to have buffer lock held */
4530  goto failed;
4531  }
4532  }
4533 
4535 
4536  /*
4537  * Make sure it's still an appropriate lock, else start over.
4538  * Also, if it wasn't updated before we released the lock, but
4539  * is updated now, we start over too; the reason is that we
4540  * now need to follow the update chain to lock the new
4541  * versions.
4542  */
4543  if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
4544  ((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
4545  !updated))
4546  goto l3;
4547 
4548  /* Things look okay, so we can skip sleeping */
4549  require_sleep = false;
4550 
4551  /*
4552  * Note we allow Xmax to change here; other updaters/lockers
4553  * could have modified it before we grabbed the buffer lock.
4554  * However, this is not a problem, because with the recheck we
4555  * just did we ensure that they still don't conflict with the
4556  * lock we want.
4557  */
4558  }
4559  }
4560  else if (mode == LockTupleShare)
4561  {
4562  /*
4563  * If we're requesting Share, we can similarly avoid sleeping if
4564  * there's no update and no exclusive lock present.
4565  */
4566  if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
4567  !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
4568  {
4570 
4571  /*
4572  * Make sure it's still an appropriate lock, else start over.
4573  * See above about allowing xmax to change.
4574  */
4575  if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
4577  goto l3;
4578  require_sleep = false;
4579  }
4580  }
4581  else if (mode == LockTupleNoKeyExclusive)
4582  {
4583  /*
4584  * If we're requesting NoKeyExclusive, we might also be able to
4585  * avoid sleeping; just ensure that there no conflicting lock
4586  * already acquired.
4587  */
4588  if (infomask & HEAP_XMAX_IS_MULTI)
4589  {
4590  if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
4591  mode, NULL))
4592  {
4593  /*
4594  * No conflict, but if the xmax changed under us in the
4595  * meantime, start over.
4596  */
4598  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4600  xwait))
4601  goto l3;
4602 
4603  /* otherwise, we're good */
4604  require_sleep = false;
4605  }
4606  }
4607  else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
4608  {
4610 
4611  /* if the xmax changed in the meantime, start over */
4612  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4614  xwait))
4615  goto l3;
4616  /* otherwise, we're good */
4617  require_sleep = false;
4618  }
4619  }
4620 
4621  /*
4622  * As a check independent from those above, we can also avoid sleeping
4623  * if the current transaction is the sole locker of the tuple. Note
4624  * that the strength of the lock already held is irrelevant; this is
4625  * not about recording the lock in Xmax (which will be done regardless
4626  * of this optimization, below). Also, note that the cases where we
4627  * hold a lock stronger than we are requesting are already handled
4628  * above by not doing anything.
4629  *
4630  * Note we only deal with the non-multixact case here; MultiXactIdWait
4631  * is well equipped to deal with this situation on its own.
4632  */
4633  if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
4635  {
4636  /* ... but if the xmax changed in the meantime, start over */
4638  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4640  xwait))
4641  goto l3;
4643  require_sleep = false;
4644  }
4645 
4646  /*
4647  * Time to sleep on the other transaction/multixact, if necessary.
4648  *
4649  * If the other transaction is an update/delete that's already
4650  * committed, then sleeping cannot possibly do any good: if we're
4651  * required to sleep, get out to raise an error instead.
4652  *
4653  * By here, we either have already acquired the buffer exclusive lock,
4654  * or we must wait for the locking transaction or multixact; so below
4655  * we ensure that we grab buffer lock after the sleep.
4656  */
4657  if (require_sleep && (result == TM_Updated || result == TM_Deleted))
4658  {
4660  goto failed;
4661  }
4662  else if (require_sleep)
4663  {
4664  /*
4665  * Acquire tuple lock to establish our priority for the tuple, or
4666  * die trying. LockTuple will release us when we are next-in-line
4667  * for the tuple. We must do this even if we are share-locking,
4668  * but not if we already have a weaker lock on the tuple.
4669  *
4670  * If we are forced to "start over" below, we keep the tuple lock;
4671  * this arranges that we stay at the head of the line while
4672  * rechecking tuple state.
4673  */
4674  if (!skip_tuple_lock &&
4675  !heap_acquire_tuplock(relation, tid, mode, wait_policy,
4676  &have_tuple_lock))
4677  {
4678  /*
4679  * This can only happen if wait_policy is Skip and the lock
4680  * couldn't be obtained.
4681  */
4682  result = TM_WouldBlock;
4683  /* recovery code expects to have buffer lock held */
4685  goto failed;
4686  }
4687 
4688  if (infomask & HEAP_XMAX_IS_MULTI)
4689  {
4691 
4692  /* We only ever lock tuples, never update them */
4693  if (status >= MultiXactStatusNoKeyUpdate)
4694  elog(ERROR, "invalid lock mode in heap_lock_tuple");
4695 
4696  /* wait for multixact to end, or die trying */
4697  switch (wait_policy)
4698  {
4699  case LockWaitBlock:
4700  MultiXactIdWait((MultiXactId) xwait, status, infomask,
4701  relation, &tuple->t_self, XLTW_Lock, NULL);
4702  break;
4703  case LockWaitSkip:
4705  status, infomask, relation,
4706  NULL))
4707  {
4708  result = TM_WouldBlock;
4709  /* recovery code expects to have buffer lock held */
4711  goto failed;
4712  }
4713  break;
4714  case LockWaitError:
4716  status, infomask, relation,
4717  NULL))
4718  ereport(ERROR,
4719  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
4720  errmsg("could not obtain lock on row in relation \"%s\"",
4721  RelationGetRelationName(relation))));
4722 
4723  break;
4724  }
4725 
4726  /*
4727  * Of course, the multixact might not be done here: if we're
4728  * requesting a light lock mode, other transactions with light
4729  * locks could still be alive, as well as locks owned by our
4730  * own xact or other subxacts of this backend. We need to
4731  * preserve the surviving MultiXact members. Note that it
4732  * isn't absolutely necessary in the latter case, but doing so
4733  * is simpler.
4734  */
4735  }
4736  else
4737  {
4738  /* wait for regular transaction to end, or die trying */
4739  switch (wait_policy)
4740  {
4741  case LockWaitBlock:
4742  XactLockTableWait(xwait, relation, &tuple->t_self,
4743  XLTW_Lock);
4744  break;
4745  case LockWaitSkip:
4746  if (!ConditionalXactLockTableWait(xwait))
4747  {
4748  result = TM_WouldBlock;
4749  /* recovery code expects to have buffer lock held */
4751  goto failed;
4752  }
4753  break;
4754  case LockWaitError:
4755  if (!ConditionalXactLockTableWait(xwait))
4756  ereport(ERROR,
4757  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
4758  errmsg("could not obtain lock on row in relation \"%s\"",
4759  RelationGetRelationName(relation))));
4760  break;
4761  }
4762  }
4763 
4764  /* if there are updates, follow the update chain */
4765  if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
4766  {
4767  TM_Result res;
4768 
4769  res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
4771  mode);
4772  if (res != TM_Ok)
4773  {
4774  result = res;
4775  /* recovery code expects to have buffer lock held */
4777  goto failed;
4778  }
4779  }
4780 
4782 
4783  /*
4784  * xwait is done, but if xwait had just locked the tuple then some
4785  * other xact could update this tuple before we get to this point.
4786  * Check for xmax change, and start over if so.
4787  */
4788  if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
4790  xwait))
4791  goto l3;
4792 
4793  if (!(infomask & HEAP_XMAX_IS_MULTI))
4794  {
4795  /*
4796  * Otherwise check if it committed or aborted. Note we cannot
4797  * be here if the tuple was only locked by somebody who didn't
4798  * conflict with us; that would have been handled above. So
4799  * that transaction must necessarily be gone by now. But
4800  * don't check for this in the multixact case, because some
4801  * locker transactions might still be running.
4802  */
4803  UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
4804  }
4805  }
4806 
4807  /* By here, we're certain that we hold buffer exclusive lock again */
4808 
4809  /*
4810  * We may lock if previous xmax aborted, or if it committed but only
4811  * locked the tuple without updating it; or if we didn't have to wait
4812  * at all for whatever reason.
4813  */
4814  if (!require_sleep ||
4815  (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
4818  result = TM_Ok;
4819  else if (!ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid))
4820  result = TM_Updated;
4821  else
4822  result = TM_Deleted;
4823  }
4824 
4825 failed:
4826  if (result != TM_Ok)
4827  {
4828  Assert(result == TM_SelfModified || result == TM_Updated ||
4829  result == TM_Deleted || result == TM_WouldBlock);
4830  Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
4831  Assert(result != TM_Updated ||
4832  !ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid));
4833  tmfd->ctid = tuple->t_data->t_ctid;
4834  tmfd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
4835  if (result == TM_SelfModified)
4836  tmfd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
4837  else
4838  tmfd->cmax = InvalidCommandId;
4839  goto out_locked;
4840  }
4841 
4842  /*
4843  * If we didn't pin the visibility map page and the page has become all
4844  * visible while we were busy locking the buffer, or during some
4845  * subsequent window during which we had it unlocked, we'll have to unlock
4846  * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
4847  * unfortunate, especially since we'll now have to recheck whether the
4848  * tuple has been locked or updated under us, but hopefully it won't
4849  * happen very often.
4850  */
4851  if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
4852  {
4853  LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
4854  visibilitymap_pin(relation, block, &vmbuffer);
4856  goto l3;
4857  }
4858 
4859  xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
4860  old_infomask = tuple->t_data->t_infomask;
4861 
4862  /*
4863  * If this is the first possibly-multixact-able operation in the current
4864  * transaction, set my per-backend OldestMemberMXactId setting. We can be
4865  * certain that the transaction will never become a member of any older
4866  * MultiXactIds than that. (We have to do this even if we end up just
4867  * using our own TransactionId below, since some other backend could
4868  * incorporate our XID into a MultiXact immediately afterwards.)
4869  */
4871 
4872  /*
4873  * Compute the new xmax and infomask to store into the tuple. Note we do
4874  * not modify the tuple just yet, because that would leave it in the wrong
4875  * state if multixact.c elogs.
4876  */
4877  compute_new_xmax_infomask(xmax, old_infomask, tuple->t_data->t_infomask2,
4878  GetCurrentTransactionId(), mode, false,
4879  &xid, &new_infomask, &new_infomask2);
4880 
4882 
4883  /*
4884  * Store transaction information of xact locking the tuple.
4885  *
4886  * Note: Cmax is meaningless in this context, so don't set it; this avoids
4887  * possibly generating a useless combo CID. Moreover, if we're locking a
4888  * previously updated tuple, it's important to preserve the Cmax.
4889  *
4890  * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
4891  * we would break the HOT chain.
4892  */
4893  tuple->t_data->t_infomask &= ~HEAP_XMAX_BITS;
4894  tuple->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
4895  tuple->t_data->t_infomask |= new_infomask;
4896  tuple->t_data->t_infomask2 |= new_infomask2;
4897  if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
4899  HeapTupleHeaderSetXmax(tuple->t_data, xid);
4900 
4901  /*
4902  * Make sure there is no forward chain link in t_ctid. Note that in the
4903  * cases where the tuple has been updated, we must not overwrite t_ctid,
4904  * because it was set by the updater. Moreover, if the tuple has been
4905  * updated, we need to follow the update chain to lock the new versions of
4906  * the tuple as well.
4907  */
4908  if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
4909  tuple->t_data->t_ctid = *tid;
4910 
4911  /* Clear only the all-frozen bit on visibility map if needed */
4912  if (PageIsAllVisible(page) &&
4913  visibilitymap_clear(relation, block, vmbuffer,
4915  cleared_all_frozen = true;
4916 
4917 
4918  MarkBufferDirty(*buffer);
4919 
4920  /*
4921  * XLOG stuff. You might think that we don't need an XLOG record because
4922  * there is no state change worth restoring after a crash. You would be
4923  * wrong however: we have just written either a TransactionId or a
4924  * MultiXactId that may never have been seen on disk before, and we need
4925  * to make sure that there are XLOG entries covering those ID numbers.
4926  * Else the same IDs might be re-used after a crash, which would be
4927  * disastrous if this page made it to disk before the crash. Essentially
4928  * we have to enforce the WAL log-before-data rule even in this case.
4929  * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
4930  * entries for everything anyway.)
4931  */
4932  if (RelationNeedsWAL(relation))
4933  {
4934  xl_heap_lock xlrec;
4935  XLogRecPtr recptr;
4936 
4937  XLogBeginInsert();
4938  XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD);
4939 
4940  xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
4941  xlrec.locking_xid = xid;
4942  xlrec.infobits_set = compute_infobits(new_infomask,
4943  tuple->t_data->t_infomask2);
4944  xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
4945  XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
4946 
4947  /* we don't decode row locks atm, so no need to log the origin */
4948 
4949  recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
4950 
4951  PageSetLSN(page, recptr);
4952  }
4953 
4954  END_CRIT_SECTION();
4955 
4956  result = TM_Ok;
4957 
4958 out_locked:
4959  LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
4960 
4961 out_unlocked:
4962  if (BufferIsValid(vmbuffer))
4963  ReleaseBuffer(vmbuffer);
4964 
4965  /*
4966  * Don't update the visibility map here. Locking a tuple doesn't change
4967  * visibility info.
4968  */
4969 
4970  /*
4971  * Now that we have successfully marked the tuple as locked, we can
4972  * release the lmgr tuple lock, if we had it.
4973  */
4974  if (have_tuple_lock)
4975  UnlockTupleTuplock(relation, tid, mode);
4976 
4977  return result;
4978 }
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:365
ItemPointerData ctid
Definition: tableam.h:126
static PgChecksumMode mode
Definition: pg_checksums.c:61
MultiXactStatus
Definition: multixact.h:41
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
#define HEAP_XMAX_BITS
Definition: htup_details.h:270
OffsetNumber offnum
Definition: heapam_xlog.h:281
static uint8 compute_infobits(uint16 infomask, uint16 infomask2)
Definition: heapam.c:2719
#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:275
TransactionId locking_xid
Definition: heapam_xlog.h:280
uint32 TransactionId
Definition: c.h:587
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:869
void visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *buf)
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1562
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define VISIBILITYMAP_ALL_FROZEN
Definition: visibilitymap.h:27
static bool xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
Definition: heapam.c:2741
#define HeapTupleHeaderClearHotUpdated(tup)
Definition: htup_details.h:495
#define END_CRIT_SECTION()
Definition: miscadmin.h:137
CommandId cmax
Definition: tableam.h:128
bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
#define InvalidBuffer
Definition: buf.h:25
#define START_CRIT_SECTION()
Definition: miscadmin.h:135
int errcode(int sqlerrcode)
Definition: elog.c:698
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3784
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
TM_Result HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, Buffer buffer)
#define UnlockTupleTuplock(rel, tup, mode)
Definition: heapam.c:165
bool ConditionalXactLockTableWait(TransactionId xid)
Definition: lmgr.c:713
#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:440
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
bool visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer buf, uint8 flags)
TransactionId xmax
Definition: tableam.h:127
#define ERROR
Definition: elog.h:46
#define HEAP_XMAX_INVALID
Definition: htup_details.h:207
int8 infobits_set
Definition: heapam_xlog.h:282
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:7066
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:438
uint32 t_len
Definition: htup.h:64
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:380
#define RelationGetRelationName(relation)
Definition: rel.h:491
Oid t_tableOid
Definition: htup.h:66
#define SizeOfHeapLock
Definition: heapam_xlog.h:286
#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:5042
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
TM_Result
Definition: tableam.h:71
#define InvalidCommandId
Definition: c.h:604
#define HEAP_XMAX_IS_LOCKED_ONLY(infomask)
Definition: htup_details.h:230
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4023
#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:1985
static bool heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode, LockWaitPolicy wait_policy, bool *have_tuple_lock)
Definition: heapam.c:4993
#define ereport(elevel,...)
Definition: elog.h:157
TransactionId MultiXactId
Definition: c.h:597
void XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, XLTW_Oper oper)
Definition: lmgr.c:640
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
static TM_Result heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid, TransactionId xid, LockTupleMode mode)
Definition: heapam.c:5759
Definition: lmgr.h:29
CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup)
Definition: combocid.c:118
Definition: tableam.h:77
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:697
#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:6889
#define RelationNeedsWAL(relation)
Definition: rel.h:570
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
#define TUPLOCK_from_mxstatus(status)
Definition: heapam.c:214
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define XLOG_HEAP_LOCK
Definition: heapam_xlog.h:38
#define elog(elevel,...)
Definition: elog.h:232
int i
static MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
Definition: heapam.c:4256
#define HEAP_XMAX_IS_EXCL_LOCKED(infomask)
Definition: htup_details.h:264
int GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members, bool from_pgupgrade, bool onlyLock)
Definition: multixact.c:1223
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define HEAP_XMAX_IS_KEYSHR_LOCKED(infomask)
Definition: htup_details.h:266
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:227
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define RelationGetRelid(relation)
Definition: rel.h:457
static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, int *remaining)
Definition: heapam.c:7088
#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 2366 of file heapam.c.

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

Referenced by CatalogTuplesMultiInsertWithInfo(), and SampleHeapTupleVisible().

2368 {
2370  HeapTuple *heaptuples;
2371  int i;
2372  int ndone;
2373  PGAlignedBlock scratch;
2374  Page page;
2375  Buffer vmbuffer = InvalidBuffer;
2376  bool needwal;
2377  Size saveFreeSpace;
2378  bool need_tuple_data = RelationIsLogicallyLogged(relation);
2379  bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
2380 
2381  /* currently not needed (thus unsupported) for heap_multi_insert() */
2383 
2384  needwal = RelationNeedsWAL(relation);
2385  saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
2387 
2388  /* Toast and set header data in all the slots */
2389  heaptuples = palloc(ntuples * sizeof(HeapTuple));
2390  for (i = 0; i < ntuples; i++)
2391  {
2392  HeapTuple tuple;
2393 
2394  tuple = ExecFetchSlotHeapTuple(slots[i], true, NULL);
2395  slots[i]->tts_tableOid = RelationGetRelid(relation);
2396  tuple->t_tableOid = slots[i]->tts_tableOid;
2397  heaptuples[i] = heap_prepare_insert(relation, tuple, xid, cid,
2398  options);
2399  }
2400 
2401  /*
2402  * We're about to do the actual inserts -- but check for conflict first,
2403  * to minimize the possibility of having to roll back work we've just
2404  * done.
2405  *
2406  * A check here does not definitively prevent a serialization anomaly;
2407  * that check MUST be done at least past the point of acquiring an
2408  * exclusive buffer content lock on every buffer that will be affected,
2409  * and MAY be done after all inserts are reflected in the buffers and
2410  * those locks are released; otherwise there is a race condition. Since
2411  * multiple buffers can be locked and unlocked in the loop below, and it
2412  * would not be feasible to identify and lock all of those buffers before
2413  * the loop, we must do a final check at the end.
2414  *
2415  * The check here could be omitted with no loss of correctness; it is
2416  * present strictly as an optimization.
2417  *
2418  * For heap inserts, we only need to check for table-level SSI locks. Our
2419  * new tuples can't possibly conflict with existing tuple locks, and heap
2420  * page locks are only consolidated versions of tuple locks; they do not
2421  * lock "gaps" as index page locks do. So we don't need to specify a
2422  * buffer when making the call, which makes for a faster check.
2423  */
2425 
2426  ndone = 0;
2427  while (ndone < ntuples)
2428  {
2429  Buffer buffer;
2430  bool starting_with_empty_page;
2431  bool all_visible_cleared = false;
2432  bool all_frozen_set = false;
2433  int nthispage;
2434 
2436 
2437  /*
2438  * Find buffer where at least the next tuple will fit. If the page is
2439  * all-visible, this will also pin the requisite visibility map page.
2440  *
2441  * Also pin visibility map page if COPY FREEZE inserts tuples into an
2442  * empty page. See all_frozen_set below.
2443  */
2444  buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
2445  InvalidBuffer, options, bistate,
2446  &vmbuffer, NULL);
2447  page = BufferGetPage(buffer);
2448 
2449  starting_with_empty_page = PageGetMaxOffsetNumber(page) == 0;
2450 
2451  if (starting_with_empty_page && (options & HEAP_INSERT_FROZEN))
2452  all_frozen_set = true;
2453 
2454  /* NO EREPORT(ERROR) from here till changes are logged */
2456 
2457  /*
2458  * RelationGetBufferForTuple has ensured that the first tuple fits.
2459  * Put that on the page, and then as many other tuples as fit.
2460  */
2461  RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
2462 
2463  /*
2464  * For logical decoding we need combo CIDs to properly decode the
2465  * catalog.
2466  */
2467  if (needwal && need_cids)
2468  log_heap_new_cid(relation, heaptuples[ndone]);
2469 
2470  for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
2471  {
2472  HeapTuple heaptup = heaptuples[ndone + nthispage];
2473 
2474  if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
2475  break;
2476 
2477  RelationPutHeapTuple(relation, buffer, heaptup, false);
2478 
2479  /*
2480  * For logical decoding we need combo CIDs to properly decode the
2481  * catalog.
2482  */
2483  if (needwal && need_cids)
2484  log_heap_new_cid(relation, heaptup);
2485  }
2486 
2487  /*
2488  * If the page is all visible, need to clear that, unless we're only
2489  * going to add further frozen rows to it.
2490  *
2491  * If we're only adding already frozen rows to a previously empty
2492  * page, mark it as all-visible.
2493  */
2494  if (PageIsAllVisible(page) && !(options & HEAP_INSERT_FROZEN))
2495  {
2496  all_visible_cleared = true;
2497  PageClearAllVisible(page);
2498  visibilitymap_clear(relation,
2499  BufferGetBlockNumber(buffer),
2500  vmbuffer, VISIBILITYMAP_VALID_BITS);
2501  }
2502  else if (all_frozen_set)
2503  PageSetAllVisible(page);
2504 
2505  /*
2506  * XXX Should we set PageSetPrunable on this page ? See heap_insert()
2507  */
2508 
2509  MarkBufferDirty(buffer);
2510 
2511  /* XLOG stuff */
2512  if (needwal)
2513  {
2514  XLogRecPtr recptr;
2515  xl_heap_multi_insert *xlrec;
2517  char *tupledata;
2518  int totaldatalen;
2519  char *scratchptr = scratch.data;
2520  bool init;
2521  int bufflags = 0;
2522 
2523  /*
2524  * If the page was previously empty, we can reinit the page
2525  * instead of restoring the whole thing.
2526  */
2527  init = starting_with_empty_page;
2528 
2529  /* allocate xl_heap_multi_insert struct from the scratch area */
2530  xlrec = (xl_heap_multi_insert *) scratchptr;
2531  scratchptr += SizeOfHeapMultiInsert;
2532 
2533  /*
2534  * Allocate offsets array. Unless we're reinitializing the page,
2535  * in that case the tuples are stored in order starting at
2536  * FirstOffsetNumber and we don't need to store the offsets
2537  * explicitly.
2538  */
2539  if (!init)
2540  scratchptr += nthispage * sizeof(OffsetNumber);
2541 
2542  /* the rest of the scratch space is used for tuple data */
2543  tupledata = scratchptr;
2544 
2545  /* check that the mutually exclusive flags are not both set */
2546  Assert (!(all_visible_cleared && all_frozen_set));
2547 
2548  xlrec->flags = 0;
2549  if (all_visible_cleared)
2551  if (all_frozen_set)
2553 
2554  xlrec->ntuples = nthispage;
2555 
2556  /*
2557  * Write out an xl_multi_insert_tuple and the tuple data itself
2558  * for each tuple.
2559  */
2560  for (i = 0; i < nthispage; i++)
2561  {
2562  HeapTuple heaptup = heaptuples[ndone + i];
2563  xl_multi_insert_tuple *tuphdr;
2564  int datalen;
2565 
2566  if (!init)
2567  xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
2568  /* xl_multi_insert_tuple needs two-byte alignment. */
2569  tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
2570  scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
2571 
2572  tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
2573  tuphdr->t_infomask = heaptup->t_data->t_infomask;
2574  tuphdr->t_hoff = heaptup->t_data->t_hoff;
2575 
2576  /* write bitmap [+ padding] [+ oid] + data */
2577  datalen = heaptup->t_len - SizeofHeapTupleHeader;
2578  memcpy(scratchptr,
2579  (char *) heaptup->t_data + SizeofHeapTupleHeader,
2580  datalen);
2581  tuphdr->datalen = datalen;
2582  scratchptr += datalen;
2583  }
2584  totaldatalen = scratchptr - tupledata;
2585  Assert((scratchptr - scratch.data) < BLCKSZ);
2586 
2587  if (need_tuple_data)
2589 
2590  /*
2591  * Signal that this is the last xl_heap_multi_insert record
2592  * emitted by this call to heap_multi_insert(). Needed for logical
2593  * decoding so it knows when to cleanup temporary data.
2594  */
2595  if (ndone + nthispage == ntuples)
2596  xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
2597 
2598  if (init)
2599  {
2600  info |= XLOG_HEAP_INIT_PAGE;
2601  bufflags |= REGBUF_WILL_INIT;
2602  }
2603 
2604  /*
2605  * If we're doing logical decoding, include the new tuple data
2606  * even if we take a full-page image of the page.
2607  */
2608  if (need_tuple_data)
2609  bufflags |= REGBUF_KEEP_DATA;
2610 
2611  XLogBeginInsert();
2612  XLogRegisterData((char *) xlrec, tupledata - scratch.data);
2613  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
2614 
2615  XLogRegisterBufData(0, tupledata, totaldatalen);
2616 
2617  /* filtering by origin on a row level is much more efficient */
2619 
2620  recptr = XLogInsert(RM_HEAP2_ID, info);
2621 
2622  PageSetLSN(page, recptr);
2623  }
2624 
2625  END_CRIT_SECTION();
2626 
2627  /*
2628  * If we've frozen everything on the page, update the visibilitymap.
2629  * We're already holding pin on the vmbuffer.
2630  */
2631  if (all_frozen_set)
2632  {
2633  Assert(PageIsAllVisible(page));
2634  Assert(visibilitymap_pin_ok(BufferGetBlockNumber(buffer), vmbuffer));
2635 
2636  /*
2637  * It's fine to use InvalidTransactionId here - this is only used
2638  * when HEAP_INSERT_FROZEN is specified, which intentionally
2639  * violates visibility rules.
2640  */
2641  visibilitymap_set(relation, BufferGetBlockNumber(buffer), buffer,
2642  InvalidXLogRecPtr, vmbuffer,
2645  }
2646 
2647  UnlockReleaseBuffer(buffer);
2648  ndone += nthispage;
2649 
2650  /*
2651  * NB: Only release vmbuffer after inserting all tuples - it's fairly
2652  * likely that we'll insert into subsequent heap pages that are likely
2653  * to use the same vm page.
2654  */
2655  }
2656 
2657  /* We're done with inserting all tuples, so release the last vmbuffer. */
2658  if (vmbuffer != InvalidBuffer)
2659  ReleaseBuffer(vmbuffer);
2660 
2661  /*
2662  * We're done with the actual inserts. Check for conflicts again, to
2663  * ensure that all rw-conflicts in to these inserts are detected. Without
2664  * this final check, a sequential scan of the heap may have locked the
2665  * table after the "before" check, missing one opportunity to detect the
2666  * conflict, and then scanned the table before the new tuples were there,
2667  * missing the other chance to detect the conflict.
2668  *
2669  * For heap inserts, we only need to check for table-level SSI locks. Our
2670  * new tuples can't possibly conflict with existing tuple locks, and heap
2671  * page locks are only consolidated versions of tuple locks; they do not
2672  * lock "gaps" as index page locks do. So we don't need to specify a
2673  * buffer when making the call.
2674  */
2676 
2677  /*
2678  * If tuples are cachable, mark them for invalidation from the caches in
2679  * case we abort. Note it is OK to do this after releasing the buffer,
2680  * because the heaptuples data structure is all in local memory, not in
2681  * the shared buffer.
2682  */
2683  if (IsCatalogRelation(relation))
2684  {
2685  for (i = 0; i < ntuples; i++)
2686  CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
2687  }
2688 
2689  /* copy t_self fields back to the caller's slots */
2690  for (i = 0; i < ntuples; i++)
2691  slots[i]->tts_tid = heaptuples[i]->t_self;
2692 
2693  pgstat_count_heap_insert(relation, ntuples);
2694 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:368
Oid tts_tableOid
Definition: tuptable.h:131
#define SizeofHeapTupleHeader
Definition: htup_details.h:184
bool IsCatalogRelation(Relation relation)
Definition: catalog.c:103
static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup)
Definition: heapam.c:8285
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple)
Definition: inval.c:1122
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid, CommandId cid, int options)
Definition: heapam.c:2312
#define XLH_INSERT_ALL_FROZEN_SET
Definition: heapam_xlog.h:73
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:587
#define HEAP_INSERT_FROZEN
Definition: heapam.h:35
void visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf, XLogRecPtr recptr, Buffer vmBuf, TransactionId cutoff_xid, uint8 flags)
OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER]
Definition: heapam_xlog.h:179
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1562
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
#define VISIBILITYMAP_ALL_FROZEN
Definition: visibilitymap.h:27
#define END_CRIT_SECTION()
Definition: miscadmin.h:137
unsigned char uint8
Definition: c.h:439
#define InvalidBuffer
Definition: buf.h:25
#define SizeOfHeapMultiInsert
Definition: heapam_xlog.h:182
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33
#define START_CRIT_SECTION()
Definition: miscadmin.h:135
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:248
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3784
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:643
#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:1141
bool visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer buf, uint8 flags)
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3807
Size PageGetHeapFreeSpace(Page page)
Definition: bufpage.c:984
#define XLH_INSERT_CONTAINS_NEW_TUPLE
Definition: heapam_xlog.h:69
ItemPointerData t_self
Definition: htup.h:65
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:438
uint32 t_len
Definition: htup.h:64
#define PageSetAllVisible(page)
Definition: bufpage.h:387
#define REGBUF_STANDARD
Definition: xloginsert.h:35
Buffer RelationGetBufferForTuple(Relation relation, Size len, Buffer otherBuffer, int options, BulkInsertState bistate, Buffer *vmbuffer, Buffer *vmbuffer_other)
Definition: hio.c:333
#define InvalidTransactionId
Definition: transam.h:31
Oid t_tableOid
Definition: htup.h:66
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:404
#define init()
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define AssertArg(condition)
Definition: c.h:806
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1614
#define RelationGetTargetPageFreeSpace(relation, defaultff)
Definition: rel.h:341
#define XLH_INSERT_LAST_IN_MULTI
Definition: heapam_xlog.h:67
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
#define RelationIsAccessibleInLogicalDecoding(relation)
Definition: rel.h:626
#define REGBUF_KEEP_DATA
Definition: xloginsert.h:38
void CheckForSerializableConflictIn(Relation relation, ItemPointer tid, BlockNumber blkno)
Definition: predicate.c:4446
#define PageClearAllVisible(page)
Definition: bufpage.h:389
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
size_t Size
Definition: c.h:540
#define InvalidBlockNumber
Definition: block.h:33
#define MAXALIGN(LEN)
Definition: c.h:757
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define RelationNeedsWAL(relation)
Definition: rel.h:570
#define VISIBILITYMAP_ALL_VISIBLE
Definition: visibilitymap.h:26
#define XLH_INSERT_ALL_VISIBLE_CLEARED
Definition: heapam_xlog.h:66
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2758
void pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
Definition: pgstat.c:2217
void * palloc(Size size)
Definition: mcxt.c:1062
#define HEAP_INSERT_NO_LOGICAL
Definition: heapam.h:36
int i
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:102
bool visibilitymap_pin_ok(BlockNumber heapBlk, Buffer buf)
#define HEAP_DEFAULT_FILLFACTOR
Definition: rel.h:312
#define SHORTALIGN(LEN)
Definition: c.h:753
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define RelationGetRelid(relation)
Definition: rel.h:457
Pointer Page
Definition: bufpage.h:78
#define SizeOfMultiInsertTuple
Definition: heapam_xlog.h:193

◆ heap_page_prune()

int heap_page_prune ( Relation  relation,
Buffer  buffer,
struct GlobalVisState vistest,
TransactionId  old_snap_xmin,
TimestampTz  old_snap_ts_ts,
bool  report_stats,
OffsetNumber off_loc 
)

Definition at line 219 of file pruneheap.c.

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

Referenced by heap_page_prune_opt(), and lazy_scan_prune().

225 {
226  int ndeleted = 0;
227  Page page = BufferGetPage(buffer);
228  OffsetNumber offnum,
229  maxoff;
230  PruneState prstate;
231 
232  /*
233  * Our strategy is to scan the page and make lists of items to change,
234  * then apply the changes within a critical section. This keeps as much
235  * logic as possible out of the critical section, and also ensures that
236  * WAL replay will work the same as the normal case.
237  *
238  * First, initialize the new pd_prune_xid value to zero (indicating no
239  * prunable tuples). If we find any tuples which may soon become
240  * prunable, we will save the lowest relevant XID in new_prune_xid. Also
241  * initialize the rest of our working state.
242  */
244  prstate.rel = relation;
245  prstate.vistest = vistest;
246  prstate.old_snap_xmin = old_snap_xmin;
247  prstate.old_snap_ts = old_snap_ts;
248  prstate.old_snap_used = false;
250  prstate.nredirected = prstate.ndead = prstate.nunused = 0;
251  memset(prstate.marked, 0, sizeof(prstate.marked));
252 
253  /* Scan the page */
254  maxoff = PageGetMaxOffsetNumber(page);
255  for (offnum = FirstOffsetNumber;
256  offnum <= maxoff;
257  offnum = OffsetNumberNext(offnum))
258  {
259  ItemId itemid;
260 
261  /* Ignore items already processed as part of an earlier chain */
262  if (prstate.marked[offnum])
263  continue;
264 
265  /*
266  * Set the offset number so that we can display it along with any
267  * error that occurred while processing this tuple.
268  */
269  if (off_loc)
270  *off_loc = offnum;
271 
272  /* Nothing to do if slot is empty or already dead */
273  itemid = PageGetItemId(page, offnum);
274  if (!ItemIdIsUsed(itemid) || ItemIdIsDead(itemid))
275  continue;
276 
277  /* Process this item or chain of items */
278  ndeleted += heap_prune_chain(buffer, offnum, &prstate);
279  }
280 
281  /* Clear the offset information once we have processed the given page. */
282  if (off_loc)
283  *off_loc = InvalidOffsetNumber;
284 
285  /* Any error while applying the changes is critical */
287 
288  /* Have we found any prunable items? */
289  if (prstate.nredirected > 0 || prstate.ndead > 0 || prstate.nunused > 0)
290  {
291  /*
292  * Apply the planned item changes, then repair page fragmentation, and
293  * update the page's hint bit about whether it has free line pointers.
294  */
296  prstate.redirected, prstate.nredirected,
297  prstate.nowdead, prstate.ndead,
298  prstate.nowunused, prstate.nunused);
299 
300  /*
301  * Update the page's pd_prune_xid field to either zero, or the lowest
302  * XID of any soon-prunable tuple.
303  */
304  ((PageHeader) page)->pd_prune_xid = prstate.new_prune_xid;
305 
306  /*
307  * Also clear the "page is full" flag, since there's no point in
308  * repeating the prune/defrag process until something else happens to
309  * the page.
310  */
311  PageClearFull(page);
312 
313  MarkBufferDirty(buffer);
314 
315  /*
316  * Emit a WAL XLOG_HEAP2_PRUNE record showing what we did
317  */
318  if (RelationNeedsWAL(relation))
319  {
320  xl_heap_prune xlrec;
321  XLogRecPtr recptr;
322 
323  xlrec.latestRemovedXid = prstate.latestRemovedXid;
324  xlrec.nredirected = prstate.nredirected;
325  xlrec.ndead = prstate.ndead;
326 
327  XLogBeginInsert();
328  XLogRegisterData((char *) &xlrec, SizeOfHeapPrune);
329 
331 
332  /*
333  * The OffsetNumber arrays are not actually in the buffer, but we
334  * pretend that they are. When XLogInsert stores the whole
335  * buffer, the offset arrays need not be stored too.
336  */
337  if (prstate.nredirected > 0)
338  XLogRegisterBufData(0, (char *) prstate.redirected,
339  prstate.nredirected *
340  sizeof(OffsetNumber) * 2);
341 
342  if (prstate.ndead > 0)
343  XLogRegisterBufData(0, (char *) prstate.nowdead,
344  prstate.ndead * sizeof(OffsetNumber));
345 
346  if (prstate.nunused > 0)
347  XLogRegisterBufData(0, (char *) prstate.nowunused,
348  prstate.nunused * sizeof(OffsetNumber));
349 
350  recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_PRUNE);
351 
352  PageSetLSN(BufferGetPage(buffer), recptr);
353  }
354  }
355  else
356  {
357  /*
358  * If we didn't prune anything, but have found a new value for the
359  * pd_prune_xid field, update it and mark the buffer dirty. This is
360  * treated as a non-WAL-logged hint.
361  *