Commit 29fb0410 authored by Brandon Nesterenko's avatar Brandon Nesterenko

MDEV-30430: Enabling system versioning on tables without primary key breaks replication

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>
parent 4ec3dca3
......@@ -164,4 +164,28 @@ update t1 set i = 0;
connection slave;
connection master;
drop table t1;
# 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
......@@ -133,4 +133,38 @@ sync_slave_with_master;
connection master;
drop table t1;
#
# 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
......@@ -13873,7 +13873,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;
/**
......@@ -13906,10 +13906,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
......@@ -14306,7 +14315,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])))
{
......@@ -14359,7 +14368,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 account all record fields
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment