From 6c6bbba00770b241818b35fe4b1d195e0d34142a Mon Sep 17 00:00:00 2001
From: "pem@mysql.comhem.se" <>
Date: Thu, 11 Mar 2004 17:18:59 +0100
Subject: [PATCH] WL#1366: Use the schema (db) associated with an SP. Phase 2:
 Make SPs belong to a DB, and use qualified names.   As a side effect, using
 USE in an SP is no longer allowed.   (It just doesn't work otherwise.)

---
 include/mysqld_error.h            |   3 +-
 include/sql_state.h               |   1 +
 mysql-test/r/sp-error.result      |   4 +
 mysql-test/r/sp-security.result   |  22 ++---
 mysql-test/r/sp.result            |  59 +++++-------
 mysql-test/t/sp-error.test        |   6 ++
 mysql-test/t/sp-security.test     |  18 ++--
 mysql-test/t/sp.test              |  31 ++-----
 sql/mysql_priv.h                  |   3 +-
 sql/share/czech/errmsg.txt        |   1 +
 sql/share/danish/errmsg.txt       |   1 +
 sql/share/dutch/errmsg.txt        |   1 +
 sql/share/english/errmsg.txt      |   1 +
 sql/share/estonian/errmsg.txt     |   1 +
 sql/share/french/errmsg.txt       |   1 +
 sql/share/german/errmsg.txt       |   1 +
 sql/share/greek/errmsg.txt        |   1 +
 sql/share/hungarian/errmsg.txt    |   1 +
 sql/share/italian/errmsg.txt      |   1 +
 sql/share/japanese/errmsg.txt     |   1 +
 sql/share/korean/errmsg.txt       |   1 +
 sql/share/norwegian-ny/errmsg.txt |   1 +
 sql/share/norwegian/errmsg.txt    |   1 +
 sql/share/polish/errmsg.txt       |   1 +
 sql/share/portuguese/errmsg.txt   |   1 +
 sql/share/romanian/errmsg.txt     |   1 +
 sql/share/russian/errmsg.txt      |   1 +
 sql/share/serbian/errmsg.txt      |   1 +
 sql/share/slovak/errmsg.txt       |   1 +
 sql/share/spanish/errmsg.txt      |   1 +
 sql/share/swedish/errmsg.txt      |   1 +
 sql/share/ukrainian/errmsg.txt    |   1 +
 sql/sp.cc                         | 147 ++++++++++++++++++++++++------
 sql/sp.h                          |  17 +++-
 sql/sp_cache.cc                   |   9 +-
 sql/sp_head.cc                    |  85 +++++++++++------
 sql/sp_head.h                     |  27 ++----
 sql/sql_db.cc                     |  95 +++++++++++--------
 sql/sql_yacc.yy                   |  21 ++++-
 39 files changed, 368 insertions(+), 203 deletions(-)

diff --git a/include/mysqld_error.h b/include/mysqld_error.h
index a3e063d4ed7..113eb07ed66 100644
--- a/include/mysqld_error.h
+++ b/include/mysqld_error.h
@@ -339,4 +339,5 @@
 #define ER_SP_DUP_CURS 1320
 #define ER_SP_CANT_ALTER 1321
 #define ER_SP_SUBSELECT_NYI 1322
-#define ER_ERROR_MESSAGES 323
+#define ER_SP_NO_USE 1323
+#define ER_ERROR_MESSAGES 324
diff --git a/include/sql_state.h b/include/sql_state.h
index f4fbe434490..c5a71daceb9 100644
--- a/include/sql_state.h
+++ b/include/sql_state.h
@@ -196,3 +196,4 @@ ER_SP_DUP_COND,			"42000", "",
 ER_SP_DUP_CURS,			"42000", "",
 /*ER_SP_CANT_ALTER*/
 ER_SP_SUBSELECT_NYI,            "0A000", "",
+ER_SP_NO_USE,                   "42000", "",
diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result
index f965cf90eb4..1877789e2b0 100644
--- a/mysql-test/r/sp-error.result
+++ b/mysql-test/r/sp-error.result
@@ -251,6 +251,10 @@ declare c cursor for select * from t1;
 declare c cursor for select field from t1;
 end|
 ERROR 42000: Duplicate cursor: c
+create procedure u()
+use sptmp;
+#|
+ERROR 42000: USE is not allowed in a stored procedure
 create procedure bug1965()
 begin
 declare c cursor for select val from t1 order by valname;
diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result
index c4fbece9d72..51439e08782 100644
--- a/mysql-test/r/sp-security.result
+++ b/mysql-test/r/sp-security.result
@@ -8,16 +8,16 @@ create table t1 ( u varchar(64), i int );
 create procedure stamp(i int)
 insert into db1_secret.t1 values (user(), i);
 show procedure status like 'stamp';
-Name	Type	Definer	Modified	Created	Security_type	Comment
-stamp	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment
+db1_secret	stamp	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
 call stamp(1);
 select * from t1;
 u	i
 root@localhost	1
-call stamp(2);
+call db1_secret.stamp(2);
 select * from db1_secret.t1;
 ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret'
-call stamp(3);
+call db1_secret.stamp(3);
 select * from db1_secret.t1;
 ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret'
 select * from t1;
@@ -27,8 +27,8 @@ user1@localhost	2
 anon@localhost	3
 alter procedure stamp sql security invoker;
 show procedure status like 'stamp';
-Name	Type	Definer	Modified	Created	Security_type	Comment
-stamp	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	INVOKER	
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment
+db1_secret	stamp	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	INVOKER	
 call stamp(4);
 select * from t1;
 u	i
@@ -36,9 +36,9 @@ root@localhost	1
 user1@localhost	2
 anon@localhost	3
 root@localhost	4
-call stamp(5);
+call db1_secret.stamp(5);
 ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret'
-call stamp(6);
+call db1_secret.stamp(6);
 ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret'
 drop database if exists db2;
 create database db2;
@@ -73,9 +73,9 @@ s1
 0
 2
 2
-drop procedure stamp;
-drop procedure p;
-drop procedure q;
+drop procedure db1_secret.stamp;
+drop procedure db2.p;
+drop procedure db2.q;
 use test;
 drop database db1_secret;
 drop database db2;
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result
index bf515e4b4cd..0a934817d85 100644
--- a/mysql-test/r/sp.result
+++ b/mysql-test/r/sp.result
@@ -18,17 +18,6 @@ id	data
 foo	42
 delete from t1;
 drop procedure foo42;
-create procedure u()
-use sptmp;
-drop database if exists sptmp;
-create database sptmp;
-use test;
-call u();
-select database();
-database()
-test
-drop database sptmp;
-drop procedure u;
 create procedure bar(x char(16), y int)
 insert into test.t1 values (x, y);
 call bar("bar", 666);
@@ -746,7 +735,7 @@ delete from t1|
 alter procedure chistics sql security invoker name chistics2|
 show create procedure chistics2|
 Procedure	Create Procedure
-chistics2	CREATE PROCEDURE `chistics2`()
+chistics2	CREATE PROCEDURE `test`.`chistics2`()
     SQL SECURITY INVOKER
     COMMENT 'Characteristics procedure test'
 insert into t1 values ("chistics", 1)
@@ -763,7 +752,7 @@ chistics()
 alter function chistics name chistics2 comment 'Characteristics function test'|
 show create function chistics2|
 Function	Create Function
-chistics2	CREATE FUNCTION `chistics2`() RETURNS int
+chistics2	CREATE FUNCTION `test`.`chistics2`() RETURNS int
     DETERMINISTIC
     SQL SECURITY INVOKER
     COMMENT 'Characteristics function test'
@@ -939,23 +928,23 @@ begin
 show create function fac;
 end|
 call bug2267_1()|
-Name	Type	Definer	Modified	Created	Security_type	Comment
-bug2267_1	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
-bug2267_2	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
-bug2267_3	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
-bug2267_4	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment
+test	bug2267_1	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
+test	bug2267_2	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
+test	bug2267_3	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
+test	bug2267_4	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
 call bug2267_2()|
-Name	Type	Definer	Modified	Created	Security_type	Comment
-fac	FUNCTION	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment
+test	fac	FUNCTION	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
 call bug2267_3()|
 Procedure	Create Procedure
-bug2267_1	CREATE PROCEDURE `bug2267_1`()
+bug2267_1	CREATE PROCEDURE `test`.`bug2267_1`()
 begin
 show procedure status;
 end
 call bug2267_4()|
 Function	Create Function
-fac	CREATE FUNCTION `fac`(n int unsigned) RETURNS bigint unsigned
+fac	CREATE FUNCTION `test`.`fac`(n int unsigned) RETURNS bigint unsigned
 begin
 declare f bigint unsigned default 1;
 while n > 1 do
@@ -1029,12 +1018,12 @@ n	f
 20	2432902008176640000
 drop table fac|
 show function status like '%f%'|
-Name	Type	Definer	Modified	Created	Security_type	Comment
-fac	FUNCTION	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment
+test	fac	FUNCTION	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
 drop procedure ifac|
 drop function fac|
 show function status like '%f%'|
-Name	Type	Definer	Modified	Created	Security_type	Comment
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment
 drop table if exists primes|
 create table primes (
 i int unsigned not null primary key,
@@ -1095,7 +1084,7 @@ end while;
 end|
 show create procedure opp|
 Procedure	Create Procedure
-opp	CREATE PROCEDURE `opp`(n bigint unsigned, out pp bool)
+opp	CREATE PROCEDURE `test`.`opp`(n bigint unsigned, out pp bool)
 begin
 declare r double;
 declare b, s bigint unsigned default 0;
@@ -1122,9 +1111,9 @@ end if;
 end loop;
 end
 show procedure status like '%p%'|
-Name	Type	Definer	Modified	Created	Security_type	Comment
-ip	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
-opp	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment
+test	ip	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
+test	opp	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
 call ip(200)|
 select * from primes where i=45 or i=100 or i=199|
 i	p
@@ -1135,7 +1124,7 @@ drop table primes|
 drop procedure opp|
 drop procedure ip|
 show procedure status like '%p%'|
-Name	Type	Definer	Modified	Created	Security_type	Comment
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment
 drop table if exists fib|
 create table fib ( f bigint unsigned not null )|
 insert into fib values (1), (1)|
@@ -1185,19 +1174,19 @@ create procedure bar(x char(16), y int)
 comment "111111111111" sql security invoker
 insert into test.t1 values (x, y)|
 show procedure status like 'bar'|
-Name	Type	Definer	Modified	Created	Security_type	Comment
-bar	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	INVOKER	111111111111
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment
+test	bar	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	INVOKER	111111111111
 alter procedure bar name bar2 comment "2222222222" sql security definer|
 alter procedure bar2 name bar comment "3333333333"|
 alter procedure bar|
 show create procedure bar|
 Procedure	Create Procedure
-bar	CREATE PROCEDURE `bar`(x char(16), y int)
+bar	CREATE PROCEDURE `test`.`bar`(x char(16), y int)
     COMMENT '3333333333'
 insert into test.t1 values (x, y)
 show procedure status like 'bar'|
-Name	Type	Definer	Modified	Created	Security_type	Comment
-bar	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	3333333333
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment
+test	bar	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	3333333333
 drop procedure bar|
 drop table t1;
 drop table t2;
diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test
index 68a5ccdbe42..43b5c04766a 100644
--- a/mysql-test/t/sp-error.test
+++ b/mysql-test/t/sp-error.test
@@ -330,6 +330,12 @@ begin
   declare c cursor for select field from t1;
 end|
 
+# USE is not allowed
+--error 1323
+create procedure u()
+  use sptmp;
+
+
 #
 # BUG#1965
 #
diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test
index ac7477869a1..2d089e72d0b 100644
--- a/mysql-test/t/sp-security.test
+++ b/mysql-test/t/sp-security.test
@@ -24,7 +24,7 @@ create table t1 ( u varchar(64), i int );
 # Our test procedure
 create procedure stamp(i int)
   insert into db1_secret.t1 values (user(), i);
---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
 show procedure status like 'stamp';
 
 # root can, of course
@@ -40,7 +40,7 @@ connect (con3anon,localhost,anon,,);
 connection con2user1;
 
 # This should work...
-call stamp(2);
+call db1_secret.stamp(2);
 
 # ...but not this
 --error 1044
@@ -52,7 +52,7 @@ select * from db1_secret.t1;
 connection con3anon;
 
 # This should work...
-call stamp(3);
+call db1_secret.stamp(3);
 
 # ...but not this
 --error 1044
@@ -68,7 +68,7 @@ select * from t1;
 # Change to invoker's rights
 #
 alter procedure stamp sql security invoker;
---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
 show procedure status like 'stamp';
 
 # root still can
@@ -82,7 +82,7 @@ connection con2user1;
 
 # This should not work
 --error 1044
-call stamp(5);
+call db1_secret.stamp(5);
 
 #
 # Anonymous cannot
@@ -91,7 +91,7 @@ connection con3anon;
 
 # This should not work
 --error 1044
-call stamp(6);
+call db1_secret.stamp(6);
 
 
 #
@@ -148,9 +148,9 @@ select * from t2;
 
 # Clean up
 connection con1root;
-drop procedure stamp;
-drop procedure p;
-drop procedure q;
+drop procedure db1_secret.stamp;
+drop procedure db2.p;
+drop procedure db2.q;
 use test;
 drop database db1_secret;
 drop database db2;
diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test
index 3cb88ec5717..1ea3914a5c5 100644
--- a/mysql-test/t/sp.test
+++ b/mysql-test/t/sp.test
@@ -31,21 +31,6 @@ delete from t1;
 drop procedure foo42;
 
 
-# USE test: Make sure we remain in the same DB.
-create procedure u()
-  use sptmp;
-
---disable_warnings
-drop database if exists sptmp;
---enable_warnings
-create database sptmp;
-use test;
-call u();
-select database();
-drop database sptmp;
-drop procedure u;
-
-
 # Single statement, two IN params.
 create procedure bar(x char(16), y int)
   insert into test.t1 values (x, y);
@@ -1094,9 +1079,9 @@ begin
   show create function fac;
 end|
 
---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
 call bug2267_1()|
---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
 call bug2267_2()|
 call bug2267_3()|
 call bug2267_4()|
@@ -1168,11 +1153,11 @@ end|
 call ifac(20)|
 select * from fac|
 drop table fac|
---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
 show function status like '%f%'|
 drop procedure ifac|
 drop function fac|
---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
 show function status like '%f%'|
 
 
@@ -1249,7 +1234,7 @@ begin
   end while;
 end|
 show create procedure opp|
---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
 show procedure status like '%p%'|
 
 # This isn't the fastest way in the world to compute prime numbers, so
@@ -1261,7 +1246,7 @@ select * from primes where i=45 or i=100 or i=199|
 drop table primes|
 drop procedure opp|
 drop procedure ip|
---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
 show procedure status like '%p%'|
 
 
@@ -1308,13 +1293,13 @@ drop procedure fib|
 create procedure bar(x char(16), y int)
  comment "111111111111" sql security invoker
  insert into test.t1 values (x, y)|
---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
 show procedure status like 'bar'|
 alter procedure bar name bar2 comment "2222222222" sql security definer|
 alter procedure bar2 name bar comment "3333333333"|
 alter procedure bar|
 show create procedure bar|
---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
 show procedure status like 'bar'|
 drop procedure bar|
 delimiter ;|
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index e17847ebe24..21822e02d29 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -445,7 +445,8 @@ int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables,
 int quick_rm_table(enum db_type base,const char *db,
 		   const char *table_name);
 bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list);
-bool mysql_change_db(THD *thd,const char *name);
+bool mysql_change_db(THD *thd,const char *name,
+		     bool empty_is_ok=0, bool no_access_check=0);
 void mysql_parse(THD *thd,char *inBuf,uint length);
 bool is_update_query(enum enum_sql_command command);
 void free_items(Item *item);
diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt
index 117bf4b37d4..eea5912cbdf 100644
--- a/sql/share/czech/errmsg.txt
+++ b/sql/share/czech/errmsg.txt
@@ -335,3 +335,4 @@ character-set=latin2
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt
index ba012e0eea3..2c2782d4cf0 100644
--- a/sql/share/danish/errmsg.txt
+++ b/sql/share/danish/errmsg.txt
@@ -329,3 +329,4 @@ character-set=latin1
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt
index 0349929f0d3..84bd5781ffb 100644
--- a/sql/share/dutch/errmsg.txt
+++ b/sql/share/dutch/errmsg.txt
@@ -337,3 +337,4 @@ character-set=latin1
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt
index 6bf4bde9b22..2c88e1b445f 100644
--- a/sql/share/english/errmsg.txt
+++ b/sql/share/english/errmsg.txt
@@ -326,3 +326,4 @@ character-set=latin1
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt
index 03e2d4d6c90..d72d7f94309 100644
--- a/sql/share/estonian/errmsg.txt
+++ b/sql/share/estonian/errmsg.txt
@@ -331,3 +331,4 @@ character-set=latin7
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt
index dab2ab9f51d..227b5138e98 100644
--- a/sql/share/french/errmsg.txt
+++ b/sql/share/french/errmsg.txt
@@ -326,3 +326,4 @@ character-set=latin1
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt
index 5b2378dde22..d4e42ed5d58 100644
--- a/sql/share/german/errmsg.txt
+++ b/sql/share/german/errmsg.txt
@@ -338,3 +338,4 @@ character-set=latin1
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt
index a840e95de97..44e072bb20e 100644
--- a/sql/share/greek/errmsg.txt
+++ b/sql/share/greek/errmsg.txt
@@ -326,3 +326,4 @@ character-set=greek
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt
index 0b24ca6afb8..72fdf4f8c51 100644
--- a/sql/share/hungarian/errmsg.txt
+++ b/sql/share/hungarian/errmsg.txt
@@ -328,3 +328,4 @@ character-set=latin2
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt
index a298baa7682..6694f220848 100644
--- a/sql/share/italian/errmsg.txt
+++ b/sql/share/italian/errmsg.txt
@@ -326,3 +326,4 @@ character-set=latin1
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt
index a25357ae079..896146749a9 100644
--- a/sql/share/japanese/errmsg.txt
+++ b/sql/share/japanese/errmsg.txt
@@ -328,3 +328,4 @@ character-set=ujis
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt
index d3799d881ed..3a92c060c67 100644
--- a/sql/share/korean/errmsg.txt
+++ b/sql/share/korean/errmsg.txt
@@ -326,3 +326,4 @@ character-set=euckr
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt
index d69d52408ff..ada0435b52a 100644
--- a/sql/share/norwegian-ny/errmsg.txt
+++ b/sql/share/norwegian-ny/errmsg.txt
@@ -328,3 +328,4 @@ character-set=latin1
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt
index 5fbbaf19480..d0688b2858c 100644
--- a/sql/share/norwegian/errmsg.txt
+++ b/sql/share/norwegian/errmsg.txt
@@ -328,3 +328,4 @@ character-set=latin1
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt
index 92572db5fdc..d0c96146441 100644
--- a/sql/share/polish/errmsg.txt
+++ b/sql/share/polish/errmsg.txt
@@ -330,3 +330,4 @@ character-set=latin2
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt
index 580fec472b6..71be50ba262 100644
--- a/sql/share/portuguese/errmsg.txt
+++ b/sql/share/portuguese/errmsg.txt
@@ -327,3 +327,4 @@ character-set=latin1
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt
index 3e6bff75591..d800906e663 100644
--- a/sql/share/romanian/errmsg.txt
+++ b/sql/share/romanian/errmsg.txt
@@ -330,3 +330,4 @@ character-set=latin2
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt
index b04cda84efd..633e29e8c48 100644
--- a/sql/share/russian/errmsg.txt
+++ b/sql/share/russian/errmsg.txt
@@ -328,3 +328,4 @@ character-set=koi8r
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt
index 2737ce26873..efda5bb12cc 100644
--- a/sql/share/serbian/errmsg.txt
+++ b/sql/share/serbian/errmsg.txt
@@ -321,3 +321,4 @@ character-set=cp1250
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt
index bf9fe6d3519..8fa84320a8f 100644
--- a/sql/share/slovak/errmsg.txt
+++ b/sql/share/slovak/errmsg.txt
@@ -334,3 +334,4 @@ character-set=latin2
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt
index fec6bbb4342..75a6d42bec8 100644
--- a/sql/share/spanish/errmsg.txt
+++ b/sql/share/spanish/errmsg.txt
@@ -328,3 +328,4 @@ character-set=latin1
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt
index 8b2892172ba..9b743acb930 100644
--- a/sql/share/swedish/errmsg.txt
+++ b/sql/share/swedish/errmsg.txt
@@ -326,3 +326,4 @@ character-set=latin1
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt
index 393c765a8ca..2b3587e2028 100644
--- a/sql/share/ukrainian/errmsg.txt
+++ b/sql/share/ukrainian/errmsg.txt
@@ -331,3 +331,4 @@ character-set=koi8u
 "Duplicate cursor: %s"
 "Failed to ALTER %s %s"
 "Subselect value not supported"
+"USE is not allowed in a stored procedure"
diff --git a/sql/sp.cc b/sql/sp.cc
index 26f21c31fd8..20cd43c2ad3 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -23,7 +23,7 @@
 static char *
 create_string(THD *thd, ulong *lenp,
 	      int sp_type,
-	      char *name, ulong namelen,
+	      sp_name *name,
 	      const char *params, ulong paramslen,
 	      const char *returns, ulong returnslen,
 	      const char *body, ulong bodylen,
@@ -69,7 +69,11 @@ db_find_routine_aux(THD *thd, int type, sp_name *name,
 		       type, name->m_name.length, name->m_name.str));
 
   // Put the key used to read the row together
-  memset(key, (int)' ', 64);	// QQ Empty db for now
+  keylen= name->m_db.length;
+  if (keylen > 64)
+    keylen= 64;
+  memcpy(key, name->m_db.str, keylen);
+  memset(key+keylen, (int)' ', 64-keylen); // Pad with space
   keylen= name->m_name.length;
   if (keylen > 64)
     keylen= 64;
@@ -213,6 +217,8 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
     char *defstr;
     ulong deflen;
     LEX *oldlex= thd->lex;
+    char olddb[128];
+    char *olddbptr;
     enum enum_sql_command oldcmd= thd->lex->sql_command;
     ulong old_sql_mode= thd->variables.sql_mode;
     ha_rows select_limit= thd->variables.select_limit;
@@ -221,23 +227,30 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
     thd->variables.select_limit= HA_POS_ERROR;
 
     if (!(defstr= create_string(thd, &deflen,
-			  type,
-			  name->m_name.str, name->m_name.length,
-			  params, strlen(params),
-			  returns, strlen(returns),
-			  body, strlen(body),
+				type,
+				name,
+				params, strlen(params),
+				returns, strlen(returns),
+				body, strlen(body),
 				&chistics)))
     {
       ret= SP_INTERNAL_ERROR;
       goto done;
     }
 
+    olddbptr= thd->db;
+    if ((ret= sp_use_new_db(thd, name->m_db.str, olddb, sizeof(olddb), 1)))
+      goto done;
+
     lex_start(thd, (uchar*)defstr, deflen);
     if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL)
     {
       LEX *newlex= thd->lex;
       sp_head *sp= newlex->sphead;
 
+      if (olddbptr != thd->db &&
+	  (ret= sp_change_db(thd, olddb, 1)))
+	goto done;
       if (sp)
       {
 	if (oldlex != newlex)
@@ -249,6 +262,9 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
     }
     else
     {
+      if (olddbptr != thd->db &&
+	  (ret= sp_change_db(thd, olddb, 1)))
+	goto done;
       *sphp= thd->lex->sphead;
       (*sphp)->set_info((char *)definer, (uint)strlen(definer),
 			created, modified, &chistics);
@@ -259,6 +275,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
   }
 
  done:
+
   if (opened)
     close_thread_tables(thd);
   DBUG_RETURN(ret);
@@ -291,9 +308,8 @@ db_create_routine(THD *thd, int type, sp_head *sp)
       ret= SP_GET_FIELD_FAILED;
       goto done;
     }
-// QQ Not yet
-//     table->field[MYSQL_PROC_FIELD_DB]->
-//       store(sp->m_db.str, sp->m_db.length, system_charset_info);
+    table->field[MYSQL_PROC_FIELD_DB]->
+      store(sp->m_db.str, sp->m_db.length, system_charset_info);
     table->field[MYSQL_PROC_FIELD_NAME]->
       store(sp->m_name.str, sp->m_name.length, system_charset_info);
     table->field[MYSQL_PROC_FIELD_TYPE]->
@@ -402,8 +418,7 @@ struct st_used_field
 
 static struct st_used_field init_fields[]=
 {
-// QQ Not yet
-//   { "Db",       NAME_LEN, MYSQL_TYPE_STRING,    0},
+  { "Db",       NAME_LEN, MYSQL_TYPE_STRING,    0},
   { "Name",     NAME_LEN, MYSQL_TYPE_STRING,    0},
   { "Type",            9, MYSQL_TYPE_STRING,    0},
   { "Definer",        77, MYSQL_TYPE_STRING,    0},
@@ -424,14 +439,20 @@ print_field_values(THD *thd, TABLE *table,
 
   if (table->field[MYSQL_PROC_FIELD_TYPE]->val_int() == type)
   {
-    String *tmp_string= new String();
+    String db_string;
+    String name_string;
     struct st_used_field *used_field= used_fields;
 
-    get_field(&thd->mem_root, used_field->field, tmp_string);
-    if (!wild || !wild[0] || !wild_compare(tmp_string->ptr(), wild, 0))
+    if (get_field(&thd->mem_root, used_field->field, &db_string))
+      db_string.set_ascii("", 0);
+    used_field+= 1;
+    get_field(&thd->mem_root, used_field->field, &name_string);
+
+    if (!wild || !wild[0] || !wild_compare(name_string.ptr(), wild, 0))
     {
       protocol->prepare_for_resend();
-      protocol->store(tmp_string);
+      protocol->store(&db_string);
+      protocol->store(&name_string);
       for (used_field++;
 	   used_field->field_name;
 	   used_field++)
@@ -448,10 +469,10 @@ print_field_values(THD *thd, TABLE *table,
 	  break;
 	default:
 	  {
-	    String *tmp_string1= new String();
+	    String tmp_string;
 
-	    get_field(&thd->mem_root, used_field->field, tmp_string1);
-	    protocol->store(tmp_string1);
+	    get_field(&thd->mem_root, used_field->field, &tmp_string);
+	    protocol->store(&tmp_string);
 	  }
 	  break;
 	}
@@ -738,17 +759,16 @@ sp_show_status_function(THD *thd, const char *wild)
 
 
 bool
-sp_function_exists(THD *thd, LEX_STRING *name)
+sp_function_exists(THD *thd, sp_name *name)
 {
   TABLE *table;
   bool ret= FALSE;
   bool opened= FALSE;
-  sp_name n(*name);
   DBUG_ENTER("sp_function_exists");
 
-  if (sp_cache_lookup(&thd->sp_func_cache, &n) ||
+  if (sp_cache_lookup(&thd->sp_func_cache, name) ||
       db_find_routine_aux(thd, TYPE_ENUM_FUNCTION,
-			  &n, TL_READ,
+			  name, TL_READ,
 			  &table, &opened) == SP_OK)
     ret= TRUE;
   if (opened)
@@ -770,11 +790,11 @@ void
 sp_add_fun_to_lex(LEX *lex, sp_name *fun)
 {
   if (! hash_search(&lex->spfuns,
-		    (byte *)fun->m_name.str, fun->m_name.length))
+		    (byte *)fun->m_qname.str, fun->m_qname.length))
   {
     LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING));
-    ls->str= sql_strmake(fun->m_name.str, fun->m_name.length);
-    ls->length= fun->m_name.length;
+    ls->str= sql_strmake(fun->m_qname.str, fun->m_qname.length);
+    ls->length= fun->m_qname.length;
 
     my_hash_insert(&lex->spfuns, (byte *)ls);
   }
@@ -805,6 +825,7 @@ sp_cache_functions(THD *thd, LEX *lex)
     LEX_STRING *ls= (LEX_STRING *)hash_element(h, i);
     sp_name name(*ls);
 
+    name.m_qname= *ls;
     if (! sp_cache_lookup(&thd->sp_func_cache, &name))
     {
       sp_head *sp;
@@ -812,6 +833,13 @@ sp_cache_functions(THD *thd, LEX *lex)
       LEX *newlex= new st_lex;
 
       thd->lex= newlex;
+      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_ENUM_FUNCTION, &name, &sp)
 	  == SP_OK)
       {
@@ -839,7 +867,7 @@ sp_cache_functions(THD *thd, LEX *lex)
 static char *
 create_string(THD *thd, ulong *lenp,
 	      int type,
-	      char *name, ulong namelen,
+	      sp_name *name,
 	      const char *params, ulong paramslen,
 	      const char *returns, ulong returnslen,
 	      const char *body, ulong bodylen,
@@ -848,14 +876,15 @@ create_string(THD *thd, ulong *lenp,
   char *buf, *ptr;
   ulong buflen;
 
-  buflen= 100 + namelen + paramslen + returnslen + bodylen +
+  buflen= 100 + name->m_qname.length + paramslen + returnslen + bodylen +
     chistics->comment.length;
   if (!(buf= thd->alloc(buflen)))
     return 0;
 
   ptr= strxmov(buf, "CREATE ",
 	       (type == TYPE_ENUM_FUNCTION) ? "FUNCTION" : "PROCEDURE",
-	       " `", name, "`(", params, ")", NullS);
+	       " `", name->m_db.str, "`.`", name->m_name.str, "`(", params, ")",
+	       NullS);
 
   if (type == TYPE_ENUM_FUNCTION)
     ptr= strxmov(ptr, " RETURNS ", returns, NullS);
@@ -874,3 +903,63 @@ create_string(THD *thd, ulong *lenp,
   *lenp= (ptr-buf);
   return buf;
 }
+
+
+//
+// Utilities...
+//
+
+int
+sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddblen,
+	      bool no_access_check)
+{
+  bool changeit;
+  DBUG_ENTER("sp_use_new_db");
+  DBUG_PRINT("enter", ("newdb: %s", newdb));
+
+  if (thd->db && thd->db[0])
+  {
+    if (my_strcasecmp(system_charset_info, thd->db, newdb) == 0)
+      changeit= 0;
+    else
+    {
+      changeit= 1;
+      strnmov(olddb, thd->db, olddblen);
+    }
+  }
+  else
+  {				// thd->db empty
+    if (newdb[0])
+      changeit= 1;
+    else
+      changeit= 0;
+    olddb[0] = '\0';
+  }
+  if (!changeit)
+  {
+    DBUG_RETURN(0);
+  }
+  else
+  {
+    int ret= sp_change_db(thd, newdb, no_access_check);
+
+    DBUG_RETURN(ret);
+  }
+}
+
+int
+sp_change_db(THD *thd, char *db, bool no_access_check)
+{
+  int ret;
+  ulong dbaccess= thd->db_access; /* mysql_change_db() changes this */ 
+  my_bool nsok= thd->net.no_send_ok; /* mysql_change_db() does send_ok() */
+  thd->net.no_send_ok= TRUE;
+  DBUG_ENTER("sp_change_db");
+  DBUG_PRINT("enter", ("db: %s, no_access_check: %d", db, no_access_check));
+
+  ret= mysql_change_db(thd, db, 1, no_access_check);
+
+  thd->net.no_send_ok= nsok;
+  thd->db_access= dbaccess;
+  DBUG_RETURN(ret);
+}
diff --git a/sql/sp.h b/sql/sp.h
index 95632d2e654..ffe3f31c157 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -70,7 +70,7 @@ int
 sp_show_status_function(THD *thd, const char *wild);
 
 bool
-sp_function_exists(THD *thd, LEX_STRING *name);
+sp_function_exists(THD *thd, sp_name *name);
 
 
 // This is needed since we have to read the functions before we
@@ -82,4 +82,19 @@ sp_merge_funs(LEX *dst, LEX *src);
 int
 sp_cache_functions(THD *thd, LEX *lex);
 
+
+//
+// Utilities...
+//
+
+// Do a "use newdb". The current db is stored at olddb.
+// If newdb is the same as the current one, nothing is changed.
+int
+sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddbmax,
+	      bool no_access_check);
+
+// Like mysql_change_db() but handles empty db name and the  send_ok() problem.
+int
+sp_change_db(THD *thd, char *db, bool no_access_check);
+
 #endif /* _SP_H_ */
diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc
index 93f51938000..e13fb2695e7 100644
--- a/sql/sp_cache.cc
+++ b/sql/sp_cache.cc
@@ -89,7 +89,7 @@ sp_cache_lookup(sp_cache **cp, sp_name *name)
     c->version= v;
     return NULL;
   }
-  return c->lookup(name->m_name.str, name->m_name.length);
+  return c->lookup(name->m_qname.str, name->m_qname.length);
 }
 
 bool
@@ -109,7 +109,7 @@ sp_cache_remove(sp_cache **cp, sp_name *name)
     if (c->version < v)
       c->remove_all();
     else
-      found= c->remove(name->m_name.str, name->m_name.length);
+      found= c->remove(name->m_qname.str, name->m_qname.length);
     c->version= v+1;
   }
   return found;
@@ -120,7 +120,10 @@ static byte *
 hash_get_key_for_sp_head(const byte *ptr, uint *plen,
 			       my_bool first)
 {
-  return (byte*) ((sp_head*)ptr)->name(plen);
+  sp_head *sp= (sp_head *)ptr;
+
+  *plen= sp->m_qname.length;
+  return (byte*) sp->m_qname.str;
 }
 
 static void
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index eebe02c1834..ebf74e25bbe 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -147,6 +147,26 @@ sp_name::init_qname(THD *thd)
 	  m_name.length, m_name.str);
 }
 
+sp_name *
+sp_name_current_db_new(THD *thd, LEX_STRING name)
+{
+  sp_name *qname;
+
+  if (! thd->db)
+    qname= new sp_name(name);
+  else
+  {
+    LEX_STRING db;
+
+    db.length= strlen(thd->db);
+    db.str= thd->strmake(thd->db, db.length);
+    qname= new sp_name(db, name);
+  }
+  qname->init_qname(thd);
+  return qname;
+}
+
+
 /* ------------------------------------------------------------------ */
 
 
