Commit 76b037dc authored by unknown's avatar unknown

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.
parent 0521fb54
......@@ -375,11 +375,21 @@ create table test.t2 select * from test.t1;
insert into test.t2 values (concat(x, "2"), y+2);
end;
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
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
begin
declare f bigint unsigned;
......@@ -390,5 +400,11 @@ set n = n - 1;
end while;
return f;
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 table t1;
......@@ -435,17 +435,31 @@ drop procedure create_select|
# Check that we get the right error, i.e. UDF declaration parses correctly,
# but foo.so doesn't exist.
--error 1126
create function foo returns real soname "foo.so"|
# QQ This generates an error message containing a misleading errno which
# 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.
create function e() returns double
return 2.7182818284590452354|
drop function e|
select e()|
# A minimal function with one argument
create function inc(i int) returns int
return i+1|
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|
# A function with flow control and a RETURN statement
select mul(1,1), mul(3,5), mul(4711, 666)|
# A function with flow control
create function fac(n int unsigned) returns bigint unsigned
begin
declare f bigint unsigned;
......@@ -458,6 +472,11 @@ begin
return f;
end|
select fac(1), fac(2), fac(10)|
drop function e|
drop function inc|
drop function mul|
drop function fac|
delimiter ;|
......
......@@ -28,6 +28,9 @@
#include <time.h>
#include <ft_global.h>
#include <zlib.h>
#include "sp_head.h"
#include "sp_rcontext.h"
#include "sp.h"
/* return TRUE if item is a constant */
......@@ -2770,3 +2773,75 @@ double Item_func_glength::val()
geom.length(&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_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();
};
......@@ -84,6 +84,8 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
ret= SP_GET_FIELD_FAILED;
goto done;
}
close_thread_tables(thd);
table= NULL;
tmplex= lex_start(thd, (uchar*)defstr, strlen(defstr));
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;
done:
if (ret == SP_OK && table)
if (ret != SP_OK && table)
close_thread_tables(thd);
DBUG_RETURN(ret);
}
......@@ -208,7 +210,7 @@ sp_drop_procedure(THD *thd, char *name, uint namelen)
sp_head *
sp_find_function(THD *thd, LEX_STRING *name)
{
DBUG_ENTER("sp_find_function_i");
DBUG_ENTER("sp_find_function");
sp_head *sp;
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);
}
// 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;
}
......@@ -46,4 +46,8 @@ sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen);
int
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_ */
......@@ -24,6 +24,26 @@
#include "sp_pcontext.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
** if nothing else.
*/
......@@ -36,48 +56,28 @@ eval_func_item(THD *thd, Item *it, enum enum_field_types type)
return it; // Shouldn't happen?
/* 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:
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_LONG:
case MYSQL_TYPE_LONGLONG:
case MYSQL_TYPE_INT24:
it= new Item_int(it->val_int());
break;
case MYSQL_TYPE_DECIMAL:
case MYSQL_TYPE_FLOAT:
case MYSQL_TYPE_DOUBLE:
it= new Item_real(it->val());
break;
case MYSQL_TYPE_VAR_STRING:
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];
String tmp(buffer, sizeof(buffer), default_charset_info);
String *s= it->val_str(&tmp);
it= new Item_string(s->c_ptr_quick(), s->length(), default_charset_info);
switch (sp_map_result_type(type)) {
case INT_RESULT:
it= new Item_int(it->val_int());
break;
case REAL_RESULT:
it= new Item_real(it->val());
break;
default:
{
char buffer[MAX_FIELD_WIDTH];
String tmp(buffer, sizeof(buffer), default_charset_info);
String *s= it->val_str(&tmp);
it= new Item_string(s->c_ptr_quick(), s->length(),
default_charset_info);
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;
......@@ -118,12 +118,69 @@ sp_head::create(THD *thd)
DBUG_RETURN(ret);
}
int
sp_head::execute(THD *thd)
{
DBUG_ENTER("sp_head::execute");
DBUG_PRINT("executing", ("procedure %s", ((String *)m_name->const_string())->c_ptr()));
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_pcontext *pctx = m_call_lex->spcont;
uint csize = pctx->max_framesize();
......@@ -135,7 +192,7 @@ sp_head::execute(THD *thd)
if (csize > 0)
{
uint i;
List_iterator_fast<Item> li(m_call_lex->value_list);
List_iterator_fast<Item> li(*args);
Item *it;
nctx = new sp_rcontext(csize);
......@@ -174,20 +231,7 @@ sp_head::execute(THD *thd)
thd->spcont= nctx;
}
{ // Execute instructions...
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);
}
}
ret= execute(thd);
// Don't copy back OUT values if we got an error
if (ret == 0 && csize > 0)
......@@ -424,3 +468,15 @@ sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
*nextp = m_ip+1;
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_PROCEDURE 2
Item_result
sp_map_result_type(enum enum_field_types type);
struct sp_label;
class sp_instr;
......@@ -62,7 +65,10 @@ public:
create(THD *thd);
int
execute(THD *thd);
execute_function(THD *thd, Item **args, uint argcount, Item **resp);
int
execute_procedure(THD *thd, List<Item> *args);
inline void
add_instr(sp_instr *i)
......@@ -103,6 +109,11 @@ public:
return n->c_ptr();
}
inline Item_result result()
{
return sp_map_result_type(m_returns);
}
private:
Item_string *m_name;
......@@ -122,10 +133,14 @@ private:
{
sp_instr *in= NULL;
get_dynamic(&m_instr, (gptr)&in, i);
if (i < m_instr.elements)
get_dynamic(&m_instr, (gptr)&in, i);
return in;
}
int
execute(THD *thd);
}; // class sp_head : public Sql_alloc
......@@ -319,4 +334,28 @@ private:
}; // 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_ */
......@@ -26,7 +26,7 @@ class sp_rcontext : public Sql_alloc
public:
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_outs = (int *)sql_alloc(size * sizeof(int));
......@@ -70,12 +70,25 @@ class sp_rcontext : public Sql_alloc
return m_outs[idx];
}
inline void
set_result(Item *it)
{
m_result= it;
}
inline Item *
get_result()
{
return m_result;
}
private:
uint m_count;
uint m_size;
Item **m_frame;
int *m_outs;
Item *m_result; // For FUNCTIONs
}; // class sp_rcontext : public Sql_alloc
......
......@@ -21,6 +21,8 @@
#include "item_create.h"
#include <m_ctype.h>
#include <hash.h>
#include "sp.h"
#include "sp_head.h"
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;
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)))
......
......@@ -2959,6 +2959,7 @@ mysql_execute_command(THD *thd)
{
uint namelen;
char *name= lex->sphead->name(&namelen);
#ifdef HAVE_DLOPEN
udf_func *udf = find_udf(name, namelen);
if (udf)
......@@ -2966,6 +2967,7 @@ mysql_execute_command(THD *thd)
net_printf(thd, ER_UDF_EXISTS, name);
goto error;
}
#endif
res= lex->sphead->create(thd);
switch (res)
{
......@@ -3000,7 +3002,7 @@ mysql_execute_command(THD *thd)
thd->net.no_send_ok= TRUE;
#endif
res= sp->execute(thd);
res= sp->execute_procedure(thd, &lex->value_list);
#ifndef EMBEDDED_LIBRARY
thd->net.no_send_ok= nsok;
#endif
......
......@@ -506,6 +506,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token ROUND
%token SECOND_SYM
%token SHARE_SYM
%token SP_FUNC
%token SUBSTRING
%token SUBSTRING_INDEX
%token TRIM
......@@ -575,7 +576,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%type <lex_str>
IDENT TEXT_STRING REAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM LEX_HOSTNAME
ULONGLONG_NUM field_ident select_alias ident ident_or_text
UNDERSCORE_CHARSET
UNDERSCORE_CHARSET SP_FUNC ident_or_spfunc
%type <lex_str_ptr>
opt_table_alias
......@@ -903,7 +904,7 @@ create:
lex->name=$4.str;
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->udf.name = $4;
......@@ -929,6 +930,11 @@ create:
}
;
ident_or_spfunc:
IDENT { $$= $1; }
| SP_FUNC { $$= $1; }
;
create_function_tail:
RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING
{
......@@ -1122,7 +1128,11 @@ sp_proc_stmt:
}
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 {}
......@@ -2863,6 +2873,13 @@ simple_expr:
{ $$= new Item_func_round($3,$5,1); }
| TRUE_SYM
{ $$= 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 ')'
{
if ($3 != NULL)
......
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