Commit 82f3117e authored by Sergey Vojtovich's avatar Sergey Vojtovich

Applying InnoDB snapshot

Detailed revision comments:

r6547 | marko | 2010-02-03 14:43:38 +0200 (Wed, 03 Feb 2010) | 14 lines
branches/zip: Clean up CHECK TABLE error handling. (Issue #220)

ha_innobase::change_active_index(): Clean up code formatting.

ha_innobase::check(): Incorporate the code from
row_check_table_for_mysql().  Report errors to the client connection
instead of writing them to the error log.

row_check_table_for_mysql(): Remove.

row_check_index_for_mysql(): Renamed from row_scan_and_check_index().
Let the caller initialize prebuilt, and assume that the index is usable.

rb://178 approved by Sunny Bains
parent 4cba299e
2010-02-03 The InnoDB Team
* handler/ha_innodb.cc, include/row0mysql.h, row/row0mysql.c:
Clean up CHECK TABLE error handling.
2010-02-01 The InnoDB Team 2010-02-01 The InnoDB Team
* handler/ha_innodb.cc, mysql-test/innodb-autoinc.test, * handler/ha_innodb.cc, mysql-test/innodb-autoinc.test,
......
...@@ -5489,7 +5489,7 @@ ha_innobase::change_active_index( ...@@ -5489,7 +5489,7 @@ ha_innobase::change_active_index(
dtuple_set_n_fields(prebuilt->search_tuple, prebuilt->index->n_fields); dtuple_set_n_fields(prebuilt->search_tuple, prebuilt->index->n_fields);
dict_index_copy_types(prebuilt->search_tuple, prebuilt->index, dict_index_copy_types(prebuilt->search_tuple, prebuilt->index,
prebuilt->index->n_fields); prebuilt->index->n_fields);
/* MySQL changes the active index for a handle also during some /* MySQL changes the active index for a handle also during some
queries, for example SELECT MAX(a), SUM(a) first retrieves the MAX() queries, for example SELECT MAX(a), SUM(a) first retrieves the MAX()
...@@ -7626,8 +7626,13 @@ ha_innobase::check( ...@@ -7626,8 +7626,13 @@ ha_innobase::check(
HA_CHECK_OPT* check_opt) /*!< in: check options, currently HA_CHECK_OPT* check_opt) /*!< in: check options, currently
ignored */ ignored */
{ {
ulint ret; dict_index_t* index;
ulint n_rows;
ulint n_rows_in_table = ULINT_UNDEFINED;
ibool is_ok = TRUE;
ulint old_isolation_level;
DBUG_ENTER("ha_innobase::check");
DBUG_ASSERT(thd == ha_thd()); DBUG_ASSERT(thd == ha_thd());
ut_a(prebuilt->trx); ut_a(prebuilt->trx);
ut_a(prebuilt->trx->magic_n == TRX_MAGIC_N); ut_a(prebuilt->trx->magic_n == TRX_MAGIC_N);
...@@ -7640,17 +7645,140 @@ ha_innobase::check( ...@@ -7640,17 +7645,140 @@ ha_innobase::check(
build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW); build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW);
} }
ret = row_check_table_for_mysql(prebuilt); if (prebuilt->table->ibd_file_missing) {
sql_print_error("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: Please refer to\n"
"InnoDB: " REFMAN "innodb-troubleshooting.html\n"
"InnoDB: how you can resolve the problem.\n",
prebuilt->table->name);
DBUG_RETURN(HA_ADMIN_CORRUPT);
}
prebuilt->trx->op_info = "checking table";
switch (ret) { old_isolation_level = prebuilt->trx->isolation_level;
case DB_SUCCESS:
return(HA_ADMIN_OK); /* We must run the index record counts at an isolation level
case DB_INTERRUPTED: >= READ COMMITTED, because a dirty read can see a wrong number
of records in some index; to play safe, we use always
REPEATABLE READ here */
prebuilt->trx->isolation_level = TRX_ISO_REPEATABLE_READ;
/* Enlarge the fatal lock wait timeout during CHECK TABLE. */
mutex_enter(&kernel_mutex);
srv_fatal_semaphore_wait_threshold += 7200; /* 2 hours */
mutex_exit(&kernel_mutex);
for (index = dict_table_get_first_index(prebuilt->table);
index != NULL;
index = dict_table_get_next_index(index)) {
#if 0
fputs("Validating index ", stderr);
ut_print_name(stderr, trx, FALSE, index->name);
putc('\n', stderr);
#endif
if (!btr_validate_index(index, prebuilt->trx)) {
is_ok = FALSE;
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_NOT_KEYFILE,
"InnoDB: The B-tree of"
" index '%-.200s' is corrupted.",
index->name);
continue;
}
/* Instead of invoking change_active_index(), set up
a dummy template for non-locking reads, disabling
access to the clustered index. */
prebuilt->index = index;
prebuilt->index_usable = row_merge_is_index_usable(
prebuilt->trx, prebuilt->index);
if (UNIV_UNLIKELY(!prebuilt->index_usable)) {
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
HA_ERR_TABLE_DEF_CHANGED,
"InnoDB: Insufficient history for"
" index '%-.200s'",
index->name);
continue;
}
prebuilt->sql_stat_start = TRUE;
prebuilt->template_type = ROW_MYSQL_DUMMY_TEMPLATE;
prebuilt->n_template = 0;
prebuilt->need_to_access_clustered = FALSE;
dtuple_set_n_fields(prebuilt->search_tuple, 0);
prebuilt->select_lock_type = LOCK_NONE;
if (!row_check_index_for_mysql(prebuilt, index, &n_rows)) {
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_NOT_KEYFILE,
"InnoDB: The B-tree of"
" index '%-.200s' is corrupted.",
index->name);
is_ok = FALSE;
}
if (thd_killed(user_thd)) {
break;
}
#if 0
fprintf(stderr, "%lu entries in index %s\n", n_rows,
index->name);
#endif
if (index == dict_table_get_first_index(prebuilt->table)) {
n_rows_in_table = n_rows;
} else if (n_rows != n_rows_in_table) {
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_NOT_KEYFILE,
"InnoDB: Index '%-.200s'"
" contains %lu entries,"
" should be %lu.",
index->name,
(ulong) n_rows,
(ulong) n_rows_in_table);
is_ok = FALSE;
}
}
/* Restore the original isolation level */
prebuilt->trx->isolation_level = old_isolation_level;
/* We validate also the whole adaptive hash index for all tables
at every CHECK TABLE */
if (!btr_search_validate()) {
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_NOT_KEYFILE,
"InnoDB: The adaptive hash index is corrupted.");
is_ok = FALSE;
}
/* Restore the fatal lock wait timeout after CHECK TABLE. */
mutex_enter(&kernel_mutex);
srv_fatal_semaphore_wait_threshold -= 7200; /* 2 hours */
mutex_exit(&kernel_mutex);
prebuilt->trx->op_info = "";
if (thd_killed(user_thd)) {
my_error(ER_QUERY_INTERRUPTED, MYF(0)); my_error(ER_QUERY_INTERRUPTED, MYF(0));
return(-1);
default:
return(HA_ADMIN_CORRUPT);
} }
DBUG_RETURN(is_ok ? HA_ADMIN_OK : HA_ADMIN_CORRUPT);
} }
/*************************************************************//** /*************************************************************//**
......
...@@ -500,14 +500,19 @@ row_rename_table_for_mysql( ...@@ -500,14 +500,19 @@ row_rename_table_for_mysql(
trx_t* trx, /*!< in: transaction handle */ trx_t* trx, /*!< in: transaction handle */
ibool commit); /*!< in: if TRUE then commit trx */ ibool commit); /*!< in: if TRUE then commit trx */
/*********************************************************************//** /*********************************************************************//**
Checks a table for corruption. Checks that the index contains entries in an ascending order, unique
@return DB_ERROR or DB_SUCCESS */ constraint is not broken, and calculates the number of index entries
in the read view of the current transaction.
@return DB_SUCCESS if ok */
UNIV_INTERN UNIV_INTERN
ulint ulint
row_check_table_for_mysql( row_check_index_for_mysql(
/*======================*/ /*======================*/
row_prebuilt_t* prebuilt); /*!< in: prebuilt struct in MySQL row_prebuilt_t* prebuilt, /*!< in: prebuilt struct
handle */ in MySQL handle */
const dict_index_t* index, /*!< in: index */
ulint* n_rows); /*!< out: number of entries
seen in the consistent read */
/*********************************************************************//** /*********************************************************************//**
Determines if a table is a magic monitor table. Determines if a table is a magic monitor table.
......
...@@ -4007,14 +4007,15 @@ Checks that the index contains entries in an ascending order, unique ...@@ -4007,14 +4007,15 @@ Checks that the index contains entries in an ascending order, unique
constraint is not broken, and calculates the number of index entries constraint is not broken, and calculates the number of index entries
in the read view of the current transaction. in the read view of the current transaction.
@return TRUE if ok */ @return TRUE if ok */
static UNIV_INTERN
ibool ibool
row_scan_and_check_index( row_check_index_for_mysql(
/*=====================*/ /*======================*/
row_prebuilt_t* prebuilt, /*!< in: prebuilt struct in MySQL */ row_prebuilt_t* prebuilt, /*!< in: prebuilt struct
dict_index_t* index, /*!< in: index */ in MySQL handle */
ulint* n_rows) /*!< out: number of entries seen in the const dict_index_t* index, /*!< in: index */
current consistent read */ ulint* n_rows) /*!< out: number of entries
seen in the consistent read */
{ {
dtuple_t* prev_entry = NULL; dtuple_t* prev_entry = NULL;
ulint matched_fields; ulint matched_fields;
...@@ -4035,31 +4036,9 @@ row_scan_and_check_index( ...@@ -4035,31 +4036,9 @@ row_scan_and_check_index(
*n_rows = 0; *n_rows = 0;
if (!row_merge_is_index_usable(prebuilt->trx, index)) {
/* A newly created index may lack some delete-marked
records that may exist in the read view of
prebuilt->trx. Thus, such indexes must not be
accessed by consistent read. */
return(is_ok);
}
buf = mem_alloc(UNIV_PAGE_SIZE); buf = mem_alloc(UNIV_PAGE_SIZE);
heap = mem_heap_create(100); heap = mem_heap_create(100);
/* Make a dummy template in prebuilt, which we will use
in scanning the index entries */
prebuilt->index = index;
/* row_merge_is_index_usable() was already checked above. */
prebuilt->index_usable = TRUE;
prebuilt->sql_stat_start = TRUE;
prebuilt->template_type = ROW_MYSQL_DUMMY_TEMPLATE;
prebuilt->n_template = 0;
prebuilt->need_to_access_clustered = FALSE;
dtuple_set_n_fields(prebuilt->search_tuple, 0);
prebuilt->select_lock_type = LOCK_NONE;
cnt = 1000; cnt = 1000;
ret = row_search_for_mysql(buf, PAGE_CUR_G, prebuilt, 0, 0); ret = row_search_for_mysql(buf, PAGE_CUR_G, prebuilt, 0, 0);
...@@ -4177,119 +4156,6 @@ not_ok: ...@@ -4177,119 +4156,6 @@ not_ok:
goto loop; goto loop;
} }
/*********************************************************************//**
Checks a table for corruption.
@return DB_ERROR or DB_SUCCESS */
UNIV_INTERN
ulint
row_check_table_for_mysql(
/*======================*/
row_prebuilt_t* prebuilt) /*!< in: prebuilt struct in MySQL
handle */
{
dict_table_t* table = prebuilt->table;
dict_index_t* index;
ulint n_rows;
ulint n_rows_in_table = ULINT_UNDEFINED;
ulint ret = DB_SUCCESS;
ulint old_isolation_level;
if (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"
"InnoDB: " REFMAN "innodb-troubleshooting.html\n"
"InnoDB: how you can resolve the problem.\n",
table->name);
return(DB_ERROR);
}
prebuilt->trx->op_info = "checking table";
old_isolation_level = prebuilt->trx->isolation_level;
/* We must run the index record counts at an isolation level
>= READ COMMITTED, because a dirty read can see a wrong number
of records in some index; to play safe, we use always
REPEATABLE READ here */
prebuilt->trx->isolation_level = TRX_ISO_REPEATABLE_READ;
/* Enlarge the fatal lock wait timeout during CHECK TABLE. */
mutex_enter(&kernel_mutex);
srv_fatal_semaphore_wait_threshold += 7200; /* 2 hours */
mutex_exit(&kernel_mutex);
index = dict_table_get_first_index(table);
while (index != NULL) {
/* fputs("Validating index ", stderr);
ut_print_name(stderr, trx, FALSE, index->name);
putc('\n', stderr); */
if (!btr_validate_index(index, prebuilt->trx)) {
ret = DB_ERROR;
} else {
if (!row_scan_and_check_index(prebuilt,index, &n_rows)){
ret = DB_ERROR;
}
if (trx_is_interrupted(prebuilt->trx)) {
ret = DB_INTERRUPTED;
break;
}
/* 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("Error: ", stderr);
dict_index_name_print(stderr,
prebuilt->trx, index);
fprintf(stderr,
" contains %lu entries,"
" should be %lu\n",
(ulong) n_rows,
(ulong) n_rows_in_table);
}
}
index = dict_table_get_next_index(index);
}
/* Restore the original isolation level */
prebuilt->trx->isolation_level = old_isolation_level;
/* We validate also the whole adaptive hash index for all tables
at every CHECK TABLE */
if (!btr_search_validate()) {
ret = DB_ERROR;
}
/* Restore the fatal lock wait timeout after CHECK TABLE. */
mutex_enter(&kernel_mutex);
srv_fatal_semaphore_wait_threshold -= 7200; /* 2 hours */
mutex_exit(&kernel_mutex);
prebuilt->trx->op_info = "";
return(ret);
}
/*********************************************************************//** /*********************************************************************//**
Determines if a table is a magic monitor table. Determines if a table is a magic monitor table.
@return TRUE if monitor table */ @return TRUE if monitor table */
......
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