mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 02:05:57 +01:00
New FUNCTION documentation, and a minor test case modification.
Docs/sp-imp-spec.txt: Updated docs about stored FUNCTIONs. Docs/sp-implemented.txt: Updated docs about stored FUNCTIONs. mysql-test/r/sp.result: Changed the ifac test example into a combination of a procedure and a function. mysql-test/t/sp.test: Changed the ifac test example into a combination of a procedure and a function.
This commit is contained in:
parent
8a9422bd2a
commit
f519382d2b
4 changed files with 151 additions and 105 deletions
|
@ -172,8 +172,9 @@ calling a PROCEDURE.
|
|||
with the addition that a FUNCTION has a return type and a RETURN
|
||||
statement, but no OUT or INOUT parameters.
|
||||
|
||||
[QQ - More details here; sp_head needs a result slot and a type flag
|
||||
indicating if it's a function or procedure]
|
||||
The main difference during parsing is that we store the result type
|
||||
in the sp_head. However, there are big differences when it comes to
|
||||
invoking a FUNCTION. (See below.)
|
||||
|
||||
|
||||
- Storing, caching, dropping...
|
||||
|
@ -273,7 +274,64 @@ calling a PROCEDURE.
|
|||
So, stored functions must be handled in a simpilar way, and as a
|
||||
consequence, UDFs and functions must not have the same name.
|
||||
|
||||
[QQ - Details of how function calls works here]
|
||||
- Detecting and parsing a FUNCTION invokation
|
||||
|
||||
The existance of UDFs are checked during the lexical analysis (in
|
||||
sql_lex.cc:find_keyword()). This has the drawback that they must
|
||||
exist before they are refered to, which was ok before SPs existed,
|
||||
but then it becomes a problem. The first implementation of SP FUNCTIONs
|
||||
will work the same way, but this should be fixed a.s.a.p. (This will
|
||||
required some reworking of the way UDFs are handled, which is why it's
|
||||
not done from the start.)
|
||||
For the time being, a FUNCTION is detected the same way, and returns
|
||||
the token SP_FUNC. During the parsing we only check for the *existance*
|
||||
of the function, we don't parse it, since wa can't call the parser
|
||||
recursively.
|
||||
|
||||
When encountering a SP_FUNC with parameters in the expression parser,
|
||||
an instance of the new Item_func_sp class is created. Unlike UDFs, we
|
||||
don't have different classes for different return types, since we at
|
||||
this point don't know the type.
|
||||
|
||||
- Collecting FUNCTIONs to invoke
|
||||
|
||||
A FUNCTION differs from a PROCEDURE in one important aspect: Whereas a
|
||||
PROCEDURE is CALLed as statement by itself, a FUNCTION is invoked
|
||||
"on-the-fly" during the execution of *another* statement.
|
||||
This makes things a lot more complicated compared to CALL:
|
||||
- We can't read and parse the FUNCTION from the mysql.proc table at the
|
||||
point of invokation; the server requires that all tables used are
|
||||
opened and locked at the beginning of the query execution.
|
||||
One "obvious" solution would be to simply push "mysql.proc" to the list
|
||||
of tables used by the query, but this implies a "join" with this table
|
||||
if the query is a select, so it doesn't work (and we can't exclude this
|
||||
table easily; since a priviledged used might in fact want to search
|
||||
the proc table).
|
||||
Another solution would of course be to allow the opening and closing
|
||||
of the mysql.proc table during a query execution, but this it not
|
||||
possible at the present.
|
||||
|
||||
So, the solution is to collect the names of the refered FUNCTIONs during
|
||||
parsing in the lex.
|
||||
Then, before doing anything else in mysql_execute_command(), read all
|
||||
functions from the database an keep them in the THD, where the function
|
||||
sp_find_function() can find them during the execution.
|
||||
Note: Even when a global in-memory cache is implemented, we must still
|
||||
make sure that the functions are indeed read and cached at this point.
|
||||
The code that read and cache functions from the database must also be
|
||||
invoked recursively for each read FUNCTION to make sure we have *all* the
|
||||
functions we need.
|
||||
|
||||
In the absence of the real in-memory cache for SPs, a temporary solution
|
||||
has been implemented with a per-THD cache for just FUNCTIONs. This is
|
||||
handled by the functions
|
||||
|
||||
void sp_add_fun_to_lex(LEX *lex, LEX_STRING fun);
|
||||
void sp_merge_funs(LEX *dst, LEX *src);
|
||||
int sp_cache_functions(THD *thd, LEX *lex);
|
||||
void sp_clear_function_cache(THD *thd);
|
||||
|
||||
in sp.cc.
|
||||
|
||||
|
||||
- Parsing DROP PROCEDURE/FUNCTION
|
||||
|
|
|
@ -3,7 +3,6 @@ Stored Procedures implemented 2003-02-02:
|
|||
|
||||
Summary of Not Yet Implemented:
|
||||
|
||||
- FUNCTIONs
|
||||
- Routine characteristics
|
||||
- External languages
|
||||
- Access control
|
||||
|
@ -26,14 +25,14 @@ Summary of what's implemented:
|
|||
|
||||
List of what's implemented:
|
||||
|
||||
- CREATE PROCEDURE name ( args ) body
|
||||
- CREATE PROCEDURE|FUNCTION name ( args ) body
|
||||
No routine characteristics yet.
|
||||
|
||||
- ALTER PROCEDURE name ...
|
||||
- ALTER PROCEDURE|FUNCTION name ...
|
||||
Is parsed, but a no-op (as there are no characteristics implemented yet).
|
||||
CASCADE/RESTRICT is not implemented (and CASCADE probably will not be).
|
||||
|
||||
- DROP PROCEDURE name
|
||||
- DROP PROCEDURE|FUNCTION name
|
||||
CASCADE/RESTRICT is not implemented (and CASCADE probably will not be).
|
||||
|
||||
- CALL name (args)
|
||||
|
@ -45,7 +44,7 @@ List of what's implemented:
|
|||
will either generate an error message, or it might even work to
|
||||
call all procedures from the top-level.
|
||||
|
||||
- Procedure body:
|
||||
- Function/Procedure body:
|
||||
- BEGIN/END
|
||||
Is parsed, but not the real thing with (optional) transaction
|
||||
control, it only serves as block syntax for multiple statements (and
|
||||
|
@ -82,9 +81,23 @@ Closed questions:
|
|||
Answer: Same tables, with an additional key-field for the type.
|
||||
|
||||
|
||||
Open questions:
|
||||
Open questions/issues:
|
||||
|
||||
- SQL-99 variables and parameters are typed. For the present we don't do
|
||||
any type checking, since this is the way MySQL works. I still don't know
|
||||
if we should keep it this way, or implement type checking. Possibly we
|
||||
should have optional, uset-settable, type checking.
|
||||
|
||||
- FUNCTIONs do not work correctly in all circumstances yet.
|
||||
For instance a function like:
|
||||
create function s() returns int
|
||||
begin
|
||||
declare s int;
|
||||
select sum(test.t.y) into s from test.t;
|
||||
return s;
|
||||
end
|
||||
do not work. Invoking this in queries like "SELECT * FROM t2 WHERE x = s()"
|
||||
will make things very complicated. And, in fact, even "SET @s=s()" and
|
||||
"SELECT s()" fail, although the exact reasons in these cases are a bit
|
||||
obscure; part of the problem might be the way the lex structure is
|
||||
bit-copied (a not completely sound thing to do).
|
||||
|
|
|
@ -277,59 +277,6 @@ h1 1
|
|||
h? 17
|
||||
delete from t1;
|
||||
drop procedure h;
|
||||
drop table if exists fac;
|
||||
create table fac (n int unsigned not null primary key, f bigint unsigned);
|
||||
create procedure ifac(n int unsigned)
|
||||
begin
|
||||
declare i int unsigned;
|
||||
set i = 1;
|
||||
if n > 20 then
|
||||
set n = 20;
|
||||
end if;
|
||||
while i <= n do
|
||||
begin
|
||||
declare f bigint unsigned;
|
||||
set f = 0; # Temp. fix, this should not be needed in the future.
|
||||
call fac(i, f);
|
||||
insert into test.fac values (i, f);
|
||||
set i = i + 1;
|
||||
end;
|
||||
end while;
|
||||
end;
|
||||
create procedure fac(n int unsigned, out f bigint unsigned)
|
||||
begin
|
||||
set f = 1;
|
||||
while n > 1 do
|
||||
set f = f * n;
|
||||
set n = n - 1;
|
||||
end while;
|
||||
end;
|
||||
call ifac(20);
|
||||
select * from fac;
|
||||
n f
|
||||
1 1
|
||||
2 2
|
||||
3 6
|
||||
4 24
|
||||
5 120
|
||||
6 720
|
||||
7 5040
|
||||
8 40320
|
||||
9 362880
|
||||
10 3628800
|
||||
11 39916800
|
||||
12 479001600
|
||||
13 6227020800
|
||||
14 87178291200
|
||||
15 1307674368000
|
||||
16 20922789888000
|
||||
17 355687428096000
|
||||
18 6402373705728000
|
||||
19 121645100408832000
|
||||
20 2432902008176640000
|
||||
drop table fac;
|
||||
drop procedure ifac;
|
||||
drop procedure fac;
|
||||
create procedure into_test(x char(16), y int)
|
||||
begin
|
||||
insert into test.t1 values (x, y);
|
||||
|
@ -441,7 +388,48 @@ drop function e;
|
|||
drop function inc;
|
||||
drop function mul;
|
||||
drop function append;
|
||||
drop function fac;
|
||||
drop function fun;
|
||||
drop table if exists fac;
|
||||
create table fac (n int unsigned not null primary key, f bigint unsigned);
|
||||
create procedure ifac(n int unsigned)
|
||||
begin
|
||||
declare i int unsigned;
|
||||
set i = 1;
|
||||
if n > 20 then
|
||||
set n = 20; # bigint overflow otherwise
|
||||
end if;
|
||||
while i <= n do
|
||||
begin
|
||||
insert into test.fac values (i, fac(i));
|
||||
set i = i + 1;
|
||||
end;
|
||||
end while;
|
||||
end;
|
||||
call ifac(20);
|
||||
select * from fac;
|
||||
n f
|
||||
1 1
|
||||
2 2
|
||||
3 6
|
||||
4 24
|
||||
5 120
|
||||
6 720
|
||||
7 5040
|
||||
8 40320
|
||||
9 362880
|
||||
10 3628800
|
||||
11 39916800
|
||||
12 479001600
|
||||
13 6227020800
|
||||
14 87178291200
|
||||
15 1307674368000
|
||||
16 20922789888000
|
||||
17 355687428096000
|
||||
18 6402373705728000
|
||||
19 121645100408832000
|
||||
20 2432902008176640000
|
||||
drop table fac;
|
||||
drop procedure ifac;
|
||||
drop function fac;
|
||||
drop table t1;
|
||||
drop table t2;
|
||||
|
|
|
@ -325,47 +325,6 @@ delete from t1|
|
|||
drop procedure h|
|
||||
|
||||
|
||||
# A "real" procedure example
|
||||
|
||||
--disable_warnings
|
||||
drop table if exists fac|
|
||||
--enable_warnings
|
||||
create table fac (n int unsigned not null primary key, f bigint unsigned)|
|
||||
|
||||
create procedure ifac(n int unsigned)
|
||||
begin
|
||||
declare i int unsigned;
|
||||
set i = 1;
|
||||
if n > 20 then
|
||||
set n = 20;
|
||||
end if;
|
||||
while i <= n do
|
||||
begin
|
||||
declare f bigint unsigned;
|
||||
set f = 0; # Temp. fix, this should not be needed in the future.
|
||||
call fac(i, f);
|
||||
insert into test.fac values (i, f);
|
||||
set i = i + 1;
|
||||
end;
|
||||
end while;
|
||||
end|
|
||||
|
||||
create procedure fac(n int unsigned, out f bigint unsigned)
|
||||
begin
|
||||
set f = 1;
|
||||
while n > 1 do
|
||||
set f = f * n;
|
||||
set n = n - 1;
|
||||
end while;
|
||||
end|
|
||||
|
||||
call ifac(20)|
|
||||
select * from fac|
|
||||
drop table fac|
|
||||
drop procedure ifac|
|
||||
drop procedure fac|
|
||||
|
||||
|
||||
# SELECT INTO local variables
|
||||
create procedure into_test(x char(16), y int)
|
||||
begin
|
||||
|
@ -510,9 +469,37 @@ drop function e|
|
|||
drop function inc|
|
||||
drop function mul|
|
||||
drop function append|
|
||||
drop function fac|
|
||||
drop function fun|
|
||||
|
||||
|
||||
# A "real" procedure and function example
|
||||
|
||||
--disable_warnings
|
||||
drop table if exists fac|
|
||||
--enable_warnings
|
||||
create table fac (n int unsigned not null primary key, f bigint unsigned)|
|
||||
|
||||
create procedure ifac(n int unsigned)
|
||||
begin
|
||||
declare i int unsigned;
|
||||
set i = 1;
|
||||
if n > 20 then
|
||||
set n = 20; # bigint overflow otherwise
|
||||
end if;
|
||||
while i <= n do
|
||||
begin
|
||||
insert into test.fac values (i, fac(i));
|
||||
set i = i + 1;
|
||||
end;
|
||||
end while;
|
||||
end|
|
||||
|
||||
call ifac(20)|
|
||||
select * from fac|
|
||||
drop table fac|
|
||||
drop procedure ifac|
|
||||
drop function fac|
|
||||
|
||||
delimiter ;|
|
||||
drop table t1;
|
||||
drop table t2;
|
||||
|
|
Loading…
Add table
Reference in a new issue