#At file:///home/lsoares/Workspace/bzr/work/bugfixing/49100/mysql-5.1-rpl-wl5092/ based on revid:luis.soares@stripped
3187 Luis Soares 2010-04-21
BUG#49100: RBR: Unexpected behavior when AI contains no usable
data for slave columns
RBR was not fully compliant with empty before and after
images (BI and AI) in row events. Several faulty scenarios
surfaced during WL#5092 implementation, especially when MINIMAL
images were used:
S1. Inserting an empty row on the master would cause slave to
skip the event (empty AI);
Problem: Master and slave would become out of sync.
Expected: slave(s) shall apply the insert and fill its
table with default values as master did.
S2. Master inserts values just for columns that slave doesn't
have (unusable AI at the slave).
Problem: In a chained replication scenario (m -> s1 -> s2),
m and s1 would be in sync, but s2 would not.
Expected: slave(s) shall apply the insert and fill its
table with default values as master did for columns that
user did not provide data.
S3. Master updates columns that only it knows about (unusable
AI at the slave) but references columns that it shares with
the slave (usable BI at the slave). This would trigger an
assertion on a third slave in chained replication: m -> s1
-> s2, s2 would abort.
Problem: in chained replication scenario (m -> s1 -> s2),
s2 would trigger an assertion while unpacking the row
logged by s1.
Expected: slave s1 shall not update any row and slave s2
shall not assert.
S4. Master updates and references columns that only it knows
about (unusable AI and BI at the slave).
Problem: although AI does not contain any data for updating
the slave (update becomes no-op), the slave stops with
error, because BI does not also contain relevant data for
finding the record.
Expected: slave(s) shall skip the event, thence not even
trying to update its tables with any value.
S5. Master references columns that only it knows
about (unusable BI at the slave) and updates columns shared
with the slave (usable AI at the slave). Slave must stop.
Problem: none really. Just adding the test case for
coverage purposes.
Expected: slave(s) shall fail with HA_ERR_END_OF_FILE.
We fix S1 to S3 by relaxing some asserts that were triggered on
when processing empty images and by doing some changes to
accomodate the fact that some images might be empty.
For S4, we deploy a check in Update_rows_log_event::do_exec_row
that verifies if there is any value in the AI. If there is not,
then the event is skipped (neither BI nor AI is are processed).
As for S5, we just add the test case.
For mysqlbinlog --verbose --base64=decode-rows we print out an
INSERT INTO t VALUES () when a write rows event contains an empty
AI.
@ mysql-test/suite/rpl/r/rpl_row_img.result
Updates to result files.
@ mysql-test/suite/rpl/t/rpl_row_img.test
Added test cases.
@ sql/log_event.cc
1. Added printout for INSERT INTO ... VALUES () when AI is empty for
Write_rows_log_events.
2. Changed assertion from 'm_curr_row < ...' to 'm_curr_row <= ...'
3. Calculation of estimated rows for bulk insert is now conditional,
and dependent of whether m_curr_row < m_rows_end or not.
4. In the Update_rows_log_event, changed it to take into consideration
if there are any values in AI. If there are not, then skip event.
@ sql/log_event.h
Changed assertion to from 'm_curr_row < ...' to ' m_curr_row <= ...'.
@ sql/rpl_record.cc
Do not unpack if master did not send any bit set in the write_set.
modified:
mysql-test/suite/rpl/r/rpl_row_img.result
mysql-test/suite/rpl/t/rpl_row_img.test
sql/log_event.cc
sql/log_event.h
sql/rpl_record.cc
=== modified file 'mysql-test/suite/rpl/r/rpl_row_img.result'
--- a/mysql-test/suite/rpl/r/rpl_row_img.result 2010-03-30 16:06:36 +0000
+++ b/mysql-test/suite/rpl/r/rpl_row_img.result 2010-04-21 00:54:25 +0000
@@ -24179,3 +24179,259 @@ FLUSH TABLES;
SHOW VARIABLES LIKE 'binlog_row_image';
Variable_name Value
binlog_row_image FULL
+include/stop_slave.inc
+RESET MASTER;
+RESET SLAVE;
+include/stop_slave.inc
+RESET MASTER;
+RESET SLAVE;
+RESET MASTER;
+RESET SLAVE;
+include/start_slave.inc
+include/start_slave.inc
+DROP TABLE IF EXISTS t1;
+CON: 'mysqld_a', IMG: 'MINIMAL', RESTART SLAVE: 'N'
+SET SESSION binlog_row_image= 'MINIMAL';
+SET GLOBAL binlog_row_image= 'MINIMAL';
+FLUSH TABLES;
+SHOW VARIABLES LIKE 'binlog_row_image';
+Variable_name Value
+binlog_row_image MINIMAL
+CON: 'mysqld_b', IMG: 'MINIMAL', RESTART SLAVE: 'Y'
+SET SESSION binlog_row_image= 'MINIMAL';
+SET GLOBAL binlog_row_image= 'MINIMAL';
+include/stop_slave.inc
+include/start_slave.inc
+FLUSH TABLES;
+SHOW VARIABLES LIKE 'binlog_row_image';
+Variable_name Value
+binlog_row_image MINIMAL
+CON: 'mysqld_c', IMG: 'MINIMAL', RESTART SLAVE: 'Y'
+SET SESSION binlog_row_image= 'MINIMAL';
+SET GLOBAL binlog_row_image= 'MINIMAL';
+include/stop_slave.inc
+include/start_slave.inc
+FLUSH TABLES;
+SHOW VARIABLES LIKE 'binlog_row_image';
+Variable_name Value
+binlog_row_image MINIMAL
+#### case #1: AI: no values logged
+CREATE TABLE t1 (c1 int DEFAULT 100);
+INSERT INTO t1 VALUES ();
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 1
+SELECT * FROM t1;
+c1
+100
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 2
+SELECT * FROM t1;
+c1
+100
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 3
+SELECT * FROM t1;
+c1
+100
+DROP TABLE t1;
+#### case #2: AI: not empty but slave does not have usable data for its columns (INSERT)
+CREATE TABLE t1 (c1 int DEFAULT 100);
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 int DEFAULT 100);
+SET SQL_LOG_BIN=1;
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 int DEFAULT 100, c2 int, primary key(c2));
+SET SQL_LOG_BIN=1;
+INSERT INTO t1(c2) VALUES (1);
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 1
+SELECT * FROM t1;
+c1 c2
+100 1
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 2
+SELECT * FROM t1;
+c1
+100
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 3
+SELECT * FROM t1;
+c1
+100
+DROP TABLE t1;
+#### case #3: BI: usable columns on the slave, AI: no usable columns on the slave
+CREATE TABLE t1 (c1 int DEFAULT 100);
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 int DEFAULT 100);
+SET SQL_LOG_BIN=1;
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 int DEFAULT 100, c2 int);
+SET SQL_LOG_BIN=1;
+INSERT INTO t1 VALUES (1,1);
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 1
+SELECT * FROM t1;
+c1 c2
+1 1
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 2
+SELECT * FROM t1;
+c1
+1
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 3
+SELECT * FROM t1;
+c1
+1
+UPDATE t1 SET c2=2 WHERE c1=1 AND c2=1;
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 1
+SELECT * FROM t1;
+c1 c2
+1 2
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 2
+SELECT * FROM t1;
+c1
+1
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 3
+SELECT * FROM t1;
+c1
+1
+DROP TABLE t1;
+#### case #4: AI, BI: no usable columns on the slave (NOOP UPDATE).
+####
+CREATE TABLE t1 (c1 int DEFAULT 100);
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 int DEFAULT 100);
+SET SQL_LOG_BIN=1;
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 int DEFAULT 100, c2 int, c3 int, primary key(c2));
+SET SQL_LOG_BIN=1;
+INSERT INTO t1 VALUES (1,1,1);
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 1
+SELECT * FROM t1;
+c1 c2 c3
+1 1 1
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 2
+SELECT * FROM t1;
+c1
+1
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 3
+SELECT * FROM t1;
+c1
+1
+UPDATE t1 SET c3=300 WHERE c2=1;
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 1
+SELECT * FROM t1;
+c1 c2 c3
+1 1 300
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 2
+SELECT * FROM t1;
+c1
+1
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 3
+SELECT * FROM t1;
+c1
+1
+DROP TABLE t1;
+#### case #5: BI: no usable columns on the slave, AI: usable columns on the slave (slave must stop).
+####
+CREATE TABLE t1 (c1 INT DEFAULT 100);
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 INT DEFAULT 100);
+SET SQL_LOG_BIN=1;
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 INT DEFAULT 100, c2 INT PRIMARY KEY);
+SET SQL_LOG_BIN=1;
+INSERT INTO t1 VALUES (1,1);
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 1
+SELECT * FROM t1;
+c1 c2
+1 1
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 2
+SELECT * FROM t1;
+c1
+1
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 3
+SELECT * FROM t1;
+c1
+1
+UPDATE t1 SET c1=300 WHERE c2=1;
+SHOW VARIABLES LIKE 'server_id';
+Variable_name Value
+server_id 1
+SELECT * FROM t1;
+c1 c2
+300 1
+SET SQL_LOG_BIN=0;
+call mtr.add_suppression("Slave: Can\'t find record in \'t1\' Error_code: 1032");
+SET SQL_LOG_BIN=1;
+### SLAVE STOPPED HAS EXPECTED!
+include/stop_slave.inc
+RESET MASTER;
+RESET SLAVE;
+include/stop_slave.inc
+RESET MASTER;
+RESET SLAVE;
+RESET MASTER;
+RESET SLAVE;
+include/start_slave.inc
+include/start_slave.inc
+DROP TABLE IF EXISTS t1;
+CON: 'mysqld_a', IMG: 'FULL', RESTART SLAVE: 'N'
+SET SESSION binlog_row_image= 'FULL';
+SET GLOBAL binlog_row_image= 'FULL';
+FLUSH TABLES;
+SHOW VARIABLES LIKE 'binlog_row_image';
+Variable_name Value
+binlog_row_image FULL
+CON: 'mysqld_b', IMG: 'FULL', RESTART SLAVE: 'Y'
+SET SESSION binlog_row_image= 'FULL';
+SET GLOBAL binlog_row_image= 'FULL';
+include/stop_slave.inc
+include/start_slave.inc
+FLUSH TABLES;
+SHOW VARIABLES LIKE 'binlog_row_image';
+Variable_name Value
+binlog_row_image FULL
+CON: 'mysqld_c', IMG: 'FULL', RESTART SLAVE: 'Y'
+SET SESSION binlog_row_image= 'FULL';
+SET GLOBAL binlog_row_image= 'FULL';
+include/stop_slave.inc
+include/start_slave.inc
+FLUSH TABLES;
+SHOW VARIABLES LIKE 'binlog_row_image';
+Variable_name Value
+binlog_row_image FULL
=== modified file 'mysql-test/suite/rpl/t/rpl_row_img.test'
--- a/mysql-test/suite/rpl/t/rpl_row_img.test 2010-03-30 16:06:36 +0000
+++ b/mysql-test/suite/rpl/t/rpl_row_img.test 2010-04-21 00:54:25 +0000
@@ -1,12 +1,7 @@
#
-# Test Description
-# ================
-#
-# In this file we will run tests that mix engines, indexes
-# and blobs.
-#
-# See WL#5096 for details.
+# This file contains tests for WL#5096 and bug fixes.
#
+
-- source include/rpl_chained_3_hosts.inc
-- source include/have_binlog_format_row.inc
@@ -18,6 +13,18 @@
-- source include/have_innodb.inc
-- connection mysqld_a
+#
+# WL#5096
+#
+# Test Description
+# ================
+#
+# WL#5096 Tests.
+#
+# In this file we will run tests that mix engines, indexes
+# and blobs.
+#
+
-- echo ###################################################################
-- echo ###################################################################
-- echo # engines and binlog-row-image mixes #
@@ -41,3 +48,247 @@
-- echo ###################################################################
-- let $row_img_test_script= extra/rpl_tests/rpl_row_img_blobs.test
-- source include/rpl_row_img_general_loop.inc
+
+#
+# BUG#49100: RBR: Unexpected behavior when AI contains no usable data
+# for slave columns
+#
+
+-- connection mysqld_c
+-- source include/stop_slave.inc
+RESET MASTER;
+RESET SLAVE;
+
+-- connection mysqld_b
+-- source include/stop_slave.inc
+RESET MASTER;
+RESET SLAVE;
+
+-- connection mysqld_a
+RESET MASTER;
+RESET SLAVE;
+
+-- connection mysqld_c
+-- source include/start_slave.inc
+
+-- connection mysqld_b
+-- source include/start_slave.inc
+
+-- disable_warnings
+DROP TABLE IF EXISTS t1;
+-- enable_warnings
+-- source include/rpl_chained_3_hosts_sync.inc
+-- connection mysqld_a
+
+-- let $row_img_set=mysqld_a:MINIMAL:N,mysqld_b:MINIMAL:Y,mysqld_c:MINIMAL:Y
+-- source include/rpl_row_img_set.inc
+
+-- echo #### case #1: AI: no values logged
+
+CREATE TABLE t1 (c1 int DEFAULT 100);
+INSERT INTO t1 VALUES ();
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- source include/rpl_chained_3_hosts_sync.inc
+-- connection mysqld_b
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- connection mysqld_c
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+
+-- connection mysqld_a
+DROP TABLE t1;
+-- source include/rpl_chained_3_hosts_sync.inc
+
+-- echo #### case #2: AI: not empty but slave does not have usable data for its columns (INSERT)
+
+-- connection mysqld_c
+CREATE TABLE t1 (c1 int DEFAULT 100);
+
+-- connection mysqld_b
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 int DEFAULT 100);
+SET SQL_LOG_BIN=1;
+
+-- connection mysqld_a
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 int DEFAULT 100, c2 int, primary key(c2));
+SET SQL_LOG_BIN=1;
+
+INSERT INTO t1(c2) VALUES (1);
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- source include/rpl_chained_3_hosts_sync.inc
+-- connection mysqld_b
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- connection mysqld_c
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+
+-- connection mysqld_a
+DROP TABLE t1;
+-- source include/rpl_chained_3_hosts_sync.inc
+
+-- echo #### case #3: BI: usable columns on the slave, AI: no usable columns on the slave
+
+-- connection mysqld_c
+CREATE TABLE t1 (c1 int DEFAULT 100);
+
+-- connection mysqld_b
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 int DEFAULT 100);
+SET SQL_LOG_BIN=1;
+
+-- connection mysqld_a
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 int DEFAULT 100, c2 int);
+SET SQL_LOG_BIN=1;
+
+INSERT INTO t1 VALUES (1,1);
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- source include/rpl_chained_3_hosts_sync.inc
+-- connection mysqld_b
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- connection mysqld_c
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+
+-- connection mysqld_a
+UPDATE t1 SET c2=2 WHERE c1=1 AND c2=1;
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- source include/rpl_chained_3_hosts_sync.inc
+-- connection mysqld_b
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- connection mysqld_c
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+
+-- connection mysqld_a
+DROP TABLE t1;
+-- source include/rpl_chained_3_hosts_sync.inc
+
+-- echo #### case #4: AI, BI: no usable columns on the slave (NOOP UPDATE).
+-- echo ####
+
+-- connection mysqld_c
+CREATE TABLE t1 (c1 int DEFAULT 100);
+
+-- connection mysqld_b
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 int DEFAULT 100);
+SET SQL_LOG_BIN=1;
+
+-- connection mysqld_a
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 int DEFAULT 100, c2 int, c3 int, primary key(c2));
+SET SQL_LOG_BIN=1;
+
+INSERT INTO t1 VALUES (1,1,1);
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- source include/rpl_chained_3_hosts_sync.inc
+-- connection mysqld_b
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- connection mysqld_c
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+
+-- connection mysqld_a
+UPDATE t1 SET c3=300 WHERE c2=1;
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- source include/rpl_chained_3_hosts_sync.inc
+-- connection mysqld_b
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- connection mysqld_c
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+
+-- connection mysqld_a
+DROP TABLE t1;
+-- source include/rpl_chained_3_hosts_sync.inc
+
+-- echo #### case #5: BI: no usable columns on the slave, AI: usable columns on the slave (slave must stop).
+-- echo ####
+
+-- connection mysqld_c
+CREATE TABLE t1 (c1 INT DEFAULT 100);
+
+-- connection mysqld_b
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 INT DEFAULT 100);
+SET SQL_LOG_BIN=1;
+
+-- connection mysqld_a
+SET SQL_LOG_BIN=0;
+CREATE TABLE t1 (c1 INT DEFAULT 100, c2 INT PRIMARY KEY);
+SET SQL_LOG_BIN=1;
+
+INSERT INTO t1 VALUES (1,1);
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- source include/rpl_chained_3_hosts_sync.inc
+-- connection mysqld_b
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- connection mysqld_c
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+
+-- connection mysqld_a
+UPDATE t1 SET c1=300 WHERE c2=1;
+SHOW VARIABLES LIKE 'server_id';
+SELECT * FROM t1;
+-- connection mysqld_b
+SET SQL_LOG_BIN=0;
+call mtr.add_suppression("Slave: Can\'t find record in \'t1\' Error_code: 1032");
+SET SQL_LOG_BIN=1;
+-- source include/wait_for_slave_sql_to_stop.inc
+-- let $errno=query_get_value("show slave status", Last_SQL_Errno, 1)
+if (`SELECT $errno <> 1032`)
+{
+ -- echo ### UNEXPECTED ERROR AT THE SLAVE: $errno
+ SHOW SLAVE STATUS;
+ -- die
+}
+-- echo ### SLAVE STOPPED HAS EXPECTED!
+
+## CLEAN UP
+
+-- connection mysqld_c
+-- source include/stop_slave.inc
+RESET MASTER;
+RESET SLAVE;
+
+-- connection mysqld_b
+-- source include/stop_slave.inc
+RESET MASTER;
+RESET SLAVE;
+
+-- connection mysqld_a
+RESET MASTER;
+RESET SLAVE;
+
+-- connection mysqld_c
+-- source include/start_slave.inc
+
+-- connection mysqld_b
+-- source include/start_slave.inc
+
+-- connection mysqld_a
+-- disable_warnings
+DROP TABLE IF EXISTS t1;
+-- enable_warnings
+-- source include/rpl_chained_3_hosts_sync.inc
+
+-- let $row_img_set=mysqld_a:FULL:N,mysqld_b:FULL:Y,mysqld_c:FULL:Y
+-- source include/rpl_row_img_set.inc
+
=== modified file 'sql/log_event.cc'
--- a/sql/log_event.cc 2010-03-04 17:00:32 +0000
+++ b/sql/log_event.cc 2010-04-21 00:54:25 +0000
@@ -1953,6 +1953,14 @@ void Rows_log_event::print_verbose(IO_CA
return;
}
+ /* If the write rows event contained no values for the AI */
+ if ((type_code == WRITE_ROWS_EVENT) && (m_rows_buf==m_rows_end))
+ {
+ my_b_printf(file, "### INSERT INTO `%s`.`%s` VALUES ()\n",
+ map->get_db_name(), map->get_table_name());
+ goto end;
+ }
+
for (const uchar *value= m_rows_buf; value < m_rows_end; )
{
size_t length;
@@ -7636,7 +7644,7 @@ int Rows_log_event::do_apply_event(Relay
// row processing loop
- while (error == 0 && m_curr_row < m_rows_end)
+ while (error == 0)
{
/* in_use can have been set to NULL in close_tables_for_reopen */
THD* old_thd= table->in_use;
@@ -7687,7 +7695,7 @@ int Rows_log_event::do_apply_event(Relay
// at this moment m_curr_row_end should be set
DBUG_ASSERT(error || m_curr_row_end != NULL);
- DBUG_ASSERT(error || m_curr_row < m_curr_row_end);
+ DBUG_ASSERT(error || m_curr_row <= m_curr_row_end);
DBUG_ASSERT(error || m_curr_row_end <= m_rows_end);
m_curr_row= m_curr_row_end;
@@ -7695,6 +7703,9 @@ int Rows_log_event::do_apply_event(Relay
if (error == 0 && !transactional_table)
thd->transaction.all.modified_non_trans_table=
thd->transaction.stmt.modified_non_trans_table= TRUE;
+
+ if (m_curr_row == m_rows_end)
+ break;
} // row processing loop
{/**
@@ -8695,7 +8706,13 @@ Rows_log_event::write_row(const Relay_lo
/* this is the first row to be inserted, we estimate the rows with
the size of the first row and use that value to initialize
storage engine for bulk insertion */
- ulong estimated_rows= (m_rows_end - m_curr_row) / (m_curr_row_end - m_curr_row);
+ DBUG_ASSERT(!(m_curr_row > m_curr_row_end));
+ ulong estimated_rows;
+ if (m_curr_row < m_curr_row_end)
+ estimated_rows= (m_rows_end - m_curr_row) / (m_curr_row_end - m_curr_row);
+ else if (m_curr_row = m_curr_row_end)
+ estimated_rows= 1;
+
m_table->file->ha_start_bulk_insert(estimated_rows);
}
@@ -9665,8 +9682,44 @@ int
Update_rows_log_event::do_exec_row(const Relay_log_info *const rli)
{
DBUG_ASSERT(m_table != NULL);
+ int error= 0;
+
+ /**
+ Check if update contains only values in AI for columns that do
+ not exist on the slave. If it does, we can just unpack the rows
+ and return (do nothing on the local table).
+
+ NOTE: We do the following optimization and check only if there
+ are usable values on the AI and disregard the fact that there
+ might be usable values in the BI. In practice this means that
+ the slave will not go through find_row (since we have nothing
+ on the record to update, why go looking for it?).
+
+ If we wanted find_row to run anyway, we could move this
+ check after find_row, but then we would have to face the fact
+ that the slave might stop without finding the proper record
+ (because it might have incomplete BI), even though there were
+ no values in AI.
+
+ On the other hand, if AI has usable values but BI has not,
+ then find_row will return an error (and the error is then
+ propagated as it was already).
+ */
+ if (!is_any_column_signaled_for_table(m_table, &m_cols_ai))
+ {
+ /*
+ Read and discard images, because:
+ 1. AI does not contain any useful values to replay;
+ 2. BI is irrelevant if there is nothing useful in AI.
+ */
+ error = unpack_current_row(rli, &m_cols);
+ m_curr_row= m_curr_row_end;
+ error = error | unpack_current_row(rli, &m_cols_ai);
+
+ return error;
+ }
- int error= find_row(rli);
+ error= find_row(rli);
if (error)
{
/*
=== modified file 'sql/log_event.h'
--- a/sql/log_event.h 2010-03-04 17:00:32 +0000
+++ b/sql/log_event.h 2010-04-21 00:54:25 +0000
@@ -3628,7 +3628,7 @@ protected:
DBUG_ASSERT(m_table);
bool first_row= (m_curr_row == m_rows_buf);
- ASSERT_OR_RETURN_ERROR(m_curr_row < m_rows_end, HA_ERR_CORRUPT_EVENT);
+ ASSERT_OR_RETURN_ERROR(m_curr_row <= m_rows_end, HA_ERR_CORRUPT_EVENT);
int const result= ::unpack_row(rli, m_table, m_width, m_curr_row, cols,
&m_curr_row_end, &m_master_reclength,
abort_on_warning, first_row);
=== modified file 'sql/rpl_record.cc'
--- a/sql/rpl_record.cc 2010-03-04 17:00:32 +0000
+++ b/sql/rpl_record.cc 2010-04-21 00:54:25 +0000
@@ -208,6 +208,18 @@ unpack_row(Relay_log_info const *rli,
uchar const *null_ptr= row_data;
uchar const *pack_ptr= row_data + master_null_byte_count;
+ if (bitmap_is_clear_all(cols))
+ {
+ /**
+ There was no data sent from the master, so there is
+ nothing to unpack.
+ */
+ *row_end= pack_ptr;
+ *master_reclength= 0;
+ DBUG_RETURN(error);
+ }
+
+
Field **const begin_ptr = table->field;
Field **field_ptr;
Field **const end_ptr= begin_ptr + colcnt;
Attachment: [text/bzr-bundle] bzr/luis.soares@sun.com-20100421005425-jwy6406nypzewrcw.bundle