From 6bef32a76866d7bfae527eec032b5d6fb41bd2f4 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Mon, 29 Nov 2010 17:30:07 +0200 Subject: [PATCH 01/12] Fixed a pb2 issue with not finding the test dirs. --- mysql-test/collections/default.push | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/collections/default.push b/mysql-test/collections/default.push index 0f4115c8565..ce7b491efe7 100644 --- a/mysql-test/collections/default.push +++ b/mysql-test/collections/default.push @@ -1,2 +1,2 @@ -perl mysql-test-run.pl --timer --force --comment=n_stm -perl mysql-test-run.pl --timer --force --comment=ps_stm --ps-protocol +perl mysql-test-run.pl --timer --force --vardir=n_stm --comment=n_stm +perl mysql-test-run.pl --timer --force --vardir=ps_stm --comment=ps_stm --ps-protocol From 2551fff8b2d50ecfd34a8100d95b2c8834f0fdb2 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Mon, 29 Nov 2010 17:33:24 +0200 Subject: [PATCH 02/12] Fixed bteam issue #37235: 5.0 trees now will work correctly in pb2 and will not display "indicated result file not found" due to wrongly named var directory. --- mysql-test/collections/default.push | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/collections/default.push b/mysql-test/collections/default.push index ce7b491efe7..638918d1026 100644 --- a/mysql-test/collections/default.push +++ b/mysql-test/collections/default.push @@ -1,2 +1,2 @@ -perl mysql-test-run.pl --timer --force --vardir=n_stm --comment=n_stm -perl mysql-test-run.pl --timer --force --vardir=ps_stm --comment=ps_stm --ps-protocol +perl mysql-test-run.pl --timer --force --vardir=var-n_stm --comment=n_stm +perl mysql-test-run.pl --timer --force --vardir=var-ps_stm --comment=ps_stm --ps-protocol From e199c7cd17e9d37befd67f907f66e7f235c71d6d Mon Sep 17 00:00:00 2001 From: Christopher Powers Date: Mon, 29 Nov 2010 18:51:46 -0600 Subject: [PATCH 03/12] Bug#35333, "If Federated table can't connect to remote host, can't retrieve metadata" Improved error handling such that queries against Information_Schema.Tables won't fail if a Federated table is unable to connect to remote host. sql/sql_show.cc: If Handler::Info() fails, save error text in TABLE COMMENTS column, clear error. --- mysql-test/r/federated_bug_35333.result | 76 ++++++++ mysql-test/r/information_schema.result | 2 + mysql-test/r/information_schema_db.result | 4 + mysql-test/r/show_check.result | 2 + mysql-test/r/view.result | 2 + mysql-test/t/federated_bug_35333.test | 74 ++++++++ sql/sql_show.cc | 200 ++++++++++++---------- 7 files changed, 273 insertions(+), 87 deletions(-) create mode 100644 mysql-test/r/federated_bug_35333.result create mode 100644 mysql-test/t/federated_bug_35333.test diff --git a/mysql-test/r/federated_bug_35333.result b/mysql-test/r/federated_bug_35333.result new file mode 100644 index 00000000000..964fedc3205 --- /dev/null +++ b/mysql-test/r/federated_bug_35333.result @@ -0,0 +1,76 @@ +# +# Bug 35333 "If a Federated table can't connect to the remote hose, can't retrieve metadata" +# +# Queries such as SHOW TABLE STATUS and SELECT * FROM INFORMATION_SCHEMA.TABLES fail +# when encountering a federated table that cannot connect to its remote table. +# +# The fix is to store the error text in the TABLE COMMENTS column of I_S.TABLES, clear +# the remote connection error and push a warning instead. This allows the SELECT operation +# to complete while still indicating a problem. This fix applies to any non-fatal system +# error that occurs during a query against I_S.TABLES.de +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +stop slave; +DROP DATABASE IF EXISTS federated; +CREATE DATABASE federated; +DROP DATABASE IF EXISTS federated; +CREATE DATABASE federated; +CREATE DATABASE IF NOT EXISTS realdb; +DROP TABLE IF EXISTS realdb.t0; +DROP TABLE IF EXISTS federated.t0; +# +# Create the base table to be referenced +# +CREATE TABLE realdb.t0 (a text, b text) ENGINE=MYISAM; +# +# Create a federated table with a bogus port number +# +CREATE TABLE federated.t0 (a text, b text) ENGINE=FEDERATED +CONNECTION='mysql://root@127.0.0.1:63333/realdb/t0'; +# +# Trigger a federated system error during a INFORMATION_SCHEMA.TABLES query +# +SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT, TABLE_ROWS, DATA_LENGTH, TABLE_COMMENT +FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'realdb' or TABLE_SCHEMA = 'federated'; +TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE ROW_FORMAT TABLE_ROWS DATA_LENGTH TABLE_COMMENT +federated t0 NULL NULL NULL NULL Unable to connect to foreign data source: Can't connect to MySQL server on '127. +realdb t0 BASE TABLE MyISAM Dynamic 0 0 +Warnings: +Warning 1429 Unable to connect to foreign data source: Can't connect to MySQL server on '127.0.0.1' (socket errno) +SHOW WARNINGS; +Level Code Message +Warning 1429 Unable to connect to foreign data source: Can't connect to MySQL server on '127.0.0.1' (socket errno) +# +# Create a MyISAM table then corrupt the file +# +USE realdb; +CREATE TABLE t1 (c1 int) ENGINE=MYISAM; +# +# Corrupt the MyISAM table by deleting the base file +# +# +# Trigger a MyISAM system error during an INFORMATION_SCHEMA.TABLES query +# +SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT, TABLE_ROWS, DATA_LENGTH, TABLE_COMMENT +FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; +TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE ROW_FORMAT TABLE_ROWS DATA_LENGTH TABLE_COMMENT +realdb t1 BASE TABLE NULL NULL NULL NULL Can't find file: 't1' (errno: 2) +Warnings: +Warning 1017 Can't find file: 't1' (errno: 2) +SHOW WARNINGS; +Level Code Message +Warning 1017 Can't find file: 't1' (errno: 2) +# +# Cleanup +# +DROP TABLE IF EXISTS realdb.t0; +DROP TABLE IF EXISTS federated.t0; +DROP DATABASE realdb; +DROP TABLE IF EXISTS federated.t1; +DROP DATABASE IF EXISTS federated; +DROP TABLE IF EXISTS federated.t1; +DROP DATABASE IF EXISTS federated; diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 54fa4be027e..9c3d9e09bad 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -1053,6 +1053,8 @@ select table_type from information_schema.tables where table_name="v1"; table_type VIEW +Warnings: +Warning 1356 View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them drop view v1; create temporary table t1(f1 int, index(f1)); show columns from t1; diff --git a/mysql-test/r/information_schema_db.result b/mysql-test/r/information_schema_db.result index 92c5b6de2ec..552114849c4 100644 --- a/mysql-test/r/information_schema_db.result +++ b/mysql-test/r/information_schema_db.result @@ -65,10 +65,14 @@ select table_name, table_type, table_comment from information_schema.tables where table_schema='inf%' and func2(); table_name table_type table_comment v1 VIEW View 'inf%.v1' references invalid table(s) or column(s) or function(s) or define +Warnings: +Warning 1356 View 'inf%.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them select table_name, table_type, table_comment from information_schema.tables where table_schema='inf%' and func2(); table_name table_type table_comment v1 VIEW View 'inf%.v1' references invalid table(s) or column(s) or function(s) or define +Warnings: +Warning 1356 View 'inf%.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them drop view v1; drop function func1; drop function func2; diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index ad73fc650a5..2d9565ac54d 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -622,6 +622,8 @@ flush tables; SHOW TABLE STATUS like 't1'; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment t1 NULL NULL NULL NULL # # # # NULL NULL NULL NULL NULL NULL NULL NULL Incorrect information in file: './test/t1.frm' +Warnings: +Warning 1033 Incorrect information in file: './test/t1.frm' show create table t1; ERROR HY000: Incorrect information in file: './test/t1.frm' drop table t1; diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 715fb225550..d98ef86b7c3 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -840,6 +840,8 @@ show table status; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment t1 MyISAM 10 Fixed 0 0 0 # 1024 0 NULL # # NULL latin1_swedish_ci NULL v1 NULL NULL NULL NULL NULL NULL # NULL NULL NULL # # NULL NULL NULL NULL View 'test.v1' references invalid table(s) or column(s) or function(s) or define +Warnings: +Warning 1356 View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them drop view v1; drop table t1; create view v1 as select 99999999999999999999999999999999999999999999999999999 as col1; diff --git a/mysql-test/t/federated_bug_35333.test b/mysql-test/t/federated_bug_35333.test new file mode 100644 index 00000000000..e04e719827e --- /dev/null +++ b/mysql-test/t/federated_bug_35333.test @@ -0,0 +1,74 @@ +--echo # +--echo # Bug 35333 "If a Federated table can't connect to the remote hose, can't retrieve metadata" +--echo # +--echo # Queries such as SHOW TABLE STATUS and SELECT * FROM INFORMATION_SCHEMA.TABLES fail +--echo # when encountering a federated table that cannot connect to its remote table. +--echo # +--echo # The fix is to store the error text in the TABLE COMMENTS column of I_S.TABLES, clear +--echo # the remote connection error and push a warning instead. This allows the SELECT operation +--echo # to complete while still indicating a problem. This fix applies to any non-fatal system +--echo # error that occurs during a query against I_S.TABLES.de + +--source include/federated.inc + +--disable_warnings +CREATE DATABASE IF NOT EXISTS realdb; +# Federated database exists +DROP TABLE IF EXISTS realdb.t0; +DROP TABLE IF EXISTS federated.t0; +--enable_warnings + +--echo # +--echo # Create the base table to be referenced +--echo # +CREATE TABLE realdb.t0 (a text, b text) ENGINE=MYISAM; + +--echo # +--echo # Create a federated table with a bogus port number +--echo # +CREATE TABLE federated.t0 (a text, b text) ENGINE=FEDERATED + CONNECTION='mysql://root@127.0.0.1:63333/realdb/t0'; + +#--warning ER_CONNECT_TO_FOREIGN_DATA_SOURCE + +--echo # +--echo # Trigger a federated system error during a INFORMATION_SCHEMA.TABLES query +--echo # +# Remove O/S-specific socket error +--replace_regex /\(.*\)/(socket errno)/ +SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT, TABLE_ROWS, DATA_LENGTH, TABLE_COMMENT + FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'realdb' or TABLE_SCHEMA = 'federated'; + +# Remove O/S-specific socket error +--replace_regex /\(.*\)/(socket errno)/ +SHOW WARNINGS; + +--echo # +--echo # Create a MyISAM table then corrupt the file +--echo # +USE realdb; +CREATE TABLE t1 (c1 int) ENGINE=MYISAM; +--echo # +--echo # Corrupt the MyISAM table by deleting the base file +--echo # +let $MYSQLD_DATADIR= `SELECT @@datadir`; +--remove_file $MYSQLD_DATADIR/realdb/t1.MYD +--remove_file $MYSQLD_DATADIR/realdb/t1.MYI + +--echo # +--echo # Trigger a MyISAM system error during an INFORMATION_SCHEMA.TABLES query +--echo # +SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT, TABLE_ROWS, DATA_LENGTH, TABLE_COMMENT + FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; + +SHOW WARNINGS; +--echo # +--echo # Cleanup +--echo # +--disable_warnings +DROP TABLE IF EXISTS realdb.t0; +DROP TABLE IF EXISTS federated.t0; +DROP DATABASE realdb; +--enable_warnings + +--source include/federated_cleanup.inc diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 13045280c5f..1075e7f76d1 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -48,7 +48,7 @@ bool schema_table_store_record(THD *thd, TABLE *table); /*************************************************************************** -** List all table types supported +** List all table types supported ***************************************************************************/ bool mysqld_show_storage_engines(THD *thd) @@ -65,7 +65,7 @@ bool mysqld_show_storage_engines(THD *thd) Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); - const char *default_type_name= + const char *default_type_name= ha_get_storage_engine((enum db_type)thd->variables.table_type); handlerton **types; @@ -406,7 +406,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) /* Clear all messages with 'error' level status and - issue a warning with 'warning' level status in + issue a warning with 'warning' level status in case of invalid view and last error is ER_VIEW_INVALID */ mysql_reset_errors(thd, true); @@ -603,7 +603,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild) Field **ptr,*field; for (ptr=table->field ; (field= *ptr); ptr++) { - if (!wild || !wild[0] || + if (!wild || !wild[0] || !wild_case_compare(system_charset_info, field->field_name,wild)) { if (table_list->view) @@ -809,13 +809,13 @@ static bool get_field_default_value(THD *thd, TABLE *table, bool has_default; bool has_now_default; enum enum_field_types field_type= field->type(); - /* + /* We are using CURRENT_TIMESTAMP instead of NOW because it is more standard */ - has_now_default= table->timestamp_field == field && + has_now_default= table->timestamp_field == field && field->unireg_check != Field::TIMESTAMP_UN_FIELD; - + has_default= (field_type != FIELD_TYPE_BLOB && !(field->flags & NO_DEFAULT_VALUE_FLAG) && field->unireg_check != Field::NEXT_NUMBER && @@ -837,7 +837,7 @@ static bool get_field_default_value(THD *thd, TABLE *table, char *ptr= longlong2str(dec, tmp + 2, 2); uint32 length= (uint32) (ptr - tmp); tmp[0]= 'b'; - tmp[1]= '\''; + tmp[1]= '\''; tmp[length]= '\''; type.length(length + 1); quoted= 0; @@ -929,7 +929,7 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet) field->sql_type(type); packet->append(type.ptr(), type.length(), system_charset_info); - if (field->has_charset() && + if (field->has_charset() && !(thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))) { if (field->charset() != share->table_charset) @@ -937,8 +937,8 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet) packet->append(STRING_WITH_LEN(" character set ")); packet->append(field->charset()->csname); } - /* - For string types dump collation name only if + /* + For string types dump collation name only if collation is not primary for the given charset */ if (!(field->charset()->state & MY_CS_PRIMARY)) @@ -965,11 +965,11 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet) packet->append(def_value.ptr(), def_value.length(), system_charset_info); } - if (!limited_mysql_mode && table->timestamp_field == field && + if (!limited_mysql_mode && table->timestamp_field == field && field->unireg_check != Field::TIMESTAMP_DN_FIELD) packet->append(STRING_WITH_LEN(" on update CURRENT_TIMESTAMP")); - if (field->unireg_check == Field::NEXT_NUMBER && + if (field->unireg_check == Field::NEXT_NUMBER && !(thd->variables.sql_mode & MODE_NO_FIELD_OPTIONS)) packet->append(STRING_WITH_LEN(" auto_increment")); @@ -1088,7 +1088,7 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet) packet->append(buff, (uint) (end - buff)); } - + if (share->table_charset && !(thd->variables.sql_mode & MODE_MYSQL323) && !(thd->variables.sql_mode & MODE_MYSQL40)) @@ -1179,7 +1179,7 @@ view_store_options(THD *thd, TABLE_LIST *table, String *buff) /* Append DEFINER clause to the given buffer. - + SYNOPSIS append_definer() thd [in] thread handle @@ -1209,7 +1209,7 @@ static void append_algorithm(TABLE_LIST *table, String *buff) /* Append DEFINER clause to the given buffer. - + SYNOPSIS append_definer() thd [in] thread handle @@ -1374,8 +1374,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) "%s:%u", tmp_sctx->host_or_ip, tmp->peer_port); } else - thd_info->host= thd->strdup(tmp_sctx->host_or_ip[0] ? - tmp_sctx->host_or_ip : + thd_info->host= thd->strdup(tmp_sctx->host_or_ip[0] ? + tmp_sctx->host_or_ip : tmp_sctx->host ? tmp_sctx->host : ""); if ((thd_info->db=tmp->db)) // Safe test thd_info->db=thd->strdup(thd_info->db); @@ -1852,14 +1852,14 @@ void calc_sum_of_all_status(STATUS_VAR *to) I_List_iterator it(threads); THD *tmp; - + /* Get global values as base */ *to= global_status_var; - + /* Add to this status from existing threads */ while ((tmp= it++)) add_to_status(to, &tmp->status_var); - + VOID(pthread_mutex_unlock(&LOCK_thread_count)); DBUG_VOID_RETURN; } @@ -1911,7 +1911,7 @@ bool schema_table_store_record(THD *thd, TABLE *table) int error; if ((error= table->file->write_row(table->record[0]))) { - if (create_myisam_from_heap(thd, table, + if (create_myisam_from_heap(thd, table, table->pos_in_table_list->schema_table_param, error, 0)) return 1; @@ -1946,7 +1946,7 @@ int make_table_list(THD *thd, SELECT_LEX *sel, { Table_ident *table_ident; LEX_STRING ident_db, ident_table; - ident_db.str= db; + ident_db.str= db; ident_db.length= (uint) strlen(db); ident_table.str= table; ident_table.length= (uint) strlen(table); @@ -1980,10 +1980,10 @@ bool uses_only_table_name_fields(Item *item, TABLE_LIST *table) const char *field_name2= schema_table->idx_field2 >= 0 ? field_info[schema_table->idx_field2].field_name : ""; if (table->table != item_field->field->table || (cs->coll->strnncollsp(cs, (uchar *) field_name1, (uint) strlen(field_name1), - (uchar *) item_field->field_name, + (uchar *) item_field->field_name, (uint) strlen(item_field->field_name), 0) && cs->coll->strnncollsp(cs, (uchar *) field_name2, (uint) strlen(field_name2), - (uchar *) item_field->field_name, + (uchar *) item_field->field_name, (uint) strlen(item_field->field_name), 0))) return 0; } @@ -2072,7 +2072,7 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table) with_i_schema returns 1 if we added 'IS' name to list otherwise returns 0 is_wild_value if value is 1 then idx_field_vals->db_name is - wild string otherwise it's db name; + wild string otherwise it's db name; RETURN zero success @@ -2094,7 +2094,7 @@ int make_db_list(THD *thd, List *files, LIKE clause (see also get_index_field_values() function) */ if (!idx_field_vals->db_value || - !wild_case_compare(system_charset_info, + !wild_case_compare(system_charset_info, INFORMATION_SCHEMA_NAME.str, idx_field_vals->db_value)) { @@ -2179,7 +2179,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) List bases; List_iterator_fast it(bases); COND *partial_cond; - uint derived_tables= lex->derived_tables; + uint derived_tables= lex->derived_tables; int error= 1; db_type not_used; Open_tables_state open_tables_state_backup; @@ -2217,7 +2217,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) Let us set fake sql_command so views won't try to merge themselves into main statement. If we don't do this, SELECT * from information_schema.xxxx will cause problems. - SQLCOM_SHOW_FIELDS is used because it satisfies 'only_view_structure()' + SQLCOM_SHOW_FIELDS is used because it satisfies 'only_view_structure()' */ lex->sql_command= SQLCOM_SHOW_FIELDS; res= open_normal_and_derived_tables(thd, show_table_list, @@ -2227,15 +2227,15 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) get_all_tables() returns 1 on failure and 0 on success thus return only these and not the result code of ::process_table() - We should use show_table_list->alias instead of + We should use show_table_list->alias instead of show_table_list->table_name because table_name could be changed during opening of I_S tables. It's safe - to use alias because alias contains original table name - in this case(this part of code is used only for + to use alias because alias contains original table name + in this case(this part of code is used only for 'show columns' & 'show statistics' commands). */ error= test(schema_table->process_table(thd, show_table_list, - table, res, + table, res, (show_table_list->view ? show_table_list->view_db.str : show_table_list->db), @@ -2263,7 +2263,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) (base_name= select_lex->db) && !bases.elements)) { #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (!check_access(thd,SELECT_ACL, base_name, + if (!check_access(thd,SELECT_ACL, base_name, &thd->col_access, 0, 1, with_i_schema) || sctx->master_access & (DB_ACLS | SHOW_DB_ACL) || acl_get(sctx->host, sctx->ip, sctx->priv_user, base_name,0) || @@ -2281,7 +2281,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) strxmov(path, mysql_data_home, "/", base_name, NullS); end= path + (len= unpack_dirname(path,path)); len= FN_LEN - len; - find_files_result res= find_files(thd, &files, base_name, + find_files_result res= find_files(thd, &files, base_name, path, idx_field_vals.table_value, 0); if (res != FIND_FILES_OK) { @@ -2367,9 +2367,9 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) res= open_normal_and_derived_tables(thd, show_table_list, MYSQL_LOCK_IGNORE_FLUSH); lex->sql_command= save_sql_command; - /* + /* They can drop table after table names list creation and - before table opening. We open non existing table and + before table opening. We open non existing table and get ER_NO_SUCH_TABLE error. In this case we do not store the record into I_S table and clear error. */ @@ -2381,10 +2381,10 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) else { /* - We should use show_table_list->alias instead of + We should use show_table_list->alias instead of show_table_list->table_name because table_name could be changed during opening of I_S tables. It's safe - to use alias because alias contains original table name + to use alias because alias contains original table name in this case. */ res= schema_table->process_table(thd, show_table_list, table, @@ -2486,28 +2486,28 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, { const char *tmp_buff; MYSQL_TIME time; + int info_error= 0; CHARSET_INFO *cs= system_charset_info; DBUG_ENTER("get_schema_tables_record"); restore_record(table, s->default_values); table->field[1]->store(base_name, (uint) strlen(base_name), cs); table->field[2]->store(file_name, (uint) strlen(file_name), cs); + if (res) { - /* - there was errors during opening tables - */ - const char *error= thd->net.last_error; + /* There was a table open error, so set the table type and return */ if (tables->view) table->field[3]->store(STRING_WITH_LEN("VIEW"), cs); else if (tables->schema_table) table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs); else table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs); - table->field[20]->store(error, (uint) strlen(error), cs); - thd->clear_error(); + + goto err; } - else if (tables->view) + + if (tables->view) { table->field[3]->store(STRING_WITH_LEN("VIEW"), cs); table->field[20]->store(STRING_WITH_LEN("VIEW"), cs); @@ -2518,8 +2518,15 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, TABLE_SHARE *share= show_table->s; handler *file= show_table->file; - file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_AUTO | - HA_STATUS_NO_LOCK); + if (!file) + goto err; + + if ((info_error= file->info(HA_STATUS_VARIABLE | + HA_STATUS_TIME | + HA_STATUS_AUTO | + HA_STATUS_NO_LOCK)) != 0) + goto err; + if (share->tmp_table == SYSTEM_TMP_TABLE) table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs); else if (share->tmp_table) @@ -2636,7 +2643,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE) ptr=strmov(ptr," delay_key_write=1"); if (share->row_type != ROW_TYPE_DEFAULT) - ptr=strxmov(ptr, " row_format=", + ptr=strxmov(ptr, " row_format=", ha_row_type[(uint) share->row_type], NullS); if (file->raid_type) @@ -2649,7 +2656,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, ptr=strmov(ptr,buff); } table->field[19]->store(option_buff+1, - (ptr == option_buff ? 0 : + (ptr == option_buff ? 0 : (uint) (ptr-option_buff)-1), cs); { char *comment; @@ -2658,13 +2665,32 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, { table->field[20]->store(comment, (comment == share->comment.str ? - share->comment.length : + share->comment.length : (uint) strlen(comment)), cs); if (comment != share->comment.str) my_free(comment, MYF(0)); } } } + +err: + if (res || info_error) + { + /* + If an error was encountered, push a warning, set the TABLE COMMENT + column with the error text, and clear the error so that the operation + can continue. + */ + const char *error= thd->net.last_error; + if (error) + { + table->field[20]->store(error, strlen(error), cs); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + thd->net.last_errno, thd->net.last_error); + thd->clear_error(); + } + } + DBUG_RETURN(schema_table_store_record(thd, table)); } @@ -2691,7 +2717,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, /* I.e. we are in SELECT FROM INFORMATION_SCHEMA.COLUMS rather than in SHOW COLUMNS - */ + */ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, thd->net.last_errno, thd->net.last_error); thd->clear_error(); @@ -2732,7 +2758,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, uint col_access; check_access(thd,SELECT_ACL | EXTRA_ACL, base_name, &tables->grant.privilege, 0, 0, test(tables->schema_table)); - col_access= get_column_grant(thd, &tables->grant, + col_access= get_column_grant(thd, &tables->grant, base_name, file_name, field->field_name) & COL_ACLS; if (!tables->schema_table && !col_access) @@ -2755,9 +2781,9 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, cs); table->field[4]->store((longlong) count, TRUE); field->sql_type(type); - table->field[14]->store(type.ptr(), type.length(), cs); + table->field[14]->store(type.ptr(), type.length(), cs); tmp_buff= strchr(type.ptr(), '('); - table->field[7]->store(type.ptr(), (uint) + table->field[7]->store(type.ptr(), (uint) (tmp_buff ? tmp_buff - type.ptr() : type.length()), cs); @@ -2778,7 +2804,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, uint32 octet_max_length= field->max_display_length(); if (is_blob && octet_max_length != (uint32) 4294967295U) octet_max_length /= field->charset()->mbmaxlen; - longlong char_max_len= is_blob ? + longlong char_max_len= is_blob ? (longlong) octet_max_length / field->charset()->mbminlen : (longlong) octet_max_length / field->charset()->mbmaxlen; table->field[8]->store(char_max_len, TRUE); @@ -2811,7 +2837,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, field_length= field->max_display_length(); decimals= -1; // return NULL break; - case FIELD_TYPE_FLOAT: + case FIELD_TYPE_FLOAT: case FIELD_TYPE_DOUBLE: field_length= field->field_length; if (decimals == NOT_FIXED_DEC) @@ -2874,7 +2900,7 @@ int fill_schema_charsets(THD *thd, TABLE_LIST *tables, COND *cond) for (cs= all_charsets ; cs < all_charsets+255 ; cs++) { CHARSET_INFO *tmp_cs= cs[0]; - if (tmp_cs && (tmp_cs->state & MY_CS_PRIMARY) && + if (tmp_cs && (tmp_cs->state & MY_CS_PRIMARY) && (tmp_cs->state & MY_CS_AVAILABLE) && !(wild && wild[0] && wild_case_compare(scs, tmp_cs->csname,wild))) @@ -2904,13 +2930,13 @@ int fill_schema_collation(THD *thd, TABLE_LIST *tables, COND *cond) { CHARSET_INFO **cl; CHARSET_INFO *tmp_cs= cs[0]; - if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) || + if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) || !(tmp_cs->state & MY_CS_PRIMARY)) continue; for (cl= all_charsets; cl < all_charsets+255 ;cl ++) { CHARSET_INFO *tmp_cl= cl[0]; - if (!tmp_cl || !(tmp_cl->state & MY_CS_AVAILABLE) || + if (!tmp_cl || !(tmp_cl->state & MY_CS_AVAILABLE) || !my_charset_same(tmp_cs, tmp_cl)) continue; if (!(wild && wild[0] && @@ -2944,13 +2970,13 @@ int fill_schema_coll_charset_app(THD *thd, TABLE_LIST *tables, COND *cond) { CHARSET_INFO **cl; CHARSET_INFO *tmp_cs= cs[0]; - if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) || + if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) || !(tmp_cs->state & MY_CS_PRIMARY)) continue; for (cl= all_charsets; cl < all_charsets+255 ;cl ++) { CHARSET_INFO *tmp_cl= cl[0]; - if (!tmp_cl || !(tmp_cl->state & MY_CS_AVAILABLE) || + if (!tmp_cl || !(tmp_cl->state & MY_CS_AVAILABLE) || !my_charset_same(tmp_cs,tmp_cl)) continue; restore_record(table, s->default_values); @@ -3014,7 +3040,7 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, table->field[10]->store(STRING_WITH_LEN("SQL"), cs); get_field(thd->mem_root, proc_table->field[6], &tmp_string); table->field[11]->store(tmp_string.ptr(), tmp_string.length(), cs); - table->field[12]->store(sp_data_access_name[enum_idx].str, + table->field[12]->store(sp_data_access_name[enum_idx].str, sp_data_access_name[enum_idx].length , cs); get_field(thd->mem_root, proc_table->field[7], &tmp_string); table->field[14]->store(tmp_string.ptr(), tmp_string.length(), cs); @@ -3290,10 +3316,10 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables, if (schema_table_store_record(thd, table)) DBUG_RETURN(1); if (res) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, thd->net.last_errno, thd->net.last_error); } - if (res) + if (res) thd->clear_error(); DBUG_RETURN(0); } @@ -3334,7 +3360,7 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables, TABLE *show_table= tables->table; KEY *key_info=show_table->key_info; uint primary_key= show_table->s->primary_key; - show_table->file->info(HA_STATUS_VARIABLE | + show_table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK | HA_STATUS_TIME); for (uint i=0 ; i < show_table->s->keys ; i++, key_info++) @@ -3363,7 +3389,7 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables, List_iterator_fast it(f_key_list); while ((f_key_info=it++)) { - if (store_constraints(thd, table, base_name, file_name, + if (store_constraints(thd, table, base_name, file_name, f_key_info->forein_id->str, (uint) strlen(f_key_info->forein_id->str), "FOREIGN KEY", 11)) @@ -3472,7 +3498,7 @@ ret: void store_key_column_usage(TABLE *table, const char*db, const char *tname, - const char *key_name, uint key_len, + const char *key_name, uint key_len, const char *con_type, uint con_len, longlong idx) { CHARSET_INFO *cs= system_charset_info; @@ -3506,7 +3532,7 @@ static int get_schema_key_column_usage_record(THD *thd, TABLE *show_table= tables->table; KEY *key_info=show_table->key_info; uint primary_key= show_table->s->primary_key; - show_table->file->info(HA_STATUS_VARIABLE | + show_table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK | HA_STATUS_TIME); for (uint i=0 ; i < show_table->s->keys ; i++, key_info++) @@ -3523,8 +3549,8 @@ static int get_schema_key_column_usage_record(THD *thd, restore_record(table, s->default_values); store_key_column_usage(table, base_name, file_name, key_info->name, - (uint) strlen(key_info->name), - key_part->field->field_name, + (uint) strlen(key_info->name), + key_part->field->field_name, (uint) strlen(key_part->field->field_name), (longlong) f_idx); if (schema_table_store_record(thd, table)) @@ -3560,7 +3586,7 @@ static int get_schema_key_column_usage_record(THD *thd, system_charset_info); table->field[9]->set_notnull(); table->field[10]->store(f_key_info->referenced_table->str, - f_key_info->referenced_table->length, + f_key_info->referenced_table->length, system_charset_info); table->field[10]->set_notnull(); table->field[11]->store(r_info->str, r_info->length, @@ -3607,7 +3633,7 @@ int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond) LEX *lex= thd->lex; const char *wild= lex->wild ? lex->wild->ptr() : NullS; pthread_mutex_lock(&LOCK_global_system_variables); - res= show_status_array(thd, wild, init_vars, + res= show_status_array(thd, wild, init_vars, lex->option_type, 0, "", tables->table); pthread_mutex_unlock(&LOCK_global_system_variables); DBUG_RETURN(res); @@ -3626,7 +3652,7 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond) if (lex->option_type == OPT_GLOBAL) calc_sum_of_all_status(&tmp); res= show_status_array(thd, wild, status_vars, OPT_GLOBAL, - (lex->option_type == OPT_GLOBAL ? + (lex->option_type == OPT_GLOBAL ? &tmp: &thd->status_var), "",tables->table); pthread_mutex_unlock(&LOCK_status); DBUG_RETURN(res); @@ -3717,7 +3743,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) break; case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: - if ((item= new Item_float(fields_info->field_name, 0.0, NOT_FIXED_DEC, + if ((item= new Item_float(fields_info->field_name, 0.0, NOT_FIXED_DEC, fields_info->field_length)) == NULL) DBUG_RETURN(NULL); break; @@ -3761,7 +3787,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) tmp_table_param->schema_table= 1; SELECT_LEX *select_lex= thd->lex->current_select; if (!(table= create_tmp_table(thd, tmp_table_param, - field_list, (ORDER*) 0, 0, 0, + field_list, (ORDER*) 0, 0, 0, (select_lex->options | thd->options | TMP_TABLE_ALL_COLUMNS), HA_POS_ERROR, table_list->alias))) @@ -4102,7 +4128,7 @@ bool get_schema_tables_result(JOIN *join, thd->no_warnings_for_error= 1; for (JOIN_TAB *tab= join->join_tab; tab < tmp_join_tab; tab++) - { + { if (!tab->table || !tab->table->pos_in_table_list) break; @@ -4454,13 +4480,13 @@ ST_FIELD_INFO variables_fields_info[]= ST_SCHEMA_TABLE schema_tables[]= { - {"CHARACTER_SETS", charsets_fields_info, create_schema_table, + {"CHARACTER_SETS", charsets_fields_info, create_schema_table, fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0}, - {"COLLATIONS", collation_fields_info, create_schema_table, + {"COLLATIONS", collation_fields_info, create_schema_table, fill_schema_collation, make_old_format, 0, -1, -1, 0}, {"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info, create_schema_table, fill_schema_coll_charset_app, 0, 0, -1, -1, 0}, - {"COLUMNS", columns_fields_info, create_schema_table, + {"COLUMNS", columns_fields_info, create_schema_table, get_all_tables, make_columns_old_format, get_schema_column_record, 1, 2, 0}, {"COLUMN_PRIVILEGES", column_privileges_fields_info, create_schema_table, fill_schema_column_privileges, 0, 0, -1, -1, 0}, @@ -4469,19 +4495,19 @@ ST_SCHEMA_TABLE schema_tables[]= {"OPEN_TABLES", open_tables_fields_info, create_schema_table, fill_open_tables, make_old_format, 0, -1, -1, 1}, {"PROFILING", query_profile_statistics_info, create_schema_table, - fill_query_profile_statistics_info, make_profile_table_for_show, + fill_query_profile_statistics_info, make_profile_table_for_show, NULL, -1, -1, false}, - {"ROUTINES", proc_fields_info, create_schema_table, + {"ROUTINES", proc_fields_info, create_schema_table, fill_schema_proc, make_proc_old_format, 0, -1, -1, 0}, {"SCHEMATA", schema_fields_info, create_schema_table, fill_schema_shemata, make_schemata_old_format, 0, 1, -1, 0}, {"SCHEMA_PRIVILEGES", schema_privileges_fields_info, create_schema_table, fill_schema_schema_privileges, 0, 0, -1, -1, 0}, - {"STATISTICS", stat_fields_info, create_schema_table, + {"STATISTICS", stat_fields_info, create_schema_table, get_all_tables, make_old_format, get_schema_stat_record, 1, 2, 0}, - {"STATUS", variables_fields_info, create_schema_table, fill_status, + {"STATUS", variables_fields_info, create_schema_table, fill_status, make_old_format, 0, -1, -1, 1}, - {"TABLES", tables_fields_info, create_schema_table, + {"TABLES", tables_fields_info, create_schema_table, get_all_tables, make_old_format, get_schema_tables_record, 1, 2, 0}, {"TABLE_CONSTRAINTS", table_constraints_fields_info, create_schema_table, get_all_tables, 0, get_schema_constraints_record, 3, 4, 0}, @@ -4491,11 +4517,11 @@ ST_SCHEMA_TABLE schema_tables[]= fill_schema_table_privileges, 0, 0, -1, -1, 0}, {"TRIGGERS", triggers_fields_info, create_schema_table, get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0}, - {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table, + {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table, fill_schema_user_privileges, 0, 0, -1, -1, 0}, {"VARIABLES", variables_fields_info, create_schema_table, fill_variables, make_old_format, 0, -1, -1, 1}, - {"VIEWS", view_fields_info, create_schema_table, + {"VIEWS", view_fields_info, create_schema_table, get_all_tables, 0, get_schema_views_record, 1, 2, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0} }; From 4edea18f8daa9b1c1e5614560753a03cfdcb1adb Mon Sep 17 00:00:00 2001 From: Christopher Powers Date: Mon, 29 Nov 2010 22:46:43 -0600 Subject: [PATCH 04/12] Bug#35333, "If Federated table can't connect to remote host, can't retrieve metadata" Improved error handling such that queries against Information_Schema.Tables won't fail if a federated table can't make a remote connection. mysql-test/r/merge.result: Updated with warnings that were previously masked. mysql-test/r/show_check.result: Updated with warnings that were previously masked. mysql-test/r/view.result: Updated with warnings that were previously masked. sql/sql_show.cc: If get_schema_tables_record() encounters an error, push a warning, set the TABLE COMMENT column with the error text, and clear the error so that the operation can continue. --- mysql-test/r/merge.result | 2 + mysql-test/r/show_check.result | 16 ++-- mysql-test/r/view.result | 2 + .../federated/federated_bug_35333.result | 67 +++++++++++++++++ .../suite/federated/federated_bug_35333.test | 74 +++++++++++++++++++ sql/sql_show.cc | 43 ++++++++--- 6 files changed, 188 insertions(+), 16 deletions(-) create mode 100644 mysql-test/suite/federated/federated_bug_35333.result create mode 100644 mysql-test/suite/federated/federated_bug_35333.test diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 1fb074d38bf..3af152672ab 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -2024,6 +2024,8 @@ SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'test' and TABLE_NAME='tm1'; TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE VERSION ROW_FORMAT TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE AUTO_INCREMENT CREATE_TIME UPDATE_TIME CHECK_TIME TABLE_COLLATION CHECKSUM CREATE_OPTIONS TABLE_COMMENT NULL test tm1 BASE TABLE NULL NULL NULL # # # # # # # # # # NULL # # Unable to open underlying table which is differently defined or of non-MyISAM ty +Warnings: +Warning 1168 Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist DROP TABLE tm1; CREATE TABLE t1(C1 INT, C2 INT, KEY C1(C1), KEY C2(C2)) ENGINE=MYISAM; CREATE TABLE t2(C1 INT, C2 INT, KEY C1(C1), KEY C2(C2)) ENGINE=MYISAM; diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index 08b9211bd31..5f444759346 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -660,6 +660,8 @@ flush tables; SHOW TABLE STATUS like 't1'; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment t1 NULL NULL NULL NULL # # # # NULL NULL NULL NULL NULL NULL NULL NULL Incorrect information in file: './test/t1.frm' +Warnings: +Warning 1033 Incorrect information in file: './test/t1.frm' show create table t1; ERROR HY000: Incorrect information in file: './test/t1.frm' drop table if exists t1; @@ -1196,7 +1198,7 @@ set names koi8r; DROP DATABASE IF EXISTS mysqltest1; CREATE DATABASE mysqltest1; use mysqltest1; -CREATE TABLE t1(ËÏÌÏÎËÁ1 INT); +CREATE TABLE t1(�������1 INT); ---> Dumping mysqltest1 to outfile1 @@ -1208,7 +1210,7 @@ DROP DATABASE mysqltest1; SHOW CREATE TABLE mysqltest1.t1; Table Create Table t1 CREATE TABLE `t1` ( - `ËÏÌÏÎËÁ1` int(11) DEFAULT NULL + `�������1` int(11) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP DATABASE mysqltest1; use test; @@ -1415,14 +1417,14 @@ DROP PROCEDURE IF EXISTS p1; DROP FUNCTION IF EXISTS f1; DROP TABLE IF EXISTS t1; DROP EVENT IF EXISTS ev1; -CREATE VIEW v1 AS SELECT 'ÔÅÓÔ' AS test; -CREATE PROCEDURE p1() SELECT 'ÔÅÓÔ' AS test; -CREATE FUNCTION f1() RETURNS CHAR(10) RETURN 'ÔÅÓÔ'; +CREATE VIEW v1 AS SELECT '����' AS test; +CREATE PROCEDURE p1() SELECT '����' AS test; +CREATE FUNCTION f1() RETURNS CHAR(10) RETURN '����'; CREATE TABLE t1(c1 CHAR(10)); CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW -SET NEW.c1 = 'ÔÅÓÔ'; -CREATE EVENT ev1 ON SCHEDULE AT '2030-01-01 00:00:00' DO SELECT 'ÔÅÓÔ' AS test; +SET NEW.c1 = '����'; +CREATE EVENT ev1 ON SCHEDULE AT '2030-01-01 00:00:00' DO SELECT '����' AS test; set names utf8; SHOW CREATE VIEW v1; View Create View character_set_client collation_connection diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 0aec44b70f1..6dee239e21e 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -840,6 +840,8 @@ show table status; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment t1 MyISAM 10 Fixed 0 0 0 # 1024 0 NULL # # NULL latin1_swedish_ci NULL v1 NULL NULL NULL NULL NULL NULL # NULL NULL NULL # # NULL NULL NULL NULL View 'test.v1' references invalid table(s) or column(s) or function(s) or define +Warnings: +Warning 1356 View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them drop view v1; drop table t1; create view v1 as select 99999999999999999999999999999999999999999999999999999 as col1; diff --git a/mysql-test/suite/federated/federated_bug_35333.result b/mysql-test/suite/federated/federated_bug_35333.result new file mode 100644 index 00000000000..7ea1b374712 --- /dev/null +++ b/mysql-test/suite/federated/federated_bug_35333.result @@ -0,0 +1,67 @@ +# +# Bug 35333 "If a Federated table can't connect to the remote hose, can't retrieve metadata" +# +# Queries such as SHOW TABLE STATUS and SELECT * FROM INFORMATION_SCHEMA.TABLES fail +# when encountering a federated table that cannot connect to its remote table. +# +# The fix is to store the error text in the TABLE COMMENTS column of I_S.TABLES, clear +# the remote connection error and push a warning instead. This allows the SELECT operation +# to complete while still indicating a problem. This fix applies to any non-fatal system +# error that occurs during a query against I_S.TABLES.de +CREATE DATABASE federated; +CREATE DATABASE federated; +CREATE DATABASE IF NOT EXISTS realdb; +DROP TABLE IF EXISTS realdb.t0; +DROP TABLE IF EXISTS federated.t0; +# +# Create the base table to be referenced +# +CREATE TABLE realdb.t0 (a text, b text) ENGINE=MYISAM; +# +# Create a federated table with a bogus port number +# +CREATE TABLE federated.t0 (a text, b text) ENGINE=FEDERATED +CONNECTION='mysql://root@127.0.0.1:63333/realdb/t0'; +# +# Trigger a federated system error during a INFORMATION_SCHEMA.TABLES query +# +SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT, TABLE_ROWS, DATA_LENGTH, TABLE_COMMENT +FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'realdb' or TABLE_SCHEMA = 'federated'; +TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE ROW_FORMAT TABLE_ROWS DATA_LENGTH TABLE_COMMENT +federated t0 BASE TABLE FEDERATED NULL 0 Unable to connect to foreign data source: Can't connect to MySQL server on '127. +realdb t0 BASE TABLE MyISAM Dynamic 0 0 +Warnings: +Warning 1429 Unable to connect to foreign data source: Can't connect to MySQL server on '127.0.0.1' (socket errno) +SHOW WARNINGS; +Level Code Message +Warning 1429 Unable to connect to foreign data source: Can't connect to MySQL server on '127.0.0.1' (socket errno) +# +# Create a MyISAM table then corrupt the file +# +USE realdb; +CREATE TABLE t1 (c1 int) ENGINE=MYISAM; +# +# Corrupt the MyISAM table by deleting the base file +# +# +# Trigger a MyISAM system error during an INFORMATION_SCHEMA.TABLES query +# +SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT, TABLE_ROWS, DATA_LENGTH, TABLE_COMMENT +FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; +TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE ROW_FORMAT TABLE_ROWS DATA_LENGTH TABLE_COMMENT +realdb t1 BASE TABLE NULL NULL NULL NULL Can't find file: 't1' (errno: 2) +Warnings: +Warning 1017 Can't find file: 't1' (errno: 2) +SHOW WARNINGS; +Level Code Message +Warning 1017 Can't find file: 't1' (errno: 2) +# +# Cleanup +# +DROP TABLE IF EXISTS realdb.t0; +DROP TABLE IF EXISTS federated.t0; +DROP DATABASE realdb; +DROP TABLE IF EXISTS federated.t1; +DROP DATABASE federated; +DROP TABLE IF EXISTS federated.t1; +DROP DATABASE federated; diff --git a/mysql-test/suite/federated/federated_bug_35333.test b/mysql-test/suite/federated/federated_bug_35333.test new file mode 100644 index 00000000000..58c217d24f2 --- /dev/null +++ b/mysql-test/suite/federated/federated_bug_35333.test @@ -0,0 +1,74 @@ +--echo # +--echo # Bug 35333 "If a Federated table can't connect to the remote hose, can't retrieve metadata" +--echo # +--echo # Queries such as SHOW TABLE STATUS and SELECT * FROM INFORMATION_SCHEMA.TABLES fail +--echo # when encountering a federated table that cannot connect to its remote table. +--echo # +--echo # The fix is to store the error text in the TABLE COMMENTS column of I_S.TABLES, clear +--echo # the remote connection error and push a warning instead. This allows the SELECT operation +--echo # to complete while still indicating a problem. This fix applies to any non-fatal system +--echo # error that occurs during a query against I_S.TABLES.de + +--source federated.inc + +--disable_warnings +CREATE DATABASE IF NOT EXISTS realdb; +# Federated database exists +DROP TABLE IF EXISTS realdb.t0; +DROP TABLE IF EXISTS federated.t0; +--enable_warnings + +--echo # +--echo # Create the base table to be referenced +--echo # +CREATE TABLE realdb.t0 (a text, b text) ENGINE=MYISAM; + +--echo # +--echo # Create a federated table with a bogus port number +--echo # +CREATE TABLE federated.t0 (a text, b text) ENGINE=FEDERATED + CONNECTION='mysql://root@127.0.0.1:63333/realdb/t0'; + +#--warning ER_CONNECT_TO_FOREIGN_DATA_SOURCE + +--echo # +--echo # Trigger a federated system error during a INFORMATION_SCHEMA.TABLES query +--echo # +# Remove O/S-specific socket error +--replace_regex /\(.*\)/(socket errno)/ +SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT, TABLE_ROWS, DATA_LENGTH, TABLE_COMMENT + FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'realdb' or TABLE_SCHEMA = 'federated'; + +# Remove O/S-specific socket error +--replace_regex /\(.*\)/(socket errno)/ +SHOW WARNINGS; + +--echo # +--echo # Create a MyISAM table then corrupt the file +--echo # +USE realdb; +CREATE TABLE t1 (c1 int) ENGINE=MYISAM; +--echo # +--echo # Corrupt the MyISAM table by deleting the base file +--echo # +let $MYSQLD_DATADIR= `SELECT @@datadir`; +--remove_file $MYSQLD_DATADIR/realdb/t1.MYD +--remove_file $MYSQLD_DATADIR/realdb/t1.MYI + +--echo # +--echo # Trigger a MyISAM system error during an INFORMATION_SCHEMA.TABLES query +--echo # +SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT, TABLE_ROWS, DATA_LENGTH, TABLE_COMMENT + FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; + +SHOW WARNINGS; +--echo # +--echo # Cleanup +--echo # +--disable_warnings +DROP TABLE IF EXISTS realdb.t0; +DROP TABLE IF EXISTS federated.t0; +DROP DATABASE realdb; +--enable_warnings + +--source federated_cleanup.inc diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 9b344204d64..ee0e2bf5ce7 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3636,28 +3636,28 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, { const char *tmp_buff; MYSQL_TIME time; + int info_error= 0; CHARSET_INFO *cs= system_charset_info; DBUG_ENTER("get_schema_tables_record"); restore_record(table, s->default_values); table->field[1]->store(db_name->str, db_name->length, cs); table->field[2]->store(table_name->str, table_name->length, cs); + if (res) { - /* - there was errors during opening tables - */ - const char *error= thd->is_error() ? thd->main_da.message() : ""; + /* There was a table open error, so set the table type and return */ if (tables->view) table->field[3]->store(STRING_WITH_LEN("VIEW"), cs); else if (tables->schema_table) table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs); else table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs); - table->field[20]->store(error, strlen(error), cs); - thd->clear_error(); + + goto err; } - else if (tables->view) + + if (tables->view) { table->field[3]->store(STRING_WITH_LEN("VIEW"), cs); table->field[20]->store(STRING_WITH_LEN("VIEW"), cs); @@ -3746,9 +3746,14 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, if (share->comment.str) table->field[20]->store(share->comment.str, share->comment.length, cs); - if(file) + if (file) { - file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_AUTO); + /* If info() fails, then there's nothing else to do */ + if ((info_error= file->info(HA_STATUS_VARIABLE | + HA_STATUS_TIME | + HA_STATUS_AUTO)) != 0) + goto err; + enum row_type row_type = file->get_row_type(); switch (row_type) { case ROW_TYPE_NOT_USED: @@ -3826,6 +3831,26 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, } } } + +err: + if (res || info_error) + { + /* + If an error was encountered, push a warning, set the TABLE COMMENT + column with the error text, and clear the error so that the operation + can continue. + */ + const char *error= thd->is_error() ? thd->main_da.message() : ""; + table->field[20]->store(error, strlen(error), cs); + + if (thd->is_error()) + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + thd->main_da.sql_errno(), thd->main_da.message()); + thd->clear_error(); + } + } + DBUG_RETURN(schema_table_store_record(thd, table)); } From bc0f5b24cf6552dbfd59644504179c0d89378ab4 Mon Sep 17 00:00:00 2001 From: Christopher Powers Date: Tue, 30 Nov 2010 09:43:50 -0600 Subject: [PATCH 05/12] Bug#35333, "If Federated table can't connect to remote host, can't retrieve metadata" mysql-test/r/show_check.result: Fixed corrupted result file --- mysql-test/r/show_check.result | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index 5f444759346..1aa3d62fc70 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -1198,7 +1198,7 @@ set names koi8r; DROP DATABASE IF EXISTS mysqltest1; CREATE DATABASE mysqltest1; use mysqltest1; -CREATE TABLE t1(�������1 INT); +CREATE TABLE t1(ËÏÌÏÎËÁ1 INT); ---> Dumping mysqltest1 to outfile1 @@ -1210,7 +1210,7 @@ DROP DATABASE mysqltest1; SHOW CREATE TABLE mysqltest1.t1; Table Create Table t1 CREATE TABLE `t1` ( - `�������1` int(11) DEFAULT NULL + `ËÏÌÏÎËÁ1` int(11) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP DATABASE mysqltest1; use test; @@ -1417,14 +1417,14 @@ DROP PROCEDURE IF EXISTS p1; DROP FUNCTION IF EXISTS f1; DROP TABLE IF EXISTS t1; DROP EVENT IF EXISTS ev1; -CREATE VIEW v1 AS SELECT '����' AS test; -CREATE PROCEDURE p1() SELECT '����' AS test; -CREATE FUNCTION f1() RETURNS CHAR(10) RETURN '����'; +CREATE VIEW v1 AS SELECT 'ÔÅÓÔ' AS test; +CREATE PROCEDURE p1() SELECT 'ÔÅÓÔ' AS test; +CREATE FUNCTION f1() RETURNS CHAR(10) RETURN 'ÔÅÓÔ'; CREATE TABLE t1(c1 CHAR(10)); CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW -SET NEW.c1 = '����'; -CREATE EVENT ev1 ON SCHEDULE AT '2030-01-01 00:00:00' DO SELECT '����' AS test; +SET NEW.c1 = 'ÔÅÓÔ'; +CREATE EVENT ev1 ON SCHEDULE AT '2030-01-01 00:00:00' DO SELECT 'ÔÅÓÔ' AS test; set names utf8; SHOW CREATE VIEW v1; View Create View character_set_client collation_connection From b0094227347bd31057c3f4c90283bb04174baa19 Mon Sep 17 00:00:00 2001 From: Luis Soares Date: Tue, 30 Nov 2010 16:55:28 +0000 Subject: [PATCH 06/12] BUG#57288: binlog_tmp_table fails sporadically: "Failed to write the DROP statement ..." Problem: When using temporary tables and closing a session, an implicit DROP TEMPORARY TABLE IF EXISTS is written to the binary log (while cleaning up the context of the session THD - see: sql_class.cc:THD::cleanup which calls close_temporary_tables). close_temporary_tables, first checks if the binary log is opened and then proceeds to creating the DROP statements. Then, such statements, are written to the binary log through MYSQL_BIN_LOG::write(Log_event *). Inside, there is another check if the binary log is opened and if not an error is returned. This is where the faulty behavior is triggered. Given that the test case replays a binary log, with temp tables statements, and right after it issues RESET MASTER, there is a chance that is_open will report false (when the mysql session is closed and the temporary tables are written). is_open may return false, because MYSQL_BIN_LOG::reset_logs is not setting the correct flag (LOG_CLOSE_TO_BE_OPENED), on the MYSQL_LOG_BIN::log_state (instead it sets just the LOG_CLOSE_INDEX flag, leaving the log_state to LOG_CLOSED). Thence, when writing the DROP statement as part of the THD::cleanup, the thread could get a return value of false for is_open - inside MYSQL_BIN_LOG::write, ultimately reporting that it can't write the event to the binary log. Fix: We fix this by adding the correct flag, missing in the second close. --- sql/log.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/log.cc b/sql/log.cc index 38f4677f06f..cff69b4cf51 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -3037,7 +3037,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd) } /* Start logging with a new file */ - close(LOG_CLOSE_INDEX); + close(LOG_CLOSE_INDEX | LOG_CLOSE_TO_BE_OPENED); if ((error= my_delete_allow_opened(index_file_name, MYF(0)))) // Reset (open will update) { if (my_errno == ENOENT) From d5e928959e55b4c124a783b83c2c70069dbedbca Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Fri, 26 Nov 2010 19:59:10 -0200 Subject: [PATCH 07/12] Bug#51817: incorrect assumption: thd->query at 0x2ab2a8360360 is an invalid pointer The problem is that the logic which checks if a pointer is valid relies on a poor heuristic based on the start and end addresses of the data segment and heap. Apart from miscalculating the heap bounds, this approach also suffers from the fact that memory can come from places other than the heap. See Bug#58528 for a more detailed explanation. On Linux, the solution is to access the process's memory through /proc/self/task//mem, which allows for retrieving the contents of pages within the virtual address space of the calling process. If a address range is not mapped, a input/output error is returned. client/mysqltest.cc: Use new interface to my_safe_print_str. include/my_stacktrace.h: Drop name from my_safe_print_str. mysys/stacktrace.c: Access the process's memory through a file descriptor and dump the contents of the memory range. The file descriptor offset is equivalent to a offset into the address space. Do not print the name of the variable associated with the address. It can be better accomplished at a higher level. sql/mysqld.cc: Put the variable dumping information within its own newline block. Use symbolic names which better convey information to the user. --- client/mysqltest.cc | 11 +++-- include/my_stacktrace.h | 2 +- mysys/stacktrace.c | 103 +++++++++++++++++++++++++++++++++++++--- sql/mysqld.cc | 14 +++--- 4 files changed, 112 insertions(+), 18 deletions(-) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 4e03ad27246..e0575a1d638 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -7782,13 +7782,16 @@ static void dump_backtrace(void) { struct st_connection *conn= cur_con; - my_safe_print_str("read_command_buf", read_command_buf, - sizeof(read_command_buf)); + fprintf(stderr, "read_command_buf (%p): ", read_command_buf); + my_safe_print_str(read_command_buf, sizeof(read_command_buf)); + if (conn) { - my_safe_print_str("conn->name", conn->name, conn->name_len); + fprintf(stderr, "conn->name (%p): ", conn->name); + my_safe_print_str(conn->name, conn->name_len); #ifdef EMBEDDED_LIBRARY - my_safe_print_str("conn->cur_query", conn->cur_query, conn->cur_query_len); + fprintf(stderr, "conn->cur_query (%p): ", conn->cur_query); + my_safe_print_str(conn->cur_query, conn->cur_query_len); #endif } fputs("Attempting backtrace...\n", stderr); diff --git a/include/my_stacktrace.h b/include/my_stacktrace.h index 9250fd4579e..b64d5d798a5 100644 --- a/include/my_stacktrace.h +++ b/include/my_stacktrace.h @@ -47,7 +47,7 @@ C_MODE_START #if defined(HAVE_STACKTRACE) || defined(HAVE_BACKTRACE) void my_init_stacktrace(); void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack); -void my_safe_print_str(const char* name, const char* val, int max_len); +void my_safe_print_str(const char* val, int max_len); void my_write_core(int sig); #if BACKTRACE_DEMANGLE char *my_demangle(const char *mangled_name, int *status); diff --git a/mysys/stacktrace.c b/mysys/stacktrace.c index ebd84548a9b..b6bf88cf3e1 100644 --- a/mysys/stacktrace.c +++ b/mysys/stacktrace.c @@ -27,6 +27,11 @@ #include #include +#ifdef __linux__ +#include /* isprint */ +#include /* SYS_gettid */ +#endif + #if HAVE_EXECINFO_H #include #endif @@ -46,10 +51,96 @@ void my_init_stacktrace() #endif } -void my_safe_print_str(const char* name, const char* val, int max_len) +#ifdef __linux__ + +static void print_buffer(char *buffer, size_t count) { - char *heap_end= (char*) sbrk(0); - fprintf(stderr, "%s at %p ", name, val); + for (; count && *buffer; --count) + { + int c= (int) *buffer++; + fputc(isprint(c) ? c : ' ', stderr); + } +} + +/** + Access the pages of this process through /proc/self/task//mem + in order to safely print the contents of a memory address range. + + @param addr The address at the start of the memory region. + @param max_len The length of the memory region. + + @return Zero on success. +*/ +static int safe_print_str(const char *addr, int max_len) +{ + int fd; + pid_t tid; + off_t offset; + ssize_t nbytes= 0; + size_t total, count; + char buf[256]; + + tid= (pid_t) syscall(SYS_gettid); + + sprintf(buf, "/proc/self/task/%d/mem", tid); + + if ((fd= open(buf, O_RDONLY)) < 0) + return -1; + + total= max_len; + offset= (off_t) addr; + + /* Read up to the maximum number of bytes. */ + while (total) + { + count= min(sizeof(buf), total); + + if ((nbytes= pread(fd, buf, count, offset)) < 0) + { + /* Just in case... */ + if (errno == EINTR) + continue; + else + break; + } + + /* Advance offset into memory. */ + total-= nbytes; + offset+= nbytes; + addr+= nbytes; + + /* Output the printable characters. */ + print_buffer(buf, nbytes); + + /* Break if less than requested... */ + if ((count - nbytes)) + break; + } + + /* Output a new line if something was printed. */ + if (total != (size_t) max_len) + fputc('\n', stderr); + + if (nbytes == -1) + fprintf(stderr, "Can't read from address %p: %m.\n", addr); + + close(fd); + + return 0; +} + +#endif + +void my_safe_print_str(const char* val, int max_len) +{ + char *heap_end; + +#ifdef __linux__ + if (!safe_print_str(val, max_len)) + return; +#endif + + heap_end= (char*) sbrk(0); if (!PTR_SANE(val)) { @@ -57,7 +148,6 @@ void my_safe_print_str(const char* name, const char* val, int max_len) return; } - fprintf(stderr, "= "); for (; max_len && PTR_SANE(val) && *val; --max_len) fputc(*val++, stderr); fputc('\n', stderr); @@ -657,10 +747,9 @@ void my_write_core(int unused) } -void my_safe_print_str(const char *name, const char *val, int len) +void my_safe_print_str(const char *val, int len) { - fprintf(stderr,"%s at %p", name, val); - __try + __try { fprintf(stderr,"=%.*s\n", len, val); } diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 98556e87838..3a33d06fb01 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2527,7 +2527,7 @@ the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n", if (!(test_flags & TEST_NO_STACKTRACE)) { - fprintf(stderr, "thd: 0x%lx\n",(long) thd); + fprintf(stderr, "Thread pointer: 0x%lx\n", (long) thd); fprintf(stderr, "Attempting backtrace. You can use the following " "information to find out\nwhere mysqld died. If " "you see no messages after this, something went\n" @@ -2555,11 +2555,13 @@ the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n", kreason= "KILLED_NO_VALUE"; break; } - fprintf(stderr, "Trying to get some variables.\n\ -Some pointers may be invalid and cause the dump to abort...\n"); - my_safe_print_str("thd->query", thd->query(), 1024); - fprintf(stderr, "thd->thread_id=%lu\n", (ulong) thd->thread_id); - fprintf(stderr, "thd->killed=%s\n", kreason); + fprintf(stderr, "\nTrying to get some variables.\n" + "Some pointers may be invalid and cause the dump to abort.\n"); + fprintf(stderr, "Query (%p): ", thd->query()); + my_safe_print_str(thd->query(), min(1024, thd->query_length())); + fprintf(stderr, "Connection ID (thread ID): %lu\n", (ulong) thd->thread_id); + fprintf(stderr, "Status: %s\n", kreason); + fputc('\n', stderr); } fprintf(stderr, "\ The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains\n\ From cd504e49bcece187dee85b33e39f72b5eab53cda Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Tue, 30 Nov 2010 21:07:55 -0200 Subject: [PATCH 08/12] Workaround a GCC warning about a pointer being cast to a larger integral type. Use intptr which is designed to hold pointer values and pass it to off_t. mysys/stacktrace.c: Add a compile time assert to ensure that off_t is large enough to hold the pointer value. --- mysys/stacktrace.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mysys/stacktrace.c b/mysys/stacktrace.c index b6bf88cf3e1..93e8ae6b508 100644 --- a/mysys/stacktrace.c +++ b/mysys/stacktrace.c @@ -87,8 +87,11 @@ static int safe_print_str(const char *addr, int max_len) if ((fd= open(buf, O_RDONLY)) < 0) return -1; + /* Ensure that off_t can hold a pointer. */ + compile_time_assert(sizeof(off_t) >= sizeof(intptr)); + total= max_len; - offset= (off_t) addr; + offset= (intptr) addr; /* Read up to the maximum number of bytes. */ while (total) From 2d81ad5be51cb2ec6921e681a6f1020e1cfa8d60 Mon Sep 17 00:00:00 2001 From: Nirbhay Choubey Date: Wed, 1 Dec 2010 12:25:31 +0530 Subject: [PATCH 09/12] Additional fix for bug#54899 Fixing the testcase to use the database name as connected_db instead of 'test' database. mysql-test/r/mysql.result: Additional fix in the test for bug#54899. mysql-test/t/mysql.test: Additional fix in the test for bug#54899. --- mysql-test/r/mysql.result | 18 ++++++++---------- mysql-test/t/mysql.test | 29 ++++++++++++++--------------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/mysql-test/r/mysql.result b/mysql-test/r/mysql.result index cc3f94528f7..f4298cc7a4c 100644 --- a/mysql-test/r/mysql.result +++ b/mysql-test/r/mysql.result @@ -294,16 +294,14 @@ Tables_in_test # Checking --one-database option with non_existent_db # specified with USE command # -SHOW TABLES IN test; -Tables_in_test -table_in_test -DROP DATABASE test; +CREATE DATABASE connected_db; +SHOW TABLES IN connected_db; +Tables_in_connected_db +table_in_connected_db -CREATE DATABASE test; -SHOW TABLES IN test; -Tables_in_test -table_in_test -DROP DATABASE test; -CREATE DATABASE test; +SHOW TABLES IN connected_db; +Tables_in_connected_db +table_in_connected_db +DROP DATABASE connected_db; End of tests diff --git a/mysql-test/t/mysql.test b/mysql-test/t/mysql.test index 5e26e0b7ef5..2dcc77a16c2 100644 --- a/mysql-test/t/mysql.test +++ b/mysql-test/t/mysql.test @@ -523,35 +523,34 @@ SHOW TABLES IN test; --echo # specified with USE command --echo # -# CASE 1 : When 'test' database exists and passed at commandline. +# CASE 1 : When 'connected_db' database exists and passed at commandline. --write_file $MYSQLTEST_VARDIR/tmp/one_db_1.sql -CREATE TABLE `table_in_test`(i INT); +CREATE TABLE `table_in_connected_db`(i INT); USE non_existent_db; # Following statement should be filtered out. CREATE TABLE `table_in_non_existent_db`(i INT); EOF -# CASE 2 : When 'test' database exists but dropped and recreated in load file. +# CASE 2 : When 'connected_db' database exists but dropped and recreated in +# load file. --write_file $MYSQLTEST_VARDIR/tmp/one_db_2.sql -DROP DATABASE test; -CREATE DATABASE test; +DROP DATABASE connected_db; +CREATE DATABASE connected_db; USE non_existent_db; # Following statements should be filtered out. CREATE TABLE `table_in_non_existent_db`(i INT); -USE test; +USE connected_db; # Following statements should not be filtered out. -CREATE TABLE `table_in_test`(i INT); +CREATE TABLE `table_in_connected_db`(i INT); EOF ---exec $MYSQL --one-database test < $MYSQLTEST_VARDIR/tmp/one_db_1.sql -SHOW TABLES IN test; -DROP DATABASE test; +CREATE DATABASE connected_db; +--exec $MYSQL --one-database connected_db < $MYSQLTEST_VARDIR/tmp/one_db_1.sql +SHOW TABLES IN connected_db; --echo -CREATE DATABASE test; ---exec $MYSQL --one-database test < $MYSQLTEST_VARDIR/tmp/one_db_2.sql -SHOW TABLES IN test; -DROP DATABASE test; -CREATE DATABASE test; +--exec $MYSQL --one-database connected_db < $MYSQLTEST_VARDIR/tmp/one_db_2.sql +SHOW TABLES IN connected_db; +DROP DATABASE connected_db; --remove_file $MYSQLTEST_VARDIR/tmp/one_db_1.sql --remove_file $MYSQLTEST_VARDIR/tmp/one_db_2.sql From fc9f3efaec61ea3086c1df2942a1157fdf9ca1c2 Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Wed, 1 Dec 2010 13:54:50 +0100 Subject: [PATCH 10/12] BUG#58246: INSTALL PLUGIN not secure & crashable When installing plugins, there is a missing check for slash (/) in the path on Windows. Note that on Windows, both / and \ can be used to separate directories. This patch fixes the issue by: - Adding a FN_DIRSEP symbol for all platforms consisting of a string of legal directory separators. - Adding a charset-aware version of strcspn(). - Adding a check_valid_path() function that uses my_strcspn() to check if any FN_DIRSEP character is in the supplied string. - Using the check_valid_path() function in sql_plugin.cc and sql_udf.cc (which means replacing the existing test there). include/config-netware.h: Adding FN_DIRSEP ****** Adding FN_DIRSEP include/config-win.h: Adding FN_DIRSEP ****** Adding FN_DIRSEP include/m_ctype.h: Adding my_strspn() and my_strcspn(). ****** Adding my_strspn() and my_strcspn(). include/my_global.h: Adding FN_DIRSEP ****** Adding FN_DIRSEP mysql-test/t/plugin_not_embedded.test: Adding test that file names containing / is disallowed on *all* platforms. ****** Adding test that file names containing / is disallowed on *all* platforms. sql/sql_plugin.cc: Introducing check_if_path() function for checking if filename is a path to include / on Windows. ****** Introducing check_if_path() function for checking if filename is a path to include / on Windows. sql/sql_udf.cc: Switching to use check_if_path() function. ****** Switching to use check_if_path() function. strings/my_strchr.c: Adding my_strspn() and my_strcspn(). ****** Adding my_strspn() and my_strcspn(). --- include/config-netware.h | 1 + include/config-win.h | 1 + include/m_ctype.h | 2 + include/my_global.h | 1 + mysql-test/r/plugin_not_embedded.result | 2 + mysql-test/t/plugin_not_embedded.test | 11 ++++ sql/sql_plugin.cc | 23 ++++++++- sql/sql_plugin.h | 1 + sql/sql_udf.cc | 12 +---- strings/my_strchr.c | 67 +++++++++++++++++++++++-- 10 files changed, 105 insertions(+), 16 deletions(-) diff --git a/include/config-netware.h b/include/config-netware.h index 4b9e1437170..156b1eff0e4 100644 --- a/include/config-netware.h +++ b/include/config-netware.h @@ -122,6 +122,7 @@ extern "C" { #define CANT_DELETE_OPEN_FILES 1 #define FN_LIBCHAR '\\' +#define FN_DIRSEP "/\\" /* Valid directory separators */ #define FN_ROOTDIR "\\" #define FN_DEVCHAR ':' diff --git a/include/config-win.h b/include/config-win.h index da9b1fc00c3..f802bbbbad8 100644 --- a/include/config-win.h +++ b/include/config-win.h @@ -332,6 +332,7 @@ inline ulonglong double2ulonglong(double d) /* File name handling */ #define FN_LIBCHAR '\\' +#define FN_DIRSEP "/\\" /* Valid directory separators */ #define FN_ROOTDIR "\\" #define FN_DEVCHAR ':' #define FN_NETWORK_DRIVES /* Uses \\ to indicate network drives */ diff --git a/include/m_ctype.h b/include/m_ctype.h index 451c8db549b..47a3a09f531 100644 --- a/include/m_ctype.h +++ b/include/m_ctype.h @@ -464,6 +464,8 @@ extern my_bool my_parse_charset_xml(const char *bug, size_t len, int (*add)(CHARSET_INFO *cs)); extern char *my_strchr(CHARSET_INFO *cs, const char *str, const char *end, pchar c); +extern size_t my_strcspn(CHARSET_INFO *cs, const char *str, const char *end, + const char *accept); my_bool my_propagate_simple(CHARSET_INFO *cs, const uchar *str, size_t len); my_bool my_propagate_complex(CHARSET_INFO *cs, const uchar *str, size_t len); diff --git a/include/my_global.h b/include/my_global.h index ec22a57329b..ac5d72249f2 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -758,6 +758,7 @@ typedef SOCKET_SIZE_TYPE size_socket; #ifndef FN_LIBCHAR #define FN_LIBCHAR '/' +#define FN_DIRSEP "/" /* Valid directory separators */ #define FN_ROOTDIR "/" #endif #define MY_NFILE 64 /* This is only used to save filenames */ diff --git a/mysql-test/r/plugin_not_embedded.result b/mysql-test/r/plugin_not_embedded.result index 82cfe7b23b8..27553366660 100644 --- a/mysql-test/r/plugin_not_embedded.result +++ b/mysql-test/r/plugin_not_embedded.result @@ -8,3 +8,5 @@ ERROR 42000: DELETE command denied to user 'bug51770'@'localhost' for table 'plu GRANT DELETE ON mysql.plugin TO bug51770@localhost; UNINSTALL PLUGIN example; DROP USER bug51770@localhost; +INSTALL PLUGIN example SONAME '../ha_example.so'; +ERROR HY000: No paths allowed for shared library diff --git a/mysql-test/t/plugin_not_embedded.test b/mysql-test/t/plugin_not_embedded.test index 15aff548c29..486b42e99d9 100644 --- a/mysql-test/t/plugin_not_embedded.test +++ b/mysql-test/t/plugin_not_embedded.test @@ -18,3 +18,14 @@ UNINSTALL PLUGIN example; disconnect con1; connection default; DROP USER bug51770@localhost; + +# +# BUG#58246: INSTALL PLUGIN not secure & crashable +# +# The bug consisted of not recognizing / on Windows, so checking / on +# all platforms should cover this case. + +let $path = `select CONCAT_WS('/', '..', $HA_EXAMPLE_SO)`; +--error ER_UDF_NO_PATHS +eval INSTALL PLUGIN example SONAME '$path'; + diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index e0fc88c3068..d8423fd153b 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -231,6 +231,26 @@ extern bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists); #endif /* EMBEDDED_LIBRARY */ +/** + Check if the provided path is valid in the sense that it does cause + a relative reference outside the directory. + + @note Currently, this function only check if there are any + characters in FN_DIRSEP in the string, but it might change in the + future. + + @code + check_valid_path("../foo.so") -> true + check_valid_path("foo.so") -> false + @endcode + */ +bool check_valid_path(const char *path, size_t len) +{ + size_t prefix= my_strcspn(files_charset_info, path, path + len, FN_DIRSEP); + return prefix < len; +} + + /**************************************************************************** Value type thunks, allows the C world to play in the C++ world ****************************************************************************/ @@ -354,13 +374,14 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) struct st_plugin_dl *tmp, plugin_dl; void *sym; DBUG_ENTER("plugin_dl_add"); + DBUG_PRINT("enter", ("dl->str: '%s', dl->length: %d", dl->str, dl->length)); plugin_dir_len= strlen(opt_plugin_dir); /* Ensure that the dll doesn't have a path. This is done to ensure that only approved libraries from the plugin directory are used (to make this even remotely secure). */ - if (my_strchr(files_charset_info, dl->str, dl->str + dl->length, FN_LIBCHAR) || + if (check_valid_path(dl->str, dl->length) || check_string_char_length((LEX_STRING *) dl, "", NAME_CHAR_LEN, system_charset_info, 1) || plugin_dir_len + dl->length + 1 >= FN_REFLEN) diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index 004d0d5abb7..72984865807 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -131,6 +131,7 @@ extern bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name); extern bool plugin_register_builtin(struct st_mysql_plugin *plugin); extern void plugin_thdvar_init(THD *thd); extern void plugin_thdvar_cleanup(THD *thd); +extern bool check_valid_path(const char *path, size_t length); typedef my_bool (plugin_foreach_func)(THD *thd, plugin_ref plugin, diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index d455a66c4f2..1d5335f0652 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -173,10 +173,7 @@ void udf_init() On windows we must check both FN_LIBCHAR and '/'. */ - if (my_strchr(files_charset_info, dl_name, - dl_name + strlen(dl_name), FN_LIBCHAR) || - IF_WIN(my_strchr(files_charset_info, dl_name, - dl_name + strlen(dl_name), '/'), 0) || + if (check_valid_path(dl_name, strlen(dl_name)) || check_string_char_length(&name, "", NAME_CHAR_LEN, system_charset_info, 1)) { @@ -416,13 +413,8 @@ int mysql_create_function(THD *thd,udf_func *udf) Ensure that the .dll doesn't have a path This is done to ensure that only approved dll from the system directories are used (to make this even remotely secure). - - On windows we must check both FN_LIBCHAR and '/'. */ - if (my_strchr(files_charset_info, udf->dl, - udf->dl + strlen(udf->dl), FN_LIBCHAR) || - IF_WIN(my_strchr(files_charset_info, udf->dl, - udf->dl + strlen(udf->dl), '/'), 0)) + if (check_valid_path(udf->dl, strlen(udf->dl))) { my_message(ER_UDF_NO_PATHS, ER(ER_UDF_NO_PATHS), MYF(0)); DBUG_RETURN(1); diff --git a/strings/my_strchr.c b/strings/my_strchr.c index 6724bf39ff2..08fa51ba17a 100644 --- a/strings/my_strchr.c +++ b/strings/my_strchr.c @@ -13,6 +13,45 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include +#include "m_string.h" +#include "m_ctype.h" + +#define NEQ(A, B) ((A) != (B)) +#define EQU(A, B) ((A) == (B)) + +/** + Macro for the body of the string scanning. + + @param CS The character set of the string + @param STR Pointer to beginning of string + @param END Pointer to one-after-end of string + @param ACC Pointer to beginning of accept (or reject) string + @param LEN Length of accept (or reject) string + @param CMP is a function-like for doing the comparison of two characters. + */ + +#define SCAN_STRING(CS, STR, END, ACC, LEN, CMP) \ + do { \ + uint mbl; \ + const char *ptr_str, *ptr_acc; \ + const char *acc_end= (ACC) + (LEN); \ + for (ptr_str= (STR) ; ptr_str < (END) ; ptr_str+= mbl) \ + { \ + mbl= my_mbcharlen((CS), *(uchar*)ptr_str); \ + if (mbl < 2) \ + { \ + DBUG_ASSERT(mbl == 1); \ + for (ptr_acc= (ACC) ; ptr_acc < acc_end ; ++ptr_acc) \ + if (CMP(*ptr_acc, *ptr_str)) \ + goto end; \ + } \ + } \ +end: \ + return (size_t) (ptr_str - (STR)); \ + } while (0) + + /* my_strchr(cs, str, end, c) returns a pointer to the first place in str where c (1-byte character) occurs, or NULL if c does not occur @@ -21,11 +60,6 @@ frequently. */ -#include -#include "m_string.h" -#include "m_ctype.h" - - char *my_strchr(CHARSET_INFO *cs, const char *str, const char *end, pchar c) { @@ -45,3 +79,26 @@ char *my_strchr(CHARSET_INFO *cs, const char *str, const char *end, return(0); } +/** + Calculate the length of the initial segment of 'str' which consists + entirely of characters not in 'reject'. + + @note The reject string points to single-byte characters so it is + only possible to find the first occurrence of a single-byte + character. Multi-byte characters in 'str' are treated as not + matching any character in the reject string. + + @todo should be moved to CHARSET_INFO if it's going to be called + frequently. + + @internal The implementation builds on the assumption that 'str' is long, + while 'reject' is short. So it compares each character in string + with the characters in 'reject' in a tight loop over the characters + in 'reject'. +*/ + +size_t my_strcspn(CHARSET_INFO *cs, const char *str, const char *str_end, + const char *reject) +{ + SCAN_STRING(cs, str, str_end, reject, strlen(reject), EQU); +} From d56a6dd02792e9a6bd5d0a644feb152f4cdae0a1 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Thu, 2 Dec 2010 08:14:43 +0100 Subject: [PATCH 11/12] BUG#58246 post-push fix broken DBG build. --- sql/sql_plugin.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index d8423fd153b..03a729258ca 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -374,7 +374,8 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) struct st_plugin_dl *tmp, plugin_dl; void *sym; DBUG_ENTER("plugin_dl_add"); - DBUG_PRINT("enter", ("dl->str: '%s', dl->length: %d", dl->str, dl->length)); + DBUG_PRINT("enter", ("dl->str: '%s', dl->length: %d", + dl->str, (int) dl->length)); plugin_dir_len= strlen(opt_plugin_dir); /* Ensure that the dll doesn't have a path. From 8888e65229b20d05b8169d0327f1ca4c355b2d6b Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Thu, 2 Dec 2010 09:13:31 +0100 Subject: [PATCH 12/12] BUG#58246: INSTALL PLUGIN not secure & crashable Fixing test case that fails on Windows because .dll is used. --- mysql-test/t/plugin_not_embedded.test | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/t/plugin_not_embedded.test b/mysql-test/t/plugin_not_embedded.test index 486b42e99d9..40024efcaad 100644 --- a/mysql-test/t/plugin_not_embedded.test +++ b/mysql-test/t/plugin_not_embedded.test @@ -26,6 +26,7 @@ DROP USER bug51770@localhost; # all platforms should cover this case. let $path = `select CONCAT_WS('/', '..', $HA_EXAMPLE_SO)`; +--replace_regex /\.dll/.so/ --error ER_UDF_NO_PATHS eval INSTALL PLUGIN example SONAME '$path';