diff options
Diffstat (limited to 'storage/innobase/handler/handler0alter.cc')
-rw-r--r-- | storage/innobase/handler/handler0alter.cc | 653 |
1 files changed, 592 insertions, 61 deletions
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 7bb370a8dc4..32501299630 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -26,7 +26,7 @@ Smart ALTER TABLE #include <sql_lex.h> // SQLCOM_CREATE_INDEX #include <innodb_priv.h> -extern "C" { +#include "dict0stats.h" #include "log0log.h" #include "row0merge.h" #include "srv0srv.h" @@ -34,7 +34,8 @@ extern "C" { #include "trx0roll.h" #include "ha_prototypes.h" #include "handler0alter.h" -} +#include "srv0mon.h" +#include "fts0priv.h" #include "ha_innodb.h" @@ -128,7 +129,7 @@ innobase_col_to_mysql( /*************************************************************//** Copies an InnoDB record to table->record[0]. */ -extern "C" UNIV_INTERN +UNIV_INTERN void innobase_rec_to_mysql( /*==================*/ @@ -141,7 +142,9 @@ innobase_rec_to_mysql( uint n_fields = table->s->fields; uint i; - ut_ad(n_fields == dict_table_get_n_user_cols(index->table)); + ut_ad(n_fields == dict_table_get_n_user_cols(index->table) + || (DICT_TF2_FLAG_IS_SET(index->table, DICT_TF2_FTS_HAS_DOC_ID) + && n_fields + 1 == dict_table_get_n_user_cols(index->table))); for (i = 0; i < n_fields; i++) { Field* field = table->field[i]; @@ -178,7 +181,7 @@ null_field: /*************************************************************//** Resets table->record[0]. */ -extern "C" UNIV_INTERN +UNIV_INTERN void innobase_rec_reset( /*===============*/ @@ -362,7 +365,7 @@ innobase_create_index_field_def( && field->type() != MYSQL_TYPE_VARCHAR) || (field->type() == MYSQL_TYPE_VARCHAR && key_part->length < field->pack_length() - - ((Field_varstring*)field)->length_bytes)) { + - ((Field_varstring*) field)->length_bytes)) { index_field->prefix_len = key_part->length; } else { @@ -416,6 +419,10 @@ innobase_create_index_def( index->ind_type |= DICT_UNIQUE; } + if (key->flags & HA_FULLTEXT) { + index->ind_type |= DICT_FTS; + } + if (key_primary) { index->ind_type |= DICT_CLUSTERED; } @@ -486,6 +493,143 @@ innobase_copy_index_def( } /*******************************************************************//** +Check whether the table has the FTS_DOC_ID column +@return TRUE if there exists the FTS_DOC_ID column, if TRUE but fts_doc_col_no + equal to ULINT_UNDEFINED then that means the column exists but is not + of the right type. */ +static +ibool +innobase_fts_check_doc_id_col( +/*==========================*/ + dict_table_t* table, /*!< in: table with FTS index */ + ulint* fts_doc_col_no) /*!< out: The column number for + Doc ID */ +{ + *fts_doc_col_no = ULINT_UNDEFINED; + + for (ulint i = 0; i + DATA_N_SYS_COLS < (ulint) table->n_cols; i++) { + const char* name = dict_table_get_col_name(table, i); + + if (strcmp(name, FTS_DOC_ID_COL_NAME) == 0) { + const dict_col_t* col; + + col = dict_table_get_nth_col(table, i); + + if (col->mtype != DATA_INT || col->len != 8) { + fprintf(stderr, + " InnoDB: %s column in table %s" + " must be of the BIGINT datatype\n", + FTS_DOC_ID_COL_NAME, table->name); + } else if (!(col->prtype & DATA_NOT_NULL)) { + fprintf(stderr, + " InnoDB: %s column in table %s" + " must be NOT NULL\n", + FTS_DOC_ID_COL_NAME, table->name); + + } else if (!(col->prtype & DATA_UNSIGNED)) { + fprintf(stderr, + " InnoDB: %s column in table %s" + " must be UNSIGNED\n", + FTS_DOC_ID_COL_NAME, table->name); + } else { + *fts_doc_col_no = i; + } + + return(TRUE); + } + } + + return(FALSE); +} + +/*******************************************************************//** +Check whether the table has a unique index with FTS_DOC_ID_INDEX_NAME +on the Doc ID column. +@return FTS_EXIST_DOC_ID_INDEX if there exists the FTS_DOC_ID index, +FTS_INCORRECT_DOC_ID_INDEX if the FTS_DOC_ID index is of wrong format */ +UNIV_INTERN +enum fts_doc_id_index_enum +innobase_fts_check_doc_id_index( +/*============================*/ + dict_table_t* table, /*!< in: table definition */ + ulint* fts_doc_col_no) /*!< out: The column number for + Doc ID */ +{ + dict_index_t* index; + dict_field_t* field; + + for (index = dict_table_get_first_index(table); + index; index = dict_table_get_next_index(index)) { + + /* Check if there exists a unique index with the name of + FTS_DOC_ID_INDEX_NAME */ + if (innobase_strcasecmp(index->name, FTS_DOC_ID_INDEX_NAME)) { + continue; + } + + if (!dict_index_is_unique(index) + || strcmp(index->name, FTS_DOC_ID_INDEX_NAME)) { + return(FTS_INCORRECT_DOC_ID_INDEX); + } + + /* Check whether the index has FTS_DOC_ID as its + first column */ + field = dict_index_get_nth_field(index, 0); + + /* The column would be of a BIGINT data type */ + if (strcmp(field->name, FTS_DOC_ID_COL_NAME) == 0 + && field->col->mtype == DATA_INT + && field->col->len == 8 + && field->col->prtype & DATA_NOT_NULL) { + if (fts_doc_col_no) { + *fts_doc_col_no = dict_col_get_no(field->col); + } + return(FTS_EXIST_DOC_ID_INDEX); + } else { + return(FTS_INCORRECT_DOC_ID_INDEX); + } + + } + + /* Not found */ + return(FTS_NOT_EXIST_DOC_ID_INDEX); +} +/*******************************************************************//** +Check whether the table has a unique index with FTS_DOC_ID_INDEX_NAME +on the Doc ID column in MySQL create index definition. +@return FTS_EXIST_DOC_ID_INDEX if there exists the FTS_DOC_ID index, +FTS_INCORRECT_DOC_ID_INDEX if the FTS_DOC_ID index is of wrong format */ +UNIV_INTERN +enum fts_doc_id_index_enum +innobase_fts_check_doc_id_index_in_def( +/*===================================*/ + ulint n_key, /*!< in: Number of keys */ + KEY * key_info) /*!< in: Key definition */ +{ + /* Check whether there is a "FTS_DOC_ID_INDEX" in the to be built index + list */ + for (ulint j = 0; j < n_key; j++) { + KEY* key = &key_info[j]; + + if (innobase_strcasecmp(key->name, FTS_DOC_ID_INDEX_NAME)) { + continue; + } + + /* Do a check on FTS DOC ID_INDEX, it must be unique, + named as "FTS_DOC_ID_INDEX" and on column "FTS_DOC_ID" */ + if (!(key->flags & HA_NOSAME) + || strcmp(key->name, FTS_DOC_ID_INDEX_NAME) + || strcmp(key->key_part[0].field->field_name, + FTS_DOC_ID_COL_NAME)) { + return(FTS_INCORRECT_DOC_ID_INDEX); + } + + return(FTS_EXIST_DOC_ID_INDEX); + } + + return(FTS_NOT_EXIST_DOC_ID_INDEX); +} +/*******************************************************************//** Create an index table where indexes are ordered as follows: IF a new primary key is defined for the table THEN @@ -507,12 +651,17 @@ merge_index_def_t* innobase_create_key_def( /*====================*/ trx_t* trx, /*!< in: trx */ - const dict_table_t*table, /*!< in: table definition */ + dict_table_t* table, /*!< in: table definition */ mem_heap_t* heap, /*!< in: heap where space for key definitions are allocated */ KEY* key_info, /*!< in: Indexes to be created */ - ulint& n_keys) /*!< in/out: Number of indexes to + ulint& n_keys, /*!< in/out: Number of indexes to be created */ + ulint* num_fts_index, /*!< out: Number of FTS indexes */ + ibool* add_fts_doc_id, /*!< out: Whether we need to add + new DOC ID column for FTS index */ + ibool* add_fts_doc_id_idx)/*!< out: Whether we need to add + new index on DOC ID column */ { ulint i = 0; merge_index_def_t* indexdef; @@ -525,6 +674,9 @@ innobase_create_key_def( mem_heap_alloc(heap, sizeof *indexdef * (n_keys + UT_LIST_GET_LEN(table->indexes))); + *add_fts_doc_id = FALSE; + *add_fts_doc_id_idx = FALSE; + /* If there is a primary key, it is always the first index defined for the table. */ @@ -552,12 +704,111 @@ innobase_create_key_def( } } - if (new_primary) { + /* Check whether any indexes in the create list are Full + Text Indexes*/ + for (ulint j = 0; j < n_keys; j++) { + if (key_info[j].flags & HA_FULLTEXT) { + (*num_fts_index)++; + } + } + + /* Check whether there is a "FTS_DOC_ID_INDEX" in the to be built index + list */ + if (innobase_fts_check_doc_id_index_in_def(n_keys, key_info) + == FTS_INCORRECT_DOC_ID_INDEX) { + push_warning_printf((THD*) trx->mysql_thd, + Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_NAME_FOR_INDEX, + " InnoDB: Index name %s is reserved" + " for the unique index on" + " FTS_DOC_ID column for FTS" + " document ID indexing" + " on table %s. Please check" + " the index definition to" + " make sure it is of correct" + " type\n", + FTS_DOC_ID_INDEX_NAME, + table->name); + DBUG_RETURN(NULL); + } + + /* If we are to build an FTS index, check whether the table + already has a DOC ID column, if not, we will need to add a + Doc ID hidden column and rebuild the primary index */ + if (*num_fts_index) { + enum fts_doc_id_index_enum ret; + ibool exists; + ulint doc_col_no; + ulint fts_doc_col_no; + + exists = innobase_fts_check_doc_id_col(table, &fts_doc_col_no); + + if (exists) { + + if (fts_doc_col_no == ULINT_UNDEFINED) { + + push_warning_printf( + (THD*) trx->mysql_thd, + Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_COLUMN_NAME, + " InnoDB: There exists a column %s " + "in table %s, but it is the wrong " + "type. Create of FTS index failed.\n", + FTS_DOC_ID_COL_NAME, table->name); + + DBUG_RETURN(NULL); + + } else if (!table->fts) { + table->fts = fts_create(table); + } + + table->fts->doc_col = fts_doc_col_no; + + } else { + *add_fts_doc_id = TRUE; + *add_fts_doc_id_idx = TRUE; + + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Rebuild table %s to add " + "DOC_ID column\n", table->name); + } + + ret = innobase_fts_check_doc_id_index(table, &doc_col_no); + + switch (ret) { + case FTS_NOT_EXIST_DOC_ID_INDEX: + *add_fts_doc_id_idx = TRUE; + break; + case FTS_INCORRECT_DOC_ID_INDEX: + + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Index %s is used for FTS" + " Doc ID indexing on table %s, it is" + " now on the wrong column or of" + " wrong format. Please drop it.\n", + FTS_DOC_ID_INDEX_NAME, table->name); + DBUG_RETURN(NULL); + + default: + ut_ad(ret == FTS_EXIST_DOC_ID_INDEX); + + ut_ad(doc_col_no == fts_doc_col_no); + } + } + + /* If DICT_TF2_FTS_ADD_DOC_ID is set, we will need to rebuild + the table to add the unique Doc ID column for FTS index. And + thus the primary index would required to be rebuilt. Copy all + the index definitions */ + if (new_primary || *add_fts_doc_id) { const dict_index_t* index; - /* Create the PRIMARY key index definition */ - innobase_create_index_def(&key_info[i++], TRUE, TRUE, - indexdef++, heap); + if (new_primary) { + /* Create the PRIMARY key index definition */ + innobase_create_index_def(&key_info[i++], + TRUE, TRUE, + indexdef++, heap); + } row_mysql_lock_data_dictionary(trx); @@ -568,17 +819,32 @@ innobase_create_key_def( index or a PRIMARY KEY. If the clustered index is a UNIQUE INDEX, it must be converted to a secondary index. */ - if (dict_index_get_nth_col(index, 0)->mtype == DATA_SYS - || !my_strcasecmp(system_charset_info, - index->name, "PRIMARY")) { + if (new_primary + && (dict_index_get_nth_col(index, 0)->mtype + == DATA_SYS + || !my_strcasecmp(system_charset_info, + index->name, "PRIMARY"))) { index = dict_table_get_next_index(index); } while (index) { innobase_copy_index_def(index, indexdef++, heap); + + if (new_primary && index->type & DICT_FTS) { + (*num_fts_index)++; + } + index = dict_table_get_next_index(index); } + /* The primary index would be rebuilt if a FTS Doc ID + column is to be added, and the primary index definition + is just copied from old table and stored in indexdefs[0] */ + if (*add_fts_doc_id) { + indexdefs[0].ind_type |= DICT_CLUSTERED; + DICT_TF2_FLAG_SET(table, DICT_TF2_FTS_ADD_DOC_ID); + } + row_mysql_unlock_data_dictionary(trx); } @@ -653,13 +919,95 @@ public: }; /*******************************************************************//** +This is to create FTS_DOC_ID_INDEX definition on the newly added Doc ID for +the FTS indexes table +@return dict_index_t for the FTS_DOC_ID_INDEX */ +dict_index_t* +innobase_create_fts_doc_id_idx( +/*===========================*/ + dict_table_t* indexed_table, /*!< in: Table where indexes are + created */ + trx_t* trx, /*!< in: Transaction */ + mem_heap_t* heap) /*!< Heap for index definitions */ +{ + dict_index_t* index; + merge_index_def_t fts_index_def; + char* index_name; + + /* Create the temp index name for FTS_DOC_ID_INDEX */ + fts_index_def.name = index_name = (char*) mem_heap_alloc( + heap, FTS_DOC_ID_INDEX_NAME_LEN + 2); + *index_name++ = TEMP_INDEX_PREFIX; + memcpy(index_name, FTS_DOC_ID_INDEX_NAME, + FTS_DOC_ID_INDEX_NAME_LEN); + index_name[FTS_DOC_ID_INDEX_NAME_LEN] = 0; + + /* Only the Doc ID will be indexed */ + fts_index_def.n_fields = 1; + fts_index_def.ind_type = DICT_UNIQUE; + fts_index_def.fields = (merge_index_field_t*) mem_heap_alloc( + heap, sizeof *fts_index_def.fields); + fts_index_def.fields[0].prefix_len = 0; + fts_index_def.fields[0].field_name = mem_heap_strdup( + heap, FTS_DOC_ID_COL_NAME); + + index = row_merge_create_index(trx, indexed_table, &fts_index_def); + return(index); +} + +/*******************************************************************//** +Clean up on ha_innobase::add_index error. */ +static +void +innobase_add_index_cleanup( +/*=======================*/ + row_prebuilt_t* prebuilt, /*!< in/out: prebuilt */ + trx_t* trx, /*!< in/out: transaction */ + dict_table_t* table) /*!< in/out: table on which + the indexes were going to be + created */ +{ + trx_rollback_to_savepoint(trx, NULL); + + ut_a(trx != prebuilt->trx); + + trx_free_for_mysql(trx); + + trx_commit_for_mysql(prebuilt->trx); + + if (table != NULL) { + + rw_lock_x_lock(&dict_operation_lock); + + dict_mutex_enter_for_mysql(); + + /* Note: This check excludes the system tables. However, we + should be safe because users cannot add indexes to system + tables. */ + + if (UT_LIST_GET_LEN(table->foreign_list) == 0 + && UT_LIST_GET_LEN(table->referenced_list) == 0 + && !table->can_be_evicted) { + + dict_table_move_from_non_lru_to_lru(table); + } + + dict_table_close(table, TRUE); + + dict_mutex_exit_for_mysql(); + + rw_lock_x_unlock(&dict_operation_lock); + } +} + +/*******************************************************************//** Create indexes. @return 0 or error number */ UNIV_INTERN int ha_innobase::add_index( /*===================*/ - TABLE* table, /*!< in: Table where indexes + TABLE* in_table, /*!< in: Table where indexes are created */ KEY* key_info, /*!< in: Indexes to be created */ @@ -667,16 +1015,21 @@ ha_innobase::add_index( to be created */ handler_add_index** add) /*!< out: context */ { - dict_index_t** index; /*!< Index to be created */ + dict_index_t** index = NULL; /*!< Index to be created */ + dict_index_t* fts_index = NULL;/*!< FTS Index to be created */ dict_table_t* indexed_table; /*!< Table where indexes are created */ merge_index_def_t* index_defs; /*!< Index definitions */ - mem_heap_t* heap; /*!< Heap for index definitions */ + mem_heap_t* heap = NULL; /*!< Heap for index definitions */ trx_t* trx; /*!< Transaction */ ulint num_of_idx; ulint num_created = 0; ibool dict_locked = FALSE; - ulint new_primary; + ulint new_primary = 0; int error; + ulint num_fts_index = 0; + ulint num_idx_create = 0; + ibool fts_add_doc_id = FALSE; + ibool fts_add_doc_idx = FALSE; DBUG_ENTER("ha_innobase::add_index"); ut_a(table); @@ -700,7 +1053,7 @@ ha_innobase::add_index( DBUG_RETURN(-1); } - indexed_table = dict_table_get(prebuilt->table->name, FALSE); + indexed_table = dict_table_open_on_name(prebuilt->table->name, FALSE); if (UNIV_UNLIKELY(!indexed_table)) { DBUG_RETURN(HA_ERR_NO_SUCH_TABLE); @@ -712,16 +1065,22 @@ ha_innobase::add_index( error = innobase_check_index_keys(key_info, num_of_keys, prebuilt->table); if (UNIV_UNLIKELY(error)) { + dict_table_close(prebuilt->table, FALSE); DBUG_RETURN(error); } /* Check each index's column length to make sure they do not exceed limit */ for (ulint i = 0; i < num_of_keys; i++) { + if (key_info[i].flags & HA_FULLTEXT) { + continue; + } + error = innobase_check_column_length(prebuilt->table, &key_info[i]); if (error) { + dict_table_close(prebuilt->table, FALSE); DBUG_RETURN(error); } } @@ -734,6 +1093,20 @@ ha_innobase::add_index( trx = innobase_trx_allocate(user_thd); trx_start_if_not_started(trx); + /* We don't want this table to be evicted from the cache while we + are building an index on it. Another issue is that while we are + building the index this table could be referred to in a foreign + key relationship. In innobase_add_index_cleanup() we check for + that condition before moving it back to the LRU list. */ + + row_mysql_lock_data_dictionary(trx); + + if (prebuilt->table->can_be_evicted) { + dict_table_move_from_lru_to_non_lru(prebuilt->table); + } + + row_mysql_unlock_data_dictionary(trx); + /* Create table containing all indexes to be built in this alter table add index so that they are in the correct order in the table. */ @@ -741,14 +1114,34 @@ ha_innobase::add_index( num_of_idx = num_of_keys; index_defs = innobase_create_key_def( - trx, prebuilt->table, heap, key_info, num_of_idx); + trx, prebuilt->table, heap, key_info, num_of_idx, + &num_fts_index, &fts_add_doc_id, &fts_add_doc_idx); + + if (!index_defs) { + error = DB_UNSUPPORTED; + goto error_handling; + } + + /* Currently, support create one single FULLTEXT index in parallel at + a time */ + if (num_fts_index > 1) { + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: Only support create ONE Fulltext index" + " at a time\n"); + error = DB_UNSUPPORTED; + goto error_handling; + } new_primary = DICT_CLUSTERED & index_defs[0].ind_type; - /* Allocate memory for dictionary index definitions */ + /* If a new FTS Doc ID column is to be added, there will be + one additional index to be built on the Doc ID column itself. */ + num_idx_create = (fts_add_doc_idx) ? num_of_idx + 1 : num_of_idx; + /* Allocate memory for dictionary index definitions */ index = (dict_index_t**) mem_heap_alloc( - heap, num_of_idx * sizeof *index); + heap, num_idx_create * sizeof *index); /* Flag this transaction as a dictionary operation, so that the data dictionary will be locked in crash recovery. */ @@ -776,8 +1169,9 @@ ha_innobase::add_index( if (UNIV_UNLIKELY(new_primary)) { /* This transaction should be the only one - operating on the table. */ - ut_a(prebuilt->table->n_mysql_handles_opened == 1); + operating on the table. The table get above + would have incremented the ref count to 2. */ + ut_a(prebuilt->table->n_ref_count == 2); char* new_table_name = innobase_create_temporary_tablename( heap, '1', prebuilt->table->name); @@ -806,11 +1200,12 @@ ha_innobase::add_index( ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE)); - mem_heap_free(heap); - trx_general_rollback_for_mysql(trx, NULL); row_mysql_unlock_data_dictionary(trx); - trx_free_for_mysql(trx); - trx_commit_for_mysql(prebuilt->trx); + mem_heap_free(heap); + + innobase_add_index_cleanup( + prebuilt, trx, prebuilt->table); + DBUG_RETURN(error); } @@ -828,6 +1223,40 @@ ha_innobase::add_index( error = trx->error_state; goto error_handling; } + + if (index[num_created]->type & DICT_FTS) { + fts_index = index[num_created]; + fts_create_index_tables(trx, fts_index); + + } + } + + /* create FTS_DOC_ID_INDEX on the Doc ID column on the table */ + if (fts_add_doc_idx) { + index[num_of_idx] = innobase_create_fts_doc_id_idx( + indexed_table, trx, heap); + /* FTS_DOC_ID_INDEX is internal defined new index */ + num_of_idx++; + num_created++; + } + + if (num_fts_index) { + DICT_TF2_FLAG_SET(indexed_table, DICT_TF2_FTS); + + if (!indexed_table->fts + || ib_vector_size(indexed_table->fts->indexes) == 0) { + fts_create_common_tables(trx, indexed_table, + prebuilt->table->name, TRUE); + + indexed_table->fts->fts_status |= TABLE_DICT_LOCKED; + innobase_fts_load_stopword( + indexed_table, trx, ha_thd()); + indexed_table->fts->fts_status &= ~TABLE_DICT_LOCKED; + } + + if (new_primary && prebuilt->table->fts) { + indexed_table->fts->doc_col = prebuilt->table->fts->doc_col; + } } ut_ad(error == DB_SUCCESS); @@ -843,8 +1272,7 @@ ha_innobase::add_index( row_mysql_unlock_data_dictionary(trx); dict_locked = FALSE; - ut_a(trx->n_active_thrs == 0); - ut_a(UT_LIST_GET_LEN(trx->signals) == 0); + ut_a(trx->lock.n_active_thrs == 0); if (UNIV_UNLIKELY(new_primary)) { /* A primary key is to be built. Acquire an exclusive @@ -867,60 +1295,66 @@ ha_innobase::add_index( index, num_of_idx, table); error_handling: + /* After an error, remove all those index definitions from the dictionary which were defined. */ + if (!dict_locked) { + row_mysql_lock_data_dictionary(trx); + dict_locked = TRUE; + } + switch (error) { case DB_SUCCESS: - ut_a(!dict_locked); - - ut_d(mutex_enter(&dict_sys->mutex)); ut_d(dict_table_check_for_dup_indexes(prebuilt->table, TRUE)); - ut_d(mutex_exit(&dict_sys->mutex)); - *add = new ha_innobase_add_index(table, key_info, num_of_keys, - indexed_table); + + *add = new ha_innobase_add_index( + table, key_info, num_of_keys, indexed_table); + + dict_table_close(prebuilt->table, dict_locked); break; case DB_TOO_BIG_RECORD: my_error(HA_ERR_TO_BIG_ROW, MYF(0)); - goto error; + goto error_exit; case DB_PRIMARY_KEY_IS_NULL: my_error(ER_PRIMARY_CANT_HAVE_NULL, MYF(0)); /* fall through */ case DB_DUPLICATE_KEY: -error: + if (fts_add_doc_idx + && prebuilt->trx->error_key_num == num_of_idx - 1) { + prebuilt->trx->error_key_num = ULINT_UNDEFINED; + } +error_exit: prebuilt->trx->error_info = NULL; /* fall through */ default: + dict_table_close(prebuilt->table, dict_locked); + trx->error_state = DB_SUCCESS; if (new_primary) { if (indexed_table != prebuilt->table) { + dict_table_close(indexed_table, dict_locked); row_merge_drop_table(trx, indexed_table); } } else { - if (!dict_locked) { - row_mysql_lock_data_dictionary(trx); - dict_locked = TRUE; - } - row_merge_drop_indexes(trx, indexed_table, index, num_created); } } + ut_ad(!new_primary || prebuilt->table->n_ref_count == 1); trx_commit_for_mysql(trx); + ut_ad(dict_locked); + row_mysql_unlock_data_dictionary(trx); + trx_free_for_mysql(trx); + mem_heap_free(heap); + if (prebuilt->trx) { trx_commit_for_mysql(prebuilt->trx); } - if (dict_locked) { - row_mysql_unlock_data_dictionary(trx); - } - - trx_free_for_mysql(trx); - mem_heap_free(heap); - /* There might be work for utility threads.*/ srv_active_wake_master_thread(); @@ -982,9 +1416,12 @@ ha_innobase::final_add_index( prebuilt->table, add->indexed_table, tmp_name, trx); + ut_a(prebuilt->table->n_ref_count == 1); + switch (error) { case DB_TABLESPACE_ALREADY_EXISTS: case DB_DUPLICATE_KEY: + ut_a(add->indexed_table->n_ref_count == 0); innobase_convert_tablename(tmp_name); my_error(HA_ERR_TABLE_EXIST, MYF(0), tmp_name); err = HA_ERR_TABLE_EXIST; @@ -1000,6 +1437,7 @@ ha_innobase::final_add_index( } if (!commit || err) { + dict_table_close(add->indexed_table, TRUE); error = row_merge_drop_table(trx, add->indexed_table); trx_commit_for_mysql(prebuilt->trx); } else { @@ -1007,7 +1445,6 @@ ha_innobase::final_add_index( trx_commit_for_mysql(prebuilt->trx); row_prebuilt_free(prebuilt, TRUE); error = row_merge_drop_table(trx, old_table); - add->indexed_table->n_mysql_handles_opened++; prebuilt = row_create_prebuilt(add->indexed_table, 0 /* XXX Do we know the mysql_row_len here? Before the addition of this parameter to @@ -1018,8 +1455,14 @@ ha_innobase::final_add_index( err = convert_error_code_to_mysql( error, prebuilt->table->flags, user_thd); - } else { - /* We created secondary indexes (!new_primary). */ + } + + if (add->indexed_table == prebuilt->table + || DICT_TF2_FLAG_IS_SET(prebuilt->table, DICT_TF2_FTS_ADD_DOC_ID)) { + /* We created secondary indexes (!new_primary) or create full + text index and added a new Doc ID column, we will need to + rename the secondary index on the Doc ID column to its + official index name.. */ if (commit) { err = convert_error_code_to_mysql( @@ -1043,13 +1486,66 @@ ha_innobase::final_add_index( } } } + + DICT_TF2_FLAG_UNSET(prebuilt->table, DICT_TF2_FTS_ADD_DOC_ID); } /* If index is successfully built, we will need to rebuild index translation table. Set valid index entry count in the translation table to zero. */ if (err == 0 && commit) { + ibool new_primary; + dict_index_t* index; + dict_index_t* next_index; + ibool new_fts = FALSE; + dict_index_t* primary; + + new_primary = !my_strcasecmp( + system_charset_info, add->key_info[0].name, "PRIMARY"); + + primary = dict_table_get_first_index(add->indexed_table); + + if (!new_primary) { + new_primary = !my_strcasecmp( + system_charset_info, add->key_info[0].name, + primary->name); + } + share->idx_trans_tbl.index_count = 0; + + if (new_primary) { + for (index = primary; index; index = next_index) { + + next_index = dict_table_get_next_index(index); + + if (index->type & DICT_FTS) { + fts_add_index(index, + add->indexed_table); + new_fts = TRUE; + } + } + } else { + ulint i; + for (i = 0; i < add->num_of_keys; i++) { + if (add->key_info[i].flags & HA_FULLTEXT) { + dict_index_t* fts_index; + + fts_index = + dict_table_get_index_on_name( + prebuilt->table, + add->key_info[i].name); + + ut_ad(fts_index); + fts_add_index(fts_index, + prebuilt->table); + new_fts = TRUE; + } + } + } + + if (new_fts) { + fts_optimize_add_table(prebuilt->table); + } } trx_commit_for_mysql(trx); @@ -1058,6 +1554,9 @@ ha_innobase::final_add_index( } ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE)); + + ut_a(fts_check_cached_index(prebuilt->table)); + row_mysql_unlock_data_dictionary(trx); trx_free_for_mysql(trx); @@ -1068,7 +1567,6 @@ ha_innobase::final_add_index( delete add; DBUG_RETURN(err); } - /*******************************************************************//** Prepare to drop some indexes of a table. @return 0 or error number */ @@ -1076,13 +1574,13 @@ UNIV_INTERN int ha_innobase::prepare_drop_index( /*============================*/ - TABLE* table, /*!< in: Table where indexes are dropped */ + TABLE* in_table, /*!< in: Table where indexes are dropped */ uint* key_num, /*!< in: Key nums to be dropped */ uint num_of_keys) /*!< in: Number of keys to be dropped */ { trx_t* trx; int err = 0; - uint n_key; + uint n_key; DBUG_ENTER("ha_innobase::prepare_drop_index"); ut_ad(table); @@ -1284,7 +1782,8 @@ UNIV_INTERN int ha_innobase::final_drop_index( /*==========================*/ - TABLE* table) /*!< in: Table where indexes are dropped */ + TABLE* iin_table) /*!< in: Table where indexes + are dropped */ { dict_index_t* index; /*!< Index to be dropped */ trx_t* trx; /*!< Transaction */ @@ -1300,12 +1799,12 @@ ha_innobase::final_drop_index( update_thd(); trx_search_latch_release_if_reserved(prebuilt->trx); - trx_start_if_not_started(prebuilt->trx); + trx_start_if_not_started_xa(prebuilt->trx); /* Create a background transaction for the operations on the data dictionary tables. */ trx = innobase_trx_allocate(user_thd); - trx_start_if_not_started(trx); + trx_start_if_not_started_xa(trx); /* Flag this transaction as a dictionary operation, so that the data dictionary will be locked in crash recovery. */ @@ -1317,6 +1816,36 @@ ha_innobase::final_drop_index( row_merge_lock_table(prebuilt->trx, prebuilt->table, LOCK_X), prebuilt->table->flags, user_thd); + /* Delete corresponding rows from the stats table. + Marko advises not to edit both user tables and SYS_* tables in one + trx, thus we use prebuilt->trx instead of trx. Because of this the + drop from SYS_* and from the stats table cannot happen in one + transaction and eventually if a crash occurs below, between + trx_commit_for_mysql(trx); which drops the indexes from SYS_* and + trx_commit_for_mysql(prebuilt->trx); + then an orphaned rows will be left in the stats table. */ + for (index = dict_table_get_first_index(prebuilt->table); + index != NULL; + index = dict_table_get_next_index(index)) { + + if (index->to_be_dropped) { + + enum db_err ret; + char errstr[1024]; + + ret = dict_stats_delete_index_stats( + index, prebuilt->trx, + errstr, sizeof(errstr)); + + if (ret != DB_SUCCESS) { + push_warning(user_thd, + Sql_condition::WARN_LEVEL_WARN, + ER_LOCK_WAIT_TIMEOUT, + errstr); + } + } + } + row_mysql_lock_data_dictionary(trx); ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE)); @@ -1344,7 +1873,6 @@ ha_innobase::final_drop_index( next_index = dict_table_get_next_index(index); if (index->to_be_dropped) { - row_merge_drop_index(index, prebuilt->table, trx); } @@ -1363,6 +1891,9 @@ ha_innobase::final_drop_index( func_exit: ut_d(dict_table_check_for_dup_indexes(prebuilt->table, FALSE)); + + ut_a(fts_check_cached_index(prebuilt->table)); + trx_commit_for_mysql(trx); trx_commit_for_mysql(prebuilt->trx); row_mysql_unlock_data_dictionary(trx); |