|
Here is a little smaller test (shorter columns, fewer transactions and rows):
--source include/have_innodb.inc
|
|
SET @save_freq=@@GLOBAL.innodb_purge_rseg_truncate_frequency;
|
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
|
|
CREATE TABLE t (a BINARY(2) NOT NULL DEFAULT '', b CHAR(2) AS (a) VIRTUAL, KEY(b(1)))
|
CHARACTER SET utf8
|
ENGINE=InnoDB;
|
BEGIN;
|
INSERT INTO t (a) VALUES ('');
|
UPDATE t SET a = 'x';
|
UPDATE t SET a = '';
|
COMMIT;
|
|
SET GLOBAL innodb_max_purge_lag_wait=0;
|
--replace_regex /.*History list length ([0-9]+).*/\1/
|
SHOW ENGINE INNODB STATUS;
|
SET GLOBAL innodb_purge_rseg_truncate_frequency=@save_freq;
|
|
DROP TABLE t;
|
|
10.6 9aea7d83c8d006519bdf1f3269136b3756ed7548
|
2022-11-17 10:25:30 0 [ERROR] InnoDB: tried to purge non-delete-marked record in index `b` of table `test`.`t`: tuple: TUPLE (info_bits=0, 2 fields): {[1] (0x00),[6] (0x000000000200)}, record: COMPACT RECORD(info_bits=0, 2 fields): {[1] (0x00),[6] (0x000000000200)}
|
2022-11-17 10:25:30 0 [ERROR] InnoDB: Flagged corruption of `b` in table `test`.`t` in purge
|
At the time sql_print_error() is invoked by purge, the indexes of the table contain the following records:
GEN_CLUST_INDEX(DB_ROW_ID,DB_TRX_ID,DB_ROLL_PTR,a)=(0x200,20,(update),'')
b(b(4),DB_ROW_ID)=('',0x200),delete-marked('x',0x200)
The contents corresponds to what I would expect. The delete-marked record should be removed.
The message is output when the undo log record that was written by the first UPDATE statement is being processed.
There was no call to innobase_get_computed_value() before the message was output. During the purge of history, it could be called by row_vers_old_has_index_entry(true, …).
The corresponding test that does not involve a virtual column does not fail:
CREATE TABLE t (a BINARY(8) NOT NULL DEFAULT '', KEY(a(4))) CHARACTER SET utf8 ENGINE=InnoDB;
|
If I change the table to use a different collation, the test will not fail. It fails with the following:
CREATE TABLE t (a BINARY(2) NOT NULL, b CHAR(2) AS (a) VIRTUAL, KEY(b(1)))
|
CHARACTER SET utf8
|
ENGINE=InnoDB;
|
It will not fail if I replace either the BINARY with CHAR or omit the CHARACTER SET utf8 specification. Due to this role of character sets and collations, I strongly suspect that something is going wrong in the SQL layer.
|
|
There is a rather similar test case in MySQL Bug #86485:
set sql_mode="";
|
drop table if exists t;
|
create table t (
|
`a` int,
|
`b` int not null,
|
`c` char(2) generated always as ('aa') virtual,
|
unique key(`b`),
|
unique key(`c`(1))
|
) engine=innodb default charset=utf8 row_format=compact;
|
|
drop procedure if exists p;
|
delimiter $
|
create procedure p()
|
begin
|
declare continue handler for sqlexception begin end;
|
repeat
|
if rand()>0.5 then insert into t(a,b) values(floor(rand()*10),floor(rand()*10)); end if;
|
if rand()>0.5 then delete from t where a<floor(rand()*10); end if;
|
if rand()>0.5 then update t set b=concat(a,a); end if;
|
if rand()>0.5 then replace into t(a,b) values(floor(rand()*10),floor(rand()*10)); end if;
|
until 1=2 end repeat;
|
end $
|
delimiter ;
|
|
call p();
|
As far as I can tell, the column a as well as the specification row_format=compact should be irrelevant. The column b could be defined as a PRIMARY KEY. Here is a slightly simplified mtr test:
--source include/have_innodb.inc
|
|
set sql_mode="";
|
create table t (
|
b int primary key,
|
c char(2) generated always as ('aa') virtual,
|
unique key(c(1))
|
) engine=innodb default charset=utf8;
|
|
delimiter $;
|
create procedure p()
|
begin
|
declare continue handler for sqlexception begin end;
|
repeat
|
if rand()>0.5 then insert into t(b) values(floor(rand()*10)); end if;
|
if rand()>0.5 then delete from t; end if;
|
if rand()>0.5 then update t set b=floor(rand()*10); end if;
|
if rand()>0.5 then replace into t(b) values(floor(rand()*10)); end if;
|
until 1=2 end repeat;
|
end $
|
delimiter ;$
|
|
call p();
|
This will eventually crash due to a debug assertion failure:
|
10.6 2325f8f33971c29503639126c69e549aa53b5585
|
mysqltest: At line 23: query 'call p()' failed: <Unknown> (2013): Lost connection to server during query
|
…
|
Version: '10.6.16-MariaDB-debug-log' socket: '/dev/shm/10.6/mysql-test/var/tmp/4/mysqld.1.sock' port: 16060 Source distribution
|
2023-09-04 8:48:30 0 [ERROR] InnoDB: tried to purge non-delete-marked record in index `c` of table `test`.`t`: tuple: TUPLE (info_bits=0, 2 fields): {[1]a(0x61),[4] (0x80000000)}, record: COMPACT RECORD(info_bits=0, 2 fields): {[1]a(0x61),[4] (0x80000000)}
|
mariadbd: /mariadb/10.6/storage/innobase/btr/btr0cur.cc:1011: dberr_t btr_cur_t::search_leaf(const dtuple_t*, page_cur_mode_t, btr_latch_mode, mtr_t*): Assertion `index()->is_btree() || index()->is_ibuf()' failed.
|
2023-09-04 8:48:30 0 [ERROR] InnoDB: Flagged corruption of `c` in table `test`.`t` in purge
|
There was an attempt to fix this bug in MySQL 8.0.11 but never in MySQL 5.7 or MariaDB Server. I think that the fix may be incorrect, because it is dealing with bytes and not characters. A test case that used non-ASCII data should reveal it. With this port of the suspected incorrect patch and the above test case, 10.6 does not crash for me:
diff --git a/storage/innobase/row/row0vers.cc b/storage/innobase/row/row0vers.cc
|
index a4fc32cc5a8..de1586ac9af 100644
|
--- a/storage/innobase/row/row0vers.cc
|
+++ b/storage/innobase/row/row0vers.cc
|
@@ -734,9 +734,15 @@ row_vers_vc_matches_cluster(
|
&& (!compare[v_col->v_pos])) {
|
|
if (ind_field->prefix_len != 0
|
- && !dfield_is_null(field2)
|
- && field2->len > ind_field->prefix_len) {
|
- field2->len = ind_field->prefix_len;
|
+ && !dfield_is_null(field2)) {
|
+ if (field2->len > ind_field->prefix_len) {
|
+ field2->len = ind_field->prefix_len;
|
+ }
|
+ if (field2->type.mbmaxlen > 1) {
|
+ /* FIXME: extract the correct number of characters
|
+ (not bytes)! */
|
+ field2->len = field1->len;
|
+ }
|
}
|
|
/* The index field mismatch */
|
I interrupted the test after 143 seconds (23 concurrent instances). The last run without the patch would end in a crash in 26 seconds.
|
|
I tested an improved version of the test case in the Description:
--source include/have_innodb.inc
|
CREATE TABLE t (a BINARY(8) NOT NULL DEFAULT '', b CHAR(8) AS (a) VIRTUAL, KEY(b(4))) CHARACTER SET utf8 ENGINE=InnoDB;
|
INSERT INTO t (a) VALUES (''),('');
|
UPDATE t SET a = 'x';
|
UPDATE t SET a = '';
|
|
SET @save_freq=@@GLOBAL.innodb_purge_rseg_truncate_frequency;
|
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
|
SET GLOBAL innodb_max_purge_lag_wait=0;
|
SET GLOBAL innodb_purge_rseg_truncate_frequency=@save_freq;
|
|
CHECK TABLE t EXTENDED;
|
DROP TABLE t;
|
This test case passed 5×100 runs when the patch was applied. When not, it would fail reliably thanks to MDEV-24402:
|
10.6 0f870914d410e8a34ea99b1e0ea7eb71094606f9
|
@@ -8,5 +8,6 @@
|
SET GLOBAL innodb_purge_rseg_truncate_frequency=@save_freq;
|
CHECK TABLE t EXTENDED;
|
Table Op Msg_type Msg_text
|
-test.t check status OK
|
+test.t check Warning InnoDB: Index b is marked as corrupted
|
+test.t check error Corrupt
|
DROP TABLE t;
|
|
mysqltest: Result length mismatch
|
|