List:Internals« Previous MessageNext Message »
From:dlenev Date:July 4 2005 10:15pm
Subject:bk commit into 5.0 tree (dlenev:1.1946) BUG#8406
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of dlenev. When dlenev does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.1946 05/07/05 00:15:13 dlenev@stripped +16 -0
  Enable support of access to tables from triggers. Thus fix bug #8406 "Triggers
  crash if referencing a table" and several other related bugs.
  
  Also couple of nice cleanups:
  - Replaced two separate hashes for stored routines used by statement with one.
  - Now instead of doing one pass through all routines used in statement for
    caching them and then doing another pass for adding their tables to table
    list, we do only one pass during which do both things.

  sql/sql_yacc.yy
    1.398 05/07/05 00:08:48 dlenev@stripped +4 -10
    - Now we use sp_add_used_routine() instead of sp_add_to_hash() for adding
      elements to the set of stored routines used in statement.
    - Enabled support of subqueries as right sides in assignments to triggers' row
      accessors.

  sql/sql_trigger.h
    1.9 05/07/05 00:08:48 dlenev@stripped +10 -0
    Table_triggers_list:
      Added sroutines_key member to store key representing triggers of this
      table in the set (hash) of routines used by this statement.
      Declared sp_cache_routines_and_add_tables_for_triggers() as friend since
      it needs access to sroutines_key and trigger bodies.

  sql/sql_trigger.cc
    1.19 05/07/05 00:08:48 dlenev@stripped +12 -0
    Table_triggers_list::check_n_load()
      Added initialization of sroutines_key which stores key representing
      triggers of this table in the set (hash) of routines used by this statement.

  sql/sql_parse.cc
    1.453 05/07/05 00:08:48 dlenev@stripped +1 -2
    mysql_execute_command():
      Replaced LEX::spfuns/spprocs with with one LEX::sroutines hash.

  sql/sql_lex.h
    1.184 05/07/05 00:08:48 dlenev@stripped +11 -7
    LEX:
      Replaced two separate hashes for stored routines used by statement with one.
      Added list linking all elements in this hash to be able to iterate through all
      elements and add new elements to this hash during at the same time.

  sql/sql_lex.cc
    1.149 05/07/05 00:08:48 dlenev@stripped +3 -4
    lex_start():
      Replaced LEX::spfuns/spprocs with with one LEX::sroutines hash.
      Added LEX::sroutines_list list linking all elements in this hash.

  sql/sql_base.cc
    1.255 05/07/05 00:08:48 dlenev@stripped +9 -16
    open_tables():
    - LEX::spfuns/spprocs hashes were replaced with one LEX::sroutines hash.
    - Now instead of doing one pass through all routines used in statement for
      caching them and then doing another pass for adding their tables to table
      list, we do only one pass during which do both things. It is easy to do
      since all routines in the set of routines used by statement are linked in
      the list. This also allows to calculate table list for prelocking more
      precisely.
    - Now triggers properly inform prelocking algorithm about tables they use.

  sql/sp_head.h
    1.56 05/07/05 00:08:48 dlenev@stripped +42 -14
    sp_name:
      Now this class also holds key identifying routine in the set (hash) of stored
      routines used by statement. 
    sp_head:
      Instead of two separate hashes sp_funs/m_spprocs representing sets of stored
      routines used by this routine we use one hash - m_sroutines. 
    sp_instr_set_trigger_field:
      Added support for subqueries in assignments to row accessors in triggers.
    Removed declaration of sp_add_sp_tables_to_table_list() since now we don't have
    separate stage on which we add tables used by routines used by statement to
    table list for prelocking. We do it on the same stage as we load those routines
    in SP cache.

  sql/sp_head.cc
    1.145 05/07/05 00:08:48 dlenev@stripped +24 -89
    sp_name::init_qname():
      Now sp_name also holds key identifying routine in the set (hash) of
      stored routines used by statement. 
    sp_head:
      Instead of two separate hashes sp_funs/m_spprocs representing sets of stored
      routines used by this routine we use one hash - m_sroutines. 
    sp_instr_set_trigger_field:
      Added support for subqueries in assignments to row accessors in triggers.
    Removed definition of sp_add_sp_tables_to_table_list() and auxilary functions 
    since now we don't have separate stage on which we add tables used by routines
    used by statement to table list for prelocking. We do it on the same stage as
    we load those routines in SP cache. So all this functionality moved to
    sp_cache_routines_and_add_tables() family of functions.

  sql/sp.h
    1.22 05/07/05 00:08:48 dlenev@stripped +10 -9
    Added declarations of functions used for manipulations with set (hash) of stored
    routines used by statement.

  sql/sp.cc
    1.78 05/07/05 00:08:48 dlenev@stripped +235 -88
    - Added SROUTINE structure that represents element in the set of stored routines
      used by statement or routine. We can't as before use LEX_STRING for this
      purprose because we want link all elements of this set in list.
    - Replaced sp_add_to_hash() with sp_add_used_routine() which takes into account
      that now we use one hash for stored routines used by statement instead of two
      and which mantains list linking all elelemnts in this hash.
    - Renamed sp_merge_hash() to sp_update_sp_used_routines().
    - Introduced sp_update_stmt_used_routines() for adding elements to the set of
      routines used by statement from another similar set for statement or routine.
      This function will also mantain list linking elements of destination set.
    - Now instead of one sp_cache_routines() function we have family of 
      sp_cache_routines_and_add_tables() functions which are also responsible for
      adding tables used by routines being cached to statement table list. Nice
      optimization - thanks to list linking all elements in the hash of routines
      used by statement we don't need to perform several iterations over this hash
      (as it was before in cases when we have added new elements to it).

  sql/mysql_priv.h
    1.314 05/07/05 00:08:48 dlenev@stripped +8 -0
    Added sp_sroutine_key() declaration. We can't put it in sp.h since we want it
    to be accessible from sql_lex.h.

  mysql-test/t/trigger.test
    1.12 05/07/05 00:08:48 dlenev@stripped +82 -1
    Added several tests for triggers using tables.

  mysql-test/t/sp.test
    1.124 05/07/05 00:08:47 dlenev@stripped +3 -5
    Updated comment about recursive views to reflect current situation.
    Updated test for LOCK TABLES with views in table list.
    (Old version of statement used in this test will work ok now, since prelocking
     algorithm was tuned and will lock only one multi-set of tables for each routine
     even if this routine is used in several different views).

  mysql-test/r/trigger.result
    1.8 05/07/05 00:08:47 dlenev@stripped +92 -1
    Added several tests for triggers using tables.

  mysql-test/r/sp.result
    1.129 05/07/05 00:08:47 dlenev@stripped +1 -1
    Updated test for LOCK TABLES with views in table list.
    (Old version of statement used in this test will work ok now, since prelocking
     algorithm was tuned and will lock only one multi-set of tables for each routine
     even if this routine is used in several different views).

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	dlenev
# Host:	brandersnatch.localdomain
# Root:	/home/dlenev/src/mysql-5.0-bg8406

--- 1.313/sql/mysql_priv.h	2005-06-09 22:01:53 +04:00
+++ 1.314/sql/mysql_priv.h	2005-07-05 00:08:48 +04:00
@@ -510,6 +510,14 @@
 int end_trans(THD *thd, enum enum_mysql_completiontype completion);
 
 Item *negate_expression(THD *thd, Item *expr);
+
+/*
+  We can't put following declaration in sp.h since we want it to be
+  accessible from sql_lex.h.
+*/
+byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first);
+
+
 #include "sql_class.h"
 #include "sql_acl.h"
 #include "tztime.h"

--- 1.254/sql/sql_base.cc	2005-06-09 01:07:46 +04:00
+++ 1.255/sql/sql_base.cc	2005-07-05 00:08:48 +04:00
@@ -1792,16 +1792,13 @@
     may be still zero for prelocked statement...
   */
   if (!thd->prelocked_mode && !thd->lex->requires_prelocking()
&&
-      (thd->lex->spfuns.records || thd->lex->spprocs.records))
+      thd->lex->sroutines.records)
   {
-    TABLE_LIST **save_query_tables_last;
-
-    sp_cache_routines(thd, thd->lex);
-    save_query_tables_last= thd->lex->query_tables_last;
+    TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
 
     DBUG_ASSERT(thd->lex->query_tables == *start);
 
-    if (sp_add_sp_tables_to_table_list(thd, thd->lex, thd->lex) ||
+    if (sp_cache_routines_and_add_tables(thd, thd->lex) ||
         *start)
     {
       query_tables_last_own= save_query_tables_last;
@@ -1837,19 +1834,16 @@
           and add tables used by them to table list.
         */
         if (!thd->prelocked_mode && !thd->lex->requires_prelocking()
&&
-            (tables->view->spfuns.records || tables->view->spprocs.records))
+            tables->view->sroutines.records)
         {
-          // FIXME We should catch recursion for both views and funcs here
-          sp_cache_routines(thd, tables->view);
-
           /* We have at least one table in TL here */
           if (!query_tables_last_own)
             query_tables_last_own= thd->lex->query_tables_last;
-          sp_add_sp_tables_to_table_list(thd, thd->lex, tables->view);
+          sp_cache_routines_and_add_tables_for_view(thd, thd->lex,
+                                                    tables->view);
         }
         /* Cleanup hashes because destructo for this LEX is never called */
-        hash_free(&tables->view->spfuns);
-        hash_free(&tables->view->spprocs);
+        hash_free(&tables->view->sroutines);
 	continue;
       }
 
@@ -1904,9 +1898,6 @@
         prelocking list.
         If we lock table for reading we won't update it so there is no need to
         process its triggers since they never will be activated.
-
-        FIXME Now we are simply turning on prelocking. Proper integration
-        and testing is to be done later.
       */
       if (!thd->prelocked_mode && !thd->lex->requires_prelocking()
&&
           tables->table->triggers &&
@@ -1914,6 +1905,8 @@
       {
         if (!query_tables_last_own)
             query_tables_last_own= thd->lex->query_tables_last;
+        sp_cache_routines_and_add_tables_for_triggers(thd, thd->lex,
+                                                      tables->table->triggers);
       }
       free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
     }

--- 1.148/sql/sql_lex.cc	2005-06-08 00:34:45 +04:00
+++ 1.149/sql/sql_lex.cc	2005-07-05 00:08:48 +04:00
@@ -172,10 +172,9 @@
   lex->proc_list.first= 0;
   lex->query_tables_own_last= 0;
 
-  if (lex->spfuns.records)
-    my_hash_reset(&lex->spfuns);
-  if (lex->spprocs.records)
-    my_hash_reset(&lex->spprocs);
+  if (lex->sroutines.records)
+    my_hash_reset(&lex->sroutines);
+  lex->sroutines_list.empty();
   DBUG_VOID_RETURN;
 }
 

--- 1.183/sql/sql_lex.h	2005-06-09 01:11:22 +04:00
+++ 1.184/sql/sql_lex.h	2005-07-05 00:08:48 +04:00
@@ -797,8 +797,14 @@
   bool sp_lex_in_use;	/* Keep track on lex usage in SPs for error handling */
   bool all_privileges;
   sp_pcontext *spcont;
-  HASH spfuns;		/* Called functions */
-  HASH spprocs;		/* Called procedures */
+  /* Set of stored routines called by statement. */
+  HASH sroutines;
+  /*
+    List linking elements of 'sroutines' set. Allows to iterate through all
+    elements in the set adding elements to this set during this process.
+  */
+  SQL_LIST sroutines_list;
+
   st_sp_chistics sp_chistics;
   bool only_view;       /* used for SHOW CREATE TABLE/VIEW */
   /*
@@ -833,15 +839,13 @@
 
   st_lex() :result(0), sql_command(SQLCOM_END), query_tables_own_last(0)
   {
-    extern byte *sp_lex_sp_key(const byte *ptr, uint *plen, my_bool first);
-    hash_init(&spfuns, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
-    hash_init(&spprocs, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
+    hash_init(&sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0);
+    sroutines_list.empty();
   }
 
   virtual ~st_lex()
   {
-    hash_free(&spfuns);
-    hash_free(&spprocs);
+    hash_free(&sroutines);
   }
 
   inline void uncacheable(uint8 cause)

--- 1.452/sql/sql_parse.cc	2005-06-13 13:13:00 +04:00
+++ 1.453/sql/sql_parse.cc	2005-07-05 00:08:48 +04:00
@@ -2296,8 +2296,7 @@
     Don't reset warnings when executing a stored routine.
   */
   if ((all_tables || &lex->select_lex != lex->all_selects_list ||
-       lex->spfuns.records || lex->spprocs.records) &&
-      !thd->spcont)
+       lex->sroutines.records) && !thd->spcont)
     mysql_reset_errors(thd, 0);
 
 #ifdef HAVE_REPLICATION

