Commit d9709ffe authored by unknown's avatar unknown

row0purge.c, row0mysql.c, os0file.c, os0file.h, fil0fil.h, fil0fil.c:

  IMPORT TABLESPACE must reset lsns if they are too high
ha_innodb.cc:
  DISCARD/IMPORT TABLESPACE must have a TL_WRITE lock on the table


sql/ha_innodb.cc:
  DISCARD/IMPORT TABLESPACE must have a TL_WRITE lock on the table
innobase/fil/fil0fil.c:
  IMPORT TABLESPACE must reset lsns if they are too high
innobase/include/fil0fil.h:
  IMPORT TABLESPACE must reset lsns if they are too high
innobase/include/os0file.h:
  IMPORT TABLESPACE must reset lsns if they are too high
innobase/os/os0file.c:
  IMPORT TABLESPACE must reset lsns if they are too high
innobase/row/row0mysql.c:
  IMPORT TABLESPACE must reset lsns if they are too high
innobase/row/row0purge.c:
  IMPORT TABLESPACE must reset lsns if they are too high
parent cec45b6a
......@@ -1302,11 +1302,13 @@ fil_write_flushed_lsn_to_data_files(
space = UT_LIST_GET_FIRST(fil_system->space_list);
while (space) {
/* We only write the lsn to the system tablespace
(space id == 0) files */
/* We only write the lsn to all existing data files which have
been open during the lifetime of the mysqld process; they are
represented by the space objects in the tablespace memory
cache. Note that all data files in the system tablespace 0 are
always open. */
if (space->id == 0) {
ut_a(space->purpose == FIL_TABLESPACE);
if (space->purpose == FIL_TABLESPACE) {
sum_of_sizes = 0;
node = UT_LIST_GET_FIRST(space->chain);
......@@ -1326,8 +1328,6 @@ fil_write_flushed_lsn_to_data_files(
sum_of_sizes += node->size;
node = UT_LIST_GET_NEXT(chain, node);
}
break; /* there is only one space with id == 0 */
}
space = UT_LIST_GET_NEXT(space_list, space);
}
......@@ -1937,6 +1937,147 @@ fil_create_new_single_table_tablespace(
return(DB_SUCCESS);
}
/************************************************************************
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 at
the shutdown stamped the latest lsn to the FIL_PAGE_FILE_FLUSH_LSN in the
first page of the .ibd file, and we can determine whether we need to reset the
lsn's just by looking at that flush lsn. */
ibool
fil_reset_too_high_lsns(
/*====================*/
/* out: TRUE if success */
char* name, /* in: table name in the databasename/tablename
format */
dulint current_lsn) /* in: reset lsn's if the lsn stamped to
FIL_PAGE_FILE_FLUSH_LSN in the first page is
too high */
{
os_file_t file;
char* filepath;
byte* page;
dulint flush_lsn;
ulint space_id;
ib_longlong file_size;
ib_longlong offset;
ulint page_no;
ibool success;
filepath = ut_malloc(OS_FILE_MAX_PATH);
ut_a(strlen(name) < OS_FILE_MAX_PATH - 10);
sprintf(filepath, "./%s.ibd", name);
srv_normalize_path_for_win(filepath);
file = os_file_create_simple_no_error_handling(filepath, OS_FILE_OPEN,
OS_FILE_READ_WRITE, &success);
if (!success) {
ut_free(filepath);
return(FALSE);
}
/* Read the first page of the tablespace */
page = ut_malloc(UNIV_PAGE_SIZE);
success = os_file_read(file, page, 0, 0, UNIV_PAGE_SIZE);
if (!success) {
goto func_exit;
}
/* We have to read the file flush lsn from the header of the file */
flush_lsn = mach_read_from_8(page + FIL_PAGE_FILE_FLUSH_LSN);
if (ut_dulint_cmp(current_lsn, flush_lsn) >= 0) {
/* Ok */
success = TRUE;
goto func_exit;
}
space_id = fsp_header_get_space_id(page);
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Flush lsn in the tablespace file %lu to be imported\n"
"InnoDB: is %lu %lu, which exceeds current system lsn %lu %lu.\n"
"InnoDB: We reset the lsn's in the file %s.\n",
space_id,
ut_dulint_get_high(flush_lsn),
ut_dulint_get_low(flush_lsn),
ut_dulint_get_high(current_lsn),
ut_dulint_get_low(current_lsn), filepath);
/* Loop through all the pages in the tablespace and reset the lsn and
the page checksum if necessary */
file_size = os_file_get_size_as_iblonglong(file);
for (offset = 0; offset < file_size; offset += UNIV_PAGE_SIZE) {
success = os_file_read(file, page,
(ulint)(offset & 0xFFFFFFFFUL),
(ulint)(offset >> 32), UNIV_PAGE_SIZE);
if (!success) {
goto func_exit;
}
if (ut_dulint_cmp(mach_read_from_8(page + FIL_PAGE_LSN),
current_lsn) > 0) {
/* We have to reset the lsn */
space_id = mach_read_from_4(page
+ FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
page_no = mach_read_from_4(page + FIL_PAGE_OFFSET);
buf_flush_init_for_writing(page, current_lsn, space_id,
page_no);
success = os_file_write(filepath, file, page,
(ulint)(offset & 0xFFFFFFFFUL),
(ulint)(offset >> 32), UNIV_PAGE_SIZE);
if (!success) {
goto func_exit;
}
}
}
success = os_file_flush(file);
if (!success) {
goto func_exit;
}
/* We now update the flush_lsn stamp at the start of the file */
success = os_file_read(file, page, 0, 0, UNIV_PAGE_SIZE);
if (!success) {
goto func_exit;
}
mach_write_to_8(page + FIL_PAGE_FILE_FLUSH_LSN, current_lsn);
success = os_file_write(filepath, file, page, 0, 0, UNIV_PAGE_SIZE);
if (!success) {
goto func_exit;
}
success = os_file_flush(file);
func_exit:
os_file_close(file);
ut_free(page);
ut_free(filepath);
return(success);
}
/************************************************************************
Tries to open a single-table tablespace and checks the space id is right in
it. If does not succeed, prints an error message to the .err log. This
......@@ -1982,7 +2123,9 @@ fil_open_single_table_tablespace(
"InnoDB: open the tablespace file %s!\n", filepath);
fprintf(stderr,
"InnoDB: have you moved InnoDB .ibd files around without using the\n"
"InnoDB: commands DISCARD TABLESPACE and IMPORT TABLESPACE?\n");
"InnoDB: commands DISCARD TABLESPACE and IMPORT TABLESPACE?\n"
"InnoDB: You can look from section 15.1 of http://www.innodb.com/ibman.html\n"
"InnoDB: how to resolve the issue.\n");
ut_free(filepath);
......@@ -2007,7 +2150,9 @@ fil_open_single_table_tablespace(
"InnoDB: data dictionary it is %lu.\n", filepath, space_id, id);
fprintf(stderr,
"InnoDB: Have you moved InnoDB .ibd files around without using the\n"
"InnoDB: commands DISCARD TABLESPACE and IMPORT TABLESPACE?\n");
"InnoDB: commands DISCARD TABLESPACE and IMPORT TABLESPACE?\n"
"InnoDB: You can look from section 15.1 of http://www.innodb.com/ibman.html\n"
"InnoDB: how to resolve the issue.\n");
ret = FALSE;
......
......@@ -326,6 +326,25 @@ fil_open_single_table_tablespace(
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
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 at
the shutdown stamped the latest lsn to the FIL_PAGE_FILE_FLUSH_LSN in the
first page of the .ibd file, and we can determine whether we need to reset the
lsn's just by looking at that flush lsn. */
ibool
fil_reset_too_high_lsns(
/*====================*/
/* out: TRUE if success */
char* name, /* in: table name in the databasename/tablename
format */
dulint current_lsn); /* in: reset lsn's if the lsn stamped to
FIL_PAGE_FILE_FLUSH_LSN in the first page is
too high */
/************************************************************************
At the server startup, if we need crash recovery, scans the database
directories under the MySQL datadir, looking for .ibd files. Those files are
single-table tablespaces. We need to know the space id in each of them so that
......
......@@ -301,6 +301,14 @@ os_file_get_size(
size */
ulint* size_high);/* out: most significant 32 bits of size */
/***************************************************************************
Gets file size as a 64-bit integer ib_longlong. */
ib_longlong
os_file_get_size_as_iblonglong(
/*===========================*/
/* out: size in bytes, -1 if error */
os_file_t file); /* in: handle to a file */
/***************************************************************************
Sets a file size. This function can be used to extend or truncate a file. */
ibool
......
......@@ -1204,6 +1204,29 @@ os_file_get_size(
#endif
}
/***************************************************************************
Gets file size as a 64-bit integer ib_longlong. */
ib_longlong
os_file_get_size_as_iblonglong(
/*===========================*/
/* out: size in bytes, -1 if error */
os_file_t file) /* in: handle to a file */
{
ulint size;
ulint size_high;
ibool success;
success = os_file_get_size(file, &size, &size_high);
if (!success) {
return(-1);
}
return((((ib_longlong)size_high) << 32) + (ib_longlong)size);
}
/***************************************************************************
Sets a file size. This function can be used to extend or truncate a file. */
......
......@@ -1871,6 +1871,16 @@ row_discard_tablespace_for_mysql(
goto funct_exit;
}
if (table->space == 0) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: table %s\n"
"InnoDB: is in the system tablespace 0 which cannot be discarded\n", name);
err = DB_ERROR;
goto funct_exit;
}
new_id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID);
sprintf(buf,
......@@ -1967,6 +1977,7 @@ row_import_tablespace_for_mysql(
{
dict_table_t* table;
ibool success;
dulint current_lsn;
ulint err = DB_SUCCESS;
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
......@@ -1975,6 +1986,30 @@ row_import_tablespace_for_mysql(
trx->op_info = (char*) "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) {
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 */
......@@ -1988,6 +2023,16 @@ row_import_tablespace_for_mysql(
goto funct_exit;
}
if (table->space == 0) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: table %s\n"
"InnoDB: is in the system tablespace 0 which cannot be imported\n", name);
err = DB_ERROR;
goto funct_exit;
}
if (!table->tablespace_discarded) {
ut_print_timestamp(stderr);
fprintf(stderr,
......
......@@ -534,7 +534,7 @@ row_purge_parse_undo_rec(
node->table = NULL;
return;
return(FALSE);
}
clust_index = dict_table_get_first_index(node->table);
......
......@@ -4572,8 +4572,7 @@ ha_innobase::external_lock(
update_thd(thd);
if (lock_type != F_UNLCK && prebuilt->table->ibd_file_missing
&& !current_thd->tablespace_op) {
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"
......
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