summaryrefslogtreecommitdiff
path: root/innobase/row/row0mysql.c
diff options
context:
space:
mode:
Diffstat (limited to 'innobase/row/row0mysql.c')
-rw-r--r--innobase/row/row0mysql.c793
1 files changed, 663 insertions, 130 deletions
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index ad5efc160c8..47f1f4c444c 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -22,12 +22,15 @@ Created 9/17/2000 Heikki Tuuri
#include "dict0dict.h"
#include "dict0crea.h"
#include "dict0load.h"
+#include "dict0boot.h"
#include "trx0roll.h"
#include "trx0purge.h"
#include "lock0lock.h"
#include "rem0cmp.h"
#include "log0log.h"
#include "btr0sea.h"
+#include "fil0fil.h"
+#include "ibuf0ibuf.h"
/* A dummy variable used to fool the compiler */
ibool row_mysql_identically_false = FALSE;
@@ -117,6 +120,19 @@ row_mysql_read_var_ref_noninline(
}
/***********************************************************************
+Frees the blob heap in prebuilt when no longer needed. */
+
+void
+row_mysql_prebuilt_free_blob_heap(
+/*==============================*/
+ row_prebuilt_t* prebuilt) /* in: prebuilt struct of a
+ ha_innobase:: table handle */
+{
+ mem_heap_free(prebuilt->blob_heap);
+ prebuilt->blob_heap = NULL;
+}
+
+/***********************************************************************
Stores a reference to a BLOB in the MySQL format. */
void
@@ -292,7 +308,8 @@ handle_new_error:
return(TRUE);
- } else if (err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT) {
+ } else if (err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT
+ || err == DB_LOCK_TABLE_FULL) {
/* Roll back the whole transaction; this resolution was added
to version 3.23.43 */
@@ -317,7 +334,7 @@ handle_new_error:
exit(1);
} else if (err == DB_CORRUPTION) {
- fputs(
+ fputs(
"InnoDB: We detected index corruption in an InnoDB type table.\n"
"InnoDB: You have to dump + drop + reimport the table or, in\n"
"InnoDB: a case of widespread corruption, dump all InnoDB\n"
@@ -328,7 +345,8 @@ handle_new_error:
" for help.\n", stderr);
} else {
- fprintf(stderr, "InnoDB: unknown error code %lu\n", err);
+ fprintf(stderr, "InnoDB: unknown error code %lu\n",
+ (ulong) err);
ut_error;
}
@@ -438,9 +456,10 @@ row_prebuilt_free(
|| prebuilt->magic_n2 != ROW_PREBUILT_ALLOCATED) {
fprintf(stderr,
"InnoDB: Error: trying to free a corrupt\n"
-"InnoDB: table handle. Magic n %lu, magic n2 %lu, table name ",
- prebuilt->magic_n, prebuilt->magic_n2);
- ut_print_name(stderr, prebuilt->table->name);
+"InnoDB: table handle. Magic n %lu, magic n2 %lu, table name",
+ (ulong) prebuilt->magic_n,
+ (ulong) prebuilt->magic_n2);
+ ut_print_name(stderr, NULL, prebuilt->table->name);
putc('\n', stderr);
mem_analyze_corruption((byte*)prebuilt);
@@ -521,7 +540,7 @@ row_update_prebuilt_trx(
fprintf(stderr,
"InnoDB: Error: trying to use a corrupt\n"
"InnoDB: trx handle. Magic n %lu\n",
- trx->magic_n);
+ (ulong) trx->magic_n);
mem_analyze_corruption((byte*)trx);
@@ -531,9 +550,9 @@ row_update_prebuilt_trx(
if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) {
fprintf(stderr,
"InnoDB: Error: trying to use a corrupt\n"
- "InnoDB: table handle. Magic n %lu, table name ",
- prebuilt->magic_n);
- ut_print_name(stderr, prebuilt->table->name);
+ "InnoDB: table handle. Magic n %lu, table name",
+ (ulong) prebuilt->magic_n);
+ ut_print_name(stderr, NULL, prebuilt->table->name);
putc('\n', stderr);
mem_analyze_corruption((byte*)prebuilt);
@@ -688,7 +707,7 @@ row_lock_table_autoinc_for_mysql(
return(DB_SUCCESS);
}
- trx->op_info = (char *) "setting auto-inc lock";
+ trx->op_info = "setting auto-inc lock";
if (node == NULL) {
row_get_prebuilt_insert_row(prebuilt);
@@ -724,14 +743,14 @@ run_again:
goto run_again;
}
- trx->op_info = (char *) "";
+ trx->op_info = "";
return(err);
}
que_thr_stop_for_mysql_no_error(thr, trx);
- trx->op_info = (char *) "";
+ trx->op_info = "";
return((int) err);
}
@@ -761,8 +780,13 @@ int
row_lock_table_for_mysql(
/*=====================*/
/* out: error code or DB_SUCCESS */
- row_prebuilt_t* prebuilt) /* in: prebuilt struct in the MySQL
+ row_prebuilt_t* prebuilt, /* in: prebuilt struct in the MySQL
table handle */
+ dict_table_t* table, /* in: table to lock, or NULL
+ if prebuilt->table should be
+ locked as LOCK_TABLE_EXP |
+ prebuilt->select_lock_type */
+ ulint mode) /* in: lock mode of table */
{
trx_t* trx = prebuilt->trx;
que_thr_t* thr;
@@ -772,7 +796,7 @@ row_lock_table_for_mysql(
ut_ad(trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
- trx->op_info = (char *) "setting table lock";
+ trx->op_info = "setting table lock";
if (prebuilt->sel_graph == NULL) {
/* Build a dummy select query graph */
@@ -795,8 +819,12 @@ run_again:
trx_start_if_not_started(trx);
- err = lock_table(LOCK_TABLE_EXP, prebuilt->table,
- prebuilt->select_lock_type, thr);
+ if (table) {
+ err = lock_table(0, table, mode, thr);
+ } else {
+ err = lock_table(LOCK_TABLE_EXP, prebuilt->table,
+ prebuilt->select_lock_type, thr);
+ }
trx->error_state = err;
@@ -809,14 +837,14 @@ run_again:
goto run_again;
}
- trx->op_info = (char *) "";
+ trx->op_info = "";
return(err);
}
que_thr_stop_for_mysql_no_error(thr, trx);
- trx->op_info = (char *) "";
+ trx->op_info = "";
return((int) err);
}
@@ -841,13 +869,27 @@ 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"
- "InnoDB: table handle. Magic n %lu, table name ",
- prebuilt->magic_n);
- ut_print_name(stderr, prebuilt->table->name);
+ "InnoDB: table handle. Magic n %lu, table name",
+ (ulong) prebuilt->magic_n);
+ ut_print_name(stderr, prebuilt->trx, prebuilt->table->name);
putc('\n', stderr);
mem_analyze_corruption((byte*)prebuilt);
@@ -867,7 +909,7 @@ row_insert_for_mysql(
return(DB_ERROR);
}
- trx->op_info = (char *) "inserting";
+ trx->op_info = "inserting";
row_mysql_delay_if_needed();
@@ -910,7 +952,7 @@ run_again:
goto run_again;
}
- trx->op_info = (char *) "";
+ trx->op_info = "";
return(err);
}
@@ -927,7 +969,7 @@ run_again:
}
row_update_statistics_if_needed(prebuilt->table);
- trx->op_info = (char *) "";
+ trx->op_info = "";
return((int) err);
}
@@ -1059,12 +1101,26 @@ 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"
- "InnoDB: table handle. Magic n %lu, table name ",
- prebuilt->magic_n);
- ut_print_name(stderr, prebuilt->table->name);
+ "InnoDB: table handle. Magic n %lu, table name",
+ (ulong) prebuilt->magic_n);
+ ut_print_name(stderr, prebuilt->trx, prebuilt->table->name);
putc('\n', stderr);
mem_analyze_corruption((byte*)prebuilt);
@@ -1084,7 +1140,7 @@ row_update_for_mysql(
return(DB_ERROR);
}
- trx->op_info = (char *) "updating or deleting";
+ trx->op_info = "updating or deleting";
row_mysql_delay_if_needed();
@@ -1133,7 +1189,7 @@ run_again:
if (err == DB_RECORD_NOT_FOUND) {
trx->error_state = DB_SUCCESS;
- trx->op_info = (char *) "";
+ trx->op_info = "";
return((int) err);
}
@@ -1144,7 +1200,7 @@ run_again:
goto run_again;
}
- trx->op_info = (char *) "";
+ trx->op_info = "";
return(err);
}
@@ -1163,7 +1219,7 @@ run_again:
row_update_statistics_if_needed(prebuilt->table);
- trx->op_info = (char *) "";
+ trx->op_info = "";
return((int) err);
}
@@ -1368,7 +1424,8 @@ row_mysql_lock_data_dictionary(
/*===========================*/
trx_t* trx) /* in: transaction */
{
- ut_a(trx->dict_operation_lock_mode == 0);
+ ut_a(trx->dict_operation_lock_mode == 0
+ || trx->dict_operation_lock_mode == RW_X_LATCH);
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks or lock waits can occur then in these operations */
@@ -1403,6 +1460,7 @@ Does a table creation operation for MySQL. If the name of the table
to be created is equal with one of the predefined magic table names,
then this also starts printing the corresponding monitor output by
the master thread. */
+
int
row_create_table_for_mysql(
/*=======================*/
@@ -1438,7 +1496,7 @@ row_create_table_for_mysql(
return(DB_ERROR);
}
- trx->op_info = (char *) "creating table";
+ trx->op_info = "creating table";
if (row_mysql_is_system_table(table->name)) {
@@ -1549,16 +1607,15 @@ row_create_table_for_mysql(
if (err == DB_OUT_OF_FILE_SPACE) {
fputs("InnoDB: Warning: cannot create table ", stderr);
- ut_print_name(stderr, table->name);
+ ut_print_name(stderr, trx, table->name);
fputs(" because tablespace full\n", stderr);
row_drop_table_for_mysql(table->name, trx, FALSE);
- } else {
- ut_a(err == DB_DUPLICATE_KEY);
+ } else if (err == DB_DUPLICATE_KEY) {
ut_print_timestamp(stderr);
fputs(" InnoDB: Error: table ", stderr);
- ut_print_name(stderr, table->name);
+ ut_print_name(stderr, trx, table->name);
fputs(" already exists in InnoDB internal\n"
"InnoDB: data dictionary. Have you deleted the .frm file\n"
"InnoDB: and not used DROP TABLE? Have you used DROP DATABASE\n"
@@ -1566,20 +1623,23 @@ row_create_table_for_mysql(
"InnoDB: See the Restrictions section of the InnoDB manual.\n"
"InnoDB: You can drop the orphaned table inside InnoDB by\n"
"InnoDB: creating an InnoDB table with the same name in another\n"
- "InnoDB: database and moving the .frm file to the current database.\n"
+ "InnoDB: database and copying the .frm file to the current database.\n"
"InnoDB: Then MySQL thinks the table exists, and DROP TABLE will\n"
"InnoDB: succeed.\n"
"InnoDB: You can look for further help from\n"
"InnoDB: http://dev.mysql.com/doc/mysql/en/"
"InnoDB_troubleshooting_datadict.html\n", stderr);
}
+
+ /* We may also get err == DB_ERROR if the .ibd file for the
+ table already exists */
trx->error_state = DB_SUCCESS;
}
que_graph_free((que_t*) que_node_get_parent(thr));
- trx->op_info = (char *) "";
+ trx->op_info = "";
return((int) err);
}
@@ -1608,7 +1668,9 @@ row_create_index_for_mysql(
#endif /* UNIV_SYNC_DEBUG */
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
- trx->op_info = (char *) "creating index";
+ trx->op_info = "creating index";
+
+ trx_start_if_not_started(trx);
/* Check that the same column does not appear twice in the index.
Starting from 4.0.14, InnoDB should be able to cope with that, but
@@ -1623,10 +1685,10 @@ row_create_index_for_mysql(
ut_print_timestamp(stderr);
fputs(" InnoDB: Error: column ", stderr);
- ut_print_name(stderr,
+ ut_print_name(stderr, trx,
dict_index_get_nth_field(index, i)->name);
fputs(" appears twice in ", stderr);
- dict_index_name_print(stderr, index);
+ dict_index_name_print(stderr, trx, index);
fputs("\n"
"InnoDB: This is not allowed in InnoDB.\n",
stderr);
@@ -1636,9 +1698,16 @@ row_create_index_for_mysql(
goto error_handling;
}
}
- }
+
+ /* Check also that prefix_len < DICT_MAX_COL_PREFIX_LEN */
- trx_start_if_not_started(trx);
+ if (dict_index_get_nth_field(index, i)->prefix_len
+ >= DICT_MAX_COL_PREFIX_LEN) {
+ err = DB_TOO_BIG_RECORD;
+
+ goto error_handling;
+ }
+ }
if (row_mysql_is_recovered_tmp_table(index->table_name)) {
@@ -1649,6 +1718,9 @@ row_create_index_for_mysql(
trx->dict_operation = TRUE;
+ /* Note that the space id where we store the index is inherited from
+ the table in dict_build_index_def_step() in dict0crea.c. */
+
node = ind_create_graph_create(index, heap);
thr = pars_complete_graph_for_exec(node, trx, heap);
@@ -1661,7 +1733,6 @@ row_create_index_for_mysql(
que_graph_free((que_t*) que_node_get_parent(thr));
error_handling:
-
if (err != DB_SUCCESS) {
/* We have special error handling here */
@@ -1674,7 +1745,7 @@ error_handling:
trx->error_state = DB_SUCCESS;
}
- trx->op_info = (char *) "";
+ trx->op_info = "";
return((int) err);
}
@@ -1691,15 +1762,16 @@ constraints which reference this table are ok. */
int
row_table_add_foreign_constraints(
/*==============================*/
- /* out: error code or DB_SUCCESS */
- trx_t* trx, /* in: transaction */
- char* sql_string, /* in: table create statement where
- foreign keys are declared like:
+ /* out: error code or DB_SUCCESS */
+ trx_t* trx, /* in: transaction */
+ const char* sql_string, /* in: table create statement where
+ foreign keys are declared like:
FOREIGN KEY (a, b) REFERENCES table2(c, d),
- table2 can be written also with the database
- name before it: test.table2 */
- char* name) /* in: table full name in the normalized form
- database_name/table_name */
+ table2 can be written also with the
+ database name before it: test.table2 */
+ const char* name) /* in: table full name in the
+ normalized form
+ database_name/table_name */
{
ulint err;
@@ -1709,7 +1781,7 @@ row_table_add_foreign_constraints(
#endif /* UNIV_SYNC_DEBUG */
ut_a(sql_string);
- trx->op_info = (char *) "adding foreign keys";
+ trx->op_info = "adding foreign keys";
trx_start_if_not_started(trx);
@@ -1753,8 +1825,8 @@ static
int
row_drop_table_for_mysql_in_background(
/*===================================*/
- /* out: error code or DB_SUCCESS */
- char* name) /* in: table name */
+ /* out: error code or DB_SUCCESS */
+ const char* name) /* in: table name */
{
ulint error;
trx_t* trx;
@@ -1778,7 +1850,7 @@ row_drop_table_for_mysql_in_background(
if (error != DB_SUCCESS) {
ut_print_timestamp(stderr);
fputs(" InnoDB: Error: Dropping table ", stderr);
- ut_print_name(stderr, name);
+ ut_print_name(stderr, trx, name);
fputs(" in background drop list failed\n", stderr);
}
@@ -1857,9 +1929,9 @@ already_dropped:
UT_LIST_REMOVE(row_mysql_drop_list, row_mysql_drop_list, drop);
ut_print_timestamp(stderr);
- fputs(" InnoDB: Dropped table ", stderr);
- ut_print_name(stderr, drop->table_name);
- fputs(" in background drop queue.\n", stderr);
+ fprintf(stderr,
+ " InnoDB: Dropped table %s in background drop queue.\n",
+ drop->table_name);
mem_free(drop->table_name);
@@ -1926,6 +1998,355 @@ row_add_table_to_background_drop_list(
}
/*************************************************************************
+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. */
+
+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
+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.
+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"
+ "old_id CHAR;\n"
+ "new_id CHAR;\n"
+ "new_id_low INT;\n"
+ "new_id_high INT;\n"
+ "table_name CHAR;\n"
+ "BEGIN\n"
+ "table_name := '";
+ static const char discard_tablespace_proc2[] =
+ "';\n"
+ "new_id_high := %lu;\n"
+ "new_id_low := %lu;\n"
+ "new_id := CONCAT(TO_BINARY(new_id_high, 4), TO_BINARY(new_id_low, 4));\n"
+ "SELECT ID INTO old_id\n"
+ "FROM SYS_TABLES\n"
+ "WHERE NAME = table_name;\n"
+ "IF (SQL %% NOTFOUND) THEN\n"
+ " COMMIT WORK;\n"
+ " RETURN;\n"
+ "END IF;\n"
+ "UPDATE SYS_TABLES SET ID = new_id\n"
+ "WHERE ID = old_id;\n"
+ "UPDATE SYS_COLUMNS SET TABLE_ID = new_id\n"
+ "WHERE TABLE_ID = old_id;\n"
+ "UPDATE SYS_INDEXES SET TABLE_ID = new_id\n"
+ "WHERE TABLE_ID = old_id;\n"
+ "COMMIT WORK;\n"
+ "END;\n";
+
+ ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
+
+ trx->op_info = "discarding tablespace";
+ trx_start_if_not_started(trx);
+
+ /* Serialize data dictionary operations with dictionary mutex:
+ no deadlocks can occur then in these operations */
+
+ row_mysql_lock_data_dictionary(trx);
+
+ table = dict_table_get_low(name);
+
+ if (!table) {
+ err = DB_TABLE_NOT_FOUND;
+
+ goto funct_exit;
+ }
+
+ if (table->space == 0) {
+ ut_print_timestamp(stderr);
+ fputs(" InnoDB: Error: table ", stderr);
+ ut_print_name(stderr, trx, name);
+ fputs("\n"
+"InnoDB: is in the system tablespace 0 which cannot be discarded\n", stderr);
+ err = DB_ERROR;
+
+ 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) +
+ (sizeof discard_tablespace_proc2) +
+ 20 + ut_strlenq(name, '\''));
+
+ memcpy(buf, discard_tablespace_proc1, sizeof discard_tablespace_proc1);
+ sprintf(ut_strcpyq(buf + (sizeof discard_tablespace_proc1 - 1),
+ '\'', name),
+ discard_tablespace_proc2,
+ (ulong) ut_dulint_get_high(new_id),
+ (ulong) ut_dulint_get_low(new_id));
+
+ graph = pars_sql(buf);
+
+ 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;
+
+ graph->fork_type = QUE_FORK_MYSQL_INTERFACE;
+
+ ut_a(thr = que_fork_start_command(graph));
+
+ que_run_threads(thr);
+
+ err = trx->error_state;
+
+ if (err != DB_SUCCESS) {
+ trx->error_state = DB_SUCCESS;
+ trx_general_rollback_for_mysql(trx, FALSE, NULL);
+ trx->error_state = DB_SUCCESS;
+ } else {
+ dict_table_change_id_in_cache(table, new_id);
+
+ success = fil_discard_tablespace(table->space);
+
+ if (!success) {
+ trx->error_state = DB_SUCCESS;
+ trx_general_rollback_for_mysql(trx, FALSE, NULL);
+ trx->error_state = DB_SUCCESS;
+
+ err = DB_ERROR;
+ } else {
+ /* Set the flag which tells that now it is legal to
+ IMPORT a tablespace for this table */
+ table->tablespace_discarded = TRUE;
+ table->ibd_file_missing = TRUE;
+ }
+ }
+funct_exit:
+ row_mysql_unlock_data_dictionary(trx);
+
+ if (graph) {
+ que_graph_free(graph);
+ }
+
+ trx_commit_for_mysql(trx);
+
+ trx->op_info = "";
+
+ return((int) err);
+}
+
+/*********************************************************************
+Imports a tablespace. The space id in the .ibd file must match the space id
+of the table in the data dictionary. */
+
+int
+row_import_tablespace_for_mysql(
+/*============================*/
+ /* out: error code or DB_SUCCESS */
+ const char* name, /* in: table name */
+ trx_t* trx) /* in: transaction handle */
+{
+ dict_table_t* table;
+ ibool success;
+ dulint current_lsn;
+ ulint err = DB_SUCCESS;
+
+ ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
+
+ trx_start_if_not_started(trx);
+
+ trx->op_info = "importing tablespace";
+
+ current_lsn = log_get_lsn();
+
+ /* 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 merge, or rollback was performed on a backup
+ taken with ibbackup. If that is the case, reset page lsn's in the
+ file. We assume that mysqld was shut down after it performed these
+ cleanup operations on the .ibd file, so that it stamped the latest lsn
+ to the FIL_PAGE_FILE_FLUSH_LSN in the first page of the .ibd file.
+
+ TODO: reset also the trx id's in clustered index records and write
+ a new space id to each data page. That would allow us to import clean
+ .ibd files from another MySQL installation. */
+
+ success = fil_reset_too_high_lsns(name, current_lsn);
+
+ if (!success) {
+ ut_print_timestamp(stderr);
+ fputs(" InnoDB: Error: cannot reset lsn's in table ", stderr);
+ ut_print_name(stderr, trx, name);
+ fputs("\n"
+ "InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n", stderr);
+
+ err = DB_ERROR;
+
+ row_mysql_lock_data_dictionary(trx);
+
+ goto funct_exit;
+ }
+
+ /* Serialize data dictionary operations with dictionary mutex:
+ no deadlocks can occur then in these operations */
+
+ row_mysql_lock_data_dictionary(trx);
+
+ table = dict_table_get_low(name);
+
+ if (!table) {
+ ut_print_timestamp(stderr);
+ fputs(" InnoDB: table ", stderr);
+ ut_print_name(stderr, trx, name);
+ fputs("\n"
+"InnoDB: does not exist in the InnoDB data dictionary\n"
+"InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n",
+ stderr);
+
+ err = DB_TABLE_NOT_FOUND;
+
+ goto funct_exit;
+ }
+
+ if (table->space == 0) {
+ ut_print_timestamp(stderr);
+ fputs(" InnoDB: Error: table ", stderr);
+ ut_print_name(stderr, trx, name);
+ fputs("\n"
+"InnoDB: is in the system tablespace 0 which cannot be imported\n", stderr);
+ err = DB_ERROR;
+
+ goto funct_exit;
+ }
+
+ if (!table->tablespace_discarded) {
+ ut_print_timestamp(stderr);
+ fputs(
+" InnoDB: Error: you are trying to IMPORT a tablespace\n"
+"InnoDB: ", stderr);
+ ut_print_name(stderr, trx, name);
+ fputs(", though you have not called DISCARD on it yet\n"
+"InnoDB: during the lifetime of the mysqld process!\n", stderr);
+
+ err = DB_ERROR;
+
+ goto funct_exit;
+ }
+
+ /* Play safe and remove all insert buffer entries, though we should
+ have removed them already when DISCARD TABLESPACE was called */
+
+ ibuf_delete_for_discarded_space(table->space);
+
+ success = fil_open_single_table_tablespace(TRUE, table->space,
+ table->name);
+ if (success) {
+ table->ibd_file_missing = FALSE;
+ table->tablespace_discarded = FALSE;
+ } else {
+ if (table->ibd_file_missing) {
+ ut_print_timestamp(stderr);
+ fputs(
+" 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"
+"InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n",
+ stderr);
+ }
+
+ err = DB_ERROR;
+ }
+
+funct_exit:
+ row_mysql_unlock_data_dictionary(trx);
+
+ trx_commit_for_mysql(trx);
+
+ trx->op_info = "";
+
+ return((int) err);
+}
+
+/*************************************************************************
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. */
@@ -1933,25 +2354,30 @@ the corresponding monitor output by the master thread. */
int
row_drop_table_for_mysql(
/*=====================*/
- /* out: error code or DB_SUCCESS */
- char* name, /* in: table name */
- trx_t* trx, /* in: transaction handle */
- ibool drop_db)/* in: TRUE=dropping whole database */
+ /* out: error code or DB_SUCCESS */
+ const char* name, /* in: table name */
+ trx_t* trx, /* in: transaction handle */
+ ibool drop_db)/* in: TRUE=dropping whole database */
{
dict_foreign_t* foreign;
dict_table_t* table;
+ ulint space_id;
que_thr_t* thr;
que_t* graph;
ulint err;
const char* table_name;
ulint namelen;
+ char* dir_path_of_temp_table = NULL;
+ ibool success;
ibool locked_dictionary = FALSE;
char* quoted_name;
char* sql;
+
/* We use the private SQL parser of Innobase to generate the
query graphs needed in deleting the dictionary data from system
tables in Innobase. Deleting a row from SYS_INDEXES table also
frees the file segments of the B-tree associated with the index. */
+
static const char str1[] =
"PROCEDURE DROP_TABLE_PROC () IS\n"
"table_name CHAR;\n"
@@ -2014,7 +2440,6 @@ row_drop_table_for_mysql(
"COMMIT WORK;\n"
"END;\n";
- ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
ut_a(name != NULL);
if (srv_created_new_raw) {
@@ -2024,12 +2449,12 @@ row_drop_table_for_mysql(
"InnoDB: database modifications by the user. Shut down\n"
"InnoDB: mysqld and edit my.cnf so that newraw is replaced\n"
"InnoDB: with raw, and innodb_force_... is removed.\n",
- stderr);
+ stderr);
return(DB_ERROR);
}
- trx->op_info = (char *) "dropping table";
+ trx->op_info = "dropping table";
trx_start_if_not_started(trx);
@@ -2054,7 +2479,6 @@ row_drop_table_for_mysql(
} else if (namelen == sizeof S_innodb_lock_monitor
&& !memcmp(table_name, S_innodb_lock_monitor,
sizeof S_innodb_lock_monitor)) {
-
srv_print_innodb_monitor = FALSE;
srv_print_innodb_lock_monitor = FALSE;
} else if (namelen == sizeof S_innodb_tablespace_monitor
@@ -2111,7 +2535,7 @@ row_drop_table_for_mysql(
ut_print_timestamp(stderr);
fputs(" InnoDB: Error: table ", stderr);
- ut_print_name(stderr, name);
+ ut_print_name(stderr, trx, name);
fputs(" does not exist in the InnoDB internal\n"
"InnoDB: data dictionary though MySQL is trying to drop it.\n"
"InnoDB: Have you copied the .frm file of the table to the\n"
@@ -2146,10 +2570,10 @@ row_drop_table_for_mysql(
ut_print_timestamp(ef);
fputs(" Cannot drop table ", ef);
- ut_print_name(ef, name);
+ ut_print_name(ef, trx, name);
fputs("\n"
"because it is referenced by ", ef);
- ut_print_name(ef, foreign->foreign_table_name);
+ ut_print_name(ef, trx, foreign->foreign_table_name);
putc('\n', ef);
mutex_exit(&dict_foreign_err_mutex);
@@ -2161,7 +2585,7 @@ row_drop_table_for_mysql(
ut_print_timestamp(stderr);
fputs(" InnoDB: Warning: MySQL is trying to drop table ",
stderr);
- ut_print_name(stderr, table->name);
+ 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",
@@ -2178,7 +2602,7 @@ row_drop_table_for_mysql(
ut_print_timestamp(stderr);
fputs(" InnoDB: You are trying to drop table ", stderr);
- ut_print_name(stderr, table->name);
+ 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",
@@ -2213,16 +2637,69 @@ row_drop_table_for_mysql(
ut_error;
} else {
+ ibool is_path;
+ const char* name_or_path;
+
+ space_id = table->space;
+
+ if (table->dir_path_of_temp_table != NULL) {
+ dir_path_of_temp_table =
+ mem_strdup(table->dir_path_of_temp_table);
+ is_path = TRUE;
+ name_or_path = dir_path_of_temp_table;
+ } else {
+ is_path = FALSE;
+ name_or_path = name;
+ }
+
dict_table_remove_from_cache(table);
if (dict_load_table(name) != NULL) {
ut_print_timestamp(stderr);
- fputs(" InnoDB: Error: dropping of table ",
+ fputs(" InnoDB: Error: not able to remove table ",
stderr);
- ut_print_name(stderr, name);
- fputs(" failed!\n", stderr);
+ ut_print_name(stderr, trx, name);
+ fputs(" from the dictionary cache!\n", stderr);
err = DB_ERROR;
}
+
+ /* Do not drop possible .ibd tablespace if something went
+ wrong: we do not want to delete valuable data of the user */
+
+ if (err == DB_SUCCESS && space_id > 0) {
+ if (!fil_space_for_table_exists_in_mem(space_id,
+ name_or_path,
+ is_path,
+ FALSE, TRUE)) {
+ err = DB_SUCCESS;
+
+ fprintf(stderr,
+"InnoDB: We removed now the InnoDB internal data dictionary entry\n"
+"InnoDB: of table ");
+ ut_print_name(stderr, trx, name);
+ fprintf(stderr, ".\n");
+
+ goto funct_exit;
+ }
+
+ success = fil_delete_tablespace(space_id);
+
+ if (!success) {
+ fprintf(stderr,
+"InnoDB: We removed now the InnoDB internal data dictionary entry\n"
+"InnoDB: of table ");
+ ut_print_name(stderr, trx, name);
+ fprintf(stderr, ".\n");
+
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+" InnoDB: Error: not able to delete tablespace %lu of table ",
+ (ulong) space_id);
+ ut_print_name(stderr, trx, name);
+ fputs("!\n", stderr);
+ err = DB_ERROR;
+ }
+ }
}
funct_exit:
@@ -2230,11 +2707,15 @@ funct_exit:
row_mysql_unlock_data_dictionary(trx);
}
+ if (dir_path_of_temp_table) {
+ mem_free(dir_path_of_temp_table);
+ }
+
que_graph_free(graph);
trx_commit_for_mysql(trx);
- trx->op_info = (char *) "";
+ trx->op_info = "";
srv_wake_master_thread();
@@ -2247,9 +2728,9 @@ Drops a database for MySQL. */
int
row_drop_database_for_mysql(
/*========================*/
- /* out: error code or DB_SUCCESS */
- char* name, /* in: database name which ends to '/' */
- trx_t* trx) /* in: transaction handle */
+ /* out: error code or DB_SUCCESS */
+ const char* name, /* in: database name which ends to '/' */
+ trx_t* trx) /* in: transaction handle */
{
dict_table_t* table;
char* table_name;
@@ -2260,7 +2741,7 @@ row_drop_database_for_mysql(
ut_a(name != NULL);
ut_a(name[namelen - 1] == '/');
- trx->op_info = (char *) "dropping database";
+ trx->op_info = "dropping database";
trx_start_if_not_started(trx);
loop:
@@ -2282,10 +2763,10 @@ loop:
ut_print_timestamp(stderr);
fputs(
" InnoDB: Warning: MySQL is trying to drop database ", stderr);
- ut_print_name(stderr, name);
+ ut_print_name(stderr, trx, name);
fputs("\n"
"InnoDB: though there are still open handles to table ", stderr);
- ut_print_name(stderr, table_name);
+ ut_print_name(stderr, trx, table_name);
fputs(".\n", stderr);
os_thread_sleep(1000000);
@@ -2301,10 +2782,10 @@ loop:
if (err != DB_SUCCESS) {
fputs("InnoDB: DROP DATABASE ", stderr);
- ut_print_name(stderr, name);
+ ut_print_name(stderr, trx, name);
fprintf(stderr, " failed with error %lu for table ",
(ulint) err);
- ut_print_name(stderr, table_name);
+ ut_print_name(stderr, trx, table_name);
putc('\n', stderr);
break;
}
@@ -2314,7 +2795,7 @@ loop:
trx_commit_for_mysql(trx);
- trx->op_info = (char *) "";
+ trx->op_info = "";
return(err);
}
@@ -2339,10 +2820,10 @@ Renames a table for MySQL. */
int
row_rename_table_for_mysql(
/*=======================*/
- /* out: error code or DB_SUCCESS */
- char* old_name, /* in: old table name */
- char* new_name, /* in: new table name */
- trx_t* trx) /* in: transaction handle */
+ /* out: error code or DB_SUCCESS */
+ const char* old_name, /* in: old table name */
+ const char* new_name, /* in: new table name */
+ trx_t* trx) /* in: transaction handle */
{
dict_table_t* table;
que_thr_t* thr;
@@ -2433,6 +2914,7 @@ row_rename_table_for_mysql(
ibool recovering_temp_table = FALSE;
ulint len;
ulint i;
+ ibool success;
/* length of database name; 0 if not renaming to a temporary table */
ulint db_name_len;
char* sql;
@@ -2466,7 +2948,7 @@ row_rename_table_for_mysql(
return(DB_ERROR);
}
- trx->op_info = (char *) "renaming table";
+ trx->op_info = "renaming table";
trx_start_if_not_started(trx);
if (row_mysql_is_recovered_tmp_table(new_name)) {
@@ -2483,7 +2965,29 @@ row_rename_table_for_mysql(
if (!table) {
err = DB_TABLE_NOT_FOUND;
+ ut_print_timestamp(stderr);
+
+ fputs(" InnoDB: Error: table ", stderr);
+ ut_print_name(stderr, trx, old_name);
+ fputs(" does not exist in the InnoDB internal\n"
+ "InnoDB: data dictionary though MySQL is trying to rename the table.\n"
+ "InnoDB: Have you copied the .frm file of the table to the\n"
+ "InnoDB: MySQL database directory from another database?\n"
+ "InnoDB: You can look for further help from section 15.1 of\n"
+ "InnoDB: http://www.innodb.com/ibman.php\n", stderr);
+ goto funct_exit;
+ }
+
+ if (table->ibd_file_missing) {
+ err = DB_TABLE_NOT_FOUND;
+ ut_print_timestamp(stderr);
+ fputs(" InnoDB: Error: table ", stderr);
+ ut_print_name(stderr, trx, old_name);
+ fputs(
+ " does not have an .ibd file in the database directory.\n"
+ "InnoDB: You can look for further help from section 15.1 of\n"
+ "InnoDB: http://www.innodb.com/ibman.php\n", stderr);
goto funct_exit;
}
@@ -2510,7 +3014,7 @@ row_rename_table_for_mysql(
goto funct_exit;
}
-
+
/* reserve space for all database names */
len += 2 * n_constraints_to_drop
* (ut_strlenq(old_name, '\'')
@@ -2610,40 +3114,56 @@ row_rename_table_for_mysql(
if (err != DB_SUCCESS) {
if (err == DB_DUPLICATE_KEY) {
ut_print_timestamp(stderr);
-
fputs(
" InnoDB: Error; possible reasons:\n"
"InnoDB: 1) Table rename would cause two FOREIGN KEY constraints\n"
"InnoDB: to have the same internal name in case-insensitive comparison.\n"
"InnoDB: 2) table ", stderr);
- ut_print_name(stderr, new_name);
- fputs(" exists in the InnoDB internal data\n"
+ ut_print_name(stderr, trx, new_name);
+ fputs(" exists in the InnoDB internal data\n"
"InnoDB: dictionary though MySQL is trying rename table ", stderr);
- ut_print_name(stderr, old_name);
- fputs(" to it.\n"
+ ut_print_name(stderr, trx, old_name);
+ fputs(" to it.\n"
"InnoDB: Have you deleted the .frm file and not used DROP TABLE?\n"
"InnoDB: You can look for further help from\n"
"InnoDB: http://dev.mysql.com/doc/mysql/en/"
"InnoDB_troubleshooting_datadict.html\n"
"InnoDB: If table ", stderr);
- ut_print_name(stderr, new_name);
+ ut_print_name(stderr, trx, new_name);
fputs(
" is a temporary table #sql..., then it can be that\n"
"InnoDB: there are still queries running on the table, and it will be\n"
"InnoDB: dropped automatically when the queries end.\n"
"InnoDB: You can drop the orphaned table inside InnoDB by\n"
"InnoDB: creating an InnoDB table with the same name in another\n"
- "InnoDB: database and moving the .frm file to the current database.\n"
+ "InnoDB: database and copying the .frm file to the current database.\n"
"InnoDB: Then MySQL thinks the table exists, and DROP TABLE will\n"
"InnoDB: succeed.\n", stderr);
}
-
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE, NULL);
trx->error_state = DB_SUCCESS;
} else {
- ut_a(dict_table_rename_in_cache(table, new_name,
- !row_is_mysql_tmp_table_name(new_name)));
+ /* The following call will also rename the .ibd data file if
+ the table is stored in a single-table tablespace */
+
+ success = dict_table_rename_in_cache(table, new_name,
+ !row_is_mysql_tmp_table_name(new_name));
+ if (!success) {
+ trx->error_state = DB_SUCCESS;
+ trx_general_rollback_for_mysql(trx, FALSE, NULL);
+ trx->error_state = DB_SUCCESS;
+ ut_print_timestamp(stderr);
+ fputs(" InnoDB: Error in table rename, cannot rename ",
+ stderr);
+ ut_print_name(stderr, trx, old_name);
+ fputs(" to ", stderr);
+ ut_print_name(stderr, trx, new_name);
+ putc('\n', stderr);
+ err = DB_ERROR;
+
+ goto funct_exit;
+ }
if (row_is_mysql_tmp_table_name(old_name)) {
@@ -2657,20 +3177,17 @@ row_rename_table_for_mysql(
err = dict_load_foreigns(new_name);
if (err != DB_SUCCESS) {
-
ut_print_timestamp(stderr);
-
- fputs(" InnoDB: Error: in ALTER TABLE table ",
+ fputs(" InnoDB: Error: in ALTER TABLE ",
stderr);
- ut_print_name(stderr, new_name);
+ ut_print_name(stderr, trx, new_name);
fputs("\n"
- "InnoDB: has or is referenced in foreign key constraints\n"
- "InnoDB: which are not compatible with the new table definition.\n",
+ "InnoDB: has or is referenced in foreign key constraints\n"
+ "InnoDB: which are not compatible with the new table definition.\n",
stderr);
-
+
ut_a(dict_table_rename_in_cache(table,
- old_name, FALSE));
-
+ old_name, FALSE));
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE,
NULL);
@@ -2686,14 +3203,14 @@ row_rename_table_for_mysql(
fputs(
" InnoDB: Error: in RENAME TABLE table ",
stderr);
- ut_print_name(stderr, new_name);
+ ut_print_name(stderr, trx, new_name);
fputs("\n"
"InnoDB: is referenced in foreign key constraints\n"
"InnoDB: which are not compatible with the new table definition.\n",
stderr);
ut_a(dict_table_rename_in_cache(table,
- old_name, FALSE));
+ old_name, FALSE));
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE,
@@ -2702,8 +3219,8 @@ row_rename_table_for_mysql(
}
}
}
-funct_exit:
- if (!recovering_temp_table) {
+funct_exit:
+ if (!recovering_temp_table) {
row_mysql_unlock_data_dictionary(trx);
}
@@ -2717,7 +3234,7 @@ funct_exit:
trx_commit_for_mysql(trx);
- trx->op_info = (char *) "";
+ trx->op_info = "";
return((int) err);
}
@@ -2810,7 +3327,8 @@ loop:
fputs("InnoDB: index records in a wrong order in ",
stderr);
not_ok:
- dict_index_name_print(stderr, index);
+ dict_index_name_print(stderr,
+ prebuilt->trx, index);
fputs("\n"
"InnoDB: prev record ", stderr);
dtuple_print(stderr, prev_entry);
@@ -2854,8 +3372,22 @@ row_check_table_for_mysql(
ulint n_rows_in_table = ULINT_UNDEFINED;
ulint ret = DB_SUCCESS;
ulint old_isolation_level;
-
- prebuilt->trx->op_info = (char *) "checking table";
+
+ 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;
@@ -2886,21 +3418,22 @@ row_check_table_for_mysql(
ret = DB_ERROR;
}
- /* fprintf(stderr, "%lu entries in index ", n_rows);
- ut_print_name(stderr, index->name);
- putc('\n', stderr); */
+ /* fprintf(stderr, "%lu entries in index %s\n", n_rows,
+ index->name); */
if (index == dict_table_get_first_index(table)) {
n_rows_in_table = n_rows;
} else if (n_rows != n_rows_in_table) {
ret = DB_ERROR;
-
- fputs("InnoDB: Error: ", stderr);
- dict_index_name_print(stderr, index);
+
+ fputs("Error: ", stderr);
+ dict_index_name_print(stderr,
+ prebuilt->trx, index);
fprintf(stderr,
" contains %lu entries, should be %lu\n",
- n_rows, n_rows_in_table);
+ (ulong) n_rows,
+ (ulong) n_rows_in_table);
}
}
@@ -2923,7 +3456,7 @@ row_check_table_for_mysql(
srv_fatal_semaphore_wait_threshold -= 7200; /* 2 hours */
mutex_exit(&kernel_mutex);
- prebuilt->trx->op_info = (char *) "";
+ prebuilt->trx->op_info = "";
return(ret);
}