diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index bd267dcb75..4fae50cd32 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,4 +1,4 @@ - + @@ -1028,6 +1028,16 @@ + + attacl + aclitem[] + + + Column-level access privileges, if any have been granted specifically + on this column + + + @@ -4250,6 +4260,17 @@ 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 diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml index dafb8ffb52..ceda72c141 100644 --- a/doc/src/sgml/ref/grant.sgml +++ b/doc/src/sgml/ref/grant.sgml @@ -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 + + 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 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, diff --git a/doc/src/sgml/ref/insert.sgml b/doc/src/sgml/ref/insert.sgml index 98a1ca28b7..a2a52d8ba4 100644 --- a/doc/src/sgml/ref/insert.sgml +++ b/doc/src/sgml/ref/insert.sgml @@ -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 INSERT privilege on the listed columns. + Use of the RETURNING clause requires SELECT + privilege on all columns mentioned in RETURNING. + If you use the 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. diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml index c8e91e0a15..0f967770c1 100644 --- a/doc/src/sgml/ref/revoke.sgml +++ b/doc/src/sgml/ref/revoke.sgml @@ -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 for information about the format. + Use 's \dp command to + display the privileges granted on existing tables and columns. See for information about the + format. For non-table objects there are other \d commands + that can display their privileges. diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml index ba14437764..5cedb1cf31 100644 --- a/doc/src/sgml/ref/select.sgml +++ b/doc/src/sgml/ref/select.sgml @@ -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). diff --git a/doc/src/sgml/ref/update.sgml b/doc/src/sgml/ref/update.sgml index 2464bf16f9..8f32c83dc4 100644 --- a/doc/src/sgml/ref/update.sgml +++ b/doc/src/sgml/ref/update.sgml @@ -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. diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 4958299a2e..ea16913c8e 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.124 2009/01/01 17:23:34 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.125 2009/01/22 20:16:00 tgl Exp $ * * NOTES * some of the executor utility code such as "ExecTypeFromTL" should be @@ -53,10 +53,14 @@ CreateTemplateTupleDesc(int natts, bool hasoid) * struct pointer alignment requirement, and hence we don't need to insert * alignment padding between the struct and the array of attribute row * pointers. + * + * Note: Only the fixed part of pg_attribute rows is included in tuple + * descriptors, so we only need ATTRIBUTE_FIXED_PART_SIZE space + * per attr. That might need alignment padding, however. */ attroffset = sizeof(struct tupleDesc) + natts * sizeof(Form_pg_attribute); attroffset = MAXALIGN(attroffset); - stg = palloc(attroffset + natts * MAXALIGN(ATTRIBUTE_TUPLE_SIZE)); + stg = palloc(attroffset + natts * MAXALIGN(ATTRIBUTE_FIXED_PART_SIZE)); desc = (TupleDesc) stg; if (natts > 0) @@ -70,7 +74,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid) for (i = 0; i < natts; i++) { attrs[i] = (Form_pg_attribute) stg; - stg += MAXALIGN(ATTRIBUTE_TUPLE_SIZE); + stg += MAXALIGN(ATTRIBUTE_FIXED_PART_SIZE); } } else @@ -139,7 +143,7 @@ CreateTupleDescCopy(TupleDesc tupdesc) for (i = 0; i < desc->natts; i++) { - memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE); + memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_FIXED_PART_SIZE); desc->attrs[i]->attnotnull = false; desc->attrs[i]->atthasdef = false; } @@ -166,7 +170,7 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) for (i = 0; i < desc->natts; i++) { - memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE); + memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_FIXED_PART_SIZE); } if (constr) @@ -356,6 +360,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) return false; if (attr1->attinhcount != attr2->attinhcount) return false; + /* attacl is ignored, since it's not even present... */ } if (tupdesc1->constr != NULL) @@ -471,6 +476,7 @@ TupleDescInitEntry(TupleDesc desc, att->attisdropped = false; att->attislocal = true; att->attinhcount = 0; + /* attacl is not set because it's not present in tupledescs */ tuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(oidtypeid), diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 00b52dce80..19aab42554 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.248 2009/01/01 17:23:36 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.249 2009/01/22 20:16:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -633,7 +633,7 @@ boot_openrel(char *relname) closerel(NULL); elog(DEBUG4, "open relation %s, attrsize %d", - relname, (int) ATTRIBUTE_TUPLE_SIZE); + relname, (int) ATTRIBUTE_FIXED_PART_SIZE); boot_reldesc = heap_openrv(makeRangeVar(NULL, relname, -1), NoLock); numattr = boot_reldesc->rd_rel->relnatts; @@ -643,7 +643,7 @@ boot_openrel(char *relname) attrtypes[i] = AllocateAttribute(); memmove((char *) attrtypes[i], (char *) boot_reldesc->rd_att->attrs[i], - ATTRIBUTE_TUPLE_SIZE); + ATTRIBUTE_FIXED_PART_SIZE); { Form_pg_attribute at = attrtypes[i]; @@ -709,7 +709,7 @@ DefineAttr(char *name, char *type, int attnum) if (attrtypes[attnum] == NULL) attrtypes[attnum] = AllocateAttribute(); - MemSet(attrtypes[attnum], 0, ATTRIBUTE_TUPLE_SIZE); + MemSet(attrtypes[attnum], 0, ATTRIBUTE_FIXED_PART_SIZE); namestrcpy(&attrtypes[attnum]->attname, name); elog(DEBUG4, "column %s %s", NameStr(attrtypes[attnum]->attname), type); @@ -1017,16 +1017,19 @@ boot_get_type_io_data(Oid typid, /* ---------------- * AllocateAttribute + * + * Note: bootstrap never sets any per-column ACLs, so we only need + * ATTRIBUTE_FIXED_PART_SIZE space per attribute. * ---------------- */ static Form_pg_attribute AllocateAttribute(void) { - Form_pg_attribute attribute = (Form_pg_attribute) malloc(ATTRIBUTE_TUPLE_SIZE); + Form_pg_attribute attribute = (Form_pg_attribute) malloc(ATTRIBUTE_FIXED_PART_SIZE); if (!PointerIsValid(attribute)) elog(FATAL, "out of memory"); - MemSet(attribute, 0, ATTRIBUTE_TUPLE_SIZE); + MemSet(attribute, 0, ATTRIBUTE_FIXED_PART_SIZE); return attribute; } diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index ad32f49e7d..b49c80e485 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.151 2009/01/01 17:23:36 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.152 2009/01/22 20:16:00 tgl Exp $ * * NOTES * See acl.h. @@ -61,14 +61,23 @@ static void ExecGrant_Namespace(InternalGrant *grantStmt); static void ExecGrant_Tablespace(InternalGrant *grantStmt); static List *objectNamesToOids(GrantObjectType objtype, List *objnames); +static void expand_col_privileges(List *colnames, Oid table_oid, + AclMode this_privileges, + AclMode *col_privileges, + int num_col_privileges); +static void expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, + AclMode this_privileges, + AclMode *col_privileges, + int num_col_privileges); static AclMode string_to_privilege(const char *privname); static const char *privilege_to_string(AclMode privilege); static AclMode restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs, AclMode privileges, Oid objectId, Oid grantorId, - AclObjectKind objkind, char *objname); -static AclMode pg_aclmask(AclObjectKind objkind, Oid table_oid, Oid roleid, - AclMode mask, AclMaskHow how); + AclObjectKind objkind, const char *objname, + AttrNumber att_number, const char *colname); +static AclMode pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, + Oid roleid, AclMode mask, AclMaskHow how); #ifdef ACLDEBUG @@ -118,7 +127,7 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant, AclItem aclitem; Acl *newer_acl; - aclitem. ai_grantee = lfirst_oid(j); + aclitem.ai_grantee = lfirst_oid(j); /* * Grant options can only be granted to individual roles, not PUBLIC. @@ -131,7 +140,7 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant, (errcode(ERRCODE_INVALID_GRANT_OPERATION), errmsg("grant options can only be granted to roles"))); - aclitem. ai_grantor = grantorId; + aclitem.ai_grantor = grantorId; /* * The asymmetry in the conditions here comes from the spec. In @@ -165,13 +174,17 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant, static AclMode restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs, AclMode privileges, Oid objectId, Oid grantorId, - AclObjectKind objkind, char *objname) + AclObjectKind objkind, const char *objname, + AttrNumber att_number, const char *colname) { AclMode this_privileges; AclMode whole_mask; switch (objkind) { + case ACL_KIND_COLUMN: + whole_mask = ACL_ALL_RIGHTS_COLUMN; + break; case ACL_KIND_CLASS: whole_mask = ACL_ALL_RIGHTS_RELATION; break; @@ -212,10 +225,15 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs, */ if (avail_goptions == ACL_NO_RIGHTS) { - if (pg_aclmask(objkind, objectId, grantorId, + if (pg_aclmask(objkind, objectId, att_number, grantorId, whole_mask | ACL_GRANT_OPTION_FOR(whole_mask), ACLMASK_ANY) == ACL_NO_RIGHTS) - aclcheck_error(ACLCHECK_NO_PRIV, objkind, objname); + { + if (objkind == ACL_KIND_COLUMN && colname) + aclcheck_error_col(ACLCHECK_NO_PRIV, objkind, objname, colname); + else + aclcheck_error(ACLCHECK_NO_PRIV, objkind, objname); + } } /* @@ -271,12 +289,11 @@ ExecuteGrantStmt(GrantStmt *stmt) istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects); /* all_privs to be filled below */ /* privileges to be filled below */ - istmt.grantees = NIL; - /* filled below */ + istmt.col_privs = NIL; /* may get filled below */ + istmt.grantees = NIL; /* filled below */ istmt.grant_option = stmt->grant_option; istmt.behavior = stmt->behavior; - /* * Convert the PrivGrantee list into an Oid list. Note that at this point * we insert an ACL_ID_PUBLIC into the list if an empty role name is @@ -297,7 +314,8 @@ ExecuteGrantStmt(GrantStmt *stmt) } /* - * Convert stmt->privileges, a textual list, into an AclMode bitmask. + * Convert stmt->privileges, a list of AccessPriv nodes, into an + * AclMode bitmask. Note: objtype can't be ACL_OBJECT_COLUMN. */ switch (stmt->objtype) { @@ -367,8 +385,26 @@ ExecuteGrantStmt(GrantStmt *stmt) foreach(cell, stmt->privileges) { - char *privname = strVal(lfirst(cell)); - AclMode priv = string_to_privilege(privname); + AccessPriv *privnode = (AccessPriv *) lfirst(cell); + AclMode priv; + + /* + * If it's a column-level specification, we just set it aside + * in col_privs for the moment; but insist it's for a relation. + */ + if (privnode->cols) + { + if (stmt->objtype != ACL_OBJECT_RELATION) + ereport(ERROR, + (errcode(ERRCODE_INVALID_GRANT_OPERATION), + errmsg("column privileges are only valid for relations"))); + istmt.col_privs = lappend(istmt.col_privs, privnode); + continue; + } + + if (privnode->priv_name == NULL) /* parser mistake? */ + elog(ERROR, "AccessPriv node must specify privilege or columns"); + priv = string_to_privilege(privnode->priv_name); if (priv & ~((AclMode) all_privileges)) ereport(ERROR, @@ -385,7 +421,9 @@ ExecuteGrantStmt(GrantStmt *stmt) /* * ExecGrantStmt_oids * - * "Internal" entrypoint for granting and revoking privileges. + * "Internal" entrypoint for granting and revoking privileges. This is + * exported for pg_shdepend.c to use in revoking privileges when dropping + * a role. */ void ExecGrantStmt_oids(InternalGrant *istmt) @@ -571,6 +609,234 @@ objectNamesToOids(GrantObjectType objtype, List *objnames) return objects; } +/* + * expand_col_privileges + * + * OR the specified privilege(s) into per-column array entries for each + * specified attribute. The per-column array is indexed starting at + * FirstLowInvalidHeapAttributeNumber, up to relation's last attribute. + */ +static void +expand_col_privileges(List *colnames, Oid table_oid, + AclMode this_privileges, + AclMode *col_privileges, + int num_col_privileges) +{ + ListCell *cell; + + foreach(cell, colnames) + { + char *colname = strVal(lfirst(cell)); + AttrNumber attnum; + + attnum = get_attnum(table_oid, colname); + if (attnum == InvalidAttrNumber) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + colname, get_rel_name(table_oid)))); + attnum -= FirstLowInvalidHeapAttributeNumber; + if (attnum <= 0 || attnum >= num_col_privileges) + elog(ERROR, "column number out of range"); /* safety check */ + col_privileges[attnum] |= this_privileges; + } +} + +/* + * expand_all_col_privileges + * + * OR the specified privilege(s) into per-column array entries for each valid + * attribute of a relation. The per-column array is indexed starting at + * FirstLowInvalidHeapAttributeNumber, up to relation's last attribute. + */ +static void +expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, + AclMode this_privileges, + AclMode *col_privileges, + int num_col_privileges) +{ + AttrNumber curr_att; + + Assert(classForm->relnatts - FirstLowInvalidHeapAttributeNumber < num_col_privileges); + for (curr_att = FirstLowInvalidHeapAttributeNumber + 1; + curr_att <= classForm->relnatts; + curr_att++) + { + HeapTuple attTuple; + bool isdropped; + + if (curr_att == InvalidAttrNumber) + continue; + + /* Skip OID column if it doesn't exist */ + if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids) + continue; + + /* Views don't have any system columns at all */ + if (classForm->relkind == RELKIND_VIEW && curr_att < 0) + continue; + + attTuple = SearchSysCache(ATTNUM, + ObjectIdGetDatum(table_oid), + Int16GetDatum(curr_att), + 0, 0); + if (!HeapTupleIsValid(attTuple)) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + curr_att, table_oid); + + isdropped = ((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped; + + ReleaseSysCache(attTuple); + + /* ignore dropped columns */ + if (isdropped) + continue; + + col_privileges[curr_att - FirstLowInvalidHeapAttributeNumber] |= this_privileges; + } +} + +/* + * This processes attributes, but expects to be called from + * ExecGrant_Relation, not directly from ExecGrantStmt. + */ +static void +ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname, + AttrNumber attnum, Oid ownerId, AclMode col_privileges, + Relation attRelation, const Acl *old_rel_acl) +{ + HeapTuple attr_tuple; + Form_pg_attribute pg_attribute_tuple; + Acl *old_acl; + Acl *new_acl; + Acl *merged_acl; + Datum aclDatum; + bool isNull; + Oid grantorId; + AclMode avail_goptions; + bool need_update; + HeapTuple newtuple; + Datum values[Natts_pg_attribute]; + bool nulls[Natts_pg_attribute]; + bool replaces[Natts_pg_attribute]; + int noldmembers; + int nnewmembers; + Oid *oldmembers; + Oid *newmembers; + + attr_tuple = SearchSysCache(ATTNUM, + ObjectIdGetDatum(relOid), + Int16GetDatum(attnum), + 0, 0); + if (!HeapTupleIsValid(attr_tuple)) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + attnum, relOid); + pg_attribute_tuple = (Form_pg_attribute) GETSTRUCT(attr_tuple); + + /* + * Get working copy of existing ACL. If there's no ACL, + * substitute the proper default. + */ + aclDatum = SysCacheGetAttr(ATTNUM, attr_tuple, Anum_pg_attribute_attacl, + &isNull); + if (isNull) + old_acl = acldefault(ACL_OBJECT_COLUMN, ownerId); + else + old_acl = DatumGetAclPCopy(aclDatum); + + /* + * In select_best_grantor we should consider existing table-level ACL bits + * as well as the per-column ACL. Build a new ACL that is their + * concatenation. (This is a bit cheap and dirty compared to merging + * them properly with no duplications, but it's all we need here.) + */ + merged_acl = aclconcat(old_rel_acl, old_acl); + + /* Determine ID to do the grant as, and available grant options */ + select_best_grantor(GetUserId(), col_privileges, + merged_acl, ownerId, + &grantorId, &avail_goptions); + + pfree(merged_acl); + + /* + * Restrict the privileges to what we can actually grant, and emit + * the standards-mandated warning and error messages. Note: we don't + * track whether the user actually used the ALL PRIVILEGES(columns) + * syntax for each column; we just approximate it by whether all the + * possible privileges are specified now. Since the all_privs flag only + * determines whether a warning is issued, this seems close enough. + */ + col_privileges = + restrict_and_check_grant(istmt->is_grant, avail_goptions, + (col_privileges == ACL_ALL_RIGHTS_COLUMN), + col_privileges, + relOid, grantorId, ACL_KIND_COLUMN, + relname, attnum, + NameStr(pg_attribute_tuple->attname)); + + /* + * Generate new ACL. + * + * We need the members of both old and new ACLs so we can correct + * the shared dependency information. + */ + noldmembers = aclmembers(old_acl, &oldmembers); + + new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, + istmt->grant_option, + istmt->behavior, istmt->grantees, + col_privileges, grantorId, + ownerId); + + nnewmembers = aclmembers(new_acl, &newmembers); + + /* finished building new ACL value, now insert it */ + MemSet(values, 0, sizeof(values)); + MemSet(nulls, false, sizeof(nulls)); + MemSet(replaces, false, sizeof(replaces)); + + /* + * If the updated ACL is empty, we can set attacl to null, and maybe + * even avoid an update of the pg_attribute row. This is worth testing + * because we'll come through here multiple times for any relation-level + * REVOKE, even if there were never any column GRANTs. Note we are + * assuming that the "default" ACL state for columns is empty. + */ + if (ACL_NUM(new_acl) > 0) + { + values[Anum_pg_attribute_attacl - 1] = PointerGetDatum(new_acl); + need_update = true; + } + else + { + nulls[Anum_pg_attribute_attacl - 1] = true; + need_update = !isNull; + } + replaces[Anum_pg_attribute_attacl - 1] = true; + + if (need_update) + { + newtuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attRelation), + values, nulls, replaces); + + simple_heap_update(attRelation, &newtuple->t_self, newtuple); + + /* keep the catalog indexes up to date */ + CatalogUpdateIndexes(attRelation, newtuple); + + /* Update the shared dependency ACL info */ + updateAclDependencies(RelationRelationId, relOid, attnum, + ownerId, istmt->is_grant, + noldmembers, oldmembers, + nnewmembers, newmembers); + } + + pfree(new_acl); + + ReleaseSysCache(attr_tuple); +} + /* * This processes both sequences and non-sequences. */ @@ -578,9 +844,11 @@ static void ExecGrant_Relation(InternalGrant *istmt) { Relation relation; + Relation attRelation; ListCell *cell; relation = heap_open(RelationRelationId, RowExclusiveLock); + attRelation = heap_open(AttributeRelationId, RowExclusiveLock); foreach(cell, istmt->objects) { @@ -588,21 +856,15 @@ ExecGrant_Relation(InternalGrant *istmt) Datum aclDatum; Form_pg_class pg_class_tuple; bool isNull; - AclMode avail_goptions; AclMode this_privileges; + AclMode *col_privileges; + int num_col_privileges; + bool have_col_privileges; Acl *old_acl; - Acl *new_acl; - Oid grantorId; + Acl *old_rel_acl; Oid ownerId; HeapTuple tuple; - HeapTuple newtuple; - Datum values[Natts_pg_class]; - bool nulls[Natts_pg_class]; - bool replaces[Natts_pg_class]; - int noldmembers; - int nnewmembers; - Oid *oldmembers; - Oid *newmembers; + ListCell *cell_colprivs; tuple = SearchSysCache(RELOID, ObjectIdGetDatum(relOid), @@ -655,9 +917,9 @@ ExecGrant_Relation(InternalGrant *istmt) if (pg_class_tuple->relkind == RELKIND_SEQUENCE) { /* - * For backward compatibility, throw just a warning for + * For backward compatibility, just throw a warning for * invalid sequence permissions when using the non-sequence - * GRANT syntax is used. + * GRANT syntax. */ if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_SEQUENCE)) { @@ -668,7 +930,7 @@ ExecGrant_Relation(InternalGrant *istmt) */ ereport(WARNING, (errcode(ERRCODE_INVALID_GRANT_OPERATION), - errmsg("sequence \"%s\" only supports USAGE, SELECT, and UPDATE", + errmsg("sequence \"%s\" only supports USAGE, SELECT, and UPDATE privileges", NameStr(pg_class_tuple->relname)))); this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE; } @@ -676,7 +938,7 @@ ExecGrant_Relation(InternalGrant *istmt) else { if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION)) - + { /* * USAGE is the only permission supported by sequences but * not by non-sequences. Don't mention the object name @@ -686,9 +948,36 @@ ExecGrant_Relation(InternalGrant *istmt) ereport(ERROR, (errcode(ERRCODE_INVALID_GRANT_OPERATION), errmsg("invalid privilege type USAGE for table"))); + } } } + /* + * Set up array in which we'll accumulate any column privilege bits + * that need modification. The array is indexed such that entry [0] + * corresponds to FirstLowInvalidHeapAttributeNumber. + */ + num_col_privileges = pg_class_tuple->relnatts - FirstLowInvalidHeapAttributeNumber + 1; + col_privileges = (AclMode *) palloc0(num_col_privileges * sizeof(AclMode)); + have_col_privileges = false; + + /* + * If we are revoking relation privileges that are also column + * privileges, we must implicitly revoke them from each column too, + * per SQL spec. (We don't need to implicitly add column privileges + * during GRANT because the permissions-checking code always checks + * both relation and per-column privileges.) + */ + if (!istmt->is_grant && + (this_privileges & ACL_ALL_RIGHTS_COLUMN) != 0) + { + expand_all_col_privileges(relOid, pg_class_tuple, + this_privileges & ACL_ALL_RIGHTS_COLUMN, + col_privileges, + num_col_privileges); + have_col_privileges = true; + } + /* * Get owner ID and working copy of existing ACL. If there's no ACL, * substitute the proper default. @@ -703,67 +992,160 @@ ExecGrant_Relation(InternalGrant *istmt) else old_acl = DatumGetAclPCopy(aclDatum); - /* Determine ID to do the grant as, and available grant options */ - select_best_grantor(GetUserId(), this_privileges, - old_acl, ownerId, - &grantorId, &avail_goptions); + /* Need an extra copy of original rel ACL for column handling */ + old_rel_acl = aclcopy(old_acl); /* - * Restrict the privileges to what we can actually grant, and emit the - * standards-mandated warning and error messages. + * Handle relation-level privileges, if any were specified */ - this_privileges = - restrict_and_check_grant(istmt->is_grant, avail_goptions, - istmt->all_privs, this_privileges, - relOid, grantorId, - pg_class_tuple->relkind == RELKIND_SEQUENCE - ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS, - NameStr(pg_class_tuple->relname)); + if (this_privileges != ACL_NO_RIGHTS) + { + AclMode avail_goptions; + Acl *new_acl; + Oid grantorId; + HeapTuple newtuple; + Datum values[Natts_pg_class]; + bool nulls[Natts_pg_class]; + bool replaces[Natts_pg_class]; + int noldmembers; + int nnewmembers; + Oid *oldmembers; + Oid *newmembers; + + /* Determine ID to do the grant as, and available grant options */ + select_best_grantor(GetUserId(), this_privileges, + old_acl, ownerId, + &grantorId, &avail_goptions); + + /* + * Restrict the privileges to what we can actually grant, and emit + * the standards-mandated warning and error messages. + */ + this_privileges = + restrict_and_check_grant(istmt->is_grant, avail_goptions, + istmt->all_privs, this_privileges, + relOid, grantorId, + pg_class_tuple->relkind == RELKIND_SEQUENCE + ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS, + NameStr(pg_class_tuple->relname), + 0, NULL); + + /* + * Generate new ACL. + * + * We need the members of both old and new ACLs so we can correct + * the shared dependency information. + */ + noldmembers = aclmembers(old_acl, &oldmembers); + + new_acl = merge_acl_with_grant(old_acl, + istmt->is_grant, + istmt->grant_option, + istmt->behavior, + istmt->grantees, + this_privileges, + grantorId, + ownerId); + + nnewmembers = aclmembers(new_acl, &newmembers); + + /* finished building new ACL value, now insert it */ + MemSet(values, 0, sizeof(values)); + MemSet(nulls, false, sizeof(nulls)); + MemSet(replaces, false, sizeof(replaces)); + + replaces[Anum_pg_class_relacl - 1] = true; + values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl); + + newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), + values, nulls, replaces); + + simple_heap_update(relation, &newtuple->t_self, newtuple); + + /* keep the catalog indexes up to date */ + CatalogUpdateIndexes(relation, newtuple); + + /* Update the shared dependency ACL info */ + updateAclDependencies(RelationRelationId, relOid, 0, + ownerId, istmt->is_grant, + noldmembers, oldmembers, + nnewmembers, newmembers); + + pfree(new_acl); + } /* - * Generate new ACL. - * - * We need the members of both old and new ACLs so we can correct the - * shared dependency information. + * Handle column-level privileges, if any were specified or implied. + * We first expand the user-specified column privileges into the + * array, and then iterate over all nonempty array entries. */ - noldmembers = aclmembers(old_acl, &oldmembers); + foreach(cell_colprivs, istmt->col_privs) + { + AccessPriv *col_privs = (AccessPriv *) lfirst(cell_colprivs); - new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, - istmt->grant_option, istmt->behavior, - istmt->grantees, this_privileges, - grantorId, ownerId); + if (col_privs->priv_name == NULL) + this_privileges = ACL_ALL_RIGHTS_COLUMN; + else + this_privileges = string_to_privilege(col_privs->priv_name); - nnewmembers = aclmembers(new_acl, &newmembers); + if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_COLUMN)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_GRANT_OPERATION), + errmsg("invalid privilege type %s for column", + privilege_to_string(this_privileges)))); - /* finished building new ACL value, now insert it */ - MemSet(values, 0, sizeof(values)); - MemSet(nulls, false, sizeof(nulls)); - MemSet(replaces, false, sizeof(replaces)); + if (pg_class_tuple->relkind == RELKIND_SEQUENCE && + this_privileges & ~((AclMode) ACL_SELECT)) + { + /* + * The only column privilege allowed on sequences is SELECT. + * This is a warning not error because we do it that way + * for relation-level privileges. + */ + ereport(WARNING, + (errcode(ERRCODE_INVALID_GRANT_OPERATION), + errmsg("sequence \"%s\" only supports SELECT column privileges", + NameStr(pg_class_tuple->relname)))); - replaces[Anum_pg_class_relacl - 1] = true; - values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl); + this_privileges &= (AclMode) ACL_SELECT; + } - newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces); + expand_col_privileges(col_privs->cols, relOid, + this_privileges, + col_privileges, + num_col_privileges); + have_col_privileges = true; + } - simple_heap_update(relation, &newtuple->t_self, newtuple); + if (have_col_privileges) + { + AttrNumber i; - /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relation, newtuple); + for (i = 0; i < num_col_privileges; i++) + { + if (col_privileges[i] == ACL_NO_RIGHTS) + continue; + ExecGrant_Attribute(istmt, + relOid, + NameStr(pg_class_tuple->relname), + i + FirstLowInvalidHeapAttributeNumber, + ownerId, + col_privileges[i], + attRelation, + old_rel_acl); + } + } - /* Update the shared dependency ACL info */ - updateAclDependencies(RelationRelationId, relOid, - ownerId, istmt->is_grant, - noldmembers, oldmembers, - nnewmembers, newmembers); + pfree(old_rel_acl); + pfree(col_privileges); ReleaseSysCache(tuple); - pfree(new_acl); - /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } + heap_close(attRelation, RowExclusiveLock); heap_close(relation, RowExclusiveLock); } @@ -833,7 +1215,8 @@ ExecGrant_Database(InternalGrant *istmt) restrict_and_check_grant(istmt->is_grant, avail_goptions, istmt->all_privs, istmt->privileges, datId, grantorId, ACL_KIND_DATABASE, - NameStr(pg_database_tuple->datname)); + NameStr(pg_database_tuple->datname), + 0, NULL); /* * Generate new ACL. @@ -867,7 +1250,7 @@ ExecGrant_Database(InternalGrant *istmt) CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ - updateAclDependencies(DatabaseRelationId, HeapTupleGetOid(tuple), + updateAclDependencies(DatabaseRelationId, HeapTupleGetOid(tuple), 0, ownerId, istmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); @@ -950,7 +1333,8 @@ ExecGrant_Fdw(InternalGrant *istmt) restrict_and_check_grant(istmt->is_grant, avail_goptions, istmt->all_privs, istmt->privileges, fdwid, grantorId, ACL_KIND_FDW, - NameStr(pg_fdw_tuple->fdwname)); + NameStr(pg_fdw_tuple->fdwname), + 0, NULL); /* * Generate new ACL. @@ -984,7 +1368,8 @@ ExecGrant_Fdw(InternalGrant *istmt) CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ - updateAclDependencies(ForeignDataWrapperRelationId, HeapTupleGetOid(tuple), + updateAclDependencies(ForeignDataWrapperRelationId, + HeapTupleGetOid(tuple), 0, ownerId, istmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); @@ -1066,7 +1451,8 @@ static void ExecGrant_ForeignServer(InternalGrant *istmt) restrict_and_check_grant(istmt->is_grant, avail_goptions, istmt->all_privs, istmt->privileges, srvid, grantorId, ACL_KIND_FOREIGN_SERVER, - NameStr(pg_server_tuple->srvname)); + NameStr(pg_server_tuple->srvname), + 0, NULL); /* * Generate new ACL. @@ -1100,7 +1486,8 @@ static void ExecGrant_ForeignServer(InternalGrant *istmt) CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ - updateAclDependencies(ForeignServerRelationId, HeapTupleGetOid(tuple), + updateAclDependencies(ForeignServerRelationId, + HeapTupleGetOid(tuple), 0, ownerId, istmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); @@ -1182,7 +1569,8 @@ ExecGrant_Function(InternalGrant *istmt) restrict_and_check_grant(istmt->is_grant, avail_goptions, istmt->all_privs, istmt->privileges, funcId, grantorId, ACL_KIND_PROC, - NameStr(pg_proc_tuple->proname)); + NameStr(pg_proc_tuple->proname), + 0, NULL); /* * Generate new ACL. @@ -1216,7 +1604,7 @@ ExecGrant_Function(InternalGrant *istmt) CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ - updateAclDependencies(ProcedureRelationId, funcId, + updateAclDependencies(ProcedureRelationId, funcId, 0, ownerId, istmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); @@ -1305,7 +1693,8 @@ ExecGrant_Language(InternalGrant *istmt) restrict_and_check_grant(istmt->is_grant, avail_goptions, istmt->all_privs, istmt->privileges, langId, grantorId, ACL_KIND_LANGUAGE, - NameStr(pg_language_tuple->lanname)); + NameStr(pg_language_tuple->lanname), + 0, NULL); /* * Generate new ACL. @@ -1339,7 +1728,7 @@ ExecGrant_Language(InternalGrant *istmt) CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ - updateAclDependencies(LanguageRelationId, HeapTupleGetOid(tuple), + updateAclDependencies(LanguageRelationId, HeapTupleGetOid(tuple), 0, ownerId, istmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); @@ -1422,7 +1811,8 @@ ExecGrant_Namespace(InternalGrant *istmt) restrict_and_check_grant(istmt->is_grant, avail_goptions, istmt->all_privs, istmt->privileges, nspid, grantorId, ACL_KIND_NAMESPACE, - NameStr(pg_namespace_tuple->nspname)); + NameStr(pg_namespace_tuple->nspname), + 0, NULL); /* * Generate new ACL. @@ -1456,7 +1846,7 @@ ExecGrant_Namespace(InternalGrant *istmt) CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ - updateAclDependencies(NamespaceRelationId, HeapTupleGetOid(tuple), + updateAclDependencies(NamespaceRelationId, HeapTupleGetOid(tuple), 0, ownerId, istmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); @@ -1545,7 +1935,8 @@ ExecGrant_Tablespace(InternalGrant *istmt) restrict_and_check_grant(istmt->is_grant, avail_goptions, istmt->all_privs, istmt->privileges, tblId, grantorId, ACL_KIND_TABLESPACE, - NameStr(pg_tablespace_tuple->spcname)); + NameStr(pg_tablespace_tuple->spcname), + 0, NULL); /* * Generate new ACL. @@ -1579,7 +1970,7 @@ ExecGrant_Tablespace(InternalGrant *istmt) CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ - updateAclDependencies(TableSpaceRelationId, tblId, + updateAclDependencies(TableSpaceRelationId, tblId, 0, ownerId, istmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); @@ -1677,6 +2068,8 @@ privilege_to_string(AclMode privilege) static const char *const no_priv_msg[MAX_ACL_KIND] = { + /* ACL_KIND_COLUMN */ + gettext_noop("permission denied for column %s"), /* ACL_KIND_CLASS */ gettext_noop("permission denied for relation %s"), /* ACL_KIND_SEQUENCE */ @@ -1713,6 +2106,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] = static const char *const not_owner_msg[MAX_ACL_KIND] = { + /* ACL_KIND_COLUMN */ + gettext_noop("must be owner of relation %s"), /* ACL_KIND_CLASS */ gettext_noop("must be owner of relation %s"), /* ACL_KIND_SEQUENCE */ @@ -1774,6 +2169,34 @@ aclcheck_error(AclResult aclerr, AclObjectKind objectkind, } +void +aclcheck_error_col(AclResult aclerr, AclObjectKind objectkind, + const char *objectname, const char *colname) +{ + switch (aclerr) + { + case ACLCHECK_OK: + /* no error, so return to caller */ + break; + case ACLCHECK_NO_PRIV: + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for column %s of relation %s", + colname, objectname))); + break; + case ACLCHECK_NOT_OWNER: + /* relation msg is OK since columns don't have separate owners */ + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg(not_owner_msg[objectkind], objectname))); + break; + default: + elog(ERROR, "unrecognized AclResult: %d", (int) aclerr); + break; + } +} + + /* Check if given user has rolcatupdate privilege according to pg_authid */ static bool has_rolcatupdate(Oid roleid) @@ -1800,11 +2223,15 @@ has_rolcatupdate(Oid roleid) * Relay for the various pg_*_mask routines depending on object kind */ static AclMode -pg_aclmask(AclObjectKind objkind, Oid table_oid, Oid roleid, +pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mask, AclMaskHow how) { switch (objkind) { + case ACL_KIND_COLUMN: + return + pg_class_aclmask(table_oid, roleid, mask, how) | + pg_attribute_aclmask(table_oid, attnum, roleid, mask, how); case ACL_KIND_CLASS: case ACL_KIND_SEQUENCE: return pg_class_aclmask(table_oid, roleid, mask, how); @@ -1830,15 +2257,105 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, Oid roleid, } } -/* - * Exported routine for examining a user's privileges for a table + +/* **************************************************************** + * Exported routines for examining a user's privileges for various objects * - * See aclmask() for a description of the API. + * See aclmask() for a description of the common API for these functions. * * Note: we give lookup failure the full ereport treatment because the - * has_table_privilege() family of functions allow users to pass - * any random OID to this function. Likewise for the sibling functions - * below. + * has_xxx_privilege() family of functions allow users to pass any random + * OID to these functions. + * **************************************************************** + */ + +/* + * Exported routine for examining a user's privileges for a column + * + * Note: this considers only privileges granted specifically on the column. + * It is caller's responsibility to take relation-level privileges into account + * as appropriate. (For the same reason, we have no special case for + * superuser-ness here.) + */ +AclMode +pg_attribute_aclmask(Oid table_oid, AttrNumber attnum, Oid roleid, + AclMode mask, AclMaskHow how) +{ + AclMode result; + HeapTuple classTuple; + HeapTuple attTuple; + Form_pg_class classForm; + Form_pg_attribute attributeForm; + Datum aclDatum; + bool isNull; + Acl *acl; + Oid ownerId; + + /* + * Must get the relation's tuple from pg_class (only needed for ownerId) + */ + classTuple = SearchSysCache(RELOID, + ObjectIdGetDatum(table_oid), + 0, 0, 0); + if (!HeapTupleIsValid(classTuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_TABLE), + errmsg("relation with OID %u does not exist", + table_oid))); + classForm = (Form_pg_class) GETSTRUCT(classTuple); + + ownerId = classForm->relowner; + + /* + * Next, get the column's ACL from pg_attribute + */ + attTuple = SearchSysCache(ATTNUM, + ObjectIdGetDatum(table_oid), + Int16GetDatum(attnum), + 0, 0); + if (!HeapTupleIsValid(attTuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("attribute %d of relation with OID %u does not exist", + attnum, table_oid))); + attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple); + + /* Throw error on dropped columns, too */ + if (attributeForm->attisdropped) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("attribute %d of relation with OID %u does not exist", + attnum, table_oid))); + + aclDatum = SysCacheGetAttr(ATTNUM, attTuple, Anum_pg_attribute_attacl, + &isNull); + + if (isNull) + { + /* No ACL, so build default ACL */ + acl = acldefault(ACL_OBJECT_COLUMN, ownerId); + aclDatum = (Datum) 0; + } + else + { + /* detoast column's ACL if necessary */ + acl = DatumGetAclP(aclDatum); + } + + result = aclmask(acl, roleid, ownerId, mask, how); + + /* if we have a detoasted copy, free it */ + if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) + pfree(acl); + + ReleaseSysCache(attTuple); + ReleaseSysCache(classTuple); + + return result; +} + +/* + * Exported routine for examining a user's privileges for a table */ AclMode pg_class_aclmask(Oid table_oid, Oid roleid, @@ -2376,6 +2893,115 @@ pg_foreign_server_aclmask(Oid srv_oid, Oid roleid, return result; } +/* + * Exported routine for checking a user's access privileges to a column + * + * Returns ACLCHECK_OK if the user has any of the privileges identified by + * 'mode'; otherwise returns a suitable error code (in practice, always + * ACLCHECK_NO_PRIV). + * + * As with pg_attribute_aclmask, only privileges granted directly on the + * column are considered here. + */ +AclResult +pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, + Oid roleid, AclMode mode) +{ + if (pg_attribute_aclmask(table_oid, attnum, roleid, mode, ACLMASK_ANY) != 0) + return ACLCHECK_OK; + else + return ACLCHECK_NO_PRIV; +} + +/* + * Exported routine for checking a user's access privileges to any/all columns + * + * If 'how' is ACLMASK_ANY, then returns ACLCHECK_OK if user has any of the + * privileges identified by 'mode' on any non-dropped column in the relation; + * otherwise returns a suitable error code (in practice, always + * ACLCHECK_NO_PRIV). + * + * If 'how' is ACLMASK_ALL, then returns ACLCHECK_OK if user has any of the + * privileges identified by 'mode' on all non-dropped columns in the relation + * (and there must be at least one such column); otherwise returns a suitable + * error code (in practice, always ACLCHECK_NO_PRIV). + * + * As with pg_attribute_aclmask, only privileges granted directly on the + * column(s) are considered here. + * + * Note: system columns are not considered here; there are cases where that + * might be appropriate but there are also cases where it wouldn't. + */ +AclResult +pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, AclMode mode, + AclMaskHow how) +{ + AclResult result; + HeapTuple classTuple; + Form_pg_class classForm; + AttrNumber nattrs; + AttrNumber curr_att; + + /* Must fetch pg_class row to check number of attributes */ + classTuple = SearchSysCache(RELOID, + ObjectIdGetDatum(table_oid), + 0, 0, 0); + if (!HeapTupleIsValid(classTuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_TABLE), + errmsg("relation with OID %u does not exist", + table_oid))); + classForm = (Form_pg_class) GETSTRUCT(classTuple); + + nattrs = classForm->relnatts; + + ReleaseSysCache(classTuple); + + /* + * Initialize result in case there are no non-dropped columns. We want + * to report failure in such cases for either value of 'how'. + */ + result = ACLCHECK_NO_PRIV; + + for (curr_att = 1; curr_att <= nattrs; curr_att++) + { + HeapTuple attTuple; + bool isdropped; + + attTuple = SearchSysCache(ATTNUM, + ObjectIdGetDatum(table_oid), + Int16GetDatum(curr_att), + 0, 0); + if (!HeapTupleIsValid(attTuple)) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + curr_att, table_oid); + + isdropped = ((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped; + + ReleaseSysCache(attTuple); + + /* ignore dropped columns */ + if (isdropped) + continue; + + if (pg_attribute_aclmask(table_oid, curr_att, roleid, + mode, ACLMASK_ANY) != 0) + { + result = ACLCHECK_OK; + if (how == ACLMASK_ANY) + break; /* succeed on any success */ + } + else + { + result = ACLCHECK_NO_PRIV; + if (how == ACLMASK_ALL) + break; /* fail on any failure */ + } + } + + return result; +} + /* * Exported routine for checking a user's access privileges to a table * diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 6f63232ff5..70c43cdec0 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.85 2009/01/01 17:23:36 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.86 2009/01/22 20:16:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -976,6 +976,13 @@ deleteOneObject(const ObjectAddress *object, Relation depRel) systable_endscan(scan); + /* + * Delete shared dependency references related to this object. Again, + * if subId = 0, remove records for sub-objects too. + */ + deleteSharedDependencyRecordsFor(object->classId, object->objectId, + object->objectSubId); + /* * Now delete the object itself, in an object-type-dependent way. */ @@ -987,13 +994,6 @@ deleteOneObject(const ObjectAddress *object, Relation depRel) */ DeleteComments(object->objectId, object->classId, object->objectSubId); - /* - * Delete shared dependency references related to this object. Sub-objects - * (columns) don't have dependencies on global objects, so skip them. - */ - if (object->objectSubId == 0) - deleteSharedDependencyRecordsFor(object->classId, object->objectId); - /* * CommandCounterIncrement here to ensure that preceding changes are all * visible to the next deletion step. diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 462a1aac61..b01edbe017 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.349 2009/01/01 17:23:36 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.350 2009/01/22 20:16:01 tgl Exp $ * * * INTERFACE ROUTINES @@ -112,37 +112,37 @@ static List *insert_ordered_unique_oid(List *list, Oid datum); static FormData_pg_attribute a1 = { 0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData), SelfItemPointerAttributeNumber, 0, -1, -1, - false, 'p', 's', true, false, false, true, 0 + false, 'p', 's', true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a2 = { 0, {"oid"}, OIDOID, 0, sizeof(Oid), ObjectIdAttributeNumber, 0, -1, -1, - true, 'p', 'i', true, false, false, true, 0 + true, 'p', 'i', true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a3 = { 0, {"xmin"}, XIDOID, 0, sizeof(TransactionId), MinTransactionIdAttributeNumber, 0, -1, -1, - true, 'p', 'i', true, false, false, true, 0 + true, 'p', 'i', true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a4 = { 0, {"cmin"}, CIDOID, 0, sizeof(CommandId), MinCommandIdAttributeNumber, 0, -1, -1, - true, 'p', 'i', true, false, false, true, 0 + true, 'p', 'i', true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a5 = { 0, {"xmax"}, XIDOID, 0, sizeof(TransactionId), MaxTransactionIdAttributeNumber, 0, -1, -1, - true, 'p', 'i', true, false, false, true, 0 + true, 'p', 'i', true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a6 = { 0, {"cmax"}, CIDOID, 0, sizeof(CommandId), MaxCommandIdAttributeNumber, 0, -1, -1, - true, 'p', 'i', true, false, false, true, 0 + true, 'p', 'i', true, false, false, true, 0, { 0 } }; /* @@ -154,7 +154,7 @@ static FormData_pg_attribute a6 = { static FormData_pg_attribute a7 = { 0, {"tableoid"}, OIDOID, 0, sizeof(Oid), TableOidAttributeNumber, 0, -1, -1, - true, 'p', 'i', true, false, false, true, 0 + true, 'p', 'i', true, false, false, true, 0, { 0 } }; static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; @@ -475,11 +475,13 @@ CheckAttributeType(const char *attname, Oid atttypid) * Construct and insert a new tuple in pg_attribute. * * Caller has already opened and locked pg_attribute. new_attribute is the - * attribute to insert. + * attribute to insert (but we ignore its attacl, if indeed it has one). * * indstate is the index state for CatalogIndexInsert. It can be passed as * NULL, in which case we'll fetch the necessary info. (Don't do this when * inserting multiple attributes, because it's a tad more expensive.) + * + * We always initialize attacl to NULL (i.e., default permissions). */ void InsertPgAttributeTuple(Relation pg_attribute_rel, @@ -512,6 +514,9 @@ InsertPgAttributeTuple(Relation pg_attribute_rel, values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal); values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount); + /* start out with empty permissions */ + nulls[Anum_pg_attribute_attacl - 1] = true; + tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls); /* finally insert the new tuple, update the indexes, and clean up */ diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index dee85b6716..e53f4f52dc 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.311 2009/01/01 17:23:37 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.312 2009/01/22 20:16:01 tgl Exp $ * * * INTERFACE ROUTINES @@ -178,7 +178,7 @@ ConstructTupleDescriptor(Relation heapRelation, * now that we've determined the "from", let's copy the tuple desc * data... */ - memcpy(to, from, ATTRIBUTE_TUPLE_SIZE); + memcpy(to, from, ATTRIBUTE_FIXED_PART_SIZE); /* * Fix the stuff that should not be the same as the underlying @@ -198,7 +198,7 @@ ConstructTupleDescriptor(Relation heapRelation, /* Expressional index */ Node *indexkey; - MemSet(to, 0, ATTRIBUTE_TUPLE_SIZE); + MemSet(to, 0, ATTRIBUTE_FIXED_PART_SIZE); if (indexpr_item == NULL) /* shouldn't happen */ elog(ERROR, "too few entries in indexprs list"); diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 989f2470af..12ffd74256 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_operator.c,v 1.107 2009/01/01 17:23:37 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_operator.c,v 1.108 2009/01/22 20:16:01 tgl Exp $ * * NOTES * these routines moved here from commands/define.c and somewhat cleaned up. @@ -774,7 +774,7 @@ makeOperatorDependencies(HeapTuple tuple) /* In case we are updating a shell, delete any existing entries */ deleteDependencyRecordsFor(myself.classId, myself.objectId); - deleteSharedDependencyRecordsFor(myself.classId, myself.objectId); + deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0); /* Dependency on namespace */ if (OidIsValid(oper->oprnamespace)) diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 191b8b8a9e..f9b2716b9f 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.160 2009/01/01 17:23:37 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.161 2009/01/22 20:16:01 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -499,7 +499,7 @@ ProcedureCreate(const char *procedureName, if (is_update) { deleteDependencyRecordsFor(ProcedureRelationId, retval); - deleteSharedDependencyRecordsFor(ProcedureRelationId, retval); + deleteSharedDependencyRecordsFor(ProcedureRelationId, retval, 0); } myself.classId = ProcedureRelationId; diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index 113c72f324..ee5329b1d7 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.30 2009/01/01 17:23:37 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.31 2009/01/22 20:16:01 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -55,15 +55,19 @@ static int getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff); static Oid classIdGetDbId(Oid classId); static void shdepLockAndCheckObject(Oid classId, Oid objectId); -static void shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, - Oid refclassid, Oid refobjid, - SharedDependencyType deptype); -static void shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId, - Oid refclassId, Oid refobjId, - SharedDependencyType deptype); -static void shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId, - Oid refclassId, Oid refobjId, - SharedDependencyType deptype); +static void shdepChangeDep(Relation sdepRel, + Oid classid, Oid objid, int32 objsubid, + Oid refclassid, Oid refobjid, + SharedDependencyType deptype); +static void shdepAddDependency(Relation sdepRel, + Oid classId, Oid objectId, int32 objsubId, + Oid refclassId, Oid refobjId, + SharedDependencyType deptype); +static void shdepDropDependency(Relation sdepRel, + Oid classId, Oid objectId, int32 objsubId, + bool drop_subobjects, + Oid refclassId, Oid refobjId, + SharedDependencyType deptype); static void storeObjectDescription(StringInfo descs, objectType type, ObjectAddress *object, SharedDependencyType deptype, @@ -111,6 +115,7 @@ recordSharedDependencyOn(ObjectAddress *depender, sdepRel)) { shdepAddDependency(sdepRel, depender->classId, depender->objectId, + depender->objectSubId, referenced->classId, referenced->objectId, deptype); } @@ -163,14 +168,15 @@ recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner) * locked. */ static void -shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, +shdepChangeDep(Relation sdepRel, + Oid classid, Oid objid, int32 objsubid, Oid refclassid, Oid refobjid, SharedDependencyType deptype) { Oid dbid = classIdGetDbId(classid); HeapTuple oldtup = NULL; HeapTuple scantup; - ScanKeyData key[3]; + ScanKeyData key[4]; SysScanDesc scan; /* @@ -194,9 +200,13 @@ shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, Anum_pg_shdepend_objid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(objid)); + ScanKeyInit(&key[3], + Anum_pg_shdepend_objsubid, + BTEqualStrategyNumber, F_INT4EQ, + Int32GetDatum(objsubid)); scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true, - SnapshotNow, 3, key); + SnapshotNow, 4, key); while ((scantup = systable_getnext(scan)) != NULL) { @@ -206,8 +216,8 @@ shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, /* Caller screwed up if multiple matches */ if (oldtup) elog(ERROR, - "multiple pg_shdepend entries for object %u/%u deptype %c", - classid, objid, deptype); + "multiple pg_shdepend entries for object %u/%u/%d deptype %c", + classid, objid, objsubid, deptype); oldtup = heap_copytuple(scantup); } @@ -244,6 +254,7 @@ shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid); values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid); values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid); + values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubid); values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid); values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid); @@ -268,6 +279,9 @@ shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, * changeDependencyOnOwner * * Update the shared dependencies to account for the new owner. + * + * Note: we don't need an objsubid argument because only whole objects + * have owners. */ void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId) @@ -277,7 +291,8 @@ changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId) sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); /* Adjust the SHARED_DEPENDENCY_OWNER entry */ - shdepChangeDep(sdepRel, classId, objectId, + shdepChangeDep(sdepRel, + classId, objectId, 0, AuthIdRelationId, newOwnerId, SHARED_DEPENDENCY_OWNER); @@ -300,7 +315,7 @@ changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId) * to make the various ALTER OWNER routines each know about it. *---------- */ - shdepDropDependency(sdepRel, classId, objectId, + shdepDropDependency(sdepRel, classId, objectId, 0, true, AuthIdRelationId, newOwnerId, SHARED_DEPENDENCY_ACL); @@ -368,7 +383,7 @@ getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff) * updateAclDependencies * Update the pg_shdepend info for an object's ACL during GRANT/REVOKE. * - * classId, objectId: identify the object whose ACL this is + * classId, objectId, objsubId: identify the object whose ACL this is * ownerId: role owning the object * isGrant: are we adding or removing ACL entries? * noldmembers, oldmembers: array of roleids appearing in old ACL @@ -388,7 +403,8 @@ getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff) * before return. */ void -updateAclDependencies(Oid classId, Oid objectId, Oid ownerId, bool isGrant, +updateAclDependencies(Oid classId, Oid objectId, int32 objsubId, + Oid ownerId, bool isGrant, int noldmembers, Oid *oldmembers, int nnewmembers, Oid *newmembers) { @@ -429,11 +445,12 @@ updateAclDependencies(Oid classId, Oid objectId, Oid ownerId, bool isGrant, continue; if (isGrant) - shdepAddDependency(sdepRel, classId, objectId, + shdepAddDependency(sdepRel, classId, objectId, objsubId, AuthIdRelationId, roleid, SHARED_DEPENDENCY_ACL); else - shdepDropDependency(sdepRel, classId, objectId, + shdepDropDependency(sdepRel, classId, objectId, objsubId, + false, /* exact match on objsubId */ AuthIdRelationId, roleid, SHARED_DEPENDENCY_ACL); } @@ -533,7 +550,7 @@ checkSharedDependencies(Oid classId, Oid objectId, object.classId = sdepForm->classid; object.objectId = sdepForm->objid; - object.objectSubId = 0; + object.objectSubId = sdepForm->objsubid; /* * If it's a dependency local to this database or it's a shared @@ -755,7 +772,7 @@ dropDatabaseDependencies(Oid databaseId) systable_endscan(scan); /* Now delete all entries corresponding to the database itself */ - shdepDropDependency(sdepRel, DatabaseRelationId, databaseId, + shdepDropDependency(sdepRel, DatabaseRelationId, databaseId, 0, true, InvalidOid, InvalidOid, SHARED_DEPENDENCY_INVALID); @@ -768,15 +785,19 @@ dropDatabaseDependencies(Oid databaseId) * Delete all pg_shdepend entries corresponding to an object that's being * dropped or modified. The object is assumed to be either a shared object * or local to the current database (the classId tells us which). + * + * If objectSubId is zero, we are deleting a whole object, so get rid of + * pg_shdepend entries for subobjects as well. */ void -deleteSharedDependencyRecordsFor(Oid classId, Oid objectId) +deleteSharedDependencyRecordsFor(Oid classId, Oid objectId, int32 objectSubId) { Relation sdepRel; sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); - shdepDropDependency(sdepRel, classId, objectId, + shdepDropDependency(sdepRel, classId, objectId, objectSubId, + (objectSubId == 0), InvalidOid, InvalidOid, SHARED_DEPENDENCY_INVALID); @@ -791,7 +812,8 @@ deleteSharedDependencyRecordsFor(Oid classId, Oid objectId) * locked. */ static void -shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId, +shdepAddDependency(Relation sdepRel, + Oid classId, Oid objectId, int32 objsubId, Oid refclassId, Oid refobjId, SharedDependencyType deptype) { @@ -814,6 +836,7 @@ shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId, values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(classIdGetDbId(classId)); values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classId); values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objectId); + values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubId); values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassId); values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjId); @@ -835,20 +858,26 @@ shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId, * Internal workhorse for deleting entries from pg_shdepend. * * We drop entries having the following properties: - * dependent object is the one identified by classId/objectId + * dependent object is the one identified by classId/objectId/objsubId * if refclassId isn't InvalidOid, it must match the entry's refclassid * if refobjId isn't InvalidOid, it must match the entry's refobjid * if deptype isn't SHARED_DEPENDENCY_INVALID, it must match entry's deptype * + * If drop_subobjects is true, we ignore objsubId and consider all entries + * matching classId/objectId. + * * sdepRel must be the pg_shdepend relation, already opened and suitably * locked. */ static void -shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId, +shdepDropDependency(Relation sdepRel, + Oid classId, Oid objectId, int32 objsubId, + bool drop_subobjects, Oid refclassId, Oid refobjId, SharedDependencyType deptype) { - ScanKeyData key[3]; + ScanKeyData key[4]; + int nkeys; SysScanDesc scan; HeapTuple tup; @@ -865,9 +894,19 @@ shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId, Anum_pg_shdepend_objid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(objectId)); + if (drop_subobjects) + nkeys = 3; + else + { + ScanKeyInit(&key[3], + Anum_pg_shdepend_objsubid, + BTEqualStrategyNumber, F_INT4EQ, + Int32GetDatum(objsubId)); + nkeys = 4; + } scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true, - SnapshotNow, 3, key); + SnapshotNow, nkeys, key); while (HeapTupleIsValid(tup = systable_getnext(scan))) { @@ -1067,7 +1106,7 @@ isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel) * * Drop the objects owned by any one of the given RoleIds. If a role has * access to an object, the grant will be removed as well (but the object - * will not, of course.) + * will not, of course). * * We can revoke grants immediately while doing the scan, but drops are * saved up and done all at once with performMultipleDeletions. This @@ -1131,10 +1170,9 @@ shdepDropOwned(List *roleids, DropBehavior behavior) while ((tuple = systable_getnext(scan)) != NULL) { - ObjectAddress obj; - GrantObjectType objtype; - InternalGrant istmt; Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple); + InternalGrant istmt; + ObjectAddress obj; /* We only operate on objects in the current database */ if (sdepForm->dbid != MyDatabaseId) @@ -1172,14 +1210,13 @@ shdepDropOwned(List *roleids, DropBehavior behavior) default: elog(ERROR, "unexpected object type %d", sdepForm->classid); - /* keep compiler quiet */ - objtype = (GrantObjectType) 0; break; } istmt.is_grant = false; istmt.objects = list_make1_oid(sdepForm->objid); istmt.all_privs = true; istmt.privileges = ACL_NO_RIGHTS; + istmt.col_privs = NIL; istmt.grantees = list_make1_oid(roleid); istmt.grant_option = false; istmt.behavior = DROP_CASCADE; @@ -1190,7 +1227,7 @@ shdepDropOwned(List *roleids, DropBehavior behavior) /* Save it for deletion below */ obj.classId = sdepForm->classid; obj.objectId = sdepForm->objid; - obj.objectSubId = 0; + obj.objectSubId = sdepForm->objsubid; add_exact_object_address(&obj, deleteobjs); break; } diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index d39a66b58d..6d28b1df2f 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.123 2009/01/01 17:23:37 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.124 2009/01/22 20:16:01 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -482,7 +482,7 @@ GenerateTypeDependencies(Oid typeNamespace, if (rebuild) { deleteDependencyRecordsFor(TypeRelationId, typeObjectId); - deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId); + deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId, 0); } myself.classId = TypeRelationId; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5f6a2c42de..33447b671f 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.132 2009/01/06 23:46:06 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.133 2009/01/22 20:16:01 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -705,11 +705,12 @@ examine_attribute(Relation onerel, int attnum) return NULL; /* - * Create the VacAttrStats struct. + * Create the VacAttrStats struct. Note that we only have a copy of + * the fixed fields of the pg_attribute tuple. */ stats = (VacAttrStats *) palloc0(sizeof(VacAttrStats)); - stats->attr = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE); - memcpy(stats->attr, attr, ATTRIBUTE_TUPLE_SIZE); + stats->attr = (Form_pg_attribute) palloc(ATTRIBUTE_FIXED_PART_SIZE); + memcpy(stats->attr, attr, ATTRIBUTE_FIXED_PART_SIZE); typtuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(attr->atttypid), 0, 0, 0); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 061d45c30a..397e010aca 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.277 2009/01/12 08:54:26 petere Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.278 2009/01/22 20:16:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -246,6 +246,7 @@ static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, Oid *opclasses); +static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts); static void validateForeignKeyConstraint(FkConstraint *fkconstraint, Relation rel, Relation pkrel, Oid constraintOid); static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, @@ -3589,6 +3590,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, attribute.attisdropped = false; attribute.attislocal = colDef->is_local; attribute.attinhcount = colDef->inhcount; + /* attribute.attacl is handled by InsertPgAttributeTuple */ ReleaseSysCache(typeTuple); @@ -4473,15 +4475,14 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, * Add a foreign-key constraint to a single table * * Subroutine for ATExecAddConstraint. Must already hold exclusive - * lock on the rel, and have done appropriate validity/permissions checks - * for it. + * lock on the rel, and have done appropriate validity checks for it. + * We do permissions checks here, however. */ static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, FkConstraint *fkconstraint) { Relation pkrel; - AclResult aclresult; int16 pkattnum[INDEX_MAX_KEYS]; int16 fkattnum[INDEX_MAX_KEYS]; Oid pktypoid[INDEX_MAX_KEYS]; @@ -4506,10 +4507,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock); /* - * Validity and permissions checks - * - * Note: REFERENCES permissions checks are redundant with CREATE TRIGGER, - * but we may as well error out sooner instead of later. + * Validity checks (permission checks wait till we have the column numbers) */ if (pkrel->rd_rel->relkind != RELKIND_RELATION) ereport(ERROR, @@ -4517,24 +4515,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, errmsg("referenced relation \"%s\" is not a table", RelationGetRelationName(pkrel)))); - aclresult = pg_class_aclcheck(RelationGetRelid(pkrel), GetUserId(), - ACL_REFERENCES); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_CLASS, - RelationGetRelationName(pkrel)); - if (!allowSystemTableMods && IsSystemRelation(pkrel)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(pkrel)))); - aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), - ACL_REFERENCES); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_CLASS, - RelationGetRelationName(rel)); - /* * Disallow reference from permanent table to temp table or vice versa. * (The ban on perm->temp is for fairly obvious reasons. The ban on @@ -4598,6 +4584,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, opclasses); } + /* + * Now we can check permissions. + */ + checkFkeyPermissions(pkrel, pkattnum, numpks); + checkFkeyPermissions(rel, fkattnum, numfks); + /* * Look up the equality operators to use in the constraint. * @@ -5016,6 +5008,30 @@ transformFkeyCheckAttrs(Relation pkrel, return indexoid; } +/* Permissions checks for ADD FOREIGN KEY */ +static void +checkFkeyPermissions(Relation rel, int16 *attnums, int natts) +{ + Oid roleid = GetUserId(); + AclResult aclresult; + int i; + + /* Okay if we have relation-level REFERENCES permission */ + aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid, + ACL_REFERENCES); + if (aclresult == ACLCHECK_OK) + return; + /* Else we must have REFERENCES on each column */ + for (i = 0; i < natts; i++) + { + aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i], + roleid, ACL_REFERENCES); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(rel)); + } +} + /* * Scan the existing rows in a table to verify they meet a proposed FK * constraint. @@ -5123,7 +5139,7 @@ CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint, fk_trigger->constrrel = fkconstraint->pktable; fk_trigger->args = NIL; - (void) CreateTrigger(fk_trigger, constraintOid); + (void) CreateTrigger(fk_trigger, constraintOid, false); /* Make changes-so-far visible */ CommandCounterIncrement(); @@ -5204,7 +5220,7 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, } fk_trigger->args = NIL; - (void) CreateTrigger(fk_trigger, constraintOid); + (void) CreateTrigger(fk_trigger, constraintOid, false); /* Make changes-so-far visible */ CommandCounterIncrement(); @@ -5256,7 +5272,7 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, } fk_trigger->args = NIL; - (void) CreateTrigger(fk_trigger, constraintOid); + (void) CreateTrigger(fk_trigger, constraintOid, false); } /* diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index 54818ff34d..b81381a6ea 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.60 2009/01/20 18:59:37 heikki Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.61 2009/01/22 20:16:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -455,7 +455,7 @@ DropTableSpace(DropTableSpaceStmt *stmt) /* * Remove dependency on owner. */ - deleteSharedDependencyRecordsFor(TableSpaceRelationId, tablespaceoid); + deleteSharedDependencyRecordsFor(TableSpaceRelationId, tablespaceoid, 0); /* * Acquire TablespaceCreateLock to ensure that no TablespaceCreateDbspace diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 699493c335..ce276e5fe5 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.245 2009/01/22 19:16:31 heikki Exp $ + * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.246 2009/01/22 20:16:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -74,11 +74,16 @@ static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, * be made to link the trigger to that constraint. constraintOid is zero when * executing a user-entered CREATE TRIGGER command. * + * If checkPermissions is true we require ACL_TRIGGER permissions on the + * relation. If not, the caller already checked permissions. (This is + * currently redundant with constraintOid being zero, but it's clearer to + * have a separate argument.) + * * Note: can return InvalidOid if we decided to not create a trigger at all, * but a foreign-key constraint. This is a kluge for backwards compatibility. */ Oid -CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid) +CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid, bool checkPermissions) { int16 tgtype; int2vector *tgattr; @@ -117,36 +122,26 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid) errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(rel)))); + if (stmt->isconstraint && stmt->constrrel != NULL) + constrrelid = RangeVarGetRelid(stmt->constrrel, false); + /* permission checks */ - - if (stmt->isconstraint) + if (checkPermissions) { - /* constraint trigger */ - aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), - ACL_REFERENCES); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_CLASS, - RelationGetRelationName(rel)); - - if (stmt->constrrel != NULL) - { - constrrelid = RangeVarGetRelid(stmt->constrrel, false); - - aclresult = pg_class_aclcheck(constrrelid, GetUserId(), - ACL_REFERENCES); - 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)); + + if (OidIsValid(constrrelid)) + { + aclresult = pg_class_aclcheck(constrrelid, GetUserId(), + ACL_TRIGGER); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + get_rel_name(constrrelid)); + } } /* Compute tgtype */ diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c index f9248a2b67..7276cd50d4 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.15 2009/01/01 17:23:40 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.16 2009/01/22 20:16:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1255,7 +1255,7 @@ makeConfigurationDependencies(HeapTuple tuple, bool removeOld, if (removeOld) { deleteDependencyRecordsFor(myself.classId, myself.objectId); - deleteSharedDependencyRecordsFor(myself.classId, myself.objectId); + deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0); } /* diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index a013e80ac9..7c1da42bc3 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.184 2009/01/01 17:23:40 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.185 2009/01/22 20:16:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1123,9 +1123,17 @@ GrantRole(GrantRoleStmt *stmt) */ foreach(item, stmt->granted_roles) { - char *rolename = strVal(lfirst(item)); - Oid roleid = get_roleid_checked(rolename); + AccessPriv *priv = (AccessPriv *) lfirst(item); + char *rolename = priv->priv_name; + Oid roleid; + /* Must reject priv(columns) and ALL PRIVILEGES(columns) */ + if (rolename == NULL || priv->cols != NIL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_GRANT_OPERATION), + errmsg("column names cannot be included in GRANT/REVOKE ROLE"))); + + roleid = get_roleid_checked(rolename); if (stmt->is_grant) AddRoleMems(rolename, roleid, stmt->grantee_roles, grantee_ids, diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index bb05b5d482..0352d9a5e4 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.320 2009/01/01 17:23:41 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.321 2009/01/22 20:16:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -34,6 +34,7 @@ #include "access/heapam.h" #include "access/reloptions.h" +#include "access/sysattr.h" #include "access/transam.h" #include "access/xact.h" #include "catalog/heap.h" @@ -453,8 +454,12 @@ static void ExecCheckRTEPerms(RangeTblEntry *rte) { AclMode requiredPerms; + AclMode relPerms; + AclMode remainingPerms; Oid relOid; Oid userid; + Bitmapset *tmpset; + int col; /* * Only plain-relation RTEs need to be checked here. Function RTEs are @@ -484,12 +489,110 @@ ExecCheckRTEPerms(RangeTblEntry *rte) userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* - * We must have *all* the requiredPerms bits, so use aclmask not aclcheck. + * We must have *all* the requiredPerms bits, but some of the bits can be + * satisfied from column-level rather than relation-level permissions. + * First, remove any bits that are satisfied by relation permissions. */ - if (pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL) - != requiredPerms) - aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, - get_rel_name(relOid)); + relPerms = pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL); + remainingPerms = requiredPerms & ~relPerms; + if (remainingPerms != 0) + { + /* + * If we lack any permissions that exist only as relation permissions, + * we can fail straight away. + */ + if (remainingPerms & ~(ACL_SELECT | ACL_INSERT | ACL_UPDATE)) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + + /* + * Check to see if we have the needed privileges at column level. + * + * Note: failures just report a table-level error; it would be nicer + * to report a column-level error if we have some but not all of the + * column privileges. + */ + if (remainingPerms & ACL_SELECT) + { + /* + * When the query doesn't explicitly reference any columns (for + * example, SELECT COUNT(*) FROM table), allow the query if we + * have SELECT on any column of the rel, as per SQL spec. + */ + if (bms_is_empty(rte->selectedCols)) + { + if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, + ACLMASK_ANY) != ACLCHECK_OK) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + } + + tmpset = bms_copy(rte->selectedCols); + while ((col = bms_first_member(tmpset)) >= 0) + { + /* remove the column number offset */ + col += FirstLowInvalidHeapAttributeNumber; + if (col == InvalidAttrNumber) + { + /* Whole-row reference, must have priv on all cols */ + if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, + ACLMASK_ALL) != ACLCHECK_OK) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + } + else + { + if (pg_attribute_aclcheck(relOid, col, userid, ACL_SELECT) + != ACLCHECK_OK) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + } + } + bms_free(tmpset); + } + + /* + * Basically the same for the mod columns, with either INSERT or UPDATE + * privilege as specified by remainingPerms. + */ + remainingPerms &= ~ACL_SELECT; + if (remainingPerms != 0) + { + /* + * When the query doesn't explicitly change any columns, allow + * the query if we have permission on any column of the rel. This + * is to handle SELECT FOR UPDATE as well as possible corner cases + * in INSERT and UPDATE. + */ + if (bms_is_empty(rte->modifiedCols)) + { + if (pg_attribute_aclcheck_all(relOid, userid, remainingPerms, + ACLMASK_ANY) != ACLCHECK_OK) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + } + + tmpset = bms_copy(rte->modifiedCols); + while ((col = bms_first_member(tmpset)) >= 0) + { + /* remove the column number offset */ + col += FirstLowInvalidHeapAttributeNumber; + if (col == InvalidAttrNumber) + { + /* whole-row reference can't happen here */ + elog(ERROR, "whole-row update is not implemented"); + } + else + { + if (pg_attribute_aclcheck(relOid, col, userid, remainingPerms) + != ACLCHECK_OK) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + } + } + bms_free(tmpset); + } + } } /* diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index da1c65cfcc..06f89b16a3 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.420 2009/01/16 13:27:23 heikki Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.421 2009/01/22 20:16:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1740,6 +1740,8 @@ _copyRangeTblEntry(RangeTblEntry *from) COPY_SCALAR_FIELD(inFromCl); COPY_SCALAR_FIELD(requiredPerms); COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(modifiedCols); return newnode; } @@ -2342,6 +2344,17 @@ _copyFuncWithArgs(FuncWithArgs *from) return newnode; } +static AccessPriv * +_copyAccessPriv(AccessPriv *from) +{ + AccessPriv *newnode = makeNode(AccessPriv); + + COPY_STRING_FIELD(priv_name); + COPY_NODE_FIELD(cols); + + return newnode; +} + static GrantRoleStmt * _copyGrantRoleStmt(GrantRoleStmt *from) { @@ -4096,6 +4109,9 @@ copyObject(void *from) case T_FuncWithArgs: retval = _copyFuncWithArgs(from); break; + case T_AccessPriv: + retval = _copyAccessPriv(from); + break; case T_XmlSerialize: retval = _copyXmlSerialize(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 190750f2e1..4e101dd23b 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -22,7 +22,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.345 2009/01/16 13:27:23 heikki Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.346 2009/01/22 20:16:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1013,6 +1013,15 @@ _equalFuncWithArgs(FuncWithArgs *a, FuncWithArgs *b) return true; } +static bool +_equalAccessPriv(AccessPriv *a, AccessPriv *b) +{ + COMPARE_STRING_FIELD(priv_name); + COMPARE_NODE_FIELD(cols); + + return true; +} + static bool _equalGrantRoleStmt(GrantRoleStmt *a, GrantRoleStmt *b) { @@ -2122,6 +2131,8 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) COMPARE_SCALAR_FIELD(inFromCl); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); + COMPARE_BITMAPSET_FIELD(selectedCols); + COMPARE_BITMAPSET_FIELD(modifiedCols); return true; } @@ -2874,6 +2885,9 @@ equal(void *a, void *b) case T_FuncWithArgs: retval = _equalFuncWithArgs(a, b); break; + case T_AccessPriv: + retval = _equalAccessPriv(a, b); + break; case T_XmlSerialize: retval = _equalXmlSerialize(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index af427fe7e6..0efd84dae8 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.349 2009/01/01 17:23:43 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.350 2009/01/22 20:16:04 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -180,8 +180,6 @@ _outList(StringInfo str, List *node) * converts a bitmap set of integers * * Note: the output format is "(b int int ...)", similar to an integer List. - * Currently bitmapsets do not appear in any node type that is stored in - * rules, so there is no support in readfuncs.c for reading this format. */ static void _outBitmapset(StringInfo str, Bitmapset *bms) @@ -2060,6 +2058,8 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) WRITE_BOOL_FIELD(inFromCl); WRITE_UINT_FIELD(requiredPerms); WRITE_OID_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(modifiedCols); } static void diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index d5f4677c2a..de62e53d1d 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.220 2009/01/01 17:23:43 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.221 2009/01/22 20:16:04 tgl Exp $ * * NOTES * Path and Plan nodes do not have any readfuncs support, because we @@ -114,6 +114,11 @@ token = pg_strtok(&length); /* skip :fldname */ \ local_node->fldname = nodeRead(NULL, 0) +/* Read a bitmapset field */ +#define READ_BITMAPSET_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + local_node->fldname = _readBitmapset() + /* Routine exit */ #define READ_DONE() \ return local_node @@ -137,6 +142,46 @@ static Datum readDatum(bool typbyval); +/* + * _readBitmapset + */ +static Bitmapset * +_readBitmapset(void) +{ + Bitmapset *result = NULL; + READ_TEMP_LOCALS(); + + token = pg_strtok(&length); + if (token == NULL) + elog(ERROR, "incomplete Bitmapset structure"); + if (length != 1 || token[0] != '(') + elog(ERROR, "unrecognized token: \"%.*s\"", length, token); + + token = pg_strtok(&length); + if (token == NULL) + elog(ERROR, "incomplete Bitmapset structure"); + if (length != 1 || token[0] != 'b') + elog(ERROR, "unrecognized token: \"%.*s\"", length, token); + + for (;;) + { + int val; + char *endptr; + + token = pg_strtok(&length); + if (token == NULL) + elog(ERROR, "unterminated Bitmapset structure"); + if (length == 1 && token[0] == ')') + break; + val = (int) strtol(token, &endptr, 10); + if (endptr != token + length) + elog(ERROR, "unrecognized integer: \"%.*s\"", length, token); + result = bms_add_member(result, val); + } + + return result; +} + /* * _readQuery @@ -1102,6 +1147,8 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(inFromCl); READ_UINT_FIELD(requiredPerms); READ_OID_FIELD(checkAsUser); + READ_BITMAPSET_FIELD(selectedCols); + READ_BITMAPSET_FIELD(modifiedCols); READ_DONE(); } diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 1a64a7742d..17016d5f3b 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.148 2009/01/01 17:23:44 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.149 2009/01/22 20:16:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -189,7 +189,8 @@ set_plan_references(PlannerGlobal *glob, Plan *plan, List *rtable) * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost * for cached plans. We keep only the alias and eref Alias fields, which - * are needed by EXPLAIN. + * are needed by EXPLAIN, and the selectedCols and modifiedCols bitmaps, + * which are needed for executor-startup permissions checking. */ foreach(lc, rtable) { diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6b54e9ba17..397e951c71 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -17,13 +17,14 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.387 2009/01/08 13:42:33 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.388 2009/01/22 20:16:04 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "access/sysattr.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" @@ -422,6 +423,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * bugs of just that nature...) */ sub_pstate->p_rtable = sub_rtable; + sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_relnamespace = sub_relnamespace; sub_pstate->p_varnamespace = sub_varnamespace; @@ -629,7 +631,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* * Generate query's target list using the computed list of expressions. + * Also, mark all the target columns as needing insert permissions. */ + rte = pstate->p_target_rangetblentry; qry->targetList = NIL; icols = list_head(icolumns); attnos = list_head(attrnos); @@ -637,17 +641,22 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { Expr *expr = (Expr *) lfirst(lc); ResTarget *col; + AttrNumber attr_num; TargetEntry *tle; col = (ResTarget *) lfirst(icols); Assert(IsA(col, ResTarget)); + attr_num = (AttrNumber) lfirst_int(attnos); tle = makeTargetEntry(expr, - (AttrNumber) lfirst_int(attnos), + attr_num, col->name, false); qry->targetList = lappend(qry->targetList, tle); + rte->modifiedCols = bms_add_member(rte->modifiedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); + icols = lnext(icols); attnos = lnext(attnos); } @@ -1129,8 +1138,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) List *targetvars, *targetnames, *sv_relnamespace, - *sv_varnamespace, - *sv_rtable; + *sv_varnamespace; + int sv_rtable_length; RangeTblEntry *jrte; int tllen; @@ -1254,16 +1263,15 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) * "ORDER BY upper(foo)" will draw the right error message rather than * "foo not found". */ - jrte = addRangeTableEntryForJoin(NULL, + sv_rtable_length = list_length(pstate->p_rtable); + + jrte = addRangeTableEntryForJoin(pstate, targetnames, JOIN_INNER, targetvars, NULL, false); - sv_rtable = pstate->p_rtable; - pstate->p_rtable = list_make1(jrte); - sv_relnamespace = pstate->p_relnamespace; pstate->p_relnamespace = NIL; /* no qualified names allowed */ @@ -1283,7 +1291,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) &qry->targetList, false /* no unknowns expected */ ); - pstate->p_rtable = sv_rtable; + pstate->p_rtable = list_truncate(pstate->p_rtable, sv_rtable_length); pstate->p_relnamespace = sv_relnamespace; pstate->p_varnamespace = sv_varnamespace; @@ -1618,6 +1626,7 @@ static Query * transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) { Query *qry = makeNode(Query); + RangeTblEntry *target_rte; Node *qual; ListCell *origTargetList; ListCell *tl; @@ -1675,6 +1684,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1; /* Prepare non-junk columns for assignment to target table */ + target_rte = pstate->p_target_rangetblentry; origTargetList = list_head(stmt->targetList); foreach(tl, qry->targetList) @@ -1715,6 +1725,10 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) origTarget->indirection, origTarget->location); + /* Mark the target column as requiring update permissions */ + target_rte->modifiedCols = bms_add_member(target_rte->modifiedCols, + attrno - FirstLowInvalidHeapAttributeNumber); + origTargetList = lnext(origTargetList); } if (origTargetList != NULL) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index cc1f812bd9..7f9e5e5b98 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.655 2009/01/16 13:27:23 heikki Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.656 2009/01/22 20:16:05 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -94,6 +94,13 @@ extern List *parsetree; /* final parse result is delivered here */ static bool QueryIsRule = FALSE; +/* Private struct for the result of privilege_target production */ +typedef struct PrivTarget +{ + GrantObjectType objtype; + List *objs; +} PrivTarget; + /* * If you need access to certain yacc-generated variables and find that * they're static by default, uncomment the next line. (this is not a @@ -167,7 +174,8 @@ static TypeName *TableFuncTypeName(List *columns); WithClause *with; A_Indices *aind; ResTarget *target; - PrivTarget *privtarget; + struct PrivTarget *privtarget; + AccessPriv *accesspriv; InsertStmt *istmt; VariableSetStmt *vsetstmt; @@ -254,7 +262,7 @@ static TypeName *TableFuncTypeName(List *columns); %type iso_level opt_encoding %type grantee %type grantee_list -%type privilege +%type 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; diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 93e393f837..7e9fb9c071 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.185 2009/01/01 17:23:45 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.186 2009/01/22 20:16:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,7 @@ static void extractRemainingColumns(List *common_colnames, List *src_colnames, List *src_colvars, List **res_colnames, List **res_colvars); static Node *transformJoinUsingClause(ParseState *pstate, + RangeTblEntry *leftRTE, RangeTblEntry *rightRTE, List *leftVars, List *rightVars); static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j, RangeTblEntry *l_rte, @@ -194,8 +195,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * * If we find an explicit reference to the rel later during parse * analysis, we will add the ACL_SELECT bit back again; see - * scanRTEForColumn (for simple field references), ExpandColumnRefStar - * (for foo.*) and ExpandAllTables (for *). + * markVarForSelectPriv and its callers. */ rte->requiredPerms = requiredPerms; @@ -305,7 +305,9 @@ extractRemainingColumns(List *common_colnames, * Result is a transformed qualification expression. */ static Node * -transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars) +transformJoinUsingClause(ParseState *pstate, + RangeTblEntry *leftRTE, RangeTblEntry *rightRTE, + List *leftVars, List *rightVars) { Node *result = NULL; ListCell *lvars, @@ -315,17 +317,25 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars) * We cheat a little bit here by building an untransformed operator tree * whose leaves are the already-transformed Vars. This is OK because * transformExpr() won't complain about already-transformed subnodes. + * However, this does mean that we have to mark the columns as requiring + * SELECT privilege for ourselves; transformExpr() won't do it. */ forboth(lvars, leftVars, rvars, rightVars) { - Node *lvar = (Node *) lfirst(lvars); - Node *rvar = (Node *) lfirst(rvars); + Var *lvar = (Var *) lfirst(lvars); + Var *rvar = (Var *) lfirst(rvars); A_Expr *e; + /* Require read access to the join variables */ + markVarForSelectPriv(pstate, lvar, leftRTE); + markVarForSelectPriv(pstate, rvar, rightRTE); + + /* Now create the lvar = rvar join condition */ e = makeSimpleA_Expr(AEXPR_OP, "=", copyObject(lvar), copyObject(rvar), -1); + /* And combine into an AND clause, if multiple join columns */ if (result == NULL) result = (Node *) e; else @@ -728,6 +738,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, *r_colvars, *res_colvars; RangeTblEntry *rte; + int k; /* * Recursively process the left and right subtrees @@ -912,6 +923,8 @@ transformFromClauseItem(ParseState *pstate, Node *n, } j->quals = transformJoinUsingClause(pstate, + l_rte, + r_rte, l_usingvars, r_usingvars); } @@ -972,6 +985,12 @@ transformFromClauseItem(ParseState *pstate, Node *n, *top_rte = rte; *top_rti = j->rtindex; + /* make a matching link to the JoinExpr for later use */ + for (k = list_length(pstate->p_joinexprs) + 1; k < j->rtindex; k++) + pstate->p_joinexprs = lappend(pstate->p_joinexprs, NULL); + pstate->p_joinexprs = lappend(pstate->p_joinexprs, j); + Assert(list_length(pstate->p_joinexprs) == j->rtindex); + /* * Prepare returned namespace list. If the JOIN has an alias then it * hides the contained RTEs as far as the relnamespace goes; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 1d0f77a5b3..2bf6174866 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.239 2009/01/01 17:23:45 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.240 2009/01/22 20:16:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1977,6 +1977,9 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname, /* location is not filled in by makeVar */ result->location = location; + /* mark relation as requiring whole-row SELECT access */ + markVarForSelectPriv(pstate, result, rte); + return (Node *) result; } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 49ad024039..eb98f470ee 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.140 2009/01/01 17:23:46 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.141 2009/01/22 20:16:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -39,6 +39,8 @@ static RangeTblEntry *scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location); static RangeTblEntry *scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location); +static void markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte, + int rtindex, AttrNumber col); static bool isLockedRel(ParseState *pstate, char *refname); static void expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up, @@ -435,14 +437,8 @@ GetCTEForRTE(ParseState *pstate, RangeTblEntry *rte, int rtelevelsup) * If found, return an appropriate Var node, else return NULL. * If the name proves ambiguous within this RTE, raise error. * - * Side effect: if we find a match, mark the RTE as requiring read access. - * See comments in setTargetTable(). - * - * NOTE: if the RTE is for a join, marking it as requiring read access does - * nothing. It might seem that we need to propagate the mark to all the - * contained RTEs, but that is not necessary. This is so because a join - * expression can only appear in a FROM clause, and any table named in - * FROM will be marked as requiring read access from the beginning. + * Side effect: if we find a match, mark the RTE as requiring read access + * for the column. */ Node * scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname, @@ -450,6 +446,7 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname, { Node *result = NULL; int attnum = 0; + Var *var; ListCell *c; /* @@ -476,9 +473,10 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname, errmsg("column reference \"%s\" is ambiguous", colname), parser_errposition(pstate, location))); - result = (Node *) make_var(pstate, rte, attnum, location); - /* Require read access */ - rte->requiredPerms |= ACL_SELECT; + var = make_var(pstate, rte, attnum, location); + /* Require read access to the column */ + markVarForSelectPriv(pstate, var, rte); + result = (Node *) var; } } @@ -504,9 +502,10 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname, Int16GetDatum(attnum), 0, 0)) { - result = (Node *) make_var(pstate, rte, attnum, location); - /* Require read access */ - rte->requiredPerms |= ACL_SELECT; + var = make_var(pstate, rte, attnum, location); + /* Require read access to the column */ + markVarForSelectPriv(pstate, var, rte); + result = (Node *) var; } } } @@ -594,6 +593,122 @@ qualifiedNameToVar(ParseState *pstate, return scanRTEForColumn(pstate, rte, colname, location); } +/* + * markRTEForSelectPriv + * Mark the specified column of an RTE as requiring SELECT privilege + * + * col == InvalidAttrNumber means a "whole row" reference + * + * The caller should pass the actual RTE if it has it handy; otherwise pass + * NULL, and we'll look it up here. (This uglification of the API is + * worthwhile because nearly all external callers have the RTE at hand.) + */ +static void +markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte, + int rtindex, AttrNumber col) +{ + if (rte == NULL) + rte = rt_fetch(rtindex, pstate->p_rtable); + + if (rte->rtekind == RTE_RELATION) + { + /* Make sure the rel as a whole is marked for SELECT access */ + rte->requiredPerms |= ACL_SELECT; + /* Must offset the attnum to fit in a bitmapset */ + rte->selectedCols = bms_add_member(rte->selectedCols, + col - FirstLowInvalidHeapAttributeNumber); + } + else if (rte->rtekind == RTE_JOIN) + { + if (col == InvalidAttrNumber) + { + /* + * A whole-row reference to a join has to be treated as + * whole-row references to the two inputs. + */ + JoinExpr *j; + + if (rtindex > 0 && rtindex <= list_length(pstate->p_joinexprs)) + j = (JoinExpr *) list_nth(pstate->p_joinexprs, rtindex - 1); + else + j = NULL; + if (j == NULL) + elog(ERROR, "could not find JoinExpr for whole-row reference"); + Assert(IsA(j, JoinExpr)); + + /* Note: we can't see FromExpr here */ + if (IsA(j->larg, RangeTblRef)) + { + int varno = ((RangeTblRef *) j->larg)->rtindex; + + markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber); + } + else if (IsA(j->larg, JoinExpr)) + { + int varno = ((JoinExpr *) j->larg)->rtindex; + + markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber); + } + else + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(j->larg)); + if (IsA(j->rarg, RangeTblRef)) + { + int varno = ((RangeTblRef *) j->rarg)->rtindex; + + markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber); + } + else if (IsA(j->rarg, JoinExpr)) + { + int varno = ((JoinExpr *) j->rarg)->rtindex; + + markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber); + } + else + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(j->rarg)); + } + else + { + /* + * Regular join attribute, look at the alias-variable list. + * + * The aliasvar could be either a Var or a COALESCE expression, + * but in the latter case we should already have marked the two + * referent variables as being selected, due to their use in the + * JOIN clause. So we need only be concerned with the simple + * Var case. + */ + Var *aliasvar; + + Assert(col > 0 && col <= list_length(rte->joinaliasvars)); + aliasvar = (Var *) list_nth(rte->joinaliasvars, col - 1); + if (IsA(aliasvar, Var)) + markVarForSelectPriv(pstate, aliasvar, NULL); + } + } + /* other RTE types don't require privilege marking */ +} + +/* + * markVarForSelectPriv + * Mark the RTE referenced by a Var as requiring SELECT privilege + * + * The caller should pass the Var's referenced RTE if it has it handy + * (nearly all do); otherwise pass NULL. + */ +void +markVarForSelectPriv(ParseState *pstate, Var *var, RangeTblEntry *rte) +{ + Index lv; + + Assert(IsA(var, Var)); + /* Find the appropriate pstate if it's an uplevel Var */ + for (lv = 0; lv < var->varlevelsup; lv++) + pstate = pstate->parentParseState; + markRTEForSelectPriv(pstate, rte, var->varno, var->varattno); +} + /* * buildRelationAliases * Construct the eref column name list for a relation RTE. @@ -838,6 +953,8 @@ addRangeTableEntry(ParseState *pstate, rte->requiredPerms = ACL_SELECT; rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -891,6 +1008,8 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->requiredPerms = ACL_SELECT; rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -969,6 +1088,8 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->requiredPerms = 0; rte->checkAsUser = InvalidOid; + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -1101,6 +1222,8 @@ addRangeTableEntryForFunction(ParseState *pstate, rte->requiredPerms = 0; rte->checkAsUser = InvalidOid; + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -1168,8 +1291,11 @@ addRangeTableEntryForValues(ParseState *pstate, */ rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; + rte->requiredPerms = 0; rte->checkAsUser = InvalidOid; + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -1239,6 +1365,8 @@ addRangeTableEntryForJoin(ParseState *pstate, rte->requiredPerms = 0; rte->checkAsUser = InvalidOid; + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -1320,6 +1448,8 @@ addRangeTableEntryForCTE(ParseState *pstate, rte->requiredPerms = 0; rte->checkAsUser = InvalidOid; + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -1803,6 +1933,7 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, * As with expandRTE, rtindex/sublevels_up determine the varno/varlevelsup * fields of the Vars produced, and location sets their location. * pstate->p_next_resno determines the resnos assigned to the TLEs. + * The referenced columns are marked as requiring SELECT access. */ List * expandRelAttrs(ParseState *pstate, RangeTblEntry *rte, @@ -1817,10 +1948,17 @@ expandRelAttrs(ParseState *pstate, RangeTblEntry *rte, expandRTE(rte, rtindex, sublevels_up, location, false, &names, &vars); + /* + * Require read access to the table. This is normally redundant with the + * markVarForSelectPriv calls below, but not if the table has zero + * columns. + */ + rte->requiredPerms |= ACL_SELECT; + forboth(name, names, var, vars) { char *label = strVal(lfirst(name)); - Node *varnode = (Node *) lfirst(var); + Var *varnode = (Var *) lfirst(var); TargetEntry *te; te = makeTargetEntry((Expr *) varnode, @@ -1828,6 +1966,9 @@ expandRelAttrs(ParseState *pstate, RangeTblEntry *rte, label, false); te_list = lappend(te_list, te); + + /* Require read access to each column */ + markVarForSelectPriv(pstate, varnode, rte); } Assert(name == NULL && var == NULL); /* lists not the same length? */ diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 2765751edb..3f804472c7 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.169 2009/01/01 17:23:46 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.170 2009/01/22 20:16:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -850,6 +850,8 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) * in a SELECT target list (where we want TargetEntry nodes in the result) * and foo.* in a ROW() or VALUES() construct (where we want just bare * expressions). + * + * The referenced columns are marked as requiring SELECT access. */ static List * ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, @@ -929,20 +931,37 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, makeRangeVar(schemaname, relname, cref->location)); - /* Require read access --- see comments in setTargetTable() */ - rte->requiredPerms |= ACL_SELECT; - rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up); if (targetlist) + { + /* expandRelAttrs handles permissions marking */ return expandRelAttrs(pstate, rte, rtindex, sublevels_up, cref->location); + } else { List *vars; + ListCell *l; expandRTE(rte, rtindex, sublevels_up, cref->location, false, NULL, &vars); + + /* + * Require read access to the table. This is normally redundant + * with the markVarForSelectPriv calls below, but not if the table + * has zero columns. + */ + rte->requiredPerms |= ACL_SELECT; + + /* Require read access to each column */ + foreach(l, vars) + { + Var *var = (Var *) lfirst(l); + + markVarForSelectPriv(pstate, var, rte); + } + return vars; } } @@ -956,6 +975,8 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * varnamespace. We do not consider relnamespace because that would include * input tables of aliasless JOINs, NEW/OLD pseudo-entries, implicit RTEs, * etc. + * + * The referenced relations/columns are marked as requiring SELECT access. */ static List * ExpandAllTables(ParseState *pstate, int location) @@ -975,9 +996,6 @@ ExpandAllTables(ParseState *pstate, int location) RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); int rtindex = RTERangeTablePosn(pstate, rte, NULL); - /* Require read access --- see comments in setTargetTable() */ - rte->requiredPerms |= ACL_SELECT; - target = list_concat(target, expandRelAttrs(pstate, rte, rtindex, 0, location)); diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 56549a5509..7cfeacb730 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.183 2009/01/22 17:27:54 petere Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.184 2009/01/22 20:16:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1178,9 +1178,13 @@ ApplyRetrieveRule(Query *parsetree, Assert(subrte->relid == relation->rd_id); subrte->requiredPerms = rte->requiredPerms; subrte->checkAsUser = rte->checkAsUser; + subrte->selectedCols = rte->selectedCols; + subrte->modifiedCols = rte->modifiedCols; rte->requiredPerms = 0; /* no permission check on subquery itself */ rte->checkAsUser = InvalidOid; + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * FOR UPDATE/SHARE of view? diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 61f23213c4..7e7d07e4f0 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.304 2009/01/01 17:23:48 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.305 2009/01/22 20:16:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -910,7 +910,7 @@ ProcessUtility(Node *parsetree, break; case T_CreateTrigStmt: - CreateTrigger((CreateTrigStmt *) parsetree, InvalidOid); + CreateTrigger((CreateTrigStmt *) parsetree, InvalidOid, true); break; case T_DropPropertyStmt: diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index b270077e8d..cb0ebf4694 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.145 2009/01/01 17:23:48 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.146 2009/01/22 20:16:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -366,6 +366,47 @@ allocacl(int n) return new_acl; } +/* + * Copy an ACL + */ +Acl * +aclcopy(const Acl *orig_acl) +{ + Acl *result_acl; + + result_acl = allocacl(ACL_NUM(orig_acl)); + + memcpy(ACL_DAT(result_acl), + ACL_DAT(orig_acl), + ACL_NUM(orig_acl) * sizeof(AclItem)); + + return result_acl; +} + +/* + * Concatenate two ACLs + * + * This is a bit cheesy, since we may produce an ACL with redundant entries. + * Be careful what the result is used for! + */ +Acl * +aclconcat(const Acl *left_acl, const Acl *right_acl) +{ + Acl *result_acl; + + result_acl = allocacl(ACL_NUM(left_acl) + ACL_NUM(right_acl)); + + memcpy(ACL_DAT(result_acl), + ACL_DAT(left_acl), + ACL_NUM(left_acl) * sizeof(AclItem)); + + memcpy(ACL_DAT(result_acl) + ACL_NUM(left_acl), + ACL_DAT(right_acl), + ACL_NUM(right_acl) * sizeof(AclItem)); + + return result_acl; +} + /* * Verify that an ACL array is acceptable (one-dimensional and has no nulls) */ @@ -542,11 +583,17 @@ acldefault(GrantObjectType objtype, Oid ownerId) { AclMode world_default; AclMode owner_default; + int nacl; Acl *acl; AclItem *aip; switch (objtype) { + case ACL_OBJECT_COLUMN: + /* by default, columns have no extra privileges */ + world_default = ACL_NO_RIGHTS; + owner_default = ACL_NO_RIGHTS; + break; case ACL_OBJECT_RELATION: world_default = ACL_NO_RIGHTS; owner_default = ACL_ALL_RIGHTS_RELATION; @@ -593,7 +640,13 @@ acldefault(GrantObjectType objtype, Oid ownerId) break; } - acl = allocacl((world_default != ACL_NO_RIGHTS) ? 2 : 1); + nacl = 0; + if (world_default != ACL_NO_RIGHTS) + nacl++; + if (owner_default != ACL_NO_RIGHTS) + nacl++; + + acl = allocacl(nacl); aip = ACL_DAT(acl); if (world_default != ACL_NO_RIGHTS) @@ -614,9 +667,12 @@ acldefault(GrantObjectType objtype, Oid ownerId) * "_SYSTEM"-like ACL entry, by internally special-casing the owner * whereever we are testing grant options. */ - aip->ai_grantee = ownerId; - aip->ai_grantor = ownerId; - ACLITEM_SET_PRIVS_GOPTIONS(*aip, owner_default, ACL_NO_RIGHTS); + if (owner_default != ACL_NO_RIGHTS) + { + aip->ai_grantee = ownerId; + aip->ai_grantor = ownerId; + ACLITEM_SET_PRIVS_GOPTIONS(*aip, owner_default, ACL_NO_RIGHTS); + } return acl; } diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 4be6e08606..ecfe59e3a8 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.281 2009/01/22 17:27:54 petere Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.282 2009/01/22 20:16:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -480,7 +480,7 @@ RelationBuildTupleDesc(Relation relation) memcpy(relation->rd_att->attrs[attp->attnum - 1], attp, - ATTRIBUTE_TUPLE_SIZE); + ATTRIBUTE_FIXED_PART_SIZE); /* Update constraint/default info */ if (attp->attnotnull) @@ -1449,7 +1449,7 @@ formrdesc(const char *relationName, Oid relationReltype, { memcpy(relation->rd_att->attrs[i], &att[i], - ATTRIBUTE_TUPLE_SIZE); + ATTRIBUTE_FIXED_PART_SIZE); has_not_null |= att[i].attnotnull; /* make sure attcacheoff is valid */ relation->rd_att->attrs[i]->attcacheoff = -1; @@ -2714,7 +2714,7 @@ BuildHardcodedDescriptor(int natts, Form_pg_attribute attrs, bool hasoids) for (i = 0; i < natts; i++) { - memcpy(result->attrs[i], &attrs[i], ATTRIBUTE_TUPLE_SIZE); + memcpy(result->attrs[i], &attrs[i], ATTRIBUTE_FIXED_PART_SIZE); /* make sure attcacheoff is valid */ result->attrs[i]->attcacheoff = -1; } @@ -3441,7 +3441,7 @@ load_relcache_init_file(void) { if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len)) goto read_failed; - if (len != ATTRIBUTE_TUPLE_SIZE) + if (len != ATTRIBUTE_FIXED_PART_SIZE) goto read_failed; if ((nread = fread(rel->rd_att->attrs[i], 1, len, fp)) != len) goto read_failed; @@ -3751,7 +3751,7 @@ write_relcache_init_file(void) /* next, do all the attribute tuple form data entries */ for (i = 0; i < relform->relnatts; i++) { - write_item(rel->rd_att->attrs[i], ATTRIBUTE_TUPLE_SIZE, fp); + write_item(rel->rd_att->attrs[i], ATTRIBUTE_FIXED_PART_SIZE, fp); } /* next, do the access method specific field */ diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index dbd9be4657..15f66cd493 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -42,7 +42,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * Portions taken from FreeBSD. * - * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.166 2009/01/01 17:23:53 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.167 2009/01/22 20:16:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1601,7 +1601,7 @@ setup_depend(void) " FROM pg_ts_template;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_ts_config;\n", - "INSERT INTO pg_shdepend SELECT 0, 0, 0, tableoid, oid, 'p' " + "INSERT INTO pg_shdepend SELECT 0,0,0,0, tableoid,oid, 'p' " " FROM pg_authid;\n", NULL }; diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c index 819bb4d2e6..85b3373c53 100644 --- a/src/bin/pg_dump/dumputils.c +++ b/src/bin/pg_dump/dumputils.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.43 2009/01/01 17:23:54 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.44 2009/01/22 20:16:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -23,11 +23,13 @@ #define supports_grant_options(version) ((version) >= 70400) -static bool parseAclItem(const char *item, const char *type, const char *name, - int remoteVersion, PQExpBuffer grantee, PQExpBuffer grantor, +static bool parseAclItem(const char *item, const char *type, + const char *name, const char *subname, int remoteVersion, + PQExpBuffer grantee, PQExpBuffer grantor, PQExpBuffer privs, PQExpBuffer privswgo); static char *copyAclUserName(PQExpBuffer output, char *input); -static void AddAcl(PQExpBuffer aclbuf, const char *keyword); +static void AddAcl(PQExpBuffer aclbuf, const char *keyword, + const char *subname); /* @@ -384,6 +386,7 @@ parsePGArray(const char *atext, char ***itemarray, int *nitems) * Build GRANT/REVOKE command(s) for an object. * * name: the object name, in the form to use in the commands (already quoted) + * subname: the sub-object name, if any (already quoted); NULL if none * type: the object type (as seen in GRANT command: must be one of * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, or TABLESPACE) * acls: the ACL string fetched from the database @@ -394,12 +397,12 @@ parsePGArray(const char *atext, char ***itemarray, int *nitems) * Returns TRUE if okay, FALSE if could not parse the acl string. * The resulting commands (if any) are appended to the contents of 'sql'. * - * Note: beware of passing fmtId() result as 'name', since this routine - * uses fmtId() internally. + * Note: beware of passing a fmtId() result directly as 'name' or 'subname', + * since this routine uses fmtId() internally. */ bool -buildACLCommands(const char *name, const char *type, - const char *acls, const char *owner, +buildACLCommands(const char *name, const char *subname, + const char *type, const char *acls, const char *owner, int remoteVersion, PQExpBuffer sql) { @@ -448,8 +451,10 @@ buildACLCommands(const char *name, const char *type, * wire-in knowledge about the default public privileges for different * kinds of objects. */ - appendPQExpBuffer(firstsql, "REVOKE ALL ON %s %s FROM PUBLIC;\n", - type, name); + appendPQExpBuffer(firstsql, "REVOKE ALL"); + if (subname) + appendPQExpBuffer(firstsql, "(%s)", subname); + appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name); /* * We still need some hacking though to cover the case where new default @@ -468,7 +473,7 @@ buildACLCommands(const char *name, const char *type, /* Scan individual ACL items */ for (i = 0; i < naclitems; i++) { - if (!parseAclItem(aclitems[i], type, name, remoteVersion, + if (!parseAclItem(aclitems[i], type, name, subname, remoteVersion, grantee, grantor, privs, privswgo)) return false; @@ -491,15 +496,19 @@ buildACLCommands(const char *name, const char *type, ? strcmp(privswgo->data, "ALL") != 0 : strcmp(privs->data, "ALL") != 0) { - appendPQExpBuffer(firstsql, "REVOKE ALL ON %s %s FROM %s;\n", - type, name, - fmtId(grantee->data)); + appendPQExpBuffer(firstsql, "REVOKE ALL"); + if (subname) + appendPQExpBuffer(firstsql, "(%s)", subname); + appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n", + type, name, fmtId(grantee->data)); if (privs->len > 0) - appendPQExpBuffer(firstsql, "GRANT %s ON %s %s TO %s;\n", + appendPQExpBuffer(firstsql, + "GRANT %s ON %s %s TO %s;\n", privs->data, type, name, fmtId(grantee->data)); if (privswgo->len > 0) - appendPQExpBuffer(firstsql, "GRANT %s ON %s %s TO %s WITH GRANT OPTION;\n", + appendPQExpBuffer(firstsql, + "GRANT %s ON %s %s TO %s WITH GRANT OPTION;\n", privswgo->data, type, name, fmtId(grantee->data)); } @@ -553,8 +562,13 @@ buildACLCommands(const char *name, const char *type, * If we didn't find any owner privs, the owner must have revoked 'em all */ if (!found_owner_privs && owner) - appendPQExpBuffer(firstsql, "REVOKE ALL ON %s %s FROM %s;\n", + { + appendPQExpBuffer(firstsql, "REVOKE ALL"); + if (subname) + appendPQExpBuffer(firstsql, "(%s)", subname); + appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n", type, name, fmtId(owner)); + } destroyPQExpBuffer(grantee); destroyPQExpBuffer(grantor); @@ -587,8 +601,9 @@ buildACLCommands(const char *name, const char *type, * appropriate. */ static bool -parseAclItem(const char *item, const char *type, const char *name, - int remoteVersion, PQExpBuffer grantee, PQExpBuffer grantor, +parseAclItem(const char *item, const char *type, + const char *name, const char *subname, int remoteVersion, + PQExpBuffer grantee, PQExpBuffer grantor, PQExpBuffer privs, PQExpBuffer privswgo) { char *buf; @@ -626,12 +641,12 @@ do { \ { \ if (*(pos + 1) == '*') \ { \ - AddAcl(privswgo, keywd); \ + AddAcl(privswgo, keywd, subname); \ all_without_go = false; \ } \ else \ { \ - AddAcl(privs, keywd); \ + AddAcl(privs, keywd, subname); \ all_with_go = false; \ } \ } \ @@ -654,13 +669,18 @@ do { \ /* table only */ CONVERT_PRIV('a', "INSERT"); if (remoteVersion >= 70200) - { - CONVERT_PRIV('d', "DELETE"); CONVERT_PRIV('x', "REFERENCES"); - CONVERT_PRIV('t', "TRIGGER"); + /* rest are not applicable to columns */ + if (subname == NULL) + { + if (remoteVersion >= 70200) + { + CONVERT_PRIV('d', "DELETE"); + CONVERT_PRIV('t', "TRIGGER"); + } + if (remoteVersion >= 80400) + CONVERT_PRIV('D', "TRUNCATE"); } - if (remoteVersion >= 80400) - CONVERT_PRIV('D', "TRUNCATE"); } /* UPDATE */ @@ -700,11 +720,15 @@ do { \ { resetPQExpBuffer(privs); printfPQExpBuffer(privswgo, "ALL"); + if (subname) + appendPQExpBuffer(privswgo, "(%s)", subname); } else if (all_without_go) { resetPQExpBuffer(privswgo); printfPQExpBuffer(privs, "ALL"); + if (subname) + appendPQExpBuffer(privs, "(%s)", subname); } free(buf); @@ -757,11 +781,13 @@ copyAclUserName(PQExpBuffer output, char *input) * Append a privilege keyword to a keyword list, inserting comma if needed. */ static void -AddAcl(PQExpBuffer aclbuf, const char *keyword) +AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname) { if (aclbuf->len > 0) appendPQExpBufferChar(aclbuf, ','); appendPQExpBuffer(aclbuf, "%s", keyword); + if (subname) + appendPQExpBuffer(aclbuf, "(%s)", subname); } diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h index 13341d77a8..b8a3532fa8 100644 --- a/src/bin/pg_dump/dumputils.h +++ b/src/bin/pg_dump/dumputils.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.h,v 1.22 2009/01/01 17:23:54 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.h,v 1.23 2009/01/22 20:16:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,8 +28,8 @@ extern void appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix); extern int parse_version(const char *versionString); extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems); -extern bool buildACLCommands(const char *name, const char *type, - const char *acls, const char *owner, +extern bool buildACLCommands(const char *name, const char *subname, + const char *type, const char *acls, const char *owner, int remoteVersion, PQExpBuffer sql); extern void processSQLNamePattern(PGconn *conn, PQExpBuffer buf, diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 76558856e2..f47333c605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.515 2009/01/22 17:27:54 petere Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.516 2009/01/22 20:16:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -165,7 +165,7 @@ static void dumpUserMappings(Archive *fout, const char *target, const char *owner, CatalogId catalogId, DumpId dumpId); static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, - const char *type, const char *name, + const char *type, const char *name, const char *subname, const char *tag, const char *nspname, const char *owner, const char *acls); @@ -5929,7 +5929,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId); dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA", - qnspname, nspinfo->dobj.name, NULL, + qnspname, NULL, nspinfo->dobj.name, NULL, nspinfo->rolname, nspinfo->nspacl); free(qnspname); @@ -6786,7 +6786,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) if (plang->lanpltrusted) dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE", - qlanname, plang->dobj.name, + qlanname, NULL, plang->dobj.name, lanschema, plang->lanowner, plang->lanacl); @@ -7342,7 +7342,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) finfo->dobj.catId, 0, finfo->dobj.dumpId); dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION", - funcsig, funcsig_tag, + funcsig, NULL, funcsig_tag, finfo->dobj.namespace->dobj.name, finfo->rolname, finfo->proacl); @@ -8828,7 +8828,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) dumpACL(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId, "FUNCTION", - aggsig, aggsig_tag, + aggsig, NULL, aggsig_tag, agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname, agginfo->aggfn.proacl); @@ -9227,7 +9227,7 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) namecopy = strdup(fmtId(fdwinfo->dobj.name)); dumpACL(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId, "FOREIGN DATA WRAPPER", - namecopy, fdwinfo->dobj.name, + namecopy, NULL, fdwinfo->dobj.name, NULL, fdwinfo->rolname, fdwinfo->fdwacl); free(namecopy); @@ -9305,7 +9305,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) namecopy = strdup(fmtId(srvinfo->dobj.name)); dumpACL(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId, "SERVER", - namecopy, srvinfo->dobj.name, + namecopy, NULL, srvinfo->dobj.name, NULL, srvinfo->rolname, srvinfo->srvacl); free(namecopy); @@ -9412,6 +9412,7 @@ dumpUserMappings(Archive *fout, const char *target, * 'objDumpId' is the dump ID of the underlying object. * 'type' must be TABLE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, or TABLESPACE. * 'name' is the formatted name of the object. Must be quoted etc. already. + * 'subname' is the formatted name of the sub-object, if any. Must be quoted. * 'tag' is the tag for the archive entry (typ. unquoted name of object). * 'nspname' is the namespace the object is in (NULL if none). * 'owner' is the owner, NULL if there is no owner (for languages). @@ -9421,7 +9422,7 @@ dumpUserMappings(Archive *fout, const char *target, */ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, - const char *type, const char *name, + const char *type, const char *name, const char *subname, const char *tag, const char *nspname, const char *owner, const char *acls) { @@ -9433,7 +9434,7 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, sql = createPQExpBuffer(); - if (!buildACLCommands(name, type, acls, owner, fout->remoteVersion, sql)) + if (!buildACLCommands(name, subname, type, acls, owner, fout->remoteVersion, sql)) { write_msg(NULL, "could not parse ACL list (%s) for object \"%s\" (%s)\n", acls, name, type); @@ -9459,10 +9460,10 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, static void dumpTable(Archive *fout, TableInfo *tbinfo) { - char *namecopy; - if (tbinfo->dobj.dump) { + char *namecopy; + if (tbinfo->relkind == RELKIND_SEQUENCE) dumpSequence(fout, tbinfo); else if (!dataOnly) @@ -9472,9 +9473,51 @@ dumpTable(Archive *fout, TableInfo *tbinfo) namecopy = strdup(fmtId(tbinfo->dobj.name)); dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE", - namecopy, tbinfo->dobj.name, + namecopy, NULL, tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, tbinfo->relacl); + + /* + * Handle column ACLs, if any. Note: we pull these with a separate + * query rather than trying to fetch them during getTableAttrs, so + * that we won't miss ACLs on system columns. + */ + if (g_fout->remoteVersion >= 80400) + { + PQExpBuffer query = createPQExpBuffer(); + PGresult *res; + int i; + + appendPQExpBuffer(query, + "SELECT attname, attacl FROM pg_catalog.pg_attribute " + "WHERE attrelid = '%u' AND NOT attisdropped AND attacl IS NOT NULL " + "ORDER BY attnum", + tbinfo->dobj.catId.oid); + res = PQexec(g_conn, query->data); + check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); + + for (i = 0; i < PQntuples(res); i++) + { + char *attname = PQgetvalue(res, i, 0); + char *attacl = PQgetvalue(res, i, 1); + char *attnamecopy; + char *acltag; + + attnamecopy = strdup(fmtId(attname)); + acltag = malloc(strlen(tbinfo->dobj.name) + strlen(attname) + 2); + sprintf(acltag, "%s.%s", tbinfo->dobj.name, attname); + /* Column's GRANT type is always TABLE */ + dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, "TABLE", + namecopy, attnamecopy, acltag, + tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, + attacl); + free(attnamecopy); + free(acltag); + } + PQclear(res); + destroyPQExpBuffer(query); + } + free(namecopy); } } diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 1be955b3e7..f5a08eb834 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.112 2009/01/06 18:01:57 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.113 2009/01/22 20:16:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -882,7 +882,7 @@ dumpTablespaces(PGconn *conn) appendPQExpBuffer(buf, ";\n"); if (!skip_acls && - !buildACLCommands(fspcname, "TABLESPACE", spcacl, spcowner, + !buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, spcowner, server_version, buf)) { fprintf(stderr, _("%s: could not parse ACL list (%s) for tablespace \"%s\"\n"), @@ -1075,7 +1075,7 @@ dumpCreateDB(PGconn *conn) } if (!skip_acls && - !buildACLCommands(fdbname, "DATABASE", dbacl, dbowner, + !buildACLCommands(fdbname, NULL, "DATABASE", dbacl, dbowner, server_version, buf)) { fprintf(stderr, _("%s: could not parse ACL list (%s) for database \"%s\"\n"), diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 01cb9bee4b..8414d1bff4 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -8,7 +8,7 @@ * * Copyright (c) 2000-2009, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.197 2009/01/20 02:13:42 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.198 2009/01/22 20:16:08 tgl Exp $ */ #include "postgres_fe.h" @@ -519,7 +519,7 @@ listAllDbs(bool verbose) /* - * List Tables Grant/Revoke Permissions + * List Tables' Grant/Revoke Permissions * \z (now also \dp -- perhaps more mnemonic) */ bool @@ -528,7 +528,7 @@ permissionsList(const char *pattern) PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, true, false}; + static const bool translate_columns[] = {false, false, true, false, false}; initPQExpBuffer(&buf); @@ -544,7 +544,18 @@ permissionsList(const char *pattern) gettext_noop("Name"), gettext_noop("table"), gettext_noop("view"), gettext_noop("sequence"), gettext_noop("Type")); + printACLColumn(&buf, "c.relacl"); + + if (pset.sversion >= 80400) + appendPQExpBuffer(&buf, + ",\n pg_catalog.array_to_string(ARRAY(\n" + " SELECT attname || E':\\n ' || pg_catalog.array_to_string(attacl, E'\\n ')\n" + " FROM pg_catalog.pg_attribute a\n" + " WHERE attrelid = c.oid AND NOT attisdropped AND attacl IS NOT NULL\n" + " ), E'\\n') AS \"%s\"", + gettext_noop("Column access privileges")); + appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" "WHERE c.relkind IN ('r', 'v', 'S')\n"); @@ -1907,7 +1918,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, true, false, false, false}; + static const bool translate_columns[] = {false, false, true, false, false, false, false}; if (!(showTables || showIndexes || showViews || showSeq)) showTables = showViews = showSeq = true; @@ -1965,7 +1976,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys if (showSeq) appendPQExpBuffer(&buf, "'S',"); if (showSystem) - appendPQExpBuffer(&buf, "'s',"); /* was RELKIND_SPECIAL in <= 8.1.X */ + appendPQExpBuffer(&buf, "'s',"); /* was RELKIND_SPECIAL in <= 8.1 */ appendPQExpBuffer(&buf, "''"); /* dummy */ appendPQExpBuffer(&buf, ")\n"); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 64a05e738d..11349023d9 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.518 2009/01/16 13:27:24 heikki Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.519 2009/01/22 20:16:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200901161 +#define CATALOG_VERSION_NO 200901221 #endif diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index f615d45011..99380da566 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.38 2009/01/01 17:23:56 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.39 2009/01/22 20:16:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -108,7 +108,7 @@ typedef struct ObjectAddress { Oid classId; /* Class Id from pg_class */ Oid objectId; /* OID of the object */ - int32 objectSubId; /* Subitem within the object (column of table) */ + int32 objectSubId; /* Subitem within object (eg column), or 0 */ } ObjectAddress; /* expansible list of ObjectAddresses (private in dependency.c) */ @@ -221,14 +221,15 @@ extern void recordSharedDependencyOn(ObjectAddress *depender, ObjectAddress *referenced, SharedDependencyType deptype); -extern void deleteSharedDependencyRecordsFor(Oid classId, Oid objectId); +extern void deleteSharedDependencyRecordsFor(Oid classId, Oid objectId, + int32 objectSubId); extern void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner); extern void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId); -extern void updateAclDependencies(Oid classId, Oid objectId, +extern void updateAclDependencies(Oid classId, Oid objectId, int32 objectSubId, Oid ownerId, bool isGrant, int noldmembers, Oid *oldmembers, int nnewmembers, Oid *newmembers); diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index e2c10efe77..1c517c7f3b 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.105 2009/01/01 17:23:56 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.106 2009/01/22 20:16:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -202,7 +202,7 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using bt #define RewriteRelRulenameIndexId 2693 /* This following index is not used for a cache and is not unique */ -DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops)); +DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops)); #define SharedDependDependerIndexId 1232 /* This following index is not used for a cache and is not unique */ DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops)); diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h index 5411019ab1..3fddf7e922 100644 --- a/src/include/catalog/pg_attribute.h +++ b/src/include/catalog/pg_attribute.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.145 2009/01/01 17:23:56 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.146 2009/01/22 20:16:08 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -144,14 +144,24 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS /* Number of times inherited from direct parent relation(s) */ int4 attinhcount; + + /* + * VARIABLE LENGTH FIELDS start here. These fields may be NULL, too. + * + * NOTE: the following fields are not present in tuple descriptors! + */ + + /* Column-level access permissions */ + aclitem attacl[1]; } FormData_pg_attribute; /* - * someone should figure out how to do this properly. (The problem is - * the size of the C struct is not the same as the size of the tuple - * because of alignment padding at the end of the struct.) + * ATTRIBUTE_FIXED_PART_SIZE is the size of the fixed-layout, + * guaranteed-not-null part of a pg_attribute row. This is in fact as much + * of the row as gets copied into tuple descriptors, so don't expect you + * can access fields beyond attinhcount except in a real tuple! */ -#define ATTRIBUTE_TUPLE_SIZE \ +#define ATTRIBUTE_FIXED_PART_SIZE \ (offsetof(FormData_pg_attribute,attinhcount) + sizeof(int4)) /* ---------------- @@ -166,7 +176,7 @@ typedef FormData_pg_attribute *Form_pg_attribute; * ---------------- */ -#define Natts_pg_attribute 17 +#define Natts_pg_attribute 18 #define Anum_pg_attribute_attrelid 1 #define Anum_pg_attribute_attname 2 #define Anum_pg_attribute_atttypid 3 @@ -184,24 +194,9 @@ typedef FormData_pg_attribute *Form_pg_attribute; #define Anum_pg_attribute_attisdropped 15 #define Anum_pg_attribute_attislocal 16 #define Anum_pg_attribute_attinhcount 17 +#define Anum_pg_attribute_attacl 18 - -/* ---------------- - * SCHEMA_ macros for declaring hardcoded tuple descriptors. - * these are used in utils/cache/relcache.c - * ---------------- -#define SCHEMA_NAME(x) CppConcat(Name_,x) -#define SCHEMA_DESC(x) CppConcat(Desc_,x) -#define SCHEMA_NATTS(x) CppConcat(Natts_,x) -#define SCHEMA_DEF(x) \ - FormData_pg_attribute \ - SCHEMA_DESC(x) [ SCHEMA_NATTS(x) ] = \ - { \ - CppConcat(Schema_,x) \ - } - */ - /* ---------------- * initial contents of pg_attribute * @@ -217,244 +212,246 @@ typedef FormData_pg_attribute *Form_pg_attribute; * ---------------- */ #define Schema_pg_type \ -{ 1247, {"typname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0 }, \ -{ 1247, {"typnamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typowner"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typlen"}, 21, -1, 2, 4, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \ -{ 1247, {"typbyval"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 1247, {"typtype"}, 18, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 1247, {"typcategory"}, 18, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 1247, {"typispreferred"},16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 1247, {"typisdefined"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 1247, {"typdelim"}, 18, -1, 1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 1247, {"typrelid"}, 26, -1, 4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typelem"}, 26, -1, 4, 12, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typarray"}, 26, -1, 4, 13, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typinput"}, 24, -1, 4, 14, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typoutput"}, 24, -1, 4, 15, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typreceive"}, 24, -1, 4, 16, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typsend"}, 24, -1, 4, 17, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typmodin"}, 24, -1, 4, 18, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typmodout"}, 24, -1, 4, 19, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typanalyze"}, 24, -1, 4, 20, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typalign"}, 18, -1, 1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 1247, {"typstorage"}, 18, -1, 1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 1247, {"typnotnull"}, 16, -1, 1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 1247, {"typbasetype"}, 26, -1, 4, 24, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typtypmod"}, 23, -1, 4, 25, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typndims"}, 23, -1, 4, 26, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 1247, {"typdefaultbin"}, 25, -1, -1, 27, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \ -{ 1247, {"typdefault"}, 25, -1, -1, 28, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 } +{ 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)); -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)); +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 } +{ 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)); -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)); +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 } +{ 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)); -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)); +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 } +{ 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)); -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)); +DATA(insert ( 1259 relname 19 -1 NAMEDATALEN 1 0 -1 -1 f p c t f f t 0 _null_)); +DATA(insert ( 1259 relnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 reltype 26 -1 4 3 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 relowner 26 -1 4 4 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 relam 26 -1 4 5 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 relfilenode 26 -1 4 6 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 reltablespace 26 -1 4 7 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 relpages 23 -1 4 8 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 reltuples 700 -1 4 9 0 -1 -1 FLOAT4PASSBYVAL p i t f f t 0 _null_)); +DATA(insert ( 1259 reltoastrelid 26 -1 4 10 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 reltoastidxid 26 -1 4 11 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 relhasindex 16 -1 1 12 0 -1 -1 t p c t f f t 0 _null_)); +DATA(insert ( 1259 relisshared 16 -1 1 13 0 -1 -1 t p c t f f t 0 _null_)); +DATA(insert ( 1259 relkind 18 -1 1 14 0 -1 -1 t p c t f f t 0 _null_)); +DATA(insert ( 1259 relnatts 21 -1 2 15 0 -1 -1 t p s t f f t 0 _null_)); +DATA(insert ( 1259 relchecks 21 -1 2 16 0 -1 -1 t p s t f f t 0 _null_)); +DATA(insert ( 1259 relhasoids 16 -1 1 17 0 -1 -1 t p c t f f t 0 _null_)); +DATA(insert ( 1259 relhaspkey 16 -1 1 18 0 -1 -1 t p c t f f t 0 _null_)); +DATA(insert ( 1259 relhasrules 16 -1 1 19 0 -1 -1 t p c t f f t 0 _null_)); +DATA(insert ( 1259 relhastriggers 16 -1 1 20 0 -1 -1 t p c t f f t 0 _null_)); +DATA(insert ( 1259 relhassubclass 16 -1 1 21 0 -1 -1 t p c t f f t 0 _null_)); +DATA(insert ( 1259 relfrozenxid 28 -1 4 22 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 relacl 1034 -1 -1 23 1 -1 -1 f x i f f f t 0 _null_)); +DATA(insert ( 1259 reloptions 1009 -1 -1 24 1 -1 -1 f x i f f f t 0 _null_)); +DATA(insert ( 1259 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0 _null_)); +DATA(insert ( 1259 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); +DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); /* ---------------- * pg_index @@ -465,19 +462,19 @@ DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); * ---------------- */ #define Schema_pg_index \ -{ 0, {"indexrelid"}, 26, -1, 4, 1, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 0, {"indrelid"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \ -{ 0, {"indnatts"}, 21, -1, 2, 3, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \ -{ 0, {"indisunique"}, 16, -1, 1, 4, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 0, {"indisprimary"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 0, {"indisclustered"}, 16, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 0, {"indisvalid"}, 16, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 0, {"indcheckxmin"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 0, {"indisready"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 0, {"indkey"}, 22, -1, -1, 10, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \ -{ 0, {"indclass"}, 30, -1, -1, 11, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \ -{ 0, {"indoption"}, 22, -1, -1, 12, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \ -{ 0, {"indexprs"}, 25, -1, -1, 13, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \ -{ 0, {"indpred"}, 25, -1, -1, 14, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 } +{ 0, {"indexrelid"}, 26, -1, 4, 1, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ +{ 0, {"indrelid"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ +{ 0, {"indnatts"}, 21, -1, 2, 3, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \ +{ 0, {"indisunique"}, 16, -1, 1, 4, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ +{ 0, {"indisprimary"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ +{ 0, {"indisclustered"}, 16, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ +{ 0, {"indisvalid"}, 16, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ +{ 0, {"indcheckxmin"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ +{ 0, {"indisready"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ +{ 0, {"indkey"}, 22, -1, -1, 10, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \ +{ 0, {"indclass"}, 30, -1, -1, 11, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \ +{ 0, {"indoption"}, 22, -1, -1, 12, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \ +{ 0, {"indexprs"}, 25, -1, -1, 13, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \ +{ 0, {"indpred"}, 25, -1, -1, 14, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } } #endif /* PG_ATTRIBUTE_H */ diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 97ea79544b..776158805d 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.111 2009/01/01 17:23:56 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.112 2009/01/22 20:16:09 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -123,7 +123,7 @@ typedef FormData_pg_class *Form_pg_class; /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */ DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 28 0 t f f f f 3 _null_ _null_ )); DESCR(""); -DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 17 0 f f f f f 3 _null_ _null_ )); +DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 18 0 f f f f f 3 _null_ _null_ )); DESCR(""); DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 25 0 t f f f f 3 _null_ _null_ )); DESCR(""); diff --git a/src/include/catalog/pg_shdepend.h b/src/include/catalog/pg_shdepend.h index f741333914..97f01df4c1 100644 --- a/src/include/catalog/pg_shdepend.h +++ b/src/include/catalog/pg_shdepend.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_shdepend.h,v 1.8 2009/01/01 17:23:58 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_shdepend.h,v 1.9 2009/01/22 20:16:09 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -39,10 +39,12 @@ CATALOG(pg_shdepend,1214) BKI_SHARED_RELATION BKI_WITHOUT_OIDS Oid dbid; /* OID of database containing object */ Oid classid; /* OID of table containing object */ Oid objid; /* OID of object itself */ + int4 objsubid; /* column number, or 0 if not used */ /* * Identification of the independent (referenced) object. This is always - * a shared object, so we need no database ID field. + * a shared object, so we need no database ID field. We don't bother + * with a sub-object ID either. */ Oid refclassid; /* OID of table containing object */ Oid refobjid; /* OID of object itself */ @@ -65,13 +67,14 @@ typedef FormData_pg_shdepend *Form_pg_shdepend; * compiler constants for pg_shdepend * ---------------- */ -#define Natts_pg_shdepend 6 +#define Natts_pg_shdepend 7 #define Anum_pg_shdepend_dbid 1 #define Anum_pg_shdepend_classid 2 #define Anum_pg_shdepend_objid 3 -#define Anum_pg_shdepend_refclassid 4 -#define Anum_pg_shdepend_refobjid 5 -#define Anum_pg_shdepend_deptype 6 +#define Anum_pg_shdepend_objsubid 4 +#define Anum_pg_shdepend_refclassid 5 +#define Anum_pg_shdepend_refobjid 6 +#define Anum_pg_shdepend_deptype 7 /* diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index 0b0c6c2232..123159e59a 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.71 2009/01/22 19:16:31 heikki Exp $ + * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.72 2009/01/22 20:16:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -104,7 +104,8 @@ extern PGDLLIMPORT int SessionReplicationRole; #define TRIGGER_FIRES_ON_REPLICA 'R' #define TRIGGER_DISABLED 'D' -extern Oid CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid); +extern Oid CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid, + bool checkPermissions); extern void DropTrigger(Oid relid, const char *trigname, DropBehavior behavior, bool missing_ok); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 270760527b..91efce9462 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.218 2009/01/01 17:24:00 momjian Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.219 2009/01/22 20:16:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -369,7 +369,7 @@ typedef enum NodeTag T_FkConstraint, T_PrivGrantee, T_FuncWithArgs, - T_PrivTarget, + T_AccessPriv, T_CreateOpClassItem, T_InhRelation, T_FunctionParameter, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index ce225f801e..b73e9f2cda 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -13,13 +13,14 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.388 2009/01/16 13:27:24 heikki Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.389 2009/01/22 20:16:09 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef PARSENODES_H #define PARSENODES_H +#include "nodes/bitmapset.h" #include "nodes/primnodes.h" #include "nodes/value.h" @@ -622,6 +623,15 @@ typedef struct XmlSerialize * then do the permissions checks using the access rights of that user, * not the current effective user ID. (This allows rules to act as * setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have + * table-wide permissions then it is sufficient to have the permissions + * on all columns identified in selectedCols (for SELECT) and/or + * modifiedCols (for INSERT/UPDATE; we can tell which from the query type). + * selectedCols and modifiedCols are bitmapsets, which cannot have negative + * integer members, so we subtract FirstLowInvalidHeapAttributeNumber from + * column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. *-------------------- */ typedef enum RTEKind @@ -644,7 +654,7 @@ typedef struct RangeTblEntry /* * XXX the fields applicable to only some rte kinds should be merged into * a union. I didn't do this yet because the diffs would impact a lot of - * code that is being actively worked on. FIXME later. + * code that is being actively worked on. FIXME someday. */ /* @@ -705,6 +715,8 @@ typedef struct RangeTblEntry bool inFromCl; /* present in FROM clause? */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ + Bitmapset *selectedCols; /* columns needing SELECT permission */ + Bitmapset *modifiedCols; /* columns needing INSERT/UPDATE permission */ } RangeTblEntry; /* @@ -1168,6 +1180,7 @@ typedef struct AlterDomainStmt */ typedef enum GrantObjectType { + ACL_OBJECT_COLUMN, /* column */ ACL_OBJECT_RELATION, /* table, view */ ACL_OBJECT_SEQUENCE, /* sequence */ ACL_OBJECT_DATABASE, /* database */ @@ -1186,8 +1199,8 @@ typedef struct GrantStmt GrantObjectType objtype; /* kind of object being operated on */ List *objects; /* list of RangeVar nodes, FuncWithArgs nodes, * or plain names (as Value strings) */ - List *privileges; /* list of privilege names (as Strings) */ - /* privileges == NIL denotes "all privileges" */ + List *privileges; /* list of AccessPriv nodes */ + /* privileges == NIL denotes ALL PRIVILEGES */ List *grantees; /* list of PrivGrantee nodes */ bool grant_option; /* grant or revoke grant option */ DropBehavior behavior; /* drop behavior (for REVOKE) */ @@ -1211,18 +1224,27 @@ typedef struct FuncWithArgs List *funcargs; /* list of Typename nodes */ } FuncWithArgs; -/* This is only used internally in gram.y. */ -typedef struct PrivTarget +/* + * An access privilege, with optional list of column names + * priv_name == NULL denotes ALL PRIVILEGES (only used with a column list) + * cols == NIL denotes "all columns" + * Note that simple "ALL PRIVILEGES" is represented as a NIL list, not + * an AccessPriv with both fields null. + */ +typedef struct AccessPriv { NodeTag type; - GrantObjectType objtype; - List *objs; -} PrivTarget; + char *priv_name; /* string name of privilege */ + List *cols; /* list of Value strings */ +} AccessPriv; /* ---------------------- * Grant/Revoke Role Statement * - * Note: the lists of roles are lists of names, as Value strings + * Note: because of the parsing ambiguity with the GRANT + * statement, granted_roles is a list of AccessPriv; the execution code + * should complain if any column lists appear. grantee_roles is a list + * of role names, as Value strings. * ---------------------- */ typedef struct GrantRoleStmt diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index fb5e1fcf45..5e49b1a3dd 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.60 2009/01/01 17:24:00 momjian Exp $ + * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.61 2009/01/22 20:16:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -33,6 +33,10 @@ * Note that neither relname nor refname of these entries are necessarily * unique; searching the rtable by name is a bad idea. * + * p_joinexprs: list of JoinExpr nodes associated with p_rtable entries. + * This is one-for-one with p_rtable, but contains NULLs for non-join + * RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins. + * * p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that * will become the fromlist of the query's top-level FromExpr node. * @@ -77,6 +81,7 @@ typedef struct ParseState struct ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ List *p_relnamespace; /* current namespace for relations */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 76622af25d..cb133fced6 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.62 2009/01/01 17:24:00 momjian Exp $ + * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.63 2009/01/22 20:16:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -46,6 +46,8 @@ extern Node *qualifiedNameToVar(ParseState *pstate, char *colname, bool implicitRTEOK, int location); +extern void markVarForSelectPriv(ParseState *pstate, Var *var, + RangeTblEntry *rte); extern Relation parserOpenTable(ParseState *pstate, const RangeVar *relation, int lockmode); extern RangeTblEntry *addRangeTableEntry(ParseState *pstate, diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 02cf425619..fed2de5b74 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.106 2009/01/01 17:24:02 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.107 2009/01/22 20:16:09 tgl Exp $ * * NOTES * An ACL array is simply an array of AclItems, representing the union @@ -143,6 +143,7 @@ typedef ArrayType Acl; /* * Bitmasks defining "all rights" for each supported object type */ +#define ACL_ALL_RIGHTS_COLUMN (ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_REFERENCES) #define ACL_ALL_RIGHTS_RELATION (ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_DELETE|ACL_TRUNCATE|ACL_REFERENCES|ACL_TRIGGER) #define ACL_ALL_RIGHTS_SEQUENCE (ACL_USAGE|ACL_SELECT|ACL_UPDATE) #define ACL_ALL_RIGHTS_DATABASE (ACL_CREATE|ACL_CREATE_TEMP|ACL_CONNECT) @@ -172,6 +173,7 @@ typedef enum /* currently it's only used to tell aclcheck_error what to say */ typedef enum AclObjectKind { + ACL_KIND_COLUMN, /* pg_attribute */ ACL_KIND_CLASS, /* pg_class */ ACL_KIND_SEQUENCE, /* pg_sequence */ ACL_KIND_DATABASE, /* pg_database */ @@ -195,9 +197,14 @@ typedef enum AclObjectKind * The information about one Grant/Revoke statement, in internal format: object * and grantees names have been turned into Oids, the privilege list is an * AclMode bitmask. If 'privileges' is ACL_NO_RIGHTS (the 0 value) and - * all_privs is true, it will be internally turned into the right kind of + * all_privs is true, 'privileges' will be internally set to the right kind of * ACL_ALL_RIGHTS_*, depending on the object type (NB - this will modify the * InternalGrant struct!) + * + * Note: 'all_privs' and 'privileges' represent object-level privileges only. + * There might also be column-level privilege specifications, which are + * represented in col_privs (this is a list of untransformed AccessPriv nodes). + * Column privileges are only valid for objtype ACL_OBJECT_RELATION. */ typedef struct { @@ -206,6 +213,7 @@ typedef struct List *objects; bool all_privs; AclMode privileges; + List *col_privs; List *grantees; bool grant_option; DropBehavior behavior; @@ -218,6 +226,8 @@ extern Acl *acldefault(GrantObjectType objtype, Oid ownerId); extern Acl *aclupdate(const Acl *old_acl, const AclItem *mod_aip, int modechg, Oid ownerId, DropBehavior behavior); extern Acl *aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId); +extern Acl *aclcopy(const Acl *orig_acl); +extern Acl *aclconcat(const Acl *left_acl, const Acl *right_acl); extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId, AclMode mask, AclMaskHow how); @@ -253,6 +263,8 @@ extern Datum hash_aclitem(PG_FUNCTION_ARGS); extern void ExecuteGrantStmt(GrantStmt *stmt); extern void ExecGrantStmt_oids(InternalGrant *istmt); +extern AclMode pg_attribute_aclmask(Oid table_oid, AttrNumber attnum, + Oid roleid, AclMode mask, AclMaskHow how); extern AclMode pg_class_aclmask(Oid table_oid, Oid roleid, AclMode mask, AclMaskHow how); extern AclMode pg_database_aclmask(Oid db_oid, Oid roleid, @@ -270,6 +282,10 @@ extern AclMode pg_foreign_data_wrapper_aclmask(Oid fdw_oid, Oid roleid, extern AclMode pg_foreign_server_aclmask(Oid srv_oid, Oid roleid, AclMode mask, AclMaskHow how); +extern AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, + Oid roleid, AclMode mode); +extern AclResult pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, + AclMode mode, AclMaskHow how); extern AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode); extern AclResult pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode); extern AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode); @@ -282,6 +298,9 @@ extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mod extern void aclcheck_error(AclResult aclerr, AclObjectKind objectkind, const char *objectname); +extern void aclcheck_error_col(AclResult aclerr, AclObjectKind objectkind, + const char *objectname, const char *colname); + /* ownercheck routines just return true (owner) or false (not) */ extern bool pg_class_ownercheck(Oid class_oid, Oid roleid); extern bool pg_type_ownercheck(Oid type_oid, Oid roleid); diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out index 178f4221fc..6eb851a378 100644 --- a/src/test/regress/expected/dependency.out +++ b/src/test/regress/expected/dependency.out @@ -68,21 +68,21 @@ NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "deptest_pkey" fo GRANT ALL ON deptest1 TO regression_user2; RESET SESSION AUTHORIZATION; \z deptest1 - Access privileges - Schema | Name | Type | Access privileges ---------+----------+-------+-------------------------------------------------- - public | deptest1 | table | regression_user0=arwdDxt/regression_user0 - : regression_user1=a*r*w*d*D*x*t*/regression_user0 - : regression_user2=arwdDxt/regression_user1 + Access privileges + Schema | Name | Type | Access privileges | Column access privileges +--------+----------+-------+--------------------------------------------------+-------------------------- + public | deptest1 | table | regression_user0=arwdDxt/regression_user0 | + : regression_user1=a*r*w*d*D*x*t*/regression_user0 + : regression_user2=arwdDxt/regression_user1 (1 row) DROP OWNED BY regression_user1; -- all grants revoked \z deptest1 - Access privileges - Schema | Name | Type | Access privileges ---------+----------+-------+------------------------------------------- - public | deptest1 | table | regression_user0=arwdDxt/regression_user0 + Access privileges + Schema | Name | Type | Access privileges | Column access privileges +--------+----------+-------+-------------------------------------------+-------------------------- + public | deptest1 | table | regression_user0=arwdDxt/regression_user0 | (1 row) -- table was dropped diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index f4a2bd8d8f..88d1ab3b78 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -246,6 +246,147 @@ SELECT * FROM atest2; -- ok SELECT * FROM atestv2; -- fail (even though regressuser2 can access underlying atest2) ERROR: permission denied for relation atest2 +-- Test column level permissions +SET SESSION AUTHORIZATION regressuser1; +CREATE TABLE atest5 (one int, two int, three int); +CREATE TABLE atest6 (one int, two int, blue int); +GRANT SELECT (one), INSERT (two), UPDATE (three) ON atest5 TO regressuser4; +GRANT ALL (one) ON atest5 TO regressuser3; +INSERT INTO atest5 VALUES (1,2,3); +SET SESSION AUTHORIZATION regressuser4; +SELECT * FROM atest5; -- fail +ERROR: permission denied for relation atest5 +SELECT one FROM atest5; -- ok + one +----- + 1 +(1 row) + +SELECT two FROM atest5; -- fail +ERROR: permission denied for relation atest5 +SELECT atest5 FROM atest5; -- fail +ERROR: permission denied for relation atest5 +SELECT 1 FROM atest5; -- ok + ?column? +---------- + 1 +(1 row) + +SELECT 1 FROM atest5 a JOIN atest5 b USING (one); -- ok + ?column? +---------- + 1 +(1 row) + +SELECT 1 FROM atest5 a JOIN atest5 b USING (two); -- fail +ERROR: permission denied for relation atest5 +SELECT 1 FROM atest5 a NATURAL JOIN atest5 b; -- fail +ERROR: permission denied for relation atest5 +SELECT (j.*) IS NULL FROM (atest5 a JOIN atest5 b USING (one)) j; -- fail +ERROR: permission denied for relation atest5 +SELECT 1 FROM atest5 WHERE two = 2; -- fail +ERROR: permission denied for relation atest5 +SELECT * FROM atest1, atest5; -- fail +ERROR: permission denied for relation atest5 +SELECT atest1.* FROM atest1, atest5; -- ok + a | b +---+----- + 1 | two + 1 | two +(2 rows) + +SELECT atest1.*,atest5.one FROM atest1, atest5; -- ok + a | b | one +---+-----+----- + 1 | two | 1 + 1 | two | 1 +(2 rows) + +SELECT atest1.*,atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.two); -- fail +ERROR: permission denied for relation atest5 +SELECT atest1.*,atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.one); -- ok + a | b | one +---+-----+----- + 1 | two | 1 + 1 | two | 1 +(2 rows) + +SELECT one, two FROM atest5; -- fail +ERROR: permission denied for relation atest5 +SET SESSION AUTHORIZATION regressuser1; +GRANT SELECT (one,two) ON atest6 TO regressuser4; +SET SESSION AUTHORIZATION regressuser4; +SELECT one, two FROM atest5 NATURAL JOIN atest6; -- fail still +ERROR: permission denied for relation atest5 +SET SESSION AUTHORIZATION regressuser1; +GRANT SELECT (two) ON atest5 TO regressuser4; +SET SESSION AUTHORIZATION regressuser4; +SELECT one, two FROM atest5 NATURAL JOIN atest6; -- ok now + one | two +-----+----- +(0 rows) + +-- test column-level privileges for INSERT and UPDATE +INSERT INTO atest5 (two) VALUES (3); -- ok +INSERT INTO atest5 (three) VALUES (4); -- fail +ERROR: permission denied for relation atest5 +INSERT INTO atest5 VALUES (5,5,5); -- fail +ERROR: permission denied for relation atest5 +UPDATE atest5 SET three = 10; -- ok +UPDATE atest5 SET one = 8; -- fail +ERROR: permission denied for relation atest5 +UPDATE atest5 SET three = 5, one = 2; -- fail +ERROR: permission denied for relation atest5 +SET SESSION AUTHORIZATION regressuser1; +REVOKE ALL (one) ON atest5 FROM regressuser4; +GRANT SELECT (one,two,blue) ON atest6 TO regressuser4; +SET SESSION AUTHORIZATION regressuser4; +SELECT one FROM atest5; -- fail +ERROR: permission denied for relation atest5 +UPDATE atest5 SET one = 1; -- fail +ERROR: permission denied for relation atest5 +SELECT atest6 FROM atest6; -- ok + atest6 +-------- +(0 rows) + +-- test column-level privileges when involved with DELETE +SET SESSION AUTHORIZATION regressuser1; +ALTER TABLE atest6 ADD COLUMN three integer; +GRANT DELETE ON atest5 TO regressuser3; +GRANT SELECT (two) ON atest5 TO regressuser3; +REVOKE ALL (one) ON atest5 FROM regressuser3; +GRANT SELECT (one) ON atest5 TO regressuser4; +SET SESSION AUTHORIZATION regressuser4; +SELECT atest6 FROM atest6; -- fail +ERROR: permission denied for relation atest6 +SELECT one FROM atest5 NATURAL JOIN atest6; -- fail +ERROR: permission denied for relation atest5 +SET SESSION AUTHORIZATION regressuser1; +ALTER TABLE atest6 DROP COLUMN three; +SET SESSION AUTHORIZATION regressuser4; +SELECT atest6 FROM atest6; -- ok + atest6 +-------- +(0 rows) + +SELECT one FROM atest5 NATURAL JOIN atest6; -- ok + one +----- +(0 rows) + +SET SESSION AUTHORIZATION regressuser1; +ALTER TABLE atest6 DROP COLUMN two; +REVOKE SELECT (one,blue) ON atest6 FROM regressuser4; +SET SESSION AUTHORIZATION regressuser4; +SELECT * FROM atest6; -- fail +ERROR: permission denied for relation atest6 +SELECT 1 FROM atest6; -- fail +ERROR: permission denied for relation atest6 +SET SESSION AUTHORIZATION regressuser3; +DELETE FROM atest5 WHERE one = 1; -- fail +ERROR: permission denied for relation atest5 +DELETE FROM atest5 WHERE two = 2; -- ok -- privileges on functions, languages -- switch to superuser \c - @@ -642,6 +783,8 @@ DROP TABLE atest1; DROP TABLE atest2; DROP TABLE atest3; DROP TABLE atest4; +DROP TABLE atest5; +DROP TABLE atest6; DROP GROUP regressgroup1; DROP GROUP regressgroup2; REVOKE USAGE ON LANGUAGE sql FROM regressuser1; diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql index 63532f7e09..dda20db855 100644 --- a/src/test/regress/sql/privileges.sql +++ b/src/test/regress/sql/privileges.sql @@ -171,6 +171,93 @@ SELECT * FROM atestv4; -- ok (even though regressuser2 cannot access underlying SELECT * FROM atest2; -- ok SELECT * FROM atestv2; -- fail (even though regressuser2 can access underlying atest2) +-- Test column level permissions + +SET SESSION AUTHORIZATION regressuser1; +CREATE TABLE atest5 (one int, two int, three int); +CREATE TABLE atest6 (one int, two int, blue int); +GRANT SELECT (one), INSERT (two), UPDATE (three) ON atest5 TO regressuser4; +GRANT ALL (one) ON atest5 TO regressuser3; + +INSERT INTO atest5 VALUES (1,2,3); + +SET SESSION AUTHORIZATION regressuser4; +SELECT * FROM atest5; -- fail +SELECT one FROM atest5; -- ok +SELECT two FROM atest5; -- fail +SELECT atest5 FROM atest5; -- fail +SELECT 1 FROM atest5; -- ok +SELECT 1 FROM atest5 a JOIN atest5 b USING (one); -- ok +SELECT 1 FROM atest5 a JOIN atest5 b USING (two); -- fail +SELECT 1 FROM atest5 a NATURAL JOIN atest5 b; -- fail +SELECT (j.*) IS NULL FROM (atest5 a JOIN atest5 b USING (one)) j; -- fail +SELECT 1 FROM atest5 WHERE two = 2; -- fail +SELECT * FROM atest1, atest5; -- fail +SELECT atest1.* FROM atest1, atest5; -- ok +SELECT atest1.*,atest5.one FROM atest1, atest5; -- ok +SELECT atest1.*,atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.two); -- fail +SELECT atest1.*,atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.one); -- ok +SELECT one, two FROM atest5; -- fail + +SET SESSION AUTHORIZATION regressuser1; +GRANT SELECT (one,two) ON atest6 TO regressuser4; + +SET SESSION AUTHORIZATION regressuser4; +SELECT one, two FROM atest5 NATURAL JOIN atest6; -- fail still + +SET SESSION AUTHORIZATION regressuser1; +GRANT SELECT (two) ON atest5 TO regressuser4; + +SET SESSION AUTHORIZATION regressuser4; +SELECT one, two FROM atest5 NATURAL JOIN atest6; -- ok now + +-- test column-level privileges for INSERT and UPDATE +INSERT INTO atest5 (two) VALUES (3); -- ok +INSERT INTO atest5 (three) VALUES (4); -- fail +INSERT INTO atest5 VALUES (5,5,5); -- fail +UPDATE atest5 SET three = 10; -- ok +UPDATE atest5 SET one = 8; -- fail +UPDATE atest5 SET three = 5, one = 2; -- fail + +SET SESSION AUTHORIZATION regressuser1; +REVOKE ALL (one) ON atest5 FROM regressuser4; +GRANT SELECT (one,two,blue) ON atest6 TO regressuser4; + +SET SESSION AUTHORIZATION regressuser4; +SELECT one FROM atest5; -- fail +UPDATE atest5 SET one = 1; -- fail +SELECT atest6 FROM atest6; -- ok + +-- test column-level privileges when involved with DELETE +SET SESSION AUTHORIZATION regressuser1; +ALTER TABLE atest6 ADD COLUMN three integer; +GRANT DELETE ON atest5 TO regressuser3; +GRANT SELECT (two) ON atest5 TO regressuser3; +REVOKE ALL (one) ON atest5 FROM regressuser3; +GRANT SELECT (one) ON atest5 TO regressuser4; + +SET SESSION AUTHORIZATION regressuser4; +SELECT atest6 FROM atest6; -- fail +SELECT one FROM atest5 NATURAL JOIN atest6; -- fail + +SET SESSION AUTHORIZATION regressuser1; +ALTER TABLE atest6 DROP COLUMN three; + +SET SESSION AUTHORIZATION regressuser4; +SELECT atest6 FROM atest6; -- ok +SELECT one FROM atest5 NATURAL JOIN atest6; -- ok + +SET SESSION AUTHORIZATION regressuser1; +ALTER TABLE atest6 DROP COLUMN two; +REVOKE SELECT (one,blue) ON atest6 FROM regressuser4; + +SET SESSION AUTHORIZATION regressuser4; +SELECT * FROM atest6; -- fail +SELECT 1 FROM atest6; -- fail + +SET SESSION AUTHORIZATION regressuser3; +DELETE FROM atest5 WHERE one = 1; -- fail +DELETE FROM atest5 WHERE two = 2; -- ok -- privileges on functions, languages @@ -369,6 +456,8 @@ DROP TABLE atest1; DROP TABLE atest2; DROP TABLE atest3; DROP TABLE atest4; +DROP TABLE atest5; +DROP TABLE atest6; DROP GROUP regressgroup1; DROP GROUP regressgroup2;