From 823e2aa5477342875b73b9941bdc9ed28d3ebe01 Mon Sep 17 00:00:00 2001 From: Marina Polyakova Date: Mon, 15 May 2017 16:05:38 +0300 Subject: [PATCH 3/3] Precalculate stable functions, costs v2 Now in Postgresql only immutable functions are precalculated; stable functions are calculated for every row so in fact they don't differ from volatile functions. This patch includes: - cost changes for cached expressions (according to their behaviour) --- src/backend/optimizer/path/costsize.c | 89 ++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 22 deletions(-) diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 52643d0..505772a 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -140,6 +140,7 @@ static MergeScanSelCache *cached_scansel(PlannerInfo *root, PathKey *pathkey); static void cost_rescan(PlannerInfo *root, Path *path, Cost *rescan_startup_cost, Cost *rescan_total_cost); +static double cost_eval_cacheable_expr_per_tuple(Node *node); static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context); static void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel, ParamPathInfo *param_info, @@ -3464,6 +3465,59 @@ cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root) *cost = context.total; } +/* + * cost_eval_cacheable_expr_per_tuple + * Evaluate per tuple cost for expressions that can be cacheable. + * + * This function was created to not duplicate code for some expression and + * cached some expression. + */ +static double +cost_eval_cacheable_expr_per_tuple(Node *node) +{ + double result; + + /* + * For each operator or function node in the given tree, we charge the + * estimated execution cost given by pg_proc.procost (remember to multiply + * this by cpu_operator_cost). + */ + if (IsA(node, FuncExpr)) + { + result = get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost; + } + else if (IsA(node, OpExpr) || + IsA(node, DistinctExpr) || + IsA(node, NullIfExpr)) + { + OpExpr *opexpr = (OpExpr *) node; + + /* rely on struct equivalence to treat these all alike */ + set_opfuncid(opexpr); + + result = get_func_cost(opexpr->opfuncid) * cpu_operator_cost; + } + else if (IsA(node, ScalarArrayOpExpr)) + { + /* + * Estimate that the operator will be applied to about half of the + * array elements before the answer is determined. + */ + ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node; + Node *arraynode = (Node *) lsecond(saop->args); + + set_sa_opfuncid(saop); + result = get_func_cost(saop->opfuncid) * cpu_operator_cost * + estimate_array_length(arraynode) * 0.5; + } + else + { + elog(ERROR, "non cacheable expression node type: %d", (int) nodeTag(node)); + } + + return result; +} + static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) { @@ -3537,32 +3591,23 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) * moreover, since our rowcount estimates for functions tend to be pretty * phony, the results would also be pretty phony. */ - if (IsA(node, FuncExpr)) + if (IsA(node, FuncExpr) || + IsA(node, OpExpr) || + IsA(node, DistinctExpr) || + IsA(node, NullIfExpr) || + IsA(node, ScalarArrayOpExpr)) { - context->total.per_tuple += - get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost; + context->total.per_tuple += cost_eval_cacheable_expr_per_tuple(node); } - else if (IsA(node, OpExpr) || - IsA(node, DistinctExpr) || - IsA(node, NullIfExpr)) - { - /* rely on struct equivalence to treat these all alike */ - set_opfuncid((OpExpr *) node); - context->total.per_tuple += - get_func_cost(((OpExpr *) node)->opfuncid) * cpu_operator_cost; - } - else if (IsA(node, ScalarArrayOpExpr)) - { + else if (IsA(node, CachedExpr)) + { /* - * Estimate that the operator will be applied to about half of the - * array elements before the answer is determined. + * Calculate subexpression cost per tuple as usual and add it to startup + * cost (because subexpression will be executed only once for all + * tuples). */ - ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node; - Node *arraynode = (Node *) lsecond(saop->args); - - set_sa_opfuncid(saop); - context->total.per_tuple += get_func_cost(saop->opfuncid) * - cpu_operator_cost * estimate_array_length(arraynode) * 0.5; + context->total.startup += cost_eval_cacheable_expr_per_tuple( + get_subexpr((CachedExpr *) node)); } else if (IsA(node, Aggref) || IsA(node, WindowFunc)) -- 1.9.1