diff options
-rw-r--r-- | client/mysqldump.c | 48 | ||||
-rw-r--r-- | mysql-test/r/gis.result | 2 | ||||
-rw-r--r-- | mysql-test/r/information_schema.result | 4 | ||||
-rw-r--r-- | mysql-test/r/mysqldump.result | 14 | ||||
-rw-r--r-- | mysql-test/r/rpl_ddl.result | 4 | ||||
-rw-r--r-- | mysql-test/r/rpl_sp.result | 30 | ||||
-rw-r--r-- | mysql-test/r/rpl_trigger.result | 2 | ||||
-rw-r--r-- | mysql-test/r/sp-security.result | 52 | ||||
-rw-r--r-- | mysql-test/r/sp.result | 28 | ||||
-rw-r--r-- | mysql-test/r/sql_mode.result | 8 | ||||
-rw-r--r-- | mysql-test/t/sp-security.test | 101 | ||||
-rw-r--r-- | sql/sp.cc | 55 | ||||
-rw-r--r-- | sql/sp_head.cc | 28 | ||||
-rw-r--r-- | sql/sp_head.h | 1 | ||||
-rw-r--r-- | sql/sql_lex.h | 14 | ||||
-rw-r--r-- | sql/sql_parse.cc | 84 | ||||
-rw-r--r-- | sql/sql_trigger.cc | 2 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 202 |
18 files changed, 521 insertions, 158 deletions
diff --git a/client/mysqldump.c b/client/mysqldump.c index 161197821d6..e0469e00031 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -80,6 +80,7 @@ static char *add_load_option(char *ptr, const char *object, const char *statement); static ulong find_set(TYPELIB *lib, const char *x, uint length, char **err_pos, uint *err_len); +static char *alloc_query_str(ulong size); static char *field_escape(char *to,const char *from,uint length); static my_bool verbose=0,tFlag=0,dFlag=0,quick= 1, extended_insert= 1, @@ -1304,19 +1305,64 @@ static uint dump_routines_for_db(char *db) routine_name, row[2], strlen(row[2]))); if (strlen(row[2])) { + char *query_str= NULL; + char *definer_begin; + if (opt_drop) fprintf(sql_file, "/*!50003 DROP %s IF EXISTS %s */;;\n", routine_type[i], routine_name); + + /* + Cover DEFINER-clause in version-specific comments. + + TODO: this is definitely a BAD IDEA to parse SHOW CREATE output. + We should user INFORMATION_SCHEMA instead. The only problem is + that now INFORMATION_SCHEMA does not provide information about + routine parameters. + */ + + definer_begin= strstr(row[2], " DEFINER"); + + if (definer_begin) + { + char *definer_end= strstr(definer_begin, " PROCEDURE"); + + if (!definer_end) + definer_end= strstr(definer_begin, " FUNCTION"); + + if (definer_end) + { + char *query_str_tail; + + /* + Allocate memory for new query string: original string + from SHOW statement and version-specific comments. + */ + query_str= alloc_query_str(strlen(row[2]) + 23); + + query_str_tail= strnmov(query_str, row[2], + definer_begin - row[2]); + query_str_tail= strmov(query_str_tail, "*/ /*!50019"); + query_str_tail= strnmov(query_str_tail, definer_begin, + definer_end - definer_begin); + query_str_tail= strxmov(query_str_tail, "*/ /*!50003", + definer_end, NullS); + } + } + /* we need to change sql_mode only for the CREATE PROCEDURE/FUNCTION otherwise we may need to re-quote routine_name */; fprintf(sql_file, "/*!50003 SET SESSION SQL_MODE=\"%s\"*/;;\n", row[1] /* sql_mode */); - fprintf(sql_file, "/*!50003 %s */;;\n", row[2]); + fprintf(sql_file, "/*!50003 %s */;;\n", + (query_str != NULL ? query_str : row[2])); fprintf(sql_file, "/*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/" ";;\n"); + + my_free(query_str, MYF(MY_ALLOW_ZERO_PTR)); } } /* end of routine printing */ } /* end of list of routines */ diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result index 049ba784dc9..13e2d56d83e 100644 --- a/mysql-test/r/gis.result +++ b/mysql-test/r/gis.result @@ -680,7 +680,7 @@ drop procedure if exists fn3; create function fn3 () returns point return GeomFromText("point(1 1)"); show create function fn3; Function sql_mode Create Function -fn3 CREATE FUNCTION `fn3`() RETURNS point +fn3 CREATE DEFINER=`root`@`localhost` FUNCTION `fn3`() RETURNS point return GeomFromText("point(1 1)") select astext(fn3()); astext(fn3()) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index d3576e24a44..1c845c73e4b 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -309,7 +309,7 @@ Function sql_mode Create Function sub1 show create function sub2; Function sql_mode Create Function -sub2 CREATE FUNCTION `sub2`(i int) RETURNS int(11) +sub2 CREATE DEFINER=`mysqltest_1`@`localhost` FUNCTION `sub2`(i int) RETURNS int(11) return i+1 show function status like "sub2"; Db Name Type Definer Modified Created Security_type Comment @@ -317,7 +317,7 @@ test sub2 FUNCTION mysqltest_1@localhost # # DEFINER drop function sub2; show create procedure sel2; Procedure sql_mode Create Procedure -sel2 CREATE PROCEDURE `sel2`() +sel2 CREATE DEFINER=`root`@`localhost` PROCEDURE `sel2`() begin select * from t1; select * from t2; diff --git a/mysql-test/r/mysqldump.result b/mysql-test/r/mysqldump.result index d156e711167..e6ac9fbc740 100644 --- a/mysql-test/r/mysqldump.result +++ b/mysql-test/r/mysqldump.result @@ -2210,12 +2210,12 @@ UNLOCK TABLES; DELIMITER ;; /*!50003 DROP FUNCTION IF EXISTS `bug9056_func1` */;; /*!50003 SET SESSION SQL_MODE=""*/;; -/*!50003 CREATE FUNCTION `bug9056_func1`(a INT, b INT) RETURNS int(11) +/*!50003 CREATE*/ /*!50019 DEFINER=`root`@`localhost`*/ /*!50003 FUNCTION `bug9056_func1`(a INT, b INT) RETURNS int(11) RETURN a+b */;; /*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/;; /*!50003 DROP FUNCTION IF EXISTS `bug9056_func2` */;; /*!50003 SET SESSION SQL_MODE=""*/;; -/*!50003 CREATE FUNCTION `bug9056_func2`(f1 char binary) RETURNS char(1) +/*!50003 CREATE*/ /*!50019 DEFINER=`root`@`localhost`*/ /*!50003 FUNCTION `bug9056_func2`(f1 char binary) RETURNS char(1) begin set f1= concat( 'hello', f1 ); return f1; @@ -2223,17 +2223,17 @@ end */;; /*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/;; /*!50003 DROP PROCEDURE IF EXISTS `a'b` */;; /*!50003 SET SESSION SQL_MODE="REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ANSI"*/;; -/*!50003 CREATE PROCEDURE "a'b"() +/*!50003 CREATE*/ /*!50019 DEFINER="root"@"localhost"*/ /*!50003 PROCEDURE "a'b"() select 1 */;; /*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/;; /*!50003 DROP PROCEDURE IF EXISTS `bug9056_proc1` */;; /*!50003 SET SESSION SQL_MODE=""*/;; -/*!50003 CREATE PROCEDURE `bug9056_proc1`(IN a INT, IN b INT, OUT c INT) +/*!50003 CREATE*/ /*!50019 DEFINER=`root`@`localhost`*/ /*!50003 PROCEDURE `bug9056_proc1`(IN a INT, IN b INT, OUT c INT) BEGIN SELECT a+b INTO c; end */;; /*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/;; /*!50003 DROP PROCEDURE IF EXISTS `bug9056_proc2` */;; /*!50003 SET SESSION SQL_MODE=""*/;; -/*!50003 CREATE PROCEDURE `bug9056_proc2`(OUT a INT) +/*!50003 CREATE*/ /*!50019 DEFINER=`root`@`localhost`*/ /*!50003 PROCEDURE `bug9056_proc2`(OUT a INT) BEGIN select sum(id) from t1 into a; END */;; @@ -2685,11 +2685,11 @@ return 42 */| select 42 */| show create function f; Function sql_mode Create Function -f CREATE FUNCTION `f`() RETURNS bigint(20) +f CREATE DEFINER=`root`@`localhost` FUNCTION `f`() RETURNS bigint(20) return 42 show create procedure p; Procedure sql_mode Create Procedure -p CREATE PROCEDURE `p`() +p CREATE DEFINER=`root`@`localhost` PROCEDURE `p`() select 42 drop function f; drop procedure p; diff --git a/mysql-test/r/rpl_ddl.result b/mysql-test/r/rpl_ddl.result index c56c9f20cf8..92e91b31459 100644 --- a/mysql-test/r/rpl_ddl.result +++ b/mysql-test/r/rpl_ddl.result @@ -1133,7 +1133,7 @@ SHOW PROCEDURE STATUS LIKE 'p1'; Db mysqltest1 Name p1 Type PROCEDURE -Definer @ +Definer root@localhost Modified # Created # Security_type DEFINER @@ -1199,7 +1199,7 @@ SHOW PROCEDURE STATUS LIKE 'p1'; Db mysqltest1 Name p1 Type PROCEDURE -Definer @ +Definer root@localhost Modified # Created # Security_type DEFINER diff --git a/mysql-test/r/rpl_sp.result b/mysql-test/r/rpl_sp.result index a42c33ce333..5dfda16c763 100644 --- a/mysql-test/r/rpl_sp.result +++ b/mysql-test/r/rpl_sp.result @@ -31,7 +31,7 @@ declare b int; set b = 8; insert into t1 values (b); insert into t1 values (unix_timestamp()); -end @ # # +end root@localhost # # set timestamp=1000000000; call foo(); select * from t1; @@ -118,7 +118,7 @@ select * from mysql.proc where name="foo4" and db='mysqltest1'; db name type specific_name language sql_data_access is_deterministic security_type param_list returns body definer created modified sql_mode comment mysqltest1 foo4 PROCEDURE foo4 SQL CONTAINS_SQL YES DEFINER begin insert into t2 values(20),(20); -end @ # # +end root@localhost # # drop procedure foo4; select * from mysql.proc where name="foo4" and db='mysqltest1'; db name type specific_name language sql_data_access is_deterministic security_type param_list returns body definer created modified sql_mode comment @@ -223,13 +223,13 @@ select * from mysql.proc where db='mysqltest1'; db name type specific_name language sql_data_access is_deterministic security_type param_list returns body definer created modified sql_mode comment mysqltest1 fn1 FUNCTION fn1 SQL NO_SQL NO DEFINER int(11) begin return unix_timestamp(); -end @ # # +end root@localhost # # mysqltest1 fn2 FUNCTION fn2 SQL NO_SQL NO DEFINER int(11) begin return unix_timestamp(); -end @ # # +end zedjzlcsjhd@localhost # # mysqltest1 fn3 FUNCTION fn3 SQL READS_SQL_DATA NO DEFINER int(11) begin return 0; -end @ # # +end root@localhost # # delete from t2; alter table t2 add unique (a); drop function fn1; @@ -274,7 +274,7 @@ Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # drop database if exists mysqltest1 master-bin.000001 # Query 1 # create database mysqltest1 master-bin.000001 # Query 1 # use `mysqltest1`; create table t1 (a varchar(100)) -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo() begin declare b int; set b = 8; @@ -284,19 +284,19 @@ end master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values ( NAME_CONST('b',8)) master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values (unix_timestamp()) master-bin.000001 # Query 1 # use `mysqltest1`; delete from t1 -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo2() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo2() select * from mysqltest1.t1 master-bin.000001 # Query 1 # use `mysqltest1`; alter procedure foo2 contains sql master-bin.000001 # Query 1 # use `mysqltest1`; drop table t1 master-bin.000001 # Query 1 # use `mysqltest1`; create table t1 (a int) master-bin.000001 # Query 1 # use `mysqltest1`; create table t2 like t1 -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo3() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo3() deterministic insert into t1 values (15) master-bin.000001 # Query 1 # use `mysqltest1`; grant CREATE ROUTINE, EXECUTE on mysqltest1.* to "zedjzlcsjhd"@127.0.0.1 master-bin.000001 # Query 1 # use `mysqltest1`; grant SELECT on mysqltest1.t1 to "zedjzlcsjhd"@127.0.0.1 master-bin.000001 # Query 1 # use `mysqltest1`; grant SELECT, INSERT on mysqltest1.t2 to "zedjzlcsjhd"@127.0.0.1 -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo4() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`zedjzlcsjhd`@`127.0.0.1` procedure foo4() deterministic begin insert into t2 values(3); @@ -311,7 +311,7 @@ master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values (5) master-bin.000001 # Query 1 # use `mysqltest1`; delete from t2 master-bin.000001 # Query 1 # use `mysqltest1`; alter table t2 add unique (a) master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo4 -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo4() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo4() deterministic begin insert into t2 values(20),(20); @@ -321,7 +321,7 @@ master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo4 master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo2 master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo3 -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1(x int) +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function fn1(x int) returns int deterministic begin @@ -332,7 +332,7 @@ master-bin.000001 # Query 1 # use `mysqltest1`; delete t1,t2 from t1,t2 master-bin.000001 # Query 1 # use `mysqltest1`; SELECT `fn1`(20) master-bin.000001 # Query 1 # use `mysqltest1`; insert into t2 values(fn1(21)) master-bin.000001 # Query 1 # use `mysqltest1`; drop function fn1 -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function fn1() returns int no sql begin @@ -340,13 +340,13 @@ return unix_timestamp(); end master-bin.000001 # Query 1 # use `mysqltest1`; delete from t1 master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(fn1()) -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn2() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`zedjzlcsjhd`@`127.0.0.1` function fn2() returns int no sql begin return unix_timestamp(); end -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn3() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function fn3() returns int not deterministic reads sql data @@ -356,7 +356,7 @@ end master-bin.000001 # Query 1 # use `mysqltest1`; delete from t2 master-bin.000001 # Query 1 # use `mysqltest1`; alter table t2 add unique (a) master-bin.000001 # Query 1 # use `mysqltest1`; drop function fn1 -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1(x int) +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function fn1(x int) returns int begin insert into t2 values(x),(x); diff --git a/mysql-test/r/rpl_trigger.result b/mysql-test/r/rpl_trigger.result index 185862097bc..b9b20b82acd 100644 --- a/mysql-test/r/rpl_trigger.result +++ b/mysql-test/r/rpl_trigger.result @@ -102,7 +102,7 @@ t1_first root@localhost SELECT routine_name, definer FROM information_schema.routines; routine_name definer -bug12480 @ +bug12480 root@localhost SELECT trigger_name, definer FROM information_schema.triggers; trigger_name definer diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index d8abc387677..90466bfcfc4 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -323,3 +323,55 @@ Warning 1287 'SHOW INNODB STATUS' is deprecated; use 'SHOW ENGINE INNODB STATUS' GRANT EXECUTE ON PROCEDURE p1 TO user_bug7787@localhost; DROP DATABASE db_bug7787; use test; + +---> connection: root +DROP DATABASE IF EXISTS mysqltest; +CREATE DATABASE mysqltest; +CREATE USER mysqltest_1@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_1@localhost; +CREATE USER mysqltest_2@localhost; +GRANT SUPER ON *.* TO mysqltest_2@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_2@localhost; + +---> connection: mysqltest_2_con +use mysqltest; +CREATE PROCEDURE wl2897_p1() SELECT 1; +CREATE FUNCTION wl2897_f1() RETURNS INT RETURN 1; + +---> connection: mysqltest_1_con +use mysqltest; +CREATE DEFINER=root@localhost PROCEDURE wl2897_p2() SELECT 2; +ERROR 42000: Access denied; you need the SUPER privilege for this operation +CREATE DEFINER=root@localhost FUNCTION wl2897_f2() RETURNS INT RETURN 2; +ERROR 42000: Access denied; you need the SUPER privilege for this operation + +---> connection: mysqltest_2_con +use mysqltest; +CREATE DEFINER='a @ b @ c'@localhost PROCEDURE wl2897_p3() SELECT 3; +Warnings: +Note 1449 There is no 'a @ b @ c'@'localhost' registered +CREATE DEFINER='a @ b @ c'@localhost FUNCTION wl2897_f3() RETURNS INT RETURN 3; +Warnings: +Note 1449 There is no 'a @ b @ c'@'localhost' registered + +---> connection: con1root +use mysqltest; +SHOW CREATE PROCEDURE wl2897_p1; +Procedure sql_mode Create Procedure +wl2897_p1 CREATE DEFINER=`mysqltest_2`@`localhost` PROCEDURE `wl2897_p1`() +SELECT 1 +SHOW CREATE PROCEDURE wl2897_p3; +Procedure sql_mode Create Procedure +wl2897_p3 CREATE DEFINER=`a @ b @ c`@`localhost` PROCEDURE `wl2897_p3`() +SELECT 3 +SHOW CREATE FUNCTION wl2897_f1; +Function sql_mode Create Function +wl2897_f1 CREATE DEFINER=`mysqltest_2`@`localhost` FUNCTION `wl2897_f1`() RETURNS int(11) +RETURN 1 +SHOW CREATE FUNCTION wl2897_f3; +Function sql_mode Create Function +wl2897_f3 CREATE DEFINER=`a @ b @ c`@`localhost` FUNCTION `wl2897_f3`() RETURNS int(11) +RETURN 3 +DROP USER mysqltest_1@localhost; +DROP USER mysqltest_2@localhost; +DROP DATABASE mysqltest; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index a4c920f8e15..3e7c51e9394 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -788,7 +788,7 @@ comment 'Characteristics procedure test' insert into t1 values ("chistics", 1)| show create procedure chistics| Procedure sql_mode Create Procedure -chistics CREATE PROCEDURE `chistics`() +chistics CREATE DEFINER=`root`@`localhost` PROCEDURE `chistics`() MODIFIES SQL DATA COMMENT 'Characteristics procedure test' insert into t1 values ("chistics", 1) @@ -800,7 +800,7 @@ delete from t1| alter procedure chistics sql security invoker| show create procedure chistics| Procedure sql_mode Create Procedure -chistics CREATE PROCEDURE `chistics`() +chistics CREATE DEFINER=`root`@`localhost` PROCEDURE `chistics`() MODIFIES SQL DATA SQL SECURITY INVOKER COMMENT 'Characteristics procedure test' @@ -815,7 +815,7 @@ comment 'Characteristics procedure test' return 42| show create function chistics| Function sql_mode Create Function -chistics CREATE FUNCTION `chistics`() RETURNS int(11) +chistics CREATE DEFINER=`root`@`localhost` FUNCTION `chistics`() RETURNS int(11) DETERMINISTIC SQL SECURITY INVOKER COMMENT 'Characteristics procedure test' @@ -828,7 +828,7 @@ no sql comment 'Characteristics function test'| show create function chistics| Function sql_mode Create Function -chistics CREATE FUNCTION `chistics`() RETURNS int(11) +chistics CREATE DEFINER=`root`@`localhost` FUNCTION `chistics`() RETURNS int(11) NO SQL DETERMINISTIC SQL SECURITY INVOKER @@ -1287,7 +1287,7 @@ end while; end| show create procedure opp| Procedure sql_mode Create Procedure -opp CREATE PROCEDURE `opp`(n bigint unsigned, out pp bool) +opp CREATE DEFINER=`root`@`localhost` PROCEDURE `opp`(n bigint unsigned, out pp bool) begin declare r double; declare b, s bigint unsigned default 0; @@ -1386,7 +1386,7 @@ alter procedure bar comment "3333333333"| alter procedure bar| show create procedure bar| Procedure sql_mode Create Procedure -bar CREATE PROCEDURE `bar`(x char(16), y int) +bar CREATE DEFINER=`root`@`localhost` PROCEDURE `bar`(x char(16), y int) COMMENT '3333333333' insert into test.t1 values (x, y) show procedure status like 'bar'| @@ -1966,13 +1966,13 @@ Db Name Type Definer Modified Created Security_type Comment test bug2267_4 FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER call bug2267_3()| Procedure sql_mode Create Procedure -bug2267_1 CREATE PROCEDURE `bug2267_1`() +bug2267_1 CREATE DEFINER=`root`@`localhost` PROCEDURE `bug2267_1`() begin show procedure status; end call bug2267_4()| Function sql_mode Create Function -bug2267_4 CREATE FUNCTION `bug2267_4`() RETURNS int(11) +bug2267_4 CREATE DEFINER=`root`@`localhost` FUNCTION `bug2267_4`() RETURNS int(11) return 100 drop procedure bug2267_1| drop procedure bug2267_2| @@ -2333,20 +2333,20 @@ return x || y$ set @@sql_mode = ''| show create procedure bug2564_1| Procedure sql_mode Create Procedure -bug2564_1 CREATE PROCEDURE `bug2564_1`() +bug2564_1 CREATE DEFINER=`root`@`localhost` PROCEDURE `bug2564_1`() COMMENT 'Joe''s procedure' insert into `t1` values ("foo", 1) show create procedure bug2564_2| Procedure sql_mode Create Procedure -bug2564_2 ANSI_QUOTES CREATE PROCEDURE "bug2564_2"() +bug2564_2 ANSI_QUOTES CREATE DEFINER="root"@"localhost" PROCEDURE "bug2564_2"() insert into "t1" values ('foo', 1) show create function bug2564_3| Function sql_mode Create Function -bug2564_3 CREATE FUNCTION `bug2564_3`(x int, y int) RETURNS int(11) +bug2564_3 CREATE DEFINER=`root`@`localhost` FUNCTION `bug2564_3`(x int, y int) RETURNS int(11) return x || y show create function bug2564_4| Function sql_mode Create Function -bug2564_4 REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ANSI CREATE FUNCTION "bug2564_4"(x int, y int) RETURNS int(11) +bug2564_4 REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ANSI CREATE DEFINER="root"@"localhost" FUNCTION "bug2564_4"(x int, y int) RETURNS int(11) return x || y drop procedure bug2564_1| drop procedure bug2564_2| @@ -4056,7 +4056,7 @@ return 42; end */;; show create function bug14723;; Function sql_mode Create Function -bug14723 CREATE FUNCTION `bug14723`() RETURNS bigint(20) +bug14723 CREATE DEFINER=`root`@`localhost` FUNCTION `bug14723`() RETURNS bigint(20) main_loop: begin return 42; end @@ -4069,7 +4069,7 @@ select 42; end */;; show create procedure bug14723;; Procedure sql_mode Create Procedure -bug14723 CREATE PROCEDURE `bug14723`() +bug14723 CREATE DEFINER=`root`@`localhost` PROCEDURE `bug14723`() main_loop: begin select 42; end diff --git a/mysql-test/r/sql_mode.result b/mysql-test/r/sql_mode.result index 66df424919b..b05680f9c54 100644 --- a/mysql-test/r/sql_mode.result +++ b/mysql-test/r/sql_mode.result @@ -445,23 +445,23 @@ SET @@SQL_MODE=''; create function `foo` () returns int return 5; show create function `foo`; Function sql_mode Create Function -foo CREATE FUNCTION `foo`() RETURNS int(11) +foo CREATE DEFINER=`root`@`localhost` FUNCTION `foo`() RETURNS int(11) return 5 SET @@SQL_MODE='ANSI_QUOTES'; show create function `foo`; Function sql_mode Create Function -foo CREATE FUNCTION `foo`() RETURNS int(11) +foo CREATE DEFINER=`root`@`localhost` FUNCTION `foo`() RETURNS int(11) return 5 drop function `foo`; create function `foo` () returns int return 5; show create function `foo`; Function sql_mode Create Function -foo ANSI_QUOTES CREATE FUNCTION "foo"() RETURNS int(11) +foo ANSI_QUOTES CREATE DEFINER="root"@"localhost" FUNCTION "foo"() RETURNS int(11) return 5 SET @@SQL_MODE=''; show create function `foo`; Function sql_mode Create Function -foo ANSI_QUOTES CREATE FUNCTION "foo"() RETURNS int(11) +foo ANSI_QUOTES CREATE DEFINER="root"@"localhost" FUNCTION "foo"() RETURNS int(11) return 5 drop function `foo`; SET @@SQL_MODE=''; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index 19f94a32d9c..b466d2125d4 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -547,4 +547,105 @@ GRANT EXECUTE ON PROCEDURE p1 TO user_bug7787@localhost; DROP DATABASE db_bug7787; use test; + +# +# WL#2897: Complete definer support in the stored routines. +# +# The following cases are tested: +# 1. check that if DEFINER-clause is not explicitly specified, stored routines +# are created with CURRENT_USER privileges; +# 2. check that if DEFINER-clause specifies non-current user, SUPER privilege +# is required to create a stored routine; +# 3. check that if DEFINER-clause specifies non-existent user, a warning is +# emitted. +# 4. check that SHOW CREATE PROCEDURE | FUNCTION works correctly; +# +# The following cases are tested in other test suites: +# - check that mysqldump dumps new attribute correctly; +# - check that slave replicates CREATE-statements with explicitly specified +# DEFINER correctly. +# + +# Setup the environment. + +--echo +--echo ---> connection: root +--connection con1root + +--disable_warnings +DROP DATABASE IF EXISTS mysqltest; +--enable_warnings + +CREATE DATABASE mysqltest; + +CREATE USER mysqltest_1@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_1@localhost; + +CREATE USER mysqltest_2@localhost; +GRANT SUPER ON *.* TO mysqltest_2@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_2@localhost; + +--connect (mysqltest_2_con,localhost,mysqltest_2,,mysqltest) +--connect (mysqltest_1_con,localhost,mysqltest_1,,mysqltest) + +# test case (1). + +--echo +--echo ---> connection: mysqltest_2_con +--connection mysqltest_2_con + +use mysqltest; + +CREATE PROCEDURE wl2897_p1() SELECT 1; + +CREATE FUNCTION wl2897_f1() RETURNS INT RETURN 1; + +# test case (2). + +--echo +--echo ---> connection: mysqltest_1_con +--connection mysqltest_1_con + +use mysqltest; + +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +CREATE DEFINER=root@localhost PROCEDURE wl2897_p2() SELECT 2; + +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +CREATE DEFINER=root@localhost FUNCTION wl2897_f2() RETURNS INT RETURN 2; + +# test case (3). + +--echo +--echo ---> connection: mysqltest_2_con +--connection mysqltest_2_con + +use mysqltest; + +CREATE DEFINER='a @ b @ c'@localhost PROCEDURE wl2897_p3() SELECT 3; + +CREATE DEFINER='a @ b @ c'@localhost FUNCTION wl2897_f3() RETURNS INT RETURN 3; + +# test case (4). + +--echo +--echo ---> connection: con1root +--connection con1root + +use mysqltest; + +SHOW CREATE PROCEDURE wl2897_p1; +SHOW CREATE PROCEDURE wl2897_p3; + +SHOW CREATE FUNCTION wl2897_f1; +SHOW CREATE FUNCTION wl2897_f3; + +# Cleanup. + +DROP USER mysqltest_1@localhost; +DROP USER mysqltest_2@localhost; + +DROP DATABASE mysqltest; + + # End of 5.0 bugs. diff --git a/sql/sp.cc b/sql/sp.cc index ce0282bf810..e4489af1fdd 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -21,6 +21,8 @@ #include "sp_cache.h" #include "sql_trigger.h" +#include <my_user.h> + static bool create_string(THD *thd, String *buf, int sp_type, @@ -28,7 +30,9 @@ create_string(THD *thd, String *buf, const char *params, ulong paramslen, const char *returns, ulong returnslen, const char *body, ulong bodylen, - st_sp_chistics *chistics); + st_sp_chistics *chistics, + const LEX_STRING *definer_user, + const LEX_STRING *definer_host); static int db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ulong sql_mode, const char *params, const char *returns, @@ -406,6 +410,15 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ulong old_sql_mode= thd->variables.sql_mode; ha_rows old_select_limit= thd->variables.select_limit; sp_rcontext *old_spcont= thd->spcont; + + char definer_user_name_holder[USERNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT definer_user_name(definer_user_name_holder, + USERNAME_LENGTH); + + char definer_host_name_holder[HOSTNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT definer_host_name(definer_host_name_holder, + HOSTNAME_LENGTH); + int ret; thd->variables.sql_mode= sql_mode; @@ -414,14 +427,25 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, thd->lex= &newlex; newlex.current_select= NULL; + parse_user(definer, strlen(definer), + definer_user_name.str, &definer_user_name.length, + definer_host_name.str, &definer_host_name.length); + defstr.set_charset(system_charset_info); + + /* + We have to add DEFINER clause and provide proper routine characterstics in + routine definition statement that we build here to be able to use this + definition for SHOW CREATE PROCEDURE later. + */ + if (!create_string(thd, &defstr, type, name, params, strlen(params), returns, strlen(returns), body, strlen(body), - &chistics)) + &chistics, &definer_user_name, &definer_host_name)) { ret= SP_INTERNAL_ERROR; goto end; @@ -449,7 +473,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, if (dbchanged && (ret= mysql_change_db(thd, olddb, 1))) goto end; *sphp= newlex.sphead; - (*sphp)->set_definer((char*) definer, (uint) strlen(definer)); + (*sphp)->set_definer(&definer_user_name, &definer_host_name); (*sphp)->set_info(created, modified, &chistics, sql_mode); (*sphp)->optimize(); } @@ -500,8 +524,10 @@ db_create_routine(THD *thd, int type, sp_head *sp) else { restore_record(table, s->default_values); // Get default values for fields - strxmov(definer, thd->security_ctx->priv_user, "@", - thd->security_ctx->priv_host, NullS); + + /* NOTE: all needed privilege checks have been already done. */ + strxmov(definer, thd->lex->definer->user.str, "@", + thd->lex->definer->host.str, NullS); if (table->s->fields != MYSQL_PROC_FIELD_COUNT) { @@ -592,8 +618,17 @@ db_create_routine(THD *thd, int type, sp_head *sp) else if (mysql_bin_log.is_open()) { thd->clear_error(); + + String log_query; + log_query.set_charset(system_charset_info); + log_query.append(STRING_WITH_LEN("CREATE ")); + append_definer(thd, &log_query, &thd->lex->definer->user, + &thd->lex->definer->host); + log_query.append(thd->lex->stmt_definition_begin); + /* Such a statement can always go directly to binlog, no trans cache */ - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); + Query_log_event qinfo(thd, log_query.c_ptr(), log_query.length(), 0, + FALSE); mysql_bin_log.write(&qinfo); } @@ -1723,14 +1758,18 @@ create_string(THD *thd, String *buf, const char *params, ulong paramslen, const char *returns, ulong returnslen, const char *body, ulong bodylen, - st_sp_chistics *chistics) + st_sp_chistics *chistics, + const LEX_STRING *definer_user, + const LEX_STRING *definer_host) { /* Make some room to begin with */ if (buf->alloc(100 + name->m_qname.length + paramslen + returnslen + bodylen + - chistics->comment.length)) + chistics->comment.length + 10 /* length of " DEFINER= "*/ + + USER_HOST_BUFF_SIZE)) return FALSE; buf->append(STRING_WITH_LEN("CREATE ")); + append_definer(thd, buf, definer_user, definer_host); if (type == TYPE_ENUM_FUNCTION) buf->append(STRING_WITH_LEN("FUNCTION ")); else diff --git a/sql/sp_head.cc b/sql/sp_head.cc index f7572a374f1..b8b7ee2f78b 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1810,19 +1810,27 @@ sp_head::set_info(longlong created, longlong modified, void sp_head::set_definer(const char *definer, uint definerlen) { - uint user_name_len; - char user_name_str[USERNAME_LENGTH + 1]; - uint host_name_len; - char host_name_str[HOSTNAME_LENGTH + 1]; + char user_name_holder[USERNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT user_name(user_name_holder, USERNAME_LENGTH); - parse_user(definer, definerlen, user_name_str, &user_name_len, - host_name_str, &host_name_len); + char host_name_holder[HOSTNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT host_name(host_name_holder, HOSTNAME_LENGTH); - m_definer_user.str= strmake_root(mem_root, user_name_str, user_name_len); - m_definer_user.length= user_name_len; + parse_user(definer, definerlen, user_name.str, &user_name.length, + host_name.str, &host_name.length); - m_definer_host.str= strmake_root(mem_root, host_name_str, host_name_len); - m_definer_host.length= host_name_len; + set_definer(&user_name, &host_name); +} + + +void +sp_head::set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name) +{ + m_definer_user.str= strmake_root(mem_root, user_name->str, user_name->length); + m_definer_user.length= user_name->length; + + m_definer_host.str= strmake_root(mem_root, host_name->str, host_name->length); + m_definer_host.length= host_name->length; } diff --git a/sql/sp_head.h b/sql/sp_head.h index a4dd68ee4a3..64d9167fb17 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -302,6 +302,7 @@ public: st_sp_chistics *chistics, ulong sql_mode); void set_definer(const char *definer, uint definerlen); + void set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name); void reset_thd_mem_root(THD *thd); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 7b2ea359fb2..8db059ae2fa 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -909,12 +909,16 @@ typedef struct st_lex SQL_LIST trg_table_fields; /* - trigger_definition_begin points to the beginning of the word "TRIGGER" in - CREATE TRIGGER statement. This is used to add possibly omitted DEFINER - clause to the trigger definition statement before dumping it to the - binlog. + stmt_definition_begin is intended to point to the next word after + DEFINER-clause in the following statements: + - CREATE TRIGGER (points to "TRIGGER"); + - CREATE PROCEDURE (points to "PROCEDURE"); + - CREATE FUNCTION (points to "FUNCTION" or "AGGREGATE"); + + This pointer is required to add possibly omitted DEFINER-clause to the + DDL-statement before dumping it to the binlog. */ - const char *trigger_definition_begin; + const char *stmt_definition_begin; /* If non-0 then indicates that query requires prelocking and points to diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 13d814bdf42..527a6a67811 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4146,6 +4146,90 @@ end_with_restore_list: #endif /* + If the definer is not specified, this means that CREATE-statement missed + DEFINER-clause. DEFINER-clause can be missed in two cases: + + - The user submitted a statement w/o the clause. This is a normal + case, we should assign CURRENT_USER as definer. + + - Our slave received an updated from the master, that does not + replicate definer for stored rountines. We should also assign + CURRENT_USER as definer here, but also we should mark this routine + as NON-SUID. This is essential for the sake of backward + compatibility. + + The problem is the slave thread is running under "special" user (@), + that actually does not exist. In the older versions we do not fail + execution of a stored routine if its definer does not exist and + continue the execution under the authorization of the invoker + (BUG#13198). And now if we try to switch to slave-current-user (@), + we will fail. + + Actually, this leads to the inconsistent state of master and + slave (different definers, different SUID behaviour), but it seems, + this is the best we can do. + */ + + if (!lex->definer) + { + bool res= FALSE; + Query_arena original_arena; + Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena); + + if (!(lex->definer= create_default_definer(thd))) + res= TRUE; + + if (ps_arena) + thd->restore_active_arena(ps_arena, &original_arena); + + if (res) + { + /* Error has been already reported. */ + delete lex->sphead; + lex->sphead= 0; + goto error; + } + + if (thd->slave_thread) + lex->sphead->m_chistics->suid= SP_IS_NOT_SUID; + } + + /* + If the specified definer differs from the current user, we should check + that the current user has SUPER privilege (in order to create a stored + routine under another user one must have SUPER privilege). + */ + + else if (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) || + my_strcasecmp(system_charset_info, + lex->definer->host.str, + thd->security_ctx->priv_host)) + { + if (check_global_access(thd, SUPER_ACL)) + { + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); + delete lex->sphead; + lex->sphead= 0; + goto error; + } + } + + /* Check that the specified definer exists. Emit a warning if not. */ + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (!is_acl_user(lex->definer->host.str, + lex->definer->user.str)) + { + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_NO_SUCH_USER, + ER(ER_NO_SUCH_USER), + lex->definer->user.str, + lex->definer->host.str); + } +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ + + /* We need to copy name and db in order to use them for check_routine_access which is called after lex->sphead has been deleted. diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 9ccc70d8ccd..1fe5bf63756 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -275,7 +275,7 @@ end: append_definer(thd, &log_query, &definer_user, &definer_host); } - log_query.append(thd->lex->trigger_definition_begin); + log_query.append(thd->lex->stmt_definition_begin); } /* Such a statement can always go directly to binlog, no trans cache. */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 7e93b9c8e73..36a684f1d4a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -833,8 +833,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa load_data opt_field_or_var_spec fields_or_vars opt_load_data_set_spec definer view_replace_or_algorithm view_replace view_algorithm_opt - view_algorithm view_or_trigger_tail view_suid view_tail view_list_opt - view_list view_select view_check_option trigger_tail + view_algorithm view_or_trigger_or_sp view_or_trigger_or_sp_tail + view_suid view_tail view_list_opt view_list view_select + view_check_option trigger_tail sp_tail END_OF_INPUT %type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt @@ -1189,81 +1190,13 @@ create: lex->name=$4.str; lex->create_info.options=$3; } - | CREATE udf_func_type FUNCTION_SYM sp_name - { - LEX *lex=Lex; - lex->spname= $4; - lex->udf.type= $2; - } - create_function_tail - {} - | CREATE PROCEDURE sp_name - { - LEX *lex= Lex; - sp_head *sp; - - if (lex->sphead) - { - my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); - YYABORT; - } - /* Order is important here: new - reset - init */ - sp= new sp_head(); - sp->reset_thd_mem_root(YYTHD); - sp->init(lex); - - sp->m_type= TYPE_ENUM_PROCEDURE; - lex->sphead= sp; - /* - * We have to turn of CLIENT_MULTI_QUERIES while parsing a - * stored procedure, otherwise yylex will chop it into pieces - * at each ';'. - */ - sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; - YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); - } - '(' - { - LEX *lex= Lex; - - lex->sphead->m_param_begin= lex->tok_start+1; - } - sp_pdparam_list - ')' - { - LEX *lex= Lex; - - lex->sphead->m_param_end= lex->tok_start; - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); - } - sp_c_chistics - { - LEX *lex= Lex; - - lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lex->tok_start; - } - sp_proc_stmt - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - - if (sp->check_backpatch(YYTHD)) - YYABORT; - sp->init_strings(YYTHD, lex, $3); - lex->sql_command= SQLCOM_CREATE_PROCEDURE; - /* Restore flag if it was cleared above */ - if (sp->m_old_cmq) - YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; - sp->restore_thd_mem_root(YYTHD); - } | CREATE { Lex->create_view_mode= VIEW_CREATE_NEW; Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; Lex->create_view_suid= TRUE; } - view_or_trigger + view_or_trigger_or_sp {} | CREATE USER clear_privileges grant_list { @@ -8959,6 +8892,34 @@ subselect_end: lex->nest_level--; }; +/************************************************************************** + + CREATE VIEW | TRIGGER | PROCEDURE statements. + +**************************************************************************/ + +view_or_trigger_or_sp: + definer view_or_trigger_or_sp_tail + {} + | view_replace_or_algorithm definer view_tail + {} + ; + +view_or_trigger_or_sp_tail: + view_tail + {} + | trigger_tail + {} + | sp_tail + {} + ; + +/************************************************************************** + + DEFINER clause support. + +**************************************************************************/ + definer: /* empty */ { @@ -8985,7 +8946,7 @@ definer: /************************************************************************** - CREATE VIEW statement options. + CREATE VIEW statement parts. **************************************************************************/ @@ -9019,20 +8980,6 @@ view_algorithm_opt: {} ; -view_or_trigger: - definer view_or_trigger_tail - {} - | view_replace_or_algorithm definer view_tail - {} - ; - -view_or_trigger_tail: - view_tail - {} - | trigger_tail - {} - ; - view_suid: /* empty */ { Lex->create_view_suid= TRUE; } @@ -9131,7 +9078,7 @@ trigger_tail: sp->reset_thd_mem_root(YYTHD); sp->init(lex); - lex->trigger_definition_begin= $2; + lex->stmt_definition_begin= $2; lex->ident.str= $7; lex->ident.length= $9 - $7; @@ -9180,6 +9127,87 @@ trigger_tail: } ; +/************************************************************************** + + CREATE FUNCTION | PROCEDURE statements parts. + +**************************************************************************/ + +sp_tail: + udf_func_type remember_name FUNCTION_SYM sp_name + { + LEX *lex=Lex; + lex->udf.type= $1; + lex->stmt_definition_begin= $2; + lex->spname= $4; + } + create_function_tail + {} + | PROCEDURE remember_name sp_name + { + LEX *lex= Lex; + sp_head *sp; + + if (lex->sphead) + { + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); + YYABORT; + } + + lex->stmt_definition_begin= $2; + + /* Order is important here: new - reset - init */ + sp= new sp_head(); + sp->reset_thd_mem_root(YYTHD); + sp->init(lex); + + sp->m_type= TYPE_ENUM_PROCEDURE; + lex->sphead= sp; + /* + * We have to turn of CLIENT_MULTI_QUERIES while parsing a + * stored procedure, otherwise yylex will chop it into pieces + * at each ';'. + */ + sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); + } + '(' + { + LEX *lex= Lex; + + lex->sphead->m_param_begin= lex->tok_start+1; + } + sp_pdparam_list + ')' + { + LEX *lex= Lex; + + lex->sphead->m_param_end= lex->tok_start; + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + } + sp_c_chistics + { + LEX *lex= Lex; + + lex->sphead->m_chistics= &lex->sp_chistics; + lex->sphead->m_body_begin= lex->tok_start; + } + sp_proc_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + if (sp->check_backpatch(YYTHD)) + YYABORT; + sp->init_strings(YYTHD, lex, $3); + lex->sql_command= SQLCOM_CREATE_PROCEDURE; + /* Restore flag if it was cleared above */ + if (sp->m_old_cmq) + YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; + sp->restore_thd_mem_root(YYTHD); + } + ; + /*************************************************************************/ xa: XA_SYM begin_or_start xid opt_join_or_resume |