From bed0948145a6874807bc696b7742cdec47679326 Mon Sep 17 00:00:00 2001 From: Sergey Glukhov Date: Fri, 19 Mar 2010 10:01:02 +0400 Subject: [PATCH 1/7] Bug#51494 crash with join, explain and 'sounds like' operator The crash happens because of discrepancy between values of conts_tables and join->const_table_map(make_join_statisctics). Calculation of conts_tables used condition with HA_STATS_RECORDS_IS_EXACT flag check. Calculation of join->const_table_map does not use this flag check. In case of MERGE table without union with index the table does not become const table and thus join_read_const_table() is not called for the table. join->const_table_map supposes this table is const and later in make_join_select this table is used for making&calculation const condition. As table record buffer is not populated it leads to crash. The fix is adding a check if an engine supports HA_STATS_RECORDS_IS_EXACT flag before updating join->const_table_map. --- mysql-test/r/merge.result | 12 ++++++++++++ mysql-test/t/merge.test | 15 +++++++++++++++ sql/sql_select.cc | 3 ++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 184cd92e053..dbffbba1b8b 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -2286,4 +2286,16 @@ m1 CREATE TABLE `m1` ( DROP TABLE m1; DROP TABLE `test@1`.`t@1`; DROP DATABASE `test@1`; +# +# Bug#51494c rash with join, explain and 'sounds like' operator +# +CREATE TABLE t1 (a INT) ENGINE=MYISAM; +INSERT INTO t1 VALUES(1); +CREATE TABLE t2 (b INT NOT NULL,c INT,d INT,e BLOB NOT NULL, +KEY idx0 (d, c)) ENGINE=MERGE; +EXPLAIN SELECT * FROM t1 NATURAL RIGHT JOIN +t2 WHERE b SOUNDS LIKE e AND d = 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +DROP TABLE t2, t1; End of 5.1 tests diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index d88467cc7a9..44b202fab97 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -1690,4 +1690,19 @@ DROP TABLE m1; DROP TABLE `test@1`.`t@1`; DROP DATABASE `test@1`; +--echo # +--echo # Bug#51494c rash with join, explain and 'sounds like' operator +--echo # + +CREATE TABLE t1 (a INT) ENGINE=MYISAM; +INSERT INTO t1 VALUES(1); + +CREATE TABLE t2 (b INT NOT NULL,c INT,d INT,e BLOB NOT NULL, +KEY idx0 (d, c)) ENGINE=MERGE; + +EXPLAIN SELECT * FROM t1 NATURAL RIGHT JOIN +t2 WHERE b SOUNDS LIKE e AND d = 1; + +DROP TABLE t2, t1; + --echo End of 5.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 055246ee0df..b3640f9dd22 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2943,7 +2943,8 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, s->quick=select->quick; s->needed_reg=select->needed_reg; select->quick=0; - if (records == 0 && s->table->reginfo.impossible_range) + if (records == 0 && s->table->reginfo.impossible_range && + (s->table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT)) { /* Impossible WHERE or ON expression From 879b705342713c98dd73f8596d3d44c9381b3622 Mon Sep 17 00:00:00 2001 From: Sergey Glukhov Date: Fri, 19 Mar 2010 10:21:37 +0400 Subject: [PATCH 2/7] Bug#51598 Inconsistent behaviour with a COALESCE statement inside an IN comparison Optimizer erroneously translated LEFT JOIN into INNER JOIN. It leads to cutting rows with NULL right side. It happens because Item_row uses not_null_tables() method form the base(Item) class and does not calculate 'null tables' properly. The fix is adding calculation of 'not null tables' to Item_row. --- mysql-test/r/join_outer.result | 20 ++++++++++++++++++++ mysql-test/t/join_outer.test | 18 ++++++++++++++++++ sql/item_row.cc | 4 +++- sql/item_row.h | 4 +++- 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/join_outer.result b/mysql-test/r/join_outer.result index 083be3737f7..4543446e807 100644 --- a/mysql-test/r/join_outer.result +++ b/mysql-test/r/join_outer.result @@ -1289,3 +1289,23 @@ a COUNT( t2.b ) SUM( t2.b ) MAX( t2.b ) 1 3 6 3 NULL 3 6 3 DROP TABLE t1, t2; +# +# Bug#51598 Inconsistent behaviour with a COALESCE statement inside an IN comparison +# +CREATE TABLE t1(f1 INT, f2 INT, f3 INT); +INSERT INTO t1 VALUES (1, NULL, 3); +CREATE TABLE t2(f1 INT, f2 INT); +INSERT INTO t2 VALUES (2, 1); +EXPLAIN EXTENDED SELECT * FROM t1 LEFT JOIN t2 ON t1.f2 = t2.f2 +WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 system NULL NULL NULL NULL 1 100.00 +1 SIMPLE t2 system NULL NULL NULL NULL 1 100.00 +Warnings: +Note 1003 select '1' AS `f1`,NULL AS `f2`,'3' AS `f3`,NULL AS `f1`,NULL AS `f2` from `test`.`t1` left join `test`.`t2` on(multiple equal(NULL)) where ((coalesce('1',NULL),'3') in ((1,3),(2,2))) +SELECT * FROM t1 LEFT JOIN t2 ON t1.f2 = t2.f2 +WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2)); +f1 f2 f3 f1 f2 +1 NULL 3 NULL NULL +DROP TABLE t1, t2; +End of 5.1 tests diff --git a/mysql-test/t/join_outer.test b/mysql-test/t/join_outer.test index aeaa69657c6..e3d68d71603 100644 --- a/mysql-test/t/join_outer.test +++ b/mysql-test/t/join_outer.test @@ -896,3 +896,21 @@ FROM t1 JOIN t2 USING( a ) GROUP BY t1.a WITH ROLLUP; DROP TABLE t1, t2; + +--echo # +--echo # Bug#51598 Inconsistent behaviour with a COALESCE statement inside an IN comparison +--echo # +CREATE TABLE t1(f1 INT, f2 INT, f3 INT); +INSERT INTO t1 VALUES (1, NULL, 3); +CREATE TABLE t2(f1 INT, f2 INT); +INSERT INTO t2 VALUES (2, 1); + +EXPLAIN EXTENDED SELECT * FROM t1 LEFT JOIN t2 ON t1.f2 = t2.f2 +WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2)); + +SELECT * FROM t1 LEFT JOIN t2 ON t1.f2 = t2.f2 +WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2)); + +DROP TABLE t1, t2; + +--echo End of 5.1 tests diff --git a/sql/item_row.cc b/sql/item_row.cc index 29b37eb2bc0..7535c1fa80b 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -30,7 +30,8 @@ */ Item_row::Item_row(List &arg): - Item(), used_tables_cache(0), const_item_cache(1), with_null(0) + Item(), used_tables_cache(0), not_null_tables_cache(0), + const_item_cache(1), with_null(0) { //TODO: think placing 2-3 component items in item (as it done for function) @@ -71,6 +72,7 @@ bool Item_row::fix_fields(THD *thd, Item **ref) Item *item= *arg; used_tables_cache |= item->used_tables(); const_item_cache&= item->const_item() && !with_null; + not_null_tables_cache|= item->not_null_tables(); /* Some subqueries transformations aren't done in the view_prepare_mode thus is_null() will fail. So we skip is_null() calculation for CREATE VIEW as diff --git a/sql/item_row.h b/sql/item_row.h index 67441f49603..76d1c875e7d 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -16,7 +16,7 @@ class Item_row: public Item { Item **items; - table_map used_tables_cache; + table_map used_tables_cache, not_null_tables_cache; uint arg_count; bool const_item_cache; bool with_null; @@ -26,6 +26,7 @@ public: Item(), items(item->items), used_tables_cache(item->used_tables_cache), + not_null_tables_cache(0), arg_count(item->arg_count), const_item_cache(item->const_item_cache), with_null(0) @@ -65,6 +66,7 @@ public: bool const_item() const { return const_item_cache; }; enum Item_result result_type() const { return ROW_RESULT; } void update_used_tables(); + table_map not_null_tables() const { return not_null_tables_cache; } virtual void print(String *str, enum_query_type query_type); bool walk(Item_processor processor, bool walk_subquery, uchar *arg); From d03133dccf51a989bcd748ba9bc5aedccf11c4cf Mon Sep 17 00:00:00 2001 From: Martin Hansson Date: Fri, 19 Mar 2010 09:23:44 +0100 Subject: [PATCH 3/7] Post-push fix to disable a subset of the test case for Bug#47762. This has been back-ported from 6.0 as the problems proved to afflict 5.1 as well. The fix exposed two new bugs. They were reported as follows. Bug no 52174: Sometimes wrong plan when reading a MAX value from non-NULL index Bug no 52173: Reading NULL value from non-NULL index gives wrong result in embedded server Both bugs taken together affect a much smaller class of queries than #47762, so the fix stays for now. --- mysql-test/include/min_null_cond.inc | 4 +++- mysql-test/r/group_min_max.result | 18 +++++++++++------- mysql-test/t/group_min_max.test | 12 ++++++++++++ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/mysql-test/include/min_null_cond.inc b/mysql-test/include/min_null_cond.inc index fcfaad43e57..c958fc7075a 100644 --- a/mysql-test/include/min_null_cond.inc +++ b/mysql-test/include/min_null_cond.inc @@ -18,11 +18,13 @@ EXPLAIN SELECT MIN( a ) FROM t1 WHERE a < NULL; SELECT MIN( a ) FROM t1 WHERE a < NULL; +if (!$skip_null_safe_test) +{ --replace_column 1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x EXPLAIN SELECT MIN( a ) FROM t1 WHERE a <=> NULL; SELECT MIN( a ) FROM t1 WHERE a <=> NULL; - +} --replace_column 1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x EXPLAIN SELECT MIN( a ) FROM t1 WHERE a BETWEEN NULL AND 10; diff --git a/mysql-test/r/group_min_max.result b/mysql-test/r/group_min_max.result index 604e93af4a0..01f27a498ef 100644 --- a/mysql-test/r/group_min_max.result +++ b/mysql-test/r/group_min_max.result @@ -2691,6 +2691,17 @@ DROP TABLE t1; ## Test for NOT NULLs CREATE TABLE t1 ( a INT NOT NULL PRIMARY KEY); INSERT INTO t1 VALUES (1), (2), (3); +# +# NULL-safe operator test disabled for non-NULL indexed columns. +# +# See bugs +# +# - Bug#52173: Reading NULL value from non-NULL index gives +# wrong result in embedded server +# +# - Bug#52174: Sometimes wrong plan when reading a MAX value from +# non-NULL index +# EXPLAIN SELECT MIN( a ) FROM t1 WHERE a = NULL; id select_type table type possible_keys key key_len ref rows Extra @@ -2720,13 +2731,6 @@ SELECT MIN( a ) FROM t1 WHERE a < NULL; MIN( a ) NULL EXPLAIN -SELECT MIN( a ) FROM t1 WHERE a <=> NULL; -id select_type table type possible_keys key key_len ref rows Extra -x x x x x x x x x No matching min/max row -SELECT MIN( a ) FROM t1 WHERE a <=> NULL; -MIN( a ) -NULL -EXPLAIN SELECT MIN( a ) FROM t1 WHERE a BETWEEN NULL AND 10; id select_type table type possible_keys key key_len ref rows Extra x x x x x x x x x Impossible WHERE noticed after reading const tables diff --git a/mysql-test/t/group_min_max.test b/mysql-test/t/group_min_max.test index 6aec0e3677f..b06f0c09fc5 100644 --- a/mysql-test/t/group_min_max.test +++ b/mysql-test/t/group_min_max.test @@ -1070,6 +1070,18 @@ DROP TABLE t1; --echo ## Test for NOT NULLs CREATE TABLE t1 ( a INT NOT NULL PRIMARY KEY); INSERT INTO t1 VALUES (1), (2), (3); +--echo # +--echo # NULL-safe operator test disabled for non-NULL indexed columns. +--echo # +--echo # See bugs +--echo # +--echo # - Bug#52173: Reading NULL value from non-NULL index gives +--echo # wrong result in embedded server +--echo # +--echo # - Bug#52174: Sometimes wrong plan when reading a MAX value from +--echo # non-NULL index +--echo # +--let $skip_null_safe_test= 1 --source include/min_null_cond.inc DROP TABLE t1; From 30df18909cc2c8f657c265d0e8a0b3e5152e0990 Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Fri, 19 Mar 2010 11:06:40 +0200 Subject: [PATCH 4/7] Bug #51648 DBUG_SYNC_POINT is not defined on all platforms and mtr cant pre-check that DBUG_SYNC_POINT has at least one strong limitation that it's not defined on all platforms. It has issues cooperating with @@debug. All in all its functionality is superseded by DEBUG_SYNC facility and there is no reason to maintain the old less flexible one. Fixed with adding debug_sync_set_action() function as a facility to set up a sync-action in the server sources code and re-writing existing simulations (found 3) to use it. Couple of tests have been reworked as well. The patch offers a pattern for setting sync-points in replication threads where the standard DEBUG_SYNC does not suffice to reach goals. --- .../rpl_get_master_version_and_clock.test | 53 ++++++++++--- .../r/rpl_get_master_version_and_clock.result | 31 +++----- .../suite/rpl/r/rpl_show_slave_running.result | 11 +-- .../t/rpl_get_master_version_and_clock.test | 27 ++++--- .../suite/rpl/t/rpl_show_slave_running.test | 12 +-- sql/debug_sync.cc | 38 ++++++++++ sql/debug_sync.h | 1 + sql/item_func.cc | 74 ------------------- sql/mysql_priv.h | 14 ---- sql/slave.cc | 33 ++++++++- sql/sql_repl.cc | 1 - 11 files changed, 152 insertions(+), 143 deletions(-) diff --git a/mysql-test/extra/rpl_tests/rpl_get_master_version_and_clock.test b/mysql-test/extra/rpl_tests/rpl_get_master_version_and_clock.test index 89c136d8893..c392686454d 100644 --- a/mysql-test/extra/rpl_tests/rpl_get_master_version_and_clock.test +++ b/mysql-test/extra/rpl_tests/rpl_get_master_version_and_clock.test @@ -3,26 +3,53 @@ # The common part of the "rpl_get_master_version_and_clock" test. # Restart slave under network disconnection between slave and master # following the steps: -# 1 - Got DBUG_SYNC_POINT lock -# 2 - Set DBUG_SYNC_POINT before call mysql_real_query(...) function in get_master_version_and_clock(...) function and hang here -# 3 - shutdown master server for simulating network disconnection -# 4 - Release DBUG_SYNC_POINT lock -# 5 - Check if the slave I/O thread tries to reconnect to master. +# 0 - Set DEBUG_SYNC_ACTION to wait +# before call mysql_real_query(...) function in get_master_version_and_clock(...) +# function and hang here +# 1 - activate a sync-point through the $dbug_sync_point argument of the test +# 2 - shutdown master server for simulating network disconnection +# 3 - signal to the IO thread through $debug_sync_action to unhold from the sync-point +# 4 - check if the slave I/O thread tries to reconnect to master. # -# Note: Please make sure initialize the $debug_lock when call the test script. +# Note: make sure to initialize the $debug_sync_action and $dbug_sync_point +# before calling the test script. # +# Pattern of usage: +# +# The caller test supplies the DBUG_EXECUTE_IF name +# +# let $dbug_sync_point = 'dbug_execute_if_name'; +# +# as well as the action list for DEBUG_SYNC +# +# let $debug_sync_action= 'now signal signal_name'; +# +# The $dbug_sync_point becomes the value of @@global.debug generating +# a newly started IO-slave thread's session value. +# Notice incremental operations to add and remove dbug_execute_if_name +# from the global variable allows propagation more dbug arguments +# out of mtr. +# The action list is to fire at proper time according to test logics +# (see pp 0-4 above). +# + connection slave; -if (`SELECT '$debug_lock' = ''`) +if (`SELECT $debug_sync_action = ''`) { - --die Cannot continue. Please set value for $debug_lock. + --die Cannot continue. Please set value for debug_sync_action. } # Restart slave --disable_warnings stop slave; source include/wait_for_slave_to_stop.inc; + +eval SET @@global.debug= "+d,$dbug_sync_point"; + start slave; source include/wait_for_slave_to_start.inc; +--echo slave is going to hang in get_master_version_and_clock + connection master; # Write file to make mysql-test-run.pl expect the "crash", but don't start # it until it's told to @@ -35,7 +62,9 @@ EOF shutdown_server 10; connection slave; -eval SELECT RELEASE_LOCK($debug_lock); +--echo slave is unblocked + +eval SET DEBUG_SYNC=$debug_sync_action; # Show slave last IO errno connection slave; @@ -53,6 +82,12 @@ if (`SELECT '$last_io_errno' = '2013' || # CR_SERVER_LOST --echo NETWORK ERROR } +# deactivate the sync point of get_master_version_and_clock() +# now to avoid restarting IO-thread to re-enter it. +# There will be a new IO thread forked out with its @@session.debug +# unset. +eval set @@global.debug = "-d,$dbug_sync_point"; + # Write file to make mysql-test-run.pl start up the server again --append_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect restart diff --git a/mysql-test/suite/rpl/r/rpl_get_master_version_and_clock.result b/mysql-test/suite/rpl/r/rpl_get_master_version_and_clock.result index d054cb573d3..a34c536feb8 100644 --- a/mysql-test/suite/rpl/r/rpl_get_master_version_and_clock.result +++ b/mysql-test/suite/rpl/r/rpl_get_master_version_and_clock.result @@ -7,34 +7,24 @@ start slave; call mtr.add_suppression("Slave I/O: Master command COM_REGISTER_SLAVE failed: .*"); call mtr.add_suppression("Fatal error: The slave I/O thread stops because master and slave have equal MySQL server ids; .*"); call mtr.add_suppression("Slave I/O thread .* register on master"); -SELECT IS_FREE_LOCK("debug_lock.before_get_UNIX_TIMESTAMP"); -IS_FREE_LOCK("debug_lock.before_get_UNIX_TIMESTAMP") -1 -SELECT GET_LOCK("debug_lock.before_get_UNIX_TIMESTAMP", 1000); -GET_LOCK("debug_lock.before_get_UNIX_TIMESTAMP", 1000) -1 -set global debug= 'd,debug_lock.before_get_UNIX_TIMESTAMP'; stop slave; +SET @@global.debug= "+d,'debug_lock.before_get_UNIX_TIMESTAMP'"; start slave; -SELECT RELEASE_LOCK("debug_lock.before_get_UNIX_TIMESTAMP"); -RELEASE_LOCK("debug_lock.before_get_UNIX_TIMESTAMP") -1 +slave is going to hang in get_master_version_and_clock +slave is unblocked +SET DEBUG_SYNC='now SIGNAL signal.get_unix_timestamp'; Check network error happened here NETWORK ERROR -SELECT IS_FREE_LOCK("debug_lock.before_get_SERVER_ID"); -IS_FREE_LOCK("debug_lock.before_get_SERVER_ID") -1 -SELECT GET_LOCK("debug_lock.before_get_SERVER_ID", 1000); -GET_LOCK("debug_lock.before_get_SERVER_ID", 1000) -1 -set global debug= 'd,debug_lock.before_get_SERVER_ID'; +set @@global.debug = "-d,'debug_lock.before_get_UNIX_TIMESTAMP'"; stop slave; +SET @@global.debug= "+d,'debug_lock.before_get_SERVER_ID'"; start slave; -SELECT RELEASE_LOCK("debug_lock.before_get_SERVER_ID"); -RELEASE_LOCK("debug_lock.before_get_SERVER_ID") -1 +slave is going to hang in get_master_version_and_clock +slave is unblocked +SET DEBUG_SYNC='now SIGNAL signal.get_server_id'; Check network error happened here NETWORK ERROR +set @@global.debug = "-d,'debug_lock.before_get_SERVER_ID'"; set global debug= ''; reset master; include/stop_slave.inc @@ -43,3 +33,4 @@ start slave; *** must be having the replicate-same-server-id IO thread error *** Slave_IO_Errno= 1593 Slave_IO_Error= Fatal error: The slave I/O thread stops because master and slave have equal MySQL server ids; these ids must be different for replication to work (or the --replicate-same-server-id option must be used on slave but this does not always make sense; please check the manual before using it). +SET DEBUG_SYNC= 'RESET'; diff --git a/mysql-test/suite/rpl/r/rpl_show_slave_running.result b/mysql-test/suite/rpl/r/rpl_show_slave_running.result index da26190e76d..598762d5188 100644 --- a/mysql-test/suite/rpl/r/rpl_show_slave_running.result +++ b/mysql-test/suite/rpl/r/rpl_show_slave_running.result @@ -4,11 +4,9 @@ reset master; reset slave; drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; start slave; +SET DEBUG_SYNC= 'RESET'; include/stop_slave.inc -SELECT GET_LOCK("debug_lock.before_get_running_status_yes", 1000); -GET_LOCK("debug_lock.before_get_running_status_yes", 1000) -1 -set global debug= 'd,debug_lock.before_get_running_status_yes'; +set global debug= 'd,dbug.before_get_running_status_yes'; Slave_running, Slave_IO_Running, Slave_SQL_Running, must be OFF, NO, NO in three following queries SHOW STATUS LIKE 'Slave_running'; Variable_name Value @@ -22,9 +20,7 @@ Variable_name Value Slave_running OFF Slave_IO_Running= No Slave_SQL_Running= No -SELECT RELEASE_LOCK("debug_lock.before_get_running_status_yes"); -RELEASE_LOCK("debug_lock.before_get_running_status_yes") -1 +SET DEBUG_SYNC='now SIGNAL signal.io_thread_let_running'; Slave_running, Slave_IO_Running, Slave_SQL_Running must be OFF YES NO in three following queries SHOW STATUS LIKE 'Slave_running'; Variable_name Value @@ -39,4 +35,5 @@ Slave_running ON Slave_IO_Running= Yes Slave_SQL_Running= Yes set global debug= ''; +SET DEBUG_SYNC= 'RESET'; End of tests diff --git a/mysql-test/suite/rpl/t/rpl_get_master_version_and_clock.test b/mysql-test/suite/rpl/t/rpl_get_master_version_and_clock.test index 24309e8a14a..4a9276d9880 100644 --- a/mysql-test/suite/rpl/t/rpl_get_master_version_and_clock.test +++ b/mysql-test/suite/rpl/t/rpl_get_master_version_and_clock.test @@ -16,8 +16,16 @@ source include/master-slave.inc; source include/have_debug.inc; +source include/have_debug_sync.inc; + +# +# The test is not supposed to have any binglog affairs. +# Hence it's enough it to run only with one binlog format +# +source include/have_binlog_format_mixed.inc; connection slave; + call mtr.add_suppression("Slave I/O: Master command COM_REGISTER_SLAVE failed: .*"); call mtr.add_suppression("Fatal error: The slave I/O thread stops because master and slave have equal MySQL server ids; .*"); call mtr.add_suppression("Slave I/O thread .* register on master"); @@ -25,20 +33,16 @@ call mtr.add_suppression("Slave I/O thread .* register on master"); #Test case 1: Try to get the value of the UNIX_TIMESTAMP from master under network disconnection let $debug_saved= `select @@global.debug`; -let $debug_lock= "debug_lock.before_get_UNIX_TIMESTAMP"; -eval SELECT IS_FREE_LOCK($debug_lock); -eval SELECT GET_LOCK($debug_lock, 1000); - -set global debug= 'd,debug_lock.before_get_UNIX_TIMESTAMP'; +# set up two parameters to pass into extra/rpl_tests/rpl_get_master_version_and_clock +let $dbug_sync_point= 'debug_lock.before_get_UNIX_TIMESTAMP'; +let $debug_sync_action= 'now SIGNAL signal.get_unix_timestamp'; source extra/rpl_tests/rpl_get_master_version_and_clock.test; #Test case 2: Try to get the value of the SERVER_ID from master under network disconnection connection slave; -let $debug_lock= "debug_lock.before_get_SERVER_ID"; -eval SELECT IS_FREE_LOCK($debug_lock); -eval SELECT GET_LOCK($debug_lock, 1000); -set global debug= 'd,debug_lock.before_get_SERVER_ID'; +let $dbug_sync_point= 'debug_lock.before_get_SERVER_ID'; +let $debug_sync_action= 'now SIGNAL signal.get_server_id'; source extra/rpl_tests/rpl_get_master_version_and_clock.test; eval set global debug= '$debug_saved'; @@ -62,4 +66,9 @@ let $last_io_error= query_get_value("show slave status", Last_IO_Error, 1); echo Slave_IO_Errno= $last_io_errno; echo Slave_IO_Error= $last_io_error; +# cleanup + +# is not really necessary but avoids mtr post-run env check warnings +SET DEBUG_SYNC= 'RESET'; + # End of tests diff --git a/mysql-test/suite/rpl/t/rpl_show_slave_running.test b/mysql-test/suite/rpl/t/rpl_show_slave_running.test index 62cc311e6b2..bfdd1c279fc 100644 --- a/mysql-test/suite/rpl/t/rpl_show_slave_running.test +++ b/mysql-test/suite/rpl/t/rpl_show_slave_running.test @@ -5,14 +5,14 @@ # source include/master-slave.inc; source include/have_debug.inc; +source include/have_debug_sync.inc; connection slave; - +SET DEBUG_SYNC= 'RESET'; source include/stop_slave.inc; let $debug_saved= `select @@global.debug`; -let $debug_lock= "debug_lock.before_get_running_status_yes"; -eval SELECT GET_LOCK($debug_lock, 1000); -set global debug= 'd,debug_lock.before_get_running_status_yes'; +set global debug= 'd,dbug.before_get_running_status_yes'; # to block due-started IO + # Test 1. Slave is stopped @@ -40,7 +40,7 @@ echo Slave_SQL_Running= $status; # Test 3. The slave IO thread is started and got connected to master # and SQL thread is still not started -eval SELECT RELEASE_LOCK($debug_lock); +SET DEBUG_SYNC='now SIGNAL signal.io_thread_let_running'; # unblock IO thread now let $slave_param= Slave_IO_Running; let $slave_param_value= YES; source include/wait_for_slave_param.inc; @@ -72,5 +72,5 @@ echo Slave_SQL_Running= $status; connection slave; eval set global debug= '$debug_saved'; - +SET DEBUG_SYNC= 'RESET'; --echo End of tests diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc index 2580d526b52..23a649a89fa 100644 --- a/sql/debug_sync.cc +++ b/sql/debug_sync.cc @@ -1903,4 +1903,42 @@ void debug_sync(THD *thd, const char *sync_point_name, size_t name_len) DBUG_VOID_RETURN; } +/** + Define debug sync action. + + @param[in] thd thread handle + @param[in] action_str action string + + @return status + @retval FALSE ok + @retval TRUE error + + @description + The function is similar to @c debug_sync_eval_action but is + to be called immediately from the server code rather than + to be triggered by setting a value to DEBUG_SYNC system variable. + + @note + The input string is copied prior to be fed to + @c debug_sync_eval_action to let the latter modify it. + + Caution. + The function allocates in THD::mem_root and therefore + is not recommended to be deployed inside big loops. +*/ + +bool debug_sync_set_action(THD *thd, const char *action_str, size_t len) +{ + bool rc; + char *value; + DBUG_ENTER("debug_sync_set_action"); + DBUG_ASSERT(thd); + DBUG_ASSERT(action_str); + + value= strmake_root(thd->mem_root, action_str, len); + rc= debug_sync_eval_action(thd, value); + DBUG_RETURN(rc); +} + + #endif /* defined(ENABLED_DEBUG_SYNC) */ diff --git a/sql/debug_sync.h b/sql/debug_sync.h index f4cd0b364cf..9ac7da39d4d 100644 --- a/sql/debug_sync.h +++ b/sql/debug_sync.h @@ -50,6 +50,7 @@ extern void debug_sync_end(void); extern void debug_sync_init_thread(THD *thd); extern void debug_sync_end_thread(THD *thd); extern void debug_sync(THD *thd, const char *sync_point_name, size_t name_len); +extern bool debug_sync_set_action(THD *thd, const char *action_str, size_t len); #else /* defined(ENABLED_DEBUG_SYNC) */ diff --git a/sql/item_func.cc b/sql/item_func.cc index 845654c1881..1e31755179b 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3362,80 +3362,6 @@ longlong Item_master_pos_wait::val_int() return event_count; } -#ifdef EXTRA_DEBUG -void debug_sync_point(const char* lock_name, uint lock_timeout) -{ - THD* thd=current_thd; - User_level_lock* ull; - struct timespec abstime; - size_t lock_name_len; - lock_name_len= strlen(lock_name); - pthread_mutex_lock(&LOCK_user_locks); - - if (thd->ull) - { - item_user_lock_release(thd->ull); - thd->ull=0; - } - - /* - If the lock has not been aquired by some client, we do not want to - create an entry for it, since we immediately release the lock. In - this case, we will not be waiting, but rather, just waste CPU and - memory on the whole deal - */ - if (!(ull= ((User_level_lock*) hash_search(&hash_user_locks, - (uchar*) lock_name, - lock_name_len)))) - { - pthread_mutex_unlock(&LOCK_user_locks); - return; - } - ull->count++; - - /* - Structure is now initialized. Try to get the lock. - Set up control struct to allow others to abort locks - */ - thd_proc_info(thd, "User lock"); - thd->mysys_var->current_mutex= &LOCK_user_locks; - thd->mysys_var->current_cond= &ull->cond; - - set_timespec(abstime,lock_timeout); - while (ull->locked && !thd->killed) - { - int error= pthread_cond_timedwait(&ull->cond, &LOCK_user_locks, &abstime); - if (error == ETIMEDOUT || error == ETIME) - break; - } - - if (ull->locked) - { - if (!--ull->count) - delete ull; // Should never happen - } - else - { - ull->locked=1; - ull->set_thread(thd); - thd->ull=ull; - } - pthread_mutex_unlock(&LOCK_user_locks); - pthread_mutex_lock(&thd->mysys_var->mutex); - thd_proc_info(thd, 0); - thd->mysys_var->current_mutex= 0; - thd->mysys_var->current_cond= 0; - pthread_mutex_unlock(&thd->mysys_var->mutex); - pthread_mutex_lock(&LOCK_user_locks); - if (thd->ull) - { - item_user_lock_release(thd->ull); - thd->ull=0; - } - pthread_mutex_unlock(&LOCK_user_locks); -} - -#endif /** Get a user level lock. If the thread has an old lock this is first released. diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 90b02f5337a..098ea514cb6 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -580,20 +580,6 @@ protected: /* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */ #define UNDEF_POS (-1) -#ifdef EXTRA_DEBUG -/** - Sync points allow us to force the server to reach a certain line of code - and block there until the client tells the server it is ok to go on. - The client tells the server to block with SELECT GET_LOCK() - and unblocks it with SELECT RELEASE_LOCK(). Used for debugging difficult - concurrency problems -*/ -#define DBUG_SYNC_POINT(lock_name,lock_timeout) \ - debug_sync_point(lock_name,lock_timeout) -void debug_sync_point(const char* lock_name, uint lock_timeout); -#else -#define DBUG_SYNC_POINT(lock_name,lock_timeout) -#endif /* EXTRA_DEBUG */ /* BINLOG_DUMP options */ diff --git a/sql/slave.cc b/sql/slave.cc index a89ac2e682b..e8405ffcd37 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -44,6 +44,7 @@ #ifdef HAVE_REPLICATION #include "rpl_tblmap.h" +#include "debug_sync.h" #define FLAGSTR(V,F) ((V)&(F)?#F" ":"") @@ -981,7 +982,16 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi) unavailable (very old master not supporting UNIX_TIMESTAMP()?). */ - DBUG_SYNC_POINT("debug_lock.before_get_UNIX_TIMESTAMP", 10); + DBUG_EXECUTE_IF("dbug.before_get_UNIX_TIMESTAMP", + { + const char act[]= + "now " + "wait_for signal.get_unix_timestamp"; + DBUG_ASSERT(opt_debug_sync_timeout > 0); + DBUG_ASSERT(!debug_sync_set_action(current_thd, + STRING_WITH_LEN(act))); + };); + master_res= NULL; if (!mysql_real_query(mysql, STRING_WITH_LEN("SELECT UNIX_TIMESTAMP()")) && (master_res= mysql_store_result(mysql)) && @@ -1020,7 +1030,15 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi) Note: we could have put a @@SERVER_ID in the previous SELECT UNIX_TIMESTAMP() instead, but this would not have worked on 3.23 masters. */ - DBUG_SYNC_POINT("debug_lock.before_get_SERVER_ID", 10); + DBUG_EXECUTE_IF("dbug.before_get_SERVER_ID", + { + const char act[]= + "now " + "wait_for signal.get_server_id"; + DBUG_ASSERT(opt_debug_sync_timeout > 0); + DBUG_ASSERT(!debug_sync_set_action(current_thd, + STRING_WITH_LEN(act))); + };); master_res= NULL; master_row= NULL; if (!mysql_real_query(mysql, @@ -2557,7 +2575,16 @@ pthread_handler_t handle_slave_io(void *arg) connected: - DBUG_SYNC_POINT("debug_lock.before_get_running_status_yes", 10); + DBUG_EXECUTE_IF("dbug.before_get_running_status_yes", + { + const char act[]= + "now " + "wait_for signal.io_thread_let_running"; + DBUG_ASSERT(opt_debug_sync_timeout > 0); + DBUG_ASSERT(!debug_sync_set_action(thd, + STRING_WITH_LEN(act))); + };); + // TODO: the assignment below should be under mutex (5.0) mi->slave_running= MYSQL_SLAVE_RUN_CONNECT; thd->slave_net = &mysql->net; diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index ae995ea5ed3..44215d90634 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1711,7 +1711,6 @@ int log_loaded_block(IO_CACHE* file) if (mysql_bin_log.write(&b)) DBUG_RETURN(1); lf_info->wrote_create_file= 1; - DBUG_SYNC_POINT("debug_lock.created_file_event",10); } } DBUG_RETURN(0); From 84917914e01415cbbc2a128cabc344c127e76db1 Mon Sep 17 00:00:00 2001 From: Sergey Glukhov Date: Fri, 19 Mar 2010 13:09:22 +0400 Subject: [PATCH 5/7] Bug#51242 HAVING clause on table join produce incorrect results The problem is that when we make conditon for grouped result const part of condition is cut off. It happens because some parts of 'having' condition which refer to outer join become const after make_join_statistics. These parts may be lost during further having condition transformation in JOIN::exec. The fix is adding 'having' condition check for const tables after make_join_statistics is performed. --- mysql-test/r/having.result | 35 +++++++++++++++++++++++++++++++++++ mysql-test/t/having.test | 30 ++++++++++++++++++++++++++++++ sql/sql_select.cc | 25 +++++++++++++++++++++++++ 3 files changed, 90 insertions(+) diff --git a/mysql-test/r/having.result b/mysql-test/r/having.result index 9568ef88786..95893510987 100644 --- a/mysql-test/r/having.result +++ b/mysql-test/r/having.result @@ -450,4 +450,39 @@ HAVING amount > 0 ORDER BY t1.id1; id1 amount DROP TABLE t1; +# +# Bug#48916 Server incorrectly processing HAVING clauses with an ORDER BY clause +# +CREATE TABLE t1 (f1 INT PRIMARY KEY, f2 INT, f3 INT); +INSERT INTO t1 VALUES (2,7,9), (4,7,9), (6,2,9), (17,0,9); +SELECT table1.f1, table2.f2 +FROM t1 AS table1 +JOIN t1 AS table2 ON table1.f3 = table2.f3 +WHERE table2.f1 = 2 +GROUP BY table1.f1, table2.f2 +HAVING (table2.f2 = 8 AND table1.f1 >= 6); +f1 f2 +EXPLAIN EXTENDED +SELECT table1.f1, table2.f2 +FROM t1 AS table1 +JOIN t1 AS table2 ON table1.f3 = table2.f3 +WHERE table2.f1 = 2 +GROUP BY table1.f1, table2.f2 +HAVING (table2.f2 = 8 AND table1.f1 >= 6); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible HAVING noticed after reading const tables +Warnings: +Note 1003 select `test`.`table1`.`f1` AS `f1`,'7' AS `f2` from `test`.`t1` `table1` join `test`.`t1` `table2` where ((`test`.`table1`.`f3` = '9')) group by `test`.`table1`.`f1`,'7' having (('7' = 8) and (`test`.`table1`.`f1` >= 6)) +EXPLAIN EXTENDED +SELECT table1.f1, table2.f2 +FROM t1 AS table1 +JOIN t1 AS table2 ON table1.f3 = table2.f3 +WHERE table2.f1 = 2 +GROUP BY table1.f1, table2.f2 +HAVING (table2.f2 = 8); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible HAVING noticed after reading const tables +Warnings: +Note 1003 select `test`.`table1`.`f1` AS `f1`,'7' AS `f2` from `test`.`t1` `table1` join `test`.`t1` `table2` where ((`test`.`table1`.`f3` = '9')) group by `test`.`table1`.`f1`,'7' having ('7' = 8) +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/having.test b/mysql-test/t/having.test index 185ca4bdddb..b68ba69b975 100644 --- a/mysql-test/t/having.test +++ b/mysql-test/t/having.test @@ -467,5 +467,35 @@ ORDER BY t1.id1; DROP TABLE t1; +--echo # +--echo # Bug#48916 Server incorrectly processing HAVING clauses with an ORDER BY clause +--echo # +CREATE TABLE t1 (f1 INT PRIMARY KEY, f2 INT, f3 INT); +INSERT INTO t1 VALUES (2,7,9), (4,7,9), (6,2,9), (17,0,9); + +SELECT table1.f1, table2.f2 +FROM t1 AS table1 +JOIN t1 AS table2 ON table1.f3 = table2.f3 +WHERE table2.f1 = 2 +GROUP BY table1.f1, table2.f2 +HAVING (table2.f2 = 8 AND table1.f1 >= 6); + +EXPLAIN EXTENDED +SELECT table1.f1, table2.f2 +FROM t1 AS table1 +JOIN t1 AS table2 ON table1.f3 = table2.f3 +WHERE table2.f1 = 2 +GROUP BY table1.f1, table2.f2 +HAVING (table2.f2 = 8 AND table1.f1 >= 6); + +EXPLAIN EXTENDED +SELECT table1.f1, table2.f2 +FROM t1 AS table1 +JOIN t1 AS table2 ON table1.f3 = table2.f3 +WHERE table2.f1 = 2 +GROUP BY table1.f1, table2.f2 +HAVING (table2.f2 = 8); + +DROP TABLE t1; --echo End of 5.0 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b3640f9dd22..618332ae89b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1112,6 +1112,31 @@ JOIN::optimize() { conds=new Item_int((longlong) 0,1); // Always false } + + /* + It's necessary to check const part of HAVING cond as + there is a chance that some cond parts may become + const items after make_join_statisctics(for example + when Item is a reference to cost table field from + outer join). + This check is performed only for those conditions + which do not use aggregate functions. In such case + temporary table may not be used and const condition + elements may be lost during further having + condition transformation in JOIN::exec. + */ + if (having && !having->with_sum_func) + { + COND *const_cond= make_cond_for_table(having, const_table_map, 0); + DBUG_EXECUTE("where", print_where(const_cond, "const_having_cond", + QT_ORDINARY);); + if (const_cond && !const_cond->val_int()) + { + zero_result_cause= "Impossible HAVING noticed after reading const tables"; + DBUG_RETURN(0); + } + } + if (make_join_select(this, select, conds)) { zero_result_cause= From ccdec568c1b1fc7b1bea109cdb96194bf2a54ded Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Mon, 22 Mar 2010 07:50:19 +0200 Subject: [PATCH 6/7] bug#51648 comments to rpl_show_slave_running test are added to ease merging --- mysql-test/suite/rpl/t/rpl_show_slave_running.test | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mysql-test/suite/rpl/t/rpl_show_slave_running.test b/mysql-test/suite/rpl/t/rpl_show_slave_running.test index bfdd1c279fc..8a3e56975ad 100644 --- a/mysql-test/suite/rpl/t/rpl_show_slave_running.test +++ b/mysql-test/suite/rpl/t/rpl_show_slave_running.test @@ -29,6 +29,12 @@ echo Slave_SQL_Running= $status; start slave io_thread; +# +# Notice a difference between versions in showing p.2: +# 5.1 has two OFF,ON IO-thread state running state whereas later versions +# have three: OFF,Connecting,ON. +# Hence, 5.1 must display OFF NO NO where as 5.1+ OFF Connecting NO +# --echo Slave_running, Slave_IO_Running, Slave_SQL_Running must be OFF NO NO in three following queries SHOW STATUS LIKE 'Slave_running'; From 2758847bf2be9cd6e30a4517885a5abbf9206fff Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Mon, 22 Mar 2010 12:33:25 +0400 Subject: [PATCH 7/7] Bug #49910: Behavioural change in SELECT/WHERE on YEAR(4) data type (Original patch by Sinisa Milivojevic) The YEAR(4) value of 2000 was equal to the "bad" YEAR(4) value of 0000. The get_year_value() function has been modified to not adjust bad YEAR(4) value to 2000. --- mysql-test/r/type_year.result | 32 ++++++++++++++++++++++++++++++++ mysql-test/t/type_year.test | 16 ++++++++++++++++ sql/item_cmpfunc.cc | 19 ++++++++++++++----- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/type_year.result b/mysql-test/r/type_year.result index 56b326327c6..8948214f565 100644 --- a/mysql-test/r/type_year.result +++ b/mysql-test/r/type_year.result @@ -309,4 +309,36 @@ yyyy c4 2069 2069 DROP TABLE t2, t4; # +# Bug #49910: Behavioural change in SELECT/WHERE on YEAR(4) data type +# +CREATE TABLE t1 (y YEAR NOT NULL, s VARCHAR(4)); +INSERT INTO t1 (s) VALUES ('bad'); +Warnings: +Warning 1364 Field 'y' doesn't have a default value +INSERT INTO t1 (y, s) VALUES (0, 0), (2000, 2000), (2001, 2001); +SELECT * FROM t1 ta, t1 tb WHERE ta.y = tb.y; +y s y s +0000 bad 0000 bad +0000 0 0000 bad +0000 bad 0000 0 +0000 0 0000 0 +2000 2000 2000 2000 +2001 2001 2001 2001 +SELECT * FROM t1 WHERE t1.y = 0; +y s +0000 bad +0000 0 +SELECT * FROM t1 WHERE t1.y = 2000; +y s +2000 2000 +SELECT ta.y AS ta_y, ta.s, tb.y AS tb_y, tb.s FROM t1 ta, t1 tb HAVING ta_y = tb_y; +ta_y s tb_y s +0000 bad 0000 bad +0000 0 0000 bad +0000 bad 0000 0 +0000 0 0000 0 +2000 2000 2000 2000 +2001 2001 2001 2001 +DROP TABLE t1; +# End of 5.1 tests diff --git a/mysql-test/t/type_year.test b/mysql-test/t/type_year.test index 16fd39a59d8..d8da4ccc82c 100644 --- a/mysql-test/t/type_year.test +++ b/mysql-test/t/type_year.test @@ -133,6 +133,22 @@ SELECT * FROM t4 WHERE yyyy > 123; DROP TABLE t2, t4; +--echo # +--echo # Bug #49910: Behavioural change in SELECT/WHERE on YEAR(4) data type +--echo # + +CREATE TABLE t1 (y YEAR NOT NULL, s VARCHAR(4)); +INSERT INTO t1 (s) VALUES ('bad'); +INSERT INTO t1 (y, s) VALUES (0, 0), (2000, 2000), (2001, 2001); + +SELECT * FROM t1 ta, t1 tb WHERE ta.y = tb.y; +SELECT * FROM t1 WHERE t1.y = 0; +SELECT * FROM t1 WHERE t1.y = 2000; + +SELECT ta.y AS ta_y, ta.s, tb.y AS tb_y, tb.s FROM t1 ta, t1 tb HAVING ta_y = tb_y; + +DROP TABLE t1; + --echo # --echo End of 5.1 tests diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index cd3f18fe1eb..6e38220abd1 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1190,12 +1190,21 @@ get_year_value(THD *thd, Item ***item_arg, Item **cache_arg, /* Coerce value to the 19XX form in order to correctly compare YEAR(2) & YEAR(4) types. + Here we are converting all item values but YEAR(4) fields since + 1) YEAR(4) already has a regular YYYY form and + 2) we don't want to convert zero/bad YEAR(4) values to the + value of 2000. */ - if (value < 70) - value+= 100; - if (value <= 1900) - value+= 1900; - + Item *real_item= item->real_item(); + if (!(real_item->type() == Item::FIELD_ITEM && + ((Item_field *)real_item)->field->type() == MYSQL_TYPE_YEAR && + ((Item_field *)real_item)->field->field_length == 4)) + { + if (value < 70) + value+= 100; + if (value <= 1900) + value+= 1900; + } /* Convert year to DATETIME of form YYYY-00-00 00:00:00 (YYYY0000000000). */ value*= 10000000000LL;