diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index dd103573a5d..15dab71cc0d 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,4 +1,4 @@
-
+
@@ -113,6 +113,11 @@
databases within this database cluster
+
+ pg_default_acl
+ default privileges for object types
+
+
pg_dependdependencies between database objects
@@ -2155,6 +2160,93 @@
+
+ pg_default_acl
+
+
+ pg_default_acl
+
+
+
+ The catalog pg_default_acl> stores initial
+ privileges to be assigned to newly created objects.
+
+
+
+ pg_default_acl> Columns
+
+
+
+
+ Name
+ Type
+ References
+ Description
+
+
+
+
+
+ defaclrole
+ oid
+ pg_authid.oid
+ The OID of the role associated with this entry
+
+
+
+ defaclnamespace
+ oid
+ pg_namespace.oid
+ The OID of the namespace associated with this entry,
+ or 0 if none
+
+
+
+ defaclobjtype
+ char
+
+
+ Type of object this entry is for:
+ r> = relation (table, view),
+ S> = sequence,
+ f> = function
+
+
+
+
+ defaclacl
+ aclitem[]
+
+
+ Access privileges that this type of object should have on creation
+
+
+
+
+
+
+
+ A pg_default_acl> entry shows the initial privileges to
+ be assigned to an object belonging to the indicated user. There are
+ currently two types of entry: global> entries with
+ defaclnamespace> = 0, and per-schema> entries
+ that reference a particular schema. If a global entry is present then
+ it overrides> the normal hard-wired default privileges
+ for the object type. A per-schema entry, if present, represents privileges
+ to be added to> the global or hard-wired default privileges.
+
+
+
+ Note that when an ACL entry in another catalog is NULL, it is taken
+ to represent the hard-wired default privileges for its object,
+ not> whatever might be in pg_default_acl>
+ at the moment. pg_default_acl> is only consulted during
+ object creation.
+
+
+
+
+
pg_depend
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 845033b6b66..c15579c5164 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -1,5 +1,5 @@
@@ -9,6 +9,7 @@ Complete list of usable sgml source files in this directory.
+
diff --git a/doc/src/sgml/ref/alter_default_privileges.sgml b/doc/src/sgml/ref/alter_default_privileges.sgml
new file mode 100644
index 00000000000..b2054b17804
--- /dev/null
+++ b/doc/src/sgml/ref/alter_default_privileges.sgml
@@ -0,0 +1,211 @@
+
+
+
+
+ ALTER DEFAULT PRIVILEGES
+ 7
+ SQL - Language Statements
+
+
+
+ ALTER DEFAULT PRIVILEGES
+ define default access privileges
+
+
+
+ ALTER DEFAULT PRIVILEGES
+
+
+
+
+ALTER DEFAULT PRIVILEGES
+ [ FOR { ROLE | USER } target_role [, ...] ]
+ [ IN SCHEMA schema_name [, ...] ]
+ abbreviated_grant_or_revoke
+
+where abbreviated_grant_or_revoke is one of:
+
+GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
+ [,...] | ALL [ PRIVILEGES ] }
+ ON TABLE
+ TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
+GRANT { { USAGE | SELECT | UPDATE }
+ [,...] | ALL [ PRIVILEGES ] }
+ ON SEQUENCE
+ TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
+GRANT { EXECUTE | ALL [ PRIVILEGES ] }
+ ON FUNCTION
+ TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
+ [,...] | ALL [ PRIVILEGES ] }
+ ON TABLE
+ FROM { [ GROUP ] role_name | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { { USAGE | SELECT | UPDATE }
+ [,...] | ALL [ PRIVILEGES ] }
+ ON SEQUENCE
+ FROM { [ GROUP ] role_name | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { EXECUTE | ALL [ PRIVILEGES ] }
+ ON FUNCTION
+ FROM { [ GROUP ] role_name | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
+
+
+
+
+ Description
+
+
+ ALTER DEFAULT PRIVILEGES> allows you to set the privileges
+ that will be applied to objects created in the future. (It does not
+ affect privileges assigned to already-existing objects.) Currently,
+ only the privileges for tables (including views), sequences, and
+ functions can be altered.
+
+
+
+ You can change default privileges only for objects that will be created by
+ yourself or by roles that you are a member of. The privileges can be set
+ globally (i.e., for all objects created in the current database),
+ or just for objects created in specified schemas. Default privileges
+ that are specified per-schema are added to whatever the global default
+ privileges are for the particular object type.
+
+
+
+ As explained under ,
+ the default privileges for any object type normally grant all grantable
+ permissions to the object owner, and may grant some privileges to
+ PUBLIC> as well. However, this behavior can be changed by
+ altering the global default privileges with
+ ALTER DEFAULT PRIVILEGES>.
+
+
+
+ Parameters
+
+
+
+ target_role
+
+
+ The name of an existing role of which the current role is a member.
+ If FOR ROLE> is omitted, the current role is assumed.
+
+
+
+
+
+ schema_name
+
+
+ The name of an existing schema. Each target_role>
+ must have CREATE> privileges for each specified schema.
+ If IN SCHEMA> is omitted, the global default privileges
+ are altered.
+
+
+
+
+
+ role_name
+
+
+ The name of an existing role to grant or revoke privileges for.
+ This parameter, and all the other parameters in
+ abbreviated_grant_or_revoke>,
+ act as described under
+ or
+ ,
+ except that one is setting permissions for a whole class of objects
+ rather than specific named objects.
+
+
+
+
+
+
+
+
+ Notes
+
+
+ Use 's \ddp command
+ to obtain information about existing assignments of default privileges.
+ The meaning of the privilege values is the same as explained for
+ \dp under
+ .
+
+
+
+ If you wish to drop a role that has had its global default privileges
+ altered, it is necessary to use DROP OWNED BY> first,
+ to get rid of the default privileges entry for the role.
+
+
+
+
+ Examples
+
+
+ Grant SELECT privilege to everyone for all tables (and views) you
+ subsequently create in schema myschema, and allow
+ role webuser> to INSERT into them too:
+
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT SELECT ON TABLE TO PUBLIC;
+ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT INSERT ON TABLE TO webuser;
+
+
+
+
+ Undo the above, so that subsequently-created tables won't have any
+ more permissions than normal:
+
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA myschema REVOKE SELECT ON TABLE FROM PUBLIC;
+ALTER DEFAULT PRIVILEGES IN SCHEMA myschema REVOKE INSERT ON TABLE FROM webuser;
+
+
+
+
+ Remove the public EXECUTE permission that is normally granted on functions,
+ for all functions subsequently created by role admin>:
+
+
+ALTER DEFAULT PRIVILEGES FOR ROLE admin REVOKE EXECUTE ON FUNCTION FROM PUBLIC;
+
+
+
+
+
+ Compatibility
+
+
+ There is no ALTER DEFAULT PRIVILEGES statement in the SQL
+ standard.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index 4dddde27b91..2dcf4aa0f0b 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -1,5 +1,5 @@
@@ -80,14 +80,6 @@ GRANT role_name [, ...] TO
-
- As of PostgreSQL 8.1, the concepts of users and
- groups have been unified into a single kind of entity called a role.
- It is therefore no longer necessary to use the keyword GROUP>
- to identify whether a grantee is a user or a group. GROUP>
- is still allowed in the command, but it is a noise word.
-
-
GRANT on Database Objects
@@ -145,6 +137,9 @@ GRANT role_name [, ...] TO REVOKE> in the same transaction that
creates the object; then there is no window in which another user
can use the object.)
+ Also, these initial default privilege settings can be changed using the
+
+ command.
@@ -388,6 +383,14 @@ GRANT role_name [, ...] TO
+
+ Since PostgreSQL 8.1, the concepts of users and
+ groups have been unified into a single kind of entity called a role.
+ It is therefore no longer necessary to use the keyword GROUP>
+ to identify whether a grantee is a user or a group. GROUP>
+ is still allowed in the command, but it is a noise word.
+
+
A user may perform SELECT>, INSERT>, etc. on a
column if he holds that privilege for either the specific column or
@@ -518,8 +521,13 @@ GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw;
REVOKE> on an object
will instantiate the default privileges (producing, for example,
{miriam=arwdDxt/miriam}>) and then modify them per the
- specified request. Entries are shown in Column access
+ specified request. Similarly, entries are shown in Column access
privileges> only for columns with nondefault privileges.
+ (Note: for this purpose, default privileges> always means the
+ built-in default privileges for the object's type. An object whose
+ privileges have been affected by an ALTER DEFAULT PRIVILEGES>
+ command will always be shown with an explicit privilege entry that
+ includes the effects of the ALTER>.)
@@ -602,9 +610,10 @@ GRANT admins TO joe;
See Also
-
-
-
+
+
+
+
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index f60c3150e9f..e689d275cb4 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1,5 +1,5 @@
@@ -978,6 +978,29 @@ testdb=>
+
+ \ddp [ pattern ]
+
+
+ Lists default access privilege settings. An entry is shown for
+ each role (and schema, if applicable) for which the default
+ privilege settings have been changed from the built-in defaults.
+ If pattern is
+ specified, only entries whose role name or schema name matches
+ the pattern are listed.
+
+
+
+ The command is used to set
+ default access privileges. The meaning of the
+ privilege display is explained under
+ .
+
+
+
+
+
\dD[S] [ pattern ]
@@ -1142,8 +1165,8 @@ testdb=>
class="parameter">pattern is specified, only
those roles whose names match the pattern are listed.
(This command is now effectively the same as \du).
- If the form \dg+ is used, additional information
- is shown about each role, including the comment for each role.
+ If the form \dg+ is used, additional information
+ is shown about each role, including the comment for each role.
@@ -1235,7 +1258,9 @@ testdb=>
The and
- commands are used to set access privileges.
+ commands are used to set access privileges. The meaning of the
+ privilege display is explained under
+ .
@@ -2045,12 +2070,6 @@ lo_import 152801
specified, only tables,views and sequences whose names match the pattern are listed.
-
- The and
-
- commands are used to set access privileges.
-
-
This is an alias for \dp (display
privileges).
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 48f8040541d..0e72fc5475b 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -1,4 +1,4 @@
-
+
Reference
@@ -37,6 +37,7 @@
&alterAggregate;
&alterConversion;
&alterDatabase;
+ &alterDefaultPrivileges;
&alterDomain;
&alterForeignDataWrapper;
&alterFunction;
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index b7624547aac..9d6f854d126 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.99 2009/09/27 01:32:11 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.100 2009/10/05 19:24:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -226,6 +226,7 @@ Boot_CreateStmt:
0,
ONCOMMIT_NOOP,
(Datum) 0,
+ false,
true);
elog(DEBUG4, "relation created with oid %u", id);
}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index e4414ed2bf5..53784e9c54b 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -2,7 +2,7 @@
#
# Makefile for backend/catalog
#
-# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.71 2009/08/26 22:24:43 petere Exp $
+# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.72 2009/10/05 19:24:34 tgl Exp $
#
#-------------------------------------------------------------------------
@@ -37,6 +37,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
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_default_acl.h \
toasting.h indexing.h \
)
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index bb15e78d1c9..b06e587a1b1 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.154 2009/06/11 14:48:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.155 2009/10/05 19:24:35 tgl Exp $
*
* NOTES
* See acl.h.
@@ -27,6 +27,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_default_acl.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
@@ -51,6 +52,51 @@
#include "utils/tqual.h"
+/*
+ * The information about one Grant/Revoke statement, in internal format: object
+ * and grantees names have been turned into Oids, the privilege list is an
+ * AclMode bitmask. If 'privileges' is ACL_NO_RIGHTS (the 0 value) and
+ * all_privs is true, 'privileges' will be internally set to the right kind of
+ * ACL_ALL_RIGHTS_*, depending on the object type (NB - this will modify the
+ * InternalGrant struct!)
+ *
+ * Note: 'all_privs' and 'privileges' represent object-level privileges only.
+ * There might also be column-level privilege specifications, which are
+ * represented in col_privs (this is a list of untransformed AccessPriv nodes).
+ * Column privileges are only valid for objtype ACL_OBJECT_RELATION.
+ */
+typedef struct
+{
+ bool is_grant;
+ GrantObjectType objtype;
+ List *objects;
+ bool all_privs;
+ AclMode privileges;
+ List *col_privs;
+ List *grantees;
+ bool grant_option;
+ DropBehavior behavior;
+} InternalGrant;
+
+/*
+ * Internal format used by ALTER DEFAULT PRIVILEGES.
+ */
+typedef struct
+{
+ Oid roleid; /* owning role */
+ Oid nspid; /* namespace, or InvalidOid if none */
+ /* remaining fields are same as in InternalGrant: */
+ bool is_grant;
+ GrantObjectType objtype;
+ bool all_privs;
+ AclMode privileges;
+ List *grantees;
+ bool grant_option;
+ DropBehavior behavior;
+} InternalDefaultACL;
+
+
+static void ExecGrantStmt_oids(InternalGrant *istmt);
static void ExecGrant_Relation(InternalGrant *grantStmt);
static void ExecGrant_Database(InternalGrant *grantStmt);
static void ExecGrant_Fdw(InternalGrant *grantStmt);
@@ -60,6 +106,9 @@ static void ExecGrant_Language(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
+static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
+static void SetDefaultACL(InternalDefaultACL *iacls);
+
static List *objectNamesToOids(GrantObjectType objtype, List *objnames);
static void expand_col_privileges(List *colnames, Oid table_oid,
AclMode this_privileges,
@@ -361,11 +410,11 @@ ExecuteGrantStmt(GrantStmt *stmt)
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
default:
+ elog(ERROR, "unrecognized GrantStmt.objtype: %d",
+ (int) stmt->objtype);
/* keep compiler quiet */
all_privileges = ACL_NO_RIGHTS;
errormsg = NULL;
- elog(ERROR, "unrecognized GrantStmt.objtype: %d",
- (int) stmt->objtype);
}
if (stmt->privileges == NIL)
@@ -421,11 +470,9 @@ ExecuteGrantStmt(GrantStmt *stmt)
/*
* ExecGrantStmt_oids
*
- * "Internal" entrypoint for granting and revoking privileges. This is
- * exported for pg_shdepend.c to use in revoking privileges when dropping
- * a role.
+ * Internal entry point for granting and revoking privileges.
*/
-void
+static void
ExecGrantStmt_oids(InternalGrant *istmt)
{
switch (istmt->objtype)
@@ -609,6 +656,563 @@ objectNamesToOids(GrantObjectType objtype, List *objnames)
return objects;
}
+/*
+ * ALTER DEFAULT PRIVILEGES statement
+ */
+void
+ExecAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *stmt)
+{
+ GrantStmt *action = stmt->action;
+ InternalDefaultACL iacls;
+ ListCell *cell;
+ List *rolenames = NIL;
+ List *nspnames = NIL;
+ DefElem *drolenames = NULL;
+ DefElem *dnspnames = NULL;
+ AclMode all_privileges;
+ const char *errormsg;
+
+ /* Deconstruct the "options" part of the statement */
+ foreach(cell, stmt->options)
+ {
+ DefElem *defel = (DefElem *) lfirst(cell);
+
+ if (strcmp(defel->defname, "schemas") == 0)
+ {
+ if (dnspnames)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ dnspnames = defel;
+ }
+ else if (strcmp(defel->defname, "roles") == 0)
+ {
+ if (drolenames)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ drolenames = defel;
+ }
+ else
+ elog(ERROR, "option \"%s\" not recognized", defel->defname);
+ }
+
+ if (dnspnames)
+ nspnames = (List *) dnspnames->arg;
+ if (drolenames)
+ rolenames = (List *) drolenames->arg;
+
+ /* Prepare the InternalDefaultACL representation of the statement */
+ /* roleid to be filled below */
+ /* nspid to be filled in SetDefaultACLsInSchemas */
+ iacls.is_grant = action->is_grant;
+ iacls.objtype = action->objtype;
+ /* all_privs to be filled below */
+ /* privileges to be filled below */
+ iacls.grantees = NIL; /* filled below */
+ iacls.grant_option = action->grant_option;
+ iacls.behavior = action->behavior;
+
+ /*
+ * 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
+ * detected (which is what the grammar uses if PUBLIC is found), so
+ * downstream there shouldn't be any additional work needed to support
+ * this case.
+ */
+ foreach(cell, action->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 action->privileges, a list of privilege strings,
+ * into an AclMode bitmask.
+ */
+ switch (action->objtype)
+ {
+ case ACL_OBJECT_RELATION:
+ all_privileges = ACL_ALL_RIGHTS_RELATION;
+ errormsg = gettext_noop("invalid privilege type %s for relation");
+ break;
+ case ACL_OBJECT_SEQUENCE:
+ all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+ errormsg = gettext_noop("invalid privilege type %s for sequence");
+ break;
+ case ACL_OBJECT_FUNCTION:
+ all_privileges = ACL_ALL_RIGHTS_FUNCTION;
+ errormsg = gettext_noop("invalid privilege type %s for function");
+ break;
+ default:
+ elog(ERROR, "unrecognized GrantStmt.objtype: %d",
+ (int) action->objtype);
+ /* keep compiler quiet */
+ all_privileges = ACL_NO_RIGHTS;
+ errormsg = NULL;
+ }
+
+ if (action->privileges == NIL)
+ {
+ iacls.all_privs = true;
+
+ /*
+ * will be turned into ACL_ALL_RIGHTS_* by the internal routines
+ * depending on the object type
+ */
+ iacls.privileges = ACL_NO_RIGHTS;
+ }
+ else
+ {
+ iacls.all_privs = false;
+ iacls.privileges = ACL_NO_RIGHTS;
+
+ foreach(cell, action->privileges)
+ {
+ AccessPriv *privnode = (AccessPriv *) lfirst(cell);
+ AclMode priv;
+
+ if (privnode->cols)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg("default privileges cannot be set for columns")));
+
+ if (privnode->priv_name == NULL) /* parser mistake? */
+ elog(ERROR, "AccessPriv node must specify privilege");
+ priv = string_to_privilege(privnode->priv_name);
+
+ if (priv & ~((AclMode) all_privileges))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg(errormsg, privilege_to_string(priv))));
+
+ iacls.privileges |= priv;
+ }
+ }
+
+ if (rolenames == NIL)
+ {
+ /* Set permissions for myself */
+ iacls.roleid = GetUserId();
+
+ SetDefaultACLsInSchemas(&iacls, nspnames);
+ }
+ else
+ {
+ /* Look up the role OIDs and do permissions checks */
+ ListCell *rolecell;
+
+ foreach(rolecell, rolenames)
+ {
+ char *rolename = strVal(lfirst(rolecell));
+
+ iacls.roleid = get_roleid_checked(rolename);
+
+ /*
+ * We insist that calling user be a member of each target role.
+ * If he has that, he could become that role anyway via SET ROLE,
+ * so FOR ROLE is just a syntactic convenience and doesn't give
+ * any special privileges.
+ */
+ check_is_member_of_role(GetUserId(), iacls.roleid);
+
+ SetDefaultACLsInSchemas(&iacls, nspnames);
+ }
+ }
+}
+
+/*
+ * Process ALTER DEFAULT PRIVILEGES for a list of target schemas
+ *
+ * All fields of *iacls except nspid were filled already
+ */
+static void
+SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames)
+{
+ if (nspnames == NIL)
+ {
+ /* Set database-wide permissions if no schema was specified */
+ iacls->nspid = InvalidOid;
+
+ SetDefaultACL(iacls);
+ }
+ else
+ {
+ /* Look up the schema OIDs and do permissions checks */
+ ListCell *nspcell;
+
+ foreach(nspcell, nspnames)
+ {
+ char *nspname = strVal(lfirst(nspcell));
+ AclResult aclresult;
+
+ /*
+ * Normally we'd use LookupCreationNamespace here, but it's
+ * important to do the permissions check against the target role
+ * not the calling user, so write it out in full. We require
+ * CREATE privileges, since without CREATE you won't be able to do
+ * anything using the default privs anyway.
+ */
+ iacls->nspid = GetSysCacheOid(NAMESPACENAME,
+ CStringGetDatum(nspname),
+ 0, 0, 0);
+ if (!OidIsValid(iacls->nspid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_SCHEMA),
+ errmsg("schema \"%s\" does not exist", nspname)));
+
+ aclresult = pg_namespace_aclcheck(iacls->nspid, iacls->roleid,
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ nspname);
+
+ SetDefaultACL(iacls);
+ }
+ }
+}
+
+
+/*
+ * Create or update a pg_default_acl entry
+ */
+static void
+SetDefaultACL(InternalDefaultACL *iacls)
+{
+ AclMode this_privileges = iacls->privileges;
+ char objtype;
+ Relation rel;
+ HeapTuple tuple;
+ bool isNew;
+ Acl *old_acl;
+ Acl *new_acl;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_default_acl];
+ bool nulls[Natts_pg_default_acl];
+ bool replaces[Natts_pg_default_acl];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ rel = heap_open(DefaultAclRelationId, RowExclusiveLock);
+
+ /*
+ * Convert ACL object type to pg_default_acl object type
+ * and handle all_privs option
+ */
+ switch (iacls->objtype)
+ {
+ case ACL_OBJECT_RELATION:
+ objtype = DEFACLOBJ_RELATION;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_RELATION;
+ break;
+
+ case ACL_OBJECT_SEQUENCE:
+ objtype = DEFACLOBJ_SEQUENCE;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+ break;
+
+ case ACL_OBJECT_FUNCTION:
+ objtype = DEFACLOBJ_FUNCTION;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_FUNCTION;
+ break;
+
+ default:
+ elog(ERROR, "unrecognized objtype: %d",
+ (int) iacls->objtype);
+ objtype = 0; /* keep compiler quiet */
+ break;
+ }
+
+ /* Search for existing row for this object type in catalog */
+ tuple = SearchSysCache(DEFACLROLENSPOBJ,
+ ObjectIdGetDatum(iacls->roleid),
+ ObjectIdGetDatum(iacls->nspid),
+ CharGetDatum(objtype),
+ 0);
+
+ if (HeapTupleIsValid(tuple))
+ {
+ Datum aclDatum;
+ bool isNull;
+
+ aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple,
+ Anum_pg_default_acl_defaclacl,
+ &isNull);
+ if (!isNull)
+ old_acl = DatumGetAclPCopy(aclDatum);
+ else
+ old_acl = NULL;
+ isNew = false;
+ }
+ else
+ {
+ old_acl = NULL;
+ isNew = true;
+ }
+
+ if (old_acl == NULL)
+ {
+ /*
+ * If we are creating a global entry, start with the hard-wired
+ * defaults and modify as per command. Otherwise, start with an empty
+ * ACL and modify that. This is needed because global entries
+ * replace the hard-wired defaults, while others do not.
+ */
+ if (!OidIsValid(iacls->nspid))
+ old_acl = acldefault(iacls->objtype, iacls->roleid);
+ else
+ old_acl = make_empty_acl();
+ }
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information. Collect data before
+ * merge_acl_with_grant throws away old_acl.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
+ /*
+ * Generate new ACL. Grantor of rights is always the same as the
+ * target role.
+ */
+ new_acl = merge_acl_with_grant(old_acl,
+ iacls->is_grant,
+ iacls->grant_option,
+ iacls->behavior,
+ iacls->grantees,
+ this_privileges,
+ iacls->roleid,
+ iacls->roleid);
+
+ /* 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)
+ {
+ values[Anum_pg_default_acl_defaclrole - 1] = ObjectIdGetDatum(iacls->roleid);
+ values[Anum_pg_default_acl_defaclnamespace - 1] = ObjectIdGetDatum(iacls->nspid);
+ values[Anum_pg_default_acl_defaclobjtype - 1] = CharGetDatum(objtype);
+ values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ simple_heap_insert(rel, newtuple);
+ }
+ else
+ {
+ values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
+ replaces[Anum_pg_default_acl_defaclacl - 1] = true;
+
+ 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);
+
+ /* these dependencies don't change in an update */
+ if (isNew)
+ {
+ /* dependency on role */
+ recordDependencyOnOwner(DefaultAclRelationId,
+ HeapTupleGetOid(newtuple),
+ iacls->roleid);
+
+ /* dependency on namespace */
+ if (OidIsValid(iacls->nspid))
+ {
+ ObjectAddress myself,
+ referenced;
+
+ myself.classId = DefaultAclRelationId;
+ myself.objectId = HeapTupleGetOid(newtuple);
+ myself.objectSubId = 0;
+
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = iacls->nspid;
+ referenced.objectSubId = 0;
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+ }
+ }
+
+ /*
+ * Update the shared dependency ACL info
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ updateAclDependencies(DefaultAclRelationId, HeapTupleGetOid(newtuple), 0,
+ iacls->roleid, iacls->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ pfree(new_acl);
+
+ if (HeapTupleIsValid(tuple))
+ ReleaseSysCache(tuple);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+
+/*
+ * RemoveRoleFromObjectACL
+ *
+ * Used by shdepDropOwned to remove mentions of a role in ACLs
+ */
+void
+RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
+{
+ if (classid == DefaultAclRelationId)
+ {
+ InternalDefaultACL iacls;
+ Form_pg_default_acl pg_default_acl_tuple;
+ Relation rel;
+ ScanKeyData skey[1];
+ SysScanDesc scan;
+ HeapTuple tuple;
+
+ /* first fetch info needed by SetDefaultACL */
+ rel = heap_open(DefaultAclRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objid));
+
+ scan = systable_beginscan(rel, DefaultAclOidIndexId, true,
+ SnapshotNow, 1, skey);
+
+ tuple = systable_getnext(scan);
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "could not find tuple for default ACL %u", objid);
+
+ pg_default_acl_tuple = (Form_pg_default_acl) GETSTRUCT(tuple);
+
+ iacls.roleid = pg_default_acl_tuple->defaclrole;
+ iacls.nspid = pg_default_acl_tuple->defaclnamespace;
+
+ switch (pg_default_acl_tuple->defaclobjtype)
+ {
+ case DEFACLOBJ_RELATION:
+ iacls.objtype = ACL_OBJECT_RELATION;
+ break;
+ case ACL_OBJECT_SEQUENCE:
+ iacls.objtype = ACL_OBJECT_SEQUENCE;
+ break;
+ case DEFACLOBJ_FUNCTION:
+ iacls.objtype = ACL_OBJECT_FUNCTION;
+ break;
+ default:
+ /* Shouldn't get here */
+ elog(ERROR, "unexpected default ACL type %d",
+ pg_default_acl_tuple->defaclobjtype);
+ break;
+ }
+
+ systable_endscan(scan);
+ heap_close(rel, AccessShareLock);
+
+ iacls.is_grant = false;
+ iacls.all_privs = true;
+ iacls.privileges = ACL_NO_RIGHTS;
+ iacls.grantees = list_make1_oid(roleid);
+ iacls.grant_option = false;
+ iacls.behavior = DROP_CASCADE;
+
+ /* Do it */
+ SetDefaultACL(&iacls);
+ }
+ else
+ {
+ InternalGrant istmt;
+
+ switch (classid)
+ {
+ case RelationRelationId:
+ /* it's OK to use RELATION for a sequence */
+ istmt.objtype = ACL_OBJECT_RELATION;
+ break;
+ case DatabaseRelationId:
+ istmt.objtype = ACL_OBJECT_DATABASE;
+ break;
+ case ProcedureRelationId:
+ istmt.objtype = ACL_OBJECT_FUNCTION;
+ break;
+ case LanguageRelationId:
+ istmt.objtype = ACL_OBJECT_LANGUAGE;
+ break;
+ case NamespaceRelationId:
+ istmt.objtype = ACL_OBJECT_NAMESPACE;
+ break;
+ case TableSpaceRelationId:
+ istmt.objtype = ACL_OBJECT_TABLESPACE;
+ break;
+ default:
+ elog(ERROR, "unexpected object class %u", classid);
+ break;
+ }
+ istmt.is_grant = false;
+ istmt.objects = list_make1_oid(objid);
+ istmt.all_privs = true;
+ istmt.privileges = ACL_NO_RIGHTS;
+ istmt.col_privs = NIL;
+ istmt.grantees = list_make1_oid(roleid);
+ istmt.grant_option = false;
+ istmt.behavior = DROP_CASCADE;
+
+ ExecGrantStmt_oids(&istmt);
+ }
+}
+
+
+/*
+ * Remove a pg_default_acl entry
+ */
+void
+RemoveDefaultACLById(Oid defaclOid)
+{
+ Relation rel;
+ ScanKeyData skey[1];
+ SysScanDesc scan;
+ HeapTuple tuple;
+
+ rel = heap_open(DefaultAclRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&skey[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(defaclOid));
+
+ scan = systable_beginscan(rel, DefaultAclOidIndexId, true,
+ SnapshotNow, 1, skey);
+
+ tuple = systable_getnext(scan);
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "could not find tuple for default ACL %u", defaclOid);
+
+ simple_heap_delete(rel, &tuple->t_self);
+
+ systable_endscan(scan);
+ heap_close(rel, RowExclusiveLock);
+}
+
+
/*
* expand_col_privileges
*
@@ -3532,3 +4136,106 @@ pg_conversion_ownercheck(Oid conv_oid, Oid roleid)
return has_privs_of_role(roleid, ownerId);
}
+
+/*
+ * Fetch pg_default_acl entry for given role, namespace and object type
+ * (object type must be given in pg_default_acl's encoding).
+ * Returns NULL if no such entry.
+ */
+static Acl *
+get_default_acl_internal(Oid roleId, Oid nsp_oid, char objtype)
+{
+ Acl *result = NULL;
+ HeapTuple tuple;
+
+ tuple = SearchSysCache(DEFACLROLENSPOBJ,
+ ObjectIdGetDatum(roleId),
+ ObjectIdGetDatum(nsp_oid),
+ CharGetDatum(objtype),
+ 0);
+
+ if (HeapTupleIsValid(tuple))
+ {
+ Datum aclDatum;
+ bool isNull;
+
+ aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple,
+ Anum_pg_default_acl_defaclacl,
+ &isNull);
+ if (!isNull)
+ result = DatumGetAclPCopy(aclDatum);
+ ReleaseSysCache(tuple);
+ }
+
+ return result;
+}
+
+/*
+ * Get default permissions for newly created object within given schema
+ *
+ * Returns NULL if built-in system defaults should be used
+ */
+Acl *
+get_user_default_acl(GrantObjectType objtype, Oid ownerId, Oid nsp_oid)
+{
+ Acl *result;
+ Acl *glob_acl;
+ Acl *schema_acl;
+ Acl *def_acl;
+ char defaclobjtype;
+
+ /*
+ * Use NULL during bootstrap, since pg_default_acl probably isn't there
+ * yet.
+ */
+ if (IsBootstrapProcessingMode())
+ return NULL;
+
+ /* Check if object type is supported in pg_default_acl */
+ switch (objtype)
+ {
+ case ACL_OBJECT_RELATION:
+ defaclobjtype = DEFACLOBJ_RELATION;
+ break;
+
+ case ACL_OBJECT_SEQUENCE:
+ defaclobjtype = DEFACLOBJ_SEQUENCE;
+ break;
+
+ case ACL_OBJECT_FUNCTION:
+ defaclobjtype = DEFACLOBJ_FUNCTION;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ /* Look up the relevant pg_default_acl entries */
+ glob_acl = get_default_acl_internal(ownerId, InvalidOid, defaclobjtype);
+ schema_acl = get_default_acl_internal(ownerId, nsp_oid, defaclobjtype);
+
+ /* Quick out if neither entry exists */
+ if (glob_acl == NULL && schema_acl == NULL)
+ return NULL;
+
+ /* We need to know the hard-wired default value, too */
+ def_acl = acldefault(objtype, ownerId);
+
+ /* If there's no global entry, substitute the hard-wired default */
+ if (glob_acl == NULL)
+ glob_acl = def_acl;
+
+ /* Merge in any per-schema privileges */
+ result = aclmerge(glob_acl, schema_acl, ownerId);
+
+ /*
+ * For efficiency, we want to return NULL if the result equals default.
+ * This requires sorting both arrays to get an accurate comparison.
+ */
+ aclitemsort(result);
+ aclitemsort(def_acl);
+ if (aclequal(result, def_acl))
+ result = NULL;
+
+ return result;
+}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index c3d87ef59eb..8a07c69c7e8 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.91 2009/09/22 15:46:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.92 2009/10/05 19:24:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -32,6 +32,7 @@
#include "catalog/pg_conversion.h"
#include "catalog/pg_conversion_fn.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_default_acl.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
@@ -64,6 +65,7 @@
#include "parser/parsetree.h"
#include "rewrite/rewriteRemove.h"
#include "storage/lmgr.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
@@ -146,7 +148,8 @@ static const Oid object_classes[MAX_OCLASS] = {
TableSpaceRelationId, /* OCLASS_TBLSPACE */
ForeignDataWrapperRelationId, /* OCLASS_FDW */
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
- UserMappingRelationId /* OCLASS_USER_MAPPING */
+ UserMappingRelationId, /* OCLASS_USER_MAPPING */
+ DefaultAclRelationId /* OCLASS_DEFACL */
};
@@ -1136,6 +1139,10 @@ doDeletion(const ObjectAddress *object)
RemoveUserMappingById(object->objectId);
break;
+ case OCLASS_DEFACL:
+ RemoveDefaultACLById(object->objectId);
+ break;
+
default:
elog(ERROR, "unrecognized object class: %u",
object->classId);
@@ -2055,6 +2062,10 @@ getObjectClass(const ObjectAddress *object)
case UserMappingRelationId:
Assert(object->objectSubId == 0);
return OCLASS_USER_MAPPING;
+
+ case DefaultAclRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_DEFACL;
}
/* shouldn't get here */
@@ -2597,6 +2608,69 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_DEFACL:
+ {
+ Relation defaclrel;
+ ScanKeyData skey[1];
+ SysScanDesc rcscan;
+ HeapTuple tup;
+ Form_pg_default_acl defacl;
+
+ defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ rcscan = systable_beginscan(defaclrel, DefaultAclOidIndexId,
+ true, SnapshotNow, 1, skey);
+
+ tup = systable_getnext(rcscan);
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "could not find tuple for default ACL %u",
+ object->objectId);
+
+ defacl = (Form_pg_default_acl) GETSTRUCT(tup);
+
+ switch (defacl->defaclobjtype)
+ {
+ case DEFACLOBJ_RELATION:
+ appendStringInfo(&buffer,
+ _("default privileges on new relations belonging to role %s"),
+ GetUserNameFromId(defacl->defaclrole));
+ break;
+ case DEFACLOBJ_SEQUENCE:
+ appendStringInfo(&buffer,
+ _("default privileges on new sequences belonging to role %s"),
+ GetUserNameFromId(defacl->defaclrole));
+ break;
+ case DEFACLOBJ_FUNCTION:
+ appendStringInfo(&buffer,
+ _("default privileges on new functions belonging to role %s"),
+ GetUserNameFromId(defacl->defaclrole));
+ break;
+ default:
+ /* shouldn't get here */
+ appendStringInfo(&buffer,
+ _("default privileges belonging to role %s"),
+ GetUserNameFromId(defacl->defaclrole));
+ break;
+ }
+
+ if (OidIsValid(defacl->defaclnamespace))
+ {
+ appendStringInfo(&buffer,
+ _(" in schema %s"),
+ get_namespace_name(defacl->defaclnamespace));
+ }
+
+ systable_endscan(rcscan);
+ heap_close(defaclrel, AccessShareLock);
+ break;
+ }
+
default:
appendStringInfo(&buffer, "unrecognized object %u %u %d",
object->classId,
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e9bdc882238..a0133062847 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.359 2009/09/26 22:42:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.360 2009/10/05 19:24:35 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -59,6 +59,7 @@
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/smgr.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
@@ -74,6 +75,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
Oid new_rel_oid, Oid new_type_oid,
Oid relowner,
char relkind,
+ Datum relacl,
Datum reloptions);
static Oid AddNewRelationType(const char *typeName,
Oid typeNamespace,
@@ -636,14 +638,16 @@ AddNewAttributeTuples(Oid new_rel_oid,
* 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).
+ * relacl and reloptions are passed in Datum form (to avoid having
+ * to reference the data types in heap.h). Pass (Datum) 0 to set them
+ * to NULL.
* --------------------------------
*/
void
InsertPgClassTuple(Relation pg_class_desc,
Relation new_rel_desc,
Oid new_rel_oid,
+ Datum relacl,
Datum reloptions)
{
Form_pg_class rd_rel = new_rel_desc->rd_rel;
@@ -678,8 +682,10 @@ InsertPgClassTuple(Relation pg_class_desc,
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 (relacl != (Datum) 0)
+ values[Anum_pg_class_relacl - 1] = relacl;
+ else
+ nulls[Anum_pg_class_relacl - 1] = true;
if (reloptions != (Datum) 0)
values[Anum_pg_class_reloptions - 1] = reloptions;
else
@@ -715,6 +721,7 @@ AddNewRelationTuple(Relation pg_class_desc,
Oid new_type_oid,
Oid relowner,
char relkind,
+ Datum relacl,
Datum reloptions)
{
Form_pg_class new_rel_reltup;
@@ -775,7 +782,8 @@ AddNewRelationTuple(Relation pg_class_desc,
new_rel_desc->rd_att->tdtypeid = new_type_oid;
/* Now build and insert the tuple */
- InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions);
+ InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid,
+ relacl, reloptions);
}
@@ -831,6 +839,27 @@ AddNewRelationType(const char *typeName,
* heap_create_with_catalog
*
* creates a new cataloged relation. see comments above.
+ *
+ * Arguments:
+ * relname: name to give to new rel
+ * relnamespace: OID of namespace it goes in
+ * reltablespace: OID of tablespace it goes in
+ * relid: OID to assign to new rel, or InvalidOid to select a new OID
+ * reltypeid: OID to assign to rel's rowtype, or InvalidOid to select one
+ * ownerid: OID of new rel's owner
+ * tupdesc: tuple descriptor (source of column definitions)
+ * cooked_constraints: list of precooked check constraints and defaults
+ * relkind: relkind for new rel
+ * shared_relation: TRUE if it's to be a shared relation
+ * oidislocal: TRUE if oid column (if any) should be marked attislocal
+ * oidinhcount: attinhcount to assign to oid column (if any)
+ * oncommit: ON COMMIT marking (only relevant if it's a temp table)
+ * reloptions: reloptions in Datum form, or (Datum) 0 if none
+ * use_user_acl: TRUE if should look for user-defined default permissions;
+ * if FALSE, relacl is always set NULL
+ * allow_system_table_mods: TRUE to allow creation in system namespaces
+ *
+ * Returns the OID of the new relation
* --------------------------------
*/
Oid
@@ -848,10 +877,12 @@ heap_create_with_catalog(const char *relname,
int oidinhcount,
OnCommitAction oncommit,
Datum reloptions,
+ bool use_user_acl,
bool allow_system_table_mods)
{
Relation pg_class_desc;
Relation new_rel_desc;
+ Acl *relacl;
Oid old_type_oid;
Oid new_type_oid;
Oid new_array_oid = InvalidOid;
@@ -920,6 +951,30 @@ heap_create_with_catalog(const char *relname,
relid = GetNewRelFileNode(reltablespace, shared_relation,
pg_class_desc);
+ /*
+ * Determine the relation's initial permissions.
+ */
+ if (use_user_acl)
+ {
+ switch (relkind)
+ {
+ case RELKIND_RELATION:
+ case RELKIND_VIEW:
+ relacl = get_user_default_acl(ACL_OBJECT_RELATION, ownerid,
+ relnamespace);
+ break;
+ case RELKIND_SEQUENCE:
+ relacl = get_user_default_acl(ACL_OBJECT_SEQUENCE, ownerid,
+ relnamespace);
+ break;
+ default:
+ relacl = NULL;
+ break;
+ }
+ }
+ else
+ relacl = NULL;
+
/*
* Create the relcache entry (mostly dummy at this point) and the physical
* disk file. (If we fail further down, it's the smgr's responsibility to
@@ -1027,6 +1082,7 @@ heap_create_with_catalog(const char *relname,
new_type_oid,
ownerid,
relkind,
+ PointerGetDatum(relacl),
reloptions);
/*
@@ -1037,12 +1093,15 @@ heap_create_with_catalog(const char *relname,
/*
* Make a dependency link to force the relation to be deleted if its
- * namespace is. Also make a dependency link to its owner.
+ * namespace is. Also make a dependency link to its owner, as well
+ * as dependencies for any roles mentioned in the default ACL.
*
* For composite types, these dependencies are tracked for the pg_type
* entry, so we needn't record them here. Likewise, TOAST tables don't
* need a namespace dependency (they live in a pinned namespace) nor an
- * owner dependency (they depend indirectly through the parent table).
+ * owner dependency (they depend indirectly through the parent table),
+ * nor should they have any ACL entries.
+ *
* Also, skip this in bootstrap mode, since we don't make dependencies
* while bootstrapping.
*/
@@ -1062,6 +1121,18 @@ heap_create_with_catalog(const char *relname,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
recordDependencyOnOwner(RelationRelationId, relid, ownerid);
+
+ if (relacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ nnewmembers = aclmembers(relacl, &newmembers);
+ updateAclDependencies(RelationRelationId, relid, 0,
+ ownerid, true,
+ 0, NULL,
+ nnewmembers, newmembers);
+ }
}
/*
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 759950b2265..b43b2910e73 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.321 2009/08/02 22:14:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.322 2009/10/05 19:24:35 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -664,6 +664,7 @@ index_create(Oid heapRelationId,
*/
InsertPgClassTuple(pg_class, indexRelation,
RelationGetRelid(indexRelation),
+ (Datum) 0,
reloptions);
/* done with pg_class */
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 1fda61cc160..db69c127ad4 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.166 2009/10/02 18:13:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.167 2009/10/05 19:24:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -90,6 +90,7 @@ ProcedureCreate(const char *procedureName,
bool internalOutParam = false;
Oid variadicType = InvalidOid;
Oid proowner = GetUserId();
+ Acl *proacl = NULL;
Relation rel;
HeapTuple tup;
HeapTuple oldtup;
@@ -331,8 +332,7 @@ ProcedureCreate(const char *procedureName,
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;
+ /* proacl will be determined later */
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
tupDesc = RelationGetDescr(rel);
@@ -489,6 +489,15 @@ ProcedureCreate(const char *procedureName,
else
{
/* Creating a new procedure */
+
+ /* First, get default permissions and set up proacl */
+ proacl = get_user_default_acl(ACL_OBJECT_FUNCTION, proowner,
+ procNamespace);
+ if (proacl != NULL)
+ values[Anum_pg_proc_proacl - 1] = PointerGetDatum(proacl);
+ else
+ nulls[Anum_pg_proc_proacl - 1] = true;
+
tup = heap_form_tuple(tupDesc, values, nulls);
simple_heap_insert(rel, tup);
is_update = false;
@@ -543,6 +552,19 @@ ProcedureCreate(const char *procedureName,
if (!is_update)
recordDependencyOnOwner(ProcedureRelationId, retval, proowner);
+ /* dependency on any roles mentioned in ACL */
+ if (!is_update && proacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ nnewmembers = aclmembers(proacl, &newmembers);
+ updateAclDependencies(ProcedureRelationId, retval, 0,
+ proowner, true,
+ 0, NULL,
+ nnewmembers, newmembers);
+ }
+
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index 08977c38633..869ec1fdd57 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.34 2009/06/11 14:48:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.35 2009/10/05 19:24:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,6 +23,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_default_acl.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_operator.h"
@@ -1180,7 +1181,6 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
while ((tuple = systable_getnext(scan)) != NULL)
{
Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
- InternalGrant istmt;
ObjectAddress obj;
/* We only operate on objects in the current database */
@@ -1195,42 +1195,9 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
elog(ERROR, "unexpected dependency type");
break;
case SHARED_DEPENDENCY_ACL:
- switch (sdepForm->classid)
- {
- case RelationRelationId:
- /* it's OK to use RELATION for a sequence */
- istmt.objtype = ACL_OBJECT_RELATION;
- break;
- case DatabaseRelationId:
- istmt.objtype = ACL_OBJECT_DATABASE;
- break;
- case ProcedureRelationId:
- istmt.objtype = ACL_OBJECT_FUNCTION;
- break;
- case LanguageRelationId:
- istmt.objtype = ACL_OBJECT_LANGUAGE;
- break;
- case NamespaceRelationId:
- istmt.objtype = ACL_OBJECT_NAMESPACE;
- break;
- case TableSpaceRelationId:
- istmt.objtype = ACL_OBJECT_TABLESPACE;
- break;
- default:
- elog(ERROR, "unexpected object type %d",
- sdepForm->classid);
- break;
- }
- istmt.is_grant = false;
- istmt.objects = list_make1_oid(sdepForm->objid);
- istmt.all_privs = true;
- istmt.privileges = ACL_NO_RIGHTS;
- istmt.col_privs = NIL;
- istmt.grantees = list_make1_oid(roleid);
- istmt.grant_option = false;
- istmt.behavior = DROP_CASCADE;
-
- ExecGrantStmt_oids(&istmt);
+ RemoveRoleFromObjectACL(roleid,
+ sdepForm->classid,
+ sdepForm->objid);
break;
case SHARED_DEPENDENCY_OWNER:
/* Save it for deletion below */
@@ -1365,8 +1332,15 @@ shdepReassignOwned(List *roleids, Oid newrole)
AlterLanguageOwner_oid(sdepForm->objid, newrole);
break;
+ case DefaultAclRelationId:
+ /*
+ * Ignore default ACLs; they should be handled by
+ * DROP OWNED, not REASSIGN OWNED.
+ */
+ break;
+
default:
- elog(ERROR, "unexpected classid %d", sdepForm->classid);
+ elog(ERROR, "unexpected classid %u", sdepForm->classid);
break;
}
/* Make sure the next iteration will see my changes */
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index ac7b52734dd..dfc178724ef 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.19 2009/09/26 22:42:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.20 2009/10/05 19:24:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -213,6 +213,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
0,
ONCOMMIT_NOOP,
reloptions,
+ false,
true);
/* make the toast relation visible, else index creation will fail */
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 5c483614e33..a05fb4736cc 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.187 2009/09/26 22:42:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.188 2009/10/05 19:24:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -713,6 +713,7 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace)
0,
ONCOMMIT_NOOP,
reloptions,
+ false,
allowSystemTableMods);
ReleaseSysCache(tuple);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 07b4af611c4..697b80d1b41 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.299 2009/09/26 22:42:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.300 2009/10/05 19:24:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -521,6 +521,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
parentOidCount,
stmt->oncommit,
reloptions,
+ true,
allowSystemTableMods);
StoreCatalogInheritance(relationId, inheritOids);
@@ -6098,6 +6099,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_FDW:
case OCLASS_FOREIGN_SERVER:
case OCLASS_USER_MAPPING:
+ case OCLASS_DEFACL:
/*
* We don't expect any of these sorts of objects to depend on
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index d18d66c64a9..493ec2b1af1 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.329 2009/09/27 20:09:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.330 2009/10/05 19:24:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2909,6 +2909,7 @@ OpenIntoRel(QueryDesc *queryDesc)
0,
into->onCommit,
reloptions,
+ true,
allowSystemTableMods);
FreeTupleDesc(tupdesc);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 5feff5d1691..0264b2b3392 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.438 2009/09/22 23:43:37 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.439 2009/10/05 19:24:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2345,6 +2345,17 @@ _copyGrantRoleStmt(GrantRoleStmt *from)
return newnode;
}
+static AlterDefaultPrivilegesStmt *
+_copyAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *from)
+{
+ AlterDefaultPrivilegesStmt *newnode = makeNode(AlterDefaultPrivilegesStmt);
+
+ COPY_NODE_FIELD(options);
+ COPY_NODE_FIELD(action);
+
+ return newnode;
+}
+
static DeclareCursorStmt *
_copyDeclareCursorStmt(DeclareCursorStmt *from)
{
@@ -3760,6 +3771,9 @@ copyObject(void *from)
case T_GrantRoleStmt:
retval = _copyGrantRoleStmt(from);
break;
+ case T_AlterDefaultPrivilegesStmt:
+ retval = _copyAlterDefaultPrivilegesStmt(from);
+ break;
case T_DeclareCursorStmt:
retval = _copyDeclareCursorStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index d7ed08cc68b..5d3cbbda1e4 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -22,7 +22,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.361 2009/09/22 23:43:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.362 2009/10/05 19:24:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1028,6 +1028,15 @@ _equalGrantRoleStmt(GrantRoleStmt *a, GrantRoleStmt *b)
return true;
}
+static bool
+_equalAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *a, AlterDefaultPrivilegesStmt *b)
+{
+ COMPARE_NODE_FIELD(options);
+ COMPARE_NODE_FIELD(action);
+
+ return true;
+}
+
static bool
_equalDeclareCursorStmt(DeclareCursorStmt *a, DeclareCursorStmt *b)
{
@@ -2537,6 +2546,9 @@ equal(void *a, void *b)
case T_GrantRoleStmt:
retval = _equalGrantRoleStmt(a, b);
break;
+ case T_AlterDefaultPrivilegesStmt:
+ retval = _equalAlterDefaultPrivilegesStmt(a, b);
+ break;
case T_DeclareCursorStmt:
retval = _equalDeclareCursorStmt(a, b);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9a203a5a48d..af2f080b634 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.679 2009/09/22 23:43:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.680 2009/10/05 19:24:38 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -188,7 +188,9 @@ static TypeName *TableFuncTypeName(List *columns);
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
- AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt
+ AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
+ AlterRoleStmt AlterRoleSetStmt
+ AlterDefaultPrivilegesStmt DefACLAction
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateGroupStmt CreateOpClassStmt
@@ -269,6 +271,9 @@ static TypeName *TableFuncTypeName(List *columns);
%type privilege_target
%type function_with_argtypes
%type function_with_argtypes_list
+%type defacl_privilege_target
+%type DefACLOption
+%type DefACLOptionList
%type stmtblock stmtmulti
OptTableElementList TableElementList OptInherit definition
@@ -625,6 +630,7 @@ stmtmulti: stmtmulti ';' stmt
stmt :
AlterDatabaseStmt
| AlterDatabaseSetStmt
+ | AlterDefaultPrivilegesStmt
| AlterDomainStmt
| AlterFdwStmt
| AlterForeignServerStmt
@@ -1891,7 +1897,7 @@ reloption_list:
;
/* This should match def_elem and also allow qualified names */
-reloption_elem:
+reloption_elem:
ColLabel '=' def_arg
{
$$ = makeDefElem($1, (Node *) $3);
@@ -4576,6 +4582,93 @@ opt_granted_by: GRANTED BY RoleId { $$ = $3; }
| /*EMPTY*/ { $$ = NULL; }
;
+/*****************************************************************************
+ *
+ * ALTER DEFAULT PRIVILEGES statement
+ *
+ *****************************************************************************/
+
+AlterDefaultPrivilegesStmt:
+ ALTER DEFAULT PRIVILEGES DefACLOptionList DefACLAction
+ {
+ AlterDefaultPrivilegesStmt *n = makeNode(AlterDefaultPrivilegesStmt);
+ n->options = $4;
+ n->action = (GrantStmt *) $5;
+ $$ = (Node*)n;
+ }
+ ;
+
+DefACLOptionList:
+ DefACLOptionList DefACLOption { $$ = lappend($1, $2); }
+ | /* EMPTY */ { $$ = NIL; }
+ ;
+
+DefACLOption:
+ IN_P SCHEMA name_list
+ {
+ $$ = makeDefElem("schemas", (Node *)$3);
+ }
+ | FOR ROLE name_list
+ {
+ $$ = makeDefElem("roles", (Node *)$3);
+ }
+ | FOR USER name_list
+ {
+ $$ = makeDefElem("roles", (Node *)$3);
+ }
+ ;
+
+/*
+ * This should match GRANT/REVOKE, except that target objects are missing
+ * and we only allow a subset of object types.
+ */
+DefACLAction:
+ GRANT privileges ON defacl_privilege_target TO grantee_list
+ opt_grant_grant_option
+ {
+ GrantStmt *n = makeNode(GrantStmt);
+ n->is_grant = true;
+ n->privileges = $2;
+ n->objtype = $4;
+ n->objects = NIL;
+ n->grantees = $6;
+ n->grant_option = $7;
+ $$ = (Node*)n;
+ }
+ | REVOKE privileges ON defacl_privilege_target
+ FROM grantee_list opt_drop_behavior
+ {
+ GrantStmt *n = makeNode(GrantStmt);
+ n->is_grant = false;
+ n->grant_option = false;
+ n->privileges = $2;
+ n->objtype = $4;
+ n->objects = NIL;
+ n->grantees = $6;
+ n->behavior = $7;
+ $$ = (Node *)n;
+ }
+ | REVOKE GRANT OPTION FOR privileges ON defacl_privilege_target
+ FROM grantee_list opt_drop_behavior
+ {
+ GrantStmt *n = makeNode(GrantStmt);
+ n->is_grant = false;
+ n->grant_option = true;
+ n->privileges = $5;
+ n->objtype = $7;
+ n->objects = NIL;
+ n->grantees = $9;
+ n->behavior = $10;
+ $$ = (Node *)n;
+ }
+ ;
+
+defacl_privilege_target:
+ TABLE { $$ = ACL_OBJECT_RELATION; }
+ | FUNCTION { $$ = ACL_OBJECT_FUNCTION; }
+ | SEQUENCE { $$ = ACL_OBJECT_SEQUENCE; }
+ ;
+
/*****************************************************************************
*
@@ -8632,10 +8725,11 @@ a_expr: c_expr { $$ = $1; }
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "<>", $1, (Node *) $6, @2);
}
/*
- * Ideally we would not use hard-wired operators below but instead use
- * opclasses. However, mixed data types and other issues make this
- * difficult: http://archives.postgresql.org/pgsql-hackers/2008-08/msg01142.php
- */
+ * Ideally we would not use hard-wired operators below but
+ * instead use opclasses. However, mixed data types and other
+ * issues make this difficult:
+ * http://archives.postgresql.org/pgsql-hackers/2008-08/msg01142.php
+ */
| a_expr BETWEEN opt_asymmetric b_expr AND b_expr %prec BETWEEN
{
$$ = (Node *) makeA_Expr(AEXPR_AND, NIL,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 0d2079fc0aa..4d7b3c2e9de 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.314 2009/09/22 23:43:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.315 2009/10/05 19:24:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -199,6 +199,7 @@ check_xact_readonly(Node *parsetree)
case T_DropPropertyStmt:
case T_GrantStmt:
case T_GrantRoleStmt:
+ case T_AlterDefaultPrivilegesStmt:
case T_TruncateStmt:
case T_DropOwnedStmt:
case T_ReassignOwnedStmt:
@@ -701,6 +702,10 @@ ProcessUtility(Node *parsetree,
GrantRole((GrantRoleStmt *) parsetree);
break;
+ case T_AlterDefaultPrivilegesStmt:
+ ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt *) parsetree);
+ break;
+
/*
* **************** object creation / destruction *****************
*/
@@ -1687,6 +1692,10 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_AlterDefaultPrivilegesStmt:
+ tag = "ALTER DEFAULT PRIVILEGES";
+ break;
+
case T_DefineStmt:
switch (((DefineStmt *) parsetree)->kind)
{
@@ -2240,6 +2249,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL;
break;
+ case T_AlterDefaultPrivilegesStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_DefineStmt:
lev = LOGSTMT_DDL;
break;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 83d93ad3483..1264dfddb32 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.149 2009/08/03 21:11:39 joe Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.150 2009/10/05 19:24:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -77,6 +77,7 @@ static Acl *allocacl(int n);
static void check_acl(const Acl *acl);
static const char *aclparse(const char *s, AclItem *aip);
static bool aclitem_match(const AclItem *a1, const AclItem *a2);
+static int aclitemComparator(const void *arg1, const void *arg2);
static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
Oid ownerId);
static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs,
@@ -382,6 +383,15 @@ allocacl(int n)
return new_acl;
}
+/*
+ * Create a zero-entry ACL
+ */
+Acl *
+make_empty_acl(void)
+{
+ return allocacl(0);
+}
+
/*
* Copy an ACL
*/
@@ -423,6 +433,98 @@ aclconcat(const Acl *left_acl, const Acl *right_acl)
return result_acl;
}
+/*
+ * Merge two ACLs
+ *
+ * This produces a properly merged ACL with no redundant entries.
+ * Returns NULL on NULL input.
+ */
+Acl *
+aclmerge(const Acl *left_acl, const Acl *right_acl, Oid ownerId)
+{
+ Acl *result_acl;
+ AclItem *aip;
+ int i,
+ num;
+
+ /* Check for cases where one or both are empty/null */
+ if (left_acl == NULL || ACL_NUM(left_acl) == 0)
+ {
+ if (right_acl == NULL || ACL_NUM(right_acl) == 0)
+ return NULL;
+ else
+ return aclcopy(right_acl);
+ }
+ else
+ {
+ if (right_acl == NULL || ACL_NUM(right_acl) == 0)
+ return aclcopy(left_acl);
+ }
+
+ /* Merge them the hard way, one item at a time */
+ result_acl = aclcopy(left_acl);
+
+ aip = ACL_DAT(right_acl);
+ num = ACL_NUM(right_acl);
+
+ for (i = 0; i < num; i++, aip++)
+ {
+ Acl *tmp_acl;
+
+ tmp_acl = aclupdate(result_acl, aip, ACL_MODECHG_ADD,
+ ownerId, DROP_RESTRICT);
+ pfree(result_acl);
+ result_acl = tmp_acl;
+ }
+
+ return result_acl;
+}
+
+/*
+ * Sort the items in an ACL (into an arbitrary but consistent order)
+ */
+void
+aclitemsort(Acl *acl)
+{
+ if (acl != NULL && ACL_NUM(acl) > 1)
+ qsort(ACL_DAT(acl), ACL_NUM(acl), sizeof(AclItem), aclitemComparator);
+}
+
+/*
+ * Check if two ACLs are exactly equal
+ *
+ * This will not detect equality if the two arrays contain the same items
+ * in different orders. To handle that case, sort both inputs first,
+ * using aclitemsort().
+ */
+bool
+aclequal(const Acl *left_acl, const Acl *right_acl)
+{
+ /* Check for cases where one or both are empty/null */
+ if (left_acl == NULL || ACL_NUM(left_acl) == 0)
+ {
+ if (right_acl == NULL || ACL_NUM(right_acl) == 0)
+ return true;
+ else
+ return false;
+ }
+ else
+ {
+ if (right_acl == NULL || ACL_NUM(right_acl) == 0)
+ return false;
+ }
+
+ if (ACL_NUM(left_acl) != ACL_NUM(right_acl))
+ return false;
+
+ if (memcmp(ACL_DAT(left_acl),
+ ACL_DAT(right_acl),
+ ACL_NUM(left_acl) * sizeof(AclItem)) == 0)
+ return true;
+
+ return false;
+}
+
/*
* Verify that an ACL array is acceptable (one-dimensional and has no nulls)
*/
@@ -555,6 +657,31 @@ aclitem_match(const AclItem *a1, const AclItem *a2)
a1->ai_grantor == a2->ai_grantor;
}
+/*
+ * aclitemComparator
+ * qsort comparison function for AclItems
+ */
+static int
+aclitemComparator(const void *arg1, const void *arg2)
+{
+ const AclItem *a1 = (const AclItem *) arg1;
+ const AclItem *a2 = (const AclItem *) arg2;
+
+ if (a1->ai_grantee > a2->ai_grantee)
+ return 1;
+ if (a1->ai_grantee < a2->ai_grantee)
+ return -1;
+ if (a1->ai_grantor > a2->ai_grantor)
+ return 1;
+ if (a1->ai_grantor < a2->ai_grantor)
+ return -1;
+ if (a1->ai_privs > a2->ai_privs)
+ return 1;
+ if (a1->ai_privs < a2->ai_privs)
+ return -1;
+ return 0;
+}
+
/*
* aclitem equality operator
*/
@@ -593,6 +720,9 @@ hash_aclitem(PG_FUNCTION_ARGS)
*
* Change this routine if you want to alter the default access policy for
* newly-created objects (or any object with a NULL acl entry).
+ *
+ * Note that these are the hard-wired "defaults" that are used in the
+ * absence of any pg_default_acl entry.
*/
Acl *
acldefault(GrantObjectType objtype, Oid ownerId)
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 922c4a626f7..efed498f120 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.120 2009/06/11 14:49:05 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.121 2009/10/05 19:24:45 tgl Exp $
*
* NOTES
* These routines allow the parser/planner/executor to perform
@@ -31,6 +31,7 @@
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_default_acl.h"
#include "catalog/pg_enum.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
@@ -344,6 +345,18 @@ static const struct cachedesc cacheinfo[] = {
},
4
},
+ {DefaultAclRelationId, /* DEFACLROLENSPOBJ */
+ DefaultAclRoleNspObjIndexId,
+ 0,
+ 3,
+ {
+ Anum_pg_default_acl_defaclrole,
+ Anum_pg_default_acl_defaclnamespace,
+ Anum_pg_default_acl_defaclobjtype,
+ 0
+ },
+ 256
+ },
{EnumRelationId, /* ENUMOID */
EnumOidIndexId,
0,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 789638fec45..ea314be0fa5 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.107 2009/06/11 14:49:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.108 2009/10/05 19:24:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -93,6 +93,7 @@ getSchemaData(int *numTablesPtr)
TSConfigInfo *cfginfo;
FdwInfo *fdwinfo;
ForeignServerInfo *srvinfo;
+ DefaultACLInfo *daclinfo;
int numNamespaces;
int numAggregates;
int numInherits;
@@ -108,6 +109,7 @@ getSchemaData(int *numTablesPtr)
int numTSConfigs;
int numForeignDataWrappers;
int numForeignServers;
+ int numDefaultACLs;
if (g_verbose)
write_msg(NULL, "reading schemas\n");
@@ -166,6 +168,10 @@ getSchemaData(int *numTablesPtr)
write_msg(NULL, "reading user-defined foreign servers\n");
srvinfo = getForeignServers(&numForeignServers);
+ if (g_verbose)
+ write_msg(NULL, "reading default privileges\n");
+ daclinfo = getDefaultACLs(&numDefaultACLs);
+
if (g_verbose)
write_msg(NULL, "reading user-defined operator families\n");
opfinfo = getOpfamilies(&numOpfamilies);
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 8037c1d7e16..a9a27625707 100644
--- a/src/bin/pg_dump/dumputils.c
+++ b/src/bin/pg_dump/dumputils.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.48 2009/08/04 21:56:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.49 2009/10/05 19:24:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -490,18 +490,22 @@ parsePGArray(const char *atext, char ***itemarray, int *nitems)
* acls: the ACL string fetched from the database
* owner: username of object owner (will be passed through fmtId); can be
* NULL or empty string to indicate "no owner known"
+ * prefix: string to prefix to each generated command; typically empty
* remoteVersion: version of database
*
* Returns TRUE if okay, FALSE if could not parse the acl string.
* The resulting commands (if any) are appended to the contents of 'sql'.
*
+ * Note: when processing a default ACL, prefix is "ALTER DEFAULT PRIVILEGES "
+ * or something similar, and name is an empty string.
+ *
* Note: beware of passing a fmtId() result directly as 'name' or 'subname',
* since this routine uses fmtId() internally.
*/
bool
buildACLCommands(const char *name, const char *subname,
const char *type, const char *acls, const char *owner,
- int remoteVersion,
+ const char *prefix, int remoteVersion,
PQExpBuffer sql)
{
char **aclitems;
@@ -549,7 +553,7 @@ buildACLCommands(const char *name, const char *subname,
* wire-in knowledge about the default public privileges for different
* kinds of objects.
*/
- appendPQExpBuffer(firstsql, "REVOKE ALL");
+ appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
if (subname)
appendPQExpBuffer(firstsql, "(%s)", subname);
appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name);
@@ -564,8 +568,8 @@ buildACLCommands(const char *name, const char *subname,
if (remoteVersion < 80200 && strcmp(type, "DATABASE") == 0)
{
/* database CONNECT priv didn't exist before 8.2 */
- appendPQExpBuffer(firstsql, "GRANT CONNECT ON %s %s TO PUBLIC;\n",
- type, name);
+ appendPQExpBuffer(firstsql, "%sGRANT CONNECT ON %s %s TO PUBLIC;\n",
+ prefix, type, name);
}
/* Scan individual ACL items */
@@ -594,20 +598,20 @@ buildACLCommands(const char *name, const char *subname,
? strcmp(privswgo->data, "ALL") != 0
: strcmp(privs->data, "ALL") != 0)
{
- appendPQExpBuffer(firstsql, "REVOKE ALL");
+ appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
if (subname)
appendPQExpBuffer(firstsql, "(%s)", subname);
appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
type, name, fmtId(grantee->data));
if (privs->len > 0)
appendPQExpBuffer(firstsql,
- "GRANT %s ON %s %s TO %s;\n",
- privs->data, type, name,
+ "%sGRANT %s ON %s %s TO %s;\n",
+ prefix, privs->data, type, name,
fmtId(grantee->data));
if (privswgo->len > 0)
appendPQExpBuffer(firstsql,
- "GRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
- privswgo->data, type, name,
+ "%sGRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
+ prefix, privswgo->data, type, name,
fmtId(grantee->data));
}
}
@@ -623,8 +627,8 @@ buildACLCommands(const char *name, const char *subname,
if (privs->len > 0)
{
- appendPQExpBuffer(secondsql, "GRANT %s ON %s %s TO ",
- privs->data, type, name);
+ appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
+ prefix, privs->data, type, name);
if (grantee->len == 0)
appendPQExpBuffer(secondsql, "PUBLIC;\n");
else if (strncmp(grantee->data, "group ",
@@ -636,8 +640,8 @@ buildACLCommands(const char *name, const char *subname,
}
if (privswgo->len > 0)
{
- appendPQExpBuffer(secondsql, "GRANT %s ON %s %s TO ",
- privswgo->data, type, name);
+ appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
+ prefix, privswgo->data, type, name);
if (grantee->len == 0)
appendPQExpBuffer(secondsql, "PUBLIC");
else if (strncmp(grantee->data, "group ",
@@ -661,7 +665,7 @@ buildACLCommands(const char *name, const char *subname,
*/
if (!found_owner_privs && owner)
{
- appendPQExpBuffer(firstsql, "REVOKE ALL");
+ appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
if (subname)
appendPQExpBuffer(firstsql, "(%s)", subname);
appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
@@ -682,6 +686,50 @@ buildACLCommands(const char *name, const char *subname,
return true;
}
+/*
+ * Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry.
+ *
+ * type: the object type (as seen in GRANT command)
+ * nspname: schema name, or NULL for global default privileges
+ * acls: the ACL string fetched from the database
+ * owner: username of privileges owner (will be passed through fmtId)
+ * remoteVersion: version of database
+ *
+ * Returns TRUE if okay, FALSE if could not parse the acl string.
+ * The resulting commands (if any) are appended to the contents of 'sql'.
+ */
+bool
+buildDefaultACLCommands(const char *type, const char *nspname,
+ const char *acls, const char *owner,
+ int remoteVersion,
+ PQExpBuffer sql)
+{
+ bool result;
+ PQExpBuffer prefix;
+
+ prefix = createPQExpBuffer();
+
+ /*
+ * We incorporate the target role directly into the command, rather than
+ * playing around with SET ROLE or anything like that. This is so that
+ * a permissions error leads to nothing happening, rather than
+ * changing default privileges for the wrong user.
+ */
+ appendPQExpBuffer(prefix, "ALTER DEFAULT PRIVILEGES FOR ROLE %s ",
+ fmtId(owner));
+ if (nspname)
+ appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname));
+
+ result = buildACLCommands("", NULL,
+ type, acls, owner,
+ prefix->data, remoteVersion,
+ sql);
+
+ destroyPQExpBuffer(prefix);
+
+ return result;
+}
+
/*
* This will parse an aclitem string, having the general form
* username=privilegecodes/grantor
diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h
index 3c56c3f9a4d..a5bfe1bcfda 100644
--- a/src/bin/pg_dump/dumputils.h
+++ b/src/bin/pg_dump/dumputils.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.h,v 1.25 2009/08/04 21:56:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.h,v 1.26 2009/10/05 19:24:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -34,8 +34,12 @@ extern int parse_version(const char *versionString);
extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems);
extern bool buildACLCommands(const char *name, const char *subname,
const char *type, const char *acls, const char *owner,
- int remoteVersion,
+ const char *prefix, int remoteVersion,
PQExpBuffer sql);
+extern bool buildDefaultACLCommands(const char *type, const char *nspname,
+ const char *acls, const char *owner,
+ int remoteVersion,
+ PQExpBuffer sql);
extern void processSQLNamePattern(PGconn *conn, PQExpBuffer buf,
const char *pattern,
bool have_where, bool force_escape,
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 20bd3eb7eac..e15e4dbdb9e 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.175 2009/08/07 22:48:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.176 2009/10/05 19:24:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2072,7 +2072,8 @@ ReadToc(ArchiveHandle *AH)
* the entries into sections
*/
if (strcmp(te->desc, "COMMENT") == 0 ||
- strcmp(te->desc, "ACL") == 0)
+ strcmp(te->desc, "ACL") == 0 ||
+ strcmp(te->desc, "DEFAULT ACL") == 0)
te->section = SECTION_NONE;
else if (strcmp(te->desc, "TABLE DATA") == 0 ||
strcmp(te->desc, "BLOBS") == 0 ||
@@ -2227,7 +2228,8 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
return 0;
/* If it's an ACL, maybe ignore it */
- if ((!include_acls || ropt->aclsSkip) && strcmp(te->desc, "ACL") == 0)
+ if ((!include_acls || ropt->aclsSkip) &&
+ (strcmp(te->desc, "ACL") == 0 || strcmp(te->desc, "DEFAULT ACL") == 0))
return 0;
if (!ropt->create && strcmp(te->desc, "DATABASE") == 0)
@@ -2721,12 +2723,14 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
/* ACLs are dumped only during acl pass */
if (acl_pass)
{
- if (strcmp(te->desc, "ACL") != 0)
+ if (!(strcmp(te->desc, "ACL") == 0 ||
+ strcmp(te->desc, "DEFAULT ACL") == 0))
return;
}
else
{
- if (strcmp(te->desc, "ACL") == 0)
+ if (strcmp(te->desc, "ACL") == 0 ||
+ strcmp(te->desc, "DEFAULT ACL") == 0)
return;
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 34ebc27168d..d1715eccce8 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12,7 +12,7 @@
* by PostgreSQL
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.548 2009/09/22 23:43:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.549 2009/10/05 19:24:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -34,6 +34,7 @@
#include "access/sysattr.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_class.h"
+#include "catalog/pg_default_acl.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
@@ -162,6 +163,7 @@ static void dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo);
static void dumpUserMappings(Archive *fout, const char *target,
const char *servername, const char *namespace,
const char *owner, CatalogId catalogId, DumpId dumpId);
+static void dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo);
static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
const char *type, const char *name, const char *subname,
@@ -1049,6 +1051,23 @@ selectDumpableType(TypeInfo *tinfo)
tinfo->dobj.dump = true;
}
+/*
+ * selectDumpableDefaultACL: policy-setting subroutine
+ * Mark a default ACL as to be dumped or not
+ *
+ * For per-schema default ACLs, dump if the schema is to be dumped.
+ * Otherwise dump if we are dumping "everything". Note that dataOnly
+ * and aclsSkip are checked separately.
+ */
+static void
+selectDumpableDefaultACL(DefaultACLInfo *dinfo)
+{
+ if (dinfo->dobj.namespace)
+ dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump;
+ else
+ dinfo->dobj.dump = include_everything;
+}
+
/*
* selectDumpableObject: policy-setting subroutine
* Mark a generic dumpable object as to be dumped or not
@@ -1779,7 +1798,7 @@ dumpDatabase(Archive *AH)
PQExpBuffer loFrozenQry = createPQExpBuffer();
PQExpBuffer loOutQry = createPQExpBuffer();
int i_relfrozenxid;
-
+
appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid\n"
"FROM pg_catalog.pg_class\n"
"WHERE oid = %d;\n",
@@ -1808,7 +1827,7 @@ dumpDatabase(Archive *AH)
loOutQry->data, "", NULL,
NULL, 0,
NULL, NULL);
-
+
PQclear(lo_res);
destroyPQExpBuffer(loFrozenQry);
destroyPQExpBuffer(loOutQry);
@@ -5645,6 +5664,94 @@ getForeignServers(int *numForeignServers)
return srvinfo;
}
+/*
+ * getDefaultACLs:
+ * read all default ACL information in the system catalogs and return
+ * them in the DefaultACLInfo structure
+ *
+ * numDefaultACLs is set to the number of ACLs read in
+ */
+DefaultACLInfo *
+getDefaultACLs(int *numDefaultACLs)
+{
+ DefaultACLInfo *daclinfo;
+ PQExpBuffer query;
+ PGresult *res;
+ int i_oid;
+ int i_tableoid;
+ int i_defaclrole;
+ int i_defaclnamespace;
+ int i_defaclobjtype;
+ int i_defaclacl;
+ int i,
+ ntups;
+
+ if (g_fout->remoteVersion < 80500)
+ {
+ *numDefaultACLs = 0;
+ return NULL;
+ }
+
+ query = createPQExpBuffer();
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ appendPQExpBuffer(query, "SELECT oid, tableoid, "
+ "(%s defaclrole) AS defaclrole, "
+ "defaclnamespace, "
+ "defaclobjtype, "
+ "defaclacl "
+ "FROM pg_default_acl",
+ username_subquery);
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+ *numDefaultACLs = ntups;
+
+ daclinfo = (DefaultACLInfo *) malloc(ntups * sizeof(DefaultACLInfo));
+
+ i_oid = PQfnumber(res, "oid");
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_defaclrole = PQfnumber(res, "defaclrole");
+ i_defaclnamespace = PQfnumber(res, "defaclnamespace");
+ i_defaclobjtype = PQfnumber(res, "defaclobjtype");
+ i_defaclacl = PQfnumber(res, "defaclacl");
+
+ for (i = 0; i < ntups; i++)
+ {
+ Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
+
+ daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
+ daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&daclinfo[i].dobj);
+ /* cheesy ... is it worth coming up with a better object name? */
+ daclinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_defaclobjtype));
+
+ if (nspid != InvalidOid)
+ daclinfo[i].dobj.namespace = findNamespace(nspid,
+ daclinfo[i].dobj.catId.oid);
+ else
+ daclinfo[i].dobj.namespace = NULL;
+
+ daclinfo[i].defaclrole = strdup(PQgetvalue(res, i, i_defaclrole));
+ daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
+ daclinfo[i].defaclacl = strdup(PQgetvalue(res, i, i_defaclacl));
+
+ /* Decide whether we want to dump it */
+ selectDumpableDefaultACL(&(daclinfo[i]));
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return daclinfo;
+}
+
/*
* dumpComment --
*
@@ -6058,6 +6165,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_FOREIGN_SERVER:
dumpForeignServer(fout, (ForeignServerInfo *) dobj);
break;
+ case DO_DEFAULT_ACL:
+ dumpDefaultACL(fout, (DefaultACLInfo *) dobj);
+ break;
case DO_BLOBS:
ArchiveEntry(fout, dobj->catId, dobj->dumpId,
dobj->name, NULL, NULL, "",
@@ -9791,6 +9901,72 @@ dumpUserMappings(Archive *fout, const char *target,
destroyPQExpBuffer(q);
}
+/*
+ * Write out default privileges information
+ */
+static void
+dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo)
+{
+ PQExpBuffer q;
+ PQExpBuffer tag;
+ const char *type;
+
+ /* Skip if not to be dumped */
+ if (!daclinfo->dobj.dump || dataOnly || aclsSkip)
+ return;
+
+ q = createPQExpBuffer();
+ tag = createPQExpBuffer();
+
+ switch (daclinfo->defaclobjtype)
+ {
+ case DEFACLOBJ_RELATION:
+ type = "TABLE";
+ break;
+ case DEFACLOBJ_SEQUENCE:
+ type = "SEQUENCE";
+ break;
+ case DEFACLOBJ_FUNCTION:
+ type = "FUNCTION";
+ break;
+ default:
+ /* shouldn't get here */
+ write_msg(NULL, "unknown object type (%d) in default privileges\n",
+ (int) daclinfo->defaclobjtype);
+ exit_nicely();
+ type = ""; /* keep compiler quiet */
+ }
+
+ appendPQExpBuffer(tag, "DEFAULT %s PRIVILEGES", type);
+
+ /* build the actual command(s) for this tuple */
+ if (!buildDefaultACLCommands(type,
+ daclinfo->dobj.namespace != NULL ?
+ daclinfo->dobj.namespace->dobj.name : NULL,
+ daclinfo->defaclacl,
+ daclinfo->defaclrole,
+ fout->remoteVersion,
+ q))
+ {
+ write_msg(NULL, "could not parse default ACL list (%s)\n",
+ daclinfo->defaclacl);
+ exit_nicely();
+ }
+
+ ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
+ tag->data,
+ daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL,
+ NULL,
+ daclinfo->defaclrole,
+ false, "DEFAULT ACL", SECTION_NONE,
+ q->data, "", NULL,
+ daclinfo->dobj.dependencies, daclinfo->dobj.nDeps,
+ NULL, NULL);
+
+ destroyPQExpBuffer(tag);
+ destroyPQExpBuffer(q);
+}
+
/*----------
* Write out grant/revoke information
*
@@ -9820,7 +9996,8 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
sql = createPQExpBuffer();
- if (!buildACLCommands(name, subname, type, acls, owner, fout->remoteVersion, sql))
+ if (!buildACLCommands(name, subname, type, acls, owner,
+ "", fout->remoteVersion, sql))
{
write_msg(NULL, "could not parse ACL list (%s) for object \"%s\" (%s)\n",
acls, name, type);
@@ -10263,7 +10440,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
fmtId(tbinfo->dobj.name));
appendPQExpBuffer(q, "ALTER COLUMN %s ",
fmtId(tbinfo->attnames[j]));
- appendPQExpBuffer(q, "SET STATISTICS DISTINCT %g;\n",
+ appendPQExpBuffer(q, "SET STATISTICS DISTINCT %g;\n",
tbinfo->attdistinct[j]);
}
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index beec160110c..5b2c3d1e6bf 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.157 2009/09/22 23:43:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.158 2009/10/05 19:24:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -114,6 +114,7 @@ typedef enum
DO_TSCONFIG,
DO_FDW,
DO_FOREIGN_SERVER,
+ DO_DEFAULT_ACL,
DO_BLOBS,
DO_BLOB_COMMENTS
} DumpableObjectType;
@@ -432,6 +433,14 @@ typedef struct _foreignServerInfo
char *srvoptions;
} ForeignServerInfo;
+typedef struct _defaultACLInfo
+{
+ DumpableObject dobj;
+ char *defaclrole;
+ char defaclobjtype;
+ char *defaclacl;
+} DefaultACLInfo;
+
/* global decls */
extern bool force_quotes; /* double-quotes for identifiers flag */
extern bool g_verbose; /* verbose flag */
@@ -516,5 +525,6 @@ extern TSTemplateInfo *getTSTemplates(int *numTSTemplates);
extern TSConfigInfo *getTSConfigurations(int *numTSConfigs);
extern FdwInfo *getForeignDataWrappers(int *numForeignDataWrappers);
extern ForeignServerInfo *getForeignServers(int *numForeignServers);
+extern DefaultACLInfo *getDefaultACLs(int *numDefaultACLs);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index a56f429a7a8..4e95618318b 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump_sort.c,v 1.25 2009/06/11 14:49:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump_sort.c,v 1.26 2009/10/05 19:24:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,8 +23,8 @@ static const char *modulename = gettext_noop("sorter");
* Objects are sorted by priority levels, and within an equal priority level
* by OID. (This is a relatively crude hack to provide semi-reasonable
* behavior for old databases without full dependency info.) Note: text
- * search and foreign-data objects can't really happen here, so the rather
- * bogus priorities for them don't matter.
+ * search, foreign-data, and default ACL objects can't really happen here,
+ * so the rather bogus priorities for them don't matter.
*/
static const int oldObjectTypePriority[] =
{
@@ -54,6 +54,7 @@ static const int oldObjectTypePriority[] =
5, /* DO_TSCONFIG */
3, /* DO_FDW */
4, /* DO_FOREIGN_SERVER */
+ 17, /* DO_DEFAULT_ACL */
10, /* DO_BLOBS */
11 /* DO_BLOB_COMMENTS */
};
@@ -90,6 +91,7 @@ static const int newObjectTypePriority[] =
13, /* DO_TSCONFIG */
14, /* DO_FDW */
15, /* DO_FOREIGN_SERVER */
+ 27, /* DO_DEFAULT_ACL */
20, /* DO_BLOBS */
21 /* DO_BLOB_COMMENTS */
};
@@ -1139,6 +1141,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"FOREIGN SERVER %s (ID %d OID %u)",
obj->name, obj->dumpId, obj->catId.oid);
return;
+ case DO_DEFAULT_ACL:
+ snprintf(buf, bufsize,
+ "DEFAULT ACL %s (ID %d OID %u)",
+ obj->name, obj->dumpId, obj->catId.oid);
+ return;
case DO_BLOBS:
snprintf(buf, bufsize,
"BLOBS (ID %d)",
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 454b95c106c..f0a4d67d2af 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
*
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.126 2009/06/11 14:49:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.127 2009/10/05 19:24:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -989,7 +989,7 @@ dumpTablespaces(PGconn *conn)
if (!skip_acls &&
!buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, spcowner,
- server_version, buf))
+ "", server_version, buf))
{
fprintf(stderr, _("%s: could not parse ACL list (%s) for tablespace \"%s\"\n"),
progname, spcacl, fspcname);
@@ -1289,7 +1289,7 @@ dumpCreateDB(PGconn *conn)
if (!skip_acls &&
!buildACLCommands(fdbname, NULL, "DATABASE", dbacl, dbowner,
- server_version, buf))
+ "", server_version, buf))
{
fprintf(stderr, _("%s: could not parse ACL list (%s) for database \"%s\"\n"),
progname, dbacl, fdbname);
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 67f05a89de0..d94d8b80c52 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.207 2009/09/13 22:18:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.208 2009/10/05 19:24:46 tgl Exp $
*/
#include "postgres_fe.h"
#include "command.h"
@@ -361,7 +361,10 @@ exec_command(const char *cmd,
success = listCasts(pattern);
break;
case 'd':
- success = objectDescription(pattern, show_system);
+ if (strcmp(cmd, "ddp") == 0)
+ success = listDefaultACLs(pattern);
+ else
+ success = objectDescription(pattern, show_system);
break;
case 'D':
success = listDomains(pattern, show_system);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 6e288da67a8..1644623812c 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -8,7 +8,7 @@
*
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.226 2009/07/29 20:56:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.227 2009/10/05 19:24:46 tgl Exp $
*/
#include "postgres_fe.h"
@@ -732,6 +732,73 @@ permissionsList(const char *pattern)
}
+/*
+ * \ddp
+ *
+ * List DefaultACLs. The pattern can match either schema or role name.
+ */
+bool
+listDefaultACLs(const char *pattern)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {false, false, true, false};
+
+ if (pset.sversion < 80500)
+ {
+ fprintf(stderr, _("The server (version %d.%d) does not support altering default privileges.\n"),
+ pset.sversion / 10000, (pset.sversion / 100) % 100);
+ return true;
+ }
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS \"%s\",\n"
+ " n.nspname AS \"%s\",\n"
+ " CASE d.defaclobjtype WHEN 'r' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END AS \"%s\",\n"
+ " ",
+ gettext_noop("Owner"),
+ gettext_noop("Schema"),
+ gettext_noop("table"),
+ gettext_noop("sequence"),
+ gettext_noop("function"),
+ gettext_noop("Type"));
+
+ printACLColumn(&buf, "d.defaclacl");
+
+ appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_default_acl d\n"
+ " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ NULL,
+ "n.nspname",
+ "pg_catalog.pg_get_userbyid(d.defaclrole)",
+ NULL);
+
+ appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3;");
+
+ res = PSQLexec(buf.data, false);
+ if (!res)
+ {
+ termPQExpBuffer(&buf);
+ return false;
+ }
+
+ myopt.nullPrint = NULL;
+ printfPQExpBuffer(&buf, _("Default access privileges"));
+ myopt.title = buf.data;
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ termPQExpBuffer(&buf);
+ PQclear(res);
+ return true;
+}
+
/*
* Get object comments
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 769ee9e975d..169ceb3739a 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/describe.h,v 1.40 2009/04/21 15:49:06 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.h,v 1.41 2009/10/05 19:24:46 tgl Exp $
*/
#ifndef DESCRIBE_H
#define DESCRIBE_H
@@ -30,6 +30,9 @@ extern bool describeRoles(const char *pattern, bool verbose);
/* \z (or \dp) */
extern bool permissionsList(const char *pattern);
+/* \ddp */
+extern bool listDefaultACLs(const char *pattern);
+
/* \dd */
extern bool objectDescription(const char *pattern, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 947eff050a1..f21099a6923 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.152 2009/09/18 05:00:42 petere Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.153 2009/10/05 19:24:46 tgl Exp $
*/
#include "postgres_fe.h"
@@ -201,6 +201,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dc[S] [PATTERN] list conversions\n"));
fprintf(output, _(" \\dC [PATTERN] list casts\n"));
fprintf(output, _(" \\dd[S] [PATTERN] show comments on objects\n"));
+ fprintf(output, _(" \\ddp [PATTERN] list default privileges\n"));
fprintf(output, _(" \\dD[S] [PATTERN] list domains\n"));
fprintf(output, _(" \\des[+] [PATTERN] list foreign servers\n"));
fprintf(output, _(" \\deu[+] [PATTERN] list user mappings\n"));
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 4ae91575566..1d3bfb0726c 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.540 2009/09/26 22:42:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.541 2009/10/05 19:24:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200909261
+#define CATALOG_VERSION_NO 200910051
#endif
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index fe04aab9643..954d3808a85 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.40 2009/06/11 14:49:09 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.41 2009/10/05 19:24:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -146,6 +146,7 @@ typedef enum ObjectClass
OCLASS_FDW, /* pg_foreign_data_wrapper */
OCLASS_FOREIGN_SERVER, /* pg_foreign_server */
OCLASS_USER_MAPPING, /* pg_user_mapping */
+ OCLASS_DEFACL, /* pg_default_acl */
MAX_OCLASS /* MUST BE LAST */
} ObjectClass;
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 0d42cf11963..5faf9c1c014 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.93 2009/09/26 22:42:02 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.94 2009/10/05 19:24:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -57,6 +57,7 @@ extern Oid heap_create_with_catalog(const char *relname,
int oidinhcount,
OnCommitAction oncommit,
Datum reloptions,
+ bool use_user_acl,
bool allow_system_table_mods);
extern void heap_drop_with_catalog(Oid relid);
@@ -76,6 +77,7 @@ extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
extern void InsertPgClassTuple(Relation pg_class_desc,
Relation new_rel_desc,
Oid new_rel_oid,
+ Datum relacl,
Datum reloptions);
extern List *AddRelationNewConstraints(Relation rel,
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ce117a8eecd..0272334f2e5 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.108 2009/06/11 14:49:09 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.109 2009/10/05 19:24:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -267,6 +267,11 @@ DECLARE_UNIQUE_INDEX(pg_user_mapping_oid_index, 174, on pg_user_mapping using bt
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_default_acl_role_nsp_obj_index, 827, on pg_default_acl using btree(defaclrole oid_ops, defaclnamespace oid_ops, defaclobjtype char_ops));
+#define DefaultAclRoleNspObjIndexId 827
+DECLARE_UNIQUE_INDEX(pg_default_acl_oid_index, 828, on pg_default_acl using btree(oid oid_ops));
+#define DefaultAclOidIndexId 828
+
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
new file mode 100644
index 00000000000..cfd1f2b3a9b
--- /dev/null
+++ b/src/include/catalog/pg_default_acl.h
@@ -0,0 +1,75 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_default_acl.h
+ * definition of default ACLs for new objects.
+ *
+ *
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/include/catalog/pg_default_acl.h,v 1.1 2009/10/05 19:24:48 tgl Exp $
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_DEFAULT_ACL_H
+#define PG_DEFAULT_ACL_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_default_acl definition. cpp turns this into
+ * typedef struct FormData_pg_default_acl
+ * ----------------
+ */
+#define DefaultAclRelationId 826
+
+CATALOG(pg_default_acl,826)
+{
+ Oid defaclrole; /* OID of role owning this ACL */
+ Oid defaclnamespace; /* OID of namespace, or 0 for all */
+ char defaclobjtype; /* see DEFACLOBJ_xxx constants below */
+
+ /*
+ * VARIABLE LENGTH FIELDS start here.
+ */
+
+ aclitem defaclacl[1]; /* permissions to add at CREATE time */
+} FormData_pg_default_acl;
+
+/* ----------------
+ * Form_pg_default_acl corresponds to a pointer to a tuple with
+ * the format of pg_default_acl relation.
+ * ----------------
+ */
+typedef FormData_pg_default_acl *Form_pg_default_acl;
+
+/* ----------------
+ * compiler constants for pg_default_acl
+ * ----------------
+ */
+
+#define Natts_pg_default_acl 4
+#define Anum_pg_default_acl_defaclrole 1
+#define Anum_pg_default_acl_defaclnamespace 2
+#define Anum_pg_default_acl_defaclobjtype 3
+#define Anum_pg_default_acl_defaclacl 4
+
+/* ----------------
+ * pg_default_acl has no initial contents
+ * ----------------
+ */
+
+/*
+ * Types of objects for which the user is allowed to specify default
+ * permissions through pg_default_acl. These codes are used in the
+ * defaclobjtype column.
+ */
+#define DEFACLOBJ_RELATION 'r' /* table, view */
+#define DEFACLOBJ_SEQUENCE 'S' /* sequence */
+#define DEFACLOBJ_FUNCTION 'f' /* function */
+
+#endif /* PG_DEFAULT_ACL_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index d842c9c3199..5fd046e95b8 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.226 2009/09/22 23:43:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.227 2009/10/05 19:24:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -261,6 +261,7 @@ typedef enum NodeTag
T_SetOperationStmt,
T_GrantStmt,
T_GrantRoleStmt,
+ T_AlterDefaultPrivilegesStmt,
T_ClosePortalStmt,
T_ClusterStmt,
T_CopyStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 1ce28b77541..24066b9b38e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.402 2009/09/22 23:43:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.403 2009/10/05 19:24:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1259,6 +1259,17 @@ typedef struct GrantRoleStmt
DropBehavior behavior; /* drop behavior (for REVOKE) */
} GrantRoleStmt;
+/* ----------------------
+ * Alter Default Privileges Statement
+ * ----------------------
+ */
+typedef struct AlterDefaultPrivilegesStmt
+{
+ NodeTag type;
+ List *options; /* list of DefElem */
+ GrantStmt *action; /* GRANT/REVOKE action (with objects=NIL) */
+} AlterDefaultPrivilegesStmt;
+
/* ----------------------
* Copy Statement
*
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 5c38822d5d7..977b00de798 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.108 2009/06/11 14:49:13 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.109 2009/10/05 19:24:49 tgl Exp $
*
* NOTES
* An ACL array is simply an array of AclItems, representing the union
@@ -193,41 +193,23 @@ typedef enum AclObjectKind
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
-/*
- * The information about one Grant/Revoke statement, in internal format: object
- * and grantees names have been turned into Oids, the privilege list is an
- * AclMode bitmask. If 'privileges' is ACL_NO_RIGHTS (the 0 value) and
- * all_privs is true, 'privileges' will be internally set to the right kind of
- * ACL_ALL_RIGHTS_*, depending on the object type (NB - this will modify the
- * InternalGrant struct!)
- *
- * Note: 'all_privs' and 'privileges' represent object-level privileges only.
- * There might also be column-level privilege specifications, which are
- * represented in col_privs (this is a list of untransformed AccessPriv nodes).
- * Column privileges are only valid for objtype ACL_OBJECT_RELATION.
- */
-typedef struct
-{
- bool is_grant;
- GrantObjectType objtype;
- List *objects;
- bool all_privs;
- AclMode privileges;
- List *col_privs;
- List *grantees;
- bool grant_option;
- DropBehavior behavior;
-} InternalGrant;
/*
* routines used internally
*/
extern Acl *acldefault(GrantObjectType objtype, Oid ownerId);
+extern Acl *get_user_default_acl(GrantObjectType objtype, Oid ownerId,
+ Oid nsp_oid);
+
extern Acl *aclupdate(const Acl *old_acl, const AclItem *mod_aip,
int modechg, Oid ownerId, DropBehavior behavior);
extern Acl *aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId);
+extern Acl *make_empty_acl(void);
extern Acl *aclcopy(const Acl *orig_acl);
extern Acl *aclconcat(const Acl *left_acl, const Acl *right_acl);
+extern Acl *aclmerge(const Acl *left_acl, const Acl *right_acl, Oid ownerId);
+extern void aclitemsort(Acl *acl);
+extern bool aclequal(const Acl *left_acl, const Acl *right_acl);
extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId,
AclMode mask, AclMaskHow how);
@@ -261,7 +243,10 @@ extern Datum hash_aclitem(PG_FUNCTION_ARGS);
* prototypes for functions in aclchk.c
*/
extern void ExecuteGrantStmt(GrantStmt *stmt);
-extern void ExecGrantStmt_oids(InternalGrant *istmt);
+extern void ExecAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *stmt);
+
+extern void RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid);
+extern void RemoveDefaultACLById(Oid defaclOid);
extern AclMode pg_attribute_aclmask(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mask, AclMaskHow how);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 1428b28d154..f0647e34194 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.74 2009/01/01 17:24:02 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.75 2009/10/05 19:24:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -49,6 +49,7 @@ enum SysCacheIdentifier
CONSTROID,
CONVOID,
DATABASEOID,
+ DEFACLROLENSPOBJ,
ENUMOID,
ENUMTYPOIDNAME,
FOREIGNDATAWRAPPERNAME,
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 809b6562171..24239276dbf 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -836,6 +836,117 @@ SELECT has_sequence_privilege('x_seq', 'USAGE');
t
(1 row)
+-- test default ACLs
+\c -
+CREATE SCHEMA testns;
+GRANT ALL ON SCHEMA testns TO regressuser1;
+CREATE TABLE testns.acltest1 (x int);
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- no
+ has_table_privilege
+---------------------
+ f
+(1 row)
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+ has_table_privilege
+---------------------
+ f
+(1 row)
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT SELECT ON TABLE TO public;
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- no
+ has_table_privilege
+---------------------
+ f
+(1 row)
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+ has_table_privilege
+---------------------
+ f
+(1 row)
+
+DROP TABLE testns.acltest1;
+CREATE TABLE testns.acltest1 (x int);
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- yes
+ has_table_privilege
+---------------------
+ t
+(1 row)
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+ has_table_privilege
+---------------------
+ f
+(1 row)
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT INSERT ON TABLE TO regressuser1;
+DROP TABLE testns.acltest1;
+CREATE TABLE testns.acltest1 (x int);
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- yes
+ has_table_privilege
+---------------------
+ t
+(1 row)
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- yes
+ has_table_privilege
+---------------------
+ t
+(1 row)
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns REVOKE INSERT ON TABLE FROM regressuser1;
+DROP TABLE testns.acltest1;
+CREATE TABLE testns.acltest1 (x int);
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- yes
+ has_table_privilege
+---------------------
+ t
+(1 row)
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+ has_table_privilege
+---------------------
+ f
+(1 row)
+
+ALTER DEFAULT PRIVILEGES FOR ROLE regressuser1 REVOKE EXECUTE ON FUNCTION FROM public;
+SET ROLE regressuser1;
+CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql;
+SELECT has_function_privilege('regressuser2', 'testns.foo()', 'EXECUTE'); -- no
+ has_function_privilege
+------------------------
+ f
+(1 row)
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT EXECUTE ON FUNCTION to public;
+DROP FUNCTION testns.foo();
+CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql;
+SELECT has_function_privilege('regressuser2', 'testns.foo()', 'EXECUTE'); -- yes
+ has_function_privilege
+------------------------
+ t
+(1 row)
+
+DROP FUNCTION testns.foo();
+RESET ROLE;
+SELECT count(*)
+ FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid
+ WHERE nspname = 'testns';
+ count
+-------
+ 2
+(1 row)
+
+DROP SCHEMA testns CASCADE;
+NOTICE: drop cascades to table testns.acltest1
+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;
+ defaclrole | defaclnamespace | defaclobjtype | defaclacl
+------------+-----------------+---------------+-----------
+(0 rows)
+
-- clean up
\c
drop sequence x_seq;
@@ -860,7 +971,9 @@ DROP TABLE atestp1;
DROP TABLE atestp2;
DROP GROUP regressgroup1;
DROP GROUP regressgroup2;
+-- these are needed to clean up permissions
REVOKE USAGE ON LANGUAGE sql FROM regressuser1;
+DROP OWNED BY regressuser1;
DROP USER regressuser1;
DROP USER regressuser2;
DROP USER regressuser3;
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 7213192d5f3..1994edc905e 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -95,6 +95,7 @@ SELECT relname, relhasindex
pg_constraint | t
pg_conversion | t
pg_database | t
+ pg_default_acl | t
pg_depend | t
pg_description | t
pg_enum | t
@@ -151,7 +152,7 @@ SELECT relname, relhasindex
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
-(140 rows)
+(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 917e8e5da4e..eaa879efa22 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -484,6 +484,73 @@ SET SESSION AUTHORIZATION regressuser2;
SELECT has_sequence_privilege('x_seq', 'USAGE');
+
+-- test default ACLs
+\c -
+
+CREATE SCHEMA testns;
+GRANT ALL ON SCHEMA testns TO regressuser1;
+
+CREATE TABLE testns.acltest1 (x int);
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- no
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT SELECT ON TABLE TO public;
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- no
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+
+DROP TABLE testns.acltest1;
+CREATE TABLE testns.acltest1 (x int);
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- yes
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT INSERT ON TABLE TO regressuser1;
+
+DROP TABLE testns.acltest1;
+CREATE TABLE testns.acltest1 (x int);
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- yes
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- yes
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns REVOKE INSERT ON TABLE FROM regressuser1;
+
+DROP TABLE testns.acltest1;
+CREATE TABLE testns.acltest1 (x int);
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- yes
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+
+ALTER DEFAULT PRIVILEGES FOR ROLE regressuser1 REVOKE EXECUTE ON FUNCTION FROM public;
+
+SET ROLE regressuser1;
+
+CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql;
+
+SELECT has_function_privilege('regressuser2', 'testns.foo()', 'EXECUTE'); -- no
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT EXECUTE ON FUNCTION to public;
+
+DROP FUNCTION testns.foo();
+CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql;
+
+SELECT has_function_privilege('regressuser2', 'testns.foo()', 'EXECUTE'); -- yes
+
+DROP FUNCTION testns.foo();
+
+RESET ROLE;
+
+SELECT count(*)
+ FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid
+ WHERE nspname = 'testns';
+
+DROP SCHEMA testns 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;
+
-- clean up
\c
@@ -513,7 +580,10 @@ DROP TABLE atestp2;
DROP GROUP regressgroup1;
DROP GROUP regressgroup2;
+-- these are needed to clean up permissions
REVOKE USAGE ON LANGUAGE sql FROM regressuser1;
+DROP OWNED BY regressuser1;
+
DROP USER regressuser1;
DROP USER regressuser2;
DROP USER regressuser3;