Commit 4deedf62 authored by unknown's avatar unknown

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.
parent e6819745
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);
}
--
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:
......
......@@ -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
......@@ -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=
......
......@@ -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
......@@ -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)
......
......@@ -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 ;|
......@@ -519,6 +519,76 @@ drop function append|
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
#
......
......@@ -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)
......
......@@ -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},
......
......@@ -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))
......
......@@ -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
......
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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"
......@@ -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);
}
......@@ -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_ */
......@@ -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;
}
......@@ -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
......
/* 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();
}
......@@ -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
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment