mirror of
https://github.com/postgres/postgres.git
synced 2025-05-29 16:21:20 +03:00
Cope with inplace update making catcache stale during TOAST fetch.
This extends ad98fb14226ae6456fbaed7990ee7591cbe5efd2 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
This commit is contained in:
parent
0f2835ea6f
commit
b08a4b6163
@ -137,6 +137,27 @@ IsCatalogRelationOid(Oid relid)
|
|||||||
return (relid < (Oid) FirstUnpinnedObjectId);
|
return (relid < (Oid) FirstUnpinnedObjectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IsInplaceUpdateRelation
|
||||||
|
* True iff core code performs inplace updates on the relation.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
IsInplaceUpdateRelation(Relation relation)
|
||||||
|
{
|
||||||
|
return IsInplaceUpdateOid(RelationGetRelid(relation));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IsInplaceUpdateOid
|
||||||
|
* Like the above, but takes an OID as argument.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
IsInplaceUpdateOid(Oid relid)
|
||||||
|
{
|
||||||
|
return (relid == RelationRelationId ||
|
||||||
|
relid == DatabaseRelationId);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* IsToastRelation
|
* IsToastRelation
|
||||||
* True iff relation is a TOAST support relation (or index).
|
* True iff relation is a TOAST support relation (or index).
|
||||||
|
44
src/backend/utils/cache/catcache.c
vendored
44
src/backend/utils/cache/catcache.c
vendored
@ -21,6 +21,7 @@
|
|||||||
#include "access/table.h"
|
#include "access/table.h"
|
||||||
#include "access/valid.h"
|
#include "access/valid.h"
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
|
#include "catalog/catalog.h"
|
||||||
#include "catalog/pg_collation.h"
|
#include "catalog/pg_collation.h"
|
||||||
#include "catalog/pg_operator.h"
|
#include "catalog/pg_operator.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
@ -1843,6 +1844,23 @@ ReleaseCatCacheList(CatCList *list)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* equalTuple
|
||||||
|
* Are these tuples memcmp()-equal?
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
equalTuple(HeapTuple a, HeapTuple b)
|
||||||
|
{
|
||||||
|
uint32 alen;
|
||||||
|
uint32 blen;
|
||||||
|
|
||||||
|
alen = a->t_len;
|
||||||
|
blen = b->t_len;
|
||||||
|
return (alen == blen &&
|
||||||
|
memcmp((char *) a->t_data,
|
||||||
|
(char *) b->t_data, blen) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CatalogCacheCreateEntry
|
* CatalogCacheCreateEntry
|
||||||
* Create a new CatCTup entry, copying the given HeapTuple and other
|
* Create a new CatCTup entry, copying the given HeapTuple and other
|
||||||
@ -1893,14 +1911,34 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, SysScanDesc scandesc,
|
|||||||
*/
|
*/
|
||||||
if (HeapTupleHasExternal(ntp))
|
if (HeapTupleHasExternal(ntp))
|
||||||
{
|
{
|
||||||
|
bool need_cmp = IsInplaceUpdateOid(cache->cc_reloid);
|
||||||
|
HeapTuple before = NULL;
|
||||||
|
bool matches = true;
|
||||||
|
|
||||||
|
if (need_cmp)
|
||||||
|
before = heap_copytuple(ntp);
|
||||||
dtp = toast_flatten_tuple(ntp, cache->cc_tupdesc);
|
dtp = toast_flatten_tuple(ntp, cache->cc_tupdesc);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The tuple could become stale while we are doing toast table
|
* The tuple could become stale while we are doing toast table
|
||||||
* access (since AcceptInvalidationMessages can run then), so we
|
* access (since AcceptInvalidationMessages can run then).
|
||||||
* must recheck its visibility afterwards.
|
* equalTuple() detects staleness from inplace updates, while
|
||||||
|
* systable_recheck_tuple() detects staleness from normal updates.
|
||||||
|
*
|
||||||
|
* While this equalTuple() follows the usual rule of reading with
|
||||||
|
* a pin and no buffer lock, it warrants suspicion since an
|
||||||
|
* inplace update could appear at any moment. It's safe because
|
||||||
|
* the inplace update sends an invalidation that can't reorder
|
||||||
|
* before the inplace heap change. If the heap change reaches
|
||||||
|
* this process just after equalTuple() looks, we've not missed
|
||||||
|
* its inval.
|
||||||
*/
|
*/
|
||||||
if (!systable_recheck_tuple(scandesc, ntp))
|
if (need_cmp)
|
||||||
|
{
|
||||||
|
matches = equalTuple(before, ntp);
|
||||||
|
heap_freetuple(before);
|
||||||
|
}
|
||||||
|
if (!matches || !systable_recheck_tuple(scandesc, ntp))
|
||||||
{
|
{
|
||||||
heap_freetuple(dtp);
|
heap_freetuple(dtp);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -21,11 +21,13 @@
|
|||||||
extern bool IsSystemRelation(Relation relation);
|
extern bool IsSystemRelation(Relation relation);
|
||||||
extern bool IsToastRelation(Relation relation);
|
extern bool IsToastRelation(Relation relation);
|
||||||
extern bool IsCatalogRelation(Relation relation);
|
extern bool IsCatalogRelation(Relation relation);
|
||||||
|
extern bool IsInplaceUpdateRelation(Relation relation);
|
||||||
|
|
||||||
extern bool IsSystemClass(Oid relid, Form_pg_class reltuple);
|
extern bool IsSystemClass(Oid relid, Form_pg_class reltuple);
|
||||||
extern bool IsToastClass(Form_pg_class reltuple);
|
extern bool IsToastClass(Form_pg_class reltuple);
|
||||||
|
|
||||||
extern bool IsCatalogRelationOid(Oid relid);
|
extern bool IsCatalogRelationOid(Oid relid);
|
||||||
|
extern bool IsInplaceUpdateOid(Oid relid);
|
||||||
|
|
||||||
extern bool IsCatalogNamespace(Oid namespaceId);
|
extern bool IsCatalogNamespace(Oid namespaceId);
|
||||||
extern bool IsToastNamespace(Oid namespaceId);
|
extern bool IsToastNamespace(Oid namespaceId);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user