Re: ALTER DEFAULT PRIVILEGES is buggy, and so is its testing - Mailing list pgsql-hackers
From | Tom Lane |
---|---|
Subject | Re: ALTER DEFAULT PRIVILEGES is buggy, and so is its testing |
Date | |
Msg-id | 16467.1541785540@sss.pgh.pa.us Whole thread Raw |
In response to | ALTER DEFAULT PRIVILEGES is buggy, and so is its testing (Tom Lane <tgl@sss.pgh.pa.us>) |
List | pgsql-hackers |
I wrote: > So it seems like testing ALTER DEFAULT PRIVILEGES in a script that > runs in parallel with anything else is a damfool idea. But there > is also a live bug of failure to correctly record privileges granted > this way. Without that bug, we'd have had our noses rubbed in the > parallel race conditions long ago. It turns out the bug only applies to schemas and types; the other callers of get_user_default_acl() already had ad-hoc code to deal with the problem. I made that a bit less ad-hoc by creating a subroutine to do the work and documenting the need to call it. To fix the tests' race condition, it seems to be sufficient to wrap a transaction around the section of privileges.sql that grants and then revokes global default privileges. The other uses of ALTER DEFAULT PRIVILEGES in the tests are targeted narrowly enough to ensure that concurrent tests shouldn't pick them up. In the attached proposed patch for HEAD, since I had to adjust the API of GenerateTypeDependencies anyway, I changed it to pass the new pg_type row and get most of the old arguments out of that, as the argument list was getting pretty unwieldy. I suppose we could make a back-branch patch that doesn't change that API and instead makes the callers responsible for recording ACL dependencies, but ugh. Does anyone think it's likely that third-party code is calling GenerateTypeDependencies? regards, tom lane diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 578e4c6..bd14775 100644 *** a/src/backend/catalog/aclchk.c --- b/src/backend/catalog/aclchk.c *************** get_default_acl_internal(Oid roleId, Oid *** 5445,5451 **** /* * Get default permissions for newly created object within given schema * ! * Returns NULL if built-in system defaults should be used */ Acl * get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid) --- 5445,5454 ---- /* * Get default permissions for newly created object within given schema * ! * 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) *************** get_user_default_acl(ObjectType objtype, *** 5521,5526 **** --- 5524,5553 ---- } /* + * 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. * * For the object passed in, this will record its ACL (if any) and the ACLs of diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 7203c86..bd4c439 100644 *** a/src/backend/catalog/heap.c --- b/src/backend/catalog/heap.c *************** heap_create_with_catalog(const char *rel *** 1375,1380 **** --- 1375,1381 ---- myself.classId = RelationRelationId; myself.objectId = relid; myself.objectSubId = 0; + referenced.classId = NamespaceRelationId; referenced.objectId = relnamespace; referenced.objectSubId = 0; *************** heap_create_with_catalog(const char *rel *** 1382,1387 **** --- 1383,1390 ---- recordDependencyOnOwner(RelationRelationId, relid, ownerid); + recordDependencyOnNewAcl(RelationRelationId, relid, 0, ownerid, relacl); + recordDependencyOnCurrentExtension(&myself, false); if (reloftypeid) *************** heap_create_with_catalog(const char *rel *** 1391,1408 **** 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 */ --- 1394,1399 ---- diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c index 2cf52be..0538e31 100644 *** a/src/backend/catalog/pg_namespace.c --- b/src/backend/catalog/pg_namespace.c *************** NamespaceCreate(const char *nspName, Oid *** 100,105 **** --- 100,108 ---- /* 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 0c81704..e367da7 100644 *** a/src/backend/catalog/pg_proc.c --- b/src/backend/catalog/pg_proc.c *************** ProcedureCreate(const char *procedureNam *** 654,670 **** 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); ! } /* dependency on extension */ recordDependencyOnCurrentExtension(&myself, is_update); --- 654,662 ---- recordDependencyOnOwner(ProcedureRelationId, retval, proowner); /* dependency on any roles mentioned in ACL */ ! 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 2ddd46d..0b82b18 100644 *** a/src/backend/catalog/pg_type.c --- b/src/backend/catalog/pg_type.c *************** TypeShellMake(const char *typeName, Oid *** 147,169 **** * 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, - NULL, false); /* Post creation hook for new shell type */ --- 147,158 ---- * Create dependencies. We can/must skip this in bootstrap mode. */ if (!IsBootstrapProcessingMode()) ! GenerateTypeDependencies(typoid, ! (Form_pg_type) GETSTRUCT(tup), ! NULL, ! NULL, 0, false, false); /* Post creation hook for new shell type */ *************** TypeCreate(Oid newTypeOid, *** 379,384 **** --- 368,376 ---- else nulls[Anum_pg_type_typdefault - 1] = true; + /* + * initialize the type's ACL, too. + */ typacl = get_user_default_acl(OBJECT_TYPE, ownerId, typeNamespace); if (typacl != NULL) *************** TypeCreate(Oid newTypeOid, *** 462,486 **** * 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, (defaultTypeBin ? stringToNode(defaultTypeBin) : NULL), rebuildDeps); /* Post creation hook for new type */ --- 454,467 ---- * Create dependencies. We can/must skip this in bootstrap mode. */ if (!IsBootstrapProcessingMode()) ! GenerateTypeDependencies(typeObjectId, ! (Form_pg_type) GETSTRUCT(tup), (defaultTypeBin ? stringToNode(defaultTypeBin) : NULL), + typacl, + relationKind, + isImplicitArray, rebuildDeps); /* Post creation hook for new type */ *************** TypeCreate(Oid newTypeOid, *** 499,504 **** --- 480,493 ---- /* * 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. + * * 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. *************** TypeCreate(Oid newTypeOid, *** 508,530 **** * 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, Node *defaultExpr, bool rebuild) { ObjectAddress myself, --- 497,508 ---- * that type will become a member of the extension.) */ void ! GenerateTypeDependencies(Oid typeObjectId, ! Form_pg_type typeForm, Node *defaultExpr, + void *typacl, + char relationKind, /* only for relation rowtypes */ + bool isImplicitArray, bool rebuild) { ObjectAddress myself, *************** GenerateTypeDependencies(Oid typeNamespa *** 542,620 **** myself.objectSubId = 0; /* ! * Make dependencies on namespace, owner, 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. */ ! if ((!OidIsValid(relationOid) || relationKind == RELKIND_COMPOSITE_TYPE) && !isImplicitArray) { referenced.classId = NamespaceRelationId; ! referenced.objectId = typeNamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); ! recordDependencyOnOwner(TypeRelationId, typeObjectId, owner); recordDependencyOnCurrentExtension(&myself, rebuild); } /* Normal dependencies on the I/O functions */ ! if (OidIsValid(inputProcedure)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = inputProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } ! if (OidIsValid(outputProcedure)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = outputProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } ! if (OidIsValid(receiveProcedure)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = receiveProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } ! if (OidIsValid(sendProcedure)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = sendProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } ! if (OidIsValid(typmodinProcedure)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = typmodinProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } ! if (OidIsValid(typmodoutProcedure)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = typmodoutProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } ! if (OidIsValid(analyzeProcedure)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = analyzeProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } --- 520,603 ---- myself.objectSubId = 0; /* ! * 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. */ ! if ((!OidIsValid(typeForm->typrelid) || ! relationKind == RELKIND_COMPOSITE_TYPE) && !isImplicitArray) { referenced.classId = NamespaceRelationId; ! referenced.objectId = typeForm->typnamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); ! recordDependencyOnOwner(TypeRelationId, typeObjectId, ! typeForm->typowner); ! ! recordDependencyOnNewAcl(TypeRelationId, typeObjectId, 0, ! typeForm->typowner, typacl); recordDependencyOnCurrentExtension(&myself, rebuild); } /* Normal dependencies on the I/O functions */ ! if (OidIsValid(typeForm->typinput)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = typeForm->typinput; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } ! if (OidIsValid(typeForm->typoutput)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = typeForm->typoutput; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } ! if (OidIsValid(typeForm->typreceive)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = typeForm->typreceive; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } ! if (OidIsValid(typeForm->typsend)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = typeForm->typsend; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } ! if (OidIsValid(typeForm->typmodin)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = typeForm->typmodin; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } ! if (OidIsValid(typeForm->typmodout)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = typeForm->typmodout; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } ! if (OidIsValid(typeForm->typanalyze)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = typeForm->typanalyze; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } *************** GenerateTypeDependencies(Oid typeNamespa *** 628,637 **** * relation is, and not otherwise. And in the latter, of course we get the * opposite effect. */ ! if (OidIsValid(relationOid)) { referenced.classId = RelationRelationId; ! referenced.objectId = relationOid; referenced.objectSubId = 0; if (relationKind != RELKIND_COMPOSITE_TYPE) --- 611,620 ---- * relation is, and not otherwise. And in the latter, of course we get the * opposite effect. */ ! if (OidIsValid(typeForm->typrelid)) { referenced.classId = RelationRelationId; ! referenced.objectId = typeForm->typrelid; referenced.objectSubId = 0; if (relationKind != RELKIND_COMPOSITE_TYPE) *************** GenerateTypeDependencies(Oid typeNamespa *** 645,674 **** * dependent on the element type. Otherwise, if it has an element type, * the dependency is a normal one. */ ! if (OidIsValid(elementType)) { referenced.classId = TypeRelationId; ! referenced.objectId = elementType; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, isImplicitArray ? DEPENDENCY_INTERNAL : DEPENDENCY_NORMAL); } /* Normal dependency from a domain to its base type. */ ! if (OidIsValid(baseType)) { referenced.classId = TypeRelationId; ! referenced.objectId = baseType; 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) { referenced.classId = CollationRelationId; ! referenced.objectId = typeCollation; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } --- 628,658 ---- * dependent on the element type. Otherwise, if it has an element type, * the dependency is a normal one. */ ! if (OidIsValid(typeForm->typelem)) { referenced.classId = TypeRelationId; ! 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(typeForm->typbasetype)) { referenced.classId = TypeRelationId; ! 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(typeForm->typcollation) && ! typeForm->typcollation != DEFAULT_COLLATION_OID) { referenced.classId = CollationRelationId; ! 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 1238800..74dd534 100644 *** a/src/backend/commands/typecmds.c --- b/src/backend/commands/typecmds.c *************** AlterDomainDefault(List *names, Node *de *** 2179,2184 **** --- 2179,2187 ---- 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]; *************** AlterDomainDefault(List *names, Node *de *** 2270,2293 **** CatalogTupleUpdate(rel, &tup->t_self, newtuple); /* 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, - defaultExpr, true); /* Rebuild is true */ InvokeObjectPostAlterHook(TypeRelationId, domainoid, 0); --- 2273,2293 ---- 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(domainoid, ! (Form_pg_type) GETSTRUCT(newtuple), ! defaultExpr, ! typacl, 0, /* relation kind is n/a */ false, /* a domain isn't an implicit array */ true); /* Rebuild is true */ InvokeObjectPostAlterHook(TypeRelationId, domainoid, 0); diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 76f3297..57595a6 100644 *** a/src/include/catalog/pg_type.h --- b/src/include/catalog/pg_type.h *************** extern ObjectAddress TypeCreate(Oid newT *** 323,345 **** 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, - Node *defaultExpr, bool rebuild); extern void RenameTypeInternal(Oid typeOid, const char *newTypeName, --- 323,334 ---- bool typeNotNull, Oid typeCollation); ! extern void GenerateTypeDependencies(Oid typeObjectId, ! Form_pg_type typeForm, ! Node *defaultExpr, ! void *typacl, char relationKind, bool isImplicitArray, 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 f4d4be8..72936ee 100644 *** a/src/include/utils/acl.h --- b/src/include/utils/acl.h *************** typedef enum *** 189,194 **** --- 189,196 ---- 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 d3925e7..3af92ed 100644 *** a/src/test/regress/expected/privileges.out --- b/src/test/regress/expected/privileges.out *************** SELECT has_table_privilege('regress_priv *** 1571,1576 **** --- 1571,1582 ---- 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 *************** SELECT has_schema_privilege('regress_pri *** 1614,1619 **** --- 1620,1626 ---- (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 0d5c2ca..e3e6930 100644 *** a/src/test/regress/sql/privileges.sql --- b/src/test/regress/sql/privileges.sql *************** ALTER DEFAULT PRIVILEGES FOR ROLE regres *** 935,940 **** --- 935,947 ---- 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; *************** SELECT has_schema_privilege('regress_pri *** 958,963 **** --- 965,972 ---- 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
pgsql-hackers by date: