On Tue, Oct 08, 2024 at 01:50:52PM -0500, Nathan Bossart wrote:
> 0002 could probably use some more commentary, but otherwise I think it is
> in decent shape.  You (Michael) seem to be implying that I should
> back-patch the actual fixes and only apply the new assertions to v18.  Am I
> understanding you correctly?

Here is a reorganized patch set.  0001 would be back-patched, but the
others would only be applied to v18.

-- 
nathan
>From 6792eeb656e8fc707d85dc07595e2183853d2ce5 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nat...@postgresql.org>
Date: Mon, 14 Oct 2024 14:43:26 -0500
Subject: [PATCH v3 1/3] Ensure we have a snapshot when updating various system
 catalogs.

Reported-by: Alexander Lakhin
Reviewed-by: Michael Paquier
Discussion: https://postgr.es/m/18127-fe54b6a667f29658%40postgresql.org
Discussion: https://postgr.es/m/18309-c0bf914950c46692%40postgresql.org
Discussion: https://postgr.es/m/ZvMSUPOqUU-VNADN%40nathan
Backpatch-through: FIXME
---
 src/backend/commands/tablecmds.c            | 13 +++++++++++
 src/backend/replication/logical/tablesync.c | 21 ++++++++++++++++++
 src/backend/replication/logical/worker.c    | 24 +++++++++++++++++++++
 3 files changed, 58 insertions(+)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1ccc80087c..c6f82e93b9 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19283,9 +19283,22 @@ ATExecDetachPartition(List **wqueue, AlteredTableInfo 
*tab, Relation rel,
                tab->rel = rel;
        }
 
+       /*
+        * Detaching the partition might involve TOAST table access, so ensure 
we
+        * have a valid snapshot.  We only expect to get here without a snapshot
+        * in the concurrent path.
+        */
+       if (concurrent)
+               PushActiveSnapshot(GetTransactionSnapshot());
+       else
+               Assert(HaveRegisteredOrActiveSnapshot());
+
        /* Do the final part of detaching */
        DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
 
+       if (concurrent)
+               PopActiveSnapshot();
+
        ObjectAddressSet(address, RelationRelationId, 
RelationGetRelid(partRel));
 
        /* keep our lock until commit */
diff --git a/src/backend/replication/logical/tablesync.c 
b/src/backend/replication/logical/tablesync.c
index e03e761392..d3fbb18438 100644
--- a/src/backend/replication/logical/tablesync.c
+++ b/src/backend/replication/logical/tablesync.c
@@ -377,6 +377,12 @@ process_syncing_tables_for_sync(XLogRecPtr current_lsn)
                replorigin_session_origin_lsn = InvalidXLogRecPtr;
                replorigin_session_origin_timestamp = 0;
 
+               /*
+                * Updating pg_replication_origin might involve TOAST table 
access, so
+                * ensure we have a valid snapshot.
+                */
+               PushActiveSnapshot(GetTransactionSnapshot());
+
                /*
                 * Drop the tablesync's origin tracking if exists.
                 *
@@ -387,6 +393,8 @@ process_syncing_tables_for_sync(XLogRecPtr current_lsn)
                 */
                replorigin_drop_by_name(originname, true, false);
 
+               PopActiveSnapshot();
+
                finish_sync_worker();
        }
        else
@@ -483,6 +491,12 @@ process_syncing_tables_for_apply(XLogRecPtr current_lsn)
                                        started_tx = true;
                                }
 
+                               /*
+                                * Updating pg_replication_origin might involve 
TOAST table
+                                * access, so ensure we have a valid snapshot.
+                                */
+                               PushActiveSnapshot(GetTransactionSnapshot());
+
                                /*
                                 * Remove the tablesync origin tracking if 
exists.
                                 *
@@ -500,6 +514,8 @@ process_syncing_tables_for_apply(XLogRecPtr current_lsn)
                                                                                
                   sizeof(originname));
                                replorigin_drop_by_name(originname, true, 
false);
 
+                               PopActiveSnapshot();
+
                                /*
                                 * Update the state to READY only after the 
origin cleanup.
                                 */
