MDEV-6714 mysqldump slow with tables in big databases

mysqldump now attempts to make use of the INFORMATION_SCHEMA tables.
If the table name is not found with a case sensitive search, it
fallbacks to a case insensitive search.
This commit is contained in:
Vicențiu Ciorbaru 2015-03-19 15:16:22 +02:00
parent c6b4212821
commit ae4b24340d

View file

@ -1735,11 +1735,11 @@ static my_bool test_if_special_chars(const char *str)
} /* test_if_special_chars */
/*
quote_name(name, buff, force)
Quotes char string, taking into account compatible mode
Quotes a string, if it requires quoting. To force quoting regardless
of the characters within the string, the force flag can be set to true.
Args
@ -1748,8 +1748,8 @@ static my_bool test_if_special_chars(const char *str)
force Flag to make it ignore 'test_if_special_chars'
Returns
buff quoted string
A pointer to the quoted string, or the original string if nothing has
changed.
*/
static char *quote_name(const char *name, char *buff, my_bool force)
@ -1815,6 +1815,26 @@ static char *quote_for_like(const char *name, char *buff)
return buff;
}
static char *quote_for_equal(const char *name, char *buff)
{
char *to= buff;
*to++= '\'';
while (*name)
{
if (*name == '\\')
{
*to++='\\';
}
if (*name == '\'')
*to++= '\\';
*to++= *name++;
}
to[0]= '\'';
to[1]= 0;
return buff;
}
/**
Quote and print a string.
@ -3341,8 +3361,10 @@ static int dump_triggers_for_table(char *table_name, char *db_name)
/* Get list of triggers. */
my_snprintf(query_buff, sizeof(query_buff),
"SHOW TRIGGERS LIKE %s",
quote_for_like(table_name, name_buff));
"SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS "
"WHERE EVENT_OBJECT_SCHEMA = DATABASE() AND "
"EVENT_OBJECT_TABLE = %s",
quote_for_equal(table_name, name_buff));
if (mysql_query_with_error_report(mysql, &show_triggers_rs, query_buff))
goto done;
@ -4695,29 +4717,37 @@ static my_bool dump_all_views_in_db(char *database)
/*
get_actual_table_name -- executes a SHOW TABLES LIKE '%s' to get the actual
table name from the server for the table name given on the command line.
we do this because the table name given on the command line may be a
different case (e.g. T1 vs t1)
RETURN
pointer to the table name
0 if error
See get_actual_table_name. Used to retrieve the correct table name
from the database schema.
*/
static char *get_actual_table_name(const char *old_table_name, MEM_ROOT *root)
static char *get_actual_table_name_helper(const char *old_table_name,
my_bool case_sensitive,
MEM_ROOT *root)
{
char *name= 0;
MYSQL_RES *table_res;
MYSQL_ROW row;
char query[50 + 2*NAME_LEN];
char show_name_buff[FN_REFLEN];
DBUG_ENTER("get_actual_table_name");
DBUG_ENTER("get_actual_table_name_helper");
/* Check memory for quote_for_like() */
DBUG_ASSERT(2*sizeof(old_table_name) < sizeof(show_name_buff));
my_snprintf(query, sizeof(query), "SHOW TABLES LIKE %s",
quote_for_like(old_table_name, show_name_buff));
if (case_sensitive)
{
DBUG_PRINT("info", ("case sensitive search"));
my_snprintf(query, sizeof(query),
"SELECT table_name FROM INFORMATION_SCHEMA.TABLES "
"WHERE table_schema = DATABASE() AND table_name = %s",
quote_for_equal(old_table_name, show_name_buff));
}
else
{
DBUG_PRINT("info", ("case insensitive search"));
my_snprintf(query, sizeof(query), "SHOW TABLES LIKE %s",
quote_for_like(old_table_name, show_name_buff));
}
if (mysql_query_with_error_report(mysql, 0, query))
return NullS;
@ -4742,6 +4772,60 @@ static char *get_actual_table_name(const char *old_table_name, MEM_ROOT *root)
DBUG_RETURN(name);
}
/*
get_actual_table_name -- executes a SELECT .. FROM I_S.tables to check
if the table name given on the command line matches the one in the database.
If the table is not found, it falls back to a slower SHOW TABLES LIKE '%s' to
get the actual table name from the server.
We do this because the table name given on the command line may be a
different case (e.g. T1 vs t1), but checking this takes a long time
when there are many tables present.
RETURN
pointer to the table name
0 if error
*/
static char *get_actual_table_name(const char *old_table_name,
int lower_case_table_names,
MEM_ROOT *root)
{
char *name= 0;
DBUG_ENTER("get_actual_table_name");
name= get_actual_table_name_helper(old_table_name, TRUE, root);
if (!name && !lower_case_table_names)
name= get_actual_table_name_helper(old_table_name, FALSE, root);
DBUG_RETURN(name);
}
/*
Retrieve the value for the server system variable lower_case_table_names.
RETURN
0 case sensitive.
> 0 case insensitive
*/
static int get_sys_var_lower_case_table_names()
{
int lower_case_table_names = 0;
MYSQL_RES *table_res;
MYSQL_ROW row;
const char *show_var_query = "SHOW VARIABLES LIKE 'lower_case_table_names'";
if (mysql_query_with_error_report(mysql, &table_res, show_var_query))
return 0; /* In case of error, assume default value of 0 */
if ((row= mysql_fetch_row(table_res)))
{
lower_case_table_names= atoi(row[1]);
mysql_free_result(table_res);
}
return lower_case_table_names;
}
static int dump_selected_tables(char *db, char **table_names, int tables)
{
@ -4749,6 +4833,7 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
DYNAMIC_STRING lock_tables_query;
MEM_ROOT root;
char **dump_tables, **pos, **end;
int lower_case_table_names;
DBUG_ENTER("dump_selected_tables");
if (init_dumping(db, init_dumping_tables))
@ -4758,11 +4843,15 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
if (!(dump_tables= pos= (char**) alloc_root(&root, tables * sizeof(char *))))
die(EX_EOM, "alloc_root failure.");
/* Figure out how to compare table names. */
lower_case_table_names = get_sys_var_lower_case_table_names();
init_dynamic_string_checked(&lock_tables_query, "LOCK TABLES ", 256, 1024);
for (; tables > 0 ; tables-- , table_names++)
{
/* the table name passed on commandline may be wrong case */
if ((*pos= get_actual_table_name(*table_names, &root)))
if ((*pos= get_actual_table_name(*table_names, lower_case_table_names,
&root)))
{
/* Add found table name to lock_tables_query */
if (lock_tables)
@ -5369,8 +5458,10 @@ char check_if_ignore_table(const char *table_name, char *table_type)
/* Check memory for quote_for_like() */
DBUG_ASSERT(2*sizeof(table_name) < sizeof(show_name_buff));
my_snprintf(buff, sizeof(buff), "show table status like %s",
quote_for_like(table_name, show_name_buff));
my_snprintf(buff, sizeof(buff),
"SELECT engine FROM INFORMATION_SCHEMA.TABLES "
"WHERE table_name = %s",
quote_for_equal(table_name, show_name_buff));
if (mysql_query_with_error_report(mysql, &res, buff))
{
if (mysql_errno(mysql) != ER_PARSE_ERROR)
@ -5388,7 +5479,7 @@ char check_if_ignore_table(const char *table_name, char *table_type)
mysql_free_result(res);
DBUG_RETURN(result); /* assume table is ok */
}
if (!(row[1]))
if (!(row[0]))
strmake(table_type, "VIEW", NAME_LEN-1);
else
{
@ -5398,7 +5489,7 @@ char check_if_ignore_table(const char *table_name, char *table_type)
these types, but we do want to use delayed inserts in the dump if
the table type is _NOT_ one of these types
*/
strmake(table_type, row[1], NAME_LEN-1);
strmake(table_type, row[0], NAME_LEN-1);
if (opt_delayed)
{
if (strcmp(table_type,"MyISAM") &&