summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorMattias Jonsson <mattias.jonsson@sun.com>2009-06-02 11:07:17 +0200
committerMattias Jonsson <mattias.jonsson@sun.com>2009-06-02 11:07:17 +0200
commit4fb78ee4c36b5c6bdec8f49d879a397aa656e639 (patch)
treed968ec17ba2d4346b12292eec0a857a9a0bf71bb /sql
parent094ddf226611d119671cd3ac1af40a732540b989 (diff)
downloadmariadb-git-4fb78ee4c36b5c6bdec8f49d879a397aa656e639.tar.gz
Bug#32430:'show innodb status' causes errors Invalid (old?) table
or database name in logs Problem was that InnoDB used filenam_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). http://lists.mysql.com/commits/70370 2773 Mattias Jonsson 2009-03-25 It has three different output styles. NOTE: This is the server side ONLY part (introducing the explain_filename function). There will be a patch for InnoDB using this function to solve the bug. 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.
Diffstat (limited to 'sql')
-rw-r--r--sql/mysql_priv.h10
-rw-r--r--sql/share/errmsg.txt21
-rw-r--r--sql/sql_table.cc228
3 files changed, 259 insertions, 0 deletions
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 69a789284b0..dcd572bc824 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -2253,6 +2253,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);
uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length);
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index 7c92c827c87..42bca02984d 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -6180,3 +6180,24 @@ ER_TOO_LONG_FIELD_COMMENT
ER_FUNC_INEXISTENT_NAME_COLLISION 42000
eng "FUNCTION %s does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual"
+
+# 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"
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 3f90a950324..0fc1d04a41b 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -68,6 +68,234 @@ static void wait_for_kill_signal(THD *thd)
#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).