mariadb/mysql-test/suite/versioning/r/replace.result
Nikita Malyavin e56f6c585e MDEV-15990 handle timestamp-based collisions as well
Timestamp-versioned row deletion was exposed to a collisional problem: if
current timestamp wasn't changed, then a sequence of row delete+insert could
get a duplication error. A row delete would find another conflicting history row
and return an error.

This is true both for REPLACE and DELETE statements, however in REPLACE, the
"optimized" path is usually taken, especially in the tests. There, delete+insert
is substituted for a single versioned row update. In the end, both paths end up
as ha_update_row + ha_write_row.

The solution is to handle a history collision somehow.

From the design perspective, the user shouldn't experience history rows loss,
unless there's a technical limitation.

To the contrary, trxid-based changes should never generate history for the same
transaction, see MDEV-15427.

If two operations on the same row happened too quickly, so that they happen at
the same timestamp, the history row shouldn't be lost. We can still write a
history row, though it'll have row_start == row_end.

We cannot store more than one such historical row, as this will violate the
unique constraint on row_end. So we will have to phisically delete the row if
the history row is already available.

In this commit:
1. Improve TABLE::delete_row to handle the history collision: if an update
   results with a duplicate error, delete a row for real.
2. use TABLE::delete_row in a non-optimistic path of REPLACE, where the
   system-versioned case now belongs entirely.
2025-08-04 17:44:05 +02:00

151 lines
4.7 KiB
Text

create or replace table t(
id int,
KEY_TYPE(id),
x int,
row_start SYS_DATATYPE as row start invisible,
row_end SYS_DATATYPE as row end invisible,
period for system_time(row_start, row_end)
) with system versioning;
insert t values (1, 2);
replace t values (1, 3);
select *, current_row(row_end) as current from t for system_time all order by x;
id x current
1 2 0
1 3 1
drop table t;
create table t (
id int unique,
x int,
row_start SYS_DATATYPE as row start invisible,
row_end SYS_DATATYPE as row end invisible,
period for system_time (row_start, row_end)
) with system versioning;
insert t values (1, 2);
replace t values (1, 3);
select *, current_row(row_end) as current from t for system_time all order by x;
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);
create or replace view v as select t1.* from t1 join t2;
replace into v (a, b) select a, b from t1;
drop view v;
drop tables t1, t2;
CREATE TABLE t1 (
pk INT AUTO_INCREMENT,
f INT,
row_start SYS_DATATYPE AS ROW START INVISIBLE,
row_end SYS_DATATYPE AS ROW END INVISIBLE,
PRIMARY KEY(pk),
UNIQUE(f),
PERIOD FOR SYSTEM_TIME(row_start, row_end)
) WITH SYSTEM VERSIONING;
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);
connect con1,localhost,root,,test;
set timestamp=1589245268.52093;
replace into t1 values (1),(2);
connection default;
replace into t1 values (1),(2);
connection con1;
replace into t1 values (1),(2);
drop table t1;
#
# MDEV-14794 Limitations which the row end as a part of PK imposes due to
# CURRENT_TIMESTAMP behavior and otherwise
#
# MDEV-15330 Server crash or assertion `table->insert_values' failure in write_record upon LOAD DATA
#
create table t1 (a int, b int, c int, vc int as (c), unique(a), unique(b)) with system versioning;
insert ignore into t1 (a,b,c) values (1,2,3);
select a, b, c into outfile '15330.data' from t1;
load data infile '15330.data' ignore into table t1 (a,b,c);
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;
#
# MDEV-15990 REPLACE on a precise-versioned table returns duplicate key
# error (ER_DUP_ENTRY)
#
create or replace table t1 (
pk int KEY_TYPE, i int,
row_start SYS_DATATYPE as row start invisible,
row_end SYS_DATATYPE as row end invisible,
period for system_time(row_start, row_end)
) with system versioning;
replace into t1 (pk,i) values (1,10),(1,100),(1,1000);
select pk, i, check_row(row_start, row_end) from t1 for system_time all;
pk i check_row(row_start, row_end)
1 10 ERROR: row_end == row_start
1 1000 CURRENT ROW
drop table t1;
create or replace table t1 (
pk int KEY_TYPE, i int,
row_start SYS_DATATYPE as row start invisible,
row_end SYS_DATATYPE as row end invisible,
period for system_time(row_start, row_end)
) with system versioning;
set timestamp= (select unix_timestamp());
begin;
insert t1(pk, i) values(1,3);
delete from t1;
insert t1(pk, i) values(1,30);
delete from t1;
insert t1(pk, i) values(1,300);
commit;
select pk, i, check_row(row_start, row_end) from t1 for system_time all;
pk i check_row(row_start, row_end)
1 3 ERROR: row_end == row_start
1 300 CURRENT ROW
drop table t1;
# In this case there'll be no unique constraint on a historical row.
create or replace table t1 (
pk int, i int,
row_start SYS_DATATYPE as row start invisible,
row_end SYS_DATATYPE as row end invisible,
period for system_time(row_start, row_end)
) with system versioning;
set timestamp= (select unix_timestamp());
begin;
insert t1(pk, i) values(1,3);
delete from t1;
insert t1(pk, i) values(1,30);
delete from t1;
insert t1(pk, i) values(1,300);
commit;
select pk, i, check_row(row_start, row_end) from t1 for system_time all;
pk i check_row(row_start, row_end)
1 3 ERROR: row_end == row_start
1 30 ERROR: row_end == row_start
1 300 CURRENT ROW
drop table t1;
#
# End of 10.5 tests
#