Altering default privileges on schemas

Extend ALTER DEFAULT PRIVILEGES command to schemas.

Author: Matheus Oliveira
Reviewed-by: Petr Jelínek, Ashutosh Sharma

https://commitfest.postgresql.org/13/887/
This commit is contained in:
Teodor Sigaev 2017-03-28 18:58:55 +03:00
parent 85163641f8
commit ab89e465cb
13 changed files with 176 additions and 12 deletions

View File

@ -46,6 +46,10 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
ON TYPES ON TYPES
TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ] TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
GRANT { USAGE | CREATE | ALL [ PRIVILEGES ] }
ON SCHEMAS
TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
REVOKE [ GRANT OPTION FOR ] REVOKE [ GRANT OPTION FOR ]
{ { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER } { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
[, ...] | ALL [ PRIVILEGES ] } [, ...] | ALL [ PRIVILEGES ] }
@ -71,6 +75,12 @@ REVOKE [ GRANT OPTION FOR ]
ON TYPES ON TYPES
FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
[ CASCADE | RESTRICT ] [ CASCADE | RESTRICT ]
REVOKE [ GRANT OPTION FOR ]
{ USAGE | CREATE | ALL [ PRIVILEGES ] }
ON SCHEMAS
FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
@ -81,8 +91,9 @@ REVOKE [ GRANT OPTION FOR ]
<command>ALTER DEFAULT PRIVILEGES</> allows you to set the privileges <command>ALTER DEFAULT PRIVILEGES</> allows you to set the privileges
that will be applied to objects created in the future. (It does not that will be applied to objects created in the future. (It does not
affect privileges assigned to already-existing objects.) Currently, affect privileges assigned to already-existing objects.) Currently,
only the privileges for tables (including views and foreign tables), only the privileges for schemas, tables (including views and foreign
sequences, functions, and types (including domains) can be altered. tables), sequences, functions, and types (including domains) can be
altered.
</para> </para>
<para> <para>
@ -125,6 +136,8 @@ REVOKE [ GRANT OPTION FOR ]
are altered for objects later created in that schema. are altered for objects later created in that schema.
If <literal>IN SCHEMA</> is omitted, the global default privileges If <literal>IN SCHEMA</> is omitted, the global default privileges
are altered. are altered.
<literal>IN SCHEMA</> is not allowed when using <literal>ON SCHEMAS</>
as schemas can't be nested.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -959,6 +959,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_TYPE; all_privileges = ACL_ALL_RIGHTS_TYPE;
errormsg = gettext_noop("invalid privilege type %s for type"); errormsg = gettext_noop("invalid privilege type %s for type");
break; break;
case ACL_OBJECT_NAMESPACE:
all_privileges = ACL_ALL_RIGHTS_NAMESPACE;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
default: default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d", elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype); (int) action->objtype);
@ -1146,6 +1150,16 @@ SetDefaultACL(InternalDefaultACL *iacls)
this_privileges = ACL_ALL_RIGHTS_TYPE; this_privileges = ACL_ALL_RIGHTS_TYPE;
break; break;
case ACL_OBJECT_NAMESPACE:
if (OidIsValid(iacls->nspid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
errmsg("cannot use IN SCHEMA clause when using GRANT/REVOKE ON SCHEMAS")));
objtype = DEFACLOBJ_NAMESPACE;
if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
this_privileges = ACL_ALL_RIGHTS_NAMESPACE;
break;
default: default:
elog(ERROR, "unrecognized objtype: %d", elog(ERROR, "unrecognized objtype: %d",
(int) iacls->objtype); (int) iacls->objtype);
@ -1369,6 +1383,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case DEFACLOBJ_TYPE: case DEFACLOBJ_TYPE:
iacls.objtype = ACL_OBJECT_TYPE; iacls.objtype = ACL_OBJECT_TYPE;
break; break;
case DEFACLOBJ_NAMESPACE:
iacls.objtype = ACL_OBJECT_NAMESPACE;
break;
default: default:
/* Shouldn't get here */ /* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d", elog(ERROR, "unexpected default ACL type: %d",
@ -5259,6 +5276,10 @@ get_user_default_acl(GrantObjectType objtype, Oid ownerId, Oid nsp_oid)
defaclobjtype = DEFACLOBJ_TYPE; defaclobjtype = DEFACLOBJ_TYPE;
break; break;
case ACL_OBJECT_NAMESPACE:
defaclobjtype = DEFACLOBJ_NAMESPACE;
break;
default: default:
return NULL; return NULL;
} }

View File

@ -1843,11 +1843,14 @@ get_object_address_defacl(List *object, bool missing_ok)
case DEFACLOBJ_TYPE: case DEFACLOBJ_TYPE:
objtype_str = "types"; objtype_str = "types";
break; break;
case DEFACLOBJ_NAMESPACE:
objtype_str = "schemas";
break;
default: default:
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized default ACL object type %c", objtype), errmsg("unrecognized default ACL object type %c", objtype),
errhint("Valid object types are \"r\", \"S\", \"f\", and \"T\"."))); errhint("Valid object types are \"r\", \"S\", \"f\", \"T\" and \"s\".")));
} }
/* /*
@ -3255,6 +3258,11 @@ getObjectDescription(const ObjectAddress *object)
_("default privileges on new types belonging to role %s"), _("default privileges on new types belonging to role %s"),
GetUserNameFromId(defacl->defaclrole, false)); GetUserNameFromId(defacl->defaclrole, false));
break; break;
case DEFACLOBJ_NAMESPACE:
appendStringInfo(&buffer,
_("default privileges on new schemas belonging to role %s"),
GetUserNameFromId(defacl->defaclrole, false));
break;
default: default:
/* shouldn't get here */ /* shouldn't get here */
appendStringInfo(&buffer, appendStringInfo(&buffer,
@ -4762,6 +4770,10 @@ getObjectIdentityParts(const ObjectAddress *object,
appendStringInfoString(&buffer, appendStringInfoString(&buffer,
" on types"); " on types");
break; break;
case DEFACLOBJ_NAMESPACE:
appendStringInfoString(&buffer,
" on schemas");
break;
} }
if (objname) if (objname)

View File

@ -31,10 +31,11 @@
* Create a namespace (schema) with the given name and owner OID. * Create a namespace (schema) with the given name and owner OID.
* *
* If isTemp is true, this schema is a per-backend schema for holding * If isTemp is true, this schema is a per-backend schema for holding
* temporary tables. Currently, the only effect of that is to prevent it * temporary tables. Currently, it is used to prevent it from being
* from being linked as a member of any active extension. (If someone * linked as a member of any active extension. (If someone does CREATE
* does CREATE TEMP TABLE in an extension script, we don't want the temp * TEMP TABLE in an extension script, we don't want the temp schema to
* schema to become part of the extension.) * become part of the extension). And to avoid checking for default ACL
* for temp namespace (as it is not necessary).
* --------------- * ---------------
*/ */
Oid Oid
@ -49,6 +50,7 @@ NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp)
TupleDesc tupDesc; TupleDesc tupDesc;
ObjectAddress myself; ObjectAddress myself;
int i; int i;
Acl *nspacl;
/* sanity checks */ /* sanity checks */
if (!nspName) if (!nspName)
@ -60,6 +62,12 @@ NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp)
(errcode(ERRCODE_DUPLICATE_SCHEMA), (errcode(ERRCODE_DUPLICATE_SCHEMA),
errmsg("schema \"%s\" already exists", nspName))); errmsg("schema \"%s\" already exists", nspName)));
if (!isTemp)
nspacl = get_user_default_acl(ACL_OBJECT_NAMESPACE, ownerId,
InvalidOid);
else
nspacl = NULL;
/* initialize nulls and values */ /* initialize nulls and values */
for (i = 0; i < Natts_pg_namespace; i++) for (i = 0; i < Natts_pg_namespace; i++)
{ {
@ -69,7 +77,10 @@ NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp)
namestrcpy(&nname, nspName); namestrcpy(&nname, nspName);
values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&nname); values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&nname);
values[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(ownerId); values[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(ownerId);
nulls[Anum_pg_namespace_nspacl - 1] = true; if (nspacl != NULL)
values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(nspacl);
else
nulls[Anum_pg_namespace_nspacl - 1] = true;
nspdesc = heap_open(NamespaceRelationId, RowExclusiveLock); nspdesc = heap_open(NamespaceRelationId, RowExclusiveLock);
tupDesc = nspdesc->rd_att; tupDesc = nspdesc->rd_att;

View File

@ -668,7 +668,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
ROW ROWS RULE ROW ROWS RULE
SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES SAVEPOINT SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
SIMILAR SIMPLE SKIP SLOT SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P SIMILAR SIMPLE SKIP SLOT SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P
START STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P START STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P
@ -7035,6 +7035,7 @@ defacl_privilege_target:
| FUNCTIONS { $$ = ACL_OBJECT_FUNCTION; } | FUNCTIONS { $$ = ACL_OBJECT_FUNCTION; }
| SEQUENCES { $$ = ACL_OBJECT_SEQUENCE; } | SEQUENCES { $$ = ACL_OBJECT_SEQUENCE; }
| TYPES_P { $$ = ACL_OBJECT_TYPE; } | TYPES_P { $$ = ACL_OBJECT_TYPE; }
| SCHEMAS { $$ = ACL_OBJECT_NAMESPACE; }
; ;
@ -14713,6 +14714,7 @@ unreserved_keyword:
| RULE | RULE
| SAVEPOINT | SAVEPOINT
| SCHEMA | SCHEMA
| SCHEMAS
| SCROLL | SCROLL
| SEARCH | SEARCH
| SECOND_P | SECOND_P

View File

@ -520,7 +520,9 @@ do { \
CONVERT_PRIV('X', "EXECUTE"); CONVERT_PRIV('X', "EXECUTE");
else if (strcmp(type, "LANGUAGE") == 0) else if (strcmp(type, "LANGUAGE") == 0)
CONVERT_PRIV('U', "USAGE"); CONVERT_PRIV('U', "USAGE");
else if (strcmp(type, "SCHEMA") == 0) else if (strcmp(type, "SCHEMA") == 0 ||
strcmp(type, "SCHEMAS") == 0
)
{ {
CONVERT_PRIV('C', "CREATE"); CONVERT_PRIV('C', "CREATE");
CONVERT_PRIV('U', "USAGE"); CONVERT_PRIV('U', "USAGE");

View File

@ -14295,6 +14295,9 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo)
case DEFACLOBJ_TYPE: case DEFACLOBJ_TYPE:
type = "TYPES"; type = "TYPES";
break; break;
case DEFACLOBJ_NAMESPACE:
type = "SCHEMAS";
break;
default: default:
/* shouldn't get here */ /* shouldn't get here */
exit_horribly(NULL, exit_horribly(NULL,

View File

@ -1028,7 +1028,7 @@ listDefaultACLs(const char *pattern)
printfPQExpBuffer(&buf, printfPQExpBuffer(&buf,
"SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS \"%s\",\n" "SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS \"%s\",\n"
" n.nspname AS \"%s\",\n" " n.nspname AS \"%s\",\n"
" CASE d.defaclobjtype WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' END AS \"%s\",\n" " CASE d.defaclobjtype WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' END AS \"%s\",\n"
" ", " ",
gettext_noop("Owner"), gettext_noop("Owner"),
gettext_noop("Schema"), gettext_noop("Schema"),
@ -1040,6 +1040,8 @@ listDefaultACLs(const char *pattern)
gettext_noop("function"), gettext_noop("function"),
DEFACLOBJ_TYPE, DEFACLOBJ_TYPE,
gettext_noop("type"), gettext_noop("type"),
DEFACLOBJ_NAMESPACE,
gettext_noop("schema"),
gettext_noop("Type")); gettext_noop("Type"));
printACLColumn(&buf, "d.defaclacl"); printACLColumn(&buf, "d.defaclacl");

View File

@ -2796,7 +2796,7 @@ psql_completion(const char *text, int start, int end)
* to the kinds of objects supported. * to the kinds of objects supported.
*/ */
if (HeadMatches3("ALTER","DEFAULT","PRIVILEGES")) if (HeadMatches3("ALTER","DEFAULT","PRIVILEGES"))
COMPLETE_WITH_LIST4("TABLES", "SEQUENCES", "FUNCTIONS", "TYPES"); COMPLETE_WITH_LIST5("TABLES", "SEQUENCES", "FUNCTIONS", "TYPES", "SCHEMAS");
else else
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf,
" UNION SELECT 'ALL FUNCTIONS IN SCHEMA'" " UNION SELECT 'ALL FUNCTIONS IN SCHEMA'"

View File

@ -70,5 +70,6 @@ typedef FormData_pg_default_acl *Form_pg_default_acl;
#define DEFACLOBJ_SEQUENCE 'S' /* sequence */ #define DEFACLOBJ_SEQUENCE 'S' /* sequence */
#define DEFACLOBJ_FUNCTION 'f' /* function */ #define DEFACLOBJ_FUNCTION 'f' /* function */
#define DEFACLOBJ_TYPE 'T' /* type */ #define DEFACLOBJ_TYPE 'T' /* type */
#define DEFACLOBJ_NAMESPACE 'n' /* namespace */
#endif /* PG_DEFAULT_ACL_H */ #endif /* PG_DEFAULT_ACL_H */

View File

@ -344,6 +344,7 @@ PG_KEYWORD("rows", ROWS, UNRESERVED_KEYWORD)
PG_KEYWORD("rule", RULE, UNRESERVED_KEYWORD) PG_KEYWORD("rule", RULE, UNRESERVED_KEYWORD)
PG_KEYWORD("savepoint", SAVEPOINT, UNRESERVED_KEYWORD) PG_KEYWORD("savepoint", SAVEPOINT, UNRESERVED_KEYWORD)
PG_KEYWORD("schema", SCHEMA, UNRESERVED_KEYWORD) PG_KEYWORD("schema", SCHEMA, UNRESERVED_KEYWORD)
PG_KEYWORD("schemas", SCHEMAS, UNRESERVED_KEYWORD)
PG_KEYWORD("scroll", SCROLL, UNRESERVED_KEYWORD) PG_KEYWORD("scroll", SCROLL, UNRESERVED_KEYWORD)
PG_KEYWORD("search", SEARCH, UNRESERVED_KEYWORD) PG_KEYWORD("search", SEARCH, UNRESERVED_KEYWORD)
PG_KEYWORD("second", SECOND_P, UNRESERVED_KEYWORD) PG_KEYWORD("second", SECOND_P, UNRESERVED_KEYWORD)

View File

@ -1356,6 +1356,64 @@ SELECT has_table_privilege('regress_user1', 'testns.acltest1', 'INSERT'); -- no
(1 row) (1 row)
ALTER DEFAULT PRIVILEGES FOR ROLE regress_user1 REVOKE EXECUTE ON FUNCTIONS FROM public; ALTER DEFAULT PRIVILEGES FOR ROLE regress_user1 REVOKE EXECUTE ON FUNCTIONS FROM public;
ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT USAGE ON SCHEMAS TO regress_user2; -- error
ERROR: cannot use IN SCHEMA clause when using GRANT/REVOKE ON SCHEMAS
ALTER DEFAULT PRIVILEGES GRANT USAGE ON SCHEMAS TO regress_user2;
CREATE SCHEMA testns2;
SELECT has_schema_privilege('regress_user2', 'testns2', 'USAGE'); -- yes
has_schema_privilege
----------------------
t
(1 row)
SELECT has_schema_privilege('regress_user2', 'testns2', 'CREATE'); -- no
has_schema_privilege
----------------------
f
(1 row)
ALTER DEFAULT PRIVILEGES REVOKE USAGE ON SCHEMAS FROM regress_user2;
CREATE SCHEMA testns3;
SELECT has_schema_privilege('regress_user2', 'testns3', 'USAGE'); -- no
has_schema_privilege
----------------------
f
(1 row)
SELECT has_schema_privilege('regress_user2', 'testns3', 'CREATE'); -- no
has_schema_privilege
----------------------
f
(1 row)
ALTER DEFAULT PRIVILEGES GRANT ALL ON SCHEMAS TO regress_user2;
CREATE SCHEMA testns4;
SELECT has_schema_privilege('regress_user2', 'testns4', 'USAGE'); -- yes
has_schema_privilege
----------------------
t
(1 row)
SELECT has_schema_privilege('regress_user2', 'testns4', 'CREATE'); -- yes
has_schema_privilege
----------------------
t
(1 row)
ALTER DEFAULT PRIVILEGES REVOKE ALL ON SCHEMAS FROM regress_user2;
CREATE SCHEMA testns5;
SELECT has_schema_privilege('regress_user2', 'testns5', 'USAGE'); -- no
has_schema_privilege
----------------------
f
(1 row)
SELECT has_schema_privilege('regress_user2', 'testns5', 'CREATE'); -- no
has_schema_privilege
----------------------
f
(1 row)
SET ROLE regress_user1; SET ROLE regress_user1;
CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql; CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql;
SELECT has_function_privilege('regress_user2', 'testns.foo()', 'EXECUTE'); -- no SELECT has_function_privilege('regress_user2', 'testns.foo()', 'EXECUTE'); -- no
@ -1403,6 +1461,10 @@ SELECT count(*)
DROP SCHEMA testns CASCADE; DROP SCHEMA testns CASCADE;
NOTICE: drop cascades to table testns.acltest1 NOTICE: drop cascades to table testns.acltest1
DROP SCHEMA testns2 CASCADE;
DROP SCHEMA testns3 CASCADE;
DROP SCHEMA testns4 CASCADE;
DROP SCHEMA testns5 CASCADE;
SELECT d.* -- check that entries went away SELECT d.* -- check that entries went away
FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid
WHERE nspname IS NULL AND defaclnamespace != 0; WHERE nspname IS NULL AND defaclnamespace != 0;

View File

@ -816,6 +816,36 @@ SELECT has_table_privilege('regress_user1', 'testns.acltest1', 'INSERT'); -- no
ALTER DEFAULT PRIVILEGES FOR ROLE regress_user1 REVOKE EXECUTE ON FUNCTIONS FROM public; ALTER DEFAULT PRIVILEGES FOR ROLE regress_user1 REVOKE EXECUTE ON FUNCTIONS FROM public;
ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT USAGE ON SCHEMAS TO regress_user2; -- error
ALTER DEFAULT PRIVILEGES GRANT USAGE ON SCHEMAS TO regress_user2;
CREATE SCHEMA testns2;
SELECT has_schema_privilege('regress_user2', 'testns2', 'USAGE'); -- yes
SELECT has_schema_privilege('regress_user2', 'testns2', 'CREATE'); -- no
ALTER DEFAULT PRIVILEGES REVOKE USAGE ON SCHEMAS FROM regress_user2;
CREATE SCHEMA testns3;
SELECT has_schema_privilege('regress_user2', 'testns3', 'USAGE'); -- no
SELECT has_schema_privilege('regress_user2', 'testns3', 'CREATE'); -- no
ALTER DEFAULT PRIVILEGES GRANT ALL ON SCHEMAS TO regress_user2;
CREATE SCHEMA testns4;
SELECT has_schema_privilege('regress_user2', 'testns4', 'USAGE'); -- yes
SELECT has_schema_privilege('regress_user2', 'testns4', 'CREATE'); -- yes
ALTER DEFAULT PRIVILEGES REVOKE ALL ON SCHEMAS FROM regress_user2;
CREATE SCHEMA testns5;
SELECT has_schema_privilege('regress_user2', 'testns5', 'USAGE'); -- no
SELECT has_schema_privilege('regress_user2', 'testns5', 'CREATE'); -- no
SET ROLE regress_user1; SET ROLE regress_user1;
CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql; CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql;
@ -853,6 +883,10 @@ SELECT count(*)
WHERE nspname = 'testns'; WHERE nspname = 'testns';
DROP SCHEMA testns CASCADE; DROP SCHEMA testns CASCADE;
DROP SCHEMA testns2 CASCADE;
DROP SCHEMA testns3 CASCADE;
DROP SCHEMA testns4 CASCADE;
DROP SCHEMA testns5 CASCADE;
SELECT d.* -- check that entries went away SELECT d.* -- check that entries went away
FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid