mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
MDEV-39306 Index corruption on UPDATE when using versioned InnoDB table (on UPDATE)
Duplicated by:
MDEV-39711 UPDATE on System-Versioned Tables uses the after-value
in Row-format Binlog's before-image
MDEV-24646 ASAN heap-use-after-free in Field_blob::pack /
THD::binlog_update_row upon DML on table with virtual column
vers_update_fields() recomputed virtual fields with VCOL_UPDATE_FOR_READ.
For BLOB virtual columns this mode swaps Field_blob::value and read_value
after the computation (swap_values). After the swap, Field_blob::value
holds the buffer that record[1] (the old row image) points to. The next
VCOL_UPDATE_FOR_WRITE computation, done by fill_record() right after
vers_update_fields(), stores the new value into that buffer and so
overwrites the old row image. As a result, InnoDB builds the old
secondary-index key from the new value (MDEV-39306), and the row-format
binlog writes the new value into the before-image (MDEV-39711), or a
freed buffer (MDEV-24646).
REPLACE had the same problem with record[2]. TABLE::delete_row() saves
the new row into record[2] and turns the conflicting row into history
with vers_update_end(). Its VCOL_UPDATE_FOR_WRITE computation stored
the virtual blobs of the old row into Field_blob::value, the buffer
record[2] points to, so the new row was written with them. Versioned
DELETE had the same problem with record[1]: it points to the virtual
blobs computed with VCOL_UPDATE_FOR_DELETE in Field_blob::value, and
vers_update_end() overwrote them.
The fix removes the virtual fields computation from vers_update_fields().
Its callers now recompute virtual fields with VCOL_UPDATE_FOR_WRITE after
the system fields are set: fill_record() already did, while
update_generated_fields(), multi_update::do_updates() and the RBR
appliers do it explicitly. multi_update::do_updates() still computes
virtual fields twice per row: before BEFORE triggers and after
vers_update_fields(), since the system fields cannot be set before
compare_record(). For REPLACE and DELETE, TABLE::delete_row() keeps
Field_blob::value with remember_blob_values() while it updates the
history row.
In the RBR appliers no VCOL_UPDATE_FOR_WRITE followed
vers_update_fields(), so record[1] was not overwritten there. Their
explicit computation replaces the one removed from vers_update_fields().
It uses VCOL_UPDATE_FOR_WRITE, so an error in a virtual field
expression now stops the slave instead of being turned into a warning.
In the Update_rows applier, vers_insert_history_row() also overwrote
the error of ha_update_row(), so a failed update on the slave was
ignored and its history row was written. The history row is now
written only when the update succeeded.
Based on the MDEV-24646/MDEV-39711 fix by ParadoxV5 (0c2f3de1a2),
which switched vers_update_fields() to VCOL_UPDATE_FOR_WRITE.
This commit is contained in:
@@ -88,3 +88,58 @@ x s e if(row_start = @ins_time, "OLD", "NEW") check_row(row_start, row_end)
|
||||
6 2000-01-01 2018-01-01 NEW CURRENT ROW
|
||||
drop table t,log_tbl;
|
||||
drop procedure log;
|
||||
#
|
||||
# MDEV-39306 Index corruption on UPDATE when using versioned InnoDB
|
||||
# table (on UPDATE)
|
||||
#
|
||||
create table t1 (x int, s date, e date, period for app (s, e),
|
||||
rs timestamp(6) as row start invisible,
|
||||
re timestamp(6) as row end invisible,
|
||||
period for system_time (rs, re),
|
||||
v text as (concat('v-', x)) virtual,
|
||||
w decimal(20,6) as (x + unix_timestamp(rs)) virtual,
|
||||
ws decimal(20,6) as (x + unix_timestamp(rs)) stored,
|
||||
key (v(10)), key (w))
|
||||
with system versioning;
|
||||
insert t1 (x, s, e) values (1, '2000-01-01', '2020-01-01'),
|
||||
(2, '2000-01-01', '2020-01-01');
|
||||
delete from t1 for portion of app from '2005-01-01' to '2010-01-01'
|
||||
where x = 1;
|
||||
update t1 for portion of app from '2005-01-01' to '2010-01-01' set x= 3
|
||||
where x = 2;
|
||||
check table t1 extended;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
select x, s, e, v, ws = x + unix_timestamp(rs) as ok
|
||||
from t1 for system_time all force index (v)
|
||||
order by x, s, rs;
|
||||
x s e v ok
|
||||
1 2000-01-01 2020-01-01 v-1 1
|
||||
1 2000-01-01 2005-01-01 v-1 1
|
||||
1 2010-01-01 2020-01-01 v-1 1
|
||||
2 2000-01-01 2020-01-01 v-2 1
|
||||
2 2000-01-01 2005-01-01 v-2 1
|
||||
2 2010-01-01 2020-01-01 v-2 1
|
||||
3 2005-01-01 2010-01-01 v-3 1
|
||||
drop table t1;
|
||||
# Error from virtual fields computed after the system fields
|
||||
set @save_sql_mode= @@sql_mode;
|
||||
set sql_mode= 'strict_all_tables,error_for_division_by_zero';
|
||||
create table t1 (x int, s date, e date, period for app (s, e),
|
||||
rs timestamp(6) as row start invisible,
|
||||
re timestamp(6) as row end invisible,
|
||||
period for system_time (rs, re),
|
||||
b int as (1 div (year(rs) - 2000 + year(e) - 2005)) stored)
|
||||
with system versioning engine=innodb;
|
||||
set timestamp= unix_timestamp('2001-01-01 00:00:00');
|
||||
insert t1 (x, s, e) values (1, '2000-01-01', '2020-01-01');
|
||||
set timestamp= unix_timestamp('2000-06-01 00:00:00');
|
||||
update t1 for portion of app from '2005-01-01' to '2010-01-01' set x= 2;
|
||||
ERROR 22012: Division by 0
|
||||
set timestamp= default;
|
||||
select x, s, e, b from t1;
|
||||
x s e b
|
||||
1 2000-01-01 2020-01-01 0
|
||||
drop table t1;
|
||||
set sql_mode= @save_sql_mode;
|
||||
# End of 10.11 tests
|
||||
|
||||
@@ -64,4 +64,62 @@ select *, if(row_start = @ins_time, "OLD", "NEW"), check_row(row_start, row_end)
|
||||
|
||||
drop table t,log_tbl;
|
||||
drop procedure log;
|
||||
|
||||
if ($default_engine == MEMORY)
|
||||
{
|
||||
--disable_query_log
|
||||
set default_storage_engine= myisam;
|
||||
--enable_query_log
|
||||
}
|
||||
--echo #
|
||||
--echo # MDEV-39306 Index corruption on UPDATE when using versioned InnoDB
|
||||
--echo # table (on UPDATE)
|
||||
--echo #
|
||||
create table t1 (x int, s date, e date, period for app (s, e),
|
||||
rs timestamp(6) as row start invisible,
|
||||
re timestamp(6) as row end invisible,
|
||||
period for system_time (rs, re),
|
||||
v text as (concat('v-', x)) virtual,
|
||||
w decimal(20,6) as (x + unix_timestamp(rs)) virtual,
|
||||
ws decimal(20,6) as (x + unix_timestamp(rs)) stored,
|
||||
key (v(10)), key (w))
|
||||
with system versioning;
|
||||
insert t1 (x, s, e) values (1, '2000-01-01', '2020-01-01'),
|
||||
(2, '2000-01-01', '2020-01-01');
|
||||
delete from t1 for portion of app from '2005-01-01' to '2010-01-01'
|
||||
where x = 1;
|
||||
update t1 for portion of app from '2005-01-01' to '2010-01-01' set x= 3
|
||||
where x = 2;
|
||||
check table t1 extended;
|
||||
select x, s, e, v, ws = x + unix_timestamp(rs) as ok
|
||||
from t1 for system_time all force index (v)
|
||||
order by x, s, rs;
|
||||
drop table t1;
|
||||
|
||||
--echo # Error from virtual fields computed after the system fields
|
||||
# Only the left part [2000, 2005), inserted by period_make_insert(),
|
||||
# divides by zero, and only with the new row_start.
|
||||
set @save_sql_mode= @@sql_mode;
|
||||
set sql_mode= 'strict_all_tables,error_for_division_by_zero';
|
||||
create table t1 (x int, s date, e date, period for app (s, e),
|
||||
rs timestamp(6) as row start invisible,
|
||||
re timestamp(6) as row end invisible,
|
||||
period for system_time (rs, re),
|
||||
b int as (1 div (year(rs) - 2000 + year(e) - 2005)) stored)
|
||||
with system versioning engine=innodb;
|
||||
set timestamp= unix_timestamp('2001-01-01 00:00:00');
|
||||
insert t1 (x, s, e) values (1, '2000-01-01', '2020-01-01');
|
||||
set timestamp= unix_timestamp('2000-06-01 00:00:00');
|
||||
--error ER_DIVISION_BY_ZERO
|
||||
update t1 for portion of app from '2005-01-01' to '2010-01-01' set x= 2;
|
||||
set timestamp= default;
|
||||
select x, s, e, b from t1;
|
||||
drop table t1;
|
||||
set sql_mode= @save_sql_mode;
|
||||
--disable_query_log
|
||||
eval set default_storage_engine= $default_engine;
|
||||
--enable_query_log
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
source suite/versioning/common_finish.inc;
|
||||
|
||||
@@ -168,3 +168,124 @@ f1 f3 check_row(row_start, row_end)
|
||||
2 2 HISTORICAL ROW
|
||||
# Cleanup
|
||||
drop tables t1, t2;
|
||||
#
|
||||
# MDEV-39306 Index corruption on UPDATE when using versioned InnoDB
|
||||
# table (on UPDATE)
|
||||
#
|
||||
# Virtual blob over row_end, computed before and after row_end is set
|
||||
create table t1 (id int primary key, a text,
|
||||
rs timestamp(6) as row start invisible,
|
||||
re timestamp(6) as row end invisible,
|
||||
period for system_time (rs, re),
|
||||
v text as (concat(a, '-', unix_timestamp(re))) virtual,
|
||||
key (v(40)))
|
||||
with system versioning;
|
||||
insert t1 (id, a) values (1, 'one'), (2, 'two');
|
||||
delete from t1 where id = 1;
|
||||
check table t1 extended;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
select id, a from t1 for system_time all force index (v) order by id;
|
||||
id a
|
||||
1 one
|
||||
2 two
|
||||
drop table t1;
|
||||
# AFTER DELETE trigger reads OLD of a virtual blob over row_end
|
||||
create table t1 (id int primary key, a text,
|
||||
rs timestamp(6) as row start invisible,
|
||||
re timestamp(6) as row end invisible,
|
||||
period for system_time (rs, re),
|
||||
v text as (concat('v-', a, '-', unix_timestamp(re))) virtual)
|
||||
with system versioning;
|
||||
create table log (id int, ok int);
|
||||
create trigger tr after delete on t1 for each row
|
||||
insert log values (old.id,
|
||||
old.v = concat('v-', old.a, '-', unix_timestamp(old.re)));
|
||||
insert t1 (id, a) values (1, 'one'), (2, 'two');
|
||||
delete from t1;
|
||||
select * from log order by id;
|
||||
id ok
|
||||
1 1
|
||||
2 1
|
||||
drop table t1, log;
|
||||
# Multi-table DELETE
|
||||
create table t1 (id int primary key, a text,
|
||||
rs timestamp(6) as row start invisible,
|
||||
re timestamp(6) as row end invisible,
|
||||
period for system_time (rs, re),
|
||||
v text as (concat('v-', a, '-', unix_timestamp(re))) virtual)
|
||||
with system versioning;
|
||||
create table log (id int, ok int);
|
||||
create trigger tr after delete on t1 for each row
|
||||
insert log values (old.id,
|
||||
old.v = concat('v-', old.a, '-', unix_timestamp(old.re)));
|
||||
create table t2 (x int);
|
||||
insert t1 (id, a) values (1, 'one'), (2, 'two');
|
||||
insert t2 values (1);
|
||||
delete t1 from t1, t2;
|
||||
select * from log order by id;
|
||||
id ok
|
||||
1 1
|
||||
2 1
|
||||
drop table t1, t2, log;
|
||||
# History row already exists: the row is deleted
|
||||
create table t1 (id int primary key, a text, v text as (concat('v-', a)) virtual,
|
||||
key (v(10))) with system versioning;
|
||||
create table log (id int, old_v text);
|
||||
create trigger tr after delete on t1 for each row
|
||||
insert log values (old.id, old.v);
|
||||
set timestamp= unix_timestamp('2001-01-01 00:00:00');
|
||||
insert t1 (id, a) values (1, 'one');
|
||||
delete from t1;
|
||||
insert t1 (id, a) values (1, 'two');
|
||||
delete from t1;
|
||||
set timestamp= default;
|
||||
check table t1 extended;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
select * from log order by old_v;
|
||||
id old_v
|
||||
1 v-one
|
||||
1 v-two
|
||||
select id, a, v from t1 for system_time all force index (v) order by v;
|
||||
id a v
|
||||
1 one v-one
|
||||
drop table t1, log;
|
||||
# Negative lifetime: the row is deleted
|
||||
create table t1 (id int primary key, a text, v text as (concat('v-', a)) virtual,
|
||||
key (v(10))) with system versioning;
|
||||
create table log (id int, old_v text);
|
||||
create trigger tr after delete on t1 for each row
|
||||
insert log values (old.id, old.v);
|
||||
set timestamp= unix_timestamp('2001-01-01 00:00:00');
|
||||
insert t1 (id, a) values (1, 'one');
|
||||
set timestamp= unix_timestamp('2000-06-01 00:00:00');
|
||||
delete from t1;
|
||||
set timestamp= default;
|
||||
check table t1 extended;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
select * from log;
|
||||
id old_v
|
||||
1 v-one
|
||||
select id, a, v from t1 for system_time all;
|
||||
id a v
|
||||
drop table t1, log;
|
||||
# ha_update_row() of the history row fails
|
||||
create table p (id int primary key, a text, v text as (concat('v-', a)) virtual)
|
||||
with system versioning engine=innodb;
|
||||
create table c (id int, foreign key (id) references p (id)) engine=innodb;
|
||||
insert p (id, a) values (1, 'one');
|
||||
insert c values (1);
|
||||
delete from p;
|
||||
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`c`, CONSTRAINT `c_ibfk_1` FOREIGN KEY (`id`) REFERENCES `p` (`id`))
|
||||
delete p from p, c;
|
||||
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`c`, CONSTRAINT `c_ibfk_1` FOREIGN KEY (`id`) REFERENCES `p` (`id`))
|
||||
check table p extended;
|
||||
Table Op Msg_type Msg_text
|
||||
test.p check status OK
|
||||
select id, a, v from p for system_time all;
|
||||
id a v
|
||||
1 one v-one
|
||||
drop table c, p;
|
||||
# End of 10.11 tests
|
||||
|
||||
@@ -3767,5 +3767,24 @@ x row_end
|
||||
set timestamp= default;
|
||||
set time_zone= default;
|
||||
drop table t1;
|
||||
#
|
||||
# MDEV-39306 Index corruption on UPDATE when using versioned InnoDB
|
||||
# table (on UPDATE)
|
||||
#
|
||||
create table t1 (id int primary key, a text, v text as (concat('v-', a)) virtual,
|
||||
key (v(10))) with system versioning
|
||||
partition by system_time (partition p0 history, partition pn current);
|
||||
insert t1 (id, a) values (1, 'one'), (2, 'two');
|
||||
delete from t1 where id = 1;
|
||||
replace t1 (id, a) values (2, 'final');
|
||||
check table t1 extended;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
select id, a, v from t1 for system_time all force index (v) order by v;
|
||||
id a v
|
||||
2 final v-final
|
||||
1 one v-one
|
||||
2 two v-two
|
||||
drop table t1;
|
||||
# End of 10.11 tests
|
||||
set global innodb_stats_persistent= @save_persistent;
|
||||
|
||||
@@ -165,4 +165,70 @@ insert into t1 (c1) values (1);
|
||||
replace delayed into t1 (c1) values (1);
|
||||
drop table t1;
|
||||
set sql_mode=@old_mode;
|
||||
#
|
||||
# MDEV-39306 Index corruption on UPDATE when using versioned InnoDB
|
||||
# table (on UPDATE)
|
||||
#
|
||||
# REPLACE turns the conflicting row into history
|
||||
create table t1 (id int primary key, a text, v text as (concat('v-', a)) virtual,
|
||||
key (v(10))) with system versioning;
|
||||
insert t1 (id, a) values (1, 'old'), (2, 'start');
|
||||
replace t1 (id, a) values (2, 'final');
|
||||
check table t1 extended;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
select id, a, v from t1 for system_time all force index (v) order by v;
|
||||
id a v
|
||||
2 final v-final
|
||||
1 old v-old
|
||||
2 start v-start
|
||||
drop table t1;
|
||||
# History row already exists: the conflicting row is deleted
|
||||
create table t1 (id int primary key, a text, v text as (concat('v-', a)) virtual,
|
||||
key (v(10))) with system versioning;
|
||||
set timestamp= unix_timestamp('2001-01-01 00:00:00');
|
||||
insert t1 (id, a) values (1, 'old'), (2, 'start');
|
||||
replace t1 (id, a) values (2, 'middle');
|
||||
replace t1 (id, a) values (2, 'final');
|
||||
set timestamp= default;
|
||||
check table t1 extended;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
select id, a, v from t1 for system_time all force index (v) order by v;
|
||||
id a v
|
||||
2 final v-final
|
||||
1 old v-old
|
||||
2 start v-start
|
||||
drop table t1;
|
||||
# Negative lifetime: the conflicting row is deleted, no history row
|
||||
create table t1 (id int primary key, a text, v text as (concat('v-', a)) virtual,
|
||||
key (v(10))) with system versioning;
|
||||
set timestamp= unix_timestamp('2001-01-01 00:00:00');
|
||||
insert t1 (id, a) values (1, 'old'), (2, 'start');
|
||||
set timestamp= unix_timestamp('2000-06-01 00:00:00');
|
||||
replace t1 (id, a) values (2, 'final');
|
||||
set timestamp= default;
|
||||
check table t1 extended;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
select id, a, v from t1 for system_time all force index (v) order by v;
|
||||
id a v
|
||||
2 final v-final
|
||||
1 old v-old
|
||||
drop table t1;
|
||||
# Two conflicting rows
|
||||
create table t1 (id int primary key, u int unique, a text,
|
||||
v text as (concat('v-', a)) virtual, key (v(10)))
|
||||
with system versioning;
|
||||
insert t1 (id, u, a) values (1, 1, 'one'), (2, 2, 'two');
|
||||
replace t1 (id, u, a) values (1, 2, 'final');
|
||||
check table t1 extended;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
select id, u, a, v from t1 for system_time all force index (v) order by v;
|
||||
id u a v
|
||||
1 2 final v-final
|
||||
1 1 one v-one
|
||||
2 2 two v-two
|
||||
drop table t1;
|
||||
# End of 10.11 tests
|
||||
|
||||
@@ -74,6 +74,101 @@ i
|
||||
connection master;
|
||||
drop table t;
|
||||
#
|
||||
# MDEV-24646 ASAN heap-use-after-free in Field_blob::pack /
|
||||
# THD::binlog_update_row upon DML on table with virtual column
|
||||
#
|
||||
connection master;
|
||||
create table t (a text, v blob as (a) virtual)
|
||||
with system versioning as select 'foo' as a;
|
||||
update t set a='bar';
|
||||
connection slave;
|
||||
select * from t;
|
||||
v a
|
||||
bar bar
|
||||
connection master;
|
||||
drop table t;
|
||||
#
|
||||
# MDEV-39711 UPDATE on System-Versioned Tables uses the after-value
|
||||
# in Row-format Binlog's before-image
|
||||
# for non-trivial Text/Blob Virtual Columns
|
||||
#
|
||||
connection master;
|
||||
create table t (a text, v text as (right(a, 1)) virtual)
|
||||
with system versioning as select 'before' as a;
|
||||
update t set a='after';
|
||||
connection slave;
|
||||
select * from t;
|
||||
v a
|
||||
r after
|
||||
connection master;
|
||||
drop table t;
|
||||
#
|
||||
# MDEV-39306 Index corruption on UPDATE when using versioned InnoDB
|
||||
# table (on UPDATE)
|
||||
# Unversioned master, versioned slave, minimal row image
|
||||
#
|
||||
connection master;
|
||||
set statement sql_log_bin=0 for
|
||||
create table t (id int primary key, a text, v text as (right(a, 1)) virtual,
|
||||
key (v(10))) engine=innodb;
|
||||
connection slave;
|
||||
set statement sql_log_bin=0 for
|
||||
create table t (id int primary key, a text, v text as (right(a, 1)) virtual,
|
||||
rs timestamp(6) as row start invisible,
|
||||
re timestamp(6) as row end invisible,
|
||||
period for system_time (rs, re),
|
||||
w decimal(20,6) as (id + unix_timestamp(rs)) virtual,
|
||||
ws decimal(20,6) as (id + unix_timestamp(rs)) stored,
|
||||
key (v(10)), key (w), key (ws))
|
||||
engine=innodb with system versioning;
|
||||
connection master;
|
||||
set @old_row_image= @@binlog_row_image;
|
||||
set binlog_row_image= minimal;
|
||||
insert t (id, a) values (1, 'before'), (2, 'start');
|
||||
update t set a= 'after';
|
||||
set binlog_row_image= @old_row_image;
|
||||
connection slave;
|
||||
check table t extended;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t check status OK
|
||||
select id, a, v, ws = id + unix_timestamp(rs) as ok
|
||||
from t for system_time all force index (v) order by v, id;
|
||||
id a v ok
|
||||
1 before e 1
|
||||
1 after r 1
|
||||
2 after r 1
|
||||
2 start t 1
|
||||
connection master;
|
||||
drop table t;
|
||||
#
|
||||
# MDEV-39306 Index corruption on UPDATE when using versioned InnoDB
|
||||
# table (on UPDATE)
|
||||
# Unversioned master, versioned slave, failed update
|
||||
#
|
||||
connection master;
|
||||
set statement sql_log_bin=0 for
|
||||
create table t (id int primary key, a int) engine=innodb;
|
||||
connection slave;
|
||||
call mtr.add_suppression("Slave SQL.*Duplicate entry");
|
||||
set statement sql_log_bin=0 for
|
||||
create table t (id int primary key, a int, unique (a))
|
||||
engine=innodb with system versioning;
|
||||
connection master;
|
||||
insert t values (1, 1), (2, 2);
|
||||
update t set a= 2 where id = 1;
|
||||
include/wait_for_slave_sql_error_and_skip.inc [errno=1062]
|
||||
connection slave;
|
||||
select id, a, row_end > now(6) as current
|
||||
from t for system_time all order by id, current;
|
||||
id a current
|
||||
1 1 1
|
||||
2 2 1
|
||||
connection master;
|
||||
drop table t;
|
||||
connection slave;
|
||||
connection master;
|
||||
# End of 10.11 tests
|
||||
#
|
||||
# MDEV-25347 DML events for auto-partitioned tables are written into binary log twice
|
||||
#
|
||||
flush binary logs;
|
||||
|
||||
@@ -490,4 +490,94 @@ create table t (a int, b int, c int with system versioning, unique(b));
|
||||
insert into t (b) values (1);
|
||||
insert into t (b) values (1),(1) on duplicate key update a = 3;
|
||||
drop table t;
|
||||
#
|
||||
# MDEV-39306 Index corruption on UPDATE when using versioned InnoDB
|
||||
# table (on UPDATE)
|
||||
#
|
||||
create table t1 (c1 date key, c2 text as (c1) virtual, index (c2(10)))
|
||||
with system versioning;
|
||||
insert t1 values ('2000-01-01', default), ('2000-01-03', default);
|
||||
update t1 set c1= c1 + interval 1 day;
|
||||
check table t1 extended;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
select c1, c2 from t1 for system_time all force index (c2) order by c2;
|
||||
c1 c2
|
||||
2000-01-01 2000-01-01
|
||||
2000-01-02 2000-01-02
|
||||
2000-01-03 2000-01-03
|
||||
2000-01-04 2000-01-04
|
||||
drop table t1;
|
||||
# Multi-table UPDATE
|
||||
create table t1 (c1 date key, c2 text as (c1) virtual, index (c2(10)))
|
||||
with system versioning;
|
||||
create table t2 (x int) engine=myisam;
|
||||
insert t1 values ('2000-01-01', default), ('2000-01-03', default);
|
||||
insert t2 values (1);
|
||||
update t1, t2 set t1.c1= t1.c1 + interval 1 day;
|
||||
check table t1 extended;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
select c1, c2 from t1 for system_time all force index (c2) order by c2;
|
||||
c1 c2
|
||||
2000-01-01 2000-01-01
|
||||
2000-01-02 2000-01-02
|
||||
2000-01-03 2000-01-03
|
||||
2000-01-04 2000-01-04
|
||||
drop table t1, t2;
|
||||
# INSERT ... ON DUPLICATE KEY UPDATE
|
||||
create table t1 (id int primary key, a text, v text as (concat('v-', a)) virtual,
|
||||
key (v(10))) with system versioning;
|
||||
insert t1 (id, a) values (1, 'old'), (2, 'start');
|
||||
insert t1 (id, a) values (2, 'x') on duplicate key update a= 'final';
|
||||
check table t1 extended;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
select id, a, v from t1 for system_time all force index (v) order by v;
|
||||
id a v
|
||||
2 final v-final
|
||||
1 old v-old
|
||||
2 start v-start
|
||||
drop table t1;
|
||||
# Virtual fields of the new row are computed once
|
||||
set @save_sql_mode= @@sql_mode;
|
||||
set sql_mode= '';
|
||||
create table t1 (a varchar(10), b int as (a) virtual)
|
||||
with system versioning;
|
||||
insert t1 (a) values ('x');
|
||||
update t1 set a= 'y';
|
||||
Warnings:
|
||||
Warning 1366 Incorrect integer value: 'x' for column `test`.`t1`.`b` at row 1
|
||||
Warning 1366 Incorrect integer value: 'y' for column `test`.`t1`.`b` at row 1
|
||||
Warning 1366 Incorrect integer value: 'x' for column `test`.`t1`.`b` at row 1
|
||||
Warning 1366 Incorrect integer value: 'x' for column `test`.`t1`.`b` at row 2
|
||||
drop table t1;
|
||||
set sql_mode= @save_sql_mode;
|
||||
# Error from virtual fields computed after the system fields
|
||||
set @save_sql_mode= @@sql_mode;
|
||||
set sql_mode= 'strict_all_tables,error_for_division_by_zero';
|
||||
create table t1 (a int,
|
||||
rs timestamp(6) as row start invisible,
|
||||
re timestamp(6) as row end invisible,
|
||||
period for system_time (rs, re),
|
||||
b int as (1 div (year(rs) - 2000)) stored)
|
||||
with system versioning engine=innodb;
|
||||
create table t2 (x int) engine=myisam;
|
||||
set timestamp= unix_timestamp('2001-01-01 00:00:00');
|
||||
insert t1 (a) values (1);
|
||||
insert t2 values (1);
|
||||
set timestamp= unix_timestamp('2000-06-01 00:00:00');
|
||||
flush status;
|
||||
update t1, t2 set t1.a= 2;
|
||||
ERROR 22012: Division by 0
|
||||
# Non-zero: t1 is updated through the temporary table
|
||||
show status like 'Handler_tmp_write';
|
||||
Variable_name Value
|
||||
Handler_tmp_write 1
|
||||
set timestamp= default;
|
||||
select a, b from t1;
|
||||
a b
|
||||
1 1
|
||||
drop table t1, t2;
|
||||
set sql_mode= @save_sql_mode;
|
||||
# End of 10.11 tests
|
||||
|
||||
@@ -121,4 +121,114 @@ select f1, f3, check_row(row_start, row_end) from t1 for system_time all order b
|
||||
--echo # Cleanup
|
||||
drop tables t1, t2;
|
||||
|
||||
if ($default_engine == MEMORY)
|
||||
{
|
||||
--disable_query_log
|
||||
set default_storage_engine= myisam;
|
||||
--enable_query_log
|
||||
}
|
||||
--echo #
|
||||
--echo # MDEV-39306 Index corruption on UPDATE when using versioned InnoDB
|
||||
--echo # table (on UPDATE)
|
||||
--echo #
|
||||
--echo # Virtual blob over row_end, computed before and after row_end is set
|
||||
create table t1 (id int primary key, a text,
|
||||
rs timestamp(6) as row start invisible,
|
||||
re timestamp(6) as row end invisible,
|
||||
period for system_time (rs, re),
|
||||
v text as (concat(a, '-', unix_timestamp(re))) virtual,
|
||||
key (v(40)))
|
||||
with system versioning;
|
||||
insert t1 (id, a) values (1, 'one'), (2, 'two');
|
||||
delete from t1 where id = 1;
|
||||
check table t1 extended;
|
||||
select id, a from t1 for system_time all force index (v) order by id;
|
||||
drop table t1;
|
||||
|
||||
--echo # AFTER DELETE trigger reads OLD of a virtual blob over row_end
|
||||
create table t1 (id int primary key, a text,
|
||||
rs timestamp(6) as row start invisible,
|
||||
re timestamp(6) as row end invisible,
|
||||
period for system_time (rs, re),
|
||||
v text as (concat('v-', a, '-', unix_timestamp(re))) virtual)
|
||||
with system versioning;
|
||||
create table log (id int, ok int);
|
||||
create trigger tr after delete on t1 for each row
|
||||
insert log values (old.id,
|
||||
old.v = concat('v-', old.a, '-', unix_timestamp(old.re)));
|
||||
insert t1 (id, a) values (1, 'one'), (2, 'two');
|
||||
delete from t1;
|
||||
select * from log order by id;
|
||||
drop table t1, log;
|
||||
|
||||
--echo # Multi-table DELETE
|
||||
create table t1 (id int primary key, a text,
|
||||
rs timestamp(6) as row start invisible,
|
||||
re timestamp(6) as row end invisible,
|
||||
period for system_time (rs, re),
|
||||
v text as (concat('v-', a, '-', unix_timestamp(re))) virtual)
|
||||
with system versioning;
|
||||
create table log (id int, ok int);
|
||||
create trigger tr after delete on t1 for each row
|
||||
insert log values (old.id,
|
||||
old.v = concat('v-', old.a, '-', unix_timestamp(old.re)));
|
||||
create table t2 (x int);
|
||||
insert t1 (id, a) values (1, 'one'), (2, 'two');
|
||||
insert t2 values (1);
|
||||
delete t1 from t1, t2;
|
||||
select * from log order by id;
|
||||
drop table t1, t2, log;
|
||||
|
||||
--echo # History row already exists: the row is deleted
|
||||
create table t1 (id int primary key, a text, v text as (concat('v-', a)) virtual,
|
||||
key (v(10))) with system versioning;
|
||||
create table log (id int, old_v text);
|
||||
create trigger tr after delete on t1 for each row
|
||||
insert log values (old.id, old.v);
|
||||
set timestamp= unix_timestamp('2001-01-01 00:00:00');
|
||||
insert t1 (id, a) values (1, 'one');
|
||||
delete from t1;
|
||||
insert t1 (id, a) values (1, 'two');
|
||||
delete from t1;
|
||||
set timestamp= default;
|
||||
check table t1 extended;
|
||||
select * from log order by old_v;
|
||||
select id, a, v from t1 for system_time all force index (v) order by v;
|
||||
drop table t1, log;
|
||||
|
||||
--echo # Negative lifetime: the row is deleted
|
||||
create table t1 (id int primary key, a text, v text as (concat('v-', a)) virtual,
|
||||
key (v(10))) with system versioning;
|
||||
create table log (id int, old_v text);
|
||||
create trigger tr after delete on t1 for each row
|
||||
insert log values (old.id, old.v);
|
||||
set timestamp= unix_timestamp('2001-01-01 00:00:00');
|
||||
insert t1 (id, a) values (1, 'one');
|
||||
set timestamp= unix_timestamp('2000-06-01 00:00:00');
|
||||
delete from t1;
|
||||
set timestamp= default;
|
||||
check table t1 extended;
|
||||
select * from log;
|
||||
select id, a, v from t1 for system_time all;
|
||||
drop table t1, log;
|
||||
|
||||
--echo # ha_update_row() of the history row fails
|
||||
create table p (id int primary key, a text, v text as (concat('v-', a)) virtual)
|
||||
with system versioning engine=innodb;
|
||||
create table c (id int, foreign key (id) references p (id)) engine=innodb;
|
||||
insert p (id, a) values (1, 'one');
|
||||
insert c values (1);
|
||||
--error ER_ROW_IS_REFERENCED_2
|
||||
delete from p;
|
||||
--error ER_ROW_IS_REFERENCED_2
|
||||
delete p from p, c;
|
||||
check table p extended;
|
||||
select id, a, v from p for system_time all;
|
||||
drop table c, p;
|
||||
--disable_query_log
|
||||
eval set default_storage_engine= $default_engine;
|
||||
--enable_query_log
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
--source suite/versioning/common_finish.inc
|
||||
|
||||
@@ -2957,6 +2957,29 @@ set timestamp= default;
|
||||
set time_zone= default;
|
||||
drop table t1;
|
||||
|
||||
if ($default_engine == MEMORY)
|
||||
{
|
||||
--disable_query_log
|
||||
set default_storage_engine= myisam;
|
||||
--enable_query_log
|
||||
}
|
||||
--echo #
|
||||
--echo # MDEV-39306 Index corruption on UPDATE when using versioned InnoDB
|
||||
--echo # table (on UPDATE)
|
||||
--echo #
|
||||
create table t1 (id int primary key, a text, v text as (concat('v-', a)) virtual,
|
||||
key (v(10))) with system versioning
|
||||
partition by system_time (partition p0 history, partition pn current);
|
||||
insert t1 (id, a) values (1, 'one'), (2, 'two');
|
||||
delete from t1 where id = 1;
|
||||
replace t1 (id, a) values (2, 'final');
|
||||
check table t1 extended;
|
||||
select id, a, v from t1 for system_time all force index (v) order by v;
|
||||
drop table t1;
|
||||
--disable_query_log
|
||||
eval set default_storage_engine= $default_engine;
|
||||
--enable_query_log
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
set global innodb_stats_persistent= @save_persistent;
|
||||
|
||||
@@ -195,6 +195,62 @@ drop table t1;
|
||||
|
||||
set sql_mode=@old_mode;
|
||||
|
||||
if ($default_engine == MEMORY)
|
||||
{
|
||||
--disable_query_log
|
||||
set default_storage_engine= myisam;
|
||||
--enable_query_log
|
||||
}
|
||||
--echo #
|
||||
--echo # MDEV-39306 Index corruption on UPDATE when using versioned InnoDB
|
||||
--echo # table (on UPDATE)
|
||||
--echo #
|
||||
--echo # REPLACE turns the conflicting row into history
|
||||
create table t1 (id int primary key, a text, v text as (concat('v-', a)) virtual,
|
||||
key (v(10))) with system versioning;
|
||||
insert t1 (id, a) values (1, 'old'), (2, 'start');
|
||||
replace t1 (id, a) values (2, 'final');
|
||||
check table t1 extended;
|
||||
select id, a, v from t1 for system_time all force index (v) order by v;
|
||||
drop table t1;
|
||||
|
||||
--echo # History row already exists: the conflicting row is deleted
|
||||
create table t1 (id int primary key, a text, v text as (concat('v-', a)) virtual,
|
||||
key (v(10))) with system versioning;
|
||||
set timestamp= unix_timestamp('2001-01-01 00:00:00');
|
||||
insert t1 (id, a) values (1, 'old'), (2, 'start');
|
||||
replace t1 (id, a) values (2, 'middle');
|
||||
replace t1 (id, a) values (2, 'final');
|
||||
set timestamp= default;
|
||||
check table t1 extended;
|
||||
select id, a, v from t1 for system_time all force index (v) order by v;
|
||||
drop table t1;
|
||||
|
||||
--echo # Negative lifetime: the conflicting row is deleted, no history row
|
||||
create table t1 (id int primary key, a text, v text as (concat('v-', a)) virtual,
|
||||
key (v(10))) with system versioning;
|
||||
set timestamp= unix_timestamp('2001-01-01 00:00:00');
|
||||
insert t1 (id, a) values (1, 'old'), (2, 'start');
|
||||
set timestamp= unix_timestamp('2000-06-01 00:00:00');
|
||||
replace t1 (id, a) values (2, 'final');
|
||||
set timestamp= default;
|
||||
check table t1 extended;
|
||||
select id, a, v from t1 for system_time all force index (v) order by v;
|
||||
drop table t1;
|
||||
|
||||
--echo # Two conflicting rows
|
||||
create table t1 (id int primary key, u int unique, a text,
|
||||
v text as (concat('v-', a)) virtual, key (v(10)))
|
||||
with system versioning;
|
||||
insert t1 (id, u, a) values (1, 1, 'one'), (2, 2, 'two');
|
||||
replace t1 (id, u, a) values (1, 2, 'final');
|
||||
check table t1 extended;
|
||||
select id, u, a, v from t1 for system_time all force index (v) order by v;
|
||||
drop table t1;
|
||||
--disable_query_log
|
||||
eval set default_storage_engine= $default_engine;
|
||||
--enable_query_log
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
--source suite/versioning/common_finish.inc
|
||||
|
||||
@@ -71,4 +71,94 @@ select * from t;
|
||||
--connection master
|
||||
drop table t;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-24646 ASAN heap-use-after-free in Field_blob::pack /
|
||||
--echo # THD::binlog_update_row upon DML on table with virtual column
|
||||
--echo #
|
||||
--connection master
|
||||
create table t (a text, v blob as (a) virtual)
|
||||
with system versioning as select 'foo' as a;
|
||||
|
||||
update t set a='bar';
|
||||
--sync_slave_with_master
|
||||
select * from t;
|
||||
--connection master
|
||||
drop table t;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-39711 UPDATE on System-Versioned Tables uses the after-value
|
||||
--echo # in Row-format Binlog's before-image
|
||||
--echo # for non-trivial Text/Blob Virtual Columns
|
||||
--echo #
|
||||
--connection master
|
||||
create table t (a text, v text as (right(a, 1)) virtual)
|
||||
with system versioning as select 'before' as a;
|
||||
|
||||
update t set a='after';
|
||||
--sync_slave_with_master
|
||||
select * from t;
|
||||
--connection master
|
||||
drop table t;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-39306 Index corruption on UPDATE when using versioned InnoDB
|
||||
--echo # table (on UPDATE)
|
||||
--echo # Unversioned master, versioned slave, minimal row image
|
||||
--echo #
|
||||
--connection master
|
||||
set statement sql_log_bin=0 for
|
||||
create table t (id int primary key, a text, v text as (right(a, 1)) virtual,
|
||||
key (v(10))) engine=innodb;
|
||||
--connection slave
|
||||
set statement sql_log_bin=0 for
|
||||
create table t (id int primary key, a text, v text as (right(a, 1)) virtual,
|
||||
rs timestamp(6) as row start invisible,
|
||||
re timestamp(6) as row end invisible,
|
||||
period for system_time (rs, re),
|
||||
w decimal(20,6) as (id + unix_timestamp(rs)) virtual,
|
||||
ws decimal(20,6) as (id + unix_timestamp(rs)) stored,
|
||||
key (v(10)), key (w), key (ws))
|
||||
engine=innodb with system versioning;
|
||||
--connection master
|
||||
set @old_row_image= @@binlog_row_image;
|
||||
set binlog_row_image= minimal;
|
||||
insert t (id, a) values (1, 'before'), (2, 'start');
|
||||
update t set a= 'after';
|
||||
set binlog_row_image= @old_row_image;
|
||||
--sync_slave_with_master
|
||||
check table t extended;
|
||||
select id, a, v, ws = id + unix_timestamp(rs) as ok
|
||||
from t for system_time all force index (v) order by v, id;
|
||||
--connection master
|
||||
drop table t;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-39306 Index corruption on UPDATE when using versioned InnoDB
|
||||
--echo # table (on UPDATE)
|
||||
--echo # Unversioned master, versioned slave, failed update
|
||||
--echo #
|
||||
--connection master
|
||||
set statement sql_log_bin=0 for
|
||||
create table t (id int primary key, a int) engine=innodb;
|
||||
--connection slave
|
||||
call mtr.add_suppression("Slave SQL.*Duplicate entry");
|
||||
set statement sql_log_bin=0 for
|
||||
create table t (id int primary key, a int, unique (a))
|
||||
engine=innodb with system versioning;
|
||||
--connection master
|
||||
insert t values (1, 1), (2, 2);
|
||||
# Duplicate on the slave only
|
||||
update t set a= 2 where id = 1;
|
||||
let $slave_sql_errno= 1062;
|
||||
--source include/wait_for_slave_sql_error_and_skip.inc
|
||||
--connection slave
|
||||
select id, a, row_end > now(6) as current
|
||||
from t for system_time all order by id, current;
|
||||
--connection master
|
||||
drop table t;
|
||||
--sync_slave_with_master
|
||||
--connection master
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
--source rpl_common.inc
|
||||
|
||||
@@ -438,6 +438,83 @@ insert into t (b) values (1);
|
||||
insert into t (b) values (1),(1) on duplicate key update a = 3;
|
||||
drop table t;
|
||||
|
||||
if ($default_engine == MEMORY)
|
||||
{
|
||||
--disable_query_log
|
||||
set default_storage_engine= myisam;
|
||||
--enable_query_log
|
||||
}
|
||||
--echo #
|
||||
--echo # MDEV-39306 Index corruption on UPDATE when using versioned InnoDB
|
||||
--echo # table (on UPDATE)
|
||||
--echo #
|
||||
create table t1 (c1 date key, c2 text as (c1) virtual, index (c2(10)))
|
||||
with system versioning;
|
||||
insert t1 values ('2000-01-01', default), ('2000-01-03', default);
|
||||
update t1 set c1= c1 + interval 1 day;
|
||||
check table t1 extended;
|
||||
select c1, c2 from t1 for system_time all force index (c2) order by c2;
|
||||
drop table t1;
|
||||
|
||||
--echo # Multi-table UPDATE
|
||||
create table t1 (c1 date key, c2 text as (c1) virtual, index (c2(10)))
|
||||
with system versioning;
|
||||
create table t2 (x int) engine=myisam;
|
||||
insert t1 values ('2000-01-01', default), ('2000-01-03', default);
|
||||
insert t2 values (1);
|
||||
update t1, t2 set t1.c1= t1.c1 + interval 1 day;
|
||||
check table t1 extended;
|
||||
select c1, c2 from t1 for system_time all force index (c2) order by c2;
|
||||
drop table t1, t2;
|
||||
|
||||
--echo # INSERT ... ON DUPLICATE KEY UPDATE
|
||||
create table t1 (id int primary key, a text, v text as (concat('v-', a)) virtual,
|
||||
key (v(10))) with system versioning;
|
||||
insert t1 (id, a) values (1, 'old'), (2, 'start');
|
||||
insert t1 (id, a) values (2, 'x') on duplicate key update a= 'final';
|
||||
check table t1 extended;
|
||||
select id, a, v from t1 for system_time all force index (v) order by v;
|
||||
drop table t1;
|
||||
|
||||
--echo # Virtual fields of the new row are computed once
|
||||
set @save_sql_mode= @@sql_mode;
|
||||
set sql_mode= '';
|
||||
create table t1 (a varchar(10), b int as (a) virtual)
|
||||
with system versioning;
|
||||
insert t1 (a) values ('x');
|
||||
update t1 set a= 'y';
|
||||
drop table t1;
|
||||
set sql_mode= @save_sql_mode;
|
||||
|
||||
--echo # Error from virtual fields computed after the system fields
|
||||
set @save_sql_mode= @@sql_mode;
|
||||
set sql_mode= 'strict_all_tables,error_for_division_by_zero';
|
||||
create table t1 (a int,
|
||||
rs timestamp(6) as row start invisible,
|
||||
re timestamp(6) as row end invisible,
|
||||
period for system_time (rs, re),
|
||||
b int as (1 div (year(rs) - 2000)) stored)
|
||||
with system versioning engine=innodb;
|
||||
# One-row MyISAM t2 is a const table and InnoDB t1 is not, so t1 is updated
|
||||
# through a temporary table in multi_update::do_updates()
|
||||
create table t2 (x int) engine=myisam;
|
||||
set timestamp= unix_timestamp('2001-01-01 00:00:00');
|
||||
insert t1 (a) values (1);
|
||||
insert t2 values (1);
|
||||
set timestamp= unix_timestamp('2000-06-01 00:00:00');
|
||||
flush status;
|
||||
--error ER_DIVISION_BY_ZERO
|
||||
update t1, t2 set t1.a= 2;
|
||||
--echo # Non-zero: t1 is updated through the temporary table
|
||||
show status like 'Handler_tmp_write';
|
||||
set timestamp= default;
|
||||
select a, b from t1;
|
||||
drop table t1, t2;
|
||||
set sql_mode= @save_sql_mode;
|
||||
--disable_query_log
|
||||
eval set default_storage_engine= $default_engine;
|
||||
--enable_query_log
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
source suite/versioning/common_finish.inc;
|
||||
|
||||
@@ -7947,10 +7947,16 @@ Write_rows_log_event::write_row(rpl_group_info *rgi,
|
||||
if (table->versioned(VERS_TIMESTAMP))
|
||||
{
|
||||
ulong sec_part;
|
||||
// Check whether a row came from unversioned table and fix vers fields.
|
||||
if (opt_secure_timestamp > SECTIME_REPL ||
|
||||
(table->vers_start_field()->get_timestamp(&sec_part) == 0 && sec_part == 0))
|
||||
table->vers_update_fields();
|
||||
/*
|
||||
If the row came from an unversioned table or the system fields from the
|
||||
master are not trusted, set the system fields here. Then compute
|
||||
vcols, which may depend on them.
|
||||
*/
|
||||
if ((opt_secure_timestamp > SECTIME_REPL ||
|
||||
(table->vers_start_field()->get_timestamp(&sec_part) == 0 &&
|
||||
sec_part == 0)) &&
|
||||
table->vers_update_fields_vcols())
|
||||
DBUG_RETURN(HA_ERR_GENERIC);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -8869,8 +8875,12 @@ Update_rows_log_event::do_exec_row(rpl_group_info *rgi)
|
||||
if (m_table->versioned())
|
||||
{
|
||||
if ((m_vers_from_plain || opt_secure_timestamp > SECTIME_REPL) &&
|
||||
m_table->versioned(VERS_TIMESTAMP))
|
||||
m_table->vers_update_fields();
|
||||
m_table->versioned(VERS_TIMESTAMP) &&
|
||||
m_table->vers_update_fields_vcols())
|
||||
{
|
||||
error= HA_ERR_GENERIC;
|
||||
goto err;
|
||||
}
|
||||
Field *end= m_table->vers_end_field();
|
||||
const uchar *old_ptr= end->ptr_in_record(m_table->record[1]);
|
||||
|
||||
@@ -8884,7 +8894,7 @@ Update_rows_log_event::do_exec_row(rpl_group_info *rgi)
|
||||
tl->trg_event_map= trg_event_map_save;
|
||||
if (unlikely(error == HA_ERR_RECORD_IS_THE_SAME))
|
||||
error= 0;
|
||||
if (m_vers_from_plain && m_table->versioned(VERS_TIMESTAMP))
|
||||
if (!error && m_vers_from_plain && m_table->versioned(VERS_TIMESTAMP))
|
||||
{
|
||||
store_record(m_table, record[2]);
|
||||
error= vers_insert_history_row(m_table);
|
||||
|
||||
@@ -309,6 +309,7 @@ int TABLE::delete_row(bool treat_versioned)
|
||||
{
|
||||
int err= 0;
|
||||
uchar *del_buf= record[replace ? 1 : 0];
|
||||
String *blob_storage= NULL;
|
||||
|
||||
bool delete_row= !treat_versioned
|
||||
|| in_use->lex->vers_conditions.delete_history
|
||||
@@ -326,6 +327,17 @@ int TABLE::delete_row(bool treat_versioned)
|
||||
{
|
||||
store_record(this, record[1]);
|
||||
}
|
||||
/*
|
||||
vers_update_end() computes virtual blobs into Field_blob::value, which
|
||||
holds the virtual blobs of the new row in record[2] (REPLACE) or of the
|
||||
old row in record[1] (computed with VCOL_UPDATE_FOR_DELETE)
|
||||
*/
|
||||
if (s->virtual_not_stored_blob_fields)
|
||||
{
|
||||
blob_storage= (String *) alloca(sizeof(String) *
|
||||
s->virtual_not_stored_blob_fields);
|
||||
remember_blob_values(blob_storage);
|
||||
}
|
||||
vers_update_end();
|
||||
|
||||
Field *row_start= vers_start_field();
|
||||
@@ -335,6 +347,8 @@ int TABLE::delete_row(bool treat_versioned)
|
||||
|
||||
if (likely(!delete_row))
|
||||
err= file->ha_update_row(record[1], record[0]);
|
||||
if (replace && blob_storage)
|
||||
restore_blob_values(blob_storage);
|
||||
if (unlikely(err))
|
||||
{
|
||||
/*
|
||||
@@ -350,7 +364,7 @@ int TABLE::delete_row(bool treat_versioned)
|
||||
|| err == HA_ERR_FOUND_DUPP_UNIQUE
|
||||
|| err == HA_ERR_FOREIGN_DUPLICATE_KEY;
|
||||
if (!delete_row)
|
||||
return err;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (delete_row)
|
||||
@@ -363,6 +377,15 @@ int TABLE::delete_row(bool treat_versioned)
|
||||
if (delete_row)
|
||||
err= file->ha_delete_row(del_buf);
|
||||
|
||||
end:
|
||||
/*
|
||||
DELETE: record[0] is the history row, which AFTER DELETE triggers read as
|
||||
OLD, so its generated blobs stay in Field_blob::value. Free the buffers
|
||||
moved aside by remember_blob_values(), kept for ha_update_row() and
|
||||
ha_delete_row() (the ones of record[1] in single-table DELETE).
|
||||
*/
|
||||
if (!replace && blob_storage)
|
||||
free_blob_values(blob_storage);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -3047,8 +3047,13 @@ int multi_update::do_updates()
|
||||
goto err2;
|
||||
}
|
||||
}
|
||||
if (has_vers_fields && table->versioned())
|
||||
table->vers_update_fields();
|
||||
/*
|
||||
Virtual fields are computed again because they may depend on the
|
||||
system fields, which are set only after compare_record().
|
||||
*/
|
||||
if (has_vers_fields && table->versioned() &&
|
||||
table->vers_update_fields_vcols())
|
||||
goto err2;
|
||||
|
||||
if (unlikely((local_error=
|
||||
table->file->ha_update_row(table->record[1],
|
||||
|
||||
58
sql/table.cc
58
sql/table.cc
@@ -8468,6 +8468,21 @@ void TABLE::restore_blob_values(String *blob_storage)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Free blob values stored by remember_blob_values() without restoring them
|
||||
*/
|
||||
|
||||
void TABLE::free_blob_values(String *blob_storage)
|
||||
{
|
||||
for (Field **vfield_ptr= vfield; *vfield_ptr; vfield_ptr++)
|
||||
{
|
||||
if ((*vfield_ptr)->type() == MYSQL_TYPE_BLOB &&
|
||||
!(*vfield_ptr)->vcol_info->stored_in_db)
|
||||
(blob_storage++)->free();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief
|
||||
Allocate space for keys
|
||||
@@ -9441,10 +9456,13 @@ int TABLE::update_generated_fields()
|
||||
next_number_field= NULL;
|
||||
}
|
||||
|
||||
if (likely(!res) && vfield)
|
||||
res= update_virtual_fields(file, VCOL_UPDATE_FOR_WRITE);
|
||||
if (likely(!res) && versioned())
|
||||
vers_update_fields();
|
||||
if (likely(!res))
|
||||
{
|
||||
if (versioned())
|
||||
res= vers_update_fields_vcols();
|
||||
else if (vfield)
|
||||
res= update_virtual_fields(file, VCOL_UPDATE_FOR_WRITE);
|
||||
}
|
||||
if (likely(!res))
|
||||
res= verify_constraints(false) == VIEW_CHECK_ERROR;
|
||||
return res;
|
||||
@@ -9570,7 +9588,18 @@ bool TABLE::check_period_overlaps(const KEY &key,
|
||||
return true;
|
||||
}
|
||||
|
||||
/* returns true if vers_end_field was updated */
|
||||
/**
|
||||
Set row_start and row_end of the row in record[0] unless they have
|
||||
explicit values. For VERS_TRX_ID only row_end is set, regardless of its
|
||||
explicit value.
|
||||
|
||||
Virtual fields are not computed: the caller must call
|
||||
update_virtual_fields(VCOL_UPDATE_FOR_WRITE) afterwards, or use
|
||||
vers_update_fields_vcols().
|
||||
|
||||
@retval true vers_end_field was updated
|
||||
@retval false vers_end_field has an explicit value and was not updated
|
||||
*/
|
||||
bool TABLE::vers_update_fields()
|
||||
{
|
||||
bool res= false;
|
||||
@@ -9586,12 +9615,27 @@ bool TABLE::vers_update_fields()
|
||||
res= true;
|
||||
}
|
||||
|
||||
if (vfield)
|
||||
update_virtual_fields(file, VCOL_UPDATE_FOR_READ);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Set the system fields and compute virtual fields, which may depend on them.
|
||||
|
||||
@see vers_update_fields()
|
||||
|
||||
@retval 0 Success
|
||||
@retval other Error from update_virtual_fields(VCOL_UPDATE_FOR_WRITE)
|
||||
*/
|
||||
int TABLE::vers_update_fields_vcols()
|
||||
{
|
||||
vers_update_fields();
|
||||
if (vfield)
|
||||
return update_virtual_fields(file, VCOL_UPDATE_FOR_WRITE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void TABLE::vers_update_end()
|
||||
{
|
||||
if (vers_end_field()->store_timestamp(in_use->query_start(),
|
||||
|
||||
@@ -1807,6 +1807,7 @@ public:
|
||||
void move_fields(Field **ptr, const uchar *to, const uchar *from);
|
||||
void remember_blob_values(String *blob_storage);
|
||||
void restore_blob_values(String *blob_storage);
|
||||
void free_blob_values(String *blob_storage);
|
||||
|
||||
uint actual_n_key_parts(KEY *keyinfo);
|
||||
ulong actual_key_flags(KEY *keyinfo);
|
||||
@@ -1947,6 +1948,8 @@ public:
|
||||
inline int delete_row(){ return delete_row<false>(versioned(VERS_TIMESTAMP)); }
|
||||
template <bool replace>
|
||||
int delete_row(bool versioned);
|
||||
/* vers_update_fields() and update_virtual_fields(VCOL_UPDATE_FOR_WRITE) */
|
||||
int vers_update_fields_vcols();
|
||||
/* Used in majority of DML (called from fill_record()) */
|
||||
bool vers_update_fields();
|
||||
/* Used in DELETE, DUP REPLACE and insert history row */
|
||||
|
||||
Reference in New Issue
Block a user