Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
MariaDB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
MariaDB
Commits
0dcc255b
Commit
0dcc255b
authored
Mar 25, 2004
by
igor@rurik.mysql.com
Browse files
Options
Browse Files
Download
Plain Diff
Merge rurik.mysql.com:/home/igor/mysql-5.0
into rurik.mysql.com:/home/igor/dev/mysql-5.0-0
parents
bae25543
5954e94f
Changes
56
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
56 changed files
with
1171 additions
and
403 deletions
+1171
-403
BitKeeper/etc/logging_ok
BitKeeper/etc/logging_ok
+1
-0
Docs/sp-imp-spec.txt
Docs/sp-imp-spec.txt
+2
-2
include/mysqld_error.h
include/mysqld_error.h
+2
-1
include/sql_state.h
include/sql_state.h
+1
-0
mysql-test/r/index_merge.result
mysql-test/r/index_merge.result
+20
-0
mysql-test/r/show_check.result
mysql-test/r/show_check.result
+0
-1
mysql-test/r/sp-error.result
mysql-test/r/sp-error.result
+4
-0
mysql-test/r/sp-security.result
mysql-test/r/sp-security.result
+81
-15
mysql-test/r/sp.result
mysql-test/r/sp.result
+58
-35
mysql-test/r/status.result
mysql-test/r/status.result
+1
-1
mysql-test/t/index_merge.test
mysql-test/t/index_merge.test
+10
-0
mysql-test/t/sp-error.test
mysql-test/t/sp-error.test
+6
-0
mysql-test/t/sp-security.test
mysql-test/t/sp-security.test
+89
-16
mysql-test/t/sp.test
mysql-test/t/sp.test
+63
-23
scripts/mysql_create_system_tables.sh
scripts/mysql_create_system_tables.sh
+2
-2
scripts/mysql_fix_privilege_tables.sql
scripts/mysql_fix_privilege_tables.sql
+6
-2
sql/item_func.cc
sql/item_func.cc
+37
-5
sql/item_func.h
sql/item_func.h
+11
-11
sql/opt_range.h
sql/opt_range.h
+9
-0
sql/share/czech/errmsg.txt
sql/share/czech/errmsg.txt
+1
-0
sql/share/danish/errmsg.txt
sql/share/danish/errmsg.txt
+1
-0
sql/share/dutch/errmsg.txt
sql/share/dutch/errmsg.txt
+1
-0
sql/share/english/errmsg.txt
sql/share/english/errmsg.txt
+1
-0
sql/share/estonian/errmsg.txt
sql/share/estonian/errmsg.txt
+1
-0
sql/share/french/errmsg.txt
sql/share/french/errmsg.txt
+1
-0
sql/share/german/errmsg.txt
sql/share/german/errmsg.txt
+1
-0
sql/share/greek/errmsg.txt
sql/share/greek/errmsg.txt
+1
-0
sql/share/hungarian/errmsg.txt
sql/share/hungarian/errmsg.txt
+1
-0
sql/share/italian/errmsg.txt
sql/share/italian/errmsg.txt
+1
-0
sql/share/japanese/errmsg.txt
sql/share/japanese/errmsg.txt
+1
-0
sql/share/korean/errmsg.txt
sql/share/korean/errmsg.txt
+1
-0
sql/share/norwegian-ny/errmsg.txt
sql/share/norwegian-ny/errmsg.txt
+1
-0
sql/share/norwegian/errmsg.txt
sql/share/norwegian/errmsg.txt
+1
-0
sql/share/polish/errmsg.txt
sql/share/polish/errmsg.txt
+1
-0
sql/share/portuguese/errmsg.txt
sql/share/portuguese/errmsg.txt
+1
-0
sql/share/romanian/errmsg.txt
sql/share/romanian/errmsg.txt
+1
-0
sql/share/russian/errmsg.txt
sql/share/russian/errmsg.txt
+1
-0
sql/share/serbian/errmsg.txt
sql/share/serbian/errmsg.txt
+1
-0
sql/share/slovak/errmsg.txt
sql/share/slovak/errmsg.txt
+1
-0
sql/share/spanish/errmsg.txt
sql/share/spanish/errmsg.txt
+1
-0
sql/share/swedish/errmsg.txt
sql/share/swedish/errmsg.txt
+1
-0
sql/share/ukrainian/errmsg.txt
sql/share/ukrainian/errmsg.txt
+1
-0
sql/sp.cc
sql/sp.cc
+339
-72
sql/sp.h
sql/sp.h
+29
-11
sql/sp_cache.cc
sql/sp_cache.cc
+15
-5
sql/sp_cache.h
sql/sp_cache.h
+5
-2
sql/sp_head.cc
sql/sp_head.cc
+124
-35
sql/sp_head.h
sql/sp_head.h
+40
-20
sql/sql_acl.cc
sql/sql_acl.cc
+21
-1
sql/sql_db.cc
sql/sql_db.cc
+2
-0
sql/sql_delete.cc
sql/sql_delete.cc
+7
-0
sql/sql_lex.cc
sql/sql_lex.cc
+0
-32
sql/sql_lex.h
sql/sql_lex.h
+2
-0
sql/sql_parse.cc
sql/sql_parse.cc
+31
-23
sql/sql_update.cc
sql/sql_update.cc
+4
-0
sql/sql_yacc.yy
sql/sql_yacc.yy
+126
-88
No files found.
BitKeeper/etc/logging_ok
View file @
0dcc255b
...
...
@@ -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
...
...
Docs/sp-imp-spec.txt
View file @
0dcc255b
...
...
@@ -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,
...
...
include/mysqld_error.h
View file @
0dcc255b
...
...
@@ -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
include/sql_state.h
View file @
0dcc255b
...
...
@@ -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"
,
""
,
mysql-test/r/index_merge.result
View file @
0dcc255b
...
...
@@ -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;
mysql-test/r/show_check.result
View file @
0dcc255b
...
...
@@ -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;
...
...
mysql-test/r/sp-error.result
View file @
0dcc255b
...
...
@@ -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;
...
...
mysql-test/r/sp-security.result
View file @
0dcc255b
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';
mysql-test/r/sp.result
View file @
0dcc255b
...
...
@@ -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;
mysql-test/r/status.result
View file @
0dcc255b
...
...
@@ -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;
mysql-test/t/index_merge.test
View file @
0dcc255b
...
...
@@ -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
;
mysql-test/t/sp-error.test
View file @
0dcc255b
...
...
@@ -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
#
...
...
mysql-test/t/sp-security.test
View file @
0dcc255b
...
...
@@ -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
(
con2
dummy
,
localhost
,
dummy
,,);
connect
(
con2
user1
,
localhost
,
user1
,,);
connect
(
con3anon
,
localhost
,
anon
,,);
#
#
Dummy
can
#
User1
can
#
connection
con2
dummy
;
connection
con2
user1
;
# 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
con2
dummy
;
connection
con2
user1
;
# 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'
;
mysql-test/t/sp.test
View file @
0dcc255b
...
...
@@ -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
;
|
...
...
scripts/mysql_create_system_tables.sh
View file @
0dcc255b
...
...
@@ -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,"
...
...
scripts/mysql_fix_privilege_tables.sql
View file @
0dcc255b
...
...
@@ -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;
sql/item_func.cc
View file @
0dcc255b
...
...
@@ -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
<
Item
>
&
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
<
LEX_STRING
*>
(
&
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
<
LEX_STRING
*>
(
&
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
:
...
...
sql/item_func.h
View file @
0dcc255b
...
...
@@ -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
<
Item
>
&
list
)
:
Item_func
(
list
),
m_name
(
name
),
m_sp
(
NULL
)
{}
Item_func_sp
(
sp_name
*
name
,
List
<
Item
>
&
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 @@ class Item_func_sp :public Item_func
Item
*
it
;
if
(
execute
(
&
it
))
{
null_value
=
1
;
return
0.0
;
}
return
it
->
val
();
}
...
...
@@ -1125,7 +1122,10 @@ class Item_func_sp :public Item_func
Item
*
it
;
if
(
execute
(
&
it
))
{
null_value
=
1
;
return
NULL
;
}
return
it
->
val_str
(
str
);
}
...
...
sql/opt_range.h
View file @
0dcc255b
...
...
@@ -87,7 +87,16 @@ class QUICK_SELECT_I
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
;
...
...
sql/share/czech/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/danish/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/dutch/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/english/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/estonian/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/french/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/german/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/greek/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/hungarian/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/italian/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/japanese/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/korean/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/norwegian-ny/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/norwegian/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/polish/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/portuguese/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/romanian/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/russian/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/serbian/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/slovak/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/spanish/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/swedish/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/share/ukrainian/errmsg.txt
View file @
0dcc255b
...
...
@@ -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"
sql/sp.cc
View file @
0dcc255b
This diff is collapsed.
Click to expand it.
sql/sp.h
View file @
0dcc255b
...
...
@@ -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_ */
sql/sp_cache.cc
View file @
0dcc255b
...
...
@@ -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
...
...
sql/sp_cache.h
View file @
0dcc255b
...
...
@@ -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
();
/*
...
...
sql/sp_head.cc
View file @
0dcc255b
...
...
@@ -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
olddb
name
[
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
,
olddb
ptr
))
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<Item> *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
<
Item
>
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
));
}
...
...
sql/sp_head.h
View file @
0dcc255b
...
...
@@ -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 @@ class sp_head : public Sql_alloc
List
<
char
*>
m_calls
;
// Called procedures.
List
<
char
*>
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 @@ class sp_head : public Sql_alloc
// 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 @@ class sp_head : public Sql_alloc
longlong
created
,
longlong
modified
,
st_sp_chistics
*
chistics
);
inline
void
reset_thd_mem_root
(
THD
*
thd
)
{
m_thd_root
=
thd
->
mem_root
;
thd
->
mem_root
=
m_mem_root
;
m_free_list
=
thd
->
free_list
;
// Keep the old list
thd
->
free_list
=
NULL
;
// Start a new one
m_thd
=
thd
;
}
void
reset_thd_mem_root
(
THD
*
thd
);
void
restore_thd_mem_root
(
THD
*
thd
);
inline
void
restore_thd_mem_root
(
THD
*
thd
)
{
Item
*
flist
=
m_free_list
;
// The old list
m_free_list
=
thd
->
free_list
;
// Get the new one
thd
->
free_list
=
flist
;
// Restore the old one
m_mem_root
=
thd
->
mem_root
;
thd
->
mem_root
=
m_thd_root
;
m_thd
=
NULL
;
}
private:
...
...
@@ -188,6 +209,7 @@ class sp_head : public Sql_alloc
MEM_ROOT
m_thd_root
;
// Temp. store for thd's mem_root
Item
*
m_free_list
;
// Where the items go
THD
*
m_thd
;
// Set if we have reset mem_root
char
*
m_thd_db
;
// Original thd->db pointer
sp_pcontext
*
m_pcont
;
// Parse context
List
<
LEX
>
m_lex
;
// Temp. store for the other lex
...
...
@@ -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
;
...
...
sql/sql_acl.cc
View file @
0dcc255b
...
...
@@ -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
*
)
""
;
...
...
sql/sql_db.cc
View file @
0dcc255b
...
...
@@ -19,6 +19,7 @@
#include "mysql_priv.h"
#include "sql_acl.h"
#include "sp.h"
#include <my_dir.h>
#include <m_ctype.h>
#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
...
...
sql/sql_delete.cc
View file @
0dcc255b
...
...
@@ -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
);
...
...
sql/sql_lex.cc
View file @
0dcc255b
...
...
@@ -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
;
}
...
...
sql/sql_lex.h
View file @
0dcc255b
...
...
@@ -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 */
...
...
sql/sql_parse.cc
View file @
0dcc255b
...
...
@@ -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
->
sp
name
)))
{
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
->
sp
name
)))
{
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
->
sp
name
);
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
->
sp
name
);
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
;
}
...
...
sql/sql_update.cc
View file @
0dcc255b
...
...
@@ -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
;
...
...
sql/sql_yacc.yy
View file @
0dcc255b
...
...
@@ -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 <lex_str_ptr>
opt_table_alias
...
...
@@ -683,7 +677,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
simple_ident_nospvar simple_ident_q
%type <item_list>
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 <key_type>
...
...
@@ -701,10 +695,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%type <table_list>
join_table_list join_table
%type <udf>
UDF_CHAR_FUNC UDF_FLOAT_FUNC UDF_INT_FUNC
UDA_CHAR_SUM UDA_FLOAT_SUM UDA_INT_SUM
%type <date_time_type> date_time_type;
%type <interval> interval
...
...
@@ -782,6 +772,7 @@ END_OF_INPUT
%type <spcondtype> sp_cond sp_hcond
%type <spblock> sp_decls sp_decl
%type <lex> sp_cursor_stmt
%type <spname> sp_name
%type <NONE>
'-' '+' '*' '/' '%' '(' ')'
...
...
@@ -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->sp
name);
/* 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->
sp
name= $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->
sp
name= $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->
sp
name= $3;
}
;
...
...
@@ -3919,55 +3923,89 @@ 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
);
$$= new Item_func_sp(
name
);
}
| UDA_CHAR_SUM '(' udf_expr_list ')'
| 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_sum_udf_str($1
, *$3);
$$ = new Item_func_udf_str(udf
, *$3);
else
$$ = new Item_sum_udf_str($1
);
$$ = new Item_func_udf_str(udf
);
}
| UDA_FLOAT_SUM '(' udf_expr_list ')'
else
{
if ($3 != NULL)
$$ = new Item_sum_udf_float($1
, *$3);
$$ = new Item_sum_udf_str(udf
, *$3);
else
$$ = new Item_sum_udf_float($1
);
$$ = new Item_sum_udf_str(udf
);
}
| UDA_INT_SUM '(' udf_expr_list ')'
break;
case REAL_RESULT:
if (udf->type == UDFTYPE_FUNCTION)
{
if ($3 != NULL)
$$ = new Item_sum_udf_int($1
, *$3);
$$ = new Item_func_udf_float(udf
, *$3);
else
$$ = new Item_sum_udf_int($1
);
$$ = new Item_func_udf_float(udf
);
}
| UDF_CHAR_FUNC '(' udf_expr_list ')'
else
{
if ($3 != NULL)
$$ = new Item_func_udf_str($1
, *$3);
$$ = new Item_sum_udf_float(udf
, *$3);
else
$$ = new Item_func_udf_str($1
);
$$ = new Item_sum_udf_float(udf
);
}
| UDF_FLOAT_FUNC '(' udf_expr_list ')'
break;
case INT_RESULT:
if (udf->type == UDFTYPE_FUNCTION)
{
if ($3 != NULL)
$$ = new Item_func_udf_float($1
, *$3);
$$ = new Item_func_udf_int(udf
, *$3);
else
$$ = new Item_func_udf_float($1
);
$$ = new Item_func_udf_int(udf
);
}
| UDF_INT_FUNC '(' udf_expr_list ')'
else
{
if ($3 != NULL)
$$ = new Item_func_udf_int($1
, *$3);
$$ = new Item_sum_udf_int(udf
, *$3);
else
$$ = new Item_func_udf_int($1);
$$ = 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 ')'
{
...
...
@@ -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->
sp
name= $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->
sp
name= $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
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment