diff options
-rw-r--r-- | mysql-test/suite/innodb/r/row_format_redundant.result | 15 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/row_format_redundant.test | 97 | ||||
-rw-r--r-- | storage/innobase/dict/dict0load.cc | 89 | ||||
-rw-r--r-- | storage/innobase/fts/fts0fts.cc | 19 |
4 files changed, 125 insertions, 95 deletions
diff --git a/mysql-test/suite/innodb/r/row_format_redundant.result b/mysql-test/suite/innodb/r/row_format_redundant.result index c6926872d1a..45dfdf01218 100644 --- a/mysql-test/suite/innodb/r/row_format_redundant.result +++ b/mysql-test/suite/innodb/r/row_format_redundant.result @@ -1,7 +1,9 @@ +SET GLOBAL innodb_file_per_table=1; # # Bug#21644827 - FTS, ASSERT !SRV_READ_ONLY_MODE || M_IMPL.M_LOG_MODE == # MTR_LOG_NO_REDO # +SET GLOBAL innodb_file_per_table=ON; create table t1 (a int not null, d varchar(15) not null, b varchar(198) not null, c char(156), fulltext ftsic(c)) engine=InnoDB @@ -49,6 +51,9 @@ ERROR HY000: Table 't3' is read only TRUNCATE TABLE t1; TRUNCATE TABLE t2; TRUNCATE TABLE t3; +corrupted SYS_TABLES.MIX_LEN for test/t1 +corrupted SYS_TABLES.MIX_LEN for test/t2 +corrupted SYS_TABLES.MIX_LEN for test/t3 TRUNCATE TABLE t1; ERROR 42S02: Table 'test.t1' doesn't exist in engine TRUNCATE TABLE t2; @@ -66,9 +71,9 @@ ERROR HY000: Error on rename of './test/t1' to './test/tee_one' (errno: 155 "The DROP TABLE t1; Warnings: Warning 1932 Table 'test.t1' doesn't exist in engine -NOT FOUND /\[ERROR\] InnoDB: Table `test`\.`t1` in InnoDB data dictionary contains invalid flags. SYS_TABLES.MIX_LEN=255;/ in mysqld.1.err -FOUND 48 /\[ERROR\] InnoDB: InnoDB: Error: table unused flags are:255/ in mysqld.1.err -FOUND 1 /\[Warning\] InnoDB: Parent table of FTS auxiliary table test/FTS_.* not found\./ in mysqld.1.err -NOT FOUND /"\[Warning\] InnoDB: Cannot open table test/t1 from the internal data dictionary of InnoDB though the \.frm file for the table exists.";/ in mysqld.1.err -FOUND 2 /\[ERROR\] InnoDB: Table `test`.`t1` does not exist in the InnoDB internal data dictionary though MariaDB is trying to (drop|rename)/ in mysqld.1.err DROP TABLE t2,t3; +FOUND 49 /\[ERROR\] InnoDB: Table `test`\.`t1` in InnoDB data dictionary contains invalid flags\. SYS_TABLES.MIX_LEN=255\b/ in mysqld.1.err +ib_buffer_pool +ib_logfile0 +ib_logfile1 +ibdata1 diff --git a/mysql-test/suite/innodb/t/row_format_redundant.test b/mysql-test/suite/innodb/t/row_format_redundant.test index 4d41314fc79..04f17ca0d63 100644 --- a/mysql-test/suite/innodb/t/row_format_redundant.test +++ b/mysql-test/suite/innodb/t/row_format_redundant.test @@ -1,13 +1,37 @@ ---source include/innodb_page_size.inc ---source include/have_debug.inc +--source include/have_innodb.inc # Embedded mode doesn't allow restarting --source include/not_embedded.inc +--disable_query_log +call mtr.add_suppression("InnoDB: Table `mysql`\\.`innodb_table_stats` not found"); +call mtr.add_suppression("InnoDB: Table `test`.`t1` in InnoDB data dictionary contains invalid flags. SYS_TABLES.MIX_LEN=255"); +call mtr.add_suppression("InnoDB: Parent table of FTS auxiliary table test/FTS_.* not found"); +call mtr.add_suppression("InnoDB: Cannot open table test/t1 from the internal data dictionary"); +call mtr.add_suppression("InnoDB: Table `test`.`t1` does not exist in the InnoDB internal data dictionary though MariaDB is trying to (rename|drop)"); +FLUSH TABLES; +--enable_query_log + +let INNODB_PAGE_SIZE=`select @@innodb_page_size`; +let MYSQLD_DATADIR=`select @@datadir`; + +let bugdir= $MYSQLTEST_VARDIR/tmp/row_format_redundant; +--mkdir $bugdir +--let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.1.err + +--let $d=--innodb-data-home-dir=$bugdir --innodb-log-group-home-dir=$bugdir +--let $d=$d --innodb-data-file-path=ibdata1:1M:autoextend +--let $d=$d --innodb-undo-tablespaces=0 --innodb-stats-persistent=0 +--let $restart_parameters= $d +--source include/restart_mysqld.inc + +SET GLOBAL innodb_file_per_table=1; + --echo # --echo # Bug#21644827 - FTS, ASSERT !SRV_READ_ONLY_MODE || M_IMPL.M_LOG_MODE == --echo # MTR_LOG_NO_REDO --echo # +SET GLOBAL innodb_file_per_table=ON; create table t1 (a int not null, d varchar(15) not null, b varchar(198) not null, c char(156), fulltext ftsic(c)) engine=InnoDB @@ -45,7 +69,7 @@ insert into t3 values(555, 'eeee', 'ccccc', 'aaaaa'); # read-only restart requires the change buffer to be empty; therefore we # do a slow shutdown. SET GLOBAL innodb_fast_shutdown=0; ---let $restart_parameters = --innodb-read-only +--let $restart_parameters= $d --innodb-read-only --source include/restart_mysqld.inc SELECT COUNT(*) FROM t1; @@ -59,7 +83,7 @@ TRUNCATE TABLE t2; --error ER_OPEN_AS_READONLY TRUNCATE TABLE t3; ---let $restart_parameters = +--let $restart_parameters= $d --source include/restart_mysqld.inc TRUNCATE TABLE t1; @@ -67,9 +91,43 @@ TRUNCATE TABLE t2; TRUNCATE TABLE t3; --source include/shutdown_mysqld.inc +--perl +use strict; +my $ps= $ENV{INNODB_PAGE_SIZE}; +my $file= "$ENV{bugdir}/ibdata1"; +open(FILE, "+<", $file) || die "Unable to open $file\n"; +# Read DICT_HDR_TABLES, the root page number of CLUST_IND (SYS_TABLES.NAME). +sysseek(FILE, 7*$ps+38+32, 0) || die "Unable to seek $file"; +die "Unable to read $file" unless sysread(FILE, $_, 4) == 4; +my $sys_tables_root = unpack("N", $_); +my $page; +sysseek(FILE, $sys_tables_root*$ps, 0) || die "Unable to seek $file"; +die "Unable to read $file" unless sysread(FILE, $page, $ps) == $ps; +for (my $offset= 0x65; $offset; + $offset= unpack("n", substr($page,$offset-2,2))) +{ + my $n_fields= unpack("n", substr($page,$offset-4,2)) >> 1 & 0x3ff; + my $start= 0; + my $end= unpack("C", substr($page, $offset-7, 1)); + my $name= substr($page,$offset+$start,$end-$start); + for (my $i= 0; $i < $n_fields; $i++) { + my $end= unpack("C", substr($page, $offset-7-$i, 1)); + # Corrupt SYS_TABLES.MIX_LEN (ignored for ROW_FORMAT=REDUNDANT) + if ($i == 7 && $name =~ '^test/t[123]') + { + print "corrupted SYS_TABLES.MIX_LEN for $name\n"; + substr($page,$offset+$start,$end-$start)= pack("N", 255); + } + $start= $end & 0x7f; + } +} +substr($page,0,4)=pack("N",0xdeadbeef); +substr($page,$ps-8,4)=pack("N",0xdeadbeef); +sysseek(FILE, $sys_tables_root*$ps, 0) || die "Unable to seek $file"; +syswrite(FILE, $page, $ps)==$ps || die "Unable to write $file\n"; +close(FILE) || die "Unable to close $file\n"; +EOF -# FIXME: Corrupt the SYS_TABLES.TYPE of the tables, do not use --debug ---let $restart_parameters = --debug=d,ib_table_invalid_flags --source include/start_mysqld.inc --error ER_NO_SUCH_TABLE_IN_ENGINE TRUNCATE TABLE t1; @@ -82,25 +140,14 @@ SELECT COUNT(*) FROM t3; --error ER_ERROR_ON_RENAME RENAME TABLE t1 TO tee_one; DROP TABLE t1; +DROP TABLE t2,t3; ---disable_query_log -call mtr.add_suppression("\\[ERROR\\] InnoDB: Table `test`\\.`t1` in InnoDB data dictionary contains invalid flags\\. SYS_TABLES\\.MIX_LEN=255"); -call mtr.add_suppression("\\[ERROR\\] InnoDB: InnoDB: Error: table unused flags are:255"); -call mtr.add_suppression("\\[Warning\\] InnoDB: Parent table of FTS auxiliary table test/FTS_.* not found\\."); -call mtr.add_suppression("\\[Warning\\] InnoDB: Cannot open table test/t1 from the internal data dictionary of InnoDB though the \\.frm file for the table exists."); -call mtr.add_suppression("\\[ERROR\\] InnoDB: Table `test`.`t1` does not exist in the InnoDB internal data dictionary though MariaDB is trying to (drop|rename)"); - ---enable_query_log -let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err; ---let SEARCH_PATTERN= \[ERROR\] InnoDB: Table `test`\.`t1` in InnoDB data dictionary contains invalid flags. SYS_TABLES.MIX_LEN=255; ---source include/search_pattern_in_file.inc ---let SEARCH_PATTERN= \[ERROR\] InnoDB: InnoDB: Error: table unused flags are:255 ---source include/search_pattern_in_file.inc ---let SEARCH_PATTERN= \[Warning\] InnoDB: Parent table of FTS auxiliary table test/FTS_.* not found\. ---source include/search_pattern_in_file.inc ---let SEARCH_PATTERN= "\\[Warning\\] InnoDB: Cannot open table test/t1 from the internal data dictionary of InnoDB though the \.frm file for the table exists."; ---source include/search_pattern_in_file.inc ---let SEARCH_PATTERN= \[ERROR\] InnoDB: Table `test`.`t1` does not exist in the InnoDB internal data dictionary though MariaDB is trying to (drop|rename) +--let SEARCH_PATTERN= \[ERROR\] InnoDB: Table `test`\.`t1` in InnoDB data dictionary contains invalid flags\. SYS_TABLES.MIX_LEN=255\b --source include/search_pattern_in_file.inc -DROP TABLE t2,t3; +--let $restart_parameters= +--source include/restart_mysqld.inc + +--list_files $bugdir +--remove_files_wildcard $bugdir +--rmdir $bugdir diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc index ba1fbc5eb5d..0ca9ebfc622 100644 --- a/storage/innobase/dict/dict0load.cc +++ b/storage/innobase/dict/dict0load.cc @@ -185,17 +185,6 @@ dict_load_field_low( for temporary storage */ const rec_t* rec); /*!< in: SYS_FIELDS record */ -/** Load a table definition from a SYS_TABLES record to dict_table_t. -Do not load any columns or indexes. -@param[in] name Table name -@param[in] rec SYS_TABLES record -@param[out,own] table table, or NULL -@return error message -@retval NULL on success */ -static -const char* -dict_load_table_low(table_name_t& name, const rec_t* rec, dict_table_t** table); - /* If this flag is TRUE, then we will load the cluster index's (and tables') metadata even if it is marked as "corrupted". */ my_bool srv_load_corrupted; @@ -1149,6 +1138,7 @@ dict_sys_tablespaces_rec_read( @param[out] flags Pointer to table flags @param[out] flags2 Pointer to table flags2 @return true if the record was read correctly, false if not. */ +MY_ATTRIBUTE((warn_unused_result)) static bool dict_sys_tables_rec_read( @@ -1164,8 +1154,6 @@ dict_sys_tables_rec_read( ulint len; ulint type; - *flags2 = 0; - field = rec_get_nth_field_old( rec, DICT_FLD__SYS_TABLES__ID, &len); ut_ad(len == 8); @@ -1202,22 +1190,40 @@ dict_sys_tables_rec_read( " data dictionary contains invalid flags." " SYS_TABLES.TYPE=" << type << " SYS_TABLES.N_COLS=" << *n_cols; - *flags = ULINT_UNDEFINED; return(false); } *flags = dict_sys_tables_type_to_tf(type, *n_cols); - /* Get flags2 from SYS_TABLES.MIX_LEN */ - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_TABLES__MIX_LEN, &len); - *flags2 = mach_read_from_4(field); + /* For tables created before MySQL 4.1, there may be + garbage in SYS_TABLES.MIX_LEN where flags2 are found. Such tables + would always be in ROW_FORMAT=REDUNDANT which do not have the + high bit set in n_cols, and flags would be zero. + MySQL 4.1 was the first version to support innodb_file_per_table, + that is, *space_id != 0. */ + if (*flags != 0 || *space_id != 0 || *n_cols & DICT_N_COLS_COMPACT) { - /* DICT_TF2_FTS will be set when indexes are being loaded */ - *flags2 &= ~DICT_TF2_FTS; + /* Get flags2 from SYS_TABLES.MIX_LEN */ + field = rec_get_nth_field_old( + rec, DICT_FLD__SYS_TABLES__MIX_LEN, &len); + *flags2 = mach_read_from_4(field); + + if (!dict_tf2_is_valid(*flags, *flags2)) { + ib::error() << "Table " << table_name << " in InnoDB" + " data dictionary contains invalid flags." + " SYS_TABLES.MIX_LEN=" << *flags2; + return(false); + } + + /* DICT_TF2_FTS will be set when indexes are being loaded */ + *flags2 &= ~DICT_TF2_FTS; + + /* Now that we have used this bit, unset it. */ + *n_cols &= ~DICT_N_COLS_COMPACT; + } else { + *flags2 = 0; + } - /* Now that we have used this bit, unset it. */ - *n_cols &= ~DICT_N_COLS_COMPACT; return(true); } @@ -1280,11 +1286,10 @@ dict_check_sys_tables( ("name: %p, '%s'", table_name.m_name, table_name.m_name)); - dict_sys_tables_rec_read(rec, table_name, - &table_id, &space_id, - &n_cols, &flags, &flags2); - if (flags == ULINT_UNDEFINED - || is_system_tablespace(space_id)) { + if (!dict_sys_tables_rec_read(rec, table_name, + &table_id, &space_id, + &n_cols, &flags, &flags2) + || space_id == TRX_SYS_SPACE) { ut_free(table_name.m_name); continue; } @@ -2091,6 +2096,8 @@ func_exit: static const char* dict_load_index_del = "delete-marked record in SYS_INDEXES"; /** Error message for table->id mismatch in dict_load_index_low() */ static const char* dict_load_index_id_err = "SYS_INDEXES.TABLE_ID mismatch"; +/** Error message for SYS_TABLES flags mismatch in dict_load_table_low() */ +static const char* dict_load_table_flags = "incorrect flags in SYS_TABLES"; /** Load an index definition from a SYS_INDEXES record to dict_index_t. If allocate=TRUE, we will create a dict_index_t structure and fill it @@ -2539,11 +2546,9 @@ dict_load_table_low(table_name_t& name, const rec_t* rec, dict_table_t** table) return(error_text); } - dict_sys_tables_rec_read(rec, name, &table_id, &space_id, - &t_num, &flags, &flags2); - - if (flags == ULINT_UNDEFINED) { - return("incorrect flags in SYS_TABLES"); + if (!dict_sys_tables_rec_read(rec, name, &table_id, &space_id, + &t_num, &flags, &flags2)) { + return(dict_load_table_flags); } dict_table_decode_n_col(t_num, &n_cols, &n_v_col); @@ -2857,8 +2862,9 @@ err_exit: err_msg = dict_load_table_low(name, rec, &table); if (err_msg) { - - ib::error() << err_msg; + if (err_msg != dict_load_table_flags) { + ib::error() << err_msg; + } goto err_exit; } @@ -2914,21 +2920,6 @@ err_exit: } } - /* We don't trust the table->flags2(retrieved from SYS_TABLES.MIX_LEN - field) if the datafiles are from 3.23.52 version. To identify this - version, we do the below check and reset the flags. */ - if (!DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID) - && table->space == srv_sys_space.space_id() - && table->flags == 0) { - table->flags2 = 0; - } - - DBUG_EXECUTE_IF("ib_table_invalid_flags", - if(strcmp(table->name.m_name, "test/t1") == 0) { - table->flags2 = 255; - table->flags = 255; - }); - if (!dict_tf2_is_valid(table->flags, table->flags2)) { ib::error() << "Table " << table->name << " in InnoDB" " data dictionary contains invalid flags." diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index 1cce58dd9c5..60cc3c91fef 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -1715,21 +1715,6 @@ fts_drop_tables( return(error); } -/** Extract only the required flags from table->flags2 for FTS Aux -tables. -@param[in] in_flags2 Table flags2 -@return extracted flags2 for FTS aux tables */ -static inline -ulint -fts_get_table_flags2_for_aux_tables( - ulint flags2) -{ - /* Extract the file_per_table flag & temporary file flag - from the main FTS table flags2 */ - return((flags2 & DICT_TF2_USE_FILE_PER_TABLE) | - (flags2 & DICT_TF2_TEMPORARY)); -} - /** Create dict_table_t object for FTS Aux tables. @param[in] aux_table_name FTS Aux table name @param[in] table table object of FTS Index @@ -1744,7 +1729,9 @@ fts_create_in_mem_aux_table( { dict_table_t* new_table = dict_mem_table_create( aux_table_name, table->space, n_cols, 0, table->flags, - fts_get_table_flags2_for_aux_tables(table->flags2)); + table->space == TRX_SYS_SPACE + ? 0 : table->space == SRV_TMP_SPACE_ID + ? DICT_TF2_TEMPORARY : DICT_TF2_USE_FILE_PER_TABLE); if (DICT_TF_HAS_DATA_DIR(table->flags)) { ut_ad(table->data_dir_path != NULL); |