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

Made stored FUNCTION invokation work almost always. Still buggy and unstable, and

various known problems, but good enough for a checkpoint commit.
parent d8c75ec8
...@@ -375,11 +375,21 @@ create table test.t2 select * from test.t1; ...@@ -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; ...@@ -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| ...@@ -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|
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 create function fac(n int unsigned) returns bigint unsigned
begin begin
declare f bigint unsigned; declare f bigint unsigned;
...@@ -458,6 +472,11 @@ begin ...@@ -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 @@ ...@@ -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() ...@@ -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 ...@@ -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();
};
...@@ -84,6 +84,8 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
}
...@@ -46,4 +46,8 @@ sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen); ...@@ -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_ */
...@@ -24,6 +24,26 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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 ...@@ -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 ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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); ...@@ -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); ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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)
......
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