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:
parent
68628fc38e
commit
cffd89ca73
@ -15,7 +15,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.337 2006/06/27 03:43:19 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.338 2006/07/01 18:38:32 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1264,6 +1264,7 @@ _copyRestrictInfo(RestrictInfo *from)
|
|||||||
COPY_SCALAR_FIELD(is_pushed_down);
|
COPY_SCALAR_FIELD(is_pushed_down);
|
||||||
COPY_SCALAR_FIELD(outerjoin_delayed);
|
COPY_SCALAR_FIELD(outerjoin_delayed);
|
||||||
COPY_SCALAR_FIELD(can_join);
|
COPY_SCALAR_FIELD(can_join);
|
||||||
|
COPY_SCALAR_FIELD(pseudoconstant);
|
||||||
COPY_BITMAPSET_FIELD(clause_relids);
|
COPY_BITMAPSET_FIELD(clause_relids);
|
||||||
COPY_BITMAPSET_FIELD(required_relids);
|
COPY_BITMAPSET_FIELD(required_relids);
|
||||||
COPY_BITMAPSET_FIELD(left_relids);
|
COPY_BITMAPSET_FIELD(left_relids);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.274 2006/04/30 18:30:39 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.275 2006/07/01 18:38:32 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Every node type that can appear in stored rules' parsetrees *must*
|
* Every node type that can appear in stored rules' parsetrees *must*
|
||||||
@ -1107,8 +1107,7 @@ _outResultPath(StringInfo str, ResultPath *node)
|
|||||||
|
|
||||||
_outPathInfo(str, (Path *) node);
|
_outPathInfo(str, (Path *) node);
|
||||||
|
|
||||||
WRITE_NODE_FIELD(subpath);
|
WRITE_NODE_FIELD(quals);
|
||||||
WRITE_NODE_FIELD(constantqual);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1185,6 +1184,7 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node)
|
|||||||
WRITE_BOOL_FIELD(hasJoinRTEs);
|
WRITE_BOOL_FIELD(hasJoinRTEs);
|
||||||
WRITE_BOOL_FIELD(hasOuterJoins);
|
WRITE_BOOL_FIELD(hasOuterJoins);
|
||||||
WRITE_BOOL_FIELD(hasHavingQual);
|
WRITE_BOOL_FIELD(hasHavingQual);
|
||||||
|
WRITE_BOOL_FIELD(hasPseudoConstantQuals);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1252,6 +1252,7 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
|
|||||||
WRITE_BOOL_FIELD(is_pushed_down);
|
WRITE_BOOL_FIELD(is_pushed_down);
|
||||||
WRITE_BOOL_FIELD(outerjoin_delayed);
|
WRITE_BOOL_FIELD(outerjoin_delayed);
|
||||||
WRITE_BOOL_FIELD(can_join);
|
WRITE_BOOL_FIELD(can_join);
|
||||||
|
WRITE_BOOL_FIELD(pseudoconstant);
|
||||||
WRITE_BITMAPSET_FIELD(clause_relids);
|
WRITE_BITMAPSET_FIELD(clause_relids);
|
||||||
WRITE_BITMAPSET_FIELD(required_relids);
|
WRITE_BITMAPSET_FIELD(required_relids);
|
||||||
WRITE_BITMAPSET_FIELD(left_relids);
|
WRITE_BITMAPSET_FIELD(left_relids);
|
||||||
|
@ -329,7 +329,7 @@ RelOptInfo - a relation or joined relations
|
|||||||
BitmapHeapPath - top of a bitmapped index scan
|
BitmapHeapPath - top of a bitmapped index scan
|
||||||
TidPath - scan by CTID
|
TidPath - scan by CTID
|
||||||
AppendPath - append multiple subpaths together
|
AppendPath - append multiple subpaths together
|
||||||
ResultPath - a Result plan node (used for variable-free tlist or qual)
|
ResultPath - a Result plan node (used for FROM-less SELECT)
|
||||||
MaterialPath - a Material plan node
|
MaterialPath - a Material plan node
|
||||||
UniquePath - remove duplicate rows
|
UniquePath - remove duplicate rows
|
||||||
NestPath - nested-loop joins
|
NestPath - nested-loop joins
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* There are several cases where we cannot push down clauses. Restrictions
|
||||||
* involving the subquery are checked by subquery_is_pushdown_safe().
|
* involving the subquery are checked by subquery_is_pushdown_safe().
|
||||||
* Restrictions on individual clauses are checked by
|
* 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
|
* Non-pushed-down clauses will get evaluated as qpquals of the
|
||||||
* SubqueryScan node.
|
* SubqueryScan node.
|
||||||
@ -466,7 +468,8 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
||||||
Node *clause = (Node *) rinfo->clause;
|
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 */
|
/* Push it down */
|
||||||
subquery_push_qual(subquery, rte, rti, clause);
|
subquery_push_qual(subquery, rte, rti, clause);
|
||||||
@ -1066,7 +1069,6 @@ print_path(PlannerInfo *root, Path *path, int indent)
|
|||||||
break;
|
break;
|
||||||
case T_ResultPath:
|
case T_ResultPath:
|
||||||
ptype = "Result";
|
ptype = "Result";
|
||||||
subpath = ((ResultPath *) path)->subpath;
|
|
||||||
break;
|
break;
|
||||||
case T_MaterialPath:
|
case T_MaterialPath:
|
||||||
ptype = "Material";
|
ptype = "Material";
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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.
|
* 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))
|
if (IsA(clause, RestrictInfo))
|
||||||
{
|
{
|
||||||
rinfo = (RestrictInfo *) clause;
|
rinfo = (RestrictInfo *) clause;
|
||||||
|
if (rinfo->pseudoconstant)
|
||||||
|
{
|
||||||
|
s1 = s1 * s2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
clause = (Node *) rinfo->clause;
|
clause = (Node *) rinfo->clause;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -422,6 +430,20 @@ clause_selectivity(PlannerInfo *root,
|
|||||||
{
|
{
|
||||||
rinfo = (RestrictInfo *) clause;
|
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
|
* If possible, cache the result of the selectivity calculation for
|
||||||
* the clause. We can cache if varRelid is zero or the clause
|
* 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))
|
else if (IsA(clause, Const))
|
||||||
{
|
{
|
||||||
/* bool constant is pretty easy... */
|
/* 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))
|
else if (IsA(clause, Param))
|
||||||
{
|
{
|
||||||
@ -519,7 +544,10 @@ clause_selectivity(PlannerInfo *root,
|
|||||||
if (IsA(subst, Const))
|
if (IsA(subst, Const))
|
||||||
{
|
{
|
||||||
/* bool constant is pretty easy... */
|
/* 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
|
else
|
||||||
{
|
{
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* 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
|
* clause's cost more than once. If the clause's cost hasn't been
|
||||||
* computed yet, the field's startup value will contain -1.
|
* 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))
|
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;
|
rinfo->eval_cost.startup = 0;
|
||||||
restrictinfo->eval_cost.per_tuple = 0;
|
rinfo->eval_cost.per_tuple = 0;
|
||||||
cost_qual_eval_walker((Node *) restrictinfo->clause,
|
cost_qual_eval_walker((Node *) rinfo->clause,
|
||||||
&restrictinfo->eval_cost);
|
&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
|
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
|
* 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
|
* 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
|
* 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
|
* to a unique-ified input, and then we can clamp to the size of the other
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
Oid expr_op;
|
||||||
bool plain_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. */
|
/* First check for boolean-index cases. */
|
||||||
if (IsBooleanOpclass(opclass))
|
if (IsBooleanOpclass(opclass))
|
||||||
{
|
{
|
||||||
@ -2212,6 +2221,7 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
|
|||||||
make_restrictinfo(boolqual,
|
make_restrictinfo(boolqual,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
NULL));
|
NULL));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2577,7 +2587,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
|||||||
matching_cols);
|
matching_cols);
|
||||||
rc->rargs = list_truncate((List *) copyObject(clause->rargs),
|
rc->rargs = list_truncate((List *) copyObject(clause->rargs),
|
||||||
matching_cols);
|
matching_cols);
|
||||||
return make_restrictinfo((Expr *) rc, true, false, NULL);
|
return make_restrictinfo((Expr *) rc, true, false, false, NULL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -2586,7 +2596,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
|||||||
opexpr = make_opclause(linitial_oid(new_ops), BOOLOID, false,
|
opexpr = make_opclause(linitial_oid(new_ops), BOOLOID, false,
|
||||||
copyObject(linitial(clause->largs)),
|
copyObject(linitial(clause->largs)),
|
||||||
copyObject(linitial(clause->rargs)));
|
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);
|
elog(ERROR, "no = operator for opclass %u", opclass);
|
||||||
expr = make_opclause(oproid, BOOLOID, false,
|
expr = make_opclause(oproid, BOOLOID, false,
|
||||||
(Expr *) leftop, (Expr *) prefix_const);
|
(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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2693,7 +2703,7 @@ prefix_quals(Node *leftop, Oid opclass,
|
|||||||
elog(ERROR, "no >= operator for opclass %u", opclass);
|
elog(ERROR, "no >= operator for opclass %u", opclass);
|
||||||
expr = make_opclause(oproid, BOOLOID, false,
|
expr = make_opclause(oproid, BOOLOID, false,
|
||||||
(Expr *) leftop, (Expr *) prefix_const);
|
(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
|
* 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);
|
elog(ERROR, "no < operator for opclass %u", opclass);
|
||||||
expr = make_opclause(oproid, BOOLOID, false,
|
expr = make_opclause(oproid, BOOLOID, false,
|
||||||
(Expr *) leftop, (Expr *) greaterstr);
|
(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;
|
return result;
|
||||||
@ -2772,7 +2783,7 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop)
|
|||||||
(Expr *) leftop,
|
(Expr *) leftop,
|
||||||
(Expr *) makeConst(datatype, -1, opr1right,
|
(Expr *) makeConst(datatype, -1, opr1right,
|
||||||
false, false));
|
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 )" */
|
/* 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 *) leftop,
|
||||||
(Expr *) makeConst(datatype, -1, opr2right,
|
(Expr *) makeConst(datatype, -1, opr2right,
|
||||||
false, false));
|
false, false));
|
||||||
result = lappend(result, make_restrictinfo(expr, true, false, NULL));
|
result = lappend(result,
|
||||||
|
make_restrictinfo(expr, true, false, false, NULL));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.211 2006/05/18 18:57:31 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.212 2006/07/01 18:38:33 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -35,11 +35,12 @@
|
|||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
|
||||||
static Scan *create_scan_plan(PlannerInfo *root, Path *best_path);
|
static Plan *create_scan_plan(PlannerInfo *root, Path *best_path);
|
||||||
static List *build_relation_tlist(RelOptInfo *rel);
|
static List *build_relation_tlist(RelOptInfo *rel);
|
||||||
static bool use_physical_tlist(RelOptInfo *rel);
|
static bool use_physical_tlist(RelOptInfo *rel);
|
||||||
static void disuse_physical_tlist(Plan *plan, Path *path);
|
static void disuse_physical_tlist(Plan *plan, Path *path);
|
||||||
static Join *create_join_plan(PlannerInfo *root, JoinPath *best_path);
|
static Plan *create_gating_plan(PlannerInfo *root, Plan *plan, List *quals);
|
||||||
|
static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path);
|
||||||
static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path);
|
static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path);
|
||||||
static Result *create_result_plan(PlannerInfo *root, ResultPath *best_path);
|
static Result *create_result_plan(PlannerInfo *root, ResultPath *best_path);
|
||||||
static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path);
|
static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path);
|
||||||
@ -74,6 +75,7 @@ static void fix_indexqual_references(List *indexquals, IndexPath *index_path,
|
|||||||
static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index,
|
static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index,
|
||||||
Oid *opclass);
|
Oid *opclass);
|
||||||
static List *get_switched_clauses(List *clauses, Relids outerrelids);
|
static List *get_switched_clauses(List *clauses, Relids outerrelids);
|
||||||
|
static List *order_qual_clauses(PlannerInfo *root, List *clauses);
|
||||||
static void copy_path_costsize(Plan *dest, Path *src);
|
static void copy_path_costsize(Plan *dest, Path *src);
|
||||||
static void copy_plan_costsize(Plan *dest, Plan *src);
|
static void copy_plan_costsize(Plan *dest, Plan *src);
|
||||||
static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
|
static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
|
||||||
@ -146,16 +148,16 @@ create_plan(PlannerInfo *root, Path *best_path)
|
|||||||
case T_TidScan:
|
case T_TidScan:
|
||||||
case T_SubqueryScan:
|
case T_SubqueryScan:
|
||||||
case T_FunctionScan:
|
case T_FunctionScan:
|
||||||
plan = (Plan *) create_scan_plan(root, best_path);
|
plan = create_scan_plan(root, best_path);
|
||||||
break;
|
break;
|
||||||
case T_HashJoin:
|
case T_HashJoin:
|
||||||
case T_MergeJoin:
|
case T_MergeJoin:
|
||||||
case T_NestLoop:
|
case T_NestLoop:
|
||||||
plan = (Plan *) create_join_plan(root,
|
plan = create_join_plan(root,
|
||||||
(JoinPath *) best_path);
|
(JoinPath *) best_path);
|
||||||
break;
|
break;
|
||||||
case T_Append:
|
case T_Append:
|
||||||
plan = (Plan *) create_append_plan(root,
|
plan = create_append_plan(root,
|
||||||
(AppendPath *) best_path);
|
(AppendPath *) best_path);
|
||||||
break;
|
break;
|
||||||
case T_Result:
|
case T_Result:
|
||||||
@ -167,7 +169,7 @@ create_plan(PlannerInfo *root, Path *best_path)
|
|||||||
(MaterialPath *) best_path);
|
(MaterialPath *) best_path);
|
||||||
break;
|
break;
|
||||||
case T_Unique:
|
case T_Unique:
|
||||||
plan = (Plan *) create_unique_plan(root,
|
plan = create_unique_plan(root,
|
||||||
(UniquePath *) best_path);
|
(UniquePath *) best_path);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -183,16 +185,14 @@ create_plan(PlannerInfo *root, Path *best_path)
|
|||||||
/*
|
/*
|
||||||
* create_scan_plan
|
* create_scan_plan
|
||||||
* Create a scan plan for the parent relation of 'best_path'.
|
* Create a scan plan for the parent relation of 'best_path'.
|
||||||
*
|
|
||||||
* Returns a Plan node.
|
|
||||||
*/
|
*/
|
||||||
static Scan *
|
static Plan *
|
||||||
create_scan_plan(PlannerInfo *root, Path *best_path)
|
create_scan_plan(PlannerInfo *root, Path *best_path)
|
||||||
{
|
{
|
||||||
RelOptInfo *rel = best_path->parent;
|
RelOptInfo *rel = best_path->parent;
|
||||||
List *tlist;
|
List *tlist;
|
||||||
List *scan_clauses;
|
List *scan_clauses;
|
||||||
Scan *plan;
|
Plan *plan;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For table scans, rather than using the relation targetlist (which is
|
* For table scans, rather than using the relation targetlist (which is
|
||||||
@ -213,22 +213,23 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
|
|||||||
tlist = build_relation_tlist(rel);
|
tlist = build_relation_tlist(rel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extract the relevant restriction clauses from the parent relation; the
|
* Extract the relevant restriction clauses from the parent relation.
|
||||||
* executor must apply all these restrictions during the scan.
|
* The executor must apply all these restrictions during the scan,
|
||||||
|
* except for pseudoconstants which we'll take care of below.
|
||||||
*/
|
*/
|
||||||
scan_clauses = rel->baserestrictinfo;
|
scan_clauses = rel->baserestrictinfo;
|
||||||
|
|
||||||
switch (best_path->pathtype)
|
switch (best_path->pathtype)
|
||||||
{
|
{
|
||||||
case T_SeqScan:
|
case T_SeqScan:
|
||||||
plan = (Scan *) create_seqscan_plan(root,
|
plan = (Plan *) create_seqscan_plan(root,
|
||||||
best_path,
|
best_path,
|
||||||
tlist,
|
tlist,
|
||||||
scan_clauses);
|
scan_clauses);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_IndexScan:
|
case T_IndexScan:
|
||||||
plan = (Scan *) create_indexscan_plan(root,
|
plan = (Plan *) create_indexscan_plan(root,
|
||||||
(IndexPath *) best_path,
|
(IndexPath *) best_path,
|
||||||
tlist,
|
tlist,
|
||||||
scan_clauses,
|
scan_clauses,
|
||||||
@ -236,28 +237,28 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case T_BitmapHeapScan:
|
case T_BitmapHeapScan:
|
||||||
plan = (Scan *) create_bitmap_scan_plan(root,
|
plan = (Plan *) create_bitmap_scan_plan(root,
|
||||||
(BitmapHeapPath *) best_path,
|
(BitmapHeapPath *) best_path,
|
||||||
tlist,
|
tlist,
|
||||||
scan_clauses);
|
scan_clauses);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_TidScan:
|
case T_TidScan:
|
||||||
plan = (Scan *) create_tidscan_plan(root,
|
plan = (Plan *) create_tidscan_plan(root,
|
||||||
(TidPath *) best_path,
|
(TidPath *) best_path,
|
||||||
tlist,
|
tlist,
|
||||||
scan_clauses);
|
scan_clauses);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_SubqueryScan:
|
case T_SubqueryScan:
|
||||||
plan = (Scan *) create_subqueryscan_plan(root,
|
plan = (Plan *) create_subqueryscan_plan(root,
|
||||||
best_path,
|
best_path,
|
||||||
tlist,
|
tlist,
|
||||||
scan_clauses);
|
scan_clauses);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_FunctionScan:
|
case T_FunctionScan:
|
||||||
plan = (Scan *) create_functionscan_plan(root,
|
plan = (Plan *) create_functionscan_plan(root,
|
||||||
best_path,
|
best_path,
|
||||||
tlist,
|
tlist,
|
||||||
scan_clauses);
|
scan_clauses);
|
||||||
@ -270,6 +271,14 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there are any pseudoconstant clauses attached to this node,
|
||||||
|
* insert a gating Result node that evaluates the pseudoconstants
|
||||||
|
* as one-time quals.
|
||||||
|
*/
|
||||||
|
if (root->hasPseudoConstantQuals)
|
||||||
|
plan = create_gating_plan(root, plan, scan_clauses);
|
||||||
|
|
||||||
return plan;
|
return plan;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,19 +374,54 @@ disuse_physical_tlist(Plan *plan, Path *path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create_gating_plan
|
||||||
|
* Deal with pseudoconstant qual clauses
|
||||||
|
*
|
||||||
|
* If the node's quals list includes any pseudoconstant quals, put them
|
||||||
|
* into a gating Result node atop the already-built plan. Otherwise,
|
||||||
|
* return the plan as-is.
|
||||||
|
*
|
||||||
|
* Note that we don't change cost or size estimates when doing gating.
|
||||||
|
* The costs of qual eval were already folded into the plan's startup cost.
|
||||||
|
* Leaving the size alone amounts to assuming that the gating qual will
|
||||||
|
* succeed, which is the conservative estimate for planning upper queries.
|
||||||
|
* We certainly don't want to assume the output size is zero (unless the
|
||||||
|
* gating qual is actually constant FALSE, and that case is dealt with in
|
||||||
|
* clausesel.c). Interpolating between the two cases is silly, because
|
||||||
|
* it doesn't reflect what will really happen at runtime, and besides which
|
||||||
|
* in most cases we have only a very bad idea of the probability of the gating
|
||||||
|
* qual being true.
|
||||||
|
*/
|
||||||
|
static Plan *
|
||||||
|
create_gating_plan(PlannerInfo *root, Plan *plan, List *quals)
|
||||||
|
{
|
||||||
|
List *pseudoconstants;
|
||||||
|
|
||||||
|
/* Pull out any pseudoconstant quals from the RestrictInfo list */
|
||||||
|
pseudoconstants = extract_actual_clauses(quals, true);
|
||||||
|
|
||||||
|
if (!pseudoconstants)
|
||||||
|
return plan;
|
||||||
|
|
||||||
|
pseudoconstants = order_qual_clauses(root, pseudoconstants);
|
||||||
|
|
||||||
|
return (Plan *) make_result((List *) copyObject(plan->targetlist),
|
||||||
|
(Node *) pseudoconstants,
|
||||||
|
plan);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* create_join_plan
|
* create_join_plan
|
||||||
* Create a join plan for 'best_path' and (recursively) plans for its
|
* Create a join plan for 'best_path' and (recursively) plans for its
|
||||||
* inner and outer paths.
|
* inner and outer paths.
|
||||||
*
|
|
||||||
* Returns a Plan node.
|
|
||||||
*/
|
*/
|
||||||
static Join *
|
static Plan *
|
||||||
create_join_plan(PlannerInfo *root, JoinPath *best_path)
|
create_join_plan(PlannerInfo *root, JoinPath *best_path)
|
||||||
{
|
{
|
||||||
Plan *outer_plan;
|
Plan *outer_plan;
|
||||||
Plan *inner_plan;
|
Plan *inner_plan;
|
||||||
Join *plan;
|
Plan *plan;
|
||||||
|
|
||||||
outer_plan = create_plan(root, best_path->outerjoinpath);
|
outer_plan = create_plan(root, best_path->outerjoinpath);
|
||||||
inner_plan = create_plan(root, best_path->innerjoinpath);
|
inner_plan = create_plan(root, best_path->innerjoinpath);
|
||||||
@ -385,19 +429,19 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
|
|||||||
switch (best_path->path.pathtype)
|
switch (best_path->path.pathtype)
|
||||||
{
|
{
|
||||||
case T_MergeJoin:
|
case T_MergeJoin:
|
||||||
plan = (Join *) create_mergejoin_plan(root,
|
plan = (Plan *) create_mergejoin_plan(root,
|
||||||
(MergePath *) best_path,
|
(MergePath *) best_path,
|
||||||
outer_plan,
|
outer_plan,
|
||||||
inner_plan);
|
inner_plan);
|
||||||
break;
|
break;
|
||||||
case T_HashJoin:
|
case T_HashJoin:
|
||||||
plan = (Join *) create_hashjoin_plan(root,
|
plan = (Plan *) create_hashjoin_plan(root,
|
||||||
(HashPath *) best_path,
|
(HashPath *) best_path,
|
||||||
outer_plan,
|
outer_plan,
|
||||||
inner_plan);
|
inner_plan);
|
||||||
break;
|
break;
|
||||||
case T_NestLoop:
|
case T_NestLoop:
|
||||||
plan = (Join *) create_nestloop_plan(root,
|
plan = (Plan *) create_nestloop_plan(root,
|
||||||
(NestPath *) best_path,
|
(NestPath *) best_path,
|
||||||
outer_plan,
|
outer_plan,
|
||||||
inner_plan);
|
inner_plan);
|
||||||
@ -409,6 +453,14 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there are any pseudoconstant clauses attached to this node,
|
||||||
|
* insert a gating Result node that evaluates the pseudoconstants
|
||||||
|
* as one-time quals.
|
||||||
|
*/
|
||||||
|
if (root->hasPseudoConstantQuals)
|
||||||
|
plan = create_gating_plan(root, plan, best_path->joinrestrictinfo);
|
||||||
|
|
||||||
#ifdef NOT_USED
|
#ifdef NOT_USED
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -473,34 +525,24 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* create_result_plan
|
* create_result_plan
|
||||||
* Create a Result plan for 'best_path' and (recursively) plans
|
* Create a Result plan for 'best_path'.
|
||||||
* for its subpaths.
|
* This is only used for the case of a query with an empty jointree.
|
||||||
*
|
*
|
||||||
* Returns a Plan node.
|
* Returns a Plan node.
|
||||||
*/
|
*/
|
||||||
static Result *
|
static Result *
|
||||||
create_result_plan(PlannerInfo *root, ResultPath *best_path)
|
create_result_plan(PlannerInfo *root, ResultPath *best_path)
|
||||||
{
|
{
|
||||||
Result *plan;
|
|
||||||
List *tlist;
|
List *tlist;
|
||||||
List *constclauses;
|
List *quals;
|
||||||
Plan *subplan;
|
|
||||||
|
|
||||||
if (best_path->path.parent)
|
/* The tlist will be installed later, since we have no RelOptInfo */
|
||||||
tlist = build_relation_tlist(best_path->path.parent);
|
Assert(best_path->path.parent == NULL);
|
||||||
else
|
tlist = NIL;
|
||||||
tlist = NIL; /* will be filled in later */
|
|
||||||
|
|
||||||
if (best_path->subpath)
|
quals = order_qual_clauses(root, best_path->quals);
|
||||||
subplan = create_plan(root, best_path->subpath);
|
|
||||||
else
|
|
||||||
subplan = NULL;
|
|
||||||
|
|
||||||
constclauses = order_qual_clauses(root, best_path->constantqual);
|
return make_result(tlist, (Node *) quals, NULL);
|
||||||
|
|
||||||
plan = make_result(tlist, (Node *) constclauses, subplan);
|
|
||||||
|
|
||||||
return plan;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -716,8 +758,8 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path,
|
|||||||
Assert(scan_relid > 0);
|
Assert(scan_relid > 0);
|
||||||
Assert(best_path->parent->rtekind == RTE_RELATION);
|
Assert(best_path->parent->rtekind == RTE_RELATION);
|
||||||
|
|
||||||
/* Reduce RestrictInfo list to bare expressions */
|
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
|
||||||
scan_clauses = get_actual_clauses(scan_clauses);
|
scan_clauses = extract_actual_clauses(scan_clauses, false);
|
||||||
|
|
||||||
/* Sort clauses into best execution order */
|
/* Sort clauses into best execution order */
|
||||||
scan_clauses = order_qual_clauses(root, scan_clauses);
|
scan_clauses = order_qual_clauses(root, scan_clauses);
|
||||||
@ -824,7 +866,8 @@ create_indexscan_plan(PlannerInfo *root,
|
|||||||
* plan so that they'll be properly rechecked by EvalPlanQual testing.
|
* plan so that they'll be properly rechecked by EvalPlanQual testing.
|
||||||
*
|
*
|
||||||
* While at it, we strip off the RestrictInfos to produce a list of plain
|
* While at it, we strip off the RestrictInfos to produce a list of plain
|
||||||
* expressions.
|
* expressions (this loop replaces extract_actual_clauses used in the
|
||||||
|
* other routines in this file). We have to ignore pseudoconstants.
|
||||||
*/
|
*/
|
||||||
qpqual = NIL;
|
qpqual = NIL;
|
||||||
foreach(l, scan_clauses)
|
foreach(l, scan_clauses)
|
||||||
@ -832,6 +875,8 @@ create_indexscan_plan(PlannerInfo *root,
|
|||||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
||||||
|
|
||||||
Assert(IsA(rinfo, RestrictInfo));
|
Assert(IsA(rinfo, RestrictInfo));
|
||||||
|
if (rinfo->pseudoconstant)
|
||||||
|
continue;
|
||||||
if (list_member_ptr(nonlossy_indexquals, rinfo))
|
if (list_member_ptr(nonlossy_indexquals, rinfo))
|
||||||
continue;
|
continue;
|
||||||
if (!contain_mutable_functions((Node *) rinfo->clause))
|
if (!contain_mutable_functions((Node *) rinfo->clause))
|
||||||
@ -900,8 +945,8 @@ create_bitmap_scan_plan(PlannerInfo *root,
|
|||||||
bitmapqualplan = create_bitmap_subplan(root, best_path->bitmapqual,
|
bitmapqualplan = create_bitmap_subplan(root, best_path->bitmapqual,
|
||||||
&bitmapqualorig, &indexquals);
|
&bitmapqualorig, &indexquals);
|
||||||
|
|
||||||
/* Reduce RestrictInfo list to bare expressions */
|
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
|
||||||
scan_clauses = get_actual_clauses(scan_clauses);
|
scan_clauses = extract_actual_clauses(scan_clauses, false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this is a innerjoin scan, the indexclauses will contain join clauses
|
* If this is a innerjoin scan, the indexclauses will contain join clauses
|
||||||
@ -1183,8 +1228,8 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
|
|||||||
Assert(scan_relid > 0);
|
Assert(scan_relid > 0);
|
||||||
Assert(best_path->path.parent->rtekind == RTE_RELATION);
|
Assert(best_path->path.parent->rtekind == RTE_RELATION);
|
||||||
|
|
||||||
/* Reduce RestrictInfo list to bare expressions */
|
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
|
||||||
scan_clauses = get_actual_clauses(scan_clauses);
|
scan_clauses = extract_actual_clauses(scan_clauses, false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove any clauses that are TID quals. This is a bit tricky since
|
* Remove any clauses that are TID quals. This is a bit tricky since
|
||||||
@ -1224,8 +1269,8 @@ create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
|
|||||||
Assert(scan_relid > 0);
|
Assert(scan_relid > 0);
|
||||||
Assert(best_path->parent->rtekind == RTE_SUBQUERY);
|
Assert(best_path->parent->rtekind == RTE_SUBQUERY);
|
||||||
|
|
||||||
/* Reduce RestrictInfo list to bare expressions */
|
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
|
||||||
scan_clauses = get_actual_clauses(scan_clauses);
|
scan_clauses = extract_actual_clauses(scan_clauses, false);
|
||||||
|
|
||||||
/* Sort clauses into best execution order */
|
/* Sort clauses into best execution order */
|
||||||
scan_clauses = order_qual_clauses(root, scan_clauses);
|
scan_clauses = order_qual_clauses(root, scan_clauses);
|
||||||
@ -1256,8 +1301,8 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path,
|
|||||||
Assert(scan_relid > 0);
|
Assert(scan_relid > 0);
|
||||||
Assert(best_path->parent->rtekind == RTE_FUNCTION);
|
Assert(best_path->parent->rtekind == RTE_FUNCTION);
|
||||||
|
|
||||||
/* Reduce RestrictInfo list to bare expressions */
|
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
|
||||||
scan_clauses = get_actual_clauses(scan_clauses);
|
scan_clauses = extract_actual_clauses(scan_clauses, false);
|
||||||
|
|
||||||
/* Sort clauses into best execution order */
|
/* Sort clauses into best execution order */
|
||||||
scan_clauses = order_qual_clauses(root, scan_clauses);
|
scan_clauses = order_qual_clauses(root, scan_clauses);
|
||||||
@ -1348,15 +1393,16 @@ create_nestloop_plan(PlannerInfo *root,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Get the join qual clauses (in plain expression form) */
|
/* Get the join qual clauses (in plain expression form) */
|
||||||
|
/* Any pseudoconstant clauses are ignored here */
|
||||||
if (IS_OUTER_JOIN(best_path->jointype))
|
if (IS_OUTER_JOIN(best_path->jointype))
|
||||||
{
|
{
|
||||||
get_actual_join_clauses(joinrestrictclauses,
|
extract_actual_join_clauses(joinrestrictclauses,
|
||||||
&joinclauses, &otherclauses);
|
&joinclauses, &otherclauses);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* We can treat all clauses alike for an inner join */
|
/* We can treat all clauses alike for an inner join */
|
||||||
joinclauses = get_actual_clauses(joinrestrictclauses);
|
joinclauses = extract_actual_clauses(joinrestrictclauses, false);
|
||||||
otherclauses = NIL;
|
otherclauses = NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1389,15 +1435,17 @@ create_mergejoin_plan(PlannerInfo *root,
|
|||||||
MergeJoin *join_plan;
|
MergeJoin *join_plan;
|
||||||
|
|
||||||
/* Get the join qual clauses (in plain expression form) */
|
/* Get the join qual clauses (in plain expression form) */
|
||||||
|
/* Any pseudoconstant clauses are ignored here */
|
||||||
if (IS_OUTER_JOIN(best_path->jpath.jointype))
|
if (IS_OUTER_JOIN(best_path->jpath.jointype))
|
||||||
{
|
{
|
||||||
get_actual_join_clauses(best_path->jpath.joinrestrictinfo,
|
extract_actual_join_clauses(best_path->jpath.joinrestrictinfo,
|
||||||
&joinclauses, &otherclauses);
|
&joinclauses, &otherclauses);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* We can treat all clauses alike for an inner join */
|
/* We can treat all clauses alike for an inner join */
|
||||||
joinclauses = get_actual_clauses(best_path->jpath.joinrestrictinfo);
|
joinclauses = extract_actual_clauses(best_path->jpath.joinrestrictinfo,
|
||||||
|
false);
|
||||||
otherclauses = NIL;
|
otherclauses = NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1473,15 +1521,17 @@ create_hashjoin_plan(PlannerInfo *root,
|
|||||||
Hash *hash_plan;
|
Hash *hash_plan;
|
||||||
|
|
||||||
/* Get the join qual clauses (in plain expression form) */
|
/* Get the join qual clauses (in plain expression form) */
|
||||||
|
/* Any pseudoconstant clauses are ignored here */
|
||||||
if (IS_OUTER_JOIN(best_path->jpath.jointype))
|
if (IS_OUTER_JOIN(best_path->jpath.jointype))
|
||||||
{
|
{
|
||||||
get_actual_join_clauses(best_path->jpath.joinrestrictinfo,
|
extract_actual_join_clauses(best_path->jpath.joinrestrictinfo,
|
||||||
&joinclauses, &otherclauses);
|
&joinclauses, &otherclauses);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* We can treat all clauses alike for an inner join */
|
/* We can treat all clauses alike for an inner join */
|
||||||
joinclauses = get_actual_clauses(best_path->jpath.joinrestrictinfo);
|
joinclauses = extract_actual_clauses(best_path->jpath.joinrestrictinfo,
|
||||||
|
false);
|
||||||
otherclauses = NIL;
|
otherclauses = NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1831,7 +1881,7 @@ get_switched_clauses(List *clauses, Relids outerrelids)
|
|||||||
* For now, we just move any quals that contain SubPlan references (but not
|
* For now, we just move any quals that contain SubPlan references (but not
|
||||||
* InitPlan references) to the end of the list.
|
* InitPlan references) to the end of the list.
|
||||||
*/
|
*/
|
||||||
List *
|
static List *
|
||||||
order_qual_clauses(PlannerInfo *root, List *clauses)
|
order_qual_clauses(PlannerInfo *root, List *clauses)
|
||||||
{
|
{
|
||||||
List *nosubplans;
|
List *nosubplans;
|
||||||
@ -2880,6 +2930,15 @@ make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* make_result
|
||||||
|
* Build a Result plan node
|
||||||
|
*
|
||||||
|
* If we have a subplan, assume that any evaluation costs for the gating qual
|
||||||
|
* were already factored into the subplan's startup cost, and just copy the
|
||||||
|
* subplan cost. If there's no subplan, we should include the qual eval
|
||||||
|
* cost. In either case, tlist eval cost is not to be included here.
|
||||||
|
*/
|
||||||
Result *
|
Result *
|
||||||
make_result(List *tlist,
|
make_result(List *tlist,
|
||||||
Node *resconstantqual,
|
Node *resconstantqual,
|
||||||
@ -2895,9 +2954,7 @@ make_result(List *tlist,
|
|||||||
plan->startup_cost = 0;
|
plan->startup_cost = 0;
|
||||||
plan->total_cost = cpu_tuple_cost;
|
plan->total_cost = cpu_tuple_cost;
|
||||||
plan->plan_rows = 1; /* wrong if we have a set-valued function? */
|
plan->plan_rows = 1; /* wrong if we have a set-valued function? */
|
||||||
plan->plan_width = 0; /* XXX try to be smarter? */
|
plan->plan_width = 0; /* XXX is it worth being smarter? */
|
||||||
}
|
|
||||||
|
|
||||||
if (resconstantqual)
|
if (resconstantqual)
|
||||||
{
|
{
|
||||||
QualCost qual_cost;
|
QualCost qual_cost;
|
||||||
@ -2907,6 +2964,7 @@ make_result(List *tlist,
|
|||||||
plan->startup_cost += qual_cost.startup + qual_cost.per_tuple;
|
plan->startup_cost += qual_cost.startup + qual_cost.per_tuple;
|
||||||
plan->total_cost += qual_cost.startup + qual_cost.per_tuple;
|
plan->total_cost += qual_cost.startup + qual_cost.per_tuple;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
plan->targetlist = tlist;
|
plan->targetlist = tlist;
|
||||||
plan->qual = NIL;
|
plan->qual = NIL;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.117 2006/03/14 22:48:19 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.118 2006/07/01 18:38:33 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -23,6 +23,7 @@
|
|||||||
#include "optimizer/pathnode.h"
|
#include "optimizer/pathnode.h"
|
||||||
#include "optimizer/paths.h"
|
#include "optimizer/paths.h"
|
||||||
#include "optimizer/planmain.h"
|
#include "optimizer/planmain.h"
|
||||||
|
#include "optimizer/prep.h"
|
||||||
#include "optimizer/restrictinfo.h"
|
#include "optimizer/restrictinfo.h"
|
||||||
#include "optimizer/tlist.h"
|
#include "optimizer/tlist.h"
|
||||||
#include "optimizer/var.h"
|
#include "optimizer/var.h"
|
||||||
@ -72,6 +73,9 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
|
|||||||
* the base relations (ie, table, subquery, and function RTEs)
|
* the base relations (ie, table, subquery, and function RTEs)
|
||||||
* appearing in the jointree.
|
* appearing in the jointree.
|
||||||
*
|
*
|
||||||
|
* The initial invocation must pass root->parse->jointree as the value of
|
||||||
|
* jtnode. Internally, the function recurses through the jointree.
|
||||||
|
*
|
||||||
* At the end of this process, there should be one baserel RelOptInfo for
|
* At the end of this process, there should be one baserel RelOptInfo for
|
||||||
* every non-join RTE that is used in the query. Therefore, this routine
|
* every non-join RTE that is used in the query. Therefore, this routine
|
||||||
* is the only place that should call build_simple_rel with reloptkind
|
* is the only place that should call build_simple_rel with reloptkind
|
||||||
@ -578,6 +582,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
|
|||||||
{
|
{
|
||||||
Relids relids;
|
Relids relids;
|
||||||
bool outerjoin_delayed;
|
bool outerjoin_delayed;
|
||||||
|
bool pseudoconstant = false;
|
||||||
bool maybe_equijoin;
|
bool maybe_equijoin;
|
||||||
bool maybe_outer_join;
|
bool maybe_outer_join;
|
||||||
RestrictInfo *restrictinfo;
|
RestrictInfo *restrictinfo;
|
||||||
@ -599,16 +604,57 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
|
|||||||
elog(ERROR, "JOIN qualification may not refer to other relations");
|
elog(ERROR, "JOIN qualification may not refer to other relations");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the clause is variable-free, we force it to be evaluated at its
|
* If the clause is variable-free, our normal heuristic for pushing it
|
||||||
* original syntactic level. Note that this should not happen for
|
* down to just the mentioned rels doesn't work, because there are none.
|
||||||
* top-level clauses, because query_planner() special-cases them. But it
|
*
|
||||||
* will happen for variable-free JOIN/ON clauses. We don't have to be
|
* If the clause is an outer-join clause, we must force it to the OJ's
|
||||||
* real smart about such a case, we just have to be correct. Also note
|
* semantic level to preserve semantics.
|
||||||
* that for an outer-join clause, we must force it to the OJ's semantic
|
*
|
||||||
* level, not the syntactic scope.
|
* Otherwise, when the clause contains volatile functions, we force it
|
||||||
|
* to be evaluated at its original syntactic level. This preserves the
|
||||||
|
* expected semantics.
|
||||||
|
*
|
||||||
|
* When the clause contains no volatile functions either, it is actually
|
||||||
|
* a pseudoconstant clause that will not change value during any one
|
||||||
|
* execution of the plan, and hence can be used as a one-time qual in
|
||||||
|
* a gating Result plan node. We put such a clause into the regular
|
||||||
|
* RestrictInfo lists for the moment, but eventually createplan.c will
|
||||||
|
* pull it out and make a gating Result node immediately above whatever
|
||||||
|
* plan node the pseudoconstant clause is assigned to. It's usually
|
||||||
|
* best to put a gating node as high in the plan tree as possible.
|
||||||
|
* If we are not below an outer join, we can actually push the
|
||||||
|
* pseudoconstant qual all the way to the top of the tree. If we are
|
||||||
|
* below an outer join, we leave the qual at its original syntactic level
|
||||||
|
* (we could push it up to just below the outer join, but that seems more
|
||||||
|
* complex than it's worth).
|
||||||
*/
|
*/
|
||||||
if (bms_is_empty(relids))
|
if (bms_is_empty(relids))
|
||||||
relids = ojscope ? ojscope : qualscope;
|
{
|
||||||
|
if (ojscope)
|
||||||
|
{
|
||||||
|
/* clause is attached to outer join, eval it there */
|
||||||
|
relids = ojscope;
|
||||||
|
/* mustn't use as gating qual, so don't mark pseudoconstant */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* eval at original syntactic level */
|
||||||
|
relids = qualscope;
|
||||||
|
if (!contain_volatile_functions(clause))
|
||||||
|
{
|
||||||
|
/* mark as gating qual */
|
||||||
|
pseudoconstant = true;
|
||||||
|
/* tell createplan.c to check for gating quals */
|
||||||
|
root->hasPseudoConstantQuals = true;
|
||||||
|
/* if not below outer join, push it to top of tree */
|
||||||
|
if (!below_outer_join)
|
||||||
|
{
|
||||||
|
relids = get_relids_in_jointree((Node *) root->parse->jointree);
|
||||||
|
is_pushed_down = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check to see if clause application must be delayed by outer-join
|
* Check to see if clause application must be delayed by outer-join
|
||||||
@ -624,6 +670,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
|
|||||||
*/
|
*/
|
||||||
Assert(bms_equal(relids, qualscope));
|
Assert(bms_equal(relids, qualscope));
|
||||||
Assert(!ojscope);
|
Assert(!ojscope);
|
||||||
|
Assert(!pseudoconstant);
|
||||||
/* Needn't feed it back for more deductions */
|
/* Needn't feed it back for more deductions */
|
||||||
outerjoin_delayed = false;
|
outerjoin_delayed = false;
|
||||||
maybe_equijoin = false;
|
maybe_equijoin = false;
|
||||||
@ -647,6 +694,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
|
|||||||
Assert(ojscope);
|
Assert(ojscope);
|
||||||
relids = ojscope;
|
relids = ojscope;
|
||||||
outerjoin_delayed = true;
|
outerjoin_delayed = true;
|
||||||
|
Assert(!pseudoconstant);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can't use such a clause to deduce equijoin (the left and right
|
* We can't use such a clause to deduce equijoin (the left and right
|
||||||
@ -738,6 +786,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
|
|||||||
restrictinfo = make_restrictinfo((Expr *) clause,
|
restrictinfo = make_restrictinfo((Expr *) clause,
|
||||||
is_pushed_down,
|
is_pushed_down,
|
||||||
outerjoin_delayed,
|
outerjoin_delayed,
|
||||||
|
pseudoconstant,
|
||||||
relids);
|
relids);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1179,6 +1228,8 @@ check_mergejoinable(RestrictInfo *restrictinfo)
|
|||||||
leftOp,
|
leftOp,
|
||||||
rightOp;
|
rightOp;
|
||||||
|
|
||||||
|
if (restrictinfo->pseudoconstant)
|
||||||
|
return;
|
||||||
if (!is_opclause(clause))
|
if (!is_opclause(clause))
|
||||||
return;
|
return;
|
||||||
if (list_length(((OpExpr *) clause)->args) != 2)
|
if (list_length(((OpExpr *) clause)->args) != 2)
|
||||||
@ -1212,6 +1263,8 @@ check_hashjoinable(RestrictInfo *restrictinfo)
|
|||||||
Expr *clause = restrictinfo->clause;
|
Expr *clause = restrictinfo->clause;
|
||||||
Oid opno;
|
Oid opno;
|
||||||
|
|
||||||
|
if (restrictinfo->pseudoconstant)
|
||||||
|
return;
|
||||||
if (!is_opclause(clause))
|
if (!is_opclause(clause))
|
||||||
return;
|
return;
|
||||||
if (list_length(((OpExpr *) clause)->args) != 2)
|
if (list_length(((OpExpr *) clause)->args) != 2)
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.15 2006/06/06 17:59:57 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.16 2006/07/01 18:38:33 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -46,8 +46,7 @@ static bool build_minmax_path(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
MinMaxAggInfo *info);
|
MinMaxAggInfo *info);
|
||||||
static ScanDirection match_agg_to_index_col(MinMaxAggInfo *info,
|
static ScanDirection match_agg_to_index_col(MinMaxAggInfo *info,
|
||||||
IndexOptInfo *index, int indexcol);
|
IndexOptInfo *index, int indexcol);
|
||||||
static void make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info,
|
static void make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info);
|
||||||
List *constant_quals);
|
|
||||||
static Node *replace_aggs_with_params_mutator(Node *node, List **context);
|
static Node *replace_aggs_with_params_mutator(Node *node, List **context);
|
||||||
static Oid fetch_agg_sort_op(Oid aggfnoid);
|
static Oid fetch_agg_sort_op(Oid aggfnoid);
|
||||||
|
|
||||||
@ -81,7 +80,6 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path)
|
|||||||
Plan *plan;
|
Plan *plan;
|
||||||
Node *hqual;
|
Node *hqual;
|
||||||
QualCost tlist_cost;
|
QualCost tlist_cost;
|
||||||
List *constant_quals;
|
|
||||||
|
|
||||||
/* Nothing to do if query has no aggregates */
|
/* Nothing to do if query has no aggregates */
|
||||||
if (!parse->hasAggs)
|
if (!parse->hasAggs)
|
||||||
@ -164,27 +162,13 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path)
|
|||||||
return NULL; /* too expensive */
|
return NULL; /* too expensive */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* OK, we are going to generate an optimized plan. The first thing we
|
* OK, we are going to generate an optimized plan.
|
||||||
* need to do is look for any non-variable WHERE clauses that
|
|
||||||
* query_planner might have removed from the basic plan. (Normal WHERE
|
|
||||||
* clauses will be properly incorporated into the sub-plans by
|
|
||||||
* create_plan.) If there are any, they will be in a gating Result node
|
|
||||||
* atop the best_path. They have to be incorporated into a gating Result
|
|
||||||
* in each sub-plan in order to produce the semantically correct result.
|
|
||||||
*/
|
*/
|
||||||
if (IsA(best_path, ResultPath))
|
|
||||||
{
|
|
||||||
constant_quals = ((ResultPath *) best_path)->constantqual;
|
|
||||||
/* no need to do this more than once: */
|
|
||||||
constant_quals = order_qual_clauses(root, constant_quals);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
constant_quals = NIL;
|
|
||||||
|
|
||||||
/* Pass 3: generate subplans and output Param nodes */
|
/* Pass 3: generate subplans and output Param nodes */
|
||||||
foreach(l, aggs_list)
|
foreach(l, aggs_list)
|
||||||
{
|
{
|
||||||
make_agg_subplan(root, (MinMaxAggInfo *) lfirst(l), constant_quals);
|
make_agg_subplan(root, (MinMaxAggInfo *) lfirst(l));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -434,11 +418,12 @@ match_agg_to_index_col(MinMaxAggInfo *info, IndexOptInfo *index, int indexcol)
|
|||||||
* Construct a suitable plan for a converted aggregate query
|
* Construct a suitable plan for a converted aggregate query
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info, List *constant_quals)
|
make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
|
||||||
{
|
{
|
||||||
PlannerInfo subroot;
|
PlannerInfo subroot;
|
||||||
Query *subparse;
|
Query *subparse;
|
||||||
Plan *plan;
|
Plan *plan;
|
||||||
|
Plan *iplan;
|
||||||
TargetEntry *tle;
|
TargetEntry *tle;
|
||||||
SortClause *sortcl;
|
SortClause *sortcl;
|
||||||
NullTest *ntest;
|
NullTest *ntest;
|
||||||
@ -482,8 +467,7 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info, List *constant_quals)
|
|||||||
/*
|
/*
|
||||||
* Generate the plan for the subquery. We already have a Path for the
|
* Generate the plan for the subquery. We already have a Path for the
|
||||||
* basic indexscan, but we have to convert it to a Plan and attach a LIMIT
|
* basic indexscan, but we have to convert it to a Plan and attach a LIMIT
|
||||||
* node above it. We might need a gating Result, too, to handle any
|
* node above it.
|
||||||
* non-variable qual clauses.
|
|
||||||
*
|
*
|
||||||
* Also we must add a "WHERE foo IS NOT NULL" restriction to the
|
* Also we must add a "WHERE foo IS NOT NULL" restriction to the
|
||||||
* indexscan, to be sure we don't return a NULL, which'd be contrary to
|
* indexscan, to be sure we don't return a NULL, which'd be contrary to
|
||||||
@ -491,21 +475,26 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info, List *constant_quals)
|
|||||||
* earlier, so that the selectivity of the restriction could be included
|
* earlier, so that the selectivity of the restriction could be included
|
||||||
* in our cost estimates. But that looks painful, and in most cases the
|
* in our cost estimates. But that looks painful, and in most cases the
|
||||||
* fraction of NULLs isn't high enough to change the decision.
|
* fraction of NULLs isn't high enough to change the decision.
|
||||||
|
*
|
||||||
|
* The NOT NULL qual has to go on the actual indexscan; create_plan
|
||||||
|
* might have stuck a gating Result atop that, if there were any
|
||||||
|
* pseudoconstant quals.
|
||||||
*/
|
*/
|
||||||
plan = create_plan(&subroot, (Path *) info->path);
|
plan = create_plan(&subroot, (Path *) info->path);
|
||||||
|
|
||||||
plan->targetlist = copyObject(subparse->targetList);
|
plan->targetlist = copyObject(subparse->targetList);
|
||||||
|
|
||||||
|
if (IsA(plan, Result))
|
||||||
|
iplan = plan->lefttree;
|
||||||
|
else
|
||||||
|
iplan = plan;
|
||||||
|
Assert(IsA(iplan, IndexScan));
|
||||||
|
|
||||||
ntest = makeNode(NullTest);
|
ntest = makeNode(NullTest);
|
||||||
ntest->nulltesttype = IS_NOT_NULL;
|
ntest->nulltesttype = IS_NOT_NULL;
|
||||||
ntest->arg = copyObject(info->target);
|
ntest->arg = copyObject(info->target);
|
||||||
|
|
||||||
plan->qual = lcons(ntest, plan->qual);
|
iplan->qual = lcons(ntest, iplan->qual);
|
||||||
|
|
||||||
if (constant_quals)
|
|
||||||
plan = (Plan *) make_result(copyObject(plan->targetlist),
|
|
||||||
copyObject(constant_quals),
|
|
||||||
plan);
|
|
||||||
|
|
||||||
plan = (Plan *) make_limit(plan,
|
plan = (Plan *) make_limit(plan,
|
||||||
subparse->limitOffset,
|
subparse->limitOffset,
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.93 2006/03/05 15:58:29 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.94 2006/07/01 18:38:33 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -82,7 +82,6 @@ query_planner(PlannerInfo *root, List *tlist, double tuple_fraction,
|
|||||||
double *num_groups)
|
double *num_groups)
|
||||||
{
|
{
|
||||||
Query *parse = root->parse;
|
Query *parse = root->parse;
|
||||||
List *constant_quals;
|
|
||||||
List *joinlist;
|
List *joinlist;
|
||||||
RelOptInfo *final_rel;
|
RelOptInfo *final_rel;
|
||||||
Path *cheapestpath;
|
Path *cheapestpath;
|
||||||
@ -99,26 +98,12 @@ query_planner(PlannerInfo *root, List *tlist, double tuple_fraction,
|
|||||||
*/
|
*/
|
||||||
if (parse->jointree->fromlist == NIL)
|
if (parse->jointree->fromlist == NIL)
|
||||||
{
|
{
|
||||||
*cheapest_path = (Path *) create_result_path(NULL, NULL,
|
*cheapest_path = (Path *)
|
||||||
(List *) parse->jointree->quals);
|
create_result_path((List *) parse->jointree->quals);
|
||||||
*sorted_path = NULL;
|
*sorted_path = NULL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Pull out any non-variable WHERE clauses so these can be put in a
|
|
||||||
* toplevel "Result" node, where they will gate execution of the whole
|
|
||||||
* plan (the Result will not invoke its descendant plan unless the quals
|
|
||||||
* are true). Note that any *really* non-variable quals will have been
|
|
||||||
* optimized away by eval_const_expressions(). What we're mostly
|
|
||||||
* interested in here is quals that depend only on outer-level vars,
|
|
||||||
* although if the qual reduces to "WHERE FALSE" this path will also be
|
|
||||||
* taken.
|
|
||||||
*/
|
|
||||||
parse->jointree->quals = (Node *)
|
|
||||||
pull_constant_clauses((List *) parse->jointree->quals,
|
|
||||||
&constant_quals);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Init planner lists to empty, and set up the array to hold RelOptInfos
|
* Init planner lists to empty, and set up the array to hold RelOptInfos
|
||||||
* for "simple" rels.
|
* for "simple" rels.
|
||||||
@ -324,20 +309,6 @@ query_planner(PlannerInfo *root, List *tlist, double tuple_fraction,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* If we have constant quals, add a toplevel Result step to process them.
|
|
||||||
*/
|
|
||||||
if (constant_quals)
|
|
||||||
{
|
|
||||||
cheapestpath = (Path *) create_result_path(final_rel,
|
|
||||||
cheapestpath,
|
|
||||||
constant_quals);
|
|
||||||
if (sortedpath)
|
|
||||||
sortedpath = (Path *) create_result_path(final_rel,
|
|
||||||
sortedpath,
|
|
||||||
constant_quals);
|
|
||||||
}
|
|
||||||
|
|
||||||
*cheapest_path = cheapestpath;
|
*cheapest_path = cheapestpath;
|
||||||
*sorted_path = sortedpath;
|
*sorted_path = sortedpath;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.200 2006/06/28 20:04:38 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.201 2006/07/01 18:38:33 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -270,6 +270,9 @@ subquery_planner(Query *parse, double tuple_fraction,
|
|||||||
*/
|
*/
|
||||||
root->hasHavingQual = (parse->havingQual != NULL);
|
root->hasHavingQual = (parse->havingQual != NULL);
|
||||||
|
|
||||||
|
/* Clear this flag; might get set in distribute_qual_to_rels */
|
||||||
|
root->hasPseudoConstantQuals = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do expression preprocessing on targetlist and quals.
|
* Do expression preprocessing on targetlist and quals.
|
||||||
*/
|
*/
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.212 2006/06/16 18:42:22 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.213 2006/07/01 18:38:33 tgl Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -1052,14 +1052,13 @@ is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* is_pseudo_constant_clause
|
* is_pseudo_constant_clause
|
||||||
* Detect whether a clause is "constant", ie, it contains no variables
|
* Detect whether an expression is "pseudo constant", ie, it contains no
|
||||||
* of the current query level and no uses of volatile functions.
|
* variables of the current query level and no uses of volatile functions.
|
||||||
* Such a clause is not necessarily a true constant: it can still contain
|
* Such an expr is not necessarily a true constant: it can still contain
|
||||||
* Params and outer-level Vars, not to mention functions whose results
|
* Params and outer-level Vars, not to mention functions whose results
|
||||||
* may vary from one statement to the next. However, the clause's value
|
* may vary from one statement to the next. However, the expr's value
|
||||||
* will be constant over any one scan of the current query, so it can be
|
* will be constant over any one scan of the current query, so it can be
|
||||||
* used as an indexscan key or (if a top-level qual) can be pushed up to
|
* used as, eg, an indexscan key.
|
||||||
* become a gating qual.
|
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
is_pseudo_constant_clause(Node *clause)
|
is_pseudo_constant_clause(Node *clause)
|
||||||
@ -1079,7 +1078,7 @@ is_pseudo_constant_clause(Node *clause)
|
|||||||
/*
|
/*
|
||||||
* is_pseudo_constant_clause_relids
|
* is_pseudo_constant_clause_relids
|
||||||
* Same as above, except caller already has available the var membership
|
* Same as above, except caller already has available the var membership
|
||||||
* of the clause; this lets us avoid the contain_var_clause() scan.
|
* of the expression; this lets us avoid the contain_var_clause() scan.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
is_pseudo_constant_clause_relids(Node *clause, Relids relids)
|
is_pseudo_constant_clause_relids(Node *clause, Relids relids)
|
||||||
@ -1090,34 +1089,6 @@ is_pseudo_constant_clause_relids(Node *clause, Relids relids)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* pull_constant_clauses
|
|
||||||
* Scan through a list of qualifications and separate "constant" quals
|
|
||||||
* from those that are not.
|
|
||||||
*
|
|
||||||
* Returns a list of the pseudo-constant clauses in constantQual and the
|
|
||||||
* remaining quals as the return value.
|
|
||||||
*/
|
|
||||||
List *
|
|
||||||
pull_constant_clauses(List *quals, List **constantQual)
|
|
||||||
{
|
|
||||||
List *constqual = NIL,
|
|
||||||
*restqual = NIL;
|
|
||||||
ListCell *q;
|
|
||||||
|
|
||||||
foreach(q, quals)
|
|
||||||
{
|
|
||||||
Node *qual = (Node *) lfirst(q);
|
|
||||||
|
|
||||||
if (is_pseudo_constant_clause(qual))
|
|
||||||
constqual = lappend(constqual, qual);
|
|
||||||
else
|
|
||||||
restqual = lappend(restqual, qual);
|
|
||||||
}
|
|
||||||
*constantQual = constqual;
|
|
||||||
return restqual;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Tests on clauses of queries
|
* Tests on clauses of queries
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.128 2006/06/06 17:59:57 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.129 2006/07/01 18:38:33 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -667,36 +667,29 @@ create_append_path(RelOptInfo *rel, List *subpaths)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* create_result_path
|
* create_result_path
|
||||||
* Creates a path corresponding to a Result plan, returning the
|
* Creates a path representing a Result-and-nothing-else plan.
|
||||||
* pathnode.
|
* This is only used for the case of a query with an empty jointree.
|
||||||
*/
|
*/
|
||||||
ResultPath *
|
ResultPath *
|
||||||
create_result_path(RelOptInfo *rel, Path *subpath, List *constantqual)
|
create_result_path(List *quals)
|
||||||
{
|
{
|
||||||
ResultPath *pathnode = makeNode(ResultPath);
|
ResultPath *pathnode = makeNode(ResultPath);
|
||||||
|
|
||||||
pathnode->path.pathtype = T_Result;
|
pathnode->path.pathtype = T_Result;
|
||||||
pathnode->path.parent = rel; /* may be NULL */
|
pathnode->path.parent = NULL;
|
||||||
|
|
||||||
if (subpath)
|
|
||||||
pathnode->path.pathkeys = subpath->pathkeys;
|
|
||||||
else
|
|
||||||
pathnode->path.pathkeys = NIL;
|
pathnode->path.pathkeys = NIL;
|
||||||
|
pathnode->quals = quals;
|
||||||
pathnode->subpath = subpath;
|
|
||||||
pathnode->constantqual = constantqual;
|
|
||||||
|
|
||||||
/* Ideally should define cost_result(), but I'm too lazy */
|
/* Ideally should define cost_result(), but I'm too lazy */
|
||||||
if (subpath)
|
|
||||||
{
|
|
||||||
pathnode->path.startup_cost = subpath->startup_cost;
|
|
||||||
pathnode->path.total_cost = subpath->total_cost;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pathnode->path.startup_cost = 0;
|
pathnode->path.startup_cost = 0;
|
||||||
pathnode->path.total_cost = cpu_tuple_cost;
|
pathnode->path.total_cost = cpu_tuple_cost;
|
||||||
}
|
/*
|
||||||
|
* In theory we should include the qual eval cost as well, but
|
||||||
|
* at present that doesn't accomplish much except duplicate work that
|
||||||
|
* will be done again in make_result; since this is only used for
|
||||||
|
* degenerate cases, nothing interesting will be done with the path
|
||||||
|
* cost values...
|
||||||
|
*/
|
||||||
|
|
||||||
return pathnode;
|
return pathnode;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.47 2006/04/07 17:05:39 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.48 2006/07/01 18:38:33 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -26,10 +26,12 @@ static RestrictInfo *make_restrictinfo_internal(Expr *clause,
|
|||||||
Expr *orclause,
|
Expr *orclause,
|
||||||
bool is_pushed_down,
|
bool is_pushed_down,
|
||||||
bool outerjoin_delayed,
|
bool outerjoin_delayed,
|
||||||
|
bool pseudoconstant,
|
||||||
Relids required_relids);
|
Relids required_relids);
|
||||||
static Expr *make_sub_restrictinfos(Expr *clause,
|
static Expr *make_sub_restrictinfos(Expr *clause,
|
||||||
bool is_pushed_down,
|
bool is_pushed_down,
|
||||||
bool outerjoin_delayed,
|
bool outerjoin_delayed,
|
||||||
|
bool pseudoconstant,
|
||||||
Relids required_relids);
|
Relids required_relids);
|
||||||
static RestrictInfo *join_clause_is_redundant(PlannerInfo *root,
|
static RestrictInfo *join_clause_is_redundant(PlannerInfo *root,
|
||||||
RestrictInfo *rinfo,
|
RestrictInfo *rinfo,
|
||||||
@ -42,9 +44,10 @@ static RestrictInfo *join_clause_is_redundant(PlannerInfo *root,
|
|||||||
*
|
*
|
||||||
* Build a RestrictInfo node containing the given subexpression.
|
* Build a RestrictInfo node containing the given subexpression.
|
||||||
*
|
*
|
||||||
* The is_pushed_down and outerjoin_delayed flags must be supplied by the
|
* The is_pushed_down, outerjoin_delayed, and pseudoconstant flags for the
|
||||||
* caller. required_relids can be NULL, in which case it defaults to the
|
* RestrictInfo must be supplied by the caller. required_relids can be NULL,
|
||||||
* actual clause contents (i.e., clause_relids).
|
* in which case it defaults to the actual clause contents (i.e.,
|
||||||
|
* clause_relids).
|
||||||
*
|
*
|
||||||
* We initialize fields that depend only on the given subexpression, leaving
|
* We initialize fields that depend only on the given subexpression, leaving
|
||||||
* others that depend on context (or may never be needed at all) to be filled
|
* others that depend on context (or may never be needed at all) to be filled
|
||||||
@ -54,6 +57,7 @@ RestrictInfo *
|
|||||||
make_restrictinfo(Expr *clause,
|
make_restrictinfo(Expr *clause,
|
||||||
bool is_pushed_down,
|
bool is_pushed_down,
|
||||||
bool outerjoin_delayed,
|
bool outerjoin_delayed,
|
||||||
|
bool pseudoconstant,
|
||||||
Relids required_relids)
|
Relids required_relids)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -64,13 +68,17 @@ make_restrictinfo(Expr *clause,
|
|||||||
return (RestrictInfo *) make_sub_restrictinfos(clause,
|
return (RestrictInfo *) make_sub_restrictinfos(clause,
|
||||||
is_pushed_down,
|
is_pushed_down,
|
||||||
outerjoin_delayed,
|
outerjoin_delayed,
|
||||||
|
pseudoconstant,
|
||||||
required_relids);
|
required_relids);
|
||||||
|
|
||||||
/* Shouldn't be an AND clause, else AND/OR flattening messed up */
|
/* Shouldn't be an AND clause, else AND/OR flattening messed up */
|
||||||
Assert(!and_clause((Node *) clause));
|
Assert(!and_clause((Node *) clause));
|
||||||
|
|
||||||
return make_restrictinfo_internal(clause, NULL,
|
return make_restrictinfo_internal(clause,
|
||||||
is_pushed_down, outerjoin_delayed,
|
NULL,
|
||||||
|
is_pushed_down,
|
||||||
|
outerjoin_delayed,
|
||||||
|
pseudoconstant,
|
||||||
required_relids);
|
required_relids);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +93,8 @@ make_restrictinfo(Expr *clause,
|
|||||||
* RestrictInfos.
|
* RestrictInfos.
|
||||||
*
|
*
|
||||||
* The caller must pass is_pushed_down, but we assume outerjoin_delayed
|
* The caller must pass is_pushed_down, but we assume outerjoin_delayed
|
||||||
* is false (no such qual should ever get into a bitmapqual).
|
* and pseudoconstant are false (no such qual should ever get into a
|
||||||
|
* bitmapqual).
|
||||||
*
|
*
|
||||||
* If include_predicates is true, we add any partial index predicates to
|
* If include_predicates is true, we add any partial index predicates to
|
||||||
* the explicit index quals. When this is not true, we return a condition
|
* the explicit index quals. When this is not true, we return a condition
|
||||||
@ -214,6 +223,7 @@ make_restrictinfo_from_bitmapqual(Path *bitmapqual,
|
|||||||
make_orclause(withris),
|
make_orclause(withris),
|
||||||
is_pushed_down,
|
is_pushed_down,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
NULL));
|
NULL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -239,6 +249,7 @@ make_restrictinfo_from_bitmapqual(Path *bitmapqual,
|
|||||||
make_restrictinfo(pred,
|
make_restrictinfo(pred,
|
||||||
is_pushed_down,
|
is_pushed_down,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
NULL));
|
NULL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,8 +269,11 @@ make_restrictinfo_from_bitmapqual(Path *bitmapqual,
|
|||||||
* Common code for the main entry points and the recursive cases.
|
* Common code for the main entry points and the recursive cases.
|
||||||
*/
|
*/
|
||||||
static RestrictInfo *
|
static RestrictInfo *
|
||||||
make_restrictinfo_internal(Expr *clause, Expr *orclause,
|
make_restrictinfo_internal(Expr *clause,
|
||||||
bool is_pushed_down, bool outerjoin_delayed,
|
Expr *orclause,
|
||||||
|
bool is_pushed_down,
|
||||||
|
bool outerjoin_delayed,
|
||||||
|
bool pseudoconstant,
|
||||||
Relids required_relids)
|
Relids required_relids)
|
||||||
{
|
{
|
||||||
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
|
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
|
||||||
@ -268,6 +282,7 @@ make_restrictinfo_internal(Expr *clause, Expr *orclause,
|
|||||||
restrictinfo->orclause = orclause;
|
restrictinfo->orclause = orclause;
|
||||||
restrictinfo->is_pushed_down = is_pushed_down;
|
restrictinfo->is_pushed_down = is_pushed_down;
|
||||||
restrictinfo->outerjoin_delayed = outerjoin_delayed;
|
restrictinfo->outerjoin_delayed = outerjoin_delayed;
|
||||||
|
restrictinfo->pseudoconstant = pseudoconstant;
|
||||||
restrictinfo->can_join = false; /* may get set below */
|
restrictinfo->can_join = false; /* may get set below */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -292,7 +307,11 @@ make_restrictinfo_internal(Expr *clause, Expr *orclause,
|
|||||||
!bms_is_empty(restrictinfo->right_relids) &&
|
!bms_is_empty(restrictinfo->right_relids) &&
|
||||||
!bms_overlap(restrictinfo->left_relids,
|
!bms_overlap(restrictinfo->left_relids,
|
||||||
restrictinfo->right_relids))
|
restrictinfo->right_relids))
|
||||||
|
{
|
||||||
restrictinfo->can_join = true;
|
restrictinfo->can_join = true;
|
||||||
|
/* pseudoconstant should certainly not be true */
|
||||||
|
Assert(!restrictinfo->pseudoconstant);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -346,13 +365,18 @@ make_restrictinfo_internal(Expr *clause, Expr *orclause,
|
|||||||
* implicit-AND lists at top level of RestrictInfo lists. Only ORs and
|
* implicit-AND lists at top level of RestrictInfo lists. Only ORs and
|
||||||
* simple clauses are valid RestrictInfos.
|
* simple clauses are valid RestrictInfos.
|
||||||
*
|
*
|
||||||
|
* The same is_pushed_down, outerjoin_delayed, and pseudoconstant flag
|
||||||
|
* values can be applied to all RestrictInfo nodes in the result.
|
||||||
|
*
|
||||||
* The given required_relids are attached to our top-level output,
|
* The given required_relids are attached to our top-level output,
|
||||||
* but any OR-clause constituents are allowed to default to just the
|
* but any OR-clause constituents are allowed to default to just the
|
||||||
* contained rels.
|
* contained rels.
|
||||||
*/
|
*/
|
||||||
static Expr *
|
static Expr *
|
||||||
make_sub_restrictinfos(Expr *clause,
|
make_sub_restrictinfos(Expr *clause,
|
||||||
bool is_pushed_down, bool outerjoin_delayed,
|
bool is_pushed_down,
|
||||||
|
bool outerjoin_delayed,
|
||||||
|
bool pseudoconstant,
|
||||||
Relids required_relids)
|
Relids required_relids)
|
||||||
{
|
{
|
||||||
if (or_clause((Node *) clause))
|
if (or_clause((Node *) clause))
|
||||||
@ -365,11 +389,13 @@ make_sub_restrictinfos(Expr *clause,
|
|||||||
make_sub_restrictinfos(lfirst(temp),
|
make_sub_restrictinfos(lfirst(temp),
|
||||||
is_pushed_down,
|
is_pushed_down,
|
||||||
outerjoin_delayed,
|
outerjoin_delayed,
|
||||||
|
pseudoconstant,
|
||||||
NULL));
|
NULL));
|
||||||
return (Expr *) make_restrictinfo_internal(clause,
|
return (Expr *) make_restrictinfo_internal(clause,
|
||||||
make_orclause(orlist),
|
make_orclause(orlist),
|
||||||
is_pushed_down,
|
is_pushed_down,
|
||||||
outerjoin_delayed,
|
outerjoin_delayed,
|
||||||
|
pseudoconstant,
|
||||||
required_relids);
|
required_relids);
|
||||||
}
|
}
|
||||||
else if (and_clause((Node *) clause))
|
else if (and_clause((Node *) clause))
|
||||||
@ -382,6 +408,7 @@ make_sub_restrictinfos(Expr *clause,
|
|||||||
make_sub_restrictinfos(lfirst(temp),
|
make_sub_restrictinfos(lfirst(temp),
|
||||||
is_pushed_down,
|
is_pushed_down,
|
||||||
outerjoin_delayed,
|
outerjoin_delayed,
|
||||||
|
pseudoconstant,
|
||||||
required_relids));
|
required_relids));
|
||||||
return make_andclause(andlist);
|
return make_andclause(andlist);
|
||||||
}
|
}
|
||||||
@ -390,6 +417,7 @@ make_sub_restrictinfos(Expr *clause,
|
|||||||
NULL,
|
NULL,
|
||||||
is_pushed_down,
|
is_pushed_down,
|
||||||
outerjoin_delayed,
|
outerjoin_delayed,
|
||||||
|
pseudoconstant,
|
||||||
required_relids);
|
required_relids);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,47 +439,91 @@ restriction_is_or_clause(RestrictInfo *restrictinfo)
|
|||||||
* get_actual_clauses
|
* get_actual_clauses
|
||||||
*
|
*
|
||||||
* Returns a list containing the bare clauses from 'restrictinfo_list'.
|
* Returns a list containing the bare clauses from 'restrictinfo_list'.
|
||||||
|
*
|
||||||
|
* This is only to be used in cases where none of the RestrictInfos can
|
||||||
|
* be pseudoconstant clauses (for instance, it's OK on indexqual lists).
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
get_actual_clauses(List *restrictinfo_list)
|
get_actual_clauses(List *restrictinfo_list)
|
||||||
{
|
{
|
||||||
List *result = NIL;
|
List *result = NIL;
|
||||||
ListCell *temp;
|
ListCell *l;
|
||||||
|
|
||||||
foreach(temp, restrictinfo_list)
|
foreach(l, restrictinfo_list)
|
||||||
{
|
{
|
||||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(temp);
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
||||||
|
|
||||||
Assert(IsA(rinfo, RestrictInfo));
|
Assert(IsA(rinfo, RestrictInfo));
|
||||||
|
|
||||||
|
Assert(!rinfo->pseudoconstant);
|
||||||
|
|
||||||
result = lappend(result, rinfo->clause);
|
result = lappend(result, rinfo->clause);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_actual_join_clauses
|
* extract_actual_clauses
|
||||||
*
|
*
|
||||||
* Extract clauses from 'restrictinfo_list', separating those that
|
* Extract bare clauses from 'restrictinfo_list', returning either the
|
||||||
|
* regular ones or the pseudoconstant ones per 'pseudoconstant'.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
extract_actual_clauses(List *restrictinfo_list,
|
||||||
|
bool pseudoconstant)
|
||||||
|
{
|
||||||
|
List *result = NIL;
|
||||||
|
ListCell *l;
|
||||||
|
|
||||||
|
foreach(l, restrictinfo_list)
|
||||||
|
{
|
||||||
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
||||||
|
|
||||||
|
Assert(IsA(rinfo, RestrictInfo));
|
||||||
|
|
||||||
|
if (rinfo->pseudoconstant == pseudoconstant)
|
||||||
|
result = lappend(result, rinfo->clause);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* extract_actual_join_clauses
|
||||||
|
*
|
||||||
|
* Extract bare clauses from 'restrictinfo_list', separating those that
|
||||||
* syntactically match the join level from those that were pushed down.
|
* syntactically match the join level from those that were pushed down.
|
||||||
|
* Pseudoconstant clauses are excluded from the results.
|
||||||
|
*
|
||||||
|
* This is only used at outer joins, since for plain joins we don't care
|
||||||
|
* about pushed-down-ness.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
get_actual_join_clauses(List *restrictinfo_list,
|
extract_actual_join_clauses(List *restrictinfo_list,
|
||||||
List **joinquals, List **otherquals)
|
List **joinquals,
|
||||||
|
List **otherquals)
|
||||||
{
|
{
|
||||||
ListCell *temp;
|
ListCell *l;
|
||||||
|
|
||||||
*joinquals = NIL;
|
*joinquals = NIL;
|
||||||
*otherquals = NIL;
|
*otherquals = NIL;
|
||||||
|
|
||||||
foreach(temp, restrictinfo_list)
|
foreach(l, restrictinfo_list)
|
||||||
{
|
{
|
||||||
RestrictInfo *clause = (RestrictInfo *) lfirst(temp);
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
||||||
|
|
||||||
if (clause->is_pushed_down)
|
Assert(IsA(rinfo, RestrictInfo));
|
||||||
*otherquals = lappend(*otherquals, clause->clause);
|
|
||||||
|
if (rinfo->is_pushed_down)
|
||||||
|
{
|
||||||
|
if (!rinfo->pseudoconstant)
|
||||||
|
*otherquals = lappend(*otherquals, rinfo->clause);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
*joinquals = lappend(*joinquals, clause->clause);
|
{
|
||||||
|
/* joinquals shouldn't have been marked pseudoconstant */
|
||||||
|
Assert(!rinfo->pseudoconstant);
|
||||||
|
*joinquals = lappend(*joinquals, rinfo->clause);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.125 2006/06/06 17:59:58 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.126 2006/07/01 18:38:33 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -114,6 +114,8 @@ typedef struct PlannerInfo
|
|||||||
bool hasJoinRTEs; /* true if any RTEs are RTE_JOIN kind */
|
bool hasJoinRTEs; /* true if any RTEs are RTE_JOIN kind */
|
||||||
bool hasOuterJoins; /* true if any RTEs are outer joins */
|
bool hasOuterJoins; /* true if any RTEs are outer joins */
|
||||||
bool hasHavingQual; /* true if havingQual was non-null */
|
bool hasHavingQual; /* true if havingQual was non-null */
|
||||||
|
bool hasPseudoConstantQuals; /* true if any RestrictInfo has
|
||||||
|
* pseudoconstant = true */
|
||||||
} PlannerInfo;
|
} PlannerInfo;
|
||||||
|
|
||||||
|
|
||||||
@ -524,25 +526,16 @@ typedef struct AppendPath
|
|||||||
} AppendPath;
|
} AppendPath;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ResultPath represents use of a Result plan node. There are several
|
* ResultPath represents use of a Result plan node to compute a variable-free
|
||||||
* applications for this:
|
* targetlist with no underlying tables (a "SELECT expressions" query).
|
||||||
* * To compute a variable-free targetlist (a "SELECT expressions" query).
|
* The query could have a WHERE clause, too, represented by "quals".
|
||||||
* In this case subpath and path.parent will both be NULL. constantqual
|
|
||||||
* might or might not be empty ("SELECT expressions WHERE something").
|
|
||||||
* * To gate execution of a subplan with a one-time (variable-free) qual
|
|
||||||
* condition. path.parent is copied from the subpath.
|
|
||||||
* * To substitute for a scan plan when we have proven that no rows in
|
|
||||||
* a table will satisfy the query. subpath is NULL but path.parent
|
|
||||||
* references the not-to-be-scanned relation, and constantqual is
|
|
||||||
* a constant FALSE.
|
|
||||||
*
|
*
|
||||||
* Note that constantqual is a list of bare clauses, not RestrictInfos.
|
* Note that quals is a list of bare clauses, not RestrictInfos.
|
||||||
*/
|
*/
|
||||||
typedef struct ResultPath
|
typedef struct ResultPath
|
||||||
{
|
{
|
||||||
Path path;
|
Path path;
|
||||||
Path *subpath;
|
List *quals;
|
||||||
List *constantqual;
|
|
||||||
} ResultPath;
|
} ResultPath;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -732,6 +725,22 @@ typedef struct HashPath
|
|||||||
* OR/AND structure. This is a convenience for OR indexscan processing:
|
* OR/AND structure. This is a convenience for OR indexscan processing:
|
||||||
* indexquals taken from either the top level or an OR subclause will have
|
* indexquals taken from either the top level or an OR subclause will have
|
||||||
* associated RestrictInfo nodes.
|
* associated RestrictInfo nodes.
|
||||||
|
*
|
||||||
|
* The can_join flag is set true if the clause looks potentially useful as
|
||||||
|
* a merge or hash join clause, that is if it is a binary opclause with
|
||||||
|
* nonoverlapping sets of relids referenced in the left and right sides.
|
||||||
|
* (Whether the operator is actually merge or hash joinable isn't checked,
|
||||||
|
* however.)
|
||||||
|
*
|
||||||
|
* The pseudoconstant flag is set true if the clause contains no Vars of
|
||||||
|
* the current query level and no volatile functions. Such a clause can be
|
||||||
|
* pulled out and used as a one-time qual in a gating Result node. We keep
|
||||||
|
* pseudoconstant clauses in the same lists as other RestrictInfos so that
|
||||||
|
* the regular clause-pushing machinery can assign them to the correct join
|
||||||
|
* level, but they need to be treated specially for cost and selectivity
|
||||||
|
* estimates. Note that a pseudoconstant clause can never be an indexqual
|
||||||
|
* or merge or hash join clause, so it's of no interest to large parts of
|
||||||
|
* the planner.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef struct RestrictInfo
|
typedef struct RestrictInfo
|
||||||
@ -744,14 +753,9 @@ typedef struct RestrictInfo
|
|||||||
|
|
||||||
bool outerjoin_delayed; /* TRUE if delayed by outer join */
|
bool outerjoin_delayed; /* TRUE if delayed by outer join */
|
||||||
|
|
||||||
/*
|
bool can_join; /* see comment above */
|
||||||
* This flag is set true if the clause looks potentially useful as a merge
|
|
||||||
* or hash join clause, that is if it is a binary opclause with
|
bool pseudoconstant; /* see comment above */
|
||||||
* nonoverlapping sets of relids referenced in the left and right sides.
|
|
||||||
* (Whether the operator is actually merge or hash joinable isn't checked,
|
|
||||||
* however.)
|
|
||||||
*/
|
|
||||||
bool can_join;
|
|
||||||
|
|
||||||
/* The set of relids (varnos) actually referenced in the clause: */
|
/* The set of relids (varnos) actually referenced in the clause: */
|
||||||
Relids clause_relids;
|
Relids clause_relids;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.83 2006/03/05 15:58:57 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.84 2006/07/01 18:38:33 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -61,7 +61,6 @@ extern Relids find_nonnullable_rels(Node *clause);
|
|||||||
|
|
||||||
extern bool is_pseudo_constant_clause(Node *clause);
|
extern bool is_pseudo_constant_clause(Node *clause);
|
||||||
extern bool is_pseudo_constant_clause_relids(Node *clause, Relids relids);
|
extern bool is_pseudo_constant_clause_relids(Node *clause, Relids relids);
|
||||||
extern List *pull_constant_clauses(List *quals, List **constantQual);
|
|
||||||
|
|
||||||
extern bool has_distinct_clause(Query *query);
|
extern bool has_distinct_clause(Query *query);
|
||||||
extern bool has_distinct_on_clause(Query *query);
|
extern bool has_distinct_on_clause(Query *query);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.68 2006/06/06 17:59:58 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.69 2006/07/01 18:38:33 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -47,8 +47,7 @@ extern BitmapOrPath *create_bitmap_or_path(PlannerInfo *root,
|
|||||||
extern TidPath *create_tidscan_path(PlannerInfo *root, RelOptInfo *rel,
|
extern TidPath *create_tidscan_path(PlannerInfo *root, RelOptInfo *rel,
|
||||||
List *tidquals);
|
List *tidquals);
|
||||||
extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
|
extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
|
||||||
extern ResultPath *create_result_path(RelOptInfo *rel, Path *subpath,
|
extern ResultPath *create_result_path(List *quals);
|
||||||
List *constantqual);
|
|
||||||
extern MaterialPath *create_material_path(RelOptInfo *rel, Path *subpath);
|
extern MaterialPath *create_material_path(RelOptInfo *rel, Path *subpath);
|
||||||
extern UniquePath *create_unique_path(PlannerInfo *root, RelOptInfo *rel,
|
extern UniquePath *create_unique_path(PlannerInfo *root, RelOptInfo *rel,
|
||||||
Path *subpath);
|
Path *subpath);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.92 2006/03/05 15:58:57 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.93 2006/07/01 18:38:33 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -42,7 +42,6 @@ extern Sort *make_sort_from_sortclauses(PlannerInfo *root, List *sortcls,
|
|||||||
Plan *lefttree);
|
Plan *lefttree);
|
||||||
extern Sort *make_sort_from_groupcols(PlannerInfo *root, List *groupcls,
|
extern Sort *make_sort_from_groupcols(PlannerInfo *root, List *groupcls,
|
||||||
AttrNumber *grpColIdx, Plan *lefttree);
|
AttrNumber *grpColIdx, Plan *lefttree);
|
||||||
extern List *order_qual_clauses(PlannerInfo *root, List *clauses);
|
|
||||||
extern Agg *make_agg(PlannerInfo *root, List *tlist, List *qual,
|
extern Agg *make_agg(PlannerInfo *root, List *tlist, List *qual,
|
||||||
AggStrategy aggstrategy,
|
AggStrategy aggstrategy,
|
||||||
int numGroupCols, AttrNumber *grpColIdx,
|
int numGroupCols, AttrNumber *grpColIdx,
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.36 2006/03/05 15:58:57 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.37 2006/07/01 18:38:33 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -20,14 +20,18 @@
|
|||||||
extern RestrictInfo *make_restrictinfo(Expr *clause,
|
extern RestrictInfo *make_restrictinfo(Expr *clause,
|
||||||
bool is_pushed_down,
|
bool is_pushed_down,
|
||||||
bool outerjoin_delayed,
|
bool outerjoin_delayed,
|
||||||
|
bool pseudoconstant,
|
||||||
Relids required_relids);
|
Relids required_relids);
|
||||||
extern List *make_restrictinfo_from_bitmapqual(Path *bitmapqual,
|
extern List *make_restrictinfo_from_bitmapqual(Path *bitmapqual,
|
||||||
bool is_pushed_down,
|
bool is_pushed_down,
|
||||||
bool include_predicates);
|
bool include_predicates);
|
||||||
extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
|
extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
|
||||||
extern List *get_actual_clauses(List *restrictinfo_list);
|
extern List *get_actual_clauses(List *restrictinfo_list);
|
||||||
extern void get_actual_join_clauses(List *restrictinfo_list,
|
extern List *extract_actual_clauses(List *restrictinfo_list,
|
||||||
List **joinquals, List **otherquals);
|
bool pseudoconstant);
|
||||||
|
extern void extract_actual_join_clauses(List *restrictinfo_list,
|
||||||
|
List **joinquals,
|
||||||
|
List **otherquals);
|
||||||
extern List *remove_redundant_join_clauses(PlannerInfo *root,
|
extern List *remove_redundant_join_clauses(PlannerInfo *root,
|
||||||
List *restrictinfo_list,
|
List *restrictinfo_list,
|
||||||
bool isouterjoin);
|
bool isouterjoin);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user