PostgreSQL Source Code git master
Loading...
Searching...
No Matches
heapam.h File Reference
#include "access/heapam_xlog.h"
#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/read_stream.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  BitmapHeapScanDescData
 
struct  IndexFetchHeapData
 
struct  HeapTupleFreeze
 
struct  HeapPageFreeze
 
struct  PruneFreezeParams
 
struct  PruneFreezeResult
 
struct  BatchMVCCState
 

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 HEAP_PAGE_PRUNE_MARK_UNUSED_NOW   (1 << 0)
 
#define HEAP_PAGE_PRUNE_FREEZE   (1 << 1)
 
#define HEAP_PAGE_PRUNE_ALLOW_FAST_PATH   (1 << 2)
 
#define HEAP_PAGE_PRUNE_SET_VM   (1 << 3)
 
#define MaxLockTupleMode   LockTupleExclusive
 
#define HEAP_FREEZE_CHECK_XMIN_COMMITTED   0x01
 
#define HEAP_FREEZE_CHECK_XMAX_ABORTED   0x02
 

Typedefs

typedef struct BulkInsertStateDataBulkInsertState
 
typedef struct GlobalVisState GlobalVisState
 
typedef struct TupleTableSlot TupleTableSlot
 
typedef struct VacuumCutoffs VacuumCutoffs
 
typedef struct VacuumParams VacuumParams
 
typedef struct HeapScanDescData HeapScanDescData
 
typedef struct HeapScanDescDataHeapScanDesc
 
typedef struct BitmapHeapScanDescData BitmapHeapScanDescData
 
typedef struct BitmapHeapScanDescDataBitmapHeapScanDesc
 
typedef struct IndexFetchHeapData IndexFetchHeapData
 
typedef struct HeapTupleFreeze HeapTupleFreeze
 
typedef struct HeapPageFreeze HeapPageFreeze
 
typedef struct PruneFreezeParams PruneFreezeParams
 
typedef struct PruneFreezeResult PruneFreezeResult
 
typedef struct BatchMVCCState BatchMVCCState
 

Enumerations

enum  HTSV_Result {
  HEAPTUPLE_DEAD , HEAPTUPLE_LIVE , HEAPTUPLE_RECENTLY_DEAD , HEAPTUPLE_INSERT_IN_PROGRESS ,
  HEAPTUPLE_DELETE_IN_PROGRESS
}
 
enum  PruneReason { PRUNE_ON_ACCESS , PRUNE_VACUUM_SCAN , PRUNE_VACUUM_CLEANUP }
 

Functions

TableScanDesc heap_beginscan (Relation relation, Snapshot snapshot, int nkeys, ScanKey key, ParallelTableScanDesc parallel_scan, uint32 flags)
 
void heap_setscanlimits (TableScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
 
void heap_prepare_pagescan (TableScanDesc sscan)
 
void heap_rescan (TableScanDesc sscan, ScanKey key, bool set_params, bool allow_strat, bool allow_sync, bool allow_pagemode)
 
void heap_endscan (TableScanDesc sscan)
 
HeapTuple heap_getnext (TableScanDesc sscan, ScanDirection direction)
 
bool heap_getnextslot (TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
 
void heap_set_tidrange (TableScanDesc sscan, ItemPointer mintid, ItemPointer maxtid)
 
bool heap_getnextslot_tidrange (TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
 
bool heap_fetch (Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf)
 
void heap_get_latest_tid (TableScanDesc sscan, ItemPointer tid)
 
BulkInsertState GetBulkInsertState (void)
 
void FreeBulkInsertState (BulkInsertState)
 
void ReleaseBulkInsertStatePin (BulkInsertState bistate)
 
void heap_insert (Relation relation, HeapTuple tup, CommandId cid, uint32 options, BulkInsertState bistate)
 
void heap_multi_insert (Relation relation, TupleTableSlot **slots, int ntuples, CommandId cid, uint32 options, BulkInsertState bistate)
 
TM_Result heap_delete (Relation relation, const ItemPointerData *tid, CommandId cid, uint32 options, Snapshot crosscheck, bool wait, TM_FailureData *tmfd)
 
void heap_finish_speculative (Relation relation, const ItemPointerData *tid)
 
void heap_abort_speculative (Relation relation, const ItemPointerData *tid)
 
TM_Result heap_update (Relation relation, const ItemPointerData *otid, HeapTuple newtup, CommandId cid, uint32 options, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
 
TM_Result heap_lock_tuple (Relation relation, HeapTuple tuple, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, bool follow_updates, Buffer *buffer, TM_FailureData *tmfd)
 
bool heap_inplace_lock (Relation relation, HeapTuple oldtup_ptr, Buffer buffer, void(*release_callback)(void *), void *arg)
 
void heap_inplace_update_and_unlock (Relation relation, HeapTuple oldtup, HeapTuple tuple, Buffer buffer)
 
void heap_inplace_unlock (Relation relation, HeapTuple oldtup, Buffer buffer)
 
bool heap_prepare_freeze_tuple (HeapTupleHeader tuple, const VacuumCutoffs *cutoffs, HeapPageFreeze *pagefrz, HeapTupleFreeze *frz, bool *totally_frozen)
 
void heap_pre_freeze_checks (Buffer buffer, HeapTupleFreeze *tuples, int ntuples)
 
void heap_freeze_prepared_tuples (Buffer buffer, HeapTupleFreeze *tuples, int ntuples)
 
bool heap_freeze_tuple (HeapTupleHeader tuple, TransactionId relfrozenxid, TransactionId relminmxid, TransactionId FreezeLimit, TransactionId MultiXactCutoff)
 
bool heap_tuple_should_freeze (HeapTupleHeader tuple, const VacuumCutoffs *cutoffs, TransactionId *NoFreezePageRelfrozenXid, MultiXactId *NoFreezePageRelminMxid)
 
bool heap_tuple_needs_eventual_freeze (HeapTupleHeader tuple)
 
void simple_heap_insert (Relation relation, HeapTuple tup)
 
void simple_heap_delete (Relation relation, const ItemPointerData *tid)
 
void simple_heap_update (Relation relation, const ItemPointerData *otid, HeapTuple tup, TU_UpdateIndexes *update_indexes)
 
TransactionId heap_index_delete_tuples (Relation rel, TM_IndexDeleteOp *delstate)
 
IndexFetchTableDataheapam_index_fetch_begin (Relation rel, uint32 flags)
 
void heapam_index_fetch_reset (IndexFetchTableData *scan)
 
void heapam_index_fetch_end (IndexFetchTableData *scan)
 
bool heap_hot_search_buffer (ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, bool *all_dead, bool first_call)
 
bool heapam_index_fetch_tuple (struct IndexFetchTableData *scan, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, bool *heap_continue, bool *all_dead)
 
void heap_page_prune_opt (Relation relation, Buffer buffer, Buffer *vmbuffer, bool rel_read_only)
 
void heap_page_prune_and_freeze (PruneFreezeParams *params, PruneFreezeResult *presult, OffsetNumber *off_loc, TransactionId *new_relfrozen_xid, MultiXactId *new_relmin_mxid)
 
void heap_page_prune_execute (Buffer buffer, bool lp_truncate_only, OffsetNumber *redirected, int nredirected, OffsetNumber *nowdead, int ndead, OffsetNumber *nowunused, int nunused)
 
void heap_get_root_tuples (Page page, OffsetNumber *root_offsets)
 
void log_heap_prune_and_freeze (Relation relation, Buffer buffer, Buffer vmbuffer, uint8 vmflags, TransactionId conflict_xid, bool cleanup_lock, PruneReason reason, HeapTupleFreeze *frozen, int nfrozen, OffsetNumber *redirected, int nredirected, OffsetNumber *dead, int ndead, OffsetNumber *unused, int nunused)
 
void heap_vacuum_rel (Relation rel, const VacuumParams *params, BufferAccessStrategy bstrategy)
 
bool HeapTupleSatisfiesVisibility (HeapTuple htup, Snapshot snapshot, Buffer buffer)
 
TM_Result HeapTupleSatisfiesUpdate (HeapTuple htup, CommandId curcid, Buffer buffer)
 
HTSV_Result HeapTupleSatisfiesVacuum (HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
 
HTSV_Result HeapTupleSatisfiesVacuumHorizon (HeapTuple htup, Buffer buffer, TransactionId *dead_after)
 
void HeapTupleSetHintBits (HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid)
 
bool HeapTupleHeaderIsOnlyLocked (HeapTupleHeader tuple)
 
bool HeapTupleIsSurelyDead (HeapTuple htup, GlobalVisState *vistest)
 
int HeapTupleSatisfiesMVCCBatch (Snapshot snapshot, Buffer buffer, int ntups, BatchMVCCState *batchmvcc, OffsetNumber *vistuples_dense)
 
bool ResolveCminCmaxDuringDecoding (struct HTAB *tuplecid_data, Snapshot snapshot, HeapTuple htup, Buffer buffer, CommandId *cmin, CommandId *cmax)
 
void HeapCheckForSerializableConflictOut (bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
 
static void heap_execute_freeze_tuple (HeapTupleHeader tuple, HeapTupleFreeze *frz)
 

Macro Definition Documentation

◆ HEAP_FREEZE_CHECK_XMAX_ABORTED

#define HEAP_FREEZE_CHECK_XMAX_ABORTED   0x02

Definition at line 150 of file heapam.h.

◆ HEAP_FREEZE_CHECK_XMIN_COMMITTED

#define HEAP_FREEZE_CHECK_XMIN_COMMITTED   0x01

Definition at line 149 of file heapam.h.

◆ HEAP_INSERT_FROZEN

#define HEAP_INSERT_FROZEN   TABLE_INSERT_FROZEN

Definition at line 37 of file heapam.h.

◆ HEAP_INSERT_NO_LOGICAL

#define HEAP_INSERT_NO_LOGICAL   TABLE_INSERT_NO_LOGICAL

Definition at line 38 of file heapam.h.

◆ HEAP_INSERT_SKIP_FSM

#define HEAP_INSERT_SKIP_FSM   TABLE_INSERT_SKIP_FSM

Definition at line 36 of file heapam.h.

◆ HEAP_INSERT_SPECULATIVE

#define HEAP_INSERT_SPECULATIVE   0x0010

Definition at line 39 of file heapam.h.

◆ HEAP_PAGE_PRUNE_ALLOW_FAST_PATH

#define HEAP_PAGE_PRUNE_ALLOW_FAST_PATH   (1 << 2)

Definition at line 44 of file heapam.h.

◆ HEAP_PAGE_PRUNE_FREEZE

#define HEAP_PAGE_PRUNE_FREEZE   (1 << 1)

Definition at line 43 of file heapam.h.

◆ HEAP_PAGE_PRUNE_MARK_UNUSED_NOW

#define HEAP_PAGE_PRUNE_MARK_UNUSED_NOW   (1 << 0)

Definition at line 42 of file heapam.h.

◆ HEAP_PAGE_PRUNE_SET_VM

#define HEAP_PAGE_PRUNE_SET_VM   (1 << 3)

Definition at line 45 of file heapam.h.

◆ MaxLockTupleMode

#define MaxLockTupleMode   LockTupleExclusive

Definition at line 53 of file heapam.h.

Typedef Documentation

◆ BatchMVCCState

◆ BitmapHeapScanDesc

Definition at line 115 of file heapam.h.

◆ BitmapHeapScanDescData

◆ BulkInsertState

Definition at line 47 of file heapam.h.

◆ GlobalVisState

Definition at line 48 of file heapam.h.

◆ HeapPageFreeze

◆ HeapScanDesc

Definition at line 107 of file heapam.h.

◆ HeapScanDescData

◆ HeapTupleFreeze

◆ IndexFetchHeapData

◆ PruneFreezeParams

◆ PruneFreezeResult

◆ TupleTableSlot

Definition at line 49 of file heapam.h.

◆ VacuumCutoffs

Definition at line 50 of file heapam.h.

◆ VacuumParams

Definition at line 51 of file heapam.h.

Enumeration Type Documentation

◆ HTSV_Result

Enumerator
HEAPTUPLE_DEAD 
HEAPTUPLE_LIVE 
HEAPTUPLE_RECENTLY_DEAD 
HEAPTUPLE_INSERT_IN_PROGRESS 
HEAPTUPLE_DELETE_IN_PROGRESS 

Definition at line 136 of file heapam.h.

137{
138 HEAPTUPLE_DEAD, /* tuple is dead and deletable */
139 HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
140 HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
141 HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
142 HEAPTUPLE_DELETE_IN_PROGRESS, /* deleting xact is still in progress */
HTSV_Result
Definition heapam.h:137
@ HEAPTUPLE_RECENTLY_DEAD
Definition heapam.h:140
@ HEAPTUPLE_INSERT_IN_PROGRESS
Definition heapam.h:141
@ HEAPTUPLE_LIVE
Definition heapam.h:139
@ HEAPTUPLE_DELETE_IN_PROGRESS
Definition heapam.h:142
@ HEAPTUPLE_DEAD
Definition heapam.h:138

◆ PruneReason

Enumerator
PRUNE_ON_ACCESS 
PRUNE_VACUUM_SCAN 
PRUNE_VACUUM_CLEANUP 

Definition at line 250 of file heapam.h.

251{
252 PRUNE_ON_ACCESS, /* on-access pruning */
253 PRUNE_VACUUM_SCAN, /* VACUUM 1st heap pass */
254 PRUNE_VACUUM_CLEANUP, /* VACUUM 2nd heap pass */
PruneReason
Definition heapam.h:251
@ PRUNE_VACUUM_CLEANUP
Definition heapam.h:254
@ PRUNE_ON_ACCESS
Definition heapam.h:252
@ PRUNE_VACUUM_SCAN
Definition heapam.h:253

Function Documentation

◆ FreeBulkInsertState()

void FreeBulkInsertState ( BulkInsertState  bistate)
extern

Definition at line 1954 of file heapam.c.

1955{
1956 if (bistate->current_buf != InvalidBuffer)
1957 ReleaseBuffer(bistate->current_buf);
1958 FreeAccessStrategy(bistate->strategy);
1959 pfree(bistate);
1960}
#define InvalidBuffer
Definition buf.h:25
void ReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5586
void FreeAccessStrategy(BufferAccessStrategy strategy)
Definition freelist.c:608
void pfree(void *pointer)
Definition mcxt.c:1616
BufferAccessStrategy strategy
Definition hio.h:31
Buffer current_buf
Definition hio.h:32

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

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

◆ GetBulkInsertState()

BulkInsertState GetBulkInsertState ( void  )
extern

Definition at line 1937 of file heapam.c.

1938{
1939 BulkInsertState bistate;
1940
1943 bistate->current_buf = InvalidBuffer;
1944 bistate->next_free = InvalidBlockNumber;
1945 bistate->last_free = InvalidBlockNumber;
1946 bistate->already_extended_by = 0;
1947 return bistate;
1948}
#define InvalidBlockNumber
Definition block.h:33
@ BAS_BULKWRITE
Definition bufmgr.h:39
#define palloc_object(type)
Definition fe_memutils.h:74
BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype)
Definition freelist.c:426
struct BulkInsertStateData * BulkInsertState
Definition heapam.h:47
BlockNumber last_free
Definition hio.h:49
uint32 already_extended_by
Definition hio.h:50
BlockNumber next_free
Definition hio.h:48

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

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

◆ heap_abort_speculative()

void heap_abort_speculative ( Relation  relation,
const ItemPointerData tid 
)
extern

Definition at line 6150 of file heapam.c.

6151{
6153 ItemId lp;
6154 HeapTupleData tp;
6155 Page page;
6156 BlockNumber block;
6157 Buffer buffer;
6158
6160
6161 block = ItemPointerGetBlockNumber(tid);
6162 buffer = ReadBuffer(relation, block);
6163 page = BufferGetPage(buffer);
6164
6166
6167 /*
6168 * Page can't be all visible, we just inserted into it, and are still
6169 * running.
6170 */
6171 Assert(!PageIsAllVisible(page));
6172
6175
6176 tp.t_tableOid = RelationGetRelid(relation);
6177 tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
6178 tp.t_len = ItemIdGetLength(lp);
6179 tp.t_self = *tid;
6180
6181 /*
6182 * Sanity check that the tuple really is a speculatively inserted tuple,
6183 * inserted by us.
6184 */
6185 if (tp.t_data->t_choice.t_heap.t_xmin != xid)
6186 elog(ERROR, "attempted to kill a tuple inserted by another transaction");
6187 if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
6188 elog(ERROR, "attempted to kill a non-speculative tuple");
6190
6191 /*
6192 * No need to check for serializable conflicts here. There is never a
6193 * need for a combo CID, either. No need to extract replica identity, or
6194 * do anything special with infomask bits.
6195 */
6196
6198
6199 /*
6200 * The tuple will become DEAD immediately. Flag that this page is a
6201 * candidate for pruning by setting xmin to TransactionXmin. While not
6202 * immediately prunable, it is the oldest xid we can cheaply determine
6203 * that's safe against wraparound / being older than the table's
6204 * relfrozenxid. To defend against the unlikely case of a new relation
6205 * having a newer relfrozenxid than our TransactionXmin, use relfrozenxid
6206 * if so (vacuum can't subsequently move relfrozenxid to beyond
6207 * TransactionXmin, so there's no race here).
6208 */
6210 {
6211 TransactionId relfrozenxid = relation->rd_rel->relfrozenxid;
6213
6214 if (TransactionIdPrecedes(TransactionXmin, relfrozenxid))
6215 prune_xid = relfrozenxid;
6216 else
6219 }
6220
6221 /* store transaction information of xact deleting the tuple */
6224
6225 /*
6226 * Set the tuple header xmin to InvalidTransactionId. This makes the
6227 * tuple immediately invisible everyone. (In particular, to any
6228 * transactions waiting on the speculative token, woken up later.)
6229 */
6231
6232 /* Clear the speculative insertion token too */
6233 tp.t_data->t_ctid = tp.t_self;
6234
6235 MarkBufferDirty(buffer);
6236
6237 /*
6238 * XLOG stuff
6239 *
6240 * The WAL records generated here match heap_delete(). The same recovery
6241 * routines are used.
6242 */
6243 if (RelationNeedsWAL(relation))
6244 {
6247
6249 xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
6250 tp.t_data->t_infomask2);
6252 xlrec.xmax = xid;
6253
6257
6258 /* No replica identity & replication origin logged */
6259
6261
6262 PageSetLSN(page, recptr);
6263 }
6264
6266
6268
6269 if (HeapTupleHasExternal(&tp))
6270 {
6271 Assert(!IsToastRelation(relation));
6272 heap_toast_delete(relation, &tp, true);
6273 }
6274
6275 /*
6276 * Never need to mark tuple for invalidation, since catalogs don't support
6277 * speculative insertion
6278 */
6279
6280 /* Now we can release the buffer */
6281 ReleaseBuffer(buffer);
6282
6283 /* count deletion, as we counted the insertion too */
6284 pgstat_count_heap_delete(relation);
6285}
uint32 BlockNumber
Definition block.h:31
int Buffer
Definition buf.h:23
void MarkBufferDirty(Buffer buffer)
Definition bufmgr.c:3147
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition bufmgr.c:879
static Page BufferGetPage(Buffer buffer)
Definition bufmgr.h:468
@ BUFFER_LOCK_EXCLUSIVE
Definition bufmgr.h:222
@ BUFFER_LOCK_UNLOCK
Definition bufmgr.h:207
static void LockBuffer(Buffer buffer, BufferLockMode mode)
Definition bufmgr.h:334
static bool PageIsAllVisible(const PageData *page)
Definition bufpage.h:454
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition bufpage.h:268
static void * PageGetItem(PageData *page, const ItemIdData *itemId)
Definition bufpage.h:378
static void PageSetLSN(Page page, XLogRecPtr lsn)
Definition bufpage.h:416
PageData * Page
Definition bufpage.h:81
#define PageSetPrunable(page, xid)
Definition bufpage.h:478
#define Assert(condition)
Definition c.h:943
uint32 TransactionId
Definition c.h:736
bool IsToastRelation(Relation relation)
Definition catalog.c:206
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
static uint8 compute_infobits(uint16 infomask, uint16 infomask2)
Definition heapam.c:2672
#define XLOG_HEAP_DELETE
Definition heapam_xlog.h:34
#define SizeOfHeapDelete
#define XLH_DELETE_IS_SUPER
void heap_toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
Definition heaptoast.c:43
HeapTupleHeaderData * HeapTupleHeader
Definition htup.h:23
static bool HeapTupleHasExternal(const HeapTupleData *tuple)
#define HEAP_XMAX_BITS
static bool HeapTupleHeaderIsHeapOnly(const HeapTupleHeaderData *tup)
#define HEAP_MOVED
static bool HeapTupleHeaderIsSpeculative(const HeapTupleHeaderData *tup)
static void HeapTupleHeaderSetXmin(HeapTupleHeaderData *tup, TransactionId xid)
#define ItemIdGetLength(itemId)
Definition itemid.h:59
#define ItemIdIsNormal(itemId)
Definition itemid.h:99
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
Definition itemptr.h:124
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition itemptr.h:103
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition itemptr.h:83
#define START_CRIT_SECTION()
Definition miscadmin.h:152
#define END_CRIT_SECTION()
Definition miscadmin.h:154
void pgstat_count_heap_delete(Relation rel)
static int fb(int x)
#define RelationGetRelid(relation)
Definition rel.h:516
#define RelationNeedsWAL(relation)
Definition rel.h:639
TransactionId TransactionXmin
Definition snapmgr.c:159
ItemPointerData t_self
Definition htup.h:65
uint32 t_len
Definition htup.h:64
HeapTupleHeader t_data
Definition htup.h:68
Oid t_tableOid
Definition htup.h:66
TransactionId t_xmin
union HeapTupleHeaderData::@54 t_choice
ItemPointerData t_ctid
HeapTupleFields t_heap
Form_pg_class rd_rel
Definition rel.h:111
#define InvalidTransactionId
Definition transam.h:31
#define TransactionIdIsValid(xid)
Definition transam.h:41
static bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition transam.h:263
TransactionId GetCurrentTransactionId(void)
Definition xact.c:456
uint64 XLogRecPtr
Definition xlogdefs.h:21
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition xloginsert.c:482
void XLogRegisterData(const void *data, uint32 len)
Definition xloginsert.c:372
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition xloginsert.c:246
void XLogBeginInsert(void)
Definition xloginsert.c:153
#define REGBUF_STANDARD
Definition xloginsert.h:35

References Assert, BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferGetPage(), compute_infobits(), elog, END_CRIT_SECTION, ERROR, fb(), xl_heap_delete::flags, GetCurrentTransactionId(), HEAP_MOVED, heap_toast_delete(), HEAP_XMAX_BITS, HeapTupleHasExternal(), HeapTupleHeaderIsHeapOnly(), HeapTupleHeaderIsSpeculative(), HeapTupleHeaderSetXmin(), InvalidTransactionId, IsToastRelation(), ItemIdGetLength, ItemIdIsNormal, ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), ItemPointerIsValid(), LockBuffer(), MarkBufferDirty(), 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(), and XLogRegisterData().

Referenced by heapam_tuple_complete_speculative(), and toast_delete_datum().

◆ heap_beginscan()

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

Definition at line 1167 of file heapam.c.

1171{
1172 HeapScanDesc scan;
1173
1174 /*
1175 * increment relation ref count while scanning relation
1176 *
1177 * This is just to make really sure the relcache entry won't go away while
1178 * the scan has a pointer to it. Caller should be holding the rel open
1179 * anyway, so this is redundant in all normal scenarios...
1180 */
1182
1183 /*
1184 * allocate and initialize scan descriptor
1185 */
1186 if (flags & SO_TYPE_BITMAPSCAN)
1187 {
1189
1190 /*
1191 * Bitmap Heap scans do not have any fields that a normal Heap Scan
1192 * does not have, so no special initializations required here.
1193 */
1194 scan = (HeapScanDesc) bscan;
1195 }
1196 else
1198
1199 scan->rs_base.rs_rd = relation;
1200 scan->rs_base.rs_snapshot = snapshot;
1201 scan->rs_base.rs_nkeys = nkeys;
1202 scan->rs_base.rs_flags = flags;
1203 scan->rs_base.rs_parallel = parallel_scan;
1204 scan->rs_base.rs_instrument = NULL;
1205 scan->rs_strategy = NULL; /* set in initscan */
1206 scan->rs_cbuf = InvalidBuffer;
1207
1208 /*
1209 * Disable page-at-a-time mode if it's not a MVCC-safe snapshot.
1210 */
1211 if (!(snapshot && IsMVCCSnapshot(snapshot)))
1213
1214 /* Check that a historic snapshot is not used for non-catalog tables */
1215 if (snapshot &&
1216 IsHistoricMVCCSnapshot(snapshot) &&
1218 {
1219 ereport(ERROR,
1221 errmsg("cannot query non-catalog table \"%s\" during logical decoding",
1222 RelationGetRelationName(relation))));
1223 }
1224
1225 /*
1226 * For seqscan and sample scans in a serializable transaction, acquire a
1227 * predicate lock on the entire relation. This is required not only to
1228 * lock all the matching tuples, but also to conflict with new insertions
1229 * into the table. In an indexscan, we take page locks on the index pages
1230 * covering the range specified in the scan qual, but in a heap scan there
1231 * is nothing more fine-grained to lock. A bitmap scan is a different
1232 * story, there we have already scanned the index and locked the index
1233 * pages covering the predicate. But in that case we still have to lock
1234 * any matching heap tuples. For sample scan we could optimize the locking
1235 * to be at least page-level granularity, but we'd need to add per-tuple
1236 * locking for that.
1237 */
1239 {
1240 /*
1241 * Ensure a missing snapshot is noticed reliably, even if the
1242 * isolation mode means predicate locking isn't performed (and
1243 * therefore the snapshot isn't used here).
1244 */
1245 Assert(snapshot);
1246 PredicateLockRelation(relation, snapshot);
1247 }
1248
1249 /* we only need to set this up once */
1250 scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
1251
1252 /*
1253 * Allocate memory to keep track of page allocation for parallel workers
1254 * when doing a parallel scan.
1255 */
1256 if (parallel_scan != NULL)
1258 else
1260
1261 /*
1262 * we do this here instead of in initscan() because heap_rescan also calls
1263 * initscan() and we don't want to allocate memory again
1264 */
1265 if (nkeys > 0)
1266 scan->rs_base.rs_key = palloc_array(ScanKeyData, nkeys);
1267 else
1268 scan->rs_base.rs_key = NULL;
1269
1270 initscan(scan, key, false);
1271
1272 scan->rs_read_stream = NULL;
1273
1274 /*
1275 * Set up a read stream for sequential scans and TID range scans. This
1276 * should be done after initscan() because initscan() allocates the
1277 * BufferAccessStrategy object passed to the read stream API.
1278 */
1279 if (scan->rs_base.rs_flags & SO_TYPE_SEQSCAN ||
1281 {
1283
1284 if (scan->rs_base.rs_parallel)
1286 else
1288
1289 /* ---
1290 * It is safe to use batchmode as the only locks taken by `cb`
1291 * are never taken while waiting for IO:
1292 * - SyncScanLock is used in the non-parallel case
1293 * - in the parallel case, only spinlocks and atomics are used
1294 * ---
1295 */
1298 scan->rs_strategy,
1299 scan->rs_base.rs_rd,
1301 cb,
1302 scan,
1303 0);
1304 }
1305 else if (scan->rs_base.rs_flags & SO_TYPE_BITMAPSCAN)
1306 {
1309 scan->rs_strategy,
1310 scan->rs_base.rs_rd,
1313 scan,
1314 sizeof(TBMIterateResult));
1315 }
1316
1317 /* enable read stream instrumentation */
1318 if ((flags & SO_SCAN_INSTRUMENT) && (scan->rs_read_stream != NULL))
1319 {
1322 &scan->rs_base.rs_instrument->io);
1323 }
1324
1325 scan->rs_vmbuffer = InvalidBuffer;
1326
1327 return (TableScanDesc) scan;
1328}
int errcode(int sqlerrcode)
Definition elog.c:874
#define ereport(elevel,...)
Definition elog.h:152
#define palloc_array(type, count)
Definition fe_memutils.h:76
#define palloc0_object(type)
Definition fe_memutils.h:75
static BlockNumber heap_scan_stream_read_next_parallel(ReadStream *stream, void *callback_private_data, void *per_buffer_data)
Definition heapam.c:254
static BlockNumber heap_scan_stream_read_next_serial(ReadStream *stream, void *callback_private_data, void *per_buffer_data)
Definition heapam.c:294
static BlockNumber bitmapheap_stream_read_next(ReadStream *pgsr, void *private_data, void *per_buffer_data)
Definition heapam.c:319
static void initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
Definition heapam.c:359
struct HeapScanDescData * HeapScanDesc
Definition heapam.h:107
static char * errmsg
void PredicateLockRelation(Relation relation, Snapshot snapshot)
Definition predicate.c:2506
ReadStream * read_stream_begin_relation(int flags, BufferAccessStrategy strategy, Relation rel, ForkNumber forknum, ReadStreamBlockNumberCB callback, void *callback_private_data, size_t per_buffer_data_size)
void read_stream_enable_stats(ReadStream *stream, IOStats *stats)
#define READ_STREAM_USE_BATCHING
Definition read_stream.h:64
BlockNumber(* ReadStreamBlockNumberCB)(ReadStream *stream, void *callback_private_data, void *per_buffer_data)
Definition read_stream.h:78
#define READ_STREAM_DEFAULT
Definition read_stream.h:21
#define READ_STREAM_SEQUENTIAL
Definition read_stream.h:36
#define RelationGetRelationName(relation)
Definition rel.h:550
#define RelationIsAccessibleInLogicalDecoding(relation)
Definition rel.h:695
void RelationIncrementReferenceCount(Relation rel)
Definition relcache.c:2177
@ MAIN_FORKNUM
Definition relpath.h:58
#define IsHistoricMVCCSnapshot(snapshot)
Definition snapmgr.h:67
#define IsMVCCSnapshot(snapshot)
Definition snapmgr.h:59
Buffer rs_vmbuffer
Definition heapam.h:100
BufferAccessStrategy rs_strategy
Definition heapam.h:75
Buffer rs_cbuf
Definition heapam.h:72
ParallelBlockTableScanWorkerData * rs_parallelworkerdata
Definition heapam.h:97
HeapTupleData rs_ctup
Definition heapam.h:77
ReadStream * rs_read_stream
Definition heapam.h:80
TableScanDescData rs_base
Definition heapam.h:60
Relation rs_rd
Definition relscan.h:36
struct TableScanInstrumentation * rs_instrument
Definition relscan.h:72
uint32 rs_flags
Definition relscan.h:64
struct ScanKeyData * rs_key
Definition relscan.h:39
struct SnapshotData * rs_snapshot
Definition relscan.h:37
struct ParallelTableScanDescData * rs_parallel
Definition relscan.h:66
@ SO_TYPE_TIDRANGESCAN
Definition tableam.h:56
@ SO_TYPE_SAMPLESCAN
Definition tableam.h:54
@ SO_TYPE_SEQSCAN
Definition tableam.h:52
@ SO_SCAN_INSTRUMENT
Definition tableam.h:74
@ SO_TYPE_BITMAPSCAN
Definition tableam.h:53

References Assert, bitmapheap_stream_read_next(), ereport, errcode(), errmsg, ERROR, fb(), heap_scan_stream_read_next_parallel(), heap_scan_stream_read_next_serial(), initscan(), InvalidBuffer, TableScanInstrumentation::io, IsHistoricMVCCSnapshot, IsMVCCSnapshot, MAIN_FORKNUM, palloc0_object, palloc_array, palloc_object, PredicateLockRelation(), read_stream_begin_relation(), READ_STREAM_DEFAULT, read_stream_enable_stats(), READ_STREAM_SEQUENTIAL, READ_STREAM_USE_BATCHING, RelationGetRelationName, RelationGetRelid, RelationIncrementReferenceCount(), RelationIsAccessibleInLogicalDecoding, HeapScanDescData::rs_base, HeapScanDescData::rs_cbuf, HeapScanDescData::rs_ctup, TableScanDescData::rs_flags, TableScanDescData::rs_instrument, TableScanDescData::rs_key, TableScanDescData::rs_nkeys, TableScanDescData::rs_parallel, HeapScanDescData::rs_parallelworkerdata, TableScanDescData::rs_rd, HeapScanDescData::rs_read_stream, TableScanDescData::rs_snapshot, HeapScanDescData::rs_strategy, HeapScanDescData::rs_vmbuffer, SO_SCAN_INSTRUMENT, SO_TYPE_BITMAPSCAN, SO_TYPE_SAMPLESCAN, SO_TYPE_SEQSCAN, SO_TYPE_TIDRANGESCAN, and HeapTupleData::t_tableOid.

◆ heap_delete()

TM_Result heap_delete ( Relation  relation,
const ItemPointerData tid,
CommandId  cid,
uint32  options,
Snapshot  crosscheck,
bool  wait,
TM_FailureData tmfd 
)
extern

Definition at line 2717 of file heapam.c.

2720{
2723 ItemId lp;
2724 HeapTupleData tp;
2725 Page page;
2726 BlockNumber block;
2727 Buffer buffer;
2728 Buffer vmbuffer = InvalidBuffer;
2729 TransactionId new_xmax;
2734 bool have_tuple_lock = false;
2735 bool iscombo;
2736 bool all_visible_cleared = false;
2737 HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
2738 bool old_key_copied = false;
2739
2741
2742 AssertHasSnapshotForToast(relation);
2743
2744 /*
2745 * Forbid this during a parallel operation, lest it allocate a combo CID.
2746 * Other workers might need that combo CID for visibility checks, and we
2747 * have no provision for broadcasting it to them.
2748 */
2749 if (IsInParallelMode())
2750 ereport(ERROR,
2752 errmsg("cannot delete tuples during a parallel operation")));
2753
2754 block = ItemPointerGetBlockNumber(tid);
2755 buffer = ReadBuffer(relation, block);
2756 page = BufferGetPage(buffer);
2757
2758 /*
2759 * Before locking the buffer, pin the visibility map page if it appears to
2760 * be necessary. Since we haven't got the lock yet, someone else might be
2761 * in the middle of changing this, so we'll need to recheck after we have
2762 * the lock.
2763 */
2764 if (PageIsAllVisible(page))
2765 visibilitymap_pin(relation, block, &vmbuffer);
2766
2768
2771
2772 tp.t_tableOid = RelationGetRelid(relation);
2773 tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2774 tp.t_len = ItemIdGetLength(lp);
2775 tp.t_self = *tid;
2776
2777l1:
2778
2779 /*
2780 * If we didn't pin the visibility map page and the page has become all
2781 * visible while we were busy locking the buffer, we'll have to unlock and
2782 * re-lock, to avoid holding the buffer lock across an I/O. That's a bit
2783 * unfortunate, but hopefully shouldn't happen often.
2784 */
2785 if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
2786 {
2788 visibilitymap_pin(relation, block, &vmbuffer);
2790 }
2791
2792 result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
2793
2794 if (result == TM_Invisible)
2795 {
2796 UnlockReleaseBuffer(buffer);
2797 ereport(ERROR,
2799 errmsg("attempted to delete invisible tuple")));
2800 }
2801 else if (result == TM_BeingModified && wait)
2802 {
2805
2806 /* must copy state data before unlocking buffer */
2809
2810 /*
2811 * Sleep until concurrent transaction ends -- except when there's a
2812 * single locker and it's our own transaction. Note we don't care
2813 * which lock mode the locker has, because we need the strongest one.
2814 *
2815 * Before sleeping, we need to acquire tuple lock to establish our
2816 * priority for the tuple (see heap_lock_tuple). LockTuple will
2817 * release us when we are next-in-line for the tuple.
2818 *
2819 * If we are forced to "start over" below, we keep the tuple lock;
2820 * this arranges that we stay at the head of the line while rechecking
2821 * tuple state.
2822 */
2824 {
2825 bool current_is_member = false;
2826
2829 {
2831
2832 /*
2833 * Acquire the lock, if necessary (but skip it when we're
2834 * requesting a lock and already have one; avoids deadlock).
2835 */
2836 if (!current_is_member)
2839
2840 /* wait for multixact */
2842 relation, &(tp.t_self), XLTW_Delete,
2843 NULL);
2845
2846 /*
2847 * If xwait had just locked the tuple then some other xact
2848 * could update this tuple before we get to this point. Check
2849 * for xmax change, and start over if so.
2850 *
2851 * We also must start over if we didn't pin the VM page, and
2852 * the page has become all visible.
2853 */
2854 if ((vmbuffer == InvalidBuffer && PageIsAllVisible(page)) ||
2857 xwait))
2858 goto l1;
2859 }
2860
2861 /*
2862 * You might think the multixact is necessarily done here, but not
2863 * so: it could have surviving members, namely our own xact or
2864 * other subxacts of this backend. It is legal for us to delete
2865 * the tuple in either case, however (the latter case is
2866 * essentially a situation of upgrading our former shared lock to
2867 * exclusive). We don't bother changing the on-disk hint bits
2868 * since we are about to overwrite the xmax altogether.
2869 */
2870 }
2872 {
2873 /*
2874 * Wait for regular transaction to end; but first, acquire tuple
2875 * lock.
2876 */
2880 XactLockTableWait(xwait, relation, &(tp.t_self), XLTW_Delete);
2882
2883 /*
2884 * xwait is done, but if xwait had just locked the tuple then some
2885 * other xact could update this tuple before we get to this point.
2886 * Check for xmax change, and start over if so.
2887 *
2888 * We also must start over if we didn't pin the VM page, and the
2889 * page has become all visible.
2890 */
2891 if ((vmbuffer == InvalidBuffer && PageIsAllVisible(page)) ||
2894 xwait))
2895 goto l1;
2896
2897 /* Otherwise check if it committed or aborted */
2898 UpdateXmaxHintBits(tp.t_data, buffer, xwait);
2899 }
2900
2901 /*
2902 * We may overwrite if previous xmax aborted, or if it committed but
2903 * only locked the tuple without updating it.
2904 */
2905 if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
2908 result = TM_Ok;
2909 else if (!ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
2911 else
2913 }
2914
2915 /* sanity check the result HeapTupleSatisfiesUpdate() and the logic above */
2916 if (result != TM_Ok)
2917 {
2919 result == TM_Updated ||
2920 result == TM_Deleted ||
2925 }
2926
2928 {
2929 /* Perform additional check for transaction-snapshot mode RI updates */
2930 if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
2932 }
2933
2934 if (result != TM_Ok)
2935 {
2936 tmfd->ctid = tp.t_data->t_ctid;
2938 if (result == TM_SelfModified)
2940 else
2941 tmfd->cmax = InvalidCommandId;
2942 UnlockReleaseBuffer(buffer);
2943 if (have_tuple_lock)
2945 if (vmbuffer != InvalidBuffer)
2946 ReleaseBuffer(vmbuffer);
2947 return result;
2948 }
2949
2950 /*
2951 * We're about to do the actual delete -- check for conflict first, to
2952 * avoid possibly having to roll back work we've just done.
2953 *
2954 * This is safe without a recheck as long as there is no possibility of
2955 * another process scanning the page between this check and the delete
2956 * being visible to the scan (i.e., an exclusive buffer content lock is
2957 * continuously held from this point until the tuple delete is visible).
2958 */
2960
2961 /* replace cid with a combo CID if necessary */
2963
2964 /*
2965 * Compute replica identity tuple before entering the critical section so
2966 * we don't PANIC upon a memory allocation failure.
2967 */
2969 ExtractReplicaIdentity(relation, &tp, true, &old_key_copied) : NULL;
2970
2971 /*
2972 * If this is the first possibly-multixact-able operation in the current
2973 * transaction, set my per-backend OldestMemberMXactId setting. We can be
2974 * certain that the transaction will never become a member of any older
2975 * MultiXactIds than that. (We have to do this even if we end up just
2976 * using our own TransactionId below, since some other backend could
2977 * incorporate our XID into a MultiXact immediately afterwards.)
2978 */
2980
2983 xid, LockTupleExclusive, true,
2984 &new_xmax, &new_infomask, &new_infomask2);
2985
2987
2988 /*
2989 * If this transaction commits, the tuple will become DEAD sooner or
2990 * later. Set flag that this page is a candidate for pruning once our xid
2991 * falls below the OldestXmin horizon. If the transaction finally aborts,
2992 * the subsequent page pruning will be a no-op and the hint will be
2993 * cleared.
2994 */
2995 PageSetPrunable(page, xid);
2996
2997 if (PageIsAllVisible(page))
2998 {
2999 all_visible_cleared = true;
3000 PageClearAllVisible(page);
3001 visibilitymap_clear(relation, BufferGetBlockNumber(buffer),
3002 vmbuffer, VISIBILITYMAP_VALID_BITS);
3003 }
3004
3005 /* store transaction information of xact deleting the tuple */
3011 HeapTupleHeaderSetXmax(tp.t_data, new_xmax);
3013 /* Make sure there is no forward chain link in t_ctid */
3014 tp.t_data->t_ctid = tp.t_self;
3015
3016 /* Signal that this is actually a move into another partition */
3017 if (changingPart)
3019
3020 MarkBufferDirty(buffer);
3021
3022 /*
3023 * XLOG stuff
3024 *
3025 * NB: heap_abort_speculative() uses the same xlog record and replay
3026 * routines.
3027 */
3028 if (RelationNeedsWAL(relation))
3029 {
3033
3034 /*
3035 * For logical decode we need combo CIDs to properly decode the
3036 * catalog
3037 */
3039 log_heap_new_cid(relation, &tp);
3040
3041 xlrec.flags = 0;
3044 if (changingPart)
3046 xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
3047 tp.t_data->t_infomask2);
3049 xlrec.xmax = new_xmax;
3050
3051 if (old_key_tuple != NULL)
3052 {
3053 if (relation->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
3055 else
3057 }
3058
3059 /*
3060 * Mark the change as not-for-logical-decoding if caller requested so.
3061 *
3062 * (This is used for changes that affect relations not visible to
3063 * other transactions, such as the transient table during concurrent
3064 * repack.)
3065 */
3066 if (!walLogical)
3068
3071
3073
3074 /*
3075 * Log replica identity of the deleted tuple if there is one
3076 */
3077 if (old_key_tuple != NULL)
3078 {
3079 xlhdr.t_infomask2 = old_key_tuple->t_data->t_infomask2;
3080 xlhdr.t_infomask = old_key_tuple->t_data->t_infomask;
3081 xlhdr.t_hoff = old_key_tuple->t_data->t_hoff;
3082
3084 XLogRegisterData((char *) old_key_tuple->t_data
3086 old_key_tuple->t_len
3088 }
3089
3090 /* filtering by origin on a row level is much more efficient */
3092
3094
3095 PageSetLSN(page, recptr);
3096 }
3097
3099
3101
3102 if (vmbuffer != InvalidBuffer)
3103 ReleaseBuffer(vmbuffer);
3104
3105 /*
3106 * If the tuple has toasted out-of-line attributes, we need to delete
3107 * those items too. We have to do this before releasing the buffer
3108 * because we need to look at the contents of the tuple, but it's OK to
3109 * release the content lock on the buffer first.
3110 */
3111 if (relation->rd_rel->relkind != RELKIND_RELATION &&
3112 relation->rd_rel->relkind != RELKIND_MATVIEW)
3113 {
3114 /* toast table entries should never be recursively toasted */
3116 }
3117 else if (HeapTupleHasExternal(&tp))
3118 heap_toast_delete(relation, &tp, false);
3119
3120 /*
3121 * Mark tuple for invalidation from system caches at next command
3122 * boundary. We have to do this before releasing the buffer because we
3123 * need to look at the contents of the tuple.
3124 */
3125 CacheInvalidateHeapTuple(relation, &tp, NULL);
3126
3127 /* Now we can release the buffer */
3128 ReleaseBuffer(buffer);
3129
3130 /*
3131 * Release the lmgr tuple lock, if we had it.
3132 */
3133 if (have_tuple_lock)
3135
3136 pgstat_count_heap_delete(relation);
3137
3140
3141 return TM_Ok;
3142}
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition bufmgr.c:4446
void UnlockReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5603
static void PageClearAllVisible(Page page)
Definition bufpage.h:464
#define InvalidCommandId
Definition c.h:753
TransactionId MultiXactId
Definition c.h:746
uint16_t uint16
Definition c.h:623
uint32 result
void HeapTupleHeaderAdjustCmax(const HeapTupleHeaderData *tup, CommandId *cmax, bool *iscombo)
Definition combocid.c:153
CommandId HeapTupleHeaderGetCmax(const HeapTupleHeaderData *tup)
Definition combocid.c:118
static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask, LockTupleMode lockmode, bool *current_is_member)
Definition heapam.c:7576
static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup)
Definition heapam.c:8998
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:5290
static bool heap_acquire_tuplock(Relation relation, const ItemPointerData *tid, LockTupleMode mode, LockWaitPolicy wait_policy, bool *have_tuple_lock)
Definition heapam.c:5241
static HeapTuple ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool key_required, bool *copy)
Definition heapam.c:9079
static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, const ItemPointerData *ctid, XLTW_Oper oper, int *remaining)
Definition heapam.c:7754
static bool xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
Definition heapam.c:2694
#define UnlockTupleTuplock(rel, tup, mode)
Definition heapam.c:171
static void AssertHasSnapshotForToast(Relation rel)
Definition heapam.c:227
static void UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
Definition heapam.c:1915
bool HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
TM_Result HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, Buffer buffer)
#define XLH_DELETE_CONTAINS_OLD_KEY
#define XLH_DELETE_ALL_VISIBLE_CLEARED
#define SizeOfHeapHeader
#define XLH_DELETE_IS_PARTITION_MOVE
#define XLH_DELETE_CONTAINS_OLD_TUPLE
#define XLH_DELETE_NO_LOGICAL
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
#define SizeofHeapTupleHeader
static bool HEAP_XMAX_IS_LOCKED_ONLY(uint16 infomask)
static void HeapTupleHeaderSetCmax(HeapTupleHeaderData *tup, CommandId cid, bool iscombo)
static void HeapTupleHeaderClearHotUpdated(HeapTupleHeaderData *tup)
static TransactionId HeapTupleHeaderGetRawXmax(const HeapTupleHeaderData *tup)
#define HEAP_XMAX_IS_MULTI
#define HEAP_XMAX_INVALID
static TransactionId HeapTupleHeaderGetUpdateXid(const HeapTupleHeaderData *tup)
static void HeapTupleHeaderSetMovedPartitions(HeapTupleHeaderData *tup)
static void HeapTupleHeaderSetXmax(HeapTupleHeaderData *tup, TransactionId xid)
void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple)
Definition inval.c:1571
bool ItemPointerEquals(const ItemPointerData *pointer1, const ItemPointerData *pointer2)
Definition itemptr.c:35
void XactLockTableWait(TransactionId xid, Relation rel, const ItemPointerData *ctid, XLTW_Oper oper)
Definition lmgr.c:663
@ XLTW_Delete
Definition lmgr.h:28
@ LockWaitBlock
Definition lockoptions.h:40
@ LockTupleExclusive
Definition lockoptions.h:59
void MultiXactIdSetOldestMember(void)
Definition multixact.c:596
@ MultiXactStatusUpdate
Definition multixact.h:45
void CheckForSerializableConflictIn(Relation relation, const ItemPointerData *tid, BlockNumber blkno)
Definition predicate.c:4266
#define InvalidSnapshot
Definition snapshot.h:119
TransactionId xmax
Definition tableam.h:172
CommandId cmax
Definition tableam.h:173
ItemPointerData ctid
Definition tableam.h:171
TM_Result
Definition tableam.h:95
@ TM_Ok
Definition tableam.h:100
@ TM_BeingModified
Definition tableam.h:122
@ TM_Deleted
Definition tableam.h:115
@ TM_Updated
Definition tableam.h:112
@ TM_SelfModified
Definition tableam.h:106
@ TM_Invisible
Definition tableam.h:103
#define TABLE_DELETE_CHANGING_PARTITION
Definition tableam.h:289
#define TABLE_DELETE_NO_LOGICAL
Definition tableam.h:290
#define TransactionIdEquals(id1, id2)
Definition transam.h:43
bool visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer vmbuf, uint8 flags)
void visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *vmbuf)
#define VISIBILITYMAP_VALID_BITS
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition xact.c:943
bool IsInParallelMode(void)
Definition xact.c:1119
#define XLOG_INCLUDE_ORIGIN
Definition xlog.h:166
void XLogSetRecordFlags(uint8 flags)
Definition xloginsert.c:464

