From cefc0b6dffcf3f7cd0e1ee7ebc6dd14eca622757 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 18 Feb 2006 17:19:16 +0100 Subject: [PATCH 1/3] Fix for BUG#16559 "Replication Problems with Non transactional tables inside an interrupted trans.": problem was: when a connection disconnects having an open transaction affecting MyISAM and InnoDB, the ROLLBACK event stored in the binary log contained a non-zero error code (1053 because of the disconnection), so when slave applied the transaction, slave complained that its ROLLBACK succeeded (error_code=0) while master's had 1053, so slave stopped. But internally generated binlog events such as this ROLLBACK should always have 0 as error code, as is true in 4.1 and was accidentally broken in 5.0, so that there is no false alarm. mysql-test/r/mix_innodb_myisam_binlog.result: result update mysql-test/t/mix_innodb_myisam_binlog.test: test for BUG#16559 sql/log.cc: Internally generated binlog events should always have an error code of zero (like in 4.1; in 5.0 this was accidentally broken). --- mysql-test/r/mix_innodb_myisam_binlog.result | 23 ++++++++++++++ mysql-test/t/mix_innodb_myisam_binlog.test | 33 +++++++++++++++++++- sql/log.cc | 6 +++- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/mix_innodb_myisam_binlog.result b/mysql-test/r/mix_innodb_myisam_binlog.result index c84bd65e748..2273b8c8756 100644 --- a/mysql-test/r/mix_innodb_myisam_binlog.result +++ b/mysql-test/r/mix_innodb_myisam_binlog.result @@ -256,3 +256,26 @@ master-bin.000001 1648 Query 1 # use `test`; create table t2 (n int) engine=inno master-bin.000001 1748 Query 1 # use `test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `test`.`t1`,`test`.`ti` do release_lock("lock1"); drop table t0,t2; +reset master; +create table t1 (a int) engine=innodb; +create table t2 (a int) engine=myisam; +select get_lock("a",10); +get_lock("a",10) +1 +begin; +insert into t1 values(8); +insert into t2 select * from t1; +select get_lock("a",10); +get_lock("a",10) +1 +select +(@a:=load_file("MYSQL_TEST_DIR/var/tmp/mix_innodb_myisam_binlog.output")) +is not null; +(@a:=load_file("MYSQL_TEST_DIR/var/tmp/mix_innodb_myisam_binlog.output")) +is not null +1 +select +@a like "%#%error_code=0%ROLLBACK;%ROLLBACK /* added by mysqlbinlog */;%", +@a not like "%#%error_code=%error_code=%"; +@a like "%#%error_code=0%ROLLBACK;%ROLLBACK /* added by mysqlbinlog */;%" @a not like "%#%error_code=%error_code=%" +1 1 diff --git a/mysql-test/t/mix_innodb_myisam_binlog.test b/mysql-test/t/mix_innodb_myisam_binlog.test index 658584b625e..7ba7b634b22 100644 --- a/mysql-test/t/mix_innodb_myisam_binlog.test +++ b/mysql-test/t/mix_innodb_myisam_binlog.test @@ -259,5 +259,36 @@ show binlog events from 98; do release_lock("lock1"); drop table t0,t2; - # End of 4.1 tests + +# Test for BUG#16559 (ROLLBACK should always have a zero error code in +# binlog). Has to be here and not earlier, as the SELECTs influence +# XIDs differently between normal and ps-protocol (and SHOW BINLOG +# EVENTS above read XIDs). + +connect (con4,localhost,root,,); +connection con3; +reset master; +create table t1 (a int) engine=innodb; +create table t2 (a int) engine=myisam; +select get_lock("a",10); +begin; +insert into t1 values(8); +insert into t2 select * from t1; +disconnect con3; + +connection con4; +select get_lock("a",10); # wait for rollback to finish + +# we check that the error code of the "ROLLBACK" event is 0 and not +# ER_SERVER_SHUTDOWN (i.e. disconnection just rolls back transaction +# and does not make slave to stop) +--exec $MYSQL_BINLOG --start-position=547 $MYSQL_TEST_DIR/var/log/master-bin.000001 > var/tmp/mix_innodb_myisam_binlog.output +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +eval select +(@a:=load_file("$MYSQL_TEST_DIR/var/tmp/mix_innodb_myisam_binlog.output")) +is not null; +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +eval select +@a like "%#%error_code=0%ROLLBACK;%ROLLBACK /* added by mysqlbinlog */;%", +@a not like "%#%error_code=%error_code=%"; diff --git a/sql/log.cc b/sql/log.cc index 6c37cb04c61..85e8c4dae2f 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -132,6 +132,7 @@ static int binlog_commit(THD *thd, bool all) DBUG_RETURN(0); } Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE); + qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE) DBUG_RETURN(binlog_end_trans(thd, trans_log, &qev)); } @@ -156,6 +157,7 @@ static int binlog_rollback(THD *thd, bool all) if (unlikely(thd->options & OPTION_STATUS_NO_TRANS_UPDATE)) { Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE); + qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE) error= binlog_end_trans(thd, trans_log, &qev); } else @@ -1826,7 +1828,9 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) Imagine this is rollback due to net timeout, after all statements of the transaction succeeded. Then we want a zero-error code in BEGIN. In other words, if there was a really serious error code it's already - in the statement's events. + in the statement's events, there is no need to put it also in this + internally generated event, and as this event is generated late it + would lead to false alarms. This is safer than thd->clear_error() against kills at shutdown. */ qinfo.error_code= 0; From 8470ae9cb1327ee5089b98a148e26bb207662fd7 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 18 Feb 2006 17:26:30 +0100 Subject: [PATCH 2/3] Fix for BUG#14769 "Function fails to replicate if fails half-way (slave stops)": if the function, invoked in a non-binlogged caller (e.g. SELECT, DO), failed half-way on the master, slave would stop and complain that error code between him and master mismatch. To solve this, when a stored function is invoked in a non-binlogged caller (e.g. SELECT, DO), we binlog the function call as SELECT instead of as DO (see revision comment of sp_head.cc for more). And: minor wording change in the help text. This cset will cause conflicts in 5.1, I'll merge. mysql-test/r/rpl_sp.result: result update mysql-test/t/rpl_sp-slave.opt: bug just fixed so option not needed mysql-test/t/rpl_sp.test: test for more half-failed functions with DO and SELECT, to test the bug of this changeset. cleanup at the end. sql/mysqld.cc: function -> stored function (change suggested by Paul) sql/sp_head.cc: When a function updates data and is called from a non-binlogged statement (SELECT, DO), we binlog it as SELECT myfunc(), and not DO myfunc() like before. --- mysql-test/r/rpl_sp.result | 21 +++++++++++++-------- mysql-test/t/rpl_sp-slave.opt | 2 +- mysql-test/t/rpl_sp.test | 12 +++++------- sql/mysqld.cc | 4 ++-- sql/sp_head.cc | 12 +++--------- 5 files changed, 24 insertions(+), 27 deletions(-) diff --git a/mysql-test/r/rpl_sp.result b/mysql-test/r/rpl_sp.result index 7180b677b8e..a42c33ce333 100644 --- a/mysql-test/r/rpl_sp.result +++ b/mysql-test/r/rpl_sp.result @@ -233,20 +233,25 @@ end @ # # delete from t2; alter table t2 add unique (a); drop function fn1; -create function fn1() +create function fn1(x int) returns int begin -insert into t2 values(20),(20); +insert into t2 values(x),(x); return 10; end| -select fn1(); +do fn1(100); +Warnings: +Error 1062 Duplicate entry '100' for key 1 +select fn1(20); ERROR 23000: Duplicate entry '20' for key 1 select * from t2; a 20 +100 select * from t2; a 20 +100 create trigger trg before insert on t1 for each row set new.a= 10; ERROR 42000: Access denied; you need the SUPER privilege for this operation delete from t1; @@ -324,7 +329,7 @@ insert into t1 values (x); return x+2; end master-bin.000001 # Query 1 # use `mysqltest1`; delete t1,t2 from t1,t2 -master-bin.000001 # Query 1 # use `mysqltest1`; DO `fn1`(20) +master-bin.000001 # Query 1 # use `mysqltest1`; SELECT `fn1`(20) master-bin.000001 # Query 1 # use `mysqltest1`; insert into t2 values(fn1(21)) master-bin.000001 # Query 1 # use `mysqltest1`; drop function fn1 master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1() @@ -351,13 +356,14 @@ end master-bin.000001 # Query 1 # use `mysqltest1`; delete from t2 master-bin.000001 # Query 1 # use `mysqltest1`; alter table t2 add unique (a) master-bin.000001 # Query 1 # use `mysqltest1`; drop function fn1 -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1() +master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1(x int) returns int begin -insert into t2 values(20),(20); +insert into t2 values(x),(x); return 10; end -master-bin.000001 # Query 1 # use `mysqltest1`; DO `fn1`() +master-bin.000001 # Query 1 # use `mysqltest1`; SELECT `fn1`(100) +master-bin.000001 # Query 1 # use `mysqltest1`; SELECT `fn1`(20) master-bin.000001 # Query 1 # use `mysqltest1`; delete from t1 master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` trigger trg before insert on t1 for each row set new.a= 10 master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values (1) @@ -415,4 +421,3 @@ col test DROP PROCEDURE p1; drop table t1; -reset master; diff --git a/mysql-test/t/rpl_sp-slave.opt b/mysql-test/t/rpl_sp-slave.opt index 611ee1f33be..709a224fd92 100644 --- a/mysql-test/t/rpl_sp-slave.opt +++ b/mysql-test/t/rpl_sp-slave.opt @@ -1 +1 @@ ---log_bin_trust_routine_creators=0 --slave-skip-errors=1062 +--log_bin_trust_routine_creators=0 diff --git a/mysql-test/t/rpl_sp.test b/mysql-test/t/rpl_sp.test index c0eaf6f817f..8be17be3822 100644 --- a/mysql-test/t/rpl_sp.test +++ b/mysql-test/t/rpl_sp.test @@ -294,21 +294,19 @@ alter table t2 add unique (a); drop function fn1; delimiter |; -create function fn1() +create function fn1(x int) returns int begin - insert into t2 values(20),(20); + insert into t2 values(x),(x); return 10; end| delimiter ;| -# Because of BUG#14769 the following statement requires that we start -# slave with --slave-skip-errors=1062. When that bug is fixed, that -# option can be removed. +do fn1(100); --error 1062 -select fn1(); +select fn1(20); select * from t2; sync_slave_with_master; @@ -440,4 +438,4 @@ DROP PROCEDURE p1; # cleanup connection master; drop table t1; -reset master; +sync_slave_with_master; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c19fdcfde82..4c32d7f9de5 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4923,8 +4923,8 @@ Disable with --skip-innodb-doublewrite.", (gptr*) &innobase_use_doublewrite, */ {"log-bin-trust-function-creators", OPT_LOG_BIN_TRUST_FUNCTION_CREATORS, "If equal to 0 (the default), then when --log-bin is used, creation of " - "a function is allowed only to users having the SUPER privilege and only " - "if this function may not break binary logging.", + "a stored function is allowed only to users having the SUPER privilege and" + " only if this function may not break binary logging.", (gptr*) &trust_function_creators, (gptr*) &trust_function_creators, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"log-error", OPT_ERROR_LOG_FILE, "Error log file.", diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 27dc0103335..aff773a32bc 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -736,13 +736,7 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b) Statements that have is_update_query(stmt) == FALSE (e.g. SELECTs) are not written into binary log. Instead we catch function calls the statement makes and write it into binary log separately (see #3). - - We actually can easily write SELECT statements into the binary log in the - right order (we don't have issues with const tables being unlocked early - because SELECTs that use FUNCTIONs unlock all tables at once) We don't do - it because replication slave thread currently can't execute SELECT - statements. Fixing this is on the TODO. - + 2. PROCEDURE calls CALL statements are not written into binary log. Instead @@ -763,7 +757,7 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b) function execution (grep for start_union_events and stop_union_events) If the answers are No and Yes, we write the function call into the binary - log as "DO spfunc(, , ...)" + log as "SELECT spfunc(, , ...)". 4. Miscellaneous issues. @@ -1310,7 +1304,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, char buf[256]; String bufstr(buf, sizeof(buf), &my_charset_bin); bufstr.length(0); - bufstr.append(STRING_WITH_LEN("DO ")); + bufstr.append(STRING_WITH_LEN("SELECT ")); append_identifier(thd, &bufstr, m_name.str, m_name.length); bufstr.append('('); for (uint i=0; i < argcount; i++) From 8dc567dbed7ab9c6489d0b2f2c72d723c87c68e5 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 18 Feb 2006 17:32:15 +0100 Subject: [PATCH 3/3] Fix for BUG#13897 "failure to do SET SQL_MODE=N where N is a number > 31" (the original bug's title isn't the simplest symptom). sys_var::check_set() was wrong. mysqlbinlog makes use of such SET SQL_MODE=N (where N is interpreted like if SQL_MODE was a field of type SET), so this bug affected recovery from binlogs if the server was running with certain SQL_MODE values, for example the default values on Windows (STRICT_TRANS_TABLES); to work around this bug people had to edit mysqlbinlog's output. mysql-test/r/sql_mode.result: result update mysql-test/t/sql_mode.test: test for various numeric SQL_MODE values sql/set_var.cc: For a set, it does not make sense to test if the supplied argument exceeds the number of elements in the set (such test would make sense for an enum), but rather to check if it exceeds 2^this (to verify that only reasonable bits are set). --- mysql-test/r/sql_mode.result | 16 ++++++++++++++++ mysql-test/t/sql_mode.test | 9 +++++++++ sql/set_var.cc | 7 ++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/sql_mode.result b/mysql-test/r/sql_mode.result index 7d9d7fb333c..66df424919b 100644 --- a/mysql-test/r/sql_mode.result +++ b/mysql-test/r/sql_mode.result @@ -478,4 +478,20 @@ v1 CREATE ALGORITHM=UNDEFINED DEFINER="root"@"localhost" SQL SECURITY DEFINER VI create view v2 as select a from t2 where a in (select a from v1); drop view v2, v1; drop table t1, t2; +select @@sql_mode; +@@sql_mode +ANSI_QUOTES +set sql_mode=2097152; +select @@sql_mode; +@@sql_mode +STRICT_TRANS_TABLES +set sql_mode=16384+(65536*4); +select @@sql_mode; +@@sql_mode +REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,NO_TABLE_OPTIONS,ANSI +set sql_mode=2147483648; +ERROR 42000: Variable 'sql_mode' can't be set to the value of '2147483648' +select @@sql_mode; +@@sql_mode +REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,NO_TABLE_OPTIONS,ANSI SET @@SQL_MODE=@OLD_SQL_MODE; diff --git a/mysql-test/t/sql_mode.test b/mysql-test/t/sql_mode.test index 8ae9ced6c68..b67f1a73db6 100644 --- a/mysql-test/t/sql_mode.test +++ b/mysql-test/t/sql_mode.test @@ -255,4 +255,13 @@ create view v2 as select a from t2 where a in (select a from v1); drop view v2, v1; drop table t1, t2; +select @@sql_mode; +set sql_mode=2097152; +select @@sql_mode; +set sql_mode=16384+(65536*4); +select @@sql_mode; +--error 1231 +set sql_mode=2147483648; # that mode does not exist +select @@sql_mode; + SET @@SQL_MODE=@OLD_SQL_MODE; diff --git a/sql/set_var.cc b/sql/set_var.cc index 02216fb2113..7be79ab59f0 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1659,7 +1659,12 @@ bool sys_var::check_set(THD *thd, set_var *var, TYPELIB *enum_names) else { ulonglong tmp= var->value->val_int(); - if (tmp >= enum_names->count) + /* + For when the enum is made to contain 64 elements, as 1ULL<<64 is + undefined, we guard with a "count<64" test. + */ + if (unlikely((tmp >= ((ULL(1)) << enum_names->count)) && + (enum_names->count < 64))) { llstr(tmp, buff); goto err;