aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/cache/relcache.c
diff options
context:
space:
mode:
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>2025-03-11 23:20:34 +0200
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>2025-03-11 23:20:34 +0200
commit8076c00592e40e8dbd1fce7a98b20d4bf075e4ba (patch)
tree261664f7645cff970a845c4c136e91987cbf8591 /src/backend/utils/cache/relcache.c
parentbd65cb3cd48a7a5ce48b26f8031ad3968efed87e (diff)
downloadpostgresql-8076c00592e40e8dbd1fce7a98b20d4bf075e4ba.tar.gz
postgresql-8076c00592e40e8dbd1fce7a98b20d4bf075e4ba.zip
Assert that a snapshot is active or registered before it's used
The comment in GetTransactionSnapshot() said that you "should call RegisterSnapshot or PushActiveSnapshot on the returned snap if it is to be used very long". That felt too unclear to me. Make the comment more strongly worded. To enforce that rule and to catch potential bugs where a snapshot might get invalidated while it's still in use, add an assertion to HeapTupleSatisfiesMVCC() to check that the snapshot is registered or pushed to active stack. No new bugs were found by this, but it seems like good future-proofing. It's not a great place for the check; HeapTupleSatisfiesMVCC() is in fact safe to call with an unregistered snapshot, and the assertion won't catch other unsafe uses. But it goes a long way in practice. Fix a few cases that were playing fast and loose with that and just assumed that the snapshot cannot be invalidated during a scan. Those assumptions were not wrong, but they're not performance critical, so let's drop the excuses and just register the snapshot. These were false positives found by the new assertion. Discussion: https://www.postgresql.org/message-id/7c56f180-b9e1-481e-8c1d-efa63de3ecbb@iki.fi
Diffstat (limited to 'src/backend/utils/cache/relcache.c')
-rw-r--r--src/backend/utils/cache/relcache.c15
1 files changed, 9 insertions, 6 deletions
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index d1ae761b3f6..9f54a9e72b7 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -371,14 +371,13 @@ ScanPgRelation(Oid targetRelId, bool indexOK, bool force_non_historic)
pg_class_desc = table_open(RelationRelationId, AccessShareLock);
/*
- * The caller might need a tuple that's newer than the one the historic
- * snapshot; currently the only case requiring to do so is looking up the
- * relfilenumber of non mapped system relations during decoding. That
- * snapshot can't change in the midst of a relcache build, so there's no
- * need to register the snapshot.
+ * The caller might need a tuple that's newer than what's visible to the
+ * historic snapshot; currently the only case requiring to do so is
+ * looking up the relfilenumber of non mapped system relations during
+ * decoding.
*/
if (force_non_historic)
- snapshot = GetNonHistoricCatalogSnapshot(RelationRelationId);
+ snapshot = RegisterSnapshot(GetNonHistoricCatalogSnapshot(RelationRelationId));
pg_class_scan = systable_beginscan(pg_class_desc, ClassOidIndexId,
indexOK && criticalRelcachesBuilt,
@@ -395,6 +394,10 @@ ScanPgRelation(Oid targetRelId, bool indexOK, bool force_non_historic)
/* all done */
systable_endscan(pg_class_scan);
+
+ if (snapshot)
+ UnregisterSnapshot(snapshot);
+
table_close(pg_class_desc, AccessShareLock);
return pg_class_tuple;