@@ -1455,8 +1471,13 @@ LogicalRepSyncTableStart(XLogRecPtr *origin_startpos)
                 * Then advance to the LSN got from walrcv_create_slot. This is 
WAL
                 * logged for the purpose of recovery. Locks are to prevent the
                 * replication origin from vanishing while advancing.
+                *
+                * Updating pg_replication_origin might involve TOAST table 
access, so
+                * ensure we have a valid snapshot.
                 */
+               PushActiveSnapshot(GetTransactionSnapshot());
                originid = replorigin_create(originname);
+               PopActiveSnapshot();
 
                LockRelationOid(ReplicationOriginRelationId, RowExclusiveLock);
                replorigin_advance(originid, *origin_startpos, 
InvalidXLogRecPtr,
diff --git a/src/backend/replication/logical/worker.c 
b/src/backend/replication/logical/worker.c
index 925dff9cc4..804e5dbb75 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -4604,8 +4604,16 @@ run_apply_worker()
                walrcv_startstreaming(LogRepWorkerWalRcvConn, &options);
 
                StartTransactionCommand();
+
+               /*
+                * Updating pg_subscription might involve TOAST table access, so
+                * ensure we have a valid snapshot.
+                */
+               PushActiveSnapshot(GetTransactionSnapshot());
+
                UpdateTwoPhaseState(MySubscription->oid, 
LOGICALREP_TWOPHASE_STATE_ENABLED);
                MySubscription->twophasestate = 
LOGICALREP_TWOPHASE_STATE_ENABLED;
+               PopActiveSnapshot();
                CommitTransactionCommand();
        }
        else
@@ -4821,7 +4829,15 @@ DisableSubscriptionAndExit(void)
 
        /* Disable the subscription */
        StartTransactionCommand();
+
+       /*
+        * Updating pg_subscription might involve TOAST table access, so ensure 
we
+        * have a valid snapshot.
+        */
+       PushActiveSnapshot(GetTransactionSnapshot());
+
        DisableSubscription(MySubscription->oid);
+       PopActiveSnapshot();
        CommitTransactionCommand();
 
        /* Ensure we remove no-longer-useful entry for worker's start time */
@@ -4925,6 +4941,12 @@ clear_subscription_skip_lsn(XLogRecPtr finish_lsn)
                started_tx = true;
        }
 
+       /*
+        * Updating pg_subscription might involve TOAST table access, so ensure 
we
+        * have a valid snapshot.
+        */
+       PushActiveSnapshot(GetTransactionSnapshot());
+
        /*
         * Protect subskiplsn of pg_subscription from being concurrently updated
         * while clearing it.
@@ -4983,6 +5005,8 @@ clear_subscription_skip_lsn(XLogRecPtr finish_lsn)
        heap_freetuple(tup);
        table_close(rel, NoLock);
 
+       PopActiveSnapshot();
+
        if (started_tx)
                CommitTransactionCommand();
 }
-- 
2.39.5 (Apple Git-154)

>From 4294f5f3c81ec4365ec5bd8bfa80069496732dc5 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nat...@postgresql.org>
Date: Mon, 14 Oct 2024 14:50:59 -0500
Subject: [PATCH v3 2/3] Introduce RelationGetToastRelid macro.

Discussion: https://postgr.es/m/ZvMSUPOqUU-VNADN%40nathan
---
 contrib/amcheck/verify_heapam.c               |  6 +++---
 src/backend/access/common/toast_internals.c   |  2 +-
 src/backend/access/heap/heaptoast.c           |  6 +++---
 src/backend/catalog/heap.c                    |  2 +-
 src/backend/catalog/index.c                   |  2 +-
 src/backend/catalog/toasting.c                |  2 +-
 src/backend/commands/cluster.c                | 19 ++++++++++---------
 src/backend/commands/indexcmds.c              |  4 ++--
 src/backend/commands/tablecmds.c              |  8 ++++----
 src/backend/commands/vacuum.c                 |  2 +-
 .../replication/logical/reorderbuffer.c       |  4 ++--
 src/backend/utils/adt/dbsize.c                |  4 ++--
 src/include/utils/rel.h                       |  6 ++++++
 13 files changed, 37 insertions(+), 30 deletions(-)

diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index f2526ed63a..78316daacd 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -367,12 +367,12 @@ verify_heapam(PG_FUNCTION_ARGS)
        }
 
        /* Optionally open the toast relation, if any. */
-       if (ctx.rel->rd_rel->reltoastrelid && check_toast)
+       if (RelationGetToastRelid(ctx.rel) && check_toast)
        {
                int                     offset;
 
                /* Main relation has associated toast relation */
-               ctx.toast_rel = table_open(ctx.rel->rd_rel->reltoastrelid,
+               ctx.toast_rel = table_open(RelationGetToastRelid(ctx.rel),
                                                                   
AccessShareLock);
                offset = toast_open_indexes(ctx.toast_rel,
                                                                        
AccessShareLock,
@@ -1721,7 +1721,7 @@ check_tuple_attribute(HeapCheckContext *ctx)
        }
 
        /* The relation better have a toast table */
-       if (!ctx->rel->rd_rel->reltoastrelid)
+       if (!OidIsValid(RelationGetToastRelid(ctx->rel)))
        {
                report_corruption(ctx,
                                                  psprintf("toast value %u is 
external but relation has no toast relation",
diff --git a/src/backend/access/common/toast_internals.c 
b/src/backend/access/common/toast_internals.c
index 90d0654e62..39d93d12f2 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -151,7 +151,7 @@ toast_save_datum(Relation rel, Datum value,
         * uniqueness of the OID we assign to the toasted item, even though it 
has
         * additional columns besides OID.
         */
-       toastrel = table_open(rel->rd_rel->reltoastrelid, RowExclusiveLock);
+       toastrel = table_open(RelationGetToastRelid(rel), RowExclusiveLock);
        toasttupDesc = toastrel->rd_att;
 
        /* Open all the toast indexes and look for the valid one */
diff --git a/src/backend/access/heap/heaptoast.c 
b/src/backend/access/heap/heaptoast.c
index a420e16530..cf6f3dff53 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -213,7 +213,7 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, 
HeapTuple oldtup,
                 * XXX maybe the threshold should be less than maxDataLen?
                 */
                if (toast_attr[biggest_attno].tai_size > maxDataLen &&
-                       rel->rd_rel->reltoastrelid != InvalidOid)
+                       OidIsValid(RelationGetToastRelid(rel)))
                        toast_tuple_externalize(&ttc, biggest_attno, options);
        }
 
@@ -224,7 +224,7 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, 
HeapTuple oldtup,
         */
        while (heap_compute_data_size(tupleDesc,
                                                                  toast_values, 
toast_isnull) > maxDataLen &&
-                  rel->rd_rel->reltoastrelid != InvalidOid)
+                  OidIsValid(RelationGetToastRelid(rel)))
        {
                int                     biggest_attno;
 
@@ -259,7 +259,7 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, 
HeapTuple oldtup,
 
        while (heap_compute_data_size(tupleDesc,
                                                                  toast_values, 
toast_isnull) > maxDataLen &&
-                  rel->rd_rel->reltoastrelid != InvalidOid)
+                  OidIsValid(RelationGetToastRelid(rel)))
        {
                int                     biggest_attno;
 
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 0078a12f26..1b912488d6 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -3073,7 +3073,7 @@ heap_truncate_one_rel(Relation rel)
        RelationTruncateIndexes(rel);
 
        /* If there is a toast table, truncate that too */
-       toastrelid = rel->rd_rel->reltoastrelid;
+       toastrelid = RelationGetToastRelid(rel);
        if (OidIsValid(toastrelid))
        {
                Relation        toastrel = table_open(toastrelid, 
AccessExclusiveLock);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 12822d0b14..3eab421a16 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -3930,7 +3930,7 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int 
flags,
                         get_namespace_name(RelationGetNamespace(rel)),
                         RelationGetRelationName(rel));
 
-       toast_relid = rel->rd_rel->reltoastrelid;
+       toast_relid = RelationGetToastRelid(rel);
 
        /*
         * Get the list of index OIDs for this relation.  (We trust the relcache
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index ad3082c62a..cf47e0960e 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -149,7 +149,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid 
toastIndexOid,
        /*
         * Is it already toasted?
         */
-       if (rel->rd_rel->reltoastrelid != InvalidOid)
+       if (OidIsValid(RelationGetToastRelid(rel)))
                return false;
 
        /*
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 78f96789b0..20da5201b6 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -780,7 +780,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid 
NewAccessMethod,
         * Note that NewHeapCreateToastTable ends with CommandCounterIncrement, 
so
         * that the TOAST table will be visible for insertion.
         */
-       toastid = OldHeap->rd_rel->reltoastrelid;
+       toastid = RelationGetToastRelid(OldHeap);
        if (OidIsValid(toastid))
        {
                /* keep the existing toast table's reloptions, if any */
@@ -870,8 +870,8 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid 
OIDOldIndex, bool verbose,
         * We don't need to open the toast relation here, just lock it.  The 
lock
         * will be held till end of transaction.
         */
-       if (OldHeap->rd_rel->reltoastrelid)
-               LockRelationOid(OldHeap->rd_rel->reltoastrelid, 
AccessExclusiveLock);
+       if (RelationGetToastRelid(OldHeap))
+               LockRelationOid(RelationGetToastRelid(OldHeap), 
AccessExclusiveLock);
 
        /*
         * If both tables have TOAST tables, perform toast swap by content.  It 
is
@@ -880,7 +880,8 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid 
OIDOldIndex, bool verbose,
         * swap by links.  This is okay because swap by content is only 
essential
         * for system catalogs, and we don't support schema changes for them.
         */
-       if (OldHeap->rd_rel->reltoastrelid && NewHeap->rd_rel->reltoastrelid)
+       if (OidIsValid(RelationGetToastRelid(OldHeap)) &&
+               OidIsValid(RelationGetToastRelid(NewHeap)))
        {
                *pSwapToastByContent = true;
 
@@ -902,7 +903,7 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid 
OIDOldIndex, bool verbose,
                 * work for values copied over from the old toast table, but 
not for
                 * any values that we toast which were previously not toasted.)
                 */
-               NewHeap->rd_toastoid = OldHeap->rd_rel->reltoastrelid;
+               NewHeap->rd_toastoid = RelationGetToastRelid(OldHeap);
        }
        else
                *pSwapToastByContent = false;
@@ -1581,19 +1582,19 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
                Relation        newrel;
 
                newrel = table_open(OIDOldHeap, NoLock);
-               if (OidIsValid(newrel->rd_rel->reltoastrelid))
+               if (OidIsValid(RelationGetToastRelid(newrel)))
                {
                        Oid                     toastidx;
                        char            NewToastName[NAMEDATALEN];
 
                        /* Get the associated valid index to be renamed */
-                       toastidx = 
toast_get_valid_index(newrel->rd_rel->reltoastrelid,
+                       toastidx = 
toast_get_valid_index(RelationGetToastRelid(newrel),
                                                                                
         NoLock);
 
                        /* rename the toast table ... */
                        snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u",
                                         OIDOldHeap);
-                       RenameRelationInternal(newrel->rd_rel->reltoastrelid,
+                       RenameRelationInternal(RelationGetToastRelid(newrel),
                                                                   
NewToastName, true, false);
 
                        /* ... and its valid index too. */
@@ -1609,7 +1610,7 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
                         * that is updated as part of RenameRelationInternal.
                         */
                        CommandCounterIncrement();
-                       ResetRelRewrite(newrel->rd_rel->reltoastrelid);
+                       ResetRelRewrite(RelationGetToastRelid(newrel));
                }
                relation_close(newrel, NoLock);
        }
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index e33ad81529..b7ddced7f0 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -3680,9 +3680,9 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid 
relationOid, const Rein
                                }
 
                                /* Also add the toast indexes */
-                               if 
(OidIsValid(heapRelation->rd_rel->reltoastrelid))
+                               if 
(OidIsValid(RelationGetToastRelid(heapRelation)))
                                {
-                                       Oid                     toastOid = 
heapRelation->rd_rel->reltoastrelid;
+                                       Oid                     toastOid = 
RelationGetToastRelid(heapRelation);
                                        Relation        toastRelation = 
table_open(toastOid,
                                                                                
                                   ShareUpdateExclusiveLock);
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c6f82e93b9..cf076dec5d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -2140,7 +2140,7 @@ ExecuteTruncateGuts(List *explicit_rels,
                        /*
                         * The same for the toast table, if any.
                         */
-                       toast_relid = rel->rd_rel->reltoastrelid;
+                       toast_relid = RelationGetToastRelid(rel);
                        if (OidIsValid(toast_relid))
                        {
                                Relation        toastrel = 
relation_open(toast_relid,
@@ -15161,10 +15161,10 @@ ATExecSetRelOptions(Relation rel, List *defList, 
AlterTableType operation,
        ReleaseSysCache(tuple);
 
        /* repeat the whole exercise for the toast table, if there's one */
-       if (OidIsValid(rel->rd_rel->reltoastrelid))
+       if (OidIsValid(RelationGetToastRelid(rel)))
        {
                Relation        toastrel;
-               Oid                     toastid = rel->rd_rel->reltoastrelid;
+               Oid                     toastid = RelationGetToastRelid(rel);
 
                toastrel = table_open(toastid, lockmode);
 
@@ -15253,7 +15253,7 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, 
LOCKMODE lockmode)
                return;
        }
 
-       reltoastrelid = rel->rd_rel->reltoastrelid;
+       reltoastrelid = RelationGetToastRelid(rel);
        /* Fetch the list of indexes on toast relation if necessary */
        if (OidIsValid(reltoastrelid))
        {
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index ac8f5d9c25..08bd489aed 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -2187,7 +2187,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams 
*params,
        if ((params->options & VACOPT_PROCESS_TOAST) != 0 &&
                ((params->options & VACOPT_FULL) == 0 ||
                 (params->options & VACOPT_PROCESS_MAIN) == 0))
-               toast_relid = rel->rd_rel->reltoastrelid;
+               toast_relid = RelationGetToastRelid(rel);
        else
                toast_relid = InvalidOid;
 
diff --git a/src/backend/replication/logical/reorderbuffer.c 
b/src/backend/replication/logical/reorderbuffer.c
index 22bcf171ff..c9d9590108 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -4822,10 +4822,10 @@ ReorderBufferToastReplace(ReorderBuffer *rb, 
ReorderBufferTXN *txn,
 
        desc = RelationGetDescr(relation);
 
-       toast_rel = RelationIdGetRelation(relation->rd_rel->reltoastrelid);
+       toast_rel = RelationIdGetRelation(RelationGetToastRelid(relation));
        if (!RelationIsValid(toast_rel))
                elog(ERROR, "could not open toast relation with OID %u (base 
relation \"%s\")",
-                        relation->rd_rel->reltoastrelid, 
RelationGetRelationName(relation));
+                        RelationGetToastRelid(relation), 
RelationGetRelationName(relation));
 
        toast_desc = RelationGetDescr(toast_rel);
 
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index e63e99c141..a3597be9bc 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -436,8 +436,8 @@ calculate_table_size(Relation rel)
        /*
         * Size of toast relation
         */
-       if (OidIsValid(rel->rd_rel->reltoastrelid))
-               size += calculate_toast_table_size(rel->rd_rel->reltoastrelid);
+       if (OidIsValid(RelationGetToastRelid(rel)))
+               size += calculate_toast_table_size(RelationGetToastRelid(rel));
 
        return size;
 }
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 8700204953..6631b37d97 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -504,6 +504,12 @@ typedef struct ViewOptions
  */
 #define RelationGetRelid(relation) ((relation)->rd_id)
 
+/*
+ * RelationGetToastRelid
+ *             Returns the OID of the relation's TOAST table (or InvalidOid if 
none)
+ */
+#define RelationGetToastRelid(relation) ((relation)->rd_rel->reltoastrelid)
+
 /*
  * RelationGetNumberOfAttributes
  *             Returns the total number of attributes in a relation.
-- 
2.39.5 (Apple Git-154)

>From 1484561a5a9cfe070293d3cbb8528bac1634f185 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nat...@postgresql.org>
Date: Mon, 14 Oct 2024 14:53:36 -0500
Subject: [PATCH v3 3/3] Assert that we have a valid snapshot if we might need
 TOAST access.

Suggested-by: Michael Paquier
Reviewed-by: Michael Paquier
Discussion: https://postgr.es/m/ZvMSUPOqUU-VNADN%40nathan
---
 src/backend/access/heap/heapam.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index da5e656a08..d51525a85c 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -210,6 +210,23 @@ static const int MultiXactStatusLock[MaxMultiXactStatus + 
1] =
 #define TUPLOCK_from_mxstatus(status) \
                        (MultiXactStatusLock[(status)])
 
+/*
+ * Check that we have a valid snapshot if we might need TOAST access.
+ */
+static inline void
+AssertHasSnapshotForToast(Relation rel)
+{
+       /* bootstrap mode in particular breaks this rule */
+       if (!IsNormalProcessingMode())
+               return;
+
+       /* if the relation doesn't have a TOAST table, we are good */
+       if (!OidIsValid(RelationGetToastRelid(rel)))
+               return;
+
+       Assert(HaveRegisteredOrActiveSnapshot());
+}
+
 /* ----------------------------------------------------------------
  *                                              heap support routines
  * ----------------------------------------------------------------
@@ -1995,6 +2012,8 @@ heap_insert(Relation relation, HeapTuple tup, CommandId 
cid,
        Assert(HeapTupleHeaderGetNatts(tup->t_data) <=
                   RelationGetNumberOfAttributes(relation));
 
+       AssertHasSnapshotForToast(relation);
+
        /*
         * Fill in tuple header fields and toast the tuple if necessary.
         *
@@ -2272,6 +2291,8 @@ heap_multi_insert(Relation relation, TupleTableSlot 
**slots, int ntuples,
        /* currently not needed (thus unsupported) for heap_multi_insert() */
        Assert(!(options & HEAP_INSERT_NO_LOGICAL));
 
+       AssertHasSnapshotForToast(relation);
+
        needwal = RelationNeedsWAL(relation);
        saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
                                                                                
                   HEAP_DEFAULT_FILLFACTOR);
@@ -2694,6 +2715,8 @@ heap_delete(Relation relation, ItemPointer tid,
 
        Assert(ItemPointerIsValid(tid));
 
+       AssertHasSnapshotForToast(relation);
+
        /*
         * Forbid this during a parallel operation, lest it allocate a combo 
CID.
         * Other workers might need that combo CID for visibility checks, and we
@@ -3189,6 +3212,8 @@ heap_update(Relation relation, ItemPointer otid, 
HeapTuple newtup,
        Assert(HeapTupleHeaderGetNatts(newtup->t_data) <=
                   RelationGetNumberOfAttributes(relation));
 
+       AssertHasSnapshotForToast(relation);
+
        /*
         * Forbid this during a parallel operation, lest it allocate a combo 
CID.
         * Other workers might need that combo CID for visibility checks, and we
-- 
2.39.5 (Apple Git-154)

Reply via email to