References Assert, AssertHasSnapshotForToast(), 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(), fb(), GetCurrentTransactionId(), heap_acquire_tuplock(), heap_freetuple(), 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(), InvalidBuffer, InvalidCommandId, InvalidSnapshot, IsInParallelMode(), ItemIdGetLength, ItemIdIsNormal, ItemPointerEquals(), ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), ItemPointerIsValid(), LockBuffer(), LockTupleExclusive, LockWaitBlock, log_heap_new_cid(), MarkBufferDirty(), MultiXactIdSetOldestMember(), MultiXactIdWait(), MultiXactStatusUpdate, PageClearAllVisible(), PageGetItem(), PageGetItemId(), PageIsAllVisible(), PageSetLSN(), PageSetPrunable, pgstat_count_heap_delete(), RelationData::rd_rel, ReadBuffer(), REGBUF_STANDARD, RelationGetRelid, RelationIsAccessibleInLogicalDecoding, RelationNeedsWAL, ReleaseBuffer(), result, SizeOfHeapDelete, SizeOfHeapHeader, SizeofHeapTupleHeader, START_CRIT_SECTION, HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleHeaderData::t_infomask, HeapTupleHeaderData::t_infomask2, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TABLE_DELETE_CHANGING_PARTITION, TABLE_DELETE_NO_LOGICAL, 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, XLH_DELETE_NO_LOGICAL, XLOG_HEAP_DELETE, XLOG_INCLUDE_ORIGIN, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), XLogRegisterData(), XLogSetRecordFlags(), XLTW_Delete, TM_FailureData::xmax, and xmax_infomask_changed().

Referenced by heapam_tuple_delete(), and simple_heap_delete().

◆ heap_endscan()

void heap_endscan ( TableScanDesc  sscan)
extern

Definition at line 1390 of file heapam.c.

1391{
1393
1394 /* Note: no locking manipulations needed */
1395
1396 /*
1397 * unpin scan buffers
1398 */
1399 if (BufferIsValid(scan->rs_cbuf))
1400 ReleaseBuffer(scan->rs_cbuf);
1401
1402 if (BufferIsValid(scan->rs_vmbuffer))
1404
1405 /*
1406 * Must free the read stream before freeing the BufferAccessStrategy.
1407 */
1408 if (scan->rs_read_stream)
1410
1411 /*
1412 * decrement relation reference count and free scan descriptor storage
1413 */
1415
1416 if (scan->rs_base.rs_key)
1417 pfree(scan->rs_base.rs_key);
1418
1419 if (scan->rs_strategy != NULL)
1421
1422 if (scan->rs_parallelworkerdata != NULL)
1424
1425 if (scan->rs_base.rs_flags & SO_TEMP_SNAPSHOT)
1427
1428 if (scan->rs_base.rs_instrument)
1430
1431 pfree(scan);
1432}
static bool BufferIsValid(Buffer bufnum)
Definition bufmgr.h:419
void read_stream_end(ReadStream *stream)
void RelationDecrementReferenceCount(Relation rel)
Definition relcache.c:2190
void UnregisterSnapshot(Snapshot snapshot)
Definition snapmgr.c:866
@ SO_TEMP_SNAPSHOT
Definition tableam.h:68

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

◆ heap_execute_freeze_tuple()

static void heap_execute_freeze_tuple ( HeapTupleHeader  tuple,
HeapTupleFreeze frz 
)
inlinestatic

Definition at line 533 of file heapam.h.

534{
535 HeapTupleHeaderSetXmax(tuple, frz->xmax);
536
537 if (frz->frzflags & XLH_FREEZE_XVAC)
539
540 if (frz->frzflags & XLH_INVALID_XVAC)
542
543 tuple->t_infomask = frz->t_infomask;
544 tuple->t_infomask2 = frz->t_infomask2;
545}
#define XLH_INVALID_XVAC
#define XLH_FREEZE_XVAC
static void HeapTupleHeaderSetXvac(HeapTupleHeaderData *tup, TransactionId xid)
#define FrozenTransactionId
Definition transam.h:33

References fb(), FrozenTransactionId, HeapTupleHeaderSetXmax(), HeapTupleHeaderSetXvac(), InvalidTransactionId, HeapTupleHeaderData::t_infomask, HeapTupleHeaderData::t_infomask2, XLH_FREEZE_XVAC, and XLH_INVALID_XVAC.

Referenced by heap_freeze_prepared_tuples(), heap_freeze_tuple(), and heap_xlog_prune_freeze().

◆ heap_fetch()

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

Definition at line 1684 of file heapam.c.

1689{
1690 ItemPointer tid = &(tuple->t_self);
1691 ItemId lp;
1692 Buffer buffer;
1693 Page page;
1694 OffsetNumber offnum;
1695 bool valid;
1696
1697 /*
1698 * Fetch and pin the appropriate page of the relation.
1699 */
1700 buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
1701
1702 /*
1703 * Need share lock on buffer to examine tuple commit status.
1704 */
1706 page = BufferGetPage(buffer);
1707
1708 /*
1709 * We'd better check for out-of-range offnum in case of VACUUM since the
1710 * TID was obtained.
1711 */
1712 offnum = ItemPointerGetOffsetNumber(tid);
1714 {
1715 UnlockReleaseBuffer(buffer);
1717 tuple->t_data = NULL;
1718 return false;
1719 }
1720
1721 /*
1722 * get the item line pointer corresponding to the requested tid
1723 */
1724 lp = PageGetItemId(page, offnum);
1725
1726 /*
1727 * Must check for deleted tuple.
1728 */
1729 if (!ItemIdIsNormal(lp))
1730 {
1731 UnlockReleaseBuffer(buffer);
1733 tuple->t_data = NULL;
1734 return false;
1735 }
1736
1737 /*
1738 * fill in *tuple fields
1739 */
1740 tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
1741 tuple->t_len = ItemIdGetLength(lp);
1742 tuple->t_tableOid = RelationGetRelid(relation);
1743
1744 /*
1745 * check tuple visibility, then release lock
1746 */
1747 valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
1748
1749 if (valid)
1750 PredicateLockTID(relation, &(tuple->t_self), snapshot,
1752
1753 HeapCheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
1754
1756
1757 if (valid)
1758 {
1759 /*
1760 * All checks passed, so return the tuple as valid. Caller is now
1761 * responsible for releasing the buffer.
1762 */
1763 *userbuf = buffer;
1764
1765 return true;
1766 }
1767
1768 /* Tuple failed time qual, but maybe caller wants to see it anyway. */
1769 if (keep_buf)
1770 *userbuf = buffer;
1771 else
1772 {
1773 ReleaseBuffer(buffer);
1775 tuple->t_data = NULL;
1776 }
1777
1778 return false;
1779}
@ BUFFER_LOCK_SHARE
Definition bufmgr.h:212
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition bufpage.h:396
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition heapam.c:9183
static TransactionId HeapTupleHeaderGetXmin(const HeapTupleHeaderData *tup)
uint16 OffsetNumber
Definition off.h:24
void PredicateLockTID(Relation relation, const ItemPointerData *tid, Snapshot snapshot, TransactionId tuple_xid)
Definition predicate.c:2551

References BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetPage(), fb(), 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 UnlockReleaseBuffer().

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

◆ heap_finish_speculative()

void heap_finish_speculative ( Relation  relation,
const ItemPointerData tid 
)
extern

Definition at line 6063 of file heapam.c.

6064{
6065 Buffer buffer;
6066 Page page;
6067 OffsetNumber offnum;
6068 ItemId lp;
6069 HeapTupleHeader htup;
6070
6071 buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
6073 page = BufferGetPage(buffer);
6074
6075 offnum = ItemPointerGetOffsetNumber(tid);
6077 elog(ERROR, "offnum out of range");
6078 lp = PageGetItemId(page, offnum);
6079 if (!ItemIdIsNormal(lp))
6080 elog(ERROR, "invalid lp");
6081
6082 htup = (HeapTupleHeader) PageGetItem(page, lp);
6083
6084 /* NO EREPORT(ERROR) from here till changes are logged */
6086
6088
6089 MarkBufferDirty(buffer);
6090
6091 /*
6092 * Replace the speculative insertion token with a real t_ctid, pointing to
6093 * itself like it does on regular tuples.
6094 */
6095 htup->t_ctid = *tid;
6096
6097 /* XLOG stuff */
6098 if (RelationNeedsWAL(relation))
6099 {
6102
6104
6106
6107 /* We want the same filtering on this as on a plain insert */
6109
6112
6114
6115 PageSetLSN(page, recptr);
6116 }
6117
6119
6120 UnlockReleaseBuffer(buffer);
6121}
#define SizeOfHeapConfirm
#define XLOG_HEAP_CONFIRM
Definition heapam_xlog.h:38
OffsetNumber offnum

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

Referenced by heapam_tuple_complete_speculative().

◆ heap_freeze_prepared_tuples()

void heap_freeze_prepared_tuples ( Buffer  buffer,
HeapTupleFreeze tuples,
int  ntuples 
)
extern

Definition at line 7360 of file heapam.c.

7361{
7362 Page page = BufferGetPage(buffer);
7363
7364 for (int i = 0; i < ntuples; i++)
7365 {
7366 HeapTupleFreeze *frz = tuples + i;
7367 ItemId itemid = PageGetItemId(page, frz->offset);
7368 HeapTupleHeader htup;
7369
7370 htup = (HeapTupleHeader) PageGetItem(page, itemid);
7372 }
7373}
static void heap_execute_freeze_tuple(HeapTupleHeader tuple, HeapTupleFreeze *frz)
Definition heapam.h:533
int i
Definition isn.c:77

References BufferGetPage(), fb(), heap_execute_freeze_tuple(), i, PageGetItem(), and PageGetItemId().

Referenced by heap_page_prune_and_freeze().

◆ heap_freeze_tuple()

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

Definition at line 7382 of file heapam.c.

7385{
7387 bool do_freeze;
7388 bool totally_frozen;
7389 struct VacuumCutoffs cutoffs;
7390 HeapPageFreeze pagefrz;
7391
7392 cutoffs.relfrozenxid = relfrozenxid;
7393 cutoffs.relminmxid = relminmxid;
7394 cutoffs.OldestXmin = FreezeLimit;
7395 cutoffs.OldestMxact = MultiXactCutoff;
7396 cutoffs.FreezeLimit = FreezeLimit;
7397 cutoffs.MultiXactCutoff = MultiXactCutoff;
7398
7399 pagefrz.freeze_required = true;
7400 pagefrz.FreezePageRelfrozenXid = FreezeLimit;
7401 pagefrz.FreezePageRelminMxid = MultiXactCutoff;
7402 pagefrz.FreezePageConflictXid = InvalidTransactionId;
7403 pagefrz.NoFreezePageRelfrozenXid = FreezeLimit;
7404 pagefrz.NoFreezePageRelminMxid = MultiXactCutoff;
7405
7406 do_freeze = heap_prepare_freeze_tuple(tuple, &cutoffs,
7407 &pagefrz, &frz, &totally_frozen);
7408
7409 /*
7410 * Note that because this is not a WAL-logged operation, we don't need to
7411 * fill in the offset in the freeze record.
7412 */
7413
7414 if (do_freeze)
7416 return do_freeze;
7417}
bool heap_prepare_freeze_tuple(HeapTupleHeader tuple, const struct VacuumCutoffs *cutoffs, HeapPageFreeze *pagefrz, HeapTupleFreeze *frz, bool *totally_frozen)
Definition heapam.c:7027
bool freeze_required
Definition heapam.h:194
TransactionId FreezeLimit
Definition vacuum.h:288
TransactionId relfrozenxid
Definition vacuum.h:262
MultiXactId relminmxid
Definition vacuum.h:263
MultiXactId MultiXactCutoff
Definition vacuum.h:289

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

Referenced by rewrite_heap_tuple().

◆ heap_get_latest_tid()

void heap_get_latest_tid ( TableScanDesc  sscan,
ItemPointer  tid 
)
extern

Definition at line 1793 of file heapam.c.

1795{
1796 Relation relation = sscan->rs_rd;
1797 Snapshot snapshot = sscan->rs_snapshot;
1798 ItemPointerData ctid;
1800
1801 /*
1802 * table_tuple_get_latest_tid() verified that the passed in tid is valid.
1803 * Assume that t_ctid links are valid however - there shouldn't be invalid
1804 * ones in the table.
1805 */
1807
1808 /*
1809 * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
1810 * need to examine, and *tid is the TID we will return if ctid turns out
1811 * to be bogus.
1812 *
1813 * Note that we will loop until we reach the end of the t_ctid chain.
1814 * Depending on the snapshot passed, there might be at most one visible
1815 * version of the row, but we don't try to optimize for that.
1816 */
1817 ctid = *tid;
1818 priorXmax = InvalidTransactionId; /* cannot check first XMIN */
1819 for (;;)
1820 {
1821 Buffer buffer;
1822 Page page;
1823 OffsetNumber offnum;
1824 ItemId lp;
1825 HeapTupleData tp;
1826 bool valid;
1827
1828 /*
1829 * Read, pin, and lock the page.
1830 */
1831 buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
1833 page = BufferGetPage(buffer);
1834
1835 /*
1836 * Check for bogus item number. This is not treated as an error
1837 * condition because it can happen while following a t_ctid link. We
1838 * just assume that the prior tid is OK and return it unchanged.
1839 */
1840 offnum = ItemPointerGetOffsetNumber(&ctid);
1842 {
1843 UnlockReleaseBuffer(buffer);
1844 break;
1845 }
1846 lp = PageGetItemId(page, offnum);
1847 if (!ItemIdIsNormal(lp))
1848 {
1849 UnlockReleaseBuffer(buffer);
1850 break;
1851 }
1852
1853 /* OK to access the tuple */
1854 tp.t_self = ctid;
1855 tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
1856 tp.t_len = ItemIdGetLength(lp);
1857 tp.t_tableOid = RelationGetRelid(relation);
1858
1859 /*
1860 * After following a t_ctid link, we might arrive at an unrelated
1861 * tuple. Check for XMIN match.
1862 */
1865 {
1866 UnlockReleaseBuffer(buffer);
1867 break;
1868 }
1869
1870 /*
1871 * Check tuple visibility; if visible, set it as the new result
1872 * candidate.
1873 */
1874 valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
1875 HeapCheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
1876 if (valid)
1877 *tid = ctid;
1878
1879 /*
1880 * If there's a valid t_ctid link, follow it, else we're done.
1881 */
1882 if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
1886 {
1887 UnlockReleaseBuffer(buffer);
1888 break;
1889 }
1890
1891 ctid = tp.t_data->t_ctid;
1893 UnlockReleaseBuffer(buffer);
1894 } /* end of loop */
1895}
static bool HeapTupleHeaderIndicatesMovedPartitions(const HeapTupleHeaderData *tup)

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

◆ heap_get_root_tuples()

void heap_get_root_tuples ( Page  page,
OffsetNumber root_offsets 
)
extern

Definition at line 2289 of file pruneheap.c.

2290{
2291 OffsetNumber offnum,
2292 maxoff;
2293
2296
2297 maxoff = PageGetMaxOffsetNumber(page);
2298 for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
2299 {
2300 ItemId lp = PageGetItemId(page, offnum);
2301 HeapTupleHeader htup;
2304
2305 /* skip unused and dead items */
2306 if (!ItemIdIsUsed(lp) || ItemIdIsDead(lp))
2307 continue;
2308
2309 if (ItemIdIsNormal(lp))
2310 {
2311 htup = (HeapTupleHeader) PageGetItem(page, lp);
2312
2313 /*
2314 * Check if this tuple is part of a HOT-chain rooted at some other
2315 * tuple. If so, skip it for now; we'll process it when we find
2316 * its root.
2317 */
2318 if (HeapTupleHeaderIsHeapOnly(htup))
2319 continue;
2320
2321 /*
2322 * This is either a plain tuple or the root of a HOT-chain.
2323 * Remember it in the mapping.
2324 */
2325 root_offsets[offnum - 1] = offnum;
2326
2327 /* If it's not the start of a HOT-chain, we're done with it */
2328 if (!HeapTupleHeaderIsHotUpdated(htup))
2329 continue;
2330
2331 /* Set up to scan the HOT-chain */
2334 }
2335 else
2336 {
2337 /* Must be a redirect item. We do not set its root_offsets entry */
2339 /* Set up to scan the HOT-chain */
2342 }
2343
2344 /*
2345 * Now follow the HOT-chain and collect other tuples in the chain.
2346 *
2347 * Note: Even though this is a nested loop, the complexity of the
2348 * function is O(N) because a tuple in the page should be visited not
2349 * more than twice, once in the outer loop and once in HOT-chain
2350 * chases.
2351 */
2352 for (;;)
2353 {
2354 /* Sanity check (pure paranoia) */
2355 if (offnum < FirstOffsetNumber)
2356 break;
2357
2358 /*
2359 * An offset past the end of page's line pointer array is possible
2360 * when the array was truncated
2361 */
2362 if (offnum > maxoff)
2363 break;
2364
2365 lp = PageGetItemId(page, nextoffnum);
2366
2367 /* Check for broken chains */
2368 if (!ItemIdIsNormal(lp))
2369 break;
2370
2371 htup = (HeapTupleHeader) PageGetItem(page, lp);
2372
2375 break;
2376
2377 /* Remember the root line pointer for this item */
2378 root_offsets[nextoffnum - 1] = offnum;
2379
2380 /* Advance to next chain member, if any */
2381 if (!HeapTupleHeaderIsHotUpdated(htup))
2382 break;
2383
2384 /* HOT implies it can't have moved to different partition */
2386
2389 }
2390 }
2391}
#define MemSet(start, val, len)
Definition c.h:1107
static bool HeapTupleHeaderIsHotUpdated(const HeapTupleHeaderData *tup)
#define MaxHeapTuplesPerPage
#define ItemIdGetRedirect(itemId)
Definition itemid.h:78
#define ItemIdIsDead(itemId)
Definition itemid.h:113
#define ItemIdIsUsed(itemId)
Definition itemid.h:92
#define ItemIdIsRedirected(itemId)
Definition itemid.h:106
#define InvalidOffsetNumber
Definition off.h:26
#define OffsetNumberNext(offsetNumber)
Definition off.h:52
#define FirstOffsetNumber
Definition off.h:27

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

Referenced by heapam_index_build_range_scan(), and heapam_index_validate_scan().

◆ heap_getnext()

HeapTuple heap_getnext ( TableScanDesc  sscan,
ScanDirection  direction 
)
extern

Definition at line 1435 of file heapam.c.

1436{
1438
1439 /*
1440 * This is still widely used directly, without going through table AM, so
1441 * add a safety check. It's possible we should, at a later point,
1442 * downgrade this to an assert. The reason for checking the AM routine,
1443 * rather than the AM oid, is that this allows to write regression tests
1444 * that create another AM reusing the heap handler.
1445 */
1446 if (unlikely(sscan->rs_rd->rd_tableam != GetHeapamTableAmRoutine()))
1447 ereport(ERROR,
1449 errmsg_internal("only heap AM is supported")));
1450
1451 /* Note: no locking manipulations needed */
1452
1454 heapgettup_pagemode(scan, direction,
1455 scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
1456 else
1457 heapgettup(scan, direction,
1458 scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
1459
1460 if (scan->rs_ctup.t_data == NULL)
1461 return NULL;
1462
1463 /*
1464 * if we get here it means we have a new current scan tuple, so point to
1465 * the proper return buffer and return the tuple.
1466 */
1467
1469
1470 return &scan->rs_ctup;
1471}
#define unlikely(x)
Definition c.h:438
int int errmsg_internal(const char *fmt,...) pg_attribute_printf(1
static void heapgettup(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition heapam.c:963
static void heapgettup_pagemode(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
Definition heapam.c:1073
const TableAmRoutine * GetHeapamTableAmRoutine(void)
#define pgstat_count_heap_getnext(rel)
Definition pgstat.h:722
@ SO_ALLOW_PAGEMODE
Definition tableam.h:65

References ereport, errcode(), errmsg_internal(), ERROR, fb(), GetHeapamTableAmRoutine(), heapgettup(), heapgettup_pagemode(), pgstat_count_heap_getnext, HeapScanDescData::rs_base, HeapScanDescData::rs_ctup, TableScanDescData::rs_flags, TableScanDescData::rs_key, TableScanDescData::rs_nkeys, TableScanDescData::rs_rd, SO_ALLOW_PAGEMODE, HeapTupleData::t_data, and unlikely.

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

◆ heap_getnextslot()

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

Definition at line 1474 of file heapam.c.

1475{
1477
1478 /* Note: no locking manipulations needed */
1479
1480 if (sscan->rs_flags & SO_ALLOW_PAGEMODE)
1481 heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1482 else
1483 heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1484
1485 if (scan->rs_ctup.t_data == NULL)
1486 {
1487 ExecClearTuple(slot);
1488 return false;
1489 }
1490
1491 /*
1492 * if we get here it means we have a new current scan tuple, so point to
1493 * the proper return buffer and return the tuple.
1494 */
1495
1497
1498 ExecStoreBufferHeapTuple(&scan->rs_ctup, slot,
1499 scan->rs_cbuf);
1500 return true;
1501}
TupleTableSlot * ExecStoreBufferHeapTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer)
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476

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

◆ heap_getnextslot_tidrange()

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

Definition at line 1577 of file heapam.c.

1579{
1581 ItemPointer mintid = &sscan->st.tidrange.rs_mintid;
1582 ItemPointer maxtid = &sscan->st.tidrange.rs_maxtid;
1583
1584 /* Note: no locking manipulations needed */
1585 for (;;)
1586 {
1587 if (sscan->rs_flags & SO_ALLOW_PAGEMODE)
1588 heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1589 else
1590 heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1591
1592 if (scan->rs_ctup.t_data == NULL)
1593 {
1594 ExecClearTuple(slot);
1595 return false;
1596 }
1597
1598 /*
1599 * heap_set_tidrange will have used heap_setscanlimits to limit the
1600 * range of pages we scan to only ones that can contain the TID range
1601 * we're scanning for. Here we must filter out any tuples from these
1602 * pages that are outside of that range.
1603 */
1604 if (ItemPointerCompare(&scan->rs_ctup.t_self, mintid) < 0)
1605 {
1606 ExecClearTuple(slot);
1607
1608 /*
1609 * When scanning backwards, the TIDs will be in descending order.
1610 * Future tuples in this direction will be lower still, so we can
1611 * just return false to indicate there will be no more tuples.
1612 */
1613 if (ScanDirectionIsBackward(direction))
1614 return false;
1615
1616 continue;
1617 }
1618
1619 /*
1620 * Likewise for the final page, we must filter out TIDs greater than
1621 * maxtid.
1622 */
1623 if (ItemPointerCompare(&scan->rs_ctup.t_self, maxtid) > 0)
1624 {
1625 ExecClearTuple(slot);
1626
1627 /*
1628 * When scanning forward, the TIDs will be in ascending order.
1629 * Future tuples in this direction will be higher still, so we can
1630 * just return false to indicate there will be no more tuples.
1631 */
1632 if (ScanDirectionIsForward(direction))
1633 return false;
1634 continue;
1635 }
1636
1637 break;
1638 }
1639
1640 /*
1641 * if we get here it means we have a new current scan tuple, so point to
1642 * the proper return buffer and return the tuple.
1643 */
1645
1646 ExecStoreBufferHeapTuple(&scan->rs_ctup, slot, scan->rs_cbuf);
1647 return true;
1648}
int32 ItemPointerCompare(const ItemPointerData *arg1, const ItemPointerData *arg2)
Definition itemptr.c:51
#define ScanDirectionIsForward(direction)
Definition sdir.h:64
#define ScanDirectionIsBackward(direction)
Definition sdir.h:50

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

◆ heap_hot_search_buffer()

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

Definition at line 90 of file heapam_indexscan.c.

93{
94 Page page = BufferGetPage(buffer);
96 BlockNumber blkno;
97 OffsetNumber offnum;
98 bool at_chain_start;
99 bool valid;
100 bool skip;
101 GlobalVisState *vistest = NULL;
102
103 /* If this is not the first call, previous call returned a (live!) tuple */
104 if (all_dead)
106
107 blkno = ItemPointerGetBlockNumber(tid);
108 offnum = ItemPointerGetOffsetNumber(tid);
110 skip = !first_call;
111
112 /* XXX: we should assert that a snapshot is pushed or registered */
114 Assert(BufferGetBlockNumber(buffer) == blkno);
115
116 /* Scan through possible multiple members of HOT-chain */
117 for (;;)
118 {
119 ItemId lp;
120
121 /* check for bogus TID */
123 break;
124
125 lp = PageGetItemId(page, offnum);
126
127 /* check for unused, dead, or redirected items */
128 if (!ItemIdIsNormal(lp))
129 {
130 /* We should only see a redirect at start of chain */
132 {
133 /* Follow the redirect */
134 offnum = ItemIdGetRedirect(lp);
135 at_chain_start = false;
136 continue;
137 }
138 /* else must be end of chain */
139 break;
140 }
141
142 /*
143 * Update heapTuple to point to the element of the HOT chain we're
144 * currently investigating. Having t_self set correctly is important
145 * because the SSI checks and the *Satisfies routine for historical
146 * MVCC snapshots need the correct tid to decide about the visibility.
147 */
148 heapTuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
149 heapTuple->t_len = ItemIdGetLength(lp);
150 heapTuple->t_tableOid = RelationGetRelid(relation);
151 ItemPointerSet(&heapTuple->t_self, blkno, offnum);
152
153 /*
154 * Shouldn't see a HEAP_ONLY tuple at chain start.
155 */
157 break;
158
159 /*
160 * The xmin should match the previous xmax value, else chain is
161 * broken.
162 */
166 break;
167
168 /*
169 * When first_call is true (and thus, skip is initially false) we'll
170 * return the first tuple we find. But on later passes, heapTuple
171 * will initially be pointing to the tuple we returned last time.
172 * Returning it again would be incorrect (and would loop forever), so
173 * we skip it and return the next match we find.
174 */
175 if (!skip)
176 {
177 /* If it's visible per the snapshot, we must return it */
178 valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
180 buffer, snapshot);
181
182 if (valid)
183 {
184 ItemPointerSetOffsetNumber(tid, offnum);
185 PredicateLockTID(relation, &heapTuple->t_self, snapshot,
187 if (all_dead)
188 *all_dead = false;
189 return true;
190 }
191 }
192 skip = false;
193
194 /*
195 * If we can't see it, maybe no one else can either. At caller
196 * request, check whether all chain members are dead to all
197 * transactions.
198 *
199 * Note: if you change the criterion here for what is "dead", fix the
200 * planner's get_actual_variable_range() function to match.
201 */
202 if (all_dead && *all_dead)
203 {
204 if (!vistest)
205 vistest = GlobalVisTestFor(relation);
206
207 if (!HeapTupleIsSurelyDead(heapTuple, vistest))
208 *all_dead = false;
209 }
210
211 /*
212 * Check to see if HOT chain continues past this tuple; if so fetch
213 * the next offnum and loop around.
214 */
216 {
217 Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
218 blkno);
219 offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
220 at_chain_start = false;
222 }
223 else
224 break; /* end of chain */
225
226 }
227
228 return false;
229}
bool HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
static bool HeapTupleIsHotUpdated(const HeapTupleData *tuple)
static bool HeapTupleIsHeapOnly(const HeapTupleData *tuple)
static void ItemPointerSet(ItemPointerData *pointer, BlockNumber blockNumber, OffsetNumber offNum)
Definition itemptr.h:135
static void ItemPointerSetOffsetNumber(ItemPointerData *pointer, OffsetNumber offsetNumber)
Definition itemptr.h:158
static const struct exclude_list_item skip[]
GlobalVisState * GlobalVisTestFor(Relation rel)
Definition procarray.c:4127
TransactionId RecentXmin
Definition snapmgr.c:160

References Assert, BufferGetBlockNumber(), BufferGetPage(), fb(), GlobalVisTestFor(), HeapCheckForSerializableConflictOut(), HeapTupleHeaderGetUpdateXid(), HeapTupleHeaderGetXmin(), HeapTupleIsHeapOnly(), HeapTupleIsHotUpdated(), HeapTupleIsSurelyDead(), HeapTupleSatisfiesVisibility(), InvalidTransactionId, ItemIdGetLength, ItemIdGetRedirect, ItemIdIsNormal, ItemIdIsRedirected, ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), ItemPointerSet(), ItemPointerSetOffsetNumber(), PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), PredicateLockTID(), RecentXmin, RelationGetRelid, skip, TransactionIdEquals, and TransactionIdIsValid.

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

◆ heap_index_delete_tuples()

TransactionId heap_index_delete_tuples ( Relation  rel,
TM_IndexDeleteOp delstate 
)
extern

Definition at line 8099 of file heapam.c.

8100{
8101 /* Initial assumption is that earlier pruning took care of conflict */
8102 TransactionId snapshotConflictHorizon = InvalidTransactionId;
8105 Page page = NULL;
8108#ifdef USE_PREFETCH
8111#endif
8113 int finalndeltids = 0,
8114 nblocksaccessed = 0;
8115
8116 /* State that's only used in bottom-up index deletion case */
8117 int nblocksfavorable = 0;
8118 int curtargetfreespace = delstate->bottomupfreespace,
8119 lastfreespace = 0,
8120 actualfreespace = 0;
8121 bool bottomup_final_block = false;
8122
8124
8125 /* Sort caller's deltids array by TID for further processing */
8127
8128 /*
8129 * Bottom-up case: resort deltids array in an order attuned to where the
8130 * greatest number of promising TIDs are to be found, and determine how
8131 * many blocks from the start of sorted array should be considered
8132 * favorable. This will also shrink the deltids array in order to
8133 * eliminate completely unfavorable blocks up front.
8134 */
8135 if (delstate->bottomup)
8137
8138#ifdef USE_PREFETCH
8139 /* Initialize prefetch state. */
8141 prefetch_state.next_item = 0;
8142 prefetch_state.ndeltids = delstate->ndeltids;
8143 prefetch_state.deltids = delstate->deltids;
8144
8145 /*
8146 * Determine the prefetch distance that we will attempt to maintain.
8147 *
8148 * Since the caller holds a buffer lock somewhere in rel, we'd better make
8149 * sure that isn't a catalog relation before we call code that does
8150 * syscache lookups, to avoid risk of deadlock.
8151 */
8152 if (IsCatalogRelation(rel))
8154 else
8157
8158 /* Cap initial prefetch distance for bottom-up deletion caller */
8159 if (delstate->bottomup)
8160 {
8164 }
8165
8166 /* Start prefetching. */
8168#endif
8169
8170 /* Iterate over deltids, determine which to delete, check their horizon */
8171 Assert(delstate->ndeltids > 0);
8172 for (int i = 0; i < delstate->ndeltids; i++)
8173 {
8174 TM_IndexDelete *ideltid = &delstate->deltids[i];
8175 TM_IndexStatus *istatus = delstate->status + ideltid->id;
8176 ItemPointer htid = &ideltid->tid;
8177 OffsetNumber offnum;
8178
8179 /*
8180 * Read buffer, and perform required extra steps each time a new block
8181 * is encountered. Avoid refetching if it's the same block as the one
8182 * from the last htid.
8183 */
8184 if (blkno == InvalidBlockNumber ||
8186 {
8187 /*
8188 * Consider giving up early for bottom-up index deletion caller
8189 * first. (Only prefetch next-next block afterwards, when it
8190 * becomes clear that we're at least going to access the next
8191 * block in line.)
8192 *
8193 * Sometimes the first block frees so much space for bottom-up
8194 * caller that the deletion process can end without accessing any
8195 * more blocks. It is usually necessary to access 2 or 3 blocks
8196 * per bottom-up deletion operation, though.
8197 */
8198 if (delstate->bottomup)
8199 {
8200 /*
8201 * We often allow caller to delete a few additional items
8202 * whose entries we reached after the point that space target
8203 * from caller was satisfied. The cost of accessing the page
8204 * was already paid at that point, so it made sense to finish
8205 * it off. When that happened, we finalize everything here
8206 * (by finishing off the whole bottom-up deletion operation
8207 * without needlessly paying the cost of accessing any more
8208 * blocks).
8209 */
8211 break;
8212
8213 /*
8214 * Give up when we didn't enable our caller to free any
8215 * additional space as a result of processing the page that we
8216 * just finished up with. This rule is the main way in which
8217 * we keep the cost of bottom-up deletion under control.
8218 */
8220 break;
8221 lastfreespace = actualfreespace; /* for next time */
8222
8223 /*
8224 * Deletion operation (which is bottom-up) will definitely
8225 * access the next block in line. Prepare for that now.
8226 *
8227 * Decay target free space so that we don't hang on for too
8228 * long with a marginal case. (Space target is only truly
8229 * helpful when it allows us to recognize that we don't need
8230 * to access more than 1 or 2 blocks to satisfy caller due to
8231 * agreeable workload characteristics.)
8232 *
8233 * We are a bit more patient when we encounter contiguous
8234 * blocks, though: these are treated as favorable blocks. The
8235 * decay process is only applied when the next block in line
8236 * is not a favorable/contiguous block. This is not an
8237 * exception to the general rule; we still insist on finding
8238 * at least one deletable item per block accessed. See
8239 * bottomup_nblocksfavorable() for full details of the theory
8240 * behind favorable blocks and heap block locality in general.
8241 *
8242 * Note: The first block in line is always treated as a
8243 * favorable block, so the earliest possible point that the
8244 * decay can be applied is just before we access the second
8245 * block in line. The Assert() verifies this for us.
8246 */
8248 if (nblocksfavorable > 0)
8250 else
8251 curtargetfreespace /= 2;
8252 }
8253
8254 /* release old buffer */
8255 if (BufferIsValid(buf))
8257
8259 buf = ReadBuffer(rel, blkno);
8261 Assert(!delstate->bottomup ||
8263
8264#ifdef USE_PREFETCH
8265
8266 /*
8267 * To maintain the prefetch distance, prefetch one more page for
8268 * each page we read.
8269 */
8271#endif
8272
8274
8275 page = BufferGetPage(buf);
8276 maxoff = PageGetMaxOffsetNumber(page);
8277 }
8278
8279 /*
8280 * In passing, detect index corruption involving an index page with a
8281 * TID that points to a location in the heap that couldn't possibly be
8282 * correct. We only do this with actual TIDs from caller's index page
8283 * (not items reached by traversing through a HOT chain).
8284 */
8286
8287 if (istatus->knowndeletable)
8288 Assert(!delstate->bottomup && !istatus->promising);
8289 else
8290 {
8291 ItemPointerData tmp = *htid;
8293
8294 /* Are any tuples from this HOT chain non-vacuumable? */
8296 &heapTuple, NULL, true))
8297 continue; /* can't delete entry */
8298
8299 /* Caller will delete, since whole HOT chain is vacuumable */
8300 istatus->knowndeletable = true;
8301
8302 /* Maintain index free space info for bottom-up deletion case */
8303 if (delstate->bottomup)
8304 {
8305 Assert(istatus->freespace > 0);
8306 actualfreespace += istatus->freespace;
8308 bottomup_final_block = true;
8309 }
8310 }
8311
8312 /*
8313 * Maintain snapshotConflictHorizon value for deletion operation as a
8314 * whole by advancing current value using heap tuple headers. This is
8315 * loosely based on the logic for pruning a HOT chain.
8316 */
8318 priorXmax = InvalidTransactionId; /* cannot check first XMIN */
8319 for (;;)
8320 {
8321 ItemId lp;
8322 HeapTupleHeader htup;
8323
8324 /* Sanity check (pure paranoia) */
8325 if (offnum < FirstOffsetNumber)
8326 break;
8327
8328 /*
8329 * An offset past the end of page's line pointer array is possible
8330 * when the array was truncated
8331 */
8332 if (offnum > maxoff)
8333 break;
8334
8335 lp = PageGetItemId(page, offnum);
8337 {
8338 offnum = ItemIdGetRedirect(lp);
8339 continue;
8340 }
8341
8342 /*
8343 * We'll often encounter LP_DEAD line pointers (especially with an
8344 * entry marked knowndeletable by our caller up front). No heap
8345 * tuple headers get examined for an htid that leads us to an
8346 * LP_DEAD item. This is okay because the earlier pruning
8347 * operation that made the line pointer LP_DEAD in the first place
8348 * must have considered the original tuple header as part of
8349 * generating its own snapshotConflictHorizon value.
8350 *
8351 * Relying on XLOG_HEAP2_PRUNE_VACUUM_SCAN records like this is
8352 * the same strategy that index vacuuming uses in all cases. Index
8353 * VACUUM WAL records don't even have a snapshotConflictHorizon
8354 * field of their own for this reason.
8355 */
8356 if (!ItemIdIsNormal(lp))
8357 break;
8358
8359 htup = (HeapTupleHeader) PageGetItem(page, lp);
8360
8361 /*
8362 * Check the tuple XMIN against prior XMAX, if any
8363 */
8366 break;
8367
8369 &snapshotConflictHorizon);
8370
8371 /*
8372 * If the tuple is not HOT-updated, then we are at the end of this
8373 * HOT-chain. No need to visit later tuples from the same update
8374 * chain (they get their own index entries) -- just move on to
8375 * next htid from index AM caller.
8376 */
8377 if (!HeapTupleHeaderIsHotUpdated(htup))
8378 break;
8379
8380 /* Advance to next HOT chain member */
8381 Assert(ItemPointerGetBlockNumber(&htup->t_ctid) == blkno);
8382 offnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
8384 }
8385
8386 /* Enable further/final shrinking of deltids for caller */
8387 finalndeltids = i + 1;
8388 }
8389
8391
8392 /*
8393 * Shrink deltids array to exclude non-deletable entries at the end. This
8394 * is not just a minor optimization. Final deltids array size might be
8395 * zero for a bottom-up caller. Index AM is explicitly allowed to rely on
8396 * ndeltids being zero in all cases with zero total deletable entries.
8397 */
8398 Assert(finalndeltids > 0 || delstate->bottomup);
8399 delstate->ndeltids = finalndeltids;
8400
8401 return snapshotConflictHorizon;
8402}
int maintenance_io_concurrency
Definition bufmgr.c:207
#define Min(x, y)
Definition c.h:1091
bool IsCatalogRelation(Relation relation)
Definition catalog.c:104
static int bottomup_sort_and_shrink(TM_IndexDeleteOp *delstate)
Definition heapam.c:8656
void HeapTupleHeaderAdvanceConflictHorizon(HeapTupleHeader tuple, TransactionId *snapshotConflictHorizon)
Definition heapam.c:7954
#define BOTTOMUP_MAX_NBLOCKS
Definition heapam.c:191
static void index_delete_check_htid(TM_IndexDeleteOp *delstate, Page page, OffsetNumber maxoff, const ItemPointerData *htid, TM_IndexStatus *istatus)
Definition heapam.c:8039
static void index_delete_sort(TM_IndexDeleteOp *delstate)
Definition heapam.c:8444
bool heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, bool *all_dead, bool first_call)
static char buf[DEFAULT_XLOG_SEG_SIZE]
#define InitNonVacuumableSnapshot(snapshotdata, vistestp)
Definition snapmgr.h:50
int get_tablespace_maintenance_io_concurrency(Oid spcid)
Definition spccache.c:230

References Assert, BOTTOMUP_MAX_NBLOCKS, bottomup_sort_and_shrink(), buf, BUFFER_LOCK_SHARE, BufferGetPage(), BufferIsValid(), fb(), FirstOffsetNumber, get_tablespace_maintenance_io_concurrency(), GlobalVisTestFor(), heap_hot_search_buffer(), HeapTupleHeaderAdvanceConflictHorizon(), HeapTupleHeaderGetUpdateXid(), HeapTupleHeaderGetXmin(), HeapTupleHeaderIsHotUpdated(), i, index_delete_check_htid(), index_delete_sort(), InitNonVacuumableSnapshot, InvalidBlockNumber, InvalidBuffer, InvalidOffsetNumber, InvalidTransactionId, IsCatalogRelation(), ItemIdGetRedirect, ItemIdIsNormal, ItemIdIsRedirected, ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), LockBuffer(), maintenance_io_concurrency, Min, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), RelationData::rd_rel, ReadBuffer(), HeapTupleHeaderData::t_ctid, TransactionIdEquals, TransactionIdIsValid, and UnlockReleaseBuffer().

◆ heap_inplace_lock()

bool heap_inplace_lock ( Relation  relation,
HeapTuple  oldtup_ptr,
Buffer  buffer,
void(*)(void *)  release_callback,
void arg 
)
extern

Definition at line 6332 of file heapam.c.

6335{
6336 HeapTupleData oldtup = *oldtup_ptr; /* minimize diff vs. heap_update() */
6338 bool ret;
6339
6340#ifdef USE_ASSERT_CHECKING
6341 if (RelationGetRelid(relation) == RelationRelationId)
6343#endif
6344
6345 Assert(BufferIsValid(buffer));
6346
6347 /*
6348 * Register shared cache invals if necessary. Other sessions may finish
6349 * inplace updates of this tuple between this step and LockTuple(). Since
6350 * inplace updates don't change cache keys, that's harmless.
6351 *
6352 * While it's tempting to register invals only after confirming we can
6353 * return true, the following obstacle precludes reordering steps that
6354 * way. Registering invals might reach a CatalogCacheInitializeCache()
6355 * that locks "buffer". That would hang indefinitely if running after our
6356 * own LockBuffer(). Hence, we must register invals before LockBuffer().
6357 */
6359
6360 LockTuple(relation, &oldtup.t_self, InplaceUpdateTupleLock);
6362
6363 /*----------
6364 * Interpret HeapTupleSatisfiesUpdate() like heap_update() does, except:
6365 *
6366 * - wait unconditionally
6367 * - already locked tuple above, since inplace needs that unconditionally
6368 * - don't recheck header after wait: simpler to defer to next iteration
6369 * - don't try to continue even if the updater aborts: likewise
6370 * - no crosscheck
6371 */
6373 buffer);
6374
6375 if (result == TM_Invisible)
6376 {
6377 /* no known way this can happen */
6378 ereport(ERROR,
6380 errmsg_internal("attempted to overwrite invisible tuple")));
6381 }
6382 else if (result == TM_SelfModified)
6383 {
6384 /*
6385 * CREATE INDEX might reach this if an expression is silly enough to
6386 * call e.g. SELECT ... FROM pg_class FOR SHARE. C code of other SQL
6387 * statements might get here after a heap_update() of the same row, in
6388 * the absence of an intervening CommandCounterIncrement().
6389 */
6390 ereport(ERROR,
6392 errmsg("tuple to be updated was already modified by an operation triggered by the current command")));
6393 }
6394 else if (result == TM_BeingModified)
6395 {
6398
6400 infomask = oldtup.t_data->t_infomask;
6401
6403 {
6406 int remain;
6407
6409 lockmode, NULL))
6410 {
6413 ret = false;
6415 relation, &oldtup.t_self, XLTW_Update,
6416 &remain);
6417 }
6418 else
6419 ret = true;
6420 }
6422 ret = true;
6424 ret = true;
6425 else
6426 {
6429 ret = false;
6430 XactLockTableWait(xwait, relation, &oldtup.t_self,
6431 XLTW_Update);
6432 }
6433 }
6434 else
6435 {
6436 ret = (result == TM_Ok);
6437 if (!ret)
6438 {
6441 }
6442 }
6443
6444 /*
6445 * GetCatalogSnapshot() relies on invalidation messages to know when to
6446 * take a new snapshot. COMMIT of xwait is responsible for sending the
6447 * invalidation. We're not acquiring heavyweight locks sufficient to
6448 * block if not yet sent, so we must take a new snapshot to ensure a later
6449 * attempt has a fair chance. While we don't need this if xwait aborted,
6450 * don't bother optimizing that.
6451 */
6452 if (!ret)
6453 {
6454 UnlockTuple(relation, &oldtup.t_self, InplaceUpdateTupleLock);
6457 }
6458 return ret;
6459}
Datum arg
Definition elog.c:1322
static bool HEAP_XMAX_IS_KEYSHR_LOCKED(uint16 infomask)
void CacheInvalidateHeapTupleInplace(Relation relation, HeapTuple key_equivalent_tuple)
Definition inval.c:1593
void ForgetInplace_Inval(void)
Definition inval.c:1286
void UnlockTuple(Relation relation, const ItemPointerData *tid, LOCKMODE lockmode)
Definition lmgr.c:601
void LockTuple(Relation relation, const ItemPointerData *tid, LOCKMODE lockmode)
Definition lmgr.c:562
@ XLTW_Update
Definition lmgr.h:27
#define InplaceUpdateTupleLock
Definition lockdefs.h:48
LockTupleMode
Definition lockoptions.h:51
@ LockTupleNoKeyExclusive
Definition lockoptions.h:57
MultiXactStatus
Definition multixact.h:37
@ MultiXactStatusNoKeyUpdate
Definition multixact.h:43
void InvalidateCatalogSnapshot(void)
Definition snapmgr.c:455
CommandId GetCurrentCommandId(bool used)
Definition xact.c:831

References arg, Assert, BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferIsValid(), CacheInvalidateHeapTupleInplace(), DoesMultiXactIdConflict(), ereport, errcode(), errmsg, errmsg_internal(), ERROR, fb(), ForgetInplace_Inval(), GetCurrentCommandId(), HEAP_XMAX_IS_KEYSHR_LOCKED(), HEAP_XMAX_IS_MULTI, HeapTupleHeaderGetRawXmax(), HeapTupleSatisfiesUpdate(), InplaceUpdateTupleLock, InvalidateCatalogSnapshot(), LockBuffer(), LockTuple(), LockTupleNoKeyExclusive, MultiXactIdWait(), MultiXactStatusNoKeyUpdate, RelationGetRelid, result, TM_BeingModified, TM_Invisible, TM_Ok, TM_SelfModified, TransactionIdIsCurrentTransactionId(), UnlockTuple(), XactLockTableWait(), and XLTW_Update.

Referenced by systable_inplace_update_begin().

◆ heap_inplace_unlock()

void heap_inplace_unlock ( Relation  relation,
HeapTuple  oldtup,
Buffer  buffer 
)
extern

◆ heap_inplace_update_and_unlock()

void heap_inplace_update_and_unlock ( Relation  relation,
HeapTuple  oldtup,
HeapTuple  tuple,
Buffer  buffer 
)
extern

Definition at line 6470 of file heapam.c.

