This patch started out as a refactoring, thinking that objectNamesToOids() in aclchk.c should really mostly be a loop around get_object_address(). This is mostly true, with a few special cases because the node representations are a bit different in some cases, and OBJECT_PARAMETER_ACL, which is obviously very different. This saves a bunch of duplicative code, which is nice.

Additionally, get_object_address() handles locking, which objectNamesToOids() somewhat famously does not do, and there is a code comment about it. With this refactoring, we get the locking pretty much for free.

Interestingly, this changes the output of the intra-grant-inplace isolation test, which is recent work. It might be good to get some review from those who worked on that, to make sure that the new behavior is still correct and/or to check whether those test cases are still applicable.

Also, it would be worth thinking about what the correct lock mode should be here.
From fb910c6af0fb0691448680ec4a4cc1eb1857cd5b Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Mon, 28 Oct 2024 16:07:01 +0100
Subject: [PATCH] Proper object locking for GRANT/REVOKE

Refactor objectNamesToOids() to use get_object_address() internally if
possible.  Not only does this save a lot of code, it also allows us to
use the object locking provided by get_object_address() for
GRANT/REVOKE.  There was previously a code comment that complained
about the lack of locking in objectNamesToOids(), which is now fixed.

The check in ExecGrant_Type_check() is obsolete because
get_object_address_type() already does the same check.
---
 src/backend/catalog/aclchk.c                  | 162 +++++-------------
 .../expected/intra-grant-inplace.out          |   2 +-
 2 files changed, 41 insertions(+), 123 deletions(-)

diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 95eb0b12277..2f91aa865a3 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -659,147 +659,76 @@ ExecGrantStmt_oids(InternalGrant *istmt)
  * objectNamesToOids
  *
  * Turn a list of object names of a given type into an Oid list.
