diff options
Diffstat (limited to 'sql')
38 files changed, 840 insertions, 428 deletions
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index be75eff2575..d6e8df9f0ff 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -257,6 +257,10 @@ static void run_query(THD *thd, char *buf, char *end, thd->options&= ~OPTION_BIN_LOG; DBUG_PRINT("query", ("%s", thd->query)); + + DBUG_ASSERT(!thd->in_sub_stmt); + DBUG_ASSERT(!thd->prelocked_mode); + mysql_parse(thd, thd->query, thd->query_length, &found_semicolon); if (no_print_error && thd->is_slave_error) @@ -265,14 +269,27 @@ static void run_query(THD *thd, char *buf, char *end, Thd_ndb *thd_ndb= get_thd_ndb(thd); for (i= 0; no_print_error[i]; i++) if ((thd_ndb->m_error_code == no_print_error[i]) || - (thd->net.last_errno == (unsigned)no_print_error[i])) + (thd->main_da.sql_errno() == (unsigned) no_print_error[i])) break; if (!no_print_error[i]) sql_print_error("NDB: %s: error %s %d(ndb: %d) %d %d", - buf, thd->net.last_error, thd->net.last_errno, + buf, + thd->main_da.message(), + thd->main_da.sql_errno(), thd_ndb->m_error_code, (int) thd->is_error(), thd->is_slave_error); } + /* + XXX: this code is broken. mysql_parse()/mysql_reset_thd_for_next_command() + can not be called from within a statement, and + run_query() can be called from anywhere, including from within + a sub-statement. + This particular reset is a temporary hack to avoid an assert + for double assignment of the diagnostics area when run_query() + is called from ndbcluster_reset_logs(), which is called from + mysql_flush(). + */ + thd->main_da.reset_diagnostics_area(); thd->options= save_thd_options; thd->query_length= save_query_length; @@ -2301,8 +2318,8 @@ static int open_ndb_binlog_index(THD *thd, TABLE_LIST *tables, if (open_tables(thd, &tables, &counter, MYSQL_LOCK_IGNORE_FLUSH)) { sql_print_error("NDB Binlog: Opening ndb_binlog_index: %d, '%s'", - thd->net.last_errno, - thd->net.last_error ? thd->net.last_error : ""); + thd->main_da.sql_errno(), + thd->main_da.message()); thd->proc_info= save_proc_info; return -1; } diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 1400d9da753..eca577cb6c8 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1753,6 +1753,7 @@ partition_element *ha_partition::find_partition_element(uint part_id) return part_elem; } DBUG_ASSERT(0); + my_error(ER_OUT_OF_RESOURCES, MYF(0)); current_thd->fatal_error(); // Abort return NULL; } diff --git a/sql/item_func.cc b/sql/item_func.cc index 101832b58a9..d219987e46b 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2949,7 +2949,6 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func, } } } - thd->net.last_error[0]=0; Udf_func_init init= u_d->func_init; if ((error=(uchar) init(&initid, &f_args, init_msg_buff))) { diff --git a/sql/log_event.cc b/sql/log_event.cc index 2b3037aedcc..00e3dc89f6b 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1611,7 +1611,8 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, if (killed_status_arg == THD::KILLED_NO_VALUE) killed_status_arg= thd_arg->killed; error_code= - (killed_status_arg == THD::NOT_KILLED) ? thd_arg->net.last_errno : + (killed_status_arg == THD::NOT_KILLED) ? + (thd_arg->is_error() ? thd_arg->main_da.sql_errno() : 0) : ((thd_arg->system_thread & SYSTEM_THREAD_DELAYED_INSERT) ? 0 : thd_arg->killed_errno()); @@ -2332,7 +2333,7 @@ START SLAVE; . Query: '%s'", expected_error, thd->query); } /* If the query was not ignored, it is printed to the general log */ - if (thd->net.last_errno != ER_SLAVE_IGNORED_TABLE) + if (!thd->is_error() || thd->main_da.sql_errno() != ER_SLAVE_IGNORED_TABLE) general_log_write(thd, COM_QUERY, thd->query, thd->query_length); compare_errors: @@ -2341,9 +2342,10 @@ compare_errors: If we expected a non-zero error code, and we don't get the same error code, and none of them should be ignored. */ - DBUG_PRINT("info",("expected_error: %d last_errno: %d", - expected_error, thd->net.last_errno)); - if ((expected_error != (actual_error= thd->net.last_errno)) && + actual_error= thd->is_error() ? thd->main_da.sql_errno() : 0; + DBUG_PRINT("info",("expected_error: %d sql_errno: %d", + expected_error, actual_error)); + if ((expected_error != actual_error) && expected_error && !ignored_error_code(actual_error) && !ignored_error_code(expected_error)) @@ -2355,7 +2357,7 @@ Error on master: '%s' (%d), Error on slave: '%s' (%d). \ Default database: '%s'. Query: '%s'", ER_SAFE(expected_error), expected_error, - actual_error ? thd->net.last_error: "no error", + actual_error ? thd->main_da.message() : "no error", actual_error, print_slave_db_safe(db), query_arg); thd->is_slave_error= 1; @@ -2377,7 +2379,7 @@ Default database: '%s'. Query: '%s'", { rli->report(ERROR_LEVEL, actual_error, "Error '%s' on query. Default database: '%s'. Query: '%s'", - (actual_error ? thd->net.last_error : + (actual_error ? thd->main_da.message() : "unexpected success or fatal error"), print_slave_db_safe(thd->db), query_arg); thd->is_slave_error= 1; @@ -3720,8 +3722,11 @@ error: /* this err/sql_errno code is copy-paste from net_send_error() */ const char *err; int sql_errno; - if ((err=thd->net.last_error)[0]) - sql_errno=thd->net.last_errno; + if (thd->is_error()) + { + err= thd->main_da.message(); + sql_errno= thd->main_da.sql_errno(); + } else { sql_errno=ER_UNKNOWN_ERROR; @@ -6222,10 +6227,10 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) Error reporting borrowed from Query_log_event with many excessive simplifications (we don't honour --slave-skip-errors) */ - uint actual_error= thd->net.last_errno; + uint actual_error= thd->main_da.sql_errno(); rli->report(ERROR_LEVEL, actual_error, "Error '%s' in %s event: when locking tables", - (actual_error ? thd->net.last_error : + (actual_error ? thd->main_da.message(): "unexpected success or fatal error"), get_type_str()); thd->is_fatal_error= 1; @@ -6266,10 +6271,10 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) Error reporting borrowed from Query_log_event with many excessive simplifications (we don't honour --slave-skip-errors) */ - uint actual_error= thd->net.last_errno; + uint actual_error= thd->main_da.sql_errno(); rli->report(ERROR_LEVEL, actual_error, "Error '%s' on reopening tables", - (actual_error ? thd->net.last_error : + (actual_error ? thd->main_da.message() : "unexpected success or fatal error")); thd->is_slave_error= 1; } @@ -6425,10 +6430,11 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) break; default: - rli->report(ERROR_LEVEL, thd->net.last_errno, + rli->report(ERROR_LEVEL, + thd->is_error() ? thd->main_da.sql_errno() : 0, "Error in %s event: row application failed. %s", get_type_str(), - thd->net.last_error ? thd->net.last_error : ""); + thd->is_error() ? thd->main_da.message() : ""); thd->is_slave_error= 1; break; } @@ -6475,12 +6481,13 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) if (error) { /* error has occured during the transaction */ - rli->report(ERROR_LEVEL, thd->net.last_errno, + rli->report(ERROR_LEVEL, + thd->is_error() ? thd->main_da.sql_errno() : 0, "Error in %s event: error during transaction execution " "on table %s.%s. %s", get_type_str(), table->s->db.str, table->s->table_name.str, - thd->net.last_error ? thd->net.last_error : ""); + thd->is_error() ? thd->main_da.message() : ""); /* If one day we honour --skip-slave-errors in row-based replication, and @@ -7094,10 +7101,10 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) Error reporting borrowed from Query_log_event with many excessive simplifications (we don't honour --slave-skip-errors) */ - uint actual_error= thd->net.last_errno; + uint actual_error= thd->main_da.sql_errno(); rli->report(ERROR_LEVEL, actual_error, "Error '%s' on opening table `%s`.`%s`", - (actual_error ? thd->net.last_error : + (actual_error ? thd->main_da.message() : "unexpected success or fatal error"), table_list->db, table_list->table_name); thd->is_slave_error= 1; @@ -7625,8 +7632,11 @@ Write_rows_log_event::do_exec_row(const Relay_log_info *const rli) DBUG_ASSERT(m_table != NULL); int error= write_row(rli, TRUE /* overwrite */); - if (error && !thd->net.last_errno) - thd->net.last_errno= error; + if (error && !thd->is_error()) + { + DBUG_ASSERT(0); + my_error(ER_UNKNOWN_ERROR, MYF(0)); + } return error; } diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index c6b691ec010..6d5d86e42fe 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -74,10 +74,10 @@ Old_rows_log_event::do_apply_event(Rows_log_event *ev, const Relay_log_info *rli Error reporting borrowed from Query_log_event with many excessive simplifications (we don't honour --slave-skip-errors) */ - uint actual_error= thd->net.last_errno; + uint actual_error= thd->main_da.sql_errno(); rli->report(ERROR_LEVEL, actual_error, "Error '%s' in %s event: when locking tables", - (actual_error ? thd->net.last_error : + (actual_error ? thd->main_da.message() : "unexpected success or fatal error"), ev->get_type_str()); thd->is_fatal_error= 1; @@ -118,10 +118,10 @@ Old_rows_log_event::do_apply_event(Rows_log_event *ev, const Relay_log_info *rli Error reporting borrowed from Query_log_event with many excessive simplifications (we don't honour --slave-skip-errors) */ - uint actual_error= thd->net.last_errno; + uint actual_error= thd->main_da.sql_errno(); rli->report(ERROR_LEVEL, actual_error, "Error '%s' on reopening tables", - (actual_error ? thd->net.last_error : + (actual_error ? thd->main_da.message() : "unexpected success or fatal error")); thd->is_slave_error= 1; } @@ -251,10 +251,10 @@ Old_rows_log_event::do_apply_event(Rows_log_event *ev, const Relay_log_info *rli break; default: - rli->report(ERROR_LEVEL, thd->net.last_errno, + rli->report(ERROR_LEVEL, thd->main_da.sql_errno(), "Error in %s event: row application failed. %s", ev->get_type_str(), - thd->net.last_error ? thd->net.last_error : ""); + thd->is_error() ? thd->main_da.message() : ""); thd->is_slave_error= 1; break; } @@ -280,12 +280,12 @@ Old_rows_log_event::do_apply_event(Rows_log_event *ev, const Relay_log_info *rli if (error) { /* error has occured during the transaction */ - rli->report(ERROR_LEVEL, thd->net.last_errno, + rli->report(ERROR_LEVEL, thd->main_da.sql_errno(), "Error in %s event: error during transaction execution " "on table %s.%s. %s", ev->get_type_str(), table->s->db.str, table->s->table_name.str, - thd->net.last_error ? thd->net.last_error : ""); + thd->is_error() ? thd->main_da.message() : ""); /* If one day we honour --skip-slave-errors in row-based replication, and diff --git a/sql/mysqld.cc b/sql/mysqld.cc index fad2e5dcd22..1938602f372 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2587,21 +2587,8 @@ int my_message_sql(uint error, const char *str, myf MyFlags) MYSQL_ERROR::WARN_LEVEL_ERROR)) DBUG_RETURN(0); - if (thd->spcont && - thd->spcont->handle_error(error, MYSQL_ERROR::WARN_LEVEL_ERROR, thd)) - { - DBUG_RETURN(0); - } - thd->is_slave_error= 1; // needed to catch query errors during replication - if (!thd->no_warnings_for_error) - { - thd->no_warnings_for_error= TRUE; - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, error, str); - thd->no_warnings_for_error= FALSE; - } - /* thd->lex->current_select == 0 if lex structure is not inited (not query command (COM_QUERY)) @@ -2618,14 +2605,39 @@ int my_message_sql(uint error, const char *str, myf MyFlags) } else { - NET *net= &thd->net; - net->report_error= 1; - query_cache_abort(net); - if (!net->last_error[0]) // Return only first message + if (! thd->main_da.is_error()) // Return only first message { - strmake(net->last_error, str, sizeof(net->last_error)-1); - net->last_errno= error ? error : ER_UNKNOWN_ERROR; + if (error == 0) + error= ER_UNKNOWN_ERROR; + if (str == NULL) + str= ER(error); + thd->main_da.set_error_status(thd, error, str); } + query_cache_abort(&thd->net); + } + /* + If a continue handler is found, the error message will be cleared + by the stored procedures code. + */ + if (thd->spcont && + thd->spcont->handle_error(error, MYSQL_ERROR::WARN_LEVEL_ERROR, thd)) + { + /* + Do not push any warnings, a handled error must be completely + silenced. + */ + DBUG_RETURN(0); + } + + if (!thd->no_warnings_for_error) + { + /* + Suppress infinite recursion if there a memory allocation error + inside push_warning. + */ + thd->no_warnings_for_error= TRUE; + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, error, str); + thd->no_warnings_for_error= FALSE; } } if (!thd || MyFlags & ME_NOREFRESH) diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 18cf1ebae5b..73eb340fbc0 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -123,20 +123,18 @@ my_bool my_net_init(NET *net, Vio* vio) MYF(MY_WME)))) DBUG_RETURN(1); net->buff_end=net->buff+net->max_packet; - net->no_send_ok= net->no_send_error= 0; net->error=0; net->return_errno=0; net->return_status=0; net->pkt_nr=net->compress_pkt_nr=0; net->write_pos=net->read_pos = net->buff; - net->last_error[0]=0; + net->client_last_error[0]=0; net->compress=0; net->reading_or_writing=0; net->where_b = net->remain_in_buf=0; - net->last_errno=0; + net->client_last_errno=0; #ifdef USE_QUERY_CACHE query_cache_init_query(net); #else net->query_cache_query= 0; #endif - net->report_error= 0; if (vio != 0) /* If real connection */ { @@ -176,9 +174,12 @@ my_bool net_realloc(NET *net, size_t length) { DBUG_PRINT("error", ("Packet too large. Max size: %lu", net->max_packet_size)); + /* @todo: 1 and 2 codes are identical. */ net->error= 1; - net->report_error= 1; - net->last_errno= ER_NET_PACKET_TOO_LARGE; + net->client_last_errno= ER_NET_PACKET_TOO_LARGE; +#ifdef MYSQL_SERVER + my_error(ER_NET_PACKET_TOO_LARGE, MYF(0)); +#endif DBUG_RETURN(1); } pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1); @@ -190,9 +191,10 @@ my_bool net_realloc(NET *net, size_t length) NET_HEADER_SIZE + COMP_HEADER_SIZE, MYF(MY_WME)))) { + /* @todo: 1 and 2 codes are identical. */ net->error= 1; - net->report_error= 1; - net->last_errno= ER_OUT_OF_RESOURCES; + net->client_last_errno= ER_OUT_OF_RESOURCES; + /* In the server the error is reported by MY_WME flag. */ DBUG_RETURN(1); } net->buff=net->write_pos=buff; @@ -582,12 +584,9 @@ net_real_write(NET *net,const uchar *packet, size_t len) if (!(b= (uchar*) my_malloc(len + NET_HEADER_SIZE + COMP_HEADER_SIZE, MYF(MY_WME)))) { -#ifdef MYSQL_SERVER - net->last_errno= ER_OUT_OF_RESOURCES; net->error= 2; - /* TODO is it needed to set this variable if we have no socket */ - net->report_error= 1; -#endif + net->client_last_errno= ER_OUT_OF_RESOURCES; + /* In the server, the error is reported by MY_WME flag. */ net->reading_or_writing= 0; DBUG_RETURN(1); } @@ -638,11 +637,11 @@ net_real_write(NET *net,const uchar *packet, size_t len) "%s: my_net_write: fcntl returned error %d, aborting thread\n", my_progname,vio_errno(net->vio)); #endif /* EXTRA_DEBUG */ -#ifdef MYSQL_SERVER - net->last_errno= ER_NET_ERROR_ON_WRITE; -#endif net->error= 2; /* Close socket */ - net->report_error= 1; + net->client_last_errno= ER_NET_PACKET_TOO_LARGE; +#ifdef MYSQL_SERVER + my_error(ER_NET_PACKET_TOO_LARGE, MYF(0)); +#endif goto end; } retry_count=0; @@ -669,10 +668,10 @@ net_real_write(NET *net,const uchar *packet, size_t len) } #endif /* defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) */ net->error= 2; /* Close socket */ - net->report_error= 1; + net->client_last_errno= (interrupted ? ER_NET_WRITE_INTERRUPTED : + ER_NET_ERROR_ON_WRITE); #ifdef MYSQL_SERVER - net->last_errno= (interrupted ? ER_NET_WRITE_INTERRUPTED : - ER_NET_ERROR_ON_WRITE); + my_error(net->client_last_errno, MYF(0)); #endif /* MYSQL_SERVER */ break; } @@ -849,9 +848,9 @@ my_real_read(NET *net, size_t *complen) #endif /* EXTRA_DEBUG */ len= packet_error; net->error= 2; /* Close socket */ - net->report_error= 1; + net->client_last_errno= ER_NET_FCNTL_ERROR; #ifdef MYSQL_SERVER - net->last_errno= ER_NET_FCNTL_ERROR; + my_error(ER_NET_FCNTL_ERROR, MYF(0)); #endif goto end; } @@ -881,10 +880,11 @@ my_real_read(NET *net, size_t *complen) remain, vio_errno(net->vio), (long) length)); len= packet_error; net->error= 2; /* Close socket */ - net->report_error= 1; + net->client_last_errno= (vio_was_interrupted(net->vio) ? + ER_NET_READ_INTERRUPTED : + ER_NET_READ_ERROR); #ifdef MYSQL_SERVER - net->last_errno= (vio_was_interrupted(net->vio) ? ER_NET_READ_INTERRUPTED : - ER_NET_READ_ERROR); + my_error(net->client_last_errno, MYF(0)); #endif goto end; } @@ -915,9 +915,9 @@ my_real_read(NET *net, size_t *complen) #endif } len= packet_error; - net->report_error= 1; + /* Not a NET error on the client. XXX: why? */ #ifdef MYSQL_SERVER - net->last_errno=ER_NET_PACKETS_OUT_OF_ORDER; + my_error(ER_NET_PACKETS_OUT_OF_ORDER, MYF(0)); #endif goto end; } @@ -1101,9 +1101,9 @@ my_net_read(NET *net) &complen)) { net->error= 2; /* caller will close socket */ - net->report_error= 1; + net->client_last_errno= ER_NET_UNCOMPRESS_ERROR; #ifdef MYSQL_SERVER - net->last_errno=ER_NET_UNCOMPRESS_ERROR; + my_error(ER_NET_UNCOMPRESS_ERROR, MYF(0)); #endif return packet_error; } diff --git a/sql/opt_range.cc b/sql/opt_range.cc index c67687b2bde..9b169bc8739 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1271,7 +1271,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) the storage engine calls in question happen to never fail with the existing storage engines. */ - thd->net.report_error= 1; /* purecov: inspected */ + my_error(ER_OUT_OF_RESOURCES, MYF(0)); /* purecov: inspected */ /* Caller will free the memory */ goto failure; /* purecov: inspected */ } diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 12ad504d738..91786ff3f4b 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -170,6 +170,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) if(error) { tl->table->file->print_error(error, MYF(0)); + tl->table->in_use->fatal_error(); return error; } count*= tl->table->file->stats.records; @@ -418,6 +419,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) return HA_ERR_KEY_NOT_FOUND; // No rows matching WHERE /* HA_ERR_LOCK_DEADLOCK or some other error */ table->file->print_error(error, MYF(0)); + table->in_use->fatal_error(); return(error); } removed_tables|= table->map; diff --git a/sql/protocol.cc b/sql/protocol.cc index 713f4ed3d25..f7b3a496447 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -23,13 +23,20 @@ #endif #include "mysql_priv.h" -#include "sp_rcontext.h" #include <stdarg.h> static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024; +/* Declared non-static only because of the embedded library. */ void net_send_error_packet(THD *thd, uint sql_errno, const char *err); +void +net_send_ok(THD *thd, + uint server_status, uint total_warn_count, + ha_rows affected_rows, ulonglong id, const char *message); +void +net_send_eof(THD *thd, uint server_status, uint total_warn_count); #ifndef EMBEDDED_LIBRARY -static void write_eof_packet(THD *thd, NET *net); +static void write_eof_packet(THD *thd, NET *net, + uint server_status, uint total_warn_count); #endif #ifndef EMBEDDED_LIBRARY @@ -68,58 +75,23 @@ bool Protocol_binary::net_store_data(const uchar *from, size_t length) void net_send_error(THD *thd, uint sql_errno, const char *err) { NET *net= &thd->net; - bool generate_warning= thd->killed != THD::KILL_CONNECTION; DBUG_ENTER("net_send_error"); - DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno, - err ? err : net->last_error[0] ? - net->last_error : "NULL")); DBUG_ASSERT(!thd->spcont); + DBUG_ASSERT(sql_errno); + DBUG_ASSERT(err && err[0]); - if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA) - { - thd->killed= THD::NOT_KILLED; - thd->mysys_var->abort= 0; - } + DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno, err)); - if (net && net->no_send_error) - { - thd->clear_error(); - thd->is_fatal_error= 0; // Error message is given - DBUG_PRINT("info", ("sending error messages prohibited")); - DBUG_VOID_RETURN; - } - - thd->is_slave_error= 1; // needed to catch query errors during replication - if (!err) - { - if (sql_errno) - err=ER(sql_errno); - else - { - if ((err=net->last_error)[0]) - { - sql_errno=net->last_errno; - generate_warning= 0; // This warning has already been given - } - else - { - sql_errno=ER_UNKNOWN_ERROR; - err=ER(sql_errno); /* purecov: inspected */ - } - } - } - - if (generate_warning) - { - /* Error that we have not got with my_error() */ - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, sql_errno, err); - } + /* + It's one case when we can push an error even though there + is an OK or EOF already. + */ + thd->main_da.can_overwrite_status= TRUE; net_send_error_packet(thd, sql_errno, err); - thd->is_fatal_error= 0; // Error message is given - thd->net.report_error= 0; + thd->main_da.can_overwrite_status= FALSE; /* Abort multi-result sets */ thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS; @@ -149,23 +121,21 @@ void net_send_error(THD *thd, uint sql_errno, const char *err) warning_count Stored in 2 bytes; New in 4.1 protocol message Stored as packed length (1-9 bytes) + message Is not stored if no message - - If net->no_send_ok return without sending packet */ #ifndef EMBEDDED_LIBRARY void -send_ok(THD *thd, ha_rows affected_rows, ulonglong id, const char *message) +net_send_ok(THD *thd, + uint server_status, uint total_warn_count, + ha_rows affected_rows, ulonglong id, const char *message) { NET *net= &thd->net; uchar buff[MYSQL_ERRMSG_SIZE+10],*pos; DBUG_ENTER("send_ok"); - if (net->no_send_ok || !net->vio) // hack for re-parsing queries + if (! net->vio) // hack for re-parsing queries { - DBUG_PRINT("info", ("no send ok: %s, vio present: %s", - (net->no_send_ok ? "YES" : "NO"), - (net->vio ? "YES" : "NO"))); + DBUG_PRINT("info", ("vio present: NO")); DBUG_VOID_RETURN; } @@ -178,28 +148,29 @@ send_ok(THD *thd, ha_rows affected_rows, ulonglong id, const char *message) ("affected_rows: %lu id: %lu status: %u warning_count: %u", (ulong) affected_rows, (ulong) id, - (uint) (thd->server_status & 0xffff), - (uint) thd->total_warn_count)); - int2store(pos,thd->server_status); + (uint) (server_status & 0xffff), + (uint) total_warn_count)); + int2store(pos, server_status); pos+=2; /* We can only return up to 65535 warnings in two bytes */ - uint tmp= min(thd->total_warn_count, 65535); + uint tmp= min(total_warn_count, 65535); int2store(pos, tmp); pos+= 2; } else if (net->return_status) // For 4.0 protocol { - int2store(pos,thd->server_status); + int2store(pos, server_status); pos+=2; } - if (message) + thd->main_da.can_overwrite_status= TRUE; + + if (message && message[0]) pos= net_store_data(pos, (uchar*) message, strlen(message)); VOID(my_net_write(net, buff, (size_t) (pos-buff))); VOID(net_flush(net)); - /* We can't anymore send an error to the client */ - thd->net.report_error= 0; - thd->net.no_send_error= 1; + + thd->main_da.can_overwrite_status= FALSE; DBUG_PRINT("info", ("OK sent, so no more error sending allowed")); DBUG_VOID_RETURN; @@ -211,7 +182,7 @@ static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */ Send eof (= end of result set) to the client SYNOPSIS - send_eof() + net_send_eof() thd Thread handler no_flush Set to 1 if there will be more data to the client, like in send_fields(). @@ -230,15 +201,17 @@ static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */ */ void -send_eof(THD *thd) +net_send_eof(THD *thd, uint server_status, uint total_warn_count) { NET *net= &thd->net; - DBUG_ENTER("send_eof"); + DBUG_ENTER("net_send_eof"); + /* Set to TRUE if no active vio, to work well in case of --init-file */ if (net->vio != 0) { - write_eof_packet(thd, net); + thd->main_da.can_overwrite_status= TRUE; + write_eof_packet(thd, net, server_status, total_warn_count); VOID(net_flush(net)); - thd->net.no_send_error= 1; + thd->main_da.can_overwrite_status= FALSE; DBUG_PRINT("info", ("EOF sent, so no more error sending allowed")); } DBUG_VOID_RETURN; @@ -250,7 +223,9 @@ send_eof(THD *thd) write it to the network output buffer. */ -static void write_eof_packet(THD *thd, NET *net) +static void write_eof_packet(THD *thd, NET *net, + uint server_status, + uint total_warn_count) { if (thd->client_capabilities & CLIENT_PROTOCOL_41) { @@ -259,7 +234,7 @@ static void write_eof_packet(THD *thd, NET *net) Don't send warn count during SP execution, as the warn_list is cleared between substatements, and mysqltest gets confused */ - uint tmp= (thd->spcont ? 0 : min(thd->total_warn_count, 65535)); + uint tmp= min(total_warn_count, 65535); buff[0]= 254; int2store(buff+1, tmp); /* @@ -268,8 +243,8 @@ static void write_eof_packet(THD *thd, NET *net) other queries (see the if test in dispatch_command / COM_QUERY) */ if (thd->is_fatal_error) - thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS; - int2store(buff+3, thd->server_status); + server_status&= ~SERVER_MORE_RESULTS_EXISTS; + int2store(buff + 3, server_status); VOID(my_net_write(net, buff, 5)); } else @@ -360,6 +335,96 @@ static uchar *net_store_length_fast(uchar *packet, uint length) return packet+2; } +/** + Send the status of the current statement execution over network. + + @param thd in fact, carries two parameters, NET for the transport and + Diagnostics_area as the source of status information. + + In MySQL, there are two types of SQL statements: those that return + a result set and those that return status information only. + + If a statement returns a result set, it consists of 3 parts: + - result set meta-data + - variable number of result set rows (can be 0) + - followed and terminated by EOF or ERROR packet + + Once the client has seen the meta-data information, it always + expects an EOF or ERROR to terminate the result set. If ERROR is + received, the result set rows are normally discarded (this is up + to the client implementation, libmysql at least does discard them). + EOF, on the contrary, means "successfully evaluated the entire + result set". Since we don't know how many rows belong to a result + set until it's evaluated, EOF/ERROR is the indicator of the end + of the row stream. Note, that we can not buffer result set rows + on the server -- there may be an arbitrary number of rows. But + we do buffer the last packet (EOF/ERROR) in the Diagnostics_area and + delay sending it till the very end of execution (here), to be able to + change EOF to an ERROR if commit failed or some other error occurred + during the last cleanup steps taken after execution. + + A statement that does not return a result set doesn't send result + set meta-data either. Instead it returns one of: + - OK packet + - ERROR packet. + Similarly to the EOF/ERROR of the previous statement type, OK/ERROR + packet is "buffered" in the diagnostics area and sent to the client + in the end of statement. + + @pre The diagnostics area is assigned or disabled. It can not be empty + -- we assume that every SQL statement or COM_* command + generates OK, ERROR, or EOF status. + + @post The status information is encoded to protocol format and sent to the + client. + + @return We conventionally return void, since the only type of error + that can happen here is a NET (transport) error, and that one + will become visible when we attempt to read from the NET the + next command. + Diagnostics_area::is_sent is set for debugging purposes only. +*/ + +void net_end_statement(THD *thd) +{ + DBUG_ASSERT(! thd->main_da.is_sent); + + /* Can not be true, but do not take chances in production. */ + if (thd->main_da.is_sent) + return; + + switch (thd->main_da.status()) { + case Diagnostics_area::DA_ERROR: + /* The query failed, send error to log and abort bootstrap. */ + net_send_error(thd, + thd->main_da.sql_errno(), + thd->main_da.message()); + break; + case Diagnostics_area::DA_EOF: + net_send_eof(thd, + thd->main_da.server_status(), + thd->main_da.total_warn_count()); + break; + case Diagnostics_area::DA_OK: + net_send_ok(thd, + thd->main_da.server_status(), + thd->main_da.total_warn_count(), + thd->main_da.affected_rows(), + thd->main_da.last_insert_id(), + thd->main_da.message()); + break; + case Diagnostics_area::DA_DISABLED: + break; + case Diagnostics_area::DA_EMPTY: + default: + DBUG_ASSERT(0); + net_send_ok(thd, thd->server_status, thd->total_warn_count, + 0, 0, NULL); + break; + } + thd->main_da.is_sent= TRUE; +} + /**************************************************************************** Functions used by the protocol functions (like send_ok) to store strings @@ -408,6 +473,17 @@ void Protocol::init(THD *thd_arg) #endif } +/** + Finish the result set with EOF packet, as is expected by the client, + if there is an error evaluating the next row and a continue handler + for the error. +*/ + +void Protocol::end_partial_result_set(THD *thd) +{ + net_send_eof(thd, thd->server_status, 0 /* no warnings, we're inside SP */); +} + bool Protocol::flush() { @@ -573,7 +649,14 @@ bool Protocol::send_fields(List<Item> *list, uint flags) } if (flags & SEND_EOF) - write_eof_packet(thd, &thd->net); + { + /* + Mark the end of meta-data result set, and store thd->server_status, + to show that there is no cursor. + Send no warning information, as it will be sent at statement end. + */ + write_eof_packet(thd, &thd->net, thd->server_status, thd->total_warn_count); + } DBUG_RETURN(prepare_for_send(list)); err: diff --git a/sql/protocol.h b/sql/protocol.h index 53584326f03..cd58ec93bbb 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -75,6 +75,7 @@ public: return 0; } virtual bool flush(); + virtual void end_partial_result_set(THD *thd); virtual void prepare_for_resend()=0; virtual bool store_null()=0; @@ -173,9 +174,7 @@ public: void send_warning(THD *thd, uint sql_errno, const char *err=0); void net_send_error(THD *thd, uint sql_errno=0, const char *err=0); -void send_ok(THD *thd, ha_rows affected_rows=0L, ulonglong id=0L, - const char *info=0); -void send_eof(THD *thd); +void net_end_statement(THD *thd); bool send_old_password_request(THD *thd); uchar *net_store_data(uchar *to,const uchar *from, size_t length); uchar *net_store_data(uchar *to,int32 from); diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 834d87532af..e294cf8ae2d 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -685,7 +685,7 @@ int connect_to_master(THD *thd, MYSQL* mysql, Master_info* mi) if (!mi->host || !*mi->host) /* empty host */ { - strmov(mysql->net.last_error, "Master is not configured"); + strmov(mysql->net.client_last_error, "Master is not configured"); DBUG_RETURN(1); } mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *) &slave_net_timeout); @@ -880,6 +880,8 @@ bool load_master_data(THD* thd) cleanup_mysql_results(db_res, cur_table_res - 1, table_res); goto err; } + /* Clear the result of mysql_create_db(). */ + thd->main_da.reset_diagnostics_area(); if (mysql_select_db(&mysql, db) || mysql_real_query(&mysql, STRING_WITH_LEN("SHOW TABLES")) || diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc index ed0dc82cf01..eb32897f937 100644 --- a/sql/rpl_record.cc +++ b/sql/rpl_record.cc @@ -338,12 +338,13 @@ int prepare_record(const Slave_reporting_capability *const log, if (check && ((f->flags & mask) == mask)) { DBUG_ASSERT(log); - log->report(ERROR_LEVEL, ER_NO_DEFAULT_FOR_FIELD, + error= ER_NO_DEFAULT_FOR_FIELD; + log->report(ERROR_LEVEL, error, "Field `%s` of table `%s`.`%s` " "has no default value and cannot be NULL", f->field_name, table->s->db.str, table->s->table_name.str); - error = ER_NO_DEFAULT_FOR_FIELD; + my_error(error, MYF(0), f->field_name); } else f->set_default(); diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 15d7d97affd..3467f6fd67c 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -166,7 +166,7 @@ int init_relay_log_info(Relay_log_info* rli, { sql_print_error("Failed to create a new relay log info file (\ file '%s', errno %d)", fname, my_errno); - msg= current_thd->net.last_error; + msg= current_thd->main_da.message(); goto err; } if (init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0, @@ -174,7 +174,7 @@ file '%s', errno %d)", fname, my_errno); { sql_print_error("Failed to create a cache on relay log info file '%s'", fname); - msg= current_thd->net.last_error; + msg= current_thd->main_da.message(); goto err; } diff --git a/sql/slave.cc b/sql/slave.cc index 0421c567c65..b1f25ee58da 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -981,17 +981,24 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, } thd->query= query; thd->is_slave_error = 0; - thd->net.no_send_ok = 1; bzero((char*) &tables,sizeof(tables)); tables.db = (char*)db; tables.alias= tables.table_name= (char*)table_name; /* Drop the table if 'overwrite' is true */ - if (overwrite && mysql_rm_table(thd,&tables,1,0)) /* drop if exists */ + if (overwrite) { - sql_print_error("create_table_from_dump: failed to drop the table"); - goto err; + if (mysql_rm_table(thd,&tables,1,0)) /* drop if exists */ + { + sql_print_error("create_table_from_dump: failed to drop the table"); + goto err; + } + else + { + /* Clear the OK result of mysql_rm_table(). */ + thd->main_da.reset_diagnostics_area(); + } } /* Create the table. We do not want to log the "create table" statement */ @@ -1012,6 +1019,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, if (thd->is_slave_error) goto err; // mysql_parse took care of the error send + thd->main_da.reset_diagnostics_area(); /* cleanup from CREATE_TABLE */ thd->proc_info = "Opening master dump table"; /* Note: If this function starts to fail for MERGE tables, @@ -1055,7 +1063,6 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, err: close_thread_tables(thd); - thd->net.no_send_ok = 0; DBUG_RETURN(error); } @@ -1107,7 +1114,6 @@ int fetch_master_table(THD *thd, const char *db_name, const char *table_name, error = 0; err: - thd->net.no_send_ok = 0; // Clear up garbage after create_table_from_dump if (!called_connected) mysql_close(mysql); if (errmsg && thd->vio_ok()) @@ -1721,26 +1727,31 @@ static int has_temporary_error(THD *thd) DBUG_ENTER("has_temporary_error"); if (thd->is_fatal_error) - { - DBUG_PRINT("info", ("thd->net.last_errno: %s", ER(thd->net.last_errno))); DBUG_RETURN(0); - } DBUG_EXECUTE_IF("all_errors_are_temporary_errors", - if (thd->net.last_errno) - thd->net.last_errno= ER_LOCK_DEADLOCK;); + if (thd->main_da.is_error()) + { + thd->clear_error(); + my_error(ER_LOCK_DEADLOCK, MYF(0)); + }); + + /* + If there is no message in THD, we can't say if it's a temporary + error or not. This is currently the case for Incident_log_event, + which sets no message. Return FALSE. + */ + if (!thd->is_error()) + DBUG_RETURN(0); /* Temporary error codes: currently, InnoDB deadlock detected by InnoDB or lock wait timeout (innodb_lock_wait_timeout exceeded */ - if (thd->net.last_errno == ER_LOCK_DEADLOCK || - thd->net.last_errno == ER_LOCK_WAIT_TIMEOUT) - { - DBUG_PRINT("info", ("thd->net.last_errno: %s", ER(thd->net.last_errno))); + if (thd->main_da.sql_errno() == ER_LOCK_DEADLOCK || + thd->main_da.sql_errno() == ER_LOCK_WAIT_TIMEOUT) DBUG_RETURN(1); - } #ifdef HAVE_NDB_BINLOG /* @@ -2551,20 +2562,21 @@ Slave SQL thread aborted. Can't execute init_slave query"); */ uint32 const last_errno= rli->last_error().number; - DBUG_PRINT("info", ("thd->net.last_errno=%d; rli->last_error.number=%d", - thd->net.last_errno, last_errno)); - if (thd->net.last_errno != 0) + if (thd->is_error()) { - char const *const errmsg= - thd->net.last_error ? thd->net.last_error : "<no message>"; + char const *const errmsg= thd->main_da.message(); + + DBUG_PRINT("info", + ("thd->main_da.sql_errno()=%d; rli->last_error.number=%d", + thd->main_da.sql_errno(), last_errno)); if (last_errno == 0) { - rli->report(ERROR_LEVEL, thd->net.last_errno, errmsg); + rli->report(ERROR_LEVEL, thd->main_da.sql_errno(), errmsg); } - else if (last_errno != thd->net.last_errno) + else if (last_errno != thd->main_da.sql_errno()) { sql_print_error("Slave (additional info): %s Error_code: %d", - errmsg, thd->net.last_errno); + errmsg, thd->main_da.sql_errno()); } } diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 4b432cef5cd..2e36593a126 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1202,12 +1202,6 @@ sp_head::execute(THD *thd) err_status= i->execute(thd, &ip); - /* - If this SP instruction have sent eof, it has caused no_send_error to be - set. Clear it back to allow the next instruction to send error. (multi- - statement execution code clears no_send_error between statements too) - */ - thd->net.no_send_error= 0; if (i->free_list) cleanup_items(i->free_list); @@ -2762,14 +2756,22 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) thd->query, thd->query_length) <= 0) { res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this); + + if (thd->main_da.is_eof()) + net_end_statement(thd); + + query_cache_end_of_result(thd); + if (!res && unlikely(thd->enable_slow_log)) log_slow_statement(thd); - query_cache_end_of_result(thd); } else *nextp= m_ip+1; thd->query= query; thd->query_length= query_length; + + if (!thd->is_error()) + thd->main_da.reset_diagnostics_area(); } DBUG_RETURN(res); } diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 70a043c4fac..8395648689b 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -287,7 +287,6 @@ sp_rcontext::find_handler(THD *thd, uint sql_errno, sql_errno The error code level Warning level thd The current thread - - thd->net.report_error is an optional output. RETURN TRUE if a handler was found. @@ -298,7 +297,6 @@ sp_rcontext::handle_error(uint sql_errno, MYSQL_ERROR::enum_warning_level level, THD *thd) { - bool handled= FALSE; MYSQL_ERROR::enum_warning_level elevated_level= level; @@ -310,25 +308,7 @@ sp_rcontext::handle_error(uint sql_errno, elevated_level= MYSQL_ERROR::WARN_LEVEL_ERROR; } - if (find_handler(thd, sql_errno, elevated_level)) - { - if (elevated_level == MYSQL_ERROR::WARN_LEVEL_ERROR) - { - /* - Forces to abort the current instruction execution. - NOTE: This code is altering the original meaning of - the net.report_error flag (send an error to the client). - In the context of stored procedures with error handlers, - the flag is reused to cause error propagation, - until the error handler is reached. - No messages will be sent to the client in that context. - */ - thd->net.report_error= 1; - } - handled= TRUE; - } - - return handled; + return find_handler(thd, sql_errno, elevated_level); } void diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 519ca429aa6..9f6442d5c49 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -695,7 +695,7 @@ my_bool acl_reload(THD *thd) if (simple_open_n_lock_tables(thd, tables)) { sql_print_error("Fatal error: Can't open and lock privilege tables: %s", - thd->net.last_error); + thd->main_da.message()); goto end; } @@ -3800,11 +3800,11 @@ my_bool grant_reload(THD *thd) close_thread_tables(thd); /* - It is ok failing to load procs_priv table because we may be + It is OK failing to load procs_priv table because we may be working with 4.1 privilege tables. */ if (grant_reload_procs_priv(thd)) - my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "mysql.procs_priv"); + return_val= 1; rw_wrlock(&LOCK_grant); grant_version++; @@ -5693,9 +5693,6 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list) if (result) my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe()); - DBUG_PRINT("info", ("thd->net.last_errno: %d", thd->net.last_errno)); - DBUG_PRINT("info", ("thd->net.last_error: %s", thd->net.last_error)); - write_bin_log(thd, FALSE, thd->query, thd->query_length); rw_unlock(&LOCK_grant); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index ba8b7fc1330..b15082f1f6c 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -491,9 +491,28 @@ static TABLE_SHARE int tmp; DBUG_ENTER("get_table_share_with_create"); - if ((share= get_table_share(thd, table_list, key, key_length, - db_flags, error)) || - thd->net.last_errno != ER_NO_SUCH_TABLE) + share= get_table_share(thd, table_list, key, key_length, db_flags, error); + /* + If share is not NULL, we found an existing share. + + If share is NULL, and there is no error, we're inside + pre-locking, which silences 'ER_NO_SUCH_TABLE' errors + with the intention to silently drop non-existing tables + from the pre-locking list. In this case we still need to try + auto-discover before returning a NULL share. + + If share is NULL and the error is ER_NO_SUCH_TABLE, this is + the same as above, only that the error was not silenced by + pre-locking. Once again, we need to try to auto-discover + the share. + + Finally, if share is still NULL, it's a real error and we need + to abort. + + @todo Rework alternative ways to deal with ER_NO_SUCH TABLE. + */ + if (share || thd->is_error() && thd->main_da.sql_errno() != ER_NO_SUCH_TABLE) + DBUG_RETURN(share); /* Table didn't exist. Check if some engine can provide it */ @@ -502,9 +521,13 @@ static TABLE_SHARE { /* No such table in any engine. - Hide "Table doesn't exist" errors if table belong to view + Hide "Table doesn't exist" errors if the table belongs to a view. + The check for thd->is_error() is necessary to not push an + unwanted error in case of pre-locking, which silences + "no such table" errors. + @todo Rework the alternative ways to deal with ER_NO_SUCH TABLE. */ - if (table_list->belong_to_view) + if (thd->is_error() && table_list->belong_to_view) { TABLE_LIST *view= table_list->belong_to_view; thd->clear_error(); diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index fa6aa8f5881..77c5155b41b 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -40,13 +40,6 @@ void mysql_client_binlog_statement(THD* thd) if (check_global_access(thd, SUPER_ACL)) DBUG_VOID_RETURN; - /* - Temporarily turn off send_ok, since different events handle this - differently - */ - my_bool nsok= thd->net.no_send_ok; - thd->net.no_send_ok= TRUE; - size_t coded_len= thd->lex->comment.length + 1; size_t decoded_len= base64_needed_decoded_length(coded_len); DBUG_ASSERT(coded_len > 0); @@ -193,20 +186,11 @@ void mysql_client_binlog_statement(THD* thd) } } - /* - Restore setting of no_send_ok - */ - thd->net.no_send_ok= nsok; DBUG_PRINT("info",("binlog base64 execution finished successfully")); send_ok(thd); end: - /* - Restore setting of no_send_ok - */ - thd->net.no_send_ok= nsok; - delete desc; my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); DBUG_VOID_RETURN; diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 56b048a4f9d..f1803a329c5 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1403,6 +1403,7 @@ def_week_frmt: %lu", thd->limit_found_rows = query->found_rows(); thd->status_var.last_query_cost= 0.0; + thd->main_da.disable_status(); BLOCK_UNLOCK_RD(query_block); DBUG_RETURN(1); // Result sent to client diff --git a/sql/sql_class.cc b/sql/sql_class.cc index f59a848a242..b68a532a2dc 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -351,6 +351,124 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length, return thd->strmake(str.ptr(), str.length()); } +/** + Clear this diagnostics area. + + Normally called at the end of a statement. +*/ + +void +Diagnostics_area::reset_diagnostics_area() +{ +#ifdef DBUG_OFF + can_overwrite_status= FALSE; + /** Don't take chances in production */ + m_message[0]= '\0'; + m_sql_errno= 0; + m_server_status= 0; + m_affected_rows= 0; + m_last_insert_id= 0; + m_total_warn_count= 0; +#endif + is_sent= FALSE; + /** Tiny reset in debug mode to see garbage right away */ + m_status= DA_EMPTY; +} + + +/** + Set OK status -- ends commands that do not return a + result set, e.g. INSERT/UPDATE/DELETE. +*/ + +void +Diagnostics_area::set_ok_status(THD *thd, ha_rows affected_rows_arg, + ulong last_insert_id_arg, + const char *message_arg) +{ + DBUG_ASSERT(! is_set()); +#ifdef DBUG_OFF + /* In production, refuse to overwrite an error with an OK packet. */ + if (is_error()) + return; +#endif + /** Only allowed to report success if has not yet reported an error */ + + m_server_status= thd->server_status; + m_total_warn_count= thd->total_warn_count; + m_affected_rows= affected_rows_arg; + m_last_insert_id= last_insert_id_arg; + if (message_arg) + strmake(m_message, message_arg, sizeof(m_message)); + else + m_message[0]= '\0'; + m_status= DA_OK; +} + + +/** + Set EOF status. +*/ + +void +Diagnostics_area::set_eof_status(THD *thd) +{ + /** Only allowed to report eof if has not yet reported an error */ + + DBUG_ASSERT(! is_set()); +#ifdef DBUG_OFF + /* In production, refuse to overwrite an error with an EOF packet. */ + if (is_error()) + return; +#endif + + m_server_status= thd->server_status; + /* + If inside a stored procedure, do not return the total + number of warnings, since they are not available to the client + anyway. + */ + m_total_warn_count= thd->spcont ? 0 : thd->total_warn_count; + + m_status= DA_EOF; +} + +/** + Set ERROR status. +*/ + +void +Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg, + const char *message_arg) +{ + /* + Only allowed to report error if has not yet reported a success + The only exception is when we flush the message to the client, + an error can happen during the flush. + */ + DBUG_ASSERT(! is_set() || can_overwrite_status); + + m_sql_errno= sql_errno_arg; + strmake(m_message, message_arg, sizeof(m_message)); + + m_status= DA_ERROR; +} + + +/** + Mark the diagnostics area as 'DISABLED'. + + This is used in rare cases when the COM_ command at hand sends a response + in a custom format. One example is the query cache, another is + COM_STMT_PREPARE. +*/ + +void +Diagnostics_area::disable_status() +{ + DBUG_ASSERT(! is_set()); + m_status= DA_DISABLED; +} THD::THD() @@ -431,7 +549,6 @@ THD::THD() net.vio=0; #endif client_capabilities= 0; // minimalistic client - net.last_error[0]=0; // If error on boot #ifdef HAVE_QUERY_CACHE query_cache_init_query(&net); // If error on boot #endif @@ -1324,12 +1441,12 @@ void select_send::abort() { DBUG_ENTER("select_send::abort"); if (is_result_set_started && thd->spcont && - thd->spcont->find_handler(thd, thd->net.last_errno, + thd->spcont->find_handler(thd, thd->main_da.sql_errno(), MYSQL_ERROR::WARN_LEVEL_ERROR)) { /* We're executing a stored procedure, have an open result - set, an SQL exception conditiona and a handler for it. + set, an SQL exception condition and a handler for it. In this situation we must abort the current statement, silence the error and start executing the continue/exit handler. @@ -1337,9 +1454,7 @@ void select_send::abort() otherwise the client will hang due to the violation of the client/server protocol. */ - thd->net.report_error= 0; - send_eof(); - thd->net.report_error= 1; // Abort SP + thd->protocol->end_partial_result_set(thd); } DBUG_VOID_RETURN; } @@ -1391,12 +1506,14 @@ bool select_send::send_data(List<Item> &items) } } thd->sent_row_count++; - if (!thd->vio_ok()) - DBUG_RETURN(0); - if (! thd->is_error()) + if (thd->is_error()) + { + protocol->remove_last_row(); + DBUG_RETURN(1); + } + if (thd->vio_ok()) DBUG_RETURN(protocol->write()); - protocol->remove_last_row(); - DBUG_RETURN(1); + DBUG_RETURN(0); } bool select_send::send_eof() @@ -1414,14 +1531,9 @@ bool select_send::send_eof() mysql_unlock_tables(thd, thd->lock); thd->lock=0; } - if (! thd->is_error()) - { - ::send_eof(thd); - is_result_set_started= 0; - return 0; - } - else - return 1; + ::send_eof(thd); + is_result_set_started= 0; + return FALSE; } @@ -2701,7 +2813,6 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, { backup->options= options; backup->in_sub_stmt= in_sub_stmt; - backup->no_send_ok= net.no_send_ok; backup->enable_slow_log= enable_slow_log; backup->limit_found_rows= limit_found_rows; backup->examined_row_count= examined_row_count; @@ -2732,9 +2843,6 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, cuted_fields= 0; transaction.savepoints= 0; first_successful_insert_id_in_cur_stmt= 0; - - /* Surpress OK packets in case if we will execute statements */ - net.no_send_ok= TRUE; } @@ -2757,7 +2865,6 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) transaction.savepoints= backup->savepoints; options= backup->options; in_sub_stmt= backup->in_sub_stmt; - net.no_send_ok= backup->no_send_ok; enable_slow_log= backup->enable_slow_log; first_successful_insert_id_in_prev_stmt= backup->first_successful_insert_id_in_prev_stmt; diff --git a/sql/sql_class.h b/sql/sql_class.h index c5b70cfa687..d46557acb4c 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -913,7 +913,6 @@ public: uint in_sub_stmt; bool enable_slow_log; bool last_insert_id_used; - my_bool no_send_ok; SAVEPOINT *savepoints; }; @@ -976,6 +975,123 @@ public: /** + Stores status of the currently executed statement. + Cleared at the beginning of the statement, and then + can hold either OK, ERROR, or EOF status. + Can not be assigned twice per statement. +*/ + +class Diagnostics_area +{ +public: + enum enum_diagnostics_status + { + /** The area is cleared at start of a statement. */ + DA_EMPTY= 0, + /** Set whenever one calls send_ok(). */ + DA_OK, + /** Set whenever one calls send_eof(). */ + DA_EOF, + /** Set whenever one calls my_error() or my_message(). */ + DA_ERROR, + /** Set in case of a custom response, such as one from COM_STMT_PREPARE. */ + DA_DISABLED + }; + /** True if status information is sent to the client. */ + bool is_sent; + /** Set to make set_error_status after set_{ok,eof}_status possible. */ + bool can_overwrite_status; + + void set_ok_status(THD *thd, ha_rows affected_rows_arg, + ulong last_insert_id_arg, + const char *message); + void set_eof_status(THD *thd); + void set_error_status(THD *thd, uint sql_errno_arg, const char *message_arg); + + void disable_status(); + + void reset_diagnostics_area(); + + bool is_set() const { return m_status != DA_EMPTY; } + bool is_error() const { return m_status == DA_ERROR; } + bool is_eof() const { return m_status == DA_EOF; } + bool is_ok() const { return m_status == DA_OK; } + bool is_disabled() const { return m_status == DA_DISABLED; } + enum_diagnostics_status status() const { return m_status; } + + const char *message() const + { DBUG_ASSERT(m_status == DA_ERROR || m_status == DA_OK); return m_message; } + + uint sql_errno() const + { DBUG_ASSERT(m_status == DA_ERROR); return m_sql_errno; } + + uint server_status() const + { + DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF); + return m_server_status; + } + + ha_rows affected_rows() const + { DBUG_ASSERT(m_status == DA_OK); return m_affected_rows; } + + ulong last_insert_id() const + { DBUG_ASSERT(m_status == DA_OK); return m_last_insert_id; } + + uint total_warn_count() const + { + DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF); + return m_total_warn_count; + } + + Diagnostics_area() { reset_diagnostics_area(); } + +private: + /** Message buffer. Can be used by OK or ERROR status. */ + char m_message[MYSQL_ERRMSG_SIZE]; + /** + SQL error number. One of ER_ codes from share/errmsg.txt. + Set by set_error_status. + */ + uint m_sql_errno; + + /** + Copied from thd->server_status when the diagnostics area is assigned. + We need this member as some places in the code use the following pattern: + thd->server_status|= ... + send_eof(thd); + thd->server_status&= ~... + Assigned by OK, EOF or ERROR. + */ + uint m_server_status; + /** + The number of rows affected by the last statement. This is + semantically close to thd->row_count_func, but has a different + life cycle. thd->row_count_func stores the value returned by + function ROW_COUNT() and is cleared only by statements that + update its value, such as INSERT, UPDATE, DELETE and few others. + This member is cleared at the beginning of the next statement. + + We could possibly merge the two, but life cycle of thd->row_count_func + can not be changed. + */ + ha_rows m_affected_rows; + /** + Similarly to the previous member, this is a replacement of + thd->first_successful_insert_id_in_prev_stmt, which is used + to implement LAST_INSERT_ID(). + */ + ulong m_last_insert_id; + /** The total number of warnings. */ + uint m_total_warn_count; + enum_diagnostics_status m_status; + /** + @todo: the following THD members belong here: + - warn_list, warn_count, + */ +}; + + +/** @class THD For each client connection we create a separate thread with THD serving as a thread/connection descriptor @@ -1400,6 +1516,7 @@ public: List <MYSQL_ERROR> warn_list; uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END]; uint total_warn_count; + Diagnostics_area main_da; /* Id of current query. Statement can be reused to execute several queries query_id is global in context of the whole MySQL server. @@ -1714,12 +1831,18 @@ public: CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length); int send_explain_fields(select_result *result); #ifndef EMBEDDED_LIBRARY + /** + Clear the current error, if any. + We do not clear is_fatal_error or is_fatal_sub_stmt_error since we + assume this is never called if the fatal error is set. + @todo: To silence an error, one should use Internal_error_handler + mechanism. In future this function will be removed. + */ inline void clear_error() { DBUG_ENTER("clear_error"); - net.last_error[0]= 0; - net.last_errno= 0; - net.report_error= 0; + if (main_da.is_error()) + main_da.reset_diagnostics_area(); is_slave_error= 0; DBUG_VOID_RETURN; } @@ -1728,10 +1851,14 @@ public: void clear_error(); inline bool vio_ok() const { return true; } #endif + /** + Mark the current error as fatal. Warning: this does not + set any error, it sets a property of the error, so must be + followed or prefixed with my_error(). + */ inline void fatal_error() { is_fatal_error= 1; - net.report_error= 1; DBUG_PRINT("error",("Fatal error set")); } /** @@ -1747,7 +1874,7 @@ public: To raise this flag, use my_error(). */ - inline bool is_error() const { return net.report_error; } + inline bool is_error() const { return main_da.is_error(); } inline CHARSET_INFO *charset() { return variables.character_set_client; } void update_charset(); @@ -1971,6 +2098,24 @@ private: }; +/** A short cut for thd->main_da.set_ok_status(). */ + +inline void +send_ok(THD *thd, ha_rows affected_rows= 0, ulonglong id= 0, + const char *message= NULL) +{ + thd->main_da.set_ok_status(thd, affected_rows, id, message); +} + + +/** A short cut for thd->main_da.set_eof_status(). */ + +inline void +send_eof(THD *thd) +{ + thd->main_da.set_eof_status(thd); +} + #define tmp_disable_binlog(A) \ {ulonglong tmp_disable_binlog__save_options= (A)->options; \ (A)->options&= ~OPTION_BIN_LOG @@ -2508,6 +2653,7 @@ public: void send_error(uint errcode,const char *err); int do_deletes(); bool send_eof(); + virtual void abort(); }; @@ -2550,6 +2696,7 @@ public: void send_error(uint errcode,const char *err); int do_updates (bool from_send_error); bool send_eof(); + virtual void abort(); }; class my_var : public Sql_alloc { diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 76237576764..309a1c7ab5d 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -948,19 +948,20 @@ bool setup_connection_thread_globals(THD *thd) bool login_connection(THD *thd) { NET *net= &thd->net; + int error; DBUG_ENTER("login_connection"); DBUG_PRINT("info", ("login_connection called by thread %lu", thd->thread_id)); - net->no_send_error= 0; - /* Use "connect_timeout" value during connection phase */ my_net_set_read_timeout(net, connect_timeout); my_net_set_write_timeout(net, connect_timeout); - if (check_connection(thd)) + error= check_connection(thd); + net_end_statement(thd); + + if (error) { // Wrong permissions - net_send_error(thd); #ifdef __NT__ if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE) my_sleep(1000); /* must wait after eof() */ @@ -989,13 +990,12 @@ void end_connection(THD *thd) if (thd->user_connect) decrease_user_connections(thd->user_connect); - if (thd->killed || - net->error && net->vio != 0 && thd->is_error()) + if (thd->killed || net->error && net->vio != 0) { statistic_increment(aborted_threads,&LOCK_status); } - if (net->error && net->vio != 0 && thd->is_error()) + if (net->error && net->vio != 0) { if (!thd->killed && thd->variables.log_warnings > 1) { @@ -1005,11 +1005,9 @@ void end_connection(THD *thd) thd->thread_id,(thd->db ? thd->db : "unconnected"), sctx->user ? sctx->user : "unauthenticated", sctx->host_or_ip, - (net->last_errno ? ER(net->last_errno) : + (thd->main_da.is_error() ? thd->main_da.message() : ER(ER_UNKNOWN_ERROR))); } - - net_send_error(thd, net->last_errno, NullS); } } @@ -1045,24 +1043,14 @@ static void prepare_new_connection_state(THD* thd) if (sys_init_connect.value_length && !(sctx->master_access & SUPER_ACL)) { execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect); - /* - execute_init_command calls net_send_error. - If there was an error during execution of the init statements, - the error at this moment is present in thd->net.last_error and also - thd->is_slave_error and thd->net.report_error are set. - net_send_error sends the contents of thd->net.last_error and - clears thd->net.report_error. It doesn't, however, clean - thd->is_slave_error or thd->net.last_error. Here we make use of this - fact. - */ - if (thd->is_slave_error) + if (thd->is_error()) { thd->killed= THD::KILL_CONNECTION; sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), thd->thread_id,(thd->db ? thd->db : "unconnected"), sctx->user ? sctx->user : "unauthenticated", sctx->host_or_ip, "init_connect command failed"); - sql_print_warning("%s", thd->net.last_error); + sql_print_warning("%s", thd->main_da.message()); } thd->proc_info=0; thd->set_time(); @@ -1129,7 +1117,6 @@ pthread_handler_t handle_one_connection(void *arg) while (!net->error && net->vio != 0 && !(thd->killed == THD::KILL_CONNECTION)) { - net->no_send_error= 0; if (do_command(thd)) break; } diff --git a/sql/sql_db.cc b/sql/sql_db.cc index ad4e0d803eb..d425510320e 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -921,6 +921,8 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) { ha_drop_database(path); query_cache_invalidate1(db); + (void) sp_drop_db_routines(thd, db); /* @todo Do not ignore errors */ + Events::drop_schema_events(thd, db); error = 0; } } @@ -956,6 +958,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) /* These DDL methods and logging protected with LOCK_mysql_create_db */ mysql_bin_log.write(&qinfo); } + thd->clear_error(); thd->server_status|= SERVER_STATUS_DB_DROPPED; send_ok(thd, (ulong) deleted); thd->server_status&= ~SERVER_STATUS_DB_DROPPED; @@ -999,8 +1002,6 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) } exit: - (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */ - Events::drop_schema_events(thd, db); /* If this database was the client's selected database, we silently change the client's selected database to nothing (to have an empty diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 509e736f6e7..a73963d7f86 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -728,6 +728,14 @@ void multi_delete::send_error(uint errcode,const char *err) /* First send error what ever it is ... */ my_message(errcode, err, MYF(0)); + DBUG_VOID_RETURN; +} + + +void multi_delete::abort() +{ + DBUG_ENTER("multi_delete::abort"); + /* the error was handled or nothing deleted and no side effects return */ if (error_handled || !thd->transaction.stmt.modified_non_trans_table && !deleted) diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index ea7545fe5cb..10b42e11b26 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -147,8 +147,9 @@ exit: /* Hide "Unknown column" or "Unknown function" error */ if (orig_table_list->view) { - if (thd->net.last_errno == ER_BAD_FIELD_ERROR || - thd->net.last_errno == ER_SP_DOES_NOT_EXIST) + if (thd->is_error() && + (thd->main_da.sql_errno() == ER_BAD_FIELD_ERROR || + thd->main_da.sql_errno() == ER_SP_DOES_NOT_EXIST)) { thd->clear_error(); my_error(ER_VIEW_INVALID, MYF(0), orig_table_list->db, diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 425c5a33329..051950a0974 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1916,7 +1916,7 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) main thread. Use of my_message will enable stored procedures continue handlers. */ - my_message(di->thd.net.last_errno, di->thd.net.last_error, + my_message(di->thd.main_da.sql_errno(), di->thd.main_da.message(), MYF(0)); } di->unlock(); @@ -1993,7 +1993,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) goto error; if (dead) { - my_message(thd.net.last_errno, thd.net.last_error, MYF(0)); + my_message(thd.main_da.sql_errno(), thd.main_da.message(), MYF(0)); goto error; } } @@ -2252,7 +2252,9 @@ pthread_handler_t handle_delayed_insert(void *arg) #if !defined( __WIN__) /* Win32 calls this in pthread_create */ if (my_thread_init()) { - strmov(thd->net.last_error,ER(thd->net.last_errno=ER_OUT_OF_RESOURCES)); + /* Can't use my_error since store_globals has not yet been called */ + thd->main_da.set_error_status(thd, ER_OUT_OF_RESOURCES, + ER(ER_OUT_OF_RESOURCES)); goto end; } #endif @@ -2261,8 +2263,10 @@ pthread_handler_t handle_delayed_insert(void *arg) thd->thread_stack= (char*) &thd; if (init_thr_lock() || thd->store_globals()) { + /* Can't use my_error since store_globals has perhaps failed */ + thd->main_da.set_error_status(thd, ER_OUT_OF_RESOURCES, + ER(ER_OUT_OF_RESOURCES)); thd->fatal_error(); - strmov(thd->net.last_error,ER(thd->net.last_errno=ER_OUT_OF_RESOURCES)); goto err; } @@ -2665,7 +2669,7 @@ bool Delayed_insert::handle_inserts(void) { /* This should never happen */ table->file->print_error(error,MYF(0)); - sql_print_error("%s",thd.net.last_error); + sql_print_error("%s", thd.main_da.message()); DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed in loop")); goto err; } @@ -2706,7 +2710,7 @@ bool Delayed_insert::handle_inserts(void) if ((error=table->file->extra(HA_EXTRA_NO_CACHE))) { // This shouldn't happen table->file->print_error(error,MYF(0)); - sql_print_error("%s",thd.net.last_error); + sql_print_error("%s", thd.main_da.message()); DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed after loop")); goto err; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index df425557bdf..58084bdd067 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -328,7 +328,6 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var, */ save_vio= thd->net.vio; thd->net.vio= 0; - thd->net.no_send_error= 0; dispatch_command(COM_QUERY, thd, init_command_var->value, init_command_var->value_length); @@ -397,8 +396,8 @@ pthread_handler_t handle_bootstrap(void *arg) /* purecov: begin tested */ if (net_realloc(&(thd->net), 2 * thd->net.max_packet)) { - net_send_error(thd, ER_NET_PACKET_TOO_LARGE, NullS); - thd->fatal_error(); + net_end_statement(thd); + bootstrap_error= 1; break; } buff= (char*) thd->net.buff; @@ -406,7 +405,7 @@ pthread_handler_t handle_bootstrap(void *arg) length+= (ulong) strlen(buff + length); /* purecov: end */ } - if (thd->is_fatal_error) + if (bootstrap_error) break; /* purecov: inspected */ while (length && (my_isspace(thd->charset(), buff[length-1]) || @@ -433,16 +432,11 @@ pthread_handler_t handle_bootstrap(void *arg) mysql_parse(thd, thd->query, length, & found_semicolon); close_thread_tables(thd); // Free tables - if (thd->is_fatal_error) - break; + bootstrap_error= thd->is_error(); + net_end_statement(thd); - if (thd->is_error()) - { - /* The query failed, send error to log and abort bootstrap */ - net_send_error(thd); - thd->fatal_error(); + if (bootstrap_error) break; - } free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); #ifdef USING_TRANSACTIONS @@ -451,9 +445,6 @@ pthread_handler_t handle_bootstrap(void *arg) } end: - /* Remember the exit code of bootstrap */ - bootstrap_error= thd->is_fatal_error; - net_end(&thd->net); thd->cleanup(); delete thd; @@ -712,7 +703,12 @@ bool do_command(THD *thd) */ my_net_set_read_timeout(net, thd->variables.net_wait_timeout); + /* + XXX: this code is here only to clear possible errors of init_connect. + Consider moving to init_connect() instead. + */ thd->clear_error(); // Clear error message + thd->main_da.reset_diagnostics_area(); net_new_transaction(net); if ((packet_length=my_net_read(net)) == packet_error) @@ -723,10 +719,13 @@ bool do_command(THD *thd) /* Check if we can continue without closing the connection */ + /* The error must be set. */ + DBUG_ASSERT(thd->is_error()); + net_end_statement(thd); + if (net->error != 3) DBUG_RETURN(TRUE); // We have to close it. - net_send_error(thd, net->last_errno, NullS); net->error= 0; DBUG_RETURN(FALSE); } @@ -860,7 +859,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, db.length= db_len; tbl_name= strmake(db.str, packet + 1, db_len)+1; strmake(tbl_name, packet + db_len + 2, tbl_len); - mysql_table_dump(thd, &db, tbl_name); + if (mysql_table_dump(thd, &db, tbl_name) == 0) + thd->main_da.disable_status(); break; } case COM_CHANGE_USER: @@ -1024,7 +1024,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, while (!thd->killed && found_semicolon && ! thd->is_error()) { char *next_packet= (char*) found_semicolon; - net->no_send_error= 0; + + net_end_statement(thd); + query_cache_end_of_result(thd); /* Multiple queries exits, execute them individually */ @@ -1125,6 +1127,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* We don't calculate statistics for this command */ general_log_print(thd, command, NullS); net->error=0; // Don't give 'abort' message + thd->main_da.disable_status(); // Don't send anything back error=TRUE; // End server break; @@ -1241,16 +1244,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, DBUG_PRINT("quit",("Got shutdown command for level %u", level)); general_log_print(thd, command, NullS); send_eof(thd); -#ifdef __WIN__ - sleep(1); // must wait after eof() -#endif - /* - The client is next going to send a COM_QUIT request (as part of - mysql_close()). Make the life simpler for the client by sending - the response for the coming COM_QUIT in advance - */ - send_eof(thd); - close_connection(thd, 0, 1); close_thread_tables(thd); // Free before kill kill_mysql(); error=TRUE; @@ -1263,13 +1256,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ulong uptime; uint length; ulonglong queries_per_second1000; -#ifndef EMBEDDED_LIBRARY char buff[250]; uint buff_len= sizeof(buff); -#else - char *buff= thd->net.last_error; - uint buff_len= sizeof(thd->net.last_error); -#endif general_log_print(thd, command, NullS); status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS]); @@ -1291,6 +1279,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, cached_open_tables(), (uint) (queries_per_second1000 / 1000), (uint) (queries_per_second1000 % 1000)); +#ifdef EMBEDDED_LIBRARY + /* Store the buffer in permanent memory */ + send_ok(thd, 0, 0, buff); +#endif #ifdef SAFEMALLOC if (sf_malloc_cur_memory) // Using SAFEMALLOC { @@ -1303,7 +1295,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #endif #ifndef EMBEDDED_LIBRARY VOID(my_net_write(net, (uchar*) buff, length)); - VOID(net_flush(net)); + VOID(net_flush(net)); + thd->main_da.disable_status(); #endif break; } @@ -1383,10 +1376,19 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->transaction.xid_state.xid.null(); /* report error issued during command execution */ - if (thd->killed_errno() && ! thd->is_error()) - thd->send_kill_message(); - if (thd->is_error()) - net_send_error(thd); + if (thd->killed_errno()) + { + if (! thd->main_da.is_set()) + thd->send_kill_message(); + } + if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA) + { + thd->killed= THD::NOT_KILLED; + thd->mysys_var->abort= 0; + } + + net_end_statement(thd); + query_cache_end_of_result(thd); log_slow_statement(thd); @@ -1756,7 +1758,6 @@ mysql_execute_command(THD *thd) SELECT_LEX_UNIT *unit= &lex->unit; /* Saved variable value */ DBUG_ENTER("mysql_execute_command"); - thd->net.no_send_error= 0; #ifdef WITH_PARTITION_STORAGE_ENGINE thd->work_part_info= 0; #endif @@ -2992,13 +2993,9 @@ end_with_restore_list: SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE, del_result, unit, select_lex); - res|= thd->net.report_error; - if (unlikely(res)) - { - /* If we had a another error reported earlier then this will be ignored */ - del_result->send_error(ER_UNKNOWN_ERROR, "Execution of the query failed"); + res|= thd->is_error(); + if (res) del_result->abort(); - } delete del_result; } else @@ -3931,8 +3928,6 @@ create_sp_error: goto error; } - my_bool save_no_send_ok= thd->net.no_send_ok; - thd->net.no_send_ok= TRUE; if (sp->m_flags & sp_head::MULTI_RESULTS) { if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS)) @@ -3942,7 +3937,6 @@ create_sp_error: back */ my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str); - thd->net.no_send_ok= save_no_send_ok; goto error; } /* @@ -3958,7 +3952,6 @@ create_sp_error: if (check_routine_access(thd, EXECUTE_ACL, sp->m_db.str, sp->m_name.str, TRUE, FALSE)) { - thd->net.no_send_ok= save_no_send_ok; goto error; } #endif @@ -3983,7 +3976,6 @@ create_sp_error: thd->variables.select_limit= select_limit; - thd->net.no_send_ok= save_no_send_ok; thd->server_status&= ~bits_to_be_cleared; if (!res) @@ -4624,7 +4616,10 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_YES, str.ptr()); } - result->send_eof(); + if (res) + result->abort(); + else + result->send_eof(); delete result; } else @@ -5251,6 +5246,7 @@ void mysql_reset_thd_for_next_command(THD *thd) { DBUG_ENTER("mysql_reset_thd_for_next_command"); DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */ + DBUG_ASSERT(! thd->in_sub_stmt); thd->free_list= 0; thd->select_number= 1; /* @@ -5277,18 +5273,18 @@ void mysql_reset_thd_for_next_command(THD *thd) } DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx); thd->thread_specific_used= FALSE; - if (!thd->in_sub_stmt) + + if (opt_bin_log) { - if (opt_bin_log) - { - reset_dynamic(&thd->user_var_events); - thd->user_var_events_alloc= thd->mem_root; - } - thd->clear_error(); - thd->total_warn_count=0; // Warnings for this query - thd->rand_used= 0; - thd->sent_row_count= thd->examined_row_count= 0; + reset_dynamic(&thd->user_var_events); + thd->user_var_events_alloc= thd->mem_root; } + thd->clear_error(); + thd->main_da.reset_diagnostics_area(); + thd->total_warn_count=0; // Warnings for this query + thd->rand_used= 0; + thd->sent_row_count= thd->examined_row_count= 0; + /* Because we come here only for start of top-statements, binlog format is constant inside a complex statement (using stored functions) etc. @@ -5526,7 +5522,6 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, /* Actually execute the query */ lex->set_trg_event_type_for_tables(); mysql_execute_command(thd); - query_cache_end_of_result(thd); } } } @@ -6378,8 +6373,10 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, } if (thd) { - (void)acl_reload(thd); - (void)grant_reload(thd); + if (acl_reload(thd)) + result= 1; + if (grant_reload(thd)) + result= 1; } if (tmp_thd) { @@ -6496,7 +6493,6 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, if (reset_master(thd)) { result=1; - thd->fatal_error(); // Ensure client get error } } #endif diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 64f96f342df..8ded0cacd77 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -2068,6 +2068,7 @@ char *generate_partition_syntax(partition_info *part_info, default: DBUG_ASSERT(0); /* We really shouldn't get here, no use in continuing from here */ + my_error(ER_OUT_OF_RESOURCES, MYF(0)); current_thd->fatal_error(); DBUG_RETURN(NULL); } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 9d4d62e57b6..52e6fcc5d58 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -230,6 +230,8 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns) NET *net= &stmt->thd->net; uchar buff[12]; uint tmp; + int error; + THD *thd= stmt->thd; DBUG_ENTER("send_prep_stmt"); buff[0]= 0; /* OK packet indicator */ @@ -244,11 +246,16 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns) Send types and names of placeholders to the client XXX: fix this nasty upcast from List<Item_param> to List<Item> */ - DBUG_RETURN(my_net_write(net, buff, sizeof(buff)) || - (stmt->param_count && - stmt->thd->protocol_text.send_fields((List<Item> *) - &stmt->lex->param_list, - Protocol::SEND_EOF))); + error= my_net_write(net, buff, sizeof(buff)); + if (stmt->param_count && ! error) + { + error= thd->protocol_text.send_fields((List<Item> *) + &stmt->lex->param_list, + Protocol::SEND_EOF); + } + /* Flag that a response has already been sent */ + thd->main_da.disable_status(); + DBUG_RETURN(error); } #else static bool send_prep_stmt(Prepared_statement *stmt, @@ -259,6 +266,7 @@ static bool send_prep_stmt(Prepared_statement *stmt, thd->client_stmt_id= stmt->id; thd->client_param_count= stmt->param_count; thd->clear_error(); + thd->main_da.disable_status(); return 0; } @@ -2526,6 +2534,8 @@ void mysql_stmt_close(THD *thd, char *packet) DBUG_ASSERT(! (stmt->flags & (uint) Prepared_statement::IS_IN_USE)); (void) stmt->deallocate(); + thd->main_da.disable_status(); + DBUG_VOID_RETURN; } @@ -2590,6 +2600,8 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) DBUG_ENTER("mysql_stmt_get_longdata"); status_var_increment(thd->status_var.com_stmt_send_long_data); + + thd->main_da.disable_status(); #ifndef EMBEDDED_LIBRARY /* Minimal size of long data packet is 6 bytes */ if (packet_length < MYSQL_LONG_DATA_HEADER) @@ -2664,11 +2676,7 @@ bool Select_fetch_protocol_binary::send_fields(List<Item> &list, uint flags) bool Select_fetch_protocol_binary::send_eof() { - Protocol *save_protocol= thd->protocol; - - thd->protocol= &protocol; ::send_eof(thd); - thd->protocol= save_protocol; return FALSE; } @@ -3097,7 +3105,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) thd->query_length) <= 0) { error= mysql_execute_command(thd); - query_cache_end_of_result(thd); } } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 952b94e6b6d..cadc27638bd 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -898,7 +898,6 @@ JOIN::optimize() } if (res > 1) { - thd->fatal_error(); error= res; DBUG_PRINT("error",("Error from opt_sum_query")); DBUG_RETURN(1); @@ -14454,6 +14453,7 @@ calc_group_buffer(JOIN *join,ORDER *group) default: /* This case should never be choosen */ DBUG_ASSERT(0); + my_error(ER_OUT_OF_RESOURCES, MYF(0)); join->thd->fatal_error(); } } diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index 602c289a605..8203ca92eed 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -239,7 +239,7 @@ bool servers_reload(THD *thd) if (simple_open_n_lock_tables(thd, tables)) { sql_print_error("Can't open and lock privilege tables: %s", - thd->net.last_error); + thd->main_da.message()); goto end; } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 9a7d7c59af3..36f9cc780bd 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -578,7 +578,8 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) /* Only one table for now, but VIEW can involve several tables */ if (open_normal_and_derived_tables(thd, table_list, 0)) { - if (!table_list->view || thd->net.last_errno != ER_VIEW_INVALID) + if (!table_list->view || + thd->is_error() && thd->main_da.sql_errno() != ER_VIEW_INVALID) DBUG_RETURN(TRUE); /* @@ -786,10 +787,9 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild) } restore_record(table, s->default_values); // Get empty record table->use_all_columns(); - if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS | - Protocol::SEND_EOF)) + if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS)) DBUG_VOID_RETURN; - thd->protocol->flush(); + send_eof(thd); DBUG_VOID_RETURN; } @@ -2919,7 +2919,7 @@ static int fill_schema_table_names(THD *thd, TABLE *table, default: DBUG_ASSERT(0); } - if (thd->net.last_errno == ER_NO_SUCH_TABLE) + if (thd->is_error() && thd->main_da.sql_errno() == ER_NO_SUCH_TABLE) { thd->clear_error(); return 0; @@ -3267,8 +3267,16 @@ 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; - - if (thd->net.last_errno == ER_NO_SUCH_TABLE) + /* + XXX: show_table_list has a flag i_is_requested, + and when it's set, open_normal_and_derived_tables() + can return an error without setting an error message + in THD, which is a hack. This is why we have to + check for res, then for thd->is_error() only then + for thd->main_da.sql_errno(). + */ + if (res && thd->is_error() && + thd->main_da.sql_errno() == ER_NO_SUCH_TABLE) { /* Hide error for not existing table. @@ -3422,7 +3430,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, /* there was errors during opening tables */ - const char *error= thd->net.last_error; + const char *error= thd->main_da.message(); if (tables->view) table->field[3]->store(STRING_WITH_LEN("VIEW"), cs); else if (tables->schema_table) @@ -3624,7 +3632,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, rather than in SHOW COLUMNS */ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->net.last_errno, thd->net.last_error); + thd->main_da.sql_errno(), thd->main_da.message()); thd->clear_error(); res= 0; } @@ -4098,9 +4106,9 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, I.e. we are in SELECT FROM INFORMATION_SCHEMA.STATISTICS rather than in SHOW KEYS */ - if (thd->net.last_errno) + if (thd->is_error()) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->net.last_errno, thd->net.last_error); + thd->main_da.sql_errno(), thd->main_da.message()); thd->clear_error(); res= 0; } @@ -4290,9 +4298,9 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables, if (schema_table_store_record(thd, table)) DBUG_RETURN(1); - if (res && thd->net.last_errno) + if (res && thd->is_error()) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->net.last_errno, thd->net.last_error); + thd->main_da.sql_errno(), thd->main_da.message()); } if (res) thd->clear_error(); @@ -4323,9 +4331,9 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables, DBUG_ENTER("get_schema_constraints_record"); if (res) { - if (thd->net.last_errno) + if (thd->is_error()) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->net.last_errno, thd->net.last_error); + thd->main_da.sql_errno(), thd->main_da.message()); thd->clear_error(); DBUG_RETURN(0); } @@ -4428,9 +4436,9 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables, */ if (res) { - if (thd->net.last_errno) + if (thd->is_error()) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->net.last_errno, thd->net.last_error); + thd->main_da.sql_errno(), thd->main_da.message()); thd->clear_error(); DBUG_RETURN(0); } @@ -4511,9 +4519,9 @@ static int get_schema_key_column_usage_record(THD *thd, DBUG_ENTER("get_schema_key_column_usage_record"); if (res) { - if (thd->net.last_errno) + if (thd->is_error()) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->net.last_errno, thd->net.last_error); + thd->main_da.sql_errno(), thd->main_da.message()); thd->clear_error(); DBUG_RETURN(0); } @@ -4706,9 +4714,9 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, if (res) { - if (thd->net.last_errno) + if (thd->is_error()) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->net.last_errno, thd->net.last_error); + thd->main_da.sql_errno(), thd->main_da.message()); thd->clear_error(); DBUG_RETURN(0); } @@ -4751,6 +4759,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, break; default: DBUG_ASSERT(0); + my_error(ER_OUT_OF_RESOURCES, MYF(0)); current_thd->fatal_error(); DBUG_RETURN(1); } @@ -5243,9 +5252,9 @@ get_referential_constraints_record(THD *thd, TABLE_LIST *tables, if (res) { - if (thd->net.last_errno) + if (thd->is_error()) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->net.last_errno, thd->net.last_error); + thd->main_da.sql_errno(), thd->main_da.message()); thd->clear_error(); DBUG_RETURN(0); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index c618d170fb7..7bcb79f8429 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1662,7 +1662,10 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, !dont_log_query); if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && (if_exists || table_type == NULL)) + { error= 0; + thd->clear_error(); + } if (error == HA_ERR_ROW_IS_REFERENCED) { /* the table is referenced by a foreign key constraint */ @@ -4191,18 +4194,22 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, (table->table->file->ha_check_for_upgrade(check_opt) == HA_ADMIN_NEEDS_ALTER)) { - my_bool save_no_send_ok= thd->net.no_send_ok; DBUG_PRINT("admin", ("recreating table")); ha_autocommit_or_rollback(thd, 1); close_thread_tables(thd); tmp_disable_binlog(thd); // binlogging is done by caller if wanted - thd->net.no_send_ok= TRUE; result_code= mysql_recreate_table(thd, table); - thd->net.no_send_ok= save_no_send_ok; reenable_binlog(thd); + /* + mysql_recreate_table() can push OK or ERROR. + Clear 'OK' status. If there is an error, keep it: + we will store the error message in a result set row + and then clear. + */ + if (thd->main_da.is_ok()) + thd->main_da.reset_diagnostics_area(); goto send_result; } - } DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name)); @@ -4296,7 +4303,6 @@ send_result_message: case HA_ADMIN_TRY_ALTER: { - my_bool save_no_send_ok= thd->net.no_send_ok; /* This is currently used only by InnoDB. ha_innobase::optimize() answers "try with alter", so here we close the table, do an ALTER TABLE, @@ -4308,10 +4314,16 @@ send_result_message: *save_next_global= table->next_global; table->next_local= table->next_global= 0; tmp_disable_binlog(thd); // binlogging is done by caller if wanted - thd->net.no_send_ok= TRUE; result_code= mysql_recreate_table(thd, table); - thd->net.no_send_ok= save_no_send_ok; reenable_binlog(thd); + /* + mysql_recreate_table() can push OK or ERROR. + Clear 'OK' status. If there is an error, keep it: + we will store the error message in a result set row + and then clear. + */ + if (thd->main_da.is_ok()) + thd->main_da.reset_diagnostics_area(); ha_autocommit_or_rollback(thd, 0); close_thread_tables(thd); if (!result_code) // recreation went ok @@ -4322,9 +4334,10 @@ send_result_message: } if (result_code) // either mysql_recreate_table or analyze failed { - const char *err_msg; - if ((err_msg= thd->net.last_error)) + DBUG_ASSERT(thd->is_error()); + if (thd->is_error()) { + const char *err_msg= thd->main_da.message(); if (!thd->vio_ok()) { sql_print_error(err_msg); @@ -4340,6 +4353,7 @@ send_result_message: protocol->store(table_name, system_charset_info); protocol->store(operator_name, system_charset_info); } + thd->clear_error(); } } result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index ecb7acda61b..1e9b7da1c81 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1696,7 +1696,11 @@ void multi_update::send_error(uint errcode,const char *err) { /* First send error what ever it is ... */ my_error(errcode, MYF(0), err); +} + +void multi_update::abort() +{ /* the error was handled or nothing deleted and no side effects return */ if (error_handled || !thd->transaction.stmt.modified_non_trans_table && !updated) diff --git a/sql/table.cc b/sql/table.cc index 2143faaff5c..133b08ba703 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3275,31 +3275,32 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type) } -/* +/** Hide errors which show view underlying table information - SYNOPSIS - TABLE_LIST::hide_view_error() - thd thread handler + @param[in,out] thd thread handler + @pre This method can be called only if there is an error. */ void TABLE_LIST::hide_view_error(THD *thd) { /* Hide "Unknown column" or "Unknown function" error */ - if (thd->net.last_errno == ER_BAD_FIELD_ERROR || - thd->net.last_errno == ER_SP_DOES_NOT_EXIST || - thd->net.last_errno == ER_PROCACCESS_DENIED_ERROR || - thd->net.last_errno == ER_COLUMNACCESS_DENIED_ERROR || - thd->net.last_errno == ER_TABLEACCESS_DENIED_ERROR || - thd->net.last_errno == ER_TABLE_NOT_LOCKED || - thd->net.last_errno == ER_NO_SUCH_TABLE) + DBUG_ASSERT(thd->is_error()); + + if (thd->main_da.sql_errno() == ER_BAD_FIELD_ERROR || + thd->main_da.sql_errno() == ER_SP_DOES_NOT_EXIST || + thd->main_da.sql_errno() == ER_PROCACCESS_DENIED_ERROR || + thd->main_da.sql_errno() == ER_COLUMNACCESS_DENIED_ERROR || + thd->main_da.sql_errno() == ER_TABLEACCESS_DENIED_ERROR || + thd->main_da.sql_errno() == ER_TABLE_NOT_LOCKED || + thd->main_da.sql_errno() == ER_NO_SUCH_TABLE) { TABLE_LIST *top= top_table(); - thd->clear_error(); + thd->clear_error(); my_error(ER_VIEW_INVALID, MYF(0), top->view_db.str, top->view_name.str); } - else if (thd->net.last_errno == ER_NO_DEFAULT_FOR_FIELD) + else if (thd->main_da.sql_errno() == ER_NO_DEFAULT_FOR_FIELD) { TABLE_LIST *top= top_table(); thd->clear_error(); diff --git a/sql/tztime.cc b/sql/tztime.cc index 920f8e87d13..f080c61e243 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1642,7 +1642,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) if (open_system_tables_for_read(thd, tz_tables, &open_tables_state_backup)) { sql_print_warning("Can't open and lock time zone table: %s " - "trying to live without them", thd->net.last_error); + "trying to live without them", thd->main_da.message()); /* We will try emulate that everything is ok */ return_val= time_zone_tables_exist= 0; goto end_with_setting_default_tz; |