summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <pem@mysql.com>2003-03-02 19:17:41 +0100
committerunknown <pem@mysql.com>2003-03-02 19:17:41 +0100
commit8a9422bd2af6ea39676171b9ec16897c64104dc8 (patch)
treef607e9e9c2756612d8e622c239e1781b56f51e63
parent1ff79b61a056e5b50fc0402680f6f74ca1eb2a57 (diff)
downloadmariadb-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.result30
-rw-r--r--mysql-test/t/sp.test27
-rw-r--r--sql/sp.cc171
-rw-r--r--sql/sp.h12
-rw-r--r--sql/sp_head.cc34
-rw-r--r--sql/sp_head.h7
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/sql_lex.cc1
-rw-r--r--sql/sql_lex.h1
-rw-r--r--sql/sql_parse.cc12
-rw-r--r--sql/sql_yacc.yy4
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;
+}
diff --git a/sql/sp.h b/sql/sp.h
index b9ade8fb3c1..21fcb4c5360 100644
--- a/sql/sp.h
+++ b/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_ */
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