#At file:///Users/mattiasj/clones/bzrroot/b32430_2-60-bugteam/
2753 Mattias Jonsson 2008-12-01
Bug#32430:'show innodb status' causes errors Invalid (old?) table
or database name in logs
Problem was that InnoDB used filename_to_tablename,
which do not hanlde partitions (due to the '#' in
the filename).
Solution is to add a new fuction for explaining
what the filename means, explain_filename.
It expands to Database, Table, [Temporary|Renamed] Partition,
Subpartition and uses errmsg.txt for localization. It also
converts from my_charset_filename to system_charset_info
(i.e. human readable form for non ascii characters).
This will first go through review in Sun for the server part,
and if approved go through review by InnoBASE and if approved,
it will then be included in both server and InnoDB.
modified:
mysql-test/r/partition_innodb.result
mysql-test/t/partition_innodb.test
sql/mysql_priv.h
sql/share/errmsg.txt
sql/sql_table.cc
storage/innobase/dict/dict0dict.c
storage/innobase/handler/ha_innodb.cc
storage/innobase/ut/ut0ut.c
per-file messages:
mysql-test/r/partition_innodb.result
Bug#32430:'show innodb status' causes errors Invalid (old?) table
or database name in logs
Updated test result
mysql-test/t/partition_innodb.test
Bug#32430:'show innodb status' causes errors Invalid (old?) table
or database name in logs
Added deterministic tests.
Note that the --replace_regex is very slow in match copy/paste
operations on long strings, so instead it only removes strings
piece by piece.
sql/mysql_priv.h
Bug#32430:'show innodb status' causes errors Invalid (old?) table
or database name in logs
Added EXPLAIN_FILENAME_MAX_EXTRA_LENGTH and explain_filename
sql/share/errmsg.txt
Bug#32430:'show innodb status' causes errors Invalid (old?) table
or database name in logs
Added messages (localized names) for explain_filename function.
sql/sql_table.cc
Bug#32430:'show innodb status' causes errors Invalid (old?) table
or database name in logs
Added explain_filename function.
To be used by Storage Engines instead of filename_to_tablename,
since the Storage Engine cannot use that for partitions.
The function splits the name from:
[.../db/]t[#P#[#SP#sp][#TMP#|#REN#]]
to:
[Database `db`, ]Table `t`[, [Temporary |Renamed ]\
Partition `p`[, Subpartition `sp`]]
Where the words Database, Table, Temporary, Renamed,Partition
and Subpartition are taken from errmsg.txt
storage/innobase/dict/dict0dict.c
Bug#32430:'show innodb status' causes errors Invalid (old?) table
or database name in logs
Proposed change for better message in 'SHOW ENGINE INNODB STATUS'
storage/innobase/handler/ha_innodb.cc
Bug#32430:'show innodb status' causes errors Invalid (old?) table
or database name in logs
Proposed update to use explain_filename instead of
filename_to_tablename.
storage/innobase/ut/ut0ut.c
Bug#32430:'show innodb status' causes errors Invalid (old?) table
or database name in logs
Proposed change for better message in 'SHOW ENGINE INNODB STATUS'
=== modified file 'mysql-test/r/partition_innodb.result'
--- a/mysql-test/r/partition_innodb.result 2008-06-12 00:08:07 +0000
+++ b/mysql-test/r/partition_innodb.result 2008-12-01 17:06:50 +0000
@@ -1,3 +1,36 @@
+SET NAMES utf8;
+CREATE TABLE t1 (a INT, PRIMARY KEY (a))
+ENGINE=InnoDB
+PARTITION BY RANGE (a)
+SUBPARTITION BY HASH (a)
+(PARTITION p0 VALUES LESS THAN (100)
+(SUBPARTITION sp0,
+SUBPARTITION sp1),
+PARTITION p1 VALUES LESS THAN (MAXVALUE)
+(SUBPARTITION sp2,
+SUBPARTITION sp3));
+INSERT INTO t1 VALUES (0), (2), (6), (10), (14), (18), (22);
+START TRANSACTION;
+# con1
+SET NAMES utf8;
+START TRANSACTION;
+# default connection
+UPDATE t1 SET a = 16 WHERE a = 0;
+# con1
+UPDATE t1 SET a = 8 WHERE a = 22;
+UPDATE t1 SET a = 12 WHERE a = 0;
+# default connection
+UPDATE t1 SET a = 4 WHERE a = 22;
+ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
+# First table reported in 'SHOW ENGINE InnoDB STATUS'
+SHOW ENGINE InnoDB STATUS;
+Type Name Status
+InnoDB Database `test`, Table `t1`, Partition `p0`, Subpartition `sp0`
+# con1
+ROLLBACK;
+# default connection
+DROP TABLE t1;
+SET NAMES DEFAULT;
# Bug#32948
CREATE TABLE t1 (c1 INT, PRIMARY KEY (c1)) ENGINE=INNODB;
CREATE TABLE t2 (c1 INT, PRIMARY KEY (c1),
=== modified file 'mysql-test/t/partition_innodb.test'
--- a/mysql-test/t/partition_innodb.test 2008-06-12 00:08:07 +0000
+++ b/mysql-test/t/partition_innodb.test 2008-12-01 17:06:50 +0000
@@ -1,6 +1,62 @@
--source include/have_partition.inc
--source include/have_innodb.inc
+# Bug#32430 - show engine innodb status causes errors
+#
+SET NAMES utf8;
+CREATE TABLE t1 (a INT, PRIMARY KEY (a))
+ENGINE=InnoDB
+PARTITION BY RANGE (a)
+SUBPARTITION BY HASH (a)
+(PARTITION p0 VALUES LESS THAN (100)
+ (SUBPARTITION sp0,
+ SUBPARTITION sp1),
+ PARTITION p1 VALUES LESS THAN (MAXVALUE)
+ (SUBPARTITION sp2,
+ SUBPARTITION sp3));
+INSERT INTO t1 VALUES (0), (2), (6), (10), (14), (18), (22);
+START TRANSACTION;
+--echo # con1
+connect(con1,localhost,root,,);
+SET NAMES utf8;
+START TRANSACTION;
+--echo # default connection
+connection default;
+UPDATE t1 SET a = 16 WHERE a = 0;
+--echo # con1
+connection con1;
+UPDATE t1 SET a = 8 WHERE a = 22;
+let $id_1= `SELECT CONNECTION_ID()`;
+SEND;
+UPDATE t1 SET a = 12 WHERE a = 0;
+--echo # default connection
+connection default;
+let $wait_timeout= 2;
+let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
+WHERE ID = $id_1 AND STATE = 'Searching rows for update';
+--source include/wait_condition.inc
+#--echo # tested wait condition $wait_condition_reps times
+--error ER_LOCK_DEADLOCK
+UPDATE t1 SET a = 4 WHERE a = 22;
+--echo # First table reported in 'SHOW ENGINE InnoDB STATUS'
+# RECORD LOCKS space id 0 page no 50 n bits 80 index `PRIMARY` in \
+# Database `test`, Table `t1`, Partition `p0`, Subpartition `sp0` \
+# trx id 0 775
+# NOTE: replace_regex is very slow on match copy/past '(.*)' regex's
+# on big texts, removing a lot of text before + after makes it much faster.
+#/.*in (.*) trx.*/\1/
+--replace_regex /.*RECORD LOCKS space id [0-9]* page no [0-9]* n bits [0-9]* // / trx id .*// /.*index .* in //
+SHOW ENGINE InnoDB STATUS;
+--echo # con1
+connection con1;
+REAP;
+ROLLBACK;
+disconnect con1;
+--echo # default connection
+connection default;
+DROP TABLE t1;
+SET NAMES DEFAULT;
+
# Bug#32948 - FKs allowed to reference partitioned table
#
-- echo # Bug#32948
=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h 2008-08-07 03:05:33 +0000
+++ b/sql/mysql_priv.h 2008-12-01 17:06:50 +0000
@@ -2322,6 +2322,9 @@ char *fn_rext(char *name);
#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
uint strconvert(CHARSET_INFO *from_cs, const char *from,
CHARSET_INFO *to_cs, char *to, uint to_length, uint *errors);
+/* depends on errmsg.txt Database `db`, Table `t` ... */
+#define EXPLAIN_FILENAME_MAX_EXTRA_LENGTH 63
+uint explain_filename(const char *from, char *to, uint to_length);
uint filename_to_tablename(const char *from, char *to, uint to_length);
uint tablename_to_filename(const char *from, char *to, uint to_length);
#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
=== modified file 'sql/share/errmsg.txt'
--- a/sql/share/errmsg.txt 2008-08-06 14:39:03 +0000
+++ b/sql/share/errmsg.txt 2008-12-01 17:06:50 +0000
@@ -6372,3 +6372,23 @@ ER_BACKUP_OBTAIN_NAME_LOCK_FAILED
eng "Restore failed to obtain the name locks on the tables."
ER_BACKUP_RELEASE_NAME_LOCK_FAILED
eng "Restore failed to release the name locks on the tables."
+# When updating these, please update EXPLAIN_FILENAME_MAX_EXTRA_LENGTH in
+# mysql_priv.h with the new maximal additional length for explain_filename.
+ER_DATABASE_NAME
+ eng "Database `%s`"
+ swe "Databas `%s`"
+ER_TABLE_NAME
+ eng "Table `%s`"
+ swe "Tabell `%s`"
+ER_PARTITION_NAME
+ eng "Partition `%s`"
+ swe "Partition `%s`"
+ER_SUBPARTITION_NAME
+ eng "Subpartition `%s`"
+ swe "Subpartition `%s`"
+ER_TEMPORARY_NAME
+ eng "Temporary"
+ swe "Temporär"
+ER_RENAMED_NAME
+ eng "Renamed"
+ swe "Namnändrad"
=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc 2008-07-26 16:38:20 +0000
+++ b/sql/sql_table.cc 2008-12-01 17:06:50 +0000
@@ -68,6 +68,202 @@ static void wait_for_kill_signal(THD *th
#endif
+/**
+ @brief Helper function for explain_filename
+*/
+static char* add_identifier(char *to_p, const char * end_p,
+ const char* name, uint name_len, int errcode)
+{
+ uint res;
+ uint errors;
+ const char *conv_name;
+ char tmp_name[FN_REFLEN];
+ char conv_string[FN_REFLEN];
+
+ DBUG_ENTER("add_identifier");
+ if (!name[name_len])
+ conv_name= name;
+ else
+ {
+ strnmov(tmp_name, name, name_len);
+ tmp_name[name_len]= 0;
+ conv_name= tmp_name;
+ }
+ res= strconvert(&my_charset_filename, conv_name, system_charset_info,
+ conv_string, FN_REFLEN, &errors);
+ if (!res || errors)
+ conv_name= name;
+ else
+ {
+ DBUG_PRINT("info", ("conv '%s' -> '%s'", conv_name, conv_string));
+ conv_name= conv_string;
+ }
+
+ to_p+= my_snprintf(to_p, end_p - to_p, ER(errcode), conv_name);
+ return to_p;
+}
+
+
+/**
+ @brief Explain a file name by split it to [database,]tablename[,partname...]
+
+ @details Break down the filename to its logic parts and add the partname
+ (database, table, partition, subpartition).
+ filename_to_tablename cannot be used on partitions, due to the #P# part.
+ There can be up to 6 '#', #P# for partition, #SP# for subpartition
+ and #TMP# or #REN# for temporary or renamed partitions.
+ This should be used when something should be presented to a user in a
+ diagnostic, error etc. when it would be useful to know what a particular
+ file [and directory] means. Such as SHOW ENGINE STATUS, error messages etc.
+
+ @param from File name in my_charset_filename
+ Null terminated in my_charset_filename, normalized
+ to use '/' as directory separation character.
+ @param to Explained name in system_charset_info
+ @param to_length Size of to buffer
+
+ @retval Length of returned string
+*/
+
+uint explain_filename(const char *from, char *to, uint to_length)
+{
+ uint res= 0;
+ char *to_p= to;
+ char *end_p= to_p + to_length;
+ const char *db_name= NULL;
+ int db_name_len= 0;
+ const char *table_name;
+ int table_name_len= 0;
+ const char *part_name= NULL;
+ int part_name_len= 0;
+ const char *subpart_name= NULL;
+ int subpart_name_len= 0;
+ enum enum_file_name_type {NORMAL, TEMP, RENAMED} name_type= NORMAL;
+ const char *tmp_p;
+ DBUG_ENTER("explain_filename");
+ DBUG_PRINT("enter", ("from '%s'", from));
+ /*
+ if '/' then take last directory part as database
+ Compose the explained name as
+ [Database %s, ]Table %s[,[ Temporary| Renamed] Partition %s
+ [, Subpartition %s]]
+ */
+ tmp_p= from;
+ table_name= from;
+ /* '/' is the directory separator, not FN_LIB_CHAR */
+ while ((tmp_p= strchr(tmp_p, '/')))
+ {
+ db_name= table_name;
+ /* calculate the length */
+ db_name_len= tmp_p - db_name;
+ tmp_p++;
+ table_name= tmp_p;
+ }
+ tmp_p= table_name;
+ while (!res && (tmp_p= strchr(tmp_p, '#')))
+ {
+ tmp_p++;
+ switch (tmp_p[0]) {
+ case 'P':
+ case 'p':
+ if (tmp_p[1] == '#')
+ part_name= tmp_p + 2;
+ else
+ res= 1;
+ tmp_p+= 2;
+ break;
+ case 'S':
+ case 's':
+ if ((tmp_p[1] == 'P' || tmp_p[1] == 'p') && tmp_p[2] == '#')
+ {
+ part_name_len= tmp_p - part_name - 1;
+ subpart_name= tmp_p + 3;
+ }
+ else
+ res= 2;
+ tmp_p+= 3;
+ break;
+ case 'T':
+ case 't':
+ if ((tmp_p[1] == 'M' || tmp_p[1] == 'm') &&
+ (tmp_p[2] == 'P' || tmp_p[2] == 'p') &&
+ tmp_p[3] == '#' && !tmp_p[4])
+ name_type= TEMP;
+ else
+ res= 3;
+ tmp_p+= 4;
+ break;
+ case 'R':
+ case 'r':
+ if ((tmp_p[1] == 'E' || tmp_p[1] == 'e') &&
+ (tmp_p[2] == 'N' || tmp_p[2] == 'n') &&
+ tmp_p[3] == '#' && !tmp_p[4])
+ name_type= RENAMED;
+ else
+ res= 4;
+ tmp_p+= 4;
+ break;
+ default:
+ res= 5;
+ }
+ }
+ if (res)
+ {
+ /* Better to give something back if we fail parsing, than nothing at all */
+ DBUG_PRINT("info", ("Error in explain_filename: %u", res));
+ sql_print_warning("Invalid (old?) table or database name '%s'", from);
+ DBUG_RETURN(my_snprintf(to, to_length,
+ "<result %u when explaining filename '%s'>",
+ res, from));
+ }
+ if (part_name)
+ {
+ table_name_len= part_name - table_name - 3;
+ if (subpart_name)
+ subpart_name_len= strlen(subpart_name);
+ else
+ part_name_len= strlen(part_name);
+ if (name_type != NORMAL)
+ {
+ if (subpart_name)
+ subpart_name_len-= 5;
+ else
+ part_name_len-= 5;
+ }
+ }
+ if (db_name)
+ {
+ to_p= add_identifier(to_p, end_p, db_name, db_name_len,
+ ER_DATABASE_NAME);
+ to_p= strnmov(to_p, ", ", end_p - to_p);
+ }
+ to_p= add_identifier(to_p, end_p, table_name, table_name_len,
+ ER_TABLE_NAME);
+ if (part_name)
+ {
+ to_p= strnmov(to_p, ", ", end_p - to_p);
+ if (name_type != NORMAL)
+ {
+ if (name_type == TEMP)
+ to_p= strnmov(to_p, ER(ER_TEMPORARY_NAME), end_p - to_p);
+ else
+ to_p= strnmov(to_p, ER(ER_RENAMED_NAME), end_p - to_p);
+ to_p= strnmov(to_p, " ", end_p - to_p);
+ }
+ to_p= add_identifier(to_p, end_p, part_name, part_name_len,
+ ER_PARTITION_NAME);
+ if (subpart_name)
+ {
+ to_p= strnmov(to_p, ", ", end_p - to_p);
+ to_p= add_identifier(to_p, end_p, subpart_name, subpart_name_len,
+ ER_SUBPARTITION_NAME);
+ }
+ }
+ DBUG_PRINT("exit", ("to '%s'", to));
+ DBUG_RETURN(to_p - to);
+}
+
+
/*
Translate a file name to a table name (WL #1324).
=== modified file 'storage/innobase/dict/dict0dict.c'
--- a/storage/innobase/dict/dict0dict.c 2008-06-12 00:08:07 +0000
+++ b/storage/innobase/dict/dict0dict.c 2008-12-01 17:06:50 +0000
@@ -4210,6 +4210,6 @@ dict_index_name_print(
{
fputs("index ", file);
ut_print_name(file, trx, FALSE, index->name);
- fputs(" of table ", file);
+ fputs(" in ", file);
ut_print_name(file, trx, TRUE, index->table_name);
}
=== modified file 'storage/innobase/handler/ha_innodb.cc'
--- a/storage/innobase/handler/ha_innodb.cc 2008-06-12 00:08:07 +0000
+++ b/storage/innobase/handler/ha_innodb.cc 2008-12-01 17:06:50 +0000
@@ -1271,14 +1271,14 @@ innobase_print_identifier(
int q;
if (table_id) {
- /* Decode the table name. The filename_to_tablename()
+ /* Decode the table name. The explain_filename()
function expects a NUL-terminated string. The input and
output strings buffers must not be shared. The function
only produces more output when the name contains other
characters than [0-9A-Z_a-z]. */
char* temp_name = (char*) my_malloc((uint) namelen + 1, MYF(MY_WME));
uint qnamelen = (uint) (namelen
- + (1 + sizeof srv_mysql50_table_name_prefix));
+ + (1 + EXPLAIN_FILENAME_MAX_EXTRA_LENGTH));
if (temp_name) {
qname = (char*) my_malloc(qnamelen, MYF(MY_WME));
@@ -1286,7 +1286,7 @@ innobase_print_identifier(
memcpy(temp_name, name, namelen);
temp_name[namelen] = 0;
s = qname;
- namelen = filename_to_tablename(temp_name,
+ namelen = explain_filename(temp_name,
qname, qnamelen);
}
my_free(temp_name, MYF(0));
@@ -1301,7 +1301,7 @@ innobase_print_identifier(
s, (int) namelen);
}
- if (q == EOF) {
+ if (q == EOF || table_id) {
fwrite(s, 1, namelen, f);
} else {
const char* e = s + namelen;
=== modified file 'storage/innobase/ut/ut0ut.c'
--- a/storage/innobase/ut/ut0ut.c 2008-06-12 00:08:07 +0000
+++ b/storage/innobase/ut/ut0ut.c 2008-12-01 17:06:50 +0000
@@ -487,22 +487,7 @@ ut_print_namel(
#ifdef UNIV_HOTBACKUP
fwrite(name, 1, namelen, f);
#else
- if (table_id) {
- char* slash = memchr(name, '/', namelen);
- if (!slash) {
-
- goto no_db_name;
- }
-
- /* Print the database name and table name separately. */
- innobase_print_identifier(f, trx, TRUE, name, slash - name);
- putc('.', f);
- innobase_print_identifier(f, trx, TRUE, slash + 1,
- namelen - (slash - name) - 1);
- } else {
-no_db_name:
- innobase_print_identifier(f, trx, table_id, name, namelen);
- }
+ innobase_print_identifier(f, trx, table_id, name, namelen);
#endif
}