List:Commits« Previous MessageNext Message »
From:Alexander Nozdrin Date:July 21 2010 2:14pm
Subject:bzr commit into mysql-trunk-bugfixing branch (alik:3133) Bug#23032
View as plain text  
#At file:///mnt/raid/alik/MySQL/bzr/00/bug23032/mysql-trunk-bugfixing-bug23032/ based on revid:jonathan.perkin@stripped

 3133 Alexander Nozdrin	2010-07-21
      Preliminary patch for Bug#23032 (Handlers declared in a SP
      do not handle warnings generated in sub-SP).

    modified:
      mysql-test/r/signal.result
      mysql-test/r/sp-error.result
      mysql-test/r/sp.result
      mysql-test/t/signal.test
      mysql-test/t/sp-error.test
      mysql-test/t/sp.test
      sql/sp_head.cc
      sql/sp_pcontext.h
      sql/sp_rcontext.cc
      sql/sp_rcontext.h
      sql/sql_class.cc
      sql/sql_class.h
      sql/sql_delete.cc
      sql/sql_error.cc
      sql/sql_error.h
      sql/sql_insert.cc
      sql/sql_parse.cc
      sql/sql_select.cc
      sql/sql_update.cc
=== modified file 'mysql-test/r/signal.result'
--- a/mysql-test/r/signal.result	2010-02-23 18:43:26 +0000
+++ b/mysql-test/r/signal.result	2010-07-21 14:14:46 +0000
@@ -2135,15 +2135,17 @@ DECLARE x CONDITION FOR SQLSTATE '42000'
 DECLARE EXIT HANDLER FOR x
 BEGIN
 SELECT '2';
+SHOW WARNINGS;
 RESIGNAL x SET MYSQL_ERRNO = 9999;
 END;
 BEGIN
 DECLARE EXIT HANDLER FOR x
 BEGIN
 SELECT '1';
+SHOW WARNINGS;
 RESIGNAL x SET
 SCHEMA_NAME = 'test',
-MYSQL_ERRNO= 1231;
+MYSQL_ERRNO= 1232;
 END;
 /* Raises ER_WRONG_VALUE_FOR_VAR : 1231, SQLSTATE 42000 */
 SET @@sql_mode=NULL;
@@ -2156,6 +2158,7 @@ DECLARE x CONDITION for SQLSTATE '42000'
 DECLARE EXIT HANDLER FOR x
 BEGIN
 SELECT '3';
+SHOW WARNINGS;
 RESIGNAL x SET
 MESSAGE_TEXT = 'Hi, I am a useless error message',
 MYSQL_ERRNO = 9999;
@@ -2166,15 +2169,21 @@ $$
 CALL peter_p2() $$
 1
 1
+Level	Code	Message
 2
 2
+Level	Code	Message
+Error	1231	Variable 'sql_mode' can't be set to the value of 'NULL'
 3
 3
+Level	Code	Message
+Error	1231	Variable 'sql_mode' can't be set to the value of 'NULL'
+Error	1232	Variable 'sql_mode' can't be set to the value of 'NULL'
 ERROR 42000: Hi, I am a useless error message
 show warnings $$
 Level	Code	Message
 Error	1231	Variable 'sql_mode' can't be set to the value of 'NULL'
-Error	1231	Variable 'sql_mode' can't be set to the value of 'NULL'
+Error	1232	Variable 'sql_mode' can't be set to the value of 'NULL'
 Error	9999	Variable 'sql_mode' can't be set to the value of 'NULL'
 Error	9999	Hi, I am a useless error message
 drop procedure peter_p1 $$

=== modified file 'mysql-test/r/sp-error.result'
--- a/mysql-test/r/sp-error.result	2010-02-24 13:52:27 +0000
+++ b/mysql-test/r/sp-error.result	2010-07-21 14:14:46 +0000
@@ -1714,3 +1714,114 @@ DROP PROCEDURE p1;
 DROP VIEW v1;
 DROP TABLE t1;
 End of 5.1 tests
+DROP PROCEDURE IF EXISTS p1;
+DROP PROCEDURE IF EXISTS p2;
+CREATE PROCEDURE p1()
+BEGIN
+SELECT CAST('10 ' as unsigned integer);
+SELECT 1;
+CALL p2();
+END|
+CREATE PROCEDURE p2()
+BEGIN
+SELECT CAST('10 ' as unsigned integer);
+END|
+CALL p1();
+CAST('10 ' as unsigned integer)
+10
+1
+1
+CAST('10 ' as unsigned integer)
+10
+Warnings:
+Warning	1292	Truncated incorrect INTEGER value: '10 '
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+DROP PROCEDURE IF EXISTS p1;
+DROP PROCEDURE IF EXISTS p2;
+DROP PROCEDURE IF EXISTS p3;
+DROP PROCEDURE IF EXISTS p4;
+DROP PROCEDURE IF EXISTS p5;
+DROP PROCEDURE IF EXISTS p6;
+CREATE PROCEDURE p1()
+BEGIN
+DECLARE c INT DEFAULT 0;
+DECLARE CONTINUE HANDLER FOR SQLWARNING SET c = c + 1;
+CALL p2();
+CALL p3();
+CALL p4();
+SELECT c;
+SELECT @@warning_count;
+SHOW WARNINGS;
+END|
+CREATE PROCEDURE p2()
+BEGIN
+SELECT CAST('10 ' as unsigned integer);
+END|
+CREATE PROCEDURE p3()
+BEGIN
+SELECT CAST('10 ' as unsigned integer);
+SELECT 1;
+END|
+CREATE PROCEDURE p4()
+BEGIN
+SELECT CAST('10 ' as unsigned integer);
+CALL p2();
+END|
+CREATE PROCEDURE p5()
+BEGIN
+SELECT CAST('10 ' as unsigned integer);
+SHOW WARNINGS;
+END|
+CREATE PROCEDURE P6()
+BEGIN
+DECLARE c INT DEFAULT 0;
+DECLARE CONTINUE HANDLER FOR SQLWARNING SET c = c + 1;
+CALL p5();
+SELECT c;
+END|
+CALL p1();
+CAST('10 ' as unsigned integer)
+10
+CAST('10 ' as unsigned integer)
+10
+1
+1
+CAST('10 ' as unsigned integer)
+10
+CAST('10 ' as unsigned integer)
+10
+c
+3
+@@warning_count
+0
+Level	Code	Message
+CALL p6();
+CAST('10 ' as unsigned integer)
+10
+Level	Code	Message
+Warning	1292	Truncated incorrect INTEGER value: '10 '
+c
+1
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+DROP PROCEDURE p3;
+DROP PROCEDURE p4;
+DROP PROCEDURE p5;
+DROP PROCEDURE p6;
+DROP TABLE IF EXISTS t1;
+DROP PROCEDURE IF EXISTS p1;
+CREATE TABLE t1 (a INT, b INT NOT NULL);
+CREATE PROCEDURE p1()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLWARNING SELECT 'warning';
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SELECT 'exception';
+INSERT INTO t1 VALUES (CAST('10 ' AS SIGNED), NULL);
+END|
+CALL p1();
+exception
+exception
+Warnings:
+Warning	1292	Truncated incorrect INTEGER value: '10 '
+DROP TABLE t1;
+DROP PROCEDURE p1;

=== modified file 'mysql-test/r/sp.result'
--- a/mysql-test/r/sp.result	2010-05-14 05:28:51 +0000
+++ b/mysql-test/r/sp.result	2010-07-21 14:14:46 +0000
@@ -4546,6 +4546,8 @@ drop procedure if exists bug15231_1|
 drop procedure if exists bug15231_2|
 drop procedure if exists bug15231_3|
 drop procedure if exists bug15231_4|
+drop procedure if exists bug15231_5|
+drop procedure if exists bug15231_6|
 create table t3 (id int not null)|
 create procedure bug15231_1()
 begin
@@ -4568,7 +4570,7 @@ end|
 create procedure bug15231_3()
 begin
 declare exit handler for sqlwarning
-select 'Caught it (wrong)' as 'Result';
+select 'Caught it (correct)' as 'Result';
 call bug15231_4();
 end|
 create procedure bug15231_4()
@@ -4576,6 +4578,20 @@ begin
 declare x decimal(2,1);
 set x = 'zap';
 select 'Missed it (correct)' as 'Result';
+show warnings;
+end|
+create procedure bug15231_5()
+begin
+declare exit handler for sqlwarning
+select 'Caught it (wrong)' as 'Result';
+call bug15231_6();
+end|
+create procedure bug15231_6()
+begin
+declare x decimal(2,1);
+set x = 'zap';
+select 'Missed it (correct)' as 'Result';
+select id from t3;
 end|
 call bug15231_1()|
 1
@@ -4583,19 +4599,25 @@ Before NOT FOUND condition is triggered
 2
 After NOT FOUND condtition is triggered
 xid	xdone
-1	0
-Warnings:
-Warning	1329	No data - zero rows fetched, selected, or processed
+1	1
 call bug15231_3()|
 Result
 Missed it (correct)
-Warnings:
+Level	Code	Message
 Warning	1366	Incorrect decimal value: 'zap' for column 'x' at row 1
-drop table if exists t3|
-drop procedure if exists bug15231_1|
-drop procedure if exists bug15231_2|
-drop procedure if exists bug15231_3|
-drop procedure if exists bug15231_4|
+Result
+Caught it (correct)
+call bug15231_5()|
+Result
+Missed it (correct)
+id
+drop table t3|
+drop procedure bug15231_1|
+drop procedure bug15231_2|
+drop procedure bug15231_3|
+drop procedure bug15231_4|
+drop procedure bug15231_5|
+drop procedure bug15231_6|
 drop procedure if exists bug15011|
 create table t3 (c1 int primary key)|
 insert into t3 values (1)|

=== modified file 'mysql-test/t/signal.test'
--- a/mysql-test/t/signal.test	2009-09-10 09:18:29 +0000
+++ b/mysql-test/t/signal.test	2010-07-21 14:14:46 +0000
@@ -2408,6 +2408,7 @@ BEGIN
   DECLARE EXIT HANDLER FOR x
   BEGIN
     SELECT '2';
+    SHOW WARNINGS;
     RESIGNAL x SET MYSQL_ERRNO = 9999;
   END;
 
@@ -2415,9 +2416,10 @@ BEGIN
     DECLARE EXIT HANDLER FOR x
     BEGIN
       SELECT '1';
+      SHOW WARNINGS;
       RESIGNAL x SET
         SCHEMA_NAME = 'test',
-        MYSQL_ERRNO= 1231;
+        MYSQL_ERRNO= 1232;
     END;
     /* Raises ER_WRONG_VALUE_FOR_VAR : 1231, SQLSTATE 42000 */
     SET @@sql_mode=NULL;
@@ -2431,6 +2433,7 @@ BEGIN
   DECLARE EXIT HANDLER FOR x
   BEGIN
     SELECT '3';
+    SHOW WARNINGS;
     RESIGNAL x SET
       MESSAGE_TEXT = 'Hi, I am a useless error message',
       MYSQL_ERRNO = 9999;

=== modified file 'mysql-test/t/sp-error.test'
--- a/mysql-test/t/sp-error.test	2010-02-04 22:08:08 +0000
+++ b/mysql-test/t/sp-error.test	2010-07-21 14:14:46 +0000
@@ -2543,3 +2543,125 @@ DROP TABLE t1;
 
 --echo End of 5.1 tests
 
+#
+# Bug#23032: Handlers declared in a SP do not handle warnings generated in sub-SP
+#
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS p1;
+DROP PROCEDURE IF EXISTS p2;
+--enable_warnings
+
+delimiter |;
+
+CREATE PROCEDURE p1()
+  BEGIN
+    SELECT CAST('10 ' as unsigned integer);
+    SELECT 1;
+    CALL p2();
+  END|
+
+CREATE PROCEDURE p2()
+  BEGIN
+    SELECT CAST('10 ' as unsigned integer);
+  END|
+
+delimiter ;|
+
+CALL p1();
+
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS p1;
+DROP PROCEDURE IF EXISTS p2;
+DROP PROCEDURE IF EXISTS p3;
+DROP PROCEDURE IF EXISTS p4;
+DROP PROCEDURE IF EXISTS p5;
+DROP PROCEDURE IF EXISTS p6;
+--enable_warnings
+
+delimiter |;
+
+CREATE PROCEDURE p1()
+  BEGIN
+    DECLARE c INT DEFAULT 0;
+    DECLARE CONTINUE HANDLER FOR SQLWARNING SET c = c + 1;
+    CALL p2();
+    CALL p3();
+    CALL p4();
+    SELECT c;
+    SELECT @@warning_count;
+    SHOW WARNINGS;
+  END|
+
+CREATE PROCEDURE p2()
+  BEGIN
+    SELECT CAST('10 ' as unsigned integer);
+  END|
+
+CREATE PROCEDURE p3()
+  BEGIN
+    SELECT CAST('10 ' as unsigned integer);
+    SELECT 1;
+  END|
+
+CREATE PROCEDURE p4()
+  BEGIN
+    SELECT CAST('10 ' as unsigned integer);
+    CALL p2();
+  END|
+
+CREATE PROCEDURE p5()
+  BEGIN
+    SELECT CAST('10 ' as unsigned integer);
+    SHOW WARNINGS;
+  END|
+
+CREATE PROCEDURE P6()
+  BEGIN
+    DECLARE c INT DEFAULT 0;
+    DECLARE CONTINUE HANDLER FOR SQLWARNING SET c = c + 1;
+    CALL p5();
+    SELECT c;
+  END|
+
+delimiter ;|
+
+CALL p1();
+CALL p6();
+
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+DROP PROCEDURE p3;
+DROP PROCEDURE p4;
+DROP PROCEDURE p5;
+DROP PROCEDURE p6;
+
+#
+# Bug#36185: Incorrect precedence for warning and exception handlers
+#
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+DROP PROCEDURE IF EXISTS p1;
+--enable_warnings
+
+CREATE TABLE t1 (a INT, b INT NOT NULL);
+
+delimiter |;
+
+CREATE PROCEDURE p1()
+BEGIN
+  DECLARE CONTINUE HANDLER FOR SQLWARNING SELECT 'warning';
+  DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SELECT 'exception';
+  INSERT INTO t1 VALUES (CAST('10 ' AS SIGNED), NULL);
+END|
+
+delimiter ;|
+
+CALL p1();
+
+DROP TABLE t1;
+DROP PROCEDURE p1;

=== modified file 'mysql-test/t/sp.test'
--- a/mysql-test/t/sp.test	2010-04-13 21:56:19 +0000
+++ b/mysql-test/t/sp.test	2010-07-21 14:14:46 +0000
@@ -5432,6 +5432,8 @@ drop procedure if exists bug15231_1|
 drop procedure if exists bug15231_2|
 drop procedure if exists bug15231_3|
 drop procedure if exists bug15231_4|
+drop procedure if exists bug15231_5|
+drop procedure if exists bug15231_6|
 --enable_warnings
 
 create table t3 (id int not null)|
@@ -5461,7 +5463,7 @@ end|
 create procedure bug15231_3()
 begin
   declare exit handler for sqlwarning
-    select 'Caught it (wrong)' as 'Result';
+    select 'Caught it (correct)' as 'Result';
 
   call bug15231_4();
 end|
