List:Commits« Previous MessageNext Message »
From:Mattias Jonsson Date:December 22 2008 11:39am
Subject:bzr commit into mysql-6.0-bugteam branch (mattias.jonsson:2753)
Bug#32430
View as plain text  
#At file:///Users/mattiasj/clones/bzrroot/b32430_2-60-bugteam/ based on revid:chad@stripped80808144421-hlpsy443bzwmx3r4

 2753 Mattias Jonsson	2008-12-22
      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 handle partitions (due to the '#' in
      the filename).
      
      Solution is to add a new function for explaining
      what the filename means: explain_filename.
      It expands the database, table, partition and subpartition
      parts 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).
      
      It has three different output styles.
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 test case.
    
    Note that the --replace_regex is very slow in match copy/paste
    operation 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, enum_explain_filename_mode
    and explain_filename.
  sql/share/errmsg.txt
    Bug#32430:'show innodb status' causes errors Invalid (old?) table
    or database name in logs
    
    Added localization names for Database, Table, Partition, Subpartition,
    Temporary and Renamed.
  sql/sql_table.cc
    Bug#32430:'show innodb status' causes errors Invalid (old?) table
    or database name in logs
    
    Added explain_filename function for giving better information
    to the user about a specific table/partitions file.
  storage/innobase/dict/dict0dict.c
    Bug#32430:'show innodb status' causes errors Invalid (old?) table
    or database name in logs
    
    Proposed change for 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 change for SHOW ENGINE INNODB STATUS.
    Uses 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 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-22 11:39:11 +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		`test`.`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-22 11:39:11 +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-22 11:39:11 +0000
@@ -2322,6 +2322,16 @@ 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
+enum enum_explain_filename_mode
+{
+  EXPLAIN_ALL_VERBOSE= 0,
+  EXPLAIN_PARTITIONS_VERBOSE,
+  EXPLAIN_PARTITIONS_AS_COMMENT
+};
+uint explain_filename(const char *from, char *to, uint to_length,
+                      enum_explain_filename_mode explain_mode);
 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-22 11:39:11 +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-22 11:39:11 +0000
@@ -68,6 +68,234 @@ 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;
+  }
+
+  if (errcode)
+    to_p+= my_snprintf(to_p, end_p - to_p, ER(errcode), conv_name);
+  else
+    to_p+= my_snprintf(to_p, end_p - to_p, "`%s`", conv_name);
+  return to_p;
+}
+
+
+/**
+  @brief Explain a path name by split it to database, table etc.
+  
+  @details Break down the path name to its logic parts
+  (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         Path 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
+   @param      explain_mode Requested output format.
+                            EXPLAIN_ALL_VERBOSE ->
+                            [Database `db`, ]Table `tbl`[,[ Temporary| Renamed]
+                            Partition `p` [, Subpartition `sp`]]
+                            EXPLAIN_PARTITIONS_VERBOSE -> `db`.`tbl`
+                            [[ Temporary| Renamed] Partition `p`
+                            [, Subpartition `sp`]]
+                            EXPLAIN_PARTITIONS_AS_COMMENT -> `db`.`tbl` |*
+                            [,[ Temporary| Renamed] Partition `p`
+                            [, Subpartition `sp`]] *|
+                            (| is really a /, and it is all in one line)
+
+   @retval     Length of returned string
+*/
+
+uint explain_filename(const char *from,
+                      char *to,
+                      uint to_length,
+                      enum_explain_filename_mode explain_mode)
+{
+  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));
+  tmp_p= from;
+  table_name= from;
+  /*
+    If '/' then take last directory part as database.
+    '/' 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)
+  {
+    if (explain_mode == EXPLAIN_ALL_VERBOSE)
+    {
+      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);
+    }
+    else
+    {
+      to_p= add_identifier(to_p, end_p, db_name, db_name_len, 0);
+      to_p= strnmov(to_p, ".", end_p - to_p);
+    }
+  }
+  if (explain_mode == EXPLAIN_ALL_VERBOSE)
+    to_p= add_identifier(to_p, end_p, table_name, table_name_len,
+                         ER_TABLE_NAME);
+  else
+    to_p= add_identifier(to_p, end_p, table_name, table_name_len, 0);
+  if (part_name)
+  {
+    if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT)
+      to_p= strnmov(to_p, " /* ", end_p - to_p);
+    else if (explain_mode == EXPLAIN_PARTITIONS_VERBOSE)
+      to_p= strnmov(to_p, " ", end_p - to_p);
+    else
+      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);
+    }
+    if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT)
+      to_p= strnmov(to_p, " */", end_p - to_p);
+  }
+  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-22 11:39:11 +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-22 11:39:11 +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,8 +1286,9 @@ innobase_print_identifier(
 				memcpy(temp_name, name, namelen);
 				temp_name[namelen] = 0;
 				s = qname;
-				namelen = filename_to_tablename(temp_name,
-						qname, qnamelen);
+				namelen = explain_filename(temp_name,
+						qname, qnamelen,
+                                                EXPLAIN_PARTITIONS_VERBOSE);
 			}
 			my_free(temp_name, MYF(0));
 		}
@@ -1301,7 +1302,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-22 11:39:11 +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
 }
 

Thread
bzr commit into mysql-6.0-bugteam branch (mattias.jonsson:2753)Bug#32430Mattias Jonsson22 Dec
  • Re: bzr commit into mysql-6.0-bugteam branch(mattias.jonsson:2753) Bug#32430Sergei Golubchik8 Jan