From: Tom Lane Date: Mon, 6 Jul 2009 18:26:30 +0000 (+0000) Subject: Fix set_append_rel_pathlist() to deal intelligently with cases where X-Git-Tag: REL8_5_ALPHA1~173 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9b27eab71c47f4f21d0e487b1d7ad277a85735e0;p=postgresql Fix set_append_rel_pathlist() to deal intelligently with cases where substituting a child rel's output expressions into the appendrel's restriction clauses yields a pseudoconstant restriction. We might be able to skip scanning that child rel entirely (if we get constant FALSE), or generate a one-time filter. 8.3 more or less accidentally generated plans that weren't completely stupid in these cases, but that was only because an extra recursive level of subquery_planner() always occurred and allowed const-simplification to happen. 8.4's ability to pull up appendrel members with non-Var outputs exposes the fact that we need to work harder here. Per gripe from Sergey Burladyan. --- diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 4a0a1012c0..e8e5b2ecb2 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.183 2009/06/11 14:48:58 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.184 2009/07/06 18:26:30 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -29,6 +29,7 @@ #include "optimizer/plancat.h" #include "optimizer/planner.h" #include "optimizer/prep.h" +#include "optimizer/restrictinfo.h" #include "optimizer/var.h" #include "parser/parse_clause.h" #include "parser/parsetree.h" @@ -318,6 +319,8 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, int childRTindex; RangeTblEntry *childRTE; RelOptInfo *childrel; + List *childquals; + Node *childqual; Path *childpath; ListCell *parentvars; ListCell *childvars; @@ -342,10 +345,34 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, * baserestrictinfo quals are needed before we can check for * constraint exclusion; so do that first and then check to see if we * can disregard this child. + * + * As of 8.4, the child rel's targetlist might contain non-Var + * expressions, which means that substitution into the quals + * could produce opportunities for const-simplification, and perhaps + * even pseudoconstant quals. To deal with this, we strip the + * RestrictInfo nodes, do the substitution, do const-simplification, + * and then reconstitute the RestrictInfo layer. */ - childrel->baserestrictinfo = (List *) - adjust_appendrel_attrs((Node *) rel->baserestrictinfo, - appinfo); + childquals = get_all_actual_clauses(rel->baserestrictinfo); + childquals = (List *) adjust_appendrel_attrs((Node *) childquals, + appinfo); + childqual = eval_const_expressions(root, (Node *) + make_ands_explicit(childquals)); + if (childqual && IsA(childqual, Const) && + (((Const *) childqual)->constisnull || + !DatumGetBool(((Const *) childqual)->constvalue))) + { + /* + * Restriction reduces to constant FALSE or constant NULL after + * substitution, so this child need not be scanned. + */ + set_dummy_rel_pathlist(childrel); + continue; + } + childquals = make_ands_implicit((Expr *) childqual); + childquals = make_restrictinfos_from_actual_clauses(root, + childquals); + childrel->baserestrictinfo = childquals; if (relation_excluded_by_constraints(root, childrel, childRTE)) { diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index b5e10a9180..956b9559f8 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -22,7 +22,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.171 2009/06/11 14:48:59 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.172 2009/07/06 18:26:30 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1636,57 +1636,7 @@ adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context) Assert(!IsA(node, SpecialJoinInfo)); Assert(!IsA(node, AppendRelInfo)); Assert(!IsA(node, PlaceHolderInfo)); - - /* - * We have to process RestrictInfo nodes specially. - */ - if (IsA(node, RestrictInfo)) - { - RestrictInfo *oldinfo = (RestrictInfo *) node; - RestrictInfo *newinfo = makeNode(RestrictInfo); - - /* Copy all flat-copiable fields */ - memcpy(newinfo, oldinfo, sizeof(RestrictInfo)); - - /* Recursively fix the clause itself */ - newinfo->clause = (Expr *) - adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context); - - /* and the modified version, if an OR clause */ - newinfo->orclause = (Expr *) - adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context); - - /* adjust relid sets too */ - newinfo->clause_relids = adjust_relid_set(oldinfo->clause_relids, - context->parent_relid, - context->child_relid); - newinfo->required_relids = adjust_relid_set(oldinfo->required_relids, - context->parent_relid, - context->child_relid); - newinfo->left_relids = adjust_relid_set(oldinfo->left_relids, - context->parent_relid, - context->child_relid); - newinfo->right_relids = adjust_relid_set(oldinfo->right_relids, - context->parent_relid, - context->child_relid); - - /* - * Reset cached derivative fields, since these might need to have - * different values when considering the child relation. - */ - newinfo->eval_cost.startup = -1; - newinfo->norm_selec = -1; - newinfo->outer_selec = -1; - newinfo->left_ec = NULL; - newinfo->right_ec = NULL; - newinfo->left_em = NULL; - newinfo->right_em = NULL; - newinfo->scansel_cache = NIL; - newinfo->left_bucketsize = -1; - newinfo->right_bucketsize = -1; - - return (Node *) newinfo; - } + Assert(!IsA(node, RestrictInfo)); /* * NOTE: we do not need to recurse into sublinks, because they should diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c index 47086a4bfc..bb06694c8a 100644 --- a/src/backend/optimizer/util/restrictinfo.c +++ b/src/backend/optimizer/util/restrictinfo.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.60 2009/06/11 14:48:59 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.61 2009/07/06 18:26:30 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -271,6 +271,57 @@ make_restrictinfo_from_bitmapqual(Path *bitmapqual, return result; } +/* + * make_restrictinfos_from_actual_clauses + * + * Given a list of implicitly-ANDed restriction clauses, produce a list + * of RestrictInfo nodes. This is used to reconstitute the RestrictInfo + * representation after doing transformations of a list of clauses. + * + * We assume that the clauses are relation-level restrictions and therefore + * we don't have to worry about is_pushed_down, outerjoin_delayed, or + * nullable_relids (these can be assumed true, false, and NULL, respectively). + * We do take care to recognize pseudoconstant clauses properly. + */ +List * +make_restrictinfos_from_actual_clauses(PlannerInfo *root, + List *clause_list) +{ + List *result = NIL; + ListCell *l; + + foreach(l, clause_list) + { + Expr *clause = (Expr *) lfirst(l); + bool pseudoconstant; + RestrictInfo *rinfo; + + /* + * It's pseudoconstant if it contains no Vars and no volatile + * functions. We probably can't see any sublinks here, so + * contain_var_clause() would likely be enough, but for safety + * use contain_vars_of_level() instead. + */ + pseudoconstant = + !contain_vars_of_level((Node *) clause, 0) && + !contain_volatile_functions((Node *) clause); + if (pseudoconstant) + { + /* tell createplan.c to check for gating quals */ + root->hasPseudoConstantQuals = true; + } + + rinfo = make_restrictinfo(clause, + true, + false, + pseudoconstant, + NULL, + NULL); + result = lappend(result, rinfo); + } + return result; +} + /* * make_restrictinfo_internal * @@ -481,6 +532,31 @@ get_actual_clauses(List *restrictinfo_list) return result; } +/* + * get_all_actual_clauses + * + * Returns a list containing the bare clauses from 'restrictinfo_list'. + * + * This loses the distinction between regular and pseudoconstant clauses, + * so be careful what you use it for. + */ +List * +get_all_actual_clauses(List *restrictinfo_list) +{ + List *result = NIL; + ListCell *l; + + foreach(l, restrictinfo_list) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); + + Assert(IsA(rinfo, RestrictInfo)); + + result = lappend(result, rinfo->clause); + } + return result; +} + /* * extract_actual_clauses * diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h index dcdc82ee1d..43de403758 100644 --- a/src/include/optimizer/restrictinfo.h +++ b/src/include/optimizer/restrictinfo.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.44 2009/05/09 22:51:41 tgl Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.45 2009/07/06 18:26:30 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,8 +30,11 @@ extern RestrictInfo *make_restrictinfo(Expr *clause, extern List *make_restrictinfo_from_bitmapqual(Path *bitmapqual, bool is_pushed_down, bool include_predicates); +extern List *make_restrictinfos_from_actual_clauses(PlannerInfo *root, + List *clause_list); extern bool restriction_is_or_clause(RestrictInfo *restrictinfo); extern List *get_actual_clauses(List *restrictinfo_list); +extern List *get_all_actual_clauses(List *restrictinfo_list); extern List *extract_actual_clauses(List *restrictinfo_list, bool pseudoconstant); extern void extract_actual_join_clauses(List *restrictinfo_list,