summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/suite/rpl/r/rpl_row_find_row.result (renamed from mysql-test/suite/rpl/r/rpl_row_disabled_slave_key.result)12
-rw-r--r--mysql-test/suite/rpl/t/rpl_row_find_row.test (renamed from mysql-test/suite/rpl/t/rpl_row_disabled_slave_key.test)31
-rw-r--r--sql/log_event.cc34
-rw-r--r--sql/log_event_old.cc34
4 files changed, 107 insertions, 4 deletions
diff --git a/mysql-test/suite/rpl/r/rpl_row_disabled_slave_key.result b/mysql-test/suite/rpl/r/rpl_row_find_row.result
index 01e3dfd6508..69516b47b7d 100644
--- a/mysql-test/suite/rpl/r/rpl_row_disabled_slave_key.result
+++ b/mysql-test/suite/rpl/r/rpl_row_find_row.result
@@ -24,3 +24,15 @@ INSERT INTO t VALUES (1,2,4);
INSERT INTO t VALUES (4,3,4);
DELETE FROM t;
DROP TABLE t;
+stop slave;
+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 (c1 INT NOT NULL, c2 INT NOT NULL, c3 INT, UNIQUE KEY(c1,c3), KEY(c2));
+INSERT INTO t1(c1,c2) VALUES(1,1);
+INSERT INTO t1(c1,c2) VALUES(1,2);
+UPDATE t1 SET c1=1000 WHERE c2=2;
+Comparing tables master:test.t1 and slave:test.t1
+DROP TABLE t1;
diff --git a/mysql-test/suite/rpl/t/rpl_row_disabled_slave_key.test b/mysql-test/suite/rpl/t/rpl_row_find_row.test
index 1d7e134f4f4..9163ab54406 100644
--- a/mysql-test/suite/rpl/t/rpl_row_disabled_slave_key.test
+++ b/mysql-test/suite/rpl/t/rpl_row_find_row.test
@@ -71,3 +71,34 @@ DELETE FROM t;
DROP TABLE t;
-- sync_slave_with_master
+
+#
+# BUG#53893: RBR: nullable unique key can lead to out-of-sync slave
+#
+
+#
+# We insert two rows. Both with part of UNIQUE KEY set to null.
+# Then we update the last row inserted. On master the correct
+# row is updated. On the slave the wrong row would be updated
+# because the engine would look it up by the NULL Unique KEY.
+# As a consquence, the wrong row would be updated.
+#
+
+-- connection master
+-- source include/master-slave-reset.inc
+-- connection master
+
+CREATE TABLE t1 (c1 INT NOT NULL, c2 INT NOT NULL, c3 INT, UNIQUE KEY(c1,c3), KEY(c2));
+INSERT INTO t1(c1,c2) VALUES(1,1);
+INSERT INTO t1(c1,c2) VALUES(1,2);
+UPDATE t1 SET c1=1000 WHERE c2=2;
+-- sync_slave_with_master
+
+-- let $diff_table_1=master:test.t1
+-- let $diff_table_2=slave:test.t1
+-- source include/diff_tables.inc
+
+-- connection master
+DROP TABLE t1;
+-- sync_slave_with_master
+
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 2cc594d85b4..c07bb573188 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -9015,8 +9015,38 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
*/
if (table->key_info->flags & HA_NOSAME)
{
- table->file->ha_index_end();
- goto ok;
+ /* Unique does not have non nullable part */
+ if (!(table->key_info->flags & (HA_NULL_PART_KEY)))
+ {
+ table->file->ha_index_end();
+ goto ok;
+ }
+ else
+ {
+ KEY *keyinfo= table->key_info;
+ /*
+ Unique has nullable part. We need to check if there is any field in the
+ BI image that is null and part of UNNI.
+ */
+ bool null_found= FALSE;
+
+ for (uint i=0, fieldnr= keyinfo->key_part[i].fieldnr - 1 ;
+ (i < keyinfo->key_parts) && !null_found ;
+ i++, fieldnr= keyinfo->key_part[i].fieldnr - 1)
+ {
+ Field **f= table->field+fieldnr;
+ if ((*f)->is_null())
+ null_found= TRUE;
+ }
+
+ if (!null_found)
+ {
+ table->file->ha_index_end();
+ goto ok;
+ }
+
+ /* else fall through to index scan */
+ }
}
/*
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index 60b0df5253a..5f2d55d6477 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -2428,8 +2428,38 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
*/
if (table->key_info->flags & HA_NOSAME)
{
- table->file->ha_index_end();
- DBUG_RETURN(0);
+ /* Unique does not have non nullable part */
+ if (!(table->key_info->flags & (HA_NULL_PART_KEY)))
+ {
+ table->file->ha_index_end();
+ DBUG_RETURN(0);
+ }
+ else
+ {
+ KEY *keyinfo= table->key_info;
+ /*
+ Unique has nullable part. We need to check if there is any field in the
+ BI image that is null and part of UNNI.
+ */
+ bool null_found= FALSE;
+
+ for (uint i=0, fieldnr= keyinfo->key_part[i].fieldnr - 1 ;
+ (i < keyinfo->key_parts) && !null_found ;
+ i++, fieldnr= keyinfo->key_part[i].fieldnr - 1)
+ {
+ Field **f= table->field+fieldnr;
+ if ((*f)->is_null())
+ null_found= TRUE;
+ }
+
+ if (!null_found)
+ {
+ table->file->ha_index_end();
+ DBUG_RETURN(0);
+ }
+
+ /* else fall through to index scan */
+ }
}
/*