diff --git a/mysql-test/main/rowid_filter_innodb.result b/mysql-test/main/rowid_filter_innodb.result index 60588e6891d..46d754cfaf8 100644 --- a/mysql-test/main/rowid_filter_innodb.result +++ b/mysql-test/main/rowid_filter_innodb.result @@ -1986,7 +1986,7 @@ ANALYZE "r_table_time_ms": "REPLACED", "r_other_time_ms": "REPLACED", "r_engine_stats": { - "pages_accessed": 3 + "pages_accessed": 2 }, "filtered": "REPLACED", "r_filtered": 66.66666667, @@ -2140,7 +2140,7 @@ ANALYZE "r_table_time_ms": "REPLACED", "r_other_time_ms": "REPLACED", "r_engine_stats": { - "pages_accessed": 3 + "pages_accessed": 2 }, "filtered": "REPLACED", "r_filtered": 66.66666667, diff --git a/mysql-test/suite/innodb/r/alter_copy_bulk,OFF.rdiff b/mysql-test/suite/innodb/r/alter_copy_bulk,OFF.rdiff new file mode 100644 index 00000000000..a644139ea78 --- /dev/null +++ b/mysql-test/suite/innodb/r/alter_copy_bulk,OFF.rdiff @@ -0,0 +1,11 @@ +--- bulk_copy_alter.result ++++ bulk_copy_alter,non_bulk_alter_copy.result +@@ -5,7 +5,7 @@ + INSERT INTO t1 SELECT repeat('b', 200), seq FROM seq_3_to_65536; + ALTER TABLE t1 ADD INDEX(f2); + ALTER TABLE t1 ADD PRIMARY KEY(f1(2)); +-ERROR 23000: Duplicate entry 'bb' for key 'PRIMARY' ++ERROR 23000: Duplicate entry 'aa' for key 'PRIMARY' + INSERT INTO t1 VALUES(repeat('a', 200), 1); + ALTER TABLE t1 ADD UNIQUE KEY(f2); + ERROR 23000: Duplicate entry '1' for key 'f2_2' diff --git a/mysql-test/suite/innodb/r/alter_copy_bulk.result b/mysql-test/suite/innodb/r/alter_copy_bulk.result new file mode 100644 index 00000000000..fa232c12dc3 --- /dev/null +++ b/mysql-test/suite/innodb/r/alter_copy_bulk.result @@ -0,0 +1,24 @@ +SET @@alter_algorithm=COPY; +CREATE TABLE t1(f1 CHAR(200), f2 INT NOT NULL)engine=InnoDB; +INSERT INTO t1 SELECT repeat('a', 200), seq FROM seq_1_to_2; +ALTER TABLE t1 FORCE; +INSERT INTO t1 SELECT repeat('b', 200), seq FROM seq_3_to_65536; +ALTER TABLE t1 ADD INDEX(f2); +ALTER TABLE t1 ADD PRIMARY KEY(f1(2)); +ERROR 23000: Duplicate entry 'bb' for key 'PRIMARY' +INSERT INTO t1 VALUES(repeat('a', 200), 1); +ALTER TABLE t1 ADD UNIQUE KEY(f2); +ERROR 23000: Duplicate entry '1' for key 'f2_2' +ALTER IGNORE TABLE t1 MODIFY f1 CHAR(200) NOT NULL; +CREATE TABLE t2(f1 INT NOT NULL, +FOREIGN KEY(f1) REFERENCES t1(f2))ENGINE=InnoDB; +INSERT INTO t2 VALUES(1); +ALTER TABLE t2 FORCE; +DROP TABLE t2, t1; +CREATE TABLE t1 (f1 INT, f2 INT) ENGINE=InnoDB PARTITION BY HASH(f1) PARTITIONS 2; +INSERT INTO t1 VALUES(1, 1); +INSERT INTO t1 SELECT seq, seq * 2 FROM seq_1_to_2; +ALTER TABLE t1 FORCE; +INSERT INTO t1 SELECT seq, seq * 2 FROM seq_3_to_65536; +ALTER TABLE t1 ADD INDEX(f2); +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/foreign_key.result b/mysql-test/suite/innodb/r/foreign_key.result index 80a0afb8d06..9f14194b6dd 100644 --- a/mysql-test/suite/innodb/r/foreign_key.result +++ b/mysql-test/suite/innodb/r/foreign_key.result @@ -1119,4 +1119,26 @@ test.collections check status OK disconnect con1; DROP TABLE binaries, collections; # End of 10.6 tests +CREATE TABLE t1 +( +f1 VARCHAR(32)BINARY NOT NULL, +f2 VARCHAR(32)BINARY NOT NULL, +PRIMARY KEY (f1), +INDEX(f2) +) ENGINE=InnoDB; +INSERT INTO t1 VALUES('MySQL', 'InnoDB'), ('MariaDB', 'NDB'); +CREATE TABLE t2 +( +f1 VARCHAR(32)BINARY NOT NULL, +f2 VARCHAR(255)BINARY NOT NULL, +f3 int, PRIMARY KEY (f1), INDEX(f1), INDEX(f2) +) ENGINE=InnoDB; +INSERT INTO t2 VALUES('MySQL', 'MySQL', 1), +('MariaDB', 'MariaDB', 1); +ALTER TABLE t1 ADD FOREIGN KEY (f1) REFERENCES t2 (f2); +ALTER TABLE t2 ADD FOREIGN KEY (f2) REFERENCES t2 (f2), +ADD UNIQUE INDEX(f3); +ERROR HY000: Cannot delete rows from table which is parent in a foreign key constraint 't1_ibfk_1' of table 't1' +drop table t1, t2; SET GLOBAL innodb_stats_persistent = @save_stats_persistent; +# End of 10.11 tests diff --git a/mysql-test/suite/innodb/t/alter_copy_bulk.combinations b/mysql-test/suite/innodb/t/alter_copy_bulk.combinations new file mode 100644 index 00000000000..602a6b5ac57 --- /dev/null +++ b/mysql-test/suite/innodb/t/alter_copy_bulk.combinations @@ -0,0 +1,5 @@ +[ON] +--innodb_alter_copy_bulk=ON + +[OFF] +--innodb_alter_copy_bulk=OFF diff --git a/mysql-test/suite/innodb/t/alter_copy_bulk.test b/mysql-test/suite/innodb/t/alter_copy_bulk.test new file mode 100644 index 00000000000..797724dcf2e --- /dev/null +++ b/mysql-test/suite/innodb/t/alter_copy_bulk.test @@ -0,0 +1,44 @@ +--source include/have_innodb.inc +--source include/have_partition.inc +--source include/have_sequence.inc +SET @@alter_algorithm=COPY; + +CREATE TABLE t1(f1 CHAR(200), f2 INT NOT NULL)engine=InnoDB; +INSERT INTO t1 SELECT repeat('a', 200), seq FROM seq_1_to_2; +# Buffer fits in the memory +ALTER TABLE t1 FORCE; + +# Insert more entries +INSERT INTO t1 SELECT repeat('b', 200), seq FROM seq_3_to_65536; +# Alter should use temporary file for sorting +ALTER TABLE t1 ADD INDEX(f2); + +# Error while buffering the insert operation +--error ER_DUP_ENTRY +ALTER TABLE t1 ADD PRIMARY KEY(f1(2)); + +INSERT INTO t1 VALUES(repeat('a', 200), 1); +# Error while applying the bulk insert operation +--error ER_DUP_ENTRY +ALTER TABLE t1 ADD UNIQUE KEY(f2); + +# Ignore shouldn't go through bulk operation +ALTER IGNORE TABLE t1 MODIFY f1 CHAR(200) NOT NULL; + +CREATE TABLE t2(f1 INT NOT NULL, + FOREIGN KEY(f1) REFERENCES t1(f2))ENGINE=InnoDB; +INSERT INTO t2 VALUES(1); +# Bulk operation shouldn't happen because of foreign key constraints +ALTER TABLE t2 FORCE; +DROP TABLE t2, t1; + +CREATE TABLE t1 (f1 INT, f2 INT) ENGINE=InnoDB PARTITION BY HASH(f1) PARTITIONS 2; +INSERT INTO t1 VALUES(1, 1); +INSERT INTO t1 SELECT seq, seq * 2 FROM seq_1_to_2; +# Buffer fits in the memory +ALTER TABLE t1 FORCE; +# Insert more entries +INSERT INTO t1 SELECT seq, seq * 2 FROM seq_3_to_65536; +# Alter should use temporary file for sorting +ALTER TABLE t1 ADD INDEX(f2); +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/foreign_key.test b/mysql-test/suite/innodb/t/foreign_key.test index e793e261abd..8c6ce939bab 100644 --- a/mysql-test/suite/innodb/t/foreign_key.test +++ b/mysql-test/suite/innodb/t/foreign_key.test @@ -1186,6 +1186,30 @@ DROP TABLE binaries, collections; --echo # End of 10.6 tests +CREATE TABLE t1 +( +f1 VARCHAR(32)BINARY NOT NULL, +f2 VARCHAR(32)BINARY NOT NULL, +PRIMARY KEY (f1), +INDEX(f2) +) ENGINE=InnoDB; +INSERT INTO t1 VALUES('MySQL', 'InnoDB'), ('MariaDB', 'NDB'); + +CREATE TABLE t2 +( +f1 VARCHAR(32)BINARY NOT NULL, +f2 VARCHAR(255)BINARY NOT NULL, +f3 int, PRIMARY KEY (f1), INDEX(f1), INDEX(f2) +) ENGINE=InnoDB; +INSERT INTO t2 VALUES('MySQL', 'MySQL', 1), + ('MariaDB', 'MariaDB', 1); +ALTER TABLE t1 ADD FOREIGN KEY (f1) REFERENCES t2 (f2); +# MDEV-33927 TODO: change the warning message +--error ER_FK_CANNOT_DELETE_PARENT +ALTER TABLE t2 ADD FOREIGN KEY (f2) REFERENCES t2 (f2), +ADD UNIQUE INDEX(f3); +drop table t1, t2; SET GLOBAL innodb_stats_persistent = @save_stats_persistent; +--echo # End of 10.11 tests --source include/wait_until_count_sessions.inc diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb.result b/mysql-test/suite/sys_vars/r/sysvars_innodb.result index f72e0d6f05d..f86a67bbb7b 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_innodb.result +++ b/mysql-test/suite/sys_vars/r/sysvars_innodb.result @@ -55,6 +55,18 @@ NUMERIC_BLOCK_SIZE 0 ENUM_VALUE_LIST NULL READ_ONLY YES COMMAND_LINE_ARGUMENT OPTIONAL +VARIABLE_NAME INNODB_ALTER_COPY_BULK +SESSION_VALUE NULL +DEFAULT_VALUE ON +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE BOOLEAN +VARIABLE_COMMENT Allow bulk insert operation for copy alter operation +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST OFF,ON +READ_ONLY NO +COMMAND_LINE_ARGUMENT NONE VARIABLE_NAME INNODB_AUTOEXTEND_INCREMENT SESSION_VALUE NULL DEFAULT_VALUE 64 diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 4353d45e0fc..5b2fe9e24ea 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -4356,7 +4356,16 @@ bool select_insert::prepare_eof() if (info.ignore || info.handle_duplicates != DUP_ERROR) if (table->file->ha_table_flags() & HA_DUPLICATE_POS) table->file->ha_rnd_end(); - table->file->extra(HA_EXTRA_END_ALTER_COPY); + if (error <= 0) + { + error= table->file->extra(HA_EXTRA_END_ALTER_COPY); + if (error == HA_ERR_FOUND_DUPP_KEY) + { + uint key_nr= table->file->get_dup_key(error); + if ((int)key_nr >= 0 && key_nr < table->s->keys) + print_keydup_error(table, &table->key_info[key_nr], MYF(0)); + } + } table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index c149112e23a..3b69a0190b6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -11766,6 +11766,59 @@ bool mysql_trans_commit_alter_copy_data(THD *thd) DBUG_RETURN(error); } +/** Handle the error when copying data from source to target table. +@param error error code +@param ignore alter ignore statement +@param to target table handler +@param thd Mysql Thread +@param alter_ctx Runtime context for alter statement +@retval false in case of error +@retval true in case of skipping the row and continue alter operation */ +static bool +copy_data_error_ignore(int &error, bool ignore, TABLE *to, + THD *thd, Alter_table_ctx *alter_ctx) +{ + if (to->file->is_fatal_error(error, HA_CHECK_DUP)) + { + /* Not a duplicate key error. */ + to->file->print_error(error, MYF(0)); + error= 1; + return false; + } + /* Duplicate key error. */ + if (unlikely(alter_ctx->fk_error_if_delete_row)) + { + /* We are trying to omit a row from the table which serves + as parent in a foreign key. This might have broken + referential integrity so emit an error. Note that we + can't ignore this error even if we are + executing ALTER IGNORE TABLE. IGNORE allows to skip rows, but + doesn't allow to break unique or foreign key constraints, */ + my_error(ER_FK_CANNOT_DELETE_PARENT, MYF(0), + alter_ctx->fk_error_id, + alter_ctx->fk_error_table); + return false; + } + if (ignore) + return true; + /* Ordinary ALTER TABLE. Report duplicate key error. */ + uint key_nr= to->file->get_dup_key(error); + if (key_nr <= MAX_KEY) + { + const char *err_msg= ER_THD(thd, ER_DUP_ENTRY_WITH_KEY_NAME); + if (key_nr == 0 && to->s->keys > 0 && + (to->key_info[0].key_part[0].field->flags & + AUTO_INCREMENT_FLAG)) + err_msg= ER_THD(thd, ER_DUP_ENTRY_AUTOINCREMENT_CASE); + print_keydup_error(to, + key_nr >= to->s->keys ? NULL : + &to->key_info[key_nr], + err_msg, MYF(0)); + } + else + to->file->print_error(error, MYF(0)); + return false; +} static int copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, bool ignore, @@ -12027,58 +12080,11 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, bool ignore, to->auto_increment_field_not_null= FALSE; if (unlikely(error)) { - if (to->file->is_fatal_error(error, HA_CHECK_DUP)) - { - /* Not a duplicate key error. */ - to->file->print_error(error, MYF(0)); - error= 1; - break; - } - else - { - /* Duplicate key error. */ - if (unlikely(alter_ctx->fk_error_if_delete_row)) - { - /* - We are trying to omit a row from the table which serves as parent - in a foreign key. This might have broken referential integrity so - emit an error. Note that we can't ignore this error even if we are - executing ALTER IGNORE TABLE. IGNORE allows to skip rows, but - doesn't allow to break unique or foreign key constraints, - */ - my_error(ER_FK_CANNOT_DELETE_PARENT, MYF(0), - alter_ctx->fk_error_id, - alter_ctx->fk_error_table); - break; - } - - if (ignore) - { - /* This ALTER IGNORE TABLE. Simply skip row and continue. */ - to->file->restore_auto_increment(prev_insert_id); - delete_count++; - } - else - { - /* Ordinary ALTER TABLE. Report duplicate key error. */ - uint key_nr= to->file->get_dup_key(error); - if ((int) key_nr >= 0) - { - const char *err_msg= ER_THD(thd, ER_DUP_ENTRY_WITH_KEY_NAME); - if (key_nr == 0 && to->s->keys > 0 && - (to->key_info[0].key_part[0].field->flags & - AUTO_INCREMENT_FLAG)) - err_msg= ER_THD(thd, ER_DUP_ENTRY_AUTOINCREMENT_CASE); - print_keydup_error(to, - key_nr >= to->s->keys ? NULL : - &to->key_info[key_nr], - err_msg, MYF(0)); - } - else - to->file->print_error(error, MYF(0)); - break; - } - } + if (!copy_data_error_ignore(error, ignore, to, thd, alter_ctx)) + break; + DBUG_ASSERT(ignore); + to->file->restore_auto_increment(prev_insert_id); + delete_count++; } else { @@ -12105,9 +12111,15 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, bool ignore, error= 1; } bulk_insert_started= 0; - if (!ignore) - to->file->extra(HA_EXTRA_END_ALTER_COPY); - + if (!ignore && error <= 0) + { + int alt_error= to->file->extra(HA_EXTRA_END_ALTER_COPY); + if (alt_error > 0) + { + error= alt_error; + copy_data_error_ignore(error, false, to, thd, alter_ctx); + } + } cleanup_done= 1; to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 26e48eb3e5b..c694619d852 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -15779,6 +15779,26 @@ ha_innobase::extra( break; case HA_EXTRA_END_ALTER_COPY: trx = check_trx_exists(ha_thd()); + if (m_prebuilt->table->skip_alter_undo) { + if (dberr_t err= trx->bulk_insert_apply()) { + m_prebuilt->table->skip_alter_undo = 0; + return convert_error_code_to_mysql( + err, + m_prebuilt->table->flags, + trx->mysql_thd); + } + + trx->end_bulk_insert(*m_prebuilt->table); + trx->bulk_insert = false; + /* During copy alter operation, InnoDB + updates the stats only for non-persistent + tables. */ + if (!dict_stats_is_persistent_enabled( + m_prebuilt->table)) { + dict_stats_update_if_needed( + m_prebuilt->table, *trx); + } + } m_prebuilt->table->skip_alter_undo = 0; if (!m_prebuilt->table->is_temporary() && !high_level_read_only) { @@ -19731,6 +19751,10 @@ static MYSQL_SYSVAR_BOOL(force_primary_key, "Do not allow creating a table without primary key (off by default)", NULL, NULL, FALSE); +static MYSQL_SYSVAR_BOOL(alter_copy_bulk, innodb_alter_copy_bulk, + PLUGIN_VAR_NOCMDARG, + "Allow bulk insert operation for copy alter operation", NULL, NULL, TRUE); + const char *page_compression_algorithms[]= { "none", "zlib", "lz4", "lzo", "lzma", "bzip2", "snappy", 0 }; static TYPELIB page_compression_algorithms_typelib= { @@ -19966,6 +19990,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(saved_page_number_debug), #endif /* UNIV_DEBUG */ MYSQL_SYSVAR(force_primary_key), + MYSQL_SYSVAR(alter_copy_bulk), MYSQL_SYSVAR(fatal_semaphore_wait_threshold), /* Table page compression feature */ MYSQL_SYSVAR(compression_default), diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 71d3177323f..3aa4b51ab5f 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -845,14 +845,13 @@ inline void dict_table_t::rollback_instant( } } -/* Report an InnoDB error to the client by invoking my_error(). */ -static ATTRIBUTE_COLD __attribute__((nonnull)) +/* Report an InnoDB error to the client by invoking my_error(). +@param error InnoDB error code +@param table table name +@param flags table flags */ +ATTRIBUTE_COLD __attribute__((nonnull)) void -my_error_innodb( -/*============*/ - dberr_t error, /*!< in: InnoDB error code */ - const char* table, /*!< in: table name */ - ulint flags) /*!< in: table flags */ +my_error_innodb(dberr_t error, const char *table, ulint flags) { switch (error) { case DB_MISSING_HISTORY: diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index e9ae48ef8da..3b8ecdd2c87 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -2435,7 +2435,7 @@ public: } /** @return whether a DDL operation is in progress on this table */ - bool is_active_ddl() const + bool is_native_online_ddl() const { return UT_LIST_GET_FIRST(indexes)->online_log; } diff --git a/storage/innobase/include/row0merge.h b/storage/innobase/include/row0merge.h index 1c2af128229..fc39a9d24d9 100644 --- a/storage/innobase/include/row0merge.h +++ b/storage/innobase/include/row0merge.h @@ -416,6 +416,14 @@ row_merge_read_rec( ulint space) /*!< in: space id */ MY_ATTRIBUTE((warn_unused_result)); +/* Report an InnoDB error to the client by invoking my_error(). +@param error InnoDB error code +@param table table name +@param flags table flags */ +ATTRIBUTE_COLD __attribute__((nonnull)) +void +my_error_innodb(dberr_t error, const char *table, ulint flags); + /** Buffer for bulk insert */ class row_merge_bulk_t { diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index 6e75359c9f1..0fe42e277a3 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -310,6 +310,7 @@ extern ulong srv_checksum_algorithm; extern my_bool srv_force_primary_key; +extern my_bool innodb_alter_copy_bulk; extern ulong srv_max_purge_lag; extern ulong srv_max_purge_lag_delay; diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index 152553544df..3d2017e4131 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -1172,8 +1172,8 @@ public: { if (UNIV_LIKELY(!bulk_insert)) return nullptr; - ut_ad(!check_unique_secondary); - ut_ad(!check_foreigns); + ut_ad(table->skip_alter_undo || !check_unique_secondary); + ut_ad(table->skip_alter_undo || !check_foreigns); auto it= mod_tables.find(table); if (it == mod_tables.end() || !it->second.bulk_buffer_exist()) return nullptr; diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index f39dad8aee7..8704dbfe65c 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -2701,7 +2701,7 @@ err_exit: block = btr_pcur_get_block(&pcur); - DBUG_EXECUTE_IF("row_ins_row_level", goto skip_bulk_insert;); + DBUG_EXECUTE_IF("row_ins_row_level", goto row_level_insert;); if (!(flags & BTR_NO_UNDO_LOG_FLAG) && page_is_empty(block->page.frame) @@ -2709,37 +2709,33 @@ err_exit: && !trx->check_unique_secondary && !trx->check_foreigns && !trx->dict_operation && block->page.id().page_no() == index->page - && !index->table->skip_alter_undo - && !index->table->n_rec_locks - && !index->table->is_active_ddl() - && !index->table->has_spatial_index() - && !index->table->versioned() - && (!dict_table_is_partition(index->table) + && !index->table->is_native_online_ddl() + && (!dict_table_is_partition(index->table) || thd_sql_command(trx->mysql_thd) == SQLCOM_INSERT)) { - DEBUG_SYNC_C("empty_root_page_insert"); - trx->bulk_insert = true; + if (!index->table->n_rec_locks + && !index->table->versioned() + && !index->table->is_temporary() + && !index->table->has_spatial_index()) { - if (!index->table->is_temporary()) { + ut_ad(!index->table->skip_alter_undo); + trx->bulk_insert = true; err = lock_table(index->table, NULL, LOCK_X, thr); - if (err != DB_SUCCESS) { trx->error_state = err; trx->bulk_insert = false; goto err_exit; } - if (index->table->n_rec_locks) { avoid_bulk: trx->bulk_insert = false; - goto skip_bulk_insert; + goto row_level_insert; } - #ifdef WITH_WSREP if (trx->is_wsrep()) { if (!wsrep_thd_is_local_transaction(trx->mysql_thd)) - goto skip_bulk_insert; + goto row_level_insert; if (wsrep_append_table_key(trx->mysql_thd, *index->table)) { trx->error_state = DB_ROLLBACK; @@ -2774,9 +2770,32 @@ avoid_bulk: export_vars.innodb_bulk_operations++; goto err_exit; } + } else if (flags == (BTR_NO_UNDO_LOG_FLAG | BTR_NO_LOCKING_FLAG) + && !index->table->n_rec_locks) { + + ut_ad(index->table->skip_alter_undo); + ut_ad(!entry->is_metadata()); + if (innodb_alter_copy_bulk + && !index->table->is_temporary() + && !index->table->versioned() + && !index->table->has_spatial_index()) { + ut_ad(page_is_empty(block->page.frame)); + /* This code path has been executed at the + start of the alter operation. Consecutive + insert operation are buffered in the + bulk buffer and doesn't check for constraint + validity of foreign key relationship. */ + trx_start_if_not_started(trx, true); + trx->bulk_insert = true; + auto m = trx->mod_tables.emplace(index->table, 0); + m.first->second.start_bulk_insert(index->table); + err = m.first->second.bulk_insert_buffered( + *entry, *index, trx); + goto err_exit; + } } -skip_bulk_insert: +row_level_insert: if (UNIV_UNLIKELY(entry->info_bits != 0)) { const rec_t* rec = btr_pcur_get_rec(&pcur); @@ -3386,9 +3405,12 @@ row_ins_index_entry( if (index->is_btree()) { if (auto t= trx->check_bulk_buffer(index->table)) { - /* MDEV-25036 FIXME: check also foreign key - constraints */ - ut_ad(!trx->check_foreigns); + /* MDEV-25036 FIXME: + row_ins_check_foreign_constraint() check + should be done before buffering the insert + operation. */ + ut_ad(index->table->skip_alter_undo + || !trx->check_foreigns); return t->bulk_insert_buffered(*entry, *index, trx); } } diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index 6fb530f0309..4ce2e79887a 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -5320,6 +5320,9 @@ func_exit: else if (index->is_primary() && table->persistent_autoinc) btr_write_autoinc(index, table->autoinc - 1); err= btr_bulk.finish(err); + if (err == DB_SUCCESS && index->is_clust()) + table->stat_n_rows= (file && file->fd != OS_FILE_CLOSED) + ? file->n_rec : buf.n_tuples; return err; } @@ -5333,8 +5336,17 @@ dberr_t row_merge_bulk_t::write_to_table(dict_table_t *table, trx_t *trx) continue; dberr_t err= write_to_index(i, trx); - if (err != DB_SUCCESS) + switch (err) { + default: + if (table->skip_alter_undo) + my_error_innodb(err, table->name.m_name, table->flags); return err; + case DB_SUCCESS: + break; + case DB_DUPLICATE_KEY: + trx->error_info= index; + return err; + } i++; } diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index 7c0c4b92c47..2bbc3d1daf8 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -353,6 +353,10 @@ my_bool srv_print_innodb_lock_monitor; PRIMARY KEY */ my_bool srv_force_primary_key; +/** innodb_alter_copy_bulk; Whether to allow bulk insert operation +inside InnoDB alter for copy algorithm; */ +my_bool innodb_alter_copy_bulk; + /** Key version to encrypt the temporary tablespace */ my_bool innodb_encrypt_temporary_tables; diff --git a/storage/innobase/trx/trx0rec.cc b/storage/innobase/trx/trx0rec.cc index 2923dc6477d..cf6c050e623 100644 --- a/storage/innobase/trx/trx0rec.cc +++ b/storage/innobase/trx/trx0rec.cc @@ -1840,7 +1840,7 @@ trx_undo_report_row_operation( auto m = trx->mod_tables.emplace(index->table, trx->undo_no); ut_ad(m.first->second.valid(trx->undo_no)); - if (m.second && index->table->is_active_ddl()) { + if (m.second && index->table->is_native_online_ddl()) { trx->apply_online_log= true; } diff --git a/storage/innobase/trx/trx0roll.cc b/storage/innobase/trx/trx0roll.cc index 126d8aadacb..fd88caa84d0 100644 --- a/storage/innobase/trx/trx0roll.cc +++ b/storage/innobase/trx/trx0roll.cc @@ -151,7 +151,7 @@ inline void trx_t::rollback_low(trx_savept_t *savept) mod_tables.erase(j); } else if (!apply_online_log) - apply_online_log= j->first->is_active_ddl(); + apply_online_log= j->first->is_native_online_ddl(); } MONITOR_INC(MONITOR_TRX_ROLLBACK_SAVEPOINT); } diff --git a/storage/innobase/trx/trx0undo.cc b/storage/innobase/trx/trx0undo.cc index c0f5b1fb22c..af876f1e45b 100644 --- a/storage/innobase/trx/trx0undo.cc +++ b/storage/innobase/trx/trx0undo.cc @@ -309,7 +309,7 @@ inline void UndorecApplier::apply_undo_rec(const trx_undo_rec_t *rec) dict_sys.unfreeze(); ut_ad(table); - if (!table->is_active_ddl()) + if (!table->is_native_online_ddl()) return; dict_index_t *index= dict_table_get_first_index(table);