diff --git a/doc/src/sgml/ref/alter_default_privileges.sgml b/doc/src/sgml/ref/alter_default_privileges.sgml
index 04064d3..7745792 100644
*** a/doc/src/sgml/ref/alter_default_privileges.sgml
--- b/doc/src/sgml/ref/alter_default_privileges.sgml
***************
*** 46,51 **** GRANT { USAGE | ALL [ PRIVILEGES ] }
--- 46,55 ----
ON TYPES
TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+ GRANT { USAGE | CREATE | ALL [ PRIVILEGES ] }
+ ON SCHEMAS
+ TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
REVOKE [ GRANT OPTION FOR ]
{ { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
[, ...] | ALL [ PRIVILEGES ] }
***************
*** 71,76 **** REVOKE [ GRANT OPTION FOR ]
--- 75,86 ----
ON TYPES
FROM { [ GROUP ] role_name | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
+
+ REVOKE [ GRANT OPTION FOR ]
+ { USAGE | CREATE | ALL [ PRIVILEGES ] }
+ ON SCHEMAS
+ FROM { [ GROUP ] role_name | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
***************
*** 125,130 **** REVOKE [ GRANT OPTION FOR ]
--- 135,142 ----
are altered for objects later created in that schema.
If IN SCHEMA> is omitted, the global default privileges
are altered.
+ IN SCHEMA> is not allowed when using ON SCHEMAS>
+ as schemas can't be nested.
diff --git a/src/backend/catalog/aclchk.c b/src/backeindex c0df671..e1256f1 100644
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
***************
*** 948,953 **** ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
--- 948,957 ----
all_privileges = ACL_ALL_RIGHTS_TYPE;
errormsg = gettext_noop("invalid privilege type %s for type");
break;
+ case ACL_OBJECT_NAMESPACE:
+ all_privileges = ACL_ALL_RIGHTS_NAMESPACE;
+ errormsg = gettext_noop("invalid privilege type %s for schema");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype);
***************
*** 1135,1140 **** SetDefaultACL(InternalDefaultACL *iacls)
--- 1139,1154 ----
this_privileges = ACL_ALL_RIGHTS_TYPE;
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:
elog(ERROR, "unrecognized objtype: %d",
(int) iacls->objtype);
***************
*** 1361,1366 **** RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
--- 1375,1383 ----
case DEFACLOBJ_TYPE:
iacls.objtype = ACL_OBJECT_TYPE;
break;
+ case DEFACLOBJ_NAMESPACE:
+ iacls.objtype = ACL_OBJECT_NAMESPACE;
+ break;
default:
/* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d",
***************
*** 5189,5194 **** get_user_default_acl(GrantObjectType objtype, Oid ownerId, Oid nsp_oid)
--- 5206,5215 ----
defaclobjtype = DEFACLOBJ_TYPE;
break;
+ case ACL_OBJECT_NAMESPACE:
+ defaclobjtype = DEFACLOBJ_NAMESPACE;
+ break;
+
default:
return NULL;
}
diff --git a/src/backend/catalog/obindex d531d17..23d6684 100644
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
***************
*** 1788,1798 **** get_object_address_defacl(List *objname, List *objargs, bool missing_ok)
case DEFACLOBJ_TYPE:
objtype_str = "types";
break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized default ACL object type %c", objtype),
! errhint("Valid object types are \"r\", \"S\", \"f\", and \"T\".")));
}
/*
--- 1788,1801 ----
case DEFACLOBJ_TYPE:
objtype_str = "types";
break;
+ case DEFACLOBJ_NAMESPACE:
+ objtype_str = "schemas";
+ break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized default ACL object type %c", objtype),
! errhint("Valid object types are \"r\", \"S\", \"f\", \"T\" and \"s\".")));
}
/*
***************
*** 3093,3098 **** getObjectDescription(const ObjectAddress *object)
--- 3096,3106 ----
_("default privileges on new types belonging to role %s"),
GetUserNameFromId(defacl->defaclrole, false));
break;
+ case DEFACLOBJ_NAMESPACE:
+ appendStringInfo(&buffer,
+ _("default privileges on new schemas belonging to role %s"),
+ GetUserNameFromId(defacl->defaclrole, false));
+ break;
default:
/* shouldn't get here */
appendStringInfo(&buffer,
***************
*** 4547,4552 **** getObjectIdentityParts(const ObjectAddress *object,
--- 4555,4564 ----
appendStringInfoString(&buffer,
" on types");
break;
+ case DEFACLOBJ_NAMESPACE:
+ appendStringInfoString(&buffer,
+ " on schemas");
+ break;
}
if (objname)
diff --git a/src/backend/catalog/pg_namespindex e5eed79..34e957f 100644
*** a/src/backend/catalog/pg_namespace.c
--- b/src/backend/catalog/pg_namespace.c
***************
*** 31,40 ****
* Create a namespace (schema) with the given name and owner OID.
*
* 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
! * from being linked as a member of any active extension. (If someone
! * does CREATE TEMP TABLE in an extension script, we don't want the temp
! * schema to become part of the extension.)
* ---------------
*/
Oid
--- 31,41 ----
* Create a namespace (schema) with the given name and owner OID.
*
* If isTemp is true, this schema is a per-backend schema for holding
! * temporary tables. Currently, it is used to prevent it from being
! * linked as a member of any active extension. (If someone * does CREATE
! * TEMP TABLE in an extension script, we don't want the temp schema to
! * become part of the extension). And to avoid checking for default ACL
! * for temp namespace (as it is not necessary).
* ---------------
*/
Oid
***************
*** 49,54 **** NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp)
--- 50,56 ----
TupleDesc tupDesc;
ObjectAddress myself;
int i;
+ Acl *nspacl;
/* sanity checks */
if (!nspName)
***************
*** 60,65 **** NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp)
--- 62,73 ----
(errcode(ERRCODE_DUPLICATE_SCHEMA),
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 */
for (i = 0; i < Natts_pg_namespace; i++)
{
***************
*** 69,75 **** NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp)
namestrcpy(&nname, nspName);
values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&nname);
values[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(ownerId);
! nulls[Anum_pg_namespace_nspacl - 1] = true;
nspdesc = heap_open(NamespaceRelationId, RowExclusiveLock);
tupDesc = nspdesc->rd_att;
--- 77,86 ----
namestrcpy(&nname, nspName);
values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&nname);
values[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(ownerId);
! if (nspacl != NULL)
! values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(nspacl);
! else
! nulls[Anum_pg_namespace_nspacl - 1] = true;
nspdesc = heap_open(NamespaceRelationId, RowExclusiveLock);
tupDesc = nspdesc->rd_att;
diff --git a/src/backend/parser/gram.y b/index 0ec1cd3..b845733 100644
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 632,638 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
ROW ROWS RULE
! SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P START
STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING
--- 632,638 ----
RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
ROW ROWS RULE
! SAVEPOINT SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P START
STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING
***************
*** 6689,6694 **** defacl_privilege_target:
--- 6689,6695 ----
| FUNCTIONS { $$ = ACL_OBJECT_FUNCTION; }
| SEQUENCES { $$ = ACL_OBJECT_SEQUENCE; }
| TYPES_P { $$ = ACL_OBJECT_TYPE; }
+ | SCHEMAS { $$ = ACL_OBJECT_NAMESPACE; }
;
***************
*** 13923,13928 **** unreserved_keyword:
--- 13924,13930 ----
| RULE
| SAVEPOINT
| SCHEMA
+ | SCHEMAS
| SCROLL
| SEARCH
| SECOND_P
diff --git a/src/bin/pg_dump/dumindex 0d51668..ff486ff 100644
*** a/src/bin/pg_dump/dumputils.c
--- b/src/bin/pg_dump/dumputils.c
***************
*** 511,517 **** do { \
CONVERT_PRIV('X', "EXECUTE");
else if (strcmp(type, "LANGUAGE") == 0)
CONVERT_PRIV('U', "USAGE");
! else if (strcmp(type, "SCHEMA") == 0)
{
CONVERT_PRIV('C', "CREATE");
CONVERT_PRIV('U', "USAGE");
--- 511,519 ----
CONVERT_PRIV('X', "EXECUTE");
else if (strcmp(type, "LANGUAGE") == 0)
CONVERT_PRIV('U', "USAGE");
! else if (strcmp(type, "SCHEMA") == 0 ||
! strcmp(type, "SCHEMAS") == 0
! )
{
CONVERT_PRIV('C', "CREATE");
CONVERT_PRIV('U', "USAGE");
diff --git a/src/bin/pg_dump/pg_duindex 9f59f53..8f5d9c5 100644
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 13373,13378 **** dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo)
--- 13373,13381 ----
case DEFACLOBJ_TYPE:
type = "TYPES";
break;
+ case DEFACLOBJ_NAMESPACE:
+ type = "SCHEMAS";
+ break;
default:
/* shouldn't get here */
exit_horribly(NULL,
diff --git a/src/bin/psql/descriindex 1632104..15dd5f9 100644
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 984,990 **** listDefaultACLs(const char *pattern)
printfPQExpBuffer(&buf,
"SELECT pg_catalog.pg_get_userbyid(d.defaclrole) 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"
" ",
gettext_noop("Owner"),
gettext_noop("Schema"),
--- 984,990 ----
printfPQExpBuffer(&buf,
"SELECT pg_catalog.pg_get_userbyid(d.defaclrole) 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' WHEN '%c' THEN '%s' END AS \"%s\",\n"
" ",
gettext_noop("Owner"),
gettext_noop("Schema"),
***************
*** 996,1001 **** listDefaultACLs(const char *pattern)
--- 996,1003 ----
gettext_noop("function"),
DEFACLOBJ_TYPE,
gettext_noop("type"),
+ DEFACLOBJ_NAMESPACE,
+ gettext_noop("schema"),
gettext_noop("Type"));
printACLColumn(&buf, "d.defaclacl");
diff --git a/src/include/catalindex 06aaaba..7b7d7b8 100644
*** a/src/include/catalog/pg_default_acl.h
--- b/src/include/catalog/pg_default_acl.h
***************
*** 70,74 **** typedef FormData_pg_default_acl *Form_pg_default_acl;
--- 70,75 ----
#define DEFACLOBJ_SEQUENCE 'S' /* sequence */
#define DEFACLOBJ_FUNCTION 'f' /* function */
#define DEFACLOBJ_TYPE 'T' /* type */
+ #define DEFACLOBJ_NAMESPACE 'n' /* namespace */
#endif /* PG_DEFAULT_ACL_H */
diff --git a/src/include/parser/kwlist.h b/index 77d873b..5dc0244 100644
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 339,344 **** PG_KEYWORD("rows", ROWS, UNRESERVED_KEYWORD)
--- 339,345 ----
PG_KEYWORD("rule", RULE, UNRESERVED_KEYWORD)
PG_KEYWORD("savepoint", SAVEPOINT, UNRESERVED_KEYWORD)
PG_KEYWORD("schema", SCHEMA, UNRESERVED_KEYWORD)
+ PG_KEYWORD("schemas", SCHEMAS, UNRESERVED_KEYWORD)
PG_KEYWORD("scroll", SCROLL, UNRESERVED_KEYWORD)
PG_KEYWORD("search", SEARCH, UNRESERVED_KEYWORD)
PG_KEYWORD("second", SECOND_P, UNRESERVED_KEYWORD)
diff --git a/src/test/regress/expeindex f66b443..449b0e3 100644
*** a/src/test/regress/expected/privileges.out
--- b/src/test/regress/expected/privileges.out
***************
*** 1337,1342 **** SELECT has_table_privilege('regress_user1', 'testns.acltest1', 'INSERT'); -- no
--- 1337,1400 ----
(1 row)
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;
CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql;
SELECT has_function_privilege('regress_user2', 'testns.foo()', 'EXECUTE'); -- no
***************
*** 1384,1389 **** SELECT count(*)
--- 1442,1451 ----
DROP SCHEMA testns CASCADE;
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
FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid
WHERE nspname IS NULL AND defaclnamespace != 0;
diff --git a/src/test/regress/sql/privileges.sqindex 00dc7bd..c2c1629 100644
*** a/src/test/regress/sql/privileges.sql
--- b/src/test/regress/sql/privileges.sql
***************
*** 807,812 **** SELECT has_table_privilege('regress_user1', 'testns.acltest1', 'INSERT'); -- no
--- 807,842 ----
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;
CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql;
***************
*** 844,849 **** SELECT count(*)
--- 874,883 ----
WHERE nspname = 'testns';
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
FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid