mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Bug#12713 "Error in a stored function called from a SELECT doesn't
cause ROLLBACK of statement", part 1. Review fixes. Do not send OK/EOF packets to the client until we reached the end of the current statement. This is a consolidation, to keep the functionality that is shared by all SQL statements in one place in the server. Currently this functionality includes: - close_thread_tables() - log_slow_statement(). After this patch and the subsequent patch for Bug#12713, it shall also include: - ha_autocommit_or_rollback() - net_end_statement() - query_cache_end_of_result(). In future it may also include: - mysql_reset_thd_for_next_command().
This commit is contained in:
126
sql/sql_parse.cc
126
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
|
||||
|
Reference in New Issue
Block a user