Support column-level privileges, as required by SQL standard.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 22 Jan 2009 20:16:10 +0000 (20:16 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 22 Jan 2009 20:16:10 +0000 (20:16 +0000)
Stephen Frost, with help from KaiGai Kohei and others

59 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/ref/grant.sgml
doc/src/sgml/ref/insert.sgml
doc/src/sgml/ref/revoke.sgml
doc/src/sgml/ref/select.sgml
doc/src/sgml/ref/update.sgml
src/backend/access/common/tupdesc.c
src/backend/bootstrap/bootstrap.c
src/backend/catalog/aclchk.c
src/backend/catalog/dependency.c
src/backend/catalog/heap.c
src/backend/catalog/index.c
src/backend/catalog/pg_operator.c
src/backend/catalog/pg_proc.c
src/backend/catalog/pg_shdepend.c
src/backend/catalog/pg_type.c
src/backend/commands/analyze.c
src/backend/commands/tablecmds.c
src/backend/commands/tablespace.c
src/backend/commands/trigger.c
src/backend/commands/tsearchcmds.c
src/backend/commands/user.c
src/backend/executor/execMain.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/plan/setrefs.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/parse_clause.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_relation.c
src/backend/parser/parse_target.c
src/backend/rewrite/rewriteHandler.c
src/backend/tcop/utility.c
src/backend/utils/adt/acl.c
src/backend/utils/cache/relcache.c
src/bin/initdb/initdb.c
src/bin/pg_dump/dumputils.c
src/bin/pg_dump/dumputils.h
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dumpall.c
src/bin/psql/describe.c
src/include/catalog/catversion.h
src/include/catalog/dependency.h
src/include/catalog/indexing.h
src/include/catalog/pg_attribute.h
src/include/catalog/pg_class.h
src/include/catalog/pg_shdepend.h
src/include/commands/trigger.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/parser/parse_node.h
src/include/parser/parse_relation.h
src/include/utils/acl.h
src/test/regress/expected/dependency.out
src/test/regress/expected/privileges.out
src/test/regress/sql/privileges.sql

index bd267dcb758b73a4b5dc9406359e24102051f609..4fae50cd326683f60e533759a76c5584249bce2d 100644 (file)
@@ -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
  -->
       </entry>
      </row>
 
+     <row>
+      <entry><structfield>attacl</structfield></entry>
+      <entry><type>aclitem[]</type></entry>
+      <entry></entry>
+      <entry>
+       Column-level access privileges, if any have been granted specifically
+       on this column
+      </entry>
+     </row>
+
     </tbody>
    </tgroup>
   </table>
       <entry>The OID of the specific dependent object</entry>
      </row>
 
+     <row>
+      <entry><structfield>objsubid</structfield></entry>
+      <entry><type>int4</type></entry>
+      <entry></entry>
+      <entry>
+       For a table column, this is the column number (the
+       <structfield>objid</> and <structfield>classid</> refer to the
+       table itself).  For all other object types, this column is zero
+      </entry>
+     </row>
+
      <row>
       <entry><structfield>refclassid</structfield></entry>
       <entry><type>oid</type></entry>
index dafb8ffb523885fda8d99b1804a72d4380a8a10f..ceda72c141d285c3151a8c9185311e58a52a1f3f 100644 (file)
@@ -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>
-=&gt; \z mytable
-                Access privileges
- Schema |  Name   | Type  |  Access privileges   
---------+---------+-------+-----------------------
- public | mytable | table | miriam=arwdDxt/miriam
-                          : =r/miriam
-                          : admin=arw/miriam
+=&gt; \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,
index 98a1ca28b7923e0452b79a4c49c3423b5bffe764..a2a52d8ba429c3d82f5d943cf5f76faa91c4c46d 100644 (file)
@@ -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>
 
index c8e91e0a159aa2a14d94508418ea418032be7dd9..0f967770c1158bb811d8b2f15af45d7371f4df8b 100644 (file)
@@ -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>
index ba14437764f9cb07b2051ab6c674163d159f4136..5cedb1cf3186da1ad1ae22108abc67c91155870b 100644 (file)
@@ -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>
 
index 2464bf16f93f7df83584860042916c58903ab7b4..8f32c83dc44d3c20e814bcb79b258ed1d7772786 100644 (file)
@@ -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>
index 4958299a2e4d3ce9c73c6dcc78e031116321277a..ea16913c8e2885dd5da065517deb60d031959dfc 100644 (file)
@@ -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),
index 00b52dce8010a31805cec23200c05973cd21522a..19aab42554e2217fdfd4eec3bd8a6a53da5697a5 100644 (file)
@@ -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;
 }
