Skip to content

Commit 706054b

Browse files
Ensure we have a snapshot when updating various system catalogs.
A few places that access system catalogs don't set up an active snapshot before potentially accessing their TOAST tables. To fix, push an active snapshot just before each section of code that might require accessing one of these TOAST tables, and pop it shortly afterwards. While at it, this commit adds some rather strict assertions in an attempt to prevent such issues in the future. Commit 16bf24e recently removed pg_replication_origin's TOAST table in order to fix the same problem for that catalog. On the back-branches, those bugs are left in place. We cannot easily remove a catalog's TOAST table on released major versions, and only replication origins with extremely long names are affected. Given the low severity of the issue, fixing older versions doesn't seem worth the trouble of significantly modifying the patch. Also, on v13 and v14, the aforementioned strict assertions have been omitted because commit 2776922, which added HaveRegisteredOrActiveSnapshot(), was not back-patched. While we could probably back-patch it now, I've opted against it because it seems unlikely that new TOAST snapshot issues will be introduced in the oldest supported versions. Reported-by: Alexander Lakhin <exclusion@gmail.com> Reviewed-by: Michael Paquier <michael@paquier.xyz> 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: 13
1 parent 232d8ca commit 706054b

File tree

5 files changed

+69
-1
lines changed

5 files changed

+69
-1
lines changed

src/backend/access/heap/heapam.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,27 @@ static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
213213
#define TUPLOCK_from_mxstatus(status) \
214214
(MultiXactStatusLock[(status)])
215215

216+
/*
217+
* Check that we have a valid snapshot if we might need TOAST access.
218+
*/
219+
static inline void
220+
AssertHasSnapshotForToast(Relation rel)
221+
{
222+
#ifdef USE_ASSERT_CHECKING
223+
224+
/* bootstrap mode in particular breaks this rule */
225+
if (!IsNormalProcessingMode())
226+
return;
227+
228+
/* if the relation doesn't have a TOAST table, we are good */
229+
if (!OidIsValid(rel->rd_rel->reltoastrelid))
230+
return;
231+
232+
Assert(HaveRegisteredOrActiveSnapshot());
233+
234+
#endif /* USE_ASSERT_CHECKING */
235+
}
236+
216237
/* ----------------------------------------------------------------
217238
* heap support routines
218239
* ----------------------------------------------------------------
@@ -2066,6 +2087,8 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
20662087
Assert(HeapTupleHeaderGetNatts(tup->t_data) <=
20672088
RelationGetNumberOfAttributes(relation));
20682089

2090+
AssertHasSnapshotForToast(relation);
2091+
20692092
/*
20702093
* Fill in tuple header fields and toast the tuple if necessary.
20712094
*
@@ -2343,6 +2366,8 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
23432366
/* currently not needed (thus unsupported) for heap_multi_insert() */
23442367
Assert(!(options & HEAP_INSERT_NO_LOGICAL));
23452368

2369+
AssertHasSnapshotForToast(relation);
2370+
23462371
needwal = RelationNeedsWAL(relation);
23472372
saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
23482373
HEAP_DEFAULT_FILLFACTOR);
@@ -2765,6 +2790,8 @@ heap_delete(Relation relation, ItemPointer tid,
27652790

27662791
Assert(ItemPointerIsValid(tid));
27672792

2793+
AssertHasSnapshotForToast(relation);
2794+
27682795
/*
27692796
* Forbid this during a parallel operation, lest it allocate a combo CID.
27702797
* Other workers might need that combo CID for visibility checks, and we
@@ -3260,6 +3287,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
32603287
Assert(HeapTupleHeaderGetNatts(newtup->t_data) <=
32613288
RelationGetNumberOfAttributes(relation));
32623289

3290+
AssertHasSnapshotForToast(relation);
3291+
32633292
/*
32643293
* Forbid this during a parallel operation, lest it allocate a combo CID.
32653294
* Other workers might need that combo CID for visibility checks, and we

src/backend/commands/indexcmds.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4226,7 +4226,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
42264226
false);
42274227

42284228
/*
4229-
* Updating pg_index might involve TOAST table access, so ensure we
4229+
* Swapping the indexes might involve TOAST table access, so ensure we
42304230
* have a valid snapshot.
42314231
*/
42324232
PushActiveSnapshot(GetTransactionSnapshot());

src/backend/commands/tablecmds.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20964,9 +20964,17 @@ ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
2096420964
tab->rel = rel;
2096520965
}
2096620966

20967+
/*
20968+
* Detaching the partition might involve TOAST table access, so ensure we
20969+
* have a valid snapshot.
20970+
*/
20971+
PushActiveSnapshot(GetTransactionSnapshot());
20972+
2096720973
/* Do the final part of detaching */
2096820974
DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
2096920975

20976+
PopActiveSnapshot();
20977+
2097020978
ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
2097120979

2097220980
/* keep our lock until commit */

src/backend/postmaster/autovacuum.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2234,6 +2234,12 @@ do_autovacuum(void)
22342234
get_namespace_name(classForm->relnamespace),
22352235
NameStr(classForm->relname))));
22362236

2237+
/*
2238+
* Deletion might involve TOAST table access, so ensure we have a
2239+
* valid snapshot.
2240+
*/
2241+
PushActiveSnapshot(GetTransactionSnapshot());
2242+
22372243
object.classId = RelationRelationId;
22382244
object.objectId = relid;
22392245
object.objectSubId = 0;
@@ -2246,6 +2252,7 @@ do_autovacuum(void)
22462252
* To commit the deletion, end current transaction and start a new
22472253
* one. Note this also releases the locks we took.
22482254
*/
2255+
PopActiveSnapshot();
22492256
CommitTransactionCommand();
22502257
StartTransactionCommand();
22512258

src/backend/replication/logical/worker.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4626,8 +4626,16 @@ run_apply_worker()
46264626
walrcv_startstreaming(LogRepWorkerWalRcvConn, &options);
46274627

46284628
StartTransactionCommand();
4629+
4630+
/*
4631+
* Updating pg_subscription might involve TOAST table access, so
4632+
* ensure we have a valid snapshot.
4633+
*/
4634+
PushActiveSnapshot(GetTransactionSnapshot());
4635+
46294636
UpdateTwoPhaseState(MySubscription->oid, LOGICALREP_TWOPHASE_STATE_ENABLED);
46304637
MySubscription->twophasestate = LOGICALREP_TWOPHASE_STATE_ENABLED;
4638+
PopActiveSnapshot();
46314639
CommitTransactionCommand();
46324640
}
46334641
else
@@ -4843,7 +4851,15 @@ DisableSubscriptionAndExit(void)
48434851

48444852
/* Disable the subscription */
48454853
StartTransactionCommand();
4854+
4855+
/*
4856+
* Updating pg_subscription might involve TOAST table access, so ensure we
4857+
* have a valid snapshot.
4858+
*/
4859+
PushActiveSnapshot(GetTransactionSnapshot());
4860+
48464861
DisableSubscription(MySubscription->oid);
4862+
PopActiveSnapshot();
48474863
CommitTransactionCommand();
48484864

48494865
/* Ensure we remove no-longer-useful entry for worker's start time */
@@ -4947,6 +4963,12 @@ clear_subscription_skip_lsn(XLogRecPtr finish_lsn)
49474963
started_tx = true;
49484964
}
49494965

4966+
/*
4967+
* Updating pg_subscription might involve TOAST table access, so ensure we
4968+
* have a valid snapshot.
4969+
*/
4970+
PushActiveSnapshot(GetTransactionSnapshot());
4971+
49504972
/*
49514973
* Protect subskiplsn of pg_subscription from being concurrently updated
49524974
* while clearing it.
@@ -5005,6 +5027,8 @@ clear_subscription_skip_lsn(XLogRecPtr finish_lsn)
50055027
heap_freetuple(tup);
50065028
table_close(rel, NoLock);
50075029

5030+
PopActiveSnapshot();
5031+
50085032
if (started_tx)
50095033
CommitTransactionCommand();
50105034
}

0 commit comments

Comments
 (0)