diff options
author | unknown <monty@mysql.com> | 2004-02-11 00:06:46 +0100 |
---|---|---|
committer | unknown <monty@mysql.com> | 2004-02-11 00:06:46 +0100 |
commit | 5b2c3126277a8eedab5bcc8a9b0ce7386ccc3dbe (patch) | |
tree | 927515d304bd661aebdb0e534418caf49d322ada /innobase | |
parent | dc792940232f3265e21905cb939853f4db5ebd16 (diff) | |
parent | 65ec6a41b65f26552481be24ac8947c83eeea198 (diff) | |
download | mariadb-git-5b2c3126277a8eedab5bcc8a9b0ce7386ccc3dbe.tar.gz |
Merge with 4.0.18
BitKeeper/etc/ignore:
auto-union
BitKeeper/etc/logging_ok:
auto-union
mysql-test/r/ctype_tis620.result-old:
Merge rename: mysql-test/r/ctype_tis620.result -> mysql-test/r/ctype_tis620.result-old
BUILD/compile-pentium-max:
Auto merged
BitKeeper/etc/config:
Auto merged
Build-tools/Bootstrap:
Auto merged
Build-tools/Do-compile:
Auto merged
configure.in:
Auto merged
mysql-test/t/ctype_tis620.test-old:
Merge rename: mysql-test/t/ctype_tis620.test -> mysql-test/t/ctype_tis620.test-old
Docs/Makefile.am:
Auto merged
client/mysqldump.c:
Auto merged
client/mysqltest.c:
Auto merged
include/my_global.h:
Auto merged
include/my_pthread.h:
Auto merged
include/my_sys.h:
Auto merged
include/myisam.h:
Auto merged
innobase/btr/btr0cur.c:
Auto merged
innobase/ibuf/ibuf0ibuf.c:
Auto merged
innobase/include/dict0dict.h:
Auto merged
innobase/include/srv0srv.h:
Auto merged
innobase/include/ut0mem.h:
Auto merged
innobase/log/log0log.c:
Auto merged
innobase/row/row0ins.c:
Auto merged
innobase/row/row0sel.c:
Auto merged
innobase/srv/srv0start.c:
Auto merged
innobase/ut/ut0mem.c:
Auto merged
myisam/mi_check.c:
Auto merged
myisam/mi_dynrec.c:
Auto merged
myisam/mi_key.c:
Auto merged
myisam/myisam_ftdump.c:
Auto merged
myisam/myisamdef.h:
Auto merged
mysql-test/mysql-test-run.sh:
Auto merged
mysql-test/r/alter_table.result:
Auto merged
mysql-test/r/bdb.result:
Auto merged
mysql-test/r/bigint.result:
Auto merged
mysql-test/r/fulltext.result:
Auto merged
Diffstat (limited to 'innobase')
-rw-r--r-- | innobase/btr/btr0cur.c | 5 | ||||
-rw-r--r-- | innobase/buf/buf0lru.c | 18 | ||||
-rw-r--r-- | innobase/dict/dict0crea.c | 79 | ||||
-rw-r--r-- | innobase/dict/dict0dict.c | 610 | ||||
-rw-r--r-- | innobase/ibuf/ibuf0ibuf.c | 8 | ||||
-rw-r--r-- | innobase/include/data0data.h | 8 | ||||
-rw-r--r-- | innobase/include/data0data.ic | 25 | ||||
-rw-r--r-- | innobase/include/dict0crea.h | 15 | ||||
-rw-r--r-- | innobase/include/dict0dict.h | 17 | ||||
-rw-r--r-- | innobase/include/ut0mem.h | 9 | ||||
-rw-r--r-- | innobase/log/log0log.c | 13 | ||||
-rw-r--r-- | innobase/row/row0ins.c | 16 | ||||
-rw-r--r-- | innobase/row/row0mysql.c | 119 | ||||
-rw-r--r-- | innobase/row/row0sel.c | 11 | ||||
-rw-r--r-- | innobase/srv/srv0start.c | 25 | ||||
-rw-r--r-- | innobase/sync/sync0sync.c | 2 | ||||
-rw-r--r-- | innobase/trx/trx0sys.c | 64 | ||||
-rw-r--r-- | innobase/ut/ut0mem.c | 24 |
18 files changed, 830 insertions, 238 deletions
diff --git a/innobase/btr/btr0cur.c b/innobase/btr/btr0cur.c index b2bfdbec4e4..ab2e2697359 100644 --- a/innobase/btr/btr0cur.c +++ b/innobase/btr/btr0cur.c @@ -2709,7 +2709,7 @@ btr_estimate_number_of_different_key_vals( ulint n_cols; ulint matched_fields; ulint matched_bytes; - ulint* n_diff; + ib_longlong* n_diff; ulint not_empty_flag = 0; ulint total_external_size = 0; ulint i; @@ -2781,7 +2781,8 @@ btr_estimate_number_of_different_key_vals( for (j = 0; j <= n_cols; j++) { index->stat_n_diff_key_vals[j] = - (n_diff[j] * index->stat_n_leaf_pages + (n_diff[j] + * (ib_longlong)index->stat_n_leaf_pages + BTR_KEY_VAL_ESTIMATE_N_PAGES - 1 + total_external_size + not_empty_flag) diff --git a/innobase/buf/buf0lru.c b/innobase/buf/buf0lru.c index 23f399503c8..db876c416cc 100644 --- a/innobase/buf/buf0lru.c +++ b/innobase/buf/buf0lru.c @@ -299,7 +299,7 @@ buf_LRU_get_free_block(void) buf_block_t* block = NULL; ibool freed; ulint n_iterations = 1; - ibool mon_value_was = 0; /* remove bug */ + ibool mon_value_was = FALSE; ibool started_monitor = FALSE; loop: mutex_enter(&(buf_pool->mutex)); @@ -318,12 +318,14 @@ loop: } else if (!recv_recovery_on && UT_LIST_GET_LEN(buf_pool->free) + UT_LIST_GET_LEN(buf_pool->LRU) < buf_pool->max_size / 5) { + if (!srv_print_innodb_monitor) { - /* Over 80 % of the buffer pool is occupied by lock heaps - or the adaptive hash index. This may be a memory leak! */ + /* Over 80 % of the buffer pool is occupied by lock + heaps or the adaptive hash index. This may be a memory + leak! */ - ut_print_timestamp(stderr); - fprintf(stderr, + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: WARNING: over 4 / 5 of the buffer pool is occupied by\n" "InnoDB: lock heaps or the adaptive hash index! Check that your\n" "InnoDB: transactions do not set too many row locks.\n" @@ -333,8 +335,9 @@ loop: "InnoDB: lock heap and hash index sizes.\n", (ulong) (buf_pool->curr_size / (1024 * 1024 / UNIV_PAGE_SIZE))); - srv_print_innodb_monitor = TRUE; - + srv_print_innodb_monitor = TRUE; + os_event_set(srv_lock_timeout_thread_event); + } } else if (!recv_recovery_on && UT_LIST_GET_LEN(buf_pool->free) + UT_LIST_GET_LEN(buf_pool->LRU) < buf_pool->max_size / 4) { @@ -423,6 +426,7 @@ loop: mon_value_was = srv_print_innodb_monitor; started_monitor = TRUE; srv_print_innodb_monitor = TRUE; + os_event_set(srv_lock_timeout_thread_event); } /* No free block was found: try to flush the LRU list */ diff --git a/innobase/dict/dict0crea.c b/innobase/dict/dict0crea.c index 038e4803441..d6b1b7261ad 100644 --- a/innobase/dict/dict0crea.c +++ b/innobase/dict/dict0crea.c @@ -1210,23 +1210,36 @@ dict_create_or_check_foreign_constraint_tables(void) } /************************************************************************ -Adds foreign key definitions to data dictionary tables in the database. */ +Adds foreign key definitions to data dictionary tables in the database. We +look at table->foreign_list, and also generate names to constraints that were +not named by the user. A generated constraint has a name of the format +databasename/tablename_ibfk_<number>, where the numbers start from 1, and are +given locally for this table, that is, the number is not global, as in the +old format constraints < 4.0.18 it used to be. */ ulint dict_create_add_foreigns_to_dictionary( /*===================================*/ /* out: error code or DB_SUCCESS */ + ulint start_id,/* in: if we are actually doing ALTER TABLE + ADD CONSTRAINT, we want to generate constraint + numbers which are bigger than in the table so + far; we number the constraints from + start_id + 1 up; start_id should be set to 0 if + we are creating a new table, or if the table + so far has no constraints for which the name + was generated here */ dict_table_t* table, /* in: table */ trx_t* trx) /* in: transaction */ { dict_foreign_t* foreign; que_thr_t* thr; que_t* graph; - dulint id; + ulint number = start_id + 1; ulint len; ulint error; + char* ebuf = dict_foreign_err_buf; ulint i; - char buf2[50]; char buf[10000]; ut_ad(mutex_own(&(dict_sys->mutex))); @@ -1254,18 +1267,18 @@ loop: "PROCEDURE ADD_FOREIGN_DEFS_PROC () IS\n" "BEGIN\n"); - /* We allocate the new id from the sequence of table id's */ - id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID); + if (foreign->id == NULL) { + /* Generate a new constraint id */ + foreign->id = mem_heap_alloc(foreign->heap, + ut_strlen(table->name) + + 20); + sprintf(foreign->id, "%s_ibfk_%lu", table->name, (ulong) number); + number++; + } - sprintf(buf2, "%lu_%lu", (ulong) ut_dulint_get_high(id), - (ulong) ut_dulint_get_low(id)); - foreign->id = mem_heap_alloc(foreign->heap, ut_strlen(buf2) + 1); - ut_memcpy(foreign->id, buf2, ut_strlen(buf2) + 1); - len += sprintf(buf + len, - "INSERT INTO SYS_FOREIGN VALUES('%lu_%lu', '%s', '%s', %lu);\n", - (ulong) ut_dulint_get_high(id), - (ulong) ut_dulint_get_low(id), + "INSERT INTO SYS_FOREIGN VALUES('%s', '%s', '%s', %lu);\n", + foreign->id, table->name, foreign->referenced_table_name, (ulong) (foreign->n_fields @@ -1274,9 +1287,8 @@ loop: for (i = 0; i < foreign->n_fields; i++) { len += sprintf(buf + len, - "INSERT INTO SYS_FOREIGN_COLS VALUES('%lu_%lu', %lu, '%s', '%s');\n", - (ulong) ut_dulint_get_high(id), - (ulong) ut_dulint_get_low(id), + "INSERT INTO SYS_FOREIGN_COLS VALUES('%s', %lu, '%s', '%s');\n", + foreign->id, (ulong) i, foreign->foreign_col_names[i], foreign->referenced_col_names[i]); @@ -1301,29 +1313,30 @@ loop: que_graph_free(graph); + if (error == DB_DUPLICATE_KEY) { + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(dict_foreign_err_buf); + sprintf(ebuf + strlen(ebuf), +" Error in foreign key constraint creation for table %.500s.\n" +"A foreign key constraint of name %.500s\n" +"already exists (note that internally InnoDB adds 'databasename/'\n" +"in front of the user-defined constraint name).\n", table->name, foreign->id); + + ut_a(strlen(ebuf) < DICT_FOREIGN_ERR_BUF_LEN); + + mutex_exit(&dict_foreign_err_mutex); + + return(error); + } + if (error != DB_SUCCESS) { fprintf(stderr, "InnoDB: Foreign key constraint creation failed:\n" "InnoDB: internal error number %lu\n", (ulong) error); - if (error == DB_DUPLICATE_KEY) { - fprintf(stderr, - "InnoDB: Duplicate key error in system table %s index %s\n", - ((dict_index_t*)trx->error_info)->table_name, - ((dict_index_t*)trx->error_info)->name); - - fprintf(stderr, "%s\n", buf); - - fprintf(stderr, - "InnoDB: Maybe the internal data dictionary of InnoDB is\n" - "InnoDB: out-of-sync from the .frm files of your tables.\n" - "InnoDB: See section 15.1 Troubleshooting data dictionary operations\n" - "InnoDB: at http://www.innodb.com/ibman.html\n"); - } - mutex_enter(&dict_foreign_err_mutex); - ut_sprintf_timestamp(buf); - sprintf(buf + strlen(buf), + ut_sprintf_timestamp(ebuf); + sprintf(ebuf + strlen(ebuf), " Internal error in foreign key constraint creation for table %.500s.\n" "See the MySQL .err log in the datadir for more information.\n", table->name); mutex_exit(&dict_foreign_err_mutex); diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c index da5ffaecf41..13ed5bd9af1 100644 --- a/innobase/dict/dict0dict.c +++ b/innobase/dict/dict0dict.c @@ -52,7 +52,7 @@ rw_lock_t dict_operation_lock; /* table create, drop, etc. reserve hash table fixed size in bytes */ #define DICT_POOL_PER_VARYING 4 /* buffer pool max size per data dictionary varying size in bytes */ - + /************************************************************************** Adds a column to the data dictionary hash table. */ static @@ -236,6 +236,29 @@ dict_remove_db_name( return(NULL); } + +/************************************************************************ +Get the database name length in a table name. */ + +ulint +dict_get_db_name_len( +/*=================*/ + /* out: database name length */ + char* name) /* in: table name in the form dbname '/' tablename */ +{ + ulint i; + + for (i = 0; i < 100000 ; i++) { + if (name[i] == '/') { + + return(i); + } + } + + ut_a(0); + + return(0); +} /************************************************************************ Reserves the dictionary system mutex for MySQL. */ @@ -870,6 +893,7 @@ dict_table_rename_in_cache( ulint fold; ulint old_size; char* name_buf; + char* old_name; ibool success; ulint i; @@ -916,6 +940,9 @@ dict_table_rename_in_cache( /* Remove table from the hash tables of tables */ HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash, ut_fold_string(table->name), table); + old_name = mem_heap_alloc(table->heap, ut_strlen(table->name) + 1); + + ut_strcpy(old_name, table->name); name_buf = mem_heap_alloc(table->heap, ut_strlen(new_name) + 1); @@ -972,7 +999,9 @@ dict_table_rename_in_cache( return(TRUE); } - /* Update the table name fields in foreign constraints */ + /* Update the table name fields in foreign constraints, and update also + the constraint id of new format >= 4.0.18 constraints. Note that at + this point we have already changed table->name to the new name. */ foreign = UT_LIST_GET_FIRST(table->foreign_list); @@ -981,14 +1010,68 @@ dict_table_rename_in_cache( ut_strlen(table->name)) { /* Allocate a longer name buffer; TODO: store buf len to save memory */ + foreign->foreign_table_name = mem_heap_alloc( foreign->heap, ut_strlen(table->name) + 1); } - ut_memcpy(foreign->foreign_table_name, table->name, - ut_strlen(table->name) + 1); - foreign->foreign_table_name[ut_strlen(table->name)] = '\0'; + sprintf(foreign->foreign_table_name, "%s", table->name); + + if (ut_str_contains(foreign->id, '/')) { + ulint db_len; + char old_id[2000]; + + /* This is a >= 4.0.18 format id */ + + ut_a(ut_strlen(foreign->id) < 1999); + + ut_strcpy(old_id, foreign->id); + + if (ut_strlen(foreign->id) > ut_strlen(old_name) + + ut_strlen("_ibfk_") + && 0 == ut_memcmp(foreign->id, old_name, + ut_strlen(old_name)) + && 0 == ut_memcmp( + foreign->id + ut_strlen(old_name), + (char*)"_ibfk_", ut_strlen("_ibfk_"))) { + + /* This is a generated >= 4.0.18 format id */ + + if (ut_strlen(table->name) + > ut_strlen(old_name)) { + foreign->id = mem_heap_alloc( + foreign->heap, + ut_strlen(table->name) + + ut_strlen(old_id) + 1); + } + + /* Replace the prefix 'databasename/tablename' + with the new names */ + sprintf(foreign->id, "%s%s", table->name, + old_id + ut_strlen(old_name)); + } else { + /* This is a >= 4.0.18 format id where the user + gave the id name */ + db_len = dict_get_db_name_len(table->name) + 1; + + if (dict_get_db_name_len(table->name) + > dict_get_db_name_len(foreign->id)) { + + foreign->id = mem_heap_alloc( + foreign->heap, + db_len + ut_strlen(old_id) + 1); + } + + /* Replace the database prefix in id with the + one from table->name */ + + ut_memcpy(foreign->id, table->name, db_len); + + sprintf(foreign->id + db_len, "%s", + dict_remove_db_name(old_id)); + } + } foreign = UT_LIST_GET_NEXT(foreign_list, foreign); } @@ -1000,14 +1083,13 @@ dict_table_rename_in_cache( ut_strlen(table->name)) { /* Allocate a longer name buffer; TODO: store buf len to save memory */ + foreign->referenced_table_name = mem_heap_alloc( foreign->heap, ut_strlen(table->name) + 1); } - ut_memcpy(foreign->referenced_table_name, table->name, - ut_strlen(table->name) + 1); - foreign->referenced_table_name[ut_strlen(table->name)] = '\0'; + sprintf(foreign->referenced_table_name, "%s", table->name); foreign = UT_LIST_GET_NEXT(referenced_list, foreign); } @@ -1805,6 +1887,24 @@ dict_index_build_internal_non_clust( /*====================== FOREIGN KEY PROCESSING ========================*/ /************************************************************************* +Checks if a table is referenced by foreign keys. */ + +ibool +dict_table_referenced_by_foreign_key( +/*=================================*/ + /* out: TRUE if table is referenced by a + foreign key */ + dict_table_t* table) /* in: InnoDB table */ +{ + if (UT_LIST_GET_LEN(table->referenced_list) > 0) { + + return(TRUE); + } + + return(FALSE); +} + +/************************************************************************* Frees a foreign key struct. */ static void @@ -2083,7 +2183,7 @@ static char* dict_scan_to( /*=========*/ - + /* out: scanned up to this */ char* ptr, /* in: scan from */ const char *string) /* in: look for this */ { @@ -2150,6 +2250,68 @@ dict_accept( } /************************************************************************* +Scans an id. For the lexical definition of an 'id', see the code below. +Strips backquotes or double quotes from around the id. */ +static +char* +dict_scan_id( +/*=========*/ + /* out: scanned to */ + char* ptr, /* in: scanned to */ + char** start, /* out: start of the id; NULL if no id was + scannable */ + ulint* len, /* out: length of the id */ + ibool accept_also_dot)/* in: TRUE if also a dot can appear in a + non-quoted id; in a quoted id it can appear + always */ +{ + char quote = '\0'; + + *start = NULL; + + while (isspace(*ptr)) { + ptr++; + } + + if (*ptr == '\0') { + + return(ptr); + } + + if (*ptr == '`' || *ptr == '"') { + quote = *ptr++; + } + + *start = ptr; + + if (quote) { + while (*ptr != quote && *ptr != '\0') { + ptr++; + } + } else { + while (!isspace(*ptr) && *ptr != '(' && *ptr != ')' + && (accept_also_dot || *ptr != '.') + && *ptr != ',' && *ptr != '\0') { + + ptr++; + } + } + + *len = (ulint) (ptr - *start); + + if (quote) { + if (*ptr == quote) { + ptr++; + } else { + /* Syntax error */ + *start = NULL; + } + } + + return(ptr); +} + +/************************************************************************* Tries to scan a column name. */ static char* @@ -2165,46 +2327,29 @@ dict_scan_col( ulint* column_name_len)/* out: column name length */ { dict_col_t* col; - char* old_ptr; ulint i; *success = FALSE; - while (isspace(*ptr)) { - ptr++; - } - - if (*ptr == '\0') { - - return(ptr); - } - - if (*ptr == '`') { - ptr++; - } + ptr = dict_scan_id(ptr, column_name, column_name_len, TRUE); - old_ptr = ptr; - - while (!isspace(*ptr) && *ptr != ',' && *ptr != ')' && *ptr != '`' - && *ptr != '\0') { + if (column_name == NULL) { - ptr++; + return(ptr); /* Syntax error */ } - *column_name_len = (ulint)(ptr - old_ptr); - if (table == NULL) { *success = TRUE; *column = NULL; - *column_name = old_ptr; } else { for (i = 0; i < dict_table_get_n_cols(table); i++) { col = dict_table_get_nth_col(table, i); - if (ut_strlen(col->name) == (ulint)(ptr - old_ptr) - && 0 == ut_cmp_in_lower_case(col->name, old_ptr, - (ulint)(ptr - old_ptr))) { + if (ut_strlen(col->name) == *column_name_len + && 0 == ut_cmp_in_lower_case(col->name, + *column_name, + *column_name_len)) { /* Found */ *success = TRUE; @@ -2216,10 +2361,6 @@ dict_scan_col( } } - if (*ptr == '`') { - ptr++; - } - return(ptr); } @@ -2238,144 +2379,112 @@ dict_scan_table_name( the referenced table name; must be at least 2500 bytes */ { - char* dot_ptr = NULL; - char* old_ptr; + char* database_name = NULL; + ulint database_name_len = 999999999; /* init to a dummy value to + suppress a compiler warning */ + char* table_name = NULL; + ulint table_name_len; + char* scanned_id; + ulint scanned_id_len; ulint i; *success = FALSE; *table = NULL; + + ptr = dict_scan_id(ptr, &scanned_id, &scanned_id_len, FALSE); - while (isspace(*ptr)) { - ptr++; + if (scanned_id == NULL) { + + return(ptr); /* Syntax error */ } - if (*ptr == '\0') { - - return(ptr); - } + if (*ptr == '.') { + /* We scanned the database name; scan also the table name */ - if (*ptr == '`') { ptr++; - } - old_ptr = ptr; - - while (!isspace(*ptr) && *ptr != '(' && *ptr != '`' && *ptr != '\0') { - if (*ptr == '.') { - dot_ptr = ptr; - } + database_name = scanned_id; + database_name_len = scanned_id_len; - ptr++; - } + ptr = dict_scan_id(ptr, &table_name, &table_name_len, FALSE); - if (ptr - old_ptr > 2000) { - return(old_ptr); - } - - if (dot_ptr == NULL) { - /* Copy the database name from 'name' to the start */ - for (i = 0;; i++) { - second_table_name[i] = name[i]; - if (name[i] == '/') { - i++; - break; - } - } -#ifdef __WIN__ - ut_cpy_in_lower_case(second_table_name + i, old_ptr, - ptr - old_ptr); -#else - if (srv_lower_case_table_names) { - ut_cpy_in_lower_case(second_table_name + i, old_ptr, - ptr - old_ptr); - } else { - ut_memcpy(second_table_name + i, old_ptr, - ptr - old_ptr); + if (table_name == NULL) { + + return(ptr); /* Syntax error */ } -#endif - second_table_name[i + (ptr - old_ptr)] = '\0'; } else { -#ifdef __WIN__ - ut_cpy_in_lower_case(second_table_name, old_ptr, - ptr - old_ptr); -#else - if (srv_lower_case_table_names) { - ut_cpy_in_lower_case(second_table_name, old_ptr, - ptr - old_ptr); - } else { - ut_memcpy(second_table_name, old_ptr, ptr - old_ptr); + /* To be able to read table dumps made with InnoDB-4.0.17 or + earlier, we must allow the dot separator between the database + name and the table name also to appear within a quoted + identifier! InnoDB used to print a constraint as: + ... REFERENCES `databasename.tablename` ... + starting from 4.0.18 it is + ... REFERENCES `databasename`.`tablename` ... */ + + for (i = 0; i < scanned_id_len; i++) { + if (scanned_id[i] == '.') { + database_name = scanned_id; + database_name_len = i; + + scanned_id = scanned_id + i + 1; + scanned_id_len -= i + 1; + } } -#endif - second_table_name[dot_ptr - old_ptr] = '/'; - second_table_name[ptr - old_ptr] = '\0'; - } - *success = TRUE; - - *table = dict_table_get_low(second_table_name); - - if (*ptr == '`') { - ptr++; + table_name = scanned_id; + table_name_len = scanned_id_len; } - return(ptr); -} - -/************************************************************************* -Scans an id. For the lexical definition of an 'id', see the code below. -Strips backquotes from around the id. */ -static -char* -dict_scan_id( -/*=========*/ - /* out: scanned to */ - char* ptr, /* in: scanned to */ - char** start, /* out: start of the id; NULL if no id was - scannable */ - ulint* len) /* out: length of the id */ -{ - ibool scanned_backquote = FALSE; - - *start = NULL; + if (database_name == NULL) { + /* Use the database name of the foreign key table */ - while (isspace(*ptr)) { - ptr++; + database_name = name; + + database_name_len = dict_get_db_name_len(name); } - if (*ptr == '\0') { - - return(ptr); - } + if (table_name_len + database_name_len > 2000) { - if (*ptr == '`') { - scanned_backquote = TRUE; - ptr++; + return(ptr); /* Too long name */ } - *start = ptr; - - while (!isspace(*ptr) && *ptr != ',' && *ptr != '(' && *ptr != ')' - && *ptr != '\0' && *ptr != '`') { - - ptr++; +#ifdef __WIN__ + ut_cpy_in_lower_case(second_table_name, database_name, + database_name_len); +#else + if (srv_lower_case_table_names) { + ut_cpy_in_lower_case(second_table_name, database_name, + database_name_len); + } else { + ut_memcpy(second_table_name, database_name, + database_name_len); } +#endif + second_table_name[database_name_len] = '/'; - *len = (ulint) (ptr - *start); - - if (scanned_backquote) { - if (*ptr == '`') { - ptr++; - } else { - /* Syntax error */ - *start = NULL; - } +#ifdef __WIN__ + ut_cpy_in_lower_case(second_table_name + database_name_len + 1, + table_name, table_name_len); +#else + if (srv_lower_case_table_names) { + ut_cpy_in_lower_case(second_table_name + database_name_len + 1, + table_name, table_name_len); + } else { + ut_memcpy(second_table_name + database_name_len + 1, + table_name, table_name_len); } +#endif + second_table_name[database_name_len + 1 + table_name_len] = '\0'; + + *success = TRUE; + + *table = dict_table_get_low(second_table_name); return(ptr); } /************************************************************************* -Skips one id. */ +Skips one id. The id is allowed to contain also '.'. */ static char* dict_skip_word( @@ -2390,7 +2499,7 @@ dict_skip_word( *success = FALSE; - ptr = dict_scan_id(ptr, &start, &len); + ptr = dict_scan_id(ptr, &start, &len, TRUE); if (start) { *success = TRUE; @@ -2506,6 +2615,52 @@ scan_more: } /************************************************************************* +Finds the highest <number> for foreign key constraints of the table. Looks +only at the >= 4.0.18-format id's, which are of the form +databasename/tablename_ibfk_<number>. */ +static +ulint +dict_table_get_highest_foreign_id( +/*==============================*/ + /* out: highest number, 0 if table has no new + format foreign key constraints */ + dict_table_t* table) /* in: table in the dictionary memory cache */ +{ + dict_foreign_t* foreign; + char* endp; + ulint biggest_id = 0; + ulint id; + + ut_a(table); + + foreign = UT_LIST_GET_FIRST(table->foreign_list); + + while (foreign) { + if (ut_strlen(foreign->id) > ut_strlen("_ibfk_") + + ut_strlen(table->name) + && 0 == ut_memcmp(foreign->id, table->name, + ut_strlen(table->name)) + && 0 == ut_memcmp(foreign->id + ut_strlen(table->name), + (char*)"_ibfk_", ut_strlen("_ibfk_"))) { + /* It is of the >= 4.0.18 format */ + + id = strtoul(foreign->id + ut_strlen(table->name) + + ut_strlen("_ibfk_"), + &endp, 10); + ut_a(id != biggest_id); + + if (id > biggest_id) { + biggest_id = id; + } + } + + foreign = UT_LIST_GET_NEXT(foreign_list, foreign); + } + + return(biggest_id); +} + +/************************************************************************* Reports a simple foreign key create clause syntax error. */ static void @@ -2547,19 +2702,26 @@ dict_create_foreign_constraints_low( FOREIGN KEY (a, b) REFERENCES table2(c, d), table2 can be written also with the database name before it: test.table2; the default - database id the database of parameter name */ + database is the database of parameter name */ char* name) /* in: table full name in the normalized form database_name/table_name */ { dict_table_t* table; dict_table_t* referenced_table; + dict_table_t* table_to_alter; + ulint highest_id_so_far = 0; dict_index_t* index; dict_foreign_t* foreign; char* ptr = sql_string; char* start_of_latest_foreign = sql_string; char* buf = dict_foreign_err_buf; + char* constraint_name; /* this is NOT a null- + terminated string */ + ulint constraint_name_len; ibool success; ulint error; + char* ptr1; + char* ptr2; ulint i; ulint j; ibool is_on_delete; @@ -2586,16 +2748,89 @@ dict_create_foreign_constraints_low( return(DB_ERROR); } + + /* First check if we are actually doing an ALTER TABLE, and in that + case look for the table being altered */ + + ptr = dict_accept(ptr, (char*) "ALTER", &success); + + if (!success) { + + goto loop; + } + + ptr = dict_accept(ptr, (char*) "TABLE", &success); + + if (!success) { + + goto loop; + } + + /* We are doing an ALTER TABLE: scan the table name we are altering; + in the call below we use the buffer 'referenced_table_name' as a dummy + buffer */ + + ptr = dict_scan_table_name(ptr, &table_to_alter, name, + &success, referenced_table_name); + if (!success) { + fprintf(stderr, +"InnoDB: Error: could not find the table being ALTERED in:\n%s\n", sql_string); + + return(DB_ERROR); + } + + /* Starting from 4.0.18 and 4.1.2, we generate foreign key id's in the + format databasename/tablename_ibfk_<number>, where <number> is local + to the table; look for the highest <number> for table_to_alter, so + that we can assign to new constraints higher numbers. */ + + /* If we are altering a temporary table, the table name after ALTER + TABLE does not correspond to the internal table name, and + table_to_alter is NULL. TODO: should we fix this somehow? */ + + if (table_to_alter == NULL) { + highest_id_so_far = 0; + } else { + highest_id_so_far = dict_table_get_highest_foreign_id( + table_to_alter); + } + + /* Scan for foreign key declarations in a loop */ loop: - ptr = dict_scan_to(ptr, (char *) "FOREIGN"); + /* Scan either to "CONSTRAINT" or "FOREIGN", whichever is closer */ - if (*ptr == '\0') { + ptr1 = dict_scan_to(ptr, (char *) "CONSTRAINT"); + ptr2 = dict_scan_to(ptr, (char *) "FOREIGN"); + constraint_name = NULL; + + if (ptr1 < ptr2) { + /* The user has specified a constraint name. Pick it so + that we can store 'databasename/constraintname' as the id of + the id of the constraint to system tables. */ + ptr = ptr1; + + ptr = dict_accept(ptr, (char *) "CONSTRAINT", &success); + + ut_a(success); + + if (!isspace(*ptr)) { + goto loop; + } + + ptr = dict_scan_id(ptr, &constraint_name, &constraint_name_len, + FALSE); + } else { + ptr = ptr2; + } + + if (*ptr == '\0') { + /**********************************************************/ /* The following call adds the foreign key constraints to the data dictionary system tables on disk */ - error = dict_create_add_foreigns_to_dictionary(table, trx); - + error = dict_create_add_foreigns_to_dictionary( + highest_id_so_far, table, trx); return(error); } @@ -2703,6 +2938,28 @@ col_loop1: foreign = dict_mem_foreign_create(); + if (constraint_name) { + ulint db_len; + + /* Catenate 'databasename/' to the constraint name specified + by the user: we conceive the constraint as belonging to the + same MySQL 'database' as the table itself. We store the name + to foreign->id. */ + + db_len = dict_get_db_name_len(table->name); + + foreign->id = mem_heap_alloc(foreign->heap, + db_len + 1 + constraint_name_len + 1); + + ut_memcpy(foreign->id, table->name, db_len); + + foreign->id[db_len] = '/'; + + ut_memcpy(foreign->id + db_len + 1, constraint_name, + constraint_name_len); + foreign->id[db_len + 1 + constraint_name_len] = '\0'; + } + foreign->foreign_table = table; foreign->foreign_table_name = table->name; foreign->foreign_index = index; @@ -3004,7 +3261,7 @@ dict_create_foreign_constraints( FOREIGN KEY (a, b) REFERENCES table2(c, d), table2 can be written also with the database name before it: test.table2; the default - database id the database of parameter name */ + database is the database of parameter name */ char* name) /* in: table full name in the normalized form database_name/table_name */ { @@ -3088,7 +3345,7 @@ loop: goto syntax_error; } - ptr = dict_scan_id(ptr, &start, &len); + ptr = dict_scan_id(ptr, &start, &len, TRUE); if (start == NULL) { @@ -3106,8 +3363,10 @@ loop: foreign = UT_LIST_GET_FIRST(table->foreign_list); while (foreign != NULL) { - if (0 == ut_strcmp(foreign->id, id)) { - + if (0 == ut_strcmp(foreign->id, id) + || (ut_str_contains(foreign->id, '/') + && 0 == ut_strcmp(id, + dict_remove_db_name(foreign->id)))) { /* Found */ break; } @@ -3120,8 +3379,8 @@ loop: ut_sprintf_timestamp(buf); sprintf(buf + strlen(buf), " Error in dropping of a foreign key constraint of table %.500s,\n" -"just before:\n%s\n in SQL command\n%s\nCannot find a constraint with the\n" -"given id %s.\n", table->name, ptr, str, id); +"in SQL command\n%s\nCannot find a constraint with the\n" +"given id %s.\n", table->name, str, id); ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); mutex_exit(&dict_foreign_err_mutex); @@ -3937,10 +4196,20 @@ dict_print_info_on_foreign_key_in_create_format( char* buf) /* in: buffer of at least 5000 bytes */ { char* buf2 = buf; + char* stripped_id; + ulint cpy_len; ulint i; + if (ut_str_contains(foreign->id, '/')) { + /* Strip the preceding database name from the constraint id */ + stripped_id = foreign->id + 1 + + dict_get_db_name_len(foreign->id); + } else { + stripped_id = foreign->id; + } + buf2 += sprintf(buf2, ",\n CONSTRAINT `%s` FOREIGN KEY (", - foreign->id); + stripped_id); for (i = 0; i < foreign->n_fields; i++) { if ((ulint)(buf2 - buf) >= 4000) { @@ -3956,24 +4225,31 @@ dict_print_info_on_foreign_key_in_create_format( if (dict_tables_have_same_db(foreign->foreign_table_name, foreign->referenced_table_name)) { - /* Do not print the database name of the referenced - table */ + /* Do not print the database name of the referenced table */ buf2 += sprintf(buf2, ") REFERENCES `%.500s` (", dict_remove_db_name( foreign->referenced_table_name)); } else { - buf2 += sprintf(buf2, ") REFERENCES `%.500s` (", - foreign->referenced_table_name); - /* Change the '/' in the table name to '.' */ - - for (i = ut_strlen(buf); i > 0; i--) { - if (buf[i] == '/') { + buf2 += sprintf(buf2, ") REFERENCES `"); + + /* Look for the '/' in the table name */ - buf[i] = '.'; + i = 0; + while (foreign->referenced_table_name[i] != '/') { + i++; + } + + cpy_len = i; - break; - } + if (cpy_len > 500) { + cpy_len = 500; } + + memcpy(buf2, foreign->referenced_table_name, cpy_len); + buf2 += cpy_len; + + buf2 += sprintf(buf2, "`.`%.500s` (", + foreign->referenced_table_name + i + 1); } for (i = 0; i < foreign->n_fields; i++) { diff --git a/innobase/ibuf/ibuf0ibuf.c b/innobase/ibuf/ibuf0ibuf.c index 805f08af361..80128c3137d 100644 --- a/innobase/ibuf/ibuf0ibuf.c +++ b/innobase/ibuf/ibuf0ibuf.c @@ -3045,6 +3045,14 @@ loop: goto reset_bit; } + /* Do NOT merge to the 4.1 code base! */ + if (trx_sys_downgrading_from_4_1_1) { + fprintf(stderr, +"InnoDB: Fatal error: you are downgrading from >= 4.1.1 to 4.0, but\n" +"InnoDB: the insert buffer was not empty.\n"); + ut_a(0); + } + if (corruption_noticed) { rec_sprintf(err_buf, 450, ibuf_rec); fprintf(stderr, diff --git a/innobase/include/data0data.h b/innobase/include/data0data.h index 889d148d3fe..2ec94a9517a 100644 --- a/innobase/include/data0data.h +++ b/innobase/include/data0data.h @@ -262,6 +262,14 @@ dtuple_set_types_binary( /*====================*/ dtuple_t* tuple, /* in: data tuple */ ulint n); /* in: number of fields to set */ +/************************************************************************** +Checks if a dtuple contains an SQL null value. */ +UNIV_INLINE +ibool +dtuple_contains_null( +/*=================*/ + /* out: TRUE if some field is SQL null */ + dtuple_t* tuple); /* in: dtuple */ /************************************************************** Checks that a data field is typed. Asserts an error if not. */ diff --git a/innobase/include/data0data.ic b/innobase/include/data0data.ic index d356664df21..def80d3f430 100644 --- a/innobase/include/data0data.ic +++ b/innobase/include/data0data.ic @@ -406,3 +406,28 @@ data_write_sql_null( data[j] = '\0'; } } + +/************************************************************************** +Checks if a dtuple contains an SQL null value. */ +UNIV_INLINE +ibool +dtuple_contains_null( +/*=================*/ + /* out: TRUE if some field is SQL null */ + dtuple_t* tuple) /* in: dtuple */ +{ + ulint n; + ulint i; + + n = dtuple_get_n_fields(tuple); + + for (i = 0; i < n; i++) { + if (dfield_get_len(dtuple_get_nth_field(tuple, i)) + == UNIV_SQL_NULL) { + + return(TRUE); + } + } + + return(FALSE); +} diff --git a/innobase/include/dict0crea.h b/innobase/include/dict0crea.h index ccdedff42c8..b2c3ad7ca96 100644 --- a/innobase/include/dict0crea.h +++ b/innobase/include/dict0crea.h @@ -81,12 +81,25 @@ dict_create_or_check_foreign_constraint_tables(void); /*================================================*/ /* out: DB_SUCCESS or error code */ /************************************************************************ -Adds foreign key definitions to data dictionary tables in the database. */ +Adds foreign key definitions to data dictionary tables in the database. We +look at table->foreign_list, and also generate names to constraints that were +not named by the user. A generated constraint has a name of the format +databasename/tablename_ibfk_<number>, where the numbers start from 1, and are +given locally for this table, that is, the number is not global, as in the +old format constraints < 4.0.18 it used to be. */ ulint dict_create_add_foreigns_to_dictionary( /*===================================*/ /* out: error code or DB_SUCCESS */ + ulint start_id,/* in: if we are actually doing ALTER TABLE + ADD CONSTRAINT, we want to generate constraint + numbers which are bigger than in the table so + far; we number the constraints from + start_id + 1 up; start_id should be set to 0 if + we are creating a new table, or if the table + so far has no constraints for which the name + was generated here */ dict_table_t* table, /* in: table */ trx_t* trx); /* in: transaction */ diff --git a/innobase/include/dict0dict.h b/innobase/include/dict0dict.h index 234dece2cda..07184d8e28d 100644 --- a/innobase/include/dict0dict.h +++ b/innobase/include/dict0dict.h @@ -26,6 +26,14 @@ Created 1/8/1996 Heikki Tuuri #include "ut0byte.h" #include "trx0types.h" +/************************************************************************ +Get the database name length in a table name. */ + +ulint +dict_get_db_name_len( +/*=================*/ + /* out: database name length */ + char* name); /* in: table name in the form dbname '/' tablename */ /************************************************************************* Accepts a specified string. Comparisons are case-insensitive. */ @@ -217,6 +225,15 @@ dict_foreign_add_to_cache( /* out: DB_SUCCESS or error code */ dict_foreign_t* foreign); /* in, own: foreign key constraint */ /************************************************************************* +Checks if a table is referenced by foreign keys. */ + +ibool +dict_table_referenced_by_foreign_key( +/*=================================*/ + /* out: TRUE if table is referenced by a + foreign key */ + dict_table_t* table); /* in: InnoDB table */ +/************************************************************************* Scans a table create SQL string and adds to the data dictionary the foreign key constraints declared in the string. This function should be called after the indexes for a table have been created. diff --git a/innobase/include/ut0mem.h b/innobase/include/ut0mem.h index ba6905a8618..ce8aabeca41 100644 --- a/innobase/include/ut0mem.h +++ b/innobase/include/ut0mem.h @@ -95,10 +95,17 @@ ut_str_catenate( /* out, own: catenated null-terminated string */ char* str1, /* in: null-terminated string */ char* str2); /* in: null-terminated string */ +/************************************************************************** +Checks if a null-terminated string contains a certain character. */ + +ibool +ut_str_contains( +/*============*/ + char* str, /* in: null-terminated string */ + char c); /* in: character */ #ifndef UNIV_NONINL #include "ut0mem.ic" #endif #endif - diff --git a/innobase/log/log0log.c b/innobase/log/log0log.c index f041137840c..a40be74cac2 100644 --- a/innobase/log/log0log.c +++ b/innobase/log/log0log.c @@ -655,7 +655,18 @@ failure: if (!success) { fprintf(stderr, -"InnoDB: Error: log file group too small for innodb_thread_concurrency\n"); +"InnoDB: Error: ib_logfiles are too small for innodb_thread_concurrency %lu.\n" +"InnoDB: The combined size of ib_logfiles should be bigger than\n" +"InnoDB: 200 kB * innodb_thread_concurrency.\n" +"InnoDB: To get mysqld to start up, set innodb_thread_concurrency in my.cnf\n" +"InnoDB: to a lower value, for example, to 8. After an ERROR-FREE shutdown\n" +"InnoDB: of mysqld you can adjust the size of ib_logfiles, as explained in\n" +"InnoDB: section 5 of http://www.innodb.com/ibman.php", + (ulong)srv_thread_concurrency); + fprintf(stderr, +"InnoDB: Cannot continue operation. Calling exit(1).\n"); + + exit(1); } return(success); diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c index c89f41f69ad..5e84f43bcf0 100644 --- a/innobase/row/row0ins.c +++ b/innobase/row/row0ins.c @@ -682,14 +682,6 @@ row_ins_foreign_check_on_constraint( (DICT_FOREIGN_ON_DELETE_CASCADE | DICT_FOREIGN_ON_DELETE_SET_NULL))) { - /* No action is defined: return a foreign key error if - NO ACTION is not specified */ - - if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) { - - return(DB_SUCCESS); - } - row_ins_foreign_report_err((char*)"Trying to delete", thr, foreign, btr_pcur_get_rec(pcur), entry); @@ -703,14 +695,6 @@ row_ins_foreign_check_on_constraint( /* This is an UPDATE */ - /* No action is defined: return a foreign key error if - NO ACTION is not specified */ - - if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) { - - return(DB_SUCCESS); - } - row_ins_foreign_report_err((char*)"Trying to update", thr, foreign, btr_pcur_get_rec(pcur), entry); diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index cf7a3efcdfc..476ab85f62e 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -2085,6 +2085,7 @@ row_drop_table_for_mysql( char* name, /* in: table name */ trx_t* trx) /* in: transaction handle */ { + dict_foreign_t* foreign; dict_table_t* table; ulint space_id; que_thr_t* thr; @@ -2282,6 +2283,38 @@ row_drop_table_for_mysql( goto funct_exit; } + /* Check if the table is referenced by foreign key constraints from + some other table (not the table itself) */ + + foreign = UT_LIST_GET_FIRST(table->referenced_list); + + while (foreign && foreign->foreign_table == table) { + foreign = UT_LIST_GET_NEXT(referenced_list, foreign); + } + + if (foreign && trx->check_foreigns) { + char* buf = dict_foreign_err_buf; + + /* We only allow dropping a referenced table if + FOREIGN_KEY_CHECKS is set to 0 */ + + err = DB_CANNOT_DROP_CONSTRAINT; + + mutex_enter(&dict_foreign_err_mutex); + ut_sprintf_timestamp(buf); + + sprintf(buf + strlen(buf), + " Cannot drop table %.500s\n", name); + sprintf(buf + strlen(buf), +"because it is referenced by %.500s\n", foreign->foreign_table_name); + + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + + mutex_exit(&dict_foreign_err_mutex); + + goto funct_exit; + } + if (table->n_mysql_handles_opened > 0) { ut_print_timestamp(stderr); @@ -2469,7 +2502,7 @@ row_is_mysql_tmp_table_name( { ulint i; - for (i = 0; i <= ut_strlen(name) - 5; i++) { + for (i = 0; i + 5 <= ut_strlen(name); i++) { if (ut_memcmp(name + i, (char*)"/#sql", 5) == 0) { return(TRUE); @@ -2505,6 +2538,7 @@ row_rename_table_for_mysql( ulint keywordlen; ulint len; ulint i; + char* db_name; ibool success; char buf[2 * OS_FILE_MAX_PATH]; @@ -2593,6 +2627,15 @@ row_rename_table_for_mysql( "PROCEDURE RENAME_TABLE_PROC () IS\n" "new_table_name CHAR;\n" "old_table_name CHAR;\n" + "gen_constr_prefix CHAR;\n" + "new_db_name CHAR;\n" + "foreign_id CHAR;\n" + "new_foreign_id CHAR;\n" + "old_db_name_len INT;\n" + "old_t_name_len INT;\n" + "new_db_name_len INT;\n" + "id_len INT;\n" + "found INT;\n" "BEGIN\n" "new_table_name :='"; @@ -2619,32 +2662,94 @@ row_rename_table_for_mysql( } str3 = mem_heap_alloc(heap, - 1000 + 500 * n_constraints_to_drop); + 1000 + 1000 * n_constraints_to_drop); *str3 = '\0'; sprintf(str3, "';\n" "UPDATE SYS_TABLES SET NAME = new_table_name\n" "WHERE NAME = old_table_name;\n"); + db_name = mem_heap_alloc(heap, 1 + dict_get_db_name_len( + old_name)); + ut_memcpy(db_name, old_name, dict_get_db_name_len(old_name)); + db_name[dict_get_db_name_len(old_name)] = '\0'; + + /* Internally, old format < 4.0.18 constraints have as the + constraint id <number>_<number>, while new format constraints + have <databasename>/<constraintname>. */ + for (i = 0; i < n_constraints_to_drop; i++) { + sprintf(str3 + strlen(str3), - "DELETE FROM SYS_FOREIGN_COLS WHERE ID = '%s';\n" - "DELETE FROM SYS_FOREIGN WHERE ID = '%s';\n", + "DELETE FROM SYS_FOREIGN_COLS WHERE ID = '%s/%s';\n" + "DELETE FROM SYS_FOREIGN WHERE ID = '%s/%s';\n", + db_name, constraints_to_drop[i], + db_name, constraints_to_drop[i]); + + if (!ut_str_contains(constraints_to_drop[i], '/')) { + /* If this happens to be an old format + constraint, let us delete it. Since all new + format constraints contain '/', it does no + harm to run these DELETEs anyway. */ + + sprintf(str3 + strlen(str3), + "DELETE FROM SYS_FOREIGN_COLS WHERE ID = '%s';\n" + "DELETE FROM SYS_FOREIGN WHERE ID = '%s';\n", constraints_to_drop[i], constraints_to_drop[i]); + } } sprintf(str3 + strlen(str3), "END;\n"); - ut_a(strlen(str3) < 1000 + 500 * n_constraints_to_drop); + ut_a(strlen(str3) < 1000 + 1000 * n_constraints_to_drop); } else { str3 = (char*) "';\n" "UPDATE SYS_TABLES SET NAME = new_table_name\n" "WHERE NAME = old_table_name;\n" - "UPDATE SYS_FOREIGN SET FOR_NAME = new_table_name\n" - "WHERE FOR_NAME = old_table_name;\n" + "found := 1;\n" + "old_db_name_len := INSTR(old_table_name, '/') - 1;\n" + "new_db_name_len := INSTR(new_table_name, '/') - 1;\n" + "new_db_name := SUBSTR(new_table_name, 0, new_db_name_len);\n" + "old_t_name_len := LENGTH(old_table_name);\n" + "gen_constr_prefix := CONCAT(old_table_name, '_ibfk_');\n" + "WHILE found = 1 LOOP\n" + " SELECT ID INTO foreign_id\n" + " FROM SYS_FOREIGN\n" + " WHERE FOR_NAME = old_table_name;\n" + " IF (SQL % NOTFOUND) THEN\n" + " found := 0;\n" + " ELSE\n" + " UPDATE SYS_FOREIGN\n" + " SET FOR_NAME = new_table_name\n" + " WHERE ID = foreign_id;\n" + " id_len := LENGTH(foreign_id);\n" + " IF (INSTR(foreign_id, '/') > 0) THEN\n" + " IF (INSTR(foreign_id,\n" + " gen_constr_prefix) > 0)\n" + " THEN\n" + " new_foreign_id :=\n" + " CONCAT(new_table_name,\n" + " SUBSTR(foreign_id, old_t_name_len,\n" + " id_len - old_t_name_len));\n" + " ELSE\n" + " new_foreign_id :=\n" + " CONCAT(new_db_name,\n" + " SUBSTR(foreign_id,\n" + " old_db_name_len,\n" + " id_len - old_db_name_len));\n" + " END IF;\n" + " UPDATE SYS_FOREIGN\n" + " SET ID = new_foreign_id\n" + " WHERE ID = foreign_id;\n" + " UPDATE SYS_FOREIGN_COLS\n" + " SET ID = new_foreign_id\n" + " WHERE ID = foreign_id;\n" + " END IF;\n" + " END IF;\n" + "END LOOP;\n" "UPDATE SYS_FOREIGN SET REF_NAME = new_table_name\n" "WHERE REF_NAME = old_table_name;\n" "END;\n"; diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index 57de309c090..b32f0b204b3 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -2886,7 +2886,16 @@ row_search_for_mysql( if (match_mode == ROW_SEL_EXACT && index->type & DICT_UNIQUE && dtuple_get_n_fields(search_tuple) - == dict_index_get_n_unique(index)) { + == dict_index_get_n_unique(index) + && (index->type & DICT_CLUSTERED + || !dtuple_contains_null(search_tuple))) { + + /* Note above that a UNIQUE secondary index can contain many + rows with the same key value if one of the columns is the SQL + null. A clustered index under MySQL can never contain null + columns because we demand that all the columns in primary key + are non-null. */ + unique_search = TRUE; /* Even if the condition is unique, MySQL seems to try to diff --git a/innobase/srv/srv0start.c b/innobase/srv/srv0start.c index e6fdc95fad0..7b9c7a4066e 100644 --- a/innobase/srv/srv0start.c +++ b/innobase/srv/srv0start.c @@ -1587,6 +1587,31 @@ NetWare. */ os_fast_mutex_free(&srv_os_test_mutex); + /***********************************************************/ + /* Do NOT merge to the 4.1 code base! */ + if (trx_sys_downgrading_from_4_1_1) { + fprintf(stderr, +"InnoDB: You are downgrading from an InnoDB version which allows multiple\n" +"InnoDB: tablespaces. Wait that purge and insert buffer merge run to\n" +"InnoDB: completion...\n"); + for (;;) { + os_thread_sleep(10000000); + + if (0 == strcmp(srv_main_thread_op_info, + "waiting for server activity")) { + break; + } + } + fprintf(stderr, +"InnoDB: Full purge and insert buffer merge completed.\n"); + + trx_sys_mark_downgraded_from_4_1_1(); + + fprintf(stderr, +"InnoDB: Downgraded from >= 4.1.1 to 4.0\n"); + } + /***********************************************************/ + if (srv_print_verbose_log) { ut_print_timestamp(stderr); fprintf(stderr, diff --git a/innobase/sync/sync0sync.c b/innobase/sync/sync0sync.c index 424f5b1b35a..eb13c59baf4 100644 --- a/innobase/sync/sync0sync.c +++ b/innobase/sync/sync0sync.c @@ -159,7 +159,7 @@ struct sync_thread_struct{ }; /* Number of slots reserved for each OS thread in the sync level array */ -#define SYNC_THREAD_N_LEVELS 7000 +#define SYNC_THREAD_N_LEVELS 10000 struct sync_level_struct{ void* latch; /* pointer to a mutex or an rw-lock; NULL means that diff --git a/innobase/trx/trx0sys.c b/innobase/trx/trx0sys.c index d9eace5ad9a..37e148fe001 100644 --- a/innobase/trx/trx0sys.c +++ b/innobase/trx/trx0sys.c @@ -45,6 +45,43 @@ or there was no master log position info inside InnoDB. */ char trx_sys_mysql_master_log_name[TRX_SYS_MYSQL_LOG_NAME_LEN]; ib_longlong trx_sys_mysql_master_log_pos = -1; +/* Do NOT merge this to the 4.1 code base! */ +ibool trx_sys_downgrading_from_4_1_1 = FALSE; + +/******************************************************************** +Do NOT merge this to the 4.1 code base! +Marks the trx sys header when we have successfully downgraded from the >= 4.1.1 +multiple tablespace format back to the 4.0 format. */ + +void +trx_sys_mark_downgraded_from_4_1_1(void) +/*====================================*/ +{ + page_t* page; + byte* doublewrite; + mtr_t mtr; + + /* Let us mark to the trx_sys header that the downgrade has been + done. */ + + mtr_start(&mtr); + + page = buf_page_get(TRX_SYS_SPACE, TRX_SYS_PAGE_NO, RW_X_LATCH, &mtr); + buf_page_dbg_add_level(page, SYNC_NO_ORDER_CHECK); + + doublewrite = page + TRX_SYS_DOUBLEWRITE; + + mlog_write_ulint(doublewrite + TRX_SYS_DOUBLEWRITE_SPACE_ID_STORED, + TRX_SYS_DOUBLEWRITE_SPACE_ID_STORED_N + 1, + MLOG_4BYTES, &mtr); + mtr_commit(&mtr); + + /* Flush the modified pages to disk and make a checkpoint */ + log_make_checkpoint_at(ut_dulint_max, TRUE); + + trx_sys_downgrading_from_4_1_1 = FALSE; +} + /******************************************************************** Determines if a page number is located inside the doublewrite buffer. */ @@ -187,7 +224,7 @@ start_again: just read in some numbers */ trx_doublewrite_init(doublewrite); - + mtr_commit(&mtr); } else { fprintf(stderr, @@ -351,6 +388,31 @@ trx_sys_doublewrite_init_or_restore_pages( == TRX_SYS_DOUBLEWRITE_MAGIC_N) { /* The doublewrite buffer has been created */ + /* Do NOT merge to the 4.1 code base! */ + if (mach_read_from_4(doublewrite + + TRX_SYS_DOUBLEWRITE_SPACE_ID_STORED) + == TRX_SYS_DOUBLEWRITE_SPACE_ID_STORED_N) { + + fprintf(stderr, +"InnoDB: You are downgrading from the multiple tablespace format of\n" +"InnoDB: >= MySQL-4.1.1 back to the old format of MySQL-4.0.\n" +"InnoDB:\n" +"InnoDB: MAKE SURE that the mysqld server is idle, and purge and the insert\n" +"InnoDB: buffer merge have run to completion under >= 4.1.1 before trying to\n" +"InnoDB: downgrade! You can determine this by looking at SHOW INNODB STATUS:\n" +"InnoDB: if the Main thread is 'waiting for server activity' and SHOW\n" +"InnoDB: PROCESSLIST shows that you have ended all other connections\n" +"InnoDB: to mysqld, then purge and the insert buffer merge have been\n" +"InnoDB: completed.\n" +"InnoDB: If you have already created tables in >= 4.1.1, then those\n" +"InnoDB: tables cannot be used under 4.0.\n" +"InnoDB: NOTE THAT this downgrade procedure has not been properly tested!\n" +"InnoDB: The safe way to downgrade is to dump all InnoDB tables and recreate\n" +"InnoDB: the whole tablespace.\n"); + + trx_sys_downgrading_from_4_1_1 = TRUE; + } + trx_doublewrite_init(doublewrite); block1 = trx_doublewrite->block1; diff --git a/innobase/ut/ut0mem.c b/innobase/ut/ut0mem.c index c367ea10ee1..a3c78dd9709 100644 --- a/innobase/ut/ut0mem.c +++ b/innobase/ut/ut0mem.c @@ -258,3 +258,27 @@ ut_str_catenate( return(str); } + +/************************************************************************** +Checks if a null-terminated string contains a certain character. */ + +ibool +ut_str_contains( +/*============*/ + char* str, /* in: null-terminated string */ + char c) /* in: character */ +{ + ulint len; + ulint i; + + len = ut_strlen(str); + + for (i = 0; i < len; i++) { + if (str[i] == c) { + + return(TRUE); + } + } + + return(FALSE); +}
\ No newline at end of file |