Support column-level privileges, as required by SQL standard.
authorTom Lane
Thu, 22 Jan 2009 20:16:10 +0000 (20:16 +0000)
committerTom Lane
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 @@
-
+
 
       
      
 
+     
+      attacl
+      aclitem[]
+      
+      
+       Column-level access privileges, if any have been granted specifically
+       on this column
+      
+     
+
     
    
   
       The OID of the specific dependent object
      
 
+     
+      objsubid
+      int4
+      
+      
+       For a table column, this is the column number (the
+       objid and classid refer to the
+       table itself).  For all other object types, this column is zero
+      
+     
+
      
       refclassid
       oid
index dafb8ffb523885fda8d99b1804a72d4380a8a10f..ceda72c141d285c3151a8c9185311e58a52a1f3f 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -26,6 +26,11 @@ GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
     ON [ TABLE ] tablename [, ...]
     TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
 
+GRANT { { SELECT | INSERT | UPDATE | REFERENCES } ( column [, ...] )
+    [,...] | ALL [ PRIVILEGES ] ( column [, ...] ) }
+    ON [ TABLE ] tablename [, ...]
+    TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
 GRANT { { USAGE | SELECT | UPDATE }
     [,...] | ALL [ PRIVILEGES ] }
     ON SEQUENCE sequencename [, ...]
@@ -68,7 +73,7 @@ GRANT role [, ...] TO 
 
   
    The GRANT 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 role [, ...] TO 
   
    Depending on the type of object, the initial default privileges might
    include granting some privileges to PUBLIC.
-   The default is no public access for tables, schemas, and tablespaces;
+   The default is no public access for tables, columns, schemas, and
+   tablespaces;
    CONNECT privilege and TEMP table creation privilege
    for databases;
    EXECUTE privilege for functions; and
@@ -145,7 +151,8 @@ GRANT role [, ...] TO 
      
       
        Allows  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
         TO.
        This privilege is also needed to reference existing column values in
@@ -162,7 +169,9 @@ GRANT role [, ...] TO 
      
       
        Allows  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 INSERT
+       command (other columns will therefore receive default values).
        Also allows  FROM.
       
      
@@ -173,14 +182,14 @@ GRANT role [, ...] TO 
      
       
        Allows  of any
-       column of the specified table.
+       column, or the specific columns listed, of the specified table.
        (In practice, any nontrivial UPDATE command will require
        SELECT privilege as well, since it must reference table
        columns to determine which rows to update, and/or to compute new
        values for columns.)
        SELECT ... FOR UPDATE
        and SELECT ... FOR SHARE
-       also require this privilege, in addition to the
+       also require this privilege on at least one column, in addition to the
        SELECT privilege.  For sequences, this
        privilege allows the use of the nextval and
        setval functions.
@@ -217,7 +226,8 @@ GRANT role [, ...] TO 
       
        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.
       
      
     
@@ -373,6 +383,14 @@ GRANT role [, ...] TO 
     to revoke access privileges.
    
 
+   
+    A user may perform SELECT, 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.
+   
+
    
     When a non-owner of an object attempts to GRANT privileges
     on the object, the command will fail outright if the user has no
@@ -428,33 +446,27 @@ GRANT role [, ...] TO 
    
 
    
-    Granting permission on a table does not automatically extend 
-    permissions to any sequences used by the table, including 
-    sequences tied to 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 SERIAL columns.  Permissions on
+    sequences must be set separately.
    
 
    
-    Currently, PostgreSQL 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.
-   
-
-   
-    Use 's \z command
-    to obtain information about existing privileges, for example:
+    Use 's \dp command
+    to obtain information about existing privileges for tables and
+    columns.  For example:
 
-=> \z mytable
-                Access privileges
- Schema |  Name   | Type  |  Access privileges   
---------+---------+-------+-----------------------
- public | mytable | table | miriam=arwdDxt/miriam
-                          : =r/miriam
-                          : admin=arw/miriam
+=> \dp mytable
+                              Access privileges
+ Schema |  Name   | Type  |   Access privileges   | Column access privileges 
+--------+---------+-------+-----------------------+--------------------------
+ public | mytable | table | miriam=arwdDxt/miriam | col1:
+                          : =r/miriam             :   miriam_rw=rw/miriam
+                          : admin=arw/miriam        
 (1 row)
 
-    The entries shown by \z are interpreted thus:
+    The entries shown by \dp are interpreted thus:
 
       rolename=xxxx -- privileges granted to a role
               =xxxx -- privileges granted to PUBLIC