@@ -224,8 +244,8 @@ sp_head::init_strings(THD *thd, LEX *lex, sp_name *name)
   /* We have to copy strings to get them into the right memroot */
   if (name->m_db.length == 0)
   {
-    m_db.length= strlen(thd->db);
-    m_db.str= strmake_root(root, thd->db, m_db.length);
+    m_db.length= (thd->db ? strlen(thd->db) : 0);
+    m_db.str= strmake_root(root, (thd->db ? thd->db : ""), m_db.length);
   }
   else
   {
@@ -317,30 +337,22 @@ int
 sp_head::execute(THD *thd)
 {
   DBUG_ENTER("sp_head::execute");
-  char olddbname[128];
-  char *olddbptr= thd->db;
+  char olddb[128];
+  char *olddbptr;
   sp_rcontext *ctx= thd->spcont;
   int ret= 0;
   uint ip= 0;
 
 #ifndef EMBEDDED_LIBRARY
-  if (check_stack_overrun(thd, olddbptr))
+  if (check_stack_overrun(thd, olddb))
   {
     DBUG_RETURN(-1);
   }
 #endif
-  if (olddbptr)
-  {
-    uint i= 0;
-    char *p= olddbptr;
-
-    /* Fast inline strncpy without padding... */
-    while (*p && i < sizeof(olddbname))
-      olddbname[i++]= *p++;
-    if (i == sizeof(olddbname))
-      i-= 1;			// QQ Error or warning for truncate?
-    olddbname[i]= '\0';
-  }
+
+  olddbptr= thd->db;
+  if ((ret= sp_use_new_db(thd, m_db.str, olddb, sizeof(olddb), 0)))
+    goto done;
 
   if (ctx)
     ctx->clear_handler();
@@ -379,18 +391,17 @@ sp_head::execute(THD *thd)
     }
   } while (ret == 0 && !thd->killed && !thd->query_error);
 
+ done:
   DBUG_PRINT("info", ("ret=%d killed=%d query_error=%d",
 		      ret, thd->killed, thd->query_error));
   if (thd->killed || thd->query_error)
     ret= -1;
   /* If the DB has changed, the pointer has changed too, but the
      original thd->db will then have been freed */
-  if (olddbptr && olddbptr != thd->db)
+  if (olddbptr != thd->db)
   {
-    /* QQ Maybe we should issue some special error message or warning here,
-       if this fails?? */
     if (! thd->killed)
-      ret= mysql_change_db(thd, olddbname);
+      ret= sp_change_db(thd, olddb, 0);
   }
   DBUG_RETURN(ret);
 }
@@ -757,6 +768,32 @@ sp_head::set_info(char *definer, uint definerlen,
 					  m_chistics->comment.length);
 }
 
+void
+sp_head::reset_thd_mem_root(THD *thd)
+{
+  m_thd_root= thd->mem_root;
+  thd->mem_root= m_mem_root;
+  m_free_list= thd->free_list; // Keep the old list
+  thd->free_list= NULL;	// Start a new one
+  /* Copy the db, since substatements will point to it */
+  m_thd_db= thd->db;
+  thd->db= strmake_root(&thd->mem_root, thd->db, thd->db_length);
+  m_thd= thd;
+}
+
+void
+sp_head::restore_thd_mem_root(THD *thd)
+{
+  Item *flist= m_free_list;	// The old list
+  m_free_list= thd->free_list; // Get the new one
+  thd->free_list= flist;	// Restore the old one
+  thd->db= m_thd_db;		// Restore the original db pointer
+  m_mem_root= thd->mem_root;
+  thd->mem_root= m_thd_root;
+  m_thd= NULL;
+}
+
+
 int
 sp_head::show_create_procedure(THD *thd)
 {
@@ -1157,8 +1194,6 @@ sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp)
   {
     ctxp->master_access= thd->master_access;
     ctxp->db_access= thd->db_access;
-    ctxp->db= thd->db;
-    ctxp->db_length= thd->db_length;
     ctxp->priv_user= thd->priv_user;
     strncpy(ctxp->priv_host, thd->priv_host, sizeof(ctxp->priv_host));
     ctxp->user= thd->user;
@@ -1174,8 +1209,6 @@ sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp)
       ctxp->changed= FALSE;
       thd->master_access= ctxp->master_access;
       thd->db_access= ctxp->db_access;
-      thd->db= ctxp->db;
-      thd->db_length= ctxp->db_length;
       thd->priv_user= ctxp->priv_user;
       strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host));
     }
@@ -1195,8 +1228,6 @@ sp_restore_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp)
     ctxp->changed= FALSE;
     thd->master_access= ctxp->master_access;
     thd->db_access= ctxp->db_access;
-    thd->db= ctxp->db;
-    thd->db_length= ctxp->db_length;
     thd->priv_user= ctxp->priv_user;
     strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host));
   }
diff --git a/sql/sp_head.h b/sql/sp_head.h
index b35b4c37501..791c6697693 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -66,6 +66,10 @@ class sp_name : public Sql_alloc
   {}
 };
 
+sp_name *
+sp_name_current_db_new(THD *thd, LEX_STRING name);
+
+
 class sp_head : public Sql_alloc
 {
   sp_head(const sp_head &);	/* Prevent use of these */
@@ -194,24 +198,10 @@ class sp_head : public Sql_alloc
 		longlong created, longlong modified,
 		st_sp_chistics *chistics);
 
-  inline void reset_thd_mem_root(THD *thd)
-  {
-    m_thd_root= thd->mem_root;
-    thd->mem_root= m_mem_root;
-    m_free_list= thd->free_list; // Keep the old list
-    thd->free_list= NULL;	// Start a new one
-    m_thd= thd;
-  }
+  void reset_thd_mem_root(THD *thd);
+
+  void restore_thd_mem_root(THD *thd);
 
-  inline void restore_thd_mem_root(THD *thd)
-  {
-    Item *flist= m_free_list;	// The old list
-    m_free_list= thd->free_list; // Get the new one
-    thd->free_list= flist;	// Restore the old one
-    m_mem_root= thd->mem_root;
-    thd->mem_root= m_thd_root;
-    m_thd= NULL;
-  }
 
 private:
 
@@ -219,6 +209,7 @@ class sp_head : public Sql_alloc
   MEM_ROOT m_thd_root;		// Temp. store for thd's mem_root
   Item *m_free_list;		// Where the items go
   THD *m_thd;			// Set if we have reset mem_root
+  char *m_thd_db;		// Original thd->db pointer
 
   sp_pcontext *m_pcont;		// Parse context
   List<LEX> m_lex;		// Temp. store for the other lex
@@ -671,8 +662,6 @@ struct st_sp_security_context
   bool changed;
   uint master_access;
   uint db_access;
-  char *db;
-  uint db_length;
   char *priv_user;
   char priv_host[MAX_HOSTNAME];
   char *user;
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index bc6b30040d6..3ea6821ef80 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -595,7 +595,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
     1	error
 */
 
-bool mysql_change_db(THD *thd, const char *name)
+bool mysql_change_db(THD *thd, const char *name,
+		     bool empty_is_ok, bool no_access_check)
 {
   int length, db_length;
   char *dbname=my_strdup((char*) name,MYF(MY_WME));
@@ -604,62 +605,76 @@ bool mysql_change_db(THD *thd, const char *name)
   HA_CREATE_INFO create;
   DBUG_ENTER("mysql_change_db");
 
-  if (!dbname || !(db_length=strip_sp(dbname)))
+  if ((!dbname || !(db_length=strip_sp(dbname))) && !empty_is_ok)
   {
     x_free(dbname);				/* purecov: inspected */
     send_error(thd,ER_NO_DB_ERROR);	/* purecov: inspected */
     DBUG_RETURN(1);				/* purecov: inspected */
   }
-  if ((db_length > NAME_LEN) || check_db_name(dbname))
+  if (!empty_is_ok || (dbname && db_length))
   {
-    net_printf(thd, ER_WRONG_DB_NAME, dbname);
-    x_free(dbname);
-    DBUG_RETURN(1);
+    if ((db_length > NAME_LEN) || check_db_name(dbname))
+    {
+      net_printf(thd, ER_WRONG_DB_NAME, dbname);
+      x_free(dbname);
+      DBUG_RETURN(1);
+    }
   }
   DBUG_PRINT("info",("Use database: %s", dbname));
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-  if (test_all_bits(thd->master_access,DB_ACLS))
-    db_access=DB_ACLS;
-  else
-    db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) |
-		thd->master_access);
-  if (!(db_access & DB_ACLS) && (!grant_option || check_grant_db(thd,dbname)))
+  if (!empty_is_ok || (dbname && db_length))
   {
-    net_printf(thd,ER_DBACCESS_DENIED_ERROR,
-	       thd->priv_user,
-	       thd->priv_host,
-	       dbname);
-    mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
-		    thd->priv_user,
-		    thd->priv_host,
-		    dbname);
-    my_free(dbname,MYF(0));
-    DBUG_RETURN(1);
-  }
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+    if (! no_access_check)
+    {
+      if (test_all_bits(thd->master_access,DB_ACLS))
+	db_access=DB_ACLS;
+      else
+	db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) |
+		    thd->master_access);  
+      if (!(db_access & DB_ACLS) &&
+	  (!grant_option || check_grant_db(thd,dbname)))
+      {
+	net_printf(thd,ER_DBACCESS_DENIED_ERROR,
+		   thd->priv_user,
+		   thd->priv_host,
+		   dbname);
+	mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
+			thd->priv_user,
+			thd->priv_host,
+			dbname);
+	my_free(dbname,MYF(0));
+	DBUG_RETURN(1);
+      }
+    }
 #endif
-  (void) sprintf(path,"%s/%s",mysql_data_home,dbname);
-  length=unpack_dirname(path,path);		// Convert if not unix
-  if (length && path[length-1] == FN_LIBCHAR)
-    path[length-1]=0;				// remove ending '\'
-  if (access(path,F_OK))
-  {
-    net_printf(thd,ER_BAD_DB_ERROR,dbname);
-    my_free(dbname,MYF(0));
-    DBUG_RETURN(1);
+    (void) sprintf(path,"%s/%s",mysql_data_home,dbname);
+    length=unpack_dirname(path,path);		// Convert if not unix
+    if (length && path[length-1] == FN_LIBCHAR)
+      path[length-1]=0;				// remove ending '\'
+    if (access(path,F_OK))
+    {
+      net_printf(thd,ER_BAD_DB_ERROR,dbname);
+      my_free(dbname,MYF(0));
+      DBUG_RETURN(1);
+    }
   }
   send_ok(thd);
   x_free(thd->db);
   thd->db=dbname;				// THD::~THD will free this
   thd->db_length=db_length;
+  if (!empty_is_ok || (dbname && db_length))
+  {
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-  thd->db_access=db_access;
+    if (! no_access_check)
+      thd->db_access=db_access;
 #endif
-  strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE);
-  load_db_opt(thd, path, &create);
-  thd->db_charset= create.default_table_charset ?
-		   create.default_table_charset :
-		   thd->variables.collation_server;
-  thd->variables.collation_database= thd->db_charset;
+    strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE);
+    load_db_opt(thd, path, &create);
+    thd->db_charset= create.default_table_charset ?
+      create.default_table_charset :
+      thd->variables.collation_server;
+    thd->variables.collation_database= thd->db_charset;
+  }
   DBUG_RETURN(0);
 }
 
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 4224971fd47..50f475eb68c 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1090,8 +1090,15 @@ create:
 	;
 
 sp_name:
-	  IDENT_sys '.' IDENT_sys { $$= new sp_name($1, $3); }
-	| IDENT_sys               { $$= new sp_name($1); }
+	  IDENT_sys '.' IDENT_sys
+	  {
+	    $$= new sp_name($1, $3);
+	    $$->init_qname(YYTHD);
+	  }
+	| IDENT_sys
+	  {
+	    $$= sp_name_current_db_new(YYTHD, $1);
+	  }
 	;
 
 create_function_tail:
@@ -1576,6 +1583,11 @@ sp_proc_stmt:
 	      /* We maybe have one or more SELECT without INTO */
 	      lex->sphead->m_multi_results= TRUE;
 	    }
+	    if (lex->sql_command == SQLCOM_CHANGE_DB)
+	    { /* "USE db" doesn't work in a procedure */
+	      send_error(YYTHD, ER_SP_NO_USE);
+	      YYABORT;
+	    }
 	    /* Don't add an instruction for empty SET statements.
 	    ** (This happens if the SET only contained local variables,
 	    **  which get their set instructions generated separately.)
@@ -3913,10 +3925,11 @@ simple_expr:
 	  { $$= new Item_int((char*) "TRUE",1,1); }
 	| IDENT_sys '(' udf_expr_list ')'
 	  {
-	    if (sp_function_exists(YYTHD, &$1))
+	    sp_name *name= sp_name_current_db_new(YYTHD, $1);
+
+	    if (sp_function_exists(YYTHD, name))
 	    {
 	      LEX *lex= Lex;
-	      sp_name *name= new sp_name($1);
 
 	      sp_add_fun_to_lex(lex, name);
 	      if ($3)
-- 
2.30.9