diff --git a/mysql-test/main/innodb_mysql_sync.result b/mysql-test/main/innodb_mysql_sync.result index a8a264d6580..3f284edde86 100644 --- a/mysql-test/main/innodb_mysql_sync.result +++ b/mysql-test/main/innodb_mysql_sync.result @@ -489,7 +489,9 @@ DROP TABLE t1; SET DEBUG_SYNC= 'alter_table_copy_after_lock_upgrade SIGNAL upgraded'; #Setup a table with FULLTEXT index. connection default; -CREATE TABLE t1(fld1 CHAR(10), FULLTEXT(fld1)) ENGINE= INNODB; +CREATE TABLE t1(fld1 CHAR(10), FULLTEXT(fld1), FULLTEXT(fld1)) ENGINE= INNODB; +Warnings: +Note 1831 Duplicate index `fld1_2`. This is deprecated and will be disallowed in a future release INSERT INTO t1 VALUES("String1"); #OPTIMIZE TABLE operation. OPTIMIZE TABLE t1; diff --git a/mysql-test/main/innodb_mysql_sync.test b/mysql-test/main/innodb_mysql_sync.test index 66935f811d7..4026080c4b4 100644 --- a/mysql-test/main/innodb_mysql_sync.test +++ b/mysql-test/main/innodb_mysql_sync.test @@ -650,7 +650,7 @@ SET DEBUG_SYNC= 'alter_table_copy_after_lock_upgrade SIGNAL upgraded'; --echo #Setup a table with FULLTEXT index. --connection default -CREATE TABLE t1(fld1 CHAR(10), FULLTEXT(fld1)) ENGINE= INNODB; +CREATE TABLE t1(fld1 CHAR(10), FULLTEXT(fld1), FULLTEXT(fld1)) ENGINE= INNODB; INSERT INTO t1 VALUES("String1"); --echo #OPTIMIZE TABLE operation. diff --git a/mysql-test/suite/innodb/r/innodb-alter.result b/mysql-test/suite/innodb/r/innodb-alter.result index d73bd154ee2..bf06e9a992b 100644 --- a/mysql-test/suite/innodb/r/innodb-alter.result +++ b/mysql-test/suite/innodb/r/innodb-alter.result @@ -439,17 +439,17 @@ tt CREATE TABLE `tt` ( FULLTEXT KEY `ct` (`ct`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ALTER TABLE tt ADD COLUMN c CHAR(1) NOT NULL FIRST, LOCK=NONE; -ERROR 0A000: LOCK=NONE is not supported. Reason: InnoDB presently supports one FULLTEXT index creation at a time. Try LOCK=SHARED +ERROR 0A000: LOCK=NONE is not supported. Reason: Fulltext index creation requires a lock. Try LOCK=SHARED ALTER TABLE tt ADD COLUMN c CHAR(1) NOT NULL, LOCK=NONE; -ERROR 0A000: LOCK=NONE is not supported. Reason: InnoDB presently supports one FULLTEXT index creation at a time. Try LOCK=SHARED +ERROR 0A000: LOCK=NONE is not supported. Reason: Fulltext index creation requires a lock. Try LOCK=SHARED CREATE TABLE tu ( pk INT PRIMARY KEY, FTS_DOC_ID BIGINT UNSIGNED NOT NULL, t TEXT, FULLTEXT INDEX(t) ) ENGINE=InnoDB; ALTER TABLE tu ADD COLUMN c CHAR(1) NOT NULL FIRST, LOCK=NONE; -ERROR 0A000: LOCK=NONE is not supported. Reason: InnoDB presently supports one FULLTEXT index creation at a time. Try LOCK=SHARED +ERROR 0A000: LOCK=NONE is not supported. Reason: Fulltext index creation requires a lock. Try LOCK=SHARED ALTER TABLE tu ADD COLUMN c CHAR(1) NOT NULL, LOCK=NONE; -ERROR 0A000: LOCK=NONE is not supported. Reason: InnoDB presently supports one FULLTEXT index creation at a time. Try LOCK=SHARED +ERROR 0A000: LOCK=NONE is not supported. Reason: Fulltext index creation requires a lock. Try LOCK=SHARED DROP TABLE tu; CREATE TABLE tv ( pk INT PRIMARY KEY, FTS_DOC_ID BIGINT UNSIGNED NOT NULL, t TEXT, @@ -457,9 +457,9 @@ UNIQUE INDEX FTS_DOC_ID_INDEX(FTS_DOC_ID), FULLTEXT INDEX(t) ) ENGINE=InnoDB; ALTER TABLE tv ADD COLUMN c CHAR(1) NOT NULL FIRST, LOCK=NONE; -ERROR 0A000: LOCK=NONE is not supported. Reason: InnoDB presently supports one FULLTEXT index creation at a time. Try LOCK=SHARED +ERROR 0A000: LOCK=NONE is not supported. Reason: Fulltext index creation requires a lock. Try LOCK=SHARED ALTER TABLE tv ADD COLUMN c CHAR(1) NOT NULL, LOCK=NONE; -ERROR 0A000: LOCK=NONE is not supported. Reason: InnoDB presently supports one FULLTEXT index creation at a time. Try LOCK=SHARED +ERROR 0A000: LOCK=NONE is not supported. Reason: Fulltext index creation requires a lock. Try LOCK=SHARED DROP TABLE tv; ALTER TABLE t1o CHANGE c1 dB_row_Id INT, ALGORITHM=COPY; ERROR 42000: Incorrect column name 'dB_row_Id' @@ -500,8 +500,6 @@ ADD COLUMN FTS_DOC_ID BIGINT UNSIGNED NOT NULL, ALGORITHM=INPLACE; ERROR 0A000: ALGORITHM=INPLACE is not supported. Reason: InnoDB presently supports one FULLTEXT index creation at a time. Try ALGORITHM=COPY ALTER TABLE t1o ADD COLUMN FTS_DOC_ID BIGINT UNSIGNED NOT NULL, ALGORITHM=INPLACE; -ERROR 0A000: ALGORITHM=INPLACE is not supported. Reason: InnoDB presently supports one FULLTEXT index creation at a time. Try ALGORITHM=COPY -ALTER TABLE t1o ADD COLUMN FTS_DOC_ID BIGINT UNSIGNED NOT NULL; ALTER TABLE t1o DROP COLUMN FTS_DOC_ID, ALGORITHM=INPLACE; ERROR 0A000: ALGORITHM=INPLACE is not supported. Reason: Cannot drop or rename FTS_DOC_ID. Try ALGORITHM=COPY ALTER TABLE t1o DROP COLUMN FTS_DOC_ID; diff --git a/mysql-test/suite/innodb/t/innodb-alter.test b/mysql-test/suite/innodb/t/innodb-alter.test index 918972eed9f..961d8653e90 100644 --- a/mysql-test/suite/innodb/t/innodb-alter.test +++ b/mysql-test/suite/innodb/t/innodb-alter.test @@ -272,13 +272,8 @@ ALTER TABLE t1o ADD FULLTEXT INDEX(cu), ADD COLUMN FTS_DOC_ID BIGINT UNSIGNED NOT NULL, ALGORITHM=INPLACE; # Replace the hidden FTS_DOC_ID column with a user-visible one. -# This used to work if there is at most one fulltext index. -# Currently, we disallow native ALTER TABLE if the table -# contains any FULLTEXT indexes. ---error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON ALTER TABLE t1o ADD COLUMN FTS_DOC_ID BIGINT UNSIGNED NOT NULL, ALGORITHM=INPLACE; -ALTER TABLE t1o ADD COLUMN FTS_DOC_ID BIGINT UNSIGNED NOT NULL; # Replace the user-visible FTS_DOC_ID column with a hidden one. # We do not support this in-place. --error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON diff --git a/mysql-test/suite/innodb_fts/r/innodb-fts-ddl.result b/mysql-test/suite/innodb_fts/r/innodb-fts-ddl.result index 6ee5f9f4322..1382457debf 100644 --- a/mysql-test/suite/innodb_fts/r/innodb-fts-ddl.result +++ b/mysql-test/suite/innodb_fts/r/innodb-fts-ddl.result @@ -79,7 +79,7 @@ CREATE FULLTEXT INDEX idx on fts_test (title, body) LOCK=NONE; ERROR 0A000: LOCK=NONE is not supported. Reason: Fulltext index creation requires a lock. Try LOCK=SHARED ALTER TABLE fts_test ADD FULLTEXT `idx` (title, body), ALGORITHM=NOCOPY; ALTER TABLE fts_test ROW_FORMAT=REDUNDANT, LOCK=NONE; -ERROR 0A000: LOCK=NONE is not supported. Reason: InnoDB presently supports one FULLTEXT index creation at a time. Try LOCK=SHARED +ERROR 0A000: LOCK=NONE is not supported. Reason: Fulltext index creation requires a lock. Try LOCK=SHARED ALTER TABLE fts_test ROW_FORMAT=REDUNDANT; SELECT * FROM fts_test WHERE MATCH (title, body) AGAINST ('Tutorial' IN NATURAL LANGUAGE MODE); @@ -228,3 +228,12 @@ DROP TABLE articles; CREATE TABLE t1 (a VARCHAR(3)) ENGINE=InnoDB; ALTER TABLE t1 ADD FULLTEXT KEY(a), ADD COLUMN b VARCHAR(3), ADD FULLTEXT KEY(b); DROP TABLE t1; +# +# MDEV-18152 Assertion 'num_fts_index <= 1' failed +# in prepare_inplace_alter_table_dict +# +CREATE TABLE t1 +(a VARCHAR(128), b VARCHAR(128), FULLTEXT INDEX(a), FULLTEXT INDEX(b)) +ENGINE=InnoDB; +ALTER TABLE t1 ADD c SERIAL; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb_fts/t/innodb-fts-ddl.test b/mysql-test/suite/innodb_fts/t/innodb-fts-ddl.test index ddd92556772..c81ec18a4e6 100644 --- a/mysql-test/suite/innodb_fts/t/innodb-fts-ddl.test +++ b/mysql-test/suite/innodb_fts/t/innodb-fts-ddl.test @@ -276,3 +276,13 @@ ALTER TABLE t1 ADD FULLTEXT KEY(a), ADD COLUMN b VARCHAR(3), ADD FULLTEXT KEY(b) # Cleanup DROP TABLE t1; + +--echo # +--echo # MDEV-18152 Assertion 'num_fts_index <= 1' failed +--echo # in prepare_inplace_alter_table_dict +--echo # +CREATE TABLE t1 +(a VARCHAR(128), b VARCHAR(128), FULLTEXT INDEX(a), FULLTEXT INDEX(b)) +ENGINE=InnoDB; +ALTER TABLE t1 ADD c SERIAL; +DROP TABLE t1; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 50398328df4..35c8b7fb1c5 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -290,11 +290,12 @@ thd_destructor_proxy(void *) mysql_cond_init(PSI_NOT_INSTRUMENTED, &thd_destructor_cond, 0); st_my_thread_var *myvar= _my_thread_var(); + myvar->current_mutex = &thd_destructor_mutex; + myvar->current_cond = &thd_destructor_cond; + THD *thd= create_thd(); thd_proc_info(thd, "InnoDB shutdown handler"); - myvar->current_mutex = &thd_destructor_mutex; - myvar->current_cond = &thd_destructor_cond; mysql_mutex_lock(&thd_destructor_mutex); srv_running.store(myvar, std::memory_order_relaxed); diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index e58eaca4cee..1edbd337e35 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -1242,20 +1242,18 @@ my_error_innodb( /** Determine if fulltext indexes exist in a given table. @param table MySQL table -@return whether fulltext indexes exist on the table */ -static -bool -innobase_fulltext_exist( -/*====================*/ - const TABLE* table) +@return number of fulltext indexes */ +static uint innobase_fulltext_exist(const TABLE* table) { + uint count = 0; + for (uint i = 0; i < table->s->keys; i++) { if (table->key_info[i].flags & HA_FULLTEXT) { - return(true); + count++; } } - return(false); + return count; } /** Determine whether indexed virtual columns exist in a table. @@ -1807,11 +1805,14 @@ ha_innobase::check_if_supported_inplace_alter( DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); } + const char* reason_rebuild = NULL; + switch (ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) { case ALTER_OPTIONS: if (alter_options_need_rebuild(ha_alter_info, table)) { - ha_alter_info->unsupported_reason = my_get_err_msg( + reason_rebuild = my_get_err_msg( ER_ALTER_OPERATION_TABLE_OPTIONS_NEED_REBUILD); + ha_alter_info->unsupported_reason = reason_rebuild; break; } /* fall through */ @@ -1946,8 +1947,9 @@ ha_innobase::check_if_supported_inplace_alter( } /* We should be able to do the operation in-place. - See if we can do it online (LOCK=NONE). */ - bool online = true; + See if we can do it online (LOCK=NONE) or without rebuild. */ + bool online = true, need_rebuild = false; + const uint fulltext_indexes = innobase_fulltext_exist(altered_table); List_iterator_fast cf_it( ha_alter_info->alter_info->create_list); @@ -2008,8 +2010,7 @@ ha_innobase::check_if_supported_inplace_alter( /* We cannot replace a hidden FTS_DOC_ID with a user-visible FTS_DOC_ID. */ - if (m_prebuilt->table->fts - && innobase_fulltext_exist(altered_table) + if (fulltext_indexes && m_prebuilt->table->fts && !my_strcasecmp( system_charset_info, key_part->field->field_name.str, @@ -2025,8 +2026,8 @@ ha_innobase::check_if_supported_inplace_alter( & AUTO_INCREMENT_FLAG)); if (key_part->field->flags & AUTO_INCREMENT_FLAG) { - /* We cannot assign an AUTO_INCREMENT - column values during online ALTER. */ + /* We cannot assign AUTO_INCREMENT values + during online or instant ALTER. */ DBUG_ASSERT(key_part->field == altered_table -> found_next_number_field); @@ -2036,6 +2037,7 @@ ha_innobase::check_if_supported_inplace_alter( } online = false; + need_rebuild = true; } if (innobase_is_v_fld(key_part->field)) { @@ -2068,7 +2070,7 @@ ha_innobase::check_if_supported_inplace_alter( || (m_prebuilt->table->fts->doc_col < dict_table_get_n_user_cols(m_prebuilt->table))); - if (m_prebuilt->table->fts && innobase_fulltext_exist(altered_table)) { + if (fulltext_indexes && m_prebuilt->table->fts) { /* FULLTEXT indexes are supposed to remain. */ /* Disallow DROP INDEX FTS_DOC_ID_INDEX */ @@ -2113,7 +2115,8 @@ ha_innobase::check_if_supported_inplace_alter( cf_it.rewind(); Field **af = altered_table->field; bool fts_need_rebuild = false; - const bool need_rebuild = innobase_need_rebuild(ha_alter_info, table); + need_rebuild = need_rebuild + || innobase_need_rebuild(ha_alter_info, table); while (Create_field* cf = cf_it++) { DBUG_ASSERT(cf->field @@ -2165,8 +2168,7 @@ ha_innobase::check_if_supported_inplace_alter( ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL); } else if (!is_non_const_value(*af) && set_default_value(*af)) { - if (m_prebuilt->table->fts - && innobase_fulltext_exist(altered_table) + if (fulltext_indexes > 1 && !my_strcasecmp(system_charset_info, (*af)->field_name.str, FTS_DOC_ID_COL_NAME)) { @@ -2189,42 +2191,36 @@ next_column: DBUG_RETURN(HA_ALTER_INPLACE_INSTANT); } - if (!online) { - /* We already determined that only a non-locking - operation is possible. */ - } else if ((need_rebuild || (ha_alter_info->handler_flags - & ALTER_ADD_PK_INDEX)) - && (innobase_fulltext_exist(altered_table) - || innobase_spatial_exist(altered_table) - || innobase_indexed_virtual_exist(altered_table))) { - /* Refuse to rebuild the table online, if - FULLTEXT OR SPATIAL indexes are to survive the rebuild. */ - online = false; + if (need_rebuild + && (fulltext_indexes + || innobase_spatial_exist(altered_table) + || innobase_indexed_virtual_exist(altered_table))) { /* If the table already contains fulltext indexes, refuse to rebuild the table natively altogether. */ - if (m_prebuilt->table->fts) { + if (fulltext_indexes > 1) { cannot_create_many_fulltext_index: ha_alter_info->unsupported_reason = my_get_err_msg(ER_INNODB_FT_LIMIT); DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); } - if (ha_alter_info->online - && !ha_alter_info->unsupported_reason) { - - if (innobase_spatial_exist(altered_table)) { - ha_alter_info->unsupported_reason = my_get_err_msg( - ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_GIS); - } else if (!innobase_fulltext_exist(altered_table)) { - /* MDEV-14341 FIXME: Remove this limitation. */ - ha_alter_info->unsupported_reason = - "online rebuild with indexed virtual columns"; - } else { - ha_alter_info->unsupported_reason = my_get_err_msg( - ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS); - } + if (!online || !ha_alter_info->online + || ha_alter_info->unsupported_reason != reason_rebuild) { + /* Either LOCK=NONE was not requested, or we already + gave specific reason to refuse it. */ + } else if (fulltext_indexes) { + ha_alter_info->unsupported_reason = my_get_err_msg( + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS); + } else if (innobase_spatial_exist(altered_table)) { + ha_alter_info->unsupported_reason = my_get_err_msg( + ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_GIS); + } else { + /* MDEV-14341 FIXME: Remove this limitation. */ + ha_alter_info->unsupported_reason = + "online rebuild with indexed virtual columns"; } + online = false; } if (ha_alter_info->handler_flags diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 0822dc44ef7..28c6a1abf21 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2000, 2018, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2015, 2018, MariaDB Corporation. +Copyright (c) 2015, 2019, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -3425,8 +3425,11 @@ row_drop_table_for_mysql( calling btr_search_drop_page_hash_index() while we hold the InnoDB dictionary lock, we will drop any adaptive hash index entries upfront. */ + bool immune = is_temp_name + || strstr(table->name.m_name, "/FTS"); + while (buf_LRU_drop_page_hash_for_tablespace(table)) { - if ((!is_temp_name && trx_is_interrupted(trx)) + if ((!immune && trx_is_interrupted(trx)) || srv_shutdown_state != SRV_SHUTDOWN_NONE) { err = DB_INTERRUPTED; diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index c997b01846c..349169219a8 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -254,9 +254,9 @@ row_sel_sec_rec_is_for_clust_rec( clust_field = static_cast(vfield->data); } else { clust_pos = dict_col_get_clust_pos(col, clust_index); - ut_ad(!rec_offs_nth_default(clust_offs, clust_pos)); - clust_field = rec_get_nth_field( - clust_rec, clust_offs, clust_pos, &clust_len); + clust_field = rec_get_nth_cfield( + clust_rec, clust_index, clust_offs, + clust_pos, &clust_len); } sec_field = rec_get_nth_field(sec_rec, sec_offs, i, &sec_len); @@ -2972,17 +2972,7 @@ row_sel_store_mysql_field_func( } else { /* The field is stored in the index record, or in the metadata for instant ADD COLUMN. */ - - if (rec_offs_nth_default(offsets, field_no)) { - ut_ad(dict_index_is_clust(index)); - ut_ad(index->is_instant()); - const dict_index_t* clust_index - = dict_table_get_first_index(prebuilt->table); - ut_ad(index == clust_index); - data = clust_index->instant_field_value(field_no,&len); - } else { - data = rec_get_nth_field(rec, offsets, field_no, &len); - } + data = rec_get_nth_cfield(rec, index, offsets, field_no, &len); if (len == UNIV_SQL_NULL) { /* MySQL assumes that the field for an SQL