diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index bd267dcb758b73a4b5dc9406359e24102051f609..4fae50cd326683f60e533759a76c5584249bce2d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,4 +1,4 @@ -<!-- $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 --> @@ -1028,6 +1028,16 @@ </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> @@ -4250,6 +4260,17 @@ <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> diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml index dafb8ffb523885fda8d99b1804a72d4380a8a10f..ceda72c141d285c3151a8c9185311e58a52a1f3f 100644 --- a/doc/src/sgml/ref/grant.sgml +++ b/doc/src/sgml/ref/grant.sgml @@ -1,5 +1,5 @@ <!-- -$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 --> @@ -26,6 +26,11 @@ GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER } 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> [, ...] @@ -68,7 +73,7 @@ GRANT <replaceable class="PARAMETER">role</replaceable> [, ...] TO <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 @@ -125,7 +130,8 @@ GRANT <replaceable class="PARAMETER">role</replaceable> [, ...] TO <replaceable <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 @@ -145,7 +151,8 @@ GRANT <replaceable class="PARAMETER">role</replaceable> [, ...] TO <replaceable <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 @@ -162,7 +169,9 @@ GRANT <replaceable class="PARAMETER">role</replaceable> [, ...] TO <replaceable <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> @@ -173,14 +182,14 @@ GRANT <replaceable class="PARAMETER">role</replaceable> [, ...] TO <replaceable <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. @@ -217,7 +226,8 @@ GRANT <replaceable class="PARAMETER">role</replaceable> [, ...] TO <replaceable <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> @@ -373,6 +383,14 @@ GRANT <replaceable class="PARAMETER">role</replaceable> [, ...] TO <replaceable 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 @@ -428,33 +446,27 @@ GRANT <replaceable class="PARAMETER">role</replaceable> [, ...] TO <replaceable </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 @@ -471,7 +483,7 @@ GRANT <replaceable class="PARAMETER">role</replaceable> [, ...] TO <replaceable 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 @@ -483,9 +495,15 @@ GRANT <replaceable class="PARAMETER">role</replaceable> [, ...] TO <replaceable <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 @@ -495,7 +513,8 @@ GRANT SELECT, UPDATE, INSERT ON mytable TO admin; <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> @@ -562,11 +581,6 @@ GRANT admins TO joe; <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, diff --git a/doc/src/sgml/ref/insert.sgml b/doc/src/sgml/ref/insert.sgml index 98a1ca28b7923e0452b79a4c49c3423b5bffe764..a2a52d8ba429c3d82f5d943cf5f76faa91c4c46d 100644 --- a/doc/src/sgml/ref/insert.sgml +++ b/doc/src/sgml/ref/insert.sgml @@ -1,5 +1,5 @@ <!-- -$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 --> @@ -69,11 +69,14 @@ INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable <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> diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml index c8e91e0a159aa2a14d94508418ea418032be7dd9..0f967770c1158bb811d8b2f15af45d7371f4df8b 100644 --- a/doc/src/sgml/ref/revoke.sgml +++ b/doc/src/sgml/ref/revoke.sgml @@ -1,5 +1,5 @@ <!-- -$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 --> @@ -28,6 +28,13 @@ REVOKE [ GRANT OPTION FOR ] 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 ] } @@ -131,6 +138,11 @@ REVOKE [ ADMIN OPTION FOR ] 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. @@ -143,9 +155,11 @@ REVOKE [ ADMIN OPTION FOR ] <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> diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml index ba14437764f9cb07b2051ab6c674163d159f4136..5cedb1cf3186da1ad1ae22108abc67c91155870b 100644 --- a/doc/src/sgml/ref/select.sgml +++ b/doc/src/sgml/ref/select.sgml @@ -1,5 +1,5 @@ <!-- -$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 --> @@ -186,10 +186,11 @@ TABLE { [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] | </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> diff --git a/doc/src/sgml/ref/update.sgml b/doc/src/sgml/ref/update.sgml index 2464bf16f93f7df83584860042916c58903ab7b4..8f32c83dc44d3c20e814bcb79b258ed1d7772786 100644 --- a/doc/src/sgml/ref/update.sgml +++ b/doc/src/sgml/ref/update.sgml @@ -1,5 +1,5 @@ <!-- -$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 --> @@ -66,9 +66,10 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <rep </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> diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 4958299a2e4d3ce9c73c6dcc78e031116321277a..ea16913c8e2885dd5da065517deb60d031959dfc 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -8,7 +8,7 @@ * * * 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 @@ -53,10 +53,14 @@ CreateTemplateTupleDesc(int natts, bool hasoid) * 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) @@ -70,7 +74,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid) for (i = 0; i < natts; i++) { attrs[i] = (Form_pg_attribute) stg; - stg += MAXALIGN(ATTRIBUTE_TUPLE_SIZE); + stg += MAXALIGN(ATTRIBUTE_FIXED_PART_SIZE); } } else @@ -139,7 +143,7 @@ CreateTupleDescCopy(TupleDesc tupdesc) 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; } @@ -166,7 +170,7 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) 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) @@ -356,6 +360,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) return false; if (attr1->attinhcount != attr2->attinhcount) return false; + /* attacl is ignored, since it's not even present... */ } if (tupdesc1->constr != NULL) @@ -471,6 +476,7 @@ TupleDescInitEntry(TupleDesc desc, 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), diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 00b52dce8010a31805cec23200c05973cd21522a..19aab42554e2217fdfd4eec3bd8a6a53da5697a5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -8,7 +8,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -633,7 +633,7 @@ boot_openrel(char *relname) 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; @@ -643,7 +643,7 @@ boot_openrel(char *relname) 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]; @@ -709,7 +709,7 @@ DefineAttr(char *name, char *type, int attnum) 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); @@ -1017,16 +1017,19 @@ boot_get_type_io_data(Oid typid, /* ---------------- * 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; } diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index ad32f49e7d06f85338025a81adedc392253eb161..b49c80e485b856130cc41dee480caf9d5d676950 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.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. @@ -61,14 +61,23 @@ static void ExecGrant_Namespace(InternalGrant *grantStmt); 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 @@ -118,7 +127,7 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant, 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. @@ -131,7 +140,7 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant, (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 @@ -165,13 +174,17 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant, 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; @@ -212,10 +225,15 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs, */ 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); + } } /* @@ -271,12 +289,11 @@ ExecuteGrantStmt(GrantStmt *stmt) 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 @@ -297,7 +314,8 @@ ExecuteGrantStmt(GrantStmt *stmt) } /* - * 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) { @@ -367,8 +385,26 @@ ExecuteGrantStmt(GrantStmt *stmt) 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, @@ -385,7 +421,9 @@ ExecuteGrantStmt(GrantStmt *stmt) /* * 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) @@ -571,6 +609,234 @@ objectNamesToOids(GrantObjectType objtype, List *objnames) 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. */ @@ -578,9 +844,11 @@ static void ExecGrant_Relation(InternalGrant *istmt) { Relation relation; + Relation attRelation; ListCell *cell; relation = heap_open(RelationRelationId, RowExclusiveLock); + attRelation = heap_open(AttributeRelationId, RowExclusiveLock); foreach(cell, istmt->objects) { @@ -588,21 +856,15 @@ ExecGrant_Relation(InternalGrant *istmt) 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), @@ -655,9 +917,9 @@ ExecGrant_Relation(InternalGrant *istmt) 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)) { @@ -668,7 +930,7 @@ ExecGrant_Relation(InternalGrant *istmt) */ 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; } @@ -676,7 +938,7 @@ ExecGrant_Relation(InternalGrant *istmt) 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 @@ -686,9 +948,36 @@ ExecGrant_Relation(InternalGrant *istmt) 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. @@ -703,67 +992,160 @@ ExecGrant_Relation(InternalGrant *istmt) 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); } @@ -833,7 +1215,8 @@ ExecGrant_Database(InternalGrant *istmt) 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. @@ -867,7 +1250,7 @@ ExecGrant_Database(InternalGrant *istmt) 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); @@ -950,7 +1333,8 @@ ExecGrant_Fdw(InternalGrant *istmt) 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. @@ -984,7 +1368,8 @@ ExecGrant_Fdw(InternalGrant *istmt) 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); @@ -1066,7 +1451,8 @@ static void ExecGrant_ForeignServer(InternalGrant *istmt) 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. @@ -1100,7 +1486,8 @@ static void ExecGrant_ForeignServer(InternalGrant *istmt) 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); @@ -1182,7 +1569,8 @@ ExecGrant_Function(InternalGrant *istmt) 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. @@ -1216,7 +1604,7 @@ ExecGrant_Function(InternalGrant *istmt) CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ - updateAclDependencies(ProcedureRelationId, funcId, + updateAclDependencies(ProcedureRelationId, funcId, 0, ownerId, istmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); @@ -1305,7 +1693,8 @@ ExecGrant_Language(InternalGrant *istmt) 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. @@ -1339,7 +1728,7 @@ ExecGrant_Language(InternalGrant *istmt) 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); @@ -1422,7 +1811,8 @@ ExecGrant_Namespace(InternalGrant *istmt) 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. @@ -1456,7 +1846,7 @@ ExecGrant_Namespace(InternalGrant *istmt) 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); @@ -1545,7 +1935,8 @@ ExecGrant_Tablespace(InternalGrant *istmt) 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. @@ -1579,7 +1970,7 @@ ExecGrant_Tablespace(InternalGrant *istmt) CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ - updateAclDependencies(TableSpaceRelationId, tblId, + updateAclDependencies(TableSpaceRelationId, tblId, 0, ownerId, istmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); @@ -1677,6 +2068,8 @@ privilege_to_string(AclMode privilege) 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 */ @@ -1713,6 +2106,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] = 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 */ @@ -1774,6 +2169,34 @@ aclcheck_error(AclResult aclerr, AclObjectKind objectkind, } +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) @@ -1800,11 +2223,15 @@ 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); @@ -1830,15 +2257,105 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, Oid roleid, } } -/* - * 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, @@ -2376,6 +2893,115 @@ pg_foreign_server_aclmask(Oid srv_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 * diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 6f63232ff5467acb843ac2a09dbb93c4c71d81be..70c43cdec02dbc32733ec7e1eb8ee33a4b26848d 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -976,6 +976,13 @@ deleteOneObject(const ObjectAddress *object, Relation depRel) 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. */ @@ -987,13 +994,6 @@ deleteOneObject(const ObjectAddress *object, Relation depRel) */ 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. diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 462a1aac614853d695dfbf8f72da3909f0c5e9ca..b01edbe0170eb581852d026511c406b68ece35ca 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.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 @@ -112,37 +112,37 @@ static List *insert_ordered_unique_oid(List *list, Oid datum); 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 } }; /* @@ -154,7 +154,7 @@ static FormData_pg_attribute a6 = { 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}; @@ -475,11 +475,13 @@ CheckAttributeType(const char *attname, Oid atttypid) * 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, @@ -512,6 +514,9 @@ 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 */ diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index dee85b671617b72ad7f76b53a83bdbecd6e29a3e..e53f4f52dcf7e4f7b4d4d5e473331af68ce749f7 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.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 @@ -178,7 +178,7 @@ ConstructTupleDescriptor(Relation heapRelation, * 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 @@ -198,7 +198,7 @@ ConstructTupleDescriptor(Relation heapRelation, /* 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"); diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 989f2470af18047dd7b2bf9791ac10aa77040e6e..12ffd742566372767e9fe36496642bcc46caaab5 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -8,7 +8,7 @@ * * * 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. @@ -774,7 +774,7 @@ makeOperatorDependencies(HeapTuple tuple) /* 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)) diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 191b8b8a9e45f5aaa3e8551b7b7132cfb1a14f5f..f9b2716b9f73e1682a32fec2c73a3fb1eb59d88f 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -499,7 +499,7 @@ ProcedureCreate(const char *procedureName, if (is_update) { deleteDependencyRecordsFor(ProcedureRelationId, retval); - deleteSharedDependencyRecordsFor(ProcedureRelationId, retval); + deleteSharedDependencyRecordsFor(ProcedureRelationId, retval, 0); } myself.classId = ProcedureRelationId; diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index 113c72f3245b5384341d123df9555bb1f2d227fe..ee5329b1d705270c936d7f71a8250af61ca8f7f4 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -55,15 +55,19 @@ static int getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, 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, @@ -111,6 +115,7 @@ recordSharedDependencyOn(ObjectAddress *depender, sdepRel)) { shdepAddDependency(sdepRel, depender->classId, depender->objectId, + depender->objectSubId, referenced->classId, referenced->objectId, deptype); } @@ -163,14 +168,15 @@ recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner) * 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; /* @@ -194,9 +200,13 @@ shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, 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) { @@ -206,8 +216,8 @@ shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, /* 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); } @@ -244,6 +254,7 @@ shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, 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); @@ -268,6 +279,9 @@ shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, * 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) @@ -277,7 +291,8 @@ 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); @@ -300,7 +315,7 @@ changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId) * 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); @@ -368,7 +383,7 @@ getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff) * 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 @@ -388,7 +403,8 @@ getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff) * 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) { @@ -429,11 +445,12 @@ updateAclDependencies(Oid classId, Oid objectId, Oid ownerId, bool isGrant, 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); } @@ -533,7 +550,7 @@ checkSharedDependencies(Oid classId, Oid objectId, 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 @@ -755,7 +772,7 @@ dropDatabaseDependencies(Oid databaseId) 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); @@ -768,15 +785,19 @@ dropDatabaseDependencies(Oid databaseId) * 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); @@ -791,7 +812,8 @@ deleteSharedDependencyRecordsFor(Oid classId, Oid objectId) * 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) { @@ -814,6 +836,7 @@ shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId, 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); @@ -835,20 +858,26 @@ shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId, * 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; @@ -865,9 +894,19 @@ shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId, 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))) { @@ -1067,7 +1106,7 @@ isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel) * * 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 @@ -1131,10 +1170,9 @@ shdepDropOwned(List *roleids, DropBehavior behavior) 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) @@ -1172,14 +1210,13 @@ shdepDropOwned(List *roleids, DropBehavior behavior) 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; @@ -1190,7 +1227,7 @@ shdepDropOwned(List *roleids, DropBehavior behavior) /* 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; } diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index d39a66b58d2a8246d90ef58bf092f63fd5fcaea2..6d28b1df2fd92a45e69387ea7df854801bf0f8b2 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -482,7 +482,7 @@ GenerateTypeDependencies(Oid typeNamespace, if (rebuild) { deleteDependencyRecordsFor(TypeRelationId, typeObjectId); - deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId); + deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId, 0); } myself.classId = TypeRelationId; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5f6a2c42de6a5825f6ffc1c3ebc1366a0c58b7dc..33447b671f114c75e68396f368edc5779a284949 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -705,11 +705,12 @@ examine_attribute(Relation onerel, int attnum) 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); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 061d45c30ac82c35da958515bb258d92fa840183..397e010acaf01d1bd7e2c5c9575c58ca28b368d7 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -246,6 +246,7 @@ static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, 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, @@ -3589,6 +3590,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, attribute.attisdropped = false; attribute.attislocal = colDef->is_local; attribute.attinhcount = colDef->inhcount; + /* attribute.attacl is handled by InsertPgAttributeTuple */ ReleaseSysCache(typeTuple); @@ -4473,15 +4475,14 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, * 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]; @@ -4506,10 +4507,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, 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, @@ -4517,24 +4515,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, 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 @@ -4598,6 +4584,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, opclasses); } + /* + * Now we can check permissions. + */ + checkFkeyPermissions(pkrel, pkattnum, numpks); + checkFkeyPermissions(rel, fkattnum, numfks); + /* * Look up the equality operators to use in the constraint. * @@ -5016,6 +5008,30 @@ transformFkeyCheckAttrs(Relation pkrel, 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. @@ -5123,7 +5139,7 @@ CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint, 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(); @@ -5204,7 +5220,7 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, } fk_trigger->args = NIL; - (void) CreateTrigger(fk_trigger, constraintOid); + (void) CreateTrigger(fk_trigger, constraintOid, false); /* Make changes-so-far visible */ CommandCounterIncrement(); @@ -5256,7 +5272,7 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, } fk_trigger->args = NIL; - (void) CreateTrigger(fk_trigger, constraintOid); + (void) CreateTrigger(fk_trigger, constraintOid, false); } /* diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index 54818ff34d72bc72860fd2a20147be7d810e3b76..b81381a6ea88c3e8cd1ccaffa7a18448ba381b27 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -37,7 +37,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -455,7 +455,7 @@ DropTableSpace(DropTableSpaceStmt *stmt) /* * Remove dependency on owner. */ - deleteSharedDependencyRecordsFor(TableSpaceRelationId, tablespaceoid); + deleteSharedDependencyRecordsFor(TableSpaceRelationId, tablespaceoid, 0); /* * Acquire TablespaceCreateLock to ensure that no TablespaceCreateDbspace diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 699493c3350e07f9100cadbed6d4223932286ec0..ce276e5fe5550a39bb43d46c6adc7fcb8711e912 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -74,11 +74,16 @@ static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, * 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; @@ -117,37 +122,27 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid) 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); diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c index f9248a2b674de6bdb6a1ac82f16e8493cbf354c2..7276cd50d40fef3f3276ad98f73566e2e4fa119a 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -9,7 +9,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -1255,7 +1255,7 @@ makeConfigurationDependencies(HeapTuple tuple, bool removeOld, if (removeOld) { deleteDependencyRecordsFor(myself.classId, myself.objectId); - deleteSharedDependencyRecordsFor(myself.classId, myself.objectId); + deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0); } /* diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index a013e80ac9426dfe4a9b04899806f53ad471e0f0..7c1da42bc3edac4a1686953231870000c8415784 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/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 $ * *------------------------------------------------------------------------- */ @@ -1123,9 +1123,17 @@ GrantRole(GrantRoleStmt *stmt) */ 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, diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index bb05b5d4828a8787a76d4b30686371891ff49d90..0352d9a5e43d8052b15475b57683c32e222575ad 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -34,6 +34,7 @@ #include "access/heapam.h" #include "access/reloptions.h" +#include "access/sysattr.h" #include "access/transam.h" #include "access/xact.h" #include "catalog/heap.h" @@ -453,8 +454,12 @@ static void 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 @@ -484,12 +489,110 @@ ExecCheckRTEPerms(RangeTblEntry *rte) 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); + } + } } /* diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index da1c65cfcca7661b608a74a9bc7f0b0f98f33b18..06f89b16a3016da3042bf253863212f5f542e0ce 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -1740,6 +1740,8 @@ _copyRangeTblEntry(RangeTblEntry *from) COPY_SCALAR_FIELD(inFromCl); COPY_SCALAR_FIELD(requiredPerms); COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(modifiedCols); return newnode; } @@ -2342,6 +2344,17 @@ _copyFuncWithArgs(FuncWithArgs *from) 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) { @@ -4096,6 +4109,9 @@ copyObject(void *from) case T_FuncWithArgs: retval = _copyFuncWithArgs(from); break; + case T_AccessPriv: + retval = _copyAccessPriv(from); + break; case T_XmlSerialize: retval = _copyXmlSerialize(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 190750f2e19ee9efd6ed9cd4ee3cae8cb0862f70..4e101dd23b845ff0dbe846fffbb8e8a9b87c0607 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -22,7 +22,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -1013,6 +1013,15 @@ _equalFuncWithArgs(FuncWithArgs *a, FuncWithArgs *b) 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) { @@ -2122,6 +2131,8 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) COMPARE_SCALAR_FIELD(inFromCl); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); + COMPARE_BITMAPSET_FIELD(selectedCols); + COMPARE_BITMAPSET_FIELD(modifiedCols); return true; } @@ -2874,6 +2885,9 @@ equal(void *a, void *b) case T_FuncWithArgs: retval = _equalFuncWithArgs(a, b); break; + case T_AccessPriv: + retval = _equalAccessPriv(a, b); + break; case T_XmlSerialize: retval = _equalXmlSerialize(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index af427fe7e6c8346af8f0833960474c2256e083df..0efd84dae8d54da8fc05cf40320c3b70adba5522 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * 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* @@ -180,8 +180,6 @@ _outList(StringInfo str, List *node) * 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) @@ -2060,6 +2058,8 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) WRITE_BOOL_FIELD(inFromCl); WRITE_UINT_FIELD(requiredPerms); WRITE_OID_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(modifiedCols); } static void diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index d5f4677c2a33a143aa9f2d7bc447250019e1c7b7..de62e53d1df8ec353f50428d2e60e66b3846eeb6 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * 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 @@ -114,6 +114,11 @@ 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 @@ -137,6 +142,46 @@ 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 @@ -1102,6 +1147,8 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(inFromCl); READ_UINT_FIELD(requiredPerms); READ_OID_FIELD(checkAsUser); + READ_BITMAPSET_FIELD(selectedCols); + READ_BITMAPSET_FIELD(modifiedCols); READ_DONE(); } diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 1a64a7742d5b0e2aea5a3a85de9d9af93f9fd3c5..17016d5f3bfb4397d7209ebff2dc1b97846e86b9 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -9,7 +9,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -189,7 +189,8 @@ set_plan_references(PlannerGlobal *glob, Plan *plan, List *rtable) * 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) { diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6b54e9ba1731f2d1e234f4f5d885dd66089ca47c..397e951c71b9aa25e07c46d47a5c16ff0bb4cff7 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -17,13 +17,14 @@ * 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" @@ -422,6 +423,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * 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; @@ -629,7 +631,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* * 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); @@ -637,17 +641,22 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { 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); } @@ -1129,8 +1138,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) List *targetvars, *targetnames, *sv_relnamespace, - *sv_varnamespace, - *sv_rtable; + *sv_varnamespace; + int sv_rtable_length; RangeTblEntry *jrte; int tllen; @@ -1254,16 +1263,15 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) * "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 */ @@ -1283,7 +1291,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) &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; @@ -1618,6 +1626,7 @@ static Query * transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) { Query *qry = makeNode(Query); + RangeTblEntry *target_rte; Node *qual; ListCell *origTargetList; ListCell *tl; @@ -1675,6 +1684,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) 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) @@ -1715,6 +1725,10 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) 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) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index cc1f812bd98fdc233d74ed50d9e90fd244037fcb..7f9e5e5b983399baac99228b3c4a7ca92b766453 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.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 @@ -94,6 +94,13 @@ extern List *parsetree; /* final parse result is delivered here */ 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 @@ -167,7 +174,8 @@ static TypeName *TableFuncTypeName(List *columns); WithClause *with; A_Indices *aind; ResTarget *target; - PrivTarget *privtarget; + struct PrivTarget *privtarget; + AccessPriv *accesspriv; InsertStmt *istmt; VariableSetStmt *vsetstmt; @@ -254,7 +262,7 @@ static TypeName *TableFuncTypeName(List *columns); %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 @@ -4210,12 +4218,11 @@ RevokeStmt: /* - * 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 */ @@ -4225,18 +4232,54 @@ privileges: privilege_list { $$ = 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; + } ; @@ -4246,70 +4289,70 @@ privilege: SELECT { $$ = pstrdup($1); } 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; diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 93e393f8379f74efc9eeba3463e739813dc3e72d..7e9fb9c071a57301f7026d28fa50fe7720dbbb9c 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,7 @@ static void extractRemainingColumns(List *common_colnames, 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, @@ -194,8 +195,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * * 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; @@ -305,7 +305,9 @@ extractRemainingColumns(List *common_colnames, * 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, @@ -315,17 +317,25 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars) * 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 @@ -728,6 +738,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, *r_colvars, *res_colvars; RangeTblEntry *rte; + int k; /* * Recursively process the left and right subtrees @@ -912,6 +923,8 @@ transformFromClauseItem(ParseState *pstate, Node *n, } j->quals = transformJoinUsingClause(pstate, + l_rte, + r_rte, l_usingvars, r_usingvars); } @@ -972,6 +985,12 @@ transformFromClauseItem(ParseState *pstate, Node *n, *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; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 1d0f77a5b359efe2fe81d7d87360c5889b6a51ed..2bf6174866cc3fd2221ee815ed49bfa5ecfb9be9 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -1977,6 +1977,9 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname, /* 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; } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 49ad024039755937fffe289c2615de353e10c3ac..eb98f470ee3796cc65b6ed89adf34cb3e7e2c0b9 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -39,6 +39,8 @@ static RangeTblEntry *scanNameSpaceForRefname(ParseState *pstate, 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, @@ -435,14 +437,8 @@ GetCTEForRTE(ParseState *pstate, RangeTblEntry *rte, int rtelevelsup) * 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, @@ -450,6 +446,7 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname, { Node *result = NULL; int attnum = 0; + Var *var; ListCell *c; /* @@ -476,9 +473,10 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname, 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; } } @@ -504,9 +502,10 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname, 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; } } } @@ -594,6 +593,122 @@ qualifiedNameToVar(ParseState *pstate, 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. @@ -838,6 +953,8 @@ addRangeTableEntry(ParseState *pstate, 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 @@ -891,6 +1008,8 @@ addRangeTableEntryForRelation(ParseState *pstate, 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 @@ -969,6 +1088,8 @@ addRangeTableEntryForSubquery(ParseState *pstate, 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 @@ -1101,6 +1222,8 @@ addRangeTableEntryForFunction(ParseState *pstate, 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 @@ -1168,8 +1291,11 @@ addRangeTableEntryForValues(ParseState *pstate, */ 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 @@ -1239,6 +1365,8 @@ addRangeTableEntryForJoin(ParseState *pstate, 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 @@ -1320,6 +1448,8 @@ addRangeTableEntryForCTE(ParseState *pstate, 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 @@ -1803,6 +1933,7 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, * 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, @@ -1817,10 +1948,17 @@ 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, @@ -1828,6 +1966,9 @@ expandRelAttrs(ParseState *pstate, RangeTblEntry *rte, 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? */ diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 2765751edb93e3d58b477165c915919212526e5d..3f804472c77a38f85020593d67befb78b53786df 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -850,6 +850,8 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) * 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, @@ -929,20 +931,37 @@ 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; } } @@ -956,6 +975,8 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * 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) @@ -975,9 +996,6 @@ 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)); diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 56549a550954901bb5b1a9dda68090dfe5c8e785..7cfeacb7305da57262f3efbad81a0da58b354bfd 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -1178,9 +1178,13 @@ ApplyRetrieveRule(Query *parsetree, 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? diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 61f23213c449be926943337feaddfd1a9fbc4c0e..7e7d07e4f032e3115569c864cba773543f0991cd 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -910,7 +910,7 @@ ProcessUtility(Node *parsetree, break; case T_CreateTrigStmt: - CreateTrigger((CreateTrigStmt *) parsetree, InvalidOid); + CreateTrigger((CreateTrigStmt *) parsetree, InvalidOid, true); break; case T_DropPropertyStmt: diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index b270077e8d8e8ecea55d702171d711335a48290e..cb0ebf4694106d05964f7a5ea455269ce54284e7 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -366,6 +366,47 @@ allocacl(int n) 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) */ @@ -542,11 +583,17 @@ acldefault(GrantObjectType objtype, Oid ownerId) { 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; @@ -593,7 +640,13 @@ acldefault(GrantObjectType objtype, Oid ownerId) 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) @@ -614,9 +667,12 @@ acldefault(GrantObjectType objtype, Oid ownerId) * "_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; } diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 4be6e08606b2331a6645b697c920d0a728c00272..ecfe59e3a8c83caa9e503d55561d0fb31c92a60d 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -480,7 +480,7 @@ RelationBuildTupleDesc(Relation relation) memcpy(relation->rd_att->attrs[attp->attnum - 1], attp, - ATTRIBUTE_TUPLE_SIZE); + ATTRIBUTE_FIXED_PART_SIZE); /* Update constraint/default info */ if (attp->attnotnull) @@ -1449,7 +1449,7 @@ formrdesc(const char *relationName, Oid relationReltype, { 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; @@ -2714,7 +2714,7 @@ BuildHardcodedDescriptor(int natts, Form_pg_attribute attrs, bool hasoids) 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; } @@ -3441,7 +3441,7 @@ load_relcache_init_file(void) { 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; @@ -3751,7 +3751,7 @@ write_relcache_init_file(void) /* 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 */ diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index dbd9be4657f86e374e11acc88d518ed37aff0e37..15f66cd4938be1f7dff99654d779d0408e22b06a 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -42,7 +42,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -1601,7 +1601,7 @@ setup_depend(void) " 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 }; diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c index 819bb4d2e61a7af43c00b64bb1ed1b530537ff99..85b3373c5353259d7018ac816c10c0983f74c543 100644 --- a/src/bin/pg_dump/dumputils.c +++ b/src/bin/pg_dump/dumputils.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -23,11 +23,13 @@ #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); /* @@ -384,6 +386,7 @@ parsePGArray(const char *atext, char ***itemarray, int *nitems) * 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 @@ -394,12 +397,12 @@ parsePGArray(const char *atext, char ***itemarray, int *nitems) * 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) { @@ -448,8 +451,10 @@ buildACLCommands(const char *name, const char *type, * 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 @@ -468,7 +473,7 @@ buildACLCommands(const char *name, const char *type, /* 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; @@ -491,15 +496,19 @@ buildACLCommands(const char *name, const char *type, ? 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)); } @@ -553,8 +562,13 @@ buildACLCommands(const char *name, const char *type, * 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); @@ -587,8 +601,9 @@ buildACLCommands(const char *name, const char *type, * 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; @@ -626,12 +641,12 @@ do { \ { \ 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; \ } \ } \ @@ -654,13 +669,18 @@ do { \ /* 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 */ @@ -700,11 +720,15 @@ do { \ { 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); @@ -757,11 +781,13 @@ copyAclUserName(PQExpBuffer output, char *input) * 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); } diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h index 13341d77a8acfbdbcba385f5365031db2d4c895b..b8a3532fa880fecd697d15343541da8ef1c94988 100644 --- a/src/bin/pg_dump/dumputils.h +++ b/src/bin/pg_dump/dumputils.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.h,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -28,8 +28,8 @@ extern void appendStringLiteralDQ(PQExpBuffer buf, const char *str, 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, diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 76558856e21d01aec1d3064e295ba70b93da4a6f..f47333c6051025299c9e73ad98b35c5f2d4b93ad 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -165,7 +165,7 @@ static void dumpUserMappings(Archive *fout, const char *target, 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); @@ -5929,7 +5929,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) 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); @@ -6786,7 +6786,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) 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); @@ -7342,7 +7342,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) 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); @@ -8828,7 +8828,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) 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); @@ -9227,7 +9227,7 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) 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); @@ -9305,7 +9305,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) 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); @@ -9412,6 +9412,7 @@ dumpUserMappings(Archive *fout, const char *target, * '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). @@ -9421,7 +9422,7 @@ dumpUserMappings(Archive *fout, const char *target, */ 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) { @@ -9433,7 +9434,7 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, 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); @@ -9459,10 +9460,10 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, 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) @@ -9472,9 +9473,51 @@ dumpTable(Archive *fout, TableInfo *tbinfo) 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); } } diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 1be955b3e72d3f5836967bdfc69715292768db46..f5a08eb834f9e47bcd00aba55ed1c4776528a90c 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -882,7 +882,7 @@ dumpTablespaces(PGconn *conn) 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"), @@ -1075,7 +1075,7 @@ dumpCreateDB(PGconn *conn) } 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"), diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 01cb9bee4be0290d01616ea23ef4872bbcc04e5e..8414d1bff4d9655f2dd64a07b2ec4321017ddd3f 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -8,7 +8,7 @@ * * Copyright (c) 2000-2009, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.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" @@ -519,7 +519,7 @@ listAllDbs(bool verbose) /* - * List Tables Grant/Revoke Permissions + * List Tables' Grant/Revoke Permissions * \z (now also \dp -- perhaps more mnemonic) */ bool @@ -528,7 +528,7 @@ permissionsList(const char *pattern) 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); @@ -544,7 +544,18 @@ permissionsList(const char *pattern) 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"); @@ -1907,7 +1918,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys 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; @@ -1965,7 +1976,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys 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"); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 64a05e738d56e9003ab8ead4cf2a6cb636335521..11349023d9431da02549097d494a28101efc2009 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200901161 +#define CATALOG_VERSION_NO 200901221 #endif diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index f615d45011e6c5e7ae2d273b9c21789f7f844e6e..99380da566e7aaaf8c7b0b0b477325671a823df8 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -108,7 +108,7 @@ typedef struct ObjectAddress { 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) */ @@ -221,14 +221,15 @@ extern void recordSharedDependencyOn(ObjectAddress *depender, 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); diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index e2c10efe7793994e9576e1d77ff9f17090091168..1c517c7f3b664f2cdd97bbfe2030c05135b57064 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -202,7 +202,7 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using bt #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)); diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h index 5411019ab145b098f9c787abc68a224f42f2a964..3fddf7e922781d02db3f13a14ef3832b8471fcb4 100644 --- a/src/include/catalog/pg_attribute.h +++ b/src/include/catalog/pg_attribute.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/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 @@ -144,14 +144,24 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS /* 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)) /* ---------------- @@ -166,7 +176,7 @@ typedef FormData_pg_attribute *Form_pg_attribute; * ---------------- */ -#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 @@ -184,24 +194,9 @@ typedef FormData_pg_attribute *Form_pg_attribute; #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 * @@ -217,244 +212,246 @@ typedef FormData_pg_attribute *Form_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 @@ -465,19 +462,19 @@ DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); * ---------------- */ #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 */ diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 97ea79544ba1ae7d790af7bd363d77b37a197503..776158805d4e341309905610d26ae238f0e3caa8 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/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 @@ -123,7 +123,7 @@ typedef FormData_pg_class *Form_pg_class; /* 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(""); diff --git a/src/include/catalog/pg_shdepend.h b/src/include/catalog/pg_shdepend.h index f741333914d1c26d50f42426a9429b9c15c176db..97f01df4c15cb495562d5480193088c00b0061c7 100644 --- a/src/include/catalog/pg_shdepend.h +++ b/src/include/catalog/pg_shdepend.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/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 @@ -39,10 +39,12 @@ CATALOG(pg_shdepend,1214) BKI_SHARED_RELATION BKI_WITHOUT_OIDS 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 */ @@ -65,13 +67,14 @@ typedef FormData_pg_shdepend *Form_pg_shdepend; * 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 /* diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index 0b0c6c22323265b3b4d9b6de653d00332bdbd531..123159e59a63dcb5ebaad306ed01fedd82d8b4c0 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/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 $ * *------------------------------------------------------------------------- */ @@ -104,7 +104,8 @@ extern PGDLLIMPORT int SessionReplicationRole; #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); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 270760527b6e07489edfb2db31b9f6191a10b608..91efce946238bc408480b0cfd79c323fef905887 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.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 $ * *------------------------------------------------------------------------- */ @@ -369,7 +369,7 @@ typedef enum NodeTag T_FkConstraint, T_PrivGrantee, T_FuncWithArgs, - T_PrivTarget, + T_AccessPriv, T_CreateOpClassItem, T_InhRelation, T_FunctionParameter, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index ce225f801eb70442be8cf15b5df0194bee270f65..b73e9f2cda6cbbe1e315016b72b98b8bbb3cf364 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -13,13 +13,14 @@ * 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" @@ -622,6 +623,15 @@ typedef struct XmlSerialize * 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 @@ -644,7 +654,7 @@ typedef struct RangeTblEntry /* * 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. */ /* @@ -705,6 +715,8 @@ typedef struct RangeTblEntry 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; /* @@ -1168,6 +1180,7 @@ typedef struct AlterDomainStmt */ typedef enum GrantObjectType { + ACL_OBJECT_COLUMN, /* column */ ACL_OBJECT_RELATION, /* table, view */ ACL_OBJECT_SEQUENCE, /* sequence */ ACL_OBJECT_DATABASE, /* database */ @@ -1186,8 +1199,8 @@ typedef struct GrantStmt 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) */ @@ -1211,18 +1224,27 @@ typedef struct FuncWithArgs 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 diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index fb5e1fcf45140188a7b450dc42c2461804676fab..5e49b1a3dd8aa1b4706afbdb3c12bdcff0cf83e7 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/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 $ * *------------------------------------------------------------------------- */ @@ -33,6 +33,10 @@ * 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. * @@ -77,6 +81,7 @@ typedef struct ParseState 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 */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 76622af25d34aafa57238f3a2ca906a575956923..cb133fced66e52e1a3cfb0f4eb3261e9552e459c 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/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 $ * *------------------------------------------------------------------------- */ @@ -46,6 +46,8 @@ extern Node *qualifiedNameToVar(ParseState *pstate, 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, diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 02cf425619e56859b92ba43a0cf8702ff60f4583..fed2de5b74ef20991b2a196e69aaa1c4a017ff64 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.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 @@ -143,6 +143,7 @@ typedef ArrayType Acl; /* * 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) @@ -172,6 +173,7 @@ typedef enum /* 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 */ @@ -195,9 +197,14 @@ typedef enum AclObjectKind * The information about one Grant/Revoke statement, in internal format: object * and grantees names have been turned into Oids, the privilege list is an * AclMode bitmask. If 'privileges' is ACL_NO_RIGHTS (the 0 value) and - * all_privs is true, 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 { @@ -206,6 +213,7 @@ typedef struct List *objects; bool all_privs; AclMode privileges; + List *col_privs; List *grantees; bool grant_option; DropBehavior behavior; @@ -218,6 +226,8 @@ extern Acl *acldefault(GrantObjectType objtype, Oid ownerId); 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); @@ -253,6 +263,8 @@ extern Datum hash_aclitem(PG_FUNCTION_ARGS); 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, @@ -270,6 +282,10 @@ extern AclMode pg_foreign_data_wrapper_aclmask(Oid fdw_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); @@ -282,6 +298,9 @@ extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mod 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); diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out index 178f4221fca4614afd7fe54da14631aded6aa83d..6eb851a378ca63c448dbbe5d369d5c412793528e 100644 --- a/src/test/regress/expected/dependency.out +++ b/src/test/regress/expected/dependency.out @@ -68,21 +68,21 @@ NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "deptest_pkey" fo 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 diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index f4a2bd8d8f50c571962e31b88ddffcb20353dea8..88d1ab3b78f79e678031b8546bf798dbd3d183d3 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -246,6 +246,147 @@ SELECT * FROM atest2; -- ok 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 - @@ -642,6 +783,8 @@ DROP TABLE atest1; 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; diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql index 63532f7e095702e7d6df93e0bc962dd53003544d..dda20db85568ce3d797f1060f35a8870b0257144 100644 --- a/src/test/regress/sql/privileges.sql +++ b/src/test/regress/sql/privileges.sql @@ -171,6 +171,93 @@ SELECT * FROM atestv4; -- ok (even though regressuser2 cannot access underlying 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 @@ -369,6 +456,8 @@ DROP TABLE atest1; DROP TABLE atest2; DROP TABLE atest3; DROP TABLE atest4; +DROP TABLE atest5; +DROP TABLE atest6; DROP GROUP regressgroup1; DROP GROUP regressgroup2;