Skip to content

Commit 1755ea4

Browse files
committed
MDEV-25444: mysql --binary-mode is not able to replay some mysqlbinlog outputs
Changes on top of Sachin’s patch. Specifically: 1) Refined the parsing break condition to only change the parser’s behavior for parsing strings in binary mode (behavior of \0 outside of strings is unchanged). 2) Prefixed binary_zero_insert.test with ‘mysql_’ to more clearly associate the purpose of the test. 3) As the input of the test contains binary zeros (0x5c00), different text editors can visualize this sequence differently, and Github would not display it at all. Therefore, the input itself was consolidated into the test and created out of hex sequences to make it easier to understand what is happening. 4) Extended test to validate that the rows which correspond to the INSERTS with 0x5c00 have the correct binary zero data. Reviewed By: =========== Andrei Elkin <andrei.elkin@mariadb.com>
1 parent 10cd281 commit 1755ea4

File tree

6 files changed

+232
-26
lines changed

6 files changed

+232
-26
lines changed

client/mysql.cc

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2319,11 +2319,14 @@ static bool add_line(String &buffer, char *line, size_t line_length,
23192319
{
23202320
// Found possbile one character command like \c
23212321

2322-
inchar = (uchar) *++pos;
2323-
// In Binary mode , when in_string is not null \0 should not be treated as
2324-
// end statement. This can happen when we are in middle of binary data which
2325-
// can contain \0 and its quoted with ' '.
2326-
if (!real_binary_mode && !*in_string && !inchar)
2322+
/*
2323+
The null-terminating character (ASCII '\0') marks the end of user
2324+
input. Then, by default, upon encountering a '\0' while parsing, it
2325+
should stop. However, some data naturally contains binary zeros
2326+
(e.g., zipped files). Real_binary_mode signals the parser to expect
2327+
'\0' within the data and not to end parsing if found.
2328+
*/
2329+
if (!(inchar = (uchar) *++pos) && (!real_binary_mode || !*in_string))
23272330
break; // readline adds one '\'
23282331
if (*in_string || inchar == 'N') // \N is short for NULL
23292332
{ // Don't allow commands in string

mysql-test/main/binary_zero_insert.result

Lines changed: 0 additions & 6 deletions
This file was deleted.

mysql-test/main/binary_zero_insert.test

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Note: This test assumes NO_BACKSLASH_ESCAPES is not set in SQL_MODE.
2+
##############################
3+
# Setup
4+
##############################
5+
#
6+
# Saving old state
7+
#
8+
set @old_sql_mode= @@global.SQL_MODE;
9+
set @@global.SQL_MODE= "";
10+
#
11+
# Create table for data entry
12+
#
13+
CREATE TABLE tb (`id` int(11) NOT NULL AUTO_INCREMENT,`cb` longblob DEFAULT NULL, PRIMARY KEY (`id`)) AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
14+
RESET MASTER;
15+
##############################
16+
# Test Case
17+
##############################
18+
#
19+
# \0 (0x5c00 in binary) should be allowed in data strings if
20+
# --binary-mode is enabled.
21+
#
22+
FOUND 10 /\x5c\x00/ in binary_zero_inserts.sql
23+
# MYSQL --binary-mode test < MYSQL_TMP_DIR/binary_zero_inserts.sql
24+
#
25+
# Ensure a row exists from each insert statement with a \0
26+
#
27+
SELECT COUNT(*)=8 from tb;
28+
COUNT(*)=8
29+
1
30+
#
31+
# Ensure that the binary zero was parsed and exists in the row data
32+
# Note: We only look for 00 because the 5c only served as an escape
33+
# in parsing.
34+
#
35+
# MYSQL_DUMP test tb --hex-blob | grep INSERT > MYSQL_TMP_DIR/dump.sql
36+
FOUND 10 /00/ in dump.sql
37+
#
38+
# Ensure data consistency on mysqlbinlog replay
39+
#
40+
FLUSH LOGS;
41+
# MYSQL_BINLOG MYSQLD_DATADIR/binlog_file > MYSQL_TMP_DIR/binlog_zeros.sql
42+
FOUND 10 /\x5c\x00/ in binlog_zeros.sql
43+
# MYSQL --binary-mode test < MYSQL_TMP_DIR/binlog_zeros.sql
44+
# Table checksum is equivalent before and after binlog replay
45+
#
46+
# A \0 should still be treated as end-of-query in binary mode.
47+
#
48+
# MYSQL --binary-mode -B test < MYSQL_TMP_DIR/binary_zero_eoq.sql
49+
##############################
50+
# Cleanup
51+
##############################
52+
SET @@global.sql_mode= @old_sql_mode;
53+
drop table tb;
54+
RESET MASTER;
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#
2+
# Purpose:
3+
# This test ensures that the mysql client is able to properly handle the
4+
# binary data sequence 0x5c00, i.e. the null-terminating character \0, in a
5+
# string when --binary-mode is enabled. Specifically, this sequence is valid to
6+
# appear anywhere within a binary data string, and it should not end the string
7+
# or SQL command. Additionally, \0 outside of a string should still end the
8+
# query.
9+
#
10+
# Methodology:
11+
# This test initially inserts data with binary strings containing \0. To
12+
# ensure the mysql client is able to process this data correctly, perl is used
13+
# to create a SQL file that contains \0 in strings, and this file is used as
14+
# input into the client. The row data is then validated by searching for binary
15+
# zeros in mysqldump output.
16+
#
17+
#
18+
# References:
19+
# MDEV-25444: mysql --binary-mode is not able to replay some mysqlbinlog
20+
# outputs
21+
22+
--echo # Note: This test assumes NO_BACKSLASH_ESCAPES is not set in SQL_MODE.
23+
24+
--source include/have_log_bin.inc
25+
26+
--echo ##############################
27+
--echo # Setup
28+
--echo ##############################
29+
30+
--echo #
31+
--echo # Saving old state
32+
--echo #
33+
set @old_sql_mode= @@global.SQL_MODE;
34+
set @@global.SQL_MODE= "";
35+
36+
--echo #
37+
--echo # Create table for data entry
38+
--echo #
39+
CREATE TABLE tb (`id` int(11) NOT NULL AUTO_INCREMENT,`cb` longblob DEFAULT NULL, PRIMARY KEY (`id`)) AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
40+
41+
# Will replay binlog later and we don't want to recreate the table
42+
RESET MASTER;
43+
44+
45+
--echo ##############################
46+
--echo # Test Case
47+
--echo ##############################
48+
49+
--echo #
50+
--echo # \0 (0x5c00 in binary) should be allowed in data strings if
51+
--echo # --binary-mode is enabled.
52+
--echo #
53+
--perl
54+
my $dir= $ENV{'MYSQL_TMP_DIR'};
55+
open (my $FILE, '>', "$dir/binary_zero_inserts.sql") or die "open(): $!";
56+
57+
print $FILE "TRUNCATE TABLE tb;\n";
58+
59+
# INSERT INTO tb(cb) VALUES(_binary '\0');
60+
print $FILE "INSERT INTO tb(cb) VALUES (_binary '";
61+
print $FILE pack "H*","5c00";
62+
print $FILE "');\n";
63+
64+
# INSERT INTO tb(cb) VALUES(_binary '\0A');
65+
print $FILE "INSERT INTO tb(cb) VALUES (_binary '";
66+
print $FILE pack "H*","5c0041";
67+
print $FILE "');\n";
68+
69+
# INSERT INTO tb(cb) VALUES(_binary 'A\0');
70+
print $FILE "INSERT INTO tb(cb) VALUES (_binary '";
71+
print $FILE pack "H*","415c00";
72+
print $FILE "');\n";
73+
74+
# INSERT INTO tb(cb) VALUES(_binary 'A\0B');
75+
print $FILE "INSERT INTO tb(cb) VALUES (_binary '";
76+
print $FILE pack "H*","415c0042";
77+
print $FILE "');\n";
78+
79+
# INSERT INTO tb(cb) VALUES(_binary '\0A\0');
80+
print $FILE "INSERT INTO tb(cb) VALUES (_binary '";
81+
print $FILE pack "H*","5c00415c00";
82+
print $FILE "');\n";
83+
84+
# INSERT INTO tb(cb) VALUES(_binary '\\\0');
85+
print $FILE "INSERT INTO tb(cb) VALUES (_binary '";
86+
print $FILE pack "H*","5c5c5c00";
87+
print $FILE "');\n";
88+
89+
# INSERT INTO tb(cb) VALUES(_binary '\0\0');
90+
print $FILE "INSERT INTO tb(cb) VALUES (_binary '";
91+
print $FILE pack "H*","5c005c00";
92+
print $FILE "');\n";
93+
94+
# INSERT INTO tb(cb) VALUES(_binary '\\0');
95+
print $FILE "INSERT INTO tb(cb) VALUES (_binary '";
96+
print $FILE pack "H*","5c5c00";
97+
print $FILE "');\n";
98+
99+
close ($FILE);
100+
EOF
101+
--let SEARCH_PATTERN= \x5c\x00
102+
--let SEARCH_FILE= $MYSQL_TMP_DIR/binary_zero_inserts.sql
103+
--source include/search_pattern_in_file.inc
104+
--echo # MYSQL --binary-mode test < MYSQL_TMP_DIR/binary_zero_inserts.sql
105+
--exec $MYSQL --binary-mode test < $MYSQL_TMP_DIR/binary_zero_inserts.sql
106+
107+
--echo #
108+
--echo # Ensure a row exists from each insert statement with a \0
109+
--echo #
110+
SELECT COUNT(*)=8 from tb;
111+
112+
--echo #
113+
--echo # Ensure that the binary zero was parsed and exists in the row data
114+
--echo # Note: We only look for 00 because the 5c only served as an escape
115+
--echo # in parsing.
116+
--echo #
117+
--echo # MYSQL_DUMP test tb --hex-blob | grep INSERT > MYSQL_TMP_DIR/dump.sql
118+
--exec $MYSQL_DUMP test tb --hex-blob | grep INSERT > $MYSQL_TMP_DIR/dump.sql
119+
--let SEARCH_PATTERN= 00
120+
--let SEARCH_FILE= $MYSQL_TMP_DIR/dump.sql
121+
--source include/search_pattern_in_file.inc
122+
123+
--echo #
124+
--echo # Ensure data consistency on mysqlbinlog replay
125+
--echo #
126+
--let $good_checksum= `CHECKSUM TABLE tb`
127+
let $MYSQLD_DATADIR= `SELECT @@datadir`;
128+
let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1);
129+
FLUSH LOGS;
130+
--echo # MYSQL_BINLOG MYSQLD_DATADIR/binlog_file > MYSQL_TMP_DIR/binlog_zeros.sql
131+
--exec $MYSQL_BINLOG $MYSQLD_DATADIR/$binlog_file > $MYSQL_TMP_DIR/binlog_zeros.sql
132+
--let SEARCH_PATTERN= \x5c\x00
133+
--let SEARCH_FILE= $MYSQL_TMP_DIR/binlog_zeros.sql
134+
--source include/search_pattern_in_file.inc
135+
--echo # MYSQL --binary-mode test < MYSQL_TMP_DIR/binlog_zeros.sql
136+
--exec $MYSQL --binary-mode test < $MYSQL_TMP_DIR/binlog_zeros.sql
137+
if ($good_checksum != `CHECKSUM TABLE tb`)
138+
{
139+
die "Blob with binary zero data changed after binary log replay";
140+
}
141+
--echo # Table checksum is equivalent before and after binlog replay
142+
143+
--echo #
144+
--echo # A \0 should still be treated as end-of-query in binary mode.
145+
--echo #
146+
--perl
147+
my $dir= $ENV{'MYSQL_TMP_DIR'};
148+
open (my $FILE, '>', "$dir/binary_zero_eoq.sql") or die "open(): $!";
149+
150+
# INSERT INTO tb(cb) VALUES(_binary 'text')\0
151+
print $FILE "INSERT INTO tb(cb) VALUES (_binary 'text')";
152+
print $FILE pack "H*","5c00";
153+
154+
close ($FILE);
155+
EOF
156+
--echo # MYSQL --binary-mode -B test < MYSQL_TMP_DIR/binary_zero_eoq.sql
157+
--exec $MYSQL --binary-mode -B test < $MYSQL_TMP_DIR/binary_zero_eoq.sql
158+
159+
160+
--echo ##############################
161+
--echo # Cleanup
162+
--echo ##############################
163+
164+
--remove_file $MYSQL_TMP_DIR/binary_zero_inserts.sql
165+
--remove_file $MYSQL_TMP_DIR/binary_zero_eoq.sql
166+
--remove_file $MYSQL_TMP_DIR/binlog_zeros.sql
167+
--remove_file $MYSQL_TMP_DIR/dump.sql
168+
SET @@global.sql_mode= @old_sql_mode;
169+
drop table tb;
170+
RESET MASTER;
-87 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)