@@ -471,7 +483,7 @@ GRANT role [, ...] TO 
                   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 role [, ...] TO 
 
 GRANT SELECT ON mytable TO PUBLIC;
 GRANT SELECT, UPDATE, INSERT ON mytable TO admin;
+GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw;
 
    
 
+   
+    For non-table objects there are other \d commands
+    that can display their privileges.
+   
+
    
     If the 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;
     REVOKE on an object
     will instantiate the default privileges (producing, for example,
     {miriam=arwdDxt/miriam}) and then modify them per the
-    specified request.
+    specified request.  Entries are shown in Column access
+    privileges only for columns with nondefault privileges.
    
 
    
@@ -562,11 +581,6 @@ GRANT admins TO joe;
     _SYSTEM, the owner cannot revoke these rights.
    
 
-   
-    PostgreSQL does not support the SQL-standard 
-    functionality of setting privileges for individual columns.
-   
-
    
     The SQL standard provides for a USAGE privilege
     on other kinds of objects: character sets, collations,
index 98a1ca28b7923e0452b79a4c49c3423b5bffe764..a2a52d8ba429c3d82f5d943cf5f76faa91c4c46d 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -69,11 +69,14 @@ INSERT INTO table [ ( 
 
   
    You must have INSERT privilege on a table in
-   order to insert into it, and SELECT privilege on it to
-   use RETURNING.  If you use the 
+   order to insert into it.  If a column list is specified, you only
+   need INSERT privilege on the listed columns.
+   Use of the RETURNING clause requires SELECT
+   privilege on all columns mentioned in RETURNING.
+   If you use the 
    class="PARAMETER">query clause to insert rows from a
-   query, you also need to have SELECT privilege on
-   any table used in the query.
+   query, you of course need to have SELECT privilege on
+   any table or column used in the query.
   
  
 
index c8e91e0a159aa2a14d94508418ea418032be7dd9..0f967770c1158bb811d8b2f15af45d7371f4df8b 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -28,6 +28,13 @@ REVOKE [ GRANT OPTION FOR ]
     FROM { [ GROUP ] rolename | PUBLIC } [, ...]
     [ CASCADE | RESTRICT ]
 
+REVOKE [ GRANT OPTION FOR ]
+    { { SELECT | INSERT | UPDATE | REFERENCES } ( column [, ...] )
+    [,...] | ALL [ PRIVILEGES ] ( column [, ...] ) }
+    ON [ TABLE ] tablename [, ...]
+    FROM { [ GROUP ] rolename | 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.
   
 
+  
+   When revoking privileges on a table, the corresponding column privileges
+   (if any) are automatically revoked on each column of the table, as well.
+  
+
   
    When revoking membership in a role, GRANT OPTION is instead
    called ADMIN OPTION, but the behavior is similar.
@@ -143,9 +155,11 @@ REVOKE [ ADMIN OPTION FOR ]
   Notes
 
   
-   Use 's \z command to
-   display the privileges granted on existing objects.  See 
-   linkend="sql-grant" endterm="sql-grant-title"> for information about the format.
+   Use 's \dp command to
+   display the privileges granted on existing tables and columns.  See 
+   linkend="sql-grant" endterm="sql-grant-title"> for information about the
+   format.  For non-table objects there are other \d commands
+   that can display their privileges.
   
 
   
index ba14437764f9cb07b2051ab6c674163d159f4136..5cedb1cf3186da1ad1ae22108abc67c91155870b 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -186,10 +186,11 @@ TABLE { [ ONLY ] table_name [ * ] |
   
 
   
-   You must have SELECT privilege on a table to
-   read its values.  The use of FOR UPDATE or
-   FOR SHARE requires
-   UPDATE privilege as well.
+   You must have SELECT privilege on each column used
+   in a SELECT command.  The use of FOR UPDATE
+   or FOR SHARE requires
+   UPDATE privilege as well (for at least one column
+   of each table so selected).
   
  
 
index 2464bf16f93f7df83584860042916c58903ab7b4..8f32c83dc44d3c20e814bcb79b258ed1d7772786 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -66,9 +66,10 @@ UPDATE [ ONLY ] table [ [ AS ] 
   
 
   
-   You must have the UPDATE privilege on the table
-   to update it, as well as the SELECT
-   privilege to any table whose values are read in the
+   You must have the UPDATE privilege on the table,
+   or at least on the column(s) that are listed to be updated.
+   You must also have the SELECT
+   privilege on any column whose values are read in the
    expressions or
    condition.
   
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         iso_level opt_encoding
 %type    grantee
 %type    grantee_list
-%type <str>        privilege
+%type <accesspriv> privilege
 %type    privileges privilege_list
 %type  privilege_target
 %type  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 
+ * 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;