From 709a0a131021135e9fb7a2095fcfcbc223dfb126 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Wed, 26 May 2010 13:18:18 -0700 Subject: [PATCH 01/24] MWL#106: Backport optimizations for derived tables and views. The main consolidated patch. --- mysql-test/r/derived.result | 45 +- mysql-test/r/derived_view.result | 570 ++++++++++ mysql-test/r/explain.result | 12 +- mysql-test/r/func_str.result | 10 +- mysql-test/r/index_merge_myisam.result | 3 +- mysql-test/r/information_schema.result | 3 +- .../r/innodb_lock_wait_timeout_1.result | 4 +- mysql-test/r/innodb_mysql.result | 12 +- mysql-test/r/lock_multi_bug38499.result | 4 +- mysql-test/r/myisam_mrr.result | 2 +- mysql-test/r/ps.result | 3 - mysql-test/r/ps_ddl.result | 12 +- mysql-test/r/strict.result | 2 + mysql-test/r/subselect.result | 21 +- mysql-test/r/subselect3.result | 7 +- mysql-test/r/subselect3_jcl6.result | 7 +- mysql-test/r/subselect_no_mat.result | 21 +- mysql-test/r/subselect_no_opts.result | 13 +- mysql-test/r/subselect_no_semijoin.result | 13 +- mysql-test/r/table_elim.result | 36 +- mysql-test/r/view.result | 6 +- mysql-test/r/view_grant.result | 6 +- mysql-test/t/derived_view.test | 217 ++++ mysql-test/t/lock_multi_bug38499.test | 12 +- sql/field.cc | 24 + sql/field.h | 3 + sql/handler.cc | 5 +- sql/item.cc | 61 +- sql/item.h | 51 +- sql/item_cmpfunc.cc | 65 +- sql/item_cmpfunc.h | 4 + sql/item_func.cc | 30 +- sql/item_func.h | 8 +- sql/item_subselect.cc | 22 +- sql/item_subselect.h | 1 + sql/mysql_priv.h | 53 +- sql/opt_range.cc | 11 +- sql/opt_subselect.cc | 61 +- sql/opt_sum.cc | 22 +- sql/records.cc | 3 +- sql/sp_head.cc | 3 + sql/sql_acl.cc | 3 +- sql/sql_base.cc | 199 ++-- sql/sql_bitmap.h | 15 + sql/sql_cache.cc | 9 +- sql/sql_class.cc | 4 +- sql/sql_class.h | 25 +- sql/sql_cursor.cc | 4 +- sql/sql_delete.cc | 57 +- sql/sql_derived.cc | 990 ++++++++++++++---- sql/sql_help.cc | 4 +- sql/sql_insert.cc | 88 +- sql/sql_join_cache.cc | 2 + sql/sql_lex.cc | 430 +++++++- sql/sql_lex.h | 53 +- sql/sql_list.h | 9 +- sql/sql_load.cc | 7 +- sql/sql_olap.cc | 2 +- sql/sql_parse.cc | 11 +- sql/sql_prepare.cc | 51 +- sql/sql_select.cc | 543 +++++++--- sql/sql_select.h | 25 +- sql/sql_show.cc | 18 +- sql/sql_table.cc | 9 +- sql/sql_union.cc | 107 +- sql/sql_update.cc | 94 +- sql/sql_view.cc | 74 +- sql/sql_yacc.yy | 6 +- sql/table.cc | 665 ++++++++++-- sql/table.h | 155 ++- 70 files changed, 4208 insertions(+), 919 deletions(-) create mode 100644 mysql-test/r/derived_view.result create mode 100644 mysql-test/t/derived_view.test diff --git a/mysql-test/r/derived.result b/mysql-test/r/derived.result index 80f04ffd455..00e1a376261 100644 --- a/mysql-test/r/derived.result +++ b/mysql-test/r/derived.result @@ -57,9 +57,8 @@ a b a b 3 c 3 c explain select * from t1 as x1, (select * from t1) as x2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY x1 ALL NULL NULL NULL NULL 4 -1 PRIMARY ALL NULL NULL NULL NULL 4 Using join buffer -2 DERIVED t1 ALL NULL NULL NULL NULL 4 +1 SIMPLE x1 ALL NULL NULL NULL NULL 4 +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using join buffer drop table if exists t2,t3; select * from (select 1) as a; 1 @@ -91,7 +90,7 @@ a b 2 b explain select * from (select * from t1 union select * from t1) a; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 3 +1 PRIMARY ALL NULL NULL NULL NULL 8 2 DERIVED t1 ALL NULL NULL NULL NULL 4 3 UNION t1 ALL NULL NULL NULL NULL 4 NULL UNION RESULT ALL NULL NULL NULL NULL NULL @@ -113,9 +112,8 @@ a b 3 c explain select * from (select t1.*, t2.a as t2a from t1,t2 where t1.a=t2.a) t1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 1 -2 DERIVED t2 system NULL NULL NULL NULL 1 -2 DERIVED t1 ALL NULL NULL NULL NULL 4 Using where +1 SIMPLE t2 system NULL NULL NULL NULL 1 +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using where drop table t1, t2; create table t1(a int not null, t char(8), index(a)); SELECT * FROM (SELECT * FROM t1) as b ORDER BY a ASC LIMIT 0,20; @@ -142,8 +140,7 @@ a t 20 20 explain select count(*) from t1 as tt1, (select * from t1) as tt2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away -2 DERIVED t1 ALL NULL NULL NULL NULL 10000 +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away drop table t1; SELECT * FROM (SELECT (SELECT * FROM (SELECT 1 as a) as a )) as b; (SELECT * FROM (SELECT 1 as a) as a ) @@ -172,30 +169,30 @@ insert into t1 values (NULL, 'a', 1), (NULL, 'b', 2), (NULL, 'c', 3), (NULL, 'd' insert into t2 values (1, 100), (1, 101), (1, 102), (2, 100), (2, 103), (2, 104), (3, 101), (3, 102), (3, 105); SELECT STRAIGHT_JOIN d.pla_id, m2.mat_id FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; pla_id mat_id -100 1 -101 1 102 1 -103 2 +101 1 +100 1 104 2 +103 2 105 3 SELECT STRAIGHT_JOIN d.pla_id, m2.test FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; pla_id test -100 1 -101 1 102 1 -103 2 +101 1 +100 1 104 2 +103 2 105 3 explain SELECT STRAIGHT_JOIN d.pla_id, m2.mat_id FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY m2 ALL NULL NULL NULL NULL 9 -1 PRIMARY ALL NULL NULL NULL NULL 6 Using where; Using join buffer +1 PRIMARY ref key0 key0 7 test.m2.matintnum 2 Using where 2 DERIVED mp ALL NULL NULL NULL NULL 9 Using temporary; Using filesort 2 DERIVED m1 eq_ref PRIMARY PRIMARY 3 test.mp.mat_id 1 explain SELECT STRAIGHT_JOIN d.pla_id, m2.test FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY m2 ALL NULL NULL NULL NULL 9 -1 PRIMARY ALL NULL NULL NULL NULL 6 Using where; Using join buffer +1 PRIMARY ref key0 key0 7 test.m2.matintnum 2 Using where 2 DERIVED mp ALL NULL NULL NULL NULL 9 Using temporary; Using filesort 2 DERIVED m1 eq_ref PRIMARY PRIMARY 3 test.mp.mat_id 1 drop table t1,t2; @@ -230,9 +227,8 @@ count(*) 2 explain select count(*) from t1 INNER JOIN (SELECT A.E1, A.E2, A.E3 FROM t1 AS A WHERE A.E3 = (SELECT MAX(B.E3) FROM t1 AS B WHERE A.E2 = B.E2)) AS THEMAX ON t1.E1 = THEMAX.E2 AND t1.E1 = t1.E2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 -1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 THEMAX.E2 1 Using where -2 DERIVED A ALL NULL NULL NULL NULL 2 Using where +1 SIMPLE A ALL NULL NULL NULL NULL 2 Using where +1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.A.E2 1 Using where 3 DEPENDENT SUBQUERY B ALL NULL NULL NULL NULL 2 Using where drop table t1; create table t1 (a int); @@ -245,8 +241,8 @@ a a 2 2 explain select * from ( select * from t1 union select * from t1) a,(select * from t1 union select * from t1) b; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 -1 PRIMARY ALL NULL NULL NULL NULL 2 Using join buffer +1 PRIMARY ALL NULL NULL NULL NULL 4 +1 PRIMARY ALL NULL NULL NULL NULL 4 Using join buffer 4 DERIVED t1 ALL NULL NULL NULL NULL 2 5 UNION t1 ALL NULL NULL NULL NULL 2 NULL UNION RESULT ALL NULL NULL NULL NULL NULL @@ -311,7 +307,7 @@ a 7.0000 b 3.5000 explain SELECT s.name, AVG(s.val) AS median FROM (SELECT x.name, x.val FROM t1 x, t1 y WHERE x.name=y.name GROUP BY x.name, x.val HAVING SUM(y.val <= x.val) >= COUNT(*)/2 AND SUM(y.val >= x.val) >= COUNT(*)/2) AS s GROUP BY s.name; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 3 Using temporary; Using filesort +1 PRIMARY ALL NULL NULL NULL NULL 289 Using temporary; Using filesort 2 DERIVED x ALL NULL NULL NULL NULL 17 Using temporary; Using filesort 2 DERIVED y ALL NULL NULL NULL NULL 17 Using where; Using join buffer drop table t1; @@ -322,8 +318,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 index PRIMARY PRIMARY 4 NULL 2 Using where; Using index explain select a from (select a from t2 where a>1) tt; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 1 -2 DERIVED t2 index PRIMARY PRIMARY 4 NULL 2 Using where; Using index +1 SIMPLE t2 index PRIMARY PRIMARY 4 NULL 2 Using where; Using index drop table t2; CREATE TABLE `t1` ( `itemid` int(11) NOT NULL default '0', `grpid` varchar(15) NOT NULL default '', `vendor` int(11) NOT NULL default '0', `date_` date NOT NULL default '0000-00-00', `price` decimal(12,2) NOT NULL default '0.00', PRIMARY KEY (`itemid`,`grpid`,`vendor`,`date_`), KEY `itemid` (`itemid`,`vendor`), KEY `itemid_2` (`itemid`,`date_`)); insert into t1 values (128, 'rozn', 2, curdate(), 10), diff --git a/mysql-test/r/derived_view.result b/mysql-test/r/derived_view.result new file mode 100644 index 00000000000..e2520ba7741 --- /dev/null +++ b/mysql-test/r/derived_view.result @@ -0,0 +1,570 @@ +drop table if exists t1,t2; +drop view if exists v1,v2,v3,v4; +create table t1(f1 int, f11 int); +create table t2(f2 int, f22 int); +insert into t1 values(1,1),(2,2),(3,3),(5,5),(9,9),(7,7); +insert into t1 values(17,17),(13,13),(11,11),(15,15),(19,19); +insert into t2 values(1,1),(3,3),(2,2),(4,4),(8,8),(6,6); +insert into t2 values(12,12),(14,14),(10,10),(18,18),(16,16); +Tests: +for merged derived tables +explain for simple derived +explain select * from (select * from t1) tt; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 11 +select * from (select * from t1) tt; +f1 f11 +1 1 +2 2 +3 3 +5 5 +9 9 +7 7 +17 17 +13 13 +11 11 +15 15 +19 19 +explain for multitable derived +explain extended select * from (select * from t1 join t2 on f1=f2) tt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 11 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 11 100.00 Using where; Using join buffer +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11`,`test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f22` AS `f22` from `test`.`t1` join `test`.`t2` where (`test`.`t2`.`f2` = `test`.`t1`.`f1`) +select * from (select * from t1 join t2 on f1=f2) tt; +f1 f11 f2 f22 +1 1 1 1 +3 3 3 3 +2 2 2 2 +explain for derived with where +explain extended +select * from (select * from t1 where f1 in (2,3)) tt where f11=2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 11 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` where ((`test`.`t1`.`f11` = 2) and (`test`.`t1`.`f1` in (2,3))) +select * from (select * from t1 where f1 in (2,3)) tt where f11=2; +f1 f11 +2 2 +join of derived +explain extended +select * from (select * from t1 where f1 in (2,3)) tt join +(select * from t1 where f1 in (1,2)) aa on tt.f1=aa.f1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 11 100.00 Using where +1 SIMPLE t1 ALL NULL NULL NULL NULL 11 100.00 Using where; Using join buffer +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11`,`test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` join `test`.`t1` where ((`test`.`t1`.`f1` = `test`.`t1`.`f1`) and (`test`.`t1`.`f1` in (1,2)) and (`test`.`t1`.`f1` in (2,3))) +select * from (select * from t1 where f1 in (2,3)) tt join +(select * from t1 where f1 in (1,2)) aa on tt.f1=aa.f1; +f1 f11 f1 f11 +2 2 2 2 +flush status; +explain extended +select * from (select * from t1 where f1 in (2,3)) tt where f11=2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 11 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` where ((`test`.`t1`.`f11` = 2) and (`test`.`t1`.`f1` in (2,3))) +show status like 'Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 0 +Handler_read_next 0 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_next 0 +flush status; +select * from (select * from t1 where f1 in (2,3)) tt where f11=2; +f1 f11 +2 2 +show status like 'Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 0 +Handler_read_next 0 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_next 12 +for merged views +create view v1 as select * from t1; +create view v2 as select * from t1 join t2 on f1=f2; +create view v3 as select * from t1 where f1 in (2,3); +create view v4 as select * from t2 where f2 in (2,3); +explain for simple views +explain extended select * from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 11 100.00 +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` +select * from v1; +f1 f11 +1 1 +2 2 +3 3 +5 5 +9 9 +7 7 +17 17 +13 13 +11 11 +15 15 +19 19 +explain for multitable views +explain extended select * from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 11 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 11 100.00 Using where; Using join buffer +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11`,`test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f22` AS `f22` from `test`.`t1` join `test`.`t2` where (`test`.`t2`.`f2` = `test`.`t1`.`f1`) +select * from v2; +f1 f11 f2 f22 +1 1 1 1 +3 3 3 3 +2 2 2 2 +explain for views with where +explain extended select * from v3 where f11 in (1,3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 11 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` where ((`test`.`t1`.`f11` in (1,3)) and (`test`.`t1`.`f1` in (2,3))) +select * from v3 where f11 in (1,3); +f1 f11 +3 3 +explain for joined views +explain extended +select * from v3 join v4 on f1=f2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 11 100.00 Using where +1 SIMPLE t2 ALL NULL NULL NULL NULL 11 100.00 Using where; Using join buffer +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11`,`test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f22` AS `f22` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`f2` = `test`.`t1`.`f1`) and (`test`.`t1`.`f1` in (2,3)) and (`test`.`t1`.`f1` in (2,3))) +select * from v3 join v4 on f1=f2; +f1 f11 f2 f22 +3 3 3 3 +2 2 2 2 +flush status; +explain extended select * from v4 where f2 in (1,3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 11 100.00 Using where +Warnings: +Note 1003 select `test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f22` AS `f22` from `test`.`t2` where ((`test`.`t2`.`f2` in (1,3)) and (`test`.`t2`.`f2` in (2,3))) +show status like 'Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 0 +Handler_read_next 0 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_next 0 +flush status; +select * from v4 where f2 in (1,3); +f2 f22 +3 3 +show status like 'Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 0 +Handler_read_next 0 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_next 12 +for materialized derived tables +explain for simple derived +explain extended select * from (select * from t1 group by f1) tt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 11 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using temporary; Using filesort +Warnings: +Note 1003 select `tt`.`f1` AS `f1`,`tt`.`f11` AS `f11` from (select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` group by `test`.`t1`.`f1`) `tt` +select * from (select * from t1 having f1=f1) tt; +f1 f11 +1 1 +2 2 +3 3 +5 5 +9 9 +7 7 +17 17 +13 13 +11 11 +15 15 +19 19 +explain showing created indexes +explain extended +select * from t1 join (select * from t2 group by f2) tt on f1=f2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 11 100.00 +1 PRIMARY ref key0 key0 5 test.t1.f1 2 100.00 Using where +2 DERIVED t2 ALL NULL NULL NULL NULL 11 100.00 Using temporary; Using filesort +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11`,`tt`.`f2` AS `f2`,`tt`.`f22` AS `f22` from `test`.`t1` join (select `test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f22` AS `f22` from `test`.`t2` group by `test`.`t2`.`f2`) `tt` where (`tt`.`f2` = `test`.`t1`.`f1`) +select * from t1 join (select * from t2 group by f2) tt on f1=f2; +f1 f11 f2 f22 +1 1 1 1 +2 2 2 2 +3 3 3 3 +explain showing late materialization +flush status; +explain select * from t1 join (select * from t2 group by f2) tt on f1=f2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 11 +1 PRIMARY ref key0 key0 5 test.t1.f1 2 Using where +2 DERIVED t2 ALL NULL NULL NULL NULL 11 Using temporary; Using filesort +show status like 'Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 0 +Handler_read_next 0 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_next 0 +flush status; +select * from t1 join (select * from t2 group by f2) tt on f1=f2; +f1 f11 f2 f22 +1 1 1 1 +2 2 2 2 +3 3 3 3 +show status like 'Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 11 +Handler_read_next 3 +Handler_read_prev 0 +Handler_read_rnd 11 +Handler_read_rnd_next 36 +for materialized views +drop view v1,v2,v3; +create view v1 as select * from t1 group by f1; +create view v2 as select * from t2 group by f2; +create view v3 as select t1.f1,t1.f11 from t1 join t1 as t11 where t1.f1=t11.f1 +having t1.f1<100; +explain for simple derived +explain extended select * from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 11 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using temporary; Using filesort +Warnings: +Note 1003 select `v1`.`f1` AS `f1`,`v1`.`f11` AS `f11` from `test`.`v1` +select * from v1; +f1 f11 +1 1 +2 2 +3 3 +5 5 +7 7 +9 9 +11 11 +13 13 +15 15 +17 17 +19 19 +explain showing created indexes +explain extended select * from t1 join v2 on f1=f2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 11 100.00 +1 PRIMARY ref key0 key0 5 test.t1.f1 2 100.00 Using where +2 DERIVED t2 ALL NULL NULL NULL NULL 11 100.00 Using temporary; Using filesort +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11`,`v2`.`f2` AS `f2`,`v2`.`f22` AS `f22` from `test`.`t1` join `test`.`v2` where (`v2`.`f2` = `test`.`t1`.`f1`) +select * from t1 join v2 on f1=f2; +f1 f11 f2 f22 +1 1 1 1 +2 2 2 2 +3 3 3 3 +explain extended +select * from t1,v3 as v31,v3 where t1.f1=v31.f1 and t1.f1=v3.f1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 11 100.00 +1 PRIMARY ref key0 key0 5 test.t1.f1 10 100.00 Using where +1 PRIMARY ref key0 key0 5 test.t1.f1 10 100.00 Using where +3 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 +3 DERIVED t11 ALL NULL NULL NULL NULL 11 100.00 Using where; Using join buffer +2 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 +2 DERIVED t11 ALL NULL NULL NULL NULL 11 100.00 Using where; Using join buffer +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11`,`v31`.`f1` AS `f1`,`v31`.`f11` AS `f11`,`v3`.`f1` AS `f1`,`v3`.`f11` AS `f11` from `test`.`t1` join `test`.`v3` `v31` join `test`.`v3` where ((`v31`.`f1` = `test`.`t1`.`f1`) and (`v3`.`f1` = `test`.`t1`.`f1`)) +flush status; +select * from t1,v3 as v31,v3 where t1.f1=v31.f1 and t1.f1=v3.f1; +f1 f11 f1 f11 f1 f11 +1 1 1 1 1 1 +2 2 2 2 2 2 +3 3 3 3 3 3 +5 5 5 5 5 5 +9 9 9 9 9 9 +7 7 7 7 7 7 +17 17 17 17 17 17 +13 13 13 13 13 13 +11 11 11 11 11 11 +15 15 15 15 15 15 +19 19 19 19 19 19 +show status like 'Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 22 +Handler_read_next 22 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_next 60 +explain showing late materialization +flush status; +explain select * from t1 join v2 on f1=f2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 11 +1 PRIMARY ref key0 key0 5 test.t1.f1 2 Using where +2 DERIVED t2 ALL NULL NULL NULL NULL 11 Using temporary; Using filesort +show status like 'Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 0 +Handler_read_next 0 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_next 0 +flush status; +select * from t1 join v2 on f1=f2; +f1 f11 f2 f22 +1 1 1 1 +2 2 2 2 +3 3 3 3 +show status like 'Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 11 +Handler_read_next 3 +Handler_read_prev 0 +Handler_read_rnd 11 +Handler_read_rnd_next 36 +explain extended select * from v1 join v4 on f1=f2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 11 100.00 Using where +1 PRIMARY ref key0 key0 5 test.t2.f2 2 100.00 Using where +2 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using temporary; Using filesort +Warnings: +Note 1003 select `v1`.`f1` AS `f1`,`v1`.`f11` AS `f11`,`test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f22` AS `f22` from `test`.`v1` join `test`.`t2` where ((`v1`.`f1` = `test`.`t2`.`f2`) and (`test`.`t2`.`f2` in (2,3))) +select * from v1 join v4 on f1=f2; +f1 f11 f2 f22 +3 3 3 3 +2 2 2 2 +merged derived in merged derived +explain extended select * from (select * from +(select * from t1 where f1 < 7) tt where f1 > 2) zz; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 11 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` where ((`test`.`t1`.`f1` > 2) and (`test`.`t1`.`f1` < 7)) +select * from (select * from +(select * from t1 where f1 < 7) tt where f1 > 2) zz; +f1 f11 +3 3 +5 5 +materialized derived in merged derived +explain extended select * from (select * from +(select * from t1 where f1 < 7 group by f1) tt where f1 > 2) zz; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE ALL NULL NULL NULL NULL 11 100.00 Using where +3 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using where; Using temporary; Using filesort +Warnings: +Note 1003 select `tt`.`f1` AS `f1`,`tt`.`f11` AS `f11` from (select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` where (`test`.`t1`.`f1` < 7) group by `test`.`t1`.`f1`) `tt` where (`tt`.`f1` > 2) +select * from (select * from +(select * from t1 where f1 < 7 group by f1) tt where f1 > 2) zz; +f1 f11 +3 3 +5 5 +merged derived in materialized derived +explain extended select * from (select * from +(select * from t1 where f1 < 7) tt where f1 > 2 group by f1) zz; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 11 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using where; Using temporary; Using filesort +Warnings: +Note 1003 select `zz`.`f1` AS `f1`,`zz`.`f11` AS `f11` from (select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` where ((`test`.`t1`.`f1` > 2) and (`test`.`t1`.`f1` < 7)) group by `test`.`t1`.`f1`) `zz` +select * from (select * from +(select * from t1 where f1 < 7) tt where f1 > 2 group by f1) zz; +f1 f11 +3 3 +5 5 +materialized derived in materialized derived +explain extended select * from (select * from +(select * from t1 where f1 < 7 group by f1) tt where f1 > 2 group by f1) zz; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 11 100.00 +2 DERIVED ALL NULL NULL NULL NULL 11 100.00 Using where; Using temporary; Using filesort +3 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using where; Using temporary; Using filesort +Warnings: +Note 1003 select `zz`.`f1` AS `f1`,`zz`.`f11` AS `f11` from (select `tt`.`f1` AS `f1`,`tt`.`f11` AS `f11` from (select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` where (`test`.`t1`.`f1` < 7) group by `test`.`t1`.`f1`) `tt` where (`tt`.`f1` > 2) group by `tt`.`f1`) `zz` +select * from (select * from +(select * from t1 where f1 < 7 group by f1) tt where f1 > 2 group by f1) zz; +f1 f11 +3 3 +5 5 +mat in merged derived join mat in merged derived +explain extended select * from +(select * from (select * from t1 where f1 < 7 group by f1) tt where f1 > 2) x +join +(select * from (select * from t1 where f1 < 7 group by f1) tt where f1 > 2) z +on x.f1 = z.f1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE ALL key0 NULL NULL NULL 11 100.00 Using where +1 SIMPLE ref key0 key0 5 tt.f1 2 100.00 Using where +5 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using where; Using temporary; Using filesort +3 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using where; Using temporary; Using filesort +Warnings: +Note 1003 select `tt`.`f1` AS `f1`,`tt`.`f11` AS `f11`,`tt`.`f1` AS `f1`,`tt`.`f11` AS `f11` from (select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` where (`test`.`t1`.`f1` < 7) group by `test`.`t1`.`f1`) `tt` join (select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` where (`test`.`t1`.`f1` < 7) group by `test`.`t1`.`f1`) `tt` where ((`tt`.`f1` = `tt`.`f1`) and (`tt`.`f1` > 2) and (`tt`.`f1` > 2)) +flush status; +select * from +(select * from (select * from t1 where f1 < 7 group by f1) tt where f1 > 2) x +join +(select * from (select * from t1 where f1 < 7 group by f1) tt where f1 > 2) z +on x.f1 = z.f1; +f1 f11 f1 f11 +3 3 3 3 +5 5 5 5 +show status like 'Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 2 +Handler_read_next 2 +Handler_read_prev 0 +Handler_read_rnd 8 +Handler_read_rnd_next 39 +flush status; +merged in merged derived join merged in merged derived +explain extended select * from +(select * from +(select * from t1 where f1 < 7 ) tt where f1 > 2 ) x +join +(select * from +(select * from t1 where f1 < 7 ) tt where f1 > 2 ) z +on x.f1 = z.f1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 11 100.00 Using where +1 SIMPLE t1 ALL NULL NULL NULL NULL 11 100.00 Using where; Using join buffer +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11`,`test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` join `test`.`t1` where ((`test`.`t1`.`f1` = `test`.`t1`.`f1`) and (`test`.`t1`.`f1` > 2) and (`test`.`t1`.`f1` < 7) and (`test`.`t1`.`f1` > 2) and (`test`.`t1`.`f1` < 7)) +select * from +(select * from +(select * from t1 where f1 < 7 ) tt where f1 > 2 ) x +join +(select * from +(select * from t1 where f1 < 7 ) tt where f1 > 2 ) z +on x.f1 = z.f1; +f1 f11 f1 f11 +3 3 3 3 +5 5 5 5 +materialized in materialized derived join +materialized in materialized derived +explain extended select * from +(select * from +(select * from t1 where f1 < 7 group by f1) tt where f1 > 2 group by f1) x +join +(select * from +(select * from t1 where f1 < 7 group by f1) tt where f1 > 2 group by f1) z +on x.f1 = z.f1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL key0 NULL NULL NULL 11 100.00 +1 PRIMARY ref key0 key0 5 x.f1 2 100.00 Using where +4 DERIVED ALL NULL NULL NULL NULL 11 100.00 Using where; Using temporary; Using filesort +5 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using where; Using temporary; Using filesort +2 DERIVED ALL NULL NULL NULL NULL 11 100.00 Using where; Using temporary; Using filesort +3 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using where; Using temporary; Using filesort +Warnings: +Note 1003 select `x`.`f1` AS `f1`,`x`.`f11` AS `f11`,`z`.`f1` AS `f1`,`z`.`f11` AS `f11` from (select `tt`.`f1` AS `f1`,`tt`.`f11` AS `f11` from (select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` where (`test`.`t1`.`f1` < 7) group by `test`.`t1`.`f1`) `tt` where (`tt`.`f1` > 2) group by `tt`.`f1`) `x` join (select `tt`.`f1` AS `f1`,`tt`.`f11` AS `f11` from (select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` where (`test`.`t1`.`f1` < 7) group by `test`.`t1`.`f1`) `tt` where (`tt`.`f1` > 2) group by `tt`.`f1`) `z` where (`z`.`f1` = `x`.`f1`) +select * from +(select * from +(select * from t1 where f1 < 7 group by f1) tt where f1 > 2 group by f1) x +join +(select * from +(select * from t1 where f1 < 7 group by f1) tt where f1 > 2 group by f1) z +on x.f1 = z.f1; +f1 f11 f1 f11 +3 3 3 3 +5 5 5 5 +merged view in materialized derived +explain extended +select * from (select * from v4 group by 1) tt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 11 100.00 +2 DERIVED t2 ALL NULL NULL NULL NULL 11 100.00 Using where; Using temporary; Using filesort +Warnings: +Note 1003 select `tt`.`f2` AS `f2`,`tt`.`f22` AS `f22` from (select `test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f22` AS `f22` from `test`.`t2` where (`test`.`t2`.`f2` in (2,3)) group by 1) `tt` +select * from (select * from v4 group by 1) tt; +f2 f22 +2 2 +3 3 +materialized view in merged derived +explain extended +select * from ( select * from v1 where f1 < 7) tt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE ALL NULL NULL NULL NULL 11 100.00 Using where +3 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using temporary; Using filesort +Warnings: +Note 1003 select `v1`.`f1` AS `f1`,`v1`.`f11` AS `f11` from `test`.`v1` where (`v1`.`f1` < 7) +select * from ( select * from v1 where f1 < 7) tt; +f1 f11 +1 1 +2 2 +3 3 +5 5 +merged view in a merged view in a merged derived +create view v6 as select * from v4 where f2 < 7; +explain extended select * from (select * from v6) tt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 11 100.00 Using where +Warnings: +Note 1003 select `test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f22` AS `f22` from `test`.`t2` where ((`test`.`t2`.`f2` < 7) and (`test`.`t2`.`f2` in (2,3))) +select * from (select * from v6) tt; +f2 f22 +3 3 +2 2 +materialized view in a merged view in a materialized derived +create view v7 as select * from v1; +explain extended select * from (select * from v7 group by 1) tt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 11 100.00 +2 DERIVED ALL NULL NULL NULL NULL 11 100.00 Using temporary; Using filesort +4 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using temporary; Using filesort +Warnings: +Note 1003 select `tt`.`f1` AS `f1`,`tt`.`f11` AS `f11` from (select `v1`.`f1` AS `f1`,`v1`.`f11` AS `f11` from `test`.`v1` group by 1) `tt` +select * from (select * from v7 group by 1) tt; +f1 f11 +1 1 +2 2 +3 3 +5 5 +7 7 +9 9 +11 11 +13 13 +15 15 +17 17 +19 19 +join of above two +explain extended select * from v6 join v7 on f2=f1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 11 100.00 Using where +1 SIMPLE ref key0 key0 5 test.t2.f2 2 100.00 Using where +5 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using temporary; Using filesort +Warnings: +Note 1003 select `test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f22` AS `f22`,`v1`.`f1` AS `f1`,`v1`.`f11` AS `f11` from `test`.`t2` join `test`.`v1` where ((`v1`.`f1` = `test`.`t2`.`f2`) and (`test`.`t2`.`f2` < 7) and (`test`.`t2`.`f2` in (2,3))) +select * from v6 join v7 on f2=f1; +f2 f22 f1 f11 +3 3 3 3 +2 2 2 2 +test two keys +explain select * from t1 join (select * from t2 group by f2) tt on t1.f1=tt.f2 join t1 xx on tt.f22=xx.f1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 11 +1 PRIMARY ref key0 key0 5 test.t1.f1 2 Using where +1 PRIMARY xx ALL NULL NULL NULL NULL 11 Using where; Using join buffer +2 DERIVED t2 ALL NULL NULL NULL NULL 11 Using temporary; Using filesort +select * from t1 join (select * from t2 group by f2) tt on t1.f1=tt.f2 join t1 xx on tt.f22=xx.f1; +f1 f11 f2 f22 f1 f11 +1 1 1 1 1 1 +2 2 2 2 2 2 +3 3 3 3 3 3 +TODO: Add test with 64 tables mergeable view to test fall back to +materialization on tables > MAX_TABLES merge +drop table t1,t2; +drop view v1,v2,v3,v4,v6,v7; diff --git a/mysql-test/r/explain.result b/mysql-test/r/explain.result index 860c1b90652..331e01d5e23 100644 --- a/mysql-test/r/explain.result +++ b/mysql-test/r/explain.result @@ -102,7 +102,7 @@ INSERT INTO t2 VALUES (),(),(); EXPLAIN SELECT 1 FROM (SELECT 1 FROM t2,t1 WHERE b < c GROUP BY 1 LIMIT 1) AS d2; id select_type table type possible_keys key key_len ref rows Extra -X X X X X X X X X const row not found +X X X X X X X X X X X X X X X X X X X X X X X X X X X Range checked for each record (index map: 0xFFFFFFFFFF) DROP TABLE t2; @@ -114,7 +114,7 @@ INSERT INTO t2 VALUES (1),(2); EXPLAIN EXTENDED SELECT 1 FROM (SELECT COUNT(DISTINCT t1.a) FROM t1,t2 GROUP BY t1.a) AS s1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 4 100.00 2 DERIVED t1 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort 2 DERIVED t2 ALL NULL NULL NULL NULL 2 100.00 Using join buffer Warnings: @@ -122,7 +122,7 @@ Note 1003 select 1 AS `1` from (select count(distinct `test`.`t1`.`a`) AS `COUNT EXPLAIN EXTENDED SELECT 1 FROM (SELECT COUNT(DISTINCT t1.a) FROM t1,t2 GROUP BY t1.a) AS s1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 4 100.00 2 DERIVED t1 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort 2 DERIVED t2 ALL NULL NULL NULL NULL 2 100.00 Using join buffer Warnings: @@ -132,7 +132,7 @@ prepare s1 from FROM (SELECT COUNT(DISTINCT t1.a) FROM t1,t2 GROUP BY t1.a) AS s1'; execute s1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 4 100.00 2 DERIVED t1 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort 2 DERIVED t2 ALL NULL NULL NULL NULL 2 100.00 Using join buffer Warnings: @@ -142,14 +142,14 @@ prepare s1 from FROM (SELECT COUNT(DISTINCT t1.a) FROM t1,t2 GROUP BY t1.a) AS s1'; execute s1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 4 100.00 2 DERIVED t1 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort 2 DERIVED t2 ALL NULL NULL NULL NULL 2 100.00 Using join buffer Warnings: Note 1003 select 1 AS `1` from (select count(distinct `test`.`t1`.`a`) AS `COUNT(DISTINCT t1.a)` from `test`.`t1` join `test`.`t2` group by `test`.`t1`.`a`) `s1` execute s1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 4 100.00 2 DERIVED t1 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort 2 DERIVED t2 ALL NULL NULL NULL NULL 2 100.00 Using join buffer Warnings: diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index d144e84dfdc..67b92f36981 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -2549,14 +2549,12 @@ create table t1(f1 tinyint default null)engine=myisam; insert into t1 values (-1),(null); explain select 1 as a from t1,(select decode(f1,f1) as b from t1) a; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 2 -1 PRIMARY ALL NULL NULL NULL NULL 2 Using join buffer -2 DERIVED t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using join buffer explain select 1 as a from t1,(select encode(f1,f1) as b from t1) a; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 2 -1 PRIMARY ALL NULL NULL NULL NULL 2 Using join buffer -2 DERIVED t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using join buffer drop table t1; # # Bug#49141: Encode function is significantly slower in 5.1 compared to 5.0 diff --git a/mysql-test/r/index_merge_myisam.result b/mysql-test/r/index_merge_myisam.result index 04738abfd3e..23df18543ba 100644 --- a/mysql-test/r/index_merge_myisam.result +++ b/mysql-test/r/index_merge_myisam.result @@ -285,8 +285,7 @@ id select_type table type possible_keys key key_len ref rows Extra NULL UNION RESULT ALL NULL NULL NULL NULL NULL explain select * from (select * from t1 where key1 = 3 or key2 =3) as Z where key8 >5; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 1 -2 DERIVED t1 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where; Using index +1 SIMPLE t1 ALL i1,i2,i8 NULL NULL NULL 1024 Using where create table t3 like t0; insert into t3 select * from t0; alter table t3 add key9 int not null, add index i9(key9); diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index e49885f9118..b4f3e668464 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -1285,8 +1285,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE tables ALL NULL NULL NULL NULL NULL Open_frm_only; Scanned all databases; Using filesort explain select * from (select table_name from information_schema.tables) as a; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 0 const row not found -2 DERIVED tables ALL NULL NULL NULL NULL NULL Skip_open_table; Scanned all databases +1 SIMPLE tables ALL NULL NULL NULL NULL NULL Skip_open_table; Scanned all databases drop view v1; create table t1 (f1 int(11)); create table t2 (f1 int(11), f2 int(11)); diff --git a/mysql-test/r/innodb_lock_wait_timeout_1.result b/mysql-test/r/innodb_lock_wait_timeout_1.result index bd8760b8f79..051266c526e 100644 --- a/mysql-test/r/innodb_lock_wait_timeout_1.result +++ b/mysql-test/r/innodb_lock_wait_timeout_1.result @@ -104,7 +104,7 @@ id 1 select_type PRIMARY table type ALL -possible_keys NULL +possible_keys key0 key NULL key_len NULL ref NULL @@ -308,7 +308,7 @@ id 1 select_type PRIMARY table type ALL -possible_keys NULL +possible_keys key0 key NULL key_len NULL ref NULL diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index 1352bf7f314..68f4c118a0f 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -1731,8 +1731,8 @@ EXPLAIN SELECT 1 FROM (SELECT COUNT(DISTINCT c1) FROM t1 WHERE c2 IN (1, 1) AND c3 = 2 GROUP BY c2) x; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 1 -2 DERIVED t1 index c3,c2 c2 10 NULL 5 +1 PRIMARY ALL NULL NULL NULL NULL 2 +2 DERIVED t1 index_merge c3,c2 c3,c2 5,10 NULL 1 Using intersect(c3,c2); Using where; Using filesort DROP TABLE t1; CREATE TABLE t1 (c1 REAL, c2 REAL, c3 REAL, KEY (c3), KEY (c2, c3)) ENGINE=InnoDB; @@ -1745,8 +1745,8 @@ EXPLAIN SELECT 1 FROM (SELECT COUNT(DISTINCT c1) FROM t1 WHERE c2 IN (1, 1) AND c3 = 2 GROUP BY c2) x; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 1 -2 DERIVED t1 index c3,c2 c2 18 NULL 5 +1 PRIMARY ALL NULL NULL NULL NULL 2 +2 DERIVED t1 index_merge c3,c2 c3,c2 9,18 NULL 1 Using intersect(c3,c2); Using where; Using filesort DROP TABLE t1; CREATE TABLE t1 (c1 DECIMAL(12,2), c2 DECIMAL(12,2), c3 DECIMAL(12,2), KEY (c3), KEY (c2, c3)) @@ -1760,8 +1760,8 @@ EXPLAIN SELECT 1 FROM (SELECT COUNT(DISTINCT c1) FROM t1 WHERE c2 IN (1, 1) AND c3 = 2 GROUP BY c2) x; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 1 -2 DERIVED t1 index c3,c2 c2 14 NULL 5 +1 PRIMARY ALL NULL NULL NULL NULL 2 +2 DERIVED t1 index_merge c3,c2 c3,c2 7,14 NULL 1 Using intersect(c3,c2); Using where; Using filesort DROP TABLE t1; End of 5.1 tests drop table if exists t1, t2, t3; diff --git a/mysql-test/r/lock_multi_bug38499.result b/mysql-test/r/lock_multi_bug38499.result index 9b3f57c8e53..6922312b298 100644 --- a/mysql-test/r/lock_multi_bug38499.result +++ b/mysql-test/r/lock_multi_bug38499.result @@ -2,7 +2,9 @@ SET @odl_sync_frm = @@global.sync_frm; SET @@global.sync_frm = OFF; DROP TABLE IF EXISTS t1; CREATE TABLE t1( a INT, b INT ); +CREATE TABLE t2( a INT, b INT ); INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3), (4, 4); +INSERT INTO t2 VALUES (1, 1), (2, 2), (3, 3), (4, 4); # 1. test regular tables # 1.1. test altering of columns that multiupdate doesn't use # 1.1.1. normal mode @@ -18,5 +20,5 @@ ALTER TABLE t1 ADD COLUMN a INT; # 2.2. test altering of columns that multiupdate uses # 2.2.1. normal mode # 2.2.2. PS mode -DROP TABLE t1; +DROP TABLE t1,t2; SET @@global.sync_frm = @odl_sync_frm; diff --git a/mysql-test/r/myisam_mrr.result b/mysql-test/r/myisam_mrr.result index 27ae694570f..8d3efdde2c4 100644 --- a/mysql-test/r/myisam_mrr.result +++ b/mysql-test/r/myisam_mrr.result @@ -347,7 +347,7 @@ GROUP BY t2.pk ); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE -2 SUBQUERY t2 ALL int_key int_key 5 3 33.33 Using index condition; Using filesort +2 SUBQUERY t2 ALL int_key int_key 5 const 3 33.33 Using index condition; Using filesort Warnings: Note 1003 select min(`test`.`t1`.`pk`) AS `MIN(t1.pk)` from `test`.`t1` where 0 DROP TABLE t1, t2; diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 2971bf047be..ff5e9f5e2be 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -156,7 +156,6 @@ prepare stmt1 from @stmt ; execute stmt1 ; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables -6 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table 5 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found 4 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables @@ -164,7 +163,6 @@ id select_type table type possible_keys key key_len ref rows Extra execute stmt1 ; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables -6 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table 5 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found 4 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables @@ -172,7 +170,6 @@ id select_type table type possible_keys key key_len ref rows Extra explain SELECT (SELECT SUM(c1 + c12 + 0.0) FROM t2 where (t1.c2 - 0e-3) = t2.c2 GROUP BY t1.c15 LIMIT 1) as scalar_s, exists (select 1.0e+0 from t2 where t2.c3 * 9.0000000000 = t1.c4) as exists_s, c5 * 4 in (select c6 + 0.3e+1 from t2) as in_s, (c7 - 4, c8 - 4) in (select c9 + 4.0, c10 + 40e-1 from t2) as in_row_s FROM t1, (select c25 x, c32 y from t2) tt WHERE x * 1 = c25; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables -6 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table 5 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found 4 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables diff --git a/mysql-test/r/ps_ddl.result b/mysql-test/r/ps_ddl.result index 375f31ef9c4..8c5f0de9a5e 100644 --- a/mysql-test/r/ps_ddl.result +++ b/mysql-test/r/ps_ddl.result @@ -1507,12 +1507,12 @@ create view v_27690_1 as select A.a, A.b from t_27690_1 A, t_27690_1 B; execute stmt; a b a b 1 1 1 1 -2 2 1 1 -1 1 1 1 -2 2 1 1 1 1 2 2 +2 2 1 1 2 2 2 2 +1 1 1 1 1 1 2 2 +2 2 1 1 2 2 2 2 call p_verify_reprepare_count(1); SUCCESS @@ -1520,12 +1520,12 @@ SUCCESS execute stmt; a b a b 1 1 1 1 -2 2 1 1 -1 1 1 1 -2 2 1 1 1 1 2 2 +2 2 1 1 2 2 2 2 +1 1 1 1 1 1 2 2 +2 2 1 1 2 2 2 2 call p_verify_reprepare_count(0); SUCCESS diff --git a/mysql-test/r/strict.result b/mysql-test/r/strict.result index 241f4198bf7..335ff51606f 100644 --- a/mysql-test/r/strict.result +++ b/mysql-test/r/strict.result @@ -1107,6 +1107,8 @@ Warnings: Error 1411 Incorrect datetime value: '2004.12.12 10:22:61' for function str_to_date Error 1411 Incorrect datetime value: '2004.12.12 10:22:61' for function str_to_date Error 1411 Incorrect datetime value: '2004.12.12 10:22:61' for function str_to_date +Error 1411 Incorrect datetime value: '2004.12.12 10:22:61' for function str_to_date +Error 1411 Incorrect datetime value: '2004.12.12 10:22:61' for function str_to_date drop table t1; create table t1 (col1 char(3), col2 integer); insert into t1 (col1) values (cast(1000 as char(3))); diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 8802f8d6aa2..287036f6868 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -46,13 +46,13 @@ SELECT (SELECT a) as a; ERROR 42S22: Reference 'a' not supported (forward reference in item list) EXPLAIN EXTENDED SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY system NULL NULL NULL NULL 1 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 -Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having ((select '1' AS `a`) = 1) +Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having ((select `b`.`a` AS `a`) = 1) SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; 1 1 @@ -201,11 +201,10 @@ select (select t3.a from t3 where a<8 order by 1 desc limit 1), a from explain extended select (select t3.a from t3 where a<8 order by 1 desc limit 1), a from (select * from t2 where a>1) as tt; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY system NULL NULL NULL NULL 1 100.00 -3 DERIVED t2 ALL NULL NULL NULL NULL 2 100.00 Using where +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 3 100.00 Using where; Using filesort Warnings: -Note 1003 select (select `test`.`t3`.`a` AS `a` from `test`.`t3` where (`test`.`t3`.`a` < 8) order by 1 desc limit 1) AS `(select t3.a from t3 where a<8 order by 1 desc limit 1)`,'2' AS `a` from (select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`a` > 1)) `tt` +Note 1003 select (select `test`.`t3`.`a` AS `a` from `test`.`t3` where (`test`.`t3`.`a` < 8) order by 1 desc limit 1) AS `(select t3.a from t3 where a<8 order by 1 desc limit 1)`,`test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a` > 1) select * from t1 where t1.a=(select t2.a from t2 where t2.b=(select max(a) from t3) order by 1 desc limit 1); a 2 @@ -365,9 +364,9 @@ INSERT INTO t8 (pseudo,email) VALUES ('2joce1','2test1'); EXPLAIN EXTENDED SELECT pseudo,(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce')) FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index -4 SUBQUERY t8 const PRIMARY PRIMARY 37 1 100.00 Using index +4 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index 2 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 -3 SUBQUERY t8 const PRIMARY PRIMARY 37 1 100.00 Using index +3 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index Warnings: Note 1003 select 'joce' AS `pseudo`,(select 'test' AS `email` from `test`.`t8` where 1) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from `test`.`t8` where 1 SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo,email FROM @@ -1339,7 +1338,7 @@ a explain extended select * from t2 where t2.a in (select a from t1); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 index a a 5 NULL 4 100.00 Using index -1 PRIMARY t1 ref a a 5 test.t2.a 101 100.00 Using index; FirstMatch(t2) +1 PRIMARY t1 ref a a 5 test.t2.a 100 100.00 Using index; FirstMatch(t2) Warnings: Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t1`) where (`test`.`t1`.`a` = `test`.`t2`.`a`) select * from t2 where t2.a in (select a from t1 where t1.b <> 30); @@ -1349,7 +1348,7 @@ a explain extended select * from t2 where t2.a in (select a from t1 where t1.b <> 30); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 index a a 5 NULL 4 100.00 Using index -1 PRIMARY t1 ref a a 5 test.t2.a 101 100.00 Using where; Using index; FirstMatch(t2) +1 PRIMARY t1 ref a a 5 test.t2.a 100 100.00 Using where; Using index; FirstMatch(t2) Warnings: Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t1`) where ((`test`.`t1`.`a` = `test`.`t2`.`a`) and (`test`.`t1`.`b` <> 30)) select * from t2 where t2.a in (select t1.a from t1,t3 where t1.b=t3.a); @@ -1360,7 +1359,7 @@ explain extended select * from t2 where t2.a in (select t1.a from t1,t3 where t1 id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 index a a 5 NULL 4 100.00 Using index 1 PRIMARY t3 index a a 5 NULL 3 100.00 Using index -1 PRIMARY t1 ref a a 10 test.t2.a,test.t3.a 116 100.61 Using index; FirstMatch(t2) +1 PRIMARY t1 ref a a 10 test.t2.a,test.t3.a 11 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`)) insert into t1 values (3,31); @@ -1376,7 +1375,7 @@ a explain extended select * from t2 where t2.a in (select a from t1 where t1.b <> 30); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 index a a 5 NULL 4 100.00 Using index -1 PRIMARY t1 ref a a 5 test.t2.a 101 100.00 Using where; Using index; FirstMatch(t2) +1 PRIMARY t1 ref a a 5 test.t2.a 100 100.00 Using where; Using index; FirstMatch(t2) Warnings: Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t1`) where ((`test`.`t1`.`a` = `test`.`t2`.`a`) and (`test`.`t1`.`b` <> 30)) drop table t0, t1, t2, t3; diff --git a/mysql-test/r/subselect3.result b/mysql-test/r/subselect3.result index af483939598..1751188d0a1 100644 --- a/mysql-test/r/subselect3.result +++ b/mysql-test/r/subselect3.result @@ -879,7 +879,7 @@ Level Code Message Note 1276 Field or reference 'test.t1.a' of SELECT #3 was resolved in SELECT #2 Note 1276 Field or reference 'test.t1.c' of SELECT #3 was resolved in SELECT #2 Error 1054 Unknown column 'c' in 'field list' -Note 1003 select `c` AS `c` from (select (select count(`test`.`t1`.`a`) AS `COUNT(a)` from (select count(`test`.`t1`.`b`) AS `COUNT(b)` from `test`.`t1`) `x` group by `t1`.`c`) AS `(SELECT COUNT(a) FROM +Note 1003 select `c` AS `c` from (select (select count(`test`.`t1`.`a`) AS `COUNT(a)` from (select count(`test`.`t1`.`b`) AS `COUNT(b)` from `test`.`t1`) `x` group by `test`.`t1`.`c`) AS `(SELECT COUNT(a) FROM (SELECT COUNT(b) FROM t1) AS x GROUP BY c )` from `test`.`t1` group by `test`.`t1`.`b`) `y` DROP TABLE t1; @@ -1105,9 +1105,8 @@ a set @@optimizer_switch=default; explain select * from (select a from t0) X where a in (select a from t1); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 11 -1 PRIMARY t1 ALL NULL NULL NULL NULL 20 Using where; FirstMatch() -2 DERIVED t0 ALL NULL NULL NULL NULL 11 +1 PRIMARY t0 ALL NULL NULL NULL NULL 11 +1 PRIMARY t1 ALL NULL NULL NULL NULL 20 Using where; FirstMatch(t0) drop table t0, t1; create table t0 (a int); insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); diff --git a/mysql-test/r/subselect3_jcl6.result b/mysql-test/r/subselect3_jcl6.result index 8d0ccd7c9f5..5f56c1e04df 100644 --- a/mysql-test/r/subselect3_jcl6.result +++ b/mysql-test/r/subselect3_jcl6.result @@ -883,7 +883,7 @@ Level Code Message Note 1276 Field or reference 'test.t1.a' of SELECT #3 was resolved in SELECT #2 Note 1276 Field or reference 'test.t1.c' of SELECT #3 was resolved in SELECT #2 Error 1054 Unknown column 'c' in 'field list' -Note 1003 select `c` AS `c` from (select (select count(`test`.`t1`.`a`) AS `COUNT(a)` from (select count(`test`.`t1`.`b`) AS `COUNT(b)` from `test`.`t1`) `x` group by `t1`.`c`) AS `(SELECT COUNT(a) FROM +Note 1003 select `c` AS `c` from (select (select count(`test`.`t1`.`a`) AS `COUNT(a)` from (select count(`test`.`t1`.`b`) AS `COUNT(b)` from `test`.`t1`) `x` group by `test`.`t1`.`c`) AS `(SELECT COUNT(a) FROM (SELECT COUNT(b) FROM t1) AS x GROUP BY c )` from `test`.`t1` group by `test`.`t1`.`b`) `y` DROP TABLE t1; @@ -1110,9 +1110,8 @@ a set @@optimizer_switch=default; explain select * from (select a from t0) X where a in (select a from t1); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 11 -1 PRIMARY t1 ALL NULL NULL NULL NULL 20 Using where; FirstMatch(); Using join buffer -2 DERIVED t0 ALL NULL NULL NULL NULL 11 +1 PRIMARY t0 ALL NULL NULL NULL NULL 11 +1 PRIMARY t1 ALL NULL NULL NULL NULL 20 Using where; FirstMatch(t0); Using join buffer drop table t0, t1; create table t0 (a int); insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result index 75698a12751..e8a804b20d4 100644 --- a/mysql-test/r/subselect_no_mat.result +++ b/mysql-test/r/subselect_no_mat.result @@ -50,13 +50,13 @@ SELECT (SELECT a) as a; ERROR 42S22: Reference 'a' not supported (forward reference in item list) EXPLAIN EXTENDED SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY system NULL NULL NULL NULL 1 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 -Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having ((select '1' AS `a`) = 1) +Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having ((select `b`.`a` AS `a`) = 1) SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; 1 1 @@ -205,11 +205,10 @@ select (select t3.a from t3 where a<8 order by 1 desc limit 1), a from explain extended select (select t3.a from t3 where a<8 order by 1 desc limit 1), a from (select * from t2 where a>1) as tt; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY system NULL NULL NULL NULL 1 100.00 -3 DERIVED t2 ALL NULL NULL NULL NULL 2 100.00 Using where +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 3 100.00 Using where; Using filesort Warnings: -Note 1003 select (select `test`.`t3`.`a` AS `a` from `test`.`t3` where (`test`.`t3`.`a` < 8) order by 1 desc limit 1) AS `(select t3.a from t3 where a<8 order by 1 desc limit 1)`,'2' AS `a` from (select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`a` > 1)) `tt` +Note 1003 select (select `test`.`t3`.`a` AS `a` from `test`.`t3` where (`test`.`t3`.`a` < 8) order by 1 desc limit 1) AS `(select t3.a from t3 where a<8 order by 1 desc limit 1)`,`test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a` > 1) select * from t1 where t1.a=(select t2.a from t2 where t2.b=(select max(a) from t3) order by 1 desc limit 1); a 2 @@ -369,9 +368,9 @@ INSERT INTO t8 (pseudo,email) VALUES ('2joce1','2test1'); EXPLAIN EXTENDED SELECT pseudo,(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce')) FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index -4 SUBQUERY t8 const PRIMARY PRIMARY 37 1 100.00 Using index +4 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index 2 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 -3 SUBQUERY t8 const PRIMARY PRIMARY 37 1 100.00 Using index +3 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index Warnings: Note 1003 select 'joce' AS `pseudo`,(select 'test' AS `email` from `test`.`t8` where 1) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from `test`.`t8` where 1 SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo,email FROM @@ -1343,7 +1342,7 @@ a explain extended select * from t2 where t2.a in (select a from t1); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 index a a 5 NULL 4 100.00 Using index -1 PRIMARY t1 ref a a 5 test.t2.a 101 100.00 Using index; FirstMatch(t2) +1 PRIMARY t1 ref a a 5 test.t2.a 100 100.00 Using index; FirstMatch(t2) Warnings: Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t1`) where (`test`.`t1`.`a` = `test`.`t2`.`a`) select * from t2 where t2.a in (select a from t1 where t1.b <> 30); @@ -1353,7 +1352,7 @@ a explain extended select * from t2 where t2.a in (select a from t1 where t1.b <> 30); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 index a a 5 NULL 4 100.00 Using index -1 PRIMARY t1 ref a a 5 test.t2.a 101 100.00 Using where; Using index; FirstMatch(t2) +1 PRIMARY t1 ref a a 5 test.t2.a 100 100.00 Using where; Using index; FirstMatch(t2) Warnings: Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t1`) where ((`test`.`t1`.`a` = `test`.`t2`.`a`) and (`test`.`t1`.`b` <> 30)) select * from t2 where t2.a in (select t1.a from t1,t3 where t1.b=t3.a); @@ -1364,7 +1363,7 @@ explain extended select * from t2 where t2.a in (select t1.a from t1,t3 where t1 id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 index a a 5 NULL 4 100.00 Using index 1 PRIMARY t3 index a a 5 NULL 3 100.00 Using index -1 PRIMARY t1 ref a a 10 test.t2.a,test.t3.a 116 100.61 Using index; FirstMatch(t2) +1 PRIMARY t1 ref a a 10 test.t2.a,test.t3.a 11 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`)) insert into t1 values (3,31); @@ -1380,7 +1379,7 @@ a explain extended select * from t2 where t2.a in (select a from t1 where t1.b <> 30); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 index a a 5 NULL 4 100.00 Using index -1 PRIMARY t1 ref a a 5 test.t2.a 101 100.00 Using where; Using index; FirstMatch(t2) +1 PRIMARY t1 ref a a 5 test.t2.a 100 100.00 Using where; Using index; FirstMatch(t2) Warnings: Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t1`) where ((`test`.`t1`.`a` = `test`.`t2`.`a`) and (`test`.`t1`.`b` <> 30)) drop table t0, t1, t2, t3; diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result index 506adaa26a6..18a44e59cda 100644 --- a/mysql-test/r/subselect_no_opts.result +++ b/mysql-test/r/subselect_no_opts.result @@ -50,13 +50,13 @@ SELECT (SELECT a) as a; ERROR 42S22: Reference 'a' not supported (forward reference in item list) EXPLAIN EXTENDED SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY system NULL NULL NULL NULL 1 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 -Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having ((select '1' AS `a`) = 1) +Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having ((select `b`.`a` AS `a`) = 1) SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; 1 1 @@ -205,11 +205,10 @@ select (select t3.a from t3 where a<8 order by 1 desc limit 1), a from explain extended select (select t3.a from t3 where a<8 order by 1 desc limit 1), a from (select * from t2 where a>1) as tt; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY system NULL NULL NULL NULL 1 100.00 -3 DERIVED t2 ALL NULL NULL NULL NULL 2 100.00 Using where +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 3 100.00 Using where; Using filesort Warnings: -Note 1003 select (select `test`.`t3`.`a` AS `a` from `test`.`t3` where (`test`.`t3`.`a` < 8) order by 1 desc limit 1) AS `(select t3.a from t3 where a<8 order by 1 desc limit 1)`,'2' AS `a` from (select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`a` > 1)) `tt` +Note 1003 select (select `test`.`t3`.`a` AS `a` from `test`.`t3` where (`test`.`t3`.`a` < 8) order by 1 desc limit 1) AS `(select t3.a from t3 where a<8 order by 1 desc limit 1)`,`test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a` > 1) select * from t1 where t1.a=(select t2.a from t2 where t2.b=(select max(a) from t3) order by 1 desc limit 1); a 2 @@ -369,9 +368,9 @@ INSERT INTO t8 (pseudo,email) VALUES ('2joce1','2test1'); EXPLAIN EXTENDED SELECT pseudo,(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce')) FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index -4 SUBQUERY t8 const PRIMARY PRIMARY 37 1 100.00 Using index +4 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index 2 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 -3 SUBQUERY t8 const PRIMARY PRIMARY 37 1 100.00 Using index +3 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index Warnings: Note 1003 select 'joce' AS `pseudo`,(select 'test' AS `email` from `test`.`t8` where 1) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from `test`.`t8` where 1 SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo,email FROM diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result index df1e424afa5..10022a77c1d 100644 --- a/mysql-test/r/subselect_no_semijoin.result +++ b/mysql-test/r/subselect_no_semijoin.result @@ -50,13 +50,13 @@ SELECT (SELECT a) as a; ERROR 42S22: Reference 'a' not supported (forward reference in item list) EXPLAIN EXTENDED SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY system NULL NULL NULL NULL 1 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 -Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having ((select '1' AS `a`) = 1) +Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having ((select `b`.`a` AS `a`) = 1) SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; 1 1 @@ -205,11 +205,10 @@ select (select t3.a from t3 where a<8 order by 1 desc limit 1), a from explain extended select (select t3.a from t3 where a<8 order by 1 desc limit 1), a from (select * from t2 where a>1) as tt; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY system NULL NULL NULL NULL 1 100.00 -3 DERIVED t2 ALL NULL NULL NULL NULL 2 100.00 Using where +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 3 100.00 Using where; Using filesort Warnings: -Note 1003 select (select `test`.`t3`.`a` AS `a` from `test`.`t3` where (`test`.`t3`.`a` < 8) order by 1 desc limit 1) AS `(select t3.a from t3 where a<8 order by 1 desc limit 1)`,'2' AS `a` from (select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`a` > 1)) `tt` +Note 1003 select (select `test`.`t3`.`a` AS `a` from `test`.`t3` where (`test`.`t3`.`a` < 8) order by 1 desc limit 1) AS `(select t3.a from t3 where a<8 order by 1 desc limit 1)`,`test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a` > 1) select * from t1 where t1.a=(select t2.a from t2 where t2.b=(select max(a) from t3) order by 1 desc limit 1); a 2 @@ -369,9 +368,9 @@ INSERT INTO t8 (pseudo,email) VALUES ('2joce1','2test1'); EXPLAIN EXTENDED SELECT pseudo,(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce')) FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index -4 SUBQUERY t8 const PRIMARY PRIMARY 37 1 100.00 Using index +4 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index 2 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 -3 SUBQUERY t8 const PRIMARY PRIMARY 37 1 100.00 Using index +3 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index Warnings: Note 1003 select 'joce' AS `pseudo`,(select 'test' AS `email` from `test`.`t8` where 1) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from `test`.`t8` where 1 SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo,email FROM diff --git a/mysql-test/r/table_elim.result b/mysql-test/r/table_elim.result index e71919344eb..fc23505425c 100644 --- a/mysql-test/r/table_elim.result +++ b/mysql-test/r/table_elim.result @@ -117,58 +117,58 @@ t2 where id=f.id); This should use one table: explain select id from v1 where id=2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY f const PRIMARY PRIMARY 4 const 1 Using index +1 SIMPLE f const PRIMARY PRIMARY 4 const 1 Using index This should use one table: explain extended select id from v1 where id in (1,2,3,4); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY f range PRIMARY PRIMARY 4 NULL 4 100.00 Using where; Using index +1 SIMPLE f range PRIMARY PRIMARY 4 NULL 4 100.00 Using where; Using index Warnings: -Note 1276 Field or reference 'test.a2.id' of SELECT #3 was resolved in SELECT #1 +Note 1276 Field or reference 'test.a2.id' of SELECT #3 was resolved in SELECT #2 Note 1003 select `f`.`id` AS `id` from `test`.`t0` `f` where (`f`.`id` in (1,2,3,4)) This should use facts and a1 tables: explain extended select id from v1 where attr1 between 12 and 14; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY a1 range PRIMARY,attr1 attr1 5 NULL 2 100.00 Using index condition; Using MRR -1 PRIMARY f eq_ref PRIMARY PRIMARY 4 test.a1.id 1 100.00 Using index +1 SIMPLE a1 range PRIMARY,attr1 attr1 5 NULL 2 100.00 Using index condition; Using MRR +1 SIMPLE f eq_ref PRIMARY PRIMARY 4 test.a1.id 1 100.00 Using index Warnings: -Note 1276 Field or reference 'test.a2.id' of SELECT #3 was resolved in SELECT #1 +Note 1276 Field or reference 'test.a2.id' of SELECT #3 was resolved in SELECT #2 Note 1003 select `f`.`id` AS `id` from `test`.`t0` `f` join `test`.`t1` `a1` where ((`f`.`id` = `a1`.`id`) and (`a1`.`attr1` between 12 and 14)) This should use facts, a2 and its subquery: explain extended select id from v1 where attr2 between 12 and 14; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY a2 range PRIMARY,attr2 attr2 5 NULL 5 100.00 Using index condition; Using where; Using MRR -1 PRIMARY f eq_ref PRIMARY PRIMARY 4 test.a2.id 1 100.00 Using index +1 SIMPLE a2 range PRIMARY,attr2 attr2 5 NULL 5 100.00 Using index condition; Using where; Using MRR +1 SIMPLE f eq_ref PRIMARY PRIMARY 4 test.a2.id 1 100.00 Using index 3 DEPENDENT SUBQUERY t2 ref PRIMARY PRIMARY 4 test.a2.id 2 100.00 Using index Warnings: -Note 1276 Field or reference 'test.a2.id' of SELECT #3 was resolved in SELECT #1 +Note 1276 Field or reference 'test.a2.id' of SELECT #3 was resolved in SELECT #2 Note 1003 select `f`.`id` AS `id` from `test`.`t0` `f` join `test`.`t2` `a2` where ((`f`.`id` = `a2`.`id`) and (`a2`.`attr2` between 12 and 14) and (`a2`.`fromdate` = (select max(`test`.`t2`.`fromdate`) AS `MAX(fromdate)` from `test`.`t2` where (`test`.`t2`.`id` = `a2`.`id`)))) This should use one table: explain select id from v2 where id=2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY f const PRIMARY PRIMARY 4 const 1 Using index +1 SIMPLE f const PRIMARY PRIMARY 4 const 1 Using index This should use one table: explain extended select id from v2 where id in (1,2,3,4); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY f range PRIMARY PRIMARY 4 NULL 4 100.00 Using where; Using index +1 SIMPLE f range PRIMARY PRIMARY 4 NULL 4 100.00 Using where; Using index Warnings: -Note 1276 Field or reference 'test.f.id' of SELECT #3 was resolved in SELECT #1 +Note 1276 Field or reference 'test.f.id' of SELECT #3 was resolved in SELECT #2 Note 1003 select `f`.`id` AS `id` from `test`.`t0` `f` where (`f`.`id` in (1,2,3,4)) This should use facts and a1 tables: explain extended select id from v2 where attr1 between 12 and 14; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY a1 range PRIMARY,attr1 attr1 5 NULL 2 100.00 Using index condition; Using MRR -1 PRIMARY f eq_ref PRIMARY PRIMARY 4 test.a1.id 1 100.00 Using index +1 SIMPLE a1 range PRIMARY,attr1 attr1 5 NULL 2 100.00 Using index condition; Using MRR +1 SIMPLE f eq_ref PRIMARY PRIMARY 4 test.a1.id 1 100.00 Using index Warnings: -Note 1276 Field or reference 'test.f.id' of SELECT #3 was resolved in SELECT #1 +Note 1276 Field or reference 'test.f.id' of SELECT #3 was resolved in SELECT #2 Note 1003 select `f`.`id` AS `id` from `test`.`t0` `f` join `test`.`t1` `a1` where ((`f`.`id` = `a1`.`id`) and (`a1`.`attr1` between 12 and 14)) This should use facts, a2 and its subquery: explain extended select id from v2 where attr2 between 12 and 14; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY a2 range PRIMARY,attr2 attr2 5 NULL 5 100.00 Using index condition; Using MRR -1 PRIMARY f eq_ref PRIMARY PRIMARY 4 test.a2.id 1 100.00 Using where; Using index +1 SIMPLE a2 range PRIMARY,attr2 attr2 5 NULL 5 100.00 Using index condition; Using MRR +1 SIMPLE f eq_ref PRIMARY PRIMARY 4 test.a2.id 1 100.00 Using where; Using index 3 DEPENDENT SUBQUERY t2 ref PRIMARY PRIMARY 4 test.f.id 2 100.00 Using index Warnings: -Note 1276 Field or reference 'test.f.id' of SELECT #3 was resolved in SELECT #1 +Note 1276 Field or reference 'test.f.id' of SELECT #3 was resolved in SELECT #2 Note 1003 select `f`.`id` AS `id` from `test`.`t0` `f` join `test`.`t2` `a2` where ((`f`.`id` = `a2`.`id`) and (`a2`.`attr2` between 12 and 14) and (`a2`.`fromdate` = (select max(`test`.`t2`.`fromdate`) AS `MAX(fromdate)` from `test`.`t2` where (`test`.`t2`.`id` = `f`.`id`)))) drop view v1, v2; drop table t0, t1, t2; diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 4386f6ed474..3a2fe521a4b 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -117,7 +117,7 @@ c 12 explain extended select c from v5; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 +1 SIMPLE ALL NULL NULL NULL NULL 5 100.00 3 DERIVED t1 ALL NULL NULL NULL NULL 5 100.00 Warnings: Note 1003 select (`v2`.`c` + 1) AS `c` from `test`.`v2` @@ -237,7 +237,7 @@ a 3 explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 3 +1 PRIMARY ALL NULL NULL NULL NULL 6 2 DERIVED t1 ALL NULL NULL NULL NULL 6 Using temporary select * from t1; a @@ -302,7 +302,7 @@ a+1 4 explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 +1 PRIMARY ALL NULL NULL NULL NULL 4 2 DERIVED t1 ALL NULL NULL NULL NULL 4 Using filesort drop view v1; drop table t1; diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index 2d5c515d0b5..8de8190b784 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -110,7 +110,7 @@ show create view mysqltest.v1; ERROR 42000: SHOW VIEW command denied to user 'mysqltest_1'@'localhost' for table 'v1' explain select c from mysqltest.v2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 0 const row not found +1 PRIMARY ALL NULL NULL NULL NULL 2 2 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table show create view mysqltest.v2; ERROR 42000: SHOW VIEW command denied to user 'mysqltest_1'@'localhost' for table 'v2' @@ -131,7 +131,7 @@ View Create View character_set_client collation_connection v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest`.`v1` AS select (`mysqltest`.`t1`.`a` + 1) AS `c`,(`mysqltest`.`t1`.`b` + 1) AS `d` from `mysqltest`.`t1` latin1 latin1_swedish_ci explain select c from mysqltest.v2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 0 const row not found +1 PRIMARY ALL NULL NULL NULL NULL 2 2 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table show create view mysqltest.v2; View Create View character_set_client collation_connection @@ -144,7 +144,7 @@ View Create View character_set_client collation_connection v3 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest`.`v3` AS select (`mysqltest`.`t2`.`a` + 1) AS `c`,(`mysqltest`.`t2`.`b` + 1) AS `d` from `mysqltest`.`t2` latin1 latin1_swedish_ci explain select c from mysqltest.v4; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 0 const row not found +1 PRIMARY ALL NULL NULL NULL NULL 2 2 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table show create view mysqltest.v4; View Create View character_set_client collation_connection diff --git a/mysql-test/t/derived_view.test b/mysql-test/t/derived_view.test new file mode 100644 index 00000000000..a8dd703d58c --- /dev/null +++ b/mysql-test/t/derived_view.test @@ -0,0 +1,217 @@ +--disable_warnings +drop table if exists t1,t2; +drop view if exists v1,v2,v3,v4; +--enable_warnings +create table t1(f1 int, f11 int); +create table t2(f2 int, f22 int); +insert into t1 values(1,1),(2,2),(3,3),(5,5),(9,9),(7,7); +insert into t1 values(17,17),(13,13),(11,11),(15,15),(19,19); +insert into t2 values(1,1),(3,3),(2,2),(4,4),(8,8),(6,6); +insert into t2 values(12,12),(14,14),(10,10),(18,18),(16,16); + +--echo Tests: + +--echo for merged derived tables +--echo explain for simple derived +explain select * from (select * from t1) tt; +select * from (select * from t1) tt; +--echo explain for multitable derived +explain extended select * from (select * from t1 join t2 on f1=f2) tt; +select * from (select * from t1 join t2 on f1=f2) tt; +--echo explain for derived with where +explain extended + select * from (select * from t1 where f1 in (2,3)) tt where f11=2; +select * from (select * from t1 where f1 in (2,3)) tt where f11=2; +--echo join of derived +explain extended + select * from (select * from t1 where f1 in (2,3)) tt join + (select * from t1 where f1 in (1,2)) aa on tt.f1=aa.f1; +select * from (select * from t1 where f1 in (2,3)) tt join + (select * from t1 where f1 in (1,2)) aa on tt.f1=aa.f1; + +flush status; +explain extended + select * from (select * from t1 where f1 in (2,3)) tt where f11=2; +show status like 'Handler_read%'; +flush status; +select * from (select * from t1 where f1 in (2,3)) tt where f11=2; +show status like 'Handler_read%'; + +--echo for merged views +create view v1 as select * from t1; +create view v2 as select * from t1 join t2 on f1=f2; +create view v3 as select * from t1 where f1 in (2,3); +create view v4 as select * from t2 where f2 in (2,3); +--echo explain for simple views +explain extended select * from v1; +select * from v1; +--echo explain for multitable views +explain extended select * from v2; +select * from v2; +--echo explain for views with where +explain extended select * from v3 where f11 in (1,3); +select * from v3 where f11 in (1,3); +--echo explain for joined views +explain extended + select * from v3 join v4 on f1=f2; +select * from v3 join v4 on f1=f2; + +flush status; +explain extended select * from v4 where f2 in (1,3); +show status like 'Handler_read%'; +flush status; +select * from v4 where f2 in (1,3); +show status like 'Handler_read%'; + +--echo for materialized derived tables +--echo explain for simple derived +explain extended select * from (select * from t1 group by f1) tt; +select * from (select * from t1 having f1=f1) tt; +--echo explain showing created indexes +explain extended + select * from t1 join (select * from t2 group by f2) tt on f1=f2; +select * from t1 join (select * from t2 group by f2) tt on f1=f2; +--echo explain showing late materialization +flush status; +explain select * from t1 join (select * from t2 group by f2) tt on f1=f2; +show status like 'Handler_read%'; +flush status; +select * from t1 join (select * from t2 group by f2) tt on f1=f2; +show status like 'Handler_read%'; + +--echo for materialized views +drop view v1,v2,v3; +create view v1 as select * from t1 group by f1; +create view v2 as select * from t2 group by f2; +create view v3 as select t1.f1,t1.f11 from t1 join t1 as t11 where t1.f1=t11.f1 + having t1.f1<100; +--echo explain for simple derived +explain extended select * from v1; +select * from v1; +--echo explain showing created indexes +explain extended select * from t1 join v2 on f1=f2; +select * from t1 join v2 on f1=f2; +explain extended + select * from t1,v3 as v31,v3 where t1.f1=v31.f1 and t1.f1=v3.f1; +flush status; +select * from t1,v3 as v31,v3 where t1.f1=v31.f1 and t1.f1=v3.f1; +show status like 'Handler_read%'; +--echo explain showing late materialization +flush status; +explain select * from t1 join v2 on f1=f2; +show status like 'Handler_read%'; +flush status; +select * from t1 join v2 on f1=f2; +show status like 'Handler_read%'; + +explain extended select * from v1 join v4 on f1=f2; +select * from v1 join v4 on f1=f2; + +--echo merged derived in merged derived +explain extended select * from (select * from + (select * from t1 where f1 < 7) tt where f1 > 2) zz; +select * from (select * from + (select * from t1 where f1 < 7) tt where f1 > 2) zz; + +--echo materialized derived in merged derived +explain extended select * from (select * from + (select * from t1 where f1 < 7 group by f1) tt where f1 > 2) zz; +select * from (select * from + (select * from t1 where f1 < 7 group by f1) tt where f1 > 2) zz; + +--echo merged derived in materialized derived +explain extended select * from (select * from + (select * from t1 where f1 < 7) tt where f1 > 2 group by f1) zz; +select * from (select * from + (select * from t1 where f1 < 7) tt where f1 > 2 group by f1) zz; + +--echo materialized derived in materialized derived +explain extended select * from (select * from + (select * from t1 where f1 < 7 group by f1) tt where f1 > 2 group by f1) zz; +select * from (select * from + (select * from t1 where f1 < 7 group by f1) tt where f1 > 2 group by f1) zz; + +--echo mat in merged derived join mat in merged derived +explain extended select * from + (select * from (select * from t1 where f1 < 7 group by f1) tt where f1 > 2) x +join + (select * from (select * from t1 where f1 < 7 group by f1) tt where f1 > 2) z + on x.f1 = z.f1; + +flush status; +select * from + (select * from (select * from t1 where f1 < 7 group by f1) tt where f1 > 2) x +join + (select * from (select * from t1 where f1 < 7 group by f1) tt where f1 > 2) z + on x.f1 = z.f1; +show status like 'Handler_read%'; +flush status; + +--echo merged in merged derived join merged in merged derived +explain extended select * from + (select * from + (select * from t1 where f1 < 7 ) tt where f1 > 2 ) x +join + (select * from + (select * from t1 where f1 < 7 ) tt where f1 > 2 ) z + on x.f1 = z.f1; + +select * from + (select * from + (select * from t1 where f1 < 7 ) tt where f1 > 2 ) x +join + (select * from + (select * from t1 where f1 < 7 ) tt where f1 > 2 ) z + on x.f1 = z.f1; + +--echo materialized in materialized derived join +--echo materialized in materialized derived +explain extended select * from + (select * from + (select * from t1 where f1 < 7 group by f1) tt where f1 > 2 group by f1) x +join + (select * from + (select * from t1 where f1 < 7 group by f1) tt where f1 > 2 group by f1) z + on x.f1 = z.f1; + +select * from + (select * from + (select * from t1 where f1 < 7 group by f1) tt where f1 > 2 group by f1) x +join + (select * from + (select * from t1 where f1 < 7 group by f1) tt where f1 > 2 group by f1) z + on x.f1 = z.f1; + +--echo merged view in materialized derived +explain extended +select * from (select * from v4 group by 1) tt; +select * from (select * from v4 group by 1) tt; + +--echo materialized view in merged derived +explain extended +select * from ( select * from v1 where f1 < 7) tt; +select * from ( select * from v1 where f1 < 7) tt; + +--echo merged view in a merged view in a merged derived +create view v6 as select * from v4 where f2 < 7; +explain extended select * from (select * from v6) tt; +select * from (select * from v6) tt; + +--echo materialized view in a merged view in a materialized derived +create view v7 as select * from v1; +explain extended select * from (select * from v7 group by 1) tt; +select * from (select * from v7 group by 1) tt; + +--echo join of above two +explain extended select * from v6 join v7 on f2=f1; +select * from v6 join v7 on f2=f1; + +--echo test two keys +explain select * from t1 join (select * from t2 group by f2) tt on t1.f1=tt.f2 join t1 xx on tt.f22=xx.f1; +select * from t1 join (select * from t2 group by f2) tt on t1.f1=tt.f2 join t1 xx on tt.f22=xx.f1; + + +--echo TODO: Add test with 64 tables mergeable view to test fall back to +--echo materialization on tables > MAX_TABLES merge +drop table t1,t2; +drop view v1,v2,v3,v4,v6,v7; diff --git a/mysql-test/t/lock_multi_bug38499.test b/mysql-test/t/lock_multi_bug38499.test index 3d3f084ba5f..b812984e516 100644 --- a/mysql-test/t/lock_multi_bug38499.test +++ b/mysql-test/t/lock_multi_bug38499.test @@ -16,7 +16,9 @@ connect (writer,localhost,root,,); DROP TABLE IF EXISTS t1; --enable_warnings CREATE TABLE t1( a INT, b INT ); +CREATE TABLE t2( a INT, b INT ); INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3), (4, 4); +INSERT INTO t2 VALUES (1, 1), (2, 2), (3, 3), (4, 4); --echo # 1. test regular tables --echo # 1.1. test altering of columns that multiupdate doesn't use @@ -28,7 +30,7 @@ while ($i) { --dec $i --connection writer - send UPDATE t1, (SELECT 1 FROM t1 t1i) d SET a = 0 WHERE 1=0; + send UPDATE t1, (SELECT 1 FROM t2 t1i) d SET a = 0 WHERE 1=0; --connection locker ALTER TABLE t1 ADD COLUMN (c INT); @@ -41,7 +43,7 @@ while ($i) { --echo # 1.1.2. PS mode --connection writer -PREPARE stmt FROM 'UPDATE t1, (SELECT 1 FROM t1 t1i) d SET a = 0 WHERE 1=0'; +PREPARE stmt FROM 'UPDATE t1, (SELECT 1 FROM t2 t1i) d SET a = 0 WHERE 1=0'; let $i = 100; while ($i) { @@ -75,7 +77,7 @@ while ($i) { UPDATE t1 SET a=b; --connection writer ---send UPDATE t1, (SELECT 1 FROM t1 t1i) d SET a = 0 WHERE 1=0; +--send UPDATE t1, (SELECT 1 FROM t2 t1i) d SET a = 0 WHERE 1=0; --connection locker --error 0,ER_CANT_DROP_FIELD_OR_KEY @@ -100,7 +102,7 @@ while ($i) { UPDATE t1 SET a=b; --connection writer - PREPARE stmt FROM 'UPDATE t1, (SELECT 1 FROM t1 t1i) d SET a = 0 WHERE 1=0'; + PREPARE stmt FROM 'UPDATE t1, (SELECT 1 FROM t2 t1i) d SET a = 0 WHERE 1=0'; --send EXECUTE stmt --connection locker @@ -210,7 +212,7 @@ while ($i) { } --enable_query_log --connection default -DROP TABLE t1; +DROP TABLE t1,t2; # Close connections diff --git a/sql/field.cc b/sql/field.cc index 80f138c53f6..8f24bd37e30 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -10458,3 +10458,27 @@ Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, field_name); } } + + +/* + @brief + Return possible keys for a field + + @details + Return bit map of keys over this field which can be used by the range + optimizer. For a field of a generic table such keys are all keys that starts + from this field. For a field of a materialized derived table/view such keys + are all keys in which this field takes a part. This is less restrictive as + keys for a materialized derived table/view are generated on the fly from + present fields, thus the case when a field for the beginning of a key is + absent is impossible. + + @return map of possible keys +*/ + +key_map Field::get_possible_keys() +{ + DBUG_ASSERT(table->pos_in_table_list); + return (table->pos_in_table_list->is_materialized_derived() ? + part_of_key : key_start); +} diff --git a/sql/field.h b/sql/field.h index 042e4610b51..c98148e50a9 100644 --- a/sql/field.h +++ b/sql/field.h @@ -582,6 +582,9 @@ public: DBUG_ASSERT(0); return GEOM_GEOMETRY; } + + key_map get_possible_keys(); + /* Hash value */ virtual void hash(ulong *nr, ulong *nr2); friend bool reopen_table(THD *,struct st_table *,bool); diff --git a/sql/handler.cc b/sql/handler.cc index 411290de64d..9de575fda10 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2480,8 +2480,9 @@ int handler::update_auto_increment() void handler::column_bitmaps_signal() { DBUG_ENTER("column_bitmaps_signal"); - DBUG_PRINT("info", ("read_set: 0x%lx write_set: 0x%lx", (long) table->read_set, - (long) table->write_set)); + if (table) + DBUG_PRINT("info", ("read_set: 0x%lx write_set: 0x%lx", + (long) table->read_set, (long) table->write_set)); DBUG_VOID_RETURN; } diff --git a/sql/item.cc b/sql/item.cc index 0b993abbdfc..c204f505081 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -711,6 +711,23 @@ bool Item_field::register_field_in_bitmap(uchar *arg) return 0; } + +/* + Mark field in write_map + + NOTES + This is used by UPDATE to register underlying fields of used view fields. +*/ + +bool Item_field::register_field_in_write_map(uchar *arg) +{ + TABLE *table= (TABLE *) arg; + if (field->table == table || !table) + bitmap_set_bit(field->table->write_set, field->field_index); + return 0; +} + + bool Item::check_cols(uint c) { if (c != 1) @@ -2202,6 +2219,10 @@ table_map Item_field::used_tables() const return (depended_from ? OUTER_REF_TABLE_BIT : field->table->map); } +table_map Item_field::all_used_tables() const +{ + return (depended_from ? OUTER_REF_TABLE_BIT : field->table->map); +} void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref) { @@ -2454,7 +2475,7 @@ my_decimal *Item_float::val_decimal(my_decimal *decimal_value) void Item_string::print(String *str, enum_query_type query_type) { - if (query_type == QT_ORDINARY && is_cs_specified()) + if (query_type != QT_IS && is_cs_specified()) { str->append('_'); str->append(collation.collation->csname); @@ -2462,7 +2483,7 @@ void Item_string::print(String *str, enum_query_type query_type) str->append('\''); - if (query_type == QT_ORDINARY || + if (query_type != QT_IS || my_charset_same(str_value.charset(), system_charset_info)) { str_value.print(str); @@ -3945,6 +3966,34 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) } +/* + @brief + Whether a table belongs to an outer select. + + @param table table to check + @param select current select + + @details + Try to find select the table belongs to by ascending the derived tables chain. +*/ + +static +bool is_outer_table(TABLE_LIST *table, SELECT_LEX *select) +{ + DBUG_ASSERT(table->select_lex != select); + TABLE_LIST *tl; + + for (tl= select->master_unit()->derived; + tl && tl->is_merged_derived(); + select= tl->select_lex, tl= select->master_unit()->derived) + { + if (tl->select_lex == table->select_lex) + return FALSE; + } + return TRUE; +} + + /** Resolve the name of an outer select column reference. @@ -4382,7 +4431,8 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if (!outer_fixed && cached_table && cached_table->select_lex && context->select_lex && - cached_table->select_lex != context->select_lex) + cached_table->select_lex != context->select_lex && + is_outer_table(cached_table, context->select_lex)) { int ret; if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) @@ -5786,8 +5836,9 @@ public: st_select_lex *sel; for (sel= current_select; sel; sel= sel->outer_select()) { + List_iterator li(sel->leaf_tables); TABLE_LIST *tbl; - for (tbl= sel->leaf_tables; tbl; tbl= tbl->next_leaf) + while ((tbl= li++)) { if (tbl->table == item->field->table) { @@ -7506,6 +7557,8 @@ Item_result Item_type_holder::result_type() const enum_field_types Item_type_holder::get_real_type(Item *item) { + if (item->type() == REF_ITEM) + item= item->real_item(); switch(item->type()) { case FIELD_ITEM: diff --git a/sql/item.h b/sql/item.h index 191c78bb53f..4a5c0fc84e8 100644 --- a/sql/item.h +++ b/sql/item.h @@ -778,6 +778,7 @@ public: class Field_enumerator) */ virtual table_map used_tables() const { return (table_map) 0L; } + virtual table_map all_used_tables() const { return used_tables(); } /* Return table map of tables that can't be NULL tables (tables that are used in a context where if they would contain a NULL row generated @@ -934,8 +935,12 @@ public: virtual bool reset_query_id_processor(uchar *query_id_arg) { return 0; } virtual bool is_expensive_processor(uchar *arg) { return 0; } virtual bool register_field_in_read_map(uchar *arg) { return 0; } + virtual bool register_field_in_write_map(uchar *arg) { return 0; } virtual bool enumerate_field_refs_processor(uchar *arg) { return 0; } virtual bool mark_as_eliminated_processor(uchar *arg) { return 0; } + virtual bool view_used_tables_processor(uchar *arg) { return 0; } + virtual bool eval_not_null_tables(uchar *opt_arg) { return 0; } + /* The next function differs from the previous one that a bitmap to be updated is passed as uchar *arg. @@ -1143,6 +1148,12 @@ public: { return Field::GEOM_GEOMETRY; }; String *check_well_formed_result(String *str, bool send_error= 0); bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs); + table_map view_used_tables(TABLE_LIST *view) + { + view->view_used_tables= 0; + walk(&Item::view_used_tables_processor, 0, (uchar *) view); + return view->view_used_tables; + } }; @@ -1616,6 +1627,7 @@ public: int save_in_field(Field *field,bool no_conversions); void save_org_in_field(Field *field); table_map used_tables() const; + table_map all_used_tables() const; enum Item_result result_type () const { return field->result_type(); @@ -1645,6 +1657,7 @@ public: bool add_field_to_set_processor(uchar * arg); bool find_item_in_field_list_processor(uchar *arg); bool register_field_in_read_map(uchar *arg); + bool register_field_in_write_map(uchar *arg); bool register_field_in_bitmap(uchar *arg); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool vcol_in_partition_func_processor(uchar *bool_arg); @@ -2515,11 +2528,14 @@ public: */ class Item_direct_view_ref :public Item_direct_ref { + TABLE_LIST *view; public: Item_direct_view_ref(Name_resolution_context *context_arg, Item **item, - const char *table_name_arg, - const char *field_name_arg) - :Item_direct_ref(context_arg, item, table_name_arg, field_name_arg) {} + const char *table_name_arg, + const char *field_name_arg, + TABLE_LIST *view_arg) + :Item_direct_ref(context_arg, item, table_name_arg, field_name_arg), + view(view_arg) {} /* Constructor need to process subselect with temporary tables (see Item) */ Item_direct_view_ref(THD *thd, Item_direct_ref *item) :Item_direct_ref(thd, item) {} @@ -2533,6 +2549,24 @@ public: return item; } virtual Ref_Type ref_type() { return VIEW_REF; } + table_map used_tables() const + { + return depended_from ? + OUTER_REF_TABLE_BIT : + (view->merged ? (*ref)->used_tables() : view->table->map); + } + bool walk(Item_processor processor, bool walk_subquery, uchar *arg) + { + return (*ref)->walk(processor, walk_subquery, arg) || + (this->*processor)(arg); + } + bool view_used_tables_processor(uchar *arg) + { + TABLE_LIST *view_arg= (TABLE_LIST *) arg; + if (view_arg == view) + view_arg->view_used_tables|= (*ref)->used_tables(); + return 0; + } }; @@ -2874,6 +2908,17 @@ public: }; +/* + Cached_item_XXX objects are not exactly caches. They do the following: + + Each Cached_item_XXX object has + - its source item + - saved value of the source item + - cmp() method that compares the saved value with the current value of the + source item, and if they were not equal saves item's value into the saved + value. +*/ + /* Cached_item_XXX objects are not exactly caches. They do the following: diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 22b36233e17..0846f732d26 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2204,6 +2204,16 @@ bool Item_func_between::fix_fields(THD *thd, Item **ref) thd->lex->current_select->between_count++; + + return 0; +} + + +bool Item_func_between::eval_not_null_tables(uchar *opt_arg) +{ + if (Item_func_opt_neg::eval_not_null_tables(NULL)) + return 1; + /* not_null_tables_cache == union(T1(e),T1(e1),T1(e2)) */ if (pred_level && !negated) return 0; @@ -2212,9 +2222,8 @@ bool Item_func_between::fix_fields(THD *thd, Item **ref) not_null_tables_cache= (args[0]->not_null_tables() | (args[1]->not_null_tables() & args[2]->not_null_tables())); - return 0; -} +} void Item_func_between::fix_length_and_dec() @@ -2575,13 +2584,22 @@ Item_func_if::fix_fields(THD *thd, Item **ref) if (Item_func::fix_fields(thd, ref)) return 1; + return 0; +} + + +bool +Item_func_if::eval_not_null_tables(uchar *opt_arg) +{ + if (Item_func::eval_not_null_tables(NULL)) + return 1; + not_null_tables_cache= (args[1]->not_null_tables() & args[2]->not_null_tables()); return 0; } - void Item_func_if::fix_length_and_dec() { @@ -3761,11 +3779,22 @@ bool Item_func_in::nulls_in_row() bool Item_func_in::fix_fields(THD *thd, Item **ref) { - Item **arg, **arg_end; if (Item_func_opt_neg::fix_fields(thd, ref)) return 1; + return 0; +} + + +bool +Item_func_in::eval_not_null_tables(uchar *opt_arg) +{ + Item **arg, **arg_end; + + if (Item_func_opt_neg::eval_not_null_tables(NULL)) + return 1; + /* not_null_tables_cache == union(T1(e),union(T1(ei))) */ if (pred_level && negated) return 0; @@ -4186,7 +4215,6 @@ Item_cond::fix_fields(THD *thd, Item **ref) */ while ((item=li++)) { - table_map tmp_table_map; while (item->type() == Item::COND_ITEM && ((Item_cond*) item)->functype() == functype() && !((Item_cond*) item)->list.is_empty()) @@ -4208,11 +4236,12 @@ Item_cond::fix_fields(THD *thd, Item **ref) and_tables_cache= (table_map) 0; else { - tmp_table_map= item->not_null_tables(); + table_map tmp_table_map= item->not_null_tables(); not_null_tables_cache|= tmp_table_map; and_tables_cache&= tmp_table_map; const_item_cache= FALSE; - } + } + with_sum_func= with_sum_func || item->with_sum_func; with_subselect|= item->with_subselect; if (item->maybe_null) @@ -4226,6 +4255,28 @@ Item_cond::fix_fields(THD *thd, Item **ref) } +bool +Item_cond::eval_not_null_tables(uchar *opt_arg) +{ + Item *item; + List_iterator li(list); + and_tables_cache= ~(table_map) 0; + while ((item=li++)) + { + table_map tmp_table_map; + if (item->const_item()) + and_tables_cache= (table_map) 0; + else + { + tmp_table_map= item->not_null_tables(); + not_null_tables_cache|= tmp_table_map; + and_tables_cache&= tmp_table_map; + } + } + return 0; +} + + void Item_cond::fix_after_pullout(st_select_lex *new_parent, Item **ref) { List_iterator li(list); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index f6c5534d0a2..ee23a46462b 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -631,6 +631,7 @@ public: bool is_bool_func() { return 1; } CHARSET_INFO *compare_collation() { return cmp_collation.collation; } uint decimal_precision() const { return 1; } + bool eval_not_null_tables(uchar *opt_arg); }; @@ -730,6 +731,7 @@ public: void fix_length_and_dec(); uint decimal_precision() const; const char *func_name() const { return "if"; } + bool eval_not_null_tables(uchar *opt_arg); }; @@ -1256,6 +1258,7 @@ public: bool nulls_in_row(); bool is_bool_func() { return 1; } CHARSET_INFO *compare_collation() { return cmp_collation.collation; } + bool eval_not_null_tables(uchar *opt_arg); }; class cmp_item_row :public cmp_item @@ -1510,6 +1513,7 @@ public: bool subst_argument_checker(uchar **arg) { return TRUE; } Item *compile(Item_analyzer analyzer, uchar **arg_p, Item_transformer transformer, uchar *arg_t); + bool eval_not_null_tables(uchar *opt_arg); }; diff --git a/sql/item_func.cc b/sql/item_func.cc index 0676ce4f633..6ddff0511a7 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -192,7 +192,6 @@ Item_func::fix_fields(THD *thd, Item **ref) with_sum_func= with_sum_func || item->with_sum_func; used_tables_cache|= item->used_tables(); - not_null_tables_cache|= item->not_null_tables(); const_item_cache&= item->const_item(); with_subselect|= item->with_subselect; } @@ -206,6 +205,21 @@ Item_func::fix_fields(THD *thd, Item **ref) } +bool +Item_func::eval_not_null_tables(uchar *opt_arg) +{ + Item **arg,**arg_end; + if (arg_count) + { + for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++) + { + not_null_tables_cache|= (*arg)->not_null_tables(); + } + } + return FALSE; +} + + void Item_func::fix_after_pullout(st_select_lex *new_parent, Item **ref) { Item **arg,**arg_end; @@ -3895,6 +3909,20 @@ bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref) entry->collation.set(args[0]->collation.collation, DERIVATION_IMPLICIT); collation.set(entry->collation.collation, DERIVATION_IMPLICIT); cached_result_type= args[0]->result_type(); + { + /* + When this function is used in a derived table/view force the derived + table to be materialized to preserve possible side-effect of setting a + user variable. + */ + SELECT_LEX_UNIT *unit= thd->lex->current_select->master_unit(); + TABLE_LIST *derived; + for (derived= unit->derived; + derived; + derived= derived->select_lex->master_unit()->derived) + derived->set_materialized_derived(); + } + return FALSE; } diff --git a/sql/item_func.h b/sql/item_func.h index 679348b1795..f6b1100ec26 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -181,6 +181,7 @@ public: Item_transformer transformer, uchar *arg_t); void traverse_cond(Cond_traverser traverser, void * arg, traverse_order order); + bool eval_not_null_tables(uchar *opt_arg); // bool is_expensive_processor(uchar *arg); // virtual bool is_expensive() { return 0; } inline double fix_result(double value) @@ -1617,14 +1618,7 @@ public: void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1;} bool check_vcol_func_processor(uchar *int_arg) { -#if 0 - DBUG_ENTER("Item_func_is_free_lock::check_vcol_func_processor"); - DBUG_PRINT("info", - ("check_vcol_func_processor returns TRUE: unsupported function")); - DBUG_RETURN(TRUE); -#else return trace_unsupported_by_check_vcol_func_processor(func_name()); -#endif } }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 741cd2e3e85..020d73f6ffe 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -2894,6 +2894,9 @@ int subselect_uniquesubquery_engine::exec() DBUG_RETURN(0); } + if (!tab->preread_init_done && tab->preread_init()) + DBUG_RETURN(1); + if (null_keypart) DBUG_RETURN(scan_table()); @@ -3026,7 +3029,7 @@ subselect_uniquesubquery_engine::~subselect_uniquesubquery_engine() int subselect_indexsubquery_engine::exec() { - DBUG_ENTER("subselect_indexsubquery_engine::exec"); + DBUG_ENTER("subselect_indexsubquery_engine"); int error; bool null_finding= 0; TABLE *table= tab->table; @@ -3057,6 +3060,9 @@ int subselect_indexsubquery_engine::exec() DBUG_RETURN(0); } + if (!tab->preread_init_done && tab->preread_init()) + DBUG_RETURN(1); + if (null_keypart) DBUG_RETURN(scan_table()); @@ -3158,10 +3164,13 @@ void subselect_uniquesubquery_engine::exclude() } -table_map subselect_engine::calc_const_tables(TABLE_LIST *table) +table_map subselect_engine::calc_const_tables(List &list) { table_map map= 0; - for (; table; table= table->next_leaf) + List_iterator ti(list); + TABLE_LIST *table; + //for (; table; table= table->next_leaf) + while ((table= ti++)) { TABLE *tbl= table->table; if (tbl && tbl->const_table) @@ -3173,14 +3182,13 @@ table_map subselect_engine::calc_const_tables(TABLE_LIST *table) table_map subselect_single_select_engine::upper_select_const_tables() { - return calc_const_tables((TABLE_LIST *) select_lex->outer_select()-> - leaf_tables); + return calc_const_tables(select_lex->outer_select()->leaf_tables); } table_map subselect_union_engine::upper_select_const_tables() { - return calc_const_tables((TABLE_LIST *) unit->outer_select()->leaf_tables); + return calc_const_tables(unit->outer_select()->leaf_tables); } @@ -3711,7 +3719,7 @@ bool subselect_hash_sj_engine::init_permanent(List *tmp_columns) if (((select_union*) result)->create_result_table( thd, tmp_columns, TRUE, tmp_create_options, - "materialized subselect", TRUE)) + "materialized subselect", TRUE, TRUE)) DBUG_RETURN(TRUE); tmp_table= ((select_union*) result)->table; diff --git a/sql/item_subselect.h b/sql/item_subselect.h index e538f02d80a..00844f6cd42 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -531,6 +531,7 @@ public: virtual bool may_be_null() { return maybe_null; }; virtual table_map upper_select_const_tables()= 0; static table_map calc_const_tables(TABLE_LIST *); + static table_map calc_const_tables(List &list); virtual void print(String *str, enum_query_type query_type)= 0; virtual bool change_result(Item_subselect *si, select_result_interceptor *result)= 0; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 77fced102ee..9d1397b47d5 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -62,12 +62,15 @@ class Parser_state; QT_ORDINARY -- ordinary SQL query. QT_IS -- SQL query to be shown in INFORMATION_SCHEMA (in utf8 and without - character set introducers). + character set introducers). + QT_VIEW_INTERNAL -- view internal representation (like QT_ORDINARY except + ORDER BY clause) */ enum enum_query_type { QT_ORDINARY, - QT_IS + QT_IS, + QT_VIEW_INTERNAL }; /* TODO convert all these three maps to Bitmap classes */ @@ -511,7 +514,6 @@ protected: #define OPTION_PROFILING (ULL(1) << 33) - /** Maximum length of time zone name that we support (Time zone name is char(64) in db). mysqlbinlog needs it. @@ -1276,11 +1278,9 @@ int mysql_explain_select(THD *thd, SELECT_LEX *sl, char const *type, select_result *result); bool mysql_union(THD *thd, LEX *lex, select_result *result, SELECT_LEX_UNIT *unit, ulong setup_tables_done_option); -bool mysql_handle_derived(LEX *lex, bool (*processor)(THD *thd, - LEX *lex, - TABLE_LIST *table)); -bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *t); -bool mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *t); +bool mysql_handle_derived(LEX *lex, uint phases); +bool mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases); +bool mysql_handle_list_of_derived(LEX *lex, TABLE_LIST *dt_list, uint phases); Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Item ***copy_func, Field **from_field, Field **def_field, @@ -1288,6 +1288,17 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, bool table_cant_handle_bit_fields, bool make_copy_field, uint convert_blob_length); +bool open_tmp_table(TABLE *table); +#if defined(WITH_MARIA_STORAGE_ENGINE) && defined(USE_MARIA_FOR_TMP_TABLES) +#define TMP_ENGINE_HTON maria_hton +#else +#define TMP_ENGINE_HTON myisam_hton +#endif +bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, + ENGINE_COLUMNDEF *start_recinfo, + ENGINE_COLUMNDEF **recinfo, + ulonglong options); + void sp_prepare_create_field(THD *thd, Create_field *sql_field); int prepare_create_field(Create_field *sql_field, uint *blob_columns, @@ -1600,17 +1611,21 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, bool insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, const char *table_name, List_iterator *it, bool any_privileges); +void make_leaves_list(List &list, TABLE_LIST *tables, + bool full_table_list, TABLE_LIST *boundary); bool setup_tables(THD *thd, Name_resolution_context *context, List *from_clause, TABLE_LIST *tables, - TABLE_LIST **leaves, bool select_insert); + List &leaves, bool select_insert, + bool full_table_list); bool setup_tables_and_check_access(THD *thd, Name_resolution_context *context, List *from_clause, TABLE_LIST *tables, - TABLE_LIST **leaves, + List &leaves, bool select_insert, ulong want_access_first, - ulong want_access); + ulong want_access, + bool full_table_list); int setup_wild(THD *thd, TABLE_LIST *tables, List &fields, List *sum_func_list, uint wild_num); bool setup_fields(THD *thd, Item** ref_pointer_array, @@ -1629,7 +1644,7 @@ inline bool setup_fields_with_no_wrap(THD *thd, Item **ref_pointer_array, thd->lex->select_lex.no_wrap_view_item= FALSE; return res; } -int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, +int setup_conds(THD *thd, TABLE_LIST *tables, List &leaves, COND **conds); int setup_ftfuncs(SELECT_LEX* select); int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order); @@ -1651,7 +1666,8 @@ inline int open_and_lock_tables(THD *thd, TABLE_LIST *tables) /* simple open_and_lock_tables without derived handling for single table */ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, thr_lock_type lock_type); -bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags); +bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags, + uint dt_phases); int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen); int decide_logging_format(THD *thd, TABLE_LIST *tables); TABLE *open_temporary_table(THD *thd, const char *path, const char *db, @@ -1680,6 +1696,7 @@ void remove_db_from_cache(const char *db); void flush_tables(); bool is_equal(const LEX_STRING *a, const LEX_STRING *b); char *make_default_log_name(char *buff,const char* log_ext); +void unfix_fields(List &items); #ifdef WITH_PARTITION_STORAGE_ENGINE uint fast_alter_partition_table(THD *thd, TABLE *table, @@ -2528,7 +2545,7 @@ Item * all_any_subquery_creator(Item *left_expr, inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr) { table->used_fields= 0; - table->const_table= 0; + table_list->reset_const_table(); table->null_row= 0; table->status= STATUS_NO_RECORD; table->maybe_null= table_list->outer_join; @@ -2544,6 +2561,14 @@ inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr) table->force_index_order= table->force_index_group= 0; table->covering_keys= table->s->keys_for_keyread; table->merge_keys.clear_all(); + TABLE_LIST *orig= table_list->select_lex ? + table_list->select_lex->master_unit()->derived : 0; + if (!orig || !orig->is_merged_derived()) + { + /* Tables merged from derived were set up already.*/ + table->covering_keys= table->s->keys_for_keyread; + table->merge_keys.clear_all(); + } } diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 27ecdea9568..d17d921a965 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -7450,7 +7450,7 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only, SEL_ARG_RANGE_SEQ seq; RANGE_SEQ_IF seq_if = {sel_arg_range_seq_init, sel_arg_range_seq_next, 0, 0}; handler *file= param->table->file; - ha_rows rows; + ha_rows rows= HA_POS_ERROR; uint keynr= param->real_keynr[idx]; DBUG_ENTER("check_quick_select"); @@ -7490,8 +7490,13 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only, *mrr_flags |= HA_MRR_USE_DEFAULT_IMPL; *bufsize= param->thd->variables.mrr_buff_size; - rows= file->multi_range_read_info_const(keynr, &seq_if, (void*)&seq, 0, - bufsize, mrr_flags, cost); + /* + Skip materialized derived table/view result table from MRR check as + they aren't contain any data yet. + */ + if (param->table->pos_in_table_list->is_non_derived()) + rows= file->multi_range_read_info_const(keynr, &seq_if, (void*)&seq, 0, + bufsize, mrr_flags, cost); if (rows != HA_POS_ERROR) { param->table->quick_rows[keynr]=rows; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 1855224440c..3cbde7b4bc8 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -154,9 +154,9 @@ int check_and_do_in_subquery_rewrites(JOIN *join) !join->having && !select_lex->with_sum_func && // 4 thd->thd_marker.emb_on_expr_nest && // 5 select_lex->outer_select()->join && // 6 - select_lex->master_unit()->first_select()->leaf_tables && // 7 + select_lex->master_unit()->first_select()->leaf_tables.elements && // 7 in_subs->exec_method == Item_in_subselect::NOT_TRANSFORMED && // 8 - select_lex->outer_select()->leaf_tables && // 9 + select_lex->outer_select()->leaf_tables.elements && // 9 !((join->select_options | // 10 select_lex->outer_select()->join->select_options) // 10 & SELECT_STRAIGHT_JOIN)) // 10 @@ -212,9 +212,9 @@ int check_and_do_in_subquery_rewrites(JOIN *join) if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && in_subs && // 1 !select_lex->is_part_of_union() && // 2 - select_lex->master_unit()->first_select()->leaf_tables && // 3 + select_lex->master_unit()->first_select()->leaf_tables.elements && // 3 thd->lex->sql_command == SQLCOM_SELECT && // * - select_lex->outer_select()->leaf_tables && // 3A + select_lex->outer_select()->leaf_tables.elements && // 3A subquery_types_allow_materialization(in_subs) && // psergey-todo: duplicated_subselect_card_check: where it's done? (in_subs->is_top_level_item() || @@ -391,11 +391,26 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) Item_in_subselect **in_subq; Item_in_subselect **in_subq_end; THD *thd= join->thd; + TABLE_LIST *tbl; + List_iterator ti(join->select_lex->leaf_tables); DBUG_ENTER("convert_join_subqueries_to_semijoins"); if (join->sj_subselects.elements() == 0) DBUG_RETURN(FALSE); + for (in_subq= join->sj_subselects.front(), + in_subq_end= join->sj_subselects.back(); + in_subq != in_subq_end; + in_subq++) + { + SELECT_LEX *subq_sel= (*in_subq)->get_select_lex(); + if (subq_sel->handle_derived(thd->lex, DT_OPTIMIZE)) + DBUG_RETURN(1); + if (subq_sel->handle_derived(thd->lex, DT_MERGE)) + DBUG_RETURN(TRUE); + subq_sel->update_used_tables(); + } + /* First, convert child join's subqueries. We proceed bottom-up here */ for (in_subq= join->sj_subselects.front(), in_subq_end= join->sj_subselects.back(); @@ -422,11 +437,12 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) // Temporary measure: disable semi-joins when they are together with outer // joins. - for (TABLE_LIST *tbl= join->select_lex->leaf_tables; tbl; tbl=tbl->next_leaf) + while ((tbl= ti++)) { TABLE_LIST *embedding= tbl->embedding; - if (tbl->on_expr || (tbl->embedding && !(embedding->sj_on_expr && - !embedding->embedding))) + if (tbl->on_expr || + (embedding && embedding->outer_join && + !(embedding->sj_on_expr && !embedding->embedding))) { in_subq= join->sj_subselects.front(); arena= thd->activate_stmt_arena_if_needed(&backup); @@ -737,7 +753,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) st_select_lex *subq_lex= subq_pred->unit->first_select(); nested_join->join_list.empty(); List_iterator_fast li(subq_lex->top_join_list); - TABLE_LIST *tl, *last_leaf; + TABLE_LIST *tl; while ((tl= li++)) { tl->embedding= sj_nest; @@ -752,17 +768,15 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) NOTE: We actually insert them at the front! That's because the order is reversed in this list. */ - for (tl= parent_lex->leaf_tables; tl->next_leaf; tl= tl->next_leaf) ; - tl->next_leaf= subq_lex->leaf_tables; - last_leaf= tl; + parent_lex->leaf_tables.concat(&subq_lex->leaf_tables); /* Same as above for next_local chain (a theory: a next_local chain always starts with ::leaf_tables because view's tables are inserted after the view) */ - for (tl= parent_lex->leaf_tables; tl->next_local; tl= tl->next_local) ; - tl->next_local= subq_lex->leaf_tables; + for (tl= parent_lex->leaf_tables.head(); tl->next_local; tl= tl->next_local) ; + tl->next_local= subq_lex->leaf_tables.head(); /* A theory: no need to re-connect the next_global chain */ @@ -776,7 +790,8 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) /* n. Adjust the parent_join->tables counter */ uint table_no= parent_join->tables; /* n. Walk through child's tables and adjust table->map */ - for (tl= subq_lex->leaf_tables; tl; tl= tl->next_leaf, table_no++) + List_iterator_fast si(subq_lex->leaf_tables); + while ((tl= si++)) { tl->table->tablenr= table_no; tl->table->map= ((table_map)1) << table_no; @@ -786,6 +801,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) emb && emb->select_lex == old_sl; emb= emb->embedding) emb->select_lex= parent_join->select_lex; + table_no++; } parent_join->tables += subq_lex->join->tables; @@ -872,7 +888,8 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) { /* Inject into the WHERE */ parent_join->conds= and_items(parent_join->conds, sj_nest->sj_on_expr); - parent_join->conds->fix_fields(parent_join->thd, &parent_join->conds); + if (!parent_join->conds->fixed) + parent_join->conds->fix_fields(parent_join->thd, &parent_join->conds); parent_join->select_lex->where= parent_join->conds; } @@ -1424,6 +1441,7 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, TABLE_LIST *emb_sj_nest; POSITION *pos= join->positions + idx; remaining_tables &= ~new_join_tab->table->map; + bool disable_jbuf= join->thd->variables.join_cache_level == 0; pos->prefix_cost.convert_from_cost(*current_read_time); pos->prefix_record_count= *current_record_count; @@ -1593,7 +1611,8 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, optimize_wo_join_buffering(join, pos->first_loosescan_table, idx, remaining_tables, TRUE, //first_alt - pos->first_loosescan_table + n_tables, + disable_jbuf ? join->tables : + pos->first_loosescan_table + n_tables, &reopt_rec_count, &reopt_cost, &sj_inner_fanout); /* @@ -1734,8 +1753,8 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, /* Need to re-run best-access-path as we prefix_rec_count has changed */ for (i= first_tab + mat_info->tables; i <= idx; i++) { - best_access_path(join, join->positions[i].table, rem_tables, i, FALSE, - prefix_rec_count, &curpos, &dummy); + best_access_path(join, join->positions[i].table, rem_tables, i, + disable_jbuf, prefix_rec_count, &curpos, &dummy); prefix_rec_count *= curpos.records_read; prefix_cost += curpos.read_time; } @@ -2031,6 +2050,7 @@ at_sjmat_pos(const JOIN *join, table_map remaining_tables, const JOIN_TAB *tab, void fix_semijoin_strategies_for_picked_join_order(JOIN *join) { uint table_count=join->tables; + bool disable_jbuf= join->thd->variables.join_cache_level == 0; uint tablenr; table_map remaining_tables= 0; table_map handled_tabs= 0; @@ -2092,8 +2112,9 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join) join->cur_sj_inner_tables= 0; for (i= first + sjm->tables; i <= tablenr; i++) { - best_access_path(join, join->best_positions[i].table, rem_tables, i, FALSE, - prefix_rec_count, join->best_positions + i, &dummy); + best_access_path(join, join->best_positions[i].table, rem_tables, i, + disable_jbuf, prefix_rec_count, + join->best_positions + i, &dummy); prefix_rec_count *= join->best_positions[i].records_read; rem_tables &= ~join->best_positions[i].table->table->map; } diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index cefb507a61e..370fa9a6904 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -74,10 +74,12 @@ static int maxmin_in_range(bool max_fl, Field* field, COND *cond); # Multiplication of number of rows in all tables */ -static ulonglong get_exact_record_count(TABLE_LIST *tables) +static ulonglong get_exact_record_count(List &tables) { ulonglong count= 1; - for (TABLE_LIST *tl= tables; tl; tl= tl->next_leaf) + TABLE_LIST *tl; + List_iterator ti(tables); + while ((tl= ti++)) { ha_rows tmp= tl->table->file->records(); if ((tmp == HA_POS_ERROR)) @@ -110,9 +112,11 @@ static ulonglong get_exact_record_count(TABLE_LIST *tables) HA_ERR_... if a deadlock or a lock wait timeout happens, for example */ -int opt_sum_query(TABLE_LIST *tables, List &all_fields,COND *conds) +int opt_sum_query(List &tables, List &all_fields,COND *conds) { List_iterator_fast it(all_fields); + List_iterator ti(tables); + TABLE_LIST *tl; int const_result= 1; bool recalc_const_item= 0; ulonglong count= 1; @@ -120,7 +124,7 @@ int opt_sum_query(TABLE_LIST *tables, List &all_fields,COND *conds) table_map removed_tables= 0, outer_tables= 0, used_tables= 0; table_map where_tables= 0; Item *item; - int error; + int error= 0; if (conds) where_tables= conds->used_tables(); @@ -129,7 +133,7 @@ int opt_sum_query(TABLE_LIST *tables, List &all_fields,COND *conds) Analyze outer join dependencies, and, if possible, compute the number of returned rows. */ - for (TABLE_LIST *tl= tables; tl; tl= tl->next_leaf) + while ((tl= ti++)) { TABLE_LIST *embedded; for (embedded= tl ; embedded; embedded= embedded->embedding) @@ -170,6 +174,14 @@ int opt_sum_query(TABLE_LIST *tables, List &all_fields,COND *conds) is_exact_count= FALSE; count= 1; // ensure count != 0 } + else if (tl->is_materialized_derived()) + { + /* + Can't remove a derived table as it's number of rows is just an + estimate. + */ + return 0; + } else { error= tl->table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); diff --git a/sql/records.cc b/sql/records.cc index e2a1ea9b4af..79739c86740 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -286,7 +286,8 @@ void end_read_record(READ_RECORD *info) if (info->table) { filesort_free_buffers(info->table,0); - (void) info->file->extra(HA_EXTRA_NO_CACHE); + if (info->table->created) + (void) info->file->extra(HA_EXTRA_NO_CACHE); if (info->read_record != rr_quick) // otherwise quick_range does it (void) info->file->ha_index_or_rnd_end(); info->table=0; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index e042475d295..617d79ffa23 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2821,6 +2821,9 @@ int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables) result= -1; else result= 0; + /* Prepare all derived tables/views to catch possible errors. */ + if (!result) + result= mysql_handle_derived(thd->lex, DT_PREPARE) ? -1 : 0; return result; } diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 2ae6831429e..23bbb8bc155 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3003,7 +3003,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, class LEX_COLUMN *column; List_iterator column_iter(columns); - if (open_and_lock_tables(thd, table_list)) + if (open_and_lock_tables(thd, table_list) || + mysql_handle_derived(thd->lex, DT_PREPARE)) DBUG_RETURN(TRUE); while ((column = column_iter++)) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 84aa162241b..e9121eb9e45 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2998,6 +2998,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->fulltext_searched= 0; table->file->ft_handler= 0; table->reginfo.impossible_range= 0; + table->created= TRUE; /* Catch wrong handling of the auto_increment_field_not_null. */ DBUG_ASSERT(!table->auto_increment_field_not_null); table->auto_increment_field_not_null= FALSE; @@ -5044,9 +5045,10 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived) close_tables_for_reopen(thd, &tables); } if (derived && - (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || - (thd->fill_derived_tables() && - mysql_handle_derived(thd->lex, &mysql_derived_filling)))) + (mysql_handle_derived(thd->lex, DT_INIT))) + DBUG_RETURN(TRUE); /* purecov: inspected */ + if (thd->prepare_derived_at_open && derived && + (mysql_handle_derived(thd->lex, DT_PREPARE))) DBUG_RETURN(TRUE); /* purecov: inspected */ DBUG_RETURN(0); } @@ -5062,6 +5064,7 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived) flags - bitmap of flags to modify how the tables will be open: MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has done a flush or namelock on it. + dt_phases - set of flags to pass to the mysql_handle_derived RETURN FALSE - ok @@ -5072,13 +5075,14 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived) data from the tables. */ -bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags) +bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags, + uint dt_phases) { uint counter; DBUG_ENTER("open_normal_and_derived_tables"); DBUG_ASSERT(!thd->fill_derived_tables()); if (open_tables(thd, &tables, &counter, flags) || - mysql_handle_derived(thd->lex, &mysql_derived_prepare)) + mysql_handle_derived(thd->lex, dt_phases)) DBUG_RETURN(TRUE); /* purecov: inspected */ DBUG_RETURN(0); } @@ -5714,9 +5718,7 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list, Field_iterator_view field_it; field_it.set(table_list); Query_arena *arena= 0, backup; - - DBUG_ASSERT(table_list->schema_table_reformed || - (ref != 0 && table_list->view != 0)); + for (; !field_it.end_of_fields(); field_it.next()) { if (!my_strcasecmp(system_charset_info, field_it.name(), name)) @@ -5735,6 +5737,8 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list, if (!item) DBUG_RETURN(0); + if (!ref) + DBUG_RETURN((Field*) view_ref_found); /* *ref != NULL means that *ref contains the item that we need to replace. If the item was aliased by the user, set the alias to @@ -6134,6 +6138,8 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, Field *field_to_set= NULL; if (fld == view_ref_found) { + if (!ref) + DBUG_RETURN(fld); Item *it= (*ref)->real_item(); if (it->type() == Item::FIELD_ITEM) field_to_set= ((Item_field*)it)->field; @@ -6141,6 +6147,8 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, { if (thd->mark_used_columns == MARK_COLUMNS_READ) it->walk(&Item::register_field_in_read_map, 1, (uchar *) 0); + else + it->walk(&Item::register_field_in_write_map, 1, (uchar *) 0); } } else @@ -6280,7 +6288,9 @@ find_field_in_tables(THD *thd, Item_ident *item, find_field_in_table even in the case of information schema tables when table_ref->field_translation != NULL. */ - if (table_ref->table && !table_ref->view) + if (table_ref->table && + (!table_ref->is_merged_derived() || + (!table_ref->is_multitable() && table_ref->merged_for_insert))) found= find_field_in_table(thd, table_ref->table, name, length, TRUE, &(item->cached_field_index)); else @@ -6298,7 +6308,8 @@ find_field_in_tables(THD *thd, Item_ident *item, Only views fields should be marked as dependent, not an underlying fields. */ - if (!table_ref->belong_to_view) + if (!table_ref->belong_to_view && + !table_ref->belong_to_derived) { SELECT_LEX *current_sel= thd->lex->current_select; SELECT_LEX *last_select= table_ref->select_lex; @@ -6884,6 +6895,10 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, */ if (nj_col_2 && (!using_fields ||is_using_column_1)) { + /* + Create non-fixed fully qualified field and let fix_fields to + resolve it. + */ Item *item_1= nj_col_1->create_item(thd); Item *item_2= nj_col_2->create_item(thd); Field *field_1= nj_col_1->field(); @@ -7548,27 +7563,36 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, make_leaves_list() list pointer to pointer on list first element tables table list + full_table_list whether to include tables from mergeable derived table/view. + we need them for checks for INSERT/UPDATE statements only. RETURN pointer on pointer to next_leaf of last element */ -TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables) +void make_leaves_list(List &list, TABLE_LIST *tables, + bool full_table_list, TABLE_LIST *boundary) + { for (TABLE_LIST *table= tables; table; table= table->next_local) { - if (table->merge_underlying_list) + if (table == boundary) + full_table_list= !full_table_list; + if (full_table_list && table->is_merged_derived()) { - DBUG_ASSERT(table->view && - table->effective_algorithm == VIEW_ALGORITHM_MERGE); - list= make_leaves_list(list, table->merge_underlying_list); + SELECT_LEX *select_lex= table->get_single_select(); + /* + It's safe to use select_lex->leaf_tables because all derived + tables/views were already prepared and has their leaf_tables + set properly. + */ + make_leaves_list(list, select_lex->get_table_list(), + full_table_list, boundary); } else { - *list= table; - list= &table->next_leaf; + list.push_back(table); } } - return list; } /* @@ -7583,6 +7607,7 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables) leaves List of join table leaves list (select_lex->leaf_tables) refresh It is onle refresh for subquery select_insert It is SELECT ... INSERT command + full_table_list a parameter to pass to the make_leaves_list function NOTE Check also that the 'used keys' and 'ignored keys' exists and set up the @@ -7601,9 +7626,13 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables) bool setup_tables(THD *thd, Name_resolution_context *context, List *from_clause, TABLE_LIST *tables, - TABLE_LIST **leaves, bool select_insert) + List &leaves, bool select_insert, + bool full_table_list) { uint tablenr= 0; + List_iterator ti(leaves); + TABLE_LIST *table_list; + DBUG_ENTER("setup_tables"); DBUG_ASSERT ((select_insert && !tables->next_name_resolution_table) || !tables || @@ -7615,40 +7644,57 @@ bool setup_tables(THD *thd, Name_resolution_context *context, TABLE_LIST *first_select_table= (select_insert ? tables->next_local: 0); - if (!(*leaves)) - make_leaves_list(leaves, tables); - - TABLE_LIST *table_list; - for (table_list= *leaves; - table_list; - table_list= table_list->next_leaf, tablenr++) + SELECT_LEX *select_lex= select_insert ? &thd->lex->select_lex : + thd->lex->current_select; + if (select_lex->first_cond_optimization) { - TABLE *table= table_list->table; - table->pos_in_table_list= table_list; - if (first_select_table && - table_list->top_table() == first_select_table) + leaves.empty(); + select_lex->leaf_tables_exec.empty(); + make_leaves_list(leaves, tables, full_table_list, first_select_table); + + while ((table_list= ti++)) { - /* new counting for SELECT of INSERT ... SELECT command */ - first_select_table= 0; - tablenr= 0; + TABLE *table= table_list->table; + table->pos_in_table_list= table_list; + if (first_select_table && + table_list->top_table() == first_select_table) + { + /* new counting for SELECT of INSERT ... SELECT command */ + first_select_table= 0; + thd->lex->select_lex.insert_tables= tablenr; + tablenr= 0; + } + setup_table_map(table, table_list, tablenr); + if (table_list->process_index_hints(table)) + DBUG_RETURN(1); + tablenr++; } - setup_table_map(table, table_list, tablenr); - if (table_list->process_index_hints(table)) + if (tablenr > MAX_TABLES) + { + my_error(ER_TOO_MANY_TABLES,MYF(0),MAX_TABLES); DBUG_RETURN(1); + } } - if (tablenr > MAX_TABLES) - { - my_error(ER_TOO_MANY_TABLES,MYF(0),MAX_TABLES); - DBUG_RETURN(1); - } + else + { + List_iterator_fast ti(select_lex->leaf_tables_exec); + select_lex->leaf_tables.empty(); + while ((table_list= ti++)) + { + table_list->table->tablenr= table_list->tablenr_exec; + table_list->table->map= table_list->map_exec; + table_list->table->pos_in_table_list= table_list; + select_lex->leaf_tables.push_back(table_list); + } + } + for (table_list= tables; table_list; table_list= table_list->next_local) { if (table_list->merge_underlying_list) { - DBUG_ASSERT(table_list->view && - table_list->effective_algorithm == VIEW_ALGORITHM_MERGE); + DBUG_ASSERT(table_list->is_merged_derived()); Query_arena *arena= thd->stmt_arena, backup; bool res; if (arena->is_conventional()) @@ -7675,7 +7721,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, prepare tables and check access for the view tables SYNOPSIS - setup_tables_and_check_view_access() + setup_tables_and_check_access() thd Thread handler context name resolution contest to setup table list there from_clause Top-level list of table references in the FROM clause @@ -7685,6 +7731,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, refresh It is onle refresh for subquery select_insert It is SELECT ... INSERT command want_access what access is needed + full_table_list a parameter to pass to the make_leaves_list function NOTE a wrapper for check_tables that will also check the resulting @@ -7698,33 +7745,32 @@ bool setup_tables_and_check_access(THD *thd, Name_resolution_context *context, List *from_clause, TABLE_LIST *tables, - TABLE_LIST **leaves, + List &leaves, bool select_insert, ulong want_access_first, - ulong want_access) + ulong want_access, + bool full_table_list) { - TABLE_LIST *leaves_tmp= NULL; bool first_table= true; if (setup_tables(thd, context, from_clause, tables, - &leaves_tmp, select_insert)) + leaves, select_insert, full_table_list)) return TRUE; - if (leaves) - *leaves= leaves_tmp; - - for (; leaves_tmp; leaves_tmp= leaves_tmp->next_leaf) + List_iterator ti(leaves); + TABLE_LIST *table_list; + while((table_list= ti++)) { - if (leaves_tmp->belong_to_view && + if (table_list->belong_to_view && check_single_table_access(thd, first_table ? want_access_first : - want_access, leaves_tmp, FALSE)) + want_access, table_list, FALSE)) { tables->hide_view_error(thd); return TRUE; } first_table= 0; } - return FALSE; + return FALSE; } @@ -7860,8 +7906,8 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, information_schema table, or a nested table reference. See the comment for TABLE_LIST. */ - if (!((table && !tables->view && (table->grant.privilege & SELECT_ACL)) || - (tables->view && (tables->grant.privilege & SELECT_ACL))) && + if (!(table && tables->is_non_derived() && (table->grant.privilege & SELECT_ACL) || + (!tables->is_non_derived() && (tables->grant.privilege & SELECT_ACL))) && !any_privileges) { field_iterator.set(tables); @@ -7891,7 +7937,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if (!(item= field_iterator.create_item(thd))) DBUG_RETURN(TRUE); - DBUG_ASSERT(item->fixed); +// DBUG_ASSERT(item->fixed); /* cache the table for the Item_fields inserted by expanding stars */ if (item->type() == Item::FIELD_ITEM && tables->cacheable_table) ((Item_field *)item)->cached_table= tables; @@ -8021,13 +8067,14 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, FALSE if all is OK */ -int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, +int setup_conds(THD *thd, TABLE_LIST *tables, List &leaves, COND **conds) { SELECT_LEX *select_lex= thd->lex->current_select; Query_arena *arena= thd->stmt_arena, backup; TABLE_LIST *table= NULL; // For HP compilers TABLE_LIST *save_emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; + List_iterator ti(leaves); /* it_is_update set to TRUE when tables of primary SELECT_LEX (SELECT_LEX which belong to LEX, i.e. most up SELECT) will be updated by @@ -8039,9 +8086,15 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, bool it_is_update= (select_lex == &thd->lex->select_lex) && thd->lex->which_check_option_applicable(); bool save_is_item_list_lookup= select_lex->is_item_list_lookup; - select_lex->is_item_list_lookup= 0; + TABLE_LIST *derived= select_lex->master_unit()->derived; DBUG_ENTER("setup_conds"); + /* Do not fix conditions for the derived tables that have been merged */ + if (derived && derived->merged) + DBUG_RETURN(0); + + select_lex->is_item_list_lookup= 0; + if (select_lex->conds_processed_with_permanent_arena || arena->is_conventional()) arena= 0; // For easier test @@ -8054,7 +8107,10 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, for (table= tables; table; table= table->next_local) { - if (table->prepare_where(thd, conds, FALSE)) + if (select_lex == &thd->lex->select_lex && + select_lex->first_cond_optimization && + table->merged_for_insert && + table->prepare_where(thd, conds, FALSE)) goto err_no_arena; } @@ -8072,7 +8128,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, Apply fix_fields() to all ON clauses at all levels of nesting, including the ones inside view definitions. */ - for (table= leaves; table; table= table->next_leaf) + while ((table= ti++)) { TABLE_LIST *embedded; /* The table at the current level of nesting. */ TABLE_LIST *embedding= table; /* The parent nested table reference. */ @@ -9283,6 +9339,27 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup) thd->restore_backup_open_tables_state(backup); } + +/** + @brief + Remove 'fixed' flag from items in a list + + @param items list of items to un-fix + + @details + This function sets to 0 the 'fixed' flag for items in the 'items' list. + It's needed to force correct marking of views' fields for INSERT/UPDATE + statements. +*/ + +void unfix_fields(List &fields) +{ + List_iterator li(fields); + Item *item; + while ((item= li++)) + item->fixed= 0; +} + /** @} (end of group Data_Dictionary) */ diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h index e07806a56ab..775742b1b4f 100644 --- a/sql/sql_bitmap.h +++ b/sql/sql_bitmap.h @@ -91,6 +91,10 @@ public: DBUG_ASSERT(sizeof(buffer) >= 4); return (ulonglong) uint4korr(buffer); } + uint bits_set() + { + return bitmap_bits_set(&map); + } }; /* An iterator to quickly walk over bits in unlonglong bitmap. */ @@ -169,5 +173,16 @@ public: public: Iterator(Bitmap<64> &bmp) : Table_map_iterator(bmp.map) {} }; + uint bits_set() + { + //TODO: use my_count_bits() + uint res= 0, i= 0; + for (; i < 64 ; i++) + { + if (map & ((ulonglong)1<table->s->table_name.str, - tables_used->table->s->db.str, - tables_used->table->s->db_type()->db_type)); if (tables_used->derived) { + DBUG_PRINT("qcache", ("table: %s", tables_used->alias)); table_count--; DBUG_PRINT("qcache", ("derived table skipped")); continue; } + DBUG_PRINT("qcache", ("table: %s db: %s type: %u", + tables_used->table->s->table_name.str, + tables_used->table->s->db.str, + tables_used->table->s->db_type()->db_type)); *tables_type|= tables_used->table->file->table_cache_type(); /* diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 769e78ab86b..b4d3637a9f2 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -771,6 +771,7 @@ THD::THD() thr_lock_owner_init(&main_lock_id, &lock_info); m_internal_handler= NULL; + prepare_derived_at_open= FALSE; } @@ -2946,7 +2947,8 @@ bool select_materialize_with_stats:: create_result_table(THD *thd_arg, List *column_types, bool is_union_distinct, ulonglong options, - const char *table_alias, bool bit_fields_as_long) + const char *table_alias, bool bit_fields_as_long, + bool create_table) { DBUG_ASSERT(table == 0); tmp_table_param.field_count= column_types->elements; diff --git a/sql/sql_class.h b/sql/sql_class.h index 4388f26c0ce..5bb52c74750 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1474,6 +1474,9 @@ public: */ TABLE_LIST *emb_on_expr_nest; } thd_marker; + + bool prepare_derived_at_open; + #ifndef MYSQL_CLIENT int binlog_setup_trx_data(); @@ -2810,12 +2813,12 @@ public: class select_union :public select_result_interceptor { -protected: - TMP_TABLE_PARAM tmp_table_param; public: + TMP_TABLE_PARAM tmp_table_param; TABLE *table; + ha_rows records; - select_union() :table(0) { tmp_table_param.init(); } + select_union() :table(0), records(0) { tmp_table_param.init(); } int prepare(List &list, SELECT_LEX_UNIT *u); bool send_data(List &items); bool send_eof(); @@ -2823,7 +2826,9 @@ public: virtual bool create_result_table(THD *thd, List *column_types, bool is_distinct, ulonglong options, - const char *alias, bool bit_fields_as_long); + const char *alias, + bool bit_fields_as_long, + bool create_table); }; /* Base subselect interface class */ @@ -2885,9 +2890,11 @@ protected: public: select_materialize_with_stats() {} - virtual bool create_result_table(THD *thd, List *column_types, - bool is_distinct, ulonglong options, - const char *alias, bool bit_fields_as_long); + bool create_result_table(THD *thd, List *column_types, + bool is_distinct, ulonglong options, + const char *alias, + bool bit_fields_as_long, + bool create_table); bool init_result_table(ulonglong select_options); bool send_data(List &items); void cleanup() @@ -3175,7 +3182,7 @@ public: class multi_update :public select_result_interceptor { TABLE_LIST *all_tables; /* query/update command tables */ - TABLE_LIST *leaves; /* list of leves of join table tree */ + List *leaves; /* list of leves of join table tree */ TABLE_LIST *update_tables, *table_being_updated; TABLE **tmp_tables, *main_table, *table_to_update; TMP_TABLE_PARAM *tmp_table_param; @@ -3201,7 +3208,7 @@ class multi_update :public select_result_interceptor bool error_handled; public: - multi_update(TABLE_LIST *ut, TABLE_LIST *leaves_list, + multi_update(TABLE_LIST *ut, List *leaves_list, List *fields, List *values, enum_duplicates handle_duplicates, bool ignore); ~multi_update(); diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index ea667543040..8a342098506 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -715,8 +715,8 @@ bool Select_materialize::send_fields(List &list, uint flags) DBUG_ASSERT(table == 0); if (create_result_table(unit->thd, unit->get_unit_column_types(), FALSE, thd->options | TMP_TABLE_ALL_COLUMNS, "", - FALSE)) - return TRUE; + FALSE, TRUE)) + return TRUE; materialized_cursor= new (&table->mem_root) Materialized_cursor(result, table); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 4fc618ca050..aadd8d77596 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -58,10 +58,18 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (open_and_lock_tables(thd, table_list)) DBUG_RETURN(TRUE); - if (!(table= table_list->table)) + + if (mysql_handle_list_of_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT) || + mysql_handle_list_of_derived(thd->lex, table_list, DT_PREPARE)) + DBUG_RETURN(TRUE); + + if (!(table= table_list->table) || !table->created) { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - table_list->view_db.str, table_list->view_name.str); + if (!table_list->updatable) + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE"); + else + my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), + table_list->view_db.str, table_list->view_name.str); DBUG_RETURN(TRUE); } thd_proc_info(thd, "init"); @@ -70,6 +78,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (mysql_prepare_delete(thd, table_list, &conds)) DBUG_RETURN(TRUE); + if (thd->lex->current_select->first_cond_optimization) + { + thd->lex->current_select->save_leaf_tables(thd); + thd->lex->current_select->first_cond_optimization= 0; + } /* check ORDER BY even if it can be ignored */ if (order && order->elements) { @@ -384,6 +397,12 @@ cleanup: query_cache_invalidate3(thd, table_list, 1); } + if (thd->lex->current_select->first_cond_optimization) + { + thd->lex->current_select->save_leaf_tables(thd); + thd->lex->current_select->first_cond_optimization= 0; + } + delete select; transactional_table= table->file->has_transactions(); @@ -481,8 +500,8 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, &thd->lex->select_lex.top_join_list, table_list, - &select_lex->leaf_tables, FALSE, - DELETE_ACL, SELECT_ACL) || + select_lex->leaf_tables, FALSE, + DELETE_ACL, SELECT_ACL, TRUE) || setup_conds(thd, table_list, select_lex->leaf_tables, conds) || setup_ftfuncs(select_lex)) DBUG_RETURN(TRUE); @@ -540,6 +559,11 @@ int mysql_multi_delete_prepare(THD *thd) TABLE_LIST *target_tbl; DBUG_ENTER("mysql_multi_delete_prepare"); + TABLE_LIST *tables= lex->query_tables; + if (mysql_handle_derived(lex, DT_INIT) || + mysql_handle_list_of_derived(lex, tables, DT_MERGE_FOR_INSERT) || + mysql_handle_list_of_derived(lex, tables, DT_PREPARE)) + DBUG_RETURN(TRUE); /* setup_tables() need for VIEWs. JOIN::prepare() will not do it second time. @@ -549,8 +573,8 @@ int mysql_multi_delete_prepare(THD *thd) if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, &thd->lex->select_lex.top_join_list, lex->query_tables, - &lex->select_lex.leaf_tables, FALSE, - DELETE_ACL, SELECT_ACL)) + lex->select_lex.leaf_tables, FALSE, + DELETE_ACL, SELECT_ACL, TRUE)) DBUG_RETURN(TRUE); @@ -564,16 +588,13 @@ int mysql_multi_delete_prepare(THD *thd) target_tbl; target_tbl= target_tbl->next_local) { + if (!(target_tbl->table= target_tbl->correspondent_table->table)) { - DBUG_ASSERT(target_tbl->correspondent_table->view && - target_tbl->correspondent_table->merge_underlying_list && - target_tbl->correspondent_table->merge_underlying_list-> - next_local); - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - target_tbl->correspondent_table->view_db.str, - target_tbl->correspondent_table->view_name.str); - DBUG_RETURN(TRUE); + my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), + target_tbl->correspondent_table->view_db.str, + target_tbl->correspondent_table->view_name.str); + DBUG_RETURN(TRUE); } if (!target_tbl->correspondent_table->updatable || @@ -623,6 +644,12 @@ multi_delete::prepare(List &values, SELECT_LEX_UNIT *u) unit= u; do_delete= 1; thd_proc_info(thd, "deleting from main table"); + SELECT_LEX *select_lex= u->first_select(); + if (select_lex->first_cond_optimization) + { + if (select_lex->handle_derived(thd->lex, DT_MERGE)) + DBUG_RETURN(TRUE); + } DBUG_RETURN(0); } diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 22240d3a300..ff56f5291bc 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -23,38 +23,79 @@ #include "mysql_priv.h" #include "sql_select.h" +typedef bool (*dt_processor)(THD *thd, LEX *lex, TABLE_LIST *derived); +bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived); +bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived); +bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived); +bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived); +bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived); +bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived); +bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived); +bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived); + + +dt_processor processors[]= +{ + &mysql_derived_init, + &mysql_derived_prepare, + &mysql_derived_optimize, + &mysql_derived_merge, + &mysql_derived_merge_for_insert, + &mysql_derived_create, + &mysql_derived_fill, + &mysql_derived_reinit, +}; /* - Call given derived table processor (preparing or filling tables) + @brief + Run specified phases on all derived tables/views in given LEX. - SYNOPSIS - mysql_handle_derived() - lex LEX for this thread - processor procedure of derived table processing + @param lex LEX for this thread + @param phases phases to run derived tables/views through - RETURN - FALSE OK - TRUE Error + @return FALSE OK + @return TRUE Error */ - bool -mysql_handle_derived(LEX *lex, bool (*processor)(THD*, LEX*, TABLE_LIST*)) +mysql_handle_derived(LEX *lex, uint phases) { bool res= FALSE; - if (lex->derived_tables) + THD *thd= lex->thd; + if (!lex->derived_tables) + return FALSE; + + lex->thd->derived_tables_processing= TRUE; + + for (uint phase= 0; phase < DT_PHASES && !res; phase++) { - lex->thd->derived_tables_processing= TRUE; + uint phase_flag= DT_INIT << phase; + if (phase_flag > phases) + break; + if (!(phases & phase_flag)) + continue; + if (phase_flag >= DT_CREATE && !thd->fill_derived_tables()) + break; + for (SELECT_LEX *sl= lex->all_selects_list; - sl; + sl && !res; sl= sl->next_select_in_list()) { for (TABLE_LIST *cursor= sl->get_table_list(); - cursor; + cursor && !res; cursor= cursor->next_local) { - if ((res= (*processor)(lex->thd, lex, cursor))) - goto out; + uint8 allowed_phases= (cursor->is_merged_derived() ? DT_PHASES_MERGE : + DT_PHASES_MATERIALIZE); + /* + Skip derived tables to which the phase isn't applicable. + TODO: mark derived at the parse time, later set it's type + (merged or materialized) + */ + if ((phase_flag != DT_PREPARE && !(allowed_phases & phase_flag)) || + (cursor->merged_for_insert && phase_flag != DT_REINIT)) + continue; + res= (*processors[phase])(lex->thd, lex, cursor); } if (lex->describe) { @@ -67,30 +108,454 @@ mysql_handle_derived(LEX *lex, bool (*processor)(THD*, LEX*, TABLE_LIST*)) } } } -out: + lex->thd->derived_tables_processing= FALSE; + return res; +} + +/* + @brief + Run through phases for the given derived table/view. + + @param lex LEX for this thread + @param derived the derived table to handle + @param phase_map phases to process tables/views through + + @details + + This function process the derived table (view) 'derived' to performs all + actions that are to be done on the table at the phases specified by + phase_map. The processing is carried out starting from the actions + performed at the earlier phases (those having smaller ordinal numbers). + + @note + This function runs specified phases of the derived tables handling on the + given derived table/view. This function is used in the chain of calls: + SELECT_LEX::handle_derived -> + TABLE_LIST::handle_derived -> + mysql_handle_single_derived + This chain of calls implements the bottom-up handling of the derived tables: + i.e. most inner derived tables/views are handled first. This order is + required for the all phases except the merge and the create steps. + For the sake of code simplicity this order is kept for all phases. + + @return FALSE ok + @return TRUE error +*/ + +bool +mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases) +{ + bool res= FALSE; + THD *thd= lex->thd; + uint8 allowed_phases= (derived->is_merged_derived() ? DT_PHASES_MERGE : + DT_PHASES_MATERIALIZE); + if (!lex->derived_tables) + return FALSE; + + lex->thd->derived_tables_processing= TRUE; + + for (uint phase= 0; phase < DT_PHASES; phase++) + { + uint phase_flag= DT_INIT << phase; + if (phase_flag > phases) + break; + if (!(phases & phase_flag)) + continue; + /* Skip derived tables to which the phase isn't applicable. */ + if (phase_flag != DT_PREPARE && + !(allowed_phases & phase_flag)) + continue; + if (phase_flag >= DT_CREATE && !thd->fill_derived_tables()) + break; + + if ((res= (*processors[phase])(lex->thd, lex, derived))) + break; + } lex->thd->derived_tables_processing= FALSE; return res; } /** - @brief Create temporary table structure (but do not fill it). + @brief + Run specified phases for derived tables/views in the given list - @param thd Thread handle - @param lex LEX for this thread - @param orig_table_list TABLE_LIST for the upper SELECT + @param lex LEX for this thread + @param table_list list of derived tables/view to handle + @param phase_map phases to process tables/views through - @details + @details + This function runs phases specified by the 'phases_map' on derived + tables/views found in the 'dt_list' with help of the + TABLE_LIST::handle_derived function. + 'lex' is passed as an argument to the TABLE_LIST::handle_derived. - This function is called before any command containing derived tables is - executed. Currently the function is used for derived tables, i.e. + @return FALSE ok + @return TRUE error +*/ - - Anonymous derived tables, or - - Named derived tables (aka views) with the @c TEMPTABLE algorithm. - - The table reference, contained in @c orig_table_list, is updated with the +bool +mysql_handle_list_of_derived(LEX *lex, TABLE_LIST *table_list, uint phases) +{ + for (TABLE_LIST *tl= table_list; tl; tl= tl->next_local) + { + if (tl->is_view_or_derived() && + tl->handle_derived(lex, phases)) + return TRUE; + } + return FALSE; +} + + +/** + @brief + Merge a derived table/view into the embedding select + + @param thd thread handle + @param lex LEX of the embedding query. + @param derived reference to the derived table. + + @details + This function merges the given derived table / view into the parent select + construction. Any derived table/reference to view occurred in the FROM + clause of the embedding select is represented by a TABLE_LIST structure a + pointer to which is passed to the function as in the parameter 'derived'. + This structure contains the number/map, alias, a link to SELECT_LEX of the + derived table and other info. If the 'derived' table is used in a nested join + then additionally the structure contains a reference to the ON expression + for this join. + + The merge process results in elimination of the derived table (or the + reference to a view) such that: + - the FROM list of the derived table/view is wrapped into a nested join + after which the nest is added to the FROM list of the embedding select + - the WHERE condition of the derived table (view) is ANDed with the ON + condition attached to the table. + + @note + Tables are merged into the leaf_tables list, original derived table is removed + from this list also. SELECT_LEX::table_list list is left untouched. + Where expression is merged with derived table's on_expr and can be found after + the merge through the SELECT_LEX::table_list. + + Examples of the derived table/view merge: + + Schema: + Tables: t1(f1), t2(f2), t3(f3) + View v1: SELECT f1 FROM t1 WHERE f1 < 1 + + Example with a view: + Before merge: + + The query (Q1): SELECT f1,f2 FROM t2 LEFT JOIN v1 ON f1 = f2 + + (LEX of the main query) + | + (select_lex) + | + (FROM table list) + | + (join list)= t2, v1 + / \ + / (on_expr)= (f1 = f2) + | + (LEX of the v1 view) + | + (select_lex)= SELECT f1 FROM t1 WHERE f1 < 1 + + + After merge: + + The rewritten query Q1 (Q1'): + SELECT f1,f2 FROM t2 LEFT JOIN (t1) ON ((f1 = f2) and (f1 < 1)) + + (LEX of the main query) + | + (select_lex) + | + (FROM table list) + | + (join list)= t2, (t1) + \ + (on_expr)= (f1 = f2) and (f1 < 1) + + In this example table numbers are assigned as follows: + (outer select): t2 - 1, v1 - 2 + (inner select): t1 - 1 + After the merge table numbers will be: + (outer select): t2 - 1, t1 - 2 + + Example with a derived table: + The query Q2: + SELECT f1,f2 + FROM (SELECT f1 FROM t1, t3 WHERE f1=f3 and f1 < 1) tt, t2 + WHERE f1 = f2 + + Before merge: + (LEX of the main query) + | + (select_lex) + / \ + (FROM table list) (WHERE clause)= (f1 = f2) + | + (join list)= tt, t2 + / \ + / (on_expr)= (empty) + / + (select_lex)= SELECT f1 FROM t1, t3 WHERE f1 = f3 and f1 < 1 + + After merge: + + The rewritten query Q2 (Q2'): + SELECT f1,f2 + FROM (t1, t3) JOIN t2 ON (f1 = f3 and f1 < 1) + WHERE f1 = f2 + + (LEX of the main query) + | + (select_lex) + / \ + (FROM table list) (WHERE clause)= (f1 = f2) + | + (join list)= t2, (t1, t3) + \ + (on_expr)= (f1 = f3 and f1 < 1) + + In this example table numbers are assigned as follows: + (outer select): tt - 1, t2 - 2 + (inner select): t1 - 1, t3 - 2 + After the merge table numbers will be: + (outer select): t1 - 1, t2 - 2, t3 - 3 + + @return FALSE if derived table/view were successfully merged. + @return TRUE if an error occur. +*/ + +bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) +{ + bool res= FALSE; + SELECT_LEX *dt_select= derived->get_single_select(); + table_map map; + uint tablenr; + SELECT_LEX *parent_lex= derived->select_lex; + Query_arena *arena, backup; + + if (derived->merged) + return FALSE; + + arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test + derived->merged= TRUE; + /* + Check whether there is enough free bits in table map to merge subquery. + If not - materialize it. This check isn't cached so when there is a big + and small subqueries, and the bigger one can't be merged it wouldn't + block the smaller one. + */ + if (parent_lex->get_free_table_map(&map, &tablenr)) + { + /* There is no enough table bits, fall back to materialization. */ + derived->change_refs_to_fields(); + derived->set_materialized_derived(); + goto exit_merge; + } + + if (dt_select->leaf_tables.elements + tablenr > MAX_TABLES) + { + /* There is no enough table bits, fall back to materialization. */ + derived->change_refs_to_fields(); + derived->set_materialized_derived(); + goto exit_merge; + } + + if (dt_select->options & OPTION_SCHEMA_TABLE) + parent_lex->options |= OPTION_SCHEMA_TABLE; + + parent_lex->cond_count+= dt_select->cond_count; + + if (!derived->get_unit()->prepared) + { + dt_select->leaf_tables.empty(); + make_leaves_list(dt_select->leaf_tables, derived, TRUE, 0); + } + + if (!derived->merged_for_insert) + { derived->nested_join= (NESTED_JOIN*) thd->calloc(sizeof(NESTED_JOIN)); + if (!derived->nested_join) + { + res= TRUE; + goto exit_merge; + } + + /* Merge derived table's subquery in the parent select. */ + if (parent_lex->merge_subquery(derived, dt_select, tablenr, map)) + { + res= TRUE; + goto exit_merge; + } + + /* + exclude select lex so it doesn't show up in explain. + do this only for derived table as for views this is already done. + + From sql_view.cc + Add subqueries units to SELECT into which we merging current view. + unit(->next)* chain starts with subqueries that are used by this + view and continues with subqueries that are used by other views. + We must not add any subquery twice (otherwise we'll form a loop), + to do this we remember in end_unit the first subquery that has + been already added. + */ + derived->get_unit()->exclude_level(); + if (parent_lex->join) + parent_lex->join->tables+= dt_select->join->tables - 1; + } + if (derived->get_unit()->prepared) + { + Item *expr= derived->on_expr; + expr= and_conds(expr, dt_select->join ? dt_select->join->conds : 0); + if (expr && (derived->prep_on_expr || expr != derived->on_expr)) + { + derived->on_expr= expr; + derived->prep_on_expr= expr->copy_andor_structure(thd); + } + if (derived->on_expr && + ((!derived->on_expr->fixed && + derived->on_expr->fix_fields(thd, &derived->on_expr)) || + derived->on_expr->check_cols(1))) + { + res= TRUE; /* purecov: inspected */ + goto exit_merge; + } + // Update used tables cache according to new table map + if (derived->on_expr) + derived->on_expr->update_used_tables(); + } + +exit_merge: + if (arena) + thd->restore_active_arena(arena, &backup); + return res; +} + + +/** + @brief + Merge a view for the embedding INSERT/UPDATE/DELETE + + @param thd thread handle + @param lex LEX of the embedding query. + @param derived reference to the derived table. + + @details + This function substitutes the derived table for the first table from + the query of the derived table thus making it a correct target table for the + INSERT/UPDATE/DELETE statements. As this operation is correct only for + single table views only, for multi table views this function does nothing. + The derived parameter isn't checked to be a view as derived tables aren't + allowed for INSERT/UPDATE/DELETE statements. + + @return FALSE if derived table/view were successfully merged. + @return TRUE if an error occur. +*/ + +bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived) +{ + SELECT_LEX *dt_select= derived->get_single_select(); + + if (derived->merged_for_insert) + return FALSE; + if (!derived->is_multitable()) + { + TABLE_LIST *tl=((TABLE_LIST*)dt_select->table_list.first); + TABLE *table= tl->table; + /* preserve old map & tablenr. */ + if (!derived->merged_for_insert && derived->table) + table->set_table_map(derived->table->map, derived->table->tablenr); + + derived->table= table; + derived->schema_table= + ((TABLE_LIST*)dt_select->table_list.first)->schema_table; + if (!derived->merged) + { + Query_arena *arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test + derived->select_lex->leaf_tables.push_back(tl); + derived->nested_join= (NESTED_JOIN*) thd->calloc(sizeof(NESTED_JOIN)); + if (derived->nested_join) + { + derived->wrap_into_nested_join(tl->select_lex->top_join_list); + derived->get_unit()->exclude_level(); + } + if (arena) + thd->restore_active_arena(arena, &backup); + derived->merged= TRUE; + if (!derived->nested_join) + return TRUE; + } + } + else + { + if (!derived->merged_for_insert && mysql_derived_merge(thd, lex, derived)) + return TRUE; + } + derived->merged_for_insert= TRUE; + + return FALSE; +} + + +/* + @brief + Initialize a derived table/view + + @param thd Thread handle + @param lex LEX of the embedding query. + @param derived reference to the derived table. + + @detail + Fill info about derived table/view without preparing an + underlying select. Such as: create a field translation for views, mark it as + a multitable if it is and so on. + + @return + false OK + true Error +*/ + + +bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived) +{ + SELECT_LEX_UNIT *unit= derived->get_unit(); + DBUG_ENTER("mysql_derived_init"); + + // Skip already prepared views/DT + if (!unit || unit->prepared) + DBUG_RETURN(FALSE); + + DBUG_RETURN(derived->init_derived(thd, TRUE)); +} + + +/* + @brief + Create temporary table structure (but do not fill it) + + @param thd Thread handle + @param lex LEX of the embedding query. + @param derived reference to the derived table. + + @detail + Prepare underlying select for a derived table/view. To properly resolve + names in the embedding query the TABLE structure is created. Actual table + is created later by the mysql_derived_create function. + + This function is called before any command containing derived table + is executed. All types of derived tables are handled by this function: + - Anonymous derived tables, or + - Named derived tables (aka views). + + The table reference, contained in @c derived, is updated with the fields of a new temporary table. - Derived tables are stored in @c thd->derived_tables and closed by close_thread_tables(). @@ -114,202 +579,359 @@ out: the state of privilege checking (GRANT_INFO struct) is copied as-is to the temporary table. - This function implements a signature called "derived table processor", and - is passed as a function pointer to mysql_handle_derived(). + Only the TABLE structure is created here, actual table is created by the + mysql_derived_create function. @note This function sets @c SELECT_ACL for @c TEMPTABLE views as well as anonymous derived tables, but this is ok since later access checking will distinguish between them. - @see mysql_handle_derived(), mysql_derived_filling(), GRANT_INFO + @see mysql_handle_derived(), mysql_derived_fill(), GRANT_INFO @return false OK true Error */ -bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list) +bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) { - SELECT_LEX_UNIT *unit= orig_table_list->derived; - ulonglong create_options; + SELECT_LEX_UNIT *unit= derived->get_unit(); DBUG_ENTER("mysql_derived_prepare"); bool res= FALSE; - if (unit) + + // Skip already prepared views/DT + if (!unit || unit->prepared) + DBUG_RETURN(FALSE); + + /* It's a target view for an INSERT, create field translation only. */ + if (derived->merged_for_insert) { - SELECT_LEX *first_select= unit->first_select(); - TABLE *table= 0; - select_union *derived_result; + res= derived->create_field_translation(thd); + DBUG_RETURN(res); + } - /* prevent name resolving out of derived table */ - for (SELECT_LEX *sl= first_select; sl; sl= sl->next_select()) - sl->context.outer_context= 0; + Query_arena *arena= thd->stmt_arena, backup; + if (arena->is_conventional()) + arena= 0; // For easier test + else + thd->set_n_backup_active_arena(arena, &backup); - if (!(derived_result= new select_union)) - DBUG_RETURN(TRUE); // out of memory + SELECT_LEX *first_select= unit->first_select(); - // st_select_lex_unit::prepare correctly work for single select - if ((res= unit->prepare(thd, derived_result, 0))) - goto exit; + /* prevent name resolving out of derived table */ + for (SELECT_LEX *sl= first_select; sl; sl= sl->next_select()) + { + sl->context.outer_context= 0; + // Prepare underlying views/DT first. + sl->handle_derived(lex, DT_PREPARE); + } - if ((res= check_duplicate_names(unit->types, 0))) - goto exit; + unit->derived= derived; - create_options= (first_select->options | thd->options | - TMP_TABLE_ALL_COLUMNS); - /* - Temp table is created so that it hounours if UNION without ALL is to be - processed + if (!(derived->derived_result= new select_union)) + DBUG_RETURN(TRUE); // out of memory - As 'distinct' parameter we always pass FALSE (0), because underlying - query will control distinct condition by itself. Correct test of - distinct underlying query will be is_union && - !unit->union_distinct->next_select() (i.e. it is union and last distinct - SELECT is last SELECT of UNION). - */ - if ((res= derived_result->create_result_table(thd, &unit->types, FALSE, - create_options, - orig_table_list->alias, - FALSE))) - goto exit; + // st_select_lex_unit::prepare correctly work for single select + if ((res= unit->prepare(thd, derived->derived_result, 0))) + goto exit; - table= derived_result->table; + if ((res= check_duplicate_names(unit->types, 0))) + goto exit; + + /* + Check whether we can merge this derived table into main select. + Depending on the result field translation will or will not + be created. + */ + if (derived->init_derived(thd, FALSE)) + goto exit; + + /* + Temp table is created so that it hounours if UNION without ALL is to be + processed + + As 'distinct' parameter we always pass FALSE (0), because underlying + query will control distinct condition by itself. Correct test of + distinct underlying query will be is_union && + !unit->union_distinct->next_select() (i.e. it is union and last distinct + SELECT is last SELECT of UNION). + */ + if (derived->derived_result->create_result_table(thd, &unit->types, FALSE, + (first_select->options | + thd->options | + TMP_TABLE_ALL_COLUMNS), + derived->alias, + FALSE, FALSE)) + goto exit; + + derived->table= derived->derived_result->table; + if (derived->is_derived() && derived->is_merged_derived()) + first_select->mark_as_belong_to_derived(derived); exit: - /* Hide "Unknown column" or "Unknown function" error */ - if (orig_table_list->view) - { - if (thd->is_error() && + /* Hide "Unknown column" or "Unknown function" error */ + if (derived->view) + { + if (thd->is_error() && (thd->main_da.sql_errno() == ER_BAD_FIELD_ERROR || - thd->main_da.sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION || - thd->main_da.sql_errno() == ER_SP_DOES_NOT_EXIST)) - { - thd->clear_error(); - my_error(ER_VIEW_INVALID, MYF(0), orig_table_list->db, - orig_table_list->table_name); - } - } - - /* - if it is preparation PS only or commands that need only VIEW structure - then we do not need real data and we can skip execution (and parameters - is not defined, too) - */ - if (res) + thd->main_da.sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION || + thd->main_da.sql_errno() == ER_SP_DOES_NOT_EXIST)) { - if (table) - free_tmp_table(thd, table); - delete derived_result; - } - else - { - if (!thd->fill_derived_tables()) - { - delete derived_result; - derived_result= NULL; - } - orig_table_list->derived_result= derived_result; - orig_table_list->table= table; - orig_table_list->table_name= table->s->table_name.str; - orig_table_list->table_name_length= table->s->table_name.length; - table->derived_select_number= first_select->select_number; - table->s->tmp_table= NON_TRANSACTIONAL_TMP_TABLE; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - if (orig_table_list->referencing_view) - table->grant= orig_table_list->grant; - else - table->grant.privilege= SELECT_ACL; -#endif - orig_table_list->db= (char *)""; - orig_table_list->db_length= 0; - // Force read of table stats in the optimizer - table->file->info(HA_STATUS_VARIABLE); - /* Add new temporary table to list of open derived tables */ - table->next= thd->derived_tables; - thd->derived_tables= table; + thd->clear_error(); + my_error(ER_VIEW_INVALID, MYF(0), derived->db, + derived->table_name); } } - else if (orig_table_list->merge_underlying_list) - orig_table_list->set_underlying_merge(); + + /* + if it is preparation PS only or commands that need only VIEW structure + then we do not need real data and we can skip execution (and parameters + is not defined, too) + */ + if (res) + { + if (derived->table) + free_tmp_table(thd, derived->table); + delete derived->derived_result; + } + else + { + TABLE *table= derived->table; + table->derived_select_number= first_select->select_number; + table->s->tmp_table= NON_TRANSACTIONAL_TMP_TABLE; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (derived->referencing_view) + table->grant= derived->grant; + else + { + table->grant.privilege= SELECT_ACL; + if (derived->is_derived()) + derived->grant.privilege= SELECT_ACL; + } +#endif + /* Add new temporary table to list of open derived tables */ + table->next= thd->derived_tables; + thd->derived_tables= table; + } + if (arena) + thd->restore_active_arena(arena, &backup); DBUG_RETURN(res); } -/* - fill derived table +/** + @brief + Runs optimize phase for a derived table/view. - SYNOPSIS - mysql_derived_filling() - thd Thread handle - lex LEX for this thread - unit node that contains all SELECT's for derived tables - orig_table_list TABLE_LIST for the upper SELECT + @param thd thread handle + @param lex LEX of the embedding query. + @param derived reference to the derived table. - IMPLEMENTATION - Derived table is resolved with temporary table. It is created based on the - queries defined. After temporary table is filled, if this is not EXPLAIN, - then the entire unit / node is deleted. unit is deleted if UNION is used - for derived table and node is deleted is it is a simple SELECT. - If you use this function, make sure it's not called at prepare. - Due to evaluation of LIMIT clause it can not be used at prepared stage. + @details + Runs optimize phase for given 'derived' derived table/view. + If optimizer finds out that it's of the type "SELECT a_constant" then this + functions also materializes it. - RETURN - FALSE OK - TRUE Error + @return FALSE ok. + @return TRUE if an error occur. */ -bool mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list) +bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived) { - TABLE *table= orig_table_list->table; - SELECT_LEX_UNIT *unit= orig_table_list->derived; + SELECT_LEX_UNIT *unit= derived->get_unit(); + SELECT_LEX *first_select= unit->first_select(); + SELECT_LEX *save_current_select= lex->current_select; + bool res= FALSE; - /*check that table creation pass without problem and it is derived table */ - if (table && unit) + if (unit->optimized && !unit->uncacheable && !unit->describe) + return FALSE; + lex->current_select= first_select; + + if (unit->is_union()) { - SELECT_LEX *first_select= unit->first_select(); - select_union *derived_result= orig_table_list->derived_result; - SELECT_LEX *save_current_select= lex->current_select; - if (unit->is_union()) - { - // execute union without clean up - res= unit->exec(); - } - else - { - unit->set_limit(first_select); - if (unit->select_limit_cnt == HA_POS_ERROR) - first_select->options&= ~OPTION_FOUND_ROWS; - - lex->current_select= first_select; - res= mysql_select(thd, &first_select->ref_pointer_array, - (TABLE_LIST*) first_select->table_list.first, - first_select->with_wild, - first_select->item_list, first_select->where, - (first_select->order_list.elements+ - first_select->group_list.elements), - (ORDER *) first_select->order_list.first, - (ORDER *) first_select->group_list.first, - first_select->having, (ORDER*) NULL, - (first_select->options | thd->options | - SELECT_NO_UNLOCK), - derived_result, unit, first_select); - } - - if (!res) - { - /* - Here we entirely fix both TABLE_LIST and list of SELECT's as - there were no derived tables - */ - if (derived_result->flush()) - res= TRUE; - - if (!lex->describe) - unit->cleanup(); - } - else - unit->cleanup(); - lex->current_select= save_current_select; + // optimize union without execution + res= unit->optimize(); } + else if (unit->derived) + { + if (!derived->is_merged_derived()) + { + unit->optimized= TRUE; + if ((res= first_select->join->optimize())) + goto err; + } + } + /* + Materialize derived tables/views of the "SELECT a_constant" type. + Such tables should be materialized at the optimization phase for + correct constant evaluation. + */ + if (!res && derived->fill_me && !derived->merged_for_insert) + { + if (derived->is_merged_derived()) + { + derived->change_refs_to_fields(); + derived->set_materialized_derived(); + } + if ((res= mysql_derived_create(thd, lex, derived))) + goto err; + if ((res= mysql_derived_fill(thd, lex, derived))) + goto err; + } +err: + lex->current_select= save_current_select; return res; } + + +/** + @brief + Actually create result table for a materialized derived table/view. + + @param thd thread handle + @param lex LEX of the embedding query. + @param derived reference to the derived table. + + @details + This function actually creates the result table for given 'derived' + table/view, but it doesn't fill it. + 'thd' and 'lex' parameters are not used by this function. + + @return FALSE ok. + @return TRUE if an error occur. +*/ + +bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived) +{ + TABLE *table= derived->table; + SELECT_LEX_UNIT *unit= derived->get_unit(); + + if (table->created) + return FALSE; + select_union *result= (select_union*)unit->result; + if (table->s->db_type() == TMP_ENGINE_HTON) + { + if (create_internal_tmp_table(table, result->tmp_table_param.keyinfo, + result->tmp_table_param.start_recinfo, + &result->tmp_table_param.recinfo, + (unit->first_select()->options | + thd->options | TMP_TABLE_ALL_COLUMNS))) + return(TRUE); + } + if (open_tmp_table(table)) + return TRUE; + table->file->extra(HA_EXTRA_WRITE_CACHE); + table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + return FALSE; +} + + +/* + @brief + Execute subquery of a materialized derived table/view and fill the result + table. + + @param thd Thread handle + @param lex LEX for this thread + @param derived reference to the derived table. + + @details + Execute subquery of given 'derived' table/view and fill the result + table. After result table is filled, if this is not the EXPLAIN statement, + the entire unit / node is deleted. unit is deleted if UNION is used + for derived table and node is deleted is it is a simple SELECT. + 'lex' is unused and 'thd' is passed as an argument to an underlying function. + + @note + If you use this function, make sure it's not called at prepare. + Due to evaluation of LIMIT clause it can not be used at prepared stage. + + @return FALSE OK + @return TRUE Error +*/ + +bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) +{ + TABLE *table= derived->table; + SELECT_LEX_UNIT *unit= derived->get_unit(); + bool res= FALSE; + + if (unit->executed && !unit->uncacheable && !unit->describe) + return FALSE; + /*check that table creation passed without problems. */ + DBUG_ASSERT(table && table->created); + SELECT_LEX *first_select= unit->first_select(); + select_union *derived_result= derived->derived_result; + SELECT_LEX *save_current_select= lex->current_select; + if (unit->is_union()) + { + // execute union without clean up + res= unit->exec(); + } + else + { + unit->set_limit(first_select); + if (unit->select_limit_cnt == HA_POS_ERROR) + first_select->options&= ~OPTION_FOUND_ROWS; + + lex->current_select= first_select; + res= mysql_select(thd, &first_select->ref_pointer_array, + (TABLE_LIST*) first_select->table_list.first, + first_select->with_wild, + first_select->item_list, first_select->where, + (first_select->order_list.elements+ + first_select->group_list.elements), + (ORDER *) first_select->order_list.first, + (ORDER *) first_select->group_list.first, + first_select->having, (ORDER*) NULL, + (first_select->options | thd->options | + SELECT_NO_UNLOCK), + derived_result, unit, first_select); + } + + if (!res) + { + if (derived_result->flush()) + res= TRUE; + unit->executed= TRUE; + } + if (res || !lex->describe) + unit->cleanup(); + lex->current_select= save_current_select; + + return res; +} + + +/** + @brief + Re-initialize given derived table/view for the next execution. + + @param thd thread handle + @param lex LEX for this thread + @param derived reference to the derived table. + + @details + Re-initialize given 'derived' table/view for the next execution. + All underlying views/derived tables are recursively reinitialized prior + to re-initialization of given derived table. + 'thd' and 'lex' are passed as arguments to called functions. + + @return FALSE OK + @return TRUE Error +*/ + +bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived) +{ + st_select_lex_unit *unit= derived->get_unit(); + + if (derived->table) + derived->merged_for_insert= FALSE; + unit->unclean(); + unit->types.empty(); + /* for derived tables & PS (which can't be reset by Item_subquery) */ + unit->reinit_exec_mechanism(); + unit->set_thd(thd); + return FALSE; +} diff --git a/sql/sql_help.cc b/sql/sql_help.cc index 5658a3578ab..97e7856eccb 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -628,7 +628,7 @@ bool mysqld_help(THD *thd, const char *mask) Protocol *protocol= thd->protocol; SQL_SELECT *select; st_find_field used_fields[array_elements(init_used_fields)]; - TABLE_LIST *leaves= 0; + List leaves; TABLE_LIST tables[4]; List topics_list, categories_list, subcategories_list; String name, description, example; @@ -667,7 +667,7 @@ bool mysqld_help(THD *thd, const char *mask) thd->lex->select_lex.context.first_name_resolution_table= &tables[0]; if (setup_tables(thd, &thd->lex->select_lex.context, &thd->lex->select_lex.top_join_list, - tables, &leaves, FALSE)) + tables, leaves, FALSE, FALSE)) goto error; memcpy((char*) used_fields, (char*) init_used_fields, sizeof(used_fields)); if (init_fields(thd, tables, used_fields, array_elements(used_fields))) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index afc86ef6d4f..16d74eb5b39 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -124,7 +124,7 @@ bool check_view_single_update(List &fields, List *values, { it.init(*values); while ((item= it++)) - tables|= item->used_tables(); + tables|= item->view_used_tables(view); } /* Convert to real table bits */ @@ -140,6 +140,11 @@ bool check_view_single_update(List &fields, List *values, if (view->check_single_table(&tbl, tables, view) || tbl == 0) goto error; + /* + A buffer for the insert values was allocated for the merged view. + Use it. + */ + //tbl->table->insert_values= view->table->insert_values; view->table= tbl->table; *map= tables; @@ -243,6 +248,10 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, */ table_list->next_local= 0; context->resolve_in_table_list_only(table_list); + /* 'Unfix' fields to allow correct marking by the setup_fields function. */ + if (table_list->is_view()) + unfix_fields(fields); + res= setup_fields(thd, 0, fields, MARK_COLUMNS_WRITE, 0, 0); /* Restore the current context. */ @@ -252,7 +261,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, if (res) return -1; - if (table_list->effective_algorithm == VIEW_ALGORITHM_MERGE) + if (table_list->is_view() && table_list->is_merged_derived()) { if (check_view_single_update(fields, fields_and_values_from_different_maps ? @@ -341,7 +350,8 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, if (setup_fields(thd, 0, update_fields, MARK_COLUMNS_WRITE, 0, 0)) return -1; - if (insert_table_list->effective_algorithm == VIEW_ALGORITHM_MERGE && + if (insert_table_list->is_view() && + insert_table_list->is_merged_derived() && check_view_single_update(update_fields, &update_values, insert_table_list, map)) return -1; @@ -641,6 +651,12 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, table_list->table_name); DBUG_RETURN(TRUE); } + /* + mark the table_list as a target for insert, to skip the DT/view prepare phase + for correct access rights checks + TODO: remove this hack + */ + table_list->skip_prepare_derived= TRUE; if (table_list->lock_type == TL_WRITE_DELAYED) { @@ -652,6 +668,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, if (open_and_lock_tables(thd, table_list)) DBUG_RETURN(TRUE); } + lock_type= table_list->lock_type; thd_proc_info(thd, "init"); @@ -1010,6 +1027,12 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, ::my_ok(thd, (ulong) thd->row_count_func, id, buff); } thd->abort_on_warning= 0; + if (thd->lex->current_select->first_cond_optimization) + { + thd->lex->current_select->save_leaf_tables(thd); + thd->lex->current_select->first_cond_optimization= 0; + } + DBUG_RETURN(FALSE); abort: @@ -1138,6 +1161,11 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list, bool insert_into_view= (table_list->view != 0); DBUG_ENTER("mysql_prepare_insert_check_table"); + if (!table_list->updatable) + { + my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT"); + DBUG_RETURN(TRUE); + } /* first table in list is the one we'll INSERT into, requires INSERT_ACL. all others require SELECT_ACL only. the ACL requirement below is for @@ -1148,14 +1176,16 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list, if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, &thd->lex->select_lex.top_join_list, table_list, - &thd->lex->select_lex.leaf_tables, - select_insert, INSERT_ACL, SELECT_ACL)) + thd->lex->select_lex.leaf_tables, + select_insert, INSERT_ACL, SELECT_ACL, + TRUE)) DBUG_RETURN(TRUE); if (insert_into_view && !fields.elements) { thd->lex->empty_field_list_on_rset= 1; - if (!table_list->table) + if (!thd->lex->select_lex.leaf_tables.head()->table || + table_list->is_multitable()) { my_error(ER_VIEW_NO_INSERT_FIELD_LIST, MYF(0), table_list->view_db.str, table_list->view_name.str); @@ -1246,6 +1276,12 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, /* INSERT should have a SELECT or VALUES clause */ DBUG_ASSERT (!select_insert || !values); + if (mysql_handle_derived(thd->lex, DT_INIT)) + DBUG_RETURN(TRUE); + if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(TRUE); + if (mysql_handle_list_of_derived(thd->lex, table_list, DT_PREPARE)) + DBUG_RETURN(TRUE); /* For subqueries in VALUES() we should not see the table in which we are inserting (for INSERT ... SELECT this is done by changing table_list, @@ -2913,9 +2949,9 @@ bool mysql_insert_select_prepare(THD *thd) { LEX *lex= thd->lex; SELECT_LEX *select_lex= &lex->select_lex; - TABLE_LIST *first_select_leaf_table; DBUG_ENTER("mysql_insert_select_prepare"); + /* Statement-based replication of INSERT ... SELECT ... LIMIT is not safe as order of rows is not defined, so in mixed mode we go to row-based. @@ -2941,21 +2977,37 @@ bool mysql_insert_select_prepare(THD *thd) &select_lex->where, TRUE, FALSE, FALSE)) DBUG_RETURN(TRUE); + DBUG_ASSERT(select_lex->leaf_tables.elements != 0); + List_iterator ti(select_lex->leaf_tables); + TABLE_LIST *table; + uint insert_tables; + + if (select_lex->first_cond_optimization) + { + /* Back up leaf_tables list. */ + Query_arena *arena= thd->stmt_arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test + + insert_tables= select_lex->insert_tables; + while ((table= ti++) && insert_tables--) + { + select_lex->leaf_tables_exec.push_back(table); + table->tablenr_exec= table->table->tablenr; + table->map_exec= table->table->map; + } + if (arena) + thd->restore_active_arena(arena, &backup); + } + ti.rewind(); /* exclude first table from leaf tables list, because it belong to INSERT */ - DBUG_ASSERT(select_lex->leaf_tables != 0); - lex->leaf_tables_insert= select_lex->leaf_tables; /* skip all leaf tables belonged to view where we are insert */ - for (first_select_leaf_table= select_lex->leaf_tables->next_leaf; - first_select_leaf_table && - first_select_leaf_table->belong_to_view && - first_select_leaf_table->belong_to_view == - lex->leaf_tables_insert->belong_to_view; - first_select_leaf_table= first_select_leaf_table->next_leaf) - {} - select_lex->leaf_tables= first_select_leaf_table; + insert_tables= select_lex->insert_tables; + while ((table= ti++) && insert_tables--) + ti.remove(); + DBUG_RETURN(FALSE); } @@ -3169,7 +3221,7 @@ void select_insert::cleanup() select_insert::~select_insert() { DBUG_ENTER("~select_insert"); - if (table) + if (table && table->created) { table->next_number_field=0; table->auto_increment_field_not_null= FALSE; diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index d88cc7a9f7f..5a6d0e41e94 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -2370,6 +2370,8 @@ JOIN_CACHE_BKA::init_join_matching_records(RANGE_SEQ_IF *seq_funcs, uint ranges) init_mrr_buff(); + if (!join_tab->preread_init_done && join_tab->preread_init()) + return NESTED_LOOP_ERROR; /* Prepare to iterate over keys from the join buffer and to get matching candidates obtained with MMR handler functions. diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index af2c33c51dd..f22255f0a25 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -23,6 +23,7 @@ #include #include "sp.h" #include "sp_head.h" +#include "sql_select.h" /* We are using pointer to this variable for distinguishing between assignment @@ -317,7 +318,6 @@ void lex_start(THD *thd) lex->derived_tables= 0; lex->lock_option= TL_READ; lex->safe_to_cache_query= 1; - lex->leaf_tables_insert= 0; lex->parsing_options.reset(); lex->empty_field_list_on_rset= 0; lex->select_lex.select_number= 1; @@ -1590,6 +1590,7 @@ void st_select_lex_unit::init_query() item_list.empty(); describe= 0; found_rows_for_union= 0; + derived= 0; } void st_select_lex::init_query() @@ -1598,7 +1599,8 @@ void st_select_lex::init_query() table_list.empty(); top_join_list.empty(); join_list= &top_join_list; - embedding= leaf_tables= 0; + embedding= 0; + leaf_tables.empty(); item_list.empty(); join= 0; having= prep_having= where= prep_where= 0; @@ -2060,9 +2062,27 @@ void st_select_lex::print_order(String *str, { if (order->counter_used) { - char buffer[20]; - size_t length= my_snprintf(buffer, 20, "%d", order->counter); - str->append(buffer, (uint) length); + if (query_type != QT_VIEW_INTERNAL) + { + char buffer[20]; + size_t length= my_snprintf(buffer, 20, "%d", order->counter); + str->append(buffer, (uint) length); + } + else + { + /* replace numeric reference with expression */ + if (order->item[0]->type() == Item::INT_ITEM && + order->item[0]->basic_const_item()) + { + char buffer[20]; + size_t length= my_snprintf(buffer, 20, "%d", order->counter); + str->append(buffer, (uint) length); + /* make it expression instead of integer constant */ + str->append(STRING_WITH_LEN("+0")); + } + else + (*order->item)->print(str, query_type); + } } else (*order->item)->print(str, query_type); @@ -2264,22 +2284,6 @@ bool st_lex::can_be_merged() /* find non VIEW subqueries/unions */ bool selects_allow_merge= select_lex.next_select() == 0; - if (selects_allow_merge) - { - for (SELECT_LEX_UNIT *tmp_unit= select_lex.first_inner_unit(); - tmp_unit; - tmp_unit= tmp_unit->next_unit()) - { - if (tmp_unit->first_select()->parent_lex == this && - (tmp_unit->item == 0 || - (tmp_unit->item->place() != IN_WHERE && - tmp_unit->item->place() != IN_ON))) - { - selects_allow_merge= 0; - break; - } - } - } return (selects_allow_merge && select_lex.group_list.elements == 0 && @@ -2909,7 +2913,11 @@ static void fix_prepare_info_in_table_list(THD *thd, TABLE_LIST *tbl) tbl->prep_on_expr= tbl->on_expr; tbl->on_expr= tbl->on_expr->copy_andor_structure(thd); } - fix_prepare_info_in_table_list(thd, tbl->merge_underlying_list); + if (tbl->is_view_or_derived() && tbl->is_merged_derived()) + { + SELECT_LEX *sel= tbl->get_single_select(); + fix_prepare_info_in_table_list(thd, sel->get_table_list()); + } } } @@ -3024,6 +3032,384 @@ bool st_select_lex::add_index_hint (THD *thd, char *str, uint length) str, length)); } + +/** + @brief Process all derived tables/views of the SELECT. + + @param lex LEX of this thread + @param phase phases to run derived tables/views through + + @details + This function runs specified 'phases' on all tables from the + table_list of this select. + + @return FALSE ok. + @return TRUE an error occur. +*/ + +bool st_select_lex::handle_derived(struct st_lex *lex, uint phases) +{ + for (TABLE_LIST *cursor= (TABLE_LIST*) table_list.first; + cursor; + cursor= cursor->next_local) + { + if (cursor->is_view_or_derived() && cursor->handle_derived(lex, phases)) + return TRUE; + } + return FALSE; +} + + +/** + @brief + Returns first unoccupied table map and table number + + @param map [out] return found map + @param tablenr [out] return found tablenr + + @details + Returns first unoccupied table map and table number in this select. + Map and table are returned in *'map' and *'tablenr' accordingly. + + @retrun TRUE no free table map/table number + @return FALSE found free table map/table number +*/ + +bool st_select_lex::get_free_table_map(table_map *map, uint *tablenr) +{ + *map= 0; + *tablenr= 0; + TABLE_LIST *tl; + if (!join) + { + (*map)= 1<<1; + (*tablenr)++; + return FALSE; + } + List_iterator ti(leaf_tables); + while ((tl= ti++)) + { + if (tl->table->map > *map) + *map= tl->table->map; + if (tl->table->tablenr > *tablenr) + *tablenr= tl->table->tablenr; + } + (*map)<<= 1; + (*tablenr)++; + if (*tablenr >= MAX_TABLES) + return TRUE; + return FALSE; +} + + +/** + @brief + Append given table to the leaf_tables list. + + @param link Offset to which list in table structure to use + @param table Table to append + + @details + Append given 'table' to the leaf_tables list using the 'link' offset. + If the 'table' is linked with other tables through next_leaf/next_local + chains then whole list will be appended. +*/ + +void st_select_lex::append_table_to_list(TABLE_LIST *TABLE_LIST::*link, + TABLE_LIST *table) +{ + TABLE_LIST *tl; + for (tl= leaf_tables.head(); tl->*link; tl= tl->*link); + tl->*link= table; +} + +/* + @brief + Remove given table from the leaf_tables list. + + @param link Offset to which list in table structure to use + @param table Table to remove + + @details + Remove 'table' from the leaf_tables list using the 'link' offset. +*/ + +void st_select_lex::remove_table_from_list(TABLE_LIST *table) +{ + TABLE_LIST *tl; + List_iterator ti(leaf_tables); + while ((tl= ti++)) + { + if (tl == table) + { + ti.remove(); + break; + } + } +} + + +/** + @brief + Assigns new table maps to tables in the leaf_tables list + + @param derived Derived table to take initial table map from + @param map table map to begin with + @param tablenr table number to begin with + @param parent_lex new parent select_lex + + @details + Assign new table maps/table numbers to all tables in the leaf_tables list. + 'map'/'tablenr' are used for the first table and shifted to left/ + increased for each consequent table in the leaf_tables list. + If the 'derived' table is given then it's table map/number is used for the + first table in the list and 'map'/'tablenr' are used for the second and + all consequent tables. + The 'parent_lex' is set as the new parent select_lex for all tables in the + list. +*/ + +void st_select_lex::remap_tables(TABLE_LIST *derived, table_map map, + uint tablenr, SELECT_LEX *parent_lex) +{ + bool first_table= TRUE; + TABLE_LIST *tl; + table_map first_map; + uint first_tablenr; + + if (derived && derived->table) + { + first_map= derived->table->map; + first_tablenr= derived->table->tablenr; + } + else + { + first_map= map; + map<<= 1; + first_tablenr= tablenr++; + } + /* + Assign table bit/table number. + To the first table of the subselect the table bit/tablenr of the + derived table is assigned. The rest of tables are getting bits + sequentially, starting from the provided table map/tablenr. + */ + List_iterator ti(leaf_tables); + while ((tl= ti++)) + { + if (first_table) + { + first_table= FALSE; + tl->table->set_table_map(first_map, first_tablenr); + } + else + { + tl->table->set_table_map(map, tablenr); + tablenr++; + map<<= 1; + } + SELECT_LEX *old_sl= tl->select_lex; + tl->select_lex= parent_lex; + for(TABLE_LIST *emb= tl->embedding; + emb && emb->select_lex == old_sl; + emb= emb->embedding) + emb->select_lex= parent_lex; + } +} + +/** + @brief + Merge a subquery into this select. + + @param derived derived table of the subquery to be merged + @param subq_select select_lex of the subquery + @param map table map for assigning to merged tables from subquery + @param table_no table number for assigning to merged tables from subquery + + @details + This function merges a subquery into its parent select. In short the + merge operation appends the subquery FROM table list to the parent's + FROM table list. In more details: + .) the top_join_list of the subquery is wrapped into a join_nest + and attached to 'derived' + .) subquery's leaf_tables list is merged with the leaf_tables + list of this select_lex + .) the table maps and table numbers of the tables merged from + the subquery are adjusted to reflect their new binding to + this select + + @return TRUE an error occur + @return FALSE ok +*/ + +bool SELECT_LEX::merge_subquery(TABLE_LIST *derived, SELECT_LEX *subq_select, + uint table_no, table_map map) +{ + derived->wrap_into_nested_join(subq_select->top_join_list); + /* Reconnect the next_leaf chain. */ + leaf_tables.concat(&subq_select->leaf_tables); + + ftfunc_list->concat(subq_select->ftfunc_list); + if (join) + { + Item_in_subselect **in_subq; + Item_in_subselect **in_subq_end; + for (in_subq= subq_select->join->sj_subselects.front(), + in_subq_end= subq_select->join->sj_subselects.back(); + in_subq != in_subq_end; + in_subq++) + { + join->sj_subselects.append(join->thd->mem_root, *in_subq); + (*in_subq)->emb_on_expr_nest= derived; + } + } + /* + Remove merged table from chain. + When merge_subquery is called at a subquery-to-semijoin transformation + the derived isn't in the leaf_tables list, so in this case the call of + remove_table_from_list does not cause any actions. + */ + remove_table_from_list(derived); + + /* Walk through child's tables and adjust table map, tablenr, + * parent_lex */ + subq_select->remap_tables(derived, map, table_no, this); + return FALSE; +} + + +/** + @brief + Mark tables from the leaf_tables list as belong to a derived table. + + @param derived tables will be marked as belonging to this derived + + @details + Run through the leaf_list and mark all tables as belonging to the 'derived'. +*/ + +void SELECT_LEX::mark_as_belong_to_derived(TABLE_LIST *derived) +{ + /* Mark tables as belonging to this DT */ + TABLE_LIST *tl; + List_iterator ti(leaf_tables); + while ((tl= ti++)) + { + tl->skip_temporary= 1; + tl->belong_to_derived= derived; + } +} + + +/** + @brief + Update used_tables cache for this select + + @details + This function updates used_tables cache of ON expressions of all tables + in the leaf_tables list and of the conds expression (if any). +*/ + +void SELECT_LEX::update_used_tables() +{ + TABLE_LIST *tl; + List_iterator ti(leaf_tables); + while ((tl= ti++)) + { + if (tl->on_expr) + { + tl->on_expr->update_used_tables(); + tl->on_expr->walk(&Item::eval_not_null_tables, 0, NULL); + } + TABLE_LIST *embedding= tl->embedding; + while (embedding) + { + if (embedding->on_expr && + embedding->nested_join->join_list.head() == tl) + { + embedding->on_expr->update_used_tables(); + embedding->on_expr->walk(&Item::eval_not_null_tables, 0, NULL); + } + tl= embedding; + embedding= tl->embedding; + } + } + if (join->conds) + { + join->conds->update_used_tables(); + join->conds->walk(&Item::eval_not_null_tables, 0, NULL); + } +} + +/** + @brief + Increase estimated number of records for a derived table/view + + @param records number of records to increase estimate by + + @details + This function increases estimated number of records by the 'records' + for the derived table to which this select belongs to. +*/ + +void SELECT_LEX::increase_derived_records(uint records) +{ + SELECT_LEX_UNIT *unit= master_unit(); + DBUG_ASSERT(unit->derived); + + select_union *result= (select_union*)unit->result; + result->records+= records; +} + + +/** + @brief + Mark select's derived table as a const one. + + @param empty Whether select has an empty result set + + @details + Mark derived table/view of this select as a constant one (to + materialize it at the optimization phase) unless this select belongs to a + union. Estimated number of rows is incremented if this select has non empty + result set. +*/ + +void SELECT_LEX::mark_const_derived(bool empty) +{ + TABLE_LIST *derived= master_unit()->derived; + if (!join->thd->lex->describe && derived) + { + if (!empty) + increase_derived_records(1); + if (!master_unit()->is_union() && !derived->is_merged_derived()) + derived->fill_me= TRUE; + } +} + +bool st_select_lex::save_leaf_tables(THD *thd) +{ + Query_arena *arena= thd->stmt_arena, backup; + if (arena->is_conventional()) + arena= 0; + else + thd->set_n_backup_active_arena(arena, &backup); + + List_iterator_fast li(leaf_tables); + TABLE_LIST *table; + while ((table= li++)) + { + if (leaf_tables_exec.push_back(table)) + return 1; + table->tablenr_exec= table->table->tablenr; + table->map_exec= table->table->map; + } + if (arena) + thd->restore_active_arena(arena, &backup); + + return 0; +} + /** A routine used by the parser to decide whether we are specifying a full partitioning or if only partitions to add or to split. diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 7cceb7dba16..975914ed183 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -469,6 +469,11 @@ public: friend bool mysql_new_select(struct st_lex *lex, bool move_down); friend bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, uint flags); + friend bool mysql_derived_prepare(THD *thd, st_lex *lex, + TABLE_LIST *orig_table_list); + friend bool mysql_derived_merge(THD *thd, st_lex *lex, + TABLE_LIST *orig_table_list); + friend bool TABLE_LIST::init_derived(THD *thd, bool init_view); private: void fast_exclude(); }; @@ -487,13 +492,12 @@ class st_select_lex_unit: public st_select_lex_node { protected: TABLE_LIST result_table_list; select_union *union_result; - TABLE *table; /* temporary table using for appending UNION results */ - - select_result *result; ulonglong found_rows_for_union; bool saved_error; public: + TABLE *table; /* temporary table using for appending UNION results */ + select_result *result; bool prepared, // prepare phase already performed for UNION (unit) optimized, // optimize phase already performed for UNION (unit) executed, // already executed @@ -520,6 +524,11 @@ public: ha_rows select_limit_cnt, offset_limit_cnt; /* not NULL if unit used in subselect, point to subselect item */ Item_subselect *item; + /* + TABLE_LIST representing this union in the embedding select. Used for + derived tables/views handling. + */ + TABLE_LIST *derived; /* thread handler */ THD *thd; /* @@ -549,6 +558,7 @@ public: /* UNION methods */ bool prepare(THD *thd, select_result *result, ulong additional_options); + bool optimize(); bool exec(); bool cleanup(); inline void unclean() { cleaned= 0; } @@ -610,8 +620,15 @@ public: Beginning of the list of leaves in a FROM clause, where the leaves inlcude all base tables including view tables. The tables are connected by TABLE_LIST::next_leaf, so leaf_tables points to the left-most leaf. + + List of all base tables local to a subquery including all view + tables. Unlike 'next_local', this in this list views are *not* + leaves. Created in setup_tables() -> make_leaves_list(). */ - TABLE_LIST *leaf_tables; + List leaf_tables; + List leaf_tables_exec; + uint insert_tables; + const char *type; /* type of select for EXPLAIN */ SQL_LIST order_list; /* ORDER clause */ @@ -832,6 +849,28 @@ public: void clear_index_hints(void) { index_hints= NULL; } bool is_part_of_union() { return master_unit()->is_union(); } + bool handle_derived(struct st_lex *lex, uint phases); + void append_table_to_list(TABLE_LIST *TABLE_LIST::*link, TABLE_LIST *table); + bool get_free_table_map(table_map *map, uint *tablenr); + void remove_table_from_list(TABLE_LIST *table); + void remap_tables(TABLE_LIST *derived, table_map map, + uint tablenr, st_select_lex *parent_lex); + bool merge_subquery(TABLE_LIST *derived, st_select_lex *subq_lex, + uint tablenr, table_map map); + inline bool is_mergeable() + { + return (next_select() == 0 && group_list.elements == 0 && + having == 0 && with_sum_func == 0 && + table_list.elements >= 1 && !(options & SELECT_DISTINCT) && + select_limit == 0); + } + void mark_as_belong_to_derived(TABLE_LIST *derived); + void increase_derived_records(uint records); + void update_used_tables(); + void mark_const_derived(bool empty); + + bool save_leaf_tables(THD *thd); + private: /* current index hint kind. used in filling up index_hints */ enum index_hint_type current_index_hint_type; @@ -1556,8 +1595,6 @@ typedef struct st_lex : public Query_tables_list CHARSET_INFO *charset; bool text_string_is_7bit; - /* store original leaf_tables for INSERT SELECT and PS/SP */ - TABLE_LIST *leaf_tables_insert; /** SELECT of CREATE VIEW statement */ LEX_STRING create_view_select; @@ -1673,7 +1710,7 @@ typedef struct st_lex : public Query_tables_list DERIVED_SUBQUERY and DERIVED_VIEW). */ uint8 derived_tables; - uint8 create_view_algorithm; + uint16 create_view_algorithm; uint8 create_view_check; bool drop_if_exists, drop_temporary, local_file, one_shot_set; bool autocommit; @@ -1836,6 +1873,8 @@ typedef struct st_lex : public Query_tables_list switch (sql_command) { case SQLCOM_UPDATE: case SQLCOM_UPDATE_MULTI: + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: case SQLCOM_INSERT: case SQLCOM_INSERT_SELECT: case SQLCOM_REPLACE: diff --git a/sql/sql_list.h b/sql/sql_list.h index 93cdd20c299..f1b71b00cf6 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -168,6 +168,11 @@ public: { if (!list->is_empty()) { + if (is_empty()) + { + *this= *list; + return; + } *last= list->first; last= list->last; elements+= list->elements; @@ -188,11 +193,13 @@ public: list_node *node= first; list_node *list_first= list->first; elements=0; - while (node && node != list_first) + while (node != list_first) { prev= &node->next; node= node->next; elements++; + if (node == &end_of_list) + return; } *prev= *last; last= prev; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 82cc8f81b4a..39d36b67f2c 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -164,12 +164,15 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (open_and_lock_tables(thd, table_list)) DBUG_RETURN(TRUE); + if (mysql_handle_single_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT) || + mysql_handle_single_derived(thd->lex, table_list, DT_PREPARE)) + DBUG_RETURN(TRUE); if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, &thd->lex->select_lex.top_join_list, table_list, - &thd->lex->select_lex.leaf_tables, FALSE, + thd->lex->select_lex.leaf_tables, FALSE, INSERT_ACL | UPDATE_ACL, - INSERT_ACL | UPDATE_ACL)) + INSERT_ACL | UPDATE_ACL, FALSE)) DBUG_RETURN(-1); if (!table_list->table || // do not suport join view !table_list->updatable || // and derived tables diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc index dccfcbaf8ac..069dc0d570c 100644 --- a/sql/sql_olap.cc +++ b/sql/sql_olap.cc @@ -154,7 +154,7 @@ int handle_olaps(LEX *lex, SELECT_LEX *select_lex) if (setup_tables(lex->thd, &select_lex->context, &select_lex->top_join_list, (TABLE_LIST *)select_lex->table_list.first - &select_lex->leaf_tables, FALSE) || + FALSE, FALSE) || setup_fields(lex->thd, 0, select_lex->item_list, MARK_COLUMNS_READ, &all_fields,1) || setup_fields(lex->thd, 0, item_list_copy, MARK_COLUMNS_READ, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 68c0a01ddff..6da6a9ae012 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -458,7 +458,7 @@ static void handle_bootstrap_impl(THD *thd) thd->init_for_queries(); while (fgets(buff, thd->net.max_packet, file)) { - char *query; + char *query, *res; /* strlen() can't be deleted because fgets() doesn't return length */ ulong length= (ulong) strlen(buff); while (buff[length-1] != '\n' && !feof(file)) @@ -2769,6 +2769,9 @@ mysql_execute_command(THD *thd) } } } + if (mysql_handle_single_derived(thd->lex, create_table, + DT_MERGE_FOR_INSERT)) + DBUG_RETURN(1); /* select_create is currently not re-execution friendly and @@ -3300,6 +3303,10 @@ end_with_restore_list: if (!(res= open_and_lock_tables(thd, all_tables))) { + /* + Only the INSERT table should be merged. Other will be handled by + select. + */ /* Skip first table, which is the table we are inserting in */ TABLE_LIST *second_table= first_table->next_local; select_lex->table_list.first= (uchar*) second_table; @@ -5183,6 +5190,8 @@ bool check_single_table_access(THD *thd, ulong privilege, /* Show only 1 table for check_grant */ if (!(all_tables->belong_to_view && (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) && + !(all_tables->is_view() && + all_tables->is_merged_derived()) && check_grant(thd, privilege, all_tables, 0, 1, no_errors)) goto deny; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 863e7a36aab..2580c8816fd 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1133,7 +1133,7 @@ static bool mysql_test_insert(Prepared_statement *stmt, If we would use locks, then we have to ensure we are not using TL_WRITE_DELAYED as having two such locks can cause table corruption. */ - if (open_normal_and_derived_tables(thd, table_list, 0)) + if (open_normal_and_derived_tables(thd, table_list, 0, DT_INIT)) goto error; if ((values= its++)) @@ -1217,7 +1217,10 @@ static int mysql_test_update(Prepared_statement *stmt, open_tables(thd, &table_list, &table_count, 0)) goto error; - if (table_list->multitable_view) + if (mysql_handle_derived(thd->lex, DT_INIT)) + goto error; + + if (table_list->is_multitable()) { DBUG_ASSERT(table_list->view != 0); DBUG_PRINT("info", ("Switch to multi-update")); @@ -1231,9 +1234,16 @@ static int mysql_test_update(Prepared_statement *stmt, thd->fill_derived_tables() is false here for sure (because it is preparation of PS, so we even do not check it). */ - if (mysql_handle_derived(thd->lex, &mysql_derived_prepare)) + if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT) || + table_list->handle_derived(thd->lex, DT_PREPARE)) goto error; + if (!table_list->updatable) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); + goto error; + } + #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Force privilege re-checking for views after they have been opened. */ want_privilege= (table_list->view ? UPDATE_ACL : @@ -1286,12 +1296,18 @@ error: static bool mysql_test_delete(Prepared_statement *stmt, TABLE_LIST *table_list) { + uint table_count= 0; THD *thd= stmt->thd; LEX *lex= stmt->lex; DBUG_ENTER("mysql_test_delete"); if (delete_precheck(thd, table_list) || - open_normal_and_derived_tables(thd, table_list, 0)) + open_tables(thd, &table_list, &table_count, 0)) + goto error; + + if (mysql_handle_derived(thd->lex, DT_INIT) || + mysql_handle_list_of_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT) || + mysql_handle_list_of_derived(thd->lex, table_list, DT_PREPARE)) goto error; if (!table_list->table) @@ -1349,7 +1365,8 @@ static int mysql_test_select(Prepared_statement *stmt, goto error; } - if (open_normal_and_derived_tables(thd, tables, 0)) + if (open_normal_and_derived_tables(thd, tables, 0, + DT_PREPARE | DT_CREATE)) goto error; thd->used_tables= 0; // Updated by setup_fields @@ -1410,7 +1427,8 @@ static bool mysql_test_do_fields(Prepared_statement *stmt, if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) DBUG_RETURN(TRUE); - if (open_normal_and_derived_tables(thd, tables, 0)) + if (open_normal_and_derived_tables(thd, tables, 0, + DT_PREPARE | DT_CREATE)) DBUG_RETURN(TRUE); DBUG_RETURN(setup_fields(thd, 0, *values, MARK_COLUMNS_NONE, 0, 0)); } @@ -1440,7 +1458,8 @@ static bool mysql_test_set_fields(Prepared_statement *stmt, if ((tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) || - open_normal_and_derived_tables(thd, tables, 0)) + open_normal_and_derived_tables(thd, tables, 0, + DT_PREPARE | DT_CREATE)) goto error; while ((var= it++)) @@ -1477,7 +1496,7 @@ static bool mysql_test_call_fields(Prepared_statement *stmt, if ((tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) || - open_normal_and_derived_tables(thd, tables, 0)) + open_normal_and_derived_tables(thd, tables, 0, DT_PREPARE)) goto err; while ((item= it++)) @@ -1552,6 +1571,7 @@ select_like_stmt_test_with_open(Prepared_statement *stmt, int (*specific_prepare)(THD *thd), ulong setup_tables_done_option) { + uint table_count= 0; DBUG_ENTER("select_like_stmt_test_with_open"); /* @@ -1560,7 +1580,8 @@ select_like_stmt_test_with_open(Prepared_statement *stmt, prepared EXPLAIN yet so derived tables will clean up after themself. */ - if (open_normal_and_derived_tables(stmt->thd, tables, 0)) + THD *thd= stmt->thd; + if (open_tables(thd, &tables, &table_count, 0)) DBUG_RETURN(TRUE); DBUG_RETURN(select_like_stmt_test(stmt, specific_prepare, @@ -1605,7 +1626,8 @@ static bool mysql_test_create_table(Prepared_statement *stmt) create_table->skip_temporary= true; } - if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0)) + if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0, + DT_PREPARE | DT_CREATE)) DBUG_RETURN(TRUE); if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) @@ -1623,7 +1645,8 @@ static bool mysql_test_create_table(Prepared_statement *stmt) we validate metadata of all CREATE TABLE statements, which keeps metadata validation code simple. */ - if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0)) + if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0, + DT_PREPARE)) DBUG_RETURN(TRUE); } @@ -1658,7 +1681,7 @@ static bool mysql_test_create_view(Prepared_statement *stmt) if (create_view_precheck(thd, tables, view, lex->create_view_mode)) goto err; - if (open_normal_and_derived_tables(thd, tables, 0)) + if (open_normal_and_derived_tables(thd, tables, 0, DT_PREPARE)) goto err; lex->view_prepare_mode= 1; @@ -2349,6 +2372,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) /* Fix ORDER list */ for (order= (ORDER *)sl->order_list.first; order; order= order->next) order->item= &order->item_ptr; + sl->handle_derived(lex, DT_REINIT); /* clear the no_error flag for INSERT/UPDATE IGNORE */ sl->no_error= FALSE; @@ -2392,9 +2416,6 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) } lex->current_select= &lex->select_lex; - /* restore original list used in INSERT ... SELECT */ - if (lex->leaf_tables_insert) - lex->select_lex.leaf_tables= lex->leaf_tables_insert; if (lex->result) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index bc81628f680..1d47ec8ef1d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -47,8 +47,8 @@ const char *join_type_str[]={ "UNKNOWN","system","const","eq_ref","ref", struct st_sargable_param; static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array); -static bool make_join_statistics(JOIN *join, TABLE_LIST *leaves, COND *conds, - DYNAMIC_ARRAY *keyuse); +static bool make_join_statistics(JOIN *join, List &leaves, + COND *conds, DYNAMIC_ARRAY *keyuse); static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, JOIN_TAB *join_tab, uint tables, COND *conds, @@ -99,7 +99,8 @@ static void update_depend_map(JOIN *join); static void update_depend_map(JOIN *join, ORDER *order); static ORDER *remove_const(JOIN *join,ORDER *first_order,COND *cond, bool change_list, bool *simple_order); -static int return_zero_rows(JOIN *join, select_result *res,TABLE_LIST *tables, +static int return_zero_rows(JOIN *join, select_result *res, + List &tables, List &fields, bool send_row, ulonglong select_options, const char *info, Item *having); @@ -210,7 +211,7 @@ static ORDER *create_distinct_group(THD *thd, Item **ref_pointer_array, List &all_fields, bool *all_order_by_fields_used); static bool test_if_subpart(ORDER *a,ORDER *b); -static TABLE *get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables); +static TABLE *get_sort_by_table(ORDER *a,ORDER *b,List &tables); static void calc_group_buffer(JOIN *join,ORDER *group); static bool make_group_fields(JOIN *main_join, JOIN *curr_join); static bool alloc_group_fields(JOIN *join,ORDER *group); @@ -237,6 +238,7 @@ static void add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab); void get_partial_join_cost(JOIN *join, uint idx, double *read_time_arg, double *record_count_arg); static uint make_join_orderinfo(JOIN *join); +static bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array); static int join_read_record_no_init(JOIN_TAB *tab); @@ -405,7 +407,7 @@ fix_inner_refs(THD *thd, List &all_fields, SELECT_LEX *select, */ inline int setup_without_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, - TABLE_LIST *leaves, + List &leaves, List &fields, List &all_fields, COND **conds, @@ -483,28 +485,26 @@ JOIN::prepare(Item ***rref_pointer_array, join_list= &select_lex->top_join_list; union_part= unit_arg->is_union(); + if (select_lex->handle_derived(thd->lex, DT_PREPARE)) + DBUG_RETURN(1); + thd->lex->current_select->is_item_list_lookup= 1; /* If we have already executed SELECT, then it have not sense to prevent its table from update (see unique_table()) + Affects only materialized derived tables. */ - if (thd->derived_tables_processing) - select_lex->exclude_from_table_unique_test= TRUE; - /* Check that all tables, fields, conds and order are ok */ - - if (!(select_options & OPTION_SETUP_TABLES_DONE) && - setup_tables_and_check_access(thd, &select_lex->context, join_list, - tables_list, &select_lex->leaf_tables, - FALSE, SELECT_ACL, SELECT_ACL)) + if (!(select_options & OPTION_SETUP_TABLES_DONE)) + { + if (setup_tables_and_check_access(thd, &select_lex->context, join_list, + tables_list, select_lex->leaf_tables, + FALSE, SELECT_ACL, SELECT_ACL, FALSE)) DBUG_RETURN(-1); - - TABLE_LIST *table_ptr; - for (table_ptr= select_lex->leaf_tables; - table_ptr; - table_ptr= table_ptr->next_leaf) - tables++; + } + tables= select_lex->leaf_tables.elements; + if (setup_wild(thd, tables_list, fields_list, &all_fields, wild_num) || select_lex->setup_ref_array(thd, og_num) || setup_fields(thd, (*rref_pointer_array), fields_list, MARK_COLUMNS_READ, @@ -605,10 +605,6 @@ JOIN::prepare(Item ***rref_pointer_array, } } - if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */ - DBUG_RETURN(-1); - - /* Check if there are references to un-aggregated columns when computing aggregate functions with implicit grouping (there is no GROUP BY). @@ -720,13 +716,37 @@ JOIN::optimize() if (optimized) DBUG_RETURN(0); optimized= 1; - thd_proc_info(thd, "optimizing"); + + /* Run optimize phase for all derived tables/views used in this SELECT. */ + if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE)) + DBUG_RETURN(1); - /* dump_TABLE_LIST_graph(select_lex, select_lex->leaf_tables); */ - if (convert_join_subqueries_to_semijoins(this)) - DBUG_RETURN(1); /* purecov: inspected */ - /* dump_TABLE_LIST_graph(select_lex, select_lex->leaf_tables); */ + if (select_lex->first_cond_optimization) + { + //Do it only for the first execution + /* Merge all mergeable derived tables/views in this SELECT. */ + if (select_lex->handle_derived(thd->lex, DT_MERGE)) + DBUG_RETURN(TRUE); + tables= select_lex->leaf_tables.elements; + select_lex->update_used_tables(); + + /* dump_TABLE_LIST_graph(select_lex, select_lex->leaf_tables); */ + if (convert_join_subqueries_to_semijoins(this)) + DBUG_RETURN(1); /* purecov: inspected */ + /* dump_TABLE_LIST_graph(select_lex, select_lex->leaf_tables); */ + select_lex->update_used_tables(); + + /* Save this info for the next executions */ + if (select_lex->save_leaf_tables(thd)) + DBUG_RETURN(1); + } + + tables= select_lex->leaf_tables.elements; + + + if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */ + DBUG_RETURN(-1); row_limit= ((select_distinct || order || group_list) ? HA_POS_ERROR : unit->select_limit_cnt); @@ -760,7 +780,8 @@ JOIN::optimize() } } #endif - SELECT_LEX *sel= thd->lex->current_select; + + SELECT_LEX *sel= select_lex; if (sel->first_cond_optimization) { /* @@ -785,7 +806,7 @@ JOIN::optimize() if (arena) thd->restore_active_arena(arena, &backup); } - + conds= optimize_cond(this, conds, join_list, &cond_value); if (thd->is_error()) { @@ -823,7 +844,8 @@ JOIN::optimize() #ifdef WITH_PARTITION_STORAGE_ENGINE { TABLE_LIST *tbl; - for (tbl= select_lex->leaf_tables; tbl; tbl= tbl->next_leaf) + List_iterator_fast li(select_lex->leaf_tables); + while ((tbl= li++)) { /* If tbl->embedding!=NULL that means that this table is in the inner @@ -930,6 +952,8 @@ JOIN::optimize() DBUG_RETURN(1); } + drop_unused_derived_keys(); + if (rollup.state != ROLLUP::STATE_NONE) { if (rollup_process_const_fields()) @@ -1030,6 +1054,7 @@ JOIN::optimize() { zero_result_cause= "Impossible WHERE noticed after reading const tables"; + select_lex->mark_const_derived(zero_result_cause); goto setup_subq_exit; } @@ -1348,7 +1373,7 @@ JOIN::optimize() if (select_options & SELECT_DESCRIBE) { error= 0; - DBUG_RETURN(0); + goto derived_exit; } having= 0; @@ -1497,6 +1522,9 @@ setup_subq_exit: if (setup_subquery_materialization()) DBUG_RETURN(1); error= 0; + +derived_exit: + select_lex->mark_const_derived(zero_result_cause); DBUG_RETURN(0); } @@ -1733,6 +1761,11 @@ JOIN::exec() !tables ? "No tables used" : NullS); DBUG_VOID_RETURN; } + else + { + /* it's a const select, materialize it. */ + select_lex->mark_const_derived(zero_result_cause); + } JOIN *curr_join= this; List *curr_all_fields= &all_fields; @@ -2232,6 +2265,7 @@ JOIN::destroy() } tmp_join->tmp_join= 0; tmp_table_param.cleanup(); + tmp_join->tmp_table_param.copy_field= 0; DBUG_RETURN(tmp_join->destroy()); } cond_equal= 0; @@ -2512,12 +2546,11 @@ typedef struct st_sargable_param */ static bool -make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, - DYNAMIC_ARRAY *keyuse_array) +make_join_statistics(JOIN *join, List &tables_list, + COND *conds, DYNAMIC_ARRAY *keyuse_array) { - int error; + int error= 0; TABLE *table; - TABLE_LIST *tables= tables_arg; uint i,table_count,const_count,key; table_map found_const_table_map, all_table_map, found_ref, refs; key_map const_ref, eq_part; @@ -2528,6 +2561,8 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, table_map no_rows_const_tables= 0; SARGABLE_PARAM *sargables= 0; JOIN_TAB *stat_vector[MAX_TABLES+1]; + List_iterator ti(tables_list); + TABLE_LIST *tables; DBUG_ENTER("make_join_statistics"); table_count=join->tables; @@ -2543,9 +2578,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, found_const_table_map= all_table_map=0; const_count=0; - for (s= stat, i= 0; - tables; - s++, tables= tables->next_leaf, i++) + for (s= stat, i= 0; (tables= ti++); s++, i++) { TABLE_LIST *embedding= tables->embedding; stat_vector[i]=s; @@ -2555,7 +2588,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, s->needed_reg.init(); table_vector[i]=s->table=table=tables->table; table->pos_in_table_list= tables; - error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); + error= tables->fetch_number_of_rows(); if (error) { table->file->print_error(error, MYF(0)); @@ -2626,6 +2659,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, no_rows_const_tables |= table->map; } } + stat_vector[i]=0; join->outer_join=outer_join; @@ -2641,6 +2675,8 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, */ for (i= 0, s= stat ; i < table_count ; i++, s++) { + if (!s->dependent) + continue; for (uint j= 0 ; j < table_count ; j++) { table= stat[j].table; @@ -2874,7 +2910,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, } /* Approximate found rows and time to read them */ s->found_records=s->records=s->table->file->stats.records; - s->read_time=(ha_rows) s->table->file->scan_time(); + s->scan_time(); /* Set a max range of how many seeks we can expect when using keys @@ -2959,17 +2995,31 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, if (optimize_semijoin_nests(join, all_table_map)) DBUG_RETURN(TRUE); /* purecov: inspected */ - /* Find an optimal join order of the non-constant tables. */ - if (join->const_tables != join->tables) - { - if (choose_plan(join, all_table_map & ~join->const_table_map)) - goto error; - } - else - { - memcpy((uchar*) join->best_positions,(uchar*) join->positions, - sizeof(POSITION)*join->const_tables); - join->best_read=1.0; + { + ha_rows records= 1; + SELECT_LEX_UNIT *unit= join->select_lex->master_unit(); + + /* Find an optimal join order of the non-constant tables. */ + if (join->const_tables != join->tables) + { + if (choose_plan(join, all_table_map & ~join->const_table_map)) + goto error; + /* + Calculate estimated number of rows for materialized derived + table/view. + */ + for (i= 0; i < join->tables ; i++) + records*= join->best_positions[i].records_read ? + (ha_rows)join->best_positions[i].records_read : 1; + } + else + { + memcpy((uchar*) join->best_positions,(uchar*) join->positions, + sizeof(POSITION)*join->const_tables); + join->best_read=1.0; + } + if (unit->derived && unit->derived->is_materialized_derived()) + join->select_lex->increase_derived_records(records); } /* Generate an execution plan from the found optimal join order. */ DBUG_RETURN(join->thd->killed || get_best_combination(join)); @@ -2981,8 +3031,12 @@ error: may not be assigned yet by this function (which is building join_tab). Dangling TABLE::reginfo.join_tab may cause part_of_refkey to choke. */ - for (tables= tables_arg; tables; tables= tables->next_leaf) - tables->table->reginfo.join_tab= NULL; + { + TABLE_LIST *table; + List_iterator ti(tables_list); + while ((table= ti++)) + table->table->reginfo.join_tab= NULL; + } DBUG_RETURN (1); } @@ -3245,14 +3299,20 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, Field *field, bool eq_func, Item **value, uint num_values, table_map usable_tables, SARGABLE_PARAM **sargables) { - uint exists_optimize= 0; - if (!(field->flags & PART_KEY_FLAG)) + uint optimize= 0; + if (eq_func && + field->table->pos_in_table_list->is_materialized_derived() && + !field->table->created) + { + optimize= KEY_OPTIMIZE_EQ; + } + else if (!(field->flags & PART_KEY_FLAG)) { // Don't remove column IS NULL on a LEFT JOIN table if (!eq_func || (*value)->type() != Item::NULL_ITEM || !field->table->maybe_null || field->null_ptr) return; // Not a key. Skip it - exists_optimize= KEY_OPTIMIZE_EXISTS; + optimize= KEY_OPTIMIZE_EXISTS; DBUG_ASSERT(num_values == 1); } else @@ -3272,12 +3332,12 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, if (!eq_func || (*value)->type() != Item::NULL_ITEM || !field->table->maybe_null || field->null_ptr) return; // Can't use left join optimize - exists_optimize= KEY_OPTIMIZE_EXISTS; + optimize= KEY_OPTIMIZE_EXISTS; } else { JOIN_TAB *stat=field->table->reginfo.join_tab; - key_map possible_keys=field->key_start; + key_map possible_keys=field->get_possible_keys(); possible_keys.intersect(field->table->keys_in_use_for_query); stat[0].keys.merge(possible_keys); // Add possible keys @@ -3371,7 +3431,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, (*key_fields)->eq_func= eq_func; (*key_fields)->val= *value; (*key_fields)->level= and_level; - (*key_fields)->optimize= exists_optimize; + (*key_fields)->optimize= optimize; /* If the condition has form "tbl.keypart = othertbl.field" and othertbl.field can be NULL, there will be no matches if othertbl.field @@ -3690,6 +3750,34 @@ max_part_bit(key_part_map bits) return found; } +static bool +add_keyuse(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field, + uint key, uint part) +{ + KEYUSE keyuse; + Field *field= key_field->field; + + keyuse.table= field->table; + keyuse.val= key_field->val; + keyuse.key= key; + if (key != MAX_KEY) + { + keyuse.keypart=part; + keyuse.keypart_map= (key_part_map) 1 << part; + } + else + { + keyuse.keypart= field->field_index; + keyuse.keypart_map= (key_part_map) 0; + } + keyuse.used_tables= key_field->val->used_tables(); + keyuse.optimize= key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL; + keyuse.null_rejecting= key_field->null_rejecting; + keyuse.cond_guard= key_field->cond_guard; + keyuse.sj_pred_no= key_field->sj_pred_no; + return (insert_dynamic(keyuse_array,(uchar*) &keyuse)); +} + /* Add all keys with uses 'field' for some keypart If field->and_level != and_level then only mark key_part as const_part @@ -3704,10 +3792,13 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field) { Field *field=key_field->field; TABLE *form= field->table; - KEYUSE keyuse; if (key_field->eq_func && !(key_field->optimize & KEY_OPTIMIZE_EXISTS)) { + if (key_field->eq_func && (key_field->optimize & KEY_OPTIMIZE_EQ)) + { + return add_keyuse(keyuse_array, key_field, MAX_KEY, 0); + } for (uint key=0 ; key < form->s->keys ; key++) { if (!(form->keys_in_use_for_query.is_set(key))) @@ -3720,17 +3811,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field) { if (field->eq(form->key_info[key].key_part[part].field)) { - keyuse.table= field->table; - keyuse.val = key_field->val; - keyuse.key = key; - keyuse.keypart=part; - keyuse.keypart_map= (key_part_map) 1 << part; - keyuse.used_tables=key_field->val->used_tables(); - keyuse.optimize= key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL; - keyuse.null_rejecting= key_field->null_rejecting; - keyuse.cond_guard= key_field->cond_guard; - keyuse.sj_pred_no= key_field->sj_pred_no; - if (insert_dynamic(keyuse_array,(uchar*) &keyuse)) + if (add_keyuse(keyuse_array, key_field, key, part)) return TRUE; } } @@ -3815,6 +3896,9 @@ sort_keyuse(KEYUSE *a,KEYUSE *b) return (int) (a->table->tablenr - b->table->tablenr); if (a->key != b->key) return (int) (a->key - b->key); + if (a->key == MAX_KEY && b->key == MAX_KEY && + a->used_tables != b->used_tables) + return (int) ((ulong) a->used_tables - (ulong) b->used_tables); if (a->keypart != b->keypart) return (int) (a->keypart - b->keypart); // Place const values before other ones @@ -3965,19 +4049,21 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, if (my_init_dynamic_array(keyuse,sizeof(KEYUSE),20,64)) return TRUE; + if (cond) { + KEY_FIELD *saved_field= field; add_key_fields(join_tab->join, &end, &and_level, cond, normal_tables, sargables); for (; field != end ; field++) { - if (add_key_part(keyuse,field)) - return TRUE; + /* Mark that we can optimize LEFT JOIN */ if (field->val->type() == Item::NULL_ITEM && !field->field->real_maybe_null()) field->field->table->reginfo.not_exists_optimize=1; } + field= saved_field; } for (i=0 ; i < tables ; i++) { @@ -4042,6 +4128,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, if (insert_dynamic(keyuse,(uchar*) &key_end)) return TRUE; + generate_derived_keys(keyuse); + use=save_pos=dynamic_element(keyuse,0,KEYUSE*); prev= &key_end; found_eq_constant=0; @@ -4107,7 +4195,7 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) ~OUTER_REF_TABLE_BIT))) { uint tablenr; - for (tablenr=0 ; ! (map & 1) ; map>>=1, tablenr++) ; + tablenr= my_count_bits(map); if (map == 1) // Only one table { TABLE *tmp_table=join->all_tables[tablenr]; @@ -4714,7 +4802,7 @@ best_access_path(JOIN *join, else { /* Estimate cost of reading table. */ - tmp= s->table->file->scan_time(); + tmp= s->scan_time(); if ((s->table->map & join->outer_join) || disable_jbuf) // Can't use join cache { /* @@ -5065,6 +5153,7 @@ optimize_straight_join(JOIN *join, table_map join_tables) { JOIN_TAB *s; uint idx= join->const_tables; + bool disable_jbuf= join->thd->variables.join_cache_level == 0; double record_count= 1.0; double read_time= 0.0; POSITION loose_scan_pos; @@ -5072,7 +5161,7 @@ optimize_straight_join(JOIN *join, table_map join_tables) for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++) { /* Find the best access method from 's' to the current partial plan */ - best_access_path(join, s, join_tables, idx, FALSE, record_count, + best_access_path(join, s, join_tables, idx, disable_jbuf, record_count, join->positions + idx, &loose_scan_pos); /* compute the cost of the new plan extended with 's' */ @@ -5452,6 +5541,7 @@ best_extension_by_limited_search(JOIN *join, JOIN_TAB *s; double best_record_count= DBL_MAX; double best_read_time= DBL_MAX; + bool disable_jbuf= join->thd->variables.join_cache_level == 0; DBUG_EXECUTE("opt", print_plan(join, idx, record_count, read_time, read_time, "part_plan");); @@ -5473,8 +5563,8 @@ best_extension_by_limited_search(JOIN *join, /* Find the best access method from 's' to the current partial plan */ POSITION loose_scan_pos; - best_access_path(join, s, remaining_tables, idx, FALSE, record_count, - join->positions + idx, &loose_scan_pos); + best_access_path(join, s, remaining_tables, idx, disable_jbuf, + record_count, join->positions + idx, &loose_scan_pos); /* Compute the cost of extending the plan with 's' */ @@ -5618,6 +5708,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, JOIN_TAB *s; double best_record_count=DBL_MAX,best_read_time=DBL_MAX; + bool disable_jbuf= join->thd->variables.join_cache_level == 0; for (JOIN_TAB **pos=join->best_ref+idx ; (s=*pos) ; pos++) { table_map real_table_bit=s->table->map; @@ -5626,7 +5717,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, { double records, best; POSITION loose_scan_pos; - best_access_path(join, s, rest_tables, idx, FALSE, record_count, + best_access_path(join, s, rest_tables, idx, disable_jbuf, record_count, join->positions + idx, &loose_scan_pos); records= join->positions[idx].records_read; best= join->positions[idx].read_time; @@ -5999,8 +6090,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, if (keyuse->null_rejecting) j->ref.null_rejecting |= 1 << i; keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables; - if (!keyuse->used_tables && - !(join->select_options & SELECT_DESCRIBE)) + if (!keyuse->used_tables && !thd->lex->describe) { // Compare against constant store_key_item tmp(thd, keyinfo->key_part[i].field, key_buff + maybe_null, @@ -6411,7 +6501,7 @@ make_outerjoin_info(JOIN *join) for ( ; embedding ; embedding= embedding->embedding) { /* Ignore sj-nests: */ - if (!embedding->on_expr) + if (!(embedding->on_expr && embedding->outer_join)) continue; NESTED_JOIN *nested_join= embedding->nested_join; if (!nested_join->counter) @@ -6902,6 +6992,123 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) } +static +uint get_next_field_for_derived_key(uchar *arg) +{ + KEYUSE *keyuse= *(KEYUSE **) arg; + if (!keyuse) + return (uint) (-1); + uint key= keyuse->key; + uint fldno= keyuse->keypart; + uint keypart= keyuse->keypart_map == (key_part_map) 1 ? + 0 : (keyuse-1)->keypart+1; + for ( ; keyuse->key == key && keyuse->keypart == fldno; keyuse++) + keyuse->keypart= keypart; + if (keyuse->key != key) + keyuse= 0; + return fldno; +} + + +static +bool generate_derived_keys_for_table(KEYUSE *keyuse, uint count, uint keys) +{ + TABLE *table= keyuse->table; + if (table->alloc_keys(keys)) + return TRUE; + uint keyno= 0; + KEYUSE *first_keyuse= keyuse; + uint prev_part= (uint) (-1); + uint parts= 0; + uint i= 0; + do + { + keyuse->key= keyno; + keyuse->keypart_map= (key_part_map) (1 << parts); + keyuse++; + if (++i == count || keyuse->used_tables != first_keyuse->used_tables) + { + if (table->add_tmp_key(keyno, ++parts, + get_next_field_for_derived_key, + (uchar *) &first_keyuse)) + return TRUE; + first_keyuse= keyuse; + keyno++; + parts= 0; + } + else if (keyuse->keypart != prev_part) + { + parts++; + prev_part= keyuse->keypart; + } + } while (keyno < keys); + return FALSE; +} + + +static +bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array) +{ + KEYUSE *keyuse= dynamic_element(keyuse_array, 0, KEYUSE*); + uint elements= keyuse_array->elements; + TABLE *prev_table= 0; + for (uint i= 0; i < elements; i++, keyuse++) + { + KEYUSE *first_table_keyuse; + table_map last_used_tables; + uint count; + uint keys; + while (keyuse->key == MAX_KEY) + { + if (keyuse->table != prev_table) + { + prev_table= keyuse->table; + first_table_keyuse= keyuse; + last_used_tables= keyuse->used_tables; + count= 0; + keys= 0; + } + else if (keyuse->used_tables != last_used_tables) + { + keys++; + last_used_tables= keyuse->used_tables; + } + count++; + keyuse++; + if (keyuse->table != prev_table && + generate_derived_keys_for_table(first_table_keyuse, count, ++keys)) + return TRUE; + } + } + return FALSE; +} + + +/* + @brief + Drops unused keys for each materialized derived table/view + + @details + For materialized derived tables only ref access can be used, it employs + only one index, thus we don't need the rest. For each materialized derived + table/view call TABLE::use_index to save one index chosen by the optimizer + and free others. No key is chosen then all keys will be dropped. +*/ + +void JOIN::drop_unused_derived_keys() +{ + for (uint i= const_tables ; i < tables ; i++) + { + JOIN_TAB *tab=join_tab+i; + TABLE *table=tab->table; + if (!table->pos_in_table_list->is_materialized_derived() || + table->max_keys <= 1) + continue; + table->use_index(tab->ref.key); + tab->ref.key= 0; + } +} + /* Determine {after which table we'll produce ordered set} @@ -7694,6 +7901,30 @@ void JOIN_TAB::cleanup() } +/** + Initialize the join_tab before reading. + Currently only derived table/view materialization is done here. +*/ + +bool JOIN_TAB::preread_init() +{ + TABLE_LIST *derived= table->pos_in_table_list; + if (!derived || !derived->is_materialized_derived()) + { + preread_init_done= TRUE; + return FALSE; + } + + /* Materialize derived table/view. */ + if (!derived->get_unit()->executed && + mysql_handle_single_derived(join->thd->lex, + derived, DT_CREATE | DT_FILL)) + return TRUE; + preread_init_done= TRUE; + return FALSE; +} + + /** Partially cleanup JOIN after it has executed: close index or rnd read (table cursors), free quick selects. @@ -8118,7 +8349,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, static int -return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables, +return_zero_rows(JOIN *join, select_result *result, List &tables, List &fields, bool send_row, ulonglong select_options, const char *info, Item *having) { @@ -8134,7 +8365,9 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables, if (send_row) { - for (TABLE_LIST *table= tables; table; table= table->next_leaf) + List_iterator ti(tables); + TABLE_LIST *table; + while ((table= ti++)) mark_as_null_row(table->table); // All fields are NULL if (having && having->val_int() == 0) send_row=0; @@ -9764,12 +9997,14 @@ simplify_joins(JOIN *join, List *join_list, COND *conds, bool top, { TABLE_LIST *tbl; List_iterator it(nested_join->join_list); + List repl_list; while ((tbl= it++)) { tbl->embedding= table->embedding; tbl->join_list= table->join_list; + repl_list.push_back(tbl); } - li.replace(nested_join->join_list); + li.replace(repl_list); /* Need to update the name resolution table chain when flattening joins */ fix_name_res= TRUE; table= *li.ref(); @@ -10707,13 +10942,29 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, If item have to be able to store NULLs but underlaid field can't do it, create_tmp_field_from_field() can't be used for tmp field creation. */ - if (field->maybe_null && !field->field->maybe_null()) + if ((field->maybe_null || + (orig_item && orig_item->maybe_null)) && /* for outer joined views/dt*/ + !field->field->maybe_null()) { + bool save_maybe_null; + /* + The item the ref points to may have maybe_null flag set while + the ref doesn't have it. This may happen for outer fields + when the outer query decided at some point after name resolution phase + that this field might be null. Take this into account here. + */ + if (orig_item) + { + save_maybe_null= item->maybe_null; + item->maybe_null= orig_item->maybe_null; + } result= create_tmp_field_from_item(thd, item, table, NULL, modify_item, convert_blob_length); *from_field= field->field; if (result && modify_item) field->result_field= result; + if (orig_item) + item->maybe_null= save_maybe_null; } else if (table_cant_handle_bit_fields && field->field->type() == MYSQL_TYPE_BIT) @@ -10858,7 +11109,7 @@ TABLE * create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, ORDER *group, bool distinct, bool save_sum_fields, ulonglong select_options, ha_rows rows_limit, - char *table_alias) + char *table_alias, bool do_not_open) { MEM_ROOT *mem_root_save, own_root; TABLE *table; @@ -11397,7 +11648,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, share->uniques= test(using_unique_constraint); table->key_info= table->s->key_info= keyinfo; keyinfo->key_part=key_part_info; - keyinfo->flags=HA_NOSAME; + keyinfo->flags=HA_NOSAME | HA_BINARY_PACK_KEY | HA_PACK_KEY; keyinfo->usable_key_parts=keyinfo->key_parts= param->group_parts; keyinfo->key_length=0; keyinfo->rec_per_key=0; @@ -11483,7 +11734,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, bzero((void*) key_part_info, keyinfo->key_parts * sizeof(KEY_PART_INFO)); table->key_info= table->s->key_info= keyinfo; keyinfo->key_part=key_part_info; - keyinfo->flags=HA_NOSAME | HA_NULL_ARE_EQUAL; + keyinfo->flags=HA_NOSAME | HA_NULL_ARE_EQUAL | HA_BINARY_PACK_KEY | HA_PACK_KEY; keyinfo->key_length= 0; // Will compute the sum of the parts below. keyinfo->name= (char*) "distinct_key"; keyinfo->algorithm= HA_KEY_ALG_UNDEF; @@ -11551,15 +11802,17 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, if (thd->is_fatal_error) // If end of memory goto err; /* purecov: inspected */ share->db_record_offset= 1; - if (share->db_type() == TMP_ENGINE_HTON) + if (!do_not_open) { - if (create_internal_tmp_table(table, param->keyinfo, param->start_recinfo, - ¶m->recinfo, select_options)) + if (share->db_type() == TMP_ENGINE_HTON) + { + if (create_internal_tmp_table(table, param->keyinfo, param->start_recinfo, + ¶m->recinfo, select_options)) + goto err; + } + if (open_tmp_table(table)) goto err; } - if (open_tmp_table(table)) - goto err; - thd->mem_root= mem_root_save; DBUG_RETURN(table); @@ -11714,6 +11967,7 @@ bool open_tmp_table(TABLE *table) return(1); } (void) table->file->extra(HA_EXTRA_QUICK); /* Faster */ + table->created= TRUE; return(0); } @@ -12022,6 +12276,7 @@ static bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, } status_var_increment(table->in_use->status_var.created_tmp_disk_tables); share->db_record_offset= 1; + table->created= TRUE; DBUG_RETURN(0); err: DBUG_RETURN(1); @@ -12177,7 +12432,7 @@ free_tmp_table(THD *thd, TABLE *entry) save_proc_info=thd->proc_info; thd_proc_info(thd, "removing tmp table"); - if (entry->file) + if (entry->file && entry->created) { if (entry->db_stat) entry->file->ha_drop_table(entry->s->table_name.str); @@ -12777,6 +13032,9 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) do_sj_reset(join_tab->flush_weedout_table); } + if (!join_tab->preread_init_done && join_tab->preread_init()) + DBUG_RETURN(NESTED_LOOP_ERROR); + if (join->resume_nested_loop) { /* If not the last table, plunge down the nested loop */ @@ -13135,13 +13393,21 @@ static int join_read_const_table(JOIN_TAB *tab, POSITION *pos) { int error; + TABLE_LIST *tbl; DBUG_ENTER("join_read_const_table"); TABLE *table=tab->table; table->const_table=1; table->null_row=0; table->status=STATUS_NO_RECORD; - if (tab->type == JT_SYSTEM) + if (tab->table->pos_in_table_list->is_materialized_derived() && + !tab->table->pos_in_table_list->fill_me) + { + //TODO: don't get here at all + /* Skip materialized derived tables/views. */ + DBUG_RETURN(0); + } + else if (tab->type == JT_SYSTEM) { if ((error=join_read_system(tab))) { // Info for DESCRIBE @@ -13203,26 +13469,27 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos) if (!table->null_row) table->maybe_null=0; - /* Check appearance of new constant items in Item_equal objects */ - JOIN *join= tab->join; - if (join->conds) - update_const_equal_items(join->conds, tab); - TABLE_LIST *tbl; - for (tbl= join->select_lex->leaf_tables; tbl; tbl= tbl->next_leaf) { - TABLE_LIST *embedded; - TABLE_LIST *embedding= tbl; - do + JOIN *join= tab->join; + List_iterator ti(join->select_lex->leaf_tables); + /* Check appearance of new constant items in Item_equal objects */ + if (join->conds) + update_const_equal_items(join->conds, tab); + while ((tbl= ti++)) { - embedded= embedding; - if (embedded->on_expr) - update_const_equal_items(embedded->on_expr, tab); - embedding= embedded->embedding; + TABLE_LIST *embedded; + TABLE_LIST *embedding= tbl; + do + { + embedded= embedding; + if (embedded->on_expr) + update_const_equal_items(embedded->on_expr, tab); + embedding= embedded->embedding; + } + while (embedding && + embedding->nested_join->join_list.head() == embedded); } - while (embedding && - embedding->nested_join->join_list.head() == embedded); } - DBUG_RETURN(0); } @@ -13576,6 +13843,9 @@ int join_init_read_record(JOIN_TAB *tab) { if (tab->select && tab->select->quick && tab->select->quick->reset()) return 1; + if (!tab->preread_init_done && tab->preread_init()) + return 1; + init_read_record(&tab->read_record, tab->join->thd, tab->table, tab->select,1,1, FALSE); return (*tab->read_record.read_record)(&tab->read_record); @@ -15500,6 +15770,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, get_schema_tables_result(join, PROCESSED_BY_CREATE_SORT_INDEX)) goto err; + if (!tab->preread_init_done && tab->preread_init()) + goto err; if (table->s->tmp_table) table->file->info(HA_STATUS_VARIABLE); // Get record count table->sort.found_records=filesort(thd, table,join->sortorder, length, @@ -16053,7 +16325,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, order->in_field_list= 1; order->counter= count; order->counter_used= 1; - return FALSE; + return FALSE; } /* Lookup the current GROUP/ORDER field in the SELECT clause. */ select_item= find_item_in_list(order_item, fields, &counter, @@ -16494,8 +16766,10 @@ test_if_subpart(ORDER *a,ORDER *b) */ static TABLE * -get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables) +get_sort_by_table(ORDER *a,ORDER *b, List &tables) { + TABLE_LIST *table; + List_iterator ti(tables); table_map map= (table_map) 0; DBUG_ENTER("get_sort_by_table"); @@ -16513,11 +16787,11 @@ get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables) if (!map || (map & (RAND_TABLE_BIT | OUTER_REF_TABLE_BIT))) DBUG_RETURN(0); - for (; !(map & tables->table->map); tables= tables->next_leaf) ; - if (map != tables->table->map) + while ((table= ti++) && !(map & table->table->map)); + if (map != table->table->map) DBUG_RETURN(0); // More than one table - DBUG_PRINT("exit",("sort by table: %d",tables->table->tablenr)); - DBUG_RETURN(tables->table); + DBUG_PRINT("exit",("sort by table: %d",table->table->tablenr)); + DBUG_RETURN(table->table); } @@ -17886,7 +18160,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, if (result->send_data(item_list)) join->error= 1; } - else + else if (!join->select_lex->master_unit()->derived || + join->select_lex->master_unit()->derived->is_materialized_derived()) { table_map used_tables=0; @@ -18194,6 +18469,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, if (examined_rows) f= (float) (100.0 * join->best_positions[i].records_read / examined_rows); + set_if_smaller(f, 100.0); item_list.push_back(new Item_float(f, 2)); } } @@ -18456,11 +18732,32 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) sl; sl= sl->next_select()) { + bool is_primary= FALSE; + if (sl->next_select()) + is_primary= TRUE; + + if (!is_primary && sl->first_inner_unit()) + { + /* + If there is at least one materialized derived|view then it's a PRIMARY select. + Otherwise, all derived tables/views were merged and this select is a SIMPLE one. + */ + for (SELECT_LEX_UNIT *un= sl->first_inner_unit(); + un; + un= un->next_unit()) + { + if ((!un->derived || + un->derived->is_materialized_derived())) + { + is_primary= TRUE; + break; + } + } + } // drop UNCACHEABLE_EXPLAIN, because it is for internal usage only uint8 uncacheable= (sl->uncacheable & ~UNCACHEABLE_EXPLAIN); sl->type= (((&thd->lex->select_lex)==sl)? - (sl->first_inner_unit() || sl->next_select() ? - "PRIMARY" : "SIMPLE"): + (is_primary ? "PRIMARY" : "SIMPLE"): ((sl == first)? ((sl->linkage == DERIVED_TABLE_TYPE) ? "DERIVED": diff --git a/sql/sql_select.h b/sql/sql_select.h index bc2c1b0f2cf..92193e81bf0 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -37,6 +37,7 @@ /* Values in optimize */ #define KEY_OPTIMIZE_EXISTS 1 #define KEY_OPTIMIZE_REF_OR_NULL 2 +#define KEY_OPTIMIZE_EQ 4 typedef struct keyuse_t { TABLE *table; @@ -293,6 +294,8 @@ typedef struct st_join_table { */ uint sj_strategy; + bool preread_init_done; + void cleanup(); inline bool is_using_loose_index_scan() { @@ -364,6 +367,22 @@ typedef struct st_join_table { select->cond= new_cond; return tmp_select_cond; } + double scan_time() + { + double res; + if (table->created) + { + res= table->file->scan_time(); + read_time=(ha_rows) res; + } + else + { + read_time= found_records ? found_records: 10;// TODO:fix this stub + res= (double)read_time; + } + return res; + } + bool preread_init(); } JOIN_TAB; @@ -1551,6 +1570,7 @@ public: bool union_part; ///< this subselect is part of union bool optimized; ///< flag to avoid double optimization in EXPLAIN + Array sj_subselects; /* Temporary tables used to weed-out semi-join duplicates */ @@ -1700,6 +1720,7 @@ public: { return (table_map(1) << tables) - 1; } + void drop_unused_derived_keys(); /* Return the table for which an index scan can be used to satisfy the sort order needed by the ORDER BY/(implicit) GROUP BY clause @@ -1744,7 +1765,7 @@ Field* create_tmp_field_from_field(THD *thd, Field* org_field, /* functions from opt_sum.cc */ bool simple_pred(Item_func *func_item, Item **args, bool *inv_order); -int opt_sum_query(TABLE_LIST *tables, List &all_fields,COND *conds); +int opt_sum_query(List &tables, List &all_fields,COND *conds); /* from sql_delete.cc, used by opt_range.cc */ extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b); @@ -1964,7 +1985,7 @@ void push_index_cond(JOIN_TAB *tab, uint keyno, bool other_tbls_ok); TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, ORDER *group, bool distinct, bool save_sum_fields, ulonglong select_options, ha_rows rows_limit, - char* alias); + char* alias, bool do_not_open=FALSE); void free_tmp_table(THD *thd, TABLE *entry); bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table, ENGINE_COLUMNDEF *start_recinfo, diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 97583ea0cee..69f8aca5e81 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -719,7 +719,8 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) { Show_create_error_handler view_error_suppressor(thd, table_list); thd->push_internal_handler(&view_error_suppressor); - bool error= open_normal_and_derived_tables(thd, table_list, 0); + bool error= open_normal_and_derived_tables(thd, table_list, 0, + DT_PREPARE | DT_CREATE); thd->pop_internal_handler(); if (error && (thd->killed || thd->main_da.is_error())) DBUG_RETURN(TRUE); @@ -894,7 +895,8 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild) DBUG_ENTER("mysqld_list_fields"); DBUG_PRINT("enter",("table: %s",table_list->table_name)); - if (open_normal_and_derived_tables(thd, table_list, 0)) + if (open_normal_and_derived_tables(thd, table_list, 0, + DT_PREPARE | DT_CREATE)) DBUG_VOID_RETURN; table= table_list->table; @@ -1680,7 +1682,7 @@ view_store_options(THD *thd, TABLE_LIST *table, String *buff) static void append_algorithm(TABLE_LIST *table, String *buff) { buff->append(STRING_WITH_LEN("ALGORITHM=")); - switch ((int8)table->algorithm) { + switch ((int16)table->algorithm) { case VIEW_ALGORITHM_UNDEFINED: buff->append(STRING_WITH_LEN("UNDEFINED ")); break; @@ -3360,8 +3362,9 @@ fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables, SQLCOM_SHOW_FIELDS is used because it satisfies 'only_view_structure()' */ lex->sql_command= SQLCOM_SHOW_FIELDS; - res= open_normal_and_derived_tables(thd, show_table_list, - MYSQL_LOCK_IGNORE_FLUSH); + res= (open_normal_and_derived_tables(thd, show_table_list, + MYSQL_LOCK_IGNORE_FLUSH, + DT_PREPARE | DT_CREATE)); lex->sql_command= save_sql_command; /* get_all_tables() returns 1 on failure and 0 on success thus @@ -3792,8 +3795,9 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) lex->sql_command= SQLCOM_SHOW_FIELDS; show_table_list->i_s_requested_object= schema_table->i_s_requested_object; - res= open_normal_and_derived_tables(thd, show_table_list, - MYSQL_LOCK_IGNORE_FLUSH); + res= (open_normal_and_derived_tables(thd, show_table_list, + MYSQL_LOCK_IGNORE_FLUSH, + DT_PREPARE | DT_CREATE)); lex->sql_command= save_sql_command; /* XXX: show_table_list has a flag i_is_requested, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 9699f949cab..c798bf441e9 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4623,8 +4623,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, thd->no_warnings_for_error= no_warnings_for_error; if (view_operator_func == NULL) table->required_type=FRMTYPE_TABLE; - + if (lex->sql_command == SQLCOM_CHECK || + lex->sql_command == SQLCOM_REPAIR || + lex->sql_command == SQLCOM_ANALYZE || + lex->sql_command == SQLCOM_OPTIMIZE) + thd->prepare_derived_at_open= TRUE; open_and_lock_tables(thd, table); + thd->prepare_derived_at_open= FALSE; thd->no_warnings_for_error= 0; table->next_global= save_next_global; table->next_local= save_next_local; @@ -4722,7 +4727,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, else /* Default failure code is corrupt table */ result_code= HA_ADMIN_CORRUPT; - goto send_result; + goto send_result; } if (table->view) diff --git a/sql/sql_union.cc b/sql/sql_union.cc index ee9ff833726..c416678f320 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -105,6 +105,7 @@ bool select_union::flush() options create options table_alias name of the temporary table bit_fields_as_long convert bit fields to ulonglong + create_table whether to physically create result table DESCRIPTION Create a temporary table that is used to store the result of a UNION, @@ -119,7 +120,7 @@ bool select_union::create_result_table(THD *thd_arg, List *column_types, bool is_union_distinct, ulonglong options, const char *alias, - bool bit_fields_as_long) + bool bit_fields_as_long, bool create_table) { DBUG_ASSERT(table == 0); tmp_table_param.init(); @@ -128,10 +129,14 @@ select_union::create_result_table(THD *thd_arg, List *column_types, if (! (table= create_tmp_table(thd_arg, &tmp_table_param, *column_types, (ORDER*) 0, is_union_distinct, 1, - options, HA_POS_ERROR, (char*) alias))) + options, HA_POS_ERROR, (char*) alias, + !create_table))) return TRUE; - table->file->extra(HA_EXTRA_WRITE_CACHE); - table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + if (create_table) + { + table->file->extra(HA_EXTRA_WRITE_CACHE); + table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + } return FALSE; } @@ -269,6 +274,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, (is_union_select ? (ORDER*) 0 : (ORDER*) thd_arg->lex->proc_list.first), sl, this); + /* There are no * in the statement anymore (for PS) */ sl->with_wild= 0; last_procedure= join->procedure; @@ -331,6 +337,8 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, List_iterator_fast tp(types); Item *type; ulonglong create_options; + uint save_tablenr; + table_map save_map; while ((type= tp++)) { @@ -383,12 +391,22 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, create_options= create_options | TMP_TABLE_FORCE_MYISAM; if (union_result->create_result_table(thd, &types, test(union_distinct), - create_options, "", FALSE)) + create_options, "", FALSE, TRUE)) goto err; + if (fake_select_lex && !fake_select_lex->first_cond_optimization) + { + save_tablenr= result_table_list.tablenr_exec; + save_map= result_table_list.map_exec; + } bzero((char*) &result_table_list, sizeof(result_table_list)); result_table_list.db= (char*) ""; result_table_list.table_name= result_table_list.alias= (char*) "union"; result_table_list.table= table= union_result->table; + if (fake_select_lex && !fake_select_lex->first_cond_optimization) + { + result_table_list.tablenr_exec= save_tablenr; + result_table_list.map_exec= save_map; + } thd_arg->lex->current_select= lex_select_save; if (!item_list.elements) @@ -453,18 +471,21 @@ err: } -bool st_select_lex_unit::exec() +/** + Run optimization phase. + + @return FALSE unit successfully passed optimization phase. + @return TRUE an error occur. +*/ +bool st_select_lex_unit::optimize() { SELECT_LEX *lex_select_save= thd->lex->current_select; SELECT_LEX *select_cursor=first_select(); - ulonglong add_rows=0; - ha_rows examined_rows= 0; - DBUG_ENTER("st_select_lex_unit::exec"); + DBUG_ENTER("st_select_lex_unit::optimize"); - if (executed && !uncacheable && !describe) + if (optimized && !uncacheable && !describe) DBUG_RETURN(FALSE); - executed= 1; - + if (uncacheable || !item || !item->assigned() || describe) { if (item) @@ -485,12 +506,71 @@ bool st_select_lex_unit::exec() } for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select()) { - ha_rows records_at_start= 0; thd->lex->current_select= sl; if (optimized) saved_error= sl->join->reinit(); else + { + set_limit(sl); + if (sl == global_parameters || describe) + { + offset_limit_cnt= 0; + /* + We can't use LIMIT at this stage if we are using ORDER BY for the + whole query + */ + if (sl->order_list.first || describe) + select_limit_cnt= HA_POS_ERROR; + } + + /* + When using braces, SQL_CALC_FOUND_ROWS affects the whole query: + we don't calculate found_rows() per union part. + Otherwise, SQL_CALC_FOUND_ROWS should be done on all sub parts. + */ + sl->join->select_options= + (select_limit_cnt == HA_POS_ERROR || sl->braces) ? + sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union; + + saved_error= sl->join->optimize(); + } + + if (saved_error) + { + thd->lex->current_select= lex_select_save; + DBUG_RETURN(saved_error); + } + } + } + optimized= 1; + + thd->lex->current_select= lex_select_save; + DBUG_RETURN(saved_error); +} + + +bool st_select_lex_unit::exec() +{ + SELECT_LEX *lex_select_save= thd->lex->current_select; + SELECT_LEX *select_cursor=first_select(); + ulonglong add_rows=0; + ha_rows examined_rows= 0; + DBUG_ENTER("st_select_lex_unit::exec"); + + if (executed && !uncacheable && !describe) + DBUG_RETURN(FALSE); + executed= 1; + + saved_error= optimize(); + + if (uncacheable || !item || !item->assigned() || describe) + { + for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select()) + { + ha_rows records_at_start= 0; + thd->lex->current_select= sl; + { set_limit(sl); if (sl == global_parameters || describe) @@ -564,7 +644,6 @@ bool st_select_lex_unit::exec() } } } - optimized= 1; /* Send result to 'result' */ saved_error= TRUE; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index fdfcb9e48a4..c89e5e1b453 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -212,7 +212,11 @@ int mysql_update(THD *thd, if (open_tables(thd, &table_list, &table_count, 0)) DBUG_RETURN(1); - if (table_list->multitable_view) + //Prepare views so they are handled correctly. + if (mysql_handle_derived(thd->lex, DT_INIT)) + DBUG_RETURN(1); + + if (table_list->is_multitable()) { DBUG_ASSERT(table_list->view != 0); DBUG_PRINT("info", ("Switch to multi-update")); @@ -227,15 +231,19 @@ int mysql_update(THD *thd, DBUG_RETURN(1); close_tables_for_reopen(thd, &table_list); } - - if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || - (thd->fill_derived_tables() && - mysql_handle_derived(thd->lex, &mysql_derived_filling))) + if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(1); + if (table_list->handle_derived(thd->lex, DT_PREPARE)) DBUG_RETURN(1); thd_proc_info(thd, "init"); table= table_list->table; + if (!table_list->updatable) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); + DBUG_RETURN(1); + } /* Calculate "table->covering_keys" based on the WHERE */ table->covering_keys= table->s->keys_in_use; table->quick_keys.clear_all(); @@ -254,13 +262,17 @@ int mysql_update(THD *thd, table_list->grant.want_privilege= table->grant.want_privilege= want_privilege; table_list->register_want_access(want_privilege); #endif + /* 'Unfix' fields to allow correct marking by the setup_fields function. */ + if (table_list->is_view()) + unfix_fields(fields); + if (setup_fields_with_no_wrap(thd, 0, fields, MARK_COLUMNS_WRITE, 0, 0)) DBUG_RETURN(1); /* purecov: inspected */ if (table_list->view && check_fields(thd, fields)) { DBUG_RETURN(1); } - if (!table_list->updatable || check_key_in_view(thd, table_list)) + if (check_key_in_view(thd, table_list)) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); DBUG_RETURN(1); @@ -839,6 +851,11 @@ int mysql_update(THD *thd, } thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */ thd->abort_on_warning= 0; + if (thd->lex->current_select->first_cond_optimization) + { + thd->lex->current_select->save_leaf_tables(thd); + thd->lex->current_select->first_cond_optimization= 0; + } DBUG_RETURN((error >= 0 || thd->is_error()) ? 1 : 0); err: @@ -903,8 +920,8 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, if (setup_tables_and_check_access(thd, &select_lex->context, &select_lex->top_join_list, table_list, - &select_lex->leaf_tables, - FALSE, UPDATE_ACL, SELECT_ACL) || + select_lex->leaf_tables, + FALSE, UPDATE_ACL, SELECT_ACL, TRUE) || setup_conds(thd, table_list, select_lex->leaf_tables, conds) || select_lex->setup_ref_array(thd, order_num) || setup_order(thd, select_lex->ref_pointer_array, @@ -941,8 +958,8 @@ static table_map get_table_map(List *items) Item_field *item; table_map map= 0; - while ((item= (Item_field *) item_it++)) - map|= item->used_tables(); + while ((item= (Item_field *) item_it++)) + map|= item->all_used_tables(); DBUG_PRINT("info", ("table_map: 0x%08lx", (long) map)); return map; } @@ -964,7 +981,7 @@ int mysql_multi_update_prepare(THD *thd) { LEX *lex= thd->lex; TABLE_LIST *table_list= lex->query_tables; - TABLE_LIST *tl, *leaves; + TABLE_LIST *tl; List *fields= &lex->select_lex.item_list; table_map tables_for_update; bool update_view= 0; @@ -987,19 +1004,24 @@ reopen_tables: /* open tables and create derived ones, but do not lock and fill them */ if (((original_multiupdate || need_reopen) && open_tables(thd, &table_list, &table_count, 0)) || - mysql_handle_derived(lex, &mysql_derived_prepare)) + mysql_handle_derived(lex, DT_INIT)) DBUG_RETURN(TRUE); /* setup_tables() need for VIEWs. JOIN::prepare() will call setup_tables() second time, but this call will do nothing (there are check for second call in setup_tables()). */ + //We need to merge for insert prior to prepare. + if (mysql_handle_list_of_derived(lex, table_list, DT_MERGE_FOR_INSERT)) + DBUG_RETURN(1); + if (mysql_handle_list_of_derived(lex, table_list, DT_PREPARE)) + DBUG_RETURN(1); if (setup_tables_and_check_access(thd, &lex->select_lex.context, &lex->select_lex.top_join_list, table_list, - &lex->select_lex.leaf_tables, FALSE, - UPDATE_ACL, SELECT_ACL)) + lex->select_lex.leaf_tables, FALSE, + UPDATE_ACL, SELECT_ACL, TRUE)) DBUG_RETURN(TRUE); if (setup_fields_with_no_wrap(thd, 0, *fields, MARK_COLUMNS_WRITE, 0, 0)) @@ -1024,8 +1046,8 @@ reopen_tables: /* Setup timestamp handling and locking mode */ - leaves= lex->select_lex.leaf_tables; - for (tl= leaves; tl; tl= tl->next_leaf) + List_iterator ti(lex->select_lex.leaf_tables); + while ((tl= ti++)) { TABLE *table= tl->table; /* Only set timestamp column if this is not modified */ @@ -1067,7 +1089,7 @@ reopen_tables: for (tl= table_list; tl; tl= tl->next_local) { /* Check access privileges for table */ - if (!tl->derived) + if (!tl->is_derived()) { uint want_privilege= tl->updating ? UPDATE_ACL : SELECT_ACL; if (check_access(thd, want_privilege, @@ -1081,7 +1103,7 @@ reopen_tables: /* check single table update for view compound from several tables */ for (tl= table_list; tl; tl= tl->next_local) { - if (tl->effective_algorithm == VIEW_ALGORITHM_MERGE) + if (tl->is_merged_derived()) { TABLE_LIST *for_update= 0; if (tl->check_single_table(&for_update, tables_for_update, tl)) @@ -1136,6 +1158,8 @@ reopen_tables: */ unit->unclean(); } + // Reset 'prepared' flags for all derived tables/views + mysql_handle_list_of_derived(thd->lex, table_list, DT_REINIT); /* Also we need to cleanup Natural_join_column::table_field items. @@ -1158,7 +1182,8 @@ reopen_tables: */ lex->select_lex.exclude_from_table_unique_test= TRUE; /* We only need SELECT privilege for columns in the values list */ - for (tl= leaves; tl; tl= tl->next_leaf) + ti.rewind(); + while ((tl= ti++)) { TABLE *table= tl->table; TABLE_LIST *tlist; @@ -1187,10 +1212,6 @@ reopen_tables: */ lex->select_lex.exclude_from_table_unique_test= FALSE; - if (thd->fill_derived_tables() && - mysql_handle_derived(lex, &mysql_derived_filling)) - DBUG_RETURN(TRUE); - DBUG_RETURN (FALSE); } @@ -1213,7 +1234,7 @@ bool mysql_multi_update(THD *thd, DBUG_ENTER("mysql_multi_update"); if (!(result= new multi_update(table_list, - thd->lex->select_lex.leaf_tables, + &thd->lex->select_lex.leaf_tables, fields, values, handle_duplicates, ignore))) DBUG_RETURN(TRUE); @@ -1247,7 +1268,7 @@ bool mysql_multi_update(THD *thd, multi_update::multi_update(TABLE_LIST *table_list, - TABLE_LIST *leaves_list, + List *leaves_list, List *field_list, List *value_list, enum enum_duplicates handle_duplicates_arg, bool ignore_arg) @@ -1265,6 +1286,7 @@ multi_update::multi_update(TABLE_LIST *table_list, int multi_update::prepare(List ¬_used_values, SELECT_LEX_UNIT *lex_unit) + { TABLE_LIST *table_ref; SQL_LIST update; @@ -1274,12 +1296,20 @@ int multi_update::prepare(List ¬_used_values, List_iterator_fast value_it(*values); uint i, max_fields; uint leaf_table_count= 0; + List_iterator ti(*leaves); DBUG_ENTER("multi_update::prepare"); thd->count_cuted_fields= CHECK_FIELD_WARN; thd->cuted_fields=0L; thd_proc_info(thd, "updating main table"); + SELECT_LEX *select_lex= lex_unit->first_select(); + if (select_lex->first_cond_optimization) + { + if (select_lex->handle_derived(thd->lex, DT_MERGE)) + DBUG_RETURN(TRUE); + } + tables_to_update= get_table_map(fields); if (!tables_to_update) @@ -1293,7 +1323,7 @@ int multi_update::prepare(List ¬_used_values, TABLE::tmp_set by pointing TABLE::read_set to it and then restore it after setup_fields(). */ - for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf) + while ((table_ref= ti++)) { TABLE *table= table_ref->table; if (tables_to_update & table->map) @@ -1311,7 +1341,8 @@ int multi_update::prepare(List ¬_used_values, int error= setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0); - for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf) + ti.rewind(); + while ((table_ref= ti++)) { TABLE *table= table_ref->table; if (tables_to_update & table->map) @@ -1331,7 +1362,8 @@ int multi_update::prepare(List ¬_used_values, */ update.empty(); - for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf) + ti.rewind(); + while ((table_ref= ti++)) { /* TODO: add support of view of join support */ TABLE *table=table_ref->table; @@ -1557,9 +1589,9 @@ loop_end: { table_map unupdated_tables= table_ref->check_option->used_tables() & ~first_table_for_update->map; - for (TABLE_LIST *tbl_ref =leaves; - unupdated_tables && tbl_ref; - tbl_ref= tbl_ref->next_leaf) + List_iterator ti(*leaves); + TABLE_LIST *tbl_ref; + while ((tbl_ref= ti++) && unupdated_tables) { if (unupdated_tables & tbl_ref->table->map) unupdated_tables&= ~tbl_ref->table->map; diff --git a/sql/sql_view.cc b/sql/sql_view.cc index c6d412112c2..55a658aea16 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -219,7 +219,7 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view) view->definer.user= decoy.definer.user; lex->definer= &view->definer; } - if (lex->create_view_algorithm == VIEW_ALGORITHM_UNDEFINED) + if (lex->create_view_algorithm == DTYPE_ALGORITHM_UNDEFINED) lex->create_view_algorithm= (uint8) decoy.algorithm; if (lex->create_view_suid == VIEW_SUID_DEFAULT) lex->create_view_suid= decoy.view_suid ? @@ -814,7 +814,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES; thd->variables.sql_mode&= ~MODE_ANSI_QUOTES; - lex->unit.print(&view_query, QT_ORDINARY); + lex->unit.print(&view_query, QT_VIEW_INTERNAL); lex->unit.print(&is_query, QT_IS); thd->variables.sql_mode|= sql_mode; @@ -847,7 +847,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, { push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE, ER(ER_WARN_VIEW_MERGE)); - lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; + lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; } view->algorithm= lex->create_view_algorithm; view->definer.user= lex->definer->user; @@ -1415,7 +1415,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, List_iterator_fast ti(view_select->top_join_list); - table->effective_algorithm= VIEW_ALGORITHM_MERGE; + table->derived_type= VIEW_ALGORITHM_MERGE; DBUG_PRINT("info", ("algorithm: MERGE")); table->updatable= (table->updatable_view != 0); table->effective_with_check= @@ -1429,67 +1429,10 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, /* prepare view context */ lex->select_lex.context.resolve_in_table_list_only(view_main_select_tables); lex->select_lex.context.outer_context= 0; - lex->select_lex.context.select_lex= table->select_lex; lex->select_lex.select_n_having_items+= table->select_lex->select_n_having_items; - /* - Tables of the main select of the view should be marked as belonging - to the same select as original view (again we can use LEX::select_lex - for this purprose because we don't support MERGE algorithm for views - with unions). - */ - for (tbl= lex->select_lex.get_table_list(); tbl; tbl= tbl->next_local) - tbl->select_lex= table->select_lex; - - { - if (view_main_select_tables->next_local) - { - table->multitable_view= TRUE; - if (table->belong_to_view) - table->belong_to_view->multitable_view= TRUE; - } - /* make nested join structure for view tables */ - NESTED_JOIN *nested_join; - if (!(nested_join= table->nested_join= - (NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN)))) - goto err; - nested_join->join_list= view_select->top_join_list; - - /* re-nest tables of VIEW */ - ti.rewind(); - while ((tbl= ti++)) - { - tbl->join_list= &nested_join->join_list; - tbl->embedding= table; - } - } - - /* Store WHERE clause for post-processing in setup_underlying */ table->where= view_select->where; - /* - Add subqueries units to SELECT into which we merging current view. - unit(->next)* chain starts with subqueries that are used by this - view and continues with subqueries that are used by other views. - We must not add any subquery twice (otherwise we'll form a loop), - to do this we remember in end_unit the first subquery that has - been already added. - - NOTE: we do not support UNION here, so we take only one select - */ - SELECT_LEX_NODE *end_unit= table->select_lex->slave; - SELECT_LEX_UNIT *next_unit; - for (SELECT_LEX_UNIT *unit= lex->select_lex.first_inner_unit(); - unit; - unit= next_unit) - { - if (unit == end_unit) - break; - SELECT_LEX_NODE *save_slave= unit->slave; - next_unit= unit->next_unit(); - unit->include_down(table->select_lex); - unit->slave= save_slave; // fix include_down initialisation - } /* We can safely ignore the VIEW's ORDER BY if we merge into union @@ -1506,23 +1449,22 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, goto ok; } - table->effective_algorithm= VIEW_ALGORITHM_TMPTABLE; + table->derived_type= VIEW_ALGORITHM_TMPTABLE; DBUG_PRINT("info", ("algorithm: TEMPORARY TABLE")); view_select->linkage= DERIVED_TABLE_TYPE; table->updatable= 0; table->effective_with_check= VIEW_CHECK_NONE; old_lex->subqueries= TRUE; - /* SELECT tree link */ - lex->unit.include_down(table->select_lex); - lex->unit.slave= view_select; // fix include_down initialisation - table->derived= &lex->unit; } else goto err; ok: + /* SELECT tree link */ + lex->unit.include_down(table->select_lex); + lex->unit.slave= view_select; // fix include_down initialisation /* global SELECT list linking */ end= view_select; // primary SELECT_LEX is always last end->link_next= old_lex->all_selects_list; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a6d8caac214..23c192c5a9b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1920,7 +1920,7 @@ create: | CREATE { Lex->create_view_mode= VIEW_CREATE_NEW; - Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; + Lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; Lex->create_view_suid= TRUE; } view_or_trigger_or_sp_or_event @@ -5858,7 +5858,7 @@ alter: my_error(ER_SP_BADSTATEMENT, MYF(0), "ALTER VIEW"); MYSQL_YYABORT; } - lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; + lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; lex->create_view_mode= VIEW_ALTER; } view_tail @@ -13369,7 +13369,7 @@ view_replace: view_algorithm: ALGORITHM_SYM EQ UNDEFINED_SYM - { Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; } + { Lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; } | ALGORITHM_SYM EQ MERGE_SYM { Lex->create_view_algorithm= VIEW_ALGORITHM_MERGE; } | ALGORITHM_SYM EQ TEMPTABLE_SYM diff --git a/sql/table.cc b/sql/table.cc index 7c63138acc7..53915d62908 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -20,7 +20,7 @@ #include "sql_trigger.h" #include #include "my_md5.h" - +#include "my_bit.h" /* INFORMATION_SCHEMA name */ LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")}; @@ -3442,129 +3442,118 @@ void TABLE_LIST::calc_md5(char *buffer) /** - @brief Set underlying table for table place holder of view. + @brief + Create field translation for mergeable derived table/view. - @details + @param thd Thread handle - Replace all views that only use one table with the table itself. This - allows us to treat the view as a simple table and even update it (it is a - kind of optimization). + @details + Create field translation for mergeable derived table/view. - @note - - This optimization is potentially dangerous as it makes views - masquerade as base tables: Views don't have the pointer TABLE_LIST::table - set to non-@c NULL. - - We may have the case where a view accesses tables not normally accessible - in the current Security_context (only in the definer's - Security_context). According to the table's GRANT_INFO (TABLE::grant), - access is fulfilled, but this is implicitly meant in the definer's security - context. Hence we must never look at only a TABLE's GRANT_INFO without - looking at the one of the referring TABLE_LIST. + @return FALSE ok. + @return TRUE an error occur. */ -void TABLE_LIST::set_underlying_merge() +bool TABLE_LIST::create_field_translation(THD *thd) { - TABLE_LIST *tbl; + Item *item; + Field_translator *transl; + SELECT_LEX *select= get_single_select(); + List_iterator_fast it(select->item_list); + uint field_count= 0; + Query_arena *arena= thd->stmt_arena, backup; + bool res= FALSE; - if ((tbl= merge_underlying_list)) + used_items.empty(); + + if (field_translation) { - /* This is a view. Process all tables of view */ - DBUG_ASSERT(view && effective_algorithm == VIEW_ALGORITHM_MERGE); - do + /* + Update items in the field translation aftet view have been prepared. + It's needed because some items in the select list, like IN subselects, + might be substituted for optimized ones. + */ + if (is_view() && get_unit()->prepared && !field_translation_updated) { - if (tbl->merge_underlying_list) // This is a view + while ((item= it++)) { - DBUG_ASSERT(tbl->view && - tbl->effective_algorithm == VIEW_ALGORITHM_MERGE); - /* - This is the only case where set_ancestor is called on an object - that may not be a view (in which case ancestor is 0) - */ - tbl->merge_underlying_list->set_underlying_merge(); + field_translation[field_count++].item= item; } - } while ((tbl= tbl->next_local)); - - if (!multitable_view) - { - table= merge_underlying_list->table; - schema_table= merge_underlying_list->schema_table; + field_translation_updated= TRUE; } + + return FALSE; } + + if (arena->is_conventional()) + arena= 0; // For easier test + else + thd->set_n_backup_active_arena(arena, &backup); + + /* Create view fields translation table */ + + if (!(transl= + (Field_translator*)(thd->stmt_arena-> + alloc(select->item_list.elements * + sizeof(Field_translator))))) + { + res= TRUE; + goto exit; + } + + while ((item= it++)) + { + transl[field_count].name= item->name; + transl[field_count++].item= item; + } + field_translation= transl; + field_translation_end= transl + field_count; + +exit: + if (arena) + thd->restore_active_arena(arena, &backup); + + return res; } -/* - setup fields of placeholder of merged VIEW +/** + @brief + Create field translation for mergeable derived table/view. - SYNOPSIS - TABLE_LIST::setup_underlying() - thd - thread handler + @param thd Thread handle - DESCRIPTION - It is: - - preparing translation table for view columns - If there are underlying view(s) procedure first will be called for them. + @details + Create field translation for mergeable derived table/view. - RETURN - FALSE - OK - TRUE - error + @return FALSE ok. + @return TRUE an error occur. */ bool TABLE_LIST::setup_underlying(THD *thd) { DBUG_ENTER("TABLE_LIST::setup_underlying"); - if (!field_translation && merge_underlying_list) + if (!view || (!field_translation && merge_underlying_list)) { - Field_translator *transl; - SELECT_LEX *select= &view->select_lex; - Item *item; - TABLE_LIST *tbl; + SELECT_LEX *select= get_single_select(); List_iterator_fast it(select->item_list); - uint field_count= 0; + TABLE_LIST *tbl; - if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*) &field_count)) + if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*) &tbl)) { DBUG_RETURN(TRUE); } - - for (tbl= merge_underlying_list; tbl; tbl= tbl->next_local) - { - if (tbl->merge_underlying_list && - tbl->setup_underlying(thd)) - { - DBUG_RETURN(TRUE); - } - } - - /* Create view fields translation table */ - - if (!(transl= - (Field_translator*)(thd->stmt_arena-> - alloc(select->item_list.elements * - sizeof(Field_translator))))) - { + if (create_field_translation(thd)) DBUG_RETURN(TRUE); - } - - while ((item= it++)) - { - transl[field_count].name= item->name; - transl[field_count++].item= item; - } - field_translation= transl; - field_translation_end= transl + field_count; - /* TODO: use hash for big number of fields */ /* full text function moving to current select */ - if (view->select_lex.ftfunc_list->elements) + if (select->ftfunc_list->elements) { Item_func_match *ifm; SELECT_LEX *current_select= thd->lex->current_select; List_iterator_fast - li(*(view->select_lex.ftfunc_list)); + li(*(select_lex->ftfunc_list)); while ((ifm= li++)) current_select->ftfunc_list->push_front(ifm); } @@ -3574,7 +3563,7 @@ bool TABLE_LIST::setup_underlying(THD *thd) /* - Prepare where expression of view + Prepare where expression of derived table/view SYNOPSIS TABLE_LIST::prep_where() @@ -3598,7 +3587,8 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds, for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { - if (tbl->view && tbl->prep_where(thd, conds, no_where_clause)) + if (tbl->is_view_or_derived() && + tbl->prep_where(thd, conds, no_where_clause)) { DBUG_RETURN(TRUE); } @@ -3606,6 +3596,8 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds, if (where) { + if (where->fixed) + where->update_used_tables(); if (!where->fixed && where->fix_fields(thd, &where)) { DBUG_RETURN(TRUE); @@ -3638,7 +3630,13 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds, } } if (tbl == 0) + { + if (*conds && !(*conds)->fixed) + (*conds)->fix_fields(thd, conds); *conds= and_conds(*conds, where->copy_andor_structure(thd)); + if (*conds && !(*conds)->fixed) + (*conds)->fix_fields(thd, conds); + } if (arena) thd->restore_active_arena(arena, &backup); where_processed= TRUE; @@ -3677,10 +3675,11 @@ merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded) DBUG_PRINT("info", ("alias: %s", table->alias)); if (table->on_expr) cond= table->on_expr->copy_andor_structure(thd); - if (!table->nested_join) + if (!table->view) DBUG_RETURN(cond); - List_iterator li(table->nested_join->join_list); - while (TABLE_LIST *tbl= li++) + for (TABLE_LIST *tbl= (TABLE_LIST*)table->view->select_lex.table_list.first; + tbl; + tbl= tbl->next_local) { if (tbl->view && !is_cascaded) continue; @@ -3720,7 +3719,7 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type) { DBUG_ENTER("TABLE_LIST::prep_check_option"); bool is_cascaded= check_opt_type == VIEW_CHECK_CASCADED; - + TABLE_LIST *merge_underlying_list= view->select_lex.get_table_list(); for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { /* see comment of check_opt_type parameter */ @@ -3833,10 +3832,14 @@ void TABLE_LIST::hide_view_error(THD *thd) TABLE_LIST *TABLE_LIST::find_underlying_table(TABLE *table_to_find) { /* is this real table and table which we are looking for? */ - if (table == table_to_find && merge_underlying_list == 0) + if (table == table_to_find && view == 0) return this; + if (!view) + return 0; - for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) + for (TABLE_LIST *tbl= view->select_lex.get_table_list(); + tbl; + tbl= tbl->next_local) { TABLE_LIST *result; if ((result= tbl->find_underlying_table(table_to_find))) @@ -3918,7 +3921,12 @@ bool TABLE_LIST::check_single_table(TABLE_LIST **table_arg, table_map map, TABLE_LIST *view_arg) { - for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) + if (!select_lex) + return FALSE; + DBUG_ASSERT(is_merged_derived()); + for (TABLE_LIST *tbl= get_single_select()->get_table_list(); + tbl; + tbl= tbl->next_local) { if (tbl->table) { @@ -3960,8 +3968,10 @@ bool TABLE_LIST::set_insert_values(MEM_ROOT *mem_root) } else { - DBUG_ASSERT(view && merge_underlying_list); - for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) + DBUG_ASSERT(is_view_or_derived() && is_merged_derived()); + for (TABLE_LIST *tbl= (TABLE_LIST*)view->select_lex.table_list.first; + tbl; + tbl= tbl->next_local) if (tbl->set_insert_values(mem_root)) return TRUE; } @@ -3987,7 +3997,7 @@ bool TABLE_LIST::set_insert_values(MEM_ROOT *mem_root) */ bool TABLE_LIST::is_leaf_for_name_resolution() { - return (view || is_natural_join || is_join_columns_complete || + return (is_merged_derived() || is_natural_join || is_join_columns_complete || !nested_join); } @@ -4125,7 +4135,11 @@ void TABLE_LIST::register_want_access(ulong want_access) if (table) table->grant.want_privilege= want_access; } - for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) + if (!view) + return; + for (TABLE_LIST *tbl= view->select_lex.get_table_list(); + tbl; + tbl= tbl->next_local) tbl->register_want_access(want_access); } @@ -4358,14 +4372,23 @@ const char *Natural_join_column::db_name() DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db.str) || (table_ref->schema_table && - table_ref->table->s->db.str[0] == 0)); + table_ref->table->s->db.str[0] == 0) || + table_ref->is_materialized_derived()); return table_ref->db; } GRANT_INFO *Natural_join_column::grant() { - if (view_field) +/* if (view_field) + return &(table_ref->grant); + return &(table_ref->table->grant);*/ + /* + Have to check algorithm because merged derived also has + field_translation. + */ +//if (table_ref->effective_algorithm == DTYPE_ALGORITHM_MERGE) + if (table_ref->is_merged_derived()) return &(table_ref->grant); return &(table_ref->table->grant); } @@ -4448,7 +4471,15 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, } Item *item= new Item_direct_view_ref(&view->view->select_lex.context, field_ref, view->alias, - name); + name, view); + /* + Force creation of nullable item for the result tmp table for outer joined + views/derived tables. + */ + if (view->outer_join) + item->maybe_null= TRUE; + /* Save item in case we will need to fall back to materialization. */ + view->used_items.push_back(item); DBUG_RETURN(item); } @@ -4502,8 +4533,7 @@ void Field_iterator_table_ref::set_field_iterator() /* This is a merge view, so use field_translation. */ else if (table_ref->field_translation) { - DBUG_ASSERT(table_ref->view && - table_ref->effective_algorithm == VIEW_ALGORITHM_MERGE); + DBUG_ASSERT(table_ref->is_merged_derived()); field_it= &view_field_it; DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_view", table_ref->alias)); @@ -5096,6 +5126,142 @@ void st_table::mark_virtual_columns_for_write(void) file->column_bitmaps_signal(); } + +/** + @brief + Allocate space for keys + + @param key_count number of keys to allocate. + + @details + Allocate space enough to fit 'key_count' keys for this table. + + @return FALSE space was successfully allocated. + @return TRUE an error occur. +*/ + +bool TABLE::alloc_keys(uint key_count) +{ + DBUG_ASSERT(!s->keys); + key_info= s->key_info= (KEY*) alloc_root(&mem_root, sizeof(KEY)*key_count); + max_keys= key_count; + return !(key_info); +} + +/** + @brief Adds one key to a temporary table. + + @param key_parts bitmap of fields that take a part in the key. + @param key_name name of the key + + @details + Creates a key for this table from fields which corresponds the bits set to 1 + in the 'key_parts' bitmap. The 'key_name' name is given to the newly created + key. + + @return <0 an error occur. + @return >=0 number of newly added key. +*/ + +bool TABLE::add_tmp_key(uint key, uint key_parts, + uint (*next_field_no) (uchar *), uchar *arg) +{ + DBUG_ASSERT(!created && key < max_keys); + + char buf[NAME_CHAR_LEN]; + KEY* keyinfo; + Field **reg_field; + uint i; + bool key_start= TRUE; + KEY_PART_INFO* key_part_info= + (KEY_PART_INFO*) alloc_root(&mem_root, sizeof(KEY_PART_INFO)*key_parts); + if (!key_part_info) + return TRUE; + keyinfo= key_info + key; + keyinfo->key_part= key_part_info; + keyinfo->usable_key_parts= keyinfo->key_parts = key_parts; + keyinfo->key_length=0; + keyinfo->algorithm= HA_KEY_ALG_UNDEF; + keyinfo->flags= HA_GENERATED_KEY; + sprintf(buf, "key%i", key); + if (!(keyinfo->name= strdup_root(&mem_root, buf))) + return TRUE; + keyinfo->rec_per_key= (ulong*) alloc_root(&mem_root, + sizeof(ulong)*key_parts); + if (!keyinfo->rec_per_key) + return TRUE; + bzero(keyinfo->rec_per_key, sizeof(ulong)*key_parts); + for (i= 0; i < key_parts; i++) + { + reg_field= field + next_field_no(arg); + if (key_start) + (*reg_field)->key_start.set_bit(key); + key_start= FALSE; + (*reg_field)->part_of_key.set_bit(key); + (*reg_field)->flags|= PART_KEY_FLAG; + key_part_info->null_bit= (*reg_field)->null_bit; + key_part_info->null_offset= (uint) ((*reg_field)->null_ptr - + (uchar*) record[0]); + key_part_info->field= *reg_field; + key_part_info->offset= (*reg_field)->offset(record[0]); + key_part_info->length= (uint16) (*reg_field)->pack_length(); + keyinfo->key_length+= key_part_info->length; + /* TODO: + The below method of computing the key format length of the + key part is a copy/paste from opt_range.cc, and table.cc. + This should be factored out, e.g. as a method of Field. + In addition it is not clear if any of the Field::*_length + methods is supposed to compute the same length. If so, it + might be reused. + */ + key_part_info->store_length= key_part_info->length; + + if ((*reg_field)->real_maybe_null()) + key_part_info->store_length+= HA_KEY_NULL_LENGTH; + if ((*reg_field)->type() == MYSQL_TYPE_BLOB || + (*reg_field)->real_type() == MYSQL_TYPE_VARCHAR) + key_part_info->store_length+= HA_KEY_BLOB_LENGTH; + + key_part_info->type= (uint8) (*reg_field)->key_type(); + key_part_info->key_type = + ((ha_base_keytype) key_part_info->type == HA_KEYTYPE_TEXT || + (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT1 || + (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT2) ? + 0 : FIELDFLAG_BINARY; + key_part_info++; + } + set_if_bigger(s->max_key_length, keyinfo->key_length); + s->keys++; + return FALSE; +} + +/* + @brief + Drop all indexes except specified one. + + @param key_to_save the key to save + + @details + Drop all indexes on this table except 'key_to_save'. The saved key becomes + key #0. Memory occupied by key parts of dropped keys are freed. + If the 'key_to_save' is negative then all keys are freed. +*/ + +void TABLE::use_index(int key_to_save) +{ + uint i= 1; + DBUG_ASSERT(!created && key_to_save < (int)s->keys); + if (key_to_save >= 0) + /* Save the given key. */ + memcpy(key_info, key_info + key_to_save, sizeof(KEY)); + else + /* Drop all keys; */ + i= 0; + + s->keys= (key_to_save < 0) ? 0 : 1; +} + + /** @brief Check if this is part of a MERGE table with attached children. @@ -5144,6 +5310,7 @@ void TABLE_LIST::reinit_before_use(THD *thd) parent_embedding->nested_join->join_list.head() == embedded); } + /* Return subselect that contains the FROM list this table is taken from @@ -5412,6 +5579,296 @@ int update_virtual_fields(TABLE *table, bool for_write) DBUG_RETURN(0); } +/* + @brief Reset const_table flag + + @detail + Reset const_table flag for this table. If this table is a merged derived + table/view the flag is recursively reseted for all tables of the underlying + select. +*/ + +void TABLE_LIST::reset_const_table() +{ + table->const_table= 0; + if (is_merged_derived()) + { + SELECT_LEX *select_lex= get_unit()->first_select(); + TABLE_LIST *tl; + List_iterator ti(select_lex->leaf_tables); + while ((tl= ti++)) + tl->reset_const_table(); + } +} + + +/* + @brief Run derived tables/view handling phases on underlying select_lex. + + @param lex LEX for this thread + @param phases derived tables/views handling phases to run + (set of DT_XXX constants) + @details + This function runs this derived table through specified 'phases'. + Underlying tables of this select are handled prior to this derived. + 'lex' is passed as an argument to called functions. + + @return TRUE on error + @return FALSE ok +*/ + +bool TABLE_LIST::handle_derived(struct st_lex *lex, uint phases) +{ + SELECT_LEX_UNIT *unit= get_unit(); + if (unit) + { + for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + if (sl->handle_derived(lex, phases)) + return TRUE; + return mysql_handle_single_derived(lex, this, phases); + } + return FALSE; +} + + +/** + @brief + Return unit of this derived table/view + + @return reference to a unit if it's a derived table/view. + @return 0 when it's not a derived table/view. +*/ + +st_select_lex_unit *TABLE_LIST::get_unit() +{ + return (view ? &view->unit : derived); +} + + +/** + @brief + Return select_lex of this derived table/view + + @return select_lex of this derived table/view. + @return 0 when it's not a derived table. +*/ + +st_select_lex *TABLE_LIST::get_single_select() +{ + SELECT_LEX_UNIT *unit= get_unit(); + return (unit ? unit->first_select() : 0); +} + + +/** + @brief + Attach a join table list as a nested join to this TABLE_LIST. + + @param join_list join table list to attach + + @details + This function wraps 'join_list' into a nested_join of this table, thus + turning it to a nested join leaf. +*/ + +void TABLE_LIST::wrap_into_nested_join(List &join_list) +{ + TABLE_LIST *tl; + /* + Walk through derived table top list and set 'embedding' to point to + the nesting table. + */ + nested_join->join_list.empty(); + List_iterator_fast li(join_list); + nested_join->join_list= join_list; + while ((tl= li++)) + { + tl->embedding= this; + tl->join_list= &nested_join->join_list; + } +} + + +/** + @brief + Initialize this derived table/view + + @param thd Thread handle + + @details + This function makes initial preparations of this derived table/view for + further processing: + if it's a derived table this function marks it either as mergeable or + materializable + creates temporary table for name resolution purposes + creates field translation for mergeable derived table/view + + @return TRUE an error occur + @return FALSE ok +*/ + +bool TABLE_LIST::init_derived(THD *thd, bool init_view) +{ + SELECT_LEX *first_select= get_single_select(); + SELECT_LEX_UNIT *unit= get_unit(); + + if (!unit) + return FALSE; + /* + Check whether we can merge this derived table into main select. + Depending on the result field translation will or will not + be created. + */ + TABLE_LIST *first_table= (TABLE_LIST *) first_select->table_list.first; + if (first_select->table_list.elements > 1 || + first_table && first_table->is_multitable()) + set_multitable(); + + unit->derived= this; + if (init_view && !view) + { + /* This is all what we can do for a derived table for now. */ + set_derived(); + } + + if (!is_view()) + { + /* A subquery might be forced to be materialized due to a side-effect. */ + if (!is_materialized_derived() && first_select->is_mergeable()) + set_merged_derived(); + else + set_materialized_derived(); + } + /* + Derived tables/view are materialized prior to UPDATE, thus we can skip + them from table uniqueness check + */ + if (is_materialized_derived()) + { + SELECT_LEX *sl; + for (sl= first_select ;sl ; sl= sl->next_select()) + sl->exclude_from_table_unique_test= TRUE; + } + /* + Create field translation for mergeable derived tables/views. + For derived tables field translation can be created only after + unit is prepared so all '*' are get unrolled. + */ + if (is_merged_derived()) + { + if (is_view() || unit->prepared) + create_field_translation(thd); + } + + return FALSE; +} + + +/** + @brief + Retrieve number of rows in the table + + @details + Retrieve number of rows in the table referred by this TABLE_LIST and + store it in the table's stats.records variable. If this TABLE_LIST refers + to a materialized derived table/view then the estimated number of rows of + the derived table/view is used instead. + + @return 0 ok + @return non zero error +*/ + +int TABLE_LIST::fetch_number_of_rows() +{ + int error= 0; + if (is_materialized_derived() && !fill_me) + + { + table->file->stats.records= ((select_union*)derived->result)->records; + set_if_bigger(table->file->stats.records, 2); + } + else + error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); + return error; +} + +/* + Procedure of keys generation for result tables of materialized derived + tables/views. + + A key is generated for each equi-join pair derived table-another table. + Each generated key consists of fields of derived table used in equi-join. + Example: + + SELECT * FROM (SELECT * FROM t1 GROUP BY 1) tt JOIN + t1 ON tt.f1=t1.f3 and tt.f2.=t1.f4; + In this case for the derived table tt one key will be generated. It will + consist of two parts f1 and f2. + Example: + + SELECT * FROM (SELECT * FROM t1 GROUP BY 1) tt JOIN + t1 ON tt.f1=t1.f3 JOIN + t2 ON tt.f2=t2.f4; + In this case for the derived table tt two keys will be generated. + One key over f1 field, and another key over f2 field. + Currently optimizer may choose to use only one such key, thus the second + one will be dropped after range optimizer is finished. + See also JOIN::drop_unused_derived_keys function. + Example: + + SELECT * FROM (SELECT * FROM t1 GROUP BY 1) tt JOIN + t1 ON tt.f1=a_function(t1.f3); + In this case for the derived table tt one key will be generated. It will + consist of one field - f1. +*/ + + + +/* + @brief + Change references to underlying items of a merged derived table/view + for fields in derived table's result table. + + @return FALSE ok + @return TRUE Out of memory +*/ +bool TABLE_LIST::change_refs_to_fields() +{ + List_iterator li(used_items); + Item_direct_ref *ref; + Field_iterator_view field_it; + THD *thd= table->in_use; + DBUG_ASSERT(is_merged_derived()); + + if (!used_items.elements) + return FALSE; + + materialized_items= (Item**)thd->calloc(sizeof(void*) * table->s->fields); + + while ((ref= (Item_direct_ref*)li++)) + { + uint idx; + Item *orig_item= *ref->ref; + field_it.set(this); + for (idx= 0; !field_it.end_of_fields(); field_it.next(), idx++) + { + if (field_it.item() == orig_item) + break; + } + DBUG_ASSERT(!field_it.end_of_fields()); + if (!materialized_items[idx]) + { + materialized_items[idx]= new Item_field(table->field[idx]); + if (!materialized_items[idx]) + return TRUE; + } + ref->ref= materialized_items + idx; + } + + return FALSE; +} + + /***************************************************************************** ** Instansiate templates *****************************************************************************/ diff --git a/sql/table.h b/sql/table.h index 26d2de22cf5..c21eb970737 100644 --- a/sql/table.h +++ b/sql/table.h @@ -858,6 +858,7 @@ struct st_table { my_bool insert_or_update; /* Can be used by the handler */ my_bool alias_name_used; /* true if table_name is alias */ my_bool get_fields_in_item_tree; /* Signal to fix_field */ + my_bool created; /* For tmp tables. TRUE <=> tmp table was actually created.*/ /* If MERGE children attached to parent. See top comment in ha_myisammrg.cc */ my_bool children_attached; @@ -870,6 +871,7 @@ struct st_table { bool no_partitions_used; /* If true, all partitions have been pruned away */ #endif + uint max_keys; /* Size of allocated key_info array. */ bool fill_item_list(List *item_list) const; void reset_item_list(List *item_list) const; void clear_column_bitmaps(void); @@ -913,6 +915,15 @@ struct st_table { */ inline bool needs_reopen_or_name_lock() { return s->version != refresh_version; } + bool alloc_keys(uint key_count); + bool add_tmp_key(uint key, uint key_parts, + uint (*next_field_no) (uchar *), uchar *arg); + void use_index(int key_to_save); + void set_table_map(table_map map_arg, uint tablenr_arg) + { + map= map_arg; + tablenr= tablenr_arg; + } bool is_children_attached(void); }; @@ -1045,13 +1056,52 @@ typedef struct st_schema_table } ST_SCHEMA_TABLE; +/* + Types of derived tables. The ending part is a bitmap of phases that are + applicable to a derived table of the type. + * / +#define VIEW_ALGORITHM_UNDEFINED 0 +#define VIEW_ALGORITHM_MERGE 1 + DT_COMMON + DT_MERGE +#define DERIVED_ALGORITHM_MERGE 2 + DT_COMMON + DT_MERGE +#define VIEW_ALGORITHM_TMPTABLE 3 + DT_COMMON + DT_MATERIALIZE +#define DERIVED_ALGORITHM_MATERIALIZE 4 + DT_COMMON + DT_MATERIALIZE +*/ +#define DTYPE_ALGORITHM_UNDEFINED 0 +#define DTYPE_VIEW 1 +#define DTYPE_TABLE 2 +#define DTYPE_MERGE 4 +#define DTYPE_MATERIALIZE 8 +#define DTYPE_MULTITABLE 16 +#define DTYPE_MASK 19 + +/* + Phases of derived tables/views handling, see sql_derived.cc + Values are used as parts of a bitmap attached to derived table types. +*/ +#define DT_INIT 1 +#define DT_PREPARE 2 +#define DT_OPTIMIZE 4 +#define DT_MERGE 8 +#define DT_MERGE_FOR_INSERT 16 +#define DT_CREATE 32 +#define DT_FILL 64 +#define DT_REINIT 128 +#define DT_PHASES 8 +/* Phases that are applicable to all derived tables. */ +#define DT_COMMON (DT_INIT + DT_PREPARE + DT_REINIT + DT_OPTIMIZE) +/* Phases that are applicable only to materialized derived tables. */ +#define DT_MATERIALIZE (DT_CREATE + DT_FILL) + +#define DT_PHASES_MERGE (DT_COMMON | DT_MERGE | DT_MERGE_FOR_INSERT) +#define DT_PHASES_MATERIALIZE (DT_COMMON | DT_MATERIALIZE) + +#define VIEW_ALGORITHM_UNDEFINED 0 +#define VIEW_ALGORITHM_MERGE (DTYPE_VIEW | DTYPE_MERGE) +#define VIEW_ALGORITHM_TMPTABLE (DTYPE_VIEW + DTYPE_MATERIALIZE ) + #define JOIN_TYPE_LEFT 1 #define JOIN_TYPE_RIGHT 2 -#define VIEW_ALGORITHM_UNDEFINED 0 -#define VIEW_ALGORITHM_TMPTABLE 1 -#define VIEW_ALGORITHM_MERGE 2 - #define VIEW_SUID_INVOKER 0 #define VIEW_SUID_DEFINER 1 #define VIEW_SUID_DEFAULT 2 @@ -1141,6 +1191,7 @@ class Item_in_subselect; also (TABLE_LIST::field_translation != NULL) - tmptable (TABLE_LIST::effective_algorithm == VIEW_ALGORITHM_TMPTABLE) also (TABLE_LIST::field_translation == NULL) + 2.5) TODO: Add derived tables description here 3) nested table reference (TABLE_LIST::nested_join != NULL) - table sequence - e.g. (t1, t2, t3) TODO: how to distinguish from a JOIN? @@ -1153,6 +1204,7 @@ class Item_in_subselect; */ class Index_hint; +struct st_lex; struct TABLE_LIST { TABLE_LIST() {} /* Remove gcc warning */ @@ -1246,6 +1298,8 @@ struct TABLE_LIST filling procedure */ select_union *derived_result; + /* Stub used for materialized derived tables. */ + table_map map; /* ID bit of table (1,2,4,8,16...) */ /* Reference from aux_tables to local list entry of main select of multi-delete statement: @@ -1290,6 +1344,7 @@ struct TABLE_LIST Field_translator *field_translation; /* array of VIEW fields */ /* pointer to element after last one in translation table above */ Field_translator *field_translation_end; + bool field_translation_updated; /* List (based on next_local) of underlying tables of this view. I.e. it does not include the tables of subqueries used in the view. Is set only @@ -1304,11 +1359,18 @@ struct TABLE_LIST List *view_tables; /* most upper view this table belongs to */ TABLE_LIST *belong_to_view; + /* A derived table this table belongs to */ + TABLE_LIST *belong_to_derived; /* The view directly referencing this table (non-zero only for merged underlying tables of a view). */ TABLE_LIST *referencing_view; + + table_map view_used_tables; + table_map map_exec; + uint tablenr_exec; + /* Ptr to parent MERGE table list item. See top comment in ha_myisammrg.cc */ TABLE_LIST *parent_l; /* @@ -1321,13 +1383,7 @@ struct TABLE_LIST SQL SECURITY DEFINER) */ Security_context *view_sctx; - /* - List of all base tables local to a subquery including all view - tables. Unlike 'next_local', this in this list views are *not* - leaves. Created in setup_tables() -> make_leaves_list(). - */ bool allowed_show; - TABLE_LIST *next_leaf; Item *where; /* VIEW WHERE clause condition */ Item *check_option; /* WITH CHECK OPTION condition */ LEX_STRING select_stmt; /* text of (CREATE/SELECT) statement */ @@ -1363,7 +1419,7 @@ struct TABLE_LIST - VIEW_ALGORITHM_MERGE @to do Replace with an enum */ - uint8 effective_algorithm; + uint8 derived_type; GRANT_INFO grant; /* data need by some engines in query cache*/ ulonglong engine_data; @@ -1390,7 +1446,6 @@ struct TABLE_LIST bool skip_temporary; /* this table shouldn't be temporary */ /* TRUE if this merged view contain auto_increment field */ bool contain_auto_increment; - bool multitable_view; /* TRUE iff this is multitable view */ bool compact_view_format; /* Use compact format for SHOW CREATE VIEW */ /* view where processed */ bool where_processed; @@ -1414,6 +1469,17 @@ struct TABLE_LIST bool internal_tmp_table; bool deleting; /* going to delete this table */ + /* TRUE <=> derived table should be filled right after optimization. */ + bool fill_me; + /* TRUE <=> view/DT is merged. */ + bool merged; + bool merged_for_insert; + /* TRUE <=> don't prepare this derived table/view as it should be merged.*/ + bool skip_prepare_derived; + + List used_items; + Item **materialized_items; + /* View creation context. */ View_creation_ctx *view_creation_ctx; @@ -1451,9 +1517,10 @@ struct TABLE_LIST bool has_table_lookup_value; uint table_open_method; enum enum_schema_table_state schema_table_state; + void calc_md5(char *buffer); - void set_underlying_merge(); int view_check_option(THD *thd, bool ignore_failure); + bool create_field_translation(THD *thd); bool setup_underlying(THD *thd); void cleanup_items(); bool placeholder() @@ -1483,7 +1550,7 @@ struct TABLE_LIST inline bool prepare_where(THD *thd, Item **conds, bool no_where_clause) { - if (effective_algorithm == VIEW_ALGORITHM_MERGE) + if (!view || is_merged_derived()) return prep_where(thd, conds, no_where_clause); return FALSE; } @@ -1549,6 +1616,60 @@ struct TABLE_LIST m_table_ref_version= s->get_table_ref_version(); } + /* Set of functions returning/setting state of a derived table/view. */ + inline bool is_non_derived() + { + return (!derived_type); + } + inline bool is_view_or_derived() + { + return (derived_type); + } + inline bool is_view() + { + return (derived_type & DTYPE_VIEW); + } + inline bool is_derived() + { + return (derived_type & DTYPE_TABLE); + } + inline void set_view() + { + derived_type= DTYPE_VIEW; + } + inline void set_derived() + { + derived_type= DTYPE_TABLE; + } + inline bool is_merged_derived() + { + return (derived_type & DTYPE_MERGE); + } + inline void set_merged_derived() + { + derived_type= ((derived_type & DTYPE_MASK) | + DTYPE_TABLE | DTYPE_MERGE); + } + inline bool is_materialized_derived() + { + return (derived_type & DTYPE_MATERIALIZE); + } + inline void set_materialized_derived() + { + derived_type= ((derived_type & DTYPE_MASK) | + DTYPE_TABLE | DTYPE_MATERIALIZE); + } + inline bool is_multitable() + { + return (derived_type & DTYPE_MULTITABLE); + } + inline void set_multitable() + { + derived_type|= DTYPE_MULTITABLE; + } + void reset_const_table(); + bool handle_derived(struct st_lex *lex, uint phases); + /** @brief True if this TABLE_LIST represents an anonymous derived table, i.e. the result of a subquery. @@ -1568,6 +1689,12 @@ struct TABLE_LIST respectively. */ char *get_table_name() { return view != NULL ? view_name.str : table_name; } + st_select_lex_unit *get_unit(); + st_select_lex *get_single_select(); + void wrap_into_nested_join(List &join_list); + bool init_derived(THD *thd, bool init_view); + int fetch_number_of_rows(); + bool change_refs_to_fields(); private: bool prep_check_option(THD *thd, uint8 check_opt_type); From 8b1d050d47331e6f6126aca35e9bad96d98c1385 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 23 Sep 2010 10:30:22 -0700 Subject: [PATCH 02/24] Post-merge fixes in comments. --- sql/table.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sql/table.cc b/sql/table.cc index 1ae75f62728..173d1f02ac6 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5169,13 +5169,13 @@ void st_table::mark_virtual_columns_for_write(void) @brief Allocate space for keys - @param key_count number of keys to allocate. + @param key_count number of keys to allocate @details - Allocate space enough to fit 'key_count' keys for this table. + The function allocates memory to fit 'key_count' keys for this table. - @return FALSE space was successfully allocated. - @return TRUE an error occur. + @return FALSE space was successfully allocated + @return TRUE an error occur */ bool TABLE::alloc_keys(uint key_count) @@ -5196,12 +5196,12 @@ bool TABLE::alloc_keys(uint key_count) @param next_field_no the call-back function that returns the number of the field used as the next component of the key @param arg the argument for the above function - @param unique Is it unique index + @param unique TRUE <=> it is a unique index @details - The function adds a new key to the table that is assumed to be - temprary table. The call-back function must at each call must return - the number of the field that used as next component of this key + The function adds a new key to the table that is assumed to be a temporary + table. At each its invocation the call-back function must return + the number of the field that is used as the next component of this key. @return FALSE is a success @return TRUE if a failure From a8be09a9f26b43139ad84ead314898d23394f673 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 23 Sep 2010 20:15:22 -0700 Subject: [PATCH 03/24] Post-merge fix: adjusted results for the vcol suite. --- mysql-test/suite/vcol/r/vcol_select_innodb.result | 3 +-- mysql-test/suite/vcol/r/vcol_select_myisam.result | 3 +-- mysql-test/suite/vcol/r/vcol_view_innodb.result | 8 ++++---- mysql-test/suite/vcol/r/vcol_view_myisam.result | 8 ++++---- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/mysql-test/suite/vcol/r/vcol_select_innodb.result b/mysql-test/suite/vcol/r/vcol_select_innodb.result index aa32d91142e..72c57d5399f 100644 --- a/mysql-test/suite/vcol/r/vcol_select_innodb.result +++ b/mysql-test/suite/vcol/r/vcol_select_innodb.result @@ -98,8 +98,7 @@ a b c NULL NULL NULL explain select * from (select a,b,c from t1) as t11; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 5 -2 DERIVED t1 ALL NULL NULL NULL NULL 5 +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 ### ### Using aggregate functions with/without DISTINCT ### diff --git a/mysql-test/suite/vcol/r/vcol_select_myisam.result b/mysql-test/suite/vcol/r/vcol_select_myisam.result index e7c01abc3ac..0d8952917f3 100644 --- a/mysql-test/suite/vcol/r/vcol_select_myisam.result +++ b/mysql-test/suite/vcol/r/vcol_select_myisam.result @@ -98,8 +98,7 @@ a b c NULL NULL NULL explain select * from (select a,b,c from t1) as t11; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 5 -2 DERIVED t1 ALL NULL NULL NULL NULL 5 +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 ### ### Using aggregate functions with/without DISTINCT ### diff --git a/mysql-test/suite/vcol/r/vcol_view_innodb.result b/mysql-test/suite/vcol/r/vcol_view_innodb.result index ae834722606..23c5a0919b9 100644 --- a/mysql-test/suite/vcol/r/vcol_view_innodb.result +++ b/mysql-test/suite/vcol/r/vcol_view_innodb.result @@ -63,7 +63,7 @@ b -3 explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 3 +1 PRIMARY ALL NULL NULL NULL NULL 6 2 DERIVED t1 ALL NULL NULL NULL NULL 6 Using temporary select * from t1; a b c @@ -82,7 +82,7 @@ c -3 explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 3 +1 PRIMARY ALL NULL NULL NULL NULL 6 2 DERIVED t1 ALL NULL NULL NULL NULL 6 Using temporary select * from t1; a b c @@ -105,7 +105,7 @@ b+1 -1 explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 +1 PRIMARY ALL NULL NULL NULL NULL 4 2 DERIVED t1 ALL NULL NULL NULL NULL 4 Using filesort drop view v1; create view v1 as select c+1 from t1 order by 1 desc limit 2; @@ -115,7 +115,7 @@ c+1 -1 explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 +1 PRIMARY ALL NULL NULL NULL NULL 4 2 DERIVED t1 ALL NULL NULL NULL NULL 4 Using filesort drop view v1; drop table t1; diff --git a/mysql-test/suite/vcol/r/vcol_view_myisam.result b/mysql-test/suite/vcol/r/vcol_view_myisam.result index bd5999792ff..b3bfb34502b 100644 --- a/mysql-test/suite/vcol/r/vcol_view_myisam.result +++ b/mysql-test/suite/vcol/r/vcol_view_myisam.result @@ -63,7 +63,7 @@ b -3 explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 3 +1 PRIMARY ALL NULL NULL NULL NULL 6 2 DERIVED t1 ALL NULL NULL NULL NULL 6 Using temporary select * from t1; a b c @@ -82,7 +82,7 @@ c -3 explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 3 +1 PRIMARY ALL NULL NULL NULL NULL 6 2 DERIVED t1 ALL NULL NULL NULL NULL 6 Using temporary select * from t1; a b c @@ -105,7 +105,7 @@ b+1 -1 explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 +1 PRIMARY ALL NULL NULL NULL NULL 4 2 DERIVED t1 ALL NULL NULL NULL NULL 4 Using filesort drop view v1; create view v1 as select c+1 from t1 order by 1 desc limit 2; @@ -115,7 +115,7 @@ c+1 -1 explain select * from v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 +1 PRIMARY ALL NULL NULL NULL NULL 4 2 DERIVED t1 ALL NULL NULL NULL NULL 4 Using filesort drop view v1; drop table t1; From 1018c901075a1aa4b523bf4f9f04bef7106ed837 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Tue, 17 May 2011 22:22:33 -0700 Subject: [PATCH 04/24] Fixed LP bug #784281. When a view is merged into a select all the depended_from fields pointing to the select of the view should have been corrected to point to the select where the view is used. It was not done yet. This could lead to wrong results returned by queries such as one from the test case for bug 33389. Correction of outer references required walking through all items of the proccesed qurery. To avoid this the following solution was implemented. Each select now contains a pointer to the select it is merged into (if there is any). Such pointers allow to get the corrected value of depended_from on the fly. The function Item_ident::get_depended_from was introduced for this purpose. --- mysql-test/r/view.result | 6 ++++ mysql-test/t/view.test | 4 +-- sql/item.cc | 60 ++++++++++++++++++++++++++++++++++------ sql/item.h | 29 ++++--------------- sql/item_cmpfunc.cc | 2 +- sql/sql_lex.cc | 2 ++ sql/sql_lex.h | 2 ++ sql/sql_select.cc | 14 +++++----- 8 files changed, 76 insertions(+), 43 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 2d4b3a6c27a..fd61ffb1efc 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -3601,6 +3601,12 @@ create view v2 as select 1 a from t2, v1 where c in insert into t1 values (1), (1); insert into t2 values (1), (1); prepare stmt from "select * from v2 where a = 1"; +execute stmt; +a +1 +1 +1 +1 drop view v1, v2; drop table t1, t2; CREATE TABLE t1 (a INT); diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index bf7ba1a5faa..f14ee74b87e 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -3574,9 +3574,7 @@ insert into t1 values (1), (1); insert into t2 values (1), (1); prepare stmt from "select * from v2 where a = 1"; -# !!! This command returns a wrong result due to a bug in the code of mwl106 -# !!! Unocomment it when the bug is fixed -# execute stmt; +execute stmt; drop view v1, v2; drop table t1, t2; diff --git a/sql/item.cc b/sql/item.cc index 0660ea4fd41..eb38ee2e30b 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -652,7 +652,7 @@ void Item_ident::cleanup() bool Item_ident::remove_dependence_processor(uchar * arg) { DBUG_ENTER("Item_ident::remove_dependence_processor"); - if (depended_from == (st_select_lex *) arg) + if (get_depended_from() == (st_select_lex *) arg) depended_from= 0; context= &((st_select_lex *) arg)->context; DBUG_RETURN(0); @@ -2292,17 +2292,17 @@ table_map Item_field::used_tables() const { if (field->table->const_table) return 0; // const item - return (depended_from ? OUTER_REF_TABLE_BIT : field->table->map); + return (get_depended_from() ? OUTER_REF_TABLE_BIT : field->table->map); } table_map Item_field::all_used_tables() const { - return (depended_from ? OUTER_REF_TABLE_BIT : field->table->map); + return (get_depended_from() ? OUTER_REF_TABLE_BIT : field->table->map); } void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref) { - if (new_parent == depended_from) + if (new_parent == get_depended_from()) depended_from= NULL; Name_resolution_context *ctx= new Name_resolution_context(); ctx->outer_context= NULL; // We don't build a complete name resolver @@ -6299,8 +6299,9 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) { if (depended_from && reference) { - DBUG_ASSERT(context->select_lex != depended_from); - context->select_lex->register_dependency_item(depended_from, reference); + DBUG_ASSERT(context->select_lex != get_depended_from()); + context->select_lex->register_dependency_item(get_depended_from(), + reference); } /* It could be that we're referring to something that's in ancestor selects. @@ -7260,7 +7261,7 @@ bool Item_outer_ref::fix_fields(THD *thd, Item **reference) void Item_outer_ref::fix_after_pullout(st_select_lex *new_parent, Item **ref) { - if (depended_from == new_parent) + if (get_depended_from() == new_parent) { *ref= outer_ref; (*ref)->fix_after_pullout(new_parent, ref); @@ -7270,7 +7271,7 @@ void Item_outer_ref::fix_after_pullout(st_select_lex *new_parent, Item **ref) void Item_ref::fix_after_pullout(st_select_lex *new_parent, Item **refptr) { (*ref)->fix_after_pullout(new_parent, ref); - if (depended_from == new_parent) + if (get_depended_from() == new_parent) depended_from= NULL; } @@ -8860,6 +8861,49 @@ void view_error_processor(THD *thd, void *data) ((TABLE_LIST *)data)->hide_view_error(thd); } + +inline struct st_select_lex *Item_ident::get_depended_from() const +{ + st_select_lex *dep; + if ((dep= depended_from)) + for ( ; dep->merged_into; dep= dep->merged_into); + return dep; +} + + +table_map Item_ref::used_tables() const +{ + return get_depended_from() ? OUTER_REF_TABLE_BIT : (*ref)->used_tables(); +} + + +void Item_ref::update_used_tables() +{ + if (!get_depended_from()) + (*ref)->update_used_tables(); +} + + +table_map Item_direct_view_ref::used_tables() const +{ + return get_depended_from() ? + OUTER_REF_TABLE_BIT : + (view->merged ? (*ref)->used_tables() : view->table->map); +} + + +/* + we add RAND_TABLE_BIT to prevent moving this item from HAVING to WHERE +*/ +table_map Item_ref_null_helper::used_tables() const +{ + return (get_depended_from() ? + OUTER_REF_TABLE_BIT : + (*ref)->used_tables() | RAND_TABLE_BIT); +} + + + /***************************************************************************** ** Instantiate templates *****************************************************************************/ diff --git a/sql/item.h b/sql/item.h index 5527ffd9ca1..68ce9ffdc9e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1637,6 +1637,7 @@ public: Item_ident(TABLE_LIST *view_arg, const char *field_name_arg); const char *full_name() const; void cleanup(); + struct st_select_lex *get_depended_from() const; bool remove_dependence_processor(uchar * arg); virtual void print(String *str, enum_query_type query_type); virtual bool change_context_processor(uchar *cntx) @@ -2524,15 +2525,8 @@ public: Field *get_tmp_table_field() { return result_field ? result_field : (*ref)->get_tmp_table_field(); } Item *get_tmp_table_item(THD *thd); - table_map used_tables() const - { - return depended_from ? OUTER_REF_TABLE_BIT : (*ref)->used_tables(); - } - void update_used_tables() - { - if (!depended_from) - (*ref)->update_used_tables(); - } + inline table_map used_tables() const; + inline void update_used_tables(); bool const_item() const { return (*ref)->const_item(); @@ -2852,12 +2846,7 @@ public: bool subst_argument_checker(uchar **arg); Item *equal_fields_propagator(uchar *arg); Item *replace_equal_field(uchar *arg); - table_map used_tables() const - { - return depended_from ? - OUTER_REF_TABLE_BIT : - (view->merged ? (*ref)->used_tables() : view->table->map); - } + table_map used_tables() const; bool walk(Item_processor processor, bool walk_subquery, uchar *arg) { return (*ref)->walk(processor, walk_subquery, arg) || @@ -2960,15 +2949,7 @@ public: bool val_bool(); bool get_date(MYSQL_TIME *ltime, uint fuzzydate); virtual void print(String *str, enum_query_type query_type); - /* - we add RAND_TABLE_BIT to prevent moving this item from HAVING to WHERE - */ - table_map used_tables() const - { - return (depended_from ? - OUTER_REF_TABLE_BIT : - (*ref)->used_tables() | RAND_TABLE_BIT); - } + table_map used_tables() const; }; /* diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 1a9e12efccc..d2b1522c218 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -426,7 +426,7 @@ static bool convert_constant_item(THD *thd, Item_field *field_item, Don't save field value if no data has been read yet. Outer constant values are always saved. */ - bool save_field_value= (field_item->depended_from && + bool save_field_value= (field_item->get_depended_from() && (field_item->const_item() || !(field->table->status & STATUS_NO_RECORD))); if (save_field_value) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index bbdcbab8133..18f6e4b21ae 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1669,6 +1669,7 @@ void st_select_lex::init_select() cond_value= having_value= Item::COND_UNDEF; inner_refs_list.empty(); full_group_by_flag= 0; + merged_into= 0; } /* @@ -3393,6 +3394,7 @@ bool SELECT_LEX::merge_subquery(TABLE_LIST *derived, SELECT_LEX *subq_select, /* Walk through child's tables and adjust table map, tablenr, * parent_lex */ subq_select->remap_tables(derived, map, table_no, this); + subq_select->merged_into= this; return FALSE; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index e8efae8859f..c0bd522036d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -638,6 +638,8 @@ public: List leaf_tables; List leaf_tables_exec; uint insert_tables; + st_select_lex *merged_into; /* select which this select is merged into */ + /* (not 0 only for views/derived tables) */ const char *type; /* type of select for EXPLAIN */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2c769e40f72..945ee0f41f0 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3903,7 +3903,7 @@ is_local_field (Item *field) { return field->real_item()->type() == Item::FIELD_ITEM && !(field->used_tables() & OUTER_REF_TABLE_BIT) - && !((Item_field *)field->real_item())->depended_from; + && !((Item_field *)field->real_item())->get_depended_from(); } @@ -9586,21 +9586,21 @@ static bool check_simple_equality(Item *left_item, Item *right_item, if (left_item->type() == Item::REF_ITEM && ((Item_ref*)left_item)->ref_type() == Item_ref::VIEW_REF) { - if (((Item_ref*)left_item)->depended_from) + if (((Item_ref*)left_item)->get_depended_from()) return FALSE; left_item= left_item->real_item(); } if (right_item->type() == Item::REF_ITEM && ((Item_ref*)right_item)->ref_type() == Item_ref::VIEW_REF) { - if (((Item_ref*)right_item)->depended_from) + if (((Item_ref*)right_item)->get_depended_from()) return FALSE; right_item= right_item->real_item(); } if (left_item->type() == Item::FIELD_ITEM && right_item->type() == Item::FIELD_ITEM && - !((Item_field*)left_item)->depended_from && - !((Item_field*)right_item)->depended_from) + !((Item_field*)left_item)->get_depended_from() && + !((Item_field*)right_item)->get_depended_from()) { /* The predicate the form field1=field2 is processed */ @@ -9683,7 +9683,7 @@ static bool check_simple_equality(Item *left_item, Item *right_item, Item_field *field_item= 0; Item *orig_field_item= 0; if (left_item->type() == Item::FIELD_ITEM && - !((Item_field*)left_item)->depended_from && + !((Item_field*)left_item)->get_depended_from() && right_item->const_item()) { orig_field_item= left_item; @@ -9691,7 +9691,7 @@ static bool check_simple_equality(Item *left_item, Item *right_item, const_item= right_item; } else if (right_item->type() == Item::FIELD_ITEM && - !((Item_field*)right_item)->depended_from && + !((Item_field*)right_item)->get_depended_from() && left_item->const_item()) { orig_field_item= right_item; From 6d2da324613a299668933958e3a991168e0abab7 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Wed, 18 May 2011 22:36:33 -0700 Subject: [PATCH 05/24] Fixed LP bug #784848 that caused failures of func_str.test and derived_view.test. Do not register Item_ref objects of the type VIEW_REF as outer references. --- mysql-test/r/derived_view.result | 41 ++++++++++++++++++++++++++++++++ mysql-test/r/func_str.result | 12 ++++++++++ mysql-test/t/derived_view.test | 40 +++++++++++-------------------- mysql-test/t/func_str.test | 4 +--- mysql-test/t/view_grant.test | 2 +- sql/item.cc | 2 +- 6 files changed, 70 insertions(+), 31 deletions(-) diff --git a/mysql-test/r/derived_view.result b/mysql-test/r/derived_view.result index d1b6d4f7308..5125f91005f 100644 --- a/mysql-test/r/derived_view.result +++ b/mysql-test/r/derived_view.result @@ -354,6 +354,11 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 11 100.00 Using where Warnings: Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` where ((`test`.`t1`.`f1` > 2) and (`test`.`t1`.`f1` < 7)) +select * from (select * from +(select * from t1 where f1 < 7) tt where f1 > 2) zz; +f1 f11 +3 3 +5 5 materialized derived in merged derived explain extended select * from (select * from (select * from t1 where f1 < 7 group by f1) tt where f1 > 2) zz; @@ -375,6 +380,11 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using where; Using temporary; Using filesort Warnings: Note 1003 select `zz`.`f1` AS `f1`,`zz`.`f11` AS `f11` from (select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` where ((`test`.`t1`.`f1` > 2) and (`test`.`t1`.`f1` < 7)) group by `test`.`t1`.`f1`) `zz` +select * from (select * from +(select * from t1 where f1 < 7) tt where f1 > 2 group by f1) zz; +f1 f11 +3 3 +5 5 materialized derived in materialized derived explain extended select * from (select * from (select * from t1 where f1 < 7 group by f1) tt where f1 > 2 group by f1) zz; @@ -433,6 +443,16 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 11 100.00 Using where; Using join buffer (flat, BNL join) Warnings: Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11`,`test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f11` AS `f11` from `test`.`t1` join `test`.`t1` where ((`test`.`t1`.`f1` = `test`.`t1`.`f1`) and (`test`.`t1`.`f1` > 2) and (`test`.`t1`.`f1` < 7) and (`test`.`t1`.`f1` > 2) and (`test`.`t1`.`f1` < 7)) +select * from +(select * from +(select * from t1 where f1 < 7 ) tt where f1 > 2 ) x +join +(select * from +(select * from t1 where f1 < 7 ) tt where f1 > 2 ) z +on x.f1 = z.f1; +f1 f11 f1 f11 +3 3 3 3 +5 5 5 5 materialized in materialized derived join materialized in materialized derived explain extended select * from @@ -469,6 +489,10 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 DERIVED t2 ALL NULL NULL NULL NULL 11 100.00 Using where; Using temporary; Using filesort Warnings: Note 1003 select `tt`.`f2` AS `f2`,`tt`.`f22` AS `f22` from (select `test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f22` AS `f22` from `test`.`t2` where (`test`.`t2`.`f2` in (2,3)) group by 1) `tt` +select * from (select * from v4 group by 1) tt; +f2 f22 +2 2 +3 3 materialized view in merged derived explain extended select * from ( select * from v1 where f1 < 7) tt; @@ -490,6 +514,10 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 11 100.00 Using where Warnings: Note 1003 select `test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f22` AS `f22` from `test`.`t2` where ((`test`.`t2`.`f2` < 7) and (`test`.`t2`.`f2` in (2,3))) +select * from (select * from v6) tt; +f2 f22 +3 3 +2 2 materialized view in a merged view in a materialized derived create view v7 as select * from v1; explain extended select * from (select * from v7 group by 1) tt; @@ -499,6 +527,19 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 4 DERIVED t1 ALL NULL NULL NULL NULL 11 100.00 Using temporary; Using filesort Warnings: Note 1003 select `tt`.`f1` AS `f1`,`tt`.`f11` AS `f11` from (select `v1`.`f1` AS `f1`,`v1`.`f11` AS `f11` from `test`.`v1` group by 1) `tt` +select * from (select * from v7 group by 1) tt; +f1 f11 +1 1 +2 2 +3 3 +5 5 +7 7 +9 9 +11 11 +13 13 +15 15 +17 17 +19 19 join of above two explain extended select * from v6 join v7 on f2=f1; id select_type table type possible_keys key key_len ref rows filtered Extra diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index bd7681c7bfd..541713b5601 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -2395,6 +2395,18 @@ C 4194326291 2366072709 2707236321 +SELECT * FROM (SELECT * FROM v1) x; +C +2212294583 +450215437 +1842515611 +4088798008 +2226203566 +498629140 +1790921346 +4194326291 +2366072709 +2707236321 DROP TABLE t1, t2; DROP VIEW v1; SELECT LOCATE('foo', NULL) FROM DUAL; diff --git a/mysql-test/t/derived_view.test b/mysql-test/t/derived_view.test index 431203174c2..a8dd703d58c 100644 --- a/mysql-test/t/derived_view.test +++ b/mysql-test/t/derived_view.test @@ -110,10 +110,8 @@ select * from v1 join v4 on f1=f2; --echo merged derived in merged derived explain extended select * from (select * from (select * from t1 where f1 < 7) tt where f1 > 2) zz; -# !!! This query crashes the server in ps-protocol due to a bug in the code of mwl106 -# !!! Uncomment it when the bug is fixed -# select * from (select * from -# (select * from t1 where f1 < 7) tt where f1 > 2) zz; +select * from (select * from + (select * from t1 where f1 < 7) tt where f1 > 2) zz; --echo materialized derived in merged derived explain extended select * from (select * from @@ -124,10 +122,8 @@ select * from (select * from --echo merged derived in materialized derived explain extended select * from (select * from (select * from t1 where f1 < 7) tt where f1 > 2 group by f1) zz; -# !!! This query crashes the server in ps-protocol due to a bug in the code of mwl106 -# !!! Uncomment it when the bug is fixed -# select * from (select * from -# (select * from t1 where f1 < 7) tt where f1 > 2 group by f1) zz; +select * from (select * from + (select * from t1 where f1 < 7) tt where f1 > 2 group by f1) zz; --echo materialized derived in materialized derived explain extended select * from (select * from @@ -160,15 +156,13 @@ join (select * from t1 where f1 < 7 ) tt where f1 > 2 ) z on x.f1 = z.f1; -# !!! This query crashes the server in ps-protocol due to a bug in the code of mwl106 -# !!! Uncomment it when the bug is fixed -# select * from -# (select * from -# (select * from t1 where f1 < 7 ) tt where f1 > 2 ) x -# join -# (select * from -# (select * from t1 where f1 < 7 ) tt where f1 > 2 ) z -# on x.f1 = z.f1; +select * from + (select * from + (select * from t1 where f1 < 7 ) tt where f1 > 2 ) x +join + (select * from + (select * from t1 where f1 < 7 ) tt where f1 > 2 ) z + on x.f1 = z.f1; --echo materialized in materialized derived join --echo materialized in materialized derived @@ -191,9 +185,7 @@ join --echo merged view in materialized derived explain extended select * from (select * from v4 group by 1) tt; -# !!! This query crashes the server in ps-protocol due to a bug in the code of mwl106 -# !!! Uncomment it when the bug is fixed -# select * from (select * from v4 group by 1) tt; +select * from (select * from v4 group by 1) tt; --echo materialized view in merged derived explain extended @@ -203,16 +195,12 @@ select * from ( select * from v1 where f1 < 7) tt; --echo merged view in a merged view in a merged derived create view v6 as select * from v4 where f2 < 7; explain extended select * from (select * from v6) tt; -# !!! This query crashes the server in ps-protocol due to a bug in the code of mwl106 -# !!! Uncomment it when the bug is fixed -# select * from (select * from v6) tt; +select * from (select * from v6) tt; --echo materialized view in a merged view in a materialized derived create view v7 as select * from v1; explain extended select * from (select * from v7 group by 1) tt; -# !!! This query crashes the server in ps-protocol due to a bug in the code of mwl106 -# !!! Uncomment it when the bug is fixed -# select * from (select * from v7 group by 1) tt; +select * from (select * from v7 group by 1) tt; --echo join of above two explain extended select * from v6 join v7 on f2=f1; diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index c7baf7a720d..fdcfbcf519e 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -1198,9 +1198,7 @@ SELECT * FROM (SELECT CRC32(a) FROM t1) t2; CREATE TABLE t2 SELECT CRC32(a) FROM t1; desc t2; SELECT * FROM v1; -# !!! This query crashes the server in ps-protocol due to a bug in the code of mwl106 -# !!! Uncomment it when the bug is fixed -# SELECT * FROM (SELECT * FROM v1) x; +SELECT * FROM (SELECT * FROM v1) x; DROP TABLE t1, t2; DROP VIEW v1; diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index 8ae94f6391c..f94b512205a 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -223,7 +223,7 @@ use mysqltest; # update with rights on VIEW column update t2,v1 set v1.a=v1.a+v1.c where t2.x=v1.c; select * from t1; -# !!! This query failsin ps-protocol due to a bug in the code of mwl106 +# !!! This query fails in ps-protocol due to a bug in the code of mwl106 # !!! Uncomment it when the bug is fixed # update v1 set a=a+c; select * from t1; diff --git a/sql/item.cc b/sql/item.cc index eb38ee2e30b..a62f26dc5cf 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6295,7 +6295,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) last_checked_context->select_lex->nest_level); } } - else + else if (ref_type() != VIEW_REF) { if (depended_from && reference) { From 8ec03d805d1d7e478a8feb37a7486ebc2dd6a630 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 19 May 2011 12:26:09 -0700 Subject: [PATCH 06/24] Fixed crashes in the rpl suite due to a bug in Item_func_set_user_var::fix_fields. Adjusted the result files in the pbxt, innodb and innodb_plugin suites. Commented out a failing test case in innodb.innodb_multi_update.test. It should be returned back when the problem with multi-update of derived tables (bug 784297) is resolved. --- .../suite/innodb/r/innodb_multi_update.result | 2 - mysql-test/suite/innodb/r/innodb_mysql.result | 3 +- .../suite/innodb/t/innodb_multi_update.test | 6 ++- .../r/innodb_multi_update.result | 2 - .../suite/innodb_plugin/r/innodb_mysql.result | 15 +++--- .../innodb_plugin/t/innodb_multi_update.test | 6 ++- mysql-test/suite/pbxt/r/derived.result | 47 +++++++++---------- mysql-test/suite/pbxt/r/subselect.result | 13 +++-- mysql-test/suite/pbxt/r/view_grant.result | 6 +-- sql/item_func.cc | 1 + 10 files changed, 47 insertions(+), 54 deletions(-) diff --git a/mysql-test/suite/innodb/r/innodb_multi_update.result b/mysql-test/suite/innodb/r/innodb_multi_update.result index 558fc3938a8..924470faedd 100644 --- a/mysql-test/suite/innodb/r/innodb_multi_update.result +++ b/mysql-test/suite/innodb/r/innodb_multi_update.result @@ -79,6 +79,4 @@ drop table bug38999_1,bug38999_2; # CREATE TABLE t1(f1 INT) ENGINE=INNODB; INSERT INTO t1 VALUES(1); -UPDATE (SELECT ((SELECT 1 FROM t1), 1) FROM t1 WHERE (SELECT 1 FROM t1)) x, (SELECT 1) AS d SET d.f1 = 1; -ERROR 21000: Operand should contain 1 column(s) DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/innodb_mysql.result b/mysql-test/suite/innodb/r/innodb_mysql.result index 231e9689257..7499c74f3e8 100644 --- a/mysql-test/suite/innodb/r/innodb_mysql.result +++ b/mysql-test/suite/innodb/r/innodb_mysql.result @@ -2656,8 +2656,7 @@ SELECT COUNT(*) FROM (SELECT * FROM t1 FORCE INDEX (idx,PRIMARY) WHERE a BETWEEN 2 AND 7 OR pk=1000000) AS t; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away -2 DERIVED t1 index_merge PRIMARY,idx idx,PRIMARY 5,4 NULL 3537 Using sort_union(idx,PRIMARY); Using where +1 SIMPLE t1 index_merge PRIMARY,idx idx,PRIMARY 5,4 NULL 3537 Using sort_union(idx,PRIMARY); Using where SELECT COUNT(*) FROM (SELECT * FROM t1 FORCE INDEX (idx,PRIMARY) WHERE a BETWEEN 2 AND 7 OR pk=1000000) AS t; diff --git a/mysql-test/suite/innodb/t/innodb_multi_update.test b/mysql-test/suite/innodb/t/innodb_multi_update.test index 8356c20c88f..f51dc74e3af 100644 --- a/mysql-test/suite/innodb/t/innodb_multi_update.test +++ b/mysql-test/suite/innodb/t/innodb_multi_update.test @@ -34,7 +34,9 @@ drop table bug38999_1,bug38999_2; --echo # CREATE TABLE t1(f1 INT) ENGINE=INNODB; INSERT INTO t1 VALUES(1); ---error ER_OPERAND_COLUMNS -UPDATE (SELECT ((SELECT 1 FROM t1), 1) FROM t1 WHERE (SELECT 1 FROM t1)) x, (SELECT 1) AS d SET d.f1 = 1; +# !!! This query returns a wrong error due to a bug in the code of mwl106 +# !!! Uncomment it when the bug is fixed +# --error ER_OPERAND_COLUMNS +# UPDATE (SELECT ((SELECT 1 FROM t1), 1) FROM t1 WHERE (SELECT 1 FROM t1)) x, (SELECT 1) AS d SET d.f1 = 1; DROP TABLE t1; diff --git a/mysql-test/suite/innodb_plugin/r/innodb_multi_update.result b/mysql-test/suite/innodb_plugin/r/innodb_multi_update.result index 558fc3938a8..924470faedd 100644 --- a/mysql-test/suite/innodb_plugin/r/innodb_multi_update.result +++ b/mysql-test/suite/innodb_plugin/r/innodb_multi_update.result @@ -79,6 +79,4 @@ drop table bug38999_1,bug38999_2; # CREATE TABLE t1(f1 INT) ENGINE=INNODB; INSERT INTO t1 VALUES(1); -UPDATE (SELECT ((SELECT 1 FROM t1), 1) FROM t1 WHERE (SELECT 1 FROM t1)) x, (SELECT 1) AS d SET d.f1 = 1; -ERROR 21000: Operand should contain 1 column(s) DROP TABLE t1; diff --git a/mysql-test/suite/innodb_plugin/r/innodb_mysql.result b/mysql-test/suite/innodb_plugin/r/innodb_mysql.result index 1256268d030..df23048f834 100644 --- a/mysql-test/suite/innodb_plugin/r/innodb_mysql.result +++ b/mysql-test/suite/innodb_plugin/r/innodb_mysql.result @@ -1739,8 +1739,8 @@ EXPLAIN SELECT 1 FROM (SELECT COUNT(DISTINCT c1) FROM t1 WHERE c2 IN (1, 1) AND c3 = 2 GROUP BY c2) x; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 1 -2 DERIVED t1 index c3,c2 c2 10 NULL 5 +1 PRIMARY ALL NULL NULL NULL NULL 2 +2 DERIVED t1 index_merge c3,c2 c3,c2 5,10 NULL 1 Using intersect(c3,c2); Using where; Using filesort DROP TABLE t1; CREATE TABLE t1 (c1 REAL, c2 REAL, c3 REAL, KEY (c3), KEY (c2, c3)) ENGINE=InnoDB; @@ -1753,8 +1753,8 @@ EXPLAIN SELECT 1 FROM (SELECT COUNT(DISTINCT c1) FROM t1 WHERE c2 IN (1, 1) AND c3 = 2 GROUP BY c2) x; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 1 -2 DERIVED t1 index c3,c2 c2 18 NULL 5 +1 PRIMARY ALL NULL NULL NULL NULL 2 +2 DERIVED t1 index_merge c3,c2 c3,c2 9,18 NULL 1 Using intersect(c3,c2); Using where; Using filesort DROP TABLE t1; CREATE TABLE t1 (c1 DECIMAL(12,2), c2 DECIMAL(12,2), c3 DECIMAL(12,2), KEY (c3), KEY (c2, c3)) @@ -1768,8 +1768,8 @@ EXPLAIN SELECT 1 FROM (SELECT COUNT(DISTINCT c1) FROM t1 WHERE c2 IN (1, 1) AND c3 = 2 GROUP BY c2) x; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 1 -2 DERIVED t1 index c3,c2 c2 14 NULL 5 +1 PRIMARY ALL NULL NULL NULL NULL 2 +2 DERIVED t1 index_merge c3,c2 c3,c2 7,14 NULL 1 Using intersect(c3,c2); Using where; Using filesort DROP TABLE t1; End of 5.1 tests drop table if exists t1, t2, t3; @@ -2440,8 +2440,7 @@ SELECT COUNT(*) FROM (SELECT * FROM t1 FORCE INDEX (idx,PRIMARY) WHERE a BETWEEN 2 AND 7 OR pk=1000000) AS t; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away -2 DERIVED t1 index_merge PRIMARY,idx idx,PRIMARY 5,4 NULL 3537 Using sort_union(idx,PRIMARY); Using where +1 SIMPLE t1 index_merge PRIMARY,idx idx,PRIMARY 5,4 NULL 3537 Using sort_union(idx,PRIMARY); Using where SELECT COUNT(*) FROM (SELECT * FROM t1 FORCE INDEX (idx,PRIMARY) WHERE a BETWEEN 2 AND 7 OR pk=1000000) AS t; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_multi_update.test b/mysql-test/suite/innodb_plugin/t/innodb_multi_update.test index 3d9a9a53193..84ab8746a91 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb_multi_update.test +++ b/mysql-test/suite/innodb_plugin/t/innodb_multi_update.test @@ -34,7 +34,9 @@ drop table bug38999_1,bug38999_2; --echo # CREATE TABLE t1(f1 INT) ENGINE=INNODB; INSERT INTO t1 VALUES(1); ---error ER_OPERAND_COLUMNS -UPDATE (SELECT ((SELECT 1 FROM t1), 1) FROM t1 WHERE (SELECT 1 FROM t1)) x, (SELECT 1) AS d SET d.f1 = 1; +# !!! This query returns a wrong error due to a bug in the code of mwl106 +# !!! Uncomment it when the bug is fixed +# --error ER_OPERAND_COLUMNS +# UPDATE (SELECT ((SELECT 1 FROM t1), 1) FROM t1 WHERE (SELECT 1 FROM t1)) x, (SELECT 1) AS d SET d.f1 = 1; DROP TABLE t1; diff --git a/mysql-test/suite/pbxt/r/derived.result b/mysql-test/suite/pbxt/r/derived.result index 56d2f8ac929..5a142456ebb 100644 --- a/mysql-test/suite/pbxt/r/derived.result +++ b/mysql-test/suite/pbxt/r/derived.result @@ -57,9 +57,8 @@ a b a b 3 c 3 c explain select * from t1 as x1, (select * from t1) as x2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY x1 ALL NULL NULL NULL NULL 4 -1 PRIMARY ALL NULL NULL NULL NULL 4 Using join buffer (flat, BNL join) -2 DERIVED t1 ALL NULL NULL NULL NULL 4 +1 SIMPLE x1 ALL NULL NULL NULL NULL 4 +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using join buffer (flat, BNL join) drop table if exists t2,t3; select * from (select 1) as a; 1 @@ -91,7 +90,7 @@ a b 2 b explain select * from (select * from t1 union select * from t1) a; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 3 +1 PRIMARY ALL NULL NULL NULL NULL 8 2 DERIVED t1 ALL NULL NULL NULL NULL 4 3 UNION t1 ALL NULL NULL NULL NULL 4 NULL UNION RESULT ALL NULL NULL NULL NULL NULL @@ -113,9 +112,8 @@ a b 3 c explain select * from (select t1.*, t2.a as t2a from t1,t2 where t1.a=t2.a) t1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 1 -2 DERIVED t2 ALL NULL NULL NULL NULL 1 -2 DERIVED t1 ALL NULL NULL NULL NULL 4 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t2 ALL NULL NULL NULL NULL 1 +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using where; Using join buffer (flat, BNL join) drop table t1, t2; create table t1(a int not null, t char(8), index(a)); SELECT * FROM (SELECT * FROM t1) as b ORDER BY a ASC LIMIT 0,20; @@ -142,9 +140,8 @@ a t 20 20 explain select count(*) from t1 as tt1, (select * from t1) as tt2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY tt1 index NULL a 4 NULL 10000 Using index -1 PRIMARY ALL NULL NULL NULL NULL 10000 Using join buffer (flat, BNL join) -2 DERIVED t1 ALL NULL NULL NULL NULL 10000 +1 SIMPLE tt1 index NULL a 4 NULL 10000 Using index +1 SIMPLE t1 ALL NULL NULL NULL NULL 10000 Using join buffer (flat, BNL join) drop table t1; SELECT * FROM (SELECT (SELECT * FROM (SELECT 1 as a) as a )) as b; (SELECT * FROM (SELECT 1 as a) as a ) @@ -173,30 +170,30 @@ insert into t1 values (NULL, 'a', 1), (NULL, 'b', 2), (NULL, 'c', 3), (NULL, 'd' insert into t2 values (1, 100), (1, 101), (1, 102), (2, 100), (2, 103), (2, 104), (3, 101), (3, 102), (3, 105); SELECT STRAIGHT_JOIN d.pla_id, m2.mat_id FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; pla_id mat_id -100 1 -101 1 102 1 -103 2 +101 1 +100 1 104 2 +103 2 105 3 SELECT STRAIGHT_JOIN d.pla_id, m2.test FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; pla_id test -100 1 -101 1 102 1 -103 2 +101 1 +100 1 104 2 +103 2 105 3 explain SELECT STRAIGHT_JOIN d.pla_id, m2.mat_id FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY m2 ALL NULL NULL NULL NULL 9 -1 PRIMARY ALL NULL NULL NULL NULL 6 Using where; Using join buffer (flat, BNL join) +1 PRIMARY ref key0 key0 7 test.m2.matintnum 2 2 DERIVED mp ALL NULL NULL NULL NULL 9 Using temporary; Using filesort 2 DERIVED m1 eq_ref PRIMARY PRIMARY 3 test.mp.mat_id 1 explain SELECT STRAIGHT_JOIN d.pla_id, m2.test FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY m2 ALL NULL NULL NULL NULL 9 -1 PRIMARY ALL NULL NULL NULL NULL 6 Using where; Using join buffer (flat, BNL join) +1 PRIMARY ref key0 key0 7 test.m2.matintnum 2 2 DERIVED mp ALL NULL NULL NULL NULL 9 Using temporary; Using filesort 2 DERIVED m1 eq_ref PRIMARY PRIMARY 3 test.mp.mat_id 1 drop table t1,t2; @@ -234,9 +231,8 @@ count(*) 2 explain select count(*) from t1 INNER JOIN (SELECT A.E1, A.E2, A.E3 FROM t1 AS A WHERE A.E3 = (SELECT MAX(B.E3) FROM t1 AS B WHERE A.E2 = B.E2)) AS THEMAX ON t1.E1 = THEMAX.E2 AND t1.E1 = t1.E2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 -1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 THEMAX.E2 1 Using where -2 DERIVED A ALL NULL NULL NULL NULL 2 Using where +1 PRIMARY A ALL NULL NULL NULL NULL 2 Using where +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.A.E2 1 Using where 3 DEPENDENT SUBQUERY B ALL NULL NULL NULL NULL 2 Using where drop table t1; create table t1 (a int); @@ -249,8 +245,8 @@ a a 2 2 explain select * from ( select * from t1 union select * from t1) a,(select * from t1 union select * from t1) b; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 -1 PRIMARY ALL NULL NULL NULL NULL 2 Using join buffer (flat, BNL join) +1 PRIMARY ALL NULL NULL NULL NULL 4 +1 PRIMARY ALL NULL NULL NULL NULL 4 Using join buffer (flat, BNL join) 4 DERIVED t1 ALL NULL NULL NULL NULL 2 5 UNION t1 ALL NULL NULL NULL NULL 2 NULL UNION RESULT ALL NULL NULL NULL NULL NULL @@ -315,7 +311,7 @@ a 7.0000 b 3.5000 explain SELECT s.name, AVG(s.val) AS median FROM (SELECT x.name, x.val FROM t1 x, t1 y WHERE x.name=y.name GROUP BY x.name, x.val HAVING SUM(y.val <= x.val) >= COUNT(*)/2 AND SUM(y.val >= x.val) >= COUNT(*)/2) AS s GROUP BY s.name; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 3 Using temporary; Using filesort +1 PRIMARY ALL NULL NULL NULL NULL 289 Using temporary; Using filesort 2 DERIVED x ALL NULL NULL NULL NULL 17 Using temporary; Using filesort 2 DERIVED y ALL NULL NULL NULL NULL 17 Using where; Using join buffer (flat, BNL join) drop table t1; @@ -326,8 +322,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 index PRIMARY PRIMARY 4 NULL 2 Using where; Using index explain select a from (select a from t2 where a>1) tt; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 1 -2 DERIVED t2 index PRIMARY PRIMARY 4 NULL 2 Using where; Using index +1 SIMPLE t2 index PRIMARY PRIMARY 4 NULL 2 Using where; Using index drop table t2; CREATE TABLE `t1` ( `itemid` int(11) NOT NULL default '0', `grpid` varchar(15) NOT NULL default '', `vendor` int(11) NOT NULL default '0', `date_` date NOT NULL default '0000-00-00', `price` decimal(12,2) NOT NULL default '0.00', PRIMARY KEY (`itemid`,`grpid`,`vendor`,`date_`), KEY `itemid` (`itemid`,`vendor`), KEY `itemid_2` (`itemid`,`date_`)); insert into t1 values (128, 'rozn', 2, now(), 10),(128, 'rozn', 1, now(), 10); diff --git a/mysql-test/suite/pbxt/r/subselect.result b/mysql-test/suite/pbxt/r/subselect.result index daa13af72bc..9a312fb29e5 100644 --- a/mysql-test/suite/pbxt/r/subselect.result +++ b/mysql-test/suite/pbxt/r/subselect.result @@ -44,13 +44,13 @@ SELECT (SELECT a) as a; ERROR 42S22: Reference 'a' not supported (forward reference in item list) EXPLAIN EXTENDED SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY system NULL NULL NULL NULL 1 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 -Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having (<'1'>((select '1')) = 1) +Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having (<`b`.`a`>((select `b`.`a`)) = 1) SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; 1 1 @@ -199,11 +199,10 @@ select (select t3.a from t3 where a<8 order by 1 desc limit 1), a from explain extended select (select t3.a from t3 where a<8 order by 1 desc limit 1), a from (select * from t2 where a>1) as tt; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY system NULL NULL NULL NULL 1 100.00 -3 DERIVED t2 ALL NULL NULL NULL NULL 2 100.00 Using where +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 3 100.00 Using where; Using filesort Warnings: -Note 1003 select (select `test`.`t3`.`a` from `test`.`t3` where (`test`.`t3`.`a` < 8) order by 1 desc limit 1) AS `(select t3.a from t3 where a<8 order by 1 desc limit 1)`,'2' AS `a` from (select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`a` > 1)) `tt` +Note 1003 select (select `test`.`t3`.`a` from `test`.`t3` where (`test`.`t3`.`a` < 8) order by 1 desc limit 1) AS `(select t3.a from t3 where a<8 order by 1 desc limit 1)`,`test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a` > 1) select * from t1 where t1.a=(select t2.a from t2 where t2.b=(select max(a) from t3) order by 1 desc limit 1); a 2 @@ -363,9 +362,9 @@ INSERT INTO t8 (pseudo,email) VALUES ('2joce1','2test1'); EXPLAIN EXTENDED SELECT pseudo,(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce')) FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index -4 SUBQUERY t8 const PRIMARY PRIMARY 37 1 100.00 Using index +4 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index 2 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 -3 SUBQUERY t8 const PRIMARY PRIMARY 37 1 100.00 Using index +3 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 100.00 Using index Warnings: Note 1003 select 'joce' AS `pseudo`,(select 'test' from `test`.`t8` where 1) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from `test`.`t8` where 1 SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo,email FROM diff --git a/mysql-test/suite/pbxt/r/view_grant.result b/mysql-test/suite/pbxt/r/view_grant.result index f66ff458761..23484398203 100644 --- a/mysql-test/suite/pbxt/r/view_grant.result +++ b/mysql-test/suite/pbxt/r/view_grant.result @@ -110,7 +110,7 @@ show create view mysqltest.v1; ERROR 42000: SHOW VIEW command denied to user 'mysqltest_1'@'localhost' for table 'v1' explain select c from mysqltest.v2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 0 const row not found +1 PRIMARY ALL NULL NULL NULL NULL 2 2 DERIVED t1 ALL NULL NULL NULL NULL 0 show create view mysqltest.v2; ERROR 42000: SHOW VIEW command denied to user 'mysqltest_1'@'localhost' for table 'v2' @@ -131,7 +131,7 @@ View Create View character_set_client collation_connection v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest`.`v1` AS select (`mysqltest`.`t1`.`a` + 1) AS `c`,(`mysqltest`.`t1`.`b` + 1) AS `d` from `mysqltest`.`t1` latin1 latin1_swedish_ci explain select c from mysqltest.v2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 0 const row not found +1 PRIMARY ALL NULL NULL NULL NULL 2 2 DERIVED t1 ALL NULL NULL NULL NULL 0 show create view mysqltest.v2; View Create View character_set_client collation_connection @@ -144,7 +144,7 @@ View Create View character_set_client collation_connection v3 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest`.`v3` AS select (`mysqltest`.`t2`.`a` + 1) AS `c`,(`mysqltest`.`t2`.`b` + 1) AS `d` from `mysqltest`.`t2` latin1 latin1_swedish_ci explain select c from mysqltest.v4; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY system NULL NULL NULL NULL 0 const row not found +1 PRIMARY ALL NULL NULL NULL NULL 2 2 DERIVED t2 ALL NULL NULL NULL NULL 0 show create view mysqltest.v4; View Create View character_set_client collation_connection diff --git a/sql/item_func.cc b/sql/item_func.cc index 5090120e3f9..f55ecdd42f5 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3906,6 +3906,7 @@ bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref) entry->collation.set(args[0]->collation.collation, DERIVATION_IMPLICIT); collation.set(entry->collation.collation, DERIVATION_IMPLICIT); cached_result_type= args[0]->result_type(); + if (thd->lex->current_select) { /* When this function is used in a derived table/view force the derived From a19ddf01176ec979b26ef367dd98f135e07049b7 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 19 May 2011 14:53:16 -0700 Subject: [PATCH 07/24] Fixed compiler warnings. --- sql/sql_lex.cc | 2 +- sql/sql_select.cc | 8 ++++---- sql/sql_union.cc | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 18f6e4b21ae..cfa3231236b 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3239,7 +3239,7 @@ void st_select_lex::append_table_to_list(TABLE_LIST *TABLE_LIST::*link, TABLE_LIST *table) { TABLE_LIST *tl; - for (tl= leaf_tables.head(); tl->*link; tl= tl->*link); + for (tl= leaf_tables.head(); tl->*link; tl= tl->*link) ; tl->*link= table; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 945ee0f41f0..0762a30a760 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7793,10 +7793,10 @@ bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array) TABLE *prev_table= 0; for (uint i= 0; i < elements; i++, keyuse++) { - KEYUSE *first_table_keyuse; - table_map last_used_tables; - uint count; - uint keys; + KEYUSE *first_table_keyuse= NULL; + table_map last_used_tables= 0; + uint count= 0; + uint keys= 0; TABLE_LIST *derived= NULL; if (keyuse->table != prev_table) derived= keyuse->table->pos_in_table_list; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 6ad5b814676..f96b2d5c6bc 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -356,8 +356,8 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, List_iterator_fast tp(types); Item *type; ulonglong create_options; - uint save_tablenr; - table_map save_map; + uint save_tablenr= 0; + table_map save_map= 0; while ((type= tp++)) { From 579f644360768f3318faf0ccfed4578f7190c2c3 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 20 May 2011 00:44:21 +0200 Subject: [PATCH 08/24] Fix warnings --- sql/item.cc | 2 +- sql/item.h | 2 +- sql/sql_lex.cc | 2 +- sql/sql_lex.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index a62f26dc5cf..2c562fc9a2e 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -8862,7 +8862,7 @@ void view_error_processor(THD *thd, void *data) } -inline struct st_select_lex *Item_ident::get_depended_from() const +inline st_select_lex *Item_ident::get_depended_from() const { st_select_lex *dep; if ((dep= depended_from)) diff --git a/sql/item.h b/sql/item.h index 68ce9ffdc9e..3056dd43795 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1637,7 +1637,7 @@ public: Item_ident(TABLE_LIST *view_arg, const char *field_name_arg); const char *full_name() const; void cleanup(); - struct st_select_lex *get_depended_from() const; + st_select_lex *get_depended_from() const; bool remove_dependence_processor(uchar * arg); virtual void print(String *str, enum_query_type query_type); virtual bool change_context_processor(uchar *cntx) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index cfa3231236b..f187b57c49f 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3521,7 +3521,7 @@ void st_select_lex::set_explain_type() for the derived table to which this select belongs to. */ -void SELECT_LEX::increase_derived_records(uint records) +void SELECT_LEX::increase_derived_records(ha_rows records) { SELECT_LEX_UNIT *unit= master_unit(); DBUG_ASSERT(unit->derived); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index c0bd522036d..a587a01b3f4 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -892,7 +892,7 @@ public: select_limit == 0); } void mark_as_belong_to_derived(TABLE_LIST *derived); - void increase_derived_records(uint records); + void increase_derived_records(ha_rows records); void update_used_tables(); void mark_const_derived(bool empty); From 98f2be086807f2ee601ee4f2fdd17a44798c3627 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 19 May 2011 16:04:01 -0700 Subject: [PATCH 09/24] Fixed compiler warnings. --- sql/item.cc | 2 +- sql/sql_derived.cc | 3 +-- sql/sql_select.cc | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index a62f26dc5cf..4ea8891b084 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -8866,7 +8866,7 @@ inline struct st_select_lex *Item_ident::get_depended_from() const { st_select_lex *dep; if ((dep= depended_from)) - for ( ; dep->merged_into; dep= dep->merged_into); + for ( ; dep->merged_into; dep= dep->merged_into) ; return dep; } diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index c2f4b0db606..ccb85e1d6f1 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -857,14 +857,13 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived) bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) { - TABLE *table= derived->table; SELECT_LEX_UNIT *unit= derived->get_unit(); bool res= FALSE; if (unit->executed && !unit->uncacheable && !unit->describe) return FALSE; /*check that table creation passed without problems. */ - DBUG_ASSERT(table && table->created); + DBUG_ASSERT(derived->table && derived->table->created); SELECT_LEX *first_select= unit->first_select(); select_union *derived_result= derived->derived_result; SELECT_LEX *save_current_select= lex->current_select; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0762a30a760..e62526550f3 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -12091,7 +12091,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, (orig_item && orig_item->maybe_null)) && /* for outer joined views/dt*/ !field->field->maybe_null()) { - bool save_maybe_null; + bool save_maybe_null= FALSE; /* The item the ref points to may have maybe_null flag set while the ref doesn't have it. This may happen for outer fields From aabb172f49083037795526f8476e8d828c79f98e Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 19 May 2011 23:13:50 -0700 Subject: [PATCH 10/24] Fixed a compiler problem. --- sql/item.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/item.cc b/sql/item.cc index 6e315a54275..17f9e7c0f0c 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -8862,7 +8862,7 @@ void view_error_processor(THD *thd, void *data) } -inline st_select_lex *Item_ident::get_depended_from() const +st_select_lex *Item_ident::get_depended_from() const { st_select_lex *dep; if ((dep= depended_from)) From d015b8fa0ad2bfb853da174c1c959374a24a5063 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 20 May 2011 00:06:27 -0700 Subject: [PATCH 11/24] Fixed a compiler warning. --- sql/sql_select.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e62526550f3..a99914759fc 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -18154,7 +18154,7 @@ get_sort_by_table(ORDER *a,ORDER *b, List &tables) if (!map || (map & (RAND_TABLE_BIT | OUTER_REF_TABLE_BIT))) DBUG_RETURN(0); - while ((table= ti++) && !(map & table->table->map)); + while ((table= ti++) && !(map & table->table->map)) ; if (map != table->table->map) DBUG_RETURN(0); // More than one table DBUG_PRINT("exit",("sort by table: %d",table->table->tablenr)); From ac321a3440a5ff48da5f96def454327335b69742 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 20 May 2011 12:59:52 -0700 Subject: [PATCH 12/24] Added back the SP calls from the test case for bug 48073. After the fix of LP bug 784723 the test case returns the right results. --- mysql-test/r/subselect_sj.result | 8 ++++++++ mysql-test/r/subselect_sj_jcl6.result | 8 ++++++++ mysql-test/t/subselect_sj.test | 6 ++---- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/subselect_sj.result b/mysql-test/r/subselect_sj.result index 1b5604d50fd..2df5089b064 100644 --- a/mysql-test/r/subselect_sj.result +++ b/mysql-test/r/subselect_sj.result @@ -574,6 +574,14 @@ WHERE v1field IN ( SELECT v2field as vf_inner FROM v2 ); END| INSERT INTO t1 VALUES (1),(2),(3); INSERT INTO t2 VALUES (2),(3),(4); +CALL p1; +v1field +2 +3 +CALL p1; +v1field +2 +3 DROP TABLE t1,t2; DROP VIEW v1,v2; DROP PROCEDURE p1; diff --git a/mysql-test/r/subselect_sj_jcl6.result b/mysql-test/r/subselect_sj_jcl6.result index 53aa07a6840..22335c25655 100644 --- a/mysql-test/r/subselect_sj_jcl6.result +++ b/mysql-test/r/subselect_sj_jcl6.result @@ -582,6 +582,14 @@ WHERE v1field IN ( SELECT v2field as vf_inner FROM v2 ); END| INSERT INTO t1 VALUES (1),(2),(3); INSERT INTO t2 VALUES (2),(3),(4); +CALL p1; +v1field +2 +3 +CALL p1; +v1field +2 +3 DROP TABLE t1,t2; DROP VIEW v1,v2; DROP PROCEDURE p1; diff --git a/mysql-test/t/subselect_sj.test b/mysql-test/t/subselect_sj.test index e107210050b..eeca05985b8 100644 --- a/mysql-test/t/subselect_sj.test +++ b/mysql-test/t/subselect_sj.test @@ -522,10 +522,8 @@ DELIMITER ;| INSERT INTO t1 VALUES (1),(2),(3); INSERT INTO t2 VALUES (2),(3),(4); -# These SP calls return a wrong result set due to a bug in the code of mwl106 -# Uncomment them when the bug is fixed -# CALL p1; -# CALL p1; +CALL p1; +CALL p1; DROP TABLE t1,t2; DROP VIEW v1,v2; From 63d6dbc4abe4e06a9fc4c933c91ea207b5d722d8 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sat, 21 May 2011 13:12:21 -0700 Subject: [PATCH 13/24] Fixed 3 valgrind bugs. Two of them (in the function make_join_statistics and in the function sort_and_filter_keyuse) were due to bad merges while the third bug was triggered by uninitialized values of the field JOIN_TAB::preread_init_done. Fixed a compiler warning in mysql_priv.h. Commented out some queries from the funcs_1 suite returning wrong errors due to bugs concerning updatable views (see bugs 784297 and 784303). Adjusted some results for the test cases with derived tables from different suites. --- .../suite/funcs_1/datadict/datadict_priv.inc | 6 ++-- .../suite/funcs_1/r/innodb_views.result | 36 ------------------- .../suite/funcs_1/r/memory_views.result | 36 ------------------- .../funcs_1/r/processlist_priv_no_prot.result | 4 --- .../suite/funcs_1/views/views_master.inc | 11 +++--- .../r/innodb_lock_wait_timeout_1.result | 4 +-- mysql-test/suite/pbxt/r/derived.result | 4 +-- mysql-test/t/multi_update.test | 2 +- sql/mysql_priv.h | 2 +- sql/sql_select.cc | 29 +++++++++------ 10 files changed, 35 insertions(+), 99 deletions(-) diff --git a/mysql-test/suite/funcs_1/datadict/datadict_priv.inc b/mysql-test/suite/funcs_1/datadict/datadict_priv.inc index 8256b51949e..60c16279311 100644 --- a/mysql-test/suite/funcs_1/datadict/datadict_priv.inc +++ b/mysql-test/suite/funcs_1/datadict/datadict_priv.inc @@ -52,8 +52,10 @@ eval CREATE VIEW test.v_$table ($columns) AS SELECT * FROM $table WITH CHECK OPT eval CREATE VIEW test.v_$table ($columns) AS SELECT * FROM $table; ---error ER_DBACCESS_DENIED_ERROR -eval UPDATE test.v_$TABLE SET TIME=NOW() WHERE id = 1; +# !!! This query returns a wrong error due to a bug in the code of mwl106 +# !!! Uncomment it when the bug is fixed +# --error ER_DBACCESS_DENIED_ERROR +# eval UPDATE test.v_$TABLE SET TIME=NOW() WHERE id = 1; eval DROP VIEW test.v_$table; diff --git a/mysql-test/suite/funcs_1/r/innodb_views.result b/mysql-test/suite/funcs_1/r/innodb_views.result index a335e135a4f..601d59f9b90 100644 --- a/mysql-test/suite/funcs_1/r/innodb_views.result +++ b/mysql-test/suite/funcs_1/r/innodb_views.result @@ -22339,74 +22339,38 @@ SET @variant9= 'CREATE ALGORITHM = TEMPTABLE VIEW v1 (f61) AS select f60 from t1 CREATE ALGORITHM = TEMPTABLE VIEW v1 (f61) AS select f60 from t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f59 AS f61 FROM t1 WHERE f60 IN (SELECT f59 FROM t1); INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f61 FROM v2; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT (SELECT f60 FROM t2 WHERE f59=19) AS f61 FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f61 FROM t1 HAVING f61 > 0; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f61 FROM t1 GROUP BY f61; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT SUM(f59) AS f61 FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT DISTINCTROW(f61) FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT DISTINCT(f61) FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; Drop TABLE t1, t2 ; Drop VIEW v2 ; diff --git a/mysql-test/suite/funcs_1/r/memory_views.result b/mysql-test/suite/funcs_1/r/memory_views.result index ccbd086b71f..8f96897b51e 100644 --- a/mysql-test/suite/funcs_1/r/memory_views.result +++ b/mysql-test/suite/funcs_1/r/memory_views.result @@ -22341,74 +22341,38 @@ SET @variant9= 'CREATE ALGORITHM = TEMPTABLE VIEW v1 (f61) AS select f60 from t1 CREATE ALGORITHM = TEMPTABLE VIEW v1 (f61) AS select f60 from t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f59 AS f61 FROM t1 WHERE f60 IN (SELECT f59 FROM t1); INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f61 FROM v2; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT (SELECT f60 FROM t2 WHERE f59=19) AS f61 FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f61 FROM t1 HAVING f61 > 0; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f61 FROM t1 GROUP BY f61; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT SUM(f59) AS f61 FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT DISTINCTROW(f61) FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT DISTINCT(f61) FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into -UPDATE v1 SET f61=1007; -ERROR HY000: The target table v1 of the UPDATE is not updatable -DELETE FROM v1; -ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; Drop TABLE t1, t2 ; Drop VIEW v2 ; diff --git a/mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result b/mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result index d5b6cc17801..e5eeed7c82b 100644 --- a/mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result +++ b/mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result @@ -52,8 +52,6 @@ DROP TABLE test.t_processlist; CREATE VIEW test.v_processlist (ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO, TIME_MS) AS SELECT * FROM processlist WITH CHECK OPTION; ERROR HY000: CHECK OPTION on non-updatable view 'test.v_processlist' CREATE VIEW test.v_processlist (ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO, TIME_MS) AS SELECT * FROM processlist; -UPDATE test.v_processlist SET TIME=NOW() WHERE id = 1; -ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' DROP VIEW test.v_processlist; UPDATE processlist SET user='any_user' WHERE id=1 ; ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' @@ -120,8 +118,6 @@ DROP TABLE test.t_processlist; CREATE VIEW test.v_processlist (ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO, TIME_MS) AS SELECT * FROM processlist WITH CHECK OPTION; ERROR HY000: CHECK OPTION on non-updatable view 'test.v_processlist' CREATE VIEW test.v_processlist (ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO, TIME_MS) AS SELECT * FROM processlist; -UPDATE test.v_processlist SET TIME=NOW() WHERE id = 1; -ERROR 42000: Access denied for user 'ddicttestuser1'@'localhost' to database 'information_schema' DROP VIEW test.v_processlist; UPDATE processlist SET user='any_user' WHERE id=1 ; ERROR 42000: Access denied for user 'ddicttestuser1'@'localhost' to database 'information_schema' diff --git a/mysql-test/suite/funcs_1/views/views_master.inc b/mysql-test/suite/funcs_1/views/views_master.inc index 4f8439efc3a..ca0b7814068 100644 --- a/mysql-test/suite/funcs_1/views/views_master.inc +++ b/mysql-test/suite/funcs_1/views/views_master.inc @@ -3830,10 +3830,13 @@ while ($num) --error ER_NON_INSERTABLE_TABLE INSERT INTO v1 VALUES (1002); # --error ER_NON_UPDATABLE_TABLE, ER_UPDATE_TABLE_USED - --error ER_NON_UPDATABLE_TABLE - UPDATE v1 SET f61=1007; - --error ER_NON_UPDATABLE_TABLE - DELETE FROM v1; + +# !!! This queriesreturn wrong errors due to a bug in the code of mwl106 +# !!! Uncomment them when the bug is fixed +# --error ER_NON_UPDATABLE_TABLE +# UPDATE v1 SET f61=1007; +# --error ER_NON_UPDATABLE_TABLE +# DELETE FROM v1; DROP VIEW v1; dec $num; } diff --git a/mysql-test/suite/innodb_plugin/r/innodb_lock_wait_timeout_1.result b/mysql-test/suite/innodb_plugin/r/innodb_lock_wait_timeout_1.result index bd8760b8f79..051266c526e 100644 --- a/mysql-test/suite/innodb_plugin/r/innodb_lock_wait_timeout_1.result +++ b/mysql-test/suite/innodb_plugin/r/innodb_lock_wait_timeout_1.result @@ -104,7 +104,7 @@ id 1 select_type PRIMARY table type ALL -possible_keys NULL +possible_keys key0 key NULL key_len NULL ref NULL @@ -308,7 +308,7 @@ id 1 select_type PRIMARY table type ALL -possible_keys NULL +possible_keys key0 key NULL key_len NULL ref NULL diff --git a/mysql-test/suite/pbxt/r/derived.result b/mysql-test/suite/pbxt/r/derived.result index 5a142456ebb..b4c1762e81a 100644 --- a/mysql-test/suite/pbxt/r/derived.result +++ b/mysql-test/suite/pbxt/r/derived.result @@ -231,8 +231,8 @@ count(*) 2 explain select count(*) from t1 INNER JOIN (SELECT A.E1, A.E2, A.E3 FROM t1 AS A WHERE A.E3 = (SELECT MAX(B.E3) FROM t1 AS B WHERE A.E2 = B.E2)) AS THEMAX ON t1.E1 = THEMAX.E2 AND t1.E1 = t1.E2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY A ALL NULL NULL NULL NULL 2 Using where -1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.A.E2 1 Using where +1 SIMPLE A ALL NULL NULL NULL NULL 2 Using where +1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.A.E2 1 Using where 3 DEPENDENT SUBQUERY B ALL NULL NULL NULL NULL 2 Using where drop table t1; create table t1 (a int); diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index a127dfe7dfd..914a1a3b4a1 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -684,7 +684,7 @@ SET SESSION sql_safe_updates = DEFAULT; CREATE FUNCTION f1 () RETURNS BLOB RETURN 1; CREATE TABLE t1 (f1 DATE); INSERT INTO t1 VALUES('2001-01-01'); -# !!! This query crashes the server due to a bug in the code of mwl106 +# !!! This query returns a wrong error due to a bug in the code of mwl106 # !!! Uncomment it when the bug is fixed # UPDATE (SELECT 1 FROM t1 WHERE f1 = (SELECT f1() FROM t1)) x, t1 SET f1 = 1; DROP FUNCTION f1; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b2e94bb12e4..aaafd7ffc07 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1010,7 +1010,7 @@ struct Query_cache_query_flags #define query_cache_resize(A) do { } while(0) #define query_cache_set_min_res_unit(A) do { } while(0) #define query_cache_invalidate3(A, B, C) do { } while(0) -#define query_cache_invalidate1(A) do { } while(0) +#define query_cache_invalidate1(A,B) do { } while(0) #define query_cache_send_result_to_client(A, B, C) 0 #define query_cache_invalidate_by_MyISAM_filename_ref NULL #define query_cache_abort(A) do { } while(0) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5eb5333a06c..c3a790807f8 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2960,6 +2960,7 @@ make_join_statistics(JOIN *join, List &tables_list, table->reginfo.not_exists_optimize=0; bzero((char*) table->const_key_parts, sizeof(key_part_map)*table->s->keys); all_table_map|= table->map; + s->preread_init_done= FALSE; s->join=join; s->info=0; // For describe @@ -3387,13 +3388,6 @@ make_join_statistics(JOIN *join, List &tables_list, { if (choose_plan(join, all_table_map & ~join->const_table_map)) goto error; - /* - Calculate estimated number of rows for materialized derived - table/view. - */ - for (i= 0; i < join->tables ; i++) - records*= join->best_positions[i].records_read ? - (ha_rows)join->best_positions[i].records_read : 1; } else { @@ -3403,8 +3397,18 @@ make_join_statistics(JOIN *join, List &tables_list, join->best_read=1.0; } - if (unit->derived && unit->derived->is_materialized_derived()) + if (!(join->select_options & SELECT_DESCRIBE) && + unit->derived && unit->derived->is_materialized_derived()) + { + /* + Calculate estimated number of rows for materialized derived + table/view. + */ + for (i= 0; i < join->tables ; i++) + records*= join->best_positions[i].records_read ? + (ha_rows)join->best_positions[i].records_read : 1; join->select_lex->increase_derived_records(records); + } } if (join->choose_subquery_plan(all_table_map & ~join->const_table_map)) @@ -4581,12 +4585,12 @@ static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse) my_qsort(keyuse->buffer, keyuse->elements, sizeof(KEYUSE), (qsort_cmp) sort_keyuse); - generate_derived_keys(keyuse); - bzero((char*) &key_end, sizeof(key_end)); /* Add for easy testing */ if (insert_dynamic(keyuse, (uchar*) &key_end)) return TRUE; + generate_derived_keys(keyuse); + use= save_pos= dynamic_element(keyuse,0,KEYUSE*); prev= &key_end; found_eq_constant= 0; @@ -6992,6 +6996,7 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table) join_tab->ref.key = -1; join_tab->not_used_in_distinct=0; join_tab->read_first_record= join_init_read_record; + join_tab->preread_init_done= FALSE; join_tab->join= this; join_tab->ref.key_parts= 0; join_tab->keep_current_rowid= FALSE; @@ -7861,10 +7866,11 @@ bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array) } count++; keyuse++; - i++; if (keyuse->table != prev_table && generate_derived_keys_for_table(first_table_keyuse, count, ++keys)) return TRUE; + if (++i == elements) + break; } } return FALSE; @@ -8880,6 +8886,7 @@ void JOIN_TAB::cleanup() { table->disable_keyread(); table->file->ha_index_or_rnd_end(); + preread_init_done= FALSE; /* We need to reset this for next select (Tested in part_of_refkey) From 317e04ce67d97ab4a11582236f1e46ccf25c94fb Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sun, 22 May 2011 15:47:46 -0700 Subject: [PATCH 14/24] Fixed 2 valgrind bugs in the tree for mwl #106. One was due the fact that the field SELECT_LEX::insert_tables was not initialized while the second was due to missing initialization of JOIN_TAB::preread_init_done in subselect_hash_sj_engine::make_unique_engine. Removed an invalid assert. --- sql/item_subselect.cc | 1 + sql/sql_lex.cc | 1 + sql/table.cc | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 9855251d404..f1aa2eb48ec 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -4165,6 +4165,7 @@ subselect_hash_sj_engine::make_unique_engine() DBUG_RETURN(NULL); tab->table= tmp_table; + tab->preread_init_done= FALSE; tab->ref.tmp_table_index_lookup_init(thd, tmp_key, it, FALSE); DBUG_RETURN(new subselect_uniquesubquery_engine(thd, tab, item, diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index e66ffe3265e..745a805d992 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1672,6 +1672,7 @@ void st_select_lex::init_select() cond_value= having_value= Item::COND_UNDEF; inner_refs_list.empty(); full_group_by_flag= 0; + insert_tables= 0; merged_into= 0; } diff --git a/sql/table.cc b/sql/table.cc index 898a42044d7..290a82eb56a 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3797,7 +3797,6 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type) if (where) { - DBUG_ASSERT(where->fixed); check_option= where->copy_andor_structure(thd); } if (is_cascaded) From c9a349488b95a9a938564fe1ac06dafc70db5864 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 27 May 2011 00:03:55 -0700 Subject: [PATCH 15/24] Applied Sanja's patch to fix LP bug #784297 in the tree for mwl106. The patch imposes unconditional materialization for derived tables used in update and multi-update statements. Fixed a bug with a wrong order of processing derived tables/views at the prepare stage that caused a crash for the variant of the query from test case for bug 52157. --- mysql-test/r/multi_update.result | 11 +++++++++ .../suite/innodb/r/innodb_multi_update.result | 2 ++ .../suite/innodb/t/innodb_multi_update.test | 6 ++--- .../r/innodb_multi_update.result | 2 ++ .../innodb_plugin/t/innodb_multi_update.test | 6 ++--- mysql-test/t/multi_update.test | 7 +++--- sql/sql_lex.h | 2 ++ sql/sql_union.cc | 24 +++++++++++++++++++ sql/sql_update.cc | 4 ++-- sql/table.cc | 8 +++---- 10 files changed, 55 insertions(+), 17 deletions(-) diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index f58861ec8ce..793763312b7 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -654,6 +654,9 @@ INSERT INTO t3 VALUES (1), (2); UPDATE IGNORE ( SELECT ( SELECT COUNT(*) FROM t1 GROUP BY a, @v ) a FROM t2 ) x, t3 SET t3.a = 0; +Warnings: +Error 1242 Subquery returns more than 1 row +Error 1242 Subquery returns more than 1 row DROP TABLE t1, t2, t3; SET SESSION sql_safe_updates = DEFAULT; # @@ -662,6 +665,14 @@ SET SESSION sql_safe_updates = DEFAULT; CREATE FUNCTION f1 () RETURNS BLOB RETURN 1; CREATE TABLE t1 (f1 DATE); INSERT INTO t1 VALUES('2001-01-01'); +UPDATE (SELECT 1 FROM t1 WHERE f1 = (SELECT f1() FROM t1)) x, t1 SET f1 = 1; +Warnings: +Warning 1292 Truncated incorrect datetime value: '1' +CREATE view v1 as SELECT f1() FROM t1; +UPDATE (SELECT 1 FROM t1 WHERE f1 = (select * from v1)) x, t1 SET f1 = 1; +Warnings: +Warning 1292 Truncated incorrect datetime value: '1' +DROP VIEW v1; DROP FUNCTION f1; DROP TABLE t1; end of tests diff --git a/mysql-test/suite/innodb/r/innodb_multi_update.result b/mysql-test/suite/innodb/r/innodb_multi_update.result index 924470faedd..558fc3938a8 100644 --- a/mysql-test/suite/innodb/r/innodb_multi_update.result +++ b/mysql-test/suite/innodb/r/innodb_multi_update.result @@ -79,4 +79,6 @@ drop table bug38999_1,bug38999_2; # CREATE TABLE t1(f1 INT) ENGINE=INNODB; INSERT INTO t1 VALUES(1); +UPDATE (SELECT ((SELECT 1 FROM t1), 1) FROM t1 WHERE (SELECT 1 FROM t1)) x, (SELECT 1) AS d SET d.f1 = 1; +ERROR 21000: Operand should contain 1 column(s) DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/innodb_multi_update.test b/mysql-test/suite/innodb/t/innodb_multi_update.test index f51dc74e3af..8356c20c88f 100644 --- a/mysql-test/suite/innodb/t/innodb_multi_update.test +++ b/mysql-test/suite/innodb/t/innodb_multi_update.test @@ -34,9 +34,7 @@ drop table bug38999_1,bug38999_2; --echo # CREATE TABLE t1(f1 INT) ENGINE=INNODB; INSERT INTO t1 VALUES(1); -# !!! This query returns a wrong error due to a bug in the code of mwl106 -# !!! Uncomment it when the bug is fixed -# --error ER_OPERAND_COLUMNS -# UPDATE (SELECT ((SELECT 1 FROM t1), 1) FROM t1 WHERE (SELECT 1 FROM t1)) x, (SELECT 1) AS d SET d.f1 = 1; +--error ER_OPERAND_COLUMNS +UPDATE (SELECT ((SELECT 1 FROM t1), 1) FROM t1 WHERE (SELECT 1 FROM t1)) x, (SELECT 1) AS d SET d.f1 = 1; DROP TABLE t1; diff --git a/mysql-test/suite/innodb_plugin/r/innodb_multi_update.result b/mysql-test/suite/innodb_plugin/r/innodb_multi_update.result index 924470faedd..558fc3938a8 100644 --- a/mysql-test/suite/innodb_plugin/r/innodb_multi_update.result +++ b/mysql-test/suite/innodb_plugin/r/innodb_multi_update.result @@ -79,4 +79,6 @@ drop table bug38999_1,bug38999_2; # CREATE TABLE t1(f1 INT) ENGINE=INNODB; INSERT INTO t1 VALUES(1); +UPDATE (SELECT ((SELECT 1 FROM t1), 1) FROM t1 WHERE (SELECT 1 FROM t1)) x, (SELECT 1) AS d SET d.f1 = 1; +ERROR 21000: Operand should contain 1 column(s) DROP TABLE t1; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_multi_update.test b/mysql-test/suite/innodb_plugin/t/innodb_multi_update.test index 84ab8746a91..3d9a9a53193 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb_multi_update.test +++ b/mysql-test/suite/innodb_plugin/t/innodb_multi_update.test @@ -34,9 +34,7 @@ drop table bug38999_1,bug38999_2; --echo # CREATE TABLE t1(f1 INT) ENGINE=INNODB; INSERT INTO t1 VALUES(1); -# !!! This query returns a wrong error due to a bug in the code of mwl106 -# !!! Uncomment it when the bug is fixed -# --error ER_OPERAND_COLUMNS -# UPDATE (SELECT ((SELECT 1 FROM t1), 1) FROM t1 WHERE (SELECT 1 FROM t1)) x, (SELECT 1) AS d SET d.f1 = 1; +--error ER_OPERAND_COLUMNS +UPDATE (SELECT ((SELECT 1 FROM t1), 1) FROM t1 WHERE (SELECT 1 FROM t1)) x, (SELECT 1) AS d SET d.f1 = 1; DROP TABLE t1; diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index 914a1a3b4a1..58a614e3a11 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -684,9 +684,10 @@ SET SESSION sql_safe_updates = DEFAULT; CREATE FUNCTION f1 () RETURNS BLOB RETURN 1; CREATE TABLE t1 (f1 DATE); INSERT INTO t1 VALUES('2001-01-01'); -# !!! This query returns a wrong error due to a bug in the code of mwl106 -# !!! Uncomment it when the bug is fixed -# UPDATE (SELECT 1 FROM t1 WHERE f1 = (SELECT f1() FROM t1)) x, t1 SET f1 = 1; +UPDATE (SELECT 1 FROM t1 WHERE f1 = (SELECT f1() FROM t1)) x, t1 SET f1 = 1; +CREATE view v1 as SELECT f1() FROM t1; +UPDATE (SELECT 1 FROM t1 WHERE f1 = (select * from v1)) x, t1 SET f1 = 1; +DROP VIEW v1; DROP FUNCTION f1; DROP TABLE t1; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index a587a01b3f4..014bcfdfd65 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -585,6 +585,8 @@ public: void set_thd(THD *thd_arg) { thd= thd_arg; } inline bool is_union (); + void set_unique_exlude(); + friend void lex_start(THD *thd); friend int subselect_union_engine::exec(); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 255f5311ac3..c6a6349fd4e 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -944,3 +944,27 @@ void st_select_lex::cleanup_all_joins(bool full) for (sl= unit->first_select(); sl; sl= sl->next_select()) sl->cleanup_all_joins(full); } + + +/** + Set exclude_from_table_unique_test for selects of this unit and all + underlying selects. + + @note used to exclude materialized derived tables (views) from unique + table check. +*/ + +void st_select_lex_unit::set_unique_exlude() +{ + for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) + { + sl->exclude_from_table_unique_test= TRUE; + for (SELECT_LEX_UNIT *unit= sl->first_inner_unit(); + unit; + unit= unit->next_unit()) + { + unit->set_unique_exlude(); + } + } +} + diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 215c5cbd4b3..4821fc2bd8f 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1040,8 +1040,8 @@ reopen_tables: //We need to merge for insert prior to prepare. if (mysql_handle_list_of_derived(lex, table_list, DT_MERGE_FOR_INSERT)) DBUG_RETURN(1); - if (mysql_handle_list_of_derived(lex, table_list, DT_PREPARE)) - DBUG_RETURN(1); + if (mysql_handle_derived(lex, DT_PREPARE)) + DBUG_RETURN(TRUE); if (setup_tables_and_check_access(thd, &lex->select_lex.context, &lex->select_lex.top_join_list, diff --git a/sql/table.cc b/sql/table.cc index 290a82eb56a..3e6b683eafb 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5851,7 +5851,9 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view) if (!is_view()) { /* A subquery might be forced to be materialized due to a side-effect. */ - if (!is_materialized_derived() && first_select->is_mergeable()) + if (!is_materialized_derived() && first_select->is_mergeable() && + !(thd->lex->sql_command == SQLCOM_UPDATE_MULTI || + thd->lex->sql_command == SQLCOM_UPDATE)) set_merged_derived(); else set_materialized_derived(); @@ -5862,9 +5864,7 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view) */ if (is_materialized_derived()) { - SELECT_LEX *sl; - for (sl= first_select ;sl ; sl= sl->next_select()) - sl->exclude_from_table_unique_test= TRUE; + unit->master_unit()->set_unique_exlude(); } /* Create field translation for mergeable derived tables/views. From cd6fc3876b08a0a85d06140a778f4d66f7ce92e7 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 30 May 2011 21:35:32 +0300 Subject: [PATCH 16/24] Fixed valgrind problem in expression cache (double opening the table). sql/sql_expression_cache.cc: The call should not open the temporary table. It will be done later after its index creation. --- sql/sql_expression_cache.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/sql_expression_cache.cc b/sql/sql_expression_cache.cc index db1f47c5603..c3450884610 100644 --- a/sql/sql_expression_cache.cc +++ b/sql/sql_expression_cache.cc @@ -109,7 +109,8 @@ void Expression_cache_tmptable::init() ~(OPTION_BIG_TABLES | TMP_TABLE_FORCE_MYISAM)), HA_POS_ERROR, - (char *)"subquery-cache-table"))) + (char *)"subquery-cache-table", + TRUE))) { DBUG_PRINT("error", ("create_tmp_table failed, caching switched off")); DBUG_VOID_RETURN; From 55d26463ad29c3262b14a9b959adf852b1e650bc Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 30 May 2011 21:40:46 +0300 Subject: [PATCH 17/24] Fixed typo (exlude -> exclude). --- sql/sql_lex.h | 2 +- sql/sql_union.cc | 4 ++-- sql/table.cc | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 014bcfdfd65..ee1fd62fdec 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -585,7 +585,7 @@ public: void set_thd(THD *thd_arg) { thd= thd_arg; } inline bool is_union (); - void set_unique_exlude(); + void set_unique_exclude(); friend void lex_start(THD *thd); friend int subselect_union_engine::exec(); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index c6a6349fd4e..c61cffa5951 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -954,7 +954,7 @@ void st_select_lex::cleanup_all_joins(bool full) table check. */ -void st_select_lex_unit::set_unique_exlude() +void st_select_lex_unit::set_unique_exclude() { for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) { @@ -963,7 +963,7 @@ void st_select_lex_unit::set_unique_exlude() unit; unit= unit->next_unit()) { - unit->set_unique_exlude(); + unit->set_unique_exclude(); } } } diff --git a/sql/table.cc b/sql/table.cc index 3e6b683eafb..efc6a449e50 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5864,7 +5864,7 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view) */ if (is_materialized_derived()) { - unit->master_unit()->set_unique_exlude(); + unit->master_unit()->set_unique_exclude(); } /* Create field translation for mergeable derived tables/views. From e89da2e0f1e04fa6291155fce6904310e911b8e9 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Tue, 31 May 2011 15:33:14 +0300 Subject: [PATCH 18/24] Return commented out tests. --- mysql-test/r/view.result | 22 +++++++++++ mysql-test/r/view_grant.result | 35 ++++++++++-------- mysql-test/t/view.test | 67 ++++++++++++++++------------------ mysql-test/t/view_grant.test | 16 +++----- 4 files changed, 79 insertions(+), 61 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index a581b83b133..4eaf1eb4793 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -2881,6 +2881,28 @@ Tables_in_test t1 DROP TABLE t1; DROP VIEW IF EXISTS v1; +CREATE DATABASE bug21261DB; +USE bug21261DB; +CREATE TABLE t1 (x INT); +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT x FROM t1; +GRANT INSERT, UPDATE ON v1 TO 'user21261'@'localhost'; +GRANT INSERT, UPDATE ON t1 TO 'user21261'@'localhost'; +CREATE TABLE t2 (y INT); +GRANT SELECT ON t2 TO 'user21261'@'localhost'; +INSERT INTO v1 (x) VALUES (5); +UPDATE v1 SET x=1; +GRANT SELECT ON v1 TO 'user21261'@'localhost'; +GRANT SELECT ON t1 TO 'user21261'@'localhost'; +UPDATE v1,t2 SET x=1 WHERE x=y; +SELECT * FROM t1; +x +1 +REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'user21261'@'localhost'; +DROP USER 'user21261'@'localhost'; +DROP VIEW v1; +DROP TABLE t1; +DROP DATABASE bug21261DB; +USE test; create table t1 (f1 datetime); create view v1 as select * from t1 where f1 between now() and now() + interval 1 minute; show create view v1; diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index 2ae5b46a048..261743e9d3c 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -172,29 +172,30 @@ a b 35 4 46 5 50 10 +update v1 set a=a+c; select * from t1; a b -13 2 -24 3 -35 4 -46 5 -50 10 +16 2 +28 3 +40 4 +52 5 +61 10 update t2,v2 set v2.a=v2.a+v2.c where t2.x=v2.c; select * from t1; a b -13 2 -27 3 -39 4 -51 5 -50 10 +16 2 +31 3 +44 4 +57 5 +61 10 update v2 set a=a+c; select * from t1; a b -15 2 -30 3 -43 4 -56 5 -60 10 +18 2 +34 3 +48 4 +62 5 +71 10 update t2,v2 set v2.c=v2.a+v2.c where t2.x=v2.c; ERROR 42000: UPDATE command denied to user 'mysqltest_1'@'localhost' for column 'c' in table 'v2' update v2 set c=a+c; @@ -575,12 +576,16 @@ UPDATE mysqltest1.v_ts SET x= 200 WHERE x = 100; ERROR 42000: UPDATE command denied to user 'readonly'@'localhost' for table 'v_ts' UPDATE mysqltest1.v_ts SET x= 200; ERROR 42000: UPDATE command denied to user 'readonly'@'localhost' for table 'v_ts' +UPDATE mysqltest1.v_tu SET x= 200 WHERE x = 100; +UPDATE mysqltest1.v_tus SET x= 200 WHERE x = 100; +UPDATE mysqltest1.v_tu SET x= 200; DELETE FROM mysqltest1.v_ts WHERE x= 200; ERROR 42000: DELETE command denied to user 'readonly'@'localhost' for table 'v_ts' DELETE FROM mysqltest1.v_ts; ERROR 42000: DELETE command denied to user 'readonly'@'localhost' for table 'v_ts' DELETE FROM mysqltest1.v_td WHERE x= 200; ERROR 42000: SELECT command denied to user 'readonly'@'localhost' for column 'x' in table 'v_td' +DELETE FROM mysqltest1.v_tds WHERE x= 200; DELETE FROM mysqltest1.v_td; DROP VIEW mysqltest1.v_tds; DROP VIEW mysqltest1.v_td; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index d1a14f19c4d..b2032d5d6ba 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -2783,42 +2783,39 @@ DROP VIEW IF EXISTS v1; # Bug#21261 Wrong access rights was required for an insert to a view # -# !!! This test case fails in ps-protocol due to a bug in the code of mwl106 -# !!! Uncomment it when the bug is fixed +CREATE DATABASE bug21261DB; +USE bug21261DB; +connect (root,localhost,root,,bug21261DB); +connection root; -# CREATE DATABASE bug21261DB; -# USE bug21261DB; -# connect (root,localhost,root,,bug21261DB); -# connection root; -# -# CREATE TABLE t1 (x INT); -# CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT x FROM t1; -# GRANT INSERT, UPDATE ON v1 TO 'user21261'@'localhost'; -# GRANT INSERT, UPDATE ON t1 TO 'user21261'@'localhost'; -# CREATE TABLE t2 (y INT); -# GRANT SELECT ON t2 TO 'user21261'@'localhost'; -# -# connect (user21261, localhost, user21261,, bug21261DB); -# connection user21261; -# INSERT INTO v1 (x) VALUES (5); -# UPDATE v1 SET x=1; -# connection root; -# GRANT SELECT ON v1 TO 'user21261'@'localhost'; -# GRANT SELECT ON t1 TO 'user21261'@'localhost'; -# connection user21261; -# UPDATE v1,t2 SET x=1 WHERE x=y; -# connection root; -# SELECT * FROM t1; -# REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'user21261'@'localhost'; -# DROP USER 'user21261'@'localhost'; -# DROP VIEW v1; -# DROP TABLE t1; -# DROP DATABASE bug21261DB; -# -# connection default; -# USE test; -# disconnect root; -# disconnect user21261; +CREATE TABLE t1 (x INT); +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT x FROM t1; +GRANT INSERT, UPDATE ON v1 TO 'user21261'@'localhost'; +GRANT INSERT, UPDATE ON t1 TO 'user21261'@'localhost'; +CREATE TABLE t2 (y INT); +GRANT SELECT ON t2 TO 'user21261'@'localhost'; + +connect (user21261, localhost, user21261,, bug21261DB); +connection user21261; +INSERT INTO v1 (x) VALUES (5); +UPDATE v1 SET x=1; +connection root; +GRANT SELECT ON v1 TO 'user21261'@'localhost'; +GRANT SELECT ON t1 TO 'user21261'@'localhost'; +connection user21261; +UPDATE v1,t2 SET x=1 WHERE x=y; +connection root; +SELECT * FROM t1; +REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'user21261'@'localhost'; +DROP USER 'user21261'@'localhost'; +DROP VIEW v1; +DROP TABLE t1; +DROP DATABASE bug21261DB; + +connection default; +USE test; +disconnect root; +disconnect user21261; # diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index f94b512205a..21c6f376f8a 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -223,9 +223,7 @@ use mysqltest; # update with rights on VIEW column update t2,v1 set v1.a=v1.a+v1.c where t2.x=v1.c; select * from t1; -# !!! This query fails in ps-protocol due to a bug in the code of mwl106 -# !!! Uncomment it when the bug is fixed -# update v1 set a=a+c; +update v1 set a=a+c; select * from t1; # update with rights on whole VIEW update t2,v2 set v2.a=v2.a+v2.c where t2.x=v2.c; @@ -763,11 +761,9 @@ INSERT INTO mysqltest1.v_ti VALUES (100); UPDATE mysqltest1.v_ts SET x= 200 WHERE x = 100; --error ER_TABLEACCESS_DENIED_ERROR UPDATE mysqltest1.v_ts SET x= 200; -# !!! These queries fail in ps-protocol due to a bug in the code of mwl106 -# !!! Uncomment it when the bug is fixed -# UPDATE mysqltest1.v_tu SET x= 200 WHERE x = 100; -# UPDATE mysqltest1.v_tus SET x= 200 WHERE x = 100; -# UPDATE mysqltest1.v_tu SET x= 200; +UPDATE mysqltest1.v_tu SET x= 200 WHERE x = 100; +UPDATE mysqltest1.v_tus SET x= 200 WHERE x = 100; +UPDATE mysqltest1.v_tu SET x= 200; --error ER_TABLEACCESS_DENIED_ERROR DELETE FROM mysqltest1.v_ts WHERE x= 200; @@ -775,9 +771,7 @@ DELETE FROM mysqltest1.v_ts WHERE x= 200; DELETE FROM mysqltest1.v_ts; --error ER_COLUMNACCESS_DENIED_ERROR DELETE FROM mysqltest1.v_td WHERE x= 200; -# !!! These queries fail in ps-protocol due to a bug in the code of mwl106 -# !!! Uncomment it when the bug is fixed -# DELETE FROM mysqltest1.v_tds WHERE x= 200; +DELETE FROM mysqltest1.v_tds WHERE x= 200; DELETE FROM mysqltest1.v_td; connection default; From dd3b27834c271e9561fcde4658ce758c30c69334 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Thu, 2 Jun 2011 10:30:05 +0300 Subject: [PATCH 19/24] Fix of privilege problem in views and PS sql/sql_base.cc: Column for views should be looked in views (not in underlying table) sql/sql_parse.cc: if we reread grants for single table we should read both database and tables grants for views (not only database grants). --- sql/sql_base.cc | 2 +- sql/sql_parse.cc | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8a7ef52529d..e7bb5525d32 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -6407,7 +6407,7 @@ find_field_in_tables(THD *thd, Item_ident *item, find_field_in_table even in the case of information schema tables when table_ref->field_translation != NULL. */ - if (table_ref->table && + if (table_ref->table && !table_ref->view && (!table_ref->is_merged_derived() || (!table_ref->is_multitable() && table_ref->merged_for_insert))) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 5636e5bd3ef..0ae6d38ce8e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5210,8 +5210,6 @@ bool check_single_table_access(THD *thd, ulong privilege, /* Show only 1 table for check_grant */ if (!(all_tables->belong_to_view && (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) && - !(all_tables->is_view() && - all_tables->is_merged_derived()) && check_grant(thd, privilege, all_tables, 0, 1, no_errors)) goto deny; From 35c35858f859f23c23a414cdb21fdfe39d5de11c Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 3 Jun 2011 21:44:37 -0700 Subject: [PATCH 20/24] Fixed bugs causing failures fot the funcs_1 test suite. Uncommented the failing test cases. Commented out the failing test case from parts.partition_repair_myisam.test. The test case has to be changed to bear the same semantics as before mwl106. --- .../suite/funcs_1/r/innodb_views.result | 36 +++++++++++++++++++ .../suite/funcs_1/r/memory_views.result | 36 +++++++++++++++++++ .../suite/funcs_1/views/views_master.inc | 10 +++--- .../parts/r/partition_repair_myisam.result | 11 ------ .../parts/t/partition_repair_myisam.test | 7 ++-- sql/sql_delete.cc | 10 +++--- sql/sql_lex.cc | 16 +++++++++ sql/sql_prepare.cc | 7 +++- 8 files changed, 108 insertions(+), 25 deletions(-) diff --git a/mysql-test/suite/funcs_1/r/innodb_views.result b/mysql-test/suite/funcs_1/r/innodb_views.result index 601d59f9b90..a335e135a4f 100644 --- a/mysql-test/suite/funcs_1/r/innodb_views.result +++ b/mysql-test/suite/funcs_1/r/innodb_views.result @@ -22339,38 +22339,74 @@ SET @variant9= 'CREATE ALGORITHM = TEMPTABLE VIEW v1 (f61) AS select f60 from t1 CREATE ALGORITHM = TEMPTABLE VIEW v1 (f61) AS select f60 from t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f59 AS f61 FROM t1 WHERE f60 IN (SELECT f59 FROM t1); INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f61 FROM v2; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT (SELECT f60 FROM t2 WHERE f59=19) AS f61 FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f61 FROM t1 HAVING f61 > 0; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f61 FROM t1 GROUP BY f61; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT SUM(f59) AS f61 FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT DISTINCTROW(f61) FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT DISTINCT(f61) FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; Drop TABLE t1, t2 ; Drop VIEW v2 ; diff --git a/mysql-test/suite/funcs_1/r/memory_views.result b/mysql-test/suite/funcs_1/r/memory_views.result index 8f96897b51e..ccbd086b71f 100644 --- a/mysql-test/suite/funcs_1/r/memory_views.result +++ b/mysql-test/suite/funcs_1/r/memory_views.result @@ -22341,38 +22341,74 @@ SET @variant9= 'CREATE ALGORITHM = TEMPTABLE VIEW v1 (f61) AS select f60 from t1 CREATE ALGORITHM = TEMPTABLE VIEW v1 (f61) AS select f60 from t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f59 AS f61 FROM t1 WHERE f60 IN (SELECT f59 FROM t1); INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f61 FROM v2; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT (SELECT f60 FROM t2 WHERE f59=19) AS f61 FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f61 FROM t1 HAVING f61 > 0; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT f61 FROM t1 GROUP BY f61; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT SUM(f59) AS f61 FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT DISTINCTROW(f61) FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; CREATE VIEW v1 AS SELECT DISTINCT(f61) FROM t1; INSERT INTO v1 VALUES (1002); ERROR HY000: The target table v1 of the INSERT is not insertable-into +UPDATE v1 SET f61=1007; +ERROR HY000: The target table v1 of the UPDATE is not updatable +DELETE FROM v1; +ERROR HY000: The target table v1 of the DELETE is not updatable DROP VIEW v1; Drop TABLE t1, t2 ; Drop VIEW v2 ; diff --git a/mysql-test/suite/funcs_1/views/views_master.inc b/mysql-test/suite/funcs_1/views/views_master.inc index ca0b7814068..5ab8d52629c 100644 --- a/mysql-test/suite/funcs_1/views/views_master.inc +++ b/mysql-test/suite/funcs_1/views/views_master.inc @@ -3831,12 +3831,10 @@ while ($num) INSERT INTO v1 VALUES (1002); # --error ER_NON_UPDATABLE_TABLE, ER_UPDATE_TABLE_USED -# !!! This queriesreturn wrong errors due to a bug in the code of mwl106 -# !!! Uncomment them when the bug is fixed -# --error ER_NON_UPDATABLE_TABLE -# UPDATE v1 SET f61=1007; -# --error ER_NON_UPDATABLE_TABLE -# DELETE FROM v1; + --error ER_NON_UPDATABLE_TABLE + UPDATE v1 SET f61=1007; + --error ER_NON_UPDATABLE_TABLE + DELETE FROM v1; DROP VIEW v1; dec $num; } diff --git a/mysql-test/suite/parts/r/partition_repair_myisam.result b/mysql-test/suite/parts/r/partition_repair_myisam.result index 4af00ddcc6d..aeba93273f9 100644 --- a/mysql-test/suite/parts/r/partition_repair_myisam.result +++ b/mysql-test/suite/parts/r/partition_repair_myisam.result @@ -393,17 +393,6 @@ partition b a length(c) 6 34 6 row 2 64 6 83 64 6 97 zzzzzZzzzzz 64 -SELECT (b % 7) AS partition, b, a FROM (SELECT b,a FROM t1_will_crash) q -WHERE (b % 7) = 6 -ORDER BY partition, b, a; -partition b a -6 6 jkl -6 13 ooo -6 34 6 row 2 -6 48 6 row 4 -6 62 6 row 6 -6 83 -6 97 zzzzzZzzzzz ALTER TABLE t1_will_crash CHECK PARTITION p6; Table Op Msg_type Msg_text test.t1_will_crash check warning Size of datafile is: 868 Should be: 604 diff --git a/mysql-test/suite/parts/t/partition_repair_myisam.test b/mysql-test/suite/parts/t/partition_repair_myisam.test index a7ceb5b7faf..3e03669c1ce 100644 --- a/mysql-test/suite/parts/t/partition_repair_myisam.test +++ b/mysql-test/suite/parts/t/partition_repair_myisam.test @@ -230,9 +230,10 @@ FLUSH TABLES; SELECT (b % 7) AS partition, b, a, length(c) FROM t1_will_crash WHERE (b % 7) = 6 ORDER BY partition, b, a; -SELECT (b % 7) AS partition, b, a FROM (SELECT b,a FROM t1_will_crash) q -WHERE (b % 7) = 6 -ORDER BY partition, b, a; +# !!! The next test case has to be changed to provide the same result set as before mwl106 +# SELECT (b % 7) AS partition, b, a FROM (SELECT b,a FROM t1_will_crash) q +# WHERE (b % 7) = 6 +# ORDER BY partition, b, a; # NOTE: REBUILD PARTITION without CHECK before, 2 + (1) records will be lost! #ALTER TABLE t1_will_crash REBUILD PARTITION p6; ALTER TABLE t1_will_crash CHECK PARTITION p6; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 67eb7bc328d..c1996ee54f1 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -63,13 +63,15 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (mysql_handle_list_of_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT) || mysql_handle_list_of_derived(thd->lex, table_list, DT_PREPARE)) - DBUG_RETURN(TRUE); + DBUG_RETURN(TRUE); + if (!table_list->updatable) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE"); + DBUG_RETURN(TRUE); + } if (!(table= table_list->table) || !table->created) { - if (!table_list->updatable) - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE"); - else my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), table_list->view_db.str, table_list->view_name.str); DBUG_RETURN(TRUE); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 745a805d992..67c9883631f 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2370,6 +2370,22 @@ bool st_lex::can_be_merged() /* find non VIEW subqueries/unions */ bool selects_allow_merge= select_lex.next_select() == 0; + if (selects_allow_merge) + { + for (SELECT_LEX_UNIT *tmp_unit= select_lex.first_inner_unit(); + tmp_unit; + tmp_unit= tmp_unit->next_unit()) + { + if (tmp_unit->first_select()->parent_lex == this && + (tmp_unit->item == 0 || + (tmp_unit->item->place() != IN_WHERE && + tmp_unit->item->place() != IN_ON))) + { + selects_allow_merge= 0; + break; + } + } + } return (selects_allow_merge && select_lex.group_list.elements == 0 && diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 38fe809e357..7bec9d6577e 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1346,7 +1346,12 @@ static bool mysql_test_delete(Prepared_statement *stmt, mysql_handle_list_of_derived(thd->lex, table_list, DT_PREPARE)) goto error; - if (!table_list->table) + if (!table_list->updatable) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE"); + goto error; + } + if (!table_list->table || !table_list->table->created) { my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), table_list->view_db.str, table_list->view_name.str); From 3bf08e549a78ad12191f6b1ca49719bc667664ef Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sun, 5 Jun 2011 21:54:25 -0700 Subject: [PATCH 21/24] Added two new flags for the optimizer switch: 'derived_merge': - if the flag is off then all derived tables are materialized - if the flag is on then mergeable derived tables are merged 'derived_with_keys': - if the flag is off then no keys are created for derived tables - if the flag is on then for any derived table a key to access the derived table may be created. Now by default both flags are on. Later the default values for the flags will be off to comply with the current behaviour of mysql-5.1. Uncommented previously commented out test case from parts.partition_repair_myisam after having added an explicit requirement to materialize the derived table used in the test case. --- mysql-test/r/optimizer_switch.result | 36 +++++++------- .../parts/r/partition_repair_myisam.result | 14 ++++++ .../parts/t/partition_repair_myisam.test | 10 ++-- sql/mysql_priv.h | 47 ++++++++++--------- sql/mysqld.cc | 10 +++- sql/sql_select.cc | 15 +++--- sql/table.cc | 3 +- 7 files changed, 83 insertions(+), 52 deletions(-) diff --git a/mysql-test/r/optimizer_switch.result b/mysql-test/r/optimizer_switch.result index 288f88cec4b..937b4dfeffa 100644 --- a/mysql-test/r/optimizer_switch.result +++ b/mysql-test/r/optimizer_switch.result @@ -4,19 +4,19 @@ # select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set optimizer_switch='index_merge=off,index_merge_union=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set optimizer_switch='index_merge_union=on'; select @@optimizer_switch; @@optimizer_switch -index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set optimizer_switch='default,index_merge_sort_union=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set optimizer_switch=4; ERROR 42000: Variable 'optimizer_switch' can't be set to the value of '4' set optimizer_switch=NULL; @@ -43,60 +43,60 @@ set optimizer_switch=default; set optimizer_switch='index_merge=off,index_merge_union=off,default'; select @@optimizer_switch; @@optimizer_switch -index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set optimizer_switch=default; select @@global.optimizer_switch; @@global.optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set @@global.optimizer_switch=default; select @@global.optimizer_switch; @@global.optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on # # Check index_merge's @@optimizer_switch flags # select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on BUG#37120 optimizer_switch allowable values not according to specification select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set optimizer_switch='default,materialization=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set optimizer_switch='default,semijoin=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set optimizer_switch='default,loosescan=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=off,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set optimizer_switch='default,semijoin=off,materialization=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set optimizer_switch='default,materialization=off,semijoin=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set optimizer_switch='default,semijoin=off,materialization=off,loosescan=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=off,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set optimizer_switch='default,semijoin=off,loosescan=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=off,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set optimizer_switch='default,materialization=off,loosescan=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=off,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on set optimizer_switch=default; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=off,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=off,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on diff --git a/mysql-test/suite/parts/r/partition_repair_myisam.result b/mysql-test/suite/parts/r/partition_repair_myisam.result index aeba93273f9..a34627a91cb 100644 --- a/mysql-test/suite/parts/r/partition_repair_myisam.result +++ b/mysql-test/suite/parts/r/partition_repair_myisam.result @@ -393,6 +393,20 @@ partition b a length(c) 6 34 6 row 2 64 6 83 64 6 97 zzzzzZzzzzz 64 +SET @save_optimizer_switch= @@optimizer_switch; +SET @@optimizer_switch='derived_merge=off'; +SELECT (b % 7) AS partition, b, a FROM (SELECT b,a FROM t1_will_crash) q +WHERE (b % 7) = 6 +ORDER BY partition, b, a; +partition b a +6 6 jkl +6 13 ooo +6 34 6 row 2 +6 48 6 row 4 +6 62 6 row 6 +6 83 +6 97 zzzzzZzzzzz +SET @@optimizer_switch=@save_optimizer_switch; ALTER TABLE t1_will_crash CHECK PARTITION p6; Table Op Msg_type Msg_text test.t1_will_crash check warning Size of datafile is: 868 Should be: 604 diff --git a/mysql-test/suite/parts/t/partition_repair_myisam.test b/mysql-test/suite/parts/t/partition_repair_myisam.test index 3e03669c1ce..849f175b920 100644 --- a/mysql-test/suite/parts/t/partition_repair_myisam.test +++ b/mysql-test/suite/parts/t/partition_repair_myisam.test @@ -230,10 +230,12 @@ FLUSH TABLES; SELECT (b % 7) AS partition, b, a, length(c) FROM t1_will_crash WHERE (b % 7) = 6 ORDER BY partition, b, a; -# !!! The next test case has to be changed to provide the same result set as before mwl106 -# SELECT (b % 7) AS partition, b, a FROM (SELECT b,a FROM t1_will_crash) q -# WHERE (b % 7) = 6 -# ORDER BY partition, b, a; +SET @save_optimizer_switch= @@optimizer_switch; +SET @@optimizer_switch='derived_merge=off'; +SELECT (b % 7) AS partition, b, a FROM (SELECT b,a FROM t1_will_crash) q +WHERE (b % 7) = 6 +ORDER BY partition, b, a; +SET @@optimizer_switch=@save_optimizer_switch; # NOTE: REBUILD PARTITION without CHECK before, 2 + (1) records will be lost! #ALTER TABLE t1_will_crash REBUILD PARTITION p6; ALTER TABLE t1_will_crash CHECK PARTITION p6; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index d339337eb1e..8ff92457783 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -568,36 +568,37 @@ protected: #define OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT 8 #define OPTIMIZER_SWITCH_INDEX_MERGE_SORT_INTERSECT 16 #define OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN 32 - -#define OPTIMIZER_SWITCH_FIRSTMATCH 64 -#define OPTIMIZER_SWITCH_LOOSE_SCAN 128 -#define OPTIMIZER_SWITCH_MATERIALIZATION 256 -#define OPTIMIZER_SWITCH_IN_TO_EXISTS 512 -#define OPTIMIZER_SWITCH_SEMIJOIN 1024 -#define OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE (1<<11) -#define OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN (1<<12) -#define OPTIMIZER_SWITCH_SUBQUERY_CACHE (1<<13) +#define OPTIMIZER_SWITCH_DERIVED_MERGE 64 +#define OPTIMIZER_SWITCH_DERIVED_WITH_KEYS 128 +#define OPTIMIZER_SWITCH_FIRSTMATCH 256 +#define OPTIMIZER_SWITCH_LOOSE_SCAN 512 +#define OPTIMIZER_SWITCH_MATERIALIZATION 1024 +#define OPTIMIZER_SWITCH_IN_TO_EXISTS (1<<11) +#define OPTIMIZER_SWITCH_SEMIJOIN (1<<12) +#define OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE (1<<13) +#define OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN (1<<14) +#define OPTIMIZER_SWITCH_SUBQUERY_CACHE (1<<15) /** If this is off, MRR is never used. */ -#define OPTIMIZER_SWITCH_MRR (1ULL << 14) +#define OPTIMIZER_SWITCH_MRR (1ULL << 16) /** If OPTIMIZER_SWITCH_MRR is on and this is on, MRR is used depending on a cost-based choice ("automatic"). If OPTIMIZER_SWITCH_MRR is on and this is off, MRR is "forced" (i.e. used as long as the storage engine is capable of doing it). */ -#define OPTIMIZER_SWITCH_MRR_COST_BASED (1ULL << 15) -#define OPTIMIZER_SWITCH_MRR_SORT_KEYS (1ULL << 16) -#define OPTIMIZER_SWITCH_OUTER_JOIN_WITH_CACHE (1ULL << 17) -#define OPTIMIZER_SWITCH_SEMIJOIN_WITH_CACHE (1ULL << 18) -#define OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL (1ULL << 19) -#define OPTIMIZER_SWITCH_JOIN_CACHE_HASHED (1ULL << 20) -#define OPTIMIZER_SWITCH_JOIN_CACHE_BKA (1ULL << 21) -#define OPTIMIZER_SWITCH_OPTIMIZE_JOIN_BUFFER_SIZE (1ULL << 22) +#define OPTIMIZER_SWITCH_MRR_COST_BASED (1ULL << 17) +#define OPTIMIZER_SWITCH_MRR_SORT_KEYS (1ULL << 18) +#define OPTIMIZER_SWITCH_OUTER_JOIN_WITH_CACHE (1ULL << 19) +#define OPTIMIZER_SWITCH_SEMIJOIN_WITH_CACHE (1ULL << 20) +#define OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL (1ULL << 21) +#define OPTIMIZER_SWITCH_JOIN_CACHE_HASHED (1ULL << 22) +#define OPTIMIZER_SWITCH_JOIN_CACHE_BKA (1ULL << 23) +#define OPTIMIZER_SWITCH_OPTIMIZE_JOIN_BUFFER_SIZE (1ULL << 24) #ifdef DBUG_OFF -# define OPTIMIZER_SWITCH_LAST (1ULL << 23) +# define OPTIMIZER_SWITCH_LAST (1ULL << 25) #else -# define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1ULL << 23) -# define OPTIMIZER_SWITCH_LAST (1ULL << 24) +# define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1ULL << 25) +# define OPTIMIZER_SWITCH_LAST (1ULL << 26) #endif #ifdef DBUG_OFF @@ -612,6 +613,8 @@ enabled by default, add OPTIMIZER_SWITCH_MATERIALIZATION OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION | \ OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT | \ OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN | \ + OPTIMIZER_SWITCH_DERIVED_MERGE | \ + OPTIMIZER_SWITCH_DERIVED_WITH_KEYS | \ OPTIMIZER_SWITCH_FIRSTMATCH | \ OPTIMIZER_SWITCH_LOOSE_SCAN | \ OPTIMIZER_SWITCH_IN_TO_EXISTS | \ @@ -632,6 +635,8 @@ enabled by default, add OPTIMIZER_SWITCH_MATERIALIZATION OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION | \ OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT | \ OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN | \ + OPTIMIZER_SWITCH_DERIVED_MERGE | \ + OPTIMIZER_SWITCH_DERIVED_WITH_KEYS | \ OPTIMIZER_SWITCH_TABLE_ELIMINATION | \ OPTIMIZER_SWITCH_FIRSTMATCH | \ OPTIMIZER_SWITCH_LOOSE_SCAN | \ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 9b8edb1d985..ffa95e378ca 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -336,6 +336,7 @@ static const char *optimizer_switch_names[]= "index_merge","index_merge_union","index_merge_sort_union", "index_merge_intersection","index_merge_sort_intersection", "index_condition_pushdown", + "derived_merge", "derived_with_keys", "firstmatch","loosescan","materialization","in_to_exists","semijoin", "partial_match_rowid_merge", "partial_match_table_scan", @@ -364,6 +365,8 @@ static const unsigned int optimizer_switch_names_len[]= sizeof("index_merge_intersection") - 1, sizeof("index_merge_sort_intersection") - 1, sizeof("index_condition_pushdown") - 1, + sizeof("derived_merge") - 1, + sizeof("derived_with_keys") - 1, sizeof("firstmatch") - 1, sizeof("loosescan") - 1, sizeof("materialization") - 1, @@ -483,6 +486,8 @@ static const char *optimizer_switch_str="index_merge=on,index_merge_union=on," "index_merge_intersection=on," "index_merge_sort_intersection=off," "index_condition_pushdown=on," + "derived_merge=on," + "derived_with_keys=on," "firstmatch=on," "loosescan=on," "materialization=off," @@ -7545,8 +7550,9 @@ each time the SQL thread starts.", {"optimizer_switch", OPT_OPTIMIZER_SWITCH, "optimizer_switch=option=val[,option=val...], where option={index_merge, " "index_merge_union, index_merge_sort_union, index_merge_intersection, " - "index_merge_sort_intersection, " - "index_condition_pushdown, firstmatch, loosescan, materialization, in_to_exists, " + "index_merge_sort_intersection, index_condition_pushdown, " + "derived_merge, derived_with_keys, " + "firstmatch, loosescan, materialization, in_to_exists, " "semijoin, partial_match_rowid_merge, partial_match_table_scan, " "subquery_cache, outer_join_with_cache, semijoin_with_cache, " "join_cache_incremental, join_cache_hashed, join_cache_bka, " diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 534d7799d53..e5714ab4bbc 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -57,7 +57,7 @@ static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, uint tables, COND *conds, table_map table_map, SELECT_LEX *select_lex, st_sargable_param **sargables); -static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse, +static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, bool skip_unprefixed_keyparts); static int sort_keyuse(KEYUSE *a,KEYUSE *b); static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, @@ -1103,7 +1103,8 @@ JOIN::optimize() DBUG_RETURN(1); } - drop_unused_derived_keys(); + if (optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_WITH_KEYS)) + drop_unused_derived_keys(); if (rollup.state != ROLLUP::STATE_NONE) { @@ -3152,7 +3153,8 @@ make_join_statistics(JOIN *join, List &tables_list, ((Item_in_subselect*)join->unit->item)->in_strategy & SUBS_IN_TO_EXISTS); if (keyuse_array->elements && - sort_and_filter_keyuse(keyuse_array, skip_unprefixed_keyparts)) + sort_and_filter_keyuse(join->thd, keyuse_array, + skip_unprefixed_keyparts)) goto error; DBUG_EXECUTE("opt", print_keyuse_array(keyuse_array);); } @@ -4654,7 +4656,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, Special treatment for ft-keys. */ -static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse, +static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, bool skip_unprefixed_keyparts) { KEYUSE key_end, *prev, *save_pos, *use; @@ -4669,7 +4671,8 @@ static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse, if (insert_dynamic(keyuse, (uchar*) &key_end)) return TRUE; - generate_derived_keys(keyuse); + if (optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_WITH_KEYS)) + generate_derived_keys(keyuse); use= save_pos= dynamic_element(keyuse,0,KEYUSE*); prev= &key_end; @@ -21432,7 +21435,7 @@ JOIN::reoptimize(Item *added_where, table_map join_tables, /* added_keyuse contents is copied, and it is no longer needed. */ delete_dynamic(&added_keyuse); - if (sort_and_filter_keyuse(&keyuse, true)) + if (sort_and_filter_keyuse(thd, &keyuse, true)) return REOPT_ERROR; optimize_keyuse(this, &keyuse); diff --git a/sql/table.cc b/sql/table.cc index a47598d40ac..8427ca6e112 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5869,8 +5869,9 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view) { /* A subquery might be forced to be materialized due to a side-effect. */ if (!is_materialized_derived() && first_select->is_mergeable() && + optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_MERGE) && !(thd->lex->sql_command == SQLCOM_UPDATE_MULTI || - thd->lex->sql_command == SQLCOM_UPDATE)) + thd->lex->sql_command == SQLCOM_DELETE_MULTI)) set_merged_derived(); else set_materialized_derived(); From db0c3406011d9a6d6fdb98c1c1f7925d243bd1f9 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Mon, 6 Jun 2011 12:19:35 -0700 Subject: [PATCH 22/24] Fixed LP bug #793436. When looking for the execution plan of a derived table to be materialized JOIN::optimize finds out that all joined tables of the derived table contain not more than one row then the derived table should be maretialized at the optimization stage. Added a test case for the bug. Adjusted results in other test cases. --- mysql-test/r/derived.result | 18 ++++++++++++++++++ mysql-test/r/subselect.result | 4 ++-- mysql-test/r/subselect_no_mat.result | 4 ++-- mysql-test/r/subselect_no_opts.result | 4 ++-- mysql-test/r/subselect_no_semijoin.result | 4 ++-- mysql-test/r/view_grant.result | 6 +++--- mysql-test/suite/pbxt/r/subselect.result | 4 ++-- mysql-test/t/derived.test | 16 ++++++++++++++++ sql/sql_derived.cc | 5 ++++- 9 files changed, 51 insertions(+), 14 deletions(-) diff --git a/mysql-test/r/derived.result b/mysql-test/r/derived.result index 7cc2af3616f..fe803ed37a5 100644 --- a/mysql-test/r/derived.result +++ b/mysql-test/r/derived.result @@ -407,3 +407,21 @@ MIN(i) 1 DROP TABLE t1; # End of 5.0 tests +# +# LP bug #793436: query with a derived table for which optimizer proves +# that it contains not more than 1 row +# +CREATE TABLE t1 (a int, KEY (a)) ; +INSERT INTO t1 VALUES (3), (1); +CREATE TABLE t2 (a int); +INSERT INTO t2 VALUES (3); +EXPLAIN +SELECT * FROM (SELECT DISTINCT * FROM t2) t, t1 WHERE t1.a = t.a; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY system NULL NULL NULL NULL 1 +1 PRIMARY t1 ref a a 5 const 1 Using index +2 DERIVED t2 system NULL NULL NULL NULL 1 +SELECT * FROM (SELECT DISTINCT * FROM t2) t, t1 WHERE t1.a = t.a; +a a +3 3 +DROP TABLE t1,t2; diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index d8cface69a6..c84d4538a88 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -47,13 +47,13 @@ SELECT (SELECT a) as a; ERROR 42S22: Reference 'a' not supported (forward reference in item list) EXPLAIN EXTENDED SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +1 PRIMARY system NULL NULL NULL NULL 1 100.00 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 -Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having (<`b`.`a`>((select `b`.`a`)) = 1) +Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having (<1>((select 1)) = 1) SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; 1 1 diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result index dccf4e1900c..f9a4a4373c2 100644 --- a/mysql-test/r/subselect_no_mat.result +++ b/mysql-test/r/subselect_no_mat.result @@ -51,13 +51,13 @@ SELECT (SELECT a) as a; ERROR 42S22: Reference 'a' not supported (forward reference in item list) EXPLAIN EXTENDED SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +1 PRIMARY system NULL NULL NULL NULL 1 100.00 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 -Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having (<`b`.`a`>((select `b`.`a`)) = 1) +Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having (<1>((select 1)) = 1) SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; 1 1 diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result index 5b7e76561a2..493ab9dba59 100644 --- a/mysql-test/r/subselect_no_opts.result +++ b/mysql-test/r/subselect_no_opts.result @@ -48,13 +48,13 @@ SELECT (SELECT a) as a; ERROR 42S22: Reference 'a' not supported (forward reference in item list) EXPLAIN EXTENDED SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +1 PRIMARY system NULL NULL NULL NULL 1 100.00 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 -Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having (<`b`.`a`>((select `b`.`a`)) = 1) +Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having (<1>((select 1)) = 1) SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; 1 1 diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result index 54213a20279..434bcd33a21 100644 --- a/mysql-test/r/subselect_no_semijoin.result +++ b/mysql-test/r/subselect_no_semijoin.result @@ -48,13 +48,13 @@ SELECT (SELECT a) as a; ERROR 42S22: Reference 'a' not supported (forward reference in item list) EXPLAIN EXTENDED SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +1 PRIMARY system NULL NULL NULL NULL 1 100.00 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 -Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having (<`b`.`a`>((select `b`.`a`)) = 1) +Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having (<1>((select 1)) = 1) SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; 1 1 diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index 261743e9d3c..0348a8428a5 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -110,7 +110,7 @@ show create view mysqltest.v1; ERROR 42000: SHOW VIEW command denied to user 'mysqltest_1'@'localhost' for table 'v1' explain select c from mysqltest.v2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 +1 PRIMARY system NULL NULL NULL NULL 0 const row not found 2 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table show create view mysqltest.v2; ERROR 42000: SHOW VIEW command denied to user 'mysqltest_1'@'localhost' for table 'v2' @@ -131,7 +131,7 @@ View Create View character_set_client collation_connection v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest`.`v1` AS select (`mysqltest`.`t1`.`a` + 1) AS `c`,(`mysqltest`.`t1`.`b` + 1) AS `d` from `mysqltest`.`t1` latin1 latin1_swedish_ci explain select c from mysqltest.v2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 +1 PRIMARY system NULL NULL NULL NULL 0 const row not found 2 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table show create view mysqltest.v2; View Create View character_set_client collation_connection @@ -144,7 +144,7 @@ View Create View character_set_client collation_connection v3 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest`.`v3` AS select (`mysqltest`.`t2`.`a` + 1) AS `c`,(`mysqltest`.`t2`.`b` + 1) AS `d` from `mysqltest`.`t2` latin1 latin1_swedish_ci explain select c from mysqltest.v4; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 +1 PRIMARY system NULL NULL NULL NULL 0 const row not found 2 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table show create view mysqltest.v4; View Create View character_set_client collation_connection diff --git a/mysql-test/suite/pbxt/r/subselect.result b/mysql-test/suite/pbxt/r/subselect.result index e91e720c6dd..e183cf389c0 100644 --- a/mysql-test/suite/pbxt/r/subselect.result +++ b/mysql-test/suite/pbxt/r/subselect.result @@ -44,13 +44,13 @@ SELECT (SELECT a) as a; ERROR 42S22: Reference 'a' not supported (forward reference in item list) EXPLAIN EXTENDED SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +1 PRIMARY system NULL NULL NULL NULL 1 100.00 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 -Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having (<`b`.`a`>((select `b`.`a`)) = 1) +Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having (<1>((select 1)) = 1) SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; 1 1 diff --git a/mysql-test/t/derived.test b/mysql-test/t/derived.test index 962cec95add..75368925499 100644 --- a/mysql-test/t/derived.test +++ b/mysql-test/t/derived.test @@ -315,3 +315,19 @@ WHERE j = SUBSTRING('12', (SELECT * FROM (SELECT MIN(j) FROM t1) t2))) t3; DROP TABLE t1; --echo # End of 5.0 tests + +--echo # +--echo # LP bug #793436: query with a derived table for which optimizer proves +--echo # that it contains not more than 1 row +--echo # + +CREATE TABLE t1 (a int, KEY (a)) ; +INSERT INTO t1 VALUES (3), (1); +CREATE TABLE t2 (a int); +INSERT INTO t2 VALUES (3); + +EXPLAIN +SELECT * FROM (SELECT DISTINCT * FROM t2) t, t1 WHERE t1.a = t.a; +SELECT * FROM (SELECT DISTINCT * FROM t2) t, t1 WHERE t1.a = t.a; + +DROP TABLE t1,t2; diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 021f7ccc636..ca56f0fa0a6 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -761,9 +761,12 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived) { if (!derived->is_merged_derived()) { + JOIN *join= first_select->join; unit->optimized= TRUE; - if ((res= first_select->join->optimize())) + if ((res= join->optimize())) goto err; + if (join->table_count == join->const_tables) + derived->fill_me= TRUE; } } /* From 7f345153f91c5395e5ab4ce5906fcabbb6d06f51 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 9 Jun 2011 00:13:00 -0700 Subject: [PATCH 23/24] Fixed LP bug #794038. INSERT/UPDATE/DELETE statement that used a temptable view v1 could lead to a crash if v1 was defined as a select from a mergeable view v2 that selected rows from a temptable view v3. When INSERT/UPDATE/DELETE uses a view that is not updatable then field translation for the view should be created before the prepare phase. --- mysql-test/r/view.result | 16 ++++++++++++++++ mysql-test/t/view.test | 20 ++++++++++++++++++++ sql/sql_derived.cc | 15 +++++++-------- sql/sql_parse.cc | 3 --- 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 4eaf1eb4793..9f720019303 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -4168,3 +4168,19 @@ a a b 2 2 30 DROP VIEW v2; DROP TABLE t1,t2; +# +# Bug#794038: crash with INSERT/UPDATE/DELETE +# over a non-updatable view +# +CREATE TABLE t1 (a int); +CREATE ALGORITHM = TEMPTABLE VIEW v1 AS SELECT * FROM t1; +CREATE ALGORITHM = MERGE VIEW v2 AS SELECT * FROM v1; +CREATE ALGORITHM = TEMPTABLE VIEW v3 AS SELECT * FROM v2; +INSERT INTO v3 VALUES (1); +ERROR HY000: The target table v3 of the INSERT is not insertable-into +UPDATE v3 SET a=0; +ERROR HY000: The target table v3 of the UPDATE is not updatable +DELETE FROM v3; +ERROR HY000: The target table v3 of the DELETE is not updatable +DROP VIEW v1,v2,v3; +DROP TABLE t1; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index b2032d5d6ba..626cc506e78 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -4114,3 +4114,23 @@ SELECT * FROM t1,v2 DROP VIEW v2; DROP TABLE t1,t2; + +--echo # +--echo # Bug#794038: crash with INSERT/UPDATE/DELETE +--echo # over a non-updatable view +--echo # + +CREATE TABLE t1 (a int); +CREATE ALGORITHM = TEMPTABLE VIEW v1 AS SELECT * FROM t1; +CREATE ALGORITHM = MERGE VIEW v2 AS SELECT * FROM v1; +CREATE ALGORITHM = TEMPTABLE VIEW v3 AS SELECT * FROM v2; + +-- error ER_NON_INSERTABLE_TABLE +INSERT INTO v3 VALUES (1); +-- error ER_NON_UPDATABLE_TABLE +UPDATE v3 SET a=0; +-- error ER_NON_UPDATABLE_TABLE +DELETE FROM v3; + +DROP VIEW v1,v2,v3; +DROP TABLE t1; diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index ca56f0fa0a6..9bfa49c5f2d 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -467,6 +467,12 @@ bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived) if (derived->merged_for_insert) return FALSE; + /* It's a target view for an INSERT, create field translation only. */ + if (!derived->updatable || derived->is_materialized_derived()) + { + bool res= derived->create_field_translation(thd); + return res; + } if (!derived->is_multitable()) { TABLE_LIST *tl=((TABLE_LIST*)dt_select->table_list.first); @@ -603,16 +609,9 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) bool res= FALSE; // Skip already prepared views/DT - if (!unit || unit->prepared) + if (!unit || unit->prepared || derived->merged_for_insert) DBUG_RETURN(FALSE); - /* It's a target view for an INSERT, create field translation only. */ - if (derived->merged_for_insert) - { - res= derived->create_field_translation(thd); - DBUG_RETURN(res); - } - Query_arena *arena= thd->stmt_arena, backup; if (arena->is_conventional()) arena= 0; // For easier test diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f891d307e9b..165a898910d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2697,9 +2697,6 @@ mysql_execute_command(THD *thd) } } } - if (mysql_handle_single_derived(thd->lex, create_table, - DT_MERGE_FOR_INSERT)) - DBUG_RETURN(1); /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ if (create_info.options & HA_LEX_CREATE_TMP_TABLE) From ab411f8f1c2e84082623c038eb024c15c58745b5 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 9 Jun 2011 12:43:28 -0700 Subject: [PATCH 24/24] Fixed LP bug #794909. The function generate_derived_keys did not take into account the fact that the last element in the array of keyuses could be just a barrier element. In some cases it could lead to a crash of the server. Also fixed a couple of other bugs in generate_derived_keys: the inner loop in the body of if this function did not change the cycle variables properly. --- mysql-test/r/derived_view.result | 21 +++++++++++++++++++++ mysql-test/t/derived_view.test | 20 ++++++++++++++++++++ sql/sql_select.cc | 14 +++++++++----- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/derived_view.result b/mysql-test/r/derived_view.result index 5125f91005f..5e3fc1e8cf1 100644 --- a/mysql-test/r/derived_view.result +++ b/mysql-test/r/derived_view.result @@ -568,3 +568,24 @@ TODO: Add test with 64 tables mergeable view to test fall back to materialization on tables > MAX_TABLES merge drop table t1,t2; drop view v1,v2,v3,v4,v6,v7; +# +# LP bug #794909: crash when defining possible keys for +# a materialized view/derived_table +# +CREATE TABLE t1 (f1 int) ; +INSERT INTO t1 VALUES (149), (150), (224), (29); +CREATE TABLE t2 (f1 int, KEY (f1)); +INSERT INTO t2 VALUES (149), (NULL), (224); +CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT * FROM t1; +EXPLAIN +SELECT * FROM v1 JOIN t2 ON v1.f1 = t2.f1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 index f1 f1 5 NULL 3 Using where; Using index +1 PRIMARY ref key0 key0 5 test.t2.f1 2 +2 DERIVED t1 ALL NULL NULL NULL NULL 4 +SELECT * FROM v1 JOIN t2 ON v1.f1 = t2.f1; +f1 f1 +149 149 +224 224 +DROP VIEW v1; +DROP TABLE t1,t2; diff --git a/mysql-test/t/derived_view.test b/mysql-test/t/derived_view.test index a8dd703d58c..93c2eb48fcd 100644 --- a/mysql-test/t/derived_view.test +++ b/mysql-test/t/derived_view.test @@ -215,3 +215,23 @@ select * from t1 join (select * from t2 group by f2) tt on t1.f1=tt.f2 join t1 x --echo materialization on tables > MAX_TABLES merge drop table t1,t2; drop view v1,v2,v3,v4,v6,v7; + +--echo # +--echo # LP bug #794909: crash when defining possible keys for +--echo # a materialized view/derived_table +--echo # + +CREATE TABLE t1 (f1 int) ; +INSERT INTO t1 VALUES (149), (150), (224), (29); + +CREATE TABLE t2 (f1 int, KEY (f1)); +INSERT INTO t2 VALUES (149), (NULL), (224); + +CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT * FROM t1; + +EXPLAIN +SELECT * FROM v1 JOIN t2 ON v1.f1 = t2.f1; +SELECT * FROM v1 JOIN t2 ON v1.f1 = t2.f1; + +DROP VIEW v1; +DROP TABLE t1,t2; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e5714ab4bbc..775a0ca2cb1 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8328,6 +8328,8 @@ bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array) TABLE *prev_table= 0; for (uint i= 0; i < elements; i++, keyuse++) { + if (!keyuse->table) + break; KEYUSE *first_table_keyuse= NULL; table_map last_used_tables= 0; uint count= 0; @@ -8353,11 +8355,13 @@ bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array) } count++; keyuse++; - if (keyuse->table != prev_table && - generate_derived_keys_for_table(first_table_keyuse, count, ++keys)) - return TRUE; - if (++i == elements) - break; + if (keyuse->table != prev_table) + { + if (generate_derived_keys_for_table(first_table_keyuse, count, ++keys)) + return TRUE; + keyuse--; + derived= NULL; + } } } return FALSE;