diff options
author | Mattias Jonsson <mattias.jonsson@sun.com> | 2009-06-02 11:07:17 +0200 |
---|---|---|
committer | Mattias Jonsson <mattias.jonsson@sun.com> | 2009-06-02 11:07:17 +0200 |
commit | 4fb78ee4c36b5c6bdec8f49d879a397aa656e639 (patch) | |
tree | d968ec17ba2d4346b12292eec0a857a9a0bf71bb | |
parent | 094ddf226611d119671cd3ac1af40a732540b989 (diff) | |
download | mariadb-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.
-rw-r--r-- | sql/mysql_priv.h | 10 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 21 | ||||
-rw-r--r-- | sql/sql_table.cc | 228 |
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). |