aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorNoah Misch <noah@leadboat.com>2024-12-28 07:16:22 -0800
committerNoah Misch <noah@leadboat.com>2024-12-28 07:16:26 -0800
commitfa6131377054d04f7c938b196b39eeb22784951b (patch)
tree5ba449ea0dfc4b9962e2208708412c63c2d9c170 /src/backend
parentd8b0c64116483c1ce9711e66700dddd709941a22 (diff)
downloadpostgresql-fa6131377054d04f7c938b196b39eeb22784951b.tar.gz
postgresql-fa6131377054d04f7c938b196b39eeb22784951b.zip
In REASSIGN OWNED of a database, lock the tuple as mandated.
Commit aac2c9b4fde889d13f859c233c2523345e72d32b mandated such locking and attempted to fulfill that mandate, but it missed REASSIGN OWNED. Hence, it remained possible to lose VACUUM's inplace update of datfrozenxid if a REASSIGN OWNED processed that database at the same time. This didn't affect the other inplace-updated catalog, pg_class. For pg_class, REASSIGN OWNED calls ATExecChangeOwner() instead of the generic AlterObjectOwner_internal(), and ATExecChangeOwner() fulfills the locking mandate. Like in GRANT, implement this by following the locking protocol for any catalog subject to the generic AlterObjectOwner_internal(). It would suffice to do this for IsInplaceUpdateOid() catalogs only. Back-patch to v13 (all supported versions). Kirill Reshke. Reported by Alexander Kukushkin. Discussion: https://postgr.es/m/CAFh8B=mpKjAy4Cuun-HP-f_vRzh2HSvYFG3rhVfYbfEBUhBAGg@mail.gmail.com
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/objectaddress.c27
-rw-r--r--src/backend/commands/alter.c9
2 files changed, 34 insertions, 2 deletions
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index dbbd5a6358a..bbd9b78050d 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -2780,13 +2780,34 @@ get_object_property_data(Oid class_id)
HeapTuple
get_catalog_object_by_oid(Relation catalog, AttrNumber oidcol, Oid objectId)
{
+ return
+ get_catalog_object_by_oid_extended(catalog, oidcol, objectId, false);
+}
+
+/*
+ * Same as get_catalog_object_by_oid(), but with an additional "locktup"
+ * argument controlling whether to acquire a LOCKTAG_TUPLE at mode
+ * InplaceUpdateTupleLock. See README.tuplock section "Locking to write
+ * inplace-updated tables".
+ */
+HeapTuple
+get_catalog_object_by_oid_extended(Relation catalog,
+ AttrNumber oidcol,
+ Oid objectId,
+ bool locktup)
+{
HeapTuple tuple;
Oid classId = RelationGetRelid(catalog);
int oidCacheId = get_object_catcache_oid(classId);
if (oidCacheId > 0)
{
- tuple = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objectId));
+ if (locktup)
+ tuple = SearchSysCacheLockedCopy1(oidCacheId,
+ ObjectIdGetDatum(objectId));
+ else
+ tuple = SearchSysCacheCopy1(oidCacheId,
+ ObjectIdGetDatum(objectId));
if (!HeapTupleIsValid(tuple)) /* should not happen */
return NULL;
}
@@ -2811,6 +2832,10 @@ get_catalog_object_by_oid(Relation catalog, AttrNumber oidcol, Oid objectId)
systable_endscan(scan);
return NULL;
}
+
+ if (locktup)
+ LockTuple(catalog, &tuple->t_self, InplaceUpdateTupleLock);
+
tuple = heap_copytuple(tuple);
systable_endscan(scan);
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 4f99ebb4470..132283b1ef2 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -59,6 +59,7 @@
#include "miscadmin.h"
#include "replication/logicalworker.h"
#include "rewrite/rewriteDefine.h"
+#include "storage/lmgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@@ -931,7 +932,9 @@ AlterObjectOwner_internal(Oid classId, Oid objectId, Oid new_ownerId)
rel = table_open(catalogId, RowExclusiveLock);
- oldtup = get_catalog_object_by_oid(rel, Anum_oid, objectId);
+ /* Search tuple and lock it. */
+ oldtup =
+ get_catalog_object_by_oid_extended(rel, Anum_oid, objectId, true);
if (oldtup == NULL)
elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
objectId, RelationGetRelationName(rel));
@@ -1031,6 +1034,8 @@ AlterObjectOwner_internal(Oid classId, Oid objectId, Oid new_ownerId)
/* Perform actual update */
CatalogTupleUpdate(rel, &newtup->t_self, newtup);
+ UnlockTuple(rel, &oldtup->t_self, InplaceUpdateTupleLock);
+
/* Update owner dependency reference */
changeDependencyOnOwner(classId, objectId, new_ownerId);
@@ -1039,6 +1044,8 @@ AlterObjectOwner_internal(Oid classId, Oid objectId, Oid new_ownerId)
pfree(nulls);
pfree(replaces);
}
+ else
+ UnlockTuple(rel, &oldtup->t_self, InplaceUpdateTupleLock);
/* Note the post-alter hook gets classId not catalogId */
InvokeObjectPostAlterHook(classId, objectId, 0);