summaryrefslogtreecommitdiff
path: root/mysql-test
diff options
context:
space:
mode:
Diffstat (limited to 'mysql-test')
-rw-r--r--mysql-test/lib/mtr_report.pm10
-rw-r--r--mysql-test/main/auto_increment_ranges_innodb.result48
-rw-r--r--mysql-test/main/auto_increment_ranges_innodb.test59
-rw-r--r--mysql-test/main/derived_cond_pushdown.result41
-rw-r--r--mysql-test/main/derived_cond_pushdown.test28
-rw-r--r--mysql-test/main/information_schema.result9
-rw-r--r--mysql-test/main/information_schema.test10
-rw-r--r--mysql-test/main/lock_view.test1
-rw-r--r--mysql-test/main/mysqldump-system.test4
-rw-r--r--mysql-test/main/partition_innodb.result2
-rw-r--r--mysql-test/main/sp.result19
-rw-r--r--mysql-test/main/sp.test23
-rw-r--r--mysql-test/main/subselect4.result34
-rw-r--r--mysql-test/main/subselect4.test37
-rw-r--r--mysql-test/main/xa.result28
-rw-r--r--mysql-test/main/xa.test43
-rw-r--r--mysql-test/suite/galera/r/galera_as_slave_replay.result95
-rw-r--r--mysql-test/suite/galera/r/galera_toi_alter_auto_increment.result2
-rw-r--r--mysql-test/suite/galera/t/galera_as_slave_replay.cnf11
-rw-r--r--mysql-test/suite/galera/t/galera_as_slave_replay.test200
-rw-r--r--mysql-test/suite/galera/t/galera_fk_cascade_delete.test8
-rw-r--r--mysql-test/suite/galera/t/galera_rsu_simple.test3
-rw-r--r--mysql-test/suite/galera/t/galera_toi_alter_auto_increment.test4
-rw-r--r--mysql-test/suite/innodb/r/foreign_key.result1
-rw-r--r--mysql-test/suite/innodb/r/instant_alter_crash.result30
-rw-r--r--mysql-test/suite/innodb/t/foreign_key.test1
-rw-r--r--mysql-test/suite/innodb/t/instant_alter_crash.test28
-rw-r--r--mysql-test/suite/mariabackup/include/corrupt-page.pl146
-rw-r--r--mysql-test/suite/mariabackup/incremental_ddl_during_backup.test2
-rw-r--r--mysql-test/suite/mariabackup/log_page_corruption.opt1
-rw-r--r--mysql-test/suite/mariabackup/log_page_corruption.result145
-rw-r--r--mysql-test/suite/mariabackup/log_page_corruption.test426
-rw-r--r--mysql-test/suite/roles/show_grants.result15
-rw-r--r--mysql-test/suite/roles/show_grants.test13
34 files changed, 1504 insertions, 23 deletions
diff --git a/mysql-test/lib/mtr_report.pm b/mysql-test/lib/mtr_report.pm
index 32250802815..473b21441e2 100644
--- a/mysql-test/lib/mtr_report.pm
+++ b/mysql-test/lib/mtr_report.pm
@@ -514,6 +514,10 @@ sub mtr_report_stats ($$$$) {
# if a test case has to be retried it should have the result MTR_RES_FAILED in jUnit XML
if ($test->{'result'} eq "MTR_RES_FAILED" || $test->{'retries'} > 0) {
my $logcontents = $test->{'logfile-failed'} || $test->{'logfile'};
+ # remove any double ] that would end the cdata
+ $logcontents =~ s/]]/\x{fffd}/g;
+ # replace wide characters that aren't allowed in XML 1.0
+ $logcontents =~ s/[\x00-\x08\x0B\x0C\x0E-\x1F]/\x{fffd}/g;
$xml_report .= qq(>\n\t\t\t<failure message="" type="MTR_RES_FAILED">\n<![CDATA[$logcontents]]>\n\t\t\t</failure>\n\t\t</testcase>\n);
} elsif ($test->{'result'} eq "MTR_RES_SKIPPED" && $test->{'disable'}) {
@@ -530,9 +534,9 @@ sub mtr_report_stats ($$$$) {
# save to file
my $xml_file = $::opt_xml_report;
- open XML_FILE, ">", $xml_file or die "Cannot create file $xml_file: $!";
- print XML_FILE $xml_report;
- close XML_FILE;
+ open (my $XML_UFILE, '>:encoding(UTF-8)', $xml_file) or die 'Cannot create file $xml_file: $!';
+ print $XML_UFILE $xml_report;
+ close $XML_UFILE or warn "File close failed!";
}
if (@$extra_warnings)
diff --git a/mysql-test/main/auto_increment_ranges_innodb.result b/mysql-test/main/auto_increment_ranges_innodb.result
index 61eccc6f944..7800099200f 100644
--- a/mysql-test/main/auto_increment_ranges_innodb.result
+++ b/mysql-test/main/auto_increment_ranges_innodb.result
@@ -289,3 +289,51 @@ pk f
5 a
6 <===
drop table t1;
+#
+# MDEV-21842: auto_increment does not increment with compound primary
+# key on partitioned table
+#
+create or replace table `t` (
+`id` bigint(20) unsigned not null auto_increment,
+`a` int(10) not null ,
+`dt` date not null,
+primary key (`id`, `dt`) ,
+unique key (`a`, `dt`)
+)
+partition by range columns(`dt`)
+(
+partition `p202002` values less than ('2020-03-01'),
+partition `P202003` values less than ('2020-04-01')
+);
+connect con1, localhost, root,,;
+connect con2, localhost, root,,;
+connection con1;
+start transaction;
+insert into t (a, dt) values (1, '2020-02-29');
+connection con2;
+start transaction;
+insert into t (a, dt) values (1, '2020-02-29');
+connection con1;
+insert into t (a, dt) values (2, '2020-02-29');
+select auto_increment from information_schema.tables where table_name='t';
+auto_increment
+4
+commit;
+connection con2;
+ERROR 23000: Duplicate entry '1-2020-02-29' for key 'a'
+connection con1;
+select auto_increment from information_schema.tables where table_name='t';
+auto_increment
+4
+insert into t (a, dt) values (3, '2020-02-29');
+insert into t (a, dt) values (4, '2020-02-29');
+disconnect con1;
+disconnect con2;
+connection default;
+select * from t;
+id a dt
+1 1 2020-02-29
+3 2 2020-02-29
+4 3 2020-02-29
+5 4 2020-02-29
+drop table t;
diff --git a/mysql-test/main/auto_increment_ranges_innodb.test b/mysql-test/main/auto_increment_ranges_innodb.test
index 016ca16bd91..92d377eb147 100644
--- a/mysql-test/main/auto_increment_ranges_innodb.test
+++ b/mysql-test/main/auto_increment_ranges_innodb.test
@@ -18,3 +18,62 @@ select * from t1;
drop table t1;
--let $datadir=`select @@datadir`
--remove_file $datadir/test/load.data
+
+--echo #
+--echo # MDEV-21842: auto_increment does not increment with compound primary
+--echo # key on partitioned table
+--echo #
+
+create or replace table `t` (
+ `id` bigint(20) unsigned not null auto_increment,
+ `a` int(10) not null ,
+ `dt` date not null,
+ primary key (`id`, `dt`) ,
+ unique key (`a`, `dt`)
+)
+ partition by range columns(`dt`)
+(
+ partition `p202002` values less than ('2020-03-01'),
+ partition `P202003` values less than ('2020-04-01')
+);
+
+connect (con1, localhost, root,,);
+connect (con2, localhost, root,,);
+
+--connection con1
+start transaction;
+insert into t (a, dt) values (1, '2020-02-29');
+
+--connection con2
+start transaction;
+let $conn2_id= `SELECT CONNECTION_ID()`;
+send insert into t (a, dt) values (1, '2020-02-29');
+
+--connection con1
+# Ensure that the above insert via conn2 increments next_auto_inc_val
+# before the following insert via conn1 starts.
+let $wait_condition=select 1 from Information_schema.INNODB_TRX
+ where trx_mysql_thread_id = $conn2_id and trx_state = 'LOCK WAIT'
+ and trx_query = "insert into t (a, dt) values (1, '2020-02-29')";
+--source include/wait_condition.inc
+
+insert into t (a, dt) values (2, '2020-02-29');
+select auto_increment from information_schema.tables where table_name='t';
+commit;
+
+--connection con2
+--error ER_DUP_ENTRY
+reap;
+
+--connection con1
+select auto_increment from information_schema.tables where table_name='t';
+insert into t (a, dt) values (3, '2020-02-29');
+insert into t (a, dt) values (4, '2020-02-29');
+
+disconnect con1;
+disconnect con2;
+
+--connection default
+select * from t;
+drop table t;
+
diff --git a/mysql-test/main/derived_cond_pushdown.result b/mysql-test/main/derived_cond_pushdown.result
index 701186847cc..0f2e6f0e2a2 100644
--- a/mysql-test/main/derived_cond_pushdown.result
+++ b/mysql-test/main/derived_cond_pushdown.result
@@ -10590,6 +10590,47 @@ a
abc
DROP VIEW v1;
DROP TABLE t1;
+#
+# MDEV-19179: pushdown into UNION of aggregation selects whose
+# corresponding columns have different names
+#
+create table t1 (a int);
+insert into t1 values (3), (7), (1);
+select *
+from (select min(a) as x from t1 union all select max(a) as y from t1) t
+where x>0;
+x
+1
+7
+explain extended select *
+from (select min(a) as x from t1 union all select max(a) as y from t1) t
+where x>0;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY <derived2> ALL NULL NULL NULL NULL 6 100.00 Using where
+2 DERIVED t1 ALL NULL NULL NULL NULL 3 100.00
+3 UNION t1 ALL NULL NULL NULL NULL 3 100.00
+Warnings:
+Note 1003 /* select#1 */ select `t`.`x` AS `x` from (/* select#2 */ select min(`test`.`t1`.`a`) AS `x` from `test`.`t1` having `x` > 0 union all /* select#3 */ select max(`test`.`t1`.`a`) AS `x` from `test`.`t1` having `x` > 0) `t` where `t`.`x` > 0
+prepare stmt from "select *
+from (select min(a) as x from t1 union all select max(a) as y from t1) t
+where x>0";
+execute stmt;
+x
+1
+7
+execute stmt;
+x
+1
+7
+deallocate prepare stmt;
+create view v1(m) as
+select min(a) as x from t1 union all select max(a) as y from t1;
+select * from v1 where m > 0;
+m
+1
+7
+drop view v1;
+drop table t1;
# End of 10.2 tests
#
# MDEV-14579: pushdown conditions into materialized views/derived tables
diff --git a/mysql-test/main/derived_cond_pushdown.test b/mysql-test/main/derived_cond_pushdown.test
index b2f97029ede..7667cd44ed2 100644
--- a/mysql-test/main/derived_cond_pushdown.test
+++ b/mysql-test/main/derived_cond_pushdown.test
@@ -2185,6 +2185,34 @@ SELECT * FROM v1 WHERE IF( a REGEXP 'def', 'foo', a ) IN ('abc', 'foobar');
DROP VIEW v1;
DROP TABLE t1;
+--echo #
+--echo # MDEV-19179: pushdown into UNION of aggregation selects whose
+--echo # corresponding columns have different names
+--echo #
+
+create table t1 (a int);
+insert into t1 values (3), (7), (1);
+
+let $q=
+select *
+from (select min(a) as x from t1 union all select max(a) as y from t1) t
+where x>0;
+
+eval $q;
+eval explain extended $q;
+
+eval prepare stmt from "$q";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+create view v1(m) as
+select min(a) as x from t1 union all select max(a) as y from t1;
+select * from v1 where m > 0;
+
+drop view v1;
+drop table t1;
+
--echo # End of 10.2 tests
--echo #
diff --git a/mysql-test/main/information_schema.result b/mysql-test/main/information_schema.result
index cf43d201ecb..51dbad1d628 100644
--- a/mysql-test/main/information_schema.result
+++ b/mysql-test/main/information_schema.result
@@ -2219,8 +2219,6 @@ SCHEMA_NAME
# End of 10.1 tests
#
#
-# Start of 10.2 Test
-#
# MDEV-14836: Assertion `m_status == DA_ERROR' failed in
# Diagnostics_area::sql_errno upon query from I_S with LIMIT ROWS EXAMINED
#
@@ -2305,5 +2303,12 @@ mysql global_priv Priv json_valid(`Priv`) def mysql
test t a `i` > 0 def test
drop table t;
#
+# MDEV-24230 subquery on information_schema fails with error message
+#
+create table t1 (n int);
+create table t2 (n int);
+insert into t1 set n = (select table_rows from information_schema.tables where table_name='t2');
+drop table t1, t2;
+#
# End of 10.3 tests
#
diff --git a/mysql-test/main/information_schema.test b/mysql-test/main/information_schema.test
index c3308224dbc..d9952538142 100644
--- a/mysql-test/main/information_schema.test
+++ b/mysql-test/main/information_schema.test
@@ -1927,8 +1927,6 @@ SELECT SCHEMA_NAME from information_schema.schemata where schema_name=REPEAT('a'
--echo #
--echo #
---echo # Start of 10.2 Test
---echo #
--echo # MDEV-14836: Assertion `m_status == DA_ERROR' failed in
--echo # Diagnostics_area::sql_errno upon query from I_S with LIMIT ROWS EXAMINED
--echo #
@@ -2002,5 +2000,13 @@ from information_schema.TABLE_CONSTRAINTS tc
drop table t;
--echo #
+--echo # MDEV-24230 subquery on information_schema fails with error message
+--echo #
+create table t1 (n int);
+create table t2 (n int);
+insert into t1 set n = (select table_rows from information_schema.tables where table_name='t2');
+drop table t1, t2;
+
+--echo #
--echo # End of 10.3 tests
--echo #
diff --git a/mysql-test/main/lock_view.test b/mysql-test/main/lock_view.test
index dd8809ab89d..4b1adac5be1 100644
--- a/mysql-test/main/lock_view.test
+++ b/mysql-test/main/lock_view.test
@@ -1,4 +1,5 @@
source include/not_embedded.inc;
+source include/have_perfschema.inc;
#
# LOCK TABLES and privileges on views
#
diff --git a/mysql-test/main/mysqldump-system.test b/mysql-test/main/mysqldump-system.test
index 3232b012acf..74638bb9265 100644
--- a/mysql-test/main/mysqldump-system.test
+++ b/mysql-test/main/mysqldump-system.test
@@ -3,6 +3,10 @@
--source include/have_udf.inc
--source include/platform.inc
+if (!$AUTH_SOCKET_SO) {
+ --skip Need auth socket plugin
+}
+
--echo #
--echo # MDEV-23630: mysqldump to logically dump system tables
--echo #
diff --git a/mysql-test/main/partition_innodb.result b/mysql-test/main/partition_innodb.result
index 554e7472a79..6be6721f0d1 100644
--- a/mysql-test/main/partition_innodb.result
+++ b/mysql-test/main/partition_innodb.result
@@ -1087,10 +1087,10 @@ INSERT INTO t1 VALUES ();
SELECT * FROM t1;
a
-1
-1
3
4
6
+7
DROP TABLE t1;
#
# End of 10.3 tests
diff --git a/mysql-test/main/sp.result b/mysql-test/main/sp.result
index 6bde488642a..10ba1311f0b 100644
--- a/mysql-test/main/sp.result
+++ b/mysql-test/main/sp.result
@@ -8450,8 +8450,25 @@ ERROR 22007: Incorrect integer value: 'y' for column ``.``.`a` at row 1
DROP TABLE t1;
SET sql_mode=DEFAULT;
#
-# Start of 10.3 tests
+# MDEV-24220: error when opening a table for the second call of SP
#
+CREATE TABLE t1 (a INT, b INT);
+INSERT INTO t1 VALUES (1,1),(2,2);
+CREATE VIEW v1 AS SELECT MAX(a) as f FROM t1;
+CREATE PROCEDURE p1()
+BEGIN
+SELECT * FROM v1;
+END $
+CALL p1;
+f
+2
+ALTER TABLE t1 DROP a;
+CALL p1;
+ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
+DROP PROCEDURE p1;
+DROP VIEW v1;
+DROP TABLE t1;
+#End of 10.2 tests
#
# MDEV-12007 Allow ROW variables as a cursor FETCH target
#
diff --git a/mysql-test/main/sp.test b/mysql-test/main/sp.test
index c6c00ca8d91..bf3a70b6283 100644
--- a/mysql-test/main/sp.test
+++ b/mysql-test/main/sp.test
@@ -9991,9 +9991,30 @@ DROP TABLE t1;
SET sql_mode=DEFAULT;
--echo #
---echo # Start of 10.3 tests
+--echo # MDEV-24220: error when opening a table for the second call of SP
--echo #
+CREATE TABLE t1 (a INT, b INT);
+INSERT INTO t1 VALUES (1,1),(2,2);
+CREATE VIEW v1 AS SELECT MAX(a) as f FROM t1;
+--delimiter $
+CREATE PROCEDURE p1()
+BEGIN
+ SELECT * FROM v1;
+END $
+--delimiter ;
+
+CALL p1;
+ALTER TABLE t1 DROP a;
+-- error ER_VIEW_INVALID
+CALL p1;
+
+DROP PROCEDURE p1;
+DROP VIEW v1;
+DROP TABLE t1;
+
+--echo #End of 10.2 tests
+
--echo #
--echo # MDEV-12007 Allow ROW variables as a cursor FETCH target
--echo #
diff --git a/mysql-test/main/subselect4.result b/mysql-test/main/subselect4.result
index 252967c09a7..79c42def277 100644
--- a/mysql-test/main/subselect4.result
+++ b/mysql-test/main/subselect4.result
@@ -2719,6 +2719,40 @@ SET join_cache_level= @save_join_cache_level;
DROP TABLE t1,t2,t3,t4;
# End of 10.2 tests
#
+# MDEV-21265: IN predicate conversion to IN subquery should be allowed for a broader set of datatype comparison
+#
+CREATE TABLE t1(a VARCHAR(50) collate utf8_general_ci, b INT);
+INSERT INTO t1 VALUES ('abc',1), ('def', 2), ('ghi', 3), ('jkl', 4), ('mno', 5);
+CREATE TABLE t2(a VARCHAR(50) collate utf8mb4_general_ci, b INT);
+INSERT INTO t2 VALUES ('abc',1), ('def', 2), ('ghi', 3), ('jkl', 4), ('mno', 5);
+set @save_in_predicate_conversion_threshold= @@in_predicate_conversion_threshold;
+set in_predicate_conversion_threshold=2;
+set names 'utf8mb4';
+#
+# IN predicate to IN subquery is not allowed as materialization is not allowed
+# The character set on the inner side is not equal to or a proper subset of the outer side
+#
+EXPLAIN
+SELECT * FROM t1 WHERE (t1.a,t1.b) IN (('abx',1),('def',2), ('abc', 3));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using where
+set names 'utf8';
+#
+# IN predicate to IN subquery is performed as materialization is llowed
+# The character set on the inner side is a proper subset of the outer side
+#
+EXPLAIN
+SELECT * FROM t2 WHERE (t2.a,t2.b) IN (('abx',1),('def',2), ('abc', 3));
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 5
+1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 16 func,func 1 Using where
+2 MATERIALIZED <derived3> ALL NULL NULL NULL NULL 3
+3 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used
+set names default;
+set @@in_predicate_conversion_threshold= @save_in_predicate_conversion_threshold;
+DROP TABLE t1,t2;
+# End of 10.3 tests
+#
# MDEV-19134: EXISTS() slower if ORDER BY is defined
#
create table t0 (a int);
diff --git a/mysql-test/main/subselect4.test b/mysql-test/main/subselect4.test
index c9b05c4f015..3a9ad7f715c 100644
--- a/mysql-test/main/subselect4.test
+++ b/mysql-test/main/subselect4.test
@@ -2240,6 +2240,43 @@ DROP TABLE t1,t2,t3,t4;
--echo # End of 10.2 tests
--echo #
+--echo # MDEV-21265: IN predicate conversion to IN subquery should be allowed for a broader set of datatype comparison
+--echo #
+
+CREATE TABLE t1(a VARCHAR(50) collate utf8_general_ci, b INT);
+INSERT INTO t1 VALUES ('abc',1), ('def', 2), ('ghi', 3), ('jkl', 4), ('mno', 5);
+
+CREATE TABLE t2(a VARCHAR(50) collate utf8mb4_general_ci, b INT);
+INSERT INTO t2 VALUES ('abc',1), ('def', 2), ('ghi', 3), ('jkl', 4), ('mno', 5);
+
+set @save_in_predicate_conversion_threshold= @@in_predicate_conversion_threshold;
+set in_predicate_conversion_threshold=2;
+
+set names 'utf8mb4';
+--echo #
+--echo # IN predicate to IN subquery is not allowed as materialization is not allowed
+--echo # The character set on the inner side is not equal to or a proper subset of the outer side
+--echo #
+
+EXPLAIN
+SELECT * FROM t1 WHERE (t1.a,t1.b) IN (('abx',1),('def',2), ('abc', 3));
+
+set names 'utf8';
+--echo #
+--echo # IN predicate to IN subquery is performed as materialization is llowed
+--echo # The character set on the inner side is a proper subset of the outer side
+--echo #
+
+EXPLAIN
+SELECT * FROM t2 WHERE (t2.a,t2.b) IN (('abx',1),('def',2), ('abc', 3));
+
+set names default;
+set @@in_predicate_conversion_threshold= @save_in_predicate_conversion_threshold;
+DROP TABLE t1,t2;
+
+--echo # End of 10.3 tests
+
+--echo #
--echo # MDEV-19134: EXISTS() slower if ORDER BY is defined
--echo #
create table t0 (a int);
diff --git a/mysql-test/main/xa.result b/mysql-test/main/xa.result
index f37a3c36531..f02cb17b6ac 100644
--- a/mysql-test/main/xa.result
+++ b/mysql-test/main/xa.result
@@ -294,7 +294,7 @@ DROP TABLE t1;
#
# Bug#12352846 - TRANS_XA_START(THD*):
# ASSERTION THD->TRANSACTION.XID_STATE.XID.IS_NULL()
-# FAILED
+# FAILED
#
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
CREATE TABLE t2 (a INT) ENGINE=InnoDB;
@@ -345,6 +345,32 @@ connection default;
XA END 'xid1';
XA ROLLBACK 'xid1';
DROP TABLE t1, t2, t3;
+#
+# MDEV 15532 XA: Assertion `!log->same_pk' failed in
+# row_log_table_apply_delete
+#
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2);
+connect con1,localhost,root,,test;
+XA START 'xid';
+UPDATE t1 SET a = 5;
+connection default;
+SET innodb_lock_wait_timeout= 2, lock_wait_timeout= 2;
+ALTER TABLE non_existing_table1;
+ERROR 42S02: Table 'test.non_existing_table1' doesn't exist
+ALTER TABLE t1 FORCE;;
+connection con1;
+ALTER TABLE non_existing_table2;
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state
+DELETE FROM t1 LIMIT 1;
+connection default;
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+connection con1;
+XA END 'xid';
+XA ROLLBACK 'xid';
+DROP TABLE t1;
+disconnect con1;
+connection default;
XA BEGIN 'xid';
CREATE TEMPORARY SEQUENCE s;
ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state
diff --git a/mysql-test/main/xa.test b/mysql-test/main/xa.test
index ce8f3834b03..9b0b54d0405 100644
--- a/mysql-test/main/xa.test
+++ b/mysql-test/main/xa.test
@@ -390,7 +390,7 @@ DROP TABLE t1;
--echo #
--echo # Bug#12352846 - TRANS_XA_START(THD*):
--echo # ASSERTION THD->TRANSACTION.XID_STATE.XID.IS_NULL()
---echo # FAILED
+--echo # FAILED
--echo #
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
@@ -447,7 +447,7 @@ CREATE TABLE t1 (pk INT PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t2 (pk INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t2 VALUES (1),(2);
CREATE TABLE t3 (i INT) ENGINE=InnoDB;
-
+
XA BEGIN 'xid1';
REPLACE INTO t1 SELECT * FROM t2;
@@ -476,6 +476,45 @@ XA END 'xid1';
XA ROLLBACK 'xid1';
DROP TABLE t1, t2, t3;
+--echo #
+--echo # MDEV 15532 XA: Assertion `!log->same_pk' failed in
+--echo # row_log_table_apply_delete
+--echo #
+
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2);
+
+--connect (con1,localhost,root,,test)
+
+XA START 'xid';
+UPDATE t1 SET a = 5;
+
+--connection default
+SET innodb_lock_wait_timeout= 2, lock_wait_timeout= 2;
+
+--error ER_NO_SUCH_TABLE
+ALTER TABLE non_existing_table1;
+
+--send ALTER TABLE t1 FORCE;
+
+--connection con1
+--error ER_XAER_RMFAIL
+
+ALTER TABLE non_existing_table2;
+DELETE FROM t1 LIMIT 1;
+
+--connection default
+--error ER_LOCK_WAIT_TIMEOUT
+--reap
+
+# Cleanup
+--connection con1
+XA END 'xid';
+XA ROLLBACK 'xid';
+DROP TABLE t1;
+--disconnect con1
+connection default;
+
--source include/wait_until_count_sessions.inc
#
diff --git a/mysql-test/suite/galera/r/galera_as_slave_replay.result b/mysql-test/suite/galera/r/galera_as_slave_replay.result
new file mode 100644
index 00000000000..760617be5f7
--- /dev/null
+++ b/mysql-test/suite/galera/r/galera_as_slave_replay.result
@@ -0,0 +1,95 @@
+connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2;
+connection node_2a;
+connection node_1;
+RESET MASTER;
+connection node_2a;
+START SLAVE;
+connection node_1;
+CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(1)) engine=innodb;
+INSERT INTO t1 VALUES (1, 'a');
+INSERT INTO t1 VALUES (3, 'a');
+set binlog_format=STATEMENT;
+SET AUTOCOMMIT=ON;
+START TRANSACTION;
+SELECT * FROM t1 FOR UPDATE;
+f1 f2
+1 a
+3 a
+UPDATE t1 SET f2 = 'c' WHERE f1 > 1;
+connection node_2a;
+SET SESSION wsrep_sync_wait = 0;
+connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3;
+connection node_3;
+SET SESSION wsrep_sync_wait = 0;
+connection node_2a;
+SET GLOBAL wsrep_provider_options = 'dbug=d,commit_monitor_enter_sync';
+SET GLOBAL debug_dbug = "d,sync.wsrep_apply_cb";
+connection node_3;
+INSERT INTO test.t1 VALUES (2, 'b');
+connection node_1;
+COMMIT;
+connection node_2a;
+SET SESSION wsrep_on = 0;
+SET SESSION wsrep_on = 1;
+SET GLOBAL debug_dbug = "";
+SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";
+connection node_2a;
+SET GLOBAL wsrep_provider_options = 'dbug=';
+SET GLOBAL wsrep_provider_options = 'signal=commit_monitor_enter_sync';
+connection node_1;
+SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'a';
+COUNT(*) = 1
+1
+SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'c';
+COUNT(*) = 1
+1
+SELECT * FROM t1;
+f1 f2
+1 a
+3 c
+connection node_2a;
+set session wsrep_sync_wait=15;
+set session wsrep_sync_wait=0;
+wsrep_local_replays
+1
+SELECT * FROM t1;
+f1 f2
+1 a
+2 b
+3 c
+SET DEBUG_SYNC = "RESET";
+#
+# test phase with real abort
+#
+connection node_1;
+set binlog_format=ROW;
+insert into t1 values (4, 'd');
+SET AUTOCOMMIT=ON;
+START TRANSACTION;
+UPDATE t1 SET f2 = 'd' WHERE f1 = 3;
+connection node_2a;
+SET GLOBAL wsrep_provider_options = 'dbug=d,commit_monitor_enter_sync';
+SET GLOBAL debug_dbug = "d,sync.wsrep_apply_cb";
+connection node_3;
+UPDATE test.t1 SET f2 = 'e' WHERE f1 = 3;
+connection node_1;
+COMMIT;
+connection node_2a;
+SET GLOBAL debug_dbug = "";
+SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";
+connection node_2a;
+SET GLOBAL wsrep_provider_options = 'dbug=';
+SET GLOBAL wsrep_provider_options = 'signal=commit_monitor_enter_sync';
+SET DEBUG_SYNC = "RESET";
+connection node_2a;
+set session wsrep_sync_wait=15;
+SELECT COUNT(*) = 1 FROM test.t1 WHERE f2 = 'e';
+COUNT(*) = 1
+1
+set session wsrep_sync_wait=0;
+STOP SLAVE;
+RESET SLAVE;
+DROP TABLE t1;
+connection node_1;
+DROP TABLE t1;
+RESET MASTER;
diff --git a/mysql-test/suite/galera/r/galera_toi_alter_auto_increment.result b/mysql-test/suite/galera/r/galera_toi_alter_auto_increment.result
index a23b0523140..a9923ba4211 100644
--- a/mysql-test/suite/galera/r/galera_toi_alter_auto_increment.result
+++ b/mysql-test/suite/galera/r/galera_toi_alter_auto_increment.result
@@ -1,7 +1,7 @@
connection node_2;
connection node_1;
connection node_1;
-CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB;
+CREATE TABLE ten (f1 INTEGER NOT NULL PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) ENGINE=InnoDB;
INSERT INTO t1 (f2) SELECT 1 FROM ten;
diff --git a/mysql-test/suite/galera/t/galera_as_slave_replay.cnf b/mysql-test/suite/galera/t/galera_as_slave_replay.cnf
new file mode 100644
index 00000000000..b1f9d7e9cbd
--- /dev/null
+++ b/mysql-test/suite/galera/t/galera_as_slave_replay.cnf
@@ -0,0 +1,11 @@
+!include ../galera_2nodes_as_slave.cnf
+
+[mysqld]
+binlog-format=row
+
+[mysqld.1]
+wsrep_restart_slave=1
+
+[mysqld.2]
+wsrep_restart_slave=1
+
diff --git a/mysql-test/suite/galera/t/galera_as_slave_replay.test b/mysql-test/suite/galera/t/galera_as_slave_replay.test
new file mode 100644
index 00000000000..93f95349e6d
--- /dev/null
+++ b/mysql-test/suite/galera/t/galera_as_slave_replay.test
@@ -0,0 +1,200 @@
+#
+# This test tests the operation of transaction replay for async replication slave.
+# If a potentially conflicting galera transaction arrives at
+# just the right time during the commit and has lock conflict with async replication transaction
+# applied by slave SQL thread, then the async replication transaction should either abort
+# or rollback and replay (depending on the nature of lock conflict).
+#
+
+--source include/have_innodb.inc
+--source include/have_debug.inc
+--source include/have_debug_sync.inc
+--source include/galera_have_debug_sync.inc
+
+--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2
+
+--connection node_2a
+--source include/galera_cluster.inc
+#--source suite/galera/include/galera_have_debug_sync.inc
+
+#
+# node 1 is native MariaDB server operating as async replication master
+#
+--connection node_1
+RESET MASTER;
+
+--connection node_2a
+#
+# count the number of wsrep replay's done in the node
+#
+--let $wsrep_local_replays_old = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_replays'`
+
+
+#
+# nodes 2 and 3 form a galera cluster, node 2 operates as slave for native MariaDB naster in node 1
+#
+--disable_query_log
+--eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_USER='root', MASTER_PORT=$NODE_MYPORT_1;
+--enable_query_log
+START SLAVE;
+
+--connection node_1
+CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(1)) engine=innodb;
+INSERT INTO t1 VALUES (1, 'a');
+INSERT INTO t1 VALUES (3, 'a');
+
+#
+# use statement format replication to cause a false positive conflict with async replication transaction
+# and galera replication. The conflict will be on GAP lock, and slave SQL thread should rollback
+# and replay
+#
+set binlog_format=STATEMENT;
+
+SET AUTOCOMMIT=ON;
+START TRANSACTION;
+
+SELECT * FROM t1 FOR UPDATE;
+UPDATE t1 SET f2 = 'c' WHERE f1 > 1;
+
+--connection node_2a
+# wait for create table and inserts to be replicated from master
+SET SESSION wsrep_sync_wait = 0;
+--let $wait_condition = SELECT COUNT(*) = 2 FROM test.t1;
+--source include/wait_condition.inc
+
+# wait for create table and inserts to be replicated in cluster
+--connect node_3, 127.0.0.1, root, , test, $NODE_MYPORT_3
+--connection node_3
+SET SESSION wsrep_sync_wait = 0;
+--let $wait_condition = SELECT COUNT(*) = 2 FROM test.t1;
+--source include/wait_condition.inc
+
+--connection node_2a
+# Block the future commit of async replication
+--let $galera_sync_point = commit_monitor_enter_sync
+--source include/galera_set_sync_point.inc
+
+# block also the applier before applying begins
+SET GLOBAL debug_dbug = "d,sync.wsrep_apply_cb";
+
+#
+# now inject a conflicting insert from node 3, it will replicate with
+# earlier seqno (than async transaction) and pause before applying in node 2
+#
+--connection node_3
+INSERT INTO test.t1 VALUES (2, 'b');
+
+#
+# send the update from master, this will succeed here, beceuase of async replication.
+# async replication will apply this in node 2 and pause before commit phase,
+--connection node_1
+--error 0
+COMMIT;
+
+# Wait until async slave commit is blocked in node_2
+--connection node_2a
+--source include/galera_wait_sync_point.inc
+
+#
+# release the applier
+# note: have to clear wsrep_apply_cb sync point first, as async replication will go for replay
+# and as this sync point, after BF applier is released to progress
+#
+SET GLOBAL debug_dbug = "";
+SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";
+
+# Unblock the async slave commit
+--connection node_2a
+--source include/galera_clear_sync_point.inc
+--source include/galera_signal_sync_point.inc
+
+--connection node_1
+
+SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'a';
+SELECT COUNT(*) = 1 FROM t1 WHERE f2 = 'c';
+SELECT * FROM t1;
+
+--connection node_2a
+
+# wsrep_local_replays has increased by 1
+set session wsrep_sync_wait=15;
+--let $wsrep_local_replays_new = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_replays'`
+set session wsrep_sync_wait=0;
+
+--disable_query_log
+--eval SELECT $wsrep_local_replays_new - $wsrep_local_replays_old = 1 AS wsrep_local_replays;
+--enable_query_log
+
+#
+# replaying of async transaction should be effective, and row 3 having 'c' in f2
+#
+SELECT * FROM t1;
+SET DEBUG_SYNC = "RESET";
+
+#********************************************************************************
+# test phase 2
+#********************************************************************************
+
+--echo #
+--echo # test phase with real abort
+--echo #
+
+--connection node_1
+
+set binlog_format=ROW;
+
+insert into t1 values (4, 'd');
+
+SET AUTOCOMMIT=ON;
+START TRANSACTION;
+
+UPDATE t1 SET f2 = 'd' WHERE f1 = 3;
+
+--connection node_2a
+# wait for the last insert to be replicated from master
+--let $wait_condition = SELECT COUNT(*) = 4 FROM test.t1;
+--source include/wait_condition.inc
+
+# Block the commit
+--let $galera_sync_point = commit_monitor_enter_sync
+--source include/galera_set_sync_point.inc
+
+# block applier
+SET GLOBAL debug_dbug = "d,sync.wsrep_apply_cb";
+
+# Inject a conflicting update from node 3
+--connection node_3
+UPDATE test.t1 SET f2 = 'e' WHERE f1 = 3;
+
+# send the update from master
+--connection node_1
+--error 0
+COMMIT;
+
+--connection node_2a
+
+# release the applier
+SET GLOBAL debug_dbug = "";
+SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";
+
+
+# Unblock the async slave commit
+--connection node_2a
+--source include/galera_clear_sync_point.inc
+--source include/galera_signal_sync_point.inc
+SET DEBUG_SYNC = "RESET";
+
+--connection node_2a
+
+set session wsrep_sync_wait=15;
+SELECT COUNT(*) = 1 FROM test.t1 WHERE f2 = 'e';
+set session wsrep_sync_wait=0;
+
+STOP SLAVE;
+RESET SLAVE;
+
+DROP TABLE t1;
+
+--connection node_1
+DROP TABLE t1;
+RESET MASTER;
diff --git a/mysql-test/suite/galera/t/galera_fk_cascade_delete.test b/mysql-test/suite/galera/t/galera_fk_cascade_delete.test
index 6f0de0a1f4a..a3e0dbcf36f 100644
--- a/mysql-test/suite/galera/t/galera_fk_cascade_delete.test
+++ b/mysql-test/suite/galera/t/galera_fk_cascade_delete.test
@@ -40,11 +40,19 @@ set wsrep_sync_wait=0;
--let $wait_condition = SELECT COUNT(*) = 2 FROM child;
--source include/wait_condition.inc
+--let $wait_condition = SELECT COUNT(*) = 2 FROM parent;
+--source include/wait_condition.inc
+--let $wait_condition = SELECT COUNT(*) = 2 FROM grandparent;
+--source include/wait_condition.inc
DELETE FROM grandparent WHERE id = 1;
--connection node_1
--let $wait_condition = SELECT COUNT(*) = 1 FROM child;
--source include/wait_condition.inc
+--let $wait_condition = SELECT COUNT(*) = 1 FROM parent;
+--source include/wait_condition.inc
+--let $wait_condition = SELECT COUNT(*) = 1 FROM grandparent;
+--source include/wait_condition.inc
SELECT COUNT(*), COUNT(*) = 0 FROM parent WHERE grandparent_id = 1;
SELECT COUNT(*), COUNT(*) = 0 FROM child WHERE parent_id = 1;
diff --git a/mysql-test/suite/galera/t/galera_rsu_simple.test b/mysql-test/suite/galera/t/galera_rsu_simple.test
index 5841dbd8006..aa6f25b6db6 100644
--- a/mysql-test/suite/galera/t/galera_rsu_simple.test
+++ b/mysql-test/suite/galera/t/galera_rsu_simple.test
@@ -8,6 +8,9 @@
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
--connection node_2
+--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'
+--source include/wait_condition.inc
+
SET SESSION wsrep_OSU_method = "RSU";
ALTER TABLE t1 ADD COLUMN f2 INTEGER;
SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
diff --git a/mysql-test/suite/galera/t/galera_toi_alter_auto_increment.test b/mysql-test/suite/galera/t/galera_toi_alter_auto_increment.test
index 641d2101c80..793e87cb53e 100644
--- a/mysql-test/suite/galera/t/galera_toi_alter_auto_increment.test
+++ b/mysql-test/suite/galera/t/galera_toi_alter_auto_increment.test
@@ -7,7 +7,7 @@
--source include/have_innodb.inc
--connection node_1
-CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB;
+CREATE TABLE ten (f1 INTEGER NOT NULL PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) ENGINE=InnoDB;
@@ -83,6 +83,8 @@ SET GLOBAL auto_increment_offset = 1;
CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 INTEGER) ENGINE=InnoDB;
--connection node_2a
+--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'
+--source include/wait_condition.inc
ALTER TABLE t1 AUTO_INCREMENT=100;
diff --git a/mysql-test/suite/innodb/r/foreign_key.result b/mysql-test/suite/innodb/r/foreign_key.result
index f916d5788f8..34d0c3dad64 100644
--- a/mysql-test/suite/innodb/r/foreign_key.result
+++ b/mysql-test/suite/innodb/r/foreign_key.result
@@ -413,6 +413,7 @@ CREATE TABLE x AS SELECT * FROM t1;
ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state
connect con1,localhost,root,,test;
SET foreign_key_checks= OFF, innodb_lock_wait_timeout= 1;
+SET lock_wait_timeout=5;
ALTER TABLE t1 ADD FOREIGN KEY f (a) REFERENCES t1 (pk), LOCK=EXCLUSIVE;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
disconnect con1;
diff --git a/mysql-test/suite/innodb/r/instant_alter_crash.result b/mysql-test/suite/innodb/r/instant_alter_crash.result
index ef0e310492a..a2c388fa103 100644
--- a/mysql-test/suite/innodb/r/instant_alter_crash.result
+++ b/mysql-test/suite/innodb/r/instant_alter_crash.result
@@ -74,7 +74,6 @@ DELETE FROM t1;
# Kill the server
disconnect ddl;
# restart
-SET @saved_frequency= @@GLOBAL.innodb_purge_rseg_truncate_frequency;
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
FOUND 3 /\[Note\] InnoDB: Rolled back recovered transaction / in mysqld.1.err
SELECT * FROM t1;
@@ -138,6 +137,23 @@ header=0x080008030000 (id=0x000000000000000100)
UNLOCK TABLES;
DELETE FROM t2;
InnoDB 0 transactions not purged
+#
+# MDEV-24323 Crash on recovery after kill during instant ADD COLUMN
+#
+connect ddl, localhost, root;
+CREATE TABLE t3(id INT PRIMARY KEY, c2 INT, v2 INT AS(c2) VIRTUAL, UNIQUE(v2))
+ENGINE=InnoDB;
+INSERT INTO t3 SET id=1,c2=1;
+SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever';
+ALTER TABLE t3 ADD COLUMN c3 TEXT NOT NULL DEFAULT 'sic transit gloria mundi';
+connection default;
+SET DEBUG_SYNC='now WAIT_FOR ddl';
+SET GLOBAL innodb_flush_log_at_trx_commit=1;
+SET debug_dbug='+d,dict_sys_mutex_avoid';
+INSERT INTO t1 VALUES(0,0);
+# Kill the server
+disconnect ddl;
+# restart
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -154,6 +170,14 @@ t2 CREATE TABLE `t2` (
PRIMARY KEY (`id`),
UNIQUE KEY `c2` (`c2`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=REDUNDANT
-DROP TABLE t1,t2;
+SHOW CREATE TABLE t3;
+Table Create Table
+t3 CREATE TABLE `t3` (
+ `id` int(11) NOT NULL,
+ `c2` int(11) DEFAULT NULL,
+ `v2` int(11) GENERATED ALWAYS AS (`c2`) VIRTUAL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `v2` (`v2`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+DROP TABLE t1,t2,t3;
db.opt
-SET GLOBAL innodb_purge_rseg_truncate_frequency=@saved_frequency;
diff --git a/mysql-test/suite/innodb/t/foreign_key.test b/mysql-test/suite/innodb/t/foreign_key.test
index 18cd0fa15e4..41299e4bc35 100644
--- a/mysql-test/suite/innodb/t/foreign_key.test
+++ b/mysql-test/suite/innodb/t/foreign_key.test
@@ -414,6 +414,7 @@ INSERT INTO t1 VALUES (1,2);
CREATE TABLE x AS SELECT * FROM t1;
--connect (con1,localhost,root,,test)
SET foreign_key_checks= OFF, innodb_lock_wait_timeout= 1;
+SET lock_wait_timeout=5;
--error ER_LOCK_WAIT_TIMEOUT
ALTER TABLE t1 ADD FOREIGN KEY f (a) REFERENCES t1 (pk), LOCK=EXCLUSIVE;# Cleanup
--disconnect con1
diff --git a/mysql-test/suite/innodb/t/instant_alter_crash.test b/mysql-test/suite/innodb/t/instant_alter_crash.test
index f9b18aa589f..43db8f619f3 100644
--- a/mysql-test/suite/innodb/t/instant_alter_crash.test
+++ b/mysql-test/suite/innodb/t/instant_alter_crash.test
@@ -91,7 +91,6 @@ DELETE FROM t1;
disconnect ddl;
--source include/start_mysqld.inc
-SET @saved_frequency= @@GLOBAL.innodb_purge_rseg_truncate_frequency;
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err;
@@ -177,11 +176,32 @@ UNLOCK TABLES;
DELETE FROM t2;
--source include/wait_all_purged.inc
+--echo #
+--echo # MDEV-24323 Crash on recovery after kill during instant ADD COLUMN
+--echo #
+connect ddl, localhost, root;
+CREATE TABLE t3(id INT PRIMARY KEY, c2 INT, v2 INT AS(c2) VIRTUAL, UNIQUE(v2))
+ENGINE=InnoDB;
+INSERT INTO t3 SET id=1,c2=1;
+
+SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever';
+--send
+ALTER TABLE t3 ADD COLUMN c3 TEXT NOT NULL DEFAULT 'sic transit gloria mundi';
+
+connection default;
+SET DEBUG_SYNC='now WAIT_FOR ddl';
+SET GLOBAL innodb_flush_log_at_trx_commit=1;
+SET debug_dbug='+d,dict_sys_mutex_avoid';
+INSERT INTO t1 VALUES(0,0);
+
+--source include/kill_mysqld.inc
+disconnect ddl;
+--source include/start_mysqld.inc
+
SHOW CREATE TABLE t1;
SHOW CREATE TABLE t2;
-DROP TABLE t1,t2;
+SHOW CREATE TABLE t3;
+DROP TABLE t1,t2,t3;
--remove_files_wildcard $MYSQLD_DATADIR/test #sql*.frm
--list_files $MYSQLD_DATADIR/test
-
-SET GLOBAL innodb_purge_rseg_truncate_frequency=@saved_frequency;
diff --git a/mysql-test/suite/mariabackup/include/corrupt-page.pl b/mysql-test/suite/mariabackup/include/corrupt-page.pl
new file mode 100644
index 00000000000..d5c75dbde55
--- /dev/null
+++ b/mysql-test/suite/mariabackup/include/corrupt-page.pl
@@ -0,0 +1,146 @@
+use strict;
+use warnings;
+use Fcntl qw(:DEFAULT :seek);
+do "$ENV{MTR_SUITE_DIR}/../innodb/include/crc32.pl";
+
+sub corrupt_space_page_id {
+ my $file_name = shift;
+ my @pages_to_corrupt = @_;
+
+ my $page_size = $ENV{INNODB_PAGE_SIZE};
+
+ sysopen my $ibd_file, $file_name, O_RDWR || die "Cannot open $file_name\n";
+ sysread($ibd_file, $_, 38) || die "Cannot read $file_name\n";
+ my $space = unpack("x[34]N", $_);
+ foreach my $page_no (@pages_to_corrupt) {
+ $space += 10; # generate wrong space id
+ sysseek($ibd_file, $page_size * $page_no, SEEK_SET)
+ || die "Cannot seek $file_name\n";
+
+ my $head = pack("Nx[18]", $page_no + 10); # generate wrong page number
+ my $body = chr(0) x ($page_size - 38 - 8);
+
+ # Calculate innodb_checksum_algorithm=crc32 for the unencrypted page.
+ # The following bytes are excluded:
+ # bytes 0..3 (the checksum is stored there)
+ # bytes 26..37 (encryption key version, post-encryption checksum, tablespace id)
+ # bytes $page_size-8..$page_size-1 (checksum, LSB of FIL_PAGE_LSN)
+ my $polynomial = 0x82f63b78; # CRC-32C
+ my $ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
+
+ my $page= pack("N",$ck).$head.pack("NNN",1,$ck,$space).$body.pack("Nx[4]",$ck);
+ die unless syswrite($ibd_file, $page, $page_size) == $page_size;
+ }
+ close $ibd_file;
+}
+
+sub extend_space {
+ my $file_name = shift;
+ my $n_pages = shift;
+
+ my $page_size = $ENV{INNODB_PAGE_SIZE};
+ my $page;
+
+ sysopen my $ibd_file, $file_name, O_RDWR || die "Cannot open $file_name\n";
+ sysread($ibd_file, $page, $page_size)
+ || die "Cannot read $file_name\n";
+ my $size = unpack("N", substr($page, 46, 4));
+ my $packed_new_size = pack("N", $size + $n_pages);
+ substr($page, 46, 4, $packed_new_size);
+
+ my $head = substr($page, 4, 22);
+ my $body = substr($page, 38, $page_size - 38 - 8);
+ my $polynomial = 0x82f63b78; # CRC-32C
+ my $ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
+ my $packed_ck = pack("N", $ck);
+ substr($page, 0, 4, $packed_ck);
+ substr($page, $page_size - 8, 4, $packed_ck);
+
+ sysseek($ibd_file, 0, SEEK_SET)
+ || die "Cannot seek $file_name\n";
+ die unless syswrite($ibd_file, $page, $page_size) == $page_size;
+
+ sysseek($ibd_file, 0, SEEK_END)
+ || die "Cannot seek $file_name\n";
+ my $pages_size = $page_size*$n_pages;
+ my $pages = chr(0) x $pages_size;
+ die unless syswrite($ibd_file, $pages, $pages_size) == $pages_size;
+ close $ibd_file;
+ return $size;
+}
+
+sub die_if_page_is_not_zero {
+ my $file_name = shift;
+ my @pages_to_check = @_;
+
+ no locale;
+ my $page_size = $ENV{INNODB_PAGE_SIZE};
+ my $zero_page = chr(0) x $page_size;
+ sysopen my $ibd_file, $file_name, O_RDWR || die "Cannot open $file_name\n";
+ foreach my $page_no_to_check (@pages_to_check) {
+ sysseek($ibd_file, $page_size*$page_no_to_check, SEEK_SET) ||
+ die "Cannot seek $file_name\n";
+ sysread($ibd_file, my $read_page, $page_size) ||
+ die "Cannot read $file_name\n";
+ die "The page $page_no_to_check is not zero-filed in $file_name"
+ if ($read_page cmp $zero_page);
+ }
+ close $ibd_file;
+}
+
+sub print_corrupted_pages_file {
+ my $file_in = shift;
+ my $file_out = shift;
+ open my $fh, '<', $file_in || die $!;
+ my $line_number = 0;
+ my $space = {};
+ my @spaces;
+ while (my $line = <$fh>) {
+ ++$line_number;
+ if ($line_number & 1) {
+ my ($name, $id) = split(/ /, $line);
+ $space->{name} = $name;
+ }
+ else {
+ $space->{pages} = $line;
+ push (@spaces, $space);
+ $space = {};
+ }
+ }
+ close $fh;
+ my @sorted_spaces = sort { $a->{name} cmp $b->{name} } @spaces;
+ open $fh, '>', $file_out || die $!;
+ foreach my $space (@sorted_spaces) {
+ print $fh $space->{name};
+ print $fh "\n";
+ print $fh $space->{pages};
+ }
+ close $fh;
+}
+
+sub append_corrupted_pages {
+ my $file_name = shift;
+ my $space_name = shift;
+ my $pages = shift;
+ open my $fh, '<', $file_name || die $!;
+ my $line_number = 0;
+ my $space_line;
+ while (my $line = <$fh>) {
+ ++$line_number;
+ if ($line_number & 1) {
+ my ($name, $id) = split(/ /, $line);
+ if ($name eq $space_name) {
+ $space_line = $line;
+ last;
+ }
+ }
+ }
+ close $fh;
+ if (not defined $space_line) {
+ die "Can't find requested space $space_name in file $file_name";
+ }
+ open $fh, '>>', $file_name || die $!;
+ print $fh $space_line;
+ print $fh "$pages\n";
+ close $fh;
+}
diff --git a/mysql-test/suite/mariabackup/incremental_ddl_during_backup.test b/mysql-test/suite/mariabackup/incremental_ddl_during_backup.test
index b1ab17a6d8f..ebdb2137523 100644
--- a/mysql-test/suite/mariabackup/incremental_ddl_during_backup.test
+++ b/mysql-test/suite/mariabackup/incremental_ddl_during_backup.test
@@ -22,7 +22,7 @@ INSERT into t1 values(1);
--let after_copy_test_t2=DROP TABLE test.t2
--let after_copy_test_t3=CREATE INDEX a_i ON test.t3(i);
--let before_copy_test_t10=DROP TABLE test.t10
---let wait_innodb_redo_before_copy=test/t10
+--let wait_innodb_redo_before_copy_test_t10 = 1
# mariabackup should crash with assertion if MDEV-24026 is not fixed
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$incremental_dir --incremental-basedir=$basedir --dbug=+d,mariabackup_events,mariabackup_inject_code;
diff --git a/mysql-test/suite/mariabackup/log_page_corruption.opt b/mysql-test/suite/mariabackup/log_page_corruption.opt
new file mode 100644
index 00000000000..c44c611ed60
--- /dev/null
+++ b/mysql-test/suite/mariabackup/log_page_corruption.opt
@@ -0,0 +1 @@
+--innodb-checksum-algorithm=crc32
diff --git a/mysql-test/suite/mariabackup/log_page_corruption.result b/mysql-test/suite/mariabackup/log_page_corruption.result
new file mode 100644
index 00000000000..be29ea435b6
--- /dev/null
+++ b/mysql-test/suite/mariabackup/log_page_corruption.result
@@ -0,0 +1,145 @@
+########
+# Test for generating "innodb_corrupted_pages" file during full and
+# incremental backup, including DDL processing
+###
+
+CREATE TABLE t1_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t2_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t3(c INT) ENGINE INNODB;
+CREATE TABLE t5_corrupted_to_rename(c INT) ENGINE INNODB;
+CREATE TABLE t6_corrupted_to_drop(c INT) ENGINE INNODB;
+CREATE TABLE t7_corrupted_to_alter(c INT) ENGINE INNODB;
+CREATE TABLE t1_inc_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t2_inc_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t3_inc(c INT) ENGINE INNODB;
+CREATE TABLE t5_inc_corrupted_to_rename(c INT) ENGINE INNODB;
+CREATE TABLE t6_inc_corrupted_to_drop(c INT) ENGINE INNODB;
+CREATE TABLE t7_inc_corrupted_to_alter(c INT) ENGINE INNODB;
+INSERT INTO t1_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t2_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t3 VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t5_corrupted_to_rename VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t6_corrupted_to_drop VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t7_corrupted_to_alter VALUES (3), (4), (5), (6), (7), (8), (9);
+# Corrupt tables
+# restart
+# Backup must fail due to page corruption
+FOUND 1 /Database page corruption detected.*/ in backup.log
+# "innodb_corrupted_pages" file must not exist
+# Backup must fail, but "innodb_corrupted_pages" file must be created due to --log-innodb-page-corruption option
+FOUND 1 /Database page corruption detected.*/ in backup.log
+--- "innodb_corrupted_pages" file content: ---
+test/t1_corrupted
+6 8 9
+test/t2_corrupted
+7 8 10
+test/t4_corrupted_new
+1
+test/t5_corrupted_to_rename_renamed
+6
+test/t7_corrupted_to_alter
+3
+------
+INSERT INTO t1_inc_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t2_inc_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t3_inc VALUES (3), (4), (5), (6), (7), (8), (9);
+# restart
+# Backup must fail, but "innodb_corrupted_pages" file must be created due to --log-innodb-page-corruption option
+--- "innodb_corrupted_pages" file content: ---
+test/t1_corrupted
+6 8 9
+test/t1_inc_corrupted
+6 8 9
+test/t2_corrupted
+7 8 10
+test/t2_inc_corrupted
+7 8 10
+test/t4_inc_corrupted_new
+1
+test/t5_corrupted_to_rename_renamed
+6
+test/t5_inc_corrupted_to_rename_renamed
+6
+test/t7_inc_corrupted_to_alter
+3
+------
+# Check if corrupted pages were copied to delta files, and non-corrupted pages are not copied.
+DROP TABLE t1_corrupted;
+DROP TABLE t2_corrupted;
+DROP TABLE t4_corrupted_new;
+DROP TABLE t5_corrupted_to_rename_renamed;
+DROP TABLE t7_corrupted_to_alter;
+DROP TABLE t1_inc_corrupted;
+DROP TABLE t2_inc_corrupted;
+DROP TABLE t4_inc_corrupted_new;
+DROP TABLE t5_inc_corrupted_to_rename_renamed;
+DROP TABLE t7_inc_corrupted_to_alter;
+
+########
+# Test for --prepare with "innodb_corrupted_pages" file
+###
+
+# Extend some tablespace and corrupt extended pages for full backup
+# restart
+# Full backup with --log-innodb-page-corruption
+--- "innodb_corrupted_pages" file content: ---
+test/t3
+6 8
+------
+# Extend some tablespace and corrupt extended pages for incremental backup
+# restart
+# Incremental backup --log-innodb-page-corruption
+--- "innodb_corrupted_pages" file content: ---
+test/t3
+6 8
+test/t3_inc
+6 8
+------
+# Full backup prepare
+# "innodb_corrupted_pages" file must not exist after successful prepare
+FOUND 1 /was successfuly fixed.*/ in backup.log
+# Check that fixed pages are zero-filled
+# Incremental backup prepare
+# "innodb_corrupted_pages" file must not exist after successful prepare
+# do not remove "innodb_corrupted_pages" in incremental dir
+FOUND 1 /was successfuly fixed.*/ in backup.log
+# Check that fixed pages are zero-filled
+# shutdown server
+# remove datadir
+# xtrabackup move back
+# restart
+SELECT * FROM t3;
+c
+3
+4
+5
+6
+7
+8
+9
+SELECT * FROM t3_inc;
+c
+3
+4
+5
+6
+7
+8
+9
+# Test the case when not all corrupted pages are fixed
+
+# Add some fake corrupted pages
+# Full backup prepare
+FOUND 1 /Error: corrupted page.*/ in backup.log
+--- "innodb_corrupted_pages" file content: ---
+test/t3
+3
+------
+# Incremental backup prepare
+FOUND 1 /Error: corrupted page.*/ in backup.log
+--- "innodb_corrupted_pages" file content: ---
+test/t3
+3
+------
+DROP TABLE t3;
+DROP TABLE t3_inc;
diff --git a/mysql-test/suite/mariabackup/log_page_corruption.test b/mysql-test/suite/mariabackup/log_page_corruption.test
new file mode 100644
index 00000000000..e9419687288
--- /dev/null
+++ b/mysql-test/suite/mariabackup/log_page_corruption.test
@@ -0,0 +1,426 @@
+--source include/have_debug.inc
+
+--echo ########
+--echo # Test for generating "innodb_corrupted_pages" file during full and
+--echo # incremental backup, including DDL processing
+--echo ###
+--echo
+
+CREATE TABLE t1_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t2_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t3(c INT) ENGINE INNODB;
+CREATE TABLE t5_corrupted_to_rename(c INT) ENGINE INNODB;
+CREATE TABLE t6_corrupted_to_drop(c INT) ENGINE INNODB;
+CREATE TABLE t7_corrupted_to_alter(c INT) ENGINE INNODB;
+
+CREATE TABLE t1_inc_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t2_inc_corrupted(c INT) ENGINE INNODB;
+CREATE TABLE t3_inc(c INT) ENGINE INNODB;
+CREATE TABLE t5_inc_corrupted_to_rename(c INT) ENGINE INNODB;
+CREATE TABLE t6_inc_corrupted_to_drop(c INT) ENGINE INNODB;
+CREATE TABLE t7_inc_corrupted_to_alter(c INT) ENGINE INNODB;
+
+# Fill tables with several pages
+INSERT INTO t1_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t2_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t3 VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t5_corrupted_to_rename VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t6_corrupted_to_drop VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t7_corrupted_to_alter VALUES (3), (4), (5), (6), (7), (8), (9);
+
+--let MYSQLD_DATADIR=`select @@datadir`
+--let INNODB_PAGE_SIZE=`select @@innodb_page_size`
+
+--source include/shutdown_mysqld.inc
+--echo # Corrupt tables
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+my $schema = "$ENV{MYSQLD_DATADIR}/test";
+
+my $last_page_no = extend_space("$schema/t1_corrupted.ibd", 4);
+corrupt_space_page_id("$schema/t1_corrupted.ibd",
+ $last_page_no, $last_page_no + 2, $last_page_no + 3);
+
+$last_page_no = extend_space("$schema/t2_corrupted.ibd", 5);
+corrupt_space_page_id("$schema/t2_corrupted.ibd",
+ $last_page_no + 1, $last_page_no + 2, $last_page_no + 4);
+
+$last_page_no = extend_space("$schema/t5_corrupted_to_rename.ibd", 1);
+corrupt_space_page_id("$schema/t5_corrupted_to_rename.ibd", $last_page_no);
+
+$last_page_no = extend_space("$schema/t6_corrupted_to_drop.ibd", );
+corrupt_space_page_id("$schema/t6_corrupted_to_drop.ibd", $last_page_no);
+EOF
+--source include/start_mysqld.inc
+
+--let targetdir=$MYSQLTEST_VARDIR/tmp/backup
+--let $backuplog=$MYSQLTEST_VARDIR/tmp/backup.log
+--let corrupted_pages_file = $targetdir/innodb_corrupted_pages
+--let corrupted_pages_file_filt = $MYSQLTEST_VARDIR/tmp/innodb_corrupted_pages_filt
+--let perl_result_file=$MYSQLTEST_VARDIR/tmp/perl_result
+
+--echo # Backup must fail due to page corruption
+--disable_result_log
+--error 1
+exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir > $backuplog;
+--enable_result_log
+
+--let SEARCH_PATTERN=Database page corruption detected.*
+--let SEARCH_FILE=$backuplog
+--source include/search_pattern_in_file.inc
+--echo # "innodb_corrupted_pages" file must not exist
+--error 1
+--file_exists $corrupted_pages_file
+--rmdir $targetdir
+
+--let after_load_tablespaces=CREATE TABLE test.t4_corrupted_new ENGINE=INNODB SELECT UUID() from test.seq_1_to_10
+--let add_corrupted_page_for_test_t4_corrupted_new=1
+--let after_copy_test_t5_corrupted_to_rename=RENAME TABLE test.t5_corrupted_to_rename TO test.t5_corrupted_to_rename_renamed
+--let after_copy_test_t6_corrupted_to_drop=DROP TABLE test.t6_corrupted_to_drop
+--let after_copy_test_t7_corrupted_to_alter=ALTER TABLE test.t7_corrupted_to_alter ADD COLUMN (d INT)
+--let add_corrupted_page_for_test_t7_corrupted_to_alter=3
+
+--echo # Backup must fail, but "innodb_corrupted_pages" file must be created due to --log-innodb-page-corruption option
+--disable_result_log
+--error 1
+--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --log-innodb-page-corruption --target-dir=$targetdir --dbug=+d,mariabackup_events,mariabackup_inject_code > $backuplog
+--enable_result_log
+
+--let SEARCH_PATTERN=Database page corruption detected.*
+--let SEARCH_FILE=$backuplog
+--source include/search_pattern_in_file.inc
+--echo --- "innodb_corrupted_pages" file content: ---
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+print_corrupted_pages_file($ENV{corrupted_pages_file},
+ $ENV{corrupted_pages_file_filt});
+EOF
+--cat_file $corrupted_pages_file_filt
+--echo ------
+--let after_load_tablespaces=
+--let add_corrupted_page_for_test_t4_corrupted_new=
+--let after_copy_test_t5_corrupted_to_rename=
+--let after_copy_test_t6_corrupted_to_drop=
+--let after_copy_test_t7_corrupted_to_alter=
+--let add_corrupted_page_for_test_t7_corrupted_to_alter=
+# Fill tables for incremental backup with several pages
+INSERT INTO t1_inc_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t2_inc_corrupted VALUES (3), (4), (5), (6), (7), (8), (9);
+INSERT INTO t3_inc VALUES (3), (4), (5), (6), (7), (8), (9);
+
+--source include/shutdown_mysqld.inc
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+my $schema="$ENV{MYSQLD_DATADIR}/test";
+
+open(my $fh, '>', $ENV{perl_result_file}) or die $!;
+
+my $last_page_no = extend_space("$schema/t1_inc_corrupted.ibd", 4);
+corrupt_space_page_id("$schema/t1_inc_corrupted.ibd",
+ $last_page_no, $last_page_no + 2, $last_page_no + 3);
+print $fh "$last_page_no\n";
+
+$last_page_no = extend_space("$schema/t2_inc_corrupted.ibd", 5);
+corrupt_space_page_id("$schema/t2_inc_corrupted.ibd",
+ $last_page_no + 1, $last_page_no + 2, $last_page_no + 4);
+print $fh "$last_page_no\n";
+
+$last_page_no = extend_space("$schema/t5_inc_corrupted_to_rename.ibd", 1);
+corrupt_space_page_id("$schema/t5_inc_corrupted_to_rename.ibd", $last_page_no);
+print $fh "$last_page_no\n";
+
+$last_page_no = extend_space("$schema/t6_inc_corrupted_to_drop.ibd", );
+corrupt_space_page_id("$schema/t6_inc_corrupted_to_drop.ibd", $last_page_no);
+
+close $fh;
+EOF
+--source include/start_mysqld.inc
+
+--let incdir=$MYSQLTEST_VARDIR/tmp/backup_inc
+
+--let after_load_tablespaces=CREATE TABLE test.t4_inc_corrupted_new ENGINE=INNODB SELECT UUID() from test.seq_1_to_10
+--let add_corrupted_page_for_test_t4_inc_corrupted_new=1
+--let after_copy_test_t5_inc_corrupted_to_rename=RENAME TABLE test.t5_inc_corrupted_to_rename TO test.t5_inc_corrupted_to_rename_renamed
+--let after_copy_test_t6_inc_corrupted_to_drop=DROP TABLE test.t6_inc_corrupted_to_drop
+--let after_copy_test_t7_inc_corrupted_to_alter=ALTER TABLE test.t7_inc_corrupted_to_alter ADD COLUMN (d INT)
+--let add_corrupted_page_for_test_t7_inc_corrupted_to_alter=3
+
+--echo # Backup must fail, but "innodb_corrupted_pages" file must be created due to --log-innodb-page-corruption option
+--disable_result_log
+--error 1
+--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --log-innodb-page-corruption --target-dir=$incdir --incremental-basedir=$targetdir --dbug=+d,mariabackup_events,mariabackup_inject_code > $backuplog
+--disable_result_log
+
+--let after_load_tablespaces=
+--let add_corrupted_page_for_test_t4_inc_corrupted_new=
+--let after_copy_test_t5_inc_corrupted_to_rename=
+--let after_copy_test_t6_inc_corrupted_to_drop=
+--let after_copy_test_t7_inc_corrupted_to_alter=
+--let add_corrupted_page_for_test_t7_inc_corrupted_to_alter=
+
+--let SEARCH_PATTERN=Database page corruption detected.*
+--let SEARCH_FILE=$backuplog
+--source include/search_pattern_in_file.inc
+--let corrupted_pages_file = $incdir/innodb_corrupted_pages
+--echo --- "innodb_corrupted_pages" file content: ---
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+print_corrupted_pages_file($ENV{corrupted_pages_file},
+ $ENV{corrupted_pages_file_filt});
+EOF
+--cat_file $corrupted_pages_file_filt
+--echo ------
+
+--echo # Check if corrupted pages were copied to delta files, and non-corrupted pages are not copied.
+perl;
+use strict;
+use warnings;
+my $schema = "$ENV{incdir}/test";
+
+open(my $fh, '<', $ENV{perl_result_file}) or die $!;
+
+my $last_page_no = <$fh>;
+die_if_no_pages("$schema/t1_corrupted.ibd.delta",
+ $last_page_no, $last_page_no + 2, $last_page_no + 3);
+
+$last_page_no = <$fh>;
+die_if_no_pages("$schema/t2_corrupted.ibd.delta",
+ $last_page_no + 1, $last_page_no + 2, $last_page_no + 4);
+
+$last_page_no = <$fh>;
+die_if_no_pages("$schema/t5_corrupted_to_rename_renamed.ibd.delta",
+ $last_page_no);
+
+close $fh;
+
+die_if_not_empty("$schema/t3.ibd.delta");
+
+sub read_first_page_from_delta {
+ my $file_name = shift;
+ my $pages_count = shift;
+
+ open my $file, '<:raw', $file_name || die "Cannot open $file_name\n";
+ read $file, my $buffer, $pages_count*4 || die "Cannot read $file_name\n";
+ close $file;
+
+ return unpack("N[$pages_count]", $buffer);
+}
+
+sub die_if_no_pages {
+ my $file_name = shift;
+ my @check_pages = @_;
+ my @read_pages =
+ read_first_page_from_delta($file_name, scalar(@check_pages) + 1);
+ for (my $i = 1; $i < @check_pages + 1; ++$i) {
+ my $check_page_no = $check_pages[$i - 1];
+ die "Corrupted page $check_page_no was not copied to $file_name."
+ if ($i >= @read_pages || $read_pages[$i] != $check_page_no);
+ }
+}
+
+sub die_if_not_empty {
+ my $file_name = shift;
+ my ($magic, $full) = read_first_page_from_delta($file_name, 2);
+ die "Delta $file_name must be empty."
+ if ($full != 0xFFFFFFFF);
+}
+EOF
+--rmdir $incdir
+--rmdir $targetdir
+
+DROP TABLE t1_corrupted;
+DROP TABLE t2_corrupted;
+DROP TABLE t4_corrupted_new;
+DROP TABLE t5_corrupted_to_rename_renamed;
+DROP TABLE t7_corrupted_to_alter;
+DROP TABLE t1_inc_corrupted;
+DROP TABLE t2_inc_corrupted;
+DROP TABLE t4_inc_corrupted_new;
+DROP TABLE t5_inc_corrupted_to_rename_renamed;
+DROP TABLE t7_inc_corrupted_to_alter;
+
+--echo
+--echo ########
+--echo # Test for --prepare with "innodb_corrupted_pages" file
+--echo ###
+--echo
+
+--echo # Extend some tablespace and corrupt extended pages for full backup
+--source include/shutdown_mysqld.inc
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+my $schema="$ENV{MYSQLD_DATADIR}/test";
+my $last_page_no = extend_space("$schema/t3.ibd", 3);
+corrupt_space_page_id("$schema/t3.ibd", $last_page_no, $last_page_no + 2);
+open(my $fh, '>', $ENV{perl_result_file}) or die $!;
+print $fh "$last_page_no\n";
+close $fh;
+EOF
+--source include/start_mysqld.inc
+
+--echo # Full backup with --log-innodb-page-corruption
+--disable_result_log
+--error 1
+--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --log-innodb-page-corruption --target-dir=$targetdir
+--enable_result_log
+--let corrupted_pages_file = $targetdir/innodb_corrupted_pages
+--echo --- "innodb_corrupted_pages" file content: ---
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+print_corrupted_pages_file($ENV{corrupted_pages_file},
+ $ENV{corrupted_pages_file_filt});
+EOF
+--cat_file $corrupted_pages_file_filt
+--echo ------
+
+--echo # Extend some tablespace and corrupt extended pages for incremental backup
+--source include/shutdown_mysqld.inc
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+my $schema="$ENV{MYSQLD_DATADIR}/test";
+my $last_page_no = extend_space("$schema/t3_inc.ibd", 3);
+corrupt_space_page_id("$schema/t3_inc.ibd", $last_page_no, $last_page_no + 2);
+open(my $fh, '>>', $ENV{perl_result_file}) or die $!;
+print $fh "$last_page_no";
+close $fh;
+EOF
+--source include/start_mysqld.inc
+
+--echo # Incremental backup --log-innodb-page-corruption
+--disable_result_log
+--error 1
+--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --log-innodb-page-corruption --target-dir=$incdir --incremental-basedir=$targetdir --dbug=+d,mariabackup_events,mariabackup_inject_code > $backuplog
+--disable_result_log
+--let corrupted_pages_file = $incdir/innodb_corrupted_pages
+--echo --- "innodb_corrupted_pages" file content: ---
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+print_corrupted_pages_file($ENV{corrupted_pages_file},
+ $ENV{corrupted_pages_file_filt});
+EOF
+--cat_file $corrupted_pages_file_filt
+--echo ------
+
+--let targetdir2=$targetdir-2
+--let incdir2=$incdir-2
+perl;
+use lib "lib";
+use My::Handles { suppress_init_messages => 1 };
+use My::File::Path;
+copytree($ENV{'targetdir'}, $ENV{'targetdir2'});
+copytree($ENV{'incdir'}, $ENV{'incdir2'});
+EOF
+
+--echo # Full backup prepare
+--disable_result_log
+exec $XTRABACKUP --prepare --target-dir=$targetdir > $backuplog;
+--enable_result_log
+
+--echo # "innodb_corrupted_pages" file must not exist after successful prepare
+--error 1
+--file_exists $targetdir/innodb_corrupted_pages
+--let SEARCH_PATTERN=was successfuly fixed.*
+--let SEARCH_FILE=$backuplog
+--source include/search_pattern_in_file.inc
+
+--echo # Check that fixed pages are zero-filled
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+open(my $fh, '<', $ENV{perl_result_file}) or die $!;
+my $last_page_no = <$fh>;
+close $fh;
+my $schema = "$ENV{targetdir}/test";
+die_if_page_is_not_zero("$schema/t3.ibd", $last_page_no, $last_page_no + 2);
+EOF
+
+--echo # Incremental backup prepare
+--disable_result_log
+exec $XTRABACKUP --prepare --target-dir=$targetdir --incremental-dir=$incdir > $backuplog;
+--enable_result_log
+
+--echo # "innodb_corrupted_pages" file must not exist after successful prepare
+--error 1
+--file_exists $targetdir/innodb_corrupted_pages
+--echo # do not remove "innodb_corrupted_pages" in incremental dir
+--file_exists $incdir/innodb_corrupted_pages
+--let SEARCH_PATTERN=was successfuly fixed.*
+--let SEARCH_FILE=$backuplog
+--source include/search_pattern_in_file.inc
+
+--echo # Check that fixed pages are zero-filled
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+open(my $fh, '<', $ENV{perl_result_file}) or die $!;
+my $last_page_no_full = <$fh>;
+my $last_page_no_inc = <$fh>;
+close $fh;
+my $schema = "$ENV{targetdir}/test";
+die_if_page_is_not_zero("$schema/t3.ibd",
+ $last_page_no_full, $last_page_no_full + 2);
+die_if_page_is_not_zero("$schema/t3_inc.ibd",
+ $last_page_no_inc, $last_page_no_inc + 2);
+EOF
+
+--source include/restart_and_restore.inc
+
+SELECT * FROM t3;
+SELECT * FROM t3_inc;
+
+--echo # Test the case when not all corrupted pages are fixed
+--echo
+--echo # Add some fake corrupted pages
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+append_corrupted_pages(
+ "$ENV{targetdir2}/innodb_corrupted_pages", 'test/t3', '3 4');
+append_corrupted_pages(
+ "$ENV{incdir2}/innodb_corrupted_pages", 'test/t3_inc', '4 5');
+EOF
+
+--echo # Full backup prepare
+--disable_result_log
+--error 1
+exec $XTRABACKUP --prepare --target-dir=$targetdir2 > $backuplog;
+--enable_result_log
+
+--let SEARCH_PATTERN=Error: corrupted page.*
+--let SEARCH_FILE=$backuplog
+--source include/search_pattern_in_file.inc
+--let corrupted_pages_file = $targetdir2/innodb_corrupted_pages
+--echo --- "innodb_corrupted_pages" file content: ---
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+print_corrupted_pages_file($ENV{corrupted_pages_file},
+ $ENV{corrupted_pages_file_filt});
+EOF
+--cat_file $corrupted_pages_file_filt
+--echo ------
+
+--echo # Incremental backup prepare
+--disable_result_log
+--error 1
+exec $XTRABACKUP --prepare --target-dir=$targetdir2 --incremental-dir=$incdir2 > $backuplog;
+--enable_result_log
+
+--let SEARCH_PATTERN=Error: corrupted page.*
+--let SEARCH_FILE=$backuplog
+--source include/search_pattern_in_file.inc
+--let corrupted_pages_file = $targetdir2/innodb_corrupted_pages
+--echo --- "innodb_corrupted_pages" file content: ---
+perl;
+do "$ENV{MTR_SUITE_DIR}/include/corrupt-page.pl";
+print_corrupted_pages_file($ENV{corrupted_pages_file},
+ $ENV{corrupted_pages_file_filt});
+EOF
+--cat_file $corrupted_pages_file_filt
+--echo ------
+
+DROP TABLE t3;
+DROP TABLE t3_inc;
+--remove_file $backuplog
+--remove_file $perl_result_file
+--remove_file $corrupted_pages_file_filt
+--rmdir $targetdir
+--rmdir $targetdir2
+--rmdir $incdir
+--rmdir $incdir2
diff --git a/mysql-test/suite/roles/show_grants.result b/mysql-test/suite/roles/show_grants.result
index 1867133525d..7ae499a9cfc 100644
--- a/mysql-test/suite/roles/show_grants.result
+++ b/mysql-test/suite/roles/show_grants.result
@@ -147,3 +147,18 @@ drop role test_role2;
delete from mysql.roles_mapping where Role='test_role1';
delete from mysql.roles_mapping where Role='test_role2';
flush privileges;
+#
+# MDEV-24289: show grants missing with grant option
+#
+create role anel;
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO 'anel';
+SHOW GRANTS for 'anel';
+Grants for anel
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO `anel`
+create role MariaDB_admin;
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO 'MariaDB_admin' WITH GRANT OPTION;
+SHOW GRANTS for 'MariaDB_admin';
+Grants for MariaDB_admin
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO `MariaDB_admin` WITH GRANT OPTION
+drop role MariaDB_admin;
+drop role anel;
diff --git a/mysql-test/suite/roles/show_grants.test b/mysql-test/suite/roles/show_grants.test
index 9c15d8b8b2b..fc2165ac53b 100644
--- a/mysql-test/suite/roles/show_grants.test
+++ b/mysql-test/suite/roles/show_grants.test
@@ -88,3 +88,16 @@ drop role test_role2;
delete from mysql.roles_mapping where Role='test_role1';
delete from mysql.roles_mapping where Role='test_role2';
flush privileges;
+
+--echo #
+--echo # MDEV-24289: show grants missing with grant option
+--echo #
+create role anel;
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO 'anel';
+SHOW GRANTS for 'anel';
+
+create role MariaDB_admin;
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO 'MariaDB_admin' WITH GRANT OPTION;
+SHOW GRANTS for 'MariaDB_admin';
+drop role MariaDB_admin;
+drop role anel;