diff options
| author | Tom Lane <tgl@sss.pgh.pa.us> | 2006-07-01 18:38:33 +0000 |
|---|---|---|
| committer | Tom Lane <tgl@sss.pgh.pa.us> | 2006-07-01 18:38:33 +0000 |
| commit | cffd89ca736e485309cd51ae056f837bd7e683ad (patch) | |
| tree | 7ebf13ae5d921d074382d80be66a8d97e7a822e1 /src/backend/optimizer/path | |
| parent | 68628fc38ea7a3c72f6a813b0193d836731d9c10 (diff) | |
| download | postgresql-cffd89ca736e485309cd51ae056f837bd7e683ad.tar.gz | |
Revise the planner's handling of "pseudoconstant" WHERE clauses, that is
clauses containing no variables and no volatile functions. Such a clause
can be used as a one-time qual in a gating Result plan node, to suppress
plan execution entirely when it is false. Even when the clause is true,
putting it in a gating node wins by avoiding repeated evaluation of the
clause. In previous PG releases, query_planner() would do this for
pseudoconstant clauses appearing at the top level of the jointree, but
there was no ability to generate a gating Result deeper in the plan tree.
To fix it, get rid of the special case in query_planner(), and instead
process pseudoconstant clauses through the normal RestrictInfo qual
distribution mechanism. When a pseudoconstant clause is found attached to
a path node in create_plan(), pull it out and generate a gating Result at
that point. This requires special-casing pseudoconstants in selectivity
estimation and cost_qual_eval, but on the whole it's pretty clean.
It probably even makes the planner a bit faster than before for the normal
case of no pseudoconstants, since removing pull_constant_clauses saves one
useless traversal of the qual tree. Per gripe from Phil Frost.
Diffstat (limited to 'src/backend/optimizer/path')
| -rw-r--r-- | src/backend/optimizer/path/allpaths.c | 10 | ||||
| -rw-r--r-- | src/backend/optimizer/path/clausesel.c | 34 | ||||
| -rw-r--r-- | src/backend/optimizer/path/costsize.c | 31 | ||||
| -rw-r--r-- | src/backend/optimizer/path/indxpath.c | 28 |
4 files changed, 78 insertions, 25 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index de82c3df8d..ad55360a85 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.146 2006/05/02 04:34:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.147 2006/07/01 18:38:32 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -446,7 +446,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, * There are several cases where we cannot push down clauses. Restrictions * involving the subquery are checked by subquery_is_pushdown_safe(). * Restrictions on individual clauses are checked by - * qual_is_pushdown_safe(). + * qual_is_pushdown_safe(). Also, we don't want to push down + * pseudoconstant clauses; better to have the gating node above the + * subquery. * * Non-pushed-down clauses will get evaluated as qpquals of the * SubqueryScan node. @@ -466,7 +468,8 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); Node *clause = (Node *) rinfo->clause; - if (qual_is_pushdown_safe(subquery, rti, clause, differentTypes)) + if (!rinfo->pseudoconstant && + qual_is_pushdown_safe(subquery, rti, clause, differentTypes)) { /* Push it down */ subquery_push_qual(subquery, rte, rti, clause); @@ -1066,7 +1069,6 @@ print_path(PlannerInfo *root, Path *path, int indent) break; case T_ResultPath: ptype = "Result"; - subpath = ((ResultPath *) path)->subpath; break; case T_MaterialPath: ptype = "Material"; diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index 3ff02902f7..f595a3f07f 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.79 2006/03/07 01:00:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.80 2006/07/01 18:38:32 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -117,10 +117,18 @@ clauselist_selectivity(PlannerInfo *root, /* * Check for being passed a RestrictInfo. + * + * If it's a pseudoconstant RestrictInfo, then s2 is either 1.0 or + * 0.0; just use that rather than looking for range pairs. */ if (IsA(clause, RestrictInfo)) { rinfo = (RestrictInfo *) clause; + if (rinfo->pseudoconstant) + { + s1 = s1 * s2; + continue; + } clause = (Node *) rinfo->clause; } else @@ -423,6 +431,20 @@ clause_selectivity(PlannerInfo *root, rinfo = (RestrictInfo *) clause; /* + * If the clause is marked pseudoconstant, then it will be used as + * a gating qual and should not affect selectivity estimates; hence + * return 1.0. The only exception is that a constant FALSE may + * be taken as having selectivity 0.0, since it will surely mean + * no rows out of the plan. This case is simple enough that we + * need not bother caching the result. + */ + if (rinfo->pseudoconstant) + { + if (! IsA(rinfo->clause, Const)) + return s1; + } + + /* * If possible, cache the result of the selectivity calculation for * the clause. We can cache if varRelid is zero or the clause * contains only vars of that relid --- otherwise varRelid will affect @@ -509,7 +531,10 @@ clause_selectivity(PlannerInfo *root, else if (IsA(clause, Const)) { /* bool constant is pretty easy... */ - s1 = ((bool) ((Const *) clause)->constvalue) ? 1.0 : 0.0; + Const *con = (Const *) clause; + + s1 = con->constisnull ? 0.0 : + DatumGetBool(con->constvalue) ? 1.0 : 0.0; } else if (IsA(clause, Param)) { @@ -519,7 +544,10 @@ clause_selectivity(PlannerInfo *root, if (IsA(subst, Const)) { /* bool constant is pretty easy... */ - s1 = ((bool) ((Const *) subst)->constvalue) ? 1.0 : 0.0; + Const *con = (Const *) subst; + + s1 = con->constisnull ? 0.0 : + DatumGetBool(con->constvalue) ? 1.0 : 0.0; } else { diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index fa8d06707b..eec165ab5d 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -54,7 +54,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.158 2006/06/06 17:59:57 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.159 2006/07/01 18:38:32 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1604,20 +1604,29 @@ cost_qual_eval(QualCost *cost, List *quals) * routine's use, so that it's not necessary to evaluate the qual * clause's cost more than once. If the clause's cost hasn't been * computed yet, the field's startup value will contain -1. + * + * If the RestrictInfo is marked pseudoconstant, it will be tested + * only once, so treat its cost as all startup cost. */ if (qual && IsA(qual, RestrictInfo)) { - RestrictInfo *restrictinfo = (RestrictInfo *) qual; + RestrictInfo *rinfo = (RestrictInfo *) qual; - if (restrictinfo->eval_cost.startup < 0) + if (rinfo->eval_cost.startup < 0) { - restrictinfo->eval_cost.startup = 0; - restrictinfo->eval_cost.per_tuple = 0; - cost_qual_eval_walker((Node *) restrictinfo->clause, - &restrictinfo->eval_cost); + rinfo->eval_cost.startup = 0; + rinfo->eval_cost.per_tuple = 0; + cost_qual_eval_walker((Node *) rinfo->clause, + &rinfo->eval_cost); + if (rinfo->pseudoconstant) + { + /* count one execution during startup */ + rinfo->eval_cost.startup += rinfo->eval_cost.per_tuple; + rinfo->eval_cost.per_tuple = 0; + } } - cost->startup += restrictinfo->eval_cost.startup; - cost->per_tuple += restrictinfo->eval_cost.per_tuple; + cost->startup += rinfo->eval_cost.startup; + cost->per_tuple += rinfo->eval_cost.per_tuple; } else { @@ -1876,7 +1885,9 @@ set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel, * * If we are doing an outer join, take that into account: the output must * be at least as large as the non-nullable input. (Is there any chance - * of being even smarter?) + * of being even smarter?) (XXX this is not really right, because it + * assumes all the restriction clauses are join clauses; we should figure + * pushed-down clauses separately.) * * For JOIN_IN and variants, the Cartesian product is figured with respect * to a unique-ified input, and then we can clamp to the size of the other diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 75f2487757..6e6f4ac3a7 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.208 2006/06/07 17:08:07 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.209 2006/07/01 18:38:32 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -998,6 +998,15 @@ match_clause_to_indexcol(IndexOptInfo *index, Oid expr_op; bool plain_op; + /* + * Never match pseudoconstants to indexes. (Normally this could not + * happen anyway, since a pseudoconstant clause couldn't contain a + * Var, but what if someone builds an expression index on a constant? + * It's not totally unreasonable to do so with a partial index, either.) + */ + if (rinfo->pseudoconstant) + return false; + /* First check for boolean-index cases. */ if (IsBooleanOpclass(opclass)) { @@ -2212,6 +2221,7 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups) make_restrictinfo(boolqual, true, false, + false, NULL)); continue; } @@ -2577,7 +2587,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo, matching_cols); rc->rargs = list_truncate((List *) copyObject(clause->rargs), matching_cols); - return make_restrictinfo((Expr *) rc, true, false, NULL); + return make_restrictinfo((Expr *) rc, true, false, false, NULL); } else { @@ -2586,7 +2596,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo, opexpr = make_opclause(linitial_oid(new_ops), BOOLOID, false, copyObject(linitial(clause->largs)), copyObject(linitial(clause->rargs))); - return make_restrictinfo(opexpr, true, false, NULL); + return make_restrictinfo(opexpr, true, false, false, NULL); } } @@ -2678,7 +2688,7 @@ prefix_quals(Node *leftop, Oid opclass, elog(ERROR, "no = operator for opclass %u", opclass); expr = make_opclause(oproid, BOOLOID, false, (Expr *) leftop, (Expr *) prefix_const); - result = list_make1(make_restrictinfo(expr, true, false, NULL)); + result = list_make1(make_restrictinfo(expr, true, false, false, NULL)); return result; } @@ -2693,7 +2703,7 @@ prefix_quals(Node *leftop, Oid opclass, elog(ERROR, "no >= operator for opclass %u", opclass); expr = make_opclause(oproid, BOOLOID, false, (Expr *) leftop, (Expr *) prefix_const); - result = list_make1(make_restrictinfo(expr, true, false, NULL)); + result = list_make1(make_restrictinfo(expr, true, false, false, NULL)); /*------- * If we can create a string larger than the prefix, we can say @@ -2709,7 +2719,8 @@ prefix_quals(Node *leftop, Oid opclass, elog(ERROR, "no < operator for opclass %u", opclass); expr = make_opclause(oproid, BOOLOID, false, (Expr *) leftop, (Expr *) greaterstr); - result = lappend(result, make_restrictinfo(expr, true, false, NULL)); + result = lappend(result, + make_restrictinfo(expr, true, false, false, NULL)); } return result; @@ -2772,7 +2783,7 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop) (Expr *) leftop, (Expr *) makeConst(datatype, -1, opr1right, false, false)); - result = list_make1(make_restrictinfo(expr, true, false, NULL)); + result = list_make1(make_restrictinfo(expr, true, false, false, NULL)); /* create clause "key <= network_scan_last( rightop )" */ @@ -2787,7 +2798,8 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop) (Expr *) leftop, (Expr *) makeConst(datatype, -1, opr2right, false, false)); - result = lappend(result, make_restrictinfo(expr, true, false, NULL)); + result = lappend(result, + make_restrictinfo(expr, true, false, false, NULL)); return result; } |
