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: 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.*"); call mtr.add_suppression("InnoDB: Fetch of persistent statistics requested for table");
CREATE TABLE test_ps_fetch_corrupted CREATE TABLE test_ps_fetch_corrupted
(a INT, PRIMARY KEY (a)) (a INT, PRIMARY KEY (a))
ENGINE=INNODB STATS_PERSISTENT=1; ENGINE=INNODB STATS_PERSISTENT=1;
...@@ -17,7 +17,21 @@ FROM mysql.innodb_table_stats WHERE table_name = 'test_ps_fetch_corrupted'; ...@@ -17,7 +17,21 @@ FROM mysql.innodb_table_stats WHERE table_name = 'test_ps_fetch_corrupted';
n_rows 0 n_rows 0
clustered_index_size 1 clustered_index_size 1
sum_of_other_index_sizes 0 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; FLUSH TABLE test_ps_fetch_corrupted;
SELECT seq_in_index, column_name, cardinality SELECT seq_in_index, column_name, cardinality
FROM information_schema.statistics WHERE table_name = 'test_ps_fetch_corrupted' FROM information_schema.statistics WHERE table_name = 'test_ps_fetch_corrupted'
...@@ -31,6 +45,5 @@ table_rows 0 ...@@ -31,6 +45,5 @@ table_rows 0
avg_row_length 0 avg_row_length 0
max_data_length 0 max_data_length 0
index_length 0 index_length 0
ALTER TABLE mysql.innodb_index_stats_ RENAME TO mysql.innodb_index_stats;
DROP TABLE test_ps_fetch_corrupted; DROP TABLE test_ps_fetch_corrupted;
# restart # restart
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
# server restart # server restart
-- source include/not_embedded.inc -- source include/not_embedded.inc
call mtr.add_suppression("InnoDB: Table `mysql`.`innodb_index_stats` not found"); 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.*"); call mtr.add_suppression("InnoDB: Fetch of persistent statistics requested for table");
-- vertical_results -- vertical_results
...@@ -27,7 +27,7 @@ SELECT n_rows, clustered_index_size, sum_of_other_index_sizes ...@@ -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'; FROM mysql.innodb_table_stats WHERE table_name = 'test_ps_fetch_corrupted';
# corrupt the persistent storage # 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 # reopen the table, this will attept to read from the persistent storage
FLUSH TABLE test_ps_fetch_corrupted; FLUSH TABLE test_ps_fetch_corrupted;
...@@ -42,7 +42,15 @@ SELECT table_rows, avg_row_length, max_data_length, index_length ...@@ -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'; FROM information_schema.tables WHERE table_name = 'test_ps_fetch_corrupted';
# restore the persistent storage # 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; DROP TABLE test_ps_fetch_corrupted;
......
...@@ -104,11 +104,6 @@ ulong zip_pad_max = 50; ...@@ -104,11 +104,6 @@ ulong zip_pad_max = 50;
/** Identifies generated InnoDB foreign key names */ /** Identifies generated InnoDB foreign key names */
static char dict_ibfk[] = "_ibfk_"; 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 Tries to find column names for the index and sets the col field of the
index. index.
...@@ -4600,220 +4595,6 @@ dict_table_check_for_dup_indexes( ...@@ -4600,220 +4595,6 @@ dict_table_check_for_dup_indexes(
} }
#endif /* UNIV_DEBUG */ #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 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 (e.g. d@i1b/a@q1b@1Kc, same format as used in dict_table_t::name) in two
......
...@@ -25,6 +25,7 @@ Created Jan 06, 2010 Vasil Dimov ...@@ -25,6 +25,7 @@ Created Jan 06, 2010 Vasil Dimov
*******************************************************/ *******************************************************/
#include "dict0stats.h" #include "dict0stats.h"
#include "dict0priv.h"
#include "ut0ut.h" #include "ut0ut.h"
#include "ut0rnd.h" #include "ut0rnd.h"
#include "dyn0buf.h" #include "dyn0buf.h"
...@@ -32,6 +33,7 @@ Created Jan 06, 2010 Vasil Dimov ...@@ -32,6 +33,7 @@ Created Jan 06, 2010 Vasil Dimov
#include "trx0trx.h" #include "trx0trx.h"
#include "pars0pars.h" #include "pars0pars.h"
#include <mysql_com.h> #include <mysql_com.h>
#include "log.h"
#include "btr0btr.h" #include "btr0btr.h"
#include <algorithm> #include <algorithm>
...@@ -159,6 +161,307 @@ dict_stats_should_ignore_index( ...@@ -159,6 +161,307 @@ dict_stats_should_ignore_index(
|| !index->is_committed()); || !index->is_committed());
} }
/** expected column definition */
struct dict_col_meta_t
{
/** column name */
const char *name;
/** main type */
unsigned mtype;
/** prtype mask; all these bits have to be set in prtype */
unsigned prtype_mask;
/** column length in bytes */
unsigned len;
};
/** For checking whether a table exists and has a predefined schema */
struct dict_table_schema_t
{
/** table name */
const char *table_name;
/** table name in SQL */
const char *table_name_sql;
/** number of columns */
unsigned n_cols;
/** columns */
const dict_col_meta_t columns[8];
};
static const dict_table_schema_t table_stats_schema =
{
TABLE_STATS_NAME, TABLE_STATS_NAME_PRINT, 6,
{
{"database_name", DATA_VARMYSQL, DATA_NOT_NULL, 192},
{"table_name", DATA_VARMYSQL, DATA_NOT_NULL, 597},
{"last_update", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, 4},
{"n_rows", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, 8},
{"clustered_index_size", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, 8},
{"sum_of_other_index_sizes", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, 8},
}
};
static const dict_table_schema_t index_stats_schema =
{
INDEX_STATS_NAME, INDEX_STATS_NAME_PRINT, 8,
{
{"database_name", DATA_VARMYSQL, DATA_NOT_NULL, 192},
{"table_name", DATA_VARMYSQL, DATA_NOT_NULL, 597},
{"index_name", DATA_VARMYSQL, DATA_NOT_NULL, 192},
{"last_update", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, 4},
{"stat_name", DATA_VARMYSQL, DATA_NOT_NULL, 64*3},
{"stat_value", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, 8},
{"sample_size", DATA_INT, DATA_UNSIGNED, 8},
{"stat_description", DATA_VARMYSQL, DATA_NOT_NULL, 1024*3}
}
};
/** Construct the type's SQL name (e.g. BIGINT UNSIGNED)
@param mtype InnoDB main type
@param prtype InnoDB precise type
@param len length of the column
@param name the SQL name
@param name_sz size of the name buffer
@return number of bytes written (excluding the terminating NUL byte) */
static int dtype_sql_name(unsigned mtype, unsigned prtype, unsigned len,
char *name, size_t name_sz)
{
const char *Unsigned= "";
const char *Main= "UNKNOWN";
switch (mtype) {
case DATA_INT:
switch (len) {
case 1:
Main= "TINYINT";
break;
case 2:
Main= "SMALLINT";
break;
case 3:
Main= "MEDIUMINT";
break;
case 4:
Main= "INT";
break;
case 8:
Main= "BIGINT";
break;
}
append_unsigned:
if (prtype & DATA_UNSIGNED)
Unsigned= " UNSIGNED";
len= 0;
break;
case DATA_FLOAT:
Main= "FLOAT";
goto append_unsigned;
case DATA_DOUBLE:
Main= "DOUBLE";
goto append_unsigned;
case DATA_FIXBINARY:
Main= "BINARY";
break;
case DATA_CHAR:
case DATA_MYSQL:
Main= "CHAR";
break;
case DATA_VARCHAR:
case DATA_VARMYSQL:
Main= "VARCHAR";
break;
case DATA_BINARY:
Main= "VARBINARY";
break;
case DATA_GEOMETRY:
Main= "GEOMETRY";
len= 0;
break;
case DATA_BLOB:
switch (len) {
case 9:
Main= "TINYBLOB";
break;
case 10:
Main= "BLOB";
break;
case 11:
Main= "MEDIUMBLOB";
break;
case 12:
Main= "LONGBLOB";
break;
}
len= 0;
}
const char* Not_null= (prtype & DATA_NOT_NULL) ? " NOT NULL" : "";
if (len)
return snprintf(name, name_sz, "%s(%u)%s%s", Main, len, Unsigned,
Not_null);
else
return snprintf(name, name_sz, "%s%s%s", Main, Unsigned, Not_null);
}
static bool innodb_table_stats_not_found;
static bool innodb_index_stats_not_found;
static bool innodb_table_stats_not_found_reported;
static bool innodb_index_stats_not_found_reported;
/*********************************************************************//**
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 */
static
dberr_t
dict_table_schema_check(
/*====================*/
const dict_table_schema_t* req_schema, /*!< in: required table
schema */
char* errstr, /*!< out: human readable error
message if != DB_SUCCESS is
returned */
size_t errstr_sz) /*!< in: errstr size */
{
const dict_table_t* table = dict_table_get_low(req_schema->table_name);
if (!table) {
if (req_schema == &table_stats_schema) {
if (innodb_table_stats_not_found_reported) {
return DB_STATS_DO_NOT_EXIST;
}
innodb_table_stats_not_found = true;
innodb_table_stats_not_found_reported = true;
} else {
ut_ad(req_schema == &index_stats_schema);
if (innodb_index_stats_not_found_reported) {
return DB_STATS_DO_NOT_EXIST;
}
innodb_index_stats_not_found = true;
innodb_index_stats_not_found_reported = true;
}
snprintf(errstr, errstr_sz, "Table %s not found.",
req_schema->table_name_sql);
return DB_TABLE_NOT_FOUND;
}
if (!table->is_readable() && !table->space) {
/* missing tablespace */
snprintf(errstr, errstr_sz,
"Tablespace for table %s is missing.",
req_schema->table_name_sql);
return DB_TABLE_NOT_FOUND;
}
if (unsigned(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 %u.",
req_schema->table_name_sql,
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 (unsigned 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,
req_schema->table_name_sql);
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) {
ut_ad(table->cols[j].len < req_schema->columns[i].len);
sql_print_warning("InnoDB: Table %s has"
" length mismatch in the"
" column name %s."
" Please run mariadb-upgrade",
req_schema->table_name_sql,
req_schema->columns[i].name);
}
/*
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)) {
} else if ((~table->cols[j].prtype
& req_schema->columns[i].prtype_mask)) {
} else {
continue;
}
int s = snprintf(errstr, errstr_sz,
"Column %s in table %s is ",
req_schema->columns[i].name,
req_schema->table_name_sql);
if (s < 0 || static_cast<size_t>(s) >= errstr_sz) {
return DB_ERROR;
}
errstr += s;
errstr_sz -= s;
s = dtype_sql_name(table->cols[j].mtype, table->cols[j].prtype,
table->cols[j].len, errstr, errstr_sz);
if (s < 0 || static_cast<size_t>(s) + sizeof " but should be "
>= errstr_sz) {
return DB_ERROR;
}
errstr += s;
memcpy(errstr, " but should be ", sizeof " but should be ");
errstr += (sizeof " but should be ") - 1;
errstr_sz -= s + (sizeof " but should be ") - 1;
s = dtype_sql_name(req_schema->columns[i].mtype,
req_schema->columns[i].prtype_mask,
req_schema->columns[i].len,
errstr, errstr_sz);
return DB_ERROR;
}
if (size_t n_foreign = table->foreign_set.size()) {
snprintf(errstr, errstr_sz,
"Table %s has %zu foreign key(s) pointing"
" to other tables, but it must have 0.",
req_schema->table_name_sql, n_foreign);
return DB_ERROR;
}
if (size_t n_referenced = table->referenced_set.size()) {
snprintf(errstr, errstr_sz,
"There are %zu foreign key(s) pointing to %s, "
"but there must be 0.", n_referenced,
req_schema->table_name_sql);
return DB_ERROR;
}
return DB_SUCCESS;
}
/*********************************************************************//** /*********************************************************************//**
Checks whether the persistent statistics storage exists and that all Checks whether the persistent statistics storage exists and that all
tables have the proper structure. tables have the proper structure.
...@@ -170,68 +473,6 @@ dict_stats_persistent_storage_check( ...@@ -170,68 +473,6 @@ dict_stats_persistent_storage_check(
bool caller_has_dict_sys_mutex) /*!< in: true if the caller bool caller_has_dict_sys_mutex) /*!< in: true if the caller
owns dict_sys.mutex */ owns dict_sys.mutex */
{ {
/* definition for the table TABLE_STATS_NAME */
dict_col_meta_t table_stats_columns[] = {
{"database_name", DATA_VARMYSQL,
DATA_NOT_NULL, 192},
{"table_name", DATA_VARMYSQL,
DATA_NOT_NULL, 597},
{"last_update", DATA_INT,
DATA_NOT_NULL | DATA_UNSIGNED, 4},
{"n_rows", DATA_INT,
DATA_NOT_NULL | DATA_UNSIGNED, 8},
{"clustered_index_size", DATA_INT,
DATA_NOT_NULL | DATA_UNSIGNED, 8},
{"sum_of_other_index_sizes", DATA_INT,
DATA_NOT_NULL | DATA_UNSIGNED, 8}
};
dict_table_schema_t table_stats_schema = {
TABLE_STATS_NAME,
UT_ARR_SIZE(table_stats_columns),
table_stats_columns,
0 /* n_foreign */,
0 /* n_referenced */
};
/* definition for the table INDEX_STATS_NAME */
dict_col_meta_t index_stats_columns[] = {
{"database_name", DATA_VARMYSQL,
DATA_NOT_NULL, 192},
{"table_name", DATA_VARMYSQL,
DATA_NOT_NULL, 597},
{"index_name", DATA_VARMYSQL,
DATA_NOT_NULL, 192},
{"last_update", DATA_INT,
DATA_NOT_NULL | DATA_UNSIGNED, 4},
{"stat_name", DATA_VARMYSQL,
DATA_NOT_NULL, 64*3},
{"stat_value", DATA_INT,
DATA_NOT_NULL | DATA_UNSIGNED, 8},
{"sample_size", DATA_INT,
DATA_UNSIGNED, 8},
{"stat_description", DATA_VARMYSQL,
DATA_NOT_NULL, 1024*3}
};
dict_table_schema_t index_stats_schema = {
INDEX_STATS_NAME,
UT_ARR_SIZE(index_stats_columns),
index_stats_columns,
0 /* n_foreign */,
0 /* n_referenced */
};
char errstr[512]; char errstr[512];
dberr_t ret; dberr_t ret;
...@@ -3925,129 +4166,6 @@ dict_stats_rename_index( ...@@ -3925,129 +4166,6 @@ dict_stats_rename_index(
/* tests @{ */ /* tests @{ */
#ifdef UNIV_ENABLE_UNIT_TEST_DICT_STATS #ifdef UNIV_ENABLE_UNIT_TEST_DICT_STATS
/* The following unit tests test some of the functions in this file
individually, such testing cannot be performed by the mysql-test framework
via SQL. */
/* test_dict_table_schema_check() @{ */
void
test_dict_table_schema_check()
{
/*
CREATE TABLE tcheck (
c01 VARCHAR(123),
c02 INT,
c03 INT NOT NULL,
c04 INT UNSIGNED,
c05 BIGINT,
c06 BIGINT UNSIGNED NOT NULL,
c07 TIMESTAMP
) ENGINE=INNODB;
*/
/* definition for the table 'test/tcheck' */
dict_col_meta_t columns[] = {
{"c01", DATA_VARCHAR, 0, 123},
{"c02", DATA_INT, 0, 4},
{"c03", DATA_INT, DATA_NOT_NULL, 4},
{"c04", DATA_INT, DATA_UNSIGNED, 4},
{"c05", DATA_INT, 0, 8},
{"c06", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, 8},
{"c07", DATA_INT, 0, 4},
{"c_extra", DATA_INT, 0, 4}
};
dict_table_schema_t schema = {
"test/tcheck",
0 /* will be set individually for each test below */,
columns
};
char errstr[512];
snprintf(errstr, sizeof(errstr), "Table not found");
/* prevent any data dictionary modifications while we are checking
the tables' structure */
dict_sys.mutex_lock();
/* check that a valid table is reported as valid */
schema.n_cols = 7;
if (dict_table_schema_check(&schema, errstr, sizeof(errstr))
== DB_SUCCESS) {
printf("OK: test.tcheck ok\n");
} else {
printf("ERROR: %s\n", errstr);
printf("ERROR: test.tcheck not present or corrupted\n");
goto test_dict_table_schema_check_end;
}
/* check columns with wrong length */
schema.columns[1].len = 8;
if (dict_table_schema_check(&schema, errstr, sizeof(errstr))
!= DB_SUCCESS) {
printf("OK: test.tcheck.c02 has different length and is"
" reported as corrupted\n");
} else {
printf("OK: test.tcheck.c02 has different length but is"
" reported as ok\n");
goto test_dict_table_schema_check_end;
}
schema.columns[1].len = 4;
/* request that c02 is NOT NULL while actually it does not have
this flag set */
schema.columns[1].prtype_mask |= DATA_NOT_NULL;
if (dict_table_schema_check(&schema, errstr, sizeof(errstr))
!= DB_SUCCESS) {
printf("OK: test.tcheck.c02 does not have NOT NULL while"
" it should and is reported as corrupted\n");
} else {
printf("ERROR: test.tcheck.c02 does not have NOT NULL while"
" it should and is not reported as corrupted\n");
goto test_dict_table_schema_check_end;
}
schema.columns[1].prtype_mask &= ~DATA_NOT_NULL;
/* check a table that contains some extra columns */
schema.n_cols = 6;
if (dict_table_schema_check(&schema, errstr, sizeof(errstr))
== DB_SUCCESS) {
printf("ERROR: test.tcheck has more columns but is not"
" reported as corrupted\n");
goto test_dict_table_schema_check_end;
} else {
printf("OK: test.tcheck has more columns and is"
" reported as corrupted\n");
}
/* check a table that has some columns missing */
schema.n_cols = 8;
if (dict_table_schema_check(&schema, errstr, sizeof(errstr))
!= DB_SUCCESS) {
printf("OK: test.tcheck has missing columns and is"
" reported as corrupted\n");
} else {
printf("ERROR: test.tcheck has missing columns but is"
" reported as ok\n");
goto test_dict_table_schema_check_end;
}
/* check non-existent table */
schema.table_name = "test/tcheck_nonexistent";
if (dict_table_schema_check(&schema, errstr, sizeof(errstr))
!= DB_SUCCESS) {
printf("OK: test.tcheck_nonexistent is not present\n");
} else {
printf("ERROR: test.tcheck_nonexistent is present!?\n");
goto test_dict_table_schema_check_end;
}
test_dict_table_schema_check_end:
dict_sys.mutex_unlock();
}
/* @} */
/* save/fetch aux macros @{ */ /* save/fetch aux macros @{ */
#define TEST_DATABASE_NAME "foobardb" #define TEST_DATABASE_NAME "foobardb"
#define TEST_TABLE_NAME "test_dict_stats" #define TEST_TABLE_NAME "test_dict_stats"
......
...@@ -479,19 +479,6 @@ dtype_new_read_for_order_and_null_size( ...@@ -479,19 +479,6 @@ dtype_new_read_for_order_and_null_size(
dtype_t* type, /*!< in: type struct */ dtype_t* type, /*!< in: type struct */
const byte* buf); /*!< in: buffer for stored type order info */ 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. Validates a data type structure.
@return TRUE if ok */ @return TRUE if ok */
......
...@@ -327,103 +327,6 @@ dtype_new_read_for_order_and_null_size( ...@@ -327,103 +327,6 @@ dtype_new_read_for_order_and_null_size(
dtype_set_mblen(type); 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. Returns the size of a fixed size data type, 0 if not a fixed size type.
@return fixed size, or 0 */ @return fixed size, or 0 */
......
...@@ -35,8 +35,6 @@ Created 1/8/1996 Heikki Tuuri ...@@ -35,8 +35,6 @@ Created 1/8/1996 Heikki Tuuri
#include <deque> #include <deque>
class MDL_ticket; 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 */ /** the first table or index ID for other than hard-coded system tables */
constexpr uint8_t DICT_HDR_FIRST_ID= 10; constexpr uint8_t DICT_HDR_FIRST_ID= 10;
...@@ -1598,62 +1596,6 @@ extern dict_sys_t dict_sys; ...@@ -1598,62 +1596,6 @@ extern dict_sys_t dict_sys;
#define dict_sys_lock() dict_sys.lock(SRW_LOCK_CALL) #define dict_sys_lock() dict_sys.lock(SRW_LOCK_CALL)
#define dict_sys_unlock() dict_sys.unlock() #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 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 (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