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

Lines changed: 51 additions & 1 deletion
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

Lines changed: 37 additions & 3 deletions
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

Lines changed: 23 additions & 4 deletions
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

Lines changed: 1 addition & 2 deletions
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

Lines changed: 81 additions & 1 deletion
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy