From c9b63e6a49618561cc960be06513279910582e29 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Wed, 20 Feb 2013 18:01:36 -0800 Subject: Fixed bug mdev-3913. The wrong result set returned by the left join query from the bug test case happened due to several inconsistencies and bugs of the legacy mysql code. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bug test case uses an execution plan that employs a scan of a materialized IN subquery from the WHERE condition. When materializing such an IN- subquery the optimizer injects additional equalities into the WHERE clause. These equalities express the constraints imposed by the subquery predicate. The injected equality of the query in the test case happens to belong to the same equality class, and a new equality imposing a condition on the rows of the materialized subquery is inferred from this class. Simultaneously the multiple equality is added to the ON expression of the LEFT JOIN used in the main query. The inferred equality of the form f1=f2 is taken into account when optimizing the scan of the rows the temporary table that is the result of the subquery materialization: only the values of the field f1 are read from the table into the record buffer. Meanwhile the inferred equality is removed from the WHERE conditions altogether as a constraint on the fields of the temporary table that has been used when filling this table. This equality is supposed to be removed from the ON expression when the multiple equalities of the ON expression are converted into an optimal set of equality predicates. It supposed to be removed from the ON expression as an equality inferred from only equalities of the WHERE condition. Yet, it did not happened due to the following bug in the code. Erroneously the code tried to build multiple equality for ON expression twice: the first time, when it called optimize_cond() for the WHERE condition, the second time, when it called this function for the HAVING condition. When executing optimize_con() for the WHERE condition a reference to the multiple equality of the WHERE condition is set in the multiple equality of the ON expression. This reference would allow later to convert multiple equalities of the ON expression into equality predicates. However the the second call of build_equal_items() for the ON expression that happened when optimize_cond() was called for the HAVING condition reset this reference to NULL. This bug fix blocks calling build_equal_items() for ON expressions for the second time. In general, it will be beneficial for many queries as it removes from ON expressions any equalities that are to be checked for the WHERE condition. The patch also fixes two bugs in the list manipulation operations and a bug in the function substitute_for_best_equal_field() that resulted in passing wrong reference to the multiple equalities of where conditions when processing multiple equalities of ON expressions. The code of substitute_for_best_equal_field() and the code the helper function eliminate_item_equal() were also streamlined and cleaned up. Now the conversion of the multiple equalities into an optimal set of equality predicates first produces the sequence of the all equalities processing multiple equalities one by one, and, only after this, it inserts the equalities at the beginning of the other conditions. The multiple changes in the output of EXPLAIN EXTENDED are mainly the result of this streamlining, but in some cases is the result of the removal of unneeded equalities from ON expressions. In some test cases this removal were reflected in the output of EXPLAIN resulted in disappearance of “Using where” in some rows of the execution plans. --- mysql-test/r/subselect.result | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'mysql-test/r/subselect.result') diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index bcfb2dad86d..8503164dde2 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1460,7 +1460,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 75.00 Using where; Using join buffer (flat, BNL join) 1 PRIMARY t3 eq_ref PRIMARY PRIMARY 4 test.t1.b 1 100.00 Using index Warnings: -Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t3` join `test`.`t2` where ((`test`.`t1`.`a` = `test`.`t2`.`a`) and (`test`.`t3`.`a` = `test`.`t1`.`b`)) +Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t3` join `test`.`t2` where ((`test`.`t3`.`a` = `test`.`t1`.`b`) and (`test`.`t1`.`a` = `test`.`t2`.`a`)) drop table t1, t2, t3; create table t1 (a int, b int, index a (a,b)); create table t2 (a int, index a (a)); @@ -1503,7 +1503,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t3 index a a 5 NULL 3 100.00 Using where; Using index 1 PRIMARY t1 ref a a 10 test.t2.a,test.t3.a 116 100.00 Using index; FirstMatch(t2) Warnings: -Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t1` join `test`.`t3`) where ((`test`.`t1`.`a` = `test`.`t2`.`a`) and (`test`.`t1`.`b` = `test`.`t3`.`a`)) +Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t1` join `test`.`t3`) where ((`test`.`t1`.`b` = `test`.`t3`.`a`) and (`test`.`t1`.`a` = `test`.`t2`.`a`)) insert into t1 values (3,31); select * from t2 where t2.a in (select a from t1 where t1.b <> 30); a @@ -2976,7 +2976,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY eq_ref distinct_key distinct_key 8 func,func 1 100.00 2 MATERIALIZED t2 ALL NULL NULL NULL NULL 9 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two` from `test`.`t1` semi join (`test`.`t2`) where ((`test`.`t2`.`flag` = 'N')) +Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two` from `test`.`t1` semi join (`test`.`t2`) where (`test`.`t2`.`flag` = 'N') explain extended SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = '0' group by one,two) as 'test' from t1; id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 8 100.00 -- cgit v1.2.1 From 599a1384af7d38e4319bd6258c6954750f5b9ba4 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 29 Mar 2013 17:53:21 +0200 Subject: Fix for MDEV-4144 Analysis: The reason for the inefficent plan was that Item_subselect::is_expensive() didn't detect the special case when a subquery was optimized, but had no join plan because it either has no table, or its tables have been optimized away, or the optimizer detected that the result set is empty. Solution: Identify the special cases above in the Item_subselect::is_expensive(), and consider such degenerate subqueries inexpensive. --- mysql-test/r/subselect.result | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'mysql-test/r/subselect.result') diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 1977fbaccee..8273bee6117 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -429,7 +429,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 select 1 AS `1` from `test`.`t1` where (1 = (select 1 union select 1)) +Note 1003 select 1 AS `1` from `test`.`t1` where 1 drop table t1; CREATE TABLE `t1` ( `numeropost` mediumint(8) unsigned NOT NULL auto_increment, @@ -558,10 +558,10 @@ Warnings: Note 1003 select max(`test`.`t1`.`numreponse`) AS `MAX(numreponse)` from `test`.`t1` where (`test`.`t1`.`numeropost` = '1') EXPLAIN EXTENDED SELECT numreponse FROM t1 WHERE numeropost='1' AND numreponse=(SELECT MAX(numreponse) FROM t1 WHERE numeropost='1'); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1 const PRIMARY,numreponse PRIMARY 7 const,const 1 100.00 Using where; Using index +1 PRIMARY t1 const PRIMARY,numreponse PRIMARY 7 const,const 1 100.00 Using index 2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL Select tables optimized away Warnings: -Note 1003 select `test`.`t1`.`numreponse` AS `numreponse` from `test`.`t1` where ((`test`.`t1`.`numeropost` = '1') and (`test`.`t1`.`numreponse` = (select max(`test`.`t1`.`numreponse`) from `test`.`t1` where (`test`.`t1`.`numeropost` = '1')))) +Note 1003 select 3 AS `numreponse` from `test`.`t1` where ((1 = '1')) drop table t1; CREATE TABLE t1 (a int(1)); INSERT INTO t1 VALUES (1); @@ -1371,7 +1371,7 @@ create table t1 (id int not null auto_increment primary key, salary int, key(sal insert into t1 (salary) values (100),(1000),(10000),(10),(500),(5000),(50000); explain extended SELECT id FROM t1 where salary = (SELECT MAX(salary) FROM t1); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1 ref salary salary 5 const 0 0.00 Using where +1 PRIMARY t1 ref salary salary 5 const 1 100.00 Using where 2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL Select tables optimized away Warnings: Note 1003 select `test`.`t1`.`id` AS `id` from `test`.`t1` where (`test`.`t1`.`salary` = (select max(`test`.`t1`.`salary`) from `test`.`t1`)) -- cgit v1.2.1