Thanks to visit codestin.com
Credit goes to doxygen.postgresql.org

PostgreSQL Source Code git master
reorderbuffer.c File Reference
#include "postgres.h"
#include <unistd.h>
#include <sys/stat.h>
#include "access/detoast.h"
#include "access/heapam.h"
#include "access/rewriteheap.h"
#include "access/transam.h"
#include "access/xact.h"
#include "access/xlog_internal.h"
#include "catalog/catalog.h"
#include "common/int.h"
#include "lib/binaryheap.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "replication/logical.h"
#include "replication/reorderbuffer.h"
#include "replication/slot.h"
#include "replication/snapbuild.h"
#include "storage/bufmgr.h"
#include "storage/fd.h"
#include "storage/procarray.h"
#include "storage/sinval.h"
#include "utils/builtins.h"
#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/relfilenumbermap.h"
Include dependency graph for reorderbuffer.c:

Go to the source code of this file.

Data Structures

struct  ReorderBufferTXNByIdEnt
 
struct  ReorderBufferTupleCidKey
 
struct  ReorderBufferTupleCidEnt
 
struct  TXNEntryFile
 
struct  ReorderBufferIterTXNEntry
 
struct  ReorderBufferIterTXNState
 
struct  ReorderBufferToastEnt
 
struct  ReorderBufferDiskChange
 
struct  RewriteMappingFile
 

Macros

#define MAX_DISTR_INVAL_MSG_PER_TXN    ((8 * 1024 * 1024) / sizeof(SharedInvalidationMessage))
 
#define IsSpecInsert(action)
 
#define IsSpecConfirmOrAbort(action)
 
#define IsInsertOrUpdate(action)
 
#define CHANGES_THRESHOLD   100
 

Typedefs

typedef struct ReorderBufferTXNByIdEnt ReorderBufferTXNByIdEnt
 
typedef struct ReorderBufferTupleCidKey ReorderBufferTupleCidKey
 
typedef struct ReorderBufferTupleCidEnt ReorderBufferTupleCidEnt
 
typedef struct TXNEntryFile TXNEntryFile
 
typedef struct ReorderBufferIterTXNEntry ReorderBufferIterTXNEntry
 
typedef struct ReorderBufferIterTXNState ReorderBufferIterTXNState
 
typedef struct ReorderBufferToastEnt ReorderBufferToastEnt
 
typedef struct ReorderBufferDiskChange ReorderBufferDiskChange
 
typedef struct RewriteMappingFile RewriteMappingFile
 

Functions

static ReorderBufferTXNReorderBufferAllocTXN (ReorderBuffer *rb)
 
static void ReorderBufferFreeTXN (ReorderBuffer *rb, ReorderBufferTXN *txn)
 
static ReorderBufferTXNReorderBufferTXNByXid (ReorderBuffer *rb, TransactionId xid, bool create, bool *is_new, XLogRecPtr lsn, bool create_as_top)
 
static void ReorderBufferTransferSnapToParent (ReorderBufferTXN *txn, ReorderBufferTXN *subtxn)
 
static void AssertTXNLsnOrder (ReorderBuffer *rb)
 
static void ReorderBufferIterTXNInit (ReorderBuffer *rb, ReorderBufferTXN *txn, ReorderBufferIterTXNState *volatile *iter_state)
 
static ReorderBufferChangeReorderBufferIterTXNNext (ReorderBuffer *rb, ReorderBufferIterTXNState *state)
 
static void ReorderBufferIterTXNFinish (ReorderBuffer *rb, ReorderBufferIterTXNState *state)
 
static void ReorderBufferExecuteInvalidations (uint32 nmsgs, SharedInvalidationMessage *msgs)
 
static void ReorderBufferCheckMemoryLimit (ReorderBuffer *rb)
 
static void ReorderBufferSerializeTXN (ReorderBuffer *rb, ReorderBufferTXN *txn)
 
static void ReorderBufferSerializeChange (ReorderBuffer *rb, ReorderBufferTXN *txn, int fd, ReorderBufferChange *change)
 
static Size ReorderBufferRestoreChanges (ReorderBuffer *rb, ReorderBufferTXN *txn, TXNEntryFile *file, XLogSegNo *segno)
 
static void ReorderBufferRestoreChange (ReorderBuffer *rb, ReorderBufferTXN *txn, char *data)
 
static void ReorderBufferRestoreCleanup (ReorderBuffer *rb, ReorderBufferTXN *txn)
 
static void ReorderBufferTruncateTXN (ReorderBuffer *rb, ReorderBufferTXN *txn, bool txn_prepared)
 
static void ReorderBufferMaybeMarkTXNStreamed (ReorderBuffer *rb, ReorderBufferTXN *txn)
 
static bool ReorderBufferCheckAndTruncateAbortedTXN (ReorderBuffer *rb, ReorderBufferTXN *txn)
 
static void ReorderBufferCleanupSerializedTXNs (const char *slotname)
 
static void ReorderBufferSerializedPath (char *path, ReplicationSlot *slot, TransactionId xid, XLogSegNo segno)
 
static int ReorderBufferTXNSizeCompare (const pairingheap_node *a, const pairingheap_node *b, void *arg)
 
static void ReorderBufferFreeSnap (ReorderBuffer *rb, Snapshot snap)
 
static Snapshot ReorderBufferCopySnap (ReorderBuffer *rb, Snapshot orig_snap, ReorderBufferTXN *txn, CommandId cid)
 
static bool ReorderBufferCanStream (ReorderBuffer *rb)
 
static bool ReorderBufferCanStartStreaming (ReorderBuffer *rb)
 
static void ReorderBufferStreamTXN (ReorderBuffer *rb, ReorderBufferTXN *txn)
 
static void ReorderBufferStreamCommit (ReorderBuffer *rb, ReorderBufferTXN *txn)
 
static void ReorderBufferToastInitHash (ReorderBuffer *rb, ReorderBufferTXN *txn)
 
static void ReorderBufferToastReset (ReorderBuffer *rb, ReorderBufferTXN *txn)
 
static void ReorderBufferToastReplace (ReorderBuffer *rb, ReorderBufferTXN *txn, Relation relation, ReorderBufferChange *change)
 
static void ReorderBufferToastAppendChunk (ReorderBuffer *rb, ReorderBufferTXN *txn, Relation relation, ReorderBufferChange *change)
 
static Size ReorderBufferChangeSize (ReorderBufferChange *change)
 
static void ReorderBufferChangeMemoryUpdate (ReorderBuffer *rb, ReorderBufferChange *change, ReorderBufferTXN *txn, bool addition, Size sz)
 
ReorderBufferReorderBufferAllocate (void)
 
void ReorderBufferFree (ReorderBuffer *rb)
 
ReorderBufferChangeReorderBufferAllocChange (ReorderBuffer *rb)
 
void ReorderBufferFreeChange (ReorderBuffer *rb, ReorderBufferChange *change, bool upd_mem)
 
HeapTuple ReorderBufferAllocTupleBuf (ReorderBuffer *rb, Size tuple_len)
 
void ReorderBufferFreeTupleBuf (HeapTuple tuple)
 
OidReorderBufferAllocRelids (ReorderBuffer *rb, int nrelids)
 
void ReorderBufferFreeRelids (ReorderBuffer *rb, Oid *relids)
 
static void ReorderBufferProcessPartialChange (ReorderBuffer *rb, ReorderBufferTXN *txn, ReorderBufferChange *change, bool toast_insert)
 
void ReorderBufferQueueChange (ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn, ReorderBufferChange *change, bool toast_insert)
 
void ReorderBufferQueueMessage (ReorderBuffer *rb, TransactionId xid, Snapshot snap, XLogRecPtr lsn, bool transactional, const char *prefix, Size message_size, const char *message)
 
static void AssertChangeLsnOrder (ReorderBufferTXN *txn)
 
ReorderBufferTXNReorderBufferGetOldestTXN (ReorderBuffer *rb)
 
TransactionId ReorderBufferGetOldestXmin (ReorderBuffer *rb)
 
void ReorderBufferSetRestartPoint (ReorderBuffer *rb, XLogRecPtr ptr)
 
void ReorderBufferAssignChild (ReorderBuffer *rb, TransactionId xid, TransactionId subxid, XLogRecPtr lsn)
 
void ReorderBufferCommitChild (ReorderBuffer *rb, TransactionId xid, TransactionId subxid, XLogRecPtr commit_lsn, XLogRecPtr end_lsn)
 
static int ReorderBufferIterCompare (Datum a, Datum b, void *arg)
 
static void ReorderBufferCleanupTXN (ReorderBuffer *rb, ReorderBufferTXN *txn)
 
static void ReorderBufferBuildTupleCidHash (ReorderBuffer *rb, ReorderBufferTXN *txn)
 
static void SetupCheckXidLive (TransactionId xid)
 
static void ReorderBufferApplyChange (ReorderBuffer *rb, ReorderBufferTXN *txn, Relation relation, ReorderBufferChange *change, bool streaming)
 
static void ReorderBufferApplyTruncate (ReorderBuffer *rb, ReorderBufferTXN *txn, int nrelations, Relation *relations, ReorderBufferChange *change, bool streaming)
 
static void ReorderBufferApplyMessage (ReorderBuffer *rb, ReorderBufferTXN *txn, ReorderBufferChange *change, bool streaming)
 
static void ReorderBufferSaveTXNSnapshot (ReorderBuffer *rb, ReorderBufferTXN *txn, Snapshot snapshot_now, CommandId command_id)
 
static void ReorderBufferResetTXN (ReorderBuffer *rb, ReorderBufferTXN *txn, Snapshot snapshot_now, CommandId command_id, XLogRecPtr last_lsn, ReorderBufferChange *specinsert)
 
static void ReorderBufferProcessTXN (ReorderBuffer *rb, ReorderBufferTXN *txn, XLogRecPtr commit_lsn, volatile Snapshot snapshot_now, volatile CommandId command_id, bool streaming)
 
static void ReorderBufferReplay (ReorderBufferTXN *txn, ReorderBuffer *rb, TransactionId xid, XLogRecPtr commit_lsn, XLogRecPtr end_lsn, TimestampTz commit_time, RepOriginId origin_id, XLogRecPtr origin_lsn)
 
void ReorderBufferCommit (ReorderBuffer *rb, TransactionId xid, XLogRecPtr commit_lsn, XLogRecPtr end_lsn, TimestampTz commit_time, RepOriginId origin_id, XLogRecPtr origin_lsn)
 
bool ReorderBufferRememberPrepareInfo (ReorderBuffer *rb, TransactionId xid, XLogRecPtr prepare_lsn, XLogRecPtr end_lsn, TimestampTz prepare_time, RepOriginId origin_id, XLogRecPtr origin_lsn)
 
void ReorderBufferSkipPrepare (ReorderBuffer *rb, TransactionId xid)
 
void ReorderBufferPrepare (ReorderBuffer *rb, TransactionId xid, char *gid)
 
void ReorderBufferFinishPrepared (ReorderBuffer *rb, TransactionId xid, XLogRecPtr commit_lsn, XLogRecPtr end_lsn, XLogRecPtr two_phase_at, TimestampTz commit_time, RepOriginId origin_id, XLogRecPtr origin_lsn, char *gid, bool is_commit)
 
void ReorderBufferAbort (ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn, TimestampTz abort_time)
 
void ReorderBufferAbortOld (ReorderBuffer *rb, TransactionId oldestRunningXid)
 
void ReorderBufferForget (ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn)
 
void ReorderBufferInvalidate (ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn)
 
void ReorderBufferImmediateInvalidation (ReorderBuffer *rb, uint32 ninvalidations, SharedInvalidationMessage *invalidations)
 
void ReorderBufferProcessXid (ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn)
 
void ReorderBufferAddSnapshot (ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn, Snapshot snap)
 
void ReorderBufferSetBaseSnapshot (ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn, Snapshot snap)
 
void ReorderBufferAddNewCommandId (ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn, CommandId cid)
 
void ReorderBufferAddNewTupleCids (ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn, RelFileLocator locator, ItemPointerData tid, CommandId cmin, CommandId cmax, CommandId combocid)
 
static void ReorderBufferQueueInvalidations (ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn, Size nmsgs, SharedInvalidationMessage *msgs)
 
static void ReorderBufferAccumulateInvalidations (SharedInvalidationMessage **invals_out, uint32 *ninvals_out, SharedInvalidationMessage *msgs_new, Size nmsgs_new)
 
void ReorderBufferAddInvalidations (ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn, Size nmsgs, SharedInvalidationMessage *msgs)
 
void ReorderBufferAddDistributedInvalidations (ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn, Size nmsgs, SharedInvalidationMessage *msgs)
 
void ReorderBufferXidSetCatalogChanges (ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn)
 
TransactionIdReorderBufferGetCatalogChangesXacts (ReorderBuffer *rb)
 
bool ReorderBufferXidHasCatalogChanges (ReorderBuffer *rb, TransactionId xid)
 
bool ReorderBufferXidHasBaseSnapshot (ReorderBuffer *rb, TransactionId xid)
 
static void ReorderBufferSerializeReserve (ReorderBuffer *rb, Size sz)
 
static ReorderBufferTXNReorderBufferLargestTXN (ReorderBuffer *rb)
 
static ReorderBufferTXNReorderBufferLargestStreamableTopTXN (ReorderBuffer *rb)
 
void StartupReorderBuffer (void)
 
static void ApplyLogicalMappingFile (HTAB *tuplecid_data, Oid relid, const char *fname)
 
static bool TransactionIdInArray (TransactionId xid, TransactionId *xip, Size num)
 
static int file_sort_by_lsn (const ListCell *a_p, const ListCell *b_p)
 
static void UpdateLogicalMappings (HTAB *tuplecid_data, Oid relid, Snapshot snapshot)
 
bool ResolveCminCmaxDuringDecoding (HTAB *tuplecid_data, Snapshot snapshot, HeapTuple htup, Buffer buffer, CommandId *cmin, CommandId *cmax)
 
uint32 ReorderBufferGetInvalidations (ReorderBuffer *rb, TransactionId xid, SharedInvalidationMessage **msgs)
 

Variables

int logical_decoding_work_mem
 
static const Size max_changes_in_memory = 4096
 
int debug_logical_replication_streaming = DEBUG_LOGICAL_REP_STREAMING_BUFFERED
 

Macro Definition Documentation

◆ CHANGES_THRESHOLD

#define CHANGES_THRESHOLD   100

◆ IsInsertOrUpdate

#define IsInsertOrUpdate (   action)
Value:

Definition at line 206 of file reorderbuffer.c.

◆ IsSpecConfirmOrAbort

#define IsSpecConfirmOrAbort (   action)
Value:
( \
)
@ REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM
Definition: reorderbuffer.h:61
@ REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT
Definition: reorderbuffer.h:62

Definition at line 201 of file reorderbuffer.c.

◆ IsSpecInsert

#define IsSpecInsert (   action)
Value:

Definition at line 197 of file reorderbuffer.c.

◆ MAX_DISTR_INVAL_MSG_PER_TXN

#define MAX_DISTR_INVAL_MSG_PER_TXN    ((8 * 1024 * 1024) / sizeof(SharedInvalidationMessage))

Definition at line 125 of file reorderbuffer.c.

Typedef Documentation

◆ ReorderBufferDiskChange

◆ ReorderBufferIterTXNEntry

◆ ReorderBufferIterTXNState

◆ ReorderBufferToastEnt

◆ ReorderBufferTupleCidEnt

◆ ReorderBufferTupleCidKey

◆ ReorderBufferTXNByIdEnt

◆ RewriteMappingFile

◆ TXNEntryFile

typedef struct TXNEntryFile TXNEntryFile

Function Documentation

◆ ApplyLogicalMappingFile()

static void ApplyLogicalMappingFile ( HTAB tuplecid_data,
Oid  relid,
const char *  fname 
)
static

Definition at line 5349 of file reorderbuffer.c.

5350{
5351 char path[MAXPGPATH];
5352 int fd;
5353 int readBytes;
5355
5356 sprintf(path, "%s/%s", PG_LOGICAL_MAPPINGS_DIR, fname);
5357 fd = OpenTransientFile(path, O_RDONLY | PG_BINARY);
5358 if (fd < 0)
5359 ereport(ERROR,
5361 errmsg("could not open file \"%s\": %m", path)));
5362
5363 while (true)
5364 {
5367 ReorderBufferTupleCidEnt *new_ent;
5368 bool found;
5369
5370 /* be careful about padding */
5371 memset(&key, 0, sizeof(ReorderBufferTupleCidKey));
5372
5373 /* read all mappings till the end of the file */
5374 pgstat_report_wait_start(WAIT_EVENT_REORDER_LOGICAL_MAPPING_READ);
5375 readBytes = read(fd, &map, sizeof(LogicalRewriteMappingData));
5377
5378 if (readBytes < 0)
5379 ereport(ERROR,
5381 errmsg("could not read file \"%s\": %m",
5382 path)));
5383 else if (readBytes == 0) /* EOF */
5384 break;
5385 else if (readBytes != sizeof(LogicalRewriteMappingData))
5386 ereport(ERROR,
5388 errmsg("could not read from file \"%s\": read %d instead of %d bytes",
5389 path, readBytes,
5390 (int32) sizeof(LogicalRewriteMappingData))));
5391
5392 key.rlocator = map.old_locator;
5394 &key.tid);
5395
5396
5397 ent = (ReorderBufferTupleCidEnt *)
5399
5400 /* no existing mapping, no need to update */
5401 if (!ent)
5402 continue;
5403
5404 key.rlocator = map.new_locator;
5406 &key.tid);
5407
5408 new_ent = (ReorderBufferTupleCidEnt *)
5410
5411 if (found)
5412 {
5413 /*
5414 * Make sure the existing mapping makes sense. We sometime update
5415 * old records that did not yet have a cmax (e.g. pg_class' own
5416 * entry while rewriting it) during rewrites, so allow that.
5417 */
5418 Assert(ent->cmin == InvalidCommandId || ent->cmin == new_ent->cmin);
5419 Assert(ent->cmax == InvalidCommandId || ent->cmax == new_ent->cmax);
5420 }
5421 else
5422 {
5423 /* update mapping */
5424 new_ent->cmin = ent->cmin;
5425 new_ent->cmax = ent->cmax;
5426 new_ent->combocid = ent->combocid;
5427 }
5428 }
5429
5430 if (CloseTransientFile(fd) != 0)
5431 ereport(ERROR,
5433 errmsg("could not close file \"%s\": %m", path)));
5434}
#define InvalidCommandId
Definition: c.h:675
#define PG_BINARY
Definition: c.h:1273
int32_t int32
Definition: c.h:535
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:952
int errcode_for_file_access(void)
Definition: elog.c:877
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:150
int CloseTransientFile(int fd)
Definition: fd.c:2868
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2691
Assert(PointerIsAligned(start, uint64))
@ HASH_FIND
Definition: hsearch.h:113
@ HASH_ENTER
Definition: hsearch.h:114
#define read(a, b, c)
Definition: win32.h:13
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
Definition: itemptr.h:172
#define MAXPGPATH
#define sprintf
Definition: port.h:241
static int fd(const char *x, int i)
Definition: preproc-init.c:105
#define PG_LOGICAL_MAPPINGS_DIR
Definition: reorderbuffer.h:23
static HTAB * tuplecid_data
Definition: snapmgr.c:162
ItemPointerData new_tid
Definition: rewriteheap.h:40
RelFileLocator old_locator
Definition: rewriteheap.h:37
ItemPointerData old_tid
Definition: rewriteheap.h:39
RelFileLocator new_locator
Definition: rewriteheap.h:38
static void pgstat_report_wait_start(uint32 wait_event_info)
Definition: wait_event.h:69
static void pgstat_report_wait_end(void)
Definition: wait_event.h:85

References Assert(), CloseTransientFile(), ReorderBufferTupleCidEnt::cmax, ReorderBufferTupleCidEnt::cmin, ReorderBufferTupleCidEnt::combocid, ereport, errcode_for_file_access(), errmsg(), ERROR, fd(), HASH_ENTER, HASH_FIND, hash_search(), InvalidCommandId, ItemPointerCopy(), sort-test::key, MAXPGPATH, LogicalRewriteMappingData::new_locator, LogicalRewriteMappingData::new_tid, LogicalRewriteMappingData::old_locator, LogicalRewriteMappingData::old_tid, OpenTransientFile(), PG_BINARY, PG_LOGICAL_MAPPINGS_DIR, pgstat_report_wait_end(), pgstat_report_wait_start(), read, sprintf, and tuplecid_data.

Referenced by UpdateLogicalMappings().

◆ AssertChangeLsnOrder()

static void AssertChangeLsnOrder ( ReorderBufferTXN txn)
static

Definition at line 1012 of file reorderbuffer.c.

1013{
1014#ifdef USE_ASSERT_CHECKING
1015 dlist_iter iter;
1016 XLogRecPtr prev_lsn = txn->first_lsn;
1017
1018 dlist_foreach(iter, &txn->changes)
1019 {
1020 ReorderBufferChange *cur_change;
1021
1022 cur_change = dlist_container(ReorderBufferChange, node, iter.cur);
1023
1025 Assert(cur_change->lsn != InvalidXLogRecPtr);
1026 Assert(txn->first_lsn <= cur_change->lsn);
1027
1028 if (txn->end_lsn != InvalidXLogRecPtr)
1029 Assert(cur_change->lsn <= txn->end_lsn);
1030
1031 Assert(prev_lsn <= cur_change->lsn);
1032
1033 prev_lsn = cur_change->lsn;
1034 }
1035#endif
1036}
#define dlist_foreach(iter, lhead)
Definition: ilist.h:623
#define dlist_container(type, membername, ptr)
Definition: ilist.h:593
XLogRecPtr first_lsn
XLogRecPtr end_lsn
dlist_head changes
dlist_node * cur
Definition: ilist.h:179
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28

References Assert(), ReorderBufferTXN::changes, dlist_iter::cur, dlist_container, dlist_foreach, ReorderBufferTXN::end_lsn, ReorderBufferTXN::first_lsn, InvalidXLogRecPtr, and ReorderBufferChange::lsn.

Referenced by ReorderBufferIterTXNInit().

◆ AssertTXNLsnOrder()

static void AssertTXNLsnOrder ( ReorderBuffer rb)
static

Definition at line 941 of file reorderbuffer.c.

942{
943#ifdef USE_ASSERT_CHECKING
945 dlist_iter iter;
946 XLogRecPtr prev_first_lsn = InvalidXLogRecPtr;
947 XLogRecPtr prev_base_snap_lsn = InvalidXLogRecPtr;
948
949 /*
950 * Skip the verification if we don't reach the LSN at which we start
951 * decoding the contents of transactions yet because until we reach the
952 * LSN, we could have transactions that don't have the association between
953 * the top-level transaction and subtransaction yet and consequently have
954 * the same LSN. We don't guarantee this association until we try to
955 * decode the actual contents of transaction. The ordering of the records
956 * prior to the start_decoding_at LSN should have been checked before the
957 * restart.
958 */
960 return;
961
963 {
965 iter.cur);
966
967 /* start LSN must be set */
969
970 /* If there is an end LSN, it must be higher than start LSN */
971 if (cur_txn->end_lsn != InvalidXLogRecPtr)
972 Assert(cur_txn->first_lsn <= cur_txn->end_lsn);
973
974 /* Current initial LSN must be strictly higher than previous */
975 if (prev_first_lsn != InvalidXLogRecPtr)
976 Assert(prev_first_lsn < cur_txn->first_lsn);
977
978 /* known-as-subtxn txns must not be listed */
980
981 prev_first_lsn = cur_txn->first_lsn;
982 }
983
985 {
987 base_snapshot_node,
988 iter.cur);
989
990 /* base snapshot (and its LSN) must be set */
991 Assert(cur_txn->base_snapshot != NULL);
993
994 /* current LSN must be strictly higher than previous */
995 if (prev_base_snap_lsn != InvalidXLogRecPtr)
996 Assert(prev_base_snap_lsn < cur_txn->base_snapshot_lsn);
997
998 /* known-as-subtxn txns must not be listed */
1000
1001 prev_base_snap_lsn = cur_txn->base_snapshot_lsn;
1002 }
1003#endif
1004}
#define rbtxn_is_known_subxact(txn)
bool SnapBuildXactNeedsSkip(SnapBuild *builder, XLogRecPtr ptr)
Definition: snapbuild.c:304
XLogReaderState * reader
Definition: logical.h:42
struct SnapBuild * snapshot_builder
Definition: logical.h:44
XLogRecPtr base_snapshot_lsn
Snapshot base_snapshot
dlist_head txns_by_base_snapshot_lsn
dlist_head toplevel_by_lsn
void * private_data
XLogRecPtr EndRecPtr
Definition: xlogreader.h:207

References Assert(), ReorderBufferTXN::base_snapshot, ReorderBufferTXN::base_snapshot_lsn, dlist_iter::cur, dlist_container, dlist_foreach, ReorderBufferTXN::end_lsn, XLogReaderState::EndRecPtr, ReorderBufferTXN::first_lsn, InvalidXLogRecPtr, ReorderBuffer::private_data, rbtxn_is_known_subxact, LogicalDecodingContext::reader, SnapBuildXactNeedsSkip(), LogicalDecodingContext::snapshot_builder, ReorderBuffer::toplevel_by_lsn, and ReorderBuffer::txns_by_base_snapshot_lsn.

Referenced by ReorderBufferAssignChild(), ReorderBufferGetOldestTXN(), ReorderBufferGetOldestXmin(), ReorderBufferSetBaseSnapshot(), and ReorderBufferTXNByXid().

◆ file_sort_by_lsn()

static int file_sort_by_lsn ( const ListCell a_p,
const ListCell b_p 
)
static

Definition at line 5451 of file reorderbuffer.c.

5452{
5455
5456 return pg_cmp_u64(a->lsn, b->lsn);
5457}
static int pg_cmp_u64(uint64 a, uint64 b)
Definition: int.h:664
int b
Definition: isn.c:74
int a
Definition: isn.c:73
#define lfirst(lc)
Definition: pg_list.h:172

References a, b, lfirst, and pg_cmp_u64().

Referenced by UpdateLogicalMappings().

◆ ReorderBufferAbort()

void ReorderBufferAbort ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  lsn,
TimestampTz  abort_time 
)

Definition at line 3086 of file reorderbuffer.c.

3088{
3089 ReorderBufferTXN *txn;
3090
3091 txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr,
3092 false);
3093
3094 /* unknown, nothing to remove */
3095 if (txn == NULL)
3096 return;
3097
3098 txn->xact_time.abort_time = abort_time;
3099
3100 /* For streamed transactions notify the remote node about the abort. */
3101 if (rbtxn_is_streamed(txn))
3102 {
3103 rb->stream_abort(rb, txn, lsn);
3104
3105 /*
3106 * We might have decoded changes for this transaction that could load
3107 * the cache as per the current transaction's view (consider DDL's
3108 * happened in this transaction). We don't want the decoding of future
3109 * transactions to use those cache entries so execute only the inval
3110 * messages in this transaction.
3111 */
3112 if (txn->ninvalidations > 0)
3114 txn->invalidations);
3115 }
3116
3117 /* cosmetic... */
3118 txn->final_lsn = lsn;
3119
3120 /* remove potential on-disk data, and deallocate */
3121 ReorderBufferCleanupTXN(rb, txn);
3122}
static void ReorderBufferCleanupTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
void ReorderBufferImmediateInvalidation(ReorderBuffer *rb, uint32 ninvalidations, SharedInvalidationMessage *invalidations)
static ReorderBufferTXN * ReorderBufferTXNByXid(ReorderBuffer *rb, TransactionId xid, bool create, bool *is_new, XLogRecPtr lsn, bool create_as_top)
#define rbtxn_is_streamed(txn)
SharedInvalidationMessage * invalidations
union ReorderBufferTXN::@119 xact_time
TimestampTz abort_time
XLogRecPtr final_lsn
ReorderBufferStreamAbortCB stream_abort

References ReorderBufferTXN::abort_time, ReorderBufferTXN::final_lsn, ReorderBufferTXN::invalidations, InvalidXLogRecPtr, ReorderBufferTXN::ninvalidations, rbtxn_is_streamed, ReorderBufferCleanupTXN(), ReorderBufferImmediateInvalidation(), ReorderBufferTXNByXid(), ReorderBuffer::stream_abort, and ReorderBufferTXN::xact_time.

Referenced by DecodeAbort().

◆ ReorderBufferAbortOld()

void ReorderBufferAbortOld ( ReorderBuffer rb,
TransactionId  oldestRunningXid 
)

Definition at line 3132 of file reorderbuffer.c.

3133{
3135
3136 /*
3137 * Iterate through all (potential) toplevel TXNs and abort all that are
3138 * older than what possibly can be running. Once we've found the first
3139 * that is alive we stop, there might be some that acquired an xid earlier
3140 * but started writing later, but it's unlikely and they will be cleaned
3141 * up in a later call to this function.
3142 */
3144 {
3145 ReorderBufferTXN *txn;
3146
3147 txn = dlist_container(ReorderBufferTXN, node, it.cur);
3148
3149 if (TransactionIdPrecedes(txn->xid, oldestRunningXid))
3150 {
3151 elog(DEBUG2, "aborting old transaction %u", txn->xid);
3152
3153 /* Notify the remote node about the crash/immediate restart. */
3154 if (rbtxn_is_streamed(txn))
3155 rb->stream_abort(rb, txn, InvalidXLogRecPtr);
3156
3157 /* remove potential on-disk data, and deallocate this tx */
3158 ReorderBufferCleanupTXN(rb, txn);
3159 }
3160 else
3161 return;
3162 }
3163}
#define DEBUG2
Definition: elog.h:29
#define elog(elevel,...)
Definition: elog.h:226
#define dlist_foreach_modify(iter, lhead)
Definition: ilist.h:640
TransactionId xid
dlist_node * cur
Definition: ilist.h:200
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:280

References dlist_mutable_iter::cur, DEBUG2, dlist_container, dlist_foreach_modify, elog, InvalidXLogRecPtr, rbtxn_is_streamed, ReorderBufferCleanupTXN(), ReorderBuffer::stream_abort, ReorderBuffer::toplevel_by_lsn, TransactionIdPrecedes(), and ReorderBufferTXN::xid.

Referenced by standby_decode().

◆ ReorderBufferAccumulateInvalidations()

static void ReorderBufferAccumulateInvalidations ( SharedInvalidationMessage **  invals_out,
uint32 ninvals_out,
SharedInvalidationMessage msgs_new,
Size  nmsgs_new 
)
static

Definition at line 3505 of file reorderbuffer.c.

3509{
3510 if (*ninvals_out == 0)
3511 {
3512 *ninvals_out = nmsgs_new;
3513 *invals_out = (SharedInvalidationMessage *)
3514 palloc(sizeof(SharedInvalidationMessage) * nmsgs_new);
3515 memcpy(*invals_out, msgs_new, sizeof(SharedInvalidationMessage) * nmsgs_new);
3516 }
3517 else
3518 {
3519 /* Enlarge the array of inval messages */
3520 *invals_out = (SharedInvalidationMessage *)
3521 repalloc(*invals_out, sizeof(SharedInvalidationMessage) *
3522 (*ninvals_out + nmsgs_new));
3523 memcpy(*invals_out + *ninvals_out, msgs_new,
3524 nmsgs_new * sizeof(SharedInvalidationMessage));
3525 *ninvals_out += nmsgs_new;
3526 }
3527}
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1610
void * palloc(Size size)
Definition: mcxt.c:1365

References palloc(), and repalloc().

Referenced by ReorderBufferAddDistributedInvalidations(), and ReorderBufferAddInvalidations().

◆ ReorderBufferAddDistributedInvalidations()

void ReorderBufferAddDistributedInvalidations ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  lsn,
Size  nmsgs,
SharedInvalidationMessage msgs 
)

Definition at line 3584 of file reorderbuffer.c.

3587{
3588 ReorderBufferTXN *txn;
3589 MemoryContext oldcontext;
3590
3591 txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
3592
3593 oldcontext = MemoryContextSwitchTo(rb->context);
3594
3595 /*
3596 * Collect all the invalidations under the top transaction, if available,
3597 * so that we can execute them all together. See comments
3598 * ReorderBufferAddInvalidations.
3599 */
3600 txn = rbtxn_get_toptxn(txn);
3601
3602 Assert(nmsgs > 0);
3603
3605 {
3606 /*
3607 * Check the transaction has enough space for storing distributed
3608 * invalidation messages.
3609 */
3611 {
3612 /*
3613 * Mark the invalidation message as overflowed and free up the
3614 * messages accumulated so far.
3615 */
3617
3619 {
3621 txn->invalidations_distributed = NULL;
3623 }
3624 }
3625 else
3628 msgs, nmsgs);
3629 }
3630
3631 /* Queue the invalidation messages into the transaction */
3632 ReorderBufferQueueInvalidations(rb, xid, lsn, nmsgs, msgs);
3633
3634 MemoryContextSwitchTo(oldcontext);
3635}
void pfree(void *pointer)
Definition: mcxt.c:1594
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
static void ReorderBufferAccumulateInvalidations(SharedInvalidationMessage **invals_out, uint32 *ninvals_out, SharedInvalidationMessage *msgs_new, Size nmsgs_new)
#define MAX_DISTR_INVAL_MSG_PER_TXN
static void ReorderBufferQueueInvalidations(ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn, Size nmsgs, SharedInvalidationMessage *msgs)
#define rbtxn_get_toptxn(txn)
#define RBTXN_DISTR_INVAL_OVERFLOWED
#define rbtxn_distr_inval_overflowed(txn)
uint32 ninvalidations_distributed
SharedInvalidationMessage * invalidations_distributed
MemoryContext context

References Assert(), ReorderBuffer::context, ReorderBufferTXN::invalidations_distributed, MAX_DISTR_INVAL_MSG_PER_TXN, MemoryContextSwitchTo(), ReorderBufferTXN::ninvalidations_distributed, pfree(), RBTXN_DISTR_INVAL_OVERFLOWED, rbtxn_distr_inval_overflowed, rbtxn_get_toptxn, ReorderBufferAccumulateInvalidations(), ReorderBufferQueueInvalidations(), ReorderBufferTXNByXid(), and ReorderBufferTXN::txn_flags.

Referenced by SnapBuildDistributeSnapshotAndInval().

◆ ReorderBufferAddInvalidations()

void ReorderBufferAddInvalidations ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  lsn,
Size  nmsgs,
SharedInvalidationMessage msgs 
)

Definition at line 3543 of file reorderbuffer.c.

3546{
3547 ReorderBufferTXN *txn;
3548 MemoryContext oldcontext;
3549
3550 txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
3551
3552 oldcontext = MemoryContextSwitchTo(rb->context);
3553
3554 /*
3555 * Collect all the invalidations under the top transaction, if available,
3556 * so that we can execute them all together. See comments atop this
3557 * function.
3558 */
3559 txn = rbtxn_get_toptxn(txn);
3560
3561 Assert(nmsgs > 0);
3562
3564 &txn->ninvalidations,
3565 msgs, nmsgs);
3566
3567 ReorderBufferQueueInvalidations(rb, xid, lsn, nmsgs, msgs);
3568
3569 MemoryContextSwitchTo(oldcontext);
3570}

References Assert(), ReorderBuffer::context, ReorderBufferTXN::invalidations, MemoryContextSwitchTo(), ReorderBufferTXN::ninvalidations, rbtxn_get_toptxn, ReorderBufferAccumulateInvalidations(), ReorderBufferQueueInvalidations(), and ReorderBufferTXNByXid().

Referenced by xact_decode().

◆ ReorderBufferAddNewCommandId()

void ReorderBufferAddNewCommandId ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  lsn,
CommandId  cid 
)

Definition at line 3356 of file reorderbuffer.c.

3358{
3360
3361 change->data.command_id = cid;
3363
3364 ReorderBufferQueueChange(rb, xid, lsn, change, false);
3365}
void ReorderBufferQueueChange(ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn, ReorderBufferChange *change, bool toast_insert)
ReorderBufferChange * ReorderBufferAllocChange(ReorderBuffer *rb)
@ REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID
Definition: reorderbuffer.h:58
ReorderBufferChangeType action
Definition: reorderbuffer.h:81
union ReorderBufferChange::@113 data

References ReorderBufferChange::action, ReorderBufferChange::command_id, ReorderBufferChange::data, REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID, ReorderBufferAllocChange(), and ReorderBufferQueueChange().

Referenced by SnapBuildProcessNewCid().

◆ ReorderBufferAddNewTupleCids()

void ReorderBufferAddNewTupleCids ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  lsn,
RelFileLocator  locator,
ItemPointerData  tid,
CommandId  cmin,
CommandId  cmax,
CommandId  combocid 
)

Definition at line 3455 of file reorderbuffer.c.

3459{
3461 ReorderBufferTXN *txn;
3462
3463 txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
3464
3465 change->data.tuplecid.locator = locator;
3466 change->data.tuplecid.tid = tid;
3467 change->data.tuplecid.cmin = cmin;
3468 change->data.tuplecid.cmax = cmax;
3469 change->data.tuplecid.combocid = combocid;
3470 change->lsn = lsn;
3471 change->txn = txn;
3473
3474 dlist_push_tail(&txn->tuplecids, &change->node);
3475 txn->ntuplecids++;
3476}
static void dlist_push_tail(dlist_head *head, dlist_node *node)
Definition: ilist.h:364
@ REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID
Definition: reorderbuffer.h:59
struct ReorderBufferChange::@113::@117 tuplecid
ItemPointerData tid
struct ReorderBufferTXN * txn
Definition: reorderbuffer.h:84
RelFileLocator locator
dlist_head tuplecids

References ReorderBufferChange::action, ReorderBufferChange::cmax, ReorderBufferChange::cmin, ReorderBufferChange::combocid, ReorderBufferChange::data, dlist_push_tail(), ReorderBufferChange::locator, ReorderBufferChange::lsn, ReorderBufferChange::node, ReorderBufferTXN::ntuplecids, REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID, ReorderBufferAllocChange(), ReorderBufferTXNByXid(), ReorderBufferChange::tid, ReorderBufferChange::tuplecid, ReorderBufferTXN::tuplecids, and ReorderBufferChange::txn.

Referenced by SnapBuildProcessNewCid().

◆ ReorderBufferAddSnapshot()

void ReorderBufferAddSnapshot ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  lsn,
Snapshot  snap 
)

◆ ReorderBufferAllocate()

ReorderBuffer * ReorderBufferAllocate ( void  )

Definition at line 324 of file reorderbuffer.c.

325{
326 ReorderBuffer *buffer;
327 HASHCTL hash_ctl;
328 MemoryContext new_ctx;
329
330 Assert(MyReplicationSlot != NULL);
331
332 /* allocate memory in own context, to have better accountability */
334 "ReorderBuffer",
336
337 buffer =
338 (ReorderBuffer *) MemoryContextAlloc(new_ctx, sizeof(ReorderBuffer));
339
340 memset(&hash_ctl, 0, sizeof(hash_ctl));
341
342 buffer->context = new_ctx;
343
344 buffer->change_context = SlabContextCreate(new_ctx,
345 "Change",
347 sizeof(ReorderBufferChange));
348
349 buffer->txn_context = SlabContextCreate(new_ctx,
350 "TXN",
352 sizeof(ReorderBufferTXN));
353
354 /*
355 * To minimize memory fragmentation caused by long-running transactions
356 * with changes spanning multiple memory blocks, we use a single
357 * fixed-size memory block for decoded tuple storage. The performance
358 * testing showed that the default memory block size maintains logical
359 * decoding performance without causing fragmentation due to concurrent
360 * transactions. One might think that we can use the max size as
361 * SLAB_LARGE_BLOCK_SIZE but the test also showed it doesn't help resolve
362 * the memory fragmentation.
363 */
364 buffer->tup_context = GenerationContextCreate(new_ctx,
365 "Tuples",
369
370 hash_ctl.keysize = sizeof(TransactionId);
371 hash_ctl.entrysize = sizeof(ReorderBufferTXNByIdEnt);
372 hash_ctl.hcxt = buffer->context;
373
374 buffer->by_txn = hash_create("ReorderBufferByXid", 1000, &hash_ctl,
376
378 buffer->by_txn_last_txn = NULL;
379
380 buffer->outbuf = NULL;
381 buffer->outbufsize = 0;
382 buffer->size = 0;
383
384 /* txn_heap is ordered by transaction size */
386
387 buffer->spillTxns = 0;
388 buffer->spillCount = 0;
389 buffer->spillBytes = 0;
390 buffer->streamTxns = 0;
391 buffer->streamCount = 0;
392 buffer->streamBytes = 0;
393 buffer->totalTxns = 0;
394 buffer->totalBytes = 0;
395
397
398 dlist_init(&buffer->toplevel_by_lsn);
400 dclist_init(&buffer->catchange_txns);
401
402 /*
403 * Ensure there's no stale data from prior uses of this slot, in case some
404 * prior exit avoided calling ReorderBufferFree. Failure to do this can
405 * produce duplicated txns, and it's very cheap if there's nothing there.
406 */
408
409 return buffer;
410}
#define NameStr(name)
Definition: c.h:752
uint32 TransactionId
Definition: c.h:658
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:358
MemoryContext GenerationContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: generation.c:162
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
static void dlist_init(dlist_head *head)
Definition: ilist.h:314
static void dclist_init(dclist_head *head)
Definition: ilist.h:671
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1229
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define SLAB_DEFAULT_BLOCK_SIZE
Definition: memutils.h:189
pairingheap * pairingheap_allocate(pairingheap_comparator compare, void *arg)
Definition: pairingheap.c:42
static int ReorderBufferTXNSizeCompare(const pairingheap_node *a, const pairingheap_node *b, void *arg)
struct ReorderBufferTXNByIdEnt ReorderBufferTXNByIdEnt
static void ReorderBufferCleanupSerializedTXNs(const char *slotname)
MemoryContext SlabContextCreate(MemoryContext parent, const char *name, Size blockSize, Size chunkSize)
Definition: slab.c:322
ReplicationSlot * MyReplicationSlot
Definition: slot.c:148
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
MemoryContext hcxt
Definition: hsearch.h:86
dclist_head catchange_txns
MemoryContext change_context
ReorderBufferTXN * by_txn_last_txn
TransactionId by_txn_last_xid
MemoryContext tup_context
pairingheap * txn_heap
MemoryContext txn_context
XLogRecPtr current_restart_decoding_lsn
ReplicationSlotPersistentData data
Definition: slot.h:192
#define InvalidTransactionId
Definition: transam.h:31

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert(), ReorderBuffer::by_txn, ReorderBuffer::by_txn_last_txn, ReorderBuffer::by_txn_last_xid, ReorderBuffer::catchange_txns, ReorderBuffer::change_context, ReorderBuffer::context, ReorderBuffer::current_restart_decoding_lsn, CurrentMemoryContext, ReplicationSlot::data, dclist_init(), dlist_init(), HASHCTL::entrysize, GenerationContextCreate(), HASH_BLOBS, HASH_CONTEXT, hash_create(), HASH_ELEM, HASHCTL::hcxt, InvalidTransactionId, InvalidXLogRecPtr, HASHCTL::keysize, MemoryContextAlloc(), MyReplicationSlot, ReplicationSlotPersistentData::name, NameStr, ReorderBuffer::outbuf, ReorderBuffer::outbufsize, pairingheap_allocate(), ReorderBufferCleanupSerializedTXNs(), ReorderBufferTXNSizeCompare(), ReorderBuffer::size, SLAB_DEFAULT_BLOCK_SIZE, SlabContextCreate(), ReorderBuffer::spillBytes, ReorderBuffer::spillCount, ReorderBuffer::spillTxns, ReorderBuffer::streamBytes, ReorderBuffer::streamCount, ReorderBuffer::streamTxns, ReorderBuffer::toplevel_by_lsn, ReorderBuffer::totalBytes, ReorderBuffer::totalTxns, ReorderBuffer::tup_context, ReorderBuffer::txn_context, ReorderBuffer::txn_heap, and ReorderBuffer::txns_by_base_snapshot_lsn.

Referenced by StartupDecodingContext().

◆ ReorderBufferAllocChange()

◆ ReorderBufferAllocRelids()

Oid * ReorderBufferAllocRelids ( ReorderBuffer rb,
int  nrelids 
)

Definition at line 624 of file reorderbuffer.c.

625{
626 Oid *relids;
627 Size alloc_len;
628
629 alloc_len = sizeof(Oid) * nrelids;
630
631 relids = (Oid *) MemoryContextAlloc(rb->context, alloc_len);
632
633 return relids;
634}
size_t Size
Definition: c.h:611
unsigned int Oid
Definition: postgres_ext.h:32

References ReorderBuffer::context, and MemoryContextAlloc().

Referenced by DecodeTruncate(), and ReorderBufferRestoreChange().

◆ ReorderBufferAllocTupleBuf()

HeapTuple ReorderBufferAllocTupleBuf ( ReorderBuffer rb,
Size  tuple_len 
)

Definition at line 591 of file reorderbuffer.c.

592{
593 HeapTuple tuple;
594 Size alloc_len;
595
596 alloc_len = tuple_len + SizeofHeapTupleHeader;
597
599 HEAPTUPLESIZE + alloc_len);
600 tuple->t_data = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
601
602 return tuple;
603}
#define HEAPTUPLESIZE
Definition: htup.h:73
HeapTupleData * HeapTuple
Definition: htup.h:71
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define SizeofHeapTupleHeader
Definition: htup_details.h:185
HeapTupleHeader t_data
Definition: htup.h:68

References HEAPTUPLESIZE, MemoryContextAlloc(), SizeofHeapTupleHeader, HeapTupleData::t_data, and ReorderBuffer::tup_context.

Referenced by DecodeDelete(), DecodeInsert(), DecodeMultiInsert(), DecodeUpdate(), and ReorderBufferRestoreChange().

◆ ReorderBufferAllocTXN()

static ReorderBufferTXN * ReorderBufferAllocTXN ( ReorderBuffer rb)
static

Definition at line 434 of file reorderbuffer.c.

435{
436 ReorderBufferTXN *txn;
437
438 txn = (ReorderBufferTXN *)
440
441 memset(txn, 0, sizeof(ReorderBufferTXN));
442
443 dlist_init(&txn->changes);
444 dlist_init(&txn->tuplecids);
445 dlist_init(&txn->subtxns);
446
447 /* InvalidCommandId is not zero, so set it explicitly */
449 txn->output_plugin_private = NULL;
450
451 return txn;
452}
CommandId command_id
void * output_plugin_private
dlist_head subtxns

References ReorderBufferTXN::changes, ReorderBufferTXN::command_id, dlist_init(), InvalidCommandId, MemoryContextAlloc(), ReorderBufferTXN::output_plugin_private, ReorderBufferTXN::subtxns, ReorderBufferTXN::tuplecids, and ReorderBuffer::txn_context.

Referenced by ReorderBufferTXNByXid().

◆ ReorderBufferApplyChange()

static void ReorderBufferApplyChange ( ReorderBuffer rb,
ReorderBufferTXN txn,
Relation  relation,
ReorderBufferChange change,
bool  streaming 
)
inlinestatic

Definition at line 2071 of file reorderbuffer.c.

2074{
2075 if (streaming)
2076 rb->stream_change(rb, txn, relation, change);
2077 else
2078 rb->apply_change(rb, txn, relation, change);
2079}
ReorderBufferStreamChangeCB stream_change
ReorderBufferApplyChangeCB apply_change

References ReorderBuffer::apply_change, and ReorderBuffer::stream_change.

Referenced by ReorderBufferProcessTXN().

◆ ReorderBufferApplyMessage()

static void ReorderBufferApplyMessage ( ReorderBuffer rb,
ReorderBufferTXN txn,
ReorderBufferChange change,
bool  streaming 
)
inlinestatic

Definition at line 2099 of file reorderbuffer.c.

2101{
2102 if (streaming)
2103 rb->stream_message(rb, txn, change->lsn, true,
2104 change->data.msg.prefix,
2105 change->data.msg.message_size,
2106 change->data.msg.message);
2107 else
2108 rb->message(rb, txn, change->lsn, true,
2109 change->data.msg.prefix,
2110 change->data.msg.message_size,
2111 change->data.msg.message);
2112}
struct ReorderBufferChange::@113::@116 msg
ReorderBufferStreamMessageCB stream_message
ReorderBufferMessageCB message

References ReorderBufferChange::data, ReorderBufferChange::lsn, ReorderBufferChange::message, ReorderBuffer::message, ReorderBufferChange::message_size, ReorderBufferChange::msg, ReorderBufferChange::prefix, and ReorderBuffer::stream_message.

Referenced by ReorderBufferProcessTXN().

◆ ReorderBufferApplyTruncate()

static void ReorderBufferApplyTruncate ( ReorderBuffer rb,
ReorderBufferTXN txn,
int  nrelations,
Relation relations,
ReorderBufferChange change,
bool  streaming 
)
inlinestatic

Definition at line 2085 of file reorderbuffer.c.

2088{
2089 if (streaming)
2090 rb->stream_truncate(rb, txn, nrelations, relations, change);
2091 else
2092 rb->apply_truncate(rb, txn, nrelations, relations, change);
2093}
ReorderBufferStreamTruncateCB stream_truncate
ReorderBufferApplyTruncateCB apply_truncate

References ReorderBuffer::apply_truncate, and ReorderBuffer::stream_truncate.

Referenced by ReorderBufferProcessTXN().

◆ ReorderBufferAssignChild()

void ReorderBufferAssignChild ( ReorderBuffer rb,
TransactionId  xid,
TransactionId  subxid,
XLogRecPtr  lsn 
)

Definition at line 1098 of file reorderbuffer.c.

1100{
1101 ReorderBufferTXN *txn;
1102 ReorderBufferTXN *subtxn;
1103 bool new_top;
1104 bool new_sub;
1105
1106 txn = ReorderBufferTXNByXid(rb, xid, true, &new_top, lsn, true);
1107 subtxn = ReorderBufferTXNByXid(rb, subxid, true, &new_sub, lsn, false);
1108
1109 if (!new_sub)
1110 {
1111 if (rbtxn_is_known_subxact(subtxn))
1112 {
1113 /* already associated, nothing to do */
1114 return;
1115 }
1116 else
1117 {
1118 /*
1119 * We already saw this transaction, but initially added it to the
1120 * list of top-level txns. Now that we know it's not top-level,
1121 * remove it from there.
1122 */
1123 dlist_delete(&subtxn->node);
1124 }
1125 }
1126
1127 subtxn->txn_flags |= RBTXN_IS_SUBXACT;
1128 subtxn->toplevel_xid = xid;
1129 Assert(subtxn->nsubtxns == 0);
1130
1131 /* set the reference to top-level transaction */
1132 subtxn->toptxn = txn;
1133
1134 /* add to subtransaction list */
1135 dlist_push_tail(&txn->subtxns, &subtxn->node);
1136 txn->nsubtxns++;
1137
1138 /* Possibly transfer the subtxn's snapshot to its top-level txn. */
1140
1141 /* Verify LSN-ordering invariant */
1143}
static void dlist_delete(dlist_node *node)
Definition: ilist.h:405
static void AssertTXNLsnOrder(ReorderBuffer *rb)
static void ReorderBufferTransferSnapToParent(ReorderBufferTXN *txn, ReorderBufferTXN *subtxn)
#define RBTXN_IS_SUBXACT
TransactionId toplevel_xid
struct ReorderBufferTXN * toptxn

References Assert(), AssertTXNLsnOrder(), dlist_delete(), dlist_push_tail(), ReorderBufferTXN::node, ReorderBufferTXN::nsubtxns, rbtxn_is_known_subxact, RBTXN_IS_SUBXACT, ReorderBufferTransferSnapToParent(), ReorderBufferTXNByXid(), ReorderBufferTXN::subtxns, ReorderBufferTXN::toplevel_xid, ReorderBufferTXN::toptxn, and ReorderBufferTXN::txn_flags.

Referenced by LogicalDecodingProcessRecord(), and ReorderBufferCommitChild().

◆ ReorderBufferBuildTupleCidHash()

static void ReorderBufferBuildTupleCidHash ( ReorderBuffer rb,
ReorderBufferTXN txn 
)
static

Definition at line 1835 of file reorderbuffer.c.

1836{
1837 dlist_iter iter;
1838 HASHCTL hash_ctl;
1839
1841 return;
1842
1843 hash_ctl.keysize = sizeof(ReorderBufferTupleCidKey);
1844 hash_ctl.entrysize = sizeof(ReorderBufferTupleCidEnt);
1845 hash_ctl.hcxt = rb->context;
1846
1847 /*
1848 * create the hash with the exact number of to-be-stored tuplecids from
1849 * the start
1850 */
1851 txn->tuplecid_hash =
1852 hash_create("ReorderBufferTupleCid", txn->ntuplecids, &hash_ctl,
1854
1855 dlist_foreach(iter, &txn->tuplecids)
1856 {
1859 bool found;
1860 ReorderBufferChange *change;
1861
1862 change = dlist_container(ReorderBufferChange, node, iter.cur);
1863
1865
1866 /* be careful about padding */
1867 memset(&key, 0, sizeof(ReorderBufferTupleCidKey));
1868
1869 key.rlocator = change->data.tuplecid.locator;
1870
1872 &key.tid);
1873
1874 ent = (ReorderBufferTupleCidEnt *)
1875 hash_search(txn->tuplecid_hash, &key, HASH_ENTER, &found);
1876 if (!found)
1877 {
1878 ent->cmin = change->data.tuplecid.cmin;
1879 ent->cmax = change->data.tuplecid.cmax;
1880 ent->combocid = change->data.tuplecid.combocid;
1881 }
1882 else
1883 {
1884 /*
1885 * Maybe we already saw this tuple before in this transaction, but
1886 * if so it must have the same cmin.
1887 */
1888 Assert(ent->cmin == change->data.tuplecid.cmin);
1889
1890 /*
1891 * cmax may be initially invalid, but once set it can only grow,
1892 * and never become invalid again.
1893 */
1894 Assert((ent->cmax == InvalidCommandId) ||
1895 ((change->data.tuplecid.cmax != InvalidCommandId) &&
1896 (change->data.tuplecid.cmax > ent->cmax)));
1897 ent->cmax = change->data.tuplecid.cmax;
1898 }
1899 }
1900}
static bool dlist_is_empty(const dlist_head *head)
Definition: ilist.h:336
struct ReorderBufferTupleCidEnt ReorderBufferTupleCidEnt
struct ReorderBufferTupleCidKey ReorderBufferTupleCidKey
#define rbtxn_has_catalog_changes(txn)

References ReorderBufferChange::action, Assert(), ReorderBufferTupleCidEnt::cmax, ReorderBufferChange::cmax, ReorderBufferTupleCidEnt::cmin, ReorderBufferChange::cmin, ReorderBufferTupleCidEnt::combocid, ReorderBufferChange::combocid, ReorderBuffer::context, dlist_iter::cur, ReorderBufferChange::data, dlist_container, dlist_foreach, dlist_is_empty(), HASHCTL::entrysize, HASH_BLOBS, HASH_CONTEXT, hash_create(), HASH_ELEM, HASH_ENTER, hash_search(), HASHCTL::hcxt, InvalidCommandId, ItemPointerCopy(), sort-test::key, HASHCTL::keysize, ReorderBufferChange::locator, ReorderBufferTXN::ntuplecids, rbtxn_has_catalog_changes, REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID, ReorderBufferChange::tid, ReorderBufferChange::tuplecid, ReorderBufferTXN::tuplecid_hash, and ReorderBufferTXN::tuplecids.

Referenced by ReorderBufferProcessTXN().

◆ ReorderBufferCanStartStreaming()

static bool ReorderBufferCanStartStreaming ( ReorderBuffer rb)
inlinestatic

Definition at line 4297 of file reorderbuffer.c.

4298{
4300 SnapBuild *builder = ctx->snapshot_builder;
4301
4302 /* We can't start streaming unless a consistent state is reached. */
4304 return false;
4305
4306 /*
4307 * We can't start streaming immediately even if the streaming is enabled
4308 * because we previously decoded this transaction and now just are
4309 * restarting.
4310 */
4311 if (ReorderBufferCanStream(rb) &&
4312 !SnapBuildXactNeedsSkip(builder, ctx->reader->ReadRecPtr))
4313 return true;
4314
4315 return false;
4316}
static bool ReorderBufferCanStream(ReorderBuffer *rb)
SnapBuildState SnapBuildCurrentState(SnapBuild *builder)
Definition: snapbuild.c:277
@ SNAPBUILD_CONSISTENT
Definition: snapbuild.h:50
XLogRecPtr ReadRecPtr
Definition: xlogreader.h:206

References ReorderBuffer::private_data, LogicalDecodingContext::reader, XLogReaderState::ReadRecPtr, ReorderBufferCanStream(), SNAPBUILD_CONSISTENT, SnapBuildCurrentState(), SnapBuildXactNeedsSkip(), and LogicalDecodingContext::snapshot_builder.

Referenced by ReorderBufferCheckMemoryLimit(), and ReorderBufferProcessPartialChange().

◆ ReorderBufferCanStream()

static bool ReorderBufferCanStream ( ReorderBuffer rb)
inlinestatic

◆ ReorderBufferChangeMemoryUpdate()

static void ReorderBufferChangeMemoryUpdate ( ReorderBuffer rb,
ReorderBufferChange change,
ReorderBufferTXN txn,
bool  addition,
Size  sz 
)
static

Definition at line 3384 of file reorderbuffer.c.

3388{
3389 ReorderBufferTXN *toptxn;
3390
3391 Assert(txn || change);
3392
3393 /*
3394 * Ignore tuple CID changes, because those are not evicted when reaching
3395 * memory limit. So we just don't count them, because it might easily
3396 * trigger a pointless attempt to spill.
3397 */
3398 if (change && change->action == REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID)
3399 return;
3400
3401 if (sz == 0)
3402 return;
3403
3404 if (txn == NULL)
3405 txn = change->txn;
3406 Assert(txn != NULL);
3407
3408 /*
3409 * Update the total size in top level as well. This is later used to
3410 * compute the decoding stats.
3411 */
3412 toptxn = rbtxn_get_toptxn(txn);
3413
3414 if (addition)
3415 {
3416 Size oldsize = txn->size;
3417
3418 txn->size += sz;
3419 rb->size += sz;
3420
3421 /* Update the total size in the top transaction. */
3422 toptxn->total_size += sz;
3423
3424 /* Update the max-heap */
3425 if (oldsize != 0)
3427 pairingheap_add(rb->txn_heap, &txn->txn_node);
3428 }
3429 else
3430 {
3431 Assert((rb->size >= sz) && (txn->size >= sz));
3432 txn->size -= sz;
3433 rb->size -= sz;
3434
3435 /* Update the total size in the top transaction. */
3436 toptxn->total_size -= sz;
3437
3438 /* Update the max-heap */
3440 if (txn->size != 0)
3441 pairingheap_add(rb->txn_heap, &txn->txn_node);
3442 }
3443
3444 Assert(txn->size <= rb->size);
3445}
void pairingheap_remove(pairingheap *heap, pairingheap_node *node)
Definition: pairingheap.c:170
void pairingheap_add(pairingheap *heap, pairingheap_node *node)
Definition: pairingheap.c:112
pairingheap_node txn_node

References ReorderBufferChange::action, Assert(), pairingheap_add(), pairingheap_remove(), rbtxn_get_toptxn, REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID, ReorderBufferTXN::size, ReorderBuffer::size, ReorderBufferTXN::total_size, ReorderBufferChange::txn, ReorderBuffer::txn_heap, and ReorderBufferTXN::txn_node.

Referenced by ReorderBufferCleanupTXN(), ReorderBufferFreeChange(), ReorderBufferQueueChange(), ReorderBufferRestoreChange(), ReorderBufferSerializeTXN(), ReorderBufferToastReplace(), and ReorderBufferTruncateTXN().

◆ ReorderBufferChangeSize()

static Size ReorderBufferChangeSize ( ReorderBufferChange change)
static

Definition at line 4440 of file reorderbuffer.c.

4441{
4442 Size sz = sizeof(ReorderBufferChange);
4443
4444 switch (change->action)
4445 {
4446 /* fall through these, they're all similar enough */
4451 {
4452 HeapTuple oldtup,
4453 newtup;
4454 Size oldlen = 0;
4455 Size newlen = 0;
4456
4457 oldtup = change->data.tp.oldtuple;
4458 newtup = change->data.tp.newtuple;
4459
4460 if (oldtup)
4461 {
4462 sz += sizeof(HeapTupleData);
4463 oldlen = oldtup->t_len;
4464 sz += oldlen;
4465 }
4466
4467 if (newtup)
4468 {
4469 sz += sizeof(HeapTupleData);
4470 newlen = newtup->t_len;
4471 sz += newlen;
4472 }
4473
4474 break;
4475 }
4477 {
4478 Size prefix_size = strlen(change->data.msg.prefix) + 1;
4479
4480 sz += prefix_size + change->data.msg.message_size +
4481 sizeof(Size) + sizeof(Size);
4482
4483 break;
4484 }
4486 {
4487 sz += sizeof(SharedInvalidationMessage) *
4488 change->data.inval.ninvalidations;
4489 break;
4490 }
4492 {
4493 Snapshot snap;
4494
4495 snap = change->data.snapshot;
4496
4497 sz += sizeof(SnapshotData) +
4498 sizeof(TransactionId) * snap->xcnt +
4499 sizeof(TransactionId) * snap->subxcnt;
4500
4501 break;
4502 }
4504 {
4505 sz += sizeof(Oid) * change->data.truncate.nrelids;
4506
4507 break;
4508 }
4513 /* ReorderBufferChange contains everything important */
4514 break;
4515 }
4516
4517 return sz;
4518}
struct HeapTupleData HeapTupleData
struct ReorderBufferChange ReorderBufferChange
@ REORDER_BUFFER_CHANGE_INVALIDATION
Definition: reorderbuffer.h:56
@ REORDER_BUFFER_CHANGE_MESSAGE
Definition: reorderbuffer.h:55
@ REORDER_BUFFER_CHANGE_TRUNCATE
Definition: reorderbuffer.h:63
@ REORDER_BUFFER_CHANGE_DELETE
Definition: reorderbuffer.h:54
struct SnapshotData SnapshotData
uint32 t_len
Definition: htup.h:64
struct ReorderBufferChange::@113::@115 truncate
struct ReorderBufferChange::@113::@118 inval
struct ReorderBufferChange::@113::@114 tp
int32 subxcnt
Definition: snapshot.h:177
uint32 xcnt
Definition: snapshot.h:165

References ReorderBufferChange::action, ReorderBufferChange::data, ReorderBufferChange::inval, ReorderBufferChange::message_size, ReorderBufferChange::msg, ReorderBufferChange::newtuple, ReorderBufferChange::ninvalidations, ReorderBufferChange::nrelids, ReorderBufferChange::oldtuple, ReorderBufferChange::prefix, REORDER_BUFFER_CHANGE_DELETE, REORDER_BUFFER_CHANGE_INSERT, REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID, REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT, REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID, REORDER_BUFFER_CHANGE_INVALIDATION, REORDER_BUFFER_CHANGE_MESSAGE, REORDER_BUFFER_CHANGE_TRUNCATE, REORDER_BUFFER_CHANGE_UPDATE, ReorderBufferChange::snapshot, SnapshotData::subxcnt, HeapTupleData::t_len, ReorderBufferChange::tp, ReorderBufferChange::truncate, and SnapshotData::xcnt.

Referenced by ReorderBufferCleanupTXN(), ReorderBufferFreeChange(), ReorderBufferQueueChange(), ReorderBufferRestoreChange(), ReorderBufferToastReplace(), and ReorderBufferTruncateTXN().

◆ ReorderBufferCheckAndTruncateAbortedTXN()

static bool ReorderBufferCheckAndTruncateAbortedTXN ( ReorderBuffer rb,
ReorderBufferTXN txn 
)
static

Definition at line 1773 of file reorderbuffer.c.

1774{
1775 /* Quick return for regression tests */
1777 return false;
1778
1779 /*
1780 * Quick return if the transaction status is already known.
1781 */
1782
1783 if (rbtxn_is_committed(txn))
1784 return false;
1785 if (rbtxn_is_aborted(txn))
1786 {
1787 /* Already-aborted transactions should not have any changes */
1788 Assert(txn->size == 0);
1789
1790 return true;
1791 }
1792
1793 /* Otherwise, check the transaction status using CLOG lookup */
1794
1796 return false;
1797
1798 if (TransactionIdDidCommit(txn->xid))
1799 {
1800 /*
1801 * Remember the transaction is committed so that we can skip CLOG
1802 * check next time, avoiding the pressure on CLOG lookup.
1803 */
1804 Assert(!rbtxn_is_aborted(txn));
1806 return false;
1807 }
1808
1809 /*
1810 * The transaction aborted. We discard both the changes collected so far
1811 * and the toast reconstruction data. The full cleanup will happen as part
1812 * of decoding ABORT record of this transaction.
1813 */
1815 ReorderBufferToastReset(rb, txn);
1816
1817 /* All changes should be discarded */
1818 Assert(txn->size == 0);
1819
1820 /*
1821 * Mark the transaction as aborted so we can ignore future changes of this
1822 * transaction.
1823 */
1826
1827 return true;
1828}
#define unlikely(x)
Definition: c.h:403
bool TransactionIdIsInProgress(TransactionId xid)
Definition: procarray.c:1402
int debug_logical_replication_streaming
static void ReorderBufferTruncateTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, bool txn_prepared)
static void ReorderBufferToastReset(ReorderBuffer *rb, ReorderBufferTXN *txn)
#define rbtxn_is_committed(txn)
@ DEBUG_LOGICAL_REP_STREAMING_IMMEDIATE
Definition: reorderbuffer.h:34
#define rbtxn_is_prepared(txn)
#define RBTXN_IS_COMMITTED
#define rbtxn_is_aborted(txn)
#define RBTXN_IS_ABORTED
bool TransactionIdDidCommit(TransactionId transactionId)
Definition: transam.c:126

