diff options
author | unknown <pem@mysql.telia.com> | 2003-09-16 14:26:08 +0200 |
---|---|---|
committer | unknown <pem@mysql.telia.com> | 2003-09-16 14:26:08 +0200 |
commit | 4deedf6263df02229a30a0aa2f6621074f140b19 (patch) | |
tree | dccbb3bbe8e061d9d2956a24883ae6f2b5002f9e | |
parent | e68197450d5f42f1b07138248fff46455190ebce (diff) | |
download | mariadb-git-4deedf6263df02229a30a0aa2f6621074f140b19.tar.gz |
Implemented SP CONDITIONs and HANDLERs, with the extension of handling
MySQL error codes as well.
(No UNDO HANDLERs yet, and no SIGNAL or RESIGNAL.)
WL#850
Docs/sp-imp-spec.txt:
Spec of CONDITIONs and HANDLERs (and updated some old stuff too).
Docs/sp-implemented.txt:
Updated info about caching, CONDITIONs and HANDLERs.
include/mysqld_error.h:
New error for undeclared CONDITION.
libmysqld/Makefile.am:
New file: sp_rcontext.cc.
mysql-test/r/sp-error.result:
New tests for CONDITIONs and HANDLERs.
mysql-test/r/sp.result:
New tests for CONDITIONs and HANDLERs.
mysql-test/t/sp-error.test:
New tests for CONDITIONs and HANDLERs.
mysql-test/t/sp.test:
New tests for CONDITIONs and HANDLERs.
sql/Makefile.am:
New file: sp_rcontext.cc.
sql/lex.h:
New symbols for CONDITIONs, HANDLERs and CURSORs.
sql/mysqld.cc:
Catch error if we have a handler for it.
sql/protocol.cc:
Catch error if we have a handler for it.
sql/share/czech/errmsg.txt:
New error for undeclared CONDITION.
sql/share/danish/errmsg.txt:
New error for undeclared CONDITION.
sql/share/dutch/errmsg.txt:
New error for undeclared CONDITION.
sql/share/english/errmsg.txt:
New error for undeclared CONDITION.
sql/share/estonian/errmsg.txt:
New error for undeclared CONDITION.
sql/share/french/errmsg.txt:
New error for undeclared CONDITION.
sql/share/german/errmsg.txt:
New error for undeclared CONDITION.
sql/share/greek/errmsg.txt:
New error for undeclared CONDITION.
sql/share/hungarian/errmsg.txt:
New error for undeclared CONDITION.
sql/share/italian/errmsg.txt:
New error for undeclared CONDITION.
sql/share/japanese/errmsg.txt:
New error for undeclared CONDITION.
sql/share/korean/errmsg.txt:
New error for undeclared CONDITION.
sql/share/norwegian-ny/errmsg.txt:
New error for undeclared CONDITION.
sql/share/norwegian/errmsg.txt:
New error for undeclared CONDITION.
sql/share/polish/errmsg.txt:
New error for undeclared CONDITION.
sql/share/portuguese/errmsg.txt:
New error for undeclared CONDITION.
sql/share/romanian/errmsg.txt:
New error for undeclared CONDITION.
sql/share/russian/errmsg.txt:
New error for undeclared CONDITION.
sql/share/serbian/errmsg.txt:
New error for undeclared CONDITION.
sql/share/slovak/errmsg.txt:
New error for undeclared CONDITION.
sql/share/spanish/errmsg.txt:
New error for undeclared CONDITION.
sql/share/swedish/errmsg.txt:
New error for undeclared CONDITION.
sql/share/ukrainian/errmsg.txt:
New error for undeclared CONDITION.
sql/sp_head.cc:
New HANDLER code.
sql/sp_head.h:
New HANDLER code.
sql/sp_pcontext.cc:
New CONDITION and HANDLER code.
sql/sp_pcontext.h:
New CONDITION and HANDLER code.
sql/sp_rcontext.h:
New CONDITION and HANDLER code.
sql/sql_yacc.yy:
New CONDITION and HANDLER code.
42 files changed, 1231 insertions, 147 deletions
diff --git a/Docs/sp-imp-spec.txt b/Docs/sp-imp-spec.txt index 385625464f1..1b72026c427 100644 --- a/Docs/sp-imp-spec.txt +++ b/Docs/sp-imp-spec.txt @@ -1,6 +1,6 @@ - Implementation specification for Stored Procedures - ================================================== + Implementation specification for Stored Procedures + ================================================== - How parsing and execution of queries work @@ -45,7 +45,8 @@ - class sp_instr (sp_head.{cc,h}) This is the base class for "instructions", that is, what is generated - by the parser. It turns out that we only need 4 different sub classes: + by the parser. It turns out that we only need a minimum of 5 different + sub classes: - sp_instr_stmt Execute a statement. This is the "call-out" any normal SQL statement, like a SELECT, INSERT etc. It contains the Lex structure for the @@ -58,6 +59,10 @@ Jump if condition is not true. It turns out that the negative test is most convenient when generating the code for the flow control constructs. + - sp_instr_freturn + Return a value from a FUNCTION and exit. + For condition HANDLERs some special instructions are also needed, see + that section below. - class sp_rcontext (sp_rcontext.h) This is the runtime context in the THD structure. @@ -120,7 +125,7 @@ with an anonymous variable bound to the value to be tested. - - An example + - A simple example Parsing the procedure: @@ -258,8 +263,8 @@ the actual database name. - It's possible to write procedures that work on a particular database by calling USE, without having to use fully qualified table names - everywhere (which doesn't help if you want to call other, "general", - procedures anyway). + everywhere (which doesn't help if you want to call other, "general", + procedures anyway). - Evaluating Items @@ -315,7 +320,7 @@ This makes things a lot more complicated compared to CALL: - We can't read and parse the FUNCTION from the mysql.proc table at the point of invokation; the server requires that all tables used are - opened and locked at the beginning of the query execution. + opened and locked at the beginning of the query execution. One "obvious" solution would be to simply push "mysql.proc" to the list of tables used by the query, but this implies a "join" with this table if the query is a select, so it doesn't work (and we can't exclude this @@ -341,9 +346,9 @@ handled by the functions 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); + void sp_merge_funs(LEX *dst, LEX *src); + int sp_cache_functions(THD *thd, LEX *lex); + void sp_clear_function_cache(THD *thd); in sp.cc. @@ -361,82 +366,231 @@ analogous to other DROP statements in MySQL. + - Condition and Handlers + + Condition names are lexical entities and are kept in the parser context + just like variables. But, condition are just "aliases" for SQLSTATE + strings, or mysqld error codes (which is a non-standard extension in + MySQL), and are only used during parsing. + + Handlers comes in three types, CONTINUE, EXIT and UNDO. The latter is + like an EXIT handler with an implicit rollback, and is currently not + implemented. + The EXIT handler jumps to the end of its BEGIN-END block when finished. + The CONTINUE handler returns to the statement following that which + invoked the handler. + + The handlers in effect at any point is part of each thread's runtime + state, so we need to push and pop handlers in the sp_rcontext during + execution. We use special instructions for this: + - sp_instr_hpush_jump + Push a handler. The instruction contains the necessary information, + like which conditions we handle and the location of the handler. + The jump takes us to the location after the handler code. + - sp_instr_hpop + Pop the handlers of the current frame (which we are just leaving). + + It might seems strange to jump past the handlers like that, but there's + no extra cost in doing this, and for technical reasons it's easiest for + the parser to generate the handler instructions when they occur in the + source. + + When an error occurs, one of the error routines is called and an error + message is normally sent back to the client immediately. + Catching a condition must be done in these error routines (there are + quite a few) to prevent them from doing this. We do this by calling + a method in the THD's sp_rcontext (if there is one). If a handler is + found, this is recorded in the context and the routine returns without + sending the error message. + The exectution loop (sp_head::execute()) checks for this after each + statement and invokes the handler that has been found. If several + errors or warnings occurs during one statement, only the first is + caught, the rest are ignored. + + Invoking and returning from a handler is trivial in the EXIT case. + We simply jump to it, and it will have an sp_instr_jump as its last + instruction. + + Calling and returning from a CONTINUE handler poses some special + problems. Since we need to return to the point after its invokation, + we push the return location on a stack in the sp_rcontext (this is + done by the exectution loop). The handler then ends with a special + instruction, sp_instr_hreturn, which returns to this location. + + CONTINUE handlers have one additional problem: They are parsed at + the lexical level where they occur, so variable offsets will assume + that it's actually called at that level. However, a handler might be + invoked from a sub-block where additional local variables have been + declared, which will then share the location of any local variables + in the handler itself. So, when calling a CONTINUE handler, we need + to save any local variables above the handler's frame offset, and + restore them upon return. (This is not a problem for EXIT handlers, + since they will leave the block anyway.) + This is taken care of by the execution loop and the sp_instr_hreturn + instruction. + + - Examples: + + - EXIT handler + begin + declare x int default 0; + + begin + declare exit handler for 'XXXXX' set x = 1; + + (statement1); + (statement2); + end; + (statement3); + end + + Pos. Instruction + 0 sp_instr_set(0, '0') + 1 sp_instr_hpush_jump(4, 1) # location and frame size + 2 sp_instr_set(0, '1') + 3 sp_instr_jump(6) + 4 sp_instr_stmt('statement1') + 5 sp_instr_stmt('statement2') + 6 sp_instr_hpop(1) + 7 sp_instr_stmt('statement3') + + - CONTINUE handler + create procedure hndlr1(val int) + begin + declare x int default 0; + declare foo condition for 1146; + declare continue handler for foo set x = 1; + + insert into t3 values ("hndlr1", val); # Non-existing table? + if x>0 then + insert into t1 values ("hndlr1", val); # This instead then + end if; + end| + + Pos. Instruction + 0 sp_instr_set(1, '0') + 1 sp_instr_hpush_jump(4, 2) + 2 sp_instr_set(1, '1') + 3 sp_instr_hreturn(2) # frame size + 4 sp_instr_stmt('insert ... t3 ...') + 5 sp_instr_jump_if_not(7, 'x>0') + 6 sp_instr_stmt('insert ... t1 ...') + 7 sp_instr_hpop(2) + + - Class and function APIs + This is an outline of the key types. Some types and other details + in the actual files have been omitted for readability. - The parser context: sp_pcontext.h typedef enum { sp_param_in, - sp_param_out, - sp_param_inout + sp_param_out, + sp_param_inout } sp_param_mode_t; typedef struct { - Item_string *name; - enum enum_field_types type; - sp_param_mode_t mode; - uint offset; // Offset in current frame - my_bool isset; + 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_cond_type + { + enum { number, state, warning, notfound, exception } type; + char sqlstate[6]; + uint mysqlerr; + } sp_cond_type_t; + class sp_pcontext { sp_pcontext(); - // Return the maximum frame size - uint max_framesize(); + // Return the maximum frame size + uint max_framesize(); - // Return the current frame size - uint current_framesize(); + // Return the current frame size + uint current_framesize(); - // Return the number of parameters - uint params(); + // Return the number of parameters + uint params(); - // Set the number of parameters to the current frame size - void set_params(); + // Set the number of parameters to the current frame size + void set_params(); - // Set type of the variable at offset 'i' in the frame - void set_type(uint i, enum enum_field_types type); + // Set type of the variable at offset 'i' in the frame + void set_type(uint i, enum enum_field_types type); - // Mark the i:th variable to "set" (i.e. having a value) with - // 'val' true. - void set_isset(uint i, my_bool val); + // Mark the i:th variable to "set" (i.e. having a value) with + // 'val' true. + void set_isset(uint i, my_bool val); - // Push the variable 'name' to the frame. - void push(LEX_STRING *name, - enum enum_field_types type, sp_param_mode_t mode); + // Push the variable 'name' to the frame. + void push_var(LEX_STRING *name, + enum enum_field_types type, sp_param_mode_t mode); // Pop 'num' variables from the frame. - void pop(uint num = 1); + void pop_var(uint num = 1); + + // Find variable by name + sp_pvar_t *find_pvar(LEX_STRING *name); + + // Find variable by index + sp_pvar_t *find_pvar(uint i); + + // Push label 'name' of instruction index 'ip' to the label context + sp_label_t *push_label(char *name, uint ip); - // Find variable by name - sp_pvar_t *find_pvar(LEX_STRING *name); + // Find label 'name' in the context + sp_label_t *find_label(char *name); - // Find variable by index - sp_pvar_t *find_pvar(uint i); + // Return the last pushed label + sp_label_t *last_label(); - // Push label 'name' of instruction index 'ip' to the label context - sp_label_t *push_label(char *name, uint ip); + // Return and remove the last pushed label. + sp_label_t *pop_label(); - // Find label 'name' in the context - sp_label_t *find_label(char *name); + // Push a condition to the context + void push_cond(LEX_STRING *name, sp_cond_type_t *val); - // Return the last pushed label - sp_label_t *last_label(); + // Pop a 'num' condition from the context + void pop_cond(uint num); - // Return and remove the last pushed label. - sp_label_t *pop_label(); + // Find a condition in the context + sp_cond_type_t *find_cond(LEX_STRING *name); + + // Increase the handler count + void add_handler(); + + // Returns the handler count + uint handlers(); } - The run-time context (call frame): sp_rcontext.h + #define SP_HANDLER_NONE 0 + #define SP_HANDLER_EXIT 1 + #define SP_HANDLER_CONTINUE 2 + #define SP_HANDLER_UNDO 3 + + typedef struct + { + struct sp_cond_type *cond; + uint handler; // Location of handler + int type; + uint foffset; // Frame offset for the handlers declare level + } sp_handler_t; + class sp_rcontext { - // 'size' is the max size of the context - sp_rcontext(uint size); + // 'fsize' is the max size of the context, 'hmax' the number of handlers + sp_rcontext(uint fsize, uint hmax); // Push value (parameter) 'i' to the frame void push_item(Item *i); @@ -459,6 +613,38 @@ // Get the FUNCTION result Item *get_result(); + + // Push handler at location 'h' for condition 'cond'. 'f' is the + // current variable frame size. + void push_handler(sp_cond_type_t *cond, uint h, int type, uint f); + + // Pop 'count' handlers + void pop_handlers(uint count); + + // Find a handler for this error. This sets the state for a found + // handler in the context. If called repeatedly without clearing, + // only the first call's state is kept. + int find_handler(uint sql_errno); + + // Returns 1 if a handler has been found, with '*ip' and '*fp' set + // to the handler location and frame size respectively. + int found_handler(uint *ip, uint *fp); + + // Clear the found handler state. + void clear_handler(); + + // Push a return address for a CONTINUE handler + void push_hstack(uint ip); + + // Pop the CONTINUE handler return stack + uint pop_hstack(); + + // Save variables from frame index 'fp' and up. + void save_variables(uint fp); + + // Restore saved variables from to frame index 'fp' and up. + void restore_variables(uint fp); + } @@ -469,28 +655,33 @@ class sp_head { - int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE + int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE - sp_head(LEX_STRING *name, LEX*); + sp_head(); + + void init(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid); // Store this procedure in the database. This is a wrapper around // the function sp_create_procedure(). int create(THD *); - // Invoke a FUNCTION - int - execute_function(THD *thd, Item **args, uint argcount, Item **resp); + // Invoke a FUNCTION + int + execute_function(THD *thd, Item **args, uint argcount, Item **resp); - // CALL a PROCEDURE - int - execute_procedure(THD *thd, List<Item> *args); + // CALL a PROCEDURE + int + execute_procedure(THD *thd, List<Item> *args); // Add the instruction to this procedure. void add_instr(sp_instr *); - // Return the number of instructions. + // Returns the number of instructions. uint instructions(); + // Returns the last instruction + sp_instr *last_instruction(); + // Resets lex in 'thd' and keeps a copy of the old one. void reset_lex(THD *); @@ -505,6 +696,17 @@ // Update all instruction with this label in the backpatch list to // the current position. void backpatch(struct sp_label *); + + // Returns the SP name (with optional length in '*lenp'). + char *name(uint *lenp = 0); + + // Returns the result type for a function + Item_result result(); + + // Sets various attributes + void sp_set_info(char *creator, uint creatorlen, + longlong created, longlong modified, + bool suid, char *comment, uint commentlen); } - Instructions @@ -530,60 +732,89 @@ int execute(THD *, uint *nextp); - // Set the statement's Lex + // Set the statement's Lex void set_lex(LEX *); - // Return the statement's Lex - LEX *get_lex(); + // Return the statement's Lex + LEX *get_lex(); } - SET instruction: class sp_instr_set : public sp_instr - { - // 'offset' is the variable's frame offset, 'val' the value, - // and 'type' the variable type. - sp_instr_set(uint ip, - uint offset, Item *val, enum enum_field_types type); + { + // 'offset' is the variable's frame offset, 'val' the value, + // and 'type' the variable type. + sp_instr_set(uint ip, + uint offset, Item *val, enum enum_field_types type); - int execute(THD *, uint *nextp); - } + int execute(THD *, uint *nextp); + } - Unconditional jump class sp_instr_jump : public sp_instr - { - // No destination, must be set. - sp_instr_jump(uint ip); + { + // No destination, must be set. + sp_instr_jump(uint ip); - // 'dest' is the destination instruction index. - sp_instr_jump(uint ip, uint dest); + // 'dest' is the destination instruction index. + sp_instr_jump(uint ip, uint dest); - int execute(THD *, uint *nextp); + int execute(THD *, uint *nextp); - // Set the destination instruction 'dest'. - void set_destination(uint dest); + // Set the destination instruction 'dest'. + void set_destination(uint dest); } - Conditional jump class sp_instr_jump_if_not : public sp_instr_jump - { - // Jump if 'i' evaluates to false. Destination not set yet. - sp_instr_jump_if_not(uint ip, Item *i); + { + // Jump if 'i' evaluates to false. Destination not set yet. + sp_instr_jump_if_not(uint ip, Item *i); - // Jump to 'dest' if 'i' evaluates to false. - sp_instr_jump_if_not(uint ip, Item *i, uint dest) + // Jump to 'dest' if 'i' evaluates to false. + sp_instr_jump_if_not(uint ip, Item *i, uint dest) - int execute(THD *, uint *nextp); + int execute(THD *, uint *nextp); } - Return a function value - class sp_instr_return : public sp_instr - { - // Return the value 'val' - sp_instr_return(uint ip, Item *val, enum enum_field_types type); - - int execute(THD *thd, uint *nextp); - } + class sp_instr_freturn : public sp_instr + { + // Return the value 'val' + sp_instr_freturn(uint ip, Item *val, enum enum_field_types type); + + int execute(THD *thd, uint *nextp); + } + + - Push a handler and jump + class sp_instr_hpush_jump : public sp_instr_jump + { + // Push handler of type 'htype', with current frame size 'fp' + sp_instr_hpush_jump(uint ip, int htype, uint fp); + + int execute(THD *thd, uint *nextp); + + // Add condition for this handler + void add_condition(struct sp_cond_type *cond); + } + - Pops handlers + class sp_instr_hpop : public sp_instr + { + // Pop 'count' handlers + sp_instr_hpop(uint ip, uint count); + + int execute(THD *thd, uint *nextp); + } + + - Return from a CONTINUE handler + class sp_instr_hreturn : public sp_instr + { + // Return from handler, and restore variables to 'fp'. + sp_instr_hreturn(uint ip, uint fp); + + int execute(THD *thd, uint *nextp); + } - Utility functions: sp.h @@ -602,7 +833,8 @@ // definition string ("create procedure ..."). int sp_create_procedure(THD *, char *name, uint namelen, - char *def, uint deflen); + char *def, uint deflen, + char *comment, uint commentlen, bool suid); // Drop the procedure 'name' from the database. int sp_drop_procedure(THD *, char *name, uint namelen); @@ -614,9 +846,28 @@ // definition string ("create function ..."). int sp_create_function(THD *, char *name, uint namelen, - char *def, uint deflen); + char *def, uint deflen, + char *comment, uint commentlen, bool suid); // Drop the function 'name' from the database. int sp_drop_function(THD *, char *name, uint namelen); + + - The cache: sp_cache.h + + class sp_cache + { + sp_cache(); + + void init(); + + void cleanup(); + + void insert(sp_head *sp); + + sp_head *lookup(char *name, uint namelen); + + void remove(sp_head *sp); + } + -- diff --git a/Docs/sp-implemented.txt b/Docs/sp-implemented.txt index 41e7c4b2923..5ce09ae3af4 100644 --- a/Docs/sp-implemented.txt +++ b/Docs/sp-implemented.txt @@ -1,4 +1,4 @@ -Stored Procedures implemented 2003-03-07: +Stored Procedures implemented 2003-09-16: Summary of Not Yet Implemented: @@ -7,13 +7,12 @@ Summary of Not Yet Implemented: - External languages - Access control - Routine characteristics (mostly used for external languages) - - Prepared SP caching; SPs are fetched and reparsed at each call - SQL-99 COMMIT (related to BEGIN/END) - DECLARE CURSOR ... - FOR-loops (as it requires cursors) - CASCADE/RESTRICT for ALTER and DROP - ALTER/DROP METHOD (as it implies User Defined Types) - - CONDITIONs, HANDLERs, SIGNAL and RESIGNAL (will probably not be implemented) + - SIGNAL and RESIGNAL, and UNDO handlers Summary of what's implemented: @@ -24,7 +23,8 @@ Summary of what's implemented: - BEGIN/END, SET, CASE, IF, LOOP, WHILE, REPEAT, ITERATE, LEAVE - SELECT INTO local variables - "Non-query" FUNCTIONs only - + - Prepared SP caching + - CONDITIONs and HANDLERs List of what's implemented: @@ -75,7 +75,17 @@ List of what's implemented: query (unlike a PROCEDURE, which is called as a statement). The table locking scheme used makes it difficult to allow "subqueries" during FUNCTION invokation. - + - SPs are cached, but with a separate cache for each thread (THD). + There are still quite a few non-reentrant constructs in the lexical + context which makes sharing prepared SPs impossible. And, even when + this is resolved, it's not necessarily the case that it will be faster + than a cache per thread. A global cache requires locks, which might + become a buttleneck. (It would save memory though.) + - CONDITIONs and HANDLERs are implemented, but not the SIGNAL and + RESIGNAL statements. (It's unclear if these can be implemented.) + The semantics of CONDITIONs is expanded to allow catching MySQL error + codes as well. UNDO handlers are not implemented (since we don't have + SQL-99 style transaction control yet). Closed questions: diff --git a/include/mysqld_error.h b/include/mysqld_error.h index aabd04bcae8..a448126084d 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -305,4 +305,5 @@ #define ER_UPDATE_LOG_DEPRECATED_TRANSLATED 1286 #define ER_QUERY_INTERRUPTED 1287 #define ER_SP_WRONG_NO_OF_ARGS 1288 -#define ER_ERROR_MESSAGES 289 +#define ER_SP_COND_MISMATCH 1289 +#define ER_ERROR_MESSAGES 290 diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index 17ff22bce65..080da0c7725 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -57,7 +57,7 @@ sqlsources = derror.cc field.cc field_conv.cc filesort.cc \ sql_update.cc sql_yacc.cc table.cc thr_malloc.cc time.cc \ unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc \ spatial.cc gstream.cc sql_help.cc \ - sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc + sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources) libmysqld_a_SOURCES= diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 95fe8edf321..d839b8a3af8 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -96,3 +96,28 @@ select f(1, 2); ERROR HY000: Wrong number of arguments for FUNCTION f, expected 1, got 2 drop procedure p; drop function f; +create procedure p(val int, out res int) +begin +declare x int default 0; +declare continue handler for foo set x = 1; +insert into test.t1 values (val); +if (x) then +set res = 0; +else +set res = 1; +end if; +end; +ERROR HY000: Undefined CONDITION: foo +create procedure p(val int, out res int) +begin +declare x int default 0; +declare foo condition for 1146; +declare continue handler for bar set x = 1; +insert into test.t1 values (val); +if (x) then +set res = 0; +else +set res = 1; +end if; +end; +ERROR HY000: Undefined CONDITION: bar diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index c08d252cfe3..42f720c7791 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -443,6 +443,63 @@ drop function inc; drop function mul; drop function append; drop function fun; +create procedure hndlr1(val int) +begin +declare x int default 0; +declare foo condition for 1146; +declare continue handler for foo set x = 1; +insert into test.t666 values ("hndlr1", val); # Non-existing table +if (x) then +insert into test.t1 values ("hndlr1", val); # This instead then +end if; +end; +call hndlr1(42); +select * from t1; +id data +hndlr1 42 +delete from t1; +drop procedure hndlr1; +create procedure hndlr2(val int) +begin +declare x int default 0; +begin +declare exit handler for '42S02' set x = 1; +insert into test.t666 values ("hndlr2", val); # Non-existing table +end; +insert into test.t1 values ("hndlr2", x); +end; +call hndlr2(42); +select * from t1; +id data +hndlr2 1 +delete from t1; +drop procedure hndlr2; +create procedure hndlr3(val int) +begin +declare x int default 0; +declare continue handler for sqlexception # Any error +begin +declare z int; +set z = 2 * val; +set x = 1; +end; +if val < 10 then +begin +declare y int; +set y = val + 10; +insert into test.t666 values ("hndlr3", y); # Non-existing table +if x then +insert into test.t1 values ("hndlr3", y); +end if; +end; +end if; +end; +call hndlr3(3); +select * from t1; +id data +hndlr3 13 +delete from t1; +drop procedure hndlr3; drop table if exists fac; create table fac (n int unsigned not null primary key, f bigint unsigned); create procedure ifac(n int unsigned) diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index 01e05246d3e..5d46c39b601 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -142,4 +142,33 @@ select f(1, 2)| drop procedure p| drop function f| +--error 1289 +create procedure p(val int, out res int) +begin + declare x int default 0; + declare continue handler for foo set x = 1; + + insert into test.t1 values (val); + if (x) then + set res = 0; + else + set res = 1; + end if; +end| + +--error 1289 +create procedure p(val int, out res int) +begin + declare x int default 0; + declare foo condition for 1146; + declare continue handler for bar set x = 1; + + insert into test.t1 values (val); + if (x) then + set res = 0; + else + set res = 1; + end if; +end| + delimiter ;| diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 61519c52929..3450b47b4e5 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -520,6 +520,76 @@ drop function fun| # +# CONDITIONs and HANDLERs +# + +create procedure hndlr1(val int) +begin + declare x int default 0; + declare foo condition for 1146; + declare continue handler for foo set x = 1; + + insert into test.t666 values ("hndlr1", val); # Non-existing table + if (x) then + insert into test.t1 values ("hndlr1", val); # This instead then + end if; +end| + +call hndlr1(42)| +select * from t1| +delete from t1| +drop procedure hndlr1| + +create procedure hndlr2(val int) +begin + declare x int default 0; + + begin + declare exit handler for '42S02' set x = 1; + + insert into test.t666 values ("hndlr2", val); # Non-existing table + end; + + insert into test.t1 values ("hndlr2", x); +end| + +call hndlr2(42)| +select * from t1| +delete from t1| +drop procedure hndlr2| + + +create procedure hndlr3(val int) +begin + declare x int default 0; + declare continue handler for sqlexception # Any error + begin + declare z int; + + set z = 2 * val; + set x = 1; + end; + + if val < 10 then + begin + declare y int; + + set y = val + 10; + insert into test.t666 values ("hndlr3", y); # Non-existing table + if x then + insert into test.t1 values ("hndlr3", y); + end if; + end; + end if; +end| + +call hndlr3(3)| +select * from t1| +delete from t1| +drop procedure hndlr3| + + +# # Some "real" examples # diff --git a/sql/Makefile.am b/sql/Makefile.am index 088e0fa64ab..b809021b3b5 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -88,7 +88,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \ client.c sql_client.cc mini_client_errors.c pack.c\ stacktrace.c repl_failsafe.h repl_failsafe.cc sql_olap.cc\ gstream.cc spatial.cc sql_help.cc protocol_cursor.cc \ - sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc + sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \ + sp_cache.cc gen_lex_hash_SOURCES = gen_lex_hash.cc gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS) diff --git a/sql/lex.h b/sql/lex.h index ca278a7e911..23347f6113c 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -104,8 +104,10 @@ static SYMBOL symbols[] = { { "COMMITTED", SYM(COMMITTED_SYM),0,0}, { "COMPRESSED", SYM(COMPRESSED_SYM),0,0}, { "CONCURRENT", SYM(CONCURRENT),0,0}, + { "CONDITION", SYM(CONDITION_SYM),0,0}, { "CONNECTION", SYM(CONNECTION_SYM),0,0}, { "CONSTRAINT", SYM(CONSTRAINT),0,0}, + { "CONTINUE", SYM(CONTINUE_SYM),0,0}, { "CREATE", SYM(CREATE),0,0}, { "CROSS", SYM(CROSS),0,0}, { "CUBE", SYM(CUBE_SYM),0,0}, @@ -156,10 +158,13 @@ static SYMBOL symbols[] = { { "ENUM", SYM(ENUM),0,0}, { "EVENTS", SYM(EVENTS_SYM),0,0}, { "EXECUTE", SYM(EXECUTE_SYM),0,0}, - { "EXPLAIN", SYM(DESCRIBE),0,0}, { "EXISTS", SYM(EXISTS),0,0}, + { "EXIT", SYM(EXIT_SYM),0,0}, + { "EXPLAIN", SYM(DESCRIBE),0,0}, { "EXTENDED", SYM(EXTENDED_SYM),0,0}, + { "FALSE", SYM(FALSE_SYM),0,0}, { "FAST", SYM(FAST_SYM),0,0}, + { "FETCH", SYM(FETCH_SYM),0,0}, { "FIELDS", SYM(COLUMNS),0,0}, { "FILE", SYM(FILE_SYM),0,0}, { "FIRST", SYM(FIRST_SYM),0,0}, @@ -168,9 +173,9 @@ static SYMBOL symbols[] = { { "FLOAT4", SYM(FLOAT_SYM),0,0}, { "FLOAT8", SYM(DOUBLE_SYM),0,0}, { "FLUSH", SYM(FLUSH_SYM),0,0}, - { "FALSE", SYM(FALSE_SYM),0,0}, { "FOREIGN", SYM(FOREIGN),0,0}, { "FORCE", SYM(FORCE_SYM),0,0}, + { "FOUND", SYM(FOUND_SYM),0,0}, { "RAID_TYPE", SYM(RAID_TYPE),0,0}, { "RAID_CHUNKS", SYM(RAID_CHUNKS),0,0}, { "RAID_CHUNKSIZE", SYM(RAID_CHUNKSIZE),0,0}, @@ -375,6 +380,8 @@ static SYMBOL symbols[] = { { "SONAME", SYM(UDF_SONAME_SYM),0,0}, { "SPATIAL", SYM(SPATIAL_SYM),0,0}, { "SPECIFIC", SYM(SPECIFIC_SYM),0,0}, + { "SQLEXCEPTION", SYM(SQLEXCEPTION_SYM),0,0}, + { "SQLWARNING", SYM(SQLWARNING_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}, @@ -412,6 +419,7 @@ static SYMBOL symbols[] = { { "TYPE", SYM(TYPE_SYM),0,0}, { "TYPES", SYM(TYPES_SYM),0,0}, { "UNCOMMITTED", SYM(UNCOMMITTED_SYM),0,0}, + { "UNDO", SYM(UNDO_SYM),0,0}, { "UNICODE", SYM(UNICODE_SYM),0,0}, { "UNION", SYM(UNION_SYM),0,0}, { "UNIQUE", SYM(UNIQUE_SYM),0,0}, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a9929cfe1c0..a6dbc41cbf7 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -37,6 +37,7 @@ #include <thr_alarm.h> #include <ft_global.h> #include <errmsg.h> +#include "sp_rcontext.h" #define mysqld_charset &my_charset_latin1 @@ -1846,6 +1847,10 @@ extern "C" int my_message_sql(uint error, const char *str, DBUG_PRINT("error", ("Message: '%s'", str)); if ((thd= current_thd)) { + if (thd->spcont && thd->spcont->find_handler(error)) + { + DBUG_RETURN(0); + } /* thd->lex.current_select equel to zero if lex structure is not inited (not query command (COM_QUERY)) diff --git a/sql/protocol.cc b/sql/protocol.cc index 940f9035f7a..0a29a182890 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -24,6 +24,7 @@ #endif #include "mysql_priv.h" +#include "sp_rcontext.h" #include <stdarg.h> #ifndef EMBEDDED_LIBRARY @@ -60,6 +61,10 @@ void send_error(THD *thd, uint sql_errno, const char *err) err ? err : net->last_error[0] ? net->last_error : "NULL")); + if (thd->spcont && thd->spcont->find_handler(sql_errno)) + { + DBUG_VOID_RETURN; + } #ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/ query_cache_abort(net); #endif @@ -139,6 +144,10 @@ void send_error(THD *thd, uint sql_errno, const char *err) void send_warning(THD *thd, uint sql_errno, const char *err) { DBUG_ENTER("send_warning"); + if (thd->spcont && thd->spcont->find_handler(sql_errno)) + { + DBUG_VOID_RETURN; + } push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, sql_errno, err ? err : ER(sql_errno)); send_ok(thd); @@ -169,6 +178,10 @@ net_printf(THD *thd, uint errcode, ...) DBUG_ENTER("net_printf"); DBUG_PRINT("enter",("message: %u",errcode)); + if (thd->spcont && thd->spcont->find_handler(errcode)) + { + DBUG_VOID_RETURN; + } thd->query_error= 1; // needed to catch query errors during replication #ifndef EMBEDDED_LIBRARY query_cache_abort(net); // Safety diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index dbe4ed7c508..876eb634677 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -294,3 +294,4 @@ v/* "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index 9c95f966273..762353384f3 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -288,3 +288,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 7da1db7a5b1..fbfeee9b042 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -296,3 +296,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 52d4b315c7d..f72175e533e 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -290,3 +290,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index 74fdb0336d1..6338aa66e7c 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -290,3 +290,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index 83e05270e1f..f56e47df734 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -285,3 +285,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 207c5653631..8e003d1742e 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -294,3 +294,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index 861f4dd1cbf..f07756f8f8c 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -285,3 +285,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index 2f655ec47cb..d8f717e2a50 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -287,3 +287,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index e1b2696a1c3..1b6e7995a1c 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -285,3 +285,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index bec7512eefd..9c7263599a0 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -287,3 +287,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index 42614efd8b5..91d8131bb3e 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -285,3 +285,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index 3f103c1bc7e..93815312817 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -287,3 +287,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index 35b06835084..c3eef6ad8e2 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -287,3 +287,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 6574a9abaf4..93cd9279018 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -289,3 +289,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index bbc487b77c2..b50b12a92ef 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -285,3 +285,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index e49a67b92d2..41255003a9f 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -289,3 +289,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index 9319898c0a8..a371cd00248 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -287,3 +287,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index 0aae9b64a6d..d442eafe220 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -280,3 +280,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index 66a58812004..66792676a5b 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -293,3 +293,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index ef9fd53e0b3..901a5335695 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -286,3 +286,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 2bf66db0183..2e5cd806a66 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -285,3 +285,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index 0f9f26b76b5..a86346faf9c 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -290,3 +290,4 @@ "The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN." "Query execution was interrupted" "Wrong number of arguments for %s %s, expected %u, got %u" +"Undefined CONDITION: %s" diff --git a/sql/sp_head.cc b/sql/sp_head.cc index c6e89222175..f34cb39ab7d 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -213,6 +213,7 @@ sp_head::execute(THD *thd) DBUG_ENTER("sp_head::execute"); char olddbname[128]; char *olddbptr= thd->db; + sp_rcontext *ctx= thd->spcont; int ret= 0; uint ip= 0; @@ -229,15 +230,38 @@ sp_head::execute(THD *thd) olddbname[i]= '\0'; } + if (ctx) + ctx->clear_handler(); do { sp_instr *i; + uint hip; // Handler ip 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); + // Check if an exception has occurred and a handler has been found + if (ret && !thd->killed && ctx) + { + uint hf; + + switch (ctx->found_handler(&hip, &hf)) + { + case SP_HANDLER_NONE: + break; + case SP_HANDLER_CONTINUE: + ctx->save_variables(hf); + ctx->push_hstack(ip); + // Fall through + default: + ip= hip; + ret= 0; + ctx->clear_handler(); + continue; + } + } } while (ret == 0 && !thd->killed); DBUG_PRINT("info", ("ret=%d killed=%d", ret, thd->killed)); @@ -263,6 +287,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) DBUG_PRINT("info", ("function %s", m_name.str)); uint csize = m_pcont->max_framesize(); uint params = m_pcont->params(); + uint hmax = m_pcont->handlers(); sp_rcontext *octx = thd->spcont; sp_rcontext *nctx = NULL; uint i; @@ -278,7 +303,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) } // QQ Should have some error checking here? (types, etc...) - nctx= new sp_rcontext(csize); + nctx= new sp_rcontext(csize, hmax); for (i= 0 ; i < params && i < argcount ; i++) { sp_pvar_t *pvar = m_pcont->find_pvar(i); @@ -311,6 +336,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) sp_instr *p; uint csize = m_pcont->max_framesize(); uint params = m_pcont->params(); + uint hmax = m_pcont->handlers(); sp_rcontext *octx = thd->spcont; sp_rcontext *nctx = NULL; my_bool tmp_octx = FALSE; // True if we have allocated a temporary octx @@ -322,16 +348,16 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) DBUG_RETURN(-1); } - if (csize > 0) + if (csize > 0 || hmax > 0) { uint i; List_iterator_fast<Item> li(*args); Item *it; - nctx = new sp_rcontext(csize); + nctx = new sp_rcontext(csize, hmax); if (! octx) { // Create a temporary old context - octx = new sp_rcontext(csize); + octx = new sp_rcontext(csize, hmax); tmp_octx = TRUE; } // QQ: Should do type checking? @@ -633,13 +659,54 @@ sp_instr_jump_if_not::execute(THD *thd, uint *nextp) } // -// sp_instr_return +// sp_instr_freturn // int -sp_instr_return::execute(THD *thd, uint *nextp) +sp_instr_freturn::execute(THD *thd, uint *nextp) { - DBUG_ENTER("sp_instr_return::execute"); + DBUG_ENTER("sp_instr_freturn::execute"); thd->spcont->set_result(eval_func_item(thd, m_value, m_type)); *nextp= UINT_MAX; DBUG_RETURN(0); } + +// +// sp_instr_hpush_jump +// +int +sp_instr_hpush_jump::execute(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_hpush_jump::execute"); + List_iterator_fast<sp_cond_type_t> li(m_cond); + sp_cond_type_t *p; + + while ((p= li++)) + thd->spcont->push_handler(p, m_handler, m_type, m_frame); + + *nextp= m_dest; + DBUG_RETURN(0); +} + +// +// sp_instr_hpop +// +int +sp_instr_hpop::execute(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_hpop::execute"); + thd->spcont->pop_handlers(m_count); + *nextp= m_ip+1; + DBUG_RETURN(0); +} + +// +// sp_instr_hreturn +// +int +sp_instr_hreturn::execute(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_hreturn::execute"); + thd->spcont->restore_variables(m_frame); + *nextp= thd->spcont->pop_hstack(); + DBUG_RETURN(0); +} diff --git a/sql/sp_head.h b/sql/sp_head.h index f0435de99d7..e7dbd3f68a6 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -36,6 +36,8 @@ struct sp_label; class sp_instr; +struct sp_cond_type; + class sp_head : public Sql_alloc { sp_head(const sp_head &); /* Prevent use of these */ @@ -93,6 +95,15 @@ public: return m_instr.elements; } + inline sp_instr * + last_instruction() + { + sp_instr *i; + + get_dynamic(&m_instr, (gptr)&i, m_instr.elements-1); + return i; + } + // Resets lex in 'thd' and keeps a copy of the old one. void reset_lex(THD *thd); @@ -385,18 +396,18 @@ private: }; // class sp_instr_jump_if_not : public sp_instr_jump -class sp_instr_return : public sp_instr +class sp_instr_freturn : public sp_instr { - sp_instr_return(const sp_instr_return &); /* Prevent use of these */ - void operator=(sp_instr_return &); + sp_instr_freturn(const sp_instr_freturn &); /* Prevent use of these */ + void operator=(sp_instr_freturn &); public: - sp_instr_return(uint ip, Item *val, enum enum_field_types type) + sp_instr_freturn(uint ip, Item *val, enum enum_field_types type) : sp_instr(ip), m_value(val), m_type(type) {} - virtual ~sp_instr_return() + virtual ~sp_instr_freturn() {} virtual int execute(THD *thd, uint *nextp); @@ -406,6 +417,89 @@ protected: Item *m_value; enum enum_field_types m_type; -}; // class sp_instr_return : public sp_instr +}; // class sp_instr_freturn : public sp_instr + + +class sp_instr_hpush_jump : public sp_instr_jump +{ + sp_instr_hpush_jump(const sp_instr_hpush_jump &); /* Prevent use of these */ + void operator=(sp_instr_hpush_jump &); + +public: + + sp_instr_hpush_jump(uint ip, int htype, uint fp) + : sp_instr_jump(ip), m_type(htype), m_frame(fp) + { + m_handler= ip+1; + m_cond.empty(); + } + + virtual ~sp_instr_hpush_jump() + { + m_cond.empty(); + } + + virtual int execute(THD *thd, uint *nextp); + + inline void add_condition(struct sp_cond_type *cond) + { + m_cond.push_front(cond); + } + +private: + + int m_type; // Handler type + uint m_frame; + uint m_handler; // Location of handler + List<struct sp_cond_type> m_cond; + +}; // class sp_instr_hpush_jump : public sp_instr_jump + + +class sp_instr_hpop : public sp_instr +{ + sp_instr_hpop(const sp_instr_hpop &); /* Prevent use of these */ + void operator=(sp_instr_hpop &); + +public: + + sp_instr_hpop(uint ip, uint count) + : sp_instr(ip), m_count(count) + {} + + virtual ~sp_instr_hpop() + {} + + virtual int execute(THD *thd, uint *nextp); + +private: + + uint m_count; + +}; // class sp_instr_hpop : public sp_instr + + +class sp_instr_hreturn : public sp_instr +{ + sp_instr_hreturn(const sp_instr_hreturn &); /* Prevent use of these */ + void operator=(sp_instr_hreturn &); + +public: + + sp_instr_hreturn(uint ip, uint fp) + : sp_instr(ip), m_frame(fp) + {} + + virtual ~sp_instr_hreturn() + {} + + virtual int execute(THD *thd, uint *nextp); + +private: + + uint m_frame; + +}; // class sp_instr_hreturn : public sp_instr + #endif /* _SP_HEAD_H_ */ diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index 3973f05b74b..a192d78b9a3 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -27,9 +27,10 @@ #include "sp_head.h" sp_pcontext::sp_pcontext() - : Sql_alloc(), m_params(0), m_framesize(0), m_genlab(0) + : Sql_alloc(), m_params(0), m_framesize(0), m_handlers(0), m_genlab(0) { VOID(my_init_dynamic_array(&m_pvar, sizeof(sp_pvar_t *), 16, 8)); + VOID(my_init_dynamic_array(&m_cond, sizeof(sp_cond_type_t *), 16, 8)); m_label.empty(); } @@ -37,6 +38,7 @@ void sp_pcontext::destroy() { delete_dynamic(&m_pvar); + delete_dynamic(&m_cond); m_label.empty(); } @@ -55,8 +57,9 @@ sp_pcontext::find_pvar(LEX_STRING *name) while (i-- > 0) { - sp_pvar_t *p= find_pvar(i); + sp_pvar_t *p; + get_dynamic(&m_pvar, (gptr)&p, i); if (my_strnncoll(system_charset_info, (const uchar *)name->str, name->length, (const uchar *)p->name.str, p->name.length) == 0) @@ -68,8 +71,8 @@ sp_pcontext::find_pvar(LEX_STRING *name) } void -sp_pcontext::push(LEX_STRING *name, enum enum_field_types type, - sp_param_mode_t mode) +sp_pcontext::push_pvar(LEX_STRING *name, enum enum_field_types type, + sp_param_mode_t mode) { sp_pvar_t *p= (sp_pvar_t *)sql_alloc(sizeof(sp_pvar_t)); @@ -113,3 +116,42 @@ sp_pcontext::find_label(char *name) return NULL; } + +void +sp_pcontext::push_cond(LEX_STRING *name, sp_cond_type_t *val) +{ + sp_cond_t *p= (sp_cond_t *)sql_alloc(sizeof(sp_cond_t)); + + if (p) + { + if (m_cond.elements == m_framesize) + m_framesize += 1; + p->name.str= name->str; + p->name.length= name->length; + p->val= val; + insert_dynamic(&m_cond, (gptr)&p); + } +} + +/* + * See comment for find_pvar() above + */ +sp_cond_type_t * +sp_pcontext::find_cond(LEX_STRING *name) +{ + uint i = m_cond.elements; + + while (i-- > 0) + { + sp_cond_t *p; + + get_dynamic(&m_cond, (gptr)&p, i); + if (my_strnncoll(system_charset_info, + (const uchar *)name->str, name->length, + (const uchar *)p->name.str, p->name.length) == 0) + { + return p->val; + } + } + return NULL; +} diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index 6900e18aa93..6fb56faccf6 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -44,6 +44,19 @@ typedef struct sp_label uint ip; // Instruction index } sp_label_t; +typedef struct sp_cond_type +{ + enum { number, state, warning, notfound, exception } type; + char sqlstate[6]; + uint mysqlerr; +} sp_cond_type_t; + +typedef struct sp_cond +{ + LEX_STRING name; + sp_cond_type_t *val; +} sp_cond_t; + class sp_pcontext : public Sql_alloc { sp_pcontext(const sp_pcontext &); /* Prevent use of these */ @@ -57,6 +70,10 @@ class sp_pcontext : public Sql_alloc void destroy(); + // + // Parameters and variables + // + inline uint max_framesize() { @@ -101,11 +118,11 @@ class sp_pcontext : public Sql_alloc } void - push(LEX_STRING *name, enum enum_field_types type, sp_param_mode_t mode); + push_pvar(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) + pop_pvar(uint num = 1) { while (num--) pop_dynamic(&m_pvar); @@ -128,6 +145,10 @@ class sp_pcontext : public Sql_alloc return p; } + // + // Labels + // + sp_label_t * push_label(char *name, uint ip); @@ -146,12 +167,47 @@ class sp_pcontext : public Sql_alloc return m_label.pop(); } + // + // Conditions + // + + void + push_cond(LEX_STRING *name, sp_cond_type_t *val); + + inline void + pop_cond(uint num) + { + while (num--) + pop_dynamic(&m_cond); + } + + sp_cond_type_t * + find_cond(LEX_STRING *name); + + // + // Handlers + // + + inline void + add_handler() + { + m_handlers+= 1; + } + + inline uint + handlers() + { + return m_handlers; + } + private: uint m_params; // The number of parameters uint m_framesize; // The maximum framesize + uint m_handlers; // The total number of handlers - DYNAMIC_ARRAY m_pvar; + DYNAMIC_ARRAY m_pvar; // Parameters/variables + DYNAMIC_ARRAY m_cond; // Conditions List<sp_label_t> m_label; // The label list uint m_genlab; // Gen. label counter diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc new file mode 100644 index 00000000000..79d02d843ea --- /dev/null +++ b/sql/sp_rcontext.cc @@ -0,0 +1,95 @@ +/* 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_rcontext.h" +#include "sp_pcontext.h" + +sp_rcontext::sp_rcontext(uint fsize, uint hmax) + : m_count(0), m_fsize(fsize), m_result(NULL), m_hcount(0), m_hsp(0) +{ + m_frame= (Item **)sql_alloc(fsize * sizeof(Item*)); + m_outs= (int *)sql_alloc(fsize * sizeof(int)); + m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t)); + m_hstack= (uint *)sql_alloc(hmax * sizeof(uint)); + m_saved.empty(); +} + +int +sp_rcontext::find_handler(uint sql_errno) +{ + if (m_hfound >= 0) + return 1; // Already got one + + const char *sqlstate= mysql_errno_to_sqlstate(sql_errno); + int i= m_hcount, found= 0; + + while (!found && i--) + { + sp_cond_type_t *cond= m_handler[i].cond; + + switch (cond->type) + { + case sp_cond_type_t::number: + if (sql_errno == cond->mysqlerr) + found= 1; + break; + case sp_cond_type_t::state: + if (strcmp(sqlstate, cond->sqlstate) == 0) + found= 1; + break; + case sp_cond_type_t::warning: + if (sqlstate[0] == '0' && sqlstate[0] == '1') + found= 1; + break; + case sp_cond_type_t::notfound: + if (sqlstate[0] == '0' && sqlstate[0] == '2') + found= 1; + break; + case sp_cond_type_t::exception: + if (sqlstate[0] != '0' || sqlstate[0] > '2') + found= 1; + break; + } + } + if (found) + m_hfound= i; + return found; +} + +void +sp_rcontext::save_variables(uint fp) +{ + while (fp < m_count) + m_saved.push_front(m_frame[fp++]); +} + +void +sp_rcontext::restore_variables(uint fp) +{ + uint i= m_count; + + while (i-- > fp) + m_frame[i]= m_saved.pop(); +} diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 78485fdd090..fe954ed0d94 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -18,6 +18,25 @@ #ifndef _SP_RCONTEXT_H_ #define _SP_RCONTEXT_H_ +#ifdef __GNUC__ +#pragma interface /* gcc class implementation */ +#endif + +struct sp_cond_type; + +#define SP_HANDLER_NONE 0 +#define SP_HANDLER_EXIT 1 +#define SP_HANDLER_CONTINUE 2 +#define SP_HANDLER_UNDO 3 + +typedef struct +{ + struct sp_cond_type *cond; + uint handler; // Location of handler + int type; + uint foffset; // Frame offset for the handlers declare level +} sp_handler_t; + class sp_rcontext : public Sql_alloc { sp_rcontext(const sp_rcontext &); /* Prevent use of these */ @@ -25,23 +44,19 @@ class sp_rcontext : public Sql_alloc 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(uint fsize, uint hmax); ~sp_rcontext() { // Not needed? //sql_element_free(m_frame); + //m_saved.empty(); } inline void push_item(Item *i) { - if (m_count < m_size) + if (m_count < m_fsize) m_frame[m_count++] = i; } @@ -82,13 +97,79 @@ class sp_rcontext : public Sql_alloc return m_result; } + inline void + push_handler(struct sp_cond_type *cond, uint h, int type, uint f) + { + m_handler[m_hcount].cond= cond; + m_handler[m_hcount].handler= h; + m_handler[m_hcount].type= type; + m_handler[m_hcount].foffset= f; + m_hcount+= 1; + } + + inline void + pop_handlers(uint count) + { + m_hcount-= count; + } + + // Returns 1 if a handler was found, 0 otherwise. + int + find_handler(uint sql_errno); + + // Returns handler type and sets *ip to location if one was found + inline int + found_handler(uint *ip, uint *fp) + { + if (m_hfound < 0) + return SP_HANDLER_NONE; + *ip= m_handler[m_hfound].handler; + *fp= m_handler[m_hfound].foffset; + return m_handler[m_hfound].type; + } + + // Clears the handler find state + inline void + clear_handler() + { + m_hfound= -1; + } + + inline void + push_hstack(uint h) + { + m_hstack[m_hsp++]= h; + } + + inline uint + pop_hstack() + { + return m_hstack[--m_hsp]; + } + + // Save variables starting at fp and up + void + save_variables(uint fp); + + // Restore variables down to fp + void + restore_variables(uint fp); + private: uint m_count; - uint m_size; + uint m_fsize; Item **m_frame; int *m_outs; Item *m_result; // For FUNCTIONs + sp_handler_t *m_handler; + uint m_hcount; + uint *m_hstack; + uint m_hsp; + + int m_hfound; // Set by find_handler; -1 if not found + + List<Item> m_saved; // Saved variables }; // class sp_rcontext : public Sql_alloc diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index d1c6975bfa4..17bd75bb71c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -37,6 +37,7 @@ #include "item_create.h" #include "sp_head.h" #include "sp_pcontext.h" +#include "sp_rcontext.h" #include "sp.h" #include <myisam.h> #include <myisammrg.h> @@ -84,6 +85,8 @@ inline Item *or_or_concat(THD *thd, Item* A, Item* B) interval_type interval; st_select_lex *select_lex; chooser_compare_func_creator boolfunc2creator; + struct sp_cond_type *spcondtype; + struct { int vars, conds, hndlrs; } spblock; } %{ @@ -204,8 +207,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token COLUMNS %token COLUMN_SYM %token CONCURRENT +%token CONDITION_SYM %token CONNECTION_SYM %token CONSTRAINT +%token CONTINUE_SYM %token CONVERT_SYM %token DATABASES %token DATA_SYM @@ -226,14 +231,17 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token DIRECTORY_SYM %token ESCAPE_SYM %token EXISTS +%token EXIT_SYM %token EXTENDED_SYM %token FALSE_SYM +%token FETCH_SYM %token FILE_SYM %token FIRST_SYM %token FIXED_SYM %token FLOAT_NUM %token FORCE_SYM %token FOREIGN +%token FOUND_SYM %token FROM %token FULL %token FULLTEXT_SYM @@ -356,6 +364,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token SHUTDOWN %token SPATIAL_SYM %token SPECIFIC_SYM +%token SQLEXCEPTION_SYM +%token SQLWARNING_SYM %token SSL_SYM %token STARTING %token STATUS_SYM @@ -382,6 +392,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token FUNCTION_SYM %token UNCOMMITTED_SYM %token UNDERSCORE_CHARSET +%token UNDO_SYM %token UNICODE_SYM %token UNION_SYM %token UNIQUE_SYM @@ -725,7 +736,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); 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 <num> sp_decl_idents sp_opt_inout sp_handler_type sp_hcond_list +%type <spcondtype> sp_cond sp_hcond +%type <spblock> sp_decls sp_decl %type <NONE> '-' '+' '*' '/' '%' '(' ')' @@ -1096,7 +1109,7 @@ sp_fdparams: sp_fdparam: ident type sp_opt_locator { - Lex->spcont->push(&$1, (enum enum_field_types)$2, sp_param_in); + Lex->spcont->push_pvar(&$1, (enum enum_field_types)$2, sp_param_in); } ; @@ -1114,9 +1127,9 @@ sp_pdparams: sp_pdparam: sp_opt_inout ident type sp_opt_locator { - Lex->spcont->push(&$2, - (enum enum_field_types)$3, - (sp_param_mode_t)$1); + Lex->spcont->push_pvar(&$2, + (enum enum_field_types)$3, + (sp_param_mode_t)$1); } ; @@ -1140,11 +1153,13 @@ sp_proc_stmts: sp_decls: /* Empty */ { - $$= 0; + $$.vars= $$.conds= $$.hndlrs= 0; } | sp_decls sp_decl ';' { - $$= $1 + $2; + $$.vars= $1.vars + $2.vars; + $$.conds= $1.conds + $2.conds; + $$.hndlrs= $1.hndlrs + $2.hndlrs; } ; @@ -1170,19 +1185,141 @@ sp_decl: lex->spcont->set_isset(i, TRUE); } } - $$= $2; + $$.vars= $2; + $$.conds= $$.hndlrs= 0; + } + | DECLARE_SYM ident CONDITION_SYM FOR_SYM sp_cond + { + YYTHD->lex->spcont->push_cond(&$2, $5); + $$.vars= $$.hndlrs= 0; + $$.conds= 1; + } + | DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_instr_hpush_jump *i= + new sp_instr_hpush_jump(sp->instructions(), $2, + ctx->current_framesize()); + + sp->add_instr(i); + sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + ctx->add_handler(); + } + sp_hcond_list sp_proc_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_label_t *hlab= lex->spcont->pop_label(); /* After this hdlr */ + + if ($2 == SP_HANDLER_CONTINUE) + sp->add_instr(new sp_instr_hreturn(sp->instructions(), + lex->spcont->current_framesize())); + else + { /* EXIT or UNDO handler, just jump to the end of the block */ + sp_instr_jump *i= new sp_instr_jump(sp->instructions()); + + sp->add_instr(i); + sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */ + } + lex->sphead->backpatch(hlab); + $$.vars= $$.conds= 0; + $$.hndlrs= $6; + } +/* QQ Not yet + | DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt + { + $$.vars= $$.conds= $$.hndlrs= 0; + }*/ + ; + +sp_handler_type: + EXIT_SYM { $$= SP_HANDLER_EXIT; } + | CONTINUE_SYM { $$= SP_HANDLER_CONTINUE; } +/* | UNDO_SYM { QQ No yet } */ + ; + +sp_hcond_list: + sp_hcond + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_instr_hpush_jump *i= (sp_instr_hpush_jump *)sp->last_instruction(); + + i->add_condition($1); + $$= 1; + } + | sp_hcond_list ',' sp_hcond + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_instr_hpush_jump *i= (sp_instr_hpush_jump *)sp->last_instruction(); + + i->add_condition($3); + $$= $1 + 1; + } + ; + +sp_cond: + ULONG_NUM + { /* mysql errno */ + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::number; + $$->mysqlerr= $1; + } + | TEXT_STRING_literal + { /* SQLSTATE */ + uint len= ($1.length < sizeof($$->sqlstate)-1 ? + $1.length : sizeof($$->sqlstate)-1); + + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::state; + memcpy($$->sqlstate, $1.str, len); + $$->sqlstate[len]= '\0'; + } + ; + +sp_hcond: + sp_cond + { + $$= $1; + } + | ident /* CONDITION name */ + { + $$= Lex->spcont->find_cond(&$1); + if ($$ == NULL) + { + net_printf(YYTHD, ER_SP_COND_MISMATCH, $1.str); + YYABORT; + } + } + | SQLWARNING_SYM /* SQLSTATEs 01??? */ + { + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::warning; + } + | NOT FOUND_SYM /* SQLSTATEs 02??? */ + { + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::notfound; + } + | SQLEXCEPTION_SYM /* All other SQLSTATEs */ + { + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::exception; } ; sp_decl_idents: ident { - Lex->spcont->push(&$1, (enum_field_types)0, sp_param_in); + Lex->spcont->push_pvar(&$1, (enum_field_types)0, sp_param_in); $$= 1; } | sp_decl_idents ',' ident { - Lex->spcont->push(&$3, (enum_field_types)0, sp_param_in); + Lex->spcont->push_pvar(&$3, (enum_field_types)0, sp_param_in); $$= $1 + 1; } ; @@ -1246,9 +1383,9 @@ sp_proc_stmt: } else { - sp_instr_return *i= - new sp_instr_return(lex->sphead->instructions(), - $2, lex->sphead->m_returns); + sp_instr_freturn *i= + new sp_instr_freturn(lex->sphead->instructions(), + $2, lex->sphead->m_returns); lex->sphead->add_instr(i); } @@ -1273,13 +1410,13 @@ sp_proc_stmt: dummy.str= (char *)""; dummy.length= 0; - lex->spcont->push(&dummy, MYSQL_TYPE_STRING, sp_param_in); + lex->spcont->push_pvar(&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(); + Lex->spcont->pop_pvar(); } | sp_labeled_control {} @@ -1331,6 +1468,12 @@ sp_proc_stmt: lex->sphead->add_instr(i); } } + | OPEN_SYM ident + {} + | FETCH_SYM ident INTO select_var_list_init + {} + | CLOSE_SYM ident + {} ; sp_if: @@ -1452,13 +1595,26 @@ sp_labeled_control: 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); + + Lex->spcont->push_label((char *)"", 0); /* For end of block */ + } + sp_decls + sp_proc_stmts + END + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_instr_hpop *i; + + sp->backpatch(ctx->pop_label()); + ctx->pop_pvar($3.vars); + ctx->pop_cond($3.conds); + i= new sp_instr_hpop(sp->instructions(), $3.hndlrs); + sp->add_instr(i); } | LOOP_SYM sp_proc_stmts END LOOP_SYM |