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

1956{
1957 if (bistate->current_buf != InvalidBuffer)
1958 ReleaseBuffer(bistate->current_buf);
1959 FreeAccessStrategy(bistate->strategy);
1960 pfree(bistate);
1961}
#define InvalidBuffer
Definition buf.h:25
void ReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5609
void FreeAccessStrategy(BufferAccessStrategy strategy)
Definition freelist.c:608
void pfree(void *pointer)
Definition mcxt.c:1619
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 1938 of file heapam.c.

1939{
1940 BulkInsertState bistate;
1941
1944 bistate->current_buf = InvalidBuffer;
1945 bistate->next_free = InvalidBlockNumber;
1946 bistate->last_free = InvalidBlockNumber;
1947 bistate->already_extended_by = 0;
1948 return bistate;
1949}
#define InvalidBlockNumber
Definition block.h:33
@ BAS_BULKWRITE
Definition bufmgr.h:39
#define palloc_object(type)
Definition fe_memutils.h:89
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 6390 of file heapam.c.

6391{
6393 ItemId lp;
6394 HeapTupleData tp;
6395 Page page;
6396 BlockNumber block;
6397 Buffer buffer;
6398
6400
6401 block = ItemPointerGetBlockNumber(tid);
6402 buffer = ReadBuffer(relation, block);
6403 page = BufferGetPage(buffer);
6404
6406
6407 /*
6408 * Page can't be all visible, we just inserted into it, and are still
6409 * running.
6410 */
6411 Assert(!PageIsAllVisible(page));
6412
6415
6416 tp.t_tableOid = RelationGetRelid(relation);
6417 tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
6418 tp.t_len = ItemIdGetLength(lp);
6419 tp.t_self = *tid;
6420
6421 /*
6422 * Sanity check that the tuple really is a speculatively inserted tuple,
6423 * inserted by us.
6424 */
6425 if (tp.t_data->t_choice.t_heap.t_xmin != xid)
6426 elog(ERROR, "attempted to kill a tuple inserted by another transaction");
6427 if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
6428 elog(ERROR, "attempted to kill a non-speculative tuple");
6430
6431 /*
6432 * No need to check for serializable conflicts here. There is never a
6433 * need for a combo CID, either. No need to extract replica identity, or
6434 * do anything special with infomask bits.
6435 */
6436
6438
6439 /*
6440 * The tuple will become DEAD immediately. Flag that this page is a
6441 * candidate for pruning by setting xmin to TransactionXmin. While not
6442 * immediately prunable, it is the oldest xid we can cheaply determine
6443 * that's safe against wraparound / being older than the table's
6444 * relfrozenxid. To defend against the unlikely case of a new relation
6445 * having a newer relfrozenxid than our TransactionXmin, use relfrozenxid
6446 * if so (vacuum can't subsequently move relfrozenxid to beyond
6447 * TransactionXmin, so there's no race here).
6448 */
6450 {
6451 TransactionId relfrozenxid = relation->rd_rel->relfrozenxid;
6453
6454 if (TransactionIdPrecedes(TransactionXmin, relfrozenxid))
6455 prune_xid = relfrozenxid;
6456 else
6459 }
6460
6461 /* store transaction information of xact deleting the tuple */
6464
6465 /*
6466 * Set the tuple header xmin to InvalidTransactionId. This makes the
6467 * tuple immediately invisible everyone. (In particular, to any
6468 * transactions waiting on the speculative token, woken up later.)
6469 */
6471
6472 /* Clear the speculative insertion token too */
6473 tp.t_data->t_ctid = tp.t_self;
6474
6475 MarkBufferDirty(buffer);
6476
6477 /*
6478 * XLOG stuff
6479 *
6480 * The WAL records generated here match heap_delete(). The same recovery
6481 * routines are used.
6482 */
6483 if (RelationNeedsWAL(relation))
6484 {
6487
6489 xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
6490 tp.t_data->t_infomask2);
6492 xlrec.xmax = xid;
6493
6497
6498 /* No replica identity & replication origin logged */
6499
6501
6502 PageSetLSN(page, recptr);
6503 }
6504
6506
6508
6509 if (HeapTupleHasExternal(&tp))
6510 {
6511 Assert(!IsToastRelation(relation));
6512 heap_toast_delete(relation, &tp, true);
6513 }
6514
6515 /*
6516 * Never need to mark tuple for invalidation, since catalogs don't support
6517 * speculative insertion
6518 */
6519
6520 /* Now we can release the buffer */
6521 ReleaseBuffer(buffer);
6522
6523 /* count deletion, as we counted the insertion too */
6524 pgstat_count_heap_delete(relation);
6525}
uint32 BlockNumber
Definition block.h:31
int Buffer
Definition buf.h:23
void MarkBufferDirty(Buffer buffer)
Definition bufmgr.c:3170
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:1002
uint32 TransactionId
Definition c.h:795
bool IsToastRelation(Relation relation)
Definition catalog.c:208
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
static uint8 compute_infobits(uint16 infomask, uint16 infomask2)
Definition heapam.c:2714
#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::@52 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 1168 of file heapam.c.

1172{
1173 HeapScanDesc scan;
1174
1175 /*
1176 * increment relation ref count while scanning relation
1177 *
1178 * This is just to make really sure the relcache entry won't go away while
1179 * the scan has a pointer to it. Caller should be holding the rel open
1180 * anyway, so this is redundant in all normal scenarios...
1181 */
1183
1184 /*
1185 * allocate and initialize scan descriptor
1186 */
1187 if (flags & SO_TYPE_BITMAPSCAN)
1188 {
1190
1191 /*
1192 * Bitmap Heap scans do not have any fields that a normal Heap Scan
1193 * does not have, so no special initializations required here.
1194 */
1195 scan = (HeapScanDesc) bscan;
1196 }
1197 else
1199
1200 scan->rs_base.rs_rd = relation;
1201 scan->rs_base.rs_snapshot = snapshot;
1202 scan->rs_base.rs_nkeys = nkeys;
1203 scan->rs_base.rs_flags = flags;
1204 scan->rs_base.rs_parallel = parallel_scan;
1205 scan->rs_base.rs_instrument = NULL;
1206 scan->rs_strategy = NULL; /* set in initscan */
1207 scan->rs_cbuf = InvalidBuffer;
1208
1209 /*
1210 * Disable page-at-a-time mode if it's not a MVCC-safe snapshot.
1211 */
1212 if (!(snapshot && IsMVCCSnapshot(snapshot)))
1214
1215 /* Check that a historic snapshot is not used for non-catalog tables */
1216 if (snapshot &&
1217 IsHistoricMVCCSnapshot(snapshot) &&
1219 {
1220 ereport(ERROR,
1222 errmsg("cannot query non-catalog table \"%s\" during logical decoding",
1223 RelationGetRelationName(relation))));
1224 }
1225
1226 /*
1227 * For seqscan and sample scans in a serializable transaction, acquire a
1228 * predicate lock on the entire relation. This is required not only to
1229 * lock all the matching tuples, but also to conflict with new insertions
1230 * into the table. In an indexscan, we take page locks on the index pages
1231 * covering the range specified in the scan qual, but in a heap scan there
1232 * is nothing more fine-grained to lock. A bitmap scan is a different
1233 * story, there we have already scanned the index and locked the index
1234 * pages covering the predicate. But in that case we still have to lock
1235 * any matching heap tuples. For sample scan we could optimize the locking
1236 * to be at least page-level granularity, but we'd need to add per-tuple
1237 * locking for that.
1238 */
1240 {
1241 /*
1242 * Ensure a missing snapshot is noticed reliably, even if the
1243 * isolation mode means predicate locking isn't performed (and
1244 * therefore the snapshot isn't used here).
1245 */
1246 Assert(snapshot);
1247 PredicateLockRelation(relation, snapshot);
1248 }
1249
1250 /* we only need to set this up once */
1251 scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
1252
1253 /*
1254 * Allocate memory to keep track of page allocation for parallel workers
1255 * when doing a parallel scan.
1256 */
1257 if (parallel_scan != NULL)
1259 else
1261
1262 /*
1263 * we do this here instead of in initscan() because heap_rescan also calls
1264 * initscan() and we don't want to allocate memory again
1265 */
1266 if (nkeys > 0)
1267 scan->rs_base.rs_key = palloc_array(ScanKeyData, nkeys);
1268 else
1269 scan->rs_base.rs_key = NULL;
1270
1271 initscan(scan, key, false);
1272
1273 scan->rs_read_stream = NULL;
1274
1275 /*
1276 * Set up a read stream for sequential scans and TID range scans. This
1277 * should be done after initscan() because initscan() allocates the
1278 * BufferAccessStrategy object passed to the read stream API.
1279 */
1280 if (scan->rs_base.rs_flags & SO_TYPE_SEQSCAN ||
1282 {
1284
1285 if (scan->rs_base.rs_parallel)
1287 else
1289
1290 /* ---
1291 * It is safe to use batchmode as the only locks taken by `cb`
1292 * are never taken while waiting for IO:
1293 * - SyncScanLock is used in the non-parallel case
1294 * - in the parallel case, only spinlocks and atomics are used
1295 * ---
1296 */
1299 scan->rs_strategy,
1300 scan->rs_base.rs_rd,
1302 cb,
1303 scan,
1304 0);
1305 }
1306 else if (scan->rs_base.rs_flags & SO_TYPE_BITMAPSCAN)
1307 {
1310 scan->rs_strategy,
1311 scan->rs_base.rs_rd,
1314 scan,
1315 sizeof(TBMIterateResult));
1316 }
1317
1318 /* enable read stream instrumentation */
1319 if ((flags & SO_SCAN_INSTRUMENT) && (scan->rs_read_stream != NULL))
1320 {
1323 &scan->rs_base.rs_instrument->io);
1324 }
1325
1326 scan->rs_vmbuffer = InvalidBuffer;
1327
1328 return (TableScanDesc) scan;
1329}
int errcode(int sqlerrcode)
Definition elog.c:875
#define ereport(elevel,...)
Definition elog.h:152
#define palloc_array(type, count)
Definition fe_memutils.h:91
#define palloc0_object(type)
Definition fe_memutils.h:90
static BlockNumber heap_scan_stream_read_next_parallel(ReadStream *stream, void *callback_private_data, void *per_buffer_data)
Definition heapam.c:255
static BlockNumber heap_scan_stream_read_next_serial(ReadStream *stream, void *callback_private_data, void *per_buffer_data)
Definition heapam.c:295
static BlockNumber bitmapheap_stream_read_next(ReadStream *pgsr, void *private_data, void *per_buffer_data)
Definition heapam.c:320
static void initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
Definition heapam.c:360
struct HeapScanDescData * HeapScanDesc
Definition heapam.h:107
static char * errmsg
void PredicateLockRelation(Relation relation, Snapshot snapshot)
Definition predicate.c:2505
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:704
void RelationIncrementReferenceCount(Relation rel)
Definition relcache.c:2191
@ 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 2759 of file heapam.c.

