diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 34679d8..66a3306 100644
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 3130,3135 ****
--- 3130,3199 ----
+
+ pg_namespace_default_acl
+
+
+ pg_namespace_default_acl
+
+
+
+ The catalog pg_namespace_default_acl> stores default
+ privileges for newly created objects inside the schema.
+
+
+
+ pg_namespace> Columns
+
+
+
+
+ Name
+ Type
+ References
+ Description
+
+
+
+
+
+ defaclnamespace
+ oid
+ pg_namespace.oid
+ Oid of the namespace
+
+
+
+ defaclgrantobjtype
+ char
+
+
+ r> = table, v> = view,
+ f> = function, S> = sequence
+
+
+
+
+ defacllist
+ aclitem[]
+
+
+ Array of acls that the object should have on creation
+ this is NOT a mask, it's exactly what the object will get.
+ See
+ ,
+ and
+
+ for details.
+
+
+
+
+
+
+
+
+
pg_opclass
diff --git a/doc/src/sgml/ref/alter_schema.sgml b/doc/src/sgml/ref/alter_schema.sgml
index 2458d19..dd3aad2 100644
*** a/doc/src/sgml/ref/alter_schema.sgml
--- b/doc/src/sgml/ref/alter_schema.sgml
*************** PostgreSQL documentation
*** 23,40 ****
ALTER SCHEMA name RENAME TO newname
ALTER SCHEMA name OWNER TO newowner
! Description
! ALTER SCHEMA changes the definition of a schema.
- You must own the schema to use ALTER SCHEMA>.
To rename a schema you must also have the
CREATE privilege for the database.
To alter the owner, you must also be a direct or
--- 23,68 ----
ALTER SCHEMA name RENAME TO newname
ALTER SCHEMA name OWNER TO newowner
+
+ ALTER SCHEMA name { SET | ADD } DEFAULT PRIVILEGES { { ON default_privileges
+ TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ] } [AND ...] } [...]
+
+ where default_privileges is:
+
+ { { TABLE | VIEW } { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
+ [,...] | ALL [ PRIVILEGES ] } |
+ SEQUENCE { { USAGE | SELECT | UPDATE }
+ [,...] | ALL [ PRIVILEGES ] } |
+ FUNCTION { EXECUTE | ALL [ PRIVILEGES ] } }
+
+ ALTER SCHEMA name DROP DEFAULT PRIVILEGES { { ON drop_default_privileges
+ FROM { [ GROUP ] rolename | PUBLIC } [, ...] } [AND ...] } [...]
+
+ where drop_default_privileges is:
+
+ { { TABLE | VIEW } [ GRANT OPTION FOR ]
+ { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
+ [,...] | ALL [ PRIVILEGES ] } |
+ SEQUENCE [ GRANT OPTION FOR ]
+ { { USAGE | SELECT | UPDATE } [,...] | ALL [ PRIVILEGES ] } |
+ FUNCTION [ GRANT OPTION FOR ]
+ { EXECUTE | ALL [ PRIVILEGES ] } }
! Description
! You must own the schema to use ALTER SCHEMA>.
+
+
+
+ Change the definition of a schema
+
To rename a schema you must also have the
CREATE privilege for the database.
To alter the owner, you must also be a direct or
*************** ALTER SCHEMA nameCREATE privilege for the database.
(Note that superusers have all these privileges automatically.)
-
! Parameters
--- 70,77 ----
CREATE privilege for the database.
(Note that superusers have all these privileges automatically.)
! Parameters
*************** ALTER SCHEMA name
+
+
+
+
+ Change the default privileges for new objects
+
+
+ The ALTER SCHEMA> { SET | ADD | DROP }> DEFAULT PRIVILEGES> command
+ allows you to define privileges for newly created objects in the schema.
+
+
+
+ See the description of the and
+ commands for more detailed
+ explanation of privilege system and the meaning of the privilege types.
+
+
+
+ The possible commands are:
+
+
+
+ SET
+
+
+ Replaces the current default privileges with the newly specified ones.
+ Behaves like GRANT> on object that had no privileges set before.
+
+
+
+
+
+ ADD
+
+
+ Adds new default privileges. Behaves like standard GRANT>.
+
+
+
+
+
+ DROP
+
+
+ Removes specified privileges from defaults. Behaves like REVOKE>.
+
+
+
+
+
+
+
+
+
+ Examples
+
+
+ Grant select privilege for every new table in schema
+ public to everybody:
+
+
+ ALTER SCHEMA public SET DEFAULT PRIVILEGES ON TABLE SELECT TO public;
+
+
+
+
+ User webuser will have select privilege on all new
+ tables created in schema users, while user
+ admin will also be able to insert, update and delete
+ rows in those tables:
+
+
+ ALTER SCHEMA users SET DEFAULT PRIVILEGES ON TABLE SELECT TO webuser, admin AND UPDATE, INSERT, DELETE TO admin;
+
+
+
+
+ Give user webuser select privilege on all new
+ tables and views, let him execute all new functions and read new sequences.
+ The admin user will be able not only to select but also
+ change contents of new tables and also grant those permissions he has on
+ tables to other users. He will also be able to select from new views,
+ execute new functions and read new sequences like webuser
+ does but will also be able to update new sequences.
+ All for schema public:
+
+
+ ALTER SCHEMA public SET DEFAULT PRIVILEGES
+ ON TABLE SELECT TO webuser, admin AND UPDATE, INSERT, DELETE TO admin WITH GRANT OPTION
+ ON VIEW SELECT TO webuser, admin
+ ON FUNCTION EXECUTE TO webuser, admin
+ ON SEQUENCE USAGE, SELECT TO webuser AND ALL PRIVILEGES TO admin;
+
+
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index bf963b8..a8d76b6 100644
*** a/doc/src/sgml/ref/grant.sgml
--- b/doc/src/sgml/ref/grant.sgml
*************** PostgreSQL documentation
*** 22,29 ****
GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
! [,...] | ALL [ PRIVILEGES ] }
! ON [ TABLE ] tablename [, ...]
TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
GRANT { { SELECT | INSERT | UPDATE | REFERENCES } ( column [, ...] )
--- 22,29 ----
GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
! [,...] | ALL [ PRIVILEGES ] | DEFAULT PRIVILEGES }
! ON [ TABLE | VIEW ] tablename [, ...]
TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
GRANT { { SELECT | INSERT | UPDATE | REFERENCES } ( column [, ...] )
*************** GRANT { { SELECT | INSERT | UPDATE | REF
*** 32,38 ****
TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
GRANT { { USAGE | SELECT | UPDATE }
! [,...] | ALL [ PRIVILEGES ] }
ON SEQUENCE sequencename [, ...]
TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
--- 32,38 ----
TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
GRANT { { USAGE | SELECT | UPDATE }
! [,...] | ALL [ PRIVILEGES ] | DEFAULT PRIVILEGES }
ON SEQUENCE sequencename [, ...]
TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
*************** GRANT { USAGE | ALL [ PRIVILEGES ] }
*** 48,54 ****
ON FOREIGN SERVER servername> [, ...]
TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
! GRANT { EXECUTE | ALL [ PRIVILEGES ] }
ON FUNCTION funcname ( [ [ argmode ] [ argname ] argtype [, ...] ] ) [, ...]
TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
--- 48,54 ----
ON FOREIGN SERVER servername> [, ...]
TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
! GRANT { EXECUTE | ALL [ PRIVILEGES ] | DEFAULT PRIVILEGES }
ON FUNCTION funcname ( [ [ argmode ] [ argname ] argtype [, ...] ] ) [, ...]
TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
*************** GRANT rol
*** 132,137 ****
--- 132,139 ----
include granting some privileges to PUBLIC.
The default is no public access for tables, columns, schemas, and
tablespaces;
+ For tables, views, functions and sequences the initial default privileges
+ can also be changed using ALTER SCHEMA command;
CONNECT> privilege and TEMP> table creation privilege
for databases;
EXECUTE> privilege for functions; and
*************** GRANT rol
*** 339,344 ****
--- 341,361 ----
+
+
+ DEFAULT PRIVILEGES
+
+
+ Replace current privileges with the ones specified using
+ .
+ The WITH GRANT OPTION parameter is not applicable
+ because it is copied from default privileges too.
+ Note: this can actually revoke some
+ privileges because it clears all existing privileges object has and
+ replaces them with default ones.
+
+
+
The privileges required by other commands are listed on the
diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml
index 8d62580..109b58c 100644
*** a/doc/src/sgml/ref/revoke.sgml
--- b/doc/src/sgml/ref/revoke.sgml
*************** PostgreSQL documentation
*** 24,30 ****
REVOKE [ GRANT OPTION FOR ]
{ { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
[,...] | ALL [ PRIVILEGES ] }
! ON [ TABLE ] tablename [, ...]
FROM { [ GROUP ] rolename | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
--- 24,30 ----
REVOKE [ GRANT OPTION FOR ]
{ { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
[,...] | ALL [ PRIVILEGES ] }
! ON [ TABLE | VIEW ] tablename [, ...]
FROM { [ GROUP ] rolename | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 400ae80..aa4b35e 100644
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
*************** POSTGRES_BKI_SRCS = $(addprefix $(top_sr
*** 37,42 ****
--- 37,43 ----
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
pg_ts_parser.h pg_ts_template.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
+ pg_namespace_default_acl.h \
toasting.h indexing.h \
)
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ec4aaf0..e991400 100644
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
***************
*** 31,36 ****
--- 31,37 ----
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
+ #include "catalog/pg_namespace_default_acl.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
***************
*** 50,57 ****
#include "utils/syscache.h"
#include "utils/tqual.h"
!
static void ExecGrant_Relation(InternalGrant *grantStmt);
static void ExecGrant_Database(InternalGrant *grantStmt);
static void ExecGrant_Fdw(InternalGrant *grantStmt);
static void ExecGrant_ForeignServer(InternalGrant *grantStmt);
--- 51,60 ----
#include "utils/syscache.h"
#include "utils/tqual.h"
! static void ExecGrantStmt_Defaults(InternalGrant *istmt);
static void ExecGrant_Relation(InternalGrant *grantStmt);
+ static void ExecGrantDefaults_Relation(InternalGrant *grantStmt);
+ static void ExecGrantDefaults_Function(InternalGrant *grantStmt);
static void ExecGrant_Database(InternalGrant *grantStmt);
static void ExecGrant_Fdw(InternalGrant *grantStmt);
static void ExecGrant_ForeignServer(InternalGrant *grantStmt);
*************** static void expand_all_col_privileges(Oi
*** 69,76 ****
AclMode this_privileges,
AclMode *col_privileges,
int num_col_privileges);
- static AclMode string_to_privilege(const char *privname);
- static const char *privilege_to_string(AclMode privilege);
static AclMode restrict_and_check_grant(bool is_grant, AclMode avail_goptions,
bool all_privs, AclMode privileges,
Oid objectId, Oid grantorId,
--- 72,77 ----
*************** dumpacl(Acl *acl)
*** 105,111 ****
*
* NB: the original old_acl is pfree'd.
*/
! static Acl *
merge_acl_with_grant(Acl *old_acl, bool is_grant,
bool grant_option, DropBehavior behavior,
List *grantees, AclMode privileges,
--- 106,112 ----
*
* NB: the original old_acl is pfree'd.
*/
! Acl *
merge_acl_with_grant(Acl *old_acl, bool is_grant,
bool grant_option, DropBehavior behavior,
List *grantees, AclMode privileges,
*************** restrict_and_check_grant(bool is_grant,
*** 270,275 ****
--- 271,277 ----
return this_privileges;
}
+
/*
* Called to execute the utility commands GRANT and REVOKE
*/
*************** ExecuteGrantStmt(GrantStmt *stmt)
*** 294,299 ****
--- 296,310 ----
istmt.grant_option = stmt->grant_option;
istmt.behavior = stmt->behavior;
+ /*
+ * GRANT DEFAULT PRIVILEGES
+ */
+ if (!stmt->grantees)
+ {
+ ExecGrantStmt_Defaults(&istmt);
+ return;
+ }
+
/*
* Convert the PrivGrantee list into an Oid list. Note that at this point
* we insert an ACL_ID_PUBLIC into the list if an empty role name is
*************** ExecGrant_Tablespace(InternalGrant *istm
*** 1988,1994 ****
}
! static AclMode
string_to_privilege(const char *privname)
{
if (strcmp(privname, "insert") == 0)
--- 1999,2218 ----
}
! /*
! * ExecGrantStmt_Defaults
! *
! * "Internal" entrypoint for reseting privileges to schema DEFAULT
! */
! static void
! ExecGrantStmt_Defaults(InternalGrant *istmt)
! {
! switch (istmt->objtype)
! {
! case ACL_OBJECT_RELATION:
! case ACL_OBJECT_SEQUENCE:
! ExecGrantDefaults_Relation(istmt);
! break;
! case ACL_OBJECT_FUNCTION:
! ExecGrantDefaults_Function(istmt);
! break;
! default:
! elog(ERROR, "unrecognized GrantStmt.objtype: %d",
! (int) istmt->objtype);
! }
! }
!
! static void
! ExecGrantDefaults_Relation(InternalGrant *grantStmt)
! {
! Relation relation;
! ListCell *cell;
!
! relation = heap_open(RelationRelationId, RowExclusiveLock);
!
! foreach(cell, grantStmt->objects)
! {
! Oid relOid = lfirst_oid(cell);
! Datum aclDatum;
! Form_pg_class pg_class_tuple;
! bool isNull;
! Acl *old_acl = NULL;
! Acl *new_acl;
! HeapTuple tuple;
! HeapTuple newtuple;
! Datum values[Natts_pg_class];
! bool nulls[Natts_pg_class];
! bool replaces[Natts_pg_class];
! int noldmembers;
! int nnewmembers;
! Oid *oldmembers;
! Oid *newmembers;
! char objtype;
!
! tuple = SearchSysCache(RELOID,
! ObjectIdGetDatum(relOid),
! 0, 0, 0);
!
! if (!HeapTupleIsValid(tuple))
! elog(ERROR, "cache lookup failed for relation %u", relOid);
! pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
!
! /* Only owner can replace acls */
! pg_class_ownercheck(relOid, GetUserId());
!
! aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
! &isNull);
! if (!isNull)
! old_acl = DatumGetAclPCopy(aclDatum);
!
! noldmembers = aclmembers(old_acl, &oldmembers);
!
! /* Check relkind and convert it to DefaultACLs object */
! switch (pg_class_tuple->relkind)
! {
! case RELKIND_SEQUENCE:
! objtype = NSPDEFACLOBJ_SEQUENCE;
! break;
! case RELKIND_VIEW:
! objtype = NSPDEFACLOBJ_VIEW;
! break;
! case RELKIND_RELATION:
! objtype = NSPDEFACLOBJ_TABLE;
! break;
! default: /* parser error ? */
! elog(ERROR, "relation can't have DEFAULT PRIVILEGES");
! }
!
! new_acl = pg_namespace_object_default_acl(pg_class_tuple->relnamespace,
! objtype, &isNull);
!
! if (isNull)
! elog(ERROR, "no DEFAULT PRIVILEGES for relation");
!
! nnewmembers = aclmembers(new_acl, &newmembers);
!
! /* Replace ACLs */
! MemSet(values, 0, sizeof(values));
! MemSet(nulls, false, sizeof(nulls));
! MemSet(replaces, false, sizeof(replaces));
!
! replaces[Anum_pg_class_relacl - 1] = true;
! values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl);
!
! newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
! values, nulls, replaces);
!
! simple_heap_update(relation, &newtuple->t_self, newtuple);
!
! /* keep the catalog indexes up to date */
! CatalogUpdateIndexes(relation, newtuple);
!
! /* Update the shared dependency ACL info */
! updateAclDependencies(RelationRelationId, relOid, 0,
! pg_class_tuple->relowner, grantStmt->is_grant,
! noldmembers, oldmembers,
! nnewmembers, newmembers);
!
! pfree(new_acl);
!
! ReleaseSysCache(tuple);
!
! /* prevent error when processing duplicate objects */
! CommandCounterIncrement();
! }
!
! heap_close(relation, RowExclusiveLock);
! }
!
!
! static void
! ExecGrantDefaults_Function(InternalGrant *grantStmt)
! {
! Relation relation;
! ListCell *cell;
!
! relation = heap_open(ProcedureRelationId, RowExclusiveLock);
!
! foreach(cell, grantStmt->objects)
! {
! Oid funcId = lfirst_oid(cell);
! Form_pg_proc pg_proc_tuple;
! Datum aclDatum;
! bool isNull;
! Acl *old_acl = NULL;
! Acl *new_acl;
! HeapTuple tuple;
! HeapTuple newtuple;
! Datum values[Natts_pg_class];
! bool nulls[Natts_pg_class];
! bool replaces[Natts_pg_class];
! int noldmembers;
! int nnewmembers;
! Oid *oldmembers;
! Oid *newmembers;
!
! tuple = SearchSysCache(PROCOID,
! ObjectIdGetDatum(funcId),
! 0, 0, 0);
! if (!HeapTupleIsValid(tuple))
! elog(ERROR, "cache lookup failed for function %u", funcId);
!
! pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple);
!
! /* Only owner can replace acls */
! pg_proc_ownercheck(funcId, GetUserId());
!
! aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl,
! &isNull);
!
! if (!isNull)
! old_acl = DatumGetAclPCopy(aclDatum);
!
! noldmembers = aclmembers(old_acl, &oldmembers);
!
! new_acl = pg_namespace_object_default_acl(pg_proc_tuple->pronamespace,
! NSPDEFACLOBJ_FUNCTION, &isNull);
!
! if (isNull)
! elog(ERROR, "no DEFAULT PRIVILEGES for function");
!
! nnewmembers = aclmembers(new_acl, &newmembers);
!
! /* Replace ACLs */
! MemSet(values, 0, sizeof(values));
! MemSet(nulls, false, sizeof(nulls));
! MemSet(replaces, false, sizeof(replaces));
!
! replaces[Anum_pg_proc_proacl - 1] = true;
! values[Anum_pg_proc_proacl - 1] = PointerGetDatum(new_acl);
!
! newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
! nulls, replaces);
!
! simple_heap_update(relation, &newtuple->t_self, newtuple);
!
! /* keep the catalog indexes up to date */
! CatalogUpdateIndexes(relation, newtuple);
!
! /* Update the shared dependency ACL info */
! updateAclDependencies(ProcedureRelationId, funcId, 0,
! pg_proc_tuple->proowner, grantStmt->is_grant,
! noldmembers, oldmembers,
! nnewmembers, newmembers);
!
! ReleaseSysCache(tuple);
!
! pfree(new_acl);
!
! /* prevent error when processing duplicate objects */
! CommandCounterIncrement();
! }
!
! heap_close(relation, RowExclusiveLock);
! }
!
!
! AclMode
string_to_privilege(const char *privname)
{
if (strcmp(privname, "insert") == 0)
*************** string_to_privilege(const char *privname
*** 2025,2031 ****
return 0; /* appease compiler */
}
! static const char *
privilege_to_string(AclMode privilege)
{
switch (privilege)
--- 2249,2255 ----
return 0; /* appease compiler */
}
! const char *
privilege_to_string(AclMode privilege)
{
switch (privilege)
*************** pg_conversion_ownercheck(Oid conv_oid, O
*** 3532,3534 ****
--- 3756,3830 ----
return has_privs_of_role(roleid, ownerId);
}
+
+ /*
+ * Get default permissions for newly created object inside given schema
+ */
+ Acl *
+ pg_namespace_object_default_acl(Oid nsp_oid, char objtype, bool *isNull)
+ {
+ ScanKeyData key[2];
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ Relation rel;
+ Datum aclDatum;
+ Acl *result;
+
+ *isNull = true;
+
+ /*
+ * Use NULL value for object acls if it is created in system schema
+ * or if it does not have default permissions support (which is usually
+ * because it does not have namespace support either).
+ */
+ if (nsp_oid == PG_CATALOG_NAMESPACE || nsp_oid == PG_TOAST_NAMESPACE)
+ return NULL;
+
+ switch (objtype)
+ {
+ case NSPDEFACLOBJ_TABLE:
+ case NSPDEFACLOBJ_VIEW:
+ case NSPDEFACLOBJ_FUNCTION:
+ case NSPDEFACLOBJ_SEQUENCE:
+ break;
+
+ default:
+ return NULL;
+ }
+
+ /*
+ * Search for default permissions for given namespace and objecttype
+ */
+ ScanKeyInit(&key[0],
+ Anum_pg_namespace_default_acl_defaclnamespace,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(nsp_oid));
+
+ ScanKeyInit(&key[1],
+ Anum_pg_namespace_default_acl_defaclgrantobjtype,
+ BTEqualStrategyNumber, F_CHAREQ,
+ CharGetDatum(objtype));
+
+ rel = heap_open(NamespaceDefaultAclRelationId, AccessShareLock);
+
+ scan = heap_beginscan(rel, SnapshotNow, 2, key);
+
+ tuple = heap_getnext(scan, ForwardScanDirection);
+
+ if (HeapTupleIsValid(tuple))
+ {
+ aclDatum = heap_getattr(tuple, Anum_pg_namespace_default_acl_defacldefacllist,
+ RelationGetDescr(rel), isNull);
+
+ result = DatumGetAclPCopy(aclDatum);
+ }
+ else
+ {
+ result = NULL;
+ }
+
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
+
+ return result;
+ }
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index f338382..b6467e6 100644
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 37,42 ****
--- 37,43 ----
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
+ #include "catalog/pg_namespace_default_acl.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
*************** static bool object_address_present_add_f
*** 175,180 ****
--- 176,182 ----
ObjectAddresses *addrs);
static void getRelationDescription(StringInfo buffer, Oid relid);
static void getOpFamilyDescription(StringInfo buffer, Oid opfid);
+ static void getDefaultACLDescription(StringInfo buffer, char objtype);
/*
*************** doDeletion(const ObjectAddress *object)
*** 1127,1132 ****
--- 1129,1138 ----
RemoveForeignDataWrapperById(object->objectId);
break;
+ case OCLASS_NSPDEFACLS:
+ RemoveDefaultACLsById(object->objectId);
+ break;
+
/* OCLASS_ROLE, OCLASS_DATABASE, OCLASS_TBLSPACE not handled */
default:
*************** getObjectClass(const ObjectAddress *obje
*** 2048,2053 ****
--- 2054,2063 ----
case UserMappingRelationId:
Assert(object->objectSubId == 0);
return OCLASS_USER_MAPPING;
+
+ case NamespaceDefaultAclRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_NSPDEFACLS;
}
/* shouldn't get here */
*************** getObjectDescription(const ObjectAddress
*** 2590,2595 ****
--- 2600,2643 ----
break;
}
+ case OCLASS_NSPDEFACLS:
+ {
+ Relation rel;
+ ScanKeyData key[1];
+ HeapScanDesc scan;
+ HeapTuple tup;
+ char *nspname;
+ Form_pg_namespace_default_acl nspda;
+
+ rel = heap_open(NamespaceDefaultAclRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ scan = heap_beginscan(rel, SnapshotNow, 1, key);
+
+ tup = heap_getnext(scan, ForwardScanDirection);
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "could not find tuple for default acls %u",
+ object->objectId);
+
+ nspda = (Form_pg_namespace_default_acl) GETSTRUCT(tup);
+
+ nspname = get_namespace_name(nspda->defaclnamespace);
+
+ appendStringInfo(&buffer,
+ _("default acls in namespace %s for new "),
+ nspname);
+ getDefaultACLDescription(&buffer, nspda->defaclobjtype);
+
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
+ break;
+ }
+
default:
appendStringInfo(&buffer, "unrecognized object %u %u %d",
object->classId,
*************** getOpFamilyDescription(StringInfo buffer
*** 2708,2710 ****
--- 2756,2784 ----
ReleaseSysCache(amTup);
ReleaseSysCache(opfTup);
}
+
+
+ /*
+ * subroutine for getObjectDescription: describe DefaultACLs object type
+ */
+ static void
+ getDefaultACLDescription(StringInfo buffer, char objtype)
+ {
+ switch (objtype)
+ {
+ case NSPDEFACLOBJ_TABLE:
+ appendStringInfo(buffer, _("TABLE"));
+ break;
+ case NSPDEFACLOBJ_VIEW:
+ appendStringInfo(buffer, _("VIEW"));
+ break;
+ case NSPDEFACLOBJ_FUNCTION:
+ appendStringInfo(buffer, _("FUNCTION"));
+ break;
+ case NSPDEFACLOBJ_SEQUENCE:
+ appendStringInfo(buffer, _("SEQUENCE"));
+ break;
+ default:
+ appendStringInfo(buffer, _("unknown"));
+ }
+ }
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 7557400..368f2ce 100644
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 67,72 ****
--- 67,73 ----
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "utils/acl.h"
static void AddNewRelationTuple(Relation pg_class_desc,
*************** AddNewAttributeTuples(Oid new_rel_oid,
*** 633,640 ****
* Caller has already opened and locked pg_class.
* Tuple data is taken from new_rel_desc->rd_rel, except for the
* variable-width fields which are not present in a cached reldesc.
! * We always initialize relacl to NULL (i.e., default permissions),
! * and reloptions is set to the passed-in text array (if any).
* --------------------------------
*/
void
--- 634,641 ----
* Caller has already opened and locked pg_class.
* Tuple data is taken from new_rel_desc->rd_rel, except for the
* variable-width fields which are not present in a cached reldesc.
! * Initial permissions are obtained from pg_namespace_default_acl
! * (if applicable) and reloptions is set to the passed-in text array (if any).
* --------------------------------
*/
void
*************** InsertPgClassTuple(Relation pg_class_des
*** 646,651 ****
--- 647,654 ----
Form_pg_class rd_rel = new_rel_desc->rd_rel;
Datum values[Natts_pg_class];
bool nulls[Natts_pg_class];
+ bool isNull;
+ Acl *relacl;
HeapTuple tup;
/* This is a tad tedious, but way cleaner than what we used to do... */
*************** InsertPgClassTuple(Relation pg_class_des
*** 675,682 ****
values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
! /* start out with empty permissions */
! nulls[Anum_pg_class_relacl - 1] = true;
if (reloptions != (Datum) 0)
values[Anum_pg_class_reloptions - 1] = reloptions;
else
--- 678,691 ----
values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
!
! /* get default permissions */
! relacl = pg_namespace_object_default_acl(rd_rel->relnamespace, rd_rel->relkind, &isNull);
! if (isNull)
! nulls[Anum_pg_class_relacl - 1] = true;
! else
! values[Anum_pg_class_relacl - 1] = PointerGetDatum(relacl);
!
if (reloptions != (Datum) 0)
values[Anum_pg_class_reloptions - 1] = reloptions;
else
*************** InsertPgClassTuple(Relation pg_class_des
*** 695,700 ****
--- 704,724 ----
CatalogUpdateIndexes(pg_class_desc, tup);
+ if (!isNull)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ /* Update the shared dependency ACL info */
+ nnewmembers = aclmembers(relacl, &newmembers);
+ updateAclDependencies(RelationRelationId, new_rel_oid, 0,
+ rd_rel->relowner, true,
+ 0, NULL,
+ nnewmembers, newmembers);
+
+ pfree(relacl);
+ }
+
heap_freetuple(tup);
}
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 1a06530..8ffb552 100644
*** a/src/backend/catalog/pg_proc.c
--- b/src/backend/catalog/pg_proc.c
***************
*** 20,25 ****
--- 20,26 ----
#include "catalog/indexing.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
+ #include "catalog/pg_namespace_default_acl.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_proc_fn.h"
#include "catalog/pg_type.h"
*************** ProcedureCreate(const char *procedureNam
*** 95,100 ****
--- 96,103 ----
bool nulls[Natts_pg_proc];
Datum values[Natts_pg_proc];
bool replaces[Natts_pg_proc];
+ bool isNull;
+ Acl *proacl;
Oid relid;
NameData procname;
TupleDesc tupDesc;
*************** ProcedureCreate(const char *procedureNam
*** 330,337 ****
values[Anum_pg_proc_proconfig - 1] = proconfig;
else
nulls[Anum_pg_proc_proconfig - 1] = true;
! /* start out with empty permissions */
! nulls[Anum_pg_proc_proacl - 1] = true;
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
tupDesc = RelationGetDescr(rel);
--- 333,345 ----
values[Anum_pg_proc_proconfig - 1] = proconfig;
else
nulls[Anum_pg_proc_proconfig - 1] = true;
!
! /* get default permissions */
! proacl = pg_namespace_object_default_acl(procNamespace, NSPDEFACLOBJ_FUNCTION, &isNull);
! if (isNull)
! nulls[Anum_pg_proc_proacl - 1] = true;
! else
! values[Anum_pg_proc_proacl - 1] = PointerGetDatum(proacl);
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
tupDesc = RelationGetDescr(rel);
*************** ProcedureCreate(const char *procedureNam
*** 539,544 ****
--- 547,567 ----
/* dependency on owner */
recordDependencyOnOwner(ProcedureRelationId, retval, GetUserId());
+ if (!isNull)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ /* Update the shared dependency ACL info */
+ nnewmembers = aclmembers(proacl, &newmembers);
+ updateAclDependencies(ProcedureRelationId, retval, 0,
+ GetUserId(), true,
+ 0, NULL,
+ nnewmembers, newmembers);
+
+ pfree(proacl);
+ }
+
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index c70d4a8..3599166 100644
*** a/src/backend/commands/schemacmds.c
--- b/src/backend/commands/schemacmds.c
***************
*** 16,26 ****
--- 16,28 ----
#include "access/heapam.h"
#include "access/xact.h"
+ #include "access/sysattr.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_namespace.h"
+ #include "catalog/pg_namespace_default_acl.h"
#include "commands/dbcommands.h"
#include "commands/schemacmds.h"
#include "miscadmin.h"
***************
*** 28,38 ****
--- 30,57 ----
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+ #include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+ #include "utils/tqual.h"
+
+
+
+ typedef struct
+ {
+ int action;
+ Oid nspId;
+ Oid nspOwnerId;
+ GrantObjectType objtype;
+ AclMode privileges;
+ List *grantees;
+ bool grant_option;
+ } InternalDefaultACLs;
+
static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId);
+ static void SetDefaultACLs(InternalDefaultACLs *iacls);
/*
* CREATE SCHEMA
*************** CreateSchemaCommand(CreateSchemaStmt *st
*** 147,152 ****
--- 166,558 ----
}
+
+ /*
+ * Default ACLs
+ * Implements ALTER SCHEMA DEFAULT PRIVILEGES
+ */
+ void
+ DefaultACLsCommand(DefaultACLsStmt *stmt)
+ {
+ InternalDefaultACLs iacls;
+ List *objlist;
+ ListCell *objcell;
+ ListCell *privcell;
+ ListCell *cell;
+ Relation relation;
+ HeapTuple tuple;
+ const char *errormsg;
+ AclMode all_privileges;
+ Form_pg_namespace pg_namespace_tuple;
+
+ relation = heap_open(NamespaceRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCache(NAMESPACENAME,
+ CStringGetDatum(stmt->schemaname),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for namespace \"%s\"", stmt->schemaname);
+
+ iacls.nspId = HeapTupleGetOid(tuple);
+ pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(tuple);
+ iacls.nspOwnerId = pg_namespace_tuple->nspowner;
+
+ if (iacls.nspId == PG_CATALOG_NAMESPACE || iacls.nspId == PG_TOAST_NAMESPACE)
+ elog(ERROR, "can't set default permissions for system catalog");
+
+ /* Permission check */
+ if (!pg_namespace_ownercheck(iacls.nspId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
+ stmt->schemaname);
+
+ objlist = transformDefaultACLsStmt(stmt);
+
+ foreach(objcell, stmt->objlist)
+ {
+ DefaultACLsObject *object = (DefaultACLsObject *) lfirst(objcell);
+
+ iacls.action = stmt->action;
+ iacls.objtype = object->objtype;
+
+ /*
+ * Set allowed privileges and apropriate error message for object type
+ * used in privilege conversion few lines down
+ */
+ switch (object->objtype)
+ {
+ case NSP_ACL_OBJ_RELATION:
+ all_privileges = ACL_ALL_RIGHTS_RELATION;
+ errormsg = gettext_noop("invalid privilege type %s for relation");
+ break;
+ case NSP_ACL_OBJ_VIEW:
+ all_privileges = ACL_ALL_RIGHTS_RELATION;
+ errormsg = gettext_noop("invalid privilege type %s for view");
+ break;
+ case NSP_ACL_OBJ_FUNCTION:
+ all_privileges = ACL_ALL_RIGHTS_FUNCTION;
+ errormsg = gettext_noop("invalid privilege type %s for function");
+ break;
+ case NSP_ACL_OBJ_SEQUENCE:
+ all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+ errormsg = gettext_noop("invalid privilege type %s for sequence");
+ break;
+ default:
+ /* keep compiler quiet */
+ all_privileges = ACL_NO_RIGHTS;
+ errormsg = NULL;
+ elog(ERROR, "unrecognized DefaultACLsObject.objtype: %d",
+ (int) object->objtype);
+ }
+
+ foreach(privcell, object->privileges)
+ {
+ DefaultACLsPriv *privs = (DefaultACLsPriv *) lfirst(privcell);
+
+ iacls.grantees = NIL;
+ iacls.grant_option = privs->grant_option;
+
+ foreach(cell, privs->grantees)
+ {
+ PrivGrantee *grantee = (PrivGrantee *) lfirst(cell);
+
+ if (grantee->rolname == NULL)
+ iacls.grantees = lappend_oid(iacls.grantees, ACL_ID_PUBLIC);
+ else
+ iacls.grantees =
+ lappend_oid(iacls.grantees,
+ get_roleid_checked(grantee->rolname));
+ }
+
+ /*
+ * Convert DefaultACLsPriv->privileges, a list of privilege strings,
+ * into an AclMode bitmask.
+ */
+ if (privs->privileges == NIL)
+ {
+ iacls.privileges = all_privileges;
+ }
+ else
+ {
+ iacls.privileges = ACL_NO_RIGHTS;
+
+ foreach(cell, privs->privileges)
+ {
+ char *priv_name = (char *) lfirst(cell);
+ AclMode priv;
+
+ priv = string_to_privilege(priv_name);
+
+ if (priv & ~((AclMode) all_privileges))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg(errormsg, privilege_to_string(priv))));
+
+ iacls.privileges |= priv;
+ }
+ }
+
+ /* Apply the privileges */
+ SetDefaultACLs(&iacls);
+
+ /* If this is SET command chnage it to ADD for subsequent privileges */
+ if (iacls.action == 0)
+ iacls.action = 1;
+ }
+ }
+
+ ReleaseSysCache(tuple);
+
+ heap_close(relation, RowExclusiveLock);
+ }
+
+
+ /*
+ * Create or update the DefaultACL entry
+ */
+ void
+ SetDefaultACLs(InternalDefaultACLs *iacls)
+ {
+ Form_pg_namespace_default_acl pg_namespace_default_acl_tuple;
+ char objtype;
+ GrantObjectType aclobjtype;
+ Datum aclDatum;
+ bool isNull;
+ bool isNew;
+ ScanKeyData key[2];
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ Relation rel;
+ Acl *old_acl;
+ Acl *base_acl;
+ Acl *new_acl;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_namespace_default_acl];
+ bool nulls[Natts_pg_namespace_default_acl];
+ bool replaces[Natts_pg_namespace_default_acl];
+ int nbasemembers;
+ int nnewmembers;
+ Oid *basemembers;
+ Oid *newmembers;
+
+ /*
+ * Convert ACL object type to DefaultACLs object type
+ */
+ switch (iacls->objtype)
+ {
+ case NSP_ACL_OBJ_RELATION:
+ objtype = NSPDEFACLOBJ_TABLE;
+ aclobjtype = ACL_OBJECT_RELATION;
+ break;
+
+ case NSP_ACL_OBJ_VIEW:
+ objtype = NSPDEFACLOBJ_VIEW;
+ aclobjtype = ACL_OBJECT_RELATION;
+ break;
+
+ case NSP_ACL_OBJ_FUNCTION:
+ objtype = NSPDEFACLOBJ_FUNCTION;
+ aclobjtype = ACL_OBJECT_FUNCTION;
+ break;
+
+ case NSP_ACL_OBJ_SEQUENCE:
+ objtype = NSPDEFACLOBJ_SEQUENCE;
+ aclobjtype = ACL_OBJECT_SEQUENCE;
+ break;
+
+ default:
+ elog(ERROR, "unrecognized objtype: %d",
+ (int) iacls->objtype);
+ objtype = 0; /* keep compiler quiet */
+ break;
+ }
+
+ /* Search for existing row for our object type in catalog */
+ ScanKeyInit(&key[0],
+ Anum_pg_namespace_default_acl_defaclnamespace,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(iacls->nspId));
+
+ ScanKeyInit(&key[1],
+ Anum_pg_namespace_default_acl_defaclgrantobjtype,
+ BTEqualStrategyNumber, F_CHAREQ,
+ CharGetDatum(objtype));
+
+ rel = heap_open(NamespaceDefaultAclRelationId, RowExclusiveLock);
+
+ scan = heap_beginscan(rel, SnapshotNow, 2, key);
+
+ tuple = heap_getnext(scan, ForwardScanDirection);
+ if (HeapTupleIsValid(tuple))
+ {
+ pg_namespace_default_acl_tuple = (Form_pg_namespace_default_acl) GETSTRUCT(tuple);
+ aclDatum = heap_getattr(tuple, Anum_pg_namespace_default_acl_defacldefacllist,
+ RelationGetDescr(rel), &isNull);
+ isNew = false;
+ }
+ else
+ {
+ isNull = true;
+ isNew = true;
+ }
+
+ /*
+ * If there are no default privileges for current schema and object type
+ * or if we are SETting the privileges, use the default privileges
+ * based on object type.
+ */
+ if (isNull)
+ old_acl = acldefault(aclobjtype, iacls->nspOwnerId);
+ else
+ old_acl = DatumGetAclPCopy(aclDatum);
+
+ /* we need to keep old_acl for later use */
+ if (!isNull && iacls->action == 0)
+ base_acl = acldefault(aclobjtype, iacls->nspOwnerId);
+ else
+ base_acl = old_acl;
+
+ nbasemembers = aclmembers(base_acl, &basemembers);
+
+ /*
+ * Generate new ACL.
+ *
+ * We need the members of both old and new ACLs so we can correct
+ * the shared dependency information.
+ */
+ new_acl = merge_acl_with_grant(base_acl,
+ iacls->action != -1,
+ iacls->grant_option,
+ DROP_RESTRICT, // this shouldn't affect us at all
+ iacls->grantees,
+ iacls->privileges,
+ GetUserId(),
+ iacls->nspOwnerId);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ if (isNew)
+ {
+ ObjectAddress myself,
+ referenced;
+
+ values[Anum_pg_namespace_default_acl_defaclnamespace - 1] = ObjectIdGetDatum(iacls->nspId);
+ values[Anum_pg_namespace_default_acl_defaclgrantobjtype - 1] = CharGetDatum(objtype);
+ values[Anum_pg_namespace_default_acl_defacldefacllist - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_form_tuple(rel->rd_att, values, nulls);
+ simple_heap_insert(rel, newtuple);
+
+ myself.classId = NamespaceDefaultAclRelationId;
+ myself.objectId = HeapTupleGetOid(newtuple);
+ myself.objectSubId = 0;
+
+ /* dependency on namespace */
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = iacls->nspId;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+ else
+ {
+ replaces[Anum_pg_namespace_default_acl_defacldefacllist - 1] = true;
+ values[Anum_pg_namespace_default_acl_defacldefacllist - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
+ values, nulls, replaces);
+ simple_heap_update(rel, &newtuple->t_self, newtuple);
+ }
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(rel, newtuple);
+
+ /*
+ * Update the shared dependency ACL info
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* remove old ACL depencencies first when using SET */
+ if (!isNull && iacls->action == 0)
+ {
+ Acl *tmp_acl;
+ int noldmembers;
+ int ntmpmembers;
+ Oid *oldmembers;
+ Oid *tmpmembers;
+
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
+ tmp_acl = merge_acl_with_grant(old_acl,
+ false,
+ iacls->grant_option,
+ DROP_RESTRICT, // this shouldn't affect us at all
+ iacls->grantees,
+ iacls->privileges,
+ GetUserId(),
+ iacls->nspOwnerId);
+
+ ntmpmembers = aclmembers(tmp_acl, &tmpmembers);
+
+ updateAclDependencies(NamespaceDefaultAclRelationId, HeapTupleGetOid(newtuple), 0,
+ iacls->nspOwnerId, false,
+ noldmembers, oldmembers,
+ ntmpmembers, tmpmembers);
+
+ pfree(tmp_acl);
+ }
+
+ updateAclDependencies(NamespaceDefaultAclRelationId, HeapTupleGetOid(newtuple), 0,
+ iacls->nspOwnerId, iacls->action != -1,
+ nbasemembers, basemembers,
+ nnewmembers, newmembers);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+
+ heap_endscan(scan);
+ heap_close(rel, RowExclusiveLock);
+ }
+
+
+ /*
+ * Remove DefaultACLs tuple.
+ */
+ void
+ RemoveDefaultACLsById(Oid nsdaOid)
+ {
+ Relation rel;
+ ScanKeyData key[1];
+ HeapScanDesc scan;
+ HeapTuple tup;
+
+ rel = heap_open(NamespaceDefaultAclRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(nsdaOid));
+
+ scan = heap_beginscan(rel, SnapshotNow, 1, key);
+
+ tup = heap_getnext(scan, ForwardScanDirection);
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "could not find tuple for default acls %u",
+ nsdaOid);
+
+ simple_heap_delete(rel, &tup->t_self);
+
+ heap_endscan(scan);
+ heap_close(rel, RowExclusiveLock);
+ }
+
+
+
/*
* RemoveSchemas
* Implements DROP SCHEMA.
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 1976648..1d69e93 100644
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyCreateSchemaStmt(CreateSchemaStmt *
*** 3224,3229 ****
--- 3224,3241 ----
return newnode;
}
+ static DefaultACLsStmt *
+ _copyDefaultACLsStmt(DefaultACLsStmt *from)
+ {
+ DefaultACLsStmt *newnode = makeNode(DefaultACLsStmt);
+
+ COPY_STRING_FIELD(schemaname);
+ COPY_SCALAR_FIELD(action);
+ COPY_NODE_FIELD(objlist);
+
+ return newnode;
+ }
+
static CreateConversionStmt *
_copyCreateConversionStmt(CreateConversionStmt *from)
{
*************** copyObject(void *from)
*** 3966,3971 ****
--- 3978,3986 ----
case T_CreateSchemaStmt:
retval = _copyCreateSchemaStmt(from);
break;
+ case T_DefaultACLsStmt:
+ retval = _copyDefaultACLsStmt(from);
+ break;
case T_CreateConversionStmt:
retval = _copyCreateConversionStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 8b466f4..4aac71d 100644
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalCreateSchemaStmt(CreateSchemaStmt
*** 1761,1766 ****
--- 1761,1776 ----
}
static bool
+ _equalDefaultACLsStmt(DefaultACLsStmt *a, DefaultACLsStmt *b)
+ {
+ COMPARE_STRING_FIELD(schemaname);
+ COMPARE_SCALAR_FIELD(action);
+ COMPARE_NODE_FIELD(objlist);
+
+ return true;
+ }
+
+ static bool
_equalCreateConversionStmt(CreateConversionStmt *a, CreateConversionStmt *b)
{
COMPARE_NODE_FIELD(conversion_name);
*************** equal(void *a, void *b)
*** 2743,2748 ****
--- 2753,2761 ----
case T_CreateSchemaStmt:
retval = _equalCreateSchemaStmt(a, b);
break;
+ case T_DefaultACLsStmt:
+ retval = _equalDefaultACLsStmt(a, b);
+ break;
case T_CreateConversionStmt:
retval = _equalCreateConversionStmt(a, b);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 858e16c..8cdbb11 100644
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static TypeName *TableFuncTypeName(List
*** 179,184 ****
--- 179,187 ----
ResTarget *target;
struct PrivTarget *privtarget;
AccessPriv *accesspriv;
+ ACLSchemaObjectType aclschemaobjecttype;
+ DefaultACLsObject *defaultaclsobject;
+ DefaultACLsPriv *defaultaclspriv;
InsertStmt *istmt;
VariableSetStmt *vsetstmt;
*************** static TypeName *TableFuncTypeName(List
*** 196,202 ****
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
! CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
--- 199,205 ----
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
! CreatedbStmt DeclareCursorStmt DefaultACLsStmt DefaultACLsDropStmt DefineStmt DeleteStmt DiscardStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
*************** static TypeName *TableFuncTypeName(List
*** 218,224 ****
simple_select values_clause
%type alter_column_default opclass_item opclass_drop alter_using
! %type add_drop opt_asc_desc opt_nulls_order
%type alter_table_cmd
%type alter_table_cmds
--- 221,227 ----
simple_select values_clause
%type alter_column_default opclass_item opclass_drop alter_using
! %type add_drop set_add opt_asc_desc opt_nulls_order
%type alter_table_cmd
%type alter_table_cmds
*************** static TypeName *TableFuncTypeName(List
*** 233,239 ****
%type opt_lock lock_type cast_context
%type opt_force opt_or_replace
opt_grant_grant_option opt_grant_admin_option
! opt_nowait opt_if_exists opt_with_data
%type OptRoleList
%type OptRoleElem
--- 236,242 ----
%type opt_lock lock_type cast_context
%type opt_force opt_or_replace
opt_grant_grant_option opt_grant_admin_option
! opt_nowait opt_if_exists opt_revoke_grant_option opt_with_data
%type OptRoleList
%type OptRoleElem
*************** static TypeName *TableFuncTypeName(List
*** 266,273 ****
%type grantee
%type grantee_list
%type privilege
! %type privileges privilege_list
%type privilege_target
%type function_with_argtypes
%type function_with_argtypes_list
--- 269,282 ----
%type grantee
%type grantee_list
%type privilege
! %type default_acls_object_list default_acls_object_list_drop
! default_acls_priv_list default_acls_priv_list_drop default_priv_list
! default_priv_type privileges privilege_list
%type privilege_target
+ %type default_priv_target
+ %type default_acls_object default_acls_object_drop
+ %type default_acls_priv default_acls_priv_drop
+ %type default_priv
%type function_with_argtypes
%type function_with_argtypes_list
*************** stmt :
*** 664,669 ****
--- 673,680 ----
| CreatedbStmt
| DeallocateStmt
| DeclareCursorStmt
+ | DefaultACLsStmt
+ | DefaultACLsDropStmt
| DefineStmt
| DeleteStmt
| DiscardStmt
*************** schema_stmt:
*** 1105,1110 ****
--- 1116,1251 ----
;
+
+ /*****************************************************************************
+ *
+ * DEFAULT PRIVILEGES for newly created objects in schema
+ *
+ *****************************************************************************/
+
+ DefaultACLsStmt:
+ ALTER SCHEMA name set_add DEFAULT PRIVILEGES
+ default_acls_object_list
+ {
+ DefaultACLsStmt *n = makeNode(DefaultACLsStmt);
+ n->schemaname = $3;
+ n->action = $4;
+ n->objlist = $7;
+ $$ = (Node*)n;
+ }
+ ;
+
+ DefaultACLsDropStmt:
+ ALTER SCHEMA name DROP DEFAULT PRIVILEGES
+ default_acls_object_list_drop
+ {
+ DefaultACLsStmt *n = makeNode(DefaultACLsStmt);
+ n->schemaname = $3;
+ n->action = -1;
+ n->objlist = $7;
+ $$ = (Node*)n;
+ }
+ ;
+
+ set_add: SET { $$ = 0; }
+ | ADD_P { $$ = +1; }
+ ;
+
+ default_acls_object:
+ ON default_priv_target default_acls_priv_list
+ {
+ DefaultACLsObject *n = makeNode(DefaultACLsObject);
+ n->objtype = $2;
+ n->privileges = $3;
+ $$ = n;
+ }
+ ;
+
+ default_acls_object_list:
+ default_acls_object { $$ = list_make1($1); }
+ | default_acls_object_list default_acls_object { $$ = lappend($1, $2); }
+ ;
+
+ default_acls_priv:
+ default_priv_type TO grantee_list opt_grant_grant_option
+ {
+ DefaultACLsPriv *n = makeNode(DefaultACLsPriv);
+ n->privileges = $1;
+ n->grantees = $3;
+ n->grant_option = $4;
+ $$ = n;
+ }
+ ;
+
+ default_acls_priv_list:
+ default_acls_priv { $$ = list_make1($1); }
+ | default_acls_priv_list AND default_acls_priv { $$ = lappend($1, $3); }
+ ;
+
+ default_acls_object_drop:
+ ON default_priv_target default_acls_priv_list_drop
+ {
+ DefaultACLsObject *n = makeNode(DefaultACLsObject);
+ n->objtype = $2;
+ n->privileges = $3;
+ $$ = n;
+ }
+ ;
+
+ default_acls_object_list_drop:
+ default_acls_object_drop { $$ = list_make1($1); }
+ | default_acls_object_list_drop default_acls_object_drop { $$ = lappend($1, $2); }
+ ;
+
+ default_acls_priv_drop:
+ opt_revoke_grant_option default_priv_type FROM grantee_list
+ {
+ DefaultACLsPriv *n = makeNode(DefaultACLsPriv);
+ n->privileges = $2;
+ n->grantees = $4;
+ n->grant_option = $1;
+ $$ = n;
+ }
+ ;
+
+ default_acls_priv_list_drop:
+ default_acls_priv_drop { $$ = list_make1($1); }
+ | default_acls_priv_list_drop AND default_acls_priv_drop { $$ = lappend($1, $3); }
+ ;
+
+ default_priv_type:
+ default_priv_list
+ { $$ = $1; }
+ | ALL
+ { $$ = NIL; }
+ | ALL PRIVILEGES
+ { $$ = NIL; }
+ ;
+
+ default_priv_list:
+ default_priv { $$ = list_make1($1); }
+ | default_priv_list ',' default_priv { $$ = lappend($1, $3); }
+ ;
+
+ default_priv:
+ SELECT { $$ = pstrdup($1); }
+ | REFERENCES { $$ = pstrdup($1); }
+ | CREATE { $$ = pstrdup($1); }
+ | ColId { $$ = $1; }
+ ;
+
+ default_priv_target:
+ TABLE { $$ = NSP_ACL_OBJ_RELATION; }
+ | VIEW { $$ = NSP_ACL_OBJ_VIEW; }
+ | FUNCTION { $$ = NSP_ACL_OBJ_FUNCTION; }
+ | SEQUENCE { $$ = NSP_ACL_OBJ_SEQUENCE; }
+ ;
+
+ opt_revoke_grant_option:
+ GRANT OPTION FOR { $$ = TRUE; }
+ | /*EMPTY*/ { $$ = FALSE; }
+ ;
+
/*****************************************************************************
*
* Set PG internal variable
*************** GrantStmt: GRANT privileges ON privilege
*** 4238,4243 ****
--- 4379,4395 ----
n->grant_option = $7;
$$ = (Node*)n;
}
+ | GRANT DEFAULT PRIVILEGES ON privilege_target
+ {
+ GrantStmt *n = makeNode(GrantStmt);
+ n->is_grant = true;
+ n->privileges = NIL;
+ n->objtype = ($5)->objtype;
+ n->objects = ($5)->objs;
+ n->grantees = NIL;
+ n->grant_option = FALSE;
+ $$ = (Node*)n;
+ }
;
RevokeStmt:
*************** privilege_target:
*** 4354,4359 ****
--- 4506,4518 ----
n->objs = $2;
$$ = n;
}
+ | VIEW qualified_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->objtype = ACL_OBJECT_RELATION;
+ n->objs = $2;
+ $$ = n;
+ }
| SEQUENCE qualified_name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 54cc9e9..57f0230 100644
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
*************** typedef struct
*** 91,96 ****
--- 91,104 ----
List *grants; /* GRANT items */
} CreateSchemaStmtContext;
+ /* State for transformDefaultACLsStmt */
+ typedef struct
+ {
+ DefaultACLsObject *table; /* TABLE permissions */
+ DefaultACLsObject *view; /* VIEW permissions */
+ DefaultACLsObject *function; /* FUNCTION permissions */
+ DefaultACLsObject *sequence; /* SEQUENCE permissions */
+ } DefaultACLsStmtContext;
static void transformColumnDefinition(ParseState *pstate,
CreateStmtContext *cxt,
*************** static void transformFKConstraints(Parse
*** 114,119 ****
--- 122,128 ----
static void transformConstraintAttrs(List *constraintList);
static void transformColumnType(ParseState *pstate, ColumnDef *column);
static void setSchemaName(char *context_schema, char **stmt_schema_name);
+ static DefaultACLsObject *mergeDefaultACLsObjects(DefaultACLsObject *a, DefaultACLsObject *b);
/*
*************** setSchemaName(char *context_schema, char
*** 2115,2117 ****
--- 2124,2203 ----
"different from the one being created (%s)",
*stmt_schema_name, context_schema)));
}
+
+
+ /*
+ * transformDefaultACLsStmt
+ * merge duplicate DefaultACLsObjects
+ *
+ * We also check if we are setting default ACLs only for supported objects,
+ * so that we can use functions from aclchk.c which allow more object types.
+ */
+ List *
+ transformDefaultACLsStmt(DefaultACLsStmt *stmt)
+ {
+ DefaultACLsStmtContext cxt;
+ ListCell *object_cell;
+ List *result;
+
+ cxt.table = NULL;
+ cxt.view = NULL;
+ cxt.function = NULL;
+ cxt.sequence = NULL;
+
+ foreach(object_cell, stmt->objlist)
+ {
+ DefaultACLsObject *object = (DefaultACLsObject *) lfirst(object_cell);
+
+ switch (object->objtype)
+ {
+ case NSP_ACL_OBJ_RELATION:
+ cxt.table = mergeDefaultACLsObjects(cxt.table, object);
+ break;
+
+ case NSP_ACL_OBJ_VIEW:
+ cxt.view = mergeDefaultACLsObjects(cxt.view, object);
+ break;
+
+ case NSP_ACL_OBJ_FUNCTION:
+ cxt.function = mergeDefaultACLsObjects(cxt.function, object);
+ break;
+
+ case NSP_ACL_OBJ_SEQUENCE:
+ cxt.sequence = mergeDefaultACLsObjects(cxt.sequence, object);
+ break;
+
+ default:
+ elog(ERROR, "unrecognized objtype: %d",
+ (int) object->objtype);
+ }
+ }
+
+ result = NIL;
+ result = lappend(result, cxt.table);
+ result = lappend(result, cxt.view);
+ result = lappend(result, cxt.function);
+ result = lappend(result, cxt.sequence);
+
+ return result;
+ }
+
+
+ static DefaultACLsObject*
+ mergeDefaultACLsObjects(DefaultACLsObject *a, DefaultACLsObject *b)
+ {
+ if (a == NULL)
+ return b;
+ if (b == NULL)
+ return a;
+
+ /* sanity checks */
+ if (a == b)
+ elog(ERROR, "cannot merge DefaultACLsObject to itself");
+
+ Assert(a->objtype == b->objtype);
+
+ a->privileges = list_concat(a->privileges, b->privileges);
+
+ return a;
+ }
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index c9f2b87..5f00fc7 100644
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
*************** check_xact_readonly(Node *parsetree)
*** 180,185 ****
--- 180,186 ----
case T_AlterOpFamilyStmt:
case T_RuleStmt:
case T_CreateSchemaStmt:
+ case T_DefaultACLsStmt:
case T_CreateSeqStmt:
case T_CreateStmt:
case T_CreateTableSpaceStmt:
*************** ProcessUtility(Node *parsetree,
*** 406,411 ****
--- 407,416 ----
queryString);
break;
+ case T_DefaultACLsStmt:
+ DefaultACLsCommand((DefaultACLsStmt *) parsetree);
+ break;
+
case T_CreateStmt:
{
List *stmts;
*************** CreateCommandTag(Node *parsetree)
*** 1362,1367 ****
--- 1367,1376 ----
tag = "CREATE SCHEMA";
break;
+ case T_DefaultACLsStmt:
+ tag = "ALTER SCHEMA";
+ break;
+
case T_CreateStmt:
tag = "CREATE TABLE";
break;
*************** GetCommandLogLevel(Node *parsetree)
*** 2131,2136 ****
--- 2140,2149 ----
lev = LOGSTMT_DDL;
break;
+ case T_DefaultACLsStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_CreateStmt:
lev = LOGSTMT_DDL;
break;
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index dbd3619..7c56b6f 100644
*** a/src/backend/utils/cache/syscache.c
--- b/src/backend/utils/cache/syscache.c
***************
*** 36,41 ****
--- 36,42 ----
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
+ #include "catalog/pg_namespace_default_acl.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index a2f6761..246a113 100644
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
*************** typedef enum ObjectClass
*** 146,151 ****
--- 146,152 ----
OCLASS_FDW, /* pg_foreign_data_wrapper */
OCLASS_FOREIGN_SERVER, /* pg_foreign_server */
OCLASS_USER_MAPPING, /* pg_user_mapping */
+ OCLASS_NSPDEFACLS, /* pg_namespace_default_acl */
MAX_OCLASS /* MUST BE LAST */
} ObjectClass;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 81e18a1..a4b9d48 100644
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
*************** DECLARE_UNIQUE_INDEX(pg_user_mapping_oid
*** 267,272 ****
--- 267,278 ----
DECLARE_UNIQUE_INDEX(pg_user_mapping_user_server_index, 175, on pg_user_mapping using btree(umuser oid_ops, umserver oid_ops));
#define UserMappingUserServerIndexId 175
+ DECLARE_UNIQUE_INDEX(pg_namespace_default_acl_nspobj_index, 626, on pg_namespace_default_acl using btree(defaclnamespace oid_ops, defaclobjtype char_ops));
+ #define NamespaceDefaultAclNspObjIndexId 626
+ DECLARE_UNIQUE_INDEX(pg_namespace_default_acl_oid_index, 627, on pg_namespace_default_acl using btree(oid oid_ops));
+ #define NamespaceDefaultAclOidIndexId 627
+
+
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
diff --git a/src/include/catalog/pg_namespace_default_acl.h b/src/include/catalog/pg_namespace_default_acl.h
index ...f969a8d .
*** a/src/include/catalog/pg_namespace_default_acl.h
--- b/src/include/catalog/pg_namespace_default_acl.h
***************
*** 0 ****
--- 1,79 ----
+ /*-------------------------------------------------------------------------
+ *
+ * pg_namespace_default_acl.h
+ * definition of default ACLs for new objects created in schemas
+ * Specified by schema and grantable object type
+ *
+ *
+ * Copyright (c) 2009, PostgreSQL Global Development Group
+ *
+ * $Id$
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef PG_NAMESPACE_DEFAULT_ACL_H
+ #define PG_NAMESPACE_DEFAULT_ACL_H
+
+ #include "catalog/genbki.h"
+
+ /* ----------------
+ * pg_namespace_default_acl definition. cpp turns this into
+ * typedef struct FormData_pg_namespace_default_acl
+ * ----------------
+ */
+ #define NamespaceDefaultAclRelationId 1248
+
+ CATALOG(pg_namespace_default_acl,1248) /*BKI_BOOTSTRAP*/
+ {
+ Oid defaclnamespace; /* OID of namespace these ACLs apply to */
+ char defaclobjtype; /* see DEFACL_OBJECT_xxx constants below */
+
+ /*
+ * VARIABLE LENGTH FIELDS start here. These fields may be NULL, too.
+ */
+
+ aclitem defacllist[1]; /* Permissions to be applied at CREATE time */
+ } FormData_pg_namespace_default_acl;
+
+ /* Size of fixed part of pg_namespace_default_acl tuples, not counting var-length fields */
+ #define NAMESPACE_DEFAULT_ACL_TUPLE_SIZE \
+ (offsetof(FormData_pg_namespace_default_acl,relfrozenxid) + sizeof(TransactionId))
+
+ /* ----------------
+ * Form_pg_namespace_default_acl corresponds to a pointer to a tuple with
+ * the format of pg_namespace_default_acl relation.
+ * ----------------
+ */
+ typedef FormData_pg_namespace_default_acl *Form_pg_namespace_default_acl;
+
+ /* ----------------
+ * compiler constants for pg_namespace_default_acl
+ * ----------------
+ */
+
+ #define Natts_pg_namespace_default_acl 3
+ #define Anum_pg_namespace_default_acl_defaclnamespace 1
+ #define Anum_pg_namespace_default_acl_defaclgrantobjtype 2
+ #define Anum_pg_namespace_default_acl_defacldefacllist 3
+
+ /* ----------------
+ * pg_namespace_default_acl has no initial contents
+ * ----------------
+ */
+
+ /*
+ * Types of objects which the user is allowed to specify default
+ * permissions on through pg_namespace_default_acl.
+ * Must have same value as relkind for objects stored in pg_class !
+ */
+
+ #define NSPDEFACLOBJ_TABLE 'r' /* table */
+ #define NSPDEFACLOBJ_VIEW 'v' /* view */
+ #define NSPDEFACLOBJ_FUNCTION 'f' /* function */
+ #define NSPDEFACLOBJ_SEQUENCE 'S' /* sequence */
+
+ #endif /* PG_NAMESPACE_DEFAULT_ACL_H */
diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h
index 5f384a1..1067bee 100644
*** a/src/include/commands/schemacmds.h
--- b/src/include/commands/schemacmds.h
***************
*** 20,25 ****
--- 20,28 ----
extern void CreateSchemaCommand(CreateSchemaStmt *parsetree,
const char *queryString);
+ extern void DefaultACLsCommand(DefaultACLsStmt *parsetree);
+ extern void RemoveDefaultACLsById(Oid nsdaOid);
+
extern void RemoveSchemas(DropStmt *drop);
extern void RemoveSchemaById(Oid schemaOid);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 925375b..5ac8538 100644
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 337,342 ****
--- 337,343 ----
T_CreateUserMappingStmt,
T_AlterUserMappingStmt,
T_DropUserMappingStmt,
+ T_DefaultACLsStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*************** typedef enum NodeTag
*** 376,381 ****
--- 377,385 ----
T_XmlSerialize,
T_WithClause,
T_CommonTableExpr,
+ T_DefaultACLsObject,
+ T_DefaultACLsPriv,
+
/*
* TAGS FOR RANDOM OTHER STUFF
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 9d53ab9..1452d9f 100644
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef enum DropBehavior
*** 1079,1084 ****
--- 1079,1085 ----
DROP_CASCADE /* remove dependent objects too */
} DropBehavior;
+
/* ----------------------
* Alter Table
* ----------------------
*************** typedef struct GrantRoleStmt
*** 1257,1262 ****
--- 1258,1300 ----
DropBehavior behavior; /* drop behavior (for REVOKE) */
} GrantRoleStmt;
+
+ /* ----------------------
+ * Alter Schema DEFAULT PRIVILEGES
+ * ----------------------
+ */
+ typedef enum ACLSchemaObjectType
+ {
+ NSP_ACL_OBJ_RELATION, /* table */
+ NSP_ACL_OBJ_VIEW, /* view */
+ NSP_ACL_OBJ_FUNCTION, /* function */
+ NSP_ACL_OBJ_SEQUENCE /* sequence */
+ } ACLSchemaObjectType;
+
+ typedef struct DefaultACLsStmt
+ {
+ NodeTag type;
+ char *schemaname;
+ int action;
+ List *objlist;
+ } DefaultACLsStmt;
+
+ typedef struct DefaultACLsObject
+ {
+ NodeTag type;
+ ACLSchemaObjectType objtype;
+ List *privileges;
+ } DefaultACLsObject;
+
+ typedef struct DefaultACLsPriv
+ {
+ NodeTag type;
+ List *privileges;
+ List *grantees;
+ bool grant_option;
+ } DefaultACLsPriv;
+
+
/* ----------------------
* Copy Statement
*
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 1fc3efd..98c13c4 100644
*** a/src/include/parser/parse_utilcmd.h
--- b/src/include/parser/parse_utilcmd.h
*************** extern IndexStmt *transformIndexStmt(Ind
*** 24,28 ****
--- 24,29 ----
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt);
+ extern List *transformDefaultACLsStmt(DefaultACLsStmt *stmt);
#endif /* PARSE_UTILCMD_H */
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index bde8727..b12e590 100644
*** a/src/include/utils/acl.h
--- b/src/include/utils/acl.h
*************** extern Datum hash_aclitem(PG_FUNCTION_AR
*** 262,267 ****
--- 262,274 ----
*/
extern void ExecuteGrantStmt(GrantStmt *stmt);
extern void ExecGrantStmt_oids(InternalGrant *istmt);
+ extern AclMode string_to_privilege(const char *privname);
+ extern const char *privilege_to_string(AclMode privilege);
+ extern Acl *merge_acl_with_grant(Acl *old_acl, bool is_grant,
+ bool grant_option, DropBehavior behavior,
+ List *grantees, AclMode privileges,
+ Oid grantorId, Oid ownerId);
+
extern AclMode pg_attribute_aclmask(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mask, AclMaskHow how);
*************** extern bool pg_ts_dict_ownercheck(Oid di
*** 317,320 ****
--- 324,329 ----
extern bool pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid);
extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid);
+ extern Acl *pg_namespace_object_default_acl(Oid nsp_oid, char objtype, bool *isNull);
+
#endif /* ACL_H */
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index a17ff59..2fa466f 100644
*** a/src/test/regress/expected/privileges.out
--- b/src/test/regress/expected/privileges.out
*************** SELECT has_table_privilege('regressuser1
*** 815,822 ****
--- 815,945 ----
t
(1 row)
+ -- Default ACLs
+ \c -
+ CREATE SCHEMA regressns;
+ GRANT USAGE ON SCHEMA regressns TO regressgroup1, regressuser2;
+ -- bad input
+ ALTER SCHEMA regressns SET DEFAULT PRIVILEGES ON FUNCTION USAGE TO regressgroup1;
+ ERROR: invalid privilege type USAGE for function
+ -- try different possible syntaxes from simple ones to more complex ones
+ ALTER SCHEMA regressns SET DEFAULT PRIVILEGES ON TABLE SELECT TO public;
+ CREATE TABLE regressns.acltest1 (one int, two int, three int);
+ SELECT has_table_privilege('regressuser1', 'regressns.acltest1', 'INSERT'); -- false
+ has_table_privilege
+ ---------------------
+ f
+ (1 row)
+
+ SELECT has_table_privilege('regressuser1', 'regressns.acltest1', 'SELECT'); -- true
+ has_table_privilege
+ ---------------------
+ t
+ (1 row)
+
+ ALTER SCHEMA regressns SET DEFAULT PRIVILEGES ON TABLE SELECT TO regressgroup1, regressuser2 AND UPDATE TO regressuser2 AND INSERT, DELETE TO regressuser2;
+ CREATE TABLE regressns.acltest2 (one int, two int, three int);
+ SELECT has_table_privilege('regressuser2', 'regressns.acltest2', 'DELETE'); -- true
+ has_table_privilege
+ ---------------------
+ t
+ (1 row)
+
+ SELECT has_table_privilege('regressuser4', 'regressns.acltest2', 'INSERT'); -- false
+ has_table_privilege
+ ---------------------
+ f
+ (1 row)
+
+ SELECT has_table_privilege('regressuser4', 'regressns.acltest2', 'SELECT'); -- true (as member of regressgroup1)
+ has_table_privilege
+ ---------------------
+ t
+ (1 row)
+
+ ALTER SCHEMA regressns ADD DEFAULT PRIVILEGES ON TABLE INSERT TO regressgroup1;
+ CREATE TABLE regressns.acltest3 (foo text);
+ SELECT has_table_privilege('regressuser4', 'regressns.acltest3', 'INSERT'); -- true
+ has_table_privilege
+ ---------------------
+ t
+ (1 row)
+
+ ALTER SCHEMA regressns DROP DEFAULT PRIVILEGES ON TABLE DELETE FROM regressuser2;
+ -- supress implicit sequence creation NOTICE
+ SET client_min_messages TO 'error';
+ CREATE TABLE regressns.acltest4 (bar serial);
+ RESET client_min_messages;
+ SELECT has_table_privilege('regressuser2', 'regressns.acltest4', 'DELETE'); -- false
+ has_table_privilege
+ ---------------------
+ f
+ (1 row)
+
+ SET SESSION AUTHORIZATION regressuser2;
+ SELECT nextval(pg_get_serial_sequence('regressns.acltest4', 'bar')::regclass); -- error
+ ERROR: permission denied for sequence acltest4_bar_seq
+ RESET SESSION AUTHORIZATION;
+ ALTER SCHEMA regressns SET DEFAULT PRIVILEGES
+ ON TABLE SELECT TO regressgroup1, regressuser2 AND UPDATE,INSERT,DELETE TO regressuser2
+ ON VIEW SELECT TO regressgroup1 AND ALL PRIVILEGES TO regressuser2 WITH GRANT OPTION
+ ON SEQUENCE ALL TO regressgroup1, regressuser2
+ ON FUNCTION EXECUTE TO regressgroup1;
+ -- supress implicit sequence creation NOTICE
+ SET client_min_messages TO 'error';
+ CREATE TABLE regressns.acltest5 (a serial, b text);
+ RESET client_min_messages;
+ SELECT has_table_privilege('regressgroup1', 'regressns.acltest5', 'INSERT'); -- false
+ has_table_privilege
+ ---------------------
+ f
+ (1 row)
+
+ SELECT has_table_privilege('regressuser2', 'regressns.acltest5', 'INSERT'); -- true
+ has_table_privilege
+ ---------------------
+ t
+ (1 row)
+
+ SET SESSION AUTHORIZATION regressuser2;
+ SELECT nextval(pg_get_serial_sequence('regressns.acltest5', 'a')::regclass); -- 1
+ nextval
+ ---------
+ 1
+ (1 row)
+
+ RESET SESSION AUTHORIZATION;
+ CREATE FUNCTION regressns.testfunc1() RETURNS int AS 'select 1;' LANGUAGE sql;
+ SELECT has_function_privilege('regressgroup1', 'regressns.testfunc1()', 'EXECUTE'); -- true
+ has_function_privilege
+ ------------------------
+ t
+ (1 row)
+
+ CREATE VIEW regressns.acltest6 AS SELECT * FROM regressns.acltest4;
+ SET SESSION AUTHORIZATION regressuser2;
+ GRANT SELECT ON regressns.acltest6 TO regressuser1;
+ SELECT has_table_privilege('regressuser1', 'regressns.acltest6', 'SELECT'); -- true
+ has_table_privilege
+ ---------------------
+ t
+ (1 row)
+
-- clean up
\c
+ DROP SCHEMA regressns CASCADE;
+ NOTICE: drop cascades to 11 other objects
+ DETAIL: drop cascades to default acls in namespace regressns for new TABLE
+ drop cascades to table regressns.acltest1
+ drop cascades to table regressns.acltest2
+ drop cascades to table regressns.acltest3
+ drop cascades to table regressns.acltest4
+ drop cascades to default acls in namespace regressns for new VIEW
+ drop cascades to default acls in namespace regressns for new SEQUENCE
+ drop cascades to default acls in namespace regressns for new FUNCTION
+ drop cascades to table regressns.acltest5
+ drop cascades to function regressns.testfunc1()
+ drop cascades to view regressns.acltest6
DROP FUNCTION testfunc2(int);
DROP FUNCTION testfunc4(boolean);
DROP VIEW atestv1;
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index c6f1f15..37411f6 100644
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
*************** SELECT relname, relhasindex
*** 9,157 ****
FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace
WHERE relkind = 'r' AND (nspname ~ '^pg_temp_') IS NOT TRUE
ORDER BY relname;
! relname | relhasindex
! -------------------------+-------------
! a | f
! a_star | f
! abstime_tbl | f
! aggtest | f
! array_index_op_test | t
! array_op_test | f
! b | f
! b_star | f
! box_tbl | f
! bprime | f
! bt_f8_heap | t
! bt_i4_heap | t
! bt_name_heap | t
! bt_txt_heap | t
! c | f
! c_star | f
! char_tbl | f
! check2_tbl | f
! check_tbl | f
! circle_tbl | t
! city | f
! copy_tbl | f
! d | f
! d_star | f
! date_tbl | f
! default_tbl | f
! defaultexpr_tbl | f
! dept | f
! e_star | f
! emp | f
! equipment_r | f
! f_star | f
! fast_emp4000 | t
! float4_tbl | f
! float8_tbl | f
! func_index_heap | t
! hash_f8_heap | t
! hash_i4_heap | t
! hash_name_heap | t
! hash_txt_heap | t
! hobbies_r | f
! ihighway | t
! inet_tbl | f
! inhe | f
! inhf | f
! inhx | t
! insert_tbl | f
! int2_tbl | f
! int4_tbl | f
! int8_tbl | f
! interval_tbl | f
! iportaltest | f
! log_table | f
! lseg_tbl | f
! main_table | f
! money_data | f
! num_data | f
! num_exp_add | t
! num_exp_div | t
! num_exp_ln | t
! num_exp_log10 | t
! num_exp_mul | t
! num_exp_power_10_ln | t
! num_exp_sqrt | t
! num_exp_sub | t
! num_input_test | f
! num_result | f
! onek | t
! onek2 | t
! path_tbl | f
! person | f
! pg_aggregate | t
! pg_am | t
! pg_amop | t
! pg_amproc | t
! pg_attrdef | t
! pg_attribute | t
! pg_auth_members | t
! pg_authid | t
! pg_cast | t
! pg_class | t
! pg_constraint | t
! pg_conversion | t
! pg_database | t
! pg_depend | t
! pg_description | t
! pg_enum | t
! pg_foreign_data_wrapper | t
! pg_foreign_server | t
! pg_index | t
! pg_inherits | t
! pg_language | t
! pg_largeobject | t
! pg_listener | f
! pg_namespace | t
! pg_opclass | t
! pg_operator | t
! pg_opfamily | t
! pg_pltemplate | t
! pg_proc | t
! pg_rewrite | t
! pg_shdepend | t
! pg_shdescription | t
! pg_statistic | t
! pg_tablespace | t
! pg_trigger | t
! pg_ts_config | t
! pg_ts_config_map | t
! pg_ts_dict | t
! pg_ts_parser | t
! pg_ts_template | t
! pg_type | t
! pg_user_mapping | t
! point_tbl | f
! polygon_tbl | t
! ramp | f
! real_city | f
! reltime_tbl | f
! road | t
! shighway | t
! slow_emp4000 | f
! sql_features | f
! sql_implementation_info | f
! sql_languages | f
! sql_packages | f
! sql_parts | f
! sql_sizing | f
! sql_sizing_profiles | f
! stud_emp | f
! student | f
! tenk1 | t
! tenk2 | t
! test_tsvector | f
! text_tbl | f
! time_tbl | f
! timestamp_tbl | f
! timestamptz_tbl | f
! timetz_tbl | f
! tinterval_tbl | f
! varchar_tbl | f
! (140 rows)
--
-- another sanity check: every system catalog that has OIDs should have
--- 9,158 ----
FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace
WHERE relkind = 'r' AND (nspname ~ '^pg_temp_') IS NOT TRUE
ORDER BY relname;
! relname | relhasindex
! --------------------------+-------------
! a | f
! a_star | f
! abstime_tbl | f
! aggtest | f
! array_index_op_test | t
! array_op_test | f
! b | f
! b_star | f
! box_tbl | f
! bprime | f
! bt_f8_heap | t
! bt_i4_heap | t
! bt_name_heap | t
! bt_txt_heap | t
! c | f
! c_star | f
! char_tbl | f
! check2_tbl | f
! check_tbl | f
! circle_tbl | t
! city | f
! copy_tbl | f
! d | f
! d_star | f
! date_tbl | f
! default_tbl | f
! defaultexpr_tbl | f
! dept | f
! e_star | f
! emp | f
! equipment_r | f
! f_star | f
! fast_emp4000 | t
! float4_tbl | f
! float8_tbl | f
! func_index_heap | t
! hash_f8_heap | t
! hash_i4_heap | t
! hash_name_heap | t
! hash_txt_heap | t
! hobbies_r | f
! ihighway | t
! inet_tbl | f
! inhe | f
! inhf | f
! inhx | t
! insert_tbl | f
! int2_tbl | f
! int4_tbl | f
! int8_tbl | f
! interval_tbl | f
! iportaltest | f
! log_table | f
! lseg_tbl | f
! main_table | f
! money_data | f
! num_data | f
! num_exp_add | t
! num_exp_div | t
! num_exp_ln | t
! num_exp_log10 | t
! num_exp_mul | t
! num_exp_power_10_ln | t
! num_exp_sqrt | t
! num_exp_sub | t
! num_input_test | f
! num_result | f
! onek | t
! onek2 | t
! path_tbl | f
! person | f
! pg_aggregate | t
! pg_am | t
! pg_amop | t
! pg_amproc | t
! pg_attrdef | t
! pg_attribute | t
! pg_auth_members | t
! pg_authid | t
! pg_cast | t
! pg_class | t
! pg_constraint | t
! pg_conversion | t
! pg_database | t
! pg_depend | t
! pg_description | t
! pg_enum | t
! pg_foreign_data_wrapper | t
! pg_foreign_server | t
! pg_index | t
! pg_inherits | t
! pg_language | t
! pg_largeobject | t
! pg_listener | f
! pg_namespace | t
! pg_namespace_default_acl | t
! pg_opclass | t
! pg_operator | t
! pg_opfamily | t
! pg_pltemplate | t
! pg_proc | t
! pg_rewrite | t
! pg_shdepend | t
! pg_shdescription | t
! pg_statistic | t
! pg_tablespace | t
! pg_trigger | t
! pg_ts_config | t
! pg_ts_config_map | t
! pg_ts_dict | t
! pg_ts_parser | t
! pg_ts_template | t
! pg_type | t
! pg_user_mapping | t
! point_tbl | f
! polygon_tbl | t
! ramp | f
! real_city | f
! reltime_tbl | f
! road | t
! shighway | t
! slow_emp4000 | f
! sql_features | f
! sql_implementation_info | f
! sql_languages | f
! sql_packages | f
! sql_parts | f
! sql_sizing | f
! sql_sizing_profiles | f
! stud_emp | f
! student | f
! tenk1 | t
! tenk2 | t
! test_tsvector | f
! text_tbl | f
! time_tbl | f
! timestamp_tbl | f
! timestamptz_tbl | f
! timetz_tbl | f
! tinterval_tbl | f
! varchar_tbl | f
! (141 rows)
--
-- another sanity check: every system catalog that has OIDs should have
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 5aa1012..eab3a3b 100644
*** a/src/test/regress/sql/privileges.sql
--- b/src/test/regress/sql/privileges.sql
*************** SELECT has_table_privilege('regressuser3
*** 469,478 ****
--- 469,542 ----
SELECT has_table_privilege('regressuser1', 'atest4', 'SELECT WITH GRANT OPTION'); -- true
+ -- Default ACLs
+
+ \c -
+
+ CREATE SCHEMA regressns;
+ GRANT USAGE ON SCHEMA regressns TO regressgroup1, regressuser2;
+
+ -- bad input
+
+ ALTER SCHEMA regressns SET DEFAULT PRIVILEGES ON FUNCTION USAGE TO regressgroup1;
+
+ -- try different possible syntaxes from simple ones to more complex ones
+
+ ALTER SCHEMA regressns SET DEFAULT PRIVILEGES ON TABLE SELECT TO public;
+ CREATE TABLE regressns.acltest1 (one int, two int, three int);
+ SELECT has_table_privilege('regressuser1', 'regressns.acltest1', 'INSERT'); -- false
+ SELECT has_table_privilege('regressuser1', 'regressns.acltest1', 'SELECT'); -- true
+
+ ALTER SCHEMA regressns SET DEFAULT PRIVILEGES ON TABLE SELECT TO regressgroup1, regressuser2 AND UPDATE TO regressuser2 AND INSERT, DELETE TO regressuser2;
+ CREATE TABLE regressns.acltest2 (one int, two int, three int);
+ SELECT has_table_privilege('regressuser2', 'regressns.acltest2', 'DELETE'); -- true
+ SELECT has_table_privilege('regressuser4', 'regressns.acltest2', 'INSERT'); -- false
+ SELECT has_table_privilege('regressuser4', 'regressns.acltest2', 'SELECT'); -- true (as member of regressgroup1)
+
+ ALTER SCHEMA regressns ADD DEFAULT PRIVILEGES ON TABLE INSERT TO regressgroup1;
+ CREATE TABLE regressns.acltest3 (foo text);
+ SELECT has_table_privilege('regressuser4', 'regressns.acltest3', 'INSERT'); -- true
+
+ ALTER SCHEMA regressns DROP DEFAULT PRIVILEGES ON TABLE DELETE FROM regressuser2;
+ -- supress implicit sequence creation NOTICE
+ SET client_min_messages TO 'error';
+ CREATE TABLE regressns.acltest4 (bar serial);
+ RESET client_min_messages;
+ SELECT has_table_privilege('regressuser2', 'regressns.acltest4', 'DELETE'); -- false
+ SET SESSION AUTHORIZATION regressuser2;
+ SELECT nextval(pg_get_serial_sequence('regressns.acltest4', 'bar')::regclass); -- error
+ RESET SESSION AUTHORIZATION;
+
+ ALTER SCHEMA regressns SET DEFAULT PRIVILEGES
+ ON TABLE SELECT TO regressgroup1, regressuser2 AND UPDATE,INSERT,DELETE TO regressuser2
+ ON VIEW SELECT TO regressgroup1 AND ALL PRIVILEGES TO regressuser2 WITH GRANT OPTION
+ ON SEQUENCE ALL TO regressgroup1, regressuser2
+ ON FUNCTION EXECUTE TO regressgroup1;
+
+ -- supress implicit sequence creation NOTICE
+ SET client_min_messages TO 'error';
+ CREATE TABLE regressns.acltest5 (a serial, b text);
+ RESET client_min_messages;
+ SELECT has_table_privilege('regressgroup1', 'regressns.acltest5', 'INSERT'); -- false
+ SELECT has_table_privilege('regressuser2', 'regressns.acltest5', 'INSERT'); -- true
+ SET SESSION AUTHORIZATION regressuser2;
+ SELECT nextval(pg_get_serial_sequence('regressns.acltest5', 'a')::regclass); -- 1
+ RESET SESSION AUTHORIZATION;
+
+ CREATE FUNCTION regressns.testfunc1() RETURNS int AS 'select 1;' LANGUAGE sql;
+ SELECT has_function_privilege('regressgroup1', 'regressns.testfunc1()', 'EXECUTE'); -- true
+
+ CREATE VIEW regressns.acltest6 AS SELECT * FROM regressns.acltest4;
+ SET SESSION AUTHORIZATION regressuser2;
+ GRANT SELECT ON regressns.acltest6 TO regressuser1;
+ SELECT has_table_privilege('regressuser1', 'regressns.acltest6', 'SELECT'); -- true
+
-- clean up
\c
+ DROP SCHEMA regressns CASCADE;
+
DROP FUNCTION testfunc2(int);
DROP FUNCTION testfunc4(boolean);