summaryrefslogtreecommitdiff
path: root/innobase
diff options
context:
space:
mode:
authorunknown <monty@mysql.com>2004-02-11 00:06:46 +0100
committerunknown <monty@mysql.com>2004-02-11 00:06:46 +0100
commit5b2c3126277a8eedab5bcc8a9b0ce7386ccc3dbe (patch)
tree927515d304bd661aebdb0e534418caf49d322ada /innobase
parentdc792940232f3265e21905cb939853f4db5ebd16 (diff)
parent65ec6a41b65f26552481be24ac8947c83eeea198 (diff)
downloadmariadb-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.c5
-rw-r--r--innobase/buf/buf0lru.c18
-rw-r--r--innobase/dict/dict0crea.c79
-rw-r--r--innobase/dict/dict0dict.c610
-rw-r--r--innobase/ibuf/ibuf0ibuf.c8
-rw-r--r--innobase/include/data0data.h8
-rw-r--r--innobase/include/data0data.ic25
-rw-r--r--innobase/include/dict0crea.h15
-rw-r--r--innobase/include/dict0dict.h17
-rw-r--r--innobase/include/ut0mem.h9
-rw-r--r--innobase/log/log0log.c13
-rw-r--r--innobase/row/row0ins.c16
-rw-r--r--innobase/row/row0mysql.c119
-rw-r--r--innobase/row/row0sel.c11
-rw-r--r--innobase/srv/srv0start.c25
-rw-r--r--innobase/sync/sync0sync.c2
-rw-r--r--innobase/trx/trx0sys.c64
-rw-r--r--innobase/ut/ut0mem.c24
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