From 3791e35f79e640f7bd2e20c895692ed1ec81e720 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 24 May 2007 15:35:43 +0500 Subject: [PATCH] Fix for bug #26842: master binary log contains invalid queries - replication fails bug #12826: Possible to get inconsistent slave using SQL syntax Prepared Statements Problem: binlogging PS' we may produce syntacticly incorrect queries in the binlog replacing some parameters with variable names (instead of variable values). E.g. in the reported case of "limit ?" clause: replacing "?" with "@var" produces "limit @var" which is not a correct SQL syntax. Also it may lead to different query execution on slave if we set and use a variable in the same statement, e.g. "insert into t1 values (@x:=@x+1, ?)" Fix: make the stored statement string created upon its execution use variable values (instead of names) to fill placeholders. mysql-test/r/ctype_cp932_binlog.result: Fix for bug #26842: master binary log contains invalid queries - replication fails bug #12826: Possible to get inconsistent slave using SQL syntax Prepared Statements - result adjusted. mysql-test/r/ctype_cp932_notembedded.result: Fix for bug #26842: master binary log contains invalid queries - replication fails bug #12826: Possible to get inconsistent slave using SQL syntax Prepared Statements - result adjusted. mysql-test/r/rpl_user_variables.result: Fix for bug #26842: master binary log contains invalid queries - replication fails bug #12826: Possible to get inconsistent slave using SQL syntax Prepared Statements - test result. mysql-test/t/ctype_cp932_binlog.test: Fix for bug #26842: master binary log contains invalid queries - replication fails bug #12826: Possible to get inconsistent slave using SQL syntax Prepared Statements - test adjusted. mysql-test/t/rpl_user_variables.test: Fix for bug #26842: master binary log contains invalid queries - replication fails bug #12826: Possible to get inconsistent slave using SQL syntax Prepared Statements - test case. sql/sql_prepare.cc: Fix for bug #26842: master binary log contains invalid queries - replication fails bug #12826: Possible to get inconsistent slave using SQL syntax Prepared Statements - set val to the variable's value (escaped if needed) then insert it into the query string in the position of the placeholder. We don't need to call get_var_with_binlog() here as there is no trace of the variable's name in the binlog. --- mysql-test/r/ctype_cp932_binlog.result | 15 +++---- mysql-test/r/ctype_cp932_notembedded.result | 3 +- mysql-test/r/rpl_user_variables.result | 40 ++++++++++++++++-- mysql-test/t/ctype_cp932_binlog.test | 2 +- mysql-test/t/rpl_user_variables.test | 47 ++++++++++++++++++--- sql/sql_prepare.cc | 37 ++++++---------- 6 files changed, 98 insertions(+), 46 deletions(-) diff --git a/mysql-test/r/ctype_cp932_binlog.result b/mysql-test/r/ctype_cp932_binlog.result index ff295961008..3385cd20ec4 100644 --- a/mysql-test/r/ctype_cp932_binlog.result +++ b/mysql-test/r/ctype_cp932_binlog.result @@ -9,8 +9,7 @@ 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 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') +master-bin.000001 188 Query 1 283 use `test`; INSERT INTO t1 VALUES(0x8300) SELECT HEX(f1) FROM t1; HEX(f1) 8300 @@ -30,17 +29,17 @@ HEX(s1) HEX(s2) d 466F6F2773206120426172 ED40ED41ED42 47.93 DROP PROCEDURE bug18293| DROP TABLE t4| -SHOW BINLOG EVENTS FROM 402| +SHOW BINLOG EVENTS FROM 362| Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 402 Query 1 568 use `test`; CREATE TABLE t4 (s1 CHAR(50) CHARACTER SET latin1, +master-bin.000001 362 Query 1 528 use `test`; CREATE TABLE t4 (s1 CHAR(50) CHARACTER SET latin1, s2 CHAR(50) CHARACTER SET cp932, d DECIMAL(10,2)) -master-bin.000001 568 Query 1 816 use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE bug18293 (IN ins1 CHAR(50), +master-bin.000001 528 Query 1 776 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 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 +master-bin.000001 776 Query 1 995 use `test`; INSERT INTO t4 VALUES ( NAME_CONST('ins1',_latin1 0x466F6F2773206120426172), NAME_CONST('ins2',_cp932 0xED40ED41ED42), NAME_CONST('ind',47.93)) +master-bin.000001 995 Query 1 1084 use `test`; DROP PROCEDURE bug18293 +master-bin.000001 1084 Query 1 1163 use `test`; DROP TABLE t4 diff --git a/mysql-test/r/ctype_cp932_notembedded.result b/mysql-test/r/ctype_cp932_notembedded.result index c58bfc65454..241fa0d1db7 100644 --- a/mysql-test/r/ctype_cp932_notembedded.result +++ b/mysql-test/r/ctype_cp932_notembedded.result @@ -9,8 +9,7 @@ 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 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') +master-bin.000001 188 Query 1 283 use `test`; INSERT INTO t1 VALUES(0x8300) SELECT HEX(f1) FROM t1; HEX(f1) 8300 diff --git a/mysql-test/r/rpl_user_variables.result b/mysql-test/r/rpl_user_variables.result index 05f2b68042e..26ac2b26aaa 100644 --- a/mysql-test/r/rpl_user_variables.result +++ b/mysql-test/r/rpl_user_variables.result @@ -253,10 +253,44 @@ SELECT * from t2; k 100 42 +drop table t1, t2; +reset master; +create table t1 (a int); +prepare s from "insert into t1 values (@a),(?)"; +set @a=98; +execute s using @a; +prepare s from "insert into t1 values (?)"; +set @a=99; +execute s using @a; +prepare s from "insert into t1 select 100 limit ?"; +set @a=100; +execute s using @a; +show binlog events from 98; +Log_name Pos Event_type Server_id End_log_pos Info +slave-bin.000001 98 Query 1 184 use `test`; create table t1 (a int) +slave-bin.000001 184 User var 2 226 @`a`=98 +slave-bin.000001 226 Query 1 320 use `test`; insert into t1 values (@a),(98) +slave-bin.000001 320 Query 1 409 use `test`; insert into t1 values (99) +slave-bin.000001 409 Query 1 507 use `test`; insert into t1 select 100 limit 100 +select * from t1; +a +98 +98 +99 +100 +drop table t1; +create table t1(a int, b int); +prepare s1 from 'insert into t1 values (@x:=@x+1, ?)'; +set @x=1; +execute s1 using @x; +select * from t1; +a b +2 1 +select * from t1; +a b +2 1 +drop table t1; 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); diff --git a/mysql-test/t/ctype_cp932_binlog.test b/mysql-test/t/ctype_cp932_binlog.test index ee0e588fdae..443036910d0 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 402| +SHOW BINLOG EVENTS FROM 362| delimiter ;| # End of 5.0 tests diff --git a/mysql-test/t/rpl_user_variables.test b/mysql-test/t/rpl_user_variables.test index b2f4bfbb29d..8f8f0accbd1 100644 --- a/mysql-test/t/rpl_user_variables.test +++ b/mysql-test/t/rpl_user_variables.test @@ -296,21 +296,55 @@ SELECT * from t1; SELECT * from t2; connection master; +drop table t1, t2; + +# +# Bug #26842: master binary log contains invalid queries - replication fails +# +save_master_pos; +connection slave; +sync_with_master; +reset master; + +connection master; +create table t1 (a int); +prepare s from "insert into t1 values (@a),(?)"; +set @a=98; execute s using @a; +prepare s from "insert into t1 values (?)"; +set @a=99; execute s using @a; +prepare s from "insert into t1 select 100 limit ?"; +set @a=100; execute s using @a; + +save_master_pos; +connection slave; +sync_with_master; +show binlog events from 98; +select * from t1; +connection master; +drop table t1; + +# +# Bug #12826: Possible to get inconsistent slave using SQL syntax Prepared Statements +# +connection master; +create table t1(a int, b int); +prepare s1 from 'insert into t1 values (@x:=@x+1, ?)'; +set @x=1; execute s1 using @x; +select * from t1; +sync_slave_with_master; +connection slave; +select * from t1; +connection master; +drop table t1; --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 @@ -358,4 +392,3 @@ DROP TABLE t1; sync_slave_with_master; stop slave; - diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 42655608196..d2f90141fa6 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -969,6 +969,7 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, String buf; const String *val; uint32 length= 0; + THD *thd= stmt->thd; DBUG_ENTER("insert_params_from_vars"); @@ -979,34 +980,20 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, { Item_param *param= *it; varname= var_it++; - if (get_var_with_binlog(stmt->thd, stmt->lex->sql_command, - *varname, &entry)) - DBUG_RETURN(1); - if (param->set_from_user_var(stmt->thd, entry)) + entry= (user_var_entry *) hash_search(&thd->user_vars, (byte*) varname->str, + varname->length); + /* + We have to call the setup_one_conversion_function() here to set + the parameter's members that might be needed further + (e.g. value.cs_info.character_set_client is used in the query_val_str()). + */ + setup_one_conversion_function(thd, param, param->param_type); + if (param->set_from_user_var(thd, entry)) DBUG_RETURN(1); - /* Insert @'escaped-varname' instead of parameter in the query */ - if (entry) - { - char *start, *ptr; - buf.length(0); - if (buf.reserve(entry->name.length*2+3)) - DBUG_RETURN(1); + val= param->query_val_str(&buf); - start= ptr= buf.c_ptr_quick(); - *ptr++= '@'; - *ptr++= '\''; - ptr+= escape_string_for_mysql(&my_charset_utf8_general_ci, - ptr, 0, entry->name.str, - entry->name.length); - *ptr++= '\''; - buf.length(ptr - start); - val= &buf; - } - else - val= &my_null_string; - - if (param->convert_str_value(stmt->thd)) + if (param->convert_str_value(thd)) DBUG_RETURN(1); /* out of memory */ if (query->replace(param->pos_in_query+length, 1, *val))