summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrandon Nesterenko <brandon.nesterenko@mariadb.com>2023-04-05 10:43:28 -0600
committerBrandon Nesterenko <brandon.nesterenko@mariadb.com>2023-04-25 12:35:55 -0600
commita749dffff7a1f06adcf10e84cff0360b4cdde6c6 (patch)
tree92ab2fee12bcad491a882d23d52cc940b0e335a9
parentd6c8696724c3a803d85f0b4738237bbce8e90f64 (diff)
downloadmariadb-git-10.11-merge-30430.tar.gz
MDEV-30430: Enabling system versioning on tables without primary key breaks replication10.11-merge-30430
When replicating MDL events for a table that uses system versioning without primary keys, ensure that for data sets with duplicate records, the updates to these records with duplicates are enacted on the correct row. That is, there was a bug (reported in MDEV-30430) such that the function to find the row to update would stop after finding the first matching record. However, in the absence of primary keys, the version of the record is needed to compare the row to ensure we are updating the correct one. The fix, therefore, updates the record comparison functionality to use system version columns when there are no primary keys on the table. Reviewed By: ============ Andrei Elkin <andrei.elkin@mariadb.com>
-rw-r--r--mysql-test/suite/versioning/r/rpl.result24
-rw-r--r--mysql-test/suite/versioning/t/rpl.test33
-rw-r--r--sql/log_event_server.cc21
3 files changed, 72 insertions, 6 deletions
diff --git a/mysql-test/suite/versioning/r/rpl.result b/mysql-test/suite/versioning/r/rpl.result
index a6ac9aad3ca..66fcbcb3925 100644
--- a/mysql-test/suite/versioning/r/rpl.result
+++ b/mysql-test/suite/versioning/r/rpl.result
@@ -455,4 +455,28 @@ a check_row_ts(rs,re)
set sql_mode=default;
connection master;
drop tables t1, t2, t3;
+# check versioned -> versioned replication without any keys on duplicate records
+connection master;
+create table t1 (a INT) with system versioning;
+insert into t1 values (1);
+insert into t1 values (1);
+delete from t1;
+connection slave;
+include/diff_tables.inc [master:test.t1,slave:test.t1]
+connection master;
+drop table t1;
+connection slave;
+# check unversioned -> versioned replication with non-unique keys on duplicate records
+connection master;
+set statement sql_log_bin=0 for create table t1 (a INT NOT NULL, b INT, INDEX(a,b));
+connection slave;
+set statement sql_log_bin=0 for create table t1 (a INT NOT NULL, b INT, INDEX(a,b)) with system versioning;
+connection master;
+insert into t1 values (1,1);
+insert into t1 values (1,1);
+delete from t1;
+connection slave;
+include/diff_tables.inc [master:test.t1,slave:test.t1]
+connection master;
+drop table t1;
include/rpl_end.inc
diff --git a/mysql-test/suite/versioning/t/rpl.test b/mysql-test/suite/versioning/t/rpl.test
index 16e6af75dba..19f096496fa 100644
--- a/mysql-test/suite/versioning/t/rpl.test
+++ b/mysql-test/suite/versioning/t/rpl.test
@@ -353,4 +353,37 @@ connection master;
drop tables t1, t2, t3;
--source suite/versioning/common_finish.inc
+#
+# MDEV-30430: Enabling system versioning on tables without primary key breaks replication
+# Note that bugs are only present with row binlog format
+#
+--echo # check versioned -> versioned replication without any keys on duplicate records
+connection master;
+create table t1 (a INT) with system versioning;
+insert into t1 values (1);
+insert into t1 values (1);
+delete from t1;
+sync_slave_with_master;
+--let $diff_tables= master:test.t1,slave:test.t1
+--source include/diff_tables.inc
+connection master;
+drop table t1;
+sync_slave_with_master;
+
+--echo # check unversioned -> versioned replication with non-unique keys on duplicate records
+connection master;
+set statement sql_log_bin=0 for create table t1 (a INT NOT NULL, b INT, INDEX(a,b));
+connection slave;
+set statement sql_log_bin=0 for create table t1 (a INT NOT NULL, b INT, INDEX(a,b)) with system versioning;
+connection master;
+insert into t1 values (1,1);
+insert into t1 values (1,1);
+delete from t1;
+sync_slave_with_master;
+--let $diff_tables= master:test.t1,slave:test.t1
+--source include/diff_tables.inc
+
+connection master;
+drop table t1;
+
--source include/rpl_end.inc
diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc
index a0126d0e338..5daf30b3016 100644
--- a/sql/log_event_server.cc
+++ b/sql/log_event_server.cc
@@ -7970,7 +7970,7 @@ uint8 Write_rows_log_event::get_trg_event_map()
Returns TRUE if different.
*/
-static bool record_compare(TABLE *table)
+static bool record_compare(TABLE *table, bool vers_from_plain= false)
{
bool result= FALSE;
/**
@@ -8003,10 +8003,19 @@ static bool record_compare(TABLE *table)
/* Compare fields */
for (Field **ptr=table->field ; *ptr ; ptr++)
{
- if (table->versioned() && (*ptr)->vers_sys_field())
- {
+ /*
+ If the table is versioned, don't compare using the version if there is a
+ primary key. If there isn't a primary key, we need the version to
+ identify the correct record if there are duplicate rows in the data set.
+ However, if the primary server is unversioned (vers_from_plain is true),
+ then we implicitly use row_end as the primary key on our side. This is
+ because the implicit row_end value will be set to the maximum value for
+ the latest row update (which is what we care about).
+ */
+ if (table->versioned() && (*ptr)->vers_sys_field() &&
+ (table->s->primary_key < MAX_KEY ||
+ (vers_from_plain && table->vers_start_field() == (*ptr))))
continue;
- }
/**
We only compare field contents that are not null.
NULL fields (i.e., their null bits) were compared
@@ -8400,7 +8409,7 @@ int Rows_log_event::find_row(rpl_group_info *rgi)
/* We use this to test that the correct key is used in test cases. */
DBUG_EXECUTE_IF("slave_crash_if_index_scan", abort(););
- while (record_compare(table))
+ while (record_compare(table, m_vers_from_plain))
{
while ((error= table->file->ha_index_next(table->record[0])))
{
@@ -8453,7 +8462,7 @@ int Rows_log_event::find_row(rpl_group_info *rgi)
goto end;
}
}
- while (record_compare(table));
+ while (record_compare(table, m_vers_from_plain));
/*
Note: above record_compare will take into accout all record fields