mirror of
https://github.com/postgres/postgres.git
synced 2025-08-22 21:53:06 +03:00
Support column-level privileges, as required by SQL standard.
Stephen Frost, with help from KaiGai Kohei and others
This commit is contained in:
@@ -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)
|
||||
|
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.655 2009/01/16 13:27:23 heikki Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.656 2009/01/22 20:16:05 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -94,6 +94,13 @@ extern List *parsetree; /* final parse result is delivered here */
|
||||
|
||||
static bool QueryIsRule = FALSE;
|
||||
|
||||
/* Private struct for the result of privilege_target production */
|
||||
typedef struct PrivTarget
|
||||
{
|
||||
GrantObjectType objtype;
|
||||
List *objs;
|
||||
} PrivTarget;
|
||||
|
||||
/*
|
||||
* If you need access to certain yacc-generated variables and find that
|
||||
* they're static by default, uncomment the next line. (this is not a
|
||||
@@ -167,7 +174,8 @@ static TypeName *TableFuncTypeName(List *columns);
|
||||
WithClause *with;
|
||||
A_Indices *aind;
|
||||
ResTarget *target;
|
||||
PrivTarget *privtarget;
|
||||
struct PrivTarget *privtarget;
|
||||
AccessPriv *accesspriv;
|
||||
|
||||
InsertStmt *istmt;
|
||||
VariableSetStmt *vsetstmt;
|
||||
@@ -254,7 +262,7 @@ static TypeName *TableFuncTypeName(List *columns);
|
||||
%type <str> iso_level opt_encoding
|
||||
%type <node> grantee
|
||||
%type <list> grantee_list
|
||||
%type <str> privilege
|
||||
%type <accesspriv> privilege
|
||||
%type <list> privileges privilege_list
|
||||
%type <privtarget> privilege_target
|
||||
%type <funwithargs> function_with_argtypes
|
||||
@@ -4210,12 +4218,11 @@ RevokeStmt:
|
||||
|
||||
|
||||
/*
|
||||
* A privilege list is represented as a list of strings; the validity of
|
||||
* the privilege names gets checked at execution. This is a bit annoying
|
||||
* but we have little choice because of the syntactic conflict with lists
|
||||
* of role names in GRANT/REVOKE. What's more, we have to call out in
|
||||
* the "privilege" production any reserved keywords that need to be usable
|
||||
* as privilege names.
|
||||
* Privilege names are represented as strings; the validity of the privilege
|
||||
* names gets checked at execution. This is a bit annoying but we have little
|
||||
* choice because of the syntactic conflict with lists of role names in
|
||||
* GRANT/REVOKE. What's more, we have to call out in the "privilege"
|
||||
* production any reserved keywords that need to be usable as privilege names.
|
||||
*/
|
||||
|
||||
/* either ALL [PRIVILEGES] or a list of individual privileges */
|
||||
@@ -4225,18 +4232,54 @@ privileges: privilege_list
|
||||
{ $$ = NIL; }
|
||||
| ALL PRIVILEGES
|
||||
{ $$ = NIL; }
|
||||
| ALL '(' columnList ')'
|
||||
{
|
||||
AccessPriv *n = makeNode(AccessPriv);
|
||||
n->priv_name = NULL;
|
||||
n->cols = $3;
|
||||
$$ = list_make1(n);
|
||||
}
|
||||
| ALL PRIVILEGES '(' columnList ')'
|
||||
{
|
||||
AccessPriv *n = makeNode(AccessPriv);
|
||||
n->priv_name = NULL;
|
||||
n->cols = $4;
|
||||
$$ = list_make1(n);
|
||||
}
|
||||
;
|
||||
|
||||
privilege_list: privilege
|
||||
{ $$ = list_make1(makeString($1)); }
|
||||
| privilege_list ',' privilege
|
||||
{ $$ = lappend($1, makeString($3)); }
|
||||
privilege_list: privilege { $$ = list_make1($1); }
|
||||
| privilege_list ',' privilege { $$ = lappend($1, $3); }
|
||||
;
|
||||
|
||||
privilege: SELECT { $$ = pstrdup($1); }
|
||||
| REFERENCES { $$ = pstrdup($1); }
|
||||
| CREATE { $$ = pstrdup($1); }
|
||||
| ColId { $$ = $1; }
|
||||
privilege: SELECT opt_column_list
|
||||
{
|
||||
AccessPriv *n = makeNode(AccessPriv);
|
||||
n->priv_name = pstrdup($1);
|
||||
n->cols = $2;
|
||||
$$ = n;
|
||||
}
|
||||
| REFERENCES opt_column_list
|
||||
{
|
||||
AccessPriv *n = makeNode(AccessPriv);
|
||||
n->priv_name = pstrdup($1);
|
||||
n->cols = $2;
|
||||
$$ = n;
|
||||
}
|
||||
| CREATE opt_column_list
|
||||
{
|
||||
AccessPriv *n = makeNode(AccessPriv);
|
||||
n->priv_name = pstrdup($1);
|
||||
n->cols = $2;
|
||||
$$ = n;
|
||||
}
|
||||
| ColId opt_column_list
|
||||
{
|
||||
AccessPriv *n = makeNode(AccessPriv);
|
||||
n->priv_name = $1;
|
||||
n->cols = $2;
|
||||
$$ = n;
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
@@ -4246,70 +4289,70 @@ privilege: SELECT { $$ = pstrdup($1); }
|
||||
privilege_target:
|
||||
qualified_name_list
|
||||
{
|
||||
PrivTarget *n = makeNode(PrivTarget);
|
||||
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
||||
n->objtype = ACL_OBJECT_RELATION;
|
||||
n->objs = $1;
|
||||
$$ = n;
|
||||
}
|
||||
| TABLE qualified_name_list
|
||||
{
|
||||
PrivTarget *n = makeNode(PrivTarget);
|
||||
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
||||
n->objtype = ACL_OBJECT_RELATION;
|
||||
n->objs = $2;
|
||||
$$ = n;
|
||||
}
|
||||
| SEQUENCE qualified_name_list
|
||||
{
|
||||
PrivTarget *n = makeNode(PrivTarget);
|
||||
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
||||
n->objtype = ACL_OBJECT_SEQUENCE;
|
||||
n->objs = $2;
|
||||
$$ = n;
|
||||
}
|
||||
| FOREIGN DATA_P WRAPPER name_list
|
||||
{
|
||||
PrivTarget *n = makeNode(PrivTarget);
|
||||
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
||||
n->objtype = ACL_OBJECT_FDW;
|
||||
n->objs = $4;
|
||||
$$ = n;
|
||||
}
|
||||
| FOREIGN SERVER name_list
|
||||
{
|
||||
PrivTarget *n = makeNode(PrivTarget);
|
||||
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
||||
n->objtype = ACL_OBJECT_FOREIGN_SERVER;
|
||||
n->objs = $3;
|
||||
$$ = n;
|
||||
}
|
||||
| FUNCTION function_with_argtypes_list
|
||||
{
|
||||
PrivTarget *n = makeNode(PrivTarget);
|
||||
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
||||
n->objtype = ACL_OBJECT_FUNCTION;
|
||||
n->objs = $2;
|
||||
$$ = n;
|
||||
}
|
||||
| DATABASE name_list
|
||||
{
|
||||
PrivTarget *n = makeNode(PrivTarget);
|
||||
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
||||
n->objtype = ACL_OBJECT_DATABASE;
|
||||
n->objs = $2;
|
||||
$$ = n;
|
||||
}
|
||||
| LANGUAGE name_list
|
||||
{
|
||||
PrivTarget *n = makeNode(PrivTarget);
|
||||
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
||||
n->objtype = ACL_OBJECT_LANGUAGE;
|
||||
n->objs = $2;
|
||||
$$ = n;
|
||||
}
|
||||
| SCHEMA name_list
|
||||
{
|
||||
PrivTarget *n = makeNode(PrivTarget);
|
||||
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
||||
n->objtype = ACL_OBJECT_NAMESPACE;
|
||||
n->objs = $2;
|
||||
$$ = n;
|
||||
}
|
||||
| TABLESPACE name_list
|
||||
{
|
||||
PrivTarget *n = makeNode(PrivTarget);
|
||||
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
||||
n->objtype = ACL_OBJECT_TABLESPACE;
|
||||
n->objs = $2;
|
||||
$$ = n;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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? */
|
||||
|
@@ -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));
|
||||
|
Reference in New Issue
Block a user