summaryrefslogtreecommitdiff
path: root/sql/ha_innodb.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/ha_innodb.cc')
-rw-r--r--sql/ha_innodb.cc502
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 */