diff options
author | unknown <pem@mysql.com> | 2003-03-27 12:09:11 +0100 |
---|---|---|
committer | unknown <pem@mysql.com> | 2003-03-27 12:09:11 +0100 |
commit | 908c9d7acfdd8f3345f9fc6edc9e2e627090d743 (patch) | |
tree | 3dbe93fb3b28f4aa6b8445c9b4aaa8c2ee776e02 /sql | |
parent | af90350eb9439e18286d3ba25c274402526ad0dc (diff) | |
parent | def0cffeaafe66d48687bf2a22137e83d85da789 (diff) | |
download | mariadb-git-908c9d7acfdd8f3345f9fc6edc9e2e627090d743.tar.gz |
Merged 4.1 into 5.0.
BitKeeper/etc/gone:
auto-union
BitKeeper/etc/logging_ok:
auto-union
client/mysql.cc:
Auto merged
client/mysqltest.c:
Auto merged
configure.in:
Auto merged
libmysqld/Makefile.am:
Auto merged
sql/item.h:
Auto merged
sql/item_func.h:
Auto merged
sql/lex.h:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/sql_class.cc:
Auto merged
sql/sql_class.h:
Auto merged
sql/sql_lex.cc:
Auto merged
sql/sql_lex.h:
Auto merged
sql/share/polish/errmsg.txt:
Auto merged
sql/share/russian/errmsg.txt:
Auto merged
sql/sql_select.cc:
Auto merged
Diffstat (limited to 'sql')
44 files changed, 3172 insertions, 82 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index b1d9149ddf4..1f00cfbd9e0 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -57,7 +57,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ lex.h lex_symbol.h sql_acl.h sql_crypt.h \ log_event.h mini_client.h sql_repl.h slave.h \ stacktrace.h sql_sort.h sql_cache.h set_var.h \ - spatial.h gstream.h + spatial.h gstream.h sp_head.h sp_pcontext.h \ + sp_rcontext.h sp.h mysqld_SOURCES = sql_lex.cc sql_handler.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \ @@ -85,7 +86,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \ slave.cc sql_repl.cc sql_union.cc sql_derived.cc \ mini_client.cc mini_client_errors.c \ stacktrace.c repl_failsafe.h repl_failsafe.cc sql_olap.cc\ - gstream.cc spatial.cc sql_help.cc + gstream.cc spatial.cc sql_help.cc \ + sp_head.cc sp_pcontext.cc sp.cc gen_lex_hash_SOURCES = gen_lex_hash.cc gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS) diff --git a/sql/item.cc b/sql/item.cc index b4d2d0670d8..f4c27061e53 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -22,6 +22,7 @@ #include "mysql_priv.h" #include <m_ctype.h> #include "my_dir.h" +#include "sp_rcontext.h" /***************************************************************************** ** Item functions @@ -175,6 +176,23 @@ CHARSET_INFO * Item::default_charset() const return current_thd->db_charset; } + +Item * +Item_splocal::this_item() +{ + THD *thd= current_thd; + + return thd->spcont->get_item(m_offset); +} + +Item * +Item_splocal::this_const_item() const +{ + THD *thd= current_thd; + + return thd->spcont->get_item(m_offset); +} + bool Item::set_charset(CHARSET_INFO *cs1, enum coercion co1, CHARSET_INFO *cs2, enum coercion co2) { diff --git a/sql/item.h b/sql/item.h index 1b5fafc180f..8009439b708 100644 --- a/sql/item.h +++ b/sql/item.h @@ -123,6 +123,8 @@ public: bool set_charset(CHARSET_INFO *cs1, enum coercion co1, CHARSET_INFO *cs2, enum coercion co2); virtual void set_outer_resolving() {} + virtual Item *this_item() { return this; } /* For SPs mostly. */ + virtual Item *this_const_item() const { return const_cast<Item*>(this); } /* For SPs mostly. */ // Row emulation virtual uint cols() { return 1; } @@ -136,6 +138,57 @@ public: }; +// A local SP variable (incl. parameters), used in runtime +class Item_splocal : public Item +{ +private: + + uint m_offset; + +public: + + Item_splocal(uint offset) + : m_offset(offset) + {} + + Item *this_item(); + Item *this_const_item() const; + + inline uint get_offset() + { + return m_offset; + } + + // Abstract methods inherited from Item. Just defer the call to + // the item in the frame + inline enum Type type() const + { + return this_const_item()->type(); + } + + inline double val() + { + return this_item()->val(); + } + + inline longlong val_int() + { + return this_item()->val_int(); + } + + inline String *val_str(String *sp) + { + return this_item()->val_str(sp); + } + + inline void make_field(Send_field *field) + { + this_item()->make_field(field); + } + +}; + + class st_select_lex; class Item_ident :public Item { diff --git a/sql/item_func.cc b/sql/item_func.cc index 5bbb6003e4f..05d66f9bfcb 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -30,6 +30,9 @@ #ifdef HAVE_COMPRESS #include <zlib.h> #endif +#include "sp_head.h" +#include "sp_rcontext.h" +#include "sp.h" /* return TRUE if item is a constant */ @@ -2839,3 +2842,74 @@ longlong Item_func_srid::val_int() uint32 res= uint4korr(swkb->ptr()); return (longlong) 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); + if (! m_sp) + DBUG_RETURN(-1); + + DBUG_RETURN(m_sp->execute_function(thd, args, arg_count, itp)); +} + +enum enum_field_types +Item_func_sp::field_type() const +{ + DBUG_ENTER("Item_func_sp::field_type"); + + if (! m_sp) + m_sp= sp_find_function(current_thd, const_cast<LEX_STRING*>(&m_name)); + if (m_sp) + { + DBUG_PRINT("info", ("m_returns = %d", m_sp->m_returns)); + DBUG_RETURN(m_sp->m_returns); + } + DBUG_RETURN(MYSQL_TYPE_STRING); +} + +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) + m_sp= sp_find_function(current_thd, const_cast<LEX_STRING*>(&m_name)); + if (m_sp) + { + DBUG_RETURN(m_sp->result()); + } + DBUG_RETURN(STRING_RESULT); +} + +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); + if (m_sp) + { + 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 0429e650071..1c70d00f7e7 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1179,3 +1179,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; + mutable 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; + } + + enum enum_field_types field_type() const; + + 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 fix_length_and_dec(); + +}; diff --git a/sql/lex.h b/sql/lex.h index 5325329f61f..9e9cb75da1c 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -60,6 +60,7 @@ static SYMBOL symbols[] = { { "AS", SYM(AS),0,0}, { "ASC", SYM(ASC),0,0}, { "ASCII", SYM(ASCII_SYM),0,0}, + { "ASENSITIVE", SYM(ASENSITIVE_SYM),0,0}, { "AVG", SYM(AVG_SYM),0,0}, { "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH),0,0}, { "AUTO_INCREMENT", SYM(AUTO_INC),0,0}, @@ -81,6 +82,7 @@ static SYMBOL symbols[] = { { "BY", SYM(BY),0,0}, { "BYTE", SYM(BYTE_SYM), 0, 0}, { "CACHE", SYM(CACHE_SYM),0,0}, + { "CALL", SYM(CALL_SYM),0,0}, { "CASCADE", SYM(CASCADE),0,0}, { "CASE", SYM(CASE_SYM),0,0}, { "CHAR", SYM(CHAR_SYM),0,0}, @@ -109,6 +111,7 @@ static SYMBOL symbols[] = { { "CURRENT_DATE", SYM(CURDATE),0,0}, { "CURRENT_TIME", SYM(CURTIME),0,0}, { "CURRENT_TIMESTAMP", SYM(NOW_SYM),0,0}, + { "CURSOR", SYM(CURSOR_SYM),0,0}, { "DATA", SYM(DATA_SYM),0,0}, { "DATABASE", SYM(DATABASE),0,0}, { "DATABASES", SYM(DATABASES),0,0}, @@ -120,6 +123,7 @@ static SYMBOL symbols[] = { { "DAY_SECOND", SYM(DAY_SECOND_SYM),0,0}, { "DEC", SYM(DECIMAL_SYM),0,0}, { "DECIMAL", SYM(DECIMAL_SYM),0,0}, + { "DECLARE", SYM(DECLARE_SYM),0,0}, { "DES_KEY_FILE", SYM(DES_KEY_FILE),0,0}, { "DEFAULT", SYM(DEFAULT),0,0}, { "DELAYED", SYM(DELAYED_SYM),0,0}, @@ -142,6 +146,7 @@ static SYMBOL symbols[] = { { "ERRORS", SYM(ERRORS),0,0}, { "END", SYM(END),0,0}, { "ELSE", SYM(ELSE),0,0}, + { "ELSEIF", SYM(ELSEIF_SYM),0,0}, { "ESCAPE", SYM(ESCAPE_SYM),0,0}, { "ESCAPED", SYM(ESCAPED),0,0}, { "ENABLE", SYM(ENABLE_SYM),0,0}, @@ -172,7 +177,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}, { "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION),0,0}, { "GLOBAL", SYM(GLOBAL_SYM),0,0}, @@ -198,6 +203,8 @@ static SYMBOL symbols[] = { { "INNER", SYM(INNER_SYM),0,0}, { "INNOBASE", SYM(INNOBASE_SYM),0,0}, { "INNODB", SYM(INNOBASE_SYM),0,0}, + { "INOUT", SYM(INOUT_SYM),0,0}, + { "INSENSITIVE", SYM(INSENSITIVE_SYM),0,0}, { "INSERT", SYM(INSERT),0,0}, { "INSERT_METHOD", SYM(INSERT_METHOD),0,0}, { "INT", SYM(INT_SYM),0,0}, @@ -215,12 +222,14 @@ static SYMBOL symbols[] = { { "ISOLATION", SYM(ISOLATION),0,0}, { "ISAM", SYM(ISAM_SYM),0,0}, { "ISSUER", SYM(ISSUER_SYM),0,0}, + { "ITERATE", SYM(ITERATE_SYM),0,0}, { "JOIN", SYM(JOIN_SYM),0,0}, { "KEY", SYM(KEY_SYM),0,0}, { "KEYS", SYM(KEYS),0,0}, { "KILL", SYM(KILL_SYM),0,0}, { "LAST", SYM(LAST_SYM),0,0}, { "LEADING", SYM(LEADING),0,0}, + { "LEAVE", SYM(LEAVE_SYM),0,0}, { "LEFT", SYM(LEFT),0,0}, { "LEVEL", SYM(LEVEL_SYM),0,0}, { "LIKE", SYM(LIKE),0,0}, @@ -236,6 +245,7 @@ static SYMBOL symbols[] = { { "LOGS", SYM(LOGS_SYM),0,0}, { "LONG", SYM(LONG_SYM),0,0}, { "LONGBLOB", SYM(LONGBLOB),0,0}, + { "LOOP", SYM(LOOP_SYM),0,0}, { "LONGTEXT", SYM(LONGTEXT),0,0}, { "LOW_PRIORITY", SYM(LOW_PRIORITY),0,0}, { "MASTER", SYM(MASTER_SYM),0,0}, @@ -290,6 +300,7 @@ static SYMBOL symbols[] = { { "OPTIONALLY", SYM(OPTIONALLY),0,0}, { "OR", SYM(OR),0,0}, { "ORDER", SYM(ORDER_SYM),0,0}, + { "OUT", SYM(OUT_SYM),0,0}, { "OUTER", SYM(OUTER),0,0}, { "OUTFILE", SYM(OUTFILE),0,0}, { "PACK_KEYS", SYM(PACK_KEYS_SYM),0,0}, @@ -319,13 +330,15 @@ static SYMBOL symbols[] = { { "REPAIR", SYM(REPAIR),0,0}, { "REPLACE", SYM(REPLACE),0,0}, { "REPLICATION", SYM(REPLICATION),0,0}, + { "REPEAT", SYM(REPEAT_SYM),0,0}, { "REPEATABLE", SYM(REPEATABLE_SYM),0,0}, { "REQUIRE", SYM(REQUIRE_SYM),0,0}, { "RESET", SYM(RESET_SYM),0,0}, { "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 */ @@ -336,6 +349,7 @@ static SYMBOL symbols[] = { { "RTREE", SYM(RTREE_SYM),0,0}, { "SECOND", SYM(SECOND_SYM),0,0}, { "SELECT", SYM(SELECT_SYM),0,0}, + { "SENSITIVE", SYM(SENSITIVE_SYM),0,0}, { "SERIAL", SYM(SERIAL_SYM),0,0}, { "SERIALIZABLE", SYM(SERIALIZABLE_SYM),0,0}, { "SESSION", SYM(SESSION_SYM),0,0}, @@ -350,6 +364,7 @@ static SYMBOL symbols[] = { { "SOME", SYM(ANY_SYM),0,0}, { "SONAME", SYM(UDF_SONAME_SYM),0,0}, { "SPATIAL", SYM(SPATIAL_SYM),0,0}, + { "SPECIFIC", SYM(SPECIFIC_SYM),0,0}, { "SQL_BIG_RESULT", SYM(SQL_BIG_RESULT),0,0}, { "SQL_BUFFER_RESULT", SYM(SQL_BUFFER_RESULT),0,0}, { "SQL_CACHE", SYM(SQL_CACHE_SYM), 0, 0}, @@ -392,6 +407,7 @@ static SYMBOL symbols[] = { { "UNIQUE", SYM(UNIQUE_SYM),0,0}, { "UNLOCK", SYM(UNLOCK_SYM),0,0}, { "UNSIGNED", SYM(UNSIGNED),0,0}, + { "UNTIL", SYM(UNTIL_SYM),0,0}, { "USE", SYM(USE_SYM),0,0}, { "USE_FRM", SYM(USE_FRM),0,0}, { "USING", SYM(USING),0,0}, @@ -410,6 +426,7 @@ static SYMBOL symbols[] = { { "WRITE", SYM(WRITE_SYM),0,0}, { "WHEN", SYM(WHEN_SYM),0,0}, { "WHERE", SYM(WHERE),0,0}, + { "WHILE", SYM(WHILE_SYM),0,0}, { "XOR", SYM(XOR),0,0}, { "X509", SYM(X509_SYM),0,0}, { "YEAR", SYM(YEAR_SYM),0,0}, @@ -568,7 +585,6 @@ static SYMBOL sql_functions[] = { { "RADIANS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_radians)}, { "RAND", SYM(RAND),0,0}, { "RELEASE_LOCK", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_release_lock)}, - { "REPEAT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_repeat)}, { "REVERSE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_reverse)}, { "ROUND", SYM(ROUND),0,0}, { "RPAD", SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_rpad)}, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 678af8f881d..c4be04943c0 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -362,7 +362,7 @@ extern "C" pthread_handler_decl(handle_one_connection,arg); extern "C" pthread_handler_decl(handle_bootstrap,arg); void end_thread(THD *thd,bool put_in_cache); void flush_thread_cache(); -void mysql_execute_command(THD *thd); +int mysql_execute_command(THD *thd); bool do_command(THD *thd); bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length); diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 169595c34c5..4d93ecf7db3 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -264,3 +264,15 @@ v/* "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index ff58079f4c8..c5cf27a94d7 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -258,3 +258,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index a0a6755c693..6105d619ec0 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -266,3 +266,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 0f48416d6db..bcd96916516 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -255,3 +255,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index 4503f011692..6f90684dcfe 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -260,3 +260,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index 9474d67f2f2..56065154b38 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -255,3 +255,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index ef4110a6b93..b1b6e695fb1 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -265,3 +265,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index 3f54e7740bd..bad888430ef 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -255,3 +255,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index 7519448ef33..1ee7d95bebe 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -257,3 +257,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index 71a02896c01..dcacb064356 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -255,3 +255,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index b10ddb1d1d2..bb928217a6d 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -257,3 +257,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index be1e85ad33c..0f6f4f95fa9 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -255,3 +255,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index 84c96a0540e..4ece1312243 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -257,3 +257,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index b57eb19906d..9b2165909aa 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -257,3 +257,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 2d07b362257..ed02910549a 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -259,3 +259,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 8db48115c49..24cc385b1ed 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -255,3 +255,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 337cfd0963e..4ab6c04d67b 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -259,3 +259,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index b5d1a0a7e07..e136e2d4e35 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -257,3 +257,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index d6883efc96b..402f4b94f70 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -251,3 +251,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index ee7d1e9c4fb..5e0cf29c734 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -263,3 +263,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index 2dfdc7573fa..e26ca91bd8d 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -256,3 +256,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 6d34b5a93b3..d2c099d38d0 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -255,3 +255,15 @@ "COLLATION '%s' är inte tillåtet för CHARACTER SET '%s'" "Slaven har redan startat" "Slaven har redan stoppat" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index be8fe2afea5..242c1bef664 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -260,3 +260,15 @@ "COLLATION '%s' is not valid for CHARACTER SET '%s'" "The slave was already running" "The slave was already stopped" +"Can't create a %s from within another stored routine" +"%s %s already exists" +"%s %s does not exist" +"Failed to DROP %s %s" +"Failed to CREATE %s %s" +"%s with no matching label: %s" +"Redefining label %s" +"End-label %s without match" +"Referring to uninitialized variable %s" +"SELECT in a stored procedure must have INTO" +"RETURN is only allowed in a FUNCTION" +"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION" diff --git a/sql/sp.cc b/sql/sp.cc new file mode 100644 index 00000000000..2bb714ab33a --- /dev/null +++ b/sql/sp.cc @@ -0,0 +1,391 @@ +/* Copyright (C) 2002 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#include "mysql_priv.h" +#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, bool *opened) +{ + DBUG_ENTER("db_find_routine_aux"); + DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name)); + TABLE *table; + 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); + + 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 + { + 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, + key, keylen, + HA_READ_KEY_EXACT)) + { + *tablep= NULL; + DBUG_RETURN(SP_KEY_NOT_FOUND); + } + *tablep= table; + + 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; + bool opened; + + // QQ Set up our own mem_root here??? + 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) + { + ret= SP_GET_FIELD_FAILED; + goto done; + } + 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) + ret= SP_PARSE_ERROR; + else + *sphp= tmplex->sphead; + + done: + if (table && opened) + close_thread_tables(thd); + DBUG_RETURN(ret); +} + +static int +db_create_routine(THD *thd, int type, + char *name, uint namelen, char *def, uint deflen) +{ + 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"; + + if (! (table= open_ltable(thd, &tables, TL_WRITE))) + ret= SP_OPEN_TABLE_FAILED; + else + { + restore_record(table, 2); // Get default values for fields + + table->field[0]->store(name, namelen, system_charset_info); + table->field[1]->store((longlong)type); + table->field[2]->store(def, deflen, system_charset_info); + + if (table->file->write_row(table->record[0])) + ret= SP_WRITE_ROW_FAILED; + else + ret= SP_OK; + } + + close_thread_tables(thd); + DBUG_RETURN(ret); +} + +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; + bool opened; + + 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; + } + + if (opened) + 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); + + DBUG_RETURN(ret); +} + +int +sp_drop_procedure(THD *thd, char *name, uint namelen) +{ + DBUG_ENTER("sp_drop_procedure"); + DBUG_PRINT("enter", ("name: %*s", namelen, name)); + int ret; + + ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen); + + DBUG_RETURN(ret); +} + + +/* + * + * FUNCTION + * + */ + +sp_head * +sp_find_function(THD *thd, LEX_STRING *name) +{ + DBUG_ENTER("sp_find_function"); + sp_head *sp; + + DBUG_PRINT("enter", ("name: %*s", name->length, name->str)); + + 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); +} + +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); +} + +// QQ Temporary until the function call detection in sql_lex has been reworked. +bool +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, &opened) == SP_OK) + { + ret= TRUE; + } + 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 new file mode 100644 index 00000000000..21fcb4c5360 --- /dev/null +++ b/sql/sp.h @@ -0,0 +1,65 @@ +/* -*- C++ -*- */ +/* Copyright (C) 2002 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _SP_H_ +#define _SP_H_ + +// 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, LEX_STRING *name); + +int +sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen); + +int +sp_drop_procedure(THD *thd, char *name, uint namelen); + + +sp_head * +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); + +// QQ Temporary until the function call detection in sql_lex has been reworked. +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 new file mode 100644 index 00000000000..09c680a0b80 --- /dev/null +++ b/sql/sp_head.cc @@ -0,0 +1,525 @@ +/* Copyright (C) 2002 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include "mysql_priv.h" +#include "sp_head.h" +#include "sp.h" +#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. +*/ +static Item * +eval_func_item(THD *thd, Item *it, enum enum_field_types type) +{ + DBUG_ENTER("eval_func_item"); + it= it->this_item(); + DBUG_PRINT("info", ("type: %d", type)); + + if (it->fix_fields(thd, 0, NULL)) + { + DBUG_PRINT("info", ("fix_fields() failed")); + DBUG_RETURN(it); // Shouldn't happen? + } + + /* QQ How do we do this? Is there some better way? */ + if (type == MYSQL_TYPE_NULL) + it= new Item_null(); + else + { + switch (sp_map_result_type(type)) { + case INT_RESULT: + DBUG_PRINT("info", ("INT_RESULT: %d", it->val_int())); + it= new Item_int(it->val_int()); + break; + case REAL_RESULT: + DBUG_PRINT("info", ("REAL_RESULT: %g", it->val())); + it= new Item_real(it->val()); + break; + default: + { + char buffer[MAX_FIELD_WIDTH]; + String tmp(buffer, sizeof(buffer), it->charset()); + String *s= it->val_str(&tmp); + + DBUG_PRINT("info", ("default result: %*s", s->length(), s->c_ptr_quick())) + it= new Item_string(sql_strmake(s->c_ptr_quick(), s->length()), + s->length(), it->charset()); + break; + } + } + } + + DBUG_RETURN(it); +} + +sp_head::sp_head(LEX_STRING *name, LEX *lex) + : m_simple_case(FALSE) +{ + const char *dstr = (const char*)lex->buf; + + m_name= new Item_string(name->str, name->length, system_charset_info); + m_defstr= new Item_string(dstr, lex->end_of_query - lex->buf, + system_charset_info); + m_pcont= lex->spcont; + my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8); + m_backpatch.empty(); +} + +int +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_RETURN(ret); +} + + +int +sp_head::execute(THD *thd) +{ + DBUG_ENTER("sp_head::execute"); + char *olddbname; + char *olddbptr= thd->db; + int ret= 0; + uint ip= 0; + + if (olddbptr) + olddbname= my_strdup(olddbptr, MYF(MY_WME)); + + 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); + + /* If the DB has changed, the pointer has changed too, but the + original thd->db will then have been freed */ + if (olddbptr && olddbptr != thd->db && olddbname) + { + /* QQ Maybe we should issue some special error message or warning here, + if this fails?? */ + ret= mysql_change_db(thd, olddbname); + my_free(olddbname, MYF(0)); + } + DBUG_RETURN(ret); +} + + +int +sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) +{ + 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; + 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 = m_pcont->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_procedure"); + DBUG_PRINT("info", ("procedure %s", ((String *)m_name->const_string())->c_ptr())); + int ret; + sp_instr *p; + 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 + + if (csize > 0) + { + uint i; + List_iterator_fast<Item> li(*args); + Item *it; + + nctx = new sp_rcontext(csize); + if (! octx) + { // Create a temporary old context + octx = new sp_rcontext(csize); + tmp_octx = TRUE; + } + // QQ: No error checking whatsoever right now. Should do type checking? + for (i = 0 ; (it= li++) && i < params ; i++) + { + sp_pvar_t *pvar = m_pcont->find_pvar(i); + + if (! pvar) + nctx->set_oindex(i, -1); // Shouldn't happen + else + { + if (pvar->mode == sp_param_out) + nctx->push_item(NULL); // OUT + else + nctx->push_item(eval_func_item(thd, it, pvar->type)); // IN or INOUT + // Note: If it's OUT or INOUT, it must be a variable. + // QQ: Need to handle "global" user/host variables too!!! + if (pvar->mode == sp_param_in) + nctx->set_oindex(i, -1); // IN + else // OUT or INOUT + nctx->set_oindex(i, static_cast<Item_splocal *>(it)->get_offset()); + } + } + // The rest of the frame are local variables which are all IN. + // QQ We haven't found any hint of what the value is when unassigned, + // so we set it to NULL for now. It's an error to refer to an + // unassigned variable anyway (which should be detected by the parser). + for (; i < csize ; i++) + nctx->push_item(NULL); + thd->spcont= nctx; + } + + ret= execute(thd); + + // Don't copy back OUT values if we got an error + if (ret == 0 && csize > 0) + { + List_iterator_fast<Item> li(*args); + Item *it; + + // Copy back all OUT or INOUT values to the previous frame, or + // set global user variables + for (uint i = 0 ; (it= li++) && i < params ; i++) + { + int oi = nctx->get_oindex(i); + + if (oi >= 0) + { + if (! tmp_octx) + octx->set_item(nctx->get_oindex(i), nctx->get_item(i)); + else + { // A global user variable +#if 0 + // QQ This works if the parameter really is a user variable, but + // for the moment we can't assure that, so it will crash if it's + // something else. So for now, we just do nothing, to avoid a crash. + // Note: This also assumes we have a get_name() method in + // the Item_func_get_user_var class. + Item *item= nctx->get_item(i); + Item_func_set_user_var *suv; + Item_func_get_user_var *guv= static_cast<Item_func_get_user_var*>(it); + + suv= new Item_func_set_user_var(guv->get_name(), item); + suv->fix_fields(thd, NULL, &item); + suv->fix_length_and_dec(); + suv->update(); +#endif + } + } + } + + if (tmp_octx) + thd->spcont= NULL; + else + thd->spcont= octx; + } + + DBUG_RETURN(ret); +} + + +// Reset lex during parsing, before we parse a sub statement. +void +sp_head::reset_lex(THD *thd) +{ + memcpy(&m_lex, &thd->lex, sizeof(LEX)); // Save old one + /* Reset most stuff. The length arguments doesn't matter here. */ + lex_start(thd, m_lex.buf, m_lex.end_of_query - m_lex.ptr); + /* We must reset ptr and end_of_query again */ + thd->lex.ptr= m_lex.ptr; + thd->lex.end_of_query= m_lex.end_of_query; + /* And keep the SP stuff too */ + thd->lex.sphead = m_lex.sphead; + thd->lex.spcont = m_lex.spcont; + /* Clear all lists. (QQ Why isn't this reset by lex_start()?). + We may be overdoing this, but we know for sure that value_list must + be cleared at least. */ + thd->lex.col_list.empty(); + thd->lex.ref_list.empty(); + thd->lex.drop_list.empty(); + thd->lex.alter_list.empty(); + thd->lex.interval_list.empty(); + thd->lex.users_list.empty(); + thd->lex.columns.empty(); + thd->lex.key_list.empty(); + thd->lex.create_list.empty(); + thd->lex.insert_list= NULL; + thd->lex.field_list.empty(); + thd->lex.value_list.empty(); + thd->lex.many_values.empty(); + thd->lex.var_list.empty(); + thd->lex.param_list.empty(); + thd->lex.proc_list.empty(); + thd->lex.auxilliary_table_list.empty(); +} + +// Restore lex during parsing, after we have parsed a sub statement. +void +sp_head::restore_lex(THD *thd) +{ + // Update some state in the old one first + m_lex.ptr= thd->lex.ptr; + 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 + // an "insert before" method to do that. + char *proc= thd->lex.udf.name.str; + + List_iterator_fast<char *> li(m_calls); + char **it; + + while ((it= li++)) + if (strcasecmp(proc, *it) == 0) + break; + if (! it) + m_calls.push_back(&proc); + + } + // Merge used tables + // QQ ...or just open tables in thd->open_tables? + // This is not entirerly clear at the moment, but for now, we collect + // tables here. + for (SELECT_LEX *sl= thd->lex.all_selects_list ; + sl ; + sl= sl->next_select()) + { + for (TABLE_LIST *tables= sl->get_table_list() ; + tables ; + tables= tables->next) + { + List_iterator_fast<char *> li(m_tables); + char **tb; + + while ((tb= li++)) + if (strcasecmp(tables->real_name, *tb) == 0) + break; + if (! tb) + m_tables.push_back(&tables->real_name); + } + } +#endif + + memcpy(&thd->lex, &m_lex, sizeof(LEX)); // Restore lex +} + +void +sp_head::push_backpatch(sp_instr *i, sp_label_t *lab) +{ + bp_t *bp= (bp_t *)my_malloc(sizeof(bp_t), MYF(MY_WME)); + + if (bp) + { + bp->lab= lab; + bp->instr= i; + (void)m_backpatch.push_front(bp); + } +} + +void +sp_head::backpatch(sp_label_t *lab) +{ + bp_t *bp; + uint dest= instructions(); + List_iterator_fast<bp_t> li(m_backpatch); + + while ((bp= li++)) + if (bp->lab == lab) + { + sp_instr_jump *i= static_cast<sp_instr_jump *>(bp->instr); + + i->set_destination(dest); + } +} + + +// ------------------------------------------------------------------ + +// +// sp_instr_stmt +// +int +sp_instr_stmt::execute(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_stmt::execute"); + DBUG_PRINT("info", ("command: %d", m_lex.sql_command)); + LEX olex; // The other lex + int res; + + memcpy(&olex, &thd->lex, sizeof(LEX)); // Save the other lex + + memcpy(&thd->lex, &m_lex, sizeof(LEX)); // Use my own lex + thd->lex.thd = thd; + + res= mysql_execute_command(thd); + if (thd->lock || thd->open_tables || thd->derived_tables) + { + thd->proc_info="closing tables"; + close_thread_tables(thd); /* Free tables */ + } + + memcpy(&thd->lex, &olex, sizeof(LEX)); // Restore the other lex + + *nextp = m_ip+1; + DBUG_RETURN(res); +} + +// +// sp_instr_set +// +int +sp_instr_set::execute(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_set::execute"); + DBUG_PRINT("info", ("offset: %u", m_offset)); + thd->spcont->set_item(m_offset, eval_func_item(thd, m_value, m_type)); + *nextp = m_ip+1; + DBUG_RETURN(0); +} + +// +// sp_instr_jump +// +int +sp_instr_jump::execute(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_jump::execute"); + DBUG_PRINT("info", ("destination: %u", m_dest)); + + *nextp= m_dest; + DBUG_RETURN(0); +} + +// +// sp_instr_jump_if +// +int +sp_instr_jump_if::execute(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_jump_if::execute"); + DBUG_PRINT("info", ("destination: %u", m_dest)); + Item *it= eval_func_item(thd, m_expr, MYSQL_TYPE_TINY); + + if (it->val_int()) + *nextp = m_dest; + else + *nextp = m_ip+1; + DBUG_RETURN(0); +} + +// +// sp_instr_jump_if_not +// +int +sp_instr_jump_if_not::execute(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_jump_if_not::execute"); + DBUG_PRINT("info", ("destination: %u", m_dest)); + Item *it= eval_func_item(thd, m_expr, MYSQL_TYPE_TINY); + + if (! it->val_int()) + *nextp = m_dest; + else + *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 new file mode 100644 index 00000000000..52cbdc56093 --- /dev/null +++ b/sql/sp_head.h @@ -0,0 +1,360 @@ +/* -*- C++ -*- */ +/* Copyright (C) 2002 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _SP_HEAD_H_ +#define _SP_HEAD_H_ + +#ifdef __GNUC__ +#pragma interface /* gcc class implementation */ +#endif + +#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 + +Item_result +sp_map_result_type(enum enum_field_types type); + +struct sp_label; + +class sp_instr; + +class sp_head : public Sql_alloc +{ + sp_head(const sp_head &); /* Prevent use of these */ + void operator=(sp_head &); + +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) + { + return (void*) sql_alloc((uint) size); + } + + static void operator delete(void *ptr, size_t size) + { + /* Empty */ + } + + sp_head(LEX_STRING *name, LEX *lex); + + int + create(THD *thd); + + int + 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) + { + insert_dynamic(&m_instr, (gptr)&i); + } + + inline uint + instructions() + { + return m_instr.elements; + } + + // Resets lex in 'thd' and keeps a copy of the old one. + void + reset_lex(THD *thd); + + // Restores lex in 'thd' from our copy, but keeps some status from the + // one in 'thd', like ptr, tables, fields, etc. + void + restore_lex(THD *thd); + + // Put the instruction on the backpatch list, associated with the label. + void + push_backpatch(sp_instr *, struct sp_label *); + + // Update all instruction with this label in the backpatch list to + // the current position. + 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(); + } + + inline Item_result result() + { + return sp_map_result_type(m_returns); + } + +private: + + Item_string *m_name; + Item_string *m_defstr; + sp_pcontext *m_pcont; // Parse context + LEX m_lex; // Temp. store for the other lex + DYNAMIC_ARRAY m_instr; // The "instructions" + typedef struct + { + struct sp_label *lab; + sp_instr *instr; + } bp_t; + List<bp_t> m_backpatch; // Instructions needing backpatching + + inline sp_instr * + get_instr(uint i) + { + sp_instr *in= NULL; + + if (i < m_instr.elements) + get_dynamic(&m_instr, (gptr)&in, i); + return in; + } + + int + execute(THD *thd); + +}; // class sp_head : public Sql_alloc + + +// +// "Instructions"... +// + +class sp_instr : public Sql_alloc +{ + sp_instr(const sp_instr &); /* Prevent use of these */ + void operator=(sp_instr &); + +public: + + // Should give each a name or type code for debugging purposes? + sp_instr(uint ip) + : Sql_alloc(), m_ip(ip) + {} + + virtual ~sp_instr() + {} + + // Execute this instrution. '*nextp' will be set to the index of the next + // instruction to execute. (For most instruction this will be the + // instruction following this one.) + // Returns 0 on success, non-zero if some error occured. + virtual int + execute(THD *thd, uint *nextp) + { // Default is a no-op. + *nextp = m_ip+1; // Next instruction + return 0; + } + +protected: + + uint m_ip; // My index + +}; // class sp_instr : public Sql_alloc + + +// +// Call out to some prepared SQL statement. +// +class sp_instr_stmt : public sp_instr +{ + sp_instr_stmt(const sp_instr_stmt &); /* Prevent use of these */ + void operator=(sp_instr_stmt &); + +public: + + sp_instr_stmt(uint ip) + : sp_instr(ip) + {} + + virtual ~sp_instr_stmt() + {} + + virtual int execute(THD *thd, uint *nextp); + + inline void + set_lex(LEX *lex) + { + memcpy(&m_lex, lex, sizeof(LEX)); + } + + inline LEX * + get_lex() + { + return &m_lex; + } + +private: + + LEX m_lex; // My own lex + +}; // class sp_instr_stmt : public sp_instr + + +class sp_instr_set : public sp_instr +{ + sp_instr_set(const sp_instr_set &); /* Prevent use of these */ + void operator=(sp_instr_set &); + +public: + + sp_instr_set(uint ip, uint offset, Item *val, enum enum_field_types type) + : sp_instr(ip), m_offset(offset), m_value(val), m_type(type) + {} + + virtual ~sp_instr_set() + {} + + virtual int execute(THD *thd, uint *nextp); + +private: + + uint m_offset; // Frame offset + Item *m_value; + enum enum_field_types m_type; // The declared type + +}; // class sp_instr_set : public sp_instr + + +class sp_instr_jump : public sp_instr +{ + sp_instr_jump(const sp_instr_jump &); /* Prevent use of these */ + void operator=(sp_instr_jump &); + +public: + + sp_instr_jump(uint ip) + : sp_instr(ip) + {} + + sp_instr_jump(uint ip, uint dest) + : sp_instr(ip), m_dest(dest) + {} + + virtual ~sp_instr_jump() + {} + + virtual int execute(THD *thd, uint *nextp); + + virtual void + set_destination(uint dest) + { + m_dest= dest; + } + +protected: + + int m_dest; // Where we will go + +}; // class sp_instr_jump : public sp_instr + + +class sp_instr_jump_if : public sp_instr_jump +{ + sp_instr_jump_if(const sp_instr_jump_if &); /* Prevent use of these */ + void operator=(sp_instr_jump_if &); + +public: + + sp_instr_jump_if(uint ip, Item *i) + : sp_instr_jump(ip), m_expr(i) + {} + + sp_instr_jump_if(uint ip, Item *i, uint dest) + : sp_instr_jump(ip, dest), m_expr(i) + {} + + virtual ~sp_instr_jump_if() + {} + + virtual int execute(THD *thd, uint *nextp); + +private: + + Item *m_expr; // The condition + +}; // class sp_instr_jump_if : public sp_instr_jump + + +class sp_instr_jump_if_not : public sp_instr_jump +{ + sp_instr_jump_if_not(const sp_instr_jump_if_not &); /* Prevent use of these */ + void operator=(sp_instr_jump_if_not &); + +public: + + sp_instr_jump_if_not(uint ip, Item *i) + : sp_instr_jump(ip), m_expr(i) + {} + + sp_instr_jump_if_not(uint ip, Item *i, uint dest) + : sp_instr_jump(ip, dest), m_expr(i) + {} + + virtual ~sp_instr_jump_if_not() + {} + + virtual int execute(THD *thd, uint *nextp); + +private: + + Item *m_expr; // The condition + +}; // 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_pcontext.cc b/sql/sp_pcontext.cc new file mode 100644 index 00000000000..94eb8df4b95 --- /dev/null +++ b/sql/sp_pcontext.cc @@ -0,0 +1,127 @@ +/* Copyright (C) 2002 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __GNUC__ +#pragma implementation +#endif + +#if defined(WIN32) || defined(__WIN__) +#undef SAFEMALLOC /* Problems with threads */ +#endif + +#include "mysql_priv.h" +#include "sp_pcontext.h" +#include "sp_head.h" + +sp_pcontext::sp_pcontext() + : m_params(0), m_framesize(0), m_i(0), m_genlab(0) +{ + m_pvar_size = 16; + m_pvar = (sp_pvar_t *)my_malloc(m_pvar_size * sizeof(sp_pvar_t), MYF(MY_WME)); + if (m_pvar) + memset(m_pvar, 0, m_pvar_size * sizeof(sp_pvar_t)); + m_label.empty(); +} + +void +sp_pcontext::grow() +{ + uint sz = m_pvar_size + 8; + sp_pvar_t *a = (sp_pvar_t *)my_realloc((char *)m_pvar, + sz * sizeof(sp_pvar_t), + MYF(MY_WME | MY_ALLOW_ZERO_PTR)); + + if (a) + { + m_pvar_size = sz; + m_pvar = a; + } +} + +/* This does a linear search (from newer to older variables, in case +** we have shadowed names). +** It's possible to have a more efficient allocation and search method, +** but it might not be worth it. The typical number of parameters and +** variables will in most cases be low (a handfull). +** And this is only called during parsing. +*/ +sp_pvar_t * +sp_pcontext::find_pvar(LEX_STRING *name) +{ + uint i = m_i; + + while (i-- > 0) + { + uint len= (m_pvar[i].name.length > name->length ? + m_pvar[i].name.length : name->length); + + if (my_strncasecmp(system_charset_info, + name->str, + m_pvar[i].name.str, + len) == 0) + { + return m_pvar + i; + } + } + return NULL; +} + +void +sp_pcontext::push(LEX_STRING *name, enum enum_field_types type, + sp_param_mode_t mode) +{ + if (m_i >= m_pvar_size) + grow(); + if (m_i < m_pvar_size) + { + if (m_i == m_framesize) + m_framesize += 1; + m_pvar[m_i].name.str= name->str; + m_pvar[m_i].name.length= name->length, + m_pvar[m_i].type= type; + m_pvar[m_i].mode= mode; + m_pvar[m_i].offset= m_i; + m_pvar[m_i].isset= (mode == sp_param_out ? FALSE : TRUE); + m_i += 1; + } +} + +sp_label_t * +sp_pcontext::push_label(char *name, uint ip) +{ + sp_label_t *lab = (sp_label_t *)my_malloc(sizeof(sp_label_t), MYF(MY_WME)); + + if (lab) + { + lab->name= name; + lab->ip= ip; + m_label.push_front(lab); + } + return lab; +} + +sp_label_t * +sp_pcontext::find_label(char *name) +{ + List_iterator_fast<sp_label_t> li(m_label); + sp_label_t *lab; + + while ((lab= li++)) + if (strcasecmp(name, lab->name) == 0) + return lab; + + return NULL; +} diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h new file mode 100644 index 00000000000..c5b0f1d410b --- /dev/null +++ b/sql/sp_pcontext.h @@ -0,0 +1,155 @@ +/* -*- C++ -*- */ +/* Copyright (C) 2002 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _SP_PCONTEXT_H_ +#define _SP_PCONTEXT_H_ + +#ifdef __GNUC__ +#pragma interface /* gcc class implementation */ +#endif + +typedef enum +{ + sp_param_in, + sp_param_out, + sp_param_inout +} sp_param_mode_t; + +typedef struct +{ + LEX_STRING name; + enum enum_field_types type; + sp_param_mode_t mode; + uint offset; // Offset in current frame + my_bool isset; +} sp_pvar_t; + +typedef struct sp_label +{ + char *name; + uint ip; // Instruction index +} sp_label_t; + +class sp_pcontext : public Sql_alloc +{ + sp_pcontext(const sp_pcontext &); /* Prevent use of these */ + void operator=(sp_pcontext &); + + public: + + sp_pcontext(); + + inline uint + max_framesize() + { + return m_framesize; + } + + inline uint + current_framesize() + { + return m_i; + } + + inline uint + params() + { + return m_params; + } + + // Set the number of parameters to the current esize + inline void + set_params() + { + m_params= m_i; + } + + inline void + set_type(uint i, enum enum_field_types type) + { + if (i < m_i) + m_pvar[i].type= type; + } + + inline void + set_isset(uint i, my_bool val) + { + if (i < m_i) + m_pvar[i].isset= val; + } + + void + push(LEX_STRING *name, enum enum_field_types type, sp_param_mode_t mode); + + // Pop the last 'num' slots of the frame + inline void + pop(uint num = 1) + { + if (num < m_i) + m_i -= num; + } + + // Find by name + sp_pvar_t * + find_pvar(LEX_STRING *name); + + // Find by index + sp_pvar_t * + find_pvar(uint i) + { + if (i >= m_i) + return NULL; + return m_pvar+i; + } + + sp_label_t * + push_label(char *name, uint ip); + + sp_label_t * + find_label(char *name); + + inline sp_label_t * + last_label() + { + return m_label.head(); + } + + inline sp_label_t * + pop_label() + { + return m_label.pop(); + } + +private: + + uint m_params; // The number of parameters + uint m_framesize; // The maximum framesize + uint m_i; // The current index (during parsing) + + sp_pvar_t *m_pvar; + uint m_pvar_size; // Current size of m_pvar. + + void + grow(); + + List<sp_label_t> m_label; // The label list + uint m_genlab; // Gen. label counter + +}; // class sp_pcontext : public Sql_alloc + + +#endif /* _SP_PCONTEXT_H_ */ diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h new file mode 100644 index 00000000000..78485fdd090 --- /dev/null +++ b/sql/sp_rcontext.h @@ -0,0 +1,95 @@ +/* -*- C++ -*- */ +/* Copyright (C) 2002 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _SP_RCONTEXT_H_ +#define _SP_RCONTEXT_H_ + +class sp_rcontext : public Sql_alloc +{ + sp_rcontext(const sp_rcontext &); /* Prevent use of these */ + void operator=(sp_rcontext &); + + public: + + sp_rcontext(uint 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)); + } + + ~sp_rcontext() + { + // Not needed? + //sql_element_free(m_frame); + } + + inline void + push_item(Item *i) + { + if (m_count < m_size) + m_frame[m_count++] = i; + } + + inline void + set_item(uint idx, Item *i) + { + if (idx < m_count) + m_frame[idx] = i; + } + + inline Item * + get_item(uint idx) + { + return m_frame[idx]; + } + + inline void + set_oindex(uint idx, int oidx) + { + m_outs[idx] = oidx; + } + + inline int + get_oindex(uint idx) + { + 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 + +#endif /* _SP_RCONTEXT_H_ */ diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 3a66e906837..d5197b65071 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -35,6 +35,7 @@ #include <io.h> #endif #include <mysys_err.h> +#include <sp_rcontext.h> /***************************************************************************** @@ -80,7 +81,7 @@ extern "C" void free_user_var(user_var_entry *entry) THD::THD():user_time(0), is_fatal_error(0), last_insert_id_used(0), insert_id_used(0), rand_used(0), in_lock_tables(0), - global_read_lock(0), bootstrap(0) + global_read_lock(0), bootstrap(0), spcont(NULL) { host=user=priv_user=db=query=ip=0; host_or_ip= "connecting host"; @@ -1002,9 +1003,12 @@ bool select_exists_subselect::send_data(List<Item> &items) int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u) { List_iterator_fast<Item> li(list); - List_iterator_fast<LEX_STRING> gl(var_list); + List_iterator_fast<my_var> gl(var_list); Item *item; + my_var *mv; LEX_STRING *ls; + + row_count= 0; if (var_list.elements != list.elements) { my_error(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, MYF(0)); @@ -1013,19 +1017,39 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u) unit=u; while ((item=li++)) { - ls= gl++; - Item_func_set_user_var *xx = new Item_func_set_user_var(*ls,item); - xx->fix_fields(thd,(TABLE_LIST*) thd->lex.select_lex.table_list.first,&item); - xx->fix_length_and_dec(); - vars.push_back(xx); + mv=gl++; + ls= &mv->s; + if (mv->local) + { + (void)local_vars.push_back(new Item_splocal(mv->offset)); + } + else + { + Item_func_set_user_var *xx = new Item_func_set_user_var(*ls,item); + xx->fix_fields(thd,(TABLE_LIST*) thd->lex.select_lex.table_list.first,&item); + xx->fix_length_and_dec(); + vars.push_back(xx); + } } return 0; } + bool select_dumpvar::send_data(List<Item> &items) { List_iterator_fast<Item_func_set_user_var> li(vars); + List_iterator_fast<Item_splocal> var_li(local_vars); + List_iterator_fast<my_var> my_li(var_list); + List_iterator_fast<Item> it(items); Item_func_set_user_var *xx; + Item_splocal *yy; + Item *item; + my_var *zz; DBUG_ENTER("send_data"); + if (unit->offset_limit_cnt) + { // using limit offset,count + unit->offset_limit_cnt--; + DBUG_RETURN(0); + } if (unit->offset_limit_cnt) { // Using limit offset,count @@ -1037,8 +1061,21 @@ bool select_dumpvar::send_data(List<Item> &items) my_error(ER_TOO_MANY_ROWS, MYF(0)); DBUG_RETURN(1); } - while ((xx=li++)) - xx->update(); + while ((zz=my_li++) && (item=it++)) + { + if (zz->local) + { + if ((yy=var_li++)) + { + thd->spcont->set_item(yy->get_offset(), item); + } + } + else + { + if ((xx=li++)) + xx->update(); + } + } DBUG_RETURN(0); } diff --git a/sql/sql_class.h b/sql/sql_class.h index 8284a2b1ea3..59bef416ece 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -26,6 +26,7 @@ class Query_log_event; class Load_log_event; class Slave_log_event; +class sp_rcontext; enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY }; @@ -526,6 +527,8 @@ public: bool volatile killed; 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 @@ -999,13 +1002,22 @@ public: bool send_eof(); }; +class my_var : public Sql_alloc { +public: + LEX_STRING s; + bool local; + uint offset; + my_var (LEX_STRING& j, bool i, uint o) : s(j), local(i), offset(o) {} + ~my_var() {} +}; class select_dumpvar :public select_result { ha_rows row_count; public: - List<LEX_STRING> var_list; + List<my_var> var_list; List<Item_func_set_user_var> vars; - select_dumpvar(void) { var_list.empty(); vars.empty(); row_count=0;} + List<Item_splocal> local_vars; + select_dumpvar(void) { var_list.empty(); local_vars.empty(); vars.empty(); row_count=0;} ~select_dumpvar() {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_fields(List<Item> &list, uint flag) {return 0;} diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index a5bed692293..028dd02463c 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}; @@ -109,6 +111,7 @@ LEX *lex_start(THD *thd, uchar *buf,uint length) LEX *lex= &thd->lex; lex->thd= thd; lex->next_state=MY_LEX_START; + lex->buf= buf; lex->end_of_query=(lex->ptr=buf)+length; lex->yylineno = 1; lex->select_lex.create_refs=lex->in_comment=0; @@ -122,6 +125,9 @@ LEX *lex_start(THD *thd, uchar *buf,uint length) lex->yacc_yyss=lex->yacc_yyvs=0; lex->ignore_space=test(thd->variables.sql_mode & MODE_IGNORE_SPACE); lex->sql_command=SQLCOM_END; + lex->sphead= NULL; + lex->spcont= NULL; + lex->spfuns.empty(); return lex; } @@ -145,6 +151,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_lex.h b/sql/sql_lex.h index 6abcd5e8fea..32bb0633ae6 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -21,6 +21,9 @@ class Table_ident; class sql_exchange; class LEX_COLUMN; +class sp_head; +class sp_instr; +class sp_pcontext; /* The following hack is needed because mysql_yacc.cc does not define @@ -72,6 +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_CREATE_SPFUNCTION, SQLCOM_CALL, + SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION, /* This should be the last !!! */ SQLCOM_END @@ -419,6 +424,7 @@ typedef struct st_lex SELECT_LEX_NODE *current_select; /* list of all SELECT_LEX */ SELECT_LEX *all_selects_list; + uchar *buf; /* The beginning of string, used by SPs */ uchar *ptr,*tok_start,*tok_end,*end_of_query; char *length,*dec,*change,*name; char *backup_dir; /* For RESTORE/BACKUP */ @@ -478,6 +484,9 @@ typedef struct st_lex uint slave_thd_opt; CHARSET_INFO *charset; 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 3169d2235f5..f22e00a31bc 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -26,6 +26,9 @@ #include "ha_innodb.h" #endif +#include "sp_head.h" +#include "sp.h" + #ifdef HAVE_OPENSSL /* Without SSL the handshake consists of one packet. This packet @@ -44,6 +47,15 @@ #define MIN_HANDSHAKE_SIZE 6 #endif /* HAVE_OPENSSL */ +/* Used in error handling only */ +#define SP_TYPE_STRING(LP) \ + ((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE") +#define SP_COM_STRING(LP) \ + ((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \ + (LP)->sql_command == SQLCOM_ALTER_FUNCTION || \ + (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \ + "FUNCTION" : "PROCEDURE") + extern int yyparse(void *thd); extern "C" pthread_mutex_t THR_LOCK_keycache; #ifdef SOLARIS @@ -1565,7 +1577,7 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length) ** Execute command saved in thd and current_lex->sql_command ****************************************************************************/ -void +int mysql_execute_command(THD *thd) { int res= 0; @@ -1576,6 +1588,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_SPFUNCTION) + { + 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 @@ -1597,7 +1621,7 @@ mysql_execute_command(THD *thd) given and the table list says the query should not be replicated */ if (table_rules_on && tables && !tables_ok(thd,tables)) - DBUG_VOID_RETURN; + DBUG_RETURN(0); #ifndef TO_BE_DELETED /* This is a workaround to deal with the shortcoming in 3.23.44-3.23.46 @@ -1632,7 +1656,7 @@ mysql_execute_command(THD *thd) { if (res < 0 || thd->net.report_error) send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0); - DBUG_VOID_RETURN; + DBUG_RETURN(res); } } } @@ -1645,7 +1669,7 @@ mysql_execute_command(THD *thd) !tables_ok(thd,tables)) #endif ) - DBUG_VOID_RETURN; + DBUG_RETURN(0); statistic_increment(com_stat[lex->sql_command],&LOCK_status); switch (lex->sql_command) { @@ -1684,7 +1708,7 @@ mysql_execute_command(THD *thd) if (!(result= new select_send())) { send_error(thd, ER_OUT_OF_RESOURCES); - DBUG_VOID_RETURN; + goto error; } else thd->send_explain_fields(result); @@ -1947,7 +1971,7 @@ mysql_execute_command(THD *thd) find_real_table_in_list(tables->next, tables->db, tables->real_name)) { net_printf(thd,ER_UPDATE_TABLE_USED,tables->real_name); - DBUG_VOID_RETURN; + DBUG_RETURN(-1); } if (tables->next) { @@ -2029,7 +2053,7 @@ mysql_execute_command(THD *thd) if (thd->locked_tables || thd->active_transaction()) { send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION); - break; + goto error; } { LOCK_ACTIVE_MI; @@ -2042,7 +2066,7 @@ mysql_execute_command(THD *thd) case SQLCOM_ALTER_TABLE: #if defined(DONT_ALLOW_SHOW_COMMANDS) send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - break; + goto error; #else { ulong priv=0; @@ -2134,7 +2158,7 @@ mysql_execute_command(THD *thd) case SQLCOM_SHOW_BINLOGS: #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - DBUG_VOID_RETURN; + goto error; #else { if (check_global_access(thd, SUPER_ACL)) @@ -2147,7 +2171,7 @@ mysql_execute_command(THD *thd) case SQLCOM_SHOW_CREATE: #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - DBUG_VOID_RETURN; + goto error; #else { if (check_db_used(thd, tables) || @@ -2225,7 +2249,7 @@ mysql_execute_command(THD *thd) if (select_lex->item_list.elements != lex->value_list.elements) { send_error(thd,ER_WRONG_VALUE_COUNT); - DBUG_VOID_RETURN; + goto error; } res= mysql_update(thd,tables, select_lex->item_list, @@ -2246,7 +2270,7 @@ mysql_execute_command(THD *thd) if (select_lex->item_list.elements != lex->value_list.elements) { send_error(thd,ER_WRONG_VALUE_COUNT); - DBUG_VOID_RETURN; + goto error; } { const char *msg= 0; @@ -2282,7 +2306,7 @@ mysql_execute_command(THD *thd) if (select_lex->item_list.elements != lex->value_list.elements) { send_error(thd,ER_WRONG_VALUE_COUNT); - DBUG_VOID_RETURN; + goto error; } res = mysql_insert(thd,tables,lex->field_list,lex->many_values, select_lex->item_list, lex->value_list, @@ -2323,8 +2347,7 @@ mysql_execute_command(THD *thd) if (find_real_table_in_list(tables->next, tables->db, tables->real_name)) { - net_printf(thd,ER_UPDATE_TABLE_USED,tables->real_name); - DBUG_VOID_RETURN; + lex->select_lex.options |= OPTION_BUFFER_RESULT; } /* Skip first table, which is the table we are inserting in */ @@ -2486,7 +2509,7 @@ mysql_execute_command(THD *thd) case SQLCOM_SHOW_DATABASES: #if defined(DONT_ALLOW_SHOW_COMMANDS) send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - DBUG_VOID_RETURN; + goto error; #else if ((specialflag & SPECIAL_SKIP_SHOW_DB) && check_global_access(thd, SHOW_DB_ACL)) @@ -2520,7 +2543,7 @@ mysql_execute_command(THD *thd) case SQLCOM_SHOW_LOGS: #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - DBUG_VOID_RETURN; + goto error; #else { if (grant_option && check_access(thd, FILE_ACL, any_db)) @@ -2533,7 +2556,7 @@ mysql_execute_command(THD *thd) /* FALL THROUGH */ #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - DBUG_VOID_RETURN; + goto error; #else { char *db=select_lex->db ? select_lex->db : thd->db; @@ -2572,7 +2595,7 @@ mysql_execute_command(THD *thd) case SQLCOM_SHOW_FIELDS: #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - DBUG_VOID_RETURN; + goto error; #else { char *db=tables->db; @@ -2597,7 +2620,7 @@ mysql_execute_command(THD *thd) case SQLCOM_SHOW_KEYS: #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - DBUG_VOID_RETURN; + goto error; #else { char *db=tables->db; @@ -2779,26 +2802,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: { @@ -2967,16 +2988,162 @@ 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_SPFUNCTION: + if (!lex->sphead) + { + res= -1; // Shouldn't happen + break; + } + else + { + uint namelen; + char *name= lex->sphead->name(&namelen); +#ifdef HAVE_DLOPEN + if (lex->sphead->m_type == TYPE_ENUM_FUNCTION) + { + udf_func *udf = find_udf(name, namelen); + + if (udf) + { + net_printf(thd, ER_UDF_EXISTS, name); + goto error; + } + } +#endif + res= lex->sphead->create(thd); + switch (res) + { + case SP_OK: + send_ok(thd); + break; + case SP_WRITE_ROW_FAILED: + net_printf(thd, ER_SP_ALREADY_EXISTS, SP_TYPE_STRING(lex), name); + goto error; + default: + net_printf(thd, ER_SP_STORE_FAILED, SP_TYPE_STRING(lex), name); + goto error; + } + } + break; + case SQLCOM_CALL: + { + sp_head *sp; + + sp= sp_find_procedure(thd, &lex->udf.name); + if (! sp) + { + net_printf(thd, ER_SP_DOES_NOT_EXIST, "PROCEDURE", lex->udf.name); + goto error; + } + else + { +#ifndef EMBEDDED_LIBRARY + // When executing substatements, they're assumed to send_error when + // it happens, but not to send_ok. + my_bool nsok= thd->net.no_send_ok; + + thd->net.no_send_ok= TRUE; +#endif + res= sp->execute_procedure(thd, &lex->value_list); +#ifndef EMBEDDED_LIBRARY + thd->net.no_send_ok= nsok; +#endif + + if (res == 0) + send_ok(thd); + else + goto error; // Substatement should already have sent error + } + } + break; + case SQLCOM_ALTER_PROCEDURE: + case SQLCOM_ALTER_FUNCTION: + { + sp_head *sp; + + 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) + { + net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),lex->udf.name); + goto error; + } + else + { + /* QQ This is an no-op right now, since we haven't + put the characteristics in yet. */ + send_ok(thd); + } + } + break; + case SQLCOM_DROP_PROCEDURE: + case SQLCOM_DROP_FUNCTION: + { + if (lex->sql_command == SQLCOM_DROP_PROCEDURE) + res= sp_drop_procedure(thd, lex->udf.name.str, lex->udf.name.length); + else + { + res= sp_drop_function(thd, lex->udf.name.str, lex->udf.name.length); +#ifdef HAVE_DLOPEN + if (res == SP_KEY_NOT_FOUND) + { + 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: + if (lex->drop_if_exists) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), + SP_COM_STRING(lex), lex->udf.name.str); + res= 0; + send_ok(thd); + break; + } + net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex), + lex->udf.name.str); + goto error; + default: + net_printf(thd, ER_SP_DROP_FAILED, SP_COM_STRING(lex), + lex->udf.name.str); + goto error; + } + } + break; default: /* Impossible */ send_ok(thd); break; } thd->proc_info="query end"; // QQ + + // We end up here if res == 0 and send_ok() has been done, + // or res != 0 and no send_error() has yet been done. if (res < 0) send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0); + DBUG_RETURN(res); error: - DBUG_VOID_RETURN; + // We end up here if send_error() has already been done. + DBUG_RETURN(-1); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0126b900b9a..5148a362eab 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -191,7 +191,8 @@ int handle_select(THD *thd, LEX *lex, select_result *result) send_error(thd, 0, NullS); res= 1; } - delete result; + if (result != lex->result) + delete result; return res; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 1d9a3f2df10..8aecb62f51c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -35,6 +35,9 @@ #include "sql_acl.h" #include "lex_symbol.h" #include "item_create.h" +#include "sp_head.h" +#include "sp_pcontext.h" +#include "sp.h" #include <myisam.h> #include <myisammrg.h> @@ -121,6 +124,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token AVG_SYM %token BEGIN_SYM %token BINLOG_SYM +%token CALL_SYM %token CHANGE %token CLIENT_SYM %token COMMENT_SYM @@ -202,6 +206,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token CONVERT_SYM %token DATABASES %token DATA_SYM +%token DECLARE_SYM %token DEFAULT %token DELAYED_SYM %token DELAY_KEY_WRITE_SYM @@ -247,6 +252,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token INFILE %token INNER_SYM %token INNOBASE_SYM +%token INOUT_SYM %token INTO %token IN_SYM %token ISOLATION @@ -261,6 +267,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token LIKE %token LINES %token LOCAL_SYM +%token LOCATOR_SYM %token LOG_SYM %token LOGS_SYM %token LONG_NUM @@ -304,6 +311,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token OR %token OR_OR_CONCAT %token ORDER_SYM +%token OUT_SYM %token OUTER %token OUTFILE %token DUMPFILE @@ -342,6 +350,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token SIMPLE_SYM %token SHUTDOWN %token SPATIAL_SYM +%token SPECIFIC_SYM %token SSL_SYM %token STARTING %token STATUS_SYM @@ -362,9 +371,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 @@ -502,6 +512,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 @@ -531,6 +542,18 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token SQL_SMALL_RESULT %token SQL_BUFFER_RESULT +%token CURSOR_SYM +%token ELSEIF_SYM +%token ITERATE_SYM +%token LEAVE_SYM +%token LOOP_SYM +%token REPEAT_SYM +%token UNTIL_SYM +%token WHILE_SYM +%token ASENSITIVE_SYM +%token INSENSITIVE_SYM +%token SENSITIVE_SYM + %token ISSUER_SYM %token SUBJECT_SYM %token CIPHER_SYM @@ -563,6 +586,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); ULONGLONG_NUM field_ident select_alias ident ident_or_text UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_db NCHAR_STRING + SP_FUNC ident_or_spfunc %type <lex_str_ptr> opt_table_alias @@ -682,8 +706,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); union_clause union_list union_option precision subselect_start opt_and charset subselect_end select_var_list select_var_list_init help opt_len + statement END_OF_INPUT +%type <NONE> call sp_proc_stmts sp_proc_stmt +%type <num> sp_decls sp_decl sp_decl_idents sp_opt_inout + %type <NONE> '-' '+' '*' '/' '%' '(' ')' ',' '!' '{' '}' '&' '|' AND OR OR_OR_CONCAT BETWEEN_SYM CASE_SYM @@ -709,10 +737,16 @@ query: | verb_clause END_OF_INPUT {}; verb_clause: + statement + | begin + ; + +/* Verb clauses, except begin */ +statement: alter | analyze | backup - | begin + | call | change | check | commit @@ -881,20 +915,509 @@ create: lex->name=$4.str; lex->create_info.options=$3; } - | CREATE udf_func_type UDF_SYM IDENT_sys + | CREATE udf_func_type FUNCTION_SYM ident_or_spfunc { 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_sys + create_function_tail + {} + | CREATE PROCEDURE ident + { + LEX *lex= Lex; + + if (lex->sphead) + { + net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "PROCEDURE"); + YYABORT; + } + lex->spcont= new sp_pcontext(); + lex->sphead= new sp_head(&$3, lex); + lex->sphead->m_type= TYPE_ENUM_PROCEDURE; + } + '(' sp_pdparam_list ')' + { + Lex->spcont->set_params(); + } + sp_proc_stmt + { + Lex->sql_command= SQLCOM_CREATE_PROCEDURE; + } + ; + +ident_or_spfunc: + IDENT_sys { $$= $1; } + | SP_FUNC { $$= $1; } + ; + +create_function_tail: + RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys { LEX *lex=Lex; - lex->udf.returns=(Item_result) $7; - lex->udf.dl=$9.str; + lex->sql_command = SQLCOM_CREATE_FUNCTION; + lex->udf.returns=(Item_result) $2; + lex->udf.dl=$4.str; } - ; + | '(' + { + LEX *lex= Lex; + + if (lex->sphead) + { + net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "FUNCTION"); + YYABORT; + } + 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 + { + Lex->sql_command = SQLCOM_CREATE_SPFUNCTION; + } + ; + +call: + CALL_SYM ident_or_spfunc + { + LEX *lex = Lex; + + lex->sql_command= SQLCOM_CALL; + lex->udf.name= $2; + lex->value_list.empty(); + } + '(' sp_cparam_list ')' {} + ; + +/* CALL parameters */ +sp_cparam_list: + /* Empty */ + | sp_cparams + ; + +sp_cparams: + sp_cparams ',' expr + { + Lex->value_list.push_back($3); + } + | expr + { + Lex->value_list.push_back($1); + } + ; + +/* Stored FUNCTION parameter declaration list */ +sp_fdparam_list: + /* Empty */ + | sp_fdparams + ; + +sp_fdparams: + sp_fdparams ',' sp_fdparam + | sp_fdparam + ; + +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, + (enum enum_field_types)$3, + (sp_param_mode_t)$1); + } + ; + +sp_opt_inout: + /* Empty */ { $$= sp_param_in; } + | IN_SYM { $$= sp_param_in; } + | OUT_SYM { $$= sp_param_out; } + | INOUT_SYM { $$= sp_param_inout; } + ; + +sp_opt_locator: + /* Empty */ + | AS LOCATOR_SYM + ; + +sp_proc_stmts: + sp_proc_stmt ';' + | sp_proc_stmts sp_proc_stmt ';' + ; + +sp_decls: + /* Empty */ + { + $$= 0; + } + | sp_decls sp_decl ';' + { + $$= $1 + $2; + } + ; + +sp_decl: + DECLARE_SYM sp_decl_idents type + { + LEX *lex= Lex; + uint max= lex->spcont->current_framesize(); + + for (uint i = max-$2 ; i < max ; i++) + { + lex->spcont->set_type(i, (enum enum_field_types)$3); + lex->spcont->set_isset(i, FALSE); + } + $$= $2; + } + ; + +sp_decl_idents: + ident + { + Lex->spcont->push(&$1, (enum_field_types)0, sp_param_in); + $$= 1; + } + | sp_decl_idents ',' ident + { + Lex->spcont->push(&$3, (enum_field_types)0, sp_param_in); + $$= $1 + 1; + } + ; + +sp_proc_stmt: + { + Lex->sphead->reset_lex(YYTHD); + } + statement + { + LEX *lex= Lex; + + if (lex->sql_command == SQLCOM_SELECT && !lex->result) + { + send_error(YYTHD, ER_SP_BADSELECT); + YYABORT; + } + else + { + /* Don't add an instruction for empty SET statements. + ** (This happens if the SET only contained local variables, + ** which get their set instructions generated separately.) + */ + if (lex->sql_command != SQLCOM_SET_OPTION || + !lex->var_list.is_empty()) + { + /* Currently we can't handle queries inside a FUNCTION, + ** because of the way table locking works. + ** This is unfortunate, and limits the usefulness of functions + ** a great deal, but it's nothing we can do about this at the + ** moment. + */ + if (lex->sphead->m_type == TYPE_ENUM_FUNCTION && + lex->sql_command != SQLCOM_SET_OPTION) + { + send_error(YYTHD, ER_SP_BADQUERY); + YYABORT; + } + else + { + sp_instr_stmt *i= new sp_instr_stmt(lex->sphead->instructions()); + + i->set_lex(lex); + lex->sphead->add_instr(i); + } + } + 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 + { + 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 {} + | CASE_SYM WHEN_SYM + { + Lex->sphead->m_simple_case= FALSE; + } + sp_case END CASE_SYM {} + | CASE_SYM expr WHEN_SYM + { + /* We "fake" this by using an anonymous variable which we + set to the expression. Note that all WHENs are evaluate + at the same frame level, so we then know that it's the + top-most variable in the frame. */ + LEX *lex= Lex; + uint offset= lex->spcont->current_framesize(); + sp_instr_set *i = new sp_instr_set(lex->sphead->instructions(), + offset, $2, MYSQL_TYPE_STRING); + LEX_STRING dummy; + + dummy.str= (char *)""; + dummy.length= 0; + lex->spcont->push(&dummy, MYSQL_TYPE_STRING, sp_param_in); + lex->sphead->add_instr(i); + lex->sphead->m_simple_case= TRUE; + } + sp_case END CASE_SYM + { + Lex->spcont->pop(); + } + | sp_labeled_control + {} + | { /* Unlabeled controls get a secret label. */ + LEX *lex= Lex; + + lex->spcont->push_label((char *)"", lex->sphead->instructions()); + } + sp_unlabeled_control + { + LEX *lex= Lex; + + lex->sphead->backpatch(lex->spcont->pop_label()); + } + | LEAVE_SYM IDENT + { + LEX *lex= Lex; + sp_head *sp = lex->sphead; + sp_label_t *lab= lex->spcont->find_label($2.str); + + if (! lab) + { + net_printf(YYTHD, ER_SP_LILABEL_MISMATCH, "LEAVE", $2.str); + YYABORT; + } + else + { + sp_instr_jump *i= new sp_instr_jump(sp->instructions()); + + sp->push_backpatch(i, lab); /* Jumping forward */ + sp->add_instr(i); + } + } + | ITERATE_SYM IDENT + { + LEX *lex= Lex; + sp_label_t *lab= lex->spcont->find_label($2.str); + + if (! lab) + { + net_printf(YYTHD, ER_SP_LILABEL_MISMATCH, "ITERATE", $2.str); + YYABORT; + } + else + { + uint ip= lex->sphead->instructions(); + sp_instr_jump *i= new sp_instr_jump(ip, lab->ip); /* Jump back */ + + lex->sphead->add_instr(i); + } + } + ; + +sp_if: + expr THEN_SYM + { + sp_head *sp= Lex->sphead; + sp_pcontext *ctx= Lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, $1); + + sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + sp->add_instr(i); + } + sp_proc_stmts + { + sp_head *sp= Lex->sphead; + sp_pcontext *ctx= Lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump *i = new sp_instr_jump(ip); + + sp->add_instr(i); + sp->backpatch(ctx->pop_label()); + sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + } + sp_elseifs + { + LEX *lex= Lex; + + lex->sphead->backpatch(lex->spcont->pop_label()); + } + ; + +sp_elseifs: + /* Empty */ + | ELSEIF_SYM sp_if + | ELSE sp_proc_stmts + ; + +sp_case: + expr THEN_SYM + { + sp_head *sp= Lex->sphead; + sp_pcontext *ctx= Lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump_if_not *i; + + if (! sp->m_simple_case) + i= new sp_instr_jump_if_not(ip, $1); + else + { /* Simple case: <caseval> = <whenval> */ + Item *var= (Item*) new Item_splocal(ctx->current_framesize()-1); + Item *expr= Item_bool_func2::eq_creator(var, $1); + + i= new sp_instr_jump_if_not(ip, expr); + } + sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + sp->add_instr(i); + } + sp_proc_stmts + { + sp_head *sp= Lex->sphead; + sp_pcontext *ctx= Lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump *i = new sp_instr_jump(ip); + + sp->add_instr(i); + sp->backpatch(ctx->pop_label()); + sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + } + sp_whens + { + LEX *lex= Lex; + + lex->sphead->backpatch(lex->spcont->pop_label()); + } + ; + +sp_whens: + /* Empty */ {} + | WHEN_SYM sp_case {} + | ELSE sp_proc_stmts {} + ; + +sp_labeled_control: + IDENT ':' + { + LEX *lex= Lex; + sp_label_t *lab= lex->spcont->find_label($1.str); + + if (lab) + { + net_printf(YYTHD, ER_SP_LABEL_REDEFINE, $1.str); + YYABORT; + } + else + { + lex->spcont->push_label($1.str, + lex->sphead->instructions()); + } + } + sp_unlabeled_control IDENT + { + LEX *lex= Lex; + sp_label_t *lab= lex->spcont->find_label($5.str); + + if (! lab || strcasecmp($5.str, lab->name) != 0) + { + net_printf(YYTHD, ER_SP_LABEL_MISMATCH, $5.str); + YYABORT; + } + else + { + lex->spcont->pop_label(); + lex->sphead->backpatch(lab); + } + } + ; + +sp_unlabeled_control: + BEGIN_SYM + sp_decls + sp_proc_stmts + END + { /* QQ This is just a dummy for grouping declarations and statements + together. No [[NOT] ATOMIC] yet, and we need to figure out how + make it coexist with the existing BEGIN COMMIT/ROLLBACK. */ + Lex->spcont->pop($2); + } + | LOOP_SYM + sp_proc_stmts END LOOP_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump *i = new sp_instr_jump(ip, lab->ip); + + lex->sphead->add_instr(i); + } + | WHILE_SYM expr DO_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint ip= sp->instructions(); + sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, $2); + + /* Jumping forward */ + sp->push_backpatch(i, lex->spcont->last_label()); + sp->add_instr(i); + } + sp_proc_stmts END WHILE_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump *i = new sp_instr_jump(ip, lab->ip); + + lex->sphead->add_instr(i); + } + | REPEAT_SYM sp_proc_stmts UNTIL_SYM expr END REPEAT_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, $4, lab->ip); + + lex->sphead->add_instr(i); + } + ; create2: '(' field_list ')' opt_create_table_options create3 {} @@ -1524,8 +2047,30 @@ alter: LEX *lex=Lex; lex->sql_command=SQLCOM_ALTER_DB; lex->name=$3.str; - }; + } + | ALTER PROCEDURE ident + /* QQ Characteristics missing for now */ + opt_restrict + { + LEX *lex=Lex; + + /* This is essensially an no-op right now, since we haven't + put the characteristics in yet. */ + lex->sql_command= SQLCOM_ALTER_PROCEDURE; + lex->udf.name= $3; + } + | ALTER FUNCTION_SYM ident + /* QQ Characteristics missing for now */ + opt_restrict + { + LEX *lex=Lex; + /* This is essensially an no-op right now, since we haven't + put the characteristics in yet. */ + lex->sql_command= SQLCOM_ALTER_FUNCTION; + lex->udf.name= $3; + } + ; alter_list: | alter_list_item @@ -2213,6 +2758,8 @@ simple_expr: { $$= ((Item*(*)(Item*,Item*))($1.symbol->create_func))($3,$5);} | FUNC_ARG3 '(' expr ',' expr ',' expr ')' { $$= ((Item*(*)(Item*,Item*,Item*))($1.symbol->create_func))($3,$5,$7);} + | REPEAT_SYM '(' expr ',' expr ')' + { $$= new Item_func_repeat($3,$5); } | ATAN '(' expr ')' { $$= new Item_func_atan($3); } | ATAN '(' expr ',' expr ')' @@ -2450,6 +2997,14 @@ simple_expr: { $$= new Item_func_round($3,$5,1); } | TRUE_SYM { $$= 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 + $$= new Item_func_sp($1); + } | UDA_CHAR_SUM '(' udf_expr_list ')' { if ($3 != NULL) @@ -3077,11 +3632,33 @@ select_var_list: | select_var_ident {} ; -select_var_ident: '@' ident_or_text +select_var_ident: + '@' ident_or_text + { + LEX *lex=Lex; + if (lex->result) + ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($2,0,0)); + else + YYABORT; + } + | ident_or_text { LEX *lex=Lex; - if (lex->result && ((select_dumpvar *)lex->result)->var_list.push_back((LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING)))) + if (!lex->spcont) + YYABORT; + sp_pvar_t *t; + if (!(t=lex->spcont->find_pvar(&$1))) + { + send_error(lex->thd, ER_SYNTAX_ERROR); YYABORT; + } + if (! lex->result) + YYABORT; + else + { + ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($1,1,t->offset)); + t->isset= TRUE; + } } ; @@ -3160,13 +3737,21 @@ drop: lex->drop_if_exists=$3; lex->name=$4.str; } - | DROP UDF_SYM IDENT_sys + | DROP FUNCTION_SYM if_exists IDENT_sys opt_restrict { LEX *lex=Lex; lex->sql_command = SQLCOM_DROP_FUNCTION; - lex->udf.name = $3; - }; - + lex->drop_if_exists= $3; + lex->udf.name= $4; + } + | DROP PROCEDURE if_exists IDENT_sys opt_restrict + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_DROP_PROCEDURE; + lex->drop_if_exists= $3; + lex->udf.name= $4; + } + ; table_list: table_name @@ -3222,7 +3807,6 @@ replace: } insert_field_spec {} - {} ; insert_lock_option: @@ -3961,8 +4545,25 @@ order_ident: simple_ident: ident { - SELECT_LEX_NODE *sel=Select; - $$ = !sel->create_refs || sel->get_in_sum_expr() > 0 ? (Item*) new Item_field(NullS,NullS,$1.str) : (Item*) new Item_ref(NullS,NullS,$1.str); + sp_pvar_t *spv; + LEX *lex = Lex; + sp_pcontext *spc = lex->spcont; + + if (spc && (spv = spc->find_pvar(&$1))) + { /* We're compiling a stored procedure and found a variable */ + if (lex->sql_command != SQLCOM_CALL && ! spv->isset) + { + net_printf(YYTHD, ER_SP_UNINIT_VAR, $1.str); + YYABORT; + } + else + $$ = (Item*) new Item_splocal(spv->offset); + } + else + { + SELECT_LEX_NODE *sel=Select; + $$ = !sel->create_refs || sel->get_in_sum_expr() > 0 ? (Item*) new Item_field(NullS,NullS,$1.str) : (Item*) new Item_ref(NullS,NullS,$1.str); + } } | ident '.' ident { @@ -4275,7 +4876,7 @@ keyword: | TIMESTAMP {} | TIME_SYM {} | TYPE_SYM {} - | UDF_SYM {} + | FUNCTION_SYM {} | UNCOMMITTED_SYM {} | UNICODE_SYM {} | USE_FRM {} @@ -4330,15 +4931,12 @@ opt_var_ident_type: ; option_value: - '@' ident_or_text equal expr - { - Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4))); - } - | internal_variable_name equal set_expr_or_default + '@' ident_or_text equal expr { - LEX *lex=Lex; - lex->var_list.push_back(new set_var(lex->option_type, $1, $3)); + Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4))); } + | internal_or_splocal + {} | '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default { LEX *lex=Lex; @@ -4439,6 +5037,32 @@ internal_variable_name: } ; +internal_or_splocal: + ident equal set_expr_or_default + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + sp_pvar_t *spv; + + if (!spc || !(spv = spc->find_pvar(&$1))) + { /* Not an SP local variable */ + sys_var *tmp= find_sys_var($1.str, $1.length); + + if (!tmp) + YYABORT; + lex->var_list.push_back(new set_var(lex->option_type, tmp, $3)); + } + else + { /* An SP local variable */ + sp_instr_set *i= new sp_instr_set(lex->sphead->instructions(), + spv->offset, $3, spv->type); + + lex->sphead->add_instr(i); + spv->isset= TRUE; + } + } + ; + isolation_types: READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; } | READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; } |