mirror of
https://github.com/postgres/postgres.git
synced 2025-07-20 05:03:10 +03:00
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.
This commit is contained in:
@ -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";
|
||||
|
@ -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
|
||||
@ -422,6 +430,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
|
||||
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user