Skip to content

Commit 6df5a61

Browse files
author
Rohit Kalhans
committed
BUG#11758263 50440: MARK UNORDERED UPDATE WITH AUTOINC UNSAFE
Problem: Statements that write to tables with auto_increment columns based on the selection from another table, may lead to master and slave going out of sync, as the order in which the rows are retrived from the table may differ on master and slave. Solution: We mark writing to a table with auto_increment table as unsafe. This will cause the execution of such statements to throw a warning and forces the statement to be logged in ROW if the logging format is mixed. Changes: 1. All the statements that writes to a table with auto_increment column(s) based on the rows fetched from another table, will now be unsafe. 2. CREATE TABLE with SELECT will now be unsafe.
1 parent c8d8a92 commit 6df5a61

27 files changed

+153
-26
lines changed

mysql-test/extra/rpl_tests/rpl_insert_id_pk.test

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
# We also check how the foreign_key_check variable is replicated
1111

1212
-- source include/master-slave.inc
13+
call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.");
14+
1315
#should work for both SBR and RBR
1416

1517
create table t1(a int auto_increment, primary key(a));
@@ -50,6 +52,7 @@ create table t2(b int auto_increment, c int, primary key(b));
5052
insert into t1 values (10);
5153
insert into t1 values (null),(null),(null);
5254
insert into t2 values (5,0);
55+
--disable_warnings ONCE
5356
insert into t2 (c) select * from t1 ORDER BY a;
5457
select * from t2 ORDER BY b;
5558
sync_slave_with_master;

mysql-test/extra/rpl_tests/rpl_multi_update.test

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
source include/master-slave.inc;
22

