diff options
author | unknown <pem@mysql.com> | 2003-03-02 19:17:41 +0100 |
---|---|---|
committer | unknown <pem@mysql.com> | 2003-03-02 19:17:41 +0100 |
commit | 8a9422bd2af6ea39676171b9ec16897c64104dc8 (patch) | |
tree | f607e9e9c2756612d8e622c239e1781b56f51e63 | |
parent | 1ff79b61a056e5b50fc0402680f6f74ca1eb2a57 (diff) | |
download | mariadb-git-8a9422bd2af6ea39676171b9ec16897c64104dc8.tar.gz |
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.
-rw-r--r-- | mysql-test/r/sp.result | 30 | ||||
-rw-r--r-- | mysql-test/t/sp.test | 27 | ||||
-rw-r--r-- | sql/sp.cc | 171 | ||||
-rw-r--r-- | sql/sp.h | 12 | ||||
-rw-r--r-- | sql/sp_head.cc | 34 | ||||
-rw-r--r-- | sql/sp_head.h | 7 | ||||
-rw-r--r-- | sql/sql_class.h | 1 | ||||
-rw-r--r-- | sql/sql_lex.cc | 1 | ||||
-rw-r--r-- | sql/sql_lex.h | 1 | ||||
-rw-r--r-- | sql/sql_parse.cc | 12 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 4 |
11 files changed, 259 insertions, 41 deletions
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index ab445e44a5a..13983fbd69e 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -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; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 17b21f612d6..a5efee28446 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -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; diff --git a/sql/sp.cc b/sql/sp.cc index 52d59eb1a3e..2bb714ab33a 100644 --- a/sql/sp.cc +++ b/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; +} @@ -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_ */ diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 9de3ee471f2..55d21c37609 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -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 } diff --git a/sql/sp_head.h b/sql/sp_head.h index fa2a76753e7..ee64a1bd0cf 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -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 diff --git a/sql/sql_class.h b/sql/sql_class.h index f316272ff0e..7740a54d007 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -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 diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index a900d87949e..9f32b45c1ad 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -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; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 9a4b4b8bd08..337b69026bf 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -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() { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 67a3865057f..94278371fd0 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1584,6 +1584,18 @@ mysql_execute_command(THD *thd) 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 that is not a SHOW command or a select that only access local diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index fc91ee9a6e4..7c2b97904fb 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -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 |