diff options
author | unknown <monty@hundin.mysql.fi> | 2002-11-09 09:51:03 +0200 |
---|---|---|
committer | unknown <monty@hundin.mysql.fi> | 2002-11-09 09:51:03 +0200 |
commit | f9fde16a8ce40093a325132197182bb16d46be40 (patch) | |
tree | 50d75360317401d22dd6d4a2f7a6a240f5ef68b0 | |
parent | a1a6f4c11ff122c7ac170f4818dfd931f8abb7bf (diff) | |
download | mariadb-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.result | 37 | ||||
-rw-r--r-- | mysql-test/t/group_by.test | 41 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 39 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 3 | ||||
-rw-r--r-- | sql/opt_sum.cc | 13 |
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++)) { |