*** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** *** 1188,1193 **** _copyFuncExpr(FuncExpr *from) --- 1188,1194 ---- COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } *************** *** 1224,1229 **** _copyOpExpr(OpExpr *from) --- 1225,1231 ---- COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } *************** *** 1244,1249 **** _copyDistinctExpr(DistinctExpr *from) --- 1246,1252 ---- COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } *************** *** 1264,1269 **** _copyNullIfExpr(NullIfExpr *from) --- 1267,1273 ---- COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } *************** *** 1282,1287 **** _copyScalarArrayOpExpr(ScalarArrayOpExpr *from) --- 1286,1292 ---- COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } *************** *** 1422,1427 **** _copyCoerceViaIO(CoerceViaIO *from) --- 1427,1433 ---- COPY_SCALAR_FIELD(resultcollid); COPY_SCALAR_FIELD(coerceformat); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } *************** *** 1442,1447 **** _copyArrayCoerceExpr(ArrayCoerceExpr *from) --- 1448,1454 ---- COPY_SCALAR_FIELD(isExplicit); COPY_SCALAR_FIELD(coerceformat); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } *************** *** 1574,1579 **** _copyRowCompareExpr(RowCompareExpr *from) --- 1581,1587 ---- COPY_NODE_FIELD(inputcollids); COPY_NODE_FIELD(largs); COPY_NODE_FIELD(rargs); + COPY_SCALAR_FIELD(depth); return newnode; } *** a/src/backend/nodes/equalfuncs.c --- b/src/backend/nodes/equalfuncs.c *************** *** 248,253 **** _equalFuncExpr(FuncExpr *a, FuncExpr *b) --- 248,254 ---- COMPARE_SCALAR_FIELD(inputcollid); COMPARE_NODE_FIELD(args); COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(depth); return true; } *************** *** 285,290 **** _equalOpExpr(OpExpr *a, OpExpr *b) --- 286,292 ---- COMPARE_SCALAR_FIELD(inputcollid); COMPARE_NODE_FIELD(args); COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(depth); return true; } *************** *** 311,316 **** _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b) --- 313,319 ---- COMPARE_SCALAR_FIELD(inputcollid); COMPARE_NODE_FIELD(args); COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(depth); return true; } *************** *** 337,342 **** _equalNullIfExpr(NullIfExpr *a, NullIfExpr *b) --- 340,346 ---- COMPARE_SCALAR_FIELD(inputcollid); COMPARE_NODE_FIELD(args); COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(depth); return true; } *************** *** 361,366 **** _equalScalarArrayOpExpr(ScalarArrayOpExpr *a, ScalarArrayOpExpr *b) --- 365,371 ---- COMPARE_SCALAR_FIELD(inputcollid); COMPARE_NODE_FIELD(args); COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(depth); return true; } *************** *** 479,484 **** _equalCoerceViaIO(CoerceViaIO *a, CoerceViaIO *b) --- 484,490 ---- return false; COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(depth); return true; } *************** *** 503,508 **** _equalArrayCoerceExpr(ArrayCoerceExpr *a, ArrayCoerceExpr *b) --- 509,515 ---- return false; COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(depth); return true; } *************** *** 613,618 **** _equalRowCompareExpr(RowCompareExpr *a, RowCompareExpr *b) --- 620,626 ---- COMPARE_NODE_FIELD(inputcollids); COMPARE_NODE_FIELD(largs); COMPARE_NODE_FIELD(rargs); + COMPARE_SCALAR_FIELD(depth); return true; } *** a/src/backend/nodes/outfuncs.c --- b/src/backend/nodes/outfuncs.c *************** *** 1004,1009 **** _outFuncExpr(StringInfo str, FuncExpr *node) --- 1004,1010 ---- WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void *************** *** 1030,1035 **** _outOpExpr(StringInfo str, OpExpr *node) --- 1031,1037 ---- WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void *************** *** 1045,1050 **** _outDistinctExpr(StringInfo str, DistinctExpr *node) --- 1047,1053 ---- WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void *************** *** 1060,1065 **** _outNullIfExpr(StringInfo str, NullIfExpr *node) --- 1063,1069 ---- WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void *************** *** 1073,1078 **** _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node) --- 1077,1083 ---- WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void *************** *** 1190,1195 **** _outCoerceViaIO(StringInfo str, CoerceViaIO *node) --- 1195,1201 ---- WRITE_OID_FIELD(resultcollid); WRITE_ENUM_FIELD(coerceformat, CoercionForm); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void *************** *** 1205,1210 **** _outArrayCoerceExpr(StringInfo str, ArrayCoerceExpr *node) --- 1211,1217 ---- WRITE_BOOL_FIELD(isExplicit); WRITE_ENUM_FIELD(coerceformat, CoercionForm); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void *************** *** 1297,1302 **** _outRowCompareExpr(StringInfo str, RowCompareExpr *node) --- 1304,1310 ---- WRITE_NODE_FIELD(inputcollids); WRITE_NODE_FIELD(largs); WRITE_NODE_FIELD(rargs); + WRITE_INT_FIELD(depth); } static void *** a/src/backend/nodes/readfuncs.c --- b/src/backend/nodes/readfuncs.c *************** *** 541,546 **** _readFuncExpr(void) --- 541,547 ---- READ_OID_FIELD(inputcollid); READ_NODE_FIELD(args); READ_LOCATION_FIELD(location); + READ_INT_FIELD(depth); READ_DONE(); } *************** *** 588,593 **** _readOpExpr(void) --- 589,595 ---- READ_OID_FIELD(inputcollid); READ_NODE_FIELD(args); READ_LOCATION_FIELD(location); + READ_INT_FIELD(depth); READ_DONE(); } *************** *** 619,624 **** _readDistinctExpr(void) --- 621,627 ---- READ_OID_FIELD(inputcollid); READ_NODE_FIELD(args); READ_LOCATION_FIELD(location); + READ_INT_FIELD(depth); READ_DONE(); } *************** *** 650,655 **** _readNullIfExpr(void) --- 653,659 ---- READ_OID_FIELD(inputcollid); READ_NODE_FIELD(args); READ_LOCATION_FIELD(location); + READ_INT_FIELD(depth); READ_DONE(); } *************** *** 679,684 **** _readScalarArrayOpExpr(void) --- 683,689 ---- READ_OID_FIELD(inputcollid); READ_NODE_FIELD(args); READ_LOCATION_FIELD(location); + READ_INT_FIELD(depth); READ_DONE(); } *************** *** 794,799 **** _readCoerceViaIO(void) --- 799,805 ---- READ_OID_FIELD(resultcollid); READ_ENUM_FIELD(coerceformat, CoercionForm); READ_LOCATION_FIELD(location); + READ_INT_FIELD(depth); READ_DONE(); } *************** *** 814,819 **** _readArrayCoerceExpr(void) --- 820,826 ---- READ_BOOL_FIELD(isExplicit); READ_ENUM_FIELD(coerceformat, CoercionForm); READ_LOCATION_FIELD(location); + READ_INT_FIELD(depth); READ_DONE(); } *************** *** 946,951 **** _readRowCompareExpr(void) --- 953,959 ---- READ_NODE_FIELD(inputcollids); READ_NODE_FIELD(largs); READ_NODE_FIELD(rargs); + READ_INT_FIELD(depth); READ_DONE(); } *** a/src/backend/optimizer/path/costsize.c --- b/src/backend/optimizer/path/costsize.c *************** *** 2593,2598 **** cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root) --- 2593,2599 ---- 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,2623 **** cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root) --- 2619,2625 ---- context.root = root; context.total.startup = 0; context.total.per_tuple = 0; + context.total.depth = 0; cost_qual_eval_walker(qual, &context); *************** *** 2647,2652 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) --- 2649,2655 ---- 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,2676 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) --- 2674,2681 ---- } 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,2699 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) --- 2699,2706 ---- { 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,2708 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) --- 2710,2717 ---- 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,2721 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) --- 2725,2732 ---- 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,2751 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) --- 2757,2764 ---- 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,2760 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) --- 2768,2775 ---- 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,2774 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) --- 2784,2791 ---- 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)) { *** a/src/backend/optimizer/plan/createplan.c --- b/src/backend/optimizer/plan/createplan.c *************** *** 2704,2709 **** order_qual_clauses(PlannerInfo *root, List *clauses) --- 2704,2710 ---- { Node *clause; Cost cost; + int depth; } QualItem; int nitems = list_length(clauses); QualItem *items; *************** *** 2729,2734 **** order_qual_clauses(PlannerInfo *root, List *clauses) --- 2730,2736 ---- cost_qual_eval_node(&qcost, clause, root); items[i].clause = clause; items[i].cost = qcost.per_tuple; + items[i].depth = qcost.depth; i++; } *************** *** 2745,2751 **** 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) break; items[j] = items[j - 1]; } --- 2747,2762 ---- /* insert newitem into the already-sorted subarray */ for (j = i; j > 0; j--) { ! /* ! * 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]; } *** a/src/backend/optimizer/plan/planner.c --- b/src/backend/optimizer/plan/planner.c *************** *** 33,38 **** --- 33,39 ---- #include "optimizer/subselect.h" #include "optimizer/tlist.h" #include "optimizer/var.h" + #include "nodes/nodeFuncs.h" #ifdef OPTIMIZER_DEBUG #include "nodes/print.h" #endif *************** *** 104,109 **** static void get_column_info_for_window(PlannerInfo *root, WindowClause *wc, --- 105,111 ---- int *ordNumCols, AttrNumber **ordColIdx, Oid **ordOperators); + static void mark_qualifiers_depth(Query *query, int depth); /***************************************************************************** *************** *** 149,154 **** standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) --- 151,162 ---- 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, 0); + + /* * 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 *************** *** 2994,2999 **** get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist, --- 3002,3127 ---- } } + /* + * 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; + } + return expression_tree_walker(node, mark_qualifiers_depth_walker, context); + } + + static void + mark_qualifiers_depth(Query *query, int depth) + { + ListCell *l; + + foreach (l, query->rtable) + { + RangeTblEntry *rte = lfirst(l); + + /* + * If and when sub-query is defined as a security-barrier, + * any qualifiers of WHERE or JOIN ... ON clause must be + * launched earlier than ones come from upper nest level, + * even if the sub-query is enough simple to be pulled-up + * later, because user can reference contents of tuples to + * be invisible using functions with side-effect and much + * smaller cost estimation. + */ + if (rte->rtekind == RTE_SUBQUERY) + { + if (rte->security_barrier) + mark_qualifiers_depth(rte->subquery, depth + 1); + else + mark_qualifiers_depth(rte->subquery, depth); + } + } + mark_qualifiers_depth_walker((Node *)query->jointree, &depth); + } /* * expression_planner *** a/src/backend/optimizer/util/clauses.c --- b/src/backend/optimizer/util/clauses.c *************** *** 111,116 **** static Expr *simplify_function(Expr *oldexpr, Oid funcid, --- 111,117 ---- 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,129 **** 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, eval_const_expressions_context *context); static Expr *inline_function(Oid funcid, Oid result_type, Oid result_collid, Oid input_collid, List *args, --- 124,130 ---- 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, 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,2235 **** eval_const_expressions_mutator(Node *node, expr->funccollid, expr->inputcollid, &args, ! has_named_args, true, context); if (simple) /* successfully simplified it */ return (Node *) simple; --- 2230,2236 ---- expr->funccollid, expr->inputcollid, &args, ! has_named_args, true, expr->depth, context); if (simple) /* successfully simplified it */ return (Node *) simple; *************** *** 2248,2253 **** eval_const_expressions_mutator(Node *node, --- 2249,2255 ---- newexpr->inputcollid = expr->inputcollid; newexpr->args = args; newexpr->location = expr->location; + newexpr->depth = expr->depth; return (Node *) newexpr; } if (IsA(node, OpExpr)) *************** *** 2282,2288 **** eval_const_expressions_mutator(Node *node, expr->opcollid, expr->inputcollid, &args, ! false, true, context); if (simple) /* successfully simplified it */ return (Node *) simple; --- 2284,2290 ---- expr->opcollid, expr->inputcollid, &args, ! false, true, expr->depth, context); if (simple) /* successfully simplified it */ return (Node *) simple; *************** *** 2313,2318 **** eval_const_expressions_mutator(Node *node, --- 2315,2321 ---- newexpr->inputcollid = expr->inputcollid; newexpr->args = args; newexpr->location = expr->location; + newexpr->depth = expr->depth; return (Node *) newexpr; } if (IsA(node, DistinctExpr)) *************** *** 2380,2386 **** eval_const_expressions_mutator(Node *node, expr->opcollid, expr->inputcollid, &args, ! false, false, context); if (simple) /* successfully simplified it */ { /* --- 2383,2389 ---- expr->opcollid, expr->inputcollid, &args, ! false, false, expr->depth, context); if (simple) /* successfully simplified it */ { /* *************** *** 2410,2415 **** eval_const_expressions_mutator(Node *node, --- 2413,2419 ---- newexpr->inputcollid = expr->inputcollid; newexpr->args = args; newexpr->location = expr->location; + newexpr->depth = expr->depth; return (Node *) newexpr; } if (IsA(node, BoolExpr)) *************** *** 2570,2576 **** eval_const_expressions_mutator(Node *node, InvalidOid, InvalidOid, &args, ! false, true, context); if (simple) /* successfully simplified output fn */ { /* --- 2574,2580 ---- InvalidOid, InvalidOid, &args, ! false, true, expr->depth, context); if (simple) /* successfully simplified output fn */ { /* *************** *** 2591,2597 **** eval_const_expressions_mutator(Node *node, expr->resultcollid, InvalidOid, &args, ! false, true, context); if (simple) /* successfully simplified input fn */ return (Node *) simple; } --- 2595,2601 ---- expr->resultcollid, InvalidOid, &args, ! false, true, expr->depth, context); if (simple) /* successfully simplified input fn */ return (Node *) simple; } *************** *** 2607,2612 **** eval_const_expressions_mutator(Node *node, --- 2611,2617 ---- newexpr->resultcollid = expr->resultcollid; newexpr->coerceformat = expr->coerceformat; newexpr->location = expr->location; + newexpr->depth = expr->depth; return (Node *) newexpr; } if (IsA(node, ArrayCoerceExpr)) *************** *** 2631,2636 **** eval_const_expressions_mutator(Node *node, --- 2636,2642 ---- 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,3452 **** simplify_function(Expr *oldexpr, Oid funcid, --- 3453,3459 ---- Oid input_collid, List **args, bool has_named_args, bool allow_inline, + int depth, eval_const_expressions_context *context) { HeapTuple func_tuple; *************** *** 3477,3483 **** simplify_function(Expr *oldexpr, Oid funcid, newexpr = evaluate_function(funcid, result_type, result_typmod, result_collid, input_collid, *args, ! func_tuple, context); /* * Some functions calls can be simplified at plan time based on properties --- 3484,3490 ---- newexpr = evaluate_function(funcid, result_type, result_typmod, result_collid, input_collid, *args, ! func_tuple, depth, context); /* * Some functions calls can be simplified at plan time based on properties *************** *** 3764,3770 **** 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, eval_const_expressions_context *context) { Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); --- 3771,3777 ---- static Expr * evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, Oid result_collid, Oid input_collid, List *args, ! HeapTuple func_tuple, int depth, eval_const_expressions_context *context) { Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); *************** *** 3850,3855 **** evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, --- 3857,3863 ---- newexpr->inputcollid = input_collid; newexpr->args = args; newexpr->location = -1; + newexpr->depth = depth; return evaluate_expr((Expr *) newexpr, result_type, result_typmod, result_collid); *** a/src/include/nodes/primnodes.h --- b/src/include/nodes/primnodes.h *************** *** 335,340 **** typedef struct FuncExpr --- 335,341 ---- 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,385 **** typedef struct OpExpr --- 381,387 ---- 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,426 **** typedef struct ScalarArrayOpExpr --- 423,429 ---- 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,690 **** typedef struct CoerceViaIO --- 688,694 ---- 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,715 **** typedef struct ArrayCoerceExpr --- 714,720 ---- 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,906 **** typedef struct RowCompareExpr --- 906,912 ---- 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; /* *** a/src/include/nodes/relation.h --- b/src/include/nodes/relation.h *************** *** 44,49 **** typedef struct QualCost --- 44,50 ---- { Cost startup; /* one-time cost */ Cost per_tuple; /* per-evaluation cost */ + int depth; /* depth of qual in the original query */ } QualCost; /* *** a/src/test/regress/expected/select_views.out --- b/src/test/regress/expected/select_views.out *************** *** 467,472 **** SELECT name, #thepath FROM iexit ORDER BY 1, 2; --- 467,486 ---- 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,736 **** 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 --- 731,736 ---- *************** *** 1247,1249 **** SELECT * FROM toyemp WHERE name = 'sharon'; --- 1247,1312 ---- sharon | 25 | (15,12) | 12000 (1 row) + -- + -- Test for leaky-view + -- + 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_normal AS + SELECT * FROM credit_cards WHERE name = getpgusername(); + CREATE VIEW your_credit_secure WITH (security_barrier) AS + SELECT * FROM credit_cards WHERE name = getpgusername(); + GRANT SELECT ON your_credit_normal TO public; + GRANT SELECT ON your_credit_secure TO public; + -- run leaky view + SET SESSION AUTHORIZATION alice; + SELECT * FROM your_credit_normal WHERE f_leak(number,expired); + NOTICE: 1111-2222-3333-4444 => Aug-2012 + NOTICE: 5555-6666-7777-8888 => Nov-2016 + NOTICE: 9801-2345-6789-0123 => Jan-2018 + name | number | expired + -------+---------------------+---------- + alice | 1111-2222-3333-4444 | Aug-2012 + (1 row) + + EXPLAIN (COSTS OFF) SELECT * FROM your_credit_normal WHERE f_leak(number,expired); + QUERY PLAN + -------------------------------------------------------------------------- + Seq Scan on credit_cards + Filter: (f_leak(number, expired) AND (name = (getpgusername())::text)) + (2 rows) + + SELECT * FROM your_credit_secure 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_secure 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 2 other objects + DETAIL: drop cascades to view your_credit_normal + drop cascades to view your_credit_secure *** a/src/test/regress/sql/select_views.sql --- b/src/test/regress/sql/select_views.sql *************** *** 8,10 **** SELECT * FROM street; --- 8,49 ---- SELECT name, #thepath FROM iexit ORDER BY 1, 2; SELECT * FROM toyemp WHERE name = 'sharon'; + + -- + -- Test for leaky-view + -- + + 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_normal AS + SELECT * FROM credit_cards WHERE name = getpgusername(); + CREATE VIEW your_credit_secure WITH (security_barrier) AS + SELECT * FROM credit_cards WHERE name = getpgusername(); + + GRANT SELECT ON your_credit_normal TO public; + GRANT SELECT ON your_credit_secure TO public; + -- run leaky view + SET SESSION AUTHORIZATION alice; + + SELECT * FROM your_credit_normal WHERE f_leak(number,expired); + EXPLAIN (COSTS OFF) SELECT * FROM your_credit_normal WHERE f_leak(number,expired); + + SELECT * FROM your_credit_secure WHERE f_leak(number,expired); + EXPLAIN (COSTS OFF) SELECT * FROM your_credit_secure 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;