Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit cffd89c

Browse files
committed
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.
1 parent 68628fc commit cffd89c

File tree

20 files changed

+465
-295
lines changed

20 files changed

+465
-295
lines changed

src/backend/nodes/copyfuncs.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* Portions Copyright (c) 1994, Regents of the University of California
1616
*
1717
* IDENTIFICATION
18-
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.337 2006/06/27 03:43:19 momjian Exp $
18+
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.338 2006/07/01 18:38:32 tgl Exp $
1919
*
2020
*-------------------------------------------------------------------------
2121
*/
@@ -1264,6 +1264,7 @@ _copyRestrictInfo(RestrictInfo *from)
12641264
COPY_SCALAR_FIELD(is_pushed_down);
12651265
COPY_SCALAR_FIELD(outerjoin_delayed);
12661266
COPY_SCALAR_FIELD(can_join);
1267+
COPY_SCALAR_FIELD(pseudoconstant);
12671268
COPY_BITMAPSET_FIELD(clause_relids);
12681269
COPY_BITMAPSET_FIELD(required_relids);
12691270
COPY_BITMAPSET_FIELD(left_relids);

src/backend/nodes/outfuncs.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.274 2006/04/30 18:30:39 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.275 2006/07/01 18:38:32 tgl Exp $
1212
*
1313
* NOTES
1414
* Every node type that can appear in stored rules' parsetrees *must*
@@ -1107,8 +1107,7 @@ _outResultPath(StringInfo str, ResultPath *node)
11071107

11081108
_outPathInfo(str, (Path *) node);
11091109

1110-
WRITE_NODE_FIELD(subpath);
1111-
WRITE_NODE_FIELD(constantqual);
1110+
WRITE_NODE_FIELD(quals);
11121111
}
11131112

11141113
static void
@@ -1185,6 +1184,7 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node)
11851184
WRITE_BOOL_FIELD(hasJoinRTEs);
11861185
WRITE_BOOL_FIELD(hasOuterJoins);
11871186
WRITE_BOOL_FIELD(hasHavingQual);
1187+
WRITE_BOOL_FIELD(hasPseudoConstantQuals);
11881188
}
11891189

11901190
static void
@@ -1252,6 +1252,7 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
12521252
WRITE_BOOL_FIELD(is_pushed_down);
12531253
WRITE_BOOL_FIELD(outerjoin_delayed);
12541254
WRITE_BOOL_FIELD(can_join);
1255+
WRITE_BOOL_FIELD(pseudoconstant);
12551256
WRITE_BITMAPSET_FIELD(clause_relids);
12561257
WRITE_BITMAPSET_FIELD(required_relids);
12571258
WRITE_BITMAPSET_FIELD(left_relids);

src/backend/optimizer/README

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ RelOptInfo - a relation or joined relations
329329
BitmapHeapPath - top of a bitmapped index scan
330330
TidPath - scan by CTID
331331
AppendPath - append multiple subpaths together
332-
ResultPath - a Result plan node (used for variable-free tlist or qual)
332+
ResultPath - a Result plan node (used for FROM-less SELECT)
333333
MaterialPath - a Material plan node
334334
UniquePath - remove duplicate rows
335335
NestPath - nested-loop joins

src/backend/optimizer/path/allpaths.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.146 2006/05/02 04:34:18 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.147 2006/07/01 18:38:32 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -446,7 +446,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
446446
* There are several cases where we cannot push down clauses. Restrictions
447447
* involving the subquery are checked by subquery_is_pushdown_safe().
448448
* Restrictions on individual clauses are checked by
449-
* qual_is_pushdown_safe().
449+
* qual_is_pushdown_safe(). Also, we don't want to push down
450+
* pseudoconstant clauses; better to have the gating node above the
451+
* subquery.
450452
*
451453
* Non-pushed-down clauses will get evaluated as qpquals of the
452454
* SubqueryScan node.
@@ -466,7 +468,8 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
466468
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
467469
Node *clause = (Node *) rinfo->clause;
468470

