summaryrefslogtreecommitdiff
path: root/innobase/row/row0mysql.c
diff options
context:
space:
mode:
authorunknown <serg@sergbook.mysql.com>2004-12-31 15:26:24 +0100
committerunknown <serg@sergbook.mysql.com>2004-12-31 15:26:24 +0100
commitb99bea6704299399c84bd8374ef7f248ff9570c5 (patch)
tree928b06c231edee4cb95ace87ed30e879e3dcb0fb /innobase/row/row0mysql.c
parentbd23099be0b22845e8c97e2fb36ee3c6eb0e2491 (diff)
parent5eaf65ab4be77911eb03cceefac9ecea48c25f71 (diff)
downloadmariadb-git-b99bea6704299399c84bd8374ef7f248ff9570c5.tar.gz
manually merged
configure.in: Auto merged client/mysqldump.c: Auto merged innobase/buf/buf0rea.c: Auto merged innobase/dict/dict0load.c: Auto merged innobase/fil/fil0fil.c: Auto merged innobase/include/fil0fil.h: Auto merged innobase/include/row0mysql.h: Auto merged innobase/include/trx0trx.h: Auto merged innobase/os/os0file.c: Auto merged innobase/row/row0ins.c: Auto merged innobase/row/row0mysql.c: Auto merged innobase/row/row0sel.c: Auto merged innobase/srv/srv0start.c: Auto merged innobase/trx/trx0trx.c: Auto merged libmysqld/lib_sql.cc: Auto merged mysql-test/mysql-test-run.sh: Auto merged mysql-test/r/alter_table.result: Auto merged mysql-test/r/ctype_ucs.result: Auto merged mysql-test/r/func_str.result: Auto merged mysql-test/r/grant.result: Auto merged mysql-test/r/grant_cache.result: Auto merged mysql-test/r/merge.result: Auto merged mysql-test/r/ndb_blob.result: Auto merged mysql-test/r/ps_1general.result: Auto merged mysql-test/r/subselect.result: Auto merged mysql-test/r/timezone2.result: Auto merged mysql-test/t/ctype_ucs.test: Auto merged mysql-test/t/grant.test: Auto merged mysql-test/t/merge.test: Auto merged mysql-test/t/multi_update.test: Auto merged mysql-test/t/mysqldump.test: Auto merged mysql-test/t/subselect.test: Auto merged ndb/src/ndbapi/NdbBlob.cpp: Auto merged ndb/src/ndbapi/NdbConnection.cpp: Auto merged ndb/src/ndbapi/NdbDictionaryImpl.cpp: Auto merged ndb/src/ndbapi/NdbOperationDefine.cpp: Auto merged sql/ha_innodb.cc: Auto merged sql/ha_myisammrg.cc: Auto merged sql/ha_myisammrg.h: Auto merged sql/item_cmpfunc.cc: Auto merged sql/item_func.cc: Auto merged sql/item_strfunc.cc: Auto merged sql/log.cc: Auto merged sql/mysql_priv.h: Auto merged sql/mysqld.cc: Auto merged sql/set_var.cc: Auto merged sql-common/client.c: Auto merged sql/sql_table.cc: Auto merged sql/strfunc.cc: Auto merged sql/unireg.cc: Auto merged vio/vio.c: Auto merged vio/viosocket.c: Auto merged
Diffstat (limited to 'innobase/row/row0mysql.c')
-rw-r--r--innobase/row/row0mysql.c268
1 files changed, 205 insertions, 63 deletions
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index 0a2e9e5eaad..380fcf236ed 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -875,7 +875,21 @@ row_insert_for_mysql(
ut_ad(trx);
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) {
fprintf(stderr,
"InnoDB: Error: trying to free a corrupt\n"
@@ -1094,6 +1108,20 @@ row_update_for_mysql(
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
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) {
fprintf(stderr,
"InnoDB: Error: trying to free a corrupt\n"
@@ -1865,20 +1893,19 @@ row_drop_table_for_mysql_in_background(
trx = trx_allocate_for_background();
+ /* If the original transaction was dropping a table referenced by
+ foreign keys, we must set the following to be able to drop the
+ table: */
+
+ trx->check_foreigns = FALSE;
+
/* fputs("InnoDB: Error: Dropping table ", stderr);
ut_print_name(stderr, name);
fputs(" in background drop list\n", stderr); */
- /* Drop the table in InnoDB */
+ /* Try to drop the table in InnoDB */
error = row_drop_table_for_mysql(name, trx, FALSE);
-
- if (error != DB_SUCCESS) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Error: Dropping table ", stderr);
- ut_print_name(stderr, trx, name);
- fputs(" in background drop list failed\n", stderr);
- }
/* Flush the log to reduce probability that the .frm files and
the InnoDB data dictionary get out-of-sync if the user runs
@@ -1890,7 +1917,7 @@ row_drop_table_for_mysql_in_background(
trx_free_for_background(trx);
- return(DB_SUCCESS);
+ return(error);
}
/*************************************************************************
@@ -1924,6 +1951,7 @@ loop:
mutex_exit(&kernel_mutex);
if (drop == NULL) {
+ /* All tables dropped */
return(n_tables + n_tables_dropped);
}
@@ -1938,16 +1966,16 @@ loop:
goto already_dropped;
}
-
- if (table->n_mysql_handles_opened > 0
- || table->n_foreign_key_checks_running > 0) {
+
+ if (DB_SUCCESS != row_drop_table_for_mysql_in_background(
+ drop->table_name)) {
+ /* If the DROP fails for some table, we return, and let the
+ main thread retry later */
return(n_tables + n_tables_dropped);
}
n_tables_dropped++;
-
- row_drop_table_for_mysql_in_background(drop->table_name);
already_dropped:
mutex_enter(&kernel_mutex);
@@ -1991,21 +2019,21 @@ row_get_background_drop_list_len_low(void)
}
/*************************************************************************
-Adds a table to the list of tables which the master thread drops in
-background. We need this on Unix because in ALTER TABLE MySQL may call
-drop table even if the table has running queries on it. */
+If a table is not yet in the drop list, adds the table to the list of tables
+which the master thread drops in background. We need this on Unix because in
+ALTER TABLE MySQL may call drop table even if the table has running queries on
+it. Also, if there are running foreign key checks on the table, we drop the
+table lazily. */
static
-void
+ibool
row_add_table_to_background_drop_list(
/*==================================*/
+ /* out: TRUE if the table was not yet in the
+ drop list, and was added there */
dict_table_t* table) /* in: table */
{
row_mysql_drop_t* drop;
- drop = mem_alloc(sizeof(row_mysql_drop_t));
-
- drop->table_name = mem_strdup(table->name);
-
mutex_enter(&kernel_mutex);
if (!row_mysql_drop_list_inited) {
@@ -2013,7 +2041,26 @@ row_add_table_to_background_drop_list(
UT_LIST_INIT(row_mysql_drop_list);
row_mysql_drop_list_inited = TRUE;
}
+
+ /* Look if the table already is in the drop list */
+ drop = UT_LIST_GET_FIRST(row_mysql_drop_list);
+
+ while (drop != NULL) {
+ if (strcmp(drop->table_name, table->name) == 0) {
+ /* Already in the list */
+
+ mutex_exit(&kernel_mutex);
+
+ return(FALSE);
+ }
+
+ drop = UT_LIST_GET_NEXT(row_mysql_drop_list, drop);
+ }
+
+ drop = mem_alloc(sizeof(row_mysql_drop_t));
+ drop->table_name = mem_strdup(table->name);
+
UT_LIST_ADD_LAST(row_mysql_drop_list, row_mysql_drop_list, drop);
/* fputs("InnoDB: Adding table ", stderr);
@@ -2021,14 +2068,32 @@ row_add_table_to_background_drop_list(
fputs(" to background drop list\n", stderr); */
mutex_exit(&kernel_mutex);
+
+ return(TRUE);
}
/*************************************************************************
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
-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.
1) SQL queries, INSERT, SELECT, ...: we must get an exclusive MySQL table lock
@@ -2044,22 +2109,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
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
-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;
+discard ongoing operations.
+5) FOREIGN KEY operations: if table->n_foreign_key_checks_running > 0, we
+do not allow the discard. We also reserve the data dictionary latch. */
static const char discard_tablespace_proc1[] =
"PROCEDURE DISCARD_TABLESPACE_PROC () IS\n"
@@ -2120,6 +2172,54 @@ row_discard_tablespace_for_mysql(
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 DISCARD 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);
buf = mem_alloc((sizeof discard_tablespace_proc1) +
@@ -2137,6 +2237,10 @@ row_discard_tablespace_for_mysql(
ut_a(graph);
+ /* Remove any locks there are on the table or its records */
+
+ lock_reset_all_on_table(table);
+
graph->trx = trx;
trx->graph = NULL;
@@ -2287,8 +2391,8 @@ row_import_tablespace_for_mysql(
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) {
table->ibd_file_missing = FALSE;
table->tablespace_discarded = FALSE;
@@ -2296,7 +2400,7 @@ row_import_tablespace_for_mysql(
if (table->ibd_file_missing) {
ut_print_timestamp(stderr);
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);
ut_print_name(stderr, trx, name);
fputs("\n"
@@ -2318,7 +2422,7 @@ funct_exit:
}
/*************************************************************************
-Drops a table for MySQL. If the name of the table to be dropped is equal
+Drops a table for MySQL. If the name of the table to be dropped is equal
with one of the predefined magic table names, then this also stops printing
the corresponding monitor output by the master thread. */
@@ -2552,36 +2656,60 @@ row_drop_table_for_mysql(
}
if (table->n_mysql_handles_opened > 0) {
+ ibool added;
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Warning: MySQL is trying to drop table ",
- stderr);
- ut_print_name(stderr, trx, table->name);
- fputs("\n"
- "InnoDB: though there are still open handles to it.\n"
- "InnoDB: Adding the table to the background drop queue.\n",
- stderr);
+ added = row_add_table_to_background_drop_list(table);
- row_add_table_to_background_drop_list(table);
+ if (added) {
+ ut_print_timestamp(stderr);
+fputs(" InnoDB: Warning: MySQL is trying to drop table ", stderr);
+ ut_print_name(stderr, trx, table->name);
+ fputs("\n"
+"InnoDB: though there are still open handles to it.\n"
+"InnoDB: Adding the table to the background drop queue.\n",
+ stderr);
+
+ /* We return DB_SUCCESS to MySQL though the drop will
+ happen lazily later */
- err = DB_SUCCESS;
+ err = DB_SUCCESS;
+ } else {
+ /* The table is already in the background drop list */
+ err = DB_ERROR;
+ }
goto funct_exit;
}
+ /* TODO: could we replace the counter n_foreign_key_checks_running
+ with lock checks on the table? Acquire here an exclusive lock on the
+ table, and rewrite lock0lock.c and the lock wait in srv0srv.c so that
+ they can cope with the table having been dropped here? Foreign key
+ checks take an IS or IX lock on the table. */
+
if (table->n_foreign_key_checks_running > 0) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: You are trying to drop table ", stderr);
- ut_print_name(stderr, trx, table->name);
- fputs("\n"
- "InnoDB: though there is a foreign key check running on it.\n"
- "InnoDB: Adding the table to the background drop queue.\n",
+ ibool added;
+
+ added = row_add_table_to_background_drop_list(table);
+
+ if (added) {
+ ut_print_timestamp(stderr);
+fputs(" InnoDB: You are trying to drop table ", stderr);
+ ut_print_name(stderr, trx, table->name);
+ fputs("\n"
+"InnoDB: though there is a foreign key check running on it.\n"
+"InnoDB: Adding the table to the background drop queue.\n",
stderr);
- row_add_table_to_background_drop_list(table);
+ /* We return DB_SUCCESS to MySQL though the drop will
+ happen lazily later */
- err = DB_SUCCESS;
+ err = DB_SUCCESS;
+ } else {
+ /* The table is already in the background drop list */
+ err = DB_ERROR;
+ }
goto funct_exit;
}
@@ -3349,6 +3477,20 @@ row_check_table_for_mysql(
ulint ret = DB_SUCCESS;
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";
old_isolation_level = prebuilt->trx->isolation_level;