--- 1.397/sql/sql_yacc.yy	2005-06-10 20:01:55 +04:00
+++ 1.398/sql/sql_yacc.yy	2005-07-05 00:08:48 +04:00
@@ -1532,7 +1532,7 @@
 	    lex->sql_command= SQLCOM_CALL;
 	    lex->spname= $2;
 	    lex->value_list.empty();
-	    sp_add_to_hash(&lex->spprocs, $2);
+	    sp_add_used_routine(lex, $2, TYPE_ENUM_PROCEDURE);
 	  }
           '(' sp_cparam_list ')' {}
 	;
@@ -4682,7 +4682,7 @@
 	    sp_name *name= new sp_name($1, $3);
 
 	    name->init_qname(YYTHD);
-	    sp_add_to_hash(&lex->spfuns, name);
+	    sp_add_used_routine(lex, name, TYPE_ENUM_FUNCTION);
 	    if ($5)
 	      $$= new Item_func_sp(name, *$5);
 	    else
@@ -4771,7 +4771,7 @@
 	      LEX *lex= Lex;
               sp_name *name= sp_name_current_db_new(YYTHD, $1);
 
-              sp_add_to_hash(&lex->spfuns, name);
+              sp_add_used_routine(lex, name, TYPE_ENUM_FUNCTION);
               if ($3)
                 $$= new Item_func_sp(name, *$3);
               else
@@ -7684,12 +7684,6 @@
               yyerror(ER(ER_SYNTAX_ERROR));
               YYABORT;
             }
-            if (lex->query_tables)
-            {
-              my_message(ER_SP_SUBSELECT_NYI, ER(ER_SP_SUBSELECT_NYI),
-              MYF(0));
-              YYABORT;
-            }
             if ($4)
               it= $4;
             else
@@ -7702,7 +7696,7 @@
                                                   $2.base_name.str)) ||
                 !(i= new sp_instr_set_trigger_field(
                            lex->sphead->instructions(), lex->spcont,
-                           trg_fld, it)))
+                           trg_fld, it, lex)))
               YYABORT;
 
             /*

--- 1.7/mysql-test/r/trigger.result	2005-05-30 18:55:52 +04:00
+++ 1.8/mysql-test/r/trigger.result	2005-07-05 00:08:47 +04:00
@@ -1,6 +1,7 @@
-drop table if exists t1, t2;
+drop table if exists t1, t2, t3;
 drop view if exists v1;
 drop database if exists mysqltest;
+drop function if exists f1;
 create table t1 (i int);
 create trigger trg before insert on t1 for each row set @a:=1;
 set @a:=0;
@@ -182,6 +183,96 @@
 @log
 (BEFORE_INSERT: new=(id=1, data=5))(BEFORE_UPDATE: old=(id=1, data=4) new=(id=1,
data=6))(AFTER_UPDATE: old=(id=1, data=4) new=(id=1, data=6))(BEFORE_INSERT: new=(id=3,
data=3))(AFTER_INSERT: new=(id=3, data=3))
 drop table t1;
+create table t1 (id int primary key, data varchar(10), fk int);
+create table t2 (event varchar(100));
+create table t3 (id int primary key);
+create trigger t1_ai after insert on t1 for each row 
+insert into t2 values (concat("INSERT INTO t1 id=", new.id, " data='", new.data, "'"));
+insert into t1 (id, data) values (1, "one"), (2, "two");
+select * from t1;
+id	data	fk
+1	one	NULL
+2	two	NULL
+select * from t2;
+event
+INSERT INTO t1 id=1 data='one'
+INSERT INTO t1 id=2 data='two'
+drop trigger t1.t1_ai;
+create trigger t1_bi before insert on t1 for each row
+begin
+if exists (select id from t3 where id=new.fk) then
+insert into t2 values (concat("INSERT INTO t1 id=", new.id, " data='", new.data, "' fk=",
new.fk));
+else
+insert into t2 values (concat("INSERT INTO t1 FAILED id=", new.id, " data='", new.data,
"' fk=", new.fk));
+set new.id= NULL;
+end if;
+end|
+insert into t3 values (1);
+insert into t1 values (4, "four", 1), (5, "five", 2);
+ERROR 23000: Column 'id' cannot be null
+select * from t1;
+id	data	fk
+1	one	NULL
+2	two	NULL
+4	four	1
+select * from t2;
+event
+INSERT INTO t1 id=1 data='one'
+INSERT INTO t1 id=2 data='two'
+INSERT INTO t1 id=4 data='four' fk=1
+INSERT INTO t1 FAILED id=5 data='five' fk=2
+drop table t1, t2, t3;
+create table t1 (id int primary key, data varchar(10));
+create table t2 (seq int);
+insert into t2 values (10);
+create function f1 () returns int return (select max(seq) from t2);
+create trigger t1_bi before insert on t1 for each row
+begin
+if new.id > f1() then
+set new.id:= f1();
+end if;
+end|
+select f1();
+f1()
+10
+insert into t1 values (1, "first");
+insert into t1 values (f1(), "max");
+select * from t1;
+id	data
+1	first
+10	max
+drop table t1, t2;
+drop function f1;
+create table t1 (id int primary key, fk_t2 int);
+create table t2 (id int primary key, fk_t3 int);
+create table t3 (id int primary key);
+insert into t1 values (1,1), (2,1), (3,2);
+insert into t2 values (1,1), (2,2);
+insert into t3 values (1), (2);
+create trigger t3_ad after delete on t3 for each row
+delete from t2 where fk_t3=old.id;
+create trigger t2_ad after delete on t2 for each row
+delete from t1 where fk_t2=old.id;
+delete from t3 where id = 1;
+select * from t1 left join (t2 left join t3 on t2.fk_t3 = t3.id) on t1.fk_t2 = t2.id;
+id	fk_t2	id	fk_t3	id
+3	2	2	2	2
+drop table t1, t2, t3;
+create table t1 (id int primary key, copy int);
+create table t2 (id int primary key, data int);
+insert into t2 values (1,1), (2,2);
+create trigger t1_bi before insert on t1 for each row
+set new.copy= (select data from t2 where id = new.id);
+create trigger t1_bu before update on t1 for each row
+set new.copy= (select data from t2 where id = new.id);
+insert into t1 values (1,3), (2,4), (3,3);
+update t1 set copy= 1 where id = 2;
+select * from t1;
+id	copy
+1	1
+2	2
+3	NULL
+drop table t1, t2;
 create table t1 (i int);
 create trigger trg before insert on t1 for each row set @a:= old.i;
 ERROR HY000: There is no OLD row in on INSERT trigger

--- 1.11/mysql-test/t/trigger.test	2005-05-30 18:55:52 +04:00
+++ 1.12/mysql-test/t/trigger.test	2005-07-05 00:08:48 +04:00
@@ -3,9 +3,10 @@
 #
 
 --disable_warnings
-drop table if exists t1, t2;
+drop table if exists t1, t2, t3;
 drop view if exists v1;
 drop database if exists mysqltest;
+drop function if exists f1;
 --enable_warnings
 
 create table t1 (i int);
@@ -198,6 +199,86 @@
 # This also drops associated triggers
 drop table t1;
 
+
+#
+# Let us test triggers which access to other tables.
+#
+# Trivial trigger which inserts data into another table
+create table t1 (id int primary key, data varchar(10), fk int);
+create table t2 (event varchar(100));
+create table t3 (id int primary key);
+create trigger t1_ai after insert on t1 for each row 
+  insert into t2 values (concat("INSERT INTO t1 id=", new.id, " data='", new.data, "'"));
+insert into t1 (id, data) values (1, "one"), (2, "two");
+select * from t1;
+select * from t2;
+drop trigger t1.t1_ai;
+# Trigger which uses couple of tables (and partially emulates FK constraint)
+delimiter |;
+create trigger t1_bi before insert on t1 for each row
+begin
+  if exists (select id from t3 where id=new.fk) then
+    insert into t2 values (concat("INSERT INTO t1 id=", new.id, " data='", new.data, "'
fk=", new.fk));
+  else
+    insert into t2 values (concat("INSERT INTO t1 FAILED id=", new.id, " data='",
new.data, "' fk=", new.fk));
+    set new.id= NULL;
+  end if;
+end|
+delimiter ;|
+insert into t3 values (1);
+--error 1048
+insert into t1 values (4, "four", 1), (5, "five", 2);
+select * from t1;
+select * from t2;
+drop table t1, t2, t3;
+# Trigger which invokes function
+create table t1 (id int primary key, data varchar(10));
+create table t2 (seq int);
+insert into t2 values (10);
+create function f1 () returns int return (select max(seq) from t2);
+delimiter |;
+create trigger t1_bi before insert on t1 for each row
+begin
+  if new.id > f1() then
+    set new.id:= f1();
+  end if;
+end|
+delimiter ;|
+# Remove this once bug #11554 will be fixed.
+select f1();
+insert into t1 values (1, "first");
+insert into t1 values (f1(), "max");
+select * from t1;
+drop table t1, t2;
+drop function f1;
+# Trigger which forces invocation of another trigger
+# (emulation of FK on delete cascade policy)
+create table t1 (id int primary key, fk_t2 int);
+create table t2 (id int primary key, fk_t3 int);
+create table t3 (id int primary key);
+insert into t1 values (1,1), (2,1), (3,2);
+insert into t2 values (1,1), (2,2);
+insert into t3 values (1), (2);
+create trigger t3_ad after delete on t3 for each row
+  delete from t2 where fk_t3=old.id;
+create trigger t2_ad after delete on t2 for each row
+  delete from t1 where fk_t2=old.id;
+delete from t3 where id = 1;
+select * from t1 left join (t2 left join t3 on t2.fk_t3 = t3.id) on t1.fk_t2 = t2.id;
+drop table t1, t2, t3;
+# Trigger which assigns value selected from table to field of row
+# being inserted/updated.
+create table t1 (id int primary key, copy int);
+create table t2 (id int primary key, data int);
+insert into t2 values (1,1), (2,2);
+create trigger t1_bi before insert on t1 for each row
+  set new.copy= (select data from t2 where id = new.id);
+create trigger t1_bu before update on t1 for each row
+  set new.copy= (select data from t2 where id = new.id);
+insert into t1 values (1,3), (2,4), (3,3);
+update t1 set copy= 1 where id = 2;
+select * from t1;
+drop table t1, t2;
 
 #
 # Test of wrong column specifiers in triggers

--- 1.18/sql/sql_trigger.cc	2005-05-24 22:19:28 +04:00
+++ 1.19/sql/sql_trigger.cc	2005-07-05 00:08:48 +04:00
@@ -418,6 +418,18 @@
       table->triggers= triggers;
 
       /*
+        Construct key that will represent triggers for this table in the set
+        of routines used by statement.
+      */
+      triggers->sroutines_key.length= 1+strlen(db)+1+strlen(table_name)+1;
+      if (!(triggers->sroutines_key.str=
+              alloc_root(&table->mem_root, triggers->sroutines_key.length)))
+        DBUG_RETURN(1);
+      triggers->sroutines_key.str[0]= TYPE_ENUM_TRIGGER;
+      strmov(strmov(strmov(triggers->sroutines_key.str+1, db), "."),
+             table_name);
+
+      /*
         TODO: This could be avoided if there is no triggers
               for UPDATE and DELETE.
       */

--- 1.8/sql/sql_trigger.h	2005-06-07 14:53:03 +04:00
+++ 1.9/sql/sql_trigger.h	2005-07-05 00:08:48 +04:00
@@ -28,6 +28,14 @@
     used in CREATE/DROP TRIGGER for looking up trigger by name.
   */
   List<LEX_STRING>  names_list;
