Commit 8049daf2 authored by pem@mysql.com's avatar pem@mysql.com

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.
parent b6ab762d
use test; use test;
drop table if exists t1; drop table if exists t1;
drop table if exists t2;
create table t1 ( create table t1 (
id char(16) not null, id char(16) not null,
data int 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() create procedure foo42()
insert into test.t1 values ("foo", 42); insert into test.t1 values ("foo", 42);
call foo42(); call foo42();
...@@ -409,9 +415,33 @@ end; ...@@ -409,9 +415,33 @@ end;
select fac(1), fac(2), fac(5), fac(10); select fac(1), fac(2), fac(5), fac(10);
fac(1) fac(2) fac(5) fac(10) fac(1) fac(2) fac(5) fac(10)
1 2 120 3628800 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 e;
drop function inc; drop function inc;
drop function mul; drop function mul;
drop function append; drop function append;
drop function fac; drop function fac;
drop function fun;
drop table t1; drop table t1;
drop table t2;
...@@ -7,12 +7,18 @@ use test; ...@@ -7,12 +7,18 @@ use test;
--disable_warnings --disable_warnings
drop table if exists t1; drop table if exists t1;
drop table if exists t2;
--enable_warnings --enable_warnings
create table t1 ( create table t1 (
id char(16) not null, id char(16) not null,
data int 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. # Single statement, no params.
...@@ -481,11 +487,32 @@ end| ...@@ -481,11 +487,32 @@ end|
select fac(1), fac(2), fac(5), fac(10)| 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 e|
drop function inc| drop function inc|
drop function mul| drop function mul|
drop function append| drop function append|
drop function fac| drop function fac|
drop function fun|
delimiter ;| delimiter ;|
drop table t1; drop table t1;
drop table t2;
...@@ -19,20 +19,23 @@ ...@@ -19,20 +19,23 @@
#include "sp.h" #include "sp.h"
#include "sp_head.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 * DB storage of Stored PROCEDUREs and FUNCTIONs
* *
*/ */
// *openeed=true means we opened ourselves
static int static int
db_find_routine_aux(THD *thd, int type, char *name, uint namelen, 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_ENTER("db_find_routine_aux");
DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name)); DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
TABLE *table; TABLE *table;
TABLE_LIST tables;
byte key[65]; // We know name is 64 and the enum is 1 byte byte key[65]; // We know name is 64 and the enum is 1 byte
uint keylen; uint keylen;
int ret; int ret;
...@@ -46,13 +49,25 @@ db_find_routine_aux(THD *thd, int type, char *name, uint namelen, ...@@ -46,13 +49,25 @@ db_find_routine_aux(THD *thd, int type, char *name, uint namelen,
key[sizeof(key)-1]= type; key[sizeof(key)-1]= type;
keylen= sizeof(key); keylen= sizeof(key);
memset(&tables, 0, sizeof(tables)); for (table= thd->open_tables ; table ; table= table->next)
tables.db= (char*)"mysql"; if (strcmp(table->table_cache_key, "mysql") == 0 &&
tables.real_name= tables.alias= (char*)"proc"; strcmp(table->real_name, "proc") == 0)
if (! (table= open_ltable(thd, &tables, ltype))) break;
if (table)
*opened= FALSE;
else
{ {
*tablep= NULL; TABLE_LIST tables;
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
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, 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) ...@@ -77,9 +92,10 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
TABLE *table; TABLE *table;
const char *defstr; const char *defstr;
int ret; int ret;
bool opened;
// QQ Set up our own mem_root here??? // 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) if (ret != SP_OK)
goto done; goto done;
if ((defstr= get_field(&thd->mem_root, table->field[2])) == NULL) 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) ...@@ -87,8 +103,11 @@ 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); if (opened)
table= NULL; {
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)
...@@ -97,7 +116,7 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) ...@@ -97,7 +116,7 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
*sphp= tmplex->sphead; *sphp= tmplex->sphead;
done: done:
if (table) if (table && opened)
close_thread_tables(thd); close_thread_tables(thd);
DBUG_RETURN(ret); DBUG_RETURN(ret);
} }
...@@ -122,9 +141,9 @@ db_create_routine(THD *thd, int type, ...@@ -122,9 +141,9 @@ db_create_routine(THD *thd, int type,
{ {
restore_record(table, 2); // Get default values for fields 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[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])) if (table->file->write_row(table->record[0]))
ret= SP_WRITE_ROW_FAILED; ret= SP_WRITE_ROW_FAILED;
...@@ -143,15 +162,17 @@ db_drop_routine(THD *thd, int type, char *name, uint namelen) ...@@ -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)); DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
TABLE *table; TABLE *table;
int ret; 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 (ret == SP_OK)
{ {
if (table->file->delete_row(table->record[0])) if (table->file->delete_row(table->record[0]))
ret= SP_DELETE_ROW_FAILED; ret= SP_DELETE_ROW_FAILED;
} }
close_thread_tables(thd); if (opened)
close_thread_tables(thd);
DBUG_RETURN(ret); DBUG_RETURN(ret);
} }
...@@ -216,10 +237,13 @@ sp_find_function(THD *thd, LEX_STRING *name) ...@@ -216,10 +237,13 @@ sp_find_function(THD *thd, LEX_STRING *name)
DBUG_PRINT("enter", ("name: %*s", name->length, name->str)); DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
if (db_find_routine(thd, TYPE_ENUM_FUNCTION, sp= sp_find_cached_function(thd, name->str, name->length);
name->str, name->length, &sp) != SP_OK) if (! sp)
sp= NULL; {
if (db_find_routine(thd, TYPE_ENUM_FUNCTION,
name->str, name->length, &sp) != SP_OK)
sp= NULL;
}
DBUG_RETURN(sp); DBUG_RETURN(sp);
} }
...@@ -253,12 +277,115 @@ sp_function_exists(THD *thd, LEX_STRING *name) ...@@ -253,12 +277,115 @@ sp_function_exists(THD *thd, LEX_STRING *name)
{ {
TABLE *table; TABLE *table;
bool ret= FALSE; bool ret= FALSE;
bool opened;
if (db_find_routine_aux(thd, TYPE_ENUM_FUNCTION, 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; 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; 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;
}
...@@ -50,4 +50,16 @@ sp_drop_function(THD *thd, char *name, uint namelen); ...@@ -50,4 +50,16 @@ sp_drop_function(THD *thd, char *name, uint namelen);
bool bool
sp_function_exists(THD *thd, LEX_STRING *name); 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_ */ #endif /* _SP_H_ */
...@@ -88,10 +88,10 @@ sp_head::sp_head(LEX_STRING *name, LEX *lex) ...@@ -88,10 +88,10 @@ sp_head::sp_head(LEX_STRING *name, LEX *lex)
{ {
const char *dstr = (const char*)lex->buf; const char *dstr = (const char*)lex->buf;
m_call_lex= lex; m_name= new Item_string(name->str, name->length, system_charset_info);
m_name= new Item_string(name->str, name->length, default_charset_info);
m_defstr= new Item_string(dstr, lex->end_of_query - lex->buf, 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); my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
m_backpatch.empty(); m_backpatch.empty();
} }
...@@ -143,11 +143,10 @@ sp_head::execute(THD *thd) ...@@ -143,11 +143,10 @@ sp_head::execute(THD *thd)
int int
sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
{ {
DBUG_ENTER("sp_head::execute"); DBUG_ENTER("sp_head::execute_function");
DBUG_PRINT("executing", ("procedure %s", ((String *)m_name->const_string())->c_ptr())); DBUG_PRINT("info", ("function %s", ((String *)m_name->const_string())->c_ptr()));
sp_pcontext *pctx = m_call_lex->spcont; uint csize = m_pcont->max_framesize();
uint csize = pctx->max_framesize(); uint params = m_pcont->params();
uint params = pctx->params();
sp_rcontext *octx = thd->spcont; sp_rcontext *octx = thd->spcont;
sp_rcontext *nctx = NULL; sp_rcontext *nctx = NULL;
uint i; uint i;
...@@ -157,7 +156,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) ...@@ -157,7 +156,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
nctx= new sp_rcontext(csize); nctx= new sp_rcontext(csize);
for (i= 0 ; i < params && i < argcount ; i++) 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)); 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) ...@@ -178,13 +177,12 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
int int
sp_head::execute_procedure(THD *thd, List<Item> *args) sp_head::execute_procedure(THD *thd, List<Item> *args)
{ {
DBUG_ENTER("sp_head::execute"); DBUG_ENTER("sp_head::execute_procedure");
DBUG_PRINT("executing", ("procedure %s", ((String *)m_name->const_string())->c_ptr())); DBUG_PRINT("info", ("procedure %s", ((String *)m_name->const_string())->c_ptr()));
int ret; int ret;
sp_instr *p; sp_instr *p;
sp_pcontext *pctx = m_call_lex->spcont; uint csize = m_pcont->max_framesize();
uint csize = pctx->max_framesize(); uint params = m_pcont->params();
uint params = pctx->params();
sp_rcontext *octx = thd->spcont; sp_rcontext *octx = thd->spcont;
sp_rcontext *nctx = NULL; sp_rcontext *nctx = NULL;
my_bool tmp_octx = FALSE; // True if we have allocated a temporary octx 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) ...@@ -204,7 +202,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
// QQ: No error checking whatsoever right now. Should do type checking? // QQ: No error checking whatsoever right now. Should do type checking?
for (i = 0 ; (it= li++) && i < params ; i++) 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) if (! pvar)
nctx->set_oindex(i, -1); // Shouldn't happen nctx->set_oindex(i, -1); // Shouldn't happen
...@@ -236,7 +234,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) ...@@ -236,7 +234,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
// 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)
{ {
List_iterator_fast<Item> li(m_call_lex->value_list); List_iterator_fast<Item> li(*args);
Item *it; Item *it;
// Copy back all OUT or INOUT values to the previous frame, or // Copy back all OUT or INOUT values to the previous frame, or
...@@ -324,6 +322,9 @@ sp_head::restore_lex(THD *thd) ...@@ -324,6 +322,9 @@ sp_head::restore_lex(THD *thd)
m_lex.next_state= thd->lex.next_state; m_lex.next_state= thd->lex.next_state;
// Collect some data from the sub statement lex. // 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) if (thd->lex.sql_command == SQLCOM_CALL)
{ {
// It would be slightly faster to keep the list sorted, but we need // It would be slightly faster to keep the list sorted, but we need
...@@ -362,6 +363,7 @@ sp_head::restore_lex(THD *thd) ...@@ -362,6 +363,7 @@ sp_head::restore_lex(THD *thd)
m_tables.push_back(&tables->real_name); m_tables.push_back(&tables->real_name);
} }
} }
#endif
memcpy(&thd->lex, &m_lex, sizeof(LEX)); // Restore lex memcpy(&thd->lex, &m_lex, sizeof(LEX)); // Restore lex
} }
......
...@@ -46,8 +46,11 @@ public: ...@@ -46,8 +46,11 @@ public:
int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE
enum enum_field_types m_returns; // For FUNCTIONs only enum enum_field_types m_returns; // For FUNCTIONs only
my_bool m_simple_case; // TRUE if parsing simple case, FALSE otherwise 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_calls; // Called procedures.
List<char *> m_tables; // Used tables. List<char *> m_tables; // Used tables.
#endif
static void *operator new(size_t size) static void *operator new(size_t size)
{ {
...@@ -59,7 +62,7 @@ public: ...@@ -59,7 +62,7 @@ public:
/* Empty */ /* Empty */
} }
sp_head(LEX_STRING *name, LEX* lex); sp_head(LEX_STRING *name, LEX *lex);
int int
create(THD *thd); create(THD *thd);
...@@ -118,7 +121,7 @@ private: ...@@ -118,7 +121,7 @@ private:
Item_string *m_name; Item_string *m_name;
Item_string *m_defstr; 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 LEX m_lex; // Temp. store for the other lex
DYNAMIC_ARRAY m_instr; // The "instructions" DYNAMIC_ARRAY m_instr; // The "instructions"
typedef struct typedef struct
......
...@@ -553,6 +553,7 @@ public: ...@@ -553,6 +553,7 @@ public:
bool prepare_command; bool prepare_command;
bool tmp_table_used; bool tmp_table_used;
sp_rcontext *spcont; // SP runtime context 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 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) ...@@ -176,6 +176,7 @@ LEX *lex_start(THD *thd, uchar *buf,uint length)
lex->sql_command=SQLCOM_END; lex->sql_command=SQLCOM_END;
lex->sphead= NULL; lex->sphead= NULL;
lex->spcont= NULL; lex->spcont= NULL;
lex->spfuns.empty();
return lex; return lex;
} }
......
...@@ -485,6 +485,7 @@ typedef struct st_lex ...@@ -485,6 +485,7 @@ typedef struct st_lex
char *help_arg; char *help_arg;
sp_head *sphead; sp_head *sphead;
sp_pcontext *spcont; sp_pcontext *spcont;
List<char> spfuns; /* Called functions */
inline void uncacheable() inline void uncacheable()
{ {
......
...@@ -1583,6 +1583,18 @@ mysql_execute_command(THD *thd) ...@@ -1583,6 +1583,18 @@ mysql_execute_command(THD *thd)
SELECT_LEX_UNIT *unit= &lex->unit; SELECT_LEX_UNIT *unit= &lex->unit;
DBUG_ENTER("mysql_execute_command"); 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 Reset warning count for each query that uses tables
A better approach would be to reset this for any commands A better approach would be to reset this for any commands
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "item_create.h" #include "item_create.h"
#include "sp_head.h" #include "sp_head.h"
#include "sp_pcontext.h" #include "sp_pcontext.h"
#include "sp.h"
#include <myisam.h> #include <myisam.h>
#include <myisammrg.h> #include <myisammrg.h>
...@@ -965,7 +966,7 @@ create_function_tail: ...@@ -965,7 +966,7 @@ create_function_tail:
; ;
call: call:
CALL_SYM ident CALL_SYM ident_or_spfunc
{ {
LEX *lex = Lex; LEX *lex = Lex;
...@@ -2875,6 +2876,7 @@ simple_expr: ...@@ -2875,6 +2876,7 @@ simple_expr:
{ $$= new Item_int((char*) "TRUE",1,1); } { $$= new Item_int((char*) "TRUE",1,1); }
| SP_FUNC '(' udf_expr_list ')' | SP_FUNC '(' udf_expr_list ')'
{ {
sp_add_fun_to_lex(Lex, $1);
if ($3) if ($3)
$$= new Item_func_sp($1, *$3); $$= new Item_func_sp($1, *$3);
else else
......
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