2762{
2765 ItemId lp;
2766 HeapTupleData tp;
2767 Page page;
2768 BlockNumber block;
2769 Buffer buffer;
2770 Buffer vmbuffer = InvalidBuffer;
2771 bool vmbuffer_modified = false;
2772 TransactionId new_xmax;
2777 bool have_tuple_lock = false;
2778 bool iscombo;
2779 bool clear_all_visible = false;
2780 HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
2781 bool old_key_copied = false;
2782
2784
2785 AssertHasSnapshotForToast(relation);
2786
2787 /*
2788 * Forbid this during a parallel operation, lest it allocate a combo CID.
2789 * Other workers might need that combo CID for visibility checks, and we
2790 * have no provision for broadcasting it to them.
2791 */
2792 if (IsInParallelMode())
2793 ereport(ERROR,
2795 errmsg("cannot delete tuples during a parallel operation")));
2796
2797 block = ItemPointerGetBlockNumber(tid);
2798 buffer = ReadBuffer(relation, block);
2799 page = BufferGetPage(buffer);
2800
2801 /*
2802 * Before locking the buffer, pin the visibility map page if it appears to
2803 * be necessary. Since we haven't got the lock yet, someone else might be
2804 * in the middle of changing this, so we'll need to recheck after we have
2805 * the lock.
2806 */
2807 if (PageIsAllVisible(page))
2808 visibilitymap_pin(relation, block, &vmbuffer);
2809
2811
2814
2815 tp.t_tableOid = RelationGetRelid(relation);
2816 tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2817 tp.t_len = ItemIdGetLength(lp);
2818 tp.t_self = *tid;
2819
2820l1:
2821
2822 /*
2823 * If we didn't pin the visibility map page and the page has become all
2824 * visible while we were busy locking the buffer, we'll have to unlock and
2825 * re-lock, to avoid holding the buffer lock across an I/O. That's a bit
2826 * unfortunate, but hopefully shouldn't happen often.
2827 */
2828 if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
2829 {
2831 visibilitymap_pin(relation, block, &vmbuffer);
2833 }
2834
2835 result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
2836
2837 if (result == TM_Invisible)
2838 {
2839 UnlockReleaseBuffer(buffer);
2840 ereport(ERROR,
2842 errmsg("attempted to delete invisible tuple")));
2843 }
2844 else if (result == TM_BeingModified && wait)
2845 {
2848
2849 /* must copy state data before unlocking buffer */
2852
2853 /*
2854 * Sleep until concurrent transaction ends -- except when there's a
2855 * single locker and it's our own transaction. Note we don't care
2856 * which lock mode the locker has, because we need the strongest one.
2857 *
2858 * Before sleeping, we need to acquire tuple lock to establish our
2859 * priority for the tuple (see heap_lock_tuple). LockTuple will
2860 * release us when we are next-in-line for the tuple.
2861 *
2862 * If we are forced to "start over" below, we keep the tuple lock;
2863 * this arranges that we stay at the head of the line while rechecking
2864 * tuple state.
2865 */
2867 {
2868 bool current_is_member = false;
2869
2872 {
2874
2875 /*
2876 * Acquire the lock, if necessary (but skip it when we're
2877 * requesting a lock and already have one; avoids deadlock).
2878 */
2879 if (!current_is_member)
2882
2883 /* wait for multixact */
2885 relation, &(tp.t_self), XLTW_Delete,
2886 NULL);
2888
2889 /*
2890 * If xwait had just locked the tuple then some other xact
2891 * could update this tuple before we get to this point. Check
2892 * for xmax change, and start over if so.
2893 *
2894 * We also must start over if we didn't pin the VM page, and
2895 * the page has become all visible.
2896 */
2897 if ((vmbuffer == InvalidBuffer && PageIsAllVisible(page)) ||
2900 xwait))
2901 goto l1;
2902 }
2903
2904 /*
2905 * You might think the multixact is necessarily done here, but not
2906 * so: it could have surviving members, namely our own xact or
2907 * other subxacts of this backend. It is legal for us to delete
2908 * the tuple in either case, however (the latter case is
2909 * essentially a situation of upgrading our former shared lock to
2910 * exclusive). We don't bother changing the on-disk hint bits
2911 * since we are about to overwrite the xmax altogether.
2912 */
2913 }
2915 {
2916 /*
2917 * Wait for regular transaction to end; but first, acquire tuple
2918 * lock.
2919 */
2923 XactLockTableWait(xwait, relation, &(tp.t_self), XLTW_Delete);
2925
2926 /*
2927 * xwait is done, but if xwait had just locked the tuple then some
2928 * other xact could update this tuple before we get to this point.
2929 * Check for xmax change, and start over if so.
2930 *
2931 * We also must start over if we didn't pin the VM page, and the
2932 * page has become all visible.
2933 */
2934 if ((vmbuffer == InvalidBuffer && PageIsAllVisible(page)) ||
2937 xwait))
2938 goto l1;
2939
2940 /* Otherwise check if it committed or aborted */
2941 UpdateXmaxHintBits(tp.t_data, buffer, xwait);
2942 }
2943
2944 /*
2945 * We may overwrite if previous xmax aborted, or if it committed but
2946 * only locked the tuple without updating it.
2947 */
2948 if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
2951 result = TM_Ok;
2952 else if (!ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
2954 else
2956 }
2957
2958 /* sanity check the result HeapTupleSatisfiesUpdate() and the logic above */
2959 if (result != TM_Ok)
2960 {
2962 result == TM_Updated ||
2963 result == TM_Deleted ||
2968 }
2969
2971 {
2972 /* Perform additional check for transaction-snapshot mode RI updates */
2973 if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
2975 }
2976
2977 if (result != TM_Ok)
2978 {
2979 tmfd->ctid = tp.t_data->t_ctid;
2981 if (result == TM_SelfModified)
2983 else
2984 tmfd->cmax = InvalidCommandId;
2985 UnlockReleaseBuffer(buffer);
2986 if (have_tuple_lock)
2988 if (vmbuffer != InvalidBuffer)
2989 ReleaseBuffer(vmbuffer);
2990 return result;
2991 }
2992
2993 /*
2994 * We're about to do the actual delete -- check for conflict first, to
2995 * avoid possibly having to roll back work we've just done.
2996 *
2997 * This is safe without a recheck as long as there is no possibility of
2998 * another process scanning the page between this check and the delete
2999 * being visible to the scan (i.e., an exclusive buffer content lock is
3000 * continuously held from this point until the tuple delete is visible).
3001 */
3003
3004 /* replace cid with a combo CID if necessary */
3006
3007 /*
3008 * Compute replica identity tuple before entering the critical section so
3009 * we don't PANIC upon a memory allocation failure.
3010 */
3012 ExtractReplicaIdentity(relation, &tp, true, &old_key_copied) : NULL;
3013
3014 /*
3015 * If this is the first possibly-multixact-able operation in the current
3016 * transaction, set my per-backend OldestMemberMXactId setting. We can be
3017 * certain that the transaction will never become a member of any older
3018 * MultiXactIds than that. (We have to do this even if we end up just
3019 * using our own TransactionId below, since some other backend could
3020 * incorporate our XID into a MultiXact immediately afterwards.)
3021 */
3023
3026 xid, LockTupleExclusive, true,
3027 &new_xmax, &new_infomask, &new_infomask2);
3028
3029 /* Lock the VM before entering the critical section */
3030 if (PageIsAllVisible(page))
3031 {
3032 clear_all_visible = true;
3034 }
3035
3037
3038 /*
3039 * If this transaction commits, the tuple will become DEAD sooner or
3040 * later. Set flag that this page is a candidate for pruning once our xid
3041 * falls below the OldestXmin horizon. If the transaction finally aborts,
3042 * the subsequent page pruning will be a no-op and the hint will be
3043 * cleared.
3044 */
3045 PageSetPrunable(page, xid);
3046
3048 {
3049 /* It's possible the VM bits were already clear */
3051 vmbuffer, VISIBILITYMAP_VALID_BITS))
3052 vmbuffer_modified = true;
3053
3054 PageClearAllVisible(page);
3055 }
3056
3057 /* store transaction information of xact deleting the tuple */
3063 HeapTupleHeaderSetXmax(tp.t_data, new_xmax);
3065 /* Make sure there is no forward chain link in t_ctid */
3066 tp.t_data->t_ctid = tp.t_self;
3067
3068 /* Signal that this is actually a move into another partition */
3069 if (changingPart)
3071
3072 MarkBufferDirty(buffer);
3073
3074 /*
3075 * XLOG stuff
3076 *
3077 * NB: heap_abort_speculative() uses the same xlog record and replay
3078 * routines.
3079 */
3080 if (RelationNeedsWAL(relation))
3081 {
3085
3086 /*
3087 * For logical decode we need combo CIDs to properly decode the
3088 * catalog
3089 */
3091 log_heap_new_cid(relation, &tp);
3092
3093 xlrec.flags = 0;
3096 if (changingPart)
3098 xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
3099 tp.t_data->t_infomask2);
3101 xlrec.xmax = new_xmax;
3102
3103 if (old_key_tuple != NULL)
3104 {
3105 if (relation->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
3107 else
3109 }
3110
3111 /*
3112 * Mark the change as not-for-logical-decoding if caller requested so.
3113 *
3114 * (This is used for changes that affect relations not visible to
3115 * other transactions, such as the transient table during concurrent
3116 * repack.)
3117 */
3118 if (!walLogical)
3120
3123
3125
3126 /*
3127 * Log replica identity of the deleted tuple if there is one
3128 */
3129 if (old_key_tuple != NULL)
3130 {
3131 xlhdr.t_infomask2 = old_key_tuple->t_data->t_infomask2;
3132 xlhdr.t_infomask = old_key_tuple->t_data->t_infomask;
3133 xlhdr.t_hoff = old_key_tuple->t_data->t_hoff;
3134
3136 XLogRegisterData((char *) old_key_tuple->t_data
3138 old_key_tuple->t_len
3140 }
3141
3142 /* filtering by origin on a row level is much more efficient */
3144
3147
3149
3150 PageSetLSN(page, recptr);
3151
3153 PageSetLSN(BufferGetPage(vmbuffer), recptr);
3154 }
3155
3157
3158 /*
3159 * Release VM lock first, since it covers many heap blocks. We locked
3160 * vmbuffer if clear_all_visible was true regardless of whether or not we
3161 * ended up modifying the vmbuffer.
3162 */
3164 LockBuffer(vmbuffer, BUFFER_LOCK_UNLOCK);
3165
3167
3168 if (vmbuffer != InvalidBuffer)
3169 ReleaseBuffer(vmbuffer);
3170
3171 /*
3172 * If the tuple has toasted out-of-line attributes, we need to delete
3173 * those items too. We have to do this before releasing the buffer
3174 * because we need to look at the contents of the tuple, but it's OK to
3175 * release the content lock on the buffer first.
3176 */
3177 if (relation->rd_rel->relkind != RELKIND_RELATION &&
3178 relation->rd_rel->relkind != RELKIND_MATVIEW)
3179 {
3180 /* toast table entries should never be recursively toasted */
3182 }
3183 else if (HeapTupleHasExternal(&tp))
3184 heap_toast_delete(relation, &tp, false);
3185
3186 /*
3187 * Mark tuple for invalidation from system caches at next command
3188 * boundary. We have to do this before releasing the buffer because we
3189 * need to look at the contents of the tuple.
3190 */
3191 CacheInvalidateHeapTuple(relation, &tp, NULL);
3192
3193 /* Now we can release the buffer */
3194 ReleaseBuffer(buffer);
3195
3196 /*
3197 * Release the lmgr tuple lock, if we had it.
3198 */
3199 if (have_tuple_lock)
3201
3202 pgstat_count_heap_delete(relation);
3203
3206
3207 return TM_Ok;
3208}
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition bufmgr.c:4469
void UnlockReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5626
static void PageClearAllVisible(Page page)
Definition bufpage.h:464
#define InvalidCommandId
Definition c.h:812
TransactionId MultiXactId
Definition c.h:805
uint16_t uint16
Definition c.h:682
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:7816
static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup)
Definition heapam.c:9257
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:5503
static bool heap_acquire_tuplock(Relation relation, const ItemPointerData *tid, LockTupleMode mode, LockWaitPolicy wait_policy, bool *have_tuple_lock)
Definition heapam.c:5454
static HeapTuple ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool key_required, bool *copy)
Definition heapam.c:9338
static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, const ItemPointerData *ctid, XLTW_Oper oper, int *remaining)
Definition heapam.c:7994
static bool xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
Definition heapam.c:2736
#define UnlockTupleTuplock(rel, tup, mode)
Definition heapam.c:172
static void AssertHasSnapshotForToast(Relation rel)
Definition heapam.c:228
static void UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
Definition heapam.c:1916
bool HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
TM_Result HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, Buffer buffer)
#define HEAP_DELETE_BLKREF_HEAP
#define XLH_DELETE_CONTAINS_OLD_KEY
#define XLH_DELETE_ALL_VISIBLE_CLEARED
#define HEAP_DELETE_BLKREF_VM
#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:1568
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:4265
#define InvalidSnapshot
Definition snapshot.h:119
RelFileLocator rd_locator
Definition rel.h:57
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(RelFileLocator rlocator, 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_DELETE_BLKREF_HEAP, HEAP_DELETE_BLKREF_VM, 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_locator, 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 1391 of file heapam.c.

1392{
1394
1395 /* Note: no locking manipulations needed */
1396
1397 /*
1398 * unpin scan buffers
1399 */
1400 if (BufferIsValid(scan->rs_cbuf))
1401 ReleaseBuffer(scan->rs_cbuf);
1402
1403 if (BufferIsValid(scan->rs_vmbuffer))
1405
1406 /*
1407 * Must free the read stream before freeing the BufferAccessStrategy.
1408 */
1409 if (scan->rs_read_stream)
1411
1412 /*
1413 * decrement relation reference count and free scan descriptor storage
1414 */
1416
1417 if (scan->rs_base.rs_key)
1418 pfree(scan->rs_base.rs_key);
1419
1420 if (scan->rs_strategy != NULL)
1422
1423 if (scan->rs_parallelworkerdata != NULL)
1425
1426 if (scan->rs_base.rs_flags & SO_TEMP_SNAPSHOT)
1428
1429 if (scan->rs_base.rs_instrument)
1431
1432 pfree(scan);
1433}
static bool BufferIsValid(Buffer bufnum)
Definition bufmgr.h:419
void read_stream_end(ReadStream *stream)
void RelationDecrementReferenceCount(Relation rel)
Definition relcache.c:2204
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 1685 of file heapam.c.

1690{
1691 ItemPointer tid = &(tuple->t_self);
1692 ItemId lp;
1693 Buffer buffer;
1694 Page page;
1695 OffsetNumber offnum;
1696 bool valid;
1697
1698 /*
1699 * Fetch and pin the appropriate page of the relation.
1700 */
1701 buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
1702
1703 /*
1704 * Need share lock on buffer to examine tuple commit status.
1705 */
1707 page = BufferGetPage(buffer);
1708
1709 /*
1710 * We'd better check for out-of-range offnum in case of VACUUM since the
1711 * TID was obtained.
1712 */
1713 offnum = ItemPointerGetOffsetNumber(tid);
1715 {
1716 UnlockReleaseBuffer(buffer);
1718 tuple->t_data = NULL;
1719 return false;
1720 }
1721
1722 /*
1723 * get the item line pointer corresponding to the requested tid
1724 */
1725 lp = PageGetItemId(page, offnum);
1726
1727 /*
1728 * Must check for deleted tuple.
1729 */
1730 if (!ItemIdIsNormal(lp))
1731 {
1732 UnlockReleaseBuffer(buffer);
1734 tuple->t_data = NULL;
1735 return false;
1736 }
1737
1738 /*
1739 * fill in *tuple fields
1740 */
1741 tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
1742 tuple->t_len = ItemIdGetLength(lp);
1743 tuple->t_tableOid = RelationGetRelid(relation);
1744
1745 /*
1746 * check tuple visibility, then release lock
1747 */
1748 valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
1749
1750 if (valid)
1751 PredicateLockTID(relation, &(tuple->t_self), snapshot,
1753
1754 HeapCheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
1755
1757
1758 if (valid)
1759 {
1760 /*
1761 * All checks passed, so return the tuple as valid. Caller is now
1762 * responsible for releasing the buffer.
1763 */
1764 *userbuf = buffer;
1765
1766 return true;
1767 }
1768
1769 /* Tuple failed time qual, but maybe caller wants to see it anyway. */
1770 if (keep_buf)
1771 *userbuf = buffer;
1772 else
1773 {
1774 ReleaseBuffer(buffer);
1776 tuple->t_data = NULL;
1777 }
1778
1779 return false;
1780}
@ 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:9442
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:2550

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

6304{
6305 Buffer buffer;
6306 Page page;
6307 OffsetNumber offnum;
6308 ItemId lp;
6309 HeapTupleHeader htup;
6310
6311 buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
6313 page = BufferGetPage(buffer);
6314
6315 offnum = ItemPointerGetOffsetNumber(tid);
6317 elog(ERROR, "offnum out of range");
6318 lp = PageGetItemId(page, offnum);
6319 if (!ItemIdIsNormal(lp))
6320 elog(ERROR, "invalid lp");
6321
6322 htup = (HeapTupleHeader) PageGetItem(page, lp);
6323
6324 /* NO EREPORT(ERROR) from here till changes are logged */
6326
6328
6329 MarkBufferDirty(buffer);
6330
6331 /*
6332 * Replace the speculative insertion token with a real t_ctid, pointing to
6333 * itself like it does on regular tuples.
6334 */
6335 htup->t_ctid = *tid;
6336
6337 /* XLOG stuff */
6338 if (RelationNeedsWAL(relation))
6339 {
6342
6344
6346
6347 /* We want the same filtering on this as on a plain insert */
6349
6352
6354
6355 PageSetLSN(page, recptr);
6356 }
6357
6359
6360 UnlockReleaseBuffer(buffer);
6361}
#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 7600 of file heapam.c.

7601{
7602 Page page = BufferGetPage(buffer);
7603
7604 for (int i = 0; i < ntuples; i++)
7605 {
7606 HeapTupleFreeze *frz = tuples + i;
7607 ItemId itemid = PageGetItemId(page, frz->offset);
7608 HeapTupleHeader htup;
7609
7610 htup = (HeapTupleHeader) PageGetItem(page, itemid);
7612 }
7613}
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 7622 of file heapam.c.

7625{
7627 bool do_freeze;
7628 bool totally_frozen;
7629 struct VacuumCutoffs cutoffs;
7630 HeapPageFreeze pagefrz;
7631
7632 cutoffs.relfrozenxid = relfrozenxid;
7633 cutoffs.relminmxid = relminmxid;
7634 cutoffs.OldestXmin = FreezeLimit;
7635 cutoffs.OldestMxact = MultiXactCutoff;
7636 cutoffs.FreezeLimit = FreezeLimit;
7637 cutoffs.MultiXactCutoff = MultiXactCutoff;
7638
7639 pagefrz.freeze_required = true;
7640 pagefrz.FreezePageRelfrozenXid = FreezeLimit;
7641 pagefrz.FreezePageRelminMxid = MultiXactCutoff;
7642 pagefrz.FreezePageConflictXid = InvalidTransactionId;
7643 pagefrz.NoFreezePageRelfrozenXid = FreezeLimit;
7644 pagefrz.NoFreezePageRelminMxid = MultiXactCutoff;
7645
7646 do_freeze = heap_prepare_freeze_tuple(tuple, &cutoffs,
7647 &pagefrz, &frz, &totally_frozen);
7648
7649 /*
7650 * Note that because this is not a WAL-logged operation, we don't need to
7651 * fill in the offset in the freeze record.
7652 */
7653
7654 if (do_freeze)
7656 return do_freeze;
7657}
bool heap_prepare_freeze_tuple(HeapTupleHeader tuple, const struct VacuumCutoffs *cutoffs, HeapPageFreeze *pagefrz, HeapTupleFreeze *frz, bool *totally_frozen)
Definition heapam.c:7267
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 1794 of file heapam.c.

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

2312{
2313 OffsetNumber offnum,
2314 maxoff;
2315
2318
2319 maxoff = PageGetMaxOffsetNumber(page);
2320 for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
2321 {
2322 ItemId lp = PageGetItemId(page, offnum);
2323 HeapTupleHeader htup;
2326
2327 /* skip unused and dead items */
2328 if (!ItemIdIsUsed(lp) || ItemIdIsDead(lp))
2329 continue;
2330
2331 if (ItemIdIsNormal(lp))
2332 {
2333 htup = (HeapTupleHeader) PageGetItem(page, lp);
2334
2335 /*
2336 * Check if this tuple is part of a HOT-chain rooted at some other
2337 * tuple. If so, skip it for now; we'll process it when we find
2338 * its root.
2339 */
2340 if (HeapTupleHeaderIsHeapOnly(htup))
2341 continue;
2342
2343 /*
2344 * This is either a plain tuple or the root of a HOT-chain.
2345 * Remember it in the mapping.
2346 */
2347 root_offsets[offnum - 1] = offnum;
2348
2349 /* If it's not the start of a HOT-chain, we're done with it */
2350 if (!HeapTupleHeaderIsHotUpdated(htup))
2351 continue;
2352
2353 /* Set up to scan the HOT-chain */
2356 }
2357 else
2358 {
2359 /* Must be a redirect item. We do not set its root_offsets entry */
2361 /* Set up to scan the HOT-chain */
2364 }
2365
2366 /*
2367 * Now follow the HOT-chain and collect other tuples in the chain.
2368 *
2369 * Note: Even though this is a nested loop, the complexity of the
2370 * function is O(N) because a tuple in the page should be visited not
2371 * more than twice, once in the outer loop and once in HOT-chain
2372 * chases.
2373 */
2374 for (;;)
2375 {
2376 /* Sanity check (pure paranoia) */
2378 break;
2379
2380 /*
2381 * An offset past the end of page's line pointer array is possible
2382 * when the array was truncated
2383 */
2384 if (nextoffnum > maxoff)
2385 break;
2386
2387 lp = PageGetItemId(page, nextoffnum);
2388
2389 /* Check for broken chains */
2390 if (!ItemIdIsNormal(lp))
2391 break;
2392
2393 htup = (HeapTupleHeader) PageGetItem(page, lp);
2394
2397 break;
2398
2399 /* Remember the root line pointer for this item */
2400 root_offsets[nextoffnum - 1] = offnum;
2401
2402 /* Advance to next chain member, if any */
2403 if (!HeapTupleHeaderIsHotUpdated(htup))
2404 break;
2405
2406 /* HOT implies it can't have moved to different partition */
2408
2411 }
2412 }
2413}
#define MemSet(start, val, len)
Definition c.h:1147
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 1436 of file heapam.c.

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

1476{
1478
1479 /* Note: no locking manipulations needed */
1480
1481 if (sscan->rs_flags & SO_ALLOW_PAGEMODE)
1482 heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1483 else
1484 heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key);
1485
1486 if (scan->rs_ctup.t_data == NULL)
1487 {
1488 ExecClearTuple(slot);
1489 return false;
1490 }
1491
1492 /*
1493 * if we get here it means we have a new current scan tuple, so point to
1494 * the proper return buffer and return the tuple.
1495 */
1496
1498
1499 ExecStoreBufferHeapTuple(&scan->rs_ctup, slot,
1500 scan->rs_cbuf);
1501 return true;
1502}
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 1578 of file heapam.c.

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

8340{
8341 /* Initial assumption is that earlier pruning took care of conflict */
8342 TransactionId snapshotConflictHorizon = InvalidTransactionId;
8345 Page page = NULL;
8348#ifdef USE_PREFETCH
8351#endif
8353 int finalndeltids = 0,
8354 nblocksaccessed = 0;
8355
8356 /* State that's only used in bottom-up index deletion case */
8357 int nblocksfavorable = 0;
8358 int curtargetfreespace = delstate->bottomupfreespace,
8359 lastfreespace = 0,
8360 actualfreespace = 0;
8361 bool bottomup_final_block = false;
8362
8364
8365 /* Sort caller's deltids array by TID for further processing */
8367
8368 /*
8369 * Bottom-up case: resort deltids array in an order attuned to where the
8370 * greatest number of promising TIDs are to be found, and determine how
8371 * many blocks from the start of sorted array should be considered
8372 * favorable. This will also shrink the deltids array in order to
8373 * eliminate completely unfavorable blocks up front.
8374 */
8375 if (delstate->bottomup)
8377
8378#ifdef USE_PREFETCH
8379 /* Initialize prefetch state. */
8381 prefetch_state.next_item = 0;
8382 prefetch_state.ndeltids = delstate->ndeltids;
8383 prefetch_state.deltids = delstate->deltids;
8384
8385 /*
8386 * Determine the prefetch distance that we will attempt to maintain.
8387 *
8388 * Since the caller holds a buffer lock somewhere in rel, we'd better make
8389 * sure that isn't a catalog relation before we call code that does
8390 * syscache lookups, to avoid risk of deadlock.
8391 */
8392 if (IsCatalogRelation(rel))
8394 else
8397
8398 /* Cap initial prefetch distance for bottom-up deletion caller */
8399 if (delstate->bottomup)
8400 {
8404 }
8405
8406 /* Start prefetching. */
8408#endif
8409
8410 /* Iterate over deltids, determine which to delete, check their horizon */
8411 Assert(delstate->ndeltids > 0);
8412 for (int i = 0; i < delstate->ndeltids; i++)
8413 {
8414 TM_IndexDelete *ideltid = &delstate->deltids[i];
8415 TM_IndexStatus *istatus = delstate->status + ideltid->id;
8416 ItemPointer htid = &ideltid->tid;
8417 OffsetNumber offnum;
8418
8419 /*
8420 * Read buffer, and perform required extra steps each time a new block
8421 * is encountered. Avoid refetching if it's the same block as the one
8422 * from the last htid.
8423 */
8424 if (blkno == InvalidBlockNumber ||
8426 {
8427 /*
8428 * Consider giving up early for bottom-up index deletion caller
8429 * first. (Only prefetch next-next block afterwards, when it
8430 * becomes clear that we're at least going to access the next
8431 * block in line.)
8432 *
8433 * Sometimes the first block frees so much space for bottom-up
8434 * caller that the deletion process can end without accessing any
8435 * more blocks. It is usually necessary to access 2 or 3 blocks
8436 * per bottom-up deletion operation, though.
8437 */
8438 if (delstate->bottomup)
8439 {
8440 /*
8441 * We often allow caller to delete a few additional items
8442 * whose entries we reached after the point that space target
8443 * from caller was satisfied. The cost of accessing the page
8444 * was already paid at that point, so it made sense to finish
8445 * it off. When that happened, we finalize everything here
8446 * (by finishing off the whole bottom-up deletion operation
8447 * without needlessly paying the cost of accessing any more
8448 * blocks).
8449 */
8451 break;
8452
8453 /*
8454 * Give up when we didn't enable our caller to free any
8455 * additional space as a result of processing the page that we
8456 * just finished up with. This rule is the main way in which
8457 * we keep the cost of bottom-up deletion under control.
8458 */
8460 break;
8461 lastfreespace = actualfreespace; /* for next time */
8462
8463 /*
8464 * Deletion operation (which is bottom-up) will definitely
8465 * access the next block in line. Prepare for that now.
8466 *
8467 * Decay target free space so that we don't hang on for too
8468 * long with a marginal case. (Space target is only truly
8469 * helpful when it allows us to recognize that we don't need
8470 * to access more than 1 or 2 blocks to satisfy caller due to
8471 * agreeable workload characteristics.)
8472 *
8473 * We are a bit more patient when we encounter contiguous
8474 * blocks, though: these are treated as favorable blocks. The
8475 * decay process is only applied when the next block in line
8476 * is not a favorable/contiguous block. This is not an
8477 * exception to the general rule; we still insist on finding
8478 * at least one deletable item per block accessed. See
8479 * bottomup_nblocksfavorable() for full details of the theory
8480 * behind favorable blocks and heap block locality in general.
8481 *
8482 * Note: The first block in line is always treated as a
8483 * favorable block, so the earliest possible point that the
8484 * decay can be applied is just before we access the second
8485 * block in line. The Assert() verifies this for us.
8486 */
8488 if (nblocksfavorable > 0)
8490 else
8491 curtargetfreespace /= 2;
8492 }
8493
8494 /* release old buffer */
8495 if (BufferIsValid(buf))
8497
8499 buf = ReadBuffer(rel, blkno);
8501 Assert(!delstate->bottomup ||
8503
8504#ifdef USE_PREFETCH
8505
8506 /*
8507 * To maintain the prefetch distance, prefetch one more page for
8508 * each page we read.
8509 */
8511#endif
8512
8514
8515 page = BufferGetPage(buf);
8516 maxoff = PageGetMaxOffsetNumber(page);
8517 }
8518
8519 /*
8520 * In passing, detect index corruption involving an index page with a
8521 * TID that points to a location in the heap that couldn't possibly be
8522 * correct. We only do this with actual TIDs from caller's index page
8523 * (not items reached by traversing through a HOT chain).
8524 */
8526
8527 if (istatus->knowndeletable)
8528 Assert(!delstate->bottomup && !istatus->promising);
8529 else
8530 {
8531 ItemPointerData tmp = *htid;
8533
8534 /* Are any tuples from this HOT chain non-vacuumable? */
8536 &heapTuple, NULL, true))
8537 continue; /* can't delete entry */
8538
8539 /* Caller will delete, since whole HOT chain is vacuumable */
8540 istatus->knowndeletable = true;
8541
8542 /* Maintain index free space info for bottom-up deletion case */
8543 if (delstate->bottomup)
8544 {
8545 Assert(istatus->freespace > 0);
8546 actualfreespace += istatus->freespace;
8548 bottomup_final_block = true;
8549 }
8550 }
8551
8552 /*
8553 * Maintain snapshotConflictHorizon value for deletion operation as a
8554 * whole by advancing current value using heap tuple headers. This is
8555 * loosely based on the logic for pruning a HOT chain.
8556 */
8558 priorXmax = InvalidTransactionId; /* cannot check first XMIN */
8559 for (;;)
8560 {
8561 ItemId lp;
8562 HeapTupleHeader htup;
8563
8564 /* Sanity check (pure paranoia) */
8565 if (offnum < FirstOffsetNumber)
8566 break;
8567
8568 /*
8569 * An offset past the end of page's line pointer array is possible
8570 * when the array was truncated
8571 */
8572 if (offnum > maxoff)
8573 break;
8574
8575 lp = PageGetItemId(page, offnum);
8577 {
8578 offnum = ItemIdGetRedirect(lp);
8579 continue;
8580 }
8581
8582 /*
8583 * We'll often encounter LP_DEAD line pointers (especially with an
8584 * entry marked knowndeletable by our caller up front). No heap
8585 * tuple headers get examined for an htid that leads us to an
8586 * LP_DEAD item. This is okay because the earlier pruning
8587 * operation that made the line pointer LP_DEAD in the first place
8588 * must have considered the original tuple header as part of
8589 * generating its own snapshotConflictHorizon value.
8590 *
8591 * Relying on XLOG_HEAP2_PRUNE_VACUUM_SCAN records like this is
8592 * the same strategy that index vacuuming uses in all cases. Index
8593 * VACUUM WAL records don't even have a snapshotConflictHorizon
8594 * field of their own for this reason.
8595 */
8596 if (!ItemIdIsNormal(lp))
8597 break;
8598
8599 htup = (HeapTupleHeader) PageGetItem(page, lp);
8600
8601 /*
8602 * Check the tuple XMIN against prior XMAX, if any
8603 */
8606 break;
8607
8609 &snapshotConflictHorizon);
8610
8611 /*
8612 * If the tuple is not HOT-updated, then we are at the end of this
8613 * HOT-chain. No need to visit later tuples from the same update
8614 * chain (they get their own index entries) -- just move on to
8615 * next htid from index AM caller.
8616 */
8617 if (!HeapTupleHeaderIsHotUpdated(htup))
8618 break;
8619
8620 /* Advance to next HOT chain member */
8621 Assert(ItemPointerGetBlockNumber(&htup->t_ctid) == blkno);
8622 offnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
8624 }
8625
8626 /* Enable further/final shrinking of deltids for caller */
8627 finalndeltids = i + 1;
8628 }
8629
8631
8632 /*
8633 * Shrink deltids array to exclude non-deletable entries at the end. This
8634 * is not just a minor optimization. Final deltids array size might be
8635 * zero for a bottom-up caller. Index AM is explicitly allowed to rely on
8636 * ndeltids being zero in all cases with zero total deletable entries.
8637 */
8638 Assert(finalndeltids > 0 || delstate->bottomup);
8639 delstate->ndeltids = finalndeltids;
8640
8641 return snapshotConflictHorizon;
8642}
int maintenance_io_concurrency
Definition bufmgr.c:207
#define Min(x, y)
Definition c.h:1131
bool IsCatalogRelation(Relation relation)
Definition catalog.c:106
static int bottomup_sort_and_shrink(TM_IndexDeleteOp *delstate)
Definition heapam.c:8896
void HeapTupleHeaderAdvanceConflictHorizon(HeapTupleHeader tuple, TransactionId *snapshotConflictHorizon)
Definition heapam.c:8194
#define BOTTOMUP_MAX_NBLOCKS
Definition heapam.c:192
static void index_delete_check_htid(TM_IndexDeleteOp *delstate, Page page, OffsetNumber maxoff, const ItemPointerData *htid, TM_IndexStatus *istatus)
Definition heapam.c:8279
static void index_delete_sort(TM_IndexDeleteOp *delstate)
Definition heapam.c:8684
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 6572 of file heapam.c.

6575{
6576 HeapTupleData oldtup = *oldtup_ptr; /* minimize diff vs. heap_update() */
6578 bool ret;
6579
6580#ifdef USE_ASSERT_CHECKING
6581 if (RelationGetRelid(relation) == RelationRelationId)
6583#endif
6584
6585 Assert(BufferIsValid(buffer));
6586
6587 /*
6588 * Register shared cache invals if necessary. Other sessions may finish
6589 * inplace updates of this tuple between this step and LockTuple(). Since
6590 * inplace updates don't change cache keys, that's harmless.
6591 *
6592 * While it's tempting to register invals only after confirming we can
6593 * return true, the following obstacle precludes reordering steps that
6594 * way. Registering invals might reach a CatalogCacheInitializeCache()
6595 * that locks "buffer". That would hang indefinitely if running after our
6596 * own LockBuffer(). Hence, we must register invals before LockBuffer().
6597 */
6599
6600 LockTuple(relation, &oldtup.t_self, InplaceUpdateTupleLock);
6602
6603 /*----------
6604 * Interpret HeapTupleSatisfiesUpdate() like heap_update() does, except:
6605 *
6606 * - wait unconditionally
6607 * - already locked tuple above, since inplace needs that unconditionally
6608 * - don't recheck header after wait: simpler to defer to next iteration
6609 * - don't try to continue even if the updater aborts: likewise
6610 * - no crosscheck
6611 */
6613 buffer);
6614
6615 if (result == TM_Invisible)
6616 {
6617 /* no known way this can happen */
6618 ereport(ERROR,
6620 errmsg_internal("attempted to overwrite invisible tuple")));
6621 }
6622 else if (result == TM_SelfModified)
6623 {
6624 /*
6625 * CREATE INDEX might reach this if an expression is silly enough to
6626 * call e.g. SELECT ... FROM pg_class FOR SHARE. C code of other SQL
6627 * statements might get here after a heap_update() of the same row, in
6628 * the absence of an intervening CommandCounterIncrement().
6629 */
6630 ereport(ERROR,
6632 errmsg("tuple to be updated was already modified by an operation triggered by the current command")));
6633 }
6634 else if (result == TM_BeingModified)
6635 {
6638
6640 infomask = oldtup.t_data->t_infomask;
6641
6643 {
6646 int remain;
6647
6649 lockmode, NULL))
6650 {
6653 ret = false;
6655 relation, &oldtup.t_self, XLTW_Update,
6656 &remain);
6657 }
6658 else
6659 ret = true;
6660 }
6662 ret = true;
6664 ret = true;
6665 else
6666 {
6669 ret = false;
6670 XactLockTableWait(xwait, relation, &oldtup.t_self,
6671 XLTW_Update);
6672 }
6673 }
6674 else
6675 {
6676 ret = (result == TM_Ok);
6677 if (!ret)
6678 {
6681 }
6682 }
6683
6684 /*
6685 * GetCatalogSnapshot() relies on invalidation messages to know when to
6686 * take a new snapshot. COMMIT of xwait is responsible for sending the
6687 * invalidation. We're not acquiring heavyweight locks sufficient to
6688 * block if not yet sent, so we must take a new snapshot to ensure a later
6689 * attempt has a fair chance. While we don't need this if xwait aborted,
6690 * don't bother optimizing that.
6691 */
6692 if (!ret)
6693 {
6694 UnlockTuple(relation, &oldtup.t_self, InplaceUpdateTupleLock);
6697 }
6698 return ret;
6699}
Datum arg
Definition elog.c:1323
static bool HEAP_XMAX_IS_KEYSHR_LOCKED(uint16 infomask)
void CacheInvalidateHeapTupleInplace(Relation relation, HeapTuple key_equivalent_tuple)
Definition inval.c:1590
void ForgetInplace_Inval(void)
Definition inval.c:1283
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 6710 of file heapam.c.

6713{
6714 HeapTupleHeader htup = oldtup->t_data;
6715 uint32 oldlen;
6716 uint32 newlen;
6717 char *dst;
6718 char *src;
6719 int nmsgs = 0;
6721 bool RelcacheInitFileInval = false;
6722
6723 Assert(ItemPointerEquals(&oldtup->t_self, &tuple->t_self));
6724 oldlen = oldtup->t_len - htup->t_hoff;
6725 newlen = tuple->t_len - tuple->t_data->t_hoff;
6726 if (oldlen != newlen || htup->t_hoff != tuple->t_data->t_hoff)
6727 elog(ERROR, "wrong tuple length");
6728
6729 dst = (char *) htup + htup->t_hoff;
6730 src = (char *) tuple->t_data + tuple->t_data->t_hoff;
6731
6732 /* Like RecordTransactionCommit(), log only if needed */
6735 &RelcacheInitFileInval);
6736
6737 /*
6738 * Unlink relcache init files as needed. If unlinking, acquire
6739 * RelCacheInitLock until after associated invalidations. By doing this
6740 * in advance, if we checkpoint and then crash between inplace
6741 * XLogInsert() and inval, we don't rely on StartupXLOG() ->
6742 * RelationCacheInitFileRemove(). That uses elevel==LOG, so replay would
6743 * neglect to PANIC on EIO.
6744 */
6746
6747 /*----------
6748 * NO EREPORT(ERROR) from here till changes are complete
6749 *
6750 * Our exclusive buffer lock won't stop a reader having already pinned and
6751 * checked visibility for this tuple. With the usual order of changes
6752 * (i.e. updating the buffer contents before WAL logging), a reader could
6753 * observe our not-yet-persistent update to relfrozenxid and update
6754 * datfrozenxid based on that. A crash in that moment could allow
6755 * datfrozenxid to overtake relfrozenxid:
6756 *
6757 * ["D" is a VACUUM (ONLY_DATABASE_STATS)]
6758 * ["R" is a VACUUM tbl]
6759 * D: vac_update_datfrozenxid() -> systable_beginscan(pg_class)
6760 * D: systable_getnext() returns pg_class tuple of tbl
6761 * R: memcpy() into pg_class tuple of tbl
6762 * D: raise pg_database.datfrozenxid, XLogInsert(), finish
6763 * [crash]
6764 * [recovery restores datfrozenxid w/o relfrozenxid]
6765 *
6766 * We avoid that by using a temporary copy of the buffer to hide our
6767 * change from other backends until the change has been WAL-logged. We
6768 * apply our change to the temporary copy and WAL-log it, before modifying
6769 * the real page. That way any action a reader of the in-place-updated
6770 * value takes will be WAL logged after this change.
6771 */
6773
6774 MarkBufferDirty(buffer);
6775
6776 /* XLOG stuff */
6777 if (RelationNeedsWAL(relation))
6778 {
6781 char *origdata = (char *) BufferGetBlock(buffer);
6782 Page page = BufferGetPage(buffer);
6783 uint16 lower = ((PageHeader) page)->pd_lower;
6784 uint16 upper = ((PageHeader) page)->pd_upper;
6786 RelFileLocator rlocator;
6787 ForkNumber forkno;
6788 BlockNumber blkno;
6790
6791 xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
6792 xlrec.dbId = MyDatabaseId;
6794 xlrec.relcacheInitFileInval = RelcacheInitFileInval;
6795 xlrec.nmsgs = nmsgs;
6796
6799 if (nmsgs != 0)
6801 nmsgs * sizeof(SharedInvalidationMessage));
6802
6803 /* register block matching what buffer will look like after changes */
6808 BufferGetTag(buffer, &rlocator, &forkno, &blkno);
6809 Assert(forkno == MAIN_FORKNUM);
6810 XLogRegisterBlock(0, &rlocator, forkno, blkno, copied_buffer.data,
6812 XLogRegisterBufData(0, src, newlen);
6813
6814 /* inplace updates aren't decoded atm, don't log the origin */
6815
6817
6818 PageSetLSN(page, recptr);
6819 }
6820
6821 memcpy(dst, src, newlen);
6822
6824
6825 /*
6826 * Send invalidations to shared queue. SearchSysCacheLocked1() assumes we
6827 * do this before UnlockTuple().
6828 */
6830
6832 UnlockTuple(relation, &tuple->t_self, InplaceUpdateTupleLock);
6833
6834 AcceptInvalidationMessages(); /* local processing of just-sent inval */
6835
6836 /*
6837 * Queue a transactional inval, for logical decoding and for third-party
6838 * code that might have been relying on it since long before inplace
6839 * update adopted immediate invalidation. See README.tuplock section
6840 * "Reading inplace-updated columns" for logical decoding details.
6841 */
6843 CacheInvalidateHeapTuple(relation, tuple, NULL);
6844}
void BufferGetTag(Buffer buffer, RelFileLocator *rlocator, ForkNumber *forknum, BlockNumber *blknum)
Definition bufmgr.c:4490
static Block BufferGetBlock(Buffer buffer)
Definition bufmgr.h:435
PageHeaderData * PageHeader
Definition bufpage.h:199
uint32_t uint32
Definition c.h:683
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:1247
void AtInplace_Inval(void)
Definition inval.c:1260
#define IsBootstrapProcessingMode()
Definition miscadmin.h:486
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 2005 of file heapam.c.

2007{
2010 Buffer buffer;
2011 Page page;
2012 Buffer vmbuffer = InvalidBuffer;
2013 bool clear_all_visible = false;
2014 bool vmbuffer_modified = false;
2015
2016 /* Cheap, simplistic check that the tuple matches the rel's rowtype. */
2019
2020 AssertHasSnapshotForToast(relation);
2021
2022 /*
2023 * Fill in tuple header fields and toast the tuple if necessary.
2024 *
2025 * Note: below this point, heaptup is the data we actually intend to store
2026 * into the relation; tup is the caller's original untoasted data.
2027 */
2028 heaptup = heap_prepare_insert(relation, tup, xid, cid, options);
2029
2030 /*
2031 * Find buffer to insert this tuple into. If the page is all visible,
2032 * this will also pin the requisite visibility map page.
2033 */
2034 buffer = RelationGetBufferForTuple(relation, heaptup->t_len,
2035 InvalidBuffer, options, bistate,
2036 &vmbuffer, NULL,
2037 0);
2038
2039 page = BufferGetPage(buffer);
2040
2041 /*
2042 * We're about to do the actual insert -- but check for conflict first, to
2043 * avoid possibly having to roll back work we've just done.
2044 *
2045 * This is safe without a recheck as long as there is no possibility of
2046 * another process scanning the page between this check and the insert
2047 * being visible to the scan (i.e., an exclusive buffer content lock is
2048 * continuously held from this point until the tuple insert is visible).
2049 *
2050 * For a heap insert, we only need to check for table-level SSI locks. Our
2051 * new tuple can't possibly conflict with existing tuple locks, and heap
2052 * page locks are only consolidated versions of tuple locks; they do not
2053 * lock "gaps" as index page locks do. So we don't need to specify a
2054 * buffer when making the call, which makes for a faster check.
2055 */
2057
2058 /* Lock the vmbuffer before the critical section */
2059 if (PageIsAllVisible(page))
2060 {
2062 clear_all_visible = true;
2063 }
2064
2065 /* NO EREPORT(ERROR) from here till changes are logged */
2067
2068 RelationPutHeapTuple(relation, buffer, heaptup,
2070
2072 {
2073 /* It's possible the VM bits were already clear */
2074 if (visibilitymap_clear(relation->rd_locator,
2076 vmbuffer, VISIBILITYMAP_VALID_BITS))
2077 vmbuffer_modified = true;
2078
2079 PageClearAllVisible(page);
2080 }
2081
2082 /*
2083 * Set pd_prune_xid to trigger heap_page_prune_and_freeze() once the page
2084 * is full so that we can set the page all-visible in the VM on the next
2085 * page access.
2086 *
2087 * Setting pd_prune_xid is also handy if the inserting transaction
2088 * eventually aborts making this tuple DEAD and hence available for
2089 * pruning. If no other tuple in this page is UPDATEd/DELETEd, the aborted
2090 * tuple would never otherwise be pruned until next vacuum is triggered.
2091 *
2092 * Don't set it if we are in bootstrap mode or we are inserting a frozen
2093 * tuple, as there is no further pruning/freezing needed in those cases.
2094 */
2096 PageSetPrunable(page, xid);
2097
2098 MarkBufferDirty(buffer);
2099
2100 /* XLOG stuff */
2101 if (RelationNeedsWAL(relation))
2102 {
2106 uint8 info = XLOG_HEAP_INSERT;
2107 int bufflags = 0;
2108
2109 /*
2110 * If this is a catalog, we need to transmit combo CIDs to properly
2111 * decode, so log that as well.
2112 */
2114 log_heap_new_cid(relation, heaptup);
2115
2116 /*
2117 * If this is the single and first tuple on page, we can reinit the
2118 * page instead of restoring the whole thing. Set flag, and hide
2119 * buffer references from XLogInsert.
2120 */
2123 {
2124 info |= XLOG_HEAP_INIT_PAGE;
2126 }
2127
2128 xlrec.offnum = ItemPointerGetOffsetNumber(&heaptup->t_self);
2129 xlrec.flags = 0;
2135
2136 /*
2137 * For logical decoding, we need the tuple even if we're doing a full
2138 * page write, so make sure it's included even if we take a full-page
2139 * image. (XXX We could alternatively store a pointer into the FPW).
2140 */
2141 if (RelationIsLogicallyLogged(relation) &&
2143 {
2146
2147 if (IsToastRelation(relation))
2149 }
2150
2153
2154 xlhdr.t_infomask2 = heaptup->t_data->t_infomask2;
2155 xlhdr.t_infomask = heaptup->t_data->t_infomask;
2156 xlhdr.t_hoff = heaptup->t_data->t_hoff;
2157
2158 /*
2159 * note we mark xlhdr as belonging to buffer; if XLogInsert decides to
2160 * write the whole page to the xlog, we don't need to store
2161 * xl_heap_header in the xlog.
2162 */
2167 /* PG73FORMAT: write bitmap [+ padding] [+ oid] + data */
2169 (char *) heaptup->t_data + SizeofHeapTupleHeader,
2171
2172 /* filtering by origin on a row level is much more efficient */
2174
2177
2178 recptr = XLogInsert(RM_HEAP_ID, info);
2179
2180 PageSetLSN(page, recptr);
2181
2183 PageSetLSN(BufferGetPage(vmbuffer), recptr);
2184 }
2185
2187
2188 UnlockReleaseBuffer(buffer);
2189
2190 /*
2191 * We locked vmbuffer if clear_all_visible was true regardless of whether
2192 * or not we ended up modifying the vmbuffer.
2193 */
2195 LockBuffer(vmbuffer, BUFFER_LOCK_UNLOCK);
2196 if (BufferIsValid(vmbuffer))
2197 ReleaseBuffer(vmbuffer);
2198
2199 /*
2200 * If tuple is cacheable, mark it for invalidation from the caches in case
2201 * we abort. Note it is OK to do this after releasing the buffer, because
2202 * the heaptup data structure is all in local memory, not in the shared
2203 * buffer.
2204 */
2206
2207 /* Note: speculative insertions are counted too, even if aborted later */
2208 pgstat_count_heap_insert(relation, 1);
2209
2210 /*
2211 * If heaptup is a private copy, release it. Don't forget to copy t_self
2212 * back to the caller's image, too.
2213 */
2214 if (heaptup != tup)
2215 {
2216 tup->t_self = heaptup->t_self;
2218 }
2219}
uint8_t uint8
Definition c.h:681
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid, CommandId cid, uint32 options)
Definition heapam.c:2228
#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 HEAP_INSERT_BLKREF_VM
#define XLH_INSERT_ALL_VISIBLE_CLEARED
Definition heapam_xlog.h:72
#define HEAP_INSERT_BLKREF_HEAP
#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:721
#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(), BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage(), BufferIsValid(), CacheInvalidateHeapTuple(), CheckForSerializableConflictIn(), END_CRIT_SECTION, fb(), FirstOffsetNumber, GetCurrentTransactionId(), heap_freetuple(), HEAP_INSERT_BLKREF_HEAP, HEAP_INSERT_BLKREF_VM, HEAP_INSERT_FROZEN, HEAP_INSERT_NO_LOGICAL, HEAP_INSERT_SPECULATIVE, heap_prepare_insert(), HeapTupleHeaderGetNatts, InvalidBlockNumber, InvalidBuffer, IsToastRelation(), ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), LockBuffer(), log_heap_new_cid(), MarkBufferDirty(), PageClearAllVisible(), PageGetMaxOffsetNumber(), PageIsAllVisible(), PageSetLSN(), PageSetPrunable, pgstat_count_heap_insert(), RelationData::rd_locator, 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 4728 of file heapam.c.

4732{
4734 ItemPointer tid = &(tuple->t_self);
4735 ItemId lp;
4736 Page page;
4737 Buffer vmbuffer = InvalidBuffer;
4738 bool unlock_vmbuffer = false;
4739 BlockNumber block;
4740 TransactionId xid,
4741 xmax;
4745 bool first_time = true;
4746 bool skip_tuple_lock = false;
4747 bool have_tuple_lock = false;
4748 bool cleared_all_frozen = false;
4749
4750 *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
4751 block = ItemPointerGetBlockNumber(tid);
4752 page = BufferGetPage(*buffer);
4753
4754 /*
4755 * Before locking the buffer, pin the visibility map page if it appears to
4756 * be necessary. Since we haven't got the lock yet, someone else might be
4757 * in the middle of changing this, so we'll need to recheck after we have
4758 * the lock.
4759 */
4760 if (PageIsAllVisible(page))
4761 visibilitymap_pin(relation, block, &vmbuffer);
4762
4764
4767
4768 tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
4769 tuple->t_len = ItemIdGetLength(lp);
4770 tuple->t_tableOid = RelationGetRelid(relation);
4771
4772l3:
4773 result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
4774
4775 if (result == TM_Invisible)
4776 {
4777 /*
4778 * This is possible, but only when locking a tuple for ON CONFLICT DO
4779 * SELECT/UPDATE. We return this value here rather than throwing an
4780 * error in order to give that case the opportunity to throw a more
4781 * specific error.
4782 */
4784 goto out_locked;
4785 }
4786 else if (result == TM_BeingModified ||
4787 result == TM_Updated ||
4788 result == TM_Deleted)
4789 {
4793 bool require_sleep;
4794 ItemPointerData t_ctid;
4795
4796 /* must copy state data before unlocking buffer */
4798 infomask = tuple->t_data->t_infomask;
4799 infomask2 = tuple->t_data->t_infomask2;
4800 ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
4801
4803
4804 /*
4805 * If any subtransaction of the current top transaction already holds
4806 * a lock as strong as or stronger than what we're requesting, we
4807 * effectively hold the desired lock already. We *must* succeed
4808 * without trying to take the tuple lock, else we will deadlock
4809 * against anyone wanting to acquire a stronger lock.
4810 *
4811 * Note we only do this the first time we loop on the HTSU result;
4812 * there is no point in testing in subsequent passes, because
4813 * evidently our own transaction cannot have acquired a new lock after
4814 * the first time we checked.
4815 */
4816 if (first_time)
4817 {
4818 first_time = false;
4819
4821 {
4822 int i;
4823 int nmembers;
4824 MultiXactMember *members;
4825
4826 /*
4827 * We don't need to allow old multixacts here; if that had
4828 * been the case, HeapTupleSatisfiesUpdate would have returned
4829 * MayBeUpdated and we wouldn't be here.
4830 */
4831 nmembers =
4832 GetMultiXactIdMembers(xwait, &members, false,
4834
4835 for (i = 0; i < nmembers; i++)
4836 {
4837 /* only consider members of our own transaction */
4838 if (!TransactionIdIsCurrentTransactionId(members[i].xid))
4839 continue;
4840
4841 if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
4842 {
4843 pfree(members);
4844 result = TM_Ok;
4845 goto out_unlocked;
4846 }
4847 else
4848 {
4849 /*
4850 * Disable acquisition of the heavyweight tuple lock.
4851 * Otherwise, when promoting a weaker lock, we might
4852 * deadlock with another locker that has acquired the
4853 * heavyweight tuple lock and is waiting for our
4854 * transaction to finish.
4855 *
4856 * Note that in this case we still need to wait for
4857 * the multixact if required, to avoid acquiring
4858 * conflicting locks.
4859 */
4860 skip_tuple_lock = true;
4861 }
4862 }
4863
4864 if (members)
4865 pfree(members);
4866 }
4868 {
4869 switch (mode)
4870 {
4871 case LockTupleKeyShare:
4875 result = TM_Ok;
4876 goto out_unlocked;
4877 case LockTupleShare:
4880 {
4881 result = TM_Ok;
4882 goto out_unlocked;
4883 }
4884 break;
4887 {
4888 result = TM_Ok;
4889 goto out_unlocked;
4890 }
4891 break;
4892 case LockTupleExclusive:
4895 {
4896 result = TM_Ok;
4897 goto out_unlocked;
4898 }
4899 break;
4900 }
4901 }
4902 }
4903
4904 /*
4905 * Initially assume that we will have to wait for the locking
4906 * transaction(s) to finish. We check various cases below in which
4907 * this can be turned off.
4908 */
4909 require_sleep = true;
4910 if (mode == LockTupleKeyShare)
4911 {
4912 /*
4913 * If we're requesting KeyShare, and there's no update present, we
4914 * don't need to wait. Even if there is an update, we can still
4915 * continue if the key hasn't been modified.
4916 *
4917 * However, if there are updates, we need to walk the update chain
4918 * to mark future versions of the row as locked, too. That way,
4919 * if somebody deletes that future version, we're protected
4920 * against the key going away. This locking of future versions
4921 * could block momentarily, if a concurrent transaction is
4922 * deleting a key; or it could return a value to the effect that
4923 * the transaction deleting the key has already committed. So we
4924 * do this before re-locking the buffer; otherwise this would be
4925 * prone to deadlocks.
4926 *
4927 * Note that the TID we're locking was grabbed before we unlocked
4928 * the buffer. For it to change while we're not looking, the
4929 * other properties we're testing for below after re-locking the
4930 * buffer would also change, in which case we would restart this
4931 * loop above.
4932 */
4934 {
4935 bool updated;
4936
4938
4939 /*
4940 * If there are updates, follow the update chain; bail out if
4941 * that cannot be done.
4942 */
4943 if (follow_updates && updated &&
4944 !ItemPointerEquals(&tuple->t_self, &t_ctid))
4945 {
4946 TM_Result res;
4947
4948 res = heap_lock_updated_tuple(relation,
4949 infomask, xwait, &t_ctid,
4951 mode);
4952 if (res != TM_Ok)
4953 {
4954 result = res;
4955 /* recovery code expects to have buffer lock held */
4957 goto failed;
4958 }
4959 }
4960
4962
4963 /*
4964 * Make sure it's still an appropriate lock, else start over.
4965 * Also, if it wasn't updated before we released the lock, but
4966 * is updated now, we start over too; the reason is that we
4967 * now need to follow the update chain to lock the new
4968 * versions.
4969 */
4970 if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
4971 ((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
4972 !updated))
4973 goto l3;
4974
4975 /* Things look okay, so we can skip sleeping */
4976 require_sleep = false;
4977
4978 /*
4979 * Note we allow Xmax to change here; other updaters/lockers
4980 * could have modified it before we grabbed the buffer lock.
4981 * However, this is not a problem, because with the recheck we
4982 * just did we ensure that they still don't conflict with the
4983 * lock we want.
4984 */
4985 }
4986 }
4987 else if (mode == LockTupleShare)
4988 {
4989 /*
4990 * If we're requesting Share, we can similarly avoid sleeping if
4991 * there's no update and no exclusive lock present.
4992 */
4995 {
4997
4998 /*
4999 * Make sure it's still an appropriate lock, else start over.
5000 * See above about allowing xmax to change.
5001 */
5004 goto l3;
5005 require_sleep = false;
5006 }
5007 }
5008 else if (mode == LockTupleNoKeyExclusive)
5009 {
5010 /*
5011 * If we're requesting NoKeyExclusive, we might also be able to
5012 * avoid sleeping; just ensure that there no conflicting lock
5013 * already acquired.
5014 */
5016 {
5018 mode, NULL))
5019 {
5020 /*
5021 * No conflict, but if the xmax changed under us in the
5022 * meantime, start over.
5023 */
5027 xwait))
5028 goto l3;
5029
5030 /* otherwise, we're good */
5031 require_sleep = false;
5032 }
5033 }
5035 {
5037
5038 /* if the xmax changed in the meantime, start over */
5041 xwait))
5042 goto l3;
5043 /* otherwise, we're good */
5044 require_sleep = false;
5045 }
5046 }
5047
5048 /*
5049 * As a check independent from those above, we can also avoid sleeping
5050 * if the current transaction is the sole locker of the tuple. Note
5051 * that the strength of the lock already held is irrelevant; this is
5052 * not about recording the lock in Xmax (which will be done regardless
5053 * of this optimization, below). Also, note that the cases where we
5054 * hold a lock stronger than we are requesting are already handled
5055 * above by not doing anything.
5056 *
5057 * Note we only deal with the non-multixact case here; MultiXactIdWait
5058 * is well equipped to deal with this situation on its own.
5059 */
5062 {
5063 /* ... but if the xmax changed in the meantime, start over */
5067 xwait))
5068 goto l3;
5070 require_sleep = false;
5071 }
5072
5073 /*
5074 * Time to sleep on the other transaction/multixact, if necessary.
5075 *
5076 * If the other transaction is an update/delete that's already
5077 * committed, then sleeping cannot possibly do any good: if we're
5078 * required to sleep, get out to raise an error instead.
5079 *
5080 * By here, we either have already acquired the buffer exclusive lock,
5081 * or we must wait for the locking transaction or multixact; so below
5082 * we ensure that we grab buffer lock after the sleep.
5083 */
5085 {
5087 goto failed;
5088 }
5089 else if (require_sleep)
5090 {
5091 /*
5092 * Acquire tuple lock to establish our priority for the tuple, or
5093 * die trying. LockTuple will release us when we are next-in-line
5094 * for the tuple. We must do this even if we are share-locking,
5095 * but not if we already have a weaker lock on the tuple.
5096 *
5097 * If we are forced to "start over" below, we keep the tuple lock;
5098 * this arranges that we stay at the head of the line while
5099 * rechecking tuple state.
5100 */
5101 if (!skip_tuple_lock &&
5102 !heap_acquire_tuplock(relation, tid, mode, wait_policy,
5104 {
5105 /*
5106 * This can only happen if wait_policy is Skip and the lock
5107 * couldn't be obtained.
5108 */
5110 /* recovery code expects to have buffer lock held */
5112 goto failed;
5113 }
5114
5116 {
5118
5119 /* We only ever lock tuples, never update them */
5120 if (status >= MultiXactStatusNoKeyUpdate)
5121 elog(ERROR, "invalid lock mode in heap_lock_tuple");
5122
5123 /* wait for multixact to end, or die trying */
5124 switch (wait_policy)
5125 {
5126 case LockWaitBlock:
5128 relation, &tuple->t_self, XLTW_Lock, NULL);
5129 break;
5130 case LockWaitSkip:
5132 status, infomask, relation,
5133 NULL, false))
5134 {
5136 /* recovery code expects to have buffer lock held */
5138 goto failed;
5139 }
5140 break;
5141 case LockWaitError:
5143 status, infomask, relation,
5145 ereport(ERROR,
5147 errmsg("could not obtain lock on row in relation \"%s\"",
5148 RelationGetRelationName(relation))));
5149
5150 break;
5151 }
5152
5153 /*
5154 * Of course, the multixact might not be done here: if we're
5155 * requesting a light lock mode, other transactions with light
5156 * locks could still be alive, as well as locks owned by our
5157 * own xact or other subxacts of this backend. We need to
5158 * preserve the surviving MultiXact members. Note that it
5159 * isn't absolutely necessary in the latter case, but doing so
5160 * is simpler.
5161 */
5162 }
5163 else
5164 {
5165 /* wait for regular transaction to end, or die trying */
5166 switch (wait_policy)
5167 {
5168 case LockWaitBlock:
5169 XactLockTableWait(xwait, relation, &tuple->t_self,
5170 XLTW_Lock);
5171 break;
5172 case LockWaitSkip:
5174 {
5176 /* recovery code expects to have buffer lock held */
5178 goto failed;
5179 }
5180 break;
5181 case LockWaitError:
5183 ereport(ERROR,
5185 errmsg("could not obtain lock on row in relation \"%s\"",
5186 RelationGetRelationName(relation))));
5187 break;
5188 }
5189 }
5190
5191 /* if there are updates, follow the update chain */
5193 !ItemPointerEquals(&tuple->t_self, &t_ctid))
5194 {
5195 TM_Result res;
5196
5197 res = heap_lock_updated_tuple(relation,
5198 infomask, xwait, &t_ctid,
5200 mode);
5201 if (res != TM_Ok)
5202 {
5203 result = res;
5204 /* recovery code expects to have buffer lock held */
5206 goto failed;
5207 }
5208 }
5209
5211
5212 /*
5213 * xwait is done, but if xwait had just locked the tuple then some
5214 * other xact could update this tuple before we get to this point.
5215 * Check for xmax change, and start over if so.
5216 */
5219 xwait))
5220 goto l3;
5221
5223 {
5224 /*
5225 * Otherwise check if it committed or aborted. Note we cannot
5226 * be here if the tuple was only locked by somebody who didn't
5227 * conflict with us; that would have been handled above. So
5228 * that transaction must necessarily be gone by now. But
5229 * don't check for this in the multixact case, because some
5230 * locker transactions might still be running.
5231 */
5232 UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
5233 }
5234 }
5235
5236 /* By here, we're certain that we hold buffer exclusive lock again */
5237
5238 /*
5239 * We may lock if previous xmax aborted, or if it committed but only
5240 * locked the tuple without updating it; or if we didn't have to wait
5241 * at all for whatever reason.
5242 */
5243 if (!require_sleep ||
5244 (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
5247 result = TM_Ok;
5248 else if (!ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid))
5250 else
5252 }
5253
5254failed:
5255 if (result != TM_Ok)
5256 {
5259
5260 /*
5261 * When locking a tuple under LockWaitSkip semantics and we fail with
5262 * TM_WouldBlock above, it's possible for concurrent transactions to
5263 * release the lock and set HEAP_XMAX_INVALID in the meantime. So
5264 * this assert is slightly different from the equivalent one in
5265 * heap_delete and heap_update.
5266 */
5268 !(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
5270 !ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid));
5271 tmfd->ctid = tuple->t_data->t_ctid;
5272 tmfd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
5273 if (result == TM_SelfModified)
5274 tmfd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
5275 else
5276 tmfd->cmax = InvalidCommandId;
5277 goto out_locked;
5278 }
5279
5280 /*
5281 * If we didn't pin the visibility map page and the page has become all
5282 * visible while we were busy locking the buffer, or during some
5283 * subsequent window during which we had it unlocked, we'll have to unlock
5284 * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
5285 * unfortunate, especially since we'll now have to recheck whether the
5286 * tuple has been locked or updated under us, but hopefully it won't
5287 * happen very often.
5288 */
5289 if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
5290 {
5292 visibilitymap_pin(relation, block, &vmbuffer);
5294 goto l3;
5295 }
5296
5297 xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
5298 old_infomask = tuple->t_data->t_infomask;
5299
5300 /*
5301 * If this is the first possibly-multixact-able operation in the current
5302 * transaction, set my per-backend OldestMemberMXactId setting. We can be
5303 * certain that the transaction will never become a member of any older
5304 * MultiXactIds than that. (We have to do this even if we end up just
5305 * using our own TransactionId below, since some other backend could
5306 * incorporate our XID into a MultiXact immediately afterwards.)
5307 */
5309
5310 /*
5311 * Compute the new xmax and infomask to store into the tuple. Note we do
5312 * not modify the tuple just yet, because that would leave it in the wrong
5313 * state if multixact.c elogs.
5314 */
5316 GetCurrentTransactionId(), mode, false,
5317 &xid, &new_infomask, &new_infomask2);
5318
5319 /* Lock VM buffer before entering critical section */
5320 if (PageIsAllVisible(page))
5321 {
5323 unlock_vmbuffer = true;
5324 }
5325
5327
5328 /*
5329 * Store transaction information of xact locking the tuple.
5330 *
5331 * Note: Cmax is meaningless in this context, so don't set it; this avoids
5332 * possibly generating a useless combo CID. Moreover, if we're locking a
5333 * previously updated tuple, it's important to preserve the Cmax.
5334 *
5335 * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
5336 * we would break the HOT chain.
5337 */
5340 tuple->t_data->t_infomask |= new_infomask;
5341 tuple->t_data->t_infomask2 |= new_infomask2;
5344 HeapTupleHeaderSetXmax(tuple->t_data, xid);
5345
5346 /*
5347 * Make sure there is no forward chain link in t_ctid. Note that in the
5348 * cases where the tuple has been updated, we must not overwrite t_ctid,
5349 * because it was set by the updater. Moreover, if the tuple has been
5350 * updated, we need to follow the update chain to lock the new versions of
5351 * the tuple as well.
5352 */
5354 tuple->t_data->t_ctid = *tid;
5355
5356 /* Clear only the all-frozen bit on visibility map if needed */
5357 if (PageIsAllVisible(page))
5358 {
5359 if (visibilitymap_clear(relation->rd_locator, block, vmbuffer,
5361 cleared_all_frozen = true;
5362 }
5363
5364
5365 MarkBufferDirty(*buffer);
5366
5367 /*
5368 * XLOG stuff. You might think that we don't need an XLOG record because
5369 * there is no state change worth restoring after a crash. You would be
5370 * wrong however: we have just written either a TransactionId or a
5371 * MultiXactId that may never have been seen on disk before, and we need
5372 * to make sure that there are XLOG entries covering those ID numbers.
5373 * Else the same IDs might be re-used after a crash, which would be
5374 * disastrous if this page made it to disk before the crash. Essentially
5375 * we have to enforce the WAL log-before-data rule even in this case.
5376 * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
5377 * entries for everything anyway.)
5378 */
5379 if (RelationNeedsWAL(relation))
5380 {
5383
5386
5387 xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
5388 xlrec.xmax = xid;
5389 xlrec.infobits_set = compute_infobits(new_infomask,
5390 tuple->t_data->t_infomask2);
5393
5396
5397 /* we don't decode row locks atm, so no need to log the origin */
5398
5400
5401 PageSetLSN(page, recptr);
5402
5404 PageSetLSN(BufferGetPage(vmbuffer), recptr);
5405 }
5406
5408
5409 /* release VM lock first, since it covers many heap blocks */
5410 if (unlock_vmbuffer)
5411 {
5412 LockBuffer(vmbuffer, BUFFER_LOCK_UNLOCK);
5413 unlock_vmbuffer = false;
5414 }
5415
5416 result = TM_Ok;
5417
5421
5423 if (BufferIsValid(vmbuffer))
5424 ReleaseBuffer(vmbuffer);
5425
5426 /*
5427 * Don't update the visibility map here. Locking a tuple doesn't change
5428 * visibility info.
5429 */
5430
5431 /*
5432 * Now that we have successfully marked the tuple as locked, we can
5433 * release the lmgr tuple lock, if we had it.
5434 */
5435 if (have_tuple_lock)
5436 UnlockTupleTuplock(relation, tid, mode);
5437
5438 return result;
5439}
#define TUPLOCK_from_mxstatus(status)
Definition heapam.c:221
static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, int *remaining, bool logLockFailure)
Definition heapam.c:8016
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:6250
static MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
Definition heapam.c:4681
#define XLH_LOCK_ALL_FROZEN_CLEARED
#define HEAP_LOCK_BLKREF_VM
#define HEAP_LOCK_BLKREF_HEAP
#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_BLKREF_HEAP, HEAP_LOCK_BLKREF_VM, 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(), RelationData::rd_locator, 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 2308 of file heapam.c.

