1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

Applied all changes from Igor and Sanja

This commit is contained in:
Michael Widenius
2013-06-15 18:32:08 +03:00
parent 3143ad589a
commit 5f1f2fc0e4
162 changed files with 11704 additions and 6298 deletions

View File

@@ -47,12 +47,12 @@ This file contains the implementation of error and warnings related
#include "sp_rcontext.h"
/*
Design notes about MYSQL_ERROR::m_message_text.
Design notes about Sql_condition::m_message_text.
The member MYSQL_ERROR::m_message_text contains the text associated with
The member Sql_condition::m_message_text contains the text associated with
an error, warning or note (which are all SQL 'conditions')
Producer of MYSQL_ERROR::m_message_text:
Producer of Sql_condition::m_message_text:
----------------------------------------
(#1) the server implementation itself, when invoking functions like
@@ -78,16 +78,16 @@ This file contains the implementation of error and warnings related
- a RESIGNAL statement,
the message text is provided by the user logic, and is expressed in UTF8.
Storage of MYSQL_ERROR::m_message_text:
Storage of Sql_condition::m_message_text:
---------------------------------------
(#4) The class MYSQL_ERROR is used to hold the message text member.
(#4) The class Sql_condition is used to hold the message text member.
This class represents a single SQL condition.
(#5) The class Warning_info represents a SQL condition area, and contains
a collection of SQL conditions in the Warning_info::m_warn_list
Consumer of MYSQL_ERROR::m_message_text:
Consumer of Sql_condition::m_message_text:
----------------------------------------
(#6) The statements SHOW WARNINGS and SHOW ERRORS display the content of
@@ -97,9 +97,9 @@ This file contains the implementation of error and warnings related
also read the content of:
- the top level statement condition area (when executed in a query),
- a sub statement (when executed in a stored program)
and return the data stored in a MYSQL_ERROR.
and return the data stored in a Sql_condition.
(#8) The RESIGNAL statement reads the MYSQL_ERROR caught by an exception
(#8) The RESIGNAL statement reads the Sql_condition caught by an exception
handler, to raise a new or modified condition (in #3).
The big picture
@@ -113,7 +113,7 @@ This file contains the implementation of error and warnings related
----------------------------|---------------------------- |
| |
V |
MYSQL_ERROR(#4) |
Sql_condition(#4) |
| |
| |
V |
@@ -151,10 +151,10 @@ This file contains the implementation of error and warnings related
As a result, the design choice for (#4) and (#5) is to store data in
the 'error_message_charset_info' CHARSET, to minimize impact on the code base.
This is implemented by using 'String MYSQL_ERROR::m_message_text'.
This is implemented by using 'String Sql_condition::m_message_text'.
The UTF8 -> error_message_charset_info conversion is implemented in
Signal_common::eval_signal_informations() (for path #B and #C).
Sql_cmd_common_signal::eval_signal_informations() (for path #B and #C).
Future work
-----------
@@ -164,14 +164,14 @@ This file contains the implementation of error and warnings related
- Change (#4 and #5) to store message text in UTF8 natively.
In practice, this means changing the type of the message text to
'<UTF8 String 128 class> MYSQL_ERROR::m_message_text', and is a direct
'<UTF8 String 128 class> Sql_condition::m_message_text', and is a direct
consequence of WL#751.
- Implement (#9) (GET DIAGNOSTICS).
See WL#2111 (Stored Procedures: Implement GET DIAGNOSTICS)
*/
MYSQL_ERROR::MYSQL_ERROR()
Sql_condition::Sql_condition()
: Sql_alloc(),
m_class_origin((const char*) NULL, 0, & my_charset_utf8_bin),
m_subclass_origin((const char*) NULL, 0, & my_charset_utf8_bin),
@@ -185,20 +185,20 @@ MYSQL_ERROR::MYSQL_ERROR()
m_cursor_name((const char*) NULL, 0, & my_charset_utf8_bin),
m_message_text(),
m_sql_errno(0),
m_level(MYSQL_ERROR::WARN_LEVEL_ERROR),
m_level(Sql_condition::WARN_LEVEL_ERROR),
m_mem_root(NULL)
{
memset(m_returned_sqlstate, 0, sizeof(m_returned_sqlstate));
}
void MYSQL_ERROR::init(MEM_ROOT *mem_root)
void Sql_condition::init(MEM_ROOT *mem_root)
{
DBUG_ASSERT(mem_root != NULL);
DBUG_ASSERT(m_mem_root == NULL);
m_mem_root= mem_root;
}
void MYSQL_ERROR::clear()
void Sql_condition::clear()
{
m_class_origin.length(0);
m_subclass_origin.length(0);
@@ -212,10 +212,10 @@ void MYSQL_ERROR::clear()
m_cursor_name.length(0);
m_message_text.length(0);
m_sql_errno= 0;
m_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
m_level= Sql_condition::WARN_LEVEL_ERROR;
}
MYSQL_ERROR::MYSQL_ERROR(MEM_ROOT *mem_root)
Sql_condition::Sql_condition(MEM_ROOT *mem_root)
: Sql_alloc(),
m_class_origin((const char*) NULL, 0, & my_charset_utf8_bin),
m_subclass_origin((const char*) NULL, 0, & my_charset_utf8_bin),
@@ -229,7 +229,7 @@ MYSQL_ERROR::MYSQL_ERROR(MEM_ROOT *mem_root)
m_cursor_name((const char*) NULL, 0, & my_charset_utf8_bin),
m_message_text(),
m_sql_errno(0),
m_level(MYSQL_ERROR::WARN_LEVEL_ERROR),
m_level(Sql_condition::WARN_LEVEL_ERROR),
m_mem_root(mem_root)
{
DBUG_ASSERT(mem_root != NULL);
@@ -254,7 +254,7 @@ static void copy_string(MEM_ROOT *mem_root, String* dst, const String* src)
}
void
MYSQL_ERROR::copy_opt_attributes(const MYSQL_ERROR *cond)
Sql_condition::copy_opt_attributes(const Sql_condition *cond)
{
DBUG_ASSERT(this != cond);
copy_string(m_mem_root, & m_class_origin, & cond->m_class_origin);
@@ -270,8 +270,8 @@ MYSQL_ERROR::copy_opt_attributes(const MYSQL_ERROR *cond)
}
void
MYSQL_ERROR::set(uint sql_errno, const char* sqlstate,
MYSQL_ERROR::enum_warning_level level, const char* msg)
Sql_condition::set(uint sql_errno, const char* sqlstate,
Sql_condition::enum_warning_level level, const char* msg)
{
DBUG_ASSERT(sql_errno != 0);
DBUG_ASSERT(sqlstate != NULL);
@@ -286,11 +286,11 @@ MYSQL_ERROR::set(uint sql_errno, const char* sqlstate,
}
void
MYSQL_ERROR::set_builtin_message_text(const char* str)
Sql_condition::set_builtin_message_text(const char* str)
{
/*
See the comments
"Design notes about MYSQL_ERROR::m_message_text."
"Design notes about Sql_condition::m_message_text."
*/
const char* copy;
@@ -300,24 +300,41 @@ MYSQL_ERROR::set_builtin_message_text(const char* str)
}
const char*
MYSQL_ERROR::get_message_text() const
Sql_condition::get_message_text() const
{
return m_message_text.ptr();
}
int
MYSQL_ERROR::get_message_octet_length() const
Sql_condition::get_message_octet_length() const
{
return m_message_text.length();
}
void
MYSQL_ERROR::set_sqlstate(const char* sqlstate)
Sql_condition::set_sqlstate(const char* sqlstate)
{
memcpy(m_returned_sqlstate, sqlstate, SQLSTATE_LENGTH);
m_returned_sqlstate[SQLSTATE_LENGTH]= '\0';
}
Diagnostics_area::Diagnostics_area(bool initialize)
: m_main_wi(0, false, initialize)
{
push_warning_info(&m_main_wi);
reset_diagnostics_area();
}
Diagnostics_area::Diagnostics_area(ulonglong warning_info_id,
bool allow_unlimited_warnings)
: m_main_wi(warning_info_id, allow_unlimited_warnings, true)
{
push_warning_info(&m_main_wi);
reset_diagnostics_area();
}
/**
Clear this diagnostics area.
@@ -337,7 +354,8 @@ Diagnostics_area::reset_diagnostics_area()
m_last_insert_id= 0;
m_statement_warn_count= 0;
#endif
is_sent= FALSE;
get_warning_info()->clear_error_condition();
set_is_sent(false);
/** Tiny reset in debug mode to see garbage right away */
m_status= DA_EMPTY;
DBUG_VOID_RETURN;
@@ -350,9 +368,9 @@ Diagnostics_area::reset_diagnostics_area()
*/
void
Diagnostics_area::set_ok_status(THD *thd, ulonglong affected_rows_arg,
ulonglong last_insert_id_arg,
const char *message_arg)
Diagnostics_area::set_ok_status(ulonglong affected_rows,
ulonglong last_insert_id,
const char *message)
{
DBUG_ENTER("set_ok_status");
DBUG_ASSERT(! is_set());
@@ -363,11 +381,11 @@ Diagnostics_area::set_ok_status(THD *thd, ulonglong affected_rows_arg,
if (is_error() || is_disabled())
return;
m_statement_warn_count= thd->warning_info->statement_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) - 1);
m_statement_warn_count= current_statement_warn_count();
m_affected_rows= affected_rows;
m_last_insert_id= last_insert_id;
if (message)
strmake(m_message, message, sizeof(m_message) - 1);
else
m_message[0]= '\0';
m_status= DA_OK;
@@ -398,20 +416,48 @@ Diagnostics_area::set_eof_status(THD *thd)
anyway.
*/
m_statement_warn_count= (thd->spcont ?
0 : thd->warning_info->statement_warn_count());
0 :
current_statement_warn_count());
m_status= DA_EOF;
DBUG_VOID_RETURN;
}
/**
Set ERROR status.
Set ERROR status in the Diagnostics Area. This function should be used to
report fatal errors (such as out-of-memory errors) when no further
processing is possible.
@param sql_errno SQL-condition error number
*/
void
Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
const char *message_arg,
const char *sqlstate)
Diagnostics_area::set_error_status(uint sql_errno)
{
set_error_status(sql_errno,
ER(sql_errno),
mysql_errno_to_sqlstate(sql_errno),
NULL);
}
/**
Set ERROR status in the Diagnostics Area.
@note error_condition may be NULL. It happens if a) OOM error is being
reported; or b) when Warning_info is full.
@param sql_errno SQL-condition error number
@param message SQL-condition message
@param sqlstate SQL-condition state
@param error_condition SQL-condition object representing the error state
*/
void
Diagnostics_area::set_error_status(uint sql_errno,
const char *message,
const char *sqlstate,
const Sql_condition *error_condition)
{
DBUG_ENTER("set_error_status");
/*
@@ -419,7 +465,14 @@ Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
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);
DBUG_ASSERT(! is_set() || m_can_overwrite_status);
// message must be set properly by the caller.
DBUG_ASSERT(message);
// sqlstate must be set properly by the caller.
DBUG_ASSERT(sqlstate);
#ifdef DBUG_OFF
/*
In production, refuse to overwrite a custom response with an
@@ -429,19 +482,17 @@ Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
return;
#endif
if (sqlstate == NULL)
sqlstate= mysql_errno_to_sqlstate(sql_errno_arg);
m_sql_errno= sql_errno_arg;
m_sql_errno= sql_errno;
memcpy(m_sqlstate, sqlstate, SQLSTATE_LENGTH);
m_sqlstate[SQLSTATE_LENGTH]= '\0';
strmake(m_message, message_arg, sizeof(m_message)-1);
strmake(m_message, message, sizeof(m_message)-1);
get_warning_info()->set_error_condition(error_condition);
m_status= DA_ERROR;
DBUG_VOID_RETURN;
}
/**
Mark the diagnostics area as 'DISABLED'.
@@ -459,15 +510,16 @@ Diagnostics_area::disable_status()
Warning_info::Warning_info(ulonglong warn_id_arg,
bool allow_unlimited_warnings, bool initialize)
:m_statement_warn_count(0),
:m_current_statement_warn_count(0),
m_current_row_for_warning(1),
m_warn_id(warn_id_arg),
m_error_condition(NULL),
m_allow_unlimited_warnings(allow_unlimited_warnings),
initialized(0),
m_read_only(FALSE)
{
m_warn_list.empty();
bzero((char*) m_warn_count, sizeof(m_warn_count));
memset(m_warn_count, 0, sizeof(m_warn_count));
if (initialize)
init();
}
@@ -492,92 +544,164 @@ Warning_info::~Warning_info()
}
/**
Reset the warning information of this connection.
*/
void Warning_info::clear_warning_info(ulonglong warn_id_arg)
bool Warning_info::has_sql_condition(const char *message_str,
ulong message_length) const
{
m_warn_id= warn_id_arg;
free_memory();
bzero((char*) m_warn_count, sizeof(m_warn_count));
m_warn_list.empty();
m_statement_warn_count= 0;
m_current_row_for_warning= 1; /* Start counting from the first row */
Diagnostics_area::Sql_condition_iterator it(m_warn_list);
const Sql_condition *err;
while ((err= it++))
{
if (strncmp(message_str, err->get_message_text(), message_length) == 0)
return true;
}
return false;
}
/**
Append warnings only if the original contents of the routine
warning info was replaced.
*/
void Warning_info::merge_with_routine_info(THD *thd, Warning_info *source)
void Warning_info::clear(ulonglong new_id)
{
/*
If a routine body is empty or if a routine did not
generate any warnings (thus m_warn_id didn't change),
do not duplicate our own contents by appending the
contents of the called routine. We know that the called
routine did not change its warning info.
id(new_id);
m_warn_list.empty();
m_marked_sql_conditions.empty();
free_memory();
memset(m_warn_count, 0, sizeof(m_warn_count));
m_current_statement_warn_count= 0;
m_current_row_for_warning= 1; /* Start counting from the first row */
clear_error_condition();
}
On the other hand, if the routine body is not empty and
some statement in the routine generates a warning or
uses tables, m_warn_id is guaranteed to have changed.
In this case we know that the routine warning info
contains only new warnings, and thus we perform a copy.
*/
if (m_warn_id != source->m_warn_id)
void Warning_info::append_warning_info(THD *thd, const Warning_info *source)
{
const Sql_condition *err;
Diagnostics_area::Sql_condition_iterator it(source->m_warn_list);
const Sql_condition *src_error_condition = source->get_error_condition();
while ((err= it++))
{
/*
If the invocation of the routine was a standalone statement,
rather than a sub-statement, in other words, if it's a CALL
of a procedure, rather than invocation of a function or a
trigger, we need to clear the current contents of the caller's
warning info.
// Do not use ::push_warning() to avoid invocation of THD-internal-handlers.
Sql_condition *new_error= Warning_info::push_warning(thd, err);
This is per MySQL rules: if a statement generates a warning,
warnings from the previous statement are flushed. Normally
it's done in push_warning(). However, here we don't use
push_warning() to avoid invocation of condition handlers or
escalation of warnings to errors.
*/
opt_clear_warning_info(thd->query_id);
append_warning_info(thd, source);
if (src_error_condition && src_error_condition == err)
set_error_condition(new_error);
if (source->is_marked_for_removal(err))
mark_condition_for_removal(new_error);
}
}
/**
Add a warning to the list of warnings. Increment the respective
counters.
Copy Sql_conditions that are not WARN_LEVEL_ERROR from the source
Warning_info to the current Warning_info.
@param thd Thread context.
@param sp_wi Stored-program Warning_info
@param thd Thread context.
@param src_wi Warning_info to copy from.
*/
MYSQL_ERROR *Warning_info::push_warning(THD *thd,
uint sql_errno, const char* sqlstate,
MYSQL_ERROR::enum_warning_level level,
const char *msg)
void Diagnostics_area::copy_non_errors_from_wi(THD *thd,
const Warning_info *src_wi)
{
MYSQL_ERROR *cond= NULL;
Sql_condition_iterator it(src_wi->m_warn_list);
const Sql_condition *cond;
Warning_info *wi= get_warning_info();
while ((cond= it++))
{
if (cond->get_level() == Sql_condition::WARN_LEVEL_ERROR)
continue;
Sql_condition *new_condition= wi->push_warning(thd, cond);
if (src_wi->is_marked_for_removal(cond))
wi->mark_condition_for_removal(new_condition);
}
}
void Warning_info::mark_sql_conditions_for_removal()
{
Sql_condition_list::Iterator it(m_warn_list);
Sql_condition *cond;
while ((cond= it++))
mark_condition_for_removal(cond);
}
void Warning_info::remove_marked_sql_conditions()
{
List_iterator_fast<Sql_condition> it(m_marked_sql_conditions);
Sql_condition *cond;
while ((cond= it++))
{
m_warn_list.remove(cond);
m_warn_count[cond->get_level()]--;
m_current_statement_warn_count--;
if (cond == m_error_condition)
m_error_condition= NULL;
}
m_marked_sql_conditions.empty();
}
bool Warning_info::is_marked_for_removal(const Sql_condition *cond) const
{
List_iterator_fast<Sql_condition> it(
const_cast<List<Sql_condition>&> (m_marked_sql_conditions));
Sql_condition *c;
while ((c= it++))
{
if (c == cond)
return true;
}
return false;
}
void Warning_info::reserve_space(THD *thd, uint count)
{
while (m_warn_list.elements() &&
(m_warn_list.elements() + count) > thd->variables.max_error_count)
m_warn_list.remove(m_warn_list.front());
}
Sql_condition *Warning_info::push_warning(THD *thd,
uint sql_errno, const char* sqlstate,
Sql_condition::enum_warning_level level,
const char *msg)
{
Sql_condition *cond= NULL;
if (! m_read_only)
{
if (m_allow_unlimited_warnings ||
m_warn_list.elements < thd->variables.max_error_count)
m_warn_list.elements() < thd->variables.max_error_count)
{
cond= new (& m_warn_root) MYSQL_ERROR(& m_warn_root);
cond= new (& m_warn_root) Sql_condition(& m_warn_root);
if (cond)
{
cond->set(sql_errno, sqlstate, level, msg);
m_warn_list.push_back(cond, &m_warn_root);
m_warn_list.push_back(cond);
}
}
m_warn_count[(uint) level]++;
}
m_statement_warn_count++;
m_current_statement_warn_count++;
return cond;
}
MYSQL_ERROR *Warning_info::push_warning(THD *thd, const MYSQL_ERROR *sql_condition)
Sql_condition *Warning_info::push_warning(THD *thd, const Sql_condition *sql_condition)
{
MYSQL_ERROR *new_condition= push_warning(thd,
Sql_condition *new_condition= push_warning(thd,
sql_condition->get_sql_errno(),
sql_condition->get_sqlstate(),
sql_condition->get_level(),
@@ -600,7 +724,7 @@ MYSQL_ERROR *Warning_info::push_warning(THD *thd, const MYSQL_ERROR *sql_conditi
msg Clear error message
*/
void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
void push_warning(THD *thd, Sql_condition::enum_warning_level level,
uint code, const char *msg)
{
DBUG_ENTER("push_warning");
@@ -611,15 +735,15 @@ void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
WARN_LEVEL_ERROR *is* a bug. Either use my_printf_error(),
my_error(), or WARN_LEVEL_WARN.
*/
DBUG_ASSERT(level != MYSQL_ERROR::WARN_LEVEL_ERROR);
DBUG_ASSERT(level != Sql_condition::WARN_LEVEL_ERROR);
if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
level= MYSQL_ERROR::WARN_LEVEL_WARN;
if (level == Sql_condition::WARN_LEVEL_ERROR)
level= Sql_condition::WARN_LEVEL_WARN;
(void) thd->raise_condition(code, NULL, level, msg);
/* Make sure we also count warnings pushed after calling set_ok_status(). */
thd->stmt_da->increment_warning();
thd->get_stmt_da()->increment_warning();
DBUG_VOID_RETURN;
}
@@ -636,7 +760,7 @@ void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
msg Clear error message
*/
void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level,
void push_warning_printf(THD *thd, Sql_condition::enum_warning_level level,
uint code, const char *format, ...)
{
va_list args;
@@ -685,7 +809,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
List<Item> field_list;
DBUG_ENTER("mysqld_show_warnings");
DBUG_ASSERT(thd->warning_info->is_read_only());
DBUG_ASSERT(thd->get_stmt_da()->is_warning_info_read_only());
field_list.push_back(new Item_empty_string("Level", 7));
field_list.push_back(new Item_return_int("Code",4, MYSQL_TYPE_LONG));
@@ -695,7 +819,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
MYSQL_ERROR *err;
const Sql_condition *err;
SELECT_LEX *sel= &thd->lex->select_lex;
SELECT_LEX_UNIT *unit= &thd->lex->unit;
ulonglong idx= 0;
@@ -703,7 +827,8 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
unit->set_limit(sel);
List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
Diagnostics_area::Sql_condition_iterator it=
thd->get_stmt_da()->sql_conditions();
while ((err= it++))
{
/* Skip levels that the user is not interested in */
@@ -726,7 +851,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
}
my_eof(thd);
thd->warning_info->set_read_only(FALSE);
thd->get_stmt_da()->set_warning_info_read_only(FALSE);
DBUG_RETURN(FALSE);
}