References Assert(), DEBUG_LOGICAL_REP_STREAMING_IMMEDIATE, debug_logical_replication_streaming, RBTXN_IS_ABORTED, rbtxn_is_aborted, RBTXN_IS_COMMITTED, rbtxn_is_committed, rbtxn_is_prepared, ReorderBufferToastReset(), ReorderBufferTruncateTXN(), ReorderBufferTXN::size, TransactionIdDidCommit(), TransactionIdIsInProgress(), ReorderBufferTXN::txn_flags, unlikely, and ReorderBufferTXN::xid.

Referenced by ReorderBufferCheckMemoryLimit().

◆ ReorderBufferCheckMemoryLimit()

static void ReorderBufferCheckMemoryLimit ( ReorderBuffer rb)
static

Definition at line 3898 of file reorderbuffer.c.

3899{
3900 ReorderBufferTXN *txn;
3901
3902 /*
3903 * Bail out if debug_logical_replication_streaming is buffered and we
3904 * haven't exceeded the memory limit.
3905 */
3907 rb->size < logical_decoding_work_mem * (Size) 1024)
3908 return;
3909
3910 /*
3911 * If debug_logical_replication_streaming is immediate, loop until there's
3912 * no change. Otherwise, loop until we reach under the memory limit. One
3913 * might think that just by evicting the largest (sub)transaction we will
3914 * come under the memory limit based on assumption that the selected
3915 * transaction is at least as large as the most recent change (which
3916 * caused us to go over the memory limit). However, that is not true
3917 * because a user can reduce the logical_decoding_work_mem to a smaller
3918 * value before the most recent change.
3919 */
3920 while (rb->size >= logical_decoding_work_mem * (Size) 1024 ||
3922 rb->size > 0))
3923 {
3924 /*
3925 * Pick the largest non-aborted transaction and evict it from memory
3926 * by streaming, if possible. Otherwise, spill to disk.
3927 */
3929 (txn = ReorderBufferLargestStreamableTopTXN(rb)) != NULL)
3930 {
3931 /* we know there has to be one, because the size is not zero */
3932 Assert(txn && rbtxn_is_toptxn(txn));
3933 Assert(txn->total_size > 0);
3934 Assert(rb->size >= txn->total_size);
3935
3936 /* skip the transaction if aborted */
3938 continue;
3939
3940 ReorderBufferStreamTXN(rb, txn);
3941 }
3942 else
3943 {
3944 /*
3945 * Pick the largest transaction (or subtransaction) and evict it
3946 * from memory by serializing it to disk.
3947 */
3948 txn = ReorderBufferLargestTXN(rb);
3949
3950 /* we know there has to be one, because the size is not zero */
3951 Assert(txn);
3952 Assert(txn->size > 0);
3953 Assert(rb->size >= txn->size);
3954
3955 /* skip the transaction if aborted */
3957 continue;
3958
3960 }
3961
3962 /*
3963 * After eviction, the transaction should have no entries in memory,
3964 * and should use 0 bytes for changes.
3965 */
3966 Assert(txn->size == 0);
3967 Assert(txn->nentries_mem == 0);
3968 }
3969
3970 /* We must be under the memory limit now. */
3971 Assert(rb->size < logical_decoding_work_mem * (Size) 1024);
3972}
static ReorderBufferTXN * ReorderBufferLargestTXN(ReorderBuffer *rb)
static bool ReorderBufferCanStartStreaming(ReorderBuffer *rb)
int logical_decoding_work_mem
static ReorderBufferTXN * ReorderBufferLargestStreamableTopTXN(ReorderBuffer *rb)
static bool ReorderBufferCheckAndTruncateAbortedTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
static void ReorderBufferSerializeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
static void ReorderBufferStreamTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
@ DEBUG_LOGICAL_REP_STREAMING_BUFFERED
Definition: reorderbuffer.h:33
#define rbtxn_is_toptxn(txn)

References Assert(), DEBUG_LOGICAL_REP_STREAMING_BUFFERED, DEBUG_LOGICAL_REP_STREAMING_IMMEDIATE, debug_logical_replication_streaming, logical_decoding_work_mem, ReorderBufferTXN::nentries_mem, rbtxn_is_toptxn, ReorderBufferCanStartStreaming(), ReorderBufferCheckAndTruncateAbortedTXN(), ReorderBufferLargestStreamableTopTXN(), ReorderBufferLargestTXN(), ReorderBufferSerializeTXN(), ReorderBufferStreamTXN(), ReorderBufferTXN::size, ReorderBuffer::size, and ReorderBufferTXN::total_size.

Referenced by ReorderBufferQueueChange().

◆ ReorderBufferCleanupSerializedTXNs()

static void ReorderBufferCleanupSerializedTXNs ( const char *  slotname)
static

Definition at line 4865 of file reorderbuffer.c.

4866{
4867 DIR *spill_dir;
4868 struct dirent *spill_de;
4869 struct stat statbuf;
4870 char path[MAXPGPATH * 2 + sizeof(PG_REPLSLOT_DIR)];
4871
4872 sprintf(path, "%s/%s", PG_REPLSLOT_DIR, slotname);
4873
4874 /* we're only handling directories here, skip if it's not ours */
4875 if (lstat(path, &statbuf) == 0 && !S_ISDIR(statbuf.st_mode))
4876 return;
4877
4878 spill_dir = AllocateDir(path);
4879 while ((spill_de = ReadDirExtended(spill_dir, path, INFO)) != NULL)
4880 {
4881 /* only look at names that can be ours */
4882 if (strncmp(spill_de->d_name, "xid", 3) == 0)
4883 {
4884 snprintf(path, sizeof(path),
4885 "%s/%s/%s", PG_REPLSLOT_DIR, slotname,
4886 spill_de->d_name);
4887
4888 if (unlink(path) != 0)
4889 ereport(ERROR,
4891 errmsg("could not remove file \"%s\" during removal of %s/%s/xid*: %m",
4892 path, PG_REPLSLOT_DIR, slotname)));
4893 }
4894 }
4895 FreeDir(spill_dir);
4896}
#define INFO
Definition: elog.h:34
int FreeDir(DIR *dir)
Definition: fd.c:3022
struct dirent * ReadDirExtended(DIR *dir, const char *dirname, int elevel)
Definition: fd.c:2985
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2904
#define snprintf
Definition: port.h:239
#define PG_REPLSLOT_DIR
Definition: slot.h:21
Definition: dirent.c:26
Definition: dirent.h:10
char d_name[MAX_PATH]
Definition: dirent.h:15
#define lstat(path, sb)
Definition: win32_port.h:275
#define S_ISDIR(m)
Definition: win32_port.h:315

References AllocateDir(), dirent::d_name, ereport, errcode_for_file_access(), errmsg(), ERROR, FreeDir(), INFO, lstat, MAXPGPATH, PG_REPLSLOT_DIR, ReadDirExtended(), S_ISDIR, snprintf, sprintf, and stat::st_mode.

Referenced by ReorderBufferAllocate(), ReorderBufferFree(), and StartupReorderBuffer().

◆ ReorderBufferCleanupTXN()

static void ReorderBufferCleanupTXN ( ReorderBuffer rb,
ReorderBufferTXN txn 
)
static

Definition at line 1534 of file reorderbuffer.c.

1535{
1536 bool found;
1537 dlist_mutable_iter iter;
1538 Size mem_freed = 0;
1539
1540 /* cleanup subtransactions & their changes */
1541 dlist_foreach_modify(iter, &txn->subtxns)
1542 {
1543 ReorderBufferTXN *subtxn;
1544
1545 subtxn = dlist_container(ReorderBufferTXN, node, iter.cur);
1546
1547 /*
1548 * Subtransactions are always associated to the toplevel TXN, even if
1549 * they originally were happening inside another subtxn, so we won't
1550 * ever recurse more than one level deep here.
1551 */
1553 Assert(subtxn->nsubtxns == 0);
1554
1555 ReorderBufferCleanupTXN(rb, subtxn);
1556 }
1557
1558 /* cleanup changes in the txn */
1559 dlist_foreach_modify(iter, &txn->changes)
1560 {
1561 ReorderBufferChange *change;
1562
1563 change = dlist_container(ReorderBufferChange, node, iter.cur);
1564
1565 /* Check we're not mixing changes from different transactions. */
1566 Assert(change->txn == txn);
1567
1568 /*
1569 * Instead of updating the memory counter for individual changes, we
1570 * sum up the size of memory to free so we can update the memory
1571 * counter all together below. This saves costs of maintaining the
1572 * max-heap.
1573 */
1574 mem_freed += ReorderBufferChangeSize(change);
1575
1576 ReorderBufferFreeChange(rb, change, false);
1577 }
1578
1579 /* Update the memory counter */
1580 ReorderBufferChangeMemoryUpdate(rb, NULL, txn, false, mem_freed);
1581
1582 /*
1583 * Cleanup the tuplecids we stored for decoding catalog snapshot access.
1584 * They are always stored in the toplevel transaction.
1585 */
1586 dlist_foreach_modify(iter, &txn->tuplecids)
1587 {
1588 ReorderBufferChange *change;
1589
1590 change = dlist_container(ReorderBufferChange, node, iter.cur);
1591
1592 /* Check we're not mixing changes from different transactions. */
1593 Assert(change->txn == txn);
1595
1596 ReorderBufferFreeChange(rb, change, true);
1597 }
1598
1599 /*
1600 * Cleanup the base snapshot, if set.
1601 */
1602 if (txn->base_snapshot != NULL)
1603 {
1606 }
1607
1608 /*
1609 * Cleanup the snapshot for the last streamed run.
1610 */
1611 if (txn->snapshot_now != NULL)
1612 {
1615 }
1616
1617 /*
1618 * Remove TXN from its containing lists.
1619 *
1620 * Note: if txn is known as subxact, we are deleting the TXN from its
1621 * parent's list of known subxacts; this leaves the parent's nsubxacts
1622 * count too high, but we don't care. Otherwise, we are deleting the TXN
1623 * from the LSN-ordered list of toplevel TXNs. We remove the TXN from the
1624 * list of catalog modifying transactions as well.
1625 */
1626 dlist_delete(&txn->node);
1629
1630 /* now remove reference from buffer */
1631 hash_search(rb->by_txn, &txn->xid, HASH_REMOVE, &found);
1632 Assert(found);
1633
1634 /* remove entries spilled to disk */
1635 if (rbtxn_is_serialized(txn))
1637
1638 /* deallocate */
1639 ReorderBufferFreeTXN(rb, txn);
1640}
@ HASH_REMOVE
Definition: hsearch.h:115
static void dclist_delete_from(dclist_head *head, dlist_node *node)
Definition: ilist.h:763
void ReorderBufferFreeChange(ReorderBuffer *rb, ReorderBufferChange *change, bool upd_mem)
static void ReorderBufferRestoreCleanup(ReorderBuffer *rb, ReorderBufferTXN *txn)
static Size ReorderBufferChangeSize(ReorderBufferChange *change)
static void ReorderBufferFreeSnap(ReorderBuffer *rb, Snapshot snap)
static void ReorderBufferFreeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
static void ReorderBufferChangeMemoryUpdate(ReorderBuffer *rb, ReorderBufferChange *change, ReorderBufferTXN *txn, bool addition, Size sz)
#define rbtxn_is_serialized(txn)
void SnapBuildSnapDecRefcount(Snapshot snap)
Definition: snapbuild.c:328
Snapshot snapshot_now
dlist_node catchange_node
dlist_node base_snapshot_node

References ReorderBufferChange::action, Assert(), ReorderBufferTXN::base_snapshot, ReorderBufferTXN::base_snapshot_node, ReorderBuffer::by_txn, ReorderBufferTXN::catchange_node, ReorderBuffer::catchange_txns, ReorderBufferTXN::changes, dlist_mutable_iter::cur, dclist_delete_from(), dlist_container, dlist_delete(), dlist_foreach_modify, HASH_REMOVE, hash_search(), ReorderBufferTXN::node, ReorderBufferTXN::nsubtxns, rbtxn_has_catalog_changes, rbtxn_is_known_subxact, rbtxn_is_serialized, rbtxn_is_streamed, REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID, ReorderBufferChangeMemoryUpdate(), ReorderBufferChangeSize(), ReorderBufferCleanupTXN(), ReorderBufferFreeChange(), ReorderBufferFreeSnap(), ReorderBufferFreeTXN(), ReorderBufferRestoreCleanup(), SnapBuildSnapDecRefcount(), ReorderBufferTXN::snapshot_now, ReorderBufferTXN::subtxns, ReorderBufferTXN::tuplecids, ReorderBufferChange::txn, and ReorderBufferTXN::xid.

Referenced by ReorderBufferAbort(), ReorderBufferAbortOld(), ReorderBufferCleanupTXN(), ReorderBufferFinishPrepared(), ReorderBufferForget(), ReorderBufferProcessTXN(), ReorderBufferReplay(), and ReorderBufferStreamCommit().

◆ ReorderBufferCommit()

void ReorderBufferCommit ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  commit_lsn,
XLogRecPtr  end_lsn,
TimestampTz  commit_time,
RepOriginId  origin_id,
XLogRecPtr  origin_lsn 
)

Definition at line 2883 of file reorderbuffer.c.

2887{
2888 ReorderBufferTXN *txn;
2889
2890 txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr,
2891 false);
2892
2893 /* unknown transaction, nothing to replay */
2894 if (txn == NULL)
2895 return;
2896
2897 ReorderBufferReplay(txn, rb, xid, commit_lsn, end_lsn, commit_time,
2898 origin_id, origin_lsn);
2899}
static void ReorderBufferReplay(ReorderBufferTXN *txn, ReorderBuffer *rb, TransactionId xid, XLogRecPtr commit_lsn, XLogRecPtr end_lsn, TimestampTz commit_time, RepOriginId origin_id, XLogRecPtr origin_lsn)

References InvalidXLogRecPtr, ReorderBufferReplay(), and ReorderBufferTXNByXid().

Referenced by DecodeCommit().

◆ ReorderBufferCommitChild()

void ReorderBufferCommitChild ( ReorderBuffer rb,
TransactionId  xid,
TransactionId  subxid,
XLogRecPtr  commit_lsn,
XLogRecPtr  end_lsn 
)

Definition at line 1218 of file reorderbuffer.c.

1221{
1222 ReorderBufferTXN *subtxn;
1223
1224 subtxn = ReorderBufferTXNByXid(rb, subxid, false, NULL,
1225 InvalidXLogRecPtr, false);
1226
1227 /*
1228 * No need to do anything if that subtxn didn't contain any changes
1229 */
1230 if (!subtxn)
1231 return;
1232
1233 subtxn->final_lsn = commit_lsn;
1234 subtxn->end_lsn = end_lsn;
1235
1236 /*
1237 * Assign this subxact as a child of the toplevel xact (no-op if already
1238 * done.)
1239 */
1241}
void ReorderBufferAssignChild(ReorderBuffer *rb, TransactionId xid, TransactionId subxid, XLogRecPtr lsn)

References ReorderBufferTXN::end_lsn, ReorderBufferTXN::final_lsn, InvalidXLogRecPtr, ReorderBufferAssignChild(), and ReorderBufferTXNByXid().

Referenced by DecodeCommit(), and DecodePrepare().

◆ ReorderBufferCopySnap()

static Snapshot ReorderBufferCopySnap ( ReorderBuffer rb,
Snapshot  orig_snap,
ReorderBufferTXN txn,
CommandId  cid 
)
static

Definition at line 1908 of file reorderbuffer.c.

1910{
1911 Snapshot snap;
1912 dlist_iter iter;
1913 int i = 0;
1914 Size size;
1915
1916 size = sizeof(SnapshotData) +
1917 sizeof(TransactionId) * orig_snap->xcnt +
1918 sizeof(TransactionId) * (txn->nsubtxns + 1);
1919
1920 snap = MemoryContextAllocZero(rb->context, size);
1921 memcpy(snap, orig_snap, sizeof(SnapshotData));
1922
1923 snap->copied = true;
1924 snap->active_count = 1; /* mark as active so nobody frees it */
1925 snap->regd_count = 0;
1926 snap->xip = (TransactionId *) (snap + 1);
1927
1928 memcpy(snap->xip, orig_snap->xip, sizeof(TransactionId) * snap->xcnt);
1929
1930 /*
1931 * snap->subxip contains all txids that belong to our transaction which we
1932 * need to check via cmin/cmax. That's why we store the toplevel
1933 * transaction in there as well.
1934 */
1935 snap->subxip = snap->xip + snap->xcnt;
1936 snap->subxip[i++] = txn->xid;
1937
1938 /*
1939 * txn->nsubtxns isn't decreased when subtransactions abort, so count
1940 * manually. Since it's an upper boundary it is safe to use it for the
1941 * allocation above.
1942 */
1943 snap->subxcnt = 1;
1944
1945 dlist_foreach(iter, &txn->subtxns)
1946 {
1947 ReorderBufferTXN *sub_txn;
1948
1949 sub_txn = dlist_container(ReorderBufferTXN, node, iter.cur);
1950 snap->subxip[i++] = sub_txn->xid;
1951 snap->subxcnt++;
1952 }
1953
1954 /* sort so we can bsearch() later */
1955 qsort(snap->subxip, snap->subxcnt, sizeof(TransactionId), xidComparator);
1956
1957 /* store the specified current CommandId */
1958 snap->curcid = cid;
1959
1960 return snap;
1961}
int i
Definition: isn.c:77
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1263
#define qsort(a, b, c, d)
Definition: port.h:479
bool copied
Definition: snapshot.h:181
uint32 regd_count
Definition: snapshot.h:201
uint32 active_count
Definition: snapshot.h:200
CommandId curcid
Definition: snapshot.h:183
TransactionId * subxip
Definition: snapshot.h:176
TransactionId * xip
Definition: snapshot.h:164
int xidComparator(const void *arg1, const void *arg2)
Definition: xid.c:152

References SnapshotData::active_count, ReorderBuffer::context, SnapshotData::copied, dlist_iter::cur, SnapshotData::curcid, dlist_container, dlist_foreach, i, MemoryContextAllocZero(), ReorderBufferTXN::nsubtxns, qsort, SnapshotData::regd_count, ReorderBufferTXN::subtxns, SnapshotData::subxcnt, SnapshotData::subxip, SnapshotData::xcnt, ReorderBufferTXN::xid, xidComparator(), and SnapshotData::xip.

Referenced by ReorderBufferProcessTXN(), ReorderBufferSaveTXNSnapshot(), and ReorderBufferStreamTXN().

◆ ReorderBufferExecuteInvalidations()

static void ReorderBufferExecuteInvalidations ( uint32  nmsgs,
SharedInvalidationMessage msgs 
)
static

Definition at line 3642 of file reorderbuffer.c.

3643{
3644 int i;
3645
3646 for (i = 0; i < nmsgs; i++)
3648}
void LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
Definition: inval.c:823

References i, and LocalExecuteInvalidationMessage().

Referenced by ReorderBufferFinishPrepared(), and ReorderBufferProcessTXN().

◆ ReorderBufferFinishPrepared()

void ReorderBufferFinishPrepared ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  commit_lsn,
XLogRecPtr  end_lsn,
XLogRecPtr  two_phase_at,
TimestampTz  commit_time,
RepOriginId  origin_id,
XLogRecPtr  origin_lsn,
char *  gid,
bool  is_commit 
)

Definition at line 3000 of file reorderbuffer.c.

3005{
3006 ReorderBufferTXN *txn;
3007 XLogRecPtr prepare_end_lsn;
3008 TimestampTz prepare_time;
3009
3010 txn = ReorderBufferTXNByXid(rb, xid, false, NULL, commit_lsn, false);
3011
3012 /* unknown transaction, nothing to do */
3013 if (txn == NULL)
3014 return;
3015
3016 /*
3017 * By this time the txn has the prepare record information, remember it to
3018 * be later used for rollback.
3019 */
3020 prepare_end_lsn = txn->end_lsn;
3021 prepare_time = txn->xact_time.prepare_time;
3022
3023 /* add the gid in the txn */
3024 txn->gid = pstrdup(gid);
3025
3026 /*
3027 * It is possible that this transaction is not decoded at prepare time
3028 * either because by that time we didn't have a consistent snapshot, or
3029 * two_phase was not enabled, or it was decoded earlier but we have
3030 * restarted. We only need to send the prepare if it was not decoded
3031 * earlier. We don't need to decode the xact for aborts if it is not done
3032 * already.
3033 */
3034 if ((txn->final_lsn < two_phase_at) && is_commit)
3035 {
3036 /*
3037 * txn must have been marked as a prepared transaction and skipped but
3038 * not sent a prepare. Also, the prepare info must have been updated
3039 * in txn even if we skip prepare.
3040 */
3044
3045 /*
3046 * By this time the txn has the prepare record information and it is
3047 * important to use that so that downstream gets the accurate
3048 * information. If instead, we have passed commit information here
3049 * then downstream can behave as it has already replayed commit
3050 * prepared after the restart.
3051 */
3052 ReorderBufferReplay(txn, rb, xid, txn->final_lsn, txn->end_lsn,
3053 txn->xact_time.prepare_time, txn->origin_id, txn->origin_lsn);
3054 }
3055
3056 txn->final_lsn = commit_lsn;
3057 txn->end_lsn = end_lsn;
3058 txn->xact_time.commit_time = commit_time;
3059 txn->origin_id = origin_id;
3060 txn->origin_lsn = origin_lsn;
3061
3062 if (is_commit)
3063 rb->commit_prepared(rb, txn, commit_lsn);
3064 else
3065 rb->rollback_prepared(rb, txn, prepare_end_lsn, prepare_time);
3066
3067 /* cleanup: make sure there's no cache pollution */
3069 txn->invalidations);
3070 ReorderBufferCleanupTXN(rb, txn);
3071}
int64 TimestampTz
Definition: timestamp.h:39
char * pstrdup(const char *in)
Definition: mcxt.c:1759
static void ReorderBufferExecuteInvalidations(uint32 nmsgs, SharedInvalidationMessage *msgs)
#define RBTXN_PREPARE_STATUS_MASK
#define RBTXN_IS_PREPARED
#define RBTXN_SKIPPED_PREPARE
TimestampTz commit_time
RepOriginId origin_id
XLogRecPtr origin_lsn
TimestampTz prepare_time
ReorderBufferCommitPreparedCB commit_prepared
ReorderBufferRollbackPreparedCB rollback_prepared

References Assert(), ReorderBuffer::commit_prepared, ReorderBufferTXN::commit_time, ReorderBufferTXN::end_lsn, ReorderBufferTXN::final_lsn, ReorderBufferTXN::gid, ReorderBufferTXN::invalidations, InvalidXLogRecPtr, ReorderBufferTXN::ninvalidations, ReorderBufferTXN::origin_id, ReorderBufferTXN::origin_lsn, ReorderBufferTXN::prepare_time, pstrdup(), RBTXN_IS_PREPARED, RBTXN_PREPARE_STATUS_MASK, RBTXN_SKIPPED_PREPARE, ReorderBufferCleanupTXN(), ReorderBufferExecuteInvalidations(), ReorderBufferReplay(), ReorderBufferTXNByXid(), ReorderBuffer::rollback_prepared, ReorderBufferTXN::txn_flags, and ReorderBufferTXN::xact_time.

Referenced by DecodeAbort(), and DecodeCommit().

◆ ReorderBufferForget()

void ReorderBufferForget ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  lsn 
)

Definition at line 3179 of file reorderbuffer.c.

3180{
3181 ReorderBufferTXN *txn;
3182
3183 txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr,
3184 false);
3185
3186 /* unknown, nothing to forget */
3187 if (txn == NULL)
3188 return;
3189
3190 /* this transaction mustn't be streamed */
3192
3193 /* cosmetic... */
3194 txn->final_lsn = lsn;
3195
3196 /*
3197 * Process only cache invalidation messages in this transaction if there
3198 * are any. Even if we're not interested in the transaction's contents, it
3199 * could have manipulated the catalog and we need to update the caches
3200 * according to that.
3201 */
3202 if (txn->base_snapshot != NULL && txn->ninvalidations > 0)
3204 txn->invalidations);
3205 else
3206 Assert(txn->ninvalidations == 0);
3207
3208 /* remove potential on-disk data, and deallocate */
3209 ReorderBufferCleanupTXN(rb, txn);
3210}

References Assert(), ReorderBufferTXN::base_snapshot, ReorderBufferTXN::final_lsn, ReorderBufferTXN::invalidations, InvalidXLogRecPtr, ReorderBufferTXN::ninvalidations, rbtxn_is_streamed, ReorderBufferCleanupTXN(), ReorderBufferImmediateInvalidation(), and ReorderBufferTXNByXid().

Referenced by DecodeCommit().

◆ ReorderBufferFree()

void ReorderBufferFree ( ReorderBuffer rb)

Definition at line 416 of file reorderbuffer.c.

417{
418 MemoryContext context = rb->context;
419
420 /*
421 * We free separately allocated data by entirely scrapping reorderbuffer's
422 * memory context.
423 */
424 MemoryContextDelete(context);
425
426 /* Free disk space used by unconsumed reorder buffers */
428}
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:469

References ReorderBuffer::context, ReplicationSlot::data, MemoryContextDelete(), MyReplicationSlot, ReplicationSlotPersistentData::name, NameStr, and ReorderBufferCleanupSerializedTXNs().

Referenced by FreeDecodingContext().

◆ ReorderBufferFreeChange()

void ReorderBufferFreeChange ( ReorderBuffer rb,
ReorderBufferChange change,
bool  upd_mem 
)

Definition at line 521 of file reorderbuffer.c.

523{
524 /* update memory accounting info */
525 if (upd_mem)
526 ReorderBufferChangeMemoryUpdate(rb, change, NULL, false,
528
529 /* free contained data */
530 switch (change->action)
531 {
536 if (change->data.tp.newtuple)
537 {
539 change->data.tp.newtuple = NULL;
540 }
541
542 if (change->data.tp.oldtuple)
543 {
545 change->data.tp.oldtuple = NULL;
546 }
547 break;
549 if (change->data.msg.prefix != NULL)
550 pfree(change->data.msg.prefix);
551 change->data.msg.prefix = NULL;
552 if (change->data.msg.message != NULL)
553 pfree(change->data.msg.message);
554 change->data.msg.message = NULL;
555 break;
557 if (change->data.inval.invalidations)
558 pfree(change->data.inval.invalidations);
559 change->data.inval.invalidations = NULL;
560 break;
562 if (change->data.snapshot)
563 {
565 change->data.snapshot = NULL;
566 }
567 break;
568 /* no data in addition to the struct itself */
570 if (change->data.truncate.relids != NULL)
571 {
573 change->data.truncate.relids = NULL;
574 }
575 break;
580 break;
581 }
582
583 pfree(change);
584}
void ReorderBufferFreeRelids(ReorderBuffer *rb, Oid *relids)
void ReorderBufferFreeTupleBuf(HeapTuple tuple)
SharedInvalidationMessage * invalidations

References ReorderBufferChange::action, ReorderBufferChange::data, ReorderBufferChange::inval, ReorderBufferChange::invalidations, ReorderBufferChange::message, ReorderBufferChange::msg, ReorderBufferChange::newtuple, ReorderBufferChange::oldtuple, pfree(), ReorderBufferChange::prefix, ReorderBufferChange::relids, REORDER_BUFFER_CHANGE_DELETE, REORDER_BUFFER_CHANGE_INSERT, REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID, REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT, REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID, REORDER_BUFFER_CHANGE_INVALIDATION, REORDER_BUFFER_CHANGE_MESSAGE, REORDER_BUFFER_CHANGE_TRUNCATE, REORDER_BUFFER_CHANGE_UPDATE, ReorderBufferChangeMemoryUpdate(), ReorderBufferChangeSize(), ReorderBufferFreeRelids(), ReorderBufferFreeSnap(), ReorderBufferFreeTupleBuf(), ReorderBufferChange::snapshot, ReorderBufferChange::tp, and ReorderBufferChange::truncate.

Referenced by ReorderBufferCleanupTXN(), ReorderBufferIterTXNFinish(), ReorderBufferIterTXNNext(), ReorderBufferProcessTXN(), ReorderBufferQueueChange(), ReorderBufferResetTXN(), ReorderBufferRestoreChanges(), ReorderBufferSerializeTXN(), ReorderBufferToastReset(), and ReorderBufferTruncateTXN().

◆ ReorderBufferFreeRelids()

void ReorderBufferFreeRelids ( ReorderBuffer rb,
Oid relids 
)

Definition at line 640 of file reorderbuffer.c.

641{
642 pfree(relids);
643}

References pfree().

Referenced by ReorderBufferFreeChange().

◆ ReorderBufferFreeSnap()

static void ReorderBufferFreeSnap ( ReorderBuffer rb,
Snapshot  snap 
)
static

Definition at line 1967 of file reorderbuffer.c.

1968{
1969 if (snap->copied)
1970 pfree(snap);
1971 else
1973}

References SnapshotData::copied, pfree(), and SnapBuildSnapDecRefcount().

Referenced by ReorderBufferCleanupTXN(), ReorderBufferFreeChange(), ReorderBufferProcessTXN(), and ReorderBufferStreamTXN().

◆ ReorderBufferFreeTupleBuf()

void ReorderBufferFreeTupleBuf ( HeapTuple  tuple)

Definition at line 609 of file reorderbuffer.c.

610{
611 pfree(tuple);
612}

References pfree().

Referenced by ReorderBufferFreeChange().

◆ ReorderBufferFreeTXN()

static void ReorderBufferFreeTXN ( ReorderBuffer rb,
ReorderBufferTXN txn 
)
static

Definition at line 458 of file reorderbuffer.c.

459{
460 /* clean the lookup cache if we were cached (quite likely) */
461 if (rb->by_txn_last_xid == txn->xid)
462 {
464 rb->by_txn_last_txn = NULL;
465 }
466
467 /* free data that's contained */
468
469 if (txn->gid != NULL)
470 {
471 pfree(txn->gid);
472 txn->gid = NULL;
473 }
474
475 if (txn->tuplecid_hash != NULL)
476 {
478 txn->tuplecid_hash = NULL;
479 }
480
481 if (txn->invalidations)
482 {
483 pfree(txn->invalidations);
484 txn->invalidations = NULL;
485 }
486
488 {
490 txn->invalidations_distributed = NULL;
491 }
492
493 /* Reset the toast hash */
495
496 /* All changes must be deallocated */
497 Assert(txn->size == 0);
498
499 pfree(txn);
500}
void hash_destroy(HTAB *hashp)
Definition: dynahash.c:865

References Assert(), ReorderBuffer::by_txn_last_txn, ReorderBuffer::by_txn_last_xid, ReorderBufferTXN::gid, hash_destroy(), ReorderBufferTXN::invalidations, ReorderBufferTXN::invalidations_distributed, InvalidTransactionId, pfree(), ReorderBufferToastReset(), ReorderBufferTXN::size, ReorderBufferTXN::tuplecid_hash, and ReorderBufferTXN::xid.

Referenced by ReorderBufferCleanupTXN().

◆ ReorderBufferGetCatalogChangesXacts()

TransactionId * ReorderBufferGetCatalogChangesXacts ( ReorderBuffer rb)

Definition at line 3692 of file reorderbuffer.c.

3693{
3694 dlist_iter iter;
3695 TransactionId *xids = NULL;
3696 size_t xcnt = 0;
3697
3698 /* Quick return if the list is empty */
3699 if (dclist_count(&rb->catchange_txns) == 0)
3700 return NULL;
3701
3702 /* Initialize XID array */
3703 xids = (TransactionId *) palloc(sizeof(TransactionId) *
3705 dclist_foreach(iter, &rb->catchange_txns)
3706 {
3708 catchange_node,
3709 iter.cur);
3710
3712
3713 xids[xcnt++] = txn->xid;
3714 }
3715
3716 qsort(xids, xcnt, sizeof(TransactionId), xidComparator);
3717
3718 Assert(xcnt == dclist_count(&rb->catchange_txns));
3719 return xids;
3720}
#define dclist_container(type, membername, ptr)
Definition: ilist.h:947
static uint32 dclist_count(const dclist_head *head)
Definition: ilist.h:932
#define dclist_foreach(iter, lhead)
Definition: ilist.h:970

References Assert(), ReorderBuffer::catchange_txns, dlist_iter::cur, dclist_container, dclist_count(), dclist_foreach, palloc(), qsort, rbtxn_has_catalog_changes, ReorderBufferTXN::xid, and xidComparator().

Referenced by SnapBuildSerialize().

◆ ReorderBufferGetInvalidations()

uint32 ReorderBufferGetInvalidations ( ReorderBuffer rb,
TransactionId  xid,
SharedInvalidationMessage **  msgs 
)

Definition at line 5616 of file reorderbuffer.c.

5618{
5619 ReorderBufferTXN *txn;
5620
5621 txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr,
5622 false);
5623
5624 if (txn == NULL)
5625 return 0;
5626
5627 *msgs = txn->invalidations;
5628
5629 return txn->ninvalidations;
5630}

References ReorderBufferTXN::invalidations, InvalidXLogRecPtr, ReorderBufferTXN::ninvalidations, and ReorderBufferTXNByXid().

Referenced by SnapBuildDistributeSnapshotAndInval().

◆ ReorderBufferGetOldestTXN()

ReorderBufferTXN * ReorderBufferGetOldestTXN ( ReorderBuffer rb)

Definition at line 1043 of file reorderbuffer.c.

1044{
1045 ReorderBufferTXN *txn;
1046
1048
1050 return NULL;
1051
1053
1056 return txn;
1057}
#define dlist_head_element(type, membername, lhead)
Definition: ilist.h:603

References Assert(), AssertTXNLsnOrder(), dlist_head_element, dlist_is_empty(), ReorderBufferTXN::first_lsn, InvalidXLogRecPtr, rbtxn_is_known_subxact, and ReorderBuffer::toplevel_by_lsn.

Referenced by SnapBuildProcessRunningXacts().

◆ ReorderBufferGetOldestXmin()

TransactionId ReorderBufferGetOldestXmin ( ReorderBuffer rb)

◆ ReorderBufferImmediateInvalidation()

void ReorderBufferImmediateInvalidation ( ReorderBuffer rb,
uint32  ninvalidations,
SharedInvalidationMessage invalidations 
)

Definition at line 3252 of file reorderbuffer.c.

3254{
3255 bool use_subtxn = IsTransactionOrTransactionBlock();
3258 int i;
3259
3260 if (use_subtxn)
3262
3263 /*
3264 * Force invalidations to happen outside of a valid transaction - that way
3265 * entries will just be marked as invalid without accessing the catalog.
3266 * That's advantageous because we don't need to setup the full state
3267 * necessary for catalog access.
3268 */
3269 if (use_subtxn)
3271
3272 for (i = 0; i < ninvalidations; i++)
3273 LocalExecuteInvalidationMessage(&invalidations[i]);
3274
3275 if (use_subtxn)
3276 {
3279 CurrentResourceOwner = cowner;
3280 }
3281}
ResourceOwner CurrentResourceOwner
Definition: resowner.c:173
bool IsTransactionOrTransactionBlock(void)
Definition: xact.c:5001
void BeginInternalSubTransaction(const char *name)
Definition: xact.c:4706
void RollbackAndReleaseCurrentSubTransaction(void)
Definition: xact.c:4808
void AbortCurrentTransaction(void)
Definition: xact.c:3463

References AbortCurrentTransaction(), BeginInternalSubTransaction(), CurrentMemoryContext, CurrentResourceOwner, i, IsTransactionOrTransactionBlock(), LocalExecuteInvalidationMessage(), MemoryContextSwitchTo(), and RollbackAndReleaseCurrentSubTransaction().

Referenced by ReorderBufferAbort(), ReorderBufferForget(), ReorderBufferInvalidate(), and xact_decode().

◆ ReorderBufferInvalidate()

void ReorderBufferInvalidate ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  lsn 
)

Definition at line 3221 of file reorderbuffer.c.

3222{
3223 ReorderBufferTXN *txn;
3224
3225 txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr,
3226 false);
3227
3228 /* unknown, nothing to do */
3229 if (txn == NULL)
3230 return;
3231
3232 /*
3233 * Process cache invalidation messages if there are any. Even if we're not
3234 * interested in the transaction's contents, it could have manipulated the
3235 * catalog and we need to update the caches according to that.
3236 */
3237 if (txn->base_snapshot != NULL && txn->ninvalidations > 0)
3239 txn->invalidations);
3240 else
3241 Assert(txn->ninvalidations == 0);
3242}

References Assert(), ReorderBufferTXN::base_snapshot, ReorderBufferTXN::invalidations, InvalidXLogRecPtr, ReorderBufferTXN::ninvalidations, ReorderBufferImmediateInvalidation(), and ReorderBufferTXNByXid().

Referenced by DecodePrepare().

◆ ReorderBufferIterCompare()

static int ReorderBufferIterCompare ( Datum  a,
Datum  b,
void *  arg 
)
static

Definition at line 1260 of file reorderbuffer.c.

1261{
1263 XLogRecPtr pos_a = state->entries[DatumGetInt32(a)].lsn;
1264 XLogRecPtr pos_b = state->entries[DatumGetInt32(b)].lsn;
1265
1266 if (pos_a < pos_b)
1267 return 1;
1268 else if (pos_a == pos_b)
1269 return 0;
1270 return -1;
1271}
void * arg
static int32 DatumGetInt32(Datum X)
Definition: postgres.h:212
Definition: regguts.h:323

References a, arg, b, and DatumGetInt32().

Referenced by ReorderBufferIterTXNInit().

◆ ReorderBufferIterTXNFinish()

static void ReorderBufferIterTXNFinish ( ReorderBuffer rb,
ReorderBufferIterTXNState state 
)
static

Definition at line 1503 of file reorderbuffer.c.

1505{
1506 int32 off;
1507
1508 for (off = 0; off < state->nr_txns; off++)
1509 {
1510 if (state->entries[off].file.vfd != -1)
1511 FileClose(state->entries[off].file.vfd);
1512 }
1513
1514 /* free memory we might have "leaked" in the last *Next call */
1515 if (!dlist_is_empty(&state->old_change))
1516 {
1517 ReorderBufferChange *change;
1518
1519 change = dlist_container(ReorderBufferChange, node,
1520 dlist_pop_head_node(&state->old_change));
1521 ReorderBufferFreeChange(rb, change, true);
1522 Assert(dlist_is_empty(&state->old_change));
1523 }
1524
1525 binaryheap_free(state->heap);
1526 pfree(state);
1527}
void binaryheap_free(binaryheap *heap)
Definition: binaryheap.c:75
void FileClose(File file)
Definition: fd.c:1979
static dlist_node * dlist_pop_head_node(dlist_head *head)
Definition: ilist.h:450

References Assert(), binaryheap_free(), dlist_container, dlist_is_empty(), dlist_pop_head_node(), FileClose(), pfree(), and ReorderBufferFreeChange().

Referenced by ReorderBufferProcessTXN().

◆ ReorderBufferIterTXNInit()

static void ReorderBufferIterTXNInit ( ReorderBuffer rb,
ReorderBufferTXN txn,
ReorderBufferIterTXNState *volatile *  iter_state 
)
static

Definition at line 1283 of file reorderbuffer.c.

1285{
1286 Size nr_txns = 0;
1288 dlist_iter cur_txn_i;
1289 int32 off;
1290
1291 *iter_state = NULL;
1292
1293 /* Check ordering of changes in the toplevel transaction. */
1295
1296 /*
1297 * Calculate the size of our heap: one element for every transaction that
1298 * contains changes. (Besides the transactions already in the reorder
1299 * buffer, we count the one we were directly passed.)
1300 */
1301 if (txn->nentries > 0)
1302 nr_txns++;
1303
1304 dlist_foreach(cur_txn_i, &txn->subtxns)
1305 {
1306 ReorderBufferTXN *cur_txn;
1307
1308 cur_txn = dlist_container(ReorderBufferTXN, node, cur_txn_i.cur);
1309
1310 /* Check ordering of changes in this subtransaction. */
1311 AssertChangeLsnOrder(cur_txn);
1312
1313 if (cur_txn->nentries > 0)
1314 nr_txns++;
1315 }
1316
1317 /* allocate iteration state */
1321 sizeof(ReorderBufferIterTXNEntry) * nr_txns);
1322
1323 state->nr_txns = nr_txns;
1324 dlist_init(&state->old_change);
1325
1326 for (off = 0; off < state->nr_txns; off++)
1327 {
1328 state->entries[off].file.vfd = -1;
1329 state->entries[off].segno = 0;
1330 }
1331
1332 /* allocate heap */
1333 state->heap = binaryheap_allocate(state->nr_txns,
1335 state);
1336
1337 /* Now that the state fields are initialized, it is safe to return it. */
1338 *iter_state = state;
1339
1340 /*
1341 * Now insert items into the binary heap, in an unordered fashion. (We
1342 * will run a heap assembly step at the end; this is more efficient.)
1343 */
1344
1345 off = 0;
1346
1347 /* add toplevel transaction if it contains changes */
1348 if (txn->nentries > 0)
1349 {
1350 ReorderBufferChange *cur_change;
1351
1352 if (rbtxn_is_serialized(txn))
1353 {
1354 /* serialize remaining changes */
1356 ReorderBufferRestoreChanges(rb, txn, &state->entries[off].file,
1357 &state->entries[off].segno);
1358 }
1359
1360 cur_change = dlist_head_element(ReorderBufferChange, node,
1361 &txn->changes);
1362
1363 state->entries[off].lsn = cur_change->lsn;
1364 state->entries[off].change = cur_change;
1365 state->entries[off].txn = txn;
1366
1368 }
1369
1370 /* add subtransactions if they contain changes */
1371 dlist_foreach(cur_txn_i, &txn->subtxns)
1372 {
1373 ReorderBufferTXN *cur_txn;
1374
1375 cur_txn = dlist_container(ReorderBufferTXN, node, cur_txn_i.cur);
1376
1377 if (cur_txn->nentries > 0)
1378 {
1379 ReorderBufferChange *cur_change;
1380
1381 if (rbtxn_is_serialized(cur_txn))
1382 {
1383 /* serialize remaining changes */
1384 ReorderBufferSerializeTXN(rb, cur_txn);
1385 ReorderBufferRestoreChanges(rb, cur_txn,
1386 &state->entries[off].file,
1387 &state->entries[off].segno);
1388 }
1389 cur_change = dlist_head_element(ReorderBufferChange, node,
1390 &cur_txn->changes);
1391
1392 state->entries[off].lsn = cur_change->lsn;
1393 state->entries[off].change = cur_change;
1394 state->entries[off].txn = cur_txn;
1395
1397 }
1398 }
1399
1400 /* assemble a valid binary heap */
1401 binaryheap_build(state->heap);
1402}
void binaryheap_build(binaryheap *heap)
Definition: binaryheap.c:138
void binaryheap_add_unordered(binaryheap *heap, bh_node_type d)
Definition: binaryheap.c:116
binaryheap * binaryheap_allocate(int capacity, binaryheap_comparator compare, void *arg)
Definition: binaryheap.c:39
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:222
static int ReorderBufferIterCompare(Datum a, Datum b, void *arg)
static void AssertChangeLsnOrder(ReorderBufferTXN *txn)
struct ReorderBufferIterTXNEntry ReorderBufferIterTXNEntry
static Size ReorderBufferRestoreChanges(ReorderBuffer *rb, ReorderBufferTXN *txn, TXNEntryFile *file, XLogSegNo *segno)

References AssertChangeLsnOrder(), binaryheap_add_unordered(), binaryheap_allocate(), binaryheap_build(), ReorderBufferTXN::changes, ReorderBuffer::context, dlist_iter::cur, dlist_container, dlist_foreach, dlist_head_element, dlist_init(), Int32GetDatum(), ReorderBufferChange::lsn, MemoryContextAllocZero(), ReorderBufferTXN::nentries, rbtxn_is_serialized, ReorderBufferIterCompare(), ReorderBufferRestoreChanges(), ReorderBufferSerializeTXN(), and ReorderBufferTXN::subtxns.

Referenced by ReorderBufferProcessTXN().

◆ ReorderBufferIterTXNNext()

static ReorderBufferChange * ReorderBufferIterTXNNext ( ReorderBuffer rb,
ReorderBufferIterTXNState state 
)
static

Definition at line 1411 of file reorderbuffer.c.

1412{
1413 ReorderBufferChange *change;
1415 int32 off;
1416
1417 /* nothing there anymore */
1418 if (binaryheap_empty(state->heap))
1419 return NULL;
1420
1421 off = DatumGetInt32(binaryheap_first(state->heap));
1422 entry = &state->entries[off];
1423
1424 /* free memory we might have "leaked" in the previous *Next call */
1425 if (!dlist_is_empty(&state->old_change))
1426 {
1427 change = dlist_container(ReorderBufferChange, node,
1428 dlist_pop_head_node(&state->old_change));
1429 ReorderBufferFreeChange(rb, change, true);
1430 Assert(dlist_is_empty(&state->old_change));
1431 }
1432
1433 change = entry->change;
1434
1435 /*
1436 * update heap with information about which transaction has the next
1437 * relevant change in LSN order
1438 */
1439
1440 /* there are in-memory changes */
1441 if (dlist_has_next(&entry->txn->changes, &entry->change->node))
1442 {
1443 dlist_node *next = dlist_next_node(&entry->txn->changes, &change->node);
1444 ReorderBufferChange *next_change =
1446
1447 /* txn stays the same */
1448 state->entries[off].lsn = next_change->lsn;
1449 state->entries[off].change = next_change;
1450
1452 return change;
1453 }
1454
1455 /* try to load changes from disk */
1456 if (entry->txn->nentries != entry->txn->nentries_mem)
1457 {
1458 /*
1459 * Ugly: restoring changes will reuse *Change records, thus delete the
1460 * current one from the per-tx list and only free in the next call.
1461 */
1462 dlist_delete(&change->node);
1463 dlist_push_tail(&state->old_change, &change->node);
1464
1465 /*
1466 * Update the total bytes processed by the txn for which we are
1467 * releasing the current set of changes and restoring the new set of
1468 * changes.
1469 */
1470 rb->totalBytes += entry->txn->size;
1471 if (ReorderBufferRestoreChanges(rb, entry->txn, &entry->file,
1472 &state->entries[off].segno))
1473 {
1474 /* successfully restored changes from disk */
1475 ReorderBufferChange *next_change =
1477 &entry->txn->changes);
1478
1479 elog(DEBUG2, "restored %u/%u changes from disk",
1480 (uint32) entry->txn->nentries_mem,
1481 (uint32) entry->txn->nentries);
1482
1483 Assert(entry->txn->nentries_mem);
1484 /* txn stays the same */
1485 state->entries[off].lsn = next_change->lsn;
1486 state->entries[off].change = next_change;
1488
1489 return change;
1490 }
1491 }
1492
1493 /* ok, no changes there anymore, remove */
1495
1496 return change;
1497}
void binaryheap_replace_first(binaryheap *heap, bh_node_type d)
Definition: binaryheap.c:255
bh_node_type binaryheap_first(binaryheap *heap)
Definition: binaryheap.c:177
bh_node_type binaryheap_remove_first(binaryheap *heap)
Definition: binaryheap.c:192
#define binaryheap_empty(h)
Definition: binaryheap.h:65
static int32 next
Definition: blutils.c:224
uint32_t uint32
Definition: c.h:539
static bool dlist_has_next(const dlist_head *head, const dlist_node *node)
Definition: ilist.h:503
static dlist_node * dlist_next_node(dlist_head *head, dlist_node *node)
Definition: ilist.h:537
ReorderBufferChange * change
ReorderBufferTXN * txn

References Assert(), binaryheap_empty, binaryheap_first(), binaryheap_remove_first(), binaryheap_replace_first(), ReorderBufferIterTXNEntry::change, ReorderBufferTXN::changes, DatumGetInt32(), DEBUG2, dlist_container, dlist_delete(), dlist_has_next(), dlist_head_element, dlist_is_empty(), dlist_next_node(), dlist_pop_head_node(), dlist_push_tail(), elog, ReorderBufferIterTXNEntry::file, Int32GetDatum(), ReorderBufferChange::lsn, ReorderBufferTXN::nentries, ReorderBufferTXN::nentries_mem, next, ReorderBufferChange::node, ReorderBufferFreeChange(), ReorderBufferRestoreChanges(), ReorderBufferTXN::size, ReorderBuffer::totalBytes, and ReorderBufferIterTXNEntry::txn.

Referenced by ReorderBufferProcessTXN().

◆ ReorderBufferLargestStreamableTopTXN()

static ReorderBufferTXN * ReorderBufferLargestStreamableTopTXN ( ReorderBuffer rb)
static

Definition at line 3848 of file reorderbuffer.c.

3849{
3850 dlist_iter iter;
3851 Size largest_size = 0;
3852 ReorderBufferTXN *largest = NULL;
3853
3854 /* Find the largest top-level transaction having a base snapshot. */
3856 {
3857 ReorderBufferTXN *txn;
3858
3859 txn = dlist_container(ReorderBufferTXN, base_snapshot_node, iter.cur);
3860
3861 /* must not be a subtxn */
3863 /* base_snapshot must be set */
3864 Assert(txn->base_snapshot != NULL);
3865
3866 /* Don't consider these kinds of transactions for eviction. */
3867 if (rbtxn_has_partial_change(txn) ||
3869 rbtxn_is_aborted(txn))
3870 continue;
3871
3872 /* Find the largest of the eviction candidates. */
3873 if ((largest == NULL || txn->total_size > largest_size) &&
3874 (txn->total_size > 0))
3875 {
3876 largest = txn;
3877 largest_size = txn->total_size;
3878 }
3879 }
3880
3881 return largest;
3882}
#define rbtxn_has_streamable_change(txn)
#define rbtxn_has_partial_change(txn)

References Assert(), ReorderBufferTXN::base_snapshot, dlist_iter::cur, dlist_container, dlist_foreach, rbtxn_has_partial_change, rbtxn_has_streamable_change, rbtxn_is_aborted, rbtxn_is_known_subxact, ReorderBufferTXN::total_size, and ReorderBuffer::txns_by_base_snapshot_lsn.

Referenced by ReorderBufferCheckMemoryLimit().

◆ ReorderBufferLargestTXN()

static ReorderBufferTXN * ReorderBufferLargestTXN ( ReorderBuffer rb)
static

Definition at line 3807 of file reorderbuffer.c.

3808{
3809 ReorderBufferTXN *largest;
3810
3811 /* Get the largest transaction from the max-heap */
3812 largest = pairingheap_container(ReorderBufferTXN, txn_node,
3814
3815 Assert(largest);
3816 Assert(largest->size > 0);
3817 Assert(largest->size <= rb->size);
3818
3819 return largest;
3820}
pairingheap_node * pairingheap_first(pairingheap *heap)
Definition: pairingheap.c:130
#define pairingheap_container(type, membername, ptr)
Definition: pairingheap.h:43

References Assert(), pairingheap_container, pairingheap_first(), ReorderBufferTXN::size, ReorderBuffer::size, and ReorderBuffer::txn_heap.

Referenced by ReorderBufferCheckMemoryLimit().

◆ ReorderBufferMaybeMarkTXNStreamed()

static void ReorderBufferMaybeMarkTXNStreamed ( ReorderBuffer rb,
ReorderBufferTXN txn 
)
static

Definition at line 2137 of file reorderbuffer.c.

2138{
2139 /*
2140 * The top-level transaction, is marked as streamed always, even if it
2141 * does not contain any changes (that is, when all the changes are in
2142 * subtransactions).
2143 *
2144 * For subtransactions, we only mark them as streamed when there are
2145 * changes in them.
2146 *
2147 * We do it this way because of aborts - we don't want to send aborts for
2148 * XIDs the downstream is not aware of. And of course, it always knows
2149 * about the top-level xact (we send the XID in all messages), but we
2150 * never stream XIDs of empty subxacts.
2151 */
2152 if (rbtxn_is_toptxn(txn) || (txn->nentries_mem != 0))
2154}
#define RBTXN_IS_STREAMED

References ReorderBufferTXN::nentries_mem, RBTXN_IS_STREAMED, rbtxn_is_toptxn, and ReorderBufferTXN::txn_flags.

Referenced by ReorderBufferProcessTXN(), and ReorderBufferTruncateTXN().

◆ ReorderBufferPrepare()

void ReorderBufferPrepare ( ReorderBuffer rb,
TransactionId  xid,
char *  gid 
)

Definition at line 2959 of file reorderbuffer.c.

2961{
2962 ReorderBufferTXN *txn;
2963
2964 txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr,
2965 false);
2966
2967 /* unknown transaction, nothing to replay */
2968 if (txn == NULL)
2969 return;
2970
2971 /*
2972 * txn must have been marked as a prepared transaction and must have
2973 * neither been skipped nor sent a prepare. Also, the prepare info must
2974 * have been updated in it by now.
2975 */
2978
2979 txn->gid = pstrdup(gid);
2980
2981 ReorderBufferReplay(txn, rb, xid, txn->final_lsn, txn->end_lsn,
2982 txn->xact_time.prepare_time, txn->origin_id, txn->origin_lsn);
2983
2984 /*
2985 * Send a prepare if not already done so. This might occur if we have
2986 * detected a concurrent abort while replaying the non-streaming
2987 * transaction.
2988 */
2989 if (!rbtxn_sent_prepare(txn))
2990 {
2991 rb->prepare(rb, txn, txn->final_lsn);
2993 }
2994}
#define RBTXN_SENT_PREPARE
#define rbtxn_sent_prepare(txn)
ReorderBufferPrepareCB prepare

References Assert(), ReorderBufferTXN::end_lsn, ReorderBufferTXN::final_lsn, ReorderBufferTXN::gid, InvalidXLogRecPtr, ReorderBufferTXN::origin_id, ReorderBufferTXN::origin_lsn, ReorderBuffer::prepare, ReorderBufferTXN::prepare_time, pstrdup(), RBTXN_IS_PREPARED, RBTXN_PREPARE_STATUS_MASK, RBTXN_SENT_PREPARE, rbtxn_sent_prepare, ReorderBufferReplay(), ReorderBufferTXNByXid(), ReorderBufferTXN::txn_flags, and ReorderBufferTXN::xact_time.

Referenced by DecodePrepare().

◆ ReorderBufferProcessPartialChange()

static void ReorderBufferProcessPartialChange ( ReorderBuffer rb,
ReorderBufferTXN txn,
ReorderBufferChange change,
bool  toast_insert 
)
static

Definition at line 740 of file reorderbuffer.c.

743{
744 ReorderBufferTXN *toptxn;
745
746 /*
747 * The partial changes need to be processed only while streaming
748 * in-progress transactions.
749 */
750 if (!ReorderBufferCanStream(rb))
751 return;
752
753 /* Get the top transaction. */
754 toptxn = rbtxn_get_toptxn(txn);
755
756 /*
757 * Indicate a partial change for toast inserts. The change will be
758 * considered as complete once we get the insert or update on the main
759 * table and we are sure that the pending toast chunks are not required
760 * anymore.
761 *
762 * If we allow streaming when there are pending toast chunks then such
763 * chunks won't be released till the insert (multi_insert) is complete and
764 * we expect the txn to have streamed all changes after streaming. This
765 * restriction is mainly to ensure the correctness of streamed
766 * transactions and it doesn't seem worth uplifting such a restriction
767 * just to allow this case because anyway we will stream the transaction
768 * once such an insert is complete.
769 */
770 if (toast_insert)
772 else if (rbtxn_has_partial_change(toptxn) &&
773 IsInsertOrUpdate(change->action) &&
775 toptxn->txn_flags &= ~RBTXN_HAS_PARTIAL_CHANGE;
776
777 /*
778 * Indicate a partial change for speculative inserts. The change will be
779 * considered as complete once we get the speculative confirm or abort
780 * token.
781 */
782 if (IsSpecInsert(change->action))
784 else if (rbtxn_has_partial_change(toptxn) &&
786 toptxn->txn_flags &= ~RBTXN_HAS_PARTIAL_CHANGE;
787
788 /*
789 * Stream the transaction if it is serialized before and the changes are
790 * now complete in the top-level transaction.
791 *
792 * The reason for doing the streaming of such a transaction as soon as we
793 * get the complete change for it is that previously it would have reached
794 * the memory threshold and wouldn't get streamed because of incomplete
795 * changes. Delaying such transactions would increase apply lag for them.
796 */
798 !(rbtxn_has_partial_change(toptxn)) &&
799 rbtxn_is_serialized(txn) &&
801 ReorderBufferStreamTXN(rb, toptxn);
802}
#define IsSpecInsert(action)
#define IsInsertOrUpdate(action)
#define IsSpecConfirmOrAbort(action)
#define RBTXN_HAS_PARTIAL_CHANGE

References ReorderBufferChange::action, ReorderBufferChange::clear_toast_afterwards, ReorderBufferChange::data, IsInsertOrUpdate, IsSpecConfirmOrAbort, IsSpecInsert, rbtxn_get_toptxn, RBTXN_HAS_PARTIAL_CHANGE, rbtxn_has_partial_change, rbtxn_has_streamable_change, rbtxn_is_serialized, ReorderBufferCanStartStreaming(), ReorderBufferCanStream(), ReorderBufferStreamTXN(), ReorderBufferChange::tp, and ReorderBufferTXN::txn_flags.

Referenced by ReorderBufferQueueChange().

◆ ReorderBufferProcessTXN()

static void ReorderBufferProcessTXN ( ReorderBuffer rb,
ReorderBufferTXN txn,
XLogRecPtr  commit_lsn,
volatile Snapshot  snapshot_now,
volatile CommandId  command_id,
bool  streaming 
)
static

Definition at line 2210 of file reorderbuffer.c.

2215{
2216 bool using_subtxn;
2219 ReorderBufferIterTXNState *volatile iterstate = NULL;
2220 volatile XLogRecPtr prev_lsn = InvalidXLogRecPtr;
2221 ReorderBufferChange *volatile specinsert = NULL;
2222 volatile bool stream_started = false;
2223 ReorderBufferTXN *volatile curtxn = NULL;
2224
2225 /* build data to be able to lookup the CommandIds of catalog tuples */
2227
2228 /* setup the initial snapshot */
2229 SetupHistoricSnapshot(snapshot_now, txn->tuplecid_hash);
2230
2231 /*
2232 * Decoding needs access to syscaches et al., which in turn use
2233 * heavyweight locks and such. Thus we need to have enough state around to
2234 * keep track of those. The easiest way is to simply use a transaction
2235 * internally. That also allows us to easily enforce that nothing writes
2236 * to the database by checking for xid assignments.
2237 *
2238 * When we're called via the SQL SRF there's already a transaction
2239 * started, so start an explicit subtransaction there.
2240 */
2241 using_subtxn = IsTransactionOrTransactionBlock();
2242
2243 PG_TRY();
2244 {
2245 ReorderBufferChange *change;
2246 int changes_count = 0; /* used to accumulate the number of
2247 * changes */
2248
2249 if (using_subtxn)
2250 BeginInternalSubTransaction(streaming ? "stream" : "replay");
2251 else
2253
2254 /*
2255 * We only need to send begin/begin-prepare for non-streamed
2256 * transactions.
2257 */
2258 if (!streaming)
2259 {
2260 if (rbtxn_is_prepared(txn))
2261 rb->begin_prepare(rb, txn);
2262 else
2263 rb->begin(rb, txn);
2264 }
2265
2266 ReorderBufferIterTXNInit(rb, txn, &iterstate);
2267 while ((change = ReorderBufferIterTXNNext(rb, iterstate)) != NULL)
2268 {
2269 Relation relation = NULL;
2270 Oid reloid;
2271
2273
2274 /*
2275 * We can't call start stream callback before processing first
2276 * change.
2277 */
2278 if (prev_lsn == InvalidXLogRecPtr)
2279 {
2280 if (streaming)
2281 {
2282 txn->origin_id = change->origin_id;
2283 rb->stream_start(rb, txn, change->lsn);
2284 stream_started = true;
2285 }
2286 }
2287
2288 /*
2289 * Enforce correct ordering of changes, merged from multiple
2290 * subtransactions. The changes may have the same LSN due to
2291 * MULTI_INSERT xlog records.
2292 */
2293 Assert(prev_lsn == InvalidXLogRecPtr || prev_lsn <= change->lsn);
2294
2295 prev_lsn = change->lsn;
2296
2297 /*
2298 * Set the current xid to detect concurrent aborts. This is
2299 * required for the cases when we decode the changes before the
2300 * COMMIT record is processed.
2301 */
2302 if (streaming || rbtxn_is_prepared(change->txn))
2303 {
2304 curtxn = change->txn;
2305 SetupCheckXidLive(curtxn->xid);
2306 }
2307
2308 switch (change->action)
2309 {
2311
2312 /*
2313 * Confirmation for speculative insertion arrived. Simply
2314 * use as a normal record. It'll be cleaned up at the end
2315 * of INSERT processing.
2316 */
2317 if (specinsert == NULL)
2318 elog(ERROR, "invalid ordering of speculative insertion changes");
2319 Assert(specinsert->data.tp.oldtuple == NULL);
2320 change = specinsert;
2322
2323 /* intentionally fall through */
2327 Assert(snapshot_now);
2328
2329 reloid = RelidByRelfilenumber(change->data.tp.rlocator.spcOid,
2330 change->data.tp.rlocator.relNumber);
2331
2332 /*
2333 * Mapped catalog tuple without data, emitted while
2334 * catalog table was in the process of being rewritten. We
2335 * can fail to look up the relfilenumber, because the
2336 * relmapper has no "historic" view, in contrast to the
2337 * normal catalog during decoding. Thus repeated rewrites
2338 * can cause a lookup failure. That's OK because we do not
2339 * decode catalog changes anyway. Normally such tuples
2340 * would be skipped over below, but we can't identify
2341 * whether the table should be logically logged without
2342 * mapping the relfilenumber to the oid.
2343 */
2344 if (reloid == InvalidOid &&
2345 change->data.tp.newtuple == NULL &&
2346 change->data.tp.oldtuple == NULL)
2347 goto change_done;
2348 else if (reloid == InvalidOid)
2349 elog(ERROR, "could not map filenumber \"%s\" to relation OID",
2350 relpathperm(change->data.tp.rlocator,
2351 MAIN_FORKNUM).str);
2352
2353 relation = RelationIdGetRelation(reloid);
2354
2355 if (!RelationIsValid(relation))
2356 elog(ERROR, "could not open relation with OID %u (for filenumber \"%s\")",
2357 reloid,
2358 relpathperm(change->data.tp.rlocator,
2359 MAIN_FORKNUM).str);
2360
2361 if (!RelationIsLogicallyLogged(relation))
2362 goto change_done;
2363
2364 /*
2365 * Ignore temporary heaps created during DDL unless the
2366 * plugin has asked for them.
2367 */
2368 if (relation->rd_rel->relrewrite && !rb->output_rewrites)
2369 goto change_done;
2370
2371 /*
2372 * For now ignore sequence changes entirely. Most of the
2373 * time they don't log changes using records we
2374 * understand, so it doesn't make sense to handle the few
2375 * cases we do.
2376 */
2377 if (relation->rd_rel->relkind == RELKIND_SEQUENCE)
2378 goto change_done;
2379
2380 /* user-triggered change */
2381 if (!IsToastRelation(relation))
2382 {
2383 ReorderBufferToastReplace(rb, txn, relation, change);
2384 ReorderBufferApplyChange(rb, txn, relation, change,
2385 streaming);
2386
2387 /*
2388 * Only clear reassembled toast chunks if we're sure
2389 * they're not required anymore. The creator of the
2390 * tuple tells us.
2391 */
2392 if (change->data.tp.clear_toast_afterwards)
2393 ReorderBufferToastReset(rb, txn);
2394 }
2395 /* we're not interested in toast deletions */
2396 else if (change->action == REORDER_BUFFER_CHANGE_INSERT)
2397 {
2398 /*
2399 * Need to reassemble the full toasted Datum in
2400 * memory, to ensure the chunks don't get reused till
2401 * we're done remove it from the list of this
2402 * transaction's changes. Otherwise it will get
2403 * freed/reused while restoring spooled data from
2404 * disk.
2405 */
2406 Assert(change->data.tp.newtuple != NULL);
2407
2408 dlist_delete(&change->node);
2409 ReorderBufferToastAppendChunk(rb, txn, relation,
2410 change);
2411 }
2412
2413 change_done:
2414
2415 /*
2416 * If speculative insertion was confirmed, the record
2417 * isn't needed anymore.
2418 */
2419 if (specinsert != NULL)
2420 {
2421 ReorderBufferFreeChange(rb, specinsert, true);
2422 specinsert = NULL;
2423 }
2424
2425 if (RelationIsValid(relation))
2426 {
2427 RelationClose(relation);
2428 relation = NULL;
2429 }
2430 break;
2431
2433
2434 /*
2435 * Speculative insertions are dealt with by delaying the
2436 * processing of the insert until the confirmation record
2437 * arrives. For that we simply unlink the record from the
2438 * chain, so it does not get freed/reused while restoring
2439 * spooled data from disk.
2440 *
2441 * This is safe in the face of concurrent catalog changes
2442 * because the relevant relation can't be changed between
2443 * speculative insertion and confirmation due to
2444 * CheckTableNotInUse() and locking.
2445 */
2446
2447 /* clear out a pending (and thus failed) speculation */
2448 if (specinsert != NULL)
2449 {
2450 ReorderBufferFreeChange(rb, specinsert, true);
2451 specinsert = NULL;
2452 }
2453
2454 /* and memorize the pending insertion */
2455 dlist_delete(&change->node);
2456 specinsert = change;
2457 break;
2458
2460
2461 /*
2462 * Abort for speculative insertion arrived. So cleanup the
2463 * specinsert tuple and toast hash.
2464 *
2465 * Note that we get the spec abort change for each toast
2466 * entry but we need to perform the cleanup only the first
2467 * time we get it for the main table.
2468 */
2469 if (specinsert != NULL)
2470 {
2471 /*
2472 * We must clean the toast hash before processing a
2473 * completely new tuple to avoid confusion about the
2474 * previous tuple's toast chunks.
2475 */
2477 ReorderBufferToastReset(rb, txn);
2478
2479 /* We don't need this record anymore. */
2480 ReorderBufferFreeChange(rb, specinsert, true);
2481 specinsert = NULL;
2482 }
2483 break;
2484
2486 {
2487 int i;
2488 int nrelids = change->data.truncate.nrelids;
2489 int nrelations = 0;
2490 Relation *relations;
2491
2492 relations = palloc0(nrelids * sizeof(Relation));
2493 for (i = 0; i < nrelids; i++)
2494 {
2495 Oid relid = change->data.truncate.relids[i];
2496 Relation rel;
2497
2498 rel = RelationIdGetRelation(relid);
2499
2500 if (!RelationIsValid(rel))
2501 elog(ERROR, "could not open relation with OID %u", relid);
2502
2503 if (!RelationIsLogicallyLogged(rel))
2504 continue;
2505
2506 relations[nrelations++] = rel;
2507 }
2508
2509 /* Apply the truncate. */
2510 ReorderBufferApplyTruncate(rb, txn, nrelations,
2511 relations, change,
2512 streaming);
2513
2514 for (i = 0; i < nrelations; i++)
2515 RelationClose(relations[i]);
2516
2517 break;
2518 }
2519
2521 ReorderBufferApplyMessage(rb, txn, change, streaming);
2522 break;
2523
2525 /* Execute the invalidation messages locally */
2527 change->data.inval.invalidations);
2528 break;
2529
2531 /* get rid of the old */
2533
2534 if (snapshot_now->copied)
2535 {
2536 ReorderBufferFreeSnap(rb, snapshot_now);
2537 snapshot_now =
2539 txn, command_id);
2540 }
2541
2542 /*
2543 * Restored from disk, need to be careful not to double
2544 * free. We could introduce refcounting for that, but for
2545 * now this seems infrequent enough not to care.
2546 */
2547 else if (change->data.snapshot->copied)
2548 {
2549 snapshot_now =
2551 txn, command_id);
2552 }
2553 else
2554 {
2555 snapshot_now = change->data.snapshot;
2556 }
2557
2558 /* and continue with the new one */
2559 SetupHistoricSnapshot(snapshot_now, txn->tuplecid_hash);
2560 break;
2561
2564
2565 if (command_id < change->data.command_id)
2566 {
2567 command_id = change->data.command_id;
2568
2569 if (!snapshot_now->copied)
2570 {
2571 /* we don't use the global one anymore */
2572 snapshot_now = ReorderBufferCopySnap(rb, snapshot_now,
2573 txn, command_id);
2574 }
2575
2576 snapshot_now->curcid = command_id;
2577
2579 SetupHistoricSnapshot(snapshot_now, txn->tuplecid_hash);
2580 }
2581
2582 break;
2583
2585 elog(ERROR, "tuplecid value in changequeue");
2586 break;
2587 }
2588
2589 /*
2590 * It is possible that the data is not sent to downstream for a
2591 * long time either because the output plugin filtered it or there
2592 * is a DDL that generates a lot of data that is not processed by
2593 * the plugin. So, in such cases, the downstream can timeout. To
2594 * avoid that we try to send a keepalive message if required.
2595 * Trying to send a keepalive message after every change has some
2596 * overhead, but testing showed there is no noticeable overhead if
2597 * we do it after every ~100 changes.
2598 */
2599#define CHANGES_THRESHOLD 100
2600
2601 if (++changes_count >= CHANGES_THRESHOLD)
2602 {
2603 rb->update_progress_txn(rb, txn, prev_lsn);
2604 changes_count = 0;
2605 }
2606 }
2607
2608 /* speculative insertion record must be freed by now */
2609 Assert(!specinsert);
2610
2611 /* clean up the iterator */
2612 ReorderBufferIterTXNFinish(rb, iterstate);
2613 iterstate = NULL;
2614
2615 /*
2616 * Update total transaction count and total bytes processed by the
2617 * transaction and its subtransactions. Ensure to not count the
2618 * streamed transaction multiple times.
2619 *
2620 * Note that the statistics computation has to be done after
2621 * ReorderBufferIterTXNFinish as it releases the serialized change
2622 * which we have already accounted in ReorderBufferIterTXNNext.
2623 */
2624 if (!rbtxn_is_streamed(txn))
2625 rb->totalTxns++;
2626
2627 rb->totalBytes += txn->total_size;
2628
2629 /*
2630 * Done with current changes, send the last message for this set of
2631 * changes depending upon streaming mode.
2632 */
2633 if (streaming)
2634 {
2635 if (stream_started)
2636 {
2637 rb->stream_stop(rb, txn, prev_lsn);
2638 stream_started = false;
2639 }
2640 }
2641 else
2642 {
2643 /*
2644 * Call either PREPARE (for two-phase transactions) or COMMIT (for
2645 * regular ones).
2646 */
2647 if (rbtxn_is_prepared(txn))
2648 {
2650 rb->prepare(rb, txn, commit_lsn);
2652 }
2653 else
2654 rb->commit(rb, txn, commit_lsn);
2655 }
2656
2657 /* this is just a sanity check against bad output plugin behaviour */
2659 elog(ERROR, "output plugin used XID %u",
2661
2662 /*
2663 * Remember the command ID and snapshot for the next set of changes in
2664 * streaming mode.
2665 */
2666 if (streaming)
2667 ReorderBufferSaveTXNSnapshot(rb, txn, snapshot_now, command_id);
2668 else if (snapshot_now->copied)
2669 ReorderBufferFreeSnap(rb, snapshot_now);
2670
2671 /* cleanup */
2673
2674 /*
2675 * Aborting the current (sub-)transaction as a whole has the right
2676 * semantics. We want all locks acquired in here to be released, not
2677 * reassigned to the parent and we do not want any database access
2678 * have persistent effects.
2679 */
2681
2682 /* make sure there's no cache pollution */
2684 {
2687 }
2688 else
2689 {
2693 }
2694
2695 if (using_subtxn)
2696 {
2699 CurrentResourceOwner = cowner;
2700 }
2701
2702 /*
2703 * We are here due to one of the four reasons: 1. Decoding an
2704 * in-progress txn. 2. Decoding a prepared txn. 3. Decoding of a
2705 * prepared txn that was (partially) streamed. 4. Decoding a committed
2706 * txn.
2707 *
2708 * For 1, we allow truncation of txn data by removing the changes
2709 * already streamed but still keeping other things like invalidations,
2710 * snapshot, and tuplecids. For 2 and 3, we indicate
2711 * ReorderBufferTruncateTXN to do more elaborate truncation of txn
2712 * data as the entire transaction has been decoded except for commit.
2713 * For 4, as the entire txn has been decoded, we can fully clean up
2714 * the TXN reorder buffer.
2715 */
2716 if (streaming || rbtxn_is_prepared(txn))
2717 {
2718 if (streaming)
2720
2722 /* Reset the CheckXidAlive */
2724 }
2725 else
2726 ReorderBufferCleanupTXN(rb, txn);
2727 }
2728 PG_CATCH();
2729 {
2731 ErrorData *errdata = CopyErrorData();
2732
2733 /* TODO: Encapsulate cleanup from the PG_TRY and PG_CATCH blocks */
2734 if (iterstate)
2735 ReorderBufferIterTXNFinish(rb, iterstate);
2736
2738
2739 /*
2740 * Force cache invalidation to happen outside of a valid transaction
2741 * to prevent catalog access as we just caught an error.
2742 */
2744
2745 /* make sure there's no cache pollution */
2747 {
2750 }
2751 else
2752 {
2756 }
2757
2758 if (using_subtxn)
2759 {
2762 CurrentResourceOwner = cowner;
2763 }
2764
2765 /*
2766 * The error code ERRCODE_TRANSACTION_ROLLBACK indicates a concurrent
2767 * abort of the (sub)transaction we are streaming or preparing. We
2768 * need to do the cleanup and return gracefully on this error, see
2769 * SetupCheckXidLive.
2770 *
2771 * This error code can be thrown by one of the callbacks we call
2772 * during decoding so we need to ensure that we return gracefully only
2773 * when we are sending the data in streaming mode and the streaming is
2774 * not finished yet or when we are sending the data out on a PREPARE
2775 * during a two-phase commit.
2776 */
2777 if (errdata->sqlerrcode == ERRCODE_TRANSACTION_ROLLBACK &&
2778 (stream_started || rbtxn_is_prepared(txn)))
2779 {
2780 /* curtxn must be set for streaming or prepared transactions */
2781 Assert(curtxn);
2782
2783 /* Cleanup the temporary error state. */
2785 FreeErrorData(errdata);
2786 errdata = NULL;
2787
2788 /* Remember the transaction is aborted. */
2789 Assert(!rbtxn_is_committed(curtxn));
2790 curtxn->txn_flags |= RBTXN_IS_ABORTED;
2791
2792 /* Mark the transaction is streamed if appropriate */
2793 if (stream_started)
2795
2796 /* Reset the TXN so that it is allowed to stream remaining data. */
2797 ReorderBufferResetTXN(rb, txn, snapshot_now,
2798 command_id, prev_lsn,
2799 specinsert);
2800 }
2801 else
2802 {
2803 ReorderBufferCleanupTXN(rb, txn);
2805 PG_RE_THROW();
2806 }
2807 }
2808 PG_END_TRY();
2809}
bool IsToastRelation(Relation relation)
Definition: catalog.c:206
void FreeErrorData(ErrorData *edata)
Definition: elog.c:1826
ErrorData * CopyErrorData(void)
Definition: elog.c:1754
void FlushErrorState(void)
Definition: elog.c:1875
#define PG_RE_THROW()
Definition: elog.h:405
#define PG_TRY(...)
Definition: elog.h:372
#define PG_END_TRY(...)
Definition: elog.h:397
#define PG_CATCH(...)
Definition: elog.h:382
void InvalidateSystemCaches(void)
Definition: inval.c:916
void * palloc0(Size size)
Definition: mcxt.c:1395
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
const void * data
#define InvalidOid
Definition: postgres_ext.h:37
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:710
#define RelationIsValid(relation)
Definition: rel.h:489
Relation RelationIdGetRelation(Oid relationId)
Definition: relcache.c:2099
void RelationClose(Relation relation)
Definition: relcache.c:2220
Oid RelidByRelfilenumber(Oid reltablespace, RelFileNumber relfilenumber)
@ MAIN_FORKNUM
Definition: relpath.h:58
#define relpathperm(rlocator, forknum)
Definition: relpath.h:146
static void ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn, Relation relation, ReorderBufferChange *change)
static void ReorderBufferResetTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, Snapshot snapshot_now, CommandId command_id, XLogRecPtr last_lsn, ReorderBufferChange *specinsert)
static void ReorderBufferIterTXNInit(ReorderBuffer *rb, ReorderBufferTXN *txn, ReorderBufferIterTXNState *volatile *iter_state)
static void ReorderBufferToastAppendChunk(ReorderBuffer *rb, ReorderBufferTXN *txn, Relation relation, ReorderBufferChange *change)
static void ReorderBufferSaveTXNSnapshot(ReorderBuffer *rb, ReorderBufferTXN *txn, Snapshot snapshot_now, CommandId command_id)
static void ReorderBufferApplyChange(ReorderBuffer *rb, ReorderBufferTXN *txn, Relation relation, ReorderBufferChange *change, bool streaming)
static void ReorderBufferIterTXNFinish(ReorderBuffer *rb, ReorderBufferIterTXNState *state)
#define CHANGES_THRESHOLD
static void ReorderBufferApplyMessage(ReorderBuffer *rb, ReorderBufferTXN *txn, ReorderBufferChange *change, bool streaming)
static void SetupCheckXidLive(TransactionId xid)
static Snapshot ReorderBufferCopySnap(ReorderBuffer *rb, Snapshot orig_snap, ReorderBufferTXN *txn, CommandId cid)
static void ReorderBufferApplyTruncate(ReorderBuffer *rb, ReorderBufferTXN *txn, int nrelations, Relation *relations, ReorderBufferChange *change, bool streaming)
static void ReorderBufferBuildTupleCidHash(ReorderBuffer *rb, ReorderBufferTXN *txn)
static ReorderBufferChange * ReorderBufferIterTXNNext(ReorderBuffer *rb, ReorderBufferIterTXNState *state)
static void ReorderBufferMaybeMarkTXNStreamed(ReorderBuffer *rb, ReorderBufferTXN *txn)
void TeardownHistoricSnapshot(bool is_error)
Definition: snapmgr.c:1683
void SetupHistoricSnapshot(Snapshot historic_snapshot, HTAB *tuplecids)
Definition: snapmgr.c:1667
int sqlerrcode
Definition: elog.h:431
RelFileNumber relNumber
Form_pg_class rd_rel
Definition: rel.h:111
RelFileLocator rlocator
Definition: reorderbuffer.h:98
RepOriginId origin_id
Definition: reorderbuffer.h:86
ReorderBufferBeginCB begin_prepare
ReorderBufferUpdateProgressTxnCB update_progress_txn
ReorderBufferStreamStopCB stream_stop
ReorderBufferCommitCB commit
ReorderBufferStreamStartCB stream_start
ReorderBufferBeginCB begin
TransactionId CheckXidAlive
Definition: xact.c:99
void StartTransactionCommand(void)
Definition: xact.c:3071
TransactionId GetCurrentTransactionIdIfAny(void)
Definition: xact.c:471
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:454

References AbortCurrentTransaction(), ReorderBufferChange::action, Assert(), ReorderBuffer::begin, ReorderBuffer::begin_prepare, BeginInternalSubTransaction(), CHANGES_THRESHOLD, CHECK_FOR_INTERRUPTS, CheckXidAlive, ReorderBufferChange::clear_toast_afterwards, ReorderBufferChange::command_id, ReorderBuffer::commit, SnapshotData::copied, CopyErrorData(), SnapshotData::curcid, CurrentMemoryContext, CurrentResourceOwner, ReorderBufferChange::data, data, dlist_delete(), elog, ERROR, FlushErrorState(), FreeErrorData(), GetCurrentTransactionId(), GetCurrentTransactionIdIfAny(), i, ReorderBufferChange::inval, InvalidateSystemCaches(), ReorderBufferChange::invalidations, ReorderBufferTXN::invalidations, ReorderBufferTXN::invalidations_distributed, InvalidCommandId, InvalidOid, InvalidTransactionId, InvalidXLogRecPtr, IsToastRelation(), IsTransactionOrTransactionBlock(), ReorderBufferChange::lsn, MAIN_FORKNUM, MemoryContextSwitchTo(), ReorderBufferChange::newtuple, ReorderBufferChange::ninvalidations, ReorderBufferTXN::ninvalidations, ReorderBufferTXN::ninvalidations_distributed, ReorderBufferChange::node, ReorderBufferChange::nrelids, ReorderBufferChange::oldtuple, ReorderBufferChange::origin_id, ReorderBufferTXN::origin_id, ReorderBuffer::output_rewrites, palloc0(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, ReorderBuffer::prepare, rbtxn_distr_inval_overflowed, RBTXN_IS_ABORTED, rbtxn_is_committed, rbtxn_is_prepared, rbtxn_is_streamed, RBTXN_SENT_PREPARE, rbtxn_sent_prepare, RelationData::rd_rel, RelationClose(), RelationIdGetRelation(), RelationIsLogicallyLogged, RelationIsValid, RelidByRelfilenumber(), ReorderBufferChange::relids, RelFileLocator::relNumber, relpathperm, REORDER_BUFFER_CHANGE_DELETE, REORDER_BUFFER_CHANGE_INSERT, REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID, REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT, REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID, REORDER_BUFFER_CHANGE_INVALIDATION, REORDER_BUFFER_CHANGE_MESSAGE, REORDER_BUFFER_CHANGE_TRUNCATE, REORDER_BUFFER_CHANGE_UPDATE, ReorderBufferApplyChange(), ReorderBufferApplyMessage(), ReorderBufferApplyTruncate(), ReorderBufferBuildTupleCidHash(), ReorderBufferCleanupTXN(), ReorderBufferCopySnap(), ReorderBufferExecuteInvalidations(), ReorderBufferFreeChange(), ReorderBufferFreeSnap(), ReorderBufferIterTXNFinish(), ReorderBufferIterTXNInit(), ReorderBufferIterTXNNext(), ReorderBufferMaybeMarkTXNStreamed(), ReorderBufferResetTXN(), ReorderBufferSaveTXNSnapshot(), ReorderBufferToastAppendChunk(), ReorderBufferToastReplace(), ReorderBufferToastReset(), ReorderBufferTruncateTXN(), ReorderBufferChange::rlocator, RollbackAndReleaseCurrentSubTransaction(), SetupCheckXidLive(), SetupHistoricSnapshot(), ReorderBufferChange::snapshot, RelFileLocator::spcOid, ErrorData::sqlerrcode, StartTransactionCommand(), ReorderBuffer::stream_start, ReorderBuffer::stream_stop, TeardownHistoricSnapshot(), ReorderBufferTXN::total_size, ReorderBuffer::totalBytes, ReorderBuffer::totalTxns, ReorderBufferChange::tp, ReorderBufferChange::truncate, ReorderBufferTXN::tuplecid_hash, ReorderBufferChange::txn, ReorderBufferTXN::txn_flags, ReorderBuffer::update_progress_txn, and ReorderBufferTXN::xid.

Referenced by ReorderBufferReplay(), and ReorderBufferStreamTXN().

◆ ReorderBufferProcessXid()

void ReorderBufferProcessXid ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  lsn 
)

Definition at line 3294 of file reorderbuffer.c.

3295{
3296 /* many records won't have an xid assigned, centralize check here */
3297 if (xid != InvalidTransactionId)
3298 ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
3299}

References InvalidTransactionId, and ReorderBufferTXNByXid().

Referenced by heap2_decode(), heap_decode(), LogicalDecodingProcessRecord(), logicalmsg_decode(), standby_decode(), xact_decode(), and xlog_decode().

◆ ReorderBufferQueueChange()

void ReorderBufferQueueChange ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  lsn,
ReorderBufferChange change,
bool  toast_insert 
)

Definition at line 809 of file reorderbuffer.c.

