diff options
Diffstat (limited to 'sql/ha_innodb.cc')
-rw-r--r-- | sql/ha_innodb.cc | 502 |
1 files changed, 193 insertions, 309 deletions
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 287b644b5d3..c4b71113c56 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -76,17 +76,32 @@ bool innodb_skip = 0; uint innobase_init_flags = 0; ulong innobase_cache_size = 0; +/* The default values for the following, type long, start-up parameters +are declared in mysqld.cc: */ + long innobase_mirrored_log_groups, innobase_log_files_in_group, innobase_log_file_size, innobase_log_buffer_size, innobase_buffer_pool_size, innobase_additional_mem_pool_size, innobase_file_io_threads, innobase_lock_wait_timeout, - innobase_thread_concurrency, innobase_force_recovery; - -char *innobase_data_home_dir; -char *innobase_log_group_home_dir, *innobase_log_arch_dir; -char *innobase_unix_file_flush_method; -my_bool innobase_flush_log_at_trx_commit, innobase_log_archive, - innobase_use_native_aio, innobase_fast_shutdown; + innobase_thread_concurrency, innobase_force_recovery; + +/* The default values for the following char* start-up parameters +are determined in innobase_init below: */ + + /* innobase_data_file_path=ibdata:15,idata2:1,... */ + +char* innobase_data_home_dir = NULL; +char* innobase_log_group_home_dir = NULL; +char* innobase_log_arch_dir = NULL; +char* innobase_unix_file_flush_method = NULL; + +/* Below we have boolean-valued start-up parameters, and their default +values */ + +my_bool innobase_flush_log_at_trx_commit = FALSE; +my_bool innobase_log_archive = FALSE; +my_bool innobase_use_native_aio = FALSE; +my_bool innobase_fast_shutdown = TRUE; /* Set default InnoDB size to 64M, to let users use InnoDB without having @@ -338,227 +353,6 @@ ha_innobase::update_thd( } /************************************************************************* -Reads the data files and their sizes from a character string given in -the .cnf file. */ -static -bool -innobase_parse_data_file_paths_and_sizes(void) -/*==========================================*/ - /* out: TRUE if ok, FALSE if parsing - error */ -{ - char* str; - char* endp; - char* path; - ulint size; - ulint i = 0; - - str = internal_innobase_data_file_path; - - /* First calculate the number of data files and check syntax: - path:size[M];path:size[M]... . Note that a Windows path may - contain a drive name and a ':'. */ - - while (*str != '\0') { - path = str; - - while ((*str != ':' && *str != '\0') - || (*str == ':' - && (*(str + 1) == '\\' || *(str + 1) == '/'))) { - str++; - } - - if (*str == '\0') { - return(FALSE); - } - - str++; - - size = strtoul(str, &endp, 10); - - str = endp; - - if ((*str != 'M') && (*str != 'G')) { - size = size / (1024 * 1024); - } else if (*str == 'G') { - size = size * 1024; - str++; - } else { - str++; - } - - if (strlen(str) >= 6 - && *str == 'n' - && *(str + 1) == 'e' - && *(str + 2) == 'w') { - str += 3; - } - - if (strlen(str) >= 3 - && *str == 'r' - && *(str + 1) == 'a' - && *(str + 2) == 'w') { - str += 3; - } - - if (size == 0) { - return(FALSE); - } - - i++; - - if (*str == ';') { - str++; - } else if (*str != '\0') { - - return(FALSE); - } - } - - srv_data_file_names = (char**)ut_malloc(i * sizeof(void*)); - srv_data_file_sizes = (ulint*)ut_malloc(i * sizeof(ulint)); - srv_data_file_is_raw_partition = (ulint*)ut_malloc(i * sizeof(ulint)); - - srv_n_data_files = i; - - /* Then store the actual values to our arrays */ - - str = internal_innobase_data_file_path; - i = 0; - - while (*str != '\0') { - path = str; - - /* Note that we must ignore the ':' in a Windows path */ - - while ((*str != ':' && *str != '\0') - || (*str == ':' - && (*(str + 1) == '\\' || *(str + 1) == '/'))) { - str++; - } - - if (*str == ':') { - /* Make path a null-terminated string */ - *str = '\0'; - str++; - } - - size = strtoul(str, &endp, 10); - - str = endp; - - if ((*str != 'M') && (*str != 'G')) { - size = size / (1024 * 1024); - } else if (*str == 'G') { - size = size * 1024; - str++; - } else { - str++; - } - - srv_data_file_is_raw_partition[i] = 0; - - if (strlen(str) >= 6 - && *str == 'n' - && *(str + 1) == 'e' - && *(str + 2) == 'w') { - str += 3; - srv_data_file_is_raw_partition[i] = SRV_NEW_RAW; - } - - if (strlen(str) >= 3 - && *str == 'r' - && *(str + 1) == 'a' - && *(str + 2) == 'w') { - str += 3; - - if (srv_data_file_is_raw_partition[i] == 0) { - srv_data_file_is_raw_partition[i] = SRV_OLD_RAW; - } - } - - srv_data_file_names[i] = path; - srv_data_file_sizes[i] = size; - - i++; - - if (*str == ';') { - str++; - } - } - - return(TRUE); -} - -/************************************************************************* -Reads log group home directories from a character string given in -the .cnf file. */ -static -bool -innobase_parse_log_group_home_dirs(void) -/*====================================*/ - /* out: TRUE if ok, FALSE if parsing - error */ -{ - char* str; - char* path; - ulint i = 0; - - str = innobase_log_group_home_dir; - - /* First calculate the number of directories and check syntax: - path;path;... */ - - while (*str != '\0') { - path = str; - - while (*str != ';' && *str != '\0') { - str++; - } - - i++; - - if (*str == ';') { - str++; - } else if (*str != '\0') { - - return(FALSE); - } - } - - if (i != (ulint) innobase_mirrored_log_groups) { - - return(FALSE); - } - - srv_log_group_home_dirs = (char**) ut_malloc(i * sizeof(void*)); - - /* Then store the actual values to our array */ - - str = innobase_log_group_home_dir; - i = 0; - - while (*str != '\0') { - path = str; - - while (*str != ';' && *str != '\0') { - str++; - } - - if (*str == ';') { - *str = '\0'; - str++; - } - - srv_log_group_home_dirs[i] = path; - - i++; - } - - return(TRUE); -} - -/************************************************************************* Opens an InnoDB database. */ bool @@ -581,9 +375,9 @@ innobase_init(void) else { /* It's better to use current lib, to keep path's short */ - current_lib[0]=FN_CURLIB; - current_lib[1]=FN_LIBCHAR; - current_lib[2]=0; + current_lib[0] = FN_CURLIB; + current_lib[1] = FN_LIBCHAR; + current_lib[2] = 0; default_path=current_lib; } @@ -605,12 +399,17 @@ innobase_init(void) srv_data_home = (innobase_data_home_dir ? innobase_data_home_dir : default_path); - srv_logs_home = (char*) ""; srv_arch_dir = (innobase_log_arch_dir ? innobase_log_arch_dir : default_path); - ret = innobase_parse_data_file_paths_and_sizes(); - + ret = (bool) + srv_parse_data_file_paths_and_sizes(innobase_data_file_path, + &srv_data_file_names, + &srv_data_file_sizes, + &srv_data_file_is_raw_partition, + &srv_n_data_files, + &srv_auto_extend_last_data_file, + &srv_last_file_size_max); if (ret == FALSE) { sql_print_error("InnoDB: syntax error in innodb_data_file_path"); DBUG_RETURN(TRUE); @@ -618,12 +417,18 @@ innobase_init(void) if (!innobase_log_group_home_dir) innobase_log_group_home_dir= default_path; - ret = innobase_parse_log_group_home_dirs(); - if (ret == FALSE) { - DBUG_RETURN(TRUE); - } + ret = (bool) + srv_parse_log_group_home_dirs(innobase_log_group_home_dir, + &srv_log_group_home_dirs); + + if (ret == FALSE || innobase_mirrored_log_groups != 1) { + fprintf(stderr, + "InnoDB: syntax error in innodb_log_group_home_dir\n" + "InnoDB: or a wrong number of mirrored log groups\n"); + DBUG_RETURN(TRUE); + } srv_unix_file_flush_method_str = (innobase_unix_file_flush_method ? innobase_unix_file_flush_method : (char*)"fdatasync"); @@ -664,10 +469,11 @@ innobase_init(void) if (err != DB_SUCCESS) { - DBUG_RETURN(1); + DBUG_RETURN(1); } + (void) hash_init(&innobase_open_tables,32,0,0, - (hash_get_key) innobase_get_key,0,0); + (hash_get_key) innobase_get_key,0,0); pthread_mutex_init(&innobase_mutex,MY_MUTEX_INIT_FAST); DBUG_RETURN(0); } @@ -1353,33 +1159,43 @@ build_template( clust_index = dict_table_get_first_index_noninline(prebuilt->table); - if (!prebuilt->in_update_remember_pos) { + if (!prebuilt->hint_no_need_to_fetch_extra_cols) { + /* We have a hint that we should at least fetch all + columns in the key, or all columns in the table */ + if (prebuilt->read_just_key) { + /* MySQL has instructed us that it is enough to + fetch the columns in the key */ + fetch_all_in_key = TRUE; } else { /* We are building a temporary table: fetch all - columns */ + columns; the reason is that MySQL may use the + clustered index key to store rows, but the mechanism + we use below to detect required columns does not + reveal that. Actually, it might be enough to + fetch only all in the key also in this case! */ templ_type = ROW_MYSQL_WHOLE_ROW; } } if (prebuilt->select_lock_type == LOCK_X) { - /* TODO: should fix the code in sql_update so that we could do - with fetching only the needed columns */ + /* We always retrieve the whole clustered index record if we + use exclusive row level locks, for example, if the read is + done in an UPDATE statement. */ templ_type = ROW_MYSQL_WHOLE_ROW; } if (templ_type == ROW_MYSQL_REC_FIELDS) { + /* In versions < 3.23.50 we always retrieved the clustered + index record if prebuilt->select_lock_type == LOCK_S, + but there is really not need for that, and in some cases + performance could be seriously degraded because the MySQL + optimizer did not know about our convention! */ - if (prebuilt->select_lock_type != LOCK_NONE) { - /* Let index be the clustered index */ - - index = clust_index; - } else { - index = prebuilt->index; - } + index = prebuilt->index; } else { index = clust_index; } @@ -1475,12 +1291,6 @@ skip_field: (index->table->cols + templ->col_no)->clust_pos; } } - - if (templ_type == ROW_MYSQL_REC_FIELDS - && prebuilt->select_lock_type != LOCK_NONE) { - - prebuilt->need_to_access_clustered = TRUE; - } } /************************************************************************ @@ -1513,7 +1323,9 @@ ha_innobase::write_row( } if (table->next_number_field && record == table->record[0]) { - + /* This is the case where the table has an + auto-increment column */ + /* Fetch the value the user possibly has set in the autoincrement field */ @@ -1597,12 +1409,6 @@ ha_innobase::write_row( } } - /* Set the 'in_update_remember_pos' flag to FALSE to - make sure all columns are fetched in the select done by - update_auto_increment */ - - prebuilt->in_update_remember_pos = FALSE; - update_auto_increment(); if (auto_inc == 0) { @@ -1626,7 +1432,7 @@ ha_innobase::write_row( } /* We have to set sql_stat_start to TRUE because - update_auto_increment has called a select, and + update_auto_increment may have called a select, and has reset that flag; row_insert_for_mysql has to know to set the IX intention lock on the table, something it only does at the start of each statement */ @@ -1866,9 +1672,7 @@ ha_innobase::update_row( /* This is not a delete */ prebuilt->upd_node->is_delete = FALSE; - if (!prebuilt->in_update_remember_pos) { - assert(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW); - } + assert(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW); srv_conc_enter_innodb(prebuilt->trx); @@ -1914,7 +1718,6 @@ ha_innobase::delete_row( /* This is a delete */ prebuilt->upd_node->is_delete = TRUE; - prebuilt->in_update_remember_pos = TRUE; srv_conc_enter_innodb(prebuilt->trx); @@ -2124,19 +1927,20 @@ ha_innobase::change_active_index( active_index = keynr; - if (keynr != MAX_KEY && table->keys > 0) - { + if (keynr != MAX_KEY && table->keys > 0) { key = table->key_info + active_index; - prebuilt->index=dict_table_get_index_noninline(prebuilt->table, key->name); - if (!prebuilt->index) - { - sql_print_error("Innodb could not find key n:o %u with name %s from dict cache for table %s", keynr, key->name, prebuilt->table->name); - return(1); - } + prebuilt->index = dict_table_get_index_noninline( + prebuilt->table, key->name); + } else { + prebuilt->index = dict_table_get_first_index_noninline( + prebuilt->table); + } + + if (!prebuilt->index) { + sql_print_error("Innodb could not find key n:o %u with name %s from dict cache for table %s", keynr, key->name, prebuilt->table->name); + return(1); } - else - prebuilt->index = dict_table_get_first_index_noninline(prebuilt->table); assert(prebuilt->search_tuple != 0); @@ -2647,7 +2451,9 @@ ha_innobase::create( /* Create the table definition in InnoDB */ - if ((error = create_table_def(trx, form, norm_name))) { + error = create_table_def(trx, form, norm_name); + + if (error) { trx_commit_for_mysql(trx); @@ -3222,11 +3028,57 @@ ha_innobase::update_table_comment( pos += sprintf(pos, "InnoDB free: %lu kB", (ulong) innobase_get_free_space()); - /* We assume 150 bytes of space to print info */ + /* We assume 450 - length bytes of space to print info */ + + if (length < 450) { + dict_print_info_on_foreign_keys(FALSE, pos, 450 - length, + prebuilt->table); + } + + return(str); +} + +/*********************************************************************** +Gets the foreign key create info for a table stored in InnoDB. */ + +char* +ha_innobase::get_foreign_key_create_info(void) +/*==========================================*/ + /* out, own: character string in the form which + can be inserted to the CREATE TABLE statement, + MUST be freed with ::free_foreign_key_create_info */ +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt; + char* str; + + if (prebuilt == NULL) { + fprintf(stderr, +"InnoDB: Error: cannot get create info for foreign keys\n"); + + return(NULL); + } + + str = (char*)ut_malloc(10000); - dict_print_info_on_foreign_keys(pos, 500, prebuilt->table); + str[0] = '\0'; + + dict_print_info_on_foreign_keys(TRUE, str, 9000, prebuilt->table); return(str); +} + +/*********************************************************************** +Frees the foreign key create info for a table stored in InnoDB, if it is +non-NULL. */ + +void +ha_innobase::free_foreign_key_create_info( +/*======================================*/ + char* str) /* in, own: create info string to free */ +{ + if (str) { + ut_free(str); + } } /*********************************************************************** @@ -3254,7 +3106,7 @@ ha_innobase::extra( prebuilt->read_just_key = 0; break; case HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE: - prebuilt->in_update_remember_pos = FALSE; + prebuilt->hint_no_need_to_fetch_extra_cols = FALSE; break; case HA_EXTRA_KEYREAD: prebuilt->read_just_key = 1; @@ -3301,7 +3153,7 @@ ha_innobase::external_lock( trx = prebuilt->trx; prebuilt->sql_stat_start = TRUE; - prebuilt->in_update_remember_pos = TRUE; + prebuilt->hint_no_need_to_fetch_extra_cols = TRUE; prebuilt->read_just_key = 0; @@ -3320,6 +3172,16 @@ ha_innobase::external_lock( thd->transaction.all.innodb_active_trans = 1; trx->n_mysql_tables_in_use++; + if (thd->tx_isolation == ISO_SERIALIZABLE + && prebuilt->select_lock_type == LOCK_NONE) { + + /* To get serializable execution we let InnoDB + conceptually add 'LOCK IN SHARE MODE' to all SELECTs + which otherwise would have been consistent reads */ + + prebuilt->select_lock_type = LOCK_S; + } + if (prebuilt->select_lock_type != LOCK_NONE) { trx->mysql_n_tables_locked++; @@ -3427,8 +3289,8 @@ ha_innobase::store_lock( lock_type == TL_READ_NO_INSERT) { /* This is a SELECT ... IN SHARE MODE, or we are doing a complex SQL statement like - INSERT INTO ... SELECT ... and the logical logging - requires the use of a locking read */ + INSERT INTO ... SELECT ... and the logical logging (MySQL + binlog) requires the use of a locking read */ prebuilt->select_lock_type = LOCK_S; } else { @@ -3468,37 +3330,59 @@ ha_innobase::get_auto_increment() /*=============================*/ /* out: the next auto-increment column value */ { - row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; - longlong nr; - int error; + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + longlong nr; + int error; - (void) extra(HA_EXTRA_KEYREAD); - index_init(table->next_number_index); + /* Also SHOW TABLE STATUS calls this function. Previously, when we did + always read the max autoinc key value, setting x-locks, users were + surprised that SHOW TABLE STATUS could end up in a deadlock with + ordinary SQL queries. We avoid these deadlocks if the auto-inc + counter for the table has been initialized by fetching the value + from the table struct in dictionary cache. */ - /* We use an exclusive lock when we read the max key value from the - auto-increment column index. This is because then build_template will - advise InnoDB to fetch all columns. In SHOW TABLE STATUS the query - id of the auto-increment column is not changed, and previously InnoDB - did not fetch it, causing SHOW TABLE STATUS to show wrong values - for the autoinc column. */ + assert(prebuilt->table); + + nr = dict_table_autoinc_read(prebuilt->table); - prebuilt->select_lock_type = LOCK_X; - prebuilt->trx->mysql_n_tables_locked += 1; + if (nr != 0) { - error=index_last(table->record[1]); + return(nr + 1); + } - if (error) { - nr = 1; - } else { - nr = (longlong) table->next_number_field-> - val_int_offset(table->rec_buff_length) + 1; - } + (void) extra(HA_EXTRA_KEYREAD); + index_init(table->next_number_index); + + /* We use an exclusive lock when we read the max key value from the + auto-increment column index. This is because then build_template will + advise InnoDB to fetch all columns. In SHOW TABLE STATUS the query + id of the auto-increment column is not changed, and previously InnoDB + did not fetch it, causing SHOW TABLE STATUS to show wrong values + for the autoinc column. */ + + prebuilt->select_lock_type = LOCK_X; + + /* Play safe and also give in another way the hint to fetch + all columns in the key: */ + + prebuilt->hint_no_need_to_fetch_extra_cols = FALSE; + + prebuilt->trx->mysql_n_tables_locked += 1; + + error = index_last(table->record[1]); + + if (error) { + nr = 1; + } else { + nr = (longlong) table->next_number_field-> + val_int_offset(table->rec_buff_length) + 1; + } - (void) extra(HA_EXTRA_NO_KEYREAD); + (void) extra(HA_EXTRA_NO_KEYREAD); - index_end(); + index_end(); - return(nr); + return(nr); } #endif /* HAVE_INNOBASE_DB */ |