Skip to content

Commit ae4b243

Browse files
committed
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.
1 parent c6b4212 commit ae4b243

File tree

1 file changed

+115
-24
lines changed

1 file changed

+115
-24
lines changed

client/mysqldump.c

Lines changed: 115 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,11 +1735,11 @@ static my_bool test_if_special_chars(const char *str)
17351735
} /* test_if_special_chars */
17361736

17371737

1738-
17391738
/*
17401739
quote_name(name, buff, force)
17411740
1742-
Quotes char string, taking into account compatible mode
1741+
Quotes a string, if it requires quoting. To force quoting regardless
1742+
of the characters within the string, the force flag can be set to true.
17431743
17441744
Args
17451745
@@ -1748,8 +1748,8 @@ static my_bool test_if_special_chars(const char *str)
17481748
force Flag to make it ignore 'test_if_special_chars'
17491749
17501750
Returns
1751-
1752-
buff quoted string
1751+
A pointer to the quoted string, or the original string if nothing has
1752+
changed.
17531753
17541754
*/
17551755
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)
18151815
return buff;
18161816
}
18171817

1818+
static char *quote_for_equal(const char *name, char *buff)
1819+
{
1820+
char *to= buff;
1821+
*to++= '\'';
1822+
while (*name)
1823+
{
1824+
if (*name == '\\')
1825+
{
1826+
*to++='\\';
1827+
}
1828+
if (*name == '\'')
1829+
*to++= '\\';
1830+
*to++= *name++;
1831+
}
1832+
to[0]= '\'';
1833+
to[1]= 0;
1834+
return buff;
1835+
1836+
}
1837+
18181838

18191839
/**
18201840
Quote and print a string.
@@ -3341,8 +3361,10 @@ static int dump_triggers_for_table(char *table_name, char *db_name)
33413361
/* Get list of triggers. */
33423362

33433363
my_snprintf(query_buff, sizeof(query_buff),
3344-
"SHOW TRIGGERS LIKE %s",
3345-
quote_for_like(table_name, name_buff));
3364+
"SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS "
3365+
"WHERE EVENT_OBJECT_SCHEMA = DATABASE() AND "
3366+
"EVENT_OBJECT_TABLE = %s",
3367+
quote_for_equal(table_name, name_buff));
33463368

33473369
if (mysql_query_with_error_report(mysql, &show_triggers_rs, query_buff))
33483370
goto done;
@@ -4695,29 +4717,37 @@ static my_bool dump_all_views_in_db(char *database)
46954717

46964718

46974719
/*
4698-
get_actual_table_name -- executes a SHOW TABLES LIKE '%s' to get the actual
4699-
table name from the server for the table name given on the command line.
4700-
we do this because the table name given on the command line may be a
4701-
different case (e.g. T1 vs t1)
4702-
4703-
RETURN
4704-
pointer to the table name
4705-
0 if error
4720+
See get_actual_table_name. Used to retrieve the correct table name
4721+
from the database schema.
47064722
*/
4707-
4708-
static char *get_actual_table_name(const char *old_table_name, MEM_ROOT *root)
4723+
static char *get_actual_table_name_helper(const char *old_table_name,
4724+
my_bool case_sensitive,
4725+
MEM_ROOT *root)
47094726
{
47104727
char *name= 0;
47114728
MYSQL_RES *table_res;
47124729
MYSQL_ROW row;
47134730
char query[50 + 2*NAME_LEN];
47144731
char show_name_buff[FN_REFLEN];
4715-
DBUG_ENTER("get_actual_table_name");
4732+
DBUG_ENTER("get_actual_table_name_helper");
47164733

47174734
/* Check memory for quote_for_like() */
47184735
DBUG_ASSERT(2*sizeof(old_table_name) < sizeof(show_name_buff));
4719-
my_snprintf(query, sizeof(query), "SHOW TABLES LIKE %s",
4720-
quote_for_like(old_table_name, show_name_buff));
4736+
4737+
if (case_sensitive)
4738+
{
4739+
DBUG_PRINT("info", ("case sensitive search"));
4740+
my_snprintf(query, sizeof(query),
4741+
"SELECT table_name FROM INFORMATION_SCHEMA.TABLES "
4742+
"WHERE table_schema = DATABASE() AND table_name = %s",
4743+
quote_for_equal(old_table_name, show_name_buff));
4744+
}
4745+
else
4746+
{
4747+
DBUG_PRINT("info", ("case insensitive search"));
4748+
my_snprintf(query, sizeof(query), "SHOW TABLES LIKE %s",
4749+
quote_for_like(old_table_name, show_name_buff));
4750+
}
47214751

47224752
if (mysql_query_with_error_report(mysql, 0, query))
47234753
return NullS;
@@ -4742,13 +4772,68 @@ static char *get_actual_table_name(const char *old_table_name, MEM_ROOT *root)
47424772
DBUG_RETURN(name);
47434773
}
47444774

4775+
/*
4776+
get_actual_table_name -- executes a SELECT .. FROM I_S.tables to check
4777+
if the table name given on the command line matches the one in the database.
4778+
If the table is not found, it falls back to a slower SHOW TABLES LIKE '%s' to
4779+
get the actual table name from the server.
4780+
4781+
We do this because the table name given on the command line may be a
4782+
different case (e.g. T1 vs t1), but checking this takes a long time
4783+
when there are many tables present.
4784+
4785+
RETURN
4786+
pointer to the table name
4787+
0 if error
4788+
*/
4789+
4790+
static char *get_actual_table_name(const char *old_table_name,
4791+
int lower_case_table_names,
4792+
MEM_ROOT *root)
4793+
{
4794+
char *name= 0;
4795+
DBUG_ENTER("get_actual_table_name");
4796+
4797+
name= get_actual_table_name_helper(old_table_name, TRUE, root);
4798+
if (!name && !lower_case_table_names)
4799+
name= get_actual_table_name_helper(old_table_name, FALSE, root);
4800+
DBUG_RETURN(name);
4801+
}
4802+
4803+
/*
4804+
Retrieve the value for the server system variable lower_case_table_names.
4805+
4806+
RETURN
4807+
0 case sensitive.
4808+
> 0 case insensitive
4809+
*/
4810+
static int get_sys_var_lower_case_table_names()
4811+
{
4812+
int lower_case_table_names = 0;
4813+
MYSQL_RES *table_res;
4814+
MYSQL_ROW row;
4815+
const char *show_var_query = "SHOW VARIABLES LIKE 'lower_case_table_names'";
4816+
if (mysql_query_with_error_report(mysql, &table_res, show_var_query))
4817+
return 0; /* In case of error, assume default value of 0 */
4818+
4819+
if ((row= mysql_fetch_row(table_res)))
4820+
{
4821+
lower_case_table_names= atoi(row[1]);
4822+
mysql_free_result(table_res);
4823+
}
4824+
4825+
return lower_case_table_names;
4826+
}
4827+
4828+
47454829

47464830
static int dump_selected_tables(char *db, char **table_names, int tables)
47474831
{
47484832
char table_buff[NAME_LEN*2+3];
47494833
DYNAMIC_STRING lock_tables_query;
47504834
MEM_ROOT root;
47514835
char **dump_tables, **pos, **end;
4836+
int lower_case_table_names;
47524837
DBUG_ENTER("dump_selected_tables");
47534838

47544839
if (init_dumping(db, init_dumping_tables))
@@ -4758,11 +4843,15 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
47584843
if (!(dump_tables= pos= (char**) alloc_root(&root, tables * sizeof(char *))))
47594844
die(EX_EOM, "alloc_root failure.");
47604845

4846+
/* Figure out how to compare table names. */
4847+
lower_case_table_names = get_sys_var_lower_case_table_names();
4848+
47614849
init_dynamic_string_checked(&lock_tables_query, "LOCK TABLES ", 256, 1024);
47624850
for (; tables > 0 ; tables-- , table_names++)
47634851
{
47644852
/* the table name passed on commandline may be wrong case */
4765-
if ((*pos= get_actual_table_name(*table_names, &root)))
4853+
if ((*pos= get_actual_table_name(*table_names, lower_case_table_names,
4854+
&root)))
47664855
{
47674856
/* Add found table name to lock_tables_query */
47684857
if (lock_tables)
@@ -5369,8 +5458,10 @@ char check_if_ignore_table(const char *table_name, char *table_type)
53695458

53705459
/* Check memory for quote_for_like() */
53715460
DBUG_ASSERT(2*sizeof(table_name) < sizeof(show_name_buff));
5372-
my_snprintf(buff, sizeof(buff), "show table status like %s",
5373-
quote_for_like(table_name, show_name_buff));
5461+
my_snprintf(buff, sizeof(buff),
5462+
"SELECT engine FROM INFORMATION_SCHEMA.TABLES "
5463+
"WHERE table_name = %s",
5464+
quote_for_equal(table_name, show_name_buff));
53745465
if (mysql_query_with_error_report(mysql, &res, buff))
53755466
{
53765467
if (mysql_errno(mysql) != ER_PARSE_ERROR)
@@ -5388,7 +5479,7 @@ char check_if_ignore_table(const char *table_name, char *table_type)
53885479
mysql_free_result(res);
53895480
DBUG_RETURN(result); /* assume table is ok */
53905481
}
5391-
if (!(row[1]))
5482+
if (!(row[0]))
53925483
strmake(table_type, "VIEW", NAME_LEN-1);
53935484
else
53945485
{
@@ -5398,7 +5489,7 @@ char check_if_ignore_table(const char *table_name, char *table_type)
53985489
these types, but we do want to use delayed inserts in the dump if
53995490
the table type is _NOT_ one of these types
54005491
*/
5401-
strmake(table_type, row[1], NAME_LEN-1);
5492+
strmake(table_type, row[0], NAME_LEN-1);
54025493
if (opt_delayed)
54035494
{
54045495
if (strcmp(table_type,"MyISAM") &&

0 commit comments

Comments
 (0)