811{
812 ReorderBufferTXN *txn;
813
814 txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
815
816 /*
817 * If we have detected that the transaction is aborted while streaming the
818 * previous changes or by checking its CLOG, there is no point in
819 * collecting further changes for it.
820 */
821 if (rbtxn_is_aborted(txn))
822 {
823 /*
824 * We don't need to update memory accounting for this change as we
825 * have not added it to the queue yet.
826 */
827 ReorderBufferFreeChange(rb, change, false);
828 return;
829 }
830
831 /*
832 * The changes that are sent downstream are considered streamable. We
833 * remember such transactions so that only those will later be considered
834 * for streaming.
835 */
836 if (change->action == REORDER_BUFFER_CHANGE_INSERT ||
842 {
843 ReorderBufferTXN *toptxn = rbtxn_get_toptxn(txn);
844
846 }
847
848 change->lsn = lsn;
849 change->txn = txn;
850
852 dlist_push_tail(&txn->changes, &change->node);
853 txn->nentries++;
854 txn->nentries_mem++;
855
856 /* update memory accounting information */
857 ReorderBufferChangeMemoryUpdate(rb, change, NULL, true,
859
860 /* process partial change */
861 ReorderBufferProcessPartialChange(rb, txn, change, toast_insert);
862
863 /* check the memory limits and evict something if needed */
865}
static void ReorderBufferProcessPartialChange(ReorderBuffer *rb, ReorderBufferTXN *txn, ReorderBufferChange *change, bool toast_insert)
static void ReorderBufferCheckMemoryLimit(ReorderBuffer *rb)
#define RBTXN_HAS_STREAMABLE_CHANGE

References ReorderBufferChange::action, Assert(), ReorderBufferTXN::changes, dlist_push_tail(), InvalidXLogRecPtr, ReorderBufferChange::lsn, ReorderBufferTXN::nentries, ReorderBufferTXN::nentries_mem, ReorderBufferChange::node, rbtxn_get_toptxn, RBTXN_HAS_STREAMABLE_CHANGE, rbtxn_is_aborted, REORDER_BUFFER_CHANGE_DELETE, REORDER_BUFFER_CHANGE_INSERT, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT, REORDER_BUFFER_CHANGE_MESSAGE, REORDER_BUFFER_CHANGE_TRUNCATE, REORDER_BUFFER_CHANGE_UPDATE, ReorderBufferChangeMemoryUpdate(), ReorderBufferChangeSize(), ReorderBufferCheckMemoryLimit(), ReorderBufferFreeChange(), ReorderBufferProcessPartialChange(), ReorderBufferTXNByXid(), ReorderBufferChange::txn, and ReorderBufferTXN::txn_flags.

Referenced by DecodeDelete(), DecodeInsert(), DecodeMultiInsert(), DecodeSpecConfirm(), DecodeTruncate(), DecodeUpdate(), ReorderBufferAddNewCommandId(), ReorderBufferAddSnapshot(), ReorderBufferQueueInvalidations(), and ReorderBufferQueueMessage().

◆ ReorderBufferQueueInvalidations()

◆ ReorderBufferQueueMessage()

void ReorderBufferQueueMessage ( ReorderBuffer rb,
TransactionId  xid,
Snapshot  snap,
XLogRecPtr  lsn,
bool  transactional,
const char *  prefix,
Size  message_size,
const char *  message 
)

Definition at line 872 of file reorderbuffer.c.

876{
877 if (transactional)
878 {
879 MemoryContext oldcontext;
880 ReorderBufferChange *change;
881
883
884 /*
885 * We don't expect snapshots for transactional changes - we'll use the
886 * snapshot derived later during apply (unless the change gets
887 * skipped).
888 */
889 Assert(!snap);
890
891 oldcontext = MemoryContextSwitchTo(rb->context);
892
893 change = ReorderBufferAllocChange(rb);
895 change->data.msg.prefix = pstrdup(prefix);
896 change->data.msg.message_size = message_size;
897 change->data.msg.message = palloc(message_size);
898 memcpy(change->data.msg.message, message, message_size);
899
900 ReorderBufferQueueChange(rb, xid, lsn, change, false);
901
902 MemoryContextSwitchTo(oldcontext);
903 }
904 else
905 {
906 ReorderBufferTXN *txn = NULL;
907 volatile Snapshot snapshot_now = snap;
908
909 /* Non-transactional changes require a valid snapshot. */
910 Assert(snapshot_now);
911
912 if (xid != InvalidTransactionId)
913 txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
914
915 /* setup snapshot to allow catalog access */
916 SetupHistoricSnapshot(snapshot_now, NULL);
917 PG_TRY();
918 {
919 rb->message(rb, txn, lsn, false, prefix, message_size, message);
920
922 }
923 PG_CATCH();
924 {
926 PG_RE_THROW();
927 }
928 PG_END_TRY();
929 }
930}

References ReorderBufferChange::action, Assert(), ReorderBuffer::context, ReorderBufferChange::data, InvalidTransactionId, MemoryContextSwitchTo(), ReorderBufferChange::message, ReorderBuffer::message, ReorderBufferChange::message_size, ReorderBufferChange::msg, palloc(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, ReorderBufferChange::prefix, pstrdup(), REORDER_BUFFER_CHANGE_MESSAGE, ReorderBufferAllocChange(), ReorderBufferQueueChange(), ReorderBufferTXNByXid(), SetupHistoricSnapshot(), and TeardownHistoricSnapshot().

Referenced by logicalmsg_decode().

◆ ReorderBufferRememberPrepareInfo()

bool ReorderBufferRememberPrepareInfo ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  prepare_lsn,
XLogRecPtr  end_lsn,
TimestampTz  prepare_time,
RepOriginId  origin_id,
XLogRecPtr  origin_lsn 
)

Definition at line 2906 of file reorderbuffer.c.

2910{
2911 ReorderBufferTXN *txn;
2912
2913 txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr, false);
2914
2915 /* unknown transaction, nothing to do */
2916 if (txn == NULL)
2917 return false;
2918
2919 /*
2920 * Remember the prepare information to be later used by commit prepared in
2921 * case we skip doing prepare.
2922 */
2923 txn->final_lsn = prepare_lsn;
2924 txn->end_lsn = end_lsn;
2925 txn->xact_time.prepare_time = prepare_time;
2926 txn->origin_id = origin_id;
2927 txn->origin_lsn = origin_lsn;
2928
2929 /* Mark this transaction as a prepared transaction */
2932
2933 return true;
2934}

References Assert(), ReorderBufferTXN::end_lsn, ReorderBufferTXN::final_lsn, InvalidXLogRecPtr, ReorderBufferTXN::origin_id, ReorderBufferTXN::origin_lsn, ReorderBufferTXN::prepare_time, RBTXN_IS_PREPARED, RBTXN_PREPARE_STATUS_MASK, ReorderBufferTXNByXid(), ReorderBufferTXN::txn_flags, and ReorderBufferTXN::xact_time.

Referenced by DecodePrepare().

◆ ReorderBufferReplay()

static void ReorderBufferReplay ( ReorderBufferTXN txn,
ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  commit_lsn,
XLogRecPtr  end_lsn,
TimestampTz  commit_time,
RepOriginId  origin_id,
XLogRecPtr  origin_lsn 
)
static

Definition at line 2822 of file reorderbuffer.c.

2827{
2828 Snapshot snapshot_now;
2829 CommandId command_id = FirstCommandId;
2830
2831 txn->final_lsn = commit_lsn;
2832 txn->end_lsn = end_lsn;
2833 txn->xact_time.commit_time = commit_time;
2834 txn->origin_id = origin_id;
2835 txn->origin_lsn = origin_lsn;
2836
2837 /*
2838 * If the transaction was (partially) streamed, we need to commit it in a
2839 * 'streamed' way. That is, we first stream the remaining part of the
2840 * transaction, and then invoke stream_commit message.
2841 *
2842 * Called after everything (origin ID, LSN, ...) is stored in the
2843 * transaction to avoid passing that information directly.
2844 */
2845 if (rbtxn_is_streamed(txn))
2846 {
2848 return;
2849 }
2850
2851 /*
2852 * If this transaction has no snapshot, it didn't make any changes to the
2853 * database, so there's nothing to decode. Note that
2854 * ReorderBufferCommitChild will have transferred any snapshots from
2855 * subtransactions if there were any.
2856 */
2857 if (txn->base_snapshot == NULL)
2858 {
2859 Assert(txn->ninvalidations == 0);
2860
2861 /*
2862 * Removing this txn before a commit might result in the computation
2863 * of an incorrect restart_lsn. See SnapBuildProcessRunningXacts.
2864 */
2865 if (!rbtxn_is_prepared(txn))
2866 ReorderBufferCleanupTXN(rb, txn);
2867 return;
2868 }
2869
2870 snapshot_now = txn->base_snapshot;
2871
2872 /* Process and send the changes to output plugin. */
2873 ReorderBufferProcessTXN(rb, txn, commit_lsn, snapshot_now,
2874 command_id, false);
2875}
#define FirstCommandId
Definition: c.h:674
uint32 CommandId
Definition: c.h:672
static void ReorderBufferStreamCommit(ReorderBuffer *rb, ReorderBufferTXN *txn)
static void ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, XLogRecPtr commit_lsn, volatile Snapshot snapshot_now, volatile CommandId command_id, bool streaming)

References Assert(), ReorderBufferTXN::base_snapshot, ReorderBufferTXN::commit_time, ReorderBufferTXN::end_lsn, ReorderBufferTXN::final_lsn, FirstCommandId, ReorderBufferTXN::ninvalidations, ReorderBufferTXN::origin_id, ReorderBufferTXN::origin_lsn, rbtxn_is_prepared, rbtxn_is_streamed, ReorderBufferCleanupTXN(), ReorderBufferProcessTXN(), ReorderBufferStreamCommit(), and ReorderBufferTXN::xact_time.

Referenced by ReorderBufferCommit(), ReorderBufferFinishPrepared(), and ReorderBufferPrepare().

◆ ReorderBufferResetTXN()

static void ReorderBufferResetTXN ( ReorderBuffer rb,
ReorderBufferTXN txn,
Snapshot  snapshot_now,
CommandId  command_id,
XLogRecPtr  last_lsn,
ReorderBufferChange specinsert 
)
static

Definition at line 2164 of file reorderbuffer.c.

2169{
2170 /* Discard the changes that we just streamed */
2172
2173 /* Free all resources allocated for toast reconstruction */
2174 ReorderBufferToastReset(rb, txn);
2175
2176 /* Return the spec insert change if it is not NULL */
2177 if (specinsert != NULL)
2178 {
2179 ReorderBufferFreeChange(rb, specinsert, true);
2180 specinsert = NULL;
2181 }
2182
2183 /*
2184 * For the streaming case, stop the stream and remember the command ID and
2185 * snapshot for the streaming run.
2186 */
2187 if (rbtxn_is_streamed(txn))
2188 {
2189 rb->stream_stop(rb, txn, last_lsn);
2190 ReorderBufferSaveTXNSnapshot(rb, txn, snapshot_now, command_id);
2191 }
2192
2193 /* All changes must be deallocated */
2194 Assert(txn->size == 0);
2195}

References Assert(), rbtxn_is_prepared, rbtxn_is_streamed, ReorderBufferFreeChange(), ReorderBufferSaveTXNSnapshot(), ReorderBufferToastReset(), ReorderBufferTruncateTXN(), ReorderBufferTXN::size, and ReorderBuffer::stream_stop.

Referenced by ReorderBufferProcessTXN().

◆ ReorderBufferRestoreChange()

static void ReorderBufferRestoreChange ( ReorderBuffer rb,
ReorderBufferTXN txn,
char *  data 
)
static

Definition at line 4668 of file reorderbuffer.c.

4670{
4672 ReorderBufferChange *change;
4673
4674 ondisk = (ReorderBufferDiskChange *) data;
4675
4676 change = ReorderBufferAllocChange(rb);
4677
4678 /* copy static part */
4679 memcpy(change, &ondisk->change, sizeof(ReorderBufferChange));
4680
4681 data += sizeof(ReorderBufferDiskChange);
4682
4683 /* restore individual stuff */
4684 switch (change->action)
4685 {
4686 /* fall through these, they're all similar enough */
4691 if (change->data.tp.oldtuple)
4692 {
4693 uint32 tuplelen = ((HeapTuple) data)->t_len;
4694
4695 change->data.tp.oldtuple =
4697
4698 /* restore ->tuple */
4699 memcpy(change->data.tp.oldtuple, data,
4700 sizeof(HeapTupleData));
4701 data += sizeof(HeapTupleData);
4702
4703 /* reset t_data pointer into the new tuplebuf */
4704 change->data.tp.oldtuple->t_data =
4705 (HeapTupleHeader) ((char *) change->data.tp.oldtuple + HEAPTUPLESIZE);
4706
4707 /* restore tuple data itself */
4708 memcpy(change->data.tp.oldtuple->t_data, data, tuplelen);
4709 data += tuplelen;
4710 }
4711
4712 if (change->data.tp.newtuple)
4713 {
4714 /* here, data might not be suitably aligned! */
4715 uint32 tuplelen;
4716
4717 memcpy(&tuplelen, data + offsetof(HeapTupleData, t_len),
4718 sizeof(uint32));
4719
4720 change->data.tp.newtuple =
4722
4723 /* restore ->tuple */
4724 memcpy(change->data.tp.newtuple, data,
4725 sizeof(HeapTupleData));
4726 data += sizeof(HeapTupleData);
4727
4728 /* reset t_data pointer into the new tuplebuf */
4729 change->data.tp.newtuple->t_data =
4730 (HeapTupleHeader) ((char *) change->data.tp.newtuple + HEAPTUPLESIZE);
4731
4732 /* restore tuple data itself */
4733 memcpy(change->data.tp.newtuple->t_data, data, tuplelen);
4734 data += tuplelen;
4735 }
4736
4737 break;
4739 {
4740 Size prefix_size;
4741
4742 /* read prefix */
4743 memcpy(&prefix_size, data, sizeof(Size));
4744 data += sizeof(Size);
4746 prefix_size);
4747 memcpy(change->data.msg.prefix, data, prefix_size);
4748 Assert(change->data.msg.prefix[prefix_size - 1] == '\0');
4749 data += prefix_size;
4750
4751 /* read the message */
4752 memcpy(&change->data.msg.message_size, data, sizeof(Size));
4753 data += sizeof(Size);
4755 change->data.msg.message_size);
4756 memcpy(change->data.msg.message, data,
4757 change->data.msg.message_size);
4758 data += change->data.msg.message_size;
4759
4760 break;
4761 }
4763 {
4764 Size inval_size = sizeof(SharedInvalidationMessage) *
4765 change->data.inval.ninvalidations;
4766
4767 change->data.inval.invalidations =
4768 MemoryContextAlloc(rb->context, inval_size);
4769
4770 /* read the message */
4771 memcpy(change->data.inval.invalidations, data, inval_size);
4772
4773 break;
4774 }
4776 {
4777 Snapshot oldsnap;
4778 Snapshot newsnap;
4779 Size size;
4780
4781 oldsnap = (Snapshot) data;
4782
4783 size = sizeof(SnapshotData) +
4784 sizeof(TransactionId) * oldsnap->xcnt +
4785 sizeof(TransactionId) * (oldsnap->subxcnt + 0);
4786
4787 change->data.snapshot = MemoryContextAllocZero(rb->context, size);
4788
4789 newsnap = change->data.snapshot;
4790
4791 memcpy(newsnap, data, size);
4792 newsnap->xip = (TransactionId *)
4793 (((char *) newsnap) + sizeof(SnapshotData));
4794 newsnap->subxip = newsnap->xip + newsnap->xcnt;
4795 newsnap->copied = true;
4796 break;
4797 }
4798 /* the base struct contains all the data, easy peasy */
4800 {
4801 Oid *relids;
4802
4803 relids = ReorderBufferAllocRelids(rb, change->data.truncate.nrelids);
4804 memcpy(relids, data, change->data.truncate.nrelids * sizeof(Oid));
4805 change->data.truncate.relids = relids;
4806
4807 break;
4808 }
4813 break;
4814 }
4815
4816 dlist_push_tail(&txn->changes, &change->node);
4817 txn->nentries_mem++;
4818
4819 /*
4820 * Update memory accounting for the restored change. We need to do this
4821 * although we don't check the memory limit when restoring the changes in
4822 * this branch (we only do that when initially queueing the changes after
4823 * decoding), because we will release the changes later, and that will
4824 * update the accounting too (subtracting the size from the counters). And
4825 * we don't want to underflow there.
4826 */
4827 ReorderBufferChangeMemoryUpdate(rb, change, NULL, true,
4828 ReorderBufferChangeSize(change));
4829}
struct ReorderBufferDiskChange ReorderBufferDiskChange
HeapTuple ReorderBufferAllocTupleBuf(ReorderBuffer *rb, Size tuple_len)
Oid * ReorderBufferAllocRelids(ReorderBuffer *rb, int nrelids)
struct SnapshotData * Snapshot
Definition: snapshot.h:117
ReorderBufferChange change

References ReorderBufferChange::action, Assert(), ReorderBufferDiskChange::change, ReorderBufferTXN::changes, ReorderBuffer::context, SnapshotData::copied, ReorderBufferChange::data, data, dlist_push_tail(), HEAPTUPLESIZE, ReorderBufferChange::inval, ReorderBufferChange::invalidations, MemoryContextAlloc(), MemoryContextAllocZero(), ReorderBufferChange::message, ReorderBufferChange::message_size, ReorderBufferChange::msg, ReorderBufferTXN::nentries_mem, ReorderBufferChange::newtuple, ReorderBufferChange::ninvalidations, ReorderBufferChange::node, ReorderBufferChange::nrelids, ReorderBufferChange::oldtuple, ReorderBufferChange::prefix, ReorderBufferChange::relids, REORDER_BUFFER_CHANGE_DELETE, REORDER_BUFFER_CHANGE_INSERT, REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID, REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT, REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID, REORDER_BUFFER_CHANGE_INVALIDATION, REORDER_BUFFER_CHANGE_MESSAGE, REORDER_BUFFER_CHANGE_TRUNCATE, REORDER_BUFFER_CHANGE_UPDATE, ReorderBufferAllocChange(), ReorderBufferAllocRelids(), ReorderBufferAllocTupleBuf(), ReorderBufferChangeMemoryUpdate(), ReorderBufferChangeSize(), SizeofHeapTupleHeader, ReorderBufferChange::snapshot, SnapshotData::subxcnt, SnapshotData::subxip, HeapTupleData::t_data, ReorderBufferChange::tp, ReorderBufferChange::truncate, SnapshotData::xcnt, and SnapshotData::xip.

Referenced by ReorderBufferRestoreChanges().

◆ ReorderBufferRestoreChanges()

static Size ReorderBufferRestoreChanges ( ReorderBuffer rb,
ReorderBufferTXN txn,
TXNEntryFile file,
XLogSegNo segno 
)
static

Definition at line 4525 of file reorderbuffer.c.

4527{
4528 Size restored = 0;
4529 XLogSegNo last_segno;
4530 dlist_mutable_iter cleanup_iter;
4531 File *fd = &file->vfd;
4532
4535
4536 /* free current entries, so we have memory for more */
4537 dlist_foreach_modify(cleanup_iter, &txn->changes)
4538 {
4540 dlist_container(ReorderBufferChange, node, cleanup_iter.cur);
4541
4542 dlist_delete(&cleanup->node);
4544 }
4545 txn->nentries_mem = 0;
4547
4548 XLByteToSeg(txn->final_lsn, last_segno, wal_segment_size);
4549
4550 while (restored < max_changes_in_memory && *segno <= last_segno)
4551 {
4552 int readBytes;
4554
4556
4557 if (*fd == -1)
4558 {
4559 char path[MAXPGPATH];
4560
4561 /* first time in */
4562 if (*segno == 0)
4563 XLByteToSeg(txn->first_lsn, *segno, wal_segment_size);
4564
4565 Assert(*segno != 0 || dlist_is_empty(&txn->changes));
4566
4567 /*
4568 * No need to care about TLIs here, only used during a single run,
4569 * so each LSN only maps to a specific WAL record.
4570 */
4572 *segno);
4573
4574 *fd = PathNameOpenFile(path, O_RDONLY | PG_BINARY);
4575
4576 /* No harm in resetting the offset even in case of failure */
4577 file->curOffset = 0;
4578
4579 if (*fd < 0 && errno == ENOENT)
4580 {
4581 *fd = -1;
4582 (*segno)++;
4583 continue;
4584 }
4585 else if (*fd < 0)
4586 ereport(ERROR,
4588 errmsg("could not open file \"%s\": %m",
4589 path)));
4590 }
4591
4592 /*
4593 * Read the statically sized part of a change which has information
4594 * about the total size. If we couldn't read a record, we're at the
4595 * end of this file.
4596 */
4598 readBytes = FileRead(file->vfd, rb->outbuf,
4600 file->curOffset, WAIT_EVENT_REORDER_BUFFER_READ);
4601
4602 /* eof */
4603 if (readBytes == 0)
4604 {
4605 FileClose(*fd);
4606 *fd = -1;
4607 (*segno)++;
4608 continue;
4609 }
4610 else if (readBytes < 0)
4611 ereport(ERROR,
4613 errmsg("could not read from reorderbuffer spill file: %m")));
4614 else if (readBytes != sizeof(ReorderBufferDiskChange))
4615 ereport(ERROR,
4617 errmsg("could not read from reorderbuffer spill file: read %d instead of %u bytes",
4618 readBytes,
4619 (uint32) sizeof(ReorderBufferDiskChange))));
4620
4621 file->curOffset += readBytes;
4622
4623 ondisk = (ReorderBufferDiskChange *) rb->outbuf;
4624
4626 sizeof(ReorderBufferDiskChange) + ondisk->size);
4627 ondisk = (ReorderBufferDiskChange *) rb->outbuf;
4628
4629 readBytes = FileRead(file->vfd,
4630 rb->outbuf + sizeof(ReorderBufferDiskChange),
4631 ondisk->size - sizeof(ReorderBufferDiskChange),
4632 file->curOffset,
4633 WAIT_EVENT_REORDER_BUFFER_READ);
4634
4635 if (readBytes < 0)
4636 ereport(ERROR,
4638 errmsg("could not read from reorderbuffer spill file: %m")));
4639 else if (readBytes != ondisk->size - sizeof(ReorderBufferDiskChange))
4640 ereport(ERROR,
4642 errmsg("could not read from reorderbuffer spill file: read %d instead of %u bytes",
4643 readBytes,
4644 (uint32) (ondisk->size - sizeof(ReorderBufferDiskChange)))));
4645
4646 file->curOffset += readBytes;
4647
4648 /*
4649 * ok, read a full change from disk, now restore it into proper
4650 * in-memory format
4651 */
4652 ReorderBufferRestoreChange(rb, txn, rb->outbuf);
4653 restored++;
4654 }
4655
4656 return restored;
4657}
static void cleanup(void)
Definition: bootstrap.c:715
File PathNameOpenFile(const char *fileName, int fileFlags)
Definition: fd.c:1576
static ssize_t FileRead(File file, void *buffer, size_t amount, off_t offset, uint32 wait_event_info)
Definition: fd.h:199
int File
Definition: fd.h:51
static void ReorderBufferSerializeReserve(ReorderBuffer *rb, Size sz)
static void ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn, char *data)
static void ReorderBufferSerializedPath(char *path, ReplicationSlot *slot, TransactionId xid, XLogSegNo segno)
static const Size max_changes_in_memory
int wal_segment_size
Definition: xlog.c:144
#define XLByteToSeg(xlrp, logSegNo, wal_segsz_bytes)
uint64 XLogSegNo
Definition: xlogdefs.h:51

References Assert(), ReorderBufferTXN::changes, CHECK_FOR_INTERRUPTS, cleanup(), dlist_mutable_iter::cur, TXNEntryFile::curOffset, dlist_container, dlist_delete(), dlist_foreach_modify, dlist_is_empty(), ereport, errcode_for_file_access(), errmsg(), ERROR, fd(), FileClose(), FileRead(), ReorderBufferTXN::final_lsn, ReorderBufferTXN::first_lsn, InvalidXLogRecPtr, max_changes_in_memory, MAXPGPATH, MyReplicationSlot, ReorderBufferTXN::nentries_mem, ReorderBuffer::outbuf, PathNameOpenFile(), PG_BINARY, ReorderBufferFreeChange(), ReorderBufferRestoreChange(), ReorderBufferSerializedPath(), ReorderBufferSerializeReserve(), ReorderBufferDiskChange::size, TXNEntryFile::vfd, wal_segment_size, ReorderBufferTXN::xid, and XLByteToSeg.

Referenced by ReorderBufferIterTXNInit(), and ReorderBufferIterTXNNext().

◆ ReorderBufferRestoreCleanup()

static void ReorderBufferRestoreCleanup ( ReorderBuffer rb,
ReorderBufferTXN txn 
)
static

Definition at line 4835 of file reorderbuffer.c.

4836{
4837 XLogSegNo first;
4838 XLogSegNo cur;
4839 XLogSegNo last;
4840
4843
4846
4847 /* iterate over all possible filenames, and delete them */
4848 for (cur = first; cur <= last; cur++)
4849 {
4850 char path[MAXPGPATH];
4851
4853 if (unlink(path) != 0 && errno != ENOENT)
4854 ereport(ERROR,
4856 errmsg("could not remove file \"%s\": %m", path)));
4857 }
4858}
struct cursor * cur
Definition: ecpg.c:29

References Assert(), cur, ereport, errcode_for_file_access(), errmsg(), ERROR, ReorderBufferTXN::final_lsn, ReorderBufferTXN::first_lsn, InvalidXLogRecPtr, MAXPGPATH, MyReplicationSlot, ReorderBufferSerializedPath(), wal_segment_size, ReorderBufferTXN::xid, and XLByteToSeg.

Referenced by ReorderBufferCleanupTXN(), and ReorderBufferTruncateTXN().

◆ ReorderBufferSaveTXNSnapshot()

static void ReorderBufferSaveTXNSnapshot ( ReorderBuffer rb,
ReorderBufferTXN txn,
Snapshot  snapshot_now,
CommandId  command_id 
)
inlinestatic

Definition at line 2119 of file reorderbuffer.c.

2121{
2122 txn->command_id = command_id;
2123
2124 /* Avoid copying if it's already copied. */
2125 if (snapshot_now->copied)
2126 txn->snapshot_now = snapshot_now;
2127 else
2128 txn->snapshot_now = ReorderBufferCopySnap(rb, snapshot_now,
2129 txn, command_id);
2130}

References ReorderBufferTXN::command_id, SnapshotData::copied, ReorderBufferCopySnap(), and ReorderBufferTXN::snapshot_now.

Referenced by ReorderBufferProcessTXN(), and ReorderBufferResetTXN().

◆ ReorderBufferSerializeChange()

static void ReorderBufferSerializeChange ( ReorderBuffer rb,
ReorderBufferTXN txn,
int  fd,
ReorderBufferChange change 
)
static

Definition at line 4073 of file reorderbuffer.c.

4075{
4077 Size sz = sizeof(ReorderBufferDiskChange);
4078
4080
4081 ondisk = (ReorderBufferDiskChange *) rb->outbuf;
4082 memcpy(&ondisk->change, change, sizeof(ReorderBufferChange));
4083
4084 switch (change->action)
4085 {
4086 /* fall through these, they're all similar enough */
4091 {
4092 char *data;
4093 HeapTuple oldtup,
4094 newtup;
4095 Size oldlen = 0;
4096 Size newlen = 0;
4097
4098 oldtup = change->data.tp.oldtuple;
4099 newtup = change->data.tp.newtuple;
4100
4101 if (oldtup)
4102 {
4103 sz += sizeof(HeapTupleData);
4104 oldlen = oldtup->t_len;
4105 sz += oldlen;
4106 }
4107
4108 if (newtup)
4109 {
4110 sz += sizeof(HeapTupleData);
4111 newlen = newtup->t_len;
4112 sz += newlen;
4113 }
4114
4115 /* make sure we have enough space */
4117
4118 data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange);
4119 /* might have been reallocated above */
4120 ondisk = (ReorderBufferDiskChange *) rb->outbuf;
4121
4122 if (oldlen)
4123 {
4124 memcpy(data, oldtup, sizeof(HeapTupleData));
4125 data += sizeof(HeapTupleData);
4126
4127 memcpy(data, oldtup->t_data, oldlen);
4128 data += oldlen;
4129 }
4130
4131 if (newlen)
4132 {
4133 memcpy(data, newtup, sizeof(HeapTupleData));
4134 data += sizeof(HeapTupleData);
4135
4136 memcpy(data, newtup->t_data, newlen);
4137 data += newlen;
4138 }
4139 break;
4140 }
4142 {
4143 char *data;
4144 Size prefix_size = strlen(change->data.msg.prefix) + 1;
4145
4146 sz += prefix_size + change->data.msg.message_size +
4147 sizeof(Size) + sizeof(Size);
4149
4150 data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange);
4151
4152 /* might have been reallocated above */
4153 ondisk = (ReorderBufferDiskChange *) rb->outbuf;
4154
4155 /* write the prefix including the size */
4156 memcpy(data, &prefix_size, sizeof(Size));
4157 data += sizeof(Size);
4158 memcpy(data, change->data.msg.prefix,
4159 prefix_size);
4160 data += prefix_size;
4161
4162 /* write the message including the size */
4163 memcpy(data, &change->data.msg.message_size, sizeof(Size));
4164 data += sizeof(Size);
4165 memcpy(data, change->data.msg.message,
4166 change->data.msg.message_size);
4167 data += change->data.msg.message_size;
4168
4169 break;
4170 }
4172 {
4173 char *data;
4174 Size inval_size = sizeof(SharedInvalidationMessage) *
4175 change->data.inval.ninvalidations;
4176
4177 sz += inval_size;
4178
4180 data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange);
4181
4182 /* might have been reallocated above */
4183 ondisk = (ReorderBufferDiskChange *) rb->outbuf;
4184 memcpy(data, change->data.inval.invalidations, inval_size);
4185 data += inval_size;
4186
4187 break;
4188 }
4190 {
4191 Snapshot snap;
4192 char *data;
4193
4194 snap = change->data.snapshot;
4195
4196 sz += sizeof(SnapshotData) +
4197 sizeof(TransactionId) * snap->xcnt +
4198 sizeof(TransactionId) * snap->subxcnt;
4199
4200 /* make sure we have enough space */
4202 data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange);
4203 /* might have been reallocated above */
4204 ondisk = (ReorderBufferDiskChange *) rb->outbuf;
4205
4206 memcpy(data, snap, sizeof(SnapshotData));
4207 data += sizeof(SnapshotData);
4208
4209 if (snap->xcnt)
4210 {
4211 memcpy(data, snap->xip,
4212 sizeof(TransactionId) * snap->xcnt);
4213 data += sizeof(TransactionId) * snap->xcnt;
4214 }
4215
4216 if (snap->subxcnt)
4217 {
4218 memcpy(data, snap->subxip,
4219 sizeof(TransactionId) * snap->subxcnt);
4220 data += sizeof(TransactionId) * snap->subxcnt;
4221 }
4222 break;
4223 }
4225 {
4226 Size size;
4227 char *data;
4228
4229 /* account for the OIDs of truncated relations */
4230 size = sizeof(Oid) * change->data.truncate.nrelids;
4231 sz += size;
4232
4233 /* make sure we have enough space */
4235
4236 data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange);
4237 /* might have been reallocated above */
4238 ondisk = (ReorderBufferDiskChange *) rb->outbuf;
4239
4240 memcpy(data, change->data.truncate.relids, size);
4241 data += size;
4242
4243 break;
4244 }
4249 /* ReorderBufferChange contains everything important */
4250 break;
4251 }
4252
4253 ondisk->size = sz;
4254
4255 errno = 0;
4256 pgstat_report_wait_start(WAIT_EVENT_REORDER_BUFFER_WRITE);
4257 if (write(fd, rb->outbuf, ondisk->size) != ondisk->size)
4258 {
4259 int save_errno = errno;
4260
4262
4263 /* if write didn't set errno, assume problem is no disk space */
4264 errno = save_errno ? save_errno : ENOSPC;
4265 ereport(ERROR,
4267 errmsg("could not write to data file for XID %u: %m",
4268 txn->xid)));
4269 }
4271
4272 /*
4273 * Keep the transaction's final_lsn up to date with each change we send to
4274 * disk, so that ReorderBufferRestoreCleanup works correctly. (We used to
4275 * only do this on commit and abort records, but that doesn't work if a
4276 * system crash leaves a transaction without its abort record).
4277 *
4278 * Make sure not to move it backwards.
4279 */
4280 if (txn->final_lsn < change->lsn)
4281 txn->final_lsn = change->lsn;
4282
4283 Assert(ondisk->change.action == change->action);
4284}
#define write(a, b, c)
Definition: win32.h:14
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81

