Finished merging wl5986 started by Igor.
This commit is contained in:
parent
2534521f9a
commit
dfcc502ab5
21 changed files with 1814 additions and 1849 deletions
|
@ -2501,12 +2501,12 @@ my_xpath_parse_VariableReference(MY_XPATH *xpath)
|
|||
xpath->item= new Item_func_get_user_var(name);
|
||||
else
|
||||
{
|
||||
sp_variable_t *spv;
|
||||
sp_variable *spv;
|
||||
sp_pcontext *spc;
|
||||
LEX *lex;
|
||||
if ((lex= current_thd->lex) &&
|
||||
(spc= lex->spcont) &&
|
||||
(spv= spc->find_variable(&name)))
|
||||
(spv= spc->find_variable(name, false)))
|
||||
{
|
||||
Item_splocal *splocal= new Item_splocal(name, spv->offset, spv->type, 0);
|
||||
#ifndef DBUG_OFF
|
||||
|
|
|
@ -5060,7 +5060,7 @@ public:
|
|||
select_value_catcher(Item_subselect *item_arg)
|
||||
:select_subselect(item_arg)
|
||||
{}
|
||||
int send_data(List<Item> &items);
|
||||
bool send_data(List<Item> &items);
|
||||
int setup(List<Item> *items);
|
||||
bool assigned; /* TRUE <=> we've caught a value */
|
||||
uint n_elements; /* How many elements we get */
|
||||
|
@ -5088,7 +5088,7 @@ int select_value_catcher::setup(List<Item> *items)
|
|||
}
|
||||
|
||||
|
||||
int select_value_catcher::send_data(List<Item> &items)
|
||||
bool select_value_catcher::send_data(List<Item> &items)
|
||||
{
|
||||
DBUG_ENTER("select_value_catcher::send_data");
|
||||
DBUG_ASSERT(!assigned);
|
||||
|
|
264
sql/sp_head.cc
264
sql/sp_head.cc
|
@ -772,7 +772,7 @@ sp_head::~sp_head()
|
|||
for (uint ip = 0 ; (i = get_instr(ip)) ; ip++)
|
||||
delete i;
|
||||
delete_dynamic(&m_instr);
|
||||
m_pcont->destroy();
|
||||
delete m_pcont;
|
||||
free_items();
|
||||
|
||||
/*
|
||||
|
@ -1078,104 +1078,6 @@ void sp_head::recursion_level_error(THD *thd)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
Find an SQL handler for any condition (warning or error) after execution
|
||||
of a stored routine instruction. Basically, this function looks for an
|
||||
appropriate SQL handler in RT-contexts. If an SQL handler is found, it is
|
||||
remembered in the RT-context for future activation (the context can be
|
||||
inactive at the moment).
|
||||
|
||||
If there is no pending condition, the function just returns.
|
||||
|
||||
If there was an error during the execution, an SQL handler for it will be
|
||||
searched within the current and outer scopes.
|
||||
|
||||
There might be several errors in the Warning Info (that's possible by using
|
||||
SIGNAL/RESIGNAL in nested scopes) -- the function is looking for an SQL
|
||||
handler for the latest (current) error only.
|
||||
|
||||
If there was a warning during the execution, an SQL handler for it will be
|
||||
searched within the current scope only.
|
||||
|
||||
If several warnings were thrown during the execution and there are different
|
||||
SQL handlers for them, it is not determined which SQL handler will be chosen.
|
||||
Only one SQL handler will be executed.
|
||||
|
||||
If warnings and errors were thrown during the execution, the error takes
|
||||
precedence. I.e. error handler will be executed. If there is no handler
|
||||
for that error, condition will remain unhandled.
|
||||
|
||||
Once a warning or an error has been handled it is not removed from
|
||||
Warning Info.
|
||||
|
||||
According to The Standard (quoting PeterG):
|
||||
|
||||
An SQL procedure statement works like this ...
|
||||
SQL/Foundation 13.5 <SQL procedure statement>
|
||||
(General Rules) (greatly summarized) says:
|
||||
(1) Empty diagnostics area, thus clearing the condition.
|
||||
(2) Execute statement.
|
||||
During execution, if Exception Condition occurs,
|
||||
set Condition Area = Exception Condition and stop
|
||||
statement.
|
||||
During execution, if No Data occurs,
|
||||
set Condition Area = No Data Condition and continue
|
||||
statement.
|
||||
During execution, if Warning occurs,
|
||||
and Condition Area is not already full due to
|
||||
an earlier No Data condition, set Condition Area
|
||||
= Warning and continue statement.
|
||||
(3) Finish statement.
|
||||
At end of execution, if Condition Area is not
|
||||
already full due to an earlier No Data or Warning,
|
||||
set Condition Area = Successful Completion.
|
||||
In effect, this system means there is a precedence:
|
||||
Exception trumps No Data, No Data trumps Warning,
|
||||
Warning trumps Successful Completion.
|
||||
|
||||
NB: "Procedure statements" include any DDL or DML or
|
||||
control statements. So CREATE and DELETE and WHILE
|
||||
and CALL and RETURN are procedure statements. But
|
||||
DECLARE and END are not procedure statements.
|
||||
|
||||
@param thd thread handle
|
||||
@param ctx runtime context of the stored routine
|
||||
*/
|
||||
|
||||
static void
|
||||
find_handler_after_execution(THD *thd, sp_rcontext *ctx)
|
||||
{
|
||||
if (thd->is_error())
|
||||
{
|
||||
ctx->find_handler(thd,
|
||||
thd->get_stmt_da()->sql_errno(),
|
||||
thd->get_stmt_da()->get_sqlstate(),
|
||||
Sql_condition::WARN_LEVEL_ERROR,
|
||||
thd->get_stmt_da()->message());
|
||||
}
|
||||
else if (thd->get_stmt_da()->statement_warn_count())
|
||||
{
|
||||
Diagnostics_area::Sql_condition_iterator it=
|
||||
thd->get_stmt_da()->sql_conditions();
|
||||
const Sql_condition *err;
|
||||
while ((err= it++))
|
||||
{
|
||||
if (err->get_level() != Sql_condition::WARN_LEVEL_WARN &&
|
||||
err->get_level() != Sql_condition::WARN_LEVEL_NOTE)
|
||||
continue;
|
||||
|
||||
if (ctx->find_handler(thd,
|
||||
err->get_sql_errno(),
|
||||
err->get_sqlstate(),
|
||||
err->get_level(),
|
||||
err->get_message_text()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Execute the routine. The main instruction jump loop is there.
|
||||
|
@ -1445,19 +1347,10 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
|
|||
errors are not catchable by SQL handlers) or the connection has been
|
||||
killed during execution.
|
||||
*/
|
||||
if (!thd->is_fatal_error && !thd->killed_errno())
|
||||
if (!thd->is_fatal_error && !thd->killed_errno() &&
|
||||
ctx->handle_sql_condition(thd, &ip, i))
|
||||
{
|
||||
/*
|
||||
Find SQL handler in the appropriate RT-contexts:
|
||||
- warnings can be handled by SQL handlers within
|
||||
the current scope only;
|
||||
- errors can be handled by any SQL handler from outer scope.
|
||||
*/
|
||||
find_handler_after_execution(thd, ctx);
|
||||
|
||||
/* If found, activate handler for the current scope. */
|
||||
if (ctx->activate_handler(thd, &ip, i, &execute_arena, &backup_arena))
|
||||
err_status= FALSE;
|
||||
err_status= FALSE;
|
||||
}
|
||||
|
||||
/* Reset sp_rcontext::end_partial_result_set flag. */
|
||||
|
@ -1743,8 +1636,7 @@ sp_head::execute_trigger(THD *thd,
|
|||
init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
|
||||
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
|
||||
|
||||
if (!(nctx= new sp_rcontext(m_pcont, 0, octx)) ||
|
||||
nctx->init(thd))
|
||||
if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL)))
|
||||
{
|
||||
err_status= TRUE;
|
||||
goto err_with_cleanup;
|
||||
|
@ -1860,8 +1752,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
|
|||
init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
|
||||
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
|
||||
|
||||
if (!(nctx= new sp_rcontext(m_pcont, return_value_fld, octx)) ||
|
||||
nctx->init(thd))
|
||||
if (!(nctx= sp_rcontext::create(thd, m_pcont, return_value_fld)))
|
||||
{
|
||||
thd->restore_active_arena(&call_arena, &backup_arena);
|
||||
err_status= TRUE;
|
||||
|
@ -2078,7 +1969,6 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||
if (! octx)
|
||||
{
|
||||
/* Create a temporary old context. */
|
||||
if (!(octx= new sp_rcontext(m_pcont, NULL, octx)) || octx->init(thd))
|
||||
{
|
||||
delete octx; /* Delete octx if it was init() that failed. */
|
||||
DBUG_RETURN(TRUE);
|
||||
|
@ -2093,8 +1983,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||
thd->spcont->callers_arena= thd;
|
||||
}
|
||||
|
||||
if (!(nctx= new sp_rcontext(m_pcont, NULL, octx)) ||
|
||||
nctx->init(thd))
|
||||
if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL)))
|
||||
{
|
||||
delete nctx; /* Delete nctx if it was init() that failed. */
|
||||
thd->spcont= save_spcont;
|
||||
|
@ -2117,12 +2006,12 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||
if (!arg_item)
|
||||
break;
|
||||
|
||||
sp_variable_t *spvar= m_pcont->find_variable(i);
|
||||
sp_variable *spvar= m_pcont->find_variable(i);
|
||||
|
||||
if (!spvar)
|
||||
continue;
|
||||
|
||||
if (spvar->mode != sp_param_in)
|
||||
if (spvar->mode != sp_variable::MODE_IN)
|
||||
{
|
||||
Settable_routine_parameter *srp=
|
||||
arg_item->get_settable_routine_parameter();
|
||||
|
@ -2134,10 +2023,10 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||
break;
|
||||
}
|
||||
|
||||
srp->set_required_privilege(spvar->mode == sp_param_inout);
|
||||
srp->set_required_privilege(spvar->mode == sp_variable::MODE_INOUT);
|
||||
}
|
||||
|
||||
if (spvar->mode == sp_param_out)
|
||||
if (spvar->mode == sp_variable::MODE_OUT)
|
||||
{
|
||||
Item_null *null_item= new Item_null();
|
||||
Item *tmp_item= null_item;
|
||||
|
@ -2237,9 +2126,9 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||
if (!arg_item)
|
||||
break;
|
||||
|
||||
sp_variable_t *spvar= m_pcont->find_variable(i);
|
||||
sp_variable *spvar= m_pcont->find_variable(i);
|
||||
|
||||
if (spvar->mode == sp_param_in)
|
||||
if (spvar->mode == sp_variable::MODE_IN)
|
||||
continue;
|
||||
|
||||
Settable_routine_parameter *srp=
|
||||
|
@ -2399,7 +2288,7 @@ sp_head::restore_lex(THD *thd)
|
|||
Put the instruction on the backpatch list, associated with the label.
|
||||
*/
|
||||
int
|
||||
sp_head::push_backpatch(sp_instr *i, sp_label_t *lab)
|
||||
sp_head::push_backpatch(sp_instr *i, sp_label *lab)
|
||||
{
|
||||
bp_t *bp= (bp_t *)sql_alloc(sizeof(bp_t));
|
||||
|
||||
|
@ -2415,7 +2304,7 @@ sp_head::push_backpatch(sp_instr *i, sp_label_t *lab)
|
|||
the current position.
|
||||
*/
|
||||
void
|
||||
sp_head::backpatch(sp_label_t *lab)
|
||||
sp_head::backpatch(sp_label *lab)
|
||||
{
|
||||
bp_t *bp;
|
||||
uint dest= instructions();
|
||||
|
@ -2427,7 +2316,7 @@ sp_head::backpatch(sp_label_t *lab)
|
|||
if (bp->lab == lab)
|
||||
{
|
||||
DBUG_PRINT("info", ("backpatch: (m_ip %d, label 0x%lx <%s>) to dest %d",
|
||||
bp->instr->m_ip, (ulong) lab, lab->name, dest));
|
||||
bp->instr->m_ip, (ulong) lab, lab->name.str, dest));
|
||||
bp->instr->backpatch(dest, lab->ctx);
|
||||
}
|
||||
}
|
||||
|
@ -3107,7 +2996,7 @@ int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
|
|||
return result;
|
||||
}
|
||||
|
||||
uint sp_instr::get_cont_dest()
|
||||
uint sp_instr::get_cont_dest() const
|
||||
{
|
||||
return (m_ip+1);
|
||||
}
|
||||
|
@ -3261,7 +3150,7 @@ sp_instr_set::print(String *str)
|
|||
{
|
||||
/* set name@offset ... */
|
||||
int rsrv = SP_INSTR_UINT_MAXLEN+6;
|
||||
sp_variable_t *var = m_ctx->find_variable(m_offset);
|
||||
sp_variable *var = m_ctx->find_variable(m_offset);
|
||||
|
||||
/* 'var' should always be non-null, but just in case... */
|
||||
if (var)
|
||||
|
@ -3314,7 +3203,7 @@ sp_instr_set_trigger_field::print(String *str)
|
|||
sp_instr_opt_meta
|
||||
*/
|
||||
|
||||
uint sp_instr_opt_meta::get_cont_dest()
|
||||
uint sp_instr_opt_meta::get_cont_dest() const
|
||||
{
|
||||
return m_cont_dest;
|
||||
}
|
||||
|
@ -3532,14 +3421,12 @@ 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_ip+1, m_type);
|
||||
int ret= thd->spcont->push_handler(m_handler, m_ip + 1);
|
||||
|
||||
*nextp= m_dest;
|
||||
DBUG_RETURN(0);
|
||||
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3549,27 +3436,22 @@ sp_instr_hpush_jump::print(String *str)
|
|||
/* hpush_jump dest fsize type */
|
||||
if (str->reserve(SP_INSTR_UINT_MAXLEN*2 + 21))
|
||||
return;
|
||||
|
||||
str->qs_append(STRING_WITH_LEN("hpush_jump "));
|
||||
str->qs_append(m_dest);
|
||||
str->qs_append(' ');
|
||||
str->qs_append(m_frame);
|
||||
switch (m_type) {
|
||||
case SP_HANDLER_NONE:
|
||||
str->qs_append(STRING_WITH_LEN(" NONE")); // This would be a bug
|
||||
break;
|
||||
case SP_HANDLER_EXIT:
|
||||
|
||||
switch (m_handler->type) {
|
||||
case sp_handler::EXIT:
|
||||
str->qs_append(STRING_WITH_LEN(" EXIT"));
|
||||
break;
|
||||
case SP_HANDLER_CONTINUE:
|
||||
case sp_handler::CONTINUE:
|
||||
str->qs_append(STRING_WITH_LEN(" CONTINUE"));
|
||||
break;
|
||||
case SP_HANDLER_UNDO:
|
||||
str->qs_append(STRING_WITH_LEN(" UNDO"));
|
||||
break;
|
||||
default:
|
||||
// This would be a bug as well
|
||||
str->qs_append(STRING_WITH_LEN(" UNKNOWN:"));
|
||||
str->qs_append(m_type);
|
||||
// The handler type must be either CONTINUE or EXIT.
|
||||
DBUG_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3597,7 +3479,7 @@ sp_instr_hpush_jump::opt_mark(sp_head *sp, List<sp_instr> *leads)
|
|||
above, so we start on m_dest+1 here.
|
||||
m_opt_hpop is the hpop marking the end of the handler scope.
|
||||
*/
|
||||
if (m_type == SP_HANDLER_CONTINUE)
|
||||
if (m_handler->type == sp_handler::CONTINUE)
|
||||
{
|
||||
for (uint scope_ip= m_dest+1; scope_ip <= m_opt_hpop; scope_ip++)
|
||||
sp->add_mark_lead(scope_ip, leads);
|
||||
|
@ -3639,13 +3521,11 @@ int
|
|||
sp_instr_hreturn::execute(THD *thd, uint *nextp)
|
||||
{
|
||||
DBUG_ENTER("sp_instr_hreturn::execute");
|
||||
if (m_dest)
|
||||
*nextp= m_dest;
|
||||
else
|
||||
{
|
||||
*nextp= thd->spcont->pop_hstack();
|
||||
}
|
||||
thd->spcont->exit_handler();
|
||||
|
||||
uint continue_ip= thd->spcont->exit_handler(thd->get_stmt_da());
|
||||
|
||||
*nextp= m_dest ? m_dest : continue_ip;
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
@ -3657,12 +3537,17 @@ sp_instr_hreturn::print(String *str)
|
|||
if (str->reserve(SP_INSTR_UINT_MAXLEN*2 + 9))
|
||||
return;
|
||||
str->qs_append(STRING_WITH_LEN("hreturn "));
|
||||
str->qs_append(m_frame);
|
||||
if (m_dest)
|
||||
{
|
||||
str->qs_append(' ');
|
||||
// NOTE: this is legacy: hreturn instruction for EXIT handler
|
||||
// should print out 0 as frame index.
|
||||
str->qs_append(STRING_WITH_LEN("0 "));
|
||||
str->qs_append(m_dest);
|
||||
}
|
||||
else
|
||||
{
|
||||
str->qs_append(m_frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -3694,41 +3579,32 @@ sp_instr_hreturn::opt_mark(sp_head *sp, List<sp_instr> *leads)
|
|||
int
|
||||
sp_instr_cpush::execute(THD *thd, uint *nextp)
|
||||
{
|
||||
Query_arena backup_arena;
|
||||
DBUG_ENTER("sp_instr_cpush::execute");
|
||||
|
||||
/*
|
||||
We should create cursors in the callers arena, as
|
||||
it could be (and usually is) used in several instructions.
|
||||
*/
|
||||
thd->set_n_backup_active_arena(thd->spcont->callers_arena, &backup_arena);
|
||||
|
||||
thd->spcont->push_cursor(&m_lex_keeper, this);
|
||||
|
||||
thd->restore_active_arena(thd->spcont->callers_arena, &backup_arena);
|
||||
int ret= thd->spcont->push_cursor(&m_lex_keeper, this);
|
||||
|
||||
*nextp= m_ip+1;
|
||||
|
||||
DBUG_RETURN(0);
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sp_instr_cpush::print(String *str)
|
||||
{
|
||||
LEX_STRING n;
|
||||
my_bool found= m_ctx->find_cursor(m_cursor, &n);
|
||||
const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
|
||||
|
||||
/* cpush name@offset */
|
||||
uint rsrv= SP_INSTR_UINT_MAXLEN+7;
|
||||
|
||||
if (found)
|
||||
rsrv+= n.length;
|
||||
if (cursor_name)
|
||||
rsrv+= cursor_name->length;
|
||||
if (str->reserve(rsrv))
|
||||
return;
|
||||
str->qs_append(STRING_WITH_LEN("cpush "));
|
||||
if (found)
|
||||
if (cursor_name)
|
||||
{
|
||||
str->qs_append(n.str, n.length);
|
||||
str->qs_append(cursor_name->str, cursor_name->length);
|
||||
str->qs_append('@');
|
||||
}
|
||||
str->qs_append(m_cursor);
|
||||
|
@ -3816,19 +3692,19 @@ sp_instr_copen::exec_core(THD *thd, uint *nextp)
|
|||
void
|
||||
sp_instr_copen::print(String *str)
|
||||
{
|
||||
LEX_STRING n;
|
||||
my_bool found= m_ctx->find_cursor(m_cursor, &n);
|
||||
const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
|
||||
|
||||
/* copen name@offset */
|
||||
uint rsrv= SP_INSTR_UINT_MAXLEN+7;
|
||||
|
||||
if (found)
|
||||
rsrv+= n.length;
|
||||
if (cursor_name)
|
||||
rsrv+= cursor_name->length;
|
||||
if (str->reserve(rsrv))
|
||||
return;
|
||||
str->qs_append(STRING_WITH_LEN("copen "));
|
||||
if (found)
|
||||
if (cursor_name)
|
||||
{
|
||||
str->qs_append(n.str, n.length);
|
||||
str->qs_append(cursor_name->str, cursor_name->length);
|
||||
str->qs_append('@');
|
||||
}
|
||||
str->qs_append(m_cursor);
|
||||
|
@ -3858,19 +3734,19 @@ sp_instr_cclose::execute(THD *thd, uint *nextp)
|
|||
void
|
||||
sp_instr_cclose::print(String *str)
|
||||
{
|
||||
LEX_STRING n;
|
||||
my_bool found= m_ctx->find_cursor(m_cursor, &n);
|
||||
const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
|
||||
|
||||
/* cclose name@offset */
|
||||
uint rsrv= SP_INSTR_UINT_MAXLEN+8;
|
||||
|
||||
if (found)
|
||||
rsrv+= n.length;
|
||||
if (cursor_name)
|
||||
rsrv+= cursor_name->length;
|
||||
if (str->reserve(rsrv))
|
||||
return;
|
||||
str->qs_append(STRING_WITH_LEN("cclose "));
|
||||
if (found)
|
||||
if (cursor_name)
|
||||
{
|
||||
str->qs_append(n.str, n.length);
|
||||
str->qs_append(cursor_name->str, cursor_name->length);
|
||||
str->qs_append('@');
|
||||
}
|
||||
str->qs_append(m_cursor);
|
||||
|
@ -3899,21 +3775,21 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp)
|
|||
void
|
||||
sp_instr_cfetch::print(String *str)
|
||||
{
|
||||
List_iterator_fast<struct sp_variable> li(m_varlist);
|
||||
sp_variable_t *pv;
|
||||
LEX_STRING n;
|
||||
my_bool found= m_ctx->find_cursor(m_cursor, &n);
|
||||
List_iterator_fast<sp_variable> li(m_varlist);
|
||||
sp_variable *pv;
|
||||
const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
|
||||
|
||||
/* cfetch name@offset vars... */
|
||||
uint rsrv= SP_INSTR_UINT_MAXLEN+8;
|
||||
|
||||
if (found)
|
||||
rsrv+= n.length;
|
||||
if (cursor_name)
|
||||
rsrv+= cursor_name->length;
|
||||
if (str->reserve(rsrv))
|
||||
return;
|
||||
str->qs_append(STRING_WITH_LEN("cfetch "));
|
||||
if (found)
|
||||
if (cursor_name)
|
||||
{
|
||||
str->qs_append(n.str, n.length);
|
||||
str->qs_append(cursor_name->str, cursor_name->length);
|
||||
str->qs_append('@');
|
||||
}
|
||||
str->qs_append(m_cursor);
|
||||
|
|
|
@ -30,8 +30,9 @@
|
|||
#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
|
||||
#include "sql_class.h" // THD, set_var.h: THD
|
||||
#include "set_var.h" // Item
|
||||
#include "sp.h"
|
||||
#include "sp_pcontext.h" // sp_pcontext
|
||||
#include <stddef.h>
|
||||
#include "sp.h"
|
||||
|
||||
/**
|
||||
@defgroup Stored_Routines Stored Routines
|
||||
|
@ -39,6 +40,11 @@
|
|||
@{
|
||||
*/
|
||||
|
||||
// Values for the type enum. This reflects the order of the enum declaration
|
||||
// in the CREATE TABLE command.
|
||||
//#define TYPE_ENUM_FUNCTION 1 #define TYPE_ENUM_PROCEDURE 2 #define
|
||||
//TYPE_ENUM_TRIGGER 3 #define TYPE_ENUM_PROXY 4
|
||||
|
||||
Item_result
|
||||
sp_map_result_type(enum enum_field_types type);
|
||||
|
||||
|
@ -48,12 +54,9 @@ sp_map_item_type(enum enum_field_types type);
|
|||
uint
|
||||
sp_get_flags_for_command(LEX *lex);
|
||||
|
||||
struct sp_label;
|
||||
class sp_instr;
|
||||
class sp_instr_opt_meta;
|
||||
class sp_instr_jump_if_not;
|
||||
struct sp_cond_type;
|
||||
struct sp_variable;
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
|
@ -602,7 +605,7 @@ public:
|
|||
Get the continuation destination of this instruction.
|
||||
@return the continuation destination
|
||||
*/
|
||||
virtual uint get_cont_dest();
|
||||
virtual uint get_cont_dest() const;
|
||||
|
||||
/*
|
||||
Execute core function of instruction after all preparations (e.g.
|
||||
|
@ -874,7 +877,7 @@ public:
|
|||
virtual void set_destination(uint old_dest, uint new_dest)
|
||||
= 0;
|
||||
|
||||
virtual uint get_cont_dest();
|
||||
virtual uint get_cont_dest() const;
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -1025,15 +1028,21 @@ class sp_instr_hpush_jump : public sp_instr_jump
|
|||
|
||||
public:
|
||||
|
||||
sp_instr_hpush_jump(uint ip, sp_pcontext *ctx, int htype, uint fp)
|
||||
: sp_instr_jump(ip, ctx), m_type(htype), m_frame(fp), m_opt_hpop(0)
|
||||
sp_instr_hpush_jump(uint ip,
|
||||
sp_pcontext *ctx,
|
||||
sp_handler *handler)
|
||||
:sp_instr_jump(ip, ctx),
|
||||
m_handler(handler),
|
||||
m_opt_hpop(0),
|
||||
m_frame(ctx->current_var_count())
|
||||
{
|
||||
m_cond.empty();
|
||||
DBUG_ASSERT(m_handler->condition_values.elements == 0);
|
||||
}
|
||||
|
||||
virtual ~sp_instr_hpush_jump()
|
||||
{
|
||||
m_cond.empty();
|
||||
m_handler->condition_values.empty();
|
||||
m_handler= NULL;
|
||||
}
|
||||
|
||||
virtual int execute(THD *thd, uint *nextp);
|
||||
|
@ -1057,17 +1066,24 @@ public:
|
|||
m_opt_hpop= dest;
|
||||
}
|
||||
|
||||
inline void add_condition(struct sp_cond_type *cond)
|
||||
{
|
||||
m_cond.push_front(cond);
|
||||
}
|
||||
void add_condition(sp_condition_value *condition_value)
|
||||
{ m_handler->condition_values.push_back(condition_value); }
|
||||
|
||||
sp_handler *get_handler()
|
||||
{ return m_handler; }
|
||||
|
||||
private:
|
||||
|
||||
int m_type; ///< Handler type
|
||||
private:
|
||||
/// Handler.
|
||||
sp_handler *m_handler;
|
||||
|
||||
/// hpop marking end of handler scope.
|
||||
uint m_opt_hpop;
|
||||
|
||||
// This attribute is needed for SHOW PROCEDURE CODE only (i.e. it's needed in
|
||||
// debug version only). It's used in print().
|
||||
uint m_frame;
|
||||
uint m_opt_hpop; // hpop marking end of handler scope.
|
||||
List<struct sp_cond_type> m_cond;
|
||||
|
||||
}; // class sp_instr_hpush_jump : public sp_instr_jump
|
||||
|
||||
|
@ -1104,8 +1120,9 @@ class sp_instr_hreturn : public sp_instr_jump
|
|||
|
||||
public:
|
||||
|
||||
sp_instr_hreturn(uint ip, sp_pcontext *ctx, uint fp)
|
||||
: sp_instr_jump(ip, ctx), m_frame(fp)
|
||||
sp_instr_hreturn(uint ip, sp_pcontext *ctx)
|
||||
:sp_instr_jump(ip, ctx),
|
||||
m_frame(ctx->current_var_count())
|
||||
{}
|
||||
|
||||
virtual ~sp_instr_hreturn()
|
||||
|
|
|
@ -22,133 +22,86 @@
|
|||
#include "sp_pcontext.h"
|
||||
#include "sp_head.h"
|
||||
|
||||
/* Initial size for the dynamic arrays in sp_pcontext */
|
||||
#define PCONTEXT_ARRAY_INIT_ALLOC 16
|
||||
/* Increment size for the dynamic arrays in sp_pcontext */
|
||||
#define PCONTEXT_ARRAY_INCREMENT_ALLOC 8
|
||||
|
||||
/*
|
||||
Sanity check for SQLSTATEs. Will not check if it's really an existing
|
||||
state (there are just too many), but will check length and bad characters.
|
||||
Returns TRUE if it's ok, FALSE if it's bad.
|
||||
*/
|
||||
bool
|
||||
sp_cond_check(LEX_STRING *sqlstate)
|
||||
bool sp_condition_value::equals(const sp_condition_value *cv) const
|
||||
{
|
||||
int i;
|
||||
const char *p;
|
||||
DBUG_ASSERT(cv);
|
||||
|
||||
if (sqlstate->length != 5)
|
||||
return FALSE;
|
||||
for (p= sqlstate->str, i= 0 ; i < 5 ; i++)
|
||||
if (this == cv)
|
||||
return true;
|
||||
|
||||
if (type != cv->type)
|
||||
return false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
char c = p[i];
|
||||
case sp_condition_value::ERROR_CODE:
|
||||
return (mysqlerr == cv->mysqlerr);
|
||||
|
||||
if ((c < '0' || '9' < c) &&
|
||||
(c < 'A' || 'Z' < c))
|
||||
return FALSE;
|
||||
case sp_condition_value::SQLSTATE:
|
||||
return (strcmp(sql_state, cv->sql_state) == 0);
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
/* SQLSTATE class '00' : completion condition */
|
||||
if (strncmp(sqlstate->str, "00", 2) == 0)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void sp_pcontext::init(uint var_offset,
|
||||
uint cursor_offset,
|
||||
int num_case_expressions)
|
||||
{
|
||||
m_var_offset= var_offset;
|
||||
m_cursor_offset= cursor_offset;
|
||||
m_num_case_exprs= num_case_expressions;
|
||||
|
||||
m_labels.empty();
|
||||
}
|
||||
|
||||
|
||||
sp_pcontext::sp_pcontext()
|
||||
: Sql_alloc(),
|
||||
m_max_var_index(0), m_max_cursor_index(0), m_max_handler_index(0),
|
||||
m_context_handlers(0), m_parent(NULL), m_pboundary(0),
|
||||
m_label_scope(LABEL_DEFAULT_SCOPE)
|
||||
m_max_var_index(0), m_max_cursor_index(0),
|
||||
m_parent(NULL), m_pboundary(0),
|
||||
m_scope(REGULAR_SCOPE)
|
||||
{
|
||||
(void) my_init_dynamic_array(&m_vars, sizeof(sp_variable_t *),
|
||||
PCONTEXT_ARRAY_INIT_ALLOC,
|
||||
PCONTEXT_ARRAY_INCREMENT_ALLOC, MYF(0));
|
||||
(void) my_init_dynamic_array(&m_case_expr_id_lst, sizeof(int),
|
||||
PCONTEXT_ARRAY_INIT_ALLOC,
|
||||
PCONTEXT_ARRAY_INCREMENT_ALLOC, MYF(0));
|
||||
(void) my_init_dynamic_array(&m_conds, sizeof(sp_cond_type_t *),
|
||||
PCONTEXT_ARRAY_INIT_ALLOC,
|
||||
PCONTEXT_ARRAY_INCREMENT_ALLOC, MYF(0));
|
||||
(void) my_init_dynamic_array(&m_cursors, sizeof(LEX_STRING),
|
||||
PCONTEXT_ARRAY_INIT_ALLOC,
|
||||
PCONTEXT_ARRAY_INCREMENT_ALLOC, MYF(0));
|
||||
(void) my_init_dynamic_array(&m_handlers, sizeof(sp_cond_type_t *),
|
||||
PCONTEXT_ARRAY_INIT_ALLOC,
|
||||
PCONTEXT_ARRAY_INCREMENT_ALLOC, MYF(0));
|
||||
m_label.empty();
|
||||
m_children.empty();
|
||||
|
||||
m_var_offset= m_cursor_offset= 0;
|
||||
m_num_case_exprs= 0;
|
||||
init(0, 0, 0);
|
||||
}
|
||||
|
||||
sp_pcontext::sp_pcontext(sp_pcontext *prev, label_scope_type label_scope)
|
||||
|
||||
sp_pcontext::sp_pcontext(sp_pcontext *prev, sp_pcontext::enum_scope scope)
|
||||
: Sql_alloc(),
|
||||
m_max_var_index(0), m_max_cursor_index(0), m_max_handler_index(0),
|
||||
m_context_handlers(0), m_parent(prev), m_pboundary(0),
|
||||
m_label_scope(label_scope)
|
||||
m_max_var_index(0), m_max_cursor_index(0),
|
||||
m_parent(prev), m_pboundary(0),
|
||||
m_scope(scope)
|
||||
{
|
||||
(void) my_init_dynamic_array(&m_vars, sizeof(sp_variable_t *),
|
||||
PCONTEXT_ARRAY_INIT_ALLOC,
|
||||
PCONTEXT_ARRAY_INCREMENT_ALLOC, MYF(0));
|
||||
(void) my_init_dynamic_array(&m_case_expr_id_lst, sizeof(int),
|
||||
PCONTEXT_ARRAY_INIT_ALLOC,
|
||||
PCONTEXT_ARRAY_INCREMENT_ALLOC, MYF(0));
|
||||
(void) my_init_dynamic_array(&m_conds, sizeof(sp_cond_type_t *),
|
||||
PCONTEXT_ARRAY_INIT_ALLOC,
|
||||
PCONTEXT_ARRAY_INCREMENT_ALLOC, MYF(0));
|
||||
(void) my_init_dynamic_array(&m_cursors, sizeof(LEX_STRING),
|
||||
PCONTEXT_ARRAY_INIT_ALLOC,
|
||||
PCONTEXT_ARRAY_INCREMENT_ALLOC, MYF(0));
|
||||
(void) my_init_dynamic_array(&m_handlers, sizeof(sp_cond_type_t *),
|
||||
PCONTEXT_ARRAY_INIT_ALLOC,
|
||||
PCONTEXT_ARRAY_INCREMENT_ALLOC, MYF(0));
|
||||
m_label.empty();
|
||||
m_children.empty();
|
||||
|
||||
m_var_offset= prev->m_var_offset + prev->m_max_var_index;
|
||||
m_cursor_offset= prev->current_cursor_count();
|
||||
m_num_case_exprs= prev->get_num_case_exprs();
|
||||
init(prev->m_var_offset + prev->m_max_var_index,
|
||||
prev->current_cursor_count(),
|
||||
prev->get_num_case_exprs());
|
||||
}
|
||||
|
||||
void
|
||||
sp_pcontext::destroy()
|
||||
|
||||
sp_pcontext::~sp_pcontext()
|
||||
{
|
||||
List_iterator_fast<sp_pcontext> li(m_children);
|
||||
sp_pcontext *child;
|
||||
|
||||
while ((child= li++))
|
||||
child->destroy();
|
||||
|
||||
m_children.empty();
|
||||
m_label.empty();
|
||||
delete_dynamic(&m_vars);
|
||||
delete_dynamic(&m_case_expr_id_lst);
|
||||
delete_dynamic(&m_conds);
|
||||
delete_dynamic(&m_cursors);
|
||||
delete_dynamic(&m_handlers);
|
||||
for (int i= 0; i < m_children.elements(); ++i)
|
||||
delete m_children.at(i);
|
||||
}
|
||||
|
||||
sp_pcontext *
|
||||
sp_pcontext::push_context(label_scope_type label_scope)
|
||||
|
||||
sp_pcontext *sp_pcontext::push_context(THD *thd, sp_pcontext::enum_scope scope)
|
||||
{
|
||||
sp_pcontext *child= new sp_pcontext(this, label_scope);
|
||||
sp_pcontext *child= new (thd->mem_root) sp_pcontext(this, scope);
|
||||
|
||||
if (child)
|
||||
m_children.push_back(child);
|
||||
m_children.append(child);
|
||||
return child;
|
||||
}
|
||||
|
||||
sp_pcontext *
|
||||
sp_pcontext::pop_context()
|
||||
|
||||
sp_pcontext *sp_pcontext::pop_context()
|
||||
{
|
||||
m_parent->m_max_var_index+= m_max_var_index;
|
||||
|
||||
uint submax= max_handler_index();
|
||||
if (submax > m_parent->m_max_handler_index)
|
||||
m_parent->m_max_handler_index= submax;
|
||||
|
||||
submax= max_cursor_index();
|
||||
uint submax= max_cursor_index();
|
||||
if (submax > m_parent->m_max_cursor_index)
|
||||
m_parent->m_max_cursor_index= submax;
|
||||
|
||||
|
@ -158,142 +111,118 @@ sp_pcontext::pop_context()
|
|||
return m_parent;
|
||||
}
|
||||
|
||||
uint
|
||||
sp_pcontext::diff_handlers(sp_pcontext *ctx, bool exclusive)
|
||||
|
||||
uint sp_pcontext::diff_handlers(const sp_pcontext *ctx, bool exclusive) const
|
||||
{
|
||||
uint n= 0;
|
||||
sp_pcontext *pctx= this;
|
||||
sp_pcontext *last_ctx= NULL;
|
||||
const sp_pcontext *pctx= this;
|
||||
const sp_pcontext *last_ctx= NULL;
|
||||
|
||||
while (pctx && pctx != ctx)
|
||||
{
|
||||
n+= pctx->m_context_handlers;
|
||||
n+= pctx->m_handlers.elements();
|
||||
last_ctx= pctx;
|
||||
pctx= pctx->parent_context();
|
||||
}
|
||||
if (pctx)
|
||||
return (exclusive && last_ctx ? n - last_ctx->m_context_handlers : n);
|
||||
return (exclusive && last_ctx ? n - last_ctx->m_handlers.elements() : n);
|
||||
return 0; // Didn't find ctx
|
||||
}
|
||||
|
||||
uint
|
||||
sp_pcontext::diff_cursors(sp_pcontext *ctx, bool exclusive)
|
||||
|
||||
uint sp_pcontext::diff_cursors(const sp_pcontext *ctx, bool exclusive) const
|
||||
{
|
||||
uint n= 0;
|
||||
sp_pcontext *pctx= this;
|
||||
sp_pcontext *last_ctx= NULL;
|
||||
const sp_pcontext *pctx= this;
|
||||
const sp_pcontext *last_ctx= NULL;
|
||||
|
||||
while (pctx && pctx != ctx)
|
||||
{
|
||||
n+= pctx->m_cursors.elements;
|
||||
n+= pctx->m_cursors.elements();
|
||||
last_ctx= pctx;
|
||||
pctx= pctx->parent_context();
|
||||
}
|
||||
if (pctx)
|
||||
return (exclusive && last_ctx ? n - last_ctx->m_cursors.elements : n);
|
||||
return (exclusive && last_ctx ? n - last_ctx->m_cursors.elements() : n);
|
||||
return 0; // Didn't find ctx
|
||||
}
|
||||
|
||||
/*
|
||||
This does a linear search (from newer to older variables, in case
|
||||
we have shadowed names).
|
||||
It's possible to have a more efficient allocation and search method,
|
||||
but it might not be worth it. The typical number of parameters and
|
||||
variables will in most cases be low (a handfull).
|
||||
...and, this is only called during parsing.
|
||||
*/
|
||||
sp_variable_t *
|
||||
sp_pcontext::find_variable(LEX_STRING *name, my_bool scoped)
|
||||
|
||||
sp_variable *sp_pcontext::find_variable(LEX_STRING name,
|
||||
bool current_scope_only) const
|
||||
{
|
||||
uint i= m_vars.elements - m_pboundary;
|
||||
uint i= m_vars.elements() - m_pboundary;
|
||||
|
||||
while (i--)
|
||||
{
|
||||
sp_variable_t *p;
|
||||
sp_variable *p= m_vars.at(i);
|
||||
|
||||
get_dynamic(&m_vars, (uchar*)&p, i);
|
||||
if (my_strnncoll(system_charset_info,
|
||||
(const uchar *)name->str, name->length,
|
||||
(const uchar *)name.str, name.length,
|
||||
(const uchar *)p->name.str, p->name.length) == 0)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
}
|
||||
if (!scoped && m_parent)
|
||||
return m_parent->find_variable(name, scoped);
|
||||
return NULL;
|
||||
|
||||
return (!current_scope_only && m_parent) ?
|
||||
m_parent->find_variable(name, false) :
|
||||
NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
Find a variable by offset from the top.
|
||||
This used for two things:
|
||||
- When evaluating parameters at the beginning, and setting out parameters
|
||||
at the end, of invokation. (Top frame only, so no recursion then.)
|
||||
- For printing of sp_instr_set. (Debug mode only.)
|
||||
*/
|
||||
sp_variable_t *
|
||||
sp_pcontext::find_variable(uint offset)
|
||||
{
|
||||
if (m_var_offset <= offset && offset < m_var_offset + m_vars.elements)
|
||||
{ // This frame
|
||||
sp_variable_t *p;
|
||||
|
||||
get_dynamic(&m_vars, (uchar*)&p, offset - m_var_offset);
|
||||
return p;
|
||||
}
|
||||
if (m_parent)
|
||||
return m_parent->find_variable(offset); // Some previous frame
|
||||
return NULL; // index out of bounds
|
||||
sp_variable *sp_pcontext::find_variable(uint offset) const
|
||||
{
|
||||
if (m_var_offset <= offset && offset < m_var_offset + m_vars.elements())
|
||||
return m_vars.at(offset - m_var_offset); // This frame
|
||||
|
||||
return m_parent ?
|
||||
m_parent->find_variable(offset) : // Some previous frame
|
||||
NULL; // Index out of bounds
|
||||
}
|
||||
|
||||
sp_variable_t *
|
||||
sp_pcontext::push_variable(LEX_STRING *name, enum enum_field_types type,
|
||||
sp_param_mode_t mode)
|
||||
|
||||
sp_variable *sp_pcontext::add_variable(THD *thd,
|
||||
LEX_STRING name,
|
||||
enum enum_field_types type,
|
||||
sp_variable::enum_mode mode)
|
||||
{
|
||||
sp_variable_t *p= (sp_variable_t *)sql_alloc(sizeof(sp_variable_t));
|
||||
sp_variable *p=
|
||||
new (thd->mem_root) sp_variable(name, type,mode, current_var_count());
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
++m_max_var_index;
|
||||
|
||||
p->name.str= name->str;
|
||||
p->name.length= name->length;
|
||||
p->type= type;
|
||||
p->mode= mode;
|
||||
p->offset= current_var_count();
|
||||
p->dflt= NULL;
|
||||
if (insert_dynamic(&m_vars, (uchar*)&p))
|
||||
return m_vars.append(p) ? NULL : p;
|
||||
}
|
||||
|
||||
|
||||
sp_label *sp_pcontext::push_label(THD *thd, LEX_STRING name, uint ip)
|
||||
{
|
||||
sp_label *label=
|
||||
new (thd->mem_root) sp_label(name, ip, sp_label::IMPLICIT, this);
|
||||
|
||||
if (!label)
|
||||
return NULL;
|
||||
return p;
|
||||
|
||||
m_labels.push_front(label);
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
sp_label_t *
|
||||
sp_pcontext::push_label(char *name, uint ip)
|
||||
sp_label *sp_pcontext::find_label(LEX_STRING name)
|
||||
{
|
||||
sp_label_t *lab = (sp_label_t *)sql_alloc(sizeof(sp_label_t));
|
||||
|
||||
if (lab)
|
||||
{
|
||||
lab->name= name;
|
||||
lab->ip= ip;
|
||||
lab->type= SP_LAB_IMPL;
|
||||
lab->ctx= this;
|
||||
m_label.push_front(lab);
|
||||
}
|
||||
return lab;
|
||||
}
|
||||
|
||||
sp_label_t *
|
||||
sp_pcontext::find_label(char *name)
|
||||
{
|
||||
List_iterator_fast<sp_label_t> li(m_label);
|
||||
sp_label_t *lab;
|
||||
List_iterator_fast<sp_label> li(m_labels);
|
||||
sp_label *lab;
|
||||
|
||||
while ((lab= li++))
|
||||
if (my_strcasecmp(system_charset_info, name, lab->name) == 0)
|
||||
{
|
||||
if (my_strcasecmp(system_charset_info, name.str, lab->name.str) == 0)
|
||||
return lab;
|
||||
}
|
||||
|
||||
/*
|
||||
Note about exception handlers.
|
||||
|
@ -303,159 +232,253 @@ sp_pcontext::find_label(char *name)
|
|||
In short, a DECLARE HANDLER block can not refer
|
||||
to labels from the parent context, as they are out of scope.
|
||||
*/
|
||||
if (m_parent && (m_label_scope == LABEL_DEFAULT_SCOPE))
|
||||
return m_parent->find_label(name);
|
||||
return NULL;
|
||||
return (m_parent && (m_scope == REGULAR_SCOPE)) ?
|
||||
m_parent->find_label(name) :
|
||||
NULL;
|
||||
}
|
||||
|
||||
int
|
||||
sp_pcontext::push_cond(LEX_STRING *name, sp_cond_type_t *val)
|
||||
|
||||
bool sp_pcontext::add_condition(THD *thd,
|
||||
LEX_STRING name,
|
||||
sp_condition_value *value)
|
||||
{
|
||||
sp_cond_t *p= (sp_cond_t *)sql_alloc(sizeof(sp_cond_t));
|
||||
sp_condition *p= new (thd->mem_root) sp_condition(name, value);
|
||||
|
||||
if (p == NULL)
|
||||
return 1;
|
||||
p->name.str= name->str;
|
||||
p->name.length= name->length;
|
||||
p->val= val;
|
||||
return insert_dynamic(&m_conds, (uchar *)&p);
|
||||
return true;
|
||||
|
||||
return m_conditions.append(p);
|
||||
}
|
||||
|
||||
/*
|
||||
See comment for find_variable() above
|
||||
*/
|
||||
sp_cond_type_t *
|
||||
sp_pcontext::find_cond(LEX_STRING *name, my_bool scoped)
|
||||
|
||||
sp_condition_value *sp_pcontext::find_condition(LEX_STRING name,
|
||||
bool current_scope_only) const
|
||||
{
|
||||
uint i= m_conds.elements;
|
||||
uint i= m_conditions.elements();
|
||||
|
||||
while (i--)
|
||||
{
|
||||
sp_cond_t *p;
|
||||
sp_condition *p= m_conditions.at(i);
|
||||
|
||||
get_dynamic(&m_conds, (uchar*)&p, i);
|
||||
if (my_strnncoll(system_charset_info,
|
||||
(const uchar *)name->str, name->length,
|
||||
(const uchar *)p->name.str, p->name.length) == 0)
|
||||
(const uchar *) name.str, name.length,
|
||||
(const uchar *) p->name.str, p->name.length) == 0)
|
||||
{
|
||||
return p->val;
|
||||
return p->value;
|
||||
}
|
||||
}
|
||||
if (!scoped && m_parent)
|
||||
return m_parent->find_cond(name, scoped);
|
||||
return NULL;
|
||||
|
||||
return (!current_scope_only && m_parent) ?
|
||||
m_parent->find_condition(name, false) :
|
||||
NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
This only searches the current context, for error checking of
|
||||
duplicates.
|
||||
Returns TRUE if found.
|
||||
*/
|
||||
bool
|
||||
sp_pcontext::find_handler(sp_cond_type_t *cond)
|
||||
|
||||
sp_handler *sp_pcontext::add_handler(THD *thd,
|
||||
sp_handler::enum_type type)
|
||||
{
|
||||
uint i= m_handlers.elements;
|
||||
sp_handler *h= new (thd->mem_root) sp_handler(type);
|
||||
|
||||
while (i--)
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
return m_handlers.append(h) ? NULL : h;
|
||||
}
|
||||
|
||||
|
||||
bool sp_pcontext::check_duplicate_handler(
|
||||
const sp_condition_value *cond_value) const
|
||||
{
|
||||
for (int i= 0; i < m_handlers.elements(); ++i)
|
||||
{
|
||||
sp_cond_type_t *p;
|
||||
sp_handler *h= m_handlers.at(i);
|
||||
|
||||
get_dynamic(&m_handlers, (uchar*)&p, i);
|
||||
if (cond->type == p->type)
|
||||
List_iterator_fast<sp_condition_value> li(h->condition_values);
|
||||
sp_condition_value *cv;
|
||||
|
||||
while ((cv= li++))
|
||||
{
|
||||
switch (p->type)
|
||||
if (cond_value->equals(cv))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
sp_handler*
|
||||
sp_pcontext::find_handler(const char *sql_state,
|
||||
uint sql_errno,
|
||||
Sql_condition::enum_warning_level level) const
|
||||
{
|
||||
sp_handler *found_handler= NULL;
|
||||
sp_condition_value *found_cv= NULL;
|
||||
|
||||
for (int i= 0; i < m_handlers.elements(); ++i)
|
||||
{
|
||||
sp_handler *h= m_handlers.at(i);
|
||||
|
||||
List_iterator_fast<sp_condition_value> li(h->condition_values);
|
||||
sp_condition_value *cv;
|
||||
|
||||
while ((cv= li++))
|
||||
{
|
||||
switch (cv->type)
|
||||
{
|
||||
case sp_cond_type_t::number:
|
||||
if (cond->mysqlerr == p->mysqlerr)
|
||||
return TRUE;
|
||||
break;
|
||||
case sp_cond_type_t::state:
|
||||
if (strcmp(cond->sqlstate, p->sqlstate) == 0)
|
||||
return TRUE;
|
||||
break;
|
||||
default:
|
||||
return TRUE;
|
||||
case sp_condition_value::ERROR_CODE:
|
||||
if (sql_errno == cv->mysqlerr &&
|
||||
(!found_cv ||
|
||||
found_cv->type > sp_condition_value::ERROR_CODE))
|
||||
{
|
||||
found_cv= cv;
|
||||
found_handler= h;
|
||||
}
|
||||
break;
|
||||
|
||||
case sp_condition_value::SQLSTATE:
|
||||
if (strcmp(sql_state, cv->sql_state) == 0 &&
|
||||
(!found_cv ||
|
||||
found_cv->type > sp_condition_value::SQLSTATE))
|
||||
{
|
||||
found_cv= cv;
|
||||
found_handler= h;
|
||||
}
|
||||
break;
|
||||
|
||||
case sp_condition_value::WARNING:
|
||||
if ((is_sqlstate_warning(sql_state) ||
|
||||
level == Sql_condition::WARN_LEVEL_WARN) && !found_cv)
|
||||
{
|
||||
found_cv= cv;
|
||||
found_handler= h;
|
||||
}
|
||||
break;
|
||||
|
||||
case sp_condition_value::NOT_FOUND:
|
||||
if (is_sqlstate_not_found(sql_state) && !found_cv)
|
||||
{
|
||||
found_cv= cv;
|
||||
found_handler= h;
|
||||
}
|
||||
break;
|
||||
|
||||
case sp_condition_value::EXCEPTION:
|
||||
if (is_sqlstate_exception(sql_state) &&
|
||||
level == Sql_condition::WARN_LEVEL_ERROR && !found_cv)
|
||||
{
|
||||
found_cv= cv;
|
||||
found_handler= h;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
if (found_handler)
|
||||
return found_handler;
|
||||
|
||||
|
||||
// There is no appropriate handler in this parsing context. We need to look up
|
||||
// in parent contexts. There might be two cases here:
|
||||
//
|
||||
// 1. The current context has REGULAR_SCOPE. That means, it's a simple
|
||||
// BEGIN..END block:
|
||||
// ...
|
||||
// BEGIN
|
||||
// ... # We're here.
|
||||
// END
|
||||
// ...
|
||||
// In this case we simply call find_handler() on parent's context recursively.
|
||||
//
|
||||
// 2. The current context has HANDLER_SCOPE. That means, we're inside an
|
||||
// SQL-handler block:
|
||||
// ...
|
||||
// DECLARE ... HANDLER FOR ...
|
||||
// BEGIN
|
||||
// ... # We're here.
|
||||
// END
|
||||
// ...
|
||||
// In this case we can not just call parent's find_handler(), because
|
||||
// parent's handler don't catch conditions from this scope. Instead, we should
|
||||
// try to find first parent context (we might have nested handler
|
||||
// declarations), which has REGULAR_SCOPE (i.e. which is regular BEGIN..END
|
||||
// block).
|
||||
|
||||
const sp_pcontext *p= this;
|
||||
|
||||
while (p && p->m_scope == HANDLER_SCOPE)
|
||||
p= p->m_parent;
|
||||
|
||||
if (!p || !p->m_parent)
|
||||
return NULL;
|
||||
|
||||
return p->m_parent->find_handler(sql_state, sql_errno, level);
|
||||
}
|
||||
|
||||
int
|
||||
sp_pcontext::push_cursor(LEX_STRING *name)
|
||||
{
|
||||
LEX_STRING n;
|
||||
|
||||
if (m_cursors.elements == m_max_cursor_index)
|
||||
m_max_cursor_index+= 1;
|
||||
n.str= name->str;
|
||||
n.length= name->length;
|
||||
return insert_dynamic(&m_cursors, (uchar *)&n);
|
||||
bool sp_pcontext::add_cursor(LEX_STRING name)
|
||||
{
|
||||
if (m_cursors.elements() == (int) m_max_cursor_index)
|
||||
++m_max_cursor_index;
|
||||
|
||||
return m_cursors.append(name);
|
||||
}
|
||||
|
||||
/*
|
||||
See comment for find_variable() above
|
||||
*/
|
||||
my_bool
|
||||
sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped)
|
||||
|
||||
bool sp_pcontext::find_cursor(LEX_STRING name,
|
||||
uint *poff,
|
||||
bool current_scope_only) const
|
||||
{
|
||||
uint i= m_cursors.elements;
|
||||
uint i= m_cursors.elements();
|
||||
|
||||
while (i--)
|
||||
{
|
||||
LEX_STRING n;
|
||||
LEX_STRING n= m_cursors.at(i);
|
||||
|
||||
get_dynamic(&m_cursors, (uchar*)&n, i);
|
||||
if (my_strnncoll(system_charset_info,
|
||||
(const uchar *)name->str, name->length,
|
||||
(const uchar *)n.str, n.length) == 0)
|
||||
(const uchar *) name.str, name.length,
|
||||
(const uchar *) n.str, n.length) == 0)
|
||||
{
|
||||
*poff= m_cursor_offset + i;
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!scoped && m_parent)
|
||||
return m_parent->find_cursor(name, poff, scoped);
|
||||
return FALSE;
|
||||
|
||||
return (!current_scope_only && m_parent) ?
|
||||
m_parent->find_cursor(name, poff, false) :
|
||||
false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sp_pcontext::retrieve_field_definitions(List<Create_field> *field_def_lst)
|
||||
void sp_pcontext::retrieve_field_definitions(
|
||||
List<Create_field> *field_def_lst) const
|
||||
{
|
||||
/* Put local/context fields in the result list. */
|
||||
|
||||
for (uint i = 0; i < m_vars.elements; ++i)
|
||||
for (int i= 0; i < m_vars.elements(); ++i)
|
||||
{
|
||||
sp_variable_t *var_def;
|
||||
get_dynamic(&m_vars, (uchar*) &var_def, i);
|
||||
sp_variable *var_def= m_vars.at(i);
|
||||
|
||||
field_def_lst->push_back(&var_def->field_def);
|
||||
}
|
||||
|
||||
/* Put the fields of the enclosed contexts in the result list. */
|
||||
|
||||
List_iterator_fast<sp_pcontext> li(m_children);
|
||||
sp_pcontext *ctx;
|
||||
|
||||
while ((ctx = li++))
|
||||
ctx->retrieve_field_definitions(field_def_lst);
|
||||
for (int i= 0; i < m_children.elements(); ++i)
|
||||
m_children.at(i)->retrieve_field_definitions(field_def_lst);
|
||||
}
|
||||
|
||||
/*
|
||||
Find a cursor by offset from the top.
|
||||
This is only used for debugging.
|
||||
*/
|
||||
my_bool
|
||||
sp_pcontext::find_cursor(uint offset, LEX_STRING *n)
|
||||
|
||||
const LEX_STRING *sp_pcontext::find_cursor(uint offset) const
|
||||
{
|
||||
if (m_cursor_offset <= offset &&
|
||||
offset < m_cursor_offset + m_cursors.elements)
|
||||
{ // This frame
|
||||
get_dynamic(&m_cursors, (uchar*)n, offset - m_cursor_offset);
|
||||
return TRUE;
|
||||
offset < m_cursor_offset + m_cursors.elements())
|
||||
{
|
||||
return &m_cursors.at(offset - m_cursor_offset); // This frame
|
||||
}
|
||||
if (m_parent)
|
||||
return m_parent->find_cursor(offset, n); // Some previous frame
|
||||
return FALSE; // index out of bounds
|
||||
|
||||
return m_parent ?
|
||||
m_parent->find_cursor(offset) : // Some previous frame
|
||||
NULL; // Index out of bounds
|
||||
}
|
||||
|
|
|
@ -24,438 +24,541 @@
|
|||
#include "sql_string.h" // LEX_STRING
|
||||
#include "mysql_com.h" // enum_field_types
|
||||
#include "field.h" // Create_field
|
||||
#include "sql_array.h" // Dynamic_array
|
||||
|
||||
class sp_pcontext;
|
||||
|
||||
typedef enum
|
||||
/// This class represents a stored program variable or a parameter
|
||||
/// (also referenced as 'SP-variable').
|
||||
|
||||
class sp_variable : public Sql_alloc
|
||||
{
|
||||
sp_param_in,
|
||||
sp_param_out,
|
||||
sp_param_inout
|
||||
} sp_param_mode_t;
|
||||
public:
|
||||
enum enum_mode
|
||||
{
|
||||
MODE_IN,
|
||||
MODE_OUT,
|
||||
MODE_INOUT
|
||||
};
|
||||
|
||||
typedef struct sp_variable
|
||||
{
|
||||
/// Name of the SP-variable.
|
||||
LEX_STRING name;
|
||||
|
||||
/// Field-type of the SP-variable.
|
||||
enum enum_field_types type;
|
||||
sp_param_mode_t mode;
|
||||
|
||||
/*
|
||||
offset -- this the index to the variable's value in the runtime frame.
|
||||
This is calculated during parsing and used when creating sp_instr_set
|
||||
instructions and Item_splocal items.
|
||||
I.e. values are set/referred by array indexing in runtime.
|
||||
*/
|
||||
|
||||
/// Mode of the SP-variable.
|
||||
enum_mode mode;
|
||||
|
||||
/// The index to the variable's value in the runtime frame.
|
||||
///
|
||||
/// It is calculated during parsing and used when creating sp_instr_set
|
||||
/// instructions and Item_splocal items. I.e. values are set/referred by
|
||||
/// array indexing in runtime.
|
||||
uint offset;
|
||||
|
||||
Item *dflt;
|
||||
/// Default value of the SP-variable (if any).
|
||||
Item *default_value;
|
||||
|
||||
/// Full type information (field meta-data) of the SP-variable.
|
||||
Create_field field_def;
|
||||
} sp_variable_t;
|
||||
|
||||
|
||||
#define SP_LAB_IMPL 0 // Implicit label generated by parser
|
||||
#define SP_LAB_BEGIN 1 // Label at BEGIN
|
||||
#define SP_LAB_ITER 2 // Label at iteration control
|
||||
|
||||
/*
|
||||
An SQL/PSM label. Can refer to the identifier used with the
|
||||
"label_name:" construct which may precede some SQL/PSM statements, or
|
||||
to an implicit implementation-dependent identifier which the parser
|
||||
inserts before a high-level flow control statement such as
|
||||
IF/WHILE/REPEAT/LOOP, when such statement is rewritten into
|
||||
a combination of low-level jump/jump_if instructions and labels.
|
||||
*/
|
||||
|
||||
typedef struct sp_label
|
||||
{
|
||||
char *name;
|
||||
uint ip; // Instruction index
|
||||
int type; // begin/iter or ref/free
|
||||
sp_pcontext *ctx; // The label's context
|
||||
} sp_label_t;
|
||||
|
||||
typedef struct sp_cond_type
|
||||
{
|
||||
enum { number, state, warning, notfound, exception } type;
|
||||
char sqlstate[SQLSTATE_LENGTH+1];
|
||||
uint mysqlerr;
|
||||
} sp_cond_type_t;
|
||||
|
||||
/*
|
||||
Sanity check for SQLSTATEs. Will not check if it's really an existing
|
||||
state (there are just too many), but will check length bad characters.
|
||||
*/
|
||||
extern bool
|
||||
sp_cond_check(LEX_STRING *sqlstate);
|
||||
|
||||
typedef struct sp_cond
|
||||
{
|
||||
LEX_STRING name;
|
||||
sp_cond_type_t *val;
|
||||
} sp_cond_t;
|
||||
|
||||
/**
|
||||
The scope of a label in Stored Procedures,
|
||||
for name resolution of labels in a parsing context.
|
||||
*/
|
||||
enum label_scope_type
|
||||
{
|
||||
/**
|
||||
The labels declared in a parent context are in scope.
|
||||
*/
|
||||
LABEL_DEFAULT_SCOPE,
|
||||
/**
|
||||
The labels declared in a parent context are not in scope.
|
||||
*/
|
||||
LABEL_HANDLER_SCOPE
|
||||
public:
|
||||
sp_variable(LEX_STRING _name, enum_field_types _type, enum_mode _mode,
|
||||
uint _offset)
|
||||
:Sql_alloc(),
|
||||
name(_name),
|
||||
type(_type),
|
||||
mode(_mode),
|
||||
offset(_offset),
|
||||
default_value(NULL)
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
The parse-time context, used to keep track of declared variables/parameters,
|
||||
conditions, handlers, cursors and labels, during parsing.
|
||||
sp_contexts are organized as a tree, with one object for each begin-end
|
||||
block, one object for each exception handler,
|
||||
plus a root-context for the parameters.
|
||||
This is used during parsing for looking up defined names (e.g. declared
|
||||
variables and visible labels), for error checking, and to calculate offsets
|
||||
to be used at runtime. (During execution variable values, active handlers
|
||||
and cursors, etc, are referred to by an index in a stack.)
|
||||
Parsing contexts for exception handlers limit the visibility of labels.
|
||||
The pcontext tree is also kept during execution and is used for error
|
||||
checking (e.g. correct number of parameters), and in the future, used by
|
||||
the debugger.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// This class represents an SQL/PSM label. Can refer to the identifier
|
||||
/// used with the "label_name:" construct which may precede some SQL/PSM
|
||||
/// statements, or to an implicit implementation-dependent identifier which
|
||||
/// the parser inserts before a high-level flow control statement such as
|
||||
/// IF/WHILE/REPEAT/LOOP, when such statement is rewritten into a
|
||||
/// combination of low-level jump/jump_if instructions and labels.
|
||||
|
||||
class sp_label : public Sql_alloc
|
||||
{
|
||||
public:
|
||||
enum enum_type
|
||||
{
|
||||
/// Implicit label generated by parser.
|
||||
IMPLICIT,
|
||||
|
||||
/// Label at BEGIN.
|
||||
BEGIN,
|
||||
|
||||
/// Label at iteration control
|
||||
ITERATION
|
||||
};
|
||||
|
||||
/// Name of the label.
|
||||
LEX_STRING name;
|
||||
|
||||
/// Instruction pointer of the label.
|
||||
uint ip;
|
||||
|
||||
/// Type of the label.
|
||||
enum_type type;
|
||||
|
||||
/// Scope of the label.
|
||||
class sp_pcontext *ctx;
|
||||
|
||||
public:
|
||||
sp_label(LEX_STRING _name, uint _ip, enum_type _type, sp_pcontext *_ctx)
|
||||
:Sql_alloc(),
|
||||
name(_name),
|
||||
ip(_ip),
|
||||
type(_type),
|
||||
ctx(_ctx)
|
||||
{ }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// This class represents condition-value term in DECLARE CONDITION or
|
||||
/// DECLARE HANDLER statements. sp_condition_value has little to do with
|
||||
/// SQL-conditions.
|
||||
///
|
||||
/// In some sense, this class is a union -- a set of filled attributes
|
||||
/// depends on the sp_condition_value::type value.
|
||||
|
||||
class sp_condition_value : public Sql_alloc
|
||||
{
|
||||
public:
|
||||
enum enum_type
|
||||
{
|
||||
ERROR_CODE,
|
||||
SQLSTATE,
|
||||
WARNING,
|
||||
NOT_FOUND,
|
||||
EXCEPTION
|
||||
};
|
||||
|
||||
/// Type of the condition value.
|
||||
enum_type type;
|
||||
|
||||
/// SQLSTATE of the condition value.
|
||||
char sql_state[SQLSTATE_LENGTH+1];
|
||||
|
||||
/// MySQL error code of the condition value.
|
||||
uint mysqlerr;
|
||||
|
||||
public:
|
||||
sp_condition_value(uint _mysqlerr)
|
||||
:Sql_alloc(),
|
||||
type(ERROR_CODE),
|
||||
mysqlerr(_mysqlerr)
|
||||
{ }
|
||||
|
||||
sp_condition_value(const char *_sql_state)
|
||||
:Sql_alloc(),
|
||||
type(SQLSTATE)
|
||||
{
|
||||
memcpy(sql_state, _sql_state, SQLSTATE_LENGTH);
|
||||
sql_state[SQLSTATE_LENGTH]= 0;
|
||||
}
|
||||
|
||||
sp_condition_value(enum_type _type)
|
||||
:Sql_alloc(),
|
||||
type(_type)
|
||||
{
|
||||
DBUG_ASSERT(type != ERROR_CODE && type != SQLSTATE);
|
||||
}
|
||||
|
||||
/// Check if two instances of sp_condition_value are equal or not.
|
||||
///
|
||||
/// @param cv another instance of sp_condition_value to check.
|
||||
///
|
||||
/// @return true if the instances are equal, false otherwise.
|
||||
bool equals(const sp_condition_value *cv) const;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// This class represents 'DECLARE CONDITION' statement.
|
||||
/// sp_condition has little to do with SQL-conditions.
|
||||
|
||||
class sp_condition : public Sql_alloc
|
||||
{
|
||||
public:
|
||||
/// Name of the condition.
|
||||
LEX_STRING name;
|
||||
|
||||
/// Value of the condition.
|
||||
sp_condition_value *value;
|
||||
|
||||
public:
|
||||
sp_condition(LEX_STRING _name, sp_condition_value *_value)
|
||||
:Sql_alloc(),
|
||||
name(_name),
|
||||
value(_value)
|
||||
{ }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// This class represents 'DECLARE HANDLER' statement.
|
||||
|
||||
class sp_handler : public Sql_alloc
|
||||
{
|
||||
public:
|
||||
/// Enumeration of possible handler types.
|
||||
/// Note: UNDO handlers are not (and have never been) supported.
|
||||
enum enum_type
|
||||
{
|
||||
EXIT,
|
||||
CONTINUE
|
||||
};
|
||||
|
||||
/// Handler type.
|
||||
enum_type type;
|
||||
|
||||
/// Conditions caught by this handler.
|
||||
List<sp_condition_value> condition_values;
|
||||
|
||||
public:
|
||||
/// The constructor.
|
||||
///
|
||||
/// @param _type SQL-handler type.
|
||||
sp_handler(enum_type _type)
|
||||
:Sql_alloc(),
|
||||
type(_type)
|
||||
{ }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// The class represents parse-time context, which keeps track of declared
|
||||
/// variables/parameters, conditions, handlers, cursors and labels.
|
||||
///
|
||||
/// sp_context objects are organized in a tree according to the following
|
||||
/// rules:
|
||||
/// - one sp_pcontext object corresponds for for each BEGIN..END block;
|
||||
/// - one sp_pcontext object corresponds for each exception handler;
|
||||
/// - one additional sp_pcontext object is created to contain
|
||||
/// Stored Program parameters.
|
||||
///
|
||||
/// sp_pcontext objects are used both at parse-time and at runtime.
|
||||
///
|
||||
/// During the parsing stage sp_pcontext objects are used:
|
||||
/// - to look up defined names (e.g. declared variables and visible
|
||||
/// labels);
|
||||
/// - to check for duplicates;
|
||||
/// - for error checking;
|
||||
/// - to calculate offsets to be used at runtime.
|
||||
///
|
||||
/// During the runtime phase, a tree of sp_pcontext objects is used:
|
||||
/// - for error checking (e.g. to check correct number of parameters);
|
||||
/// - to resolve SQL-handlers.
|
||||
|
||||
class sp_pcontext : public Sql_alloc
|
||||
{
|
||||
public:
|
||||
enum enum_scope
|
||||
{
|
||||
/// REGULAR_SCOPE designates regular BEGIN ... END blocks.
|
||||
REGULAR_SCOPE,
|
||||
|
||||
/**
|
||||
Constructor.
|
||||
Builds a parsing context root node.
|
||||
*/
|
||||
/// HANDLER_SCOPE designates SQL-handler blocks.
|
||||
HANDLER_SCOPE
|
||||
};
|
||||
|
||||
public:
|
||||
sp_pcontext();
|
||||
~sp_pcontext();
|
||||
|
||||
// Free memory
|
||||
void
|
||||
destroy();
|
||||
|
||||
/**
|
||||
Create and push a new context in the tree.
|
||||
@param label_scope label scope for the new parsing context
|
||||
@return the node created
|
||||
*/
|
||||
sp_pcontext *
|
||||
push_context(label_scope_type label_scope);
|
||||
/// Create and push a new context in the tree.
|
||||
|
||||
/**
|
||||
Pop a node from the parsing context tree.
|
||||
@return the parent node
|
||||
*/
|
||||
sp_pcontext *
|
||||
pop_context();
|
||||
/// @param thd thread context.
|
||||
/// @param scope scope of the new parsing context.
|
||||
/// @return the node created.
|
||||
sp_pcontext *push_context(THD *thd, enum_scope scope);
|
||||
|
||||
sp_pcontext *
|
||||
parent_context()
|
||||
/// Pop a node from the parsing context tree.
|
||||
/// @return the parent node.
|
||||
sp_pcontext *pop_context();
|
||||
|
||||
sp_pcontext *parent_context() const
|
||||
{ return m_parent; }
|
||||
|
||||
/// Calculate and return the number of handlers to pop between the given
|
||||
/// context and this one.
|
||||
///
|
||||
/// @param ctx the other parsing context.
|
||||
/// @param exclusive specifies if the last scope should be excluded.
|
||||
///
|
||||
/// @return the number of handlers to pop between the given context and
|
||||
/// this one. If 'exclusive' is true, don't count the last scope we are
|
||||
/// leaving; this is used for LEAVE where we will jump to the hpop
|
||||
/// instructions.
|
||||
uint diff_handlers(const sp_pcontext *ctx, bool exclusive) const;
|
||||
|
||||
/// Calculate and return the number of cursors to pop between the given
|
||||
/// context and this one.
|
||||
///
|
||||
/// @param ctx the other parsing context.
|
||||
/// @param exclusive specifies if the last scope should be excluded.
|
||||
///
|
||||
/// @return the number of cursors to pop between the given context and
|
||||
/// this one. If 'exclusive' is true, don't count the last scope we are
|
||||
/// leaving; this is used for LEAVE where we will jump to the cpop
|
||||
/// instructions.
|
||||
uint diff_cursors(const sp_pcontext *ctx, bool exclusive) const;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// SP-variables (parameters and variables).
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// @return the maximum number of variables used in this and all child
|
||||
/// contexts. For the root parsing context, this gives us the number of
|
||||
/// slots needed for variables during the runtime phase.
|
||||
uint max_var_index() const
|
||||
{ return m_max_var_index; }
|
||||
|
||||
/// @return the current number of variables used in the parent contexts
|
||||
/// (from the root), including this context.
|
||||
uint current_var_count() const
|
||||
{ return m_var_offset + m_vars.elements(); }
|
||||
|
||||
/// @return the number of variables in this context alone.
|
||||
uint context_var_count() const
|
||||
{ return m_vars.elements(); }
|
||||
|
||||
/// @return map index in this parsing context to runtime offset.
|
||||
uint var_context2runtime(uint i) const
|
||||
{ return m_var_offset + i; }
|
||||
|
||||
/// Add SP-variable to the parsing context.
|
||||
///
|
||||
/// @param thd Thread context.
|
||||
/// @param name Name of the SP-variable.
|
||||
/// @param type Type of the SP-variable.
|
||||
/// @param mode Mode of the SP-variable.
|
||||
///
|
||||
/// @return instance of newly added SP-variable.
|
||||
sp_variable *add_variable(THD *thd,
|
||||
LEX_STRING name,
|
||||
enum enum_field_types type,
|
||||
sp_variable::enum_mode mode);
|
||||
|
||||
/// Retrieve full type information about SP-variables in this parsing
|
||||
/// context and its children.
|
||||
///
|
||||
/// @param field_def_lst[out] Container to store type information.
|
||||
void retrieve_field_definitions(List<Create_field> *field_def_lst) const;
|
||||
|
||||
/// Find SP-variable by name.
|
||||
///
|
||||
/// The function does a linear search (from newer to older variables,
|
||||
/// in case we have shadowed names).
|
||||
///
|
||||
/// The function is called only at parsing time.
|
||||
///
|
||||
/// @param name Variable name.
|
||||
/// @param current_scope_only A flag if we search only in current scope.
|
||||
///
|
||||
/// @return instance of found SP-variable, or NULL if not found.
|
||||
sp_variable *find_variable(LEX_STRING name, bool current_scope_only) const;
|
||||
|
||||
/// Find SP-variable by the offset in the root parsing context.
|
||||
///
|
||||
/// The function is used for two things:
|
||||
/// - When evaluating parameters at the beginning, and setting out parameters
|
||||
/// at the end, of invocation. (Top frame only, so no recursion then.)
|
||||
/// - For printing of sp_instr_set. (Debug mode only.)
|
||||
///
|
||||
/// @param offset Variable offset in the root parsing context.
|
||||
///
|
||||
/// @return instance of found SP-variable, or NULL if not found.
|
||||
sp_variable *find_variable(uint offset) const;
|
||||
|
||||
/// Set the current scope boundary (for default values).
|
||||
///
|
||||
/// @param n The number of variables to skip.
|
||||
void declare_var_boundary(uint n)
|
||||
{ m_pboundary= n; }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// CASE expressions.
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int register_case_expr()
|
||||
{ return m_num_case_exprs++; }
|
||||
|
||||
int get_num_case_exprs() const
|
||||
{ return m_num_case_exprs; }
|
||||
|
||||
bool push_case_expr_id(int case_expr_id)
|
||||
{ return m_case_expr_ids.append(case_expr_id); }
|
||||
|
||||
void pop_case_expr_id()
|
||||
{ m_case_expr_ids.pop(); }
|
||||
|
||||
int get_current_case_expr_id() const
|
||||
{ return *m_case_expr_ids.back(); }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// Labels.
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
sp_label *push_label(THD *thd, LEX_STRING name, uint ip);
|
||||
|
||||
sp_label *find_label(LEX_STRING name);
|
||||
|
||||
sp_label *last_label()
|
||||
{
|
||||
return m_parent;
|
||||
sp_label *label= m_labels.head();
|
||||
|
||||
if (!label && m_parent)
|
||||
label= m_parent->last_label();
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
/*
|
||||
Number of handlers/cursors to pop between this context and 'ctx'.
|
||||
If 'exclusive' is true, don't count the last block we are leaving;
|
||||
this is used for LEAVE where we will jump to the cpop/hpop instructions.
|
||||
*/
|
||||
uint
|
||||
diff_handlers(sp_pcontext *ctx, bool exclusive);
|
||||
uint
|
||||
diff_cursors(sp_pcontext *ctx, bool exclusive);
|
||||
sp_label *pop_label()
|
||||
{ return m_labels.pop(); }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// Conditions.
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Parameters and variables
|
||||
//
|
||||
bool add_condition(THD *thd, LEX_STRING name, sp_condition_value *value);
|
||||
|
||||
/*
|
||||
The maximum number of variables used in this and all child contexts
|
||||
In the root, this gives us the number of slots needed for variables
|
||||
during execution.
|
||||
*/
|
||||
inline uint
|
||||
max_var_index()
|
||||
{
|
||||
return m_max_var_index;
|
||||
}
|
||||
/// See comment for find_variable() above.
|
||||
sp_condition_value *find_condition(LEX_STRING name,
|
||||
bool current_scope_only) const;
|
||||
|
||||
/*
|
||||
The current number of variables used in the parents (from the root),
|
||||
including this context.
|
||||
*/
|
||||
inline uint
|
||||
current_var_count()
|
||||
{
|
||||
return m_var_offset + m_vars.elements;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// Handlers.
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* The number of variables in this context alone */
|
||||
inline uint
|
||||
context_var_count()
|
||||
{
|
||||
return m_vars.elements;
|
||||
}
|
||||
sp_handler *add_handler(THD* thd, sp_handler::enum_type type);
|
||||
|
||||
/* Map index in this pcontext to runtime offset */
|
||||
inline uint
|
||||
var_context2runtime(uint i)
|
||||
{
|
||||
return m_var_offset + i;
|
||||
}
|
||||
/// This is an auxilary parsing-time function to check if an SQL-handler
|
||||
/// exists in the current parsing context (current scope) for the given
|
||||
/// SQL-condition. This function is used to check for duplicates during
|
||||
/// the parsing phase.
|
||||
///
|
||||
/// This function can not be used during the runtime phase to check
|
||||
/// SQL-handler existence because it searches for the SQL-handler in the
|
||||
/// current scope only (during runtime, current and parent scopes
|
||||
/// should be checked according to the SQL-handler resolution rules).
|
||||
///
|
||||
/// @param condition_value the handler condition value
|
||||
/// (not SQL-condition!).
|
||||
///
|
||||
/// @retval true if such SQL-handler exists.
|
||||
/// @retval false otherwise.
|
||||
bool check_duplicate_handler(const sp_condition_value *cond_value) const;
|
||||
|
||||
/* Set type of variable. 'i' is the offset from the top */
|
||||
inline void
|
||||
set_type(uint i, enum enum_field_types type)
|
||||
{
|
||||
sp_variable_t *p= find_variable(i);
|
||||
/// Find an SQL handler for the given SQL condition according to the
|
||||
/// SQL-handler resolution rules. This function is used at runtime.
|
||||
///
|
||||
/// @param sql_state The SQL condition state
|
||||
/// @param sql_errno The error code
|
||||
/// @param level The SQL condition level
|
||||
///
|
||||
/// @return a pointer to the found SQL-handler or NULL.
|
||||
sp_handler *find_handler(const char *sql_state,
|
||||
uint sql_errno,
|
||||
Sql_condition::enum_warning_level level) const;
|
||||
|
||||
if (p)
|
||||
p->type= type;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// Cursors.
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* Set default value of variable. 'i' is the offset from the top */
|
||||
inline void
|
||||
set_default(uint i, Item *it)
|
||||
{
|
||||
sp_variable_t *p= find_variable(i);
|
||||
bool add_cursor(LEX_STRING name);
|
||||
|
||||
if (p)
|
||||
p->dflt= it;
|
||||
}
|
||||
/// See comment for find_variable() above.
|
||||
bool find_cursor(LEX_STRING name, uint *poff, bool current_scope_only) const;
|
||||
|
||||
sp_variable_t *
|
||||
push_variable(LEX_STRING *name, enum enum_field_types type,
|
||||
sp_param_mode_t mode);
|
||||
/// Find cursor by offset (for debugging only).
|
||||
const LEX_STRING *find_cursor(uint offset) const;
|
||||
|
||||
/*
|
||||
Retrieve definitions of fields from the current context and its
|
||||
children.
|
||||
*/
|
||||
void
|
||||
retrieve_field_definitions(List<Create_field> *field_def_lst);
|
||||
uint max_cursor_index() const
|
||||
{ return m_max_cursor_index + m_cursors.elements(); }
|
||||
|
||||
// Find by name
|
||||
sp_variable_t *
|
||||
find_variable(LEX_STRING *name, my_bool scoped=0);
|
||||
|
||||
// Find by offset (from the top)
|
||||
sp_variable_t *
|
||||
find_variable(uint offset);
|
||||
|
||||
/*
|
||||
Set the current scope boundary (for default values).
|
||||
The argument is the number of variables to skip.
|
||||
*/
|
||||
inline void
|
||||
declare_var_boundary(uint n)
|
||||
{
|
||||
m_pboundary= n;
|
||||
}
|
||||
|
||||
/*
|
||||
CASE expressions support.
|
||||
*/
|
||||
|
||||
inline int
|
||||
register_case_expr()
|
||||
{
|
||||
return m_num_case_exprs++;
|
||||
}
|
||||
|
||||
inline int
|
||||
get_num_case_exprs() const
|
||||
{
|
||||
return m_num_case_exprs;
|
||||
}
|
||||
|
||||
inline bool
|
||||
push_case_expr_id(int case_expr_id)
|
||||
{
|
||||
return insert_dynamic(&m_case_expr_id_lst, (uchar*) &case_expr_id);
|
||||
}
|
||||
|
||||
inline void
|
||||
pop_case_expr_id()
|
||||
{
|
||||
pop_dynamic(&m_case_expr_id_lst);
|
||||
}
|
||||
|
||||
inline int
|
||||
get_current_case_expr_id() const
|
||||
{
|
||||
int case_expr_id;
|
||||
|
||||
get_dynamic((DYNAMIC_ARRAY*)&m_case_expr_id_lst, (uchar*) &case_expr_id,
|
||||
m_case_expr_id_lst.elements - 1);
|
||||
|
||||
return case_expr_id;
|
||||
}
|
||||
|
||||
//
|
||||
// Labels
|
||||
//
|
||||
|
||||
sp_label_t *
|
||||
push_label(char *name, uint ip);
|
||||
|
||||
sp_label_t *
|
||||
find_label(char *name);
|
||||
|
||||
inline sp_label_t *
|
||||
last_label()
|
||||
{
|
||||
sp_label_t *lab= m_label.head();
|
||||
|
||||
if (!lab && m_parent)
|
||||
lab= m_parent->last_label();
|
||||
return lab;
|
||||
}
|
||||
|
||||
inline sp_label_t *
|
||||
pop_label()
|
||||
{
|
||||
return m_label.pop();
|
||||
}
|
||||
|
||||
//
|
||||
// Conditions
|
||||
//
|
||||
|
||||
int
|
||||
push_cond(LEX_STRING *name, sp_cond_type_t *val);
|
||||
|
||||
sp_cond_type_t *
|
||||
find_cond(LEX_STRING *name, my_bool scoped=0);
|
||||
|
||||
//
|
||||
// Handlers
|
||||
//
|
||||
|
||||
inline void
|
||||
push_handler(sp_cond_type_t *cond)
|
||||
{
|
||||
insert_dynamic(&m_handlers, (uchar*)&cond);
|
||||
}
|
||||
|
||||
bool
|
||||
find_handler(sp_cond_type *cond);
|
||||
|
||||
inline uint
|
||||
max_handler_index()
|
||||
{
|
||||
return m_max_handler_index + m_context_handlers;
|
||||
}
|
||||
|
||||
inline void
|
||||
add_handlers(uint n)
|
||||
{
|
||||
m_context_handlers+= n;
|
||||
}
|
||||
|
||||
//
|
||||
// Cursors
|
||||
//
|
||||
|
||||
int
|
||||
push_cursor(LEX_STRING *name);
|
||||
|
||||
my_bool
|
||||
find_cursor(LEX_STRING *name, uint *poff, my_bool scoped=0);
|
||||
|
||||
/* Find by offset (for debugging only) */
|
||||
my_bool
|
||||
find_cursor(uint offset, LEX_STRING *n);
|
||||
|
||||
inline uint
|
||||
max_cursor_index()
|
||||
{
|
||||
return m_max_cursor_index + m_cursors.elements;
|
||||
}
|
||||
|
||||
inline uint
|
||||
current_cursor_count()
|
||||
{
|
||||
return m_cursor_offset + m_cursors.elements;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
Constructor for a tree node.
|
||||
@param prev the parent parsing context
|
||||
@param label_scope label_scope for this parsing context
|
||||
*/
|
||||
sp_pcontext(sp_pcontext *prev, label_scope_type label_scope);
|
||||
|
||||
/*
|
||||
m_max_var_index -- number of variables (including all types of arguments)
|
||||
in this context including all children contexts.
|
||||
|
||||
m_max_var_index >= m_vars.elements.
|
||||
|
||||
m_max_var_index of the root parsing context contains number of all
|
||||
variables (including arguments) in all enclosed contexts.
|
||||
*/
|
||||
uint m_max_var_index;
|
||||
|
||||
// The maximum sub context's framesizes
|
||||
uint m_max_cursor_index;
|
||||
uint m_max_handler_index;
|
||||
uint m_context_handlers; // No. of handlers in this context
|
||||
uint current_cursor_count() const
|
||||
{ return m_cursor_offset + m_cursors.elements(); }
|
||||
|
||||
private:
|
||||
/// Constructor for a tree node.
|
||||
/// @param prev the parent parsing context
|
||||
/// @param scope scope of this parsing context
|
||||
sp_pcontext(sp_pcontext *prev, enum_scope scope);
|
||||
|
||||
sp_pcontext *m_parent; // Parent context
|
||||
void init(uint var_offset, uint cursor_offset, int num_case_expressions);
|
||||
|
||||
/*
|
||||
m_var_offset -- this is an index of the first variable in this
|
||||
parsing context.
|
||||
|
||||
m_var_offset is 0 for root context.
|
||||
/* Prevent use of these */
|
||||
sp_pcontext(const sp_pcontext &);
|
||||
void operator=(sp_pcontext &);
|
||||
|
||||
Since now each variable is stored in separate place, no reuse is done,
|
||||
so m_var_offset is different for all enclosed contexts.
|
||||
*/
|
||||
private:
|
||||
/// m_max_var_index -- number of variables (including all types of arguments)
|
||||
/// in this context including all children contexts.
|
||||
///
|
||||
/// m_max_var_index >= m_vars.elements().
|
||||
///
|
||||
/// m_max_var_index of the root parsing context contains number of all
|
||||
/// variables (including arguments) in all enclosed contexts.
|
||||
uint m_max_var_index;
|
||||
|
||||
/// The maximum sub context's framesizes.
|
||||
uint m_max_cursor_index;
|
||||
|
||||
/// Parent context.
|
||||
sp_pcontext *m_parent;
|
||||
|
||||
/// An index of the first SP-variable in this parsing context. The index
|
||||
/// belongs to a runtime table of SP-variables.
|
||||
///
|
||||
/// Note:
|
||||
/// - m_var_offset is 0 for root parsing context;
|
||||
/// - m_var_offset is different for all nested parsing contexts.
|
||||
uint m_var_offset;
|
||||
|
||||
uint m_cursor_offset; // Cursor offset for this context
|
||||
/// Cursor offset for this context.
|
||||
uint m_cursor_offset;
|
||||
|
||||
/*
|
||||
Boundary for finding variables in this context. This is the number
|
||||
of variables currently "invisible" to default clauses.
|
||||
This is normally 0, but will be larger during parsing of
|
||||
DECLARE ... DEFAULT, to get the scope right for DEFAULT values.
|
||||
*/
|
||||
/// Boundary for finding variables in this context. This is the number of
|
||||
/// variables currently "invisible" to default clauses. This is normally 0,
|
||||
/// but will be larger during parsing of DECLARE ... DEFAULT, to get the
|
||||
/// scope right for DEFAULT values.
|
||||
uint m_pboundary;
|
||||
|
||||
int m_num_case_exprs;
|
||||
|
||||
DYNAMIC_ARRAY m_vars; // Parameters/variables
|
||||
DYNAMIC_ARRAY m_case_expr_id_lst; /* Stack of CASE expression ids. */
|
||||
DYNAMIC_ARRAY m_conds; // Conditions
|
||||
DYNAMIC_ARRAY m_cursors; // Cursors
|
||||
DYNAMIC_ARRAY m_handlers; // Handlers, for checking for duplicates
|
||||
/// SP parameters/variables.
|
||||
Dynamic_array<sp_variable *> m_vars;
|
||||
|
||||
List<sp_label_t> m_label; // The label list
|
||||
/// Stack of CASE expression ids.
|
||||
Dynamic_array<int> m_case_expr_ids;
|
||||
|
||||
List<sp_pcontext> m_children; // Children contexts, used for destruction
|
||||
/// Stack of SQL-conditions.
|
||||
Dynamic_array<sp_condition *> m_conditions;
|
||||
|
||||
/**
|
||||
Scope of labels for this parsing context.
|
||||
*/
|
||||
label_scope_type m_label_scope;
|
||||
/// Stack of cursors.
|
||||
Dynamic_array<LEX_STRING> m_cursors;
|
||||
|
||||
private:
|
||||
sp_pcontext(const sp_pcontext &); /* Prevent use of these */
|
||||
void operator=(sp_pcontext &);
|
||||
/// Stack of SQL-handlers.
|
||||
Dynamic_array<sp_handler *> m_handlers;
|
||||
|
||||
/// List of labels.
|
||||
List<sp_label> m_labels;
|
||||
|
||||
/// Children contexts, used for destruction.
|
||||
Dynamic_array<sp_pcontext *> m_children;
|
||||
|
||||
/// Scope of this parsing context.
|
||||
enum_scope m_scope;
|
||||
}; // class sp_pcontext : public Sql_alloc
|
||||
|
||||
|
||||
|
|
|
@ -26,23 +26,21 @@
|
|||
#include "sp_pcontext.h"
|
||||
#include "sql_select.h" // create_virtual_tmp_table
|
||||
|
||||
sp_rcontext::sp_rcontext(sp_pcontext *root_parsing_ctx,
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// sp_rcontext implementation.
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
sp_rcontext::sp_rcontext(const sp_pcontext *root_parsing_ctx,
|
||||
Field *return_value_fld,
|
||||
sp_rcontext *prev_runtime_ctx)
|
||||
:end_partial_result_set(FALSE),
|
||||
bool in_sub_stmt)
|
||||
:end_partial_result_set(false),
|
||||
m_root_parsing_ctx(root_parsing_ctx),
|
||||
m_var_table(0),
|
||||
m_var_items(0),
|
||||
m_var_table(NULL),
|
||||
m_return_value_fld(return_value_fld),
|
||||
m_return_value_set(FALSE),
|
||||
in_sub_stmt(FALSE),
|
||||
m_hcount(0),
|
||||
m_hsp(0),
|
||||
m_ihsp(0),
|
||||
m_hfound(-1),
|
||||
m_ccount(0),
|
||||
m_case_expr_holders(0),
|
||||
m_prev_runtime_ctx(prev_runtime_ctx)
|
||||
m_return_value_set(false),
|
||||
m_in_sub_stmt(in_sub_stmt),
|
||||
m_ccount(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -51,166 +49,167 @@ sp_rcontext::~sp_rcontext()
|
|||
{
|
||||
if (m_var_table)
|
||||
free_blobs(m_var_table);
|
||||
|
||||
// Leave m_handlers, m_handler_call_stack, m_var_items, m_cstack
|
||||
// and m_case_expr_holders untouched.
|
||||
// They are allocated in mem roots and will be freed accordingly.
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Initialize sp_rcontext instance.
|
||||
|
||||
SYNOPSIS
|
||||
thd Thread handle
|
||||
RETURN
|
||||
FALSE on success
|
||||
TRUE on error
|
||||
*/
|
||||
|
||||
bool sp_rcontext::init(THD *thd)
|
||||
sp_rcontext *sp_rcontext::create(THD *thd,
|
||||
const sp_pcontext *root_parsing_ctx,
|
||||
Field *return_value_fld)
|
||||
{
|
||||
uint handler_count= m_root_parsing_ctx->max_handler_index();
|
||||
sp_rcontext *ctx= new (thd->mem_root) sp_rcontext(root_parsing_ctx,
|
||||
return_value_fld,
|
||||
thd->in_sub_stmt);
|
||||
|
||||
in_sub_stmt= thd->in_sub_stmt;
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
if (init_var_table(thd) || init_var_items())
|
||||
return TRUE;
|
||||
if (ctx->alloc_arrays(thd) ||
|
||||
ctx->init_var_table(thd) ||
|
||||
ctx->init_var_items(thd))
|
||||
{
|
||||
delete ctx;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(m_raised_conditions= new (thd->mem_root) Sql_condition_info[handler_count]))
|
||||
return TRUE;
|
||||
|
||||
return
|
||||
!(m_handler=
|
||||
(sp_handler_t*)thd->alloc(handler_count * sizeof(sp_handler_t))) ||
|
||||
!(m_hstack=
|
||||
(uint*)thd->alloc(handler_count * sizeof(uint))) ||
|
||||
!(m_in_handler=
|
||||
(sp_active_handler_t*)thd->alloc(handler_count *
|
||||
sizeof(sp_active_handler_t))) ||
|
||||
!(m_cstack=
|
||||
(sp_cursor**)thd->alloc(m_root_parsing_ctx->max_cursor_index() *
|
||||
sizeof(sp_cursor*))) ||
|
||||
!(m_case_expr_holders=
|
||||
(Item_cache**)thd->calloc(m_root_parsing_ctx->get_num_case_exprs() *
|
||||
sizeof (Item_cache*)));
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Create and initialize a table to store SP-vars.
|
||||
bool sp_rcontext::alloc_arrays(THD *thd)
|
||||
{
|
||||
{
|
||||
size_t n= m_root_parsing_ctx->max_cursor_index();
|
||||
m_cstack.reset(
|
||||
static_cast<sp_cursor **> (
|
||||
thd->alloc(n * sizeof (sp_cursor*))),
|
||||
n);
|
||||
}
|
||||
|
||||
SYNOPSIS
|
||||
thd Thread handler.
|
||||
RETURN
|
||||
FALSE on success
|
||||
TRUE on error
|
||||
*/
|
||||
{
|
||||
size_t n= m_root_parsing_ctx->get_num_case_exprs();
|
||||
m_case_expr_holders.reset(
|
||||
static_cast<Item_cache **> (
|
||||
thd->calloc(n * sizeof (Item_cache*))),
|
||||
n);
|
||||
}
|
||||
|
||||
bool
|
||||
sp_rcontext::init_var_table(THD *thd)
|
||||
return !m_cstack.array() || !m_case_expr_holders.array();
|
||||
}
|
||||
|
||||
|
||||
bool sp_rcontext::init_var_table(THD *thd)
|
||||
{
|
||||
List<Create_field> field_def_lst;
|
||||
|
||||
if (!m_root_parsing_ctx->max_var_index())
|
||||
return FALSE;
|
||||
return false;
|
||||
|
||||
m_root_parsing_ctx->retrieve_field_definitions(&field_def_lst);
|
||||
|
||||
DBUG_ASSERT(field_def_lst.elements == m_root_parsing_ctx->max_var_index());
|
||||
|
||||
|
||||
if (!(m_var_table= create_virtual_tmp_table(thd, field_def_lst)))
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
m_var_table->copy_blobs= TRUE;
|
||||
m_var_table->alias.set("", 0, table_alias_charset);
|
||||
m_var_table->copy_blobs= true;
|
||||
m_var_table->alias.set("", 0, m_var_table->alias.charset());
|
||||
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Create and initialize an Item-adapter (Item_field) for each SP-var field.
|
||||
|
||||
RETURN
|
||||
FALSE on success
|
||||
TRUE on error
|
||||
*/
|
||||
|
||||
bool
|
||||
sp_rcontext::init_var_items()
|
||||
bool sp_rcontext::init_var_items(THD *thd)
|
||||
{
|
||||
uint idx;
|
||||
uint num_vars= m_root_parsing_ctx->max_var_index();
|
||||
|
||||
if (!(m_var_items= (Item**) sql_alloc(num_vars * sizeof (Item *))))
|
||||
return TRUE;
|
||||
m_var_items.reset(
|
||||
static_cast<Item **> (
|
||||
thd->alloc(num_vars * sizeof (Item *))),
|
||||
num_vars);
|
||||
|
||||
for (idx = 0; idx < num_vars; ++idx)
|
||||
if (!m_var_items.array())
|
||||
return true;
|
||||
|
||||
for (uint idx = 0; idx < num_vars; ++idx)
|
||||
{
|
||||
if (!(m_var_items[idx]= new Item_field(m_var_table->field[idx])))
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
|
||||
bool sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
|
||||
{
|
||||
DBUG_ASSERT(m_return_value_fld);
|
||||
|
||||
m_return_value_set = TRUE;
|
||||
m_return_value_set = true;
|
||||
|
||||
return sp_eval_expr(thd, m_return_value_fld, return_value_item);
|
||||
}
|
||||
|
||||
|
||||
#define IS_WARNING_CONDITION(S) ((S)[0] == '0' && (S)[1] == '1')
|
||||
#define IS_NOT_FOUND_CONDITION(S) ((S)[0] == '0' && (S)[1] == '2')
|
||||
#define IS_EXCEPTION_CONDITION(S) ((S)[0] != '0' || (S)[1] > '2')
|
||||
|
||||
/**
|
||||
Find an SQL handler for the given error.
|
||||
|
||||
SQL handlers are pushed on the stack m_handler, with the latest/innermost
|
||||
one on the top; we then search for matching handlers from the top and
|
||||
down.
|
||||
|
||||
We search through all the handlers, looking for the most specific one
|
||||
(sql_errno more specific than sqlstate more specific than the rest).
|
||||
Note that mysql error code handlers is a MySQL extension, not part of
|
||||
the standard.
|
||||
|
||||
SQL handlers for warnings are searched in the current scope only.
|
||||
|
||||
SQL handlers for errors are searched in the current and in outer scopes.
|
||||
That's why finding and activation of handler must be separated: an errror
|
||||
handler might be located in the outer scope, which is not active at the
|
||||
moment. Before such handler can be activated, execution flow should
|
||||
unwind to that scope.
|
||||
|
||||
Found SQL handler is remembered in m_hfound for future activation.
|
||||
If no handler is found, m_hfound is -1.
|
||||
|
||||
@param thd Thread handle
|
||||
@param sql_errno The error code
|
||||
@param sqlstate The error SQL state
|
||||
@param level The error level
|
||||
@param msg The error message
|
||||
|
||||
@retval TRUE if an SQL handler was found
|
||||
@retval FALSE otherwise
|
||||
*/
|
||||
|
||||
bool
|
||||
sp_rcontext::find_handler(THD *thd,
|
||||
uint sql_errno,
|
||||
const char *sqlstate,
|
||||
Sql_condition::enum_warning_level level,
|
||||
const char *msg)
|
||||
bool sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper,
|
||||
sp_instr_cpush *i)
|
||||
{
|
||||
int i= m_hcount;
|
||||
/*
|
||||
We should create cursors in the callers arena, as
|
||||
it could be (and usually is) used in several instructions.
|
||||
*/
|
||||
sp_cursor *c= new (callers_arena->mem_root) sp_cursor(lex_keeper, i);
|
||||
|
||||
/* Reset previously found handler. */
|
||||
m_hfound= -1;
|
||||
if (c == NULL)
|
||||
return true;
|
||||
|
||||
m_cstack[m_ccount++]= c;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void sp_rcontext::pop_cursors(uint count)
|
||||
{
|
||||
DBUG_ASSERT(m_ccount >= count);
|
||||
|
||||
while (count--)
|
||||
delete m_cstack[--m_ccount];
|
||||
}
|
||||
|
||||
|
||||
bool sp_rcontext::push_handler(sp_handler *handler, uint first_ip)
|
||||
{
|
||||
/*
|
||||
We should create handler entries in the callers arena, as
|
||||
they could be (and usually are) used in several instructions.
|
||||
*/
|
||||
sp_handler_entry *he=
|
||||
new (callers_arena->mem_root) sp_handler_entry(handler, first_ip);
|
||||
|
||||
if (he == NULL)
|
||||
return true;
|
||||
|
||||
return m_handlers.append(he);
|
||||
}
|
||||
|
||||
|
||||
void sp_rcontext::pop_handlers(int count)
|
||||
{
|
||||
DBUG_ASSERT(m_handlers.elements() >= count);
|
||||
|
||||
for (int i= 0; i < count; ++i)
|
||||
m_handlers.pop();
|
||||
}
|
||||
|
||||
|
||||
bool sp_rcontext::handle_sql_condition(THD *thd,
|
||||
uint *ip,
|
||||
const sp_instr *cur_spi)
|
||||
{
|
||||
DBUG_ENTER("sp_rcontext::handle_sql_condition");
|
||||
|
||||
/*
|
||||
If this is a fatal sub-statement error, and this runtime
|
||||
|
@ -218,255 +217,139 @@ sp_rcontext::find_handler(THD *thd,
|
|||
handlers from this context are applicable: try to locate one
|
||||
in the outer scope.
|
||||
*/
|
||||
if (thd->is_fatal_sub_stmt_error && in_sub_stmt)
|
||||
i= 0;
|
||||
if (thd->is_fatal_sub_stmt_error && m_in_sub_stmt)
|
||||
DBUG_RETURN(false);
|
||||
|
||||
/* Search handlers from the latest (innermost) to the oldest (outermost) */
|
||||
while (i--)
|
||||
Diagnostics_area *da= thd->get_stmt_da();
|
||||
const sp_handler *found_handler= NULL;
|
||||
const Sql_condition *found_condition= NULL;
|
||||
|
||||
if (thd->is_error())
|
||||
{
|
||||
sp_cond_type_t *cond= m_handler[i].cond;
|
||||
int j= m_ihsp;
|
||||
found_handler=
|
||||
cur_spi->m_ctx->find_handler(da->get_sqlstate(),
|
||||
da->sql_errno(),
|
||||
Sql_condition::WARN_LEVEL_ERROR);
|
||||
|
||||
/* Check active handlers, to avoid invoking one recursively */
|
||||
while (j--)
|
||||
if (m_in_handler[j].ip == m_handler[i].handler)
|
||||
break;
|
||||
if (j >= 0)
|
||||
continue; // Already executing this handler
|
||||
if (found_handler)
|
||||
found_condition= da->get_error_condition();
|
||||
}
|
||||
else if (da->current_statement_warn_count())
|
||||
{
|
||||
Diagnostics_area::Sql_condition_iterator it= da->sql_conditions();
|
||||
const Sql_condition *c;
|
||||
|
||||
switch (cond->type)
|
||||
// Here we need to find the last warning/note from the stack.
|
||||
// In MySQL most substantial warning is the last one.
|
||||
// (We could have used a reverse iterator here if one existed)
|
||||
|
||||
while ((c= it++))
|
||||
{
|
||||
case sp_cond_type_t::number:
|
||||
if (sql_errno == cond->mysqlerr &&
|
||||
(m_hfound < 0 || m_handler[m_hfound].cond->type > sp_cond_type_t::number))
|
||||
m_hfound= i; // Always the most specific
|
||||
break;
|
||||
case sp_cond_type_t::state:
|
||||
if (strcmp(sqlstate, cond->sqlstate) == 0 &&
|
||||
(m_hfound < 0 || m_handler[m_hfound].cond->type > sp_cond_type_t::state))
|
||||
m_hfound= i;
|
||||
break;
|
||||
case sp_cond_type_t::warning:
|
||||
if ((IS_WARNING_CONDITION(sqlstate) ||
|
||||
level == Sql_condition::WARN_LEVEL_WARN) &&
|
||||
m_hfound < 0)
|
||||
m_hfound= i;
|
||||
break;
|
||||
case sp_cond_type_t::notfound:
|
||||
if (IS_NOT_FOUND_CONDITION(sqlstate) && m_hfound < 0)
|
||||
m_hfound= i;
|
||||
break;
|
||||
case sp_cond_type_t::exception:
|
||||
if (IS_EXCEPTION_CONDITION(sqlstate) &&
|
||||
level == Sql_condition::WARN_LEVEL_ERROR &&
|
||||
m_hfound < 0)
|
||||
m_hfound= i;
|
||||
if (c->get_level() == Sql_condition::WARN_LEVEL_WARN ||
|
||||
c->get_level() == Sql_condition::WARN_LEVEL_NOTE)
|
||||
{
|
||||
const sp_handler *handler=
|
||||
cur_spi->m_ctx->find_handler(c->get_sqlstate(),
|
||||
c->get_sql_errno(),
|
||||
c->get_level());
|
||||
if (handler)
|
||||
{
|
||||
found_handler= handler;
|
||||
found_condition= c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_handler)
|
||||
DBUG_RETURN(false);
|
||||
|
||||
// At this point, we know that:
|
||||
// - there is a pending SQL-condition (error or warning);
|
||||
// - there is an SQL-handler for it.
|
||||
|
||||
DBUG_ASSERT(found_condition);
|
||||
|
||||
sp_handler_entry *handler_entry= NULL;
|
||||
for (int i= 0; i < m_handlers.elements(); ++i)
|
||||
{
|
||||
sp_handler_entry *h= m_handlers.at(i);
|
||||
|
||||
if (h->handler == found_handler)
|
||||
{
|
||||
handler_entry= h;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_hfound >= 0)
|
||||
{
|
||||
DBUG_ASSERT((uint) m_hfound < m_root_parsing_ctx->max_handler_index());
|
||||
|
||||
m_raised_conditions[m_hfound].clear();
|
||||
m_raised_conditions[m_hfound].set(sql_errno, sqlstate, level, msg);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
Only "exception conditions" are propagated to handlers in calling
|
||||
contexts. If no handler is found locally for a "completion condition"
|
||||
(warning or "not found") we will simply resume execution.
|
||||
handler_entry usually should not be NULL here, as that indicates
|
||||
that the parser context thinks a HANDLER should be activated,
|
||||
but the runtime context cannot find it.
|
||||
|
||||
However, this can happen (and this is in line with the Standard)
|
||||
if SQL-condition has been raised before DECLARE HANDLER instruction
|
||||
is processed.
|
||||
|
||||
For example:
|
||||
CREATE PROCEDURE p()
|
||||
BEGIN
|
||||
DECLARE v INT DEFAULT 'get'; -- raises SQL-warning here
|
||||
DECLARE EXIT HANDLER ... -- this handler does not catch the warning
|
||||
END
|
||||
*/
|
||||
if (m_prev_runtime_ctx && IS_EXCEPTION_CONDITION(sqlstate) &&
|
||||
level == Sql_condition::WARN_LEVEL_ERROR)
|
||||
{
|
||||
return m_prev_runtime_ctx->find_handler(thd, sql_errno, sqlstate,
|
||||
level, msg);
|
||||
}
|
||||
if (!handler_entry)
|
||||
DBUG_RETURN(false);
|
||||
|
||||
return FALSE;
|
||||
// Mark active conditions so that they can be deleted when the handler exits.
|
||||
da->mark_sql_conditions_for_removal();
|
||||
|
||||
uint continue_ip= handler_entry->handler->type == sp_handler::CONTINUE ?
|
||||
cur_spi->get_cont_dest() : 0;
|
||||
|
||||
/* End aborted result set. */
|
||||
if (end_partial_result_set)
|
||||
thd->protocol->end_partial_result_set(thd);
|
||||
|
||||
/* Reset error state. */
|
||||
thd->clear_error();
|
||||
thd->killed= NOT_KILLED; // Some errors set thd->killed
|
||||
// (e.g. "bad data").
|
||||
|
||||
/* Add a frame to handler-call-stack. */
|
||||
Sql_condition_info *cond_info=
|
||||
new (callers_arena->mem_root) Sql_condition_info(found_condition,
|
||||
callers_arena);
|
||||
Handler_call_frame *frame=
|
||||
new (callers_arena->mem_root) Handler_call_frame(cond_info, continue_ip);
|
||||
m_handler_call_stack.append(frame);
|
||||
|
||||
*ip= handler_entry->first_ip;
|
||||
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
|
||||
void
|
||||
sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
|
||||
{
|
||||
DBUG_ENTER("sp_rcontext::push_cursor");
|
||||
DBUG_ASSERT(m_ccount < m_root_parsing_ctx->max_cursor_index());
|
||||
m_cstack[m_ccount++]= new sp_cursor(lex_keeper, i);
|
||||
DBUG_PRINT("info", ("m_ccount: %d", m_ccount));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
void
|
||||
sp_rcontext::pop_cursors(uint count)
|
||||
{
|
||||
DBUG_ENTER("sp_rcontext::pop_cursors");
|
||||
DBUG_ASSERT(m_ccount >= count);
|
||||
while (count--)
|
||||
{
|
||||
delete m_cstack[--m_ccount];
|
||||
}
|
||||
DBUG_PRINT("info", ("m_ccount: %d", m_ccount));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
void
|
||||
sp_rcontext::push_handler(struct sp_cond_type *cond, uint h, int type)
|
||||
{
|
||||
DBUG_ENTER("sp_rcontext::push_handler");
|
||||
DBUG_ASSERT(m_hcount < m_root_parsing_ctx->max_handler_index());
|
||||
|
||||
m_handler[m_hcount].cond= cond;
|
||||
m_handler[m_hcount].handler= h;
|
||||
m_handler[m_hcount].type= type;
|
||||
m_hcount+= 1;
|
||||
|
||||
DBUG_PRINT("info", ("m_hcount: %d", m_hcount));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
void
|
||||
sp_rcontext::pop_handlers(uint count)
|
||||
{
|
||||
DBUG_ENTER("sp_rcontext::pop_handlers");
|
||||
DBUG_ASSERT(m_hcount >= count);
|
||||
|
||||
m_hcount-= count;
|
||||
|
||||
DBUG_PRINT("info", ("m_hcount: %d", m_hcount));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
void
|
||||
sp_rcontext::push_hstack(uint h)
|
||||
{
|
||||
DBUG_ENTER("sp_rcontext::push_hstack");
|
||||
DBUG_ASSERT(m_hsp < m_root_parsing_ctx->max_handler_index());
|
||||
|
||||
m_hstack[m_hsp++]= h;
|
||||
|
||||
DBUG_PRINT("info", ("m_hsp: %d", m_hsp));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
uint
|
||||
sp_rcontext::pop_hstack()
|
||||
{
|
||||
uint handler;
|
||||
DBUG_ENTER("sp_rcontext::pop_hstack");
|
||||
DBUG_ASSERT(m_hsp);
|
||||
|
||||
handler= m_hstack[--m_hsp];
|
||||
|
||||
DBUG_PRINT("info", ("m_hsp: %d", m_hsp));
|
||||
DBUG_RETURN(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
Prepare found handler to be executed.
|
||||
|
||||
@retval TRUE if an SQL handler is activated (was found) and IP of the
|
||||
first handler instruction.
|
||||
@retval FALSE if there is no active handler
|
||||
*/
|
||||
|
||||
bool
|
||||
sp_rcontext::activate_handler(THD *thd,
|
||||
uint *ip,
|
||||
sp_instr *instr,
|
||||
Query_arena *execute_arena,
|
||||
Query_arena *backup_arena)
|
||||
{
|
||||
if (m_hfound < 0)
|
||||
return FALSE;
|
||||
|
||||
switch (m_handler[m_hfound].type) {
|
||||
case SP_HANDLER_NONE:
|
||||
break;
|
||||
|
||||
case SP_HANDLER_CONTINUE:
|
||||
thd->restore_active_arena(execute_arena, backup_arena);
|
||||
thd->set_n_backup_active_arena(execute_arena, backup_arena);
|
||||
push_hstack(instr->get_cont_dest());
|
||||
|
||||
/* Fall through */
|
||||
|
||||
default:
|
||||
/* End aborted result set. */
|
||||
|
||||
if (end_partial_result_set)
|
||||
thd->protocol->end_partial_result_set(thd);
|
||||
|
||||
/* Enter handler. */
|
||||
|
||||
DBUG_ASSERT(m_ihsp < m_root_parsing_ctx->max_handler_index());
|
||||
DBUG_ASSERT(m_hfound >= 0);
|
||||
|
||||
m_in_handler[m_ihsp].ip= m_handler[m_hfound].handler;
|
||||
m_in_handler[m_ihsp].index= m_hfound;
|
||||
m_ihsp++;
|
||||
|
||||
DBUG_PRINT("info", ("Entering handler..."));
|
||||
DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp));
|
||||
|
||||
/* Reset error state. */
|
||||
|
||||
thd->clear_error();
|
||||
thd->reset_killed(); // Some errors set thd->killed
|
||||
// (e.g. "bad data").
|
||||
|
||||
/* Return IP of the activated SQL handler. */
|
||||
*ip= m_handler[m_hfound].handler;
|
||||
|
||||
/* Reset found handler. */
|
||||
m_hfound= -1;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
sp_rcontext::exit_handler()
|
||||
uint sp_rcontext::exit_handler(Diagnostics_area *da)
|
||||
{
|
||||
DBUG_ENTER("sp_rcontext::exit_handler");
|
||||
DBUG_ASSERT(m_ihsp);
|
||||
DBUG_ASSERT(m_handler_call_stack.elements() > 0);
|
||||
|
||||
uint hindex= m_in_handler[m_ihsp-1].index;
|
||||
m_raised_conditions[hindex].clear();
|
||||
m_ihsp-= 1;
|
||||
Handler_call_frame *f= m_handler_call_stack.pop();
|
||||
|
||||
DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
/*
|
||||
Remove the SQL conditions that were present in DA when the
|
||||
handler was activated.
|
||||
*/
|
||||
da->remove_marked_sql_conditions();
|
||||
|
||||
Sql_condition_info* sp_rcontext::raised_condition() const
|
||||
{
|
||||
if (m_ihsp > 0)
|
||||
{
|
||||
uint hindex= m_in_handler[m_ihsp - 1].index;
|
||||
Sql_condition_info *raised= & m_raised_conditions[hindex];
|
||||
return raised;
|
||||
}
|
||||
uint continue_ip= f->continue_ip;
|
||||
|
||||
if (m_prev_runtime_ctx)
|
||||
return m_prev_runtime_ctx->raised_condition();
|
||||
|
||||
return NULL;
|
||||
DBUG_RETURN(continue_ip);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sp_rcontext::set_variable(THD *thd, uint var_idx, Item **value)
|
||||
{
|
||||
return set_variable(thd, m_var_table->field[var_idx], value);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sp_rcontext::set_variable(THD *thd, Field *field, Item **value)
|
||||
int sp_rcontext::set_variable(THD *thd, Field *field, Item **value)
|
||||
{
|
||||
if (!value)
|
||||
{
|
||||
|
@ -478,25 +361,47 @@ sp_rcontext::set_variable(THD *thd, Field *field, Item **value)
|
|||
}
|
||||
|
||||
|
||||
Item *
|
||||
sp_rcontext::get_item(uint var_idx)
|
||||
Item_cache *sp_rcontext::create_case_expr_holder(THD *thd,
|
||||
const Item *item) const
|
||||
{
|
||||
return m_var_items[var_idx];
|
||||
Item_cache *holder;
|
||||
Query_arena current_arena;
|
||||
|
||||
thd->set_n_backup_active_arena(thd->spcont->callers_arena, ¤t_arena);
|
||||
|
||||
holder= Item_cache::get_cache(item);
|
||||
|
||||
thd->restore_active_arena(thd->spcont->callers_arena, ¤t_arena);
|
||||
|
||||
return holder;
|
||||
}
|
||||
|
||||
|
||||
Item **
|
||||
sp_rcontext::get_item_addr(uint var_idx)
|
||||
bool sp_rcontext::set_case_expr(THD *thd, int case_expr_id,
|
||||
Item **case_expr_item_ptr)
|
||||
{
|
||||
return m_var_items + var_idx;
|
||||
Item *case_expr_item= sp_prepare_func_item(thd, case_expr_item_ptr);
|
||||
if (!case_expr_item)
|
||||
return true;
|
||||
|
||||
if (!m_case_expr_holders[case_expr_id] ||
|
||||
m_case_expr_holders[case_expr_id]->result_type() !=
|
||||
case_expr_item->result_type())
|
||||
{
|
||||
m_case_expr_holders[case_expr_id]=
|
||||
create_case_expr_holder(thd, case_expr_item);
|
||||
}
|
||||
|
||||
m_case_expr_holders[case_expr_id]->store(case_expr_item);
|
||||
m_case_expr_holders[case_expr_id]->cache_value();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* sp_cursor
|
||||
*
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// sp_cursor implementation.
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
|
||||
:m_lex_keeper(lex_keeper),
|
||||
|
@ -523,8 +428,7 @@ sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
|
|||
0 in case of success, -1 otherwise
|
||||
*/
|
||||
|
||||
int
|
||||
sp_cursor::open(THD *thd)
|
||||
int sp_cursor::open(THD *thd)
|
||||
{
|
||||
if (server_side_cursor)
|
||||
{
|
||||
|
@ -538,8 +442,7 @@ sp_cursor::open(THD *thd)
|
|||
}
|
||||
|
||||
|
||||
int
|
||||
sp_cursor::close(THD *thd)
|
||||
int sp_cursor::close(THD *thd)
|
||||
{
|
||||
if (! server_side_cursor)
|
||||
{
|
||||
|
@ -551,16 +454,14 @@ sp_cursor::close(THD *thd)
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
sp_cursor::destroy()
|
||||
void sp_cursor::destroy()
|
||||
{
|
||||
delete server_side_cursor;
|
||||
server_side_cursor= 0;
|
||||
server_side_cursor= NULL;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sp_cursor::fetch(THD *thd, List<struct sp_variable> *vars)
|
||||
int sp_cursor::fetch(THD *thd, List<sp_variable> *vars)
|
||||
{
|
||||
if (! server_side_cursor)
|
||||
{
|
||||
|
@ -599,108 +500,13 @@ sp_cursor::fetch(THD *thd, List<struct sp_variable> *vars)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Create an instance of appropriate Item_cache class depending on the
|
||||
specified type in the callers arena.
|
||||
|
||||
SYNOPSIS
|
||||
thd thread handler
|
||||
result_type type of the expression
|
||||
|
||||
RETURN
|
||||
Pointer to valid object on success
|
||||
NULL on error
|
||||
|
||||
NOTE
|
||||
We should create cache items in the callers arena, as they are used
|
||||
between in several instructions.
|
||||
*/
|
||||
|
||||
Item_cache *
|
||||
sp_rcontext::create_case_expr_holder(THD *thd, const Item *item)
|
||||
{
|
||||
Item_cache *holder;
|
||||
Query_arena current_arena;
|
||||
|
||||
thd->set_n_backup_active_arena(thd->spcont->callers_arena, ¤t_arena);
|
||||
|
||||
holder= Item_cache::get_cache(item);
|
||||
|
||||
thd->restore_active_arena(thd->spcont->callers_arena, ¤t_arena);
|
||||
|
||||
return holder;
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// sp_cursor::Select_fetch_into_spvars implementation.
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/*
|
||||
Set CASE expression to the specified value.
|
||||
|
||||
SYNOPSIS
|
||||
thd thread handler
|
||||
case_expr_id identifier of the CASE expression
|
||||
case_expr_item a value of the CASE expression
|
||||
|
||||
RETURN
|
||||
FALSE on success
|
||||
TRUE on error
|
||||
|
||||
NOTE
|
||||
The idea is to reuse Item_cache for the expression of the one CASE
|
||||
statement. This optimization takes place when there is CASE statement
|
||||
inside of a loop. So, in other words, we will use the same object on each
|
||||
iteration instead of creating a new one for each iteration.
|
||||
|
||||
TODO
|
||||
Hypothetically, a type of CASE expression can be different for each
|
||||
iteration. For instance, this can happen if the expression contains a
|
||||
session variable (something like @@VAR) and its type is changed from one
|
||||
iteration to another.
|
||||
|
||||
In order to cope with this problem, we check type each time, when we use
|
||||
already created object. If the type does not match, we re-create Item.
|
||||
This also can (should?) be optimized.
|
||||
*/
|
||||
|
||||
int
|
||||
sp_rcontext::set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr)
|
||||
{
|
||||
Item *case_expr_item= sp_prepare_func_item(thd, case_expr_item_ptr);
|
||||
if (!case_expr_item)
|
||||
return TRUE;
|
||||
|
||||
if (!m_case_expr_holders[case_expr_id] ||
|
||||
m_case_expr_holders[case_expr_id]->result_type() !=
|
||||
case_expr_item->result_type())
|
||||
{
|
||||
m_case_expr_holders[case_expr_id]=
|
||||
create_case_expr_holder(thd, case_expr_item);
|
||||
}
|
||||
|
||||
m_case_expr_holders[case_expr_id]->store(case_expr_item);
|
||||
m_case_expr_holders[case_expr_id]->cache_value();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
Item *
|
||||
sp_rcontext::get_case_expr(int case_expr_id)
|
||||
{
|
||||
return m_case_expr_holders[case_expr_id];
|
||||
}
|
||||
|
||||
|
||||
Item **
|
||||
sp_rcontext::get_case_expr_addr(int case_expr_id)
|
||||
{
|
||||
return (Item**) m_case_expr_holders + case_expr_id;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
Select_fetch_into_spvars
|
||||
****************************************************************************/
|
||||
|
||||
int Select_fetch_into_spvars::prepare(List<Item> &fields, SELECT_LEX_UNIT *u)
|
||||
int sp_cursor::Select_fetch_into_spvars::prepare(List<Item> &fields,
|
||||
SELECT_LEX_UNIT *u)
|
||||
{
|
||||
/*
|
||||
Cache the number of columns in the result set in order to easily
|
||||
|
@ -711,11 +517,11 @@ int Select_fetch_into_spvars::prepare(List<Item> &fields, SELECT_LEX_UNIT *u)
|
|||
}
|
||||
|
||||
|
||||
int Select_fetch_into_spvars::send_data(List<Item> &items)
|
||||
bool sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items)
|
||||
{
|
||||
List_iterator_fast<struct sp_variable> spvar_iter(*spvar_list);
|
||||
List_iterator_fast<sp_variable> spvar_iter(*spvar_list);
|
||||
List_iterator_fast<Item> item_iter(items);
|
||||
sp_variable_t *spvar;
|
||||
sp_variable *spvar;
|
||||
Item *item;
|
||||
|
||||
/* Must be ensured by the caller */
|
||||
|
@ -728,7 +534,7 @@ int Select_fetch_into_spvars::send_data(List<Item> &items)
|
|||
for (; spvar= spvar_iter++, item= item_iter++; )
|
||||
{
|
||||
if (thd->spcont->set_variable(thd, spvar->offset, &item))
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -22,80 +22,18 @@
|
|||
#endif
|
||||
|
||||
#include "sql_class.h" // select_result_interceptor
|
||||
#include "sp_pcontext.h" // sp_condition_value
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// sp_rcontext declaration.
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct sp_cond_type;
|
||||
class sp_cursor;
|
||||
struct sp_variable;
|
||||
class sp_lex_keeper;
|
||||
class sp_instr_cpush;
|
||||
class Query_arena;
|
||||
class sp_head;
|
||||
class sp_pcontext;
|
||||
class Item_cache;
|
||||
typedef class st_select_lex_unit SELECT_LEX_UNIT;
|
||||
class Server_side_cursor;
|
||||
|
||||
#define SP_HANDLER_NONE 0
|
||||
#define SP_HANDLER_EXIT 1
|
||||
#define SP_HANDLER_CONTINUE 2
|
||||
#define SP_HANDLER_UNDO 3
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/** Condition caught by this HANDLER. */
|
||||
struct sp_cond_type *cond;
|
||||
/** Location (instruction pointer) of the handler code. */
|
||||
uint handler;
|
||||
/** Handler type (EXIT, CONTINUE). */
|
||||
int type;
|
||||
} sp_handler_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/** Instruction pointer of the active handler. */
|
||||
uint ip;
|
||||
/** Handler index of the active handler. */
|
||||
uint index;
|
||||
} sp_active_handler_t;
|
||||
|
||||
|
||||
class Sql_condition_info : public Sql_alloc
|
||||
{
|
||||
public:
|
||||
/** SQL error code. */
|
||||
uint m_sql_errno;
|
||||
|
||||
/** Error level. */
|
||||
Sql_condition::enum_warning_level m_level;
|
||||
|
||||
/** SQLSTATE. */
|
||||
char m_sql_state[SQLSTATE_LENGTH + 1];
|
||||
|
||||
/** Text message. */
|
||||
char m_message[MYSQL_ERRMSG_SIZE];
|
||||
|
||||
void set(uint sql_errno, const char* sqlstate,
|
||||
Sql_condition::enum_warning_level level,
|
||||
const char* msg)
|
||||
{
|
||||
m_sql_errno= sql_errno;
|
||||
m_level= level;
|
||||
|
||||
memcpy(m_sql_state, sqlstate, SQLSTATE_LENGTH);
|
||||
m_sql_state[SQLSTATE_LENGTH]= '\0';
|
||||
|
||||
strncpy(m_message, msg, MYSQL_ERRMSG_SIZE);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_sql_errno= 0;
|
||||
m_level= Sql_condition::WARN_LEVEL_ERROR;
|
||||
|
||||
m_sql_state[0]= '\0';
|
||||
m_message[0]= '\0';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
|
@ -119,251 +57,412 @@ public:
|
|||
|
||||
class sp_rcontext : public Sql_alloc
|
||||
{
|
||||
sp_rcontext(const sp_rcontext &); /* Prevent use of these */
|
||||
void operator=(sp_rcontext &);
|
||||
|
||||
public:
|
||||
|
||||
/*
|
||||
Arena used to (re) allocate items on . E.g. reallocate INOUT/OUT
|
||||
SP parameters when they don't fit into prealloced items. This
|
||||
is common situation with String items. It is used mainly in
|
||||
sp_eval_func_item().
|
||||
*/
|
||||
Query_arena *callers_arena;
|
||||
|
||||
/*
|
||||
End a open result set before start executing a continue/exit
|
||||
handler if one is found as otherwise the client will hang
|
||||
due to a violation of the client/server protocol.
|
||||
*/
|
||||
bool end_partial_result_set;
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
/*
|
||||
The routine for which this runtime context is created. Used for checking
|
||||
if correct runtime context is used for variable handling.
|
||||
*/
|
||||
sp_head *sp;
|
||||
#endif
|
||||
|
||||
sp_rcontext(sp_pcontext *root_parsing_ctx, Field *return_value_fld,
|
||||
sp_rcontext *prev_runtime_ctx);
|
||||
bool init(THD *thd);
|
||||
public:
|
||||
/// Construct and properly initialize a new sp_rcontext instance. The static
|
||||
/// create-function is needed because we need a way to return an error from
|
||||
/// the constructor.
|
||||
///
|
||||
/// @param thd Thread handle.
|
||||
/// @param root_parsing_ctx Top-level parsing context for this stored program.
|
||||
/// @param return_value_fld Field object to store the return value
|
||||
/// (for stored functions only).
|
||||
///
|
||||
/// @return valid sp_rcontext object or NULL in case of OOM-error.
|
||||
static sp_rcontext *create(THD *thd,
|
||||
const sp_pcontext *root_parsing_ctx,
|
||||
Field *return_value_fld);
|
||||
|
||||
~sp_rcontext();
|
||||
|
||||
int
|
||||
set_variable(THD *thd, uint var_idx, Item **value);
|
||||
private:
|
||||
sp_rcontext(const sp_pcontext *root_parsing_ctx,
|
||||
Field *return_value_fld,
|
||||
bool in_sub_stmt);
|
||||
|
||||
Item *
|
||||
get_item(uint var_idx);
|
||||
|
||||
Item **
|
||||
get_item_addr(uint var_idx);
|
||||
|
||||
bool
|
||||
set_return_value(THD *thd, Item **return_value_item);
|
||||
|
||||
inline bool
|
||||
is_return_value_set() const
|
||||
{
|
||||
return m_return_value_set;
|
||||
}
|
||||
|
||||
/*
|
||||
SQL handlers support.
|
||||
*/
|
||||
|
||||
void push_handler(struct sp_cond_type *cond, uint h, int type);
|
||||
|
||||
void pop_handlers(uint count);
|
||||
|
||||
bool
|
||||
find_handler(THD *thd,
|
||||
uint sql_errno,
|
||||
const char *sqlstate,
|
||||
Sql_condition::enum_warning_level level,
|
||||
const char *msg);
|
||||
|
||||
Sql_condition_info *raised_condition() const;
|
||||
|
||||
void
|
||||
push_hstack(uint h);
|
||||
|
||||
uint
|
||||
pop_hstack();
|
||||
|
||||
bool
|
||||
activate_handler(THD *thd,
|
||||
uint *ip,
|
||||
sp_instr *instr,
|
||||
Query_arena *execute_arena,
|
||||
Query_arena *backup_arena);
|
||||
|
||||
|
||||
void
|
||||
exit_handler();
|
||||
|
||||
void
|
||||
push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
|
||||
|
||||
void
|
||||
pop_cursors(uint count);
|
||||
|
||||
inline void
|
||||
pop_all_cursors()
|
||||
{
|
||||
pop_cursors(m_ccount);
|
||||
}
|
||||
|
||||
inline sp_cursor *
|
||||
get_cursor(uint i)
|
||||
{
|
||||
return m_cstack[i];
|
||||
}
|
||||
|
||||
/*
|
||||
CASE expressions support.
|
||||
*/
|
||||
|
||||
int
|
||||
set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr);
|
||||
|
||||
Item *
|
||||
get_case_expr(int case_expr_id);
|
||||
|
||||
Item **
|
||||
get_case_expr_addr(int case_expr_id);
|
||||
// Prevent use of copying constructor and operator.
|
||||
sp_rcontext(const sp_rcontext &);
|
||||
void operator=(sp_rcontext &);
|
||||
|
||||
private:
|
||||
sp_pcontext *m_root_parsing_ctx;
|
||||
/// This is an auxillary class to store entering instruction pointer for an
|
||||
/// SQL-handler.
|
||||
class sp_handler_entry : public Sql_alloc
|
||||
{
|
||||
public:
|
||||
/// Handler definition (from parsing context).
|
||||
const sp_handler *handler;
|
||||
|
||||
/* Virtual table for storing variables. */
|
||||
TABLE *m_var_table;
|
||||
/// Instruction pointer to the first instruction.
|
||||
uint first_ip;
|
||||
|
||||
/*
|
||||
Collection of Item_field proxies, each of them points to the corresponding
|
||||
field in m_var_table.
|
||||
*/
|
||||
Item **m_var_items;
|
||||
/// The constructor.
|
||||
///
|
||||
/// @param _handler sp_handler object.
|
||||
/// @param _first_ip first instruction pointer.
|
||||
sp_handler_entry(const sp_handler *_handler, uint _first_ip)
|
||||
:handler(_handler), first_ip(_first_ip)
|
||||
{ }
|
||||
};
|
||||
|
||||
/*
|
||||
This is a pointer to a field, which should contain return value for stored
|
||||
functions (only). For stored procedures, this pointer is NULL.
|
||||
*/
|
||||
Field *m_return_value_fld;
|
||||
public:
|
||||
/// This class stores basic information about SQL-condition, such as:
|
||||
/// - SQL error code;
|
||||
/// - error level;
|
||||
/// - SQLSTATE;
|
||||
/// - text message.
|
||||
///
|
||||
/// It's used to organize runtime SQL-handler call stack.
|
||||
///
|
||||
/// Standard Sql_condition class can not be used, because we don't always have
|
||||
/// an Sql_condition object for an SQL-condition in Diagnostics_area.
|
||||
///
|
||||
/// Eventually, this class should be moved to sql_error.h, and be a part of
|
||||
/// standard SQL-condition processing (Diagnostics_area should contain an
|
||||
/// object for active SQL-condition, not just information stored in DA's
|
||||
/// fields).
|
||||
class Sql_condition_info : public Sql_alloc
|
||||
{
|
||||
public:
|
||||
/// SQL error code.
|
||||
uint sql_errno;
|
||||
|
||||
/*
|
||||
Indicates whether the return value (in m_return_value_fld) has been set
|
||||
during execution.
|
||||
*/
|
||||
bool m_return_value_set;
|
||||
/// Error level.
|
||||
Sql_condition::enum_warning_level level;
|
||||
|
||||
/**
|
||||
TRUE if the context is created for a sub-statement.
|
||||
*/
|
||||
bool in_sub_stmt;
|
||||
/// SQLSTATE.
|
||||
char sql_state[SQLSTATE_LENGTH + 1];
|
||||
|
||||
sp_handler_t *m_handler; // Visible handlers
|
||||
/// Text message.
|
||||
char *message;
|
||||
|
||||
/**
|
||||
SQL conditions caught by each handler.
|
||||
This is an array indexed by handler index.
|
||||
*/
|
||||
Sql_condition_info *m_raised_conditions;
|
||||
/// The constructor.
|
||||
///
|
||||
/// @param _sql_condition The SQL condition.
|
||||
/// @param arena Query arena for SP
|
||||
Sql_condition_info(const Sql_condition *_sql_condition,
|
||||
Query_arena *arena)
|
||||
:sql_errno(_sql_condition->get_sql_errno()),
|
||||
level(_sql_condition->get_level())
|
||||
{
|
||||
memcpy(sql_state, _sql_condition->get_sqlstate(), SQLSTATE_LENGTH);
|
||||
sql_state[SQLSTATE_LENGTH]= '\0';
|
||||
|
||||
uint m_hcount; // Stack pointer for m_handler
|
||||
uint *m_hstack; // Return stack for continue handlers
|
||||
uint m_hsp; // Stack pointer for m_hstack
|
||||
/** Active handler stack. */
|
||||
sp_active_handler_t *m_in_handler;
|
||||
uint m_ihsp; // Stack pointer for m_in_handler
|
||||
int m_hfound; // Set by find_handler; -1 if not found
|
||||
|
||||
sp_cursor **m_cstack;
|
||||
uint m_ccount;
|
||||
|
||||
Item_cache **m_case_expr_holders;
|
||||
|
||||
/* Previous runtime context (NULL if none) */
|
||||
sp_rcontext *m_prev_runtime_ctx;
|
||||
message= strdup_root(arena->mem_root, _sql_condition->get_message_text());
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
/// This class represents a call frame of SQL-handler (one invocation of a
|
||||
/// handler). Basically, it's needed to store continue instruction pointer for
|
||||
/// CONTINUE SQL-handlers.
|
||||
class Handler_call_frame : public Sql_alloc
|
||||
{
|
||||
public:
|
||||
/// SQL-condition, triggered handler activation.
|
||||
const Sql_condition_info *sql_condition;
|
||||
|
||||
/// Continue-instruction-pointer for CONTINUE-handlers.
|
||||
/// The attribute contains 0 for EXIT-handlers.
|
||||
uint continue_ip;
|
||||
|
||||
/// The constructor.
|
||||
///
|
||||
/// @param _sql_condition SQL-condition, triggered handler activation.
|
||||
/// @param _continue_ip Continue instruction pointer.
|
||||
Handler_call_frame(const Sql_condition_info *_sql_condition,
|
||||
uint _continue_ip)
|
||||
:sql_condition(_sql_condition),
|
||||
continue_ip(_continue_ip)
|
||||
{ }
|
||||
};
|
||||
|
||||
public:
|
||||
/// Arena used to (re) allocate items on. E.g. reallocate INOUT/OUT
|
||||
/// SP-variables when they don't fit into prealloced items. This is common
|
||||
/// situation with String items. It is used mainly in sp_eval_func_item().
|
||||
Query_arena *callers_arena;
|
||||
|
||||
/// Flag to end an open result set before start executing an SQL-handler
|
||||
/// (if one is found). Otherwise the client will hang due to a violation
|
||||
/// of the client/server protocol.
|
||||
bool end_partial_result_set;
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
/// The stored program for which this runtime context is created. Used for
|
||||
/// checking if correct runtime context is used for variable handling.
|
||||
sp_head *sp;
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// SP-variables.
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int set_variable(THD *thd, uint var_idx, Item **value)
|
||||
{ return set_variable(thd, m_var_table->field[var_idx], value); }
|
||||
|
||||
Item *get_item(uint var_idx) const
|
||||
{ return m_var_items[var_idx]; }
|
||||
|
||||
Item **get_item_addr(uint var_idx) const
|
||||
{ return m_var_items.array() + var_idx; }
|
||||
|
||||
bool set_return_value(THD *thd, Item **return_value_item);
|
||||
|
||||
bool is_return_value_set() const
|
||||
{ return m_return_value_set; }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// SQL-handlers.
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Create a new sp_handler_entry instance and push it to the handler call
|
||||
/// stack.
|
||||
///
|
||||
/// @param handler SQL-handler object.
|
||||
/// @param first_ip First instruction pointer of the handler.
|
||||
///
|
||||
/// @return error flag.
|
||||
/// @retval false on success.
|
||||
/// @retval true on error.
|
||||
bool push_handler(sp_handler *handler, uint first_ip);
|
||||
|
||||
/// Pop and delete given number of sp_handler_entry instances from the handler
|
||||
/// call stack.
|
||||
///
|
||||
/// @param count Number of handler entries to pop & delete.
|
||||
void pop_handlers(int count);
|
||||
|
||||
const Sql_condition_info *raised_condition() const
|
||||
{
|
||||
return m_handler_call_stack.elements() ?
|
||||
(*m_handler_call_stack.back())->sql_condition : NULL;
|
||||
}
|
||||
|
||||
/// Handle current SQL condition (if any).
|
||||
///
|
||||
/// This is the public-interface function to handle SQL conditions in
|
||||
/// stored routines.
|
||||
///
|
||||
/// @param thd Thread handle.
|
||||
/// @param ip[out] Instruction pointer to the first handler
|
||||
/// instruction.
|
||||
/// @param cur_spi Current SP instruction.
|
||||
///
|
||||
/// @retval true if an SQL-handler has been activated. That means, all of
|
||||
/// the following conditions are satisfied:
|
||||
/// - the SP-instruction raised SQL-condition(s),
|
||||
/// - and there is an SQL-handler to process at least one of those
|
||||
/// SQL-conditions,
|
||||
/// - and that SQL-handler has been activated.
|
||||
/// Note, that the return value has nothing to do with "error flag"
|
||||
/// semantics.
|
||||
///
|
||||
/// @retval false otherwise.
|
||||
bool handle_sql_condition(THD *thd,
|
||||
uint *ip,
|
||||
const sp_instr *cur_spi);
|
||||
|
||||
/// Remove latest call frame from the handler call stack.
|
||||
///
|
||||
/// @param da Diagnostics area containing handled conditions.
|
||||
///
|
||||
/// @return continue instruction pointer of the removed handler.
|
||||
uint exit_handler(Diagnostics_area *da);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// Cursors.
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Create a new sp_cursor instance and push it to the cursor stack.
|
||||
///
|
||||
/// @param lex_keeper SP-instruction execution helper.
|
||||
/// @param i Cursor-push instruction.
|
||||
///
|
||||
/// @return error flag.
|
||||
/// @retval false on success.
|
||||
/// @retval true on error.
|
||||
bool push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
|
||||
|
||||
/// Pop and delete given number of sp_cursor instance from the cursor stack.
|
||||
///
|
||||
/// @param count Number of cursors to pop & delete.
|
||||
void pop_cursors(uint count);
|
||||
|
||||
void pop_all_cursors()
|
||||
{ pop_cursors(m_ccount); }
|
||||
|
||||
sp_cursor *get_cursor(uint i) const
|
||||
{ return m_cstack[i]; }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// CASE expressions.
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Set CASE expression to the specified value.
|
||||
///
|
||||
/// @param thd Thread handler.
|
||||
/// @param case_expr_id The CASE expression identifier.
|
||||
/// @param case_expr_item The CASE expression value
|
||||
///
|
||||
/// @return error flag.
|
||||
/// @retval false on success.
|
||||
/// @retval true on error.
|
||||
///
|
||||
/// @note The idea is to reuse Item_cache for the expression of the one
|
||||
/// CASE statement. This optimization takes place when there is CASE
|
||||
/// statement inside of a loop. So, in other words, we will use the same
|
||||
/// object on each iteration instead of creating a new one for each
|
||||
/// iteration.
|
||||
///
|
||||
/// TODO
|
||||
/// Hypothetically, a type of CASE expression can be different for each
|
||||
/// iteration. For instance, this can happen if the expression contains
|
||||
/// a session variable (something like @@VAR) and its type is changed
|
||||
/// from one iteration to another.
|
||||
///
|
||||
/// In order to cope with this problem, we check type each time, when we
|
||||
/// use already created object. If the type does not match, we re-create
|
||||
/// Item. This also can (should?) be optimized.
|
||||
bool set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr);
|
||||
|
||||
Item *get_case_expr(int case_expr_id) const
|
||||
{ return m_case_expr_holders[case_expr_id]; }
|
||||
|
||||
Item ** get_case_expr_addr(int case_expr_id) const
|
||||
{ return (Item**) m_case_expr_holders.array() + case_expr_id; }
|
||||
|
||||
private:
|
||||
/// Internal function to allocate memory for arrays.
|
||||
///
|
||||
/// @param thd Thread handle.
|
||||
///
|
||||
/// @return error flag: false on success, true in case of failure.
|
||||
bool alloc_arrays(THD *thd);
|
||||
|
||||
/// Create and initialize a table to store SP-variables.
|
||||
///
|
||||
/// param thd Thread handle.
|
||||
///
|
||||
/// @return error flag.
|
||||
/// @retval false on success.
|
||||
/// @retval true on error.
|
||||
bool init_var_table(THD *thd);
|
||||
bool init_var_items();
|
||||
|
||||
Item_cache *create_case_expr_holder(THD *thd, const Item *item);
|
||||
/// Create and initialize an Item-adapter (Item_field) for each SP-var field.
|
||||
///
|
||||
/// param thd Thread handle.
|
||||
///
|
||||
/// @return error flag.
|
||||
/// @retval false on success.
|
||||
/// @retval true on error.
|
||||
bool init_var_items(THD *thd);
|
||||
|
||||
/// Create an instance of appropriate Item_cache class depending on the
|
||||
/// specified type in the callers arena.
|
||||
///
|
||||
/// @note We should create cache items in the callers arena, as they are
|
||||
/// used between in several instructions.
|
||||
///
|
||||
/// @param thd Thread handler.
|
||||
/// @param item Item to get the expression type.
|
||||
///
|
||||
/// @return Pointer to valid object on success, or NULL in case of error.
|
||||
Item_cache *create_case_expr_holder(THD *thd, const Item *item) const;
|
||||
|
||||
int set_variable(THD *thd, Field *field, Item **value);
|
||||
|
||||
private:
|
||||
/// Top-level (root) parsing context for this runtime context.
|
||||
const sp_pcontext *m_root_parsing_ctx;
|
||||
|
||||
/// Virtual table for storing SP-variables.
|
||||
TABLE *m_var_table;
|
||||
|
||||
/// Collection of Item_field proxies, each of them points to the
|
||||
/// corresponding field in m_var_table.
|
||||
Bounds_checked_array<Item *> m_var_items;
|
||||
|
||||
/// This is a pointer to a field, which should contain return value for
|
||||
/// stored functions (only). For stored procedures, this pointer is NULL.
|
||||
Field *m_return_value_fld;
|
||||
|
||||
/// Indicates whether the return value (in m_return_value_fld) has been
|
||||
/// set during execution.
|
||||
bool m_return_value_set;
|
||||
|
||||
/// Flag to tell if the runtime context is created for a sub-statement.
|
||||
bool m_in_sub_stmt;
|
||||
|
||||
/// Stack of visible handlers.
|
||||
Dynamic_array<sp_handler_entry *> m_handlers;
|
||||
|
||||
/// Stack of caught SQL conditions.
|
||||
Dynamic_array<Handler_call_frame *> m_handler_call_stack;
|
||||
|
||||
/// Stack of cursors.
|
||||
Bounds_checked_array<sp_cursor *> m_cstack;
|
||||
|
||||
/// Current number of cursors in m_cstack.
|
||||
uint m_ccount;
|
||||
|
||||
/// Array of CASE expression holders.
|
||||
Bounds_checked_array<Item_cache *> m_case_expr_holders;
|
||||
}; // class sp_rcontext : public Sql_alloc
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// sp_cursor declaration.
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
An interceptor of cursor result set used to implement
|
||||
FETCH <cname> INTO <varlist>.
|
||||
*/
|
||||
|
||||
class Select_fetch_into_spvars: public select_result_interceptor
|
||||
{
|
||||
List<struct sp_variable> *spvar_list;
|
||||
uint field_count;
|
||||
public:
|
||||
Select_fetch_into_spvars() {} /* Remove gcc warning */
|
||||
uint get_field_count() { return field_count; }
|
||||
void set_spvar_list(List<struct sp_variable> *vars) { spvar_list= vars; }
|
||||
|
||||
virtual bool send_eof() { return FALSE; }
|
||||
virtual int send_data(List<Item> &items);
|
||||
virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
||||
};
|
||||
|
||||
class Server_side_cursor;
|
||||
typedef class st_select_lex_unit SELECT_LEX_UNIT;
|
||||
|
||||
/* A mediator between stored procedures and server side cursors */
|
||||
|
||||
class sp_cursor : public Sql_alloc
|
||||
{
|
||||
public:
|
||||
private:
|
||||
/// An interceptor of cursor result set used to implement
|
||||
/// FETCH <cname> INTO <varlist>.
|
||||
class Select_fetch_into_spvars: public select_result_interceptor
|
||||
{
|
||||
List<sp_variable> *spvar_list;
|
||||
uint field_count;
|
||||
public:
|
||||
Select_fetch_into_spvars() {} /* Remove gcc warning */
|
||||
uint get_field_count() { return field_count; }
|
||||
void set_spvar_list(List<sp_variable> *vars) { spvar_list= vars; }
|
||||
|
||||
virtual bool send_eof() { return FALSE; }
|
||||
virtual bool send_data(List<Item> &items);
|
||||
virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
||||
};
|
||||
|
||||
public:
|
||||
sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
|
||||
|
||||
virtual ~sp_cursor()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
{ destroy(); }
|
||||
|
||||
sp_lex_keeper *
|
||||
get_lex_keeper() { return m_lex_keeper; }
|
||||
sp_lex_keeper *get_lex_keeper() { return m_lex_keeper; }
|
||||
|
||||
int
|
||||
open(THD *thd);
|
||||
int open(THD *thd);
|
||||
|
||||
int
|
||||
close(THD *thd);
|
||||
int close(THD *thd);
|
||||
|
||||
inline bool
|
||||
is_open()
|
||||
{
|
||||
return test(server_side_cursor);
|
||||
}
|
||||
my_bool is_open()
|
||||
{ return test(server_side_cursor); }
|
||||
|
||||
int
|
||||
fetch(THD *, List<struct sp_variable> *vars);
|
||||
int fetch(THD *, List<sp_variable> *vars);
|
||||
|
||||
inline sp_instr_cpush *
|
||||
get_instr()
|
||||
{
|
||||
return m_i;
|
||||
}
|
||||
sp_instr_cpush *get_instr()
|
||||
{ return m_i; }
|
||||
|
||||
private:
|
||||
|
||||
Select_fetch_into_spvars result;
|
||||
sp_lex_keeper *m_lex_keeper;
|
||||
Server_side_cursor *server_side_cursor;
|
||||
sp_instr_cpush *m_i; // My push instruction
|
||||
void
|
||||
destroy();
|
||||
void destroy();
|
||||
|
||||
}; // class sp_cursor : public Sql_alloc
|
||||
|
||||
|
|
132
sql/sql_array.h
132
sql/sql_array.h
|
@ -99,29 +99,65 @@ template <class Elem> class Dynamic_array
|
|||
DYNAMIC_ARRAY array;
|
||||
public:
|
||||
Dynamic_array(uint prealloc=16, uint increment=16)
|
||||
{
|
||||
init(prealloc, increment);
|
||||
}
|
||||
|
||||
void init(uint prealloc=16, uint increment=16)
|
||||
{
|
||||
my_init_dynamic_array(&array, sizeof(Elem), prealloc, increment,
|
||||
MYF(MY_THREAD_SPECIFIC));
|
||||
}
|
||||
|
||||
/**
|
||||
@note Though formally this could be declared "const" it would be
|
||||
misleading at it returns a non-const pointer to array's data.
|
||||
*/
|
||||
Elem& at(int idx)
|
||||
{
|
||||
return *(((Elem*)array.buffer) + idx);
|
||||
}
|
||||
/// Const variant of at(), which cannot change data
|
||||
const Elem& at(int idx) const
|
||||
{
|
||||
return *(((Elem*)array.buffer) + idx);
|
||||
}
|
||||
|
||||
/// @returns pointer to first element; undefined behaviour if array is empty
|
||||
Elem *front()
|
||||
{
|
||||
DBUG_ASSERT(array.elements >= 1);
|
||||
return (Elem*)array.buffer;
|
||||
}
|
||||
|
||||
Elem *back()
|
||||
/// @returns pointer to first element; undefined behaviour if array is empty
|
||||
const Elem *front() const
|
||||
{
|
||||
return ((Elem*)array.buffer) + array.elements;
|
||||
DBUG_ASSERT(array.elements >= 1);
|
||||
return (const Elem*)array.buffer;
|
||||
}
|
||||
|
||||
bool append(Elem &el)
|
||||
/// @returns pointer to last element; undefined behaviour if array is empty.
|
||||
Elem *back()
|
||||
{
|
||||
return (insert_dynamic(&array, (uchar*)&el));
|
||||
DBUG_ASSERT(array.elements >= 1);
|
||||
return ((Elem*)array.buffer) + (array.elements - 1);
|
||||
}
|
||||
|
||||
/// @returns pointer to last element; undefined behaviour if array is empty.
|
||||
const Elem *back() const
|
||||
{
|
||||
DBUG_ASSERT(array.elements >= 1);
|
||||
return ((const Elem*)array.buffer) + (array.elements - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@retval false ok
|
||||
@retval true OOM, @c my_error() has been called.
|
||||
*/
|
||||
bool append(const Elem &el)
|
||||
{
|
||||
return insert_dynamic(&array, &el);
|
||||
}
|
||||
|
||||
/// Pops the last element. Does nothing if array is empty.
|
||||
|
@ -135,11 +171,27 @@ public:
|
|||
delete_dynamic_element(&array, idx);
|
||||
}
|
||||
|
||||
int elements()
|
||||
int elements() const
|
||||
{
|
||||
return array.elements;
|
||||
}
|
||||
|
||||
void elements(uint num_elements)
|
||||
{
|
||||
DBUG_ASSERT(num_elements <= array.max_element);
|
||||
array.elements= num_elements;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
elements(0);
|
||||
}
|
||||
|
||||
void set(uint idx, const Elem &el)
|
||||
{
|
||||
set_dynamic(&array, &el, idx);
|
||||
}
|
||||
|
||||
~Dynamic_array()
|
||||
{
|
||||
delete_dynamic(&array);
|
||||
|
@ -153,74 +205,4 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Array of pointers to Elem that uses memory from MEM_ROOT
|
||||
|
||||
MEM_ROOT has no realloc() so this is supposed to be used for cases when
|
||||
reallocations are rare.
|
||||
*/
|
||||
|
||||
template <class Elem> class Array
|
||||
{
|
||||
enum {alloc_increment = 16};
|
||||
Elem **buffer;
|
||||
uint n_elements, max_element;
|
||||
public:
|
||||
Array(MEM_ROOT *mem_root, uint prealloc=16)
|
||||
{
|
||||
buffer= (Elem**)alloc_root(mem_root, prealloc * sizeof(Elem**));
|
||||
max_element = buffer? prealloc : 0;
|
||||
n_elements= 0;
|
||||
}
|
||||
|
||||
Elem& at(int idx)
|
||||
{
|
||||
return *(((Elem*)buffer) + idx);
|
||||
}
|
||||
|
||||
Elem **front()
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
Elem **back()
|
||||
{
|
||||
return buffer + n_elements;
|
||||
}
|
||||
|
||||
bool append(MEM_ROOT *mem_root, Elem *el)
|
||||
{
|
||||
if (n_elements == max_element)
|
||||
{
|
||||
Elem **newbuf;
|
||||
if (!(newbuf= (Elem**)alloc_root(mem_root, (n_elements + alloc_increment)*
|
||||
sizeof(Elem**))))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
memcpy(newbuf, buffer, n_elements*sizeof(Elem*));
|
||||
buffer= newbuf;
|
||||
}
|
||||
buffer[n_elements++]= el;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int elements()
|
||||
{
|
||||
return n_elements;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
n_elements= 0;
|
||||
}
|
||||
|
||||
typedef int (*CMP_FUNC)(Elem * const *el1, Elem *const *el2);
|
||||
|
||||
void sort(CMP_FUNC cmp_func)
|
||||
{
|
||||
my_qsort(buffer, n_elements, sizeof(Elem*), (qsort_cmp)cmp_func);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* SQL_ARRAY_INCLUDED */
|
||||
|
|
|
@ -72,6 +72,8 @@
|
|||
char internal_table_name[2]= "*";
|
||||
char empty_c_string[1]= {0}; /* used for not defined db */
|
||||
|
||||
LEX_STRING EMPTY_STR= { (char *) "", 0 };
|
||||
|
||||
const char * const THD::DEFAULT_WHERE= "field list";
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -2454,7 +2456,7 @@ void select_send::cleanup()
|
|||
|
||||
/* Send data to client. Returns 0 if ok */
|
||||
|
||||
int select_send::send_data(List<Item> &items)
|
||||
bool select_send::send_data(List<Item> &items)
|
||||
{
|
||||
Protocol *protocol= thd->protocol;
|
||||
DBUG_ENTER("select_send::send_data");
|
||||
|
@ -2744,7 +2746,7 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
|
|||
(int) (uchar) (x) == line_sep_char || \
|
||||
!(x))
|
||||
|
||||
int select_export::send_data(List<Item> &items)
|
||||
bool select_export::send_data(List<Item> &items)
|
||||
{
|
||||
|
||||
DBUG_ENTER("select_export::send_data");
|
||||
|
@ -3003,7 +3005,7 @@ select_dump::prepare(List<Item> &list __attribute__((unused)),
|
|||
}
|
||||
|
||||
|
||||
int select_dump::send_data(List<Item> &items)
|
||||
bool select_dump::send_data(List<Item> &items)
|
||||
{
|
||||
List_iterator_fast<Item> li(items);
|
||||
char buff[MAX_FIELD_WIDTH];
|
||||
|
@ -3051,7 +3053,7 @@ select_subselect::select_subselect(Item_subselect *item_arg)
|
|||
}
|
||||
|
||||
|
||||
int select_singlerow_subselect::send_data(List<Item> &items)
|
||||
bool select_singlerow_subselect::send_data(List<Item> &items)
|
||||
{
|
||||
DBUG_ENTER("select_singlerow_subselect::send_data");
|
||||
Item_singlerow_subselect *it= (Item_singlerow_subselect *)item;
|
||||
|
@ -3085,7 +3087,7 @@ void select_max_min_finder_subselect::cleanup()
|
|||
}
|
||||
|
||||
|
||||
int select_max_min_finder_subselect::send_data(List<Item> &items)
|
||||
bool select_max_min_finder_subselect::send_data(List<Item> &items)
|
||||
{
|
||||
DBUG_ENTER("select_max_min_finder_subselect::send_data");
|
||||
Item_maxmin_subselect *it= (Item_maxmin_subselect *)item;
|
||||
|
@ -3202,7 +3204,7 @@ bool select_max_min_finder_subselect::cmp_str()
|
|||
return (sortcmp(val1, val2, cache->collation.collation) < 0);
|
||||
}
|
||||
|
||||
int select_exists_subselect::send_data(List<Item> &items)
|
||||
bool select_exists_subselect::send_data(List<Item> &items)
|
||||
{
|
||||
DBUG_ENTER("select_exists_subselect::send_data");
|
||||
Item_exists_subselect *it= (Item_exists_subselect *)item;
|
||||
|
@ -3585,7 +3587,7 @@ Statement_map::~Statement_map()
|
|||
my_hash_free(&st_hash);
|
||||
}
|
||||
|
||||
int select_dumpvar::send_data(List<Item> &items)
|
||||
bool select_dumpvar::send_data(List<Item> &items)
|
||||
{
|
||||
List_iterator_fast<my_var> var_li(var_list);
|
||||
List_iterator<Item> it(items);
|
||||
|
@ -3691,7 +3693,7 @@ void select_materialize_with_stats::cleanup()
|
|||
@return FALSE on success
|
||||
*/
|
||||
|
||||
int select_materialize_with_stats::send_data(List<Item> &items)
|
||||
bool select_materialize_with_stats::send_data(List<Item> &items)
|
||||
{
|
||||
List_iterator_fast<Item> item_it(items);
|
||||
Item *cur_item;
|
||||
|
|
|
@ -121,6 +121,7 @@ enum enum_filetype { FILETYPE_CSV, FILETYPE_XML };
|
|||
|
||||
extern char internal_table_name[2];
|
||||
extern char empty_c_string[1];
|
||||
extern LEX_STRING EMPTY_STR;
|
||||
extern MYSQL_PLUGIN_IMPORT const char **errmesg;
|
||||
|
||||
extern bool volatile shutdown_in_progress;
|
||||
|
@ -3465,7 +3466,7 @@ public:
|
|||
send_data returns 0 on ok, 1 on error and -1 if data was ignored, for
|
||||
example for a duplicate row entry written to a temp table.
|
||||
*/
|
||||
virtual int send_data(List<Item> &items)=0;
|
||||
virtual bool send_data(List<Item> &items)=0;
|
||||
virtual ~select_result_sink() {};
|
||||
};
|
||||
|
||||
|
@ -3557,7 +3558,7 @@ public:
|
|||
TABLE *dst_table; /* table to write into */
|
||||
|
||||
/* The following is called in the child thread: */
|
||||
int send_data(List<Item> &items);
|
||||
bool send_data(List<Item> &items);
|
||||
};
|
||||
|
||||
|
||||
|
@ -3592,7 +3593,7 @@ class select_send :public select_result {
|
|||
public:
|
||||
select_send() :is_result_set_started(FALSE) {}
|
||||
bool send_result_set_metadata(List<Item> &list, uint flags);
|
||||
int send_data(List<Item> &items);
|
||||
bool send_data(List<Item> &items);
|
||||
bool send_eof();
|
||||
virtual bool check_simple_select() const { return FALSE; }
|
||||
void abort_result_set();
|
||||
|
@ -3655,7 +3656,7 @@ public:
|
|||
select_export(sql_exchange *ex) :select_to_file(ex) {}
|
||||
~select_export();
|
||||
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
||||
int send_data(List<Item> &items);
|
||||
bool send_data(List<Item> &items);
|
||||
};
|
||||
|
||||
|
||||
|
@ -3663,7 +3664,7 @@ class select_dump :public select_to_file {
|
|||
public:
|
||||
select_dump(sql_exchange *ex) :select_to_file(ex) {}
|
||||
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
||||
int send_data(List<Item> &items);
|
||||
bool send_data(List<Item> &items);
|
||||
};
|
||||
|
||||
|
||||
|
@ -3682,7 +3683,7 @@ class select_insert :public select_result_interceptor {
|
|||
~select_insert();
|
||||
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
||||
virtual int prepare2(void);
|
||||
virtual int send_data(List<Item> &items);
|
||||
virtual bool send_data(List<Item> &items);
|
||||
virtual void store_values(List<Item> &values);
|
||||
virtual bool can_rollback_data() { return 0; }
|
||||
void send_error(uint errcode,const char *err);
|
||||
|
@ -3865,7 +3866,7 @@ public:
|
|||
|
||||
select_union() :write_err(0), table(0), records(0) { tmp_table_param.init(); }
|
||||
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
||||
int send_data(List<Item> &items);
|
||||
bool send_data(List<Item> &items);
|
||||
bool send_eof();
|
||||
bool flush();
|
||||
void cleanup();
|
||||
|
@ -3884,7 +3885,7 @@ protected:
|
|||
Item_subselect *item;
|
||||
public:
|
||||
select_subselect(Item_subselect *item);
|
||||
int send_data(List<Item> &items)=0;
|
||||
bool send_data(List<Item> &items)=0;
|
||||
bool send_eof() { return 0; };
|
||||
};
|
||||
|
||||
|
@ -3895,7 +3896,7 @@ public:
|
|||
select_singlerow_subselect(Item_subselect *item_arg)
|
||||
:select_subselect(item_arg)
|
||||
{}
|
||||
int send_data(List<Item> &items);
|
||||
bool send_data(List<Item> &items);
|
||||
};
|
||||
|
||||
|
||||
|
@ -3945,7 +3946,7 @@ public:
|
|||
bool bit_fields_as_long,
|
||||
bool create_table);
|
||||
bool init_result_table(ulonglong select_options);
|
||||
int send_data(List<Item> &items);
|
||||
bool send_data(List<Item> &items);
|
||||
void cleanup();
|
||||
ha_rows get_null_count_of_col(uint idx)
|
||||
{
|
||||
|
@ -3979,7 +3980,7 @@ public:
|
|||
:select_subselect(item_arg), cache(0), fmax(mx), is_all(all)
|
||||
{}
|
||||
void cleanup();
|
||||
int send_data(List<Item> &items);
|
||||
bool send_data(List<Item> &items);
|
||||
bool cmp_real();
|
||||
bool cmp_int();
|
||||
bool cmp_decimal();
|
||||
|
@ -3992,7 +3993,7 @@ class select_exists_subselect :public select_subselect
|
|||
public:
|
||||
select_exists_subselect(Item_subselect *item_arg)
|
||||
:select_subselect(item_arg){}
|
||||
int send_data(List<Item> &items);
|
||||
bool send_data(List<Item> &items);
|
||||
};
|
||||
|
||||
|
||||
|
@ -4244,7 +4245,7 @@ public:
|
|||
multi_delete(TABLE_LIST *dt, uint num_of_tables);
|
||||
~multi_delete();
|
||||
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
||||
int send_data(List<Item> &items);
|
||||
bool send_data(List<Item> &items);
|
||||
bool initialize_tables (JOIN *join);
|
||||
void send_error(uint errcode,const char *err);
|
||||
int do_deletes();
|
||||
|
@ -4292,7 +4293,7 @@ public:
|
|||
enum_duplicates handle_duplicates, bool ignore);
|
||||
~multi_update();
|
||||
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
||||
int send_data(List<Item> &items);
|
||||
bool send_data(List<Item> &items);
|
||||
bool initialize_tables (JOIN *join);
|
||||
void send_error(uint errcode,const char *err);
|
||||
int do_updates();
|
||||
|
@ -4335,7 +4336,7 @@ public:
|
|||
select_dumpvar() { var_list.empty(); row_count= 0;}
|
||||
~select_dumpvar() {}
|
||||
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
||||
int send_data(List<Item> &items);
|
||||
bool send_data(List<Item> &items);
|
||||
bool send_eof();
|
||||
virtual bool check_simple_select() const;
|
||||
void cleanup();
|
||||
|
|
|
@ -759,7 +759,7 @@ multi_delete::~multi_delete()
|
|||
}
|
||||
|
||||
|
||||
int multi_delete::send_data(List<Item> &values)
|
||||
bool multi_delete::send_data(List<Item> &values)
|
||||
{
|
||||
int secure_counter= delete_while_scanning ? -1 : 0;
|
||||
TABLE_LIST *del_table;
|
||||
|
|
|
@ -1001,3 +1001,32 @@ uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs,
|
|||
*errors= error_count;
|
||||
return (uint32) (to - to_start);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Sanity check for SQLSTATEs. The function does not check if it's really an
|
||||
existing SQL-state (there are just too many), it just checks string length and
|
||||
looks for bad characters.
|
||||
|
||||
@param sqlstate the condition SQLSTATE.
|
||||
|
||||
@retval true if it's ok.
|
||||
@retval false if it's bad.
|
||||
*/
|
||||
|
||||
bool is_sqlstate_valid(const LEX_STRING *sqlstate)
|
||||
{
|
||||
if (sqlstate->length != 5)
|
||||
return false;
|
||||
|
||||
for (int i= 0 ; i < 5 ; ++i)
|
||||
{
|
||||
char c = sqlstate->str[i];
|
||||
|
||||
if ((c < '0' || '9' < c) &&
|
||||
(c < 'A' || 'Z' < c))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3537,7 +3537,7 @@ select_insert::~select_insert()
|
|||
}
|
||||
|
||||
|
||||
int select_insert::send_data(List<Item> &values)
|
||||
bool select_insert::send_data(List<Item> &values)
|
||||
{
|
||||
DBUG_ENTER("select_insert::send_data");
|
||||
bool error=0;
|
||||
|
|
|
@ -126,7 +126,7 @@ class Select_fetch_protocol_binary: public select_send
|
|||
public:
|
||||
Select_fetch_protocol_binary(THD *thd);
|
||||
virtual bool send_result_set_metadata(List<Item> &list, uint flags);
|
||||
virtual int send_data(List<Item> &items);
|
||||
virtual bool send_data(List<Item> &items);
|
||||
virtual bool send_eof();
|
||||
#ifdef EMBEDDED_LIBRARY
|
||||
void begin_dataset()
|
||||
|
@ -3057,7 +3057,7 @@ bool Select_fetch_protocol_binary::send_eof()
|
|||
}
|
||||
|
||||
|
||||
int
|
||||
bool
|
||||
Select_fetch_protocol_binary::send_data(List<Item> &fields)
|
||||
{
|
||||
Protocol *save_protocol= thd->protocol;
|
||||
|
|
|
@ -2367,7 +2367,7 @@ void Show_explain_request::call_in_target_thread()
|
|||
}
|
||||
|
||||
|
||||
int select_result_explain_buffer::send_data(List<Item> &items)
|
||||
bool select_result_explain_buffer::send_data(List<Item> &items)
|
||||
{
|
||||
int res;
|
||||
THD *cur_thd= current_thd;
|
||||
|
@ -5713,16 +5713,16 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table,
|
|||
for (uint i= 0 ; i < params ; i++)
|
||||
{
|
||||
const char *tmp_buff;
|
||||
sp_variable_t *spvar= spcont->find_variable(i);
|
||||
sp_variable *spvar= spcont->find_variable(i);
|
||||
field_def= &spvar->field_def;
|
||||
switch (spvar->mode) {
|
||||
case sp_param_in:
|
||||
case sp_variable::MODE_IN:
|
||||
tmp_buff= "IN";
|
||||
break;
|
||||
case sp_param_out:
|
||||
case sp_variable::MODE_OUT:
|
||||
tmp_buff= "OUT";
|
||||
break;
|
||||
case sp_param_inout:
|
||||
case sp_variable::MODE_INOUT:
|
||||
tmp_buff= "INOUT";
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -115,8 +115,8 @@ void Sql_cmd_common_signal::eval_defaults(THD *thd, Sql_condition *cond)
|
|||
/*
|
||||
SIGNAL is restricted in sql_yacc.yy to only signal SQLSTATE conditions.
|
||||
*/
|
||||
DBUG_ASSERT(m_cond->type == sp_cond_type::state);
|
||||
sqlstate= m_cond->sqlstate;
|
||||
DBUG_ASSERT(m_cond->type == sp_condition_value::SQLSTATE);
|
||||
sqlstate= m_cond->sql_state;
|
||||
cond->set_sqlstate(sqlstate);
|
||||
}
|
||||
else
|
||||
|
@ -488,8 +488,8 @@ bool Sql_cmd_signal::execute(THD *thd)
|
|||
|
||||
bool Sql_cmd_resignal::execute(THD *thd)
|
||||
{
|
||||
Sql_condition_info *signaled;
|
||||
Diagnostics_area *da= thd->get_stmt_da();
|
||||
const sp_rcontext::Sql_condition_info *signaled;
|
||||
int result= TRUE;
|
||||
|
||||
DBUG_ENTER("Resignal_statement::execute");
|
||||
|
@ -505,16 +505,31 @@ bool Sql_cmd_resignal::execute(THD *thd)
|
|||
}
|
||||
|
||||
Sql_condition signaled_err(thd->mem_root);
|
||||
signaled_err.set(signaled->m_sql_errno,
|
||||
signaled->m_sql_state,
|
||||
signaled->m_level,
|
||||
signaled->m_message);
|
||||
signaled_err.set(signaled->sql_errno,
|
||||
signaled->sql_state,
|
||||
signaled->level,
|
||||
signaled->message);
|
||||
|
||||
if (m_cond == NULL)
|
||||
{
|
||||
/* RESIGNAL without signal_value */
|
||||
result= raise_condition(thd, &signaled_err);
|
||||
DBUG_RETURN(result);
|
||||
query_cache_abort(&thd->query_cache_tls);
|
||||
|
||||
/* Keep handled conditions. */
|
||||
da->unmark_sql_conditions_from_removal();
|
||||
|
||||
/* Check if the old condition still exists. */
|
||||
if (da->has_sql_condition(signaled->message, strlen(signaled->message)))
|
||||
{
|
||||
/* Make room for the new RESIGNAL condition. */
|
||||
da->reserve_space(thd, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Make room for old condition + the new RESIGNAL condition. */
|
||||
da->reserve_space(thd, 2);
|
||||
|
||||
da->push_warning(thd, &signaled_err);
|
||||
}
|
||||
}
|
||||
|
||||
/* RESIGNAL with signal_value */
|
||||
|
|
|
@ -29,7 +29,7 @@ protected:
|
|||
@param cond the condition signaled if any, or NULL.
|
||||
@param set collection of signal condition item assignments.
|
||||
*/
|
||||
Sql_cmd_common_signal(const sp_cond_type *cond,
|
||||
Sql_cmd_common_signal(const sp_condition_value *cond,
|
||||
const Set_signal_information& set)
|
||||
: Sql_cmd(),
|
||||
m_cond(cond),
|
||||
|
@ -80,7 +80,7 @@ protected:
|
|||
The condition to signal or resignal.
|
||||
This member is optional and can be NULL (RESIGNAL).
|
||||
*/
|
||||
const sp_cond_type *m_cond;
|
||||
const sp_condition_value *m_cond;
|
||||
|
||||
/**
|
||||
Collection of 'SET item = value' assignments in the
|
||||
|
@ -100,7 +100,7 @@ public:
|
|||
@param cond the SQL condition to signal (required).
|
||||
@param set the collection of signal informations to signal.
|
||||
*/
|
||||
Sql_cmd_signal(const sp_cond_type *cond,
|
||||
Sql_cmd_signal(const sp_condition_value *cond,
|
||||
const Set_signal_information& set)
|
||||
: Sql_cmd_common_signal(cond, set)
|
||||
{}
|
||||
|
@ -127,7 +127,7 @@ public:
|
|||
@param cond the SQL condition to resignal (optional, may be NULL).
|
||||
@param set the collection of signal informations to resignal.
|
||||
*/
|
||||
Sql_cmd_resignal(const sp_cond_type *cond,
|
||||
Sql_cmd_resignal(const sp_condition_value *cond,
|
||||
const Set_signal_information& set)
|
||||
: Sql_cmd_common_signal(cond, set)
|
||||
{}
|
||||
|
|
|
@ -52,7 +52,7 @@ int select_union::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
|
|||
}
|
||||
|
||||
|
||||
int select_union::send_data(List<Item> &values)
|
||||
bool select_union::send_data(List<Item> &values)
|
||||
{
|
||||
if (unit->offset_limit_cnt)
|
||||
{ // using limit offset,count
|
||||
|
|
|
@ -1829,7 +1829,7 @@ multi_update::~multi_update()
|
|||
}
|
||||
|
||||
|
||||
int multi_update::send_data(List<Item> ¬_used_values)
|
||||
bool multi_update::send_data(List<Item> ¬_used_values)
|
||||
{
|
||||
TABLE_LIST *cur_table;
|
||||
DBUG_ENTER("multi_update::send_data");
|
||||
|
|
234
sql/sql_yacc.yy
234
sql/sql_yacc.yy
|
@ -284,7 +284,7 @@ void case_stmt_action_case(LEX *lex)
|
|||
(Instruction 12 in the example)
|
||||
*/
|
||||
|
||||
lex->spcont->push_label((char *)"", lex->sphead->instructions());
|
||||
lex->spcont->push_label(current_thd, EMPTY_STR, lex->sphead->instructions());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -353,7 +353,7 @@ int case_stmt_action_when(LEX *lex, Item *when, bool simple)
|
|||
*/
|
||||
|
||||
return !test(i) ||
|
||||
sp->push_backpatch(i, ctx->push_label((char *)"", 0)) ||
|
||||
sp->push_backpatch(i, ctx->push_label(current_thd, EMPTY_STR, 0)) ||
|
||||
sp->add_cont_backpatch(i) ||
|
||||
sp->add_instr(i);
|
||||
}
|
||||
|
@ -469,7 +469,7 @@ set_system_variable(THD *thd, struct sys_var_with_base *tmp,
|
|||
*/
|
||||
|
||||
static bool
|
||||
set_local_variable(THD *thd, sp_variable_t *spv, Item *val)
|
||||
set_local_variable(THD *thd, sp_variable *spv, Item *val)
|
||||
{
|
||||
Item *it;
|
||||
LEX *lex= thd->lex;
|
||||
|
@ -477,8 +477,8 @@ set_local_variable(THD *thd, sp_variable_t *spv, Item *val)
|
|||
|
||||
if (val)
|
||||
it= val;
|
||||
else if (spv->dflt)
|
||||
it= spv->dflt;
|
||||
else if (spv->default_value)
|
||||
it= spv->default_value;
|
||||
else
|
||||
{
|
||||
it= new (thd->mem_root) Item_null();
|
||||
|
@ -559,7 +559,7 @@ set_trigger_new_row(THD *thd, LEX_STRING *name, Item *val)
|
|||
@return An Item_splocal object representing the SP variable, or NULL on error.
|
||||
*/
|
||||
static Item_splocal*
|
||||
create_item_for_sp_var(THD *thd, LEX_STRING name, sp_variable_t *spvar,
|
||||
create_item_for_sp_var(THD *thd, LEX_STRING name, sp_variable *spvar,
|
||||
const char *start_in_q, const char *end_in_q)
|
||||
{
|
||||
Item_splocal *item;
|
||||
|
@ -569,7 +569,7 @@ create_item_for_sp_var(THD *thd, LEX_STRING name, sp_variable_t *spvar,
|
|||
|
||||
/* If necessary, look for the variable. */
|
||||
if (spc && !spvar)
|
||||
spvar= spc->find_variable(&name);
|
||||
spvar= spc->find_variable(name, false);
|
||||
|
||||
if (!spvar)
|
||||
{
|
||||
|
@ -931,7 +931,7 @@ static bool sp_create_assignment_instr(THD *thd, bool no_lookahead)
|
|||
timestamp_type date_time_type;
|
||||
st_select_lex *select_lex;
|
||||
chooser_compare_func_creator boolfunc2creator;
|
||||
struct sp_cond_type *spcondtype;
|
||||
class sp_condition_value *spcondvalue;
|
||||
struct { int vars, conds, hndlrs, curs; } spblock;
|
||||
sp_name *spname;
|
||||
LEX *lex;
|
||||
|
@ -1857,7 +1857,7 @@ END_OF_INPUT
|
|||
%type <NONE> case_stmt_specification simple_case_stmt searched_case_stmt
|
||||
|
||||
%type <num> sp_decl_idents sp_opt_inout sp_handler_type sp_hcond_list
|
||||
%type <spcondtype> sp_cond sp_hcond sqlstate signal_value opt_signal_value
|
||||
%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value
|
||||
%type <spblock> sp_decls sp_decl
|
||||
%type <lex> sp_cursor_stmt
|
||||
%type <spname> sp_name
|
||||
|
@ -2822,14 +2822,16 @@ sp_fdparam:
|
|||
LEX *lex= Lex;
|
||||
sp_pcontext *spc= lex->spcont;
|
||||
|
||||
if (spc->find_variable(&$1, TRUE))
|
||||
if (spc->find_variable($1, TRUE))
|
||||
{
|
||||
my_error(ER_SP_DUP_PARAM, MYF(0), $1.str);
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
sp_variable_t *spvar= spc->push_variable(&$1,
|
||||
(enum enum_field_types)$3,
|
||||
sp_param_in);
|
||||
|
||||
sp_variable *spvar= spc->add_variable(YYTHD,
|
||||
$1,
|
||||
(enum enum_field_types) $3,
|
||||
sp_variable::MODE_IN);
|
||||
|
||||
if (lex->sphead->fill_field_definition(YYTHD, lex,
|
||||
(enum enum_field_types) $3,
|
||||
|
@ -2859,14 +2861,15 @@ sp_pdparam:
|
|||
LEX *lex= Lex;
|
||||
sp_pcontext *spc= lex->spcont;
|
||||
|
||||
if (spc->find_variable(&$3, TRUE))
|
||||
if (spc->find_variable($3, TRUE))
|
||||
{
|
||||
my_error(ER_SP_DUP_PARAM, MYF(0), $3.str);
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
sp_variable_t *spvar= spc->push_variable(&$3,
|
||||
(enum enum_field_types)$4,
|
||||
(sp_param_mode_t)$1);
|
||||
sp_variable *spvar= spc->add_variable(YYTHD,
|
||||
$3,
|
||||
(enum enum_field_types) $4,
|
||||
(sp_variable::enum_mode) $1);
|
||||
|
||||
if (lex->sphead->fill_field_definition(YYTHD, lex,
|
||||
(enum enum_field_types) $4,
|
||||
|
@ -2880,10 +2883,10 @@ sp_pdparam:
|
|||
;
|
||||
|
||||
sp_opt_inout:
|
||||
/* Empty */ { $$= sp_param_in; }
|
||||
| IN_SYM { $$= sp_param_in; }
|
||||
| OUT_SYM { $$= sp_param_out; }
|
||||
| INOUT_SYM { $$= sp_param_inout; }
|
||||
/* Empty */ { $$= sp_variable::MODE_IN; }
|
||||
| IN_SYM { $$= sp_variable::MODE_IN; }
|
||||
| OUT_SYM { $$= sp_variable::MODE_OUT; }
|
||||
| INOUT_SYM { $$= sp_variable::MODE_INOUT; }
|
||||
;
|
||||
|
||||
sp_proc_stmts:
|
||||
|
@ -2955,13 +2958,13 @@ sp_decl:
|
|||
for (uint i = num_vars-$2 ; i < num_vars ; i++)
|
||||
{
|
||||
uint var_idx= pctx->var_context2runtime(i);
|
||||
sp_variable_t *spvar= pctx->find_variable(var_idx);
|
||||
sp_variable *spvar= pctx->find_variable(var_idx);
|
||||
|
||||
if (!spvar)
|
||||
MYSQL_YYABORT;
|
||||
|
||||
spvar->type= var_type;
|
||||
spvar->dflt= dflt_value_item;
|
||||
spvar->default_value= dflt_value_item;
|
||||
|
||||
if (lex->sphead->fill_field_definition(YYTHD, lex, var_type,
|
||||
&spvar->field_def))
|
||||
|
@ -2997,36 +3000,41 @@ sp_decl:
|
|||
LEX *lex= Lex;
|
||||
sp_pcontext *spc= lex->spcont;
|
||||
|
||||
if (spc->find_cond(&$2, TRUE))
|
||||
if (spc->find_condition($2, TRUE))
|
||||
{
|
||||
my_error(ER_SP_DUP_COND, MYF(0), $2.str);
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
if(YYTHD->lex->spcont->push_cond(&$2, $5))
|
||||
if(spc->add_condition(YYTHD, $2, $5))
|
||||
MYSQL_YYABORT;
|
||||
$$.vars= $$.hndlrs= $$.curs= 0;
|
||||
$$.conds= 1;
|
||||
}
|
||||
| DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM
|
||||
{
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= Lex;
|
||||
sp_head *sp= lex->sphead;
|
||||
|
||||
lex->spcont= lex->spcont->push_context(LABEL_HANDLER_SCOPE);
|
||||
sp_handler *h= lex->spcont->add_handler(thd,
|
||||
(sp_handler::enum_type) $2);
|
||||
|
||||
lex->spcont= lex->spcont->push_context(thd,
|
||||
sp_pcontext::HANDLER_SCOPE);
|
||||
|
||||
sp_pcontext *ctx= lex->spcont;
|
||||
sp_instr_hpush_jump *i=
|
||||
new sp_instr_hpush_jump(sp->instructions(), ctx, $2,
|
||||
ctx->current_var_count());
|
||||
new sp_instr_hpush_jump(sp->instructions(), ctx, h);
|
||||
|
||||
if (i == NULL || sp->add_instr(i))
|
||||
MYSQL_YYABORT;
|
||||
|
||||
/* For continue handlers, mark end of handler scope. */
|
||||
if ($2 == SP_HANDLER_CONTINUE &&
|
||||
if ($2 == sp_handler::CONTINUE &&
|
||||
sp->push_backpatch(i, ctx->last_label()))
|
||||
MYSQL_YYABORT;
|
||||
|
||||
if (sp->push_backpatch(i, ctx->push_label(empty_c_string, 0)))
|
||||
if (sp->push_backpatch(i, ctx->push_label(thd, EMPTY_STR, 0)))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
sp_hcond_list sp_proc_stmt
|
||||
|
@ -3034,20 +3042,19 @@ sp_decl:
|
|||
LEX *lex= Lex;
|
||||
sp_head *sp= lex->sphead;
|
||||
sp_pcontext *ctx= lex->spcont;
|
||||
sp_label_t *hlab= lex->spcont->pop_label(); /* After this hdlr */
|
||||
sp_label *hlab= lex->spcont->pop_label(); /* After this hdlr */
|
||||
sp_instr_hreturn *i;
|
||||
|
||||
if ($2 == SP_HANDLER_CONTINUE)
|
||||
if ($2 == sp_handler::CONTINUE)
|
||||
{
|
||||
i= new sp_instr_hreturn(sp->instructions(), ctx,
|
||||
ctx->current_var_count());
|
||||
i= new sp_instr_hreturn(sp->instructions(), ctx);
|
||||
if (i == NULL ||
|
||||
sp->add_instr(i))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
else
|
||||
{ /* EXIT or UNDO handler, just jump to the end of the block */
|
||||
i= new sp_instr_hreturn(sp->instructions(), ctx, 0);
|
||||
i= new sp_instr_hreturn(sp->instructions(), ctx);
|
||||
if (i == NULL ||
|
||||
sp->add_instr(i) ||
|
||||
sp->push_backpatch(i, lex->spcont->last_label())) /* Block end */
|
||||
|
@ -3058,8 +3065,7 @@ sp_decl:
|
|||
lex->spcont= ctx->pop_context();
|
||||
|
||||
$$.vars= $$.conds= $$.curs= 0;
|
||||
$$.hndlrs= $6;
|
||||
lex->spcont->add_handlers($6);
|
||||
$$.hndlrs= 1;
|
||||
}
|
||||
| DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt
|
||||
{
|
||||
|
@ -3069,7 +3075,7 @@ sp_decl:
|
|||
uint offp;
|
||||
sp_instr_cpush *i;
|
||||
|
||||
if (ctx->find_cursor(&$2, &offp, TRUE))
|
||||
if (ctx->find_cursor($2, &offp, TRUE))
|
||||
{
|
||||
my_error(ER_SP_DUP_CURS, MYF(0), $2.str);
|
||||
delete $5;
|
||||
|
@ -3079,7 +3085,7 @@ sp_decl:
|
|||
ctx->current_cursor_count());
|
||||
if (i == NULL ||
|
||||
sp->add_instr(i) ||
|
||||
ctx->push_cursor(&$2))
|
||||
ctx->add_cursor($2))
|
||||
MYSQL_YYABORT;
|
||||
$$.vars= $$.conds= $$.hndlrs= 0;
|
||||
$$.curs= 1;
|
||||
|
@ -3110,9 +3116,9 @@ sp_cursor_stmt:
|
|||
;
|
||||
|
||||
sp_handler_type:
|
||||
EXIT_SYM { $$= SP_HANDLER_EXIT; }
|
||||
| CONTINUE_SYM { $$= SP_HANDLER_CONTINUE; }
|
||||
/*| UNDO_SYM { QQ No yet } */
|
||||
EXIT_SYM { $$= sp_handler::EXIT; }
|
||||
| CONTINUE_SYM { $$= sp_handler::CONTINUE; }
|
||||
/*| UNDO_SYM { QQ No yet } */
|
||||
;
|
||||
|
||||
sp_hcond_list:
|
||||
|
@ -3129,7 +3135,7 @@ sp_hcond_element:
|
|||
sp_head *sp= lex->sphead;
|
||||
sp_pcontext *ctx= lex->spcont->parent_context();
|
||||
|
||||
if (ctx->find_handler($1))
|
||||
if (ctx->check_duplicate_handler($1))
|
||||
{
|
||||
my_message(ER_SP_DUP_HANDLER, ER(ER_SP_DUP_HANDLER), MYF(0));
|
||||
MYSQL_YYABORT;
|
||||
|
@ -3140,7 +3146,6 @@ sp_hcond_element:
|
|||
(sp_instr_hpush_jump *)sp->last_instruction();
|
||||
|
||||
i->add_condition($1);
|
||||
ctx->push_handler($1);
|
||||
}
|
||||
}
|
||||
;
|
||||
|
@ -3153,11 +3158,9 @@ sp_cond:
|
|||
my_error(ER_WRONG_VALUE, MYF(0), "CONDITION", "0");
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
$$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
|
||||
$$= new (YYTHD->mem_root) sp_condition_value($1);
|
||||
if ($$ == NULL)
|
||||
MYSQL_YYABORT;
|
||||
$$->type= sp_cond_type_t::number;
|
||||
$$->mysqlerr= $1;
|
||||
}
|
||||
| sqlstate
|
||||
;
|
||||
|
@ -3165,17 +3168,22 @@ sp_cond:
|
|||
sqlstate:
|
||||
SQLSTATE_SYM opt_value TEXT_STRING_literal
|
||||
{ /* SQLSTATE */
|
||||
if (!sp_cond_check(&$3))
|
||||
|
||||
/*
|
||||
An error is triggered:
|
||||
- if the specified string is not a valid SQLSTATE,
|
||||
- or if it represents the completion condition -- it is not
|
||||
allowed to SIGNAL, or declare a handler for the completion
|
||||
condition.
|
||||
*/
|
||||
if (!is_sqlstate_valid(&$3) || is_sqlstate_completion($3.str))
|
||||
{
|
||||
my_error(ER_SP_BAD_SQLSTATE, MYF(0), $3.str);
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
$$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
|
||||
$$= new (YYTHD->mem_root) sp_condition_value($3.str);
|
||||
if ($$ == NULL)
|
||||
MYSQL_YYABORT;
|
||||
$$->type= sp_cond_type_t::state;
|
||||
memcpy($$->sqlstate, $3.str, SQLSTATE_LENGTH);
|
||||
$$->sqlstate[SQLSTATE_LENGTH]= '\0';
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -3191,7 +3199,7 @@ sp_hcond:
|
|||
}
|
||||
| ident /* CONDITION name */
|
||||
{
|
||||
$$= Lex->spcont->find_cond(&$1);
|
||||
$$= Lex->spcont->find_condition($1, false);
|
||||
if ($$ == NULL)
|
||||
{
|
||||
my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str);
|
||||
|
@ -3200,24 +3208,22 @@ sp_hcond:
|
|||
}
|
||||
| SQLWARNING_SYM /* SQLSTATEs 01??? */
|
||||
{
|
||||
$$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
|
||||
$$= new (YYTHD->mem_root) sp_condition_value(sp_condition_value::WARNING);
|
||||
if ($$ == NULL)
|
||||
MYSQL_YYABORT;
|
||||
$$->type= sp_cond_type_t::warning;
|
||||
}
|
||||
| not FOUND_SYM /* SQLSTATEs 02??? */
|
||||
{
|
||||
$$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
|
||||
$$= new (YYTHD->mem_root) sp_condition_value(sp_condition_value::NOT_FOUND);
|
||||
if ($$ == NULL)
|
||||
MYSQL_YYABORT;
|
||||
$$->type= sp_cond_type_t::notfound;
|
||||
}
|
||||
| SQLEXCEPTION_SYM /* All other SQLSTATEs */
|
||||
{
|
||||
$$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
|
||||
$$= (sp_condition_value *)YYTHD->alloc(sizeof(sp_condition_value));
|
||||
$$= new (YYTHD->mem_root) sp_condition_value(sp_condition_value::EXCEPTION);
|
||||
if ($$ == NULL)
|
||||
MYSQL_YYABORT;
|
||||
$$->type= sp_cond_type_t::exception;
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -3240,20 +3246,20 @@ signal_value:
|
|||
ident
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_cond_type_t *cond;
|
||||
sp_condition_value *cond;
|
||||
if (lex->spcont == NULL)
|
||||
{
|
||||
/* SIGNAL foo cannot be used outside of stored programs */
|
||||
my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str);
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
cond= lex->spcont->find_cond(&$1);
|
||||
cond= lex->spcont->find_condition($1, false);
|
||||
if (cond == NULL)
|
||||
{
|
||||
my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str);
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
if (cond->type != sp_cond_type_t::state)
|
||||
if (cond->type != sp_condition_value::SQLSTATE)
|
||||
{
|
||||
my_error(ER_SIGNAL_BAD_CONDITION_TYPE, MYF(0));
|
||||
MYSQL_YYABORT;
|
||||
|
@ -3530,12 +3536,15 @@ sp_decl_idents:
|
|||
LEX *lex= Lex;
|
||||
sp_pcontext *spc= lex->spcont;
|
||||
|
||||
if (spc->find_variable(&$1, TRUE))
|
||||
if (spc->find_variable($1, TRUE))
|
||||
{
|
||||
my_error(ER_SP_DUP_VAR, MYF(0), $1.str);
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
spc->push_variable(&$1, (enum_field_types)0, sp_param_in);
|
||||
spc->add_variable(YYTHD,
|
||||
$1,
|
||||
MYSQL_TYPE_DECIMAL,
|
||||
sp_variable::MODE_IN);
|
||||
$$= 1;
|
||||
}
|
||||
| sp_decl_idents ',' ident
|
||||
|
@ -3545,12 +3554,15 @@ sp_decl_idents:
|
|||
LEX *lex= Lex;
|
||||
sp_pcontext *spc= lex->spcont;
|
||||
|
||||
if (spc->find_variable(&$3, TRUE))
|
||||
if (spc->find_variable($3, TRUE))
|
||||
{
|
||||
my_error(ER_SP_DUP_VAR, MYF(0), $3.str);
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
spc->push_variable(&$3, (enum_field_types)0, sp_param_in);
|
||||
spc->add_variable(YYTHD,
|
||||
$3,
|
||||
MYSQL_TYPE_DECIMAL,
|
||||
sp_variable::MODE_IN);
|
||||
$$= $1 + 1;
|
||||
}
|
||||
;
|
||||
|
@ -3672,7 +3684,9 @@ sp_proc_stmt_unlabeled:
|
|||
{ /* Unlabeled controls get a secret label. */
|
||||
LEX *lex= Lex;
|
||||
|
||||
lex->spcont->push_label((char *)"", lex->sphead->instructions());
|
||||
lex->spcont->push_label(YYTHD,
|
||||
EMPTY_STR,
|
||||
lex->sphead->instructions());
|
||||
}
|
||||
sp_unlabeled_control
|
||||
{
|
||||
|
@ -3688,7 +3702,7 @@ sp_proc_stmt_leave:
|
|||
LEX *lex= Lex;
|
||||
sp_head *sp = lex->sphead;
|
||||
sp_pcontext *ctx= lex->spcont;
|
||||
sp_label_t *lab= ctx->find_label($2.str);
|
||||
sp_label *lab= ctx->find_label($2);
|
||||
|
||||
if (! lab)
|
||||
{
|
||||
|
@ -3708,7 +3722,7 @@ sp_proc_stmt_leave:
|
|||
there are no hpop/cpop at the jump destination,
|
||||
so we should include the block context here for cleanup.
|
||||
*/
|
||||
bool exclusive= (lab->type == SP_LAB_BEGIN);
|
||||
bool exclusive= (lab->type == sp_label::BEGIN);
|
||||
|
||||
n= ctx->diff_handlers(lab->ctx, exclusive);
|
||||
if (n)
|
||||
|
@ -3741,9 +3755,9 @@ sp_proc_stmt_iterate:
|
|||
LEX *lex= Lex;
|
||||
sp_head *sp= lex->sphead;
|
||||
sp_pcontext *ctx= lex->spcont;
|
||||
sp_label_t *lab= ctx->find_label($2.str);
|
||||
sp_label *lab= ctx->find_label($2);
|
||||
|
||||
if (! lab || lab->type != SP_LAB_ITER)
|
||||
if (! lab || lab->type != sp_label::ITERATION)
|
||||
{
|
||||
my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str);
|
||||
MYSQL_YYABORT;
|
||||
|
@ -3786,7 +3800,7 @@ sp_proc_stmt_open:
|
|||
uint offset;
|
||||
sp_instr_copen *i;
|
||||
|
||||
if (! lex->spcont->find_cursor(&$2, &offset))
|
||||
if (! lex->spcont->find_cursor($2, &offset, false))
|
||||
{
|
||||
my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str);
|
||||
MYSQL_YYABORT;
|
||||
|
@ -3806,7 +3820,7 @@ sp_proc_stmt_fetch:
|
|||
uint offset;
|
||||
sp_instr_cfetch *i;
|
||||
|
||||
if (! lex->spcont->find_cursor(&$3, &offset))
|
||||
if (! lex->spcont->find_cursor($3, &offset, false))
|
||||
{
|
||||
my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $3.str);
|
||||
MYSQL_YYABORT;
|
||||
|
@ -3828,7 +3842,7 @@ sp_proc_stmt_close:
|
|||
uint offset;
|
||||
sp_instr_cclose *i;
|
||||
|
||||
if (! lex->spcont->find_cursor(&$2, &offset))
|
||||
if (! lex->spcont->find_cursor($2, &offset, false))
|
||||
{
|
||||
my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str);
|
||||
MYSQL_YYABORT;
|
||||
|
@ -3852,9 +3866,9 @@ sp_fetch_list:
|
|||
LEX *lex= Lex;
|
||||
sp_head *sp= lex->sphead;
|
||||
sp_pcontext *spc= lex->spcont;
|
||||
sp_variable_t *spv;
|
||||
sp_variable *spv;
|
||||
|
||||
if (!spc || !(spv = spc->find_variable(&$1)))
|
||||
if (!spc || !(spv = spc->find_variable($1, false)))
|
||||
{
|
||||
my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str);
|
||||
MYSQL_YYABORT;
|
||||
|
@ -3872,9 +3886,9 @@ sp_fetch_list:
|
|||
LEX *lex= Lex;
|
||||
sp_head *sp= lex->sphead;
|
||||
sp_pcontext *spc= lex->spcont;
|
||||
sp_variable_t *spv;
|
||||
sp_variable *spv;
|
||||
|
||||
if (!spc || !(spv = spc->find_variable(&$3)))
|
||||
if (!spc || !(spv = spc->find_variable($3, false)))
|
||||
{
|
||||
my_error(ER_SP_UNDECLARED_VAR, MYF(0), $3.str);
|
||||
MYSQL_YYABORT;
|
||||
|
@ -3900,7 +3914,7 @@ sp_if:
|
|||
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx,
|
||||
$2, lex);
|
||||
if (i == NULL ||
|
||||
sp->push_backpatch(i, ctx->push_label((char *)"", 0)) ||
|
||||
sp->push_backpatch(i, ctx->push_label(YYTHD, EMPTY_STR, 0)) ||
|
||||
sp->add_cont_backpatch(i) ||
|
||||
sp->add_instr(i))
|
||||
MYSQL_YYABORT;
|
||||
|
@ -3917,7 +3931,7 @@ sp_if:
|
|||
sp->add_instr(i))
|
||||
MYSQL_YYABORT;
|
||||
sp->backpatch(ctx->pop_label());
|
||||
sp->push_backpatch(i, ctx->push_label((char *)"", 0));
|
||||
sp->push_backpatch(i, ctx->push_label(YYTHD, EMPTY_STR, 0));
|
||||
}
|
||||
sp_elseifs
|
||||
{
|
||||
|
@ -4059,7 +4073,7 @@ sp_labeled_control:
|
|||
{
|
||||
LEX *lex= Lex;
|
||||
sp_pcontext *ctx= lex->spcont;
|
||||
sp_label_t *lab= ctx->find_label($1.str);
|
||||
sp_label *lab= ctx->find_label($1);
|
||||
|
||||
if (lab)
|
||||
{
|
||||
|
@ -4068,19 +4082,18 @@ sp_labeled_control:
|
|||
}
|
||||
else
|
||||
{
|
||||
lab= lex->spcont->push_label($1.str,
|
||||
lex->sphead->instructions());
|
||||
lab->type= SP_LAB_ITER;
|
||||
lab= lex->spcont->push_label(YYTHD, $1, lex->sphead->instructions());
|
||||
lab->type= sp_label::ITERATION;
|
||||
}
|
||||
}
|
||||
sp_unlabeled_control sp_opt_label
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_label_t *lab= lex->spcont->pop_label();
|
||||
sp_label *lab= lex->spcont->pop_label();
|
||||
|
||||
if ($5.str)
|
||||
{
|
||||
if (my_strcasecmp(system_charset_info, $5.str, lab->name) != 0)
|
||||
if (my_strcasecmp(system_charset_info, $5.str, lab->name.str) != 0)
|
||||
{
|
||||
my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str);
|
||||
MYSQL_YYABORT;
|
||||
|
@ -4100,7 +4113,7 @@ sp_labeled_block:
|
|||
{
|
||||
LEX *lex= Lex;
|
||||
sp_pcontext *ctx= lex->spcont;
|
||||
sp_label_t *lab= ctx->find_label($1.str);
|
||||
sp_label *lab= ctx->find_label($1);
|
||||
|
||||
if (lab)
|
||||
{
|
||||
|
@ -4108,18 +4121,17 @@ sp_labeled_block:
|
|||
MYSQL_YYABORT;
|
||||
}
|
||||
|
||||
lab= lex->spcont->push_label($1.str,
|
||||
lex->sphead->instructions());
|
||||
lab->type= SP_LAB_BEGIN;
|
||||
lab= lex->spcont->push_label(YYTHD, $1, lex->sphead->instructions());
|
||||
lab->type= sp_label::BEGIN;
|
||||
}
|
||||
sp_block_content sp_opt_label
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_label_t *lab= lex->spcont->pop_label();
|
||||
sp_label *lab= lex->spcont->pop_label();
|
||||
|
||||
if ($5.str)
|
||||
{
|
||||
if (my_strcasecmp(system_charset_info, $5.str, lab->name) != 0)
|
||||
if (my_strcasecmp(system_charset_info, $5.str, lab->name.str) != 0)
|
||||
{
|
||||
my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str);
|
||||
MYSQL_YYABORT;
|
||||
|
@ -4132,8 +4144,8 @@ sp_unlabeled_block:
|
|||
{ /* Unlabeled blocks get a secret label. */
|
||||
LEX *lex= Lex;
|
||||
uint ip= lex->sphead->instructions();
|
||||
sp_label_t *lab= lex->spcont->push_label((char *)"", ip);
|
||||
lab->type= SP_LAB_BEGIN;
|
||||
sp_label *lab= lex->spcont->push_label(YYTHD, EMPTY_STR, ip);
|
||||
lab->type= sp_label::BEGIN;
|
||||
}
|
||||
sp_block_content
|
||||
{
|
||||
|
@ -4148,7 +4160,8 @@ sp_block_content:
|
|||
together. No [[NOT] ATOMIC] yet, and we need to figure out how
|
||||
make it coexist with the existing BEGIN COMMIT/ROLLBACK. */
|
||||
LEX *lex= Lex;
|
||||
lex->spcont= lex->spcont->push_context(LABEL_DEFAULT_SCOPE);
|
||||
lex->spcont= lex->spcont->push_context(YYTHD,
|
||||
sp_pcontext::REGULAR_SCOPE);
|
||||
}
|
||||
sp_decls
|
||||
sp_proc_stmts
|
||||
|
@ -4184,7 +4197,7 @@ sp_unlabeled_control:
|
|||
{
|
||||
LEX *lex= Lex;
|
||||
uint ip= lex->sphead->instructions();
|
||||
sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
|
||||
sp_label *lab= lex->spcont->last_label(); /* Jumping back */
|
||||
sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip);
|
||||
if (i == NULL ||
|
||||
lex->sphead->add_instr(i))
|
||||
|
@ -4212,7 +4225,7 @@ sp_unlabeled_control:
|
|||
{
|
||||
LEX *lex= Lex;
|
||||
uint ip= lex->sphead->instructions();
|
||||
sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
|
||||
sp_label *lab= lex->spcont->last_label(); /* Jumping back */
|
||||
sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip);
|
||||
if (i == NULL ||
|
||||
lex->sphead->add_instr(i))
|
||||
|
@ -4225,7 +4238,7 @@ sp_unlabeled_control:
|
|||
{
|
||||
LEX *lex= Lex;
|
||||
uint ip= lex->sphead->instructions();
|
||||
sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
|
||||
sp_label *lab= lex->spcont->last_label(); /* Jumping back */
|
||||
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont,
|
||||
$5, lab->ip,
|
||||
lex);
|
||||
|
@ -11107,9 +11120,9 @@ limit_option:
|
|||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= & thd->m_parser_state->m_lip;
|
||||
sp_variable_t *spv;
|
||||
sp_variable *spv;
|
||||
sp_pcontext *spc = lex->spcont;
|
||||
if (spc && (spv = spc->find_variable(&$1)))
|
||||
if (spc && (spv = spc->find_variable($1, false)))
|
||||
{
|
||||
splocal= new (thd->mem_root)
|
||||
Item_splocal($1, spv->offset, spv->type,
|
||||
|
@ -11329,9 +11342,9 @@ select_var_ident:
|
|||
| ident_or_text
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
sp_variable_t *t;
|
||||
sp_variable *t;
|
||||
|
||||
if (!lex->spcont || !(t=lex->spcont->find_variable(&$1)))
|
||||
if (!lex->spcont || !(t=lex->spcont->find_variable($1, false)))
|
||||
{
|
||||
my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str);
|
||||
MYSQL_YYABORT;
|
||||
|
@ -13255,9 +13268,9 @@ simple_ident:
|
|||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= YYLIP;
|
||||
sp_variable_t *spv;
|
||||
sp_variable *spv;
|
||||
sp_pcontext *spc = lex->spcont;
|
||||
if (spc && (spv = spc->find_variable(&$1)))
|
||||
if (spc && (spv = spc->find_variable($1, false)))
|
||||
{
|
||||
/* We're compiling a stored procedure and found a variable */
|
||||
if (! lex->parsing_options.allows_variable)
|
||||
|
@ -14224,12 +14237,11 @@ option_value_no_option_type:
|
|||
{
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= Lex;
|
||||
LEX_STRING *name= &$1.base_name;
|
||||
|
||||
if ($1.var == trg_new_row_fake_var)
|
||||
{
|
||||
/* We are in trigger and assigning value to field of new row */
|
||||
if (set_trigger_new_row(YYTHD, name, $3))
|
||||
if (set_trigger_new_row(YYTHD, &$1.base_name, $3))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
else if ($1.var)
|
||||
|
@ -14241,7 +14253,7 @@ option_value_no_option_type:
|
|||
else
|
||||
{
|
||||
sp_pcontext *spc= lex->spcont;
|
||||
sp_variable *spv= spc->find_variable(name, false);
|
||||
sp_variable *spv= spc->find_variable($1.base_name, false);
|
||||
|
||||
/* It is a local variable. */
|
||||
if (set_local_variable(thd, spv, $3))
|
||||
|
@ -14294,7 +14306,7 @@ option_value_no_option_type:
|
|||
|
||||
names.str= (char *)"names";
|
||||
names.length= 5;
|
||||
if (spc && spc->find_variable(&names, false))
|
||||
if (spc && spc->find_variable(names, false))
|
||||
my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str);
|
||||
else
|
||||
my_parse_error(ER(ER_SYNTAX_ERROR));
|
||||
|
@ -14330,7 +14342,7 @@ option_value_no_option_type:
|
|||
|
||||
pw.str= (char *)"password";
|
||||
pw.length= 8;
|
||||
if (spc && spc->find_variable(&pw, false))
|
||||
if (spc && spc->find_variable(pw, false))
|
||||
{
|
||||
my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str);
|
||||
MYSQL_YYABORT;
|
||||
|
@ -14365,10 +14377,10 @@ internal_variable_name:
|
|||
{
|
||||
THD *thd= YYTHD;
|
||||
sp_pcontext *spc= thd->lex->spcont;
|
||||
sp_variable_t *spv;
|
||||
sp_variable *spv;
|
||||
|
||||
/* Best effort lookup for system variable. */
|
||||
if (!spc || !(spv = spc->find_variable(&$1)))
|
||||
if (!spc || !(spv = spc->find_variable($1, false)))
|
||||
{
|
||||
struct sys_var_with_base tmp= {NULL, $1};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue