From fc0adebe0eb34e94eacc7a677a1d55b349131953 Mon Sep 17 00:00:00 2001 From: "cbell/Chuck@mysql_cab_desk." <> Date: Thu, 18 Jan 2007 13:35:49 -0500 Subject: [PATCH 01/15] BUG#21490 - No warning issued for deprecated replication parameters This patch deprecates the replication startup options in the configuration file and on the command line. The options deprecated include: MASTER_HOST, MASTER_USER, MASTER_PASSWORD, MASTER_PORT, MASTER_CONNECT_RETRY, MASTER_SSL, MASTER_SSL_CA, MASTER_SSL_CAPATH, MASTER_SSL_CERT, MASTER_SSL_KEY, and MASTER_SSL_CIPHER The code is designed to print the warning message once. --- sql/mysql_priv.h | 12 ++++++++++++ sql/mysqld.cc | 25 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index cad42320db4..06a02978994 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -91,6 +91,18 @@ char* query_table_status(THD *thd,const char *db,const char *table_name); #define PREV_BITS(type,A) ((type) (((type) 1 << (A)) -1)) #define all_bits_set(A,B) ((A) & (B) != (B)) +#define WARN_DEPRECATED(Thd,Ver,Old,New) \ + do { \ + DBUG_ASSERT(strncmp(Ver, MYSQL_SERVER_VERSION, sizeof(Ver)-1) > 0); \ + if (Thd != NULL) \ + push_warning_printf(((THD *)Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \ + ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \ + (Old), (Ver), (New)); \ + else \ + sql_print_warning("The syntax %s is deprecated and will be removed " \ + "in MySQL %s. Please use %s instead.", (Old), (Ver), (New)); \ + } while(0) + extern CHARSET_INFO *system_charset_info, *files_charset_info ; extern CHARSET_INFO *national_charset_info, *table_alias_charset; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 488751eb015..a7dc8bede84 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -356,6 +356,8 @@ my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; my_bool opt_log_slave_updates= 0; my_bool opt_innodb; +bool slave_warning_issued = false; + #ifdef HAVE_NDBCLUSTER_DB const char *opt_ndbcluster_connectstring= 0; const char *opt_ndb_connectstring= 0; @@ -6888,6 +6890,29 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case (int) OPT_STANDALONE: /* Dummy option for NT */ break; #endif + /* + The following change issues a deprecation warning if the slave + configuration is specified either in the my.cnf file or on + the command-line. See BUG#21490. + */ + case OPT_MASTER_HOST: + case OPT_MASTER_USER: + case OPT_MASTER_PASSWORD: + case OPT_MASTER_PORT: + case OPT_MASTER_CONNECT_RETRY: + case OPT_MASTER_SSL: + case OPT_MASTER_SSL_KEY: + case OPT_MASTER_SSL_CERT: + case OPT_MASTER_SSL_CAPATH: + case OPT_MASTER_SSL_CIPHER: + case OPT_MASTER_SSL_CA: + if (!slave_warning_issued) //only show the warning once + { + slave_warning_issued = true; + WARN_DEPRECATED(0, "5.2", "for replication startup options", + "'CHANGE MASTER'"); + } + break; case OPT_CONSOLE: if (opt_console) opt_error_log= 0; // Force logs to stdout From ffeaffc26fc27e3d068210966254d3189d4868c5 Mon Sep 17 00:00:00 2001 From: "bar@mysql.com" <> Date: Wed, 24 Jan 2007 16:37:38 +0400 Subject: [PATCH 02/15] Bug#25815 Data truncated for column TEXT Problem: "Data truncated" warning was incorrectly generated when storing a Japanese character encoded in utf8 into a cp932 column. Reason: Incorrect wrong warning condition compared the original length of the character in bytes (which is 3 in utf8) to the converted length of the character in bytes (which is 2 in cp932). Fix: use "how many bytes were scanned from input" instead of "how many bytes were put to the column" in the condition. --- mysql-test/r/ctype_cp932.result | 12 ++++++++++++ mysql-test/t/ctype_cp932.test | 11 +++++++++++ sql/field.cc | 2 +- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/ctype_cp932.result b/mysql-test/r/ctype_cp932.result index ed57b87c1ba..10451686e2c 100755 --- a/mysql-test/r/ctype_cp932.result +++ b/mysql-test/r/ctype_cp932.result @@ -11353,3 +11353,15 @@ a a a drop table t1; +set names utf8; +create table t1 (a text) default character set cp932; +insert into t1 values (_utf8 0xE38182); +show warnings; +Level Code Message +select * from t1; +a +あ +select hex(a) from t1; +hex(a) +82A0 +drop table t1; diff --git a/mysql-test/t/ctype_cp932.test b/mysql-test/t/ctype_cp932.test index 688d06c4dde..c6196b928b0 100644 --- a/mysql-test/t/ctype_cp932.test +++ b/mysql-test/t/ctype_cp932.test @@ -413,3 +413,14 @@ select * from t1; insert into t1 values ('abc'); select * from t1; drop table t1; + +# +# Bug#25815 Data truncated for column TEXT +# +set names utf8; +create table t1 (a text) default character set cp932; +insert into t1 values (_utf8 0xE38182); +show warnings; +select * from t1; +select hex(a) from t1; +drop table t1; diff --git a/sql/field.cc b/sql/field.cc index 257c7846468..61a687567fc 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -7024,7 +7024,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) cannot_convert_error_pos, from + length)) return 2; - if (copy_length < length) + if (from_end_pos < from + length) { report_data_too_long(this); return 2; From b50f3c3149f58a6d4669095de3b6e2d00794008f Mon Sep 17 00:00:00 2001 From: "rafal@quant.(none)" <> Date: Sat, 3 Feb 2007 20:14:16 +0100 Subject: [PATCH 03/15] BUG#25306 (Race conditions during replication slave shutdown (valgrind stacks)): The possibility of the race is removed by changing sequence of calls pthread_mutex_unlock(&mi->run_lock); pthread_cond_broadcast(&mi->stop_cond); into pthread_cond_broadcast(&mi->stop_cond); pthread_mutex_unlock(&mi->run_lock); at the end of I/O thread (similar change at the end of SQL thread). This ensures that no thread waiting on the condition executes between the broadcast and the unlock and thus can't delete the mi structure which caused the bug. --- sql/slave.cc | 19 ++++++++++++++----- sql/sql_repl.cc | 8 +++++--- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/sql/slave.cc b/sql/slave.cc index 8805f950d50..13793705b9a 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3758,8 +3758,13 @@ err: mi->abort_slave= 0; mi->slave_running= 0; mi->io_thd= 0; - pthread_mutex_unlock(&mi->run_lock); + /* + Note: the order of the two following calls (first broadcast, then unlock) + is important. Otherwise a killer_thread can execute between the calls and + delete the mi structure leading to a crash! (see BUG#25306 for details) + */ pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done + pthread_mutex_unlock(&mi->run_lock); #ifndef DBUG_OFF if (abort_slave_event_count && !events_till_abort) goto slave_begin; @@ -3977,8 +3982,13 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ THD_CHECK_SENTRY(thd); delete thd; pthread_mutex_unlock(&LOCK_thread_count); - pthread_cond_broadcast(&rli->stop_cond); + /* + Note: the order of the broadcast and unlock calls below (first broadcast, then unlock) + is important. Otherwise a killer_thread can execute between the calls and + delete the mi structure leading to a crash! (see BUG#25306 for details) + */ + pthread_cond_broadcast(&rli->stop_cond); #ifndef DBUG_OFF /* Bug #19938 Valgrind error (race) in handle_slave_sql() @@ -3986,9 +3996,8 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ */ const int eta= rli->events_till_abort; #endif - - // tell the world we are done - pthread_mutex_unlock(&rli->run_lock); + pthread_mutex_unlock(&rli->run_lock); // tell the world we are done + #ifndef DBUG_OFF // TODO: reconsider the code below if (abort_slave_event_count && !eta) goto slave_begin; diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 88a752f7acb..004abc775bf 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -882,12 +882,14 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report ) { + DBUG_ENTER("stop_slave"); + int slave_errno; if (!thd) thd = current_thd; if (check_access(thd, SUPER_ACL, any_db,0,0,0,0)) - return 1; + DBUG_RETURN(1); thd->proc_info = "Killing slave"; int thread_mask; lock_slave_threads(mi); @@ -921,12 +923,12 @@ int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report ) { if (net_report) my_message(slave_errno, ER(slave_errno), MYF(0)); - return 1; + DBUG_RETURN(1); } else if (net_report) send_ok(thd); - return 0; + DBUG_RETURN(0); } From b3a03dada994b5e257ce880c0d4f478b1fd989bf Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Thu, 8 Feb 2007 15:53:14 +0100 Subject: [PATCH 04/15] Fix for BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values". When in an INSERT ON DUPLICATE KEY UPDATE, using an autoincrement column, we inserted some autogenerated values and also updated some rows, some autogenerated values were not used (for example, even if 10 was the largest autoinc value in the table at the start of the statement, 12 could be the first autogenerated value inserted by the statement, instead of 11). One autogenerated value was lost per updated row. Led to exhausting the range of the autoincrement column faster. Bug introduced by fix of BUG#20188; present since 5.0.24 and 5.1.12. This bug breaks replication from a pre-5.0.24 master. But the present bugfix, as it makes INSERT ON DUP KEY UPDATE behave like pre-5.0.24, breaks replication from a [5.0.24,5.0.34] master to a fixed (5.0.36) slave! To warn users against this when they upgrade their slave, as agreed with the support team, we add code for a fixed slave to detect that it is connected to a buggy master in a situation (INSERT ON DUP KEY UPDATE into autoinc column) likely to break replication, in which case it cannot replicate so stops and prints a message to the slave's error log and to SHOW SLAVE STATUS. For 5.0.36->[5.0.24,5.0.34] replication we cannot warn as master does not know the slave's version (but we always recommended to users to have slave at least as new as master). As agreed with support, I'll also ask for an alert to be put into the MySQL Network Monitoring and Advisory Service. --- mysql-test/r/rpl_insert_id.result | 58 ++++++++ mysql-test/r/rpl_known_bugs_detection.result | 133 ++++++++++++++++++ mysql-test/t/rpl_insert_id.test | 53 +++++++ .../t/rpl_known_bugs_detection-master.opt | 1 + mysql-test/t/rpl_known_bugs_detection.test | 90 ++++++++++++ sql/log_event.cc | 37 +++++ sql/log_event.h | 2 + sql/slave.cc | 64 +++++++++ sql/slave.h | 1 + sql/sql_insert.cc | 20 ++- 10 files changed, 458 insertions(+), 1 deletion(-) create mode 100644 mysql-test/r/rpl_known_bugs_detection.result create mode 100644 mysql-test/t/rpl_known_bugs_detection-master.opt create mode 100644 mysql-test/t/rpl_known_bugs_detection.test diff --git a/mysql-test/r/rpl_insert_id.result b/mysql-test/r/rpl_insert_id.result index d133a2ae8ed..a5c8e17f67e 100644 --- a/mysql-test/r/rpl_insert_id.result +++ b/mysql-test/r/rpl_insert_id.result @@ -234,6 +234,64 @@ n b 2 100 3 350 drop table t1; +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT, +UNIQUE(b)); +INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; +SELECT * FROM t1; +a b +1 10 +2 2 +SELECT * FROM t1; +a b +1 10 +2 2 +drop table t1; +CREATE TABLE t1 ( +id bigint(20) unsigned NOT NULL auto_increment, +field_1 int(10) unsigned NOT NULL, +field_2 varchar(255) NOT NULL, +field_3 varchar(255) NOT NULL, +PRIMARY KEY (id), +UNIQUE KEY field_1 (field_1, field_2) +); +CREATE TABLE t2 ( +field_a int(10) unsigned NOT NULL, +field_b varchar(255) NOT NULL, +field_c varchar(255) NOT NULL +); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (1, 'a', '1a'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (2, 'b', '2b'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (3, 'c', '3c'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (4, 'd', '4d'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (5, 'e', '5e'); +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +INSERT INTO t2 (field_a, field_b, field_c) VALUES (6, 'f', '6f'); +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +SELECT * FROM t1; +id field_1 field_2 field_3 +1 1 a 1a +2 2 b 2b +3 3 c 3c +4 4 d 4d +5 5 e 5e +6 6 f 6f +SELECT * FROM t1; +id field_1 field_2 field_3 +1 1 a 1a +2 2 b 2b +3 3 c 3c +4 4 d 4d +5 5 e 5e +6 6 f 6f +drop table t1, t2; DROP PROCEDURE IF EXISTS p1; DROP TABLE IF EXISTS t1, t2; SELECT LAST_INSERT_ID(0); diff --git a/mysql-test/r/rpl_known_bugs_detection.result b/mysql-test/r/rpl_known_bugs_detection.result new file mode 100644 index 00000000000..c23586c6f61 --- /dev/null +++ b/mysql-test/r/rpl_known_bugs_detection.result @@ -0,0 +1,133 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT, +UNIQUE(b)); +INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; +SELECT * FROM t1; +a b +1 10 +2 2 +show slave status;; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port # +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1105 +Last_Error Error 'master may suffer from http://bugs.mysql.com/bug.php?id=24432 so slave stops; check error log on slave for more info' on query. Default database: 'test'. Query: 'INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10' +Skip_Counter 0 +Exec_Master_Log_Pos 238 +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SELECT * FROM t1; +a b +stop slave; +reset slave; +reset master; +drop table t1; +start slave; +CREATE TABLE t1 ( +id bigint(20) unsigned NOT NULL auto_increment, +field_1 int(10) unsigned NOT NULL, +field_2 varchar(255) NOT NULL, +field_3 varchar(255) NOT NULL, +PRIMARY KEY (id), +UNIQUE KEY field_1 (field_1, field_2) +); +CREATE TABLE t2 ( +field_a int(10) unsigned NOT NULL, +field_b varchar(255) NOT NULL, +field_c varchar(255) NOT NULL +); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (1, 'a', '1a'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (2, 'b', '2b'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (3, 'c', '3c'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (4, 'd', '4d'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (5, 'e', '5e'); +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +INSERT INTO t2 (field_a, field_b, field_c) VALUES (6, 'f', '6f'); +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +SELECT * FROM t1; +id field_1 field_2 field_3 +1 1 a 1a +2 2 b 2b +3 3 c 3c +4 4 d 4d +5 5 e 5e +6 6 f 6f +show slave status;; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port # +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1105 +Last_Error Error 'master may suffer from http://bugs.mysql.com/bug.php?id=24432 so slave stops; check error log on slave for more info' on query. Default database: 'test'. Query: 'INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c' +Skip_Counter 0 +Exec_Master_Log_Pos 1270 +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SELECT * FROM t1; +id field_1 field_2 field_3 +drop table t1, t2; +drop table t1, t2; diff --git a/mysql-test/t/rpl_insert_id.test b/mysql-test/t/rpl_insert_id.test index 331a913256c..be2948e9678 100644 --- a/mysql-test/t/rpl_insert_id.test +++ b/mysql-test/t/rpl_insert_id.test @@ -245,6 +245,59 @@ select * from t1 order by n; connection master; drop table t1; +# +# BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values" +# + +# testcase with INSERT VALUES +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT, +UNIQUE(b)); +INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; +SELECT * FROM t1; +sync_slave_with_master; +SELECT * FROM t1; +connection master; +drop table t1; + +# tescase with INSERT SELECT +CREATE TABLE t1 ( + id bigint(20) unsigned NOT NULL auto_increment, + field_1 int(10) unsigned NOT NULL, + field_2 varchar(255) NOT NULL, + field_3 varchar(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY field_1 (field_1, field_2) +); +CREATE TABLE t2 ( + field_a int(10) unsigned NOT NULL, + field_b varchar(255) NOT NULL, + field_c varchar(255) NOT NULL +); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (1, 'a', '1a'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (2, 'b', '2b'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (3, 'c', '3c'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (4, 'd', '4d'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (5, 'e', '5e'); +# Updating table t1 based on values from table t2 +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +# Inserting new record into t2 +INSERT INTO t2 (field_a, field_b, field_c) VALUES (6, 'f', '6f'); +# Updating t1 again +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +SELECT * FROM t1; +sync_slave_with_master; +SELECT * FROM t1; +connection master; +drop table t1, t2; + # # BUG#20339: stored procedure using LAST_INSERT_ID() does not # replicate statement-based diff --git a/mysql-test/t/rpl_known_bugs_detection-master.opt b/mysql-test/t/rpl_known_bugs_detection-master.opt new file mode 100644 index 00000000000..d4ba386a1a0 --- /dev/null +++ b/mysql-test/t/rpl_known_bugs_detection-master.opt @@ -0,0 +1 @@ +--loose-debug=d,pretend_version_50034_in_binlog diff --git a/mysql-test/t/rpl_known_bugs_detection.test b/mysql-test/t/rpl_known_bugs_detection.test new file mode 100644 index 00000000000..4719716d4a1 --- /dev/null +++ b/mysql-test/t/rpl_known_bugs_detection.test @@ -0,0 +1,90 @@ +# Test to see if slave can detect certain known bugs present +# on the master, and appropriately decides to stop +# (assuming the bug is fixed in the slave, slave cannot of course +# imitate the bug, so it has to stop). + +source include/have_debug.inc; +source include/master-slave.inc; + +# +# This is to test that slave properly detects if +# master may suffer from: +# BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values" +# (i.e. on master, INSERT ON DUPLICATE KEY UPDATE is used and manipulates +# an auto_increment column, and is binlogged statement-based). +# + +# testcase with INSERT VALUES +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT, +UNIQUE(b)); +sync_slave_with_master; +connection master; +INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; +SELECT * FROM t1; +connection slave; +wait_for_slave_to_stop; +# show the error message +--replace_column 1 # 4 # 7 # 8 # 9 # 23 # 33 # +--query_vertical show slave status; +# show that it was not replicated +SELECT * FROM t1; + +# restart replication for the next testcase +stop slave; +reset slave; +connection master; +reset master; +drop table t1; +connection slave; +start slave; + +# testcase with INSERT SELECT +connection master; +CREATE TABLE t1 ( + id bigint(20) unsigned NOT NULL auto_increment, + field_1 int(10) unsigned NOT NULL, + field_2 varchar(255) NOT NULL, + field_3 varchar(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY field_1 (field_1, field_2) +); +CREATE TABLE t2 ( + field_a int(10) unsigned NOT NULL, + field_b varchar(255) NOT NULL, + field_c varchar(255) NOT NULL +); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (1, 'a', '1a'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (2, 'b', '2b'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (3, 'c', '3c'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (4, 'd', '4d'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (5, 'e', '5e'); +sync_slave_with_master; +connection master; +# Updating table t1 based on values from table t2 +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +# Inserting new record into t2 +INSERT INTO t2 (field_a, field_b, field_c) VALUES (6, 'f', '6f'); +# Updating t1 again +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +SELECT * FROM t1; +connection slave; +wait_for_slave_to_stop; +# show the error message +--replace_column 1 # 4 # 7 # 8 # 9 # 23 # 33 # +--query_vertical show slave status; +# show that it was not replicated +SELECT * FROM t1; +connection master; +drop table t1, t2; +connection slave; +drop table t1, t2; + +# End of 5.0 tests diff --git a/sql/log_event.cc b/sql/log_event.cc index 657fd510e78..a14cd79461d 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -2047,6 +2047,8 @@ Start_log_event_v3::Start_log_event_v3(const char* buf, binlog_version= uint2korr(buf+ST_BINLOG_VER_OFFSET); memcpy(server_version, buf+ST_SERVER_VER_OFFSET, ST_SERVER_VER_LEN); + // prevent overrun if log is corrupted on disk + server_version[ST_SERVER_VER_LEN-1]= 0; created= uint4korr(buf+ST_CREATED_OFFSET); /* We use log_pos to mark if this was an artificial event or not */ artificial_event= (log_pos == 0); @@ -2170,6 +2172,8 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver) switch (binlog_ver) { case 4: /* MySQL 5.0 */ memcpy(server_version, ::server_version, ST_SERVER_VER_LEN); + DBUG_EXECUTE_IF("pretend_version_50034_in_binlog", + strmov(server_version, "5.0.34");); common_header_len= LOG_EVENT_HEADER_LEN; number_of_event_types= LOG_EVENT_TYPES; /* we'll catch my_malloc() error in is_valid() */ @@ -2241,6 +2245,7 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver) post_header_len= 0; /* will make is_valid() fail */ break; } + calc_server_version_split(); } @@ -2280,6 +2285,7 @@ Format_description_log_event(const char* buf, post_header_len= (uint8*) my_memdup((byte*)buf+ST_COMMON_HEADER_LEN_OFFSET+1, number_of_event_types* sizeof(*post_header_len), MYF(0)); + calc_server_version_split(); DBUG_VOID_RETURN; } @@ -2380,6 +2386,37 @@ int Format_description_log_event::exec_event(struct st_relay_log_info* rli) } #endif + +/** + Splits the event's 'server_version' string into three numeric pieces stored + into 'server_version_split': + X.Y.Zabc (X,Y,Z numbers, a not a digit) -> {X,Y,Z} + X.Yabc -> {X,Y,0} + Xabc -> {X,0,0} + 'server_version_split' is then used for lookups to find if the server which + created this event has some known bug. +*/ +void Format_description_log_event::calc_server_version_split() +{ + char *p= server_version, *r; + ulong number; + for (uint i= 0; i<=2; i++) + { + number= strtoul(p, &r, 10); + server_version_split[i]= (uchar)number; + DBUG_ASSERT(number < 256); // fit in uchar + p= r; + DBUG_ASSERT(!((i == 0) && (*r != '.'))); // should be true in practice + if (*r == '.') + p++; // skip the dot + } + DBUG_PRINT("info",("Format_description_log_event::server_version_split:" + " '%s' %d %d %d", server_version, + server_version_split[0], + server_version_split[1], server_version_split[2])); +} + + /************************************************************************** Load_log_event methods General note about Load_log_event: the binlogging of LOAD DATA INFILE is diff --git a/sql/log_event.h b/sql/log_event.h index fd2a5e5fb63..d3ebe6860f5 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -1102,6 +1102,7 @@ public: uint8 number_of_event_types; /* The list of post-headers' lengthes */ uint8 *post_header_len; + uchar server_version_split[3]; Format_description_log_event(uint8 binlog_ver, const char* server_ver=0); @@ -1133,6 +1134,7 @@ public: */ return FORMAT_DESCRIPTION_HEADER_LEN; } + void calc_server_version_split(); }; diff --git a/sql/slave.cc b/sql/slave.cc index 8805f950d50..37c782a42c3 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -5165,6 +5165,70 @@ end: } +/** + Detects, based on master's version (as found in the relay log), if master + has a certain bug. + @param rli RELAY_LOG_INFO which tells the master's version + @param bug_id Number of the bug as found in bugs.mysql.com + @return TRUE if master has the bug, FALSE if it does not. +*/ +bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id) +{ + struct st_version_range_for_one_bug { + uint bug_id; + const uchar introduced_in[3]; // first version with bug + const uchar fixed_in[3]; // first version with fix + }; + static struct st_version_range_for_one_bug versions_for_all_bugs[]= + { + {24432, { 5, 0, 24 }, { 5, 0, 36 } }, + {24432, { 5, 1, 12 }, { 5, 1, 16 } } + }; + const uchar *master_ver= + rli->relay_log.description_event_for_exec->server_version_split; + + DBUG_ASSERT(sizeof(rli->relay_log.description_event_for_exec->server_version_split) == 3); + + for (uint i= 0; + i < sizeof(versions_for_all_bugs)/sizeof(*versions_for_all_bugs);i++) + { + const uchar *introduced_in= versions_for_all_bugs[i].introduced_in, + *fixed_in= versions_for_all_bugs[i].fixed_in; + if ((versions_for_all_bugs[i].bug_id == bug_id) && + (memcmp(introduced_in, master_ver, 3) <= 0) && + (memcmp(fixed_in, master_ver, 3) > 0)) + { + // a verbose message for the error log + slave_print_error(rli, ER_UNKNOWN_ERROR, + "According to the master's version ('%s')," + " it is probable that master suffers from this bug:" + " http://bugs.mysql.com/bug.php?id=%u" + " and thus replicating the current binary log event" + " may make the slave's data become different from the" + " master's data." + " To take no risk, slave refuses to replicate" + " this event and stops." + " We recommend that all updates be stopped on the" + " master and slave, that the data of both be" + " manually synchronized," + " that master's binary logs be deleted," + " that master be upgraded to a version at least" + " equal to '%d.%d.%d'. Then replication can be" + " restarted.", + rli->relay_log.description_event_for_exec->server_version, + bug_id, + fixed_in[0], fixed_in[1], fixed_in[2]); + // a short message for SHOW SLAVE STATUS (message length constraints) + my_printf_error(ER_UNKNOWN_ERROR, "master may suffer from" + " http://bugs.mysql.com/bug.php?id=%u" + " so slave stops; check error log on slave" + " for more info", MYF(0), bug_id); + return TRUE; + } + } + return FALSE; +} + #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION template class I_List_iterator; template class I_List_iterator; diff --git a/sql/slave.h b/sql/slave.h index bbf450bab75..e7d4456ccd9 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -533,6 +533,7 @@ void table_rule_ent_hash_to_str(String* s, HASH* h); void table_rule_ent_dynamic_array_to_str(String* s, DYNAMIC_ARRAY* a); bool show_master_info(THD* thd, MASTER_INFO* mi); bool show_binlog_info(THD* thd); +bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id); /* See if the query uses any tables that should not be replicated */ bool tables_ok(THD* thd, TABLE_LIST* tables); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index c60d3c307d0..c5f1524c556 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -58,6 +58,7 @@ #include "sp_head.h" #include "sql_trigger.h" #include "sql_select.h" +#include "slave.h" static int check_null_fields(THD *thd,TABLE *entry); #ifndef EMBEDDED_LIBRARY @@ -468,6 +469,14 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, thd->cuted_fields = 0L; table->next_number_field=table->found_next_number_field; +#ifdef HAVE_REPLICATION + if (thd->slave_thread && + (info.handle_duplicates == DUP_UPDATE) && + (table->next_number_field != NULL) && + rpl_master_has_bug(&active_mi->rli, 24432)) + goto abort; +#endif + error=0; id=0; thd->proc_info="update"; @@ -1133,11 +1142,11 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (res == VIEW_CHECK_ERROR) goto before_trg_err; + table->file->restore_auto_increment(); if ((error=table->file->update_row(table->record[1],table->record[0]))) { if ((error == HA_ERR_FOUND_DUPP_KEY) && info->ignore) { - table->file->restore_auto_increment(); goto ok_or_after_trg_err; } goto err; @@ -2369,6 +2378,15 @@ select_insert::prepare(List &values, SELECT_LEX_UNIT *u) } restore_record(table,s->default_values); // Get empty record table->next_number_field=table->found_next_number_field; + +#ifdef HAVE_REPLICATION + if (thd->slave_thread && + (info.handle_duplicates == DUP_UPDATE) && + (table->next_number_field != NULL) && + rpl_master_has_bug(&active_mi->rli, 24432)) + DBUG_RETURN(1); +#endif + thd->cuted_fields=0; if (info.ignore || info.handle_duplicates != DUP_ERROR) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); From 8b1609a63717227b8931bb3a7fdaf45a43b831c7 Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Thu, 15 Feb 2007 15:39:03 +0100 Subject: [PATCH 05/15] Fix for BUG#25507 "multi-row insert delayed + auto increment causes duplicate key entries on slave" (two concurrrent connections doing multi-row INSERT DELAYED to insert into an auto_increment column, caused replication slave to stop with "duplicate key error" (and binlog was wrong)), and BUG#26116 "If multi-row INSERT DELAYED has errors, statement-based binlogging breaks" (the binlog was not accounting for all rows inserted, or slave could stop). The fix is that: if (statement-based) binlogging is on, a multi-row INSERT DELAYED is silently converted to a non-delayed INSERT. Note: it is not possible to test BUG#25507 in 5.0 (requires mysqlslap), so it is tested only in the changeset for 5.1. However, BUG#26116 is tested here, and the fix for BUG#25507 is the same code change. --- mysql-test/r/innodb-replace.result | 4 +- mysql-test/r/rpl_insert_delayed.result | 31 ++++++++++++ mysql-test/t/innodb-replace.test | 4 +- mysql-test/t/rpl_insert_delayed.test | 67 ++++++++++++++++++++++++++ sql/sql_insert.cc | 21 ++++++++ 5 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 mysql-test/r/rpl_insert_delayed.result create mode 100644 mysql-test/t/rpl_insert_delayed.test diff --git a/mysql-test/r/innodb-replace.result b/mysql-test/r/innodb-replace.result index b7edcc49e56..77e0aeb38fd 100644 --- a/mysql-test/r/innodb-replace.result +++ b/mysql-test/r/innodb-replace.result @@ -2,11 +2,11 @@ drop table if exists t1; create table t1 (c1 char(5) unique not null, c2 int, stamp timestamp) engine=innodb; select * from t1; c1 c2 stamp -replace delayed into t1 (c1, c2) values ( "text1","11"),( "text2","12"); +replace delayed into t1 (c1, c2) values ( "text1","11"); ERROR HY000: Table storage engine for 't1' doesn't have this option select * from t1; c1 c2 stamp -replace delayed into t1 (c1, c2) values ( "text1","12"),( "text2","13"),( "text3","14", "a" ),( "text4","15", "b" ); +replace delayed into t1 (c1, c2) values ( "text1","12"); ERROR HY000: Table storage engine for 't1' doesn't have this option select * from t1; c1 c2 stamp diff --git a/mysql-test/r/rpl_insert_delayed.result b/mysql-test/r/rpl_insert_delayed.result new file mode 100644 index 00000000000..38e2cddd650 --- /dev/null +++ b/mysql-test/r/rpl_insert_delayed.result @@ -0,0 +1,31 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +CREATE TABLE t1 (id INT primary key auto_increment, name VARCHAR(64)); +truncate table t1; +insert delayed into t1 values(10, "my name"); +insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); +ERROR 23000: Duplicate entry '10' for key 1 +flush table t1; +select * from t1; +id name +10 my name +select * from t1; +id name +10 my name +delete from t1 where id!=10; +insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); +ERROR 23000: Duplicate entry '10' for key 1 +flush table t1; +select * from t1; +id name +10 my name +20 is Bond +select * from t1; +id name +10 my name +20 is Bond +drop table t1; diff --git a/mysql-test/t/innodb-replace.test b/mysql-test/t/innodb-replace.test index 51b70f34b65..d44ede65ce8 100644 --- a/mysql-test/t/innodb-replace.test +++ b/mysql-test/t/innodb-replace.test @@ -12,10 +12,10 @@ drop table if exists t1; create table t1 (c1 char(5) unique not null, c2 int, stamp timestamp) engine=innodb; select * from t1; --error 1031 -replace delayed into t1 (c1, c2) values ( "text1","11"),( "text2","12"); +replace delayed into t1 (c1, c2) values ( "text1","11"); select * from t1; --error 1031 -replace delayed into t1 (c1, c2) values ( "text1","12"),( "text2","13"),( "text3","14", "a" ),( "text4","15", "b" ); +replace delayed into t1 (c1, c2) values ( "text1","12"); select * from t1; drop table t1; diff --git a/mysql-test/t/rpl_insert_delayed.test b/mysql-test/t/rpl_insert_delayed.test new file mode 100644 index 00000000000..3f72f3a3625 --- /dev/null +++ b/mysql-test/t/rpl_insert_delayed.test @@ -0,0 +1,67 @@ +--source include/master-slave.inc +--source include/not_embedded.inc +--source include/not_windows.inc + +connection master; + +let $binlog_format_statement=1; + +CREATE TABLE t1 (id INT primary key auto_increment, name VARCHAR(64)); + +sync_slave_with_master; + +# +# BUG#26116 "If multi-row INSERT DELAYED has errors, +# statement-based binlogging breaks"; +# happened only in statement-based binlogging. +# + +connection master; +truncate table t1; +# first scenario: duplicate on first row +insert delayed into t1 values(10, "my name"); +if ($binlog_format_statement) +{ + # statement below will be converted to non-delayed INSERT and so + # will stop at first error, guaranteeing replication. + --error ER_DUP_ENTRY + insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); +} +if (!$binlog_format_statement) +{ + insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); +} +flush table t1; # to wait for INSERT DELAYED to be done +select * from t1; +sync_slave_with_master; +# when bug existed in statement-based binlogging, t1 on slave had +# different content from on master +select * from t1; + +# second scenario: duplicate on second row +connection master; +delete from t1 where id!=10; +if ($binlog_format_statement) +{ + # statement below will be converted to non-delayed INSERT and so + # will be binlogged with its ER_DUP_ENTRY error code, guaranteeing + # replication (slave will hit the same error code and so be fine). + --error ER_DUP_ENTRY + insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); +} +if (!$binlog_format_statement) +{ + insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); +} +flush table t1; # to wait for INSERT DELAYED to be done +select * from t1; +sync_slave_with_master; +# when bug existed in statement-based binlogging, query was binlogged +# with error_code=0 so slave stopped +select * from t1; + +# clean up +connection master; +drop table t1; +sync_slave_with_master; +connection master; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index c5f1524c556..f0708ee548a 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -356,6 +356,27 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, (duplic == DUP_UPDATE)) lock_type=TL_WRITE; #endif + if ((lock_type == TL_WRITE_DELAYED) && + log_on && mysql_bin_log.is_open() && + (values_list.elements > 1)) + { + /* + Statement-based binary logging does not work in this case, because: + a) two concurrent statements may have their rows intermixed in the + queue, leading to autoincrement replication problems on slave (because + the values generated used for one statement don't depend only on the + value generated for the first row of this statement, so are not + replicable) + b) if first row of the statement has an error the full statement is + not binlogged, while next rows of the statement may be inserted. + c) if first row succeeds, statement is binlogged immediately with a + zero error code (i.e. "no error"), if then second row fails, query + will fail on slave too and slave will stop (wrongly believing that the + master got no error). + So we fallback to non-delayed INSERT. + */ + lock_type= TL_WRITE; + } table_list->lock_type= lock_type; #ifndef EMBEDDED_LIBRARY From 2f75c9cd69412de5d3736e775dd302ebdbc1e36d Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Thu, 15 Feb 2007 15:50:56 +0100 Subject: [PATCH 06/15] Backport from the Falcon tree. When opening/creating the transaction coordinator's log, if binlog is used, the tc log is the binlog so we use the binlog's name; otherwise we use the mmap-based log, named after the mandatory argument of the --log-tc option (meant for that). --- sql/log.cc | 2 +- sql/mysqld.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/log.cc b/sql/log.cc index 1961a5b6f88..aef8e2eff2c 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2523,7 +2523,7 @@ int TC_LOG_MMAP::open(const char *opt_name) goto err; if (using_heuristic_recover()) return 1; - if ((fd= my_create(logname, O_RDWR, 0, MYF(MY_WME))) < 0) + if ((fd= my_create(logname, CREATE_MODE, O_RDWR, MYF(MY_WME))) < 0) goto err; inited=1; file_length= opt_tc_log_size; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index d4f9791dade..672f585e721 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3171,7 +3171,7 @@ server."); (TC_LOG *) &tc_log_mmap) : (TC_LOG *) &tc_log_dummy); - if (tc_log->open(opt_bin_logname)) + if (tc_log->open(opt_bin_log ? opt_bin_logname : opt_tc_log_file)) { sql_print_error("Can't init tc log"); unireg_abort(1); From 44c6c4cc0540365ea865123a3965235bba00e530 Mon Sep 17 00:00:00 2001 From: "gbichot@dl145h.mysql.com" <> Date: Fri, 23 Feb 2007 15:32:51 +0100 Subject: [PATCH 07/15] the fix for BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values" didn't make it into 5.0.36 and 5.1.16, so we need to adjust the bug-detection-based-on-version-number code. Because the rpl tree has a too old version, rpl_insert_id cannot pass, so I disable it (like is already the case in 5.1-rpl for the same reason), and the repl team will re-enable it when they merge 5.0 and 5.1 into their trees (thus getting the right version number). --- mysql-test/t/disabled.def | 1 + sql/slave.cc | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 3213bd4eb5b..1464ddaa6b0 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -14,4 +14,5 @@ im_daemon_life_cycle : Bug#24415 see note: [19 Dec 23:17] Trudy Pelzer ndb_load : Bug#17233 user_limits : Bug#23921 random failure of user_limits.test flush2 : Bug#24805 Pushbuild can't handle test with --disable-log-bin +rpl_insert_id : replication team will enable as soon as they merge their tree with main tree and so got version number 5.0.38 diff --git a/sql/slave.cc b/sql/slave.cc index 37c782a42c3..f61d1e2ed69 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -5181,8 +5181,8 @@ bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id) }; static struct st_version_range_for_one_bug versions_for_all_bugs[]= { - {24432, { 5, 0, 24 }, { 5, 0, 36 } }, - {24432, { 5, 1, 12 }, { 5, 1, 16 } } + {24432, { 5, 0, 24 }, { 5, 0, 38 } }, + {24432, { 5, 1, 12 }, { 5, 1, 17 } } }; const uchar *master_ver= rli->relay_log.description_event_for_exec->server_version_split; From 4c6ced9fc581366fd5a26db647bf122c9421d860 Mon Sep 17 00:00:00 2001 From: "cbell/Chuck@mysql_cab_desk." <> Date: Fri, 23 Feb 2007 12:58:56 -0500 Subject: [PATCH 08/15] BUG#20141 "User-defined variables are not replicated properly for SF/ Triggers in SBR mode." BUG#14914 "SP: Uses of session variables in routines are not always replicated" BUG#25167 "Dupl. usage of user-variables in trigger/function is not replicated correctly" User-defined variables used inside of stored functions/triggers in statements which did not update tables directly were not replicated. We also had problems with replication of user-defined variables which were used in triggers (or stored functions called from table-updating statements) more than once. This patch addresses the first issue by enabling logging of all references to user-defined variables in triggers/stored functions and not only references from table-updating statements. The second issue stemmed from the fact that for user-defined variables used from triggers or stored functions called from table-updating statements we were writing binlog events for each reference instead of only one event for the first reference. This problem is already solved for stored functions called from non-updating statements with help of "event unioning" mechanism. So the patch simply extends this mechanism to the case affected. It also fixes small problem in this mechanism which caused wrong logging of references to user-variables in cases when non-updating statement called several stored functions which used the same variable and some of these function calls were omitted from binlog as they were not updating any tables. --- mysql-test/r/rpl_user_variables.result | 176 ++++++++++++++ mysql-test/t/rpl_user_variables.test | 303 ++++++++++++++++++++++++- sql/item_func.cc | 9 +- sql/log.cc | 4 +- sql/sp_head.cc | 18 +- sql/sql_class.cc | 7 + sql/sql_class.h | 2 +- 7 files changed, 511 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/rpl_user_variables.result b/mysql-test/r/rpl_user_variables.result index 45618688a33..05f2b68042e 100644 --- a/mysql-test/r/rpl_user_variables.result +++ b/mysql-test/r/rpl_user_variables.result @@ -108,4 +108,180 @@ slave-bin.000001 # User var 2 # @`a`=NULL slave-bin.000001 # Query 1 # use `test`; insert into t1 values (@a),(@a),(@a*5) insert into t1 select * FROM (select @var1 union select @var2) AS t2; drop table t1; +End of 4.1 tests. +DROP TABLE IF EXISTS t20; +DROP TABLE IF EXISTS t21; +DROP PROCEDURE IF EXISTS test.insert; +CREATE TABLE t20 (a VARCHAR(20)); +CREATE TABLE t21 (a VARCHAR(20)); +CREATE PROCEDURE test.insert() +BEGIN +IF (@VAR) +THEN +INSERT INTO test.t20 VALUES ('SP_TRUE'); +ELSE +INSERT INTO test.t20 VALUES ('SP_FALSE'); +END IF; +END| +CREATE TRIGGER test.insert_bi BEFORE INSERT +ON test.t20 FOR EACH ROW +BEGIN +IF (@VAR) +THEN +INSERT INTO test.t21 VALUES ('TRIG_TRUE'); +ELSE +INSERT INTO test.t21 VALUES ('TRIG_FALSE'); +END IF; +END| +SET @VAR=0; +CALL test.insert(); +SET @VAR=1; +CALL test.insert(); +On master: Check the tables for correct data +SELECT * FROM t20; +a +SP_FALSE +SP_TRUE +SELECT * FROM t21; +a +TRIG_FALSE +TRIG_TRUE +On slave: Check the tables for correct data and it matches master +SELECT * FROM t20; +a +SP_FALSE +SP_TRUE +SELECT * FROM t21; +a +TRIG_FALSE +TRIG_TRUE +DROP TABLE t20; +DROP TABLE t21; +DROP PROCEDURE test.insert; +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS test.square; +CREATE TABLE t1 (i INT); +CREATE FUNCTION test.square() RETURNS INTEGER DETERMINISTIC RETURN (@var * @var); +SET @var = 1; +INSERT INTO t1 VALUES (square()); +SET @var = 2; +INSERT INTO t1 VALUES (square()); +SET @var = 3; +INSERT INTO t1 VALUES (square()); +SET @var = 4; +INSERT INTO t1 VALUES (square()); +SET @var = 5; +INSERT INTO t1 VALUES (square()); +On master: Retrieve the values from the table +SELECT * FROM t1; +i +1 +4 +9 +16 +25 +On slave: Retrieve the values from the table and verify they are the same as on master +SELECT * FROM t1; +i +1 +4 +9 +16 +25 +DROP TABLE t1; +DROP FUNCTION test.square; +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +CREATE TABLE t1(a int); +CREATE FUNCTION f1() returns int deterministic +BEGIN +return @a; +END | +CREATE FUNCTION f2() returns int deterministic +BEGIN +IF (@b > 0) then +SET @c = (@a + @b); +else +SET @c = (@a - 1); +END if; +return @c; +END | +SET @a=500; +INSERT INTO t1 values(f1()); +SET @b = 125; +SET @c = 1; +INSERT INTO t1 values(f2()); +On master: Retrieve the values from the table +SELECT * from t1; +a +500 +625 +On slave: Check the tables for correct data and it matches master +SELECT * from t1; +a +500 +625 +DROP TABLE t1; +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +CREATE TABLE t1 (i int); +CREATE TABLE t2 (k int); +CREATE trigger t1_bi before INSERT on t1 for each row +BEGIN +INSERT INTO t2 values (@a); +SET @a:=42; +INSERT INTO t2 values (@a); +END | +SET @a:=100; +INSERT INTO t1 values (5); +On master: Check to see that data was inserted correctly in both tables +SELECT * from t1; +i +5 +SELECT * from t2; +k +100 +42 +On slave: Check the tables for correct data and it matches master +SELECT * from t1; +i +5 +SELECT * from t2; +k +100 +42 +End of 5.0 tests. +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +CREATE TABLE t1 (i INT); +CREATE FUNCTION f1() RETURNS INT RETURN @a; +CREATE FUNCTION f2() RETURNS INT +BEGIN +INSERT INTO t1 VALUES (10 + @a); +RETURN 0; +END| +SET @a:=123; +SELECT f1(), f2(); +f1() f2() +123 0 +On master: Check to see that data was inserted correctly +INSERT INTO t1 VALUES(f1()); +SELECT * FROM t1; +i +133 +123 +On slave: Check the table for correct data and it matches master +SELECT * FROM t1; +i +133 +123 +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE t1; stop slave; diff --git a/mysql-test/t/rpl_user_variables.test b/mysql-test/t/rpl_user_variables.test index dbe75a15038..b2f4bfbb29d 100644 --- a/mysql-test/t/rpl_user_variables.test +++ b/mysql-test/t/rpl_user_variables.test @@ -57,8 +57,305 @@ insert into t1 select * FROM (select @var1 union select @var2) AS t2; drop table t1; save_master_pos; -connection slave; -sync_with_master; +--echo End of 4.1 tests. + +# BUG#20141 +# The following tests ensure that if user-defined variables are used in SF/Triggers +# that they are replicated correctly. These tests should be run in both SBR and RBR +# modes. + +# This test uses a procedure that inserts data values based on the value of a +# user-defined variable. It also has a trigger that inserts data based on the +# same variable. Successful test runs show that the @var is replicated +# properly and that the procedure and trigger insert the correct data on the +# slave. +# +# The test of stored procedure was included for completeness. Replication of stored +# procedures was not directly affected by BUG#20141. +# +# This test was constructed for BUG#20141 + +--disable_warnings +DROP TABLE IF EXISTS t20; +DROP TABLE IF EXISTS t21; +DROP PROCEDURE IF EXISTS test.insert; +--enable_warnings + +CREATE TABLE t20 (a VARCHAR(20)); +CREATE TABLE t21 (a VARCHAR(20)); +DELIMITER |; + +# Create a procedure that uses the @var for flow control + +CREATE PROCEDURE test.insert() +BEGIN + IF (@VAR) + THEN + INSERT INTO test.t20 VALUES ('SP_TRUE'); + ELSE + INSERT INTO test.t20 VALUES ('SP_FALSE'); + END IF; +END| + +# Create a trigger that uses the @var for flow control + +CREATE TRIGGER test.insert_bi BEFORE INSERT + ON test.t20 FOR EACH ROW + BEGIN + IF (@VAR) + THEN + INSERT INTO test.t21 VALUES ('TRIG_TRUE'); + ELSE + INSERT INTO test.t21 VALUES ('TRIG_FALSE'); + END IF; + END| +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set @var and call the procedure, repeat with different values + +SET @VAR=0; +CALL test.insert(); +SET @VAR=1; +CALL test.insert(); + +--echo On master: Check the tables for correct data + +SELECT * FROM t20; +SELECT * FROM t21; + +sync_slave_with_master; + +--echo On slave: Check the tables for correct data and it matches master + +SELECT * FROM t20; +SELECT * FROM t21; +connection master; + +# Cleanup + +DROP TABLE t20; +DROP TABLE t21; +DROP PROCEDURE test.insert; + +# This test uses a stored function that uses user-defined variables to return data +# This test was constructed for BUG#20141 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS test.square; +--enable_warnings + +CREATE TABLE t1 (i INT); + +# Create function that returns a value from @var. In this case, the square function + +CREATE FUNCTION test.square() RETURNS INTEGER DETERMINISTIC RETURN (@var * @var); + +sync_slave_with_master; +connection master; + +# Set the @var to different values and insert them into a table + +SET @var = 1; +INSERT INTO t1 VALUES (square()); +SET @var = 2; +INSERT INTO t1 VALUES (square()); +SET @var = 3; +INSERT INTO t1 VALUES (square()); +SET @var = 4; +INSERT INTO t1 VALUES (square()); +SET @var = 5; +INSERT INTO t1 VALUES (square()); + +--echo On master: Retrieve the values from the table + +SELECT * FROM t1; + +sync_slave_with_master; + +--echo On slave: Retrieve the values from the table and verify they are the same as on master + +SELECT * FROM t1; + +connection master; + +# Cleanup + +DROP TABLE t1; +DROP FUNCTION test.square; + +# This test uses stored functions that uses user-defined variables to return data +# based on the use of @vars inside a function body. +# This test was constructed for BUG#14914 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +--enable_warnings + +CREATE TABLE t1(a int); +DELIMITER |; + +# Create a function that simply returns the value of an @var. +# Create a function that uses an @var for flow control, creates and uses another +# @var and sets its value to a value based on another @var. + +CREATE FUNCTION f1() returns int deterministic +BEGIN + return @a; +END | + +CREATE FUNCTION f2() returns int deterministic +BEGIN + IF (@b > 0) then + SET @c = (@a + @b); + else + SET @c = (@a - 1); + END if; + return @c; +END | +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set an @var to a value and insert data into a table using the first function. +# Set two more @vars to some values and insert data into a table using the second function. + +SET @a=500; +INSERT INTO t1 values(f1()); +SET @b = 125; +SET @c = 1; +INSERT INTO t1 values(f2()); + +sync_slave_with_master; + +--echo On master: Retrieve the values from the table + +SELECT * from t1; + +--echo On slave: Check the tables for correct data and it matches master + +SELECT * from t1; + +connection master; + +# Cleanup + +DROP TABLE t1; +DROP FUNCTION f1; +DROP FUNCTION f2; + +# This test uses a function that changes a user-defined variable in its body. This test +# will ensure the @vars are replicated when needed and not interrupt the normal execution +# of the function on the slave. This also applies to triggers. +# +# This test was constructed for BUG#25167 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +--enable_warnings +CREATE TABLE t1 (i int); +CREATE TABLE t2 (k int); +DELIMITER |; + +# Create a trigger that inserts data into another table, changes the @var then inserts +# another row with the modified value. + +CREATE trigger t1_bi before INSERT on t1 for each row +BEGIN + INSERT INTO t2 values (@a); + SET @a:=42; + INSERT INTO t2 values (@a); +END | +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set the @var to a value then insert data into first table. + +SET @a:=100; +INSERT INTO t1 values (5); + +--echo On master: Check to see that data was inserted correctly in both tables + +SELECT * from t1; +SELECT * from t2; + +sync_slave_with_master; + +--echo On slave: Check the tables for correct data and it matches master + +SELECT * from t1; +SELECT * from t2; + +connection master; + +--echo End of 5.0 tests. + +# Cleanup + +DROP TABLE t1; +DROP TABLE t2; + +# This test uses a stored function that uses user-defined variables to return data +# The test ensures the value of the user-defined variable is replicated correctly +# and in the correct order of assignment. +# This test was constructed for BUG#20141 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +--enable_warnings + +CREATE TABLE t1 (i INT); + +# Create two functions. One simply returns the user-defined variable. The other +# returns a value based on the user-defined variable. + +CREATE FUNCTION f1() RETURNS INT RETURN @a; +DELIMITER |; +CREATE FUNCTION f2() RETURNS INT +BEGIN + INSERT INTO t1 VALUES (10 + @a); + RETURN 0; +END| +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set the variable and execute the functions. + +SET @a:=123; +SELECT f1(), f2(); + +--echo On master: Check to see that data was inserted correctly + +INSERT INTO t1 VALUES(f1()); +SELECT * FROM t1; + +sync_slave_with_master; + +--echo On slave: Check the table for correct data and it matches master + +SELECT * FROM t1; + +connection master; + +# Cleanup + +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE t1; + +sync_slave_with_master; stop slave; -# End of 4.1 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index 1ef77208469..99640e9185c 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4223,7 +4223,14 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command, user_var_entry *var_entry; var_entry= get_variable(&thd->user_vars, name, 0); - if (!(opt_bin_log && is_update_query(sql_command))) + /* + Any reference to user-defined variable which is done from stored + function or trigger affects their execution and the execution of the + calling statement. We must log all such variables even if they are + not involved in table-updating statements. + */ + if (!(opt_bin_log && + (is_update_query(sql_command) || thd->in_sub_stmt))) { *out_entry= var_entry; return 0; diff --git a/sql/log.cc b/sql/log.cc index 1961a5b6f88..ba46490b100 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1577,13 +1577,13 @@ bool MYSQL_LOG::flush_and_sync() return err; } -void MYSQL_LOG::start_union_events(THD *thd) +void MYSQL_LOG::start_union_events(THD *thd, query_id_t query_id_param) { DBUG_ASSERT(!thd->binlog_evt_union.do_union); thd->binlog_evt_union.do_union= TRUE; thd->binlog_evt_union.unioned_events= FALSE; thd->binlog_evt_union.unioned_events_trans= FALSE; - thd->binlog_evt_union.first_query_id= thd->query_id; + thd->binlog_evt_union.first_query_id= query_id_param; } void MYSQL_LOG::stop_union_events(THD *thd) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index de0edabda3e..d1a39422c7f 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1464,8 +1464,24 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, binlog_save_options= thd->options; if (need_binlog_call) { + query_id_t q; reset_dynamic(&thd->user_var_events); - mysql_bin_log.start_union_events(thd); + /* + In case of artificially constructed events for function calls + we have separate union for each such event and hence can't use + query_id of real calling statement as the start of all these + unions (this will break logic of replication of user-defined + variables). So we use artifical value which is guaranteed to + be greater than all query_id's of all statements belonging + to previous events/unions. + Possible alternative to this is logging of all function invocations + as one select and not resetting THD::user_var_events before + each invocation. + */ + VOID(pthread_mutex_lock(&LOCK_thread_count)); + q= ::query_id; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + mysql_bin_log.start_union_events(thd, q + 1); } /* diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0794d4c797a..8e1a0c5f1e3 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2051,6 +2051,10 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, if (!lex->requires_prelocking() || is_update_query(lex->sql_command)) options&= ~OPTION_BIN_LOG; + + if ((backup->options & OPTION_BIN_LOG) && is_update_query(lex->sql_command)) + mysql_bin_log.start_union_events(this, this->query_id); + /* Disable result sets */ client_capabilities &= ~CLIENT_MULTI_RESULTS; in_sub_stmt|= new_state; @@ -2097,6 +2101,9 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) sent_row_count= backup->sent_row_count; client_capabilities= backup->client_capabilities; + if ((options & OPTION_BIN_LOG) && is_update_query(lex->sql_command)) + mysql_bin_log.stop_union_events(this); + /* The following is added to the old values as we are interested in the total complexity of the query diff --git a/sql/sql_class.h b/sql/sql_class.h index 05034ebd573..05ea9ac215e 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -311,7 +311,7 @@ public: bool write(Log_event* event_info); // binary log write bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event); - void start_union_events(THD *thd); + void start_union_events(THD *thd, query_id_t query_id_param); void stop_union_events(THD *thd); bool is_query_in_union(THD *thd, query_id_t query_id_param); From aafc285d75c5bbba3afca5bb717aa7b290ac2047 Mon Sep 17 00:00:00 2001 From: "lars/lthalmann@mysql.com/dl145j.mysql.com" <> Date: Sun, 25 Feb 2007 00:31:26 +0100 Subject: [PATCH 09/15] log_on needed also in embedded server --- sql/sql_insert.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index cd5f424fc14..613b28faf63 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -381,11 +381,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, List_item *values; Name_resolution_context *context; Name_resolution_context_state ctx_state; -#ifndef EMBEDDED_LIBRARY char *query= thd->query; bool log_on= (thd->options & OPTION_BIN_LOG) || (!(thd->security_ctx->master_access & SUPER_ACL)); -#endif thr_lock_type lock_type = table_list->lock_type; Item *unused_conds= 0; DBUG_ENTER("mysql_insert"); From 8f97dea5f8c28b69e4492b71b923b9fe6382f845 Mon Sep 17 00:00:00 2001 From: "bar@mysql.com" <> Date: Mon, 26 Feb 2007 15:25:43 +0400 Subject: [PATCH 10/15] Bug#24478 DROP TRIGGER is not caught by replicate-*-table filters Problem: DROP TRIGGER was not properly handled in combination with slave filters, which made replication stop Fix: loading table name before checking slave filters when dropping a trigger. --- mysql-test/r/rpl_replicate_do.result | 34 ++++++++++++++++++++++++++++ mysql-test/t/rpl_replicate_do.test | 32 ++++++++++++++++++++++++++ sql/sql_parse.cc | 25 ++++++++++++++++++++ sql/sql_trigger.cc | 6 +---- sql/sql_trigger.h | 4 ++++ 5 files changed, 96 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/rpl_replicate_do.result b/mysql-test/r/rpl_replicate_do.result index f79f6305342..9bacffb0609 100644 --- a/mysql-test/r/rpl_replicate_do.result +++ b/mysql-test/r/rpl_replicate_do.result @@ -41,3 +41,37 @@ select * from t1; ts 2005-08-12 00:00:00 drop table t1; +*** master *** +create table t1 (a int, b int); +create trigger trg1 before insert on t1 for each row set new.b=2; +create table t2 (a int, b int); +create trigger trg2 before insert on t2 for each row set new.b=2; +show tables; +Tables_in_test +t1 +t2 +show triggers; +Trigger Event Table Statement Timing Created sql_mode Definer +trg1 INSERT t1 set new.b=2 BEFORE NULL root@localhost +trg2 INSERT t2 set new.b=2 BEFORE NULL root@localhost +*** slave *** +show tables; +Tables_in_test +t1 +show triggers; +Trigger Event Table Statement Timing Created sql_mode Definer +trg1 INSERT t1 set new.b=2 BEFORE NULL root@localhost +*** master *** +drop trigger trg1; +drop trigger trg2; +show triggers; +Trigger Event Table Statement Timing Created sql_mode Definer +*** slave *** +show tables; +Tables_in_test +t1 +show triggers; +Trigger Event Table Statement Timing Created sql_mode Definer +*** master *** +drop table t1; +drop table t2; diff --git a/mysql-test/t/rpl_replicate_do.test b/mysql-test/t/rpl_replicate_do.test index 9dec8c06c79..0e95d71514b 100644 --- a/mysql-test/t/rpl_replicate_do.test +++ b/mysql-test/t/rpl_replicate_do.test @@ -59,3 +59,35 @@ drop table t1; sync_slave_with_master; # End of 4.1 tests + +# +# Bug#24478 DROP TRIGGER is not caught by replicate-*-table filters +# +--echo *** master *** +connection master; +create table t1 (a int, b int); +create trigger trg1 before insert on t1 for each row set new.b=2; +create table t2 (a int, b int); +create trigger trg2 before insert on t2 for each row set new.b=2; +show tables; +show triggers; +sync_slave_with_master; +--echo *** slave *** +connection slave; +show tables; +show triggers; +--echo *** master *** +connection master; +drop trigger trg1; +drop trigger trg2; +show triggers; +sync_slave_with_master; +--echo *** slave *** +connection slave; +show tables; +show triggers; +--echo *** master *** +connection master; +drop table t1; +drop table t2; +sync_slave_with_master; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index b0b7afc6528..7226a8e3c86 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -33,6 +33,7 @@ #include "sp_head.h" #include "sp.h" #include "sp_cache.h" +#include "sql_trigger.h" #ifdef HAVE_OPENSSL /* @@ -2485,6 +2486,30 @@ mysql_execute_command(THD *thd) #ifdef HAVE_REPLICATION if (unlikely(thd->slave_thread)) { + if (lex->sql_command == SQLCOM_DROP_TRIGGER) + { + /* + When dropping a trigger, we need to load its table name + before checking slave filter rules. + */ + add_table_for_trigger(thd, thd->lex->spname, 1, &all_tables); + + if (!all_tables) + { + /* + If table name cannot be loaded, + it means the trigger does not exists possibly because + CREATE TRIGGER was previously skipped for this trigger + according to slave filtering rules. + Returning success without producing any errors in this case. + */ + DBUG_RETURN(0); + } + + // force searching in slave.cc:tables_ok() + all_tables->updating= 1; + } + /* Check if statment should be skipped because of slave filtering rules diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 3569733d064..0b648570b86 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -107,10 +107,6 @@ const LEX_STRING trg_event_type_names[]= }; -static int -add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, - TABLE_LIST ** table); - class Handle_old_incorrect_sql_modes_hook: public Unknown_key_hook { private: @@ -1183,7 +1179,7 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event, 1 Error */ -static int +int add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, TABLE_LIST **table) { diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 7e0fadfa677..8970986b931 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -139,3 +139,7 @@ private: extern const LEX_STRING trg_action_time_type_names[]; extern const LEX_STRING trg_event_type_names[]; + +int +add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, + TABLE_LIST **table); From d204dc2d216bb35cfa4f2337d7dd4faddf34dacb Mon Sep 17 00:00:00 2001 From: "gbichot@dl145h.mysql.com" <> Date: Mon, 26 Feb 2007 20:35:28 +0100 Subject: [PATCH 11/15] Fix for BUG#26050 "LOAD DATA INFILE breaks ACID"; the ok must be sent to the client only after the binlog write and engine commit. No testcase for this bug, as to reproduce it, we need to "kill -9" mysqld, which we cannot do in the testsuite. But, I tested by hand. --- sql/sql_load.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 5710f9c4c97..10124e5f5ff 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -353,7 +353,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, } sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted, (ulong) (info.records - info.copied), (ulong) thd->cuted_fields); - send_ok(thd,info.copied+info.deleted,0L,name); // on the slave thd->query is never initialized if (!thd->slave_thread) mysql_update_log.write(thd,thd->query,thd->query_length); @@ -378,6 +377,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, #endif /*!EMBEDDED_LIBRARY*/ if (transactional_table) error=ha_autocommit_or_rollback(thd,error); + + /* ok to client sent only after binlog write and engine commit */ + send_ok(thd, info.copied + info.deleted, 0L, name); err: if (thd->lock) { From d13c3b9456f98a1e2449ada9aec5129ab4da0b9e Mon Sep 17 00:00:00 2001 From: "cbell/Chuck@mysql_cab_desk." <> Date: Tue, 27 Feb 2007 10:32:45 -0500 Subject: [PATCH 12/15] BUG#20141 "User-defined variables are not replicated properly for SF/Triggers in SBR mode." BUG#14914 "SP: Uses of session variables in routines are not always replicated" BUG#25167 "Dupl. usage of user-variables in trigger/function is not replicated correctly" This patch corrects a minor error in the previous patch for BUG#20141. This patch corrects an errant code change to sp_head.cc. The comments for the first patch follow: User-defined variables used inside of stored functions/triggers in statements which did not update tables directly were not replicated. We also had problems with replication of user-defined variables which were used in triggers (or stored functions called from table-updating statements) more than once. This patch addresses the first issue by enabling logging of all references to user-defined variables in triggers/stored functions and not only references from table-updating statements. The second issue stemmed from the fact that for user-defined variables used from triggers or stored functions called from table-updating statements we were writing binlog events for each reference instead of only one event for the first reference. This problem is already solved for stored functions called from non-updating statements with help of "event unioning" mechanism. So the patch simply extends this mechanism to the case affected. It also fixes small problem in this mechanism which caused wrong logging of references to user-variables in cases when non-updating statement called several stored functions which used the same variable and some of these function calls were omitted from binlog as they were not updating any tables. --- sql/sp_head.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index cabdd07b711..24bdd2db5b6 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1477,7 +1477,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, each invocation. */ VOID(pthread_mutex_lock(&LOCK_thread_count)); - q= ::query_id; + q= global_query_id; VOID(pthread_mutex_unlock(&LOCK_thread_count)); mysql_bin_log.start_union_events(thd, q + 1); } From dd0c43d5fa36337c69750ff4deb7b34d560855de Mon Sep 17 00:00:00 2001 From: "bar@mysql.com" <> Date: Wed, 28 Feb 2007 17:06:57 +0400 Subject: [PATCH 13/15] Bug#15126 character_set_database is not replicated (LOAD DATA INFILE need it) This patch fixes problem that LOAD DATA could use different character sets when loading files on master and on slave sides: - Adding replication of thd->variables.collation_database - Adding optional character set clause into LOAD DATA Note, the second way, with explicit CHARACTER SET clause should be the recommended way to load data using an alternative character set. The old way, using "SET @@character_set_database=xxx" should be gradually depricated. --- mysql-test/r/mysqlbinlog.result | 58 +++++++++++++++++++++++++++++++ mysql-test/r/rpl_loaddata2.result | 37 ++++++++++++++++++++ mysql-test/std_data/loaddata6.dat | 1 + mysql-test/t/mysqlbinlog.test | 24 +++++++++++++ mysql-test/t/rpl_loaddata2.test | 33 ++++++++++++++++++ sql/log_event.cc | 48 ++++++++++++++++++++++--- sql/log_event.h | 5 ++- sql/sql_class.cc | 1 + sql/sql_class.h | 1 + sql/sql_load.cc | 3 +- sql/sql_yacc.yy | 7 ++++ 11 files changed, 212 insertions(+), 6 deletions(-) create mode 100644 mysql-test/r/rpl_loaddata2.result create mode 100644 mysql-test/std_data/loaddata6.dat create mode 100644 mysql-test/t/rpl_loaddata2.test diff --git a/mysql-test/r/mysqlbinlog.result b/mysql-test/r/mysqlbinlog.result index b2571ec5d12..0f34259d11d 100644 --- a/mysql-test/r/mysqlbinlog.result +++ b/mysql-test/r/mysqlbinlog.result @@ -270,3 +270,61 @@ call p1(); 1 drop procedure p1; drop table t1, t2, t03, t04, t3, t4, t5; +flush logs; +create table t1 (a varchar(64) character set utf8); +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=latin1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=latin1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8r; +select hex(a) from t1; +hex(a) +C3BF +D0AA +C3BF +C3BF +D0AA +C3BF +D0AA +drop table t1; +flush logs; +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; +DELIMITER /*!*/; +use test/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=1, @@session.unique_checks=1/*!*/; +SET @@session.sql_mode=0/*!*/; +/*!\C latin1 *//*!*/; +SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/; +create table t1 (a varchar(64) character set utf8)/*!*/; +SET TIMESTAMP=1000000000/*!*/; +load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-6-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.collation_database=7/*!*/; +load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-7-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.collation_database=DEFAULT/*!*/; +load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-8-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-9-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.collation_database=7/*!*/; +load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-a-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.collation_database=DEFAULT/*!*/; +load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-b-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-c-0' INTO table t1 character set koi8r/*!*/; +SET TIMESTAMP=1000000000/*!*/; +drop table t1/*!*/; +DELIMITER ; +# End of log file +ROLLBACK /* added by mysqlbinlog */; +/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; diff --git a/mysql-test/r/rpl_loaddata2.result b/mysql-test/r/rpl_loaddata2.result new file mode 100644 index 00000000000..929d06e74cf --- /dev/null +++ b/mysql-test/r/rpl_loaddata2.result @@ -0,0 +1,37 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +create table t1 (a varchar(10) character set utf8); +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=DEFAULT; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=DEFAULT; +load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8r; +select hex(a) from t1; +hex(a) +C3BF +D0AA +C3BF +C3BF +C3BF +D0AA +D0AA +select hex(a) from t1; +hex(a) +C3BF +D0AA +C3BF +C3BF +C3BF +D0AA +D0AA +drop table t1; diff --git a/mysql-test/std_data/loaddata6.dat b/mysql-test/std_data/loaddata6.dat new file mode 100644 index 00000000000..29e181ebb88 --- /dev/null +++ b/mysql-test/std_data/loaddata6.dat @@ -0,0 +1 @@ + diff --git a/mysql-test/t/mysqlbinlog.test b/mysql-test/t/mysqlbinlog.test index b2bda247cd7..11ddc0d5a01 100644 --- a/mysql-test/t/mysqlbinlog.test +++ b/mysql-test/t/mysqlbinlog.test @@ -179,4 +179,28 @@ drop procedure p1; # clean up drop table t1, t2, t03, t04, t3, t4, t5; +# +# Bug#15126 character_set_database is not replicated +# (LOAD DATA INFILE need it) +# + +flush logs; +create table t1 (a varchar(64) character set utf8); +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=latin1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=latin1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8r; +select hex(a) from t1; +drop table t1; +flush logs; +--exec $MYSQL_BINLOG --short-form $MYSQLTEST_VARDIR/log/master-bin.000011 + # End of 5.0 tests + diff --git a/mysql-test/t/rpl_loaddata2.test b/mysql-test/t/rpl_loaddata2.test new file mode 100644 index 00000000000..7f2389cb9f6 --- /dev/null +++ b/mysql-test/t/rpl_loaddata2.test @@ -0,0 +1,33 @@ +# +# Check LOAD DATA + character sets + replication +# +source include/master-slave.inc; + +# +# Bug#15126 character_set_database is not replicated +# (LOAD DATA INFILE need it) +# +connection master; +create table t1 (a varchar(10) character set utf8); +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=DEFAULT; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=DEFAULT; +load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8r; + +select hex(a) from t1; + +save_master_pos; +connection slave; +sync_with_master; + +select hex(a) from t1; +connection master; +drop table t1; +sync_slave_with_master; diff --git a/sql/log_event.cc b/sql/log_event.cc index a14cd79461d..daf7fdeab2c 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1088,7 +1088,8 @@ bool Query_log_event::write(IO_CACHE* file) 1+4+ // code of autoinc and the 2 autoinc variables 1+6+ // code of charset and charset 1+1+MAX_TIME_ZONE_NAME_LENGTH+ // code of tz and tz length and tz name - 1+2 // code of lc_time_names and lc_time_names_number + 1+2+ // code of lc_time_names and lc_time_names_number + 1+2 // code of charset_database and charset_database_number ], *start, *start_of_status; ulong event_length; @@ -1207,6 +1208,13 @@ bool Query_log_event::write(IO_CACHE* file) int2store(start, lc_time_names_number); start+= 2; } + if (charset_database_number) + { + DBUG_ASSERT(charset_database_number <= 0xFFFF); + *start++= Q_CHARSET_DATABASE_CODE; + int2store(start, charset_database_number); + start+= 2; + } /* Here there could be code like if (command-line-option-which-says-"log_this_variable" && inited) @@ -1272,7 +1280,8 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, sql_mode(thd_arg->variables.sql_mode), auto_increment_increment(thd_arg->variables.auto_increment_increment), auto_increment_offset(thd_arg->variables.auto_increment_offset), - lc_time_names_number(thd_arg->variables.lc_time_names->number) + lc_time_names_number(thd_arg->variables.lc_time_names->number), + charset_database_number(0) { time_t end_time; time(&end_time); @@ -1280,6 +1289,9 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, catalog_len = (catalog) ? (uint32) strlen(catalog) : 0; /* status_vars_len is set just before writing the event */ db_len = (db) ? (uint32) strlen(db) : 0; + if (thd_arg->variables.collation_database != thd_arg->db_charset) + charset_database_number= thd_arg->variables.collation_database->number; + /* If we don't use flags2 for anything else than options contained in thd->options, it would be more efficient to flags2=thd_arg->options @@ -1350,7 +1362,7 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, db(NullS), catalog_len(0), status_vars_len(0), flags2_inited(0), sql_mode_inited(0), charset_inited(0), auto_increment_increment(1), auto_increment_offset(1), - time_zone_len(0), lc_time_names_number(0) + time_zone_len(0), lc_time_names_number(0), charset_database_number(0) { ulong data_len; uint32 tmp; @@ -1455,6 +1467,10 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, lc_time_names_number= uint2korr(pos); pos+= 2; break; + case Q_CHARSET_DATABASE_CODE: + charset_database_number= uint2korr(pos); + pos+= 2; + break; default: /* That's why you must write status vars in growing order of code */ DBUG_PRINT("info",("Query_log_event has unknown status vars (first has\ @@ -1652,6 +1668,16 @@ void Query_log_event::print_query_header(FILE* file, lc_time_names_number, print_event_info->delimiter); print_event_info->lc_time_names_number= lc_time_names_number; } + if (charset_database_number != print_event_info->charset_database_number) + { + if (charset_database_number) + fprintf(file, "SET @@session.collation_database=%d%s\n", + charset_database_number, print_event_info->delimiter); + else + fprintf(file, "SET @@session.collation_database=DEFAULT%s\n", + print_event_info->delimiter); + print_event_info->charset_database_number= charset_database_number; + } } @@ -1817,7 +1843,21 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli, } else thd->variables.lc_time_names= &my_locale_en_US; - + if (charset_database_number) + { + CHARSET_INFO *cs; + if (!(cs= get_charset(charset_database_number, MYF(0)))) + { + char buf[20]; + int10_to_str((int) charset_database_number, buf, -10); + my_error(ER_UNKNOWN_COLLATION, MYF(0), buf); + goto compare_errors; + } + thd->variables.collation_database= cs; + } + else + thd->variables.collation_database= thd->db_charset; + /* Execute the query (note that we bypass dispatch_command()) */ mysql_parse(thd, thd->query, thd->query_length); diff --git a/sql/log_event.h b/sql/log_event.h index d3ebe6860f5..c08306e30d2 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -272,6 +272,7 @@ struct sql_ex_info #define Q_LC_TIME_NAMES_CODE 7 +#define Q_CHARSET_DATABASE_CODE 8 /* Intvar event post-header */ #define I_TYPE_OFFSET 0 @@ -509,10 +510,11 @@ typedef struct st_print_event_info char charset[6]; // 3 variables, each of them storable in 2 bytes char time_zone_str[MAX_TIME_ZONE_NAME_LENGTH]; uint lc_time_names_number; + uint charset_database_number; st_print_event_info() :flags2_inited(0), sql_mode_inited(0), auto_increment_increment(1),auto_increment_offset(1), charset_inited(0), - lc_time_names_number(0) + lc_time_names_number(0), charset_database_number(0) { /* Currently we only use static PRINT_EVENT_INFO objects, so zeroed at @@ -797,6 +799,7 @@ public: uint time_zone_len; /* 0 means uninited */ const char *time_zone_str; uint lc_time_names_number; /* 0 means en_US */ + uint charset_database_number; #ifndef MYSQL_CLIENT diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0794d4c797a..a020c570cb6 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -902,6 +902,7 @@ sql_exchange::sql_exchange(char *name,bool flag) enclosed= line_start= &my_empty_string; line_term= &default_line_term; escaped= &default_escaped; + cs= NULL; } bool select_send::send_fields(List &list, uint flags) diff --git a/sql/sql_class.h b/sql/sql_class.h index 05034ebd573..817a033a526 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1687,6 +1687,7 @@ public: bool opt_enclosed; bool dumpfile; ulong skip_lines; + CHARSET_INFO *cs; sql_exchange(char *name,bool dumpfile_flag); }; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 0e4057d9ae4..d971eae56c0 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -313,7 +313,8 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, info.handle_duplicates=handle_duplicates; info.escape_char=escaped->length() ? (*escaped)[0] : INT_MAX; - READ_INFO read_info(file,tot_length,thd->variables.collation_database, + READ_INFO read_info(file,tot_length, + ex->cs ? ex->cs : thd->variables.collation_database, *field_term,*ex->line_start, *ex->line_term, *enclosed, info.escape_char, read_file_from_client, is_fifo); if (read_info.error) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 92640ea58d6..8843423c352 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -992,6 +992,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); old_or_new_charset_name_or_default collation_name collation_name_or_default + opt_load_data_charset %type internal_variable_name @@ -3262,6 +3263,10 @@ charset_name_or_default: charset_name { $$=$1; } | DEFAULT { $$=NULL; } ; +opt_load_data_charset: + /* Empty */ { $$= NULL; } + | charset charset_name_or_default { $$= $2; } + ; old_or_new_charset_name: ident_or_text @@ -7214,6 +7219,8 @@ load_data: lex->update_list.empty(); lex->value_list.empty(); } + opt_load_data_charset + { Lex->exchange->cs= $12; } opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec opt_load_data_set_spec {} From c48a5e8bd143a54c170de535a1f2f3e0f0ffeb29 Mon Sep 17 00:00:00 2001 From: "bar@mysql.com" <> Date: Wed, 28 Feb 2007 17:32:18 +0400 Subject: [PATCH 14/15] After merge fix --- mysql-test/r/mysqlbinlog.result | 1 - mysql-test/t/mysqlbinlog.test | 2 -- 2 files changed, 3 deletions(-) diff --git a/mysql-test/r/mysqlbinlog.result b/mysql-test/r/mysqlbinlog.result index 412b4ad3b85..8ca2422443d 100644 --- a/mysql-test/r/mysqlbinlog.result +++ b/mysql-test/r/mysqlbinlog.result @@ -272,7 +272,6 @@ call p1(); 1 1 drop procedure p1; -drop table t1, t2, t03, t04, t3, t4, t5; flush logs; create table t1 (a varchar(64) character set utf8); load data infile '../std_data_ln/loaddata6.dat' into table t1; diff --git a/mysql-test/t/mysqlbinlog.test b/mysql-test/t/mysqlbinlog.test index 1545e7f1d99..fe86d850267 100644 --- a/mysql-test/t/mysqlbinlog.test +++ b/mysql-test/t/mysqlbinlog.test @@ -189,8 +189,6 @@ drop procedure p1; --exec $MYSQL_BINLOG --version 2>&1 > /dev/null --exec $MYSQL_BINLOG --help 2>&1 > /dev/null --enable_query_log -# clean up -drop table t1, t2, t03, t04, t3, t4, t5; # # Bug#15126 character_set_database is not replicated From 233d4fb214c939dadd0efbfebfd3f75f86a18678 Mon Sep 17 00:00:00 2001 From: "bar@mysql.com" <> Date: Wed, 28 Feb 2007 18:13:23 +0400 Subject: [PATCH 15/15] Additional test changes for Bug N 15126 character_set_database is not replicated (LOAD DATA INFILE need it) Positions of some binlog events were changed because of additional logging of @@collation_database. --- mysql-test/r/ctype_cp932_binlog.result | 18 +++++++++--------- mysql-test/r/ctype_cp932_notembedded.result | 6 +++--- mysql-test/t/ctype_cp932_binlog.test | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/ctype_cp932_binlog.result b/mysql-test/r/ctype_cp932_binlog.result index d3d800b7bf0..ff295961008 100644 --- a/mysql-test/r/ctype_cp932_binlog.result +++ b/mysql-test/r/ctype_cp932_binlog.result @@ -8,9 +8,9 @@ SET @var1= x'8300'; EXECUTE stmt1 USING @var1; SHOW BINLOG EVENTS FROM 98; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 98 Query 1 185 use `test`; CREATE TABLE t1(f1 blob) -master-bin.000001 185 User var 1 224 @`var1`=_binary 0x8300 COLLATE binary -master-bin.000001 224 Query 1 317 use `test`; INSERT INTO t1 VALUES(@'var1') +master-bin.000001 98 Query 1 188 use `test`; CREATE TABLE t1(f1 blob) +master-bin.000001 188 User var 1 227 @`var1`=_binary 0x8300 COLLATE binary +master-bin.000001 227 Query 1 323 use `test`; INSERT INTO t1 VALUES(@'var1') SELECT HEX(f1) FROM t1; HEX(f1) 8300 @@ -30,17 +30,17 @@ HEX(s1) HEX(s2) d 466F6F2773206120426172 ED40ED41ED42 47.93 DROP PROCEDURE bug18293| DROP TABLE t4| -SHOW BINLOG EVENTS FROM 393| +SHOW BINLOG EVENTS FROM 402| Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 393 Query 1 556 use `test`; CREATE TABLE t4 (s1 CHAR(50) CHARACTER SET latin1, +master-bin.000001 402 Query 1 568 use `test`; CREATE TABLE t4 (s1 CHAR(50) CHARACTER SET latin1, s2 CHAR(50) CHARACTER SET cp932, d DECIMAL(10,2)) -master-bin.000001 556 Query 1 801 use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE bug18293 (IN ins1 CHAR(50), +master-bin.000001 568 Query 1 816 use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE bug18293 (IN ins1 CHAR(50), IN ins2 CHAR(50) CHARACTER SET cp932, IN ind DECIMAL(10,2)) BEGIN INSERT INTO t4 VALUES (ins1, ins2, ind); END -master-bin.000001 801 Query 1 1017 use `test`; INSERT INTO t4 VALUES ( NAME_CONST('ins1',_latin1 0x466F6F2773206120426172), NAME_CONST('ins2',_cp932 0xED40ED41ED42), NAME_CONST('ind',47.93)) -master-bin.000001 1017 Query 1 1103 use `test`; DROP PROCEDURE bug18293 -master-bin.000001 1103 Query 1 1179 use `test`; DROP TABLE t4 +master-bin.000001 816 Query 1 1035 use `test`; INSERT INTO t4 VALUES ( NAME_CONST('ins1',_latin1 0x466F6F2773206120426172), NAME_CONST('ins2',_cp932 0xED40ED41ED42), NAME_CONST('ind',47.93)) +master-bin.000001 1035 Query 1 1124 use `test`; DROP PROCEDURE bug18293 +master-bin.000001 1124 Query 1 1203 use `test`; DROP TABLE t4 diff --git a/mysql-test/r/ctype_cp932_notembedded.result b/mysql-test/r/ctype_cp932_notembedded.result index d04fce7738c..c58bfc65454 100644 --- a/mysql-test/r/ctype_cp932_notembedded.result +++ b/mysql-test/r/ctype_cp932_notembedded.result @@ -8,9 +8,9 @@ SET @var1= x'8300'; EXECUTE stmt1 USING @var1; SHOW BINLOG EVENTS FROM 98; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 98 Query 1 185 use `test`; CREATE TABLE t1(f1 blob) -master-bin.000001 185 User var 1 224 @`var1`=_binary 0x8300 COLLATE binary -master-bin.000001 224 Query 1 317 use `test`; INSERT INTO t1 VALUES(@'var1') +master-bin.000001 98 Query 1 188 use `test`; CREATE TABLE t1(f1 blob) +master-bin.000001 188 User var 1 227 @`var1`=_binary 0x8300 COLLATE binary +master-bin.000001 227 Query 1 323 use `test`; INSERT INTO t1 VALUES(@'var1') SELECT HEX(f1) FROM t1; HEX(f1) 8300 diff --git a/mysql-test/t/ctype_cp932_binlog.test b/mysql-test/t/ctype_cp932_binlog.test index 3bbbe94f0e3..ee0e588fdae 100644 --- a/mysql-test/t/ctype_cp932_binlog.test +++ b/mysql-test/t/ctype_cp932_binlog.test @@ -51,7 +51,7 @@ CALL bug18293("Foo's a Bar", _cp932 0xED40ED41ED42, 47.93)| SELECT HEX(s1),HEX(s2),d FROM t4| DROP PROCEDURE bug18293| DROP TABLE t4| -SHOW BINLOG EVENTS FROM 393| +SHOW BINLOG EVENTS FROM 402| delimiter ;| # End of 5.0 tests