3+
call mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.');
4+
35
eval CREATE TABLE t1 (
46
a int unsigned not null auto_increment primary key,
57
b int unsigned
@@ -11,13 +13,15 @@ eval CREATE TABLE t2 (
1113
) ENGINE=$engine_type;
1214

1315
INSERT INTO t1 VALUES (NULL, 0);
16+
--disable_warnings ONCE
1417
INSERT INTO t1 SELECT NULL, 0 FROM t1;
1518

1619
INSERT INTO t2 VALUES (NULL, 0), (NULL,1);
1720

1821
SELECT * FROM t1 ORDER BY a;
1922
SELECT * FROM t2 ORDER BY a;
2023

24+
--disable_warnings ONCE
2125
UPDATE t1, t2 SET t1.b = t2.b WHERE t1.a = t2.a;
2226
sync_slave_with_master;
2327

mysql-test/extra/rpl_tests/rpl_multi_update2.test

+3
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@ eval CREATE TABLE t2 (
1717
) ENGINE=$engine_type;
1818

1919
INSERT INTO t1 VALUES (NULL, 0);
20+
21+
--disable_warnings ONCE
2022
INSERT INTO t1 SELECT NULL, 0 FROM t1;
2123

2224
INSERT INTO t2 VALUES (NULL, 0), (NULL,1);
2325

2426
SELECT * FROM t1 ORDER BY a;
2527
SELECT * FROM t2 ORDER BY a;
2628

29+
--disable_warnings ONCE
2730
UPDATE t1, t2 SET t1.b = (t2.b+4) WHERE t1.a = t2.a;
2831
SELECT * FROM t1 ORDER BY a;
2932
SELECT * FROM t2 ORDER BY a;

mysql-test/extra/rpl_tests/rpl_multi_update3.test

+3
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@ eval CREATE TABLE t2 (
1818
) ENGINE=$engine_type;
1919

2020
INSERT INTO t1 VALUES (NULL, 0);
21+
22+
--disable_warnings ONCE
2123
INSERT INTO t1 SELECT NULL, 0 FROM t1;
2224

2325
INSERT INTO t2 VALUES (NULL, 0), (NULL,1);
2426

2527
SELECT * FROM t1 ORDER BY a;
2628
SELECT * FROM t2 ORDER BY a;
2729

30+
--disable_warnings ONCE
2831
UPDATE t2, (SELECT a FROM t1 ORDER BY a) AS t SET t2.b = t.a+5 ;
2932
SELECT * FROM t1 ORDER BY a;
3033
SELECT * FROM t2 ORDER BY a;

mysql-test/r/ps.result

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
call mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.');
12
drop table if exists t1,t2,t3,t4;
23
drop database if exists client_test_db;
34
create table t1

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

+37-18
Large diffs are not rendered by default.

mysql-test/suite/binlog/t/binlog_unsafe.test

+1-1
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ while ($unsafe_type < 9) {
233233
--let $value_0=
234234
--let $sel_sidef_0=
235235
--let $sel_retval_0=
236-
--let $CRC_ARG_expected_number_of_warnings= 6
236+
--let $CRC_ARG_expected_number_of_warnings= 7
237237
}
238238

239239
if ($unsafe_type == 8) {

mysql-test/suite/rpl/r/rpl_auto_increment_11932.result

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
include/master-slave.inc
22
[connection master]
3+
call mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.');
34
drop database if exists test1;
45
create database test1;
56
use test1;

mysql-test/suite/rpl/r/rpl_insert_id_pk.result

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
include/master-slave.inc
22
[connection master]
3+
call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.");
34
create table t1(a int auto_increment, primary key(a));
45
create table t2(b int auto_increment, c int, primary key(b));
56
insert into t1 values (1),(2),(3);

mysql-test/suite/rpl/r/rpl_multi_update.result

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
include/master-slave.inc
22
[connection master]
3+
call mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.');
34
CREATE TABLE t1 (
45
a int unsigned not null auto_increment primary key,
56
b int unsigned

mysql-test/suite/rpl/r/rpl_multi_update2.result

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
include/master-slave.inc
22
[connection master]
3+
call mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT');
34
drop table if exists t1,t2;
45
CREATE TABLE t1 (
56
a int unsigned not null auto_increment primary key,

mysql-test/suite/rpl/r/rpl_multi_update3.result

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
include/master-slave.inc
22
[connection master]
3+
call mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.');
34

45
-------- Test for BUG#9361 --------
56
CREATE TABLE t1 (

mysql-test/suite/rpl/r/rpl_rotate_logs.result

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
CALL mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.");
12
start slave;
23
Got one of the listed errors
34
start slave;

mysql-test/suite/rpl/t/rpl_auto_increment_11932.test

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
# Test supplied by Are Casilla
99

1010
source include/master-slave.inc;
11+
call mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.');
1112
--disable_warnings
1213
connection master;
1314
drop database if exists test1;
@@ -42,12 +43,16 @@ CREATE PROCEDURE simpleproc3 ()
4243
$
4344
DELIMITER ;$
4445

46+
--disable_warnings
4547
CALL simpleproc3();
48+
--enable_warnings
4649

4750
select * from t2;
4851

4952
TRUNCATE TABLE `t1`;
53+
--disable_warnings
5054
CALL simpleproc3();
55+
--enable_warnings
5156

5257
select * from t1;
5358

mysql-test/suite/rpl/t/rpl_multi_update2.test

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#######################################################
77
--source include/not_ndb_default.inc
88
--source include/master-slave.inc
9+
call mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT');
910
let $engine_type=MyISAM;
1011
--source extra/rpl_tests/rpl_multi_update2.test
1112
--source include/rpl_end.inc

mysql-test/suite/rpl/t/rpl_multi_update3.test

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#######################################################
77
--source include/not_ndb_default.inc
88
--source include/master-slave.inc
9+
call mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.');
910
let $engine_type=MyISAM;
1011
-- source extra/rpl_tests/rpl_multi_update3.test
1112
--source include/rpl_end.inc

mysql-test/suite/rpl/t/rpl_optimize.test

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ enable_query_log;
1919

2020
create table t1 (a int not null auto_increment primary key, b int, key(b));
2121
INSERT INTO t1 (a) VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
22+
--disable_warnings
2223
INSERT INTO t1 (a) SELECT null FROM t1;
2324
INSERT INTO t1 (a) SELECT null FROM t1;
2425
INSERT INTO t1 (a) SELECT null FROM t1;
@@ -32,6 +33,7 @@ INSERT INTO t1 (a) SELECT null FROM t1;
3233
INSERT INTO t1 (a) SELECT null FROM t1;
3334
INSERT INTO t1 (a) SELECT null FROM t1;
3435
INSERT INTO t1 (a) SELECT null FROM t1;
36+
--enable_warnings
3537
save_master_pos;
3638
# a few updates to force OPTIMIZE to do something
3739
--disable_warnings

mysql-test/suite/rpl/t/rpl_rotate_logs.test

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ EOF
2727
chmod 0000 $MYSQLD_SLAVE_DATADIR/master.info;
2828
connection slave;
2929

30+
CALL mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.");
3031
# START SLAVE will fail because it can't read the file (mode 000)
3132
# (system error 13)
3233
--replace_result $MYSQL_TEST_DIR TESTDIR

mysql-test/suite/rpl/t/rpl_semi_sync_event.test

+2-1
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,10 @@ SET GLOBAL event_scheduler = ON;
5252
replace_result $engine_type ENGINE_TYPE;
5353
eval CREATE TABLE t1 (i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, f varchar(8)) ENGINE=$engine_type;
5454
INSERT INTO t1 (f) VALUES ('a'),('a'),('a'),('a'),('a');
55+
--disable_warnings
5556
INSERT INTO t1 SELECT i+5, f FROM t1;
5657
INSERT INTO t1 SELECT i+10, f FROM t1;
57-
58+
--enable_warnings
5859
CREATE EVENT ev1 ON SCHEDULE EVERY 1 SECOND
5960
DO INSERT INTO t1 VALUES (SLEEP(5),CONCAT('ev1_',CONNECTION_ID()));
6061
CREATE EVENT ev2 ON SCHEDULE EVERY 1 SECOND

mysql-test/suite/rpl/t/rpl_timezone.test

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
# timezone used in CONVERT_TZ is not binlogged. To debug (by Guilhem
1414
# and possibly Konstantin).
1515

16+
source include/master-slave.inc;
17+
1618
--disable_query_log
1719
CALL mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
1820
--enable_query_log
1921

2022
--disable_ps_protocol
2123

22-
source include/master-slave.inc;
23-
2424
# Save original timezone
2525
set @my_time_zone= @@global.time_zone;
2626

@@ -90,6 +90,7 @@ insert into t1 values ('20040101000000',NULL), ('20040611093902',NULL);
9090
# from originally inserted)
9191
#
9292
set time_zone='MET';
93+
--disable_warnings ONCE
9394
insert into t2 (select * from t1);
9495
SELECT * FROM t1 ORDER BY n;
9596
sync_slave_with_master;

mysql-test/t/multi_update.test

+1
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ create table `t2` (`c2_id` int(10) unsigned NULL auto_increment, `c2_p_id` int(1
352352
insert into t1 values (0,'A01-Comp',1);
353353
insert into t1 values (0,'B01-Comp',1);
354354
insert into t2 values (0,1,'A Note',1);
355+
--disable_warnings ONCE
355356
update t1 left join t2 on p_id = c2_p_id set c2_note = 'asdf-1' where p_id = 2;
356357
select * from t1;
357358
select * from t2;

mysql-test/t/ps.test

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
-- source include/not_embedded.inc
22
-- source include/have_log_bin.inc
3+
call mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.');
34
#
45
# SQL Syntax for Prepared Statements test
56
#
@@ -432,6 +433,7 @@ deallocate prepare stmt;
432433
create table t1 (a int);
433434
insert into t1 values (1),(2),(3);
434435
create table t2 select * from t1;
436+
--disable_warnings
435437
prepare stmt FROM 'create table t2 select * from t1';
436438
drop table t2;
437439
execute stmt;
@@ -441,6 +443,7 @@ execute stmt;
441443
execute stmt;
442444
drop table t2;
443445
execute stmt;
446+
--enable_warnings
444447
drop table t1,t2;
445448
deallocate prepare stmt;
446449

@@ -1176,13 +1179,15 @@ create database mysqltest character set utf8;
11761179
prepare stmt1 from "create table mysqltest.t1 (c char(10))";
11771180
prepare stmt2 from "create table mysqltest.t2 select 'test'";
11781181
execute stmt1;
1182+
--disable_warnings ONCE
11791183
execute stmt2;
11801184
show create table mysqltest.t1;
11811185
show create table mysqltest.t2;
11821186
drop table mysqltest.t1;
11831187
drop table mysqltest.t2;
11841188
alter database mysqltest character set latin1;
11851189
execute stmt1;
1190+
--disable_warnings ONCE
11861191
execute stmt2;
11871192
show create table mysqltest.t1;
11881193
show create table mysqltest.t2;

sql/share/errmsg-utf8.txt

+8
Original file line numberDiff line numberDiff line change
@@ -6486,3 +6486,11 @@ ER_PLUGIN_NO_UNINSTALL
64866486

64876487
ER_PLUGIN_NO_INSTALL
64886488
eng "Plugin '%s' is marked as not dynamically installable. You have to stop the server to install it."
6489+
6490+
6491+
ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT
6492+
eng "Statements writing to a table with an auto-increment column after selecting from another table are unsafe because the order in which rows are retrieved determines what (if any) rows will be written. This order cannot be predicted and may differ on master and the slave."
6493+
6494+
ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC
6495+
eng "CREATE TABLE... SELECT... on a table with an auto-increment column is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are inserted. This order cannot be predicted and may differ on master and the slave."
6496+

sql/sql_base.cc

+40-1
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list);
214214
static void free_cache_entry(TABLE *entry);
215215
static bool
216216
has_write_table_with_auto_increment(TABLE_LIST *tables);
217-
217+
static bool
218+
has_write_table_with_auto_increment_and_select(TABLE_LIST *tables);
218219

219220
uint cached_open_tables(void)
220221
{
@@ -5683,6 +5684,11 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
56835684
/* We have to emulate LOCK TABLES if we are statement needs prelocking. */
56845685
if (thd->lex->requires_prelocking())
56855686
{
5687+
5688+
if (thd->variables.binlog_format != BINLOG_FORMAT_ROW && tables &&
5689+
has_write_table_with_auto_increment_and_select(tables))
5690+
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_WRITE_AUTOINC_SELECT);
5691+
56865692
/*
56875693
A query that modifies autoinc column in sub-statement can make the
56885694
master and slave inconsistent.
@@ -9080,6 +9086,39 @@ has_write_table_with_auto_increment(TABLE_LIST *tables)
90809086
return 0;
90819087
}
90829088

9089+
/*
9090+
checks if the tables have select tables in the table list and write tables
9091+
with auto-increment column.
9092+
9093+
SYNOPSIS
9094+
has_two_write_locked_tables_with_auto_increment_and_select
9095+
tables Table list
9096+
9097+
RETURN VALUES
9098+
9099+
-true if the table list has atleast one table with auto-increment column
9100+
and atleast one table to select from.
9101+
-false otherwise
9102+
*/
9103+
9104+
static bool
9105+
has_write_table_with_auto_increment_and_select(TABLE_LIST *tables)
9106+
{
9107+
bool has_select= false;
9108+
bool has_auto_increment_tables = has_write_table_with_auto_increment(tables);
9109+
for(TABLE_LIST *table= tables; table; table= table->next_global)
9110+
{
9111+
if (!table->placeholder() &&
9112+
(table->lock_type <= TL_READ_NO_INSERT))
9113+
{
9114+
has_select= true;
9115+
break;
9116+
}
9117+
}
9118+
return(has_select && has_auto_increment_tables);
9119+
}
9120+
9121+
90839122

90849123
/*
90859124
Open and lock system tables for read.

sql/sql_lex.cc

+2
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@ Query_tables_list::binlog_stmt_unsafe_errcode[BINLOG_STMT_UNSAFE_COUNT] =
6161
ER_BINLOG_UNSAFE_MIXED_STATEMENT,
6262
ER_BINLOG_UNSAFE_INSERT_IGNORE_SELECT,
6363
ER_BINLOG_UNSAFE_INSERT_SELECT_UPDATE,
64+
ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT,
6465
ER_BINLOG_UNSAFE_REPLACE_SELECT,
6566
ER_BINLOG_UNSAFE_CREATE_IGNORE_SELECT,
6667
ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT,
68+
ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC,
6769
ER_BINLOG_UNSAFE_UPDATE_IGNORE
6870
};
6971

sql/sql_lex.h

+15
Original file line numberDiff line numberDiff line change
@@ -1268,6 +1268,13 @@ class Query_tables_list
12681268
*/
12691269
BINLOG_STMT_UNSAFE_INSERT_SELECT_UPDATE,
12701270

1271+
/**
1272+
Query that writes to a table with auto_inc column after selecting from
1273+
other tables are unsafe as the order in which the rows are retrieved by
1274+
select may differ on master and slave.
1275+
*/
1276+
BINLOG_STMT_UNSAFE_WRITE_AUTOINC_SELECT,
1277+
12711278
/**
12721279
INSERT...REPLACE SELECT is unsafe because which rows are replaced depends
12731280
on the order that rows are retrieved by SELECT. This order cannot be
@@ -1289,6 +1296,14 @@ class Query_tables_list
12891296
*/
12901297
BINLOG_STMT_UNSAFE_CREATE_REPLACE_SELECT,
12911298

1299+
/**
1300+
CREATE TABLE...SELECT on a table with auto-increment column is unsafe
1301+
because which rows are replaced depends on the order that rows are
1302+
retrieved from SELECT. This order cannot be predicted and may differ on
1303+
master and the slave
1304+
*/
1305+
BINLOG_STMT_UNSAFE_CREATE_SELECT_AUTOINC,
1306+
12921307
/**
12931308
UPDATE...IGNORE is unsafe because which rows are ignored depends on the
12941309
order that rows are updated. This order cannot be predicted and may differ

0 commit comments

Comments
 (0)