Create an ALTER DEFAULT PRIVILEGES command, which allows users to adjust

the privileges that will be applied to subsequently-created objects.

Such adjustments are always per owning role, and can be restricted to objects
created in particular schemas too.  A notable benefit is that users can
override the traditional default privilege settings, eg, the PUBLIC EXECUTE
privilege traditionally granted by default for functions.

Petr Jelinek
This commit is contained in:
Tom Lane 2009-10-05 19:24:49 +00:00
parent 41f89e3bbc
commit 249724cb01
48 changed files with 2240 additions and 180 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.207 2009/09/22 23:43:37 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.208 2009/10/05 19:24:32 tgl Exp $ -->
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
-->
@ -113,6 +113,11 @@
<entry>databases within this database cluster</entry>
</row>
<row>
<entry><link linkend="catalog-pg-default-acl"><structname>pg_default_acl</structname></link></entry>
<entry>default privileges for object types</entry>
</row>
<row>
<entry><link linkend="catalog-pg-depend"><structname>pg_depend</structname></link></entry>
<entry>dependencies between database objects</entry>
@ -2155,6 +2160,93 @@
</sect1>
<sect1 id="catalog-pg-default-acl">
<title><structname>pg_default_acl</structname></title>
<indexterm zone="catalog-pg-default-acl">
<primary>pg_default_acl</primary>
</indexterm>
<para>
The catalog <structname>pg_default_acl</> stores initial
privileges to be assigned to newly created objects.
</para>
<table>
<title><structname>pg_default_acl</> Columns</title>
<tgroup cols="4">
<thead>
<row>
<entry>Name</entry>
<entry>Type</entry>
<entry>References</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><structfield>defaclrole</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
<entry>The OID of the role associated with this entry</entry>
</row>
<row>
<entry><structfield>defaclnamespace</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
<entry>The OID of the namespace associated with this entry,
or 0 if none</entry>
</row>
<row>
<entry><structfield>defaclobjtype</structfield></entry>
<entry><type>char</type></entry>
<entry></entry>
<entry>
Type of object this entry is for:
<literal>r</> = relation (table, view),
<literal>S</> = sequence,
<literal>f</> = function
</entry>
</row>
<row>
<entry><structfield>defaclacl</structfield></entry>
<entry><type>aclitem[]</type></entry>
<entry></entry>
<entry>
Access privileges that this type of object should have on creation
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
A <structname>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: <quote>global</> entries with
<structfield>defaclnamespace</> = 0, and <quote>per-schema</> entries
that reference a particular schema. If a global entry is present then
it <emphasis>overrides</> the normal hard-wired default privileges
for the object type. A per-schema entry, if present, represents privileges
to be <emphasis>added to</> the global or hard-wired default privileges.
</para>
<para>
Note that when an ACL entry in another catalog is NULL, it is taken
to represent the hard-wired default privileges for its object,
<emphasis>not</> whatever might be in <structname>pg_default_acl</>
at the moment. <structname>pg_default_acl</> is only consulted during
object creation.
</para>
</sect1>
<sect1 id="catalog-pg-depend">
<title><structname>pg_depend</structname></title>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.75 2009/09/22 23:43:37 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.76 2009/10/05 19:24:33 tgl Exp $
PostgreSQL documentation
Complete list of usable sgml source files in this directory.
-->
@ -9,6 +9,7 @@ Complete list of usable sgml source files in this directory.
<!entity alterAggregate system "alter_aggregate.sgml">
<!entity alterConversion system "alter_conversion.sgml">
<!entity alterDatabase system "alter_database.sgml">
<!entity alterDefaultPrivileges system "alter_default_privileges.sgml">
<!entity alterDomain system "alter_domain.sgml">
<!entity alterForeignDataWrapper system "alter_foreign_data_wrapper.sgml">
<!entity alterFunction system "alter_function.sgml">

View File

@ -0,0 +1,211 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_default_privileges.sgml,v 1.1 2009/10/05 19:24:33 tgl Exp $
PostgreSQL documentation
-->
<refentry id="SQL-ALTERDEFAULTPRIVILEGES">
<refmeta>
<refentrytitle id="SQL-ALTERDEFAULTPRIVILEGES-TITLE">ALTER DEFAULT PRIVILEGES</refentrytitle>
<manvolnum>7</manvolnum>
<refmiscinfo>SQL - Language Statements</refmiscinfo>
</refmeta>
<refnamediv>
<refname>ALTER DEFAULT PRIVILEGES</refname>
<refpurpose>define default access privileges</refpurpose>
</refnamediv>
<indexterm zone="sql-alterdefaultprivileges">
<primary>ALTER DEFAULT PRIVILEGES</primary>
</indexterm>
<refsynopsisdiv>
<synopsis>
ALTER DEFAULT PRIVILEGES
[ FOR { ROLE | USER } <replaceable>target_role</replaceable> [, ...] ]
[ IN SCHEMA <replaceable>schema_name</replaceable> [, ...] ]
<replaceable class="parameter">abbreviated_grant_or_revoke</replaceable>
<phrase>where <replaceable class="parameter">abbreviated_grant_or_revoke</replaceable> is one of:</phrase>
GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
[,...] | ALL [ PRIVILEGES ] }
ON TABLE
TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
GRANT { { USAGE | SELECT | UPDATE }
[,...] | ALL [ PRIVILEGES ] }
ON SEQUENCE
TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
GRANT { EXECUTE | ALL [ PRIVILEGES ] }
ON FUNCTION
TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
REVOKE [ GRANT OPTION FOR ]
{ { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
[,...] | ALL [ PRIVILEGES ] }
ON TABLE
FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
REVOKE [ GRANT OPTION FOR ]
{ { USAGE | SELECT | UPDATE }
[,...] | ALL [ PRIVILEGES ] }
ON SEQUENCE
FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
REVOKE [ GRANT OPTION FOR ]
{ EXECUTE | ALL [ PRIVILEGES ] }
ON FUNCTION
FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
<refsect1 id="sql-alterdefaultprivileges-description">
<title>Description</title>
<para>
<command>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.
</para>
<para>
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.
</para>
<para>
As explained under <xref linkend="sql-grant" endterm="sql-grant-title">,
the default privileges for any object type normally grant all grantable
permissions to the object owner, and may grant some privileges to
<literal>PUBLIC</> as well. However, this behavior can be changed by
altering the global default privileges with
<command>ALTER DEFAULT PRIVILEGES</>.
</para>
<refsect2>
<title>Parameters</title>
<variablelist>
<varlistentry>
<term><replaceable>target_role</replaceable></term>
<listitem>
<para>
The name of an existing role of which the current role is a member.
If <literal>FOR ROLE</> is omitted, the current role is assumed.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>schema_name</replaceable></term>
<listitem>
<para>
The name of an existing schema. Each <replaceable>target_role</>
must have <literal>CREATE</> privileges for each specified schema.
If <literal>IN SCHEMA</> is omitted, the global default privileges
are altered.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>role_name</replaceable></term>
<listitem>
<para>
The name of an existing role to grant or revoke privileges for.
This parameter, and all the other parameters in
<replaceable class="parameter">abbreviated_grant_or_revoke</>,
act as described under
<xref linkend="sql-grant" endterm="sql-grant-title"> or
<xref linkend="sql-revoke" endterm="sql-revoke-title">,
except that one is setting permissions for a whole class of objects
rather than specific named objects.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect2>
</refsect1>
<refsect1 id="sql-alterdefaultprivileges-notes">
<title>Notes</title>
<para>
Use <xref linkend="app-psql">'s <command>\ddp</command> command
to obtain information about existing assignments of default privileges.
The meaning of the privilege values is the same as explained for
<command>\dp</command> under
<xref linkend="sql-grant" endterm="sql-grant-title">.
</para>
<para>
If you wish to drop a role that has had its global default privileges
altered, it is necessary to use <command>DROP OWNED BY</> first,
to get rid of the default privileges entry for the role.
</para>
</refsect1>
<refsect1 id="sql-alterdefaultprivileges-examples">
<title>Examples</title>
<para>
Grant SELECT privilege to everyone for all tables (and views) you
subsequently create in schema <literal>myschema</literal>, and allow
role <literal>webuser</> to INSERT into them too:
<programlisting>
ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT SELECT ON TABLE TO PUBLIC;
ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT INSERT ON TABLE TO webuser;
</programlisting>
</para>
<para>
Undo the above, so that subsequently-created tables won't have any
more permissions than normal:
<programlisting>
ALTER DEFAULT PRIVILEGES IN SCHEMA myschema REVOKE SELECT ON TABLE FROM PUBLIC;
ALTER DEFAULT PRIVILEGES IN SCHEMA myschema REVOKE INSERT ON TABLE FROM webuser;
</programlisting>
</para>
<para>
Remove the public EXECUTE permission that is normally granted on functions,
for all functions subsequently created by role <literal>admin</>:
<programlisting>
ALTER DEFAULT PRIVILEGES FOR ROLE admin REVOKE EXECUTE ON FUNCTION FROM PUBLIC;
</programlisting>
</para>
</refsect1>
<refsect1>
<title>Compatibility</title>
<para>
There is no <command>ALTER DEFAULT PRIVILEGES</command> statement in the SQL
standard.
</para>
</refsect1>
<refsect1>
<title>See Also</title>
<simplelist type="inline">
<member><xref linkend="sql-grant" endterm="sql-grant-title"></member>
<member><xref linkend="sql-revoke" endterm="sql-revoke-title"></member>
</simplelist>
</refsect1>
</refentry>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/grant.sgml,v 1.77 2009/09/19 10:23:27 petere Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/grant.sgml,v 1.78 2009/10/05 19:24:34 tgl Exp $
PostgreSQL documentation
-->
@ -80,14 +80,6 @@ GRANT <replaceable class="PARAMETER">role_name</replaceable> [, ...] TO <replace
they are different enough to be described separately.
</para>
<para>
As of <productname>PostgreSQL</productname> 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 <literal>GROUP</>
to identify whether a grantee is a user or a group. <literal>GROUP</>
is still allowed in the command, but it is a noise word.
</para>
<refsect2 id="sql-grant-description-objects">
<title>GRANT on Database Objects</title>
@ -145,6 +137,9 @@ GRANT <replaceable class="PARAMETER">role_name</replaceable> [, ...] TO <replace
security, issue the <command>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
<xref linkend="sql-alterdefaultprivileges" endterm="sql-alterdefaultprivileges-title">
command.
</para>
<para>
@ -388,6 +383,14 @@ GRANT <replaceable class="PARAMETER">role_name</replaceable> [, ...] TO <replace
to revoke access privileges.
</para>
<para>
Since <productname>PostgreSQL</productname> 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 <literal>GROUP</>
to identify whether a grantee is a user or a group. <literal>GROUP</>
is still allowed in the command, but it is a noise word.
</para>
<para>
A user may perform <command>SELECT</>, <command>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;
<command>REVOKE</> on an object
will instantiate the default privileges (producing, for example,
<literal>{miriam=arwdDxt/miriam}</>) and then modify them per the
specified request. Entries are shown in <quote>Column access
specified request. Similarly, entries are shown in <quote>Column access
privileges</> only for columns with nondefault privileges.
(Note: for this purpose, <quote>default privileges</> always means the
built-in default privileges for the object's type. An object whose
privileges have been affected by an <command>ALTER DEFAULT PRIVILEGES</>
command will always be shown with an explicit privilege entry that
includes the effects of the <command>ALTER</>.)
</para>
<para>
@ -602,9 +610,10 @@ GRANT admins TO joe;
<refsect1>
<title>See Also</title>
<simpara>
<xref linkend="sql-revoke" endterm="sql-revoke-title">
</simpara>
<simplelist type="inline">
<member><xref linkend="sql-revoke" endterm="sql-revoke-title"></member>
<member><xref linkend="sql-alterdefaultprivileges" endterm="sql-alterdefaultprivileges-title"></member>
</simplelist>
</refsect1>
</refentry>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.229 2009/08/11 12:02:58 momjian Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.230 2009/10/05 19:24:34 tgl Exp $
PostgreSQL documentation
-->
@ -978,6 +978,29 @@ testdb=&gt;
</varlistentry>
<varlistentry>
<term><literal>\ddp [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
<listitem>
<para>
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 <replaceable class="parameter">pattern</replaceable> is
specified, only entries whose role name or schema name matches
the pattern are listed.
</para>
<para>
The <xref linkend="sql-alterdefaultprivileges"
endterm="sql-alterdefaultprivileges-title"> command is used to set
default access privileges. The meaning of the
privilege display is explained under
<xref linkend="sql-grant" endterm="sql-grant-title">.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>\dD[S] [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
<listitem>
@ -1142,8 +1165,8 @@ testdb=&gt;
class="parameter">pattern</replaceable> is specified, only
those roles whose names match the pattern are listed.
(This command is now effectively the same as <literal>\du</literal>).
If the form <literal>\dg+</literal> is used, additional information
is shown about each role, including the comment for each role.
If the form <literal>\dg+</literal> is used, additional information
is shown about each role, including the comment for each role.
</para>
</listitem>
</varlistentry>
@ -1235,7 +1258,9 @@ testdb=&gt;
<para>
The <xref linkend="sql-grant" endterm="sql-grant-title"> and
<xref linkend="sql-revoke" endterm="sql-revoke-title">
commands are used to set access privileges.
commands are used to set access privileges. The meaning of the
privilege display is explained under
<xref linkend="sql-grant" endterm="sql-grant-title">.
</para>
</listitem>
</varlistentry>
@ -2045,12 +2070,6 @@ lo_import 152801
specified, only tables,views and sequences whose names match the pattern are listed.
</para>
<para>
The <xref linkend="sql-grant" endterm="sql-grant-title"> and
<xref linkend="sql-revoke" endterm="sql-revoke-title">
commands are used to set access privileges.
</para>
<para>
This is an alias for <command>\dp</command> (<quote>display
privileges</quote>).

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.68 2009/09/22 23:43:37 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.69 2009/10/05 19:24:33 tgl Exp $ -->
<part id="reference">
<title>Reference</title>
@ -37,6 +37,7 @@
&alterAggregate;
&alterConversion;
&alterDatabase;
&alterDefaultPrivileges;
&alterDomain;
&alterForeignDataWrapper;
&alterFunction;

View File

@ -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);
}

View File

@ -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 \
)

View File

@ -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;
}

View File

@ -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,

View File

@ -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);
}
}
/*

View File

@ -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 */

View File

@ -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);

View File

@ -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 */

View File

@ -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 */

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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 <privtarget> privilege_target
%type <funwithargs> function_with_argtypes
%type <list> function_with_argtypes_list
%type <ival> defacl_privilege_target
%type <defelt> DefACLOption
%type <list> DefACLOptionList
%type <list> 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,

View File

@ -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;

View File

@ -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)

View File

@ -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,

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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;
}

View File

@ -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]);
}

View File

@ -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 */

View File

@ -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)",

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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"));

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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

View File

@ -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 */

View File

@ -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,

View File

@ -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
*

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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;