469-
if (qual_is_pushdown_safe(subquery, rti, clause, differentTypes))
471+
if (!rinfo->pseudoconstant &&
472+
qual_is_pushdown_safe(subquery, rti, clause, differentTypes))
470473
{
471474
/* Push it down */
472475
subquery_push_qual(subquery, rte, rti, clause);
@@ -1066,7 +1069,6 @@ print_path(PlannerInfo *root, Path *path, int indent)
10661069
break;
10671070
case T_ResultPath:
10681071
ptype = "Result";
1069-
subpath = ((ResultPath *) path)->subpath;
10701072
break;
10711073
case T_MaterialPath:
10721074
ptype = "Material";

src/backend/optimizer/path/clausesel.c

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.79 2006/03/07 01:00:15 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.80 2006/07/01 18:38:32 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -117,10 +117,18 @@ clauselist_selectivity(PlannerInfo *root,
117117

118118
/*
119119
* Check for being passed a RestrictInfo.
120+
*
121+
* If it's a pseudoconstant RestrictInfo, then s2 is either 1.0 or
122+
* 0.0; just use that rather than looking for range pairs.
120123
*/
121124
if (IsA(clause, RestrictInfo))
122125
{
123126
rinfo = (RestrictInfo *) clause;
127+
if (rinfo->pseudoconstant)
128+
{
129+
s1 = s1 * s2;
130+
continue;
131+
}
124132
clause = (Node *) rinfo->clause;
125133
}
126134
else
@@ -422,6 +430,20 @@ clause_selectivity(PlannerInfo *root,
422430
{
423431
rinfo = (RestrictInfo *) clause;
424432

433+
/*
434+
* If the clause is marked pseudoconstant, then it will be used as
435+
* a gating qual and should not affect selectivity estimates; hence
436+
* return 1.0. The only exception is that a constant FALSE may
437+
* be taken as having selectivity 0.0, since it will surely mean
438+
* no rows out of the plan. This case is simple enough that we
439+
* need not bother caching the result.
440+
*/
441+
if (rinfo->pseudoconstant)
442+
{
443+
if (! IsA(rinfo->clause, Const))
444+
return s1;
445+
}
446+
425447
/*
426448
* If possible, cache the result of the selectivity calculation for
427449
* the clause. We can cache if varRelid is zero or the clause
@@ -509,7 +531,10 @@ clause_selectivity(PlannerInfo *root,
509531
else if (IsA(clause, Const))
510532
{
511533
/* bool constant is pretty easy... */
512-
s1 = ((bool) ((Const *) clause)->constvalue) ? 1.0 : 0.0;
534+
Const *con = (Const *) clause;
535+
536+
s1 = con->constisnull ? 0.0 :
537+
DatumGetBool(con->constvalue) ? 1.0 : 0.0;
513538
}
514539
else if (IsA(clause, Param))
515540
{
@@ -519,7 +544,10 @@ clause_selectivity(PlannerInfo *root,
519544
if (IsA(subst, Const))
520545
{
521546
/* bool constant is pretty easy... */
522-
s1 = ((bool) ((Const *) subst)->constvalue) ? 1.0 : 0.0;
547+
Const *con = (Const *) subst;
548+
549+
s1 = con->constisnull ? 0.0 :
550+
DatumGetBool(con->constvalue) ? 1.0 : 0.0;
523551
}
524552
else
525553
{

src/backend/optimizer/path/costsize.c

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
* Portions Copyright (c) 1994, Regents of the University of California
5555
*
5656
* IDENTIFICATION
57-
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.158 2006/06/06 17:59:57 tgl Exp $
57+
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.159 2006/07/01 18:38:32 tgl Exp $
5858
*
5959
*-------------------------------------------------------------------------
6060
*/
@@ -1604,20 +1604,29 @@ cost_qual_eval(QualCost *cost, List *quals)
16041604
* routine's use, so that it's not necessary to evaluate the qual
16051605
* clause's cost more than once. If the clause's cost hasn't been
16061606
* computed yet, the field's startup value will contain -1.
1607+
*
1608+
* If the RestrictInfo is marked pseudoconstant, it will be tested
1609+
* only once, so treat its cost as all startup cost.
16071610
*/
16081611
if (qual && IsA(qual, RestrictInfo))
16091612
{
1610-
RestrictInfo *restrictinfo = (RestrictInfo *) qual;
1613+
RestrictInfo *rinfo = (RestrictInfo *) qual;
16111614

1612-
if (restrictinfo->eval_cost.startup < 0)
1615+
if (rinfo->eval_cost.startup < 0)
16131616
{
1614-
restrictinfo->eval_cost.startup = 0;
1615-
restrictinfo->eval_cost.per_tuple = 0;
1616-
cost_qual_eval_walker((Node *) restrictinfo->clause,
1617-
&restrictinfo->eval_cost);
1617+
rinfo->eval_cost.startup = 0;
1618+
rinfo->eval_cost.per_tuple = 0;
1619+
cost_qual_eval_walker((Node *) rinfo->clause,
1620+
&rinfo->eval_cost);
1621+
if (rinfo->pseudoconstant)
1622+
{
1623+
/* count one execution during startup */
1624+
rinfo->eval_cost.startup += rinfo->eval_cost.per_tuple;
1625+
rinfo->eval_cost.per_tuple = 0;
1626+
}
16181627
}
1619-
cost->startup += restrictinfo->eval_cost.startup;
1620-
cost->per_tuple += restrictinfo->eval_cost.per_tuple;
1628+
cost->startup += rinfo->eval_cost.startup;
1629+
cost->per_tuple += rinfo->eval_cost.per_tuple;
16211630
}
16221631
else
16231632
{
@@ -1876,7 +1885,9 @@ set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel,
18761885
*
18771886
* If we are doing an outer join, take that into account: the output must
18781887
* be at least as large as the non-nullable input. (Is there any chance
1879-
* of being even smarter?)
1888+
* of being even smarter?) (XXX this is not really right, because it
1889+
* assumes all the restriction clauses are join clauses; we should figure
1890+
* pushed-down clauses separately.)
18801891
*
18811892
* For JOIN_IN and variants, the Cartesian product is figured with respect
18821893
* to a unique-ified input, and then we can clamp to the size of the other

src/backend/optimizer/path/indxpath.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.208 2006/06/07 17:08:07 tgl Exp $
12+
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.209 2006/07/01 18:38:32 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -998,6 +998,15 @@ match_clause_to_indexcol(IndexOptInfo *index,
998998
Oid expr_op;
999999
bool plain_op;
10001000

1001+
/*
1002+
* Never match pseudoconstants to indexes. (Normally this could not
1003+
* happen anyway, since a pseudoconstant clause couldn't contain a
1004+
* Var, but what if someone builds an expression index on a constant?
1005+
* It's not totally unreasonable to do so with a partial index, either.)
1006+
*/
1007+
if (rinfo->pseudoconstant)
1008+
return false;
1009+
10011010
/* First check for boolean-index cases. */
10021011
if (IsBooleanOpclass(opclass))
10031012
{
@@ -2212,6 +2221,7 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
22122221
make_restrictinfo(boolqual,
22132222
true,
22142223
false,
2224+
false,
22152225
NULL));
22162226
continue;
22172227
}
@@ -2577,7 +2587,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
25772587
matching_cols);
25782588
rc->rargs = list_truncate((List *) copyObject(clause->rargs),
25792589
matching_cols);
2580-
return make_restrictinfo((Expr *) rc, true, false, NULL);
2590+
return make_restrictinfo((Expr *) rc, true, false, false, NULL);
25812591
}
25822592
else
25832593
{
@@ -2586,7 +2596,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
25862596
opexpr = make_opclause(linitial_oid(new_ops), BOOLOID, false,
25872597
copyObject(linitial(clause->largs)),
25882598
copyObject(linitial(clause->rargs)));
2589-
return make_restrictinfo(opexpr, true, false, NULL);
2599+
return make_restrictinfo(opexpr, true, false, false, NULL);
25902600
}
25912601
}
25922602

@@ -2678,7 +2688,7 @@ prefix_quals(Node *leftop, Oid opclass,
26782688
elog(ERROR, "no = operator for opclass %u", opclass);
26792689
expr = make_opclause(oproid, BOOLOID, false,
26802690
(Expr *) leftop, (Expr *) prefix_const);
2681-
result = list_make1(make_restrictinfo(expr, true, false, NULL));
2691+
result = list_make1(make_restrictinfo(expr, true, false, false, NULL));
26822692
return result;
26832693
}
26842694

@@ -2693,7 +2703,7 @@ prefix_quals(Node *leftop, Oid opclass,
26932703
elog(ERROR, "no >= operator for opclass %u", opclass);
26942704
expr = make_opclause(oproid, BOOLOID, false,
26952705
(Expr *) leftop, (Expr *) prefix_const);
2696-
result = list_make1(make_restrictinfo(expr, true, false, NULL));
2706+
result = list_make1(make_restrictinfo(expr, true, false, false, NULL));
26972707

26982708
/*-------
26992709
* If we can create a string larger than the prefix, we can say
@@ -2709,7 +2719,8 @@ prefix_quals(Node *leftop, Oid opclass,
27092719
elog(ERROR, "no < operator for opclass %u", opclass);
27102720
expr = make_opclause(oproid, BOOLOID, false,
27112721
(Expr *) leftop, (Expr *) greaterstr);
2712-
result = lappend(result, make_restrictinfo(expr, true, false, NULL));
2722+
result = lappend(result,
2723+
make_restrictinfo(expr, true, false, false, NULL));
27132724
}
27142725

27152726
return result;
@@ -2772,7 +2783,7 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop)
27722783
(Expr *) leftop,
27732784
(Expr *) makeConst(datatype, -1, opr1right,
27742785
false, false));
2775-
result = list_make1(make_restrictinfo(expr, true, false, NULL));
2786+
result = list_make1(make_restrictinfo(expr, true, false, false, NULL));
27762787

27772788
/* create clause "key <= network_scan_last( rightop )" */
27782789

@@ -2787,7 +2798,8 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop)
27872798
(Expr *) leftop,
27882799
(Expr *) makeConst(datatype, -1, opr2right,
27892800
false, false));
2790-
result = lappend(result, make_restrictinfo(expr, true, false, NULL));
2801+
result = lappend(result,
2802+
make_restrictinfo(expr, true, false, false, NULL));
27912803

27922804
return result;
27932805
}

0 commit comments

Comments
 (0)