diff options
author | unknown <pem@mysql.com> | 2003-02-21 17:37:05 +0100 |
---|---|---|
committer | unknown <pem@mysql.com> | 2003-02-21 17:37:05 +0100 |
commit | 0521fb5444df9a97e0682307242700bd94c6595e (patch) | |
tree | 09532f901b8cb45042f6189083d6fe928d50f20f /sql | |
parent | 3c88ebdc9dec478a44de8f6d55a22c105f1d20f0 (diff) | |
download | mariadb-git-0521fb5444df9a97e0682307242700bd94c6595e.tar.gz |
Most of the groundwork for sprint task 729 (implement FUNCTIONs).
Expanded the mysql.proc table, reworked the find/create/drop functions
completely, added new functions for FUNCTIONs (lotta functions here :),
got rid of some unnecessary use of Item_strings while at it. Extended
the parser correspondingly, and fiddled around a bit to make SP FUNCTIONs
coexist with UDFs.
Can now CREATE and DROP FUNCTIONs. Invoking yet to come...
Docs/sp-implemented.txt:
Updated with info about CASCADE/RESTICT and METHOD, and some answers to questions.
include/mysqld_error.h:
New error message for misuse of RETURN.
mysql-test/install_test_db.sh:
Added enum field to mysql.proc to distinguish between FUNCTION and PROCEDURE.
mysql-test/r/sp.result:
New test for creating and dropping FUNCTIONS.
mysql-test/t/sp.test:
New test for creating and dropping FUNCTIONS.
scripts/mysql_install_db.sh:
Added enum field to mysql.proc to distinguish between FUNCTION and PROCEDURE.
sql/lex.h:
De-UDFed some symbol names, as they are now used for SPs as well.
Added RETURN_SYM.
sql/share/czech/errmsg.txt:
New error message for misuse of RETURN.
sql/share/danish/errmsg.txt:
New error message for misuse of RETURN.
sql/share/dutch/errmsg.txt:
New error message for misuse of RETURN.
sql/share/english/errmsg.txt:
New error message for misuse of RETURN.
sql/share/estonian/errmsg.txt:
New error message for misuse of RETURN.
sql/share/french/errmsg.txt:
New error message for misuse of RETURN.
sql/share/german/errmsg.txt:
New error message for misuse of RETURN.
sql/share/greek/errmsg.txt:
New error message for misuse of RETURN.
sql/share/hungarian/errmsg.txt:
New error message for misuse of RETURN.
sql/share/italian/errmsg.txt:
New error message for misuse of RETURN.
sql/share/japanese/errmsg.txt:
New error message for misuse of RETURN.
sql/share/korean/errmsg.txt:
New error message for misuse of RETURN.
sql/share/norwegian-ny/errmsg.txt:
New error message for misuse of RETURN.
sql/share/norwegian/errmsg.txt:
New error message for misuse of RETURN.
sql/share/polish/errmsg.txt:
New error message for misuse of RETURN.
sql/share/portuguese/errmsg.txt:
New error message for misuse of RETURN.
sql/share/romanian/errmsg.txt:
New error message for misuse of RETURN.
sql/share/russian/errmsg.txt:
New error message for misuse of RETURN.
sql/share/serbian/errmsg.txt:
New error message for misuse of RETURN.
sql/share/slovak/errmsg.txt:
New error message for misuse of RETURN.
sql/share/spanish/errmsg.txt:
New error message for misuse of RETURN.
sql/share/swedish/errmsg.txt:
New error message for misuse of RETURN.
sql/share/ukrainian/errmsg.txt:
New error message for misuse of RETURN.
sql/sp.cc:
Major rehack to accomodate FUNCTIONs, and to make it easier to add
future in-memory cache of prepared SPs.
sql/sp.h:
Major rehack to accomodate FUNCTIONs, and to make it easier to add
future in-memory cache of prepared SPs.
sql/sp_head.cc:
Now creates FUNCTIONs too. (And got rid of some unnecessary Item_string use.)
sql/sp_head.h:
Now creates FUNCTIONs too. (And got rid of some unnecessary Item_string use.)
sql/sql_lex.h:
New stored FUNCTION commands.
sql/sql_parse.cc:
Added FUNCTION support ("drop" merged with the old UDF code), and made some
additional changes for better error handling (following the sp.cc rehacking).
sql/sql_yacc.yy:
Some former UDF specific symbols renamed.
Added CREATE FUNCTION parsing.
DROP FUNCTION had to be partly merged with the old UDF code, because of the similar
syntax.
RETURN statement added, but still a no-op.
Diffstat (limited to 'sql')
31 files changed, 408 insertions, 167 deletions
diff --git a/sql/lex.h b/sql/lex.h index 0cec9ef8a5d..33fbb90d21f 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -175,7 +175,7 @@ static SYMBOL symbols[] = { { "FOR", SYM(FOR_SYM),0,0}, { "FULL", SYM(FULL),0,0}, { "FULLTEXT", SYM(FULLTEXT_SYM),0,0}, - { "FUNCTION", SYM(UDF_SYM),0,0}, + { "FUNCTION", SYM(FUNCTION_SYM),0,0}, { "GEOMETRY", SYM(GEOMETRY_SYM),0,0}, { "GLOBAL", SYM(GLOBAL_SYM),0,0}, { "GRANT", SYM(GRANT),0,0}, @@ -332,7 +332,8 @@ static SYMBOL symbols[] = { { "USER_RESOURCES", SYM(RESOURCES),0,0}, { "RESTORE", SYM(RESTORE_SYM),0,0}, { "RESTRICT", SYM(RESTRICT),0,0}, - { "RETURNS", SYM(UDF_RETURNS_SYM),0,0}, + { "RETURN", SYM(RETURN_SYM),0,0}, + { "RETURNS", SYM(RETURNS_SYM),0,0}, { "REVOKE", SYM(REVOKE),0,0}, { "RIGHT", SYM(RIGHT),0,0}, { "RLIKE", SYM(REGEXP),0,0}, /* Like in mSQL2 */ diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index dd2df4c5243..67a7fc8259d 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -270,3 +270,4 @@ v/* "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index 3c66c5a64f2..193d8204c7d 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -264,3 +264,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index cdae9092602..e328bc64cfd 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -272,3 +272,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 6aa45c2ced2..b2e56d8b701 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -261,3 +261,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index af32e4cc630..46b7240dd6d 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -266,3 +266,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index 147948d567f..5dba7cd4739 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -261,3 +261,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index c3b4713241a..0a90b30d07c 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -271,3 +271,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index 43ba3c3b173..0d64095b2e3 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -261,3 +261,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index 9355daa634e..4927b9b86e0 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -263,3 +263,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index aee2c229f80..7a38bb4c9f0 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -261,3 +261,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index 2b7515c12d1..21c891fb982 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -263,3 +263,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index b8e0e7a4f25..61aa944c92d 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -261,3 +261,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index 15894decc4a..527db96e708 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -263,3 +263,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index 01ce52ec060..9af96c322c6 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -263,3 +263,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index a2440d5928e..cec72e6a1a2 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -265,3 +265,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 86b44f4d97d..93f1bc4af81 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -261,3 +261,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index f6cc343a70d..c6cd6efc9a0 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -265,3 +265,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index 33dc4e5d3b8..ecad689594d 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -264,3 +264,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index eb699fc812c..456df149576 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -257,3 +257,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index b1253642081..a2a9f94e970 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -269,3 +269,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index 6be74571317..9faddd8b7f1 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -262,3 +262,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 512314cf856..33359dbf7aa 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -261,3 +261,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index 337157829a9..dfdb3e1c378 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -266,3 +266,4 @@ "End-label without match" "Referring to uninitialized variable" "SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a stored FUNCTION" diff --git a/sql/sp.cc b/sql/sp.cc index 2d4cf97bce1..eb3a9871ab5 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -19,79 +19,170 @@ #include "sp.h" #include "sp_head.h" -// Finds the SP 'name'. Currently this always reads from the database -// and prepares (parse) it, but in the future it will first look in -// the in-memory cache for SPs. (And store newly prepared SPs there of -// course.) -sp_head * -sp_find_procedure(THD *thd, Item_string *iname) +/* + * + * DB storage of Stored PROCEDUREs and FUNCTIONs + * + */ + +static int +db_find_routine_aux(THD *thd, int type, char *name, uint namelen, + enum thr_lock_type ltype, TABLE **tablep) { - DBUG_ENTER("sp_find_procedure"); - extern int yyparse(void *thd); - LEX *tmplex; + DBUG_ENTER("db_find_routine_aux"); + DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name)); TABLE *table; TABLE_LIST tables; - const char *defstr; - String *name; - sp_head *sp = NULL; + byte key[65]; // We know name is 64 and the enum is 1 byte + uint keylen; + int ret; + + // Put the key together + keylen= namelen; + if (keylen > sizeof(key)-1) + keylen= sizeof(key)-1; + memcpy(key, name, keylen); + memset(key+keylen, (int)' ', sizeof(key)-1 - keylen); // Pad with space + key[sizeof(key)-1]= type; + keylen= sizeof(key); - name = iname->const_string(); - DBUG_PRINT("enter", ("name: %*s", name->length(), name->c_ptr())); memset(&tables, 0, sizeof(tables)); tables.db= (char*)"mysql"; tables.real_name= tables.alias= (char*)"proc"; - if (! (table= open_ltable(thd, &tables, TL_READ))) - DBUG_RETURN(NULL); + if (! (table= open_ltable(thd, &tables, ltype))) + DBUG_RETURN(SP_OPEN_TABLE_FAILED); if (table->file->index_read_idx(table->record[0], 0, - (byte*)name->c_ptr(), name->length(), + key, keylen, HA_READ_KEY_EXACT)) - goto done; + { + close_thread_tables(thd); + DBUG_RETURN(SP_KEY_NOT_FOUND); + } + *tablep= table; - if ((defstr= get_field(&thd->mem_root, table->field[1])) == NULL) - goto done; + DBUG_RETURN(SP_OK); +} + +static int +db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) +{ + DBUG_ENTER("db_find_routine"); + DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name)); + extern int yyparse(void *thd); + LEX *tmplex; + TABLE *table; + const char *defstr; + int ret; // QQ Set up our own mem_root here??? + ret= db_find_routine_aux(thd, type, name, namelen, TL_READ, &table); + if (ret != SP_OK) + goto done; + if ((defstr= get_field(&thd->mem_root, table->field[2])) == NULL) + { + ret= SP_GET_FIELD_FAILED; + goto done; + } + tmplex= lex_start(thd, (uchar*)defstr, strlen(defstr)); if (yyparse(thd) || thd->is_fatal_error || tmplex->sphead == NULL) - goto done; // Error + ret= SP_PARSE_ERROR; else - sp = tmplex->sphead; + *sphp= tmplex->sphead; done: - if (table) + if (ret == SP_OK && table) close_thread_tables(thd); - DBUG_RETURN(sp); + DBUG_RETURN(ret); } -int -sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen) +static int +db_create_routine(THD *thd, int type, + char *name, uint namelen, char *def, uint deflen) { - DBUG_ENTER("sp_create_procedure"); - DBUG_PRINT("enter", ("name: %*s def: %*s", namelen, name, deflen, def)); - int ret= 0; + DBUG_ENTER("db_create_routine"); + DBUG_PRINT("enter", ("type: %d name: %*s def: %*s", type, namelen, name, deflen, def)); + int ret; TABLE *table; TABLE_LIST tables; memset(&tables, 0, sizeof(tables)); tables.db= (char*)"mysql"; tables.real_name= tables.alias= (char*)"proc"; - /* Allow creation of procedures even if we can't open proc table */ + if (! (table= open_ltable(thd, &tables, TL_WRITE))) + ret= SP_OPEN_TABLE_FAILED; + else { - ret= -1; - goto done; + restore_record(table, 2); // Get default values for fields + + table->field[0]->store(name, namelen, default_charset_info); + table->field[1]->store((longlong)type); + table->field[2]->store(def, deflen, default_charset_info); + + if (table->file->write_row(table->record[0])) + ret= SP_WRITE_ROW_FAILED; + else + ret= SP_OK; } - restore_record(table, 2); // Get default values for fields + if (ret == SP_OK && table) + close_thread_tables(thd); + DBUG_RETURN(ret); +} - table->field[0]->store(name, namelen, default_charset_info); - table->field[1]->store(def, deflen, default_charset_info); +static int +db_drop_routine(THD *thd, int type, char *name, uint namelen) +{ + DBUG_ENTER("db_drop_routine"); + DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name)); + TABLE *table; + int ret; - ret= table->file->write_row(table->record[0]); + ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table); + if (ret == SP_OK) + { + if (table->file->delete_row(table->record[0])) + ret= SP_DELETE_ROW_FAILED; + } + + if (ret == SP_OK && table) + close_thread_tables(thd); + DBUG_RETURN(ret); +} + + +/* + * + * PROCEDURE + * + */ + +sp_head * +sp_find_procedure(THD *thd, LEX_STRING *name) +{ + DBUG_ENTER("sp_find_procedure"); + sp_head *sp; + + DBUG_PRINT("enter", ("name: %*s", name->length, name->str)); + + if (db_find_routine(thd, TYPE_ENUM_PROCEDURE, + name->str, name->length, &sp) != SP_OK) + sp= NULL; + + DBUG_RETURN(sp); +} + +int +sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen) +{ + DBUG_ENTER("sp_create_procedure"); + DBUG_PRINT("enter", ("name: %*s def: %*s", namelen, name, deflen, def)); + int ret; + + ret= db_create_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen, def, deflen); - done: - close_thread_tables(thd); DBUG_RETURN(ret); } @@ -100,26 +191,55 @@ sp_drop_procedure(THD *thd, char *name, uint namelen) { DBUG_ENTER("sp_drop_procedure"); DBUG_PRINT("enter", ("name: %*s", namelen, name)); - TABLE *table; - TABLE_LIST tables; + int ret; - tables.db= (char *)"mysql"; - tables.real_name= tables.alias= (char *)"proc"; - if (! (table= open_ltable(thd, &tables, TL_WRITE))) - goto err; - if (! table->file->index_read_idx(table->record[0], 0, - (byte *)name, namelen, - HA_READ_KEY_EXACT)) - { - int error; + ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen); + + DBUG_RETURN(ret); +} - if ((error= table->file->delete_row(table->record[0]))) - table->file->print_error(error, MYF(0)); - } - close_thread_tables(thd); - DBUG_RETURN(0); - err: - close_thread_tables(thd); - DBUG_RETURN(-1); +/* + * + * FUNCTION + * + */ + +sp_head * +sp_find_function(THD *thd, LEX_STRING *name) +{ + DBUG_ENTER("sp_find_function_i"); + sp_head *sp; + + 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; + + DBUG_RETURN(sp); +} + +int +sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen) +{ + DBUG_ENTER("sp_create_function"); + DBUG_PRINT("enter", ("name: %*s def: %*s", namelen, name, deflen, def)); + int ret; + + ret= db_create_routine(thd, TYPE_ENUM_FUNCTION, name, namelen, def, deflen); + + DBUG_RETURN(ret); +} + +int +sp_drop_function(THD *thd, char *name, uint namelen) +{ + DBUG_ENTER("sp_drop_function"); + DBUG_PRINT("enter", ("name: %*s", namelen, name)); + int ret; + + ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name, namelen); + + DBUG_RETURN(ret); } @@ -18,11 +18,17 @@ #ifndef _SP_H_ #define _SP_H_ -// -// Finds a stored procedure given its name. Returns NULL if not found. -// +// Return codes from sp_create_* and sp_drop_*: +#define SP_OK 0 +#define SP_KEY_NOT_FOUND -1 +#define SP_OPEN_TABLE_FAILED -2 +#define SP_WRITE_ROW_FAILED -3 +#define SP_DELETE_ROW_FAILED -4 +#define SP_GET_FIELD_FAILED -5 +#define SP_PARSE_ERROR -6 + sp_head * -sp_find_procedure(THD *thd, Item_string *name); +sp_find_procedure(THD *thd, LEX_STRING *name); int sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen); @@ -30,15 +36,14 @@ sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen); int sp_drop_procedure(THD *thd, char *name, uint namelen); -#if 0 + sp_head * -sp_find_function(THD *thd, Item_string *name); +sp_find_function(THD *thd, LEX_STRING *name); int sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen); int sp_drop_function(THD *thd, char *name, uint namelen); -#endif #endif /* _SP_H_ */ diff --git a/sql/sp_head.cc b/sql/sp_head.cc index ff487429ec7..8682fa2cd9d 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -102,11 +102,20 @@ sp_head::create(THD *thd) DBUG_ENTER("sp_head::create"); String *name= m_name->const_string(); String *def= m_defstr->const_string(); + int ret; + + DBUG_PRINT("info", ("type: %d name: %s def: %s", + m_type, name->c_ptr(), def->c_ptr())); + if (m_type == TYPE_ENUM_FUNCTION) + ret= sp_create_function(thd, + name->c_ptr(), name->length(), + def->c_ptr(), def->length()); + else + ret= sp_create_procedure(thd, + name->c_ptr(), name->length(), + def->c_ptr(), def->length()); - DBUG_PRINT("info", ("name: %s def: %s", name->c_ptr(), def->c_ptr())); - DBUG_RETURN(sp_create_procedure(thd, - name->c_ptr(), name->length(), - def->c_ptr(), def->length())); + DBUG_RETURN(ret); } int @@ -127,7 +136,7 @@ sp_head::execute(THD *thd) { uint i; List_iterator_fast<Item> li(m_call_lex->value_list); - Item *it = li++; // Skip first one, it's the procedure name + Item *it; nctx = new sp_rcontext(csize); if (! octx) @@ -184,7 +193,7 @@ sp_head::execute(THD *thd) if (ret == 0 && csize > 0) { List_iterator_fast<Item> li(m_call_lex->value_list); - Item *it = li++; // Skip first one, it's the procedure name + Item *it; // Copy back all OUT or INOUT values to the previous frame, or // set global user variables @@ -273,23 +282,18 @@ sp_head::restore_lex(THD *thd) // Collect some data from the sub statement lex. if (thd->lex.sql_command == SQLCOM_CALL) { - // We know they are Item_strings (since we put them there ourselves) // It would be slightly faster to keep the list sorted, but we need // an "insert before" method to do that. - Item_string *proc= static_cast<Item_string*>(thd->lex.value_list.head()); - String *snew= proc->val_str(NULL); - List_iterator_fast<Item_string> li(m_calls); - Item_string *it; + char *proc= thd->lex.udf.name.str; - while ((it= li++)) - { - String *sold= it->val_str(NULL); + List_iterator_fast<char *> li(m_calls); + char **it; - if (stringcmp(snew, sold) == 0) + while ((it= li++)) + if (strcasecmp(proc, *it) == 0) break; - } if (! it) - m_calls.push_back(proc); + m_calls.push_back(&proc); } // Merge used tables diff --git a/sql/sp_head.h b/sql/sp_head.h index 820193e8184..b0f57757f98 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -24,6 +24,11 @@ #include <stddef.h> +// Values for the type enum. This reflects the order of the enum declaration +// in the CREATE TABLE command. +#define TYPE_ENUM_FUNCTION 1 +#define TYPE_ENUM_PROCEDURE 2 + struct sp_label; class sp_instr; @@ -35,8 +40,10 @@ class sp_head : public Sql_alloc 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 - List<Item_string> m_calls; // Called procedures. + List<char *> m_calls; // Called procedures. List<char *> m_tables; // Used tables. static void *operator new(size_t size) @@ -87,6 +94,15 @@ public: void backpatch(struct sp_label *); + char *name(uint *lenp = 0) const + { + String *n= m_name->const_string(); + + if (lenp) + *lenp= n->length(); + return n->c_ptr(); + } + private: Item_string *m_name; @@ -99,7 +115,7 @@ private: struct sp_label *lab; sp_instr *instr; } bp_t; - List<bp_t> m_backpatch; // Instructions needing backpaching + List<bp_t> m_backpatch; // Instructions needing backpatching inline sp_instr * get_instr(uint i) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 5e3a0a5762b..9a4b4b8bd08 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -75,8 +75,8 @@ enum enum_sql_command { SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS, SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_TABLE_TYPES, SQLCOM_SHOW_PRIVILEGES, SQLCOM_HELP, - SQLCOM_CREATE_PROCEDURE, SQLCOM_CALL, SQLCOM_DROP_PROCEDURE, - SQLCOM_ALTER_PROCEDURE, + SQLCOM_CREATE_PROCEDURE, SQLCOM_CALL, + SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION, /* This should be the last !!! */ SQLCOM_END diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ce592b8ae97..a4ff6a91b33 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2763,26 +2763,24 @@ mysql_execute_command(THD *thd) res=mysqld_show_create_db(thd,lex->name,&lex->create_info); break; } - case SQLCOM_CREATE_FUNCTION: - if (check_access(thd,INSERT_ACL,"mysql",0,1)) - break; + case SQLCOM_CREATE_FUNCTION: // UDF function + { + if (check_access(thd,INSERT_ACL,"mysql",0,1)) + break; #ifdef HAVE_DLOPEN - if (!(res = mysql_create_function(thd,&lex->udf))) - send_ok(thd); + sp_head *sph= sp_find_function(thd, &lex->udf.name); + if (sph) + { + net_printf(thd, ER_UDF_EXISTS, lex->udf.name.str); + goto error; + } + if (!(res = mysql_create_function(thd,&lex->udf))) + send_ok(thd); #else - res= -1; + res= -1; #endif - break; - case SQLCOM_DROP_FUNCTION: - if (check_access(thd,DELETE_ACL,"mysql",0,1)) break; -#ifdef HAVE_DLOPEN - if (!(res = mysql_drop_function(thd,&lex->udf.name))) - send_ok(thd); -#else - res= -1; -#endif - break; + } case SQLCOM_REVOKE: case SQLCOM_GRANT: { @@ -2951,7 +2949,7 @@ mysql_execute_command(THD *thd) res= -1; thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); break; - case SQLCOM_CREATE_PROCEDURE: + case SQLCOM_CREATE_PROCEDURE: // FUNCTION too (but not UDF!) if (!lex->sphead) { send_error(thd, ER_SP_NO_RECURSIVE_CREATE); @@ -2959,22 +2957,35 @@ mysql_execute_command(THD *thd) } else { + uint namelen; + char *name= lex->sphead->name(&namelen); + udf_func *udf = find_udf(name, namelen); + + if (udf) + { + net_printf(thd, ER_UDF_EXISTS, name); + goto error; + } res= lex->sphead->create(thd); - if (res != 0) + switch (res) { + case SP_OK: + send_ok(thd); + break; + case SP_WRITE_ROW_FAILED: send_error(thd, ER_SP_ALREADY_EXISTS); goto error; + default: + send_error(thd, ER_SP_STORE_FAILED); + goto error; } - send_ok(thd); } break; case SQLCOM_CALL: { - Item_string *s; sp_head *sp; - s= (Item_string*)lex->value_list.head(); - sp= sp_find_procedure(thd, s); + sp= sp_find_procedure(thd, &lex->udf.name); if (! sp) { send_error(thd, ER_SP_DOES_NOT_EXIST); @@ -3002,12 +3013,14 @@ mysql_execute_command(THD *thd) } break; case SQLCOM_ALTER_PROCEDURE: + case SQLCOM_ALTER_FUNCTION: { - Item_string *s; sp_head *sp; - s= (Item_string*)lex->value_list.head(); - sp= sp_find_procedure(thd, s); + if (lex->sql_command == SQLCOM_ALTER_PROCEDURE) + sp= sp_find_procedure(thd, &lex->udf.name); + else + sp= sp_find_function(thd, &lex->udf.name); if (! sp) { send_error(thd, ER_SP_DOES_NOT_EXIST); @@ -3022,28 +3035,41 @@ mysql_execute_command(THD *thd) } break; case SQLCOM_DROP_PROCEDURE: + case SQLCOM_DROP_FUNCTION: { - Item_string *s; - sp_head *sp; - - s = (Item_string*)lex->value_list.head(); - sp = sp_find_procedure(thd, s); - if (! sp) - { - send_error(thd, ER_SP_DOES_NOT_EXIST); - goto error; - } + if (lex->sql_command == SQLCOM_DROP_PROCEDURE) + res= sp_drop_procedure(thd, lex->udf.name.str, lex->udf.name.length); else { - String *name = s->const_string(); - - res= sp_drop_procedure(thd, name->c_ptr(), name->length()); - if (res != 0) + res= sp_drop_function(thd, lex->udf.name.str, lex->udf.name.length); +#ifdef HAVE_DLOPEN + if (res == SP_KEY_NOT_FOUND) { - send_error(thd, ER_SP_DROP_FAILED); - goto error; + udf_func *udf = find_udf(lex->udf.name.str, lex->udf.name.length); + if (udf) + { + if (check_access(thd, DELETE_ACL, "mysql", 0, 1)) + goto error; + if (!(res = mysql_drop_function(thd,&lex->udf.name))) + { + send_ok(thd); + break; + } + } } +#endif + } + switch (res) + { + case SP_OK: send_ok(thd); + break; + case SP_KEY_NOT_FOUND: + send_error(thd, ER_SP_DOES_NOT_EXIST); + goto error; + default: + send_error(thd, ER_SP_DROP_FAILED); + goto error; } } break; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ea3b550146b..bfb9d06c16c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -366,9 +366,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token FUNC_ARG1 %token FUNC_ARG2 %token FUNC_ARG3 -%token UDF_RETURNS_SYM +%token RETURN_SYM +%token RETURNS_SYM %token UDF_SONAME_SYM -%token UDF_SYM +%token FUNCTION_SYM %token UNCOMMITTED_SYM %token UNDERSCORE_CHARSET %token UNICODE_SYM @@ -902,27 +903,23 @@ create: lex->name=$4.str; lex->create_info.options=$3; } - | CREATE udf_func_type UDF_SYM IDENT + | CREATE udf_func_type FUNCTION_SYM IDENT { LEX *lex=Lex; - lex->sql_command = SQLCOM_CREATE_FUNCTION; lex->udf.name = $4; lex->udf.type= $2; } - UDF_RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING - { - LEX *lex=Lex; - lex->udf.returns=(Item_result) $7; - lex->udf.dl=$9.str; - } + create_function_tail + {} | CREATE PROCEDURE ident { LEX *lex= Lex; - lex->spcont = new sp_pcontext(); - lex->sphead = new sp_head(&$3, lex); + lex->spcont= new sp_pcontext(); + lex->sphead= new sp_head(&$3, lex); + lex->sphead->m_type= TYPE_ENUM_PROCEDURE; } - '(' sp_dparam_list ')' + '(' sp_pdparam_list ')' { Lex->spcont->set_params(); } @@ -932,15 +929,43 @@ create: } ; +create_function_tail: + RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_CREATE_FUNCTION; + lex->udf.returns=(Item_result) $2; + lex->udf.dl=$4.str; + } + | '(' + { + LEX *lex= Lex; + + lex->sql_command = SQLCOM_CREATE_PROCEDURE; + lex->spcont= new sp_pcontext(); + lex->sphead= new sp_head(&lex->udf.name, lex); + lex->sphead->m_type= TYPE_ENUM_FUNCTION; + } + sp_fdparam_list ')' + { + Lex->spcont->set_params(); + } + RETURNS_SYM type + { + Lex->sphead->m_returns= (enum enum_field_types)$7; + } + sp_proc_stmt + {} + ; + call: CALL_SYM ident { LEX *lex = Lex; lex->sql_command= SQLCOM_CALL; + lex->udf.name= $2; lex->value_list.empty(); - lex->value_list.push_back( - (Item*)new Item_string($2.str, $2.length, default_charset_info)); } '(' sp_cparam_list ')' {} ; @@ -962,18 +987,36 @@ sp_cparams: } ; -/* SP parameter declaration list */ -sp_dparam_list: +/* Stored FUNCTION parameter declaration list */ +sp_fdparam_list: /* Empty */ - | sp_dparams + | sp_fdparams ; -sp_dparams: - sp_dparams ',' sp_dparam - | sp_dparam +sp_fdparams: + sp_fdparams ',' sp_fdparam + | sp_fdparam ; -sp_dparam: +sp_fdparam: + ident type sp_opt_locator + { + Lex->spcont->push(&$1, (enum enum_field_types)$2, sp_param_in); + } + ; + +/* Stored PROCEDURE parameter declaration list */ +sp_pdparam_list: + /* Empty */ + | sp_pdparams + ; + +sp_pdparams: + sp_pdparams ',' sp_pdparam + | sp_pdparam + ; + +sp_pdparam: sp_opt_inout ident type sp_opt_locator { Lex->spcont->push(&$2, @@ -1068,6 +1111,20 @@ sp_proc_stmt: lex->sphead->restore_lex(YYTHD); } } + | RETURN_SYM expr + { + LEX *lex= Lex; + + if (lex->sphead->m_type == TYPE_ENUM_PROCEDURE) + { + send_error(YYTHD, ER_SP_BADRETURN); + YYABORT; + } + else + { + /* QQ nothing yet */ + } + } | IF sp_if END IF {} | CASE_SYM WHEN_SYM { @@ -1886,7 +1943,7 @@ alter: lex->sql_command=SQLCOM_ALTER_DB; lex->name=$3.str; } - | ALTER PROCEDURE opt_specific ident + | ALTER PROCEDURE ident /* QQ Characteristics missing for now */ opt_restrict { @@ -1895,18 +1952,10 @@ alter: /* This is essensially an no-op right now, since we haven't put the characteristics in yet. */ lex->sql_command= SQLCOM_ALTER_PROCEDURE; - lex->value_list.empty(); - lex->value_list.push_back( - (Item*)new Item_string($4.str, $4.length, default_charset_info)); + lex->udf.name= $3; } ; -opt_specific: - /* Empty */ - | - SPECIFIC_SYM - ; - alter_list: | alter_list_item | alter_list ',' alter_list_item; @@ -3540,23 +3589,20 @@ drop: lex->drop_if_exists=$3; lex->name=$4.str; } - | DROP UDF_SYM IDENT + | DROP FUNCTION_SYM IDENT opt_restrict { LEX *lex=Lex; lex->sql_command = SQLCOM_DROP_FUNCTION; - lex->udf.name = $3; + lex->udf.name= $3; } | DROP PROCEDURE ident opt_restrict { LEX *lex=Lex; lex->sql_command = SQLCOM_DROP_PROCEDURE; - lex->value_list.empty(); - lex->value_list.push_back( - (Item*)new Item_string($3.str, $3.length, default_charset_info)); + lex->udf.name= $3; } ; - table_list: table_name | table_list ',' table_name; @@ -4583,7 +4629,7 @@ keyword: | TIMESTAMP {} | TIME_SYM {} | TYPE_SYM {} - | UDF_SYM {} + | FUNCTION_SYM {} | UNCOMMITTED_SYM {} | UNICODE_SYM {} | USE_FRM {} |