From eb09580b67ee19f7ac30c1a41c8307b9c7d482d1 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Tue, 28 May 2019 14:53:08 -0700 Subject: [PATCH 1/8] MDEV-19588 Wrong results from query, using left join. This bug could happen when queries with nested outer joins were executed employing join buffers. At such an execution if the method JOIN_CACHE::join_records() is called when a join buffer has become full no 'first_unmatched' field should be cleaned up in the JOIN_TAB structure to which the join cache with this buffer is attached. --- mysql-test/r/join_nested.result | 33 +++++++++++++++++++++++++++ mysql-test/r/join_nested_jcl6.result | 33 +++++++++++++++++++++++++++ mysql-test/t/join_nested.test | 34 ++++++++++++++++++++++++++++ sql/sql_join_cache.cc | 3 ++- 4 files changed, 102 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/join_nested.result b/mysql-test/r/join_nested.result index 6ddd39cbfec..d618f7c3ac8 100644 --- a/mysql-test/r/join_nested.result +++ b/mysql-test/r/join_nested.result @@ -1966,3 +1966,36 @@ Note 1003 select 1 AS `K1`,'T1Row1' AS `Name`,`t2a`.`K2` AS `K2`,`t2a`.`K1r` AS DROP VIEW v1; DROP TABLE t1,t2; set optimizer_search_depth= @tmp_mdev621; +# +# MDEV-19588: Nested left joins using optimized join cache +# +set optimizer_switch='optimize_join_buffer_size=on'; +set @save_join_cache_level= @@join_cache_level; +set join_cache_level=2; +CREATE TABLE t1 (i1 int, c1 varchar(20), pk int) engine=myisam; +CREATE TABLE t2 (pk int, c1 varchar(20), i1 int) engine=myisam; +INSERT INTO t2 VALUES (7,'a',-912),(8,'a',5); +CREATE TABLE t3 (pk int, c1 varchar(20), i1 int) engine=myisam; +INSERT INTO t3 VALUES +(1,'a',-145),(2,'a',6),(3,'a',1),(7,'a',NULL),(8,'a',889),(9,'a',146), +(10,'a',177),(16,'a',-433),(17,'a',NULL),(18,'a',2),(19,'a',3),(20,'a',5), +(21,'a',-484),(22,'a',369),(23,'a',-192),(24,'a',-163),(25,'a',5),(26,'a',NULL); +SELECT t3.* +FROM t3 LEFT JOIN t1 LEFT JOIN t2 ON t1.i1 = t2.i1 ON t3.i1 = t1.i1 +WHERE t2.pk < 13 OR t3.i1 IS NULL; +pk c1 i1 +7 a NULL +17 a NULL +26 a NULL +explain extended SELECT t3.* +FROM t3 LEFT JOIN t1 LEFT JOIN t2 ON t1.i1 = t2.i1 ON t3.i1 = t1.i1 +WHERE t2.pk < 13 OR t3.i1 IS NULL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 ALL NULL NULL NULL NULL 18 100.00 +1 SIMPLE t1 ALL NULL NULL NULL NULL 0 0.00 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 100.00 Using where; Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select `test`.`t3`.`pk` AS `pk`,`test`.`t3`.`c1` AS `c1`,`test`.`t3`.`i1` AS `i1` from `test`.`t3` left join (`test`.`t1` left join `test`.`t2` on((`test`.`t2`.`i1` = `test`.`t3`.`i1`))) on((`test`.`t1`.`i1` = `test`.`t3`.`i1`)) where ((`test`.`t2`.`pk` < 13) or isnull(`test`.`t3`.`i1`)) +DROP TABLE t1,t2,t3; +set join_cache_level= @save_join_cache_level; +set optimizer_switch=@save_optimizer_switch; diff --git a/mysql-test/r/join_nested_jcl6.result b/mysql-test/r/join_nested_jcl6.result index bac8e1cb7db..b99b0200792 100644 --- a/mysql-test/r/join_nested_jcl6.result +++ b/mysql-test/r/join_nested_jcl6.result @@ -1977,6 +1977,39 @@ Note 1003 select 1 AS `K1`,'T1Row1' AS `Name`,`t2a`.`K2` AS `K2`,`t2a`.`K1r` AS DROP VIEW v1; DROP TABLE t1,t2; set optimizer_search_depth= @tmp_mdev621; +# +# MDEV-19588: Nested left joins using optimized join cache +# +set optimizer_switch='optimize_join_buffer_size=on'; +set @save_join_cache_level= @@join_cache_level; +set join_cache_level=2; +CREATE TABLE t1 (i1 int, c1 varchar(20), pk int) engine=myisam; +CREATE TABLE t2 (pk int, c1 varchar(20), i1 int) engine=myisam; +INSERT INTO t2 VALUES (7,'a',-912),(8,'a',5); +CREATE TABLE t3 (pk int, c1 varchar(20), i1 int) engine=myisam; +INSERT INTO t3 VALUES +(1,'a',-145),(2,'a',6),(3,'a',1),(7,'a',NULL),(8,'a',889),(9,'a',146), +(10,'a',177),(16,'a',-433),(17,'a',NULL),(18,'a',2),(19,'a',3),(20,'a',5), +(21,'a',-484),(22,'a',369),(23,'a',-192),(24,'a',-163),(25,'a',5),(26,'a',NULL); +SELECT t3.* +FROM t3 LEFT JOIN t1 LEFT JOIN t2 ON t1.i1 = t2.i1 ON t3.i1 = t1.i1 +WHERE t2.pk < 13 OR t3.i1 IS NULL; +pk c1 i1 +7 a NULL +17 a NULL +26 a NULL +explain extended SELECT t3.* +FROM t3 LEFT JOIN t1 LEFT JOIN t2 ON t1.i1 = t2.i1 ON t3.i1 = t1.i1 +WHERE t2.pk < 13 OR t3.i1 IS NULL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 ALL NULL NULL NULL NULL 18 100.00 +1 SIMPLE t1 ALL NULL NULL NULL NULL 0 0.00 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 100.00 Using where; Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select `test`.`t3`.`pk` AS `pk`,`test`.`t3`.`c1` AS `c1`,`test`.`t3`.`i1` AS `i1` from `test`.`t3` left join (`test`.`t1` left join `test`.`t2` on((`test`.`t2`.`i1` = `test`.`t3`.`i1`))) on((`test`.`t1`.`i1` = `test`.`t3`.`i1`)) where ((`test`.`t2`.`pk` < 13) or isnull(`test`.`t3`.`i1`)) +DROP TABLE t1,t2,t3; +set join_cache_level= @save_join_cache_level; +set optimizer_switch=@save_optimizer_switch; CREATE TABLE t5 (a int, b int, c int, PRIMARY KEY(a), KEY b_i (b)); CREATE TABLE t6 (a int, b int, c int, PRIMARY KEY(a), KEY b_i (b)); CREATE TABLE t7 (a int, b int, c int, PRIMARY KEY(a), KEY b_i (b)); diff --git a/mysql-test/t/join_nested.test b/mysql-test/t/join_nested.test index e60b7827f75..cfb24a63304 100644 --- a/mysql-test/t/join_nested.test +++ b/mysql-test/t/join_nested.test @@ -1380,3 +1380,37 @@ DROP VIEW v1; DROP TABLE t1,t2; set optimizer_search_depth= @tmp_mdev621; + +--echo # +--echo # MDEV-19588: Nested left joins using optimized join cache +--echo # + +set optimizer_switch='optimize_join_buffer_size=on'; + +set @save_join_cache_level= @@join_cache_level; +set join_cache_level=2; + +CREATE TABLE t1 (i1 int, c1 varchar(20), pk int) engine=myisam; + +CREATE TABLE t2 (pk int, c1 varchar(20), i1 int) engine=myisam; +INSERT INTO t2 VALUES (7,'a',-912),(8,'a',5); + +CREATE TABLE t3 (pk int, c1 varchar(20), i1 int) engine=myisam; +INSERT INTO t3 VALUES +(1,'a',-145),(2,'a',6),(3,'a',1),(7,'a',NULL),(8,'a',889),(9,'a',146), +(10,'a',177),(16,'a',-433),(17,'a',NULL),(18,'a',2),(19,'a',3),(20,'a',5), +(21,'a',-484),(22,'a',369),(23,'a',-192),(24,'a',-163),(25,'a',5),(26,'a',NULL); + +let $q= +SELECT t3.* +FROM t3 LEFT JOIN t1 LEFT JOIN t2 ON t1.i1 = t2.i1 ON t3.i1 = t1.i1 +WHERE t2.pk < 13 OR t3.i1 IS NULL; + +eval $q; +eval explain extended $q; + +DROP TABLE t1,t2,t3; + +set join_cache_level= @save_join_cache_level; + +set optimizer_switch=@save_optimizer_switch; diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index 73ee2013811..909eeb84971 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -2150,7 +2150,8 @@ enum_nested_loop_state JOIN_CACHE::join_records(bool skip_last) } finish: - if (outer_join_first_inner) + if (outer_join_first_inner && + join_tab->first_inner == join_tab->first_unmatched) { /* All null complemented rows have been already generated for all From cbb90f77cdbf57c02145dc6cd86acf8ebb8a88f0 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Tue, 28 May 2019 23:26:36 -0700 Subject: [PATCH 2/8] MDEV-18479 Complement This patch complements the patch that fixes bug MDEV-18479. This patch takes care of possible overflow when calculating the estimated number of rows in a materialized derived table / view. --- include/my_base.h | 1 + mysql-test/r/derived_view.result | 20 ++++++++++---------- mysql-test/t/derived_view.test | 2 +- sql/sql_lex.cc | 5 ++++- sql/sql_select.cc | 21 +++++++++++++-------- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/include/my_base.h b/include/my_base.h index 54e8443d86c..86be94399d6 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -586,6 +586,7 @@ typedef ulong ha_rows; #define HA_POS_ERROR (~ (ha_rows) 0) #define HA_OFFSET_ERROR (~ (my_off_t) 0) +#define HA_ROWS_MAX HA_POS_ERROR #if SYSTEM_SIZEOF_OFF_T == 4 #define MAX_FILE_SIZE INT_MAX32 diff --git a/mysql-test/r/derived_view.result b/mysql-test/r/derived_view.result index ab363934e8f..d74b532d5e8 100644 --- a/mysql-test/r/derived_view.result +++ b/mysql-test/r/derived_view.result @@ -2641,7 +2641,7 @@ DROP TABLE t1, t2; set optimizer_switch=@exit_optimizer_switch; set join_cache_level=@exit_join_cache_level; # -# Bug mdev-12812: EXPLAIN for query with many expensive derived +# Bug mdev-18479: EXPLAIN for query with many expensive derived # CREATE TABLE t1 (id int auto_increment primary key, @@ -2942,15 +2942,15 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE p10 ALL NULL NULL NULL NULL 550 Using where; Using join buffer (incremental, BNL join) 1 SIMPLE ALL NULL NULL NULL NULL 50328437500000 Using where; Using join buffer (incremental, BNL join) 1 SIMPLE ALL NULL NULL NULL NULL 27680640625000000 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL 7798774269472204288 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL 7798774269472204288 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL -3222391729959551616 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL -3222391729959551616 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL -3222391729959551616 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL -3222391729959551616 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL -3222391729959551616 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL -3222391729959551616 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL -3222391729959551616 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) 17 DERIVED t2 system NULL NULL NULL NULL 1 17 DERIVED p4 ALL NULL NULL NULL NULL 550 Using where 17 DERIVED p5 ALL NULL NULL NULL NULL 550 Using where; Using join buffer (flat, BNL join) diff --git a/mysql-test/t/derived_view.test b/mysql-test/t/derived_view.test index f6613e2593a..61c4278b43f 100644 --- a/mysql-test/t/derived_view.test +++ b/mysql-test/t/derived_view.test @@ -1936,7 +1936,7 @@ set optimizer_switch=@exit_optimizer_switch; set join_cache_level=@exit_join_cache_level; --echo # ---echo # Bug mdev-12812: EXPLAIN for query with many expensive derived +--echo # Bug mdev-18479: EXPLAIN for query with many expensive derived --echo # CREATE TABLE t1 diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 3e20cdb48da..28f56282bad 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -4100,7 +4100,10 @@ void SELECT_LEX::increase_derived_records(ha_rows records) DBUG_ASSERT(unit->derived); select_union *result= (select_union*)unit->result; - result->records+= records; + if (HA_ROWS_MAX - records > result->records) + result->records+= records; + else + result->records= HA_ROWS_MAX; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 961a7dac265..37f8292b563 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3830,7 +3830,7 @@ make_join_statistics(JOIN *join, List &tables_list, DBUG_RETURN(TRUE); /* purecov: inspected */ { - ha_rows records= 1; + double records= 1; SELECT_LEX_UNIT *unit= join->select_lex->master_unit(); /* Find an optimal join order of the non-constant tables. */ @@ -3855,10 +3855,14 @@ make_join_statistics(JOIN *join, List &tables_list, table/view. */ for (i= 0; i < join->table_count ; i++) - records*= join->best_positions[i].records_read ? - (ha_rows)join->best_positions[i].records_read : 1; - set_if_smaller(records, unit->select_limit_cnt); - join->select_lex->increase_derived_records(records); + { + records= COST_MULT(records, + join->best_positions[i].records_read ? + join->best_positions[i].records_read : 1); + } + ha_rows rows= records > HA_ROWS_MAX ? HA_ROWS_MAX : (ha_rows) records; + set_if_smaller(rows, unit->select_limit_cnt); + join->select_lex->increase_derived_records(rows); } } @@ -10795,7 +10799,7 @@ ha_rows JOIN_TAB::get_examined_rows() } } else - examined_rows= (ha_rows) records_read; + examined_rows= (ha_rows) records_read; return examined_rows; } @@ -22924,8 +22928,9 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, else { ha_rows examined_rows= tab->get_examined_rows(); - - item_list.push_back(new Item_int((longlong) (ulonglong) examined_rows, + ha_rows displ_rows= examined_rows; + set_if_smaller(displ_rows, HA_ROWS_MAX/2); + item_list.push_back(new Item_int((longlong) (ulonglong) displ_rows, MY_INT64_NUM_DECIMAL_DIGITS)); /* Add "filtered" field to item_list. */ From 5de08a53ef8567d9c316c5a9eb4eda0ed6a2b11e Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Thu, 11 Apr 2019 15:46:39 +0300 Subject: [PATCH 3/8] MDEV-13631 Make use of clang-format Add .clang-format files to a root directory and to storage/innobase --- .clang-format | 18 ++++++++++++++++++ storage/innobase/.clang-format | 10 ++++++++++ 2 files changed, 28 insertions(+) create mode 100644 .clang-format create mode 100644 storage/innobase/.clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000000..3b735b16d74 --- /dev/null +++ b/.clang-format @@ -0,0 +1,18 @@ +SpaceBeforeAssignmentOperators: false +SpaceAfterCStyleCast: true + +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true diff --git a/storage/innobase/.clang-format b/storage/innobase/.clang-format new file mode 100644 index 00000000000..f7a72f3cf24 --- /dev/null +++ b/storage/innobase/.clang-format @@ -0,0 +1,10 @@ +UseTab: Always +TabWidth: 8 +IndentWidth: 8 +BreakBeforeBinaryOperators: All +PointerAlignment: Left +AlwaysBreakAfterReturnType: TopLevel +BreakBeforeBraces: Custom +BraceWrapping: + AfterFunction: true +AccessModifierOffset: -8 From 1d4ac3d4d3ec3d52284f7260907111618f6cb6d9 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 22 May 2019 22:05:56 +0200 Subject: [PATCH 4/8] cleanup --- mysql-test/r/derived.result | 1 - mysql-test/r/multi_update.result | 4 ++- mysql-test/r/multi_update_innodb.result | 3 ++ mysql-test/t/derived.test | 3 -- mysql-test/t/multi_update.test | 9 ++---- mysql-test/t/multi_update_innodb.test | 4 +++ sql/sql_base.cc | 8 ++--- sql/sql_base.h | 12 +------- sql/sql_update.cc | 41 ++++++++----------------- 9 files changed, 30 insertions(+), 55 deletions(-) diff --git a/mysql-test/r/derived.result b/mysql-test/r/derived.result index 5debfaed5e3..2c0d0095678 100644 --- a/mysql-test/r/derived.result +++ b/mysql-test/r/derived.result @@ -1,4 +1,3 @@ -drop table if exists t1,t2,t3; set @save_derived_optimizer_switch=@@optimizer_switch; set optimizer_switch='derived_merge=off,derived_with_keys=off'; select * from (select 2 from DUAL) b; diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index e04637a3a54..e38d8737355 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -1038,4 +1038,6 @@ triggered triggered drop table t1,t2, t3; drop user foo; -end of 5.5 tests +# +# end of 5.5 tests +# diff --git a/mysql-test/r/multi_update_innodb.result b/mysql-test/r/multi_update_innodb.result index 643287c3a93..1d727f00e3e 100644 --- a/mysql-test/r/multi_update_innodb.result +++ b/mysql-test/r/multi_update_innodb.result @@ -67,3 +67,6 @@ SELECT * FROM t2; col_int_key pk_1 pk_2 col_int 1 2 3 4 DROP TABLE t1,t2; +# +# end of 5.5 tests +# diff --git a/mysql-test/t/derived.test b/mysql-test/t/derived.test index 217203422e0..62b2c43ca12 100644 --- a/mysql-test/t/derived.test +++ b/mysql-test/t/derived.test @@ -1,7 +1,4 @@ # Initialize ---disable_warnings -drop table if exists t1,t2,t3; ---enable_warnings set @save_derived_optimizer_switch=@@optimizer_switch; set optimizer_switch='derived_merge=off,derived_with_keys=off'; diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index 964108b9b99..14c5574f61c 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -371,7 +371,6 @@ drop table t1, t2; # connect (root,localhost,root,,test,$MASTER_MYPORT,$MASTER_MYSOCK); -connection root; --disable_warnings create database mysqltest; --enable_warnings @@ -381,7 +380,6 @@ create table mysqltest.t3 (a int, b int, primary key (a)); grant select on mysqltest.* to mysqltest_1@localhost; grant update on mysqltest.t1 to mysqltest_1@localhost; connect (user1,localhost,mysqltest_1,,mysqltest,$MASTER_MYPORT,$MASTER_MYSOCK); -connection user1; update t1, t2 set t1.b=1 where t1.a=t2.a; update t1, t2 set t1.b=(select t3.b from t3 where t1.a=t3.a) where t1.a=t2.a; connection root; @@ -494,15 +492,12 @@ insert into t2 values (10), (20), (30); create view v1 as select a as b, a/10 as a from t2; connect (locker,localhost,root,,test); -connection locker; lock table t1 write; connect (changer,localhost,root,,test); -connection changer; send alter table t1 add column c int default 100 after a; connect (updater,localhost,root,,test); -connection updater; # Wait till "alter table t1 ..." of session changer is in work. # = There is one session waiting. let $wait_condition= select count(*)= 1 from information_schema.processlist @@ -1086,5 +1081,7 @@ select * from t2; drop table t1,t2, t3; drop user foo; ---echo end of 5.5 tests +--echo # +--echo # end of 5.5 tests +--echo # diff --git a/mysql-test/t/multi_update_innodb.test b/mysql-test/t/multi_update_innodb.test index 51757c29553..5807af4f489 100644 --- a/mysql-test/t/multi_update_innodb.test +++ b/mysql-test/t/multi_update_innodb.test @@ -75,3 +75,7 @@ UPDATE t2 AS A NATURAL JOIN t2 B SET A.pk_2=10,B.pk_2=11; SELECT * FROM t2; DROP TABLE t1,t2; + +--echo # +--echo # end of 5.5 tests +--echo # diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 4cc7b2fe3b9..a98cfbbd5c0 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5003,8 +5003,7 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, @retval TRUE Error, reported. */ -bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, - Sroutine_hash_entry **sroutine_to_open_list, uint flags, +bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, Prelocking_strategy *prelocking_strategy) { /* @@ -5053,7 +5052,7 @@ restart: has_prelocking_list= thd->lex->requires_prelocking(); table_to_open= start; - sroutine_to_open= sroutine_to_open_list; + sroutine_to_open= &thd->lex->sroutines_list.first; *counter= 0; thd_proc_info(thd, "Opening tables"); @@ -5112,8 +5111,7 @@ restart: elements in prelocking list/set. */ while (*table_to_open || - (thd->locked_tables_mode <= LTM_LOCK_TABLES && - *sroutine_to_open)) + (thd->locked_tables_mode <= LTM_LOCK_TABLES && *sroutine_to_open)) { /* For every table in the list of tables to open, try to find or open diff --git a/sql/sql_base.h b/sql/sql_base.h index 8a2317d2386..ea92e880db7 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -276,8 +276,7 @@ int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order); bool lock_table_names(THD *thd, TABLE_LIST *table_list, TABLE_LIST *table_list_end, ulong lock_wait_timeout, uint flags); -bool open_tables(THD *thd, TABLE_LIST **tables, uint *counter, - Sroutine_hash_entry **sroutine_to_open, uint flags, +bool open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags, Prelocking_strategy *prelocking_strategy); /* open_and_lock_tables with optional derived handling */ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, @@ -499,15 +498,6 @@ private: }; -inline bool -open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags, - Prelocking_strategy *prelocking_strategy) -{ - return open_tables(thd, tables, counter, &thd->lex->sroutines_list.first, - flags, prelocking_strategy); -} - - inline bool open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags) { diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 38444911523..ac066507042 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1251,9 +1251,8 @@ int mysql_multi_update_prepare(THD *thd) 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, FALSE)) + table_list, lex->select_lex.leaf_tables, + FALSE, UPDATE_ACL, SELECT_ACL, FALSE)) DBUG_RETURN(TRUE); if (lex->select_lex.handle_derived(thd->lex, DT_MERGE)) @@ -1272,9 +1271,7 @@ int mysql_multi_update_prepare(THD *thd) } if (update_view && check_fields(thd, *fields)) - { DBUG_RETURN(TRUE); - } thd->table_map_for_update= tables_for_update= get_table_map(fields); @@ -1293,8 +1290,7 @@ int mysql_multi_update_prepare(THD *thd) TABLE *table= tl->table; /* Only set timestamp column if this is not modified */ if (table->timestamp_field && - bitmap_is_set(table->write_set, - table->timestamp_field->field_index)) + bitmap_is_set(table->write_set, table->timestamp_field->field_index)) table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; /* if table will be updated then check that it is unique */ @@ -1355,10 +1351,8 @@ int mysql_multi_update_prepare(THD *thd) if (!tl->is_derived()) { uint want_privilege= tl->updating ? UPDATE_ACL : SELECT_ACL; - if (check_access(thd, want_privilege, tl->db, - &tl->grant.privilege, - &tl->grant.m_internal, - 0, 0) || + if (check_access(thd, want_privilege, tl->db, &tl->grant.privilege, + &tl->grant.m_internal, 0, 0) || check_grant(thd, want_privilege, tl, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); } @@ -1434,25 +1428,18 @@ int mysql_multi_update_prepare(THD *thd) Setup multi-update handling and call SELECT to do the join */ -bool mysql_multi_update(THD *thd, - TABLE_LIST *table_list, - List *fields, - List *values, - COND *conds, - ulonglong options, +bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List *fields, + List *values, COND *conds, ulonglong options, enum enum_duplicates handle_duplicates, - bool ignore, - SELECT_LEX_UNIT *unit, - SELECT_LEX *select_lex, - multi_update **result) + bool ignore, SELECT_LEX_UNIT *unit, + SELECT_LEX *select_lex, multi_update **result) { bool res; DBUG_ENTER("mysql_multi_update"); if (!(*result= new multi_update(table_list, &thd->lex->select_lex.leaf_tables, - fields, values, - handle_duplicates, ignore))) + fields, values, handle_duplicates, ignore))) { DBUG_RETURN(TRUE); } @@ -1463,11 +1450,9 @@ bool mysql_multi_update(THD *thd, List total_list; - res= mysql_select(thd, &select_lex->ref_pointer_array, - table_list, select_lex->with_wild, - total_list, - conds, 0, (ORDER *) NULL, (ORDER *)NULL, (Item *) NULL, - (ORDER *)NULL, + res= mysql_select(thd, &select_lex->ref_pointer_array, table_list, + select_lex->with_wild, total_list, conds, 0, NULL, NULL, + NULL, NULL, options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE, *result, unit, select_lex); From 6660c072ada63847e0284026598f65f1e6d6bb2e Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 22 May 2019 21:56:36 +0200 Subject: [PATCH 5/8] MDEV-19491 update query stopped working after mariadb upgrade 10.2.23 -> 10.2.24 as well as MDEV-19500 Update with join stopped worked if there is a call to a procedure in a trigger MDEV-19521 Update Table Fails with Trigger and Stored Function MDEV-19497 Replication stops because table not found MDEV-19527 UPDATE + JOIN + TRIGGERS = table doesn't exists error Reimplement the fix for (5d510fdbf00) MDEV-18507 can't update temporary table when joined with table with triggers on read-only instead of calling open_tables() twice, put multi-update prepare code inside open_tables() loop. Add a test for a MDL backoff-and-retry loop inside open_tables() across multi-update prepare code. --- mysql-test/r/multi_update.result | 18 ++ mysql-test/r/multi_update_debug.result | 13 ++ mysql-test/r/multi_update_innodb.result | 14 ++ mysql-test/t/multi_update.test | 29 ++++ mysql-test/t/multi_update_debug.test | 27 +++ mysql-test/t/multi_update_innodb.test | 18 ++ sql/sql_base.cc | 3 + sql/sql_base.h | 2 + sql/sql_update.cc | 210 +++++++++++++----------- 9 files changed, 234 insertions(+), 100 deletions(-) create mode 100644 mysql-test/r/multi_update_debug.result create mode 100644 mysql-test/t/multi_update_debug.test diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index e38d8737355..05fc619c50c 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -1038,6 +1038,24 @@ triggered triggered drop table t1,t2, t3; drop user foo; +create table t1 (a int, b int); +create table t2 (c int, d int); +insert t1 values (1,2),(3,4); +insert t2 values (5,6),(7,8); +create table t0 (x int); +insert t0 values (11), (22); +create trigger tr1 before update on t2 for each row insert t0 values (new.c); +lock table t0 write; +update t1 join t2 on (a=c+4) set b=d; +drop table t1, t2, t0; +create table t1 (a int, b varchar(50), c varchar(50)); +insert t1 (a,b) values (1,'1'), (2,'2'), (3,'3'); +create function f1() returns varchar(50) return 'result'; +create trigger tr before update on t1 for each row set new.c = (select f1()); +create table t2 select a, b from t1; +update t1 join t2 using (a) set t1.b = t2.b; +drop table t1, t2; +drop function f1; # # end of 5.5 tests # diff --git a/mysql-test/r/multi_update_debug.result b/mysql-test/r/multi_update_debug.result new file mode 100644 index 00000000000..8bcd813e5b7 --- /dev/null +++ b/mysql-test/r/multi_update_debug.result @@ -0,0 +1,13 @@ +create table t1 (a int, b int); +create table t2 (c int, d int); +insert t1 values (1,2),(3,4); +insert t2 values (5,6),(7,8); +create table t0 (x int); +insert t0 values (11), (22); +create trigger tr1 before update on t1 for each row insert t0 values (new.b); +set debug_sync='open_tables_after_open_and_process_table WAIT_FOR cont'; +update t1 join t2 on (a=c+4) set b=d; +set debug_sync='mdl_acquire_lock_wait SIGNAL cont'; +lock table t1 write, t0 write; +drop table t1, t2, t0; +set debug_sync='reset'; diff --git a/mysql-test/r/multi_update_innodb.result b/mysql-test/r/multi_update_innodb.result index 1d727f00e3e..5da8cf46f5a 100644 --- a/mysql-test/r/multi_update_innodb.result +++ b/mysql-test/r/multi_update_innodb.result @@ -67,6 +67,20 @@ SELECT * FROM t2; col_int_key pk_1 pk_2 col_int 1 2 3 4 DROP TABLE t1,t2; +create table t1 (id serial, size int(11)) engine=innodb; +create table t2 (id serial, size int, account_id int) engine=innodb; +create table t3 (id serial, size int, article_id int) engine=innodb; +create table t4 (id serial, file_id int, article_id int) engine=innodb; +insert t1 values(null, 400); +insert t2 values(null, 0, 1), (null, 1, 1); +insert t3 values(null, 100, 1); +insert t4 values(null, 1, 2); +create trigger file_update_article before update on t3 for each row +update t2 set t2.size = new.size where t2.id = new.article_id; +create trigger article_update_account before update on t2 for each row +update t1 set t1.size = t1.size + new.size where t1.id = new.account_id; +update t3 join t4 on t4.file_id =t3.id and t4.article_id=2 set t3.size=t3.size + 2; +drop table t1, t2, t3, t4; # # end of 5.5 tests # diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index 14c5574f61c..26943c347c0 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -1081,6 +1081,35 @@ select * from t2; drop table t1,t2, t3; drop user foo; +# +# Another test on not-opening tables unnecessary +# +create table t1 (a int, b int); +create table t2 (c int, d int); +insert t1 values (1,2),(3,4); +insert t2 values (5,6),(7,8); +create table t0 (x int); +insert t0 values (11), (22); +create trigger tr1 before update on t2 for each row insert t0 values (new.c); +connect con1, localhost, root; +lock table t0 write; +connection default; +update t1 join t2 on (a=c+4) set b=d; +disconnect con1; +drop table t1, t2, t0; + +# +# MDEV-19521 Update Table Fails with Trigger and Stored Function +# +create table t1 (a int, b varchar(50), c varchar(50)); +insert t1 (a,b) values (1,'1'), (2,'2'), (3,'3'); +create function f1() returns varchar(50) return 'result'; +create trigger tr before update on t1 for each row set new.c = (select f1()); +create table t2 select a, b from t1; +update t1 join t2 using (a) set t1.b = t2.b; +drop table t1, t2; +drop function f1; + --echo # --echo # end of 5.5 tests --echo # diff --git a/mysql-test/t/multi_update_debug.test b/mysql-test/t/multi_update_debug.test new file mode 100644 index 00000000000..2da376e1b87 --- /dev/null +++ b/mysql-test/t/multi_update_debug.test @@ -0,0 +1,27 @@ +# +# test MDL backoff-and-retry during multi-update +# +source include/have_debug_sync.inc; +create table t1 (a int, b int); +create table t2 (c int, d int); +insert t1 values (1,2),(3,4); +insert t2 values (5,6),(7,8); +create table t0 (x int); +insert t0 values (11), (22); +create trigger tr1 before update on t1 for each row insert t0 values (new.b); + +set debug_sync='open_tables_after_open_and_process_table WAIT_FOR cont'; +send update t1 join t2 on (a=c+4) set b=d; + +connect con1, localhost, root; +let $wait_condition= select count(*) from information_schema.processlist where state = ' debug sync point: open_tables_after_open_and_process_table' +source include/wait_condition.inc; +set debug_sync='mdl_acquire_lock_wait SIGNAL cont'; +lock table t1 write, t0 write; +let $wait_condition= select count(*) from information_schema.processlist where state = 'Waiting for table metadata lock' +source include/wait_condition.inc; +disconnect con1; +connection default; +reap; +drop table t1, t2, t0; +set debug_sync='reset'; diff --git a/mysql-test/t/multi_update_innodb.test b/mysql-test/t/multi_update_innodb.test index 5807af4f489..9042f267c66 100644 --- a/mysql-test/t/multi_update_innodb.test +++ b/mysql-test/t/multi_update_innodb.test @@ -76,6 +76,24 @@ SELECT * FROM t2; DROP TABLE t1,t2; +# +# MDEV-19491 update query stopped working after mariadb upgrade 10.2.23 -> 10.2.24 +# +create table t1 (id serial, size int(11)) engine=innodb; +create table t2 (id serial, size int, account_id int) engine=innodb; +create table t3 (id serial, size int, article_id int) engine=innodb; +create table t4 (id serial, file_id int, article_id int) engine=innodb; +insert t1 values(null, 400); +insert t2 values(null, 0, 1), (null, 1, 1); +insert t3 values(null, 100, 1); +insert t4 values(null, 1, 2); +create trigger file_update_article before update on t3 for each row + update t2 set t2.size = new.size where t2.id = new.article_id; +create trigger article_update_account before update on t2 for each row + update t1 set t1.size = t1.size + new.size where t1.id = new.account_id; +update t3 join t4 on t4.file_id =t3.id and t4.article_id=2 set t3.size=t3.size + 2; +drop table t1, t2, t3, t4; + --echo # --echo # end of 5.5 tests --echo # diff --git a/sql/sql_base.cc b/sql/sql_base.cc index a98cfbbd5c0..27ee8957b25 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5055,6 +5055,7 @@ restart: sroutine_to_open= &thd->lex->sroutines_list.first; *counter= 0; thd_proc_info(thd, "Opening tables"); + prelocking_strategy->reset(thd); /* If we are executing LOCK TABLES statement or a DDL statement @@ -5218,6 +5219,8 @@ restart: } } } + if ((error= prelocking_strategy->handle_end(thd))) + goto err; } /* diff --git a/sql/sql_base.h b/sql/sql_base.h index ea92e880db7..439052a28f5 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -426,6 +426,7 @@ class Prelocking_strategy public: virtual ~Prelocking_strategy() { } + virtual void reset(THD *thd) { }; virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx, Sroutine_hash_entry *rt, sp_head *sp, bool *need_prelocking) = 0; @@ -433,6 +434,7 @@ public: TABLE_LIST *table_list, bool *need_prelocking) = 0; virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx, TABLE_LIST *table_list, bool *need_prelocking)= 0; + virtual bool handle_end(THD *thd) { return 0; }; }; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index ac066507042..e01fe0926dd 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1188,103 +1188,81 @@ bool unsafe_key_update(List leaves, table_map tables_for_update) } -/* - make update specific preparation and checks after opening tables - - SYNOPSIS - mysql_multi_update_prepare() - thd thread handler - - RETURN - FALSE OK - TRUE Error -*/ - -int mysql_multi_update_prepare(THD *thd) +class Multiupdate_prelocking_strategy : public DML_prelocking_strategy { + bool done; + bool has_prelocking_list; +public: + void reset(THD *thd); + bool handle_end(THD *thd); +}; + +void Multiupdate_prelocking_strategy::reset(THD *thd) +{ + done= false; + has_prelocking_list= thd->lex->requires_prelocking(); +} + +/** + Determine what tables could be updated in the multi-update + + For these tables we'll need to open triggers and continue prelocking + until all is open. +*/ +bool Multiupdate_prelocking_strategy::handle_end(THD *thd) +{ + DBUG_ENTER("Multiupdate_prelocking_strategy::handle_end"); + if (done) + DBUG_RETURN(0); + LEX *lex= thd->lex; - TABLE_LIST *table_list= lex->query_tables; - TABLE_LIST *tl; - List *fields= &lex->select_lex.item_list; - table_map tables_for_update; - bool update_view= 0; - DML_prelocking_strategy prelocking_strategy; - bool has_prelocking_list= thd->lex->requires_prelocking(); + SELECT_LEX *select_lex= &lex->select_lex; + TABLE_LIST *table_list= lex->query_tables, *tl; - /* - if this multi-update was converted from usual update, here is table - counter else junk will be assigned here, but then replaced with real - count in open_tables() - */ - uint table_count= lex->table_count; - const bool using_lock_tables= thd->locked_tables_mode != LTM_NONE; - bool original_multiupdate= (thd->lex->sql_command == SQLCOM_UPDATE_MULTI); - DBUG_ENTER("mysql_multi_update_prepare"); + done= true; - /* following need for prepared statements, to run next time multi-update */ - thd->lex->sql_command= SQLCOM_UPDATE_MULTI; + if (mysql_handle_derived(lex, DT_INIT) || + mysql_handle_derived(lex, DT_MERGE_FOR_INSERT) || + mysql_handle_derived(lex, DT_PREPARE)) + DBUG_RETURN(1); - /* - Open tables and create derived ones, but do not lock and fill them yet. - - During prepare phase acquire only S metadata locks instead of SW locks to - keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE - and global read lock. - */ - if ((original_multiupdate && open_tables(thd, &table_list, &table_count, - thd->stmt_arena->is_stmt_prepare() - ? MYSQL_OPEN_FORCE_SHARED_MDL : 0, - &prelocking_strategy)) || - 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_derived(lex, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); - if (mysql_handle_derived(lex, DT_PREPARE)) - DBUG_RETURN(TRUE); + 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, FALSE)) + 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, FALSE)) - DBUG_RETURN(TRUE); - - if (lex->select_lex.handle_derived(thd->lex, DT_MERGE)) - DBUG_RETURN(TRUE); + if (select_lex->handle_derived(thd->lex, DT_MERGE)) + DBUG_RETURN(1); + List *fields= &lex->select_lex.item_list; if (setup_fields_with_no_wrap(thd, 0, *fields, MARK_COLUMNS_WRITE, 0, 0)) - DBUG_RETURN(TRUE); + DBUG_RETURN(1); for (tl= table_list; tl ; tl= tl->next_local) - { if (tl->view) { - update_view= 1; - break; + if (check_fields(thd, *fields)) + DBUG_RETURN(1); + else + break; } - } - if (update_view && check_fields(thd, *fields)) - DBUG_RETURN(TRUE); + table_map tables_for_update= thd->table_map_for_update= get_table_map(fields); - thd->table_map_for_update= tables_for_update= get_table_map(fields); - - if (unsafe_key_update(lex->select_lex.leaf_tables, tables_for_update)) - DBUG_RETURN(true); - - TABLE_LIST **new_tables= lex->query_tables_last; - DBUG_ASSERT(*new_tables== NULL); + if (unsafe_key_update(select_lex->leaf_tables, tables_for_update)) + DBUG_RETURN(1); /* Setup timestamp handling and locking mode */ - List_iterator ti(lex->select_lex.leaf_tables); + List_iterator ti(select_lex->leaf_tables); + const bool using_lock_tables= thd->locked_tables_mode != LTM_NONE; while ((tl= ti++)) { TABLE *table= tl->table; @@ -1299,7 +1277,7 @@ int mysql_multi_update_prepare(THD *thd) if (!tl->single_table_updatable() || check_key_in_view(thd, tl)) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), tl->alias, "UPDATE"); - DBUG_RETURN(TRUE); + DBUG_RETURN(1); } DBUG_PRINT("info",("setting table `%s` for update", tl->alias)); @@ -1310,8 +1288,8 @@ int mysql_multi_update_prepare(THD *thd) tl->updating= 1; if (tl->belong_to_view) tl->belong_to_view->updating= 1; - if (extend_table_list(thd, tl, &prelocking_strategy, has_prelocking_list)) - DBUG_RETURN(TRUE); + if (extend_table_list(thd, tl, this, has_prelocking_list)) + DBUG_RETURN(1); } else { @@ -1333,15 +1311,62 @@ int mysql_multi_update_prepare(THD *thd) } } - uint addon_table_count= 0; - if (*new_tables) + /* check single table update for view compound from several tables */ + for (tl= table_list; tl; tl= tl->next_local) { - Sroutine_hash_entry **new_routines= thd->lex->sroutines_list.next; - DBUG_ASSERT(*new_routines == NULL); - if (open_tables(thd, new_tables, &addon_table_count, new_routines, - thd->stmt_arena->is_stmt_prepare() - ? MYSQL_OPEN_FORCE_SHARED_MDL : 0, - &prelocking_strategy)) + TABLE_LIST *for_update= 0; + if (tl->is_merged_derived() && + tl->check_single_table(&for_update, tables_for_update, tl)) + { + my_error(ER_VIEW_MULTIUPDATE, MYF(0), tl->view_db.str, tl->view_name.str); + DBUG_RETURN(1); + } + } + + DBUG_RETURN(0); +} + +/* + make update specific preparation and checks after opening tables + + SYNOPSIS + mysql_multi_update_prepare() + thd thread handler + + RETURN + FALSE OK + TRUE Error +*/ + +int mysql_multi_update_prepare(THD *thd) +{ + LEX *lex= thd->lex; + TABLE_LIST *table_list= lex->query_tables; + TABLE_LIST *tl; + Multiupdate_prelocking_strategy prelocking_strategy; + uint table_count= lex->table_count; + DBUG_ENTER("mysql_multi_update_prepare"); + + /* + Open tables and create derived ones, but do not lock and fill them yet. + + During prepare phase acquire only S metadata locks instead of SW locks to + keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE + and global read lock. + */ + if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI) + { + if (open_tables(thd, &table_list, &table_count, + thd->stmt_arena->is_stmt_prepare() ? MYSQL_OPEN_FORCE_SHARED_MDL : 0, + &prelocking_strategy)) + DBUG_RETURN(TRUE); + } + else + { + /* following need for prepared statements, to run next time multi-update */ + thd->lex->sql_command= SQLCOM_UPDATE_MULTI; + prelocking_strategy.reset(thd); + if (prelocking_strategy.handle_end(thd)) DBUG_RETURN(TRUE); } @@ -1358,24 +1383,9 @@ int mysql_multi_update_prepare(THD *thd) } } - /* check single table update for view compound from several tables */ - for (tl= table_list; tl; tl= tl->next_local) - { - if (tl->is_merged_derived()) - { - TABLE_LIST *for_update= 0; - if (tl->check_single_table(&for_update, tables_for_update, tl)) - { - my_error(ER_VIEW_MULTIUPDATE, MYF(0), - tl->view_db.str, tl->view_name.str); - DBUG_RETURN(-1); - } - } - } - /* now lock and fill tables */ if (!thd->stmt_arena->is_stmt_prepare() && - lock_tables(thd, table_list, table_count + addon_table_count, 0)) + lock_tables(thd, table_list, table_count, 0)) { DBUG_RETURN(TRUE); } @@ -1387,7 +1397,7 @@ int mysql_multi_update_prepare(THD *thd) */ lex->select_lex.exclude_from_table_unique_test= TRUE; /* We only need SELECT privilege for columns in the values list */ - ti.rewind(); + List_iterator ti(lex->select_lex.leaf_tables); while ((tl= ti++)) { TABLE *table= tl->table; From 6db2ebbb2a63994ef2b43d42a11dbacb8b55c207 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sun, 9 Jun 2019 10:39:52 -0700 Subject: [PATCH 6/8] MDEV-19580 Unrelated JOINs corrupt usage of 'WHERE function() IN (subquery)' Handling of top level conjuncts in WHERE whose used_tables() contained RAND_TABLE_BIT in the function make_join_select() was incorrect. As a result if such a conjunct referred to fields non of which belonged to the last joined table it was pushed twice. (This could be seen for a test case from subselect.test whose output was changed after this patch had been applied. In 10.1 when running EXPLAIN FORMAT=JSON for the query from this test case we clearly see that one of the conjuncts is pushed twice.) This fact by itself was not good. Besides, if such a conjunct was pushed to a table that was the result of materialization of a semi-join the query could return a wrong result set. In particular we could watch it for queries with semi-join subqueries whose left parts used stored functions without "deterministic' specifier. --- mysql-test/r/subselect.result | 2 +- mysql-test/r/subselect_mat.result | 89 +++++++++++++++++++++++ mysql-test/r/subselect_no_mat.result | 2 +- mysql-test/r/subselect_no_opts.result | 2 +- mysql-test/r/subselect_no_scache.result | 2 +- mysql-test/r/subselect_no_semijoin.result | 2 +- mysql-test/r/subselect_sj_mat.result | 89 +++++++++++++++++++++++ mysql-test/t/subselect_sj_mat.test | 75 +++++++++++++++++++ sql/sql_select.cc | 87 ++++++++++++++++++---- 9 files changed, 331 insertions(+), 19 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index cdedc02f825..2999f4cfc8f 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -6939,7 +6939,7 @@ WHERE SLEEP(0.1) OR c < 'p' OR b = ( SELECT MIN(b) FROM t2 ); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 system NULL NULL NULL NULL 1 1 PRIMARY t2 ALL b NULL NULL NULL 2 Using where -1 PRIMARY t3 ref d d 5 test.t2.b 2 Using where; Using index +1 PRIMARY t3 ref d d 5 test.t2.b 2 Using index 3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away set @tmp_mdev410=@@global.userstat; set global userstat=on; diff --git a/mysql-test/r/subselect_mat.result b/mysql-test/r/subselect_mat.result index efc348a26ce..0e10800331a 100644 --- a/mysql-test/r/subselect_mat.result +++ b/mysql-test/r/subselect_mat.result @@ -2453,6 +2453,95 @@ SELECT 1 FROM t1 where t1.id IN (SELECT v1.i1 from v1); 1 drop table t1,t2; drop view v1; +# +# MDEV-19580: function invocation in the left part of IN subquery +# +create table t1 (id int, a varchar(50), b int); +insert into t1 values +(1,'mrs',2), (2,'joe',2), (3,'paul',1), (4,'art',1); +create table t2 (id int, a varchar(50), x int); +insert into t2 values +(1,'grand',1),(2,'average',1),(3,'serf',0); +create table t3 (d1 date, d2 date, t1_id int, t2_id int ); +insert into t3 values +('1972-01-01','1988-12-31',3,1), ('1972-01-01','1988-12-31',4,1), +('1972-01-01','1988-12-31',1,2), ('1972-01-01','1988-12-31',2,3); +create table t4 ( id int, a varchar(50) ); +insert into t4 values +(1,'songwriter'),(2,'song character'); +create function f1(who int, dt date) returns int +deterministic +begin +declare result int; +select t2_id into result from t3 where dt>=d1 and dt<=d2 and t1_id=who; +return result; +end$$ +create function f2(who int, dt date) returns int +begin +declare result int; +select t2_id into result from t3 where dt>=d1 and dt<=d2 and t1_id=who; +return result; +end$$ +# Deterministic function in left part of IN subquery: semi-join is OK +select * from t1 +left join t4 on t1.b = t4.id +where f1(t1.id, '1980-01-01') in (select id from t2 where x=1); +id a b id a +3 paul 1 1 songwriter +4 art 1 1 songwriter +1 mrs 2 2 song character +explain extended select * from t1 +left join t4 on t1.b = t4.id +where f1(t1.id, '1980-01-01') in (select id from t2 where x=1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 Using where +1 PRIMARY t4 ALL NULL NULL NULL NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t4`.`id` AS `id`,`test`.`t4`.`a` AS `a` from `test`.`t1` semi join (`test`.`t2`) left join `test`.`t4` on((`test`.`t4`.`id` = `test`.`t1`.`b`)) where ((`test`.`t2`.`x` = 1) and (`f1`(`test`.`t1`.`id`,'1980-01-01') = `test`.`t2`.`id`)) +# Non-deterministic function in left part of IN subq: semi-join is OK +select * from t1 +left join t4 on t1.b = t4.id +where f2(t1.id, '1980-01-01') in (select id from t2 where x=1); +id a b id a +3 paul 1 1 songwriter +4 art 1 1 songwriter +1 mrs 2 2 song character +explain extended select * from t1 +left join t4 on t1.b = t4.id +where f2(t1.id, '1980-01-01') in (select id from t2 where x=1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 Using where +1 PRIMARY t4 ALL NULL NULL NULL NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t4`.`id` AS `id`,`test`.`t4`.`a` AS `a` from `test`.`t1` semi join (`test`.`t2`) left join `test`.`t4` on((`test`.`t4`.`id` = `test`.`t1`.`b`)) where ((`test`.`t2`.`x` = 1) and (`f2`(`test`.`t1`.`id`,'1980-01-01') = `test`.`t2`.`id`)) +select t1.*, t4.*, +(select max(t4.id) from t4 where t4.id=t1.b and sleep(0) = 0) as s +from t1 left join t4 on t1.b = t4.id +where f2(t1.id, '1980-01-01') in (select id from t2 where x=1); +id a b id a s +3 paul 1 1 songwriter 1 +4 art 1 1 songwriter 1 +1 mrs 2 2 song character 2 +explain extended select t1.*, t4.*, +(select max(t4.id) from t4 where t4.id=t1.b and sleep(0) = 0) as s +from t1 left join t4 on t1.b = t4.id +where f2(t1.id, '1980-01-01') in (select id from t2 where x=1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 Using where +1 PRIMARY t4 ALL NULL NULL NULL NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +3 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00 Using where +2 DEPENDENT SUBQUERY t4 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1276 Field or reference 'test.t1.b' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t4`.`id` AS `id`,`test`.`t4`.`a` AS `a`,(select max(`test`.`t4`.`id`) from `test`.`t4` where ((`test`.`t4`.`id` = `test`.`t1`.`b`) and (sleep(0) = 0))) AS `s` from `test`.`t1` semi join (`test`.`t2`) left join `test`.`t4` on((`test`.`t4`.`id` = `test`.`t1`.`b`)) where ((`test`.`t2`.`x` = 1) and (`f2`(`test`.`t1`.`id`,'1980-01-01') = `test`.`t2`.`id`)) +drop function f1; +drop function f2; +drop table t1,t2,t3,t4; # End of 5.5 tests set @subselect_mat_test_optimizer_switch_value=null; set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result index a7291297e7c..5c21e746b60 100644 --- a/mysql-test/r/subselect_no_mat.result +++ b/mysql-test/r/subselect_no_mat.result @@ -6937,7 +6937,7 @@ WHERE SLEEP(0.1) OR c < 'p' OR b = ( SELECT MIN(b) FROM t2 ); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 system NULL NULL NULL NULL 1 1 PRIMARY t2 ALL b NULL NULL NULL 2 Using where -1 PRIMARY t3 ref d d 5 test.t2.b 2 Using where; Using index +1 PRIMARY t3 ref d d 5 test.t2.b 2 Using index 3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away set @tmp_mdev410=@@global.userstat; set global userstat=on; diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result index c41fa1be47b..8ab1f1bd3f9 100644 --- a/mysql-test/r/subselect_no_opts.result +++ b/mysql-test/r/subselect_no_opts.result @@ -6934,7 +6934,7 @@ WHERE SLEEP(0.1) OR c < 'p' OR b = ( SELECT MIN(b) FROM t2 ); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 system NULL NULL NULL NULL 1 1 PRIMARY t2 ALL b NULL NULL NULL 2 Using where -1 PRIMARY t3 ref d d 5 test.t2.b 2 Using where; Using index +1 PRIMARY t3 ref d d 5 test.t2.b 2 Using index 3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away set @tmp_mdev410=@@global.userstat; set global userstat=on; diff --git a/mysql-test/r/subselect_no_scache.result b/mysql-test/r/subselect_no_scache.result index 1c181357050..f0afa7ef19c 100644 --- a/mysql-test/r/subselect_no_scache.result +++ b/mysql-test/r/subselect_no_scache.result @@ -6945,7 +6945,7 @@ WHERE SLEEP(0.1) OR c < 'p' OR b = ( SELECT MIN(b) FROM t2 ); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 system NULL NULL NULL NULL 1 1 PRIMARY t2 ALL b NULL NULL NULL 2 Using where -1 PRIMARY t3 ref d d 5 test.t2.b 2 Using where; Using index +1 PRIMARY t3 ref d d 5 test.t2.b 2 Using index 3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away set @tmp_mdev410=@@global.userstat; set global userstat=on; diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result index 89c671252ff..0d5fbc3498a 100644 --- a/mysql-test/r/subselect_no_semijoin.result +++ b/mysql-test/r/subselect_no_semijoin.result @@ -6934,7 +6934,7 @@ WHERE SLEEP(0.1) OR c < 'p' OR b = ( SELECT MIN(b) FROM t2 ); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 system NULL NULL NULL NULL 1 1 PRIMARY t2 ALL b NULL NULL NULL 2 Using where -1 PRIMARY t3 ref d d 5 test.t2.b 2 Using where; Using index +1 PRIMARY t3 ref d d 5 test.t2.b 2 Using index 3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away set @tmp_mdev410=@@global.userstat; set global userstat=on; diff --git a/mysql-test/r/subselect_sj_mat.result b/mysql-test/r/subselect_sj_mat.result index fd9435e8a39..cb4b63047e5 100644 --- a/mysql-test/r/subselect_sj_mat.result +++ b/mysql-test/r/subselect_sj_mat.result @@ -2493,4 +2493,93 @@ SELECT 1 FROM t1 where t1.id IN (SELECT v1.i1 from v1); 1 drop table t1,t2; drop view v1; +# +# MDEV-19580: function invocation in the left part of IN subquery +# +create table t1 (id int, a varchar(50), b int); +insert into t1 values +(1,'mrs',2), (2,'joe',2), (3,'paul',1), (4,'art',1); +create table t2 (id int, a varchar(50), x int); +insert into t2 values +(1,'grand',1),(2,'average',1),(3,'serf',0); +create table t3 (d1 date, d2 date, t1_id int, t2_id int ); +insert into t3 values +('1972-01-01','1988-12-31',3,1), ('1972-01-01','1988-12-31',4,1), +('1972-01-01','1988-12-31',1,2), ('1972-01-01','1988-12-31',2,3); +create table t4 ( id int, a varchar(50) ); +insert into t4 values +(1,'songwriter'),(2,'song character'); +create function f1(who int, dt date) returns int +deterministic +begin +declare result int; +select t2_id into result from t3 where dt>=d1 and dt<=d2 and t1_id=who; +return result; +end$$ +create function f2(who int, dt date) returns int +begin +declare result int; +select t2_id into result from t3 where dt>=d1 and dt<=d2 and t1_id=who; +return result; +end$$ +# Deterministic function in left part of IN subquery: semi-join is OK +select * from t1 +left join t4 on t1.b = t4.id +where f1(t1.id, '1980-01-01') in (select id from t2 where x=1); +id a b id a +3 paul 1 1 songwriter +4 art 1 1 songwriter +1 mrs 2 2 song character +explain extended select * from t1 +left join t4 on t1.b = t4.id +where f1(t1.id, '1980-01-01') in (select id from t2 where x=1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 Using where +1 PRIMARY t4 ALL NULL NULL NULL NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t4`.`id` AS `id`,`test`.`t4`.`a` AS `a` from `test`.`t1` semi join (`test`.`t2`) left join `test`.`t4` on((`test`.`t4`.`id` = `test`.`t1`.`b`)) where ((`test`.`t2`.`x` = 1) and (`f1`(`test`.`t1`.`id`,'1980-01-01') = `test`.`t2`.`id`)) +# Non-deterministic function in left part of IN subq: semi-join is OK +select * from t1 +left join t4 on t1.b = t4.id +where f2(t1.id, '1980-01-01') in (select id from t2 where x=1); +id a b id a +3 paul 1 1 songwriter +4 art 1 1 songwriter +1 mrs 2 2 song character +explain extended select * from t1 +left join t4 on t1.b = t4.id +where f2(t1.id, '1980-01-01') in (select id from t2 where x=1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 Using where +1 PRIMARY t4 ALL NULL NULL NULL NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t4`.`id` AS `id`,`test`.`t4`.`a` AS `a` from `test`.`t1` semi join (`test`.`t2`) left join `test`.`t4` on((`test`.`t4`.`id` = `test`.`t1`.`b`)) where ((`test`.`t2`.`x` = 1) and (`f2`(`test`.`t1`.`id`,'1980-01-01') = `test`.`t2`.`id`)) +select t1.*, t4.*, +(select max(t4.id) from t4 where t4.id=t1.b and sleep(0) = 0) as s +from t1 left join t4 on t1.b = t4.id +where f2(t1.id, '1980-01-01') in (select id from t2 where x=1); +id a b id a s +3 paul 1 1 songwriter 1 +4 art 1 1 songwriter 1 +1 mrs 2 2 song character 2 +explain extended select t1.*, t4.*, +(select max(t4.id) from t4 where t4.id=t1.b and sleep(0) = 0) as s +from t1 left join t4 on t1.b = t4.id +where f2(t1.id, '1980-01-01') in (select id from t2 where x=1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 Using where +1 PRIMARY t4 ALL NULL NULL NULL NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +3 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00 Using where +2 DEPENDENT SUBQUERY t4 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1276 Field or reference 'test.t1.b' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t4`.`id` AS `id`,`test`.`t4`.`a` AS `a`,(select max(`test`.`t4`.`id`) from `test`.`t4` where ((`test`.`t4`.`id` = `test`.`t1`.`b`) and (sleep(0) = 0))) AS `s` from `test`.`t1` semi join (`test`.`t2`) left join `test`.`t4` on((`test`.`t4`.`id` = `test`.`t1`.`b`)) where ((`test`.`t2`.`x` = 1) and (`f2`(`test`.`t1`.`id`,'1980-01-01') = `test`.`t2`.`id`)) +drop function f1; +drop function f2; +drop table t1,t2,t3,t4; # End of 5.5 tests diff --git a/mysql-test/t/subselect_sj_mat.test b/mysql-test/t/subselect_sj_mat.test index c82c1e7acec..9c3788a1cd9 100644 --- a/mysql-test/t/subselect_sj_mat.test +++ b/mysql-test/t/subselect_sj_mat.test @@ -2230,4 +2230,79 @@ explain SELECT 1 FROM t1 where t1.id IN (SELECT v1.i1 from v1); SELECT 1 FROM t1 where t1.id IN (SELECT v1.i1 from v1); drop table t1,t2; drop view v1; + + +--echo # +--echo # MDEV-19580: function invocation in the left part of IN subquery +--echo # + +create table t1 (id int, a varchar(50), b int); +insert into t1 values +(1,'mrs',2), (2,'joe',2), (3,'paul',1), (4,'art',1); + +create table t2 (id int, a varchar(50), x int); +insert into t2 values +(1,'grand',1),(2,'average',1),(3,'serf',0); + +create table t3 (d1 date, d2 date, t1_id int, t2_id int ); +insert into t3 values +('1972-01-01','1988-12-31',3,1), ('1972-01-01','1988-12-31',4,1), +('1972-01-01','1988-12-31',1,2), ('1972-01-01','1988-12-31',2,3); + +create table t4 ( id int, a varchar(50) ); +insert into t4 values +(1,'songwriter'),(2,'song character'); + +delimiter $$; + +create function f1(who int, dt date) returns int +deterministic +begin + declare result int; + select t2_id into result from t3 where dt>=d1 and dt<=d2 and t1_id=who; + return result; +end$$ + +create function f2(who int, dt date) returns int +begin + declare result int; + select t2_id into result from t3 where dt>=d1 and dt<=d2 and t1_id=who; + return result; +end$$ + +delimiter ;$$ + +--echo # Deterministic function in left part of IN subquery: semi-join is OK + +let $q1= +select * from t1 + left join t4 on t1.b = t4.id + where f1(t1.id, '1980-01-01') in (select id from t2 where x=1); + +eval $q1; +eval explain extended $q1; + +--echo # Non-deterministic function in left part of IN subq: semi-join is OK + +let $q2= +select * from t1 + left join t4 on t1.b = t4.id + where f2(t1.id, '1980-01-01') in (select id from t2 where x=1); + +eval $q2; +eval explain extended $q2; + +let $q3= +select t1.*, t4.*, + (select max(t4.id) from t4 where t4.id=t1.b and sleep(0) = 0) as s + from t1 left join t4 on t1.b = t4.id + where f2(t1.id, '1980-01-01') in (select id from t2 where x=1); + +eval $q3; +eval explain extended $q3; + +drop function f1; +drop function f2; +drop table t1,t2,t3,t4; + --echo # End of 5.5 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 37f8292b563..d7ff92a5100 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -204,7 +204,8 @@ static COND *make_cond_for_table_from_pred(THD *thd, Item *root_cond, table_map used_table, int join_tab_idx_arg, bool exclude_expensive_cond, - bool retain_ref_cond); + bool retain_ref_cond, + bool is_top_and_level); static Item* part_of_refkey(TABLE *form,Field *field); uint find_shortest_key(TABLE *table, const key_map *usable_keys); @@ -8922,12 +8923,6 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) RAND_TABLE_BIT; } - /* - Following force including random expression in last table condition. - It solve problem with select like SELECT * FROM t1 WHERE rand() > 0.5 - */ - if (tab == join->join_tab + last_top_base_tab_idx) - current_map|= RAND_TABLE_BIT; used_tables|=current_map; if (tab->type == JT_REF && tab->quick && @@ -8968,6 +8963,20 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) { tmp= make_cond_for_table(thd, cond, used_tables, current_map, i, FALSE, FALSE); + if (tab == join->join_tab + last_top_base_tab_idx) + { + /* + This pushes conjunctive conditions of WHERE condition such that: + - their used_tables() contain RAND_TABLE_BIT + - the conditions does not refer to any fields + (such like rand() > 0.5) + */ + table_map rand_table_bit= (table_map) RAND_TABLE_BIT; + COND *rand_cond= make_cond_for_table(thd, cond, used_tables, + rand_table_bit, -1, + FALSE, FALSE); + add_cond_and_fix(thd, &tmp, rand_cond); + } } /* Add conditions added by add_not_null_conds(). */ if (tab->select_cond) @@ -9283,8 +9292,24 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) psergey: have put the -1 below. It's bad, will need to fix it. */ COND *tmp_cond= make_cond_for_table(thd, on_expr, used_tables2, - current_map, /*(tab - first_tab)*/ -1, - FALSE, FALSE); + current_map, + /*(tab - first_tab)*/ -1, + FALSE, FALSE); + if (tab == last_tab) + { + /* + This pushes conjunctive conditions of ON expression of an outer + join such that: + - their used_tables() contain RAND_TABLE_BIT + - the conditions does not refer to any fields + (such like rand() > 0.5) + */ + table_map rand_table_bit= (table_map) RAND_TABLE_BIT; + COND *rand_cond= make_cond_for_table(thd, on_expr, used_tables2, + rand_table_bit, -1, + FALSE, FALSE); + add_cond_and_fix(thd, &tmp_cond, rand_cond); + } bool is_sjm_lookup_tab= FALSE; if (tab->bush_children) { @@ -18824,7 +18849,7 @@ make_cond_for_table(THD *thd, Item *cond, table_map tables, return make_cond_for_table_from_pred(thd, cond, cond, tables, used_table, join_tab_idx_arg, exclude_expensive_cond, - retain_ref_cond); + retain_ref_cond, true); } @@ -18834,9 +18859,12 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, int join_tab_idx_arg, bool exclude_expensive_cond __attribute__ ((unused)), - bool retain_ref_cond) + bool retain_ref_cond, + bool is_top_and_level) { + table_map rand_table_bit= (table_map) RAND_TABLE_BIT; + if (used_table && !(cond->used_tables() & used_table)) return (COND*) 0; // Already checked @@ -18852,11 +18880,28 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, Item *item; while ((item=li++)) { + /* + Special handling of top level conjuncts with RAND_TABLE_BIT: + if such a conjunct contains a reference to a field that is not + an outer field then it is pushed to the corresponding table by + the same rule as all other conjuncts. Otherwise, if the conjunct + is used in WHERE is is pushed to the last joined table, if is it + is used in ON condition of an outer join it is pushed into the + last inner table of the outer join. Such conjuncts are pushed in + a call of make_cond_for_table_from_pred() with the + parameter 'used_table' equal to PSEUDO_TABLE_BITS. + */ + if (is_top_and_level && used_table == rand_table_bit && + (item->used_tables() & ~OUTER_REF_TABLE_BIT) != rand_table_bit) + { + /* The conjunct with RAND_TABLE_BIT has been allready pushed */ + continue; + } Item *fix=make_cond_for_table_from_pred(thd, root_cond, item, tables, used_table, - join_tab_idx_arg, + join_tab_idx_arg, exclude_expensive_cond, - retain_ref_cond); + retain_ref_cond, false); if (fix) new_cond->argument_list()->push_back(fix); } @@ -18880,6 +18925,13 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, } else { // Or list + if (is_top_and_level && used_table == rand_table_bit && + (cond->used_tables() & ~OUTER_REF_TABLE_BIT) != rand_table_bit) + { + /* This top level formula with RAND_TABLE_BIT has been already pushed */ + return (COND*) 0; + } + Item_cond_or *new_cond=new Item_cond_or; if (!new_cond) return (COND*) 0; // OOM /* purecov: inspected */ @@ -18891,7 +18943,7 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, tables, 0L, join_tab_idx_arg, exclude_expensive_cond, - retain_ref_cond); + retain_ref_cond, false); if (!fix) return (COND*) 0; // Always true new_cond->argument_list()->push_back(fix); @@ -18908,6 +18960,13 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, } } + if (is_top_and_level && used_table == rand_table_bit && + (cond->used_tables() & ~OUTER_REF_TABLE_BIT) != rand_table_bit) + { + /* This top level formula with RAND_TABLE_BIT has been already pushed */ + return (COND*) 0; + } + /* Because the following test takes a while and it can be done table_count times, we mark each item that we have examined with the result From 96ee9ea02e69fb45f369815ace2187dd73398ac4 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Mon, 10 Jun 2019 22:38:55 -0700 Subject: [PATCH 7/8] MDEV-18479 Another complement This patch complements the patch that fixes bug MDEV-18479. This patch takes care of possible overflow in JOIN::get_examined_rows(). --- mysql-test/r/derived_view.result | 18 +++++++++--------- sql/sql_select.cc | 17 ++++++++++------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/mysql-test/r/derived_view.result b/mysql-test/r/derived_view.result index d74b532d5e8..f8cf9190126 100644 --- a/mysql-test/r/derived_view.result +++ b/mysql-test/r/derived_view.result @@ -2942,15 +2942,15 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE p10 ALL NULL NULL NULL NULL 550 Using where; Using join buffer (incremental, BNL join) 1 SIMPLE ALL NULL NULL NULL NULL 50328437500000 Using where; Using join buffer (incremental, BNL join) 1 SIMPLE ALL NULL NULL NULL NULL 27680640625000000 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) -1 SIMPLE ALL NULL NULL NULL NULL 9223372036854775807 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 15224352343750000640 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 15224352343750000640 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 15224352343750000640 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 15224352343750000640 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 15224352343750000640 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 15224352343750000640 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 15224352343750000640 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 18446744073709551615 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE ALL NULL NULL NULL NULL 18446744073709551615 Using where; Using join buffer (incremental, BNL join) 17 DERIVED t2 system NULL NULL NULL NULL 1 17 DERIVED p4 ALL NULL NULL NULL NULL 550 Using where 17 DERIVED p5 ALL NULL NULL NULL NULL 550 Using where; Using join buffer (flat, BNL join) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d7ff92a5100..a273aae5425 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6894,17 +6894,22 @@ double JOIN::get_examined_rows() { ha_rows examined_rows; double prev_fanout= 1; + double records; JOIN_TAB *tab= first_breadth_first_tab(this, WALK_OPTIMIZATION_TABS); JOIN_TAB *prev_tab= tab; - examined_rows= tab->get_examined_rows(); + records= tab->get_examined_rows(); while ((tab= next_breadth_first_tab(this, WALK_OPTIMIZATION_TABS, tab))) { - prev_fanout *= prev_tab->records_read; - examined_rows+= (ha_rows) (tab->get_examined_rows() * prev_fanout); + prev_fanout= COST_MULT(prev_fanout, prev_tab->records_read); + records= + COST_ADD(records, + COST_MULT((double) (tab->get_examined_rows()), prev_fanout)); prev_tab= tab; } + examined_rows= + records > (double) HA_ROWS_MAX ? HA_ROWS_MAX : (ha_rows) records; return examined_rows; } @@ -10824,7 +10829,7 @@ ha_rows JOIN_TAB::get_examined_rows() } } else - examined_rows= (ha_rows) records_read; + examined_rows= records_read; return examined_rows; } @@ -22987,9 +22992,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, else { ha_rows examined_rows= tab->get_examined_rows(); - ha_rows displ_rows= examined_rows; - set_if_smaller(displ_rows, HA_ROWS_MAX/2); - item_list.push_back(new Item_int((longlong) (ulonglong) displ_rows, + item_list.push_back(new Item_int((ulonglong) examined_rows, MY_INT64_NUM_DECIMAL_DIGITS)); /* Add "filtered" field to item_list. */ From 7a7d9904e12335ee8b1eea9671138b3c469a3829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 11 Jun 2019 12:02:26 +0300 Subject: [PATCH 8/8] MDEV-18479: Avoid COST_MULT(records, 1) --- sql/sql_select.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a273aae5425..0d94ba2f293 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3856,11 +3856,8 @@ make_join_statistics(JOIN *join, List &tables_list, table/view. */ for (i= 0; i < join->table_count ; i++) - { - records= COST_MULT(records, - join->best_positions[i].records_read ? - join->best_positions[i].records_read : 1); - } + if (double rr= join->best_positions[i].records_read) + records= COST_MULT(records, rr); ha_rows rows= records > HA_ROWS_MAX ? HA_ROWS_MAX : (ha_rows) records; set_if_smaller(rows, unit->select_limit_cnt); join->select_lex->increase_derived_records(rows);