diff options
author | unknown <pem@mysql.com> | 2003-02-26 19:22:29 +0100 |
---|---|---|
committer | unknown <pem@mysql.com> | 2003-02-26 19:22:29 +0100 |
commit | 76b037dc4288fada1b64efbef422cb8b4bd0d5b5 (patch) | |
tree | 21d88c3a00a56c11d4f65d0c77e082899658ec01 /sql | |
parent | 0521fb5444df9a97e0682307242700bd94c6595e (diff) | |
download | mariadb-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.cc | 75 | ||||
-rw-r--r-- | sql/item_func.h | 66 | ||||
-rw-r--r-- | sql/sp.cc | 21 | ||||
-rw-r--r-- | sql/sp.h | 4 | ||||
-rw-r--r-- | sql/sp_head.cc | 166 | ||||
-rw-r--r-- | sql/sp_head.h | 43 | ||||
-rw-r--r-- | sql/sp_rcontext.h | 15 | ||||
-rw-r--r-- | sql/sql_lex.cc | 13 | ||||
-rw-r--r-- | sql/sql_parse.cc | 4 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 23 |
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; +} @@ -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) |