List:Commits« Previous MessageNext Message »
From:Mattias Jonsson Date:December 1 2008 5:07pm
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/

 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
 }
 

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