diff options
Diffstat (limited to 'src/backend/optimizer/util/clauses.c')
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 5255 |
1 files changed, 5255 insertions, 0 deletions
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c new file mode 100644 index 0000000..f2216f5 --- /dev/null +++ b/src/backend/optimizer/util/clauses.c @@ -0,0 +1,5255 @@ +/*------------------------------------------------------------------------- + * + * clauses.c + * routines to manipulate qualification clauses + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/optimizer/util/clauses.c + * + * HISTORY + * AUTHOR DATE MAJOR EVENT + * Andrew Yu Nov 3, 1994 clause.c and clauses.c combined + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/htup_details.h" +#include "catalog/pg_aggregate.h" +#include "catalog/pg_class.h" +#include "catalog/pg_language.h" +#include "catalog/pg_operator.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_type.h" +#include "executor/executor.h" +#include "executor/functions.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "nodes/subscripting.h" +#include "nodes/supportnodes.h" +#include "optimizer/clauses.h" +#include "optimizer/cost.h" +#include "optimizer/optimizer.h" +#include "optimizer/plancat.h" +#include "optimizer/planmain.h" +#include "parser/analyze.h" +#include "parser/parse_agg.h" +#include "parser/parse_coerce.h" +#include "parser/parse_func.h" +#include "rewrite/rewriteHandler.h" +#include "rewrite/rewriteManip.h" +#include "tcop/tcopprot.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/fmgroids.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/syscache.h" +#include "utils/typcache.h" + +typedef struct +{ + ParamListInfo boundParams; + PlannerInfo *root; + List *active_fns; + Node *case_val; + bool estimate; +} eval_const_expressions_context; + +typedef struct +{ + int nargs; + List *args; + int *usecounts; +} substitute_actual_parameters_context; + +typedef struct +{ + int nargs; + List *args; + int sublevels_up; +} substitute_actual_srf_parameters_context; + +typedef struct +{ + char *proname; + char *prosrc; +} inline_error_callback_arg; + +typedef struct +{ + char max_hazard; /* worst proparallel hazard found so far */ + char max_interesting; /* worst proparallel hazard of interest */ + List *safe_param_ids; /* PARAM_EXEC Param IDs to treat as safe */ +} max_parallel_hazard_context; + +static bool contain_agg_clause_walker(Node *node, void *context); +static bool find_window_functions_walker(Node *node, WindowFuncLists *lists); +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_volatile_functions_not_nextval_walker(Node *node, void *context); +static bool max_parallel_hazard_walker(Node *node, + max_parallel_hazard_context *context); +static bool contain_nonstrict_functions_walker(Node *node, void *context); +static bool contain_exec_param_walker(Node *node, List *param_ids); +static bool contain_context_dependent_node(Node *clause); +static bool contain_context_dependent_node_walker(Node *node, int *flags); +static bool contain_leaked_vars_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); +static bool convert_saop_to_hashed_saop_walker(Node *node, void *context); +static Node *eval_const_expressions_mutator(Node *node, + eval_const_expressions_context *context); +static bool contain_non_const_walker(Node *node, void *context); +static bool ece_function_is_safe(Oid funcid, + eval_const_expressions_context *context); +static List *simplify_or_arguments(List *args, + eval_const_expressions_context *context, + bool *haveNull, bool *forceTrue); +static List *simplify_and_arguments(List *args, + eval_const_expressions_context *context, + bool *haveNull, bool *forceFalse); +static Node *simplify_boolean_equality(Oid opno, List *args); +static Expr *simplify_function(Oid funcid, + Oid result_type, int32 result_typmod, + Oid result_collid, Oid input_collid, List **args_p, + bool funcvariadic, bool process_args, bool allow_non_const, + eval_const_expressions_context *context); +static List *reorder_function_arguments(List *args, int pronargs, + HeapTuple func_tuple); +static List *add_function_defaults(List *args, int pronargs, + HeapTuple func_tuple); +static List *fetch_function_defaults(HeapTuple func_tuple); +static void recheck_cast_function_args(List *args, Oid result_type, + Oid *proargtypes, int pronargs, + HeapTuple func_tuple); +static Expr *evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, + Oid result_collid, Oid input_collid, List *args, + bool funcvariadic, + 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, + bool funcvariadic, + HeapTuple func_tuple, + eval_const_expressions_context *context); +static Node *substitute_actual_parameters(Node *expr, int nargs, List *args, + int *usecounts); +static Node *substitute_actual_parameters_mutator(Node *node, + substitute_actual_parameters_context *context); +static void sql_inline_error_callback(void *arg); +static Query *substitute_actual_srf_parameters(Query *expr, + int nargs, List *args); +static Node *substitute_actual_srf_parameters_mutator(Node *node, + substitute_actual_srf_parameters_context *context); +static bool pull_paramids_walker(Node *node, Bitmapset **context); + + +/***************************************************************************** + * Aggregate-function clause manipulation + *****************************************************************************/ + +/* + * contain_agg_clause + * Recursively search for Aggref/GroupingFunc nodes within a clause. + * + * Returns true if any aggregate found. + * + * This does not descend into subqueries, and so should be used only after + * reduction of sublinks to subplans, or in contexts where it's known there + * are no subqueries. There mustn't be outer-aggregate references either. + * + * (If you want something like this but able to deal with subqueries, + * see rewriteManip.c's contain_aggs_of_level().) + */ +bool +contain_agg_clause(Node *clause) +{ + return contain_agg_clause_walker(clause, NULL); +} + +static bool +contain_agg_clause_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + if (IsA(node, Aggref)) + { + Assert(((Aggref *) node)->agglevelsup == 0); + return true; /* abort the tree traversal and return true */ + } + if (IsA(node, GroupingFunc)) + { + Assert(((GroupingFunc *) node)->agglevelsup == 0); + return true; /* abort the tree traversal and return true */ + } + Assert(!IsA(node, SubLink)); + return expression_tree_walker(node, contain_agg_clause_walker, context); +} + +/***************************************************************************** + * Window-function clause manipulation + *****************************************************************************/ + +/* + * contain_window_function + * Recursively search for WindowFunc nodes within a clause. + * + * Since window functions don't have level fields, but are hard-wired to + * be associated with the current query level, this is just the same as + * rewriteManip.c's function. + */ +bool +contain_window_function(Node *clause) +{ + return contain_windowfuncs(clause); +} + +/* + * find_window_functions + * Locate all the WindowFunc nodes in an expression tree, and organize + * them by winref ID number. + * + * Caller must provide an upper bound on the winref IDs expected in the tree. + */ +WindowFuncLists * +find_window_functions(Node *clause, Index maxWinRef) +{ + WindowFuncLists *lists = palloc(sizeof(WindowFuncLists)); + + lists->numWindowFuncs = 0; + lists->maxWinRef = maxWinRef; + lists->windowFuncs = (List **) palloc0((maxWinRef + 1) * sizeof(List *)); + (void) find_window_functions_walker(clause, lists); + return lists; +} + +static bool +find_window_functions_walker(Node *node, WindowFuncLists *lists) +{ + if (node == NULL) + return false; + if (IsA(node, WindowFunc)) + { + WindowFunc *wfunc = (WindowFunc *) node; + + /* winref is unsigned, so one-sided test is OK */ + if (wfunc->winref > lists->maxWinRef) + elog(ERROR, "WindowFunc contains out-of-range winref %u", + wfunc->winref); + /* eliminate duplicates, so that we avoid repeated computation */ + if (!list_member(lists->windowFuncs[wfunc->winref], wfunc)) + { + lists->windowFuncs[wfunc->winref] = + lappend(lists->windowFuncs[wfunc->winref], wfunc); + lists->numWindowFuncs++; + } + + /* + * We assume that the parser checked that there are no window + * functions in the arguments or filter clause. Hence, we need not + * recurse into them. (If either the parser or the planner screws up + * on this point, the executor will still catch it; see ExecInitExpr.) + */ + return false; + } + Assert(!IsA(node, SubLink)); + return expression_tree_walker(node, find_window_functions_walker, + (void *) lists); +} + + +/***************************************************************************** + * Support for expressions returning sets + *****************************************************************************/ + +/* + * expression_returns_set_rows + * Estimate the number of rows returned by a set-returning expression. + * The result is 1 if it's not a set-returning expression. + * + * We should only examine the top-level function or operator; it used to be + * appropriate to recurse, but not anymore. (Even if there are more SRFs in + * the function's inputs, their multipliers are accounted for separately.) + * + * Note: keep this in sync with expression_returns_set() in nodes/nodeFuncs.c. + */ +double +expression_returns_set_rows(PlannerInfo *root, Node *clause) +{ + if (clause == NULL) + return 1.0; + if (IsA(clause, FuncExpr)) + { + FuncExpr *expr = (FuncExpr *) clause; + + if (expr->funcretset) + return clamp_row_est(get_function_rows(root, expr->funcid, clause)); + } + if (IsA(clause, OpExpr)) + { + OpExpr *expr = (OpExpr *) clause; + + if (expr->opretset) + { + set_opfuncid(expr); + return clamp_row_est(get_function_rows(root, expr->opfuncid, clause)); + } + } + return 1.0; +} + + +/***************************************************************************** + * Subplan clause manipulation + *****************************************************************************/ + +/* + * contain_subplans + * Recursively search for subplan nodes within a clause. + * + * If we see a SubLink node, we will return true. This is only possible if + * the expression tree hasn't yet been transformed by subselect.c. We do not + * know whether the node will produce a true subplan or just an initplan, + * but we make the conservative assumption that it will be a subplan. + * + * Returns true if any subplan found. + */ +bool +contain_subplans(Node *clause) +{ + return contain_subplans_walker(clause, NULL); +} + +static bool +contain_subplans_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + if (IsA(node, SubPlan) || + IsA(node, AlternativeSubPlan) || + IsA(node, SubLink)) + return true; /* abort the tree traversal and return true */ + return expression_tree_walker(node, contain_subplans_walker, context); +} + + +/***************************************************************************** + * Check clauses for mutable functions + *****************************************************************************/ + +/* + * contain_mutable_functions + * Recursively search for mutable functions within a clause. + * + * Returns true if any mutable function (or operator implemented by a + * mutable function) is found. This test is needed so that we don't + * mistakenly think that something like "WHERE random() < 0.5" can be treated + * as a constant qualification. + * + * We will recursively look into Query nodes (i.e., SubLink sub-selects) + * but not into SubPlans. See comments for contain_volatile_functions(). + */ +bool +contain_mutable_functions(Node *clause) +{ + return contain_mutable_functions_walker(clause, NULL); +} + +static bool +contain_mutable_functions_checker(Oid func_id, void *context) +{ + return (func_volatile(func_id) != PROVOLATILE_IMMUTABLE); +} + +static bool +contain_mutable_functions_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + /* Check for mutable functions in node itself */ + if (check_functions_in_node(node, contain_mutable_functions_checker, + context)) + return true; + + if (IsA(node, SQLValueFunction)) + { + /* all variants of SQLValueFunction are stable */ + return true; + } + + if (IsA(node, NextValueExpr)) + { + /* NextValueExpr is volatile */ + return true; + } + + /* + * It should be safe to treat MinMaxExpr as immutable, because it will + * depend on a non-cross-type btree comparison function, and those should + * always be immutable. Treating XmlExpr as immutable is more dubious, + * and treating CoerceToDomain as immutable is outright dangerous. But we + * have done so historically, and changing this would probably cause more + * problems than it would fix. In practice, if you have a non-immutable + * domain constraint you are in for pain anyhow. + */ + + /* Recurse to check arguments */ + if (IsA(node, Query)) + { + /* Recurse into subselects */ + return query_tree_walker((Query *) node, + contain_mutable_functions_walker, + context, 0); + } + return expression_tree_walker(node, contain_mutable_functions_walker, + context); +} + + +/***************************************************************************** + * Check clauses for volatile functions + *****************************************************************************/ + +/* + * contain_volatile_functions + * Recursively search for volatile functions within a clause. + * + * Returns true if any volatile function (or operator implemented by a + * volatile function) is found. This test prevents, for example, + * invalid conversions of volatile expressions into indexscan quals. + * + * We will recursively look into Query nodes (i.e., SubLink sub-selects) + * but not into SubPlans. This is a bit odd, but intentional. If we are + * looking at a SubLink, we are probably deciding whether a query tree + * transformation is safe, and a contained sub-select should affect that; + * for example, duplicating a sub-select containing a volatile function + * would be bad. However, once we've got to the stage of having SubPlans, + * subsequent planning need not consider volatility within those, since + * the executor won't change its evaluation rules for a SubPlan based on + * volatility. + * + * For some node types, for example, RestrictInfo and PathTarget, we cache + * whether we found any volatile functions or not and reuse that value in any + * future checks for that node. All of the logic for determining if the + * cached value should be set to VOLATILITY_NOVOLATILE or VOLATILITY_VOLATILE + * belongs in this function. Any code which makes changes to these nodes + * which could change the outcome this function must set the cached value back + * to VOLATILITY_UNKNOWN. That allows this function to redetermine the + * correct value during the next call, should we need to redetermine if the + * node contains any volatile functions again in the future. + */ +bool +contain_volatile_functions(Node *clause) +{ + return contain_volatile_functions_walker(clause, NULL); +} + +static bool +contain_volatile_functions_checker(Oid func_id, void *context) +{ + return (func_volatile(func_id) == PROVOLATILE_VOLATILE); +} + +static bool +contain_volatile_functions_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + /* Check for volatile functions in node itself */ + if (check_functions_in_node(node, contain_volatile_functions_checker, + context)) + return true; + + if (IsA(node, NextValueExpr)) + { + /* NextValueExpr is volatile */ + return true; + } + + if (IsA(node, RestrictInfo)) + { + RestrictInfo *rinfo = (RestrictInfo *) node; + + /* + * For RestrictInfo, check if we've checked the volatility of it + * before. If so, we can just use the cached value and not bother + * checking it again. Otherwise, check it and cache if whether we + * found any volatile functions. + */ + if (rinfo->has_volatile == VOLATILITY_NOVOLATILE) + return false; + else if (rinfo->has_volatile == VOLATILITY_VOLATILE) + return true; + else + { + bool hasvolatile; + + hasvolatile = contain_volatile_functions_walker((Node *) rinfo->clause, + context); + if (hasvolatile) + rinfo->has_volatile = VOLATILITY_VOLATILE; + else + rinfo->has_volatile = VOLATILITY_NOVOLATILE; + + return hasvolatile; + } + } + + if (IsA(node, PathTarget)) + { + PathTarget *target = (PathTarget *) node; + + /* + * We also do caching for PathTarget the same as we do above for + * RestrictInfos. + */ + if (target->has_volatile_expr == VOLATILITY_NOVOLATILE) + return false; + else if (target->has_volatile_expr == VOLATILITY_VOLATILE) + return true; + else + { + bool hasvolatile; + + hasvolatile = contain_volatile_functions_walker((Node *) target->exprs, + context); + + if (hasvolatile) + target->has_volatile_expr = VOLATILITY_VOLATILE; + else + target->has_volatile_expr = VOLATILITY_NOVOLATILE; + + return hasvolatile; + } + } + + /* + * See notes in contain_mutable_functions_walker about why we treat + * MinMaxExpr, XmlExpr, and CoerceToDomain as immutable, while + * SQLValueFunction is stable. Hence, none of them are of interest here. + */ + + /* Recurse to check arguments */ + if (IsA(node, Query)) + { + /* Recurse into subselects */ + return query_tree_walker((Query *) node, + contain_volatile_functions_walker, + context, 0); + } + return expression_tree_walker(node, contain_volatile_functions_walker, + context); +} + +/* + * Special purpose version of contain_volatile_functions() for use in COPY: + * ignore nextval(), but treat all other functions normally. + */ +bool +contain_volatile_functions_not_nextval(Node *clause) +{ + return contain_volatile_functions_not_nextval_walker(clause, NULL); +} + +static bool +contain_volatile_functions_not_nextval_checker(Oid func_id, void *context) +{ + return (func_id != F_NEXTVAL && + func_volatile(func_id) == PROVOLATILE_VOLATILE); +} + +static bool +contain_volatile_functions_not_nextval_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + /* Check for volatile functions in node itself */ + if (check_functions_in_node(node, + contain_volatile_functions_not_nextval_checker, + context)) + return true; + + /* + * See notes in contain_mutable_functions_walker about why we treat + * MinMaxExpr, XmlExpr, and CoerceToDomain as immutable, while + * SQLValueFunction is stable. Hence, none of them are of interest here. + * Also, since we're intentionally ignoring nextval(), presumably we + * should ignore NextValueExpr. + */ + + /* Recurse to check arguments */ + if (IsA(node, Query)) + { + /* Recurse into subselects */ + return query_tree_walker((Query *) node, + contain_volatile_functions_not_nextval_walker, + context, 0); + } + return expression_tree_walker(node, + contain_volatile_functions_not_nextval_walker, + context); +} + + +/***************************************************************************** + * Check queries for parallel unsafe and/or restricted constructs + *****************************************************************************/ + +/* + * max_parallel_hazard + * Find the worst parallel-hazard level in the given query + * + * Returns the worst function hazard property (the earliest in this list: + * PROPARALLEL_UNSAFE, PROPARALLEL_RESTRICTED, PROPARALLEL_SAFE) that can + * be found in the given parsetree. We use this to find out whether the query + * can be parallelized at all. The caller will also save the result in + * PlannerGlobal so as to short-circuit checks of portions of the querytree + * later, in the common case where everything is SAFE. + */ +char +max_parallel_hazard(Query *parse) +{ + max_parallel_hazard_context context; + + context.max_hazard = PROPARALLEL_SAFE; + context.max_interesting = PROPARALLEL_UNSAFE; + context.safe_param_ids = NIL; + (void) max_parallel_hazard_walker((Node *) parse, &context); + return context.max_hazard; +} + +/* + * is_parallel_safe + * Detect whether the given expr contains only parallel-safe functions + * + * root->glob->maxParallelHazard must previously have been set to the + * result of max_parallel_hazard() on the whole query. + */ +bool +is_parallel_safe(PlannerInfo *root, Node *node) +{ + max_parallel_hazard_context context; + PlannerInfo *proot; + ListCell *l; + + /* + * Even if the original querytree contained nothing unsafe, we need to + * search the expression if we have generated any PARAM_EXEC Params while + * planning, because those are parallel-restricted and there might be one + * in this expression. But otherwise we don't need to look. + */ + if (root->glob->maxParallelHazard == PROPARALLEL_SAFE && + root->glob->paramExecTypes == NIL) + return true; + /* Else use max_parallel_hazard's search logic, but stop on RESTRICTED */ + context.max_hazard = PROPARALLEL_SAFE; + context.max_interesting = PROPARALLEL_RESTRICTED; + context.safe_param_ids = NIL; + + /* + * The params that refer to the same or parent query level are considered + * parallel-safe. The idea is that we compute such params at Gather or + * Gather Merge node and pass their value to workers. + */ + for (proot = root; proot != NULL; proot = proot->parent_root) + { + foreach(l, proot->init_plans) + { + SubPlan *initsubplan = (SubPlan *) lfirst(l); + + context.safe_param_ids = list_concat(context.safe_param_ids, + initsubplan->setParam); + } + } + + return !max_parallel_hazard_walker(node, &context); +} + +/* core logic for all parallel-hazard checks */ +static bool +max_parallel_hazard_test(char proparallel, max_parallel_hazard_context *context) +{ + switch (proparallel) + { + case PROPARALLEL_SAFE: + /* nothing to see here, move along */ + break; + case PROPARALLEL_RESTRICTED: + /* increase max_hazard to RESTRICTED */ + Assert(context->max_hazard != PROPARALLEL_UNSAFE); + context->max_hazard = proparallel; + /* done if we are not expecting any unsafe functions */ + if (context->max_interesting == proparallel) + return true; + break; + case PROPARALLEL_UNSAFE: + context->max_hazard = proparallel; + /* we're always done at the first unsafe construct */ + return true; + default: + elog(ERROR, "unrecognized proparallel value \"%c\"", proparallel); + break; + } + return false; +} + +/* check_functions_in_node callback */ +static bool +max_parallel_hazard_checker(Oid func_id, void *context) +{ + return max_parallel_hazard_test(func_parallel(func_id), + (max_parallel_hazard_context *) context); +} + +static bool +max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context) +{ + if (node == NULL) + return false; + + /* Check for hazardous functions in node itself */ + if (check_functions_in_node(node, max_parallel_hazard_checker, + context)) + return true; + + /* + * It should be OK to treat MinMaxExpr as parallel-safe, since btree + * opclass support functions are generally parallel-safe. XmlExpr is a + * bit more dubious but we can probably get away with it. We err on the + * side of caution by treating CoerceToDomain as parallel-restricted. + * (Note: in principle that's wrong because a domain constraint could + * contain a parallel-unsafe function; but useful constraints probably + * never would have such, and assuming they do would cripple use of + * parallel query in the presence of domain types.) SQLValueFunction + * should be safe in all cases. NextValueExpr is parallel-unsafe. + */ + if (IsA(node, CoerceToDomain)) + { + if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context)) + return true; + } + + else if (IsA(node, NextValueExpr)) + { + if (max_parallel_hazard_test(PROPARALLEL_UNSAFE, context)) + return true; + } + + /* + * Treat window functions as parallel-restricted because we aren't sure + * whether the input row ordering is fully deterministic, and the output + * of window functions might vary across workers if not. (In some cases, + * like where the window frame orders by a primary key, we could relax + * this restriction. But it doesn't currently seem worth expending extra + * effort to do so.) + */ + else if (IsA(node, WindowFunc)) + { + if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context)) + return true; + } + + /* + * As a notational convenience for callers, look through RestrictInfo. + */ + else if (IsA(node, RestrictInfo)) + { + RestrictInfo *rinfo = (RestrictInfo *) node; + + return max_parallel_hazard_walker((Node *) rinfo->clause, context); + } + + /* + * Really we should not see SubLink during a max_interesting == restricted + * scan, but if we do, return true. + */ + else if (IsA(node, SubLink)) + { + if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context)) + return true; + } + + /* + * Only parallel-safe SubPlans can be sent to workers. Within the + * testexpr of the SubPlan, Params representing the output columns of the + * subplan can be treated as parallel-safe, so temporarily add their IDs + * to the safe_param_ids list while examining the testexpr. + */ + else if (IsA(node, SubPlan)) + { + SubPlan *subplan = (SubPlan *) node; + List *save_safe_param_ids; + + if (!subplan->parallel_safe && + max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context)) + return true; + save_safe_param_ids = context->safe_param_ids; + context->safe_param_ids = list_concat_copy(context->safe_param_ids, + subplan->paramIds); + if (max_parallel_hazard_walker(subplan->testexpr, context)) + return true; /* no need to restore safe_param_ids */ + list_free(context->safe_param_ids); + context->safe_param_ids = save_safe_param_ids; + /* we must also check args, but no special Param treatment there */ + if (max_parallel_hazard_walker((Node *) subplan->args, context)) + return true; + /* don't want to recurse normally, so we're done */ + return false; + } + + /* + * We can't pass Params to workers at the moment either, so they are also + * parallel-restricted, unless they are PARAM_EXTERN Params or are + * PARAM_EXEC Params listed in safe_param_ids, meaning they could be + * either generated within workers or can be computed by the leader and + * then their value can be passed to workers. + */ + else if (IsA(node, Param)) + { + Param *param = (Param *) node; + + if (param->paramkind == PARAM_EXTERN) + return false; + + if (param->paramkind != PARAM_EXEC || + !list_member_int(context->safe_param_ids, param->paramid)) + { + if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context)) + return true; + } + return false; /* nothing to recurse to */ + } + + /* + * When we're first invoked on a completely unplanned tree, we must + * recurse into subqueries so to as to locate parallel-unsafe constructs + * anywhere in the tree. + */ + else if (IsA(node, Query)) + { + Query *query = (Query *) node; + + /* SELECT FOR UPDATE/SHARE must be treated as unsafe */ + if (query->rowMarks != NULL) + { + context->max_hazard = PROPARALLEL_UNSAFE; + return true; + } + + /* Recurse into subselects */ + return query_tree_walker(query, + max_parallel_hazard_walker, + context, 0); + } + + /* Recurse to check arguments */ + return expression_tree_walker(node, + max_parallel_hazard_walker, + context); +} + + +/***************************************************************************** + * Check clauses for nonstrict functions + *****************************************************************************/ + +/* + * contain_nonstrict_functions + * Recursively search for nonstrict functions within a clause. + * + * Returns true if any nonstrict construct is found --- ie, anything that + * could produce non-NULL output with a NULL input. + * + * The idea here is that the caller has verified that the expression contains + * one or more Var or Param nodes (as appropriate for the caller's need), and + * now wishes to prove that the expression result will be NULL if any of these + * inputs is NULL. If we return false, then the proof succeeded. + */ +bool +contain_nonstrict_functions(Node *clause) +{ + return contain_nonstrict_functions_walker(clause, NULL); +} + +static bool +contain_nonstrict_functions_checker(Oid func_id, void *context) +{ + return !func_strict(func_id); +} + +static bool +contain_nonstrict_functions_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + if (IsA(node, Aggref)) + { + /* an aggregate could return non-null with null input */ + return true; + } + if (IsA(node, GroupingFunc)) + { + /* + * A GroupingFunc doesn't evaluate its arguments, and therefore must + * be treated as nonstrict. + */ + return true; + } + if (IsA(node, WindowFunc)) + { + /* a window function could return non-null with null input */ + return true; + } + if (IsA(node, SubscriptingRef)) + { + SubscriptingRef *sbsref = (SubscriptingRef *) node; + const SubscriptRoutines *sbsroutines; + + /* Subscripting assignment is always presumed nonstrict */ + if (sbsref->refassgnexpr != NULL) + return true; + /* Otherwise we must look up the subscripting support methods */ + sbsroutines = getSubscriptingRoutines(sbsref->refcontainertype, NULL); + if (!(sbsroutines && sbsroutines->fetch_strict)) + return true; + /* else fall through to check args */ + } + if (IsA(node, DistinctExpr)) + { + /* IS DISTINCT FROM is inherently non-strict */ + return true; + } + if (IsA(node, NullIfExpr)) + { + /* NULLIF is inherently non-strict */ + return true; + } + if (IsA(node, BoolExpr)) + { + BoolExpr *expr = (BoolExpr *) node; + + switch (expr->boolop) + { + case AND_EXPR: + case OR_EXPR: + /* AND, OR are inherently non-strict */ + return true; + default: + break; + } + } + if (IsA(node, SubLink)) + { + /* In some cases a sublink might be strict, but in general not */ + return true; + } + if (IsA(node, SubPlan)) + return true; + if (IsA(node, AlternativeSubPlan)) + return true; + if (IsA(node, FieldStore)) + return true; + if (IsA(node, CoerceViaIO)) + { + /* + * CoerceViaIO is strict regardless of whether the I/O functions are, + * so just go look at its argument; asking check_functions_in_node is + * useless expense and could deliver the wrong answer. + */ + return contain_nonstrict_functions_walker((Node *) ((CoerceViaIO *) node)->arg, + context); + } + if (IsA(node, ArrayCoerceExpr)) + { + /* + * ArrayCoerceExpr is strict at the array level, regardless of what + * the per-element expression is; so we should ignore elemexpr and + * recurse only into the arg. + */ + return contain_nonstrict_functions_walker((Node *) ((ArrayCoerceExpr *) node)->arg, + context); + } + if (IsA(node, CaseExpr)) + return true; + if (IsA(node, ArrayExpr)) + return true; + if (IsA(node, RowExpr)) + return true; + if (IsA(node, RowCompareExpr)) + return true; + if (IsA(node, CoalesceExpr)) + return true; + if (IsA(node, MinMaxExpr)) + return true; + if (IsA(node, XmlExpr)) + return true; + if (IsA(node, NullTest)) + return true; + if (IsA(node, BooleanTest)) + return true; + + /* Check other function-containing nodes */ + if (check_functions_in_node(node, contain_nonstrict_functions_checker, + context)) + return true; + + return expression_tree_walker(node, contain_nonstrict_functions_walker, + context); +} + +/***************************************************************************** + * Check clauses for Params + *****************************************************************************/ + +/* + * contain_exec_param + * Recursively search for PARAM_EXEC Params within a clause. + * + * Returns true if the clause contains any PARAM_EXEC Param with a paramid + * appearing in the given list of Param IDs. Does not descend into + * subqueries! + */ +bool +contain_exec_param(Node *clause, List *param_ids) +{ + return contain_exec_param_walker(clause, param_ids); +} + +static bool +contain_exec_param_walker(Node *node, List *param_ids) +{ + if (node == NULL) + return false; + if (IsA(node, Param)) + { + Param *p = (Param *) node; + + if (p->paramkind == PARAM_EXEC && + list_member_int(param_ids, p->paramid)) + return true; + } + return expression_tree_walker(node, contain_exec_param_walker, param_ids); +} + +/***************************************************************************** + * Check clauses for context-dependent nodes + *****************************************************************************/ + +/* + * contain_context_dependent_node + * Recursively search for context-dependent nodes within a clause. + * + * CaseTestExpr nodes must appear directly within the corresponding CaseExpr, + * not nested within another one, or they'll see the wrong test value. If one + * appears "bare" in the arguments of a SQL function, then we can't inline the + * SQL function for fear of creating such a situation. The same applies for + * CaseTestExpr used within the elemexpr of an ArrayCoerceExpr. + * + * CoerceToDomainValue would have the same issue if domain CHECK expressions + * could get inlined into larger expressions, but presently that's impossible. + * Still, it might be allowed in future, or other node types with similar + * issues might get invented. So give this function a generic name, and set + * up the recursion state to allow multiple flag bits. + */ +static bool +contain_context_dependent_node(Node *clause) +{ + int flags = 0; + + return contain_context_dependent_node_walker(clause, &flags); +} + +#define CCDN_CASETESTEXPR_OK 0x0001 /* CaseTestExpr okay here? */ + +static bool +contain_context_dependent_node_walker(Node *node, int *flags) +{ + if (node == NULL) + return false; + if (IsA(node, CaseTestExpr)) + return !(*flags & CCDN_CASETESTEXPR_OK); + else if (IsA(node, CaseExpr)) + { + CaseExpr *caseexpr = (CaseExpr *) node; + + /* + * If this CASE doesn't have a test expression, then it doesn't create + * a context in which CaseTestExprs should appear, so just fall + * through and treat it as a generic expression node. + */ + if (caseexpr->arg) + { + int save_flags = *flags; + bool res; + + /* + * Note: in principle, we could distinguish the various sub-parts + * of a CASE construct and set the flag bit only for some of them, + * since we are only expecting CaseTestExprs to appear in the + * "expr" subtree of the CaseWhen nodes. But it doesn't really + * seem worth any extra code. If there are any bare CaseTestExprs + * elsewhere in the CASE, something's wrong already. + */ + *flags |= CCDN_CASETESTEXPR_OK; + res = expression_tree_walker(node, + contain_context_dependent_node_walker, + (void *) flags); + *flags = save_flags; + return res; + } + } + else if (IsA(node, ArrayCoerceExpr)) + { + ArrayCoerceExpr *ac = (ArrayCoerceExpr *) node; + int save_flags; + bool res; + + /* Check the array expression */ + if (contain_context_dependent_node_walker((Node *) ac->arg, flags)) + return true; + + /* Check the elemexpr, which is allowed to contain CaseTestExpr */ + save_flags = *flags; + *flags |= CCDN_CASETESTEXPR_OK; + res = contain_context_dependent_node_walker((Node *) ac->elemexpr, + flags); + *flags = save_flags; + return res; + } + return expression_tree_walker(node, contain_context_dependent_node_walker, + (void *) flags); +} + +/***************************************************************************** + * Check clauses for Vars passed to non-leakproof functions + *****************************************************************************/ + +/* + * contain_leaked_vars + * Recursively scan a clause to discover whether it contains any Var + * nodes (of the current query level) that are passed as arguments to + * leaky functions. + * + * Returns true if the clause contains any non-leakproof functions that are + * passed Var nodes of the current query level, and which might therefore leak + * data. Such clauses must be applied after any lower-level security barrier + * clauses. + */ +bool +contain_leaked_vars(Node *clause) +{ + return contain_leaked_vars_walker(clause, NULL); +} + +static bool +contain_leaked_vars_checker(Oid func_id, void *context) +{ + return !get_func_leakproof(func_id); +} + +static bool +contain_leaked_vars_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_FieldSelect: + case T_FieldStore: + case T_NamedArgExpr: + case T_BoolExpr: + case T_RelabelType: + case T_CollateExpr: + case T_CaseExpr: + case T_CaseTestExpr: + case T_RowExpr: + case T_SQLValueFunction: + case T_NullTest: + case T_BooleanTest: + case T_NextValueExpr: + 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: + case T_OpExpr: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + + /* + * If node contains a leaky function call, and there's any Var + * underneath it, reject. + */ + if (check_functions_in_node(node, contain_leaked_vars_checker, + context) && + contain_var_clause(node)) + return true; + break; + + case T_SubscriptingRef: + { + SubscriptingRef *sbsref = (SubscriptingRef *) node; + const SubscriptRoutines *sbsroutines; + + /* Consult the subscripting support method info */ + sbsroutines = getSubscriptingRoutines(sbsref->refcontainertype, + NULL); + if (!sbsroutines || + !(sbsref->refassgnexpr != NULL ? + sbsroutines->store_leakproof : + sbsroutines->fetch_leakproof)) + { + /* Node is leaky, so reject if it contains Vars */ + if (contain_var_clause(node)) + return true; + } + } + break; + + case T_RowCompareExpr: + { + /* + * It's worth special-casing this because a leaky comparison + * function only compromises one pair of row elements, which + * might not contain Vars while others do. + */ + RowCompareExpr *rcexpr = (RowCompareExpr *) node; + ListCell *opid; + ListCell *larg; + ListCell *rarg; + + forthree(opid, rcexpr->opnos, + larg, rcexpr->largs, + rarg, rcexpr->rargs) + { + Oid funcid = get_opcode(lfirst_oid(opid)); + + if (!get_func_leakproof(funcid) && + (contain_var_clause((Node *) lfirst(larg)) || + contain_var_clause((Node *) lfirst(rarg)))) + return true; + } + } + break; + + case T_MinMaxExpr: + { + /* + * MinMaxExpr is leakproof if the comparison function it calls + * is leakproof. + */ + MinMaxExpr *minmaxexpr = (MinMaxExpr *) node; + TypeCacheEntry *typentry; + bool leakproof; + + /* Look up the btree comparison function for the datatype */ + typentry = lookup_type_cache(minmaxexpr->minmaxtype, + TYPECACHE_CMP_PROC); + if (OidIsValid(typentry->cmp_proc)) + leakproof = get_func_leakproof(typentry->cmp_proc); + else + { + /* + * The executor will throw an error, but here we just + * treat the missing function as leaky. + */ + leakproof = false; + } + + if (!leakproof && + contain_var_clause((Node *) minmaxexpr->args)) + return true; + } + break; + + case T_CurrentOfExpr: + + /* + * WHERE CURRENT OF doesn't contain leaky function calls. + * Moreover, it is essential that this is considered non-leaky, + * since the planner must always generate a TID scan when CURRENT + * OF is present -- cf. cost_tidscan. + */ + return false; + + 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_leaked_vars_walker, + context); +} + +/* + * find_nonnullable_rels + * Determine which base rels are forced nonnullable by given clause. + * + * Returns the set of all Relids that are referenced in the clause in such + * a way that the clause cannot possibly return TRUE if any of these Relids + * is an all-NULL row. (It is OK to err on the side of conservatism; hence + * the analysis here is simplistic.) + * + * The semantics here are subtly different from contain_nonstrict_functions: + * that function is concerned with NULL results from arbitrary expressions, + * but here we assume that the input is a Boolean expression, and wish to + * see if NULL inputs will provably cause a FALSE-or-NULL result. We expect + * the expression to have been AND/OR flattened and converted to implicit-AND + * format. + * + * Note: this function is largely duplicative of find_nonnullable_vars(). + * The reason not to simplify this function into a thin wrapper around + * find_nonnullable_vars() is that the tested conditions really are different: + * a clause like "t1.v1 IS NOT NULL OR t1.v2 IS NOT NULL" does not prove + * that either v1 or v2 can't be NULL, but it does prove that the t1 row + * as a whole can't be all-NULL. Also, the behavior for PHVs is different. + * + * top_level is true while scanning top-level AND/OR structure; here, showing + * the result is either FALSE or NULL is good enough. top_level is false when + * we have descended below a NOT or a strict function: now we must be able to + * prove that the subexpression goes to NULL. + * + * We don't use expression_tree_walker here because we don't want to descend + * through very many kinds of nodes; only the ones we can be sure are strict. + */ +Relids +find_nonnullable_rels(Node *clause) +{ + return find_nonnullable_rels_walker(clause, true); +} + +static Relids +find_nonnullable_rels_walker(Node *node, bool top_level) +{ + Relids result = NULL; + ListCell *l; + + if (node == NULL) + return NULL; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + if (var->varlevelsup == 0) + result = bms_make_singleton(var->varno); + } + else if (IsA(node, List)) + { + /* + * At top level, we are examining an implicit-AND list: if any of the + * arms produces FALSE-or-NULL then the result is FALSE-or-NULL. If + * not at top level, we are examining the arguments of a strict + * function: if any of them produce NULL then the result of the + * function must be NULL. So in both cases, the set of nonnullable + * rels is the union of those found in the arms, and we pass down the + * top_level flag unmodified. + */ + foreach(l, (List *) node) + { + result = bms_join(result, + find_nonnullable_rels_walker(lfirst(l), + top_level)); + } + } + else if (IsA(node, FuncExpr)) + { + FuncExpr *expr = (FuncExpr *) node; + + if (func_strict(expr->funcid)) + result = find_nonnullable_rels_walker((Node *) expr->args, false); + } + else if (IsA(node, OpExpr)) + { + OpExpr *expr = (OpExpr *) node; + + set_opfuncid(expr); + if (func_strict(expr->opfuncid)) + result = find_nonnullable_rels_walker((Node *) expr->args, false); + } + else if (IsA(node, ScalarArrayOpExpr)) + { + ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; + + if (is_strict_saop(expr, true)) + result = find_nonnullable_rels_walker((Node *) expr->args, false); + } + else if (IsA(node, BoolExpr)) + { + BoolExpr *expr = (BoolExpr *) node; + + switch (expr->boolop) + { + case AND_EXPR: + /* At top level we can just recurse (to the List case) */ + if (top_level) + { + result = find_nonnullable_rels_walker((Node *) expr->args, + top_level); + break; + } + + /* + * Below top level, even if one arm produces NULL, the result + * could be FALSE (hence not NULL). However, if *all* the + * arms produce NULL then the result is NULL, so we can take + * the intersection of the sets of nonnullable rels, just as + * for OR. Fall through to share code. + */ + /* FALL THRU */ + case OR_EXPR: + + /* + * OR is strict if all of its arms are, so we can take the + * intersection of the sets of nonnullable rels for each arm. + * This works for both values of top_level. + */ + foreach(l, expr->args) + { + Relids subresult; + + subresult = find_nonnullable_rels_walker(lfirst(l), + top_level); + if (result == NULL) /* first subresult? */ + result = subresult; + else + result = bms_int_members(result, subresult); + + /* + * If the intersection is empty, we can stop looking. This + * also justifies the test for first-subresult above. + */ + if (bms_is_empty(result)) + break; + } + break; + case NOT_EXPR: + /* NOT will return null if its arg is null */ + result = find_nonnullable_rels_walker((Node *) expr->args, + false); + break; + default: + elog(ERROR, "unrecognized boolop: %d", (int) expr->boolop); + break; + } + } + else if (IsA(node, RelabelType)) + { + RelabelType *expr = (RelabelType *) node; + + result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); + } + else if (IsA(node, CoerceViaIO)) + { + /* not clear this is useful, but it can't hurt */ + CoerceViaIO *expr = (CoerceViaIO *) node; + + result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); + } + else if (IsA(node, ArrayCoerceExpr)) + { + /* ArrayCoerceExpr is strict at the array level; ignore elemexpr */ + ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; + + result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); + } + else if (IsA(node, ConvertRowtypeExpr)) + { + /* not clear this is useful, but it can't hurt */ + ConvertRowtypeExpr *expr = (ConvertRowtypeExpr *) node; + + result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); + } + else if (IsA(node, CollateExpr)) + { + CollateExpr *expr = (CollateExpr *) node; + + result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); + } + else if (IsA(node, NullTest)) + { + /* IS NOT NULL can be considered strict, but only at top level */ + NullTest *expr = (NullTest *) node; + + if (top_level && expr->nulltesttype == IS_NOT_NULL && !expr->argisrow) + result = find_nonnullable_rels_walker((Node *) expr->arg, false); + } + else if (IsA(node, BooleanTest)) + { + /* Boolean tests that reject NULL are strict at top level */ + BooleanTest *expr = (BooleanTest *) node; + + if (top_level && + (expr->booltesttype == IS_TRUE || + expr->booltesttype == IS_FALSE || + expr->booltesttype == IS_NOT_UNKNOWN)) + result = find_nonnullable_rels_walker((Node *) expr->arg, false); + } + else if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + /* + * If the contained expression forces any rels non-nullable, so does + * the PHV. + */ + result = find_nonnullable_rels_walker((Node *) phv->phexpr, top_level); + + /* + * If the PHV's syntactic scope is exactly one rel, it will be forced + * to be evaluated at that rel, and so it will behave like a Var of + * that rel: if the rel's entire output goes to null, so will the PHV. + * (If the syntactic scope is a join, we know that the PHV will go to + * null if the whole join does; but that is AND semantics while we + * need OR semantics for find_nonnullable_rels' result, so we can't do + * anything with the knowledge.) + */ + if (phv->phlevelsup == 0 && + bms_membership(phv->phrels) == BMS_SINGLETON) + result = bms_add_members(result, phv->phrels); + } + return result; +} + +/* + * find_nonnullable_vars + * Determine which Vars are forced nonnullable by given clause. + * + * Returns a list of all level-zero Vars that are referenced in the clause in + * such a way that the clause cannot possibly return TRUE if any of these Vars + * is NULL. (It is OK to err on the side of conservatism; hence the analysis + * here is simplistic.) + * + * The semantics here are subtly different from contain_nonstrict_functions: + * that function is concerned with NULL results from arbitrary expressions, + * but here we assume that the input is a Boolean expression, and wish to + * see if NULL inputs will provably cause a FALSE-or-NULL result. We expect + * the expression to have been AND/OR flattened and converted to implicit-AND + * format. + * + * The result is a palloc'd List, but we have not copied the member Var nodes. + * Also, we don't bother trying to eliminate duplicate entries. + * + * top_level is true while scanning top-level AND/OR structure; here, showing + * the result is either FALSE or NULL is good enough. top_level is false when + * we have descended below a NOT or a strict function: now we must be able to + * prove that the subexpression goes to NULL. + * + * We don't use expression_tree_walker here because we don't want to descend + * through very many kinds of nodes; only the ones we can be sure are strict. + */ +List * +find_nonnullable_vars(Node *clause) +{ + return find_nonnullable_vars_walker(clause, true); +} + +static List * +find_nonnullable_vars_walker(Node *node, bool top_level) +{ + List *result = NIL; + ListCell *l; + + if (node == NULL) + return NIL; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + if (var->varlevelsup == 0) + result = list_make1(var); + } + else if (IsA(node, List)) + { + /* + * At top level, we are examining an implicit-AND list: if any of the + * arms produces FALSE-or-NULL then the result is FALSE-or-NULL. If + * not at top level, we are examining the arguments of a strict + * function: if any of them produce NULL then the result of the + * function must be NULL. So in both cases, the set of nonnullable + * vars is the union of those found in the arms, and we pass down the + * top_level flag unmodified. + */ + foreach(l, (List *) node) + { + result = list_concat(result, + find_nonnullable_vars_walker(lfirst(l), + top_level)); + } + } + else if (IsA(node, FuncExpr)) + { + FuncExpr *expr = (FuncExpr *) node; + + if (func_strict(expr->funcid)) + result = find_nonnullable_vars_walker((Node *) expr->args, false); + } + else if (IsA(node, OpExpr)) + { + OpExpr *expr = (OpExpr *) node; + + set_opfuncid(expr); + if (func_strict(expr->opfuncid)) + result = find_nonnullable_vars_walker((Node *) expr->args, false); + } + else if (IsA(node, ScalarArrayOpExpr)) + { + ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; + + if (is_strict_saop(expr, true)) + result = find_nonnullable_vars_walker((Node *) expr->args, false); + } + else if (IsA(node, BoolExpr)) + { + BoolExpr *expr = (BoolExpr *) node; + + switch (expr->boolop) + { + case AND_EXPR: + /* At top level we can just recurse (to the List case) */ + if (top_level) + { + result = find_nonnullable_vars_walker((Node *) expr->args, + top_level); + break; + } + + /* + * Below top level, even if one arm produces NULL, the result + * could be FALSE (hence not NULL). However, if *all* the + * arms produce NULL then the result is NULL, so we can take + * the intersection of the sets of nonnullable vars, just as + * for OR. Fall through to share code. + */ + /* FALL THRU */ + case OR_EXPR: + + /* + * OR is strict if all of its arms are, so we can take the + * intersection of the sets of nonnullable vars for each arm. + * This works for both values of top_level. + */ + foreach(l, expr->args) + { + List *subresult; + + subresult = find_nonnullable_vars_walker(lfirst(l), + top_level); + if (result == NIL) /* first subresult? */ + result = subresult; + else + result = list_intersection(result, subresult); + + /* + * If the intersection is empty, we can stop looking. This + * also justifies the test for first-subresult above. + */ + if (result == NIL) + break; + } + break; + case NOT_EXPR: + /* NOT will return null if its arg is null */ + result = find_nonnullable_vars_walker((Node *) expr->args, + false); + break; + default: + elog(ERROR, "unrecognized boolop: %d", (int) expr->boolop); + break; + } + } + else if (IsA(node, RelabelType)) + { + RelabelType *expr = (RelabelType *) node; + + result = find_nonnullable_vars_walker((Node *) expr->arg, top_level); + } + else if (IsA(node, CoerceViaIO)) + { + /* not clear this is useful, but it can't hurt */ + CoerceViaIO *expr = (CoerceViaIO *) node; + + result = find_nonnullable_vars_walker((Node *) expr->arg, false); + } + else if (IsA(node, ArrayCoerceExpr)) + { + /* ArrayCoerceExpr is strict at the array level; ignore elemexpr */ + ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; + + result = find_nonnullable_vars_walker((Node *) expr->arg, top_level); + } + else if (IsA(node, ConvertRowtypeExpr)) + { + /* not clear this is useful, but it can't hurt */ + ConvertRowtypeExpr *expr = (ConvertRowtypeExpr *) node; + + result = find_nonnullable_vars_walker((Node *) expr->arg, top_level); + } + else if (IsA(node, CollateExpr)) + { + CollateExpr *expr = (CollateExpr *) node; + + result = find_nonnullable_vars_walker((Node *) expr->arg, top_level); + } + else if (IsA(node, NullTest)) + { + /* IS NOT NULL can be considered strict, but only at top level */ + NullTest *expr = (NullTest *) node; + + if (top_level && expr->nulltesttype == IS_NOT_NULL && !expr->argisrow) + result = find_nonnullable_vars_walker((Node *) expr->arg, false); + } + else if (IsA(node, BooleanTest)) + { + /* Boolean tests that reject NULL are strict at top level */ + BooleanTest *expr = (BooleanTest *) node; + + if (top_level && + (expr->booltesttype == IS_TRUE || + expr->booltesttype == IS_FALSE || + expr->booltesttype == IS_NOT_UNKNOWN)) + result = find_nonnullable_vars_walker((Node *) expr->arg, false); + } + else if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + result = find_nonnullable_vars_walker((Node *) phv->phexpr, top_level); + } + return result; +} + +/* + * find_forced_null_vars + * Determine which Vars must be NULL for the given clause to return TRUE. + * + * This is the complement of find_nonnullable_vars: find the level-zero Vars + * that must be NULL for the clause to return TRUE. (It is OK to err on the + * side of conservatism; hence the analysis here is simplistic. In fact, + * we only detect simple "var IS NULL" tests at the top level.) + * + * The result is a palloc'd List, but we have not copied the member Var nodes. + * Also, we don't bother trying to eliminate duplicate entries. + */ +List * +find_forced_null_vars(Node *node) +{ + List *result = NIL; + Var *var; + ListCell *l; + + if (node == NULL) + return NIL; + /* Check single-clause cases using subroutine */ + var = find_forced_null_var(node); + if (var) + { + result = list_make1(var); + } + /* Otherwise, handle AND-conditions */ + else if (IsA(node, List)) + { + /* + * At top level, we are examining an implicit-AND list: if any of the + * arms produces FALSE-or-NULL then the result is FALSE-or-NULL. + */ + foreach(l, (List *) node) + { + result = list_concat(result, + find_forced_null_vars(lfirst(l))); + } + } + else if (IsA(node, BoolExpr)) + { + BoolExpr *expr = (BoolExpr *) node; + + /* + * We don't bother considering the OR case, because it's fairly + * unlikely anyone would write "v1 IS NULL OR v1 IS NULL". Likewise, + * the NOT case isn't worth expending code on. + */ + if (expr->boolop == AND_EXPR) + { + /* At top level we can just recurse (to the List case) */ + result = find_forced_null_vars((Node *) expr->args); + } + } + return result; +} + +/* + * find_forced_null_var + * Return the Var forced null by the given clause, or NULL if it's + * not an IS NULL-type clause. For success, the clause must enforce + * *only* nullness of the particular Var, not any other conditions. + * + * This is just the single-clause case of find_forced_null_vars(), without + * any allowance for AND conditions. It's used by initsplan.c on individual + * qual clauses. The reason for not just applying find_forced_null_vars() + * is that if an AND of an IS NULL clause with something else were to somehow + * survive AND/OR flattening, initsplan.c might get fooled into discarding + * the whole clause when only the IS NULL part of it had been proved redundant. + */ +Var * +find_forced_null_var(Node *node) +{ + if (node == NULL) + return NULL; + if (IsA(node, NullTest)) + { + /* check for var IS NULL */ + NullTest *expr = (NullTest *) node; + + if (expr->nulltesttype == IS_NULL && !expr->argisrow) + { + Var *var = (Var *) expr->arg; + + if (var && IsA(var, Var) && + var->varlevelsup == 0) + return var; + } + } + else if (IsA(node, BooleanTest)) + { + /* var IS UNKNOWN is equivalent to var IS NULL */ + BooleanTest *expr = (BooleanTest *) node; + + if (expr->booltesttype == IS_UNKNOWN) + { + Var *var = (Var *) expr->arg; + + if (var && IsA(var, Var) && + var->varlevelsup == 0) + return var; + } + } + return NULL; +} + +/* + * Can we treat a ScalarArrayOpExpr as strict? + * + * If "falseOK" is true, then a "false" result can be considered strict, + * else we need to guarantee an actual NULL result for NULL input. + * + * "foo op ALL array" is strict if the op is strict *and* we can prove + * that the array input isn't an empty array. We can check that + * for the cases of an array constant and an ARRAY[] construct. + * + * "foo op ANY array" is strict in the falseOK sense if the op is strict. + * If not falseOK, the test is the same as for "foo op ALL array". + */ +static bool +is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK) +{ + Node *rightop; + + /* The contained operator must be strict. */ + set_sa_opfuncid(expr); + if (!func_strict(expr->opfuncid)) + return false; + /* If ANY and falseOK, that's all we need to check. */ + if (expr->useOr && falseOK) + return true; + /* Else, we have to see if the array is provably non-empty. */ + Assert(list_length(expr->args) == 2); + rightop = (Node *) lsecond(expr->args); + if (rightop && IsA(rightop, Const)) + { + Datum arraydatum = ((Const *) rightop)->constvalue; + bool arrayisnull = ((Const *) rightop)->constisnull; + ArrayType *arrayval; + int nitems; + + if (arrayisnull) + return false; + arrayval = DatumGetArrayTypeP(arraydatum); + nitems = ArrayGetNItems(ARR_NDIM(arrayval), ARR_DIMS(arrayval)); + if (nitems > 0) + return true; + } + else if (rightop && IsA(rightop, ArrayExpr)) + { + ArrayExpr *arrayexpr = (ArrayExpr *) rightop; + + if (arrayexpr->elements != NIL && !arrayexpr->multidims) + return true; + } + return false; +} + + +/***************************************************************************** + * Check for "pseudo-constant" clauses + *****************************************************************************/ + +/* + * is_pseudo_constant_clause + * Detect whether an expression is "pseudo constant", ie, it contains no + * variables of the current query level and no uses of volatile functions. + * Such an expr is not necessarily a true constant: it can still contain + * Params and outer-level Vars, not to mention functions whose results + * may vary from one statement to the next. However, the expr's value + * will be constant over any one scan of the current query, so it can be + * used as, eg, an indexscan key. (Actually, the condition for indexscan + * keys is weaker than this; see is_pseudo_constant_for_index().) + * + * CAUTION: this function omits to test for one very important class of + * not-constant expressions, namely aggregates (Aggrefs). In current usage + * this is only applied to WHERE clauses and so a check for Aggrefs would be + * a waste of cycles; but be sure to also check contain_agg_clause() if you + * want to know about pseudo-constness in other contexts. The same goes + * for window functions (WindowFuncs). + */ +bool +is_pseudo_constant_clause(Node *clause) +{ + /* + * We could implement this check in one recursive scan. But since the + * check for volatile functions is both moderately expensive and unlikely + * to fail, it seems better to look for Vars first and only check for + * volatile functions if we find no Vars. + */ + if (!contain_var_clause(clause) && + !contain_volatile_functions(clause)) + return true; + return false; +} + +/* + * is_pseudo_constant_clause_relids + * Same as above, except caller already has available the var membership + * of the expression; this lets us avoid the contain_var_clause() scan. + */ +bool +is_pseudo_constant_clause_relids(Node *clause, Relids relids) +{ + if (bms_is_empty(relids) && + !contain_volatile_functions(clause)) + return true; + return false; +} + + +/***************************************************************************** + * * + * General clause-manipulating routines * + * * + *****************************************************************************/ + +/* + * NumRelids + * (formerly clause_relids) + * + * Returns the number of different relations referenced in 'clause'. + */ +int +NumRelids(PlannerInfo *root, Node *clause) +{ + Relids varnos = pull_varnos(root, clause); + int result = bms_num_members(varnos); + + bms_free(varnos); + return result; +} + +/* + * CommuteOpExpr: commute a binary operator clause + * + * XXX the clause is destructively modified! + */ +void +CommuteOpExpr(OpExpr *clause) +{ + Oid opoid; + Node *temp; + + /* Sanity checks: caller is at fault if these fail */ + if (!is_opclause(clause) || + list_length(clause->args) != 2) + elog(ERROR, "cannot commute non-binary-operator clause"); + + opoid = get_commutator(clause->opno); + + if (!OidIsValid(opoid)) + elog(ERROR, "could not find commutator for operator %u", + clause->opno); + + /* + * modify the clause in-place! + */ + clause->opno = opoid; + clause->opfuncid = InvalidOid; + /* opresulttype, opretset, opcollid, inputcollid need not change */ + + temp = linitial(clause->args); + linitial(clause->args) = lsecond(clause->args); + lsecond(clause->args) = temp; +} + +/* + * Helper for eval_const_expressions: check that datatype of an attribute + * is still what it was when the expression was parsed. This is needed to + * guard against improper simplification after ALTER COLUMN TYPE. (XXX we + * may well need to make similar checks elsewhere?) + * + * rowtypeid may come from a whole-row Var, and therefore it can be a domain + * over composite, but for this purpose we only care about checking the type + * of a contained field. + */ +static bool +rowtype_field_matches(Oid rowtypeid, int fieldnum, + Oid expectedtype, int32 expectedtypmod, + Oid expectedcollation) +{ + TupleDesc tupdesc; + Form_pg_attribute attr; + + /* No issue for RECORD, since there is no way to ALTER such a type */ + if (rowtypeid == RECORDOID) + return true; + tupdesc = lookup_rowtype_tupdesc_domain(rowtypeid, -1, false); + if (fieldnum <= 0 || fieldnum > tupdesc->natts) + { + ReleaseTupleDesc(tupdesc); + return false; + } + attr = TupleDescAttr(tupdesc, fieldnum - 1); + if (attr->attisdropped || + attr->atttypid != expectedtype || + attr->atttypmod != expectedtypmod || + attr->attcollation != expectedcollation) + { + ReleaseTupleDesc(tupdesc); + return false; + } + ReleaseTupleDesc(tupdesc); + return true; +} + + +/*-------------------- + * eval_const_expressions + * + * Reduce any recognizably constant subexpressions of the given + * expression tree, for example "2 + 2" => "4". More interestingly, + * we can reduce certain boolean expressions even when they contain + * non-constant subexpressions: "x OR true" => "true" no matter what + * the subexpression x is. (XXX We assume that no such subexpression + * will have important side-effects, which is not necessarily a good + * assumption in the presence of user-defined functions; do we need a + * pg_proc flag that prevents discarding the execution of a function?) + * + * We do understand that certain functions may deliver non-constant + * results even with constant inputs, "nextval()" being the classic + * example. Functions that are not marked "immutable" in pg_proc + * will not be pre-evaluated here, although we will reduce their + * arguments as far as possible. + * + * Whenever a function is eliminated from the expression by means of + * constant-expression evaluation or inlining, we add the function to + * root->glob->invalItems. This ensures the plan is known to depend on + * such functions, even though they aren't referenced anymore. + * + * We assume that the tree has already been type-checked and contains + * only operators and functions that are reasonable to try to execute. + * + * NOTE: "root" can be passed as NULL if the caller never wants to do any + * Param substitutions nor receive info about inlined functions. + * + * NOTE: the planner assumes that this will always flatten nested AND and + * OR clauses into N-argument form. See comments in prepqual.c. + * + * NOTE: another critical effect is that any function calls that require + * default arguments will be expanded, and named-argument calls will be + * converted to positional notation. The executor won't handle either. + *-------------------- + */ +Node * +eval_const_expressions(PlannerInfo *root, Node *node) +{ + eval_const_expressions_context context; + + if (root) + context.boundParams = root->glob->boundParams; /* bound Params */ + else + context.boundParams = NULL; + context.root = root; /* for inlined-function dependencies */ + context.active_fns = NIL; /* nothing being recursively simplified */ + context.case_val = NULL; /* no CASE being examined */ + context.estimate = false; /* safe transformations only */ + return eval_const_expressions_mutator(node, &context); +} + +#define MIN_ARRAY_SIZE_FOR_HASHED_SAOP 9 +/*-------------------- + * convert_saop_to_hashed_saop + * + * Recursively search 'node' for ScalarArrayOpExprs and fill in the hash + * function for any ScalarArrayOpExpr that looks like it would be useful to + * evaluate using a hash table rather than a linear search. + * + * We'll use a hash table if all of the following conditions are met: + * 1. The 2nd argument of the array contain only Consts. + * 2. useOr is true or there is a valid negator operator for the + * ScalarArrayOpExpr's opno. + * 3. There's valid hash function for both left and righthand operands and + * these hash functions are the same. + * 4. If the array contains enough elements for us to consider it to be + * worthwhile using a hash table rather than a linear search. + */ +void +convert_saop_to_hashed_saop(Node *node) +{ + (void) convert_saop_to_hashed_saop_walker(node, NULL); +} + +static bool +convert_saop_to_hashed_saop_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + + if (IsA(node, ScalarArrayOpExpr)) + { + ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node; + Expr *arrayarg = (Expr *) lsecond(saop->args); + Oid lefthashfunc; + Oid righthashfunc; + + if (arrayarg && IsA(arrayarg, Const) && + !((Const *) arrayarg)->constisnull) + { + if (saop->useOr) + { + if (get_op_hash_functions(saop->opno, &lefthashfunc, &righthashfunc) && + lefthashfunc == righthashfunc) + { + Datum arrdatum = ((Const *) arrayarg)->constvalue; + ArrayType *arr = (ArrayType *) DatumGetPointer(arrdatum); + int nitems; + + /* + * Only fill in the hash functions if the array looks + * large enough for it to be worth hashing instead of + * doing a linear search. + */ + nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)); + + if (nitems >= MIN_ARRAY_SIZE_FOR_HASHED_SAOP) + { + /* Looks good. Fill in the hash functions */ + saop->hashfuncid = lefthashfunc; + } + return true; + } + } + else /* !saop->useOr */ + { + Oid negator = get_negator(saop->opno); + + /* + * Check if this is a NOT IN using an operator whose negator + * is hashable. If so we can still build a hash table and + * just ensure the lookup items are not in the hash table. + */ + if (OidIsValid(negator) && + get_op_hash_functions(negator, &lefthashfunc, &righthashfunc) && + lefthashfunc == righthashfunc) + { + Datum arrdatum = ((Const *) arrayarg)->constvalue; + ArrayType *arr = (ArrayType *) DatumGetPointer(arrdatum); + int nitems; + + /* + * Only fill in the hash functions if the array looks + * large enough for it to be worth hashing instead of + * doing a linear search. + */ + nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)); + + if (nitems >= MIN_ARRAY_SIZE_FOR_HASHED_SAOP) + { + /* Looks good. Fill in the hash functions */ + saop->hashfuncid = lefthashfunc; + + /* + * Also set the negfuncid. The executor will need + * that to perform hashtable lookups. + */ + saop->negfuncid = get_opcode(negator); + } + return true; + } + } + } + } + + return expression_tree_walker(node, convert_saop_to_hashed_saop_walker, NULL); +} + + +/*-------------------- + * estimate_expression_value + * + * This function attempts to estimate the value of an expression for + * planning purposes. It is in essence a more aggressive version of + * eval_const_expressions(): we will perform constant reductions that are + * not necessarily 100% safe, but are reasonable for estimation purposes. + * + * Currently the extra steps that are taken in this mode are: + * 1. Substitute values for Params, where a bound Param value has been made + * available by the caller of planner(), even if the Param isn't marked + * constant. This effectively means that we plan using the first supplied + * value of the Param. + * 2. Fold stable, as well as immutable, functions to constants. + * 3. Reduce PlaceHolderVar nodes to their contained expressions. + *-------------------- + */ +Node * +estimate_expression_value(PlannerInfo *root, Node *node) +{ + eval_const_expressions_context context; + + context.boundParams = root->glob->boundParams; /* bound Params */ + /* we do not need to mark the plan as depending on inlined functions */ + context.root = NULL; + context.active_fns = NIL; /* nothing being recursively simplified */ + context.case_val = NULL; /* no CASE being examined */ + context.estimate = true; /* unsafe transformations OK */ + return eval_const_expressions_mutator(node, &context); +} + +/* + * The generic case in eval_const_expressions_mutator is to recurse using + * expression_tree_mutator, which will copy the given node unchanged but + * const-simplify its arguments (if any) as far as possible. If the node + * itself does immutable processing, and each of its arguments were reduced + * to a Const, we can then reduce it to a Const using evaluate_expr. (Some + * node types need more complicated logic; for example, a CASE expression + * might be reducible to a constant even if not all its subtrees are.) + */ +#define ece_generic_processing(node) \ + expression_tree_mutator((Node *) (node), eval_const_expressions_mutator, \ + (void *) context) + +/* + * Check whether all arguments of the given node were reduced to Consts. + * By going directly to expression_tree_walker, contain_non_const_walker + * is not applied to the node itself, only to its children. + */ +#define ece_all_arguments_const(node) \ + (!expression_tree_walker((Node *) (node), contain_non_const_walker, NULL)) + +/* Generic macro for applying evaluate_expr */ +#define ece_evaluate_expr(node) \ + ((Node *) evaluate_expr((Expr *) (node), \ + exprType((Node *) (node)), \ + exprTypmod((Node *) (node)), \ + exprCollation((Node *) (node)))) + +/* + * Recursive guts of eval_const_expressions/estimate_expression_value + */ +static Node * +eval_const_expressions_mutator(Node *node, + eval_const_expressions_context *context) +{ + if (node == NULL) + return NULL; + switch (nodeTag(node)) + { + case T_Param: + { + Param *param = (Param *) node; + ParamListInfo paramLI = context->boundParams; + + /* Look to see if we've been given a value for this Param */ + if (param->paramkind == PARAM_EXTERN && + paramLI != NULL && + param->paramid > 0 && + param->paramid <= paramLI->numParams) + { + ParamExternData *prm; + ParamExternData prmdata; + + /* + * Give hook a chance in case parameter is dynamic. Tell + * it that this fetch is speculative, so it should avoid + * erroring out if parameter is unavailable. + */ + if (paramLI->paramFetch != NULL) + prm = paramLI->paramFetch(paramLI, param->paramid, + true, &prmdata); + else + prm = ¶mLI->params[param->paramid - 1]; + + /* + * We don't just check OidIsValid, but insist that the + * fetched type match the Param, just in case the hook did + * something unexpected. No need to throw an error here + * though; leave that for runtime. + */ + if (OidIsValid(prm->ptype) && + prm->ptype == param->paramtype) + { + /* OK to substitute parameter value? */ + if (context->estimate || + (prm->pflags & PARAM_FLAG_CONST)) + { + /* + * Return a Const representing the param value. + * Must copy pass-by-ref datatypes, since the + * Param might be in a memory context + * shorter-lived than our output plan should be. + */ + int16 typLen; + bool typByVal; + Datum pval; + Const *con; + + get_typlenbyval(param->paramtype, + &typLen, &typByVal); + if (prm->isnull || typByVal) + pval = prm->value; + else + pval = datumCopy(prm->value, typByVal, typLen); + con = makeConst(param->paramtype, + param->paramtypmod, + param->paramcollid, + (int) typLen, + pval, + prm->isnull, + typByVal); + con->location = param->location; + return (Node *) con; + } + } + } + + /* + * Not replaceable, so just copy the Param (no need to + * recurse) + */ + return (Node *) copyObject(param); + } + case T_WindowFunc: + { + WindowFunc *expr = (WindowFunc *) node; + Oid funcid = expr->winfnoid; + List *args; + Expr *aggfilter; + HeapTuple func_tuple; + WindowFunc *newexpr; + + /* + * We can't really simplify a WindowFunc node, but we mustn't + * just fall through to the default processing, because we + * have to apply expand_function_arguments to its argument + * list. That takes care of inserting default arguments and + * expanding named-argument notation. + */ + func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); + if (!HeapTupleIsValid(func_tuple)) + elog(ERROR, "cache lookup failed for function %u", funcid); + + args = expand_function_arguments(expr->args, + false, expr->wintype, + func_tuple); + + ReleaseSysCache(func_tuple); + + /* Now, recursively simplify the args (which are a List) */ + args = (List *) + expression_tree_mutator((Node *) args, + eval_const_expressions_mutator, + (void *) context); + /* ... and the filter expression, which isn't */ + aggfilter = (Expr *) + eval_const_expressions_mutator((Node *) expr->aggfilter, + context); + + /* And build the replacement WindowFunc node */ + newexpr = makeNode(WindowFunc); + newexpr->winfnoid = expr->winfnoid; + newexpr->wintype = expr->wintype; + newexpr->wincollid = expr->wincollid; + newexpr->inputcollid = expr->inputcollid; + newexpr->args = args; + newexpr->aggfilter = aggfilter; + newexpr->winref = expr->winref; + newexpr->winstar = expr->winstar; + newexpr->winagg = expr->winagg; + newexpr->location = expr->location; + + return (Node *) newexpr; + } + case T_FuncExpr: + { + FuncExpr *expr = (FuncExpr *) node; + List *args = expr->args; + Expr *simple; + FuncExpr *newexpr; + + /* + * Code for op/func reduction is pretty bulky, so split it out + * as a separate function. Note: exprTypmod normally returns + * -1 for a FuncExpr, but not when the node is recognizably a + * length coercion; we want to preserve the typmod in the + * eventual Const if so. + */ + simple = simplify_function(expr->funcid, + expr->funcresulttype, + exprTypmod(node), + expr->funccollid, + expr->inputcollid, + &args, + expr->funcvariadic, + true, + true, + context); + if (simple) /* successfully simplified it */ + return (Node *) simple; + + /* + * The expression cannot be simplified any further, so build + * and return a replacement FuncExpr node using the + * possibly-simplified arguments. Note that we have also + * converted the argument list to positional notation. + */ + newexpr = makeNode(FuncExpr); + newexpr->funcid = expr->funcid; + newexpr->funcresulttype = expr->funcresulttype; + newexpr->funcretset = expr->funcretset; + newexpr->funcvariadic = expr->funcvariadic; + newexpr->funcformat = expr->funcformat; + newexpr->funccollid = expr->funccollid; + newexpr->inputcollid = expr->inputcollid; + newexpr->args = args; + newexpr->location = expr->location; + return (Node *) newexpr; + } + case T_OpExpr: + { + OpExpr *expr = (OpExpr *) node; + List *args = expr->args; + Expr *simple; + OpExpr *newexpr; + + /* + * Need to get OID of underlying function. Okay to scribble + * on input to this extent. + */ + set_opfuncid(expr); + + /* + * Code for op/func reduction is pretty bulky, so split it out + * as a separate function. + */ + simple = simplify_function(expr->opfuncid, + expr->opresulttype, -1, + expr->opcollid, + expr->inputcollid, + &args, + false, + true, + true, + context); + if (simple) /* successfully simplified it */ + return (Node *) simple; + + /* + * If the operator is boolean equality or inequality, we know + * how to simplify cases involving one constant and one + * non-constant argument. + */ + if (expr->opno == BooleanEqualOperator || + expr->opno == BooleanNotEqualOperator) + { + simple = (Expr *) simplify_boolean_equality(expr->opno, + args); + if (simple) /* successfully simplified it */ + return (Node *) simple; + } + + /* + * The expression cannot be simplified any further, so build + * and return a replacement OpExpr node using the + * possibly-simplified arguments. + */ + newexpr = makeNode(OpExpr); + newexpr->opno = expr->opno; + newexpr->opfuncid = expr->opfuncid; + newexpr->opresulttype = expr->opresulttype; + newexpr->opretset = expr->opretset; + newexpr->opcollid = expr->opcollid; + newexpr->inputcollid = expr->inputcollid; + newexpr->args = args; + newexpr->location = expr->location; + return (Node *) newexpr; + } + case T_DistinctExpr: + { + DistinctExpr *expr = (DistinctExpr *) node; + List *args; + ListCell *arg; + bool has_null_input = false; + bool all_null_input = true; + bool has_nonconst_input = false; + Expr *simple; + DistinctExpr *newexpr; + + /* + * Reduce constants in the DistinctExpr's arguments. We know + * args is either NIL or a List node, so we can call + * expression_tree_mutator directly rather than recursing to + * self. + */ + args = (List *) expression_tree_mutator((Node *) expr->args, + eval_const_expressions_mutator, + (void *) context); + + /* + * We must do our own check for NULLs because DistinctExpr has + * different results for NULL input than the underlying + * operator does. + */ + foreach(arg, args) + { + if (IsA(lfirst(arg), Const)) + { + has_null_input |= ((Const *) lfirst(arg))->constisnull; + all_null_input &= ((Const *) lfirst(arg))->constisnull; + } + else + has_nonconst_input = true; + } + + /* all constants? then can optimize this out */ + if (!has_nonconst_input) + { + /* all nulls? then not distinct */ + if (all_null_input) + return makeBoolConst(false, false); + + /* one null? then distinct */ + if (has_null_input) + return makeBoolConst(true, false); + + /* otherwise try to evaluate the '=' operator */ + /* (NOT okay to try to inline it, though!) */ + + /* + * Need to get OID of underlying function. Okay to + * scribble on input to this extent. + */ + set_opfuncid((OpExpr *) expr); /* rely on struct + * equivalence */ + + /* + * Code for op/func reduction is pretty bulky, so split it + * out as a separate function. + */ + simple = simplify_function(expr->opfuncid, + expr->opresulttype, -1, + expr->opcollid, + expr->inputcollid, + &args, + false, + false, + false, + context); + if (simple) /* successfully simplified it */ + { + /* + * Since the underlying operator is "=", must negate + * its result + */ + Const *csimple = castNode(Const, simple); + + csimple->constvalue = + BoolGetDatum(!DatumGetBool(csimple->constvalue)); + return (Node *) csimple; + } + } + + /* + * The expression cannot be simplified any further, so build + * and return a replacement DistinctExpr node using the + * possibly-simplified arguments. + */ + newexpr = makeNode(DistinctExpr); + newexpr->opno = expr->opno; + newexpr->opfuncid = expr->opfuncid; + newexpr->opresulttype = expr->opresulttype; + newexpr->opretset = expr->opretset; + newexpr->opcollid = expr->opcollid; + newexpr->inputcollid = expr->inputcollid; + newexpr->args = args; + newexpr->location = expr->location; + return (Node *) newexpr; + } + case T_NullIfExpr: + { + NullIfExpr *expr; + ListCell *arg; + bool has_nonconst_input = false; + + /* Copy the node and const-simplify its arguments */ + expr = (NullIfExpr *) ece_generic_processing(node); + + /* If either argument is NULL they can't be equal */ + foreach(arg, expr->args) + { + if (!IsA(lfirst(arg), Const)) + has_nonconst_input = true; + else if (((Const *) lfirst(arg))->constisnull) + return (Node *) linitial(expr->args); + } + + /* + * Need to get OID of underlying function before checking if + * the function is OK to evaluate. + */ + set_opfuncid((OpExpr *) expr); + + if (!has_nonconst_input && + ece_function_is_safe(expr->opfuncid, context)) + return ece_evaluate_expr(expr); + + return (Node *) expr; + } + case T_ScalarArrayOpExpr: + { + ScalarArrayOpExpr *saop; + + /* Copy the node and const-simplify its arguments */ + saop = (ScalarArrayOpExpr *) ece_generic_processing(node); + + /* Make sure we know underlying function */ + set_sa_opfuncid(saop); + + /* + * If all arguments are Consts, and it's a safe function, we + * can fold to a constant + */ + if (ece_all_arguments_const(saop) && + ece_function_is_safe(saop->opfuncid, context)) + return ece_evaluate_expr(saop); + return (Node *) saop; + } + case T_BoolExpr: + { + BoolExpr *expr = (BoolExpr *) node; + + switch (expr->boolop) + { + case OR_EXPR: + { + List *newargs; + bool haveNull = false; + bool forceTrue = false; + + newargs = simplify_or_arguments(expr->args, + context, + &haveNull, + &forceTrue); + if (forceTrue) + return makeBoolConst(true, false); + if (haveNull) + newargs = lappend(newargs, + makeBoolConst(false, true)); + /* If all the inputs are FALSE, result is FALSE */ + if (newargs == NIL) + return makeBoolConst(false, false); + + /* + * If only one nonconst-or-NULL input, it's the + * result + */ + if (list_length(newargs) == 1) + return (Node *) linitial(newargs); + /* Else we still need an OR node */ + return (Node *) make_orclause(newargs); + } + case AND_EXPR: + { + List *newargs; + bool haveNull = false; + bool forceFalse = false; + + newargs = simplify_and_arguments(expr->args, + context, + &haveNull, + &forceFalse); + if (forceFalse) + return makeBoolConst(false, false); + if (haveNull) + newargs = lappend(newargs, + makeBoolConst(false, true)); + /* If all the inputs are TRUE, result is TRUE */ + if (newargs == NIL) + return makeBoolConst(true, false); + + /* + * If only one nonconst-or-NULL input, it's the + * result + */ + if (list_length(newargs) == 1) + return (Node *) linitial(newargs); + /* Else we still need an AND node */ + return (Node *) make_andclause(newargs); + } + case NOT_EXPR: + { + Node *arg; + + Assert(list_length(expr->args) == 1); + arg = eval_const_expressions_mutator(linitial(expr->args), + context); + + /* + * Use negate_clause() to see if we can simplify + * away the NOT. + */ + return negate_clause(arg); + } + default: + elog(ERROR, "unrecognized boolop: %d", + (int) expr->boolop); + break; + } + break; + } + case T_SubPlan: + case T_AlternativeSubPlan: + + /* + * Return a SubPlan unchanged --- too late to do anything with it. + * + * XXX should we ereport() here instead? Probably this routine + * should never be invoked after SubPlan creation. + */ + return node; + case T_RelabelType: + { + RelabelType *relabel = (RelabelType *) node; + Node *arg; + + /* Simplify the input ... */ + arg = eval_const_expressions_mutator((Node *) relabel->arg, + context); + /* ... and attach a new RelabelType node, if needed */ + return applyRelabelType(arg, + relabel->resulttype, + relabel->resulttypmod, + relabel->resultcollid, + relabel->relabelformat, + relabel->location, + true); + } + case T_CoerceViaIO: + { + CoerceViaIO *expr = (CoerceViaIO *) node; + List *args; + Oid outfunc; + bool outtypisvarlena; + Oid infunc; + Oid intypioparam; + Expr *simple; + CoerceViaIO *newexpr; + + /* Make a List so we can use simplify_function */ + args = list_make1(expr->arg); + + /* + * CoerceViaIO represents calling the source type's output + * function then the result type's input function. So, try to + * simplify it as though it were a stack of two such function + * calls. First we need to know what the functions are. + * + * Note that the coercion functions are assumed not to care + * about input collation, so we just pass InvalidOid for that. + */ + getTypeOutputInfo(exprType((Node *) expr->arg), + &outfunc, &outtypisvarlena); + getTypeInputInfo(expr->resulttype, + &infunc, &intypioparam); + + simple = simplify_function(outfunc, + CSTRINGOID, -1, + InvalidOid, + InvalidOid, + &args, + false, + true, + true, + context); + if (simple) /* successfully simplified output fn */ + { + /* + * Input functions may want 1 to 3 arguments. We always + * supply all three, trusting that nothing downstream will + * complain. + */ + args = list_make3(simple, + makeConst(OIDOID, + -1, + InvalidOid, + sizeof(Oid), + ObjectIdGetDatum(intypioparam), + false, + true), + makeConst(INT4OID, + -1, + InvalidOid, + sizeof(int32), + Int32GetDatum(-1), + false, + true)); + + simple = simplify_function(infunc, + expr->resulttype, -1, + expr->resultcollid, + InvalidOid, + &args, + false, + false, + true, + context); + if (simple) /* successfully simplified input fn */ + return (Node *) simple; + } + + /* + * The expression cannot be simplified any further, so build + * and return a replacement CoerceViaIO node using the + * possibly-simplified argument. + */ + newexpr = makeNode(CoerceViaIO); + newexpr->arg = (Expr *) linitial(args); + newexpr->resulttype = expr->resulttype; + newexpr->resultcollid = expr->resultcollid; + newexpr->coerceformat = expr->coerceformat; + newexpr->location = expr->location; + return (Node *) newexpr; + } + case T_ArrayCoerceExpr: + { + ArrayCoerceExpr *ac = makeNode(ArrayCoerceExpr); + Node *save_case_val; + + /* + * Copy the node and const-simplify its arguments. We can't + * use ece_generic_processing() here because we need to mess + * with case_val only while processing the elemexpr. + */ + memcpy(ac, node, sizeof(ArrayCoerceExpr)); + ac->arg = (Expr *) + eval_const_expressions_mutator((Node *) ac->arg, + context); + + /* + * Set up for the CaseTestExpr node contained in the elemexpr. + * We must prevent it from absorbing any outer CASE value. + */ + save_case_val = context->case_val; + context->case_val = NULL; + + ac->elemexpr = (Expr *) + eval_const_expressions_mutator((Node *) ac->elemexpr, + context); + + context->case_val = save_case_val; + + /* + * If constant argument and the per-element expression is + * immutable, we can simplify the whole thing to a constant. + * Exception: although contain_mutable_functions considers + * CoerceToDomain immutable for historical reasons, let's not + * do so here; this ensures coercion to an array-over-domain + * does not apply the domain's constraints until runtime. + */ + if (ac->arg && IsA(ac->arg, Const) && + ac->elemexpr && !IsA(ac->elemexpr, CoerceToDomain) && + !contain_mutable_functions((Node *) ac->elemexpr)) + return ece_evaluate_expr(ac); + + return (Node *) ac; + } + case T_CollateExpr: + { + /* + * We replace CollateExpr with RelabelType, so as to improve + * uniformity of expression representation and thus simplify + * comparison of expressions. Hence this looks very nearly + * the same as the RelabelType case, and we can apply the same + * optimizations to avoid unnecessary RelabelTypes. + */ + CollateExpr *collate = (CollateExpr *) node; + Node *arg; + + /* Simplify the input ... */ + arg = eval_const_expressions_mutator((Node *) collate->arg, + context); + /* ... and attach a new RelabelType node, if needed */ + return applyRelabelType(arg, + exprType(arg), + exprTypmod(arg), + collate->collOid, + COERCE_IMPLICIT_CAST, + collate->location, + true); + } + case T_CaseExpr: + { + /*---------- + * CASE expressions can be simplified if there are constant + * condition clauses: + * FALSE (or NULL): drop the alternative + * TRUE: drop all remaining alternatives + * If the first non-FALSE alternative is a constant TRUE, + * we can simplify the entire CASE to that alternative's + * expression. If there are no non-FALSE alternatives, + * we simplify the entire CASE to the default result (ELSE). + * + * If we have a simple-form CASE with constant test + * expression, we substitute the constant value for contained + * CaseTestExpr placeholder nodes, so that we have the + * opportunity to reduce constant test conditions. For + * example this allows + * CASE 0 WHEN 0 THEN 1 ELSE 1/0 END + * to reduce to 1 rather than drawing a divide-by-0 error. + * Note that when the test expression is constant, we don't + * have to include it in the resulting CASE; for example + * CASE 0 WHEN x THEN y ELSE z END + * is transformed by the parser to + * CASE 0 WHEN CaseTestExpr = x THEN y ELSE z END + * which we can simplify to + * CASE WHEN 0 = x THEN y ELSE z END + * It is not necessary for the executor to evaluate the "arg" + * expression when executing the CASE, since any contained + * CaseTestExprs that might have referred to it will have been + * replaced by the constant. + *---------- + */ + CaseExpr *caseexpr = (CaseExpr *) node; + CaseExpr *newcase; + Node *save_case_val; + Node *newarg; + List *newargs; + bool const_true_cond; + Node *defresult = NULL; + ListCell *arg; + + /* Simplify the test expression, if any */ + newarg = eval_const_expressions_mutator((Node *) caseexpr->arg, + context); + + /* Set up for contained CaseTestExpr nodes */ + save_case_val = context->case_val; + if (newarg && IsA(newarg, Const)) + { + context->case_val = newarg; + newarg = NULL; /* not needed anymore, see above */ + } + else + context->case_val = NULL; + + /* Simplify the WHEN clauses */ + newargs = NIL; + const_true_cond = false; + foreach(arg, caseexpr->args) + { + CaseWhen *oldcasewhen = lfirst_node(CaseWhen, arg); + Node *casecond; + Node *caseresult; + + /* Simplify this alternative's test condition */ + casecond = eval_const_expressions_mutator((Node *) oldcasewhen->expr, + context); + + /* + * If the test condition is constant FALSE (or NULL), then + * drop this WHEN clause completely, without processing + * the result. + */ + if (casecond && IsA(casecond, Const)) + { + Const *const_input = (Const *) casecond; + + if (const_input->constisnull || + !DatumGetBool(const_input->constvalue)) + continue; /* drop alternative with FALSE cond */ + /* Else it's constant TRUE */ + const_true_cond = true; + } + + /* Simplify this alternative's result value */ + caseresult = eval_const_expressions_mutator((Node *) oldcasewhen->result, + context); + + /* If non-constant test condition, emit a new WHEN node */ + if (!const_true_cond) + { + CaseWhen *newcasewhen = makeNode(CaseWhen); + + newcasewhen->expr = (Expr *) casecond; + newcasewhen->result = (Expr *) caseresult; + newcasewhen->location = oldcasewhen->location; + newargs = lappend(newargs, newcasewhen); + continue; + } + + /* + * Found a TRUE condition, so none of the remaining + * alternatives can be reached. We treat the result as + * the default result. + */ + defresult = caseresult; + break; + } + + /* Simplify the default result, unless we replaced it above */ + if (!const_true_cond) + defresult = eval_const_expressions_mutator((Node *) caseexpr->defresult, + context); + + context->case_val = save_case_val; + + /* + * If no non-FALSE alternatives, CASE reduces to the default + * result + */ + if (newargs == NIL) + return defresult; + /* Otherwise we need a new CASE node */ + newcase = makeNode(CaseExpr); + newcase->casetype = caseexpr->casetype; + newcase->casecollid = caseexpr->casecollid; + newcase->arg = (Expr *) newarg; + newcase->args = newargs; + newcase->defresult = (Expr *) defresult; + newcase->location = caseexpr->location; + return (Node *) newcase; + } + case T_CaseTestExpr: + { + /* + * If we know a constant test value for the current CASE + * construct, substitute it for the placeholder. Else just + * return the placeholder as-is. + */ + if (context->case_val) + return copyObject(context->case_val); + else + return copyObject(node); + } + case T_SubscriptingRef: + case T_ArrayExpr: + case T_RowExpr: + case T_MinMaxExpr: + { + /* + * Generic handling for node types whose own processing is + * known to be immutable, and for which we need no smarts + * beyond "simplify if all inputs are constants". + * + * Treating SubscriptingRef this way assumes that subscripting + * fetch and assignment are both immutable. This constrains + * type-specific subscripting implementations; maybe we should + * relax it someday. + * + * Treating MinMaxExpr this way amounts to assuming that the + * btree comparison function it calls is immutable; see the + * reasoning in contain_mutable_functions_walker. + */ + + /* Copy the node and const-simplify its arguments */ + node = ece_generic_processing(node); + /* If all arguments are Consts, we can fold to a constant */ + if (ece_all_arguments_const(node)) + return ece_evaluate_expr(node); + return node; + } + case T_CoalesceExpr: + { + CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; + CoalesceExpr *newcoalesce; + List *newargs; + ListCell *arg; + + newargs = NIL; + foreach(arg, coalesceexpr->args) + { + Node *e; + + e = eval_const_expressions_mutator((Node *) lfirst(arg), + context); + + /* + * We can remove null constants from the list. For a + * non-null constant, if it has not been preceded by any + * other non-null-constant expressions then it is the + * result. Otherwise, it's the next argument, but we can + * drop following arguments since they will never be + * reached. + */ + if (IsA(e, Const)) + { + if (((Const *) e)->constisnull) + continue; /* drop null constant */ + if (newargs == NIL) + return e; /* first expr */ + newargs = lappend(newargs, e); + break; + } + newargs = lappend(newargs, e); + } + + /* + * If all the arguments were constant null, the result is just + * null + */ + if (newargs == NIL) + return (Node *) makeNullConst(coalesceexpr->coalescetype, + -1, + coalesceexpr->coalescecollid); + + newcoalesce = makeNode(CoalesceExpr); + newcoalesce->coalescetype = coalesceexpr->coalescetype; + newcoalesce->coalescecollid = coalesceexpr->coalescecollid; + newcoalesce->args = newargs; + newcoalesce->location = coalesceexpr->location; + return (Node *) newcoalesce; + } + case T_SQLValueFunction: + { + /* + * All variants of SQLValueFunction are stable, so if we are + * estimating the expression's value, we should evaluate the + * current function value. Otherwise just copy. + */ + SQLValueFunction *svf = (SQLValueFunction *) node; + + if (context->estimate) + return (Node *) evaluate_expr((Expr *) svf, + svf->type, + svf->typmod, + InvalidOid); + else + return copyObject((Node *) svf); + } + case T_FieldSelect: + { + /* + * We can optimize field selection from a whole-row Var into a + * simple Var. (This case won't be generated directly by the + * parser, because ParseComplexProjection short-circuits it. + * But it can arise while simplifying functions.) Also, we + * can optimize field selection from a RowExpr construct, or + * of course from a constant. + * + * However, replacing a whole-row Var in this way has a + * pitfall: if we've already built the rel targetlist for the + * source relation, then the whole-row Var is scheduled to be + * produced by the relation scan, but the simple Var probably + * isn't, which will lead to a failure in setrefs.c. This is + * not a problem when handling simple single-level queries, in + * which expression simplification always happens first. It + * is a risk for lateral references from subqueries, though. + * To avoid such failures, don't optimize uplevel references. + * + * We must also check that the declared type of the field is + * still the same as when the FieldSelect was created --- this + * can change if someone did ALTER COLUMN TYPE on the rowtype. + * If it isn't, we skip the optimization; the case will + * probably fail at runtime, but that's not our problem here. + */ + FieldSelect *fselect = (FieldSelect *) node; + FieldSelect *newfselect; + Node *arg; + + arg = eval_const_expressions_mutator((Node *) fselect->arg, + context); + if (arg && IsA(arg, Var) && + ((Var *) arg)->varattno == InvalidAttrNumber && + ((Var *) arg)->varlevelsup == 0) + { + if (rowtype_field_matches(((Var *) arg)->vartype, + fselect->fieldnum, + fselect->resulttype, + fselect->resulttypmod, + fselect->resultcollid)) + return (Node *) makeVar(((Var *) arg)->varno, + fselect->fieldnum, + fselect->resulttype, + fselect->resulttypmod, + fselect->resultcollid, + ((Var *) arg)->varlevelsup); + } + if (arg && IsA(arg, RowExpr)) + { + RowExpr *rowexpr = (RowExpr *) arg; + + if (fselect->fieldnum > 0 && + fselect->fieldnum <= list_length(rowexpr->args)) + { + Node *fld = (Node *) list_nth(rowexpr->args, + fselect->fieldnum - 1); + + if (rowtype_field_matches(rowexpr->row_typeid, + fselect->fieldnum, + fselect->resulttype, + fselect->resulttypmod, + fselect->resultcollid) && + fselect->resulttype == exprType(fld) && + fselect->resulttypmod == exprTypmod(fld) && + fselect->resultcollid == exprCollation(fld)) + return fld; + } + } + newfselect = makeNode(FieldSelect); + newfselect->arg = (Expr *) arg; + newfselect->fieldnum = fselect->fieldnum; + newfselect->resulttype = fselect->resulttype; + newfselect->resulttypmod = fselect->resulttypmod; + newfselect->resultcollid = fselect->resultcollid; + if (arg && IsA(arg, Const)) + { + Const *con = (Const *) arg; + + if (rowtype_field_matches(con->consttype, + newfselect->fieldnum, + newfselect->resulttype, + newfselect->resulttypmod, + newfselect->resultcollid)) + return ece_evaluate_expr(newfselect); + } + return (Node *) newfselect; + } + case T_NullTest: + { + NullTest *ntest = (NullTest *) node; + NullTest *newntest; + Node *arg; + + arg = eval_const_expressions_mutator((Node *) ntest->arg, + context); + if (ntest->argisrow && arg && IsA(arg, RowExpr)) + { + /* + * We break ROW(...) IS [NOT] NULL into separate tests on + * its component fields. This form is usually more + * efficient to evaluate, as well as being more amenable + * to optimization. + */ + RowExpr *rarg = (RowExpr *) arg; + List *newargs = NIL; + ListCell *l; + + foreach(l, rarg->args) + { + Node *relem = (Node *) lfirst(l); + + /* + * A constant field refutes the whole NullTest if it's + * of the wrong nullness; else we can discard it. + */ + if (relem && IsA(relem, Const)) + { + Const *carg = (Const *) relem; + + if (carg->constisnull ? + (ntest->nulltesttype == IS_NOT_NULL) : + (ntest->nulltesttype == IS_NULL)) + return makeBoolConst(false, false); + continue; + } + + /* + * Else, make a scalar (argisrow == false) NullTest + * for this field. Scalar semantics are required + * because IS [NOT] NULL doesn't recurse; see comments + * in ExecEvalRowNullInt(). + */ + newntest = makeNode(NullTest); + newntest->arg = (Expr *) relem; + newntest->nulltesttype = ntest->nulltesttype; + newntest->argisrow = false; + newntest->location = ntest->location; + newargs = lappend(newargs, newntest); + } + /* If all the inputs were constants, result is TRUE */ + if (newargs == NIL) + return makeBoolConst(true, false); + /* If only one nonconst input, it's the result */ + if (list_length(newargs) == 1) + return (Node *) linitial(newargs); + /* Else we need an AND node */ + return (Node *) make_andclause(newargs); + } + if (!ntest->argisrow && arg && IsA(arg, Const)) + { + Const *carg = (Const *) arg; + bool result; + + switch (ntest->nulltesttype) + { + case IS_NULL: + result = carg->constisnull; + break; + case IS_NOT_NULL: + result = !carg->constisnull; + break; + default: + elog(ERROR, "unrecognized nulltesttype: %d", + (int) ntest->nulltesttype); + result = false; /* keep compiler quiet */ + break; + } + + return makeBoolConst(result, false); + } + + newntest = makeNode(NullTest); + newntest->arg = (Expr *) arg; + newntest->nulltesttype = ntest->nulltesttype; + newntest->argisrow = ntest->argisrow; + newntest->location = ntest->location; + return (Node *) newntest; + } + case T_BooleanTest: + { + /* + * This case could be folded into the generic handling used + * for ArrayExpr etc. But because the simplification logic is + * so trivial, applying evaluate_expr() to perform it would be + * a heavy overhead. BooleanTest is probably common enough to + * justify keeping this bespoke implementation. + */ + BooleanTest *btest = (BooleanTest *) node; + BooleanTest *newbtest; + Node *arg; + + arg = eval_const_expressions_mutator((Node *) btest->arg, + context); + if (arg && IsA(arg, Const)) + { + Const *carg = (Const *) arg; + bool result; + + switch (btest->booltesttype) + { + case IS_TRUE: + result = (!carg->constisnull && + DatumGetBool(carg->constvalue)); + break; + case IS_NOT_TRUE: + result = (carg->constisnull || + !DatumGetBool(carg->constvalue)); + break; + case IS_FALSE: + result = (!carg->constisnull && + !DatumGetBool(carg->constvalue)); + break; + case IS_NOT_FALSE: + result = (carg->constisnull || + DatumGetBool(carg->constvalue)); + break; + case IS_UNKNOWN: + result = carg->constisnull; + break; + case IS_NOT_UNKNOWN: + result = !carg->constisnull; + break; + default: + elog(ERROR, "unrecognized booltesttype: %d", + (int) btest->booltesttype); + result = false; /* keep compiler quiet */ + break; + } + + return makeBoolConst(result, false); + } + + newbtest = makeNode(BooleanTest); + newbtest->arg = (Expr *) arg; + newbtest->booltesttype = btest->booltesttype; + newbtest->location = btest->location; + return (Node *) newbtest; + } + case T_CoerceToDomain: + { + /* + * If the domain currently has no constraints, we replace the + * CoerceToDomain node with a simple RelabelType, which is + * both far faster to execute and more amenable to later + * optimization. We must then mark the plan as needing to be + * rebuilt if the domain's constraints change. + * + * Also, in estimation mode, always replace CoerceToDomain + * nodes, effectively assuming that the coercion will succeed. + */ + CoerceToDomain *cdomain = (CoerceToDomain *) node; + CoerceToDomain *newcdomain; + Node *arg; + + arg = eval_const_expressions_mutator((Node *) cdomain->arg, + context); + if (context->estimate || + !DomainHasConstraints(cdomain->resulttype)) + { + /* Record dependency, if this isn't estimation mode */ + if (context->root && !context->estimate) + record_plan_type_dependency(context->root, + cdomain->resulttype); + + /* Generate RelabelType to substitute for CoerceToDomain */ + return applyRelabelType(arg, + cdomain->resulttype, + cdomain->resulttypmod, + cdomain->resultcollid, + cdomain->coercionformat, + cdomain->location, + true); + } + + newcdomain = makeNode(CoerceToDomain); + newcdomain->arg = (Expr *) arg; + newcdomain->resulttype = cdomain->resulttype; + newcdomain->resulttypmod = cdomain->resulttypmod; + newcdomain->resultcollid = cdomain->resultcollid; + newcdomain->coercionformat = cdomain->coercionformat; + newcdomain->location = cdomain->location; + return (Node *) newcdomain; + } + case T_PlaceHolderVar: + + /* + * In estimation mode, just strip the PlaceHolderVar node + * altogether; this amounts to estimating that the contained value + * won't be forced to null by an outer join. In regular mode we + * just use the default behavior (ie, simplify the expression but + * leave the PlaceHolderVar node intact). + */ + if (context->estimate) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + return eval_const_expressions_mutator((Node *) phv->phexpr, + context); + } + break; + case T_ConvertRowtypeExpr: + { + ConvertRowtypeExpr *cre = castNode(ConvertRowtypeExpr, node); + Node *arg; + ConvertRowtypeExpr *newcre; + + arg = eval_const_expressions_mutator((Node *) cre->arg, + context); + + newcre = makeNode(ConvertRowtypeExpr); + newcre->resulttype = cre->resulttype; + newcre->convertformat = cre->convertformat; + newcre->location = cre->location; + + /* + * In case of a nested ConvertRowtypeExpr, we can convert the + * leaf row directly to the topmost row format without any + * intermediate conversions. (This works because + * ConvertRowtypeExpr is used only for child->parent + * conversion in inheritance trees, which works by exact match + * of column name, and a column absent in an intermediate + * result can't be present in the final result.) + * + * No need to check more than one level deep, because the + * above recursion will have flattened anything else. + */ + if (arg != NULL && IsA(arg, ConvertRowtypeExpr)) + { + ConvertRowtypeExpr *argcre = (ConvertRowtypeExpr *) arg; + + arg = (Node *) argcre->arg; + + /* + * Make sure an outer implicit conversion can't hide an + * inner explicit one. + */ + if (newcre->convertformat == COERCE_IMPLICIT_CAST) + newcre->convertformat = argcre->convertformat; + } + + newcre->arg = (Expr *) arg; + + if (arg != NULL && IsA(arg, Const)) + return ece_evaluate_expr((Node *) newcre); + return (Node *) newcre; + } + default: + break; + } + + /* + * For any node type not handled above, copy the node unchanged but + * const-simplify its subexpressions. This is the correct thing for node + * types whose behavior might change between planning and execution, such + * as CurrentOfExpr. It's also a safe default for new node types not + * known to this routine. + */ + return ece_generic_processing(node); +} + +/* + * Subroutine for eval_const_expressions: check for non-Const nodes. + * + * We can abort recursion immediately on finding a non-Const node. This is + * critical for performance, else eval_const_expressions_mutator would take + * O(N^2) time on non-simplifiable trees. However, we do need to descend + * into List nodes since expression_tree_walker sometimes invokes the walker + * function directly on List subtrees. + */ +static bool +contain_non_const_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + if (IsA(node, Const)) + return false; + if (IsA(node, List)) + return expression_tree_walker(node, contain_non_const_walker, context); + /* Otherwise, abort the tree traversal and return true */ + return true; +} + +/* + * Subroutine for eval_const_expressions: check if a function is OK to evaluate + */ +static bool +ece_function_is_safe(Oid funcid, eval_const_expressions_context *context) +{ + char provolatile = func_volatile(funcid); + + /* + * Ordinarily we are only allowed to simplify immutable functions. But for + * purposes of estimation, we consider it okay to simplify functions that + * are merely stable; the risk that the result might change from planning + * time to execution time is worth taking in preference to not being able + * to estimate the value at all. + */ + if (provolatile == PROVOLATILE_IMMUTABLE) + return true; + if (context->estimate && provolatile == PROVOLATILE_STABLE) + return true; + return false; +} + +/* + * Subroutine for eval_const_expressions: process arguments of an OR clause + * + * This includes flattening of nested ORs as well as recursion to + * eval_const_expressions to simplify the OR arguments. + * + * After simplification, OR arguments are handled as follows: + * non constant: keep + * FALSE: drop (does not affect result) + * TRUE: force result to TRUE + * NULL: keep only one + * We must keep one NULL input because OR expressions evaluate to NULL when no + * input is TRUE and at least one is NULL. We don't actually include the NULL + * here, that's supposed to be done by the caller. + * + * The output arguments *haveNull and *forceTrue must be initialized false + * by the caller. They will be set true if a NULL constant or TRUE constant, + * respectively, is detected anywhere in the argument list. + */ +static List * +simplify_or_arguments(List *args, + eval_const_expressions_context *context, + bool *haveNull, bool *forceTrue) +{ + List *newargs = NIL; + List *unprocessed_args; + + /* + * We want to ensure that any OR immediately beneath another OR gets + * flattened into a single OR-list, so as to simplify later reasoning. + * + * To avoid stack overflow from recursion of eval_const_expressions, we + * resort to some tenseness here: we keep a list of not-yet-processed + * inputs, and handle flattening of nested ORs by prepending to the to-do + * list instead of recursing. Now that the parser generates N-argument + * ORs from simple lists, this complexity is probably less necessary than + * it once was, but we might as well keep the logic. + */ + unprocessed_args = list_copy(args); + while (unprocessed_args) + { + Node *arg = (Node *) linitial(unprocessed_args); + + unprocessed_args = list_delete_first(unprocessed_args); + + /* flatten nested ORs as per above comment */ + if (is_orclause(arg)) + { + List *subargs = ((BoolExpr *) arg)->args; + List *oldlist = unprocessed_args; + + unprocessed_args = list_concat_copy(subargs, unprocessed_args); + /* perhaps-overly-tense code to avoid leaking old lists */ + list_free(oldlist); + continue; + } + + /* If it's not an OR, simplify it */ + arg = eval_const_expressions_mutator(arg, context); + + /* + * It is unlikely but not impossible for simplification of a non-OR + * clause to produce an OR. Recheck, but don't be too tense about it + * since it's not a mainstream case. In particular we don't worry + * about const-simplifying the input twice, nor about list leakage. + */ + if (is_orclause(arg)) + { + List *subargs = ((BoolExpr *) arg)->args; + + unprocessed_args = list_concat_copy(subargs, unprocessed_args); + continue; + } + + /* + * OK, we have a const-simplified non-OR argument. Process it per + * comments above. + */ + if (IsA(arg, Const)) + { + Const *const_input = (Const *) arg; + + if (const_input->constisnull) + *haveNull = true; + else if (DatumGetBool(const_input->constvalue)) + { + *forceTrue = true; + + /* + * Once we detect a TRUE result we can just exit the loop + * immediately. However, if we ever add a notion of + * non-removable functions, we'd need to keep scanning. + */ + return NIL; + } + /* otherwise, we can drop the constant-false input */ + continue; + } + + /* else emit the simplified arg into the result list */ + newargs = lappend(newargs, arg); + } + + return newargs; +} + +/* + * Subroutine for eval_const_expressions: process arguments of an AND clause + * + * This includes flattening of nested ANDs as well as recursion to + * eval_const_expressions to simplify the AND arguments. + * + * After simplification, AND arguments are handled as follows: + * non constant: keep + * TRUE: drop (does not affect result) + * FALSE: force result to FALSE + * NULL: keep only one + * We must keep one NULL input because AND expressions evaluate to NULL when + * no input is FALSE and at least one is NULL. We don't actually include the + * NULL here, that's supposed to be done by the caller. + * + * The output arguments *haveNull and *forceFalse must be initialized false + * by the caller. They will be set true if a null constant or false constant, + * respectively, is detected anywhere in the argument list. + */ +static List * +simplify_and_arguments(List *args, + eval_const_expressions_context *context, + bool *haveNull, bool *forceFalse) +{ + List *newargs = NIL; + List *unprocessed_args; + + /* See comments in simplify_or_arguments */ + unprocessed_args = list_copy(args); + while (unprocessed_args) + { + Node *arg = (Node *) linitial(unprocessed_args); + + unprocessed_args = list_delete_first(unprocessed_args); + + /* flatten nested ANDs as per above comment */ + if (is_andclause(arg)) + { + List *subargs = ((BoolExpr *) arg)->args; + List *oldlist = unprocessed_args; + + unprocessed_args = list_concat_copy(subargs, unprocessed_args); + /* perhaps-overly-tense code to avoid leaking old lists */ + list_free(oldlist); + continue; + } + + /* If it's not an AND, simplify it */ + arg = eval_const_expressions_mutator(arg, context); + + /* + * It is unlikely but not impossible for simplification of a non-AND + * clause to produce an AND. Recheck, but don't be too tense about it + * since it's not a mainstream case. In particular we don't worry + * about const-simplifying the input twice, nor about list leakage. + */ + if (is_andclause(arg)) + { + List *subargs = ((BoolExpr *) arg)->args; + + unprocessed_args = list_concat_copy(subargs, unprocessed_args); + continue; + } + + /* + * OK, we have a const-simplified non-AND argument. Process it per + * comments above. + */ + if (IsA(arg, Const)) + { + Const *const_input = (Const *) arg; + + if (const_input->constisnull) + *haveNull = true; + else if (!DatumGetBool(const_input->constvalue)) + { + *forceFalse = true; + + /* + * Once we detect a FALSE result we can just exit the loop + * immediately. However, if we ever add a notion of + * non-removable functions, we'd need to keep scanning. + */ + return NIL; + } + /* otherwise, we can drop the constant-true input */ + continue; + } + + /* else emit the simplified arg into the result list */ + newargs = lappend(newargs, arg); + } + + return newargs; +} + +/* + * Subroutine for eval_const_expressions: try to simplify boolean equality + * or inequality condition + * + * Inputs are the operator OID and the simplified arguments to the operator. + * Returns a simplified expression if successful, or NULL if cannot + * simplify the expression. + * + * The idea here is to reduce "x = true" to "x" and "x = false" to "NOT x", + * or similarly "x <> true" to "NOT x" and "x <> false" to "x". + * This is only marginally useful in itself, but doing it in constant folding + * ensures that we will recognize these forms as being equivalent in, for + * example, partial index matching. + * + * We come here only if simplify_function has failed; therefore we cannot + * see two constant inputs, nor a constant-NULL input. + */ +static Node * +simplify_boolean_equality(Oid opno, List *args) +{ + Node *leftop; + Node *rightop; + + Assert(list_length(args) == 2); + leftop = linitial(args); + rightop = lsecond(args); + if (leftop && IsA(leftop, Const)) + { + Assert(!((Const *) leftop)->constisnull); + if (opno == BooleanEqualOperator) + { + if (DatumGetBool(((Const *) leftop)->constvalue)) + return rightop; /* true = foo */ + else + return negate_clause(rightop); /* false = foo */ + } + else + { + if (DatumGetBool(((Const *) leftop)->constvalue)) + return negate_clause(rightop); /* true <> foo */ + else + return rightop; /* false <> foo */ + } + } + if (rightop && IsA(rightop, Const)) + { + Assert(!((Const *) rightop)->constisnull); + if (opno == BooleanEqualOperator) + { + if (DatumGetBool(((Const *) rightop)->constvalue)) + return leftop; /* foo = true */ + else + return negate_clause(leftop); /* foo = false */ + } + else + { + if (DatumGetBool(((Const *) rightop)->constvalue)) + return negate_clause(leftop); /* foo <> true */ + else + return leftop; /* foo <> false */ + } + } + return NULL; +} + +/* + * Subroutine for eval_const_expressions: try to simplify a function call + * (which might originally have been an operator; we don't care) + * + * Inputs are the function OID, actual result type OID (which is needed for + * polymorphic functions), result typmod, result collation, the input + * collation to use for the function, the original argument list (not + * const-simplified yet, unless process_args is false), and some flags; + * also the context data for eval_const_expressions. + * + * Returns a simplified expression if successful, or NULL if cannot + * simplify the function call. + * + * This function is also responsible for converting named-notation argument + * lists into positional notation and/or adding any needed default argument + * expressions; which is a bit grotty, but it avoids extra fetches of the + * function's pg_proc tuple. For this reason, the args list is + * pass-by-reference. Conversion and const-simplification of the args list + * will be done even if simplification of the function call itself is not + * possible. + */ +static Expr * +simplify_function(Oid funcid, Oid result_type, int32 result_typmod, + Oid result_collid, Oid input_collid, List **args_p, + bool funcvariadic, bool process_args, bool allow_non_const, + eval_const_expressions_context *context) +{ + List *args = *args_p; + HeapTuple func_tuple; + Form_pg_proc func_form; + Expr *newexpr; + + /* + * We have three strategies for simplification: execute the function to + * deliver a constant result, use a transform function to generate a + * substitute node tree, or expand in-line the body of the function + * definition (which only works for simple SQL-language functions, but + * that is a common case). Each case needs access to the function's + * pg_proc tuple, so fetch it just once. + * + * Note: the allow_non_const flag suppresses both the second and third + * strategies; so if !allow_non_const, simplify_function can only return a + * Const or NULL. Argument-list rewriting happens anyway, though. + */ + func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); + if (!HeapTupleIsValid(func_tuple)) + elog(ERROR, "cache lookup failed for function %u", funcid); + func_form = (Form_pg_proc) GETSTRUCT(func_tuple); + + /* + * Process the function arguments, unless the caller did it already. + * + * Here we must deal with named or defaulted arguments, and then + * recursively apply eval_const_expressions to the whole argument list. + */ + if (process_args) + { + args = expand_function_arguments(args, false, result_type, func_tuple); + args = (List *) expression_tree_mutator((Node *) args, + eval_const_expressions_mutator, + (void *) context); + /* Argument processing done, give it back to the caller */ + *args_p = args; + } + + /* Now attempt simplification of the function call proper. */ + + newexpr = evaluate_function(funcid, result_type, result_typmod, + result_collid, input_collid, + args, funcvariadic, + func_tuple, context); + + if (!newexpr && allow_non_const && OidIsValid(func_form->prosupport)) + { + /* + * Build a SupportRequestSimplify node to pass to the support + * function, pointing to a dummy FuncExpr node containing the + * simplified arg list. We use this approach to present a uniform + * interface to the support function regardless of how the target + * function is actually being invoked. + */ + SupportRequestSimplify req; + FuncExpr fexpr; + + fexpr.xpr.type = T_FuncExpr; + fexpr.funcid = funcid; + fexpr.funcresulttype = result_type; + fexpr.funcretset = func_form->proretset; + fexpr.funcvariadic = funcvariadic; + fexpr.funcformat = COERCE_EXPLICIT_CALL; + fexpr.funccollid = result_collid; + fexpr.inputcollid = input_collid; + fexpr.args = args; + fexpr.location = -1; + + req.type = T_SupportRequestSimplify; + req.root = context->root; + req.fcall = &fexpr; + + newexpr = (Expr *) + DatumGetPointer(OidFunctionCall1(func_form->prosupport, + PointerGetDatum(&req))); + + /* catch a possible API misunderstanding */ + Assert(newexpr != (Expr *) &fexpr); + } + + if (!newexpr && allow_non_const) + newexpr = inline_function(funcid, result_type, result_collid, + input_collid, args, funcvariadic, + func_tuple, context); + + ReleaseSysCache(func_tuple); + + return newexpr; +} + +/* + * expand_function_arguments: convert named-notation args to positional args + * and/or insert default args, as needed + * + * Returns a possibly-transformed version of the args list. + * + * If include_out_arguments is true, then the args list and the result + * include OUT arguments. + * + * The expected result type of the call must be given, for sanity-checking + * purposes. Also, we ask the caller to provide the function's actual + * pg_proc tuple, not just its OID. + * + * If we need to change anything, the input argument list is copied, not + * modified. + * + * Note: this gets applied to operator argument lists too, even though the + * cases it handles should never occur there. This should be OK since it + * will fall through very quickly if there's nothing to do. + */ +List * +expand_function_arguments(List *args, bool include_out_arguments, + Oid result_type, HeapTuple func_tuple) +{ + Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); + Oid *proargtypes = funcform->proargtypes.values; + int pronargs = funcform->pronargs; + bool has_named_args = false; + ListCell *lc; + + /* + * If we are asked to match to OUT arguments, then use the proallargtypes + * array (which includes those); otherwise use proargtypes (which + * doesn't). Of course, if proallargtypes is null, we always use + * proargtypes. (Fetching proallargtypes is annoyingly expensive + * considering that we may have nothing to do here, but fortunately the + * common case is include_out_arguments == false.) + */ + if (include_out_arguments) + { + Datum proallargtypes; + bool isNull; + + proallargtypes = SysCacheGetAttr(PROCOID, func_tuple, + Anum_pg_proc_proallargtypes, + &isNull); + if (!isNull) + { + ArrayType *arr = DatumGetArrayTypeP(proallargtypes); + + pronargs = ARR_DIMS(arr)[0]; + if (ARR_NDIM(arr) != 1 || + pronargs < 0 || + ARR_HASNULL(arr) || + ARR_ELEMTYPE(arr) != OIDOID) + elog(ERROR, "proallargtypes is not a 1-D Oid array or it contains nulls"); + Assert(pronargs >= funcform->pronargs); + proargtypes = (Oid *) ARR_DATA_PTR(arr); + } + } + + /* Do we have any named arguments? */ + foreach(lc, args) + { + Node *arg = (Node *) lfirst(lc); + + if (IsA(arg, NamedArgExpr)) + { + has_named_args = true; + break; + } + } + + /* If so, we must apply reorder_function_arguments */ + if (has_named_args) + { + args = reorder_function_arguments(args, pronargs, func_tuple); + /* Recheck argument types and add casts if needed */ + recheck_cast_function_args(args, result_type, + proargtypes, pronargs, + func_tuple); + } + else if (list_length(args) < pronargs) + { + /* No named args, but we seem to be short some defaults */ + args = add_function_defaults(args, pronargs, func_tuple); + /* Recheck argument types and add casts if needed */ + recheck_cast_function_args(args, result_type, + proargtypes, pronargs, + func_tuple); + } + + return args; +} + +/* + * reorder_function_arguments: convert named-notation args to positional args + * + * This function also inserts default argument values as needed, since it's + * impossible to form a truly valid positional call without that. + */ +static List * +reorder_function_arguments(List *args, int pronargs, HeapTuple func_tuple) +{ + Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); + int nargsprovided = list_length(args); + Node *argarray[FUNC_MAX_ARGS]; + ListCell *lc; + int i; + + Assert(nargsprovided <= pronargs); + if (pronargs < 0 || pronargs > FUNC_MAX_ARGS) + elog(ERROR, "too many function arguments"); + memset(argarray, 0, pronargs * sizeof(Node *)); + + /* Deconstruct the argument list into an array indexed by argnumber */ + i = 0; + foreach(lc, args) + { + Node *arg = (Node *) lfirst(lc); + + if (!IsA(arg, NamedArgExpr)) + { + /* positional argument, assumed to precede all named args */ + Assert(argarray[i] == NULL); + argarray[i++] = arg; + } + else + { + NamedArgExpr *na = (NamedArgExpr *) arg; + + Assert(na->argnumber >= 0 && na->argnumber < pronargs); + Assert(argarray[na->argnumber] == NULL); + argarray[na->argnumber] = (Node *) na->arg; + } + } + + /* + * Fetch default expressions, if needed, and insert into array at proper + * locations (they aren't necessarily consecutive or all used) + */ + if (nargsprovided < pronargs) + { + List *defaults = fetch_function_defaults(func_tuple); + + i = pronargs - funcform->pronargdefaults; + foreach(lc, defaults) + { + if (argarray[i] == NULL) + argarray[i] = (Node *) lfirst(lc); + i++; + } + } + + /* Now reconstruct the args list in proper order */ + args = NIL; + for (i = 0; i < pronargs; i++) + { + Assert(argarray[i] != NULL); + args = lappend(args, argarray[i]); + } + + return args; +} + +/* + * add_function_defaults: add missing function arguments from its defaults + * + * This is used only when the argument list was positional to begin with, + * and so we know we just need to add defaults at the end. + */ +static List * +add_function_defaults(List *args, int pronargs, HeapTuple func_tuple) +{ + int nargsprovided = list_length(args); + List *defaults; + int ndelete; + + /* Get all the default expressions from the pg_proc tuple */ + defaults = fetch_function_defaults(func_tuple); + + /* Delete any unused defaults from the list */ + ndelete = nargsprovided + list_length(defaults) - pronargs; + if (ndelete < 0) + elog(ERROR, "not enough default arguments"); + if (ndelete > 0) + defaults = list_delete_first_n(defaults, ndelete); + + /* And form the combined argument list, not modifying the input list */ + return list_concat_copy(args, defaults); +} + +/* + * fetch_function_defaults: get function's default arguments as expression list + */ +static List * +fetch_function_defaults(HeapTuple func_tuple) +{ + List *defaults; + Datum proargdefaults; + bool isnull; + char *str; + + /* The error cases here shouldn't happen, but check anyway */ + proargdefaults = SysCacheGetAttr(PROCOID, func_tuple, + Anum_pg_proc_proargdefaults, + &isnull); + if (isnull) + elog(ERROR, "not enough default arguments"); + str = TextDatumGetCString(proargdefaults); + defaults = castNode(List, stringToNode(str)); + pfree(str); + return defaults; +} + +/* + * recheck_cast_function_args: recheck function args and typecast as needed + * after adding defaults. + * + * It is possible for some of the defaulted arguments to be polymorphic; + * therefore we can't assume that the default expressions have the correct + * data types already. We have to re-resolve polymorphics and do coercion + * just like the parser did. + * + * This should be a no-op if there are no polymorphic arguments, + * but we do it anyway to be sure. + * + * Note: if any casts are needed, the args list is modified in-place; + * caller should have already copied the list structure. + */ +static void +recheck_cast_function_args(List *args, Oid result_type, + Oid *proargtypes, int pronargs, + HeapTuple func_tuple) +{ + Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); + int nargs; + Oid actual_arg_types[FUNC_MAX_ARGS]; + Oid declared_arg_types[FUNC_MAX_ARGS]; + Oid rettype; + ListCell *lc; + + if (list_length(args) > FUNC_MAX_ARGS) + elog(ERROR, "too many function arguments"); + nargs = 0; + foreach(lc, args) + { + actual_arg_types[nargs++] = exprType((Node *) lfirst(lc)); + } + Assert(nargs == pronargs); + memcpy(declared_arg_types, proargtypes, pronargs * sizeof(Oid)); + rettype = enforce_generic_type_consistency(actual_arg_types, + declared_arg_types, + nargs, + funcform->prorettype, + false); + /* let's just check we got the same answer as the parser did ... */ + if (rettype != result_type) + elog(ERROR, "function's resolved result type changed during planning"); + + /* perform any necessary typecasting of arguments */ + make_fn_arguments(NULL, args, actual_arg_types, declared_arg_types); +} + +/* + * evaluate_function: try to pre-evaluate a function call + * + * We can do this if the function is strict and has any constant-null inputs + * (just return a null constant), or if the function is immutable and has all + * constant inputs (call it and return the result as a Const node). In + * estimation mode we are willing to pre-evaluate stable functions too. + * + * Returns a simplified expression if successful, or NULL if cannot + * simplify the function. + */ +static Expr * +evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, + Oid result_collid, Oid input_collid, List *args, + bool funcvariadic, + HeapTuple func_tuple, + eval_const_expressions_context *context) +{ + Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); + bool has_nonconst_input = false; + bool has_null_input = false; + ListCell *arg; + FuncExpr *newexpr; + + /* + * Can't simplify if it returns a set. + */ + if (funcform->proretset) + return NULL; + + /* + * Can't simplify if it returns RECORD. The immediate problem is that it + * will be needing an expected tupdesc which we can't supply here. + * + * In the case where it has OUT parameters, it could get by without an + * expected tupdesc, but we still have issues: get_expr_result_type() + * doesn't know how to extract type info from a RECORD constant, and in + * the case of a NULL function result there doesn't seem to be any clean + * way to fix that. In view of the likelihood of there being still other + * gotchas, seems best to leave the function call unreduced. + */ + if (funcform->prorettype == RECORDOID) + return NULL; + + /* + * Check for constant inputs and especially constant-NULL inputs. + */ + foreach(arg, args) + { + if (IsA(lfirst(arg), Const)) + has_null_input |= ((Const *) lfirst(arg))->constisnull; + else + has_nonconst_input = true; + } + + /* + * If the function is strict and has a constant-NULL input, it will never + * be called at all, so we can replace the call by a NULL constant, even + * if there are other inputs that aren't constant, and even if the + * function is not otherwise immutable. + */ + if (funcform->proisstrict && has_null_input) + return (Expr *) makeNullConst(result_type, result_typmod, + result_collid); + + /* + * Otherwise, can simplify only if all inputs are constants. (For a + * non-strict function, constant NULL inputs are treated the same as + * constant non-NULL inputs.) + */ + if (has_nonconst_input) + return NULL; + + /* + * Ordinarily we are only allowed to simplify immutable functions. But for + * purposes of estimation, we consider it okay to simplify functions that + * are merely stable; the risk that the result might change from planning + * time to execution time is worth taking in preference to not being able + * to estimate the value at all. + */ + if (funcform->provolatile == PROVOLATILE_IMMUTABLE) + /* okay */ ; + else if (context->estimate && funcform->provolatile == PROVOLATILE_STABLE) + /* okay */ ; + else + return NULL; + + /* + * OK, looks like we can simplify this operator/function. + * + * Build a new FuncExpr node containing the already-simplified arguments. + */ + newexpr = makeNode(FuncExpr); + newexpr->funcid = funcid; + newexpr->funcresulttype = result_type; + newexpr->funcretset = false; + newexpr->funcvariadic = funcvariadic; + newexpr->funcformat = COERCE_EXPLICIT_CALL; /* doesn't matter */ + newexpr->funccollid = result_collid; /* doesn't matter */ + newexpr->inputcollid = input_collid; + newexpr->args = args; + newexpr->location = -1; + + return evaluate_expr((Expr *) newexpr, result_type, result_typmod, + result_collid); +} + +/* + * inline_function: try to expand a function call inline + * + * If the function is a sufficiently simple SQL-language function + * (just "SELECT expression"), then we can inline it and avoid the rather + * high per-call overhead of SQL functions. Furthermore, this can expose + * opportunities for constant-folding within the function expression. + * + * We have to beware of some special cases however. A directly or + * indirectly recursive function would cause us to recurse forever, + * so we keep track of which functions we are already expanding and + * do not re-expand them. Also, if a parameter is used more than once + * in the SQL-function body, we require it not to contain any volatile + * functions (volatiles might deliver inconsistent answers) nor to be + * unreasonably expensive to evaluate. The expensiveness check not only + * prevents us from doing multiple evaluations of an expensive parameter + * at runtime, but is a safety value to limit growth of an expression due + * to repeated inlining. + * + * We must also beware of changing the volatility or strictness status of + * functions by inlining them. + * + * Also, at the moment we can't inline functions returning RECORD. This + * doesn't work in the general case because it discards information such + * as OUT-parameter declarations. + * + * Also, context-dependent expression nodes in the argument list are trouble. + * + * Returns a simplified expression if successful, or NULL if cannot + * simplify the function. + */ +static Expr * +inline_function(Oid funcid, Oid result_type, Oid result_collid, + Oid input_collid, List *args, + bool funcvariadic, + HeapTuple func_tuple, + eval_const_expressions_context *context) +{ + Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); + char *src; + Datum tmp; + bool isNull; + MemoryContext oldcxt; + MemoryContext mycxt; + inline_error_callback_arg callback_arg; + ErrorContextCallback sqlerrcontext; + FuncExpr *fexpr; + SQLFunctionParseInfoPtr pinfo; + TupleDesc rettupdesc; + ParseState *pstate; + List *raw_parsetree_list; + List *querytree_list; + Query *querytree; + Node *newexpr; + int *usecounts; + ListCell *arg; + int i; + + /* + * Forget it if the function is not SQL-language or has other showstopper + * properties. (The prokind and nargs checks are just paranoia.) + */ + if (funcform->prolang != SQLlanguageId || + funcform->prokind != PROKIND_FUNCTION || + funcform->prosecdef || + funcform->proretset || + funcform->prorettype == RECORDOID || + !heap_attisnull(func_tuple, Anum_pg_proc_proconfig, NULL) || + funcform->pronargs != list_length(args)) + return NULL; + + /* Check for recursive function, and give up trying to expand if so */ + if (list_member_oid(context->active_fns, funcid)) + return NULL; + + /* Check permission to call function (fail later, if not) */ + if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) + return NULL; + + /* Check whether a plugin wants to hook function entry/exit */ + if (FmgrHookIsNeeded(funcid)) + return NULL; + + /* + * Make a temporary memory context, so that we don't leak all the stuff + * that parsing might create. + */ + mycxt = AllocSetContextCreate(CurrentMemoryContext, + "inline_function", + ALLOCSET_DEFAULT_SIZES); + oldcxt = MemoryContextSwitchTo(mycxt); + + /* + * We need a dummy FuncExpr node containing the already-simplified + * arguments. (In some cases we don't really need it, but building it is + * cheap enough that it's not worth contortions to avoid.) + */ + fexpr = makeNode(FuncExpr); + fexpr->funcid = funcid; + fexpr->funcresulttype = result_type; + fexpr->funcretset = false; + fexpr->funcvariadic = funcvariadic; + fexpr->funcformat = COERCE_EXPLICIT_CALL; /* doesn't matter */ + fexpr->funccollid = result_collid; /* doesn't matter */ + fexpr->inputcollid = input_collid; + fexpr->args = args; + fexpr->location = -1; + + /* Fetch the function body */ + tmp = SysCacheGetAttr(PROCOID, + func_tuple, + Anum_pg_proc_prosrc, + &isNull); + if (isNull) + elog(ERROR, "null prosrc for function %u", funcid); + src = TextDatumGetCString(tmp); + + /* + * Setup error traceback support for ereport(). This is so that we can + * finger the function that bad information came from. + */ + callback_arg.proname = NameStr(funcform->proname); + callback_arg.prosrc = src; + + sqlerrcontext.callback = sql_inline_error_callback; + sqlerrcontext.arg = (void *) &callback_arg; + sqlerrcontext.previous = error_context_stack; + error_context_stack = &sqlerrcontext; + + /* If we have prosqlbody, pay attention to that not prosrc */ + tmp = SysCacheGetAttr(PROCOID, + func_tuple, + Anum_pg_proc_prosqlbody, + &isNull); + if (!isNull) + { + Node *n; + List *querytree_list; + + n = stringToNode(TextDatumGetCString(tmp)); + if (IsA(n, List)) + querytree_list = linitial_node(List, castNode(List, n)); + else + querytree_list = list_make1(n); + if (list_length(querytree_list) != 1) + goto fail; + querytree = linitial(querytree_list); + + /* + * Because we'll insist below that the querytree have an empty rtable + * and no sublinks, it cannot have any relation references that need + * to be locked or rewritten. So we can omit those steps. + */ + } + else + { + /* Set up to handle parameters while parsing the function body. */ + pinfo = prepare_sql_fn_parse_info(func_tuple, + (Node *) fexpr, + input_collid); + + /* + * We just do parsing and parse analysis, not rewriting, because + * rewriting will not affect table-free-SELECT-only queries, which is + * all that we care about. Also, we can punt as soon as we detect + * more than one command in the function body. + */ + raw_parsetree_list = pg_parse_query(src); + if (list_length(raw_parsetree_list) != 1) + goto fail; + + pstate = make_parsestate(NULL); + pstate->p_sourcetext = src; + sql_fn_parser_setup(pstate, pinfo); + + querytree = transformTopLevelStmt(pstate, linitial(raw_parsetree_list)); + + free_parsestate(pstate); + } + + /* + * The single command must be a simple "SELECT expression". + * + * Note: if you change the tests involved in this, see also plpgsql's + * exec_simple_check_plan(). That generally needs to have the same idea + * of what's a "simple expression", so that inlining a function that + * previously wasn't inlined won't change plpgsql's conclusion. + */ + if (!IsA(querytree, Query) || + querytree->commandType != CMD_SELECT || + querytree->hasAggs || + querytree->hasWindowFuncs || + querytree->hasTargetSRFs || + querytree->hasSubLinks || + querytree->cteList || + querytree->rtable || + querytree->jointree->fromlist || + querytree->jointree->quals || + querytree->groupClause || + querytree->groupingSets || + querytree->havingQual || + querytree->windowClause || + querytree->distinctClause || + querytree->sortClause || + querytree->limitOffset || + querytree->limitCount || + querytree->setOperations || + list_length(querytree->targetList) != 1) + goto fail; + + /* If the function result is composite, resolve it */ + (void) get_expr_result_type((Node *) fexpr, + NULL, + &rettupdesc); + + /* + * Make sure the function (still) returns what it's declared to. This + * will raise an error if wrong, but that's okay since the function would + * fail at runtime anyway. Note that check_sql_fn_retval will also insert + * a coercion if needed to make the tlist expression match the declared + * type of the function. + * + * Note: we do not try this until we have verified that no rewriting was + * needed; that's probably not important, but let's be careful. + */ + querytree_list = list_make1(querytree); + if (check_sql_fn_retval(list_make1(querytree_list), + result_type, rettupdesc, + false, NULL)) + goto fail; /* reject whole-tuple-result cases */ + + /* + * Given the tests above, check_sql_fn_retval shouldn't have decided to + * inject a projection step, but let's just make sure. + */ + if (querytree != linitial(querytree_list)) + goto fail; + + /* Now we can grab the tlist expression */ + newexpr = (Node *) ((TargetEntry *) linitial(querytree->targetList))->expr; + + /* + * If the SQL function returns VOID, we can only inline it if it is a + * SELECT of an expression returning VOID (ie, it's just a redirection to + * another VOID-returning function). In all non-VOID-returning cases, + * check_sql_fn_retval should ensure that newexpr returns the function's + * declared result type, so this test shouldn't fail otherwise; but we may + * as well cope gracefully if it does. + */ + if (exprType(newexpr) != result_type) + goto fail; + + /* + * Additional validity checks on the expression. It mustn't be more + * volatile than the surrounding function (this is to avoid breaking hacks + * that involve pretending a function is immutable when it really ain't). + * If the surrounding function is declared strict, then the expression + * must contain only strict constructs and must use all of the function + * parameters (this is overkill, but an exact analysis is hard). + */ + if (funcform->provolatile == PROVOLATILE_IMMUTABLE && + contain_mutable_functions(newexpr)) + goto fail; + else if (funcform->provolatile == PROVOLATILE_STABLE && + contain_volatile_functions(newexpr)) + goto fail; + + if (funcform->proisstrict && + contain_nonstrict_functions(newexpr)) + goto fail; + + /* + * If any parameter expression contains a context-dependent node, we can't + * inline, for fear of putting such a node into the wrong context. + */ + if (contain_context_dependent_node((Node *) args)) + goto fail; + + /* + * We may be able to do it; there are still checks on parameter usage to + * make, but those are most easily done in combination with the actual + * substitution of the inputs. So start building expression with inputs + * substituted. + */ + usecounts = (int *) palloc0(funcform->pronargs * sizeof(int)); + newexpr = substitute_actual_parameters(newexpr, funcform->pronargs, + args, usecounts); + + /* Now check for parameter usage */ + i = 0; + foreach(arg, args) + { + Node *param = lfirst(arg); + + if (usecounts[i] == 0) + { + /* Param not used at all: uncool if func is strict */ + if (funcform->proisstrict) + goto fail; + } + else if (usecounts[i] != 1) + { + /* Param used multiple times: uncool if expensive or volatile */ + QualCost eval_cost; + + /* + * We define "expensive" as "contains any subplan or more than 10 + * operators". Note that the subplan search has to be done + * explicitly, since cost_qual_eval() will barf on unplanned + * subselects. + */ + if (contain_subplans(param)) + goto fail; + cost_qual_eval(&eval_cost, list_make1(param), NULL); + if (eval_cost.startup + eval_cost.per_tuple > + 10 * cpu_operator_cost) + goto fail; + + /* + * Check volatility last since this is more expensive than the + * above tests + */ + if (contain_volatile_functions(param)) + goto fail; + } + i++; + } + + /* + * Whew --- we can make the substitution. Copy the modified expression + * out of the temporary memory context, and clean up. + */ + MemoryContextSwitchTo(oldcxt); + + newexpr = copyObject(newexpr); + + MemoryContextDelete(mycxt); + + /* + * If the result is of a collatable type, force the result to expose the + * correct collation. In most cases this does not matter, but it's + * possible that the function result is used directly as a sort key or in + * other places where we expect exprCollation() to tell the truth. + */ + if (OidIsValid(result_collid)) + { + Oid exprcoll = exprCollation(newexpr); + + if (OidIsValid(exprcoll) && exprcoll != result_collid) + { + CollateExpr *newnode = makeNode(CollateExpr); + + newnode->arg = (Expr *) newexpr; + newnode->collOid = result_collid; + newnode->location = -1; + + newexpr = (Node *) newnode; + } + } + + /* + * Since there is now no trace of the function in the plan tree, we must + * explicitly record the plan's dependency on the function. + */ + if (context->root) + record_plan_function_dependency(context->root, funcid); + + /* + * Recursively try to simplify the modified expression. Here we must add + * the current function to the context list of active functions. + */ + context->active_fns = lappend_oid(context->active_fns, funcid); + newexpr = eval_const_expressions_mutator(newexpr, context); + context->active_fns = list_delete_last(context->active_fns); + + error_context_stack = sqlerrcontext.previous; + + return (Expr *) newexpr; + + /* Here if func is not inlinable: release temp memory and return NULL */ +fail: + MemoryContextSwitchTo(oldcxt); + MemoryContextDelete(mycxt); + error_context_stack = sqlerrcontext.previous; + + return NULL; +} + +/* + * Replace Param nodes by appropriate actual parameters + */ +static Node * +substitute_actual_parameters(Node *expr, int nargs, List *args, + int *usecounts) +{ + substitute_actual_parameters_context context; + + context.nargs = nargs; + context.args = args; + context.usecounts = usecounts; + + return substitute_actual_parameters_mutator(expr, &context); +} + +static Node * +substitute_actual_parameters_mutator(Node *node, + substitute_actual_parameters_context *context) +{ + if (node == NULL) + return NULL; + if (IsA(node, Param)) + { + Param *param = (Param *) node; + + if (param->paramkind != PARAM_EXTERN) + elog(ERROR, "unexpected paramkind: %d", (int) param->paramkind); + if (param->paramid <= 0 || param->paramid > context->nargs) + elog(ERROR, "invalid paramid: %d", param->paramid); + + /* Count usage of parameter */ + context->usecounts[param->paramid - 1]++; + + /* Select the appropriate actual arg and replace the Param with it */ + /* We don't need to copy at this time (it'll get done later) */ + return list_nth(context->args, param->paramid - 1); + } + return expression_tree_mutator(node, substitute_actual_parameters_mutator, + (void *) context); +} + +/* + * error context callback to let us supply a call-stack traceback + */ +static void +sql_inline_error_callback(void *arg) +{ + inline_error_callback_arg *callback_arg = (inline_error_callback_arg *) arg; + int syntaxerrposition; + + /* If it's a syntax error, convert to internal syntax error report */ + syntaxerrposition = geterrposition(); + if (syntaxerrposition > 0) + { + errposition(0); + internalerrposition(syntaxerrposition); + internalerrquery(callback_arg->prosrc); + } + + errcontext("SQL function \"%s\" during inlining", callback_arg->proname); +} + +/* + * evaluate_expr: pre-evaluate a constant expression + * + * We use the executor's routine ExecEvalExpr() to avoid duplication of + * code and ensure we get the same result as the executor would get. + */ +Expr * +evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, + Oid result_collation) +{ + EState *estate; + ExprState *exprstate; + MemoryContext oldcontext; + Datum const_val; + bool const_is_null; + int16 resultTypLen; + bool resultTypByVal; + + /* + * To use the executor, we need an EState. + */ + estate = CreateExecutorState(); + + /* We can use the estate's working context to avoid memory leaks. */ + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + /* Make sure any opfuncids are filled in. */ + fix_opfuncids((Node *) expr); + + /* + * Prepare expr for execution. (Note: we can't use ExecPrepareExpr + * because it'd result in recursively invoking eval_const_expressions.) + */ + exprstate = ExecInitExpr(expr, NULL); + + /* + * And evaluate it. + * + * It is OK to use a default econtext because none of the ExecEvalExpr() + * code used in this situation will use econtext. That might seem + * fortuitous, but it's not so unreasonable --- a constant expression does + * not depend on context, by definition, n'est ce pas? + */ + const_val = ExecEvalExprSwitchContext(exprstate, + GetPerTupleExprContext(estate), + &const_is_null); + + /* Get info needed about result datatype */ + get_typlenbyval(result_type, &resultTypLen, &resultTypByVal); + + /* Get back to outer memory context */ + MemoryContextSwitchTo(oldcontext); + + /* + * Must copy result out of sub-context used by expression eval. + * + * Also, if it's varlena, forcibly detoast it. This protects us against + * storing TOAST pointers into plans that might outlive the referenced + * data. (makeConst would handle detoasting anyway, but it's worth a few + * extra lines here so that we can do the copy and detoast in one step.) + */ + if (!const_is_null) + { + if (resultTypLen == -1) + const_val = PointerGetDatum(PG_DETOAST_DATUM_COPY(const_val)); + else + const_val = datumCopy(const_val, resultTypByVal, resultTypLen); + } + + /* Release all the junk we just created */ + FreeExecutorState(estate); + + /* + * Make the constant result node. + */ + return (Expr *) makeConst(result_type, result_typmod, result_collation, + resultTypLen, + const_val, const_is_null, + resultTypByVal); +} + + +/* + * inline_set_returning_function + * Attempt to "inline" a set-returning function in the FROM clause. + * + * "rte" is an RTE_FUNCTION rangetable entry. If it represents a call of a + * set-returning SQL function that can safely be inlined, expand the function + * and return the substitute Query structure. Otherwise, return NULL. + * + * We assume that the RTE's expression has already been put through + * eval_const_expressions(), which among other things will take care of + * default arguments and named-argument notation. + * + * This has a good deal of similarity to inline_function(), but that's + * for the non-set-returning case, and there are enough differences to + * justify separate functions. + */ +Query * +inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) +{ + RangeTblFunction *rtfunc; + FuncExpr *fexpr; + Oid func_oid; + HeapTuple func_tuple; + Form_pg_proc funcform; + char *src; + Datum tmp; + bool isNull; + MemoryContext oldcxt; + MemoryContext mycxt; + inline_error_callback_arg callback_arg; + ErrorContextCallback sqlerrcontext; + SQLFunctionParseInfoPtr pinfo; + TypeFuncClass functypclass; + TupleDesc rettupdesc; + List *raw_parsetree_list; + List *querytree_list; + Query *querytree; + + Assert(rte->rtekind == RTE_FUNCTION); + + /* + * It doesn't make a lot of sense for a SQL SRF to refer to itself in its + * own FROM clause, since that must cause infinite recursion at runtime. + * It will cause this code to recurse too, so check for stack overflow. + * (There's no need to do more.) + */ + check_stack_depth(); + + /* Fail if the RTE has ORDINALITY - we don't implement that here. */ + if (rte->funcordinality) + return NULL; + + /* Fail if RTE isn't a single, simple FuncExpr */ + if (list_length(rte->functions) != 1) + return NULL; + rtfunc = (RangeTblFunction *) linitial(rte->functions); + + if (!IsA(rtfunc->funcexpr, FuncExpr)) + return NULL; + fexpr = (FuncExpr *) rtfunc->funcexpr; + + func_oid = fexpr->funcid; + + /* + * The function must be declared to return a set, else inlining would + * change the results if the contained SELECT didn't return exactly one + * row. + */ + if (!fexpr->funcretset) + return NULL; + + /* + * Refuse to inline if the arguments contain any volatile functions or + * sub-selects. Volatile functions are rejected because inlining may + * result in the arguments being evaluated multiple times, risking a + * change in behavior. Sub-selects are rejected partly for implementation + * reasons (pushing them down another level might change their behavior) + * and partly because they're likely to be expensive and so multiple + * evaluation would be bad. + */ + if (contain_volatile_functions((Node *) fexpr->args) || + contain_subplans((Node *) fexpr->args)) + return NULL; + + /* Check permission to call function (fail later, if not) */ + if (pg_proc_aclcheck(func_oid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) + return NULL; + + /* Check whether a plugin wants to hook function entry/exit */ + if (FmgrHookIsNeeded(func_oid)) + return NULL; + + /* + * OK, let's take a look at the function's pg_proc entry. + */ + func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(func_oid)); + if (!HeapTupleIsValid(func_tuple)) + elog(ERROR, "cache lookup failed for function %u", func_oid); + funcform = (Form_pg_proc) GETSTRUCT(func_tuple); + + /* + * Forget it if the function is not SQL-language or has other showstopper + * properties. In particular it mustn't be declared STRICT, since we + * couldn't enforce that. It also mustn't be VOLATILE, because that is + * supposed to cause it to be executed with its own snapshot, rather than + * sharing the snapshot of the calling query. We also disallow returning + * SETOF VOID, because inlining would result in exposing the actual result + * of the function's last SELECT, which should not happen in that case. + * (Rechecking prokind, proretset, and pronargs is just paranoia.) + */ + if (funcform->prolang != SQLlanguageId || + funcform->prokind != PROKIND_FUNCTION || + funcform->proisstrict || + funcform->provolatile == PROVOLATILE_VOLATILE || + funcform->prorettype == VOIDOID || + funcform->prosecdef || + !funcform->proretset || + list_length(fexpr->args) != funcform->pronargs || + !heap_attisnull(func_tuple, Anum_pg_proc_proconfig, NULL)) + { + ReleaseSysCache(func_tuple); + return NULL; + } + + /* + * Make a temporary memory context, so that we don't leak all the stuff + * that parsing might create. + */ + mycxt = AllocSetContextCreate(CurrentMemoryContext, + "inline_set_returning_function", + ALLOCSET_DEFAULT_SIZES); + oldcxt = MemoryContextSwitchTo(mycxt); + + /* Fetch the function body */ + tmp = SysCacheGetAttr(PROCOID, + func_tuple, + Anum_pg_proc_prosrc, + &isNull); + if (isNull) + elog(ERROR, "null prosrc for function %u", func_oid); + src = TextDatumGetCString(tmp); + + /* + * Setup error traceback support for ereport(). This is so that we can + * finger the function that bad information came from. + */ + callback_arg.proname = NameStr(funcform->proname); + callback_arg.prosrc = src; + + sqlerrcontext.callback = sql_inline_error_callback; + sqlerrcontext.arg = (void *) &callback_arg; + sqlerrcontext.previous = error_context_stack; + error_context_stack = &sqlerrcontext; + + /* If we have prosqlbody, pay attention to that not prosrc */ + tmp = SysCacheGetAttr(PROCOID, + func_tuple, + Anum_pg_proc_prosqlbody, + &isNull); + if (!isNull) + { + Node *n; + + n = stringToNode(TextDatumGetCString(tmp)); + if (IsA(n, List)) + querytree_list = linitial_node(List, castNode(List, n)); + else + querytree_list = list_make1(n); + if (list_length(querytree_list) != 1) + goto fail; + querytree = linitial(querytree_list); + + /* Acquire necessary locks, then apply rewriter. */ + AcquireRewriteLocks(querytree, true, false); + querytree_list = pg_rewrite_query(querytree); + if (list_length(querytree_list) != 1) + goto fail; + querytree = linitial(querytree_list); + } + else + { + /* + * Set up to handle parameters while parsing the function body. We + * can use the FuncExpr just created as the input for + * prepare_sql_fn_parse_info. + */ + pinfo = prepare_sql_fn_parse_info(func_tuple, + (Node *) fexpr, + fexpr->inputcollid); + + /* + * Parse, analyze, and rewrite (unlike inline_function(), we can't + * skip rewriting here). We can fail as soon as we find more than one + * query, though. + */ + raw_parsetree_list = pg_parse_query(src); + if (list_length(raw_parsetree_list) != 1) + goto fail; + + querytree_list = pg_analyze_and_rewrite_withcb(linitial(raw_parsetree_list), + src, + (ParserSetupHook) sql_fn_parser_setup, + pinfo, NULL); + if (list_length(querytree_list) != 1) + goto fail; + querytree = linitial(querytree_list); + } + + /* + * Also resolve the actual function result tupdesc, if composite. If the + * function is just declared to return RECORD, dig the info out of the AS + * clause. + */ + functypclass = get_expr_result_type((Node *) fexpr, NULL, &rettupdesc); + if (functypclass == TYPEFUNC_RECORD) + rettupdesc = BuildDescFromLists(rtfunc->funccolnames, + rtfunc->funccoltypes, + rtfunc->funccoltypmods, + rtfunc->funccolcollations); + + /* + * The single command must be a plain SELECT. + */ + if (!IsA(querytree, Query) || + querytree->commandType != CMD_SELECT) + goto fail; + + /* + * Make sure the function (still) returns what it's declared to. This + * will raise an error if wrong, but that's okay since the function would + * fail at runtime anyway. Note that check_sql_fn_retval will also insert + * coercions if needed to make the tlist expression(s) match the declared + * type of the function. We also ask it to insert dummy NULL columns for + * any dropped columns in rettupdesc, so that the elements of the modified + * tlist match up to the attribute numbers. + * + * If the function returns a composite type, don't inline unless the check + * shows it's returning a whole tuple result; otherwise what it's + * returning is a single composite column which is not what we need. + */ + if (!check_sql_fn_retval(list_make1(querytree_list), + fexpr->funcresulttype, rettupdesc, + true, NULL) && + (functypclass == TYPEFUNC_COMPOSITE || + functypclass == TYPEFUNC_COMPOSITE_DOMAIN || + functypclass == TYPEFUNC_RECORD)) + goto fail; /* reject not-whole-tuple-result cases */ + + /* + * check_sql_fn_retval might've inserted a projection step, but that's + * fine; just make sure we use the upper Query. + */ + querytree = linitial_node(Query, querytree_list); + + /* + * Looks good --- substitute parameters into the query. + */ + querytree = substitute_actual_srf_parameters(querytree, + funcform->pronargs, + fexpr->args); + + /* + * Copy the modified query out of the temporary memory context, and clean + * up. + */ + MemoryContextSwitchTo(oldcxt); + + querytree = copyObject(querytree); + + MemoryContextDelete(mycxt); + error_context_stack = sqlerrcontext.previous; + ReleaseSysCache(func_tuple); + + /* + * We don't have to fix collations here because the upper query is already + * parsed, ie, the collations in the RTE are what count. + */ + + /* + * Since there is now no trace of the function in the plan tree, we must + * explicitly record the plan's dependency on the function. + */ + record_plan_function_dependency(root, func_oid); + + /* + * We must also notice if the inserted query adds a dependency on the + * calling role due to RLS quals. + */ + if (querytree->hasRowSecurity) + root->glob->dependsOnRole = true; + + return querytree; + + /* Here if func is not inlinable: release temp memory and return NULL */ +fail: + MemoryContextSwitchTo(oldcxt); + MemoryContextDelete(mycxt); + error_context_stack = sqlerrcontext.previous; + ReleaseSysCache(func_tuple); + + return NULL; +} + +/* + * Replace Param nodes by appropriate actual parameters + * + * This is just enough different from substitute_actual_parameters() + * that it needs its own code. + */ +static Query * +substitute_actual_srf_parameters(Query *expr, int nargs, List *args) +{ + substitute_actual_srf_parameters_context context; + + context.nargs = nargs; + context.args = args; + context.sublevels_up = 1; + + return query_tree_mutator(expr, + substitute_actual_srf_parameters_mutator, + &context, + 0); +} + +static Node * +substitute_actual_srf_parameters_mutator(Node *node, + substitute_actual_srf_parameters_context *context) +{ + Node *result; + + if (node == NULL) + return NULL; + if (IsA(node, Query)) + { + context->sublevels_up++; + result = (Node *) query_tree_mutator((Query *) node, + substitute_actual_srf_parameters_mutator, + (void *) context, + 0); + context->sublevels_up--; + return result; + } + if (IsA(node, Param)) + { + Param *param = (Param *) node; + + if (param->paramkind == PARAM_EXTERN) + { + if (param->paramid <= 0 || param->paramid > context->nargs) + elog(ERROR, "invalid paramid: %d", param->paramid); + + /* + * Since the parameter is being inserted into a subquery, we must + * adjust levels. + */ + result = copyObject(list_nth(context->args, param->paramid - 1)); + IncrementVarSublevelsUp(result, context->sublevels_up, 0); + return result; + } + } + return expression_tree_mutator(node, + substitute_actual_srf_parameters_mutator, + (void *) context); +} + +/* + * pull_paramids + * Returns a Bitmapset containing the paramids of all Params in 'expr'. + */ +Bitmapset * +pull_paramids(Expr *expr) +{ + Bitmapset *result = NULL; + + (void) pull_paramids_walker((Node *) expr, &result); + + return result; +} + +static bool +pull_paramids_walker(Node *node, Bitmapset **context) +{ + if (node == NULL) + return false; + if (IsA(node, Param)) + { + Param *param = (Param *) node; + + *context = bms_add_member(*context, param->paramid); + return false; + } + return expression_tree_walker(node, pull_paramids_walker, + (void *) context); +} |