Skip to content

Commit 932ec58

Browse files
committed
MDEV-23644 Assertion on evaluating foreign referential action for self-reference in system versioned table
First part of the fix (row0mysql.cc) addresses external columns when adding history row on referential action. The full data must be retrieved before the row is inserted. Second part of the fix (the rest) avoids duplicate primary key error between the history row generated on referential action and the history row generated by SQL command. Both command and referential action can happen on same table since foreign key can be self-reference (parent and child tables are same). Moreover, the self-reference can refer multiple rows when the key is non-unique. In such case history is generated by referential action occured on first row but processed all rows by a matched key. The second round is when the next row is processed by a command but history already exists. In such case we check TRX_ID of existing history row and if it is the same we assume the above situation and skip adding one more history row or failing the command.
1 parent 7410ff4 commit 932ec58

File tree

13 files changed

+129
-9
lines changed

13 files changed

+129
-9
lines changed

mysql-test/suite/versioning/common.inc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ returns int
7070
deterministic
7171
return sys_trx_end = $sys_datatype_max;
7272

73+
eval create or replace function current_row_ts(sys_trx_end timestamp(6))
74+
returns int
75+
deterministic
76+
return convert_tz(sys_trx_end, '+00:00', @@time_zone) = TIMESTAMP'2038-01-19 03:14:07.999999';
77+
7378
delimiter ~~;
7479
eval create or replace function check_row(row_start $sys_datatype_expl, row_end $sys_datatype_expl)
7580
returns varchar(255)
@@ -86,4 +91,20 @@ begin
8691
end~~
8792
delimiter ;~~
8893

94+
delimiter ~~;
95+
eval create or replace function check_row_ts(row_start timestamp(6), row_end timestamp(6))
96+
returns varchar(255)
97+
deterministic
98+
begin
99+
if row_end < row_start then
100+
return "ERROR: row_end < row_start";
101+
elseif row_end = row_start then
102+
return "ERROR: row_end == row_start";
103+
elseif current_row_ts(row_end) then
104+
return "CURRENT ROW";
105+
end if;
106+
return "HISTORICAL ROW";
107+
end~~
108+
delimiter ;~~
109+
89110
--enable_query_log

mysql-test/suite/versioning/common_finish.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@ drop procedure if exists verify_trt;
44
drop procedure if exists verify_trt_dummy;
55
drop function if exists current_row;
66
drop function if exists check_row;
7+
drop function if exists current_row_ts;
8+
drop function if exists check_row_ts;
79
--enable_warnings
810
--enable_query_log

mysql-test/suite/versioning/r/delete.result

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,13 @@ f1 int, f2 text, f3 int, fulltext (f2), key(f1), key(f3),
138138
foreign key r (f3) references t1 (f1) on delete set null)
139139
with system versioning engine innodb;
140140
insert into t1 values (1, repeat('a', 8193), 1), (1, repeat('b', 8193), 1);
141-
select f1, f3, check_row(row_start, row_end) from t1;
142-
f1 f3check_row(row_start, row_end)
141+
select f1, f3, check_row_ts(row_start, row_end) from t1;
142+
f1 f3check_row_ts(row_start, row_end)
143143
1 1 CURRENT ROW
144144
1 1 CURRENT ROW
145145
delete from t1;
146-
select f1, f3, check_row(row_start, row_end) from t1 for system_time all;
147-
f1 f3check_row(row_start, row_end)
146+
select f1, f3, check_row_ts(row_start, row_end) from t1 for system_time all;
147+
f1 f3check_row_ts(row_start, row_end)
148148
1 1 HISTORICAL ROW
149149
1 NULL ERROR: row_end == row_start
150150
1 1 HISTORICAL ROW

mysql-test/suite/versioning/r/foreign.result

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,8 @@ Warning 1265 Data truncated for column 'f12' at row 7
398398
SET timestamp = 9;
399399
REPLACE INTO t2 SELECT * FROM t2;
400400
DROP TABLE t1, t2;
401+
set timestamp= default;
402+
set time_zone='+00:00';
401403
#
402404
# MDEV-16210 FK constraints on versioned tables use historical rows, which may cause constraint violation
403405
#
@@ -427,3 +429,17 @@ insert into t2 values (1), (1);
427429
# DELETE from foreign table is allowed
428430
delete from t2;
429431
drop tables t2, t1;
432+
#
433+
# MDEV-23644 Assertion on evaluating foreign referential action for self-reference in system versioned table
434+
#
435+
create table t1 (pk int primary key, f1 int,f2 int, f3 text,
436+
key(f1), fulltext(f3), key(f3(10)),
437+
foreign key (f2) references t1 (f1) on delete set null
438+
) engine=innodb with system versioning;
439+
insert into t1 values (1, 8, 8, 'SHORT'), (2, 8, 8, repeat('LONG', 8071));
440+
delete from t1;
441+
select pk, f1, f2, left(f3, 4), check_row_ts(row_start, row_end) from t1 for system_time all order by pk;
442+
pk f1 f2 left(f3, 4) check_row_ts(row_start, row_end)
443+
1 8 8 SHOR HISTORICAL ROW
444+
2 8 8 LONG HISTORICAL ROW
445+
drop table t1;