index ad32f49e7d06f85338025a81adedc392253eb161..b49c80e485b856130cc41dee480caf9d5d676950 100644 (file)
@@ -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
  *
index 6f63232ff5467acb843ac2a09dbb93c4c71d81be..70c43cdec02dbc32733ec7e1eb8ee33a4b26848d 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.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.
index 462a1aac614853d695dfbf8f72da3909f0c5e9ca..b01edbe0170eb581852d026511c406b68ece35ca 100644 (file)
@@ -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 */
index dee85b671617b72ad7f76b53a83bdbecd6e29a3e..e53f4f52dcf7e4f7b4d4d5e473331af68ce749f7 100644 (file)
@@ -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");
index 989f2470af18047dd7b2bf9791ac10aa77040e6e..12ffd742566372767e9fe36496642bcc46caaab5 100644 (file)
@@ -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))
index 191b8b8a9e45f5aaa3e8551b7b7132cfb1a14f5f..f9b2716b9f73e1682a32fec2c73a3fb1eb59d88f 100644 (file)
@@ -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;
index 113c72f3245b5384341d123df9555bb1f2d227fe..ee5329b1d705270c936d7f71a8250af61ca8f7f4 100644 (file)
@@ -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;
            }
index d39a66b58d2a8246d90ef58bf092f63fd5fcaea2..6d28b1df2fd92a45e69387ea7df854801bf0f8b2 100644 (file)
@@ -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;
index 5f6a2c42de6a5825f6ffc1c3ebc1366a0c58b7dc..33447b671f114c75e68396f368edc5779a284949 100644 (file)
@@ -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);
index 061d45c30ac82c35da958515bb258d92fa840183..397e010acaf01d1bd7e2c5c9575c58ca28b368d7 100644 (file)
@@ -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);
 }
 
 /*
index 54818ff34d72bc72860fd2a20147be7d810e3b76..b81381a6ea88c3e8cd1ccaffa7a18448ba381b27 100644 (file)
@@ -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
index 699493c3350e07f9100cadbed6d4223932286ec0..ce276e5fe5550a39bb43d46c6adc7fcb8711e912 100644 (file)
@@ -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);
index f9248a2b674de6bdb6a1ac82f16e8493cbf354c2..7276cd50d40fef3f3276ad98f73566e2e4fa119a 100644 (file)
@@ -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);
    }
 
    /*
index a013e80ac9426dfe4a9b04899806f53ad471e0f0..7c1da42bc3edac4a1686953231870000c8415784 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/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,
index bb05b5d4828a8787a76d4b30686371891ff49d90..0352d9a5e43d8052b15475b57683c32e222575ad 100644 (file)
@@ -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);
+       }
+   }
 }
 
 /*
index da1c65cfcca7661b608a74a9bc7f0b0f98f33b18..06f89b16a3016da3042bf253863212f5f542e0ce 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.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;
index 190750f2e19ee9efd6ed9cd4ee3cae8cb0862f70..4e101dd23b845ff0dbe846fffbb8e8a9b87c0607 100644 (file)
@@ -22,7 +22,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.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;
index af427fe7e6c8346af8f0833960474c2256e083df..0efd84dae8d54da8fc05cf40320c3b70adba5522 100644 (file)
@@ -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
index d5f4677c2a33a143aa9f2d7bc447250019e1c7b7..de62e53d1df8ec353f50428d2e60e66b3846eeb6 100644 (file)
@@ -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
    token = pg_strtok(&length);     /* skip :fldname */ \
    local_node->fldname = nodeRead(NULL, 0)
 
