]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
In REASSIGN OWNED of a database, lock the tuple as mandated.
authorNoah Misch <noah@leadboat.com>
Sat, 28 Dec 2024 15:16:22 +0000 (07:16 -0800)
committerNoah Misch <noah@leadboat.com>
Sat, 28 Dec 2024 15:16:22 +0000 (07:16 -0800)
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

src/backend/catalog/objectaddress.c
src/backend/commands/alter.c
src/include/catalog/objectaddress.h
src/test/regress/expected/database.out
src/test/regress/sql/database.sql

index be7e4a5dd01d43e42e709cd05028f156d3303dc4..87af58fac3a62b34cdcbc6e36d55067ee222ea08 100644 (file)
@@ -2784,6 +2784,22 @@ 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);
@@ -2791,7 +2807,12 @@ get_catalog_object_by_oid(Relation catalog, AttrNumber oidcol, Oid objectId)
 
        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;
        }
@@ -2816,6 +2837,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);
index a45f3bb6b8347924d14d2497fadbe21fe6f50907..abb80fd1224a26ec14d6604222b7c9fa5cc5f7a8 100644 (file)
@@ -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"
@@ -924,7 +925,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));
@@ -1024,6 +1027,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);
 
@@ -1032,6 +1037,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);
index 3a70d80e3201ee89273ed9da16b8b8a27543f107..c4ba1cb2b32933aa52516ce9906ab1e053e49f07 100644 (file)
@@ -69,6 +69,10 @@ extern bool get_object_namensp_unique(Oid class_id);
 
 extern HeapTuple get_catalog_object_by_oid(Relation catalog,
                                                                                   AttrNumber oidcol, Oid objectId);
+extern HeapTuple get_catalog_object_by_oid_extended(Relation catalog,
+                                                                                                       AttrNumber oidcol,
+                                                                                                       Oid objectId,
+                                                                                                       bool locktup);
 
 extern char *getObjectDescription(const ObjectAddress *object,
                                                                  bool missing_ok);
index 454db91ec090bc93cffd39a21cd8a36de0ddee2a..4cbdbdf84d0c55f9eb5f43a49adc4f60cd040765 100644 (file)
@@ -12,4 +12,10 @@ WHERE datname = 'regression_utf8';
 -- load catcache entry, if nothing else does
 ALTER DATABASE regression_utf8 RESET TABLESPACE;
 ROLLBACK;
+CREATE ROLE regress_datdba_before;
+CREATE ROLE regress_datdba_after;
+ALTER DATABASE regression_utf8 OWNER TO regress_datdba_before;
+REASSIGN OWNED BY regress_datdba_before TO regress_datdba_after;
 DROP DATABASE regression_utf8;
+DROP ROLE regress_datdba_before;
+DROP ROLE regress_datdba_after;
index 0367c0e37ab1fe85673355d182ac4c94627d739c..46ad2634781ea61fc1686bf987733f942fd552e2 100644 (file)
@@ -14,4 +14,11 @@ WHERE datname = 'regression_utf8';
 ALTER DATABASE regression_utf8 RESET TABLESPACE;
 ROLLBACK;
 
+CREATE ROLE regress_datdba_before;
+CREATE ROLE regress_datdba_after;
+ALTER DATABASE regression_utf8 OWNER TO regress_datdba_before;
+REASSIGN OWNED BY regress_datdba_before TO regress_datdba_after;
+
 DROP DATABASE regression_utf8;
+DROP ROLE regress_datdba_before;
+DROP ROLE regress_datdba_after;