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 | 3 +- src/backend/optimizer/prep/prepjointree.c | 171 +++++++++++++++++++++++++---- src/backend/optimizer/util/clauses.c | 24 +++-- src/include/nodes/primnodes.h | 6 + src/include/nodes/relation.h | 1 + src/include/optimizer/prep.h | 3 +- 12 files changed, 234 insertions(+), 36 deletions(-) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 661a516..4e61789 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1183,6 +1183,7 @@ _copyFuncExpr(FuncExpr *from) COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } @@ -1219,6 +1220,7 @@ _copyOpExpr(OpExpr *from) COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } @@ -1239,6 +1241,7 @@ _copyDistinctExpr(DistinctExpr *from) COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } @@ -1259,6 +1262,7 @@ _copyNullIfExpr(NullIfExpr *from) COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } @@ -1277,6 +1281,7 @@ _copyScalarArrayOpExpr(ScalarArrayOpExpr *from) COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } @@ -1417,6 +1422,7 @@ _copyCoerceViaIO(CoerceViaIO *from) COPY_SCALAR_FIELD(resultcollid); COPY_SCALAR_FIELD(coerceformat); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } @@ -1437,6 +1443,7 @@ _copyArrayCoerceExpr(ArrayCoerceExpr *from) COPY_SCALAR_FIELD(isExplicit); COPY_SCALAR_FIELD(coerceformat); COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(depth); return newnode; } @@ -1569,6 +1576,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 4052a9a..abef8a7 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 0d0ce3c..16a0239 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -997,6 +997,7 @@ _outFuncExpr(StringInfo str, FuncExpr *node) WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void @@ -1023,6 +1024,7 @@ _outOpExpr(StringInfo str, OpExpr *node) WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void @@ -1038,6 +1040,7 @@ _outDistinctExpr(StringInfo str, DistinctExpr *node) WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void @@ -1053,6 +1056,7 @@ _outNullIfExpr(StringInfo str, NullIfExpr *node) WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void @@ -1066,6 +1070,7 @@ _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node) WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); + WRITE_INT_FIELD(depth); } static void @@ -1183,6 +1188,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 @@ -1198,6 +1204,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 @@ -1290,6 +1297,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 7812a86..0a758dc 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 0dedeba..8460c4b 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -2702,6 +2702,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses) { Node *clause; Cost cost; + int depth; } QualItem; int nitems = list_length(clauses); QualItem *items; @@ -2727,6 +2728,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++; } @@ -2743,7 +2745,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 b498274..5f17177 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -334,7 +334,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, * query. */ parse->jointree = (FromExpr *) - pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL); + pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL, 0); /* * If this is a simple UNION ALL query, flatten it into an appendrel. We @@ -3155,7 +3155,6 @@ get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist, } } - /* * expression_planner * Perform planner's transformations on a standalone expression. diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index aeaae8c..4bd9956 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -23,6 +23,7 @@ */ #include "postgres.h" +#include "catalog/pg_class.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" @@ -34,6 +35,7 @@ #include "parser/parse_relation.h" #include "parser/parsetree.h" #include "rewrite/rewriteManip.h" +#include "utils/syscache.h" typedef struct pullup_replace_vars_context @@ -62,12 +64,13 @@ static Node *pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, JoinExpr *lowest_outer_join, - AppendRelInfo *containing_appendrel); + AppendRelInfo *containing_appendrel, + int qubquery_depth); static Node *pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, - RangeTblEntry *rte); + RangeTblEntry *rte, int subquery_depth); static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, Query *setOpQuery, - int childRToffset); + int childRToffset, int subquery_depth); static void make_setop_translation_list(Query *query, Index newvarno, List **translated_vars); static bool is_simple_subquery(Query *subquery); @@ -91,6 +94,7 @@ static void reduce_outer_joins_pass2(Node *jtnode, List *forced_null_vars); static void substitute_multiple_relids(Node *node, int varno, Relids subrelids); +static void mark_qualifiers_depth(Node *node, int depth); static void fix_append_rel_relids(List *append_rel_list, int varno, Relids subrelids); static Node *find_jointree_node_for_rel(Node *jtnode, int relid); @@ -525,7 +529,8 @@ inline_set_returning_functions(PlannerInfo *root) Node * pull_up_subqueries(PlannerInfo *root, Node *jtnode, JoinExpr *lowest_outer_join, - AppendRelInfo *containing_appendrel) + AppendRelInfo *containing_appendrel, + int subquery_depth) { if (jtnode == NULL) return NULL; @@ -547,7 +552,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, is_safe_append_member(rte->subquery))) return pull_up_simple_subquery(root, jtnode, rte, lowest_outer_join, - containing_appendrel); + containing_appendrel, + subquery_depth); /* * Alternatively, is it a simple UNION ALL subquery? If so, flatten @@ -560,7 +566,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, */ if (rte->rtekind == RTE_SUBQUERY && is_simple_union_all(rte->subquery)) - return pull_up_simple_union_all(root, jtnode, rte); + return pull_up_simple_union_all(root, jtnode, rte, + subquery_depth); /* Otherwise, do nothing at this node. */ } @@ -572,7 +579,16 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, Assert(containing_appendrel == NULL); foreach(l, f->fromlist) lfirst(l) = pull_up_subqueries(root, lfirst(l), - lowest_outer_join, NULL); + lowest_outer_join, NULL, + subquery_depth); + /* + * Mark the sub-query depth of qualifiers to determine the original + * level of them, if necessary. Expr->depth is initialized to zero, + * so we don't need to walk on the expression tree, if security view + * was not used. + */ + if (subquery_depth > 0) + mark_qualifiers_depth(f->quals, subquery_depth); } else if (IsA(jtnode, JoinExpr)) { @@ -584,35 +600,45 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, { case JOIN_INNER: j->larg = pull_up_subqueries(root, j->larg, - lowest_outer_join, NULL); + lowest_outer_join, NULL, + subquery_depth); j->rarg = pull_up_subqueries(root, j->rarg, - lowest_outer_join, NULL); + lowest_outer_join, NULL, + subquery_depth); break; case JOIN_LEFT: case JOIN_SEMI: case JOIN_ANTI: j->larg = pull_up_subqueries(root, j->larg, - lowest_outer_join, NULL); + lowest_outer_join, NULL, + subquery_depth); j->rarg = pull_up_subqueries(root, j->rarg, - j, NULL); + j, NULL, + subquery_depth); break; case JOIN_FULL: j->larg = pull_up_subqueries(root, j->larg, - j, NULL); + j, NULL, + subquery_depth); j->rarg = pull_up_subqueries(root, j->rarg, - j, NULL); + j, NULL, + subquery_depth); break; case JOIN_RIGHT: j->larg = pull_up_subqueries(root, j->larg, - j, NULL); + j, NULL, + subquery_depth); j->rarg = pull_up_subqueries(root, j->rarg, - lowest_outer_join, NULL); + lowest_outer_join, NULL, + subquery_depth); break; default: elog(ERROR, "unrecognized join type: %d", (int) j->jointype); break; } + if (subquery_depth > 0) + mark_qualifiers_depth(j->quals, subquery_depth); } else elog(ERROR, "unrecognized node type: %d", @@ -635,7 +661,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, static Node * pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, JoinExpr *lowest_outer_join, - AppendRelInfo *containing_appendrel) + AppendRelInfo *containing_appendrel, + int subquery_depth) { Query *parse = root->parse; int varno = ((RangeTblRef *) jtnode)->rtindex; @@ -679,6 +706,31 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, Assert(subquery->cteList == NIL); /* + * Increment subquery_depth, if the supplied sub-query come from + * a view defined with CREATE SECURITY VIEW. This depth value shall + * be checked to determine the order to launch qualifiers during + * table scans. It enables to prevent unexpected information leaks + * due to the interaction between query optimization and functions + * with side-effects. + */ + if (OidIsValid(rte->relid)) + { + HeapTuple tup; + Form_pg_class class_form; + + tup = SearchSysCache1(RELOID, ObjectIdGetDatum(rte->relid)); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for relation %u", rte->relid); + + class_form = (Form_pg_class) GETSTRUCT(tup); + Assert(class_form->relkind == RELKIND_VIEW); + if (class_form->relissecbarrier) + subquery_depth += 1; + + ReleaseSysCache(tup); + } + + /* * Pull up any SubLinks within the subquery's quals, so that we don't * leave unoptimized SubLinks behind. */ @@ -701,7 +753,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, * handling an appendrel member. */ subquery->jointree = (FromExpr *) - pull_up_subqueries(subroot, (Node *) subquery->jointree, NULL, NULL); + pull_up_subqueries(subroot, (Node *) subquery->jointree, + NULL, NULL, subquery_depth); /* * Now we must recheck whether the subquery is still simple enough to pull @@ -903,7 +956,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, * jtnode, since we don't actually need to change the query jointree. */ static Node * -pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) +pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, + int subquery_depth) { int varno = ((RangeTblRef *) jtnode)->rtindex; Query *subquery = rte->subquery; @@ -930,7 +984,7 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) */ Assert(subquery->setOperations); pull_up_union_leaf_queries(subquery->setOperations, root, varno, subquery, - rtoffset); + rtoffset, subquery_depth); /* * Mark the parent as an append relation. @@ -960,7 +1014,8 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) */ static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, - Query *setOpQuery, int childRToffset) + Query *setOpQuery, int childRToffset, + int subquery_depth) { if (IsA(setOp, RangeTblRef)) { @@ -995,7 +1050,8 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, */ rtr = makeNode(RangeTblRef); rtr->rtindex = childRTindex; - (void) pull_up_subqueries(root, (Node *) rtr, NULL, appinfo); + (void) pull_up_subqueries(root, (Node *) rtr, NULL, + appinfo, subquery_depth); } else if (IsA(setOp, SetOperationStmt)) { @@ -1003,9 +1059,9 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, /* Recurse to reach leaf queries */ pull_up_union_leaf_queries(op->larg, root, parentRTindex, setOpQuery, - childRToffset); + childRToffset, subquery_depth); pull_up_union_leaf_queries(op->rarg, root, parentRTindex, setOpQuery, - childRToffset); + childRToffset, subquery_depth); } else { @@ -1566,7 +1622,7 @@ flatten_simple_union_all(PlannerInfo *root) * weren't previously referenced by the jointree, and so were missed by * the main invocation of pull_up_subqueries.) */ - pull_up_union_leaf_queries((Node *) topop, root, leftmostRTI, parse, 0); + pull_up_union_leaf_queries((Node *) topop, root, leftmostRTI, parse, 0, 0); } @@ -2051,6 +2107,73 @@ substitute_multiple_relids(Node *node, int varno, Relids subrelids) } /* + * 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 *)node)->depth = depth; + } + else if (IsA(node, OpExpr)) + { + ((OpExpr *)node)->depth = depth; + } + else if (IsA(node, DistinctExpr)) + { + ((DistinctExpr *)node)->depth = depth; + } + else if (IsA(node, ScalarArrayOpExpr)) + { + ((ScalarArrayOpExpr *)node)->depth = depth; + } + else if (IsA(node, CoerceViaIO)) + { + ((CoerceViaIO *)node)->depth = depth; + } + else if (IsA(node, ArrayCoerceExpr)) + { + ((ArrayCoerceExpr *)node)->depth = depth; + } + else if (IsA(node, NullIfExpr)) + { + ((NullIfExpr *)node)->depth = depth; + } + else if (IsA(node, RowCompareExpr)) + { + ((RowCompareExpr *)node)->depth = depth; + } + return expression_tree_walker(node, mark_qualifiers_depth_walker, context); +} + +static void +mark_qualifiers_depth(Node *node, int depth) +{ + mark_qualifiers_depth_walker(node, &depth); +} + +/* * fix_append_rel_relids: update RT-index fields of AppendRelInfo nodes * * When we pull up a subquery, any AppendRelInfo references to the subquery's diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index baa90fa..6df99f6 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -110,6 +110,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, @@ -122,7 +123,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, @@ -2188,7 +2189,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; @@ -2207,6 +2208,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)) @@ -2241,7 +2243,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; @@ -2272,6 +2274,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)) @@ -2339,7 +2342,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 */ { /* @@ -2369,6 +2372,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)) @@ -2529,7 +2533,7 @@ eval_const_expressions_mutator(Node *node, InvalidOid, InvalidOid, &args, - false, true, context); + false, true, expr->depth, context); if (simple) /* successfully simplified output fn */ { /* @@ -2550,7 +2554,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; } @@ -2566,6 +2570,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)) @@ -2590,6 +2595,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 @@ -3406,6 +3412,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; @@ -3436,7 +3443,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 @@ -3723,7 +3730,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); @@ -3809,6 +3816,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 ecbbc1c..88b4826 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -43,6 +43,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/include/optimizer/prep.h b/src/include/optimizer/prep.h index 5e94620..292ec06 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -25,7 +25,8 @@ extern void pull_up_sublinks(PlannerInfo *root); extern void inline_set_returning_functions(PlannerInfo *root); extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode, JoinExpr *lowest_outer_join, - AppendRelInfo *containing_appendrel); + AppendRelInfo *containing_appendrel, + int subquery_depth); extern void flatten_simple_union_all(PlannerInfo *root); extern void reduce_outer_joins(PlannerInfo *root); extern Relids get_relids_in_jointree(Node *jtnode, bool include_joins);