diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 578e4c6592..bd147752ef 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -5445,7 +5445,10 @@ get_default_acl_internal(Oid roleId, Oid nsp_oid, char objtype) /* * Get default permissions for newly created object within given schema * - * Returns NULL if built-in system defaults should be used + * Returns NULL if built-in system defaults should be used. + * + * If the result is not NULL, caller must call recordDependencyOnNewAcl + * once the OID of the new object is known. */ Acl * get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid) @@ -5520,6 +5523,30 @@ get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid) return result; } +/* + * Record dependencies on roles mentioned in a new object's ACL. + */ +void +recordDependencyOnNewAcl(Oid classId, Oid objectId, int32 objsubId, + Oid ownerId, Acl *acl) +{ + int nmembers; + Oid *members; + + /* Nothing to do if ACL is defaulted */ + if (acl == NULL) + return; + + /* Extract roles mentioned in ACL */ + nmembers = aclmembers(acl, &members); + + /* Update the shared dependency ACL info */ + updateAclDependencies(classId, objectId, objsubId, + ownerId, + 0, NULL, + nmembers, members); +} + /* * Record initial privileges for the top-level object passed in. * diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 7203c86c4d..bd4c439ef3 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -1375,6 +1375,7 @@ heap_create_with_catalog(const char *relname, myself.classId = RelationRelationId; myself.objectId = relid; myself.objectSubId = 0; + referenced.classId = NamespaceRelationId; referenced.objectId = relnamespace; referenced.objectSubId = 0; @@ -1382,6 +1383,8 @@ heap_create_with_catalog(const char *relname, recordDependencyOnOwner(RelationRelationId, relid, ownerid); + recordDependencyOnNewAcl(RelationRelationId, relid, 0, ownerid, relacl); + recordDependencyOnCurrentExtension(&myself, false); if (reloftypeid) @@ -1391,18 +1394,6 @@ heap_create_with_catalog(const char *relname, referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } - - if (relacl != NULL) - { - int nnewmembers; - Oid *newmembers; - - nnewmembers = aclmembers(relacl, &newmembers); - updateAclDependencies(RelationRelationId, relid, 0, - ownerid, - 0, NULL, - nnewmembers, newmembers); - } } /* Post creation hook for new relation */ diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c index 2cf52be025..0538e31b3b 100644 --- a/src/backend/catalog/pg_namespace.c +++ b/src/backend/catalog/pg_namespace.c @@ -100,6 +100,9 @@ NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp) /* dependency on owner */ recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId); + /* dependences on roles mentioned in default ACL */ + recordDependencyOnNewAcl(NamespaceRelationId, nspoid, 0, ownerId, nspacl); + /* dependency on extension ... but not for magic temp schemas */ if (!isTemp) recordDependencyOnCurrentExtension(&myself, false); diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 0c817047cd..e367da7dba 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -654,17 +654,9 @@ ProcedureCreate(const char *procedureName, recordDependencyOnOwner(ProcedureRelationId, retval, proowner); /* dependency on any roles mentioned in ACL */ - if (!is_update && proacl != NULL) - { - int nnewmembers; - Oid *newmembers; - - nnewmembers = aclmembers(proacl, &newmembers); - updateAclDependencies(ProcedureRelationId, retval, 0, - proowner, - 0, NULL, - nnewmembers, newmembers); - } + if (!is_update) + recordDependencyOnNewAcl(ProcedureRelationId, retval, 0, + proowner, proacl); /* dependency on extension */ recordDependencyOnCurrentExtension(&myself, is_update); diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 2ddd46d48e..b729e7ec95 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -147,23 +147,13 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) * Create dependencies. We can/must skip this in bootstrap mode. */ if (!IsBootstrapProcessingMode()) - GenerateTypeDependencies(typeNamespace, - typoid, - InvalidOid, - 0, - ownerId, - F_SHELL_IN, - F_SHELL_OUT, - InvalidOid, - InvalidOid, - InvalidOid, - InvalidOid, - InvalidOid, - InvalidOid, - false, - InvalidOid, - InvalidOid, + GenerateTypeDependencies(typoid, + (Form_pg_type) GETSTRUCT(tup), NULL, + NULL, + 0, + false, + false, false); /* Post creation hook for new shell type */ @@ -225,14 +215,15 @@ TypeCreate(Oid newTypeOid, { Relation pg_type_desc; Oid typeObjectId; + bool isDependentType; bool rebuildDeps = false; + Acl *typacl; HeapTuple tup; bool nulls[Natts_pg_type]; bool replaces[Natts_pg_type]; Datum values[Natts_pg_type]; NameData name; int i; - Acl *typacl = NULL; ObjectAddress address; /* @@ -320,6 +311,17 @@ TypeCreate(Oid newTypeOid, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("fixed-size types must have storage PLAIN"))); + /* + * This is a dependent type if it's an implicitly-created array type, or + * if it's a relation rowtype that's not a composite type. For such types + * we'll leave the ACL empty, and we'll skip creating some dependency + * records because there will be a dependency already through the + * depended-on type or relation. (Caution: this is closely intertwined + * with some behavior in GenerateTypeDependencies.) + */ + isDependentType = isImplicitArray || + (OidIsValid(relationOid) && relationKind != RELKIND_COMPOSITE_TYPE); + /* * initialize arrays needed for heap_form_tuple or heap_modify_tuple */ @@ -379,8 +381,14 @@ TypeCreate(Oid newTypeOid, else nulls[Anum_pg_type_typdefault - 1] = true; - typacl = get_user_default_acl(OBJECT_TYPE, ownerId, - typeNamespace); + /* + * Initialize the type's ACL, too. But dependent types don't get one. + */ + if (isDependentType) + typacl = NULL; + else + typacl = get_user_default_acl(OBJECT_TYPE, ownerId, + typeNamespace); if (typacl != NULL) values[Anum_pg_type_typacl - 1] = PointerGetDatum(typacl); else @@ -462,25 +470,15 @@ TypeCreate(Oid newTypeOid, * Create dependencies. We can/must skip this in bootstrap mode. */ if (!IsBootstrapProcessingMode()) - GenerateTypeDependencies(typeNamespace, - typeObjectId, - relationOid, - relationKind, - ownerId, - inputProcedure, - outputProcedure, - receiveProcedure, - sendProcedure, - typmodinProcedure, - typmodoutProcedure, - analyzeProcedure, - elementType, - isImplicitArray, - baseType, - typeCollation, + GenerateTypeDependencies(typeObjectId, + (Form_pg_type) GETSTRUCT(tup), (defaultTypeBin ? stringToNode(defaultTypeBin) : NULL), + typacl, + relationKind, + isImplicitArray, + isDependentType, rebuildDeps); /* Post creation hook for new type */ @@ -499,6 +497,17 @@ TypeCreate(Oid newTypeOid, /* * GenerateTypeDependencies: build the dependencies needed for a type * + * Most of what this function needs to know about the type is passed as the + * new pg_type row, typeForm. But we can't get at the varlena fields through + * that, so defaultExpr and typacl are passed separately. (typacl is really + * "Acl *", but we declare it "void *" to avoid including acl.h in pg_type.h.) + * + * relationKind and isImplicitArray aren't visible in the pg_type row either, + * so they're also passed separately. + * + * isDependentType is true if this is an implicit array or relation rowtype; + * that means it doesn't need its own dependencies on owner etc. + * * If rebuild is true, we remove existing dependencies and rebuild them * from scratch. This is needed for ALTER TYPE, and also when replacing * a shell type. We don't remove an existing extension dependency, though. @@ -508,23 +517,13 @@ TypeCreate(Oid newTypeOid, * that type will become a member of the extension.) */ void -GenerateTypeDependencies(Oid typeNamespace, - Oid typeObjectId, - Oid relationOid, /* only for relation rowtypes */ - char relationKind, /* ditto */ - Oid owner, - Oid inputProcedure, - Oid outputProcedure, - Oid receiveProcedure, - Oid sendProcedure, - Oid typmodinProcedure, - Oid typmodoutProcedure, - Oid analyzeProcedure, - Oid elementType, - bool isImplicitArray, - Oid baseType, - Oid typeCollation, +GenerateTypeDependencies(Oid typeObjectId, + Form_pg_type typeForm, Node *defaultExpr, + void *typacl, + char relationKind, /* only for relation rowtypes */ + bool isImplicitArray, + bool isDependentType, bool rebuild) { ObjectAddress myself, @@ -542,79 +541,80 @@ GenerateTypeDependencies(Oid typeNamespace, myself.objectSubId = 0; /* - * Make dependencies on namespace, owner, extension. + * Make dependencies on namespace, owner, ACL, extension. * - * For a relation rowtype (that's not a composite type), we should skip - * these because we'll depend on them indirectly through the pg_class - * entry. Likewise, skip for implicit arrays since we'll depend on them - * through the element type. + * Skip these for a dependent type, since it will have such dependencies + * indirectly through its depended-on type or relation. */ - if ((!OidIsValid(relationOid) || relationKind == RELKIND_COMPOSITE_TYPE) && - !isImplicitArray) + if (!isDependentType) { referenced.classId = NamespaceRelationId; - referenced.objectId = typeNamespace; + referenced.objectId = typeForm->typnamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - recordDependencyOnOwner(TypeRelationId, typeObjectId, owner); + recordDependencyOnOwner(TypeRelationId, typeObjectId, + typeForm->typowner); + + recordDependencyOnNewAcl(TypeRelationId, typeObjectId, 0, + typeForm->typowner, typacl); recordDependencyOnCurrentExtension(&myself, rebuild); } /* Normal dependencies on the I/O functions */ - if (OidIsValid(inputProcedure)) + if (OidIsValid(typeForm->typinput)) { referenced.classId = ProcedureRelationId; - referenced.objectId = inputProcedure; + referenced.objectId = typeForm->typinput; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } - if (OidIsValid(outputProcedure)) + if (OidIsValid(typeForm->typoutput)) { referenced.classId = ProcedureRelationId; - referenced.objectId = outputProcedure; + referenced.objectId = typeForm->typoutput; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } - if (OidIsValid(receiveProcedure)) + if (OidIsValid(typeForm->typreceive)) { referenced.classId = ProcedureRelationId; - referenced.objectId = receiveProcedure; + referenced.objectId = typeForm->typreceive; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } - if (OidIsValid(sendProcedure)) + if (OidIsValid(typeForm->typsend)) { referenced.classId = ProcedureRelationId; - referenced.objectId = sendProcedure; + referenced.objectId = typeForm->typsend; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } - if (OidIsValid(typmodinProcedure)) + if (OidIsValid(typeForm->typmodin)) { referenced.classId = ProcedureRelationId; - referenced.objectId = typmodinProcedure; + referenced.objectId = typeForm->typmodin; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } - if (OidIsValid(typmodoutProcedure)) + if (OidIsValid(typeForm->typmodout)) { referenced.classId = ProcedureRelationId; - referenced.objectId = typmodoutProcedure; + referenced.objectId = typeForm->typmodout; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } - if (OidIsValid(analyzeProcedure)) + if (OidIsValid(typeForm->typanalyze)) { referenced.classId = ProcedureRelationId; - referenced.objectId = analyzeProcedure; + referenced.objectId = typeForm->typanalyze; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } @@ -628,10 +628,10 @@ GenerateTypeDependencies(Oid typeNamespace, * relation is, and not otherwise. And in the latter, of course we get the * opposite effect. */ - if (OidIsValid(relationOid)) + if (OidIsValid(typeForm->typrelid)) { referenced.classId = RelationRelationId; - referenced.objectId = relationOid; + referenced.objectId = typeForm->typrelid; referenced.objectSubId = 0; if (relationKind != RELKIND_COMPOSITE_TYPE) @@ -645,30 +645,31 @@ GenerateTypeDependencies(Oid typeNamespace, * dependent on the element type. Otherwise, if it has an element type, * the dependency is a normal one. */ - if (OidIsValid(elementType)) + if (OidIsValid(typeForm->typelem)) { referenced.classId = TypeRelationId; - referenced.objectId = elementType; + referenced.objectId = typeForm->typelem; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, isImplicitArray ? DEPENDENCY_INTERNAL : DEPENDENCY_NORMAL); } /* Normal dependency from a domain to its base type. */ - if (OidIsValid(baseType)) + if (OidIsValid(typeForm->typbasetype)) { referenced.classId = TypeRelationId; - referenced.objectId = baseType; + referenced.objectId = typeForm->typbasetype; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Normal dependency from a domain to its collation. */ /* We know the default collation is pinned, so don't bother recording it */ - if (OidIsValid(typeCollation) && typeCollation != DEFAULT_COLLATION_OID) + if (OidIsValid(typeForm->typcollation) && + typeForm->typcollation != DEFAULT_COLLATION_OID) { referenced.classId = CollationRelationId; - referenced.objectId = typeCollation; + referenced.objectId = typeForm->typcollation; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 12388004d9..66f7c57726 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -2179,6 +2179,9 @@ AlterDomainDefault(List *names, Node *defaultRaw) Relation rel; char *defaultValue; Node *defaultExpr = NULL; /* NULL if no default specified */ + Acl *typacl; + Datum aclDatum; + bool isNull; Datum new_record[Natts_pg_type]; bool new_record_nulls[Natts_pg_type]; bool new_record_repl[Natts_pg_type]; @@ -2270,25 +2273,23 @@ AlterDomainDefault(List *names, Node *defaultRaw) CatalogTupleUpdate(rel, &tup->t_self, newtuple); + /* Must extract ACL for use of GenerateTypeDependencies */ + aclDatum = heap_getattr(newtuple, Anum_pg_type_typacl, + RelationGetDescr(rel), &isNull); + if (isNull) + typacl = NULL; + else + typacl = DatumGetAclPCopy(aclDatum); + /* Rebuild dependencies */ - GenerateTypeDependencies(typTup->typnamespace, - domainoid, - InvalidOid, /* typrelid is n/a */ - 0, /* relation kind is n/a */ - typTup->typowner, - typTup->typinput, - typTup->typoutput, - typTup->typreceive, - typTup->typsend, - typTup->typmodin, - typTup->typmodout, - typTup->typanalyze, - InvalidOid, - false, /* a domain isn't an implicit array */ - typTup->typbasetype, - typTup->typcollation, + GenerateTypeDependencies(domainoid, + (Form_pg_type) GETSTRUCT(newtuple), defaultExpr, - true); /* Rebuild is true */ + typacl, + 0, /* relation kind is n/a */ + false, /* a domain isn't an implicit array */ + false, /* nor is it any kind of dependent type */ + true); /* We do need to rebuild dependencies */ InvokeObjectPostAlterHook(TypeRelationId, domainoid, 0); diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 76f3297e1d..87c9b673cf 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -323,23 +323,13 @@ extern ObjectAddress TypeCreate(Oid newTypeOid, bool typeNotNull, Oid typeCollation); -extern void GenerateTypeDependencies(Oid typeNamespace, - Oid typeObjectId, - Oid relationOid, - char relationKind, - Oid owner, - Oid inputProcedure, - Oid outputProcedure, - Oid receiveProcedure, - Oid sendProcedure, - Oid typmodinProcedure, - Oid typmodoutProcedure, - Oid analyzeProcedure, - Oid elementType, - bool isImplicitArray, - Oid baseType, - Oid typeCollation, +extern void GenerateTypeDependencies(Oid typeObjectId, + Form_pg_type typeForm, Node *defaultExpr, + void *typacl, + char relationKind, /* only for relation rowtypes */ + bool isImplicitArray, + bool isDependentType, bool rebuild); extern void RenameTypeInternal(Oid typeOid, const char *newTypeName, diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index f4d4be8d0d..72936eeb4d 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -189,6 +189,8 @@ typedef enum extern Acl *acldefault(ObjectType objtype, Oid ownerId); extern Acl *get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid); +extern void recordDependencyOnNewAcl(Oid classId, Oid objectId, int32 objsubId, + Oid ownerId, Acl *acl); extern Acl *aclupdate(const Acl *old_acl, const AclItem *mod_aip, int modechg, Oid ownerId, DropBehavior behavior); diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index d3925e75f5..3af92ed1a8 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -1571,6 +1571,12 @@ SELECT has_table_privilege('regress_priv_user1', 'testns.acltest1', 'INSERT'); - ALTER DEFAULT PRIVILEGES FOR ROLE regress_priv_user1 REVOKE EXECUTE ON FUNCTIONS FROM public; ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT USAGE ON SCHEMAS TO regress_priv_user2; -- error ERROR: cannot use IN SCHEMA clause when using GRANT/REVOKE ON SCHEMAS +-- +-- Testing blanket default grants is very hazardous since it might change +-- the privileges attached to objects created by concurrent regression tests. +-- To avoid that, be sure to revoke the privileges again before committing. +-- +BEGIN; ALTER DEFAULT PRIVILEGES GRANT USAGE ON SCHEMAS TO regress_priv_user2; CREATE SCHEMA testns2; SELECT has_schema_privilege('regress_priv_user2', 'testns2', 'USAGE'); -- yes @@ -1614,6 +1620,7 @@ SELECT has_schema_privilege('regress_priv_user2', 'testns4', 'CREATE'); -- yes (1 row) ALTER DEFAULT PRIVILEGES REVOKE ALL ON SCHEMAS FROM regress_priv_user2; +COMMIT; CREATE SCHEMA testns5; SELECT has_schema_privilege('regress_priv_user2', 'testns5', 'USAGE'); -- no has_schema_privilege diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql index 0d5c2cac24..e3e69302a2 100644 --- a/src/test/regress/sql/privileges.sql +++ b/src/test/regress/sql/privileges.sql @@ -935,6 +935,13 @@ ALTER DEFAULT PRIVILEGES FOR ROLE regress_priv_user1 REVOKE EXECUTE ON FUNCTIONS ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT USAGE ON SCHEMAS TO regress_priv_user2; -- error +-- +-- Testing blanket default grants is very hazardous since it might change +-- the privileges attached to objects created by concurrent regression tests. +-- To avoid that, be sure to revoke the privileges again before committing. +-- +BEGIN; + ALTER DEFAULT PRIVILEGES GRANT USAGE ON SCHEMAS TO regress_priv_user2; CREATE SCHEMA testns2; @@ -958,6 +965,8 @@ SELECT has_schema_privilege('regress_priv_user2', 'testns4', 'CREATE'); -- yes ALTER DEFAULT PRIVILEGES REVOKE ALL ON SCHEMAS FROM regress_priv_user2; +COMMIT; + CREATE SCHEMA testns5; SELECT has_schema_privilege('regress_priv_user2', 'testns5', 'USAGE'); -- no