Hi Davi
See my comments below.
Some points are still to be discussed, and others have been addressed
already.
Davi Arnaut wrote:
> Hi Marc,
>
> Here goes some initial review comments. I guess that some bits are
> missing and I need to take a more careful look at this patch. Let's
> first discuss the issues that I raise here.
>
> Marc Alff wrote:
>
>> #At file:///home/malff/BZR-TREE/mysql-6.0-wl2110-review-part2/
>>
>> 2675 Marc Alff 2008-07-22
>> WL#2110 (Stored Procedures: Implement SIGNAL)
>>
>> Formal review part 2/10: Core
>>
>> This patch contains the core implementation of the SIGNAL and RESIGNAL
>> statements.
>> In particular,
>> - SQL_condition is a new class used to represent a SQL condition
>> - SIGNAL / RESIGNAL is implemented in sql_signal.cc
>> - raising conditions (my_message_sql, push_warning),
>> and the SQL diagnostics area are changed.
>> added:
>> sql/sql_signal.cc
>> modified:
>> sql/derror.cc
>> sql/mysql_priv.h
>> sql/mysqld.cc
>> sql/share/errmsg.txt
>> sql/sql_class.cc
>> sql/sql_class.h
>> sql/sql_error.cc
>> sql/sql_error.h
>> sql/sql_insert.cc
>>
>> === modified file 'sql/derror.cc'
>> --- a/sql/derror.cc 2008-04-09 00:56:49 +0000
>> +++ b/sql/derror.cc 2008-07-23 00:25:11 +0000
>> @@ -121,7 +121,7 @@ Please install the latest version of thi
>> }
>>
>> /* TODO: Convert the character set to server system character set */
>> - if (!get_charset(head[30],MYF(MY_WME)))
>> + if (!(error_message_charset_info= get_charset(head[30],MYF(MY_WME))))
>> {
>> sql_print_error("Character set #%d is not supported for messagefile '%s'",
>> (int)head[30],name);
>>
>> === modified file 'sql/mysql_priv.h'
>> --- a/sql/mysql_priv.h 2008-07-09 07:12:43 +0000
>> +++ b/sql/mysql_priv.h 2008-07-23 00:25:11 +0000
>> @@ -152,6 +152,10 @@ char* query_table_status(THD *thd,const
>> extern CHARSET_INFO *system_charset_info, *files_charset_info ;
>> extern CHARSET_INFO *national_charset_info, *table_alias_charset;
>>
>> +/**
>> + Character set of the buildin error messages loaded from errmsg.sys.
>> +*/
>> +extern CHARSET_INFO *error_message_charset_info;
>>
>> enum Derivation
>> {
>> @@ -789,6 +793,7 @@ typedef my_bool (*qc_engine_callback)(TH
>> uint key_length,
>> ulonglong *engine_data);
>> #include "sql_string.h"
>> +#include "sql_fixstring.h"
>> #include "sql_list.h"
>> #include "sql_map.h"
>> #include "my_decimal.h"
>>
>> === modified file 'sql/mysqld.cc'
>> --- a/sql/mysqld.cc 2008-07-09 07:12:43 +0000
>> +++ b/sql/mysqld.cc 2008-07-23 00:25:11 +0000
>> @@ -664,6 +664,7 @@ MY_BITMAP temp_pool;
>> CHARSET_INFO *system_charset_info, *files_charset_info ;
>> CHARSET_INFO *national_charset_info, *table_alias_charset;
>> CHARSET_INFO *character_set_filesystem;
>> +CHARSET_INFO *error_message_charset_info= NULL;
>>
>
> No need to set to NULL.
>
>
Are you actually asking me to leave this variable as a random pointer
instead ?
No change.
>>
>> MY_LOCALE *my_default_lc_time_names;
>>
>> @@ -1897,7 +1898,10 @@ void close_connection(THD *thd, uint err
>> if ((vio= thd->net.vio) != 0)
>> {
>> if (errcode)
>> - net_send_error(thd, errcode, ER(errcode)); /* purecov: inspected */
>> + {
>> + const char* sqlstate= mysql_errno_to_sqlstate(errcode);
>> + net_send_error(thd, errcode, ER(errcode), sqlstate); /* purecov: inspected
> */
>> + }
>> vio_close(vio); /* vio is freed in delete thd */
>> }
>> if (lock)
>> @@ -2947,8 +2951,6 @@ extern "C" void my_message_sql(uint erro
>> void my_message_sql(uint error, const char *str, myf MyFlags)
>> {
>> THD *thd;
>> - MYSQL_ERROR::enum_warning_level level;
>> - sql_print_message_func func;
>> DBUG_ENTER("my_message_sql");
>> DBUG_PRINT("error", ("error: %u message: '%s'", error, str));
>> /*
>> @@ -2956,106 +2958,44 @@ void my_message_sql(uint error, const ch
>> will be fixed
>> DBUG_ASSERT(error != 0);
>> */
>> +
>> + /*
>> + TODO: ME_JUST_INFO and ME_JUST_WARNING are back doors used in
>> + storage/maria to print to the server log files.
>> + my_error/my_message_sql should not be used for this, since we
>> + don't want the exception handlers / SIGNAL / RESIGNAL to interfere
>> + with the error handling in this case.
>> + Instead, the server should expose a clean interface to the storage
>> + engines for printing into the server logs (sql/log.cc),
>> + and the storage engine should call that interface (see ma_message_end_user)
>> + */
>> +
>> + /*
>> + Flags for printing to the logs are mutually exclusive
>> + */
>> + DBUG_ASSERT(!(MyFlags & ME_JUST_INFO) ||
>> + !(MyFlags & ME_JUST_WARNING) ||
>> + !(MyFlags & ME_NOREFRESH));
>> +
>> +
>>
>
> Please go there and remove this hideous stuff. I have a hard time
> trying to understand why it was done this way since they already use
> server internals, why didn't they use sql_print_information..?!
>
> I think we can push this first.
>
>
SIGNAL / RESIGNAL will go into 6.1
The cleanup of Maria should happen before that, I will enter a separate
bug report for this.
>> if (MyFlags & ME_JUST_INFO)
>> {
>> - level= MYSQL_ERROR::WARN_LEVEL_NOTE;
>> - func= sql_print_information;
>> - }
>> - else if (MyFlags & ME_JUST_WARNING)
>> - {
>> - level= MYSQL_ERROR::WARN_LEVEL_WARN;
>> - func= sql_print_warning;
>> + sql_print_information("%s: %s", my_progname, str);
>> + DBUG_VOID_RETURN;
>> }
>> - else
>> +
>> + if (MyFlags & ME_JUST_WARNING)
>> {
>> - level= MYSQL_ERROR::WARN_LEVEL_ERROR;
>> - func= sql_print_error;
>> + sql_print_warning("%s: %s", my_progname, str);
>> + DBUG_VOID_RETURN;
>> }
>>
>> if ((thd= current_thd))
>> {
>> - if (MyFlags & ME_FATALERROR)
>> - thd->is_fatal_error= 1;
>> -
>> -#ifdef BUG_36098_FIXED
>> - mysql_audit_general(thd,MYSQL_AUDIT_GENERAL_ERROR,error,my_time(0),
>> - 0,0,str,str ? strlen(str) : 0,
>> - thd->query,thd->query_length,
>> - thd->variables.character_set_client,
>> - thd->row_count);
>> -#endif
>> -
>> -
>> - /*
>> - TODO: There are two exceptions mechanism (THD and sp_rcontext),
>> - this could be improved by having a common stack of handlers.
>> - */
>> - if (thd->handle_error(error, str, level))
>> - DBUG_VOID_RETURN;
>> -
>> - if (level == MYSQL_ERROR::WARN_LEVEL_WARN)
>> - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, error, str);
>> - if (level != MYSQL_ERROR::WARN_LEVEL_ERROR)
>> - goto to_error_log;
>> -
>> - thd->is_slave_error= 1; // needed to catch query errors during
> replication
>> -
>> - /*
>> - thd->lex->current_select == 0 if lex structure is not inited
>> - (not query command (COM_QUERY))
>> - */
>> - if (thd->lex->current_select &&
>> - thd->lex->current_select->no_error &&
> !thd->is_fatal_error)
>> - {
>> - DBUG_PRINT("error",
>> - ("Error converted to warning: current_select: no_error %d "
>> - "fatal_error: %d",
>> - (thd->lex->current_select ?
>> - thd->lex->current_select->no_error : 0),
>> - (int) thd->is_fatal_error));
>> - }
>> - else
>> - {
>> - if (! thd->main_da.is_error()) // Return only first message
>> - {
>> - 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->query_cache_tls);
>> - }
>> - /*
>> - If a continue handler is found, the error message will be cleared
>> - by the stored procedures code.
>> - */
>> - if (!thd->is_fatal_error && thd->spcont &&
>> - ! (MyFlags & ME_NO_SP_HANDLER) &&
>> - thd->spcont->handle_error(error, MYSQL_ERROR::WARN_LEVEL_ERROR,
> thd))
>> - {
>> - /*
>> - Do not push any warnings, a handled error must be completely
>> - silenced.
>> - */
>> - DBUG_VOID_RETURN;
>> - }
>> -
>> - if (!thd->is_fatal_error && !thd->no_warnings_for_error
> &&
>> - !(MyFlags & ME_NO_WARNING_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;
>> - }
>> + thd->raise_error(error, str, MyFlags);
>>
>
> Good.
>
>
>> }
>> -to_error_log:
>> - if (!thd || (MyFlags & ME_NOREFRESH))
>> - (*func)("%s: %s", my_progname_short, str); /* purecov: inspected */
>> + if (!thd || MyFlags & ME_NOREFRESH)
>> + sql_print_error("%s: %s", my_progname, str); /* purecov: inspected */
>> DBUG_VOID_RETURN;
>> }
>>
>> @@ -3223,6 +3163,7 @@ SHOW_VAR com_status_vars[]= {
>> {"replace", (char*) offsetof(STATUS_VAR, com_stat[(uint)
> SQLCOM_REPLACE]), SHOW_LONG_STATUS},
>> {"replace_select", (char*) offsetof(STATUS_VAR, com_stat[(uint)
> SQLCOM_REPLACE_SELECT]), SHOW_LONG_STATUS},
>> {"reset", (char*) offsetof(STATUS_VAR, com_stat[(uint)
> SQLCOM_RESET]), SHOW_LONG_STATUS},
>> + {"resignal", (char*) offsetof(STATUS_VAR, com_stat[(uint)
> SQLCOM_RESIGNAL]), SHOW_LONG_STATUS},
>> {"restore", (char*) offsetof(STATUS_VAR, com_stat[(uint)
> SQLCOM_RESTORE]), SHOW_LONG_STATUS},
>> {"revoke", (char*) offsetof(STATUS_VAR, com_stat[(uint)
> SQLCOM_REVOKE]), SHOW_LONG_STATUS},
>> {"revoke_all", (char*) offsetof(STATUS_VAR, com_stat[(uint)
> SQLCOM_REVOKE_ALL]), SHOW_LONG_STATUS},
>> @@ -3231,6 +3172,7 @@ SHOW_VAR com_status_vars[]= {
>> {"savepoint", (char*) offsetof(STATUS_VAR, com_stat[(uint)
> SQLCOM_SAVEPOINT]), SHOW_LONG_STATUS},
>> {"select", (char*) offsetof(STATUS_VAR, com_stat[(uint)
> SQLCOM_SELECT]), SHOW_LONG_STATUS},
>> {"set_option", (char*) offsetof(STATUS_VAR, com_stat[(uint)
> SQLCOM_SET_OPTION]), SHOW_LONG_STATUS},
>> + {"signal", (char*) offsetof(STATUS_VAR, com_stat[(uint)
> SQLCOM_SIGNAL]), SHOW_LONG_STATUS},
>> {"show_authors", (char*) offsetof(STATUS_VAR, com_stat[(uint)
> SQLCOM_SHOW_AUTHORS]), SHOW_LONG_STATUS},
>> {"show_binlog_events", (char*) offsetof(STATUS_VAR, com_stat[(uint)
> SQLCOM_SHOW_BINLOG_EVENTS]), SHOW_LONG_STATUS},
>> {"show_binlogs", (char*) offsetof(STATUS_VAR, com_stat[(uint)
> SQLCOM_SHOW_BINLOGS]), SHOW_LONG_STATUS},
>> @@ -4932,6 +4874,8 @@ void create_thread_to_handle_connection(
>> handle_one_connection,
>> (void*) thd)))
>> {
>> + const char* sqlstate;
>> +
>> /* purecov: begin inspected */
>> DBUG_PRINT("error",
>> ("Can't create thread to handle request (error %d)",
>> @@ -4948,7 +4892,8 @@ void create_thread_to_handle_connection(
>> /* Can't use my_error() since store_globals has not been called. */
>> my_snprintf(error_message_buff, sizeof(error_message_buff),
>> ER(ER_CANT_CREATE_THREAD), error);
>> - net_send_error(thd, ER_CANT_CREATE_THREAD, error_message_buff);
>> + sqlstate= mysql_errno_to_sqlstate(ER_CANT_CREATE_THREAD);
>> + net_send_error(thd, ER_CANT_CREATE_THREAD, error_message_buff, sqlstate);
>> (void) pthread_mutex_lock(&LOCK_thread_count);
>> close_connection(thd,0,0);
>> delete thd;
>>
>> === modified file 'sql/share/errmsg.txt'
>> --- a/sql/share/errmsg.txt 2008-07-09 07:12:43 +0000
>> +++ b/sql/share/errmsg.txt 2008-07-23 00:25:11 +0000
>> @@ -6372,3 +6372,28 @@ ER_BACKUP_OBTAIN_NAME_LOCK_FAILED
>> eng "Restore failed to obtain the name locks on the tables."
>> ER_BACKUP_RELEASE_NAME_LOCK_FAILED
>> eng "Restore failed to release the name locks on the tables."
>> +
>> +ER_DUP_SIGNAL_SET 42000
>> + eng "Duplicate condition information item: %s"
>>
>
> I think it's better to use a more descriptive message. How about
> something like: "The condition information item '%s' was specified
> more than once". In any case, don't forget to add apostrophe between
> the conversion specification.
>
> The above applies for all error messages added here.
>
>
Fixed %s -> '%s' in error texts.
Now, if we want to change the error wording itself, I will wait for
other opinions before making any change.
>> +
>> +ER_SIGNAL_WARN 01000
>> + eng "Unhandled user-defined warning"
>> +
>> +ER_SIGNAL_NOT_FOUND 02000
>> + eng "Unhandled user-defined not found"
>> +
>> +ER_SIGNAL_EXCEPTION HY000
>> + eng "Unhandled user-defined exception"
>> +
>> +ER_RESIGNAL_NO_HANDLER 0K000
>> + eng "RESIGNAL when handler not active"
>> +
>> +ER_SIGNAL_BAD_CONDITION_TYPE
>> + eng "SIGNAL/RESIGNAL can only use a CONDITION defined with SQLSTATE"
>> +
>> +WARN_COND_ITEM_TRUNCATED
>> + eng "Data truncated for condition item %s"
>> +
>> +ER_COND_ITEM_TOO_LONG
>> + eng "Data too long for condition item %s"
>> +
>>
>
>
>> === modified file 'sql/sql_class.cc'
>> --- a/sql/sql_class.cc 2008-07-14 22:06:19 +0000
>> +++ b/sql/sql_class.cc 2008-07-23 00:25:11 +0000
>> @@ -206,8 +206,9 @@ bool foreign_key_prefix(Key *a, Key *b)
>> bool
>> Reprepare_observer::report_error(THD *thd)
>> {
>> - my_error(ER_NEED_REPREPARE, MYF(ME_NO_WARNING_FOR_ERROR|ME_NO_SP_HANDLER));
>> -
>> + /* No handler, no error in the condition area */
>> + thd->main_da.set_error_status(thd, ER_NEED_REPREPARE,
>> + ER(ER_NEED_REPREPARE), "HY000");
>> m_invalidated= TRUE;
>>
>> return TRUE;
>> @@ -370,6 +371,16 @@ char *thd_security_context(THD *thd, cha
>> return thd->strmake(str.ptr(), str.length());
>> }
>>
>> +void Diagnostics_stmt_area::clear()
>> +{
>> + m_number= 0;
>> + m_more= FALSE;
>> + m_rowcount= 0;
>> +
>> + warn_list.empty();
>> + bzero((char*) warn_count, sizeof(warn_count));
>>
>
> memset
>
>
Fixed
>> +}
>> +
>> /**
>> Clear this diagnostics area.
>>
>> @@ -385,6 +396,7 @@ Diagnostics_area::reset_diagnostics_area
>> /** Don't take chances in production */
>> m_message[0]= '\0';
>> m_sql_errno= 0;
>> + memset(m_sqlstate, 0, sizeof(m_sqlstate));
>> m_server_status= 0;
>> m_affected_rows= 0;
>> m_last_insert_id= 0;
>> @@ -396,7 +408,6 @@ Diagnostics_area::reset_diagnostics_area
>> DBUG_VOID_RETURN;
>> }
>>
>> -
>> /**
>> Set OK status -- ends commands that do not return a
>> result set, e.g. INSERT/UPDATE/DELETE.
>> @@ -425,6 +436,12 @@ Diagnostics_area::set_ok_status(THD *thd
>> else
>> m_message[0]= '\0';
>> m_status= DA_OK;
>> +
>> + if (! m_stmt_area.is_read_only())
>> + {
>> + m_stmt_area.m_number++;
>> + m_stmt_area.m_rowcount= m_affected_rows;
>> + }
>> DBUG_VOID_RETURN;
>> }
>>
>> @@ -463,9 +480,18 @@ Diagnostics_area::set_eof_status(THD *th
>> */
>>
>> void
>> -Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
>> +Diagnostics_area::set_default_error_status(THD *thd, uint sql_errno_arg,
>> const char *message_arg)
>> {
>> + const char* sqlstate= mysql_errno_to_sqlstate(sql_errno_arg);
>> + set_error_status(thd, sql_errno_arg, message_arg, sqlstate);
>> +}
>> +
>> +void
>> +Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
>> + const char *message_arg,
>> + const char *sqlstate)
>>
>
> Hum, can we have also a inline set_error_status that retrieves the
> error message using ER(sql_errno_arg)?
>
>
calling set_error_status directly instead of reporting a condition with
my_error() or thd->raise_xxx() is not a normal path.
I would rather not provide helpers that encourage developers to do the
wrong thing.
>> +{
>> DBUG_ENTER("set_error_status");
>> /*
>> Only allowed to report error if has not yet reported a success
>> @@ -483,8 +509,9 @@ Diagnostics_area::set_error_status(THD *
>> #endif
>>
>> m_sql_errno= sql_errno_arg;
>> + strncpy(m_sqlstate, sqlstate, SQLSTATE_LENGTH);
>> + m_sqlstate[SQLSTATE_LENGTH]= '\0';
>> strmake(m_message, message_arg, sizeof(m_message)-1);
>> -
>> m_status= DA_ERROR;
>> DBUG_VOID_RETURN;
>> }
>> @@ -528,6 +555,7 @@ THD::THD()
>> bootstrap(0),
>> derived_tables_processing(FALSE),
>> spcont(NULL),
>> + end_partial_result_set(FALSE),
>> m_lip(NULL),
>> /*
>> @todo The following is a work around for online backup and the DDL blocker.
>> @@ -536,6 +564,7 @@ THD::THD()
>> when the DDL blocker is engaged.
>> */
>> DDL_exception(FALSE),
>> + m_tmp_syn(NULL),
>> #if defined(ENABLED_DEBUG_SYNC)
>> debug_sync_control(0),
>> #endif /* defined(ENABLED_DEBUG_SYNC) */
>> @@ -666,12 +695,11 @@ void THD::push_internal_handler(Internal
>> }
>>
>>
>> -bool THD::handle_error(uint sql_errno, const char *message,
>> - MYSQL_ERROR::enum_warning_level level)
>> +bool THD::handle_condition(const SQL_condition *cond)
>> {
>> if (m_internal_handler)
>> {
>> - return m_internal_handler->handle_error(sql_errno, message, level,
> this);
>> + return m_internal_handler->handle_condition(this, cond);
>> }
>>
>> return FALSE; // 'FALSE', as per coding style
>> @@ -684,6 +712,222 @@ void THD::pop_internal_handler()
>> m_internal_handler= NULL;
>> }
>>
>> +void THD::raise_error(uint code, const char *str, myf MyFlags)
>> +{
>> + SQL_condition cond(this->mem_root);
>> + cond.set(this, code, str, MYSQL_ERROR::WARN_LEVEL_ERROR, MyFlags);
>> +
>>
>
> Drop spurious new line (you do the same in other places..)
>
>
Changed (and in other places also)
>> + raise_condition(& cond);
>> +}
>> +
>> +void THD::raise_error_printf(uint code, const char *format, myf MyFlags, ...)
>> +{
>> + va_list args;
>> + char ebuff[ERRMSGSIZE+20];
>> +
>> +
>>
>
> Drop spurious new line.
>
>
Changed
>> + DBUG_ENTER("THD::raise_error_printf");
>> + DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d Format: %s",
>> + code, MyFlags, errno, format));
>> +
>> + va_start(args, MyFlags);
>> + (void) my_vsnprintf (ebuff, sizeof(ebuff), format, args);
>>
>
> Drop (void).
>
>
Changed
>> + va_end(args);
>> +
>> + SQL_condition cond(this->mem_root);
>> + cond.set(this, code, ebuff, MYSQL_ERROR::WARN_LEVEL_ERROR, MyFlags);
>> +
>>
>
> Drop spurious new line. Same applies for above.
>
>
Changed
>> + raise_condition(& cond);
>> + DBUG_VOID_RETURN;
>> +}
>> +
>> +void THD::raise_warning(uint code, const char *msg)
>> +{
>> + SQL_condition cond(this->mem_root);
>> + cond.set(this, code, msg, MYSQL_ERROR::WARN_LEVEL_WARN, MYF(0));
>> +
>> + raise_condition(& cond);
>> +}
>> +
>> +void THD::raise_warning_printf(uint code, const char *format, ...)
>> +{
>> + va_list args;
>> + char ebuff[ERRMSGSIZE+20];
>> +
>> +
>> + DBUG_ENTER("THD::raise_warning_printf");
>> + DBUG_PRINT("enter", ("warning: %u", code));
>> +
>> + va_start(args, format);
>> + my_vsnprintf(ebuff, sizeof(ebuff), format, args);
>> + va_end(args);
>> +
>> + SQL_condition cond(this->mem_root);
>> + cond.set(this, code, ebuff, MYSQL_ERROR::WARN_LEVEL_WARN, MYF(0));
>> +
>> + raise_condition(& cond);
>> + DBUG_VOID_RETURN;
>> +}
>> +
>> +void THD::raise_note(uint code, const char *msg)
>> +{
>> + DBUG_ENTER("THD::raise_note");
>> + DBUG_PRINT("enter", ("code: %d, msg: %s", code, msg));
>> +
>> + if (!(this->options & OPTION_SQL_NOTES))
>> + DBUG_VOID_RETURN;
>> +
>> + SQL_condition cond(this->mem_root);
>> + cond.set(this, code, msg, MYSQL_ERROR::WARN_LEVEL_NOTE, MYF(0));
>> +
>> + raise_condition(& cond);
>> + DBUG_VOID_RETURN;
>> +}
>> +
>> +void THD::raise_note_printf(uint code, const char *format, ...)
>> +{
>> + va_list args;
>> + char ebuff[ERRMSGSIZE+20];
>> +
>> +
>> + DBUG_ENTER("THD::raise_note_printf");
>> + DBUG_PRINT("enter",("code: %u", code));
>> +
>> + if (!(this->options & OPTION_SQL_NOTES))
>> + DBUG_VOID_RETURN;
>> +
>> + va_start(args, format);
>> + my_vsnprintf(ebuff, sizeof(ebuff), format, args);
>> + va_end(args);
>> +
>> + SQL_condition cond(this->mem_root);
>> + cond.set(this, code, ebuff, MYSQL_ERROR::WARN_LEVEL_NOTE, MYF(0));
>> +
>> + raise_condition(& cond);
>> + DBUG_VOID_RETURN;
>> +}
>> +
>> +void THD::raise_condition(const SQL_condition *cond)
>> +{
>> + const char* msg= cond->get_message_text();
>> +
>> + DBUG_ENTER("THD::raise_condition");
>> +
>> + if (!(this->options & OPTION_SQL_NOTES) &&
>> + (cond->m_level == MYSQL_ERROR::WARN_LEVEL_NOTE))
>> + DBUG_VOID_RETURN;
>> +
>> + if (query_id != warn_id && !spcont)
>> + mysql_reset_errors(this, 0);
>> +
>> + switch (cond->m_level)
>> + {
>> + case MYSQL_ERROR::WARN_LEVEL_NOTE:
>> + case MYSQL_ERROR::WARN_LEVEL_WARN:
>> + got_warning= 1;
>> + break;
>> + case MYSQL_ERROR::WARN_LEVEL_ERROR:
>> + if (cond->m_flags & ME_FATALERROR)
>> + is_fatal_error= 1;
>> + break;
>> + default:
>> + DBUG_ASSERT(FALSE);
>> + }
>> +
>> + if (handle_condition(cond))
>> + DBUG_VOID_RETURN;
>> +
>> + if (cond->m_level == MYSQL_ERROR::WARN_LEVEL_ERROR)
>> + {
>> + is_slave_error= 1; // needed to catch query errors during replication
>> +
>> + /*
>> + thd->lex->current_select == 0 if lex structure is not inited
>> + (not query command (COM_QUERY))
>> + */
>> + if (lex->current_select &&
>> + lex->current_select->no_error && !is_fatal_error)
>> + {
>> + DBUG_PRINT("error",
>> + ("Error converted to warning: current_select: no_error %d "
>> + "fatal_error: %d",
>> + (lex->current_select ?
>> + lex->current_select->no_error : 0),
>> + (int) is_fatal_error));
>>
>
> This message doesn't make much sense...
>
>
I totally agree with that.
However, this was existing code in my_message_sql() that got moved: this
code has been left unchanged.
Cleaning how the implementation of select interfere with the
error/warning component is an unrelated task.
No change.
>> + }
>> + else if (! main_da.is_error())
>> + main_da.set_error_status(this, cond->m_sql_errno,
>> + msg, cond->get_sqlstate());
>> + }
>> +
>> + /*
>> + If a continue handler is found, the error message will be cleared
>> + by the stored procedures code.
>> + */
>> + if (!is_fatal_error && spcont &&
>> + spcont->handle_condition(this, cond))
>> + {
>> + /*
>> + Do not push any warnings, a handled error must be completely
>> + silenced.
>> + */
>> + DBUG_VOID_RETURN;
>> + }
>> +
>> + /* Un-handled conditions */
>> +
>> + raise_condition_no_handler(cond);
>> + DBUG_VOID_RETURN;
>> +}
>> +
>> +void THD::raise_condition_no_handler(const SQL_condition *cond)
>> +{
>> + DBUG_ENTER("THD::raise_condition_no_handler");
>> +
>> + query_cache_abort(& query_cache_tls);
>> +
>> + /* FIXME: broken special case */
>> + if (no_warnings_for_error && (cond->m_level ==
> MYSQL_ERROR::WARN_LEVEL_ERROR))
>> + DBUG_VOID_RETURN;
>> +
>> +#ifdef BUG_36098_FIXED
>> + mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_ERROR, error, my_time(0),
>> + 0, 0, msg, msg ? strlen(msg) : 0,
>> + query, query_length,
>> + variables.character_set_client,
>> + row_count);
>>
>
> Hum.. this used to be before the various checks. Are you sure you
> can move it?
>
>
Given that there is controversy, I just removed the reference of
mysql_audit_general from this code,
to avoid giving the false impression that the fix is only to remove the
#ifdef.
The code was not compiling anyway due to changes, and more analysis
should be done when mysql_audit_general is properly implemented.
>> +#endif
>> +
>> + if (! main_da.m_stmt_area.is_read_only())
>> + {
>> + MYSQL_ERROR::enum_warning_level level;
>> +
>> + /* FIXME: broken special case, waiting for bug#36777 */
>> + level= (cond->m_broken_caller) ?
>> + MYSQL_ERROR::WARN_LEVEL_ERROR : cond->m_level;
>> +
>> + if (main_da.m_stmt_area.warn_list.elements < variables.max_error_count)
>> + {
>> + /* We have to use warn_root, as mem_root is freed after each query */
>> + SQL_condition *stored_cond;
>> + stored_cond= SQL_condition::deep_copy(this, & warn_root, cond);
>>
>
> I get the impression that we should just drop warn_root. It doesn't
> make much sense and complicates things. SQL_condition is the type of
> object that begs for a simple slab..
>
>
Things are not as simple.
SQL_condition are allocated
- in the warn_root, when they are not caught anywhere
- in a stored procedure sp_head::mem_root, when caught in a stored
procedure handler
(see patch 5/10)
To remove warn_root and use malloc/free instead, malloc/free would have
to be used in stored procedures also,
which is yet another story.
The MEM_ROOT implementation might be ugly, but it also provide a
reasonably robust way to prevent memory leaks,
that calling alloc/free does not provide.
No change.
>> + if (stored_cond)
>> + {
>> + stored_cond->m_level= level;
>> + main_da.m_stmt_area.warn_list.push_back(stored_cond, & warn_root);
>> + main_da.m_stmt_area.m_number++;
>> + }
>> + }
>> + else
>> + main_da.m_stmt_area.m_more= TRUE;
>> +
>> + main_da.m_stmt_area.warn_count[(uint) level]++;
>> + }
>> +
>> + total_warn_count++;
>> + DBUG_VOID_RETURN;
>> +}
>> +
>> extern "C"
>> void *thd_alloc(MYSQL_THD thd, unsigned int size)
>> {
>> @@ -766,8 +1010,8 @@ void THD::init(void)
>> TL_WRITE_LOW_PRIORITY :
>> TL_WRITE);
>> session_tx_isolation= (enum_tx_isolation) variables.tx_isolation;
>> - warn_list.empty();
>> - bzero((char*) warn_count, sizeof(warn_count));
>> + main_da.reset_diagnostics_area();
>> + main_da.m_stmt_area.clear();
>> total_warn_count= 0;
>> update_charset();
>> reset_current_stmt_binlog_row_based();
>> @@ -1566,9 +1810,8 @@ bool select_send::send_fields(List<Item>
>> void select_send::abort()
>> {
>> DBUG_ENTER("select_send::abort");
>> - if (is_result_set_started && thd->spcont &&
>> - thd->spcont->find_handler(thd, thd->main_da.sql_errno(),
>> - MYSQL_ERROR::WARN_LEVEL_ERROR))
>> +
>> + if (is_result_set_started && thd->spcont)
>> {
>> /*
>> We're executing a stored procedure, have an open result
>> @@ -1580,7 +1823,7 @@ void select_send::abort()
>> otherwise the client will hang due to the violation of the
>> client/server protocol.
>> */
>> - thd->protocol->end_partial_result_set(thd);
>> + thd->end_partial_result_set= TRUE;
>> }
>> DBUG_VOID_RETURN;
>> }
>>
>> === modified file 'sql/sql_class.h'
>> --- a/sql/sql_class.h 2008-07-14 22:06:19 +0000
>> +++ b/sql/sql_class.h 2008-07-23 00:25:11 +0000
>> @@ -78,6 +78,7 @@ class Slave_log_event;
>> class sp_rcontext;
>> class sp_cache;
>> class Lex_input_stream;
>> +class Tmp_syn;
>> class Rows_log_event;
>>
>> enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
>> @@ -303,6 +304,119 @@ struct Query_cache_tls
>> Query_cache_tls() :first_query_block(NULL) {}
>> };
>>
>> +/* SIGNAL / RESIGNAL / GET DIAGNOSTICS */
>> +
>> +#define FIRST_DIAG_SET_PROPERTY 0
>> +#define LAST_DIAG_SET_PROPERTY 11
>> +
>> +#define FIRST_DIAG_PROPERTY 0
>> +#define LAST_DIAG_PROPERTY 28
>>
>
> These defines are not necessary, use a const.
>
>
Removed
>> +/**
>> + This enumeration list all the condition item names of a condition in the
>> + SQL condition area.
>> +*/
>> +typedef enum enum_diag_condition_item_name
>> +{
>> + /*
>> + Conditions that can be set by the user (SIGNAL/RESIGNAL),
>> + and by the server implementation (THD::raise_ER_XXX).
>> + */
>> +
>> + DIAG_CLASS_ORIGIN= 0,
>>
>
> FIRST_DIAG_SET_PROPERTY = DIAG_CLASS_ORIGIN
>
>
Fixed
>> + DIAG_SUBCLASS_ORIGIN= 1,
>> + DIAG_CONSTRAINT_CATALOG= 2,
>> + DIAG_CONSTRAINT_SCHEMA= 3,
>> + DIAG_CONSTRAINT_NAME= 4,
>> + DIAG_CATALOG_NAME= 5,
>> + DIAG_SCHEMA_NAME= 6,
>> + DIAG_TABLE_NAME= 7,
>> + DIAG_COLUMN_NAME= 8,
>> + DIAG_CURSOR_NAME= 9,
>> + DIAG_MESSAGE_TEXT= 10,
>> + DIAG_MYSQL_ERRNO= 11,
>>
>
> LAST_DIAG_SET_PROPERTY = DIAG_MYSQL_ERRNO
>
>
Fixed
>> +
>> + /*
>> + Conditions that can be set only by the server implementation
>> + (THD::raise_ER_XXX).
>> + */
>> +
>> + DIAG_CONDITION_IDENTIFIER= 12,
>> + DIAG_CONDITION_NUMBER= 13,
>> + DIAG_CONNECTION_NAME= 14,
>> + DIAG_MESSAGE_LENGTH= 15,
>> + DIAG_MESSAGE_OCTET_LENGTH= 16,
>> + DIAG_PARAMETER_MODE= 17,
>> + DIAG_PARAMETER_NAME= 18,
>> + DIAG_PARAMETER_ORDINAL_POSITION= 19,
>> + DIAG_RETURNED_SQLSTATE= 20,
>> + DIAG_ROUTINE_CATALOG= 21,
>> + DIAG_ROUTINE_NAME= 22,
>> + DIAG_ROUTINE_SCHEMA= 23,
>> + DIAG_SERVER_NAME= 24,
>> + DIAG_SPECIFIC_NAME= 25,
>> + DIAG_TRIGGER_CATALOG= 26,
>> + DIAG_TRIGGER_NAME= 27,
>> + DIAG_TRIGGER_SCHEMA= 28,
>> +} Diag_condition_item_name;
>> +
>> +/**
>> + Name of each diagnostic condition item.
>> + This array is indexed by Diag_condition_item_name.
>> +*/
>> +extern const LEX_STRING Diag_condition_item_names[];
>>
>
> I couldn't find any other uses for this..
>
>
Removed the unused DIAG_xxx.
These condition items are not strictly required for SIGNAL / RESIGNAL,
they are only used for GET DIAGNOSTICS.
Diag_condition_item_names is used in sql_yacc.yy, when raising the error
ER_DUP_SIGNAL_SET.
>> +
>> +/**
>> + Set_signal_information is a container used in the parsed tree to represent
>> + the collection of assignments to condition items in the SIGNAL and RESIGNAL
>> + statements.
>> +*/
>> +class Set_signal_information
>> +{
>> +public:
>> + /** Constructor. */
>> + Set_signal_information();
>> +
>> + /** Copy constructor. */
>> + Set_signal_information(const Set_signal_information& set);
>> +
>> + /** Destructor. */
>> + ~Set_signal_information()
>> + {}
>> +
>> + /** Clear all items. */
>> + void clear();
>> +
>> + /**
>> + For each contition item assignment, m_item[] contains the parsed tree
>> + that represents the expression assigned, if any.
>> + m_item[] is an array indexed by Diag_condition_item_name.
>> + */
>> + Item *m_item[LAST_DIAG_SET_PROPERTY+1];
>> +};
>> +
>> +
>> +/**
>> + Parser temporary internal state, used during syntax analysis.
>> +*/
>> +/* FIXME: Waiting for Bug#35577 to be merged, to use Yacc_state */
>> +class Tmp_syn
>> +{
>> +public:
>> + Tmp_syn()
>> + : m_set_signal_info()
>> + {}
>> +
>> + ~Tmp_syn()
>> + {}
>> +
>> + /**
>> + Fragments of parsed tree,
>> + used during the parsing of SIGNAL and RESIGNAL.
>> + */
>> + Set_signal_information m_set_signal_info;
>> +};
>> +
>> #include "sql_lex.h" /* Must be here */
>>
>> class Delayed_insert;
>> @@ -1073,6 +1187,202 @@ enum enum_thread_type
>>
>>
>> /**
>> + Representation of a SQL condition.
>> + A SQL condition can be a completion condition (note, warning),
>> + or an exception contition (error, not found).
>> +*/
>> +class SQL_condition : public Sql_alloc
>> +{
>> +public:
>> + /**
>> + Constructor.
>> + @param mem_root The memory root to use for the condition items
>> + of this condition
>> + */
>> + SQL_condition(MEM_ROOT *mem_root);
>> +
>> + /** Destructor. */
>> + ~SQL_condition()
>> + {}
>> +
>> + /**
>> + Deep copy (static method).
>> + Builds a copy of a condition using a given memory root.
>> + 'Deep copy' is useful to propagate SQL conditions raised from a short
>> + lived runtime environment to a parent execution environment with a longer
>> + life cycle.
>> + For example, when proc_p1() calls proc_p2(), an exception raised in
>> + proc_p2() should be copied when caught in proc_p1(),
>> + before destroying the proc_p2() memory root.
>> + @param thd the current thread.
>> + @param mem_root the memory root to use for memory allocation.
>> + @param cond the condition to copy.
>> + @return the duplicated condition.
>> + */
>> + static SQL_condition* deep_copy(THD *thd, MEM_ROOT *mem_root,
>> + const SQL_condition *cond);
>> +
>> + /**
>> + Deep copy (instance method).
>> + @param cond the condition to copy.
>> + */
>> + void deep_copy(const SQL_condition *cond);
>> +
>> +
>> + /**
>> + Set this condition area with a fixed message text.
>> + @param thd the current thread.
>> + @param code the error number for this condition.
>> + @param str the message text for this condition.
>> + @param level the error level for this condition.
>> + @param MyFlags additional flags.
>> + */
>> + void set(THD *thd, uint code, const char *str,
>> + MYSQL_ERROR::enum_warning_level level, myf MyFlags);
>>
>
> Hum, I think it would be better to only set public
> set_warning/error/note interfaces and make this one private.
>
>
This is a public method of SQL_condition, used by the SIGNAL
implementation (SQLCOM_xxx).
How to raise a condition (SIGNAL, among others) and what a condition is
(SQL_condition) are two separate classes as it should be.
SIGNAL is more generic, and accesses a more generic set method(), but
nothing is exposed in this method that can't be done otherwise,
so there is no breaking of the encapsulation here.
No change.
>> + /**
>> + Set this condition area with formatting of the message text.
>> + @param thd the current thread.
>> + @param code the error number for this condition.
>> + @param str the message text printf format for this condition.
>> + @param level the error level for this condition.
>> + @param MyFlags additional flags.
>> + */
>> + void set_printf(THD *thd, uint code, const char *str,
>> + MYSQL_ERROR::enum_warning_level level, myf MyFlags, ...);
>> +
>> + /**
>> + Set the condition message test.
>> + @param str Message text, expressed in the character set derived from
>> + the server --language option
>> + */
>> + void set_builtin_message_text(const char* str);
>> +
>> + /**
>> + Get the MESSAGE_TEXT of this condition.
>> + @return the message text.
>> + */
>> + const char* get_message_text() const;
>> +
>> + /**
>> + Get the MESSAGE_OCTET_LENGTH of this condition.
>> + @return the length in bytes of the message text.
>> + */
>> + int get_message_octet_length() const;
>> +
>> + /** Set the SQLSTATE of this condition. */
>> + void set_sqlstate(const char* sqlstate);
>> +
>> + /**
>> + Get the SQLSTATE of this condition.
>> + @return the sql state.
>> + */
>> + const char* get_sqlstate() const
>> + { return m_returned_sqlstate; }
>> +
>> +public:
>> + /** SQL CLASS_ORIGIN condition item. */
>> + UTF8String64 m_class_origin;
>> +
>> + /** SQL SUBCLASS_ORIGIN condition item. */
>> + UTF8String64 m_subclass_origin;
>> +
>> + /** SQL CONSTRAINT_CATALOG condition item. */
>> + UTF8String64 m_constraint_catalog;
>> +
>> + /** SQL CONSTRAINT_SCHEMA condition item. */
>> + UTF8String64 m_constraint_schema;
>> +
>> + /** SQL CONSTRAINT_NAME condition item. */
>> + UTF8String64 m_constraint_name;
>> +
>> + /** SQL CATALOG_NAME condition item. */
>> + UTF8String64 m_catalog_name;
>> +
>> + /** SQL SCHEMA_NAME condition item. */
>> + UTF8String64 m_schema_name;
>> +
>> + /** SQL TABLE_NAME condition item. */
>> + UTF8String64 m_table_name;
>> +
>> + /** SQL COLUMN_NAME condition item. */
>> + UTF8String64 m_column_name;
>> +
>> + /** SQL CURSOR_NAME condition item. */
>> + UTF8String64 m_cursor_name;
>>
>
> Hum.. I personally don't like this proliferation of string
> parameters. Can't we make this stuff smarter? encapsulating it in
> classes and using a array. This also needlessly bloats the class and
> we shouldn't keep things that aren't necessary.
>
> Let's discuss this more in-depth on IRC.
>
>
Discussed on IRC privately.
I'll wait to see if you have a practical proposal, to evaluate which
solution is best.
For the record:
setting the condition area can be done by mostly 2 paths:
- in a generic way, using the SIGNAL statement,
- in a non generic way, when the internal implementation of the server
raises a condition
(in practice, any call to my_error(), push_warning(), or
thd->raise_ER_XXX()).
Changing the code to be more "generic" or more "specific" is only doing
a favor to 1 path at the expense of the other,
so looking at 1 code fragment alone to make a conclusion can only lead
to wrong conclusions.
No change.
>> +private:
>> + /** Message text, expressed in the character set implied by --language. */
>> + String m_message_text;
>> +
>> +public:
>> + /** MySQL extension, MYSQL_ERRNO condition item. */
>> + int m_sql_errno;
>> +
>> + /** SQL CONDITION_IDENTIFIER condition item. */
>> + UTF8String64 m_condition_identifier;
>> +
>> + /** SQL CONNECTION_NAME condition item. */
>> + UTF8String64 m_connection_name;
>> +
>> + /** SQL PARAMETER_MODE condition item. */
>> + UTF8String64 m_parameter_mode;
>> +
>> + /** SQL PARAMETER_NAME condition item. */
>> + UTF8String64 m_parameter_name;
>> +
>> + /** SQL PARAMETER_ORDINAL_POSITION condition item. */
>> + int m_parameter_ordinal_position;
>> +
>> +private:
>> + /**
>> + SQL RETURNED_SQLSTATE condition item.
>> + This member is always NUL terminated.
>> + */
>> + char m_returned_sqlstate[SQLSTATE_LENGTH+1];
>> +
>> +public:
>> + /** SQL ROUTINE_CATALOG condition item. */
>> + UTF8String64 m_routine_catalog;
>> +
>> + /** SQL ROUTINE_NAME condition item. */
>> + UTF8String64 m_routine_name;
>> +
>> + /** SQL ROUTINE_SCHEMA condition item. */
>> + UTF8String64 m_routine_schema;
>> +
>> + /** SQL SERVER_NAME condition item. */
>> + UTF8String64 m_server_name;
>> +
>> + /** SQL SPECIFIC_NAME condition item. */
>> + UTF8String64 m_specific_name;
>> +
>> + /** SQL TRIGGER_CATALOG condition item. */
>> + UTF8String64 m_trigger_catalog;
>> +
>> + /** SQL TRIGGER_NAME condition item. */
>> + UTF8String64 m_trigger_name;
>> +
>> + /** SQL TRIGGER_SCHEMA condition item. */
>> + UTF8String64 m_trigger_schema;
>> +
>> + /** Severity (error, warning, note) of this condition. */
>> + MYSQL_ERROR::enum_warning_level m_level;
>> +
>> + /** Additional flags. */
>> + myf m_flags;
>> +
>> + /** Memory root to use to hold condition item values. */
>> + MEM_ROOT *m_mem_root;
>> +
>> + /*
>> + FIXME: push_warning() called with a level of ERROR:
>> + waiting for bug#36777 to be merged to remove this.
>> + */
>> + bool m_broken_caller;
>> +};
>> +
>> +/**
>> This class represents the interface for internal error handlers.
>> Internal error handlers are exception handlers used by the server
>> implementation.
>> @@ -1085,12 +1395,12 @@ protected:
>>
>> public:
>> /**
>> - Handle an error condition.
>> + Handle a sql condition.
>> This method can be implemented by a subclass to achieve any of the
>> following:
>> - - mask an error internally, prevent exposing it to the user,
>> - - mask an error and throw another one instead.
>> - When this method returns true, the error condition is considered
>> + - mask a warning/error internally, prevent exposing it to the user,
>> + - mask a warning/error and throw another one instead.
>> + When this method returns true, the sql condition is considered
>> 'handled', and will not be propagated to upper layers.
>> It is the responsability of the code installing an internal handler
>> to then check for trapped conditions, and implement logic to recover
>> @@ -1104,15 +1414,102 @@ public:
>> before removing it from the exception stack with
>> <code>THD::pop_internal_handler()</code>.
>>
>> - @param sql_errno the error number
>> - @param level the error level
>> @param thd the calling thread
>> - @return true if the error is handled
>> + @param cond the condition raised.
>> + @return true if the condition is handled
>> + */
>> + virtual bool handle_condition(THD *thd, const SQL_condition *cond) = 0;
>> +};
>> +
>> +
>> +/**
>> + Statement diagnostics area.
>> +*/
>> +class Diagnostics_stmt_area
>> +{
>> +public:
>> + /** Constructor. */
>> + Diagnostics_stmt_area()
>> + : m_number(0),
>> + m_more(FALSE),
>> + m_command_function_cmd(SQLCOM_END),
>> + m_dynamic_function_cmd(SQLCOM_END),
>> + m_rowcount(0),
>> + warn_list(),
>> + m_read_only(FALSE)
>> + {
>> + bzero((char*) warn_count, sizeof(warn_count));
>>
>
> memset
>
>
Fixed
>> + }
>> +
>> + /** Destructor. */
>> + ~Diagnostics_stmt_area()
>> + {}
>> +
>> + /** Clear the statement area. */
>> + void clear();
>> +
>> + /**
>> + Set the read only status for this statement area.
>> + This is a priviledged operation, reserved for the implementation of
>> + diagnostics related statements, to enforce that the statement area is
>> + left untouched during execution.
>> + The diagnostics statements are:
>> + - SHOW WARNINGS
>> + - SHOW ERRORS
>> + - GET DIAGNOSTICS
>> + @param read_only the read only property to set
>> + */
>> + void set_read_only(bool read_only)
>> + { m_read_only= read_only; }
>> +
>> + /**
>> + Read only status.
>> + @return the read only property
>> + */
>> + bool is_read_only() const
>> + { return m_read_only; }
>> +
>> + /** SQL 'NUMBER' statement information item. */
>> + uint m_number;
>> +
>> + /** SQL 'MORE' statement information item. */
>> + bool m_more;
>>
>
> Use?
>
>
Removed.
This also was a left over from the GET DIAGNOSTICS prototype,
that was missed during the final clean-up.
>> +
>> + /**
>> + Internal command of a statement.
>> + The following statement information items:
>> + - SQL 'COMMAND_FUNCTION'
>> + - SQL 'COMMAND_FUNCTION_CODE'
>> + derive from this attribute.
>> + */
>> + enum_sql_command m_command_function_cmd;
>> +
>> + /**
>> + Internal command of a statement,
>> + for dynamic statements.
>> + The following statement information items:
>> + - SQL 'DYNAMIC_FUNCTION'
>> + - SQL 'DYNAMIC_FUNCTION_CODE'
>> + derive from this attribute.
>> + */
>> + enum_sql_command m_dynamic_function_cmd;
>> +
>> + /** SQL 'ROWCOUNT' statement information item. */
>> + ha_rows m_rowcount;
>>
>
> I haven't been able to find a meaningful use for m_rowcount,
> m_number, etc in any of the patches. Is something missing or I
> missed something?
>
>
Removed.
>> + /**
>> + Condition area.
>> + */
>> + List <SQL_condition> warn_list;
>> +
>> + /**
>> + Counters for errors/warnings/notes in the condition area.
>> */
>> - virtual bool handle_error(uint sql_errno,
>> - const char *message,
>> - MYSQL_ERROR::enum_warning_level level,
>> - THD *thd) = 0;
>> + uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
>> +
>> +private:
>> + /** Read only status. */
>> + bool m_read_only;
>> };
>>
>>
>> @@ -1148,7 +1545,10 @@ public:
>> ulonglong 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 set_default_error_status(THD *thd, uint sql_errno_arg,
>> + const char *message_arg);
>> + void set_error_status(THD *thd, uint sql_errno_arg, const char *message_arg,
>> + const char* sqlstate);
>>
>> void disable_status();
>>
>> @@ -1167,6 +1567,9 @@ public:
>> uint sql_errno() const
>> { DBUG_ASSERT(m_status == DA_ERROR); return m_sql_errno; }
>>
>> + const char* get_sqlstate() const
>> + { DBUG_ASSERT(m_status == DA_ERROR); return m_sqlstate; }
>> +
>> uint server_status() const
>> {
>> DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF);
>> @@ -1196,6 +1599,8 @@ private:
>> */
>> uint m_sql_errno;
>>
>> + char m_sqlstate[SQLSTATE_LENGTH+1];
>> +
>> /**
>> 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:
>> @@ -1224,12 +1629,11 @@ private:
>> */
>> ulonglong m_last_insert_id;
>> /** The total number of warnings. */
>> - uint m_total_warn_count;
>> + uint m_total_warn_count;
>> enum_diagnostics_status m_status;
>> - /**
>> - @todo: the following THD members belong here:
>> - - warn_list, warn_count,
>> - */
>> +
>> +public:
>> + Diagnostics_stmt_area m_stmt_area;
>> };
>>
>> /**
>> @@ -1752,15 +2156,7 @@ public:
>> table_map used_tables;
>> USER_CONN *user_connect;
>> CHARSET_INFO *db_charset;
>> - /*
>> - FIXME: this, and some other variables like 'count_cuted_fields'
>> - maybe should be statement/cursor local, that is, moved to Statement
>> - class. With current implementation warnings produced in each prepared
>> - statement/cursor settle here.
>> - */
>> - List <MYSQL_ERROR> warn_list;
>> - uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
>> - uint total_warn_count;
>> + uint total_warn_count;
>> Diagnostics_area main_da;
>> #if defined(ENABLED_PROFILING)
>> PROFILING profiling;
>> @@ -1872,6 +2268,8 @@ public:
>> my_bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */
>>
>> sp_rcontext *spcont; // SP runtime context
>> + bool end_partial_result_set;
>> +
>> sp_cache *sp_proc_cache;
>> sp_cache *sp_func_cache;
>>
>> @@ -1934,6 +2332,12 @@ public:
>> */
>> my_bool DDL_exception; // Allow some DDL if there is an exception
>>
>> + /**
>> + Parser internal temporary state, used during syntax parsing.
>> + This member is only valid during parsing.
>> + */
>> + Tmp_syn *m_tmp_syn;
>> +
>> Locked_tables_list locked_tables_list;
>>
>> #ifdef WITH_PARTITION_STORAGE_ENGINE
>> @@ -2355,19 +2759,90 @@ public:
>> void push_internal_handler(Internal_error_handler *handler);
>>
>> /**
>> - Handle an error condition.
>> - @param sql_errno the error number
>> - @param level the error level
>> + Handle a sql condition.
>> + @param cond the sql condition to handle.
>> @return true if the error is handled
>> */
>> - virtual bool handle_error(uint sql_errno, const char *message,
>> - MYSQL_ERROR::enum_warning_level level);
>> + virtual bool handle_condition(const SQL_condition *cond);
>>
>> /**
>> Remove the error handler last pushed.
>> */
>> void pop_internal_handler();
>>
>> + /**
>> + Raise an ER_NO_SUCH_TABLE exception condition.
>> + @param db the schema name of the missing table
>> + @param table the table name of the missing table
>> + */
>> + void raise_ER_NO_SUCH_TABLE(const char* db, const char* table)
>> + {
>> + SQL_condition cond(this->mem_root);
>> + cond.set_printf(this, ER_NO_SUCH_TABLE, ER(ER_NO_SUCH_TABLE),
>> + MYSQL_ERROR::WARN_LEVEL_ERROR, MYF(0),
>> + db, table);
>> + cond.m_schema_name.set(db);
>> + cond.m_table_name.set(table);
>> + raise_condition(& cond);
>> + }
>> +
>> + /**
>> + Raise an exception condition, with a fixed message.
>> + @param code the MYSQL_ERRNO error code of the error
>> + @param str the MESSAGE_TEXT of the error
>> + @param MyFlags additional flags
>> + */
>> + void raise_error(uint code, const char *str, myf MyFlags);
>> +
>> + /**
>> + Raise an exception condition, with a formatted message.
>> + @param code the MYSQL_ERRNO error code of the error
>> + @param format the MESSAGE_TEXT printf format of the error
>> + @param MyFlags additional flags
>> + */
>> + void raise_error_printf(uint code, const char *format, myf MyFlags, ...);
>> +
>> + /**
>> + Raise a completion condition (warning), with a fixed message.
>> + @param code the MYSQL_ERRNO error code of the warning
>> + @param str the MESSAGE_TEXT of the warning
>> + */
>> + void raise_warning(uint code, const char *str);
>> +
>> + /**
>> + Raise a completion condition (warning), with a formatted message.
>> + @param code the MYSQL_ERRNO error code of the warning
>> + @param format the MESSAGE_TEXT printf format of the warning
>> + */
>> + void raise_warning_printf(uint code, const char *format, ...);
>> +
>> + /**
>> + Raise a completion condition (note), with a fixed message.
>> + @param code the MYSQL_ERRNO error code of the note
>> + @param str the MESSAGE_TEXT of the note
>> + */
>> + void raise_note(uint code, const char *str);
>> +
>> + /**
>> + Raise a completion condition (note), with a formatted message.
>> + @param code the MYSQL_ERRNO error code of the note
>> + @param format the MESSAGE_TEXT printf format of the note
>> + */
>> + void raise_note_printf(uint code, const char *format, ...);
>> +
>> + /**
>> + Raise a generic SQL condition.
>> + @param cond the condition to raise
>> + */
>> + void raise_condition(const SQL_condition *cond);
>> +
>> + /**
>> + Raise a generic SQL condition, without activation any SQL condition
>> + handlers.
>> + @param cond the condition to raise
>> + */
>> + void raise_condition_no_handler(const SQL_condition *cond);
>> +
>> private:
>> /** The current internal error handler for this thread, or NULL. */
>> Internal_error_handler *m_internal_handler;
>>
>> === modified file 'sql/sql_error.cc'
>> --- a/sql/sql_error.cc 2008-02-19 12:59:19 +0000
>> +++ b/sql/sql_error.cc 2008-07-23 00:25:11 +0000
>> @@ -74,14 +74,17 @@ void MYSQL_ERROR::set_msg(THD *thd, cons
>> void mysql_reset_errors(THD *thd, bool force)
>> {
>> DBUG_ENTER("mysql_reset_errors");
>> +
>> + if (thd->main_da.m_stmt_area.is_read_only())
>> + DBUG_VOID_RETURN;
>> +
>>
>
> Cool.
>
>
>> if (thd->query_id != thd->warn_id || force)
>> {
>> thd->warn_id= thd->query_id;
>> + thd->main_da.m_stmt_area.clear();
>> free_root(&thd->warn_root,MYF(0));
>> - bzero((char*) thd->warn_count, sizeof(thd->warn_count));
>> if (force)
>> thd->total_warn_count= 0;
>> - thd->warn_list.empty();
>> thd->row_count= 1; // by default point to row 1
>> }
>> DBUG_VOID_RETURN;
>> @@ -97,68 +100,40 @@ void mysql_reset_errors(THD *thd, bool f
>> level Severity of warning (note, warning, error ...)
>> code Error number
>> msg Clear error message
>> -
>> - RETURN
>> - pointer on MYSQL_ERROR object
>> */
>>
>> -MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
>> - uint code, const char *msg)
>> +void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
>> + uint code, const char *msg)
>> {
>> - MYSQL_ERROR *err= 0;
>> DBUG_ENTER("push_warning");
>> DBUG_PRINT("enter", ("code: %d, msg: %s", code, msg));
>>
>> - if (level == MYSQL_ERROR::WARN_LEVEL_NOTE &&
>> - !(thd->options & OPTION_SQL_NOTES))
>> - DBUG_RETURN(0);
>> -
>> - if (thd->query_id != thd->warn_id && !thd->spcont)
>> - mysql_reset_errors(thd, 0);
>> - thd->got_warning= 1;
>> -
>> - /* Abort if we are using strict mode and we are not using IGNORE */
>> - if ((int) level >= (int) MYSQL_ERROR::WARN_LEVEL_WARN &&
>> - thd->really_abort_on_warning())
>> + switch(level)
>> {
>> - /* Avoid my_message() calling push_warning */
>> - bool no_warnings_for_error= thd->no_warnings_for_error;
>> - sp_rcontext *spcont= thd->spcont;
>> -
>> - thd->no_warnings_for_error= 1;
>> - thd->spcont= NULL;
>> -
>> - thd->killed= THD::KILL_BAD_DATA;
>> - my_message(code, msg, MYF(0));
>> -
>> - thd->spcont= spcont;
>> - thd->no_warnings_for_error= no_warnings_for_error;
>> - /* Store error in error list (as my_message() didn't do it) */
>> - level= MYSQL_ERROR::WARN_LEVEL_ERROR;
>> - }
>> -
>> - if (thd->handle_error(code, msg, level))
>> - DBUG_RETURN(NULL);
>> -
>> - if (thd->spcont &&
>> - thd->spcont->handle_error(code, level, thd))
>> + case MYSQL_ERROR::WARN_LEVEL_NOTE:
>> + thd->raise_note(code, msg);
>> + break;
>> + case MYSQL_ERROR::WARN_LEVEL_WARN:
>> + thd->raise_warning(code, msg);
>> + break;
>> + case MYSQL_ERROR::WARN_LEVEL_ERROR:
>> {
>> - DBUG_RETURN(NULL);
>> + SQL_condition cond(thd->mem_root);
>> + cond.set(thd, code, msg, MYSQL_ERROR::WARN_LEVEL_WARN, MYF(0));
>> + /* FIXME: broken calling code */
>> + cond.m_broken_caller= TRUE;
>> + thd->raise_condition(& cond);
>> + break;
>> }
>> - query_cache_abort(&thd->query_cache_tls);
>> -
>>
>> - if (thd->warn_list.elements < thd->variables.max_error_count)
>> - {
>> - /* We have to use warn_root, as mem_root is freed after each query */
>> - if ((err= new (&thd->warn_root) MYSQL_ERROR(thd, code, level, msg)))
>> - thd->warn_list.push_back(err, &thd->warn_root);
>> + default:
>> + DBUG_ASSERT(FALSE);
>> }
>> - thd->warn_count[(uint) level]++;
>> - thd->total_warn_count++;
>> - DBUG_RETURN(err);
>> +
>> + DBUG_VOID_RETURN;
>> }
>>
>
> I think we should drop push_warning all together..
>
>
In the long term, I agree with that.
At the minimum, this will avoid calling _current_thd again and again.
When replaced with thd->raise_WARN_XXX(), bugs in the printf format
arguments will be reduced also.
I think that however, there are good reasons to keep push_warning for now:
a)
grep push_warning sql/*.cc | wc -l --> 238
At least removing calls to push_warning should be a separate task / patch
b)
To merge code from 6.0 to 6.1, keeping the push_warning API around would
be most welcome for maintenance,
otherwise every single bug fix raising a warning will cause rework after
a merge.
>> +
>> /*
>> Push the warning/error to error list if there is still room in the list
>>
>> @@ -211,10 +186,12 @@ const LEX_STRING warning_level_names[]=
>> };
>>
>> bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
>> -{
>> +{
>> List<Item> field_list;
>> DBUG_ENTER("mysqld_show_warnings");
>>
>> + DBUG_ASSERT(thd->main_da.m_stmt_area.is_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));
>> field_list.push_back(new Item_empty_string("Message",MYSQL_ERRMSG_SIZE));
>> @@ -223,7 +200,7 @@ bool mysqld_show_warnings(THD *thd, ulon
>> Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
>> DBUG_RETURN(TRUE);
>>
>> - MYSQL_ERROR *err;
>> + SQL_condition *cond;
>> SELECT_LEX *sel= &thd->lex->select_lex;
>> SELECT_LEX_UNIT *unit= &thd->lex->unit;
>> ha_rows idx= 0;
>> @@ -231,24 +208,30 @@ bool mysqld_show_warnings(THD *thd, ulon
>>
>> unit->set_limit(sel);
>>
>> - List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
>> - while ((err= it++))
>> + List_iterator_fast<SQL_condition>
> it(thd->main_da.m_stmt_area.warn_list);
>> + while ((cond= it++))
>> {
>> /* Skip levels that the user is not interested in */
>> - if (!(levels_to_show & ((ulong) 1 << err->level)))
>> + if (!(levels_to_show & ((ulong) 1 << cond->m_level)))
>> continue;
>> if (++idx <= unit->offset_limit_cnt)
>> continue;
>> if (idx > unit->select_limit_cnt)
>> break;
>> protocol->prepare_for_resend();
>> - protocol->store(warning_level_names[err->level].str,
>> - warning_level_names[err->level].length, system_charset_info);
>> - protocol->store((uint32) err->code);
>> - protocol->store(err->msg, strlen(err->msg), system_charset_info);
>> + protocol->store(warning_level_names[cond->m_level].str,
>> + warning_level_names[cond->m_level].length,
>> + system_charset_info);
>> + protocol->store((uint32) cond->m_sql_errno);
>> + protocol->store(cond->get_message_text(),
>> + cond->get_message_octet_length(),
>> + system_charset_info);
>> if (protocol->write())
>> DBUG_RETURN(TRUE);
>> }
>> my_eof(thd);
>> +
>> + thd->main_da.m_stmt_area.set_read_only(FALSE);
>> +
>> DBUG_RETURN(FALSE);
>> }
>>
>> === modified file 'sql/sql_error.h'
>> --- a/sql/sql_error.h 2008-06-12 19:04:52 +0000
>> +++ b/sql/sql_error.h 2008-07-23 00:25:11 +0000
>> @@ -35,8 +35,8 @@ private:
>> void set_msg(THD *thd, const char *msg_arg);
>> };
>>
>> -MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
>> - uint code, const char *msg);
>> +void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
>> + uint code, const char *msg);
>> void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level,
>> uint code, const char *format, ...);
>> void mysql_reset_errors(THD *thd, bool force);
>>
>> === modified file 'sql/sql_insert.cc'
>> --- a/sql/sql_insert.cc 2008-07-14 14:56:49 +0000
>> +++ b/sql/sql_insert.cc 2008-07-23 00:25:11 +0000
>> @@ -2314,8 +2314,8 @@ pthread_handler_t handle_delayed_insert(
>> if (my_thread_init())
>> {
>> /* 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));
>> + thd->main_da.set_default_error_status(thd, ER_OUT_OF_RESOURCES,
>> + ER(ER_OUT_OF_RESOURCES));
>> goto end;
>> }
>> #endif
>> @@ -2325,8 +2325,8 @@ pthread_handler_t handle_delayed_insert(
>> 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->main_da.set_default_error_status(thd, ER_OUT_OF_RESOURCES,
>> + ER(ER_OUT_OF_RESOURCES));
>> thd->fatal_error();
>> goto err;
>> }
>>
>> === added file 'sql/sql_signal.cc'
>> --- a/sql/sql_signal.cc 1970-01-01 00:00:00 +0000
>> +++ b/sql/sql_signal.cc 2008-07-23 00:25:11 +0000
>> @@ -0,0 +1,651 @@
>> +/* Copyright (C) 2008 Sun Microsystems, Inc
>> +
>> + This program is free software; you can redistribute it and/or modify
>> + it under the terms of the GNU General Public License as published by
>> + the Free Software Foundation; version 2 of the License.
>> +
>> + This program is distributed in the hope that it will be useful,
>> + but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + GNU General Public License for more details.
>> +
>> + You should have received a copy of the GNU General Public License
>> + along with this program; if not, write to the Free Software
>> + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
>> +
>> +#include "mysql_priv.h"
>> +#include "sp_head.h"
>> +#include "sp_pcontext.h"
>> +#include "sp_rcontext.h"
>> +#include "sql_signal.h"
>> +
>> +/*
>> + The parser accepts any error code (desired)
>> + The runtime internally supports any error code (desired)
>> + The client server protocol is limited to 16 bits error codes (restriction)
>> + Enforcing the 65535 limit in the runtime until the protocol can change.
>> +*/
>> +#define MAX_MYSQL_ERRNO UINT_MAX16
>> +
>> +const LEX_STRING Diag_condition_item_names[]=
>> +{
>> + { C_STRING_WITH_LEN("CLASS_ORIGIN") },
>> + { C_STRING_WITH_LEN("SUBCLASS_ORIGIN") },
>> + { C_STRING_WITH_LEN("CONSTRAINT_CATALOG") },
>> + { C_STRING_WITH_LEN("CONSTRAINT_SCHEMA") },
>> + { C_STRING_WITH_LEN("CONSTRAINT_NAME") },
>> + { C_STRING_WITH_LEN("CATALOG_NAME") },
>> + { C_STRING_WITH_LEN("SCHEMA_NAME") },
>> + { C_STRING_WITH_LEN("TABLE_NAME") },
>> + { C_STRING_WITH_LEN("COLUMN_NAME") },
>> + { C_STRING_WITH_LEN("CURSOR_NAME") },
>> + { C_STRING_WITH_LEN("MESSAGE_TEXT") },
>> + { C_STRING_WITH_LEN("MYSQL_ERRNO") },
>> +
>> + { C_STRING_WITH_LEN("CONDITION_IDENTIFIER") },
>> + { C_STRING_WITH_LEN("CONDITION_NUMBER") },
>> + { C_STRING_WITH_LEN("CONNECTION_NAME") },
>> + { C_STRING_WITH_LEN("MESSAGE_LENGTH") },
>> + { C_STRING_WITH_LEN("MESSAGE_OCTET_LENGTH") },
>> + { C_STRING_WITH_LEN("PARAMETER_MODE") },
>> + { C_STRING_WITH_LEN("PARAMETER_NAME") },
>> + { C_STRING_WITH_LEN("PARAMETER_ORDINAL_POSITION") },
>> + { C_STRING_WITH_LEN("RETURNED_SQLSTATE") },
>> + { C_STRING_WITH_LEN("ROUTINE_CATALOG") },
>> + { C_STRING_WITH_LEN("ROUTINE_NAME") },
>> + { C_STRING_WITH_LEN("ROUTINE_SCHEMA") },
>> + { C_STRING_WITH_LEN("SERVER_NAME") },
>> + { C_STRING_WITH_LEN("SPECIFIC_NAME") },
>> + { C_STRING_WITH_LEN("TRIGGER_CATALOG") },
>> + { C_STRING_WITH_LEN("TRIGGER_NAME") },
>> + { C_STRING_WITH_LEN("TRIGGER_SCHEMA") }
>> +};
>> +
>> +const LEX_STRING Diag_statement_item_names[]=
>> +{
>> + { C_STRING_WITH_LEN("NUMBER") },
>> + { C_STRING_WITH_LEN("MORE") },
>> + { C_STRING_WITH_LEN("COMMAND_FUNCTION") },
>> + { C_STRING_WITH_LEN("COMMAND_FUNCTION_CODE") },
>> + { C_STRING_WITH_LEN("DYNAMIC_FUNCTION") },
>> + { C_STRING_WITH_LEN("DYNAMIC_FUNCTION_CODE") },
>> + { C_STRING_WITH_LEN("ROW_COUNT") },
>> + { C_STRING_WITH_LEN("TRANSACTIONS_COMMITTED") },
>> + { C_STRING_WITH_LEN("TRANSACTIONS_ROLLED_BACK") },
>> + { C_STRING_WITH_LEN("TRANSACTION_ACTIVE") }
>> +};
>> +
>> +
>> +SQL_condition::SQL_condition(MEM_ROOT *mem_root)
>> + : Sql_alloc(),
>> + m_class_origin(mem_root),
>> + m_subclass_origin(mem_root),
>> + m_constraint_catalog(mem_root),
>> + m_constraint_schema(mem_root),
>> + m_constraint_name(mem_root),
>> + m_catalog_name(mem_root),
>> + m_schema_name(mem_root),
>> + m_table_name(mem_root),
>> + m_column_name(mem_root),
>> + m_cursor_name(mem_root),
>> + m_message_text(),
>> + m_sql_errno(0),
>> + m_condition_identifier(mem_root),
>> + m_connection_name(mem_root),
>> + m_parameter_mode(mem_root),
>> + m_parameter_name(mem_root),
>> + m_parameter_ordinal_position(0),
>> + m_routine_catalog(mem_root),
>> + m_routine_name(mem_root),
>> + m_routine_schema(mem_root),
>> + m_server_name(mem_root),
>> + m_specific_name(mem_root),
>> + m_trigger_catalog(mem_root),
>> + m_trigger_name(mem_root),
>> + m_trigger_schema(mem_root),
>> + m_level(MYSQL_ERROR::WARN_LEVEL_ERROR),
>> + m_flags(0),
>> + m_mem_root(mem_root),
>> + m_broken_caller(FALSE)
>>
>
> Ugh.. we definitely need to improve this. Let's discuss it.
>
>
I don't see a problem here.
The code is brutal but explicit and robust.
A SQL_condition *has* many attributes, per the requirements.
Each attributes might have a different type.
String attributes might have different length.
Replacing:
- String m_a
- String m_b
by
- String m_array[2]
won't allocate less memory,
and will just create a need for more indexing and more set() / get()
helpers,
to create a final result where some complexity and indirections are added,
making it not necessarily easier to read / maintain.
No change, waiting for a concrete proposal to evaluate (see previous
comments).
>> +{
>> + memset(m_returned_sqlstate, 0, sizeof(m_returned_sqlstate));
>> +}
>> +
>> +SQL_condition *
>> +SQL_condition::deep_copy(THD *thd, MEM_ROOT *mem_root,
>> + const SQL_condition *cond)
>> +{
>> + SQL_condition *copy= new (mem_root) SQL_condition(mem_root);
>> + if (copy)
>> + copy->deep_copy(cond);
>> + return copy;
>> +}
>> +
>> +void
>> +SQL_condition::deep_copy(const SQL_condition *cond)
>> +{
>> + memcpy(m_returned_sqlstate, cond->m_returned_sqlstate,
>> + sizeof(m_returned_sqlstate));
>> +
>> + if (cond->m_message_text.length())
>> + {
>> + const char* copy;
>> +
>> + copy= strdup_root(m_mem_root, cond->m_message_text.ptr());
>> + m_message_text.set(copy, cond->m_message_text.length(),
>> + error_message_charset_info);
>> + }
>> + else
>> + m_message_text.length(0);
>> +
>> + DBUG_ASSERT(! m_message_text.is_alloced());
>> +
>> + m_class_origin.copy(& cond->m_class_origin);
>> + m_subclass_origin.copy(& cond->m_subclass_origin);
>> + m_constraint_catalog.copy(& cond->m_constraint_catalog);
>> + m_constraint_schema.copy(& cond->m_constraint_schema);
>> + m_constraint_name.copy(& cond->m_constraint_name);
>> + m_catalog_name.copy(& cond->m_catalog_name);
>> + m_schema_name.copy(& cond->m_schema_name);
>> + m_table_name.copy(& cond->m_table_name);
>> + m_column_name.copy(& cond->m_column_name);
>> + m_cursor_name.copy(& cond->m_cursor_name);
>> + m_sql_errno= cond->m_sql_errno;
>> + m_condition_identifier.copy(& cond->m_condition_identifier);
>> + m_connection_name.copy(& cond->m_connection_name);
>> + m_parameter_mode.copy(& cond->m_parameter_mode);
>> + m_parameter_name.copy(& cond->m_parameter_name);
>> + m_parameter_ordinal_position= cond->m_parameter_ordinal_position;
>> + m_routine_catalog.copy(& cond->m_routine_catalog);
>> + m_routine_name.copy(& cond->m_routine_name);
>> + m_routine_schema.copy(& cond->m_routine_schema);
>> + m_server_name.copy(& cond->m_server_name);
>> + m_specific_name.copy(& cond->m_specific_name);
>> + m_trigger_catalog.copy(& cond->m_trigger_catalog);
>> + m_trigger_name.copy(& cond->m_trigger_name);
>> + m_trigger_schema.copy(& cond->m_trigger_schema);
>> + m_level= cond->m_level;
>> + m_flags= cond->m_flags;
>> + m_broken_caller= cond->m_broken_caller;
>> +}
>> +
>> +void
>> +SQL_condition::set_printf(THD *thd, uint code, const char *str,
>> + MYSQL_ERROR::enum_warning_level level,
>> + myf flags, ...)
>> +{
>> + va_list args;
>> + char ebuff[ERRMSGSIZE+20];
>> +
>> +
>> + DBUG_ENTER("SQL_condition::set_printf");
>> +
>> + va_start(args, flags);
>> + (void) my_vsnprintf (ebuff, sizeof(ebuff), str, args);
>>
>
> Drop (void)
>
>
Fixed
>> + va_end(args);
>> +
>> + set(thd, code, ebuff, level, flags);
>> +
>> + DBUG_VOID_RETURN;
>> +}
>> +
>> +void
>> +SQL_condition::set(THD *thd, uint code, const char *str,
>> + MYSQL_ERROR::enum_warning_level level, myf flags)
>> +{
>> + const char* sqlstate;
>> +
>> + if (code == 0)
>> + code= ER_UNKNOWN_ERROR;
>>
>
> Hum.. is there a valid use case for this?
>
>
This is an existing work around from my_message_sql.
It's needed until bugs like
- Bug #36768 partition_info::check_partition_info() reports mal
formed warnings
are fixed, so that an assert can be used instead.
>> + if (str == NULL)
>> + str= ER(code);
>>
>
> Good.
>
>
>> + m_sql_errno= code;
>> +
>> + sqlstate= mysql_errno_to_sqlstate(m_sql_errno);
>> + memcpy(m_returned_sqlstate, sqlstate, SQLSTATE_LENGTH);
>> + m_returned_sqlstate[SQLSTATE_LENGTH]= '\0';
>> +
>> + set_builtin_message_text(str);
>> + if ((level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
>> + thd->really_abort_on_warning())
>> + {
>> + /*
>> + FIXME:
>> + push_warning and strict SQL_MODE case.
>> + */
>> + m_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
>> + thd->killed= THD::KILL_BAD_DATA;
>> + }
>> + else
>> + m_level= level;
>> + m_flags= flags;
>> +}
>> +
>> +void
>> +SQL_condition::set_builtin_message_text(const char* str)
>> +{
>> + const char* copy;
>> +
>> + copy= strdup_root(m_mem_root, str);
>> + m_message_text.set(copy, strlen(copy), error_message_charset_info);
>> + DBUG_ASSERT(! m_message_text.is_alloced());
>> +}
>> +
>> +const char*
>> +SQL_condition::get_message_text() const
>> +{
>> + return m_message_text.ptr();
>> +}
>> +
>> +int
>> +SQL_condition::get_message_octet_length() const
>> +{
>> + return m_message_text.length();
>> +}
>> +
>> +void
>> +SQL_condition::set_sqlstate(const char* sqlstate)
>> +{
>> + memcpy(m_returned_sqlstate, sqlstate, SQLSTATE_LENGTH);
>> + m_returned_sqlstate[SQLSTATE_LENGTH]= '\0';
>> +}
>> +
>> +Set_signal_information::Set_signal_information()
>> +{
>> + int i;
>> + for (i= FIRST_DIAG_SET_PROPERTY;
>> + i <= LAST_DIAG_SET_PROPERTY;
>> + i++)
>> + {
>> + m_item[i]= NULL;
>> + }
>>
>
> Replace this with clear();
>
>
Fixed
>> +}
>> +
>> +Set_signal_information::Set_signal_information(
>> + const Set_signal_information& set)
>> +{
>> + int i;
>> + for (i= FIRST_DIAG_SET_PROPERTY;
>> + i <= LAST_DIAG_SET_PROPERTY;
>> + i++)
>> + {
>> + m_item[i]= set.m_item[i];
>> + }
>> +}
>> +
>> +void Set_signal_information::clear()
>> +{
>> + int i;
>> + for (i= FIRST_DIAG_SET_PROPERTY;
>> + i <= LAST_DIAG_SET_PROPERTY;
>> + i++)
>> + {
>> + m_item[i]= NULL;
>> + }
>> +}
>> +
>> +int Abstract_signal::eval_sqlcode_sqlstate(THD *thd, SQL_condition *cond)
>> +{
>> + DBUG_ASSERT(m_cond);
>> + DBUG_ASSERT(cond);
>> +
>> + /*
>> + SIGNAL is restricted in sql_yacc.yy to only signal SQLSTATE conditions
>> + */
>> + DBUG_ASSERT(m_cond->type == sp_cond_type::state);
>> +
>> + cond->m_sql_errno= 0;
>> + cond->set_sqlstate(m_cond->sqlstate);
>> +
>> + return 0;
>> +}
>> +
>> +int Abstract_signal::eval_defaults(THD *thd, SQL_condition *cond)
>> +{
>> + const char* sqlstate= cond->get_sqlstate();
>> +
>> + DBUG_ASSERT((sqlstate[0] != '0') || (sqlstate[1] != '0'));
>> +
>> + if ((sqlstate[0] == '0') && (sqlstate[1] == '1'))
>> + {
>> + /* SQLSTATE class "01": warning */
>> + cond->set_builtin_message_text(ER(ER_SIGNAL_WARN));
>> + cond->m_level= MYSQL_ERROR::WARN_LEVEL_WARN;
>> + if (cond->m_sql_errno == 0)
>> + cond->m_sql_errno= ER_SIGNAL_WARN;
>> + }
>> + else if ((sqlstate[0] == '0') && (sqlstate[1] == '2'))
>> + {
>> + /* SQLSTATE class "02": not found */
>> + cond->set_builtin_message_text(ER(ER_SIGNAL_NOT_FOUND));
>> + cond->m_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
>> + if (cond->m_sql_errno == 0)
>> + cond->m_sql_errno= ER_SIGNAL_NOT_FOUND;
>> + }
>> + else
>> + {
>> + cond->set_builtin_message_text(ER(ER_SIGNAL_EXCEPTION));
>> + cond->m_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
>> + if (cond->m_sql_errno == 0)
>> + cond->m_sql_errno= ER_SIGNAL_EXCEPTION;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +int assign_condition_item(const char* name, THD *thd, Item *set,
>> + UTF8String64 *ci)
>> +{
>> + String str_value;
>> + String *str;
>> +
>> + DBUG_ENTER("assign_condition_item");
>> +
>> + if (set->is_null())
>> + {
>> + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, "NULL");
>> + DBUG_RETURN(1);
>> + }
>> +
>> + str= set->val_str(& str_value);
>> + ci->set(str);
>> + if (ci->is_truncated())
>> + {
>> + if (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
>> + MODE_STRICT_ALL_TABLES))
>> + {
>> + my_error(ER_COND_ITEM_TOO_LONG, MYF(0), name);
>> + DBUG_RETURN(1);
>> + }
>> +
>> + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
>> + WARN_COND_ITEM_TRUNCATED,
>> + ER(WARN_COND_ITEM_TRUNCATED),
>> + name);
>> + }
>> +
>> + DBUG_RETURN(0);
>> +}
>> +
>> +
>> +int Abstract_signal::eval_signal_informations(THD *thd, SQL_condition *cond)
>> +{
>> + Item *set;
>> + String str_value;
>> + String *str;
>> + int i;
>> + int result= 1;
>> +
>> + DBUG_ENTER("Abstract_signal::eval_signal_informations");
>> +
>> + for (i= FIRST_DIAG_SET_PROPERTY;
>> + i <= LAST_DIAG_SET_PROPERTY;
>> + i++)
>> + {
>> + set= m_set_signal_information.m_item[i];
>> + if (set)
>> + {
>> + if (! set->fixed)
>> + {
>> + if (set->fix_fields(thd, & set))
>> + goto end;
>> + m_set_signal_information.m_item[i]= set;
>> + }
>> + }
>> + }
>> +
>> + set= m_set_signal_information.m_item[DIAG_CLASS_ORIGIN];
>> + if (set != NULL)
>> + {
>> + if (assign_condition_item("CLASS_ORIGIN", thd, set,
>> + & cond->m_class_origin))
>> + goto end;
>> + }
>> +
>> + set= m_set_signal_information.m_item[DIAG_SUBCLASS_ORIGIN];
>> + if (set != NULL)
>> + {
>> + if (assign_condition_item("SUBCLASS_ORIGIN", thd, set,
>> + & cond->m_subclass_origin))
>> + goto end;
>> + }
>> +
>> + set= m_set_signal_information.m_item[DIAG_CONSTRAINT_CATALOG];
>> + if (set != NULL)
>> + {
>> + if (assign_condition_item("CONSTRAINT_CATALOG", thd, set,
>> + & cond->m_constraint_catalog))
>> + goto end;
>> + }
>> +
>> + set= m_set_signal_information.m_item[DIAG_CONSTRAINT_SCHEMA];
>> + if (set != NULL)
>> + {
>> + if (assign_condition_item("CONSTRAINT_SCHEMA", thd, set,
>> + & cond->m_constraint_schema))
>> + goto end;
>> + }
>> +
>> + set= m_set_signal_information.m_item[DIAG_CONSTRAINT_NAME];
>> + if (set != NULL)
>> + {
>> + if (assign_condition_item("CONSTRAINT_NAME", thd, set,
>> + & cond->m_constraint_name))
>> + goto end;
>> + }
>> +
>> + set= m_set_signal_information.m_item[DIAG_CATALOG_NAME];
>> + if (set != NULL)
>> + {
>> + if (assign_condition_item("CATALOG_NAME", thd, set,
>> + & cond->m_catalog_name))
>> + goto end;
>> + }
>> +
>> + set= m_set_signal_information.m_item[DIAG_SCHEMA_NAME];
>> + if (set != NULL)
>> + {
>> + if (assign_condition_item("SCHEMA_NAME", thd, set,
>> + & cond->m_schema_name))
>> + goto end;
>> + }
>> +
>> + set= m_set_signal_information.m_item[DIAG_TABLE_NAME];
>> + if (set != NULL)
>> + {
>> + if (assign_condition_item("TABLE_NAME", thd, set,
>> + & cond->m_table_name))
>> + goto end;
>> + }
>> +
>> + set= m_set_signal_information.m_item[DIAG_COLUMN_NAME];
>> + if (set != NULL)
>> + {
>> + if (assign_condition_item("COLUMN_NAME", thd, set,
>> + & cond->m_column_name))
>> + goto end;
>> + }
>> +
>> + set= m_set_signal_information.m_item[DIAG_CURSOR_NAME];
>> + if (set != NULL)
>> + {
>> + if (assign_condition_item("CURSOR_NAME", thd, set,
>> + & cond->m_cursor_name))
>> + goto end;
>> + }
>>
>
> Ugh. The variables could also be a array as m_item and we would just
> need to loop, index it, and assign as needed. We already have arrays
> containing the names, index, etc.
>
>
As discussed on IRC,
the array would need a *different* index only on string attributes and
not on every attributes,
and create complexity elsewhere.
I tried that earlier, and this leads to :
- a "generic loop" that calls an assign_item function,
- an assign item function that consist of a giant switch case for each item.
The code posted in the patch corresponds to this loop + switch unrolled,
and it's much better IMO.
No change, waiting for a concrete proposal to be evaluated.
>> + set= m_set_signal_information.m_item[DIAG_MESSAGE_TEXT];
>> + if (set != NULL)
>> + {
>> + if (set->is_null())
>> + {
>> + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "MESSAGE_TEXT", "NULL");
>> + goto end;
>> + }
>> + /*
>> + Enforce that SET MESSAGE_TEXT = <value> evaluates the value
>> + as VARCHAR(128) CHARACTER SET UTF8.
>> + */
>> + UTF8String128 utf8_text(thd->mem_root);
>>
>
> I can't find UTF8String128 anywhere..
>
>
It's in sql/sql_fixstring.h, in patch 8/10
>> + str= set->val_str(& str_value);
>> + utf8_text.set(str->ptr(), (size_t) str->length(), str->charset());
>> +
>> + if (utf8_text.is_truncated())
>> + {
>> + if (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
>> + MODE_STRICT_ALL_TABLES))
>> + {
>> + my_error(ER_COND_ITEM_TOO_LONG, MYF(0), "MESSAGE_TEXT");
>> + goto end;
>> + }
>> +
>> + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
>> + WARN_COND_ITEM_TRUNCATED,
>> + ER(WARN_COND_ITEM_TRUNCATED),
>> + "MESSAGE_TEXT");
>> + }
>> +
>> + /*
>> + Convert to the character set used with --language.
>> + This code should be removed when WL#751 is implemented.
>> + */
>> + String converted_text;
>> + converted_text.set_charset(error_message_charset_info);
>> + converted_text.append(utf8_text.ptr(), utf8_text.length(),
>> + (CHARSET_INFO *) utf8_text.charset());
>> + cond->set_builtin_message_text(converted_text.c_ptr_safe());
>> + }
>> +
>> + set= m_set_signal_information.m_item[DIAG_MYSQL_ERRNO];
>> + if (set != NULL)
>> + {
>> + if (set->is_null())
>> + {
>> + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "MYSQL_ERRNO", "NULL");
>> + goto end;
>> + }
>> + longlong code= set->val_int();
>> + if ((code <= 0) || (code > MAX_MYSQL_ERRNO))
>> + {
>> + str= set->val_str(& str_value);
>> + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0),
>> + "MYSQL_ERRNO", str->c_ptr_safe());
>> + goto end;
>> + }
>> + cond->m_sql_errno= (int) code;
>> + }
>> +
>> + /*
>> + The various item->val_xxx() methods don't return an error code,
>> + but flag thd in case of failure.
>> + */
>> + if (! thd->is_error())
>> + result= 0;
>> +
>> +end:
>> + for (i= FIRST_DIAG_SET_PROPERTY;
>> + i <= LAST_DIAG_SET_PROPERTY;
>> + i++)
>> + {
>> + set= m_set_signal_information.m_item[i];
>> + if (set)
>> + {
>> + if (set->fixed)
>> + set->cleanup();
>> + }
>> + }
>> +
>> + DBUG_RETURN(result);
>> +}
>> +
>> +int Abstract_signal::raise_condition(THD *thd, SQL_condition *cond)
>> +{
>> + int result= 1;
>> +
>> + DBUG_ENTER("Abstract_signal::raise_condition");
>> +
>> + DBUG_ASSERT(m_lex->query_tables == NULL);
>> +
>> + if (m_cond != NULL)
>> + {
>> + if (eval_sqlcode_sqlstate(thd, cond))
>> + DBUG_RETURN(result);
>> +
>> + if (eval_defaults(thd, cond))
>> + DBUG_RETURN(result);
>> + }
>> +
>> + if (eval_signal_informations(thd, cond))
>> + DBUG_RETURN(result);
>> +
>> + /* SIGNAL should not signal WARN_LEVEL_NOTE */
>> + DBUG_ASSERT((cond->m_level == MYSQL_ERROR::WARN_LEVEL_WARN) ||
>> + (cond->m_level == MYSQL_ERROR::WARN_LEVEL_ERROR));
>> +
>> + thd->raise_condition(cond);
>> +
>> + if (cond->m_level == MYSQL_ERROR::WARN_LEVEL_WARN)
>> + {
>> + my_ok(thd);
>> + result= 0;
>> + }
>> +
>> + DBUG_RETURN(result);
>> +}
>> +
>> +int SQLCOM_signal::execute(THD *thd)
>> +{
>> + int result= 1;
>> + SQL_condition cond(thd->mem_root);
>> +
>> + DBUG_ENTER("SQLCOM_signal::execute");
>> +
>> + thd->main_da.reset_diagnostics_area();
>> + thd->row_count_func= 0;
>> + mysql_reset_errors(thd, TRUE);
>> +
>> + result= raise_condition(thd, &cond);
>> +
>> + DBUG_RETURN(result);
>> +}
>> +
>> +
>> +int SQLCOM_resignal::execute(THD *thd)
>> +{
>> + SQL_condition *signaled;
>> + int result= 1;
>> +
>> + DBUG_ENTER("SQLCOM_resignal::execute");
>> +
>> + if (! thd->spcont || ! (signaled= thd->spcont->raised_condition()))
>> + {
>> + my_error(ER_RESIGNAL_NO_HANDLER, MYF(0));
>> + DBUG_RETURN(result);
>> + }
>> +
>> + if (m_cond == NULL)
>> + {
>> + /* RESIGNAL without signal_value */
>> + result= raise_condition(thd, signaled);
>> + DBUG_RETURN(result);
>> + }
>> +
>> + /* RESIGNAL with signal_value */
>> +
>> + /* Make room for 2 conditions */
>> + while ((thd->main_da.m_stmt_area.warn_list.elements > 0) &&
>> + ((thd->main_da.m_stmt_area.warn_list.elements + 2)
>> + > thd->variables.max_error_count))
>> + {
>> + thd->main_da.m_stmt_area.warn_list.pop();
>> + thd->main_da.m_stmt_area.m_more= TRUE;
>> + }
>> +
>> + thd->raise_condition_no_handler(signaled);
>> +
>> + SQL_condition new_cond(thd->mem_root);
>> + new_cond.deep_copy(signaled);
>> + result= raise_condition(thd, & new_cond);
>> + DBUG_RETURN(result);
>> +}
>> +
>>
In addition to the changes from this review, I also implemented a change
in sql_signal.h / sql_signal.cc
to implement the behavior documented yesterday in the RESIGNAL work log
(WL#2265).
RESIGNAL passed architecture review,
RESIGNAL is now code complete,
so that this code review should cover both SIGNAL and RESIGNAL.
Regards,
-- Marc.