Commit d007a950 authored by Tom Lane's avatar Tom Lane

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.
parent 9af9d674
<!--
$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.338 2005/07/14 05:13:38 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.339 2005/07/23 21:05:45 tgl Exp $
-->
<chapter Id="runtime">
......@@ -2278,6 +2278,56 @@ archive_command = 'copy "%p" /mnt/server/archivedir/"%f"' # Windows
</listitem>
</varlistentry>
<varlistentry id="guc-enable-constraint-exclusion" xreflabel="enable_constraint_exclusion">
<term><varname>enable_constraint_exclusion</varname> (<type>boolean</type>)</term>
<indexterm>
<primary>constraint exclusion</primary>
</indexterm>
<indexterm>
<primary><varname>enable_constraint_exclusion</> configuration parameter</primary>
</indexterm>
<listitem>
<para>
Enables or disables the query planner's use of table constraints.
The default is <literal>off</>.
</para>
<para>
When this parameter is <literal>on</>, the planner compares query
conditions to table CHECK constraints, and omits scanning tables
for which the conditions contradict the constraints. (Presently
this is done only for child tables of inheritance scans.) For
example:
<programlisting>
CREATE TABLE parent(key integer, ...);
CREATE TABLE child1000(check (key between 1000 and 1999)) INHERITS(parent);
CREATE TABLE child2000(check (key between 2000 and 2999)) INHERITS(parent);
...
SELECT * FROM parent WHERE key = 2400;
</programlisting>
With constraint exclusion enabled, this SELECT will not scan
<structname>child1000</> at all. This can improve performance when
inheritance is used to build partitioned tables.
</para>
<para>
Currently, <varname>enable_constraint_exclusion</> defaults to
<literal>off</>, because it creates a risk of wrong answers when
query plans are cached: if a table constraint is changed or dropped,
the previously generated plan may now be wrong, and there is no
built-in mechanism to force re-planning. (This deficiency will
probably be addressed in a future
<productname>PostgreSQL</productname> release.) Another reason
for keeping it off is that the constraint checks are relatively
expensive to make, and in many circumstances will yield no savings.
It is recommended to turn this on only if you are actually using
partitioned tables designed to take advantage of the feature.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-from-collapse-limit" xreflabel="from_collapse_limit">
<term><varname>from_collapse_limit</varname> (<type>integer</type>)</term>
<indexterm>
......
......@@ -8,13 +8,14 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.134 2005/06/10 03:32:21 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.135 2005/07/23 21:05:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/makefuncs.h"
#ifdef OPTIMIZER_DEBUG
#include "nodes/print.h"
#endif
......@@ -25,6 +26,7 @@
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
#include "optimizer/planner.h"
#include "optimizer/predtest.h"
#include "optimizer/prep.h"
#include "optimizer/var.h"
#include "parser/parsetree.h"
......@@ -34,6 +36,7 @@
/* These parameters are set by GUC */
bool enable_constraint_exclusion = false;
bool enable_geqo = false; /* just in case GUC doesn't set it */
int geqo_threshold;
......@@ -311,7 +314,37 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
childOID);
/*
* Now compute child access paths, and save the cheapest.
* If we can prove we don't need to scan this child via constraint
* exclusion, just ignore it. (We have to have converted the
* baserestrictinfo Vars before we can make the test.)
*/
if (enable_constraint_exclusion)
{
List *constraint_pred;
constraint_pred = get_relation_constraints(childOID, childrel);
/*
* We do not currently enforce that CHECK constraints contain
* only immutable functions, so it's necessary to check here.
* We daren't draw conclusions from plan-time evaluation of
* non-immutable functions.
*/
if (!contain_mutable_functions((Node *) constraint_pred))
{
/*
* The constraints are effectively ANDed together, so we can
* just try to refute the entire collection at once. This may
* allow us to make proofs that would fail if we took them
* individually.
*/
if (predicate_refuted_by(constraint_pred,
childrel->baserestrictinfo))
continue;
}
}
/*
* Compute the child's access paths, and save the cheapest.
*/
set_plain_rel_pathlist(root, childrel, childrte);
......@@ -345,7 +378,8 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
/*
* Finally, build Append path and install it as the only access path
* for the parent rel.
* for the parent rel. (Note: this is correct even if we have zero
* or one live subpath due to constraint exclusion.)
*/
add_path(rel, (Path *) create_append_path(rel, subpaths));
......
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.194 2005/07/15 22:02:51 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.195 2005/07/23 21:05:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -40,7 +40,7 @@ static List *build_relation_tlist(RelOptInfo *rel);
static bool use_physical_tlist(RelOptInfo *rel);
static void disuse_physical_tlist(Plan *plan, Path *path);
static Join *create_join_plan(PlannerInfo *root, JoinPath *best_path);
static Append *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 Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path);
static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path);
......@@ -435,7 +435,7 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
*
* Returns a Plan node.
*/
static Append *
static Plan *
create_append_plan(PlannerInfo *root, AppendPath *best_path)
{
Append *plan;
......@@ -443,6 +443,25 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
List *subplans = NIL;
ListCell *subpaths;
/*
* It is possible for the subplans list to contain only one entry,
* or even no entries. Handle these cases specially.
*
* XXX ideally, if there's just one entry, we'd not bother to generate
* an Append node but just return the single child. At the moment this
* does not work because the varno of the child scan plan won't match
* the parent-rel Vars it'll be asked to emit.
*/
if (best_path->subpaths == NIL)
{
/* Generate a Result plan with constant-FALSE gating qual */
return (Plan *) make_result(tlist,
(Node *) list_make1(makeBoolConst(false,
false)),
NULL);
}
/* Normal case with multiple subpaths */
foreach(subpaths, best_path->subpaths)
{
Path *subpath = (Path *) lfirst(subpaths);
......@@ -452,7 +471,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
plan = make_append(subplans, false, tlist);
return plan;
return (Plan *) plan;
}
/*
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.5 2005/06/05 22:32:56 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.6 2005/07/23 21:05:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -184,7 +184,6 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path)
*/
if (IsA(best_path, ResultPath))
{
Assert(((ResultPath *) best_path)->subpath != NULL);
constant_quals = ((ResultPath *) best_path)->constantqual;
/* no need to do this more than once: */
constant_quals = order_qual_clauses(root, constant_quals);
......
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.112 2005/06/13 23:14:48 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.113 2005/07/23 21:05:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -25,6 +25,7 @@
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/plancat.h"
#include "optimizer/prep.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "parser/parse_expr.h"
......@@ -359,6 +360,85 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
}
}
/*
* get_relation_constraints
*
* Retrieve the CHECK constraint expressions of the given relation.
*
* Returns a List (possibly empty) of constraint expressions. Each one
* has been canonicalized, and its Vars are changed to have the varno
* indicated by rel->relid. This allows the expressions to be easily
* compared to expressions taken from WHERE.
*
* Note: at present this is invoked at most once per relation per planner
* run, and in many cases it won't be invoked at all, so there seems no
* point in caching the data in RelOptInfo.
*/
List *
get_relation_constraints(Oid relationObjectId, RelOptInfo *rel)
{
List *result = NIL;
Index varno = rel->relid;
Relation relation;
TupleConstr *constr;
/*
* We assume the relation has already been safely locked.
*/
relation = heap_open(relationObjectId, NoLock);
constr = relation->rd_att->constr;
if (constr != NULL)
{
int num_check = constr->num_check;
int i;
for (i = 0; i < num_check; i++)
{
Node *cexpr;
cexpr = stringToNode(constr->check[i].ccbin);
/*
* Run each expression through const-simplification and
* canonicalization. This is not just an optimization, but is
* necessary, because we will be comparing it to
* similarly-processed qual clauses, and may fail to detect valid
* matches without this. This must match the processing done to
* qual clauses in preprocess_expression()! (We can skip the
* stuff involving subqueries, however, since we don't allow any
* in check constraints.)
*/
cexpr = eval_const_expressions(cexpr);
cexpr = (Node *) canonicalize_qual((Expr *) cexpr);
/*
* Also mark any coercion format fields as "don't care", so that
* we can match to both explicit and implicit coercions.
*/
set_coercionform_dontcare(cexpr);
/* Fix Vars to have the desired varno */
if (varno != 1)
ChangeVarNodes(cexpr, 1, varno, 0);
/*
* Finally, convert to implicit-AND format (that is, a List)
* and append the resulting item(s) to our output list.
*/
result = list_concat(result,
make_ands_implicit((Expr *) cexpr));
}
}
heap_close(relation, NoLock);
return result;
}
/*
* build_physical_tlist
*
......
This diff is collapsed.
......@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.276 2005/07/21 18:06:12 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.277 2005/07/23 21:05:47 tgl Exp $
*
*--------------------------------------------------------------------
*/
......@@ -435,6 +435,16 @@ static struct config_bool ConfigureNamesBool[] =
&enable_hashjoin,
true, NULL, NULL
},
{
{"enable_constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER,
gettext_noop("Enables the planner's use of constraints in queries."),
gettext_noop("Constraints will be examined to exclude tables "
"that can be proven not to be required to produce "
"a correct result for the query.")
},
&enable_constraint_exclusion,
false, NULL, NULL
},
{
{"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
gettext_noop("Enables genetic query optimization."),
......
......@@ -173,6 +173,7 @@
# - Other Planner Options -
#default_statistics_target = 10 # range 1-1000
#enable_constraint_exclusion = off
#from_collapse_limit = 8
#join_collapse_limit = 8 # 1 disables collapsing of explicit JOINs
......
......@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2005, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.133 2005/06/22 21:14:30 tgl Exp $
* $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.134 2005/07/23 21:05:47 tgl Exp $
*/
/*----------------------------------------------------------------------
......@@ -540,6 +540,7 @@ psql_completion(char *text, int start, int end)
"dynamic_library_path",
"effective_cache_size",
"enable_bitmapscan",
"enable_constraint_exclusion",
"enable_hashagg",
"enable_hashjoin",
"enable_indexscan",
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.116 2005/07/02 23:00:42 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.117 2005/07/23 21:05:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -516,6 +516,9 @@ typedef struct TidPath
* AppendPath represents an Append plan, ie, successive execution of
* several member plans. Currently it is only used to handle expansion
* of inheritance trees.
*
* Note: it is possible for "subpaths" to contain only one, or even no,
* elements. These cases are optimized during create_append_plan.
*/
typedef struct AppendPath
{
......@@ -524,10 +527,17 @@ typedef struct AppendPath
} AppendPath;
/*
* ResultPath represents use of a Result plan node, either to compute a
* variable-free targetlist or to gate execution of a subplan with a
* one-time (variable-free) qual condition. Note that in the former case
* path.parent will be NULL; in the latter case it is copied from the subpath.
* ResultPath represents use of a Result plan node. There are several
* applications for this:
* * To compute a variable-free targetlist (a "SELECT expressions" query).
* 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.
*/
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.68 2005/06/05 22:32:58 tgl Exp $
* $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.69 2005/07/23 21:05:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -49,6 +49,7 @@ extern bool enable_hashagg;
extern bool enable_nestloop;
extern bool enable_mergejoin;
extern bool enable_hashjoin;
extern bool enable_constraint_exclusion;
extern double clamp_row_est(double nrows);
extern void cost_seqscan(Path *path, PlannerInfo *root, RelOptInfo *baserel);
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/plancat.h,v 1.36 2005/06/05 22:32:58 tgl Exp $
* $PostgreSQL: pgsql/src/include/optimizer/plancat.h,v 1.37 2005/07/23 21:05:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -19,6 +19,8 @@
extern void get_relation_info(Oid relationObjectId, RelOptInfo *rel);
extern List *get_relation_constraints(Oid relationObjectId, RelOptInfo *rel);
extern List *build_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
extern List *find_inheritance_children(Oid inhparent);
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/predtest.h,v 1.1 2005/06/10 22:25:37 tgl Exp $
* $PostgreSQL: pgsql/src/include/optimizer/predtest.h,v 1.2 2005/07/23 21:05:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -19,5 +19,7 @@
extern bool predicate_implied_by(List *predicate_list,
List *restrictinfo_list);
extern bool predicate_refuted_by(List *predicate_list,
List *restrictinfo_list);
#endif /* PREDTEST_H */
SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%';
name | setting
-------------------+---------
enable_bitmapscan | on
enable_hashagg | on
enable_hashjoin | on
enable_indexscan | on
enable_mergejoin | on
enable_nestloop | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
(9 rows)
name | setting
-----------------------------+---------
enable_bitmapscan | on
enable_constraint_exclusion | off
enable_hashagg | on
enable_hashjoin | on
enable_indexscan | on
enable_mergejoin | on
enable_nestloop | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
(10 rows)
CREATE TABLE foo2(fooid int, f2 int);
INSERT INTO foo2 VALUES(1, 11);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment