List:Commits« Previous MessageNext Message »
From:marc.alff Date:November 2 2007 5:20pm
Subject:bk commit into 5.1 tree (malff:1.2603) BUG#12093
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of malff. When malff 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@stripped, 2007-11-02 11:20:03-06:00, malff@stripped. +31 -0
  Bug#12093 (SP not found on second PS execution if another thread drops other
  SP in between)
  
  TEMPORARY PATCH FOR INTERNAL DISCUSION
  
  NOT READY FOR PRODUCTION
  
  This patch is only a prototype, for review.
  The following bugs are fixed and tested:
  - Bug 12093 (SP not found on second PS execution if another thread drops other
    SP in between)
  - Bug 21294 (executing a prepared statement that executes a stored function
    which was recreat)
  - Bug 27420 (A combination of PS and view operations cause error + assertion
    on shutdown)
  - Bug 27430 Crash in subquery code when in PS and table DDL changed after
    PREPARE()
  - Bug 27690 (Re-execution of prepared statement after table was replaced with
    a view crashes)

  mysql-test/r/marc_12093.result@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +40 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION
    

  mysql-test/r/marc_12093.result@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +0 -0

  mysql-test/r/marc_21294.result@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +16 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION
    

  mysql-test/r/marc_21294.result@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +0 -0

  mysql-test/r/marc_27420.result@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +27 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION
    

  mysql-test/r/marc_27420.result@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +0 -0

  mysql-test/r/marc_27430.result@stripped, 2007-11-02 11:19:58-06:00, malff@stripped. +42 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION
    

  mysql-test/r/marc_27430.result@stripped, 2007-11-02 11:19:58-06:00, malff@stripped. +0 -0

  mysql-test/r/marc_27690.result@stripped, 2007-11-02 11:19:58-06:00, malff@stripped. +22 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION
    

  mysql-test/r/marc_27690.result@stripped, 2007-11-02 11:19:58-06:00, malff@stripped. +0 -0

  mysql-test/r/ps_1general.result@stripped, 2007-11-02 11:19:53-06:00, malff@stripped. +6 -11
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  mysql-test/r/sp-error.result@stripped, 2007-11-02 11:19:54-06:00, malff@stripped. +1 -1
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  mysql-test/r/sp.result@stripped, 2007-11-02 11:19:54-06:00, malff@stripped. +6 -6
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  mysql-test/r/sp_notembedded.result@stripped, 2007-11-02 11:19:54-06:00, malff@stripped. +6 -6
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  mysql-test/r/trigger.result@stripped, 2007-11-02 11:19:54-06:00, malff@stripped. +2 -1
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  mysql-test/t/marc_12093.test@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +69 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION
    

  mysql-test/t/marc_12093.test@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +0 -0

  mysql-test/t/marc_21294.test@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +35 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION
    

  mysql-test/t/marc_21294.test@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +0 -0

  mysql-test/t/marc_27420.test@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +58 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION
    

  mysql-test/t/marc_27420.test@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +0 -0

  mysql-test/t/marc_27430.test@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +55 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION
    

  mysql-test/t/marc_27430.test@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +0 -0

  mysql-test/t/marc_27690.test@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +36 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION
    

  mysql-test/t/marc_27690.test@stripped, 2007-11-02 11:19:57-06:00, malff@stripped. +0 -0

  mysql-test/t/ps_1general.test@stripped, 2007-11-02 11:19:54-06:00, malff@stripped. +12 -1
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  mysql-test/t/sp-error.test@stripped, 2007-11-02 11:19:54-06:00, malff@stripped. +0 -4
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  mysql-test/t/sp.test@stripped, 2007-11-02 11:19:55-06:00, malff@stripped. +6 -6
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  mysql-test/t/sp_notembedded.test@stripped, 2007-11-02 11:19:55-06:00, malff@stripped. +6 -6
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  mysql-test/t/trigger.test@stripped, 2007-11-02 11:19:55-06:00, malff@stripped. +2 -4
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  sql/share/errmsg.txt@stripped, 2007-11-02 11:19:56-06:00, malff@stripped. +4 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  sql/sp.cc@stripped, 2007-11-02 11:19:55-06:00, malff@stripped. +147 -50
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  sql/sp.h@stripped, 2007-11-02 11:19:55-06:00, malff@stripped. +1 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  sql/sp_head.cc@stripped, 2007-11-02 11:19:55-06:00, malff@stripped. +1 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  sql/sp_head.h@stripped, 2007-11-02 11:19:55-06:00, malff@stripped. +2 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  sql/sql_base.cc@stripped, 2007-11-02 11:19:56-06:00, malff@stripped. +36 -1
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  sql/sql_class.cc@stripped, 2007-11-02 11:19:56-06:00, malff@stripped. +60 -1
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  sql/sql_class.h@stripped, 2007-11-02 11:19:56-06:00, malff@stripped. +106 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  sql/sql_prepare.cc@stripped, 2007-11-02 11:19:56-06:00, malff@stripped. +669 -13
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  sql/sql_view.cc@stripped, 2007-11-02 11:19:56-06:00, malff@stripped. +51 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

  sql/sql_view.h@stripped, 2007-11-02 11:19:56-06:00, malff@stripped. +2 -0
    TEMPORARY PATCH FOR INTERNAL DISCUSION
    
    NOT READY FOR PRODUCTION

diff -Nrup a/mysql-test/r/marc_12093.result b/mysql-test/r/marc_12093.result
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/marc_12093.result	2007-11-02 11:19:57 -06:00
@@ -0,0 +1,40 @@
+drop table if exists table_12093;
+drop function if exists func_12093;
+drop function if exists func_12093_unrelated;
+drop procedure if exists proc_12093;
+create table table_12093(a int);
+create function func_12093()
+returns int
+begin
+return (select count(*) from table_12093);
+end//
+create procedure proc_12093(a int)
+begin
+select * from table_12093;
+end//
+create function func_12093_unrelated() returns int return 2;
+create procedure proc_12093_unrelated() begin end;
+prepare stmt_sf from 'select func_12093();';
+prepare stmt_sp from 'call proc_12093(func_12093())';
+execute stmt_sf;
+func_12093()
+0
+execute stmt_sp;
+a
+drop function func_12093_unrelated;
+drop procedure proc_12093_unrelated;
+execute stmt_sf;
+func_12093()
+0
+execute stmt_sp;
+a
+execute stmt_sf;
+func_12093()
+0
+execute stmt_sp;
+a
+deallocate prepare stmt_sf;
+deallocate prepare stmt_sp;
+drop table table_12093;
+drop function func_12093;
+drop procedure proc_12093;
diff -Nrup a/mysql-test/r/marc_21294.result b/mysql-test/r/marc_21294.result
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/marc_21294.result	2007-11-02 11:19:57 -06:00
@@ -0,0 +1,16 @@
+drop function if exists func_21294;
+create function func_21294() returns int return 10;
+prepare stmt from "select func_21294()";
+execute stmt;
+func_21294()
+10
+drop function func_21294;
+create function func_21294() returns int return 10;
+execute stmt;
+ERROR HY000: Prepared statement has been invalidated, because FUNCTION 'test.func_21294' has changed
+drop function func_21294;
+create function func_21294() returns int return 20;
+execute stmt;
+ERROR HY000: Prepared statement has been invalidated, because FUNCTION 'test.func_21294' has changed
+deallocate prepare stmt;
+drop function func_21294;
diff -Nrup a/mysql-test/r/marc_27420.result b/mysql-test/r/marc_27420.result
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/marc_27420.result	2007-11-02 11:19:57 -06:00
@@ -0,0 +1,27 @@
+drop table if exists t_27420_100;
+drop table if exists t_27420_101;
+drop view if exists v_27420;
+create table t_27420_100(a int);
+insert into t_27420_100 values (1), (2);
+create table t_27420_101(a int);
+insert into t_27420_101 values (1), (2);
+create view v_27420 as select t_27420_100.a X, t_27420_101.a Y
+from t_27420_100, t_27420_101
+where t_27420_100.a=t_27420_101.a;
+prepare stmt from 'select * from v_27420';
+execute stmt;
+X	Y
+1	1
+2	2
+drop view v_27420;
+create table v_27420(X int, Y int);
+execute stmt;
+ERROR HY000: Prepared statement has been invalidated, because VIEW 'test.v_27420' has changed
+drop table v_27420;
+create table v_27420 (a int, b int, filler char(200));
+execute stmt;
+ERROR HY000: Prepared statement has been invalidated, because VIEW 'test.v_27420' has changed
+deallocate prepare stmt;
+drop table t_27420_100;
+drop table t_27420_101;
+drop table v_27420;
diff -Nrup a/mysql-test/r/marc_27430.result b/mysql-test/r/marc_27430.result
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/marc_27430.result	2007-11-02 11:19:58 -06:00
@@ -0,0 +1,42 @@
+drop table if exists t_27430_1;
+drop table if exists t_27430_2;
+create table t_27430_1 (a int not null, oref int not null, key(a));
+insert into t_27430_1 values
+(1, 1),
+(1, 1234),
+(2, 3),
+(2, 1234),
+(3, 1234);
+create table t_27430_2 (a int not null, oref int not null);
+insert into t_27430_2 values
+(1, 1),
+(2, 2),
+(1234, 3),
+(1234, 4);
+prepare stmt from 
+'select oref, a, a in (select a from t_27430_1 where oref=t_27430_2.oref) Z from t_27430_2';
+execute stmt;
+oref	a	Z
+1	1	1
+2	2	0
+3	1234	0
+4	1234	0
+drop table t_27430_1, t_27430_2;
+create table t_27430_1 (a int, oref int, key(a));
+insert into t_27430_1 values 
+(1, 1),
+(1, NULL),
+(2, 3),
+(2, NULL),
+(3, NULL);
+create table t_27430_2 (a int, oref int);
+insert into t_27430_2 values
+(1, 1),
+(2,2),
+(NULL, 3),
+(NULL, 4);
+execute stmt;
+ERROR HY000: Prepared statement has been invalidated, because TABLE 'test.t_27430_2' has changed
+deallocate prepare stmt;
+drop table t_27430_1;
+drop table t_27430_2;
diff -Nrup a/mysql-test/r/marc_27690.result b/mysql-test/r/marc_27690.result
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/marc_27690.result	2007-11-02 11:19:58 -06:00
@@ -0,0 +1,22 @@
+drop table if exists t_27690_1;
+drop view if exists v_27690_1;
+drop table if exists v_27690_2;
+create table t_27690_1 (a int, b int);
+insert into t_27690_1 values (1,1),(2,2);
+create table v_27690_1 as select * from t_27690_1;
+create table v_27690_2 as select * from t_27690_1;
+prepare stmt from 'select * from v_27690_1, v_27690_2';
+execute stmt;
+a	b	a	b
+1	1	1	1
+2	2	1	1
+1	1	2	2
+2	2	2	2
+drop table v_27690_1;
+create view v_27690_1 as select A.a, A.b from t_27690_1 A, t_27690_1 B;
+execute stmt;
+ERROR HY000: Prepared statement has been invalidated, because TABLE 'test.v_27690_1' has changed
+deallocate prepare stmt;
+drop table t_27690_1;
+drop view v_27690_1;
+drop table v_27690_2;
diff -Nrup a/mysql-test/r/ps_1general.result b/mysql-test/r/ps_1general.result
--- a/mysql-test/r/ps_1general.result	2007-09-20 02:56:25 -06:00
+++ b/mysql-test/r/ps_1general.result	2007-11-02 11:19:53 -06:00
@@ -132,8 +132,7 @@ c int
 );
 insert into t5( a, b, c) values( 9, 'recreated table', 9);
 execute stmt2 ;
-a	b	c
-9	recreated table	9
+ERROR HY000: Prepared statement has been invalidated, because TABLE 'test.t5' has changed
 drop table t5 ;
 create table t5
 (
@@ -143,8 +142,7 @@ b char(30)
 );
 insert into t5( a, b, c) values( 9, 'recreated table', 9);
 execute stmt2 ;
-a	b	c
-9	recreated table	9
+ERROR HY000: Prepared statement has been invalidated, because TABLE 'test.t5' has changed
 drop table t5 ;
 create table t5
 (
@@ -155,8 +153,7 @@ d timestamp default current_timestamp
 );
 insert into t5( a, b, c) values( 9, 'recreated table', 9);
 execute stmt2 ;
-a	b	c
-9	recreated table	9
+ERROR HY000: Prepared statement has been invalidated, because TABLE 'test.t5' has changed
 drop table t5 ;
 create table t5
 (
@@ -167,8 +164,7 @@ c int
 );
 insert into t5( a, b, c) values( 9, 'recreated table', 9);
 execute stmt2 ;
-a	b	c
-9	recreated table	9
+ERROR HY000: Prepared statement has been invalidated, because TABLE 'test.t5' has changed
 drop table t5 ;
 create table t5
 (
@@ -178,8 +174,7 @@ c int
 );
 insert into t5( b, c) values( 'recreated table', 9);
 execute stmt2 ;
-a	b	c
-2004-02-29 18:01:59	recreated table	9
+ERROR HY000: Prepared statement has been invalidated, because TABLE 'test.t5' has changed
 drop table t5 ;
 create table t5
 (
@@ -189,7 +184,7 @@ f3 int
 );
 insert into t5( f1, f2, f3) values( 9, 'recreated table', 9);
 execute stmt2 ;
-ERROR 42S22: Unknown column 'test.t5.a' in 'field list'
+ERROR HY000: Prepared statement has been invalidated, because TABLE 'test.t5' has changed
 drop table t5 ;
 prepare stmt1 from ' select * from t1 where a <= 2 ' ;
 execute stmt1 ;
diff -Nrup a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result
--- a/mysql-test/r/sp-error.result	2007-10-30 16:51:01 -06:00
+++ b/mysql-test/r/sp-error.result	2007-11-02 11:19:54 -06:00
@@ -796,7 +796,7 @@ bug11834_2()
 10
 drop function bug11834_1;
 execute stmt;
-ERROR 42000: FUNCTION test.bug11834_2 does not exist
+ERROR 42000: FUNCTION test.bug11834_1 does not exist
 deallocate prepare stmt;
 drop function bug11834_2;
 DROP FUNCTION IF EXISTS bug12953|
diff -Nrup a/mysql-test/r/sp.result b/mysql-test/r/sp.result
--- a/mysql-test/r/sp.result	2007-10-29 08:37:17 -06:00
+++ b/mysql-test/r/sp.result	2007-11-02 11:19:54 -06:00
@@ -4095,7 +4095,7 @@ create procedure bug10100pt(level int, l
 begin
 if level < lim then
 update t3 set a=level;
-FLUSH TABLES;
+## FLUSH TABLES;
 call bug10100pt(level+1, lim);
 else
 select * from t3;
@@ -4105,7 +4105,7 @@ create procedure bug10100pv(level int, l
 begin
 if level < lim then
 update v1 set a=level;
-FLUSH TABLES;
+## FLUSH TABLES;
 call bug10100pv(level+1, lim);
 else
 select * from v1;
@@ -4118,11 +4118,11 @@ if level < lim then
 select level;
 prepare stmt1 from "update t3 set a=a+2";
 execute stmt1;
-FLUSH TABLES;
+## FLUSH TABLES;
 execute stmt1;
-FLUSH TABLES;
+## FLUSH TABLES;
 execute stmt1;
-FLUSH TABLES;
+## FLUSH TABLES;
 deallocate prepare stmt1;
 execute stmt2;
 select * from t3;
@@ -4141,7 +4141,7 @@ select level;
 fetch c into lv;
 select lv;
 update t3 set a=level+lv;
-FLUSH TABLES;
+## FLUSH TABLES;
 call bug10100pc(level+1, lim);
 else
 select * from t3;
diff -Nrup a/mysql-test/r/sp_notembedded.result b/mysql-test/r/sp_notembedded.result
--- a/mysql-test/r/sp_notembedded.result	2007-09-05 12:02:59 -06:00
+++ b/mysql-test/r/sp_notembedded.result	2007-11-02 11:19:54 -06:00
@@ -123,7 +123,7 @@ create procedure bug10100pt(level int, l
 begin
 if level < lim then
 update t3 set a=level;
-FLUSH TABLES;
+## FLUSH TABLES;
 call bug10100pt(level+1, lim);
 else
 select * from t3;
@@ -133,7 +133,7 @@ create procedure bug10100pv(level int, l
 begin
 if level < lim then
 update v1 set a=level;
-FLUSH TABLES;
+## FLUSH TABLES;
 call bug10100pv(level+1, lim);
 else
 select * from v1;
@@ -146,11 +146,11 @@ if level < lim then
 select level;
 prepare stmt1 from "update t3 set a=a+2";
 execute stmt1;
-FLUSH TABLES;
+## FLUSH TABLES;
 execute stmt1;
-FLUSH TABLES;
+## FLUSH TABLES;
 execute stmt1;
-FLUSH TABLES;
+## FLUSH TABLES;
 deallocate prepare stmt1;
 execute stmt2;
 select * from t3;
@@ -169,7 +169,7 @@ select level;
 fetch c into lv;
 select lv;
 update t3 set a=level+lv;
-FLUSH TABLES;
+## FLUSH TABLES;
 call bug10100pc(level+1, lim);
 else
 select * from t3;
diff -Nrup a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result
--- a/mysql-test/r/trigger.result	2007-07-19 09:40:42 -06:00
+++ b/mysql-test/r/trigger.result	2007-11-02 11:19:54 -06:00
@@ -809,6 +809,7 @@ create procedure p1() insert into t1 val
 call p1();
 drop trigger t1_bi;
 execute stmt1;
+ERROR HY000: Prepared statement has been invalidated, because TABLE 'test.t1' has changed
 call p1();
 deallocate prepare stmt1;
 drop procedure p1;
@@ -820,7 +821,7 @@ call p1();
 drop trigger t1_bi;
 create trigger t1_bi after insert on t1 for each row insert into t3 values (new.id);
 execute stmt1;
-ERROR 42S02: Table 'test.t3' doesn't exist
+ERROR HY000: Prepared statement has been invalidated, because TABLE 'test.t1' has changed
 call p1();
 ERROR 42S02: Table 'test.t3' doesn't exist
 deallocate prepare stmt1;
diff -Nrup a/mysql-test/t/marc_12093.test b/mysql-test/t/marc_12093.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/marc_12093.test	2007-11-02 11:19:57 -06:00
@@ -0,0 +1,69 @@
+
+#
+# Bug#12093 (SP not found on second PS execution if another thread drops
+# other SP in between)
+#
+
+--disable_warnings
+drop table if exists table_12093;
+drop function if exists func_12093;
+drop function if exists func_12093_unrelated;
+drop procedure if exists proc_12093;
+--enable_warnings
+
+connect (con1,localhost,root,,);
+
+connection default;
+
+create table table_12093(a int);
+
+delimiter //;
+
+create function func_12093()
+returns int
+begin
+  return (select count(*) from table_12093);
+end//
+
+create procedure proc_12093(a int)
+begin
+  select * from table_12093;
+end//
+
+delimiter ;//
+
+create function func_12093_unrelated() returns int return 2;
+create procedure proc_12093_unrelated() begin end;
+
+prepare stmt_sf from 'select func_12093();';
+prepare stmt_sp from 'call proc_12093(func_12093())';
+
+execute stmt_sf;
+execute stmt_sp;
+
+connection con1;
+
+drop function func_12093_unrelated;
+drop procedure proc_12093_unrelated;
+
+connection default;
+
+# previously, failed with --error 1305
+execute stmt_sf;
+# previously, failed with --error 1305
+execute stmt_sp;
+
+# previously, failed with --error 1305
+execute stmt_sf;
+# previously, failed with --error 1305
+execute stmt_sp;
+
+deallocate prepare stmt_sf;
+deallocate prepare stmt_sp;
+
+disconnect con1;
+
+drop table table_12093;
+drop function func_12093;
+drop procedure proc_12093;
+
diff -Nrup a/mysql-test/t/marc_21294.test b/mysql-test/t/marc_21294.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/marc_21294.test	2007-11-02 11:19:57 -06:00
@@ -0,0 +1,35 @@
+
+#
+# Bug#21294 (executing a prepared statement that executes a stored function
+# which was recreat)
+#
+
+--disable_warnings
+drop function if exists func_21294;
+--enable_warnings
+
+create function func_21294() returns int return 10;
+
+# sp_head::m_version_id is based on time
+--sleep 2
+
+prepare stmt from "select func_21294()";
+execute stmt;
+
+drop function func_21294;
+create function func_21294() returns int return 10;
+
+# might pass or fail, implementation dependent
+--error ER_PS_INVALIDATED
+execute stmt;
+
+drop function func_21294;
+create function func_21294() returns int return 20;
+
+# should fail
+--error ER_PS_INVALIDATED
+execute stmt;
+
+deallocate prepare stmt;
+drop function func_21294;
+
diff -Nrup a/mysql-test/t/marc_27420.test b/mysql-test/t/marc_27420.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/marc_27420.test	2007-11-02 11:19:57 -06:00
@@ -0,0 +1,58 @@
+
+#
+# Bug#27420 (A combination of PS and view operations cause error + assertion
+# on shutdown)
+#
+
+--disable_warnings
+drop table if exists t_27420_100;
+drop table if exists t_27420_101;
+drop view if exists v_27420;
+--enable_warnings
+
+connect (con1,localhost,root,,);
+
+connection default;
+
+create table t_27420_100(a int);
+insert into t_27420_100 values (1), (2);
+
+create table t_27420_101(a int);
+insert into t_27420_101 values (1), (2);
+
+create view v_27420 as select t_27420_100.a X, t_27420_101.a Y
+  from t_27420_100, t_27420_101
+  where t_27420_100.a=t_27420_101.a;
+
+prepare stmt from 'select * from v_27420';
+
+execute stmt;
+
+connection con1;
+
+drop view v_27420;
+create table v_27420(X int, Y int);
+
+connection default;
+
+--error ER_PS_INVALIDATED
+execute stmt;
+
+connection con1;
+
+drop table v_27420;
+# passes in 5.0, fails in 5.1, should pass
+create table v_27420 (a int, b int, filler char(200));
+
+connection default;
+
+--error ER_PS_INVALIDATED
+execute stmt;
+
+disconnect con1;
+
+deallocate prepare stmt;
+drop table t_27420_100;
+drop table t_27420_101;
+drop table v_27420;
+
diff -Nrup a/mysql-test/t/marc_27430.test b/mysql-test/t/marc_27430.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/marc_27430.test	2007-11-02 11:19:57 -06:00
@@ -0,0 +1,55 @@
+
+#
+# Bug#27430 (Crash in subquery code when in PS and table DDL changed after
+# PREPARE)
+#
+
+--disable_warnings
+drop table if exists t_27430_1;
+drop table if exists t_27430_2;
+--enable_warnings
+
+create table t_27430_1 (a int not null, oref int not null, key(a));
+insert into t_27430_1 values
+  (1, 1),
+  (1, 1234),
+  (2, 3),
+  (2, 1234),
+  (3, 1234);
+
+create table t_27430_2 (a int not null, oref int not null);
+insert into t_27430_2 values
+  (1, 1),
+  (2, 2),
+  (1234, 3),
+  (1234, 4);
+
+prepare stmt from 
+ 'select oref, a, a in (select a from t_27430_1 where oref=t_27430_2.oref) Z from t_27430_2';
+
+execute stmt; 
+
+drop table t_27430_1, t_27430_2;
+
+create table t_27430_1 (a int, oref int, key(a));
+insert into t_27430_1 values 
+  (1, 1),
+  (1, NULL),
+  (2, 3),
+  (2, NULL),
+  (3, NULL);
+
+create table t_27430_2 (a int, oref int);
+insert into t_27430_2 values
+  (1, 1),
+  (2,2),
+  (NULL, 3),
+  (NULL, 4);
+
+--error ER_PS_INVALIDATED
+execute stmt;
+
+deallocate prepare stmt;
+drop table t_27430_1;
+drop table t_27430_2;
+
diff -Nrup a/mysql-test/t/marc_27690.test b/mysql-test/t/marc_27690.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/marc_27690.test	2007-11-02 11:19:57 -06:00
@@ -0,0 +1,36 @@
+
+#
+# Bug#27690 (Re-execution of prepared statement after table was replaced
+# with a view crashes)
+#
+
+--disable_warnings
+drop table if exists t_27690_1;
+drop view if exists v_27690_1;
+drop table if exists v_27690_2;
+--enable_warnings
+
+create table t_27690_1 (a int, b int);
+insert into t_27690_1 values (1,1),(2,2);
+
+create table v_27690_1 as select * from t_27690_1;
+create table v_27690_2 as select * from t_27690_1;
+
+prepare stmt from 'select * from v_27690_1, v_27690_2'; 
+execute stmt;
+
+drop table v_27690_1;
+
+## --error ER_PS_INVALIDATED
+## execute stmt;
+
+create view v_27690_1 as select A.a, A.b from t_27690_1 A, t_27690_1 B;
+
+--error ER_PS_INVALIDATED
+execute stmt;
+
+deallocate prepare stmt;
+drop table t_27690_1;
+drop view v_27690_1;
+drop table v_27690_2;
+
diff -Nrup a/mysql-test/t/ps_1general.test b/mysql-test/t/ps_1general.test
--- a/mysql-test/t/ps_1general.test	2007-08-15 02:18:40 -06:00
+++ b/mysql-test/t/ps_1general.test	2007-11-02 11:19:54 -06:00
@@ -161,6 +161,8 @@ create table t5
   c int
 );
 insert into t5( a, b, c) values( 9, 'recreated table', 9);
+# table is the same: pass or fail is implementation dependent
+--error ER_PS_INVALIDATED
 execute stmt2 ;
 drop table t5 ;
 # 2. drop + create table (same column names/types but different order)
@@ -172,6 +174,8 @@ create table t5
   b char(30)
 );
 insert into t5( a, b, c) values( 9, 'recreated table', 9);
+# Table is different, should fail
+--error ER_PS_INVALIDATED
 execute stmt2 ;
 drop table t5 ;
 # 3. drop + create table (same column names/types/order+extra column) 
@@ -184,6 +188,8 @@ create table t5
   d timestamp default current_timestamp
 );
 insert into t5( a, b, c) values( 9, 'recreated table', 9);
+# Table is different, should fail
+--error ER_PS_INVALIDATED
 execute stmt2 ;
 drop table t5 ;
 # 4. drop + create table (same column names/types, different order +
@@ -196,6 +202,8 @@ create table t5
   c int
 );
 insert into t5( a, b, c) values( 9, 'recreated table', 9);
+# Table is different, should fail
+--error ER_PS_INVALIDATED
 execute stmt2 ;
 drop table t5 ;
 # 5. drop + create table (same column names/order, different types)
@@ -207,6 +215,8 @@ create table t5
   c int
 );
 insert into t5( b, c) values( 'recreated table', 9);
+# Table is different, should fail
+--error ER_PS_INVALIDATED
 execute stmt2 ;
 drop table t5 ;
 # 6. drop + create table (same column types/order, different names) 
@@ -218,7 +228,8 @@ create table t5
   f3 int
 );
 insert into t5( f1, f2, f3) values( 9, 'recreated table', 9);
---error 1054
+# Table is different, should fail
+--error ER_PS_INVALIDATED
 execute stmt2 ;
 drop table t5 ;
 
diff -Nrup a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test
--- a/mysql-test/t/sp-error.test	2007-10-30 16:51:01 -06:00
+++ b/mysql-test/t/sp-error.test	2007-11-02 11:19:54 -06:00
@@ -1114,10 +1114,6 @@ execute stmt;
 drop function bug11834_1;
 # Attempt to execute statement should return proper error and 
 # should not crash server.
-
-# NOTE! The error we get from the below query indicates that the sp bug11834_2
-# does not exist(this is wrong but can be accepted)
-# This behaviour has been reported as bug#21294
 --error ER_SP_DOES_NOT_EXIST
 execute stmt;
 deallocate prepare stmt;
diff -Nrup a/mysql-test/t/sp.test b/mysql-test/t/sp.test
--- a/mysql-test/t/sp.test	2007-10-29 08:37:17 -06:00
+++ b/mysql-test/t/sp.test	2007-11-02 11:19:55 -06:00
@@ -4965,7 +4965,7 @@ create procedure bug10100pt(level int, l
 begin
   if level < lim then
     update t3 set a=level;
-    FLUSH TABLES;
+    ## FLUSH TABLES;
     call bug10100pt(level+1, lim);
   else
     select * from t3;
@@ -4976,7 +4976,7 @@ create procedure bug10100pv(level int, l
 begin
   if level < lim then
     update v1 set a=level;
-    FLUSH TABLES;
+    ## FLUSH TABLES;
     call bug10100pv(level+1, lim);
   else
     select * from v1;
@@ -4990,11 +4990,11 @@ begin
     select level;
     prepare stmt1 from "update t3 set a=a+2";
     execute stmt1;
-    FLUSH TABLES;
+    ## FLUSH TABLES;
     execute stmt1;
-    FLUSH TABLES;
+    ## FLUSH TABLES;
     execute stmt1;
-    FLUSH TABLES;
+    ## FLUSH TABLES;
     deallocate prepare stmt1;
     execute stmt2;
     select * from t3;
@@ -5014,7 +5014,7 @@ begin
     fetch c into lv;
     select lv;
     update t3 set a=level+lv;
-    FLUSH TABLES;
+    ## FLUSH TABLES;
     call bug10100pc(level+1, lim);
   else
     select * from t3;
diff -Nrup a/mysql-test/t/sp_notembedded.test b/mysql-test/t/sp_notembedded.test
--- a/mysql-test/t/sp_notembedded.test	2007-09-05 12:02:59 -06:00
+++ b/mysql-test/t/sp_notembedded.test	2007-11-02 11:19:55 -06:00
@@ -178,7 +178,7 @@ create procedure bug10100pt(level int, l
 begin
   if level < lim then
     update t3 set a=level;
-    FLUSH TABLES;
+    ## FLUSH TABLES;
     call bug10100pt(level+1, lim);
   else
     select * from t3;
@@ -189,7 +189,7 @@ create procedure bug10100pv(level int, l
 begin
   if level < lim then
     update v1 set a=level;
-    FLUSH TABLES;
+    ## FLUSH TABLES;
     call bug10100pv(level+1, lim);
   else
     select * from v1;
@@ -203,11 +203,11 @@ begin
     select level;
     prepare stmt1 from "update t3 set a=a+2";
     execute stmt1;
-    FLUSH TABLES;
+    ## FLUSH TABLES;
     execute stmt1;
-    FLUSH TABLES;
+    ## FLUSH TABLES;
     execute stmt1;
-    FLUSH TABLES;
+    ## FLUSH TABLES;
     deallocate prepare stmt1;
     execute stmt2;
     select * from t3;
@@ -227,7 +227,7 @@ begin
     fetch c into lv;
     select lv;
     update t3 set a=level+lv;
-    FLUSH TABLES;
+    ## FLUSH TABLES;
     call bug10100pc(level+1, lim);
   else
     select * from t3;
diff -Nrup a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test
--- a/mysql-test/t/trigger.test	2007-08-15 02:18:40 -06:00
+++ b/mysql-test/t/trigger.test	2007-11-02 11:19:55 -06:00
@@ -982,6 +982,7 @@ call p1();
 # Actually it is enough to do FLUSH TABLES instead of DROP TRIGGER
 drop trigger t1_bi;
 # Server should not crash on these two statements
+--error ER_PS_INVALIDATED
 execute stmt1;
 call p1();
 deallocate prepare stmt1;
@@ -997,10 +998,7 @@ call p1();
 # Altering trigger forcing it use different set of tables
 drop trigger t1_bi;
 create trigger t1_bi after insert on t1 for each row insert into t3 values (new.id);
-# Until we implement proper mechanism for invalidation of PS/SP when table
-# or SP's are changed these two statements will fail with 'Table ... was
-# not locked' error (this mechanism should be based on the new TDC).
---error ER_NO_SUCH_TABLE 
+--error ER_PS_INVALIDATED 
 execute stmt1;
 --error ER_NO_SUCH_TABLE 
 call p1();
diff -Nrup a/sql/share/errmsg.txt b/sql/share/errmsg.txt
--- a/sql/share/errmsg.txt	2007-10-17 02:13:54 -06:00
+++ b/sql/share/errmsg.txt	2007-11-02 11:19:56 -06:00
@@ -6112,3 +6112,7 @@ ER_TRG_CANT_OPEN_TABLE
 
 ER_CANT_CREATE_SROUTINE
   eng "Cannot create stored routine `%-.64s`. Check warnings"
+
+ER_PS_INVALIDATED
+  eng "Prepared statement has been invalidated, because %s '%s' has changed"
+
diff -Nrup a/sql/sp.cc b/sql/sp.cc
--- a/sql/sp.cc	2007-10-30 11:08:11 -06:00
+++ b/sql/sp.cc	2007-11-02 11:19:55 -06:00
@@ -1798,6 +1798,148 @@ static void sp_update_stmt_used_routines
 }
 
 
+/**
+  This helper ensures that a given Stored Function or Stored Procedure
+  is present in the corresponding per session cache,
+  and loads the Stored Function/Procedure from storage into the cache,
+  if necessary.
+  @param [in] thd Current thread
+  @param [in] type Type of stored object to lookup/load
+  @param [in] name Name of the stored object
+  @param [out] sphp Stored object refreshed in the cache, if found
+  @return An error status
+    @retval 0 Success, sphp will contain the stored object fetched, if any
+*/
+
+static int
+sp_cache_load(THD *thd, int type, sp_name *name, sp_head **sphp)
+{
+  sp_cache **cp;
+  sp_head *sp;
+  int ret;
+
+  DBUG_ENTER("sp_cache_load");
+
+  DBUG_ASSERT(name);
+  DBUG_ASSERT(sphp);
+  DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
+              type == TYPE_ENUM_TRIGGER ||
+              type == TYPE_ENUM_FUNCTION);
+
+  /* Triggers are cached with procedures */
+  cp= (type == TYPE_ENUM_FUNCTION ? &thd->sp_func_cache : &thd->sp_proc_cache);
+
+  sp= sp_cache_lookup(cp, name);
+
+  if (! sp)
+  {
+    switch ((ret= db_find_routine(thd, type, name, &sp)))
+    {
+    case SP_OK:
+      sp_cache_insert(cp, sp);
+
+      if (thd->m_object_observer)
+      {
+        if (thd->m_object_observer->notify_known_routine(thd, sp))
+          DBUG_RETURN(1);
+      }
+
+      *sphp= sp;
+      ret= 0;
+      break;
+    case SP_KEY_NOT_FOUND:
+      if (thd->m_object_observer)
+      {
+        if (thd->m_object_observer->notify_unknown_routine(thd, type, name))
+          DBUG_RETURN(1);
+      }
+
+      *sphp= NULL;
+      ret= 0;
+      break;
+    default:
+      /*
+        Any error when loading an existing routine is either some problem
+        with the mysql.proc table, or a parse error because the contents
+        has been tampered with (in which case we clear that error).
+      */
+      if (ret == SP_PARSE_ERROR)
+        thd->clear_error();
+      /*
+        If we cleared the parse error, or when db_find_routine() flagged
+        an error with it's return value without calling my_error(), we
+        set the generic "mysql.proc table corrupt" error here.
+       */
+      if (! thd->is_error())
+      {
+        /*
+          SP allows full NAME_LEN chars thus he have to allocate enough
+          size in bytes. Otherwise there is stack overrun could happen
+          if multibyte sequence is `name`. `db` is still safe because the
+          rest of the server checks against NAME_LEN bytes and not chars.
+          Hence, the overrun happens only if the name is in length > 32 and
+          uses multibyte (cyrillic, greek, etc.)
+        */
+        char n[NAME_LEN*2+2];
+
+        /* m_qname.str is not always \0 terminated */
+        memcpy(n, name->m_qname.str, name->m_qname.length);
+        n[name->m_qname.length]= '\0';
+        my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret);
+        ret= 1;
+      }
+      break;
+    }
+  }
+  else
+  {
+    if (thd->m_object_observer)
+    {
+      if (thd->m_object_observer->notify_known_routine(thd, sp))
+        DBUG_RETURN(2);
+    }
+
+    ret= 0;
+    *sphp= sp;
+  }
+
+  DBUG_RETURN(ret);
+}
+
+
+/**
+  Refresh all the routines used by a statement in the session cache.
+  Note that routines that can be found are reloaded,
+  while routines that can not be found are ignored.
+  @param [in] thd Current thread
+  @param [in] lex Statement semantic tree
+  @return An error status
+    @retval 0 Success
+*/
+int
+sp_refresh_routines(THD *thd, LEX *lex)
+{
+  Sroutine_hash_entry *start;
+  int ret= 0;
+
+  DBUG_ENTER("sp_recache_routines");
+  start= (Sroutine_hash_entry *)lex->sroutines_list.first;
+
+  for (Sroutine_hash_entry *rt= start; rt; rt= rt->next)
+  {
+    sp_name name(thd, rt->key.str, rt->key.length);
+    int type= rt->key.str[0];
+    sp_head *sp;
+
+    ret= sp_cache_load(thd, type, &name, &sp);
+
+    if (ret)
+      break;
+  }
+  DBUG_RETURN(ret);
+}
+
+
 /*
   Cache sub-set of routines used by statement, add tables used by these
   routines to statement table list. Do the same for all routines used
@@ -1836,56 +1978,11 @@ sp_cache_routines_and_add_tables_aux(THD
     int type= rt->key.str[0];
     sp_head *sp;
 
-    if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ?
-                              &thd->sp_func_cache : &thd->sp_proc_cache),
-                              &name)))
-    {
-      switch ((ret= db_find_routine(thd, type, &name, &sp)))
-      {
-      case SP_OK:
-        {
-          if (type == TYPE_ENUM_FUNCTION)
-            sp_cache_insert(&thd->sp_func_cache, sp);
-          else
-            sp_cache_insert(&thd->sp_proc_cache, sp);
-        }
-        break;
-      case SP_KEY_NOT_FOUND:
-        ret= SP_OK;
-        break;
-      default:
-        /*
-          Any error when loading an existing routine is either some problem
-          with the mysql.proc table, or a parse error because the contents
-          has been tampered with (in which case we clear that error).
-        */
-        if (ret == SP_PARSE_ERROR)
-          thd->clear_error();
-        /*
-          If we cleared the parse error, or when db_find_routine() flagged
-          an error with it's return value without calling my_error(), we
-          set the generic "mysql.proc table corrupt" error here.
-         */
-        if (! thd->is_error())
-        {
-          /*
-            SP allows full NAME_LEN chars thus he have to allocate enough
-            size in bytes. Otherwise there is stack overrun could happen
-            if multibyte sequence is `name`. `db` is still safe because the
-            rest of the server checks agains NAME_LEN bytes and not chars.
-            Hence, the overrun happens only if the name is in length > 32 and
-            uses multibyte (cyrillic, greek, etc.)
-          */
-          char n[NAME_LEN*2+2];
-
-          /* m_qname.str is not always \0 terminated */
-          memcpy(n, name.m_qname.str, name.m_qname.length);
-          n[name.m_qname.length]= '\0';
-          my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret);
-        }
-        break;
-      }
-    }
+    ret= sp_cache_load(thd, type, &name, &sp);
+
+    if (ret)
+      break;
+
     if (sp)
     {
       if (!(first && first_no_prelock))
diff -Nrup a/sql/sp.h b/sql/sp.h
--- a/sql/sp.h	2007-10-17 02:13:53 -06:00
+++ b/sql/sp.h	2007-11-02 11:19:55 -06:00
@@ -70,6 +70,7 @@ void sp_add_used_routine(LEX *lex, Query
                          sp_name *rt, char rt_type);
 void sp_remove_not_own_routines(LEX *lex);
 void sp_update_sp_used_routines(HASH *dst, HASH *src);
+int sp_refresh_routines(THD *thd, LEX *lex);
 int sp_cache_routines_and_add_tables(THD *thd, LEX *lex,
                                      bool first_no_prelock);
 int sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex,
diff -Nrup a/sql/sp_head.cc b/sql/sp_head.cc
--- a/sql/sp_head.cc	2007-10-30 11:08:11 -06:00
+++ b/sql/sp_head.cc	2007-11-02 11:19:55 -06:00
@@ -2152,6 +2152,7 @@ sp_head::set_info(longlong created, long
 {
   m_created= created;
   m_modified= modified;
+  m_version_id.set_long(m_modified);
   m_chistics= (st_sp_chistics *) memdup_root(mem_root, (char*) chistics,
                                              sizeof(*chistics));
   if (m_chistics->comment.length == 0)
diff -Nrup a/sql/sp_head.h b/sql/sp_head.h
--- a/sql/sp_head.h	2007-10-16 15:41:27 -06:00
+++ b/sql/sp_head.h	2007-11-02 11:19:55 -06:00
@@ -211,6 +211,8 @@ public:
 
   longlong m_created;
   longlong m_modified;
+  Version_id m_version_id;
+
   /* Recursion level of the current SP instance. The levels are numbered from 0 */
   ulong m_recursion_level;
   /*
diff -Nrup a/sql/sql_base.cc b/sql/sql_base.cc
--- a/sql/sql_base.cc	2007-10-30 13:35:11 -06:00
+++ b/sql/sql_base.cc	2007-11-02 11:19:56 -06:00
@@ -2239,7 +2239,7 @@ bool check_if_table_exists(THD *thd, TAB
 */
 
 
-TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
+TABLE *open_table_impl(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
 		  bool *refresh, uint flags)
 {
   reg1	TABLE *table;
@@ -2709,6 +2709,41 @@ TABLE *open_table(THD *thd, TABLE_LIST *
   table->clear_column_bitmaps();
   DBUG_ASSERT(table->key_read == 0);
   DBUG_RETURN(table);
+}
+
+
+TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
+                  bool *refresh, uint flags)
+{
+  TABLE *table;
+
+  table= open_table_impl(thd, table_list, mem_root, refresh, flags);
+
+  if (thd->m_object_observer)
+  {
+    if (table)
+    {
+      if (thd->m_object_observer->notify_known_table(thd, table->s))
+      {
+        /* FIXME (prototyping) : thd::open_tables needs cleanup */
+        return(NULL);
+      }
+    }
+    else
+    {
+      /* FIXME: Fix TABLE_LIST::db, TABLE_LIST::table_name */
+      LEX_STRING lex_db;
+      LEX_STRING lex_name;
+      lex_db.str= table_list->db;
+      lex_db.length= strlen(lex_db.str);
+      lex_name.str= table_list->table_name;
+      lex_name.length= strlen(lex_name.str);
+
+      thd->m_object_observer->notify_unknown_table(thd, & lex_db, & lex_name);
+    }
+  }
+
+  return table;
 }
 
 
diff -Nrup a/sql/sql_class.cc b/sql/sql_class.cc
--- a/sql/sql_class.cc	2007-10-31 09:33:06 -06:00
+++ b/sql/sql_class.cc	2007-11-02 11:19:56 -06:00
@@ -375,7 +375,8 @@ THD::THD()
    bootstrap(0),
    derived_tables_processing(FALSE),
    spcont(NULL),
-   m_lip(NULL)
+   m_lip(NULL),
+   m_object_observer(NULL)
 {
   ulong tmp;
 
@@ -3420,6 +3421,64 @@ bool Discrete_intervals_list::append(ulo
     elements++;
   }
   DBUG_RETURN(0);
+}
+
+
+Version_id::Version_id()
+  : m_kind(NULL_VERSION),
+    m_long_value(0)
+{
+  m_md5_value[0]= 0;
+}
+
+Version_id::~Version_id()
+{}
+
+bool
+Version_id::is_equal(const Version_id& vid) const
+{
+  if (m_kind != vid.m_kind)
+    return false;
+
+  switch(m_kind)
+  {
+    case NULL_VERSION:
+      return true;
+    case LONG_VERSION:
+      return (m_long_value == vid.m_long_value);
+    case MD5_VERSION:
+      return (strncmp(m_md5_value, vid.m_md5_value, sizeof(m_md5_value)) == 0);
+    default:
+      DBUG_ASSERT(false);
+  }
+}
+
+bool
+Version_id::is_null_or_equal(const Version_id& vid) const
+{
+  return is_null() || vid.is_null() || is_equal(vid);
+}
+
+bool
+Version_id::is_null() const
+{
+  return (m_kind == NULL_VERSION);
+}
+
+void
+Version_id::set_long(longlong value)
+{
+  m_kind= LONG_VERSION;
+  m_long_value= value;
+}
+
+void
+Version_id::set_md5(const LEX_STRING* md5)
+{
+  DBUG_ASSERT(md5 != NULL);
+  DBUG_ASSERT(md5->length == sizeof(m_md5_value));
+  m_kind= MD5_VERSION;
+  memcpy(m_md5_value, md5->str, sizeof(m_md5_value));
 }
 
 #endif /* !defined(MYSQL_CLIENT) */
diff -Nrup a/sql/sql_class.h b/sql/sql_class.h
--- a/sql/sql_class.h	2007-10-31 09:33:06 -06:00
+++ b/sql/sql_class.h	2007-11-02 11:19:56 -06:00
@@ -976,6 +976,110 @@ public:
 
 
 /**
+  A Version_id identifies a version of a given object.
+  Different objects (TABLE, VIEW, FUNCTION, PROCEDURE, ...)
+  can use version ids of different types (MD5, timstampt, counter, ...),
+  so that a Version_id is an heterogeneous type.
+  Versions can be compared for equality, but don't define any order.
+*/
+class Version_id
+{
+public:
+  Version_id();
+  ~Version_id();
+
+  bool is_null() const;
+  bool is_equal(const Version_id& v) const;
+  bool is_null_or_equal(const Version_id& v) const;
+
+  void set_long(longlong value);
+  void set_md5(const LEX_STRING* md5);
+
+private:
+  enum kind {
+    NULL_VERSION,
+    LONG_VERSION,
+    MD5_VERSION
+  };
+
+  kind m_kind;
+  longlong m_long_value;
+  char m_md5_value[32];
+};
+
+
+/**
+  Interface class for observers, used to inspect TABLE / VIEW / FUNCTION
+  and PROCEDURE referenced by a statement.
+  An Object_observer can inspect existing objects,
+  as well as get notified that some objects used in a statement are missing.
+  Observers can cause the current operation to fail, by returning an error
+  from the notification methods.
+*/
+class Object_observer
+{
+protected:
+  Object_observer() {}
+  virtual ~Object_observer() {}
+
+public:
+  /**
+    Notify that an existing FUNCTION or PROCEDURE is used in a statement.
+    @param [in] thd Current thread
+    @param [in] sp Routine used
+    @return An error status
+      @retval 0 Sucess
+  */
+  virtual int notify_known_routine(THD *thd,
+                                   const sp_head* sp) = 0;
+
+  /**
+    Notify that an unknown FUNCTION or PROCEDURE is used in a statement.
+    @param [in] thd Current thread
+    @param [in] type Type of the missing routine
+    @param [in] name Name of the missing routine
+    @return An error status
+      @retval 0 Sucess
+  */
+  virtual int notify_unknown_routine(THD *thd,
+                                     int type,
+                                     const sp_name *name) = 0;
+
+  /**
+    Notify that an existing TABLE is used in a statement.
+    @param [in] thd Current thread
+    @param [in] share Table used
+    @return An error status
+      @retval 0 Sucess
+  */
+  virtual int notify_known_table(THD *thd,
+                                 const TABLE_SHARE *share) = 0;
+
+  /**
+    Notify that an unknown TABLE is used in a statement.
+    @param [in] thd Current thread
+    @param [in] db Schema name of the missing table
+    @param [in] name Table name of the missing table
+    @return An error status
+      @retval 0 Sucess
+  */
+  virtual int notify_unknown_table(THD *thd,
+                                   const LEX_STRING *db,
+                                   const LEX_STRING *name) = 0;
+
+  /**
+    Notify that an existing VIEW is used in a statement.
+    @param [in] thd Current thread
+    @param [in] view View used
+    @return An error status
+      @retval 0 Sucess
+  */
+  virtual int notify_known_view(THD *thd,
+                                const TABLE_LIST *view) = 0;
+};
+
+
+/**
   @class THD
   For each client connection we create a separate thread with THD serving as
   a thread/connection descriptor
@@ -1542,6 +1646,8 @@ public:
     and may point to invalid memory after that.
   */
   Lex_input_stream *m_lip;
+
+  Object_observer *m_object_observer;
 
 #ifdef WITH_PARTITION_STORAGE_ENGINE
   partition_info *work_part_info;
diff -Nrup a/sql/sql_prepare.cc b/sql/sql_prepare.cc
--- a/sql/sql_prepare.cc	2007-10-30 11:08:12 -06:00
+++ b/sql/sql_prepare.cc	2007-11-02 11:19:56 -06:00
@@ -109,6 +109,610 @@ public:
 #endif
 };
 
+
+#define MAX_OBJ_REF_KEY_LEN (2*NAME_LEN + 3)
+
+class Object_reference
+{
+public:
+  Object_reference();
+  Object_reference(const Object_reference& ref);
+  ~Object_reference() {}
+
+  int set_table(const LEX_STRING *db, const LEX_STRING *name)
+  { return set_key('T', db, name); }
+
+  int set_routine(int type, const LEX_STRING *db, const LEX_STRING *name)
+  { return set_key((type == TYPE_ENUM_FUNCTION ? 'F' : 'P'), db, name); }
+
+  int set_view(const LEX_STRING *db, const LEX_STRING *name)
+  { return set_key('V', db, name); }
+
+  uchar* get_hash_key(size_t *length) const
+  {
+    *length= m_key_length;
+    return (uchar*) & m_key[0];
+  }
+
+  const char* get_key_type() const;
+
+  const char* get_key_name() const;
+
+private:
+  int set_key(char kind, const LEX_STRING *db, const LEX_STRING *name);
+
+private:
+  char m_key[MAX_OBJ_REF_KEY_LEN];
+  int m_key_length;
+};
+
+
+class Version_reference
+{
+public:
+  Version_reference(const Object_reference& key, const Version_id& v)
+   : m_key(key), m_version_id(v)
+  {}
+  ~Version_reference() {}
+
+  Object_reference m_key;
+  Version_id m_version_id;
+};
+
+
+class Version_reference_collection
+{
+public:
+  Version_reference_collection();
+  ~Version_reference_collection();
+
+  int init(MEM_ROOT *mem_root);
+  int add(const Object_reference& key, const Version_id & v);
+  int find(const Object_reference& key, const Version_reference ** vref) const;
+
+private:
+  HASH m_hash;
+  MEM_ROOT *m_mem_root;
+
+private:
+  Version_reference_collection(const Version_reference_collection&);
+  Version_reference_collection& operator=(const Version_reference_collection&);
+};
+
+
+class Prepare_observer : public Object_observer
+{
+public:
+  Prepare_observer(Version_reference_collection* refs)
+    : m_references(refs)
+  {}
+
+  virtual ~Prepare_observer() {}
+
+  virtual int notify_known_routine(
+    THD *thd, const sp_head* sp);
+
+  virtual int notify_unknown_routine(
+    THD *thd, int type, const sp_name *name);
+
+  virtual int notify_known_table(
+    THD *thd, const TABLE_SHARE *share);
+
+  virtual int notify_unknown_table(
+    THD *thd, const LEX_STRING *db, const LEX_STRING *name);
+
+  virtual int notify_known_view(
+    THD *thd, const TABLE_LIST *view);
+
+private:
+  Version_reference_collection *m_references;
+};
+
+
+class Execute_observer : public Object_observer
+{
+public:
+  Execute_observer(const Version_reference_collection* refs)
+    : m_error(false), m_references(refs)
+  {}
+
+  virtual ~Execute_observer() {}
+
+  virtual int notify_known_routine(
+    THD *thd, const sp_head* sp);
+
+  virtual int notify_unknown_routine(
+    THD *thd, int type, const sp_name *name);
+
+  virtual int notify_known_table(
+    THD *thd, const TABLE_SHARE *share);
+
+  virtual int notify_unknown_table(
+    THD *thd, const LEX_STRING *db, const LEX_STRING *name);
+
+  virtual int notify_known_view(
+    THD *thd, const TABLE_LIST *view);
+
+private:
+  bool m_error;
+  const Version_reference_collection *m_references;
+};
+
+
+int visit_all_views(THD *thd, TABLE_LIST *start)
+{
+  Object_observer *observer;
+  TABLE_LIST *tables;
+
+  DBUG_ASSERT(thd->m_object_observer);
+
+  observer= thd->m_object_observer;
+  for (tables= start; tables; tables= tables->next_global)
+  {
+    if (tables->view)
+    {
+      if (observer->notify_known_view(thd, tables))
+        return 1;
+    }
+  }
+
+  return 0;
+}
+
+
+int recheck_all_views(THD *thd, TABLE_LIST *start)
+{
+  static char null_md5_buff[32]= {
+    0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,
+  };
+  Object_observer *observer;
+  TABLE_LIST *tables;
+  int rc;
+
+  DBUG_ASSERT(thd->m_object_observer);
+
+  observer= thd->m_object_observer;
+
+  /*
+    FIXME: There is no equivalent of sroutines_list for views,
+    so we have to iterate in TABLE_LIST.
+    This is poor, since the same view might be expanded
+    in multiple places, and will cause multiple parsing
+  */
+  for (tables= start; tables; tables= tables->next_global)
+  {
+    if (tables->view)
+    {
+      TABLE_LIST dummy;
+      dummy.db= tables->db;
+      dummy.table_name= tables->table_name;
+
+      rc= mysql_load_view_md5(thd, &dummy);
+
+      if (rc != 0)
+      {
+        dummy.md5.str= null_md5_buff;
+        dummy.md5.length= sizeof(null_md5_buff);
+      }
+
+      if (observer->notify_known_view(thd, & dummy))
+        return 1;
+    }
+  }
+
+  return 0;
+}
+
+
+
+Object_reference::Object_reference()
+{
+  m_key[0]= '\0';
+  m_key_length= 0;
+}
+
+Object_reference::Object_reference(const Object_reference& ref)
+{
+  memcpy(m_key, ref.m_key, sizeof(m_key));
+  m_key_length= ref.m_key_length;
+}
+
+int
+Object_reference::set_key(
+  char kind, const LEX_STRING *db, const LEX_STRING *name)
+{
+  DBUG_ASSERT(db->length <= NAME_LEN);
+  DBUG_ASSERT(name->length <= NAME_LEN);
+
+  /* format: <type> <db> . <name> \0 */
+  m_key_length= sprintf(m_key, "%c%.*s.%.*s",
+                        kind,
+                        (int) db->length, db->str,
+                        (int) name->length, name->str);
+
+  return 0;
+}
+
+
+const char*
+Object_reference::get_key_type() const
+{
+  switch (m_key[0])
+  {
+  case 'T': return "TABLE";
+  case 'P': return "PROCEDURE";
+  case 'F': return "FUNCTION";
+  case 'V': return "VIEW";
+  default: return "OBJECT";
+  }
+}
+
+const char*
+Object_reference::get_key_name() const
+{
+  return & m_key[1];
+}
+
+Version_reference_collection::Version_reference_collection()
+  : m_mem_root(NULL)
+{}
+
+Version_reference_collection::~Version_reference_collection()
+{}
+
+extern "C" uchar*
+get_version_ref_hash_key(const uchar *buff, size_t *length,
+                         my_bool /* unused */)
+{
+  Version_reference *vref= (Version_reference*) buff;
+  return vref->m_key.get_hash_key(length);
+}
+
+int
+Version_reference_collection::init(MEM_ROOT *mem_root)
+{
+  DBUG_ENTER("Version_reference_collection::init");
+  int rc;
+
+  DBUG_ASSERT(m_mem_root == NULL);
+  m_mem_root= mem_root;
+
+  rc= hash_init(& m_hash,
+                & my_charset_utf8_bin,
+                0,                             /* initial size */
+                0,
+                0,
+                (hash_get_key) get_version_ref_hash_key,
+                NULL,                          /* Nothing to free */
+                MYF(0));
+
+  DBUG_RETURN(rc);
+}
+
+int
+Version_reference_collection::add(
+  const Object_reference& key,
+  const Version_id& vid)
+{
+  Version_reference *vref;
+  size_t hash_key_length;
+  uchar* hash_key;
+
+  DBUG_ENTER("Version_reference_collection::add()");
+
+  hash_key= key.get_hash_key(& hash_key_length);
+
+  vref= (Version_reference*) hash_search(& m_hash, hash_key, hash_key_length);
+
+  if (vref)
+  {
+    if (! vref->m_version_id.is_equal(vid))
+      DBUG_RETURN(1);
+  }
+  else
+  {
+    vref= new Version_reference(key, vid);
+    if (my_hash_insert(& m_hash, (uchar*) vref))
+      DBUG_RETURN(2);
+  }
+
+  DBUG_PRINT("cached",("key: %s  length: %d",
+                      hash_key, (int) hash_key_length));
+  DBUG_RETURN(0);
+}
+
+int
+Version_reference_collection::find(
+  const Object_reference& key,
+  const Version_reference ** vref) const
+{
+  Version_reference *v;
+  size_t hash_key_length;
+  uchar* hash_key;
+
+  hash_key= key.get_hash_key(& hash_key_length);
+  v= (Version_reference*) hash_search(& m_hash, hash_key, hash_key_length);
+
+  *vref= v;
+  return 0;
+}
+
+int
+Prepare_observer::notify_known_routine(THD *thd, const sp_head* sp)
+{
+  Object_reference ref;
+  int rc;
+
+  DBUG_ENTER("Prepare_observer::notify_known_routine()");
+
+  rc= ref.set_routine(sp->m_type, & sp->m_db, & sp->m_name);
+  if (rc == 0)
+  {
+    rc= m_references->add(ref, sp->m_version_id);
+  }
+
+  DBUG_RETURN(rc);
+}
+
+int
+Prepare_observer::notify_unknown_routine(
+  THD *thd, int type, const sp_name *name)
+{
+  Object_reference ref;
+  Version_id none;
+  int rc;
+
+  DBUG_ENTER("Prepare_observer::notify_unknown_routine()");
+
+  rc= ref.set_routine(type, & name->m_db, & name->m_name);
+  if (rc == 0)
+  {
+    rc= m_references->add(ref, none);
+  }
+
+  DBUG_RETURN(rc);
+}
+
+int Prepare_observer::notify_known_table(THD *thd, const TABLE_SHARE *share)
+{
+  Object_reference ref;
+  Version_id version;
+  int rc;
+
+  DBUG_ENTER("Prepare_observer::notify_known_table()");
+  DBUG_ASSERT(share != NULL);
+
+  rc= ref.set_table(& share->db, & share->table_name);
+  if (rc == 0)
+  {
+    version.set_long(share->table_map_id);
+    rc= m_references->add(ref, version);
+  }
+
+  DBUG_RETURN(rc);
+}
+
+int Prepare_observer::notify_unknown_table(
+  THD *thd, const LEX_STRING *db, const LEX_STRING *name)
+{
+  Object_reference ref;
+  Version_id none;
+  int rc;
+
+  DBUG_ENTER("Prepare_observer::notify_unknown_table()");
+
+  rc= ref.set_table(db, name);
+  if (rc == 0)
+  {
+    rc= m_references->add(ref, none);
+  }
+
+  DBUG_RETURN(rc);
+}
+
+int Prepare_observer::notify_known_view(THD *thd, const TABLE_LIST *view)
+{
+  Object_reference ref;
+  Version_id version;
+  int rc;
+
+  DBUG_ENTER("Prepare_observer::notify_known_view()");
+  DBUG_ASSERT(view != NULL);
+
+  /* FIXME: Fix TABLE_LIST::db, TABLE_LIST::table_name */
+  LEX_STRING lex_db;
+  LEX_STRING lex_name;
+  lex_db.str= view->db;
+  lex_db.length= strlen(lex_db.str);
+  lex_name.str= view->table_name;
+  lex_name.length= strlen(lex_name.str);
+
+  rc= ref.set_view(& lex_db, & lex_name);
+  if (rc == 0)
+  {
+    version.set_md5(& view->md5);
+    rc= m_references->add(ref, version);
+  }
+
+  DBUG_RETURN(rc);
+}
+
+
+int Execute_observer::notify_known_routine(THD *thd, const sp_head* sp)
+{
+  Object_reference ref;
+  const Version_reference *vref;
+  int rc;
+
+  DBUG_ENTER("Execute_observer::notify_known_routine()");
+
+  rc= ref.set_routine(sp->m_type, & sp->m_db, & sp->m_name);
+  if (rc == 0)
+  {
+    rc= m_references->find(ref, & vref);
+    if (rc == 0)
+    {
+      if (! vref || ! vref->m_version_id.is_equal(sp->m_version_id))
+      {
+        my_error(ER_PS_INVALIDATED, MYF(0),
+                 ref.get_key_type(),
+                 ref.get_key_name());
+        m_error= true;
+        rc= 1;
+      }
+    }
+  }
+
+  DBUG_RETURN(rc);
+}
+
+
+int Execute_observer::notify_unknown_routine(
+  THD *thd, int type, const sp_name *name)
+{
+  DBUG_ENTER("Execute_observer::notify_unknown_routine()");
+
+  DBUG_RETURN(0);
+}
+
+int Execute_observer::notify_known_table(THD *thd, const TABLE_SHARE *share)
+{
+  Object_reference ref;
+  const Version_reference *vref= NULL;
+  Version_id table_version;
+  int rc;
+
+  DBUG_ENTER("Execute_observer::notify_known_table()");
+
+  rc= ref.set_table(& share->db, & share->table_name);
+  if (rc == 0)
+  {
+    rc= m_references->find(ref, & vref);
+    if (rc == 0)
+    {
+      table_version.set_long(share->table_map_id);
+
+      if (vref)
+      {
+        /* FIXME: is_null_or_equal */
+        if (! vref->m_version_id.is_equal(table_version))
+        {
+          my_error(ER_PS_INVALIDATED, MYF(0),
+                   ref.get_key_type(),
+                   ref.get_key_name());
+          m_error= true;
+          rc= 1;
+        }
+      }
+      else
+      {
+        /*
+          - SHOW COLUMNS FROM <table>
+            --> open the table <table>
+          - INSTALL PLUGIN ... SONAME ...
+            --> open the table mysql.plugin
+          - SHOW CREATE TRIGGER t1_bi
+            --> open the table t1 (that the trigger relates to)
+        */
+        DBUG_PRINT("info", ("Ignoring dependency to '%s' '%s'",
+                            ref.get_key_type(),
+                            ref.get_key_name()));
+      }
+    }
+  }
+
+  DBUG_RETURN(rc);
+}
+
+int Execute_observer::notify_unknown_table(
+  THD *thd, const LEX_STRING *db, const LEX_STRING *name)
+{
+  Object_reference ref;
+  const Version_reference *vref= NULL;
+  int rc;
+
+  DBUG_ENTER("Execute_observer::notify_unknown_table()");
+
+  rc= ref.set_table(db, name);
+  if (rc == 0)
+  {
+    rc= m_references->find(ref, & vref);
+    if (rc == 0)
+    {
+      if (vref)
+      {
+        if (! vref->m_version_id.is_null())
+        {
+          my_error(ER_PS_INVALIDATED, MYF(0),
+                   ref.get_key_type(),
+                   ref.get_key_name());
+          m_error= true;
+          rc= 1;
+        }
+      }
+      else
+      {
+        /*
+          - SHOW COLUMNS FROM <table>
+            --> open the table <table>
+          - INSTALL PLUGIN ... SONAME ...
+            --> open the table mysql.plugin
+          - SHOW CREATE TRIGGER t1_bi
+            --> open the table t1 (that the trigger relates to)
+        */
+        DBUG_PRINT("info", ("Ignoring dependency to '%s' '%s'",
+                            ref.get_key_type(),
+                            ref.get_key_name()));
+      }
+    }
+  }
+
+  DBUG_RETURN(rc);
+}
+
+
+int Execute_observer::notify_known_view(THD *thd, const TABLE_LIST *view)
+{
+  Object_reference ref;
+  const Version_reference *vref= NULL;
+  Version_id view_version;
+  int rc;
+
+  DBUG_ENTER("Execute_observer::notify_known_view()");
+
+  /* FIXME: Fix TABLE_LIST::db, TABLE_LIST::table_name */
+  LEX_STRING lex_db;
+  LEX_STRING lex_name;
+  lex_db.str= view->db;
+  lex_db.length= strlen(lex_db.str);
+  lex_name.str= view->table_name;
+  lex_name.length= strlen(lex_name.str);
+
+  rc= ref.set_view(& lex_db, & lex_name);
+  if (rc == 0)
+  {
+    rc= m_references->find(ref, & vref);
+    if (rc == 0)
+    {
+      view_version.set_md5(& view->md5);
+
+      if (! vref || ! vref->m_version_id.is_equal(view_version))
+      {
+        my_error(ER_PS_INVALIDATED, MYF(0),
+                 ref.get_key_type(),
+                 ref.get_key_name());
+        m_error= true;
+        rc= 1;
+      }
+    }
+  }
+
+  DBUG_RETURN(rc);
+}
+
 /****************************************************************************/
 
 /**
@@ -163,6 +767,8 @@ private:
     SELECT_LEX and other classes).
   */
   MEM_ROOT main_mem_root;
+
+  Version_reference_collection m_vrefs;
 };
 
 
@@ -2698,11 +3304,19 @@ Prepared_statement::Prepared_statement(T
   param_array(0),
   param_count(0),
   last_errno(0),
-  flags((uint) IS_IN_USE)
+  flags((uint) IS_IN_USE),
+  m_vrefs()
 {
   init_alloc_root(&main_mem_root, thd_arg->variables.query_alloc_block_size,
                   thd_arg->variables.query_prealloc_size);
   *last_error= '\0';
+
+  if (m_vrefs.init(& main_mem_root))
+  {
+    state= Query_arena::ERROR;
+    last_errno= ER_OUTOFMEMORY;
+    sprintf(last_error, ER(ER_OUTOFMEMORY), 0);
+  }
 }
 
 
@@ -2898,6 +3512,10 @@ bool Prepared_statement::prepare(const c
   */
   DBUG_ASSERT(thd->change_list.is_empty());
 
+  Prepare_observer observer(& m_vrefs);
+  Object_observer *save_observer= thd->m_object_observer;
+  thd->m_object_observer= & observer;
+
   /* 
    The only case where we should have items in the thd->free_list is
    after stmt->set_params_from_vars(), which may in some cases create
@@ -2907,6 +3525,14 @@ bool Prepared_statement::prepare(const c
   if (error == 0)
     error= check_prepared_statement(this, name.str != 0);
 
+  if (error == 0)
+    error= sp_refresh_routines(thd, thd->lex);
+
+  if (error == 0)
+    error= visit_all_views(thd, thd->lex->query_tables);
+
+  thd->m_object_observer= save_observer;
+
   /*
     Currently CREATE PROCEDURE/TRIGGER/EVENT are prohibited in prepared
     statements: ensure we have no memory leak here if by someone tries
@@ -3083,22 +3709,52 @@ bool Prepared_statement::execute(String 
 
   /* Go! */
 
-  if (open_cursor)
-    error= mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR,
-                             &result, &cursor);
-  else
-  {
+  /*
+    Try to find it in the query cache, if not, execute it.
+    Note that multi-statements cannot exist here (they are not supported in
+    prepared statements).
+  */
+  if (query_cache_send_result_to_client(thd, thd->query,
+                                        thd->query_length) <= 0)
+  {
+    int rc;
+    Execute_observer observer(& m_vrefs);
+    Object_observer *save_observer= thd->m_object_observer;
+    thd->m_object_observer= & observer;
+
     /*
-      Try to find it in the query cache, if not, execute it.
-      Note that multi-statements cannot exist here (they are not supported in
-      prepared statements).
+      For tables, the TABLE_SHARE cache automatically detects outdated
+      or missing tables, and transparently reload them.
+      For Stored Procedures and Stored Functions, sp_cache does not
+      provide this functionality, so that the list of routines used
+      by this prepared statement has to be refreshed in the sp_cache
+      explicitly.
     */
-    if (query_cache_send_result_to_client(thd, thd->query,
-                                          thd->query_length) <= 0)
+    rc= sp_refresh_routines(thd, thd->lex);
+
+    /*
+      For views, there is no caching at all,
+      and the content of thd->lex->query_tables could be obsolete by now.
+      Making sure that the views in-lined in TABLE_LIST are still valid.
+    */
+    if (rc == 0)
+      rc= recheck_all_views(thd, thd->lex->query_tables);
+
+    if (rc == 0)
     {
-      error= mysql_execute_command(thd);
-      query_cache_end_of_result(thd);
+      if (open_cursor)
+        error= mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR,
+                                 &result, &cursor);
+      else
+      {
+        error= mysql_execute_command(thd);
+        query_cache_end_of_result(thd);
+      }
     }
+    else
+      error= 1;
+
+    thd->m_object_observer= save_observer;
   }
 
   /*
diff -Nrup a/sql/sql_view.cc b/sql/sql_view.cc
--- a/sql/sql_view.cc	2007-10-30 11:08:13 -06:00
+++ b/sql/sql_view.cc	2007-11-02 11:19:56 -06:00
@@ -674,6 +674,19 @@ static File_option view_parameters[]=
   FILE_OPTIONS_STRING}
 };
 
+/* Short parsing, only to read MD5 */
+static File_option view_md5_parameters[]=
+{{{ C_STRING_WITH_LEN("query")},
+  my_offsetof(TABLE_LIST, select_stmt),
+  FILE_OPTIONS_ESTRING},
+ {{ C_STRING_WITH_LEN("md5")},
+  my_offsetof(TABLE_LIST, md5),
+  FILE_OPTIONS_STRING},
+ {{NullS, 0},			0,
+  FILE_OPTIONS_STRING}
+};
+
+
 static LEX_STRING view_file_type[]= {{(char*) STRING_WITH_LEN("VIEW") }};
 
 
@@ -1440,6 +1453,44 @@ err:
   table->view= 0;	// now it is not VIEW placeholder
   result= 1;
   goto end;
+}
+
+int mysql_load_view_md5(THD *thd, TABLE_LIST *view)
+{
+  int rc;
+  LEX_STRING pathstr;
+  File_parser *parser;
+  char path_buff[FN_REFLEN];
+  TABLE_LIST dummy;
+
+  DBUG_ENTER("mysql_load_view_md5");
+  DBUG_PRINT("info", ("view: (%s.%s)", view->db, view->table_name));
+
+  bzero(& dummy, sizeof(dummy));
+
+  pathstr.str= (char *) path_buff;
+  pathstr.length= build_table_filename(path_buff, sizeof(path_buff) - 1,
+                                       view->db, view->table_name,
+                                       reg_ext, 0);
+
+  parser= sql_parse_prepare(&pathstr, thd->mem_root, 0);
+  if (parser &&
+      parser->ok() &&
+      is_equal(&view_type, parser->type()) &&
+      parser->parse((uchar*) &dummy,
+                    thd->mem_root,
+                    view_md5_parameters,
+                    array_elements(view_md5_parameters)-1,
+                    &file_parser_dummy_hook) == 0)
+  {
+    view->md5.str= dummy.md5.str;
+    view->md5.length= dummy.md5.length;
+    rc= 0;
+  }
+  else
+    rc= 1;
+
+  DBUG_RETURN(rc);
 }
 
 
diff -Nrup a/sql/sql_view.h b/sql/sql_view.h
--- a/sql/sql_view.h	2006-12-23 12:19:56 -07:00
+++ b/sql/sql_view.h	2007-11-02 11:19:56 -06:00
@@ -31,6 +31,8 @@ frm_type_enum mysql_frm_type(THD *thd, c
 
 int view_checksum(THD *thd, TABLE_LIST *view);
 
+int mysql_load_view_md5(THD *thd, TABLE_LIST *view);
+
 extern TYPELIB updatable_views_with_limit_typelib;
 
 bool check_duplicate_names(List<Item>& item_list, bool gen_unique_view_names);
Thread
bk commit into 5.1 tree (malff:1.2603) BUG#12093marc.alff2 Nov