mirror of
https://github.com/postgres/postgres.git
synced 2025-10-19 15:49:24 +03:00
Commit 1585ff7387
changed GetTransactionSnapshot() to throw an error
if it's called during logical decoding, instead of returning the
historic snapshot. I made that change for extra protection, because a
historic snapshot can only be used to access catalog tables while
GetTransactionSnapshot() is usually called when you're executing
arbitrary queries. You might get very subtle visibility problems if
you tried to use the historic snapshot for arbitrary queries.
There's no built-in code in PostgreSQL that calls
GetTransactionSnapshot() during logical decoding, but it turns out
that the pglogical extension does just that, to evaluate row filter
expressions. You would get weird results if the row filter runs
arbitrary queries, but it is sane as long as you don't access any
non-catalog tables. Even though there are no checks to enforce that in
pglogical, a typical row filter expression does not access any tables
and works fine. Accessing tables marked with the user_catalog_table =
true option is also OK.
To fix pglogical with row filters, and any other extensions that might
do similar things, revert GetTransactionSnapshot() to return a
historic snapshot during logical decoding.
To try to still catch the unsafe usage of historic snapshots, add
checks in heap_beginscan() and index_beginscan() to complain if you
try to use a historic snapshot to scan a non-catalog table. We're very
close to the version 18 release however, so add those new checks only
in master.
Backpatch-through: 18
Reported-by: Noah Misch <noah@leadboat.com>
Reviewed-by: Noah Misch <noah@leadboat.com>
Discussion: https://www.postgresql.org/message-id/20250809222338.cc.nmisch@google.com
126 lines
4.7 KiB
C
126 lines
4.7 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* snapmgr.h
|
|
* POSTGRES snapshot manager
|
|
*
|
|
* Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* src/include/utils/snapmgr.h
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#ifndef SNAPMGR_H
|
|
#define SNAPMGR_H
|
|
|
|
#include "access/transam.h"
|
|
#include "utils/relcache.h"
|
|
#include "utils/resowner.h"
|
|
#include "utils/snapshot.h"
|
|
|
|
|
|
extern PGDLLIMPORT bool FirstSnapshotSet;
|
|
|
|
extern PGDLLIMPORT TransactionId TransactionXmin;
|
|
extern PGDLLIMPORT TransactionId RecentXmin;
|
|
|
|
/* Variables representing various special snapshot semantics */
|
|
extern PGDLLIMPORT SnapshotData SnapshotSelfData;
|
|
extern PGDLLIMPORT SnapshotData SnapshotAnyData;
|
|
extern PGDLLIMPORT SnapshotData SnapshotToastData;
|
|
|
|
#define SnapshotSelf (&SnapshotSelfData)
|
|
#define SnapshotAny (&SnapshotAnyData)
|
|
|
|
/* Use get_toast_snapshot() for the TOAST snapshot */
|
|
|
|
/*
|
|
* We don't provide a static SnapshotDirty variable because it would be
|
|
* non-reentrant. Instead, users of that snapshot type should declare a
|
|
* local variable of type SnapshotData, and initialize it with this macro.
|
|
*/
|
|
#define InitDirtySnapshot(snapshotdata) \
|
|
((snapshotdata).snapshot_type = SNAPSHOT_DIRTY)
|
|
|
|
/*
|
|
* Similarly, some initialization is required for a NonVacuumable snapshot.
|
|
* The caller must supply the visibility cutoff state to use (c.f.
|
|
* GlobalVisTestFor()).
|
|
*/
|
|
#define InitNonVacuumableSnapshot(snapshotdata, vistestp) \
|
|
((snapshotdata).snapshot_type = SNAPSHOT_NON_VACUUMABLE, \
|
|
(snapshotdata).vistest = (vistestp))
|
|
|
|
/* This macro encodes the knowledge of which snapshots are MVCC-safe */
|
|
#define IsMVCCSnapshot(snapshot) \
|
|
((snapshot)->snapshot_type == SNAPSHOT_MVCC || \
|
|
(snapshot)->snapshot_type == SNAPSHOT_HISTORIC_MVCC)
|
|
|
|
#define IsHistoricMVCCSnapshot(snapshot) \
|
|
((snapshot)->snapshot_type == SNAPSHOT_HISTORIC_MVCC)
|
|
|
|
extern Snapshot GetTransactionSnapshot(void);
|
|
extern Snapshot GetLatestSnapshot(void);
|
|
extern void SnapshotSetCommandId(CommandId curcid);
|
|
|
|
extern Snapshot GetCatalogSnapshot(Oid relid);
|
|
extern Snapshot GetNonHistoricCatalogSnapshot(Oid relid);
|
|
extern void InvalidateCatalogSnapshot(void);
|
|
extern void InvalidateCatalogSnapshotConditionally(void);
|
|
|
|
extern void PushActiveSnapshot(Snapshot snapshot);
|
|
extern void PushActiveSnapshotWithLevel(Snapshot snapshot, int snap_level);
|
|
extern void PushCopiedSnapshot(Snapshot snapshot);
|
|
extern void UpdateActiveSnapshotCommandId(void);
|
|
extern void PopActiveSnapshot(void);
|
|
extern Snapshot GetActiveSnapshot(void);
|
|
extern bool ActiveSnapshotSet(void);
|
|
|
|
extern Snapshot RegisterSnapshot(Snapshot snapshot);
|
|
extern void UnregisterSnapshot(Snapshot snapshot);
|
|
extern Snapshot RegisterSnapshotOnOwner(Snapshot snapshot, ResourceOwner owner);
|
|
extern void UnregisterSnapshotFromOwner(Snapshot snapshot, ResourceOwner owner);
|
|
|
|
extern void AtSubCommit_Snapshot(int level);
|
|
extern void AtSubAbort_Snapshot(int level);
|
|
extern void AtEOXact_Snapshot(bool isCommit, bool resetXmin);
|
|
|
|
extern void ImportSnapshot(const char *idstr);
|
|
extern bool XactHasExportedSnapshots(void);
|
|
extern void DeleteAllExportedSnapshotFiles(void);
|
|
extern void WaitForOlderSnapshots(TransactionId limitXmin, bool progress);
|
|
extern bool ThereAreNoPriorRegisteredSnapshots(void);
|
|
extern bool HaveRegisteredOrActiveSnapshot(void);
|
|
|
|
extern char *ExportSnapshot(Snapshot snapshot);
|
|
|
|
/*
|
|
* These live in procarray.c because they're intimately linked to the
|
|
* procarray contents, but thematically they better fit into snapmgr.h.
|
|
*/
|
|
typedef struct GlobalVisState GlobalVisState;
|
|
extern GlobalVisState *GlobalVisTestFor(Relation rel);
|
|
extern bool GlobalVisTestIsRemovableXid(GlobalVisState *state, TransactionId xid);
|
|
extern bool GlobalVisTestIsRemovableFullXid(GlobalVisState *state, FullTransactionId fxid);
|
|
extern bool GlobalVisCheckRemovableXid(Relation rel, TransactionId xid);
|
|
extern bool GlobalVisCheckRemovableFullXid(Relation rel, FullTransactionId fxid);
|
|
|
|
/*
|
|
* Utility functions for implementing visibility routines in table AMs.
|
|
*/
|
|
extern bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
|
|
|
|
/* Support for catalog timetravel for logical decoding */
|
|
struct HTAB;
|
|
extern struct HTAB *HistoricSnapshotGetTupleCids(void);
|
|
extern void SetupHistoricSnapshot(Snapshot historic_snapshot, struct HTAB *tuplecids);
|
|
extern void TeardownHistoricSnapshot(bool is_error);
|
|
extern bool HistoricSnapshotActive(void);
|
|
|
|
extern Size EstimateSnapshotSpace(Snapshot snapshot);
|
|
extern void SerializeSnapshot(Snapshot snapshot, char *start_address);
|
|
extern Snapshot RestoreSnapshot(char *start_address);
|
|
extern void RestoreTransactionSnapshot(Snapshot snapshot, void *source_pgproc);
|
|
|
|
#endif /* SNAPMGR_H */
|