aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2007-05-26 18:23:02 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2007-05-26 18:23:02 +0000
commitcadb78330eedceafeda99bf12ac690cda773be62 (patch)
tree6d4b9004701763c29fb88f1afecef8c81207df9e
parent604ffd280b955100e5fc24649ee4d42a6f3ebf35 (diff)
downloadpostgresql-cadb78330eedceafeda99bf12ac690cda773be62.tar.gz
postgresql-cadb78330eedceafeda99bf12ac690cda773be62.zip
Repair two constraint-exclusion corner cases triggered by proving that an
inheritance child of an UPDATE/DELETE target relation can be excluded by constraints. I had rearranged some code in set_append_rel_pathlist() to avoid "useless" work when a child is excluded, but overdid it and left the child with no cheapest_path entry, causing possible failure later if the appendrel was involved in a join. Also, it seems that the dummy plan generated by inheritance_planner() when all branches are excluded has to be a bit less dummy now than was required in 8.2. Per report from Jan Wieck. Add his test case to the regression tests.
-rw-r--r--src/backend/optimizer/path/allpaths.c47
-rw-r--r--src/backend/optimizer/plan/planner.c7
-rw-r--r--src/test/regress/expected/rules.out58
-rw-r--r--src/test/regress/sql/rules.sql38
4 files changed, 134 insertions, 16 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 07a99e9c6b7..0ad3dc5aae1 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.163 2007/04/21 21:01:44 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.164 2007/05/26 18:23:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -45,6 +45,7 @@ static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
+static void set_dummy_rel_pathlist(RelOptInfo *rel);
static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel,
@@ -198,23 +199,14 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
/*
* If we can prove we don't need to scan the rel via constraint exclusion,
- * set up a single dummy path for it. (Rather than inventing a special
- * "dummy" path type, we represent this as an AppendPath with no members.)
- * We only need to check for regular baserels; if it's an otherrel, CE
- * was already checked in set_append_rel_pathlist().
+ * set up a single dummy path for it. We only need to check for regular
+ * baserels; if it's an otherrel, CE was already checked in
+ * set_append_rel_pathlist().
*/
if (rel->reloptkind == RELOPT_BASEREL &&
relation_excluded_by_constraints(rel, rte))
{
- /* Set dummy size estimates --- we leave attr_widths[] as zeroes */
- rel->rows = 0;
- rel->width = 0;
-
- add_path(rel, (Path *) create_append_path(rel, NIL));
-
- /* Select cheapest path (pretty easy in this case...) */
- set_cheapest(rel);
-
+ set_dummy_rel_pathlist(rel);
return;
}
@@ -330,7 +322,12 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
if (relation_excluded_by_constraints(childrel, childRTE))
{
- /* this child need not be scanned, so just disregard it */
+ /*
+ * This child need not be scanned, so we can omit it from the
+ * appendrel. Mark it with a dummy cheapest-path though, in
+ * case best_appendrel_indexscan() looks at it later.
+ */
+ set_dummy_rel_pathlist(childrel);
continue;
}
@@ -425,6 +422,26 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
set_cheapest(rel);
}
+/*
+ * set_dummy_rel_pathlist
+ * Build a dummy path for a relation that's been excluded by constraints
+ *
+ * Rather than inventing a special "dummy" path type, we represent this as an
+ * AppendPath with no members.
+ */
+static void
+set_dummy_rel_pathlist(RelOptInfo *rel)
+{
+ /* Set dummy size estimates --- we leave attr_widths[] as zeroes */
+ rel->rows = 0;
+ rel->width = 0;
+
+ add_path(rel, (Path *) create_append_path(rel, NIL));
+
+ /* Select cheapest path (pretty easy in this case...) */
+ set_cheapest(rel);
+}
+
/* quick-and-dirty test to see if any joining is needed */
static bool
has_multiple_baserels(PlannerInfo *root)
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 0f9776cdca2..e2396d42ca6 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.220 2007/05/25 17:54:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.221 2007/05/26 18:23:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -669,11 +669,16 @@ inheritance_planner(PlannerInfo *root)
* If we managed to exclude every child rel, return a dummy plan
*/
if (subplans == NIL)
+ {
+ root->resultRelations = list_make1_int(parentRTindex);
+ /* although dummy, it must have a valid tlist for executor */
+ tlist = preprocess_targetlist(root, parse->targetList);
return (Plan *) make_result(root,
tlist,
(Node *) list_make1(makeBoolConst(false,
false)),
NULL);
+ }
/*
* Planning might have modified the rangetable, due to changes of the
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 112891bbb2c..e1098fd6845 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1493,3 +1493,61 @@ select * from id_ordered;
set client_min_messages to warning; -- suppress cascade notices
drop table id cascade;
+reset client_min_messages;
+--
+-- check corner case where an entirely-dummy subplan is created by
+-- constraint exclusion
+--
+create temp table t1 (a integer primary key);
+NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1"
+create temp table t1_1 (check (a >= 0 and a < 10)) inherits (t1);
+create temp table t1_2 (check (a >= 10 and a < 20)) inherits (t1);
+create rule t1_ins_1 as on insert to t1
+ where new.a >= 0 and new.a < 10
+ do instead
+ insert into t1_1 values (new.a);
+create rule t1_ins_2 as on insert to t1
+ where new.a >= 10 and new.a < 20
+ do instead
+ insert into t1_2 values (new.a);
+create rule t1_upd_1 as on update to t1
+ where old.a >= 0 and old.a < 10
+ do instead
+ update t1_1 set a = new.a where a = old.a;
+create rule t1_upd_2 as on update to t1
+ where old.a >= 10 and old.a < 20
+ do instead
+ update t1_2 set a = new.a where a = old.a;
+set constraint_exclusion = on;
+insert into t1 select * from generate_series(5,19,1) g;
+update t1 set a = 4 where a = 5;
+select * from only t1;
+ a
+---
+(0 rows)
+
+select * from only t1_1;
+ a
+---
+ 6
+ 7
+ 8
+ 9
+ 4
+(5 rows)
+
+select * from only t1_2;
+ a
+----
+ 10
+ 11
+ 12
+ 13
+ 14
+ 15
+ 16
+ 17
+ 18
+ 19
+(10 rows)
+
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index 374e11d671c..e898336d92a 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -881,3 +881,41 @@ select * from id_ordered;
set client_min_messages to warning; -- suppress cascade notices
drop table id cascade;
+reset client_min_messages;
+
+--
+-- check corner case where an entirely-dummy subplan is created by
+-- constraint exclusion
+--
+
+create temp table t1 (a integer primary key);
+
+create temp table t1_1 (check (a >= 0 and a < 10)) inherits (t1);
+create temp table t1_2 (check (a >= 10 and a < 20)) inherits (t1);
+
+create rule t1_ins_1 as on insert to t1
+ where new.a >= 0 and new.a < 10
+ do instead
+ insert into t1_1 values (new.a);
+create rule t1_ins_2 as on insert to t1
+ where new.a >= 10 and new.a < 20
+ do instead
+ insert into t1_2 values (new.a);
+
+create rule t1_upd_1 as on update to t1
+ where old.a >= 0 and old.a < 10
+ do instead
+ update t1_1 set a = new.a where a = old.a;
+create rule t1_upd_2 as on update to t1
+ where old.a >= 10 and old.a < 20
+ do instead
+ update t1_2 set a = new.a where a = old.a;
+
+set constraint_exclusion = on;
+
+insert into t1 select * from generate_series(5,19,1) g;
+update t1 set a = 4 where a = 5;
+
+select * from only t1;
+select * from only t1_1;
+select * from only t1_2;