summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Malyavin <nikitamalyavin@gmail.com>2022-07-22 02:18:21 +0300
committerNikita Malyavin <nikitamalyavin@gmail.com>2022-08-23 16:36:28 +0300
commitca4f84b95b42a87ca9f828ec2b963c91a1e308d6 (patch)
tree875013c3a692273971ea12c396311ff7edcf73ab
parent955fceb53fdc98996b087ff901bae70031543ed2 (diff)
downloadmariadb-git-bb-10.10-MDEV-29181.tar.gz
MDEV-29068 Cascade foreign key updates do not apply in online alterbb-10.10-MDEV-29181
Foreign key cascade operations are still implemented through innodb. Add support for online logging in row_ins_foreign_check_on_constraint. upd_node_t::sql_is_online_alter is added to reuse cascade->row and cascade->upd_row.
-rw-r--r--mysql-test/main/alter_table_online_debug.result326
-rw-r--r--mysql-test/main/alter_table_online_debug.test166
-rw-r--r--sql/sql_class.cc29
-rw-r--r--sql/table.h9
-rw-r--r--storage/innobase/btr/btr0cur.cc3
-rw-r--r--storage/innobase/row/row0ins.cc108
-rw-r--r--storage/innobase/row/row0upd.cc11
7 files changed, 649 insertions, 3 deletions
diff --git a/mysql-test/main/alter_table_online_debug.result b/mysql-test/main/alter_table_online_debug.result
index 18b552ca53a..2797d691174 100644
--- a/mysql-test/main/alter_table_online_debug.result
+++ b/mysql-test/main/alter_table_online_debug.result
@@ -1072,5 +1072,331 @@ xa rollback 'xid';
drop table t;
set debug_sync= reset;
#
+# MDEV-29068 Cascade foreign key updates do not apply in online alter
+#
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+create table t2 (b int, foreign key (b) references t1 (a) on delete cascade on update cascade)
+engine=innodb;
+insert into t2 values (1),(2),(3);
+set debug_sync= 'now wait_for downgraded';
+connection con2;
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+alter table t2 add c int default(b), algorithm=copy, lock=none;
+connection default;
+update t1 set a = 22 where a = 2;
+set debug_sync= 'now signal goforit';
+connection con2;
+select * from t2;
+b c
+1 1
+22 22
+3 3
+connection default;
+drop table t2, t1;
+set debug_sync= reset;
+#
+# Big BLOB
+#
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+create table t2 (a int, b text,
+foreign key (a) references t1 (a) on delete cascade on update cascade)
+engine=innodb ROW_FORMAT=COMPRESSED;
+insert into t2 values (1, NULL),(2, REPEAT('a', 65535)),(3, 'xyz');
+set debug_sync= 'now wait_for downgraded';
+connection con2;
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+alter table t2 add c int default(a), algorithm=copy, lock=none;
+connection default;
+update t1 set a = 22 where a = 2;
+delete from t1 where a = 3;
+set debug_sync= 'now signal goforit';
+connection con2;
+select a, if(b = REPEAT('a', 65535), 1, b) from t2;
+a if(b = REPEAT('a', 65535), 1, b)
+1 NULL
+22 1
+connection default;
+drop table t2, t1;
+set debug_sync= reset;
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+create table t2 (a int, b text,
+foreign key (a) references t1 (a) on delete cascade on update cascade)
+engine=innodb ROW_FORMAT=DYNAMIC;
+insert into t2 values (1, NULL),(2, REPEAT('a', 65535)),(3, 'xyz');
+set debug_sync= 'now wait_for downgraded';
+connection con2;
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+alter table t2 add c int default(a), algorithm=copy, lock=none;
+connection default;
+update t1 set a = 22 where a = 2;
+delete from t1 where a = 3;
+set debug_sync= 'now signal goforit';
+connection con2;
+select a, if(b = REPEAT('a', 65535), 1, b) from t2;
+a if(b = REPEAT('a', 65535), 1, b)
+1 NULL
+22 1
+connection default;
+drop table t2, t1;
+set debug_sync= reset;
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+create table t2 (a int, b text,
+foreign key (a) references t1 (a) on delete cascade on update cascade)
+engine=innodb ROW_FORMAT=COMPACT;
+insert into t2 values (1, NULL),(2, REPEAT('a', 65535)),(3, 'xyz');
+set debug_sync= 'now wait_for downgraded';
+connection con2;
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+alter table t2 add c int default(a), algorithm=copy, lock=none;
+connection default;
+update t1 set a = 22 where a = 2;
+delete from t1 where a = 3;
+set debug_sync= 'now signal goforit';
+connection con2;
+select a, if(b = REPEAT('a', 65535), 1, b) from t2;
+a if(b = REPEAT('a', 65535), 1, b)
+1 NULL
+22 1
+connection default;
+drop table t2, t1;
+set debug_sync= reset;
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+create table t2 (a int, b text,
+foreign key (a) references t1 (a) on delete cascade on update cascade)
+engine=innodb ROW_FORMAT=REDUNDANT;
+insert into t2 values (1, NULL),(2, REPEAT('a', 65535)),(3, 'xyz');
+set debug_sync= 'now wait_for downgraded';
+connection con2;
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+alter table t2 add c int default(a), algorithm=copy, lock=none;
+connection default;
+update t1 set a = 22 where a = 2;
+delete from t1 where a = 3;
+set debug_sync= 'now signal goforit';
+connection con2;
+select a, if(b = REPEAT('a', 65535), 1, b) from t2;
+a if(b = REPEAT('a', 65535), 1, b)
+1 NULL
+22 1
+connection default;
+drop table t2, t1;
+set debug_sync= reset;
+#
+# VCOLs
+#
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+create table t2 (a int, b text as (REPEAT('a', 65535)), key(b(20)),
+foreign key (a) references t1 (a) on delete cascade on update cascade)
+engine=innodb;
+insert into t2(a) values (1),(2),(3);
+set debug_sync= 'now wait_for downgraded';
+connection con2;
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+alter table t2 add c int default(a), algorithm=copy, lock=none;
+connection default;
+update t1 set a = 22 where a = 2;
+set debug_sync= 'now signal goforit';
+connection con2;
+select a, if(length(b) > 1, b = REPEAT('a', 65535), b) from t2;
+a if(length(b) > 1, b = REPEAT('a', 65535), b)
+1 1
+22 1
+3 1
+connection default;
+drop table t2, t1;
+set debug_sync= reset;
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+create table t2 (a int, b text, c text as (b),
+foreign key (a) references t1 (a) on delete cascade on update cascade)
+engine=innodb;
+insert into t2(a, b) values (1, NULL),(2, REPEAT('a', 65535)),(3, 'x');
+set debug_sync= 'now wait_for downgraded';
+connection con2;
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+alter table t2 add d char DEFAULT(SUBSTR(b, 1, 1)), algorithm=copy, lock=none;
+connection default;
+update t1 set a = 22 where a = 2;
+delete from t1 where a = 3;
+set debug_sync= 'now signal goforit';
+connection con2;
+select a, if(length(b) > 1, b = REPEAT('a', 65535), b), c = b, d from t2;
+a if(length(b) > 1, b = REPEAT('a', 65535), b) c = b d
+1 NULL NULL NULL
+22 1 1 a
+connection default;
+drop table t2, t1;
+set debug_sync= reset;
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+create table t2 (b int, foreign key (b) references t1 (a) on delete set null on update set null)
+engine=innodb;
+insert into t2 values (1),(2),(3);
+set debug_sync= 'now wait_for downgraded';
+connection con2;
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+alter table t2 add c int default(b), algorithm=copy, lock=none;
+connection default;
+update t1 set a = 22 where a = 2;
+set debug_sync= 'now signal goforit';
+connection con2;
+select * from t2;
+b c
+1 1
+NULL NULL
+3 3
+connection default;
+drop table t2, t1;
+set debug_sync= reset;
+#
+# Big BLOB
+#
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+create table t2 (a int, b text,
+foreign key (a) references t1 (a) on delete set null on update set null)
+engine=innodb ROW_FORMAT=COMPRESSED;
+insert into t2 values (1, NULL),(2, REPEAT('a', 65535)),(3, 'xyz');
+set debug_sync= 'now wait_for downgraded';
+connection con2;
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+alter table t2 add c int default(a), algorithm=copy, lock=none;
+connection default;
+update t1 set a = 22 where a = 2;
+delete from t1 where a = 3;
+set debug_sync= 'now signal goforit';
+connection con2;
+select a, if(b = REPEAT('a', 65535), 1, b) from t2;
+a if(b = REPEAT('a', 65535), 1, b)
+1 NULL
+NULL 1
+NULL xyz
+connection default;
+drop table t2, t1;
+set debug_sync= reset;
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+create table t2 (a int, b text,
+foreign key (a) references t1 (a) on delete set null on update set null)
+engine=innodb ROW_FORMAT=DYNAMIC;
+insert into t2 values (1, NULL),(2, REPEAT('a', 65535)),(3, 'xyz');
+set debug_sync= 'now wait_for downgraded';
+connection con2;
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+alter table t2 add c int default(a), algorithm=copy, lock=none;
+connection default;
+update t1 set a = 22 where a = 2;
+delete from t1 where a = 3;
+set debug_sync= 'now signal goforit';
+connection con2;
+select a, if(b = REPEAT('a', 65535), 1, b) from t2;
+a if(b = REPEAT('a', 65535), 1, b)
+1 NULL
+NULL 1
+NULL xyz
+connection default;
+drop table t2, t1;
+set debug_sync= reset;
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+create table t2 (a int, b text,
+foreign key (a) references t1 (a) on delete set null on update set null)
+engine=innodb ROW_FORMAT=COMPACT;
+insert into t2 values (1, NULL),(2, REPEAT('a', 65535)),(3, 'xyz');
+set debug_sync= 'now wait_for downgraded';
+connection con2;
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+alter table t2 add c int default(a), algorithm=copy, lock=none;
+connection default;
+update t1 set a = 22 where a = 2;
+delete from t1 where a = 3;
+set debug_sync= 'now signal goforit';
+connection con2;
+select a, if(b = REPEAT('a', 65535), 1, b) from t2;
+a if(b = REPEAT('a', 65535), 1, b)
+1 NULL
+NULL 1
+NULL xyz
+connection default;
+drop table t2, t1;
+set debug_sync= reset;
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+create table t2 (a int, b text,
+foreign key (a) references t1 (a) on delete set null on update set null)
+engine=innodb ROW_FORMAT=REDUNDANT;
+insert into t2 values (1, NULL),(2, REPEAT('a', 65535)),(3, 'xyz');
+set debug_sync= 'now wait_for downgraded';
+connection con2;
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+alter table t2 add c int default(a), algorithm=copy, lock=none;
+connection default;
+update t1 set a = 22 where a = 2;
+delete from t1 where a = 3;
+set debug_sync= 'now signal goforit';
+connection con2;
+select a, if(b = REPEAT('a', 65535), 1, b) from t2;
+a if(b = REPEAT('a', 65535), 1, b)
+1 NULL
+NULL 1
+NULL xyz
+connection default;
+drop table t2, t1;
+set debug_sync= reset;
+#
+# VCOLs
+#
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+create table t2 (a int, b text as (REPEAT('a', 65535)), key(b(20)),
+foreign key (a) references t1 (a) on delete set null on update set null)
+engine=innodb;
+insert into t2(a) values (1),(2),(3);
+set debug_sync= 'now wait_for downgraded';
+connection con2;
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+alter table t2 add c int default(a), algorithm=copy, lock=none;
+connection default;
+update t1 set a = 22 where a = 2;
+set debug_sync= 'now signal goforit';
+connection con2;
+select a, if(length(b) > 1, b = REPEAT('a', 65535), b) from t2;
+a if(length(b) > 1, b = REPEAT('a', 65535), b)
+1 1
+NULL 1
+3 1
+connection default;
+drop table t2, t1;
+set debug_sync= reset;
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+create table t2 (a int, b text, c text as (b),
+foreign key (a) references t1 (a) on delete set null on update set null)
+engine=innodb;
+insert into t2(a, b) values (1, NULL),(2, REPEAT('a', 65535)),(3, 'x');
+set debug_sync= 'now wait_for downgraded';
+connection con2;
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+alter table t2 add d char DEFAULT(SUBSTR(b, 1, 1)), algorithm=copy, lock=none;
+connection default;
+update t1 set a = 22 where a = 2;
+delete from t1 where a = 3;
+set debug_sync= 'now signal goforit';
+connection con2;
+select a, if(length(b) > 1, b = REPEAT('a', 65535), b), c = b, d from t2;
+a if(length(b) > 1, b = REPEAT('a', 65535), b) c = b d
+1 NULL NULL NULL
+NULL 1 1 a
+NULL x 1 x
+connection default;
+drop table t2, t1;
+set debug_sync= reset;
+#
# End of 10.10 tests
#
diff --git a/mysql-test/main/alter_table_online_debug.test b/mysql-test/main/alter_table_online_debug.test
index 719eb4c477d..cdf41622b22 100644
--- a/mysql-test/main/alter_table_online_debug.test
+++ b/mysql-test/main/alter_table_online_debug.test
@@ -1251,6 +1251,172 @@ xa rollback 'xid';
drop table t;
set debug_sync= reset;
+
+--echo #
+--echo # MDEV-29068 Cascade foreign key updates do not apply in online alter
+--echo #
+
+--let $cascade_rule= on delete cascade on update cascade
+
+--let $i=2
+
+--while ($i) {
+
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+eval create table t2 (b int, foreign key (b) references t1 (a) $cascade_rule)
+ engine=innodb;
+insert into t2 values (1),(2),(3);
+--send
+set debug_sync= 'now wait_for downgraded';
+
+--connection con2
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+--send
+alter table t2 add c int default(b), algorithm=copy, lock=none;
+
+--connection default
+--reap
+update t1 set a = 22 where a = 2;
+set debug_sync= 'now signal goforit';
+
+--connection con2
+--reap
+select * from t2;
+
+# Cleanup
+--connection default
+drop table t2, t1;
+set debug_sync= reset;
+
+--echo #
+--echo # Big BLOB
+--echo #
+--let $j = 4
+--while($j){
+--let $jj=$j
+--dec $jj
+--if (!$jj){
+--let $row_format=REDUNDANT
+--}
+--dec $jj
+--if (!$jj){
+--let $row_format=COMPACT
+--}
+--dec $jj
+--if (!$jj){
+--let $row_format=DYNAMIC
+--}
+--dec $jj
+--if (!$jj){
+--let $row_format=COMPRESSED
+--}
+
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+eval create table t2 (a int, b text,
+ foreign key (a) references t1 (a) $cascade_rule)
+ engine=innodb ROW_FORMAT=$row_format;
+insert into t2 values (1, NULL),(2, REPEAT('a', 65535)),(3, 'xyz');
+--send
+set debug_sync= 'now wait_for downgraded';
+
+--connection con2
+
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+--send
+alter table t2 add c int default(a), algorithm=copy, lock=none;
+
+--connection default
+--reap
+update t1 set a = 22 where a = 2;
+delete from t1 where a = 3;
+set debug_sync= 'now signal goforit';
+
+--connection con2
+--reap
+select a, if(b = REPEAT('a', 65535), 1, b) from t2;
+
+# Cleanup
+--connection default
+drop table t2, t1;
+set debug_sync= reset;
+
+--dec $j
+# End while($j)
+--}
+
+
+--echo #
+--echo # VCOLs
+--echo #
+
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+eval create table t2 (a int, b text as (REPEAT('a', 65535)), key(b(20)),
+ foreign key (a) references t1 (a) $cascade_rule)
+ engine=innodb;
+insert into t2(a) values (1),(2),(3);
+--send
+set debug_sync= 'now wait_for downgraded';
+
+--connection con2
+
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+--send
+alter table t2 add c int default(a), algorithm=copy, lock=none;
+
+--connection default
+--reap
+update t1 set a = 22 where a = 2;
+set debug_sync= 'now signal goforit';
+
+--connection con2
+--reap
+select a, if(length(b) > 1, b = REPEAT('a', 65535), b) from t2;
+
+# Cleanup
+--connection default
+drop table t2, t1;
+set debug_sync= reset;
+
+
+create table t1 (a int primary key) engine=innodb;
+insert into t1 values (1),(2),(3);
+eval create table t2 (a int, b text, c text as (b),
+ foreign key (a) references t1 (a) $cascade_rule)
+ engine=innodb;
+insert into t2(a, b) values (1, NULL),(2, REPEAT('a', 65535)),(3, 'x');
+--send
+set debug_sync= 'now wait_for downgraded';
+
+--connection con2
+
+set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
+--send
+alter table t2 add d char DEFAULT(SUBSTR(b, 1, 1)), algorithm=copy, lock=none;
+
+--connection default
+--reap
+update t1 set a = 22 where a = 2;
+delete from t1 where a = 3;
+set debug_sync= 'now signal goforit';
+
+--connection con2
+--reap
+select a, if(length(b) > 1, b = REPEAT('a', 65535), b), c = b, d from t2;
+
+# Cleanup
+--connection default
+drop table t2, t1;
+set debug_sync= reset;
+
+--let $cascade_rule= on delete set null on update set null
+--dec $i
+# End while ($i)
+--}
+
+
--echo #
--echo # End of 10.10 tests
--echo #
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 351d0d3ad13..7bc048b3634 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -8180,3 +8180,32 @@ void Charset_loader_server::raise_not_applicable_error(const char *cs,
{
my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), cl, cs);
}
+
+
+int sql_log_cascade_update(TABLE *table)
+{
+#ifdef HAVE_REPLICATION
+ DBUG_ASSERT(table->s->online_alter_binlog);
+ Log_func *log_func= Update_rows_log_event::binlog_row_logging_function;
+ bitmap_set_all(&table->def_read_set);
+ return binlog_log_row_online_alter(table, table->record[0], table->record[1],
+ log_func);
+#else
+ DBUG_ASSERT(0);
+ return 1; // Will report corruption in innodb
+#endif
+}
+
+
+int sql_log_cascade_delete(TABLE *table)
+{
+#ifdef HAVE_REPLICATION
+ DBUG_ASSERT(table->s->online_alter_binlog);
+ Log_func *log_func= Delete_rows_log_event::binlog_row_logging_function;
+ bitmap_set_all(&table->def_read_set);
+ return binlog_log_row_online_alter(table, table->record[0], NULL, log_func);
+#else
+ DBUG_ASSERT(0);
+ return 1; // Will report corruption in innodb
+#endif
+}
diff --git a/sql/table.h b/sql/table.h
index e8abf10af89..a54dcae9958 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1567,6 +1567,15 @@ public:
online_alter_cache_data *online_alter_cache;
+ bool is_online_alter() const
+ {
+#ifdef HAVE_REPLICATION
+ return s->online_alter_binlog != NULL;
+#else
+ return false;
+#endif
+ }
+
inline void reset() { bzero((void*)this, sizeof(*this)); }
void init(THD *thd, TABLE_LIST *tl);
bool fill_item_list(List<Item> *item_list) const;
diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc
index b4cee3acb53..ac5b36bdcf0 100644
--- a/storage/innobase/btr/btr0cur.cc
+++ b/storage/innobase/btr/btr0cur.cc
@@ -7868,6 +7868,9 @@ btr_copy_externally_stored_field(
buf = (byte*) mem_heap_alloc(heap, local_len + extern_len);
+ if (UNIV_UNLIKELY(buf == NULL))
+ return NULL;
+
memcpy(buf, data, local_len);
*len = local_len
+ btr_copy_externally_stored_field_prefix_low(buf + local_len,
diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc
index 4f807fdd7ed..bbe245b0002 100644
--- a/storage/innobase/row/row0ins.cc
+++ b/storage/innobase/row/row0ins.cc
@@ -43,6 +43,7 @@ Created 4/20/1996 Heikki Tuuri
#include "buf0lru.h"
#include "fts0fts.h"
#include "fts0types.h"
+#include "ha_innodb.h"
#ifdef BTR_CUR_HASH_ADAPT
# include "btr0sea.h"
#endif
@@ -50,6 +51,8 @@ Created 4/20/1996 Heikki Tuuri
#include "wsrep_mysqld.h"
#endif /* WITH_WSREP */
+#include "scope.h"
+
/*************************************************************************
IMPORTANT NOTE: Any operation that generates redo MUST check that there
is enough space in the redo log before for that operation. This is
@@ -978,6 +981,107 @@ dberr_t wsrep_append_foreign_key(trx_t *trx,
Wsrep_service_key_type key_type);
#endif /* WITH_WSREP */
+
+int sql_log_cascade_update(TABLE *table);
+int sql_log_cascade_delete(TABLE *table);
+
+static bool row_ins_store_row_in_mysql(dtuple_t *row,
+ row_prebuilt_t *prebuilt,
+ uchar *mysql_rec)
+{
+ mysql_row_templ_t *templ= prebuilt->mysql_template;
+ for (uint i = 0; i < prebuilt->n_template; i++)
+ {
+ const dfield_t* df= dtuple_get_nth_field(row, i);
+ byte null_mask= static_cast<byte>(templ->mysql_null_bit_mask);
+
+ if (dfield_is_null(df))
+ {
+ mysql_rec[templ->mysql_null_byte_offset]|= null_mask;
+ }
+ else
+ {
+ if (templ->mysql_null_bit_mask)
+ mysql_rec[templ->mysql_null_byte_offset]&= (uchar)~null_mask;
+
+ byte *data= static_cast<byte*>(df->data);
+ ulint len= df->len;
+
+ if (dfield_is_ext(df))
+ {
+ data= btr_copy_externally_stored_field(&len, data,
+ prebuilt->table->space->zip_size(),
+ len, prebuilt->blob_heap);
+ if (UNIV_UNLIKELY(data == NULL))
+ return false;
+ }
+
+ row_sel_field_store_in_mysql_format(mysql_rec + templ->mysql_col_offset,
+ templ, prebuilt->index,
+ templ->clust_rec_field_no,
+ data, len);
+ }
+
+ templ++;
+ }
+ return true;
+}
+
+static dberr_t report_row_update(upd_node_t *cascade,
+ dict_index_t* clust_index)
+{
+ row_prebuilt_t *prebuilt= cascade->prebuilt;
+ prebuilt->index= clust_index;
+ TABLE *maria_table= prebuilt->m_mysql_table;
+
+ ulint n_ext= dtuple_get_n_ext(cascade->row)
+ + (cascade->is_delete ? 0 : dtuple_get_n_ext(cascade->upd_row));
+
+ auto _= make_scope_exit([cascade, prebuilt, n_ext](){
+
+ cascade->row= NULL;
+ cascade->ext= NULL;
+ cascade->upd_row= NULL;
+ cascade->upd_ext= NULL;
+ mem_heap_empty(cascade->heap);
+ if (n_ext && prebuilt->blob_heap)
+ {
+ mem_heap_free(prebuilt->blob_heap);
+ prebuilt->blob_heap= NULL;
+ }
+ });
+
+ if (n_ext)
+ {
+ if (UNIV_UNLIKELY(prebuilt->blob_heap != NULL))
+ mem_heap_empty(prebuilt->blob_heap);
+ prebuilt->blob_heap= mem_heap_create(srv_page_size);
+
+ if (UNIV_UNLIKELY(prebuilt->blob_heap == NULL))
+ return DB_OUT_OF_MEMORY;
+ }
+
+ if (UNIV_UNLIKELY(!row_ins_store_row_in_mysql(cascade->row, prebuilt,
+ maria_table->record[0])))
+ return DB_OUT_OF_MEMORY;
+
+ if (cascade->is_delete)
+ {
+ if (UNIV_UNLIKELY(sql_log_cascade_delete(maria_table)))
+ return DB_ERROR;
+ }
+ else
+ {
+ if (UNIV_UNLIKELY(!row_ins_store_row_in_mysql(cascade->upd_row, prebuilt,
+ maria_table->record[1])))
+ return DB_OUT_OF_MEMORY;
+ if (UNIV_UNLIKELY(sql_log_cascade_update(maria_table)))
+ return DB_ERROR;
+ }
+
+ return DB_SUCCESS;
+}
+
/*********************************************************************//**
Perform referential actions or checks when a parent row is deleted or updated
and the constraint had an ON DELETE or ON UPDATE condition which was not
@@ -1352,6 +1456,10 @@ row_ins_foreign_check_on_constraint(
err = row_update_cascade_for_mysql(thr, cascade,
foreign->foreign_table);
+ if (UNIV_UNLIKELY(prebuilt->m_mysql_table->is_online_alter())
+ && UNIV_LIKELY(!err))
+ err = report_row_update(cascade, clust_index);
+
mtr_start(mtr);
/* Restore pcur position */
diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc
index 3a4b9554581..8cff74ced84 100644
--- a/storage/innobase/row/row0upd.cc
+++ b/storage/innobase/row/row0upd.cc
@@ -2854,9 +2854,14 @@ row_upd(
ut_ad(err == DB_SUCCESS);
- /* Do some cleanup */
-
- if (node->row != NULL) {
+ /* Do some cleanup.
+ If it is a cascade update/delete during ONLINE ALTER TABLE,
+ this row data will be used later for reporting row changes to sql.
+ See report_row_update(). */
+
+ if (node->row != NULL
+ && !(node->foreign
+ && node->prebuilt->m_mysql_table->is_online_alter())) {
node->row = NULL;
node->ext = NULL;
node->upd_row = NULL;