summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <monty@hundin.mysql.fi>2002-11-09 09:51:03 +0200
committerunknown <monty@hundin.mysql.fi>2002-11-09 09:51:03 +0200
commitf9fde16a8ce40093a325132197182bb16d46be40 (patch)
tree50d75360317401d22dd6d4a2f7a6a240f5ef68b0
parenta1a6f4c11ff122c7ac170f4818dfd931f8abb7bf (diff)
downloadmariadb-git-f9fde16a8ce40093a325132197182bb16d46be40.tar.gz
Fixed bug in MAX() optimization when used with JOIN and ON expressions
sql/item_cmpfunc.cc: Create an AND expression from two expressions sql/item_cmpfunc.h: Create an AND expression from two expressions
-rw-r--r--mysql-test/r/group_by.result37
-rw-r--r--mysql-test/t/group_by.test41
-rw-r--r--sql/item_cmpfunc.cc39
-rw-r--r--sql/item_cmpfunc.h3
-rw-r--r--sql/opt_sum.cc13
5 files changed, 131 insertions, 2 deletions
diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result
index 95a272e7b4a..0e8c6520d5c 100644
--- a/mysql-test/r/group_by.result
+++ b/mysql-test/r/group_by.result
@@ -1,4 +1,4 @@
-drop table if exists t1,t2;
+drop table if exists t1,t2,t3;
CREATE TABLE t1 (
spID int(10) unsigned,
userID int(10) unsigned,
@@ -417,3 +417,38 @@ xID xID1 Level
3 134 ***
4 185 ****
drop table t1;
+CREATE TABLE t1 (
+pid int(11) unsigned NOT NULL default '0',
+c1id int(11) unsigned default NULL,
+c2id int(11) unsigned default NULL,
+value int(11) unsigned NOT NULL default '0',
+UNIQUE KEY pid2 (pid,c1id,c2id),
+UNIQUE KEY pid (pid,value)
+) TYPE=MyISAM;
+INSERT INTO t1 VALUES (1, 1, NULL, 1),(1, 2, NULL, 2),(1, NULL, 3, 3),(1, 4, NULL, 4),(1, 5, NULL, 5);
+CREATE TABLE t2 (
+id int(11) unsigned NOT NULL default '0',
+active enum('Yes','No') NOT NULL default 'Yes',
+PRIMARY KEY (id)
+) TYPE=MyISAM;
+INSERT INTO t2 VALUES (1, 'Yes'),(2, 'No'),(4, 'Yes'),(5, 'No');
+CREATE TABLE t3 (
+id int(11) unsigned NOT NULL default '0',
+active enum('Yes','No') NOT NULL default 'Yes',
+PRIMARY KEY (id)
+);
+INSERT INTO t3 VALUES (3, 'Yes');
+select * from t1 AS m LEFT JOIN t2 AS c1 ON m.c1id =
+c1.id AND c1.active = 'Yes' LEFT JOIN t3 AS c2 ON m.c2id = c2.id AND
+c2.active = 'Yes' WHERE m.pid=1 AND (c1.id IS NOT NULL OR c2.id IS NOT NULL);
+pid c1id c2id value id active id active
+1 1 NULL 1 1 Yes NULL NULL
+1 NULL 3 3 NULL NULL 3 Yes
+1 4 NULL 4 4 Yes NULL NULL
+select max(value) from t1 AS m LEFT JOIN t2 AS c1 ON
+m.c1id = c1.id AND c1.active = 'Yes' LEFT JOIN t3 AS c2 ON m.c2id =
+c2.id AND c2.active = 'Yes' WHERE m.pid=1 AND (c1.id IS NOT NULL OR c2.id IS
+NOT NULL);
+max(value)
+4
+drop table t1,t2,t3;
diff --git a/mysql-test/t/group_by.test b/mysql-test/t/group_by.test
index 072a1830f57..2f2f50c4085 100644
--- a/mysql-test/t/group_by.test
+++ b/mysql-test/t/group_by.test
@@ -2,7 +2,7 @@
# Test of group (Failed for Lars Hoss <lh@pbm.de>)
#
-drop table if exists t1,t2;
+drop table if exists t1,t2,t3;
CREATE TABLE t1 (
spID int(10) unsigned,
userID int(10) unsigned,
@@ -312,3 +312,42 @@ insert into t1 values (1,244,NULL),(2,243,NULL),(134,223,NULL),(185,186,NULL);
select S.ID as xID, S.ID1 as xID1 from t1 as S left join t1 as yS on S.ID1 between yS.ID1 and yS.ID2;
select S.ID as xID, S.ID1 as xID1, repeat('*',count(distinct yS.ID)) as Level from t1 as S left join t1 as yS on S.ID1 between yS.ID1 and yS.ID2 group by xID order by xID1;
drop table t1;
+
+#
+# Problem with MAX and LEFT JOIN
+#
+
+CREATE TABLE t1 (
+ pid int(11) unsigned NOT NULL default '0',
+ c1id int(11) unsigned default NULL,
+ c2id int(11) unsigned default NULL,
+ value int(11) unsigned NOT NULL default '0',
+ UNIQUE KEY pid2 (pid,c1id,c2id),
+ UNIQUE KEY pid (pid,value)
+) TYPE=MyISAM;
+
+INSERT INTO t1 VALUES (1, 1, NULL, 1),(1, 2, NULL, 2),(1, NULL, 3, 3),(1, 4, NULL, 4),(1, 5, NULL, 5);
+
+CREATE TABLE t2 (
+ id int(11) unsigned NOT NULL default '0',
+ active enum('Yes','No') NOT NULL default 'Yes',
+ PRIMARY KEY (id)
+) TYPE=MyISAM;
+
+INSERT INTO t2 VALUES (1, 'Yes'),(2, 'No'),(4, 'Yes'),(5, 'No');
+
+CREATE TABLE t3 (
+ id int(11) unsigned NOT NULL default '0',
+ active enum('Yes','No') NOT NULL default 'Yes',
+ PRIMARY KEY (id)
+);
+INSERT INTO t3 VALUES (3, 'Yes');
+
+select * from t1 AS m LEFT JOIN t2 AS c1 ON m.c1id =
+c1.id AND c1.active = 'Yes' LEFT JOIN t3 AS c2 ON m.c2id = c2.id AND
+c2.active = 'Yes' WHERE m.pid=1 AND (c1.id IS NOT NULL OR c2.id IS NOT NULL);
+select max(value) from t1 AS m LEFT JOIN t2 AS c1 ON
+m.c1id = c1.id AND c1.active = 'Yes' LEFT JOIN t3 AS c2 ON m.c2id =
+c2.id AND c2.active = 'Yes' WHERE m.pid=1 AND (c1.id IS NOT NULL OR c2.id IS
+NOT NULL);
+drop table t1,t2,t3;
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index bf3c0af1ea6..3cd55934950 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1236,6 +1236,45 @@ longlong Item_cond_or::val_int()
return 0;
}
+/*
+ Create an AND expression from two expressions
+
+ SYNOPSIS
+ and_expressions()
+ a expression or NULL
+ b expression.
+ org_item Don't modify a if a == *org_item
+ If a == NULL, org_item is set to point at b,
+ to ensure that future calls will not modify b.
+
+ NOTES
+ This will not modify item pointed to by org_item or b
+ The idea is that one can call this in a loop and create and
+ 'and' over all items without modifying any of the original items.
+
+ RETURN
+ NULL Error
+ Item
+*/
+
+Item *and_expressions(Item *a, Item *b, Item **org_item)
+{
+ if (!a)
+ return (*org_item= (Item*) b);
+ if (a == *org_item)
+ {
+ Item_cond *res;
+ if ((res= new Item_cond_and(a, (Item*) b)))
+ res->used_tables_cache= a->used_tables() | b->used_tables();
+ return res;
+ }
+ if (((Item_cond_and*) a)->add((Item*) b))
+ return 0;
+ ((Item_cond_and*) a)->used_tables_cache|= b->used_tables();
+ return a;
+}
+
+
longlong Item_func_isnull::val_int()
{
/*
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 214abff4b77..83035720df6 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -621,3 +621,6 @@ public:
longlong val_int();
const char *func_name() const { return "xor"; }
};
+
+
+Item *and_expressions(Item *a, Item *b, Item **org_item);
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index 74e7b2ef3be..4b6a196051e 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -37,6 +37,19 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
bool recalc_const_item=0;
table_map removed_tables=0;
Item *item;
+ COND *org_conds= conds;
+
+ /* Add all ON conditions to WHERE condition */
+ for (TABLE_LIST *tl=tables; tl ; tl= tl->next)
+ {
+ if (tl->on_expr)
+ conds= and_expressions(conds, tl->on_expr, &org_conds);
+ }
+
+ /*
+ Iterate through item is select part and replace COUNT(), MIN() and MAX()
+ with constants (if possible)
+ */
while ((item= it++))
{