summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOleksandr Byelkin <sanja@mariadb.com>2020-03-19 15:02:09 +0100
committerOleksandr Byelkin <sanja@mariadb.com>2020-03-19 15:02:09 +0100
commit5514b48673ceeefc0427834df233e2e8e381f862 (patch)
tree2bd7ae7992f0ca8d364ebc11542fc7f529873673
parent5c1ed707a3d03a081fde2b2c960998d797757adf (diff)
downloadmariadb-git-bb-10.1-MDEV-20939.tar.gz
MDEV-20939: Race condition between mysqldump import and InnoDB persistent statistics calculationbb-10.1-MDEV-20939
For mysql.innodb_index_stats and mysql.innodb_table_stats by default backed up only structure. --ignore-table-data parameter added.
-rw-r--r--client/client_priv.h1
-rw-r--r--client/mysqldump.c48
-rw-r--r--mysql-test/r/mysqldump.result41
-rw-r--r--mysql-test/t/mysqldump.test60
4 files changed, 144 insertions, 6 deletions
diff --git a/client/client_priv.h b/client/client_priv.h
index 1668227cc96..34c44e41a08 100644
--- a/client/client_priv.h
+++ b/client/client_priv.h
@@ -93,6 +93,7 @@ enum options_client
OPT_REPORT_PROGRESS,
OPT_SKIP_ANNOTATE_ROWS_EVENTS,
OPT_SSL_CRL, OPT_SSL_CRLPATH,
+ OPT_IGNORE_DATA,
OPT_MAX_CLIENT_OPTION /* should be always the last */
};
diff --git a/client/mysqldump.c b/client/mysqldump.c
index 44f9849306f..8eaec07cd09 100644
--- a/client/mysqldump.c
+++ b/client/mysqldump.c
@@ -90,6 +90,7 @@
/* Max length GTID position that we will output. */
#define MAX_GTID_LENGTH 1024
+static my_bool ignore_table_data(const uchar *hash_key, size_t len);
static void add_load_option(DYNAMIC_STRING *str, const char *option,
const char *option_value);
static ulong find_set(TYPELIB *, const char *, size_t, char **, uint *);
@@ -210,7 +211,7 @@ TYPELIB compatible_mode_typelib= {array_elements(compatible_mode_names) - 1,
#define MED_ENGINES "MRG_MyISAM, MRG_ISAM, CONNECT, OQGRAPH, SPIDER, VP, FEDERATED"
-HASH ignore_table;
+HASH ignore_table, ignore_data;
static struct my_option my_long_options[] =
{
@@ -371,6 +372,12 @@ static struct my_option my_long_options[] =
&opt_hex_blob, &opt_hex_blob, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"host", 'h', "Connect to host.", &current_host,
&current_host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"ignore-table-data", OPT_IGNORE_DATA,
+ "Do not dump the specified table data. To specify more than one table "
+ "to ignore, use the directive multiple times, once for each table. "
+ "Each table must be specified with both database and table names, e.g., "
+ "--ignore-table-data=database.table.",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"ignore-table", OPT_IGNORE_TABLE,
"Do not dump the specified table. To specify more than one table to ignore, "
"use the directive multiple times, once for each table. Each table must "
@@ -895,6 +902,18 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
case (int) OPT_TABLES:
opt_databases=0;
break;
+ case (int) OPT_IGNORE_DATA:
+ {
+ if (!strchr(argument, '.'))
+ {
+ fprintf(stderr,
+ "Illegal use of option --ignore-table-data=<database>.<table>\n");
+ exit(1);
+ }
+ if (my_hash_insert(&ignore_data, (uchar*)my_strdup(argument, MYF(0))))
+ exit(EX_EOM);
+ break;
+ }
case (int) OPT_IGNORE_TABLE:
{
if (!strchr(argument, '.'))
@@ -993,6 +1012,16 @@ static int get_options(int *argc, char ***argv)
(uchar*) my_strdup("mysql.slow_log", MYF(MY_WME))))
return(EX_EOM);
+ if (my_hash_init(&ignore_data, charset_info, 16, 0, 0,
+ (my_hash_get_key) get_table_key, my_free, 0))
+ return(EX_EOM);
+ if (my_hash_insert(&ignore_data,
+ (uchar*) my_strdup("mysql.innodb_index_stats",
+ MYF(MY_WME))) ||
+ my_hash_insert(&ignore_data,
+ (uchar*) my_strdup("mysql.innodb_table_stats",
+ MYF(MY_WME))))
+ return(EX_EOM);
if ((ho_error= handle_options(argc, argv, my_long_options, get_one_option)))
return(ho_error);
@@ -1637,6 +1666,8 @@ static void free_resources()
free_root(&glob_root, MYF(0));
if (my_hash_inited(&ignore_table))
my_hash_free(&ignore_table);
+ if (my_hash_inited(&ignore_data))
+ my_hash_free(&ignore_data);
dynstr_free(&extended_row);
dynstr_free(&dynamic_where);
dynstr_free(&insert_pat);
@@ -3601,7 +3632,7 @@ static char *alloc_query_str(ulong size)
*/
-static void dump_table(char *table, char *db)
+static void dump_table(char *table, char *db, const uchar *hash_key, size_t len)
{
char ignore_flag;
char buf[200], table_buff[NAME_LEN+3];
@@ -3629,7 +3660,7 @@ static void dump_table(char *table, char *db)
DBUG_VOID_RETURN;
/* Check --no-data flag */
- if (opt_no_data)
+ if (opt_no_data || (hash_key && ignore_table_data(hash_key, len)))
{
verbose_msg("-- Skipping dump data for table '%s', --no-data was used\n",
table);
@@ -4557,10 +4588,14 @@ static int init_dumping(char *database, int init_func(char*))
/* Return 1 if we should copy the table */
-my_bool include_table(const uchar *hash_key, size_t len)
+static my_bool include_table(const uchar *hash_key, size_t len)
{
return ! my_hash_search(&ignore_table, hash_key, len);
}
+static my_bool ignore_table_data(const uchar *hash_key, size_t len)
+{
+ return my_hash_search(&ignore_data, hash_key, len) != NULL;
+}
static int dump_all_tables_in_db(char *database)
@@ -4625,7 +4660,7 @@ static int dump_all_tables_in_db(char *database)
char *end= strmov(afterdot, table);
if (include_table((uchar*) hash_key, end - hash_key))
{
- dump_table(table,database);
+ dump_table(table, database, (uchar*) hash_key, end - hash_key);
my_free(order_by);
order_by= 0;
if (opt_dump_triggers && mysql_get_server_version(mysql) >= 50009)
@@ -5014,7 +5049,7 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
for (pos= dump_tables; pos < end; pos++)
{
DBUG_PRINT("info",("Dumping table %s", *pos));
- dump_table(*pos, db);
+ dump_table(*pos, db, NULL, 0);
if (opt_dump_triggers &&
mysql_get_server_version(mysql) >= 50009)
{
@@ -5995,6 +6030,7 @@ int main(int argc, char **argv)
compatible_mode_normal_str[0]= 0;
default_charset= (char *)mysql_universal_client_charset;
bzero((char*) &ignore_table, sizeof(ignore_table));
+ bzero((char*) &ignore_data, sizeof(ignore_data));
exit_code= get_options(&argc, &argv);
if (exit_code)
diff --git a/mysql-test/r/mysqldump.result b/mysql-test/r/mysqldump.result
index 8d8e3b7eda4..4d3991404b7 100644
--- a/mysql-test/r/mysqldump.result
+++ b/mysql-test/r/mysqldump.result
@@ -5628,3 +5628,44 @@ select count(*) from t2;
count(*)
2
drop tables t2, t1;
+#
+# MDEV-20939:Race condition between mysqldump import and InnoDB
+# persistent statistics calculation
+#
+use mysql;
+# add smomething to be sure the tables are not empty
+insert into mysql.innodb_table_stats
+(database_name, table_name, n_rows, clustered_index_size,
+sum_of_other_index_sizes)
+values ("test","t1", 1, 1, 1);
+insert into mysql.innodb_index_stats
+(database_name, index_name, table_name, stat_name, stat_value, sample_size)
+values ("test", "i1", "t1", "i1", 1, 1);
+Warnings:
+Warning 1364 Field 'stat_description' doesn't have a default value
+# check that all tables we need are not empty
+select count(*) >= 1 from mysql.innodb_table_stats;
+count(*) >= 1
+1
+select count(*) >= 1 from mysql.innodb_index_stats;
+count(*) >= 1
+1
+select count(*) >= 1 from mysql.proc;
+count(*) >= 1
+1
+select count(*) >= 1 from mysql.db;
+count(*) >= 1
+1
+# for proc we have CREATE and INSERT for all other only CREATE
+FOUND /INSERT INTO `proc`/ in MDEV-20939.sql
+NOT FOUND /INSERT INTO `innodb_table_stats`/ in MDEV-20939.sql
+NOT FOUND /INSERT INTO `innodb_index_stats`/ in MDEV-20939.sql
+NOT FOUND /INSERT INTO `db`/ in MDEV-20939.sql
+FOUND /CREATE TABLE `innodb_table_stats`/ in MDEV-20939.sql
+FOUND /CREATE TABLE `innodb_index_stats`/ in MDEV-20939.sql
+FOUND /CREATE TABLE `db`/ in MDEV-20939.sql
+FOUND /CREATE TABLE `proc`/ in MDEV-20939.sql
+delete from mysql.innodb_table_stats;
+delete from mysql.innodb_index_stats;
+use test;
+# End of 10.1 tests
diff --git a/mysql-test/t/mysqldump.test b/mysql-test/t/mysqldump.test
index 7382bd455c8..22a040144ca 100644
--- a/mysql-test/t/mysqldump.test
+++ b/mysql-test/t/mysqldump.test
@@ -2678,3 +2678,63 @@ select count(*) from t2;
--remove_file $MYSQLTEST_VARDIR/tmp/t2.txt
drop tables t2, t1;
+
+
+--echo #
+--echo # MDEV-20939:Race condition between mysqldump import and InnoDB
+--echo # persistent statistics calculation
+--echo #
+
+use mysql;
+
+--echo # add smomething to be sure the tables are not empty
+
+insert into mysql.innodb_table_stats
+ (database_name, table_name, n_rows, clustered_index_size,
+ sum_of_other_index_sizes)
+ values ("test","t1", 1, 1, 1);
+
+insert into mysql.innodb_index_stats
+ (database_name, index_name, table_name, stat_name, stat_value, sample_size)
+ values ("test", "i1", "t1", "i1", 1, 1);
+
+
+--echo # check that all tables we need are not empty
+
+select count(*) >= 1 from mysql.innodb_table_stats;
+select count(*) >= 1 from mysql.innodb_index_stats;
+select count(*) >= 1 from mysql.proc;
+select count(*) >= 1 from mysql.db;
+
+
+--exec $MYSQL_DUMP mysql --ignore-table-data=mysql.db >$MYSQLTEST_VARDIR/tmp/MDEV-20939.sql
+
+
+--echo # for proc we have CREATE and INSERT for all other only CREATE
+
+let SEARCH_RANGE=500000000;
+let SEARCH_FILE=$MYSQLTEST_VARDIR/tmp/MDEV-20939.sql;
+let SEARCH_PATTERN=INSERT INTO `proc`;
+source include/search_pattern_in_file.inc;
+let SEARCH_PATTERN=INSERT INTO `innodb_table_stats`;
+source include/search_pattern_in_file.inc;
+let SEARCH_PATTERN=INSERT INTO `innodb_index_stats`;
+source include/search_pattern_in_file.inc;
+let SEARCH_PATTERN=INSERT INTO `db`;
+source include/search_pattern_in_file.inc;
+let SEARCH_PATTERN=CREATE TABLE `innodb_table_stats`;
+source include/search_pattern_in_file.inc;
+let SEARCH_PATTERN=CREATE TABLE `innodb_index_stats`;
+source include/search_pattern_in_file.inc;
+let SEARCH_PATTERN=CREATE TABLE `db`;
+source include/search_pattern_in_file.inc;
+let SEARCH_PATTERN=CREATE TABLE `proc`;
+source include/search_pattern_in_file.inc;
+
+--remove_file $MYSQLTEST_VARDIR/tmp/MDEV-20939.sql
+delete from mysql.innodb_table_stats;
+delete from mysql.innodb_index_stats;
+use test;
+
+
+--echo # End of 10.1 tests