Consider the procedure of tablespace importing for two cases:
1) with cfg file:
Let's look into row_import_for_mysql():
...
err = row_import_read_cfg(table, trx->mysql_thd, cfg);
/* Check if the table column definitions match the contents
of the config file. */
if (err == DB_SUCCESS) {
/* We have a schema file, try and match it with our
data dictionary. */
err = cfg.match_schema(trx->mysql_thd);
/* Update index->page and SYS_INDEXES.PAGE_NO to match the
B-tree root page numbers in the tablespace. Use the index
name from the .cfg file to find match. */
if (err == DB_SUCCESS) {
cfg.set_root_by_name();
autoinc = cfg.m_autoinc;
}
So row_import_read_cfg() just reads metadata from cfg file into cfg object, while
row_import::match_schema() just checks the correctness of loaded metadata comparing them with what is currently in internal table object. row_import::set_root_by_name()
sets page and space numbers for the corresponding internal index objects in table->indexes container. To correspond loaded cfg index description with internal index description in table->indexes the index names are used (see row_import::set_root_by_name()).
2) without cfg file:
row_import_for_mysql()
...
} else if (cfg.m_missing) {
rw_lock_s_unlock_gen(dict_operation_lock, 0);
/* We don't have a schema file, we will have to discover
the index root pages from the .ibd file and skip the schema
matching step. */
fetchIndexRootPages gathers index space-pageno pairs from the attached tablespace in
the order of their root pages appearance in the tablespace. The same order will be preserved for cfg object, as fetchIndexRootPages.build_row_import() just fills index descriptors array in cfg object. Then row_import::set_root_by_heuristic() does the same thing as row_import::set_root_by_name() in (1), i.e. sets page and space numbers for the corresponding internal index objects in tables->indexes container.
But the general thing is that row_import::set_root_by_heuristic() does not use index names to map internal index descriptor from table->indexes container to cfg index descriptor, indexes order is used instead.
So, for example, if indexes order during table creation is not the same as indexes root pages order in the attached table space, then the correspondence will be wrong and the indexes in table->indexes container will contain wrong page number.
There are two mtr test cases attached. They are identical as the reason of the error is the same - wrong root page number for the index if the tablespace is imported without cfg file. The difference is in the results itself. The first test finishes with crash, while the second test outputs wrong data when the correspondent index is used.
To make them work just copy them into "mysql-test/suite/innodb/t/".
The detailed description is the following. How the initial data led to the indexes order issue see item 1 in https:/ /percona. zendesk. com/attachments /token/ EzERiNDmk8a1gJj tQMhPfA7Dv/ ?name=descr. txt.
All the code and description is for 5.7.
Consider the procedure of tablespace importing for two cases:
1) with cfg file: for_mysql( ): read_cfg( table, trx->mysql_thd, cfg);
Let's look into row_import_
...
err = row_import_
/* Check if the table column definitions match the contents
of the config file. */
if (err == DB_SUCCESS) {
/* We have a schema file, try and match it with our
data dictionary. */
err = cfg.match_ schema( trx->mysql_ thd);
/* Update index->page and SYS_INDEXES.PAGE_NO to match the
B-tree root page numbers in the tablespace. Use the index
name from the .cfg file to find match. */
if (err == DB_SUCCESS) { set_root_ by_name( );
cfg.
autoinc = cfg.m_autoinc;
}
rw_lock_ s_unlock_ gen(dict_ operation_ lock, 0);
DBUG_ EXECUTE_ IF("ib_ import_ set_index_ root_failure" , MANY_CONCURRENT _TRXS;) ;
err = DB_TOO_
} else if (cfg.m_missing) {
...
So row_import_ read_cfg( ) just reads metadata from cfg file into cfg object, while :match_ schema( ) just checks the correctness of loaded metadata comparing them with what is currently in internal table object. row_import: :set_root_ by_name( ) :set_root_ by_name( )).
row_import:
sets page and space numbers for the corresponding internal index objects in table->indexes container. To correspond loaded cfg index description with internal index description in table->indexes the index names are used (see row_import:
2) without cfg file: for_mysql( )
row_import_
...
} else if (cfg.m_missing) {
rw_lock_ s_unlock_ gen(dict_ operation_ lock, 0);
/* We don't have a schema file, we will have to discover
the index root pages from the .ibd file and skip the schema
matching step. */
ut_a(err == DB_FAIL);
cfg.m_ page_size. copy_from( univ_page_ size);
FetchIndexRoo tPages fetchIndexRootP ages(table, trx);
err = fil_tablespace_ iterate( m_page_ size.physical( ), m_page_ size.physical( )), otPages) ;
table, IO_BUFFER_SIZE(
cfg.
cfg.
fetchIndexRo
if (err == DB_SUCCESS) {
err = fetchIndexRootP ages.build_ row_import( &cfg);
/* Update index->page and SYS_INDEXES.PAGE_NO
to match the B-tree root page numbers in the
tablespace. */
if (err == DB_SUCCESS) { root_by_ heuristic( );
err = cfg.set_
}
}
space_flags = fetchIndexRootP ages.get_ space_flags( );
} else {
...
fetchIndexRootPages gathers index space-pageno pairs from the attached tablespace in ages.build_ row_import( ) just fills index descriptors array in cfg object. Then row_import: :set_root_ by_heuristic( ) does the same thing as row_import: :set_root_ by_name( ) in (1), i.e. sets page and space numbers for the corresponding internal index objects in tables->indexes container.
the order of their root pages appearance in the tablespace. The same order will be preserved for cfg object, as fetchIndexRootP
But the general thing is that row_import: :set_root_ by_heuristic( ) does not use index names to map internal index descriptor from table->indexes container to cfg index descriptor, indexes order is used instead.
So, for example, if indexes order during table creation is not the same as indexes root pages order in the attached table space, then the correspondence will be wrong and the indexes in table->indexes container will contain wrong page number.
There are two mtr test cases attached. They are identical as the reason of the error is the same - wrong root page number for the index if the tablespace is imported without cfg file. The difference is in the results itself. The first test finishes with crash, while the second test outputs wrong data when the correspondent index is used.
To make them work just copy them into "mysql- test/suite/ innodb/ t/".