Skip to content

Commit b08a4b6

Browse files
committed
Cope with inplace update making catcache stale during TOAST fetch.
This extends ad98fb1 to invals of inplace updates. Trouble requires an inplace update of a catalog having a TOAST table, so only pg_database was at risk. (The other catalog on which core code performs inplace updates, pg_class, has no TOAST table.) Trouble would require something like the inplace-inval.spec test. Consider GRANT ... ON DATABASE fetching a stale row from cache and discarding a datfrozenxid update that vac_truncate_clog() has already relied upon. Back-patch to v12 (all supported versions). Reviewed (in an earlier version) by Robert Haas. Discussion: https://postgr.es/m/20240114201411.d0@rfd.leadboat.com Discussion: https://postgr.es/m/20240512232923.aa.nmisch@google.com
1 parent 0f2835e commit b08a4b6

File tree

3 files changed

+64
-3
lines changed

3 files changed

+64
-3
lines changed

src/backend/catalog/catalog.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,27 @@ IsCatalogRelationOid(Oid relid)
137137
return (relid < (Oid) FirstUnpinnedObjectId);
138138
}
139139

140+
/*
141+
* IsInplaceUpdateRelation
142+
* True iff core code performs inplace updates on the relation.
143+
*/
144+
bool
145+
IsInplaceUpdateRelation(Relation relation)
146+
{
147+
return IsInplaceUpdateOid(RelationGetRelid(relation));
148+
}
149+
150+
/*
151+
* IsInplaceUpdateOid
152+
* Like the above, but takes an OID as argument.
153+
*/
154+
bool
155+
IsInplaceUpdateOid(Oid relid)
156+
{
157+
return (relid == RelationRelationId ||
158+
relid == DatabaseRelationId);
159+
}
160+
140161
/*
141162
* IsToastRelation
142163
* True iff relation is a TOAST support relation (or index).

src/backend/utils/cache/catcache.c

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "access/table.h"
2222
#include "access/valid.h"
2323
#include "access/xact.h"
24+
#include "catalog/catalog.h"
2425
#include "catalog/pg_collation.h"
2526
#include "catalog/pg_operator.h"
2627
#include "catalog/pg_type.h"
@@ -1843,6 +1844,23 @@ ReleaseCatCacheList(CatCList *list)
18431844
}
18441845

18451846

1847+
/*
1848+
* equalTuple
1849+
* Are these tuples memcmp()-equal?
1850+
*/
1851+
static bool
1852+
equalTuple(HeapTuple a, HeapTuple b)
1853+
{
1854+
uint32 alen;
1855+
uint32 blen;
1856+
1857+
alen = a->t_len;
1858+
blen = b->t_len;
1859+
return (alen == blen &&
1860+
memcmp((char *) a->t_data,
1861+
(char *) b->t_data, blen) == 0);
1862+
}
1863+
18461864
/*
18471865
* CatalogCacheCreateEntry
18481866
* Create a new CatCTup entry, copying the given HeapTuple and other
@@ -1893,14 +1911,34 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, SysScanDesc scandesc,
18931911
*/
18941912
if (HeapTupleHasExternal(ntp))
18951913
{
1914+
bool need_cmp = IsInplaceUpdateOid(cache->cc_reloid);
1915+
HeapTuple before = NULL;
1916+
bool matches = true;
1917+
1918+
if (need_cmp)
1919+
before = heap_copytuple(ntp);
18961920
dtp = toast_flatten_tuple(ntp, cache->cc_tupdesc);
18971921

18981922
/*
18991923
* The tuple could become stale while we are doing toast table
1900-
* access (since AcceptInvalidationMessages can run then), so we
1901-
* must recheck its visibility afterwards.
1924+
* access (since AcceptInvalidationMessages can run then).
1925+
* equalTuple() detects staleness from inplace updates, while
1926+
* systable_recheck_tuple() detects staleness from normal updates.
1927+
*
1928+
* While this equalTuple() follows the usual rule of reading with
1929+
* a pin and no buffer lock, it warrants suspicion since an
1930+
* inplace update could appear at any moment. It's safe because
1931+
* the inplace update sends an invalidation that can't reorder
1932+
* before the inplace heap change. If the heap change reaches
1933+
* this process just after equalTuple() looks, we've not missed
1934+
* its inval.
19021935
*/
1903-
if (!systable_recheck_tuple(scandesc, ntp))
1936+
if (need_cmp)
1937+
{
1938+
matches = equalTuple(before, ntp);
1939+
heap_freetuple(before);
1940+
}
1941+
if (!matches || !systable_recheck_tuple(scandesc, ntp))
19041942
{
19051943
heap_freetuple(dtp);
19061944
return NULL;

src/include/catalog/catalog.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@
2121
extern bool IsSystemRelation(Relation relation);
2222
extern bool IsToastRelation(Relation relation);
2323
extern bool IsCatalogRelation(Relation relation);
24+
extern bool IsInplaceUpdateRelation(Relation relation);
2425

2526
extern bool IsSystemClass(Oid relid, Form_pg_class reltuple);
2627
extern bool IsToastClass(Form_pg_class reltuple);
2728

2829
extern bool IsCatalogRelationOid(Oid relid);
30+
extern bool IsInplaceUpdateOid(Oid relid);
2931

3032
extern bool IsCatalogNamespace(Oid namespaceId);
3133
extern bool IsToastNamespace(Oid namespaceId);

0 commit comments

Comments
 (0)