+/* Read a bitmapset field */
+#define READ_BITMAPSET_FIELD(fldname) \
+   token = pg_strtok(&length);     /* skip :fldname */ \
+   local_node->fldname = _readBitmapset()
+
 /* Routine exit */
 #define READ_DONE() \
    return local_node
 
 static Datum readDatum(bool typbyval);
 
+/*
+ * _readBitmapset
+ */
+static Bitmapset *
+_readBitmapset(void)
+{
+   Bitmapset *result = NULL;
+   READ_TEMP_LOCALS();
+
+   token = pg_strtok(&length);
+   if (token == NULL)
+       elog(ERROR, "incomplete Bitmapset structure");
+   if (length != 1 || token[0] != '(')
+       elog(ERROR, "unrecognized token: \"%.*s\"", length, token);
+
+   token = pg_strtok(&length);
+   if (token == NULL)
+       elog(ERROR, "incomplete Bitmapset structure");
+   if (length != 1 || token[0] != 'b')
+       elog(ERROR, "unrecognized token: \"%.*s\"", length, token);
+
+   for (;;)
+   {
+       int     val;
+       char   *endptr;
+
+       token = pg_strtok(&length);
+       if (token == NULL)
+           elog(ERROR, "unterminated Bitmapset structure");
+       if (length == 1 && token[0] == ')')
+           break;
+       val = (int) strtol(token, &endptr, 10);
+       if (endptr != token + length)
+           elog(ERROR, "unrecognized integer: \"%.*s\"", length, token);
+       result = bms_add_member(result, val);
+   }
+
+   return result;
+}
+
 
 /*
  * _readQuery
@@ -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();
 }
index 1a64a7742d5b0e2aea5a3a85de9d9af93f9fd3c5..17016d5f3bfb4397d7209ebff2dc1b97846e86b9 100644 (file)
@@ -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)
    {
index 6b54e9ba1731f2d1e234f4f5d885dd66089ca47c..397e951c71b9aa25e07c46d47a5c16ff0bb4cff7 100644 (file)
  * 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)
index cc1f812bd98fdc233d74ed50d9e90fd244037fcb..7f9e5e5b983399baac99228b3c4a7ca92b766453 100644 (file)
@@ -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;
index 93e393f8379f74efc9eeba3463e739813dc3e72d..7e9fb9c071a57301f7026d28fa50fe7720dbbb9c 100644 (file)
@@ -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;
index 1d0f77a5b359efe2fe81d7d87360c5889b6a51ed..2bf6174866cc3fd2221ee815ed49bfa5ecfb9be9 100644 (file)
@@ -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;
 }
 
index 49ad024039755937fffe289c2615de353e10c3ac..eb98f470ee3796cc65b6ed89adf34cb3e7e2c0b9 100644 (file)
@@ -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? */
index 2765751edb93e3d58b477165c915919212526e5d..3f804472c77a38f85020593d67befb78b53786df 100644 (file)
@@ -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));
index 56549a550954901bb5b1a9dda68090dfe5c8e785..7cfeacb7305da57262f3efbad81a0da58b354bfd 100644 (file)
@@ -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?
index 61f23213c449be926943337feaddfd1a9fbc4c0e..7e7d07e4f032e3115569c864cba773543f0991cd 100644 (file)
@@ -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:
index b270077e8d8e8ecea55d702171d711335a48290e..cb0ebf4694106d05964f7a5ea455269ce54284e7 100644 (file)
@@ -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;
 }
index 4be6e08606b2331a6645b697c920d0a728c00272..ecfe59e3a8c83caa9e503d55561d0fb31c92a60d 100644 (file)
@@ -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 */
index dbd9be4657f86e374e11acc88d518ed37aff0e37..15f66cd4938be1f7dff99654d779d0408e22b06a 100644 (file)
@@ -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
    };
index 819bb4d2e61a7af43c00b64bb1ed1b530537ff99..85b3373c5353259d7018ac816c10c0983f74c543 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.43 2009/01/01 17:23:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.44 2009/01/22 20:16:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #define supports_grant_options(version) ((version) >= 70400)
 