2310{
2313 int i;
2314 int ndone;
2316 Page page;
2317 Buffer vmbuffer = InvalidBuffer;
2318 bool needwal;
2322 bool starting_with_empty_page = false;
2323 int npages = 0;
2324 int npages_used = 0;
2325
2326 /* currently not needed (thus unsupported) for heap_multi_insert() */
2328
2329 AssertHasSnapshotForToast(relation);
2330
2331 needwal = RelationNeedsWAL(relation);
2334
2335 /* Toast and set header data in all the slots */
2336 heaptuples = palloc(ntuples * sizeof(HeapTuple));
2337 for (i = 0; i < ntuples; i++)
2338 {
2339 HeapTuple tuple;
2340
2341 tuple = ExecFetchSlotHeapTuple(slots[i], true, NULL);
2342 slots[i]->tts_tableOid = RelationGetRelid(relation);
2343 tuple->t_tableOid = slots[i]->tts_tableOid;
2344 heaptuples[i] = heap_prepare_insert(relation, tuple, xid, cid,
2345 options);
2346 }
2347
2348 /*
2349 * We're about to do the actual inserts -- but check for conflict first,
2350 * to minimize the possibility of having to roll back work we've just
2351 * done.
2352 *
2353 * A check here does not definitively prevent a serialization anomaly;
2354 * that check MUST be done at least past the point of acquiring an
2355 * exclusive buffer content lock on every buffer that will be affected,
2356 * and MAY be done after all inserts are reflected in the buffers and
2357 * those locks are released; otherwise there is a race condition. Since
2358 * multiple buffers can be locked and unlocked in the loop below, and it
2359 * would not be feasible to identify and lock all of those buffers before
2360 * the loop, we must do a final check at the end.
2361 *
2362 * The check here could be omitted with no loss of correctness; it is
2363 * present strictly as an optimization.
2364 *
2365 * For heap inserts, we only need to check for table-level SSI locks. Our
2366 * new tuples can't possibly conflict with existing tuple locks, and heap
2367 * page locks are only consolidated versions of tuple locks; they do not
2368 * lock "gaps" as index page locks do. So we don't need to specify a
2369 * buffer when making the call, which makes for a faster check.
2370 */
2372
2373 ndone = 0;
2374 while (ndone < ntuples)
2375 {
2376 Buffer buffer;
2377 bool clear_all_visible = false;
2378 bool all_frozen_set = false;
2379 bool vmbuffer_modified = false;
2380 int nthispage;
2381
2383
2384 /*
2385 * Compute number of pages needed to fit the to-be-inserted tuples in
2386 * the worst case. This will be used to determine how much to extend
2387 * the relation by in RelationGetBufferForTuple(), if needed. If we
2388 * filled a prior page from scratch, we can just update our last
2389 * computation, but if we started with a partially filled page,
2390 * recompute from scratch, the number of potentially required pages
2391 * can vary due to tuples needing to fit onto the page, page headers
2392 * etc.
2393 */
2394 if (ndone == 0 || !starting_with_empty_page)
2395 {
2396 npages = heap_multi_insert_pages(heaptuples, ndone, ntuples,
2398 npages_used = 0;
2399 }
2400 else
2401 npages_used++;
2402
2403 /*
2404 * Find buffer where at least the next tuple will fit. If the page is
2405 * all-visible, this will also pin the requisite visibility map page.
2406 *
2407 * Also pin visibility map page if COPY FREEZE inserts tuples into an
2408 * empty page. See all_frozen_set below.
2409 */
2410 buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
2411 InvalidBuffer, options, bistate,
2412 &vmbuffer, NULL,
2413 npages - npages_used);
2414 page = BufferGetPage(buffer);
2415
2417
2419 {
2420 all_frozen_set = true;
2421 /* Lock the vmbuffer before entering the critical section */
2423 }
2424 else if (PageIsAllVisible(page) && !(options & HEAP_INSERT_FROZEN))
2425 {
2426 clear_all_visible = true;
2427 /* Lock the vmbuffer before entering the critical section */
2429 }
2430
2431 /* NO EREPORT(ERROR) from here till changes are logged */
2433
2434 /*
2435 * RelationGetBufferForTuple has ensured that the first tuple fits.
2436 * Put that on the page, and then as many other tuples as fit.
2437 */
2438 RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
2439
2440 /*
2441 * For logical decoding we need combo CIDs to properly decode the
2442 * catalog.
2443 */
2444 if (needwal && need_cids)
2445 log_heap_new_cid(relation, heaptuples[ndone]);
2446
2447 for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
2448 {
2450
2451 if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
2452 break;
2453
2454 RelationPutHeapTuple(relation, buffer, heaptup, false);
2455
2456 /*
2457 * For logical decoding we need combo CIDs to properly decode the
2458 * catalog.
2459 */
2460 if (needwal && need_cids)
2461 log_heap_new_cid(relation, heaptup);
2462 }
2463
2464 /*
2465 * If the page is all visible, need to clear that, unless we're only
2466 * going to add further frozen rows to it.
2467 *
2468 * If we're only adding already frozen rows to a previously empty
2469 * page, mark it as all-frozen and update the visibility map. We're
2470 * already holding a pin on the vmbuffer.
2471 */
2473 {
2475 /* It's possible the VM bits were already clear */
2476 if (visibilitymap_clear(relation->rd_locator,
2477 BufferGetBlockNumber(buffer),
2478 vmbuffer, VISIBILITYMAP_VALID_BITS))
2479 vmbuffer_modified = true;
2480
2481 PageClearAllVisible(page);
2482 }
2483 else if (all_frozen_set)
2484 {
2485 PageSetAllVisible(page);
2486 PageClearPrunable(page);
2488 vmbuffer,
2491 relation->rd_locator);
2492 }
2493
2494 /*
2495 * Set pd_prune_xid. See heap_insert() for more on why we do this when
2496 * inserting tuples. This only makes sense if we aren't already
2497 * setting the page frozen in the VM and we're not in bootstrap mode.
2498 */
2500 PageSetPrunable(page, xid);
2501
2502 MarkBufferDirty(buffer);
2503
2504 /* XLOG stuff */
2505 if (needwal)
2506 {
2510 char *tupledata;
2511 int totaldatalen;
2512 char *scratchptr = scratch.data;
2513 bool init;
2514 int bufflags = 0;
2515
2516 /*
2517 * If the page was previously empty, we can reinit the page
2518 * instead of restoring the whole thing.
2519 */
2521
2522 /* allocate xl_heap_multi_insert struct from the scratch area */
2525
2526 /*
2527 * Allocate offsets array. Unless we're reinitializing the page,
2528 * in that case the tuples are stored in order starting at
2529 * FirstOffsetNumber and we don't need to store the offsets
2530 * explicitly.
2531 */
2532 if (!init)
2533 scratchptr += nthispage * sizeof(OffsetNumber);
2534
2535 /* the rest of the scratch space is used for tuple data */
2536 tupledata = scratchptr;
2537
2538 /* check that the mutually exclusive flags are not both set */
2540
2541 xlrec->flags = 0;
2544
2545 /*
2546 * We don't have to worry about including a conflict xid in the
2547 * WAL record, as HEAP_INSERT_FROZEN intentionally violates
2548 * visibility rules.
2549 */
2550 if (all_frozen_set)
2552
2553 xlrec->ntuples = nthispage;
2554
2555 /*
2556 * Write out an xl_multi_insert_tuple and the tuple data itself
2557 * for each tuple.
2558 */
2559 for (i = 0; i < nthispage; i++)
2560 {
2562 xl_multi_insert_tuple *tuphdr;
2563 int datalen;
2564
2565 if (!init)
2566 xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
2567 /* xl_multi_insert_tuple needs two-byte alignment. */
2569 scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
2570
2571 tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
2572 tuphdr->t_infomask = heaptup->t_data->t_infomask;
2573 tuphdr->t_hoff = heaptup->t_data->t_hoff;
2574
2575 /* write bitmap [+ padding] [+ oid] + data */
2576 datalen = heaptup->t_len - SizeofHeapTupleHeader;
2578 (char *) heaptup->t_data + SizeofHeapTupleHeader,
2579 datalen);
2580 tuphdr->datalen = datalen;
2581 scratchptr += datalen;
2582 }
2583 totaldatalen = scratchptr - tupledata;
2584 Assert((scratchptr - scratch.data) < BLCKSZ);
2585
2586 if (need_tuple_data)
2588
2589 /*
2590 * Signal that this is the last xl_heap_multi_insert record
2591 * emitted by this call to heap_multi_insert(). Needed for logical
2592 * decoding so it knows when to cleanup temporary data.
2593 */
2594 if (ndone + nthispage == ntuples)
2596
2597 if (init)
2598 {
2599 info |= XLOG_HEAP_INIT_PAGE;
2601 }
2602
2603 /*
2604 * If we're doing logical decoding, include the new tuple data
2605 * even if we take a full-page image of the page.
2606 */
2607 if (need_tuple_data)
2609
2611 XLogRegisterData(xlrec, tupledata - scratch.data);
2616
2618 totaldatalen);
2619
2620 /* filtering by origin on a row level is much more efficient */
2622
2623 recptr = XLogInsert(RM_HEAP2_ID, info);
2624
2625 PageSetLSN(page, recptr);
2627 {
2628 Assert(BufferIsDirty(vmbuffer));
2629 PageSetLSN(BufferGetPage(vmbuffer), recptr);
2630 }
2631 }
2632
2634
2635 /*
2636 * We locked vmbuffer if clear_all_visible was true regardless of
2637 * whether or not we ended up modifying the vmbuffer.
2638 */
2640 LockBuffer(vmbuffer, BUFFER_LOCK_UNLOCK);
2641
2642 UnlockReleaseBuffer(buffer);
2643 ndone += nthispage;
2644
2645 /*
2646 * NB: Only release vmbuffer after inserting all tuples - it's fairly
2647 * likely that we'll insert into subsequent heap pages that are likely
2648 * to use the same vm page.
2649 */
2650 }
2651
2652 /* We're done with inserting all tuples, so release the last vmbuffer. */
2653 if (vmbuffer != InvalidBuffer)
2654 ReleaseBuffer(vmbuffer);
2655
2656 /*
2657 * We're done with the actual inserts. Check for conflicts again, to
2658 * ensure that all rw-conflicts in to these inserts are detected. Without
2659 * this final check, a sequential scan of the heap may have locked the
2660 * table after the "before" check, missing one opportunity to detect the
2661 * conflict, and then scanned the table before the new tuples were there,
2662 * missing the other chance to detect the conflict.
2663 *
2664 * For heap inserts, we only need to check for table-level SSI locks. Our
2665 * new tuples can't possibly conflict with existing tuple locks, and heap
2666 * page locks are only consolidated versions of tuple locks; they do not
2667 * lock "gaps" as index page locks do. So we don't need to specify a
2668 * buffer when making the call.
2669 */
2671
2672 /*
2673 * If tuples are cacheable, mark them for invalidation from the caches in
2674 * case we abort. Note it is OK to do this after releasing the buffer,
2675 * because the heaptuples data structure is all in local memory, not in
2676 * the shared buffer.
2677 */
2678 if (IsCatalogRelation(relation))
2679 {
2680 for (i = 0; i < ntuples; i++)
2682 }
2683
2684 /* copy t_self fields back to the caller's slots */
2685 for (i = 0; i < ntuples; i++)
2686 slots[i]->tts_tid = heaptuples[i]->t_self;
2687
2688 pgstat_count_heap_insert(relation, ntuples);
2689}
bool BufferIsDirty(Buffer buffer)
Definition bufmgr.c:3137
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:955
#define SHORTALIGN(LEN)
Definition c.h:951
size_t Size
Definition c.h:748
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:2276
#define SizeOfHeapMultiInsert
#define XLOG_HEAP2_MULTI_INSERT
Definition heapam_xlog.h:64
#define HEAP_MULTI_INSERT_BLKREF_HEAP
#define XLH_INSERT_LAST_IN_MULTI
Definition heapam_xlog.h:73
#define XLH_INSERT_ALL_FROZEN_SET
Definition heapam_xlog.h:79
#define SizeOfMultiInsertTuple
#define HEAP_MULTI_INSERT_BLKREF_VM
void * palloc(Size size)
Definition mcxt.c:1390
#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()
void visibilitymap_set(BlockNumber heapBlk, Buffer vmBuf, uint8 flags, 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_BLKREF_HEAP, HEAP_MULTI_INSERT_BLKREF_VM, 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 1112 of file pruneheap.c.

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

2091{
2092 Page page = BufferGetPage(buffer);
2093 OffsetNumber *offnum;
2095
2096 /* Shouldn't be called unless there's something to do */
2097 Assert(nredirected > 0 || ndead > 0 || nunused > 0);
2098
2099 /* If 'lp_truncate_only', we can only remove already-dead line pointers */
2100 Assert(!lp_truncate_only || (nredirected == 0 && ndead == 0));
2101
2102 /* Update all redirected line pointers */
2103 offnum = redirected;
2104 for (int i = 0; i < nredirected; i++)
2105 {
2106 OffsetNumber fromoff = *offnum++;
2107 OffsetNumber tooff = *offnum++;
2110
2111#ifdef USE_ASSERT_CHECKING
2112
2113 /*
2114 * Any existing item that we set as an LP_REDIRECT (any 'from' item)
2115 * must be the first item from a HOT chain. If the item has tuple
2116 * storage then it can't be a heap-only tuple. Otherwise we are just
2117 * maintaining an existing LP_REDIRECT from an existing HOT chain that
2118 * has been pruned at least once before now.
2119 */
2121 {
2123
2124 htup = (HeapTupleHeader) PageGetItem(page, fromlp);
2126 }
2127 else
2128 {
2129 /* We shouldn't need to redundantly set the redirect */
2131 }
2132
2133 /*
2134 * The item that we're about to set as an LP_REDIRECT (the 'from'
2135 * item) will point to an existing item (the 'to' item) that is
2136 * already a heap-only tuple. There can be at most one LP_REDIRECT
2137 * item per HOT chain.
2138 *
2139 * We need to keep around an LP_REDIRECT item (after original
2140 * non-heap-only root tuple gets pruned away) so that it's always
2141 * possible for VACUUM to easily figure out what TID to delete from
2142 * indexes when an entire HOT chain becomes dead. A heap-only tuple
2143 * can never become LP_DEAD; an LP_REDIRECT item or a regular heap
2144 * tuple can.
2145 *
2146 * This check may miss problems, e.g. the target of a redirect could
2147 * be marked as unused subsequently. The page_verify_redirects() check
2148 * below will catch such problems.
2149 */
2150 tolp = PageGetItemId(page, tooff);
2152 htup = (HeapTupleHeader) PageGetItem(page, tolp);
2154#endif
2155
2157 }
2158
2159 /* Update all now-dead line pointers */
2160 offnum = nowdead;
2161 for (int i = 0; i < ndead; i++)
2162 {
2163 OffsetNumber off = *offnum++;
2164 ItemId lp = PageGetItemId(page, off);
2165
2166#ifdef USE_ASSERT_CHECKING
2167
2168 /*
2169 * An LP_DEAD line pointer must be left behind when the original item
2170 * (which is dead to everybody) could still be referenced by a TID in
2171 * an index. This should never be necessary with any individual
2172 * heap-only tuple item, though. (It's not clear how much of a problem
2173 * that would be, but there is no reason to allow it.)
2174 */
2175 if (ItemIdHasStorage(lp))
2176 {
2178 htup = (HeapTupleHeader) PageGetItem(page, lp);
2180 }
2181 else
2182 {
2183 /* Whole HOT chain becomes dead */
2185 }
2186#endif
2187
2189 }
2190
2191 /* Update all now-unused line pointers */
2192 offnum = nowunused;
2193 for (int i = 0; i < nunused; i++)
2194 {
2195 OffsetNumber off = *offnum++;
2196 ItemId lp = PageGetItemId(page, off);
2197
2198#ifdef USE_ASSERT_CHECKING
2199
2200 if (lp_truncate_only)
2201 {
2202 /* Setting LP_DEAD to LP_UNUSED in vacuum's second pass */
2204 }
2205 else
2206 {
2207 /*
2208 * When heap_page_prune_and_freeze() was called, mark_unused_now
2209 * may have been passed as true, which allows would-be LP_DEAD
2210 * items to be made LP_UNUSED instead. This is only possible if
2211 * the relation has no indexes. If there are any dead items, then
2212 * mark_unused_now was not true and every item being marked
2213 * LP_UNUSED must refer to a heap-only tuple.
2214 */
2215 if (ndead > 0)
2216 {
2218 htup = (HeapTupleHeader) PageGetItem(page, lp);
2220 }
2221 else
2223 }
2224
2225#endif
2226
2228 }
2229
2230 if (lp_truncate_only)
2232 else
2233 {
2234 /*
2235 * Finally, repair any fragmentation, and update the page's hint bit
2236 * about whether it has free pointers.
2237 */
2239
2240 /*
2241 * Now that the page has been modified, assert that redirect items
2242 * still point to valid targets.
2243 */
2245 }
2246}
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:308
#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:2263

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

274{
275 Page page = BufferGetPage(buffer);
277 GlobalVisState *vistest;
279
280 /*
281 * We can't write WAL in recovery mode, so there's no point trying to
282 * clean the page. The primary will likely issue a cleaning WAL record
283 * soon anyway, so this is no particular loss.
284 */
285 if (RecoveryInProgress())
286 return;
287
288 /*
289 * First check whether there's any chance there's something to prune,
290 * determining the appropriate horizon is a waste if there's no prune_xid
291 * (i.e. no updates/deletes left potentially dead tuples around and no
292 * inserts inserted new tuples that may be visible to all).
293 */
296 return;
297
298 /*
299 * Check whether prune_xid indicates that there may be dead rows that can
300 * be cleaned up.
301 */
302 vistest = GlobalVisTestFor(relation);
303
304 if (!GlobalVisTestIsRemovableXid(vistest, prune_xid, true))
305 return;
306
307 /*
308 * We prune when a previous UPDATE failed to find enough space on the page
309 * for a new tuple version, or when free space falls below the relation's
310 * fill-factor target (but not less than 10%).
311 *
312 * Checking free space here is questionable since we aren't holding any
313 * lock on the buffer; in the worst case we could get a bogus answer. It's
314 * unlikely to be *seriously* wrong, though, since reading either pd_lower
315 * or pd_upper is probably atomic. Avoiding taking a lock seems more
316 * important than sometimes getting a wrong answer in what is after all
317 * just a heuristic estimate.
318 */
321 minfree = Max(minfree, BLCKSZ / 10);
322
323 if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
324 {
325 bool record_free_space = false;
326 Size freespace = 0;
327
328 /* OK, try to get exclusive buffer lock */
330 return;
331
332 /*
333 * Now that we have buffer lock, get accurate information about the
334 * page's free space, and recheck the heuristic about whether to
335 * prune.
336 */
337 if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
338 {
341 PruneFreezeParams params;
342
343 visibilitymap_pin(relation, BufferGetBlockNumber(buffer),
344 vmbuffer);
345
346 params.relation = relation;
347 params.buffer = buffer;
348 params.vmbuffer = *vmbuffer;
349 params.reason = PRUNE_ON_ACCESS;
350 params.vistest = vistest;
351 params.cutoffs = NULL;
352
353 /*
354 * We don't pass the HEAP_PAGE_PRUNE_MARK_UNUSED_NOW option
355 * regardless of whether or not the relation has indexes, since we
356 * cannot safely determine that during on-access pruning with the
357 * current implementation.
358 */
360 if (rel_read_only)
362
364 NULL, NULL);
365
366 /*
367 * Report the number of tuples reclaimed to pgstats. This is
368 * presult.ndeleted minus the number of newly-LP_DEAD-set items.
369 *
370 * We derive the number of dead tuples like this to avoid totally
371 * forgetting about items that were set to LP_DEAD, since they
372 * still need to be cleaned up by VACUUM. We only want to count
373 * heap-only tuples that just became LP_UNUSED in our report,
374 * which don't.
375 *
376 * VACUUM doesn't have to compensate in the same way when it
377 * tracks ndeleted, since it will set the same LP_DEAD items to
378 * LP_UNUSED separately.
379 */
380 if (presult.ndeleted > presult.nnewlpdead)
382 presult.ndeleted - presult.nnewlpdead);
383
384 /*
385 * If this prune newly set the page all-visible, VACUUM may later
386 * skip the page and not update the free space map (FSM) for it.
387 * Keep the FSM from going stale by recording it now. We do not
388 * want to update the freespace map otherwise, to reserve
389 * freespace on this page for HOT updates.
390 */
391 if (presult.newly_all_visible)
392 {
393 record_free_space = true;
394 freespace = PageGetHeapFreeSpace(page);
395 }
396 }
397
398 /* And release buffer lock */
400
401 /*
402 * RecordPageWithFreeSpace() only dirties the FSM when the recorded
403 * free-space category actually changes. Note that vacuum will still
404 * do FreeSpaceMapVacuum() for ranges of pages that are skipped, so we
405 * don't have to worry about that here.
406 */
408 RecordPageWithFreeSpace(relation, BufferGetBlockNumber(buffer), freespace);
409 }
410}
bool ConditionalLockBufferForCleanup(Buffer buffer)
Definition bufmgr.c:6892
#define Max(x, y)
Definition c.h:1125
void RecordPageWithFreeSpace(Relation rel, BlockNumber heapBlk, Size spaceAvail)
Definition freespace.c:194
#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:4277
void heap_page_prune_and_freeze(PruneFreezeParams *params, PruneFreezeResult *presult, OffsetNumber *off_loc, TransactionId *new_relfrozen_xid, MultiXactId *new_relmin_mxid)
Definition pruneheap.c:1112
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:6835

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, RecordPageWithFreeSpace(), 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 7547 of file heapam.c.

7549{
7550 Page page = BufferGetPage(buffer);
7551
7552 for (int i = 0; i < ntuples; i++)
7553 {
7554 HeapTupleFreeze *frz = tuples + i;
7555 ItemId itemid = PageGetItemId(page, frz->offset);
7556 HeapTupleHeader htup;
7557
7558 htup = (HeapTupleHeader) PageGetItem(page, itemid);
7559
7560 /* Deliberately avoid relying on tuple hint bits here */
7561 if (frz->checkflags & HEAP_FREEZE_CHECK_XMIN_COMMITTED)
7562 {
7564
7566 if (unlikely(!TransactionIdDidCommit(xmin)))
7567 ereport(ERROR,
7569 errmsg_internal("uncommitted xmin %u needs to be frozen",
7570 xmin)));
7571 }
7572
7573 /*
7574 * TransactionIdDidAbort won't work reliably in the presence of XIDs
7575 * left behind by transactions that were in progress during a crash,
7576 * so we can only check that xmax didn't commit
7577 */
7578 if (frz->checkflags & HEAP_FREEZE_CHECK_XMAX_ABORTED)
7579 {
7581
7584 ereport(ERROR,
7586 errmsg_internal("cannot freeze committed xmax %u",
7587 xmax)));
7588 }
7589 }
7590}
#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 619 of file heapam.c.

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

