diff options
Diffstat (limited to 'sql/ha_innodb.cc')
-rw-r--r-- | sql/ha_innodb.cc | 255 |
1 files changed, 189 insertions, 66 deletions
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 9aa63cc1435..7787b543f34 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -40,17 +40,12 @@ InnoDB */ #include "ha_innodb.h" -/* We must declare this here because we undef SAFE_MUTEX below */ pthread_mutex_t innobase_mutex; /* Store MySQL definition of 'byte': in Linux it is char while InnoDB uses unsigned char */ typedef byte mysql_byte; -#ifdef SAFE_MUTEX -#undef pthread_mutex_t -#endif - #define INSIDE_HA_INNOBASE_CC /* Include necessary InnoDB headers */ @@ -97,6 +92,8 @@ are determined in innobase_init below: */ char* innobase_data_home_dir = NULL; char* innobase_log_group_home_dir = NULL; char* innobase_log_arch_dir = NULL; +/* The following has a midleading name: starting from 4.0.5 this also +affects Windows */ char* innobase_unix_file_flush_method = NULL; /* Below we have boolean-valued start-up parameters, and their default @@ -253,6 +250,10 @@ convert_error_code_to_mysql( } else if (error == (int) DB_TOO_BIG_RECORD) { return(HA_ERR_TO_BIG_ROW); + + } else if (error == (int) DB_CORRUPTION) { + + return(HA_ERR_CRASHED); } else { return(-1); // Unknown error } @@ -346,7 +347,8 @@ check_trx_exists( trx = trx_allocate_for_mysql(); trx->mysql_thd = thd; - + trx->mysql_query_str = &((*thd).query); + thd->transaction.all.innobase_tid = trx; /* The execution of a single SQL statement is denoted by @@ -713,9 +715,10 @@ innobase_init(void) DBUG_RETURN(TRUE); } - srv_unix_file_flush_method_str = (innobase_unix_file_flush_method ? + + srv_file_flush_method_str = (innobase_unix_file_flush_method ? innobase_unix_file_flush_method : - (char*)"fdatasync"); + NULL); srv_n_log_groups = (ulint) innobase_mirrored_log_groups; srv_n_log_files = (ulint) innobase_log_files_in_group; @@ -725,8 +728,6 @@ innobase_init(void) srv_log_buffer_size = (ulint) innobase_log_buffer_size; srv_flush_log_at_trx_commit = (ulint) innobase_flush_log_at_trx_commit; - srv_use_native_aio = 0; - srv_pool_size = (ulint) innobase_buffer_pool_size; srv_mem_pool_size = (ulint) innobase_additional_mem_pool_size; @@ -750,6 +751,14 @@ innobase_init(void) default_charset_info->sort_order, 256); } + /* Since we in this module access directly the fields of a trx + struct, and due to different headers and flags it might happen that + mutex_t has a different size in this module and in InnoDB + modules, we check at run time that the size is the same in + these compilation modules. */ + + srv_sizeof_trx_t_in_ha_innodb_cc = sizeof(trx_t); + err = innobase_start_or_create_for_mysql(); if (err != DB_SUCCESS) { @@ -1147,15 +1156,15 @@ ha_innobase::open( ib_table = dict_table_get_and_increment_handle_count( norm_name, NULL); if (NULL == ib_table) { - - sql_print_error("InnoDB error:\n\ -Cannot find table %s from the internal data dictionary\n\ -of InnoDB though the .frm file for the table exists. Maybe you\n\ -have deleted and recreated InnoDB data files but have forgotten\n\ -to delete the corresponding .frm files of InnoDB tables, or you\n\ -have moved .frm files to another database?\n\ -Look from section 15.1 of http://www.innodb.com/ibman.html\n\ -how you can resolve the problem.\n", + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB error:\n" +"Cannot find table %s from the internal data dictionary\n" +"of InnoDB though the .frm file for the table exists. Maybe you\n" +"have deleted and recreated InnoDB data files but have forgotten\n" +"to delete the corresponding .frm files of InnoDB tables, or you\n" +"have moved .frm files to another database?\n" +"Look from section 15.1 of http://www.innodb.com/ibman.html\n" +"how you can resolve the problem.\n", norm_name); free_share(share); @@ -1168,7 +1177,10 @@ how you can resolve the problem.\n", ((row_prebuilt_t*)innobase_prebuilt)->mysql_row_len = table->reclength; - primary_key = MAX_KEY; + /* Looks like MySQL-3.23 sometimes has primary key number != 0 */ + + primary_key = table->primary_key; + key_used_on_scan = primary_key; /* Allocate a buffer for a 'row reference'. A row reference is a string of bytes of length ref_length which uniquely specifies @@ -1177,13 +1189,14 @@ how you can resolve the problem.\n", of length ref_length! */ if (!row_table_got_default_clust_index(ib_table)) { + if (primary_key >= MAX_KEY) { + fprintf(stderr, + "InnoDB: Error: table %s has a primary key in InnoDB\n" + "InnoDB: data dictionary, but not in MySQL!\n", name); + } ((row_prebuilt_t*)innobase_prebuilt) ->clust_index_was_generated = FALSE; - - primary_key = 0; - key_used_on_scan = 0; - /* MySQL allocates the buffer for ref. key_info->key_length includes space for all key columns + one byte for each column @@ -1192,8 +1205,14 @@ how you can resolve the problem.\n", based on ref_length. */ - ref_length = table->key_info->key_length; + ref_length = table->key_info[primary_key].key_length; } else { + if (primary_key != MAX_KEY) { + fprintf(stderr, + "InnoDB: Error: table %s has no primary key in InnoDB\n" + "InnoDB: data dictionary, but has one in MySQL!\n", name); + } + ((row_prebuilt_t*)innobase_prebuilt) ->clust_index_was_generated = TRUE; @@ -1501,7 +1520,8 @@ ha_innobase::store_key_val_for_row( are equal */ bzero(buff, (ref_length- (uint) (buff - buff_start))); - DBUG_RETURN(ref_length); + + DBUG_RETURN((uint)(buff - buff_start)); } /****************************************************************** @@ -1679,6 +1699,7 @@ ha_innobase::write_row( longlong dummy; ibool incremented_auto_inc_for_stat = FALSE; ibool incremented_auto_inc_counter = FALSE; + ibool skip_auto_inc_decr; DBUG_ENTER("ha_innobase::write_row"); @@ -1843,13 +1864,31 @@ ha_innobase::write_row( if (error != DB_SUCCESS) { /* If the insert did not succeed we restore the value of the auto-inc counter we used; note that this behavior was - introduced only in version 4.0.4 */ + introduced only in version 4.0.4. + NOTE that a REPLACE command handles a duplicate key error + itself, and we must not decrement the autoinc counter + if we are performing a REPLACE statement. + NOTE 2: if there was an error, for example a deadlock, + which caused InnoDB to roll back the whole transaction + already in the call of row_insert_for_mysql(), we may no + longer have the AUTO-INC lock, and cannot decrement + the counter here. */ + + skip_auto_inc_decr = FALSE; + + if (error == DB_DUPLICATE_KEY) { + ut_a(user_thd->query); + dict_accept(user_thd->query, "REPLACE", + &skip_auto_inc_decr); + } - if (incremented_auto_inc_counter) { + if (!skip_auto_inc_decr && incremented_auto_inc_counter + && prebuilt->trx->auto_inc_lock) { dict_table_autoinc_decrement(prebuilt->table); } - if (incremented_auto_inc_for_stat) { + if (!skip_auto_inc_decr && incremented_auto_inc_for_stat + && prebuilt->trx->auto_inc_lock) { auto_inc_counter_for_this_stat--; } } @@ -2181,8 +2220,16 @@ convert_search_mode_to_innobase( case HA_READ_AFTER_KEY: return(PAGE_CUR_G); case HA_READ_BEFORE_KEY: return(PAGE_CUR_L); case HA_READ_PREFIX: return(PAGE_CUR_GE); - case HA_READ_PREFIX_LAST: return(PAGE_CUR_LE); - /* HA_READ_PREFIX_LAST does not yet work in InnoDB! */ + case HA_READ_PREFIX_LAST: + /* ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: Warning: Using HA_READ_PREFIX_LAST\n"); */ + return(PAGE_CUR_LE); + + /* InnoDB does not yet support ..PREFIX_LAST! + We have to add a new search flag + PAGE_CUR_LE_OR_PREFIX to InnoDB. */ + /* the above PREFIX flags mean that the last field in the key value may just be a prefix of the complete fixed length field */ @@ -2751,7 +2798,11 @@ ha_innobase::position( that len is always fixed for this table. The following assertion checks this. */ - ut_a(len == ref_length); + if (len != ref_length) { + fprintf(stderr, + "InnoDB: Error: stored ref len is %lu, but table ref len is %lu\n", + (ulint)len, (ulint)ref_length); + } } @@ -2935,16 +2986,21 @@ ha_innobase::create( trx->check_unique_secondary = FALSE; } + if (lower_case_table_names) { + srv_lower_case_table_names = TRUE; + } else { + srv_lower_case_table_names = FALSE; + } fn_format(name2, name, "", "",2); // Remove the .frm extension normalize_table_name(norm_name, name2); - /* Latch the InnoDB data dictionary exclusive so that no deadlocks + /* Latch the InnoDB data dictionary exclusively so that no deadlocks or lock waits can happen in it during a table create operation. - (Drop table etc. do this latching in row0mysql.c.) */ + Drop table etc. do this latching in row0mysql.c. */ - row_mysql_lock_data_dictionary(); + row_mysql_lock_data_dictionary(trx); /* Create the table definition in InnoDB */ @@ -2953,7 +3009,7 @@ ha_innobase::create( if (error) { innobase_commit_low(trx); - row_mysql_unlock_data_dictionary(); + row_mysql_unlock_data_dictionary(trx); trx_free_for_mysql(trx); @@ -2983,7 +3039,7 @@ ha_innobase::create( if (error) { innobase_commit_low(trx); - row_mysql_unlock_data_dictionary(); + row_mysql_unlock_data_dictionary(trx); trx_free_for_mysql(trx); @@ -2998,7 +3054,7 @@ ha_innobase::create( (uint) primary_key_no))) { innobase_commit_low(trx); - row_mysql_unlock_data_dictionary(); + row_mysql_unlock_data_dictionary(trx); trx_free_for_mysql(trx); @@ -3014,7 +3070,7 @@ ha_innobase::create( innobase_commit_low(trx); - row_mysql_unlock_data_dictionary(); + row_mysql_unlock_data_dictionary(trx); trx_free_for_mysql(trx); @@ -3023,24 +3079,27 @@ ha_innobase::create( } } - error = row_table_add_foreign_constraints(trx, - create_info->create_statement, norm_name); + if (current_thd->query != NULL) { + + error = row_table_add_foreign_constraints(trx, + current_thd->query, norm_name); - error = convert_error_code_to_mysql(error, NULL); + error = convert_error_code_to_mysql(error, NULL); - if (error) { - innobase_commit_low(trx); + if (error) { + innobase_commit_low(trx); - row_mysql_unlock_data_dictionary(); + row_mysql_unlock_data_dictionary(trx); - trx_free_for_mysql(trx); + trx_free_for_mysql(trx); - DBUG_RETURN(error); + DBUG_RETURN(error); + } } innobase_commit_low(trx); - row_mysql_unlock_data_dictionary(); + row_mysql_unlock_data_dictionary(trx); /* Flush the log to reduce probability that the .frm files and the InnoDB data dictionary get out-of-sync if the user runs @@ -3082,6 +3141,12 @@ ha_innobase::delete_table( DBUG_ENTER("ha_innobase::delete_table"); + if (lower_case_table_names) { + srv_lower_case_table_names = TRUE; + } else { + srv_lower_case_table_names = FALSE; + } + trx = trx_allocate_for_mysql(); name_len = strlen(name); @@ -3095,7 +3160,7 @@ ha_innobase::delete_table( /* Drop the table in InnoDB */ - error = row_drop_table_for_mysql(norm_name, trx, FALSE); + error = row_drop_table_for_mysql(norm_name, trx); /* Flush the log to reduce probability that the .frm files and the InnoDB data dictionary get out-of-sync if the user runs @@ -3192,6 +3257,12 @@ ha_innobase::rename_table( DBUG_ENTER("ha_innobase::rename_table"); + if (lower_case_table_names) { + srv_lower_case_table_names = TRUE; + } else { + srv_lower_case_table_names = FALSE; + } + trx = trx_allocate_for_mysql(); name_len1 = strlen(from); @@ -3318,7 +3389,7 @@ ha_innobase::estimate_number_of_rows(void) row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; dict_index_t* index; ulonglong estimate; - ulonglong data_file_length; + ulonglong local_data_file_length; /* Warning: since it is not sure that MySQL calls external_lock before calling this function, the trx field in prebuilt can be @@ -3328,7 +3399,7 @@ ha_innobase::estimate_number_of_rows(void) index = dict_table_get_first_index_noninline(prebuilt->table); - data_file_length = ((ulonglong) index->stat_n_leaf_pages) + local_data_file_length = ((ulonglong) index->stat_n_leaf_pages) * UNIV_PAGE_SIZE; /* Calculate a minimum length for a clustered index record and from @@ -3337,7 +3408,7 @@ ha_innobase::estimate_number_of_rows(void) by a threshold factor, we must add a safety factor 2 in front of the formula below. */ - estimate = 2 * data_file_length / dict_index_calc_min_rec_len(index); + estimate = 2 * local_data_file_length / dict_index_calc_min_rec_len(index); DBUG_RETURN((ha_rows) estimate); } @@ -3374,12 +3445,21 @@ ha_innobase::info( row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; dict_table_t* ib_table; dict_index_t* index; - ulong rec_per_key; + ha_rows rec_per_key; ulong j; ulong i; DBUG_ENTER("info"); + /* If we are forcing recovery at a high level, we will suppress + statistics calculation on tables, because that may crash the + server if an index is badly corrupted. */ + + if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) { + + return; + } + /* Warning: since it is not sure that MySQL calls external_lock before calling this function, the trx field in prebuilt can be obsolete! */ @@ -3426,7 +3506,7 @@ ha_innobase::info( rec_per_key = records; } else { - rec_per_key = (ulong)(records / + rec_per_key = (ha_rows)(records / index->stat_n_diff_key_vals[j + 1]); } @@ -3441,8 +3521,9 @@ ha_innobase::info( rec_per_key = 1; } - table->key_info[i].rec_per_key[j] - = rec_per_key; + table->key_info[i].rec_per_key[j]= + rec_per_key >= ~(ulong) 0 ? ~(ulong) 0 : + rec_per_key; } index = dict_table_get_next_index_noninline(index); @@ -3641,7 +3722,6 @@ ha_innobase::reset(void) return(0); } - /********************************************************************** When we create a temporary table inside MySQL LOCK TABLES, MySQL will not call external_lock for the temporary table when it uses it. Instead, @@ -3663,12 +3743,20 @@ ha_innobase::start_stmt( innobase_release_stat_resources(trx); trx_mark_sql_stat_end(trx); + if (trx->isolation_level <= TRX_ISO_READ_COMMITTED + && trx->read_view) { + /* At low transaction isolation levels we let + each consistent read set its own snapshot */ + + read_view_close_for_mysql(trx); + } + auto_inc_counter_for_this_stat = 0; prebuilt->sql_stat_start = TRUE; prebuilt->hint_no_need_to_fetch_extra_cols = TRUE; prebuilt->read_just_key = 0; - if (prebuilt->select_lock_type == LOCK_NONE) { + if (!prebuilt->mysql_has_locked) { /* This handle is for a temporary table created inside this same LOCK TABLES; since MySQL does NOT call external_lock in this case, we must use x-row locks inside InnoDB to be @@ -3683,6 +3771,24 @@ ha_innobase::start_stmt( } /********************************************************************** +Maps a MySQL trx isolation level code to the InnoDB isolation level code */ +inline +ulint +innobase_map_isolation_level( +/*=========================*/ + /* out: InnoDB isolation level */ + enum_tx_isolation iso) /* in: MySQL isolation level code */ +{ + switch(iso) { + case ISO_READ_COMMITTED: return(TRX_ISO_READ_COMMITTED); + case ISO_REPEATABLE_READ: return(TRX_ISO_REPEATABLE_READ); + case ISO_SERIALIZABLE: return(TRX_ISO_SERIALIZABLE); + case ISO_READ_UNCOMMITTED: return(TRX_ISO_READ_UNCOMMITTED); + default: ut_a(0); return(0); + } +} + +/********************************************************************** As MySQL will execute an external lock for every new table it uses when it starts to process an SQL statement (an exception is when MySQL calls start_stmt for the handle) we can use this function to store the pointer to @@ -3703,6 +3809,7 @@ ha_innobase::external_lock( trx_t* trx; DBUG_ENTER("ha_innobase::external_lock"); + DBUG_PRINT("enter",("lock_type: %d", lock_type)); update_thd(thd); @@ -3727,8 +3834,15 @@ ha_innobase::external_lock( thd->transaction.all.innodb_active_trans = 1; trx->n_mysql_tables_in_use++; + prebuilt->mysql_has_locked = TRUE; - if (thd->variables.tx_isolation == ISO_SERIALIZABLE + if (thd->variables.tx_isolation != ISO_REPEATABLE_READ) { + trx->isolation_level = innobase_map_isolation_level( + (enum_tx_isolation) + thd->variables.tx_isolation); + } + + if (trx->isolation_level == TRX_ISO_SERIALIZABLE && prebuilt->select_lock_type == LOCK_NONE) { /* To get serializable execution we let InnoDB @@ -3744,6 +3858,7 @@ ha_innobase::external_lock( } } else { trx->n_mysql_tables_in_use--; + prebuilt->mysql_has_locked = FALSE; auto_inc_counter_for_this_stat = 0; if (trx->n_mysql_tables_in_use == 0) { @@ -3755,6 +3870,15 @@ ha_innobase::external_lock( innobase_release_stat_resources(trx); + if (trx->isolation_level <= TRX_ISO_READ_COMMITTED + && trx->read_view) { + + /* At low transaction isolation levels we let + each consistent read set its own snapshot */ + + read_view_close_for_mysql(trx); + } + if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) { @@ -3779,14 +3903,13 @@ innodb_show_status( char* buf; DBUG_ENTER("innodb_show_status"); - + if (innodb_skip) { - - fprintf(stderr, - "Cannot call SHOW INNODB STATUS because skip-innodb is defined\n"); - - DBUG_RETURN(-1); - } + my_message(ER_NOT_SUPPORTED_YET, + "Cannot call SHOW INNODB STATUS because skip-innodb is defined", + MYF(0)); + DBUG_RETURN(-1); + } /* We let the InnoDB Monitor to output at most 100 kB of text, add a safety margin of 10 kB for buffer overruns */ |