Commit 383f77cd authored by Marko Mäkelä's avatar Marko Mäkelä

Cleanup: Simplify dict_table_schema_check()

parent 7ff9e583
call mtr.add_suppression("InnoDB: Table `mysql`.`innodb_index_stats` not found");
call mtr.add_suppression("InnoDB: Fetch of persistent statistics requested for table.*");
call mtr.add_suppression("InnoDB: Column stat_value in table mysql\\.innodb_index_stats is BIGINT UNSIGNED but should be BIGINT UNSIGNED NOT NULL");
call mtr.add_suppression("InnoDB: Fetch of persistent statistics requested for table");
CREATE TABLE test_ps_fetch_corrupted
(a INT, PRIMARY KEY (a))
ENGINE=INNODB STATS_PERSISTENT=1;
......@@ -17,7 +17,21 @@ FROM mysql.innodb_table_stats WHERE table_name = 'test_ps_fetch_corrupted';
n_rows 0
clustered_index_size 1
sum_of_other_index_sizes 0
ALTER TABLE mysql.innodb_index_stats RENAME TO mysql.innodb_index_stats_;
ALTER TABLE mysql.innodb_index_stats MODIFY stat_value BIGINT UNSIGNED NULL;
FLUSH TABLE test_ps_fetch_corrupted;
SELECT seq_in_index, column_name, cardinality
FROM information_schema.statistics WHERE table_name = 'test_ps_fetch_corrupted'
ORDER BY index_name, seq_in_index;
seq_in_index 1
column_name a
cardinality 0
SELECT table_rows, avg_row_length, max_data_length, index_length
FROM information_schema.tables WHERE table_name = 'test_ps_fetch_corrupted';
table_rows 0
avg_row_length 0
max_data_length 0
index_length 0
ALTER TABLE mysql.innodb_index_stats MODIFY stat_value BIGINT UNSIGNED NOT NULL;
FLUSH TABLE test_ps_fetch_corrupted;
SELECT seq_in_index, column_name, cardinality
FROM information_schema.statistics WHERE table_name = 'test_ps_fetch_corrupted'
......@@ -31,6 +45,5 @@ table_rows 0
avg_row_length 0
max_data_length 0
index_length 0
ALTER TABLE mysql.innodb_index_stats_ RENAME TO mysql.innodb_index_stats;
DROP TABLE test_ps_fetch_corrupted;
# restart
......@@ -10,8 +10,8 @@
# server restart
-- source include/not_embedded.inc
call mtr.add_suppression("InnoDB: Table `mysql`.`innodb_index_stats` not found");
call mtr.add_suppression("InnoDB: Fetch of persistent statistics requested for table.*");
call mtr.add_suppression("InnoDB: Column stat_value in table mysql\\.innodb_index_stats is BIGINT UNSIGNED but should be BIGINT UNSIGNED NOT NULL");
call mtr.add_suppression("InnoDB: Fetch of persistent statistics requested for table");
-- vertical_results
......@@ -27,7 +27,7 @@ SELECT n_rows, clustered_index_size, sum_of_other_index_sizes
FROM mysql.innodb_table_stats WHERE table_name = 'test_ps_fetch_corrupted';
# corrupt the persistent storage
ALTER TABLE mysql.innodb_index_stats RENAME TO mysql.innodb_index_stats_;
ALTER TABLE mysql.innodb_index_stats MODIFY stat_value BIGINT UNSIGNED NULL;
# reopen the table, this will attept to read from the persistent storage
FLUSH TABLE test_ps_fetch_corrupted;
......@@ -42,7 +42,15 @@ SELECT table_rows, avg_row_length, max_data_length, index_length
FROM information_schema.tables WHERE table_name = 'test_ps_fetch_corrupted';
# restore the persistent storage
ALTER TABLE mysql.innodb_index_stats_ RENAME TO mysql.innodb_index_stats;
ALTER TABLE mysql.innodb_index_stats MODIFY stat_value BIGINT UNSIGNED NOT NULL;
FLUSH TABLE test_ps_fetch_corrupted;
SELECT seq_in_index, column_name, cardinality
FROM information_schema.statistics WHERE table_name = 'test_ps_fetch_corrupted'
ORDER BY index_name, seq_in_index;
SELECT table_rows, avg_row_length, max_data_length, index_length
FROM information_schema.tables WHERE table_name = 'test_ps_fetch_corrupted';
DROP TABLE test_ps_fetch_corrupted;
......
......@@ -104,11 +104,6 @@ ulong zip_pad_max = 50;
/** Identifies generated InnoDB foreign key names */
static char dict_ibfk[] = "_ibfk_";
bool innodb_table_stats_not_found = false;
bool innodb_index_stats_not_found = false;
static bool innodb_table_stats_not_found_reported = false;
static bool innodb_index_stats_not_found_reported = false;
/*******************************************************************//**
Tries to find column names for the index and sets the col field of the
index.
......@@ -4600,220 +4595,6 @@ dict_table_check_for_dup_indexes(
}
#endif /* UNIV_DEBUG */
/** Auxiliary macro used inside dict_table_schema_check(). */
#define CREATE_TYPES_NAMES() \
dtype_sql_name((unsigned) req_schema->columns[i].mtype, \
(unsigned) req_schema->columns[i].prtype_mask, \
(unsigned) req_schema->columns[i].len, \
req_type, sizeof(req_type)); \
dtype_sql_name(table->cols[j].mtype, \
table->cols[j].prtype, \
table->cols[j].len, \
actual_type, sizeof(actual_type))
/*********************************************************************//**
Checks whether a table exists and whether it has the given structure.
The table must have the same number of columns with the same names and
types. The order of the columns does not matter.
The caller must own the dictionary mutex.
dict_table_schema_check() @{
@return DB_SUCCESS if the table exists and contains the necessary columns */
dberr_t
dict_table_schema_check(
/*====================*/
dict_table_schema_t* req_schema, /*!< in/out: required table
schema */
char* errstr, /*!< out: human readable error
message if != DB_SUCCESS is
returned */
size_t errstr_sz) /*!< in: errstr size */
{
char buf[MAX_FULL_NAME_LEN];
char req_type[64];
char actual_type[64];
dict_table_t* table;
ulint i;
dict_sys.assert_locked();
table = dict_table_get_low(req_schema->table_name);
if (table == NULL) {
bool should_print=true;
/* no such table */
if (innobase_strcasecmp(req_schema->table_name, "mysql/innodb_table_stats") == 0) {
if (innodb_table_stats_not_found_reported == false) {
innodb_table_stats_not_found = true;
innodb_table_stats_not_found_reported = true;
} else {
should_print = false;
}
} else if (innobase_strcasecmp(req_schema->table_name, "mysql/innodb_index_stats") == 0 ) {
if (innodb_index_stats_not_found_reported == false) {
innodb_index_stats_not_found = true;
innodb_index_stats_not_found_reported = true;
} else {
should_print = false;
}
}
if (should_print) {
snprintf(errstr, errstr_sz,
"Table %s not found.",
ut_format_name(req_schema->table_name,
buf, sizeof(buf)));
return(DB_TABLE_NOT_FOUND);
} else {
return(DB_STATS_DO_NOT_EXIST);
}
}
if (!table->is_readable() && !table->space) {
/* missing tablespace */
snprintf(errstr, errstr_sz,
"Tablespace for table %s is missing.",
ut_format_name(req_schema->table_name,
buf, sizeof(buf)));
return(DB_TABLE_NOT_FOUND);
}
if (ulint(table->n_def - DATA_N_SYS_COLS) != req_schema->n_cols) {
/* the table has a different number of columns than required */
snprintf(errstr, errstr_sz,
"%s has %d columns but should have " ULINTPF ".",
ut_format_name(req_schema->table_name, buf,
sizeof buf),
table->n_def - DATA_N_SYS_COLS,
req_schema->n_cols);
return(DB_ERROR);
}
/* For each column from req_schema->columns[] search
whether it is present in table->cols[].
The following algorithm is O(n_cols^2), but is optimized to
be O(n_cols) if the columns are in the same order in both arrays. */
for (i = 0; i < req_schema->n_cols; i++) {
ulint j = dict_table_has_column(
table, req_schema->columns[i].name, i);
if (j == table->n_def) {
snprintf(errstr, errstr_sz,
"required column %s"
" not found in table %s.",
req_schema->columns[i].name,
ut_format_name(
req_schema->table_name,
buf, sizeof(buf)));
return(DB_ERROR);
}
/* we found a column with the same name on j'th position,
compare column types and flags */
/* check length for exact match */
if (req_schema->columns[i].len == table->cols[j].len) {
} else if (!strcmp(req_schema->table_name, TABLE_STATS_NAME)
|| !strcmp(req_schema->table_name,
INDEX_STATS_NAME)) {
ut_ad(table->cols[j].len < req_schema->columns[i].len);
ib::warn() << "Table " << req_schema->table_name
<< " has length mismatch in the"
<< " column name "
<< req_schema->columns[i].name
<< ". Please run mysql_upgrade";
} else {
CREATE_TYPES_NAMES();
snprintf(errstr, errstr_sz,
"Column %s in table %s is %s"
" but should be %s (length mismatch).",
req_schema->columns[i].name,
ut_format_name(req_schema->table_name,
buf, sizeof(buf)),
actual_type, req_type);
return(DB_ERROR);
}
/*
check mtype for exact match.
This check is relaxed to allow use to use TIMESTAMP
(ie INT) for last_update instead of DATA_BINARY.
We have to test for both values as the innodb_table_stats
table may come from MySQL and have the old type.
*/
if (req_schema->columns[i].mtype != table->cols[j].mtype &&
!(req_schema->columns[i].mtype == DATA_INT &&
table->cols[j].mtype == DATA_FIXBINARY))
{
CREATE_TYPES_NAMES();
snprintf(errstr, errstr_sz,
"Column %s in table %s is %s"
" but should be %s (type mismatch).",
req_schema->columns[i].name,
ut_format_name(req_schema->table_name,
buf, sizeof(buf)),
actual_type, req_type);
return(DB_ERROR);
}
/* check whether required prtype mask is set */
if (req_schema->columns[i].prtype_mask != 0
&& (table->cols[j].prtype
& req_schema->columns[i].prtype_mask)
!= req_schema->columns[i].prtype_mask) {
CREATE_TYPES_NAMES();
snprintf(errstr, errstr_sz,
"Column %s in table %s is %s"
" but should be %s (flags mismatch).",
req_schema->columns[i].name,
ut_format_name(req_schema->table_name,
buf, sizeof(buf)),
actual_type, req_type);
return(DB_ERROR);
}
}
if (req_schema->n_foreign != table->foreign_set.size()) {
snprintf(
errstr, errstr_sz,
"Table %s has " ULINTPF " foreign key(s) pointing"
" to other tables, but it must have " ULINTPF ".",
ut_format_name(req_schema->table_name,
buf, sizeof(buf)),
static_cast<ulint>(table->foreign_set.size()),
req_schema->n_foreign);
return(DB_ERROR);
}
if (req_schema->n_referenced != table->referenced_set.size()) {
snprintf(
errstr, errstr_sz,
"There are " ULINTPF " foreign key(s) pointing to %s, "
"but there must be " ULINTPF ".",
static_cast<ulint>(table->referenced_set.size()),
ut_format_name(req_schema->table_name,
buf, sizeof(buf)),
req_schema->n_referenced);
return(DB_ERROR);
}
return(DB_SUCCESS);
}
/* @} */
/*********************************************************************//**
Converts a database and table name from filesystem encoding
(e.g. d@i1b/a@q1b@1Kc, same format as used in dict_table_t::name) in two
......
This diff is collapsed.
......@@ -479,19 +479,6 @@ dtype_new_read_for_order_and_null_size(
dtype_t* type, /*!< in: type struct */
const byte* buf); /*!< in: buffer for stored type order info */
/*********************************************************************//**
Returns the type's SQL name (e.g. BIGINT UNSIGNED) from mtype,prtype,len
@return the SQL type name */
UNIV_INLINE
char*
dtype_sql_name(
/*===========*/
unsigned mtype, /*!< in: mtype */
unsigned prtype, /*!< in: prtype */
unsigned len, /*!< in: len */
char* name, /*!< out: SQL name */
unsigned name_sz);/*!< in: size of the name buffer */
/*********************************************************************//**
Validates a data type structure.
@return TRUE if ok */
......
......@@ -327,103 +327,6 @@ dtype_new_read_for_order_and_null_size(
dtype_set_mblen(type);
}
/*********************************************************************//**
Returns the type's SQL name (e.g. BIGINT UNSIGNED) from mtype,prtype,len
@return the SQL type name */
UNIV_INLINE
char*
dtype_sql_name(
/*===========*/
unsigned mtype, /*!< in: mtype */
unsigned prtype, /*!< in: prtype */
unsigned len, /*!< in: len */
char* name, /*!< out: SQL name */
unsigned name_sz)/*!< in: size of the name buffer */
{
#define APPEND_UNSIGNED() \
do { \
if (prtype & DATA_UNSIGNED) { \
snprintf(name + strlen(name), \
name_sz - strlen(name), \
" UNSIGNED"); \
} \
} while (0)
snprintf(name, name_sz, "UNKNOWN");
switch (mtype) {
case DATA_INT:
switch (len) {
case 1:
snprintf(name, name_sz, "TINYINT");
break;
case 2:
snprintf(name, name_sz, "SMALLINT");
break;
case 3:
snprintf(name, name_sz, "MEDIUMINT");
break;
case 4:
snprintf(name, name_sz, "INT");
break;
case 8:
snprintf(name, name_sz, "BIGINT");
break;
}
APPEND_UNSIGNED();
break;
case DATA_FLOAT:
snprintf(name, name_sz, "FLOAT");
APPEND_UNSIGNED();
break;
case DATA_DOUBLE:
snprintf(name, name_sz, "DOUBLE");
APPEND_UNSIGNED();
break;
case DATA_FIXBINARY:
snprintf(name, name_sz, "BINARY(%u)", len);
break;
case DATA_CHAR:
case DATA_MYSQL:
snprintf(name, name_sz, "CHAR(%u)", len);
break;
case DATA_VARCHAR:
case DATA_VARMYSQL:
snprintf(name, name_sz, "VARCHAR(%u)", len);
break;
case DATA_BINARY:
snprintf(name, name_sz, "VARBINARY(%u)", len);
break;
case DATA_GEOMETRY:
snprintf(name, name_sz, "GEOMETRY");
break;
case DATA_BLOB:
switch (len) {
case 9:
snprintf(name, name_sz, "TINYBLOB");
break;
case 10:
snprintf(name, name_sz, "BLOB");
break;
case 11:
snprintf(name, name_sz, "MEDIUMBLOB");
break;
case 12:
snprintf(name, name_sz, "LONGBLOB");
break;
}
}
if (prtype & DATA_NOT_NULL) {
snprintf(name + strlen(name),
name_sz - strlen(name),
" NOT NULL");
}
return(name);
}
/***********************************************************************//**
Returns the size of a fixed size data type, 0 if not a fixed size type.
@return fixed size, or 0 */
......
......@@ -35,8 +35,6 @@ Created 1/8/1996 Heikki Tuuri
#include <deque>
class MDL_ticket;
extern bool innodb_table_stats_not_found;
extern bool innodb_index_stats_not_found;
/** the first table or index ID for other than hard-coded system tables */
constexpr uint8_t DICT_HDR_FIRST_ID= 10;
......@@ -1598,62 +1596,6 @@ extern dict_sys_t dict_sys;
#define dict_sys_lock() dict_sys.lock(SRW_LOCK_CALL)
#define dict_sys_unlock() dict_sys.unlock()
/* Auxiliary structs for checking a table definition @{ */
/* This struct is used to specify the name and type that a column must
have when checking a table's schema. */
struct dict_col_meta_t {
const char* name; /* column name */
ulint mtype; /* required column main type */
ulint prtype_mask; /* required column precise type mask;
if this is non-zero then all the
bits it has set must also be set
in the column's prtype */
ulint len; /* required column length */
};
/* This struct is used for checking whether a given table exists and
whether it has a predefined schema (number of columns and column names
and types) */
struct dict_table_schema_t {
const char* table_name; /* the name of the table whose
structure we are checking */
ulint n_cols; /* the number of columns the
table must have */
dict_col_meta_t* columns; /* metadata for the columns;
this array has n_cols
elements */
ulint n_foreign; /* number of foreign keys this
table has, pointing to other
tables (where this table is
FK child) */
ulint n_referenced; /* number of foreign keys other
tables have, pointing to this
table (where this table is
parent) */
};
/* @} */
/*********************************************************************//**
Checks whether a table exists and whether it has the given structure.
The table must have the same number of columns with the same names and
types. The order of the columns does not matter.
The caller must own the dictionary mutex.
dict_table_schema_check() @{
@return DB_SUCCESS if the table exists and contains the necessary columns */
dberr_t
dict_table_schema_check(
/*====================*/
dict_table_schema_t* req_schema, /*!< in/out: required table
schema */
char* errstr, /*!< out: human readable error
message if != DB_SUCCESS and
!= DB_TABLE_NOT_FOUND is
returned */
size_t errstr_sz) /*!< in: errstr size */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/* @} */
/*********************************************************************//**
Converts a database and table name from filesystem encoding
(e.g. d@i1b/a@q1b@1Kc, same format as used in dict_table_t::name) in two
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment