Commit c4b76c26 authored by pem@mysql.com's avatar pem@mysql.com

New FUNCTION documentation, and a minor test case modification.

parent 8049daf2
...@@ -172,8 +172,9 @@ calling a PROCEDURE. ...@@ -172,8 +172,9 @@ calling a PROCEDURE.
with the addition that a FUNCTION has a return type and a RETURN with the addition that a FUNCTION has a return type and a RETURN
statement, but no OUT or INOUT parameters. statement, but no OUT or INOUT parameters.
[QQ - More details here; sp_head needs a result slot and a type flag The main difference during parsing is that we store the result type
indicating if it's a function or procedure] in the sp_head. However, there are big differences when it comes to
invoking a FUNCTION. (See below.)
- Storing, caching, dropping... - Storing, caching, dropping...
...@@ -273,7 +274,64 @@ calling a PROCEDURE. ...@@ -273,7 +274,64 @@ calling a PROCEDURE.
So, stored functions must be handled in a simpilar way, and as a So, stored functions must be handled in a simpilar way, and as a
consequence, UDFs and functions must not have the same name. 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 - Parsing DROP PROCEDURE/FUNCTION
......
...@@ -3,7 +3,6 @@ Stored Procedures implemented 2003-02-02: ...@@ -3,7 +3,6 @@ Stored Procedures implemented 2003-02-02:
Summary of Not Yet Implemented: Summary of Not Yet Implemented:
- FUNCTIONs
- Routine characteristics - Routine characteristics
- External languages - External languages
- Access control - Access control
...@@ -26,14 +25,14 @@ Summary of what's implemented: ...@@ -26,14 +25,14 @@ Summary of what's implemented:
List of what's implemented: List of what's implemented:
- CREATE PROCEDURE name ( args ) body - CREATE PROCEDURE|FUNCTION name ( args ) body
No routine characteristics yet. No routine characteristics yet.
- ALTER PROCEDURE name ... - ALTER PROCEDURE|FUNCTION name ...
Is parsed, but a no-op (as there are no characteristics implemented yet). Is parsed, but a no-op (as there are no characteristics implemented yet).
CASCADE/RESTRICT is not implemented (and CASCADE probably will not be). 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). CASCADE/RESTRICT is not implemented (and CASCADE probably will not be).
- CALL name (args) - CALL name (args)
...@@ -45,7 +44,7 @@ List of what's implemented: ...@@ -45,7 +44,7 @@ List of what's implemented:
will either generate an error message, or it might even work to will either generate an error message, or it might even work to
call all procedures from the top-level. call all procedures from the top-level.
- Procedure body: - Function/Procedure body:
- BEGIN/END - BEGIN/END
Is parsed, but not the real thing with (optional) transaction Is parsed, but not the real thing with (optional) transaction
control, it only serves as block syntax for multiple statements (and control, it only serves as block syntax for multiple statements (and
...@@ -82,9 +81,23 @@ Closed questions: ...@@ -82,9 +81,23 @@ Closed questions:
Answer: Same tables, with an additional key-field for the type. 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 - 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 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 if we should keep it this way, or implement type checking. Possibly we
should have optional, uset-settable, type checking. 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 ...@@ -277,59 +277,6 @@ h1 1
h? 17 h? 17
delete from t1; delete from t1;
drop procedure h; 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) create procedure into_test(x char(16), y int)
begin begin
insert into test.t1 values (x, y); insert into test.t1 values (x, y);
...@@ -441,7 +388,48 @@ drop function e; ...@@ -441,7 +388,48 @@ drop function e;
drop function inc; drop function inc;
drop function mul; drop function mul;
drop function append; drop function append;
drop function fac;
drop function fun; 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 t1;
drop table t2; drop table t2;
...@@ -325,47 +325,6 @@ delete from t1| ...@@ -325,47 +325,6 @@ delete from t1|
drop procedure h| 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 # SELECT INTO local variables
create procedure into_test(x char(16), y int) create procedure into_test(x char(16), y int)
begin begin
...@@ -510,9 +469,37 @@ drop function e| ...@@ -510,9 +469,37 @@ drop function e|
drop function inc| drop function inc|
drop function mul| drop function mul|
drop function append| drop function append|
drop function fac|
drop function fun| 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 ;| delimiter ;|
drop table t1; drop table t1;
drop table t2; drop table t2;
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment