mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
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.
This commit is contained in:
@ -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:
|
||||
|
||||
@ -361,7 +366,121 @@
|
||||
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
|
||||
|
||||
@ -374,13 +493,20 @@
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Item_string *name;
|
||||
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();
|
||||
@ -405,11 +531,11 @@
|
||||
void set_isset(uint i, my_bool val);
|
||||
|
||||
// Push the variable 'name' to the frame.
|
||||
void push(LEX_STRING *name,
|
||||
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);
|
||||
@ -428,15 +554,43 @@
|
||||
|
||||
// Return and remove the last pushed label.
|
||||
sp_label_t *pop_label();
|
||||
|
||||
// Push a condition to the context
|
||||
void push_cond(LEX_STRING *name, sp_cond_type_t *val);
|
||||
|
||||
// Pop a 'num' condition from the context
|
||||
void pop_cond(uint num);
|
||||
|
||||
// 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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -471,7 +657,9 @@
|
||||
{
|
||||
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().
|
||||
@ -488,9 +676,12 @@
|
||||
// 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
|
||||
@ -576,14 +778,43 @@
|
||||
}
|
||||
|
||||
- Return a function value
|
||||
class sp_instr_return : public sp_instr
|
||||
class sp_instr_freturn : public sp_instr
|
||||
{
|
||||
// Return the value 'val'
|
||||
sp_instr_return(uint ip, Item *val, enum enum_field_types type);
|
||||
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);
|
||||
}
|
||||
|
||||
--
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
12
sql/lex.h
12
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},
|
||||
|
@ -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);
|
||||
}
|
||||
|
106
sql/sp_head.h
106
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_ */
|
||||
|
@ -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,7 +71,7 @@ sp_pcontext::find_pvar(LEX_STRING *name)
|
||||
}
|
||||
|
||||
void
|
||||
sp_pcontext::push(LEX_STRING *name, enum enum_field_types type,
|
||||
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
|
||||
|
95
sql/sp_rcontext.cc
Normal file
95
sql/sp_rcontext.cc
Normal file
@ -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();
|
||||
}
|
@ -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
|
||||
|
||||
|
188
sql/sql_yacc.yy
188
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,7 +1127,7 @@ sp_pdparams:
|
||||
sp_pdparam:
|
||||
sp_opt_inout ident type sp_opt_locator
|
||||
{
|
||||
Lex->spcont->push(&$2,
|
||||
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,8 +1383,8 @@ sp_proc_stmt:
|
||||
}
|
||||
else
|
||||
{
|
||||
sp_instr_return *i=
|
||||
new sp_instr_return(lex->sphead->instructions(),
|
||||
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
|
||||
|
Reference in New Issue
Block a user