@@ -5472,16 +5474,37 @@ begin
 
   set x = 'zap';
   select 'Missed it (correct)' as 'Result';
+  show warnings;
+end|
+
+create procedure bug15231_5()
+begin
+  declare exit handler for sqlwarning
+    select 'Caught it (wrong)' as 'Result';
+
+  call bug15231_6();
+end|
+
+create procedure bug15231_6()
+begin
+  declare x decimal(2,1);
+
+  set x = 'zap';
+  select 'Missed it (correct)' as 'Result';
+  select id from t3;
 end|
 
 call bug15231_1()|
 call bug15231_3()|
+call bug15231_5()|
 
-drop table if exists t3|
-drop procedure if exists bug15231_1|
-drop procedure if exists bug15231_2|
-drop procedure if exists bug15231_3|
-drop procedure if exists bug15231_4|
+drop table t3|
+drop procedure bug15231_1|
+drop procedure bug15231_2|
+drop procedure bug15231_3|
+drop procedure bug15231_4|
+drop procedure bug15231_5|
+drop procedure bug15231_6|
 
 
 #

=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc	2010-06-17 13:31:51 +0000
+++ b/sql/sp_head.cc	2010-07-21 14:14:46 +0000
@@ -1074,6 +1074,127 @@ void sp_head::recursion_level_error(THD 
 
 
 /**
+  Check if an exception has occurred and a handler has been found
+
+  @param thd thread handle
+  @param ctx runtime context of the stored routine
+  @param ip location of the found handler
+  @param instr stored procedure instruction
+  @param execute_arena per-instruction arena
+  @param backup_arena per-instruction arena
+
+  @return TRUE if a handler has been found, FALSE otherwise.
+*/
+
+static bool
+find_and_push_handler(THD *thd, sp_rcontext *ctx)
+{
+  /* Fatal errors are not catchable. Do nothing if killed. */
+  if (! ctx || thd->is_fatal_error || thd->killed_errno())
+    return FALSE;
+
+  /* Precaution: ditch any previously found handler. */
+  ctx->clear_handler();
+
+  if (thd->is_error())
+  {
+    if (ctx->find_handler(thd,
+                          thd->stmt_da->sql_errno(),
+                          thd->stmt_da->get_sqlstate(),
+                          MYSQL_ERROR::WARN_LEVEL_ERROR,
+                          thd->stmt_da->message()))
+    {
+      List_iterator<MYSQL_ERROR> it(thd->warning_info->warn_list());
+      MYSQL_ERROR *err;
+      while ((err= it++))
+      {
+
+        if (err->get_level() != MYSQL_ERROR::WARN_LEVEL_ERROR ||
+            err->get_sql_errno() != thd->stmt_da->sql_errno() ||
+            strcmp(err->get_sqlstate(), thd->stmt_da->get_sqlstate()) ||
+            strcmp(err->get_message_text(), thd->stmt_da->message()))
+          continue;
+
+        // This is our error. Because exception handler is found, it should
+        // be cleared from the warning list.
+        it.remove();
+        thd->warning_info->remove_warning(thd, err);
+        break;
+      }
+
+      return TRUE;
+    }
+    else
+    {
+      ctx->end_partial_result_set= FALSE;
+      return FALSE;
+    }
+  }
+
+  if (thd->warning_info->statement_warn_count())
+  {
+    List_iterator<MYSQL_ERROR> it(thd->warning_info->warn_list());
+    MYSQL_ERROR *err;
+    while ((err= it++))
+    {
+      if (err->get_level() != MYSQL_ERROR::WARN_LEVEL_WARN)
+        continue;
+
+      if (ctx->find_handler(thd,
+                            err->get_sql_errno(),
+                            err->get_sqlstate(),
+                            err->get_level(),
+                            err->get_message_text()))
+      {
+        thd->warning_info->clear_warning_info(thd->warning_info->warn_id());
+        return TRUE;
+      }
+    }
+  }
+
+  return FALSE;
+}
+
+
+static bool activate_handler(THD *thd, sp_rcontext *ctx,
+                             uint *ip,
+                             sp_instr *instr,
+                             Query_arena *execute_arena,
+                             Query_arena *backup_arena)
+{
+  int found_handler_index= ctx->get_found_handler_index();
+
+  if (found_handler_index < 0)
+    return FALSE;
+
+  const sp_handler_t *found_handler= ctx->get_handler(found_handler_index);
+
+  switch (found_handler->type) {
+  case SP_HANDLER_NONE:
+    break;
+  case SP_HANDLER_CONTINUE:
+    thd->restore_active_arena(execute_arena, backup_arena);
+    thd->set_n_backup_active_arena(execute_arena, backup_arena);
+    ctx->push_hstack(instr->get_cont_dest());
+    /* Fall through */
+  default:
+    if (ctx->end_partial_result_set)
+      thd->protocol->end_partial_result_set(thd);
+    *ip= found_handler->handler;
+    ctx->clear_handler();
+    ctx->enter_handler(*ip, found_handler_index);
+    thd->clear_error();
+    /* Some errors set thd->killed (e.g. "bad data"). */
+    thd->killed= THD::NOT_KILLED;
+  }
+
+  ctx->end_partial_result_set= FALSE;
+
+  return TRUE;
+}
+
+
+/**
   Execute the routine. The main instruction jump loop is there.
   Assume the parameters already set.
   @todo
@@ -1094,7 +1215,7 @@ sp_head::execute(THD *thd)
   LEX_STRING saved_cur_db_name=
     { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
   bool cur_db_changed= FALSE;
-  sp_rcontext *ctx;
+  sp_rcontext *ctx= thd->spcont;
   bool err_status= FALSE;
   uint ip= 0;
   ulong save_sql_mode;
@@ -1155,8 +1276,6 @@ sp_head::execute(THD *thd)
     goto done;
   }
 
-  if ((ctx= thd->spcont))
-    ctx->clear_handler();
   thd->is_slave_error= 0;
   old_arena= thd->stmt_arena;
 
@@ -1241,7 +1360,6 @@ sp_head::execute(THD *thd)
   do
   {
     sp_instr *i;
-    uint hip;
 
 #if defined(ENABLED_PROFILING)
     /*
@@ -1263,6 +1381,10 @@ sp_head::execute(THD *thd)
       break;
     }
 
+    /* Reset number of warnings for this query */
+    // XXX thd->total_warn_count= 0;
+    thd->warning_info->reset_for_next_command();
+
     DBUG_PRINT("execute", ("Instruction %u", ip));
 
     /*
@@ -1306,41 +1428,11 @@ sp_head::execute(THD *thd)
     thd->cleanup_after_query();
     free_root(&execute_mem_root, MYF(0));
 
-    /*
-      Check if an exception has occurred and a handler has been found
-      Note: We have to check even if err_status == FALSE, since warnings (and
-      some errors) don't return a non-zero value. We also have to check even
-      if thd->killed != 0, since some errors return with this even when a
-      handler has been found (e.g. "bad data").
-    */
-    if (ctx)
-    {
-      uint handler_index;
+    find_and_push_handler(thd, ctx);
 
-      switch (ctx->found_handler(& hip, & handler_index)) {
-      case SP_HANDLER_NONE:
-        break;
-      case SP_HANDLER_CONTINUE:
-        thd->restore_active_arena(&execute_arena, &backup_arena);
-        thd->set_n_backup_active_arena(&execute_arena, &backup_arena);
-        ctx->push_hstack(i->get_cont_dest());
-        /* Fall through */
-      default:
-        if (ctx->end_partial_result_set)
-          thd->protocol->end_partial_result_set(thd);
-        ip= hip;
-        err_status= FALSE;
-        ctx->clear_handler();
-        ctx->enter_handler(hip, handler_index);
-        thd->clear_error();
-        thd->is_fatal_error= 0;
-        thd->killed= THD::NOT_KILLED;
-        thd->mysys_var->abort= 0;
-        continue;
-      }
+    if (activate_handler(thd, ctx, &ip, i, &execute_arena, &backup_arena))
+      err_status= FALSE;
 
-      ctx->end_partial_result_set= FALSE;
-    }
   } while (!err_status && !thd->killed && !thd->is_fatal_error);
 
 #if defined(ENABLED_PROFILING)
