Commit 2dbd2693 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-7283 UDF: CREATE OR REPLACE, CREATE IF NOT EXISTS, DROP IF EXISTS

MDEV-7282 SP: CREATE OR REPLACE, CREATE IF NOT EXISTS
parent 48ed8ab2
...@@ -27,6 +27,111 @@ Log_name Pos Event_type Server_id End_log_pos Info ...@@ -27,6 +27,111 @@ Log_name Pos Event_type Server_id End_log_pos Info
# # Query 1 # DROP DATABASE IF EXISTS d1 # # Query 1 # DROP DATABASE IF EXISTS d1
RESET MASTER; RESET MASTER;
USE test; USE test;
CREATE OR REPLACE FUNCTION f1() RETURNS INT RETURN 1;
CREATE OR REPLACE FUNCTION f1() RETURNS INT RETURN 1;
DROP FUNCTION f1;
CREATE FUNCTION IF NOT EXISTS f1() RETURNS INT RETURN 1;
CREATE FUNCTION IF NOT EXISTS f1() RETURNS INT RETURN 1;
Warnings:
Note 1304 FUNCTION f1 already exists
DROP FUNCTION IF EXISTS f1;
DROP FUNCTION IF EXISTS f1;
Warnings:
Note 1305 FUNCTION test.f1 does not exist
SHOW BINLOG EVENTS;
Log_name Pos Event_type Server_id End_log_pos Info
# # Format_desc 1 # VER
# # Gtid_list 1 # []
# # Binlog_checkpoint 1 # master-bin.000001
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11)
RETURN 1
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11)
RETURN 1
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; DROP FUNCTION f1
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; CREATE DEFINER=`root`@`localhost` FUNCTION IF NOT EXISTS `f1`() RETURNS int(11)
RETURN 1
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; CREATE DEFINER=`root`@`localhost` FUNCTION IF NOT EXISTS `f1`() RETURNS int(11)
RETURN 1
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; DROP FUNCTION IF EXISTS f1
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; DROP FUNCTION IF EXISTS f1
RESET MASTER;
CREATE TABLE t1 (a DATETIME);
CREATE OR REPLACE PROCEDURE p1() DELETE FROM t1;
CREATE OR REPLACE PROCEDURE p1() DELETE FROM t1;
DROP PROCEDURE p1;
CREATE PROCEDURE IF NOT EXISTS p1() DELETE FROM t1;
CREATE PROCEDURE IF NOT EXISTS p1() DELETE FROM t1;
Warnings:
Note 1304 PROCEDURE p1 already exists
DROP PROCEDURE IF EXISTS p1;
DROP PROCEDURE IF EXISTS p1;
Warnings:
Note 1305 PROCEDURE test.p1 does not exist
SHOW BINLOG EVENTS;
Log_name Pos Event_type Server_id End_log_pos Info
# # Format_desc 1 # VER
# # Gtid_list 1 # []
# # Binlog_checkpoint 1 # master-bin.000001
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; CREATE TABLE t1 (a DATETIME)
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `p1`()
DELETE FROM t1
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `p1`()
DELETE FROM t1
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; DROP PROCEDURE p1
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE IF NOT EXISTS `p1`()
DELETE FROM t1
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE IF NOT EXISTS `p1`()
DELETE FROM t1
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; DROP PROCEDURE IF EXISTS p1
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; DROP PROCEDURE IF EXISTS p1
DROP TABLE t1;
RESET MASTER;
CREATE OR REPLACE FUNCTION metaphon RETURNS STRING SONAME 'UDF_EXAMPLE_LIB';
CREATE OR REPLACE FUNCTION metaphon RETURNS STRING SONAME 'UDF_EXAMPLE_LIB';
DROP FUNCTION metaphon;
CREATE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME 'UDF_EXAMPLE_LIB';
CREATE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME 'UDF_EXAMPLE_LIB';
Warnings:
Note 1125 Function 'metaphon' already exists
DROP FUNCTION IF EXISTS metaphon;
DROP FUNCTION IF EXISTS metaphon;
Warnings:
Note 1305 FUNCTION test.metaphon does not exist
SHOW BINLOG EVENTS;
Log_name Pos Event_type Server_id End_log_pos Info
# # Format_desc 1 # VER
# # Gtid_list 1 # []
# # Binlog_checkpoint 1 # master-bin.000001
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; CREATE OR REPLACE FUNCTION metaphon RETURNS STRING SONAME 'UDM_EXAMPLE_LIB'
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; CREATE OR REPLACE FUNCTION metaphon RETURNS STRING SONAME 'UDM_EXAMPLE_LIB'
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; DROP FUNCTION metaphon
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; CREATE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME 'UDM_EXAMPLE_LIB'
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; CREATE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME 'UDM_EXAMPLE_LIB'
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; DROP FUNCTION IF EXISTS metaphon
# # Gtid 1 # GTID #-#-#
# # Query 1 # use `test`; DROP FUNCTION IF EXISTS metaphon
RESET MASTER;
# #
# CREATE SERVER is not logged # CREATE SERVER is not logged
# #
......
SET timestamp=UNIX_TIMESTAMP('2014-09-30 08:00:00');
CREATE FUNCTION f1(str char(20))
RETURNS CHAR(100)
RETURN CONCAT('Hello, ', str, '!');
SELECT * FROM mysql.proc WHERE name like 'f1';
db name type specific_name language sql_data_access is_deterministic security_type param_list returns body definer created modified sql_mode comment character_set_client collation_connection db_collation body_utf8
test f1 FUNCTION f1 SQL CONTAINS_SQL NO DEFINER str char(20) char(100) CHARSET latin1 RETURN CONCAT('Hello, ', str, '!') root@localhost 2014-09-30 08:00:00 2014-09-30 08:00:00 latin1 latin1_swedish_ci latin1_swedish_ci RETURN CONCAT('Hello, ', str, '!')
SELECT f1('world');
f1('world')
Hello, world!
CREATE FUNCTION f1(str char(20))
RETURNS TEXT
RETURN CONCAT('Hello2, ', str, '!');
ERROR 42000: FUNCTION f1 already exists
SELECT body FROM mysql.proc WHERE name like 'f1';
body
RETURN CONCAT('Hello, ', str, '!')
CREATE FUNCTION IF NOT EXISTS f1(str char(20))
RETURNS CHAR(100)
RETURN CONCAT('Hello3, ', str, '!');
Warnings:
Note 1304 FUNCTION f1 already exists
SELECT body FROM mysql.proc WHERE name like 'f1';
body
RETURN CONCAT('Hello, ', str, '!')
CREATE OR REPLACE FUNCTION IF NOT EXISTS f1(str char(20))
RETURNS CHAR(100)
RETURN CONCAT('Hello4, ', str, '!');
ERROR HY000: Incorrect usage of OR REPLACE and IF NOT EXISTS
SELECT body FROM mysql.proc WHERE name like 'f1';
body
RETURN CONCAT('Hello, ', str, '!')
CREATE OR REPLACE FUNCTION f1(str char(20))
RETURNS CHAR(100)
RETURN CONCAT('Hello5, ', str, '!');
SELECT body FROM mysql.proc WHERE name like 'f1';
body
RETURN CONCAT('Hello5, ', str, '!')
DROP FUNCTION f1;
CREATE FUNCTION IF NOT EXISTS f1(str char(20))
RETURNS CHAR(100)
RETURN CONCAT('Hello6, ', str, '!');
SELECT body FROM mysql.proc WHERE name like 'f1';
body
RETURN CONCAT('Hello6, ', str, '!')
SELECT f1('world');
f1('world')
Hello6, world!
DROP FUNCTION IF EXISTS f1;
SELECT body FROM mysql.proc WHERE name like 'f1';
body
DROP FUNCTION IF EXISTS f1;
Warnings:
Note 1305 FUNCTION test.f1 does not exist
CREATE TABLE t1 (id INT);
CREATE PROCEDURE proc1 (OUT cnt INT) COMMENT 'comment1' BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
CALL proc1(@cnt);
SELECT @cnt;
@cnt
0
INSERT INTO t1 VALUES (1), (2), (3);
CALL proc1(@cnt);
SELECT @cnt;
@cnt
3
CREATE PROCEDURE proc1 (OUT cnt INT) COMMENT 'comment2'
BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
ERROR 42000: PROCEDURE proc1 already exists
SELECT comment FROM mysql.proc WHERE name='proc1'$$
comment
comment1
CREATE PROCEDURE IF NOT EXISTS proc1 (OUT cnt INT) COMMENT 'comment3'
BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
Warnings:
Note 1304 PROCEDURE proc1 already exists
SELECT comment FROM mysql.proc WHERE name='proc1'$$
comment
comment1
CREATE OR REPLACE PROCEDURE IF NOT EXISTS proc1 (OUT cnt INT) COMMENT 'comment4'
BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
ERROR HY000: Incorrect usage of OR REPLACE and IF NOT EXISTS
SELECT comment FROM mysql.proc WHERE name='proc1'$$
comment
comment1
CREATE OR REPLACE PROCEDURE proc1 (OUT cnt INT) COMMENT 'comment5'
BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
SELECT comment FROM mysql.proc WHERE name='proc1'$$
comment
comment5
DROP PROCEDURE proc1;
CREATE PROCEDURE IF NOT EXISTS proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
INSERT INTO t1 VALUES (1), (2), (3);
CALL proc1(@cnt);
SELECT @cnt;
@cnt
6
DROP TABLE IF EXISTS t1;
DROP PROCEDURE IF EXISTS proc1;
DROP PROCEDURE IF EXISTS proc1;
Warnings:
Note 1305 PROCEDURE test.proc1 does not exist
CREATE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME "UDF_EXAMPLE_LIB";
SELECT ret FROM mysql.func WHERE name like 'metaphon';
ret
0
CREATE FUNCTION metaphon RETURNS INT SONAME "$UDF_EXAMPLE_SO";
ERROR HY000: Function 'metaphon' already exists
SELECT ret FROM mysql.func WHERE name like 'metaphon';
ret
0
CREATE FUNCTION IF NOT EXISTS metaphon RETURNS REAL SONAME "UDF_EXAMPLE_LIB";
Warnings:
Note 1125 Function 'metaphon' already exists
SELECT ret FROM mysql.func WHERE name like 'metaphon';
ret
0
DROP FUNCTION IF EXISTS metaphon;
CREATE FUNCTION IF NOT EXISTS metaphon RETURNS REAL SONAME "UDF_EXAMPLE_LIB";
SELECT ret FROM mysql.func WHERE name like 'metaphon';
ret
1
CREATE OR REPLACE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME "udf_example.so";
ERROR HY000: Incorrect usage of OR REPLACE and IF NOT EXISTS
SELECT ret FROM mysql.func WHERE name like 'metaphon';
ret
1
CREATE OR REPLACE FUNCTION metaphon RETURNS STRING SONAME "UDF_EXAMPLE_LIB";
SELECT ret FROM mysql.func WHERE name like 'metaphon';
ret
0
CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so";
ERROR HY000: Function 'metaphon' already exists
SELECT metaphon('mariadb');
metaphon('mariadb')
MRTB
DROP FUNCTION metaphon;
DROP FUNCTION IF EXISTS metaphon;
Warnings:
Note 1305 FUNCTION test.metaphon does not exist
...@@ -29,6 +29,12 @@ ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'db2' ...@@ -29,6 +29,12 @@ ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'db2'
USE db1; USE db1;
CREATE OR REPLACE TABLE t1(id INT); CREATE OR REPLACE TABLE t1(id INT);
ERROR 42000: DROP command denied to user 'mysqltest_1'@'localhost' for table 't1' ERROR 42000: DROP command denied to user 'mysqltest_1'@'localhost' for table 't1'
CREATE OR REPLACE PROCEDURE proc1 (OUT cnt INT) BEGIN END;
ERROR 42000: alter routine command denied to user 'mysqltest_1'@'localhost' for routine 'db1.proc1'
CREATE OR REPLACE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysql'
CREATE OR REPLACE FUNCTION hello(str char(20)) RETURNS TEXT RETURN CONCAT('Hello, ', str, '!');
ERROR 42000: alter routine command denied to user 'mysqltest_1'@'localhost' for routine 'db1.hello'
SELECT CURRENT_USER; SELECT CURRENT_USER;
CURRENT_USER CURRENT_USER
root@localhost root@localhost
......
...@@ -1223,9 +1223,7 @@ END' at line 1 ...@@ -1223,9 +1223,7 @@ END' at line 1
CREATE PROCEDURE IF NOT EXISTS bug14702() CREATE PROCEDURE IF NOT EXISTS bug14702()
BEGIN BEGIN
END; END;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'IF NOT EXISTS bug14702() DROP PROCEDURE IF EXISTS bug14702;
BEGIN
END' at line 1
DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (i INT); CREATE TABLE t1 (i INT);
CREATE PROCEDURE bug20953() CREATE VIEW v AS SELECT 1 INTO @a; CREATE PROCEDURE bug20953() CREATE VIEW v AS SELECT 1 INTO @a;
......
include/master-slave.inc
[connection master]
CREATE FUNCTION hello (str CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN
CONCAT('Hello, ', str, '!');
CREATE FUNCTION hello (str CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN
CONCAT('Hello, ', str, '!');
ERROR 42000: FUNCTION hello already exists
SHOW CREATE FUNCTION hello;
Function sql_mode Create Function character_set_client collation_connection Database Collation
hello CREATE DEFINER=`root`@`localhost` FUNCTION `hello`(str CHAR(20)) RETURNS char(50) CHARSET latin1
DETERMINISTIC
RETURN
CONCAT('Hello, ', str, '!') latin1 latin1_swedish_ci latin1_swedish_ci
CREATE OR REPLACE FUNCTION IF NOT EXISTS hello (str CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN
CONCAT('Hello, ', str, '!');
ERROR HY000: Incorrect usage of OR REPLACE and IF NOT EXISTS
CREATE OR REPLACE FUNCTION hello (str CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN
CONCAT('Hello, ', str, '!');
SELECT hello('master');
hello('master')
Hello, master!
SELECT hello('slave');
hello('slave')
Hello, slave!
CREATE FUNCTION IF NOT EXISTS hello (str CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN
CONCAT('Hello, ', str, '!');
Warnings:
Note 1304 FUNCTION hello already exists
CREATE OR REPLACE FUNCTION bye (str CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN
CONCAT('Bye, ', str, '!');
SELECT hello('master');
hello('master')
Hello, master!
SELECT bye('master');
bye('master')
Bye, master!
SELECT hello('slave');
hello('slave')
Hello, slave!
SELECT bye('slave');
bye('slave')
Bye, slave!
DROP FUNCTION hello;
DROP FUNCTION IF EXISTS bye;
DROP FUNCTION hello;
ERROR 42000: FUNCTION test.hello does not exist
DROP FUNCTION IF EXISTS bye;
Warnings:
Note 1305 FUNCTION test.bye does not exist
include/rpl_end.inc
include/master-slave.inc
[connection master]
CREATE TABLE t1 (id INT);
CREATE PROCEDURE proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
CALL proc1(@cnt);
SELECT @cnt;
@cnt
0
INSERT INTO t1 VALUES (1), (2), (3);
CALL proc1(@cnt);
SELECT @cnt;
@cnt
3
# Syncing with slave
CALL proc1(@cnt);
SELECT @cnt;
@cnt
3
CREATE PROCEDURE proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
ERROR 42000: PROCEDURE proc1 already exists
CREATE PROCEDURE IF NOT EXISTS proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
Warnings:
Note 1304 PROCEDURE proc1 already exists
CREATE OR REPLACE PROCEDURE IF NOT EXISTS proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
ERROR HY000: Incorrect usage of OR REPLACE and IF NOT EXISTS
CREATE OR REPLACE PROCEDURE proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
# Syncing with slave
CALL proc1(@cnt);
SELECT @cnt;
@cnt
3
DROP PROCEDURE proc1;
CREATE PROCEDURE IF NOT EXISTS proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
INSERT INTO t1 VALUES (1), (2), (3);
CALL proc1(@cnt);
SELECT @cnt;
@cnt
6
# Syncing with slave
CALL proc1(@cnt);
SELECT @cnt;
@cnt
6
DROP PROCEDURE proc1;
CREATE OR REPLACE PROCEDURE proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
INSERT INTO t1 VALUES (1), (2), (3);
CALL proc1(@cnt);
SELECT @cnt;
@cnt
9
# Syncing with slave
CALL proc1(@cnt);
SELECT @cnt;
@cnt
9
DROP TABLE IF EXISTS t1;
DROP PROCEDURE proc1;
DROP PROCEDURE IF EXISTS proc2;
Warnings:
Note 1305 PROCEDURE test.proc2 does not exist
# Syncing with slave
DROP PROCEDURE proc1;
ERROR 42000: PROCEDURE test.proc1 does not exist
include/rpl_end.inc
include/master-slave.inc
[connection master]
CREATE FUNCTION metaphon RETURNS STRING SONAME "UDF_EXAMPLE_LIB";
SELECT metaphon('master');
metaphon('master')
MSTR
CREATE FUNCTION metaphon RETURNS INT SONAME "udf_example.so";
ERROR HY000: Function 'metaphon' already exists
SELECT metaphon('slave');
metaphon('slave')
SLF
CREATE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME "udf_example.so";
Warnings:
Note 1125 Function 'metaphon' already exists
DROP FUNCTION IF EXISTS random_function_name;
Warnings:
Note 1305 FUNCTION test.random_function_name does not exist
CREATE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME "udf_example.so";
Warnings:
Note 1125 Function 'metaphon' already exists
CREATE OR REPLACE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME "udf_example.so";
ERROR HY000: Incorrect usage of OR REPLACE and IF NOT EXISTS
SELECT metaphon('slave');
metaphon('slave')
SLF
DROP FUNCTION metaphon;
CREATE OR REPLACE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so";
CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so";
ERROR HY000: Function 'metaphon' already exists
SELECT metaphon('slave');
metaphon('slave')
SLF
DROP FUNCTION metaphon;
DROP FUNCTION IF EXISTS metaphon;
Warnings:
Note 1305 FUNCTION test.metaphon does not exist
DROP FUNCTION metaphon;
ERROR 42000: FUNCTION test.metaphon does not exist
DROP FUNCTION IF EXISTS metaphon;
Warnings:
Note 1305 FUNCTION test.metaphon does not exist
include/rpl_end.inc
--source include/master-slave.inc
connection master;
CREATE FUNCTION hello (str CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN
CONCAT('Hello, ', str, '!');
--error ER_SP_ALREADY_EXISTS
CREATE FUNCTION hello (str CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN
CONCAT('Hello, ', str, '!');
sync_slave_with_master;
SHOW CREATE FUNCTION hello;
connection master;
--error ER_WRONG_USAGE
CREATE OR REPLACE FUNCTION IF NOT EXISTS hello (str CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN
CONCAT('Hello, ', str, '!');
CREATE OR REPLACE FUNCTION hello (str CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN
CONCAT('Hello, ', str, '!');
SELECT hello('master');
sync_slave_with_master;
SELECT hello('slave');
connection master;
CREATE FUNCTION IF NOT EXISTS hello (str CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN
CONCAT('Hello, ', str, '!');
CREATE OR REPLACE FUNCTION bye (str CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN
CONCAT('Bye, ', str, '!');
SELECT hello('master');
SELECT bye('master');
sync_slave_with_master;
SELECT hello('slave');
SELECT bye('slave');
connection master;
DROP FUNCTION hello;
DROP FUNCTION IF EXISTS bye;
sync_slave_with_master;
--error ER_SP_DOES_NOT_EXIST
DROP FUNCTION hello;
DROP FUNCTION IF EXISTS bye;
--source include/rpl_end.inc
--source include/master-slave.inc
connection master;
CREATE TABLE t1 (id INT);
DELIMITER $$;
CREATE PROCEDURE proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
DELIMITER ;$$
CALL proc1(@cnt);
SELECT @cnt;
INSERT INTO t1 VALUES (1), (2), (3);
CALL proc1(@cnt);
SELECT @cnt;
--echo # Syncing with slave
sync_slave_with_master;
CALL proc1(@cnt);
SELECT @cnt;
connection master;
DELIMITER $$;
--error ER_SP_ALREADY_EXISTS
CREATE PROCEDURE proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
CREATE PROCEDURE IF NOT EXISTS proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
--error ER_WRONG_USAGE
CREATE OR REPLACE PROCEDURE IF NOT EXISTS proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
CREATE OR REPLACE PROCEDURE proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
DELIMITER ;$$
--echo # Syncing with slave
sync_slave_with_master;
CALL proc1(@cnt);
SELECT @cnt;
connection master;
DROP PROCEDURE proc1;
DELIMITER $$;
CREATE PROCEDURE IF NOT EXISTS proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
DELIMITER ;$$
INSERT INTO t1 VALUES (1), (2), (3);
CALL proc1(@cnt);
SELECT @cnt;
--echo # Syncing with slave
sync_slave_with_master;
CALL proc1(@cnt);
SELECT @cnt;
connection master;
DROP PROCEDURE proc1;
DELIMITER $$;
CREATE OR REPLACE PROCEDURE proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
DELIMITER ;$$
INSERT INTO t1 VALUES (1), (2), (3);
CALL proc1(@cnt);
SELECT @cnt;
--echo # Syncing with slave
sync_slave_with_master;
CALL proc1(@cnt);
SELECT @cnt;
connection master;
DROP TABLE IF EXISTS t1;
DROP PROCEDURE proc1;
DROP PROCEDURE IF EXISTS proc2;
--echo # Syncing with slave
sync_slave_with_master;
--error ER_SP_DOES_NOT_EXIST
DROP PROCEDURE proc1;
--source include/rpl_end.inc
--source include/master-slave.inc
--replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB
connection master;
eval CREATE FUNCTION metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_SO";
SELECT metaphon('master');
--error ER_UDF_EXISTS
eval CREATE FUNCTION metaphon RETURNS INT SONAME "$UDF_EXAMPLE_SO";
sync_slave_with_master;
SELECT metaphon('slave');
connection master;
eval CREATE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_SO";
DROP FUNCTION IF EXISTS random_function_name;
eval CREATE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_SO";
--error ER_WRONG_USAGE
eval CREATE OR REPLACE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_SO";
sync_slave_with_master;
SELECT metaphon('slave');
connection master;
DROP FUNCTION metaphon;
eval CREATE OR REPLACE FUNCTION metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_SO";
--error ER_UDF_EXISTS
eval CREATE FUNCTION metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_SO";
sync_slave_with_master;
SELECT metaphon('slave');
connection master;
DROP FUNCTION metaphon;
DROP FUNCTION IF EXISTS metaphon;
sync_slave_with_master;
--error ER_SP_DOES_NOT_EXIST
DROP FUNCTION metaphon;
DROP FUNCTION IF EXISTS metaphon;
--source include/rpl_end.inc
...@@ -19,6 +19,53 @@ DROP DATABASE IF EXISTS d1; ...@@ -19,6 +19,53 @@ DROP DATABASE IF EXISTS d1;
RESET MASTER; RESET MASTER;
USE test; USE test;
CREATE OR REPLACE FUNCTION f1() RETURNS INT RETURN 1;
CREATE OR REPLACE FUNCTION f1() RETURNS INT RETURN 1;
DROP FUNCTION f1;
CREATE FUNCTION IF NOT EXISTS f1() RETURNS INT RETURN 1;
CREATE FUNCTION IF NOT EXISTS f1() RETURNS INT RETURN 1;
DROP FUNCTION IF EXISTS f1;
DROP FUNCTION IF EXISTS f1;
--replace_column 1 # 2 # 5 #
--replace_regex /xid=[0-9]+/xid=XX/ /GTID [0-9]+-[0-9]+-[0-9]+/GTID #-#-#/ /Server.ver.*/VER/
SHOW BINLOG EVENTS;
RESET MASTER;
CREATE TABLE t1 (a DATETIME);
CREATE OR REPLACE PROCEDURE p1() DELETE FROM t1;
CREATE OR REPLACE PROCEDURE p1() DELETE FROM t1;
DROP PROCEDURE p1;
CREATE PROCEDURE IF NOT EXISTS p1() DELETE FROM t1;
CREATE PROCEDURE IF NOT EXISTS p1() DELETE FROM t1;
DROP PROCEDURE IF EXISTS p1;
DROP PROCEDURE IF EXISTS p1;
--replace_column 1 # 2 # 5 #
--replace_regex /xid=[0-9]+/xid=XX/ /GTID [0-9]+-[0-9]+-[0-9]+/GTID #-#-#/ /Server.ver.*/VER/
SHOW BINLOG EVENTS;
DROP TABLE t1;
RESET MASTER;
--replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB
eval CREATE OR REPLACE FUNCTION metaphon RETURNS STRING SONAME '$UDF_EXAMPLE_SO';
--replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB
eval CREATE OR REPLACE FUNCTION metaphon RETURNS STRING SONAME '$UDF_EXAMPLE_SO';
DROP FUNCTION metaphon;
--replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB
eval CREATE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME '$UDF_EXAMPLE_SO';
--replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB
eval CREATE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME '$UDF_EXAMPLE_SO';
DROP FUNCTION IF EXISTS metaphon;
DROP FUNCTION IF EXISTS metaphon;
--replace_column 1 # 2 # 5 #
--replace_result $UDF_EXAMPLE_SO UDM_EXAMPLE_LIB
--replace_regex /xid=[0-9]+/xid=XX/ /GTID [0-9]+-[0-9]+-[0-9]+/GTID #-#-#/ /Server.ver.*/VER/
SHOW BINLOG EVENTS;
RESET MASTER;
--echo # --echo #
--echo # CREATE SERVER is not logged --echo # CREATE SERVER is not logged
--echo # --echo #
......
SET timestamp=UNIX_TIMESTAMP('2014-09-30 08:00:00');
CREATE FUNCTION f1(str char(20))
RETURNS CHAR(100)
RETURN CONCAT('Hello, ', str, '!');
SELECT * FROM mysql.proc WHERE name like 'f1';
SELECT f1('world');
--error ER_SP_ALREADY_EXISTS
CREATE FUNCTION f1(str char(20))
RETURNS TEXT
RETURN CONCAT('Hello2, ', str, '!');
SELECT body FROM mysql.proc WHERE name like 'f1';
CREATE FUNCTION IF NOT EXISTS f1(str char(20))
RETURNS CHAR(100)
RETURN CONCAT('Hello3, ', str, '!');
SELECT body FROM mysql.proc WHERE name like 'f1';
--error ER_WRONG_USAGE
CREATE OR REPLACE FUNCTION IF NOT EXISTS f1(str char(20))
RETURNS CHAR(100)
RETURN CONCAT('Hello4, ', str, '!');
SELECT body FROM mysql.proc WHERE name like 'f1';
CREATE OR REPLACE FUNCTION f1(str char(20))
RETURNS CHAR(100)
RETURN CONCAT('Hello5, ', str, '!');
SELECT body FROM mysql.proc WHERE name like 'f1';
DROP FUNCTION f1;
CREATE FUNCTION IF NOT EXISTS f1(str char(20))
RETURNS CHAR(100)
RETURN CONCAT('Hello6, ', str, '!');
SELECT body FROM mysql.proc WHERE name like 'f1';
SELECT f1('world');
DROP FUNCTION IF EXISTS f1;
SELECT body FROM mysql.proc WHERE name like 'f1';
DROP FUNCTION IF EXISTS f1;
CREATE TABLE t1 (id INT);
DELIMITER $$;
CREATE PROCEDURE proc1 (OUT cnt INT) COMMENT 'comment1' BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
DELIMITER ;$$
CALL proc1(@cnt);
SELECT @cnt;
INSERT INTO t1 VALUES (1), (2), (3);
CALL proc1(@cnt);
SELECT @cnt;
DELIMITER $$;
--error ER_SP_ALREADY_EXISTS
CREATE PROCEDURE proc1 (OUT cnt INT) COMMENT 'comment2'
BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
SELECT comment FROM mysql.proc WHERE name='proc1'$$
CREATE PROCEDURE IF NOT EXISTS proc1 (OUT cnt INT) COMMENT 'comment3'
BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
SELECT comment FROM mysql.proc WHERE name='proc1'$$
--error ER_WRONG_USAGE
CREATE OR REPLACE PROCEDURE IF NOT EXISTS proc1 (OUT cnt INT) COMMENT 'comment4'
BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
SELECT comment FROM mysql.proc WHERE name='proc1'$$
CREATE OR REPLACE PROCEDURE proc1 (OUT cnt INT) COMMENT 'comment5'
BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
SELECT comment FROM mysql.proc WHERE name='proc1'$$
DELIMITER ;$$
DROP PROCEDURE proc1;
DELIMITER $$;
CREATE PROCEDURE IF NOT EXISTS proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$$
DELIMITER ;$$
INSERT INTO t1 VALUES (1), (2), (3);
CALL proc1(@cnt);
SELECT @cnt;
DROP TABLE IF EXISTS t1;
DROP PROCEDURE IF EXISTS proc1;
DROP PROCEDURE IF EXISTS proc1;
--replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB
eval CREATE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_SO";
SELECT ret FROM mysql.func WHERE name like 'metaphon';
--error ER_UDF_EXISTS
CREATE FUNCTION metaphon RETURNS INT SONAME "$UDF_EXAMPLE_SO";
SELECT ret FROM mysql.func WHERE name like 'metaphon';
--replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB
eval CREATE FUNCTION IF NOT EXISTS metaphon RETURNS REAL SONAME "$UDF_EXAMPLE_SO";
SELECT ret FROM mysql.func WHERE name like 'metaphon';
DROP FUNCTION IF EXISTS metaphon;
--replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB
eval CREATE FUNCTION IF NOT EXISTS metaphon RETURNS REAL SONAME "$UDF_EXAMPLE_SO";
SELECT ret FROM mysql.func WHERE name like 'metaphon';
--error ER_WRONG_USAGE
eval CREATE OR REPLACE FUNCTION IF NOT EXISTS metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_SO";
SELECT ret FROM mysql.func WHERE name like 'metaphon';
--replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB
eval CREATE OR REPLACE FUNCTION metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_SO";
SELECT ret FROM mysql.func WHERE name like 'metaphon';
--error ER_UDF_EXISTS
eval CREATE FUNCTION metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_SO";
SELECT metaphon('mariadb');
DROP FUNCTION metaphon;
DROP FUNCTION IF EXISTS metaphon;
...@@ -41,18 +41,16 @@ USE db1; ...@@ -41,18 +41,16 @@ USE db1;
--error ER_TABLEACCESS_DENIED_ERROR --error ER_TABLEACCESS_DENIED_ERROR
CREATE OR REPLACE TABLE t1(id INT); CREATE OR REPLACE TABLE t1(id INT);
#TODO: add this when "MDEV-5359 CREATE OR REPLACE..." is done --error ER_PROCACCESS_DENIED_ERROR
#DELIMITER $; CREATE OR REPLACE PROCEDURE proc1 (OUT cnt INT) BEGIN END;
#--error ER_PROCACCESS_DENIED_ERROR
#CREATE OR REPLACE PROCEDURE proc1 (OUT cnt INT) BEGIN SELECT COUNT(*) INTO cnt FROM t1; END$ --error ER_DBACCESS_DENIED_ERROR
#DELIMITER ;$ CREATE OR REPLACE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
#
#--error ER_DBACCESS_DENIED_ERROR --error ER_PROCACCESS_DENIED_ERROR
#CREATE OR REPLACE FUNCTION lookup RETURNS STRING SONAME "udf_example.so"; CREATE OR REPLACE FUNCTION hello(str char(20)) RETURNS TEXT RETURN CONCAT('Hello, ', str, '!');
#
#--error ER_PROCACCESS_DENIED_ERROR # TODO: add this when "MDEV-5359 CREATE OR REPLACE..." is done
#CREATE OR REPLACE FUNCTION hello(str char(20)) RETURNS TEXT RETURN CONCAT('Hello, ', str, '!');
#
#--error ER_SPECIFIC_ACCESS_DENIED_ERROR #--error ER_SPECIFIC_ACCESS_DENIED_ERROR
#CREATE OR REPLACE USER u1@localhost; #CREATE OR REPLACE USER u1@localhost;
# #
......
...@@ -1766,11 +1766,10 @@ CREATE IF NOT EXISTS PROCEDURE bug14702() ...@@ -1766,11 +1766,10 @@ CREATE IF NOT EXISTS PROCEDURE bug14702()
BEGIN BEGIN
END; END;
--error ER_PARSE_ERROR
CREATE PROCEDURE IF NOT EXISTS bug14702() CREATE PROCEDURE IF NOT EXISTS bug14702()
BEGIN BEGIN
END; END;
DROP PROCEDURE IF EXISTS bug14702;
# #
# BUG#20953: create proc with a create view that uses local # BUG#20953: create proc with a create view that uses local
......
...@@ -823,6 +823,8 @@ db_load_routine(THD *thd, stored_procedure_type type, ...@@ -823,6 +823,8 @@ db_load_routine(THD *thd, stored_procedure_type type,
thd->lex= &newlex; thd->lex= &newlex;
newlex.current_select= NULL; newlex.current_select= NULL;
// Resetting REPLACE and EXIST flags in create_info, for show_create_sp()
newlex.create_info.DDL_options_st::init();
defstr.set_charset(creation_ctx->get_client_cs()); defstr.set_charset(creation_ctx->get_client_cs());
...@@ -940,6 +942,51 @@ sp_returns_type(THD *thd, String &result, sp_head *sp) ...@@ -940,6 +942,51 @@ sp_returns_type(THD *thd, String &result, sp_head *sp)
} }
/**
Delete the record for the stored routine object from mysql.proc,
which is already opened, locked, and positioned to the record with the
record to be deleted.
The operation deletes the record for the current record in "table"
and invalidates the stored-routine cache.
@param thd Thread context.
@param type Stored routine type (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
@param name Stored routine name.
@param table A pointer to the opened mysql.proc table
@returns Error code.
@return SP_OK on success, or SP_DELETE_ROW_FAILED on error.
used to indicate about errors.
*/
static int
sp_drop_routine_internal(THD *thd, stored_procedure_type type,
sp_name *name, TABLE *table)
{
DBUG_ENTER("sp_drop_routine_internal");
if (table->file->ha_delete_row(table->record[0]))
DBUG_RETURN(SP_DELETE_ROW_FAILED);
/* Make change permanent and avoid 'table is marked as crashed' errors */
table->file->extra(HA_EXTRA_FLUSH);
sp_cache_invalidate();
/*
A lame workaround for lack of cache flush:
make sure the routine is at least gone from the
local cache.
*/
sp_head *sp;
sp_cache **spc= (type == TYPE_ENUM_FUNCTION ?
&thd->sp_func_cache : &thd->sp_proc_cache);
sp= sp_cache_lookup(spc, name);
if (sp)
sp_cache_flush_obsolete(spc, &sp);
DBUG_RETURN(SP_OK);
}
/** /**
Write stored-routine object into mysql.proc. Write stored-routine object into mysql.proc.
...@@ -966,6 +1013,7 @@ sp_returns_type(THD *thd, String &result, sp_head *sp) ...@@ -966,6 +1013,7 @@ sp_returns_type(THD *thd, String &result, sp_head *sp)
int int
sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
{ {
LEX *lex= thd->lex;
int ret; int ret;
TABLE *table; TABLE *table;
char definer_buf[USER_HOST_BUFF_SIZE]; char definer_buf[USER_HOST_BUFF_SIZE];
...@@ -1003,6 +1051,36 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) ...@@ -1003,6 +1051,36 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
ret= SP_OPEN_TABLE_FAILED; ret= SP_OPEN_TABLE_FAILED;
else else
{ {
/* Checking if the routine already exists */
if (db_find_routine_aux(thd, type, lex->spname, table) == SP_OK)
{
if (lex->create_info.or_replace())
{
if ((ret= sp_drop_routine_internal(thd, type, lex->spname, table)))
goto done;
}
else if (lex->create_info.if_not_exists())
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_SP_ALREADY_EXISTS, ER(ER_SP_ALREADY_EXISTS),
type == TYPE_ENUM_FUNCTION ?
"FUNCTION" : "PROCEDURE",
lex->spname->m_name.str);
ret= SP_OK;
// Setting retstr as it is used for logging.
if (sp->m_type == TYPE_ENUM_FUNCTION)
sp_returns_type(thd, retstr, sp);
goto log;
}
else
{
ret= SP_WRITE_ROW_FAILED;
goto done;
}
}
restore_record(table, s->default_values); // Get default values for fields restore_record(table, s->default_values); // Get default values for fields
/* NOTE: all needed privilege checks have been already done. */ /* NOTE: all needed privilege checks have been already done. */
...@@ -1166,38 +1244,39 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) ...@@ -1166,38 +1244,39 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
if (ret == SP_OK) if (ret == SP_OK)
sp_cache_invalidate(); sp_cache_invalidate();
}
if (ret == SP_OK && mysql_bin_log.is_open()) log:
if (ret == SP_OK && mysql_bin_log.is_open())
{
thd->clear_error();
String log_query;
log_query.set_charset(system_charset_info);
if (!show_create_sp(thd, &log_query,
sp->m_type,
(sp->m_explicit_name ? sp->m_db.str : NULL),
(sp->m_explicit_name ? sp->m_db.length : 0),
sp->m_name.str, sp->m_name.length,
sp->m_params.str, sp->m_params.length,
retstr.ptr(), retstr.length(),
sp->m_body.str, sp->m_body.length,
sp->m_chistics, &(thd->lex->definer->user),
&(thd->lex->definer->host),
saved_mode))
{ {
thd->clear_error(); ret= SP_INTERNAL_ERROR;
goto done;
String log_query;
log_query.set_charset(system_charset_info);
if (!show_create_sp(thd, &log_query,
sp->m_type,
(sp->m_explicit_name ? sp->m_db.str : NULL),
(sp->m_explicit_name ? sp->m_db.length : 0),
sp->m_name.str, sp->m_name.length,
sp->m_params.str, sp->m_params.length,
retstr.ptr(), retstr.length(),
sp->m_body.str, sp->m_body.length,
sp->m_chistics, &(thd->lex->definer->user),
&(thd->lex->definer->host),
saved_mode))
{
ret= SP_INTERNAL_ERROR;
goto done;
}
/* restore sql_mode when binloging */
thd->variables.sql_mode= saved_mode;
/* Such a statement can always go directly to binlog, no trans cache */
if (thd->binlog_query(THD::STMT_QUERY_TYPE,
log_query.ptr(), log_query.length(),
FALSE, FALSE, FALSE, 0))
ret= SP_INTERNAL_ERROR;
thd->variables.sql_mode= 0;
} }
/* restore sql_mode when binloging */
thd->variables.sql_mode= saved_mode;
/* Such a statement can always go directly to binlog, no trans cache */
if (thd->binlog_query(THD::STMT_QUERY_TYPE,
log_query.ptr(), log_query.length(),
FALSE, FALSE, FALSE, 0))
ret= SP_INTERNAL_ERROR;
thd->variables.sql_mode= 0;
} }
done: done:
...@@ -1209,7 +1288,8 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) ...@@ -1209,7 +1288,8 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
/** /**
Delete the record for the stored routine object from mysql.proc. Delete the record for the stored routine object from mysql.proc
and do binary logging.
The operation deletes the record for the stored routine specified by name The operation deletes the record for the stored routine specified by name
from the mysql.proc table and invalidates the stored-routine cache. from the mysql.proc table and invalidates the stored-routine cache.
...@@ -1244,39 +1324,17 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name) ...@@ -1244,39 +1324,17 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
if (!(table= open_proc_table_for_update(thd))) if (!(table= open_proc_table_for_update(thd)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED); DBUG_RETURN(SP_OPEN_TABLE_FAILED);
if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK)
ret= sp_drop_routine_internal(thd, type, name, table);
if (ret == SP_OK &&
write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
ret= SP_INTERNAL_ERROR;
/* /*
This statement will be replicated as a statement, even when using This statement will be replicated as a statement, even when using
row-based replication. The flag will be reset at the end of the row-based replication. The flag will be reset at the end of the
statement. statement.
*/ */
if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK)
{
if (table->file->ha_delete_row(table->record[0]))
ret= SP_DELETE_ROW_FAILED;
/* Make change permanent and avoid 'table is marked as crashed' errors */
table->file->extra(HA_EXTRA_FLUSH);
}
if (ret == SP_OK)
{
if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
ret= SP_INTERNAL_ERROR;
sp_cache_invalidate();
/*
A lame workaround for lack of cache flush:
make sure the routine is at least gone from the
local cache.
*/
{
sp_head *sp;
sp_cache **spc= (type == TYPE_ENUM_FUNCTION ?
&thd->sp_func_cache : &thd->sp_proc_cache);
sp= sp_cache_lookup(spc, name);
if (sp)
sp_cache_flush_obsolete(spc, &sp);
}
}
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
DBUG_RETURN(ret); DBUG_RETURN(ret);
} }
...@@ -2136,11 +2194,16 @@ show_create_sp(THD *thd, String *buf, ...@@ -2136,11 +2194,16 @@ show_create_sp(THD *thd, String *buf,
thd->variables.sql_mode= sql_mode; thd->variables.sql_mode= sql_mode;
buf->append(STRING_WITH_LEN("CREATE ")); buf->append(STRING_WITH_LEN("CREATE "));
if (thd->lex->create_info.or_replace())
buf->append(STRING_WITH_LEN("OR REPLACE "));
append_definer(thd, buf, definer_user, definer_host); append_definer(thd, buf, definer_user, definer_host);
if (type == TYPE_ENUM_FUNCTION) if (type == TYPE_ENUM_FUNCTION)
buf->append(STRING_WITH_LEN("FUNCTION ")); buf->append(STRING_WITH_LEN("FUNCTION "));
else else
buf->append(STRING_WITH_LEN("PROCEDURE ")); buf->append(STRING_WITH_LEN("PROCEDURE "));
if (thd->lex->create_info.if_not_exists())
buf->append(STRING_WITH_LEN("IF NOT EXISTS "));
if (dblen > 0) if (dblen > 0)
{ {
append_identifier(thd, buf, db, dblen); append_identifier(thd, buf, db, dblen);
......
...@@ -4480,7 +4480,9 @@ mysql_execute_command(THD *thd) ...@@ -4480,7 +4480,9 @@ mysql_execute_command(THD *thd)
#endif #endif
case SQLCOM_CREATE_FUNCTION: // UDF function case SQLCOM_CREATE_FUNCTION: // UDF function
{ {
if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 0)) if (check_access(thd, lex->create_info.or_replace() ?
(INSERT_ACL | DELETE_ACL) : INSERT_ACL,
"mysql", NULL, NULL, 1, 0))
break; break;
#ifdef HAVE_DLOPEN #ifdef HAVE_DLOPEN
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
...@@ -4950,6 +4952,15 @@ mysql_execute_command(THD *thd) ...@@ -4950,6 +4952,15 @@ mysql_execute_command(THD *thd)
goto create_sp_error; goto create_sp_error;
} }
/* Checking the drop permissions if CREATE OR REPLACE is used */
if (lex->create_info.or_replace())
{
if (check_routine_access(thd, ALTER_PROC_ACL, lex->spname->m_db.str,
lex->spname->m_name.str,
lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
goto create_sp_error;
}
name= lex->sphead->name(&namelen); name= lex->sphead->name(&namelen);
#ifdef HAVE_DLOPEN #ifdef HAVE_DLOPEN
if (lex->sphead->m_type == TYPE_ENUM_FUNCTION) if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
......
...@@ -413,6 +413,50 @@ static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl, ...@@ -413,6 +413,50 @@ static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl,
return tmp; return tmp;
} }
/*
Drop user defined function.
@param thd Thread handler.
@param udf Existing udf_func pointer which is to be deleted.
@param table mysql.func table reference (opened and locked)
Assumption
- udf is not null.
- table is already opened and locked
*/
static int mysql_drop_function_internal(THD *thd, udf_func *udf, TABLE *table)
{
DBUG_ENTER("mysql_drop_function_internal");
char *exact_name_str= udf->name.str;
uint exact_name_len= udf->name.length;
del_udf(udf);
/*
Close the handle if this was function that was found during boot or
CREATE FUNCTION and it's not in use by any other udf function
*/
if (udf->dlhandle && !find_udf_dl(udf->dl))
dlclose(udf->dlhandle);
if (!table)
DBUG_RETURN(1);
table->use_all_columns();
table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin);
if (!table->file->ha_index_read_idx_map(table->record[0], 0,
(uchar*) table->field[0]->ptr,
HA_WHOLE_KEY,
HA_READ_KEY_EXACT))
{
int error;
if ((error= table->file->ha_delete_row(table->record[0])))
table->file->print_error(error, MYF(0));
}
DBUG_RETURN(0);
}
/** /**
Create a user defined function. Create a user defined function.
...@@ -466,10 +510,26 @@ int mysql_create_function(THD *thd,udf_func *udf) ...@@ -466,10 +510,26 @@ int mysql_create_function(THD *thd,udf_func *udf)
mysql_rwlock_wrlock(&THR_LOCK_udf); mysql_rwlock_wrlock(&THR_LOCK_udf);
DEBUG_SYNC(current_thd, "mysql_create_function_after_lock"); DEBUG_SYNC(current_thd, "mysql_create_function_after_lock");
if ((my_hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length))) if ((u_d= (udf_func*) my_hash_search(&udf_hash, (uchar*) udf->name.str,
udf->name.length)))
{ {
my_error(ER_UDF_EXISTS, MYF(0), udf->name.str); if (thd->lex->create_info.or_replace())
goto err; {
if ((error= mysql_drop_function_internal(thd, u_d, table)))
goto err;
}
else if (thd->lex->create_info.if_not_exists())
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_UDF_EXISTS,
ER(ER_UDF_EXISTS), udf->name.str);
goto done;
}
else
{
my_error(ER_UDF_EXISTS, MYF(0), udf->name.str);
goto err;
}
} }
if (!(dl = find_udf_dl(udf->dl))) if (!(dl = find_udf_dl(udf->dl)))
{ {
...@@ -496,16 +556,16 @@ int mysql_create_function(THD *thd,udf_func *udf) ...@@ -496,16 +556,16 @@ int mysql_create_function(THD *thd,udf_func *udf)
goto err; goto err;
} }
} }
udf->name.str=strdup_root(&mem,udf->name.str); udf->name.str= strdup_root(&mem,udf->name.str);
udf->dl=strdup_root(&mem,udf->dl); udf->dl= strdup_root(&mem,udf->dl);
if (!(u_d=add_udf(&udf->name,udf->returns,udf->dl,udf->type))) if (!(u_d=add_udf(&udf->name,udf->returns,udf->dl,udf->type)))
goto err; goto err;
u_d->dlhandle = dl; u_d->dlhandle= dl;
u_d->func=udf->func; u_d->func= udf->func;
u_d->func_init=udf->func_init; u_d->func_init= udf->func_init;
u_d->func_deinit=udf->func_deinit; u_d->func_deinit= udf->func_deinit;
u_d->func_clear=udf->func_clear; u_d->func_clear= udf->func_clear;
u_d->func_add=udf->func_add; u_d->func_add= udf->func_add;
/* create entry in mysql.func table */ /* create entry in mysql.func table */
...@@ -527,6 +587,8 @@ int mysql_create_function(THD *thd,udf_func *udf) ...@@ -527,6 +587,8 @@ int mysql_create_function(THD *thd,udf_func *udf)
del_udf(u_d); del_udf(u_d);
goto err; goto err;
} }
done:
mysql_rwlock_unlock(&THR_LOCK_udf); mysql_rwlock_unlock(&THR_LOCK_udf);
/* Binlog the create function. */ /* Binlog the create function. */
...@@ -535,7 +597,7 @@ int mysql_create_function(THD *thd,udf_func *udf) ...@@ -535,7 +597,7 @@ int mysql_create_function(THD *thd,udf_func *udf)
DBUG_RETURN(0); DBUG_RETURN(0);
err: err:
if (new_dl) if (new_dl)
dlclose(dl); dlclose(dl);
mysql_rwlock_unlock(&THR_LOCK_udf); mysql_rwlock_unlock(&THR_LOCK_udf);
...@@ -548,8 +610,6 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) ...@@ -548,8 +610,6 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
TABLE *table; TABLE *table;
TABLE_LIST tables; TABLE_LIST tables;
udf_func *udf; udf_func *udf;
char *exact_name_str;
uint exact_name_len;
DBUG_ENTER("mysql_drop_function"); DBUG_ENTER("mysql_drop_function");
if (!initialized) if (!initialized)
...@@ -567,35 +627,25 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) ...@@ -567,35 +627,25 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
mysql_rwlock_wrlock(&THR_LOCK_udf); mysql_rwlock_wrlock(&THR_LOCK_udf);
DEBUG_SYNC(current_thd, "mysql_drop_function_after_lock"); DEBUG_SYNC(current_thd, "mysql_drop_function_after_lock");
if (!(udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) udf_name->str, if (!(udf= (udf_func*) my_hash_search(&udf_hash, (uchar*) udf_name->str,
(uint) udf_name->length))) (uint) udf_name->length)) )
{ {
if (thd->lex->check_exists)
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_FUNCTION_NOT_DEFINED, ER(ER_FUNCTION_NOT_DEFINED),
udf_name->str);
goto done;
}
my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str); my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
goto err; goto err;
} }
exact_name_str= udf->name.str;
exact_name_len= udf->name.length;
del_udf(udf);
/*
Close the handle if this was function that was found during boot or
CREATE FUNCTION and it's not in use by any other udf function
*/
if (udf->dlhandle && !find_udf_dl(udf->dl))
dlclose(udf->dlhandle);
if (!table) if (mysql_drop_function_internal(thd, udf, table))
goto err; goto err;
table->use_all_columns();
table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); done:
if (!table->file->ha_index_read_idx_map(table->record[0], 0,
(uchar*) table->field[0]->ptr,
HA_WHOLE_KEY,
HA_READ_KEY_EXACT))
{
int error;
if ((error = table->file->ha_delete_row(table->record[0])))
table->file->print_error(error, MYF(0));
}
mysql_rwlock_unlock(&THR_LOCK_udf); mysql_rwlock_unlock(&THR_LOCK_udf);
/* /*
......
...@@ -1936,7 +1936,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ...@@ -1936,7 +1936,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
view_algorithm view_or_trigger_or_sp_or_event view_algorithm view_or_trigger_or_sp_or_event
definer_tail no_definer_tail definer_tail no_definer_tail
view_suid view_tail view_list_opt view_list view_select view_suid view_tail view_list_opt view_list view_select
view_check_option trigger_tail sp_tail sf_tail udf_tail event_tail view_check_option trigger_tail sp_tail sf_tail event_tail
udf_tail udf_tail2
install uninstall partition_entry binlog_base64_event install uninstall partition_entry binlog_base64_event
init_key_options normal_key_options normal_key_opts all_key_opt init_key_options normal_key_options normal_key_opts all_key_opt
spatial_key_options fulltext_key_options normal_key_opt spatial_key_options fulltext_key_options normal_key_opt
...@@ -2561,7 +2562,11 @@ create: ...@@ -2561,7 +2562,11 @@ create:
view_or_trigger_or_sp_or_event view_or_trigger_or_sp_or_event
{ {
// TODO: remove this when "MDEV-5359 CREATE OR REPLACE..." is done // TODO: remove this when "MDEV-5359 CREATE OR REPLACE..." is done
if ($1.or_replace() && Lex->sql_command != SQLCOM_CREATE_VIEW) if ($1.or_replace() &&
Lex->sql_command != SQLCOM_CREATE_VIEW &&
Lex->sql_command != SQLCOM_CREATE_FUNCTION &&
Lex->sql_command != SQLCOM_CREATE_SPFUNCTION &&
Lex->sql_command != SQLCOM_CREATE_PROCEDURE)
{ {
my_error(ER_WRONG_USAGE, MYF(0), "OR REPLACE", my_error(ER_WRONG_USAGE, MYF(0), "OR REPLACE",
"TRIGGERS / SP / EVENT"); "TRIGGERS / SP / EVENT");
...@@ -16218,45 +16223,32 @@ trigger_tail: ...@@ -16218,45 +16223,32 @@ trigger_tail:
**************************************************************************/ **************************************************************************/
udf_tail: udf_tail:
AGGREGATE_SYM remember_name FUNCTION_SYM ident AGGREGATE_SYM udf_tail2 { thd->lex->udf.type= UDFTYPE_AGGREGATE; }
| udf_tail2 { thd->lex->udf.type= UDFTYPE_FUNCTION; }
;
udf_tail2:
FUNCTION_SYM opt_if_not_exists ident
RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
{ {
LEX *lex= thd->lex; LEX *lex= thd->lex;
if (is_native_function(thd, & $4)) if (lex->add_create_options_with_check($2))
{
my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0),
$4.str);
MYSQL_YYABORT; MYSQL_YYABORT;
}
lex->sql_command = SQLCOM_CREATE_FUNCTION;
lex->udf.type= UDFTYPE_AGGREGATE;
lex->stmt_definition_begin= $2;
lex->udf.name = $4;
lex->udf.returns=(Item_result) $6;
lex->udf.dl=$8.str;
}
| remember_name FUNCTION_SYM ident
RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
{
LEX *lex= thd->lex;
if (is_native_function(thd, & $3)) if (is_native_function(thd, & $3))
{ {
my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0), my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0), $3.str);
$3.str);
MYSQL_YYABORT; MYSQL_YYABORT;
} }
lex->sql_command = SQLCOM_CREATE_FUNCTION; lex->sql_command= SQLCOM_CREATE_FUNCTION;
lex->udf.type= UDFTYPE_FUNCTION; lex->udf.name= $3;
lex->stmt_definition_begin= $1; lex->udf.returns= (Item_result) $5;
lex->udf.name = $3; lex->udf.dl= $7.str;
lex->udf.returns=(Item_result) $5;
lex->udf.dl=$7.str;
} }
; ;
sf_tail: sf_tail:
remember_name /* $1 */ FUNCTION_SYM /* $1 */
FUNCTION_SYM /* $2 */ opt_if_not_exists /* $2 */
sp_name /* $3 */ sp_name /* $3 */
'(' /* $4 */ '(' /* $4 */
{ /* $5 */ { /* $5 */
...@@ -16264,7 +16256,8 @@ sf_tail: ...@@ -16264,7 +16256,8 @@ sf_tail:
Lex_input_stream *lip= YYLIP; Lex_input_stream *lip= YYLIP;
const char* tmp_param_begin; const char* tmp_param_begin;
lex->stmt_definition_begin= $1; if (lex->add_create_options_with_check($2))
MYSQL_YYABORT;
lex->spname= $3; lex->spname= $3;
if (lex->sphead) if (lex->sphead)
...@@ -16359,18 +16352,20 @@ sf_tail: ...@@ -16359,18 +16352,20 @@ sf_tail:
; ;
sp_tail: sp_tail:
PROCEDURE_SYM remember_name sp_name PROCEDURE_SYM opt_if_not_exists sp_name
{ {
if (Lex->add_create_options_with_check($2))
MYSQL_YYABORT;
if (Lex->sphead) if (Lex->sphead)
{ {
my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE");
MYSQL_YYABORT; MYSQL_YYABORT;
} }
Lex->stmt_definition_begin= $2;
if (!make_sp_head(thd, $3, TYPE_ENUM_PROCEDURE)) if (!make_sp_head(thd, $3, TYPE_ENUM_PROCEDURE))
MYSQL_YYABORT; MYSQL_YYABORT;
Lex->spname= $3;
} }
'(' '('
{ {
......
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