Skip to content

Commit ffdd2c3

Browse files
committed
Bug 21577278 INNODB: DO NOT TREAT A BAD ISL FILE LIKE A MISSING ISL FILE
During recovery, if an ISL file is found that refers to a missing or invalid datafile, do not open that datafile, even if a valid version can be found in the MLOG_FILE_NAME record. This will cause recovery to fail. Approved by Deb in rb#9958
1 parent 5c44a9f commit ffdd2c3

File tree

6 files changed

+153
-75
lines changed

6 files changed

+153
-75
lines changed

mysql-test/suite/innodb/r/tablespace_crash.result

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -200,24 +200,28 @@ INSERT INTO t3 VALUES (6);
200200
INSERT INTO t4 VALUES (6);
201201
#
202202
# Kill the server
203-
# Mess up the ISL files so that they point to the wrong file.
203+
# Mess up a general tablespace ISL file so that it points to the wrong file.
204+
# Recovery will fail. Check the error message.
205+
# Mess up a file-per-table tablespace ISL file so that it points to the wrong file.
206+
# Recovery will fail. Check the error message.
207+
# Delete the ISL files and move all remote files to default locations
204208
# Restart mysqld and display which datafiles were recovered.
205209
# restart
206-
# It should be the remote datafiles again, but this time
207-
# they must have been found via the REDO log.
210+
# It should be the default datafiles
211+
# The two previous attempts to recover should not cause a problem
208212
#
209213
=== information_schema.innodb_sys_tablespaces and innodb_sys_datafiles ===
210214
Space_Name Space_Type Page_Size Zip_Size Formats_Permitted Path
211-
ts1 General DEFAULT 0 Any MYSQL_TMP_DIR/tablespace1.ibd
212-
ts2 General DEFAULT 0 Any MYSQL_TMP_DIR/tablespace2.ibd
213-
test/t3 Single DEFAULT 0 Compact or Redundant MYSQL_TMP_DIR/test/t3.ibd
214-
test/t4 Single DEFAULT 0 Compact or Redundant MYSQL_TMP_DIR/test/t4.ibd
215+
ts1 General DEFAULT 0 Any MYSQLD_DATADIR/tablespace1.ibd
216+
ts2 General DEFAULT 0 Any MYSQLD_DATADIR/tablespace2.ibd
217+
test/t3 Single DEFAULT 0 Compact or Redundant MYSQLD_DATADIR/test/t3.ibd
218+
test/t4 Single DEFAULT 0 Compact or Redundant MYSQLD_DATADIR/test/t4.ibd
215219
=== information_schema.files ===
216220
Space_Name File_Type Engine Status Tablespace_Name Path
217-
ts1 TABLESPACE InnoDB NORMAL ts1 MYSQL_TMP_DIR/tablespace1.ibd
218-
ts2 TABLESPACE InnoDB NORMAL ts2 MYSQL_TMP_DIR/tablespace2.ibd
219-
test/t3 TABLESPACE InnoDB NORMAL innodb_file_per_table.## MYSQL_TMP_DIR/test/t3.ibd
220-
test/t4 TABLESPACE InnoDB NORMAL innodb_file_per_table.## MYSQL_TMP_DIR/test/t4.ibd
221+
ts1 TABLESPACE InnoDB NORMAL ts1 MYSQLD_DATADIR/tablespace1.ibd
222+
ts2 TABLESPACE InnoDB NORMAL ts2 MYSQLD_DATADIR/tablespace2.ibd
223+
test/t3 TABLESPACE InnoDB NORMAL innodb_file_per_table.## MYSQLD_DATADIR/test/t3.ibd
224+
test/t4 TABLESPACE InnoDB NORMAL innodb_file_per_table.## MYSQLD_DATADIR/test/t4.ibd
221225
select * from t1a;
222226
a
223227
1
@@ -265,7 +269,7 @@ SHOW CREATE TABLE t4;
265269
Table Create Table
266270
t4 CREATE TABLE `t4` (
267271
`a` int(11) DEFAULT NULL
268-
) /*!50100 TABLESPACE `innodb_file_per_table` */ ENGINE=InnoDB DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQL_TMP_DIR/'
272+
) /*!50100 TABLESPACE `innodb_file_per_table` */ ENGINE=InnoDB DEFAULT CHARSET=latin1
269273
#
270274
# Cleanup
271275
#

