@ -640,6 +640,18 @@ innobase_create_temporary_tablename(
return ( name ) ;
}
class ha_innobase_add_index : public handler_add_index
{
public :
/** table where the indexes are being created */
dict_table_t * indexed_table ;
ha_innobase_add_index ( TABLE * table , KEY * key_info , uint num_of_keys ,
dict_table_t * indexed_table_arg ) :
handler_add_index ( table , key_info , num_of_keys ) ,
indexed_table ( indexed_table_arg ) { }
~ ha_innobase_add_index ( ) { }
} ;
/*******************************************************************/ /**
Create indexes .
@ return 0 or error number */
@ -647,12 +659,15 @@ UNIV_INTERN
int
ha_innobase : : add_index (
/*===================*/
TABLE * table , /*!< in: Table where indexes are created */
KEY * key_info , /*!< in: Indexes to be created */
uint num_of_keys ) /*!< in: Number of indexes to be created */
TABLE * table , /*!< in: Table where indexes
are created */
KEY * key_info , /*!< in: Indexes
to be created */
uint num_of_keys , /*!< in: Number of indexes
to be created */
handler_add_index * * add ) /*!< out: context */
{
dict_index_t * * index ; /*!< Index to be created */
dict_table_t * innodb_table ; /*!< InnoDB table in dictionary */
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 */
@ -668,6 +683,8 @@ ha_innobase::add_index(
ut_a ( key_info ) ;
ut_a ( num_of_keys ) ;
* add = NULL ;
if ( srv_created_new_raw | | srv_force_recovery ) {
DBUG_RETURN ( HA_ERR_WRONG_COMMAND ) ;
}
@ -683,15 +700,16 @@ ha_innobase::add_index(
DBUG_RETURN ( - 1 ) ;
}
innodb_table = indexed_table
= dict_table_get ( prebuilt - > table - > name , FALSE ) ;
indexed_table = dict_table_get ( prebuilt - > table - > name , FALSE ) ;
if ( UNIV_UNLIKELY ( ! innodb _table ) ) {
if ( UNIV_UNLIKELY ( ! indexed _table ) ) {
DBUG_RETURN ( HA_ERR_NO_SUCH_TABLE ) ;
}
ut_a ( indexed_table = = prebuilt - > table ) ;
/* Check that index keys are sensible */
error = innobase_check_index_keys ( key_info , num_of_keys , innodb_table ) ;
error = innobase_check_index_keys ( key_info , num_of_keys , prebuilt - > table ) ;
if ( UNIV_UNLIKELY ( error ) ) {
DBUG_RETURN ( error ) ;
@ -700,7 +718,7 @@ ha_innobase::add_index(
/* Check each index's column length to make sure they do not
exceed limit */
for ( ulint i = 0 ; i < num_of_keys ; i + + ) {
error = innobase_check_column_length ( innodb_ table,
error = innobase_check_column_length ( prebuilt - > table ,
& key_info [ i ] ) ;
if ( error ) {
@ -723,7 +741,7 @@ ha_innobase::add_index(
num_of_idx = num_of_keys ;
index_defs = innobase_create_key_def (
trx , innodb_ table, heap , key_info , num_of_idx ) ;
trx , prebuilt - > table , heap , key_info , num_of_idx ) ;
new_primary = DICT_CLUSTERED & index_defs [ 0 ] . ind_type ;
@ -737,7 +755,7 @@ ha_innobase::add_index(
trx_set_dict_operation ( trx , TRX_DICT_OP_INDEX ) ;
/* Acquire a lock on the table before creating any indexes. */
error = row_merge_lock_table ( prebuilt - > trx , innodb_ table,
error = row_merge_lock_table ( prebuilt - > trx , prebuilt - > table ,
new_primary ? LOCK_X : LOCK_S ) ;
if ( UNIV_UNLIKELY ( error ! = DB_SUCCESS ) ) {
@ -751,7 +769,7 @@ ha_innobase::add_index(
row_mysql_lock_data_dictionary ( trx ) ;
dict_locked = TRUE ;
ut_d ( dict_table_check_for_dup_indexes ( innodb_ table, FALSE ) ) ;
ut_d ( dict_table_check_for_dup_indexes ( prebuilt - > table , FALSE ) ) ;
/* If a new primary key is defined for the table we need
to drop the original table and rebuild all indexes . */
@ -759,15 +777,15 @@ ha_innobase::add_index(
if ( UNIV_UNLIKELY ( new_primary ) ) {
/* This transaction should be the only one
operating on the table . */
ut_a ( innodb_ table- > n_mysql_handles_opened = = 1 ) ;
ut_a ( prebuilt - > table - > n_mysql_handles_opened = = 1 ) ;
char * new_table_name = innobase_create_temporary_tablename (
heap , ' 1 ' , innodb_ table- > name ) ;
heap , ' 1 ' , prebuilt - > table - > name ) ;
/* Clone the table. */
trx_set_dict_operation ( trx , TRX_DICT_OP_TABLE ) ;
indexed_table = row_merge_create_temporary_table (
new_table_name , index_defs , innodb_ table, trx ) ;
new_table_name , index_defs , prebuilt - > table , trx ) ;
if ( ! indexed_table ) {
@ -781,11 +799,12 @@ ha_innobase::add_index(
break ;
default :
error = convert_error_code_to_mysql (
trx - > error_state , innodb_table - > flags ,
trx - > error_state ,
prebuilt - > table - > flags ,
user_thd ) ;
}
ut_d ( dict_table_check_for_dup_indexes ( innodb_ table,
ut_d ( dict_table_check_for_dup_indexes ( prebuilt - > table ,
FALSE ) ) ;
mem_heap_free ( heap ) ;
trx_general_rollback_for_mysql ( trx , NULL ) ;
@ -800,17 +819,15 @@ ha_innobase::add_index(
/* Create the indexes in SYS_INDEXES and load into dictionary. */
for ( ulint i = 0 ; i < num_of_idx ; i + + ) {
for ( num_created = 0 ; num_created < num_of_idx ; num_created + + ) {
index [ i ] = row_merge_create_index ( trx , indexed_table ,
& index_defs [ i ] ) ;
index [ num_created ] = row_merge_create_index (
trx , indexed_table , & index_defs [ num_created ] ) ;
if ( ! index [ i ] ) {
if ( ! index [ num_created ] ) {
error = trx - > error_state ;
goto error_handling ;
}
num_created + + ;
}
ut_ad ( error = = DB_SUCCESS ) ;
@ -832,7 +849,7 @@ ha_innobase::add_index(
if ( UNIV_UNLIKELY ( new_primary ) ) {
/* A primary key is to be built. Acquire an exclusive
table lock also on the table that is being created . */
ut_ad ( indexed_table ! = innodb_ table) ;
ut_ad ( indexed_table ! = prebuilt - > table ) ;
error = row_merge_lock_table ( prebuilt - > trx , indexed_table ,
LOCK_X ) ;
@ -846,7 +863,7 @@ ha_innobase::add_index(
/* Read the clustered index of the table and build indexes
based on this information using temporary files and merge sort . */
error = row_merge_build_indexes ( prebuilt - > trx ,
innodb_ table, indexed_table ,
prebuilt - > table , indexed_table ,
index , num_of_idx , table ) ;
error_handling :
@ -854,63 +871,15 @@ error_handling:
dictionary which were defined . */
switch ( error ) {
const char * old_name ;
char * tmp_name ;
case DB_SUCCESS :
ut_a ( ! dict_locked ) ;
row_mysql_lock_data_dictionary ( trx ) ;
dict_locked = TRUE ;
ut_d ( mutex_enter ( & dict_sys - > mutex ) ) ;
ut_d ( dict_table_check_for_dup_indexes ( prebuilt - > table , TRUE ) ) ;
if ( ! new_primary ) {
error = row_merge_rename_indexes ( trx , indexed_table ) ;
if ( error ! = DB_SUCCESS ) {
row_merge_drop_indexes ( trx , indexed_table ,
index , num_created ) ;
}
goto convert_error ;
}
/* If a new primary key was defined for the table and
there was no error at this point , we can now rename
the old table as a temporary table , rename the new
temporary table as the old table and drop the old table . */
old_name = innodb_table - > name ;
tmp_name = innobase_create_temporary_tablename ( heap , ' 2 ' ,
old_name ) ;
error = row_merge_rename_tables ( innodb_table , indexed_table ,
tmp_name , trx ) ;
if ( error ! = DB_SUCCESS ) {
row_merge_drop_table ( trx , indexed_table ) ;
switch ( error ) {
case DB_TABLESPACE_ALREADY_EXISTS :
case DB_DUPLICATE_KEY :
innobase_convert_tablename ( tmp_name ) ;
my_error ( HA_ERR_TABLE_EXIST , MYF ( 0 ) , tmp_name ) ;
error = HA_ERR_TABLE_EXIST ;
break ;
default :
goto convert_error ;
}
break ;
}
trx_commit_for_mysql ( prebuilt - > trx ) ;
row_prebuilt_free ( prebuilt , TRUE ) ;
prebuilt = row_create_prebuilt ( indexed_table ) ;
indexed_table - > n_mysql_handles_opened + + ;
error = row_merge_drop_table ( trx , innodb_table ) ;
innodb_table = indexed_table ;
goto convert_error ;
ut_d ( mutex_exit ( & dict_sys - > mutex ) ) ;
* add = new ha_innobase_add_index ( table , key_info , num_of_keys ,
indexed_table ) ;
break ;
case DB_TOO_BIG_RECORD :
my_error ( HA_ERR_TO_BIG_ROW , MYF ( 0 ) ) ;
@ -926,7 +895,7 @@ error:
trx - > error_state = DB_SUCCESS ;
if ( new_primary ) {
if ( indexed_table ! = innodb_ table) {
if ( indexed_table ! = prebuilt - > table ) {
row_merge_drop_table ( trx , indexed_table ) ;
}
} else {
@ -938,38 +907,161 @@ error:
row_merge_drop_indexes ( trx , indexed_table ,
index , num_created ) ;
}
convert_error :
if ( error = = DB_SUCCESS ) {
/* Build index is successful. We will need to
rebuild index translation table . Reset the
index entry count in the translation table
to zero , so that translation table will be rebuilt */
share - > idx_trans_tbl . index_count = 0 ;
}
error = convert_error_code_to_mysql ( error ,
innodb_table - > flags ,
user_thd ) ;
}
mem_heap_free ( heap ) ;
trx_commit_for_mysql ( trx ) ;
if ( prebuilt - > trx ) {
trx_commit_for_mysql ( prebuilt - > trx ) ;
}
if ( dict_locked ) {
ut_d ( dict_table_check_for_dup_indexes ( innodb_table , FALSE ) ) ;
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 ( ) ;
DBUG_RETURN ( error ) ;
DBUG_RETURN ( convert_error_code_to_mysql ( error , prebuilt - > table - > flags ,
user_thd ) ) ;
}
/*******************************************************************/ /**
Finalize or undo add_index ( ) .
@ return 0 or error number */
UNIV_INTERN
int
ha_innobase : : final_add_index (
/*=========================*/
handler_add_index * add_arg , /*!< in: context from add_index() */
bool commit ) /*!< in: true=commit, false=rollback */
{
ha_innobase_add_index * add ;
trx_t * trx ;
int err = 0 ;
DBUG_ENTER ( " ha_innobase::final_add_index " ) ;
ut_ad ( add_arg ) ;
add = static_cast < class ha_innobase_add_index * > ( add_arg ) ;
/* Create a background transaction for the operations on
the data dictionary tables . */
trx = innobase_trx_allocate ( user_thd ) ;
trx_start_if_not_started ( trx ) ;
/* Flag this transaction as a dictionary operation, so that
the data dictionary will be locked in crash recovery . */
trx_set_dict_operation ( trx , TRX_DICT_OP_INDEX ) ;
/* Latch the InnoDB data dictionary exclusively so that no deadlocks
or lock waits can happen in it during an index create operation . */
row_mysql_lock_data_dictionary ( trx ) ;
if ( add - > indexed_table ! = prebuilt - > table ) {
ulint error ;
/* We copied the table (new_primary). */
if ( commit ) {
mem_heap_t * heap ;
char * tmp_name ;
heap = mem_heap_create ( 1024 ) ;
/* A new primary key was defined for the table
and there was no error at this point . We can
now rename the old table as a temporary table ,
rename the new temporary table as the old
table and drop the old table . */
tmp_name = innobase_create_temporary_tablename (
heap , ' 2 ' , prebuilt - > table - > name ) ;
error = row_merge_rename_tables (
prebuilt - > table , add - > indexed_table ,
tmp_name , trx ) ;
switch ( error ) {
case DB_TABLESPACE_ALREADY_EXISTS :
case DB_DUPLICATE_KEY :
innobase_convert_tablename ( tmp_name ) ;
my_error ( HA_ERR_TABLE_EXIST , MYF ( 0 ) , tmp_name ) ;
err = HA_ERR_TABLE_EXIST ;
break ;
default :
err = convert_error_code_to_mysql (
error , prebuilt - > table - > flags ,
user_thd ) ;
break ;
}
mem_heap_free ( heap ) ;
}
if ( ! commit | | err ) {
error = row_merge_drop_table ( trx , add - > indexed_table ) ;
trx_commit_for_mysql ( prebuilt - > trx ) ;
} else {
dict_table_t * old_table = prebuilt - > table ;
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 ) ;
}
err = convert_error_code_to_mysql (
error , prebuilt - > table - > flags , user_thd ) ;
} else {
/* We created secondary indexes (!new_primary). */
if ( commit ) {
err = convert_error_code_to_mysql (
row_merge_rename_indexes ( trx , prebuilt - > table ) ,
prebuilt - > table - > flags , user_thd ) ;
}
if ( ! commit | | err ) {
dict_index_t * index ;
dict_index_t * next_index ;
for ( index = dict_table_get_first_index (
prebuilt - > table ) ;
index ; index = next_index ) {
next_index = dict_table_get_next_index ( index ) ;
if ( * index - > name = = TEMP_INDEX_PREFIX ) {
row_merge_drop_index (
index , prebuilt - > table , trx ) ;
}
}
}
}
/* 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 ) {
share - > idx_trans_tbl . index_count = 0 ;
}
trx_commit_for_mysql ( trx ) ;
if ( prebuilt - > trx ) {
trx_commit_for_mysql ( prebuilt - > trx ) ;
}
ut_d ( dict_table_check_for_dup_indexes ( prebuilt - > table , FALSE ) ) ;
row_mysql_unlock_data_dictionary ( trx ) ;
trx_free_for_mysql ( trx ) ;
/* There might be work for utility threads.*/
srv_active_wake_master_thread ( ) ;
delete add ;
DBUG_RETURN ( err ) ;
}
/*******************************************************************/ /**