diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/allpaths.c | 11 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 140 |
2 files changed, 142 insertions, 9 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 19bf7340ad4..e99e4cc1761 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -1042,16 +1042,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); Node *clause = (Node *) rinfo->clause; - /* - * XXX. You might wonder why we're testing rte->security_barrier - * qual-by-qual here rather than hoisting the test up into the - * surrounding if statement; after all, the answer will be the - * same for all quals. The answer is that we expect to shortly - * change this logic to allow pushing down some quals that use only - * "leakproof" operators even through a security barrier. - */ if (!rinfo->pseudoconstant && - !rte->security_barrier && + (!rte->security_barrier || + !contain_leaky_functions(clause)) && qual_is_pushdown_safe(subquery, rti, clause, differentTypes)) { /* Push it down */ diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 126d49452e2..cd3da46bc5e 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -93,6 +93,7 @@ static bool contain_subplans_walker(Node *node, void *context); static bool contain_mutable_functions_walker(Node *node, void *context); static bool contain_volatile_functions_walker(Node *node, void *context); static bool contain_nonstrict_functions_walker(Node *node, void *context); +static bool contain_leaky_functions_walker(Node *node, void *context); static Relids find_nonnullable_rels_walker(Node *node, bool top_level); static List *find_nonnullable_vars_walker(Node *node, bool top_level); static bool is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK); @@ -1129,6 +1130,145 @@ contain_nonstrict_functions_walker(Node *node, void *context) context); } +/***************************************************************************** + * Check clauses for non-leakproof functions + *****************************************************************************/ + +/* + * contain_leaky_functions + * Recursively search for leaky functions within a clause. + * + * Returns true if any function call with side-effect may be present in the + * clause. Qualifiers from outside the a security_barrier view should not + * be pushed down into the view, lest the contents of tuples intended to be + * filtered out be revealed via side effects. + */ +bool +contain_leaky_functions(Node *clause) +{ + return contain_leaky_functions_walker(clause, NULL); +} + +static bool +contain_leaky_functions_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + + switch (nodeTag(node)) + { + case T_Var: + case T_Const: + case T_Param: + case T_ArrayExpr: + case T_NamedArgExpr: + case T_BoolExpr: + case T_RelabelType: + case T_CaseExpr: + case T_CaseTestExpr: + case T_RowExpr: + case T_MinMaxExpr: + case T_NullTest: + case T_BooleanTest: + case T_List: + /* + * We know these node types don't contain function calls; but + * something further down in the node tree might. + */ + break; + + case T_FuncExpr: + { + FuncExpr *expr = (FuncExpr *) node; + + if (!get_func_leakproof(expr->funcid)) + return true; + } + break; + + case T_OpExpr: + case T_DistinctExpr: /* struct-equivalent to OpExpr */ + case T_NullIfExpr: /* struct-equivalent to OpExpr */ + { + OpExpr *expr = (OpExpr *) node; + + set_opfuncid(expr); + if (!get_func_leakproof(expr->opfuncid)) + return true; + } + break; + + case T_ScalarArrayOpExpr: + { + ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; + + set_sa_opfuncid(expr); + if (!get_func_leakproof(expr->opfuncid)) + return true; + } + break; + + case T_CoerceViaIO: + { + CoerceViaIO *expr = (CoerceViaIO *) node; + Oid funcid; + Oid ioparam; + bool varlena; + + getTypeInputInfo(exprType((Node *)expr->arg), + &funcid, &ioparam); + if (!get_func_leakproof(funcid)) + return true; + + getTypeOutputInfo(expr->resulttype, &funcid, &varlena); + if (!get_func_leakproof(funcid)) + return true; + } + break; + + case T_ArrayCoerceExpr: + { + ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; + Oid funcid; + Oid ioparam; + bool varlena; + + getTypeInputInfo(exprType((Node *)expr->arg), + &funcid, &ioparam); + if (!get_func_leakproof(funcid)) + return true; + getTypeOutputInfo(expr->resulttype, &funcid, &varlena); + if (!get_func_leakproof(funcid)) + return true; + } + break; + + case T_RowCompareExpr: + { + RowCompareExpr *rcexpr = (RowCompareExpr *) node; + ListCell *opid; + + foreach(opid, rcexpr->opnos) + { + Oid funcid = get_opcode(lfirst_oid(opid)); + + if (!get_func_leakproof(funcid)) + return true; + } + } + break; + + default: + /* + * If we don't recognize the node tag, assume it might be leaky. + * This prevents an unexpected security hole if someone adds a new + * node type that can call a function. + */ + return true; + } + return expression_tree_walker(node, contain_leaky_functions_walker, + context); +} /* * find_nonnullable_rels |