Skip to content

Commit 3477629

Browse files
author
Ramil Kalimullin
committed
Fix for
bug#33094: Error in upgrading from 5.0 to 5.1 when table contains triggers and #41385: Crash when attempting to repair a #mysql50# upgraded table with triggers. Problem: 1. trigger code didn't assume a table name may have a "#mysql50#" prefix, that may lead to a failing ASSERT(). 2. "ALTER DATABASE ... UPGRADE DATA DIRECTORY NAME" failed for databases with "#mysql50#" prefix if any trigger. 3. mysqlcheck --fix-table-name didn't use UTF8 as a default character set that resulted in (parsing) errors for tables with non-latin symbols in their names and definitions of triggers. Fix: 1. properly handle table/database names with "#mysql50#" prefix. 2. handle --default-character-set mysqlcheck option; if mysqlcheck is launched with --fix-table-name or --fix-db-name set default character set to UTF8 if no --default-character-set option given. Note: if given --fix-table-name or --fix-db-name option, without --default-character-set mysqlcheck option default character set is UTF8.
1 parent 2c1cf1a commit 3477629

File tree

7 files changed

+241
-40
lines changed

7 files changed

+241
-40
lines changed

client/mysqlcheck.c

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,13 @@ static uint verbose = 0, opt_mysql_port=0;
4040
static int my_end_arg;
4141
static char * opt_mysql_unix_port = 0;
4242
static char *opt_password = 0, *current_user = 0,
43-
*default_charset = (char *)MYSQL_DEFAULT_CHARSET_NAME,
44-
*current_host = 0;
43+
*default_charset= 0, *current_host= 0;
4544
static int first_error = 0;
4645
DYNAMIC_ARRAY tables4repair;
4746
#ifdef HAVE_SMEM
4847
static char *shared_memory_base_name=0;
4948
#endif
5049
static uint opt_protocol=0;
51-
static CHARSET_INFO *charset_info= &my_charset_latin1;
5250

5351
enum operations { DO_CHECK, DO_REPAIR, DO_ANALYZE, DO_OPTIMIZE, DO_UPGRADE };
5452

@@ -282,12 +280,10 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
282280
break;
283281
case OPT_FIX_DB_NAMES:
284282
what_to_do= DO_UPGRADE;
285-
default_charset= (char*) "utf8";
286283
opt_databases= 1;
287284
break;
288285
case OPT_FIX_TABLE_NAMES:
289286
what_to_do= DO_UPGRADE;
290-
default_charset= (char*) "utf8";
291287
break;
292288
case 'p':
293289
if (argument)
@@ -367,11 +363,20 @@ static int get_options(int *argc, char ***argv)
367363
what_to_do = DO_CHECK;
368364
}
369365

370-
/* TODO: This variable is not yet used */
371-
if (strcmp(default_charset, charset_info->csname) &&
372-
!(charset_info= get_charset_by_csname(default_charset,
373-
MY_CS_PRIMARY, MYF(MY_WME))))
374-
exit(1);
366+
/*
367+
If there's no --default-character-set option given with
368+
--fix-table-name or --fix-db-name set the default character set to "utf8".
369+
*/
370+
if (!default_charset && (opt_fix_db_names || opt_fix_table_names))
371+
{
372+
default_charset= (char*) "utf8";
373+
}
374+
if (default_charset && !get_charset_by_csname(default_charset, MY_CS_PRIMARY,
375+
MYF(MY_WME)))
376+
{
377+
printf("Unsupported character set: %s\n", default_charset);
378+
return 1;
379+
}
375380
if (*argc > 0 && opt_alldbs)
376381
{
377382
printf("You should give only options, no arguments at all, with option\n");
@@ -779,6 +784,8 @@ static int dbConnect(char *host, char *user, char *passwd)
779784
if (shared_memory_base_name)
780785
mysql_options(&mysql_connection,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name);
781786
#endif
787+
if (default_charset)
788+
mysql_options(&mysql_connection, MYSQL_SET_CHARSET_NAME, default_charset);
782789
if (!(sock = mysql_real_connect(&mysql_connection, host, user, passwd,
783790
NULL, opt_mysql_port, opt_mysql_unix_port, 0)))
784791
{

mysql-test/r/mysqlcheck.result

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,58 @@ v1
130130
v-1
131131
drop view v1, `v-1`;
132132
drop table t1;
133+
SET NAMES utf8;
134+
CREATE TABLE `#mysql50#@` (a INT);
135+
SHOW TABLES;
136+
Tables_in_test
137+
#mysql50#@
138+
SET NAMES DEFAULT;
139+
mysqlcheck --fix-table-names --databases test
140+
SET NAMES utf8;
141+
SHOW TABLES;
142+
Tables_in_test
143+
@
144+
DROP TABLE `@`;
145+
CREATE TABLE `я` (a INT);
146+
SET NAMES DEFAULT;
147+
mysqlcheck --default-character-set="latin1" --databases test
148+
test.?
149+
Error : Table 'test.?' doesn't exist
150+
error : Corrupt
151+
mysqlcheck --default-character-set="utf8" --databases test
152+
test.я OK
153+
SET NAMES utf8;
154+
DROP TABLE `я`;
155+
SET NAMES DEFAULT;
156+
CREATE DATABASE `#mysql50#a@b`;
157+
USE `#mysql50#a@b`;
158+
CREATE TABLE `#mysql50#c@d` (a INT);
159+
CREATE TABLE t1 (a INT);
160+
SELECT * FROM INFORMATION_SCHEMA.TRIGGERS
161+
WHERE TRIGGER_SCHEMA="#mysql50#a@b" ORDER BY trigger_name;
162+
TRIGGER_CATALOG TRIGGER_SCHEMA TRIGGER_NAME EVENT_MANIPULATION EVENT_OBJECT_CATALOG EVENT_OBJECT_SCHEMA EVENT_OBJECT_TABLE ACTION_ORDER ACTION_CONDITION ACTION_STATEMENT ACTION_ORIENTATION ACTION_TIMING ACTION_REFERENCE_OLD_TABLE ACTION_REFERENCE_NEW_TABLE ACTION_REFERENCE_OLD_ROW ACTION_REFERENCE_NEW_ROW CREATED SQL_MODE DEFINER CHARACTER_SET_CLIENT COLLATION_CONNECTION DATABASE_COLLATION
163+
NULL #mysql50#a@b tr1 INSERT NULL #mysql50#a@b #mysql50#c@d 0 NULL SET NEW.a = 10 * NEW.a ROW BEFORE NULL NULL OLD NEW NULL root@localhost latin1 latin1_swedish_ci latin1_swedish_ci
164+
NULL #mysql50#a@b tr2 INSERT NULL #mysql50#a@b t1 0 NULL SET NEW.a = 100 * NEW.a ROW BEFORE NULL NULL OLD NEW NULL root@localhost latin1 latin1_swedish_ci latin1_swedish_ci
165+
Warnings:
166+
Warning 1603 Triggers for table `#mysql50#a@b`.`#mysql50#c@d` have no creation context
167+
Warning 1603 Triggers for table `#mysql50#a@b`.`t1` have no creation context
168+
mysqlcheck --fix-db-names --fix-table-names --all-databases
169+
USE `a@b`;
170+
SELECT * FROM INFORMATION_SCHEMA.TRIGGERS
171+
WHERE TRIGGER_SCHEMA="a@b" ORDER BY trigger_name;
172+
TRIGGER_CATALOG TRIGGER_SCHEMA TRIGGER_NAME EVENT_MANIPULATION EVENT_OBJECT_CATALOG EVENT_OBJECT_SCHEMA EVENT_OBJECT_TABLE ACTION_ORDER ACTION_CONDITION ACTION_STATEMENT ACTION_ORIENTATION ACTION_TIMING ACTION_REFERENCE_OLD_TABLE ACTION_REFERENCE_NEW_TABLE ACTION_REFERENCE_OLD_ROW ACTION_REFERENCE_NEW_ROW CREATED SQL_MODE DEFINER CHARACTER_SET_CLIENT COLLATION_CONNECTION DATABASE_COLLATION
173+
NULL a@b tr1 INSERT NULL a@b c@d 0 NULL SET NEW.a = 10 * NEW.a ROW BEFORE NULL NULL OLD NEW NULL root@localhost utf8 utf8_general_ci latin1_swedish_ci
174+
NULL a@b tr2 INSERT NULL a@b t1 0 NULL SET NEW.a = 100 * NEW.a ROW BEFORE NULL NULL OLD NEW NULL root@localhost utf8 utf8_general_ci latin1_swedish_ci
175+
INSERT INTO `c@d` VALUES (2), (1);
176+
SELECT * FROM `c@d`;
177+
a
178+
20
179+
10
180+
INSERT INTO t1 VALUES (3), (5);
181+
SELECT * FROM t1;
182+
a
183+
300
184+
500
185+
DROP DATABASE `a@b`;
186+
USE test;
187+
End of 5.1 tests

mysql-test/t/mysqlcheck.test

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,76 @@ show tables;
112112
show tables;
113113
drop view v1, `v-1`;
114114
drop table t1;
115+
116+
#
117+
# Bug #33094: Error in upgrading from 5.0 to 5.1 when table contains triggers
118+
# Bug #41385: Crash when attempting to repair a #mysql50# upgraded table with
119+
# triggers
120+
#
121+
SET NAMES utf8;
122+
CREATE TABLE `#mysql50#@` (a INT);
123+
SHOW TABLES;
124+
SET NAMES DEFAULT;
125+
--echo mysqlcheck --fix-table-names --databases test
126+
--exec $MYSQL_CHECK --fix-table-names --databases test
127+
SET NAMES utf8;
128+
SHOW TABLES;
129+
DROP TABLE `@`;
130+
131+
CREATE TABLE `я` (a INT);
132+
SET NAMES DEFAULT;
133+
--echo mysqlcheck --default-character-set="latin1" --databases test
134+
--exec $MYSQL_CHECK --default-character-set="latin1" --databases test
135+
--echo mysqlcheck --default-character-set="utf8" --databases test
136+
--exec $MYSQL_CHECK --default-character-set="utf8" --databases test
137+
SET NAMES utf8;
138+
DROP TABLE `я`;
139+
SET NAMES DEFAULT;
140+
141+
CREATE DATABASE `#mysql50#a@b`;
142+
USE `#mysql50#a@b`;
143+
CREATE TABLE `#mysql50#c@d` (a INT);
144+
CREATE TABLE t1 (a INT);
145+
146+
# Create 5.0 like triggers
147+
--write_file $MYSQLTEST_VARDIR/master-data/a@b/c@d.TRG
148+
TYPE=TRIGGERS
149+
triggers='CREATE DEFINER=`root`@`localhost` TRIGGER tr1 BEFORE INSERT ON `c@d` FOR EACH ROW SET NEW.a = 10 * NEW.a'
150+
sql_modes=0
151+
definers='root@localhost'
152+
EOF
153+
--write_file $MYSQLTEST_VARDIR/master-data/a@b/tr1.TRN
154+
TYPE=TRIGGERNAME
155+
trigger_table=c@d
156+
EOF
157+
--write_file $MYSQLTEST_VARDIR/master-data/a@b/t1.TRG
158+
TYPE=TRIGGERS
159+
triggers='CREATE DEFINER=`root`@`localhost` TRIGGER tr2 BEFORE INSERT ON `a@b`.t1 FOR EACH ROW SET NEW.a = 100 * NEW.a'
160+
sql_modes=0
161+
definers='root@localhost'
162+
EOF
163+
--write_file $MYSQLTEST_VARDIR/master-data/a@b/tr2.TRN
164+
TYPE=TRIGGERNAME
165+
trigger_table=t1
166+
EOF
167+
168+
SELECT * FROM INFORMATION_SCHEMA.TRIGGERS
169+
WHERE TRIGGER_SCHEMA="#mysql50#a@b" ORDER BY trigger_name;
170+
171+
--echo mysqlcheck --fix-db-names --fix-table-names --all-databases
172+
--exec $MYSQL_CHECK --fix-db-names --fix-table-names --all-databases
173+
174+
USE `a@b`;
175+
SELECT * FROM INFORMATION_SCHEMA.TRIGGERS
176+
WHERE TRIGGER_SCHEMA="a@b" ORDER BY trigger_name;
177+
178+
INSERT INTO `c@d` VALUES (2), (1);
179+
SELECT * FROM `c@d`;
180+
INSERT INTO t1 VALUES (3), (5);
181+
SELECT * FROM t1;
182+
183+
DROP DATABASE `a@b`;
184+
185+
USE test;
186+
187+
--echo End of 5.1 tests

sql/mysql_priv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2230,6 +2230,7 @@ uint strconvert(CHARSET_INFO *from_cs, const char *from,
22302230
CHARSET_INFO *to_cs, char *to, uint to_length, uint *errors);
22312231
uint filename_to_tablename(const char *from, char *to, uint to_length);
22322232
uint tablename_to_filename(const char *from, char *to, uint to_length);
2233+
uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length);
22332234
#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
22342235
#ifdef MYSQL_SERVER
22352236
uint build_table_filename(char *buff, size_t bufflen, const char *db,

sql/sql_table.cc

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,30 @@ uint filename_to_tablename(const char *from, char *to, uint to_length)
114114
}
115115

116116

117+
/**
118+
Check if given string begins with "#mysql50#" prefix, cut it if so.
119+
120+
@param from string to check and cut
121+
@param to[out] buffer for result string
122+
@param to_length its size
123+
124+
@retval
125+
0 no prefix found
126+
@retval
127+
non-0 result string length
128+
*/
129+
130+
uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length)
131+
{
132+
if (from[0] == '#' &&
133+
!strncmp(from, MYSQL50_TABLE_NAME_PREFIX,
134+
MYSQL50_TABLE_NAME_PREFIX_LENGTH))
135+
return (uint) (strmake(to, from + MYSQL50_TABLE_NAME_PREFIX_LENGTH,
136+
to_length - 1) - to);
137+
return 0;
138+
}
139+
140+
117141
/*
118142
Translate a table name to a file name (WL #1324).
119143
@@ -133,11 +157,8 @@ uint tablename_to_filename(const char *from, char *to, uint to_length)
133157
DBUG_ENTER("tablename_to_filename");
134158
DBUG_PRINT("enter", ("from '%s'", from));
135159

136-
if (from[0] == '#' && !strncmp(from, MYSQL50_TABLE_NAME_PREFIX,
137-
MYSQL50_TABLE_NAME_PREFIX_LENGTH))
138-
DBUG_RETURN((uint) (strmake(to, from+MYSQL50_TABLE_NAME_PREFIX_LENGTH,
139-
to_length-1) -
140-
(from + MYSQL50_TABLE_NAME_PREFIX_LENGTH)));
160+
if ((length= check_n_cut_mysql50_prefix(from, to, to_length)))
161+
DBUG_RETURN(length);
141162
length= strconvert(system_charset_info, from,
142163
&my_charset_filename, to, to_length, &errors);
143164
if (check_if_legal_tablename(to) &&

0 commit comments

Comments
 (0)