mysql-test/suite/innodb/t/tablespace_crash.test

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ INSERT INTO t4 VALUES (6);
102102
--move_file $MYSQLD_DATADIR/tablespace2.ibd $MYSQL_TMP_DIR/tablespace2.ibd
103103
--move_file $MYSQLD_DATADIR/test/t3.ibd $MYSQL_TMP_DIR/test/t3.ibd
104104
--move_file $MYSQLD_DATADIR/test/t4.ibd $MYSQL_TMP_DIR/test/t4.ibd
105+
--exec echo $MYSQL_TMP_DIR/tablespace1.ibd > $MYSQLD_DATADIR/tablespace1.isl
106+
--exec echo $MYSQL_TMP_DIR/tablespace2.ibd > $MYSQLD_DATADIR/tablespace2.isl
107+
--exec echo $MYSQL_TMP_DIR/test/t3.ibd > $MYSQLD_DATADIR/tablespace3.isl
108+
--exec echo $MYSQL_TMP_DIR/test/t4.ibd > $MYSQLD_DATADIR/tablespace4.isl
105109

106110
--echo # Restart mysqld and show that the remote files were opened and recovered.
107111
--source include/start_mysqld.inc
@@ -135,17 +139,61 @@ INSERT INTO t4 VALUES (6);
135139
--echo #
136140
--source include/kill_mysqld.inc
137141

138-
--echo # Mess up the ISL files so that they point to the wrong file.
142+
--echo # Mess up a general tablespace ISL file so that it points to the wrong file.
143+
--echo # Recovery will fail. Check the error message.
144+
139145
--exec echo $MYSQL_TMP_DIR/bad/tablespace1.ibd > $MYSQLD_DATADIR/tablespace1.isl
140-
--exec echo $MYSQL_TMP_DIR/bad/tablespace2.ibd > $MYSQLD_DATADIR/tablespace2.isl
146+
147+
let SEARCH_FILE= $MYSQLTEST_VARDIR/log/my_restart.err;
148+
--error 3
149+
--exec $MYSQLD_CMD --core-file --console > $SEARCH_FILE 2>&1;
150+
151+
let SEARCH_PATTERN= \[ERROR\] InnoDB: ISL file '.*tablespace1.isl' was found but the linked file '.*tablespace1.ibd' could not be opened or is not correct;
152+
--source include/search_pattern_in_file.inc
153+
154+
let SEARCH_PATTERN= \[ERROR\] InnoDB: Tablespace .* was not found at .*tablespace1.ibd;
155+
--source include/search_pattern_in_file.inc
156+
157+
let SEARCH_PATTERN= \[ERROR\] InnoDB: Cannot continue operation;
158+
--source include/search_pattern_in_file.inc
159+
160+
--remove_file $SEARCH_FILE
161+
162+
--echo # Mess up a file-per-table tablespace ISL file so that it points to the wrong file.
163+
--echo # Recovery will fail. Check the error message.
164+
165+
--exec echo $MYSQL_TMP_DIR/tablespace1.ibd > $MYSQLD_DATADIR/tablespace1.isl
141166
--exec echo $MYSQL_TMP_DIR/bad/t3.ibd > $MYSQLD_DATADIR/test/t3.isl
142-
--exec echo $MYSQL_TMP_DIR/bad/t4.ibd > $MYSQLD_DATADIR/test/t4.isl
167+
168+
--error 3
169+
--exec $MYSQLD_CMD --core-file --console > $SEARCH_FILE 2>&1;
170+
171+
let SEARCH_PATTERN= \[ERROR\] InnoDB: ISL file '.*t3.isl' was found but the linked file '.*t3.ibd' could not be opened or is not correct;
172+
--source include/search_pattern_in_file.inc
173+
174+
let SEARCH_PATTERN= \[ERROR\] InnoDB: Tablespace .* was not found at .*t3.ibd;
175+
--source include/search_pattern_in_file.inc
176+
177+
let SEARCH_PATTERN= \[ERROR\] InnoDB: Cannot continue operation;
178+
--source include/search_pattern_in_file.inc
179+
180+
--remove_file $SEARCH_FILE
181+
182+
--echo # Delete the ISL files and move all remote files to default locations
183+
--remove_file $MYSQLD_DATADIR/tablespace1.isl
184+
--remove_file $MYSQLD_DATADIR/tablespace2.isl
185+
--remove_file $MYSQLD_DATADIR/test/t3.isl
186+
--remove_file $MYSQLD_DATADIR/test/t4.isl
187+
--move_file $MYSQL_TMP_DIR/tablespace1.ibd $MYSQLD_DATADIR/tablespace1.ibd
188+
--move_file $MYSQL_TMP_DIR/tablespace2.ibd $MYSQLD_DATADIR/tablespace2.ibd
189+
--move_file $MYSQL_TMP_DIR/test/t3.ibd $MYSQLD_DATADIR/test/t3.ibd
190+
--move_file $MYSQL_TMP_DIR/test/t4.ibd $MYSQLD_DATADIR/test/t4.ibd
143191

