From e9b5cafa8b2a95a0bf414922ac61335ea199c765 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 May 2006 19:21:52 +0200 Subject: [PATCH] Bug#19995 (Extreneous table maps generated for statements that do not generate rows): Switched to writing out table maps for tables that are locked when the first row in a statement is seen. mysql-test/include/master-slave.inc: Moved code to reset master and slave into separate file. mysql-test/r/binlog_row_blackhole.result: Result change mysql-test/r/binlog_row_mix_innodb_myisam.result: Result change mysql-test/r/ndb_binlog_ignore_db.result: Result change mysql-test/r/rpl_ndb_charset.result: Result change mysql-test/r/rpl_row_basic_11bugs.result: Result change mysql-test/r/rpl_row_charset.result: Result change mysql-test/r/rpl_row_create_table.result: Result change mysql-test/t/rpl_row_basic_11bugs.test: Added test to check that no events are generated when no rows are changed. mysql-test/t/rpl_row_create_table.test: Master log position changed sql/handler.cc: Adding function write_locked_table_maps() that will write table maps for all tables locked for write. Using "table->in_use" instead of "current_thd" since tables are now locked when the function is called. Removing old code to write table map. sql/log_event.cc: Added assertion sql/sql_class.cc: Removing code to write "dummy termination event". sql/sql_class.h: Adding getter for binlog_table_maps. sql/sql_insert.cc: Setting thd->lock before calling write_record for the execution of CREATE-SELECT and INSERT-SELECT since they keep multiple locks in the air at the same time. mysql-test/include/master-slave-reset.inc: New BitKeeper file ``mysql-test/include/master-slave-reset.inc'' --- mysql-test/include/master-slave-reset.inc | 22 ++++ mysql-test/include/master-slave.inc | 24 +--- mysql-test/r/binlog_row_blackhole.result | 12 -- .../r/binlog_row_mix_innodb_myisam.result | 38 +++--- mysql-test/r/ndb_binlog_ignore_db.result | 2 - mysql-test/r/rpl_ndb_charset.result | 10 -- mysql-test/r/rpl_row_basic_11bugs.result | 17 +++ mysql-test/r/rpl_row_charset.result | 10 -- mysql-test/r/rpl_row_create_table.result | 22 ++-- mysql-test/t/rpl_row_basic_11bugs.test | 15 +++ mysql-test/t/rpl_row_create_table.test | 8 +- sql/handler.cc | 108 +++++++++++------- sql/log_event.cc | 1 + sql/sql_class.cc | 25 ---- sql/sql_class.h | 17 +-- sql/sql_insert.cc | 32 +++++- 16 files changed, 197 insertions(+), 166 deletions(-) create mode 100644 mysql-test/include/master-slave-reset.inc diff --git a/mysql-test/include/master-slave-reset.inc b/mysql-test/include/master-slave-reset.inc new file mode 100644 index 00000000000..204c6c5f5f6 --- /dev/null +++ b/mysql-test/include/master-slave-reset.inc @@ -0,0 +1,22 @@ +connection slave; +#we expect STOP SLAVE to produce a warning as the slave is stopped +#(the server was started with skip-slave-start) +--disable_warnings +stop slave; +--wait_for_slave_to_stop +--enable_warnings +connection master; +--disable_warnings +--disable_query_log +use test; +--enable_query_log +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +--enable_warnings +reset master; +connection slave; +reset slave; +# Clean up old test tables +--disable_warnings +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +--enable_warnings +start slave; diff --git a/mysql-test/include/master-slave.inc b/mysql-test/include/master-slave.inc index ea09f4e842b..fe1dacb433e 100644 --- a/mysql-test/include/master-slave.inc +++ b/mysql-test/include/master-slave.inc @@ -2,28 +2,8 @@ connect (master,127.0.0.1,root,,test,$MASTER_MYPORT,); connect (master1,127.0.0.1,root,,test,$MASTER_MYPORT,); connect (slave,127.0.0.1,root,,test,$SLAVE_MYPORT,); connect (slave1,127.0.0.1,root,,test,$SLAVE_MYPORT,); -connection slave; -#we expect STOP SLAVE to produce a warning as the slave is stopped -#(the server was started with skip-slave-start) ---disable_warnings -stop slave; ---enable_warnings ---require r/slave-stopped.result -show status like 'Slave_running'; -connection master; ---disable_warnings -drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; ---enable_warnings -reset master; -connection slave; -reset slave; -# Clean up old test tables ---disable_warnings -drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; ---enable_warnings -start slave; ---require r/slave-running.result -show status like 'Slave_running'; + +-- source include/master-slave-reset.inc # Set the default connection to 'master' connection master; diff --git a/mysql-test/r/binlog_row_blackhole.result b/mysql-test/r/binlog_row_blackhole.result index 42bf7a10888..140d7d4da46 100644 --- a/mysql-test/r/binlog_row_blackhole.result +++ b/mysql-test/r/binlog_row_blackhole.result @@ -118,12 +118,6 @@ master-bin.000001 # Query 1 # use `test`; COMMIT master-bin.000001 # Table_map 1 # table_id: # (test.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # use `test`; COMMIT -master-bin.000001 # Table_map 1 # table_id: # (test.t1) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `test`; COMMIT -master-bin.000001 # Table_map 1 # table_id: # (test.t1) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `test`; COMMIT master-bin.000001 # Query 1 # use `test`; create table t2 (a varchar(200)) engine=blackhole master-bin.000001 # Table_map 1 # table_id: # (test.t2) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F @@ -131,12 +125,6 @@ master-bin.000001 # Query 1 # use `test`; COMMIT master-bin.000001 # Query 1 # use `test`; alter table t1 add b int master-bin.000001 # Query 1 # use `test`; alter table t1 drop b master-bin.000001 # Query 1 # use `test`; create table t3 like t1 -master-bin.000001 # Table_map 1 # table_id: # (test.t1) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `test`; COMMIT -master-bin.000001 # Table_map 1 # table_id: # (test.t1) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `test`; COMMIT drop table t1,t2,t3; reset master; create table t1 (a int) engine=blackhole; diff --git a/mysql-test/r/binlog_row_mix_innodb_myisam.result b/mysql-test/r/binlog_row_mix_innodb_myisam.result index 078a95d5abd..25ef16284fa 100644 --- a/mysql-test/r/binlog_row_mix_innodb_myisam.result +++ b/mysql-test/r/binlog_row_mix_innodb_myisam.result @@ -262,27 +262,23 @@ master-bin.000001 209 Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 243 Table_map 1 # table_id: # (test.t1) master-bin.000001 282 Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 316 Xid 1 # COMMIT /* xid= */ -master-bin.000001 343 Table_map 1 # table_id: # (test.t1) -master-bin.000001 382 Query 1 # use `test`; delete from t1 -master-bin.000001 459 Xid 1 # COMMIT /* xid= */ -master-bin.000001 486 Table_map 1 # table_id: # (test.t2) -master-bin.000001 525 Query 1 # use `test`; delete from t2 -master-bin.000001 602 Xid 1 # COMMIT /* xid= */ -master-bin.000001 629 Query 1 # use `test`; alter table t2 engine=MyISAM -master-bin.000001 720 Table_map 1 # table_id: # (test.t1) -master-bin.000001 759 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 793 Xid 1 # COMMIT /* xid= */ -master-bin.000001 820 Query 1 # use `test`; BEGIN -master-bin.000001 888 Table_map 1 # table_id: # (test.t1) -master-bin.000001 927 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 956 Xid 1 # COMMIT /* xid= */ -master-bin.000001 983 Query 1 # use `test`; drop table t1,t2 -master-bin.000001 1062 Query 1 # use `test`; create table t0 (n int) -master-bin.000001 1148 Table_map 1 # table_id: # (test.t0) -master-bin.000001 1187 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 1221 Table_map 1 # table_id: # (test.t0) -master-bin.000001 1260 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 1294 Query 1 # use `test`; create table t2 (n int) engine=innodb +master-bin.000001 343 Query 1 # use `test`; delete from t1 +master-bin.000001 420 Xid 1 # COMMIT /* xid= */ +master-bin.000001 447 Query 1 # use `test`; delete from t2 +master-bin.000001 524 Xid 1 # COMMIT /* xid= */ +master-bin.000001 551 Query 1 # use `test`; alter table t2 engine=MyISAM +master-bin.000001 642 Table_map 1 # table_id: # (test.t1) +master-bin.000001 681 Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 715 Xid 1 # COMMIT /* xid= */ +master-bin.000001 742 Table_map 1 # table_id: # (test.t2) +master-bin.000001 781 Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 815 Query 1 # use `test`; drop table t1,t2 +master-bin.000001 894 Query 1 # use `test`; create table t0 (n int) +master-bin.000001 980 Table_map 1 # table_id: # (test.t0) +master-bin.000001 1019 Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 1053 Table_map 1 # table_id: # (test.t0) +master-bin.000001 1092 Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 1126 Query 1 # use `test`; create table t2 (n int) engine=innodb do release_lock("lock1"); drop table t0,t2; reset master; diff --git a/mysql-test/r/ndb_binlog_ignore_db.result b/mysql-test/r/ndb_binlog_ignore_db.result index 2f411fcd03d..8dc2c1ff1f8 100644 --- a/mysql-test/r/ndb_binlog_ignore_db.result +++ b/mysql-test/r/ndb_binlog_ignore_db.result @@ -7,6 +7,4 @@ insert into t1 values (1, 1); show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query # # use `test`; drop table if exists t1 -master-bin.000001 # Table_map # # table_id: # (mysql.proc) -master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F drop database mysqltest; diff --git a/mysql-test/r/rpl_ndb_charset.result b/mysql-test/r/rpl_ndb_charset.result index 31a7e1368f3..0ce4446c8a5 100644 --- a/mysql-test/r/rpl_ndb_charset.result +++ b/mysql-test/r/rpl_ndb_charset.result @@ -112,16 +112,10 @@ drop database mysqltest3; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # drop database if exists mysqltest2 -master-bin.000001 # Table_map 1 # table_id: # (mysql.proc) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # drop database if exists mysqltest3 -master-bin.000001 # Table_map 1 # table_id: # (mysql.proc) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # create database mysqltest2 character set latin2 master-bin.000001 # Query 1 # create database mysqltest3 master-bin.000001 # Query 1 # drop database mysqltest3 -master-bin.000001 # Table_map 1 # table_id: # (mysql.proc) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # create database mysqltest3 master-bin.000001 # Query 1 # use `mysqltest2`; create table t1 (a int auto_increment primary key, b varchar(100)) master-bin.000001 # Table_map 1 # table_id: # (mysqltest2.t1) @@ -147,11 +141,7 @@ master-bin.000001 # Query 1 # use `mysqltest2`; truncate table t1 master-bin.000001 # Table_map 1 # table_id: # (mysqltest2.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # drop database mysqltest2 -master-bin.000001 # Table_map 1 # table_id: # (mysql.proc) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # drop database mysqltest3 -master-bin.000001 # Table_map 1 # table_id: # (mysql.proc) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F select "--- --global--" as ""; --- --global-- diff --git a/mysql-test/r/rpl_row_basic_11bugs.result b/mysql-test/r/rpl_row_basic_11bugs.result index 4ed7d54647b..7cd895f2041 100644 --- a/mysql-test/r/rpl_row_basic_11bugs.result +++ b/mysql-test/r/rpl_row_basic_11bugs.result @@ -44,3 +44,20 @@ t1 USE test_ignore; ERROR 42000: Unknown database 'test_ignore' DROP DATABASE test_ignore; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +CREATE TABLE t1 (a INT); +DELETE FROM t1; +INSERT INTO t1 VALUES (1),(2); +DELETE FROM t1 WHERE a = 0; +UPDATE t1 SET a=99 WHERE a = 0; +SHOW BINLOG EVENTS; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 4 Format_desc 1 102 Server ver: 5.1.11-beta-debug-log, Binlog ver: 4 +master-bin.000001 102 Query 1 188 use `test`; CREATE TABLE t1 (a INT) +master-bin.000001 188 Query 1 265 use `test`; DELETE FROM t1 +master-bin.000001 265 Table_map 1 304 table_id: # (test.t1) +master-bin.000001 304 Write_rows 1 343 table_id: # flags: STMT_END_F diff --git a/mysql-test/r/rpl_row_charset.result b/mysql-test/r/rpl_row_charset.result index d80b5fa60de..79cf75c8cc1 100644 --- a/mysql-test/r/rpl_row_charset.result +++ b/mysql-test/r/rpl_row_charset.result @@ -112,16 +112,10 @@ drop database mysqltest3; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # drop database if exists mysqltest2 -master-bin.000001 # Table_map 1 # table_id: # (mysql.proc) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # drop database if exists mysqltest3 -master-bin.000001 # Table_map 1 # table_id: # (mysql.proc) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # create database mysqltest2 character set latin2 master-bin.000001 # Query 1 # create database mysqltest3 master-bin.000001 # Query 1 # drop database mysqltest3 -master-bin.000001 # Table_map 1 # table_id: # (mysql.proc) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # create database mysqltest3 master-bin.000001 # Query 1 # use `mysqltest2`; create table t1 (a int auto_increment primary key, b varchar(100)) master-bin.000001 # Table_map 1 # table_id: # (mysqltest2.t1) @@ -147,11 +141,7 @@ master-bin.000001 # Query 1 # use `mysqltest2`; truncate table t1 master-bin.000001 # Table_map 1 # table_id: # (mysqltest2.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # drop database mysqltest2 -master-bin.000001 # Table_map 1 # table_id: # (mysql.proc) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # drop database mysqltest3 -master-bin.000001 # Table_map 1 # table_id: # (mysql.proc) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F select "--- --global--" as ""; --- --global-- diff --git a/mysql-test/r/rpl_row_create_table.result b/mysql-test/r/rpl_row_create_table.result index b3a8f5e1a0f..f314aa39b81 100644 --- a/mysql-test/r/rpl_row_create_table.result +++ b/mysql-test/r/rpl_row_create_table.result @@ -127,7 +127,7 @@ NULL 5 10 NULL 6 12 CREATE TABLE t7 (UNIQUE(b)) SELECT a,b FROM tt3; ERROR 23000: Duplicate entry '2' for key 'b' -SHOW BINLOG EVENTS FROM 1326; +SHOW BINLOG EVENTS FROM 1256; Log_name Pos Event_type Server_id End_log_pos Info CREATE TABLE t7 (a INT, b INT UNIQUE); INSERT INTO t7 SELECT a,b FROM tt3; @@ -137,11 +137,11 @@ a b 1 2 2 4 3 6 -SHOW BINLOG EVENTS FROM 1326; +SHOW BINLOG EVENTS FROM 1256; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 1326 Query 1 1426 use `test`; CREATE TABLE t7 (a INT, b INT UNIQUE) -master-bin.000001 1426 Table_map 1 1466 table_id: # (test.t7) -master-bin.000001 1466 Write_rows 1 1522 table_id: # flags: STMT_END_F +master-bin.000001 1256 Query 1 1356 use `test`; CREATE TABLE t7 (a INT, b INT UNIQUE) +master-bin.000001 1356 Table_map 1 1396 table_id: # (test.t7) +master-bin.000001 1396 Write_rows 1 1452 table_id: # flags: STMT_END_F SELECT * FROM t7 ORDER BY a,b; a b 1 2 @@ -154,10 +154,10 @@ INSERT INTO t7 SELECT a,b FROM tt4; ROLLBACK; Warnings: Warning 1196 Some non-transactional changed tables couldn't be rolled back -SHOW BINLOG EVENTS FROM 1522; +SHOW BINLOG EVENTS FROM 1452; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 1522 Table_map 1 1562 table_id: # (test.t7) -master-bin.000001 1562 Write_rows 1 1618 table_id: # flags: STMT_END_F +master-bin.000001 1452 Table_map 1 1492 table_id: # (test.t7) +master-bin.000001 1492 Write_rows 1 1548 table_id: # flags: STMT_END_F SELECT * FROM t7 ORDER BY a,b; a b 1 2 @@ -191,10 +191,10 @@ Create Table CREATE TABLE `t9` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 -SHOW BINLOG EVENTS FROM 1618; +SHOW BINLOG EVENTS FROM 1548; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 1618 Query 1 1704 use `test`; CREATE TABLE t8 LIKE t4 -master-bin.000001 1704 Query 1 1843 use `test`; CREATE TABLE `t9` ( +master-bin.000001 1548 Query 1 1634 use `test`; CREATE TABLE t8 LIKE t4 +master-bin.000001 1634 Query 1 1773 use `test`; CREATE TABLE `t9` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL ) diff --git a/mysql-test/t/rpl_row_basic_11bugs.test b/mysql-test/t/rpl_row_basic_11bugs.test index 3a686ea6b3d..bd582ce349a 100644 --- a/mysql-test/t/rpl_row_basic_11bugs.test +++ b/mysql-test/t/rpl_row_basic_11bugs.test @@ -36,3 +36,18 @@ USE test_ignore; connection master; DROP DATABASE test_ignore; sync_slave_with_master; + +# Bug#19995: Extreneous table maps generated for statements that does +# not generate rows +--disable_query_log +--source include/master-slave-reset.inc +--enable_query_log + +connection master; +CREATE TABLE t1 (a INT); +DELETE FROM t1; +INSERT INTO t1 VALUES (1),(2); +DELETE FROM t1 WHERE a = 0; +UPDATE t1 SET a=99 WHERE a = 0; +--replace_regex /table_id: [0-9]+/table_id: #/ +SHOW BINLOG EVENTS; diff --git a/mysql-test/t/rpl_row_create_table.test b/mysql-test/t/rpl_row_create_table.test index 0cb0fd766a3..8a8ea01d688 100644 --- a/mysql-test/t/rpl_row_create_table.test +++ b/mysql-test/t/rpl_row_create_table.test @@ -67,7 +67,7 @@ connection master; CREATE TABLE t7 (UNIQUE(b)) SELECT a,b FROM tt3; # Shouldn't be written to the binary log --replace_regex /table_id: [0-9]+/table_id: #/ -SHOW BINLOG EVENTS FROM 1326; +SHOW BINLOG EVENTS FROM 1256; # Test that INSERT-SELECT works the same way as for SBR. CREATE TABLE t7 (a INT, b INT UNIQUE); @@ -76,7 +76,7 @@ INSERT INTO t7 SELECT a,b FROM tt3; SELECT * FROM t7 ORDER BY a,b; # Should be written to the binary log --replace_regex /table_id: [0-9]+/table_id: #/ -SHOW BINLOG EVENTS FROM 1326; +SHOW BINLOG EVENTS FROM 1256; sync_slave_with_master; SELECT * FROM t7 ORDER BY a,b; @@ -87,7 +87,7 @@ BEGIN; INSERT INTO t7 SELECT a,b FROM tt4; ROLLBACK; --replace_regex /table_id: [0-9]+/table_id: #/ -SHOW BINLOG EVENTS FROM 1522; +SHOW BINLOG EVENTS FROM 1452; SELECT * FROM t7 ORDER BY a,b; sync_slave_with_master; SELECT * FROM t7 ORDER BY a,b; @@ -101,7 +101,7 @@ CREATE TEMPORARY TABLE tt6 LIKE tt4; --query_vertical SHOW CREATE TABLE t8 --query_vertical SHOW CREATE TABLE t9 --replace_regex /table_id: [0-9]+/table_id: #/ -SHOW BINLOG EVENTS FROM 1618; +SHOW BINLOG EVENTS FROM 1548; sync_slave_with_master; --echo **** On Slave **** --query_vertical SHOW CREATE TABLE t8 diff --git a/sql/handler.cc b/sql/handler.cc index b9ef05a33c2..dfa3789f6a0 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -3199,6 +3199,58 @@ namespace { } } +/** + Write table maps for all (manually or automatically) locked tables + to the binary log. + + This function will generate and write table maps for all tables + that are locked by the thread 'thd'. Either manually locked + (stored in THD::locked_tables) and automatically locked (stored in + THD::lock) are considered. + + See THD::lock and THD::locked_tables for more information. + */ +static int +write_locked_table_maps(THD *thd) +{ + DBUG_ENTER("write_locked_table_maps"); + DBUG_PRINT("enter", ("thd=%p, thd->lock=%p, thd->locked_tables=%p", + thd, thd->lock, thd->locked_tables)); + + if (thd->get_binlog_table_maps() == 0) + { + /* + Exactly one table has to be locked, otherwise this code is not + guaranteed to work. + */ + DBUG_ASSERT((thd->lock != NULL) + (thd->locked_tables != NULL) == 1); + + MYSQL_LOCK *lock= thd->lock ? thd->lock : thd->locked_tables; + DBUG_ASSERT(lock->table_count > 0); + TABLE **const end_ptr= lock->table + lock->table_count; + for (TABLE **table_ptr= lock->table ; + table_ptr != end_ptr ; + ++table_ptr) + { + TABLE *const table= *table_ptr; + DBUG_PRINT("info", ("Checking table %s", table->s->table_name)); + if (table->current_lock == F_WRLCK && + check_table_binlog_row_based(thd, table)) + { + int const has_trans= table->file->has_transactions(); + int const error= thd->binlog_write_table_map(table, has_trans); + /* + If an error occurs, it is the responsibility of the caller to + roll back the transaction. + */ + if (unlikely(error)) + DBUG_RETURN(1); + } + } + } + DBUG_RETURN(0); +} + template int binlog_log_row(TABLE* table, const byte *before_record, const byte *after_record) @@ -3206,7 +3258,7 @@ template int binlog_log_row(TABLE* table, if (table->file->is_injective()) return 0; bool error= 0; - THD *const thd= current_thd; + THD *const thd= table->in_use; if (check_table_binlog_row_based(thd, table)) { @@ -3215,17 +3267,26 @@ template int binlog_log_row(TABLE* table, uint32 bitbuf[BITMAP_STACKBUF_SIZE/sizeof(uint32)]; uint n_fields= table->s->fields; my_bool use_bitbuf= n_fields <= sizeof(bitbuf)*8; + + /* + If there are no table maps written to the binary log, this is + the first row handled in this statement. In that case, we need + to write table maps for all locked tables to the binary log. + */ if (likely(!(error= bitmap_init(&cols, use_bitbuf ? bitbuf : NULL, (n_fields + 7) & ~7UL, false)))) { bitmap_set_all(&cols); - error= - RowsEventT::binlog_row_logging_function(thd, table, - table->file->has_transactions(), - &cols, table->s->fields, - before_record, after_record); + if (likely(!(error= write_locked_table_maps(thd)))) + { + error= + RowsEventT::binlog_row_logging_function(thd, table, + table->file->has_transactions(), + &cols, table->s->fields, + before_record, after_record); + } if (!use_bitbuf) bitmap_free(&cols); } @@ -3251,41 +3312,6 @@ int handler::ha_external_lock(THD *thd, int lock_type) int error; if (unlikely(error= external_lock(thd, lock_type))) DBUG_RETURN(error); -#ifdef HAVE_ROW_BASED_REPLICATION - if (table->file->is_injective()) - DBUG_RETURN(0); - - /* - There is a number of statements that are logged statement-based - but call external lock. For these, we do not need to generate a - table map. - - TODO: The need for this switch is an indication that the model for - locking combined with row-based replication needs to be looked - over. Ideally, no such special handling should be needed. - */ - switch (thd->lex->sql_command) { - case SQLCOM_TRUNCATE: - case SQLCOM_ALTER_TABLE: - case SQLCOM_OPTIMIZE: - case SQLCOM_REPAIR: - DBUG_RETURN(0); - default: - break; - } - - /* - If we are locking a table for writing, we generate a table map. - For all other kinds of locks, we don't do anything. - */ - if (lock_type == F_WRLCK && check_table_binlog_row_based(thd, table)) - { - int const has_trans= table->file->has_transactions(); - error= thd->binlog_write_table_map(table, has_trans); - if (unlikely(error)) - DBUG_RETURN(error); - } -#endif DBUG_RETURN(0); } diff --git a/sql/log_event.cc b/sql/log_event.cc index ab9fa2975a1..e78a6fc5865 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -5602,6 +5602,7 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) bool Rows_log_event::write_data_header(IO_CACHE *file) { byte buf[ROWS_HEADER_LEN]; // No need to init the buffer + DBUG_ASSERT(m_table_id != ~0UL); DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", { int4store(buf + 0, m_table_id); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 916bcd94e0e..b0f399b3c12 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2624,31 +2624,6 @@ int THD::binlog_flush_pending_rows_event(bool stmt_end) error= mysql_bin_log.flush_and_set_pending_rows_event(this, 0); } - else if (stmt_end && binlog_table_maps > 0) - { /* there is no pending event at this point */ - /* - If pending is null and we are going to end the statement, we - have to write an extra, empty, binrow event so that the slave - knows to discard the tables it has received. Otherwise, the - table maps written this far will be included in the table maps - for the following statement. - - TODO: Remove the need for a dummy event altogether. It can be - fixed if we can write table maps to a memory buffer before - writing the first binrow event. We can then flush and clear the - memory buffer with table map events before writing the first - binrow event. In the event of a crash, nothing is lost since - the table maps are only needed if there are binrow events. - */ - - Rows_log_event *ev= - new Write_rows_log_event(this, 0, ~0UL, 0, FALSE); - ev->set_flags(Rows_log_event::STMT_END_F); - binlog_set_pending_rows_event(ev); - - error= mysql_bin_log.flush_and_set_pending_rows_event(this, 0); - binlog_table_maps= 0; - } DBUG_RETURN(error); } diff --git a/sql/sql_class.h b/sql/sql_class.h index 0135763b3b2..11225bb85fa 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -941,16 +941,18 @@ public: int binlog_flush_pending_rows_event(bool stmt_end); void binlog_delete_pending_rows_event(); -private: #ifdef HAVE_ROW_BASED_REPLICATION +private: uint binlog_table_maps; // Number of table maps currently in the binlog -#endif /* HAVE_ROW_BASED_REPLICATION */ - public: - + uint get_binlog_table_maps() const { + return binlog_table_maps; + } +#endif /* HAVE_ROW_BASED_REPLICATION */ #endif /* MYSQL_CLIENT */ #ifndef MYSQL_CLIENT +public: enum enum_binlog_query_type { /* The query can be logged row-based or statement-based @@ -1572,6 +1574,9 @@ class select_insert :public select_result_interceptor { bool send_eof(); /* not implemented: select_insert is never re-used in prepared statements */ void cleanup(); + +protected: + MYSQL_LOCK *lock; }; @@ -1581,7 +1586,6 @@ class select_create: public select_insert { List *extra_fields; List *keys; HA_CREATE_INFO *create_info; - MYSQL_LOCK *lock; Field **field; public: select_create (TABLE_LIST *table, @@ -1590,8 +1594,7 @@ public: List &keys_par, List &select_fields,enum_duplicates duplic, bool ignore) :select_insert (NULL, NULL, &select_fields, 0, 0, duplic, ignore), create_table(table), - extra_fields(&fields_par),keys(&keys_par), create_info(create_info_par), - lock(0) + extra_fields(&fields_par),keys(&keys_par), create_info(create_info_par) {} int prepare(List &list, SELECT_LEX_UNIT *u); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index aed8b12d2c1..8dee3b054b3 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2152,6 +2152,7 @@ select_insert::select_insert(TABLE_LIST *table_list_par, TABLE *table_par, bool ignore_check_option_errors) :table_list(table_list_par), table(table_par), fields(fields_par), last_insert_id(0), + lock(0), insert_into_view(table_list_par && table_list_par->view != 0) { bzero((char*) &info,sizeof(info)); @@ -2348,7 +2349,36 @@ bool select_insert::send_data(List &values) DBUG_RETURN(1); } } - if (!(error= write_record(thd, table, &info))) + + /* + The thd->lock lock contain the locks for the select part of the + statement and the 'lock' variable contain the write lock for the + currently locked table that is being created or inserted + into. However, the row-based replication will investigate the + thd->lock to decide what table maps are to be written, so this one + has to contain the tables locked for writing. To be able to write + table map for the table being created, we temporarily set + THD::lock to select_insert::lock while writing the record to the + storage engine. We cannot set this elsewhere, since the execution + of a stored function inside the select expression might cause the + lock structures to be NULL. + */ + + { + MYSQL_LOCK *saved_lock= NULL; + if (lock) + { + saved_lock= thd->lock; + thd->lock= lock; + } + + error= write_record(thd, table, &info); + + if (lock) + thd->lock= saved_lock; + } + + if (!error) { if (table->triggers || info.handle_duplicates == DUP_UPDATE) {