mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
Made FUNCTIONs work in insert and select queries, as well as nested function invocations.
Had to add a cahing mechanism which is in parts an ugly kludge, but it will be reworked once the real SP caching is implemented. mysql-test/r/sp.result: New function tests. mysql-test/t/sp.test: New function tests. sql/sp.cc: Big rehack of mysql.proc table usage strategy and adding a function cache mechanism, since we need to read used functions from the db before doing anything else when executing a query. (This cache is temporary and will probably be replaced by the real thing later.) sql/sp.h: New (temporary) FUNCTION caching functions. sql/sp_head.cc: Fixed some bugs in the function and procedure execution. Disabled some data collections that's not used at the moment. sql/sp_head.h: Fixed some bugs in the function and procedure execution. Disabled some data collections that's not used at the moment. sql/sql_class.h: Added SP function cache list to thd. sql/sql_lex.cc: Added SP function name list to lex. sql/sql_lex.h: Added SP function name list to lex. sql/sql_parse.cc: Read used FUNCTIONs from db and cache them in thd before doing anything else in a query execution. (This is necessary since we can't open mysql.proc during query execution.) sql/sql_yacc.yy: Collect used function names in lex.
This commit is contained in:
parent
1ff79b61a0
commit
8a9422bd2a
11 changed files with 259 additions and 41 deletions
|
@ -1,9 +1,15 @@
|
|||
use test;
|
||||
drop table if exists t1;
|
||||
drop table if exists t2;
|
||||
create table t1 (
|
||||
id char(16) not null,
|
||||
data int not null
|
||||
);
|
||||
create table t2 (
|
||||
s char(16) not null,
|
||||
i int not null,
|
||||
d double not null
|
||||
);
|
||||
create procedure foo42()
|
||||
insert into test.t1 values ("foo", 42);
|
||||
call foo42();
|
||||
|
@ -409,9 +415,33 @@ end;
|
|||
select fac(1), fac(2), fac(5), fac(10);
|
||||
fac(1) fac(2) fac(5) fac(10)
|
||||
1 2 120 3628800
|
||||
create function fun(d double, i int, u int unsigned) returns double
|
||||
return mul(inc(i), fac(u)) / e();
|
||||
select fun(2.3, 3, 5);
|
||||
fun(2.3, 3, 5)
|
||||
176.58213176229
|
||||
insert into t2 values (append("xxx", "yyy"), mul(4,3), e());
|
||||
insert into t2 values (append("a", "b"), mul(2,mul(3,4)), fun(1.7, 4, 6));
|
||||
select * from t2 where s = append("a", "b");
|
||||
s i d
|
||||
ab 24 1324.36598821719
|
||||
select * from t2 where i = mul(4,3) or i = mul(mul(3,4),2);
|
||||
s i d
|
||||
xxxyyy 12 2.71828182845905
|
||||
ab 24 1324.36598821719
|
||||
select * from t2 where d = e();
|
||||
s i d
|
||||
xxxyyy 12 2.71828182845905
|
||||
select * from t2;
|
||||
s i d
|
||||
xxxyyy 12 2.71828182845905
|
||||
ab 24 1324.36598821719
|
||||
delete from t2;
|
||||
drop function e;
|
||||
drop function inc;
|
||||
drop function mul;
|
||||
drop function append;
|
||||
drop function fac;
|
||||
drop function fun;
|
||||
drop table t1;
|
||||
drop table t2;
|
||||
|
|
|
@ -7,12 +7,18 @@ use test;
|
|||
|
||||
--disable_warnings
|
||||
drop table if exists t1;
|
||||
drop table if exists t2;
|
||||
--enable_warnings
|
||||
|
||||
create table t1 (
|
||||
id char(16) not null,
|
||||
data int not null
|
||||
);
|
||||
create table t2 (
|
||||
s char(16) not null,
|
||||
i int not null,
|
||||
d double not null
|
||||
);
|
||||
|
||||
|
||||
# Single statement, no params.
|
||||
|
@ -481,11 +487,32 @@ end|
|
|||
|
||||
select fac(1), fac(2), fac(5), fac(10)|
|
||||
|
||||
# Nested calls
|
||||
create function fun(d double, i int, u int unsigned) returns double
|
||||
return mul(inc(i), fac(u)) / e()|
|
||||
|
||||
select fun(2.3, 3, 5)|
|
||||
|
||||
|
||||
# Various function calls in differen statements
|
||||
|
||||
insert into t2 values (append("xxx", "yyy"), mul(4,3), e())|
|
||||
insert into t2 values (append("a", "b"), mul(2,mul(3,4)), fun(1.7, 4, 6))|
|
||||
|
||||
# These don't work yet.
|
||||
select * from t2 where s = append("a", "b")|
|
||||
select * from t2 where i = mul(4,3) or i = mul(mul(3,4),2)|
|
||||
select * from t2 where d = e()|
|
||||
select * from t2|
|
||||
delete from t2|
|
||||
|
||||
drop function e|
|
||||
drop function inc|
|
||||
drop function mul|
|
||||
drop function append|
|
||||
drop function fac|
|
||||
drop function fun|
|
||||
|
||||
delimiter ;|
|
||||
drop table t1;
|
||||
drop table t2;
|
||||
|
|
171
sql/sp.cc
171
sql/sp.cc
|
@ -19,20 +19,23 @@
|
|||
#include "sp.h"
|
||||
#include "sp_head.h"
|
||||
|
||||
static sp_head *
|
||||
sp_find_cached_function(THD *thd, char *name, uint namelen);
|
||||
|
||||
/*
|
||||
*
|
||||
* DB storage of Stored PROCEDUREs and FUNCTIONs
|
||||
*
|
||||
*/
|
||||
|
||||
// *openeed=true means we opened ourselves
|
||||
static int
|
||||
db_find_routine_aux(THD *thd, int type, char *name, uint namelen,
|
||||
enum thr_lock_type ltype, TABLE **tablep)
|
||||
enum thr_lock_type ltype, TABLE **tablep, bool *opened)
|
||||
{
|
||||
DBUG_ENTER("db_find_routine_aux");
|
||||
DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
|
||||
TABLE *table;
|
||||
TABLE_LIST tables;
|
||||
byte key[65]; // We know name is 64 and the enum is 1 byte
|
||||
uint keylen;
|
||||
int ret;
|
||||
|
@ -46,13 +49,25 @@ db_find_routine_aux(THD *thd, int type, char *name, uint namelen,
|
|||
key[sizeof(key)-1]= type;
|
||||
keylen= sizeof(key);
|
||||
|
||||
memset(&tables, 0, sizeof(tables));
|
||||
tables.db= (char*)"mysql";
|
||||
tables.real_name= tables.alias= (char*)"proc";
|
||||
if (! (table= open_ltable(thd, &tables, ltype)))
|
||||
for (table= thd->open_tables ; table ; table= table->next)
|
||||
if (strcmp(table->table_cache_key, "mysql") == 0 &&
|
||||
strcmp(table->real_name, "proc") == 0)
|
||||
break;
|
||||
if (table)
|
||||
*opened= FALSE;
|
||||
else
|
||||
{
|
||||
*tablep= NULL;
|
||||
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
|
||||
TABLE_LIST tables;
|
||||
|
||||
memset(&tables, 0, sizeof(tables));
|
||||
tables.db= (char*)"mysql";
|
||||
tables.real_name= tables.alias= (char*)"proc";
|
||||
if (! (table= open_ltable(thd, &tables, ltype)))
|
||||
{
|
||||
*tablep= NULL;
|
||||
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
|
||||
}
|
||||
*opened= TRUE;
|
||||
}
|
||||
|
||||
if (table->file->index_read_idx(table->record[0], 0,
|
||||
|
@ -77,9 +92,10 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
|
|||
TABLE *table;
|
||||
const char *defstr;
|
||||
int ret;
|
||||
bool opened;
|
||||
|
||||
// QQ Set up our own mem_root here???
|
||||
ret= db_find_routine_aux(thd, type, name, namelen, TL_READ, &table);
|
||||
ret= db_find_routine_aux(thd, type, name, namelen, TL_READ, &table, &opened);
|
||||
if (ret != SP_OK)
|
||||
goto done;
|
||||
if ((defstr= get_field(&thd->mem_root, table->field[2])) == NULL)
|
||||
|
@ -87,8 +103,11 @@ 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;
|
||||
if (opened)
|
||||
{
|
||||
close_thread_tables(thd);
|
||||
table= NULL;
|
||||
}
|
||||
|
||||
tmplex= lex_start(thd, (uchar*)defstr, strlen(defstr));
|
||||
if (yyparse(thd) || thd->is_fatal_error || tmplex->sphead == NULL)
|
||||
|
@ -97,7 +116,7 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
|
|||
*sphp= tmplex->sphead;
|
||||
|
||||
done:
|
||||
if (table)
|
||||
if (table && opened)
|
||||
close_thread_tables(thd);
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
@ -122,9 +141,9 @@ db_create_routine(THD *thd, int type,
|
|||
{
|
||||
restore_record(table, 2); // Get default values for fields
|
||||
|
||||
table->field[0]->store(name, namelen, default_charset_info);
|
||||
table->field[0]->store(name, namelen, system_charset_info);
|
||||
table->field[1]->store((longlong)type);
|
||||
table->field[2]->store(def, deflen, default_charset_info);
|
||||
table->field[2]->store(def, deflen, system_charset_info);
|
||||
|
||||
if (table->file->write_row(table->record[0]))
|
||||
ret= SP_WRITE_ROW_FAILED;
|
||||
|
@ -143,15 +162,17 @@ db_drop_routine(THD *thd, int type, char *name, uint namelen)
|
|||
DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
|
||||
TABLE *table;
|
||||
int ret;
|
||||
bool opened;
|
||||
|
||||
ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table);
|
||||
ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table, &opened);
|
||||
if (ret == SP_OK)
|
||||
{
|
||||
if (table->file->delete_row(table->record[0]))
|
||||
ret= SP_DELETE_ROW_FAILED;
|
||||
}
|
||||
|
||||
close_thread_tables(thd);
|
||||
if (opened)
|
||||
close_thread_tables(thd);
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
|
@ -216,10 +237,13 @@ sp_find_function(THD *thd, LEX_STRING *name)
|
|||
|
||||
DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
|
||||
|
||||
if (db_find_routine(thd, TYPE_ENUM_FUNCTION,
|
||||
name->str, name->length, &sp) != SP_OK)
|
||||
sp= NULL;
|
||||
|
||||
sp= sp_find_cached_function(thd, name->str, name->length);
|
||||
if (! sp)
|
||||
{
|
||||
if (db_find_routine(thd, TYPE_ENUM_FUNCTION,
|
||||
name->str, name->length, &sp) != SP_OK)
|
||||
sp= NULL;
|
||||
}
|
||||
DBUG_RETURN(sp);
|
||||
}
|
||||
|
||||
|
@ -253,12 +277,115 @@ sp_function_exists(THD *thd, LEX_STRING *name)
|
|||
{
|
||||
TABLE *table;
|
||||
bool ret= FALSE;
|
||||
bool opened;
|
||||
|
||||
if (db_find_routine_aux(thd, TYPE_ENUM_FUNCTION,
|
||||
name->str, name->length, TL_READ, &table) == SP_OK)
|
||||
name->str, name->length, TL_READ,
|
||||
&table, &opened) == SP_OK)
|
||||
{
|
||||
ret= TRUE;
|
||||
}
|
||||
close_thread_tables(thd);
|
||||
if (opened)
|
||||
close_thread_tables(thd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* The temporary FUNCTION cache. (QQ This will be rehacked later, but
|
||||
* it's needed now to make functions work at all.)
|
||||
*
|
||||
*/
|
||||
|
||||
void
|
||||
sp_add_fun_to_lex(LEX *lex, LEX_STRING fun)
|
||||
{
|
||||
List_iterator_fast<char> li(lex->spfuns);
|
||||
char *fn;
|
||||
|
||||
while ((fn= li++))
|
||||
{
|
||||
if (strncasecmp(fn, fun.str, fun.length) == 0)
|
||||
break;
|
||||
}
|
||||
if (! fn)
|
||||
{
|
||||
char *s= sql_strmake(fun.str, fun.length);
|
||||
lex->spfuns.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sp_merge_funs(LEX *dst, LEX *src)
|
||||
{
|
||||
List_iterator_fast<char> li(src->spfuns);
|
||||
char *fn;
|
||||
|
||||
while ((fn= li++))
|
||||
{
|
||||
LEX_STRING lx;
|
||||
|
||||
lx.str= fn; lx.length= strlen(fn);
|
||||
sp_add_fun_to_lex(dst, lx);
|
||||
}
|
||||
}
|
||||
|
||||
/* QQ Not terribly efficient right now, but it'll do for starters.
|
||||
We should actually open the mysql.proc table just once. */
|
||||
int
|
||||
sp_cache_functions(THD *thd, LEX *lex)
|
||||
{
|
||||
List_iterator<char> li(lex->spfuns);
|
||||
char *fn;
|
||||
enum_sql_command cmd= lex->sql_command;
|
||||
int ret= 0;
|
||||
|
||||
while ((fn= li++))
|
||||
{
|
||||
List_iterator_fast<sp_head> lisp(thd->spfuns);
|
||||
sp_head *sp;
|
||||
|
||||
while ((sp= lisp++))
|
||||
{
|
||||
if (strcasecmp(fn, sp->name()) == 0)
|
||||
break;
|
||||
}
|
||||
if (sp)
|
||||
continue;
|
||||
if (db_find_routine(thd, TYPE_ENUM_FUNCTION, fn, strlen(fn), &sp) == SP_OK)
|
||||
{
|
||||
ret= sp_cache_functions(thd, &thd->lex);
|
||||
if (ret)
|
||||
break;
|
||||
thd->spfuns.push_back(sp);
|
||||
}
|
||||
else
|
||||
{
|
||||
send_error(thd, ER_SP_DOES_NOT_EXIST);
|
||||
ret= 1;
|
||||
}
|
||||
}
|
||||
lex->sql_command= cmd;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
sp_clear_function_cache(THD *thd)
|
||||
{
|
||||
thd->spfuns.empty();
|
||||
}
|
||||
|
||||
static sp_head *
|
||||
sp_find_cached_function(THD *thd, char *name, uint namelen)
|
||||
{
|
||||
List_iterator_fast<sp_head> li(thd->spfuns);
|
||||
sp_head *sp;
|
||||
|
||||
while ((sp= li++))
|
||||
{
|
||||
if (strncasecmp(name, sp->name(), namelen) == 0)
|
||||
break;
|
||||
}
|
||||
return sp;
|
||||
}
|
||||
|
|
12
sql/sp.h
12
sql/sp.h
|
@ -50,4 +50,16 @@ sp_drop_function(THD *thd, char *name, uint namelen);
|
|||
bool
|
||||
sp_function_exists(THD *thd, LEX_STRING *name);
|
||||
|
||||
|
||||
// QQ More temporary stuff until the real cache is implemented. This is
|
||||
// needed since we have to read the functions before we do anything else.
|
||||
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);
|
||||
|
||||
#endif /* _SP_H_ */
|
||||
|
|
|
@ -88,10 +88,10 @@ sp_head::sp_head(LEX_STRING *name, LEX *lex)
|
|||
{
|
||||
const char *dstr = (const char*)lex->buf;
|
||||
|
||||
m_call_lex= lex;
|
||||
m_name= new Item_string(name->str, name->length, default_charset_info);
|
||||
m_name= new Item_string(name->str, name->length, system_charset_info);
|
||||
m_defstr= new Item_string(dstr, lex->end_of_query - lex->buf,
|
||||
default_charset_info);
|
||||
system_charset_info);
|
||||
m_pcont= lex->spcont;
|
||||
my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
|
||||
m_backpatch.empty();
|
||||
}
|
||||
|
@ -143,11 +143,10 @@ sp_head::execute(THD *thd)
|
|||
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();
|
||||
DBUG_ENTER("sp_head::execute_function");
|
||||
DBUG_PRINT("info", ("function %s", ((String *)m_name->const_string())->c_ptr()));
|
||||
uint csize = m_pcont->max_framesize();
|
||||
uint params = m_pcont->params();
|
||||
sp_rcontext *octx = thd->spcont;
|
||||
sp_rcontext *nctx = NULL;
|
||||
uint i;
|
||||
|
@ -157,7 +156,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
|
|||
nctx= new sp_rcontext(csize);
|
||||
for (i= 0 ; i < params && i < argcount ; i++)
|
||||
{
|
||||
sp_pvar_t *pvar = pctx->find_pvar(i);
|
||||
sp_pvar_t *pvar = m_pcont->find_pvar(i);
|
||||
|
||||
nctx->push_item(eval_func_item(thd, *argp++, pvar->type));
|
||||
}
|
||||
|
@ -178,13 +177,12 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
|
|||
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()));
|
||||
DBUG_ENTER("sp_head::execute_procedure");
|
||||
DBUG_PRINT("info", ("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();
|
||||
uint params = pctx->params();
|
||||
uint csize = m_pcont->max_framesize();
|
||||
uint params = m_pcont->params();
|
||||
sp_rcontext *octx = thd->spcont;
|
||||
sp_rcontext *nctx = NULL;
|
||||
my_bool tmp_octx = FALSE; // True if we have allocated a temporary octx
|
||||
|
@ -204,7 +202,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||
// QQ: No error checking whatsoever right now. Should do type checking?
|
||||
for (i = 0 ; (it= li++) && i < params ; i++)
|
||||
{
|
||||
sp_pvar_t *pvar = pctx->find_pvar(i);
|
||||
sp_pvar_t *pvar = m_pcont->find_pvar(i);
|
||||
|
||||
if (! pvar)
|
||||
nctx->set_oindex(i, -1); // Shouldn't happen
|
||||
|
@ -236,7 +234,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||
// Don't copy back OUT values if we got an error
|
||||
if (ret == 0 && csize > 0)
|
||||
{
|
||||
List_iterator_fast<Item> li(m_call_lex->value_list);
|
||||
List_iterator_fast<Item> li(*args);
|
||||
Item *it;
|
||||
|
||||
// Copy back all OUT or INOUT values to the previous frame, or
|
||||
|
@ -324,6 +322,9 @@ sp_head::restore_lex(THD *thd)
|
|||
m_lex.next_state= thd->lex.next_state;
|
||||
|
||||
// Collect some data from the sub statement lex.
|
||||
sp_merge_funs(&m_lex, &thd->lex);
|
||||
#if 0
|
||||
// We're not using this at the moment.
|
||||
if (thd->lex.sql_command == SQLCOM_CALL)
|
||||
{
|
||||
// It would be slightly faster to keep the list sorted, but we need
|
||||
|
@ -362,6 +363,7 @@ sp_head::restore_lex(THD *thd)
|
|||
m_tables.push_back(&tables->real_name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
memcpy(&thd->lex, &m_lex, sizeof(LEX)); // Restore lex
|
||||
}
|
||||
|
|
|
@ -46,8 +46,11 @@ public:
|
|||
int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE
|
||||
enum enum_field_types m_returns; // For FUNCTIONs only
|
||||
my_bool m_simple_case; // TRUE if parsing simple case, FALSE otherwise
|
||||
#if 0
|
||||
// We're not using this at the moment.
|
||||
List<char *> m_calls; // Called procedures.
|
||||
List<char *> m_tables; // Used tables.
|
||||
#endif
|
||||
|
||||
static void *operator new(size_t size)
|
||||
{
|
||||
|
@ -59,7 +62,7 @@ public:
|
|||
/* Empty */
|
||||
}
|
||||
|
||||
sp_head(LEX_STRING *name, LEX* lex);
|
||||
sp_head(LEX_STRING *name, LEX *lex);
|
||||
|
||||
int
|
||||
create(THD *thd);
|
||||
|
@ -118,7 +121,7 @@ private:
|
|||
|
||||
Item_string *m_name;
|
||||
Item_string *m_defstr;
|
||||
LEX *m_call_lex; // The CALL's own lex
|
||||
sp_pcontext *m_pcont; // Parse context
|
||||
LEX m_lex; // Temp. store for the other lex
|
||||
DYNAMIC_ARRAY m_instr; // The "instructions"
|
||||
typedef struct
|
||||
|
|
|
@ -553,6 +553,7 @@ public:
|
|||
bool prepare_command;
|
||||
bool tmp_table_used;
|
||||
sp_rcontext *spcont; // SP runtime context
|
||||
List<sp_head> spfuns; // SP FUNCTIONs
|
||||
|
||||
/*
|
||||
If we do a purge of binary logs, log index info of the threads
|
||||
|
|
|
@ -176,6 +176,7 @@ LEX *lex_start(THD *thd, uchar *buf,uint length)
|
|||
lex->sql_command=SQLCOM_END;
|
||||
lex->sphead= NULL;
|
||||
lex->spcont= NULL;
|
||||
lex->spfuns.empty();
|
||||
return lex;
|
||||
}
|
||||
|
||||
|
|
|
@ -485,6 +485,7 @@ typedef struct st_lex
|
|||
char *help_arg;
|
||||
sp_head *sphead;
|
||||
sp_pcontext *spcont;
|
||||
List<char> spfuns; /* Called functions */
|
||||
|
||||
inline void uncacheable()
|
||||
{
|
||||
|
|
|
@ -1583,6 +1583,18 @@ mysql_execute_command(THD *thd)
|
|||
SELECT_LEX_UNIT *unit= &lex->unit;
|
||||
DBUG_ENTER("mysql_execute_command");
|
||||
|
||||
/*
|
||||
Clear the SP function cache before each statement (QQ this is a temporary
|
||||
solution; caching will be rehacked later), and the new ones.
|
||||
*/
|
||||
sp_clear_function_cache(thd);
|
||||
if (lex->sql_command != SQLCOM_CREATE_PROCEDURE &&
|
||||
lex->sql_command != SQLCOM_CREATE_FUNCTION)
|
||||
{
|
||||
if (sp_cache_functions(thd, lex))
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
Reset warning count for each query that uses tables
|
||||
A better approach would be to reset this for any commands
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "item_create.h"
|
||||
#include "sp_head.h"
|
||||
#include "sp_pcontext.h"
|
||||
#include "sp.h"
|
||||
#include <myisam.h>
|
||||
#include <myisammrg.h>
|
||||
|
||||
|
@ -965,7 +966,7 @@ create_function_tail:
|
|||
;
|
||||
|
||||
call:
|
||||
CALL_SYM ident
|
||||
CALL_SYM ident_or_spfunc
|
||||
{
|
||||
LEX *lex = Lex;
|
||||
|
||||
|
@ -2875,6 +2876,7 @@ simple_expr:
|
|||
{ $$= new Item_int((char*) "TRUE",1,1); }
|
||||
| SP_FUNC '(' udf_expr_list ')'
|
||||
{
|
||||
sp_add_fun_to_lex(Lex, $1);
|
||||
if ($3)
|
||||
$$= new Item_func_sp($1, *$3);
|
||||
else
|
||||
|
|
Loading…
Reference in a new issue