@@ -3012,23 +3104,14 @@ sp_instr_set::exec_core(THD *thd, uint *
 {
   int res= thd->spcont->set_variable(thd, m_offset, &m_value);
 
-  if (res && thd->spcont->found_handler_here())
+  if (res)
   {
-    /*
-      Failed to evaluate the value, and a handler has been found. Reset the
-      variable to NULL.
-    */
+    /* Failed to evaluate the value. Reset the variable to NULL. */
 
     if (thd->spcont->set_variable(thd, m_offset, 0))
     {
       /* If this also failed, let's abort. */
-
-      sp_rcontext *spcont= thd->spcont;
-
-      thd->spcont= NULL;           /* Avoid handlers */
-      my_error(ER_OUT_OF_RESOURCES, MYF(0));
-      spcont->clear_handler();
-      thd->spcont= spcont;
+      my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
     }
   }
 
@@ -3561,18 +3644,6 @@ sp_instr_copen::execute(THD *thd, uint *
     if (thd->stmt_arena->free_list)
       cleanup_items(thd->stmt_arena->free_list);
     thd->stmt_arena= old_arena;
-    /*
-      Work around the fact that errors in selects are not returned properly
-      (but instead converted into a warning), so if a condition handler
-      caught, we have lost the result code.
-    */
-    if (!res)
-    {
-      uint dummy1, dummy2;
-
-      if (thd->spcont->found_handler(&dummy1, &dummy2))
-        res= -1;
-    }
     /* TODO: Assert here that we either have an error or a cursor */
   }
   DBUG_RETURN(res);
@@ -3748,13 +3819,11 @@ sp_instr_set_case_expr::exec_core(THD *t
 {
   int res= thd->spcont->set_case_expr(thd, m_case_expr_id, &m_case_expr);
 
-  if (res &&
-      !thd->spcont->get_case_expr(m_case_expr_id) &&
-      thd->spcont->found_handler_here())
+  if (res && !thd->spcont->get_case_expr(m_case_expr_id))
   {
     /*
       Failed to evaluate the value, the case expression is still not
-      initialized, and a handler has been found. Set to NULL so we can continue.
+      initialized. Set to NULL so we can continue.
     */
 
     Item *null_item= new Item_null();
@@ -3763,13 +3832,7 @@ sp_instr_set_case_expr::exec_core(THD *t
         thd->spcont->set_case_expr(thd, m_case_expr_id, &null_item))
     {
       /* If this also failed, we have to abort. */
-
-      sp_rcontext *spcont= thd->spcont;
-
-      thd->spcont= NULL;           /* Avoid handlers */
-      my_error(ER_OUT_OF_RESOURCES, MYF(0));
-      spcont->clear_handler();
-      thd->spcont= spcont;
+      my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
     }
   }
   else

=== modified file 'sql/sp_pcontext.h'
--- a/sql/sp_pcontext.h	2010-03-31 14:05:33 +0000
+++ b/sql/sp_pcontext.h	2010-07-21 14:14:46 +0000
@@ -332,13 +332,6 @@ public:
   int
   push_cond(LEX_STRING *name, sp_cond_type_t *val);
 
-  inline void
-  pop_cond(uint num)
-  {
-    while (num--)
-      pop_dynamic(&m_conds);
-  }
-
   sp_cond_type_t *
   find_cond(LEX_STRING *name, my_bool scoped=0);
 

=== modified file 'sql/sp_rcontext.cc'
--- a/sql/sp_rcontext.cc	2010-07-08 21:20:08 +0000
+++ b/sql/sp_rcontext.cc	2010-07-21 14:14:46 +0000
@@ -203,15 +203,8 @@ sp_rcontext::find_handler(THD *thd,
                           uint sql_errno,
                           const char* sqlstate,
                           MYSQL_ERROR::enum_warning_level level,
-                          const char* msg,
-                          MYSQL_ERROR ** cond_hdl)
+                          const char* msg)
 {
-  if (m_hfound >= 0)
-  {
-    *cond_hdl= NULL;
-    return TRUE;			// Already got one
-  }
-
   int i= m_hcount, found= -1;
 
   /*
@@ -266,7 +259,18 @@ sp_rcontext::find_handler(THD *thd,
       break;
     }
   }
-  if (found < 0)
+  if (found >= 0)
+  {
+    DBUG_ASSERT((uint) found < m_root_parsing_ctx->max_handler_index());
+
+    m_raised_conditions[found].clear();
+    m_raised_conditions[found].set(sql_errno, sqlstate, level, msg);
+
+    m_hfound= found;
+
+    return TRUE;
+  }
+  else
   {
     /*
       Only "exception conditions" are propagated to handlers in calling
@@ -275,70 +279,13 @@ sp_rcontext::find_handler(THD *thd,
     */
     if (m_prev_runtime_ctx && IS_EXCEPTION_CONDITION(sqlstate) &&
         level == MYSQL_ERROR::WARN_LEVEL_ERROR)
-      return m_prev_runtime_ctx->find_handler(thd,
-                                              sql_errno,
-                                              sqlstate,
-                                              level,
-                                              msg,
-                                              cond_hdl);
-    *cond_hdl= NULL;
-    return FALSE;
-  }
-
-  m_hfound= found;
-
-  MYSQL_ERROR *raised= NULL;
-  DBUG_ASSERT(m_hfound >= 0);
-  DBUG_ASSERT((uint) m_hfound < m_root_parsing_ctx->max_handler_index());
-  raised= & m_raised_conditions[m_hfound];
-  raised->clear();
-  raised->set(sql_errno, sqlstate, level, msg);
-
-  *cond_hdl= raised;
-  return TRUE;
-}
-
-/*
-   Handle the error for a given errno.
-   The severity of the error is adjusted depending of the current sql_mode.
-   If an handler is present for the error (see find_handler()),
-   this function will return true.
-   If a handler is found and if the severity of the error indicate
-   that the current instruction executed should abort,
-   the flag thd->net.report_error is also set.
-   This will cause the execution of the current instruction in a
-   sp_instr* to fail, and give control to the handler code itself
-   in the sp_head::execute() loop.
-
-  SYNOPSIS
-    sql_errno     The error code
-    level         Warning level
-    thd           The current thread
-
-  RETURN
-    TRUE       if a handler was found.
-    FALSE      if no handler was found.
-*/
-bool
-sp_rcontext::handle_condition(THD *thd,
-                              uint sql_errno,
-                              const char* sqlstate,
-                              MYSQL_ERROR::enum_warning_level level,
-                              const char* msg,
-                              MYSQL_ERROR ** cond_hdl)
-{
-  MYSQL_ERROR::enum_warning_level elevated_level= level;
-
+    {
+      return m_prev_runtime_ctx->find_handler(thd, sql_errno, sqlstate,
+                                              level, msg);
+    }
 
-  /* Depending on the sql_mode of execution,
-     warnings may be considered errors */
-  if ((level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
-      thd->really_abort_on_warning())
-  {
-    elevated_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
+    return FALSE;
   }
-
-  return find_handler(thd, sql_errno, sqlstate, elevated_level, msg, cond_hdl);
 }
 
 void

=== modified file 'sql/sp_rcontext.h'
--- a/sql/sp_rcontext.h	2010-03-31 14:05:33 +0000
+++ b/sql/sp_rcontext.h	2010-07-21 14:14:46 +0000
@@ -135,44 +135,45 @@ class sp_rcontext : public Sql_alloc
 
   void pop_handlers(uint count);
 
-  // Returns 1 if a handler was found, 0 otherwise.
+  // Returns TRUE if a handler was found, FALSE otherwise.
+  //
+  // XXX:
+  // For warnings: find handler in current runtime context only. If a
+  // handler is found, it's remembered in m_hfound. It will be executed
+  // afterwards.
+  //
+  // For errors: find handler in the current or in any outer runtime
+  // contexts. If a handler is found, it's remembered in m_hfound of the
+  // corresponding runtime context. The handler will be executed in its
+  // runtime context when the context will be active again (the execution
+  // will unwind to that context).
+  //
+  // For warnings it could be possible to invoke handler right after it was
+  // found (because warning handlers are located in this runtime context).
+  // The reason finding and invocation of handlers are separated is error
+  // handlers -- they can reside in another (outer) runtime context. Thus,
+  // such handlers can not be invoked/activated until the execution flow
+  // reaches that outer runtime context again.
+
   bool
   find_handler(THD *thd,
                uint sql_errno,
                const char* sqlstate,
                MYSQL_ERROR::enum_warning_level level,
-               const char* msg,
-               MYSQL_ERROR ** cond_hdl);
+               const char* msg);
 
-  // If there is an error handler for this error, handle it and return TRUE.
-  bool
-  handle_condition(THD *thd,
-                   uint sql_errno,
-                   const char* sqlstate,
-                   MYSQL_ERROR::enum_warning_level level,
-                   const char* msg,
-                   MYSQL_ERROR ** cond_hdl);
-
-  // Returns handler type and sets *ip to location if one was found
-  inline int
-  found_handler(uint *ip, uint *index)
-  {
-    if (m_hfound < 0)
-      return SP_HANDLER_NONE;
-    *ip= m_handler[m_hfound].handler;
-    *index= m_hfound;
-    return m_handler[m_hfound].type;
+  inline int get_found_handler_index() const
+  {
+    return m_hfound;
   }
 
-  MYSQL_ERROR* raised_condition() const;
-
-  // Returns true if we found a handler in this context
-  inline bool
-  found_handler_here()
+  inline const sp_handler_t *get_handler(uint handler_index) const
   {
-    return (m_hfound >= 0);
+    return &m_handler[handler_index];
   }
 
+  MYSQL_ERROR* raised_condition() const;
+
   // Clears the handler find state
   inline void
   clear_handler()

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2010-07-15 13:47:50 +0000
+++ b/sql/sql_class.cc	2010-07-21 14:14:46 +0000
@@ -848,20 +848,6 @@ MYSQL_ERROR* THD::raise_condition(uint s
     }
   }
 
-  /*
-    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, sql_errno, sqlstate, level, msg, &cond))
-  {
-    /*
-      Do not push any warnings, a handled error must be completely
-      silenced.
-    */
-    DBUG_RETURN(cond);
-  }
-
   /* Un-handled conditions */
 
   cond= raise_condition_no_handler(sql_errno, sqlstate, level, msg);
@@ -1741,9 +1727,9 @@ bool select_send::send_result_set_metada
   return res;
 }
 
