Made stored FUNCTION invokation work almost always. Still buggy and unstable, and
various known problems, but good enough for a checkpoint commit. mysql-test/r/sp.result: New tests for invoking simple FUNCTIONs. mysql-test/t/sp.test: New tests for invoking simple FUNCTIONs. sql/item_func.cc: New Item_func_sp for stored FUNCTIONs. sql/item_func.h: New Item_func_sp for stored FUNCTIONs. sql/sp.cc: Close mysql.proc table earlier so recursive find_function calls work. Added temporary sp_function_exists() function for checking without parsing. sql/sp.h: Added temporary sp_function_exists() function for checking without parsing. sql/sp_head.cc: New code for executing a FUNCTION. (And reworked some of the old code in the process.) sql/sp_head.h: New code for executing a FUNCTION. sql/sp_rcontext.h: Added result slot for FUNCTIONs. sql/sql_lex.cc: Added check for stored FUNCTION, analogous to UDFs. sql/sql_parse.cc: sp_head::execute was renamed into execute_procedure. sql/sql_yacc.yy: Added parsing of stored FUNCTION invocation and code generation for RETURN statement.
This commit is contained in:
parent
0521fb5444
commit
76b037dc42
@ -375,11 +375,21 @@ create table test.t2 select * from test.t1;
|
|||||||
insert into test.t2 values (concat(x, "2"), y+2);
|
insert into test.t2 values (concat(x, "2"), y+2);
|
||||||
end;
|
end;
|
||||||
drop procedure create_select;
|
drop procedure create_select;
|
||||||
create function foo returns real soname "foo.so";
|
|
||||||
Can't open shared library 'foo.so' (errno: 22 foo.so: cannot open shared object file: No such file or director)
|
|
||||||
create function e() returns double
|
create function e() returns double
|
||||||
return 2.7182818284590452354;
|
return 2.7182818284590452354;
|
||||||
drop function e;
|
select e();
|
||||||
|
e()
|
||||||
|
2.718281828459
|
||||||
|
create function inc(i int) returns int
|
||||||
|
return i+1;
|
||||||
|
select inc(1), inc(99), inc(-71);
|
||||||
|
inc(1) inc(99) inc(-71)
|
||||||
|
2 100 -70
|
||||||
|
create function mul(x int, y int) returns int
|
||||||
|
return x*y;
|
||||||
|
select mul(1,1), mul(3,5), mul(4711, 666);
|
||||||
|
mul(1,1) mul(3,5) mul(4711, 666)
|
||||||
|
1 15 3137526
|
||||||
create function fac(n int unsigned) returns bigint unsigned
|
create function fac(n int unsigned) returns bigint unsigned
|
||||||
begin
|
begin
|
||||||
declare f bigint unsigned;
|
declare f bigint unsigned;
|
||||||
@ -390,5 +400,11 @@ set n = n - 1;
|
|||||||
end while;
|
end while;
|
||||||
return f;
|
return f;
|
||||||
end;
|
end;
|
||||||
|
select fac(1), fac(2), fac(10);
|
||||||
|
fac(1) fac(2) fac(10)
|
||||||
|
1 2 3628800
|
||||||
|
drop function e;
|
||||||
|
drop function inc;
|
||||||
|
drop function mul;
|
||||||
drop function fac;
|
drop function fac;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
@ -435,17 +435,31 @@ drop procedure create_select|
|
|||||||
|
|
||||||
# Check that we get the right error, i.e. UDF declaration parses correctly,
|
# Check that we get the right error, i.e. UDF declaration parses correctly,
|
||||||
# but foo.so doesn't exist.
|
# but foo.so doesn't exist.
|
||||||
--error 1126
|
# QQ This generates an error message containing a misleading errno which
|
||||||
create function foo returns real soname "foo.so"|
|
# might vary between systems (it usually doesn't have anything to do with
|
||||||
|
# the actual failing dlopen()).
|
||||||
|
#--error 1126
|
||||||
|
#create function foo returns real soname "foo.so"|
|
||||||
|
|
||||||
# A minimal, constant FUNCTION.
|
# A minimal, constant FUNCTION.
|
||||||
create function e() returns double
|
create function e() returns double
|
||||||
return 2.7182818284590452354|
|
return 2.7182818284590452354|
|
||||||
|
|
||||||
drop function e|
|
select e()|
|
||||||
|
|
||||||
|
# A minimal function with one argument
|
||||||
|
create function inc(i int) returns int
|
||||||
|
return i+1|
|
||||||
|
|
||||||
# A function with flow control and a RETURN statement
|
select inc(1), inc(99), inc(-71)|
|
||||||
|
|
||||||
|
# A minimal function with two arguments
|
||||||
|
create function mul(x int, y int) returns int
|
||||||
|
return x*y|
|
||||||
|
|
||||||
|
select mul(1,1), mul(3,5), mul(4711, 666)|
|
||||||
|
|
||||||
|
# A function with flow control
|
||||||
create function fac(n int unsigned) returns bigint unsigned
|
create function fac(n int unsigned) returns bigint unsigned
|
||||||
begin
|
begin
|
||||||
declare f bigint unsigned;
|
declare f bigint unsigned;
|
||||||
@ -458,6 +472,11 @@ begin
|
|||||||
return f;
|
return f;
|
||||||
end|
|
end|
|
||||||
|
|
||||||
|
select fac(1), fac(2), fac(10)|
|
||||||
|
|
||||||
|
drop function e|
|
||||||
|
drop function inc|
|
||||||
|
drop function mul|
|
||||||
drop function fac|
|
drop function fac|
|
||||||
|
|
||||||
delimiter ;|
|
delimiter ;|
|
||||||
|
@ -28,6 +28,9 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <ft_global.h>
|
#include <ft_global.h>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
#include "sp_head.h"
|
||||||
|
#include "sp_rcontext.h"
|
||||||
|
#include "sp.h"
|
||||||
|
|
||||||
/* return TRUE if item is a constant */
|
/* return TRUE if item is a constant */
|
||||||
|
|
||||||
@ -2770,3 +2773,75 @@ double Item_func_glength::val()
|
|||||||
geom.length(&res));
|
geom.length(&res));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
Item_func_sp::execute(Item **itp)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("Item_func_sp::execute");
|
||||||
|
THD *thd= current_thd;
|
||||||
|
|
||||||
|
if (!m_sp && !(m_sp= sp_find_function(thd, &m_name)))
|
||||||
|
DBUG_RETURN(-1);
|
||||||
|
|
||||||
|
DBUG_RETURN(m_sp->execute_function(thd, args, arg_count, itp));
|
||||||
|
}
|
||||||
|
|
||||||
|
Item_result
|
||||||
|
Item_func_sp::result_type() const
|
||||||
|
{
|
||||||
|
DBUG_ENTER("Item_func_sp::result_type");
|
||||||
|
DBUG_PRINT("info", ("m_sp = %p", m_sp));
|
||||||
|
|
||||||
|
if (m_sp)
|
||||||
|
{
|
||||||
|
DBUG_RETURN(m_sp->result());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sp_head *sp= sp_find_function(current_thd, (LEX_STRING *)(&m_name));
|
||||||
|
if (sp)
|
||||||
|
DBUG_RETURN(m_sp->result());
|
||||||
|
DBUG_RETURN(STRING_RESULT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Item_func_sp::make_field(Send_field *field)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("Item_func_sp::make_field");
|
||||||
|
Item *it;
|
||||||
|
|
||||||
|
if (!execute(&it))
|
||||||
|
{
|
||||||
|
it->set_name(name, 0);
|
||||||
|
init_make_field(field, field_type());
|
||||||
|
it->make_field(field);
|
||||||
|
}
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
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)))
|
||||||
|
{
|
||||||
|
switch (m_sp->result()) {
|
||||||
|
case STRING_RESULT:
|
||||||
|
maybe_null= 1;
|
||||||
|
max_length= 0;
|
||||||
|
break;
|
||||||
|
case REAL_RESULT:
|
||||||
|
decimals= NOT_FIXED_DEC;
|
||||||
|
max_length= float_length(decimals);
|
||||||
|
break;
|
||||||
|
case INT_RESULT:
|
||||||
|
decimals= 0;
|
||||||
|
max_length= 21;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
@ -1147,3 +1147,69 @@ enum Item_cast
|
|||||||
ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT,
|
ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT,
|
||||||
ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME, ITEM_CAST_CHAR
|
ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME, ITEM_CAST_CHAR
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Stored FUNCTIONs
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
class sp_head;
|
||||||
|
|
||||||
|
class Item_func_sp :public Item_func
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
LEX_STRING m_name;
|
||||||
|
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(LEX_STRING name, List<Item> &list)
|
||||||
|
:Item_func(list), m_name(name), m_sp(NULL)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual ~Item_func_sp()
|
||||||
|
{}
|
||||||
|
|
||||||
|
const char *func_name() const
|
||||||
|
{
|
||||||
|
return m_name.str;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item_result result_type() const;
|
||||||
|
|
||||||
|
longlong val_int()
|
||||||
|
{
|
||||||
|
return (longlong)Item_func_sp::val();
|
||||||
|
}
|
||||||
|
|
||||||
|
double val()
|
||||||
|
{
|
||||||
|
Item *it;
|
||||||
|
|
||||||
|
if (execute(&it))
|
||||||
|
return 0.0;
|
||||||
|
return it->val();
|
||||||
|
}
|
||||||
|
|
||||||
|
String *val_str(String *str)
|
||||||
|
{
|
||||||
|
Item *it;
|
||||||
|
|
||||||
|
if (execute(&it))
|
||||||
|
return NULL;
|
||||||
|
return it->val_str(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_field(Send_field *field);
|
||||||
|
|
||||||
|
void fix_length_and_dec();
|
||||||
|
|
||||||
|
};
|
||||||
|
21
sql/sp.cc
21
sql/sp.cc
@ -84,6 +84,8 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
|
|||||||
ret= SP_GET_FIELD_FAILED;
|
ret= SP_GET_FIELD_FAILED;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
close_thread_tables(thd);
|
||||||
|
table= NULL;
|
||||||
|
|
||||||
tmplex= lex_start(thd, (uchar*)defstr, strlen(defstr));
|
tmplex= lex_start(thd, (uchar*)defstr, strlen(defstr));
|
||||||
if (yyparse(thd) || thd->is_fatal_error || tmplex->sphead == NULL)
|
if (yyparse(thd) || thd->is_fatal_error || tmplex->sphead == NULL)
|
||||||
@ -92,7 +94,7 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
|
|||||||
*sphp= tmplex->sphead;
|
*sphp= tmplex->sphead;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (ret == SP_OK && table)
|
if (ret != SP_OK && table)
|
||||||
close_thread_tables(thd);
|
close_thread_tables(thd);
|
||||||
DBUG_RETURN(ret);
|
DBUG_RETURN(ret);
|
||||||
}
|
}
|
||||||
@ -208,7 +210,7 @@ sp_drop_procedure(THD *thd, char *name, uint namelen)
|
|||||||
sp_head *
|
sp_head *
|
||||||
sp_find_function(THD *thd, LEX_STRING *name)
|
sp_find_function(THD *thd, LEX_STRING *name)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("sp_find_function_i");
|
DBUG_ENTER("sp_find_function");
|
||||||
sp_head *sp;
|
sp_head *sp;
|
||||||
|
|
||||||
DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
|
DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
|
||||||
@ -243,3 +245,18 @@ sp_drop_function(THD *thd, char *name, uint namelen)
|
|||||||
|
|
||||||
DBUG_RETURN(ret);
|
DBUG_RETURN(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QQ Temporary until the function call detection in sql_lex has been reworked.
|
||||||
|
bool
|
||||||
|
sp_function_exists(THD *thd, LEX_STRING *name)
|
||||||
|
{
|
||||||
|
TABLE *table;
|
||||||
|
|
||||||
|
if (db_find_routine_aux(thd, TYPE_ENUM_FUNCTION,
|
||||||
|
name->str, name->length, TL_READ, &table) == SP_OK)
|
||||||
|
{
|
||||||
|
close_thread_tables(thd);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
4
sql/sp.h
4
sql/sp.h
@ -46,4 +46,8 @@ sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen);
|
|||||||
int
|
int
|
||||||
sp_drop_function(THD *thd, char *name, uint namelen);
|
sp_drop_function(THD *thd, char *name, uint namelen);
|
||||||
|
|
||||||
|
// QQ Temporary until the function call detection in sql_lex has been reworked.
|
||||||
|
bool
|
||||||
|
sp_function_exists(THD *thd, LEX_STRING *name);
|
||||||
|
|
||||||
#endif /* _SP_H_ */
|
#endif /* _SP_H_ */
|
||||||
|
148
sql/sp_head.cc
148
sql/sp_head.cc
@ -24,6 +24,26 @@
|
|||||||
#include "sp_pcontext.h"
|
#include "sp_pcontext.h"
|
||||||
#include "sp_rcontext.h"
|
#include "sp_rcontext.h"
|
||||||
|
|
||||||
|
Item_result
|
||||||
|
sp_map_result_type(enum enum_field_types type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case MYSQL_TYPE_TINY:
|
||||||
|
case MYSQL_TYPE_SHORT:
|
||||||
|
case MYSQL_TYPE_LONG:
|
||||||
|
case MYSQL_TYPE_LONGLONG:
|
||||||
|
case MYSQL_TYPE_INT24:
|
||||||
|
return INT_RESULT;
|
||||||
|
case MYSQL_TYPE_DECIMAL:
|
||||||
|
case MYSQL_TYPE_FLOAT:
|
||||||
|
case MYSQL_TYPE_DOUBLE:
|
||||||
|
return REAL_RESULT;
|
||||||
|
default:
|
||||||
|
return STRING_RESULT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Evaluate a (presumed) func item. Always returns an item, the parameter
|
/* Evaluate a (presumed) func item. Always returns an item, the parameter
|
||||||
** if nothing else.
|
** if nothing else.
|
||||||
*/
|
*/
|
||||||
@ -36,48 +56,28 @@ eval_func_item(THD *thd, Item *it, enum enum_field_types type)
|
|||||||
return it; // Shouldn't happen?
|
return it; // Shouldn't happen?
|
||||||
|
|
||||||
/* QQ How do we do this? Is there some better way? */
|
/* QQ How do we do this? Is there some better way? */
|
||||||
switch (type)
|
if (type == MYSQL_TYPE_NULL)
|
||||||
|
it= new Item_null();
|
||||||
|
else
|
||||||
{
|
{
|
||||||
case MYSQL_TYPE_TINY:
|
switch (sp_map_result_type(type)) {
|
||||||
case MYSQL_TYPE_SHORT:
|
case INT_RESULT:
|
||||||
case MYSQL_TYPE_LONG:
|
|
||||||
case MYSQL_TYPE_LONGLONG:
|
|
||||||
case MYSQL_TYPE_INT24:
|
|
||||||
it= new Item_int(it->val_int());
|
it= new Item_int(it->val_int());
|
||||||
break;
|
break;
|
||||||
case MYSQL_TYPE_DECIMAL:
|
case REAL_RESULT:
|
||||||
case MYSQL_TYPE_FLOAT:
|
|
||||||
case MYSQL_TYPE_DOUBLE:
|
|
||||||
it= new Item_real(it->val());
|
it= new Item_real(it->val());
|
||||||
break;
|
break;
|
||||||
case MYSQL_TYPE_VAR_STRING:
|
default:
|
||||||
case MYSQL_TYPE_STRING:
|
|
||||||
case MYSQL_TYPE_TIMESTAMP:
|
|
||||||
case MYSQL_TYPE_DATE:
|
|
||||||
case MYSQL_TYPE_TIME:
|
|
||||||
case MYSQL_TYPE_DATETIME:
|
|
||||||
case MYSQL_TYPE_YEAR:
|
|
||||||
case MYSQL_TYPE_NEWDATE:
|
|
||||||
{
|
{
|
||||||
char buffer[MAX_FIELD_WIDTH];
|
char buffer[MAX_FIELD_WIDTH];
|
||||||
String tmp(buffer, sizeof(buffer), default_charset_info);
|
String tmp(buffer, sizeof(buffer), default_charset_info);
|
||||||
String *s= it->val_str(&tmp);
|
String *s= it->val_str(&tmp);
|
||||||
|
|
||||||
it= new Item_string(s->c_ptr_quick(), s->length(), default_charset_info);
|
it= new Item_string(s->c_ptr_quick(), s->length(),
|
||||||
|
default_charset_info);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MYSQL_TYPE_NULL:
|
}
|
||||||
it= new Item_null(); // A NULL is a NULL is a NULL...
|
|
||||||
break;
|
|
||||||
case MYSQL_TYPE_ENUM:
|
|
||||||
case MYSQL_TYPE_SET:
|
|
||||||
case MYSQL_TYPE_TINY_BLOB:
|
|
||||||
case MYSQL_TYPE_MEDIUM_BLOB:
|
|
||||||
case MYSQL_TYPE_LONG_BLOB:
|
|
||||||
case MYSQL_TYPE_BLOB:
|
|
||||||
case MYSQL_TYPE_GEOMETRY:
|
|
||||||
/* QQ Don't know what to do with the rest. */
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return it;
|
return it;
|
||||||
@ -118,12 +118,69 @@ sp_head::create(THD *thd)
|
|||||||
DBUG_RETURN(ret);
|
DBUG_RETURN(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
sp_head::execute(THD *thd)
|
sp_head::execute(THD *thd)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("sp_head::execute");
|
DBUG_ENTER("sp_head::execute");
|
||||||
DBUG_PRINT("executing", ("procedure %s", ((String *)m_name->const_string())->c_ptr()));
|
|
||||||
int ret= 0;
|
int ret= 0;
|
||||||
|
uint ip= 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
sp_instr *i;
|
||||||
|
|
||||||
|
i = get_instr(ip); // Returns NULL when we're done.
|
||||||
|
if (i == NULL)
|
||||||
|
break;
|
||||||
|
DBUG_PRINT("execute", ("Instruction %u", ip));
|
||||||
|
ret= i->execute(thd, &ip);
|
||||||
|
} while (ret == 0);
|
||||||
|
DBUG_RETURN(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("sp_head::execute");
|
||||||
|
DBUG_PRINT("executing", ("procedure %s", ((String *)m_name->const_string())->c_ptr()));
|
||||||
|
sp_pcontext *pctx = m_call_lex->spcont;
|
||||||
|
uint csize = pctx->max_framesize();
|
||||||
|
uint params = pctx->params();
|
||||||
|
sp_rcontext *octx = thd->spcont;
|
||||||
|
sp_rcontext *nctx = NULL;
|
||||||
|
uint i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
// QQ Should have some error checking here? (no. of args, types, etc...)
|
||||||
|
nctx= new sp_rcontext(csize);
|
||||||
|
for (i= 0 ; i < params && i < argcount ; i++)
|
||||||
|
{
|
||||||
|
sp_pvar_t *pvar = pctx->find_pvar(i);
|
||||||
|
|
||||||
|
nctx->push_item(eval_func_item(thd, *argp++, pvar->type));
|
||||||
|
}
|
||||||
|
// The rest of the frame are local variables which are all IN.
|
||||||
|
// QQ See comment in execute_procedure below.
|
||||||
|
for (; i < csize ; i++)
|
||||||
|
nctx->push_item(NULL);
|
||||||
|
thd->spcont= nctx;
|
||||||
|
|
||||||
|
ret= execute(thd);
|
||||||
|
if (ret == 0)
|
||||||
|
*resp= nctx->get_result();
|
||||||
|
|
||||||
|
thd->spcont= octx;
|
||||||
|
DBUG_RETURN(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sp_head::execute_procedure(THD *thd, List<Item> *args)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("sp_head::execute");
|
||||||
|
DBUG_PRINT("executing", ("procedure %s", ((String *)m_name->const_string())->c_ptr()));
|
||||||
|
int ret;
|
||||||
sp_instr *p;
|
sp_instr *p;
|
||||||
sp_pcontext *pctx = m_call_lex->spcont;
|
sp_pcontext *pctx = m_call_lex->spcont;
|
||||||
uint csize = pctx->max_framesize();
|
uint csize = pctx->max_framesize();
|
||||||
@ -135,7 +192,7 @@ sp_head::execute(THD *thd)
|
|||||||
if (csize > 0)
|
if (csize > 0)
|
||||||
{
|
{
|
||||||
uint i;
|
uint i;
|
||||||
List_iterator_fast<Item> li(m_call_lex->value_list);
|
List_iterator_fast<Item> li(*args);
|
||||||
Item *it;
|
Item *it;
|
||||||
|
|
||||||
nctx = new sp_rcontext(csize);
|
nctx = new sp_rcontext(csize);
|
||||||
@ -174,20 +231,7 @@ sp_head::execute(THD *thd)
|
|||||||
thd->spcont= nctx;
|
thd->spcont= nctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Execute instructions...
|
ret= execute(thd);
|
||||||
uint ip= 0;
|
|
||||||
|
|
||||||
while (ret == 0)
|
|
||||||
{
|
|
||||||
sp_instr *i;
|
|
||||||
|
|
||||||
i = get_instr(ip); // Returns NULL when we're done.
|
|
||||||
if (i == NULL)
|
|
||||||
break;
|
|
||||||
DBUG_PRINT("execute", ("Instruction %u", ip));
|
|
||||||
ret= i->execute(thd, &ip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't copy back OUT values if we got an error
|
// Don't copy back OUT values if we got an error
|
||||||
if (ret == 0 && csize > 0)
|
if (ret == 0 && csize > 0)
|
||||||
@ -424,3 +468,15 @@ sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
|
|||||||
*nextp = m_ip+1;
|
*nextp = m_ip+1;
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// sp_instr_return
|
||||||
|
//
|
||||||
|
int
|
||||||
|
sp_instr_return::execute(THD *thd, uint *nextp)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("sp_instr_return::execute");
|
||||||
|
thd->spcont->set_result(eval_func_item(thd, m_value, m_type));
|
||||||
|
*nextp= UINT_MAX;
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
@ -29,6 +29,9 @@
|
|||||||
#define TYPE_ENUM_FUNCTION 1
|
#define TYPE_ENUM_FUNCTION 1
|
||||||
#define TYPE_ENUM_PROCEDURE 2
|
#define TYPE_ENUM_PROCEDURE 2
|
||||||
|
|
||||||
|
Item_result
|
||||||
|
sp_map_result_type(enum enum_field_types type);
|
||||||
|
|
||||||
struct sp_label;
|
struct sp_label;
|
||||||
|
|
||||||
class sp_instr;
|
class sp_instr;
|
||||||
@ -62,7 +65,10 @@ public:
|
|||||||
create(THD *thd);
|
create(THD *thd);
|
||||||
|
|
||||||
int
|
int
|
||||||
execute(THD *thd);
|
execute_function(THD *thd, Item **args, uint argcount, Item **resp);
|
||||||
|
|
||||||
|
int
|
||||||
|
execute_procedure(THD *thd, List<Item> *args);
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
add_instr(sp_instr *i)
|
add_instr(sp_instr *i)
|
||||||
@ -103,6 +109,11 @@ public:
|
|||||||
return n->c_ptr();
|
return n->c_ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Item_result result()
|
||||||
|
{
|
||||||
|
return sp_map_result_type(m_returns);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Item_string *m_name;
|
Item_string *m_name;
|
||||||
@ -122,10 +133,14 @@ private:
|
|||||||
{
|
{
|
||||||
sp_instr *in= NULL;
|
sp_instr *in= NULL;
|
||||||
|
|
||||||
|
if (i < m_instr.elements)
|
||||||
get_dynamic(&m_instr, (gptr)&in, i);
|
get_dynamic(&m_instr, (gptr)&in, i);
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
execute(THD *thd);
|
||||||
|
|
||||||
}; // class sp_head : public Sql_alloc
|
}; // class sp_head : public Sql_alloc
|
||||||
|
|
||||||
|
|
||||||
@ -319,4 +334,28 @@ private:
|
|||||||
|
|
||||||
}; // class sp_instr_jump_if_not : public sp_instr_jump
|
}; // class sp_instr_jump_if_not : public sp_instr_jump
|
||||||
|
|
||||||
|
|
||||||
|
class sp_instr_return : public sp_instr
|
||||||
|
{
|
||||||
|
sp_instr_return(const sp_instr_return &); /* Prevent use of these */
|
||||||
|
void operator=(sp_instr_return &);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
sp_instr_return(uint ip, Item *val, enum enum_field_types type)
|
||||||
|
: sp_instr(ip), m_value(val), m_type(type)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual ~sp_instr_return()
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual int execute(THD *thd, uint *nextp);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
Item *m_value;
|
||||||
|
enum enum_field_types m_type;
|
||||||
|
|
||||||
|
}; // class sp_instr_return : public sp_instr
|
||||||
|
|
||||||
#endif /* _SP_HEAD_H_ */
|
#endif /* _SP_HEAD_H_ */
|
||||||
|
@ -26,7 +26,7 @@ class sp_rcontext : public Sql_alloc
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
sp_rcontext(uint size)
|
sp_rcontext(uint size)
|
||||||
: m_count(0), m_size(size)
|
: m_count(0), m_size(size), m_result(NULL)
|
||||||
{
|
{
|
||||||
m_frame = (Item **)sql_alloc(size * sizeof(Item*));
|
m_frame = (Item **)sql_alloc(size * sizeof(Item*));
|
||||||
m_outs = (int *)sql_alloc(size * sizeof(int));
|
m_outs = (int *)sql_alloc(size * sizeof(int));
|
||||||
@ -70,12 +70,25 @@ class sp_rcontext : public Sql_alloc
|
|||||||
return m_outs[idx];
|
return m_outs[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
set_result(Item *it)
|
||||||
|
{
|
||||||
|
m_result= it;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Item *
|
||||||
|
get_result()
|
||||||
|
{
|
||||||
|
return m_result;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
uint m_count;
|
uint m_count;
|
||||||
uint m_size;
|
uint m_size;
|
||||||
Item **m_frame;
|
Item **m_frame;
|
||||||
int *m_outs;
|
int *m_outs;
|
||||||
|
Item *m_result; // For FUNCTIONs
|
||||||
|
|
||||||
}; // class sp_rcontext : public Sql_alloc
|
}; // class sp_rcontext : public Sql_alloc
|
||||||
|
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
#include "item_create.h"
|
#include "item_create.h"
|
||||||
#include <m_ctype.h>
|
#include <m_ctype.h>
|
||||||
#include <hash.h>
|
#include <hash.h>
|
||||||
|
#include "sp.h"
|
||||||
|
#include "sp_head.h"
|
||||||
|
|
||||||
LEX_STRING tmp_table_alias= {(char*) "tmp-table",8};
|
LEX_STRING tmp_table_alias= {(char*) "tmp-table",8};
|
||||||
|
|
||||||
@ -197,6 +199,17 @@ static int find_keyword(LEX *lex, uint len, bool function)
|
|||||||
lex->yylval->symbol.length=len;
|
lex->yylval->symbol.length=len;
|
||||||
return symbol->tok;
|
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
|
#ifdef HAVE_DLOPEN
|
||||||
udf_func *udf;
|
udf_func *udf;
|
||||||
if (function && using_udf_functions && (udf=find_udf((char*) tok, len)))
|
if (function && using_udf_functions && (udf=find_udf((char*) tok, len)))
|
||||||
|
@ -2959,6 +2959,7 @@ mysql_execute_command(THD *thd)
|
|||||||
{
|
{
|
||||||
uint namelen;
|
uint namelen;
|
||||||
char *name= lex->sphead->name(&namelen);
|
char *name= lex->sphead->name(&namelen);
|
||||||
|
#ifdef HAVE_DLOPEN
|
||||||
udf_func *udf = find_udf(name, namelen);
|
udf_func *udf = find_udf(name, namelen);
|
||||||
|
|
||||||
if (udf)
|
if (udf)
|
||||||
@ -2966,6 +2967,7 @@ mysql_execute_command(THD *thd)
|
|||||||
net_printf(thd, ER_UDF_EXISTS, name);
|
net_printf(thd, ER_UDF_EXISTS, name);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
res= lex->sphead->create(thd);
|
res= lex->sphead->create(thd);
|
||||||
switch (res)
|
switch (res)
|
||||||
{
|
{
|
||||||
@ -3000,7 +3002,7 @@ mysql_execute_command(THD *thd)
|
|||||||
|
|
||||||
thd->net.no_send_ok= TRUE;
|
thd->net.no_send_ok= TRUE;
|
||||||
#endif
|
#endif
|
||||||
res= sp->execute(thd);
|
res= sp->execute_procedure(thd, &lex->value_list);
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
thd->net.no_send_ok= nsok;
|
thd->net.no_send_ok= nsok;
|
||||||
#endif
|
#endif
|
||||||
|
@ -506,6 +506,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
|||||||
%token ROUND
|
%token ROUND
|
||||||
%token SECOND_SYM
|
%token SECOND_SYM
|
||||||
%token SHARE_SYM
|
%token SHARE_SYM
|
||||||
|
%token SP_FUNC
|
||||||
%token SUBSTRING
|
%token SUBSTRING
|
||||||
%token SUBSTRING_INDEX
|
%token SUBSTRING_INDEX
|
||||||
%token TRIM
|
%token TRIM
|
||||||
@ -575,7 +576,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
|||||||
%type <lex_str>
|
%type <lex_str>
|
||||||
IDENT TEXT_STRING REAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM LEX_HOSTNAME
|
IDENT TEXT_STRING REAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM LEX_HOSTNAME
|
||||||
ULONGLONG_NUM field_ident select_alias ident ident_or_text
|
ULONGLONG_NUM field_ident select_alias ident ident_or_text
|
||||||
UNDERSCORE_CHARSET
|
UNDERSCORE_CHARSET SP_FUNC ident_or_spfunc
|
||||||
|
|
||||||
%type <lex_str_ptr>
|
%type <lex_str_ptr>
|
||||||
opt_table_alias
|
opt_table_alias
|
||||||
@ -903,7 +904,7 @@ create:
|
|||||||
lex->name=$4.str;
|
lex->name=$4.str;
|
||||||
lex->create_info.options=$3;
|
lex->create_info.options=$3;
|
||||||
}
|
}
|
||||||
| CREATE udf_func_type FUNCTION_SYM IDENT
|
| CREATE udf_func_type FUNCTION_SYM ident_or_spfunc
|
||||||
{
|
{
|
||||||
LEX *lex=Lex;
|
LEX *lex=Lex;
|
||||||
lex->udf.name = $4;
|
lex->udf.name = $4;
|
||||||
@ -929,6 +930,11 @@ create:
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
ident_or_spfunc:
|
||||||
|
IDENT { $$= $1; }
|
||||||
|
| SP_FUNC { $$= $1; }
|
||||||
|
;
|
||||||
|
|
||||||
create_function_tail:
|
create_function_tail:
|
||||||
RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING
|
RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING
|
||||||
{
|
{
|
||||||
@ -1122,7 +1128,11 @@ sp_proc_stmt:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* QQ nothing yet */
|
sp_instr_return *i=
|
||||||
|
new sp_instr_return(lex->sphead->instructions(),
|
||||||
|
$2, lex->sphead->m_returns);
|
||||||
|
|
||||||
|
lex->sphead->add_instr(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| IF sp_if END IF {}
|
| IF sp_if END IF {}
|
||||||
@ -2863,6 +2873,13 @@ simple_expr:
|
|||||||
{ $$= new Item_func_round($3,$5,1); }
|
{ $$= new Item_func_round($3,$5,1); }
|
||||||
| TRUE_SYM
|
| TRUE_SYM
|
||||||
{ $$= new Item_int((char*) "TRUE",1,1); }
|
{ $$= new Item_int((char*) "TRUE",1,1); }
|
||||||
|
| SP_FUNC '(' udf_expr_list ')'
|
||||||
|
{
|
||||||
|
if ($3)
|
||||||
|
$$= new Item_func_sp($1, *$3);
|
||||||
|
else
|
||||||
|
$$= new Item_func_sp($1);
|
||||||
|
}
|
||||||
| UDA_CHAR_SUM '(' udf_expr_list ')'
|
| UDA_CHAR_SUM '(' udf_expr_list ')'
|
||||||
{
|
{
|
||||||
if ($3 != NULL)
|
if ($3 != NULL)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user