mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +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:
155
sql/sql_class.cc
155
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;
|
||||
|
Reference in New Issue
Block a user