diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/clausesel.c | 23 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 18 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 160 |
3 files changed, 159 insertions, 42 deletions
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index e736c6e309e..996c98cc469 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.67 2004/05/30 23:40:28 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.68 2004/06/11 01:08:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -487,16 +487,27 @@ clause_selectivity(Query *root, } } } - else if (IsA(clause, Param)) - { - /* XXX any way to do better? */ - s1 = 1.0; - } else if (IsA(clause, Const)) { /* bool constant is pretty easy... */ s1 = ((bool) ((Const *) clause)->constvalue) ? 1.0 : 0.0; } + else if (IsA(clause, Param)) + { + /* see if we can replace the Param */ + Node *subst = estimate_expression_value(clause); + + if (IsA(subst, Const)) + { + /* bool constant is pretty easy... */ + s1 = ((bool) ((Const *) subst)->constvalue) ? 1.0 : 0.0; + } + else + { + /* XXX any way to do better? */ + s1 = (Selectivity) 0.5; + } + } else if (not_clause(clause)) { /* inverse of the selectivity of the underlying clause */ diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 064981b5af0..2fc82556c32 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.171 2004/05/30 23:40:29 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.172 2004/06/11 01:08:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -43,6 +43,9 @@ #include "utils/syscache.h" +ParamListInfo PlannerBoundParamList = NULL; /* current boundParams */ + + /* Expression kind codes for preprocess_expression */ #define EXPRKIND_QUAL 0 #define EXPRKIND_TARGET 1 @@ -71,20 +74,24 @@ static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist); * *****************************************************************************/ Plan * -planner(Query *parse, bool isCursor, int cursorOptions) +planner(Query *parse, bool isCursor, int cursorOptions, + ParamListInfo boundParams) { double tuple_fraction; Plan *result_plan; Index save_PlannerQueryLevel; List *save_PlannerParamList; + ParamListInfo save_PlannerBoundParamList; /* * The planner can be called recursively (an example is when * eval_const_expressions tries to pre-evaluate an SQL function). So, * these global state variables must be saved and restored. * - * These vars cannot be moved into the Query structure since their whole - * purpose is communication across multiple sub-Queries. + * Query level and the param list cannot be moved into the Query structure + * since their whole purpose is communication across multiple sub-Queries. + * Also, boundParams is explicitly info from outside the Query, and so + * is likewise better handled as a global variable. * * Note we do NOT save and restore PlannerPlanId: it exists to assign * unique IDs to SubPlan nodes, and we want those IDs to be unique for @@ -93,10 +100,12 @@ planner(Query *parse, bool isCursor, int cursorOptions) */ save_PlannerQueryLevel = PlannerQueryLevel; save_PlannerParamList = PlannerParamList; + save_PlannerBoundParamList = PlannerBoundParamList; /* Initialize state for handling outer-level references and params */ PlannerQueryLevel = 0; /* will be 1 in top-level subquery_planner */ PlannerParamList = NIL; + PlannerBoundParamList = boundParams; /* Determine what fraction of the plan is likely to be scanned */ if (isCursor) @@ -139,6 +148,7 @@ planner(Query *parse, bool isCursor, int cursorOptions) /* restore state for outer planner, if any */ PlannerQueryLevel = save_PlannerQueryLevel; PlannerParamList = save_PlannerParamList; + PlannerBoundParamList = save_PlannerBoundParamList; return result_plan; } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index a3a2ddfbd83..e7088d2b761 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.175 2004/06/09 19:08:16 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.176 2004/06/11 01:08:54 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -28,6 +28,7 @@ #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/planmain.h" +#include "optimizer/planner.h" #include "optimizer/var.h" #include "parser/analyze.h" #include "parser/parse_clause.h" @@ -43,6 +44,12 @@ typedef struct { + List *active_fns; + bool estimate; +} eval_const_expressions_context; + +typedef struct +{ int nargs; List *args; int *usecounts; @@ -57,17 +64,20 @@ static bool contain_mutable_functions_walker(Node *node, void *context); static bool contain_volatile_functions_walker(Node *node, void *context); static bool contain_nonstrict_functions_walker(Node *node, void *context); static bool set_coercionform_dontcare_walker(Node *node, void *context); -static Node *eval_const_expressions_mutator(Node *node, List *active_fns); +static Node *eval_const_expressions_mutator(Node *node, + eval_const_expressions_context *context); static List *simplify_or_arguments(List *args, bool *haveNull, bool *forceTrue); static List *simplify_and_arguments(List *args, bool *haveNull, bool *forceFalse); static Expr *simplify_function(Oid funcid, Oid result_type, List *args, - bool allow_inline, List *active_fns); + bool allow_inline, + eval_const_expressions_context *context); static Expr *evaluate_function(Oid funcid, Oid result_type, List *args, HeapTuple func_tuple); static Expr *inline_function(Oid funcid, Oid result_type, List *args, - HeapTuple func_tuple, List *active_fns); + 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, @@ -1070,18 +1080,101 @@ set_coercionform_dontcare_walker(Node *node, void *context) Node * eval_const_expressions(Node *node) { - /* - * The context for the mutator is a list of SQL functions being - * recursively simplified, so we start with an empty list. - */ - return eval_const_expressions_mutator(node, NIL); + eval_const_expressions_context context; + + context.active_fns = NIL; /* nothing being recursively simplified */ + context.estimate = false; /* safe transformations only */ + return eval_const_expressions_mutator(node, &context); +} + +/* + * 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 only such transform is to substitute values for Params, + * when a bound Param value has been made available by the caller of planner(). + * In future we might consider other things, such as reducing now() to current + * time. (XXX seems like there could be a lot of scope for ideas here... + * but we might need more volatility classifications ...) + */ +Node * +estimate_expression_value(Node *node) +{ + eval_const_expressions_context context; + + context.active_fns = NIL; /* nothing being recursively simplified */ + context.estimate = true; /* unsafe transformations OK */ + return eval_const_expressions_mutator(node, &context); } static Node * -eval_const_expressions_mutator(Node *node, List *active_fns) +eval_const_expressions_mutator(Node *node, + eval_const_expressions_context *context) { if (node == NULL) return NULL; + if (IsA(node, Param)) + { + Param *param = (Param *) node; + int thisParamKind = param->paramkind; + + /* OK to try to substitute value? */ + if (context->estimate && thisParamKind != PARAM_EXEC && + PlannerBoundParamList != NULL) + { + ParamListInfo paramList = PlannerBoundParamList; + bool matchFound = false; + + /* Search to see if we've been given a value for this Param */ + while (paramList->kind != PARAM_INVALID && !matchFound) + { + if (thisParamKind == paramList->kind) + { + switch (thisParamKind) + { + case PARAM_NAMED: + if (strcmp(paramList->name, param->paramname) == 0) + matchFound = true; + break; + case PARAM_NUM: + if (paramList->id == param->paramid) + matchFound = true; + break; + default: + elog(ERROR, "unrecognized paramkind: %d", + thisParamKind); + } + } + if (!matchFound) + paramList++; + } + if (matchFound) + { + /* + * Found it, so return a Const representing the param value. + * Note that we don't copy pass-by-ref datatypes, so the + * Const will only be valid as long as the bound parameter + * list exists. This is okay for intended uses of + * estimate_expression_value(). + */ + int16 typLen; + bool typByVal; + + get_typlenbyval(param->paramtype, &typLen, &typByVal); + return (Node *) makeConst(param->paramtype, + (int) typLen, + paramList->value, + paramList->isnull, + typByVal); + } + } + /* Not replaceable, so just copy the Param (no need to recurse) */ + return (Node *) copyObject(param); + } if (IsA(node, FuncExpr)) { FuncExpr *expr = (FuncExpr *) node; @@ -1096,14 +1189,14 @@ eval_const_expressions_mutator(Node *node, List *active_fns) */ args = (List *) expression_tree_mutator((Node *) expr->args, eval_const_expressions_mutator, - (void *) active_fns); + (void *) context); /* * Code for op/func reduction is pretty bulky, so split it out as * a separate function. */ simple = simplify_function(expr->funcid, expr->funcresulttype, args, - true, active_fns); + true, context); if (simple) /* successfully simplified it */ return (Node *) simple; @@ -1134,7 +1227,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns) */ args = (List *) expression_tree_mutator((Node *) expr->args, eval_const_expressions_mutator, - (void *) active_fns); + (void *) context); /* * Need to get OID of underlying function. Okay to scribble on @@ -1147,7 +1240,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns) * a separate function. */ simple = simplify_function(expr->opfuncid, expr->opresulttype, args, - true, active_fns); + true, context); if (simple) /* successfully simplified it */ return (Node *) simple; @@ -1182,7 +1275,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns) */ args = (List *) expression_tree_mutator((Node *) expr->args, eval_const_expressions_mutator, - (void *) active_fns); + (void *) context); /* * We must do our own check for NULLs because DistinctExpr has @@ -1226,7 +1319,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns) * as a separate function. */ simple = simplify_function(expr->opfuncid, expr->opresulttype, - args, false, active_fns); + args, false, context); if (simple) /* successfully simplified it */ { /* @@ -1267,7 +1360,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns) */ args = (List *) expression_tree_mutator((Node *) expr->args, eval_const_expressions_mutator, - (void *) active_fns); + (void *) context); switch (expr->boolop) { @@ -1360,7 +1453,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns) Node *arg; arg = eval_const_expressions_mutator((Node *) relabel->arg, - active_fns); + context); /* * If we find stacked RelabelTypes (eg, from foo :: int :: oid) we @@ -1424,7 +1517,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns) /* Simplify the test expression, if any */ newarg = eval_const_expressions_mutator((Node *) caseexpr->arg, - active_fns); + context); /* Simplify the WHEN clauses */ newargs = NIL; @@ -1434,7 +1527,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns) CaseWhen *casewhen = (CaseWhen *) expression_tree_mutator((Node *) lfirst(arg), eval_const_expressions_mutator, - (void *) active_fns); + (void *) context); Assert(IsA(casewhen, CaseWhen)); if (casewhen->expr == NULL || @@ -1464,7 +1557,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns) /* Simplify the default result */ defresult = eval_const_expressions_mutator((Node *) caseexpr->defresult, - active_fns); + context); /* * If no non-FALSE alternatives, CASE reduces to the default @@ -1494,7 +1587,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns) Node *e; e = eval_const_expressions_mutator((Node *) lfirst(element), - active_fns); + context); if (!IsA(e, Const)) all_const = false; newelems = lappend(newelems, e); @@ -1525,7 +1618,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns) Node *e; e = eval_const_expressions_mutator((Node *) lfirst(arg), - active_fns); + context); /* * We can remove null constants from the list. For a non-null @@ -1561,7 +1654,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns) Node *arg; arg = eval_const_expressions_mutator((Node *) fselect->arg, - active_fns); + context); if (arg && IsA(arg, Var) && ((Var *) arg)->varattno == InvalidAttrNumber) { @@ -1595,7 +1688,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns) * simplify constant expressions in its subscripts. */ return expression_tree_mutator(node, eval_const_expressions_mutator, - (void *) active_fns); + (void *) context); } /* @@ -1726,14 +1819,15 @@ simplify_and_arguments(List *args, bool *haveNull, bool *forceFalse) * * Inputs are the function OID, actual result type OID (which is needed for * polymorphic functions), and the pre-simplified argument list; - * also a list of already-active inline function expansions. + * also the context data for eval_const_expressions. * * Returns a simplified expression if successful, or NULL if cannot * simplify the function call. */ static Expr * simplify_function(Oid funcid, Oid result_type, List *args, - bool allow_inline, List *active_fns) + bool allow_inline, + eval_const_expressions_context *context) { HeapTuple func_tuple; Expr *newexpr; @@ -1756,7 +1850,7 @@ simplify_function(Oid funcid, Oid result_type, List *args, if (!newexpr && allow_inline) newexpr = inline_function(funcid, result_type, args, - func_tuple, active_fns); + func_tuple, context); ReleaseSysCache(func_tuple); @@ -1860,7 +1954,8 @@ evaluate_function(Oid funcid, Oid result_type, List *args, */ static Expr * inline_function(Oid funcid, Oid result_type, List *args, - HeapTuple func_tuple, List *active_fns) + HeapTuple func_tuple, + eval_const_expressions_context *context) { Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); bool polymorphic = false; @@ -1890,7 +1985,7 @@ inline_function(Oid funcid, Oid result_type, List *args, return NULL; /* Check for recursive function, and give up trying to expand if so */ - if (list_member_oid(active_fns, funcid)) + if (list_member_oid(context->active_fns, funcid)) return NULL; /* Check permission to call function (fail later, if not) */ @@ -2083,8 +2178,9 @@ inline_function(Oid funcid, Oid result_type, List *args, * Recursively try to simplify the modified expression. Here we must * add the current function to the context list of active functions. */ - newexpr = eval_const_expressions_mutator(newexpr, - lcons_oid(funcid, active_fns)); + context->active_fns = lcons_oid(funcid, context->active_fns); + newexpr = eval_const_expressions_mutator(newexpr, context); + context->active_fns = list_delete_first(context->active_fns); error_context_stack = sqlerrcontext.previous; |