#At file:///home/alik/MySQL/bzr/00/bug55847/mysql-trunk-bug55847/ based on revid:anders.song@stripped
3532 Alexander Nozdrin 2011-01-19
A patch for Bug#55847: SHOW WARNINGS returns empty result set
when SQLEXCEPTION is active.
The problem was in a hackish THD::no_warnings_for_error attribute.
When it was set, an error was not written to Warning_info -- only
Diagnostics_area state was changed. That means, Diagnostics_area
might contain error state, which is not present in Warning_info.
The fix is to remove that hack. This patch is needed to fix Bug 55843.
modified:
mysql-test/r/warnings.result
mysql-test/t/warnings.test
sql/sql_admin.cc
sql/sql_class.cc
sql/sql_class.h
sql/sql_error.cc
sql/sql_error.h
sql/sql_parse.cc
sql/sql_show.cc
sql/sql_trigger.cc
=== modified file 'mysql-test/r/warnings.result'
--- a/mysql-test/r/warnings.result 2010-08-30 06:38:09 +0000
+++ b/mysql-test/r/warnings.result 2011-01-19 12:50:36 +0000
@@ -316,3 +316,25 @@ SHOW ERRORS;
Level Code Message
Error 1051 Unknown table 'test.t1'
End of 5.0 tests
+
+-- Bug#55847
+
+DROP TABLE IF EXISTS t1;
+DROP FUNCTION IF EXISTS f1;
+CREATE TABLE t1(a INT UNIQUE);
+CREATE FUNCTION f1(x INT) RETURNS INT
+BEGIN
+INSERT INTO t1 VALUES(x);
+INSERT INTO t1 VALUES(x);
+RETURN x;
+END|
+
+SHOW TABLES WHERE f1(11) = 11;
+ERROR 23000: Duplicate entry '11' for key 'a'
+
+SHOW WARNINGS;
+Level Code Message
+Error 1062 Duplicate entry '11' for key 'a'
+
+DROP TABLE t1;
+DROP FUNCTION f1;
=== modified file 'mysql-test/t/warnings.test'
--- a/mysql-test/t/warnings.test 2009-11-13 10:17:53 +0000
+++ b/mysql-test/t/warnings.test 2011-01-19 12:50:36 +0000
@@ -228,3 +228,43 @@ DROP TABLE t1;
SHOW ERRORS;
--echo End of 5.0 tests
+
+#
+# Bug#55847: SHOW WARNINGS returns empty result set when SQLEXCEPTION is active
+#
+
+--echo
+--echo -- Bug#55847
+--echo
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+DROP FUNCTION IF EXISTS f1;
+--enable_warnings
+
+CREATE TABLE t1(a INT UNIQUE);
+
+delimiter |;
+
+CREATE FUNCTION f1(x INT) RETURNS INT
+BEGIN
+ INSERT INTO t1 VALUES(x);
+ INSERT INTO t1 VALUES(x);
+ RETURN x;
+END|
+
+delimiter ;|
+
+--echo
+
+--error ER_DUP_ENTRY
+SHOW TABLES WHERE f1(11) = 11;
+
+--echo
+
+SHOW WARNINGS;
+
+--echo
+
+DROP TABLE t1;
+DROP FUNCTION f1;
=== modified file 'sql/sql_admin.cc'
--- a/sql/sql_admin.cc 2011-01-10 13:26:13 +0000
+++ b/sql/sql_admin.cc 2011-01-19 12:50:36 +0000
@@ -336,13 +336,20 @@ static bool mysql_admin_table(THD* thd,
so any errors opening the table are logical errors.
In these cases it makes sense to report them.
*/
- if (!thd->locked_tables_mode)
- thd->no_warnings_for_error= no_warnings_for_error;
+ Warning_info wi(thd->query_id);
+ Warning_info *wi_saved= thd->warning_info;
+
+ if (!thd->locked_tables_mode && no_warnings_for_error)
+ thd->warning_info= &wi;
+
if (view_operator_func == NULL)
table->required_type=FRMTYPE_TABLE;
open_error= open_and_lock_tables(thd, table, TRUE, 0);
- thd->no_warnings_for_error= 0;
+
+ if (!thd->locked_tables_mode && no_warnings_for_error)
+ thd->warning_info= wi_saved;
+
table->next_global= save_next_global;
table->next_local= save_next_local;
thd->open_options&= ~extra_open_options;
=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc 2010-12-17 16:14:15 +0000
+++ b/sql/sql_class.cc 2011-01-19 12:50:36 +0000
@@ -584,7 +584,7 @@ THD::THD()
client_capabilities= 0; // minimalistic client
ull=0;
system_thread= NON_SYSTEM_THREAD;
- cleanup_done= abort_on_warning= no_warnings_for_error= 0;
+ cleanup_done= abort_on_warning= 0;
peer_port= 0; // For SHOW PROCESSLIST
transaction.m_pending_rows_event= 0;
transaction.on= 1;
@@ -857,10 +857,6 @@ MYSQL_ERROR* THD::raise_condition(uint s
query_cache_abort(&query_cache_tls);
- /* FIXME: broken special case */
- if (no_warnings_for_error && (level == MYSQL_ERROR::WARN_LEVEL_ERROR))
- DBUG_RETURN(NULL);
-
/* When simulating OOM, skip writing to error log to avoid mtr errors */
DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_RETURN(NULL););
=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h 2010-12-29 00:38:59 +0000
+++ b/sql/sql_class.h 2011-01-19 12:50:36 +0000
@@ -2147,7 +2147,6 @@ public:
bool enable_slow_log; /* enable slow log for current statement */
bool abort_on_warning;
bool got_warning; /* Set on call to push_warning() */
- bool no_warnings_for_error; /* no warnings on call to my_error() */
/* set during loop of derived table processing */
bool derived_tables_processing;
my_bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */
=== modified file 'sql/sql_error.cc'
--- a/sql/sql_error.cc 2010-11-18 16:34:56 +0000
+++ b/sql/sql_error.cc 2011-01-19 12:50:36 +0000
@@ -558,6 +558,20 @@ MYSQL_ERROR *Warning_info::push_warning(
return cond;
}
+MYSQL_ERROR *Warning_info::push_warning(THD *thd, const MYSQL_ERROR *sql_condition)
+{
+ MYSQL_ERROR *new_condition= push_warning(thd,
+ sql_condition->get_sql_errno(),
+ sql_condition->get_sqlstate(),
+ sql_condition->get_level(),
+ sql_condition->get_message_text());
+
+ if (new_condition)
+ new_condition->copy_opt_attributes(sql_condition);
+
+ return new_condition;
+}
+
/*
Push the warning to error list if there is still room in the list
=== modified file 'sql/sql_error.h'
--- a/sql/sql_error.h 2010-11-18 16:34:56 +0000
+++ b/sql/sql_error.h 2011-01-19 12:50:36 +0000
@@ -383,19 +383,13 @@ public:
void append_warnings(THD *thd, List<MYSQL_ERROR> *src)
{
MYSQL_ERROR *err;
- MYSQL_ERROR *copy;
List_iterator_fast<MYSQL_ERROR> it(*src);
/*
Don't use ::push_warning() to avoid invocation of condition
handlers or escalation of warnings to errors.
*/
while ((err= it++))
- {
- copy= Warning_info::push_warning(thd, err->get_sql_errno(), err->get_sqlstate(),
- err->get_level(), err->get_message_text());
- if (copy)
- copy->copy_opt_attributes(err);
- }
+ Warning_info::push_warning(thd, err);
}
/**
@@ -461,6 +455,9 @@ public:
MYSQL_ERROR::enum_warning_level level,
const char* msg);
+ /** Add a new condition to the current list. */
+ MYSQL_ERROR *push_warning(THD *thd, const MYSQL_ERROR *sql_condition);
+
/**
Set the read only status for this statement area.
This is a privileged operation, reserved for the implementation of
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc 2011-01-15 05:56:24 +0000
+++ b/sql/sql_parse.cc 2011-01-19 12:50:36 +0000
@@ -7274,9 +7274,16 @@ bool parse_sql(THD *thd,
bool mysql_parse_status= MYSQLparse(thd) != 0;
- /* Check that if MYSQLparse() failed, thd->is_error() is set. */
+ /*
+ Check that if MYSQLparse() failed either thd->is_error() is set, or an
+ internal error handler is set.
+
+ If there is an internal error handler, it might be used to catch
+ parsing error, so thd->is_error() is not set.
+ */
DBUG_ASSERT(!mysql_parse_status ||
+ (mysql_parse_status && thd->get_internal_handler()) ||
(mysql_parse_status && thd->is_error()));
/* Reset parser state. */
=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc 2011-01-07 12:03:21 +0000
+++ b/sql/sql_show.cc 2011-01-19 12:50:36 +0000
@@ -3412,6 +3412,27 @@ end:
}
+class Trigger_error_handler : public Internal_error_handler
+{
+public:
+ bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl)
+ {
+ if (sql_errno == ER_PARSE_ERROR ||
+ sql_errno == ER_TRG_NO_DEFINER ||
+ sql_errno == ER_TRG_NO_CREATION_CTX)
+ return true;
+
+ return false;
+ }
+};
+
+
+
/**
@brief Fill I_S tables whose data are retrieved
from frm files and storage engine
@@ -3561,7 +3582,6 @@ int get_all_tables(THD *thd, TABLE_LIST
acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name->str, 0))
#endif
{
- thd->no_warnings_for_error= 1;
List<LEX_STRING> table_names;
int res= make_table_name_list(thd, &table_names, lex,
&lookup_field_vals,
@@ -3610,9 +3630,23 @@ int get_all_tables(THD *thd, TABLE_LIST
if (!(table_open_method & ~OPEN_FRM_ONLY) &&
!with_i_schema)
{
- if (!fill_schema_table_from_frm(thd, tables, schema_table, db_name,
- table_name, schema_table_idx,
- can_deadlock))
+ /*
+ Here we need to filter out warnings, which can happen
+ during loading of triggers in fill_schema_table_from_frm(),
+ because we don't need those warnings to pollute output of
+ SELECT from I_S / SHOW-statements.
+ */
+
+ Trigger_error_handler err_handler;
+ thd->push_internal_handler(&err_handler);
+
+ int res= fill_schema_table_from_frm(thd, tables, schema_table, db_name,
+ table_name, schema_table_idx,
+ can_deadlock);
+
+ thd->pop_internal_handler();
+
+ if (!res)
continue;
}
@@ -3622,7 +3656,6 @@ int get_all_tables(THD *thd, TABLE_LIST
Set the parent lex of 'sel' because it is needed by
sel.init_query() which is called inside make_table_list.
*/
- thd->no_warnings_for_error= 1;
sel.parent_lex= lex;
/* db_name can be changed in make_table_list() func */
if (!thd->make_lex_string(&orig_db_name, db_name->str,
@@ -6664,6 +6697,51 @@ int make_schema_select(THD *thd, SELECT_
}
+static bool do_fill_table(THD *thd,
+ TABLE_LIST *table_list,
+ JOIN_TAB *join_table)
+{
+ Warning_info wi(thd->query_id);
+ Warning_info *wi_saved= thd->warning_info;
+
+ thd->warning_info= &wi;
+
+ bool res= table_list->schema_table->fill_table(
+ thd, table_list, join_table->select_cond);
+
+ thd->warning_info= wi_saved;
+
+ // Pass an error if any.
+
+ if (thd->stmt_da->is_error())
+ {
+ thd->warning_info->push_warning(thd,
+ thd->stmt_da->sql_errno(),
+ thd->stmt_da->get_sqlstate(),
+ MYSQL_ERROR::WARN_LEVEL_ERROR,
+ thd->stmt_da->message());
+ }
+
+ // Pass warnings (if any).
+
+ List_iterator_fast<MYSQL_ERROR> it(wi.warn_list());
+ while (true)
+ {
+ MYSQL_ERROR *err = it++;
+
+ if (!err)
+ break;
+
+ if (err->get_level() == MYSQL_ERROR::WARN_LEVEL_ERROR)
+ continue;
+
+ thd->warning_info->push_warning(thd, err);
+ }
+
+ return res;
+}
+
+
/*
Fill temporary schema tables before SELECT
@@ -6686,7 +6764,6 @@ bool get_schema_tables_result(JOIN *join
bool result= 0;
DBUG_ENTER("get_schema_tables_result");
- thd->no_warnings_for_error= 1;
for (JOIN_TAB *tab= join->join_tab; tab < tmp_join_tab; tab++)
{
if (!tab->table || !tab->table->pos_in_table_list)
@@ -6737,8 +6814,7 @@ bool get_schema_tables_result(JOIN *join
else
table_list->table->file->stats.records= 0;
- if (table_list->schema_table->fill_table(thd, table_list,
- tab->select_cond))
+ if (do_fill_table(thd, table_list, tab))
{
result= 1;
join->error= 1;
@@ -6750,7 +6826,6 @@ bool get_schema_tables_result(JOIN *join
table_list->schema_table_state= executed_place;
}
}
- thd->no_warnings_for_error= 0;
DBUG_RETURN(result);
}
=== modified file 'sql/sql_trigger.cc'
--- a/sql/sql_trigger.cc 2010-11-29 16:27:58 +0000
+++ b/sql/sql_trigger.cc 2011-01-19 12:50:36 +0000
@@ -1219,13 +1219,12 @@ bool Table_triggers_list::check_n_load(T
DBUG_RETURN(1); // EOM
}
-
- if (!thd->no_warnings_for_error)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TRG_NO_CREATION_CTX,
- ER(ER_TRG_NO_CREATION_CTX),
- (const char*) db,
- (const char*) table_name);
+
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TRG_NO_CREATION_CTX,
+ ER(ER_TRG_NO_CREATION_CTX),
+ (const char*) db,
+ (const char*) table_name);
if (!(trg_client_cs_name= alloc_lex_string(&table->mem_root)) ||
!(trg_connection_cl_name= alloc_lex_string(&table->mem_root)) ||
@@ -1356,12 +1355,12 @@ bool Table_triggers_list::check_n_load(T
MySQL, which does not support triggers definers. We should emit
warning here.
*/
- if (!thd->no_warnings_for_error)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TRG_NO_DEFINER, ER(ER_TRG_NO_DEFINER),
- (const char*) db,
- (const char*) sp->m_name.str);
-
+
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TRG_NO_DEFINER, ER(ER_TRG_NO_DEFINER),
+ (const char*) db,
+ (const char*) sp->m_name.str);
+
/*
Set definer to the '' to correct displaying in the information
schema.
Attachment: [text/bzr-bundle] bzr/alexander.nozdrin@oracle.com-20110119125036-1i9rl6gunxvu9va0.bundle
| Thread |
|---|
| • bzr commit into mysql-trunk branch (alexander.nozdrin:3532) Bug#55847 | Alexander Nozdrin | 19 Jan |