-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.190 2009/01/22 17:27:54 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.191 2009/01/22 20:15:59 tgl Exp $ -->
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
-->
</entry>
</row>
+ <row>
+ <entry><structfield>attacl</structfield></entry>
+ <entry><type>aclitem[]</type></entry>
+ <entry></entry>
+ <entry>
+ Column-level access privileges, if any have been granted specifically
+ on this column
+ </entry>
+ </row>
+
</tbody>
</tgroup>
</table>
<entry>The OID of the specific dependent object</entry>
</row>
+ <row>
+ <entry><structfield>objsubid</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>
+ For a table column, this is the column number (the
+ <structfield>objid</> and <structfield>classid</> refer to the
+ table itself). For all other object types, this column is zero
+ </entry>
+ </row>
+
<row>
<entry><structfield>refclassid</structfield></entry>
<entry><type>oid</type></entry>
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/grant.sgml,v 1.73 2008/12/19 16:25:16 petere Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/grant.sgml,v 1.74 2009/01/22 20:15:59 tgl Exp $
PostgreSQL documentation
-->
ON [ TABLE ] <replaceable class="PARAMETER">tablename</replaceable> [, ...]
TO { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+GRANT { { SELECT | INSERT | UPDATE | REFERENCES } ( <replaceable class="PARAMETER">column</replaceable> [, ...] )
+ [,...] | ALL [ PRIVILEGES ] ( <replaceable class="PARAMETER">column</replaceable> [, ...] ) }
+ ON [ TABLE ] <replaceable class="PARAMETER">tablename</replaceable> [, ...]
+ TO { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
GRANT { { USAGE | SELECT | UPDATE }
[,...] | ALL [ PRIVILEGES ] }
ON SEQUENCE <replaceable class="PARAMETER">sequencename</replaceable> [, ...]
<para>
The <command>GRANT</command> command has two basic variants: one
- that grants privileges on a database object (table, view, sequence,
+ that grants privileges on a database object (table, column, view, sequence,
database, foreign-data wrapper, foreign server, function,
procedural language, schema, or tablespace), and one that grants
membership in a role. These variants are similar in many ways, but
<para>
Depending on the type of object, the initial default privileges might
include granting some privileges to <literal>PUBLIC</literal>.
- The default is no public access for tables, schemas, and tablespaces;
+ The default is no public access for tables, columns, schemas, and
+ tablespaces;
<literal>CONNECT</> privilege and <literal>TEMP</> table creation privilege
for databases;
<literal>EXECUTE</> privilege for functions; and
<listitem>
<para>
Allows <xref linkend="sql-select" endterm="sql-select-title"> from
- any column of the specified table, view, or sequence.
+ any column, or the specific columns listed, of the specified table,
+ view, or sequence.
Also allows the use of
<xref linkend="sql-copy" endterm="sql-copy-title"> TO.
This privilege is also needed to reference existing column values in
<listitem>
<para>
Allows <xref linkend="sql-insert" endterm="sql-insert-title"> of a new
- row into the specified table.
+ row into the specified table. If specific columns are listed,
+ only those columns may be assigned to in the <command>INSERT</>
+ command (other columns will therefore receive default values).
Also allows <xref linkend="sql-copy" endterm="sql-copy-title"> FROM.
</para>
</listitem>
<listitem>
<para>
Allows <xref linkend="sql-update" endterm="sql-update-title"> of any
- column of the specified table.
+ column, or the specific columns listed, of the specified table.
(In practice, any nontrivial <command>UPDATE</> command will require
<literal>SELECT</> privilege as well, since it must reference table
columns to determine which rows to update, and/or to compute new
values for columns.)
<literal>SELECT ... FOR UPDATE</literal>
and <literal>SELECT ... FOR SHARE</literal>
- also require this privilege, in addition to the
+ also require this privilege on at least one column, in addition to the
<literal>SELECT</literal> privilege. For sequences, this
privilege allows the use of the <function>nextval</function> and
<function>setval</function> functions.
<para>
To create a foreign key constraint, it is
necessary to have this privilege on both the referencing and
- referenced tables.
+ referenced columns. The privilege may be granted for all columns
+ of a table, or just specific columns.
</para>
</listitem>
</varlistentry>
to revoke access privileges.
</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
+ its whole table. Granting the privilege at the table level and then
+ revoking it for one column will not do what you might wish: the
+ table-level grant is unaffected by a column-level operation.
+ </para>
+
<para>
When a non-owner of an object attempts to <command>GRANT</> privileges
on the object, the command will fail outright if the user has no
</para>
<para>
- Granting permission on a table does not automatically extend
- permissions to any sequences used by the table, including
- sequences tied to <type>SERIAL</> columns. Permissions on
- sequence must be set separately.
+ Granting permission on a table does not automatically extend
+ permissions to any sequences used by the table, including
+ sequences tied to <type>SERIAL</> columns. Permissions on
+ sequences must be set separately.
</para>
<para>
- Currently, <productname>PostgreSQL</productname> does not support
- granting or revoking privileges for individual columns of a table.
- One possible workaround is to create a view having just the desired
- columns and then grant privileges to that view.
- </para>
-
- <para>
- Use <xref linkend="app-psql">'s <command>\z</command> command
- to obtain information about existing privileges, for example:
+ Use <xref linkend="app-psql">'s <command>\dp</command> command
+ to obtain information about existing privileges for tables and
+ columns. For example:
<programlisting>
-=> \z mytable
- Access privileges
- Schema | Name | Type | Access privileges
---------+---------+-------+-----------------------
- public | mytable | table | miriam=arwdDxt/miriam
- : =r/miriam
- : admin=arw/miriam
+=> \dp mytable
+ Access privileges
+ Schema | Name | Type | Access privileges | Column access privileges
+--------+---------+-------+-----------------------+--------------------------
+ public | mytable | table | miriam=arwdDxt/miriam | col1:
+ : =r/miriam : miriam_rw=rw/miriam
+ : admin=arw/miriam
(1 row)
</programlisting>
- The entries shown by <command>\z</command> are interpreted thus:
+ The entries shown by <command>\dp</command> are interpreted thus:
<programlisting>
rolename=xxxx -- privileges granted to a role
=xxxx -- privileges granted to PUBLIC
C -- CREATE
c -- CONNECT
T -- TEMPORARY
- arwdDxt -- ALL PRIVILEGES (for tables)
+ arwdDxt -- ALL PRIVILEGES (for tables, varies for other objects)
* -- grant option for preceding privilege
/yyyy -- role that granted this privilege
<programlisting>
GRANT SELECT ON mytable TO PUBLIC;
GRANT SELECT, UPDATE, INSERT ON mytable TO admin;
+GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw;
</programlisting>
</para>
+ <para>
+ For non-table objects there are other <command>\d</> commands
+ that can display their privileges.
+ </para>
+
<para>
If the <quote>Access privileges</> column is empty for a given object,
it means the object has default privileges (that is, its privileges column
<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.
+ specified request. Entries are shown in <quote>Column access
+ privileges</> only for columns with nondefault privileges.
</para>
<para>
<quote>_SYSTEM</>, the owner cannot revoke these rights.
</para>
- <para>
- <productname>PostgreSQL</productname> does not support the SQL-standard
- functionality of setting privileges for individual columns.
- </para>
-
<para>
The SQL standard provides for a <literal>USAGE</literal> privilege
on other kinds of objects: character sets, collations,
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/insert.sgml,v 1.37 2008/11/14 10:22:47 petere Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/insert.sgml,v 1.38 2009/01/22 20:15:59 tgl Exp $
PostgreSQL documentation
-->
<para>
You must have <literal>INSERT</literal> privilege on a table in
- order to insert into it, and <literal>SELECT</> privilege on it to
- use <literal>RETURNING</>. If you use the <replaceable
+ order to insert into it. If a column list is specified, you only
+ need <literal>INSERT</literal> privilege on the listed columns.
+ Use of the <literal>RETURNING</> clause requires <literal>SELECT</>
+ privilege on all columns mentioned in <literal>RETURNING</>.
+ If you use the <replaceable
class="PARAMETER">query</replaceable> clause to insert rows from a
- query, you also need to have <literal>SELECT</literal> privilege on
- any table used in the query.
+ query, you of course need to have <literal>SELECT</literal> privilege on
+ any table or column used in the query.
</para>
</refsect1>
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/revoke.sgml,v 1.50 2008/12/19 16:25:16 petere Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/revoke.sgml,v 1.51 2009/01/22 20:15:59 tgl Exp $
PostgreSQL documentation
-->
FROM { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
+REVOKE [ GRANT OPTION FOR ]
+ { { SELECT | INSERT | UPDATE | REFERENCES } ( <replaceable class="PARAMETER">column</replaceable> [, ...] )
+ [,...] | ALL [ PRIVILEGES ] ( <replaceable class="PARAMETER">column</replaceable> [, ...] ) }
+ ON [ TABLE ] <replaceable class="PARAMETER">tablename</replaceable> [, ...]
+ FROM { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
+
REVOKE [ GRANT OPTION FOR ]
{ { USAGE | SELECT | UPDATE }
[,...] | ALL [ PRIVILEGES ] }
was also granted through other users.
</para>
+ <para>
+ When revoking privileges on a table, the corresponding column privileges
+ (if any) are automatically revoked on each column of the table, as well.
+ </para>
+
<para>
When revoking membership in a role, <literal>GRANT OPTION</> is instead
called <literal>ADMIN OPTION</>, but the behavior is similar.
<title>Notes</title>
<para>
- Use <xref linkend="app-psql">'s <command>\z</command> command to
- display the privileges granted on existing objects. See <xref
- linkend="sql-grant" endterm="sql-grant-title"> for information about the format.
+ Use <xref linkend="app-psql">'s <command>\dp</command> command to
+ display the privileges granted on existing tables and columns. See <xref
+ linkend="sql-grant" endterm="sql-grant-title"> for information about the
+ format. For non-table objects there are other <command>\d</> commands
+ that can display their privileges.
</para>
<para>
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.117 2009/01/12 14:06:20 petere Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.118 2009/01/22 20:15:59 tgl Exp $
PostgreSQL documentation
-->
</para>
<para>
- You must have <literal>SELECT</literal> privilege on a table to
- read its values. The use of <literal>FOR UPDATE</literal> or
- <literal>FOR SHARE</literal> requires
- <literal>UPDATE</literal> privilege as well.
+ You must have <literal>SELECT</literal> privilege on each column used
+ in a <command>SELECT</> command. The use of <literal>FOR UPDATE</literal>
+ or <literal>FOR SHARE</literal> requires
+ <literal>UPDATE</literal> privilege as well (for at least one column
+ of each table so selected).
</para>
</refsect1>
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/update.sgml,v 1.48 2008/11/16 17:34:28 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/update.sgml,v 1.49 2009/01/22 20:16:00 tgl Exp $
PostgreSQL documentation
-->
</para>
<para>
- You must have the <literal>UPDATE</literal> privilege on the table
- to update it, as well as the <literal>SELECT</literal>
- privilege to any table whose values are read in the
+ You must have the <literal>UPDATE</literal> privilege on the table,
+ or at least on the column(s) that are listed to be updated.
+ You must also have the <literal>SELECT</literal>
+ privilege on any column whose values are read in the
<replaceable class="parameter">expressions</replaceable> or
<replaceable class="parameter">condition</replaceable>.
</para>
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.124 2009/01/01 17:23:34 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.125 2009/01/22 20:16:00 tgl Exp $
*
* NOTES
* some of the executor utility code such as "ExecTypeFromTL" should be
* struct pointer alignment requirement, and hence we don't need to insert
* alignment padding between the struct and the array of attribute row
* pointers.
+ *
+ * Note: Only the fixed part of pg_attribute rows is included in tuple
+ * descriptors, so we only need ATTRIBUTE_FIXED_PART_SIZE space
+ * per attr. That might need alignment padding, however.
*/
attroffset = sizeof(struct tupleDesc) + natts * sizeof(Form_pg_attribute);
attroffset = MAXALIGN(attroffset);
- stg = palloc(attroffset + natts * MAXALIGN(ATTRIBUTE_TUPLE_SIZE));
+ stg = palloc(attroffset + natts * MAXALIGN(ATTRIBUTE_FIXED_PART_SIZE));
desc = (TupleDesc) stg;
if (natts > 0)
for (i = 0; i < natts; i++)
{
attrs[i] = (Form_pg_attribute) stg;
- stg += MAXALIGN(ATTRIBUTE_TUPLE_SIZE);
+ stg += MAXALIGN(ATTRIBUTE_FIXED_PART_SIZE);
}
}
else
for (i = 0; i < desc->natts; i++)
{
- memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
+ memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
desc->attrs[i]->attnotnull = false;
desc->attrs[i]->atthasdef = false;
}
for (i = 0; i < desc->natts; i++)
{
- memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
+ memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
}
if (constr)
return false;
if (attr1->attinhcount != attr2->attinhcount)
return false;
+ /* attacl is ignored, since it's not even present... */
}
if (tupdesc1->constr != NULL)
att->attisdropped = false;
att->attislocal = true;
att->attinhcount = 0;
+ /* attacl is not set because it's not present in tupledescs */
tuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(oidtypeid),
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.248 2009/01/01 17:23:36 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.249 2009/01/22 20:16:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
closerel(NULL);
elog(DEBUG4, "open relation %s, attrsize %d",
- relname, (int) ATTRIBUTE_TUPLE_SIZE);
+ relname, (int) ATTRIBUTE_FIXED_PART_SIZE);
boot_reldesc = heap_openrv(makeRangeVar(NULL, relname, -1), NoLock);
numattr = boot_reldesc->rd_rel->relnatts;
attrtypes[i] = AllocateAttribute();
memmove((char *) attrtypes[i],
(char *) boot_reldesc->rd_att->attrs[i],
- ATTRIBUTE_TUPLE_SIZE);
+ ATTRIBUTE_FIXED_PART_SIZE);
{
Form_pg_attribute at = attrtypes[i];
if (attrtypes[attnum] == NULL)
attrtypes[attnum] = AllocateAttribute();
- MemSet(attrtypes[attnum], 0, ATTRIBUTE_TUPLE_SIZE);
+ MemSet(attrtypes[attnum], 0, ATTRIBUTE_FIXED_PART_SIZE);
namestrcpy(&attrtypes[attnum]->attname, name);
elog(DEBUG4, "column %s %s", NameStr(attrtypes[attnum]->attname), type);
/* ----------------
* AllocateAttribute
+ *
+ * Note: bootstrap never sets any per-column ACLs, so we only need
+ * ATTRIBUTE_FIXED_PART_SIZE space per attribute.
* ----------------
*/
static Form_pg_attribute
AllocateAttribute(void)
{
- Form_pg_attribute attribute = (Form_pg_attribute) malloc(ATTRIBUTE_TUPLE_SIZE);
+ Form_pg_attribute attribute = (Form_pg_attribute) malloc(ATTRIBUTE_FIXED_PART_SIZE);
if (!PointerIsValid(attribute))
elog(FATAL, "out of memory");
- MemSet(attribute, 0, ATTRIBUTE_TUPLE_SIZE);
+ MemSet(attribute, 0, ATTRIBUTE_FIXED_PART_SIZE);
return attribute;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.151 2009/01/01 17:23:36 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.152 2009/01/22 20:16:00 tgl Exp $
*
* NOTES
* See acl.h.
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
static List *objectNamesToOids(GrantObjectType objtype, List *objnames);
+static void expand_col_privileges(List *colnames, Oid table_oid,
+ AclMode this_privileges,
+ AclMode *col_privileges,
+ int num_col_privileges);
+static void expand_all_col_privileges(Oid table_oid, Form_pg_class classForm,
+ AclMode this_privileges,
+ AclMode *col_privileges,
+ int num_col_privileges);
static AclMode string_to_privilege(const char *privname);
static const char *privilege_to_string(AclMode privilege);
static AclMode restrict_and_check_grant(bool is_grant, AclMode avail_goptions,
bool all_privs, AclMode privileges,
Oid objectId, Oid grantorId,
- AclObjectKind objkind, char *objname);
-static AclMode pg_aclmask(AclObjectKind objkind, Oid table_oid, Oid roleid,
- AclMode mask, AclMaskHow how);
+ AclObjectKind objkind, const char *objname,
+ AttrNumber att_number, const char *colname);
+static AclMode pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum,
+ Oid roleid, AclMode mask, AclMaskHow how);
#ifdef ACLDEBUG
AclItem aclitem;
Acl *newer_acl;
- aclitem. ai_grantee = lfirst_oid(j);
+ aclitem.ai_grantee = lfirst_oid(j);
/*
* Grant options can only be granted to individual roles, not PUBLIC.
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
errmsg("grant options can only be granted to roles")));
- aclitem. ai_grantor = grantorId;
+ aclitem.ai_grantor = grantorId;
/*
* The asymmetry in the conditions here comes from the spec. In
static AclMode
restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
AclMode privileges, Oid objectId, Oid grantorId,
- AclObjectKind objkind, char *objname)
+ AclObjectKind objkind, const char *objname,
+ AttrNumber att_number, const char *colname)
{
AclMode this_privileges;
AclMode whole_mask;
switch (objkind)
{
+ case ACL_KIND_COLUMN:
+ whole_mask = ACL_ALL_RIGHTS_COLUMN;
+ break;
case ACL_KIND_CLASS:
whole_mask = ACL_ALL_RIGHTS_RELATION;
break;
*/
if (avail_goptions == ACL_NO_RIGHTS)
{
- if (pg_aclmask(objkind, objectId, grantorId,
+ if (pg_aclmask(objkind, objectId, att_number, grantorId,
whole_mask | ACL_GRANT_OPTION_FOR(whole_mask),
ACLMASK_ANY) == ACL_NO_RIGHTS)
- aclcheck_error(ACLCHECK_NO_PRIV, objkind, objname);
+ {
+ if (objkind == ACL_KIND_COLUMN && colname)
+ aclcheck_error_col(ACLCHECK_NO_PRIV, objkind, objname, colname);
+ else
+ aclcheck_error(ACLCHECK_NO_PRIV, objkind, objname);
+ }
}
/*
istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects);
/* all_privs to be filled below */
/* privileges to be filled below */
- istmt.grantees = NIL;
- /* filled below */
+ istmt.col_privs = NIL; /* may get filled below */
+ istmt.grantees = NIL; /* filled below */
istmt.grant_option = stmt->grant_option;
istmt.behavior = stmt->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
}
/*
- * Convert stmt->privileges, a textual list, into an AclMode bitmask.
+ * Convert stmt->privileges, a list of AccessPriv nodes, into an
+ * AclMode bitmask. Note: objtype can't be ACL_OBJECT_COLUMN.
*/
switch (stmt->objtype)
{
foreach(cell, stmt->privileges)
{
- char *privname = strVal(lfirst(cell));
- AclMode priv = string_to_privilege(privname);
+ AccessPriv *privnode = (AccessPriv *) lfirst(cell);
+ AclMode priv;
+
+ /*
+ * If it's a column-level specification, we just set it aside
+ * in col_privs for the moment; but insist it's for a relation.
+ */
+ if (privnode->cols)
+ {
+ if (stmt->objtype != ACL_OBJECT_RELATION)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg("column privileges are only valid for relations")));
+ istmt.col_privs = lappend(istmt.col_privs, privnode);
+ continue;
+ }
+
+ if (privnode->priv_name == NULL) /* parser mistake? */
+ elog(ERROR, "AccessPriv node must specify privilege or columns");
+ priv = string_to_privilege(privnode->priv_name);
if (priv & ~((AclMode) all_privileges))
ereport(ERROR,
/*
* ExecGrantStmt_oids
*
- * "Internal" entrypoint for granting and revoking privileges.
+ * "Internal" entrypoint for granting and revoking privileges. This is
+ * exported for pg_shdepend.c to use in revoking privileges when dropping
+ * a role.
*/
void
ExecGrantStmt_oids(InternalGrant *istmt)
return objects;
}
+/*
+ * expand_col_privileges
+ *
+ * OR the specified privilege(s) into per-column array entries for each
+ * specified attribute. The per-column array is indexed starting at
+ * FirstLowInvalidHeapAttributeNumber, up to relation's last attribute.
+ */
+static void
+expand_col_privileges(List *colnames, Oid table_oid,
+ AclMode this_privileges,
+ AclMode *col_privileges,
+ int num_col_privileges)
+{
+ ListCell *cell;
+
+ foreach(cell, colnames)
+ {
+ char *colname = strVal(lfirst(cell));
+ AttrNumber attnum;
+
+ attnum = get_attnum(table_oid, colname);
+ if (attnum == InvalidAttrNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ colname, get_rel_name(table_oid))));
+ attnum -= FirstLowInvalidHeapAttributeNumber;
+ if (attnum <= 0 || attnum >= num_col_privileges)
+ elog(ERROR, "column number out of range"); /* safety check */
+ col_privileges[attnum] |= this_privileges;
+ }
+}
+
+/*
+ * expand_all_col_privileges
+ *
+ * OR the specified privilege(s) into per-column array entries for each valid
+ * attribute of a relation. The per-column array is indexed starting at
+ * FirstLowInvalidHeapAttributeNumber, up to relation's last attribute.
+ */
+static void
+expand_all_col_privileges(Oid table_oid, Form_pg_class classForm,
+ AclMode this_privileges,
+ AclMode *col_privileges,
+ int num_col_privileges)
+{
+ AttrNumber curr_att;
+
+ Assert(classForm->relnatts - FirstLowInvalidHeapAttributeNumber < num_col_privileges);
+ for (curr_att = FirstLowInvalidHeapAttributeNumber + 1;
+ curr_att <= classForm->relnatts;
+ curr_att++)
+ {
+ HeapTuple attTuple;
+ bool isdropped;
+
+ if (curr_att == InvalidAttrNumber)
+ continue;
+
+ /* Skip OID column if it doesn't exist */
+ if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids)
+ continue;
+
+ /* Views don't have any system columns at all */
+ if (classForm->relkind == RELKIND_VIEW && curr_att < 0)
+ continue;
+
+ attTuple = SearchSysCache(ATTNUM,
+ ObjectIdGetDatum(table_oid),
+ Int16GetDatum(curr_att),
+ 0, 0);
+ if (!HeapTupleIsValid(attTuple))
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ curr_att, table_oid);
+
+ isdropped = ((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped;
+
+ ReleaseSysCache(attTuple);
+
+ /* ignore dropped columns */
+ if (isdropped)
+ continue;
+
+ col_privileges[curr_att - FirstLowInvalidHeapAttributeNumber] |= this_privileges;
+ }
+}
+
+/*
+ * This processes attributes, but expects to be called from
+ * ExecGrant_Relation, not directly from ExecGrantStmt.
+ */
+static void
+ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname,
+ AttrNumber attnum, Oid ownerId, AclMode col_privileges,
+ Relation attRelation, const Acl *old_rel_acl)
+{
+ HeapTuple attr_tuple;
+ Form_pg_attribute pg_attribute_tuple;
+ Acl *old_acl;
+ Acl *new_acl;
+ Acl *merged_acl;
+ Datum aclDatum;
+ bool isNull;
+ Oid grantorId;
+ AclMode avail_goptions;
+ bool need_update;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_attribute];
+ bool nulls[Natts_pg_attribute];
+ bool replaces[Natts_pg_attribute];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ attr_tuple = SearchSysCache(ATTNUM,
+ ObjectIdGetDatum(relOid),
+ Int16GetDatum(attnum),
+ 0, 0);
+ if (!HeapTupleIsValid(attr_tuple))
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ attnum, relOid);
+ pg_attribute_tuple = (Form_pg_attribute) GETSTRUCT(attr_tuple);
+
+ /*
+ * Get working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ aclDatum = SysCacheGetAttr(ATTNUM, attr_tuple, Anum_pg_attribute_attacl,
+ &isNull);
+ if (isNull)
+ old_acl = acldefault(ACL_OBJECT_COLUMN, ownerId);
+ else
+ old_acl = DatumGetAclPCopy(aclDatum);
+
+ /*
+ * In select_best_grantor we should consider existing table-level ACL bits
+ * as well as the per-column ACL. Build a new ACL that is their
+ * concatenation. (This is a bit cheap and dirty compared to merging
+ * them properly with no duplications, but it's all we need here.)
+ */
+ merged_acl = aclconcat(old_rel_acl, old_acl);
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), col_privileges,
+ merged_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ pfree(merged_acl);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit
+ * the standards-mandated warning and error messages. Note: we don't
+ * track whether the user actually used the ALL PRIVILEGES(columns)
+ * syntax for each column; we just approximate it by whether all the
+ * possible privileges are specified now. Since the all_privs flag only
+ * determines whether a warning is issued, this seems close enough.
+ */
+ col_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ (col_privileges == ACL_ALL_RIGHTS_COLUMN),
+ col_privileges,
+ relOid, grantorId, ACL_KIND_COLUMN,
+ relname, attnum,
+ NameStr(pg_attribute_tuple->attname));
+
+ /*
+ * Generate new ACL.
+ *
+ * We need the members of both old and new ACLs so we can correct
+ * the shared dependency information.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option,
+ istmt->behavior, istmt->grantees,
+ col_privileges, grantorId,
+ ownerId);
+
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ /*
+ * If the updated ACL is empty, we can set attacl to null, and maybe
+ * even avoid an update of the pg_attribute row. This is worth testing
+ * because we'll come through here multiple times for any relation-level
+ * REVOKE, even if there were never any column GRANTs. Note we are
+ * assuming that the "default" ACL state for columns is empty.
+ */
+ if (ACL_NUM(new_acl) > 0)
+ {
+ values[Anum_pg_attribute_attacl - 1] = PointerGetDatum(new_acl);
+ need_update = true;
+ }
+ else
+ {
+ nulls[Anum_pg_attribute_attacl - 1] = true;
+ need_update = !isNull;
+ }
+ replaces[Anum_pg_attribute_attacl - 1] = true;
+
+ if (need_update)
+ {
+ newtuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attRelation),
+ values, nulls, replaces);
+
+ simple_heap_update(attRelation, &newtuple->t_self, newtuple);
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(attRelation, newtuple);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(RelationRelationId, relOid, attnum,
+ ownerId, istmt->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+ }
+
+ pfree(new_acl);
+
+ ReleaseSysCache(attr_tuple);
+}
+
/*
* This processes both sequences and non-sequences.
*/
ExecGrant_Relation(InternalGrant *istmt)
{
Relation relation;
+ Relation attRelation;
ListCell *cell;
relation = heap_open(RelationRelationId, RowExclusiveLock);
+ attRelation = heap_open(AttributeRelationId, RowExclusiveLock);
foreach(cell, istmt->objects)
{
Datum aclDatum;
Form_pg_class pg_class_tuple;
bool isNull;
- AclMode avail_goptions;
AclMode this_privileges;
+ AclMode *col_privileges;
+ int num_col_privileges;
+ bool have_col_privileges;
Acl *old_acl;
- Acl *new_acl;
- Oid grantorId;
+ Acl *old_rel_acl;
Oid ownerId;
HeapTuple tuple;
- HeapTuple newtuple;
- Datum values[Natts_pg_class];
- bool nulls[Natts_pg_class];
- bool replaces[Natts_pg_class];
- int noldmembers;
- int nnewmembers;
- Oid *oldmembers;
- Oid *newmembers;
+ ListCell *cell_colprivs;
tuple = SearchSysCache(RELOID,
ObjectIdGetDatum(relOid),
if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
{
/*
- * For backward compatibility, throw just a warning for
+ * For backward compatibility, just throw a warning for
* invalid sequence permissions when using the non-sequence
- * GRANT syntax is used.
+ * GRANT syntax.
*/
if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_SEQUENCE))
{
*/
ereport(WARNING,
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
- errmsg("sequence \"%s\" only supports USAGE, SELECT, and UPDATE",
+ errmsg("sequence \"%s\" only supports USAGE, SELECT, and UPDATE privileges",
NameStr(pg_class_tuple->relname))));
this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE;
}
else
{
if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION))
-
+ {
/*
* USAGE is the only permission supported by sequences but
* not by non-sequences. Don't mention the object name
ereport(ERROR,
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
errmsg("invalid privilege type USAGE for table")));
+ }
}
}
+ /*
+ * Set up array in which we'll accumulate any column privilege bits
+ * that need modification. The array is indexed such that entry [0]
+ * corresponds to FirstLowInvalidHeapAttributeNumber.
+ */
+ num_col_privileges = pg_class_tuple->relnatts - FirstLowInvalidHeapAttributeNumber + 1;
+ col_privileges = (AclMode *) palloc0(num_col_privileges * sizeof(AclMode));
+ have_col_privileges = false;
+
+ /*
+ * If we are revoking relation privileges that are also column
+ * privileges, we must implicitly revoke them from each column too,
+ * per SQL spec. (We don't need to implicitly add column privileges
+ * during GRANT because the permissions-checking code always checks
+ * both relation and per-column privileges.)
+ */
+ if (!istmt->is_grant &&
+ (this_privileges & ACL_ALL_RIGHTS_COLUMN) != 0)
+ {
+ expand_all_col_privileges(relOid, pg_class_tuple,
+ this_privileges & ACL_ALL_RIGHTS_COLUMN,
+ col_privileges,
+ num_col_privileges);
+ have_col_privileges = true;
+ }
+
/*
* Get owner ID and working copy of existing ACL. If there's no ACL,
* substitute the proper default.
else
old_acl = DatumGetAclPCopy(aclDatum);
- /* Determine ID to do the grant as, and available grant options */
- select_best_grantor(GetUserId(), this_privileges,
- old_acl, ownerId,
- &grantorId, &avail_goptions);
+ /* Need an extra copy of original rel ACL for column handling */
+ old_rel_acl = aclcopy(old_acl);
/*
- * Restrict the privileges to what we can actually grant, and emit the
- * standards-mandated warning and error messages.
+ * Handle relation-level privileges, if any were specified
*/
- this_privileges =
- restrict_and_check_grant(istmt->is_grant, avail_goptions,
- istmt->all_privs, this_privileges,
- relOid, grantorId,
- pg_class_tuple->relkind == RELKIND_SEQUENCE
- ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS,
- NameStr(pg_class_tuple->relname));
+ if (this_privileges != ACL_NO_RIGHTS)
+ {
+ AclMode avail_goptions;
+ Acl *new_acl;
+ Oid grantorId;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_class];
+ bool nulls[Natts_pg_class];
+ bool replaces[Natts_pg_class];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), this_privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit
+ * the standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, this_privileges,
+ relOid, grantorId,
+ pg_class_tuple->relkind == RELKIND_SEQUENCE
+ ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS,
+ NameStr(pg_class_tuple->relname),
+ 0, NULL);
+
+ /*
+ * Generate new ACL.
+ *
+ * We need the members of both old and new ACLs so we can correct
+ * the shared dependency information.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
+ new_acl = merge_acl_with_grant(old_acl,
+ istmt->is_grant,
+ istmt->grant_option,
+ istmt->behavior,
+ istmt->grantees,
+ this_privileges,
+ grantorId,
+ ownerId);
+
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_class_relacl - 1] = true;
+ values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
+ values, nulls, replaces);
+
+ simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(relation, newtuple);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(RelationRelationId, relOid, 0,
+ ownerId, istmt->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ pfree(new_acl);
+ }
/*
- * Generate new ACL.
- *
- * We need the members of both old and new ACLs so we can correct the
- * shared dependency information.
+ * Handle column-level privileges, if any were specified or implied.
+ * We first expand the user-specified column privileges into the
+ * array, and then iterate over all nonempty array entries.
*/
- noldmembers = aclmembers(old_acl, &oldmembers);
+ foreach(cell_colprivs, istmt->col_privs)
+ {
+ AccessPriv *col_privs = (AccessPriv *) lfirst(cell_colprivs);
- new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
- istmt->grant_option, istmt->behavior,
- istmt->grantees, this_privileges,
- grantorId, ownerId);
+ if (col_privs->priv_name == NULL)
+ this_privileges = ACL_ALL_RIGHTS_COLUMN;
+ else
+ this_privileges = string_to_privilege(col_privs->priv_name);
- nnewmembers = aclmembers(new_acl, &newmembers);
+ if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_COLUMN))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg("invalid privilege type %s for column",
+ privilege_to_string(this_privileges))));
- /* finished building new ACL value, now insert it */
- MemSet(values, 0, sizeof(values));
- MemSet(nulls, false, sizeof(nulls));
- MemSet(replaces, false, sizeof(replaces));
+ if (pg_class_tuple->relkind == RELKIND_SEQUENCE &&
+ this_privileges & ~((AclMode) ACL_SELECT))
+ {
+ /*
+ * The only column privilege allowed on sequences is SELECT.
+ * This is a warning not error because we do it that way
+ * for relation-level privileges.
+ */
+ ereport(WARNING,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg("sequence \"%s\" only supports SELECT column privileges",
+ NameStr(pg_class_tuple->relname))));
- replaces[Anum_pg_class_relacl - 1] = true;
- values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl);
+ this_privileges &= (AclMode) ACL_SELECT;
+ }
- newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
+ expand_col_privileges(col_privs->cols, relOid,
+ this_privileges,
+ col_privileges,
+ num_col_privileges);
+ have_col_privileges = true;
+ }
- simple_heap_update(relation, &newtuple->t_self, newtuple);
+ if (have_col_privileges)
+ {
+ AttrNumber i;
- /* keep the catalog indexes up to date */
- CatalogUpdateIndexes(relation, newtuple);
+ for (i = 0; i < num_col_privileges; i++)
+ {
+ if (col_privileges[i] == ACL_NO_RIGHTS)
+ continue;
+ ExecGrant_Attribute(istmt,
+ relOid,
+ NameStr(pg_class_tuple->relname),
+ i + FirstLowInvalidHeapAttributeNumber,
+ ownerId,
+ col_privileges[i],
+ attRelation,
+ old_rel_acl);
+ }
+ }
- /* Update the shared dependency ACL info */
- updateAclDependencies(RelationRelationId, relOid,
- ownerId, istmt->is_grant,
- noldmembers, oldmembers,
- nnewmembers, newmembers);
+ pfree(old_rel_acl);
+ pfree(col_privileges);
ReleaseSysCache(tuple);
- pfree(new_acl);
-
/* prevent error when processing duplicate objects */
CommandCounterIncrement();
}
+ heap_close(attRelation, RowExclusiveLock);
heap_close(relation, RowExclusiveLock);
}
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, istmt->privileges,
datId, grantorId, ACL_KIND_DATABASE,
- NameStr(pg_database_tuple->datname));
+ NameStr(pg_database_tuple->datname),
+ 0, NULL);
/*
* Generate new ACL.
CatalogUpdateIndexes(relation, newtuple);
/* Update the shared dependency ACL info */
- updateAclDependencies(DatabaseRelationId, HeapTupleGetOid(tuple),
+ updateAclDependencies(DatabaseRelationId, HeapTupleGetOid(tuple), 0,
ownerId, istmt->is_grant,
noldmembers, oldmembers,
nnewmembers, newmembers);
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, istmt->privileges,
fdwid, grantorId, ACL_KIND_FDW,
- NameStr(pg_fdw_tuple->fdwname));
+ NameStr(pg_fdw_tuple->fdwname),
+ 0, NULL);
/*
* Generate new ACL.
CatalogUpdateIndexes(relation, newtuple);
/* Update the shared dependency ACL info */
- updateAclDependencies(ForeignDataWrapperRelationId, HeapTupleGetOid(tuple),
+ updateAclDependencies(ForeignDataWrapperRelationId,
+ HeapTupleGetOid(tuple), 0,
ownerId, istmt->is_grant,
noldmembers, oldmembers,
nnewmembers, newmembers);
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, istmt->privileges,
srvid, grantorId, ACL_KIND_FOREIGN_SERVER,
- NameStr(pg_server_tuple->srvname));
+ NameStr(pg_server_tuple->srvname),
+ 0, NULL);
/*
* Generate new ACL.
CatalogUpdateIndexes(relation, newtuple);
/* Update the shared dependency ACL info */
- updateAclDependencies(ForeignServerRelationId, HeapTupleGetOid(tuple),
+ updateAclDependencies(ForeignServerRelationId,
+ HeapTupleGetOid(tuple), 0,
ownerId, istmt->is_grant,
noldmembers, oldmembers,
nnewmembers, newmembers);
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, istmt->privileges,
funcId, grantorId, ACL_KIND_PROC,
- NameStr(pg_proc_tuple->proname));
+ NameStr(pg_proc_tuple->proname),
+ 0, NULL);
/*
* Generate new ACL.
CatalogUpdateIndexes(relation, newtuple);
/* Update the shared dependency ACL info */
- updateAclDependencies(ProcedureRelationId, funcId,
+ updateAclDependencies(ProcedureRelationId, funcId, 0,
ownerId, istmt->is_grant,
noldmembers, oldmembers,
nnewmembers, newmembers);
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, istmt->privileges,
langId, grantorId, ACL_KIND_LANGUAGE,
- NameStr(pg_language_tuple->lanname));
+ NameStr(pg_language_tuple->lanname),
+ 0, NULL);
/*
* Generate new ACL.
CatalogUpdateIndexes(relation, newtuple);
/* Update the shared dependency ACL info */
- updateAclDependencies(LanguageRelationId, HeapTupleGetOid(tuple),
+ updateAclDependencies(LanguageRelationId, HeapTupleGetOid(tuple), 0,
ownerId, istmt->is_grant,
noldmembers, oldmembers,
nnewmembers, newmembers);
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, istmt->privileges,
nspid, grantorId, ACL_KIND_NAMESPACE,
- NameStr(pg_namespace_tuple->nspname));
+ NameStr(pg_namespace_tuple->nspname),
+ 0, NULL);
/*
* Generate new ACL.
CatalogUpdateIndexes(relation, newtuple);
/* Update the shared dependency ACL info */
- updateAclDependencies(NamespaceRelationId, HeapTupleGetOid(tuple),
+ updateAclDependencies(NamespaceRelationId, HeapTupleGetOid(tuple), 0,
ownerId, istmt->is_grant,
noldmembers, oldmembers,
nnewmembers, newmembers);
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, istmt->privileges,
tblId, grantorId, ACL_KIND_TABLESPACE,
- NameStr(pg_tablespace_tuple->spcname));
+ NameStr(pg_tablespace_tuple->spcname),
+ 0, NULL);
/*
* Generate new ACL.
CatalogUpdateIndexes(relation, newtuple);
/* Update the shared dependency ACL info */
- updateAclDependencies(TableSpaceRelationId, tblId,
+ updateAclDependencies(TableSpaceRelationId, tblId, 0,
ownerId, istmt->is_grant,
noldmembers, oldmembers,
nnewmembers, newmembers);
static const char *const no_priv_msg[MAX_ACL_KIND] =
{
+ /* ACL_KIND_COLUMN */
+ gettext_noop("permission denied for column %s"),
/* ACL_KIND_CLASS */
gettext_noop("permission denied for relation %s"),
/* ACL_KIND_SEQUENCE */
static const char *const not_owner_msg[MAX_ACL_KIND] =
{
+ /* ACL_KIND_COLUMN */
+ gettext_noop("must be owner of relation %s"),
/* ACL_KIND_CLASS */
gettext_noop("must be owner of relation %s"),
/* ACL_KIND_SEQUENCE */
}
+void
+aclcheck_error_col(AclResult aclerr, AclObjectKind objectkind,
+ const char *objectname, const char *colname)
+{
+ switch (aclerr)
+ {
+ case ACLCHECK_OK:
+ /* no error, so return to caller */
+ break;
+ case ACLCHECK_NO_PRIV:
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied for column %s of relation %s",
+ colname, objectname)));
+ break;
+ case ACLCHECK_NOT_OWNER:
+ /* relation msg is OK since columns don't have separate owners */
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg(not_owner_msg[objectkind], objectname)));
+ break;
+ default:
+ elog(ERROR, "unrecognized AclResult: %d", (int) aclerr);
+ break;
+ }
+}
+
+
/* Check if given user has rolcatupdate privilege according to pg_authid */
static bool
has_rolcatupdate(Oid roleid)
* Relay for the various pg_*_mask routines depending on object kind
*/
static AclMode
-pg_aclmask(AclObjectKind objkind, Oid table_oid, Oid roleid,
+pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid,
AclMode mask, AclMaskHow how)
{
switch (objkind)
{
+ case ACL_KIND_COLUMN:
+ return
+ pg_class_aclmask(table_oid, roleid, mask, how) |
+ pg_attribute_aclmask(table_oid, attnum, roleid, mask, how);
case ACL_KIND_CLASS:
case ACL_KIND_SEQUENCE:
return pg_class_aclmask(table_oid, roleid, mask, how);
}
}
-/*
- * Exported routine for examining a user's privileges for a table
+
+/* ****************************************************************
+ * Exported routines for examining a user's privileges for various objects
*
- * See aclmask() for a description of the API.
+ * See aclmask() for a description of the common API for these functions.
*
* Note: we give lookup failure the full ereport treatment because the
- * has_table_privilege() family of functions allow users to pass
- * any random OID to this function. Likewise for the sibling functions
- * below.
+ * has_xxx_privilege() family of functions allow users to pass any random
+ * OID to these functions.
+ * ****************************************************************
+ */
+
+/*
+ * Exported routine for examining a user's privileges for a column
+ *
+ * Note: this considers only privileges granted specifically on the column.
+ * It is caller's responsibility to take relation-level privileges into account
+ * as appropriate. (For the same reason, we have no special case for
+ * superuser-ness here.)
+ */
+AclMode
+pg_attribute_aclmask(Oid table_oid, AttrNumber attnum, Oid roleid,
+ AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple classTuple;
+ HeapTuple attTuple;
+ Form_pg_class classForm;
+ Form_pg_attribute attributeForm;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ /*
+ * Must get the relation's tuple from pg_class (only needed for ownerId)
+ */
+ classTuple = SearchSysCache(RELOID,
+ ObjectIdGetDatum(table_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(classTuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation with OID %u does not exist",
+ table_oid)));
+ classForm = (Form_pg_class) GETSTRUCT(classTuple);
+
+ ownerId = classForm->relowner;
+
+ /*
+ * Next, get the column's ACL from pg_attribute
+ */
+ attTuple = SearchSysCache(ATTNUM,
+ ObjectIdGetDatum(table_oid),
+ Int16GetDatum(attnum),
+ 0, 0);
+ if (!HeapTupleIsValid(attTuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("attribute %d of relation with OID %u does not exist",
+ attnum, table_oid)));
+ attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple);
+
+ /* Throw error on dropped columns, too */
+ if (attributeForm->attisdropped)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("attribute %d of relation with OID %u does not exist",
+ attnum, table_oid)));
+
+ aclDatum = SysCacheGetAttr(ATTNUM, attTuple, Anum_pg_attribute_attacl,
+ &isNull);
+
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(ACL_OBJECT_COLUMN, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast column's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(attTuple);
+ ReleaseSysCache(classTuple);
+
+ return result;
+}
+
+/*
+ * Exported routine for examining a user's privileges for a table
*/
AclMode
pg_class_aclmask(Oid table_oid, Oid roleid,
return result;
}
+/*
+ * Exported routine for checking a user's access privileges to a column
+ *
+ * Returns ACLCHECK_OK if the user has any of the privileges identified by
+ * 'mode'; otherwise returns a suitable error code (in practice, always
+ * ACLCHECK_NO_PRIV).
+ *
+ * As with pg_attribute_aclmask, only privileges granted directly on the
+ * column are considered here.
+ */
+AclResult
+pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
+ Oid roleid, AclMode mode)
+{
+ if (pg_attribute_aclmask(table_oid, attnum, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
+/*
+ * Exported routine for checking a user's access privileges to any/all columns
+ *
+ * If 'how' is ACLMASK_ANY, then returns ACLCHECK_OK if user has any of the
+ * privileges identified by 'mode' on any non-dropped column in the relation;
+ * otherwise returns a suitable error code (in practice, always
+ * ACLCHECK_NO_PRIV).
+ *
+ * If 'how' is ACLMASK_ALL, then returns ACLCHECK_OK if user has any of the
+ * privileges identified by 'mode' on all non-dropped columns in the relation
+ * (and there must be at least one such column); otherwise returns a suitable
+ * error code (in practice, always ACLCHECK_NO_PRIV).
+ *
+ * As with pg_attribute_aclmask, only privileges granted directly on the
+ * column(s) are considered here.
+ *
+ * Note: system columns are not considered here; there are cases where that
+ * might be appropriate but there are also cases where it wouldn't.
+ */
+AclResult
+pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, AclMode mode,
+ AclMaskHow how)
+{
+ AclResult result;
+ HeapTuple classTuple;
+ Form_pg_class classForm;
+ AttrNumber nattrs;
+ AttrNumber curr_att;
+
+ /* Must fetch pg_class row to check number of attributes */
+ classTuple = SearchSysCache(RELOID,
+ ObjectIdGetDatum(table_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(classTuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation with OID %u does not exist",
+ table_oid)));
+ classForm = (Form_pg_class) GETSTRUCT(classTuple);
+
+ nattrs = classForm->relnatts;
+
+ ReleaseSysCache(classTuple);
+
+ /*
+ * Initialize result in case there are no non-dropped columns. We want
+ * to report failure in such cases for either value of 'how'.
+ */
+ result = ACLCHECK_NO_PRIV;
+
+ for (curr_att = 1; curr_att <= nattrs; curr_att++)
+ {
+ HeapTuple attTuple;
+ bool isdropped;
+
+ attTuple = SearchSysCache(ATTNUM,
+ ObjectIdGetDatum(table_oid),
+ Int16GetDatum(curr_att),
+ 0, 0);
+ if (!HeapTupleIsValid(attTuple))
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ curr_att, table_oid);
+
+ isdropped = ((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped;
+
+ ReleaseSysCache(attTuple);
+
+ /* ignore dropped columns */
+ if (isdropped)
+ continue;
+
+ if (pg_attribute_aclmask(table_oid, curr_att, roleid,
+ mode, ACLMASK_ANY) != 0)
+ {
+ result = ACLCHECK_OK;
+ if (how == ACLMASK_ANY)
+ break; /* succeed on any success */
+ }
+ else
+ {
+ result = ACLCHECK_NO_PRIV;
+ if (how == ACLMASK_ALL)
+ break; /* fail on any failure */
+ }
+ }
+
+ return result;
+}
+
/*
* Exported routine for checking a user's access privileges to a table
*
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.85 2009/01/01 17:23:36 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.86 2009/01/22 20:16:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
systable_endscan(scan);
+ /*
+ * Delete shared dependency references related to this object. Again,
+ * if subId = 0, remove records for sub-objects too.
+ */
+ deleteSharedDependencyRecordsFor(object->classId, object->objectId,
+ object->objectSubId);
+
/*
* Now delete the object itself, in an object-type-dependent way.
*/
*/
DeleteComments(object->objectId, object->classId, object->objectSubId);
- /*
- * Delete shared dependency references related to this object. Sub-objects
- * (columns) don't have dependencies on global objects, so skip them.
- */
- if (object->objectSubId == 0)
- deleteSharedDependencyRecordsFor(object->classId, object->objectId);
-
/*
* CommandCounterIncrement here to ensure that preceding changes are all
* visible to the next deletion step.
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.349 2009/01/01 17:23:36 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.350 2009/01/22 20:16:01 tgl Exp $
*
*
* INTERFACE ROUTINES
static FormData_pg_attribute a1 = {
0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData),
SelfItemPointerAttributeNumber, 0, -1, -1,
- false, 'p', 's', true, false, false, true, 0
+ false, 'p', 's', true, false, false, true, 0, { 0 }
};
static FormData_pg_attribute a2 = {
0, {"oid"}, OIDOID, 0, sizeof(Oid),
ObjectIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0
+ true, 'p', 'i', true, false, false, true, 0, { 0 }
};
static FormData_pg_attribute a3 = {
0, {"xmin"}, XIDOID, 0, sizeof(TransactionId),
MinTransactionIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0
+ true, 'p', 'i', true, false, false, true, 0, { 0 }
};
static FormData_pg_attribute a4 = {
0, {"cmin"}, CIDOID, 0, sizeof(CommandId),
MinCommandIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0
+ true, 'p', 'i', true, false, false, true, 0, { 0 }
};
static FormData_pg_attribute a5 = {
0, {"xmax"}, XIDOID, 0, sizeof(TransactionId),
MaxTransactionIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0
+ true, 'p', 'i', true, false, false, true, 0, { 0 }
};
static FormData_pg_attribute a6 = {
0, {"cmax"}, CIDOID, 0, sizeof(CommandId),
MaxCommandIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0
+ true, 'p', 'i', true, false, false, true, 0, { 0 }
};
/*
static FormData_pg_attribute a7 = {
0, {"tableoid"}, OIDOID, 0, sizeof(Oid),
TableOidAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0
+ true, 'p', 'i', true, false, false, true, 0, { 0 }
};
static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7};
* Construct and insert a new tuple in pg_attribute.
*
* Caller has already opened and locked pg_attribute. new_attribute is the
- * attribute to insert.
+ * attribute to insert (but we ignore its attacl, if indeed it has one).
*
* indstate is the index state for CatalogIndexInsert. It can be passed as
* NULL, in which case we'll fetch the necessary info. (Don't do this when
* inserting multiple attributes, because it's a tad more expensive.)
+ *
+ * We always initialize attacl to NULL (i.e., default permissions).
*/
void
InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
+ /* start out with empty permissions */
+ nulls[Anum_pg_attribute_attacl - 1] = true;
+
tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls);
/* finally insert the new tuple, update the indexes, and clean up */
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.311 2009/01/01 17:23:37 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.312 2009/01/22 20:16:01 tgl Exp $
*
*
* INTERFACE ROUTINES
* now that we've determined the "from", let's copy the tuple desc
* data...
*/
- memcpy(to, from, ATTRIBUTE_TUPLE_SIZE);
+ memcpy(to, from, ATTRIBUTE_FIXED_PART_SIZE);
/*
* Fix the stuff that should not be the same as the underlying
/* Expressional index */
Node *indexkey;
- MemSet(to, 0, ATTRIBUTE_TUPLE_SIZE);
+ MemSet(to, 0, ATTRIBUTE_FIXED_PART_SIZE);
if (indexpr_item == NULL) /* shouldn't happen */
elog(ERROR, "too few entries in indexprs list");
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_operator.c,v 1.107 2009/01/01 17:23:37 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_operator.c,v 1.108 2009/01/22 20:16:01 tgl Exp $
*
* NOTES
* these routines moved here from commands/define.c and somewhat cleaned up.
/* In case we are updating a shell, delete any existing entries */
deleteDependencyRecordsFor(myself.classId, myself.objectId);
- deleteSharedDependencyRecordsFor(myself.classId, myself.objectId);
+ deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
/* Dependency on namespace */
if (OidIsValid(oper->oprnamespace))
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.160 2009/01/01 17:23:37 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.161 2009/01/22 20:16:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (is_update)
{
deleteDependencyRecordsFor(ProcedureRelationId, retval);
- deleteSharedDependencyRecordsFor(ProcedureRelationId, retval);
+ deleteSharedDependencyRecordsFor(ProcedureRelationId, retval, 0);
}
myself.classId = ProcedureRelationId;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.30 2009/01/01 17:23:37 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.31 2009/01/22 20:16:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Oid **diff);
static Oid classIdGetDbId(Oid classId);
static void shdepLockAndCheckObject(Oid classId, Oid objectId);
-static void shdepChangeDep(Relation sdepRel, Oid classid, Oid objid,
- Oid refclassid, Oid refobjid,
- SharedDependencyType deptype);
-static void shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId,
- Oid refclassId, Oid refobjId,
- SharedDependencyType deptype);
-static void shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId,
- Oid refclassId, Oid refobjId,
- SharedDependencyType deptype);
+static void shdepChangeDep(Relation sdepRel,
+ Oid classid, Oid objid, int32 objsubid,
+ Oid refclassid, Oid refobjid,
+ SharedDependencyType deptype);
+static void shdepAddDependency(Relation sdepRel,
+ Oid classId, Oid objectId, int32 objsubId,
+ Oid refclassId, Oid refobjId,
+ SharedDependencyType deptype);
+static void shdepDropDependency(Relation sdepRel,
+ Oid classId, Oid objectId, int32 objsubId,
+ bool drop_subobjects,
+ Oid refclassId, Oid refobjId,
+ SharedDependencyType deptype);
static void storeObjectDescription(StringInfo descs, objectType type,
ObjectAddress *object,
SharedDependencyType deptype,
sdepRel))
{
shdepAddDependency(sdepRel, depender->classId, depender->objectId,
+ depender->objectSubId,
referenced->classId, referenced->objectId,
deptype);
}
* locked.
*/
static void
-shdepChangeDep(Relation sdepRel, Oid classid, Oid objid,
+shdepChangeDep(Relation sdepRel,
+ Oid classid, Oid objid, int32 objsubid,
Oid refclassid, Oid refobjid,
SharedDependencyType deptype)
{
Oid dbid = classIdGetDbId(classid);
HeapTuple oldtup = NULL;
HeapTuple scantup;
- ScanKeyData key[3];
+ ScanKeyData key[4];
SysScanDesc scan;
/*
Anum_pg_shdepend_objid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(objid));
+ ScanKeyInit(&key[3],
+ Anum_pg_shdepend_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(objsubid));
scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
- SnapshotNow, 3, key);
+ SnapshotNow, 4, key);
while ((scantup = systable_getnext(scan)) != NULL)
{
/* Caller screwed up if multiple matches */
if (oldtup)
elog(ERROR,
- "multiple pg_shdepend entries for object %u/%u deptype %c",
- classid, objid, deptype);
+ "multiple pg_shdepend entries for object %u/%u/%d deptype %c",
+ classid, objid, objsubid, deptype);
oldtup = heap_copytuple(scantup);
}
values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid);
values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid);
values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid);
+ values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubid);
values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid);
values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid);
* changeDependencyOnOwner
*
* Update the shared dependencies to account for the new owner.
+ *
+ * Note: we don't need an objsubid argument because only whole objects
+ * have owners.
*/
void
changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
/* Adjust the SHARED_DEPENDENCY_OWNER entry */
- shdepChangeDep(sdepRel, classId, objectId,
+ shdepChangeDep(sdepRel,
+ classId, objectId, 0,
AuthIdRelationId, newOwnerId,
SHARED_DEPENDENCY_OWNER);
* to make the various ALTER OWNER routines each know about it.
*----------
*/
- shdepDropDependency(sdepRel, classId, objectId,
+ shdepDropDependency(sdepRel, classId, objectId, 0, true,
AuthIdRelationId, newOwnerId,
SHARED_DEPENDENCY_ACL);
* updateAclDependencies
* Update the pg_shdepend info for an object's ACL during GRANT/REVOKE.
*
- * classId, objectId: identify the object whose ACL this is
+ * classId, objectId, objsubId: identify the object whose ACL this is
* ownerId: role owning the object
* isGrant: are we adding or removing ACL entries?
* noldmembers, oldmembers: array of roleids appearing in old ACL
* before return.
*/
void
-updateAclDependencies(Oid classId, Oid objectId, Oid ownerId, bool isGrant,
+updateAclDependencies(Oid classId, Oid objectId, int32 objsubId,
+ Oid ownerId, bool isGrant,
int noldmembers, Oid *oldmembers,
int nnewmembers, Oid *newmembers)
{
continue;
if (isGrant)
- shdepAddDependency(sdepRel, classId, objectId,
+ shdepAddDependency(sdepRel, classId, objectId, objsubId,
AuthIdRelationId, roleid,
SHARED_DEPENDENCY_ACL);
else
- shdepDropDependency(sdepRel, classId, objectId,
+ shdepDropDependency(sdepRel, classId, objectId, objsubId,
+ false, /* exact match on objsubId */
AuthIdRelationId, roleid,
SHARED_DEPENDENCY_ACL);
}
object.classId = sdepForm->classid;
object.objectId = sdepForm->objid;
- object.objectSubId = 0;
+ object.objectSubId = sdepForm->objsubid;
/*
* If it's a dependency local to this database or it's a shared
systable_endscan(scan);
/* Now delete all entries corresponding to the database itself */
- shdepDropDependency(sdepRel, DatabaseRelationId, databaseId,
+ shdepDropDependency(sdepRel, DatabaseRelationId, databaseId, 0, true,
InvalidOid, InvalidOid,
SHARED_DEPENDENCY_INVALID);
* Delete all pg_shdepend entries corresponding to an object that's being
* dropped or modified. The object is assumed to be either a shared object
* or local to the current database (the classId tells us which).
+ *
+ * If objectSubId is zero, we are deleting a whole object, so get rid of
+ * pg_shdepend entries for subobjects as well.
*/
void
-deleteSharedDependencyRecordsFor(Oid classId, Oid objectId)
+deleteSharedDependencyRecordsFor(Oid classId, Oid objectId, int32 objectSubId)
{
Relation sdepRel;
sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
- shdepDropDependency(sdepRel, classId, objectId,
+ shdepDropDependency(sdepRel, classId, objectId, objectSubId,
+ (objectSubId == 0),
InvalidOid, InvalidOid,
SHARED_DEPENDENCY_INVALID);
* locked.
*/
static void
-shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId,
+shdepAddDependency(Relation sdepRel,
+ Oid classId, Oid objectId, int32 objsubId,
Oid refclassId, Oid refobjId,
SharedDependencyType deptype)
{
values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(classIdGetDbId(classId));
values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classId);
values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objectId);
+ values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubId);
values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassId);
values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjId);
* Internal workhorse for deleting entries from pg_shdepend.
*
* We drop entries having the following properties:
- * dependent object is the one identified by classId/objectId
+ * dependent object is the one identified by classId/objectId/objsubId
* if refclassId isn't InvalidOid, it must match the entry's refclassid
* if refobjId isn't InvalidOid, it must match the entry's refobjid
* if deptype isn't SHARED_DEPENDENCY_INVALID, it must match entry's deptype
*
+ * If drop_subobjects is true, we ignore objsubId and consider all entries
+ * matching classId/objectId.
+ *
* sdepRel must be the pg_shdepend relation, already opened and suitably
* locked.
*/
static void
-shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId,
+shdepDropDependency(Relation sdepRel,
+ Oid classId, Oid objectId, int32 objsubId,
+ bool drop_subobjects,
Oid refclassId, Oid refobjId,
SharedDependencyType deptype)
{
- ScanKeyData key[3];
+ ScanKeyData key[4];
+ int nkeys;
SysScanDesc scan;
HeapTuple tup;
Anum_pg_shdepend_objid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(objectId));
+ if (drop_subobjects)
+ nkeys = 3;
+ else
+ {
+ ScanKeyInit(&key[3],
+ Anum_pg_shdepend_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(objsubId));
+ nkeys = 4;
+ }
scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
- SnapshotNow, 3, key);
+ SnapshotNow, nkeys, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
*
* Drop the objects owned by any one of the given RoleIds. If a role has
* access to an object, the grant will be removed as well (but the object
- * will not, of course.)
+ * will not, of course).
*
* We can revoke grants immediately while doing the scan, but drops are
* saved up and done all at once with performMultipleDeletions. This
while ((tuple = systable_getnext(scan)) != NULL)
{
- ObjectAddress obj;
- GrantObjectType objtype;
- InternalGrant istmt;
Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
+ InternalGrant istmt;
+ ObjectAddress obj;
/* We only operate on objects in the current database */
if (sdepForm->dbid != MyDatabaseId)
default:
elog(ERROR, "unexpected object type %d",
sdepForm->classid);
- /* keep compiler quiet */
- objtype = (GrantObjectType) 0;
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;
/* Save it for deletion below */
obj.classId = sdepForm->classid;
obj.objectId = sdepForm->objid;
- obj.objectSubId = 0;
+ obj.objectSubId = sdepForm->objsubid;
add_exact_object_address(&obj, deleteobjs);
break;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.123 2009/01/01 17:23:37 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.124 2009/01/22 20:16:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (rebuild)
{
deleteDependencyRecordsFor(TypeRelationId, typeObjectId);
- deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId);
+ deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId, 0);
}
myself.classId = TypeRelationId;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.132 2009/01/06 23:46:06 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.133 2009/01/22 20:16:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return NULL;
/*
- * Create the VacAttrStats struct.
+ * Create the VacAttrStats struct. Note that we only have a copy of
+ * the fixed fields of the pg_attribute tuple.
*/
stats = (VacAttrStats *) palloc0(sizeof(VacAttrStats));
- stats->attr = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
- memcpy(stats->attr, attr, ATTRIBUTE_TUPLE_SIZE);
+ stats->attr = (Form_pg_attribute) palloc(ATTRIBUTE_FIXED_PART_SIZE);
+ memcpy(stats->attr, attr, ATTRIBUTE_FIXED_PART_SIZE);
typtuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(attr->atttypid),
0, 0, 0);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.277 2009/01/12 08:54:26 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.278 2009/01/22 20:16:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static Oid transformFkeyCheckAttrs(Relation pkrel,
int numattrs, int16 *attnums,
Oid *opclasses);
+static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
static void validateForeignKeyConstraint(FkConstraint *fkconstraint,
Relation rel, Relation pkrel, Oid constraintOid);
static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
attribute.attisdropped = false;
attribute.attislocal = colDef->is_local;
attribute.attinhcount = colDef->inhcount;
+ /* attribute.attacl is handled by InsertPgAttributeTuple */
ReleaseSysCache(typeTuple);
* Add a foreign-key constraint to a single table
*
* Subroutine for ATExecAddConstraint. Must already hold exclusive
- * lock on the rel, and have done appropriate validity/permissions checks
- * for it.
+ * lock on the rel, and have done appropriate validity checks for it.
+ * We do permissions checks here, however.
*/
static void
ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
FkConstraint *fkconstraint)
{
Relation pkrel;
- AclResult aclresult;
int16 pkattnum[INDEX_MAX_KEYS];
int16 fkattnum[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock);
/*
- * Validity and permissions checks
- *
- * Note: REFERENCES permissions checks are redundant with CREATE TRIGGER,
- * but we may as well error out sooner instead of later.
+ * Validity checks (permission checks wait till we have the column numbers)
*/
if (pkrel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
errmsg("referenced relation \"%s\" is not a table",
RelationGetRelationName(pkrel))));
- aclresult = pg_class_aclcheck(RelationGetRelid(pkrel), GetUserId(),
- ACL_REFERENCES);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, ACL_KIND_CLASS,
- RelationGetRelationName(pkrel));
-
if (!allowSystemTableMods && IsSystemRelation(pkrel))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied: \"%s\" is a system catalog",
RelationGetRelationName(pkrel))));
- aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
- ACL_REFERENCES);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, ACL_KIND_CLASS,
- RelationGetRelationName(rel));
-
/*
* Disallow reference from permanent table to temp table or vice versa.
* (The ban on perm->temp is for fairly obvious reasons. The ban on
opclasses);
}
+ /*
+ * Now we can check permissions.
+ */
+ checkFkeyPermissions(pkrel, pkattnum, numpks);
+ checkFkeyPermissions(rel, fkattnum, numfks);
+
/*
* Look up the equality operators to use in the constraint.
*
return indexoid;
}
+/* Permissions checks for ADD FOREIGN KEY */
+static void
+checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
+{
+ Oid roleid = GetUserId();
+ AclResult aclresult;
+ int i;
+
+ /* Okay if we have relation-level REFERENCES permission */
+ aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
+ ACL_REFERENCES);
+ if (aclresult == ACLCHECK_OK)
+ return;
+ /* Else we must have REFERENCES on each column */
+ for (i = 0; i < natts; i++)
+ {
+ aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
+ roleid, ACL_REFERENCES);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_CLASS,
+ RelationGetRelationName(rel));
+ }
+}
+
/*
* Scan the existing rows in a table to verify they meet a proposed FK
* constraint.
fk_trigger->constrrel = fkconstraint->pktable;
fk_trigger->args = NIL;
- (void) CreateTrigger(fk_trigger, constraintOid);
+ (void) CreateTrigger(fk_trigger, constraintOid, false);
/* Make changes-so-far visible */
CommandCounterIncrement();
}
fk_trigger->args = NIL;
- (void) CreateTrigger(fk_trigger, constraintOid);
+ (void) CreateTrigger(fk_trigger, constraintOid, false);
/* Make changes-so-far visible */
CommandCounterIncrement();
}
fk_trigger->args = NIL;
- (void) CreateTrigger(fk_trigger, constraintOid);
+ (void) CreateTrigger(fk_trigger, constraintOid, false);
}
/*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.60 2009/01/20 18:59:37 heikki Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.61 2009/01/22 20:16:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* Remove dependency on owner.
*/
- deleteSharedDependencyRecordsFor(TableSpaceRelationId, tablespaceoid);
+ deleteSharedDependencyRecordsFor(TableSpaceRelationId, tablespaceoid, 0);
/*
* Acquire TablespaceCreateLock to ensure that no TablespaceCreateDbspace
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.245 2009/01/22 19:16:31 heikki Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.246 2009/01/22 20:16:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* be made to link the trigger to that constraint. constraintOid is zero when
* executing a user-entered CREATE TRIGGER command.
*
+ * If checkPermissions is true we require ACL_TRIGGER permissions on the
+ * relation. If not, the caller already checked permissions. (This is
+ * currently redundant with constraintOid being zero, but it's clearer to
+ * have a separate argument.)
+ *
* Note: can return InvalidOid if we decided to not create a trigger at all,
* but a foreign-key constraint. This is a kluge for backwards compatibility.
*/
Oid
-CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid)
+CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid, bool checkPermissions)
{
int16 tgtype;
int2vector *tgattr;
errmsg("permission denied: \"%s\" is a system catalog",
RelationGetRelationName(rel))));
- /* permission checks */
+ if (stmt->isconstraint && stmt->constrrel != NULL)
+ constrrelid = RangeVarGetRelid(stmt->constrrel, false);
- if (stmt->isconstraint)
+ /* permission checks */
+ if (checkPermissions)
{
- /* constraint trigger */
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
- ACL_REFERENCES);
+ ACL_TRIGGER);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
- if (stmt->constrrel != NULL)
+ if (OidIsValid(constrrelid))
{
- constrrelid = RangeVarGetRelid(stmt->constrrel, false);
-
aclresult = pg_class_aclcheck(constrrelid, GetUserId(),
- ACL_REFERENCES);
+ ACL_TRIGGER);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_CLASS,
get_rel_name(constrrelid));
}
}
- else
- {
- /* regular trigger */
- aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
- ACL_TRIGGER);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, ACL_KIND_CLASS,
- RelationGetRelationName(rel));
- }
/* Compute tgtype */
TRIGGER_CLEAR_TYPE(tgtype);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.15 2009/01/01 17:23:40 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.16 2009/01/22 20:16:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (removeOld)
{
deleteDependencyRecordsFor(myself.classId, myself.objectId);
- deleteSharedDependencyRecordsFor(myself.classId, myself.objectId);
+ deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
}
/*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.184 2009/01/01 17:23:40 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.185 2009/01/22 20:16:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
foreach(item, stmt->granted_roles)
{
- char *rolename = strVal(lfirst(item));
- Oid roleid = get_roleid_checked(rolename);
+ AccessPriv *priv = (AccessPriv *) lfirst(item);
+ char *rolename = priv->priv_name;
+ Oid roleid;
+
+ /* Must reject priv(columns) and ALL PRIVILEGES(columns) */
+ if (rolename == NULL || priv->cols != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg("column names cannot be included in GRANT/REVOKE ROLE")));
+ roleid = get_roleid_checked(rolename);
if (stmt->is_grant)
AddRoleMems(rolename, roleid,
stmt->grantee_roles, grantee_ids,
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.320 2009/01/01 17:23:41 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.321 2009/01/22 20:16:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "access/heapam.h"
#include "access/reloptions.h"
+#include "access/sysattr.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/heap.h"
ExecCheckRTEPerms(RangeTblEntry *rte)
{
AclMode requiredPerms;
+ AclMode relPerms;
+ AclMode remainingPerms;
Oid relOid;
Oid userid;
+ Bitmapset *tmpset;
+ int col;
/*
* Only plain-relation RTEs need to be checked here. Function RTEs are
userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
/*
- * We must have *all* the requiredPerms bits, so use aclmask not aclcheck.
+ * We must have *all* the requiredPerms bits, but some of the bits can be
+ * satisfied from column-level rather than relation-level permissions.
+ * First, remove any bits that are satisfied by relation permissions.
*/
- if (pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL)
- != requiredPerms)
- aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
- get_rel_name(relOid));
+ relPerms = pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL);
+ remainingPerms = requiredPerms & ~relPerms;
+ if (remainingPerms != 0)
+ {
+ /*
+ * If we lack any permissions that exist only as relation permissions,
+ * we can fail straight away.
+ */
+ if (remainingPerms & ~(ACL_SELECT | ACL_INSERT | ACL_UPDATE))
+ aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
+ get_rel_name(relOid));
+
+ /*
+ * Check to see if we have the needed privileges at column level.
+ *
+ * Note: failures just report a table-level error; it would be nicer
+ * to report a column-level error if we have some but not all of the
+ * column privileges.
+ */
+ if (remainingPerms & ACL_SELECT)
+ {
+ /*
+ * When the query doesn't explicitly reference any columns (for
+ * example, SELECT COUNT(*) FROM table), allow the query if we
+ * have SELECT on any column of the rel, as per SQL spec.
+ */
+ if (bms_is_empty(rte->selectedCols))
+ {
+ if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT,
+ ACLMASK_ANY) != ACLCHECK_OK)
+ aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
+ get_rel_name(relOid));
+ }
+
+ tmpset = bms_copy(rte->selectedCols);
+ while ((col = bms_first_member(tmpset)) >= 0)
+ {
+ /* remove the column number offset */
+ col += FirstLowInvalidHeapAttributeNumber;
+ if (col == InvalidAttrNumber)
+ {
+ /* Whole-row reference, must have priv on all cols */
+ if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT,
+ ACLMASK_ALL) != ACLCHECK_OK)
+ aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
+ get_rel_name(relOid));
+ }
+ else
+ {
+ if (pg_attribute_aclcheck(relOid, col, userid, ACL_SELECT)
+ != ACLCHECK_OK)
+ aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
+ get_rel_name(relOid));
+ }
+ }
+ bms_free(tmpset);
+ }
+
+ /*
+ * Basically the same for the mod columns, with either INSERT or UPDATE
+ * privilege as specified by remainingPerms.
+ */
+ remainingPerms &= ~ACL_SELECT;
+ if (remainingPerms != 0)
+ {
+ /*
+ * When the query doesn't explicitly change any columns, allow
+ * the query if we have permission on any column of the rel. This
+ * is to handle SELECT FOR UPDATE as well as possible corner cases
+ * in INSERT and UPDATE.
+ */
+ if (bms_is_empty(rte->modifiedCols))
+ {
+ if (pg_attribute_aclcheck_all(relOid, userid, remainingPerms,
+ ACLMASK_ANY) != ACLCHECK_OK)
+ aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
+ get_rel_name(relOid));
+ }
+
+ tmpset = bms_copy(rte->modifiedCols);
+ while ((col = bms_first_member(tmpset)) >= 0)
+ {
+ /* remove the column number offset */
+ col += FirstLowInvalidHeapAttributeNumber;
+ if (col == InvalidAttrNumber)
+ {
+ /* whole-row reference can't happen here */
+ elog(ERROR, "whole-row update is not implemented");
+ }
+ else
+ {
+ if (pg_attribute_aclcheck(relOid, col, userid, remainingPerms)
+ != ACLCHECK_OK)
+ aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
+ get_rel_name(relOid));
+ }
+ }
+ bms_free(tmpset);
+ }
+ }
}
/*
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.420 2009/01/16 13:27:23 heikki Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.421 2009/01/22 20:16:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
COPY_SCALAR_FIELD(inFromCl);
COPY_SCALAR_FIELD(requiredPerms);
COPY_SCALAR_FIELD(checkAsUser);
+ COPY_BITMAPSET_FIELD(selectedCols);
+ COPY_BITMAPSET_FIELD(modifiedCols);
return newnode;
}
return newnode;
}
+static AccessPriv *
+_copyAccessPriv(AccessPriv *from)
+{
+ AccessPriv *newnode = makeNode(AccessPriv);
+
+ COPY_STRING_FIELD(priv_name);
+ COPY_NODE_FIELD(cols);
+
+ return newnode;
+}
+
static GrantRoleStmt *
_copyGrantRoleStmt(GrantRoleStmt *from)
{
case T_FuncWithArgs:
retval = _copyFuncWithArgs(from);
break;
+ case T_AccessPriv:
+ retval = _copyAccessPriv(from);
+ break;
case T_XmlSerialize:
retval = _copyXmlSerialize(from);
break;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.345 2009/01/16 13:27:23 heikki Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.346 2009/01/22 20:16:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return true;
}
+static bool
+_equalAccessPriv(AccessPriv *a, AccessPriv *b)
+{
+ COMPARE_STRING_FIELD(priv_name);
+ COMPARE_NODE_FIELD(cols);
+
+ return true;
+}
+
static bool
_equalGrantRoleStmt(GrantRoleStmt *a, GrantRoleStmt *b)
{
COMPARE_SCALAR_FIELD(inFromCl);
COMPARE_SCALAR_FIELD(requiredPerms);
COMPARE_SCALAR_FIELD(checkAsUser);
+ COMPARE_BITMAPSET_FIELD(selectedCols);
+ COMPARE_BITMAPSET_FIELD(modifiedCols);
return true;
}
case T_FuncWithArgs:
retval = _equalFuncWithArgs(a, b);
break;
+ case T_AccessPriv:
+ retval = _equalAccessPriv(a, b);
+ break;
case T_XmlSerialize:
retval = _equalXmlSerialize(a, b);
break;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.349 2009/01/01 17:23:43 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.350 2009/01/22 20:16:04 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
* converts a bitmap set of integers
*
* Note: the output format is "(b int int ...)", similar to an integer List.
- * Currently bitmapsets do not appear in any node type that is stored in
- * rules, so there is no support in readfuncs.c for reading this format.
*/
static void
_outBitmapset(StringInfo str, Bitmapset *bms)
WRITE_BOOL_FIELD(inFromCl);
WRITE_UINT_FIELD(requiredPerms);
WRITE_OID_FIELD(checkAsUser);
+ WRITE_BITMAPSET_FIELD(selectedCols);
+ WRITE_BITMAPSET_FIELD(modifiedCols);
}
static void
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.220 2009/01/01 17:23:43 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.221 2009/01/22 20:16:04 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
token = pg_strtok(&length); /* skip :fldname */ \
local_node->fldname = nodeRead(NULL, 0)
+/* Read a bitmapset field */
+#define READ_BITMAPSET_FIELD(fldname) \
+ token = pg_strtok(&length); /* skip :fldname */ \
+ local_node->fldname = _readBitmapset()
+
/* Routine exit */
#define READ_DONE() \
return local_node
static Datum readDatum(bool typbyval);
+/*
+ * _readBitmapset
+ */
+static Bitmapset *
+_readBitmapset(void)
+{
+ Bitmapset *result = NULL;
+ READ_TEMP_LOCALS();
+
+ token = pg_strtok(&length);
+ if (token == NULL)
+ elog(ERROR, "incomplete Bitmapset structure");
+ if (length != 1 || token[0] != '(')
+ elog(ERROR, "unrecognized token: \"%.*s\"", length, token);
+
+ token = pg_strtok(&length);
+ if (token == NULL)
+ elog(ERROR, "incomplete Bitmapset structure");
+ if (length != 1 || token[0] != 'b')
+ elog(ERROR, "unrecognized token: \"%.*s\"", length, token);
+
+ for (;;)
+ {
+ int val;
+ char *endptr;
+
+ token = pg_strtok(&length);
+ if (token == NULL)
+ elog(ERROR, "unterminated Bitmapset structure");
+ if (length == 1 && token[0] == ')')
+ break;
+ val = (int) strtol(token, &endptr, 10);
+ if (endptr != token + length)
+ elog(ERROR, "unrecognized integer: \"%.*s\"", length, token);
+ result = bms_add_member(result, val);
+ }
+
+ return result;
+}
+
/*
* _readQuery
READ_BOOL_FIELD(inFromCl);
READ_UINT_FIELD(requiredPerms);
READ_OID_FIELD(checkAsUser);
+ READ_BITMAPSET_FIELD(selectedCols);
+ READ_BITMAPSET_FIELD(modifiedCols);
READ_DONE();
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.148 2009/01/01 17:23:44 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.149 2009/01/22 20:16:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* In the flat rangetable, we zero out substructure pointers that are not
* needed by the executor; this reduces the storage space and copying cost
* for cached plans. We keep only the alias and eref Alias fields, which
- * are needed by EXPLAIN.
+ * are needed by EXPLAIN, and the selectedCols and modifiedCols bitmaps,
+ * which are needed for executor-startup permissions checking.
*/
foreach(lc, rtable)
{
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.387 2009/01/08 13:42:33 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.388 2009/01/22 20:16:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "access/sysattr.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
* bugs of just that nature...)
*/
sub_pstate->p_rtable = sub_rtable;
+ sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */
sub_pstate->p_relnamespace = sub_relnamespace;
sub_pstate->p_varnamespace = sub_varnamespace;
/*
* Generate query's target list using the computed list of expressions.
+ * Also, mark all the target columns as needing insert permissions.
*/
+ rte = pstate->p_target_rangetblentry;
qry->targetList = NIL;
icols = list_head(icolumns);
attnos = list_head(attrnos);
{
Expr *expr = (Expr *) lfirst(lc);
ResTarget *col;
+ AttrNumber attr_num;
TargetEntry *tle;
col = (ResTarget *) lfirst(icols);
Assert(IsA(col, ResTarget));
+ attr_num = (AttrNumber) lfirst_int(attnos);
tle = makeTargetEntry(expr,
- (AttrNumber) lfirst_int(attnos),
+ attr_num,
col->name,
false);
qry->targetList = lappend(qry->targetList, tle);
+ rte->modifiedCols = bms_add_member(rte->modifiedCols,
+ attr_num - FirstLowInvalidHeapAttributeNumber);
+
icols = lnext(icols);
attnos = lnext(attnos);
}
List *targetvars,
*targetnames,
*sv_relnamespace,
- *sv_varnamespace,
- *sv_rtable;
+ *sv_varnamespace;
+ int sv_rtable_length;
RangeTblEntry *jrte;
int tllen;
* "ORDER BY upper(foo)" will draw the right error message rather than
* "foo not found".
*/
- jrte = addRangeTableEntryForJoin(NULL,
+ sv_rtable_length = list_length(pstate->p_rtable);
+
+ jrte = addRangeTableEntryForJoin(pstate,
targetnames,
JOIN_INNER,
targetvars,
NULL,
false);
- sv_rtable = pstate->p_rtable;
- pstate->p_rtable = list_make1(jrte);
-
sv_relnamespace = pstate->p_relnamespace;
pstate->p_relnamespace = NIL; /* no qualified names allowed */
&qry->targetList,
false /* no unknowns expected */ );
- pstate->p_rtable = sv_rtable;
+ pstate->p_rtable = list_truncate(pstate->p_rtable, sv_rtable_length);
pstate->p_relnamespace = sv_relnamespace;
pstate->p_varnamespace = sv_varnamespace;
transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
{
Query *qry = makeNode(Query);
+ RangeTblEntry *target_rte;
Node *qual;
ListCell *origTargetList;
ListCell *tl;
pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
/* Prepare non-junk columns for assignment to target table */
+ target_rte = pstate->p_target_rangetblentry;
origTargetList = list_head(stmt->targetList);
foreach(tl, qry->targetList)
origTarget->indirection,
origTarget->location);
+ /* Mark the target column as requiring update permissions */
+ target_rte->modifiedCols = bms_add_member(target_rte->modifiedCols,
+ attrno - FirstLowInvalidHeapAttributeNumber);
+
origTargetList = lnext(origTargetList);
}
if (origTargetList != NULL)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.655 2009/01/16 13:27:23 heikki Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.656 2009/01/22 20:16:05 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
static bool QueryIsRule = FALSE;
+/* Private struct for the result of privilege_target production */
+typedef struct PrivTarget
+{
+ GrantObjectType objtype;
+ List *objs;
+} PrivTarget;
+
/*
* If you need access to certain yacc-generated variables and find that
* they're static by default, uncomment the next line. (this is not a
WithClause *with;
A_Indices *aind;
ResTarget *target;
- PrivTarget *privtarget;
+ struct PrivTarget *privtarget;
+ AccessPriv *accesspriv;
InsertStmt *istmt;
VariableSetStmt *vsetstmt;
%type <str> iso_level opt_encoding
%type <node> grantee
%type <list> grantee_list
-%type <str> privilege
+%type <accesspriv> privilege
%type <list> privileges privilege_list
%type <privtarget> privilege_target
%type <funwithargs> function_with_argtypes
/*
- * A privilege list is represented as a list of strings; the validity of
- * the privilege names gets checked at execution. This is a bit annoying
- * but we have little choice because of the syntactic conflict with lists
- * of role names in GRANT/REVOKE. What's more, we have to call out in
- * the "privilege" production any reserved keywords that need to be usable
- * as privilege names.
+ * Privilege names are represented as strings; the validity of the privilege
+ * names gets checked at execution. This is a bit annoying but we have little
+ * choice because of the syntactic conflict with lists of role names in
+ * GRANT/REVOKE. What's more, we have to call out in the "privilege"
+ * production any reserved keywords that need to be usable as privilege names.
*/
/* either ALL [PRIVILEGES] or a list of individual privileges */
{ $$ = NIL; }
| ALL PRIVILEGES
{ $$ = NIL; }
+ | ALL '(' columnList ')'
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = NULL;
+ n->cols = $3;
+ $$ = list_make1(n);
+ }
+ | ALL PRIVILEGES '(' columnList ')'
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = NULL;
+ n->cols = $4;
+ $$ = list_make1(n);
+ }
;
-privilege_list: privilege
- { $$ = list_make1(makeString($1)); }
- | privilege_list ',' privilege
- { $$ = lappend($1, makeString($3)); }
+privilege_list: privilege { $$ = list_make1($1); }
+ | privilege_list ',' privilege { $$ = lappend($1, $3); }
;
-privilege: SELECT { $$ = pstrdup($1); }
- | REFERENCES { $$ = pstrdup($1); }
- | CREATE { $$ = pstrdup($1); }
- | ColId { $$ = $1; }
+privilege: SELECT opt_column_list
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup($1);
+ n->cols = $2;
+ $$ = n;
+ }
+ | REFERENCES opt_column_list
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup($1);
+ n->cols = $2;
+ $$ = n;
+ }
+ | CREATE opt_column_list
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup($1);
+ n->cols = $2;
+ $$ = n;
+ }
+ | ColId opt_column_list
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = $1;
+ n->cols = $2;
+ $$ = n;
+ }
;
privilege_target:
qualified_name_list
{
- PrivTarget *n = makeNode(PrivTarget);
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->objtype = ACL_OBJECT_RELATION;
n->objs = $1;
$$ = n;
}
| TABLE qualified_name_list
{
- PrivTarget *n = makeNode(PrivTarget);
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->objtype = ACL_OBJECT_RELATION;
n->objs = $2;
$$ = n;
}
| SEQUENCE qualified_name_list
{
- PrivTarget *n = makeNode(PrivTarget);
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->objtype = ACL_OBJECT_SEQUENCE;
n->objs = $2;
$$ = n;
}
| FOREIGN DATA_P WRAPPER name_list
{
- PrivTarget *n = makeNode(PrivTarget);
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->objtype = ACL_OBJECT_FDW;
n->objs = $4;
$$ = n;
}
| FOREIGN SERVER name_list
{
- PrivTarget *n = makeNode(PrivTarget);
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->objtype = ACL_OBJECT_FOREIGN_SERVER;
n->objs = $3;
$$ = n;
}
| FUNCTION function_with_argtypes_list
{
- PrivTarget *n = makeNode(PrivTarget);
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->objtype = ACL_OBJECT_FUNCTION;
n->objs = $2;
$$ = n;
}
| DATABASE name_list
{
- PrivTarget *n = makeNode(PrivTarget);
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->objtype = ACL_OBJECT_DATABASE;
n->objs = $2;
$$ = n;
}
| LANGUAGE name_list
{
- PrivTarget *n = makeNode(PrivTarget);
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->objtype = ACL_OBJECT_LANGUAGE;
n->objs = $2;
$$ = n;
}
| SCHEMA name_list
{
- PrivTarget *n = makeNode(PrivTarget);
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->objtype = ACL_OBJECT_NAMESPACE;
n->objs = $2;
$$ = n;
}
| TABLESPACE name_list
{
- PrivTarget *n = makeNode(PrivTarget);
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
n->objtype = ACL_OBJECT_TABLESPACE;
n->objs = $2;
$$ = n;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.185 2009/01/01 17:23:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.186 2009/01/22 20:16:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
List *src_colnames, List *src_colvars,
List **res_colnames, List **res_colvars);
static Node *transformJoinUsingClause(ParseState *pstate,
+ RangeTblEntry *leftRTE, RangeTblEntry *rightRTE,
List *leftVars, List *rightVars);
static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j,
RangeTblEntry *l_rte,
*
* If we find an explicit reference to the rel later during parse
* analysis, we will add the ACL_SELECT bit back again; see
- * scanRTEForColumn (for simple field references), ExpandColumnRefStar
- * (for foo.*) and ExpandAllTables (for *).
+ * markVarForSelectPriv and its callers.
*/
rte->requiredPerms = requiredPerms;
* Result is a transformed qualification expression.
*/
static Node *
-transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
+transformJoinUsingClause(ParseState *pstate,
+ RangeTblEntry *leftRTE, RangeTblEntry *rightRTE,
+ List *leftVars, List *rightVars)
{
Node *result = NULL;
ListCell *lvars,
* We cheat a little bit here by building an untransformed operator tree
* whose leaves are the already-transformed Vars. This is OK because
* transformExpr() won't complain about already-transformed subnodes.
+ * However, this does mean that we have to mark the columns as requiring
+ * SELECT privilege for ourselves; transformExpr() won't do it.
*/
forboth(lvars, leftVars, rvars, rightVars)
{
- Node *lvar = (Node *) lfirst(lvars);
- Node *rvar = (Node *) lfirst(rvars);
+ Var *lvar = (Var *) lfirst(lvars);
+ Var *rvar = (Var *) lfirst(rvars);
A_Expr *e;
+ /* Require read access to the join variables */
+ markVarForSelectPriv(pstate, lvar, leftRTE);
+ markVarForSelectPriv(pstate, rvar, rightRTE);
+
+ /* Now create the lvar = rvar join condition */
e = makeSimpleA_Expr(AEXPR_OP, "=",
copyObject(lvar), copyObject(rvar),
-1);
+ /* And combine into an AND clause, if multiple join columns */
if (result == NULL)
result = (Node *) e;
else
*r_colvars,
*res_colvars;
RangeTblEntry *rte;
+ int k;
/*
* Recursively process the left and right subtrees
}
j->quals = transformJoinUsingClause(pstate,
+ l_rte,
+ r_rte,
l_usingvars,
r_usingvars);
}
*top_rte = rte;
*top_rti = j->rtindex;
+ /* make a matching link to the JoinExpr for later use */
+ for (k = list_length(pstate->p_joinexprs) + 1; k < j->rtindex; k++)
+ pstate->p_joinexprs = lappend(pstate->p_joinexprs, NULL);
+ pstate->p_joinexprs = lappend(pstate->p_joinexprs, j);
+ Assert(list_length(pstate->p_joinexprs) == j->rtindex);
+
/*
* Prepare returned namespace list. If the JOIN has an alias then it
* hides the contained RTEs as far as the relnamespace goes;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.239 2009/01/01 17:23:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.240 2009/01/22 20:16:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* location is not filled in by makeVar */
result->location = location;
+ /* mark relation as requiring whole-row SELECT access */
+ markVarForSelectPriv(pstate, result, rte);
+
return (Node *) result;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.140 2009/01/01 17:23:46 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.141 2009/01/22 20:16:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
const char *refname, int location);
static RangeTblEntry *scanNameSpaceForRelid(ParseState *pstate, Oid relid,
int location);
+static void markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte,
+ int rtindex, AttrNumber col);
static bool isLockedRel(ParseState *pstate, char *refname);
static void expandRelation(Oid relid, Alias *eref,
int rtindex, int sublevels_up,
* If found, return an appropriate Var node, else return NULL.
* If the name proves ambiguous within this RTE, raise error.
*
- * Side effect: if we find a match, mark the RTE as requiring read access.
- * See comments in setTargetTable().
- *
- * NOTE: if the RTE is for a join, marking it as requiring read access does
- * nothing. It might seem that we need to propagate the mark to all the
- * contained RTEs, but that is not necessary. This is so because a join
- * expression can only appear in a FROM clause, and any table named in
- * FROM will be marked as requiring read access from the beginning.
+ * Side effect: if we find a match, mark the RTE as requiring read access
+ * for the column.
*/
Node *
scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname,
{
Node *result = NULL;
int attnum = 0;
+ Var *var;
ListCell *c;
/*
errmsg("column reference \"%s\" is ambiguous",
colname),
parser_errposition(pstate, location)));
- result = (Node *) make_var(pstate, rte, attnum, location);
- /* Require read access */
- rte->requiredPerms |= ACL_SELECT;
+ var = make_var(pstate, rte, attnum, location);
+ /* Require read access to the column */
+ markVarForSelectPriv(pstate, var, rte);
+ result = (Node *) var;
}
}
Int16GetDatum(attnum),
0, 0))
{
- result = (Node *) make_var(pstate, rte, attnum, location);
- /* Require read access */
- rte->requiredPerms |= ACL_SELECT;
+ var = make_var(pstate, rte, attnum, location);
+ /* Require read access to the column */
+ markVarForSelectPriv(pstate, var, rte);
+ result = (Node *) var;
}
}
}
return scanRTEForColumn(pstate, rte, colname, location);
}
+/*
+ * markRTEForSelectPriv
+ * Mark the specified column of an RTE as requiring SELECT privilege
+ *
+ * col == InvalidAttrNumber means a "whole row" reference
+ *
+ * The caller should pass the actual RTE if it has it handy; otherwise pass
+ * NULL, and we'll look it up here. (This uglification of the API is
+ * worthwhile because nearly all external callers have the RTE at hand.)
+ */
+static void
+markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte,
+ int rtindex, AttrNumber col)
+{
+ if (rte == NULL)
+ rte = rt_fetch(rtindex, pstate->p_rtable);
+
+ if (rte->rtekind == RTE_RELATION)
+ {
+ /* Make sure the rel as a whole is marked for SELECT access */
+ rte->requiredPerms |= ACL_SELECT;
+ /* Must offset the attnum to fit in a bitmapset */
+ rte->selectedCols = bms_add_member(rte->selectedCols,
+ col - FirstLowInvalidHeapAttributeNumber);
+ }
+ else if (rte->rtekind == RTE_JOIN)
+ {
+ if (col == InvalidAttrNumber)
+ {
+ /*
+ * A whole-row reference to a join has to be treated as
+ * whole-row references to the two inputs.
+ */
+ JoinExpr *j;
+
+ if (rtindex > 0 && rtindex <= list_length(pstate->p_joinexprs))
+ j = (JoinExpr *) list_nth(pstate->p_joinexprs, rtindex - 1);
+ else
+ j = NULL;
+ if (j == NULL)
+ elog(ERROR, "could not find JoinExpr for whole-row reference");
+ Assert(IsA(j, JoinExpr));
+
+ /* Note: we can't see FromExpr here */
+ if (IsA(j->larg, RangeTblRef))
+ {
+ int varno = ((RangeTblRef *) j->larg)->rtindex;
+
+ markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber);
+ }
+ else if (IsA(j->larg, JoinExpr))
+ {
+ int varno = ((JoinExpr *) j->larg)->rtindex;
+
+ markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber);
+ }
+ else
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(j->larg));
+ if (IsA(j->rarg, RangeTblRef))
+ {
+ int varno = ((RangeTblRef *) j->rarg)->rtindex;
+
+ markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber);
+ }
+ else if (IsA(j->rarg, JoinExpr))
+ {
+ int varno = ((JoinExpr *) j->rarg)->rtindex;
+
+ markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber);
+ }
+ else
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(j->rarg));
+ }
+ else
+ {
+ /*
+ * Regular join attribute, look at the alias-variable list.
+ *
+ * The aliasvar could be either a Var or a COALESCE expression,
+ * but in the latter case we should already have marked the two
+ * referent variables as being selected, due to their use in the
+ * JOIN clause. So we need only be concerned with the simple
+ * Var case.
+ */
+ Var *aliasvar;
+
+ Assert(col > 0 && col <= list_length(rte->joinaliasvars));
+ aliasvar = (Var *) list_nth(rte->joinaliasvars, col - 1);
+ if (IsA(aliasvar, Var))
+ markVarForSelectPriv(pstate, aliasvar, NULL);
+ }
+ }
+ /* other RTE types don't require privilege marking */
+}
+
+/*
+ * markVarForSelectPriv
+ * Mark the RTE referenced by a Var as requiring SELECT privilege
+ *
+ * The caller should pass the Var's referenced RTE if it has it handy
+ * (nearly all do); otherwise pass NULL.
+ */
+void
+markVarForSelectPriv(ParseState *pstate, Var *var, RangeTblEntry *rte)
+{
+ Index lv;
+
+ Assert(IsA(var, Var));
+ /* Find the appropriate pstate if it's an uplevel Var */
+ for (lv = 0; lv < var->varlevelsup; lv++)
+ pstate = pstate->parentParseState;
+ markRTEForSelectPriv(pstate, rte, var->varno, var->varattno);
+}
+
/*
* buildRelationAliases
* Construct the eref column name list for a relation RTE.
rte->requiredPerms = ACL_SELECT;
rte->checkAsUser = InvalidOid; /* not set-uid by default, either */
+ rte->selectedCols = NULL;
+ rte->modifiedCols = NULL;
/*
* Add completed RTE to pstate's range table list, but not to join list
rte->requiredPerms = ACL_SELECT;
rte->checkAsUser = InvalidOid; /* not set-uid by default, either */
+ rte->selectedCols = NULL;
+ rte->modifiedCols = NULL;
/*
* Add completed RTE to pstate's range table list, but not to join list
rte->requiredPerms = 0;
rte->checkAsUser = InvalidOid;
+ rte->selectedCols = NULL;
+ rte->modifiedCols = NULL;
/*
* Add completed RTE to pstate's range table list, but not to join list
rte->requiredPerms = 0;
rte->checkAsUser = InvalidOid;
+ rte->selectedCols = NULL;
+ rte->modifiedCols = NULL;
/*
* Add completed RTE to pstate's range table list, but not to join list
*/
rte->inh = false; /* never true for values RTEs */
rte->inFromCl = inFromCl;
+
rte->requiredPerms = 0;
rte->checkAsUser = InvalidOid;
+ rte->selectedCols = NULL;
+ rte->modifiedCols = NULL;
/*
* Add completed RTE to pstate's range table list, but not to join list
rte->requiredPerms = 0;
rte->checkAsUser = InvalidOid;
+ rte->selectedCols = NULL;
+ rte->modifiedCols = NULL;
/*
* Add completed RTE to pstate's range table list, but not to join list
rte->requiredPerms = 0;
rte->checkAsUser = InvalidOid;
+ rte->selectedCols = NULL;
+ rte->modifiedCols = NULL;
/*
* Add completed RTE to pstate's range table list, but not to join list
* As with expandRTE, rtindex/sublevels_up determine the varno/varlevelsup
* fields of the Vars produced, and location sets their location.
* pstate->p_next_resno determines the resnos assigned to the TLEs.
+ * The referenced columns are marked as requiring SELECT access.
*/
List *
expandRelAttrs(ParseState *pstate, RangeTblEntry *rte,
expandRTE(rte, rtindex, sublevels_up, location, false,
&names, &vars);
+ /*
+ * Require read access to the table. This is normally redundant with the
+ * markVarForSelectPriv calls below, but not if the table has zero
+ * columns.
+ */
+ rte->requiredPerms |= ACL_SELECT;
+
forboth(name, names, var, vars)
{
char *label = strVal(lfirst(name));
- Node *varnode = (Node *) lfirst(var);
+ Var *varnode = (Var *) lfirst(var);
TargetEntry *te;
te = makeTargetEntry((Expr *) varnode,
label,
false);
te_list = lappend(te_list, te);
+
+ /* Require read access to each column */
+ markVarForSelectPriv(pstate, varnode, rte);
}
Assert(name == NULL && var == NULL); /* lists not the same length? */
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.169 2009/01/01 17:23:46 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.170 2009/01/22 20:16:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* in a SELECT target list (where we want TargetEntry nodes in the result)
* and foo.* in a ROW() or VALUES() construct (where we want just bare
* expressions).
+ *
+ * The referenced columns are marked as requiring SELECT access.
*/
static List *
ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
makeRangeVar(schemaname, relname,
cref->location));
- /* Require read access --- see comments in setTargetTable() */
- rte->requiredPerms |= ACL_SELECT;
-
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
if (targetlist)
+ {
+ /* expandRelAttrs handles permissions marking */
return expandRelAttrs(pstate, rte, rtindex, sublevels_up,
cref->location);
+ }
else
{
List *vars;
+ ListCell *l;
expandRTE(rte, rtindex, sublevels_up, cref->location, false,
NULL, &vars);
+
+ /*
+ * Require read access to the table. This is normally redundant
+ * with the markVarForSelectPriv calls below, but not if the table
+ * has zero columns.
+ */
+ rte->requiredPerms |= ACL_SELECT;
+
+ /* Require read access to each column */
+ foreach(l, vars)
+ {
+ Var *var = (Var *) lfirst(l);
+
+ markVarForSelectPriv(pstate, var, rte);
+ }
+
return vars;
}
}
* varnamespace. We do not consider relnamespace because that would include
* input tables of aliasless JOINs, NEW/OLD pseudo-entries, implicit RTEs,
* etc.
+ *
+ * The referenced relations/columns are marked as requiring SELECT access.
*/
static List *
ExpandAllTables(ParseState *pstate, int location)
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
int rtindex = RTERangeTablePosn(pstate, rte, NULL);
- /* Require read access --- see comments in setTargetTable() */
- rte->requiredPerms |= ACL_SELECT;
-
target = list_concat(target,
expandRelAttrs(pstate, rte, rtindex, 0,
location));
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.183 2009/01/22 17:27:54 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.184 2009/01/22 20:16:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Assert(subrte->relid == relation->rd_id);
subrte->requiredPerms = rte->requiredPerms;
subrte->checkAsUser = rte->checkAsUser;
+ subrte->selectedCols = rte->selectedCols;
+ subrte->modifiedCols = rte->modifiedCols;
rte->requiredPerms = 0; /* no permission check on subquery itself */
rte->checkAsUser = InvalidOid;
+ rte->selectedCols = NULL;
+ rte->modifiedCols = NULL;
/*
* FOR UPDATE/SHARE of view?
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.304 2009/01/01 17:23:48 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.305 2009/01/22 20:16:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
break;
case T_CreateTrigStmt:
- CreateTrigger((CreateTrigStmt *) parsetree, InvalidOid);
+ CreateTrigger((CreateTrigStmt *) parsetree, InvalidOid, true);
break;
case T_DropPropertyStmt:
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.145 2009/01/01 17:23:48 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.146 2009/01/22 20:16:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return new_acl;
}
+/*
+ * Copy an ACL
+ */
+Acl *
+aclcopy(const Acl *orig_acl)
+{
+ Acl *result_acl;
+
+ result_acl = allocacl(ACL_NUM(orig_acl));
+
+ memcpy(ACL_DAT(result_acl),
+ ACL_DAT(orig_acl),
+ ACL_NUM(orig_acl) * sizeof(AclItem));
+
+ return result_acl;
+}
+
+/*
+ * Concatenate two ACLs
+ *
+ * This is a bit cheesy, since we may produce an ACL with redundant entries.
+ * Be careful what the result is used for!
+ */
+Acl *
+aclconcat(const Acl *left_acl, const Acl *right_acl)
+{
+ Acl *result_acl;
+
+ result_acl = allocacl(ACL_NUM(left_acl) + ACL_NUM(right_acl));
+
+ memcpy(ACL_DAT(result_acl),
+ ACL_DAT(left_acl),
+ ACL_NUM(left_acl) * sizeof(AclItem));
+
+ memcpy(ACL_DAT(result_acl) + ACL_NUM(left_acl),
+ ACL_DAT(right_acl),
+ ACL_NUM(right_acl) * sizeof(AclItem));
+
+ return result_acl;
+}
+
/*
* Verify that an ACL array is acceptable (one-dimensional and has no nulls)
*/
{
AclMode world_default;
AclMode owner_default;
+ int nacl;
Acl *acl;
AclItem *aip;
switch (objtype)
{
+ case ACL_OBJECT_COLUMN:
+ /* by default, columns have no extra privileges */
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_NO_RIGHTS;
+ break;
case ACL_OBJECT_RELATION:
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_RELATION;
break;
}
- acl = allocacl((world_default != ACL_NO_RIGHTS) ? 2 : 1);
+ nacl = 0;
+ if (world_default != ACL_NO_RIGHTS)
+ nacl++;
+ if (owner_default != ACL_NO_RIGHTS)
+ nacl++;
+
+ acl = allocacl(nacl);
aip = ACL_DAT(acl);
if (world_default != ACL_NO_RIGHTS)
* "_SYSTEM"-like ACL entry, by internally special-casing the owner
* whereever we are testing grant options.
*/
- aip->ai_grantee = ownerId;
- aip->ai_grantor = ownerId;
- ACLITEM_SET_PRIVS_GOPTIONS(*aip, owner_default, ACL_NO_RIGHTS);
+ if (owner_default != ACL_NO_RIGHTS)
+ {
+ aip->ai_grantee = ownerId;
+ aip->ai_grantor = ownerId;
+ ACLITEM_SET_PRIVS_GOPTIONS(*aip, owner_default, ACL_NO_RIGHTS);
+ }
return acl;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.281 2009/01/22 17:27:54 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.282 2009/01/22 20:16:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
memcpy(relation->rd_att->attrs[attp->attnum - 1],
attp,
- ATTRIBUTE_TUPLE_SIZE);
+ ATTRIBUTE_FIXED_PART_SIZE);
/* Update constraint/default info */
if (attp->attnotnull)
{
memcpy(relation->rd_att->attrs[i],
&att[i],
- ATTRIBUTE_TUPLE_SIZE);
+ ATTRIBUTE_FIXED_PART_SIZE);
has_not_null |= att[i].attnotnull;
/* make sure attcacheoff is valid */
relation->rd_att->attrs[i]->attcacheoff = -1;
for (i = 0; i < natts; i++)
{
- memcpy(result->attrs[i], &attrs[i], ATTRIBUTE_TUPLE_SIZE);
+ memcpy(result->attrs[i], &attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
/* make sure attcacheoff is valid */
result->attrs[i]->attcacheoff = -1;
}
{
if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
goto read_failed;
- if (len != ATTRIBUTE_TUPLE_SIZE)
+ if (len != ATTRIBUTE_FIXED_PART_SIZE)
goto read_failed;
if ((nread = fread(rel->rd_att->attrs[i], 1, len, fp)) != len)
goto read_failed;
/* next, do all the attribute tuple form data entries */
for (i = 0; i < relform->relnatts; i++)
{
- write_item(rel->rd_att->attrs[i], ATTRIBUTE_TUPLE_SIZE, fp);
+ write_item(rel->rd_att->attrs[i], ATTRIBUTE_FIXED_PART_SIZE, fp);
}
/* next, do the access method specific field */
* Portions Copyright (c) 1994, Regents of the University of California
* Portions taken from FreeBSD.
*
- * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.166 2009/01/01 17:23:53 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.167 2009/01/22 20:16:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
" FROM pg_ts_template;\n",
"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
" FROM pg_ts_config;\n",
- "INSERT INTO pg_shdepend SELECT 0, 0, 0, tableoid, oid, 'p' "
+ "INSERT INTO pg_shdepend SELECT 0,0,0,0, tableoid,oid, 'p' "
" FROM pg_authid;\n",
NULL
};
* 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.43 2009/01/01 17:23:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.44 2009/01/22 20:16:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define supports_grant_options(version) ((version) >= 70400)
-static bool parseAclItem(const char *item, const char *type, const char *name,
- int remoteVersion, PQExpBuffer grantee, PQExpBuffer grantor,
+static bool parseAclItem(const char *item, const char *type,
+ const char *name, const char *subname, int remoteVersion,
+ PQExpBuffer grantee, PQExpBuffer grantor,
PQExpBuffer privs, PQExpBuffer privswgo);
static char *copyAclUserName(PQExpBuffer output, char *input);
-static void AddAcl(PQExpBuffer aclbuf, const char *keyword);
+static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
+ const char *subname);
/*
* Build GRANT/REVOKE command(s) for an object.
*
* name: the object name, in the form to use in the commands (already quoted)
+ * subname: the sub-object name, if any (already quoted); NULL if none
* type: the object type (as seen in GRANT command: must be one of
* TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, or TABLESPACE)
* acls: the ACL string fetched from the 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: beware of passing fmtId() result as 'name', since this routine
- * uses fmtId() internally.
+ * 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 *type,
- const char *acls, const char *owner,
+buildACLCommands(const char *name, const char *subname,
+ const char *type, const char *acls, const char *owner,
int remoteVersion,
PQExpBuffer sql)
{
* wire-in knowledge about the default public privileges for different
* kinds of objects.
*/
- appendPQExpBuffer(firstsql, "REVOKE ALL ON %s %s FROM PUBLIC;\n",
- type, name);
+ appendPQExpBuffer(firstsql, "REVOKE ALL");
+ if (subname)
+ appendPQExpBuffer(firstsql, "(%s)", subname);
+ appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name);
/*
* We still need some hacking though to cover the case where new default
/* Scan individual ACL items */
for (i = 0; i < naclitems; i++)
{
- if (!parseAclItem(aclitems[i], type, name, remoteVersion,
+ if (!parseAclItem(aclitems[i], type, name, subname, remoteVersion,
grantee, grantor, privs, privswgo))
return false;
? strcmp(privswgo->data, "ALL") != 0
: strcmp(privs->data, "ALL") != 0)
{
- appendPQExpBuffer(firstsql, "REVOKE ALL ON %s %s FROM %s;\n",
- type, name,
- fmtId(grantee->data));
+ appendPQExpBuffer(firstsql, "REVOKE ALL");
+ 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",
+ appendPQExpBuffer(firstsql,
+ "GRANT %s ON %s %s TO %s;\n",
privs->data, type, name,
fmtId(grantee->data));
if (privswgo->len > 0)
- appendPQExpBuffer(firstsql, "GRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
+ appendPQExpBuffer(firstsql,
+ "GRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
privswgo->data, type, name,
fmtId(grantee->data));
}
* If we didn't find any owner privs, the owner must have revoked 'em all
*/
if (!found_owner_privs && owner)
- appendPQExpBuffer(firstsql, "REVOKE ALL ON %s %s FROM %s;\n",
+ {
+ appendPQExpBuffer(firstsql, "REVOKE ALL");
+ if (subname)
+ appendPQExpBuffer(firstsql, "(%s)", subname);
+ appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
type, name, fmtId(owner));
+ }
destroyPQExpBuffer(grantee);
destroyPQExpBuffer(grantor);
* appropriate.
*/
static bool
-parseAclItem(const char *item, const char *type, const char *name,
- int remoteVersion, PQExpBuffer grantee, PQExpBuffer grantor,
+parseAclItem(const char *item, const char *type,
+ const char *name, const char *subname, int remoteVersion,
+ PQExpBuffer grantee, PQExpBuffer grantor,
PQExpBuffer privs, PQExpBuffer privswgo)
{
char *buf;
{ \
if (*(pos + 1) == '*') \
{ \
- AddAcl(privswgo, keywd); \
+ AddAcl(privswgo, keywd, subname); \
all_without_go = false; \
} \
else \
{ \
- AddAcl(privs, keywd); \
+ AddAcl(privs, keywd, subname); \
all_with_go = false; \
} \
} \
/* table only */
CONVERT_PRIV('a', "INSERT");
if (remoteVersion >= 70200)
- {
- CONVERT_PRIV('d', "DELETE");
CONVERT_PRIV('x', "REFERENCES");
- CONVERT_PRIV('t', "TRIGGER");
+ /* rest are not applicable to columns */
+ if (subname == NULL)
+ {
+ if (remoteVersion >= 70200)
+ {
+ CONVERT_PRIV('d', "DELETE");
+ CONVERT_PRIV('t', "TRIGGER");
+ }
+ if (remoteVersion >= 80400)
+ CONVERT_PRIV('D', "TRUNCATE");
}
- if (remoteVersion >= 80400)
- CONVERT_PRIV('D', "TRUNCATE");
}
/* UPDATE */
{
resetPQExpBuffer(privs);
printfPQExpBuffer(privswgo, "ALL");
+ if (subname)
+ appendPQExpBuffer(privswgo, "(%s)", subname);
}
else if (all_without_go)
{
resetPQExpBuffer(privswgo);
printfPQExpBuffer(privs, "ALL");
+ if (subname)
+ appendPQExpBuffer(privs, "(%s)", subname);
}
free(buf);
* Append a privilege keyword to a keyword list, inserting comma if needed.
*/
static void
-AddAcl(PQExpBuffer aclbuf, const char *keyword)
+AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname)
{
if (aclbuf->len > 0)
appendPQExpBufferChar(aclbuf, ',');
appendPQExpBuffer(aclbuf, "%s", keyword);
+ if (subname)
+ appendPQExpBuffer(aclbuf, "(%s)", subname);
}
* 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.22 2009/01/01 17:23:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.h,v 1.23 2009/01/22 20:16:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
const char *dqprefix);
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 *type,
- const char *acls, const char *owner,
+extern bool buildACLCommands(const char *name, const char *subname,
+ const char *type, const char *acls, const char *owner,
int remoteVersion,
PQExpBuffer sql);
extern void processSQLNamePattern(PGconn *conn, PQExpBuffer buf,
* by PostgreSQL
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.515 2009/01/22 17:27:54 petere Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.516 2009/01/22 20:16:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
const char *owner, CatalogId catalogId, DumpId dumpId);
static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
- const char *type, const char *name,
+ const char *type, const char *name, const char *subname,
const char *tag, const char *nspname, const char *owner,
const char *acls);
nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
- qnspname, nspinfo->dobj.name, NULL,
+ qnspname, NULL, nspinfo->dobj.name, NULL,
nspinfo->rolname, nspinfo->nspacl);
free(qnspname);
if (plang->lanpltrusted)
dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE",
- qlanname, plang->dobj.name,
+ qlanname, NULL, plang->dobj.name,
lanschema,
plang->lanowner, plang->lanacl);
finfo->dobj.catId, 0, finfo->dobj.dumpId);
dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION",
- funcsig, funcsig_tag,
+ funcsig, NULL, funcsig_tag,
finfo->dobj.namespace->dobj.name,
finfo->rolname, finfo->proacl);
dumpACL(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
"FUNCTION",
- aggsig, aggsig_tag,
+ aggsig, NULL, aggsig_tag,
agginfo->aggfn.dobj.namespace->dobj.name,
agginfo->aggfn.rolname, agginfo->aggfn.proacl);
namecopy = strdup(fmtId(fdwinfo->dobj.name));
dumpACL(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
"FOREIGN DATA WRAPPER",
- namecopy, fdwinfo->dobj.name,
+ namecopy, NULL, fdwinfo->dobj.name,
NULL, fdwinfo->rolname,
fdwinfo->fdwacl);
free(namecopy);
namecopy = strdup(fmtId(srvinfo->dobj.name));
dumpACL(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
"SERVER",
- namecopy, srvinfo->dobj.name,
+ namecopy, NULL, srvinfo->dobj.name,
NULL, srvinfo->rolname,
srvinfo->srvacl);
free(namecopy);
* 'objDumpId' is the dump ID of the underlying object.
* 'type' must be TABLE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, or TABLESPACE.
* 'name' is the formatted name of the object. Must be quoted etc. already.
+ * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
* 'tag' is the tag for the archive entry (typ. unquoted name of object).
* 'nspname' is the namespace the object is in (NULL if none).
* 'owner' is the owner, NULL if there is no owner (for languages).
*/
static void
dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
- const char *type, const char *name,
+ const char *type, const char *name, const char *subname,
const char *tag, const char *nspname, const char *owner,
const char *acls)
{
sql = createPQExpBuffer();
- if (!buildACLCommands(name, 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);
static void
dumpTable(Archive *fout, TableInfo *tbinfo)
{
- char *namecopy;
-
if (tbinfo->dobj.dump)
{
+ char *namecopy;
+
if (tbinfo->relkind == RELKIND_SEQUENCE)
dumpSequence(fout, tbinfo);
else if (!dataOnly)
namecopy = strdup(fmtId(tbinfo->dobj.name));
dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
(tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE",
- namecopy, tbinfo->dobj.name,
+ namecopy, NULL, tbinfo->dobj.name,
tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
tbinfo->relacl);
+
+ /*
+ * Handle column ACLs, if any. Note: we pull these with a separate
+ * query rather than trying to fetch them during getTableAttrs, so
+ * that we won't miss ACLs on system columns.
+ */
+ if (g_fout->remoteVersion >= 80400)
+ {
+ PQExpBuffer query = createPQExpBuffer();
+ PGresult *res;
+ int i;
+
+ appendPQExpBuffer(query,
+ "SELECT attname, attacl FROM pg_catalog.pg_attribute "
+ "WHERE attrelid = '%u' AND NOT attisdropped AND attacl IS NOT NULL "
+ "ORDER BY attnum",
+ tbinfo->dobj.catId.oid);
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ char *attname = PQgetvalue(res, i, 0);
+ char *attacl = PQgetvalue(res, i, 1);
+ char *attnamecopy;
+ char *acltag;
+
+ attnamecopy = strdup(fmtId(attname));
+ acltag = malloc(strlen(tbinfo->dobj.name) + strlen(attname) + 2);
+ sprintf(acltag, "%s.%s", tbinfo->dobj.name, attname);
+ /* Column's GRANT type is always TABLE */
+ dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, "TABLE",
+ namecopy, attnamecopy, acltag,
+ tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
+ attacl);
+ free(attnamecopy);
+ free(acltag);
+ }
+ PQclear(res);
+ destroyPQExpBuffer(query);
+ }
+
free(namecopy);
}
}
* Portions Copyright (c) 1994, Regents of the University of California
*
*
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.112 2009/01/06 18:01:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.113 2009/01/22 20:16:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
appendPQExpBuffer(buf, ";\n");
if (!skip_acls &&
- !buildACLCommands(fspcname, "TABLESPACE", spcacl, spcowner,
+ !buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, spcowner,
server_version, buf))
{
fprintf(stderr, _("%s: could not parse ACL list (%s) for tablespace \"%s\"\n"),
}
if (!skip_acls &&
- !buildACLCommands(fdbname, "DATABASE", dbacl, dbowner,
+ !buildACLCommands(fdbname, NULL, "DATABASE", dbacl, dbowner,
server_version, buf))
{
fprintf(stderr, _("%s: could not parse ACL list (%s) for database \"%s\"\n"),
*
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.197 2009/01/20 02:13:42 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.198 2009/01/22 20:16:08 tgl Exp $
*/
#include "postgres_fe.h"
/*
- * List Tables Grant/Revoke Permissions
+ * List Tables' Grant/Revoke Permissions
* \z (now also \dp -- perhaps more mnemonic)
*/
bool
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
- static const bool translate_columns[] = {false, false, true, false};
+ static const bool translate_columns[] = {false, false, true, false, false};
initPQExpBuffer(&buf);
gettext_noop("Name"),
gettext_noop("table"), gettext_noop("view"), gettext_noop("sequence"),
gettext_noop("Type"));
+
printACLColumn(&buf, "c.relacl");
+
+ if (pset.sversion >= 80400)
+ appendPQExpBuffer(&buf,
+ ",\n pg_catalog.array_to_string(ARRAY(\n"
+ " SELECT attname || E':\\n ' || pg_catalog.array_to_string(attacl, E'\\n ')\n"
+ " FROM pg_catalog.pg_attribute a\n"
+ " WHERE attrelid = c.oid AND NOT attisdropped AND attacl IS NOT NULL\n"
+ " ), E'\\n') AS \"%s\"",
+ gettext_noop("Column access privileges"));
+
appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_class c\n"
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
"WHERE c.relkind IN ('r', 'v', 'S')\n");
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
- static const bool translate_columns[] = {false, false, true, false, false, false};
+ static const bool translate_columns[] = {false, false, true, false, false, false, false};
if (!(showTables || showIndexes || showViews || showSeq))
showTables = showViews = showSeq = true;
if (showSeq)
appendPQExpBuffer(&buf, "'S',");
if (showSystem)
- appendPQExpBuffer(&buf, "'s',"); /* was RELKIND_SPECIAL in <= 8.1.X */
+ appendPQExpBuffer(&buf, "'s',"); /* was RELKIND_SPECIAL in <= 8.1 */
appendPQExpBuffer(&buf, "''"); /* dummy */
appendPQExpBuffer(&buf, ")\n");
* 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.518 2009/01/16 13:27:24 heikki Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.519 2009/01/22 20:16:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200901161
+#define CATALOG_VERSION_NO 200901221
#endif
* 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.38 2009/01/01 17:23:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.39 2009/01/22 20:16:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
Oid classId; /* Class Id from pg_class */
Oid objectId; /* OID of the object */
- int32 objectSubId; /* Subitem within the object (column of table) */
+ int32 objectSubId; /* Subitem within object (eg column), or 0 */
} ObjectAddress;
/* expansible list of ObjectAddresses (private in dependency.c) */
ObjectAddress *referenced,
SharedDependencyType deptype);
-extern void deleteSharedDependencyRecordsFor(Oid classId, Oid objectId);
+extern void deleteSharedDependencyRecordsFor(Oid classId, Oid objectId,
+ int32 objectSubId);
extern void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner);
extern void changeDependencyOnOwner(Oid classId, Oid objectId,
Oid newOwnerId);
-extern void updateAclDependencies(Oid classId, Oid objectId,
+extern void updateAclDependencies(Oid classId, Oid objectId, int32 objectSubId,
Oid ownerId, bool isGrant,
int noldmembers, Oid *oldmembers,
int nnewmembers, Oid *newmembers);
* 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.105 2009/01/01 17:23:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.106 2009/01/22 20:16:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define RewriteRelRulenameIndexId 2693
/* This following index is not used for a cache and is not unique */
-DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops));
+DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
#define SharedDependDependerIndexId 1232
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
* 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_attribute.h,v 1.145 2009/01/01 17:23:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.146 2009/01/22 20:16:08 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
/* Number of times inherited from direct parent relation(s) */
int4 attinhcount;
+
+ /*
+ * VARIABLE LENGTH FIELDS start here. These fields may be NULL, too.
+ *
+ * NOTE: the following fields are not present in tuple descriptors!
+ */
+
+ /* Column-level access permissions */
+ aclitem attacl[1];
} FormData_pg_attribute;
/*
- * someone should figure out how to do this properly. (The problem is
- * the size of the C struct is not the same as the size of the tuple
- * because of alignment padding at the end of the struct.)
+ * ATTRIBUTE_FIXED_PART_SIZE is the size of the fixed-layout,
+ * guaranteed-not-null part of a pg_attribute row. This is in fact as much
+ * of the row as gets copied into tuple descriptors, so don't expect you
+ * can access fields beyond attinhcount except in a real tuple!
*/
-#define ATTRIBUTE_TUPLE_SIZE \
+#define ATTRIBUTE_FIXED_PART_SIZE \
(offsetof(FormData_pg_attribute,attinhcount) + sizeof(int4))
/* ----------------
* ----------------
*/
-#define Natts_pg_attribute 17
+#define Natts_pg_attribute 18
#define Anum_pg_attribute_attrelid 1
#define Anum_pg_attribute_attname 2
#define Anum_pg_attribute_atttypid 3
#define Anum_pg_attribute_attisdropped 15
#define Anum_pg_attribute_attislocal 16
#define Anum_pg_attribute_attinhcount 17
+#define Anum_pg_attribute_attacl 18
-
-/* ----------------
- * SCHEMA_ macros for declaring hardcoded tuple descriptors.
- * these are used in utils/cache/relcache.c
- * ----------------
-#define SCHEMA_NAME(x) CppConcat(Name_,x)
-#define SCHEMA_DESC(x) CppConcat(Desc_,x)
-#define SCHEMA_NATTS(x) CppConcat(Natts_,x)
-#define SCHEMA_DEF(x) \
- FormData_pg_attribute \
- SCHEMA_DESC(x) [ SCHEMA_NATTS(x) ] = \
- { \
- CppConcat(Schema_,x) \
- }
- */
-
/* ----------------
* initial contents of pg_attribute
*
* ----------------
*/
#define Schema_pg_type \
-{ 1247, {"typname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0 }, \
-{ 1247, {"typnamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typowner"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typlen"}, 21, -1, 2, 4, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
-{ 1247, {"typbyval"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1247, {"typtype"}, 18, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1247, {"typcategory"}, 18, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1247, {"typispreferred"},16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1247, {"typisdefined"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1247, {"typdelim"}, 18, -1, 1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1247, {"typrelid"}, 26, -1, 4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typelem"}, 26, -1, 4, 12, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typarray"}, 26, -1, 4, 13, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typinput"}, 24, -1, 4, 14, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typoutput"}, 24, -1, 4, 15, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typreceive"}, 24, -1, 4, 16, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typsend"}, 24, -1, 4, 17, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typmodin"}, 24, -1, 4, 18, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typmodout"}, 24, -1, 4, 19, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typanalyze"}, 24, -1, 4, 20, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typalign"}, 18, -1, 1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1247, {"typstorage"}, 18, -1, 1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1247, {"typnotnull"}, 16, -1, 1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1247, {"typbasetype"}, 26, -1, 4, 24, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typtypmod"}, 23, -1, 4, 25, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typndims"}, 23, -1, 4, 26, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typdefaultbin"}, 25, -1, -1, 27, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
-{ 1247, {"typdefault"}, 25, -1, -1, 28, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
-
-DATA(insert ( 1247 typname 19 -1 NAMEDATALEN 1 0 -1 -1 f p c t f f t 0));
-DATA(insert ( 1247 typnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typowner 26 -1 4 3 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typlen 21 -1 2 4 0 -1 -1 t p s t f f t 0));
-DATA(insert ( 1247 typbyval 16 -1 1 5 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1247 typtype 18 -1 1 6 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1247 typcategory 18 -1 1 7 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1247 typispreferred 16 -1 1 8 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1247 typisdefined 16 -1 1 9 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1247 typdelim 18 -1 1 10 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1247 typrelid 26 -1 4 11 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typelem 26 -1 4 12 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typarray 26 -1 4 13 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typinput 24 -1 4 14 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typoutput 24 -1 4 15 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typreceive 24 -1 4 16 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typsend 24 -1 4 17 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typmodin 24 -1 4 18 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typmodout 24 -1 4 19 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typanalyze 24 -1 4 20 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typalign 18 -1 1 21 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1247 typstorage 18 -1 1 22 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1247 typnotnull 16 -1 1 23 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1247 typbasetype 26 -1 4 24 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typtypmod 23 -1 4 25 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typndims 23 -1 4 26 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typdefaultbin 25 -1 -1 27 0 -1 -1 f x i f f f t 0));
-DATA(insert ( 1247 typdefault 25 -1 -1 28 0 -1 -1 f x i f f f t 0));
-DATA(insert ( 1247 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
-DATA(insert ( 1247 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
+{ 1247, {"typname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typnamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typowner"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typlen"}, 21, -1, 2, 4, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typbyval"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typtype"}, 18, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typcategory"}, 18, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typispreferred"},16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typisdefined"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typdelim"}, 18, -1, 1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typrelid"}, 26, -1, 4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typelem"}, 26, -1, 4, 12, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typarray"}, 26, -1, 4, 13, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typinput"}, 24, -1, 4, 14, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typoutput"}, 24, -1, 4, 15, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typreceive"}, 24, -1, 4, 16, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typsend"}, 24, -1, 4, 17, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typmodin"}, 24, -1, 4, 18, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typmodout"}, 24, -1, 4, 19, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typanalyze"}, 24, -1, 4, 20, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typalign"}, 18, -1, 1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typstorage"}, 18, -1, 1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typnotnull"}, 16, -1, 1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typbasetype"}, 26, -1, 4, 24, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typtypmod"}, 23, -1, 4, 25, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typndims"}, 23, -1, 4, 26, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1247, {"typdefaultbin"}, 25, -1, -1, 27, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
+{ 1247, {"typdefault"}, 25, -1, -1, 28, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
+
+DATA(insert ( 1247 typname 19 -1 NAMEDATALEN 1 0 -1 -1 f p c t f f t 0 _null_));
+DATA(insert ( 1247 typnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typowner 26 -1 4 3 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typlen 21 -1 2 4 0 -1 -1 t p s t f f t 0 _null_));
+DATA(insert ( 1247 typbyval 16 -1 1 5 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1247 typtype 18 -1 1 6 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1247 typcategory 18 -1 1 7 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1247 typispreferred 16 -1 1 8 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1247 typisdefined 16 -1 1 9 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1247 typdelim 18 -1 1 10 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1247 typrelid 26 -1 4 11 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typelem 26 -1 4 12 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typarray 26 -1 4 13 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typinput 24 -1 4 14 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typoutput 24 -1 4 15 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typreceive 24 -1 4 16 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typsend 24 -1 4 17 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typmodin 24 -1 4 18 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typmodout 24 -1 4 19 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typanalyze 24 -1 4 20 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typalign 18 -1 1 21 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1247 typstorage 18 -1 1 22 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1247 typnotnull 16 -1 1 23 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1247 typbasetype 26 -1 4 24 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typtypmod 23 -1 4 25 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typndims 23 -1 4 26 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 typdefaultbin 25 -1 -1 27 0 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1247 typdefault 25 -1 -1 28 0 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1247 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0 _null_));
+DATA(insert ( 1247 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_));
/* ----------------
* pg_proc
* ----------------
*/
#define Schema_pg_proc \
-{ 1255, {"proname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0 }, \
-{ 1255, {"pronamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1255, {"proowner"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1255, {"prolang"}, 26, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1255, {"procost"}, 700, -1, 4, 5, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', true, false, false, true, 0 }, \
-{ 1255, {"prorows"}, 700, -1, 4, 6, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', true, false, false, true, 0 }, \
-{ 1255, {"provariadic"}, 26, -1, 4, 7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1255, {"proisagg"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1255, {"proiswindow"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1255, {"prosecdef"}, 16, -1, 1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1255, {"proisstrict"}, 16, -1, 1, 11, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1255, {"proretset"}, 16, -1, 1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1255, {"provolatile"}, 18, -1, 1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1255, {"pronargs"}, 21, -1, 2, 14, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
-{ 1255, {"pronargdefaults"}, 21, -1, 2, 15, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
-{ 1255, {"prorettype"}, 26, -1, 4, 16, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1255, {"proargtypes"}, 30, -1, -1, 17, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
-{ 1255, {"proallargtypes"}, 1028, -1, -1, 18, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
-{ 1255, {"proargmodes"}, 1002, -1, -1, 19, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
-{ 1255, {"proargnames"}, 1009, -1, -1, 20, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
-{ 1255, {"proargdefaults"}, 25, -1, -1, 21, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
-{ 1255, {"prosrc"}, 25, -1, -1, 22, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
-{ 1255, {"probin"}, 17, -1, -1, 23, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
-{ 1255, {"proconfig"}, 1009, -1, -1, 24, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
-{ 1255, {"proacl"}, 1034, -1, -1, 25, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
-
-DATA(insert ( 1255 proname 19 -1 NAMEDATALEN 1 0 -1 -1 f p c t f f t 0));
-DATA(insert ( 1255 pronamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1255 proowner 26 -1 4 3 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1255 prolang 26 -1 4 4 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1255 procost 700 -1 4 5 0 -1 -1 FLOAT4PASSBYVAL p i t f f t 0));
-DATA(insert ( 1255 prorows 700 -1 4 6 0 -1 -1 FLOAT4PASSBYVAL p i t f f t 0));
-DATA(insert ( 1255 provariadic 26 -1 4 7 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1255 proisagg 16 -1 1 8 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1255 proiswindow 16 -1 1 9 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1255 prosecdef 16 -1 1 10 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1255 proisstrict 16 -1 1 11 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1255 proretset 16 -1 1 12 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1255 provolatile 18 -1 1 13 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1255 pronargs 21 -1 2 14 0 -1 -1 t p s t f f t 0));
-DATA(insert ( 1255 pronargdefaults 21 -1 2 15 0 -1 -1 t p s t f f t 0));
-DATA(insert ( 1255 prorettype 26 -1 4 16 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1255 proargtypes 30 -1 -1 17 1 -1 -1 f p i t f f t 0));
-DATA(insert ( 1255 proallargtypes 1028 -1 -1 18 1 -1 -1 f x i f f f t 0));
-DATA(insert ( 1255 proargmodes 1002 -1 -1 19 1 -1 -1 f x i f f f t 0));
-DATA(insert ( 1255 proargnames 1009 -1 -1 20 1 -1 -1 f x i f f f t 0));
-DATA(insert ( 1255 proargdefaults 25 -1 -1 21 0 -1 -1 f x i f f f t 0));
-DATA(insert ( 1255 prosrc 25 -1 -1 22 0 -1 -1 f x i f f f t 0));
-DATA(insert ( 1255 probin 17 -1 -1 23 0 -1 -1 f x i f f f t 0));
-DATA(insert ( 1255 proconfig 1009 -1 -1 24 1 -1 -1 f x i f f f t 0));
-DATA(insert ( 1255 proacl 1034 -1 -1 25 1 -1 -1 f x i f f f t 0));
-DATA(insert ( 1255 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
-DATA(insert ( 1255 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1255 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1255 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
+{ 1255, {"proname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"pronamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"proowner"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"prolang"}, 26, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"procost"}, 700, -1, 4, 5, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"prorows"}, 700, -1, 4, 6, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"provariadic"}, 26, -1, 4, 7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"proisagg"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"proiswindow"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"prosecdef"}, 16, -1, 1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"proisstrict"}, 16, -1, 1, 11, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"proretset"}, 16, -1, 1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"provolatile"}, 18, -1, 1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"pronargs"}, 21, -1, 2, 14, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"pronargdefaults"}, 21, -1, 2, 15, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"prorettype"}, 26, -1, 4, 16, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"proargtypes"}, 30, -1, -1, 17, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1255, {"proallargtypes"}, 1028, -1, -1, 18, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
+{ 1255, {"proargmodes"}, 1002, -1, -1, 19, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
+{ 1255, {"proargnames"}, 1009, -1, -1, 20, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
+{ 1255, {"proargdefaults"}, 25, -1, -1, 21, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
+{ 1255, {"prosrc"}, 25, -1, -1, 22, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
+{ 1255, {"probin"}, 17, -1, -1, 23, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
+{ 1255, {"proconfig"}, 1009, -1, -1, 24, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
+{ 1255, {"proacl"}, 1034, -1, -1, 25, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
+
+DATA(insert ( 1255 proname 19 -1 NAMEDATALEN 1 0 -1 -1 f p c t f f t 0 _null_));
+DATA(insert ( 1255 pronamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1255 proowner 26 -1 4 3 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1255 prolang 26 -1 4 4 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1255 procost 700 -1 4 5 0 -1 -1 FLOAT4PASSBYVAL p i t f f t 0 _null_));
+DATA(insert ( 1255 prorows 700 -1 4 6 0 -1 -1 FLOAT4PASSBYVAL p i t f f t 0 _null_));
+DATA(insert ( 1255 provariadic 26 -1 4 7 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1255 proisagg 16 -1 1 8 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1255 proiswindow 16 -1 1 9 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1255 prosecdef 16 -1 1 10 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1255 proisstrict 16 -1 1 11 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1255 proretset 16 -1 1 12 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1255 provolatile 18 -1 1 13 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1255 pronargs 21 -1 2 14 0 -1 -1 t p s t f f t 0 _null_));
+DATA(insert ( 1255 pronargdefaults 21 -1 2 15 0 -1 -1 t p s t f f t 0 _null_));
+DATA(insert ( 1255 prorettype 26 -1 4 16 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1255 proargtypes 30 -1 -1 17 1 -1 -1 f p i t f f t 0 _null_));
+DATA(insert ( 1255 proallargtypes 1028 -1 -1 18 1 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1255 proargmodes 1002 -1 -1 19 1 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1255 proargnames 1009 -1 -1 20 1 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1255 proargdefaults 25 -1 -1 21 0 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1255 prosrc 25 -1 -1 22 0 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1255 probin 17 -1 -1 23 0 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1255 proconfig 1009 -1 -1 24 1 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1255 proacl 1034 -1 -1 25 1 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1255 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0 _null_));
+DATA(insert ( 1255 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1255 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1255 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_));
/* ----------------
* pg_attribute
* ----------------
*/
#define Schema_pg_attribute \
-{ 1249, {"attrelid"}, 26, -1, 4, 1, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1249, {"attname"}, 19, -1, NAMEDATALEN, 2, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0 }, \
-{ 1249, {"atttypid"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1249, {"attstattarget"}, 23, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1249, {"attlen"}, 21, -1, 2, 5, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
-{ 1249, {"attnum"}, 21, -1, 2, 6, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
-{ 1249, {"attndims"}, 23, -1, 4, 7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1249, {"attcacheoff"}, 23, -1, 4, 8, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1249, {"atttypmod"}, 23, -1, 4, 9, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1249, {"attbyval"}, 16, -1, 1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1249, {"attstorage"}, 18, -1, 1, 11, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1249, {"attalign"}, 18, -1, 1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1249, {"attnotnull"}, 16, -1, 1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1249, {"atthasdef"}, 16, -1, 1, 14, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1249, {"attisdropped"}, 16, -1, 1, 15, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1249, {"attislocal"}, 16, -1, 1, 16, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1249, {"attinhcount"}, 23, -1, 4, 17, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }
-
-DATA(insert ( 1249 attrelid 26 -1 4 1 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1249 attname 19 -1 NAMEDATALEN 2 0 -1 -1 f p c t f f t 0));
-DATA(insert ( 1249 atttypid 26 -1 4 3 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1249 attstattarget 23 -1 4 4 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1249 attlen 21 -1 2 5 0 -1 -1 t p s t f f t 0));
-DATA(insert ( 1249 attnum 21 -1 2 6 0 -1 -1 t p s t f f t 0));
-DATA(insert ( 1249 attndims 23 -1 4 7 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1249 attcacheoff 23 -1 4 8 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1249 atttypmod 23 -1 4 9 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1249 attbyval 16 -1 1 10 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1249 attstorage 18 -1 1 11 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1249 attalign 18 -1 1 12 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1249 attnotnull 16 -1 1 13 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1249 atthasdef 16 -1 1 14 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1249 attisdropped 16 -1 1 15 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1249 attislocal 16 -1 1 16 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1249 attinhcount 23 -1 4 17 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1249 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
+{ 1249, {"attrelid"}, 26, -1, 4, 1, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"attname"}, 19, -1, NAMEDATALEN, 2, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"atttypid"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"attstattarget"}, 23, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"attlen"}, 21, -1, 2, 5, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"attnum"}, 21, -1, 2, 6, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"attndims"}, 23, -1, 4, 7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"attcacheoff"}, 23, -1, 4, 8, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"atttypmod"}, 23, -1, 4, 9, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"attbyval"}, 16, -1, 1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"attstorage"}, 18, -1, 1, 11, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"attalign"}, 18, -1, 1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"attnotnull"}, 16, -1, 1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"atthasdef"}, 16, -1, 1, 14, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"attisdropped"}, 16, -1, 1, 15, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"attislocal"}, 16, -1, 1, 16, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"attinhcount"}, 23, -1, 4, 17, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1249, {"attacl"}, 1034, -1, -1, 18, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
+
+DATA(insert ( 1249 attrelid 26 -1 4 1 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1249 attname 19 -1 NAMEDATALEN 2 0 -1 -1 f p c t f f t 0 _null_));
+DATA(insert ( 1249 atttypid 26 -1 4 3 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1249 attstattarget 23 -1 4 4 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1249 attlen 21 -1 2 5 0 -1 -1 t p s t f f t 0 _null_));
+DATA(insert ( 1249 attnum 21 -1 2 6 0 -1 -1 t p s t f f t 0 _null_));
+DATA(insert ( 1249 attndims 23 -1 4 7 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1249 attcacheoff 23 -1 4 8 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1249 atttypmod 23 -1 4 9 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1249 attbyval 16 -1 1 10 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1249 attstorage 18 -1 1 11 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1249 attalign 18 -1 1 12 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1249 attnotnull 16 -1 1 13 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1249 atthasdef 16 -1 1 14 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1249 attisdropped 16 -1 1 15 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1249 attislocal 16 -1 1 16 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1249 attinhcount 23 -1 4 17 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1249 attacl 1034 -1 -1 18 1 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1249 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0 _null_));
/* no OIDs in pg_attribute */
-DATA(insert ( 1249 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1249 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1249 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_));
/* ----------------
* pg_class
* ----------------
*/
#define Schema_pg_class \
-{ 1259, {"relname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relnamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltype"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"relowner"}, 26, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"relam"}, 26, -1, 4, 5, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"relfilenode"}, 26, -1, 4, 6, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltablespace"}, 26, -1, 4, 7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"relpages"}, 23, -1, 4, 8, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltuples"}, 700, -1, 4, 9, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltoastrelid"}, 26, -1, 4, 10, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltoastidxid"}, 26, -1, 4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"relhasindex"}, 16, -1, 1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relisshared"}, 16, -1, 1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relkind"}, 18, -1, 1, 14, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relnatts"}, 21, -1, 2, 15, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
-{ 1259, {"relchecks"}, 21, -1, 2, 16, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
-{ 1259, {"relhasoids"}, 16, -1, 1, 17, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relhaspkey"}, 16, -1, 1, 18, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relhasrules"}, 16, -1, 1, 19, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relhastriggers"},16, -1, 1, 20, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relhassubclass"},16, -1, 1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relfrozenxid"}, 28, -1, 4, 22, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"relacl"}, 1034, -1, -1, 23, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
-{ 1259, {"reloptions"}, 1009, -1, -1, 24, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
-
-DATA(insert ( 1259 relname 19 -1 NAMEDATALEN 1 0 -1 -1 f p c t f f t 0));
-DATA(insert ( 1259 relnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 reltype 26 -1 4 3 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 relowner 26 -1 4 4 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 relam 26 -1 4 5 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 relfilenode 26 -1 4 6 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 reltablespace 26 -1 4 7 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 relpages 23 -1 4 8 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 reltuples 700 -1 4 9 0 -1 -1 FLOAT4PASSBYVAL p i t f f t 0));
-DATA(insert ( 1259 reltoastrelid 26 -1 4 10 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 reltoastidxid 26 -1 4 11 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 relhasindex 16 -1 1 12 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relisshared 16 -1 1 13 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relkind 18 -1 1 14 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relnatts 21 -1 2 15 0 -1 -1 t p s t f f t 0));
-DATA(insert ( 1259 relchecks 21 -1 2 16 0 -1 -1 t p s t f f t 0));
-DATA(insert ( 1259 relhasoids 16 -1 1 17 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relhaspkey 16 -1 1 18 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relhasrules 16 -1 1 19 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relhastriggers 16 -1 1 20 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relhassubclass 16 -1 1 21 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relfrozenxid 28 -1 4 22 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 relacl 1034 -1 -1 23 1 -1 -1 f x i f f f t 0));
-DATA(insert ( 1259 reloptions 1009 -1 -1 24 1 -1 -1 f x i f f f t 0));
-DATA(insert ( 1259 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
-DATA(insert ( 1259 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
+{ 1259, {"relname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relnamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"reltype"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relowner"}, 26, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relam"}, 26, -1, 4, 5, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relfilenode"}, 26, -1, 4, 6, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"reltablespace"}, 26, -1, 4, 7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relpages"}, 23, -1, 4, 8, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"reltuples"}, 700, -1, 4, 9, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"reltoastrelid"}, 26, -1, 4, 10, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"reltoastidxid"}, 26, -1, 4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relhasindex"}, 16, -1, 1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relisshared"}, 16, -1, 1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relkind"}, 18, -1, 1, 14, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relnatts"}, 21, -1, 2, 15, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relchecks"}, 21, -1, 2, 16, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relhasoids"}, 16, -1, 1, 17, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relhaspkey"}, 16, -1, 1, 18, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relhasrules"}, 16, -1, 1, 19, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relhastriggers"},16, -1, 1, 20, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relhassubclass"},16, -1, 1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relfrozenxid"}, 28, -1, 4, 22, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relacl"}, 1034, -1, -1, 23, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
+{ 1259, {"reloptions"}, 1009, -1, -1, 24, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
+
+DATA(insert ( 1259 relname 19 -1 NAMEDATALEN 1 0 -1 -1 f p c t f f t 0 _null_));
+DATA(insert ( 1259 relnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 reltype 26 -1 4 3 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 relowner 26 -1 4 4 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 relam 26 -1 4 5 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 relfilenode 26 -1 4 6 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 reltablespace 26 -1 4 7 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 relpages 23 -1 4 8 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 reltuples 700 -1 4 9 0 -1 -1 FLOAT4PASSBYVAL p i t f f t 0 _null_));
+DATA(insert ( 1259 reltoastrelid 26 -1 4 10 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 reltoastidxid 26 -1 4 11 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 relhasindex 16 -1 1 12 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1259 relisshared 16 -1 1 13 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1259 relkind 18 -1 1 14 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1259 relnatts 21 -1 2 15 0 -1 -1 t p s t f f t 0 _null_));
+DATA(insert ( 1259 relchecks 21 -1 2 16 0 -1 -1 t p s t f f t 0 _null_));
+DATA(insert ( 1259 relhasoids 16 -1 1 17 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1259 relhaspkey 16 -1 1 18 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1259 relhasrules 16 -1 1 19 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1259 relhastriggers 16 -1 1 20 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1259 relhassubclass 16 -1 1 21 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1259 relfrozenxid 28 -1 4 22 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 relacl 1034 -1 -1 23 1 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1259 reloptions 1009 -1 -1 24 1 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1259 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0 _null_));
+DATA(insert ( 1259 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_));
/* ----------------
* pg_index
* ----------------
*/
#define Schema_pg_index \
-{ 0, {"indexrelid"}, 26, -1, 4, 1, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 0, {"indrelid"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 0, {"indnatts"}, 21, -1, 2, 3, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
-{ 0, {"indisunique"}, 16, -1, 1, 4, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 0, {"indisprimary"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 0, {"indisclustered"}, 16, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 0, {"indisvalid"}, 16, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 0, {"indcheckxmin"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 0, {"indisready"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 0, {"indkey"}, 22, -1, -1, 10, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
-{ 0, {"indclass"}, 30, -1, -1, 11, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
-{ 0, {"indoption"}, 22, -1, -1, 12, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
-{ 0, {"indexprs"}, 25, -1, -1, 13, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
-{ 0, {"indpred"}, 25, -1, -1, 14, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
+{ 0, {"indexrelid"}, 26, -1, 4, 1, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 0, {"indrelid"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 0, {"indnatts"}, 21, -1, 2, 3, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
+{ 0, {"indisunique"}, 16, -1, 1, 4, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 0, {"indisprimary"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 0, {"indisclustered"}, 16, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 0, {"indisvalid"}, 16, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 0, {"indcheckxmin"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 0, {"indisready"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 0, {"indkey"}, 22, -1, -1, 10, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 0, {"indclass"}, 30, -1, -1, 11, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 0, {"indoption"}, 22, -1, -1, 12, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 0, {"indexprs"}, 25, -1, -1, 13, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
+{ 0, {"indpred"}, 25, -1, -1, 14, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
#endif /* PG_ATTRIBUTE_H */
* 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_class.h,v 1.111 2009/01/01 17:23:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.112 2009/01/22 20:16:09 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 28 0 t f f f f 3 _null_ _null_ ));
DESCR("");
-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 17 0 f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 18 0 f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 25 0 t f f f f 3 _null_ _null_ ));
DESCR("");
* 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_shdepend.h,v 1.8 2009/01/01 17:23:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_shdepend.h,v 1.9 2009/01/22 20:16:09 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
Oid dbid; /* OID of database containing object */
Oid classid; /* OID of table containing object */
Oid objid; /* OID of object itself */
+ int4 objsubid; /* column number, or 0 if not used */
/*
* Identification of the independent (referenced) object. This is always
- * a shared object, so we need no database ID field.
+ * a shared object, so we need no database ID field. We don't bother
+ * with a sub-object ID either.
*/
Oid refclassid; /* OID of table containing object */
Oid refobjid; /* OID of object itself */
* compiler constants for pg_shdepend
* ----------------
*/
-#define Natts_pg_shdepend 6
+#define Natts_pg_shdepend 7
#define Anum_pg_shdepend_dbid 1
#define Anum_pg_shdepend_classid 2
#define Anum_pg_shdepend_objid 3
-#define Anum_pg_shdepend_refclassid 4
-#define Anum_pg_shdepend_refobjid 5
-#define Anum_pg_shdepend_deptype 6
+#define Anum_pg_shdepend_objsubid 4
+#define Anum_pg_shdepend_refclassid 5
+#define Anum_pg_shdepend_refobjid 6
+#define Anum_pg_shdepend_deptype 7
/*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.71 2009/01/22 19:16:31 heikki Exp $
+ * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.72 2009/01/22 20:16:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define TRIGGER_FIRES_ON_REPLICA 'R'
#define TRIGGER_DISABLED 'D'
-extern Oid CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid);
+extern Oid CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid,
+ bool checkPermissions);
extern void DropTrigger(Oid relid, const char *trigname,
DropBehavior behavior, bool missing_ok);
* 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.218 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.219 2009/01/22 20:16:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
T_FkConstraint,
T_PrivGrantee,
T_FuncWithArgs,
- T_PrivTarget,
+ T_AccessPriv,
T_CreateOpClassItem,
T_InhRelation,
T_FunctionParameter,
* 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.388 2009/01/16 13:27:24 heikki Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.389 2009/01/22 20:16:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef PARSENODES_H
#define PARSENODES_H
+#include "nodes/bitmapset.h"
#include "nodes/primnodes.h"
#include "nodes/value.h"
* then do the permissions checks using the access rights of that user,
* not the current effective user ID. (This allows rules to act as
* setuid gateways.)
+ *
+ * For SELECT/INSERT/UPDATE permissions, if the user doesn't have
+ * table-wide permissions then it is sufficient to have the permissions
+ * on all columns identified in selectedCols (for SELECT) and/or
+ * modifiedCols (for INSERT/UPDATE; we can tell which from the query type).
+ * selectedCols and modifiedCols are bitmapsets, which cannot have negative
+ * integer members, so we subtract FirstLowInvalidHeapAttributeNumber from
+ * column numbers before storing them in these fields. A whole-row Var
+ * reference is represented by setting the bit for InvalidAttrNumber.
*--------------------
*/
typedef enum RTEKind
/*
* XXX the fields applicable to only some rte kinds should be merged into
* a union. I didn't do this yet because the diffs would impact a lot of
- * code that is being actively worked on. FIXME later.
+ * code that is being actively worked on. FIXME someday.
*/
/*
bool inFromCl; /* present in FROM clause? */
AclMode requiredPerms; /* bitmask of required access permissions */
Oid checkAsUser; /* if valid, check access as this role */
+ Bitmapset *selectedCols; /* columns needing SELECT permission */
+ Bitmapset *modifiedCols; /* columns needing INSERT/UPDATE permission */
} RangeTblEntry;
/*
*/
typedef enum GrantObjectType
{
+ ACL_OBJECT_COLUMN, /* column */
ACL_OBJECT_RELATION, /* table, view */
ACL_OBJECT_SEQUENCE, /* sequence */
ACL_OBJECT_DATABASE, /* database */
GrantObjectType objtype; /* kind of object being operated on */
List *objects; /* list of RangeVar nodes, FuncWithArgs nodes,
* or plain names (as Value strings) */
- List *privileges; /* list of privilege names (as Strings) */
- /* privileges == NIL denotes "all privileges" */
+ List *privileges; /* list of AccessPriv nodes */
+ /* privileges == NIL denotes ALL PRIVILEGES */
List *grantees; /* list of PrivGrantee nodes */
bool grant_option; /* grant or revoke grant option */
DropBehavior behavior; /* drop behavior (for REVOKE) */
List *funcargs; /* list of Typename nodes */
} FuncWithArgs;
-/* This is only used internally in gram.y. */
-typedef struct PrivTarget
+/*
+ * An access privilege, with optional list of column names
+ * priv_name == NULL denotes ALL PRIVILEGES (only used with a column list)
+ * cols == NIL denotes "all columns"
+ * Note that simple "ALL PRIVILEGES" is represented as a NIL list, not
+ * an AccessPriv with both fields null.
+ */
+typedef struct AccessPriv
{
NodeTag type;
- GrantObjectType objtype;
- List *objs;
-} PrivTarget;
+ char *priv_name; /* string name of privilege */
+ List *cols; /* list of Value strings */
+} AccessPriv;
/* ----------------------
* Grant/Revoke Role Statement
*
- * Note: the lists of roles are lists of names, as Value strings
+ * Note: because of the parsing ambiguity with the GRANT <privileges>
+ * statement, granted_roles is a list of AccessPriv; the execution code
+ * should complain if any column lists appear. grantee_roles is a list
+ * of role names, as Value strings.
* ----------------------
*/
typedef struct GrantRoleStmt
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.60 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.61 2009/01/22 20:16:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Note that neither relname nor refname of these entries are necessarily
* unique; searching the rtable by name is a bad idea.
*
+ * p_joinexprs: list of JoinExpr nodes associated with p_rtable entries.
+ * This is one-for-one with p_rtable, but contains NULLs for non-join
+ * RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins.
+ *
* p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that
* will become the fromlist of the query's top-level FromExpr node.
*
struct ParseState *parentParseState; /* stack link */
const char *p_sourcetext; /* source text, or NULL if not available */
List *p_rtable; /* range table so far */
+ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */
List *p_joinlist; /* join items so far (will become FromExpr
* node's fromlist) */
List *p_relnamespace; /* current namespace for relations */
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.62 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.63 2009/01/22 20:16:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
char *colname,
bool implicitRTEOK,
int location);
+extern void markVarForSelectPriv(ParseState *pstate, Var *var,
+ RangeTblEntry *rte);
extern Relation parserOpenTable(ParseState *pstate, const RangeVar *relation,
int lockmode);
extern RangeTblEntry *addRangeTableEntry(ParseState *pstate,
* 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.106 2009/01/01 17:24:02 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.107 2009/01/22 20:16:09 tgl Exp $
*
* NOTES
* An ACL array is simply an array of AclItems, representing the union
/*
* Bitmasks defining "all rights" for each supported object type
*/
+#define ACL_ALL_RIGHTS_COLUMN (ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_REFERENCES)
#define ACL_ALL_RIGHTS_RELATION (ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_DELETE|ACL_TRUNCATE|ACL_REFERENCES|ACL_TRIGGER)
#define ACL_ALL_RIGHTS_SEQUENCE (ACL_USAGE|ACL_SELECT|ACL_UPDATE)
#define ACL_ALL_RIGHTS_DATABASE (ACL_CREATE|ACL_CREATE_TEMP|ACL_CONNECT)
/* currently it's only used to tell aclcheck_error what to say */
typedef enum AclObjectKind
{
+ ACL_KIND_COLUMN, /* pg_attribute */
ACL_KIND_CLASS, /* pg_class */
ACL_KIND_SEQUENCE, /* pg_sequence */
ACL_KIND_DATABASE, /* pg_database */
* 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, it will be internally turned into the right kind of
+ * 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
{
List *objects;
bool all_privs;
AclMode privileges;
+ List *col_privs;
List *grantees;
bool grant_option;
DropBehavior behavior;
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 *aclcopy(const Acl *orig_acl);
+extern Acl *aclconcat(const Acl *left_acl, const Acl *right_acl);
extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId,
AclMode mask, AclMaskHow how);
extern void ExecuteGrantStmt(GrantStmt *stmt);
extern void ExecGrantStmt_oids(InternalGrant *istmt);
+extern AclMode pg_attribute_aclmask(Oid table_oid, AttrNumber attnum,
+ Oid roleid, AclMode mask, AclMaskHow how);
extern AclMode pg_class_aclmask(Oid table_oid, Oid roleid,
AclMode mask, AclMaskHow how);
extern AclMode pg_database_aclmask(Oid db_oid, Oid roleid,
extern AclMode pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+extern AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
+ Oid roleid, AclMode mode);
+extern AclResult pg_attribute_aclcheck_all(Oid table_oid, Oid roleid,
+ AclMode mode, AclMaskHow how);
extern AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode);
extern AclResult pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode);
extern AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode);
extern void aclcheck_error(AclResult aclerr, AclObjectKind objectkind,
const char *objectname);
+extern void aclcheck_error_col(AclResult aclerr, AclObjectKind objectkind,
+ const char *objectname, const char *colname);
+
/* ownercheck routines just return true (owner) or false (not) */
extern bool pg_class_ownercheck(Oid class_oid, Oid roleid);
extern bool pg_type_ownercheck(Oid type_oid, Oid roleid);
GRANT ALL ON deptest1 TO regression_user2;
RESET SESSION AUTHORIZATION;
\z deptest1
- Access privileges
- Schema | Name | Type | Access privileges
---------+----------+-------+--------------------------------------------------
- public | deptest1 | table | regression_user0=arwdDxt/regression_user0
- : regression_user1=a*r*w*d*D*x*t*/regression_user0
- : regression_user2=arwdDxt/regression_user1
+ Access privileges
+ Schema | Name | Type | Access privileges | Column access privileges
+--------+----------+-------+--------------------------------------------------+--------------------------
+ public | deptest1 | table | regression_user0=arwdDxt/regression_user0 |
+ : regression_user1=a*r*w*d*D*x*t*/regression_user0
+ : regression_user2=arwdDxt/regression_user1
(1 row)
DROP OWNED BY regression_user1;
-- all grants revoked
\z deptest1
- Access privileges
- Schema | Name | Type | Access privileges
---------+----------+-------+-------------------------------------------
- public | deptest1 | table | regression_user0=arwdDxt/regression_user0
+ Access privileges
+ Schema | Name | Type | Access privileges | Column access privileges
+--------+----------+-------+-------------------------------------------+--------------------------
+ public | deptest1 | table | regression_user0=arwdDxt/regression_user0 |
(1 row)
-- table was dropped
SELECT * FROM atestv2; -- fail (even though regressuser2 can access underlying atest2)
ERROR: permission denied for relation atest2
+-- Test column level permissions
+SET SESSION AUTHORIZATION regressuser1;
+CREATE TABLE atest5 (one int, two int, three int);
+CREATE TABLE atest6 (one int, two int, blue int);
+GRANT SELECT (one), INSERT (two), UPDATE (three) ON atest5 TO regressuser4;
+GRANT ALL (one) ON atest5 TO regressuser3;
+INSERT INTO atest5 VALUES (1,2,3);
+SET SESSION AUTHORIZATION regressuser4;
+SELECT * FROM atest5; -- fail
+ERROR: permission denied for relation atest5
+SELECT one FROM atest5; -- ok
+ one
+-----
+ 1
+(1 row)
+
+SELECT two FROM atest5; -- fail
+ERROR: permission denied for relation atest5
+SELECT atest5 FROM atest5; -- fail
+ERROR: permission denied for relation atest5
+SELECT 1 FROM atest5; -- ok
+ ?column?
+----------
+ 1
+(1 row)
+
+SELECT 1 FROM atest5 a JOIN atest5 b USING (one); -- ok
+ ?column?
+----------
+ 1
+(1 row)
+
+SELECT 1 FROM atest5 a JOIN atest5 b USING (two); -- fail
+ERROR: permission denied for relation atest5
+SELECT 1 FROM atest5 a NATURAL JOIN atest5 b; -- fail
+ERROR: permission denied for relation atest5
+SELECT (j.*) IS NULL FROM (atest5 a JOIN atest5 b USING (one)) j; -- fail
+ERROR: permission denied for relation atest5
+SELECT 1 FROM atest5 WHERE two = 2; -- fail
+ERROR: permission denied for relation atest5
+SELECT * FROM atest1, atest5; -- fail
+ERROR: permission denied for relation atest5
+SELECT atest1.* FROM atest1, atest5; -- ok
+ a | b
+---+-----
+ 1 | two
+ 1 | two
+(2 rows)
+
+SELECT atest1.*,atest5.one FROM atest1, atest5; -- ok
+ a | b | one
+---+-----+-----
+ 1 | two | 1
+ 1 | two | 1
+(2 rows)
+
+SELECT atest1.*,atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.two); -- fail
+ERROR: permission denied for relation atest5
+SELECT atest1.*,atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.one); -- ok
+ a | b | one
+---+-----+-----
+ 1 | two | 1
+ 1 | two | 1
+(2 rows)
+
+SELECT one, two FROM atest5; -- fail
+ERROR: permission denied for relation atest5
+SET SESSION AUTHORIZATION regressuser1;
+GRANT SELECT (one,two) ON atest6 TO regressuser4;
+SET SESSION AUTHORIZATION regressuser4;
+SELECT one, two FROM atest5 NATURAL JOIN atest6; -- fail still
+ERROR: permission denied for relation atest5
+SET SESSION AUTHORIZATION regressuser1;
+GRANT SELECT (two) ON atest5 TO regressuser4;
+SET SESSION AUTHORIZATION regressuser4;
+SELECT one, two FROM atest5 NATURAL JOIN atest6; -- ok now
+ one | two
+-----+-----
+(0 rows)
+
+-- test column-level privileges for INSERT and UPDATE
+INSERT INTO atest5 (two) VALUES (3); -- ok
+INSERT INTO atest5 (three) VALUES (4); -- fail
+ERROR: permission denied for relation atest5
+INSERT INTO atest5 VALUES (5,5,5); -- fail
+ERROR: permission denied for relation atest5
+UPDATE atest5 SET three = 10; -- ok
+UPDATE atest5 SET one = 8; -- fail
+ERROR: permission denied for relation atest5
+UPDATE atest5 SET three = 5, one = 2; -- fail
+ERROR: permission denied for relation atest5
+SET SESSION AUTHORIZATION regressuser1;
+REVOKE ALL (one) ON atest5 FROM regressuser4;
+GRANT SELECT (one,two,blue) ON atest6 TO regressuser4;
+SET SESSION AUTHORIZATION regressuser4;
+SELECT one FROM atest5; -- fail
+ERROR: permission denied for relation atest5
+UPDATE atest5 SET one = 1; -- fail
+ERROR: permission denied for relation atest5
+SELECT atest6 FROM atest6; -- ok
+ atest6
+--------
+(0 rows)
+
+-- test column-level privileges when involved with DELETE
+SET SESSION AUTHORIZATION regressuser1;
+ALTER TABLE atest6 ADD COLUMN three integer;
+GRANT DELETE ON atest5 TO regressuser3;
+GRANT SELECT (two) ON atest5 TO regressuser3;
+REVOKE ALL (one) ON atest5 FROM regressuser3;
+GRANT SELECT (one) ON atest5 TO regressuser4;
+SET SESSION AUTHORIZATION regressuser4;
+SELECT atest6 FROM atest6; -- fail
+ERROR: permission denied for relation atest6
+SELECT one FROM atest5 NATURAL JOIN atest6; -- fail
+ERROR: permission denied for relation atest5
+SET SESSION AUTHORIZATION regressuser1;
+ALTER TABLE atest6 DROP COLUMN three;
+SET SESSION AUTHORIZATION regressuser4;
+SELECT atest6 FROM atest6; -- ok
+ atest6
+--------
+(0 rows)
+
+SELECT one FROM atest5 NATURAL JOIN atest6; -- ok
+ one
+-----
+(0 rows)
+
+SET SESSION AUTHORIZATION regressuser1;
+ALTER TABLE atest6 DROP COLUMN two;
+REVOKE SELECT (one,blue) ON atest6 FROM regressuser4;
+SET SESSION AUTHORIZATION regressuser4;
+SELECT * FROM atest6; -- fail
+ERROR: permission denied for relation atest6
+SELECT 1 FROM atest6; -- fail
+ERROR: permission denied for relation atest6
+SET SESSION AUTHORIZATION regressuser3;
+DELETE FROM atest5 WHERE one = 1; -- fail
+ERROR: permission denied for relation atest5
+DELETE FROM atest5 WHERE two = 2; -- ok
-- privileges on functions, languages
-- switch to superuser
\c -
DROP TABLE atest2;
DROP TABLE atest3;
DROP TABLE atest4;
+DROP TABLE atest5;
+DROP TABLE atest6;
DROP GROUP regressgroup1;
DROP GROUP regressgroup2;
REVOKE USAGE ON LANGUAGE sql FROM regressuser1;
SELECT * FROM atest2; -- ok
SELECT * FROM atestv2; -- fail (even though regressuser2 can access underlying atest2)
+-- Test column level permissions
+
+SET SESSION AUTHORIZATION regressuser1;
+CREATE TABLE atest5 (one int, two int, three int);
+CREATE TABLE atest6 (one int, two int, blue int);
+GRANT SELECT (one), INSERT (two), UPDATE (three) ON atest5 TO regressuser4;
+GRANT ALL (one) ON atest5 TO regressuser3;
+
+INSERT INTO atest5 VALUES (1,2,3);
+
+SET SESSION AUTHORIZATION regressuser4;
+SELECT * FROM atest5; -- fail
+SELECT one FROM atest5; -- ok
+SELECT two FROM atest5; -- fail
+SELECT atest5 FROM atest5; -- fail
+SELECT 1 FROM atest5; -- ok
+SELECT 1 FROM atest5 a JOIN atest5 b USING (one); -- ok
+SELECT 1 FROM atest5 a JOIN atest5 b USING (two); -- fail
+SELECT 1 FROM atest5 a NATURAL JOIN atest5 b; -- fail
+SELECT (j.*) IS NULL FROM (atest5 a JOIN atest5 b USING (one)) j; -- fail
+SELECT 1 FROM atest5 WHERE two = 2; -- fail
+SELECT * FROM atest1, atest5; -- fail
+SELECT atest1.* FROM atest1, atest5; -- ok
+SELECT atest1.*,atest5.one FROM atest1, atest5; -- ok
+SELECT atest1.*,atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.two); -- fail
+SELECT atest1.*,atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.one); -- ok
+SELECT one, two FROM atest5; -- fail
+
+SET SESSION AUTHORIZATION regressuser1;
+GRANT SELECT (one,two) ON atest6 TO regressuser4;
+
+SET SESSION AUTHORIZATION regressuser4;
+SELECT one, two FROM atest5 NATURAL JOIN atest6; -- fail still
+
+SET SESSION AUTHORIZATION regressuser1;
+GRANT SELECT (two) ON atest5 TO regressuser4;
+
+SET SESSION AUTHORIZATION regressuser4;
+SELECT one, two FROM atest5 NATURAL JOIN atest6; -- ok now
+
+-- test column-level privileges for INSERT and UPDATE
+INSERT INTO atest5 (two) VALUES (3); -- ok
+INSERT INTO atest5 (three) VALUES (4); -- fail
+INSERT INTO atest5 VALUES (5,5,5); -- fail
+UPDATE atest5 SET three = 10; -- ok
+UPDATE atest5 SET one = 8; -- fail
+UPDATE atest5 SET three = 5, one = 2; -- fail
+
+SET SESSION AUTHORIZATION regressuser1;
+REVOKE ALL (one) ON atest5 FROM regressuser4;
+GRANT SELECT (one,two,blue) ON atest6 TO regressuser4;
+
+SET SESSION AUTHORIZATION regressuser4;
+SELECT one FROM atest5; -- fail
+UPDATE atest5 SET one = 1; -- fail
+SELECT atest6 FROM atest6; -- ok
+
+-- test column-level privileges when involved with DELETE
+SET SESSION AUTHORIZATION regressuser1;
+ALTER TABLE atest6 ADD COLUMN three integer;
+GRANT DELETE ON atest5 TO regressuser3;
+GRANT SELECT (two) ON atest5 TO regressuser3;
+REVOKE ALL (one) ON atest5 FROM regressuser3;
+GRANT SELECT (one) ON atest5 TO regressuser4;
+
+SET SESSION AUTHORIZATION regressuser4;
+SELECT atest6 FROM atest6; -- fail
+SELECT one FROM atest5 NATURAL JOIN atest6; -- fail
+
+SET SESSION AUTHORIZATION regressuser1;
+ALTER TABLE atest6 DROP COLUMN three;
+
+SET SESSION AUTHORIZATION regressuser4;
+SELECT atest6 FROM atest6; -- ok
+SELECT one FROM atest5 NATURAL JOIN atest6; -- ok
+
+SET SESSION AUTHORIZATION regressuser1;
+ALTER TABLE atest6 DROP COLUMN two;
+REVOKE SELECT (one,blue) ON atest6 FROM regressuser4;
+
+SET SESSION AUTHORIZATION regressuser4;
+SELECT * FROM atest6; -- fail
+SELECT 1 FROM atest6; -- fail
+
+SET SESSION AUTHORIZATION regressuser3;
+DELETE FROM atest5 WHERE one = 1; -- fail
+DELETE FROM atest5 WHERE two = 2; -- ok
-- privileges on functions, languages
DROP TABLE atest2;
DROP TABLE atest3;
DROP TABLE atest4;
+DROP TABLE atest5;
+DROP TABLE atest6;
DROP GROUP regressgroup1;
DROP GROUP regressgroup2;