Commit d9d4dc5e authored by anozdrin@mysql.com's avatar anozdrin@mysql.com

Merge bk-internal.mysql.com:/home/bk/mysql-5.0

into  mysql.com:/home/alik/Documents/AllProgs/MySQL/devel/5.0-rt
parents 647a9406 c4cbe46b
......@@ -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 */
......
......@@ -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())
......
......@@ -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;
......
......@@ -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;
......@@ -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
......
......@@ -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);
......
......@@ -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
......@@ -855,3 +855,44 @@ f3
drop trigger trg11;
drop table t21,t31;
drop table t11;
STOP SLAVE;
FLUSH LOGS;
RESET SLAVE;
START SLAVE;
SELECT MASTER_POS_WAIT('master-bin.000001', 513) >= 0;
MASTER_POS_WAIT('master-bin.000001', 513) >= 0
1
SHOW TABLES;
Tables_in_test
t1
t2
SHOW TRIGGERS;
Trigger Event Table Statement Timing Created sql_mode Definer
trg1 INSERT t1 INSERT INTO t2 VALUES(CURRENT_USER()) AFTER NULL
SELECT * FROM t1;
c
1
SELECT * FROM t2;
s
@
INSERT INTO t1 VALUES(2);
SELECT * FROM t1;
c
1
2
SELECT * FROM t2;
s
@
root@localhost
DROP TRIGGER trg1;
Warnings:
Warning 1454 No definer attribute for trigger 'test'.'trg1'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger.
DROP TABLE t1;
DROP TABLE t2;
STOP SLAVE;
RESET SLAVE;
SHOW TABLES;
Tables_in_test
SHOW TRIGGERS;
Trigger Event Table Statement Timing Created sql_mode Definer
RESET MASTER;
......@@ -2,10 +2,6 @@ drop table if exists t1,v1;
drop view if exists t1,v1;
drop procedure if exists f1;
use test;
create table t1 (field1 INT);
CREATE VIEW v1 AS SELECT field1 FROM t1;
ERROR HY000: Definer is not fully qualified
drop table t1;
create procedure f1() select 1;
drop procedure f1;
create table t1 (a int);
......@@ -15,3 +11,20 @@ drop table t1;
drop function if exists f1;
Warnings:
Note 1305 FUNCTION f1 does not exist
DROP VIEW IF EXISTS v1;
DROP VIEW IF EXISTS v2;
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(c INT);
CREATE TRIGGER t1_bi BEFORE INSERT ON t1
FOR EACH ROW
SET @a = 1;
CREATE VIEW v1 AS SELECT * FROM t1;
CREATE DEFINER=a@b TRIGGER ti_ai AFTER INSERT ON t1
FOR EACH ROW
SET @b = 1;
CREATE DEFINER=a@b VIEW v2 AS SELECT * FROM t1;
DROP TRIGGER t1_bi;
DROP TRIGGER ti_ai;
DROP VIEW v1;
DROP VIEW v2;
DROP TABLE t1;
......@@ -323,3 +323,99 @@ 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;
---> 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 ALL PRIVILEGES ON mysqltest.* TO mysqltest_2@localhost;
---> connection: mysqltest_1_con
use mysqltest;
CREATE PROCEDURE bug13198_p1()
SELECT 1;
CREATE FUNCTION bug13198_f1() RETURNS INT
RETURN 1;
CALL bug13198_p1();
1
1
SELECT bug13198_f1();
bug13198_f1()
1
---> connection: mysqltest_2_con
use mysqltest;
CALL bug13198_p1();
1
1
SELECT bug13198_f1();
bug13198_f1()
1
---> connection: root
DROP USER mysqltest_1@localhost;
---> connection: mysqltest_2_con
use mysqltest;
CALL bug13198_p1();
ERROR HY000: There is no 'mysqltest_1'@'localhost' registered
SELECT bug13198_f1();
ERROR HY000: There is no 'mysqltest_1'@'localhost' registered
---> connection: root
DROP USER mysqltest_2@localhost;
DROP DATABASE mysqltest;
......@@ -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
......
......@@ -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='';
......
......@@ -162,6 +162,7 @@ use test;
drop table t1,t2;
drop database other;
#
# Test specific triggers including SELECT into var with replication
# BUG#13227:
......@@ -257,6 +258,79 @@ while ($rnd)
}
#
# BUG#16266: Definer is not fully qualified error during replication.
#
# The idea of this test is to emulate replication of a trigger from the old
# master (master w/o "DEFINER in triggers" support) to the new slave and check
# that:
# 1. the trigger on the slave will be replicated w/o errors;
# 2. the trigger on the slave will be non-SUID (will have no DEFINER);
# 3. the trigger can be activated later on the slave w/o errors.
#
# In order to emulate this kind of replication, we make the slave playing the binlog,
# recorded by 5.0.16 master. This binlog contains the following statements:
# CREATE TABLE t1(c INT);
# CREATE TABLE t2(s CHAR(200));
# CREATE TRIGGER trg1 AFTER INSERT ON t1
# FOR EACH ROW
# INSERT INTO t2 VALUES(CURRENT_USER());
# INSERT INTO t1 VALUES(1);
#
# 1. Check that the trigger's replication is succeeded.
# Stop the slave.
connection slave;
STOP SLAVE;
# Replace master's binlog.
connection master;
FLUSH LOGS;
exec cp $MYSQL_TEST_DIR/std_data/bug16266.000001 $MYSQLTEST_VARDIR/log/master-bin.000001;
# Make the slave to replay the new binlog.
connection slave;
RESET SLAVE;
START SLAVE;
SELECT MASTER_POS_WAIT('master-bin.000001', 513) >= 0;
# Check that the replication succeeded.
SHOW TABLES;
SHOW TRIGGERS;
SELECT * FROM t1;
SELECT * FROM t2;
# 2. Check that the trigger is non-SUID on the slave;
# 3. Check that the trigger can be activated on the slave.
INSERT INTO t1 VALUES(2);
SELECT * FROM t1;
SELECT * FROM t2;
# That's all, cleanup.
DROP TRIGGER trg1;
DROP TABLE t1;
DROP TABLE t2;
STOP SLAVE;
RESET SLAVE;
# The master should be clean.
connection master;
SHOW TABLES;
SHOW TRIGGERS;
RESET MASTER;
#
# End of tests
......
......@@ -8,14 +8,6 @@ drop procedure if exists f1;
--enable_warnings
use test;
#
# test that we can create VIEW if privileges check switched off
#
create table t1 (field1 INT);
-- error ER_MALFORMED_DEFINER
CREATE VIEW v1 AS SELECT field1 FROM t1;
drop table t1;
#
# Test that we can create and drop procedure without warnings
# see bug#9993
......@@ -33,3 +25,48 @@ drop table t1;
# BUG#17595: DROP FUNCTION IF EXISTS f1 crashes server
drop function if exists f1;
#
# BUG#16777: Can not create trigger nor view w/o definer if --skip-grant-tables
# specified
#
# Also, a test that we can create VIEW if privileges check switched off has
# been moved here.
#
# Prepare.
--disable_warnings
DROP VIEW IF EXISTS v1;
DROP VIEW IF EXISTS v2;
DROP TABLE IF EXISTS t1;
--enable_warnings
# Test case.
CREATE TABLE t1(c INT);
CREATE TRIGGER t1_bi BEFORE INSERT ON t1
FOR EACH ROW
SET @a = 1;
CREATE VIEW v1 AS SELECT * FROM t1;
CREATE DEFINER=a@b TRIGGER ti_ai AFTER INSERT ON t1
FOR EACH ROW
SET @b = 1;
CREATE DEFINER=a@b VIEW v2 AS SELECT * FROM t1;
# Cleanup.
DROP TRIGGER t1_bi;
DROP TRIGGER ti_ai;
DROP VIEW v1;
DROP VIEW v2;
DROP TABLE t1;
......@@ -547,4 +547,200 @@ 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;
--disconnect mysqltest_1_con
--disconnect mysqltest_2_con
#
# BUG#13198: SP executes if definer does not exist
#
# Prepare 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 ALL PRIVILEGES ON mysqltest.* TO mysqltest_2@localhost;
--connect (mysqltest_1_con,localhost,mysqltest_1,,mysqltest)
--connect (mysqltest_2_con,localhost,mysqltest_2,,mysqltest)
# Create a procedure/function under u1.
--echo
--echo ---> connection: mysqltest_1_con
--connection mysqltest_1_con
use mysqltest;
CREATE PROCEDURE bug13198_p1()
SELECT 1;
CREATE FUNCTION bug13198_f1() RETURNS INT
RETURN 1;
CALL bug13198_p1();
SELECT bug13198_f1();
# Check that u2 can call the procedure/function.
--echo
--echo ---> connection: mysqltest_2_con
--connection mysqltest_2_con
use mysqltest;
CALL bug13198_p1();
SELECT bug13198_f1();
# Drop user u1 (definer of the object);
--echo
--echo ---> connection: root
--connection con1root
--disconnect mysqltest_1_con
DROP USER mysqltest_1@localhost;
# Check that u2 can not call the procedure/function.
--echo
--echo ---> connection: mysqltest_2_con
--connection mysqltest_2_con
use mysqltest;
--error ER_NO_SUCH_USER
CALL bug13198_p1();
--error ER_NO_SUCH_USER
SELECT bug13198_f1();
# Cleanup.
--echo
--echo ---> connection: root
--connection con1root
--disconnect mysqltest_2_con
DROP USER mysqltest_2@localhost;
DROP DATABASE mysqltest;
# End of 5.0 bugs.
......@@ -530,7 +530,8 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables);
bool create_table_precheck(THD *thd, TABLE_LIST *tables,
TABLE_LIST *create_table);
bool get_default_definer(THD *thd, LEX_USER *definer);
void get_default_definer(THD *thd, LEX_USER *definer);
LEX_USER *create_default_definer(THD *thd);
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name);
enum enum_mysql_completiontype {
......
......@@ -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
......
......@@ -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;
}
......@@ -3163,24 +3171,9 @@ sp_change_security_context(THD *thd, sp_head *sp, Security_context **backup)
sp->m_definer_host.str,
sp->m_db.str))
{
#ifdef NOT_YET_REPLICATION_SAFE
/*
Until we don't properly replicate information about stored routine
definer with stored routine creation statement all stored routines
on slave are created under ''@'' definer. Therefore we won't be able
to run any routine which was replicated from master on slave server
if we emit error here. This will cause big problems for users
who use slave for fail-over. So until we fully implement WL#2897
"Complete definer support in the stored routines" we run suid
stored routines for which we were unable to find definer under
invoker security context.
*/
my_error(ER_NO_SUCH_USER, MYF(0), sp->m_definer_user.str,
sp->m_definer_host.str);
return TRUE;
#else
return FALSE;
#endif
}
*backup= thd->security_ctx;
thd->security_ctx= &sp->m_security_ctx;
......
......@@ -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);
......
......@@ -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
......
......@@ -4145,6 +4145,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
......@@ -7168,41 +7252,49 @@ Item *negate_expression(THD *thd, Item *expr)
/*
Set the specified definer to the default value, which is the current user in
the thread. Also check that the current user satisfies to the definers
requirements.
the thread.
SYNOPSIS
get_default_definer()
thd [in] thread handler
definer [out] definer
RETURN
error status, that is:
- FALSE -- on success;
- TRUE -- on error (current user can not be a definer).
*/
bool get_default_definer(THD *thd, LEX_USER *definer)
void get_default_definer(THD *thd, LEX_USER *definer)
{
/* Check that current user has non-empty host name. */
const Security_context *sctx= thd->security_ctx;
if (sctx->priv_host[0] == 0)
{
my_error(ER_MALFORMED_DEFINER, MYF(0));
return TRUE;
}
/* Fill in. */
definer->user.str= (char *) sctx->priv_user;
definer->user.length= strlen(definer->user.str);
definer->host.str= (char *) sctx->priv_host;
definer->host.length= strlen(definer->host.str);
}
return FALSE;
/*
Create default definer for the specified THD.
SYNOPSIS
create_default_definer()
thd [in] thread handler
RETURN
On success, return a valid pointer to the created and initialized
LEX_USER, which contains definer information.
On error, return 0.
*/
LEX_USER *create_default_definer(THD *thd)
{
LEX_USER *definer;
if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
return 0;
get_default_definer(thd, definer);
return definer;
}
......@@ -7218,7 +7310,7 @@ bool get_default_definer(THD *thd, LEX_USER *definer)
RETURN
On success, return a valid pointer to the created and initialized
LEX_STRING, which contains definer information.
LEX_USER, which contains definer information.
On error, return 0.
*/
......
......@@ -264,8 +264,18 @@ end:
log_query.set((char *) 0, 0, system_charset_info); /* reset log_query */
log_query.append(STRING_WITH_LEN("CREATE "));
append_definer(thd, &log_query, &definer_user, &definer_host);
log_query.append(thd->lex->trigger_definition_begin);
if (definer_user.str && definer_host.str)
{
/*
Append definer-clause if the trigger is SUID (a usual trigger in
new MySQL versions).
*/
append_definer(thd, &log_query, &definer_user, &definer_host);
}
log_query.append(thd->lex->stmt_definition_begin);
}
/* Such a statement can always go directly to binlog, no trans cache. */
......@@ -289,17 +299,30 @@ end:
LEX)
tables - table list containing one open table for which the
trigger is created.
definer_user - [out] after a call it points to 0-terminated string,
which contains user name part of the actual trigger
definer. The caller is responsible to provide memory for
definer_user - [out] after a call it points to 0-terminated string or
contains the NULL-string:
- 0-terminated is returned if the trigger is SUID. The
string contains user name part of the actual trigger
definer.
- NULL-string is returned if the trigger is non-SUID.
Anyway, the caller is responsible to provide memory for
storing LEX_STRING object.
definer_host - [out] after a call it points to 0-terminated string,
which contains host name part of the actual trigger
definer. The caller is responsible to provide memory for
definer_host - [out] after a call it points to 0-terminated string or
contains the NULL-string:
- 0-terminated string is returned if the trigger is
SUID. The string contains host name part of the
actual trigger definer.
- NULL-string is returned if the trigger is non-SUID.
Anyway, the caller is responsible to provide memory for
storing LEX_STRING object.
NOTE
Assumes that trigger name is fully qualified.
- Assumes that trigger name is fully qualified.
- NULL-string means the following LEX_STRING instance:
{ str = 0; length = 0 }.
- In other words, definer_user and definer_host should contain
simultaneously NULL-strings (non-SUID/old trigger) or valid strings
(SUID/new trigger).
RETURN VALUE
False - success
......@@ -336,12 +359,30 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
return 1;
}
/*
Definer attribute of the Lex instance is always set in sql_yacc.yy when
trigger is created.
*/
if (!lex->definer)
{
/*
DEFINER-clause is missing.
DBUG_ASSERT(lex->definer);
If we are in slave thread, this means that we received CREATE TRIGGER
from the master, that does not support definer in triggers. So, we
should mark this trigger as non-SUID. Note that this does not happen
when we parse triggers' definitions during opening .TRG file.
LEX::definer is ignored in that case.
Otherwise, we should use CURRENT_USER() as definer.
NOTE: when CREATE TRIGGER statement is allowed to be executed in PS/SP,
it will be required to create the definer below in persistent MEM_ROOT
of PS/SP.
*/
if (!thd->slave_thread)
{
if (!(lex->definer= create_default_definer(thd)))
return 1;
}
}
/*
If the specified definer differs from the current user, we should check
......@@ -349,10 +390,11 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
under another user one must have SUPER privilege).
*/
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 (lex->definer &&
(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))
{
......@@ -446,8 +488,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
*trg_sql_mode= thd->variables.sql_mode;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (!is_acl_user(lex->definer->host.str,
lex->definer->user.str))
if (lex->definer && !is_acl_user(lex->definer->host.str,
lex->definer->user.str))
{
push_warning_printf(thd,
MYSQL_ERROR::WARN_LEVEL_NOTE,
......@@ -458,12 +500,30 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
}
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
*definer_user= lex->definer->user;
*definer_host= lex->definer->host;
if (lex->definer)
{
/* SUID trigger. */
*definer_user= lex->definer->user;
*definer_host= lex->definer->host;
trg_definer->str= trg_definer_holder;
trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@",
definer_host->str, NullS) - trg_definer->str;
trg_definer->str= trg_definer_holder;
trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@",
definer_host->str, NullS) - trg_definer->str;
}
else
{
/* non-SUID trigger. */
definer_user->str= 0;
definer_user->length= 0;
definer_host->str= 0;
definer_host->length= 0;
trg_definer->str= (char*) "";
trg_definer->length= 0;
}
if (!sql_create_definition_file(&dir, &file, &triggers_file_type,
(gptr)this, triggers_file_parameters, 0))
......
......@@ -208,6 +208,26 @@ bool mysql_create_view(THD *thd,
if (mode != VIEW_CREATE_NEW)
sp_cache_invalidate();
if (!lex->definer)
{
/*
DEFINER-clause is missing; we have to create default definer in
persistent arena to be PS/SP friendly.
*/
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)
goto err;
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
check definer of view:
......@@ -815,8 +835,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table)
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_VIEW_FRM_NO_USER, ER(ER_VIEW_FRM_NO_USER),
table->db, table->table_name);
if (get_default_definer(thd, &table->definer))
goto err;
get_default_definer(thd, &table->definer);
}
/*
......
......@@ -778,7 +778,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <symbol> FUNC_ARG0 FUNC_ARG1 FUNC_ARG2 FUNC_ARG3 keyword keyword_sp
%type <lex_user> user grant_user get_definer
%type <lex_user> user grant_user
%type <charset>
opt_collate
......@@ -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
{
......@@ -8957,45 +8890,61 @@ 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:
get_definer
/* empty */
{
THD *thd= YYTHD;
if (! (thd->lex->definer= create_definer(thd, &$1->user, &$1->host)))
YYABORT;
/*
We have to distinguish missing DEFINER-clause from case when
CURRENT_USER specified as definer explicitly in order to properly
handle CREATE TRIGGER statements which come to replication thread
from older master servers (i.e. to create non-suid trigger in this
case).
*/
YYTHD->lex->definer= 0;
}
;
get_definer:
opt_current_definer
| DEFINER_SYM EQ CURRENT_USER optional_braces
{
THD *thd= YYTHD;
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
YYABORT;
if (get_default_definer(thd, $$))
YYABORT;
if (! (YYTHD->lex->definer= create_default_definer(YYTHD)))
YYABORT;
}
| DEFINER_SYM EQ ident_or_text '@' ident_or_text
{
if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user))))
YYABORT;
$$->user= $3;
$$->host= $5;
if (!(YYTHD->lex->definer= create_definer(YYTHD, &$3, &$5)))
YYABORT;
}
;
opt_current_definer:
/* empty */
| DEFINER_SYM EQ CURRENT_USER optional_braces
;
/**************************************************************************
CREATE VIEW statement options.
CREATE VIEW statement parts.
**************************************************************************/
......@@ -9029,20 +8978,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; }
......@@ -9141,7 +9076,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= $10 - $7;
......@@ -9190,6 +9125,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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment