aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPeter Eisentraut <peter@eisentraut.org>2024-11-15 10:53:54 +0100
committerPeter Eisentraut <peter@eisentraut.org>2024-11-15 11:03:48 +0100
commitd31bbfb6590e586f731345960311861d5eb4c23f (patch)
treedaf707be4a1b020d8a537047e0968dbac83ae8e1 /src
parentcfd7f36c83cdcf6322c7ea0d1d9ef62bc2b00375 (diff)
downloadpostgresql-d31bbfb6590e586f731345960311861d5eb4c23f.tar.gz
postgresql-d31bbfb6590e586f731345960311861d5eb4c23f.zip
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. Reviewed-by: Bertrand Drouvot <bertranddrouvot.pg@gmail.com> Discussion: https://www.postgresql.org/message-id/flat/bf72b82c-124d-4efa-a484-bb928e9494e4@eisentraut.org
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/aclchk.c157
-rw-r--r--src/test/isolation/expected/intra-grant-inplace.out2
2 files changed, 39 insertions, 120 deletions
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 95eb0b12277..aaf96a965e4 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -659,147 +659,77 @@ 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)
{
- case OBJECT_TABLE:
- case OBJECT_SEQUENCE:
- foreach(cell, objnames)
- {
- RangeVar *relvar = (RangeVar *) lfirst(cell);
- Oid relOid;
-
- relOid = RangeVarGetRelid(relvar, NoLock, 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:
- foreach(cell, objnames)
- {
- List *typname = (List *) lfirst(cell);
- Oid oid;
-
- oid = typenameTypeId(NULL, makeTypeNameFromNameList(typname));
- objects = lappend_oid(objects, oid);
- }
- break;
- case OBJECT_FUNCTION:
- foreach(cell, objnames)
- {
- ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
- Oid funcid;
+ default:
- funcid = LookupFuncWithArgs(OBJECT_FUNCTION, func, false);
- objects = lappend_oid(objects, funcid);
- }
- break;
- case OBJECT_LANGUAGE:
+ /*
+ * For most object types, we use get_object_address() directly.
+ */
foreach(cell, objnames)
{
- char *langname = strVal(lfirst(cell));
- Oid oid;
+ ObjectAddress address;
- oid = get_language_oid(langname, false);
- objects = lappend_oid(objects, oid);
+ address = get_object_address(objtype, lfirst(cell), NULL, lockmode, false);
+ objects = lappend_oid(objects, address.objectId);
}
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;
+ case OBJECT_TABLE:
+ case OBJECT_SEQUENCE:
- oid = get_namespace_oid(nspname, false);
- objects = lappend_oid(objects, oid);
- }
- break;
- case OBJECT_PROCEDURE:
+ /*
+ * 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)
{
- ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
- Oid procid;
+ RangeVar *relvar = (RangeVar *) lfirst(cell);
+ Oid relOid;
- procid = LookupFuncWithArgs(OBJECT_PROCEDURE, func, false);
- objects = lappend_oid(objects, procid);
+ relOid = RangeVarGetRelid(relvar, lockmode, false);
+ objects = lappend_oid(objects, relOid);
}
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;
+ case OBJECT_DOMAIN:
+ case OBJECT_TYPE:
- spcoid = get_tablespace_oid(spcname, false);
- objects = lappend_oid(objects, spcoid);
- }
- break;
- case OBJECT_FDW:
+ /*
+ * 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)
{
- char *fdwname = strVal(lfirst(cell));
- Oid fdwid = get_foreign_data_wrapper_oid(fdwname, false);
+ List *typname = (List *) lfirst(cell);
+ TypeName *tn = makeTypeNameFromNameList(typname);
+ ObjectAddress address;
+ Relation relation;
- objects = lappend_oid(objects, fdwid);
+ address = get_object_address(objtype, (Node *) tn, &relation, lockmode, false);
+ Assert(relation == NULL);
+ objects = lappend_oid(objects, address.objectId);
}
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);
- }
- break;
case OBJECT_PARAMETER_ACL:
+
+ /*
+ * Parameters are handled completely differently.
+ */
foreach(cell, objnames)
{
/*
@@ -830,9 +760,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 +2385,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;