6473{
6474 HeapTupleHeader htup = oldtup->t_data;
6475 uint32 oldlen;
6476 uint32 newlen;
6477 char *dst;
6478 char *src;
6479 int nmsgs = 0;
6481 bool RelcacheInitFileInval = false;
6482
6483 Assert(ItemPointerEquals(&oldtup->t_self, &tuple->t_self));
6484 oldlen = oldtup->t_len - htup->t_hoff;
6485 newlen = tuple->t_len - tuple->t_data->t_hoff;
6486 if (oldlen != newlen || htup->t_hoff != tuple->t_data->t_hoff)
6487 elog(ERROR, "wrong tuple length");
6488
6489 dst = (char *) htup + htup->t_hoff;
6490 src = (char *) tuple->t_data + tuple->t_data->t_hoff;
6491
6492 /* Like RecordTransactionCommit(), log only if needed */
6495 &RelcacheInitFileInval);
6496
6497 /*
6498 * Unlink relcache init files as needed. If unlinking, acquire
6499 * RelCacheInitLock until after associated invalidations. By doing this
6500 * in advance, if we checkpoint and then crash between inplace
6501 * XLogInsert() and inval, we don't rely on StartupXLOG() ->
6502 * RelationCacheInitFileRemove(). That uses elevel==LOG, so replay would
6503 * neglect to PANIC on EIO.
6504 */
6506
6507 /*----------
6508 * NO EREPORT(ERROR) from here till changes are complete
6509 *
6510 * Our exclusive buffer lock won't stop a reader having already pinned and
6511 * checked visibility for this tuple. With the usual order of changes
6512 * (i.e. updating the buffer contents before WAL logging), a reader could
6513 * observe our not-yet-persistent update to relfrozenxid and update
6514 * datfrozenxid based on that. A crash in that moment could allow
6515 * datfrozenxid to overtake relfrozenxid:
6516 *
6517 * ["D" is a VACUUM (ONLY_DATABASE_STATS)]
6518 * ["R" is a VACUUM tbl]
6519 * D: vac_update_datfrozenxid() -> systable_beginscan(pg_class)
6520 * D: systable_getnext() returns pg_class tuple of tbl
6521 * R: memcpy() into pg_class tuple of tbl
6522 * D: raise pg_database.datfrozenxid, XLogInsert(), finish
6523 * [crash]
6524 * [recovery restores datfrozenxid w/o relfrozenxid]
6525 *
6526 * We avoid that by using a temporary copy of the buffer to hide our
6527 * change from other backends until the change has been WAL-logged. We
6528 * apply our change to the temporary copy and WAL-log it, before modifying
6529 * the real page. That way any action a reader of the in-place-updated
6530 * value takes will be WAL logged after this change.
6531 */
6533
6534 MarkBufferDirty(buffer);
6535
6536 /* XLOG stuff */
6537 if (RelationNeedsWAL(relation))
6538 {
6541 char *origdata = (char *) BufferGetBlock(buffer);
6542 Page page = BufferGetPage(buffer);
6543 uint16 lower = ((PageHeader) page)->pd_lower;
6544 uint16 upper = ((PageHeader) page)->pd_upper;
6546 RelFileLocator rlocator;
6547 ForkNumber forkno;
6548 BlockNumber blkno;
6550
6551 xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
6552 xlrec.dbId = MyDatabaseId;
6554 xlrec.relcacheInitFileInval = RelcacheInitFileInval;
6555 xlrec.nmsgs = nmsgs;
6556
6559 if (nmsgs != 0)
6561 nmsgs * sizeof(SharedInvalidationMessage));
6562
6563 /* register block matching what buffer will look like after changes */
6568 BufferGetTag(buffer, &rlocator, &forkno, &blkno);
6569 Assert(forkno == MAIN_FORKNUM);
6570 XLogRegisterBlock(0, &rlocator, forkno, blkno, copied_buffer.data,
6572 XLogRegisterBufData(0, src, newlen);
6573
6574 /* inplace updates aren't decoded atm, don't log the origin */
6575
6577
6578 PageSetLSN(page, recptr);
6579 }
6580
6581 memcpy(dst, src, newlen);
6582
6584
6585 /*
6586 * Send invalidations to shared queue. SearchSysCacheLocked1() assumes we
6587 * do this before UnlockTuple().
6588 */
6590
6592 UnlockTuple(relation, &tuple->t_self, InplaceUpdateTupleLock);
6593
6594 AcceptInvalidationMessages(); /* local processing of just-sent inval */
6595
6596 /*
6597 * Queue a transactional inval, for logical decoding and for third-party
6598 * code that might have been relying on it since long before inplace
6599 * update adopted immediate invalidation. See README.tuplock section
6600 * "Reading inplace-updated columns" for logical decoding details.
6601 */
6603 CacheInvalidateHeapTuple(relation, tuple, NULL);
6604}
void BufferGetTag(Buffer buffer, RelFileLocator *rlocator, ForkNumber *forknum, BlockNumber *blknum)
Definition bufmgr.c:4467
static Block BufferGetBlock(Buffer buffer)
Definition bufmgr.h:435
PageHeaderData * PageHeader
Definition bufpage.h:199
uint32_t uint32
Definition c.h:624
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
Oid MyDatabaseTableSpace
Definition globals.c:98
Oid MyDatabaseId
Definition globals.c:96
#define MinSizeOfHeapInplace
#define XLOG_HEAP_INPLACE
Definition heapam_xlog.h:40
void AcceptInvalidationMessages(void)
Definition inval.c:930
int inplaceGetInvalidationMessages(SharedInvalidationMessage **msgs, bool *RelcacheInitFileInval)
Definition inval.c:1088
void PreInplace_Inval(void)
Definition inval.c:1250
void AtInplace_Inval(void)
Definition inval.c:1263
#define IsBootstrapProcessingMode()
Definition miscadmin.h:495
Datum lower(PG_FUNCTION_ARGS)
Datum upper(PG_FUNCTION_ARGS)
ForkNumber
Definition relpath.h:56
#define XLogStandbyInfoActive()
Definition xlog.h:126
void XLogRegisterBufData(uint8 block_id, const void *data, uint32 len)
Definition xloginsert.c:413
void XLogRegisterBlock(uint8 block_id, RelFileLocator *rlocator, ForkNumber forknum, BlockNumber blknum, const PageData *page, uint8 flags)
Definition xloginsert.c:317

References AcceptInvalidationMessages(), Assert, AtInplace_Inval(), BUFFER_LOCK_UNLOCK, BufferGetBlock(), BufferGetPage(), BufferGetTag(), CacheInvalidateHeapTuple(), elog, END_CRIT_SECTION, ERROR, fb(), inplaceGetInvalidationMessages(), InplaceUpdateTupleLock, IsBootstrapProcessingMode, ItemPointerEquals(), ItemPointerGetOffsetNumber(), LockBuffer(), lower(), MAIN_FORKNUM, MarkBufferDirty(), memcpy(), MinSizeOfHeapInplace, MyDatabaseId, MyDatabaseTableSpace, PageSetLSN(), PreInplace_Inval(), REGBUF_STANDARD, RelationNeedsWAL, START_CRIT_SECTION, HeapTupleData::t_data, HeapTupleHeaderData::t_hoff, HeapTupleData::t_len, HeapTupleData::t_self, UnlockTuple(), upper(), XLOG_HEAP_INPLACE, XLogBeginInsert(), XLogInsert(), XLogRegisterBlock(), XLogRegisterBufData(), XLogRegisterData(), and XLogStandbyInfoActive.

Referenced by systable_inplace_update_finish().

◆ heap_insert()

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

Definition at line 2004 of file heapam.c.

2006{
2009 Buffer buffer;
2010 Page page;
2011 Buffer vmbuffer = InvalidBuffer;
2012 bool all_visible_cleared = false;
2013
2014 /* Cheap, simplistic check that the tuple matches the rel's rowtype. */
2017
2018 AssertHasSnapshotForToast(relation);
2019
2020 /*
2021 * Fill in tuple header fields and toast the tuple if necessary.
2022 *
2023 * Note: below this point, heaptup is the data we actually intend to store
2024 * into the relation; tup is the caller's original untoasted data.
2025 */
2026 heaptup = heap_prepare_insert(relation, tup, xid, cid, options);
2027
2028 /*
2029 * Find buffer to insert this tuple into. If the page is all visible,
2030 * this will also pin the requisite visibility map page.
2031 */
2032 buffer = RelationGetBufferForTuple(relation, heaptup->t_len,
2033 InvalidBuffer, options, bistate,
2034 &vmbuffer, NULL,
2035 0);
2036
2037 page = BufferGetPage(buffer);
2038
2039 /*
2040 * We're about to do the actual insert -- but check for conflict first, to
2041 * avoid possibly having to roll back work we've just done.
2042 *
2043 * This is safe without a recheck as long as there is no possibility of
2044 * another process scanning the page between this check and the insert
2045 * being visible to the scan (i.e., an exclusive buffer content lock is
2046 * continuously held from this point until the tuple insert is visible).
2047 *
2048 * For a heap insert, we only need to check for table-level SSI locks. Our
2049 * new tuple can't possibly conflict with existing tuple locks, and heap
2050 * page locks are only consolidated versions of tuple locks; they do not
2051 * lock "gaps" as index page locks do. So we don't need to specify a
2052 * buffer when making the call, which makes for a faster check.
2053 */
2055
2056 /* NO EREPORT(ERROR) from here till changes are logged */
2058
2059 RelationPutHeapTuple(relation, buffer, heaptup,
2061
2062 if (PageIsAllVisible(page))
2063 {
2064 all_visible_cleared = true;
2065 PageClearAllVisible(page);
2066 visibilitymap_clear(relation,
2068 vmbuffer, VISIBILITYMAP_VALID_BITS);
2069 }
2070
2071 /*
2072 * Set pd_prune_xid to trigger heap_page_prune_and_freeze() once the page
2073 * is full so that we can set the page all-visible in the VM on the next
2074 * page access.
2075 *
2076 * Setting pd_prune_xid is also handy if the inserting transaction
2077 * eventually aborts making this tuple DEAD and hence available for
2078 * pruning. If no other tuple in this page is UPDATEd/DELETEd, the aborted
2079 * tuple would never otherwise be pruned until next vacuum is triggered.
2080 *
2081 * Don't set it if we are in bootstrap mode or we are inserting a frozen
2082 * tuple, as there is no further pruning/freezing needed in those cases.
2083 */
2085 PageSetPrunable(page, xid);
2086
2087 MarkBufferDirty(buffer);
2088
2089 /* XLOG stuff */
2090 if (RelationNeedsWAL(relation))
2091 {
2095 uint8 info = XLOG_HEAP_INSERT;
2096 int bufflags = 0;
2097
2098 /*
2099 * If this is a catalog, we need to transmit combo CIDs to properly
2100 * decode, so log that as well.
2101 */
2103 log_heap_new_cid(relation, heaptup);
2104
2105 /*
2106 * If this is the single and first tuple on page, we can reinit the
2107 * page instead of restoring the whole thing. Set flag, and hide
2108 * buffer references from XLogInsert.
2109 */
2112 {
2113 info |= XLOG_HEAP_INIT_PAGE;
2115 }
2116
2117 xlrec.offnum = ItemPointerGetOffsetNumber(&heaptup->t_self);
2118 xlrec.flags = 0;
2124
2125 /*
2126 * For logical decoding, we need the tuple even if we're doing a full
2127 * page write, so make sure it's included even if we take a full-page
2128 * image. (XXX We could alternatively store a pointer into the FPW).
2129 */
2130 if (RelationIsLogicallyLogged(relation) &&
2132 {
2135
2136 if (IsToastRelation(relation))
2138 }
2139
2142
2143 xlhdr.t_infomask2 = heaptup->t_data->t_infomask2;
2144 xlhdr.t_infomask = heaptup->t_data->t_infomask;
2145 xlhdr.t_hoff = heaptup->t_data->t_hoff;
2146
2147 /*
2148 * note we mark xlhdr as belonging to buffer; if XLogInsert decides to
2149 * write the whole page to the xlog, we don't need to store
2150 * xl_heap_header in the xlog.
2151 */
2154 /* PG73FORMAT: write bitmap [+ padding] [+ oid] + data */
2156 (char *) heaptup->t_data + SizeofHeapTupleHeader,
2158
2159 /* filtering by origin on a row level is much more efficient */
2161
2162 recptr = XLogInsert(RM_HEAP_ID, info);
2163
2164 PageSetLSN(page, recptr);
2165 }
2166
2168
2169 UnlockReleaseBuffer(buffer);
2170 if (vmbuffer != InvalidBuffer)
2171 ReleaseBuffer(vmbuffer);
2172
2173 /*
2174 * If tuple is cacheable, mark it for invalidation from the caches in case
2175 * we abort. Note it is OK to do this after releasing the buffer, because
2176 * the heaptup data structure is all in local memory, not in the shared
2177 * buffer.
2178 */
2180
2181 /* Note: speculative insertions are counted too, even if aborted later */
2182 pgstat_count_heap_insert(relation, 1);
2183
2184 /*
2185 * If heaptup is a private copy, release it. Don't forget to copy t_self
2186 * back to the caller's image, too.
2187 */
2188 if (heaptup != tup)
2189 {
2190 tup->t_self = heaptup->t_self;
2192 }
2193}
uint8_t uint8
Definition c.h:622
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid, CommandId cid, uint32 options)
Definition heapam.c:2202
#define HEAP_INSERT_SPECULATIVE
Definition heapam.h:39
#define HEAP_INSERT_FROZEN
Definition heapam.h:37
#define HEAP_INSERT_NO_LOGICAL
Definition heapam.h:38
#define XLH_INSERT_ON_TOAST_RELATION
Definition heapam_xlog.h:76
#define XLH_INSERT_IS_SPECULATIVE
Definition heapam_xlog.h:74
#define XLH_INSERT_ALL_VISIBLE_CLEARED
Definition heapam_xlog.h:72
#define XLOG_HEAP_INSERT
Definition heapam_xlog.h:33
#define SizeOfHeapInsert
#define XLH_INSERT_CONTAINS_NEW_TUPLE
Definition heapam_xlog.h:75
#define XLOG_HEAP_INIT_PAGE
Definition heapam_xlog.h:47
void RelationPutHeapTuple(Relation relation, Buffer buffer, HeapTuple tuple, bool token)
Definition hio.c:35
Buffer RelationGetBufferForTuple(Relation relation, Size len, Buffer otherBuffer, uint32 options, BulkInsertState bistate, Buffer *vmbuffer, Buffer *vmbuffer_other, int num_pages)
Definition hio.c:500
#define HeapTupleHeaderGetNatts(tup)
void pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
#define RelationIsLogicallyLogged(relation)
Definition rel.h:712
#define RelationGetNumberOfAttributes(relation)
Definition rel.h:522
#define TransactionIdIsNormal(xid)
Definition transam.h:42
#define REGBUF_KEEP_DATA
Definition xloginsert.h:36
#define REGBUF_WILL_INIT
Definition xloginsert.h:34

References Assert, AssertHasSnapshotForToast(), BufferGetBlockNumber(), BufferGetPage(), CacheInvalidateHeapTuple(), CheckForSerializableConflictIn(), END_CRIT_SECTION, fb(), FirstOffsetNumber, GetCurrentTransactionId(), heap_freetuple(), HEAP_INSERT_FROZEN, HEAP_INSERT_NO_LOGICAL, HEAP_INSERT_SPECULATIVE, heap_prepare_insert(), HeapTupleHeaderGetNatts, InvalidBlockNumber, InvalidBuffer, IsToastRelation(), ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), log_heap_new_cid(), MarkBufferDirty(), PageClearAllVisible(), PageGetMaxOffsetNumber(), PageIsAllVisible(), PageSetLSN(), PageSetPrunable, pgstat_count_heap_insert(), REGBUF_KEEP_DATA, REGBUF_STANDARD, REGBUF_WILL_INIT, RelationGetBufferForTuple(), RelationGetNumberOfAttributes, RelationIsAccessibleInLogicalDecoding, RelationIsLogicallyLogged, RelationNeedsWAL, RelationPutHeapTuple(), ReleaseBuffer(), SizeOfHeapHeader, SizeOfHeapInsert, SizeofHeapTupleHeader, START_CRIT_SECTION, TransactionIdIsNormal, UnlockReleaseBuffer(), visibilitymap_clear(), VISIBILITYMAP_VALID_BITS, XLH_INSERT_ALL_VISIBLE_CLEARED, XLH_INSERT_CONTAINS_NEW_TUPLE, XLH_INSERT_IS_SPECULATIVE, XLH_INSERT_ON_TOAST_RELATION, XLOG_HEAP_INIT_PAGE, XLOG_HEAP_INSERT, XLOG_INCLUDE_ORIGIN, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), XLogRegisterData(), and XLogSetRecordFlags().

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

◆ heap_lock_tuple()

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

Definition at line 4539 of file heapam.c.

4543{
4545 ItemPointer tid = &(tuple->t_self);
4546 ItemId lp;
4547 Page page;
4548 Buffer vmbuffer = InvalidBuffer;
4549 BlockNumber block;
4550 TransactionId xid,
4551 xmax;
4555 bool first_time = true;
4556 bool skip_tuple_lock = false;
4557 bool have_tuple_lock = false;
4558 bool cleared_all_frozen = false;
4559
4560 *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
4561 block = ItemPointerGetBlockNumber(tid);
4562
4563 /*
4564 * Before locking the buffer, pin the visibility map page if it appears to
4565 * be necessary. Since we haven't got the lock yet, someone else might be
4566 * in the middle of changing this, so we'll need to recheck after we have
4567 * the lock.
4568 */
4569 if (PageIsAllVisible(BufferGetPage(*buffer)))
4570 visibilitymap_pin(relation, block, &vmbuffer);
4571
4573
4574 page = BufferGetPage(*buffer);
4577
4578 tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
4579 tuple->t_len = ItemIdGetLength(lp);
4580 tuple->t_tableOid = RelationGetRelid(relation);
4581
4582l3:
4583 result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
4584
4585 if (result == TM_Invisible)
4586 {
4587 /*
4588 * This is possible, but only when locking a tuple for ON CONFLICT DO
4589 * SELECT/UPDATE. We return this value here rather than throwing an
4590 * error in order to give that case the opportunity to throw a more
4591 * specific error.
4592 */
4594 goto out_locked;
4595 }
4596 else if (result == TM_BeingModified ||
4597 result == TM_Updated ||
4598 result == TM_Deleted)
4599 {
4603 bool require_sleep;
4604 ItemPointerData t_ctid;
4605
4606 /* must copy state data before unlocking buffer */
4608 infomask = tuple->t_data->t_infomask;
4609 infomask2 = tuple->t_data->t_infomask2;
4610 ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
4611
4613
4614 /*
4615 * If any subtransaction of the current top transaction already holds
4616 * a lock as strong as or stronger than what we're requesting, we
4617 * effectively hold the desired lock already. We *must* succeed
4618 * without trying to take the tuple lock, else we will deadlock
4619 * against anyone wanting to acquire a stronger lock.
4620 *
4621 * Note we only do this the first time we loop on the HTSU result;
4622 * there is no point in testing in subsequent passes, because
4623 * evidently our own transaction cannot have acquired a new lock after
4624 * the first time we checked.
4625 */
4626 if (first_time)
4627 {
4628 first_time = false;
4629
4631 {
4632 int i;
4633 int nmembers;
4634 MultiXactMember *members;
4635
4636 /*
4637 * We don't need to allow old multixacts here; if that had
4638 * been the case, HeapTupleSatisfiesUpdate would have returned
4639 * MayBeUpdated and we wouldn't be here.
4640 */
4641 nmembers =
4642 GetMultiXactIdMembers(xwait, &members, false,
4644
4645 for (i = 0; i < nmembers; i++)
4646 {
4647 /* only consider members of our own transaction */
4648 if (!TransactionIdIsCurrentTransactionId(members[i].xid))
4649 continue;
4650
4651 if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
4652 {
4653 pfree(members);
4654 result = TM_Ok;
4655 goto out_unlocked;
4656 }
4657 else
4658 {
4659 /*
4660 * Disable acquisition of the heavyweight tuple lock.
4661 * Otherwise, when promoting a weaker lock, we might
4662 * deadlock with another locker that has acquired the
4663 * heavyweight tuple lock and is waiting for our
4664 * transaction to finish.
4665 *
4666 * Note that in this case we still need to wait for
4667 * the multixact if required, to avoid acquiring
4668 * conflicting locks.
4669 */
4670 skip_tuple_lock = true;
4671 }
4672 }
4673
4674 if (members)
4675 pfree(members);
4676 }
4678 {
4679 switch (mode)
4680 {
4681 case LockTupleKeyShare:
4685 result = TM_Ok;
4686 goto out_unlocked;
4687 case LockTupleShare:
4690 {
4691 result = TM_Ok;
4692 goto out_unlocked;
4693 }
4694 break;
4697 {
4698 result = TM_Ok;
4699 goto out_unlocked;
4700 }
4701 break;
4702 case LockTupleExclusive:
4705 {
4706 result = TM_Ok;
4707 goto out_unlocked;
4708 }
4709 break;
4710 }
4711 }
4712 }
4713
4714 /*
4715 * Initially assume that we will have to wait for the locking
4716 * transaction(s) to finish. We check various cases below in which
4717 * this can be turned off.
4718 */
4719 require_sleep = true;
4720 if (mode == LockTupleKeyShare)
4721 {
4722 /*
4723 * If we're requesting KeyShare, and there's no update present, we
4724 * don't need to wait. Even if there is an update, we can still
4725 * continue if the key hasn't been modified.
4726 *
4727 * However, if there are updates, we need to walk the update chain
4728 * to mark future versions of the row as locked, too. That way,
4729 * if somebody deletes that future version, we're protected
4730 * against the key going away. This locking of future versions
4731 * could block momentarily, if a concurrent transaction is
4732 * deleting a key; or it could return a value to the effect that
4733 * the transaction deleting the key has already committed. So we
4734 * do this before re-locking the buffer; otherwise this would be
4735 * prone to deadlocks.
4736 *
4737 * Note that the TID we're locking was grabbed before we unlocked
4738 * the buffer. For it to change while we're not looking, the
4739 * other properties we're testing for below after re-locking the
4740 * buffer would also change, in which case we would restart this
4741 * loop above.
4742 */
4744 {
4745 bool updated;
4746
4748
4749 /*
4750 * If there are updates, follow the update chain; bail out if
4751 * that cannot be done.
4752 */
4753 if (follow_updates && updated &&
4754 !ItemPointerEquals(&tuple->t_self, &t_ctid))
4755 {
4756 TM_Result res;
4757
4758 res = heap_lock_updated_tuple(relation,
4759 infomask, xwait, &t_ctid,
4761 mode);
4762 if (res != TM_Ok)
4763 {
4764 result = res;
4765 /* recovery code expects to have buffer lock held */
4767 goto failed;
4768 }
4769 }
4770
4772
4773 /*
4774 * Make sure it's still an appropriate lock, else start over.
4775 * Also, if it wasn't updated before we released the lock, but
4776 * is updated now, we start over too; the reason is that we
4777 * now need to follow the update chain to lock the new
4778 * versions.
4779 */
4780 if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
4781 ((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
4782 !updated))
4783 goto l3;
4784
4785 /* Things look okay, so we can skip sleeping */
4786 require_sleep = false;
4787
4788 /*
4789 * Note we allow Xmax to change here; other updaters/lockers
4790 * could have modified it before we grabbed the buffer lock.
4791 * However, this is not a problem, because with the recheck we
4792 * just did we ensure that they still don't conflict with the
4793 * lock we want.
4794 */
4795 }
4796 }
4797 else if (mode == LockTupleShare)
4798 {
4799 /*
4800 * If we're requesting Share, we can similarly avoid sleeping if
4801 * there's no update and no exclusive lock present.
4802 */
4805 {
4807
4808 /*
4809 * Make sure it's still an appropriate lock, else start over.
4810 * See above about allowing xmax to change.
4811 */
4814 goto l3;
4815 require_sleep = false;
4816 }
4817 }
4818 else if (mode == LockTupleNoKeyExclusive)
4819 {
4820 /*
4821 * If we're requesting NoKeyExclusive, we might also be able to
4822 * avoid sleeping; just ensure that there no conflicting lock
4823 * already acquired.
4824 */
4826 {
4828 mode, NULL))
4829 {
4830 /*
4831 * No conflict, but if the xmax changed under us in the
4832 * meantime, start over.
4833 */
4837 xwait))
4838 goto l3;
4839
4840 /* otherwise, we're good */
4841 require_sleep = false;
4842 }
4843 }
4845 {
4847
4848 /* if the xmax changed in the meantime, start over */
4851 xwait))
4852 goto l3;
4853 /* otherwise, we're good */
4854 require_sleep = false;
4855 }
4856 }
4857
4858 /*
4859 * As a check independent from those above, we can also avoid sleeping
4860 * if the current transaction is the sole locker of the tuple. Note
4861 * that the strength of the lock already held is irrelevant; this is
4862 * not about recording the lock in Xmax (which will be done regardless
4863 * of this optimization, below). Also, note that the cases where we
4864 * hold a lock stronger than we are requesting are already handled
4865 * above by not doing anything.
4866 *
4867 * Note we only deal with the non-multixact case here; MultiXactIdWait
4868 * is well equipped to deal with this situation on its own.
4869 */
4872 {
4873 /* ... but if the xmax changed in the meantime, start over */
4877 xwait))
4878 goto l3;
4880 require_sleep = false;
4881 }
4882
4883 /*
4884 * Time to sleep on the other transaction/multixact, if necessary.
4885 *
4886 * If the other transaction is an update/delete that's already
4887 * committed, then sleeping cannot possibly do any good: if we're
4888 * required to sleep, get out to raise an error instead.
4889 *
4890 * By here, we either have already acquired the buffer exclusive lock,
4891 * or we must wait for the locking transaction or multixact; so below
4892 * we ensure that we grab buffer lock after the sleep.
4893 */
4895 {
4897 goto failed;
4898 }
4899 else if (require_sleep)
4900 {
4901 /*
4902 * Acquire tuple lock to establish our priority for the tuple, or
4903 * die trying. LockTuple will release us when we are next-in-line
4904 * for the tuple. We must do this even if we are share-locking,
4905 * but not if we already have a weaker lock on the tuple.
4906 *
4907 * If we are forced to "start over" below, we keep the tuple lock;
4908 * this arranges that we stay at the head of the line while
4909 * rechecking tuple state.
4910 */
4911 if (!skip_tuple_lock &&
4912 !heap_acquire_tuplock(relation, tid, mode, wait_policy,
4914 {
4915 /*
4916 * This can only happen if wait_policy is Skip and the lock
4917 * couldn't be obtained.
4918 */
4920 /* recovery code expects to have buffer lock held */
4922 goto failed;
4923 }
4924
4926 {
4928
4929 /* We only ever lock tuples, never update them */
4930 if (status >= MultiXactStatusNoKeyUpdate)
4931 elog(ERROR, "invalid lock mode in heap_lock_tuple");
4932
4933 /* wait for multixact to end, or die trying */
4934 switch (wait_policy)
4935 {
4936 case LockWaitBlock:
4938 relation, &tuple->t_self, XLTW_Lock, NULL);
4939 break;
4940 case LockWaitSkip:
4942 status, infomask, relation,
4943 NULL, false))
4944 {
4946 /* recovery code expects to have buffer lock held */
4948 goto failed;
4949 }
4950 break;
4951 case LockWaitError:
4953 status, infomask, relation,
4955 ereport(ERROR,
4957 errmsg("could not obtain lock on row in relation \"%s\"",
4958 RelationGetRelationName(relation))));
4959
4960 break;
4961 }
4962
4963 /*
4964 * Of course, the multixact might not be done here: if we're
4965 * requesting a light lock mode, other transactions with light
4966 * locks could still be alive, as well as locks owned by our
4967 * own xact or other subxacts of this backend. We need to
4968 * preserve the surviving MultiXact members. Note that it
4969 * isn't absolutely necessary in the latter case, but doing so
4970 * is simpler.
4971 */
4972 }
4973 else
4974 {
4975 /* wait for regular transaction to end, or die trying */
4976 switch (wait_policy)
4977 {
4978 case LockWaitBlock:
4979 XactLockTableWait(xwait, relation, &tuple->t_self,
4980 XLTW_Lock);
4981 break;
4982 case LockWaitSkip:
4984 {
4986 /* recovery code expects to have buffer lock held */
4988 goto failed;
4989 }
4990 break;
4991 case LockWaitError:
4993 ereport(ERROR,
4995 errmsg("could not obtain lock on row in relation \"%s\"",
4996 RelationGetRelationName(relation))));
4997 break;
4998 }
4999 }
5000
5001 /* if there are updates, follow the update chain */
5003 !ItemPointerEquals(&tuple->t_self, &t_ctid))
5004 {
5005 TM_Result res;
5006
5007 res = heap_lock_updated_tuple(relation,
5008 infomask, xwait, &t_ctid,
5010 mode);
5011 if (res != TM_Ok)
5012 {
5013 result = res;
5014 /* recovery code expects to have buffer lock held */
5016 goto failed;
5017 }
5018 }
5019
5021
5022 /*
5023 * xwait is done, but if xwait had just locked the tuple then some
5024 * other xact could update this tuple before we get to this point.
5025 * Check for xmax change, and start over if so.
5026 */
5029 xwait))
5030 goto l3;
5031
5033 {
5034 /*
5035 * Otherwise check if it committed or aborted. Note we cannot
5036 * be here if the tuple was only locked by somebody who didn't
5037 * conflict with us; that would have been handled above. So
5038 * that transaction must necessarily be gone by now. But
5039 * don't check for this in the multixact case, because some
5040 * locker transactions might still be running.
5041 */
5042 UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
5043 }
5044 }
5045
5046 /* By here, we're certain that we hold buffer exclusive lock again */
5047
5048 /*
5049 * We may lock if previous xmax aborted, or if it committed but only
5050 * locked the tuple without updating it; or if we didn't have to wait
5051 * at all for whatever reason.
5052 */
5053 if (!require_sleep ||
5054 (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
5057 result = TM_Ok;
5058 else if (!ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid))
5060 else
5062 }
5063
5064failed:
5065 if (result != TM_Ok)
5066 {
5069
5070 /*
5071 * When locking a tuple under LockWaitSkip semantics and we fail with
5072 * TM_WouldBlock above, it's possible for concurrent transactions to
5073 * release the lock and set HEAP_XMAX_INVALID in the meantime. So
5074 * this assert is slightly different from the equivalent one in
5075 * heap_delete and heap_update.
5076 */
5078 !(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
5080 !ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid));
5081 tmfd->ctid = tuple->t_data->t_ctid;
5082 tmfd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
5083 if (result == TM_SelfModified)
5084 tmfd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
5085 else
5086 tmfd->cmax = InvalidCommandId;
5087 goto out_locked;
5088 }
5089
5090 /*
5091 * If we didn't pin the visibility map page and the page has become all
5092 * visible while we were busy locking the buffer, or during some
5093 * subsequent window during which we had it unlocked, we'll have to unlock
5094 * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
5095 * unfortunate, especially since we'll now have to recheck whether the
5096 * tuple has been locked or updated under us, but hopefully it won't
5097 * happen very often.
5098 */
5099 if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
5100 {
5102 visibilitymap_pin(relation, block, &vmbuffer);
5104 goto l3;
5105 }
5106
5107 xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
5108 old_infomask = tuple->t_data->t_infomask;
5109
5110 /*
5111 * If this is the first possibly-multixact-able operation in the current
5112 * transaction, set my per-backend OldestMemberMXactId setting. We can be
5113 * certain that the transaction will never become a member of any older
5114 * MultiXactIds than that. (We have to do this even if we end up just
5115 * using our own TransactionId below, since some other backend could
5116 * incorporate our XID into a MultiXact immediately afterwards.)
5117 */
5119
5120 /*
5121 * Compute the new xmax and infomask to store into the tuple. Note we do
5122 * not modify the tuple just yet, because that would leave it in the wrong
5123 * state if multixact.c elogs.
5124 */
5126 GetCurrentTransactionId(), mode, false,
5127 &xid, &new_infomask, &new_infomask2);
5128
5130
5131 /*
5132 * Store transaction information of xact locking the tuple.
5133 *
5134 * Note: Cmax is meaningless in this context, so don't set it; this avoids
5135 * possibly generating a useless combo CID. Moreover, if we're locking a
5136 * previously updated tuple, it's important to preserve the Cmax.
5137 *
5138 * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
5139 * we would break the HOT chain.
5140 */
5143 tuple->t_data->t_infomask |= new_infomask;
5144 tuple->t_data->t_infomask2 |= new_infomask2;
5147 HeapTupleHeaderSetXmax(tuple->t_data, xid);
5148
5149 /*
5150 * Make sure there is no forward chain link in t_ctid. Note that in the
5151 * cases where the tuple has been updated, we must not overwrite t_ctid,
5152 * because it was set by the updater. Moreover, if the tuple has been
5153 * updated, we need to follow the update chain to lock the new versions of
5154 * the tuple as well.
5155 */
5157 tuple->t_data->t_ctid = *tid;
5158
5159 /* Clear only the all-frozen bit on visibility map if needed */
5160 if (PageIsAllVisible(page) &&
5161 visibilitymap_clear(relation, block, vmbuffer,
5163 cleared_all_frozen = true;
5164
5165
5166 MarkBufferDirty(*buffer);
5167
5168 /*
5169 * XLOG stuff. You might think that we don't need an XLOG record because
5170 * there is no state change worth restoring after a crash. You would be
5171 * wrong however: we have just written either a TransactionId or a
5172 * MultiXactId that may never have been seen on disk before, and we need
5173 * to make sure that there are XLOG entries covering those ID numbers.
5174 * Else the same IDs might be re-used after a crash, which would be
5175 * disastrous if this page made it to disk before the crash. Essentially
5176 * we have to enforce the WAL log-before-data rule even in this case.
5177 * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
5178 * entries for everything anyway.)
5179 */
5180 if (RelationNeedsWAL(relation))
5181 {
5184
5187
5188 xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
5189 xlrec.xmax = xid;
5190 xlrec.infobits_set = compute_infobits(new_infomask,
5191 tuple->t_data->t_infomask2);
5194
5195 /* we don't decode row locks atm, so no need to log the origin */
5196
5198
5199 PageSetLSN(page, recptr);
5200 }
5201
5203
5204 result = TM_Ok;
5205
5208
5210 if (BufferIsValid(vmbuffer))
5211 ReleaseBuffer(vmbuffer);
5212
5213 /*
5214 * Don't update the visibility map here. Locking a tuple doesn't change
5215 * visibility info.
5216 */
5217
5218 /*
5219 * Now that we have successfully marked the tuple as locked, we can
5220 * release the lmgr tuple lock, if we had it.
5221 */
5222 if (have_tuple_lock)
5223 UnlockTupleTuplock(relation, tid, mode);
5224
5225 return result;
5226}
#define TUPLOCK_from_mxstatus(status)
Definition heapam.c:220
static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, int *remaining, bool logLockFailure)
Definition heapam.c:7776
static TM_Result heap_lock_updated_tuple(Relation rel, uint16 prior_infomask, TransactionId prior_raw_xmax, const ItemPointerData *prior_ctid, TransactionId xid, LockTupleMode mode)
Definition heapam.c:6010
static MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
Definition heapam.c:4492
#define XLH_LOCK_ALL_FROZEN_CLEARED
#define XLOG_HEAP_LOCK
Definition heapam_xlog.h:39
#define SizeOfHeapLock
#define HEAP_KEYS_UPDATED
static bool HEAP_XMAX_IS_SHR_LOCKED(uint16 infomask)
static bool HEAP_XMAX_IS_EXCL_LOCKED(uint16 infomask)
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
Definition itemptr.h:172
bool ConditionalXactLockTableWait(TransactionId xid, bool logLockFailure)
Definition lmgr.c:739
@ XLTW_Lock
Definition lmgr.h:29
bool log_lock_failures
Definition lock.c:57
@ LockWaitSkip
Definition lockoptions.h:42
@ LockWaitError
Definition lockoptions.h:44
@ LockTupleShare
Definition lockoptions.h:55
@ LockTupleKeyShare
Definition lockoptions.h:53
int GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members, bool from_pgupgrade, bool isLockOnly)
Definition multixact.c:1172
static PgChecksumMode mode
@ TM_WouldBlock
Definition tableam.h:125
#define VISIBILITYMAP_ALL_FROZEN