144192
--echo # Restart mysqld and display which datafiles were recovered.
145193
--source include/start_mysqld.inc
146194

147-
--echo # It should be the remote datafiles again, but this time
148-
--echo # they must have been found via the REDO log.
195+
--echo # It should be the default datafiles
196+
--echo # The two previous attempts to recover should not cause a problem
149197
--echo #
150198

151199
--source suite/innodb/include/show_i_s_tablespaces.inc
@@ -171,4 +219,12 @@ DROP TABLE t3;
171219
DROP TABLE t4;
172220
DROP TABLESPACE ts1;
173221
DROP TABLESPACE ts2;
174-
--remove_file $MYSQLD_DATADIR/test/t3.isl
222+
223+
-- disable_query_log
224+
# This log message can occur with --repeat=2+ because the previous test will
225+
# have entries in the REDO log within the same checkpoint. So there will be
226+
# MLOG_FILE_NAME records for remote datafiles with the same basename but the
227+
# previous space_ids.
228+
call mtr.add_suppression("\\[ERROR\\] InnoDB: ISL file '.*isl' was found but the linked file '.*ibd' could not be opened or is not correct.");
229+
-- enable_query_log
230+

storage/innobase/dict/dict0load.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1447,7 +1447,7 @@ dict_check_sys_tables(
14471447
char *dict_path = dict_get_first_path(space_id);
14481448
char *fil_path = fil_space_get_first_path(space_id);
14491449
if (dict_path && fil_path
1450-
&& strcmp(dict_path, fil_path)) {
1450+
&& strcmp(dict_path, fil_path)) {
14511451
dict_update_filepath(space_id, fil_path);
14521452
}
14531453
ut_free(dict_path);

storage/innobase/fil/fil0fil.cc

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4174,23 +4174,49 @@ fil_ibd_discover(
41744174
/* Did not find a general or file-per-table datafile in the
41754175
default location. Look for a remote general tablespace. */
41764176
df_rem_gen.set_name(basename);
4177-
if (df_rem_gen.open_read_only(false) == DB_SUCCESS
4178-
&& df_rem_gen.validate_for_recovery() == DB_SUCCESS
4179-
&& df_rem_gen.space_id() == space_id) {
4180-
df.set_filepath(df_rem_gen.filepath());
4181-
df.open_read_only(false);
4182-
return(true);
4177+
if (df_rem_gen.open_link_file() == DB_SUCCESS) {
4178+
/* An ISL file was found with contents. */
4179+
if (df_rem_gen.open_read_only(false) == DB_SUCCESS
4180+
&& df_rem_gen.validate_for_recovery() == DB_SUCCESS
4181+
&& df_rem_gen.space_id() == space_id) {
4182+
df.set_filepath(df_rem_gen.filepath());
4183+
df.open_read_only(false);
4184+
return(true);
4185+
}
4186+
4187+
/* Assume that this ISL file is intended to be used.
4188+
Since it is not pointing to the correct file, do not
4189+
continue looking for another file. */
4190+
ib::error() << "ISL file '"
4191+
<< df_rem_gen.link_filepath()
4192+
<< "' was found but the linked file '"
4193+
<< df_rem_gen.filepath()
4194+
<< "' could not be opened or is not correct.";
4195+
return(false);
41834196
}
41844197

41854198
/* Look for a remote file-per-table tablespace. */
41864199
if (sep_found == 2) {
41874200
df_rem_per.set_name(db);
4188-
if (df_rem_per.open_read_only(false) == DB_SUCCESS
4189-
&& df_rem_per.validate_for_recovery() == DB_SUCCESS
4190-
&& df_rem_per.space_id() == space_id) {
4191-
df.set_filepath(df_rem_per.filepath());
4192-
df.open_read_only(false);
4193-
return(true);
4201+
if (df_rem_per.open_link_file() == DB_SUCCESS) {
4202+
/* An ISL file was found with contents. */
4203+
if (df_rem_per.open_read_only(false) == DB_SUCCESS
4204+
&& df_rem_per.validate_for_recovery() == DB_SUCCESS
4205+
&& df_rem_per.space_id() == space_id) {
4206+
df.set_filepath(df_rem_per.filepath());
4207+
df.open_read_only(false);
4208+
return(true);
4209+
}
4210+
4211+
/* Assume that this ISL file is intended to be used.
4212+
Since it is not pointing to the correct file, do not
4213+
continue looking for another file. */
4214+
ib::error() << "ISL file '"
4215+
<< df_rem_per.link_filepath()
4216+
<< "' was found but the linked file '"
4217+
<< df_rem_per.filepath()
4218+
<< "' could not be opened or is not correct.";
4219+
return(false);
41944220
}
41954221
}
41964222

@@ -4202,7 +4228,7 @@ fil_ibd_discover(
42024228
return(true);
42034229
}
42044230

4205-
/* A datafile was not discovered for the filanem given. */
4231+
/* A datafile was not discovered for the filename given. */
42064232
return(false);
42074233
}
42084234

@@ -4237,7 +4263,8 @@ fil_ibd_load(
42374263
previously. Fail if it is different. */
42384264
fil_node_t* node = UT_LIST_GET_FIRST(space->chain);
42394265

4240-
if (0 != strcmp(filename, node->name)) {
4266+
if (0 != strcmp(innobase_basename(filename),
4267+
innobase_basename(node->name))) {
42414268
ib::info() << "Ignoring data file '" << filename
42424269
<< "' with space ID " << space->id
42434270
<< ". Another data file called " << node->name

storage/innobase/fsp/fsp0file.cc

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -828,28 +828,28 @@ Datafile::restore_from_doublewrite(
828828
m_filepath, m_handle, page, 0, page_size.physical()));
829829
}
830830

831+
/** Create a link filename based on the contents of m_name,
832+
open that file, and read the contents into m_filepath.
833+
@retval DB_SUCCESS if remote linked tablespace file is opened and read.
834+
@retval DB_CANNOT_OPEN_FILE if the link file does not exist. */
835+
dberr_t
836+
RemoteDatafile::open_link_file()
837+
{
838+
set_link_filepath(NULL);
839+
m_filepath = read_link_file(m_link_filepath);
840+
841+
return(m_filepath == NULL ? DB_CANNOT_OPEN_FILE : DB_SUCCESS);
842+
}
843+
831844
/** Opens a handle to the file linked to in an InnoDB Symbolic Link file
832845
in read-only mode so that it can be validated.
833846
@param[in] strict whether to issue error messages
834847
@return DB_SUCCESS if remote linked tablespace file is found and opened. */
835848
dberr_t
836849
RemoteDatafile::open_read_only(bool strict)
837850
{
838-
if (m_filepath == NULL) {
839-
if (m_link_filepath == NULL) {
840-
m_link_filepath = fil_make_filepath(
841-
NULL, name(), ISL, false);
842-
}
843-
844-
bool is_shared = FSP_FLAGS_GET_SHARED(flags());
845-
m_filepath = read_link_file(m_link_filepath, is_shared);
846-
847-
/* Validate m_filepath */
848-
if (m_filepath == NULL) {
849-
/* There is no ISL file or
850-
its contents are not valid. */
851-
return(DB_ERROR);
852-
}
851+
if (m_filepath == NULL && open_link_file() == DB_CANNOT_OPEN_FILE) {
852+
return(DB_ERROR);
853853
}
854854

855855
dberr_t err = Datafile::open_read_only(strict);
@@ -872,19 +872,8 @@ in read-write mode so that it can be restored from doublewrite and validated.
872872
dberr_t
873873
RemoteDatafile::open_read_write(bool read_only_mode)
874874
{
875-
if (m_filepath == NULL) {
876-
if (m_link_filepath == NULL) {
877-
m_link_filepath = fil_make_filepath(
878-
NULL, name(), ISL, false);
879-
}
880-
881-
bool is_shared = FSP_FLAGS_GET_SHARED(flags());
882-
m_filepath = read_link_file(m_link_filepath, is_shared);
883-
884-
if (m_filepath == NULL) {
885-
/* There is no remote file */
886-
return(DB_ERROR);
887-
}
875+
if (m_filepath == NULL && open_link_file() == DB_CANNOT_OPEN_FILE) {
876+
return(DB_ERROR);
888877
}
889878

890879
dberr_t err = Datafile::open_read_write(read_only_mode);
@@ -919,17 +908,19 @@ the path provided without its suffix, plus DOT_ISL.
919908
void
920909
RemoteDatafile::set_link_filepath(const char* path)
921910
{
922-
bool is_shared = FSP_FLAGS_GET_SHARED(flags());
911+
if (m_link_filepath != NULL) {
912+
return;
913+
}
923914

924-
if (is_shared) {
925-
ut_ad(path != NULL);
915+
if (path != NULL && FSP_FLAGS_GET_SHARED(flags())) {
916+
/* Make the link_filepath based on the basename. */
926917
ut_ad(strcmp(&path[strlen(path) - strlen(DOT_IBD)],
927918
DOT_IBD) == 0);
928919

929920
m_link_filepath = fil_make_filepath(NULL, base_name(path),
930921
ISL, false);
931922
} else {
932-
ut_ad(path == NULL);
923+
/* Make the link_filepath based on the m_name. */
933924
m_link_filepath = fil_make_filepath(NULL, name(), ISL, false);
934925
}
935926
}
@@ -986,7 +977,7 @@ RemoteDatafile::create_link_file(
986977
return(DB_ERROR);
987978
}
988979

989-
prev_filepath = read_link_file(link_filepath, is_shared);
980+
prev_filepath = read_link_file(link_filepath);
990981
if (prev_filepath) {
991982
/* Truncate will call this with an existing
992983
link file which contains the same filepath. */
@@ -1080,13 +1071,10 @@ For general tablespaces, there will be no 'database' directory.
10801071
The 'basename.isl' will be in the datadir.
10811072
The caller must free the memory returned if it is not null.
10821073
@param[in] link_filepath filepath of the ISL file
1083-
@param[in] is_shared true for general tablespace,
1084-
false for file-per-table
10851074
@return Filepath of the IBD file read from the ISL file */
10861075
char*
10871076
RemoteDatafile::read_link_file(
1088-
const char* link_filepath,
1089-
bool is_shared)
1077+
const char* link_filepath)
10901078
{
10911079
char* filepath = NULL;
10921080
FILE* file = NULL;

storage/innobase/include/fsp0file.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ class RemoteDatafile : public Datafile
476476
{
477477
private:
478478
/** Link filename (full path) */
479-
char* m_link_filepath;
479+
char* m_link_filepath;
480480

481481
public:
482482

@@ -515,6 +515,12 @@ class RemoteDatafile : public Datafile
515515
If NULL, use m_name as the basename. */
516516
void set_link_filepath(const char* path);
517517

518+
/** Create a link filename based on the contents of m_name,
519+
open that file, and read the contents into m_filepath.
520+
@retval DB_SUCCESS if remote linked tablespace file is opened and read.
521+
@retval DB_CANNOT_OPEN_FILE if the link file does not exist. */
522+
dberr_t open_link_file();
523+
518524
/** Delete an InnoDB Symbolic Link (ISL) file. */
519525
void delete_link_file(void);
520526

@@ -563,11 +569,8 @@ class RemoteDatafile : public Datafile
563569
The 'basename.isl' will be in the datadir.
564570
The caller must free the memory returned if it is not null.
565571
@param[in] link_filepath filepath of the ISL file
566-
@param[in] is_shared true for general tablespace,
567-
false for file-per-table
568572
@return Filepath of the IBD file read from the ISL file */
569573
static char* read_link_file(
570-
const char* link_filepath,
571-
bool is_shared);
574+
const char* link_filepath);
572575
};
573576
#endif /* fsp0file_h */

0 commit comments

Comments
 (0)