On Wed, Sep 25, 2024 at 01:05:26PM +0900, Michael Paquier wrote: > On Tue, Sep 24, 2024 at 02:26:08PM -0500, Nathan Bossart wrote: >> So... maybe we >> should just remove pg_replication_origin's TOAST table instead... > > I'd rather keep it, FWIW. Contrary to pg_authid it does not imply > problems at the same scale because we would have access to the toast > relation in all the code paths with logical workers or table syncs. > The other one was at early authentication stages.
Okay. > It sounds to me that we should be much more proactive in detecting > these failures and add something like that on HEAD. That's cheap > enough. As the checks are the same for all these code paths, perhaps > just hide them behind a local macro to reduce the duplication? In v2, I moved the assertions to a new function called by the heapam.c routines. I was hoping to move them to the tableam.h routines, but several callers (in particular, the catalog/indexing.c ones that are causing problems) call the heap ones directly. I've also included a 0001 patch that introduces a RelationGetToastRelid() macro because I got tired of typing "rel->rd_rel->reltoastrelid". 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? -- nathan
>From 3eb1c0997e18d9e7a56cc3e974076e58613a1bb9 Mon Sep 17 00:00:00 2001 From: Nathan Bossart <nat...@postgresql.org> Date: Tue, 8 Oct 2024 11:28:58 -0500 Subject: [PATCH v2 1/2] add RelationGetToastRelid macro --- 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 78e59384d1..966cf39f34 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -3075,7 +3075,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 6084dfa97c..7f07fda372 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 af8c05b91f..1788fbcf60 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -2139,7 +2139,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, @@ -15160,10 +15160,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); @@ -15252,7 +15252,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 5f4f8053ee4e11276540d9cd48e149c8f02a7588 Mon Sep 17 00:00:00 2001 From: Nathan Bossart <nat...@postgresql.org> Date: Tue, 8 Oct 2024 13:39:30 -0500 Subject: [PATCH v2 2/2] ensure we have a snapshot if we might need toast access --- src/backend/access/heap/heapam.c | 25 +++++++++++++++++++++ src/backend/commands/tablecmds.c | 8 +++++++ src/backend/replication/logical/tablesync.c | 10 +++++++++ src/backend/replication/logical/worker.c | 8 +++++++ 4 files changed, 51 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 diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1788fbcf60..2e468d1d7d 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -19281,9 +19281,17 @@ ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, tab->rel = rel; } + 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..ed7d187bfe 100644 --- a/src/backend/replication/logical/tablesync.c +++ b/src/backend/replication/logical/tablesync.c @@ -377,6 +377,8 @@ process_syncing_tables_for_sync(XLogRecPtr current_lsn) replorigin_session_origin_lsn = InvalidXLogRecPtr; replorigin_session_origin_timestamp = 0; + PushActiveSnapshot(GetTransactionSnapshot()); + /* * Drop the tablesync's origin tracking if exists. * @@ -387,6 +389,8 @@ process_syncing_tables_for_sync(XLogRecPtr current_lsn) */ replorigin_drop_by_name(originname, true, false); + PopActiveSnapshot(); + finish_sync_worker(); } else @@ -483,6 +487,8 @@ process_syncing_tables_for_apply(XLogRecPtr current_lsn) started_tx = true; } + PushActiveSnapshot(GetTransactionSnapshot()); + /* * Remove the tablesync origin tracking if exists. * @@ -500,6 +506,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. */ @@ -1456,7 +1464,9 @@ LogicalRepSyncTableStart(XLogRecPtr *origin_startpos) * logged for the purpose of recovery. Locks are to prevent the * replication origin from vanishing while advancing. */ + 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..502033f3d8 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -4604,8 +4604,10 @@ run_apply_worker() walrcv_startstreaming(LogRepWorkerWalRcvConn, &options); StartTransactionCommand(); + PushActiveSnapshot(GetTransactionSnapshot()); UpdateTwoPhaseState(MySubscription->oid, LOGICALREP_TWOPHASE_STATE_ENABLED); MySubscription->twophasestate = LOGICALREP_TWOPHASE_STATE_ENABLED; + PopActiveSnapshot(); CommitTransactionCommand(); } else @@ -4821,7 +4823,9 @@ DisableSubscriptionAndExit(void) /* Disable the subscription */ StartTransactionCommand(); + PushActiveSnapshot(GetTransactionSnapshot()); DisableSubscription(MySubscription->oid); + PopActiveSnapshot(); CommitTransactionCommand(); /* Ensure we remove no-longer-useful entry for worker's start time */ @@ -4925,6 +4929,8 @@ clear_subscription_skip_lsn(XLogRecPtr finish_lsn) started_tx = true; } + PushActiveSnapshot(GetTransactionSnapshot()); + /* * Protect subskiplsn of pg_subscription from being concurrently updated * while clearing it. @@ -4983,6 +4989,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)