mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-10435 crash with bad stat tables.
Functions from sql/statistics.cc don't seem to expect stat tables to fail or to have inadequate structure. Table open errors suppressed and some validity checks added. Invalid tables reported to the server log.
This commit is contained in:
@ -1676,4 +1676,24 @@ analyze table t1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Table is already up to date
|
||||
drop table t1;
|
||||
#
|
||||
# MDEV-10435 crash with bad stat tables
|
||||
#
|
||||
set use_stat_tables='preferably';
|
||||
call mtr.add_suppression("Column count of mysql.table_stats is wrong. Expected 3, found 1. The table is probably corrupted");
|
||||
rename table mysql.table_stats to test.table_stats;
|
||||
flush tables;
|
||||
create table t1 (a int);
|
||||
rename table t1 to t2, t3 to t4;
|
||||
ERROR 42S02: Table 'test.t3' doesn't exist
|
||||
drop table t1;
|
||||
rename table test.table_stats to mysql.table_stats;
|
||||
rename table mysql.table_stats to test.table_stats;
|
||||
create table mysql.table_stats (a int);
|
||||
flush tables;
|
||||
create table t1 (a int);
|
||||
rename table t1 to t2, t3 to t4;
|
||||
ERROR 42S02: Table 'test.t3' doesn't exist
|
||||
drop table t1, mysql.table_stats;
|
||||
rename table test.table_stats to mysql.table_stats;
|
||||
set use_stat_tables=@save_use_stat_tables;
|
||||
|
@ -740,4 +740,28 @@ show variables like 'use_stat_tables';
|
||||
analyze table t1;
|
||||
drop table t1;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-10435 crash with bad stat tables
|
||||
--echo #
|
||||
|
||||
set use_stat_tables='preferably';
|
||||
call mtr.add_suppression("Column count of mysql.table_stats is wrong. Expected 3, found 1. The table is probably corrupted");
|
||||
|
||||
rename table mysql.table_stats to test.table_stats;
|
||||
flush tables;
|
||||
create table t1 (a int);
|
||||
--error ER_NO_SUCH_TABLE
|
||||
rename table t1 to t2, t3 to t4;
|
||||
drop table t1;
|
||||
rename table test.table_stats to mysql.table_stats;
|
||||
|
||||
rename table mysql.table_stats to test.table_stats;
|
||||
create table mysql.table_stats (a int);
|
||||
flush tables;
|
||||
create table t1 (a int);
|
||||
--error ER_NO_SUCH_TABLE
|
||||
rename table t1 to t2, t3 to t4;
|
||||
drop table t1, mysql.table_stats;
|
||||
rename table test.table_stats to mysql.table_stats;
|
||||
|
||||
set use_stat_tables=@save_use_stat_tables;
|
||||
|
@ -166,20 +166,8 @@ const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] =
|
||||
static const TABLE_FIELD_DEF
|
||||
event_table_def= {ET_FIELD_COUNT, event_table_fields, 0, (uint*) 0};
|
||||
|
||||
class Event_db_intact : public Table_check_intact
|
||||
{
|
||||
protected:
|
||||
void report_error(uint, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
error_log_print(ERROR_LEVEL, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
};
|
||||
|
||||
/** In case of an error, a message is printed to the error log. */
|
||||
static Event_db_intact table_intact;
|
||||
static Table_check_intact_log_error table_intact;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -448,19 +448,7 @@ static const TABLE_FIELD_DEF mysql_gtid_slave_pos_tabledef= {
|
||||
mysql_rpl_slave_state_pk_parts
|
||||
};
|
||||
|
||||
class Gtid_db_intact : public Table_check_intact
|
||||
{
|
||||
protected:
|
||||
void report_error(uint, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
error_log_print(ERROR_LEVEL, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
};
|
||||
|
||||
static Gtid_db_intact gtid_table_intact;
|
||||
static Table_check_intact_log_error gtid_table_intact;
|
||||
|
||||
/*
|
||||
Check that the mysql.gtid_slave_pos table has the correct definition.
|
||||
|
@ -129,6 +129,128 @@ inline void init_table_list_for_single_stat_table(TABLE_LIST *tbl,
|
||||
}
|
||||
|
||||
|
||||
static Table_check_intact_log_error stat_table_intact;
|
||||
|
||||
static const
|
||||
TABLE_FIELD_TYPE table_stat_fields[TABLE_STAT_N_FIELDS] =
|
||||
{
|
||||
{
|
||||
{ C_STRING_WITH_LEN("db_name") },
|
||||
{ C_STRING_WITH_LEN("varchar(64)") },
|
||||
{ C_STRING_WITH_LEN("utf8") }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("table_name") },
|
||||
{ C_STRING_WITH_LEN("varchar(64)") },
|
||||
{ C_STRING_WITH_LEN("utf8") }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("cardinality") },
|
||||
{ C_STRING_WITH_LEN("bigint(21)") },
|
||||
{ NULL, 0 }
|
||||
},
|
||||
};
|
||||
static const uint table_stat_pk_col[]= {0,1};
|
||||
static const TABLE_FIELD_DEF
|
||||
table_stat_def= {TABLE_STAT_N_FIELDS, table_stat_fields, 2, table_stat_pk_col };
|
||||
|
||||
static const
|
||||
TABLE_FIELD_TYPE column_stat_fields[COLUMN_STAT_N_FIELDS] =
|
||||
{
|
||||
{
|
||||
{ C_STRING_WITH_LEN("db_name") },
|
||||
{ C_STRING_WITH_LEN("varchar(64)") },
|
||||
{ C_STRING_WITH_LEN("utf8") }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("table_name") },
|
||||
{ C_STRING_WITH_LEN("varchar(64)") },
|
||||
{ C_STRING_WITH_LEN("utf8") }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("column_name") },
|
||||
{ C_STRING_WITH_LEN("varchar(64)") },
|
||||
{ C_STRING_WITH_LEN("utf8") }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("min_value") },
|
||||
{ C_STRING_WITH_LEN("varbinary(255)") },
|
||||
{ NULL, 0 }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("max_value") },
|
||||
{ C_STRING_WITH_LEN("varbinary(255)") },
|
||||
{ NULL, 0 }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("nulls_ratio") },
|
||||
{ C_STRING_WITH_LEN("decimal(12,4)") },
|
||||
{ NULL, 0 }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("avg_length") },
|
||||
{ C_STRING_WITH_LEN("decimal(12,4)") },
|
||||
{ NULL, 0 }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("avg_frequency") },
|
||||
{ C_STRING_WITH_LEN("decimal(12,4)") },
|
||||
{ NULL, 0 }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("hist_size") },
|
||||
{ C_STRING_WITH_LEN("tinyint(3)") },
|
||||
{ NULL, 0 }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("hist_type") },
|
||||
{ C_STRING_WITH_LEN("enum('SINGLE_PREC_HB','DOUBLE_PREC_HB')") },
|
||||
{ C_STRING_WITH_LEN("utf8") }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("histogram") },
|
||||
{ C_STRING_WITH_LEN("varbinary(255)") },
|
||||
{ NULL, 0 }
|
||||
}
|
||||
};
|
||||
static const uint column_stat_pk_col[]= {0,1,2};
|
||||
static const TABLE_FIELD_DEF
|
||||
column_stat_def= {COLUMN_STAT_N_FIELDS, column_stat_fields, 3, column_stat_pk_col};
|
||||
|
||||
static const
|
||||
TABLE_FIELD_TYPE index_stat_fields[INDEX_STAT_N_FIELDS] =
|
||||
{
|
||||
{
|
||||
{ C_STRING_WITH_LEN("db_name") },
|
||||
{ C_STRING_WITH_LEN("varchar(64)") },
|
||||
{ C_STRING_WITH_LEN("utf8") }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("table_name") },
|
||||
{ C_STRING_WITH_LEN("varchar(64)") },
|
||||
{ C_STRING_WITH_LEN("utf8") }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("index") },
|
||||
{ C_STRING_WITH_LEN("varchar(64)") },
|
||||
{ C_STRING_WITH_LEN("utf8") }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("prefix_arity") },
|
||||
{ C_STRING_WITH_LEN("int(11)") },
|
||||
{ NULL, 0 }
|
||||
},
|
||||
{
|
||||
{ C_STRING_WITH_LEN("avg_frequency") },
|
||||
{ C_STRING_WITH_LEN("decimal(12,4)") },
|
||||
{ NULL, 0 }
|
||||
}
|
||||
};
|
||||
static const uint index_stat_pk_col[]= {0,1,2,3};
|
||||
static const TABLE_FIELD_DEF
|
||||
index_stat_def= {INDEX_STAT_N_FIELDS, index_stat_fields, 4, index_stat_pk_col};
|
||||
|
||||
|
||||
/**
|
||||
@brief
|
||||
Open all statistical tables and lock them
|
||||
@ -139,10 +261,30 @@ inline int open_stat_tables(THD *thd, TABLE_LIST *tables,
|
||||
Open_tables_backup *backup,
|
||||
bool for_write)
|
||||
{
|
||||
int rc;
|
||||
|
||||
Dummy_error_handler deh; // suppress errors
|
||||
thd->push_internal_handler(&deh);
|
||||
init_table_list_for_stat_tables(tables, for_write);
|
||||
init_mdl_requests(tables);
|
||||
return open_system_tables_for_read(thd, tables, backup);
|
||||
}
|
||||
rc= open_system_tables_for_read(thd, tables, backup);
|
||||
thd->pop_internal_handler();
|
||||
|
||||
|
||||
/* If the number of tables changes, we should revise the check below. */
|
||||
DBUG_ASSERT(STATISTICS_TABLES == 3);
|
||||
|
||||
if (!rc &&
|
||||
(stat_table_intact.check(tables[TABLE_STAT].table, &table_stat_def) ||
|
||||
stat_table_intact.check(tables[COLUMN_STAT].table, &column_stat_def) ||
|
||||
stat_table_intact.check(tables[INDEX_STAT].table, &index_stat_def)))
|
||||
{
|
||||
close_system_tables(thd, backup);
|
||||
rc= 1;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -2725,10 +2867,7 @@ int update_statistics_for_table(THD *thd, TABLE *table)
|
||||
DEBUG_SYNC(thd, "statistics_update_start");
|
||||
|
||||
if (open_stat_tables(thd, tables, &open_tables_backup, TRUE))
|
||||
{
|
||||
thd->clear_error();
|
||||
DBUG_RETURN(rc);
|
||||
}
|
||||
|
||||
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
|
||||
|
||||
@ -3156,10 +3295,7 @@ int delete_statistics_for_table(THD *thd, LEX_STRING *db, LEX_STRING *tab)
|
||||
DBUG_ENTER("delete_statistics_for_table");
|
||||
|
||||
if (open_stat_tables(thd, tables, &open_tables_backup, TRUE))
|
||||
{
|
||||
thd->clear_error();
|
||||
DBUG_RETURN(rc);
|
||||
}
|
||||
|
||||
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
|
||||
|
||||
@ -3398,10 +3534,7 @@ int rename_table_in_stat_tables(THD *thd, LEX_STRING *db, LEX_STRING *tab,
|
||||
DBUG_ENTER("rename_table_in_stat_tables");
|
||||
|
||||
if (open_stat_tables(thd, tables, &open_tables_backup, TRUE))
|
||||
{
|
||||
thd->clear_error();
|
||||
DBUG_RETURN(rc);
|
||||
}
|
||||
DBUG_RETURN(0); // not an error
|
||||
|
||||
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
|
||||
|
||||
|
@ -52,7 +52,8 @@ enum enum_table_stat_col
|
||||
{
|
||||
TABLE_STAT_DB_NAME,
|
||||
TABLE_STAT_TABLE_NAME,
|
||||
TABLE_STAT_CARDINALITY
|
||||
TABLE_STAT_CARDINALITY,
|
||||
TABLE_STAT_N_FIELDS
|
||||
};
|
||||
|
||||
enum enum_column_stat_col
|
||||
@ -67,7 +68,8 @@ enum enum_column_stat_col
|
||||
COLUMN_STAT_AVG_FREQUENCY,
|
||||
COLUMN_STAT_HIST_SIZE,
|
||||
COLUMN_STAT_HIST_TYPE,
|
||||
COLUMN_STAT_HISTOGRAM
|
||||
COLUMN_STAT_HISTOGRAM,
|
||||
COLUMN_STAT_N_FIELDS
|
||||
};
|
||||
|
||||
enum enum_index_stat_col
|
||||
@ -76,7 +78,8 @@ enum enum_index_stat_col
|
||||
INDEX_STAT_TABLE_NAME,
|
||||
INDEX_STAT_INDEX_NAME,
|
||||
INDEX_STAT_PREFIX_ARITY,
|
||||
INDEX_STAT_AVG_FREQUENCY
|
||||
INDEX_STAT_AVG_FREQUENCY,
|
||||
INDEX_STAT_N_FIELDS
|
||||
};
|
||||
|
||||
inline
|
||||
|
@ -3861,6 +3861,15 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
|
||||
}
|
||||
|
||||
|
||||
void Table_check_intact_log_error::report_error(uint, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
error_log_print(ERROR_LEVEL, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Traverse portion of wait-for graph which is reachable through edge
|
||||
represented by this flush ticket in search for deadlocks.
|
||||
|
10
sql/table.h
10
sql/table.h
@ -524,6 +524,16 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
If the table isn't valid, report the error to the server log only.
|
||||
*/
|
||||
class Table_check_intact_log_error : public Table_check_intact
|
||||
{
|
||||
protected:
|
||||
void report_error(uint, const char *fmt, ...);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Class representing the fact that some thread waits for table
|
||||
share to be flushed. Is used to represent information about
|
||||
|
Reference in New Issue
Block a user