Finished merging wl5986 started by Igor.

This commit is contained in:
unknown 2013-06-19 14:32:14 +03:00
parent 2534521f9a
commit dfcc502ab5
21 changed files with 1814 additions and 1849 deletions

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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()

View file

@ -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
}

View file

@ -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

View file

@ -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, &current_arena);
holder= Item_cache::get_cache(item);
thd->restore_active_arena(thd->spcont->callers_arena, &current_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, &current_arena);
holder= Item_cache::get_cache(item);
thd->restore_active_arena(thd->spcont->callers_arena, &current_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;
}

View file

@ -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

View file

@ -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 */

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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:

View file

@ -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 */

View file

@ -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)
{}

View file

@ -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

View file

@ -1829,7 +1829,7 @@ multi_update::~multi_update()
}
int multi_update::send_data(List<Item> &not_used_values)
bool multi_update::send_data(List<Item> &not_used_values)
{
TABLE_LIST *cur_table;
DBUG_ENTER("multi_update::send_data");

View file

@ -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};