diff --git a/mysql-test/suite/innodb/include/import_tablespace_ifnotexists.inc b/mysql-test/suite/innodb/include/import_tablespace_ifnotexists.inc new file mode 100644 index 00000000000..9f6f852332f --- /dev/null +++ b/mysql-test/suite/innodb/include/import_tablespace_ifnotexists.inc @@ -0,0 +1,44 @@ +# Export Table and Import from saved files .cfg and .ibd +# Caller should create t1 table definition and populate table + +let $MYSQLD_DATADIR = `SELECT @@datadir`; + +if(!$source_db) { + let $source_db = test; +} + +if(!$dest_db) { + let $dest_db = test; +} + +eval FLUSH TABLES $source_db.t1 FOR EXPORT; + +--copy_file $MYSQLD_DATADIR/$source_db/t1.cfg $MYSQLD_DATADIR/t1.cfg_back +--copy_file $MYSQLD_DATADIR/$source_db/t1.ibd $MYSQLD_DATADIR/t1.ibd_back + +UNLOCK TABLES; + +if($source_db != $dest_db) { + eval USE $dest_db; + let $create1 = query_get_value(SHOW CREATE TABLE $source_db.t1, Create Table, 1); + eval $create1; +} + +eval ALTER TABLE $dest_db.t1 DISCARD TABLESPACE; + +--move_file $MYSQLD_DATADIR/t1.cfg_back $MYSQLD_DATADIR/$dest_db/t1.cfg +--move_file $MYSQLD_DATADIR/t1.ibd_back $MYSQLD_DATADIR/$dest_db/t1.ibd + +eval ALTER TABLE $dest_db.t1 IMPORT TABLESPACE; +--error ER_TABLESPACE_EXISTS +eval ALTER TABLE $dest_db.t1 IMPORT TABLESPACE; +eval ALTER TABLE $dest_db.t1 IMPORT TABLESPACE IF NOT EXISTS; + +eval CHECK TABLE $dest_db.t1; +eval SHOW CREATE TABLE $dest_db.t1; +eval SELECT * FROM $dest_db.t1; + + +if($source_db != $dest_db) { + eval DROP TABLE $dest_db.t1; +} \ No newline at end of file diff --git a/mysql-test/suite/innodb/r/import-tablespace-ifnotexists.result b/mysql-test/suite/innodb/r/import-tablespace-ifnotexists.result new file mode 100644 index 00000000000..62430f06761 --- /dev/null +++ b/mysql-test/suite/innodb/r/import-tablespace-ifnotexists.result @@ -0,0 +1,32 @@ +# +# ALTER TABLE ... IMPORT TABLESPACE IF NOT EXISTS +# +CREATE TABLE t1 (c1 VARCHAR(32), c2 VARCHAR(32), c3 VARCHAR(32), +PRIMARY KEY (c1, c2, c3)) +ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +INSERT INTO t1 VALUES ('Test Data -1', 'Test Data -2', 'Test Data -3'); +FLUSH TABLES test.t1 FOR EXPORT; +UNLOCK TABLES; +ALTER TABLE test.t1 DISCARD TABLESPACE; +ALTER TABLE test.t1 IMPORT TABLESPACE; +ALTER TABLE test.t1 IMPORT TABLESPACE; +ERROR HY000: Tablespace 'test/t1' exists. +ALTER TABLE test.t1 IMPORT TABLESPACE IF NOT EXISTS; +Warnings: +Warning 1813 InnoDB: Tablespace 'test/t1' exists. +CHECK TABLE test.t1; +Table Op Msg_type Msg_text +test.t1 check status OK +SHOW CREATE TABLE test.t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` varchar(32) NOT NULL, + `c2` varchar(32) NOT NULL, + `c3` varchar(32) NOT NULL, + PRIMARY KEY (`c1`,`c2`,`c3`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +SELECT * FROM test.t1; +c1 c2 c3 +Test Data -1 Test Data -2 Test Data -3 +DROP TABLE t1; +# Test End diff --git a/mysql-test/suite/innodb/t/import-tablespace-ifnotexists.test b/mysql-test/suite/innodb/t/import-tablespace-ifnotexists.test new file mode 100644 index 00000000000..f5e00c1f395 --- /dev/null +++ b/mysql-test/suite/innodb/t/import-tablespace-ifnotexists.test @@ -0,0 +1,16 @@ +# Test "ALTER TABLE ... IMPORT TABLESPACE IF NOT EXISTS" in InnoDB +#--source include/have_innodb.inc + +--echo # +--echo # ALTER TABLE ... IMPORT TABLESPACE IF NOT EXISTS +--echo # + +CREATE TABLE t1 (c1 VARCHAR(32), c2 VARCHAR(32), c3 VARCHAR(32), +PRIMARY KEY (c1, c2, c3)) +ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +INSERT INTO t1 VALUES ('Test Data -1', 'Test Data -2', 'Test Data -3'); + +--source suite/innodb/include/import_tablespace_ifnotexists.inc + +DROP TABLE t1; \ No newline at end of file diff --git a/sql/handler.cc b/sql/handler.cc index 8f149393b17..20f993a7375 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -4911,11 +4911,12 @@ int handler::ha_enable_indexes(uint mode) { */ int handler::ha_discard_or_import_tablespace(bool discard, + uint option, dd::Table *table_def) { assert(table_share->tmp_table != NO_TMP_TABLE || m_lock_type == F_WRLCK); mark_trx_read_write(); - return discard_or_import_tablespace(discard, table_def); + return discard_or_import_tablespace(discard, option, table_def); } bool handler::ha_prepare_inplace_alter_table(TABLE *altered_table, diff --git a/sql/handler.h b/sql/handler.h index 8b7747becef..9962c201c58 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -626,6 +626,9 @@ enum enum_alter_inplace_result { #define HA_LEX_CREATE_INTERNAL_TMP_TABLE 8 #define HA_MAX_REC_LENGTH 65535U +/* Alter table import tablespace if not exists */ +#define HA_LEX_IMPORT_TABLESPACE_IF_NOT_EXISTS 1 + /** Options for the START TRANSACTION statement. @@ -2910,8 +2913,8 @@ constexpr const decltype(handlerton::flags) HTON_SUPPORTS_ENGINE_ATTRIBUTE{ 1 << 17}; /** Engine supports Generated invisible primary key. */ -constexpr const decltype( - handlerton::flags) HTON_SUPPORTS_GENERATED_INVISIBLE_PK{1 << 18}; +constexpr const decltype(handlerton::flags) + HTON_SUPPORTS_GENERATED_INVISIBLE_PK{1 << 18}; /** Whether the secondary engine supports DDLs. No meaning if the engine is not * secondary. */ @@ -4006,9 +4009,7 @@ class Ft_hints { @return pointer to ft_hints struct */ - struct ft_hints *get_hints() { - return &hints; - } + struct ft_hints *get_hints() { return &hints; } }; /** @@ -4770,7 +4771,8 @@ class handler { bool ha_check_and_repair(THD *thd); int ha_disable_indexes(uint mode); int ha_enable_indexes(uint mode); - int ha_discard_or_import_tablespace(bool discard, dd::Table *table_def); + int ha_discard_or_import_tablespace(bool discard, uint import_tablespace_option, + dd::Table *table_def); int ha_rename_table(const char *from, const char *to, const dd::Table *from_table_def, dd::Table *to_table_def); int ha_delete_table(const char *name, const dd::Table *table_def); @@ -6769,6 +6771,7 @@ class handler { Discard or import tablespace. @param [in] discard Indicates whether this is discard operation. + @param[in] option if value is HA_LEX_IMPORT_TABLESPACE_IF_NOT_EXISTS will ignore import tablespace if tablespace exists @param [in,out] table_def dd::Table object describing the table in which tablespace needs to be discarded or imported. This object can be adjusted by @@ -6781,6 +6784,7 @@ class handler { */ virtual int discard_or_import_tablespace(bool discard [[maybe_unused]], + uint option [[maybe_unused]], dd::Table *table_def [[maybe_unused]]) { set_my_errno(HA_ERR_WRONG_COMMAND); diff --git a/sql/parse_tree_nodes.h b/sql/parse_tree_nodes.h index 9127438cf50..db09485881f 100644 --- a/sql/parse_tree_nodes.h +++ b/sql/parse_tree_nodes.h @@ -4564,12 +4564,24 @@ class PT_alter_table_import_partition_tablespace final public: explicit PT_alter_table_import_partition_tablespace( - const List *opt_partition_list) - : super(Alter_info::ALTER_IMPORT_TABLESPACE, opt_partition_list) {} + const List *opt_partition_list, + uint alter_table_import_tablespace_option) + : super(Alter_info::ALTER_IMPORT_TABLESPACE, opt_partition_list), + m_alter_table_import_tablespace_option( + alter_table_import_tablespace_option) {} + + bool contextualize(Table_ddl_parse_context *pc) override { + pc->alter_info->import_tablespace_option = + m_alter_table_import_tablespace_option; + return super::contextualize(pc); + } Sql_cmd *make_cmd(Table_ddl_parse_context *pc) override { return new (pc->mem_root) Sql_cmd_discard_import_tablespace(pc->alter_info); } + + private: + const uint m_alter_table_import_tablespace_option; }; class PT_alter_table_discard_tablespace final @@ -4590,12 +4602,23 @@ class PT_alter_table_import_tablespace final typedef PT_alter_table_standalone_action super; public: - PT_alter_table_import_tablespace() - : super(Alter_info::ALTER_IMPORT_TABLESPACE) {} + PT_alter_table_import_tablespace(uint alter_table_import_tablespace_option) + : super(Alter_info::ALTER_IMPORT_TABLESPACE), + m_alter_table_import_tablespace_option( + alter_table_import_tablespace_option) {} + + bool contextualize(Table_ddl_parse_context *pc) override { + pc->alter_info->import_tablespace_option = + m_alter_table_import_tablespace_option; + return super::contextualize(pc); + } Sql_cmd *make_cmd(Table_ddl_parse_context *pc) override { return new (pc->mem_root) Sql_cmd_discard_import_tablespace(pc->alter_info); } + + private: + const uint m_alter_table_import_tablespace_option; }; class PT_alter_table_stmt final : public PT_table_ddl_stmt_base { @@ -4963,9 +4986,9 @@ class PT_alter_tablespace_option final const Option_type m_value; }; -typedef PT_alter_tablespace_option +typedef PT_alter_tablespace_option< + decltype(Tablespace_options::autoextend_size), + &Tablespace_options::autoextend_size> PT_alter_tablespace_option_autoextend_size; typedef PT_alter_tablespace_option PT_alter_tablespace_option_max_size; -typedef PT_alter_tablespace_option +typedef PT_alter_tablespace_option< + decltype(Tablespace_options::redo_buffer_size), + &Tablespace_options::redo_buffer_size> PT_alter_tablespace_option_redo_buffer_size; -typedef PT_alter_tablespace_option +typedef PT_alter_tablespace_option< + decltype(Tablespace_options::undo_buffer_size), + &Tablespace_options::undo_buffer_size> PT_alter_tablespace_option_undo_buffer_size; typedef PT_alter_tablespace_option< diff --git a/sql/sql_alter.h b/sql/sql_alter.h index 0d97284a39f..dc11d4f142d 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -441,6 +441,9 @@ class Alter_info { */ enum_with_validation with_validation; + // Option for alter table import tablespace + uint import_tablespace_option; + /// "new_db" (if any) or "db" (if any) or default database from /// ALTER TABLE [db.]table [ RENAME [TO|AS|=] [new_db.]new_table ] LEX_CSTRING new_db_name; @@ -466,6 +469,7 @@ class Alter_info { requested_algorithm(ALTER_TABLE_ALGORITHM_DEFAULT), requested_lock(ALTER_TABLE_LOCK_DEFAULT), with_validation(ALTER_VALIDATION_DEFAULT), + import_tablespace_option(0), new_db_name(LEX_CSTRING{nullptr, 0}), new_table_name(LEX_CSTRING{nullptr, 0}) {} diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 12dd00e5766..7a5770ff34c 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -11193,9 +11193,9 @@ bool mysql_create_like_table(THD *thd, Table_ref *table, Table_ref *src_table, goto err; } } else // Case 1 - if (write_bin_log(thd, true, thd->query().str, thd->query().length, - is_trans)) - goto err; + if (write_bin_log(thd, true, thd->query().str, thd->query().length, + is_trans)) + goto err; } /* Case 3 and 4 does nothing under RBR @@ -11382,6 +11382,7 @@ bool Sql_cmd_discard_import_tablespace::mysql_discard_or_import_tablespace( bool discard = (m_alter_info->flags & Alter_info::ALTER_DISCARD_TABLESPACE); error = table_list->table->file->ha_discard_or_import_tablespace(discard, + m_alter_info->import_tablespace_option, table_def); THD_STAGE_INFO(thd, stage_end); @@ -14681,7 +14682,6 @@ bool prepare_fields_and_keys(THD *thd, const dd::Table *src_table, TABLE *table, /* Reset auto_increment value if it was dropped */ if ((field->auto_flags & Field::NEXT_NUMBER) && !(used_fields & HA_CREATE_USED_AUTO)) { - added_auto_inc_field -= 1; // IPK: AUTO_INCREMENT field is dropped create_info->auto_increment_value = 0; @@ -14826,7 +14826,6 @@ bool prepare_fields_and_keys(THD *thd, const dd::Table *src_table, TABLE *table, */ if (def->change && !def->after) continue; - /** RDS IPK : If this is an AUTO_INCREMENT field, @@ -14909,8 +14908,9 @@ bool prepare_fields_and_keys(THD *thd, const dd::Table *src_table, TABLE *table, // If no implicit row id be found, then putting new column in the last new_create_list.push_back(def); } else - // If no implicit row id in this table, then putting new column in the last - new_create_list.push_back(def); + // If no implicit row id in this table, then putting new column in the + // last + new_create_list.push_back(def); } else { const Create_field *find; if (def->change) { @@ -14994,8 +14994,7 @@ bool prepare_fields_and_keys(THD *thd, const dd::Table *src_table, TABLE *table, while (drop_idx < drop_list.size()) { const Alter_drop *drop = drop_list[drop_idx]; if (drop->type == Alter_drop::KEY && - !my_strcasecmp(system_charset_info, key_name, drop->name)) - { + !my_strcasecmp(system_charset_info, key_name, drop->name)) { /* RDS IPK : If dropped PK/UK, reduce the number */ if (key_info->flags & HA_NOSAME) added_unique_key--; break; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e68ce19d5c2..bbb3bd8b43c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1524,6 +1524,7 @@ void warn_about_deprecated_binary(THD *thd) signed_num opt_ignore_unknown_user opt_consensus_log_index + opt_import_tablespace_if_not_exists %type @@ -8903,9 +8904,9 @@ standalone_alter_commands: { $$= NEW_PTN PT_alter_table_discard_tablespace; } - | IMPORT TABLESPACE_SYM + | IMPORT TABLESPACE_SYM opt_import_tablespace_if_not_exists { - $$= NEW_PTN PT_alter_table_import_tablespace; + $$= NEW_PTN PT_alter_table_import_tablespace($3); } /* This part was added for release 5.1 by Mikael Ronström. @@ -8985,9 +8986,9 @@ standalone_alter_commands: $$= NEW_PTN PT_alter_table_discard_partition_tablespace($3); } | IMPORT PARTITION_SYM all_or_alt_part_name_list - TABLESPACE_SYM + TABLESPACE_SYM opt_import_tablespace_if_not_exists { - $$= NEW_PTN PT_alter_table_import_partition_tablespace($3); + $$= NEW_PTN PT_alter_table_import_partition_tablespace($3, $5); } | SECONDARY_LOAD_SYM { @@ -9273,6 +9274,10 @@ alter_lock_option_value: } } ; +opt_import_tablespace_if_not_exists: + /* empty */ { $$= 0; } + | IF not EXISTS { $$=HA_LEX_IMPORT_TABLESPACE_IF_NOT_EXISTS; } + ; opt_column: /* empty */ diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index bd579e554ee..b7bc9068f08 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -15374,6 +15374,7 @@ int ha_innobase::create(const char *name, TABLE *form, /** Discards or imports an InnoDB tablespace. @param[in] discard true if discard, else import +@param[in] option if value is HA_LEX_IMPORT_TABLESPACE_IF_NOT_EXISTS will ignore import tablespace if tablespace exists @param[in,out] table_def dd::Table describing table which tablespace is to be imported or discarded. Can be adjusted by SE, the changes will be saved into the data-dictionary at statement @@ -15381,6 +15382,7 @@ commit time. @return 0 == success, -1 == error */ int ha_innobase::discard_or_import_tablespace(bool discard, + uint option, dd::Table *table_def) { DBUG_TRACE; @@ -15463,15 +15465,27 @@ int ha_innobase::discard_or_import_tablespace(bool discard, m_prebuilt->trx); } else if (!dict_table->ibd_file_missing) { - ib::error(ER_IB_MSG_567) - << "Unable to import tablespace " << dict_table->name - << " because it already" - " exists. Please DISCARD the tablespace" - " before IMPORT."; - ib_senderrf(m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR, - ER_TABLESPACE_EXISTS, dict_table->name.m_name); + if (option & HA_LEX_IMPORT_TABLESPACE_IF_NOT_EXISTS) { + ib::warn(ER_IB_MSG_567) + << "Unable to import tablespace " << dict_table->name + << " because it already" + " exists. Please DISCARD the tablespace" + " before IMPORT."; + ib_senderrf(m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_WARN, + ER_TABLESPACE_EXISTS, dict_table->name.m_name); + // ignore if tablespace already exists, but write the binlog for replication + return (0); + } else { + ib::error(ER_IB_MSG_567) + << "Unable to import tablespace " << dict_table->name + << " because it already" + " exists. Please DISCARD the tablespace" + " before IMPORT."; + ib_senderrf(m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR, + ER_TABLESPACE_EXISTS, dict_table->name.m_name); - return HA_ERR_TABLE_EXIST; + return HA_ERR_TABLE_EXIST; + } } else { err = row_import_for_mysql(dict_table, table_def, m_prebuilt); @@ -18700,7 +18714,7 @@ int ha_innobase::extra(enum ha_extra_function operation) m_prebuilt->no_autoinc_locking = true; break; default: /* Do nothing */ - ; + ; } return (0); @@ -24792,7 +24806,8 @@ bool thd_get_transaction_group(THD *thd) { return THDVAR(thd, transaction_group); } -void ha_innobase::get_create_info(const char *table_name, const dd::Table *table_def, +void ha_innobase::get_create_info(const char *table_name, + const dd::Table *table_def, HA_CREATE_INFO *create_info) { dict_table_t *dict_table = nullptr; THD *m_thd = ha_thd(); diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index c1c7e25106f..07624d6c722 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -205,7 +205,7 @@ class ha_innobase : public handler { int optimize(THD *thd, HA_CHECK_OPT *check_opt) override; - int discard_or_import_tablespace(bool discard, dd::Table *table_def) override; + int discard_or_import_tablespace(bool discard, uint option, dd::Table *table_def) override; int extra(ha_extra_function operation) override; diff --git a/storage/innobase/handler/ha_innopart.cc b/storage/innobase/handler/ha_innopart.cc index 7d018ee236e..087c4eb5ec8 100644 --- a/storage/innobase/handler/ha_innopart.cc +++ b/storage/innobase/handler/ha_innopart.cc @@ -2825,12 +2825,14 @@ int ha_innopart::set_dd_discard_attribute(dd::Table *table_def, bool discard) { /** Discards or imports an InnoDB tablespace. @param[in] discard True if discard, else import. +@param[in] option if value is HA_LEX_IMPORT_TABLESPACE_IF_NOT_EXISTS will ignore import tablespace if tablespace exists @param[in,out] table_def dd::Table describing table which tablespaces are to be imported or discarded. Can be adjusted by SE, the changes will be saved into the data-dictionary at statement commit time. @return 0 or error number. */ int ha_innopart::discard_or_import_tablespace(bool discard, + uint option, dd::Table *table_def) { int error = 0; uint i; @@ -2840,7 +2842,7 @@ int ha_innopart::discard_or_import_tablespace(bool discard, for (i = m_part_info->get_first_used_partition(); i < m_tot_parts; i = m_part_info->get_next_used_partition(i)) { m_prebuilt->table = m_part_share->get_table_part(i); - error = ha_innobase::discard_or_import_tablespace(discard, table_def); + error = ha_innobase::discard_or_import_tablespace(discard, option, table_def); if (error != 0) { break; } diff --git a/storage/innobase/handler/ha_innopart.h b/storage/innobase/handler/ha_innopart.h index 4643ea5ed86..9aafd13cac5 100644 --- a/storage/innobase/handler/ha_innopart.h +++ b/storage/innobase/handler/ha_innopart.h @@ -381,10 +381,11 @@ class ha_innopart : public ha_innobase, /** Set DD discard attribute for tablespace. @param[in] table_def dd table @param[in] discard True if this table is discarded + @param[in] option if value is HA_LEX_IMPORT_TABLESPACE_IF_NOT_EXISTS will ignore import tablespace if tablespace exists @return 0 or error number. */ int set_dd_discard_attribute(dd::Table *table_def, bool discard); - int discard_or_import_tablespace(bool discard, dd::Table *table_def) override; + int discard_or_import_tablespace(bool discard, uint option, dd::Table *table_def) override; /** Compare key and rowid. Helper function for sorting records in the priority queue.