Skip to content

Commit 892760d

Browse files
committed
Bug#12601974 - STORED PROCEDURE SQL_MODE=NO_BACKSLASH_ESCAPES IGNORED AND BREAKS REPLICATION
Analysis: ======================== sql_mode "NO_BACKSLASH_ESCAPES": When user want to use backslash as character input, instead of escape character in a string literal then sql_mode can be set to "NO_BACKSLASH_ESCAPES". With this mode enabled, backslash becomes an ordinary character like any other. SQL_MODE set applies to the current client session. And while creating the stored procedure, MySQL stores the current sql_mode and always executes the stored procedure in sql_mode stored with the Procedure, regardless of the server SQL mode in effect when the routine is invoked. In the scenario (for which bug is reported), the routine is created with sql_mode=NO_BACKSLASH_ESCAPES. And routine is executed with the invoker sql_mode is "" (NOT SET) by executing statement "call testp('Axel\'s')". Since invoker sql_mode is "" (NOT_SET), the '\' in 'Axel\'s'(argument to function) is considered as escape character and column "a" (of table "t1") values are updated with "Axel's". The binary log generated for above update operation is as below, set sql_mode=XXXXXX (for no_backslash_escapes) update test.t1 set a= NAME_CONST('var',_latin1'Axel\'s' COLLATE 'latin1_swedish_ci'); While logging stored procedure statements, the local variables (params) used in statements are replaced with the NAME_CONST(var_name, var_value) (Internal function) (http://dev.mysql.com/doc/refman/5.6/en/miscellaneous-functions.html#function_name-const) On slave, these logs are applied. NAME_CONST is parsed to get the variable and its value. Since, stored procedure is created with sql_mode="NO_BACKSLASH_ESCAPES", the sql_mode is also logged in. So that at slave this sql_mode is set before executing the statements of routine. So at slave, sql_mode is set to "NO_BACKSLASH_ESCAPES" and then while parsing NAME_CONST of string variable, '\' is considered as NON ESCAPE character and parsing reported error for "'" (as we have only one "'" no backslash). At slave, parsing was proper with sql_mode "NO_BACKSLASH_ESCAPES". But above error reported while writing bin log, "'" (of Axel's) is escaped with "\" character. Actually, all special characters (n, r, ', ", \, 0...) are escaped while writing NAME_CONST for string variable(param, local variable) in bin log irrespective of "NO_BACKSLASH_ESCAPES" sql_mode. So, basically, the problem is that logging string parameter does not take into account sql_mode value. Fix: ======================== So when sql_mode is set to "NO_BACKSLASH_ESCAPES", escaping characters as (n, r, ', ", \, 0...) should be avoided. To do so, added a check to not to escape such characters while writing NAME_CONST for string variables in bin log. And when sql_mode is set to NO_BACKSLASH_ESCAPES, quote character "'" is represented as ''. http://dev.mysql.com/doc/refman/5.6/en/string-literals.html (There are several ways to include quote characters within a string: )
2 parents 26ed79e + 9af695f commit 892760d

File tree

10 files changed

+596
-13
lines changed

10 files changed

+596
-13
lines changed

mysql-test/r/sql_mode.result

+204
Original file line numberDiff line numberDiff line change
@@ -527,3 +527,207 @@ SELECT * FROM mysql.columns_priv WHERE Host = 'localhost' AND User LIKE 'user_%P
527527
Host Db User Table_name Column_name Timestamp Column_priv
528528
DROP TABLE t1;
529529
DROP TABLE t2;
530+
531+
#
532+
# Test for Bug#12601974 - STORED PROCEDURE SQL_MODE=NO_BACKSLASH_ESCAPES
533+
# IGNORED AND BREAKS REPLICATION
534+
#
535+
DROP TABLE IF EXISTS test_table;
536+
DROP FUNCTION IF EXISTS test_function;
537+
CREATE TABLE test_table (c1 CHAR(50));
538+
SET @org_mode=@@sql_mode;
539+
SET @@sql_mode='';
540+
PREPARE insert_stmt FROM 'INSERT INTO test_table VALUES (?)';
541+
PREPARE update_stmt FROM 'UPDATE test_table SET c1= ? WHERE c1= ?';
542+
CREATE FUNCTION test_function(var CHAR(50)) RETURNS CHAR(50)
543+
BEGIN
544+
DECLARE char_val CHAR(50);
545+
SELECT c1 INTO char_val FROM test_table WHERE c1=var;
546+
RETURN char_val;
547+
END
548+
$
549+
SET @var1='abcd\'ef';
550+
SET @var2='abcd\"ef';
551+
SET @var3='abcd\bef';
552+
SET @var4='abcd\nef';
553+
SET @var5='abcd\ref';
554+
SET @var6='abcd\tef';
555+
SET @var7='abcd\\ef';
556+
SET @var8='abcd\%ef';
557+
SET @var9='abcd\_ef';
558+
SET @to_var1='wxyz\'ef';
559+
SET @to_var2='wxyz\"ef';
560+
SET @to_var3='wxyz\bef';
561+
SET @to_var4='wxyz\nef';
562+
SET @to_var5='wxyz\ref';
563+
SET @to_var6='wxyz\tef';
564+
SET @to_var7='wxyz\\ef';
565+
SET @to_var8='wxyz\%ef';
566+
SET @to_var9='wxyz\_ef';
567+
# STRING LILTERAL WITH BACKSLASH IN PREPARE STATEMENT
568+
EXECUTE insert_stmt USING @var1;
569+
EXECUTE insert_stmt USING @var2;
570+
EXECUTE insert_stmt USING @var3;
571+
EXECUTE insert_stmt USING @var4;
572+
EXECUTE insert_stmt USING @var5;
573+
EXECUTE insert_stmt USING @var6;
574+
EXECUTE insert_stmt USING @var7;
575+
EXECUTE insert_stmt USING @var8;
576+
EXECUTE insert_stmt USING @var9;
577+
SELECT * FROM test_table;
578+
c1
579+
abcd'ef
580+
abcd"ef
581+
abcdef
582+
abcd
583+
ef
584+
abcdef
585+
abcd ef
586+
abcd\ef
587+
abcd\%ef
588+
abcd\_ef
589+
EXECUTE update_stmt USING @to_var1, @var1;
590+
EXECUTE update_stmt USING @to_var2, @var2;
591+
EXECUTE update_stmt USING @to_var3, @var3;
592+
EXECUTE update_stmt USING @to_var4, @var4;
593+
EXECUTE update_stmt USING @to_var5, @var5;
594+
EXECUTE update_stmt USING @to_var6, @var6;
595+
EXECUTE update_stmt USING @to_var7, @var7;
596+
EXECUTE update_stmt USING @to_var8, @var8;
597+
EXECUTE update_stmt USING @to_var9, @var9;
598+
SELECT * FROM test_table;
599+
c1
600+
wxyz'ef
601+
wxyz"ef
602+
wxyzef
603+
wxyz
604+
ef
605+
wxyzef
606+
wxyz ef
607+
wxyz\ef
608+
wxyz\%ef
609+
wxyz\_ef
610+
611+
# END OF CASE - STRING LILTERAL WITH BACKSLASH IN PREPARE STATEMENT
612+
# STRING LILTERAL WITH BACKSLASH IN FUNCTION RETURNING STRING
613+
select test_function(@to_var1);
614+
test_function(@to_var1)
615+
wxyz'ef
616+
SELECT test_function(@to_var2);
617+
test_function(@to_var2)
618+
wxyz"ef
619+
SELECT test_function(@to_var3);
620+
test_function(@to_var3)
621+
wxyzef
622+
SELECT test_function(@to_var4);
623+
test_function(@to_var4)
624+
wxyz
625+
ef
626+
SELECT test_function(@to_var5);
627+
test_function(@to_var5)
628+
wxyzef
629+
SELECT test_function(@to_var6);
630+
test_function(@to_var6)
631+
wxyz ef
632+
SELECT test_function(@to_var7);
633+
test_function(@to_var7)
634+
wxyz\ef
635+
SELECT test_function(@to_var8);
636+
test_function(@to_var8)
637+
wxyz\%ef
638+
SELECT test_function(@to_var9);
639+
test_function(@to_var9)
640+
wxyz\_ef
641+
642+
# END OF CASE - STRING LILTERAL WITH BACKSLASH IN FUNCTION RETURNING STRING
643+
DELETE FROM test_table;
644+
DROP FUNCTION test_function;
645+
SET @@sql_mode='NO_BACKSLASH_ESCAPES';
646+
CREATE FUNCTION test_function(var CHAR(50)) RETURNS CHAR(50)
647+
BEGIN
648+
DECLARE char_val CHAR(50);
649+
SELECT c1 INTO char_val FROM test_table WHERE c1=var;
650+
RETURN char_val;
651+
END
652+
$
653+
# STRING LILTERAL WITH BACKSLASH IN PREPARE STATEMENT
654+
EXECUTE insert_stmt USING @var1;
655+
EXECUTE insert_stmt USING @var2;
656+
EXECUTE insert_stmt USING @var3;
657+
EXECUTE insert_stmt USING @var4;
658+
EXECUTE insert_stmt USING @var5;
659+
EXECUTE insert_stmt USING @var6;
660+
EXECUTE insert_stmt USING @var7;
661+
EXECUTE insert_stmt USING @var8;
662+
EXECUTE insert_stmt USING @var9;
663+
SELECT * FROM test_table;
664+
c1
665+
abcd'ef
666+
abcd"ef
667+
abcdef
668+
abcd
669+
ef
670+
abcdef
671+
abcd ef
672+
abcd\ef
673+
abcd\%ef
674+
abcd\_ef
675+
EXECUTE update_stmt USING @to_var1, @var1;
676+
EXECUTE update_stmt USING @to_var2, @var2;
677+
EXECUTE update_stmt USING @to_var3, @var3;
678+
EXECUTE update_stmt USING @to_var4, @var4;
679+
EXECUTE update_stmt USING @to_var5, @var5;
680+
EXECUTE update_stmt USING @to_var6, @var6;
681+
EXECUTE update_stmt USING @to_var7, @var7;
682+
EXECUTE update_stmt USING @to_var8, @var8;
683+
EXECUTE update_stmt USING @to_var9, @var9;
684+
SELECT * FROM test_table;
685+
c1
686+
wxyz'ef
687+
wxyz"ef
688+
wxyzef
689+
wxyz
690+
ef
691+
wxyzef
692+
wxyz ef
693+
wxyz\ef
694+
wxyz\%ef
695+
wxyz\_ef
696+
697+
# END OF CASE - STRING LILTERAL WITH BACKSLASH IN PREPARE STATEMENT
698+
# STRING LILTERAL WITH BACKSLASH IN FUNCTION RETURNING STRING
699+
select test_function(@to_var1);
700+
test_function(@to_var1)
701+
wxyz'ef
702+
SELECT test_function(@to_var2);
703+
test_function(@to_var2)
704+
wxyz"ef
705+
SELECT test_function(@to_var3);
706+
test_function(@to_var3)
707+
wxyzef
708+
SELECT test_function(@to_var4);
709+
test_function(@to_var4)
710+
wxyz
711+
ef
712+
SELECT test_function(@to_var5);
713+
test_function(@to_var5)
714+
wxyzef
715+
SELECT test_function(@to_var6);
716+
test_function(@to_var6)
717+
wxyz ef
718+
SELECT test_function(@to_var7);
719+
test_function(@to_var7)
720+
wxyz\ef
721+
SELECT test_function(@to_var8);
722+
test_function(@to_var8)
723+
wxyz\%ef
724+
SELECT test_function(@to_var9);
725+
test_function(@to_var9)
726+
wxyz\_ef
727+
728+
# END OF CASE - STRING LILTERAL WITH BACKSLASH IN FUNCTION RETURNING STRING
729+
DROP TABLE test_table;
730+
DROP FUNCTION test_function;
731+
SET @@sql_mode= @org_mode;
732+
733+
#End of Test for Bug#12601974

mysql-test/suite/binlog/r/binlog_sql_mode.result

+111
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,114 @@ DROP VIEW testView;
3838
DROP TABLE t1;
3939
SET @@global.sql_mode= @old_sql_mode;
4040
SET @@session.binlog_format=@old_binlog_format;
41+
42+
#
43+
# Test for Bug#12601974 - STORED PROCEDURE SQL_MODE=NO_BACKSLASH_ESCAPES
44+
# IGNORED AND BREAKS REPLICATION
45+
#
46+
DROP DATABASE IF EXISTS mysqltest_db;
47+
DROP TABLE IF EXISTS test_table;
48+
CREATE DATABASE mysqltest_db;
49+
USE mysqltest_db;
50+
CREATE TABLE test_table (c1 CHAR(50));
51+
SET @org_mode=@@sql_mode;
52+
SET @@sql_mode='';
53+
CREATE PROCEDURE proc_without_sql_mode (IN param1 CHAR(50), IN param2 CHAR(50))
54+
BEGIN
55+
DECLARE var1 CHAR(50) DEFAULT param1;
56+
DECLARE var2 CHAR(50) DEFAULT param2;
57+
DECLARE var3 CHAR(50) DEFAULT 'abcd\bef';
58+
DECLARE var4 CHAR(50) DEFAULT 'abcd\nef';
59+
DECLARE var5 CHAR(50) DEFAULT 'abcd\ref';
60+
DECLARE var6 CHAR(50) DEFAULT 'abcd\tef';
61+
DECLARE var7 CHAR(50) DEFAULT 'abcd\\ef';
62+
DECLARE var8 CHAR(50) DEFAULT 'abcd\%ef';
63+
DECLARE var9 CHAR(50) DEFAULT 'abcd\_ef';
64+
INSERT INTO test_table VALUES (var1);
65+
INSERT INTO test_table VALUES (var2);
66+
INSERT INTO test_table VALUES (var3);
67+
INSERT INTO test_table VALUES (var4);
68+
INSERT INTO test_table VALUES (var5);
69+
INSERT INTO test_table VALUES (var6);
70+
INSERT INTO test_table VALUES (var7);
71+
INSERT INTO test_table VALUES (var8);
72+
INSERT INTO test_table VALUES (var9);
73+
END
74+
$
75+
SET @@sql_mode='NO_BACKSLASH_ESCAPES'$
76+
CREATE PROCEDURE proc_with_sql_mode (IN param1 CHAR(50), IN param2 CHAR(50))
77+
BEGIN
78+
DECLARE var1 CHAR(50) DEFAULT param1;
79+
DECLARE var2 CHAR(50) DEFAULT param2;
80+
DECLARE var3 CHAR(50) DEFAULT 'wxyz\bef';
81+
DECLARE var4 CHAR(50) DEFAULT 'wxyz\nef';
82+
DECLARE var5 CHAR(50) DEFAULT 'wxyz\ref';
83+
DECLARE var6 CHAR(50) DEFAULT 'wxyz\tef';
84+
DECLARE var7 CHAR(50) DEFAULT 'wxyz\\ef';
85+
DECLARE var8 CHAR(50) DEFAULT 'wxyz\%ef';
86+
DECLARE var9 CHAR(50) DEFAULT 'wxyz\_ef';
87+
INSERT INTO test_table VALUES (var1);
88+
INSERT INTO test_table VALUES (var2);
89+
INSERT INTO test_table VALUES (var3);
90+
INSERT INTO test_table VALUES (var4);
91+
INSERT INTO test_table VALUES (var5);
92+
INSERT INTO test_table VALUES (var6);
93+
INSERT INTO test_table VALUES (var7);
94+
INSERT INTO test_table VALUES (var8);
95+
INSERT INTO test_table VALUES (var9);
96+
END
97+
$
98+
SET @@sql_mode='';
99+
CALL proc_without_sql_mode('abcd\'ef', 'abcd\"ef');
100+
CALL proc_with_sql_mode('wxyz\'ef', 'wxyz\"ef');
101+
SELECT * FROM test_table;
102+
c1
103+
abcd'ef
104+
abcd"ef
105+
abcdef
106+
abcd
107+
ef
108+
abcdef
109+
abcd ef
110+
abcd\ef
111+
abcd\%ef
112+
abcd\_ef
113+
wxyz'ef
114+
wxyz"ef
115+
wxyz\bef
116+
wxyz\nef
117+
wxyz\ref
118+
wxyz\tef
119+
wxyz\\ef
120+
wxyz\%ef
121+
wxyz\_ef
122+
"Dropping table test_table"
123+
DROP TABLE test_table;
124+
#"test_table" content after replaying the binlog
125+
SELECT * FROM test_table;
126+
c1
127+
abcd'ef
128+
abcd"ef
129+
abcdef
130+
abcd
131+
ef
132+
abcdef
133+
abcd ef
134+
abcd\ef
135+
abcd\%ef
136+
abcd\_ef
137+
wxyz'ef
138+
wxyz"ef
139+
wxyz\bef
140+
wxyz\nef
141+
wxyz\ref
142+
wxyz\tef
143+
wxyz\\ef
144+
wxyz\%ef
145+
wxyz\_ef
146+
#Clean up
147+
DROP DATABASE mysqltest_db;
148+
SET @@sql_mode= @org_mode;
149+
use test;
150+
151+
#End of Test for Bug#12601974

0 commit comments

Comments
 (0)