List:Commits« Previous MessageNext Message »
From:Mattias Jonsson Date:December 19 2008 9:53am
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-19
      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).
      
      It can also return only db and table name as well as
      append the partition/subpartition as a comment.
      
      
      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
    operation on long strings, so intead 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_EXRA_LENGTH ad explain_filename.
  sql/share/errmsg.txt
    Bug#32430:'show innodb status' causes errors Invalid (old?) table
    or database name in logs
    
    Added localized 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 file.
  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-19 09:53:03 +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-19 09:53:03 +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-19 09:53:03 +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_ONLY_DB_AND_TABLE_NAME= 0,
+  EXPLAIN_ALL_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-19 09:53:03 +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-19 09:53:03 +0000
@@ -68,6 +68,230 @@ 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_ONLY_DB_AND_TABLE_NAME -> `db`.`tbl`
+                            EXPLAIN_ALL_VERBOSE ->
+                            [Database `db`, ]Table `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 && explain_mode != EXPLAIN_ONLY_DB_AND_TABLE_NAME)
+  {
+    if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT)
+      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-19 09:53:03 +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-19 09:53:03 +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_AS_COMMENT);
 			}
 			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-19 09:53:03 +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 Jonsson19 Dec