[MDEV-30235] InnoDB crash on table-rebuilding DDL when the statistics tables are corrupted Created: 2022-12-15  Updated: 2022-12-15  Resolved: 2022-12-15

Status: Closed
Project: MariaDB Server
Component/s: Storage Engine - InnoDB
Affects Version/s: 10.6, 10.7, 10.8, 10.9, 10.10, 10.11
Fix Version/s: 10.11.2, 10.6.12, 10.7.8, 10.8.7, 10.9.5, 10.10.3

Type: Bug Priority: Major
Reporter: Marko Mäkelä Assignee: Marko Mäkelä
Resolution: Fixed Votes: 0
Labels: crash

Issue Links:
Blocks
blocks MDEV-25004 Missing row in FTS_DOC_ID_INDEX durin... Closed

 Description   

An upgrade test related to MDEV-25004 triggered the following:

2022-12-15 15:22:32 3 [ERROR] InnoDB: Table `mysql`.`innodb_index_stats` is corrupted. Please drop the table and recreate.
2022-12-15 15:22:32 3 [ERROR] InnoDB: Table `mysql`.`innodb_table_stats` is corrupted. Please drop the table and recreate.
2022-12-15 15:22:32 0x7f999f7686c0  InnoDB: Assertion failure in file /mariadb/10.6m/storage/innobase/pars/pars0pars.cc line 771
InnoDB: Failing assertion: sym_node->table != NULL

The server had been started up with a subset of a 10.3 data directory. According to the InnoDB data dictionary, the statistics tables exist, but the .ibd files for them had been omitted. After MDEV-13542 was fixed, InnoDB is not expected to crash on any corruption, but it did here:

#4  0x000055bb9490ce23 in ut_dbg_assertion_failed (
    expr=expr@entry=0x55bb910ed760 "sym_node->table != NULL", 
    file=file@entry=0x55bb910ec7e0 "/mariadb/10.6m/storage/innobase/pars/pars0pars.cc", line=line@entry=771) at /mariadb/10.6m/storage/innobase/ut/ut0dbg.cc:60
#5  0x000055bb9460a356 in pars_retrieve_table_def (
    sym_node=sym_node@entry=0x629000c174d0)
    at /mariadb/10.6m/storage/innobase/pars/pars0pars.cc:771
#6  0x000055bb946117c7 in pars_update_statement (node=0x629000c175d8, 
    cursor_sym=cursor_sym@entry=0x0, 
    search_cond=search_cond@entry=0x629000c17ba8)
    at /mariadb/10.6m/storage/innobase/pars/pars0pars.cc:1182
#7  0x000055bb94617596 in yyparse () at /dev/shm/10.6mu/pars0grm.y:372
#8  0x000055bb9460ee6b in pars_sql (info=info@entry=0x61700012fb20, 
    str=str@entry=0x55bb91031680 "PROCEDURE DELETE_FROM_TABLE_STATS () IS\nBEGIN\nDELETE FROM \"mysql/innodb_table_stats\" WHERE\ndatabase_name = :database_name AND\ntable_name = :table_name;\nEND;\n")
    at /mariadb/10.6m/storage/innobase/pars/pars0pars.cc:1986
#9  0x000055bb94632227 in que_eval_sql (info=info@entry=0x61700012fb20, 
    sql=sql@entry=0x55bb91031680 "PROCEDURE DELETE_FROM_TABLE_STATS () IS\nBEGIN\nDELETE FROM \"mysql/innodb_table_stats\" WHERE\ndatabase_name = :database_name AND\ntable_name = :table_name;\nEND;\n", trx=trx@entry=0x7f99a5aefe40)
    at /mariadb/10.6m/storage/innobase/que/que0que.cc:705
#10 0x000055bb94230804 in dict_stats_exec_sql (
    pinfo=pinfo@entry=0x61700012fb20, 
    sql=sql@entry=0x55bb91031680 "PROCEDURE DELETE_FROM_TABLE_STATS () IS\nBEGIN\nDELETE FROM \"mysql/innodb_table_stats\" WHERE\ndatabase_name = :database_name AND\ntable_name = :table_name;\nEND;\n", trx=trx@entry=0x7f99a5aefe40)
    at /mariadb/10.6m/storage/innobase/dict/dict0stats.cc:532
#11 0x000055bb94230934 in dict_stats_delete_from_table_stats (
    database_name=database_name@entry=0x7f999f762330 "test", 
    table_name=table_name@entry=0x7f999f762440 "articles2", 
    trx=trx@entry=0x7f99a5aefe40)
    at /mariadb/10.6m/storage/innobase/dict/dict0stats.cc:4244
#12 0x000055bb942a782d in trx_t::drop_table_statistics (
    this=this@entry=0x7f99a5aefe40, 
      name=@0x7f999f762600: {m_name = 0x61e000056620 "test/articles2"})
    at /mariadb/10.6m/storage/innobase/dict/drop.cc:133
#13 0x000055bb943dfee4 in commit_try_rebuild (
    ha_alter_info=ha_alter_info@entry=0x7f999f763820, 
    ctx=ctx@entry=0x62b0000c6830, 
    altered_table=altered_table@entry=0x7f999f7643a0, 
    old_table=old_table@entry=0x6190000ca898, trx=trx@entry=0x7f99a5aefe40, 
    table_name=table_name@entry=0x61b00010a655 "articles2")
    at /mariadb/10.6m/storage/innobase/handler/handler0alter.cc:10191

I tried but failed to create a simple test case of this. The fix is simple: avoid the call when the statistics tables could not be opened.

diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index f91c0a23c08..dd83ed49af0 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -10101,6 +10101,7 @@ commit_try_rebuild(
 	ha_innobase_inplace_ctx*ctx,
 	TABLE*			altered_table,
 	const TABLE*		old_table,
+	bool			statistics_exist,
 	trx_t*			trx,
 	const char*		table_name)
 {
@@ -10171,7 +10172,9 @@ commit_try_rebuild(
 		if (error == DB_SUCCESS) {
 			/* The statistics for the surviving indexes will be
 			re-inserted in alter_stats_rebuild(). */
-			error = trx->drop_table_statistics(old_name);
+			if (statistics_exist) {
+				error = trx->drop_table_statistics(old_name);
+			}
 			if (error == DB_SUCCESS) {
 				error = trx->drop_table(*user_table);
 			}
@@ -11316,6 +11319,7 @@ ha_innobase::commit_inplace_alter_table(
 
 			if (commit_try_rebuild(ha_alter_info, ctx,
 					       altered_table, table,
+					       table_stats && index_stats,
 					       trx,
 					       table_share->table_name.str)) {
 				goto fail;

On TRUNCATE TABLE, a corresponding check was already in place:

    if (err == DB_SUCCESS && table_stats && index_stats)
      err= trx->drop_table_statistics(table->name);


Generated at Thu Feb 08 10:14:42 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.