Commit 04f0570f authored by pem@mysql.telia.com's avatar pem@mysql.telia.com

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
parent 7a7f3c13
This diff is collapsed.
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