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

Commit d007a95

Browse files
committed
Simple constraint exclusion. For now, only child tables of inheritance
scans are candidates for exclusion; this should be fixed eventually. Simon Riggs, with some help from Tom Lane.
1 parent 9af9d67 commit d007a95

File tree

14 files changed

+621
-105
lines changed

14 files changed

+621
-105
lines changed

doc/src/sgml/runtime.sgml

+51-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.338 2005/07/14 05:13:38 tgl Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.339 2005/07/23 21:05:45 tgl Exp $
33
-->
44

55
<chapter Id="runtime">
@@ -2278,6 +2278,56 @@ archive_command = 'copy "%p" /mnt/server/archivedir/"%f"' # Windows
22782278
</listitem>
22792279
</varlistentry>
22802280

2281+
<varlistentry id="guc-enable-constraint-exclusion" xreflabel="enable_constraint_exclusion">
2282+
<term><varname>enable_constraint_exclusion</varname> (<type>boolean</type>)</term>
2283+
<indexterm>
2284+
<primary>constraint exclusion</primary>
2285+
</indexterm>
2286+
<indexterm>
2287+
<primary><varname>enable_constraint_exclusion</> configuration parameter</primary>
2288+
</indexterm>
2289+
<listitem>
2290+
<para>
2291+
Enables or disables the query planner's use of table constraints.
2292+
The default is <literal>off</>.
2293+
</para>
2294+
2295+
<para>
2296+
When this parameter is <literal>on</>, the planner compares query
2297+
conditions to table CHECK constraints, and omits scanning tables
2298+
for which the conditions contradict the constraints. (Presently
2299+
this is done only for child tables of inheritance scans.) For
2300+
example:
2301+
2302+
<programlisting>
2303+
CREATE TABLE parent(key integer, ...);
2304+
CREATE TABLE child1000(check (key between 1000 and 1999)) INHERITS(parent);
2305+
CREATE TABLE child2000(check (key between 2000 and 2999)) INHERITS(parent);
2306+
...
2307+
SELECT * FROM parent WHERE key = 2400;
2308+
</programlisting>
2309+
2310+
With constraint exclusion enabled, this SELECT will not scan
2311+
<structname>child1000</> at all. This can improve performance when
2312+
inheritance is used to build partitioned tables.
2313+
</para>
2314+
2315+
<para>
2316+
Currently, <varname>enable_constraint_exclusion</> defaults to
2317+
<literal>off</>, because it creates a risk of wrong answers when
2318+
query plans are cached: if a table constraint is changed or dropped,
2319+
the previously generated plan may now be wrong, and there is no
2320+
built-in mechanism to force re-planning. (This deficiency will
2321+
probably be addressed in a future
2322+
<productname>PostgreSQL</productname> release.) Another reason
2323+
for keeping it off is that the constraint checks are relatively
2324+
expensive to make, and in many circumstances will yield no savings.
2325+
It is recommended to turn this on only if you are actually using
2326+
partitioned tables designed to take advantage of the feature.
2327+
</para>
2328+
</listitem>
2329+
</varlistentry>
2330+
22812331
<varlistentry id="guc-from-collapse-limit" xreflabel="from_collapse_limit">
22822332
<term><varname>from_collapse_limit</varname> (<type>integer</type>)</term>
22832333
<indexterm>

src/backend/optimizer/path/allpaths.c

+37-3
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.134 2005/06/10 03:32:21 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.135 2005/07/23 21:05:46 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
1515

1616
#include "postgres.h"
1717

18+
#include "nodes/makefuncs.h"
1819
#ifdef OPTIMIZER_DEBUG
1920
#include "nodes/print.h"
2021
#endif
@@ -25,6 +26,7 @@
2526
#include "optimizer/paths.h"
2627
#include "optimizer/plancat.h"
2728
#include "optimizer/planner.h"
29+
#include "optimizer/predtest.h"
2830
#include "optimizer/prep.h"
2931
#include "optimizer/var.h"
3032
#include "parser/parsetree.h"
@@ -34,6 +36,7 @@
3436

3537

3638
/* These parameters are set by GUC */
39+
bool enable_constraint_exclusion = false;
3740
bool enable_geqo = false; /* just in case GUC doesn't set it */
3841
int geqo_threshold;
3942

@@ -311,7 +314,37 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
311314
childOID);
312315

313316
/*
314-
* Now compute child access paths, and save the cheapest.
317+
* If we can prove we don't need to scan this child via constraint
318+
* exclusion, just ignore it. (We have to have converted the
319+
* baserestrictinfo Vars before we can make the test.)
320+
*/
321+
if (enable_constraint_exclusion)
322+
{
323+
List *constraint_pred;
324+
325+
constraint_pred = get_relation_constraints(childOID, childrel);
326+
/*
327+
* We do not currently enforce that CHECK constraints contain
328+
* only immutable functions, so it's necessary to check here.
329+
* We daren't draw conclusions from plan-time evaluation of
330+
* non-immutable functions.
331+
*/
332+
if (!contain_mutable_functions((Node *) constraint_pred))
333+
{
334+
/*
335+
* The constraints are effectively ANDed together, so we can
336+
* just try to refute the entire collection at once. This may
337+
* allow us to make proofs that would fail if we took them
338+
* individually.
339+
*/
340+
if (predicate_refuted_by(constraint_pred,
341+
childrel->baserestrictinfo))
342+
continue;
343+
}
344+
}
345+
346+
/*
347+
* Compute the child's access paths, and save the cheapest.
315348
*/
316349
set_plain_rel_pathlist(root, childrel, childrte);
317350

@@ -345,7 +378,8 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
345378

346379
/*
347380
* Finally, build Append path and install it as the only access path
348-
* for the parent rel.
381+
* for the parent rel. (Note: this is correct even if we have zero
382+
* or one live subpath due to constraint exclusion.)
349383
*/
350384
add_path(rel, (Path *) create_append_path(rel, subpaths));
351385

src/backend/optimizer/plan/createplan.c

+23-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*
1111
*
1212
* IDENTIFICATION
13-
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.194 2005/07/15 22:02:51 tgl Exp $
13+
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.195 2005/07/23 21:05:46 tgl Exp $
1414
*
1515
*-------------------------------------------------------------------------
1616
*/
@@ -40,7 +40,7 @@ static List *build_relation_tlist(RelOptInfo *rel);
4040
static bool use_physical_tlist(RelOptInfo *rel);
4141
static void disuse_physical_tlist(Plan *plan, Path *path);
4242
static Join *create_join_plan(PlannerInfo *root, JoinPath *best_path);
43-
static Append *create_append_plan(PlannerInfo *root, AppendPath *best_path);
43+
static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path);
4444
static Result *create_result_plan(PlannerInfo *root, ResultPath *best_path);
4545
static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path);
4646
static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path);
@@ -435,14 +435,33 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
435435
*
436436
* Returns a Plan node.
437437
*/
438-
static Append *
438+
static Plan *
439439
create_append_plan(PlannerInfo *root, AppendPath *best_path)
440440
{
441441
Append *plan;
442442
List *tlist = build_relation_tlist(best_path->path.parent);
443443
List *subplans = NIL;
444444
ListCell *subpaths;
445445

446+
/*
447+
* It is possible for the subplans list to contain only one entry,
448+
* or even no entries. Handle these cases specially.
449+
*
450+
* XXX ideally, if there's just one entry, we'd not bother to generate
451+
* an Append node but just return the single child. At the moment this
452+
* does not work because the varno of the child scan plan won't match
453+
* the parent-rel Vars it'll be asked to emit.
454+
*/
455+
if (best_path->subpaths == NIL)
456+
{
457+
/* Generate a Result plan with constant-FALSE gating qual */
458+
return (Plan *) make_result(tlist,
459+
(Node *) list_make1(makeBoolConst(false,
460+
false)),
461+
NULL);
462+
}
463+
464+
/* Normal case with multiple subpaths */
446465
foreach(subpaths, best_path->subpaths)
447466
{
448467
Path *subpath = (Path *) lfirst(subpaths);
@@ -452,7 +471,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
452471

453472
plan = make_append(subplans, false, tlist);
454473

455-
return plan;
474+
return (Plan *) plan;
456475
}
457476

458477
/*

src/backend/optimizer/plan/planagg.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.5 2005/06/05 22:32:56 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.6 2005/07/23 21:05:46 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -184,7 +184,6 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path)
184184
*/
185185
if (IsA(best_path, ResultPath))
186186
{
187-
Assert(((ResultPath *) best_path)->subpath != NULL);
188187
constant_quals = ((ResultPath *) best_path)->constantqual;
189188
/* no need to do this more than once: */
190189
constant_quals = order_qual_clauses(root, constant_quals);

src/backend/optimizer/util/plancat.c

+81-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.112 2005/06/13 23:14:48 tgl Exp $
12+
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.113 2005/07/23 21:05:47 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -25,6 +25,7 @@
2525
#include "nodes/makefuncs.h"
2626
#include "optimizer/clauses.h"
2727
#include "optimizer/plancat.h"
28+
#include "optimizer/prep.h"
2829
#include "optimizer/tlist.h"
2930
#include "parser/parsetree.h"
3031
#include "parser/parse_expr.h"
@@ -359,6 +360,85 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
359360
}
360361
}
361362

363+
364+
/*
365+
* get_relation_constraints
366+
*
367+
* Retrieve the CHECK constraint expressions of the given relation.
368+
*
369+
* Returns a List (possibly empty) of constraint expressions. Each one
370+
* has been canonicalized, and its Vars are changed to have the varno
371+
* indicated by rel->relid. This allows the expressions to be easily
372+
* compared to expressions taken from WHERE.
373+
*
374+
* Note: at present this is invoked at most once per relation per planner
375+
* run, and in many cases it won't be invoked at all, so there seems no
376+
* point in caching the data in RelOptInfo.
377+
*/
378+
List *
379+
get_relation_constraints(Oid relationObjectId, RelOptInfo *rel)
380+
{
381+
List *result = NIL;
382+
Index varno = rel->relid;
383+
Relation relation;
384+
TupleConstr *constr;
385+
386+
/*
387+
* We assume the relation has already been safely locked.
388+
*/
389+
relation = heap_open(relationObjectId, NoLock);
390+
391+
constr = relation->rd_att->constr;
392+
if (constr != NULL)
393+
{
394+
int num_check = constr->num_check;
395+
int i;
396+
397+
for (i = 0; i < num_check; i++)
398+
{
399+
Node *cexpr;
400+
401+
cexpr = stringToNode(constr->check[i].ccbin);
402+
403+
/*
404+
* Run each expression through const-simplification and
405+
* canonicalization. This is not just an optimization, but is
406+
* necessary, because we will be comparing it to
407+
* similarly-processed qual clauses, and may fail to detect valid
408+
* matches without this. This must match the processing done to
409+
* qual clauses in preprocess_expression()! (We can skip the
410+
* stuff involving subqueries, however, since we don't allow any
411+
* in check constraints.)
412+
*/
413+
cexpr = eval_const_expressions(cexpr);
414+
415+
cexpr = (Node *) canonicalize_qual((Expr *) cexpr);
416+
417+
/*
418+
* Also mark any coercion format fields as "don't care", so that
419+
* we can match to both explicit and implicit coercions.
420+
*/
421+
set_coercionform_dontcare(cexpr);
422+
423+
/* Fix Vars to have the desired varno */
424+
if (varno != 1)
425+
ChangeVarNodes(cexpr, 1, varno, 0);
426+
427+
/*
428+
* Finally, convert to implicit-AND format (that is, a List)
429+
* and append the resulting item(s) to our output list.
430+
*/
431+
result = list_concat(result,
432+
make_ands_implicit((Expr *) cexpr));
433+
}
434+
}
435+
436+
heap_close(relation, NoLock);
437+
438+
return result;
439+
}
440+
441+
362442
/*
363443
* build_physical_tlist
364444
*

0 commit comments

Comments
 (0)