mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
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>
This commit is contained in:
@ -164,4 +164,28 @@ update t1 set i = 0;
|
|||||||
connection slave;
|
connection slave;
|
||||||
connection master;
|
connection master;
|
||||||
drop table t1;
|
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
|
include/rpl_end.inc
|
||||||
|
@ -133,4 +133,38 @@ sync_slave_with_master;
|
|||||||
connection master;
|
connection master;
|
||||||
drop table t1;
|
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
|
--source include/rpl_end.inc
|
||||||
|
@ -13873,7 +13873,7 @@ uint8 Write_rows_log_event::get_trg_event_map()
|
|||||||
|
|
||||||
Returns TRUE if different.
|
Returns TRUE if different.
|
||||||
*/
|
*/
|
||||||
static bool record_compare(TABLE *table)
|
static bool record_compare(TABLE *table, bool vers_from_plain= false)
|
||||||
{
|
{
|
||||||
bool result= FALSE;
|
bool result= FALSE;
|
||||||
/**
|
/**
|
||||||
@ -13906,10 +13906,19 @@ static bool record_compare(TABLE *table)
|
|||||||
/* Compare fields */
|
/* Compare fields */
|
||||||
for (Field **ptr=table->field ; *ptr ; ptr++)
|
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;
|
continue;
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
We only compare field contents that are not null.
|
We only compare field contents that are not null.
|
||||||
NULL fields (i.e., their null bits) were compared
|
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. */
|
/* We use this to test that the correct key is used in test cases. */
|
||||||
DBUG_EXECUTE_IF("slave_crash_if_index_scan", abort(););
|
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])))
|
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;
|
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
|
Note: above record_compare will take into account all record fields
|
||||||
|
Reference in New Issue
Block a user