]> granicus.if.org Git - postgresql/commitdiff
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;