1334{
1336
1337 if (set_params)
1338 {
1339 if (allow_strat)
1341 else
1343
1344 if (allow_sync)
1346 else
1348
1349 if (allow_pagemode && scan->rs_base.rs_snapshot &&
1352 else
1354 }
1355
1356 /*
1357 * unpin scan buffers
1358 */
1359 if (BufferIsValid(scan->rs_cbuf))
1360 {
1361 ReleaseBuffer(scan->rs_cbuf);
1362 scan->rs_cbuf = InvalidBuffer;
1363 }
1364
1365 if (BufferIsValid(scan->rs_vmbuffer))
1366 {
1368 scan->rs_vmbuffer = InvalidBuffer;
1369 }
1370
1371 /*
1372 * SO_TYPE_BITMAPSCAN would be cleaned up here, but it does not hold any
1373 * additional data vs a normal HeapScan
1374 */
1375
1376 /*
1377 * The read stream is reset on rescan. This must be done before
1378 * initscan(), as some state referred to by read_stream_reset() is reset
1379 * in initscan().
1380 */
1381 if (scan->rs_read_stream)
1383
1384 /*
1385 * reinitialize scan descriptor
1386 */
1387 initscan(scan, key, true);
1388}
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 1505 of file heapam.c.

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

504{
506
507 Assert(!scan->rs_inited); /* else too late to change */
508 /* else rs_startblock is significant */
510
511 /* Check startBlk is valid (but allow case of zero blocks...) */
512 Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
513
514 scan->rs_startblock = startBlk;
515 scan->rs_numblocks = numBlks;
516}
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 8031 of file heapam.c.

8032{
8033 TransactionId xid;
8034
8035 /*
8036 * If xmin is a normal transaction ID, this tuple is definitely not
8037 * frozen.
8038 */
8039 xid = HeapTupleHeaderGetXmin(tuple);
8040 if (TransactionIdIsNormal(xid))
8041 return true;
8042
8043 /*
8044 * If xmax is a valid xact or multixact, this tuple is also not frozen.
8045 */
8046 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
8047 {
8048 MultiXactId multi;
8049
8050 multi = HeapTupleHeaderGetRawXmax(tuple);
8051 if (MultiXactIdIsValid(multi))
8052 return true;
8053 }
8054 else
8055 {
8056 xid = HeapTupleHeaderGetRawXmax(tuple);
8057 if (TransactionIdIsNormal(xid))
8058 return true;
8059 }
8060
8061 if (tuple->t_infomask & HEAP_MOVED)
8062 {
8063 xid = HeapTupleHeaderGetXvac(tuple);
8064 if (TransactionIdIsNormal(xid))
8065 return true;
8066 }
8067
8068 return false;
8069}
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:1729
bool TimestampDifferenceExceeds(TimestampTz start_time, TimestampTz stop_time, int msec)
Definition timestamp.c:1789
TimestampTz GetCurrentTimestamp(void)
Definition timestamp.c:1649
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:1310
int32_t int32
Definition c.h:679
int64 TimestampTz
Definition timestamp.h:39
ErrorContextCallback * error_context_stack
Definition elog.c:100
#define _(x)
Definition elog.c:96
#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:1392
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3682
char * pstrdup(const char *in)
Definition mcxt.c:1910
void * palloc0(Size size)
Definition mcxt.c:1420
#define AmAutoVacuumWorkerProcess()
Definition miscadmin.h:389
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:2374
void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode)
Definition vacuum.c:2417
bool vacuum_get_cutoffs(Relation rel, const VacuumParams *params, struct VacuumCutoffs *cutoffs)
Definition vacuum.c:1106
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:1432
#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 9442 of file heapam.c.

9445{
9446 TransactionId xid;
9448
9449 if (!CheckForSerializableConflictOutNeeded(relation, snapshot))
9450 return;
9451
9452 /*
9453 * Check to see whether the tuple has been written to by a concurrent
9454 * transaction, either to create it not visible to us, or to delete it
9455 * while it is visible to us. The "visible" bool indicates whether the
9456 * tuple is visible to us, while HeapTupleSatisfiesVacuum checks what else
9457 * is going on with it.
9458 *
9459 * In the event of a concurrently inserted tuple that also happens to have
9460 * been concurrently updated (by a separate transaction), the xmin of the
9461 * tuple will be used -- not the updater's xid.
9462 */
9464 switch (htsvResult)
9465 {
9466 case HEAPTUPLE_LIVE:
9467 if (visible)
9468 return;
9469 xid = HeapTupleHeaderGetXmin(tuple->t_data);
9470 break;
9473 if (visible)
9474 xid = HeapTupleHeaderGetUpdateXid(tuple->t_data);
9475 else
9476 xid = HeapTupleHeaderGetXmin(tuple->t_data);
9477
9479 {
9480 /* This is like the HEAPTUPLE_DEAD case */
9481 Assert(!visible);
9482 return;
9483 }
9484 break;
9486 xid = HeapTupleHeaderGetXmin(tuple->t_data);
9487 break;
9488 case HEAPTUPLE_DEAD:
9489 Assert(!visible);
9490 return;
9491 default:
9492
9493 /*
9494 * The only way to get to this default clause is if a new value is
9495 * added to the enum type without adding it to this switch
9496 * statement. That's a bug, so elog.
9497 */
9498 elog(ERROR, "unrecognized return value from HeapTupleSatisfiesVacuum: %u", htsvResult);
9499
9500 /*
9501 * In spite of having all enum values covered and calling elog on
9502 * this default, some compilers think this is a code path which
9503 * allows xid to be used below without initialization. Silence
9504 * that warning.
9505 */
9507 }
9508
9511
9512 /*
9513 * Find top level xid. Bail out if xid is too early to be a conflict, or
9514 * if it's our own xid.
9515 */
9517 return;
9520 return;
9521
9522 CheckForSerializableConflictOut(relation, xid, snapshot);
9523}
HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
void CheckForSerializableConflictOut(Relation relation, TransactionId xid, Snapshot snapshot)
Definition predicate.c:3952
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:7800
#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:7119
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:3110

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

2592{
2595 uint8 info;
2597
2598 Page heap_page = BufferGetPage(buffer);
2599
2600 /* The following local variables hold data registered in the WAL record: */
2604 xlhp_prune_items dead_items;
2607 bool do_prune = nredirected > 0 || ndead > 0 || nunused > 0;
2609 bool heap_fpi_allowed = true;
2610
2612
2613 xlrec.flags = 0;
2615
2616 /*
2617 * We can avoid an FPI of the heap page if the only modification we are
2618 * making to it is to set PD_ALL_VISIBLE and checksums/wal_log_hints are
2619 * disabled.
2620 *
2621 * However, if the page has never been WAL-logged (LSN is invalid), we
2622 * must force an FPI regardless. This can happen when another backend
2623 * extends the heap, initializes the page, and then fails before WAL-
2624 * logging it. Since heap extension is not WAL-logged, recovery might try
2625 * to replay our record and find that the page isn't initialized, which
2626 * would cause a PANIC.
2627 */
2630 else if (!do_prune && nfrozen == 0 && (!do_set_vm || !XLogHintBitIsNeeded()))
2631 {
2633 heap_fpi_allowed = false;
2634 }
2635
2636 /*
2637 * Prepare data for the buffer. The arrays are not actually in the
2638 * buffer, but we pretend that they are. When XLogInsert stores a full
2639 * page image, the arrays can be omitted.
2640 */
2643
2644 if (do_set_vm)
2645 XLogRegisterBuffer(1, vmbuffer, 0);
2646
2647 if (nfrozen > 0)
2648 {
2649 int nplans;
2650
2652
2653 /*
2654 * Prepare deduplicated representation for use in the WAL record. This
2655 * destructively sorts frozen tuples array in-place.
2656 */
2657 nplans = heap_log_freeze_plan(frozen, nfrozen, plans, frz_offsets);
2658
2659 freeze_plans.nplans = nplans;
2661 offsetof(xlhp_freeze_plans, plans));
2662 XLogRegisterBufData(0, plans,
2663 sizeof(xlhp_freeze_plan) * nplans);
2664 }
2665 if (nredirected > 0)
2666 {
2668
2669 redirect_items.ntargets = nredirected;
2672 XLogRegisterBufData(0, redirected,
2673 sizeof(OffsetNumber[2]) * nredirected);
2674 }
2675 if (ndead > 0)
2676 {
2677 xlrec.flags |= XLHP_HAS_DEAD_ITEMS;
2678
2679 dead_items.ntargets = ndead;
2680 XLogRegisterBufData(0, &dead_items,
2682 XLogRegisterBufData(0, dead,
2683 sizeof(OffsetNumber) * ndead);
2684 }
2685 if (nunused > 0)
2686 {
2688
2689 unused_items.ntargets = nunused;
2692 XLogRegisterBufData(0, unused,
2693 sizeof(OffsetNumber) * nunused);
2694 }
2695 if (nfrozen > 0)
2697 sizeof(OffsetNumber) * nfrozen);
2698
2699 /*
2700 * Prepare the main xl_heap_prune record. We already set the XLHP_HAS_*
2701 * flag above.
2702 */
2704 {
2705 xlrec.flags |= XLHP_VM_ALL_VISIBLE;
2707 xlrec.flags |= XLHP_VM_ALL_FROZEN;
2708 }
2710 xlrec.flags |= XLHP_IS_CATALOG_REL;
2713 if (cleanup_lock)
2714 xlrec.flags |= XLHP_CLEANUP_LOCK;
2715 else
2716 {
2717 Assert(nredirected == 0 && ndead == 0);
2718 /* also, any items in 'unused' must've been LP_DEAD previously */
2719 }
2723
2724 switch (reason)
2725 {
2726 case PRUNE_ON_ACCESS:
2728 break;
2729 case PRUNE_VACUUM_SCAN:
2731 break;
2734 break;
2735 default:
2736 elog(ERROR, "unrecognized prune reason: %d", (int) reason);
2737 break;
2738 }
2739 recptr = XLogInsert(RM_HEAP2_ID, info);
2740
2741 if (do_set_vm)
2742 {
2743 Assert(BufferIsDirty(vmbuffer));
2744 PageSetLSN(BufferGetPage(vmbuffer), recptr);
2745 }
2746
2747 /*
2748 * If we explicitly skip an FPI, we must not stamp the heap page with this
2749 * record's LSN. Recovery skips records <= the stamped LSN, so this could
2750 * lead to skipping an earlier FPI needed to repair a torn page.
2751 */
2752 if (heap_fpi_allowed)
2753 {
2754 Assert(BufferIsDirty(buffer));
2756 }
2757}
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:2504
#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 1967 of file heapam.c.

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

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 5550 of file reorderbuffer.c.

5554{
5557 ForkNumber forkno;
5558 BlockNumber blockno;
5559 bool updated_mapping = false;
5560
5561 /*
5562 * Return unresolved if tuplecid_data is not valid. That's because when
5563 * streaming in-progress transactions we may run into tuples with the CID
5564 * before actually decoding them. Think e.g. about INSERT followed by
5565 * TRUNCATE, where the TRUNCATE may not be decoded yet when applying the
5566 * INSERT. So in such cases, we assume the CID is from the future
5567 * command.
5568 */
5569 if (tuplecid_data == NULL)
5570 return false;
5571
5572 /* be careful about padding */
5573 memset(&key, 0, sizeof(key));
5574
5575 Assert(!BufferIsLocal(buffer));
5576
5577 /*
5578 * get relfilelocator from the buffer, no convenient way to access it
5579 * other than that.
5580 */
5581 BufferGetTag(buffer, &key.rlocator, &forkno, &blockno);
5582
5583 /* tuples can only be in the main fork */
5584 Assert(forkno == MAIN_FORKNUM);
5585 Assert(blockno == ItemPointerGetBlockNumber(&htup->t_self));
5586
5587 ItemPointerCopy(&htup->t_self,
5588 &key.tid);
5589
5590restart:
5593
5594 /*
5595 * failed to find a mapping, check whether the table was rewritten and
5596 * apply mapping if so, but only do that once - there can be no new
5597 * mappings while we are in here since we have to hold a lock on the
5598 * relation.
5599 */
5600 if (ent == NULL && !updated_mapping)
5601 {
5603 /* now check but don't update for a mapping again */
5604 updated_mapping = true;
5605 goto restart;
5606 }
5607 else if (ent == NULL)
5608 return false;
5609
5610 if (cmin)
5611 *cmin = ent->cmin;
5612 if (cmax)
5613 *cmax = ent->cmax;
5614 return true;
5615}
#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 3219 of file heapam.c.

3220{
3222 TM_FailureData tmfd;
3223
3224 result = heap_delete(relation, tid,
3225 GetCurrentCommandId(true),
3226 0,
3228 true /* wait for commit */ ,
3229 &tmfd);
3230 switch (result)
3231 {
3232 case TM_SelfModified:
3233 /* Tuple was already updated in current command? */
3234 elog(ERROR, "tuple already updated by self");
3235 break;
3236
3237 case TM_Ok:
3238 /* done successfully */
3239 break;
3240
3241 case TM_Updated:
3242 elog(ERROR, "tuple concurrently updated");
3243 break;
3244
3245 case TM_Deleted:
3246 elog(ERROR, "tuple concurrently deleted");
3247 break;
3248
3249 default:
3250 elog(ERROR, "unrecognized heap_delete status: %u", result);
3251 break;
3252 }
3253}
TM_Result heap_delete(Relation relation, const ItemPointerData *tid, CommandId cid, uint32 options, Snapshot crosscheck, bool wait, TM_FailureData *tmfd)
Definition heapam.c:2759

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

2702{
2703 heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
2704}
void heap_insert(Relation relation, HeapTuple tup, CommandId cid, uint32 options, BulkInsertState bistate)
Definition heapam.c:2005

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

4641{
4643 TM_FailureData tmfd;
4644 LockTupleMode lockmode;
4645
4646 result = heap_update(relation, otid, tup,
4647 GetCurrentCommandId(true), 0,
4649 true /* wait for commit */ ,
4650 &tmfd, &lockmode, update_indexes);
4651 switch (result)
4652 {
4653 case TM_SelfModified:
4654 /* Tuple was already updated in current command? */
4655 elog(ERROR, "tuple already updated by self");
4656 break;
4657
4658 case TM_Ok:
4659 /* done successfully */
4660 break;
4661
4662 case TM_Updated:
4663 elog(ERROR, "tuple concurrently updated");
4664 break;
4665
4666 case TM_Deleted:
4667 elog(ERROR, "tuple concurrently deleted");
4668 break;
4669
4670 default:
4671 elog(ERROR, "unrecognized heap_update status: %u", result);
4672 break;
4673 }
4674}
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:3267

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

Referenced by CatalogTupleUpdate(), and CatalogTupleUpdateWithInfo().