mysql-test/suite/versioning/r/trx_id.result

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,15 @@ sys_trx_start bigint(20) unsigned as row start invisible,
55
sys_trx_end bigint(20) unsigned as row end invisible,
66
period for system_time (sys_trx_start, sys_trx_end)
77
) with system versioning;
8+
# No history inside the transaction
9+
start transaction;
810
insert into t1 (x) values (1);
11+
update t1 set x= x + 1;
12+
update t1 set x= x + 1;
13+
commit;
14+
select *, sys_trx_start > 1, sys_trx_end from t1 for system_time all;
15+
x sys_trx_start > 1 sys_trx_end
16+
3 1 18446744073709551615
917
# ALTER ADD SYSTEM VERSIONING should write to mysql.transaction_registry
1018
set @@system_versioning_alter_history=keep;
1119
create or replace table t1 (x int);

mysql-test/suite/versioning/t/delete.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ create table t1 (
102102
foreign key r (f3) references t1 (f1) on delete set null)
103103
with system versioning engine innodb;
104104
insert into t1 values (1, repeat('a', 8193), 1), (1, repeat('b', 8193), 1);
105-
select f1, f3, check_row(row_start, row_end) from t1;
105+
select f1, f3, check_row_ts(row_start, row_end) from t1;
106106
delete from t1;
107-
select f1, f3, check_row(row_start, row_end) from t1 for system_time all;
107+
select f1, f3, check_row_ts(row_start, row_end) from t1 for system_time all;
108108

109109
# cleanup
110110
drop table t1;

mysql-test/suite/versioning/t/foreign.test

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,8 @@ REPLACE INTO t2 SELECT * FROM t2;
421421

422422
# Cleanup
423423
DROP TABLE t1, t2;
424+
set timestamp= default;
425+
set time_zone='+00:00';
424426
--let $datadir= `select @@datadir`
425427
--remove_file $datadir/test/t1.data
426428
--remove_file $datadir/test/t1.data.2
@@ -458,4 +460,20 @@ insert into t2 values (1), (1);
458460
delete from t2;
459461
drop tables t2, t1;
460462

463+
--echo #
464+
--echo # MDEV-23644 Assertion on evaluating foreign referential action for self-reference in system versioned table
465+
--echo #
466+
create table t1 (pk int primary key, f1 int,f2 int, f3 text,
467+
key(f1), fulltext(f3), key(f3(10)),
468+
foreign key (f2) references t1 (f1) on delete set null
469+
) engine=innodb with system versioning;
470+
471+
insert into t1 values (1, 8, 8, 'SHORT'), (2, 8, 8, repeat('LONG', 8071));
472+
473+
delete from t1;
474+
select pk, f1, f2, left(f3, 4), check_row_ts(row_start, row_end) from t1 for system_time all order by pk;
475+
476+
# cleanup
477+
drop table t1;
478+
461479
--source suite/versioning/common_finish.inc

mysql-test/suite/versioning/t/trx_id.test

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@ create or replace table t1 (
1414
period for system_time (sys_trx_start, sys_trx_end)
1515
) with system versioning;
1616

17+
--echo # No history inside the transaction
18+
start transaction;
1719
insert into t1 (x) values (1);
20+
update t1 set x= x + 1;
21+
update t1 set x= x + 1;
22+
commit;
23+
select *, sys_trx_start > 1, sys_trx_end from t1 for system_time all;
1824

1925
--echo # ALTER ADD SYSTEM VERSIONING should write to mysql.transaction_registry
2026
set @@system_versioning_alter_history=keep;

sql/sql_delete.cc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,15 @@ int TABLE::delete_row()
246246

247247
store_record(this, record[1]);
248248
vers_update_end();
249-
return file->ha_update_row(record[1], record[0]);
249+
int err= file->ha_update_row(record[1], record[0]);
250+
/*
251+
MDEV-23644: we get HA_ERR_FOREIGN_DUPLICATE_KEY iff we already got history
252+
row with same trx_id which is the result of foreign key action, so we
253+
don't need one more history row.
254+
*/
255+
if (err == HA_ERR_FOREIGN_DUPLICATE_KEY)
256+
return file->ha_delete_row(record[0]);
257+
return err;
250258
}
251259

252260

storage/innobase/data/data0data.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ void
738738
dtuple_convert_back_big_rec(
739739
/*========================*/
740740
dict_index_t* index MY_ATTRIBUTE((unused)),/*!< in: index */
741-
dtuple_t* entry,/*!< in: entry whose data was put to vector */
741+
dtuple_t* entry,/*!< in/out: entry whose data was put to vector */
742742
big_rec_t* vector)/*!< in, own: big rec vector; it is
743743
freed in this function */
744744
{

0 commit comments

Comments
 (0)