-void select_send::abort()
+void select_send::abort_result_set()
 {
-  DBUG_ENTER("select_send::abort");
+  DBUG_ENTER("select_send::abort_result_set");
 
   if (is_result_set_started && thd->spcont)
   {

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2010-07-15 13:47:50 +0000
+++ b/sql/sql_class.h	2010-07-21 14:14:46 +0000
@@ -2991,7 +2991,7 @@ public:
     @retval TRUE      error, an error message is set
   */
   virtual bool check_simple_select() const;
-  virtual void abort() {}
+  virtual void abort_result_set() {}
   /*
     Cleanup instance of this class for next execution of a prepared
     statement/stored procedure.
@@ -3034,7 +3034,7 @@ public:
   bool send_data(List<Item> &items);
   bool send_eof();
   virtual bool check_simple_select() const { return FALSE; }
-  void abort();
+  void abort_result_set();
   virtual void cleanup();
 };
 
@@ -3126,7 +3126,7 @@ class select_insert :public select_resul
   virtual bool can_rollback_data() { return 0; }
   void send_error(uint errcode,const char *err);
   bool send_eof();
-  void abort();
+  virtual void abort_result_set();
   /* not implemented: select_insert is never re-used in prepared statements */
   void cleanup();
 };
@@ -3162,7 +3162,7 @@ public:
   void store_values(List<Item> &values);
   void send_error(uint errcode,const char *err);
   bool send_eof();
-  void abort();
+  virtual void abort_result_set();
   virtual bool can_rollback_data() { return 1; }
 
   // Needed for access from local class MY_HOOKS in prepare(), since thd is proteted.
@@ -3496,7 +3496,7 @@ public:
   {
     return deleted;
   }
-  virtual void abort();
+  virtual void abort_result_set();
 };
 
 
