Skip to content

Commit 1a9c371

Browse files
author
Sergey Vojtovich
committed
BUG#48265 - MRG_MYISAM problem (works in 5.0.85, does't
work in 5.1.40) MERGE engine fails to open child table from a different database if child table/database name contains characters that are subject for table name to filename encoding (WL1324). Another problem is that MERGE engine didn't properly open child table from the same database if child table name contains characters like '/', '#'. The problem was that table name to file name encoding was applied inconsistently: * On CREATE: encode table name + database name if child table is in different database; do not encode table name if child table is in the same database; * No decoding on open. With this fix child table/database names are always encoded on CREATE and decoded on open. Compatibility with older tables preserved. Along with this patch comes fix for SHOW CREATE TABLE, which used to show child table/database path instead of child table/database names.
1 parent 29b7338 commit 1a9c371

File tree

5 files changed

+205
-53
lines changed

5 files changed

+205
-53
lines changed

mysql-test/r/merge.result

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2219,4 +2219,71 @@ Trigger sql_mode SQL Original Statement character_set_client collation_connectio
22192219
tr1 CREATE DEFINER=`root`@`localhost` TRIGGER tr1 AFTER INSERT ON t3 FOR EACH ROW CALL foo() latin1 latin1_swedish_ci latin1_swedish_ci
22202220
DROP TRIGGER tr1;
22212221
DROP TABLE t1, t2, t3;
2222+
#
2223+
# BUG#48265 - MRG_MYISAM problem (works in 5.0.85, does't work in 5.1.40)
2224+
#
2225+
CREATE DATABASE `test/1`;
2226+
CREATE TABLE `test/1`.`t/1`(a INT);
2227+
CREATE TABLE m1(a INT) ENGINE=MERGE UNION=(`test/1`.`t/1`);
2228+
SELECT * FROM m1;
2229+
a
2230+
SHOW CREATE TABLE m1;
2231+
Table Create Table
2232+
m1 CREATE TABLE `m1` (
2233+
`a` int(11) DEFAULT NULL
2234+
) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 UNION=(`test/1`.`t/1`)
2235+
DROP TABLE m1;
2236+
CREATE TABLE `test/1`.m1(a INT) ENGINE=MERGE UNION=(`test/1`.`t/1`);
2237+
SELECT * FROM `test/1`.m1;
2238+
a
2239+
SHOW CREATE TABLE `test/1`.m1;
2240+
Table Create Table
2241+
m1 CREATE TABLE `m1` (
2242+
`a` int(11) DEFAULT NULL
2243+
) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 UNION=(`t/1`)
2244+
DROP TABLE `test/1`.m1;
2245+
DROP TABLE `test/1`.`t/1`;
2246+
CREATE TEMPORARY TABLE `test/1`.`t/1`(a INT);
2247+
CREATE TEMPORARY TABLE m1(a INT) ENGINE=MERGE UNION=(`test/1`.`t/1`);
2248+
SELECT * FROM m1;
2249+
a
2250+
SHOW CREATE TABLE m1;
2251+
Table Create Table
2252+
m1 CREATE TEMPORARY TABLE `m1` (
2253+
`a` int(11) DEFAULT NULL
2254+
) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 UNION=(`test/1`.`t/1`)
2255+
DROP TABLE m1;
2256+
CREATE TEMPORARY TABLE `test/1`.m1(a INT) ENGINE=MERGE UNION=(`test/1`.`t/1`);
2257+
SELECT * FROM `test/1`.m1;
2258+
a
2259+
SHOW CREATE TABLE `test/1`.m1;
2260+
Table Create Table
2261+
m1 CREATE TEMPORARY TABLE `m1` (
2262+
`a` int(11) DEFAULT NULL
2263+
) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 UNION=(`t/1`)
2264+
DROP TABLE `test/1`.m1;
2265+
DROP TABLE `test/1`.`t/1`;
2266+
DROP DATABASE `test/1`;
2267+
CREATE TABLE `t@1`(a INT);
2268+
SELECT * FROM m1;
2269+
a
2270+
SHOW CREATE TABLE m1;
2271+
Table Create Table
2272+
m1 CREATE TABLE `m1` (
2273+
`a` int(11) DEFAULT NULL
2274+
) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 UNION=(`t@1`)
2275+
DROP TABLE `t@1`;
2276+
CREATE DATABASE `test@1`;
2277+
CREATE TABLE `test@1`.`t@1`(a INT);
2278+
FLUSH TABLE m1;
2279+
SELECT * FROM m1;
2280+
a
2281+
SHOW CREATE TABLE m1;
2282+
Table Create Table
2283+
m1 CREATE TABLE `m1` (
2284+
`a` int(11) DEFAULT NULL
2285+
) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 UNION=(`test@1`.`t@1`)
2286+
DROP TABLE m1;
2287+
DROP TABLE `test@1`.`t@1`;
2288+
DROP DATABASE `test@1`;
22222289
End of 5.1 tests

mysql-test/std_data/bug48265.frm

8.35 KB
Binary file not shown.

mysql-test/t/merge.test

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ drop table if exists t1,t2,t3,t4,t5,t6;
77
drop database if exists mysqltest;
88
--enable_warnings
99

10+
let $MYSQLD_DATADIR= `select @@datadir`;
11+
1012
create table t1 (a int not null primary key auto_increment, message char(20));
1113
create table t2 (a int not null primary key auto_increment, message char(20));
1214
INSERT INTO t1 (message) VALUES ("Testing"),("table"),("t1");
@@ -1633,4 +1635,59 @@ SHOW CREATE TRIGGER tr1;
16331635
DROP TRIGGER tr1;
16341636
DROP TABLE t1, t2, t3;
16351637

1638+
--echo #
1639+
--echo # BUG#48265 - MRG_MYISAM problem (works in 5.0.85, does't work in 5.1.40)
1640+
--echo #
1641+
CREATE DATABASE `test/1`;
1642+
1643+
CREATE TABLE `test/1`.`t/1`(a INT);
1644+
CREATE TABLE m1(a INT) ENGINE=MERGE UNION=(`test/1`.`t/1`);
1645+
SELECT * FROM m1;
1646+
SHOW CREATE TABLE m1;
1647+
DROP TABLE m1;
1648+
1649+
CREATE TABLE `test/1`.m1(a INT) ENGINE=MERGE UNION=(`test/1`.`t/1`);
1650+
SELECT * FROM `test/1`.m1;
1651+
SHOW CREATE TABLE `test/1`.m1;
1652+
DROP TABLE `test/1`.m1;
1653+
DROP TABLE `test/1`.`t/1`;
1654+
1655+
CREATE TEMPORARY TABLE `test/1`.`t/1`(a INT);
1656+
CREATE TEMPORARY TABLE m1(a INT) ENGINE=MERGE UNION=(`test/1`.`t/1`);
1657+
SELECT * FROM m1;
1658+
SHOW CREATE TABLE m1;
1659+
DROP TABLE m1;
1660+
1661+
CREATE TEMPORARY TABLE `test/1`.m1(a INT) ENGINE=MERGE UNION=(`test/1`.`t/1`);
1662+
SELECT * FROM `test/1`.m1;
1663+
SHOW CREATE TABLE `test/1`.m1;
1664+
DROP TABLE `test/1`.m1;
1665+
DROP TABLE `test/1`.`t/1`;
1666+
1667+
DROP DATABASE `test/1`;
1668+
1669+
# Test compatibility. Use '@' instead of '/' (was not allowed in 5.0)
1670+
1671+
CREATE TABLE `t@1`(a INT);
1672+
copy_file std_data/bug48265.frm $MYSQLD_DATADIR/test/m1.frm;
1673+
write_file $MYSQLD_DATADIR/test/m1.MRG;
1674+
t@1
1675+
EOF
1676+
SELECT * FROM m1;
1677+
SHOW CREATE TABLE m1;
1678+
DROP TABLE `t@1`;
1679+
1680+
CREATE DATABASE `test@1`;
1681+
CREATE TABLE `test@1`.`t@1`(a INT);
1682+
FLUSH TABLE m1;
1683+
remove_file $MYSQLD_DATADIR/test/m1.MRG;
1684+
write_file $MYSQLD_DATADIR/test/m1.MRG;
1685+
./test@1/t@1
1686+
EOF
1687+
SELECT * FROM m1;
1688+
SHOW CREATE TABLE m1;
1689+
DROP TABLE m1;
1690+
DROP TABLE `test@1`.`t@1`;
1691+
DROP DATABASE `test@1`;
1692+
16361693
--echo End of 5.1 tests

storage/myisammrg/ha_myisammrg.cc

Lines changed: 81 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -214,36 +214,14 @@ const char *ha_myisammrg::index_type(uint key_number)
214214
static int myisammrg_parent_open_callback(void *callback_param,
215215
const char *filename)
216216
{
217-
ha_myisammrg *ha_myrg;
218-
TABLE *parent;
217+
ha_myisammrg *ha_myrg= (ha_myisammrg*) callback_param;
218+
TABLE *parent= ha_myrg->table_ptr();
219219
TABLE_LIST *child_l;
220-
const char *db;
221-
const char *table_name;
222220
size_t dirlen;
223221
char dir_path[FN_REFLEN];
222+
char name_buf[NAME_LEN];
224223
DBUG_ENTER("myisammrg_parent_open_callback");
225224

226-
/* Extract child table name and database name from filename. */
227-
dirlen= dirname_length(filename);
228-
if (dirlen >= FN_REFLEN)
229-
{
230-
/* purecov: begin inspected */
231-
DBUG_PRINT("error", ("name too long: '%.64s'", filename));
232-
my_errno= ENAMETOOLONG;
233-
DBUG_RETURN(1);
234-
/* purecov: end */
235-
}
236-
table_name= filename + dirlen;
237-
dirlen--; /* Strip off trailing '/'. */
238-
memcpy(dir_path, filename, dirlen);
239-
dir_path[dirlen]= '\0';
240-
db= base_name(dir_path);
241-
dirlen-= db - dir_path; /* This is now the length of 'db'. */
242-
DBUG_PRINT("myrg", ("open: '%s'.'%s'", db, table_name));
243-
244-
ha_myrg= (ha_myisammrg*) callback_param;
245-
parent= ha_myrg->table_ptr();
246-
247225
/* Get a TABLE_LIST object. */
248226
if (!(child_l= (TABLE_LIST*) alloc_root(&parent->mem_root,
249227
sizeof(TABLE_LIST))))
@@ -255,13 +233,69 @@ static int myisammrg_parent_open_callback(void *callback_param,
255233
}
256234
bzero((char*) child_l, sizeof(TABLE_LIST));
257235

258-
/* Set database (schema) name. */
259-
child_l->db_length= dirlen;
260-
child_l->db= strmake_root(&parent->mem_root, db, dirlen);
261-
/* Set table name. */
262-
child_l->table_name_length= strlen(table_name);
263-
child_l->table_name= strmake_root(&parent->mem_root, table_name,
264-
child_l->table_name_length);
236+
/*
237+
Depending on MySQL version, filename may be encoded by table name to
238+
file name encoding or not. Always encoded if parent table is created
239+
by 5.1.46+. Encoded if parent is created by 5.1.6+ and child table is
240+
in different database.
241+
*/
242+
if (!has_path(filename))
243+
{
244+
/* Child is in the same database as parent. */
245+
child_l->db_length= parent->s->db.length;
246+
child_l->db= strmake_root(&parent->mem_root, parent->s->db.str,
247+
child_l->db_length);
248+
/* Child table name is encoded in parent dot-MRG starting with 5.1.46. */
249+
if (parent->s->mysql_version >= 50146)
250+
{
251+
child_l->table_name_length= filename_to_tablename(filename, name_buf,
252+
sizeof(name_buf));
253+
child_l->table_name= strmake_root(&parent->mem_root, name_buf,
254+
child_l->table_name_length);
255+
}
256+
else
257+
{
258+
child_l->table_name_length= strlen(filename);
259+
child_l->table_name= strmake_root(&parent->mem_root, filename,
260+
child_l->table_name_length);
261+
}
262+
}
263+
else
264+
{
265+
DBUG_ASSERT(strlen(filename) < sizeof(dir_path));
266+
fn_format(dir_path, filename, "", "", 0);
267+
/* Extract child table name and database name from filename. */
268+
dirlen= dirname_length(dir_path);
269+
/* Child db/table name is encoded in parent dot-MRG starting with 5.1.6. */
270+
if (parent->s->mysql_version >= 50106)
271+
{
272+
child_l->table_name_length= filename_to_tablename(dir_path + dirlen,
273+
name_buf,
274+
sizeof(name_buf));
275+
child_l->table_name= strmake_root(&parent->mem_root, name_buf,
276+
child_l->table_name_length);
277+
dir_path[dirlen - 1]= 0;
278+
dirlen= dirname_length(dir_path);
279+
child_l->db_length= filename_to_tablename(dir_path + dirlen, name_buf,
280+
sizeof(name_buf));
281+
child_l->db= strmake_root(&parent->mem_root, name_buf, child_l->db_length);
282+
}
283+
else
284+
{
285+
child_l->table_name_length= strlen(dir_path + dirlen);
286+
child_l->table_name= strmake_root(&parent->mem_root, dir_path + dirlen,
287+
child_l->table_name_length);
288+
dir_path[dirlen - 1]= 0;
289+
dirlen= dirname_length(dir_path);
290+
child_l->db_length= strlen(dir_path + dirlen);
291+
child_l->db= strmake_root(&parent->mem_root, dir_path + dirlen,
292+
child_l->db_length);
293+
}
294+
}
295+
296+
DBUG_PRINT("myrg", ("open: '%.*s'.'%.*s'", child_l->db_length, child_l->db,
297+
child_l->table_name_length, child_l->table_name));
298+
265299
/* Convert to lowercase if required. */
266300
if (lower_case_table_names && child_l->table_name_length)
267301
child_l->table_name_length= my_casedn_str(files_charset_info,
@@ -1132,7 +1166,7 @@ int ha_myisammrg::create(const char *name, register TABLE *form,
11321166
/* Create child path names. */
11331167
for (pos= table_names; tables; tables= tables->next_local)
11341168
{
1135-
const char *table_name;
1169+
const char *table_name= buff;
11361170

11371171
/*
11381172
Construct the path to the MyISAM table. Try to meet two conditions:
@@ -1158,10 +1192,12 @@ int ha_myisammrg::create(const char *name, register TABLE *form,
11581192
as the MyISAM tables are from the same database as the MERGE table.
11591193
*/
11601194
if ((dirname_length(buff) == dirlgt) && ! memcmp(buff, name, dirlgt))
1161-
table_name= tables->table_name;
1162-
else
1163-
if (! (table_name= thd->strmake(buff, length)))
1164-
DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
1195+
{
1196+
table_name+= dirlgt;
1197+
length-= dirlgt;
1198+
}
1199+
if (!(table_name= thd->strmake(table_name, length)))
1200+
DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
11651201

11661202
*pos++= table_name;
11671203
}
@@ -1182,7 +1218,7 @@ void ha_myisammrg::append_create_info(String *packet)
11821218
const char *current_db;
11831219
size_t db_length;
11841220
THD *thd= current_thd;
1185-
MYRG_TABLE *open_table, *first;
1221+
TABLE_LIST *open_table, *first;
11861222

11871223
if (file->merge_insert_method != MERGE_INSERT_DISABLED)
11881224
{
@@ -1200,14 +1236,11 @@ void ha_myisammrg::append_create_info(String *packet)
12001236
current_db= table->s->db.str;
12011237
db_length= table->s->db.length;
12021238

1203-
for (first=open_table=file->open_tables ;
1204-
open_table != file->end_table ;
1205-
open_table++)
1239+
for (first= open_table= table->child_l;;
1240+
open_table= open_table->next_global)
12061241
{
1207-
LEX_STRING db, name;
1208-
LINT_INIT(db.str);
1242+
LEX_STRING db= { open_table->db, open_table->db_length };
12091243

1210-
split_file_name(open_table->table->filename, &db, &name);
12111244
if (open_table != first)
12121245
packet->append(',');
12131246
/* Report database for mapped table if it isn't in current database */
@@ -1218,7 +1251,10 @@ void ha_myisammrg::append_create_info(String *packet)
12181251
append_identifier(thd, packet, db.str, db.length);
12191252
packet->append('.');
12201253
}
1221-
append_identifier(thd, packet, name.str, name.length);
1254+
append_identifier(thd, packet, open_table->table_name,
1255+
open_table->table_name_length);
1256+
if (&open_table->next_global == table->child_last_l)
1257+
break;
12221258
}
12231259
packet->append(')');
12241260
}

storage/myisammrg/myrg_open.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -311,14 +311,6 @@ MYRG_INFO *myrg_parent_open(const char *parent_name,
311311
if (!child_name_buff[0] || (child_name_buff[0] == '#'))
312312
continue;
313313

314-
if (!has_path(child_name_buff))
315-
{
316-
VOID(strmake(parent_name_buff + dir_length, child_name_buff,
317-
sizeof(parent_name_buff) - 1 - dir_length));
318-
VOID(cleanup_dirname(child_name_buff, parent_name_buff));
319-
}
320-
else
321-
fn_format(child_name_buff, child_name_buff, "", "", 0);
322314
DBUG_PRINT("info", ("child: '%s'", child_name_buff));
323315

324316
/* Callback registers child with handler table. */

0 commit comments

Comments
 (0)