diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index 6e53347b0a6..b8607af3b3c 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -24,6 +24,7 @@ bk@admin.bk bk@mysql.r18.ru carsten@tsort.bitbybit.dk davida@isil.mysql.com +dlenev@brandersnatch.localdomain dlenev@build.mysql.com dlenev@mysql.com gerberb@ou800.zenez.com diff --git a/Docs/sp-imp-spec.txt b/Docs/sp-imp-spec.txt index 24a47aa2c38..ac17a375926 100644 --- a/Docs/sp-imp-spec.txt +++ b/Docs/sp-imp-spec.txt @@ -1057,9 +1057,9 @@ CREATE TABLE proc ( db char(64) binary DEFAULT '' NOT NULL, - name char(64) binary DEFAULT '' NOT NULL, + name char(64) DEFAULT '' NOT NULL, type enum('FUNCTION','PROCEDURE') NOT NULL, - specific_name char(64) binary DEFAULT '' NOT NULL, + specific_name char(64) DEFAULT '' NOT NULL, language enum('SQL') DEFAULT 'SQL' NOT NULL, sql_data_access enum('CONTAINS_SQL') DEFAULT 'CONTAINS_SQL' NOT NULL, is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL, diff --git a/include/mysqld_error.h b/include/mysqld_error.h index a3e063d4ed7..113eb07ed66 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -339,4 +339,5 @@ #define ER_SP_DUP_CURS 1320 #define ER_SP_CANT_ALTER 1321 #define ER_SP_SUBSELECT_NYI 1322 -#define ER_ERROR_MESSAGES 323 +#define ER_SP_NO_USE 1323 +#define ER_ERROR_MESSAGES 324 diff --git a/include/sql_state.h b/include/sql_state.h index f4fbe434490..c5a71daceb9 100644 --- a/include/sql_state.h +++ b/include/sql_state.h @@ -196,3 +196,4 @@ ER_SP_DUP_COND, "42000", "", ER_SP_DUP_CURS, "42000", "", /*ER_SP_CANT_ALTER*/ ER_SP_SUBSELECT_NYI, "0A000", "", +ER_SP_NO_USE, "42000", "", diff --git a/mysql-test/r/index_merge.result b/mysql-test/r/index_merge.result index 03202427fc8..e68ba2dc4ab 100644 --- a/mysql-test/r/index_merge.result +++ b/mysql-test/r/index_merge.result @@ -314,4 +314,24 @@ key1 key2 key3 key4 key5 key6 key7 key8 key9 keyA keyB keyC 11 11 11 11 11 11 11 1013 11 11 11 11 12 12 12 12 12 12 12 1012 12 12 12 12 1016 1016 1016 1016 1016 1016 1016 8 1016 1016 1016 1016 +explain select * from t0 where key1 < 3 or key2 < 4; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 7 Using where +select * from t0 where key1 < 3 or key2 < 4; +key1 key2 key3 key4 key5 key6 key7 key8 +1 1 1 1 1 1 1 1023 +2 2 2 2 2 2 2 1022 +3 3 3 3 3 3 3 1021 +update t0 set key8=123 where key1 < 3 or key2 < 4; +select * from t0 where key1 < 3 or key2 < 4; +key1 key2 key3 key4 key5 key6 key7 key8 +1 1 1 1 1 1 1 123 +2 2 2 2 2 2 2 123 +3 3 3 3 3 3 3 123 +delete from t0 where key1 < 3 or key2 < 4; +select * from t0 where key1 < 3 or key2 < 4; +key1 key2 key3 key4 key5 key6 key7 key8 +select count(*) from t0; +count(*) +1021 drop table t0, t1, t2, t3, t4; diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index 3fa700ccf26..4645e7d9b9c 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -144,7 +144,6 @@ insert into t1 values (1); show open tables; Database Table In_use Name_locked test t1 0 0 -mysql proc 0 0 drop table t1; create table t1 (a int not null, b VARCHAR(10), INDEX (b) ) AVG_ROW_LENGTH=10 CHECKSUM=1 COMMENT="test" ENGINE=MYISAM MIN_ROWS=10 MAX_ROWS=100 PACK_KEYS=1 DELAY_KEY_WRITE=1 ROW_FORMAT=fixed; show create table t1; diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index f965cf90eb4..1877789e2b0 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -251,6 +251,10 @@ declare c cursor for select * from t1; declare c cursor for select field from t1; end| ERROR 42000: Duplicate cursor: c +create procedure u() +use sptmp; +#| +ERROR 42000: USE is not allowed in a stored procedure create procedure bug1965() begin declare c cursor for select val from t1 order by valname; diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index 9d5f71225b6..60adad0181c 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -1,5 +1,6 @@ use test; -grant usage on *.* to dummy@localhost; +grant usage on *.* to user1@localhost; +flush privileges; drop database if exists db1_secret; create database db1_secret; use db1_secret; @@ -7,39 +8,104 @@ create table t1 ( u varchar(64), i int ); create procedure stamp(i int) insert into db1_secret.t1 values (user(), i); show procedure status like 'stamp'; -Name Type Definer Modified Created Security_type Comment -stamp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +Db Name Type Definer Modified Created Security_type Comment +db1_secret stamp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +create function db() returns varchar(64) return database(); +show function status like 'db'; +Db Name Type Definer Modified Created Security_type Comment +db1_secret db FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER call stamp(1); select * from t1; u i root@localhost 1 -call stamp(2); +select db(); +db() +db1_secret +call db1_secret.stamp(2); +select db1_secret.db(); +db1_secret.db() +db1_secret select * from db1_secret.t1; -ERROR 42000: Access denied for user: 'dummy'@'localhost' to database 'db1_secret' -call stamp(3); +ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret' +call db1_secret.stamp(3); +select db1_secret.db(); +db1_secret.db() +db1_secret select * from db1_secret.t1; ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret' select * from t1; u i root@localhost 1 -dummy@localhost 2 +user1@localhost 2 anon@localhost 3 alter procedure stamp sql security invoker; show procedure status like 'stamp'; -Name Type Definer Modified Created Security_type Comment -stamp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER +Db Name Type Definer Modified Created Security_type Comment +db1_secret stamp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER +alter function db sql security invoker; +show function status like 'db'; +Db Name Type Definer Modified Created Security_type Comment +db1_secret db FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER call stamp(4); select * from t1; u i root@localhost 1 -dummy@localhost 2 +user1@localhost 2 anon@localhost 3 root@localhost 4 -call stamp(5); -ERROR 42000: Access denied for user: 'dummy'@'localhost' to database 'db1_secret' -call stamp(6); +select db(); +db() +db1_secret +call db1_secret.stamp(5); +ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret' +select db1_secret.db(); +ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret' +call db1_secret.stamp(6); ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret' -drop procedure stamp; +select db1_secret.db(); +ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret' +drop database if exists db2; +create database db2; +use db2; +create table t2 (s1 int); +insert into t2 values (0); +grant usage on db2.* to user1@localhost; +grant select on db2.* to user1@localhost; +grant usage on db2.* to user2@localhost; +grant select,insert,update,delete on db2.* to user2@localhost; +flush privileges; +use db2; +create procedure p () insert into t2 values (1); +call p(); +ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db2' +use db2; +call p(); +ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db2' +select * from t2; +s1 +0 +create procedure q () insert into t2 values (2); +call q(); +select * from t2; +s1 +0 +2 +use db2; +call q(); +select * from t2; +s1 +0 +2 +2 use test; +select type,db,name from mysql.proc; +type db name +FUNCTION db1_secret db +PROCEDURE db1_secret stamp +PROCEDURE db2 p +PROCEDURE db2 q drop database db1_secret; -delete from mysql.user where user='dummy'; +drop database db2; +select type,db,name from mysql.proc; +type db name +delete from mysql.user where user='user1' or user='user2'; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index bf515e4b4cd..1f3064605bc 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -18,17 +18,6 @@ id data foo 42 delete from t1; drop procedure foo42; -create procedure u() -use sptmp; -drop database if exists sptmp; -create database sptmp; -use test; -call u(); -select database(); -database() -test -drop database sptmp; -drop procedure u; create procedure bar(x char(16), y int) insert into test.t1 values (x, y); call bar("bar", 666); @@ -746,7 +735,7 @@ delete from t1| alter procedure chistics sql security invoker name chistics2| show create procedure chistics2| Procedure Create Procedure -chistics2 CREATE PROCEDURE `chistics2`() +chistics2 CREATE PROCEDURE `test`.`chistics2`() SQL SECURITY INVOKER COMMENT 'Characteristics procedure test' insert into t1 values ("chistics", 1) @@ -763,7 +752,7 @@ chistics() alter function chistics name chistics2 comment 'Characteristics function test'| show create function chistics2| Function Create Function -chistics2 CREATE FUNCTION `chistics2`() RETURNS int +chistics2 CREATE FUNCTION `test`.`chistics2`() RETURNS int DETERMINISTIC SQL SECURITY INVOKER COMMENT 'Characteristics function test' @@ -797,6 +786,22 @@ select @c1, @c2| 12 3 delete from t1| drop procedure modes| +create database sp_db1| +drop database sp_db1| +create database sp_db2| +use sp_db2| +create table t3 ( s char(4), t int )| +insert into t3 values ("abcd", 42), ("dcba", 666)| +use test| +drop database sp_db2| +create database sp_db3| +use sp_db3| +create procedure dummy(out x int) +set x = 42| +use test| +drop database sp_db3| +select type,db,name from mysql.proc where db = 'sp_db3'| +type db name create procedure bug822(a_id char(16), a_data int) begin declare n int; @@ -939,23 +944,23 @@ begin show create function fac; end| call bug2267_1()| -Name Type Definer Modified Created Security_type Comment -bug2267_1 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER -bug2267_2 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER -bug2267_3 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER -bug2267_4 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +Db Name Type Definer Modified Created Security_type Comment +test bug2267_1 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +test bug2267_2 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +test bug2267_3 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +test bug2267_4 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER call bug2267_2()| -Name Type Definer Modified Created Security_type Comment -fac FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +Db Name Type Definer Modified Created Security_type Comment +test fac FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER call bug2267_3()| Procedure Create Procedure -bug2267_1 CREATE PROCEDURE `bug2267_1`() +bug2267_1 CREATE PROCEDURE `test`.`bug2267_1`() begin show procedure status; end call bug2267_4()| Function Create Function -fac CREATE FUNCTION `fac`(n int unsigned) RETURNS bigint unsigned +fac CREATE FUNCTION `test`.`fac`(n int unsigned) RETURNS bigint unsigned begin declare f bigint unsigned default 1; while n > 1 do @@ -989,6 +994,24 @@ call bug2614()| call bug2614()| drop table t3| drop procedure bug2614| +create function bug2674 () returns int +return @@sort_buffer_size| +select bug2674()| +bug2674() +262136 +drop function bug2674| +create procedure bug3259_1 () begin end| +create procedure BUG3259_2 () begin end| +create procedure Bug3259_3 () begin end| +call BUG3259_1()| +call BUG3259_1()| +call bug3259_2()| +call Bug3259_2()| +call bug3259_3()| +call bUG3259_3()| +drop procedure bUg3259_1| +drop procedure BuG3259_2| +drop procedure BUG3259_3| drop table if exists fac| create table fac (n int unsigned not null primary key, f bigint unsigned)| create procedure ifac(n int unsigned) @@ -1029,12 +1052,12 @@ n f 20 2432902008176640000 drop table fac| show function status like '%f%'| -Name Type Definer Modified Created Security_type Comment -fac FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +Db Name Type Definer Modified Created Security_type Comment +test fac FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER drop procedure ifac| drop function fac| show function status like '%f%'| -Name Type Definer Modified Created Security_type Comment +Db Name Type Definer Modified Created Security_type Comment drop table if exists primes| create table primes ( i int unsigned not null primary key, @@ -1095,7 +1118,7 @@ end while; end| show create procedure opp| Procedure Create Procedure -opp CREATE PROCEDURE `opp`(n bigint unsigned, out pp bool) +opp CREATE PROCEDURE `test`.`opp`(n bigint unsigned, out pp bool) begin declare r double; declare b, s bigint unsigned default 0; @@ -1122,9 +1145,9 @@ end if; end loop; end show procedure status like '%p%'| -Name Type Definer Modified Created Security_type Comment -ip PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER -opp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +Db Name Type Definer Modified Created Security_type Comment +test ip PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +test opp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER call ip(200)| select * from primes where i=45 or i=100 or i=199| i p @@ -1135,7 +1158,7 @@ drop table primes| drop procedure opp| drop procedure ip| show procedure status like '%p%'| -Name Type Definer Modified Created Security_type Comment +Db Name Type Definer Modified Created Security_type Comment drop table if exists fib| create table fib ( f bigint unsigned not null )| insert into fib values (1), (1)| @@ -1185,19 +1208,19 @@ create procedure bar(x char(16), y int) comment "111111111111" sql security invoker insert into test.t1 values (x, y)| show procedure status like 'bar'| -Name Type Definer Modified Created Security_type Comment -bar PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER 111111111111 +Db Name Type Definer Modified Created Security_type Comment +test bar PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER 111111111111 alter procedure bar name bar2 comment "2222222222" sql security definer| alter procedure bar2 name bar comment "3333333333"| alter procedure bar| show create procedure bar| Procedure Create Procedure -bar CREATE PROCEDURE `bar`(x char(16), y int) +bar CREATE PROCEDURE `test`.`bar`(x char(16), y int) COMMENT '3333333333' insert into test.t1 values (x, y) show procedure status like 'bar'| -Name Type Definer Modified Created Security_type Comment -bar PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER 3333333333 +Db Name Type Definer Modified Created Security_type Comment +test bar PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER 3333333333 drop procedure bar| drop table t1; drop table t2; diff --git a/mysql-test/r/status.result b/mysql-test/r/status.result index 3134fdcf5ee..e9616232fa1 100644 --- a/mysql-test/r/status.result +++ b/mysql-test/r/status.result @@ -14,6 +14,6 @@ update t1 set n = 3; unlock tables; show status like 'Table_lock%'; Variable_name Value -Table_locks_immediate 4 +Table_locks_immediate 3 Table_locks_waited 1 drop table t1; diff --git a/mysql-test/t/index_merge.test b/mysql-test/t/index_merge.test index 029c2e4a013..9745723ca59 100644 --- a/mysql-test/t/index_merge.test +++ b/mysql-test/t/index_merge.test @@ -267,5 +267,15 @@ select * from t3 where key5=5 or key6=6 or key7=7 or key8=8 or key9=9 or keyA=10 or keyB=11 or keyC=12; +# Test for Bug#3183 +explain select * from t0 where key1 < 3 or key2 < 4; +select * from t0 where key1 < 3 or key2 < 4; + +update t0 set key8=123 where key1 < 3 or key2 < 4; +select * from t0 where key1 < 3 or key2 < 4; + +delete from t0 where key1 < 3 or key2 < 4; +select * from t0 where key1 < 3 or key2 < 4; +select count(*) from t0; drop table t0, t1, t2, t3, t4; diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index 68a5ccdbe42..43b5c04766a 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -330,6 +330,12 @@ begin declare c cursor for select field from t1; end| +# USE is not allowed +--error 1323 +create procedure u() + use sptmp; + + # # BUG#1965 # diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index 0d77b53210e..8fc51357bc4 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -7,8 +7,9 @@ connect (con1root,localhost,root,,); connection con1root; use test; -# Create dummy user with no particular access rights -grant usage on *.* to dummy@localhost; +# Create user user1 with no particular access rights +grant usage on *.* to user1@localhost; +flush privileges; --disable_warnings drop database if exists db1_secret; @@ -20,26 +21,32 @@ use db1_secret; create table t1 ( u varchar(64), i int ); -# Our test procedure +# A test procedure and function create procedure stamp(i int) insert into db1_secret.t1 values (user(), i); ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show procedure status like 'stamp'; +create function db() returns varchar(64) return database(); +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' +show function status like 'db'; + # root can, of course call stamp(1); select * from t1; +select db(); -connect (con2dummy,localhost,dummy,,); +connect (con2user1,localhost,user1,,); connect (con3anon,localhost,anon,,); # -# Dummy can +# User1 can # -connection con2dummy; +connection con2user1; # This should work... -call stamp(2); +call db1_secret.stamp(2); +select db1_secret.db(); # ...but not this --error 1044 @@ -51,7 +58,8 @@ select * from db1_secret.t1; connection con3anon; # This should work... -call stamp(3); +call db1_secret.stamp(3); +select db1_secret.db(); # ...but not this --error 1044 @@ -67,21 +75,28 @@ select * from t1; # Change to invoker's rights # alter procedure stamp sql security invoker; ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show procedure status like 'stamp'; +alter function db sql security invoker; +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' +show function status like 'db'; + # root still can call stamp(4); select * from t1; +select db(); # -# Dummy cannot +# User1 cannot # -connection con2dummy; +connection con2user1; # This should not work --error 1044 -call stamp(5); +call db1_secret.stamp(5); +--error 1044 +select db1_secret.db(); # # Anonymous cannot @@ -90,11 +105,69 @@ connection con3anon; # This should not work --error 1044 -call stamp(6); +call db1_secret.stamp(6); +--error 1044 +select db1_secret.db(); + +# +# BUG#2777 +# + +connection con1root; +--disable_warnings +drop database if exists db2; +--enable_warnings +create database db2; + +use db2; + +create table t2 (s1 int); +insert into t2 values (0); + +grant usage on db2.* to user1@localhost; +grant select on db2.* to user1@localhost; +grant usage on db2.* to user2@localhost; +grant select,insert,update,delete on db2.* to user2@localhost; +flush privileges; + +connection con2user1; +use db2; + +create procedure p () insert into t2 values (1); + +# Check that this doesn't work. +--error 1044 +call p(); + +connect (con4user2,localhost,user2,,); + +connection con4user2; +use db2; + +# This should not work, since p is executed with definer's (user1's) rights. +--error 1044 +call p(); +select * from t2; + +create procedure q () insert into t2 values (2); + +call q(); +select * from t2; + +connection con2user1; +use db2; + +# This should work +call q(); +select * from t2; # Clean up connection con1root; -drop procedure stamp; use test; +select type,db,name from mysql.proc; drop database db1_secret; -delete from mysql.user where user='dummy'; +drop database db2; +# Make sure the routines are gone +select type,db,name from mysql.proc; +# Get rid of the users +delete from mysql.user where user='user1' or user='user2'; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 3cb88ec5717..69433514728 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -31,21 +31,6 @@ delete from t1; drop procedure foo42; -# USE test: Make sure we remain in the same DB. -create procedure u() - use sptmp; - ---disable_warnings -drop database if exists sptmp; ---enable_warnings -create database sptmp; -use test; -call u(); -select database(); -drop database sptmp; -drop procedure u; - - # Single statement, two IN params. create procedure bar(x char(16), y int) insert into test.t1 values (x, y); @@ -920,6 +905,32 @@ delete from t1| drop procedure modes| +# Check that dropping a database without routines works. +# (Dropping with routines is tested in sp-security.test) +# First an empty db. +create database sp_db1| +drop database sp_db1| + +# Again, with a table. +create database sp_db2| +use sp_db2| +# Just put something in here... +create table t3 ( s char(4), t int )| +insert into t3 values ("abcd", 42), ("dcba", 666)| +use test| +drop database sp_db2| + +# And yet again, with just a procedure. +create database sp_db3| +use sp_db3| +create procedure dummy(out x int) + set x = 42| +use test| +drop database sp_db3| +# Check that it's gone +select type,db,name from mysql.proc where db = 'sp_db3'| + + # # Test cases for old bugs # @@ -1094,9 +1105,9 @@ begin show create function fac; end| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' call bug2267_1()| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' call bug2267_2()| call bug2267_3()| call bug2267_4()| @@ -1138,6 +1149,35 @@ call bug2614()| drop table t3| drop procedure bug2614| +# +# BUG#2674 +# + +create function bug2674 () returns int + return @@sort_buffer_size| + +select bug2674()| +drop function bug2674| + +# +# BUG#3259 +# + +create procedure bug3259_1 () begin end| +create procedure BUG3259_2 () begin end| +create procedure Bug3259_3 () begin end| + +call BUG3259_1()| +call BUG3259_1()| +call bug3259_2()| +call Bug3259_2()| +call bug3259_3()| +call bUG3259_3()| + +drop procedure bUg3259_1| +drop procedure BuG3259_2| +drop procedure BUG3259_3| + # # Some "real" examples @@ -1168,11 +1208,11 @@ end| call ifac(20)| select * from fac| drop table fac| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show function status like '%f%'| drop procedure ifac| drop function fac| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show function status like '%f%'| @@ -1249,7 +1289,7 @@ begin end while; end| show create procedure opp| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show procedure status like '%p%'| # This isn't the fastest way in the world to compute prime numbers, so @@ -1261,7 +1301,7 @@ select * from primes where i=45 or i=100 or i=199| drop table primes| drop procedure opp| drop procedure ip| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show procedure status like '%p%'| @@ -1308,13 +1348,13 @@ drop procedure fib| create procedure bar(x char(16), y int) comment "111111111111" sql security invoker insert into test.t1 values (x, y)| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show procedure status like 'bar'| alter procedure bar name bar2 comment "2222222222" sql security definer| alter procedure bar2 name bar comment "3333333333"| alter procedure bar| show create procedure bar| ---replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' show procedure status like 'bar'| drop procedure bar| delimiter ;| diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh index d20d745d514..06999b81ca1 100644 --- a/scripts/mysql_create_system_tables.sh +++ b/scripts/mysql_create_system_tables.sh @@ -290,9 +290,9 @@ if test ! -f $mdata/proc.frm then c_p="$c_p CREATE TABLE proc (" c_p="$c_p db char(64) binary DEFAULT '' NOT NULL," - c_p="$c_p name char(64) binary DEFAULT '' NOT NULL," + c_p="$c_p name char(64) DEFAULT '' NOT NULL," c_p="$c_p type enum('FUNCTION','PROCEDURE') NOT NULL," - c_p="$c_p specific_name char(64) binary DEFAULT '' NOT NULL," + c_p="$c_p specific_name char(64) DEFAULT '' NOT NULL," c_p="$c_p language enum('SQL') DEFAULT 'SQL' NOT NULL," c_p="$c_p sql_data_access enum('CONTAINS_SQL') DEFAULT 'CONTAINS_SQL' NOT NULL," c_p="$c_p is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL," diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql index 115236948c9..e64bf59353f 100644 --- a/scripts/mysql_fix_privilege_tables.sql +++ b/scripts/mysql_fix_privilege_tables.sql @@ -141,9 +141,9 @@ unique index (name) CREATE TABLE IF NOT EXISTS proc ( db char(64) binary DEFAULT '' NOT NULL, - name char(64) binary DEFAULT '' NOT NULL, + name char(64) DEFAULT '' NOT NULL, type enum('FUNCTION','PROCEDURE') NOT NULL, - specific_name char(64) binary DEFAULT '' NOT NULL, + specific_name char(64) DEFAULT '' NOT NULL, language enum('SQL') DEFAULT 'SQL' NOT NULL, sql_data_access enum('CONTAINS_SQL') DEFAULT 'CONTAINS_SQL' NOT NULL, is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL, @@ -179,3 +179,7 @@ CREATE TABLE IF NOT EXISTS proc ( comment char(64) binary DEFAULT '' NOT NULL, PRIMARY KEY (db,name,type) ) comment='Stored Procedures'; + +# Correct the name fields to not binary +ALTER TABLE proc MODIFY name char(64) DEFAULT '' NOT NULL, + MODIFY specific_name char(64) DEFAULT '' NOT NULL; diff --git a/sql/item_func.cc b/sql/item_func.cc index 5d9507f435e..4af389c28e5 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3127,6 +3127,25 @@ longlong Item_func_is_used_lock::val_int() return ull->thread_id; } + +Item_func_sp::Item_func_sp(sp_name *name) + :Item_func(), m_name(name), m_sp(NULL) +{ + m_name->init_qname(current_thd); +} + +Item_func_sp::Item_func_sp(sp_name *name, List &list) + :Item_func(list), m_name(name), m_sp(NULL) +{ + m_name->init_qname(current_thd); +} + +const char * +Item_func_sp::func_name() const +{ + return m_name->m_name.str; +} + int Item_func_sp::execute(Item **itp) { @@ -3138,9 +3157,13 @@ Item_func_sp::execute(Item **itp) #endif if (! m_sp) - m_sp= sp_find_function(thd, &m_name); + m_sp= sp_find_function(thd, m_name); if (! m_sp) + { + my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0), + "FUNCTION", m_name->m_qname); DBUG_RETURN(-1); + } #ifndef NO_EMBEDDED_ACCESS_CHECKS sp_change_security_context(thd, m_sp, &save_ctx); @@ -3161,12 +3184,14 @@ Item_func_sp::field_type() const DBUG_ENTER("Item_func_sp::field_type"); if (! m_sp) - m_sp= sp_find_function(current_thd, const_cast(&m_name)); + m_sp= sp_find_function(current_thd, m_name); if (m_sp) { DBUG_PRINT("info", ("m_returns = %d", m_sp->m_returns)); DBUG_RETURN(m_sp->m_returns); } + my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0), + "FUNCTION", m_name->m_qname); DBUG_RETURN(MYSQL_TYPE_STRING); } @@ -3177,11 +3202,13 @@ Item_func_sp::result_type() const DBUG_PRINT("info", ("m_sp = %p", m_sp)); if (! m_sp) - m_sp= sp_find_function(current_thd, const_cast(&m_name)); + m_sp= sp_find_function(current_thd, m_name); if (m_sp) { DBUG_RETURN(m_sp->result()); } + my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0), + "FUNCTION", m_name->m_qname); DBUG_RETURN(STRING_RESULT); } @@ -3191,8 +3218,13 @@ Item_func_sp::fix_length_and_dec() DBUG_ENTER("Item_func_sp::fix_length_and_dec"); if (! m_sp) - m_sp= sp_find_function(current_thd, &m_name); - if (m_sp) + m_sp= sp_find_function(current_thd, m_name); + if (! m_sp) + { + my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0), + "FUNCTION", m_name->m_qname); + } + else { switch (m_sp->result()) { case STRING_RESULT: diff --git a/sql/item_func.h b/sql/item_func.h index a1994fffc70..804b3be142d 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1075,32 +1075,26 @@ enum Cast_target */ class sp_head; +class sp_name; class Item_func_sp :public Item_func { private: - LEX_STRING m_name; + sp_name *m_name; mutable sp_head *m_sp; int execute(Item **itp); public: - Item_func_sp(LEX_STRING name) - :Item_func(), m_name(name), m_sp(NULL) - {} + Item_func_sp(sp_name *name); - Item_func_sp(LEX_STRING name, List &list) - :Item_func(list), m_name(name), m_sp(NULL) - {} + Item_func_sp(sp_name *name, List &list); virtual ~Item_func_sp() {} - const char *func_name() const - { - return m_name.str; - } + const char *func_name() const; enum enum_field_types field_type() const; @@ -1116,7 +1110,10 @@ public: Item *it; if (execute(&it)) + { + null_value= 1; return 0.0; + } return it->val(); } @@ -1125,7 +1122,10 @@ public: Item *it; if (execute(&it)) + { + null_value= 1; return NULL; + } return it->val_str(str); } diff --git a/sql/opt_range.h b/sql/opt_range.h index 1e5f58bc1f5..3c528719b29 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -87,7 +87,16 @@ public: QUICK_SELECT_I(); virtual ~QUICK_SELECT_I(){}; + /* + Call init() immediately after creation of quick select. if init() call + fails, reset() or get_next() must not be called. + */ virtual int init() = 0; + + /* + Call reset() before first get_next call. get_next must not be called if + reset() call fails. + */ virtual int reset(void) = 0; virtual int get_next() = 0; /* get next record to retrieve */ virtual bool reverse_sorted() = 0; diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 117bf4b37d4..eea5912cbdf 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -335,3 +335,4 @@ character-set=latin2 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index ba012e0eea3..2c2782d4cf0 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -329,3 +329,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 0349929f0d3..84bd5781ffb 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -337,3 +337,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 6bf4bde9b22..2c88e1b445f 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -326,3 +326,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index 03e2d4d6c90..d72d7f94309 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -331,3 +331,4 @@ character-set=latin7 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index dab2ab9f51d..227b5138e98 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -326,3 +326,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 5b2378dde22..d4e42ed5d58 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -338,3 +338,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index a840e95de97..44e072bb20e 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -326,3 +326,4 @@ character-set=greek "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index 0b24ca6afb8..72fdf4f8c51 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -328,3 +328,4 @@ character-set=latin2 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index a298baa7682..6694f220848 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -326,3 +326,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index a25357ae079..896146749a9 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -328,3 +328,4 @@ character-set=ujis "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index d3799d881ed..3a92c060c67 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -326,3 +326,4 @@ character-set=euckr "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index d69d52408ff..ada0435b52a 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -328,3 +328,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index 5fbbaf19480..d0688b2858c 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -328,3 +328,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 92572db5fdc..d0c96146441 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -330,3 +330,4 @@ character-set=latin2 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 580fec472b6..71be50ba262 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -327,3 +327,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 3e6bff75591..d800906e663 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -330,3 +330,4 @@ character-set=latin2 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index b04cda84efd..633e29e8c48 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -328,3 +328,4 @@ character-set=koi8r "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index 2737ce26873..efda5bb12cc 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -321,3 +321,4 @@ character-set=cp1250 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index bf9fe6d3519..8fa84320a8f 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -334,3 +334,4 @@ character-set=latin2 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index fec6bbb4342..75a6d42bec8 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -328,3 +328,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 8b2892172ba..9b743acb930 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -326,3 +326,4 @@ character-set=latin1 "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index 393c765a8ca..2b3587e2028 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -331,3 +331,4 @@ character-set=koi8u "Duplicate cursor: %s" "Failed to ALTER %s %s" "Subselect value not supported" +"USE is not allowed in a stored procedure" diff --git a/sql/sp.cc b/sql/sp.cc index 83fbd8c5173..2be9ceeaad3 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -16,6 +16,7 @@ #include "mysql_priv.h" +#include "sql_acl.h" #include "sp.h" #include "sp_head.h" #include "sp_cache.h" @@ -23,7 +24,7 @@ static char * create_string(THD *thd, ulong *lenp, int sp_type, - char *name, ulong namelen, + sp_name *name, const char *params, ulong paramslen, const char *returns, ulong returnslen, const char *body, ulong bodylen, @@ -58,21 +59,26 @@ enum /* *opened=true means we opened ourselves */ static int -db_find_routine_aux(THD *thd, int type, char *name, uint namelen, +db_find_routine_aux(THD *thd, int type, sp_name *name, enum thr_lock_type ltype, TABLE **tablep, bool *opened) { TABLE *table; byte key[64+64+1]; // db, name, type uint keylen; DBUG_ENTER("db_find_routine_aux"); - DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name)); + DBUG_PRINT("enter", ("type: %d name: %*s", + type, name->m_name.length, name->m_name.str)); // Put the key used to read the row together - memset(key, (int)' ', 64); // QQ Empty db for now - keylen= namelen; + keylen= name->m_db.length; if (keylen > 64) keylen= 64; - memcpy(key+64, name, keylen); + memcpy(key, name->m_db.str, keylen); + memset(key+keylen, (int)' ', 64-keylen); // Pad with space + keylen= name->m_name.length; + if (keylen > 64) + keylen= 64; + memcpy(key+64, name->m_name.str, keylen); memset(key+64+keylen, (int)' ', 64-keylen); // Pad with space key[128]= type; keylen= sizeof(key); @@ -112,7 +118,7 @@ db_find_routine_aux(THD *thd, int type, char *name, uint namelen, static int -db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) +db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) { extern int yyparse(void *thd); TABLE *table; @@ -129,9 +135,10 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) String str(buff, sizeof(buff), &my_charset_bin); ulong sql_mode; DBUG_ENTER("db_find_routine"); - DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name)); + DBUG_PRINT("enter", ("type: %d name: %*s", + type, name->m_name.length, name->m_name.str)); - ret= db_find_routine_aux(thd, type, name, namelen, TL_READ, &table, &opened); + ret= db_find_routine_aux(thd, type, name, TL_READ, &table, &opened); if (ret != SP_OK) goto done; @@ -211,6 +218,8 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) char *defstr; ulong deflen; LEX *oldlex= thd->lex; + char olddb[128]; + char *olddbptr; enum enum_sql_command oldcmd= thd->lex->sql_command; ulong old_sql_mode= thd->variables.sql_mode; ha_rows select_limit= thd->variables.select_limit; @@ -219,23 +228,43 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) thd->variables.select_limit= HA_POS_ERROR; if (!(defstr= create_string(thd, &deflen, - type, - name, namelen, - params, strlen(params), - returns, strlen(returns), - body, strlen(body), + type, + name, + params, strlen(params), + returns, strlen(returns), + body, strlen(body), &chistics))) { ret= SP_INTERNAL_ERROR; goto done; } - lex_start(thd, (uchar*)defstr, deflen); + olddbptr= thd->db; + if ((ret= sp_use_new_db(thd, name->m_db.str, olddb, sizeof(olddb), 1))) + goto done; + + { + /* This is something of a kludge. We need to initialize some fields + * in thd->lex (the unit and master stuff), and the easiest way to + * do it is, is to call mysql_init_query(), but this unfortunately + * resets teh value_list where we keep the CALL parameters. So we + * copy the list and then restore it. + */ + List vals= thd->lex->value_list; + + mysql_init_query(thd, TRUE); + lex_start(thd, (uchar*)defstr, deflen); + thd->lex->value_list= vals; + } + if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL) { LEX *newlex= thd->lex; sp_head *sp= newlex->sphead; + if (olddbptr != thd->db && + (ret= sp_change_db(thd, olddb, 1))) + goto done; if (sp) { if (oldlex != newlex) @@ -247,6 +276,9 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) } else { + if (olddbptr != thd->db && + (ret= sp_change_db(thd, olddb, 1))) + goto done; *sphp= thd->lex->sphead; (*sphp)->set_info((char *)definer, (uint)strlen(definer), created, modified, &chistics); @@ -257,6 +289,7 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) } done: + if (opened) close_thread_tables(thd); DBUG_RETURN(ret); @@ -289,6 +322,8 @@ db_create_routine(THD *thd, int type, sp_head *sp) ret= SP_GET_FIELD_FAILED; goto done; } + table->field[MYSQL_PROC_FIELD_DB]-> + store(sp->m_db.str, sp->m_db.length, system_charset_info); table->field[MYSQL_PROC_FIELD_NAME]-> store(sp->m_name.str, sp->m_name.length, system_charset_info); table->field[MYSQL_PROC_FIELD_TYPE]-> @@ -329,15 +364,16 @@ done: static int -db_drop_routine(THD *thd, int type, char *name, uint namelen) +db_drop_routine(THD *thd, int type, sp_name *name) { TABLE *table; int ret; bool opened; DBUG_ENTER("db_drop_routine"); - DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name)); + DBUG_PRINT("enter", ("type: %d name: %*s", + type, name->m_name.length, name->m_name.str)); - ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table, &opened); + ret= db_find_routine_aux(thd, type, name, TL_WRITE, &table, &opened); if (ret == SP_OK) { if (table->file->delete_row(table->record[0])) @@ -351,7 +387,7 @@ db_drop_routine(THD *thd, int type, char *name, uint namelen) static int -db_update_routine(THD *thd, int type, char *name, uint namelen, +db_update_routine(THD *thd, int type, sp_name *name, char *newname, uint newnamelen, st_sp_chistics *chistics) { @@ -359,9 +395,10 @@ db_update_routine(THD *thd, int type, char *name, uint namelen, int ret; bool opened; DBUG_ENTER("db_update_routine"); - DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name)); + DBUG_PRINT("enter", ("type: %d name: %*s", + type, name->m_name.length, name->m_name.str)); - ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table, &opened); + ret= db_find_routine_aux(thd, type, name, TL_WRITE, &table, &opened); if (ret == SP_OK) { store_record(table,record[1]); @@ -395,6 +432,7 @@ struct st_used_field static struct st_used_field init_fields[]= { + { "Db", NAME_LEN, MYSQL_TYPE_STRING, 0}, { "Name", NAME_LEN, MYSQL_TYPE_STRING, 0}, { "Type", 9, MYSQL_TYPE_STRING, 0}, { "Definer", 77, MYSQL_TYPE_STRING, 0}, @@ -415,14 +453,20 @@ print_field_values(THD *thd, TABLE *table, if (table->field[MYSQL_PROC_FIELD_TYPE]->val_int() == type) { - String *tmp_string= new String(); + String db_string; + String name_string; struct st_used_field *used_field= used_fields; - get_field(&thd->mem_root, used_field->field, tmp_string); - if (!wild || !wild[0] || !wild_compare(tmp_string->ptr(), wild, 0)) + if (get_field(&thd->mem_root, used_field->field, &db_string)) + db_string.set_ascii("", 0); + used_field+= 1; + get_field(&thd->mem_root, used_field->field, &name_string); + + if (!wild || !wild[0] || !wild_compare(name_string.ptr(), wild, 0)) { protocol->prepare_for_resend(); - protocol->store(tmp_string); + protocol->store(&db_string); + protocol->store(&name_string); for (used_field++; used_field->field_name; used_field++) @@ -439,10 +483,10 @@ print_field_values(THD *thd, TABLE *table, break; default: { - String *tmp_string1= new String(); + String tmp_string; - get_field(&thd->mem_root, used_field->field, tmp_string1); - protocol->store(tmp_string1); + get_field(&thd->mem_root, used_field->field, &tmp_string); + protocol->store(&tmp_string); } break; } @@ -545,21 +589,87 @@ done: } +/* Drop all routines in database 'db' */ +int +sp_drop_db_routines(THD *thd, char *db) +{ + TABLE *table; + byte key[64]; // db + uint keylen; + int ret; + DBUG_ENTER("sp_drop_db_routines"); + DBUG_PRINT("enter", ("db: %s", db)); + + // Put the key used to read the row together + keylen= strlen(db); + if (keylen > 64) + keylen= 64; + memcpy(key, db, keylen); + memset(key+keylen, (int)' ', 64-keylen); // Pad with space + keylen= sizeof(key); + + for (table= thd->open_tables ; table ; table= table->next) + if (strcmp(table->table_cache_key, "mysql") == 0 && + strcmp(table->real_name, "proc") == 0) + break; + if (! table) + { + TABLE_LIST tables; + + memset(&tables, 0, sizeof(tables)); + tables.db= (char*)"mysql"; + tables.real_name= tables.alias= (char*)"proc"; + if (! (table= open_ltable(thd, &tables, TL_WRITE))) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + } + + ret= SP_OK; + table->file->index_init(0); + if (! table->file->index_read(table->record[0], + key, keylen, HA_READ_KEY_EXACT)) + { + int nxtres; + bool deleted= FALSE; + + do { + if (! table->file->delete_row(table->record[0])) + deleted= TRUE; /* We deleted something */ + else + { + ret= SP_DELETE_ROW_FAILED; + nxtres= 0; + break; + } + } while (! (nxtres= table->file->index_next_same(table->record[0], + key, keylen))); + if (nxtres != HA_ERR_END_OF_FILE) + ret= SP_KEY_NOT_FOUND; + if (deleted) + sp_cache_invalidate(); + } + + close_thread_tables(thd); + + DBUG_RETURN(ret); +} + + /***************************************************************************** PROCEDURE ******************************************************************************/ sp_head * -sp_find_procedure(THD *thd, LEX_STRING *name) +sp_find_procedure(THD *thd, sp_name *name) { sp_head *sp; DBUG_ENTER("sp_find_procedure"); - DBUG_PRINT("enter", ("name: %*s", name->length, name->str)); + DBUG_PRINT("enter", ("name: %*s.%*s", + name->m_db.length, name->m_db.str, + name->m_name.length, name->m_name.str)); - if (!(sp= sp_cache_lookup(&thd->sp_proc_cache, name->str, name->length))) + if (!(sp= sp_cache_lookup(&thd->sp_proc_cache, name))) { - if (db_find_routine(thd, TYPE_ENUM_PROCEDURE, - name->str, name->length, &sp) == SP_OK) + if (db_find_routine(thd, TYPE_ENUM_PROCEDURE, name, &sp) == SP_OK) sp_cache_insert(&thd->sp_proc_cache, sp); } @@ -580,40 +690,40 @@ sp_create_procedure(THD *thd, sp_head *sp) int -sp_drop_procedure(THD *thd, char *name, uint namelen) +sp_drop_procedure(THD *thd, sp_name *name) { int ret; DBUG_ENTER("sp_drop_procedure"); - DBUG_PRINT("enter", ("name: %*s", namelen, name)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - sp_cache_remove(&thd->sp_proc_cache, name, namelen); - ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen); + sp_cache_remove(&thd->sp_proc_cache, name); + ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name); DBUG_RETURN(ret); } int -sp_update_procedure(THD *thd, char *name, uint namelen, +sp_update_procedure(THD *thd, sp_name *name, char *newname, uint newnamelen, st_sp_chistics *chistics) { int ret; DBUG_ENTER("sp_update_procedure"); - DBUG_PRINT("enter", ("name: %*s", namelen, name)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - sp_cache_remove(&thd->sp_proc_cache, name, namelen); - ret= db_update_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen, + sp_cache_remove(&thd->sp_proc_cache, name); + ret= db_update_routine(thd, TYPE_ENUM_PROCEDURE, name, newname, newnamelen, chistics); DBUG_RETURN(ret); } int -sp_show_create_procedure(THD *thd, LEX_STRING *name) +sp_show_create_procedure(THD *thd, sp_name *name) { sp_head *sp; DBUG_ENTER("sp_show_create_procedure"); - DBUG_PRINT("enter", ("name: %*s", name->length, name->str)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); if ((sp= sp_find_procedure(thd, name))) { @@ -642,16 +752,15 @@ sp_show_status_procedure(THD *thd, const char *wild) ******************************************************************************/ sp_head * -sp_find_function(THD *thd, LEX_STRING *name) +sp_find_function(THD *thd, sp_name *name) { sp_head *sp; DBUG_ENTER("sp_find_function"); - DBUG_PRINT("enter", ("name: %*s", name->length, name->str)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - if (!(sp= sp_cache_lookup(&thd->sp_func_cache, name->str, name->length))) + if (!(sp= sp_cache_lookup(&thd->sp_func_cache, name))) { - if (db_find_routine(thd, TYPE_ENUM_FUNCTION, - name->str, name->length, &sp) != SP_OK) + if (db_find_routine(thd, TYPE_ENUM_FUNCTION, name, &sp) != SP_OK) sp= NULL; else sp_cache_insert(&thd->sp_func_cache, sp); @@ -673,40 +782,40 @@ sp_create_function(THD *thd, sp_head *sp) int -sp_drop_function(THD *thd, char *name, uint namelen) +sp_drop_function(THD *thd, sp_name *name) { int ret; DBUG_ENTER("sp_drop_function"); - DBUG_PRINT("enter", ("name: %*s", namelen, name)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - sp_cache_remove(&thd->sp_func_cache, name, namelen); - ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name, namelen); + sp_cache_remove(&thd->sp_func_cache, name); + ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name); DBUG_RETURN(ret); } int -sp_update_function(THD *thd, char *name, uint namelen, +sp_update_function(THD *thd, sp_name *name, char *newname, uint newnamelen, st_sp_chistics *chistics) { int ret; DBUG_ENTER("sp_update_procedure"); - DBUG_PRINT("enter", ("name: %*s", namelen, name)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - sp_cache_remove(&thd->sp_func_cache, name, namelen); - ret= db_update_routine(thd, TYPE_ENUM_FUNCTION, name, namelen, + sp_cache_remove(&thd->sp_func_cache, name); + ret= db_update_routine(thd, TYPE_ENUM_FUNCTION, name, newname, newnamelen, chistics); DBUG_RETURN(ret); } int -sp_show_create_function(THD *thd, LEX_STRING *name) +sp_show_create_function(THD *thd, sp_name *name) { sp_head *sp; DBUG_ENTER("sp_show_create_function"); - DBUG_PRINT("enter", ("name: %*s", name->length, name->str)); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); if ((sp= sp_find_function(thd, name))) { @@ -728,18 +837,17 @@ sp_show_status_function(THD *thd, const char *wild) } -// QQ Temporary until the function call detection in sql_lex has been reworked. bool -sp_function_exists(THD *thd, LEX_STRING *name) +sp_function_exists(THD *thd, sp_name *name) { TABLE *table; bool ret= FALSE; bool opened= FALSE; DBUG_ENTER("sp_function_exists"); - if (sp_cache_lookup(&thd->sp_func_cache, name->str, name->length) || + if (sp_cache_lookup(&thd->sp_func_cache, name) || db_find_routine_aux(thd, TYPE_ENUM_FUNCTION, - name->str, name->length, TL_READ, + name, TL_READ, &table, &opened) == SP_OK) ret= TRUE; if (opened) @@ -758,13 +866,14 @@ sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first) void -sp_add_fun_to_lex(LEX *lex, LEX_STRING fun) +sp_add_fun_to_lex(LEX *lex, sp_name *fun) { - if (! hash_search(&lex->spfuns, (byte *)fun.str, fun.length)) + if (! hash_search(&lex->spfuns, + (byte *)fun->m_qname.str, fun->m_qname.length)) { LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING)); - ls->str= sql_strmake(fun.str, fun.length); - ls->length= fun.length; + ls->str= sql_strmake(fun->m_qname.str, fun->m_qname.length); + ls->length= fun->m_qname.length; my_hash_insert(&lex->spfuns, (byte *)ls); } @@ -793,15 +902,25 @@ sp_cache_functions(THD *thd, LEX *lex) for (uint i=0 ; i < h->records ; i++) { LEX_STRING *ls= (LEX_STRING *)hash_element(h, i); + sp_name name(*ls); - if (! sp_cache_lookup(&thd->sp_func_cache, ls->str, ls->length)) + name.m_qname= *ls; + if (! sp_cache_lookup(&thd->sp_func_cache, &name)) { sp_head *sp; LEX *oldlex= thd->lex; LEX *newlex= new st_lex; thd->lex= newlex; - if (db_find_routine(thd, TYPE_ENUM_FUNCTION, ls->str, ls->length, &sp) == SP_OK) + name.m_name.str= strchr(name.m_qname.str, '.'); + name.m_db.length= name.m_name.str - name.m_qname.str; + name.m_db.str= strmake_root(&thd->mem_root, + name.m_qname.str, name.m_db.length); + name.m_name.str+= 1; + name.m_name.length= name.m_qname.length - name.m_db.length - 1; + + if (db_find_routine(thd, TYPE_ENUM_FUNCTION, &name, &sp) + == SP_OK) { ret= sp_cache_functions(thd, newlex); delete newlex; @@ -827,7 +946,7 @@ sp_cache_functions(THD *thd, LEX *lex) static char * create_string(THD *thd, ulong *lenp, int type, - char *name, ulong namelen, + sp_name *name, const char *params, ulong paramslen, const char *returns, ulong returnslen, const char *body, ulong bodylen, @@ -836,12 +955,15 @@ create_string(THD *thd, ulong *lenp, char *buf, *ptr; ulong buflen; - buflen= 100 + namelen + paramslen + returnslen + bodylen + chistics->comment.length; + buflen= 100 + name->m_qname.length + paramslen + returnslen + bodylen + + chistics->comment.length; if (!(buf= thd->alloc(buflen))) return 0; - ptr= strxmov(buf, "CREATE ", (type == TYPE_ENUM_FUNCTION) ? "FUNCTION" : "PROCEDURE", - " `", name, "`(", params, ")", NullS); + ptr= strxmov(buf, "CREATE ", + (type == TYPE_ENUM_FUNCTION) ? "FUNCTION" : "PROCEDURE", + " `", name->m_db.str, "`.`", name->m_name.str, "`(", params, ")", + NullS); if (type == TYPE_ENUM_FUNCTION) ptr= strxmov(ptr, " RETURNS ", returns, NullS); @@ -860,3 +982,148 @@ create_string(THD *thd, ulong *lenp, *lenp= (ptr-buf); return buf; } + + +// +// Utilities... +// + +int +sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddblen, + bool no_access_check) +{ + bool changeit; + DBUG_ENTER("sp_use_new_db"); + DBUG_PRINT("enter", ("newdb: %s", newdb)); + + if (thd->db && thd->db[0]) + { + if (my_strcasecmp(system_charset_info, thd->db, newdb) == 0) + changeit= 0; + else + { + changeit= 1; + strnmov(olddb, thd->db, olddblen); + } + } + else + { // thd->db empty + if (newdb[0]) + changeit= 1; + else + changeit= 0; + olddb[0] = '\0'; + } + if (!changeit) + { + DBUG_RETURN(0); + } + else + { + int ret= sp_change_db(thd, newdb, no_access_check); + + DBUG_RETURN(ret); + } +} + +/* + Change database. + + SYNOPSIS + sp_change_db() + thd Thread handler + name Database name + empty_is_ok True= it's ok with "" as name + no_access_check True= don't do access check + + DESCRIPTION + This is the same as mysql_change_db(), but with some extra + arguments for Stored Procedure usage; doing implicit "use" + when executing an SP in a different database. + We also use different error routines, since this might be + invoked from a function when executing a query or statement. + Note: We would have prefered to reuse mysql_change_db(), but + the error handling in particular made that too awkward, so + we (reluctantly) have a "copy" here. + + RETURN VALUES + 0 ok + 1 error +*/ + +int +sp_change_db(THD *thd, char *name, bool no_access_check) +{ + int length, db_length; + char *dbname=my_strdup((char*) name,MYF(MY_WME)); + char path[FN_REFLEN]; + ulong db_access; + HA_CREATE_INFO create; + DBUG_ENTER("sp_change_db"); + DBUG_PRINT("enter", ("db: %s, no_access_check: %d", name, no_access_check)); + + db_length= (!dbname ? 0 : strip_sp(dbname)); + if (dbname && db_length) + { + if ((db_length > NAME_LEN) || check_db_name(dbname)) + { + my_printf_error(ER_WRONG_DB_NAME, ER(ER_WRONG_DB_NAME), MYF(0), dbname); + x_free(dbname); + DBUG_RETURN(1); + } + } + + if (dbname && db_length) + { +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (! no_access_check) + { + if (test_all_bits(thd->master_access,DB_ACLS)) + db_access=DB_ACLS; + else + db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) | + thd->master_access); + if (!(db_access & DB_ACLS) && + (!grant_option || check_grant_db(thd,dbname))) + { + my_printf_error(ER_DBACCESS_DENIED_ERROR, ER(ER_DBACCESS_DENIED_ERROR), + MYF(0), + thd->priv_user, + thd->priv_host, + dbname); + mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR), + thd->priv_user, + thd->priv_host, + dbname); + my_free(dbname,MYF(0)); + DBUG_RETURN(1); + } + } +#endif + (void) sprintf(path,"%s/%s",mysql_data_home,dbname); + length=unpack_dirname(path,path); // Convert if not unix + if (length && path[length-1] == FN_LIBCHAR) + path[length-1]=0; // remove ending '\' + if (access(path,F_OK)) + { + my_printf_error(ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR), MYF(0), dbname); + my_free(dbname,MYF(0)); + DBUG_RETURN(1); + } + } + + x_free(thd->db); + thd->db=dbname; // THD::~THD will free this + thd->db_length=db_length; + + if (dbname && db_length) + { + strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE); + load_db_opt(thd, path, &create); + thd->db_charset= create.default_table_charset ? + create.default_table_charset : + thd->variables.collation_server; + thd->variables.collation_database= thd->db_charset; + } + DBUG_RETURN(0); +} diff --git a/sql/sp.h b/sql/sp.h index b9f29138de2..a4fec50aca2 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -28,59 +28,77 @@ #define SP_PARSE_ERROR -6 #define SP_INTERNAL_ERROR -7 +/* Drop all routines in database 'db' */ +int +sp_drop_db_routines(THD *thd, char *db); + sp_head * -sp_find_procedure(THD *thd, LEX_STRING *name); +sp_find_procedure(THD *thd, sp_name *name); int sp_create_procedure(THD *thd, sp_head *sp); int -sp_drop_procedure(THD *thd, char *name, uint namelen); +sp_drop_procedure(THD *thd, sp_name *name); int -sp_update_procedure(THD *thd, char *name, uint namelen, +sp_update_procedure(THD *thd, sp_name *name, char *newname, uint newnamelen, st_sp_chistics *chistics); int -sp_show_create_procedure(THD *thd, LEX_STRING *name); +sp_show_create_procedure(THD *thd, sp_name *name); int sp_show_status_procedure(THD *thd, const char *wild); sp_head * -sp_find_function(THD *thd, LEX_STRING *name); +sp_find_function(THD *thd, sp_name *name); int sp_create_function(THD *thd, sp_head *sp); int -sp_drop_function(THD *thd, char *name, uint namelen); +sp_drop_function(THD *thd, sp_name *name); int -sp_update_function(THD *thd, char *name, uint namelen, +sp_update_function(THD *thd, sp_name *name, char *newname, uint newnamelen, st_sp_chistics *chistics); int -sp_show_create_function(THD *thd, LEX_STRING *name); +sp_show_create_function(THD *thd, sp_name *name); int sp_show_status_function(THD *thd, const char *wild); -// QQ Temporary until the function call detection in sql_lex has been reworked. bool -sp_function_exists(THD *thd, LEX_STRING *name); +sp_function_exists(THD *thd, sp_name *name); // This is needed since we have to read the functions before we // do anything else. void -sp_add_fun_to_lex(LEX *lex, LEX_STRING fun); +sp_add_fun_to_lex(LEX *lex, sp_name *fun); void sp_merge_funs(LEX *dst, LEX *src); int sp_cache_functions(THD *thd, LEX *lex); + +// +// Utilities... +// + +// Do a "use newdb". The current db is stored at olddb. +// If newdb is the same as the current one, nothing is changed. +int +sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddbmax, + bool no_access_check); + +// Like mysql_change_db() but handles empty db name and the send_ok() problem. +int +sp_change_db(THD *thd, char *db, bool no_access_check); + #endif /* _SP_H_ */ diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index 657a96ec33d..056ac6d7e96 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -71,7 +71,7 @@ sp_cache_insert(sp_cache **cp, sp_head *sp) } sp_head * -sp_cache_lookup(sp_cache **cp, char *name, uint namelen) +sp_cache_lookup(sp_cache **cp, sp_name *name) { ulong v; sp_cache *c= *cp; @@ -89,11 +89,11 @@ sp_cache_lookup(sp_cache **cp, char *name, uint namelen) c->version= v; return NULL; } - return c->lookup(name, namelen); + return c->lookup(name->m_qname.str, name->m_qname.length); } bool -sp_cache_remove(sp_cache **cp, char *name, uint namelen) +sp_cache_remove(sp_cache **cp, sp_name *name) { sp_cache *c= *cp; bool found= FALSE; @@ -109,18 +109,28 @@ sp_cache_remove(sp_cache **cp, char *name, uint namelen) if (c->version < v) c->remove_all(); else - found= c->remove(name, namelen); + found= c->remove(name->m_qname.str, name->m_qname.length); c->version= v+1; } return found; } +void +sp_cache_invalidate() +{ + pthread_mutex_lock(&Cversion_lock); // LOCK + Cversion++; + pthread_mutex_unlock(&Cversion_lock); // UNLOCK +} static byte * hash_get_key_for_sp_head(const byte *ptr, uint *plen, my_bool first) { - return (byte*) ((sp_head*)ptr)->name(plen); + sp_head *sp= (sp_head *)ptr; + + *plen= sp->m_qname.length; + return (byte*) sp->m_qname.str; } static void diff --git a/sql/sp_cache.h b/sql/sp_cache.h index da25227303b..754a987090e 100644 --- a/sql/sp_cache.h +++ b/sql/sp_cache.h @@ -35,10 +35,13 @@ void sp_cache_clear(sp_cache **cp); void sp_cache_insert(sp_cache **cp, sp_head *sp); /* Lookup an SP in cache */ -sp_head *sp_cache_lookup(sp_cache **cp, char *name, uint namelen); +sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name); /* Remove an SP from cache. Returns true if something was removed */ -bool sp_cache_remove(sp_cache **cp, char *name, uint namelen); +bool sp_cache_remove(sp_cache **cp, sp_name *name); + +/* Invalidate a cache */ +void sp_cache_invalidate(); /* diff --git a/sql/sp_head.cc b/sql/sp_head.cc index a68118cecd1..c8be113e2e1 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -130,6 +130,52 @@ sp_eval_func_item(THD *thd, Item *it, enum enum_field_types type) DBUG_RETURN(it); } + +/* + * + * sp_name + * + */ + +void +sp_name::init_qname(THD *thd) +{ + m_qname.length= m_db.length+m_name.length+1; + m_qname.str= alloc_root(&thd->mem_root, m_qname.length+1); + sprintf(m_qname.str, "%*s.%*s", + m_db.length, (m_db.length ? m_db.str : ""), + m_name.length, m_name.str); +} + +sp_name * +sp_name_current_db_new(THD *thd, LEX_STRING name) +{ + sp_name *qname; + + if (! thd->db) + qname= new sp_name(name); + else + { + LEX_STRING db; + + db.length= strlen(thd->db); + db.str= thd->strmake(thd->db, db.length); + qname= new sp_name(db, name); + } + qname->init_qname(thd); + return qname; +} + + +/* ------------------------------------------------------------------ */ + + +/* + * + * sp_head + * + */ + void * sp_head::operator new(size_t size) { @@ -178,22 +224,42 @@ sp_head::init(LEX *lex) lex->spcont= m_pcont= new sp_pcontext(); my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8); m_param_begin= m_param_end= m_returns_begin= m_returns_end= m_body_begin= 0; - m_name.str= m_params.str= m_retstr.str= m_body.str= m_defstr.str= 0; - m_name.length= m_params.length= m_retstr.length= m_body.length= - m_defstr.length= 0; + m_qname.str= m_db.str= m_name.str= m_params.str= m_retstr.str= + m_body.str= m_defstr.str= 0; + m_qname.length= m_db.length= m_name.length= m_params.length= + m_retstr.length= m_body.length= m_defstr.length= 0; DBUG_VOID_RETURN; } void -sp_head::init_strings(THD *thd, LEX *lex, LEX_STRING *name) +sp_head::init_strings(THD *thd, LEX *lex, sp_name *name) { DBUG_ENTER("sp_head::init_strings"); /* During parsing, we must use thd->mem_root */ MEM_ROOT *root= &thd->mem_root; - DBUG_PRINT("info", ("name: %*s", name->length, name->str)); - m_name.length= name->length; - m_name.str= strmake_root(root, name->str, name->length); + DBUG_PRINT("info", ("name: %*.s%*s", + name->m_db.length, name->m_db.str, + name->m_name.length, name->m_name.str)); + /* We have to copy strings to get them into the right memroot */ + if (name->m_db.length == 0) + { + m_db.length= (thd->db ? strlen(thd->db) : 0); + m_db.str= strmake_root(root, (thd->db ? thd->db : ""), m_db.length); + } + else + { + m_db.length= name->m_db.length; + m_db.str= strmake_root(root, name->m_db.str, name->m_db.length); + } + m_name.length= name->m_name.length; + m_name.str= strmake_root(root, name->m_name.str, name->m_name.length); + + if (name->m_qname.length == 0) + name->init_qname(thd); + m_qname.length= name->m_qname.length; + m_qname.str= strmake_root(root, name->m_qname.str, m_qname.length); + m_params.length= m_param_end- m_param_begin; m_params.str= strmake_root(root, (char *)m_param_begin, m_params.length); @@ -271,30 +337,22 @@ int sp_head::execute(THD *thd) { DBUG_ENTER("sp_head::execute"); - char olddbname[128]; - char *olddbptr= thd->db; + char olddb[128]; + char *olddbptr; sp_rcontext *ctx= thd->spcont; int ret= 0; uint ip= 0; #ifndef EMBEDDED_LIBRARY - if (check_stack_overrun(thd, olddbptr)) + if (check_stack_overrun(thd, olddb)) { DBUG_RETURN(-1); } #endif - if (olddbptr) - { - uint i= 0; - char *p= olddbptr; - /* Fast inline strncpy without padding... */ - while (*p && i < sizeof(olddbname)) - olddbname[i++]= *p++; - if (i == sizeof(olddbname)) - i-= 1; // QQ Error or warning for truncate? - olddbname[i]= '\0'; - } + olddbptr= thd->db; + if ((ret= sp_use_new_db(thd, m_db.str, olddb, sizeof(olddb), 0))) + goto done; if (ctx) ctx->clear_handler(); @@ -331,20 +389,20 @@ sp_head::execute(THD *thd) continue; } } - } while (ret == 0 && !thd->killed && !thd->query_error); + } while (ret == 0 && !thd->killed && !thd->query_error && + !thd->net.report_error); + done: DBUG_PRINT("info", ("ret=%d killed=%d query_error=%d", ret, thd->killed, thd->query_error)); - if (thd->killed || thd->query_error) + if (thd->killed || thd->query_error || thd->net.report_error) ret= -1; /* If the DB has changed, the pointer has changed too, but the original thd->db will then have been freed */ - if (olddbptr && olddbptr != thd->db) + if (olddbptr != thd->db) { - /* QQ Maybe we should issue some special error message or warning here, - if this fails?? */ if (! thd->killed) - ret= mysql_change_db(thd, olddbname); + ret= sp_change_db(thd, olddb, 0); } DBUG_RETURN(ret); } @@ -496,7 +554,12 @@ sp_head::execute_procedure(THD *thd, List *args) ret= execute(thd); // Don't copy back OUT values if we got an error - if (ret == 0 && csize > 0) + if (ret) + { + if (thd->net.report_error) + send_error(thd, 0, NullS); + } + else if (csize > 0) { List_iterator_fast li(*args); Item *it; @@ -711,6 +774,32 @@ sp_head::set_info(char *definer, uint definerlen, m_chistics->comment.length); } +void +sp_head::reset_thd_mem_root(THD *thd) +{ + m_thd_root= thd->mem_root; + thd->mem_root= m_mem_root; + m_free_list= thd->free_list; // Keep the old list + thd->free_list= NULL; // Start a new one + /* Copy the db, since substatements will point to it */ + m_thd_db= thd->db; + thd->db= strmake_root(&thd->mem_root, thd->db, thd->db_length); + m_thd= thd; +} + +void +sp_head::restore_thd_mem_root(THD *thd) +{ + Item *flist= m_free_list; // The old list + m_free_list= thd->free_list; // Get the new one + thd->free_list= flist; // Restore the old one + thd->db= m_thd_db; // Restore the original db pointer + m_mem_root= thd->mem_root; + thd->mem_root= m_thd_root; + m_thd= NULL; +} + + int sp_head::show_create_procedure(THD *thd) { @@ -796,7 +885,10 @@ sp_instr_stmt::exec_stmt(THD *thd, LEX *lex) thd->lex->unit.thd= thd; // QQ Not reentrant freelist= thd->free_list; thd->free_list= NULL; + + VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id= query_id++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); // Copy WHERE clause pointers to avoid damaging by optimisation // Also clear ref_pointer_arrays. @@ -1089,10 +1181,13 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp) DBUG_RETURN(res); } +/* ------------------------------------------------------------------ */ + // // Security context swapping // + #ifndef NO_EMBEDDED_ACCESS_CHECKS void sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp) @@ -1105,8 +1200,6 @@ sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp) { ctxp->master_access= thd->master_access; ctxp->db_access= thd->db_access; - ctxp->db= thd->db; - ctxp->db_length= thd->db_length; ctxp->priv_user= thd->priv_user; strncpy(ctxp->priv_host, thd->priv_host, sizeof(ctxp->priv_host)); ctxp->user= thd->user; @@ -1122,8 +1215,6 @@ sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp) ctxp->changed= FALSE; thd->master_access= ctxp->master_access; thd->db_access= ctxp->db_access; - thd->db= ctxp->db; - thd->db_length= ctxp->db_length; thd->priv_user= ctxp->priv_user; strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host)); } @@ -1143,8 +1234,6 @@ sp_restore_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp) ctxp->changed= FALSE; thd->master_access= ctxp->master_access; thd->db_access= ctxp->db_access; - thd->db= ctxp->db; - thd->db_length= ctxp->db_length; thd->priv_user= ctxp->priv_user; strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host)); } diff --git a/sql/sp_head.h b/sql/sp_head.h index b2dee5204bb..791c6697693 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -37,6 +37,39 @@ class sp_instr; struct sp_cond_type; struct sp_pvar; +class sp_name : public Sql_alloc +{ +public: + + LEX_STRING m_db; + LEX_STRING m_name; + LEX_STRING m_qname; + + sp_name(LEX_STRING name) + : m_name(name) + { + m_db.str= m_qname.str= 0; + m_db.length= m_qname.length= 0; + } + + sp_name(LEX_STRING db, LEX_STRING name) + : m_db(db), m_name(name) + { + m_qname.str= 0; + m_qname.length= 0; + } + + // Init. the qualified name from the db and name. + void init_qname(THD *thd); // thd for memroot allocation + + ~sp_name() + {} +}; + +sp_name * +sp_name_current_db_new(THD *thd, LEX_STRING name); + + class sp_head : public Sql_alloc { sp_head(const sp_head &); /* Prevent use of these */ @@ -56,6 +89,8 @@ public: List m_calls; // Called procedures. List m_tables; // Used tables. #endif + LEX_STRING m_qname; // db.name + LEX_STRING m_db; LEX_STRING m_name; LEX_STRING m_params; LEX_STRING m_retstr; // For FUNCTIONs only @@ -83,7 +118,7 @@ public: // Initialize strings after parsing header void - init_strings(THD *thd, LEX *lex, LEX_STRING *name); + init_strings(THD *thd, LEX *lex, sp_name *name); int create(THD *thd); @@ -163,24 +198,10 @@ public: longlong created, longlong modified, st_sp_chistics *chistics); - inline void reset_thd_mem_root(THD *thd) - { - m_thd_root= thd->mem_root; - thd->mem_root= m_mem_root; - m_free_list= thd->free_list; // Keep the old list - thd->free_list= NULL; // Start a new one - m_thd= thd; - } + void reset_thd_mem_root(THD *thd); + + void restore_thd_mem_root(THD *thd); - inline void restore_thd_mem_root(THD *thd) - { - Item *flist= m_free_list; // The old list - m_free_list= thd->free_list; // Get the new one - thd->free_list= flist; // Restore the old one - m_mem_root= thd->mem_root; - thd->mem_root= m_thd_root; - m_thd= NULL; - } private: @@ -188,6 +209,7 @@ private: MEM_ROOT m_thd_root; // Temp. store for thd's mem_root Item *m_free_list; // Where the items go THD *m_thd; // Set if we have reset mem_root + char *m_thd_db; // Original thd->db pointer sp_pcontext *m_pcont; // Parse context List m_lex; // Temp. store for the other lex @@ -640,8 +662,6 @@ struct st_sp_security_context bool changed; uint master_access; uint db_access; - char *db; - uint db_length; char *priv_user; char priv_host[MAX_HOSTNAME]; char *user; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 5febb49e110..d294055ff8a 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -794,6 +794,7 @@ int acl_getroot_no_password(THD *thd) { ulong user_access= NO_ACCESS; int res= 1; + uint i; ACL_USER *acl_user= 0; DBUG_ENTER("acl_getroot_no_password"); @@ -810,13 +811,16 @@ int acl_getroot_no_password(THD *thd) VOID(pthread_mutex_lock(&acl_cache->lock)); + thd->master_access= 0; + thd->db_access= 0; + /* Find acl entry in user database. This is specially tailored to suit the check we do for CALL of a stored procedure; thd->user is set to what is actually a priv_user, which can be ''. */ - for (uint i=0 ; i < acl_users.elements ; i++) + for (i=0 ; i < acl_users.elements ; i++) { acl_user= dynamic_element(&acl_users,i,ACL_USER*); if ((!acl_user->user && (!thd->user || !thd->user[0])) || @@ -832,6 +836,22 @@ int acl_getroot_no_password(THD *thd) if (acl_user) { + for (i=0 ; i < acl_dbs.elements ; i++) + { + ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*); + if (!acl_db->user || + (thd->user && thd->user[0] && !strcmp(thd->user, acl_db->user))) + { + if (compare_hostname(&acl_db->host, thd->host, thd->ip)) + { + if (!acl_db->db || (thd->db && !strcmp(acl_db->db, thd->db))) + { + thd->db_access= acl_db->access; + break; + } + } + } + } thd->master_access= acl_user->access; thd->priv_user= acl_user->user ? thd->user : (char *) ""; diff --git a/sql/sql_db.cc b/sql/sql_db.cc index bc6b30040d6..54265b58bd4 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -19,6 +19,7 @@ #include "mysql_priv.h" #include "sql_acl.h" +#include "sp.h" #include #include #ifdef __WIN__ @@ -386,6 +387,7 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) } exit: + (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */ start_waiting_global_read_lock(thd); /* If this database was the client's selected database, we silently change the diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 1fa216fdb58..7ebe9cb6002 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -150,6 +150,13 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, select= 0; } + /* If quick select is used, initialize it before retrieving rows. */ + if (select && select->quick && select->quick->reset()) + { + delete select; + free_underlaid_joins(thd, &thd->lex->select_lex); + DBUG_RETURN(-1); // This will force out message + } init_read_record(&info,thd,table,select,1,1); deleted=0L; init_ftfuncs(thd, &thd->lex->select_lex, 1); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index f37f133f105..6f6a8925412 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -168,38 +168,6 @@ static int find_keyword(LEX *lex, uint len, bool function) lex->yylval->symbol.length=len; return symbol->tok; } - - LEX_STRING ls; - ls.str = (char *)tok; ls.length= len; - if (function && sp_function_exists(current_thd, &ls)) // QQ temp fix - { - lex->safe_to_cache_query= 0; - lex->yylval->lex_str.str= lex->thd->strmake((char*)lex->tok_start, len); - lex->yylval->lex_str.length= len; - return SP_FUNC; - } - -#ifdef HAVE_DLOPEN - udf_func *udf; - if (function && using_udf_functions && (udf=find_udf((char*) tok, len))) - { - lex->safe_to_cache_query=0; - lex->yylval->udf=udf; - switch (udf->returns) { - case STRING_RESULT: - return (udf->type == UDFTYPE_FUNCTION) ? UDF_CHAR_FUNC : UDA_CHAR_SUM; - case REAL_RESULT: - return (udf->type == UDFTYPE_FUNCTION) ? UDF_FLOAT_FUNC : UDA_FLOAT_SUM; - case INT_RESULT: - return (udf->type == UDFTYPE_FUNCTION) ? UDF_INT_FUNC : UDA_INT_SUM; - case ROW_RESULT: - default: - // This case should never be choosen - DBUG_ASSERT(0); - return 0; - } - } -#endif return 0; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 50b8f322731..221f864db81 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -22,6 +22,7 @@ class Table_ident; class sql_exchange; class LEX_COLUMN; class sp_head; +class sp_name; class sp_instr; class sp_pcontext; @@ -604,6 +605,7 @@ typedef struct st_lex bool derived_tables; bool safe_to_cache_query; sp_head *sphead; + sp_name *spname; bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */ sp_pcontext *spcont; HASH spfuns; /* Called functions */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ae507c34272..273e994189f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1086,6 +1086,10 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) thd->query_length=length; thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1); thd->query[length] = '\0'; + /* + We don't need to obtain LOCK_thread_count here because in bootstrap + mode we have only one thread. + */ thd->query_id=query_id++; if (mqh_used && thd->user_connect && check_mqh(thd, SQLCOM_END)) { @@ -3101,9 +3105,9 @@ mysql_execute_command(THD *thd) if (check_access(thd,INSERT_ACL,"mysql",0,1,0)) break; #ifdef HAVE_DLOPEN - if ((sph= sp_find_function(thd, &lex->udf.name))) + if ((sph= sp_find_function(thd, lex->spname))) { - net_printf(thd, ER_UDF_EXISTS, lex->udf.name.str); + net_printf(thd, ER_UDF_EXISTS, lex->spname->m_name.str); goto error; } if (!(res = mysql_create_function(thd,&lex->udf))) @@ -3441,9 +3445,10 @@ mysql_execute_command(THD *thd) { sp_head *sp; - if (!(sp= sp_find_procedure(thd, &lex->udf.name))) + if (!(sp= sp_find_procedure(thd, lex->spname))) { - net_printf(thd, ER_SP_DOES_NOT_EXIST, "PROCEDURE", lex->udf.name); + net_printf(thd, ER_SP_DOES_NOT_EXIST, "PROCEDURE", + lex->spname->m_name.str); goto error; } else @@ -3521,10 +3526,10 @@ mysql_execute_command(THD *thd) goto error; } if (lex->sql_command == SQLCOM_ALTER_PROCEDURE) - res= sp_update_procedure(thd, lex->udf.name.str, lex->udf.name.length, + res= sp_update_procedure(thd, lex->spname, lex->name, newname_len, &lex->sp_chistics); else - res= sp_update_function(thd, lex->udf.name.str, lex->udf.name.length, + res= sp_update_function(thd, lex->spname, lex->name, newname_len, &lex->sp_chistics); switch (res) { @@ -3532,10 +3537,12 @@ mysql_execute_command(THD *thd) send_ok(thd); break; case SP_KEY_NOT_FOUND: - net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),lex->udf.name); + net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex), + lex->spname->m_name.str); goto error; default: - net_printf(thd, ER_SP_CANT_ALTER, SP_COM_STRING(lex),lex->udf.name); + net_printf(thd, ER_SP_CANT_ALTER, SP_COM_STRING(lex), + lex->spname->m_name.str); goto error; } break; @@ -3544,19 +3551,20 @@ mysql_execute_command(THD *thd) case SQLCOM_DROP_FUNCTION: { if (lex->sql_command == SQLCOM_DROP_PROCEDURE) - res= sp_drop_procedure(thd, lex->udf.name.str, lex->udf.name.length); + res= sp_drop_procedure(thd, lex->spname); else { - res= sp_drop_function(thd, lex->udf.name.str, lex->udf.name.length); + res= sp_drop_function(thd, lex->spname); #ifdef HAVE_DLOPEN if (res == SP_KEY_NOT_FOUND) { - udf_func *udf = find_udf(lex->udf.name.str, lex->udf.name.length); + udf_func *udf = find_udf(lex->spname->m_name.str, + lex->spname->m_name.length); if (udf) { if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0)) goto error; - if (!(res = mysql_drop_function(thd,&lex->udf.name))) + if (!(res = mysql_drop_function(thd,&lex->spname->m_name))) { send_ok(thd); break; @@ -3575,17 +3583,17 @@ mysql_execute_command(THD *thd) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), - SP_COM_STRING(lex), lex->udf.name.str); + SP_COM_STRING(lex), lex->spname->m_name.str); res= 0; send_ok(thd); break; } net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex), - lex->udf.name.str); + lex->spname->m_name.str); goto error; default: net_printf(thd, ER_SP_DROP_FAILED, SP_COM_STRING(lex), - lex->udf.name.str); + lex->spname->m_name.str); goto error; } break; @@ -3593,16 +3601,16 @@ mysql_execute_command(THD *thd) case SQLCOM_SHOW_CREATE_PROC: { res= -1; - if (lex->udf.name.length > NAME_LEN) + if (lex->spname->m_name.length > NAME_LEN) { - net_printf(thd, ER_TOO_LONG_IDENT, lex->udf.name.str); + net_printf(thd, ER_TOO_LONG_IDENT, lex->spname->m_name.str); goto error; } - res= sp_show_create_procedure(thd, &lex->udf.name); + res= sp_show_create_procedure(thd, lex->spname); if (res != SP_OK) { /* We don't distinguish between errors for now */ net_printf(thd, ER_SP_DOES_NOT_EXIST, - SP_COM_STRING(lex), lex->udf.name.str); + SP_COM_STRING(lex), lex->spname->m_name.str); res= 0; goto error; } @@ -3610,16 +3618,16 @@ mysql_execute_command(THD *thd) } case SQLCOM_SHOW_CREATE_FUNC: { - if (lex->udf.name.length > NAME_LEN) + if (lex->spname->m_name.length > NAME_LEN) { - net_printf(thd, ER_TOO_LONG_IDENT, lex->udf.name.str); + net_printf(thd, ER_TOO_LONG_IDENT, lex->spname->m_name.str); goto error; } - res= sp_show_create_function(thd, &lex->udf.name); + res= sp_show_create_function(thd, lex->spname); if (res != SP_OK) { /* We don't distinguish between errors for now */ net_printf(thd, ER_SP_DOES_NOT_EXIST, - SP_COM_STRING(lex), lex->udf.name.str); + SP_COM_STRING(lex), lex->spname->m_name.str); res= 0; goto error; } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 03ab3d01c1a..24199df9b84 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -246,7 +246,11 @@ int mysql_update(THD *thd, DISK_BUFFER_SIZE, MYF(MY_WME))) goto err; + /* If quick select is used, initialize it before retrieving rows. */ + if (select && select->quick && select->quick->reset()) + goto err; init_read_record(&info,thd,table,select,0,1); + thd->proc_info="Searching rows for update"; uint tmp_limit= limit; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4c217b325ec..d2f7e73b2b8 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -92,6 +92,7 @@ inline Item *or_or_concat(THD *thd, Item* A, Item* B) chooser_compare_func_creator boolfunc2creator; struct sp_cond_type *spcondtype; struct { int vars, conds, hndlrs, curs; } spblock; + sp_name *spname; struct st_lex *lex; } @@ -568,17 +569,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token SECOND_SYM %token SECOND_MICROSECOND_SYM %token SHARE_SYM -%token SP_FUNC %token SUBDATE_SYM %token SUBSTRING %token SUBSTRING_INDEX %token TRIM -%token UDA_CHAR_SUM -%token UDA_FLOAT_SUM -%token UDA_INT_SUM -%token UDF_CHAR_FUNC -%token UDF_FLOAT_FUNC -%token UDF_INT_FUNC %token UNIQUE_USERS %token UNIX_TIMESTAMP %token USER @@ -640,7 +634,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_literal NCHAR_STRING opt_component key_cache_name - SP_FUNC ident_or_spfunc sp_opt_label + sp_opt_label %type opt_table_alias @@ -683,7 +677,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); simple_ident_nospvar simple_ident_q %type - expr_list sp_expr_list udf_expr_list udf_expr_list2 when_list + expr_list udf_expr_list udf_expr_list2 when_list ident_list ident_list_arg %type @@ -701,10 +695,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %type join_table_list join_table -%type - UDF_CHAR_FUNC UDF_FLOAT_FUNC UDF_INT_FUNC - UDA_CHAR_SUM UDA_FLOAT_SUM UDA_INT_SUM - %type date_time_type; %type interval @@ -782,6 +772,7 @@ END_OF_INPUT %type sp_cond sp_hcond %type sp_decls sp_decl %type sp_cursor_stmt +%type sp_name %type '-' '+' '*' '/' '%' '(' ')' @@ -1030,15 +1021,15 @@ create: lex->name=$4.str; lex->create_info.options=$3; } - | CREATE udf_func_type FUNCTION_SYM ident_or_spfunc + | CREATE udf_func_type FUNCTION_SYM sp_name { LEX *lex=Lex; - lex->udf.name = $4; + lex->spname= $4; lex->udf.type= $2; } create_function_tail {} - | CREATE PROCEDURE ident + | CREATE PROCEDURE sp_name { LEX *lex= Lex; sp_head *sp; @@ -1089,7 +1080,7 @@ create: { LEX *lex= Lex; - lex->sphead->init_strings(YYTHD, lex, &$3); + lex->sphead->init_strings(YYTHD, lex, $3); lex->sql_command= SQLCOM_CREATE_PROCEDURE; /* Restore flag if it was cleared above */ if (lex->sphead->m_old_cmq) @@ -1098,9 +1089,16 @@ create: } ; -ident_or_spfunc: - IDENT_sys { $$= $1; } - | SP_FUNC { $$= $1; } +sp_name: + IDENT_sys '.' IDENT_sys + { + $$= new sp_name($1, $3); + $$->init_qname(YYTHD); + } + | IDENT_sys + { + $$= sp_name_current_db_new(YYTHD, $1); + } ; create_function_tail: @@ -1108,6 +1106,7 @@ create_function_tail: { LEX *lex=Lex; lex->sql_command = SQLCOM_CREATE_FUNCTION; + lex->udf.name = lex->spname->m_name; lex->udf.returns=(Item_result) $2; lex->udf.dl=$4.str; } @@ -1169,7 +1168,7 @@ create_function_tail: sp_head *sp= lex->sphead; lex->sql_command= SQLCOM_CREATE_SPFUNCTION; - sp->init_strings(YYTHD, lex, &lex->udf.name); + sp->init_strings(YYTHD, lex, lex->spname); /* Restore flag if it was cleared above */ if (sp->m_old_cmq) YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; @@ -1219,12 +1218,12 @@ sp_suid: ; call: - CALL_SYM ident_or_spfunc + CALL_SYM sp_name { LEX *lex = Lex; lex->sql_command= SQLCOM_CALL; - lex->udf.name= $2; + lex->spname= $2; lex->value_list.empty(); } '(' sp_cparam_list ')' {} @@ -1584,6 +1583,11 @@ sp_proc_stmt: /* We maybe have one or more SELECT without INTO */ lex->sphead->m_multi_results= TRUE; } + if (lex->sql_command == SQLCOM_CHANGE_DB) + { /* "USE db" doesn't work in a procedure */ + send_error(YYTHD, ER_SP_NO_USE); + YYABORT; + } /* Don't add an instruction for empty SET statements. ** (This happens if the SET only contained local variables, ** which get their set instructions generated separately.) @@ -2739,7 +2743,7 @@ alter: lex->sql_command=SQLCOM_ALTER_DB; lex->name=$3.str; } - | ALTER PROCEDURE ident + | ALTER PROCEDURE sp_name { LEX *lex= Lex; @@ -2752,9 +2756,9 @@ alter: LEX *lex=Lex; lex->sql_command= SQLCOM_ALTER_PROCEDURE; - lex->udf.name= $3; + lex->spname= $3; } - | ALTER FUNCTION_SYM ident + | ALTER FUNCTION_SYM sp_name { LEX *lex= Lex; @@ -2767,7 +2771,7 @@ alter: LEX *lex=Lex; lex->sql_command= SQLCOM_ALTER_FUNCTION; - lex->udf.name= $3; + lex->spname= $3; } ; @@ -3919,56 +3923,90 @@ simple_expr: { $$= new Item_func_round($3,$5,1); } | TRUE_SYM { $$= new Item_int((char*) "TRUE",1,1); } - | SP_FUNC '(' sp_expr_list ')' + | ident '.' ident '(' udf_expr_list ')' { - sp_add_fun_to_lex(Lex, $1); - if ($3) - $$= new Item_func_sp($1, *$3); + LEX *lex= Lex; + sp_name *name= new sp_name($1, $3); + + name->init_qname(YYTHD); + sp_add_fun_to_lex(Lex, name); + if ($5) + $$= new Item_func_sp(name, *$5); else - $$= new Item_func_sp($1); - } - | UDA_CHAR_SUM '(' udf_expr_list ')' - { - if ($3 != NULL) - $$ = new Item_sum_udf_str($1, *$3); - else - $$ = new Item_sum_udf_str($1); - } - | UDA_FLOAT_SUM '(' udf_expr_list ')' - { - if ($3 != NULL) - $$ = new Item_sum_udf_float($1, *$3); - else - $$ = new Item_sum_udf_float($1); - } - | UDA_INT_SUM '(' udf_expr_list ')' - { - if ($3 != NULL) - $$ = new Item_sum_udf_int($1, *$3); - else - $$ = new Item_sum_udf_int($1); - } - | UDF_CHAR_FUNC '(' udf_expr_list ')' - { - if ($3 != NULL) - $$ = new Item_func_udf_str($1, *$3); - else - $$ = new Item_func_udf_str($1); - } - | UDF_FLOAT_FUNC '(' udf_expr_list ')' - { - if ($3 != NULL) - $$ = new Item_func_udf_float($1, *$3); - else - $$ = new Item_func_udf_float($1); - } - | UDF_INT_FUNC '(' udf_expr_list ')' - { - if ($3 != NULL) - $$ = new Item_func_udf_int($1, *$3); - else - $$ = new Item_func_udf_int($1); + $$= new Item_func_sp(name); } + | IDENT_sys '(' udf_expr_list ')' + { +#ifdef HAVE_DLOPEN + udf_func *udf; + + if (using_udf_functions && (udf=find_udf($1.str, $1.length))) + { + switch (udf->returns) { + case STRING_RESULT: + if (udf->type == UDFTYPE_FUNCTION) + { + if ($3 != NULL) + $$ = new Item_func_udf_str(udf, *$3); + else + $$ = new Item_func_udf_str(udf); + } + else + { + if ($3 != NULL) + $$ = new Item_sum_udf_str(udf, *$3); + else + $$ = new Item_sum_udf_str(udf); + } + break; + case REAL_RESULT: + if (udf->type == UDFTYPE_FUNCTION) + { + if ($3 != NULL) + $$ = new Item_func_udf_float(udf, *$3); + else + $$ = new Item_func_udf_float(udf); + } + else + { + if ($3 != NULL) + $$ = new Item_sum_udf_float(udf, *$3); + else + $$ = new Item_sum_udf_float(udf); + } + break; + case INT_RESULT: + if (udf->type == UDFTYPE_FUNCTION) + { + if ($3 != NULL) + $$ = new Item_func_udf_int(udf, *$3); + else + $$ = new Item_func_udf_int(udf); + } + else + { + if ($3 != NULL) + $$ = new Item_sum_udf_int(udf, *$3); + else + $$ = new Item_sum_udf_int(udf); + } + break; + default: + YYABORT; + } + } + else +#endif /* HAVE_DLOPEN */ + { + sp_name *name= sp_name_current_db_new(YYTHD, $1); + + sp_add_fun_to_lex(Lex, name); + if ($3) + $$= new Item_func_sp(name, *$3); + else + $$= new Item_func_sp(name); + } + } | UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' expr_list ')' { $$= new Item_func_unique_users($3,atoi($5.str),atoi($7.str), * $9); @@ -4075,10 +4113,6 @@ fulltext_options: | IN_SYM BOOLEAN_SYM MODE_SYM { $$= FT_BOOL; } ; -sp_expr_list: - /* empty */ { $$= NULL; } - | expr_list { $$= $1;}; - udf_expr_list: /* empty */ { $$= NULL; } | udf_expr_list2 { $$= $1;} @@ -4825,19 +4859,19 @@ drop: lex->drop_if_exists=$3; lex->name=$4.str; } - | DROP FUNCTION_SYM if_exists IDENT_sys opt_restrict + | DROP FUNCTION_SYM if_exists sp_name opt_restrict { LEX *lex=Lex; lex->sql_command = SQLCOM_DROP_FUNCTION; lex->drop_if_exists= $3; - lex->udf.name= $4; + lex->spname= $4; } - | DROP PROCEDURE if_exists IDENT_sys opt_restrict + | DROP PROCEDURE if_exists sp_name opt_restrict { LEX *lex=Lex; lex->sql_command = SQLCOM_DROP_PROCEDURE; lex->drop_if_exists= $3; - lex->udf.name= $4; + lex->spname= $4; } | DROP USER { @@ -5316,15 +5350,19 @@ show_param: { Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; } - | CREATE PROCEDURE ident + | CREATE PROCEDURE sp_name { - Lex->sql_command = SQLCOM_SHOW_CREATE_PROC; - Lex->udf.name= $3; + LEX *lex= Lex; + + lex->sql_command = SQLCOM_SHOW_CREATE_PROC; + lex->spname= $3; } - | CREATE FUNCTION_SYM ident + | CREATE FUNCTION_SYM sp_name { - Lex->sql_command = SQLCOM_SHOW_CREATE_FUNC; - Lex->udf.name= $3; + LEX *lex= Lex; + + lex->sql_command = SQLCOM_SHOW_CREATE_FUNC; + lex->spname= $3; } | PROCEDURE STATUS_SYM wild {