Many files:

  Fix InnoDB critical bug #7496; we scan the InnoDB data dictionary also at a normal mysqld startup, and create the spaces, so that we know the mapping space id -> .ibd file name; fix an infinite loop if DISCARD TABLESPACE coincides with INSERT or some other table operation; fix a potential crash if DISCARD TABLESPACE coincides with a cascaded FOREIGN KEY operation in the same table; do not allow DISCARD TABLESPACE of a referenced table if FOREIGN_KEY_CHECKS=1
parent aa9c7a67
...@@ -326,7 +326,7 @@ buf_read_page( ...@@ -326,7 +326,7 @@ buf_read_page(
if (err == DB_TABLESPACE_DELETED) { if (err == DB_TABLESPACE_DELETED) {
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
" InnoDB: error: trying to access tablespace %lu page no. %lu,\n" " InnoDB: Error: trying to access tablespace %lu page no. %lu,\n"
"InnoDB: but the tablespace does not exist or is just being dropped.\n", "InnoDB: but the tablespace does not exist or is just being dropped.\n",
(ulong) space, (ulong) offset); (ulong) space, (ulong) offset);
} }
......
...@@ -205,12 +205,14 @@ loop: ...@@ -205,12 +205,14 @@ loop:
In a crash recovery we already have all the tablespace objects created. In a crash recovery we already have all the tablespace objects created.
This function compares the space id information in the InnoDB data dictionary This function compares the space id information in the InnoDB data dictionary
to what we already read with fil_load_single_table_tablespaces(). to what we already read with fil_load_single_table_tablespaces().
In a normal startup we just scan the biggest space id, and store it to
fil_system. */ In a normal startup, we create the tablespace objects for every table in
InnoDB's data dictionary, if the corresponding .ibd file exists.
We also scan the biggest space id, and store it to fil_system. */
void void
dict_check_tablespaces_or_store_max_id( dict_check_tablespaces_and_store_max_id(
/*===================================*/ /*====================================*/
ibool in_crash_recovery) /* in: are we doing a crash recovery */ ibool in_crash_recovery) /* in: are we doing a crash recovery */
{ {
dict_table_t* sys_tables; dict_table_t* sys_tables;
...@@ -280,6 +282,14 @@ loop: ...@@ -280,6 +282,14 @@ loop:
FALSE, TRUE, TRUE); FALSE, TRUE, TRUE);
} }
if (space_id != 0 && !in_crash_recovery) {
/* It is a normal database startup: create the space
object and check that the .ibd file exists. */
fil_open_single_table_tablespace(FALSE, space_id,
name);
}
mem_free(name); mem_free(name);
if (space_id > max_space_id) { if (space_id > max_space_id) {
...@@ -796,8 +806,18 @@ dict_load_table( ...@@ -796,8 +806,18 @@ dict_load_table(
/* Ok; (if we did a crash recovery then the tablespace /* Ok; (if we did a crash recovery then the tablespace
can already be in the memory cache) */ can already be in the memory cache) */
} else { } else {
/* In >= 4.1.9, InnoDB scans the data dictionary also
at a normal mysqld startup. It is an error if the
space object does not exist in memory. */
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: error: space object of table %s,\n"
"InnoDB: space id %lu did not exist in memory. Retrying an open.\n",
name, (ulong)space);
/* Try to open the tablespace */ /* Try to open the tablespace */
if (!fil_open_single_table_tablespace(space, name)) { if (!fil_open_single_table_tablespace(TRUE,
space, name)) {
/* We failed to find a sensible tablespace /* We failed to find a sensible tablespace
file */ file */
......
...@@ -466,6 +466,10 @@ fil_node_open_file( ...@@ -466,6 +466,10 @@ fil_node_open_file(
ulint size_low; ulint size_low;
ulint size_high; ulint size_high;
ibool ret; ibool ret;
byte* buf2;
byte* page;
ibool success;
ulint space_id;
#ifdef UNIV_SYNC_DEBUG #ifdef UNIV_SYNC_DEBUG
ut_ad(mutex_own(&(system->mutex))); ut_ad(mutex_own(&(system->mutex)));
...@@ -494,6 +498,8 @@ fil_node_open_file( ...@@ -494,6 +498,8 @@ fil_node_open_file(
system->n_open++; system->n_open++;
if (node->size == 0) { if (node->size == 0) {
ut_a(space->purpose != FIL_LOG);
os_file_get_size(node->handle, &size_low, &size_high); os_file_get_size(node->handle, &size_low, &size_high);
size_bytes = (((ib_longlong)size_high) << 32) size_bytes = (((ib_longlong)size_high) << 32)
...@@ -507,6 +513,46 @@ fil_node_open_file( ...@@ -507,6 +513,46 @@ fil_node_open_file(
ut_a(space->id != 0); ut_a(space->id != 0);
if (size_bytes < FIL_IBD_FILE_INITIAL_SIZE * UNIV_PAGE_SIZE) {
fprintf(stderr,
"InnoDB: Error: the size of single-table tablespace file %s\n"
"InnoDB: is only %lu %lu, should be at least %lu!", node->name,
(ulong) size_high,
(ulong) size_low, (ulong) (4 * UNIV_PAGE_SIZE));
ut_a(0);
}
/* Read the first page of the tablespace */
buf2 = ut_malloc(2 * UNIV_PAGE_SIZE);
/* Align the memory for file i/o if we might have O_DIRECT
set */
page = ut_align(buf2, UNIV_PAGE_SIZE);
success = os_file_read(node->handle, page, 0, 0,
UNIV_PAGE_SIZE);
space_id = fsp_header_get_space_id(page);
ut_free(buf2);
if (space_id == ULINT_UNDEFINED || space_id == 0) {
fprintf(stderr,
"InnoDB: Error: tablespace id %lu in file %s is not sensible\n",
(ulong) space_id,
node->name);
ut_a(0);
}
if (space_id != space->id) {
fprintf(stderr,
"InnoDB: Error: tablespace id is %lu in the data dictionary\n"
"InnoDB: but in file %s it is %lu!\n", space->id, node->name, space_id);
ut_a(0);
}
if (size_bytes >= FSP_EXTENT_SIZE * UNIV_PAGE_SIZE) { if (size_bytes >= FSP_EXTENT_SIZE * UNIV_PAGE_SIZE) {
node->size = (ulint) ((size_bytes / (1024 * 1024)) node->size = (ulint) ((size_bytes / (1024 * 1024))
* ((1024 * 1024) / UNIV_PAGE_SIZE)); * ((1024 * 1024) / UNIV_PAGE_SIZE));
...@@ -2487,21 +2533,29 @@ func_exit: ...@@ -2487,21 +2533,29 @@ func_exit:
} }
/************************************************************************ /************************************************************************
Tries to open a single-table tablespace and checks the space id is right in Tries to open a single-table tablespace and optionally checks the space id is
it. If does not succeed, prints an error message to the .err log. This right in it. If does not succeed, prints an error message to the .err log. This
function is used to open the tablespace when we load a table definition function is used to open a tablespace when we start up mysqld, and also in
to the dictionary cache. NOTE that we assume this operation is used under the IMPORT TABLESPACE.
protection of the dictionary mutex, so that two users cannot race here. This NOTE that we assume this operation is used either at the database startup
operation does not leave the file associated with the tablespace open, but or under the protection of the dictionary mutex, so that two users cannot
closes it after we have looked at the space id in it. */ race here. This operation does not leave the file associated with the
tablespace open, but closes it after we have looked at the space id in it. */
ibool ibool
fil_open_single_table_tablespace( fil_open_single_table_tablespace(
/*=============================*/ /*=============================*/
/* out: TRUE if success */ /* out: TRUE if success */
ulint id, /* in: space id */ ibool check_space_id, /* in: should we check that the space
const char* name) /* in: table name in the id in the file is right; we assume
databasename/tablename format */ that this function runs much faster
if no check is made, since accessing
the file inode probably is much
faster (the OS caches them) than
accessing the first page of the file */
ulint id, /* in: space id */
const char* name) /* in: table name in the
databasename/tablename format */
{ {
os_file_t file; os_file_t file;
char* filepath; char* filepath;
...@@ -2540,6 +2594,12 @@ fil_open_single_table_tablespace( ...@@ -2540,6 +2594,12 @@ fil_open_single_table_tablespace(
return(FALSE); return(FALSE);
} }
if (!check_space_id) {
space_id = id;
goto skip_check;
}
/* Read the first page of the tablespace */ /* Read the first page of the tablespace */
buf2 = ut_malloc(2 * UNIV_PAGE_SIZE); buf2 = ut_malloc(2 * UNIV_PAGE_SIZE);
...@@ -2552,6 +2612,8 @@ fil_open_single_table_tablespace( ...@@ -2552,6 +2612,8 @@ fil_open_single_table_tablespace(
space_id = fsp_header_get_space_id(page); space_id = fsp_header_get_space_id(page);
ut_free(buf2);
if (space_id != id) { if (space_id != id) {
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
...@@ -2572,6 +2634,7 @@ fil_open_single_table_tablespace( ...@@ -2572,6 +2634,7 @@ fil_open_single_table_tablespace(
goto func_exit; goto func_exit;
} }
skip_check:
success = fil_space_create(filepath, space_id, FIL_TABLESPACE); success = fil_space_create(filepath, space_id, FIL_TABLESPACE);
if (!success) { if (!success) {
...@@ -2584,7 +2647,6 @@ fil_open_single_table_tablespace( ...@@ -2584,7 +2647,6 @@ fil_open_single_table_tablespace(
fil_node_create(filepath, 0, space_id, FALSE); fil_node_create(filepath, 0, space_id, FALSE);
func_exit: func_exit:
os_file_close(file); os_file_close(file);
ut_free(buf2);
mem_free(filepath); mem_free(filepath);
return(ret); return(ret);
...@@ -2651,7 +2713,7 @@ fil_load_single_table_tablespace( ...@@ -2651,7 +2713,7 @@ fil_load_single_table_tablespace(
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: could not open single-table tablespace file\n" "InnoDB: Error: could not open single-table tablespace file\n"
"InnoDB: %s!\n" "InnoDB: %s!\n"
"InnoDB: We do not continue crash recovery, because the table will become\n" "InnoDB: We do not continue the crash recovery, because the table may become\n"
"InnoDB: corrupt if we cannot apply the log records in the InnoDB log to it.\n" "InnoDB: corrupt if we cannot apply the log records in the InnoDB log to it.\n"
"InnoDB: To fix the problem and start mysqld:\n" "InnoDB: To fix the problem and start mysqld:\n"
"InnoDB: 1) If there is a permission problem in the file and mysqld cannot\n" "InnoDB: 1) If there is a permission problem in the file and mysqld cannot\n"
...@@ -2822,8 +2884,9 @@ fil_load_single_table_tablespace( ...@@ -2822,8 +2884,9 @@ fil_load_single_table_tablespace(
goto func_exit; goto func_exit;
} }
/* We do not measure the size of the file, that is why we pass the 0 /* We do not use the size information we have about the file, because
below */ the rounding formulat for extents and pages is somewhat complex; we
let fil_node_open() do that task. */
fil_node_create(filepath, 0, space_id, FALSE); fil_node_create(filepath, 0, space_id, FALSE);
func_exit: func_exit:
......
...@@ -18,12 +18,14 @@ Created 4/24/1996 Heikki Tuuri ...@@ -18,12 +18,14 @@ Created 4/24/1996 Heikki Tuuri
In a crash recovery we already have all the tablespace objects created. In a crash recovery we already have all the tablespace objects created.
This function compares the space id information in the InnoDB data dictionary This function compares the space id information in the InnoDB data dictionary
to what we already read with fil_load_single_table_tablespaces(). to what we already read with fil_load_single_table_tablespaces().
In a normal startup we just scan the biggest space id, and store it to
fil_system. */ In a normal startup, we create the tablespace objects for every table in
InnoDB's data dictionary, if the corresponding .ibd file exists.
We also scan the biggest space id, and store it to fil_system. */
void void
dict_check_tablespaces_or_store_max_id( dict_check_tablespaces_and_store_max_id(
/*===================================*/ /*====================================*/
ibool in_crash_recovery); /* in: are we doing a crash recovery */ ibool in_crash_recovery); /* in: are we doing a crash recovery */
/************************************************************************ /************************************************************************
Finds the first table name in the given database. */ Finds the first table name in the given database. */
......
...@@ -364,19 +364,29 @@ fil_create_new_single_table_tablespace( ...@@ -364,19 +364,29 @@ fil_create_new_single_table_tablespace(
tablespace file in pages, tablespace file in pages,
must be >= FIL_IBD_FILE_INITIAL_SIZE */ must be >= FIL_IBD_FILE_INITIAL_SIZE */
/************************************************************************ /************************************************************************
Tries to open a single-table tablespace and checks the space id is right in Tries to open a single-table tablespace and optionally checks the space id is
it. If does not succeed, prints an error message to the .err log. This right in it. If does not succeed, prints an error message to the .err log. This
function is used to open the tablespace when we load a table definition function is used to open a tablespace when we start up mysqld, and also in
to the dictionary cache. NOTE that we assume this operation is used under the IMPORT TABLESPACE.
protection of the dictionary mutex, so that two users cannot race here. */ NOTE that we assume this operation is used either at the database startup
or under the protection of the dictionary mutex, so that two users cannot
race here. This operation does not leave the file associated with the
tablespace open, but closes it after we have looked at the space id in it. */
ibool ibool
fil_open_single_table_tablespace( fil_open_single_table_tablespace(
/*=============================*/ /*=============================*/
/* out: TRUE if success */ /* out: TRUE if success */
ulint id, /* in: space id */ ibool check_space_id, /* in: should we check that the space
const char* name); /* in: table name in the id in the file is right; we assume
databasename/tablename format */ that this function runs much faster
if no check is made, since accessing
the file inode probably is much
faster (the OS caches them) than
accessing the first page of the file */
ulint id, /* in: space id */
const char* name); /* in: table name in the
databasename/tablename format */
/************************************************************************ /************************************************************************
It is possible, though very improbable, that the lsn's in the tablespace to be It is possible, though very improbable, that the lsn's in the tablespace to be
imported have risen above the current system lsn, if a lengthy purge, ibuf imported have risen above the current system lsn, if a lengthy purge, ibuf
......
...@@ -367,25 +367,7 @@ row_drop_table_for_mysql( ...@@ -367,25 +367,7 @@ row_drop_table_for_mysql(
/************************************************************************* /*************************************************************************
Discards the tablespace of a table which stored in an .ibd file. Discarding Discards the tablespace of a table which stored in an .ibd file. Discarding
means that this function deletes the .ibd file and assigns a new table id for means that this function deletes the .ibd file and assigns a new table id for
the table. Also the flag table->ibd_file_missing is set TRUE. the table. Also the flag table->ibd_file_missing is set TRUE. */
How do we prevent crashes caused by ongoing operations on the table? Old
operations could try to access non-existent pages.
1) SQL queries, INSERT, SELECT, ...: we must get an exclusive MySQL table lock
on the table before we can do DISCARD TABLESPACE. Then there are no running
queries on the table.
2) Purge and rollback: we assign a new table id for the table. Since purge and
rollback look for the table based on the table id, they see the table as
'dropped' and discard their operations.
3) Insert buffer: we remove all entries for the tablespace in the insert
buffer tree; as long as the tablespace mem object does not exist, ongoing
insert buffer page merges are discarded in buf0rea.c. If we recreate the
tablespace mem object with IMPORT TABLESPACE later, then the tablespace will
have the same id, but the tablespace_version field in the mem object is
different, and ongoing old insert buffer page merges get discarded.
4) Linear readahead and random readahead: we use the same method as in 3) to
discard ongoing operations. */
int int
row_discard_tablespace_for_mysql( row_discard_tablespace_for_mysql(
......
...@@ -378,6 +378,19 @@ struct trx_struct{ ...@@ -378,6 +378,19 @@ struct trx_struct{
replication slave, this is the replication slave, this is the
position in the log file up to which position in the log file up to which
replication has processed */ replication has processed */
/* A MySQL variable mysql_thd->synchronous_repl tells if we have
to use synchronous replication. See ha_innodb.cc. */
char* repl_wait_binlog_name;/* NULL, or if synchronous MySQL
replication is used, the binlog name
up to which we must communicate the
binlog to the slave, before returning
from a commit; this is the same as
mysql_log_file_name, but we allocate
and copy the name to a separate buffer
here */
ib_longlong repl_wait_binlog_pos;/* see above at
repl_wait_binlog_name */
os_thread_id_t mysql_thread_id;/* id of the MySQL thread associated os_thread_id_t mysql_thread_id;/* id of the MySQL thread associated
with this transaction object */ with this transaction object */
ulint mysql_process_no;/* since in Linux, 'top' reports ulint mysql_process_no;/* since in Linux, 'top' reports
......
...@@ -1173,7 +1173,7 @@ run_again: ...@@ -1173,7 +1173,7 @@ run_again:
check_index = foreign->foreign_index; check_index = foreign->foreign_index;
} }
if (check_table == NULL) { if (check_table == NULL || check_table->ibd_file_missing) {
if (check_ref) { if (check_ref) {
FILE* ef = dict_foreign_err_file; FILE* ef = dict_foreign_err_file;
mutex_enter(&dict_foreign_err_mutex); mutex_enter(&dict_foreign_err_mutex);
...@@ -1192,7 +1192,7 @@ run_again: ...@@ -1192,7 +1192,7 @@ run_again:
dtuple_print(ef, entry); dtuple_print(ef, entry);
fputs("\nBut the parent table ", ef); fputs("\nBut the parent table ", ef);
ut_print_name(ef, trx, foreign->referenced_table_name); ut_print_name(ef, trx, foreign->referenced_table_name);
fputs(" does not currently exist!\n", ef); fputs("\nor its .ind file does not currently exist!\n", ef);
mutex_exit(&dict_foreign_err_mutex); mutex_exit(&dict_foreign_err_mutex);
return(DB_NO_REFERENCED_ROW); return(DB_NO_REFERENCED_ROW);
......
...@@ -869,7 +869,21 @@ row_insert_for_mysql( ...@@ -869,7 +869,21 @@ row_insert_for_mysql(
ut_ad(trx); ut_ad(trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
if (prebuilt->table->ibd_file_missing) {
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB: Error:\n"
"InnoDB: MySQL is trying to use a table handle but the .ibd file for\n"
"InnoDB: table %s does not exist.\n"
"InnoDB: Have you deleted the .ibd file from the database directory under\n"
"InnoDB: the MySQL datadir, or have you used DISCARD TABLESPACE?\n"
"InnoDB: Look from\n"
"http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n"
"InnoDB: how you can resolve the problem.\n",
prebuilt->table->name);
return(DB_ERROR);
}
if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) {
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: trying to free a corrupt\n" "InnoDB: Error: trying to free a corrupt\n"
...@@ -1087,6 +1101,20 @@ row_update_for_mysql( ...@@ -1087,6 +1101,20 @@ row_update_for_mysql(
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
UT_NOT_USED(mysql_rec); UT_NOT_USED(mysql_rec);
if (prebuilt->table->ibd_file_missing) {
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB: Error:\n"
"InnoDB: MySQL is trying to use a table handle but the .ibd file for\n"
"InnoDB: table %s does not exist.\n"
"InnoDB: Have you deleted the .ibd file from the database directory under\n"
"InnoDB: the MySQL datadir, or have you used DISCARD TABLESPACE?\n"
"InnoDB: Look from\n"
"http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n"
"InnoDB: how you can resolve the problem.\n",
prebuilt->table->name);
return(DB_ERROR);
}
if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) {
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: trying to free a corrupt\n" "InnoDB: Error: trying to free a corrupt\n"
...@@ -1966,9 +1994,25 @@ row_add_table_to_background_drop_list( ...@@ -1966,9 +1994,25 @@ row_add_table_to_background_drop_list(
/************************************************************************* /*************************************************************************
Discards the tablespace of a table which stored in an .ibd file. Discarding Discards the tablespace of a table which stored in an .ibd file. Discarding
means that this function deletes the .ibd file and assigns a new table id for means that this function deletes the .ibd file and assigns a new table id for
the table. Also the flag table->ibd_file_missing is set TRUE. the table. Also the flag table->ibd_file_missing is set TRUE. */
How do we prevent crashes caused by ongoing operations on the table? Old int
row_discard_tablespace_for_mysql(
/*=============================*/
/* out: error code or DB_SUCCESS */
const char* name, /* in: table name */
trx_t* trx) /* in: transaction handle */
{
dict_foreign_t* foreign;
dulint new_id;
dict_table_t* table;
que_thr_t* thr;
que_t* graph = NULL;
ibool success;
ulint err;
char* buf;
/* How do we prevent crashes caused by ongoing operations on the table? Old
operations could try to access non-existent pages. operations could try to access non-existent pages.
1) SQL queries, INSERT, SELECT, ...: we must get an exclusive MySQL table lock 1) SQL queries, INSERT, SELECT, ...: we must get an exclusive MySQL table lock
...@@ -1984,22 +2028,9 @@ tablespace mem object with IMPORT TABLESPACE later, then the tablespace will ...@@ -1984,22 +2028,9 @@ tablespace mem object with IMPORT TABLESPACE later, then the tablespace will
have the same id, but the tablespace_version field in the mem object is have the same id, but the tablespace_version field in the mem object is
different, and ongoing old insert buffer page merges get discarded. different, and ongoing old insert buffer page merges get discarded.
4) Linear readahead and random readahead: we use the same method as in 3) to 4) Linear readahead and random readahead: we use the same method as in 3) to
discard ongoing operations. */ discard ongoing operations.
5) FOREIGN KEY operations: if table->n_foreign_key_checks_running > 0, we
int do not allow the discard. We also reserve the data dictionary latch. */
row_discard_tablespace_for_mysql(
/*=============================*/
/* out: error code or DB_SUCCESS */
const char* name, /* in: table name */
trx_t* trx) /* in: transaction handle */
{
dulint new_id;
dict_table_t* table;
que_thr_t* thr;
que_t* graph = NULL;
ibool success;
ulint err;
char* buf;
static const char discard_tablespace_proc1[] = static const char discard_tablespace_proc1[] =
"PROCEDURE DISCARD_TABLESPACE_PROC () IS\n" "PROCEDURE DISCARD_TABLESPACE_PROC () IS\n"
...@@ -2060,6 +2091,54 @@ row_discard_tablespace_for_mysql( ...@@ -2060,6 +2091,54 @@ row_discard_tablespace_for_mysql(
goto funct_exit; goto funct_exit;
} }
if (table->n_foreign_key_checks_running > 0) {
ut_print_timestamp(stderr);
fputs(" InnoDB: You are trying to DISCARD table ", stderr);
ut_print_name(stderr, trx, table->name);
fputs("\n"
"InnoDB: though there is a foreign key check running on it.\n"
"InnoDB: Cannot discard the table.\n",
stderr);
err = DB_ERROR;
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) {
FILE* ef = dict_foreign_err_file;
/* We only allow discarding a referenced table if
FOREIGN_KEY_CHECKS is set to 0 */
err = DB_CANNOT_DROP_CONSTRAINT;
mutex_enter(&dict_foreign_err_mutex);
rewind(ef);
ut_print_timestamp(ef);
fputs(" Cannot drop table ", ef);
ut_print_name(ef, trx, name);
fputs("\n"
"because it is referenced by ", ef);
ut_print_name(ef, trx, foreign->foreign_table_name);
putc('\n', ef);
mutex_exit(&dict_foreign_err_mutex);
goto funct_exit;
}
new_id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID); new_id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID);
buf = mem_alloc((sizeof discard_tablespace_proc1) + buf = mem_alloc((sizeof discard_tablespace_proc1) +
...@@ -2077,6 +2156,10 @@ row_discard_tablespace_for_mysql( ...@@ -2077,6 +2156,10 @@ row_discard_tablespace_for_mysql(
ut_a(graph); ut_a(graph);
/* Remove any locks there are on the table or its records */
lock_reset_all_on_table(table);
graph->trx = trx; graph->trx = trx;
trx->graph = NULL; trx->graph = NULL;
...@@ -2227,8 +2310,8 @@ row_import_tablespace_for_mysql( ...@@ -2227,8 +2310,8 @@ row_import_tablespace_for_mysql(
ibuf_delete_for_discarded_space(table->space); ibuf_delete_for_discarded_space(table->space);
success = fil_open_single_table_tablespace(table->space, table->name); success = fil_open_single_table_tablespace(TRUE, table->space,
table->name);
if (success) { if (success) {
table->ibd_file_missing = FALSE; table->ibd_file_missing = FALSE;
table->tablespace_discarded = FALSE; table->tablespace_discarded = FALSE;
...@@ -2236,7 +2319,7 @@ row_import_tablespace_for_mysql( ...@@ -2236,7 +2319,7 @@ row_import_tablespace_for_mysql(
if (table->ibd_file_missing) { if (table->ibd_file_missing) {
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
fputs( fputs(
" InnoDB: cannot find of open in the database directory the .ibd file of\n" " InnoDB: cannot find or open in the database directory the .ibd file of\n"
"InnoDB: table ", stderr); "InnoDB: table ", stderr);
ut_print_name(stderr, trx, name); ut_print_name(stderr, trx, name);
fputs("\n" fputs("\n"
...@@ -3284,6 +3367,20 @@ row_check_table_for_mysql( ...@@ -3284,6 +3367,20 @@ row_check_table_for_mysql(
ulint ret = DB_SUCCESS; ulint ret = DB_SUCCESS;
ulint old_isolation_level; ulint old_isolation_level;
if (prebuilt->table->ibd_file_missing) {
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB: Error:\n"
"InnoDB: MySQL is trying to use a table handle but the .ibd file for\n"
"InnoDB: table %s does not exist.\n"
"InnoDB: Have you deleted the .ibd file from the database directory under\n"
"InnoDB: the MySQL datadir, or have you used DISCARD TABLESPACE?\n"
"InnoDB: Look from\n"
"http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n"
"InnoDB: how you can resolve the problem.\n",
prebuilt->table->name);
return(DB_ERROR);
}
prebuilt->trx->op_info = "checking table"; prebuilt->trx->op_info = "checking table";
old_isolation_level = prebuilt->trx->isolation_level; old_isolation_level = prebuilt->trx->isolation_level;
......
...@@ -2776,7 +2776,7 @@ row_search_for_mysql( ...@@ -2776,7 +2776,7 @@ row_search_for_mysql(
/* out: DB_SUCCESS, /* out: DB_SUCCESS,
DB_RECORD_NOT_FOUND, DB_RECORD_NOT_FOUND,
DB_END_OF_INDEX, DB_DEADLOCK, DB_END_OF_INDEX, DB_DEADLOCK,
DB_LOCK_TABLE_FULL, DB_LOCK_TABLE_FULL, DB_CORRUPTION,
or DB_TOO_BIG_RECORD */ or DB_TOO_BIG_RECORD */
byte* buf, /* in/out: buffer for the fetched byte* buf, /* in/out: buffer for the fetched
row in the MySQL format */ row in the MySQL format */
...@@ -2828,7 +2828,21 @@ row_search_for_mysql( ...@@ -2828,7 +2828,21 @@ row_search_for_mysql(
ut_ad(index && pcur && search_tuple); ut_ad(index && pcur && search_tuple);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
if (prebuilt->table->ibd_file_missing) {
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB: Error:\n"
"InnoDB: MySQL is trying to use a table handle but the .ibd file for\n"
"InnoDB: table %s does not exist.\n"
"InnoDB: Have you deleted the .ibd file from the database directory under\n"
"InnoDB: the MySQL datadir, or have you used DISCARD TABLESPACE?\n"
"InnoDB: Look from\n"
"http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n"
"InnoDB: how you can resolve the problem.\n",
prebuilt->table->name);
return(DB_ERROR);
}
if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) {
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: trying to free a corrupt\n" "InnoDB: Error: trying to free a corrupt\n"
......
...@@ -1378,14 +1378,39 @@ NetWare. */ ...@@ -1378,14 +1378,39 @@ NetWare. */
return(DB_ERROR); return(DB_ERROR);
} }
/* Since ibuf init is in dict_boot, and ibuf is needed /* Since the insert buffer init is in dict_boot, and the
in any disk i/o, first call dict_boot */ insert buffer is needed in any disk i/o, first we call
dict_boot(). Note that trx_sys_init_at_db_start() only needs
to access space 0, and the insert buffer at this stage already
works for space 0. */
dict_boot(); dict_boot();
trx_sys_init_at_db_start(); trx_sys_init_at_db_start();
/* The following needs trx lists which are initialized in if (srv_force_recovery < SRV_FORCE_NO_IBUF_MERGE) {
trx_sys_init_at_db_start */ /* The following call is necessary for the insert
buffer to work with multiple tablespaces. We must
know the mapping between space id's and .ibd file
names.
In a crash recovery, we check that the info in data
dictionary is consistent with what we already know
about space id's from the call of
fil_load_single_table_tablespaces().
In a normal startup, we create the space objects for
every table in the InnoDB data dictionary that has
an .ibd file.
We also determine the maximum tablespace id used.
TODO: We may have incomplete transactions in the
data dictionary tables. Does that harm the scanning of
the data dictionary below? */
dict_check_tablespaces_and_store_max_id(
recv_needed_recovery);
}
srv_startup_is_before_trx_rollback_phase = FALSE; srv_startup_is_before_trx_rollback_phase = FALSE;
...@@ -1393,6 +1418,9 @@ NetWare. */ ...@@ -1393,6 +1418,9 @@ NetWare. */
system */ system */
fsp_header_get_free_limit(0); fsp_header_get_free_limit(0);
/* recv_recovery_from_checkpoint_finish needs trx lists which
are initialized in trx_sys_init_at_db_start(). */
recv_recovery_from_checkpoint_finish(); recv_recovery_from_checkpoint_finish();
} }
...@@ -1433,13 +1461,6 @@ NetWare. */ ...@@ -1433,13 +1461,6 @@ NetWare. */
} }
} }
#endif /* UNIV_LOG_ARCHIVE */ #endif /* UNIV_LOG_ARCHIVE */
if (!create_new_db && srv_force_recovery == 0) {
/* After a crash recovery we only check that the info in data
dictionary is consistent with what we already know about space
id's from the call of fil_load_single_table_tablespaces(). */
dict_check_tablespaces_or_store_max_id(recv_needed_recovery);
}
if (srv_measure_contention) { if (srv_measure_contention) {
/* os_thread_create(&test_measure_cont, NULL, thread_ids + /* os_thread_create(&test_measure_cont, NULL, thread_ids +
......
...@@ -109,6 +109,9 @@ trx_create( ...@@ -109,6 +109,9 @@ trx_create(
trx->mysql_log_offset = 0; trx->mysql_log_offset = 0;
trx->mysql_master_log_file_name = ""; trx->mysql_master_log_file_name = "";
trx->mysql_master_log_pos = 0; trx->mysql_master_log_pos = 0;
trx->repl_wait_binlog_name = NULL;
trx->repl_wait_binlog_pos = 0;
mutex_create(&(trx->undo_mutex)); mutex_create(&(trx->undo_mutex));
mutex_set_level(&(trx->undo_mutex), SYNC_TRX_UNDO); mutex_set_level(&(trx->undo_mutex), SYNC_TRX_UNDO);
...@@ -271,6 +274,11 @@ trx_free( ...@@ -271,6 +274,11 @@ trx_free(
trx_undo_arr_free(trx->undo_no_arr); trx_undo_arr_free(trx->undo_no_arr);
} }
if (trx->repl_wait_binlog_name != NULL) {
mem_free(trx->repl_wait_binlog_name);
}
ut_a(UT_LIST_GET_LEN(trx->signals) == 0); ut_a(UT_LIST_GET_LEN(trx->signals) == 0);
ut_a(UT_LIST_GET_LEN(trx->reply_signals) == 0); ut_a(UT_LIST_GET_LEN(trx->reply_signals) == 0);
......
...@@ -4977,7 +4977,7 @@ the SQL statement in case of an error. */ ...@@ -4977,7 +4977,7 @@ the SQL statement in case of an error. */
int int
ha_innobase::external_lock( ha_innobase::external_lock(
/*=======================*/ /*=======================*/
/* out: 0 */ /* out: 0 or HA_ERR_CRASHED */
THD* thd, /* in: handle to the user thread */ THD* thd, /* in: handle to the user thread */
int lock_type) /* in: lock type */ int lock_type) /* in: lock type */
{ {
...@@ -4989,19 +4989,6 @@ ha_innobase::external_lock( ...@@ -4989,19 +4989,6 @@ ha_innobase::external_lock(
update_thd(thd); update_thd(thd);
if (prebuilt->table->ibd_file_missing && !current_thd->tablespace_op) {
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB error:\n"
"MySQL is trying to use a table handle but the .ibd file for\n"
"table %s does not exist.\n"
"Have you deleted the .ibd file from the database directory under\n"
"the MySQL datadir, or have you used DISCARD TABLESPACE?\n"
"Look from section 15.1 of http://www.innodb.com/ibman.html\n"
"how you can resolve the problem.\n",
prebuilt->table->name);
DBUG_RETURN(HA_ERR_CRASHED);
}
trx = prebuilt->trx; trx = prebuilt->trx;
prebuilt->sql_stat_start = TRUE; prebuilt->sql_stat_start = TRUE;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment