MDEV-35343 unexpected replace behaviour when long unique index on

system versioned table

For versioned table REPLACE first tries to insert a row, if it gets
duplicate key error and optimization is possible it does UPDATE +
INSERT history. If optimization is not possible it goes normal branch
for UPDATE to history and repeats the cycle of INSERT.

The failure was in normal branch when we tried UPDATE to history but
such history already exists from previous cycles. There is no such
failures in optimized branch because vers_insert_history_row() already
ignores duplicates.

The fix ignores duplicate errors for UPDATE to history and does DELETE
instead.
This commit is contained in:
Aleksey Midenkov 2025-01-13 15:40:59 +03:00
parent e1e1e50bba
commit 78c192644c
3 changed files with 50 additions and 2 deletions

View file

@ -27,7 +27,9 @@ id x current
1 2 0
1 3 1
drop table t;
#
# MDEV-15645 Assertion `table->insert_values' failed in write_record upon REPLACE into a view with underlying versioned table
#
create or replace table t1 (a int, b int, primary key (a), unique(b)) with system versioning;
insert into t1 values (1,1);
create or replace table t2 (c int);
@ -48,7 +50,9 @@ INSERT INTO t1 () VALUES (),(),(),(),(),();
UPDATE IGNORE t1 SET f = 1;
REPLACE t1 SELECT * FROM t1;
DROP TABLE t1;
#
# MDEV-22540 ER_DUP_ENTRY upon REPLACE or Assertion failed
#
set timestamp=1589245268.41934;
create table t1 (a int primary key) with system versioning;
insert into t1 values (1),(2);
@ -72,3 +76,15 @@ Warnings:
Warning 1062 Duplicate entry '1' for key 'a'
load data infile '15330.data' replace into table t1 (a,b,c);
drop table t1;
#
# MDEV-35343 unexpected replace behaviour when long unique index on system versioned table
#
create table t1 (data char(10));
insert into t1 values ('o');
alter ignore table t1 add unique index (data);
alter ignore table t1 add unique index (data);
Warnings:
Note 1831 Duplicate index `data_2`. This is deprecated and will be disallowed in a future release
alter table t1 add system versioning;
replace into t1 values ('o'), ('o');
drop table t1;

View file

@ -35,7 +35,9 @@ replace t values (1, 3);
select *, current_row(row_end) as current from t for system_time all order by x;
drop table t;
--echo #
--echo # MDEV-15645 Assertion `table->insert_values' failed in write_record upon REPLACE into a view with underlying versioned table
--echo #
create or replace table t1 (a int, b int, primary key (a), unique(b)) with system versioning;
insert into t1 values (1,1);
create or replace table t2 (c int);
@ -59,7 +61,9 @@ UPDATE IGNORE t1 SET f = 1;
REPLACE t1 SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # MDEV-22540 ER_DUP_ENTRY upon REPLACE or Assertion failed
--echo #
set timestamp=1589245268.41934;
create table t1 (a int primary key) with system versioning;
insert into t1 values (1),(2);
@ -105,4 +109,15 @@ drop table t1;
eval set default_storage_engine= $default_engine;
--enable_query_log
--echo #
--echo # MDEV-35343 unexpected replace behaviour when long unique index on system versioned table
--echo #
create table t1 (data char(10));
insert into t1 values ('o');
alter ignore table t1 add unique index (data);
alter ignore table t1 add unique index (data);
alter table t1 add system versioning;
replace into t1 values ('o'), ('o');
drop table t1;
--source suite/versioning/common_finish.inc

View file

@ -2126,6 +2126,9 @@ int write_record(THD *thd, TABLE *table, COPY_INFO *info, select_result *sink)
!table->file->referenced_by_foreign_key() &&
(!table->triggers || !table->triggers->has_delete_triggers()))
{
/*
Optimized dup handling via UPDATE (and insert history for versioned).
*/
if (table->versioned(VERS_TRX_ID))
{
bitmap_set_bit(table->write_set, table->vers_start_field()->field_index);
@ -2160,25 +2163,39 @@ int write_record(THD *thd, TABLE *table, COPY_INFO *info, select_result *sink)
}
else
{
/*
Normal dup handling via DELETE (or UPDATE to history for versioned)
and repeating the cycle of INSERT.
*/
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_BEFORE, TRUE))
goto before_trg_err;
if (!table->versioned(VERS_TIMESTAMP))
bool do_delete= !table->versioned(VERS_TIMESTAMP);
if (do_delete)
error= table->file->ha_delete_row(table->record[1]);
else
{
/* Update existing row to history */
store_record(table, record[2]);
restore_record(table, record[1]);
table->vers_update_end();
error= table->file->ha_update_row(table->record[1],
table->record[0]);
restore_record(table, record[2]);
if (error == HA_ERR_FOUND_DUPP_KEY || /* Unique index, any SE */
error == HA_ERR_FOREIGN_DUPLICATE_KEY || /* Unique index, InnoDB */
error == HA_ERR_RECORD_IS_THE_SAME) /* No index */
{
/* Such history row was already generated from previous cycles */
error= table->file->ha_delete_row(table->record[1]);
do_delete= true;
}
}
if (unlikely(error))
goto err;
if (!table->versioned(VERS_TIMESTAMP))
if (do_delete)
info->deleted++;
else
info->updated++;