+  /*
+    Key representing triggers for this table in set of all stored
+    routines used by statement.
+    TODO: We won't need this member once triggers namespace will be
+    database-wide instead of table-wide because then we will be able
+    to use key based on sp_name as for other stored routines.
+  */
+  LEX_STRING        sroutines_key;
 
 public:
   /*
@@ -112,6 +120,8 @@
   }
 
   friend class Item_trigger_field;
+  friend void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
+                Table_triggers_list *triggers);
 
 private:
   bool prepare_record1_accessors(TABLE *table);

--- 1.128/mysql-test/r/sp.result	2005-06-10 18:56:25 +04:00
+++ 1.129/mysql-test/r/sp.result	2005-07-05 00:08:47 +04:00
@@ -1229,7 +1229,7 @@
 select * from v1|
 a
 3
-select * from v1, v2|
+select * from v1, t1|
 ERROR HY000: Table 't1' was not locked with LOCK TABLES
 select f4()|
 ERROR HY000: Table 't2' was not locked with LOCK TABLES

--- 1.123/mysql-test/t/sp.test	2005-06-10 18:56:25 +04:00
+++ 1.124/mysql-test/t/sp.test	2005-07-05 00:08:47 +04:00
@@ -1414,7 +1414,8 @@
 # views and functions ?
 create function f1() returns int
   return (select sum(data) from t1) + (select sum(data) from v1)|
-# FIXME All these just exceed file limit for me :)
+# This queries will crash server because we can't use LEX in
+# reenterable fashion yet. Patch diableing recursion will heal this.
 #select f1()|
 #select * from v1|
 #select * from v2|
@@ -1459,13 +1460,10 @@
 select * from v1|
 # These should not work as we have too little instances of tables locked
 --error 1100
-select * from v1, v2|
+select * from v1, t1|
 --error 1100
 select f4()|
 unlock tables|
-
-
-# TODO We also should test integration with triggers
 
 
 # Cleanup

--- 1.77/sql/sp.cc	2005-05-31 20:36:27 +04:00
+++ 1.78/sql/sp.cc	2005-07-05 00:08:48 +04:00
@@ -19,6 +19,7 @@
 #include "sp.h"
 #include "sp_head.h"
 #include "sp_cache.h"
+#include "sql_trigger.h"
 
 static bool
 create_string(THD *thd, String *buf,
@@ -1072,144 +1073,290 @@
 }
 
 
-byte *
-sp_lex_sp_key(const byte *ptr, uint *plen, my_bool first)
+/*
+  Structure that represents element in the set of stored routines
+  used by statement or routine.
+*/
+struct st_sroutine;
+
+typedef struct st_sroutine
+{
+  /* Set key consisting of one-byte routine type and quoted routine name. */
+  LEX_STRING key;
+  /*
+    Next element in list linking all routines in set. See also comments
+    for LEX::sroutine/sroutine_list and sp_head::m_sroutines.
+  */
+  st_sroutine *next;
+} SROUTINE;
+
+
+byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first)
 {
-  LEX_STRING *lsp= (LEX_STRING *)ptr;
-  *plen= lsp->length;
-  return (byte *)lsp->str;
+  SROUTINE *rn= (SROUTINE *)ptr;
+  *plen= rn->key.length;
+  return (byte *)rn->key.str;
 }
 
 
-void
-sp_add_to_hash(HASH *h, sp_name *fun)
+/*
+  Auxilary function that adds new element to the set of stored routines
+  used by statement.
+
+  SYNOPSIS
+    add_used_routine()
+      lex     - LEX representing statement
+      key     - key for the hash representing set
+
+  NOTE
+    Will also add element to end of 'LEX::sroutines_list' list.
+
+  RETURN VALUE
+    TRUE  - new element was added.
+    FALSE - element was not added (because it is already present in the set).
+*/
+
+static bool add_used_routine(LEX *lex, const LEX_STRING *key)
 {
-  if (! hash_search(h, (byte *)fun->m_qname.str, fun->m_qname.length))
+  if (!hash_search(&lex->sroutines, (byte *)key->str, key->length))
   {
-    LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING));
-    ls->str= sql_strmake(fun->m_qname.str, fun->m_qname.length);
-    ls->length= fun->m_qname.length;
-
-    my_hash_insert(h, (byte *)ls);
+    SROUTINE *rn= (SROUTINE *)sql_alloc(sizeof(SROUTINE) + key->length);
+    if (!rn)              // OOM. Error will be reported using fatal_error().
+      return FALSE;
+    rn->key.length= key->length;
+    rn->key.str= (char *)rn + sizeof(SROUTINE);
+    memcpy(rn->key.str, key->str, key->length);
+    my_hash_insert(&lex->sroutines, (byte *)rn);
+    lex->sroutines_list.link_in_list((byte *)rn, (byte **)&rn->next);
+    return TRUE;
   }
+  return FALSE;
+}
+
+
+/*
+  Add routine to the set of stored routines used by statement.
+
+  SYNOPSIS
+    sp_add_used_routine()
+      lex     - LEX representing statement
+      rt      - routine name
+      rt_type - routine type (one of TYPE_ENUM_PROCEDURE/...)
+
+  NOTE
+    Will also add element to end of 'LEX::sroutines_list' list.
+*/
+
+void sp_add_used_routine(LEX *lex, sp_name *rt, char rt_type)
+{
+  rt->set_routine_type(rt_type);
+  (void)add_used_routine(lex, &rt->m_sroutines_key);
 }
 
 
 /*
-  Merge contents of two hashes containing LEX_STRING's
+  Merge contents of two hashes representing sets of routines used
+  by statements or by other routines.
 
   SYNOPSIS
-    sp_merge_hash()
+    sp_update_sp_used_routines()
       dst - hash to which elements should be added
       src - hash from which elements merged
 
-  RETURN VALUE
-    TRUE  - if we have added some new elements to destination hash.
-    FALSE - there were no new elements in src.
+  NOTE
+    This procedure won't create new SROUTINE objects, instead it
+    will simply add elements from source to destination hash. Thus
+    time of life of elements in destination hash becomes dependant
+    on time of life of elements from source hash. It also won't
+    touch lists linking elements in source and destination hashes.
 */
 
-bool
-sp_merge_hash(HASH *dst, HASH *src)
+void sp_update_sp_used_routines(HASH *dst, HASH *src)
 {
-  bool res= FALSE;
   for (uint i=0 ; i < src->records ; i++)
   {
-    LEX_STRING *ls= (LEX_STRING *)hash_element(src, i);
+    SROUTINE *rt= (SROUTINE *)hash_element(src, i);
+    if (!hash_search(dst, (byte *)rt->key.str, rt->key.length))
+      my_hash_insert(dst, (byte *)rt);
+  }
+}
 
-    if (! hash_search(dst, (byte *)ls->str, ls->length))
-    {
-      my_hash_insert(dst, (byte *)ls);
-      res= TRUE;
-    }
+
+/*
+  Add contents of hash representing set of routines to the set of
+  routines used by statement.
+
+  SYNOPSIS
+    sp_update_stmt_used_routines()
+      lex - LEX representing statement
+      src - hash representing set from which routines will be added
+
+  NOTE
+    It will also add elements to end of 'LEX::sroutines_list' list.
+*/
+
+static void sp_update_stmt_used_routines(LEX *lex, HASH *src)
+{
+  for (uint i=0 ; i < src->records ; i++)
+  {
+    SROUTINE *rt= (SROUTINE *)hash_element(src, i);
+    (void)add_used_routine(lex, &rt->key);
   }
-  return res;
 }
 
 
 /*
-  Cache all routines implicitly or explicitly used by query
-  (or whatever object is represented by LEX).
+  Cache sub-set of routines used by statement, add tables used by those
+  routines to statement table list. Do the same for all routines used
+  by those routines.
 
   SYNOPSIS
-    sp_cache_routines()
-      thd - thread context
-      lex - LEX representing query
+    sp_cache_routines_and_add_tables_aux()
+      thd   - thread context
+      lex   - LEX representing statement
+      start - first routine from the list of routines to be cached
+              (this list defines mentioned sub-set).
 
   NOTE
     If some function is missing this won't be reported here.
     Instead this fact will be discovered during query execution.
 
-  TODO
-    Currently if after passing through routine hashes we discover
-    that we have added something to them, we do one more pass to
-    process all routines which were missed on previous pass because
-    of these additions. We can avoid this if along with hashes
-    we use lists holding routine names and iterate other these
-    lists instead of hashes (since addition to the end of list
-    does not reorder elements in it).
+  RETURN VALUE
+    TRUE  - some tables were added
+    FALSE - no tables were added.
 */
 
-void
-sp_cache_routines(THD *thd, LEX *lex)
+static bool
+sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, SROUTINE *start)
 {
-  bool routines_added= TRUE;
+  bool result= FALSE;
 
-  DBUG_ENTER("sp_cache_routines");
+  DBUG_ENTER("sp_cache_routines_and_add_tables_aux");
 
-  while (routines_added)
+  for (SROUTINE *rt= start; rt; rt= rt->next)
   {
-    routines_added= FALSE;
+    sp_name name(rt->key.str, rt->key.length);
+    int type= rt->key.str[0];
+    sp_head *sp;
 
-    for (int type= TYPE_ENUM_FUNCTION; type < TYPE_ENUM_TRIGGER; type++)
+    if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ?
+                              &thd->sp_func_cache : &thd->sp_proc_cache),
+                              &name)))
     {
-      HASH *h= (type == TYPE_ENUM_FUNCTION ? &lex->spfuns : &lex->spprocs);
+      LEX *oldlex= thd->lex;
+      LEX *newlex= new st_lex;
+      thd->lex= newlex;
+      /* Pass hint pointer to mysql.proc table */
+      newlex->proc_table= oldlex->proc_table;
+      newlex->current_select= NULL;
+      name.m_name.str= strchr(name.m_qname.str, '.');
+      name.m_db.length= name.m_name.str - name.m_qname.str;
+      name.m_db.str= strmake_root(thd->mem_root, name.m_qname.str,
+                                  name.m_db.length);
+      name.m_name.str+= 1;
+      name.m_name.length= name.m_qname.length - name.m_db.length - 1;
 
-      for (uint i=0 ; i < h->records ; i++)
+      if (db_find_routine(thd, type, &name, &sp) == SP_OK)
       {
-        LEX_STRING *ls= (LEX_STRING *)hash_element(h, i);
-        sp_name name(*ls);
-        sp_head *sp;
-
-        name.m_qname= *ls;
-        if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ?
-                                   &thd->sp_func_cache :
&thd->sp_proc_cache),
-                                  &name)))
-        {
-          LEX *oldlex= thd->lex;
-          LEX *newlex= new st_lex;
+        if (type == TYPE_ENUM_FUNCTION)
+          sp_cache_insert(&thd->sp_func_cache, sp);
+        else
+          sp_cache_insert(&thd->sp_proc_cache, sp);
+      }
+      delete newlex;
+      thd->lex= oldlex;
+    }
+    if (sp)
+    {
+      sp_update_stmt_used_routines(lex, &sp->m_sroutines);
+      result|= sp->add_used_tables_to_table_list(thd, &lex->query_tables_last);
+    }
+  }
+  DBUG_RETURN(result);
+}
+
+
+/*
+  Cache all routines from the set of used by statement, add tables used
+  by those routines to statement table list. Do the same for all routines
+  used by those routines.
+
+  SYNOPSIS
+    sp_cache_routines_and_add_tables()
+      thd   - thread context
+      lex   - LEX representing statement
+
+  RETURN VALUE
+    TRUE  - some tables were added
+    FALSE - no tables were added.
+*/
+
+bool
+sp_cache_routines_and_add_tables(THD *thd, LEX *lex)
+{
+
+  return sp_cache_routines_and_add_tables_aux(thd, lex,
+           (SROUTINE *)lex->sroutines_list.first);
+}
+
+
+/*
+  Add all routines used by view to the set of routines used by statement.
+  Add tables used by those routines to statement table list. Do the same
+  for all routines used by these routines.
+
+  SYNOPSIS
+    sp_cache_routines_and_add_tables_for_view()
+      thd     - thread context
+      lex     - LEX representing statement
+      aux_lex - LEX representing view
+*/
+
+void
+sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, LEX *aux_lex)
+{
+  SROUTINE **last_cached_routine_ptr=
+                 (SROUTINE **)lex->sroutines_list.next;
+  sp_update_stmt_used_routines(lex, &aux_lex->sroutines);
+  (void)sp_cache_routines_and_add_tables_aux(thd, lex,
+                                             *last_cached_routine_ptr);
+}
 
-          thd->lex= newlex;
-          /* Pass hint pointer to mysql.proc table */
-          newlex->proc_table= oldlex->proc_table;
-          newlex->current_select= NULL;
-          name.m_name.str= strchr(name.m_qname.str, '.');
-          name.m_db.length= name.m_name.str - name.m_qname.str;
-          name.m_db.str= strmake_root(thd->mem_root, name.m_qname.str,
-                                      name.m_db.length);
-          name.m_name.str+= 1;
-          name.m_name.length= name.m_qname.length - name.m_db.length - 1;
-
-          if (db_find_routine(thd, type, &name, &sp) == SP_OK)
-          {
-            if (type == TYPE_ENUM_FUNCTION)
-              sp_cache_insert(&thd->sp_func_cache, sp);
-            else
-              sp_cache_insert(&thd->sp_proc_cache, sp);
-          }
-          delete newlex;
-          thd->lex= oldlex;
-        }
 
-        if (sp)
+/*
+  Add triggers for table to the set of routines used by statement.
+  Add tables used by them to statement table list. Do the same for
+  all implictly used routines.
+
+  SYNOPSIS
+    sp_cache_routines_and_add_tables_for_triggers()
+      thd      - thread context
+      lex      - LEX respresenting statement
+      triggers - triggers of the table
+*/
+
+void
+sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
+                                              Table_triggers_list *triggers)
+{
+  if (add_used_routine(lex, &triggers->sroutines_key))
+  {
+    SROUTINE **last_cached_routine_ptr= (SROUTINE **)lex->sroutines_list.next;
+    for (int i= 0; i < 3; i++)
+      for (int j= 0; j < 2; j++)
+        if (triggers->bodies[i][j])
         {
-          routines_added|= sp_merge_hash(&lex->spfuns, &sp->m_spfuns);
-          routines_added|= sp_merge_hash(&lex->spprocs, &sp->m_spprocs);
+          (void)triggers->bodies[i][j]->add_used_tables_to_table_list(thd,
+                                          &lex->query_tables_last);
+          sp_update_stmt_used_routines(lex,
+                                       &triggers->bodies[i][j]->m_sroutines);
         }
-      }
-    }
+
+    (void)sp_cache_routines_and_add_tables_aux(thd, lex,
+                                               *last_cached_routine_ptr);
   }
-  DBUG_VOID_RETURN;
 }
+
 
 /*
  * Generates the CREATE... string from the table information.

--- 1.21/sql/sp.h	2005-05-31 20:36:27 +04:00
+++ 1.22/sql/sp.h	2005-07-05 00:08:48 +04:00
@@ -79,15 +79,16 @@
 
 
 /*
- *  For precaching of functions and procedures
- */
-void
-sp_add_to_hash(HASH *h, sp_name *fun);
-bool
-sp_merge_hash(HASH *dst, HASH *src);
-void
-sp_cache_routines(THD *thd, LEX *lex);
-
+  Procedures for pre-caching of stored routines and building table list
+  for prelocking.
+*/
+void sp_add_used_routine(LEX *lex, sp_name *rt, char rt_type);
+void sp_update_sp_used_routines(HASH *dst, HASH *src);
+bool sp_cache_routines_and_add_tables(THD *thd, LEX *lex);
+void sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex,
+                                               LEX *aux_lex);
+void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
+                                         Table_triggers_list *triggers);
 
 //
 // Utilities...

--- 1.144/sql/sp_head.cc	2005-06-10 18:56:25 +04:00
+++ 1.145/sql/sp_head.cc	2005-07-05 00:08:48 +04:00
@@ -242,8 +242,11 @@
 void
 sp_name::init_qname(THD *thd)
 {
-  m_qname.length= m_db.length+m_name.length+1;
-  m_qname.str= thd->alloc(m_qname.length+1);
+  m_sroutines_key.length=  m_db.length + m_name.length + 2;
+  if (!(m_sroutines_key.str= thd->alloc(m_sroutines_key.length)))
+    return;
+  m_qname.length= m_sroutines_key.length - 1;
+  m_qname.str= m_sroutines_key.str + 1;
   sprintf(m_qname.str, "%*s.%*s",
 	  m_db.length, (m_db.length ? m_db.str : ""),
 	  m_name.length, m_name.str);
@@ -315,16 +318,13 @@
 {
   extern byte *
     sp_table_key(const byte *ptr, uint *plen, my_bool first);
-  extern byte 
-    *sp_lex_sp_key(const byte *ptr, uint *plen, my_bool first);
   DBUG_ENTER("sp_head::sp_head");
 
   state= INITIALIZED_FOR_SP;
   m_backpatch.empty();
   m_lex.empty();
   hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
-  hash_init(&m_spfuns, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
-  hash_init(&m_spprocs, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
+  hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0);
   DBUG_VOID_RETURN;
 }
 
@@ -527,8 +527,7 @@
   }
 
   hash_free(&m_sptabs);
-  hash_free(&m_spfuns);
-  hash_free(&m_spprocs);
+  hash_free(&m_sroutines);
   DBUG_VOID_RETURN;
 }
 
@@ -1037,11 +1036,10 @@
   oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
 
   /*
-    Add routines which are used by statement to respective sets for
-    this routine
+    Add routines which are used by statement to respective set for
+    this routine.
   */
-  sp_merge_hash(&m_spfuns, &sublex->spfuns);
-  sp_merge_hash(&m_spprocs, &sublex->spprocs);
+  sp_update_sp_used_routines(&m_sroutines, &sublex->sroutines);
   /*
     Merge tables used by this statement (but not by its functions or
     procedures) to multiset of tables used by this routine.
@@ -1578,16 +1576,22 @@
 int
 sp_instr_set_trigger_field::execute(THD *thd, uint *nextp)
 {
-  int res= 0;
-
   DBUG_ENTER("sp_instr_set_trigger_field::execute");
-  /* QQ: Still unsure what should we return in case of error 1 or -1 ? */
-  if (!value->fixed && value->fix_fields(thd, 0, &value) ||
-      trigger_field->fix_fields(thd, 0, 0) ||
-      (value->save_in_field(trigger_field->field, 0) < 0))
+  DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
+}
+
+
+int
+sp_instr_set_trigger_field::exec_core(THD *thd, uint *nextp)
+{
+  int res= 0;
+  Item *it= sp_prepare_func_item(thd, &value);
+  if (!it ||
+      !trigger_field->fixed && trigger_field->fix_fields(thd, 0, 0) ||
+      (it->save_in_field(trigger_field->field, 0) < 0))
     res= -1;
-  *nextp= m_ip + 1;
-  DBUG_RETURN(res);
+  *nextp = m_ip+1;
+  return res;
 }
 
 void
@@ -2399,72 +2403,3 @@
   return table;
 }
 
-
-/*
-  Auxilary function for adding tables used by routines used in query
-  to table lists.
-
-  SYNOPSIS
-    sp_add_sp_tables_to_table_list_aux()
-      thd       - thread context
-      lex       - LEX to which table list tables will be added
-      func_hash - routines for which tables should be added
-      func_cache- SP cache in which this routines should be looked up
-
-  NOTE
-    See sp_add_sp_tables_to_table_list() for more info.
-
-  RETURN VALUE
-    TRUE  - some tables were added
-    FALSE - no tables were added.
-*/
-
-static bool
-sp_add_sp_tables_to_table_list_aux(THD *thd, LEX *lex, HASH *func_hash,
-                                   sp_cache **func_cache)
-{
-  uint i;
-  bool result= FALSE;
-
-  for (i= 0 ; i < func_hash->records ; i++)
-  {
-    sp_head *sp;
-    LEX_STRING *ls= (LEX_STRING *)hash_element(func_hash, i);
-    sp_name name(*ls);
-
-    name.m_qname= *ls;
-    if ((sp= sp_cache_lookup(func_cache, &name)))
-      result|= sp->add_used_tables_to_table_list(thd, &lex->query_tables_last);
-  }
-
-  return result;
-}
-
-
-/*
-  Add tables used by routines used in query to table list.
-
-  SYNOPSIS
-    sp_add_sp_tables_to_table_list()
-      thd      - thread context
-      lex      - LEX to which table list tables will be added
-      func_lex - LEX for which functions we get tables
-                 (useful for adding tables used by view routines)
-
-  NOTE
-    Elements of list will be allocated in PS memroot, so this
-    list will be persistent between PS execetutions.
-
-  RETURN VALUE
-    TRUE  - some tables were added
-    FALSE - no tables were added.
-*/
-
-bool
-sp_add_sp_tables_to_table_list(THD *thd, LEX *lex, LEX *func_lex)
-{
-  return (sp_add_sp_tables_to_table_list_aux(thd, lex, &func_lex->spfuns,
-                                             &thd->sp_func_cache) |
-          sp_add_sp_tables_to_table_list_aux(thd, lex, &func_lex->spprocs,
-                                             &thd->sp_proc_cache));
-}

--- 1.55/sql/sp_head.h	2005-06-14 23:45:44 +04:00
+++ 1.56/sql/sp_head.h	2005-07-05 00:08:48 +04:00
@@ -48,24 +48,50 @@
   LEX_STRING m_db;
   LEX_STRING m_name;
   LEX_STRING m_qname;
+  /*
+    Key representing routine in the set of stored routines used by statement.
+    Consists of 1-byte routine type and m_qname (which usually refences to
+    same buffer). Can't be treated fully unitialized until one won't set
+    routine type using set_routine_type() method.
+  */
+  LEX_STRING m_sroutines_key;
 
   sp_name(LEX_STRING name)
     : m_name(name)
   {
-    m_db.str= m_qname.str= 0;
-    m_db.length= m_qname.length= 0;
+    m_db.str= m_qname.str= m_sroutines_key.str= 0;
+    m_db.length= m_qname.length= m_sroutines_key.length= 0;
   }
 
   sp_name(LEX_STRING db, LEX_STRING name)
     : m_db(db), m_name(name)
   {
-    m_qname.str= 0;
-    m_qname.length= 0;
+    m_qname.str= m_sroutines_key.str= 0;
+    m_qname.length= m_sroutines_key.length= 0;
+  }
+
+  /*
+    Creates temporary sp_name object from key, used mainly
+    for SP-cachelookups.
+  */
+  sp_name(char *key, uint key_len)
+  {
+    m_sroutines_key.str= key;
+    m_sroutines_key.length= key_len;
+    m_name.str= m_qname.str= key + 1;
+    m_name.length= m_qname.length= key_len - 1;
+    m_db.str= 0;
+    m_db.length= 0;
   }
 
   // Init. the qualified name from the db and name.
   void init_qname(THD *thd);	// thd for memroot allocation
 
+  void set_routine_type(char type)
+  {
+    m_sroutines_key.str[0]= type;
+  }
+
   ~sp_name()
   {}
 };
@@ -106,13 +132,13 @@
   longlong m_created;
   longlong m_modified;
   /*
-    Sets containing names of SP and SF used by this routine.
-
-    TODO Probably we should combine these two hashes in one. It will
-    decrease memory overhead ans simplify algorithms using them. The
-    same applies to similar hashes in LEX.
+    Set containing names of stored routines used by this routine.
+    Note that unlike elements of similar set for statement elements of this
+    set are not linked in one list. Because of this we are able save memory
+    by using for this set same objects that are used in 'sroutines' sets
+    for statements of which this stored routine consists.
   */
-  HASH m_spfuns, m_spprocs;
+  HASH m_sroutines;
   // Pointers set during parsing
   uchar *m_param_begin, *m_param_end, *m_body_begin;
 
@@ -471,10 +497,11 @@
 public:
 
   sp_instr_set_trigger_field(uint ip, sp_pcontext *ctx,
-                             Item_trigger_field *trg_fld, Item *val)
+                             Item_trigger_field *trg_fld,
+                             Item *val, LEX *lex)
     : sp_instr(ip, ctx),
       trigger_field(trg_fld),
-      value(val)
+      value(val), m_lex_keeper(lex, TRUE)
   {}
 
   virtual ~sp_instr_set_trigger_field()
@@ -482,11 +509,14 @@
 
   virtual int execute(THD *thd, uint *nextp);
 
+  virtual int exec_core(THD *thd, uint *nextp);
+
   virtual void print(String *str);
 
 private:
   Item_trigger_field *trigger_field;
   Item *value;
+  sp_lex_keeper m_lex_keeper;
 }; // class sp_instr_trigger_field : public sp_instr
 
 
@@ -951,7 +981,5 @@
 sp_add_to_query_tables(THD *thd, LEX *lex,
 		       const char *db, const char *name,
 		       thr_lock_type locktype);
-bool
-sp_add_sp_tables_to_table_list(THD *thd, LEX *lex, LEX *func_lex);
 
 #endif /* _SP_HEAD_H_ */
Thread
bk commit into 5.0 tree (dlenev:1.1946) BUG#8406dlenev4 Jul