summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <pem@mysql.com>2003-02-26 19:22:29 +0100
committerunknown <pem@mysql.com>2003-02-26 19:22:29 +0100
commit76b037dc4288fada1b64efbef422cb8b4bd0d5b5 (patch)
tree21d88c3a00a56c11d4f65d0c77e082899658ec01 /sql
parent0521fb5444df9a97e0682307242700bd94c6595e (diff)
downloadmariadb-git-76b037dc4288fada1b64efbef422cb8b4bd0d5b5.tar.gz
Made stored FUNCTION invokation work almost always. Still buggy and unstable, and
various known problems, but good enough for a checkpoint commit. mysql-test/r/sp.result: New tests for invoking simple FUNCTIONs. mysql-test/t/sp.test: New tests for invoking simple FUNCTIONs. sql/item_func.cc: New Item_func_sp for stored FUNCTIONs. sql/item_func.h: New Item_func_sp for stored FUNCTIONs. sql/sp.cc: Close mysql.proc table earlier so recursive find_function calls work. Added temporary sp_function_exists() function for checking without parsing. sql/sp.h: Added temporary sp_function_exists() function for checking without parsing. sql/sp_head.cc: New code for executing a FUNCTION. (And reworked some of the old code in the process.) sql/sp_head.h: New code for executing a FUNCTION. sql/sp_rcontext.h: Added result slot for FUNCTIONs. sql/sql_lex.cc: Added check for stored FUNCTION, analogous to UDFs. sql/sql_parse.cc: sp_head::execute was renamed into execute_procedure. sql/sql_yacc.yy: Added parsing of stored FUNCTION invocation and code generation for RETURN statement.
Diffstat (limited to 'sql')
-rw-r--r--sql/item_func.cc75
-rw-r--r--sql/item_func.h66
-rw-r--r--sql/sp.cc21
-rw-r--r--sql/sp.h4
-rw-r--r--sql/sp_head.cc166
-rw-r--r--sql/sp_head.h43
-rw-r--r--sql/sp_rcontext.h15
-rw-r--r--sql/sql_lex.cc13
-rw-r--r--sql/sql_parse.cc4
-rw-r--r--sql/sql_yacc.yy23
10 files changed, 366 insertions, 64 deletions
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 45d666fb47b..945a6e25e4f 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -28,6 +28,9 @@
#include <time.h>
#include <ft_global.h>
#include <zlib.h>
+#include "sp_head.h"
+#include "sp_rcontext.h"
+#include "sp.h"
/* return TRUE if item is a constant */
@@ -2770,3 +2773,75 @@ double Item_func_glength::val()
geom.length(&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;
+}
diff --git a/sql/item_func.h b/sql/item_func.h
index 68804b83d26..07201093473 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -1147,3 +1147,69 @@ enum Item_cast
ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT,
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();
+
+};
diff --git a/sql/sp.cc b/sql/sp.cc
index eb3a9871ab5..f7b8cf235ca 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -84,6 +84,8 @@ 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;
tmplex= lex_start(thd, (uchar*)defstr, strlen(defstr));
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)
*sphp= tmplex->sphead;
done:
- if (ret == SP_OK && table)
+ if (ret != SP_OK && table)
close_thread_tables(thd);
DBUG_RETURN(ret);
}
@@ -208,7 +210,7 @@ sp_drop_procedure(THD *thd, char *name, uint namelen)
sp_head *
sp_find_function(THD *thd, LEX_STRING *name)
{
- DBUG_ENTER("sp_find_function_i");
+ DBUG_ENTER("sp_find_function");
sp_head *sp;
DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
@@ -243,3 +245,18 @@ sp_drop_function(THD *thd, char *name, uint namelen)
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;
+}
diff --git a/sql/sp.h b/sql/sp.h
index 084afb8c8e0..b9ade8fb3c1 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -46,4 +46,8 @@ sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen);
int
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_ */
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 8682fa2cd9d..64ecdc5b380 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -24,6 +24,26 @@
#include "sp_pcontext.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
** if nothing else.
*/
@@ -36,48 +56,28 @@ eval_func_item(THD *thd, Item *it, enum enum_field_types type)
return it; // Shouldn't happen?
/* 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:
- case MYSQL_TYPE_SHORT:
- case MYSQL_TYPE_LONG:
- case MYSQL_TYPE_LONGLONG:
- case MYSQL_TYPE_INT24:
- it= new Item_int(it->val_int());
- break;
- case MYSQL_TYPE_DECIMAL:
- case MYSQL_TYPE_FLOAT:
- case MYSQL_TYPE_DOUBLE:
- it= new Item_real(it->val());
- break;
- case MYSQL_TYPE_VAR_STRING:
- 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];
- String tmp(buffer, sizeof(buffer), default_charset_info);
- String *s= it->val_str(&tmp);
-
- it= new Item_string(s->c_ptr_quick(), s->length(), default_charset_info);
+ switch (sp_map_result_type(type)) {
+ case INT_RESULT:
+ it= new Item_int(it->val_int());
+ break;
+ case REAL_RESULT:
+ it= new Item_real(it->val());
break;
+ default:
+ {
+ char buffer[MAX_FIELD_WIDTH];
+ String tmp(buffer, sizeof(buffer), default_charset_info);
+ String *s= it->val_str(&tmp);
+
+ it= new Item_string(s->c_ptr_quick(), s->length(),
+ default_charset_info);
+ 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;
@@ -118,12 +118,69 @@ sp_head::create(THD *thd)
DBUG_RETURN(ret);
}
+
int
sp_head::execute(THD *thd)
{
DBUG_ENTER("sp_head::execute");
- DBUG_PRINT("executing", ("procedure %s", ((String *)m_name->const_string())->c_ptr()));
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_pcontext *pctx = m_call_lex->spcont;
uint csize = pctx->max_framesize();
@@ -135,7 +192,7 @@ sp_head::execute(THD *thd)
if (csize > 0)
{
uint i;
- List_iterator_fast<Item> li(m_call_lex->value_list);
+ List_iterator_fast<Item> li(*args);
Item *it;
nctx = new sp_rcontext(csize);
@@ -174,20 +231,7 @@ sp_head::execute(THD *thd)
thd->spcont= nctx;
}
- { // Execute instructions...
- 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);
- }
- }
+ ret= execute(thd);
// Don't copy back OUT values if we got an error
if (ret == 0 && csize > 0)
@@ -424,3 +468,15 @@ sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
*nextp = m_ip+1;
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);
+}
diff --git a/sql/sp_head.h b/sql/sp_head.h
index b0f57757f98..fa2a76753e7 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -29,6 +29,9 @@
#define TYPE_ENUM_FUNCTION 1
#define TYPE_ENUM_PROCEDURE 2
+Item_result
+sp_map_result_type(enum enum_field_types type);
+
struct sp_label;
class sp_instr;
@@ -62,7 +65,10 @@ public:
create(THD *thd);
int
- execute(THD *thd);
+ execute_function(THD *thd, Item **args, uint argcount, Item **resp);
+
+ int
+ execute_procedure(THD *thd, List<Item> *args);
inline void
add_instr(sp_instr *i)
@@ -103,6 +109,11 @@ public:
return n->c_ptr();
}
+ inline Item_result result()
+ {
+ return sp_map_result_type(m_returns);
+ }
+
private:
Item_string *m_name;
@@ -122,10 +133,14 @@ private:
{
sp_instr *in= NULL;
- get_dynamic(&m_instr, (gptr)&in, i);
+ if (i < m_instr.elements)
+ get_dynamic(&m_instr, (gptr)&in, i);
return in;
}
+ int
+ execute(THD *thd);
+
}; // class sp_head : public Sql_alloc
@@ -319,4 +334,28 @@ private:
}; // 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_ */
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index 5ffbb0266e6..78485fdd090 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -26,7 +26,7 @@ class sp_rcontext : public Sql_alloc
public:
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_outs = (int *)sql_alloc(size * sizeof(int));
@@ -70,12 +70,25 @@ class sp_rcontext : public Sql_alloc
return m_outs[idx];
}
+ inline void
+ set_result(Item *it)
+ {
+ m_result= it;
+ }
+
+ inline Item *
+ get_result()
+ {
+ return m_result;
+ }
+
private:
uint m_count;
uint m_size;
Item **m_frame;
int *m_outs;
+ Item *m_result; // For FUNCTIONs
}; // class sp_rcontext : public Sql_alloc
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 5bd5e69cdb8..a900d87949e 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -21,6 +21,8 @@
#include "item_create.h"
#include <m_ctype.h>
#include <hash.h>
+#include "sp.h"
+#include "sp_head.h"
LEX_STRING tmp_table_alias= {(char*) "tmp-table",8};
@@ -197,6 +199,17 @@ static int find_keyword(LEX *lex, uint len, bool function)
lex->yylval->symbol.length=len;
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
udf_func *udf;
if (function && using_udf_functions && (udf=find_udf((char*) tok, len)))
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index a4ff6a91b33..67a3865057f 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2959,6 +2959,7 @@ mysql_execute_command(THD *thd)
{
uint namelen;
char *name= lex->sphead->name(&namelen);
+#ifdef HAVE_DLOPEN
udf_func *udf = find_udf(name, namelen);
if (udf)
@@ -2966,6 +2967,7 @@ mysql_execute_command(THD *thd)
net_printf(thd, ER_UDF_EXISTS, name);
goto error;
}
+#endif
res= lex->sphead->create(thd);
switch (res)
{
@@ -3000,7 +3002,7 @@ mysql_execute_command(THD *thd)
thd->net.no_send_ok= TRUE;
#endif
- res= sp->execute(thd);
+ res= sp->execute_procedure(thd, &lex->value_list);
#ifndef EMBEDDED_LIBRARY
thd->net.no_send_ok= nsok;
#endif
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index bfb9d06c16c..fc91ee9a6e4 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -506,6 +506,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token ROUND
%token SECOND_SYM
%token SHARE_SYM
+%token SP_FUNC
%token SUBSTRING
%token SUBSTRING_INDEX
%token TRIM
@@ -575,7 +576,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%type <lex_str>
IDENT TEXT_STRING REAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM LEX_HOSTNAME
ULONGLONG_NUM field_ident select_alias ident ident_or_text
- UNDERSCORE_CHARSET
+ UNDERSCORE_CHARSET SP_FUNC ident_or_spfunc
%type <lex_str_ptr>
opt_table_alias
@@ -903,7 +904,7 @@ create:
lex->name=$4.str;
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->udf.name = $4;
@@ -929,6 +930,11 @@ create:
}
;
+ident_or_spfunc:
+ IDENT { $$= $1; }
+ | SP_FUNC { $$= $1; }
+ ;
+
create_function_tail:
RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING
{
@@ -1122,7 +1128,11 @@ sp_proc_stmt:
}
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 {}
@@ -2863,6 +2873,13 @@ simple_expr:
{ $$= new Item_func_round($3,$5,1); }
| TRUE_SYM
{ $$= 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 ')'
{
if ($3 != NULL)