References Assert, BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferGetPage(), BufferIsValid(), TM_FailureData::cmax, compute_infobits(), compute_new_xmax_infomask(), ConditionalMultiXactIdWait(), ConditionalXactLockTableWait(), TM_FailureData::ctid, DoesMultiXactIdConflict(), elog, END_CRIT_SECTION, ereport, errcode(), errmsg, ERROR, fb(), get_mxact_status_for_lock(), GetCurrentTransactionId(), GetMultiXactIdMembers(), heap_acquire_tuplock(), HEAP_KEYS_UPDATED, heap_lock_updated_tuple(), 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, InvalidBuffer, InvalidCommandId, ItemIdGetLength, ItemIdIsNormal, ItemPointerCopy(), ItemPointerEquals(), ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), LockBuffer(), LockTupleExclusive, LockTupleKeyShare, LockTupleNoKeyExclusive, LockTupleShare, LockWaitBlock, LockWaitError, LockWaitSkip, log_lock_failures, MarkBufferDirty(), mode, MultiXactIdSetOldestMember(), MultiXactIdWait(), MultiXactStatusNoKeyUpdate, PageGetItem(), PageGetItemId(), PageIsAllVisible(), PageSetLSN(), pfree(), ReadBuffer(), REGBUF_STANDARD, RelationGetRelationName, RelationGetRelid, RelationNeedsWAL, ReleaseBuffer(), result, SizeOfHeapLock, START_CRIT_SECTION, HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleHeaderData::t_infomask, HeapTupleHeaderData::t_infomask2, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TM_BeingModified, TM_Deleted, TM_Invisible, TM_Ok, TM_SelfModified, TM_Updated, TM_WouldBlock, TransactionIdEquals, TransactionIdIsCurrentTransactionId(), TUPLOCK_from_mxstatus, UnlockTupleTuplock, UpdateXmaxHintBits(), VISIBILITYMAP_ALL_FROZEN, visibilitymap_clear(), visibilitymap_pin(), XactLockTableWait(), XLH_LOCK_ALL_FROZEN_CLEARED, XLOG_HEAP_LOCK, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), XLogRegisterData(), XLTW_Lock, TM_FailureData::xmax, and xmax_infomask_changed().

Referenced by heapam_tuple_lock().

◆ heap_multi_insert()

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

Definition at line 2282 of file heapam.c.

2284{
2287 int i;
2288 int ndone;
2290 Page page;
2291 Buffer vmbuffer = InvalidBuffer;
2292 bool needwal;
2296 bool starting_with_empty_page = false;
2297 int npages = 0;
2298 int npages_used = 0;
2299
2300 /* currently not needed (thus unsupported) for heap_multi_insert() */
2302
2303 AssertHasSnapshotForToast(relation);
2304
2305 needwal = RelationNeedsWAL(relation);
2308
2309 /* Toast and set header data in all the slots */
2310 heaptuples = palloc(ntuples * sizeof(HeapTuple));
2311 for (i = 0; i < ntuples; i++)
2312 {
2313 HeapTuple tuple;
2314
2315 tuple = ExecFetchSlotHeapTuple(slots[i], true, NULL);
2316 slots[i]->tts_tableOid = RelationGetRelid(relation);
2317 tuple->t_tableOid = slots[i]->tts_tableOid;
2318 heaptuples[i] = heap_prepare_insert(relation, tuple, xid, cid,
2319 options);
2320 }
2321
2322 /*
2323 * We're about to do the actual inserts -- but check for conflict first,
2324 * to minimize the possibility of having to roll back work we've just
2325 * done.
2326 *
2327 * A check here does not definitively prevent a serialization anomaly;
2328 * that check MUST be done at least past the point of acquiring an
2329 * exclusive buffer content lock on every buffer that will be affected,
2330 * and MAY be done after all inserts are reflected in the buffers and
2331 * those locks are released; otherwise there is a race condition. Since
2332 * multiple buffers can be locked and unlocked in the loop below, and it
2333 * would not be feasible to identify and lock all of those buffers before
2334 * the loop, we must do a final check at the end.
2335 *
2336 * The check here could be omitted with no loss of correctness; it is
2337 * present strictly as an optimization.
2338 *
2339 * For heap inserts, we only need to check for table-level SSI locks. Our
2340 * new tuples can't possibly conflict with existing tuple locks, and heap
2341 * page locks are only consolidated versions of tuple locks; they do not
2342 * lock "gaps" as index page locks do. So we don't need to specify a
2343 * buffer when making the call, which makes for a faster check.
2344 */
2346
2347 ndone = 0;
2348 while (ndone < ntuples)
2349 {
2350 Buffer buffer;
2351 bool all_visible_cleared = false;
2352 bool all_frozen_set = false;
2353 int nthispage;
2354
2356
2357 /*
2358 * Compute number of pages needed to fit the to-be-inserted tuples in
2359 * the worst case. This will be used to determine how much to extend
2360 * the relation by in RelationGetBufferForTuple(), if needed. If we
2361 * filled a prior page from scratch, we can just update our last
2362 * computation, but if we started with a partially filled page,
2363 * recompute from scratch, the number of potentially required pages
2364 * can vary due to tuples needing to fit onto the page, page headers
2365 * etc.
2366 */
2367 if (ndone == 0 || !starting_with_empty_page)
2368 {
2369 npages = heap_multi_insert_pages(heaptuples, ndone, ntuples,
2371 npages_used = 0;
2372 }
2373 else
2374 npages_used++;
2375
2376 /*
2377 * Find buffer where at least the next tuple will fit. If the page is
2378 * all-visible, this will also pin the requisite visibility map page.
2379 *
2380 * Also pin visibility map page if COPY FREEZE inserts tuples into an
2381 * empty page. See all_frozen_set below.
2382 */
2383 buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
2384 InvalidBuffer, options, bistate,
2385 &vmbuffer, NULL,
2386 npages - npages_used);
2387 page = BufferGetPage(buffer);
2388
2390
2392 {
2393 all_frozen_set = true;
2394 /* Lock the vmbuffer before entering the critical section */
2396 }
2397
2398 /* NO EREPORT(ERROR) from here till changes are logged */
2400
2401 /*
2402 * RelationGetBufferForTuple has ensured that the first tuple fits.
2403 * Put that on the page, and then as many other tuples as fit.
2404 */
2405 RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
2406
2407 /*
2408 * For logical decoding we need combo CIDs to properly decode the
2409 * catalog.
2410 */
2411 if (needwal && need_cids)
2412 log_heap_new_cid(relation, heaptuples[ndone]);
2413
2414 for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
2415 {
2417
2418 if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
2419 break;
2420
2421 RelationPutHeapTuple(relation, buffer, heaptup, false);
2422
2423 /*
2424 * For logical decoding we need combo CIDs to properly decode the
2425 * catalog.
2426 */
2427 if (needwal && need_cids)
2428 log_heap_new_cid(relation, heaptup);
2429 }
2430
2431 /*
2432 * If the page is all visible, need to clear that, unless we're only
2433 * going to add further frozen rows to it.
2434 *
2435 * If we're only adding already frozen rows to a previously empty
2436 * page, mark it as all-frozen and update the visibility map. We're
2437 * already holding a pin on the vmbuffer.
2438 */
2440 {
2441 all_visible_cleared = true;
2442 PageClearAllVisible(page);
2443 visibilitymap_clear(relation,
2444 BufferGetBlockNumber(buffer),
2445 vmbuffer, VISIBILITYMAP_VALID_BITS);
2446 }
2447 else if (all_frozen_set)
2448 {
2449 PageSetAllVisible(page);
2450 PageClearPrunable(page);
2452 vmbuffer,
2455 relation->rd_locator);
2456 }
2457
2458 /*
2459 * Set pd_prune_xid. See heap_insert() for more on why we do this when
2460 * inserting tuples. This only makes sense if we aren't already
2461 * setting the page frozen in the VM and we're not in bootstrap mode.
2462 */
2464 PageSetPrunable(page, xid);
2465
2466 MarkBufferDirty(buffer);
2467
2468 /* XLOG stuff */
2469 if (needwal)
2470 {
2474 char *tupledata;
2475 int totaldatalen;
2476 char *scratchptr = scratch.data;
2477 bool init;
2478 int bufflags = 0;
2479
2480 /*
2481 * If the page was previously empty, we can reinit the page
2482 * instead of restoring the whole thing.
2483 */
2485
2486 /* allocate xl_heap_multi_insert struct from the scratch area */
2489
2490 /*
2491 * Allocate offsets array. Unless we're reinitializing the page,
2492 * in that case the tuples are stored in order starting at
2493 * FirstOffsetNumber and we don't need to store the offsets
2494 * explicitly.
2495 */
2496 if (!init)
2497 scratchptr += nthispage * sizeof(OffsetNumber);
2498
2499 /* the rest of the scratch space is used for tuple data */
2500 tupledata = scratchptr;
2501
2502 /* check that the mutually exclusive flags are not both set */
2504
2505 xlrec->flags = 0;
2508
2509 /*
2510 * We don't have to worry about including a conflict xid in the
2511 * WAL record, as HEAP_INSERT_FROZEN intentionally violates
2512 * visibility rules.
2513 */
2514 if (all_frozen_set)
2516
2517 xlrec->ntuples = nthispage;
2518
2519 /*
2520 * Write out an xl_multi_insert_tuple and the tuple data itself
2521 * for each tuple.
2522 */
2523 for (i = 0; i < nthispage; i++)
2524 {
2526 xl_multi_insert_tuple *tuphdr;
2527 int datalen;
2528
2529 if (!init)
2530 xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
2531 /* xl_multi_insert_tuple needs two-byte alignment. */
2533 scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
2534
2535 tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
2536 tuphdr->t_infomask = heaptup->t_data->t_infomask;
2537 tuphdr->t_hoff = heaptup->t_data->t_hoff;
2538
2539 /* write bitmap [+ padding] [+ oid] + data */
2540 datalen = heaptup->t_len - SizeofHeapTupleHeader;
2542 (char *) heaptup->t_data + SizeofHeapTupleHeader,
2543 datalen);
2544 tuphdr->datalen = datalen;
2545 scratchptr += datalen;
2546 }
2547 totaldatalen = scratchptr - tupledata;
2548 Assert((scratchptr - scratch.data) < BLCKSZ);
2549
2550 if (need_tuple_data)
2552
2553 /*
2554 * Signal that this is the last xl_heap_multi_insert record
2555 * emitted by this call to heap_multi_insert(). Needed for logical
2556 * decoding so it knows when to cleanup temporary data.
2557 */
2558 if (ndone + nthispage == ntuples)
2560
2561 if (init)
2562 {
2563 info |= XLOG_HEAP_INIT_PAGE;
2565 }
2566
2567 /*
2568 * If we're doing logical decoding, include the new tuple data
2569 * even if we take a full-page image of the page.
2570 */
2571 if (need_tuple_data)
2573
2575 XLogRegisterData(xlrec, tupledata - scratch.data);
2577 if (all_frozen_set)
2578 XLogRegisterBuffer(1, vmbuffer, 0);
2579
2580 XLogRegisterBufData(0, tupledata, totaldatalen);
2581
2582 /* filtering by origin on a row level is much more efficient */
2584
2585 recptr = XLogInsert(RM_HEAP2_ID, info);
2586
2587 PageSetLSN(page, recptr);
2588 if (all_frozen_set)
2589 {
2590 Assert(BufferIsDirty(vmbuffer));
2591 PageSetLSN(BufferGetPage(vmbuffer), recptr);
2592 }
2593 }
2594
2596
2597 if (all_frozen_set)
2598 LockBuffer(vmbuffer, BUFFER_LOCK_UNLOCK);
2599
2600 UnlockReleaseBuffer(buffer);
2601 ndone += nthispage;
2602
2603 /*
2604 * NB: Only release vmbuffer after inserting all tuples - it's fairly
2605 * likely that we'll insert into subsequent heap pages that are likely
2606 * to use the same vm page.
2607 */
2608 }
2609
2610 /* We're done with inserting all tuples, so release the last vmbuffer. */
2611 if (vmbuffer != InvalidBuffer)
2612 ReleaseBuffer(vmbuffer);
2613
2614 /*
2615 * We're done with the actual inserts. Check for conflicts again, to
2616 * ensure that all rw-conflicts in to these inserts are detected. Without
2617 * this final check, a sequential scan of the heap may have locked the
2618 * table after the "before" check, missing one opportunity to detect the
2619 * conflict, and then scanned the table before the new tuples were there,
2620 * missing the other chance to detect the conflict.
2621 *
2622 * For heap inserts, we only need to check for table-level SSI locks. Our
2623 * new tuples can't possibly conflict with existing tuple locks, and heap
2624 * page locks are only consolidated versions of tuple locks; they do not
2625 * lock "gaps" as index page locks do. So we don't need to specify a
2626 * buffer when making the call.
2627 */
2629
2630 /*
2631 * If tuples are cacheable, mark them for invalidation from the caches in
2632 * case we abort. Note it is OK to do this after releasing the buffer,
2633 * because the heaptuples data structure is all in local memory, not in
2634 * the shared buffer.
2635 */
2636 if (IsCatalogRelation(relation))
2637 {
2638 for (i = 0; i < ntuples; i++)
2640 }
2641
2642 /* copy t_self fields back to the caller's slots */
2643 for (i = 0; i < ntuples; i++)
2644 slots[i]->tts_tid = heaptuples[i]->t_self;
2645
2646 pgstat_count_heap_insert(relation, ntuples);
2647}
bool BufferIsDirty(Buffer buffer)
Definition bufmgr.c:3114
Size PageGetHeapFreeSpace(const PageData *page)
Definition bufpage.c:1000
static void PageSetAllVisible(Page page)
Definition bufpage.h:459
#define PageClearPrunable(page)
Definition bufpage.h:485
#define MAXALIGN(LEN)
Definition c.h:896
#define SHORTALIGN(LEN)
Definition c.h:892
size_t Size
Definition c.h:689
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
static int heap_multi_insert_pages(HeapTuple *heaptuples, int done, int ntuples, Size saveFreeSpace)
Definition heapam.c:2250
#define SizeOfHeapMultiInsert
#define XLOG_HEAP2_MULTI_INSERT
Definition heapam_xlog.h:64
#define XLH_INSERT_LAST_IN_MULTI
Definition heapam_xlog.h:73
#define XLH_INSERT_ALL_FROZEN_SET
Definition heapam_xlog.h:79
#define SizeOfMultiInsertTuple
void * palloc(Size size)
Definition mcxt.c:1387
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
#define RelationGetTargetPageFreeSpace(relation, defaultff)
Definition rel.h:391
#define HEAP_DEFAULT_FILLFACTOR
Definition rel.h:362
#define init()
RelFileLocator rd_locator
Definition rel.h:57
void visibilitymap_set(BlockNumber heapBlk, Buffer vmBuf, uint8 flags, const RelFileLocator rlocator)
#define VISIBILITYMAP_ALL_VISIBLE

References Assert, AssertHasSnapshotForToast(), BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage(), BufferIsDirty(), CacheInvalidateHeapTuple(), CHECK_FOR_INTERRUPTS, CheckForSerializableConflictIn(), xl_multi_insert_tuple::datalen, END_CRIT_SECTION, ExecFetchSlotHeapTuple(), fb(), GetCurrentTransactionId(), HEAP_DEFAULT_FILLFACTOR, HEAP_INSERT_FROZEN, HEAP_INSERT_NO_LOGICAL, heap_multi_insert_pages(), heap_prepare_insert(), i, init, InvalidBlockNumber, InvalidBuffer, IsCatalogRelation(), ItemPointerGetOffsetNumber(), LockBuffer(), log_heap_new_cid(), MarkBufferDirty(), MAXALIGN, memcpy(), PageClearAllVisible(), PageClearPrunable, PageGetHeapFreeSpace(), PageGetMaxOffsetNumber(), PageIsAllVisible(), PageSetAllVisible(), PageSetLSN(), PageSetPrunable, palloc(), pgstat_count_heap_insert(), RelationData::rd_locator, REGBUF_KEEP_DATA, REGBUF_STANDARD, REGBUF_WILL_INIT, RelationGetBufferForTuple(), RelationGetRelid, RelationGetTargetPageFreeSpace, RelationIsAccessibleInLogicalDecoding, RelationIsLogicallyLogged, RelationNeedsWAL, RelationPutHeapTuple(), ReleaseBuffer(), SHORTALIGN, SizeOfHeapMultiInsert, SizeofHeapTupleHeader, SizeOfMultiInsertTuple, START_CRIT_SECTION, xl_multi_insert_tuple::t_hoff, xl_multi_insert_tuple::t_infomask, xl_multi_insert_tuple::t_infomask2, HeapTupleData::t_tableOid, TransactionIdIsNormal, TupleTableSlot::tts_tableOid, UnlockReleaseBuffer(), VISIBILITYMAP_ALL_FROZEN, VISIBILITYMAP_ALL_VISIBLE, visibilitymap_clear(), visibilitymap_set(), VISIBILITYMAP_VALID_BITS, XLH_INSERT_ALL_FROZEN_SET, XLH_INSERT_ALL_VISIBLE_CLEARED, XLH_INSERT_CONTAINS_NEW_TUPLE, XLH_INSERT_LAST_IN_MULTI, XLOG_HEAP2_MULTI_INSERT, XLOG_HEAP_INIT_PAGE, XLOG_INCLUDE_ORIGIN, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), XLogRegisterData(), and XLogSetRecordFlags().

Referenced by CatalogTuplesMultiInsertWithInfo().

◆ heap_page_prune_and_freeze()

void heap_page_prune_and_freeze ( PruneFreezeParams params,
PruneFreezeResult presult,
OffsetNumber off_loc,
TransactionId new_relfrozen_xid,
MultiXactId new_relmin_mxid 
)
extern

Definition at line 1090 of file pruneheap.c.

1095{
1097 bool do_freeze;
1098 bool do_prune;
1099 bool do_hint_prune;
1100 bool do_set_vm;
1101 bool did_tuple_hint_fpi;
1104
1105 /* Initialize prstate */
1106 prune_freeze_setup(params,
1108 presult, &prstate);
1109
1110 /*
1111 * If the VM is set but PD_ALL_VISIBLE is clear, fix that corruption
1112 * before pruning and freezing so that the page and VM start out in a
1113 * consistent state.
1114 */
1115 if ((prstate.old_vmbits & VISIBILITYMAP_VALID_BITS) &&
1119
1120 /*
1121 * If the page is already all-frozen, or already all-visible when freezing
1122 * is not being attempted, take the fast path, skipping pruning and
1123 * freezing code entirely. This must be done after fixing any discrepancy
1124 * between the page-level visibility hint and the VM, since that may have
1125 * cleared old_vmbits.
1126 */
1127 if ((params->options & HEAP_PAGE_PRUNE_ALLOW_FAST_PATH) != 0 &&
1128 ((prstate.old_vmbits & VISIBILITYMAP_ALL_FROZEN) ||
1129 ((prstate.old_vmbits & VISIBILITYMAP_ALL_VISIBLE) &&
1130 !prstate.attempt_freeze)))
1131 {
1133 return;
1134 }
1135
1136 /*
1137 * Examine all line pointers and tuple visibility information to determine
1138 * which line pointers should change state and which tuples may be frozen.
1139 * Prepare queue of state changes to later be executed in a critical
1140 * section.
1141 */
1143
1144 /*
1145 * After processing all the live tuples on the page, if the newest xmin
1146 * amongst them may be considered running by any snapshot, the page cannot
1147 * be all-visible. This should be done before determining whether or not
1148 * to opportunistically freeze.
1149 */
1150 if (prstate.set_all_visible &&
1151 TransactionIdIsNormal(prstate.newest_live_xid) &&
1153 prstate.newest_live_xid,
1154 true))
1155 prstate.set_all_visible = prstate.set_all_frozen = false;
1156
1157 /*
1158 * If checksums are enabled, calling heap_prune_satisfies_vacuum() while
1159 * checking tuple visibility information in prune_freeze_plan() may have
1160 * caused an FPI to be emitted.
1161 */
1163
1164 do_prune = prstate.nredirected > 0 ||
1165 prstate.ndead > 0 ||
1166 prstate.nunused > 0;
1167
1168 /*
1169 * Even if we don't prune anything, if we found a new value for the
1170 * pd_prune_xid field or the page was marked full, we will update the hint
1171 * bit.
1172 */
1173 do_hint_prune = PageGetPruneXid(prstate.page) != prstate.new_prune_xid ||
1174 PageIsFull(prstate.page);
1175
1176 /*
1177 * Decide if we want to go ahead with freezing according to the freeze
1178 * plans we prepared, or not.
1179 */
1181 do_prune,
1183 &prstate);
1184
1185 /*
1186 * While scanning the line pointers, we did not clear
1187 * set_all_visible/set_all_frozen when encountering LP_DEAD items because
1188 * we wanted the decision whether or not to freeze the page to be
1189 * unaffected by the short-term presence of LP_DEAD items. These LP_DEAD
1190 * items are effectively assumed to be LP_UNUSED items in the making. It
1191 * doesn't matter which vacuum heap pass (initial pass or final pass) ends
1192 * up setting the page all-frozen, as long as the ongoing VACUUM does it.
1193 *
1194 * Now that we finished determining whether or not to freeze the page,
1195 * update set_all_visible and set_all_frozen so that they reflect the true
1196 * state of the page for setting PD_ALL_VISIBLE and VM bits.
1197 */
1198 if (prstate.lpdead_items > 0)
1199 prstate.set_all_visible = prstate.set_all_frozen = false;
1200
1201 Assert(!prstate.set_all_frozen || prstate.set_all_visible);
1202 Assert(!prstate.set_all_visible || prstate.attempt_set_vm);
1203 Assert(!prstate.set_all_visible || (prstate.lpdead_items == 0));
1204
1206
1207 /*
1208 * new_vmbits should be 0 regardless of whether or not the page is
1209 * all-visible if we do not intend to set the VM.
1210 */
1211 Assert(do_set_vm || prstate.new_vmbits == 0);
1212
1213 /*
1214 * The snapshot conflict horizon for the whole record is the most
1215 * conservative (newest) horizon required by any change in the record.
1216 */
1218 if (do_set_vm)
1219 conflict_xid = prstate.newest_live_xid;
1220 if (do_freeze && TransactionIdFollows(prstate.pagefrz.FreezePageConflictXid, conflict_xid))
1221 conflict_xid = prstate.pagefrz.FreezePageConflictXid;
1222 if (do_prune && TransactionIdFollows(prstate.latest_xid_removed, conflict_xid))
1223 conflict_xid = prstate.latest_xid_removed;
1224
1225 /* Lock vmbuffer before entering a critical section */
1226 if (do_set_vm)
1228
1229 /* Any error while applying the changes is critical */
1231
1232 if (do_hint_prune)
1233 {
1234 /*
1235 * Update the page's pd_prune_xid field to either zero, or the lowest
1236 * XID of any soon-prunable tuple.
1237 */
1238 ((PageHeader) prstate.page)->pd_prune_xid = prstate.new_prune_xid;
1239
1240 /*
1241 * Also clear the "page is full" flag, since there's no point in
1242 * repeating the prune/defrag process until something else happens to
1243 * the page.
1244 */
1245 PageClearFull(prstate.page);
1246
1247 /*
1248 * If that's all we had to do to the page, this is a non-WAL-logged
1249 * hint. If we are going to freeze or prune the page or set
1250 * PD_ALL_VISIBLE, we will mark the buffer dirty below.
1251 *
1252 * Setting PD_ALL_VISIBLE is fully WAL-logged because it is forbidden
1253 * for the VM to be set and PD_ALL_VISIBLE to be clear.
1254 */
1255 if (!do_freeze && !do_prune && !do_set_vm)
1256 MarkBufferDirtyHint(prstate.buffer, true);
1257 }
1258
1259 if (do_prune || do_freeze || do_set_vm)
1260 {
1261 /* Apply the planned item changes and repair page fragmentation. */
1262 if (do_prune)
1263 {
1264 heap_page_prune_execute(prstate.buffer, false,
1265 prstate.redirected, prstate.nredirected,
1266 prstate.nowdead, prstate.ndead,
1267 prstate.nowunused, prstate.nunused);
1268 }
1269
1270 if (do_freeze)
1271 heap_freeze_prepared_tuples(prstate.buffer, prstate.frozen, prstate.nfrozen);
1272
1273 /* Set the visibility map and page visibility hint */
1274 if (do_set_vm)
1275 {
1276 /*
1277 * While it is valid for PD_ALL_VISIBLE to be set when the
1278 * corresponding VM bit is clear, we strongly prefer to keep them
1279 * in sync.
1280 *
1281 * The heap buffer must be marked dirty before adding it to the
1282 * WAL chain when setting the VM. We don't worry about
1283 * unnecessarily dirtying the heap buffer if PD_ALL_VISIBLE is
1284 * already set, though. It is extremely rare to have a clean heap
1285 * buffer with PD_ALL_VISIBLE already set and the VM bits clear,
1286 * so there is no point in optimizing it.
1287 */
1290 visibilitymap_set(prstate.block, prstate.vmbuffer, prstate.new_vmbits,
1291 prstate.relation->rd_locator);
1292 }
1293
1294 MarkBufferDirty(prstate.buffer);
1295
1296 /*
1297 * Emit a WAL XLOG_HEAP2_PRUNE* record showing what we did
1298 */
1299 if (RelationNeedsWAL(prstate.relation))
1300 {
1301 log_heap_prune_and_freeze(prstate.relation, prstate.buffer,
1302 do_set_vm ? prstate.vmbuffer : InvalidBuffer,
1303 do_set_vm ? prstate.new_vmbits : 0,
1305 true, /* cleanup lock */
1306 params->reason,
1307 prstate.frozen, prstate.nfrozen,
1308 prstate.redirected, prstate.nredirected,
1309 prstate.nowdead, prstate.ndead,
1310 prstate.nowunused, prstate.nunused);
1311 }
1312 }
1313
1315
1316 if (do_set_vm)
1318
1319 /*
1320 * During its second pass over the heap, VACUUM calls
1321 * heap_page_would_be_all_visible() to determine whether a page is
1322 * all-visible and all-frozen. The logic here is similar. After completing
1323 * pruning and freezing, use an assertion to verify that our results
1324 * remain consistent with heap_page_would_be_all_visible(). It's also a
1325 * valuable cross-check of the page state after pruning and freezing.
1326 */
1327#ifdef USE_ASSERT_CHECKING
1328 if (prstate.set_all_visible)
1329 {
1331 bool debug_all_frozen;
1332
1333 Assert(prstate.lpdead_items == 0);
1334
1336 prstate.vistest,
1339
1341 debug_cutoff == prstate.newest_live_xid);
1342
1343 /*
1344 * It's possible the page is composed entirely of frozen tuples but is
1345 * not set all-frozen in the VM and did not pass
1346 * HEAP_PAGE_PRUNE_FREEZE. In this case, it's possible
1347 * heap_page_is_all_visible() finds the page completely frozen, even
1348 * though prstate.set_all_frozen is false.
1349 */
1350 Assert(!prstate.set_all_frozen || debug_all_frozen);
1351 }
1352#endif
1353
1354 /* Copy information back for caller */
1355 presult->ndeleted = prstate.ndeleted;
1356 presult->nnewlpdead = prstate.ndead;
1357 presult->nfrozen = prstate.nfrozen;
1358 presult->live_tuples = prstate.live_tuples;
1359 presult->recently_dead_tuples = prstate.recently_dead_tuples;
1360 presult->hastup = prstate.hastup;
1361
1362 presult->lpdead_items = prstate.lpdead_items;
1363 /* the presult->deadoffsets array was already filled in */
1364
1365 presult->newly_all_visible = false;
1366 presult->newly_all_frozen = false;
1367 presult->newly_all_visible_frozen = false;
1368 if (do_set_vm)
1369 {
1370 if ((prstate.old_vmbits & VISIBILITYMAP_ALL_VISIBLE) == 0)
1371 {
1372 presult->newly_all_visible = true;
1373 if (prstate.set_all_frozen)
1374 presult->newly_all_visible_frozen = true;
1375 }
1376 else if ((prstate.old_vmbits & VISIBILITYMAP_ALL_FROZEN) == 0 &&
1377 prstate.set_all_frozen)
1378 presult->newly_all_frozen = true;
1379 }
1380
1381 if (prstate.attempt_freeze)
1382 {
1383 if (presult->nfrozen > 0)
1384 {
1385 *new_relfrozen_xid = prstate.pagefrz.FreezePageRelfrozenXid;
1386 *new_relmin_mxid = prstate.pagefrz.FreezePageRelminMxid;
1387 }
1388 else
1389 {
1390 *new_relfrozen_xid = prstate.pagefrz.NoFreezePageRelfrozenXid;
1391 *new_relmin_mxid = prstate.pagefrz.NoFreezePageRelminMxid;
1392 }
1393 }
1394}
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition bufmgr.c:5821
static TransactionId PageGetPruneXid(const PageData *page)
Definition bufpage.h:470
static void PageClearFull(Page page)
Definition bufpage.h:448
static bool PageIsFull(const PageData *page)
Definition bufpage.h:438
int64_t int64
Definition c.h:621
void heap_freeze_prepared_tuples(Buffer buffer, HeapTupleFreeze *tuples, int ntuples)
Definition heapam.c:7360
#define HEAP_PAGE_PRUNE_ALLOW_FAST_PATH
Definition heapam.h:44
WalUsage pgWalUsage
Definition instrument.c:27
return true
Definition isn.c:130
bool GlobalVisTestXidConsideredRunning(GlobalVisState *state, TransactionId xid, bool allow_update)
Definition procarray.c:4328
static void prune_freeze_fast_path(PruneState *prstate, PruneFreezeResult *presult)
Definition pruneheap.c:1007
static void prune_freeze_plan(PruneState *prstate, OffsetNumber *off_loc)
Definition pruneheap.c:531
static bool heap_page_will_freeze(bool did_tuple_hint_fpi, bool do_prune, bool do_hint_prune, PruneState *prstate)
Definition pruneheap.c:734
static bool heap_page_will_set_vm(PruneState *prstate, PruneReason reason, bool do_prune, bool do_freeze)
Definition pruneheap.c:950
@ VM_CORRUPT_MISSING_PAGE_HINT
Definition pruneheap.c:193
void log_heap_prune_and_freeze(Relation relation, Buffer buffer, Buffer vmbuffer, uint8 vmflags, TransactionId conflict_xid, bool cleanup_lock, PruneReason reason, HeapTupleFreeze *frozen, int nfrozen, OffsetNumber *redirected, int nredirected, OffsetNumber *dead, int ndead, OffsetNumber *unused, int nunused)
Definition pruneheap.c:2561
static void prune_freeze_setup(PruneFreezeParams *params, TransactionId *new_relfrozen_xid, MultiXactId *new_relmin_mxid, PruneFreezeResult *presult, PruneState *prstate)
Definition pruneheap.c:400
static void heap_page_fix_vm_corruption(PruneState *prstate, OffsetNumber offnum, VMCorruptionType ctype)
Definition pruneheap.c:852
void heap_page_prune_execute(Buffer buffer, bool lp_truncate_only, OffsetNumber *redirected, int nredirected, OffsetNumber *nowdead, int ndead, OffsetNumber *nowunused, int nunused)
Definition pruneheap.c:2065
PruneReason reason
Definition heapam.h:276
int64 wal_fpi
Definition instrument.h:54
static bool TransactionIdFollows(TransactionId id1, TransactionId id2)
Definition transam.h:297

References Assert, BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, END_CRIT_SECTION, fb(), GlobalVisTestXidConsideredRunning(), heap_freeze_prepared_tuples(), heap_page_fix_vm_corruption(), HEAP_PAGE_PRUNE_ALLOW_FAST_PATH, heap_page_prune_execute(), heap_page_will_freeze(), heap_page_will_set_vm(), InvalidBuffer, InvalidOffsetNumber, InvalidTransactionId, LockBuffer(), log_heap_prune_and_freeze(), MarkBufferDirty(), MarkBufferDirtyHint(), PruneFreezeParams::options, PageClearFull(), PageClearPrunable, PageGetPruneXid(), PageIsAllVisible(), PageIsFull(), PageSetAllVisible(), pgWalUsage, prune_freeze_fast_path(), prune_freeze_plan(), prune_freeze_setup(), PruneFreezeParams::reason, RelationNeedsWAL, START_CRIT_SECTION, TransactionIdFollows(), TransactionIdIsNormal, TransactionIdIsValid, VISIBILITYMAP_ALL_FROZEN, VISIBILITYMAP_ALL_VISIBLE, visibilitymap_set(), VISIBILITYMAP_VALID_BITS, VM_CORRUPT_MISSING_PAGE_HINT, and WalUsage::wal_fpi.

Referenced by heap_page_prune_opt(), and lazy_scan_prune().

◆ heap_page_prune_execute()

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

Definition at line 2065 of file pruneheap.c.

2069{
2070 Page page = BufferGetPage(buffer);
2071 OffsetNumber *offnum;
2073
2074 /* Shouldn't be called unless there's something to do */
2075 Assert(nredirected > 0 || ndead > 0 || nunused > 0);
2076
2077 /* If 'lp_truncate_only', we can only remove already-dead line pointers */
2078 Assert(!lp_truncate_only || (nredirected == 0 && ndead == 0));
2079
2080 /* Update all redirected line pointers */
2081 offnum = redirected;
2082 for (int i = 0; i < nredirected; i++)
2083 {
2084 OffsetNumber fromoff = *offnum++;
2085 OffsetNumber tooff = *offnum++;
2088
2089#ifdef USE_ASSERT_CHECKING
2090
2091 /*
2092 * Any existing item that we set as an LP_REDIRECT (any 'from' item)
2093 * must be the first item from a HOT chain. If the item has tuple
2094 * storage then it can't be a heap-only tuple. Otherwise we are just
2095 * maintaining an existing LP_REDIRECT from an existing HOT chain that
2096 * has been pruned at least once before now.
2097 */
2099 {
2101
2102 htup = (HeapTupleHeader) PageGetItem(page, fromlp);
2104 }
2105 else
2106 {
2107 /* We shouldn't need to redundantly set the redirect */
2109 }
2110
2111 /*
2112 * The item that we're about to set as an LP_REDIRECT (the 'from'
2113 * item) will point to an existing item (the 'to' item) that is
2114 * already a heap-only tuple. There can be at most one LP_REDIRECT
2115 * item per HOT chain.
2116 *
2117 * We need to keep around an LP_REDIRECT item (after original
2118 * non-heap-only root tuple gets pruned away) so that it's always
2119 * possible for VACUUM to easily figure out what TID to delete from
2120 * indexes when an entire HOT chain becomes dead. A heap-only tuple
2121 * can never become LP_DEAD; an LP_REDIRECT item or a regular heap
2122 * tuple can.
2123 *
2124 * This check may miss problems, e.g. the target of a redirect could
2125 * be marked as unused subsequently. The page_verify_redirects() check
2126 * below will catch such problems.
2127 */
2128 tolp = PageGetItemId(page, tooff);
2130 htup = (HeapTupleHeader) PageGetItem(page, tolp);
2132#endif
2133
2135 }
2136
2137 /* Update all now-dead line pointers */
2138 offnum = nowdead;
2139 for (int i = 0; i < ndead; i++)
2140 {
2141 OffsetNumber off = *offnum++;
2142 ItemId lp = PageGetItemId(page, off);
2143
2144#ifdef USE_ASSERT_CHECKING
2145
2146 /*
2147 * An LP_DEAD line pointer must be left behind when the original item
2148 * (which is dead to everybody) could still be referenced by a TID in
2149 * an index. This should never be necessary with any individual
2150 * heap-only tuple item, though. (It's not clear how much of a problem
2151 * that would be, but there is no reason to allow it.)
2152 */
2153 if (ItemIdHasStorage(lp))
2154 {
2156 htup = (HeapTupleHeader) PageGetItem(page, lp);
2158 }
2159 else
2160 {
2161 /* Whole HOT chain becomes dead */
2163 }
2164#endif
2165
2167 }
2168
2169 /* Update all now-unused line pointers */
2170 offnum = nowunused;
2171 for (int i = 0; i < nunused; i++)
2172 {
2173 OffsetNumber off = *offnum++;
2174 ItemId lp = PageGetItemId(page, off);
2175
2176#ifdef USE_ASSERT_CHECKING
2177
2178 if (lp_truncate_only)
2179 {
2180 /* Setting LP_DEAD to LP_UNUSED in vacuum's second pass */
2182 }
2183 else
2184 {
2185 /*
2186 * When heap_page_prune_and_freeze() was called, mark_unused_now
2187 * may have been passed as true, which allows would-be LP_DEAD
2188 * items to be made LP_UNUSED instead. This is only possible if
2189 * the relation has no indexes. If there are any dead items, then
2190 * mark_unused_now was not true and every item being marked
2191 * LP_UNUSED must refer to a heap-only tuple.
2192 */
2193 if (ndead > 0)
2194 {
2196 htup = (HeapTupleHeader) PageGetItem(page, lp);
2198 }
2199 else
2201 }
2202
2203#endif
2204
2206 }
2207
2208 if (lp_truncate_only)
2210 else
2211 {
2212 /*
2213 * Finally, repair any fragmentation, and update the page's hint bit
2214 * about whether it has free pointers.
2215 */
2217
2218 /*
2219 * Now that the page has been modified, assert that redirect items
2220 * still point to valid targets.
2221 */
2223 }
2224}
void PageRepairFragmentation(Page page)
Definition bufpage.c:708
void PageTruncateLinePointerArray(Page page)
Definition bufpage.c:844
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:249
#define ItemIdSetRedirect(itemId, link)
Definition itemid.h:152
#define ItemIdSetDead(itemId)
Definition itemid.h:164
#define ItemIdSetUnused(itemId)
Definition itemid.h:128
#define ItemIdHasStorage(itemId)
Definition itemid.h:120
static void page_verify_redirects(Page page)
Definition pruneheap.c:2241

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

Referenced by heap_page_prune_and_freeze(), and heap_xlog_prune_freeze().

◆ heap_page_prune_opt()

void heap_page_prune_opt ( Relation  relation,
Buffer  buffer,
Buffer vmbuffer,
bool  rel_read_only 
)
extern

Definition at line 271 of file pruneheap.c.

273{
274 Page page = BufferGetPage(buffer);
276 GlobalVisState *vistest;
278
279 /*
280 * We can't write WAL in recovery mode, so there's no point trying to
281 * clean the page. The primary will likely issue a cleaning WAL record
282 * soon anyway, so this is no particular loss.
283 */
284 if (RecoveryInProgress())
285 return;
286
287 /*
288 * First check whether there's any chance there's something to prune,
289 * determining the appropriate horizon is a waste if there's no prune_xid
290 * (i.e. no updates/deletes left potentially dead tuples around and no
291 * inserts inserted new tuples that may be visible to all).
292 */
295 return;
296
297 /*
298 * Check whether prune_xid indicates that there may be dead rows that can
299 * be cleaned up.
300 */
301 vistest = GlobalVisTestFor(relation);
302
303 if (!GlobalVisTestIsRemovableXid(vistest, prune_xid, true))
304 return;
305
306 /*
307 * We prune when a previous UPDATE failed to find enough space on the page
308 * for a new tuple version, or when free space falls below the relation's
309 * fill-factor target (but not less than 10%).
310 *
311 * Checking free space here is questionable since we aren't holding any
312 * lock on the buffer; in the worst case we could get a bogus answer. It's
313 * unlikely to be *seriously* wrong, though, since reading either pd_lower
314 * or pd_upper is probably atomic. Avoiding taking a lock seems more
315 * important than sometimes getting a wrong answer in what is after all
316 * just a heuristic estimate.
317 */
320 minfree = Max(minfree, BLCKSZ / 10);
321
322 if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
323 {
324 /* OK, try to get exclusive buffer lock */
326 return;
327
328 /*
329 * Now that we have buffer lock, get accurate information about the
330 * page's free space, and recheck the heuristic about whether to
331 * prune.
332 */
333 if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
334 {
337 PruneFreezeParams params;
338
339 visibilitymap_pin(relation, BufferGetBlockNumber(buffer),
340 vmbuffer);
341
342 params.relation = relation;
343 params.buffer = buffer;
344 params.vmbuffer = *vmbuffer;
345 params.reason = PRUNE_ON_ACCESS;
346 params.vistest = vistest;
347 params.cutoffs = NULL;
348
349 /*
350 * We don't pass the HEAP_PAGE_PRUNE_MARK_UNUSED_NOW option
351 * regardless of whether or not the relation has indexes, since we
352 * cannot safely determine that during on-access pruning with the
353 * current implementation.
354 */
356 if (rel_read_only)
358
360 NULL, NULL);
361
362 /*
363 * Report the number of tuples reclaimed to pgstats. This is
364 * presult.ndeleted minus the number of newly-LP_DEAD-set items.
365 *
366 * We derive the number of dead tuples like this to avoid totally
367 * forgetting about items that were set to LP_DEAD, since they
368 * still need to be cleaned up by VACUUM. We only want to count
369 * heap-only tuples that just became LP_UNUSED in our report,
370 * which don't.
371 *
372 * VACUUM doesn't have to compensate in the same way when it
373 * tracks ndeleted, since it will set the same LP_DEAD items to
374 * LP_UNUSED separately.
375 */
376 if (presult.ndeleted > presult.nnewlpdead)
378 presult.ndeleted - presult.nnewlpdead);
379 }
380
381 /* And release buffer lock */
383
384 /*
385 * We avoid reuse of any free space created on the page by unrelated
386 * UPDATEs/INSERTs by opting to not update the FSM at this point. The
387 * free space should be reused by UPDATEs to *this* page.
388 */
389 }
390}
bool ConditionalLockBufferForCleanup(Buffer buffer)
Definition bufmgr.c:6843
#define Max(x, y)
Definition c.h:1085
#define HEAP_PAGE_PRUNE_SET_VM
Definition heapam.h:45
void pgstat_update_heap_dead_tuples(Relation rel, int delta)
bool GlobalVisTestIsRemovableXid(GlobalVisState *state, TransactionId xid, bool allow_update)
Definition procarray.c:4290
void heap_page_prune_and_freeze(PruneFreezeParams *params, PruneFreezeResult *presult, OffsetNumber *off_loc, TransactionId *new_relfrozen_xid, MultiXactId *new_relmin_mxid)
Definition pruneheap.c:1090
VacuumCutoffs * cutoffs
Definition heapam.h:301
GlobalVisState * vistest
Definition heapam.h:292
Relation relation
Definition heapam.h:262
Buffer vmbuffer
Definition heapam.h:270
bool RecoveryInProgress(void)
Definition xlog.c:6830

References PruneFreezeParams::buffer, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage(), ConditionalLockBufferForCleanup(), PruneFreezeParams::cutoffs, fb(), GlobalVisTestFor(), GlobalVisTestIsRemovableXid(), HEAP_DEFAULT_FILLFACTOR, HEAP_PAGE_PRUNE_ALLOW_FAST_PATH, heap_page_prune_and_freeze(), HEAP_PAGE_PRUNE_SET_VM, LockBuffer(), Max, PruneFreezeParams::options, PageGetHeapFreeSpace(), PageGetPruneXid(), PageIsFull(), pgstat_update_heap_dead_tuples(), PRUNE_ON_ACCESS, PruneFreezeParams::reason, RecoveryInProgress(), PruneFreezeParams::relation, RelationGetTargetPageFreeSpace, TransactionIdIsValid, visibilitymap_pin(), PruneFreezeParams::vistest, and PruneFreezeParams::vmbuffer.

Referenced by BitmapHeapScanNextBlock(), heap_prepare_pagescan(), and heapam_index_fetch_tuple().

◆ heap_pre_freeze_checks()

void heap_pre_freeze_checks ( Buffer  buffer,
HeapTupleFreeze tuples,
int  ntuples 
)
extern

Definition at line 7307 of file heapam.c.

7309{
7310 Page page = BufferGetPage(buffer);
7311
7312 for (int i = 0; i < ntuples; i++)
7313 {
7314 HeapTupleFreeze *frz = tuples + i;
7315 ItemId itemid = PageGetItemId(page, frz->offset);
7316 HeapTupleHeader htup;
7317
7318 htup = (HeapTupleHeader) PageGetItem(page, itemid);
7319
7320 /* Deliberately avoid relying on tuple hint bits here */
7321 if (frz->checkflags & HEAP_FREEZE_CHECK_XMIN_COMMITTED)
7322 {
7324
7326 if (unlikely(!TransactionIdDidCommit(xmin)))
7327 ereport(ERROR,
7329 errmsg_internal("uncommitted xmin %u needs to be frozen",
7330 xmin)));
7331 }
7332
7333 /*
7334 * TransactionIdDidAbort won't work reliably in the presence of XIDs
7335 * left behind by transactions that were in progress during a crash,
7336 * so we can only check that xmax didn't commit
7337 */
7338 if (frz->checkflags & HEAP_FREEZE_CHECK_XMAX_ABORTED)
7339 {
7341
7344 ereport(ERROR,
7346 errmsg_internal("cannot freeze committed xmax %u",
7347 xmax)));
7348 }
7349 }
7350}
#define HEAP_FREEZE_CHECK_XMAX_ABORTED
Definition heapam.h:150
#define HEAP_FREEZE_CHECK_XMIN_COMMITTED
Definition heapam.h:149
static bool HeapTupleHeaderXminFrozen(const HeapTupleHeaderData *tup)
static TransactionId HeapTupleHeaderGetRawXmin(const HeapTupleHeaderData *tup)
#define ERRCODE_DATA_CORRUPTED
bool TransactionIdDidCommit(TransactionId transactionId)
Definition transam.c:126

References Assert, BufferGetPage(), ereport, errcode(), ERRCODE_DATA_CORRUPTED, errmsg_internal(), ERROR, fb(), HEAP_FREEZE_CHECK_XMAX_ABORTED, HEAP_FREEZE_CHECK_XMIN_COMMITTED, HeapTupleHeaderGetRawXmax(), HeapTupleHeaderGetRawXmin(), HeapTupleHeaderXminFrozen(), i, PageGetItem(), PageGetItemId(), TransactionIdDidCommit(), TransactionIdIsNormal, and unlikely.

Referenced by heap_page_will_freeze().

◆ heap_prepare_freeze_tuple()

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

◆ heap_prepare_pagescan()

void heap_prepare_pagescan ( TableScanDesc  sscan)
extern

Definition at line 618 of file heapam.c.

619{
621 Buffer buffer = scan->rs_cbuf;
622 BlockNumber block = scan->rs_cblock;
623 Snapshot snapshot;
624 Page page;
625 int lines;
626 bool all_visible;
628
629 Assert(BufferGetBlockNumber(buffer) == block);
630
631 /* ensure we're not accidentally being used when not in pagemode */
633 snapshot = scan->rs_base.rs_snapshot;
634
635 /*
636 * Prune and repair fragmentation for the whole page, if possible.
637 */
638 heap_page_prune_opt(scan->rs_base.rs_rd, buffer, &scan->rs_vmbuffer,
639 sscan->rs_flags & SO_HINT_REL_READ_ONLY);
640
641 /*
642 * We must hold share lock on the buffer content while examining tuple
643 * visibility. Afterwards, however, the tuples we have found to be
644 * visible are guaranteed good as long as we hold the buffer pin.
645 */
647
648 page = BufferGetPage(buffer);
649 lines = PageGetMaxOffsetNumber(page);
650
651 /*
652 * If the all-visible flag indicates that all tuples on the page are
653 * visible to everyone, we can skip the per-tuple visibility tests.
654 *
655 * Note: In hot standby, a tuple that's already visible to all
656 * transactions on the primary might still be invisible to a read-only
657 * transaction in the standby. We partly handle this problem by tracking
658 * the minimum xmin of visible tuples as the cut-off XID while marking a
659 * page all-visible on the primary and WAL log that along with the
660 * visibility map SET operation. In hot standby, we wait for (or abort)
661 * all transactions that can potentially may not see one or more tuples on
662 * the page. That's how index-only scans work fine in hot standby. A
663 * crucial difference between index-only scans and heap scans is that the
664 * index-only scan completely relies on the visibility map where as heap
665 * scan looks at the page-level PD_ALL_VISIBLE flag. We are not sure if
666 * the page-level flag can be trusted in the same way, because it might
667 * get propagated somehow without being explicitly WAL-logged, e.g. via a
668 * full page write. Until we can prove that beyond doubt, let's check each
669 * tuple for visibility the hard way.
670 */
671 all_visible = PageIsAllVisible(page) && !snapshot->takenDuringRecovery;
674
675 /*
676 * We call page_collect_tuples() with constant arguments, to get the
677 * compiler to constant fold the constant arguments. Separate calls with
678 * constant arguments, rather than variables, are needed on several
679 * compilers to actually perform constant folding.
680 */
681 if (likely(all_visible))
682 {
684 scan->rs_ntuples = page_collect_tuples(scan, snapshot, page, buffer,
685 block, lines, true, false);
686 else
687 scan->rs_ntuples = page_collect_tuples(scan, snapshot, page, buffer,
688 block, lines, true, true);
689 }
690 else
691 {
693 scan->rs_ntuples = page_collect_tuples(scan, snapshot, page, buffer,
694 block, lines, false, false);
695 else
696 scan->rs_ntuples = page_collect_tuples(scan, snapshot, page, buffer,
697 block, lines, false, true);
698 }
699
701}
#define likely(x)
Definition c.h:437
static pg_attribute_always_inline int page_collect_tuples(HeapScanDesc scan, Snapshot snapshot, Page page, Buffer buffer, BlockNumber block, int lines, bool all_visible, bool check_serializable)
Definition heapam.c:524
bool CheckForSerializableConflictOutNeeded(Relation relation, Snapshot snapshot)
Definition predicate.c:3921
void heap_page_prune_opt(Relation relation, Buffer buffer, Buffer *vmbuffer, bool rel_read_only)
Definition pruneheap.c:271
uint32 rs_ntuples
Definition heapam.h:104
BlockNumber rs_cblock
Definition heapam.h:71
bool takenDuringRecovery
Definition snapshot.h:180
@ SO_HINT_REL_READ_ONLY
Definition tableam.h:71

References Assert, BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage(), CheckForSerializableConflictOutNeeded(), fb(), heap_page_prune_opt(), likely, LockBuffer(), page_collect_tuples(), PageGetMaxOffsetNumber(), PageIsAllVisible(), HeapScanDescData::rs_base, HeapScanDescData::rs_cblock, HeapScanDescData::rs_cbuf, TableScanDescData::rs_flags, HeapScanDescData::rs_ntuples, TableScanDescData::rs_rd, TableScanDescData::rs_snapshot, HeapScanDescData::rs_vmbuffer, SO_ALLOW_PAGEMODE, SO_HINT_REL_READ_ONLY, and SnapshotData::takenDuringRecovery.

Referenced by heapam_scan_sample_next_block(), and heapgettup_pagemode().

◆ heap_rescan()

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

Definition at line 1331 of file heapam.c.

1333{
1335
1336 if (set_params)
1337 {
1338 if (allow_strat)
1340 else
1342
1343 if (allow_sync)
1345 else
1347
1348 if (allow_pagemode && scan->rs_base.rs_snapshot &&
1351 else
1353 }
1354
1355 /*
1356 * unpin scan buffers
1357 */
1358 if (BufferIsValid(scan->rs_cbuf))
1359 {
1360 ReleaseBuffer(scan->rs_cbuf);
1361 scan->rs_cbuf = InvalidBuffer;
1362 }
1363
1364 if (BufferIsValid(scan->rs_vmbuffer))
1365 {
1367 scan->rs_vmbuffer = InvalidBuffer;
1368 }
1369
1370 /*
1371 * SO_TYPE_BITMAPSCAN would be cleaned up here, but it does not hold any
1372 * additional data vs a normal HeapScan
1373 */
1374
1375 /*
1376 * The read stream is reset on rescan. This must be done before
1377 * initscan(), as some state referred to by read_stream_reset() is reset
1378 * in initscan().
1379 */
1380 if (scan->rs_read_stream)
1382
1383 /*
1384 * reinitialize scan descriptor
1385 */
1386 initscan(scan, key, true);
1387}
void read_stream_reset(ReadStream *stream)
@ SO_ALLOW_STRAT
Definition tableam.h:61
@ SO_ALLOW_SYNC
Definition tableam.h:63

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

◆ heap_set_tidrange()

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

Definition at line 1504 of file heapam.c.

1506{
1512
1513 /*
1514 * For relations without any pages, we can simply leave the TID range
1515 * unset. There will be no tuples to scan, therefore no tuples outside
1516 * the given TID range.
1517 */
1518 if (scan->rs_nblocks == 0)
1519 return;
1520
1521 /*
1522 * Set up some ItemPointers which point to the first and last possible
1523 * tuples in the heap.
1524 */
1527
1528 /*
1529 * If the given maximum TID is below the highest possible TID in the
1530 * relation, then restrict the range to that, otherwise we scan to the end
1531 * of the relation.
1532 */
1535
1536 /*
1537 * If the given minimum TID is above the lowest possible TID in the
1538 * relation, then restrict the range to only scan for TIDs above that.
1539 */
1542
1543 /*
1544 * Check for an empty range and protect from would be negative results
1545 * from the numBlks calculation below.
1546 */
1548 {
1549 /* Set an empty range of blocks to scan */
1551 return;
1552 }
1553
1554 /*
1555 * Calculate the first block and the number of blocks we must scan. We
1556 * could be more aggressive here and perform some more validation to try
1557 * and further narrow the scope of blocks to scan by checking if the
1558 * lowestItem has an offset above MaxOffsetNumber. In this case, we could
1559 * advance startBlk by one. Likewise, if highestItem has an offset of 0
1560 * we could scan one fewer blocks. However, such an optimization does not
1561 * seem worth troubling over, currently.
1562 */
1564
1567
1568 /* Set the start block and number of blocks to scan */
1570
1571 /* Finally, set the TID range in sscan */
1572 ItemPointerCopy(&lowestItem, &sscan->st.tidrange.rs_mintid);
1573 ItemPointerCopy(&highestItem, &sscan->st.tidrange.rs_maxtid);
1574}
void heap_setscanlimits(TableScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
Definition heapam.c:502
static BlockNumber ItemPointerGetBlockNumberNoCheck(const ItemPointerData *pointer)
Definition itemptr.h:93
#define MaxOffsetNumber
Definition off.h:28
BlockNumber rs_nblocks
Definition heapam.h:63

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

◆ heap_setscanlimits()

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

Definition at line 502 of file heapam.c.

503{
505
506 Assert(!scan->rs_inited); /* else too late to change */
507 /* else rs_startblock is significant */
509
510 /* Check startBlk is valid (but allow case of zero blocks...) */
511 Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
512
513 scan->rs_startblock = startBlk;
514 scan->rs_numblocks = numBlks;
515}
BlockNumber rs_startblock
Definition heapam.h:64
BlockNumber rs_numblocks
Definition heapam.h:65

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

Referenced by heap_set_tidrange(), and heapam_index_build_range_scan().

◆ heap_tuple_needs_eventual_freeze()

bool heap_tuple_needs_eventual_freeze ( HeapTupleHeader  tuple)
extern

Definition at line 7791 of file heapam.c.

7792{
7793 TransactionId xid;
7794
7795 /*
7796 * If xmin is a normal transaction ID, this tuple is definitely not
7797 * frozen.
7798 */
7799 xid = HeapTupleHeaderGetXmin(tuple);
7800 if (TransactionIdIsNormal(xid))
7801 return true;
7802
7803 /*
7804 * If xmax is a valid xact or multixact, this tuple is also not frozen.
7805 */
7806 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
7807 {
7808 MultiXactId multi;
7809
7810 multi = HeapTupleHeaderGetRawXmax(tuple);
7811 if (MultiXactIdIsValid(multi))
7812 return true;
7813 }
7814 else
7815 {
7816 xid = HeapTupleHeaderGetRawXmax(tuple);
7817 if (TransactionIdIsNormal(xid))
7818 return true;
7819 }
7820
7821 if (tuple->t_infomask & HEAP_MOVED)
7822 {
7823 xid = HeapTupleHeaderGetXvac(tuple);
7824 if (TransactionIdIsNormal(xid))
7825 return true;
7826 }
7827
7828 return false;
7829}
static TransactionId HeapTupleHeaderGetXvac(const HeapTupleHeaderData *tup)
#define MultiXactIdIsValid(multi)
Definition multixact.h:29

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

Referenced by collect_corrupt_items(), and heap_page_would_be_all_visible().

◆ heap_tuple_should_freeze()

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

◆ heap_update()

TM_Result heap_update ( Relation  relation,
const ItemPointerData otid,
HeapTuple  newtup,
CommandId  cid,
uint32  options,
Snapshot  crosscheck,
bool  wait,
TM_FailureData tmfd,
LockTupleMode lockmode,
TU_UpdateIndexes update_indexes 
)
extern

◆ heap_vacuum_rel()

void heap_vacuum_rel ( Relation  rel,
const VacuumParams params,
BufferAccessStrategy  bstrategy 
)
extern

Definition at line 624 of file vacuumlazy.c.

626{
628 bool verbose,
629 instrument,
630 skipwithvm,
638 TimestampTz starttime = 0;
640 startwritetime = 0;
643 ErrorContextCallback errcallback;
644 char **indnames = NULL;
646
647 verbose = (params->options & VACOPT_VERBOSE) != 0;
648 instrument = (verbose || (AmAutoVacuumWorkerProcess() &&
649 params->log_vacuum_min_duration >= 0));
650 if (instrument)
651 {
653 if (track_io_timing)
654 {
657 }
658 }
659
660 /* Used for instrumentation and stats report */
661 starttime = GetCurrentTimestamp();
662
664 RelationGetRelid(rel));
667 params->is_wraparound
670 else
673
674 /*
675 * Setup error traceback support for ereport() first. The idea is to set
676 * up an error context callback to display additional information on any
677 * error during a vacuum. During different phases of vacuum, we update
678 * the state so that the error context callback always display current
679 * information.
680 *
681 * Copy the names of heap rel into local memory for error reporting
682 * purposes, too. It isn't always safe to assume that we can get the name
683 * of each rel. It's convenient for code in lazy_scan_heap to always use
684 * these temp copies.
685 */
688 vacrel->relnamespace = get_namespace_name(RelationGetNamespace(rel));
689 vacrel->relname = pstrdup(RelationGetRelationName(rel));
690 vacrel->indname = NULL;
692 vacrel->verbose = verbose;
693 errcallback.callback = vacuum_error_callback;
694 errcallback.arg = vacrel;
695 errcallback.previous = error_context_stack;
696 error_context_stack = &errcallback;
697
698 /* Set up high level stuff about rel and its indexes */
699 vacrel->rel = rel;
701 &vacrel->indrels);
702 vacrel->bstrategy = bstrategy;
703 if (instrument && vacrel->nindexes > 0)
704 {
705 /* Copy index names used by instrumentation (not error reporting) */
706 indnames = palloc_array(char *, vacrel->nindexes);
707 for (int i = 0; i < vacrel->nindexes; i++)
709 }
710
711 /*
712 * The index_cleanup param either disables index vacuuming and cleanup or
713 * forces it to go ahead when we would otherwise apply the index bypass
714 * optimization. The default is 'auto', which leaves the final decision
715 * up to lazy_vacuum().
716 *
717 * The truncate param allows user to avoid attempting relation truncation,
718 * though it can't force truncation to happen.
719 */
722 params->truncate != VACOPTVALUE_AUTO);
723
724 /*
725 * While VacuumFailSafeActive is reset to false before calling this, we
726 * still need to reset it here due to recursive calls.
727 */
728 VacuumFailsafeActive = false;
729 vacrel->consider_bypass_optimization = true;
730 vacrel->do_index_vacuuming = true;
731 vacrel->do_index_cleanup = true;
732 vacrel->do_rel_truncate = (params->truncate != VACOPTVALUE_DISABLED);
733 if (params->index_cleanup == VACOPTVALUE_DISABLED)
734 {
735 /* Force disable index vacuuming up-front */
736 vacrel->do_index_vacuuming = false;
737 vacrel->do_index_cleanup = false;
738 }
739 else if (params->index_cleanup == VACOPTVALUE_ENABLED)
740 {
741 /* Force index vacuuming. Note that failsafe can still bypass. */
742 vacrel->consider_bypass_optimization = false;
743 }
744 else
745 {
746 /* Default/auto, make all decisions dynamically */
748 }
749
750 /* Initialize page counters explicitly (be tidy) */
751 vacrel->scanned_pages = 0;
752 vacrel->eager_scanned_pages = 0;
753 vacrel->removed_pages = 0;
754 vacrel->new_frozen_tuple_pages = 0;
755 vacrel->lpdead_item_pages = 0;
756 vacrel->missed_dead_pages = 0;
757 vacrel->nonempty_pages = 0;
758 /* dead_items_alloc allocates vacrel->dead_items later on */
759
760 /* Allocate/initialize output statistics state */
761 vacrel->new_rel_tuples = 0;
762 vacrel->new_live_tuples = 0;
763 vacrel->indstats = (IndexBulkDeleteResult **)
764 palloc0(vacrel->nindexes * sizeof(IndexBulkDeleteResult *));
765
766 /* Initialize remaining counters (be tidy) */
767 vacrel->num_index_scans = 0;
768 vacrel->num_dead_items_resets = 0;
769 vacrel->total_dead_items_bytes = 0;
770 vacrel->tuples_deleted = 0;
771 vacrel->tuples_frozen = 0;
772 vacrel->lpdead_items = 0;
773 vacrel->live_tuples = 0;
774 vacrel->recently_dead_tuples = 0;
775 vacrel->missed_dead_tuples = 0;
776
777 vacrel->new_all_visible_pages = 0;
778 vacrel->new_all_visible_all_frozen_pages = 0;
779 vacrel->new_all_frozen_pages = 0;
780
781 vacrel->worker_usage.vacuum.nlaunched = 0;
782 vacrel->worker_usage.vacuum.nplanned = 0;
783 vacrel->worker_usage.cleanup.nlaunched = 0;
784 vacrel->worker_usage.cleanup.nplanned = 0;
785
786 /*
787 * Get cutoffs that determine which deleted tuples are considered DEAD,
788 * not just RECENTLY_DEAD, and which XIDs/MXIDs to freeze. Then determine
789 * the extent of the blocks that we'll scan in lazy_scan_heap. It has to
790 * happen in this order to ensure that the OldestXmin cutoff field works
791 * as an upper bound on the XIDs stored in the pages we'll actually scan
792 * (NewRelfrozenXid tracking must never be allowed to miss unfrozen XIDs).
793 *
794 * Next acquire vistest, a related cutoff that's used in pruning. We use
795 * vistest in combination with OldestXmin to ensure that
796 * heap_page_prune_and_freeze() always removes any deleted tuple whose
797 * xmax is < OldestXmin. lazy_scan_prune must never become confused about
798 * whether a tuple should be frozen or removed. (In the future we might
799 * want to teach lazy_scan_prune to recompute vistest from time to time,
800 * to increase the number of dead tuples it can prune away.)
801 */
802 vacrel->aggressive = vacuum_get_cutoffs(rel, params, &vacrel->cutoffs);
804 vacrel->vistest = GlobalVisTestFor(rel);
805
806 /* Initialize state used to track oldest extant XID/MXID */
807 vacrel->NewRelfrozenXid = vacrel->cutoffs.OldestXmin;
808 vacrel->NewRelminMxid = vacrel->cutoffs.OldestMxact;
809
810 /*
811 * Initialize state related to tracking all-visible page skipping. This is
812 * very important to determine whether or not it is safe to advance the
813 * relfrozenxid/relminmxid.
814 */
815 vacrel->skippedallvis = false;
816 skipwithvm = true;
818 {
819 /*
820 * Force aggressive mode, and disable skipping blocks using the
821 * visibility map (even those set all-frozen)
822 */
823 vacrel->aggressive = true;
824 skipwithvm = false;
825 }
826
827 vacrel->skipwithvm = skipwithvm;
828
829 /*
830 * Set up eager scan tracking state. This must happen after determining
831 * whether or not the vacuum must be aggressive, because only normal
832 * vacuums use the eager scan algorithm.
833 */
835
836 /* Report the vacuum mode: 'normal' or 'aggressive' */
838 vacrel->aggressive
841
842 if (verbose)
843 {
844 if (vacrel->aggressive)
846 (errmsg("aggressively vacuuming \"%s.%s.%s\"",
847 vacrel->dbname, vacrel->relnamespace,
848 vacrel->relname)));
849 else
851 (errmsg("vacuuming \"%s.%s.%s\"",
852 vacrel->dbname, vacrel->relnamespace,
853 vacrel->relname)));
854 }
855
856 /*
857 * Allocate dead_items memory using dead_items_alloc. This handles
858 * parallel VACUUM initialization as part of allocating shared memory
859 * space used for dead_items. (But do a failsafe precheck first, to
860 * ensure that parallel VACUUM won't be attempted at all when relfrozenxid
861 * is already dangerously old.)
862 */
865
866#ifdef USE_INJECTION_POINTS
867
868 /*
869 * Used by tests to pause before parallel vacuum is launched, allowing
870 * test code to modify configuration that the leader then propagates to
871 * workers.
872 */
874 INJECTION_POINT("autovacuum-start-parallel-vacuum", NULL);
875#endif
876
877 /*
878 * Call lazy_scan_heap to perform all required heap pruning, index
879 * vacuuming, and heap vacuuming (plus related processing)
880 */
882
883 /*
884 * Save dead items max_bytes and update the memory usage statistics before
885 * cleanup, they are freed in parallel vacuum cases during
886 * dead_items_cleanup().
887 */
888 dead_items_max_bytes = vacrel->dead_items_info->max_bytes;
889 vacrel->total_dead_items_bytes += TidStoreMemoryUsage(vacrel->dead_items);
890
891 /*
892 * Free resources managed by dead_items_alloc. This ends parallel mode in
893 * passing when necessary.
894 */
897
898 /*
899 * Update pg_class entries for each of rel's indexes where appropriate.
900 *
901 * Unlike the later update to rel's pg_class entry, this is not critical.
902 * Maintains relpages/reltuples statistics used by the planner only.
903 */
904 if (vacrel->do_index_cleanup)
906
907 /* Done with rel's indexes */
908 vac_close_indexes(vacrel->nindexes, vacrel->indrels, NoLock);
909
910 /* Optionally truncate rel */
913
914 /* Pop the error context stack */
915 error_context_stack = errcallback.previous;
916
917 /* Report that we are now doing final cleanup */
920
921 /*
922 * Prepare to update rel's pg_class entry.
923 *
924 * Aggressive VACUUMs must always be able to advance relfrozenxid to a
925 * value >= FreezeLimit, and relminmxid to a value >= MultiXactCutoff.
926 * Non-aggressive VACUUMs may advance them by any amount, or not at all.
927 */
928 Assert(vacrel->NewRelfrozenXid == vacrel->cutoffs.OldestXmin ||
929 TransactionIdPrecedesOrEquals(vacrel->aggressive ? vacrel->cutoffs.FreezeLimit :
930 vacrel->cutoffs.relfrozenxid,
931 vacrel->NewRelfrozenXid));
932 Assert(vacrel->NewRelminMxid == vacrel->cutoffs.OldestMxact ||
933 MultiXactIdPrecedesOrEquals(vacrel->aggressive ? vacrel->cutoffs.MultiXactCutoff :
934 vacrel->cutoffs.relminmxid,
935 vacrel->NewRelminMxid));
936 if (vacrel->skippedallvis)
937 {
938 /*
939 * Must keep original relfrozenxid in a non-aggressive VACUUM that
940 * chose to skip an all-visible page range. The state that tracks new
941 * values will have missed unfrozen XIDs from the pages we skipped.
942 */
943 Assert(!vacrel->aggressive);
944 vacrel->NewRelfrozenXid = InvalidTransactionId;
945 vacrel->NewRelminMxid = InvalidMultiXactId;
946 }
947
948 /*
949 * For safety, clamp relallvisible to be not more than what we're setting
950 * pg_class.relpages to
951 */
952 new_rel_pages = vacrel->rel_pages; /* After possible rel truncation */
956
957 /*
958 * An all-frozen block _must_ be all-visible. As such, clamp the count of
959 * all-frozen blocks to the count of all-visible blocks. This matches the
960 * clamping of relallvisible above.
961 */
964
965 /*
966 * Now actually update rel's pg_class entry.
967 *
968 * In principle new_live_tuples could be -1 indicating that we (still)
969 * don't know the tuple count. In practice that can't happen, since we
970 * scan every page that isn't skipped using the visibility map.
971 */
972 vac_update_relstats(rel, new_rel_pages, vacrel->new_live_tuples,
974 vacrel->nindexes > 0,
975 vacrel->NewRelfrozenXid, vacrel->NewRelminMxid,
977
978 /*
979 * Report results to the cumulative stats system, too.
980 *
981 * Deliberately avoid telling the stats system about LP_DEAD items that
982 * remain in the table due to VACUUM bypassing index and heap vacuuming.
983 * ANALYZE will consider the remaining LP_DEAD items to be dead "tuples".
984 * It seems like a good idea to err on the side of not vacuuming again too
985 * soon in cases where the failsafe prevented significant amounts of heap
986 * vacuuming.
987 */
989 Max(vacrel->new_live_tuples, 0),
990 vacrel->recently_dead_tuples +
991 vacrel->missed_dead_tuples,
992 starttime);
994
995 if (instrument)
996 {
998
999 if (verbose || params->log_vacuum_min_duration == 0 ||
1001 params->log_vacuum_min_duration))
1002 {
1003 long secs_dur;
1004 int usecs_dur;
1005 WalUsage walusage;
1006 BufferUsage bufferusage;
1008 char *msgfmt;
1009 int32 diff;
1010 double read_rate = 0,
1011 write_rate = 0;
1015
1017 memset(&walusage, 0, sizeof(WalUsage));
1019 memset(&bufferusage, 0, sizeof(BufferUsage));
1021
1022 total_blks_hit = bufferusage.shared_blks_hit +
1023 bufferusage.local_blks_hit;
1024 total_blks_read = bufferusage.shared_blks_read +
1025 bufferusage.local_blks_read;
1027 bufferusage.local_blks_dirtied;
1028
1030 if (verbose)
1031 {
1032 /*
1033 * Aggressiveness already reported earlier, in dedicated
1034 * VACUUM VERBOSE ereport
1035 */
1036 Assert(!params->is_wraparound);
1037 msgfmt = _("finished vacuuming \"%s.%s.%s\": index scans: %d\n");
1038 }
1039 else if (params->is_wraparound)
1040 {
1041 /*
1042 * While it's possible for a VACUUM to be both is_wraparound
1043 * and !aggressive, that's just a corner-case -- is_wraparound
1044 * implies aggressive. Produce distinct output for the corner
1045 * case all the same, just in case.
1046 */
1047 if (vacrel->aggressive)
1048 msgfmt = _("automatic aggressive vacuum to prevent wraparound of table \"%s.%s.%s\": index scans: %d\n");
1049 else
1050 msgfmt = _("automatic vacuum to prevent wraparound of table \"%s.%s.%s\": index scans: %d\n");
1051 }
1052 else
1053 {
1054 if (vacrel->aggressive)
1055 msgfmt = _("automatic aggressive vacuum of table \"%s.%s.%s\": index scans: %d\n");
1056 else
1057 msgfmt = _("automatic vacuum of table \"%s.%s.%s\": index scans: %d\n");
1058 }
1060 vacrel->dbname,
1061 vacrel->relnamespace,
1062 vacrel->relname,
1063 vacrel->num_index_scans);
1064 appendStringInfo(&buf, _("pages: %u removed, %u remain, %u scanned (%.2f%% of total), %u eagerly scanned\n"),
1065 vacrel->removed_pages,
1067 vacrel->scanned_pages,
1068 orig_rel_pages == 0 ? 100.0 :
1069 100.0 * vacrel->scanned_pages /
1071 vacrel->eager_scanned_pages);
1073 _("tuples: %" PRId64 " removed, %" PRId64 " remain, %" PRId64 " are dead but not yet removable\n"),
1074 vacrel->tuples_deleted,
1075 (int64) vacrel->new_rel_tuples,
1076 vacrel->recently_dead_tuples);
1077 if (vacrel->missed_dead_tuples > 0)
1079 _("tuples missed: %" PRId64 " dead from %u pages not removed due to cleanup lock contention\n"),
1080 vacrel->missed_dead_tuples,
1081 vacrel->missed_dead_pages);
1083 vacrel->cutoffs.OldestXmin);
1085 _("removable cutoff: %u, which was %d XIDs old when operation ended\n"),
1086 vacrel->cutoffs.OldestXmin, diff);
1088 {
1089 diff = (int32) (vacrel->NewRelfrozenXid -
1090 vacrel->cutoffs.relfrozenxid);
1092 _("new relfrozenxid: %u, which is %d XIDs ahead of previous value\n"),
1093 vacrel->NewRelfrozenXid, diff);
1094 }
1095 if (minmulti_updated)
1096 {
1097 diff = (int32) (vacrel->NewRelminMxid -
1098 vacrel->cutoffs.relminmxid);
1100 _("new relminmxid: %u, which is %d MXIDs ahead of previous value\n"),
1101 vacrel->NewRelminMxid, diff);
1102 }
1103 appendStringInfo(&buf, _("frozen: %u pages from table (%.2f%% of total) had %" PRId64 " tuples frozen\n"),
1104 vacrel->new_frozen_tuple_pages,
1105 orig_rel_pages == 0 ? 100.0 :
1106 100.0 * vacrel->new_frozen_tuple_pages /
1108 vacrel->tuples_frozen);
1109
1111 _("visibility map: %u pages set all-visible, %u pages set all-frozen (%u were all-visible)\n"),
1112 vacrel->new_all_visible_pages,
1113 vacrel->new_all_visible_all_frozen_pages +
1114 vacrel->new_all_frozen_pages,
1115 vacrel->new_all_frozen_pages);
1116 if (vacrel->do_index_vacuuming)
1117 {
1118 if (vacrel->nindexes == 0 || vacrel->num_index_scans == 0)
1119 appendStringInfoString(&buf, _("index scan not needed: "));
1120 else
1121 appendStringInfoString(&buf, _("index scan needed: "));
1122
1123 msgfmt = _("%u pages from table (%.2f%% of total) had %" PRId64 " dead item identifiers removed\n");
1124 }
1125 else
1126 {
1128 appendStringInfoString(&buf, _("index scan bypassed: "));
1129 else
1130 appendStringInfoString(&buf, _("index scan bypassed by failsafe: "));
1131
1132 msgfmt = _("%u pages from table (%.2f%% of total) have %" PRId64 " dead item identifiers\n");
1133 }
1135 vacrel->lpdead_item_pages,
1136 orig_rel_pages == 0 ? 100.0 :
1137 100.0 * vacrel->lpdead_item_pages / orig_rel_pages,
1138 vacrel->lpdead_items);
1139
1140 if (vacrel->worker_usage.vacuum.nplanned > 0)
1142 _("parallel workers: index vacuum: %d planned, %d launched in total\n"),
1143 vacrel->worker_usage.vacuum.nplanned,
1144 vacrel->worker_usage.vacuum.nlaunched);
1145
1146 if (vacrel->worker_usage.cleanup.nplanned > 0)
1148 _("parallel workers: index cleanup: %d planned, %d launched\n"),
1149 vacrel->worker_usage.cleanup.nplanned,
1150 vacrel->worker_usage.cleanup.nlaunched);
1151
1152 for (int i = 0; i < vacrel->nindexes; i++)
1153 {
1154 IndexBulkDeleteResult *istat = vacrel->indstats[i];
1155
1156 if (!istat)
1157 continue;
1158
1160 _("index \"%s\": pages: %u in total, %u newly deleted, %u currently deleted, %u reusable\n"),
1161 indnames[i],
1162 istat->num_pages,
1163 istat->pages_newly_deleted,
1164 istat->pages_deleted,
1165 istat->pages_free);
1166 }
1168 {
1169 /*
1170 * We bypass the changecount mechanism because this value is
1171 * only updated by the calling process. We also rely on the
1172 * above call to pgstat_progress_end_command() to not clear
1173 * the st_progress_param array.
1174 */
1175 appendStringInfo(&buf, _("delay time: %.3f ms\n"),
1177 }
1178 if (track_io_timing)
1179 {
1180 double read_ms = (double) (pgStatBlockReadTime - startreadtime) / 1000;
1181 double write_ms = (double) (pgStatBlockWriteTime - startwritetime) / 1000;
1182
1183 appendStringInfo(&buf, _("I/O timings: read: %.3f ms, write: %.3f ms\n"),
1184 read_ms, write_ms);
1185 }
1186 if (secs_dur > 0 || usecs_dur > 0)
1187 {
1189 (1024 * 1024) / (secs_dur + usecs_dur / 1000000.0);
1191 (1024 * 1024) / (secs_dur + usecs_dur / 1000000.0);
1192 }
1193 appendStringInfo(&buf, _("avg read rate: %.3f MB/s, avg write rate: %.3f MB/s\n"),
1196 _("buffer usage: %" PRId64 " hits, %" PRId64 " reads, %" PRId64 " dirtied\n"),
1201 _("WAL usage: %" PRId64 " records, %" PRId64 " full page images, %" PRIu64 " bytes, %" PRIu64 " full page image bytes, %" PRId64 " buffers full\n"),
1202 walusage.wal_records,
1203 walusage.wal_fpi,
1204 walusage.wal_bytes,
1205 walusage.wal_fpi_bytes,
1206 walusage.wal_buffers_full);
1207
1208 /*
1209 * Report the dead items memory usage.
1210 *
1211 * The num_dead_items_resets counter increases when we reset the
1212 * collected dead items, so the counter is non-zero if at least
1213 * one dead items are collected, even if index vacuuming is
1214 * disabled.
1215 */
1217 ngettext("memory usage: dead item storage %.2f MB accumulated across %d reset (limit %.2f MB each)\n",
1218 "memory usage: dead item storage %.2f MB accumulated across %d resets (limit %.2f MB each)\n",
1219 vacrel->num_dead_items_resets),
1220 (double) vacrel->total_dead_items_bytes / (1024 * 1024),
1221 vacrel->num_dead_items_resets,
1222 (double) dead_items_max_bytes / (1024 * 1024));
1223 appendStringInfo(&buf, _("system usage: %s"), pg_rusage_show(&ru0));
1224
1225 ereport(verbose ? INFO : LOG,
1226 (errmsg_internal("%s", buf.data)));
1227 pfree(buf.data);
1228 }
1229 }
1230
1231 /* Cleanup index statistics and index names */
1232 for (int i = 0; i < vacrel->nindexes; i++)
1233 {
1234 if (vacrel->indstats[i])
1235 pfree(vacrel->indstats[i]);
1236
1237 if (instrument)
1238 pfree(indnames[i]);
1239 }
1240}
void TimestampDifference(TimestampTz start_time, TimestampTz stop_time, long *secs, int *microsecs)
Definition timestamp.c:1715
bool TimestampDifferenceExceeds(TimestampTz start_time, TimestampTz stop_time, int msec)
Definition timestamp.c:1775
TimestampTz GetCurrentTimestamp(void)
Definition timestamp.c:1639
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
void pgstat_progress_update_param(int index, int64 val)
void pgstat_progress_end_command(void)
@ PROGRESS_COMMAND_VACUUM
PgBackendStatus * MyBEEntry
bool track_io_timing
Definition bufmgr.c:192
#define RelationGetNumberOfBlocks(reln)
Definition bufmgr.h:309
#define ngettext(s, p, n)
Definition c.h:1270
int32_t int32
Definition c.h:620
int64 TimestampTz
Definition timestamp.h:39
ErrorContextCallback * error_context_stack
Definition elog.c:99
#define _(x)
Definition elog.c:95
#define LOG
Definition elog.h:32
#define INFO
Definition elog.h:35
#define INJECTION_POINT(name, arg)
void WalUsageAccumDiff(WalUsage *dst, const WalUsage *add, const WalUsage *sub)
Definition instrument.c:367
BufferUsage pgBufferUsage
Definition instrument.c:25
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
Definition instrument.c:327
#define NoLock
Definition lockdefs.h:34
#define RowExclusiveLock
Definition lockdefs.h:38
char * get_database_name(Oid dbid)
Definition lsyscache.c:1312
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3588
char * pstrdup(const char *in)
Definition mcxt.c:1781
void * palloc0(Size size)
Definition mcxt.c:1417
#define AmAutoVacuumWorkerProcess()
Definition miscadmin.h:398
bool MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
Definition multixact.c:2879
#define InvalidMultiXactId
Definition multixact.h:25
static int verbose
const void * data
const char * pg_rusage_show(const PGRUsage *ru0)
Definition pg_rusage.c:40
void pg_rusage_init(PGRUsage *ru0)
Definition pg_rusage.c:27
int64 PgStat_Counter
Definition pgstat.h:71
PgStat_Counter pgStatBlockReadTime
PgStat_Counter pgStatBlockWriteTime
void pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples, PgStat_Counter deadtuples, TimestampTz starttime)
#define PROGRESS_VACUUM_PHASE_FINAL_CLEANUP
Definition progress.h:41
#define PROGRESS_VACUUM_MODE
Definition progress.h:32
#define PROGRESS_VACUUM_MODE_NORMAL
Definition progress.h:44
#define PROGRESS_VACUUM_STARTED_BY_AUTOVACUUM
Definition progress.h:50
#define PROGRESS_VACUUM_PHASE
Definition progress.h:21
#define PROGRESS_VACUUM_DELAY_TIME
Definition progress.h:31
#define PROGRESS_VACUUM_STARTED_BY_AUTOVACUUM_WRAPAROUND
Definition progress.h:51
#define PROGRESS_VACUUM_STARTED_BY_MANUAL
Definition progress.h:49
#define PROGRESS_VACUUM_STARTED_BY
Definition progress.h:33
#define PROGRESS_VACUUM_MODE_AGGRESSIVE
Definition progress.h:45
#define RelationGetNamespace(relation)
Definition rel.h:557
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
void appendStringInfoString(StringInfo str, const char *s)
Definition stringinfo.c:230
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
int64 shared_blks_dirtied
Definition instrument.h:28
int64 local_blks_hit
Definition instrument.h:30
int64 shared_blks_read
Definition instrument.h:27
int64 local_blks_read
Definition instrument.h:31
int64 local_blks_dirtied
Definition instrument.h:32
int64 shared_blks_hit
Definition instrument.h:26
struct ErrorContextCallback * previous
Definition elog.h:299
void(* callback)(void *arg)
Definition elog.h:300
BlockNumber pages_deleted
Definition genam.h:90
BlockNumber pages_newly_deleted
Definition genam.h:89
BlockNumber pages_free
Definition genam.h:91
BlockNumber num_pages
Definition genam.h:85
int64 st_progress_param[PGSTAT_NUM_PROGRESS_PARAM]
int nworkers
Definition vacuum.h:250
VacOptValue truncate
Definition vacuum.h:235
int log_vacuum_min_duration
Definition vacuum.h:226
uint32 options
Definition vacuum.h:218
bool is_wraparound
Definition vacuum.h:225
VacOptValue index_cleanup
Definition vacuum.h:234
int64 wal_buffers_full
Definition instrument.h:57
uint64 wal_bytes
Definition instrument.h:55
uint64 wal_fpi_bytes
Definition instrument.h:56
int64 wal_records
Definition instrument.h:53
size_t TidStoreMemoryUsage(TidStore *ts)
Definition tidstore.c:532
static TransactionId ReadNextTransactionId(void)
Definition transam.h:375
static bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
Definition transam.h:282
bool track_cost_delay_timing
Definition vacuum.c:83
void vac_open_indexes(Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
Definition vacuum.c:2369
void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode)
Definition vacuum.c:2412
bool vacuum_get_cutoffs(Relation rel, const VacuumParams *params, struct VacuumCutoffs *cutoffs)
Definition vacuum.c:1101
bool VacuumFailsafeActive
Definition vacuum.c:111
void vac_update_relstats(Relation relation, BlockNumber num_pages, double num_tuples, BlockNumber num_all_visible_pages, BlockNumber num_all_frozen_pages, bool hasindex, TransactionId frozenxid, MultiXactId minmulti, bool *frozenxid_updated, bool *minmulti_updated, bool in_outer_xact)
Definition vacuum.c:1427
#define VACOPT_VERBOSE
Definition vacuum.h:181
@ VACOPTVALUE_AUTO
Definition vacuum.h:202
@ VACOPTVALUE_ENABLED
Definition vacuum.h:204
@ VACOPTVALUE_UNSPECIFIED
Definition vacuum.h:201
@ VACOPTVALUE_DISABLED
Definition vacuum.h:203
#define VACOPT_DISABLE_PAGE_SKIPPING
Definition vacuum.h:187
static void dead_items_cleanup(LVRelState *vacrel)
static void update_relstats_all_indexes(LVRelState *vacrel)
static void vacuum_error_callback(void *arg)
static void lazy_truncate_heap(LVRelState *vacrel)
static bool should_attempt_truncation(LVRelState *vacrel)
@ VACUUM_ERRCB_PHASE_UNKNOWN
Definition vacuumlazy.c:226
static void lazy_scan_heap(LVRelState *vacrel)
#define ParallelVacuumIsActive(vacrel)
Definition vacuumlazy.c:221
static bool lazy_check_wraparound_failsafe(LVRelState *vacrel)
static void heap_vacuum_eager_scan_setup(LVRelState *vacrel, const VacuumParams *params)
Definition vacuumlazy.c:497
static void dead_items_alloc(LVRelState *vacrel, int nworkers)
void visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_frozen)

References _, AmAutoVacuumWorkerProcess, appendStringInfo(), appendStringInfoString(), ErrorContextCallback::arg, Assert, buf, BufferUsageAccumDiff(), ErrorContextCallback::callback, dead_items_alloc(), dead_items_cleanup(), ereport, errmsg, errmsg_internal(), error_context_stack, fb(), get_database_name(), get_namespace_name(), GetCurrentTimestamp(), GlobalVisTestFor(), heap_vacuum_eager_scan_setup(), i, VacuumParams::index_cleanup, INFO, initStringInfo(), INJECTION_POINT, InvalidMultiXactId, InvalidTransactionId, VacuumParams::is_wraparound, IsInParallelMode(), lazy_check_wraparound_failsafe(), lazy_scan_heap(), lazy_truncate_heap(), BufferUsage::local_blks_dirtied, BufferUsage::local_blks_hit, BufferUsage::local_blks_read, LOG, VacuumParams::log_vacuum_min_duration, Max, MultiXactIdPrecedesOrEquals(), MyBEEntry, MyDatabaseId, ngettext, NoLock, IndexBulkDeleteResult::num_pages, VacuumParams::nworkers, VacuumParams::options, IndexBulkDeleteResult::pages_deleted, IndexBulkDeleteResult::pages_free, IndexBulkDeleteResult::pages_newly_deleted, palloc0(), palloc0_object, palloc_array, ParallelVacuumIsActive, pfree(), pg_rusage_init(), pg_rusage_show(), pgBufferUsage, pgstat_progress_end_command(), pgstat_progress_start_command(), pgstat_progress_update_param(), pgstat_report_vacuum(), pgStatBlockReadTime, pgStatBlockWriteTime, pgWalUsage, ErrorContextCallback::previous, PROGRESS_COMMAND_VACUUM, PROGRESS_VACUUM_DELAY_TIME, PROGRESS_VACUUM_MODE, PROGRESS_VACUUM_MODE_AGGRESSIVE, PROGRESS_VACUUM_MODE_NORMAL, PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE_FINAL_CLEANUP, PROGRESS_VACUUM_STARTED_BY, PROGRESS_VACUUM_STARTED_BY_AUTOVACUUM, PROGRESS_VACUUM_STARTED_BY_AUTOVACUUM_WRAPAROUND, PROGRESS_VACUUM_STARTED_BY_MANUAL, pstrdup(), ReadNextTransactionId(), RelationGetNamespace, RelationGetNumberOfBlocks, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, BufferUsage::shared_blks_dirtied, BufferUsage::shared_blks_hit, BufferUsage::shared_blks_read, should_attempt_truncation(), PgBackendStatus::st_progress_param, TidStoreMemoryUsage(), TimestampDifference(), TimestampDifferenceExceeds(), track_cost_delay_timing, track_io_timing, TransactionIdPrecedesOrEquals(), VacuumParams::truncate, update_relstats_all_indexes(), vac_close_indexes(), vac_open_indexes(), vac_update_relstats(), VACOPT_DISABLE_PAGE_SKIPPING, VACOPT_VERBOSE, VACOPTVALUE_AUTO, VACOPTVALUE_DISABLED, VACOPTVALUE_ENABLED, VACOPTVALUE_UNSPECIFIED, VACUUM_ERRCB_PHASE_UNKNOWN, vacuum_error_callback(), vacuum_get_cutoffs(), VacuumFailsafeActive, verbose, visibilitymap_count(), WalUsage::wal_buffers_full, WalUsage::wal_bytes, WalUsage::wal_fpi, WalUsage::wal_fpi_bytes, WalUsage::wal_records, and WalUsageAccumDiff().

◆ heapam_index_fetch_begin()

IndexFetchTableData * heapam_index_fetch_begin ( Relation  rel,
uint32  flags 
)
extern

Definition at line 28 of file heapam_indexscan.c.

29{
31
32 hscan->xs_base.rel = rel;
33 hscan->xs_base.flags = flags;
34 hscan->xs_cbuf = InvalidBuffer;
35 hscan->xs_blk = InvalidBlockNumber;
36 hscan->xs_vmbuffer = InvalidBuffer;
37
38 return &hscan->xs_base;
39}

References fb(), InvalidBlockNumber, InvalidBuffer, and palloc0_object.

◆ heapam_index_fetch_end()

void heapam_index_fetch_end ( IndexFetchTableData scan)
extern

Definition at line 54 of file heapam_indexscan.c.

55{
57
58 /* drop pin if there's a pinned heap page */
59 if (BufferIsValid(hscan->xs_cbuf))
60 ReleaseBuffer(hscan->xs_cbuf);
61
62 /* drop pin if there's a pinned visibility map page */
63 if (BufferIsValid(hscan->xs_vmbuffer))
64 ReleaseBuffer(hscan->xs_vmbuffer);
65
66 pfree(hscan);
67}

References BufferIsValid(), fb(), pfree(), and ReleaseBuffer().

◆ heapam_index_fetch_reset()

void heapam_index_fetch_reset ( IndexFetchTableData scan)
extern

Definition at line 42 of file heapam_indexscan.c.

43{
44 /*
45 * Resets are a no-op.
46 *
47 * Deliberately avoid dropping pins now held in xs_cbuf and xs_vmbuffer.
48 * This saves cycles during certain tight nested loop joins (it can avoid
49 * repeated pinning and unpinning of the same buffer across rescans).
50 */
51}

◆ heapam_index_fetch_tuple()

bool heapam_index_fetch_tuple ( struct IndexFetchTableData scan,
ItemPointer  tid,
Snapshot  snapshot,
TupleTableSlot slot,
bool heap_continue,
bool all_dead 
)
extern

Definition at line 232 of file heapam_indexscan.c.

237{
240 bool got_heap_tuple;
241
243
244 /* We can skip the buffer-switching logic if we're on the same page. */
245 if (hscan->xs_blk != ItemPointerGetBlockNumber(tid))
246 {
248
249 /* Remember this buffer's block number for next time */
250 hscan->xs_blk = ItemPointerGetBlockNumber(tid);
251
252 if (BufferIsValid(hscan->xs_cbuf))
253 ReleaseBuffer(hscan->xs_cbuf);
254
255 hscan->xs_cbuf = ReadBuffer(hscan->xs_base.rel, hscan->xs_blk);
256
257 /*
258 * Prune page when it is pinned for the first time
259 */
260 heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf,
261 &hscan->xs_vmbuffer,
262 hscan->xs_base.flags & SO_HINT_REL_READ_ONLY);
263 }
264
265 Assert(BufferGetBlockNumber(hscan->xs_cbuf) == hscan->xs_blk);
266 Assert(hscan->xs_blk == ItemPointerGetBlockNumber(tid));
267
268 /* Obtain share-lock on the buffer so we can examine visibility */
271 hscan->xs_base.rel,
272 hscan->xs_cbuf,
273 snapshot,
274 &bslot->base.tupdata,
275 all_dead,
276 !*heap_continue);
277 bslot->base.tupdata.t_self = *tid;
279
280 if (got_heap_tuple)
281 {
282 /*
283 * Only in a non-MVCC snapshot can more than one member of the HOT
284 * chain be visible.
285 */
286 *heap_continue = !IsMVCCLikeSnapshot(snapshot);
287
288 slot->tts_tableOid = RelationGetRelid(scan->rel);
289 ExecStoreBufferHeapTuple(&bslot->base.tupdata, slot, hscan->xs_cbuf);
290 }
291 else
292 {
293 /* We've reached the end of the HOT chain. */
294 *heap_continue = false;
295 }
296
297 return got_heap_tuple;
298}
#define IsMVCCLikeSnapshot(snapshot)
Definition snapmgr.h:74
#define TTS_IS_BUFFERTUPLE(slot)
Definition tuptable.h:256

References Assert, BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferIsValid(), ExecStoreBufferHeapTuple(), fb(), heap_hot_search_buffer(), heap_page_prune_opt(), IsMVCCLikeSnapshot, ItemPointerGetBlockNumber(), LockBuffer(), ReadBuffer(), IndexFetchTableData::rel, RelationGetRelid, ReleaseBuffer(), SO_HINT_REL_READ_ONLY, TTS_IS_BUFFERTUPLE, and TupleTableSlot::tts_tableOid.

◆ HeapCheckForSerializableConflictOut()

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

Definition at line 9183 of file heapam.c.

9186{
9187 TransactionId xid;
9189
9190 if (!CheckForSerializableConflictOutNeeded(relation, snapshot))
9191 return;
9192
9193 /*
9194 * Check to see whether the tuple has been written to by a concurrent
9195 * transaction, either to create it not visible to us, or to delete it
9196 * while it is visible to us. The "visible" bool indicates whether the
9197 * tuple is visible to us, while HeapTupleSatisfiesVacuum checks what else
9198 * is going on with it.
9199 *
9200 * In the event of a concurrently inserted tuple that also happens to have
9201 * been concurrently updated (by a separate transaction), the xmin of the
9202 * tuple will be used -- not the updater's xid.
9203 */
9205 switch (htsvResult)
9206 {
9207 case HEAPTUPLE_LIVE:
9208 if (visible)
9209 return;
9210 xid = HeapTupleHeaderGetXmin(tuple->t_data);
9211 break;
9214 if (visible)
9215 xid = HeapTupleHeaderGetUpdateXid(tuple->t_data);
9216 else
9217 xid = HeapTupleHeaderGetXmin(tuple->t_data);
9218
9220 {
9221 /* This is like the HEAPTUPLE_DEAD case */
9222 Assert(!visible);
9223 return;
9224 }
9225 break;
9227 xid = HeapTupleHeaderGetXmin(tuple->t_data);
9228 break;
9229 case HEAPTUPLE_DEAD:
9230 Assert(!visible);
9231 return;
9232 default:
9233
9234 /*
9235 * The only way to get to this default clause is if a new value is
9236 * added to the enum type without adding it to this switch
9237 * statement. That's a bug, so elog.
9238 */
9239 elog(ERROR, "unrecognized return value from HeapTupleSatisfiesVacuum: %u", htsvResult);
9240
9241 /*
9242 * In spite of having all enum values covered and calling elog on
9243 * this default, some compilers think this is a code path which
9244 * allows xid to be used below without initialization. Silence
9245 * that warning.
9246 */
9248 }
9249
9252
9253 /*
9254 * Find top level xid. Bail out if xid is too early to be a conflict, or
9255 * if it's our own xid.
9256 */
9258 return;
9261 return;
9262
9263 CheckForSerializableConflictOut(relation, xid, snapshot);
9264}
HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
void CheckForSerializableConflictOut(Relation relation, TransactionId xid, Snapshot snapshot)
Definition predicate.c:3953
TransactionId SubTransGetTopmostTransaction(TransactionId xid)
Definition subtrans.c:170
static bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
Definition transam.h:312
TransactionId GetTopTransactionIdIfAny(void)
Definition xact.c:443

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

Referenced by BitmapHeapScanNextBlock(), heap_fetch(), heap_get_latest_tid(), heap_hot_search_buffer(), heapam_scan_sample_next_tuple(), heapgettup(), and page_collect_tuples().

◆ HeapTupleHeaderIsOnlyLocked()

bool HeapTupleHeaderIsOnlyLocked ( HeapTupleHeader  tuple)
extern

Definition at line 1437 of file heapam_visibility.c.

1438{
1439 TransactionId xmax;
1440
1441 /* if there's no valid Xmax, then there's obviously no update either */
1442 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1443 return true;
1444
1445 if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
1446 return true;
1447
1448 /* invalid xmax means no update */
1450 return true;
1451
1452 /*
1453 * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
1454 * necessarily have been updated
1455 */
1456 if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
1457 return false;
1458
1459 /* ... but if it's a multi, then perhaps the updating Xid aborted. */
1460 xmax = HeapTupleGetUpdateXid(tuple);
1461
1462 /* not LOCKED_ONLY, so it has to have an xmax */
1464
1466 return false;
1467 if (TransactionIdIsInProgress(xmax))
1468 return false;
1469 if (TransactionIdDidCommit(xmax))
1470 return false;
1471
1472 /*
1473 * not current, not in progress, not committed -- must have aborted or
1474 * crashed
1475 */
1476 return true;
1477}
TransactionId HeapTupleGetUpdateXid(const HeapTupleHeaderData *tup)
Definition heapam.c:7560
#define HEAP_XMAX_LOCK_ONLY
bool TransactionIdIsInProgress(TransactionId xid)
Definition procarray.c:1393

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

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

◆ HeapTupleIsSurelyDead()

bool HeapTupleIsSurelyDead ( HeapTuple  htup,
GlobalVisState vistest 
)
extern

Definition at line 1381 of file heapam_visibility.c.

1382{
1383 HeapTupleHeader tuple = htup->t_data;
1384
1386 Assert(htup->t_tableOid != InvalidOid);
1387
1388 /*
1389 * If the inserting transaction is marked invalid, then it aborted, and
1390 * the tuple is definitely dead. If it's marked neither committed nor
1391 * invalid, then we assume it's still alive (since the presumption is that
1392 * all relevant hint bits were just set moments ago).
1393 */
1394 if (!HeapTupleHeaderXminCommitted(tuple))
1395 return HeapTupleHeaderXminInvalid(tuple);
1396
1397 /*
1398 * If the inserting transaction committed, but any deleting transaction
1399 * aborted, the tuple is still alive.
1400 */
1401 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1402 return false;
1403
1404 /*
1405 * If the XMAX is just a lock, the tuple is still alive.
1406 */
1408 return false;
1409
1410 /*
1411 * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
1412 * know without checking pg_multixact.
1413 */
1414 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1415 return false;
1416
1417 /* If deleter isn't known to have committed, assume it's still running. */
1418 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1419 return false;
1420
1421 /* Deleter committed, so tuple is dead if the XID is old enough. */
1422 return GlobalVisTestIsRemovableXid(vistest,
1424 true);
1425}
static bool HeapTupleHeaderXminInvalid(const HeapTupleHeaderData *tup)
#define HEAP_XMAX_COMMITTED
static bool HeapTupleHeaderXminCommitted(const HeapTupleHeaderData *tup)
#define InvalidOid

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

Referenced by heap_hot_search_buffer().

◆ HeapTupleSatisfiesMVCCBatch()

int HeapTupleSatisfiesMVCCBatch ( Snapshot  snapshot,
Buffer  buffer,
int  ntups,
BatchMVCCState batchmvcc,
OffsetNumber vistuples_dense 
)
extern

Definition at line 1690 of file heapam_visibility.c.

1694{
1695 int nvis = 0;
1697
1698 Assert(IsMVCCSnapshot(snapshot));
1699
1700 for (int i = 0; i < ntups; i++)
1701 {
1702 bool valid;
1703 HeapTuple tup = &batchmvcc->tuples[i];
1704
1705 valid = HeapTupleSatisfiesMVCC(tup, snapshot, buffer, &state);
1706 batchmvcc->visible[i] = valid;
1707
1708 if (likely(valid))
1709 {
1710 vistuples_dense[nvis] = tup->t_self.ip_posid;
1711 nvis++;
1712 }
1713 }
1714
1715 if (state == SHB_ENABLED)
1716 BufferFinishSetHintBits(buffer, true, true);
1717
1718 return nvis;
1719}
void BufferFinishSetHintBits(Buffer buffer, bool mark_dirty, bool buffer_std)
Definition bufmgr.c:7070
static bool HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot, Buffer buffer, SetHintBitsState *state)
SetHintBitsState
@ SHB_ENABLED
@ SHB_INITIAL

References Assert, BufferFinishSetHintBits(), fb(), HeapTupleSatisfiesMVCC(), i, IsMVCCSnapshot, likely, SHB_ENABLED, and SHB_INITIAL.

Referenced by page_collect_tuples().

◆ HeapTupleSatisfiesUpdate()

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

Definition at line 511 of file heapam_visibility.c.

513{
514 HeapTupleHeader tuple = htup->t_data;
515
517 Assert(htup->t_tableOid != InvalidOid);
518
520 {
522 return TM_Invisible;
523
524 else if (!HeapTupleCleanMoved(tuple, buffer))
525 return TM_Invisible;
527 {
528 if (HeapTupleHeaderGetCmin(tuple) >= curcid)
529 return TM_Invisible; /* inserted after scan started */
530
531 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
532 return TM_Ok;
533
535 {
536 TransactionId xmax;
537
538 xmax = HeapTupleHeaderGetRawXmax(tuple);
539
540 /*
541 * Careful here: even though this tuple was created by our own
542 * transaction, it might be locked by other transactions, if
543 * the original version was key-share locked when we updated
544 * it.
545 */
546
547 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
548 {
549 if (MultiXactIdIsRunning(xmax, true))
550 return TM_BeingModified;
551 else
552 return TM_Ok;
553 }
554
555 /*
556 * If the locker is gone, then there is nothing of interest
557 * left in this Xmax; otherwise, report the tuple as
558 * locked/updated.
559 */
560 if (!TransactionIdIsInProgress(xmax))
561 return TM_Ok;
562 return TM_BeingModified;
563 }
564
565 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
566 {
567 TransactionId xmax;
568
569 xmax = HeapTupleGetUpdateXid(tuple);
570
571 /* not LOCKED_ONLY, so it has to have an xmax */
573
574 /* deleting subtransaction must have aborted */
576 {
578 false))
579 return TM_BeingModified;
580 return TM_Ok;
581 }
582 else
583 {
584 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
585 return TM_SelfModified; /* updated after scan started */
586 else
587 return TM_Invisible; /* updated before scan started */
588 }
589 }
590
592 {
593 /* deleting subtransaction must have aborted */
594 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
596 return TM_Ok;
597 }
598
599 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
600 return TM_SelfModified; /* updated after scan started */
601 else
602 return TM_Invisible; /* updated before scan started */
603 }
605 return TM_Invisible;
607 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
609 else
610 {
611 /* it must have aborted or crashed */
612 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
614 return TM_Invisible;
615 }
616 }
617
618 /* by here, the inserting transaction has committed */
619
620 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
621 return TM_Ok;
622
623 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
624 {
626 return TM_Ok;
627 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
628 return TM_Updated; /* updated by other */
629 else
630 return TM_Deleted; /* deleted by other */
631 }
632
633 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
634 {
635 TransactionId xmax;
636
638 return TM_Ok;
639
641 {
643 return TM_BeingModified;
644
646 return TM_Ok;
647 }
648
649 xmax = HeapTupleGetUpdateXid(tuple);
650 if (!TransactionIdIsValid(xmax))
651 {
653 return TM_BeingModified;
654 }
655
656 /* not LOCKED_ONLY, so it has to have an xmax */
658
660 {
661 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
662 return TM_SelfModified; /* updated after scan started */
663 else
664 return TM_Invisible; /* updated before scan started */
665 }
666
668 return TM_BeingModified;
669
670 if (TransactionIdDidCommit(xmax))
671 {
672 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
673 return TM_Updated;
674 else
675 return TM_Deleted;
676 }
677
678 /*
679 * By here, the update in the Xmax is either aborted or crashed, but
680 * what about the other members?
681 */
682
684 {
685 /*
686 * There's no member, even just a locker, alive anymore, so we can
687 * mark the Xmax as invalid.
688 */
689 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
691 return TM_Ok;
692 }
693 else
694 {
695 /* There are lockers running */
696 return TM_BeingModified;
697 }
698 }
699
701 {
703 return TM_BeingModified;
704 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
705 return TM_SelfModified; /* updated after scan started */
706 else
707 return TM_Invisible; /* updated before scan started */
708 }
709
711 return TM_BeingModified;
712
714 {
715 /* it must have aborted or crashed */
716 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
718 return TM_Ok;
719 }
720
721 /* xmax transaction committed */
722
724 {
725 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
727 return TM_Ok;
728 }
729
730 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
732 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
733 return TM_Updated; /* updated by other */
734 else
735 return TM_Deleted; /* deleted by other */
736}
CommandId HeapTupleHeaderGetCmin(const HeapTupleHeaderData *tup)
Definition combocid.c:104
static void SetHintBits(HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid)
static bool HeapTupleCleanMoved(HeapTupleHeader tuple, Buffer buffer)
#define HEAP_XMIN_COMMITTED
#define HEAP_XMIN_INVALID
static bool HEAP_LOCKED_UPGRADED(uint16 infomask)
bool MultiXactIdIsRunning(MultiXactId multi, bool isLockOnly)
Definition multixact.c:522

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

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

◆ HeapTupleSatisfiesVacuum()

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

◆ HeapTupleSatisfiesVacuumHorizon()

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

Definition at line 1147 of file heapam_visibility.c.

1148{
1149 HeapTupleHeader tuple = htup->t_data;
1150
1152 Assert(htup->t_tableOid != InvalidOid);
1154
1156
1157 /*
1158 * Has inserting transaction committed?
1159 *
1160 * If the inserting transaction aborted, then the tuple was never visible
1161 * to any other transaction, so we can delete it immediately.
1162 */
1163 if (!HeapTupleHeaderXminCommitted(tuple))
1164 {
1165 if (HeapTupleHeaderXminInvalid(tuple))
1166 return HEAPTUPLE_DEAD;
1167 else if (!HeapTupleCleanMoved(tuple, buffer))
1168 return HEAPTUPLE_DEAD;
1170 {
1171 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1173 /* only locked? run infomask-only check first, for performance */
1177 /* inserted and then deleted by same xact */
1180 /* deleting subtransaction must have aborted */
1182 }
1184 {
1185 /*
1186 * It'd be possible to discern between INSERT/DELETE in progress
1187 * here by looking at xmax - but that doesn't seem beneficial for
1188 * the majority of callers and even detrimental for some. We'd
1189 * rather have callers look at/wait for xmin than xmax. It's
1190 * always correct to return INSERT_IN_PROGRESS because that's
1191 * what's happening from the view of other backends.
1192 */
1194 }
1196 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1198 else
1199 {
1200 /*
1201 * Not in Progress, Not Committed, so either Aborted or crashed
1202 */
1203 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1205 return HEAPTUPLE_DEAD;
1206 }
1207
1208 /*
1209 * At this point the xmin is known committed, but we might not have
1210 * been able to set the hint bit yet; so we can no longer Assert that
1211 * it's set.
1212 */
1213 }
1214
1215 /*
1216 * Okay, the inserter committed, so it was good at some point. Now what
1217 * about the deleting transaction?
1218 */
1219 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1220 return HEAPTUPLE_LIVE;
1221
1223 {
1224 /*
1225 * "Deleting" xact really only locked it, so the tuple is live in any
1226 * case. However, we should make sure that either XMAX_COMMITTED or
1227 * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
1228 * examining the tuple for future xacts.
1229 */
1230 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1231 {
1232 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1233 {
1234 /*
1235 * If it's a pre-pg_upgrade tuple, the multixact cannot
1236 * possibly be running; otherwise have to check.
1237 */
1238 if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
1240 true))
1241 return HEAPTUPLE_LIVE;
1243 }
1244 else
1245 {
1247 return HEAPTUPLE_LIVE;
1248 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1250 }
1251 }
1252
1253 /*
1254 * We don't really care whether xmax did commit, abort or crash. We
1255 * know that xmax did lock the tuple, but it did not and will never
1256 * actually update it.
1257 */
1258
1259 return HEAPTUPLE_LIVE;
1260 }
1261
1262 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1263 {
1265
1266 /* already checked above */
1268
1269 /* not LOCKED_ONLY, so it has to have an xmax */
1271
1272 if (TransactionIdIsInProgress(xmax))
1274 else if (TransactionIdDidCommit(xmax))
1275 {
1276 /*
1277 * The multixact might still be running due to lockers. Need to
1278 * allow for pruning if below the xid horizon regardless --
1279 * otherwise we could end up with a tuple where the updater has to
1280 * be removed due to the horizon, but is not pruned away. It's
1281 * not a problem to prune that tuple, because any remaining
1282 * lockers will also be present in newer tuple versions.
1283 */
1284 *dead_after = xmax;
1286 }
1287 else if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
1288 {
1289 /*
1290 * Not in Progress, Not Committed, so either Aborted or crashed.
1291 * Mark the Xmax as invalid.
1292 */
1294 }
1295
1296 return HEAPTUPLE_LIVE;
1297 }
1298
1299 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1300 {
1304 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1306 else
1307 {
1308 /*
1309 * Not in Progress, Not Committed, so either Aborted or crashed
1310 */
1311 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1313 return HEAPTUPLE_LIVE;
1314 }
1315
1316 /*
1317 * At this point the xmax is known committed, but we might not have
1318 * been able to set the hint bit yet; so we can no longer Assert that
1319 * it's set.
1320 */
1321 }
1322
1323 /*
1324 * Deleter committed, allow caller to check if it was recent enough that
1325 * some open transactions could still see the tuple.
1326 */
1329}

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

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

◆ HeapTupleSatisfiesVisibility()

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

Definition at line 1732 of file heapam_visibility.c.

1733{
1734 switch (snapshot->snapshot_type)
1735 {
1736 case SNAPSHOT_MVCC:
1737 return HeapTupleSatisfiesMVCC(htup, snapshot, buffer, NULL);
1738 case SNAPSHOT_SELF:
1739 return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
1740 case SNAPSHOT_ANY:
1741 return HeapTupleSatisfiesAny(htup, snapshot, buffer);
1742 case SNAPSHOT_TOAST:
1743 return HeapTupleSatisfiesToast(htup, snapshot, buffer);
1744 case SNAPSHOT_DIRTY:
1745 return HeapTupleSatisfiesDirty(htup, snapshot, buffer);
1747 return HeapTupleSatisfiesHistoricMVCC(htup, snapshot, buffer);
1749 return HeapTupleSatisfiesNonVacuumable(htup, snapshot, buffer);
1750 }
1751
1752 return false; /* keep compiler quiet */
1753}
static bool HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot, Buffer buffer)
@ SNAPSHOT_TOAST
Definition snapshot.h:70
@ SNAPSHOT_SELF
Definition snapshot.h:60
@ SNAPSHOT_NON_VACUUMABLE
Definition snapshot.h:114
@ SNAPSHOT_MVCC
Definition snapshot.h:46
@ SNAPSHOT_ANY
Definition snapshot.h:65
@ SNAPSHOT_HISTORIC_MVCC
Definition snapshot.h:105
@ SNAPSHOT_DIRTY
Definition snapshot.h:98
SnapshotType snapshot_type
Definition snapshot.h:140

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

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

◆ HeapTupleSetHintBits()

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

Definition at line 212 of file heapam_visibility.c.

214{
215 /*
216 * The uses from heapam.c rely on being able to perform the hint bit
217 * updates, which can only be guaranteed if we are holding an exclusive
218 * lock on the buffer - which all callers are doing.
219 */
221
222 SetHintBits(tuple, buffer, infomask, xid);
223}
bool BufferIsLockedByMeInMode(Buffer buffer, BufferLockMode mode)
Definition bufmgr.c:3087

References Assert, BUFFER_LOCK_EXCLUSIVE, BufferIsLockedByMeInMode(), fb(), and SetHintBits().

Referenced by UpdateXmaxHintBits().

◆ log_heap_prune_and_freeze()

void log_heap_prune_and_freeze ( Relation  relation,
Buffer  buffer,
Buffer  vmbuffer,
uint8  vmflags,
TransactionId  conflict_xid,
bool  cleanup_lock,
PruneReason  reason,
HeapTupleFreeze frozen,
int  nfrozen,
OffsetNumber redirected,
int  nredirected,
OffsetNumber dead,
int  ndead,
OffsetNumber unused,
int  nunused 
)
extern

Definition at line 2561 of file pruneheap.c.

2570{
2573 uint8 info;
2575
2576 Page heap_page = BufferGetPage(buffer);
2577
2578 /* The following local variables hold data registered in the WAL record: */
2582 xlhp_prune_items dead_items;
2585 bool do_prune = nredirected > 0 || ndead > 0 || nunused > 0;
2587 bool heap_fpi_allowed = true;
2588
2590
2591 xlrec.flags = 0;
2593
2594 /*
2595 * We can avoid an FPI of the heap page if the only modification we are
2596 * making to it is to set PD_ALL_VISIBLE and checksums/wal_log_hints are
2597 * disabled.
2598 *
2599 * However, if the page has never been WAL-logged (LSN is invalid), we
2600 * must force an FPI regardless. This can happen when another backend
2601 * extends the heap, initializes the page, and then fails before WAL-
2602 * logging it. Since heap extension is not WAL-logged, recovery might try
2603 * to replay our record and find that the page isn't initialized, which
2604 * would cause a PANIC.
2605 */
2608 else if (!do_prune && nfrozen == 0 && (!do_set_vm || !XLogHintBitIsNeeded()))
2609 {
2611 heap_fpi_allowed = false;
2612 }
2613
2614 /*
2615 * Prepare data for the buffer. The arrays are not actually in the
2616 * buffer, but we pretend that they are. When XLogInsert stores a full
2617 * page image, the arrays can be omitted.
2618 */
2621
2622 if (do_set_vm)
2623 XLogRegisterBuffer(1, vmbuffer, 0);
2624
2625 if (nfrozen > 0)
2626 {
2627 int nplans;
2628
2630
2631 /*
2632 * Prepare deduplicated representation for use in the WAL record. This
2633 * destructively sorts frozen tuples array in-place.
2634 */
2635 nplans = heap_log_freeze_plan(frozen, nfrozen, plans, frz_offsets);
2636
2637 freeze_plans.nplans = nplans;
2639 offsetof(xlhp_freeze_plans, plans));
2640 XLogRegisterBufData(0, plans,
2641 sizeof(xlhp_freeze_plan) * nplans);
2642 }
2643 if (nredirected > 0)
2644 {
2646
2647 redirect_items.ntargets = nredirected;
2650 XLogRegisterBufData(0, redirected,
2651 sizeof(OffsetNumber[2]) * nredirected);
2652 }
2653 if (ndead > 0)
2654 {
2655 xlrec.flags |= XLHP_HAS_DEAD_ITEMS;
2656
2657 dead_items.ntargets = ndead;
2658 XLogRegisterBufData(0, &dead_items,
2660 XLogRegisterBufData(0, dead,
2661 sizeof(OffsetNumber) * ndead);
2662 }
2663 if (nunused > 0)
2664 {
2666
2667 unused_items.ntargets = nunused;
2670 XLogRegisterBufData(0, unused,
2671 sizeof(OffsetNumber) * nunused);
2672 }
2673 if (nfrozen > 0)
2675 sizeof(OffsetNumber) * nfrozen);
2676
2677 /*
2678 * Prepare the main xl_heap_prune record. We already set the XLHP_HAS_*
2679 * flag above.
2680 */
2682 {
2683 xlrec.flags |= XLHP_VM_ALL_VISIBLE;
2685 xlrec.flags |= XLHP_VM_ALL_FROZEN;
2686 }
2688 xlrec.flags |= XLHP_IS_CATALOG_REL;
2691 if (cleanup_lock)
2692 xlrec.flags |= XLHP_CLEANUP_LOCK;
2693 else
2694 {
2695 Assert(nredirected == 0 && ndead == 0);
2696 /* also, any items in 'unused' must've been LP_DEAD previously */
2697 }
2701
2702 switch (reason)
2703 {
2704 case PRUNE_ON_ACCESS:
2706 break;
2707 case PRUNE_VACUUM_SCAN:
2709 break;
2712 break;
2713 default:
2714 elog(ERROR, "unrecognized prune reason: %d", (int) reason);
2715 break;
2716 }
2717 recptr = XLogInsert(RM_HEAP2_ID, info);
2718
2719 if (do_set_vm)
2720 {
2721 Assert(BufferIsDirty(vmbuffer));
2722 PageSetLSN(BufferGetPage(vmbuffer), recptr);
2723 }
2724
2725 /*
2726 * If we explicitly skip an FPI, we must not stamp the heap page with this
2727 * record's LSN. Recovery skips records <= the stamped LSN, so this could
2728 * lead to skipping an earlier FPI needed to repair a torn page.
2729 */
2730 if (heap_fpi_allowed)
2731 {
2732 Assert(BufferIsDirty(buffer));
2734 }
2735}
static XLogRecPtr PageGetLSN(const PageData *page)
Definition bufpage.h:410
#define XLHP_HAS_CONFLICT_HORIZON
#define XLHP_HAS_FREEZE_PLANS
#define XLHP_VM_ALL_VISIBLE
#define SizeOfHeapPrune
#define XLHP_HAS_NOW_UNUSED_ITEMS
#define XLHP_VM_ALL_FROZEN
#define XLHP_HAS_REDIRECTIONS
#define XLOG_HEAP2_PRUNE_VACUUM_SCAN
Definition heapam_xlog.h:61
#define XLOG_HEAP2_PRUNE_ON_ACCESS
Definition heapam_xlog.h:60
#define XLHP_CLEANUP_LOCK
#define XLHP_HAS_DEAD_ITEMS
#define XLOG_HEAP2_PRUNE_VACUUM_CLEANUP
Definition heapam_xlog.h:62
#define XLHP_IS_CATALOG_REL
static int heap_log_freeze_plan(HeapTupleFreeze *tuples, int ntuples, xlhp_freeze_plan *plans_out, OffsetNumber *offsets_out)
Definition pruneheap.c:2482
#define XLogHintBitIsNeeded()
Definition xlog.h:123
#define XLogRecPtrIsValid(r)
Definition xlogdefs.h:29
#define REGBUF_FORCE_IMAGE
Definition xloginsert.h:32
#define REGBUF_NO_IMAGE
Definition xloginsert.h:33

References Assert, BufferGetPage(), BufferIsDirty(), data, elog, ERROR, fb(), heap_log_freeze_plan(), MaxHeapTuplesPerPage, xlhp_prune_items::ntargets, PageGetLSN(), PageSetLSN(), PRUNE_ON_ACCESS, PRUNE_VACUUM_CLEANUP, PRUNE_VACUUM_SCAN, REGBUF_FORCE_IMAGE, REGBUF_NO_IMAGE, REGBUF_STANDARD, RelationIsAccessibleInLogicalDecoding, SizeOfHeapPrune, TransactionIdIsValid, VISIBILITYMAP_ALL_FROZEN, VISIBILITYMAP_ALL_VISIBLE, VISIBILITYMAP_VALID_BITS, XLHP_CLEANUP_LOCK, XLHP_HAS_CONFLICT_HORIZON, XLHP_HAS_DEAD_ITEMS, XLHP_HAS_FREEZE_PLANS, XLHP_HAS_NOW_UNUSED_ITEMS, XLHP_HAS_REDIRECTIONS, XLHP_IS_CATALOG_REL, XLHP_VM_ALL_FROZEN, XLHP_VM_ALL_VISIBLE, XLOG_HEAP2_PRUNE_ON_ACCESS, XLOG_HEAP2_PRUNE_VACUUM_CLEANUP, XLOG_HEAP2_PRUNE_VACUUM_SCAN, XLogBeginInsert(), XLogHintBitIsNeeded, XLogInsert(), XLogRecPtrIsValid, XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by heap_page_prune_and_freeze(), lazy_scan_new_or_empty(), and lazy_vacuum_heap_page().

◆ ReleaseBulkInsertStatePin()

void ReleaseBulkInsertStatePin ( BulkInsertState  bistate)
extern

Definition at line 1966 of file heapam.c.

1967{
1968 if (bistate->current_buf != InvalidBuffer)
1969 ReleaseBuffer(bistate->current_buf);
1970 bistate->current_buf = InvalidBuffer;
1971
1972 /*
1973 * Despite the name, we also reset bulk relation extension state.
1974 * Otherwise we can end up erroring out due to looking for free space in
1975 * ->next_free of one partition, even though ->next_free was set when
1976 * extending another partition. It could obviously also be bad for
1977 * efficiency to look at existing blocks at offsets from another
1978 * partition, even if we don't error out.
1979 */
1980 bistate->next_free = InvalidBlockNumber;
1981 bistate->last_free = InvalidBlockNumber;
1982}

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

Referenced by CopyFrom().

◆ ResolveCminCmaxDuringDecoding()

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

Definition at line 5555 of file reorderbuffer.c.

5559{
5562 ForkNumber forkno;
5563 BlockNumber blockno;
5564 bool updated_mapping = false;
5565
5566 /*
5567 * Return unresolved if tuplecid_data is not valid. That's because when
5568 * streaming in-progress transactions we may run into tuples with the CID
5569 * before actually decoding them. Think e.g. about INSERT followed by
5570 * TRUNCATE, where the TRUNCATE may not be decoded yet when applying the
5571 * INSERT. So in such cases, we assume the CID is from the future
5572 * command.
5573 */
5574 if (tuplecid_data == NULL)
5575 return false;
5576
5577 /* be careful about padding */
5578 memset(&key, 0, sizeof(key));
5579
5580 Assert(!BufferIsLocal(buffer));
5581
5582 /*
5583 * get relfilelocator from the buffer, no convenient way to access it
5584 * other than that.
5585 */
5586 BufferGetTag(buffer, &key.rlocator, &forkno, &blockno);
5587
5588 /* tuples can only be in the main fork */
5589 Assert(forkno == MAIN_FORKNUM);
5590 Assert(blockno == ItemPointerGetBlockNumber(&htup->t_self));
5591
5592 ItemPointerCopy(&htup->t_self,
5593 &key.tid);
5594
5595restart:
5598
5599 /*
5600 * failed to find a mapping, check whether the table was rewritten and
5601 * apply mapping if so, but only do that once - there can be no new
5602 * mappings while we are in here since we have to hold a lock on the
5603 * relation.
5604 */
5605 if (ent == NULL && !updated_mapping)
5606 {
5608 /* now check but don't update for a mapping again */
5609 updated_mapping = true;
5610 goto restart;
5611 }
5612 else if (ent == NULL)
5613 return false;
5614
5615 if (cmin)
5616 *cmin = ent->cmin;
5617 if (cmax)
5618 *cmax = ent->cmax;
5619 return true;
5620}
#define BufferIsLocal(buffer)
Definition buf.h:37
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition dynahash.c:889
@ HASH_FIND
Definition hsearch.h:108
static void UpdateLogicalMappings(HTAB *tuplecid_data, Oid relid, Snapshot snapshot)
static HTAB * tuplecid_data
Definition snapmgr.c:163

References Assert, BufferGetTag(), BufferIsLocal, fb(), HASH_FIND, hash_search(), ItemPointerCopy(), ItemPointerGetBlockNumber(), MAIN_FORKNUM, HeapTupleData::t_self, HeapTupleData::t_tableOid, tuplecid_data, and UpdateLogicalMappings().

Referenced by HeapTupleSatisfiesHistoricMVCC().

◆ simple_heap_delete()

void simple_heap_delete ( Relation  relation,
const ItemPointerData tid 
)
extern

Definition at line 3153 of file heapam.c.

3154{
3156 TM_FailureData tmfd;
3157
3158 result = heap_delete(relation, tid,
3159 GetCurrentCommandId(true),
3160 0,
3162 true /* wait for commit */ ,
3163 &tmfd);
3164 switch (result)
3165 {
3166 case TM_SelfModified:
3167 /* Tuple was already updated in current command? */
3168 elog(ERROR, "tuple already updated by self");
3169 break;
3170
3171 case TM_Ok:
3172 /* done successfully */
3173 break;
3174
3175 case TM_Updated:
3176 elog(ERROR, "tuple concurrently updated");
3177 break;
3178
3179 case TM_Deleted:
3180 elog(ERROR, "tuple concurrently deleted");
3181 break;
3182
3183 default:
3184 elog(ERROR, "unrecognized heap_delete status: %u", result);
3185 break;
3186 }
3187}
TM_Result heap_delete(Relation relation, const ItemPointerData *tid, CommandId cid, uint32 options, Snapshot crosscheck, bool wait, TM_FailureData *tmfd)
Definition heapam.c:2717

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

Referenced by CatalogTupleDelete(), and toast_delete_datum().

◆ simple_heap_insert()

void simple_heap_insert ( Relation  relation,
HeapTuple  tup 
)
extern

Definition at line 2659 of file heapam.c.

2660{
2661 heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
2662}
void heap_insert(Relation relation, HeapTuple tup, CommandId cid, uint32 options, BulkInsertState bistate)
Definition heapam.c:2004

References fb(), GetCurrentCommandId(), and heap_insert().

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

◆ simple_heap_update()

void simple_heap_update ( Relation  relation,
const ItemPointerData otid,
HeapTuple  tup,
TU_UpdateIndexes update_indexes 
)
extern

Definition at line 4450 of file heapam.c.

4452{
4454 TM_FailureData tmfd;
4455 LockTupleMode lockmode;
4456
4457 result = heap_update(relation, otid, tup,
4458 GetCurrentCommandId(true), 0,
4460 true /* wait for commit */ ,
4461 &tmfd, &lockmode, update_indexes);
4462 switch (result)
4463 {
4464 case TM_SelfModified:
4465 /* Tuple was already updated in current command? */
4466 elog(ERROR, "tuple already updated by self");
4467 break;
4468
4469 case TM_Ok:
4470 /* done successfully */
4471 break;
4472
4473 case TM_Updated:
4474 elog(ERROR, "tuple concurrently updated");
4475 break;
4476
4477 case TM_Deleted:
4478 elog(ERROR, "tuple concurrently deleted");
4479 break;
4480
4481 default:
4482 elog(ERROR, "unrecognized heap_update status: %u", result);
4483 break;
4484 }
4485}
TM_Result heap_update(Relation relation, const ItemPointerData *otid, HeapTuple newtup, CommandId cid, uint32 options pg_attribute_unused(), Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
Definition heapam.c:3201

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

Referenced by CatalogTupleUpdate(), and CatalogTupleUpdateWithInfo().