References ReorderBufferChange::action, Assert(), ReorderBufferDiskChange::change, CloseTransientFile(), ReorderBufferChange::data, data, ereport, errcode_for_file_access(), errmsg(), ERROR, fd(), ReorderBufferTXN::final_lsn, if(), ReorderBufferChange::inval, ReorderBufferChange::invalidations, ReorderBufferChange::lsn, ReorderBufferChange::message, ReorderBufferChange::message_size, ReorderBufferChange::msg, ReorderBufferChange::newtuple, ReorderBufferChange::ninvalidations, ReorderBufferChange::nrelids, ReorderBufferChange::oldtuple, ReorderBuffer::outbuf, pgstat_report_wait_end(), pgstat_report_wait_start(), ReorderBufferChange::prefix, ReorderBufferChange::relids, REORDER_BUFFER_CHANGE_DELETE, REORDER_BUFFER_CHANGE_INSERT, REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID, REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM, REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT, REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID, REORDER_BUFFER_CHANGE_INVALIDATION, REORDER_BUFFER_CHANGE_MESSAGE, REORDER_BUFFER_CHANGE_TRUNCATE, REORDER_BUFFER_CHANGE_UPDATE, ReorderBufferSerializeReserve(), ReorderBufferDiskChange::size, ReorderBufferChange::snapshot, SnapshotData::subxcnt, SnapshotData::subxip, HeapTupleData::t_data, HeapTupleData::t_len, ReorderBufferChange::tp, ReorderBufferChange::truncate, write, SnapshotData::xcnt, ReorderBufferTXN::xid, and SnapshotData::xip.

Referenced by ReorderBufferSerializeTXN().

◆ ReorderBufferSerializedPath()

static void ReorderBufferSerializedPath ( char *  path,
ReplicationSlot slot,
TransactionId  xid,
XLogSegNo  segno 
)
static

Definition at line 4904 of file reorderbuffer.c.

4906{
4907 XLogRecPtr recptr;
4908
4909 XLogSegNoOffsetToRecPtr(segno, 0, wal_segment_size, recptr);
4910
4911 snprintf(path, MAXPGPATH, "%s/%s/xid-%u-lsn-%X-%X.spill",
4914 xid, LSN_FORMAT_ARGS(recptr));
4915}
#define XLogSegNoOffsetToRecPtr(segno, offset, wal_segsz_bytes, dest)
#define LSN_FORMAT_ARGS(lsn)
Definition: xlogdefs.h:46

References ReplicationSlot::data, LSN_FORMAT_ARGS, MAXPGPATH, MyReplicationSlot, ReplicationSlotPersistentData::name, NameStr, PG_REPLSLOT_DIR, snprintf, wal_segment_size, and XLogSegNoOffsetToRecPtr.

Referenced by ReorderBufferRestoreChanges(), ReorderBufferRestoreCleanup(), and ReorderBufferSerializeTXN().

◆ ReorderBufferSerializeReserve()

static void ReorderBufferSerializeReserve ( ReorderBuffer rb,
Size  sz 
)
static

Definition at line 3774 of file reorderbuffer.c.

3775{
3776 if (!rb->outbufsize)
3777 {
3778 rb->outbuf = MemoryContextAlloc(rb->context, sz);
3779 rb->outbufsize = sz;
3780 }
3781 else if (rb->outbufsize < sz)
3782 {
3783 rb->outbuf = repalloc(rb->outbuf, sz);
3784 rb->outbufsize = sz;
3785 }
3786}

References ReorderBuffer::context, MemoryContextAlloc(), ReorderBuffer::outbuf, ReorderBuffer::outbufsize, and repalloc().

Referenced by ReorderBufferRestoreChanges(), and ReorderBufferSerializeChange().

◆ ReorderBufferSerializeTXN()

static void ReorderBufferSerializeTXN ( ReorderBuffer rb,
ReorderBufferTXN txn 
)
static

Definition at line 3978 of file reorderbuffer.c.

3979{
3980 dlist_iter subtxn_i;
3981 dlist_mutable_iter change_i;
3982 int fd = -1;
3983 XLogSegNo curOpenSegNo = 0;
3984 Size spilled = 0;
3985 Size size = txn->size;
3986
3987 elog(DEBUG2, "spill %u changes in XID %u to disk",
3988 (uint32) txn->nentries_mem, txn->xid);
3989
3990 /* do the same to all child TXs */
3991 dlist_foreach(subtxn_i, &txn->subtxns)
3992 {
3993 ReorderBufferTXN *subtxn;
3994
3995 subtxn = dlist_container(ReorderBufferTXN, node, subtxn_i.cur);
3996 ReorderBufferSerializeTXN(rb, subtxn);
3997 }
3998
3999 /* serialize changestream */
4000 dlist_foreach_modify(change_i, &txn->changes)
4001 {
4002 ReorderBufferChange *change;
4003
4004 change = dlist_container(ReorderBufferChange, node, change_i.cur);
4005
4006 /*
4007 * store in segment in which it belongs by start lsn, don't split over
4008 * multiple segments tho
4009 */
4010 if (fd == -1 ||
4011 !XLByteInSeg(change->lsn, curOpenSegNo, wal_segment_size))
4012 {
4013 char path[MAXPGPATH];
4014
4015 if (fd != -1)
4017
4018 XLByteToSeg(change->lsn, curOpenSegNo, wal_segment_size);
4019
4020 /*
4021 * No need to care about TLIs here, only used during a single run,
4022 * so each LSN only maps to a specific WAL record.
4023 */
4025 curOpenSegNo);
4026
4027 /* open segment, create it if necessary */
4028 fd = OpenTransientFile(path,
4029 O_CREAT | O_WRONLY | O_APPEND | PG_BINARY);
4030
4031 if (fd < 0)
4032 ereport(ERROR,
4034 errmsg("could not open file \"%s\": %m", path)));
4035 }
4036
4037 ReorderBufferSerializeChange(rb, txn, fd, change);
4038 dlist_delete(&change->node);
4039 ReorderBufferFreeChange(rb, change, false);
4040
4041 spilled++;
4042 }
4043
4044 /* Update the memory counter */
4045 ReorderBufferChangeMemoryUpdate(rb, NULL, txn, false, size);
4046
4047 /* update the statistics iff we have spilled anything */
4048 if (spilled)
4049 {
4050 rb->spillCount += 1;
4051 rb->spillBytes += size;
4052
4053 /* don't consider already serialized transactions */
4054 rb->spillTxns += (rbtxn_is_serialized(txn) || rbtxn_is_serialized_clear(txn)) ? 0 : 1;
4055
4056 /* update the decoding stats */
4058 }
4059
4060 Assert(spilled == txn->nentries_mem);
4062 txn->nentries_mem = 0;
4064
4065 if (fd != -1)
4067}
void UpdateDecodingStats(LogicalDecodingContext *ctx)
Definition: logical.c:1952
static void ReorderBufferSerializeChange(ReorderBuffer *rb, ReorderBufferTXN *txn, int fd, ReorderBufferChange *change)
#define rbtxn_is_serialized_clear(txn)
#define RBTXN_IS_SERIALIZED
#define XLByteInSeg(xlrp, logSegNo, wal_segsz_bytes)

References Assert(), ReorderBufferTXN::changes, CloseTransientFile(), dlist_iter::cur, dlist_mutable_iter::cur, DEBUG2, dlist_container, dlist_delete(), dlist_foreach, dlist_foreach_modify, dlist_is_empty(), elog, ereport, errcode_for_file_access(), errmsg(), ERROR, fd(), ReorderBufferChange::lsn, MAXPGPATH, MyReplicationSlot, ReorderBufferTXN::nentries_mem, ReorderBufferChange::node, OpenTransientFile(), PG_BINARY, ReorderBuffer::private_data, RBTXN_IS_SERIALIZED, rbtxn_is_serialized, rbtxn_is_serialized_clear, ReorderBufferChangeMemoryUpdate(), ReorderBufferFreeChange(), ReorderBufferSerializeChange(), ReorderBufferSerializedPath(), ReorderBufferSerializeTXN(), ReorderBufferTXN::size, ReorderBuffer::spillBytes, ReorderBuffer::spillCount, ReorderBuffer::spillTxns, ReorderBufferTXN::subtxns, ReorderBufferTXN::txn_flags, UpdateDecodingStats(), wal_segment_size, ReorderBufferTXN::xid, XLByteInSeg, and XLByteToSeg.

Referenced by ReorderBufferCheckMemoryLimit(), ReorderBufferIterTXNInit(), and ReorderBufferSerializeTXN().

◆ ReorderBufferSetBaseSnapshot()

void ReorderBufferSetBaseSnapshot ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  lsn,
Snapshot  snap 
)

Definition at line 3325 of file reorderbuffer.c.

3327{
3328 ReorderBufferTXN *txn;
3329 bool is_new;
3330
3331 Assert(snap != NULL);
3332
3333 /*
3334 * Fetch the transaction to operate on. If we know it's a subtransaction,
3335 * operate on its top-level transaction instead.
3336 */
3337 txn = ReorderBufferTXNByXid(rb, xid, true, &is_new, lsn, true);
3338 if (rbtxn_is_known_subxact(txn))
3339 txn = ReorderBufferTXNByXid(rb, txn->toplevel_xid, false,
3340 NULL, InvalidXLogRecPtr, false);
3341 Assert(txn->base_snapshot == NULL);
3342
3343 txn->base_snapshot = snap;
3344 txn->base_snapshot_lsn = lsn;
3346
3348}

References Assert(), AssertTXNLsnOrder(), ReorderBufferTXN::base_snapshot, ReorderBufferTXN::base_snapshot_lsn, ReorderBufferTXN::base_snapshot_node, dlist_push_tail(), InvalidXLogRecPtr, rbtxn_is_known_subxact, ReorderBufferTXNByXid(), ReorderBufferTXN::toplevel_xid, and ReorderBuffer::txns_by_base_snapshot_lsn.

Referenced by SnapBuildCommitTxn(), and SnapBuildProcessChange().

◆ ReorderBufferSetRestartPoint()

void ReorderBufferSetRestartPoint ( ReorderBuffer rb,
XLogRecPtr  ptr 
)

Definition at line 1086 of file reorderbuffer.c.

1087{
1089}

References ReorderBuffer::current_restart_decoding_lsn.

Referenced by SnapBuildRestore(), and SnapBuildSerialize().

◆ ReorderBufferSkipPrepare()

void ReorderBufferSkipPrepare ( ReorderBuffer rb,
TransactionId  xid 
)

Definition at line 2938 of file reorderbuffer.c.

2939{
2940 ReorderBufferTXN *txn;
2941
2942 txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr, false);
2943
2944 /* unknown transaction, nothing to do */
2945 if (txn == NULL)
2946 return;
2947
2948 /* txn must have been marked as a prepared transaction */
2951}

References Assert(), InvalidXLogRecPtr, RBTXN_IS_PREPARED, RBTXN_PREPARE_STATUS_MASK, RBTXN_SKIPPED_PREPARE, ReorderBufferTXNByXid(), and ReorderBufferTXN::txn_flags.

Referenced by DecodePrepare().

◆ ReorderBufferStreamCommit()

static void ReorderBufferStreamCommit ( ReorderBuffer rb,
ReorderBufferTXN txn 
)
static

Definition at line 1982 of file reorderbuffer.c.

1983{
1984 /* we should only call this for previously streamed transactions */
1986
1987 ReorderBufferStreamTXN(rb, txn);
1988
1989 if (rbtxn_is_prepared(txn))
1990 {
1991 /*
1992 * Note, we send stream prepare even if a concurrent abort is
1993 * detected. See DecodePrepare for more information.
1994 */
1996 rb->stream_prepare(rb, txn, txn->final_lsn);
1998
1999 /*
2000 * This is a PREPARED transaction, part of a two-phase commit. The
2001 * full cleanup will happen as part of the COMMIT PREPAREDs, so now
2002 * just truncate txn by removing changes and tuplecids.
2003 */
2004 ReorderBufferTruncateTXN(rb, txn, true);
2005 /* Reset the CheckXidAlive */
2007 }
2008 else
2009 {
2010 rb->stream_commit(rb, txn, txn->final_lsn);
2011 ReorderBufferCleanupTXN(rb, txn);
2012 }
2013}
ReorderBufferStreamPrepareCB stream_prepare
ReorderBufferStreamCommitCB stream_commit

References Assert(), CheckXidAlive, ReorderBufferTXN::final_lsn, InvalidTransactionId, rbtxn_is_prepared, rbtxn_is_streamed, RBTXN_SENT_PREPARE, rbtxn_sent_prepare, ReorderBufferCleanupTXN(), ReorderBufferStreamTXN(), ReorderBufferTruncateTXN(), ReorderBuffer::stream_commit, ReorderBuffer::stream_prepare, and ReorderBufferTXN::txn_flags.

Referenced by ReorderBufferReplay().

◆ ReorderBufferStreamTXN()

static void ReorderBufferStreamTXN ( ReorderBuffer rb,
ReorderBufferTXN txn 
)
static

Definition at line 4323 of file reorderbuffer.c.

4324{
4325 Snapshot snapshot_now;
4326 CommandId command_id;
4327 Size stream_bytes;
4328 bool txn_is_streamed;
4329
4330 /* We can never reach here for a subtransaction. */
4331 Assert(rbtxn_is_toptxn(txn));
4332
4333 /*
4334 * We can't make any assumptions about base snapshot here, similar to what
4335 * ReorderBufferCommit() does. That relies on base_snapshot getting
4336 * transferred from subxact in ReorderBufferCommitChild(), but that was
4337 * not yet called as the transaction is in-progress.
4338 *
4339 * So just walk the subxacts and use the same logic here. But we only need
4340 * to do that once, when the transaction is streamed for the first time.
4341 * After that we need to reuse the snapshot from the previous run.
4342 *
4343 * Unlike DecodeCommit which adds xids of all the subtransactions in
4344 * snapshot's xip array via SnapBuildCommitTxn, we can't do that here but
4345 * we do add them to subxip array instead via ReorderBufferCopySnap. This
4346 * allows the catalog changes made in subtransactions decoded till now to
4347 * be visible.
4348 */
4349 if (txn->snapshot_now == NULL)
4350 {
4351 dlist_iter subxact_i;
4352
4353 /* make sure this transaction is streamed for the first time */
4355
4356 /* at the beginning we should have invalid command ID */
4358
4359 dlist_foreach(subxact_i, &txn->subtxns)
4360 {
4361 ReorderBufferTXN *subtxn;
4362
4363 subtxn = dlist_container(ReorderBufferTXN, node, subxact_i.cur);
4365 }
4366
4367 /*
4368 * If this transaction has no snapshot, it didn't make any changes to
4369 * the database till now, so there's nothing to decode.
4370 */
4371 if (txn->base_snapshot == NULL)
4372 {
4373 Assert(txn->ninvalidations == 0);
4374 return;
4375 }
4376
4377 command_id = FirstCommandId;
4378 snapshot_now = ReorderBufferCopySnap(rb, txn->base_snapshot,
4379 txn, command_id);
4380 }
4381 else
4382 {
4383 /* the transaction must have been already streamed */
4385
4386 /*
4387 * Nah, we already have snapshot from the previous streaming run. We
4388 * assume new subxacts can't move the LSN backwards, and so can't beat
4389 * the LSN condition in the previous branch (so no need to walk
4390 * through subxacts again). In fact, we must not do that as we may be
4391 * using snapshot half-way through the subxact.
4392 */
4393 command_id = txn->command_id;
4394
4395 /*
4396 * We can't use txn->snapshot_now directly because after the last
4397 * streaming run, we might have got some new sub-transactions. So we
4398 * need to add them to the snapshot.
4399 */
4400 snapshot_now = ReorderBufferCopySnap(rb, txn->snapshot_now,
4401 txn, command_id);
4402
4403 /* Free the previously copied snapshot. */
4404 Assert(txn->snapshot_now->copied);
4406 txn->snapshot_now = NULL;
4407 }
4408
4409 /*
4410 * Remember this information to be used later to update stats. We can't
4411 * update the stats here as an error while processing the changes would
4412 * lead to the accumulation of stats even though we haven't streamed all
4413 * the changes.
4414 */
4415 txn_is_streamed = rbtxn_is_streamed(txn);
4416 stream_bytes = txn->total_size;
4417
4418 /* Process and send the changes to output plugin. */
4419 ReorderBufferProcessTXN(rb, txn, InvalidXLogRecPtr, snapshot_now,
4420 command_id, true);
4421
4422 rb->streamCount += 1;
4423 rb->streamBytes += stream_bytes;
4424
4425 /* Don't consider already streamed transaction. */
4426 rb->streamTxns += (txn_is_streamed) ? 0 : 1;
4427
4428 /* update the decoding stats */
4430
4432 Assert(txn->nentries == 0);
4433 Assert(txn->nentries_mem == 0);
4434}

References Assert(), ReorderBufferTXN::base_snapshot, ReorderBufferTXN::changes, ReorderBufferTXN::command_id, SnapshotData::copied, dlist_iter::cur, dlist_container, dlist_foreach, dlist_is_empty(), FirstCommandId, InvalidCommandId, InvalidXLogRecPtr, ReorderBufferTXN::nentries, ReorderBufferTXN::nentries_mem, ReorderBufferTXN::ninvalidations, ReorderBuffer::private_data, rbtxn_is_streamed, rbtxn_is_toptxn, ReorderBufferCopySnap(), ReorderBufferFreeSnap(), ReorderBufferProcessTXN(), ReorderBufferTransferSnapToParent(), ReorderBufferTXN::snapshot_now, ReorderBuffer::streamBytes, ReorderBuffer::streamCount, ReorderBuffer::streamTxns, ReorderBufferTXN::subtxns, ReorderBufferTXN::total_size, and UpdateDecodingStats().

Referenced by ReorderBufferCheckMemoryLimit(), ReorderBufferProcessPartialChange(), and ReorderBufferStreamCommit().

◆ ReorderBufferToastAppendChunk()

static void ReorderBufferToastAppendChunk ( ReorderBuffer rb,
ReorderBufferTXN txn,
Relation  relation,
ReorderBufferChange change 
)
static

Definition at line 4976 of file reorderbuffer.c.

4978{
4980 HeapTuple newtup;
4981 bool found;
4982 int32 chunksize;
4983 bool isnull;
4984 Pointer chunk;
4985 TupleDesc desc = RelationGetDescr(relation);
4986 Oid chunk_id;
4987 int32 chunk_seq;
4988
4989 if (txn->toast_hash == NULL)
4991
4992 Assert(IsToastRelation(relation));
4993
4994 newtup = change->data.tp.newtuple;
4995 chunk_id = DatumGetObjectId(fastgetattr(newtup, 1, desc, &isnull));
4996 Assert(!isnull);
4997 chunk_seq = DatumGetInt32(fastgetattr(newtup, 2, desc, &isnull));
4998 Assert(!isnull);
4999
5000 ent = (ReorderBufferToastEnt *)
5001 hash_search(txn->toast_hash, &chunk_id, HASH_ENTER, &found);
5002
5003 if (!found)
5004 {
5005 Assert(ent->chunk_id == chunk_id);
5006 ent->num_chunks = 0;
5007 ent->last_chunk_seq = 0;
5008 ent->size = 0;
5009 ent->reconstructed = NULL;
5010 dlist_init(&ent->chunks);
5011
5012 if (chunk_seq != 0)
5013 elog(ERROR, "got sequence entry %d for toast chunk %u instead of seq 0",
5014 chunk_seq, chunk_id);
5015 }
5016 else if (found && chunk_seq != ent->last_chunk_seq + 1)
5017 elog(ERROR, "got sequence entry %d for toast chunk %u instead of seq %d",
5018 chunk_seq, chunk_id, ent->last_chunk_seq + 1);
5019
5020 chunk = DatumGetPointer(fastgetattr(newtup, 3, desc, &isnull));
5021 Assert(!isnull);
5022
5023 /* calculate size so we can allocate the right size at once later */
5024 if (!VARATT_IS_EXTENDED(chunk))
5025 chunksize = VARSIZE(chunk) - VARHDRSZ;
5026 else if (VARATT_IS_SHORT(chunk))
5027 /* could happen due to heap_form_tuple doing its thing */
5028 chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
5029 else
5030 elog(ERROR, "unexpected type of toast chunk");
5031
5032 ent->size += chunksize;
5033 ent->last_chunk_seq = chunk_seq;
5034 ent->num_chunks++;
5035 dlist_push_tail(&ent->chunks, &change->node);
5036}
char * Pointer
Definition: c.h:530
#define VARHDRSZ
Definition: c.h:698
static Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:861
static Oid DatumGetObjectId(Datum X)
Definition: postgres.h:252
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:322
#define RelationGetDescr(relation)
Definition: rel.h:540
static void ReorderBufferToastInitHash(ReorderBuffer *rb, ReorderBufferTXN *txn)
struct varlena * reconstructed
#define VARHDRSZ_SHORT
Definition: varatt.h:278
static bool VARATT_IS_SHORT(const void *PTR)
Definition: varatt.h:403
static bool VARATT_IS_EXTENDED(const void *PTR)
Definition: varatt.h:410
static Size VARSIZE(const void *PTR)
Definition: varatt.h:298
static Size VARSIZE_SHORT(const void *PTR)
Definition: varatt.h:312

References Assert(), ReorderBufferToastEnt::chunk_id, ReorderBufferToastEnt::chunks, ReorderBufferChange::data, DatumGetInt32(), DatumGetObjectId(), DatumGetPointer(), dlist_init(), dlist_push_tail(), elog, ERROR, fastgetattr(), HASH_ENTER, hash_search(), IsToastRelation(), ReorderBufferToastEnt::last_chunk_seq, ReorderBufferChange::newtuple, ReorderBufferChange::node, ReorderBufferToastEnt::num_chunks, ReorderBufferToastEnt::reconstructed, RelationGetDescr, ReorderBufferToastInitHash(), ReorderBufferToastEnt::size, ReorderBufferTXN::toast_hash, ReorderBufferChange::tp, VARATT_IS_EXTENDED(), VARATT_IS_SHORT(), VARHDRSZ, VARHDRSZ_SHORT, VARSIZE(), and VARSIZE_SHORT().

Referenced by ReorderBufferProcessTXN().

◆ ReorderBufferToastInitHash()

static void ReorderBufferToastInitHash ( ReorderBuffer rb,
ReorderBufferTXN txn 
)
static

Definition at line 4956 of file reorderbuffer.c.

4957{
4958 HASHCTL hash_ctl;
4959
4960 Assert(txn->toast_hash == NULL);
4961
4962 hash_ctl.keysize = sizeof(Oid);
4963 hash_ctl.entrysize = sizeof(ReorderBufferToastEnt);
4964 hash_ctl.hcxt = rb->context;
4965 txn->toast_hash = hash_create("ReorderBufferToastHash", 5, &hash_ctl,
4967}
struct ReorderBufferToastEnt ReorderBufferToastEnt

References Assert(), ReorderBuffer::context, HASHCTL::entrysize, HASH_BLOBS, HASH_CONTEXT, hash_create(), HASH_ELEM, HASHCTL::hcxt, HASHCTL::keysize, and ReorderBufferTXN::toast_hash.

Referenced by ReorderBufferToastAppendChunk().

◆ ReorderBufferToastReplace()

static void ReorderBufferToastReplace ( ReorderBuffer rb,
ReorderBufferTXN txn,
Relation  relation,
ReorderBufferChange change 
)
static

Definition at line 5059 of file reorderbuffer.c.

5061{
5062 TupleDesc desc;
5063 int natt;
5064 Datum *attrs;
5065 bool *isnull;
5066 bool *free;
5067 HeapTuple tmphtup;
5068 Relation toast_rel;
5069 TupleDesc toast_desc;
5070 MemoryContext oldcontext;
5071 HeapTuple newtup;
5072 Size old_size;
5073
5074 /* no toast tuples changed */
5075 if (txn->toast_hash == NULL)
5076 return;
5077
5078 /*
5079 * We're going to modify the size of the change. So, to make sure the
5080 * accounting is correct we record the current change size and then after
5081 * re-computing the change we'll subtract the recorded size and then
5082 * re-add the new change size at the end. We don't immediately subtract
5083 * the old size because if there is any error before we add the new size,
5084 * we will release the changes and that will update the accounting info
5085 * (subtracting the size from the counters). And we don't want to
5086 * underflow there.
5087 */
5088 old_size = ReorderBufferChangeSize(change);
5089
5090 oldcontext = MemoryContextSwitchTo(rb->context);
5091
5092 /* we should only have toast tuples in an INSERT or UPDATE */
5093 Assert(change->data.tp.newtuple);
5094
5095 desc = RelationGetDescr(relation);
5096
5097 toast_rel = RelationIdGetRelation(relation->rd_rel->reltoastrelid);
5098 if (!RelationIsValid(toast_rel))
5099 elog(ERROR, "could not open toast relation with OID %u (base relation \"%s\")",
5100 relation->rd_rel->reltoastrelid, RelationGetRelationName(relation));
5101
5102 toast_desc = RelationGetDescr(toast_rel);
5103
5104 /* should we allocate from stack instead? */
5105 attrs = palloc0(sizeof(Datum) * desc->natts);
5106 isnull = palloc0(sizeof(bool) * desc->natts);
5107 free = palloc0(sizeof(bool) * desc->natts);
5108
5109 newtup = change->data.tp.newtuple;
5110
5111 heap_deform_tuple(newtup, desc, attrs, isnull);
5112
5113 for (natt = 0; natt < desc->natts; natt++)
5114 {
5115 Form_pg_attribute attr = TupleDescAttr(desc, natt);
5117 struct varlena *varlena;
5118
5119 /* va_rawsize is the size of the original datum -- including header */
5120 struct varatt_external toast_pointer;
5121 struct varatt_indirect redirect_pointer;
5122 struct varlena *new_datum = NULL;
5123 struct varlena *reconstructed;
5124 dlist_iter it;
5125 Size data_done = 0;
5126
5127 /* system columns aren't toasted */
5128 if (attr->attnum < 0)
5129 continue;
5130
5131 if (attr->attisdropped)
5132 continue;
5133
5134 /* not a varlena datatype */
5135 if (attr->attlen != -1)
5136 continue;
5137
5138 /* no data */
5139 if (isnull[natt])
5140 continue;
5141
5142 /* ok, we know we have a toast datum */
5143 varlena = (struct varlena *) DatumGetPointer(attrs[natt]);
5144
5145 /* no need to do anything if the tuple isn't external */
5147 continue;
5148
5149 VARATT_EXTERNAL_GET_POINTER(toast_pointer, varlena);
5150
5151 /*
5152 * Check whether the toast tuple changed, replace if so.
5153 */
5154 ent = (ReorderBufferToastEnt *)
5156 &toast_pointer.va_valueid,
5157 HASH_FIND,
5158 NULL);
5159 if (ent == NULL)
5160 continue;
5161
5162 new_datum =
5164
5165 free[natt] = true;
5166
5167 reconstructed = palloc0(toast_pointer.va_rawsize);
5168
5169 ent->reconstructed = reconstructed;
5170
5171 /* stitch toast tuple back together from its parts */
5172 dlist_foreach(it, &ent->chunks)
5173 {
5174 bool cisnull;
5175 ReorderBufferChange *cchange;
5176 HeapTuple ctup;
5177 Pointer chunk;
5178
5179 cchange = dlist_container(ReorderBufferChange, node, it.cur);
5180 ctup = cchange->data.tp.newtuple;
5181 chunk = DatumGetPointer(fastgetattr(ctup, 3, toast_desc, &cisnull));
5182
5183 Assert(!cisnull);
5184 Assert(!VARATT_IS_EXTERNAL(chunk));
5185 Assert(!VARATT_IS_SHORT(chunk));
5186
5187 memcpy(VARDATA(reconstructed) + data_done,
5188 VARDATA(chunk),
5189 VARSIZE(chunk) - VARHDRSZ);
5190 data_done += VARSIZE(chunk) - VARHDRSZ;
5191 }
5192 Assert(data_done == VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer));
5193
5194 /* make sure its marked as compressed or not */
5195 if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
5196 SET_VARSIZE_COMPRESSED(reconstructed, data_done + VARHDRSZ);
5197 else
5198 SET_VARSIZE(reconstructed, data_done + VARHDRSZ);
5199
5200 memset(&redirect_pointer, 0, sizeof(redirect_pointer));
5201 redirect_pointer.pointer = reconstructed;
5202
5204 memcpy(VARDATA_EXTERNAL(new_datum), &redirect_pointer,
5205 sizeof(redirect_pointer));
5206
5207 attrs[natt] = PointerGetDatum(new_datum);
5208 }
5209
5210 /*
5211 * Build tuple in separate memory & copy tuple back into the tuplebuf
5212 * passed to the output plugin. We can't directly heap_fill_tuple() into
5213 * the tuplebuf because attrs[] will point back into the current content.
5214 */
5215 tmphtup = heap_form_tuple(desc, attrs, isnull);
5216 Assert(newtup->t_len <= MaxHeapTupleSize);
5217 Assert(newtup->t_data == (HeapTupleHeader) ((char *) newtup + HEAPTUPLESIZE));
5218
5219 memcpy(newtup->t_data, tmphtup->t_data, tmphtup->t_len);
5220 newtup->t_len = tmphtup->t_len;
5221
5222 /*
5223 * free resources we won't further need, more persistent stuff will be
5224 * free'd in ReorderBufferToastReset().
5225 */
5226 RelationClose(toast_rel);
5227 pfree(tmphtup);
5228 for (natt = 0; natt < desc->natts; natt++)
5229 {
5230 if (free[natt])
5231 pfree(DatumGetPointer(attrs[natt]));
5232 }
5233 pfree(attrs);
5234 pfree(free);
5235 pfree(isnull);
5236
5237 MemoryContextSwitchTo(oldcontext);
5238
5239 /* subtract the old change size */
5240 ReorderBufferChangeMemoryUpdate(rb, change, NULL, false, old_size);
5241 /* now add the change back, with the correct size */
5242 ReorderBufferChangeMemoryUpdate(rb, change, NULL, true,
5243 ReorderBufferChangeSize(change));
5244}
#define INDIRECT_POINTER_SIZE
Definition: detoast.h:34
#define VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr)
Definition: detoast.h:22
#define free(a)
Definition: header.h:65
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1117
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1346
#define MaxHeapTupleSize
Definition: htup_details.h:610
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:202
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:332
uint64_t Datum
Definition: postgres.h:70
#define RelationGetRelationName(relation)
Definition: rel.h:548
Definition: c.h:693
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:160
static void SET_VARSIZE_COMPRESSED(void *PTR, Size len)
Definition: varatt.h:446
static Size VARATT_EXTERNAL_GET_EXTSIZE(struct varatt_external toast_pointer)
Definition: varatt.h:507
static bool VARATT_IS_EXTERNAL(const void *PTR)
Definition: varatt.h:354
static char * VARDATA_EXTERNAL(const void *PTR)
Definition: varatt.h:340
static char * VARDATA(const void *PTR)
Definition: varatt.h:305
static void SET_VARTAG_EXTERNAL(void *PTR, vartag_external tag)
Definition: varatt.h:453
@ VARTAG_INDIRECT
Definition: varatt.h:86
static bool VARATT_EXTERNAL_IS_COMPRESSED(struct varatt_external toast_pointer)
Definition: varatt.h:536
static void SET_VARSIZE(void *PTR, Size len)
Definition: varatt.h:432

References Assert(), ReorderBufferToastEnt::chunks, ReorderBuffer::context, dlist_iter::cur, ReorderBufferChange::data, DatumGetPointer(), dlist_container, dlist_foreach, elog, ERROR, fastgetattr(), free, HASH_FIND, hash_search(), heap_deform_tuple(), heap_form_tuple(), HEAPTUPLESIZE, INDIRECT_POINTER_SIZE, MaxHeapTupleSize, MemoryContextSwitchTo(), TupleDescData::natts, ReorderBufferChange::newtuple, palloc0(), pfree(), varatt_indirect::pointer, PointerGetDatum(), RelationData::rd_rel, ReorderBufferToastEnt::reconstructed, RelationClose(), RelationGetDescr, RelationGetRelationName, RelationIdGetRelation(), RelationIsValid, ReorderBufferChangeMemoryUpdate(), ReorderBufferChangeSize(), SET_VARSIZE(), SET_VARSIZE_COMPRESSED(), SET_VARTAG_EXTERNAL(), HeapTupleData::t_data, HeapTupleData::t_len, ReorderBufferTXN::toast_hash, ReorderBufferChange::tp, TupleDescAttr(), varatt_external::va_rawsize, varatt_external::va_valueid, VARATT_EXTERNAL_GET_EXTSIZE(), VARATT_EXTERNAL_GET_POINTER, VARATT_EXTERNAL_IS_COMPRESSED(), VARATT_IS_EXTERNAL(), VARATT_IS_SHORT(), VARDATA(), VARDATA_EXTERNAL(), VARHDRSZ, VARSIZE(), and VARTAG_INDIRECT.

Referenced by ReorderBufferProcessTXN().

◆ ReorderBufferToastReset()

static void ReorderBufferToastReset ( ReorderBuffer rb,
ReorderBufferTXN txn 
)
static

Definition at line 5250 of file reorderbuffer.c.

5251{
5252 HASH_SEQ_STATUS hstat;
5254
5255 if (txn->toast_hash == NULL)
5256 return;
5257
5258 /* sequentially walk over the hash and free everything */
5259 hash_seq_init(&hstat, txn->toast_hash);
5260 while ((ent = (ReorderBufferToastEnt *) hash_seq_search(&hstat)) != NULL)
5261 {
5263
5264 if (ent->reconstructed != NULL)
5265 pfree(ent->reconstructed);
5266
5267 dlist_foreach_modify(it, &ent->chunks)
5268 {
5269 ReorderBufferChange *change =
5271
5272 dlist_delete(&change->node);
5273 ReorderBufferFreeChange(rb, change, true);
5274 }
5275 }
5276
5278 txn->toast_hash = NULL;
5279}
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1415
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1380

References ReorderBufferToastEnt::chunks, dlist_mutable_iter::cur, dlist_container, dlist_delete(), dlist_foreach_modify, hash_destroy(), hash_seq_init(), hash_seq_search(), ReorderBufferChange::node, pfree(), ReorderBufferToastEnt::reconstructed, ReorderBufferFreeChange(), and ReorderBufferTXN::toast_hash.

Referenced by ReorderBufferCheckAndTruncateAbortedTXN(), ReorderBufferFreeTXN(), ReorderBufferProcessTXN(), and ReorderBufferResetTXN().

◆ ReorderBufferTransferSnapToParent()

static void ReorderBufferTransferSnapToParent ( ReorderBufferTXN txn,
ReorderBufferTXN subtxn 
)
static

Definition at line 1164 of file reorderbuffer.c.

1166{
1167 Assert(subtxn->toplevel_xid == txn->xid);
1168
1169 if (subtxn->base_snapshot != NULL)
1170 {
1171 if (txn->base_snapshot == NULL ||
1172 subtxn->base_snapshot_lsn < txn->base_snapshot_lsn)
1173 {
1174 /*
1175 * If the toplevel transaction already has a base snapshot but
1176 * it's newer than the subxact's, purge it.
1177 */
1178 if (txn->base_snapshot != NULL)
1179 {
1182 }
1183
1184 /*
1185 * The snapshot is now the top transaction's; transfer it, and
1186 * adjust the list position of the top transaction in the list by
1187 * moving it to where the subtransaction is.
1188 */
1189 txn->base_snapshot = subtxn->base_snapshot;
1190 txn->base_snapshot_lsn = subtxn->base_snapshot_lsn;
1192 &txn->base_snapshot_node);
1193
1194 /*
1195 * The subtransaction doesn't have a snapshot anymore (so it
1196 * mustn't be in the list.)
1197 */
1198 subtxn->base_snapshot = NULL;
1201 }
1202 else
1203 {
1204 /* Base snap of toplevel is fine, so subxact's is not needed */
1207 subtxn->base_snapshot = NULL;
1209 }
1210 }
1211}
static void dlist_insert_before(dlist_node *before, dlist_node *node)
Definition: ilist.h:393

References Assert(), ReorderBufferTXN::base_snapshot, ReorderBufferTXN::base_snapshot_lsn, ReorderBufferTXN::base_snapshot_node, dlist_delete(), dlist_insert_before(), InvalidXLogRecPtr, SnapBuildSnapDecRefcount(), ReorderBufferTXN::toplevel_xid, and ReorderBufferTXN::xid.

Referenced by ReorderBufferAssignChild(), and ReorderBufferStreamTXN().

◆ ReorderBufferTruncateTXN()

static void ReorderBufferTruncateTXN ( ReorderBuffer rb,
ReorderBufferTXN txn,
bool  txn_prepared 
)
static

Definition at line 1655 of file reorderbuffer.c.

1656{
1657 dlist_mutable_iter iter;
1658 Size mem_freed = 0;
1659
1660 /* cleanup subtransactions & their changes */
1661 dlist_foreach_modify(iter, &txn->subtxns)
1662 {
1663 ReorderBufferTXN *subtxn;
1664
1665 subtxn = dlist_container(ReorderBufferTXN, node, iter.cur);
1666
1667 /*
1668 * Subtransactions are always associated to the toplevel TXN, even if
1669 * they originally were happening inside another subtxn, so we won't
1670 * ever recurse more than one level deep here.
1671 */
1673 Assert(subtxn->nsubtxns == 0);
1674
1676 ReorderBufferTruncateTXN(rb, subtxn, txn_prepared);
1677 }
1678
1679 /* cleanup changes in the txn */
1680 dlist_foreach_modify(iter, &txn->changes)
1681 {
1682 ReorderBufferChange *change;
1683
1684 change = dlist_container(ReorderBufferChange, node, iter.cur);
1685
1686 /* Check we're not mixing changes from different transactions. */
1687 Assert(change->txn == txn);
1688
1689 /* remove the change from its containing list */
1690 dlist_delete(&change->node);
1691
1692 /*
1693 * Instead of updating the memory counter for individual changes, we
1694 * sum up the size of memory to free so we can update the memory
1695 * counter all together below. This saves costs of maintaining the
1696 * max-heap.
1697 */
1698 mem_freed += ReorderBufferChangeSize(change);
1699
1700 ReorderBufferFreeChange(rb, change, false);
1701 }
1702
1703 /* Update the memory counter */
1704 ReorderBufferChangeMemoryUpdate(rb, NULL, txn, false, mem_freed);
1705
1706 if (txn_prepared)
1707 {
1708 /*
1709 * If this is a prepared txn, cleanup the tuplecids we stored for
1710 * decoding catalog snapshot access. They are always stored in the
1711 * toplevel transaction.
1712 */
1713 dlist_foreach_modify(iter, &txn->tuplecids)
1714 {
1715 ReorderBufferChange *change;
1716
1717 change = dlist_container(ReorderBufferChange, node, iter.cur);
1718
1719 /* Check we're not mixing changes from different transactions. */
1720 Assert(change->txn == txn);
1722
1723 /* Remove the change from its containing list. */
1724 dlist_delete(&change->node);
1725
1726 ReorderBufferFreeChange(rb, change, true);
1727 }
1728 }
1729
1730 /*
1731 * Destroy the (relfilelocator, ctid) hashtable, so that we don't leak any
1732 * memory. We could also keep the hash table and update it with new ctid
1733 * values, but this seems simpler and good enough for now.
1734 */
1735 if (txn->tuplecid_hash != NULL)
1736 {
1738 txn->tuplecid_hash = NULL;
1739 }
1740
1741 /* If this txn is serialized then clean the disk space. */
1742 if (rbtxn_is_serialized(txn))
1743 {
1745 txn->txn_flags &= ~RBTXN_IS_SERIALIZED;
1746
1747 /*
1748 * We set this flag to indicate if the transaction is ever serialized.
1749 * We need this to accurately update the stats as otherwise the same
1750 * transaction can be counted as serialized multiple times.
1751 */
1753 }
1754
1755 /* also reset the number of entries in the transaction */
1756 txn->nentries_mem = 0;
1757 txn->nentries = 0;
1758}
#define RBTXN_IS_SERIALIZED_CLEAR

References ReorderBufferChange::action, Assert(), ReorderBufferTXN::changes, dlist_mutable_iter::cur, dlist_container, dlist_delete(), dlist_foreach_modify, hash_destroy(), ReorderBufferTXN::nentries, ReorderBufferTXN::nentries_mem, ReorderBufferChange::node, ReorderBufferTXN::nsubtxns, rbtxn_is_known_subxact, rbtxn_is_serialized, RBTXN_IS_SERIALIZED_CLEAR, REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID, ReorderBufferChangeMemoryUpdate(), ReorderBufferChangeSize(), ReorderBufferFreeChange(), ReorderBufferMaybeMarkTXNStreamed(), ReorderBufferRestoreCleanup(), ReorderBufferTruncateTXN(), ReorderBufferTXN::subtxns, ReorderBufferTXN::tuplecid_hash, ReorderBufferTXN::tuplecids, ReorderBufferChange::txn, and ReorderBufferTXN::txn_flags.

Referenced by ReorderBufferCheckAndTruncateAbortedTXN(), ReorderBufferProcessTXN(), ReorderBufferResetTXN(), ReorderBufferStreamCommit(), and ReorderBufferTruncateTXN().

◆ ReorderBufferTXNByXid()

static ReorderBufferTXN * ReorderBufferTXNByXid ( ReorderBuffer rb,
TransactionId  xid,
bool  create,
bool *  is_new,
XLogRecPtr  lsn,
bool  create_as_top 
)
static

Definition at line 652 of file reorderbuffer.c.

654{
655 ReorderBufferTXN *txn;
657 bool found;
658
660
661 /*
662 * Check the one-entry lookup cache first
663 */
665 rb->by_txn_last_xid == xid)
666 {
667 txn = rb->by_txn_last_txn;
668
669 if (txn != NULL)
670 {
671 /* found it, and it's valid */
672 if (is_new)
673 *is_new = false;
674 return txn;
675 }
676
677 /*
678 * cached as non-existent, and asked not to create? Then nothing else
679 * to do.
680 */
681 if (!create)
682 return NULL;
683 /* otherwise fall through to create it */
684 }
685
686 /*
687 * If the cache wasn't hit or it yielded a "does-not-exist" and we want to
688 * create an entry.
689 */
690
691 /* search the lookup table */
694 &xid,
695 create ? HASH_ENTER : HASH_FIND,
696 &found);
697 if (found)
698 txn = ent->txn;
699 else if (create)
700 {
701 /* initialize the new entry, if creation was requested */
702 Assert(ent != NULL);
704
705 ent->txn = ReorderBufferAllocTXN(rb);
706 ent->txn->xid = xid;
707 txn = ent->txn;
708 txn->first_lsn = lsn;
710
711 if (create_as_top)
712 {
715 }
716 }
717 else
718 txn = NULL; /* not found and not asked to create */
719
720 /* update cache */
721 rb->by_txn_last_xid = xid;
722 rb->by_txn_last_txn = txn;
723
724 if (is_new)
725 *is_new = !found;
726
727 Assert(!create || txn != NULL);
728 return txn;
729}
static ReorderBufferTXN * ReorderBufferAllocTXN(ReorderBuffer *rb)
ReorderBufferTXN * txn
XLogRecPtr restart_decoding_lsn
#define TransactionIdIsValid(xid)
Definition: transam.h:41

References Assert(), AssertTXNLsnOrder(), ReorderBuffer::by_txn, ReorderBuffer::by_txn_last_txn, ReorderBuffer::by_txn_last_xid, ReorderBuffer::current_restart_decoding_lsn, dlist_push_tail(), ReorderBufferTXN::first_lsn, HASH_ENTER, HASH_FIND, hash_search(), InvalidXLogRecPtr, ReorderBufferTXN::node, ReorderBufferAllocTXN(), ReorderBufferTXN::restart_decoding_lsn, ReorderBuffer::toplevel_by_lsn, TransactionIdIsValid, ReorderBufferTXNByIdEnt::txn, and ReorderBufferTXN::xid.

Referenced by ReorderBufferAbort(), ReorderBufferAddDistributedInvalidations(), ReorderBufferAddInvalidations(), ReorderBufferAddNewTupleCids(), ReorderBufferAssignChild(), ReorderBufferCommit(), ReorderBufferCommitChild(), ReorderBufferFinishPrepared(), ReorderBufferForget(), ReorderBufferGetInvalidations(), ReorderBufferInvalidate(), ReorderBufferPrepare(), ReorderBufferProcessXid(), ReorderBufferQueueChange(), ReorderBufferQueueMessage(), ReorderBufferRememberPrepareInfo(), ReorderBufferSetBaseSnapshot(), ReorderBufferSkipPrepare(), ReorderBufferXidHasBaseSnapshot(), ReorderBufferXidHasCatalogChanges(), and ReorderBufferXidSetCatalogChanges().

◆ ReorderBufferTXNSizeCompare()

static int ReorderBufferTXNSizeCompare ( const pairingheap_node a,
const pairingheap_node b,
void *  arg 
)
static

Definition at line 3791 of file reorderbuffer.c.

3792{
3795
3796 if (ta->size < tb->size)
3797 return -1;
3798 if (ta->size > tb->size)
3799 return 1;
3800 return 0;
3801}
#define pairingheap_const_container(type, membername, ptr)
Definition: pairingheap.h:51

References a, b, pairingheap_const_container, and ReorderBufferTXN::size.

Referenced by ReorderBufferAllocate().

◆ ReorderBufferXidHasBaseSnapshot()

bool ReorderBufferXidHasBaseSnapshot ( ReorderBuffer rb,
TransactionId  xid 
)

Definition at line 3744 of file reorderbuffer.c.

3745{
3746 ReorderBufferTXN *txn;
3747
3748 txn = ReorderBufferTXNByXid(rb, xid, false,
3749 NULL, InvalidXLogRecPtr, false);
3750
3751 /* transaction isn't known yet, ergo no snapshot */
3752 if (txn == NULL)
3753 return false;
3754
3755 /* a known subtxn? operate on top-level txn instead */
3756 if (rbtxn_is_known_subxact(txn))
3757 txn = ReorderBufferTXNByXid(rb, txn->toplevel_xid, false,
3758 NULL, InvalidXLogRecPtr, false);
3759
3760 return txn->base_snapshot != NULL;
3761}

References ReorderBufferTXN::base_snapshot, InvalidXLogRecPtr, rbtxn_is_known_subxact, ReorderBufferTXNByXid(), and ReorderBufferTXN::toplevel_xid.

Referenced by SnapBuildCommitTxn(), SnapBuildDistributeSnapshotAndInval(), and SnapBuildProcessChange().

◆ ReorderBufferXidHasCatalogChanges()

bool ReorderBufferXidHasCatalogChanges ( ReorderBuffer rb,
TransactionId  xid 
)

Definition at line 3727 of file reorderbuffer.c.

3728{
3729 ReorderBufferTXN *txn;
3730
3731 txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr,
3732 false);
3733 if (txn == NULL)
3734 return false;
3735
3736 return rbtxn_has_catalog_changes(txn);
3737}

References InvalidXLogRecPtr, rbtxn_has_catalog_changes, and ReorderBufferTXNByXid().

Referenced by SnapBuildXidHasCatalogChanges().

◆ ReorderBufferXidSetCatalogChanges()

void ReorderBufferXidSetCatalogChanges ( ReorderBuffer rb,
TransactionId  xid,
XLogRecPtr  lsn 
)

Definition at line 3654 of file reorderbuffer.c.

3656{
3657 ReorderBufferTXN *txn;
3658
3659 txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
3660
3661 if (!rbtxn_has_catalog_changes(txn))
3662 {
3665 }
3666
3667 /*
3668 * Mark top-level transaction as having catalog changes too if one of its
3669 * children has so that the ReorderBufferBuildTupleCidHash can
3670 * conveniently check just top-level transaction and decide whether to
3671 * build the hash table or not.
3672 */
3673 if (rbtxn_is_subtxn(txn))
3674 {
3675 ReorderBufferTXN *toptxn = rbtxn_get_toptxn(txn);
3676
3677 if (!rbtxn_has_catalog_changes(toptxn))
3678 {
3681 }
3682 }
3683}
static void dclist_push_tail(dclist_head *head, dlist_node *node)
Definition: ilist.h:709
#define rbtxn_is_subtxn(txn)
#define RBTXN_HAS_CATALOG_CHANGES

References ReorderBufferTXN::catchange_node, ReorderBuffer::catchange_txns, dclist_push_tail(), rbtxn_get_toptxn, RBTXN_HAS_CATALOG_CHANGES, rbtxn_has_catalog_changes, rbtxn_is_subtxn, ReorderBufferTXNByXid(), and ReorderBufferTXN::txn_flags.

Referenced by SnapBuildProcessNewCid(), and xact_decode().

◆ ResolveCminCmaxDuringDecoding()

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

Definition at line 5542 of file reorderbuffer.c.

5546{
5549 ForkNumber forkno;
5550 BlockNumber blockno;
5551 bool updated_mapping = false;
5552
5553 /*
5554 * Return unresolved if tuplecid_data is not valid. That's because when
5555 * streaming in-progress transactions we may run into tuples with the CID
5556 * before actually decoding them. Think e.g. about INSERT followed by
5557 * TRUNCATE, where the TRUNCATE may not be decoded yet when applying the
5558 * INSERT. So in such cases, we assume the CID is from the future
5559 * command.
5560 */
5561 if (tuplecid_data == NULL)
5562 return false;
5563
5564 /* be careful about padding */
5565 memset(&key, 0, sizeof(key));
5566
5567 Assert(!BufferIsLocal(buffer));
5568
5569 /*
5570 * get relfilelocator from the buffer, no convenient way to access it
5571 * other than that.
5572 */
5573 BufferGetTag(buffer, &key.rlocator, &forkno, &blockno);
5574
5575 /* tuples can only be in the main fork */
5576 Assert(forkno == MAIN_FORKNUM);
5577 Assert(blockno == ItemPointerGetBlockNumber(&htup->t_self));
5578
5579 ItemPointerCopy(&htup->t_self,
5580 &key.tid);
5581
5582restart:
5583 ent = (ReorderBufferTupleCidEnt *)
5585
5586 /*
5587 * failed to find a mapping, check whether the table was rewritten and
5588 * apply mapping if so, but only do that once - there can be no new
5589 * mappings while we are in here since we have to hold a lock on the
5590 * relation.
5591 */
5592 if (ent == NULL && !updated_mapping)
5593 {
5595 /* now check but don't update for a mapping again */
5596 updated_mapping = true;
5597 goto restart;
5598 }
5599 else if (ent == NULL)
5600 return false;
5601
5602 if (cmin)
5603 *cmin = ent->cmin;
5604 if (cmax)
5605 *cmax = ent->cmax;
5606 return true;
5607}
uint32 BlockNumber
Definition: block.h:31
#define BufferIsLocal(buffer)
Definition: buf.h:37
void BufferGetTag(Buffer buffer, RelFileLocator *rlocator, ForkNumber *forknum, BlockNumber *blknum)
Definition: bufmgr.c:4219
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition: itemptr.h:103
ForkNumber
Definition: relpath.h:56
static void UpdateLogicalMappings(HTAB *tuplecid_data, Oid relid, Snapshot snapshot)
ItemPointerData t_self
Definition: htup.h:65
Oid t_tableOid
Definition: htup.h:66

References Assert(), BufferGetTag(), BufferIsLocal, ReorderBufferTupleCidEnt::cmax, ReorderBufferTupleCidEnt::cmin, HASH_FIND, hash_search(), ItemPointerCopy(), ItemPointerGetBlockNumber(), sort-test::key, MAIN_FORKNUM, HeapTupleData::t_self, HeapTupleData::t_tableOid, tuplecid_data, and UpdateLogicalMappings().

Referenced by HeapTupleSatisfiesHistoricMVCC().

◆ SetupCheckXidLive()

static void SetupCheckXidLive ( TransactionId  xid)
inlinestatic

Definition at line 2048 of file reorderbuffer.c.

2049{
2050 /*
2051 * If the input transaction id is already set as a CheckXidAlive then
2052 * nothing to do.
2053 */
2055 return;
2056
2057 /*
2058 * setup CheckXidAlive if it's not committed yet. We don't check if the
2059 * xid is aborted. That will happen during catalog access.
2060 */
2061 if (!TransactionIdDidCommit(xid))
2062 CheckXidAlive = xid;
2063 else
2065}
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43

References CheckXidAlive, InvalidTransactionId, TransactionIdDidCommit(), and TransactionIdEquals.

Referenced by ReorderBufferProcessTXN().

◆ StartupReorderBuffer()

void StartupReorderBuffer ( void  )

Definition at line 4922 of file reorderbuffer.c.

4923{
4924 DIR *logical_dir;
4925 struct dirent *logical_de;
4926
4927 logical_dir = AllocateDir(PG_REPLSLOT_DIR);
4928 while ((logical_de = ReadDir(logical_dir, PG_REPLSLOT_DIR)) != NULL)
4929 {
4930 if (strcmp(logical_de->d_name, ".") == 0 ||
4931 strcmp(logical_de->d_name, "..") == 0)
4932 continue;
4933
4934 /* if it cannot be a slot, skip the directory */
4935 if (!ReplicationSlotValidateName(logical_de->d_name, true, DEBUG2))
4936 continue;
4937
4938 /*
4939 * ok, has to be a surviving logical slot, iterate and delete
4940 * everything starting with xid-*
4941 */
4943 }
4944 FreeDir(logical_dir);
4945}
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition: fd.c:2970
bool ReplicationSlotValidateName(const char *name, bool allow_reserved_name, int elevel)
Definition: slot.c:272

References AllocateDir(), dirent::d_name, DEBUG2, FreeDir(), PG_REPLSLOT_DIR, ReadDir(), ReorderBufferCleanupSerializedTXNs(), and ReplicationSlotValidateName().

Referenced by StartupXLOG().

◆ TransactionIdInArray()

static bool TransactionIdInArray ( TransactionId  xid,
TransactionId xip,
Size  num 
)
static

Definition at line 5441 of file reorderbuffer.c.

5442{
5443 return bsearch(&xid, xip, num,
5444 sizeof(TransactionId), xidComparator) != NULL;
5445}

References xidComparator().

Referenced by UpdateLogicalMappings().

◆ UpdateLogicalMappings()

static void UpdateLogicalMappings ( HTAB tuplecid_data,
Oid  relid,
Snapshot  snapshot 
)
static

Definition at line 5464 of file reorderbuffer.c.

5465{
5466 DIR *mapping_dir;
5467 struct dirent *mapping_de;
5468 List *files = NIL;
5469 ListCell *file;
5470 Oid dboid = IsSharedRelation(relid) ? InvalidOid : MyDatabaseId;
5471
5472 mapping_dir = AllocateDir(PG_LOGICAL_MAPPINGS_DIR);
5473 while ((mapping_de = ReadDir(mapping_dir, PG_LOGICAL_MAPPINGS_DIR)) != NULL)
5474 {
5475 Oid f_dboid;
5476 Oid f_relid;
5477 TransactionId f_mapped_xid;
5478 TransactionId f_create_xid;
5479 XLogRecPtr f_lsn;
5480 uint32 f_hi,
5481 f_lo;
5483
5484 if (strcmp(mapping_de->d_name, ".") == 0 ||
5485 strcmp(mapping_de->d_name, "..") == 0)
5486 continue;
5487
5488 /* Ignore files that aren't ours */
5489 if (strncmp(mapping_de->d_name, "map-", 4) != 0)
5490 continue;
5491
5492 if (sscanf(mapping_de->d_name, LOGICAL_REWRITE_FORMAT,
5493 &f_dboid, &f_relid, &f_hi, &f_lo,
5494 &f_mapped_xid, &f_create_xid) != 6)
5495 elog(ERROR, "could not parse filename \"%s\"", mapping_de->d_name);
5496
5497 f_lsn = ((uint64) f_hi) << 32 | f_lo;
5498
5499 /* mapping for another database */
5500 if (f_dboid != dboid)
5501 continue;
5502
5503 /* mapping for another relation */
5504 if (f_relid != relid)
5505 continue;
5506
5507 /* did the creating transaction abort? */
5508 if (!TransactionIdDidCommit(f_create_xid))
5509 continue;
5510
5511 /* not for our transaction */
5512 if (!TransactionIdInArray(f_mapped_xid, snapshot->subxip, snapshot->subxcnt))
5513 continue;
5514
5515 /* ok, relevant, queue for apply */
5516 f = palloc(sizeof(RewriteMappingFile));
5517 f->lsn = f_lsn;
5518 strcpy(f->fname, mapping_de->d_name);
5519 files = lappend(files, f);
5520 }
5521 FreeDir(mapping_dir);
5522
5523 /* sort files so we apply them in LSN order */
5525
5526 foreach(file, files)
5527 {
5529
5530 elog(DEBUG1, "applying mapping: \"%s\" in %u", f->fname,
5531 snapshot->subxip[0]);
5533 pfree(f);
5534 }
5535}
uint64_t uint64
Definition: c.h:540
bool IsSharedRelation(Oid relationId)
Definition: catalog.c:304
#define DEBUG1
Definition: elog.h:30
Oid MyDatabaseId
Definition: globals.c:94
List * lappend(List *list, void *datum)
Definition: list.c:339
void list_sort(List *list, list_sort_comparator cmp)
Definition: list.c:1674
#define NIL
Definition: pg_list.h:68
static int file_sort_by_lsn(const ListCell *a_p, const ListCell *b_p)
static void ApplyLogicalMappingFile(HTAB *tuplecid_data, Oid relid, const char *fname)
static bool TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
#define LOGICAL_REWRITE_FORMAT
Definition: rewriteheap.h:54
Definition: pg_list.h:54
char fname[MAXPGPATH]

References AllocateDir(), ApplyLogicalMappingFile(), dirent::d_name, DEBUG1, elog, ERROR, file_sort_by_lsn(), RewriteMappingFile::fname, FreeDir(), InvalidOid, IsSharedRelation(), lappend(), lfirst, list_sort(), LOGICAL_REWRITE_FORMAT, RewriteMappingFile::lsn, MyDatabaseId, NIL, palloc(), pfree(), PG_LOGICAL_MAPPINGS_DIR, ReadDir(), SnapshotData::subxcnt, SnapshotData::subxip, TransactionIdDidCommit(), TransactionIdInArray(), and tuplecid_data.

Referenced by ResolveCminCmaxDuringDecoding().

Variable Documentation

◆ debug_logical_replication_streaming

int debug_logical_replication_streaming = DEBUG_LOGICAL_REP_STREAMING_BUFFERED

◆ logical_decoding_work_mem

int logical_decoding_work_mem

Definition at line 225 of file reorderbuffer.c.

Referenced by ReorderBufferCheckMemoryLimit().

◆ max_changes_in_memory

const Size max_changes_in_memory = 4096
static

Definition at line 226 of file reorderbuffer.c.

Referenced by ReorderBufferRestoreChanges().