diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 13d08eef3e..43aa515ae5 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -22,7 +22,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.166 2009/02/25 03:30:37 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.167 2009/03/05 17:30:29 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,6 +30,7 @@ #include "access/heapam.h" +#include "access/sysattr.h" #include "catalog/namespace.h" #include "catalog/pg_type.h" #include "miscadmin.h" @@ -95,6 +96,8 @@ static void make_inh_translation_list(Relation oldrelation, Relation newrelation, Index newvarno, List **translated_vars); +static Bitmapset *translate_col_privs(const Bitmapset *parent_privs, + List *translated_vars); static Node *adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context); static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid); @@ -1295,6 +1298,19 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) appinfo->parent_reloid = parentOID; appinfos = lappend(appinfos, appinfo); + /* + * Translate the column permissions bitmaps to the child's attnums + * (we have to build the translated_vars list before we can do this). + * But if this is the parent table, leave copyObject's result alone. + */ + if (childOID != parentOID) + { + childrte->selectedCols = translate_col_privs(rte->selectedCols, + appinfo->translated_vars); + childrte->modifiedCols = translate_col_privs(rte->modifiedCols, + appinfo->translated_vars); + } + /* * Build a RowMarkClause if parent is marked FOR UPDATE/SHARE. */ @@ -1437,6 +1453,59 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation, *translated_vars = vars; } +/* + * translate_col_privs + * Translate a bitmapset representing per-column privileges from the + * parent rel's attribute numbering to the child's. + * + * The only surprise here is that we don't translate a parent whole-row + * reference into a child whole-row reference. That would mean requiring + * permissions on all child columns, which is overly strict, since the + * query is really only going to reference the inherited columns. Instead + * we set the per-column bits for all inherited columns. + */ +static Bitmapset * +translate_col_privs(const Bitmapset *parent_privs, + List *translated_vars) +{ + Bitmapset *child_privs = NULL; + bool whole_row; + int attno; + ListCell *lc; + + /* System attributes have the same numbers in all tables */ + for (attno = FirstLowInvalidHeapAttributeNumber+1; attno < 0; attno++) + { + if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber, + parent_privs)) + child_privs = bms_add_member(child_privs, + attno - FirstLowInvalidHeapAttributeNumber); + } + + /* Check if parent has whole-row reference */ + whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber, + parent_privs); + + /* And now translate the regular user attributes, using the vars list */ + attno = InvalidAttrNumber; + foreach(lc, translated_vars) + { + Var *var = (Var *) lfirst(lc); + + attno++; + if (var == NULL) /* ignore dropped columns */ + continue; + Assert(IsA(var, Var)); + if (whole_row || + bms_is_member(attno - FirstLowInvalidHeapAttributeNumber, + parent_privs)) + child_privs = bms_add_member(child_privs, + var->varattno - FirstLowInvalidHeapAttributeNumber); + } + + return child_privs; +} + /* * adjust_appendrel_attrs * Copy the specified query or expression and translate Vars referring diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index 8129f5b915..a17ff59d0c 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -393,6 +393,48 @@ SET SESSION AUTHORIZATION regressuser3; DELETE FROM atest5 WHERE one = 1; -- fail ERROR: permission denied for relation atest5 DELETE FROM atest5 WHERE two = 2; -- ok +-- check inheritance cases +SET SESSION AUTHORIZATION regressuser1; +CREATE TABLE atestp1 (f1 int, f2 int) WITH OIDS; +CREATE TABLE atestp2 (fx int, fy int) WITH OIDS; +CREATE TABLE atestc (fz int) INHERITS (atestp1, atestp2); +GRANT SELECT(fx,fy,oid) ON atestp2 TO regressuser2; +GRANT SELECT(fx) ON atestc TO regressuser2; +SET SESSION AUTHORIZATION regressuser2; +SELECT fx FROM atestp2; -- ok + fx +---- +(0 rows) + +SELECT fy FROM atestp2; -- fail, no privilege on atestc.fy +ERROR: permission denied for relation atestc +SELECT atestp2 FROM atestp2; -- fail, no privilege on atestc.fy +ERROR: permission denied for relation atestc +SELECT oid FROM atestp2; -- fail, no privilege on atestc.oid +ERROR: permission denied for relation atestc +SET SESSION AUTHORIZATION regressuser1; +GRANT SELECT(fy,oid) ON atestc TO regressuser2; +SET SESSION AUTHORIZATION regressuser2; +SELECT fx FROM atestp2; -- still ok + fx +---- +(0 rows) + +SELECT fy FROM atestp2; -- ok + fy +---- +(0 rows) + +SELECT atestp2 FROM atestp2; -- ok + atestp2 +--------- +(0 rows) + +SELECT oid FROM atestp2; -- ok + oid +----- +(0 rows) + -- privileges on functions, languages -- switch to superuser \c - @@ -791,6 +833,9 @@ DROP TABLE atest3; DROP TABLE atest4; DROP TABLE atest5; DROP TABLE atest6; +DROP TABLE atestc; +DROP TABLE atestp1; +DROP TABLE atestp2; 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 2316d11616..5aa1012f3f 100644 --- a/src/test/regress/sql/privileges.sql +++ b/src/test/regress/sql/privileges.sql @@ -267,6 +267,29 @@ SET SESSION AUTHORIZATION regressuser3; DELETE FROM atest5 WHERE one = 1; -- fail DELETE FROM atest5 WHERE two = 2; -- ok +-- check inheritance cases +SET SESSION AUTHORIZATION regressuser1; +CREATE TABLE atestp1 (f1 int, f2 int) WITH OIDS; +CREATE TABLE atestp2 (fx int, fy int) WITH OIDS; +CREATE TABLE atestc (fz int) INHERITS (atestp1, atestp2); +GRANT SELECT(fx,fy,oid) ON atestp2 TO regressuser2; +GRANT SELECT(fx) ON atestc TO regressuser2; + +SET SESSION AUTHORIZATION regressuser2; +SELECT fx FROM atestp2; -- ok +SELECT fy FROM atestp2; -- fail, no privilege on atestc.fy +SELECT atestp2 FROM atestp2; -- fail, no privilege on atestc.fy +SELECT oid FROM atestp2; -- fail, no privilege on atestc.oid + +SET SESSION AUTHORIZATION regressuser1; +GRANT SELECT(fy,oid) ON atestc TO regressuser2; + +SET SESSION AUTHORIZATION regressuser2; +SELECT fx FROM atestp2; -- still ok +SELECT fy FROM atestp2; -- ok +SELECT atestp2 FROM atestp2; -- ok +SELECT oid FROM atestp2; -- ok + -- privileges on functions, languages -- switch to superuser @@ -466,6 +489,9 @@ DROP TABLE atest3; DROP TABLE atest4; DROP TABLE atest5; DROP TABLE atest6; +DROP TABLE atestc; +DROP TABLE atestp1; +DROP TABLE atestp2; DROP GROUP regressgroup1; DROP GROUP regressgroup2;