-static bool parseAclItem(const char *item, const char *type, const char *name,
-            int remoteVersion, PQExpBuffer grantee, PQExpBuffer grantor,
+static bool parseAclItem(const char *item, const char *type,
+            const char *name, const char *subname, int remoteVersion,
+            PQExpBuffer grantee, PQExpBuffer grantor,
             PQExpBuffer privs, PQExpBuffer privswgo);
 static char *copyAclUserName(PQExpBuffer output, char *input);
-static void AddAcl(PQExpBuffer aclbuf, const char *keyword);
+static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
+                  const char *subname);
 
 
 /*
@@ -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);
 }
 
 
index 13341d77a8acfbdbcba385f5365031db2d4c895b..b8a3532fa880fecd697d15343541da8ef1c94988 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.h,v 1.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,
index 76558856e21d01aec1d3064e295ba70b93da4a6f..f47333c6051025299c9e73ad98b35c5f2d4b93ad 100644 (file)
@@ -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);
    }
 }
index 1be955b3e72d3f5836967bdfc69715292768db46..f5a08eb834f9e47bcd00aba55ed1c4776528a90c 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.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"),
index 01cb9bee4be0290d01616ea23ef4872bbcc04e5e..8414d1bff4d9655f2dd64a07b2ec4321017ddd3f 100644 (file)
@@ -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");
 
index 64a05e738d56e9003ab8ead4cf2a6cb636335521..11349023d9431da02549097d494a28101efc2009 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.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
index f615d45011e6c5e7ae2d273b9c21789f7f844e6e..99380da566e7aaaf8c7b0b0b477325671a823df8 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.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);
index e2c10efe7793994e9576e1d77ff9f17090091168..1c517c7f3b664f2cdd97bbfe2030c05135b57064 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.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));
index 5411019ab145b098f9c787abc68a224f42f2a964..3fddf7e922781d02db3f13a14ef3832b8471fcb4 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/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 */
index 97ea79544ba1ae7d790af7bd363d77b37a197503..776158805d4e341309905610d26ae238f0e3caa8 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/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("");
index f741333914d1c26d50f42426a9429b9c15c176db..97f01df4c15cb495562d5480193088c00b0061c7 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/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
 
 
 /*
index 0b0c6c22323265b3b4d9b6de653d00332bdbd531..123159e59a63dcb5ebaad306ed01fedd82d8b4c0 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/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);
index 270760527b6e07489edfb2db31b9f6191a10b608..91efce946238bc408480b0cfd79c323fef905887 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.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,
index ce225f801eb70442be8cf15b5df0194bee270f65..b73e9f2cda6cbbe1e315016b72b98b8bbb3cf364 100644 (file)
  * 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
index fb5e1fcf45140188a7b450dc42c2461804676fab..5e49b1a3dd8aa1b4706afbdb3c12bdcff0cf83e7 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.60 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.61 2009/01/22 20:16:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  * Note that neither relname nor refname of these entries are necessarily
  * unique; searching the rtable by name is a bad idea.
  *
+ * p_joinexprs: list of JoinExpr nodes associated with p_rtable entries.
+ * This is one-for-one with p_rtable, but contains NULLs for non-join
+ * RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins.
+ *
  * p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that
  * will become the fromlist of the query's top-level FromExpr node.
  *
@@ -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 */
index 76622af25d34aafa57238f3a2ca906a575956923..cb133fced66e52e1a3cfb0f4eb3261e9552e459c 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/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,
index 02cf425619e56859b92ba43a0cf8702ff60f4583..fed2de5b74ef20991b2a196e69aaa1c4a017ff64 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.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);
index 178f4221fca4614afd7fe54da14631aded6aa83d..6eb851a378ca63c448dbbe5d369d5c412793528e 100644 (file)
@@ -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
index f4a2bd8d8f50c571962e31b88ddffcb20353dea8..88d1ab3b78f79e678031b8546bf798dbd3d183d3 100644 (file)
@@ -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;
index 63532f7e095702e7d6df93e0bc962dd53003544d..dda20db85568ce3d797f1060f35a8870b0257144 100644 (file)
@@ -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;