- *
- * XXX: This function doesn't take any sort of locks on the objects whose
- * names it looks up.  In the face of concurrent DDL, we might easily latch
- * onto an old version of an object, causing the GRANT or REVOKE statement
- * to fail.
  */
 static List *
 objectNamesToOids(ObjectType objtype, List *objnames, bool is_grant)
 {
        List       *objects = NIL;
        ListCell   *cell;
+       const LOCKMODE lockmode = AccessShareLock;
 
        Assert(objnames != NIL);
 
        switch (objtype)
        {
+               default:
+
+                       /*
+                        * For most object types, we use get_object_address() 
directly.
+                        */
+                       foreach(cell, objnames)
+                       {
+                               ObjectAddress address;
+                               Relation        relation;
+
+                               address = get_object_address(objtype, 
lfirst(cell), &relation, lockmode, false);
+                               Assert(relation == NULL);
+                               objects = lappend_oid(objects, 
address.objectId);
+                       }
+                       break;
                case OBJECT_TABLE:
                case OBJECT_SEQUENCE:
+
+                       /*
+                        * Here, we don't use get_object_address().  It 
requires that the
+                        * specified object type match the actual type of the 
object, but
+                        * in GRANT/REVOKE, all table-like things are addressed 
as TABLE.
+                        */
                        foreach(cell, objnames)
                        {
                                RangeVar   *relvar = (RangeVar *) lfirst(cell);
                                Oid                     relOid;
 
-                               relOid = RangeVarGetRelid(relvar, NoLock, 
false);
+                               relOid = RangeVarGetRelid(relvar, lockmode, 
false);
                                objects = lappend_oid(objects, relOid);
                        }
                        break;
-               case OBJECT_DATABASE:
-                       foreach(cell, objnames)
-                       {
-                               char       *dbname = strVal(lfirst(cell));
-                               Oid                     dbid;
-
-                               dbid = get_database_oid(dbname, false);
-                               objects = lappend_oid(objects, dbid);
-                       }
-                       break;
                case OBJECT_DOMAIN:
                case OBJECT_TYPE:
+
+                       /*
+                        * The parse representation of types and domains in 
privilege
+                        * targets is different from that expected by 
get_object_address()
+                        * (for parse conflict reasons), so we have to do a bit 
of
+                        * conversion here.
+                        */
                        foreach(cell, objnames)
                        {
                                List       *typname = (List *) lfirst(cell);
-                               Oid                     oid;
+                               TypeName   *tn = 
makeTypeNameFromNameList(typname);
+                               ObjectAddress address;
+                               Relation        relation;
 
-                               oid = typenameTypeId(NULL, 
makeTypeNameFromNameList(typname));
-                               objects = lappend_oid(objects, oid);
-                       }
-                       break;
-               case OBJECT_FUNCTION:
-                       foreach(cell, objnames)
-                       {
-                               ObjectWithArgs *func = (ObjectWithArgs *) 
lfirst(cell);
-                               Oid                     funcid;
-
-                               funcid = LookupFuncWithArgs(OBJECT_FUNCTION, 
func, false);
-                               objects = lappend_oid(objects, funcid);
-                       }
-                       break;
-               case OBJECT_LANGUAGE:
-                       foreach(cell, objnames)
-                       {
-                               char       *langname = strVal(lfirst(cell));
-                               Oid                     oid;
-
-                               oid = get_language_oid(langname, false);
-                               objects = lappend_oid(objects, oid);
-                       }
-                       break;
-               case OBJECT_LARGEOBJECT:
-                       foreach(cell, objnames)
-                       {
-                               Oid                     lobjOid = 
oidparse(lfirst(cell));
-
-                               if (!LargeObjectExists(lobjOid))
-                                       ereport(ERROR,
-                                                       
(errcode(ERRCODE_UNDEFINED_OBJECT),
-                                                        errmsg("large object 
%u does not exist",
-                                                                       
lobjOid)));
-
-                               objects = lappend_oid(objects, lobjOid);
-                       }
-                       break;
-               case OBJECT_SCHEMA:
-                       foreach(cell, objnames)
-                       {
-                               char       *nspname = strVal(lfirst(cell));
-                               Oid                     oid;
-
-                               oid = get_namespace_oid(nspname, false);
-                               objects = lappend_oid(objects, oid);
-                       }
-                       break;
-               case OBJECT_PROCEDURE:
-                       foreach(cell, objnames)
-                       {
-                               ObjectWithArgs *func = (ObjectWithArgs *) 
lfirst(cell);
-                               Oid                     procid;
-
-                               procid = LookupFuncWithArgs(OBJECT_PROCEDURE, 
func, false);
-                               objects = lappend_oid(objects, procid);
-                       }
-                       break;
-               case OBJECT_ROUTINE:
-                       foreach(cell, objnames)
-                       {
-                               ObjectWithArgs *func = (ObjectWithArgs *) 
lfirst(cell);
-                               Oid                     routid;
-
-                               routid = LookupFuncWithArgs(OBJECT_ROUTINE, 
func, false);
-                               objects = lappend_oid(objects, routid);
-                       }
-                       break;
-               case OBJECT_TABLESPACE:
-                       foreach(cell, objnames)
-                       {
-                               char       *spcname = strVal(lfirst(cell));
-                               Oid                     spcoid;
-
-                               spcoid = get_tablespace_oid(spcname, false);
-                               objects = lappend_oid(objects, spcoid);
-                       }
-                       break;
-               case OBJECT_FDW:
-                       foreach(cell, objnames)
-                       {
-                               char       *fdwname = strVal(lfirst(cell));
-                               Oid                     fdwid = 
get_foreign_data_wrapper_oid(fdwname, false);
-
-                               objects = lappend_oid(objects, fdwid);
-                       }
-                       break;
-               case OBJECT_FOREIGN_SERVER:
-                       foreach(cell, objnames)
-                       {
-                               char       *srvname = strVal(lfirst(cell));
-                               Oid                     srvid = 
get_foreign_server_oid(srvname, false);
-
-                               objects = lappend_oid(objects, srvid);
+                               address = get_object_address(objtype, (Node *) 
tn, &relation, lockmode, false);
+                               Assert(relation == NULL);
+                               objects = lappend_oid(objects, 
address.objectId);
                        }
                        break;
                case OBJECT_PARAMETER_ACL:
+
+                       /*
+                        * Parameters are handled completely differently.
+                        */
                        foreach(cell, objnames)
                        {
                                /*
@@ -830,9 +759,6 @@ objectNamesToOids(ObjectType objtype, List *objnames, bool 
is_grant)
                                        objects = lappend_oid(objects, 
parameterId);
                        }
                        break;
-               default:
-                       elog(ERROR, "unrecognized GrantStmt.objtype: %d",
-                                (int) objtype);
        }
 
        return objects;
@@ -2458,14 +2384,6 @@ ExecGrant_Type_check(InternalGrant *istmt, HeapTuple 
tuple)
                                (errcode(ERRCODE_INVALID_GRANT_OPERATION),
                                 errmsg("cannot set privileges of multirange 
types"),
                                 errhint("Set the privileges of the range type 
instead.")));
-
-       /* Used GRANT DOMAIN on a non-domain? */
-       if (istmt->objtype == OBJECT_DOMAIN &&
-               pg_type_tuple->typtype != TYPTYPE_DOMAIN)
-               ereport(ERROR,
-                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                errmsg("\"%s\" is not a domain",
-                                               
NameStr(pg_type_tuple->typname))));
 }
 
 static void
diff --git a/src/test/isolation/expected/intra-grant-inplace.out 
b/src/test/isolation/expected/intra-grant-inplace.out
index 4e9695a0214..1aa9da622da 100644
--- a/src/test/isolation/expected/intra-grant-inplace.out
+++ b/src/test/isolation/expected/intra-grant-inplace.out
@@ -248,6 +248,6 @@ relhasindex
 -----------
 (0 rows)
 
-s4: WARNING:  got: cache lookup failed for relation REDACTED
+s4: WARNING:  got: relation "intra_grant_inplace" does not exist
 step revoke4: <... completed>
 step r3: ROLLBACK;
-- 
2.47.0

Reply via email to