src/backend/nodes/copyfuncs.c | 8 ++ src/backend/nodes/equalfuncs.c | 8 ++ src/backend/nodes/outfuncs.c | 8 ++ src/backend/nodes/readfuncs.c | 8 ++ src/backend/optimizer/path/costsize.c | 17 ++++ src/backend/optimizer/plan/createplan.c | 13 +++- src/backend/optimizer/plan/planner.c | 114 ++++++++++++++++++++++++++++ src/backend/optimizer/util/clauses.c | 24 ++++-- src/include/nodes/primnodes.h | 6 ++ src/include/nodes/relation.h | 1 + src/test/regress/expected/select_views.out | 70 ++++++++++++++---- src/test/regress/sql/select_views.sql | 35 +++++++++ 12 files changed, 289 insertions(+), 23 deletions(-) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index c9133dd..1b1cf93 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1188,6 +1188,7 @@ _copyFuncExpr(FuncExpr *from) COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } @@ -1224,6 +1225,7 @@ _copyOpExpr(OpExpr *from) COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } @@ -1244,6 +1246,7 @@ _copyDistinctExpr(DistinctExpr *from) COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } @@ -1264,6 +1267,7 @@ _copyNullIfExpr(NullIfExpr *from) COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } @@ -1282,6 +1286,7 @@ _copyScalarArrayOpExpr(ScalarArrayOpExpr *from) COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } @@ -1422,6 +1427,7 @@ _copyCoerceViaIO(CoerceViaIO *from) COPY_SCALAR_FIELD(resultcollid); COPY_SCALAR_FIELD(coerceformat); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } @@ -1442,6 +1448,7 @@ _copyArrayCoerceExpr(ArrayCoerceExpr *from) COPY_SCALAR_FIELD(isExplicit); COPY_SCALAR_FIELD(coerceformat); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } @@ -1574,6 +1581,7 @@ _copyRowCompareExpr(RowCompareExpr *from) COPY_NODE_FIELD(inputcollids); COPY_NODE_FIELD(largs); COPY_NODE_FIELD(rargs); + COPY_SCALAR_FIELD(depth); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 3a0267c..71c4694 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -248,6 +248,7 @@ _equalFuncExpr(FuncExpr *a, FuncExpr *b) COMPARE_SCALAR_FIELD(inputcollid); COMPARE_NODE_FIELD(args); COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(depth); return true; } @@ -285,6 +286,7 @@ _equalOpExpr(OpExpr *a, OpExpr *b) COMPARE_SCALAR_FIELD(inputcollid); COMPARE_NODE_FIELD(args); COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(depth); return true; } @@ -311,6 +313,7 @@ _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b) COMPARE_SCALAR_FIELD(inputcollid); COMPARE_NODE_FIELD(args); COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(depth); return true; } @@ -337,6 +340,7 @@ _equalNullIfExpr(NullIfExpr *a, NullIfExpr *b) COMPARE_SCALAR_FIELD(inputcollid); COMPARE_NODE_FIELD(args); COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(depth); return true; } @@ -361,6 +365,7 @@ _equalScalarArrayOpExpr(ScalarArrayOpExpr *a, ScalarArrayOpExpr *b) COMPARE_SCALAR_FIELD(inputcollid); COMPARE_NODE_FIELD(args); COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(depth); return true; } @@ -479,6 +484,7 @@ _equalCoerceViaIO(CoerceViaIO *a, CoerceViaIO *b) return false; COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(depth); return true; } @@ -503,6 +509,7 @@ _equalArrayCoerceExpr(ArrayCoerceExpr *a, ArrayCoerceExpr *b) return false; COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(depth); return true; } @@ -613,6 +620,7 @@ _equalRowCompareExpr(RowCompareExpr *a, RowCompareExpr *b) COMPARE_NODE_FIELD(inputcollids); COMPARE_NODE_FIELD(largs); COMPARE_NODE_FIELD(rargs); + COMPARE_SCALAR_FIELD(depth); return true; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 681f5f8..f9a8aef 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1004,6 +1004,7 @@ _outFuncExpr(StringInfo str, FuncExpr *node) WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void @@ -1030,6 +1031,7 @@ _outOpExpr(StringInfo str, OpExpr *node) WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void @@ -1045,6 +1047,7 @@ _outDistinctExpr(StringInfo str, DistinctExpr *node) WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void @@ -1060,6 +1063,7 @@ _outNullIfExpr(StringInfo str, NullIfExpr *node) WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void @@ -1073,6 +1077,7 @@ _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node) WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void @@ -1190,6 +1195,7 @@ _outCoerceViaIO(StringInfo str, CoerceViaIO *node) WRITE_OID_FIELD(resultcollid); WRITE_ENUM_FIELD(coerceformat, CoercionForm); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void @@ -1205,6 +1211,7 @@ _outArrayCoerceExpr(StringInfo str, ArrayCoerceExpr *node) WRITE_BOOL_FIELD(isExplicit); WRITE_ENUM_FIELD(coerceformat, CoercionForm); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void @@ -1297,6 +1304,7 @@ _outRowCompareExpr(StringInfo str, RowCompareExpr *node) WRITE_NODE_FIELD(inputcollids); WRITE_NODE_FIELD(largs); WRITE_NODE_FIELD(rargs); + WRITE_INT_FIELD(depth); } static void diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 29a0e8f..c4f934d 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -541,6 +541,7 @@ _readFuncExpr(void) READ_OID_FIELD(inputcollid); READ_NODE_FIELD(args); READ_LOCATION_FIELD(location); + READ_INT_FIELD(depth); READ_DONE(); } @@ -588,6 +589,7 @@ _readOpExpr(void) READ_OID_FIELD(inputcollid); READ_NODE_FIELD(args); READ_LOCATION_FIELD(location); + READ_INT_FIELD(depth); READ_DONE(); } @@ -619,6 +621,7 @@ _readDistinctExpr(void) READ_OID_FIELD(inputcollid); READ_NODE_FIELD(args); READ_LOCATION_FIELD(location); + READ_INT_FIELD(depth); READ_DONE(); } @@ -650,6 +653,7 @@ _readNullIfExpr(void) READ_OID_FIELD(inputcollid); READ_NODE_FIELD(args); READ_LOCATION_FIELD(location); + READ_INT_FIELD(depth); READ_DONE(); } @@ -679,6 +683,7 @@ _readScalarArrayOpExpr(void) READ_OID_FIELD(inputcollid); READ_NODE_FIELD(args); READ_LOCATION_FIELD(location); + READ_INT_FIELD(depth); READ_DONE(); } @@ -794,6 +799,7 @@ _readCoerceViaIO(void) READ_OID_FIELD(resultcollid); READ_ENUM_FIELD(coerceformat, CoercionForm); READ_LOCATION_FIELD(location); + READ_INT_FIELD(depth); READ_DONE(); } @@ -814,6 +820,7 @@ _readArrayCoerceExpr(void) READ_BOOL_FIELD(isExplicit); READ_ENUM_FIELD(coerceformat, CoercionForm); READ_LOCATION_FIELD(location); + READ_INT_FIELD(depth); READ_DONE(); } @@ -946,6 +953,7 @@ _readRowCompareExpr(void) READ_NODE_FIELD(inputcollids); READ_NODE_FIELD(largs); READ_NODE_FIELD(rargs); + READ_INT_FIELD(depth); READ_DONE(); } diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index bb38768..0302ad2 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -2593,6 +2593,7 @@ cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root) context.root = root; context.total.startup = 0; context.total.per_tuple = 0; + context.total.depth = 0; /* We don't charge any cost for the implicit ANDing at top level ... */ @@ -2618,6 +2619,7 @@ cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root) context.root = root; context.total.startup = 0; context.total.per_tuple = 0; + context.total.depth = 0; cost_qual_eval_walker(qual, &context); @@ -2647,6 +2649,7 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) locContext.root = context->root; locContext.total.startup = 0; locContext.total.per_tuple = 0; + locContext.total.depth = 0; /* * For an OR clause, recurse into the marked-up tree so that we @@ -2671,6 +2674,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) } context->total.startup += rinfo->eval_cost.startup; context->total.per_tuple += rinfo->eval_cost.per_tuple; + if (rinfo->eval_cost.depth > context->total.depth) + context->total.depth = rinfo->eval_cost.depth; /* do NOT recurse into children */ return false; } @@ -2694,6 +2699,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) { context->total.per_tuple += get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost; + if (((FuncExpr *)node)->depth > context->total.depth) + context->total.depth = ((FuncExpr *)node)->depth; } else if (IsA(node, OpExpr) || IsA(node, DistinctExpr) || @@ -2703,6 +2710,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) set_opfuncid((OpExpr *) node); context->total.per_tuple += get_func_cost(((OpExpr *) node)->opfuncid) * cpu_operator_cost; + if (((OpExpr *)node)->depth > context->total.depth) + context->total.depth = ((OpExpr *)node)->depth; } else if (IsA(node, ScalarArrayOpExpr)) { @@ -2716,6 +2725,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) set_sa_opfuncid(saop); context->total.per_tuple += get_func_cost(saop->opfuncid) * cpu_operator_cost * estimate_array_length(arraynode) * 0.5; + if (saop->depth > context->total.depth) + context->total.depth = saop->depth; } else if (IsA(node, Aggref) || IsA(node, WindowFunc)) @@ -2746,6 +2757,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) getTypeOutputInfo(exprType((Node *) iocoerce->arg), &iofunc, &typisvarlena); context->total.per_tuple += get_func_cost(iofunc) * cpu_operator_cost; + if (iocoerce->depth > context->total.depth) + context->total.depth = iocoerce->depth; } else if (IsA(node, ArrayCoerceExpr)) { @@ -2755,6 +2768,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) if (OidIsValid(acoerce->elemfuncid)) context->total.per_tuple += get_func_cost(acoerce->elemfuncid) * cpu_operator_cost * estimate_array_length(arraynode); + if (acoerce->depth > context->total.depth) + context->total.depth = acoerce->depth; } else if (IsA(node, RowCompareExpr)) { @@ -2769,6 +2784,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) context->total.per_tuple += get_func_cost(get_opcode(opid)) * cpu_operator_cost; } + if (rcexpr->depth > context->total.depth) + context->total.depth = rcexpr->depth; } else if (IsA(node, CurrentOfExpr)) { diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index e4ccf5c..3414b4c 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -2704,6 +2704,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses) { Node *clause; Cost cost; + int depth; } QualItem; int nitems = list_length(clauses); QualItem *items; @@ -2729,6 +2730,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses) cost_qual_eval_node(&qcost, clause, root); items[i].clause = clause; items[i].cost = qcost.per_tuple; + items[i].depth = qcost.depth; i++; } @@ -2745,7 +2747,16 @@ order_qual_clauses(PlannerInfo *root, List *clauses) /* insert newitem into the already-sorted subarray */ for (j = i; j > 0; j--) { - if (newitem.cost >= items[j - 1].cost) + /* + * Higher priority shall be given to the items originated from + * deeper nest level. If same level, it shall be given to the + * items with smaller estimated cost. + * Such kind of consideration is needed to prevent leaky-view + * problem. + */ + if (newitem.depth < items[j - 1].depth || + (newitem.depth == items[j - 1].depth && + newitem.cost >= items[j - 1].cost)) break; items[j] = items[j - 1]; } diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 9aafc8a..d58d789 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -33,6 +33,7 @@ #include "optimizer/subselect.h" #include "optimizer/tlist.h" #include "optimizer/var.h" +#include "nodes/nodeFuncs.h" #ifdef OPTIMIZER_DEBUG #include "nodes/print.h" #endif @@ -103,6 +104,7 @@ static void get_column_info_for_window(PlannerInfo *root, WindowClause *wc, int *ordNumCols, AttrNumber **ordColIdx, Oid **ordOperators); +static void mark_qualifiers_depth(Query *query); /***************************************************************************** @@ -148,6 +150,12 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) cursorOptions |= ((DeclareCursorStmt *) parse->utilityStmt)->options; /* + * Mark qualifiers its original depth to prevent reversal of orders + * on evaluation of WHERE clause during relation scanns. + */ + mark_qualifiers_depth(parse); + + /* * Set up global state for this planner invocation. This data is needed * across all levels of sub-Query that might exist in the given command, * so we keep it in a separate struct that's linked to by each per-Query @@ -2993,6 +3001,112 @@ get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist, } } +/* + * mark_qualifiers_depth + * + * It marks depth field of the each expression nodes that eventually + * invokes functions, to track the original nest-level. On the evaluation + * of qualifiers within WHERE or JOIN ... ON clauses during relation scans, + * these items shall be reordered according to the nest-level and estimated + * cost. + * The optimizer may pull-up simple sub-queries or join clause, and + * qualifiers to filter out tuples shall be mixed with ones in upper- + * level. Thus, we need to track the original nest-level of qualifiers + * to prevent reverse of order in evaluation, because some of qualifiers + * can have side-effects that allows to leak supplied argument to outside. + * It can be abused to break row-level security using a user defined function + * with very small estimated cost, so nest level of qualifiers originated + * from is used as a criteria, rather than estimated cost, to decide order + * to evaluate qualifiers. + */ +static bool +mark_qualifiers_depth_walker(Node *node, void *context) +{ + int depth = *((int *)(context)); + + if (node == NULL) + return false; + if (IsA(node, FuncExpr)) + { + FuncExpr *exp = (FuncExpr *)node; + + exp->depth = depth; + + return false; + } + else if (IsA(node, OpExpr)) + { + OpExpr *exp = (OpExpr *)node; + + exp->depth = depth; + + return false; + } + else if (IsA(node, DistinctExpr)) + { + DistinctExpr *exp = (DistinctExpr *)node; + + exp->depth = depth; + + return false; + } + else if (IsA(node, ScalarArrayOpExpr)) + { + ScalarArrayOpExpr *exp = (ScalarArrayOpExpr *)node; + + exp->depth = depth; + + return false; + } + else if (IsA(node, CoerceViaIO)) + { + CoerceViaIO *exp = (CoerceViaIO *)node; + + exp->depth = depth; + + return false; + } + else if (IsA(node, ArrayCoerceExpr)) + { + ArrayCoerceExpr *exp = (ArrayCoerceExpr *)node; + + exp->depth = depth; + + return false; + } + else if (IsA(node, NullIfExpr)) + { + NullIfExpr *exp = (NullIfExpr *)node; + + exp->depth = depth; + + return false; + } + else if (IsA(node, RowCompareExpr)) + { + RowCompareExpr *exp = (RowCompareExpr *)node; + + exp->depth = depth; + + return false; + } + else if (IsA(node, Query)) + { + depth += 2; + + query_tree_walker((Query *)node, mark_qualifiers_depth_walker, &depth, 0); + return false; + } + return expression_tree_walker(node, mark_qualifiers_depth_walker, context); +} + +static void +mark_qualifiers_depth(Query *query) +{ + int depth = 0; + + query_tree_walker(query, mark_qualifiers_depth_walker, &depth, 0); +} /* * expression_planner diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index be0935d..95c07aa 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -111,6 +111,7 @@ static Expr *simplify_function(Expr *oldexpr, Oid funcid, Oid input_collid, List **args, bool has_named_args, bool allow_inline, + int depth, eval_const_expressions_context *context); static List *reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple, @@ -123,7 +124,7 @@ static void recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple); static Expr *evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, Oid result_collid, Oid input_collid, List *args, - HeapTuple func_tuple, + HeapTuple func_tuple, int depth, eval_const_expressions_context *context); static Expr *inline_function(Oid funcid, Oid result_type, Oid result_collid, Oid input_collid, List *args, @@ -2229,7 +2230,7 @@ eval_const_expressions_mutator(Node *node, expr->funccollid, expr->inputcollid, &args, - has_named_args, true, context); + has_named_args, true, expr->depth, context); if (simple) /* successfully simplified it */ return (Node *) simple; @@ -2248,6 +2249,7 @@ eval_const_expressions_mutator(Node *node, newexpr->inputcollid = expr->inputcollid; newexpr->args = args; newexpr->location = expr->location; + newexpr->depth = expr->depth; return (Node *) newexpr; } if (IsA(node, OpExpr)) @@ -2282,7 +2284,7 @@ eval_const_expressions_mutator(Node *node, expr->opcollid, expr->inputcollid, &args, - false, true, context); + false, true, expr->depth, context); if (simple) /* successfully simplified it */ return (Node *) simple; @@ -2313,6 +2315,7 @@ eval_const_expressions_mutator(Node *node, newexpr->inputcollid = expr->inputcollid; newexpr->args = args; newexpr->location = expr->location; + newexpr->depth = expr->depth; return (Node *) newexpr; } if (IsA(node, DistinctExpr)) @@ -2380,7 +2383,7 @@ eval_const_expressions_mutator(Node *node, expr->opcollid, expr->inputcollid, &args, - false, false, context); + false, false, expr->depth, context); if (simple) /* successfully simplified it */ { /* @@ -2410,6 +2413,7 @@ eval_const_expressions_mutator(Node *node, newexpr->inputcollid = expr->inputcollid; newexpr->args = args; newexpr->location = expr->location; + newexpr->depth = expr->depth; return (Node *) newexpr; } if (IsA(node, BoolExpr)) @@ -2570,7 +2574,7 @@ eval_const_expressions_mutator(Node *node, InvalidOid, InvalidOid, &args, - false, true, context); + false, true, expr->depth, context); if (simple) /* successfully simplified output fn */ { /* @@ -2591,7 +2595,7 @@ eval_const_expressions_mutator(Node *node, expr->resultcollid, InvalidOid, &args, - false, true, context); + false, true, expr->depth, context); if (simple) /* successfully simplified input fn */ return (Node *) simple; } @@ -2607,6 +2611,7 @@ eval_const_expressions_mutator(Node *node, newexpr->resultcollid = expr->resultcollid; newexpr->coerceformat = expr->coerceformat; newexpr->location = expr->location; + newexpr->depth = expr->depth; return (Node *) newexpr; } if (IsA(node, ArrayCoerceExpr)) @@ -2631,6 +2636,7 @@ eval_const_expressions_mutator(Node *node, newexpr->isExplicit = expr->isExplicit; newexpr->coerceformat = expr->coerceformat; newexpr->location = expr->location; + newexpr->depth = expr->depth; /* * If constant argument and it's a binary-coercible or immutable @@ -3447,6 +3453,7 @@ simplify_function(Expr *oldexpr, Oid funcid, Oid input_collid, List **args, bool has_named_args, bool allow_inline, + int depth, eval_const_expressions_context *context) { HeapTuple func_tuple; @@ -3477,7 +3484,7 @@ simplify_function(Expr *oldexpr, Oid funcid, newexpr = evaluate_function(funcid, result_type, result_typmod, result_collid, input_collid, *args, - func_tuple, context); + func_tuple, depth, context); /* * Some functions calls can be simplified at plan time based on properties @@ -3764,7 +3771,7 @@ recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple) static Expr * evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, Oid result_collid, Oid input_collid, List *args, - HeapTuple func_tuple, + HeapTuple func_tuple, int depth, eval_const_expressions_context *context) { Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); @@ -3850,6 +3857,7 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, newexpr->inputcollid = input_collid; newexpr->args = args; newexpr->location = -1; + newexpr->depth = depth; return evaluate_expr((Expr *) newexpr, result_type, result_typmod, result_collid); diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index f1e20ef..b626386 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -335,6 +335,7 @@ typedef struct FuncExpr Oid inputcollid; /* OID of collation that function should use */ List *args; /* arguments to the function */ int location; /* token location, or -1 if unknown */ + int depth; /* depth of clause in the original query */ } FuncExpr; /* @@ -380,6 +381,7 @@ typedef struct OpExpr Oid inputcollid; /* OID of collation that operator should use */ List *args; /* arguments to the operator (1 or 2) */ int location; /* token location, or -1 if unknown */ + int depth; /* depth of clause in the original query */ } OpExpr; /* @@ -421,6 +423,7 @@ typedef struct ScalarArrayOpExpr Oid inputcollid; /* OID of collation that operator should use */ List *args; /* the scalar and array operands */ int location; /* token location, or -1 if unknown */ + int depth; /* depth of clause in the original query */ } ScalarArrayOpExpr; /* @@ -685,6 +688,7 @@ typedef struct CoerceViaIO Oid resultcollid; /* OID of collation, or InvalidOid if none */ CoercionForm coerceformat; /* how to display this node */ int location; /* token location, or -1 if unknown */ + int depth; /* depth of clause in the original query */ } CoerceViaIO; /* ---------------- @@ -710,6 +714,7 @@ typedef struct ArrayCoerceExpr bool isExplicit; /* conversion semantics flag to pass to func */ CoercionForm coerceformat; /* how to display this node */ int location; /* token location, or -1 if unknown */ + int depth; /* depth of clause in the original query */ } ArrayCoerceExpr; /* ---------------- @@ -901,6 +906,7 @@ typedef struct RowCompareExpr List *inputcollids; /* OID list of collations for comparisons */ List *largs; /* the left-hand input arguments */ List *rargs; /* the right-hand input arguments */ + int depth; /* depth of clause in the original query */ } RowCompareExpr; /* diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index f659269..250191b 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -44,6 +44,7 @@ typedef struct QualCost { Cost startup; /* one-time cost */ Cost per_tuple; /* per-evaluation cost */ + int depth; /* depth of qual in the original query */ } QualCost; /* diff --git a/src/test/regress/expected/select_views.out b/src/test/regress/expected/select_views.out index 6cd317c..466689f 100644 --- a/src/test/regress/expected/select_views.out +++ b/src/test/regress/expected/select_views.out @@ -467,6 +467,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2; I- 580 | 21 I- 580 | 22 I- 580 | 22 + I- 580/I-680 Ramp | 2 + I- 580/I-680 Ramp | 2 + I- 580/I-680 Ramp | 2 + I- 580/I-680 Ramp | 2 + I- 580/I-680 Ramp | 2 + I- 580/I-680 Ramp | 2 + I- 580/I-680 Ramp | 4 + I- 580/I-680 Ramp | 4 + I- 580/I-680 Ramp | 4 + I- 580/I-680 Ramp | 4 + I- 580/I-680 Ramp | 5 + I- 580/I-680 Ramp | 6 + I- 580/I-680 Ramp | 6 + I- 580/I-680 Ramp | 6 I- 580 Ramp | 2 I- 580 Ramp | 2 I- 580 Ramp | 2 @@ -717,20 +731,6 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2; I- 580 Ramp | 8 I- 580 Ramp | 8 I- 580 Ramp | 8 - I- 580/I-680 Ramp | 2 - I- 580/I-680 Ramp | 2 - I- 580/I-680 Ramp | 2 - I- 580/I-680 Ramp | 2 - I- 580/I-680 Ramp | 2 - I- 580/I-680 Ramp | 2 - I- 580/I-680 Ramp | 4 - I- 580/I-680 Ramp | 4 - I- 580/I-680 Ramp | 4 - I- 580/I-680 Ramp | 4 - I- 580/I-680 Ramp | 5 - I- 580/I-680 Ramp | 6 - I- 580/I-680 Ramp | 6 - I- 580/I-680 Ramp | 6 I- 680 | 2 I- 680 | 2 I- 680 | 2 @@ -1247,3 +1247,45 @@ SELECT * FROM toyemp WHERE name = 'sharon'; sharon | 25 | (15,12) | 12000 (1 row) +-- +-- Test for leaky-view problem +-- +CREATE USER alice; +CREATE FUNCTION f_leak(text, text) + RETURNS bool LANGUAGE 'plpgsql' + COST 0.00000001 + AS 'begin raise notice ''% => %'', $1, $2; return true; end'; +CREATE TABLE credit_cards ( + name text, + number text, + expired text +); +INSERT INTO credit_cards VALUES ('alice', '1111-2222-3333-4444', 'Aug-2012'), + ('bob', '5555-6666-7777-8888', 'Nov-2016'), + ('eve', '9801-2345-6789-0123', 'Jan-2018'); +CREATE VIEW your_credit AS + SELECT * FROM credit_cards WHERE name = getpgusername(); +GRANT SELECT ON your_credit TO public; +-- run leaky view +SET SESSION AUTHORIZATION alice; +SELECT * FROM your_credit WHERE f_leak(number,expired); +NOTICE: 1111-2222-3333-4444 => Aug-2012 + name | number | expired +-------+---------------------+---------- + alice | 1111-2222-3333-4444 | Aug-2012 +(1 row) + +EXPLAIN (COSTS OFF) SELECT * FROM your_credit WHERE f_leak(number,expired); + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on credit_cards + Filter: ((name = (getpgusername())::text) AND f_leak(number, expired)) +(2 rows) + +\c - +-- cleanups +DROP ROLE IF EXISTS alice; +DROP FUNCTION IF EXISTS f_leak(text); +NOTICE: function f_leak(text) does not exist, skipping +DROP TABLE IF EXISTS credit_cards CASCADE; +NOTICE: drop cascades to view your_credit diff --git a/src/test/regress/sql/select_views.sql b/src/test/regress/sql/select_views.sql index 14f1be8..8ed1512 100644 --- a/src/test/regress/sql/select_views.sql +++ b/src/test/regress/sql/select_views.sql @@ -8,3 +8,38 @@ SELECT * FROM street; SELECT name, #thepath FROM iexit ORDER BY 1, 2; SELECT * FROM toyemp WHERE name = 'sharon'; + +-- +-- Test for leaky-view problem +-- + +CREATE USER alice; +CREATE FUNCTION f_leak(text, text) + RETURNS bool LANGUAGE 'plpgsql' + COST 0.00000001 + AS 'begin raise notice ''% => %'', $1, $2; return true; end'; +CREATE TABLE credit_cards ( + name text, + number text, + expired text +); + +INSERT INTO credit_cards VALUES ('alice', '1111-2222-3333-4444', 'Aug-2012'), + ('bob', '5555-6666-7777-8888', 'Nov-2016'), + ('eve', '9801-2345-6789-0123', 'Jan-2018'); +CREATE VIEW your_credit AS + SELECT * FROM credit_cards WHERE name = getpgusername(); + +GRANT SELECT ON your_credit TO public; +-- run leaky view +SET SESSION AUTHORIZATION alice; + +SELECT * FROM your_credit WHERE f_leak(number,expired); +EXPLAIN (COSTS OFF) SELECT * FROM your_credit WHERE f_leak(number,expired); + +\c - +-- cleanups +DROP ROLE IF EXISTS alice; + +DROP FUNCTION IF EXISTS f_leak(text); +DROP TABLE IF EXISTS credit_cards CASCADE;