@@ -3547,7 +3547,7 @@ public:
   {
     return updated;
   }
-  virtual void abort();
+  virtual void abort_result_set();
 };
 
 class my_var : public Sql_alloc  {

=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc	2010-07-19 09:21:24 +0000
+++ b/sql/sql_delete.cc	2010-07-21 14:14:46 +0000
@@ -770,9 +770,9 @@ void multi_delete::send_error(uint errco
 }
 
 
-void multi_delete::abort()
+void multi_delete::abort_result_set()
 {
-  DBUG_ENTER("multi_delete::abort");
+  DBUG_ENTER("multi_delete::abort_result_set");
 
   /* the error was handled or nothing deleted and no side effects return */
   if (error_handled ||

=== modified file 'sql/sql_error.cc'
--- a/sql/sql_error.cc	2010-03-31 14:05:33 +0000
+++ b/sql/sql_error.cc	2010-07-21 14:14:46 +0000
@@ -570,6 +570,12 @@ MYSQL_ERROR *Warning_info::push_warning(
   return cond;
 }
 
+void Warning_info::remove_warning(THD *thd, const MYSQL_ERROR *warning)
+{
+  m_warn_count[warning->get_level()]--;
+  m_statement_warn_count--;
+}
+
 /*
   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-03-31 14:05:33 +0000
+++ b/sql/sql_error.h	2010-07-21 14:14:46 +0000
@@ -489,6 +489,8 @@ public:
                             MYSQL_ERROR::enum_warning_level level,
                             const char* msg);
 
+  void remove_warning(THD *thd, const MYSQL_ERROR *warning);
+
   /**
     Set the read only status for this statement area.
     This is a privileged operation, reserved for the implementation of

=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc	2010-07-08 21:20:08 +0000
+++ b/sql/sql_insert.cc	2010-07-21 14:14:46 +0000
@@ -3413,9 +3413,9 @@ bool select_insert::send_eof()
   DBUG_RETURN(0);
 }
 
-void select_insert::abort() {
+void select_insert::abort_result_set() {
 
-  DBUG_ENTER("select_insert::abort");
+  DBUG_ENTER("select_insert::abort_result_set");
   /*
     If the creation of the table failed (due to a syntax error, for
     example), no table will have been opened and therefore 'table'
@@ -3952,9 +3952,9 @@ bool select_create::send_eof()
 }
 
 
-void select_create::abort()
+void select_create::abort_result_set()
 {
-  DBUG_ENTER("select_create::abort");
+  DBUG_ENTER("select_create::abort_result_set");
 
   /*
     In select_insert::abort() we roll back the statement, including
@@ -3972,7 +3972,7 @@ void select_create::abort()
     log state.
   */
   tmp_disable_binlog(thd);
-  select_insert::abort();
+  select_insert::abort_result_set();
   thd->transaction.stmt.modified_non_trans_table= FALSE;
   reenable_binlog(thd);
   /* possible error of writing binary log is ignored deliberately */

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2010-07-15 13:47:50 +0000
+++ b/sql/sql_parse.cc	2010-07-21 14:14:46 +0000
@@ -3401,7 +3401,7 @@ end_with_restore_list:
       res|= thd->is_error();
       MYSQL_MULTI_DELETE_DONE(res, del_result->num_deleted());
       if (res)
-        del_result->abort();
+        del_result->abort_result_set();
       delete del_result;
     }
     else
@@ -4858,7 +4858,7 @@ static bool execute_sqlcom_select(THD *t
                      ER_YES, str.ptr());
       }
       if (res)
-        result->abort();
+        result->abort_result_set();
       else
         result->send_eof();
       delete result;

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2010-07-19 09:21:24 +0000
+++ b/sql/sql_select.cc	2010-07-21 14:14:46 +0000
@@ -300,7 +300,7 @@ bool handle_select(THD *thd, LEX *lex, s
 		     thd->is_error()));
   res|= thd->is_error();
   if (unlikely(res))
-    result->abort();
+    result->abort_result_set();
 
   MYSQL_SELECT_DONE((int) res, (ulong) thd->limit_found_rows);
   DBUG_RETURN(res);

=== modified file 'sql/sql_update.cc'
--- a/sql/sql_update.cc	2010-07-19 09:21:24 +0000
+++ b/sql/sql_update.cc	2010-07-21 14:14:46 +0000
@@ -1261,7 +1261,7 @@ bool mysql_multi_update(THD *thd,
   {
     /* If we had a another error reported earlier then this will be ignored */
     (*result)->send_error(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR));
-    (*result)->abort();
+    (*result)->abort_result_set();
   }
   thd->abort_on_warning= 0;
   DBUG_RETURN(res);
@@ -1861,7 +1861,7 @@ void multi_update::send_error(uint errco
 }
 
 
-void multi_update::abort()
+void multi_update::abort_result_set()
 {
   /* the error was handled or nothing deleted and no side effects return */
   if (error_handled ||


Attachment: [text/bzr-bundle] bzr/alik@sun.com-20100721141446-rvvvwuudafxhnd03.bundle
Thread
bzr commit into mysql-trunk-bugfixing branch (alik:3133) Bug#23032Alexander Nozdrin21 Jul