diff options
-rw-r--r-- | sql/item_xmlfunc.cc | 4 | ||||
-rw-r--r-- | sql/opt_subselect.cc | 4 | ||||
-rw-r--r-- | sql/sp_head.cc | 264 | ||||
-rw-r--r-- | sql/sp_head.h | 55 | ||||
-rw-r--r-- | sql/sp_pcontext.cc | 563 | ||||
-rw-r--r-- | sql/sp_pcontext.h | 813 | ||||
-rw-r--r-- | sql/sp_rcontext.cc | 720 | ||||
-rw-r--r-- | sql/sp_rcontext.h | 589 | ||||
-rw-r--r-- | sql/sql_array.h | 120 | ||||
-rw-r--r-- | sql/sql_class.cc | 18 | ||||
-rw-r--r-- | sql/sql_class.h | 31 | ||||
-rw-r--r-- | sql/sql_delete.cc | 2 | ||||
-rw-r--r-- | sql/sql_error.cc | 29 | ||||
-rw-r--r-- | sql/sql_insert.cc | 2 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 4 | ||||
-rw-r--r-- | sql/sql_show.cc | 10 | ||||
-rw-r--r-- | sql/sql_signal.cc | 35 | ||||
-rw-r--r-- | sql/sql_signal.h | 8 | ||||
-rw-r--r-- | sql/sql_union.cc | 2 | ||||
-rw-r--r-- | sql/sql_update.cc | 2 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 234 |
21 files changed, 1737 insertions, 1772 deletions
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index b05d1da82a5..c1b09d16430 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -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 diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 4d3d60fc2f5..bda9532e874 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -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); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 59363a7a04a..03631e107db 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -772,7 +772,7 @@ sp_head::~sp_head() for (uint ip = 0 ; (i = get_instr(ip)) ; ip++) delete i; delete_dynamic(&m_instr); - m_pcont->destroy(); + delete m_pcont; free_items(); /* @@ -1078,104 +1078,6 @@ void sp_head::recursion_level_error(THD *thd) } -/** - Find an SQL handler for any condition (warning or error) after execution - of a stored routine instruction. Basically, this function looks for an - appropriate SQL handler in RT-contexts. If an SQL handler is found, it is - remembered in the RT-context for future activation (the context can be - inactive at the moment). - - If there is no pending condition, the function just returns. - - If there was an error during the execution, an SQL handler for it will be - searched within the current and outer scopes. - - There might be several errors in the Warning Info (that's possible by using - SIGNAL/RESIGNAL in nested scopes) -- the function is looking for an SQL - handler for the latest (current) error only. - - If there was a warning during the execution, an SQL handler for it will be - searched within the current scope only. - - If several warnings were thrown during the execution and there are different - SQL handlers for them, it is not determined which SQL handler will be chosen. - Only one SQL handler will be executed. - - If warnings and errors were thrown during the execution, the error takes - precedence. I.e. error handler will be executed. If there is no handler - for that error, condition will remain unhandled. - - Once a warning or an error has been handled it is not removed from - Warning Info. - - According to The Standard (quoting PeterG): - - An SQL procedure statement works like this ... - SQL/Foundation 13.5 <SQL procedure statement> - (General Rules) (greatly summarized) says: - (1) Empty diagnostics area, thus clearing the condition. - (2) Execute statement. - During execution, if Exception Condition occurs, - set Condition Area = Exception Condition and stop - statement. - During execution, if No Data occurs, - set Condition Area = No Data Condition and continue - statement. - During execution, if Warning occurs, - and Condition Area is not already full due to - an earlier No Data condition, set Condition Area - = Warning and continue statement. - (3) Finish statement. - At end of execution, if Condition Area is not - already full due to an earlier No Data or Warning, - set Condition Area = Successful Completion. - In effect, this system means there is a precedence: - Exception trumps No Data, No Data trumps Warning, - Warning trumps Successful Completion. - - NB: "Procedure statements" include any DDL or DML or - control statements. So CREATE and DELETE and WHILE - and CALL and RETURN are procedure statements. But - DECLARE and END are not procedure statements. - - @param thd thread handle - @param ctx runtime context of the stored routine -*/ - -static void -find_handler_after_execution(THD *thd, sp_rcontext *ctx) -{ - if (thd->is_error()) - { - ctx->find_handler(thd, - thd->get_stmt_da()->sql_errno(), - thd->get_stmt_da()->get_sqlstate(), - Sql_condition::WARN_LEVEL_ERROR, - thd->get_stmt_da()->message()); - } - else if (thd->get_stmt_da()->statement_warn_count()) - { - Diagnostics_area::Sql_condition_iterator it= - thd->get_stmt_da()->sql_conditions(); - const Sql_condition *err; - while ((err= it++)) - { - if (err->get_level() != Sql_condition::WARN_LEVEL_WARN && - err->get_level() != Sql_condition::WARN_LEVEL_NOTE) - continue; - - if (ctx->find_handler(thd, - err->get_sql_errno(), - err->get_sqlstate(), - err->get_level(), - err->get_message_text())) - { - break; - } - } - } -} - /** Execute the routine. The main instruction jump loop is there. @@ -1445,19 +1347,10 @@ sp_head::execute(THD *thd, bool merge_da_on_success) errors are not catchable by SQL handlers) or the connection has been killed during execution. */ - if (!thd->is_fatal_error && !thd->killed_errno()) + if (!thd->is_fatal_error && !thd->killed_errno() && + ctx->handle_sql_condition(thd, &ip, i)) { - /* - Find SQL handler in the appropriate RT-contexts: - - warnings can be handled by SQL handlers within - the current scope only; - - errors can be handled by any SQL handler from outer scope. - */ - find_handler_after_execution(thd, ctx); - - /* If found, activate handler for the current scope. */ - if (ctx->activate_handler(thd, &ip, i, &execute_arena, &backup_arena)) - err_status= FALSE; + err_status= FALSE; } /* Reset sp_rcontext::end_partial_result_set flag. */ @@ -1743,8 +1636,7 @@ sp_head::execute_trigger(THD *thd, init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0)); thd->set_n_backup_active_arena(&call_arena, &backup_arena); - if (!(nctx= new sp_rcontext(m_pcont, 0, octx)) || - nctx->init(thd)) + if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL))) { err_status= TRUE; goto err_with_cleanup; @@ -1860,8 +1752,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0)); thd->set_n_backup_active_arena(&call_arena, &backup_arena); - if (!(nctx= new sp_rcontext(m_pcont, return_value_fld, octx)) || - nctx->init(thd)) + if (!(nctx= sp_rcontext::create(thd, m_pcont, return_value_fld))) { thd->restore_active_arena(&call_arena, &backup_arena); err_status= TRUE; @@ -2078,7 +1969,6 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) if (! octx) { /* Create a temporary old context. */ - if (!(octx= new sp_rcontext(m_pcont, NULL, octx)) || octx->init(thd)) { delete octx; /* Delete octx if it was init() that failed. */ DBUG_RETURN(TRUE); @@ -2093,8 +1983,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) thd->spcont->callers_arena= thd; } - if (!(nctx= new sp_rcontext(m_pcont, NULL, octx)) || - nctx->init(thd)) + if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL))) { delete nctx; /* Delete nctx if it was init() that failed. */ thd->spcont= save_spcont; @@ -2117,12 +2006,12 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) if (!arg_item) break; - sp_variable_t *spvar= m_pcont->find_variable(i); + sp_variable *spvar= m_pcont->find_variable(i); if (!spvar) continue; - if (spvar->mode != sp_param_in) + if (spvar->mode != sp_variable::MODE_IN) { Settable_routine_parameter *srp= arg_item->get_settable_routine_parameter(); @@ -2134,10 +2023,10 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) break; } - srp->set_required_privilege(spvar->mode == sp_param_inout); + srp->set_required_privilege(spvar->mode == sp_variable::MODE_INOUT); } - if (spvar->mode == sp_param_out) + if (spvar->mode == sp_variable::MODE_OUT) { Item_null *null_item= new Item_null(); Item *tmp_item= null_item; @@ -2237,9 +2126,9 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) if (!arg_item) break; - sp_variable_t *spvar= m_pcont->find_variable(i); + sp_variable *spvar= m_pcont->find_variable(i); - if (spvar->mode == sp_param_in) + if (spvar->mode == sp_variable::MODE_IN) continue; Settable_routine_parameter *srp= @@ -2399,7 +2288,7 @@ sp_head::restore_lex(THD *thd) Put the instruction on the backpatch list, associated with the label. */ int -sp_head::push_backpatch(sp_instr *i, sp_label_t *lab) +sp_head::push_backpatch(sp_instr *i, sp_label *lab) { bp_t *bp= (bp_t *)sql_alloc(sizeof(bp_t)); @@ -2415,7 +2304,7 @@ sp_head::push_backpatch(sp_instr *i, sp_label_t *lab) the current position. */ void -sp_head::backpatch(sp_label_t *lab) +sp_head::backpatch(sp_label *lab) { bp_t *bp; uint dest= instructions(); @@ -2427,7 +2316,7 @@ sp_head::backpatch(sp_label_t *lab) if (bp->lab == lab) { DBUG_PRINT("info", ("backpatch: (m_ip %d, label 0x%lx <%s>) to dest %d", - bp->instr->m_ip, (ulong) lab, lab->name, dest)); + bp->instr->m_ip, (ulong) lab, lab->name.str, dest)); bp->instr->backpatch(dest, lab->ctx); } } @@ -3107,7 +2996,7 @@ int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables) return result; } -uint sp_instr::get_cont_dest() +uint sp_instr::get_cont_dest() const { return (m_ip+1); } @@ -3261,7 +3150,7 @@ sp_instr_set::print(String *str) { /* set name@offset ... */ int rsrv = SP_INSTR_UINT_MAXLEN+6; - sp_variable_t *var = m_ctx->find_variable(m_offset); + sp_variable *var = m_ctx->find_variable(m_offset); /* 'var' should always be non-null, but just in case... */ if (var) @@ -3314,7 +3203,7 @@ sp_instr_set_trigger_field::print(String *str) sp_instr_opt_meta */ -uint sp_instr_opt_meta::get_cont_dest() +uint sp_instr_opt_meta::get_cont_dest() const { return m_cont_dest; } @@ -3532,14 +3421,12 @@ int sp_instr_hpush_jump::execute(THD *thd, uint *nextp) { DBUG_ENTER("sp_instr_hpush_jump::execute"); - List_iterator_fast<sp_cond_type_t> li(m_cond); - sp_cond_type_t *p; - while ((p= li++)) - thd->spcont->push_handler(p, m_ip+1, m_type); + int ret= thd->spcont->push_handler(m_handler, m_ip + 1); *nextp= m_dest; - DBUG_RETURN(0); + + DBUG_RETURN(ret); } @@ -3549,27 +3436,22 @@ sp_instr_hpush_jump::print(String *str) /* hpush_jump dest fsize type */ if (str->reserve(SP_INSTR_UINT_MAXLEN*2 + 21)) return; + str->qs_append(STRING_WITH_LEN("hpush_jump ")); str->qs_append(m_dest); str->qs_append(' '); str->qs_append(m_frame); - switch (m_type) { - case SP_HANDLER_NONE: - str->qs_append(STRING_WITH_LEN(" NONE")); // This would be a bug - break; - case SP_HANDLER_EXIT: + + switch (m_handler->type) { + case sp_handler::EXIT: str->qs_append(STRING_WITH_LEN(" EXIT")); break; - case SP_HANDLER_CONTINUE: + case sp_handler::CONTINUE: str->qs_append(STRING_WITH_LEN(" CONTINUE")); break; - case SP_HANDLER_UNDO: - str->qs_append(STRING_WITH_LEN(" UNDO")); - break; default: - // This would be a bug as well - str->qs_append(STRING_WITH_LEN(" UNKNOWN:")); - str->qs_append(m_type); + // The handler type must be either CONTINUE or EXIT. + DBUG_ASSERT(0); } } @@ -3597,7 +3479,7 @@ sp_instr_hpush_jump::opt_mark(sp_head *sp, List<sp_instr> *leads) above, so we start on m_dest+1 here. m_opt_hpop is the hpop marking the end of the handler scope. */ - if (m_type == SP_HANDLER_CONTINUE) + if (m_handler->type == sp_handler::CONTINUE) { for (uint scope_ip= m_dest+1; scope_ip <= m_opt_hpop; scope_ip++) sp->add_mark_lead(scope_ip, leads); @@ -3639,13 +3521,11 @@ int sp_instr_hreturn::execute(THD *thd, uint *nextp) { DBUG_ENTER("sp_instr_hreturn::execute"); - if (m_dest) - *nextp= m_dest; - else - { - *nextp= thd->spcont->pop_hstack(); - } - thd->spcont->exit_handler(); + + uint continue_ip= thd->spcont->exit_handler(thd->get_stmt_da()); + + *nextp= m_dest ? m_dest : continue_ip; + DBUG_RETURN(0); } @@ -3657,12 +3537,17 @@ sp_instr_hreturn::print(String *str) if (str->reserve(SP_INSTR_UINT_MAXLEN*2 + 9)) return; str->qs_append(STRING_WITH_LEN("hreturn ")); - str->qs_append(m_frame); if (m_dest) { - str->qs_append(' '); + // NOTE: this is legacy: hreturn instruction for EXIT handler + // should print out 0 as frame index. + str->qs_append(STRING_WITH_LEN("0 ")); str->qs_append(m_dest); } + else + { + str->qs_append(m_frame); + } } @@ -3694,41 +3579,32 @@ sp_instr_hreturn::opt_mark(sp_head *sp, List<sp_instr> *leads) int sp_instr_cpush::execute(THD *thd, uint *nextp) { - Query_arena backup_arena; DBUG_ENTER("sp_instr_cpush::execute"); - /* - We should create cursors in the callers arena, as - it could be (and usually is) used in several instructions. - */ - thd->set_n_backup_active_arena(thd->spcont->callers_arena, &backup_arena); - - thd->spcont->push_cursor(&m_lex_keeper, this); - - thd->restore_active_arena(thd->spcont->callers_arena, &backup_arena); + int ret= thd->spcont->push_cursor(&m_lex_keeper, this); *nextp= m_ip+1; - DBUG_RETURN(0); + DBUG_RETURN(ret); } void sp_instr_cpush::print(String *str) { - LEX_STRING n; - my_bool found= m_ctx->find_cursor(m_cursor, &n); + const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor); + /* cpush name@offset */ uint rsrv= SP_INSTR_UINT_MAXLEN+7; - if (found) - rsrv+= n.length; + if (cursor_name) + rsrv+= cursor_name->length; if (str->reserve(rsrv)) return; str->qs_append(STRING_WITH_LEN("cpush ")); - if (found) + if (cursor_name) { - str->qs_append(n.str, n.length); + str->qs_append(cursor_name->str, cursor_name->length); str->qs_append('@'); } str->qs_append(m_cursor); @@ -3816,19 +3692,19 @@ sp_instr_copen::exec_core(THD *thd, uint *nextp) void sp_instr_copen::print(String *str) { - LEX_STRING n; - my_bool found= m_ctx->find_cursor(m_cursor, &n); + const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor); + /* copen name@offset */ uint rsrv= SP_INSTR_UINT_MAXLEN+7; - if (found) - rsrv+= n.length; + if (cursor_name) + rsrv+= cursor_name->length; if (str->reserve(rsrv)) return; str->qs_append(STRING_WITH_LEN("copen ")); - if (found) + if (cursor_name) { - str->qs_append(n.str, n.length); + str->qs_append(cursor_name->str, cursor_name->length); str->qs_append('@'); } str->qs_append(m_cursor); @@ -3858,19 +3734,19 @@ sp_instr_cclose::execute(THD *thd, uint *nextp) void sp_instr_cclose::print(String *str) { - LEX_STRING n; - my_bool found= m_ctx->find_cursor(m_cursor, &n); + const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor); + /* cclose name@offset */ uint rsrv= SP_INSTR_UINT_MAXLEN+8; - if (found) - rsrv+= n.length; + if (cursor_name) + rsrv+= cursor_name->length; if (str->reserve(rsrv)) return; str->qs_append(STRING_WITH_LEN("cclose ")); - if (found) + if (cursor_name) { - str->qs_append(n.str, n.length); + str->qs_append(cursor_name->str, cursor_name->length); str->qs_append('@'); } str->qs_append(m_cursor); @@ -3899,21 +3775,21 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp) void sp_instr_cfetch::print(String *str) { - List_iterator_fast<struct sp_variable> li(m_varlist); - sp_variable_t *pv; - LEX_STRING n; - my_bool found= m_ctx->find_cursor(m_cursor, &n); + List_iterator_fast<sp_variable> li(m_varlist); + sp_variable *pv; + const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor); + /* cfetch name@offset vars... */ uint rsrv= SP_INSTR_UINT_MAXLEN+8; - if (found) - rsrv+= n.length; + if (cursor_name) + rsrv+= cursor_name->length; if (str->reserve(rsrv)) return; str->qs_append(STRING_WITH_LEN("cfetch ")); - if (found) + if (cursor_name) { - str->qs_append(n.str, n.length); + str->qs_append(cursor_name->str, cursor_name->length); str->qs_append('@'); } str->qs_append(m_cursor); diff --git a/sql/sp_head.h b/sql/sp_head.h index f5254c2ead2..f91e30ab8a4 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -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: private: + /// Handler. + sp_handler *m_handler; + + /// hpop marking end of handler scope. + uint m_opt_hpop; - int m_type; ///< Handler type + // 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() diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index f11daeecb7b..fc2f3e8bb2f 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -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); + + case sp_condition_value::SQLSTATE: + return (strcmp(sql_state, cv->sql_state) == 0); - if ((c < '0' || '9' < c) && - (c < 'A' || 'Z' < c)) - return FALSE; + 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) + +sp_variable *sp_pcontext::find_variable(uint offset) const { - if (m_var_offset <= offset && offset < m_var_offset + m_vars.elements) - { // This frame - sp_variable_t *p; + if (m_var_offset <= offset && offset < m_var_offset + m_vars.elements()) + return m_vars.at(offset - m_var_offset); // This frame - 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 + 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 NULL; - return p; + return m_vars.append(p) ? NULL : p; } -sp_label_t * -sp_pcontext::push_label(char *name, uint ip) +sp_label *sp_pcontext::push_label(THD *thd, LEX_STRING name, uint ip) { - sp_label_t *lab = (sp_label_t *)sql_alloc(sizeof(sp_label_t)); + sp_label *label= + new (thd->mem_root) sp_label(name, ip, sp_label::IMPLICIT, this); - if (lab) - { - lab->name= name; - lab->ip= ip; - lab->type= SP_LAB_IMPL; - lab->ctx= this; - m_label.push_front(lab); - } - return lab; + if (!label) + return NULL; + + m_labels.push_front(label); + + return label; } -sp_label_t * -sp_pcontext::find_label(char *name) + +sp_label *sp_pcontext::find_label(LEX_STRING 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); + + List_iterator_fast<sp_condition_value> li(h->condition_values); + sp_condition_value *cv; - get_dynamic(&m_handlers, (uchar*)&p, i); - if (cond->type == p->type) + 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) + +bool sp_pcontext::add_cursor(LEX_STRING name) { - LEX_STRING n; + if (m_cursors.elements() == (int) m_max_cursor_index) + ++m_max_cursor_index; - 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); + 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 } diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index f1d0d250c47..fbf32244665 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -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 -{ - sp_param_in, - sp_param_out, - sp_param_inout -} sp_param_mode_t; +/// This class represents a stored program variable or a parameter +/// (also referenced as 'SP-variable'). -typedef struct sp_variable +class sp_variable : public Sql_alloc { - LEX_STRING name; - 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. - */ - uint offset; - - Item *dflt; - Create_field field_def; -} sp_variable_t; +public: + enum enum_mode + { + MODE_IN, + MODE_OUT, + MODE_INOUT + }; + /// Name of the SP-variable. + LEX_STRING name; -#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 + /// Field-type of the SP-variable. + enum enum_field_types type; -/* - 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. -*/ + /// Mode of the SP-variable. + enum_mode mode; -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; + /// 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; -typedef struct sp_cond_type -{ - enum { number, state, warning, notfound, exception } type; - char sqlstate[SQLSTATE_LENGTH+1]; - uint mysqlerr; -} sp_cond_type_t; + /// Default value of the SP-variable (if any). + Item *default_value; -/* - 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); + /// Full type information (field meta-data) of the SP-variable. + Create_field field_def; -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. -*/ +/////////////////////////////////////////////////////////////////////////// -class sp_pcontext : public Sql_alloc +/// 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: - - /** - Constructor. - Builds a parsing context root node. - */ - 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); - - /** - Pop a node from the parsing context tree. - @return the parent node - */ - sp_pcontext * - pop_context(); - - sp_pcontext * - parent_context() + enum enum_type { - return m_parent; - } + /// Implicit label generated by parser. + IMPLICIT, - /* - 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); - - - // - // Parameters and variables - // - - /* - 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; - } + /// Label at BEGIN. + BEGIN, - /* - 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; - } + /// Label at iteration control + ITERATION + }; - /* The number of variables in this context alone */ - inline uint - context_var_count() - { - return m_vars.elements; - } + /// Name of the label. + LEX_STRING name; - /* Map index in this pcontext to runtime offset */ - inline uint - var_context2runtime(uint i) - { - return m_var_offset + i; - } + /// Instruction pointer of the label. + uint ip; - /* 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); + /// Type of the label. + enum_type type; - if (p) - p->type= type; - } + /// Scope of the label. + class sp_pcontext *ctx; - /* 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); +public: + sp_label(LEX_STRING _name, uint _ip, enum_type _type, sp_pcontext *_ctx) + :Sql_alloc(), + name(_name), + ip(_ip), + type(_type), + ctx(_ctx) + { } +}; - if (p) - p->dflt= it; - } +/////////////////////////////////////////////////////////////////////////// + +/// 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. - sp_variable_t * - push_variable(LEX_STRING *name, enum enum_field_types type, - sp_param_mode_t mode); - - /* - Retrieve definitions of fields from the current context and its - children. - */ - void - retrieve_field_definitions(List<Create_field> *field_def_lst); - - // 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) +class sp_condition_value : public Sql_alloc +{ +public: + enum enum_type { - m_pboundary= n; - } + ERROR_CODE, + SQLSTATE, + WARNING, + NOT_FOUND, + EXCEPTION + }; - /* - CASE expressions support. - */ + /// Type of the condition value. + enum_type type; - inline int - register_case_expr() - { - return m_num_case_exprs++; - } + /// SQLSTATE of the condition value. + char sql_state[SQLSTATE_LENGTH+1]; - inline int - get_num_case_exprs() const - { - return m_num_case_exprs; - } + /// MySQL error code of the condition value. + uint mysqlerr; - inline bool - push_case_expr_id(int case_expr_id) +public: + sp_condition_value(uint _mysqlerr) + :Sql_alloc(), + type(ERROR_CODE), + mysqlerr(_mysqlerr) + { } + + sp_condition_value(const char *_sql_state) + :Sql_alloc(), + type(SQLSTATE) { - return insert_dynamic(&m_case_expr_id_lst, (uchar*) &case_expr_id); + memcpy(sql_state, _sql_state, SQLSTATE_LENGTH); + sql_state[SQLSTATE_LENGTH]= 0; } - inline void - pop_case_expr_id() + sp_condition_value(enum_type _type) + :Sql_alloc(), + type(_type) { - pop_dynamic(&m_case_expr_id_lst); + DBUG_ASSERT(type != ERROR_CODE && type != SQLSTATE); } - inline int - get_current_case_expr_id() const - { - int case_expr_id; + /// 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; +}; - get_dynamic((DYNAMIC_ARRAY*)&m_case_expr_id_lst, (uchar*) &case_expr_id, - m_case_expr_id_lst.elements - 1); +/////////////////////////////////////////////////////////////////////////// - return case_expr_id; - } +/// This class represents 'DECLARE CONDITION' statement. +/// sp_condition has little to do with SQL-conditions. - // - // Labels - // +class sp_condition : public Sql_alloc +{ +public: + /// Name of the condition. + LEX_STRING name; - sp_label_t * - push_label(char *name, uint ip); + /// Value of the condition. + sp_condition_value *value; - sp_label_t * - find_label(char *name); +public: + sp_condition(LEX_STRING _name, sp_condition_value *_value) + :Sql_alloc(), + name(_name), + value(_value) + { } +}; - inline sp_label_t * - last_label() - { - sp_label_t *lab= m_label.head(); +/////////////////////////////////////////////////////////////////////////// - if (!lab && m_parent) - lab= m_parent->last_label(); - return lab; - } +/// This class represents 'DECLARE HANDLER' statement. - inline sp_label_t * - pop_label() +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 { - return m_label.pop(); - } + EXIT, + CONTINUE + }; - // - // Conditions - // + /// Handler type. + enum_type type; - int - push_cond(LEX_STRING *name, sp_cond_type_t *val); + /// Conditions caught by this handler. + List<sp_condition_value> condition_values; - sp_cond_type_t * - find_cond(LEX_STRING *name, my_bool scoped=0); +public: + /// The constructor. + /// + /// @param _type SQL-handler type. + sp_handler(enum_type _type) + :Sql_alloc(), + type(_type) + { } +}; - // - // Handlers - // +/////////////////////////////////////////////////////////////////////////// + +/// 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. - inline void - push_handler(sp_cond_type_t *cond) +class sp_pcontext : public Sql_alloc +{ +public: + enum enum_scope { - insert_dynamic(&m_handlers, (uchar*)&cond); - } - - bool - find_handler(sp_cond_type *cond); + /// REGULAR_SCOPE designates regular BEGIN ... END blocks. + REGULAR_SCOPE, - inline uint - max_handler_index() - { - return m_max_handler_index + m_context_handlers; - } + /// HANDLER_SCOPE designates SQL-handler blocks. + HANDLER_SCOPE + }; - inline void - add_handlers(uint n) +public: + sp_pcontext(); + ~sp_pcontext(); + + + /// Create and push a new context in the tree. + + /// @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); + + /// 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() { - m_context_handlers+= n; - } - - // - // Cursors - // + sp_label *label= m_labels.head(); - int - push_cursor(LEX_STRING *name); + if (!label && m_parent) + label= m_parent->last_label(); - 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; + return label; } -protected: + sp_label *pop_label() + { return m_labels.pop(); } + + ///////////////////////////////////////////////////////////////////////// + // Conditions. + ///////////////////////////////////////////////////////////////////////// + + bool add_condition(THD *thd, LEX_STRING name, sp_condition_value *value); + + /// See comment for find_variable() above. + sp_condition_value *find_condition(LEX_STRING name, + bool current_scope_only) const; + + ///////////////////////////////////////////////////////////////////////// + // Handlers. + ///////////////////////////////////////////////////////////////////////// + + sp_handler *add_handler(THD* thd, sp_handler::enum_type type); + + /// 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; + + /// 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; + + ///////////////////////////////////////////////////////////////////////// + // Cursors. + ///////////////////////////////////////////////////////////////////////// + + bool add_cursor(LEX_STRING name); + + /// See comment for find_variable() above. + bool find_cursor(LEX_STRING name, uint *poff, bool current_scope_only) const; + + /// Find cursor by offset (for debugging only). + const LEX_STRING *find_cursor(uint offset) const; + + uint max_cursor_index() const + { return m_max_cursor_index + m_cursors.elements(); } + + uint current_cursor_count() const + { return m_cursor_offset + m_cursors.elements(); } - /** - 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. +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); - 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; + void init(uint var_offset, uint cursor_offset, int num_case_expressions); - // 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 + /* Prevent use of these */ + sp_pcontext(const sp_pcontext &); + void operator=(sp_pcontext &); 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; - sp_pcontext *m_parent; // Parent context - - /* - m_var_offset -- this is an index of the first variable in this - parsing context. - - m_var_offset is 0 for root context. + /// Parent context. + sp_pcontext *m_parent; - Since now each variable is stored in separate place, no reuse is done, - so m_var_offset is different for all enclosed contexts. - */ + /// 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 diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index fc6a67fb496..3e79cb3fa36 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -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,422 +49,307 @@ 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. +sp_rcontext *sp_rcontext::create(THD *thd, + const sp_pcontext *root_parsing_ctx, + Field *return_value_fld) +{ + sp_rcontext *ctx= new (thd->mem_root) sp_rcontext(root_parsing_ctx, + return_value_fld, + thd->in_sub_stmt); - SYNOPSIS - thd Thread handle - RETURN - FALSE on success - TRUE on error -*/ + if (!ctx) + return NULL; -bool sp_rcontext::init(THD *thd) -{ - uint handler_count= m_root_parsing_ctx->max_handler_index(); - - in_sub_stmt= thd->in_sub_stmt; - - if (init_var_table(thd) || init_var_items()) - return TRUE; - - 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*))); + if (ctx->alloc_arrays(thd) || + ctx->init_var_table(thd) || + ctx->init_var_items(thd)) + { + delete ctx; + return NULL; + } + + 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); + } + + { + 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); + } + + return !m_cstack.array() || !m_case_expr_holders.array(); +} - SYNOPSIS - thd Thread handler. - RETURN - FALSE on success - TRUE on error -*/ -bool -sp_rcontext::init_var_table(THD *thd) +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); + + if (!m_var_items.array()) + return true; - for (idx = 0; idx < num_vars; ++idx) + 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; - - /* Reset previously found handler. */ - m_hfound= -1; - /* - If this is a fatal sub-statement error, and this runtime - context corresponds to a sub-statement, no CONTINUE/EXIT - handlers from this context are applicable: try to locate one - in the outer scope. + We should create cursors in the callers arena, as + it could be (and usually is) used in several instructions. */ - if (thd->is_fatal_sub_stmt_error && in_sub_stmt) - i= 0; - - /* Search handlers from the latest (innermost) to the oldest (outermost) */ - while (i--) - { - sp_cond_type_t *cond= m_handler[i].cond; - int j= m_ihsp; + sp_cursor *c= new (callers_arena->mem_root) sp_cursor(lex_keeper, i); - /* 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 (c == NULL) + return true; - switch (cond->type) - { - 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; - 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. - */ - 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); - } - - return FALSE; + m_cstack[m_ccount++]= c; + return false; } -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) +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) +bool sp_rcontext::push_handler(sp_handler *handler, uint first_ip) { - DBUG_ENTER("sp_rcontext::pop_handlers"); - DBUG_ASSERT(m_hcount >= count); + /* + 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); - m_hcount-= count; + if (he == NULL) + return true; - DBUG_PRINT("info", ("m_hcount: %d", m_hcount)); - DBUG_VOID_RETURN; + return m_handlers.append(he); } -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; +void sp_rcontext::pop_handlers(int count) +{ + DBUG_ASSERT(m_handlers.elements() >= count); - DBUG_PRINT("info", ("m_hsp: %d", m_hsp)); - DBUG_VOID_RETURN; + for (int i= 0; i < count; ++i) + m_handlers.pop(); } -uint -sp_rcontext::pop_hstack() + +bool sp_rcontext::handle_sql_condition(THD *thd, + uint *ip, + const sp_instr *cur_spi) { - uint handler; - DBUG_ENTER("sp_rcontext::pop_hstack"); - DBUG_ASSERT(m_hsp); + DBUG_ENTER("sp_rcontext::handle_sql_condition"); - handler= m_hstack[--m_hsp]; + /* + If this is a fatal sub-statement error, and this runtime + context corresponds to a sub-statement, no CONTINUE/EXIT + handlers from this context are applicable: try to locate one + in the outer scope. + */ + if (thd->is_fatal_sub_stmt_error && m_in_sub_stmt) + DBUG_RETURN(false); - DBUG_PRINT("info", ("m_hsp: %d", m_hsp)); - DBUG_RETURN(handler); -} + Diagnostics_area *da= thd->get_stmt_da(); + const sp_handler *found_handler= NULL; + const Sql_condition *found_condition= NULL; -/** - Prepare found handler to be executed. + if (thd->is_error()) + { + found_handler= + cur_spi->m_ctx->find_handler(da->get_sqlstate(), + da->sql_errno(), + Sql_condition::WARN_LEVEL_ERROR); - @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 -*/ + 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; -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; + // 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) - switch (m_handler[m_hfound].type) { - case SP_HANDLER_NONE: - break; + while ((c= it++)) + { + 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; + } + } + } + } - 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()); + if (!found_handler) + DBUG_RETURN(false); - /* Fall through */ + // At this point, we know that: + // - there is a pending SQL-condition (error or warning); + // - there is an SQL-handler for it. - default: - /* End aborted result set. */ + DBUG_ASSERT(found_condition); - if (end_partial_result_set) - thd->protocol->end_partial_result_set(thd); + sp_handler_entry *handler_entry= NULL; + for (int i= 0; i < m_handlers.elements(); ++i) + { + sp_handler_entry *h= m_handlers.at(i); - /* Enter handler. */ + if (h->handler == found_handler) + { + handler_entry= h; + break; + } + } - DBUG_ASSERT(m_ihsp < m_root_parsing_ctx->max_handler_index()); - DBUG_ASSERT(m_hfound >= 0); + /* + 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 (!handler_entry) + DBUG_RETURN(false); - m_in_handler[m_ihsp].ip= m_handler[m_hfound].handler; - m_in_handler[m_ihsp].index= m_hfound; - m_ihsp++; + // Mark active conditions so that they can be deleted when the handler exits. + da->mark_sql_conditions_for_removal(); - DBUG_PRINT("info", ("Entering handler...")); - DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp)); + uint continue_ip= handler_entry->handler->type == sp_handler::CONTINUE ? + cur_spi->get_cont_dest() : 0; - /* Reset error state. */ + /* End aborted result set. */ + if (end_partial_result_set) + thd->protocol->end_partial_result_set(thd); - thd->clear_error(); - thd->reset_killed(); // Some errors set thd->killed - // (e.g. "bad data"). + /* Reset error state. */ + thd->clear_error(); + thd->killed= NOT_KILLED; // Some errors set thd->killed + // (e.g. "bad data"). - /* Return IP of the activated SQL handler. */ - *ip= m_handler[m_hfound].handler; + /* 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); - /* Reset found handler. */ - m_hfound= -1; - } + *ip= handler_entry->first_ip; - return TRUE; + DBUG_RETURN(true); } -void -sp_rcontext::exit_handler() -{ - DBUG_ENTER("sp_rcontext::exit_handler"); - DBUG_ASSERT(m_ihsp); - - uint hindex= m_in_handler[m_ihsp-1].index; - m_raised_conditions[hindex].clear(); - m_ihsp-= 1; - DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp)); - DBUG_VOID_RETURN; -} - -Sql_condition_info* sp_rcontext::raised_condition() const +uint sp_rcontext::exit_handler(Diagnostics_area *da) { - if (m_ihsp > 0) - { - uint hindex= m_in_handler[m_ihsp - 1].index; - Sql_condition_info *raised= & m_raised_conditions[hindex]; - return raised; - } + DBUG_ENTER("sp_rcontext::exit_handler"); + DBUG_ASSERT(m_handler_call_stack.elements() > 0); - if (m_prev_runtime_ctx) - return m_prev_runtime_ctx->raised_condition(); + Handler_call_frame *f= m_handler_call_stack.pop(); - return NULL; -} + /* + Remove the SQL conditions that were present in DA when the + handler was activated. + */ + da->remove_marked_sql_conditions(); + uint continue_ip= f->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); + DBUG_RETURN(continue_ip); } -int -sp_rcontext::set_variable(THD *thd, Field *field, Item **value) +int sp_rcontext::set_variable(THD *thd, Field *field, Item **value) { if (!value) { @@ -478,25 +361,47 @@ sp_rcontext::set_variable(THD *thd, Field *field, Item **value) } -Item * -sp_rcontext::get_item(uint var_idx) +Item_cache *sp_rcontext::create_case_expr_holder(THD *thd, + const Item *item) const { - return m_var_items[var_idx]; + Item_cache *holder; + Query_arena current_arena; + + thd->set_n_backup_active_arena(thd->spcont->callers_arena, ¤t_arena); + + holder= Item_cache::get_cache(item); + + thd->restore_active_arena(thd->spcont->callers_arena, ¤t_arena); + + return holder; } -Item ** -sp_rcontext::get_item_addr(uint var_idx) +bool sp_rcontext::set_case_expr(THD *thd, int case_expr_id, + Item **case_expr_item_ptr) { - return m_var_items + var_idx; + Item *case_expr_item= sp_prepare_func_item(thd, case_expr_item_ptr); + if (!case_expr_item) + return true; + + if (!m_case_expr_holders[case_expr_id] || + m_case_expr_holders[case_expr_id]->result_type() != + case_expr_item->result_type()) + { + m_case_expr_holders[case_expr_id]= + create_case_expr_holder(thd, case_expr_item); + } + + m_case_expr_holders[case_expr_id]->store(case_expr_item); + m_case_expr_holders[case_expr_id]->cache_value(); + return false; } -/* - * - * sp_cursor - * - */ +/////////////////////////////////////////////////////////////////////////// +// sp_cursor implementation. +/////////////////////////////////////////////////////////////////////////// + sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i) :m_lex_keeper(lex_keeper), @@ -523,8 +428,7 @@ sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i) 0 in case of success, -1 otherwise */ -int -sp_cursor::open(THD *thd) +int sp_cursor::open(THD *thd) { if (server_side_cursor) { @@ -538,8 +442,7 @@ sp_cursor::open(THD *thd) } -int -sp_cursor::close(THD *thd) +int sp_cursor::close(THD *thd) { if (! server_side_cursor) { @@ -551,16 +454,14 @@ sp_cursor::close(THD *thd) } -void -sp_cursor::destroy() +void sp_cursor::destroy() { delete server_side_cursor; - server_side_cursor= 0; + server_side_cursor= NULL; } -int -sp_cursor::fetch(THD *thd, List<struct sp_variable> *vars) +int sp_cursor::fetch(THD *thd, List<sp_variable> *vars) { if (! server_side_cursor) { @@ -599,108 +500,13 @@ sp_cursor::fetch(THD *thd, List<struct sp_variable> *vars) } -/* - Create an instance of appropriate Item_cache class depending on the - specified type in the callers arena. - - SYNOPSIS - thd thread handler - result_type type of the expression - - RETURN - Pointer to valid object on success - NULL on error - - NOTE - We should create cache items in the callers arena, as they are used - between in several instructions. -*/ - -Item_cache * -sp_rcontext::create_case_expr_holder(THD *thd, const Item *item) -{ - Item_cache *holder; - Query_arena current_arena; - - thd->set_n_backup_active_arena(thd->spcont->callers_arena, ¤t_arena); - - holder= Item_cache::get_cache(item); - - thd->restore_active_arena(thd->spcont->callers_arena, ¤t_arena); - - return holder; -} - - -/* - 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 +/////////////////////////////////////////////////////////////////////////// +// sp_cursor::Select_fetch_into_spvars implementation. +/////////////////////////////////////////////////////////////////////////// - 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; } diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index dd1d625a46c..00fbf3e13ab 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -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); - - Item * - get_item(uint var_idx); +private: + sp_rcontext(const sp_pcontext *root_parsing_ctx, + Field *return_value_fld, + bool in_sub_stmt); - Item ** - get_item_addr(uint var_idx); + // Prevent use of copying constructor and operator. + sp_rcontext(const sp_rcontext &); + void operator=(sp_rcontext &); - bool - set_return_value(THD *thd, Item **return_value_item); +private: + /// 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; + + /// Instruction pointer to the first instruction. + uint first_ip; + + /// 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) + { } + }; - inline bool - is_return_value_set() const +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 { - return m_return_value_set; - } + public: + /// SQL error code. + uint sql_errno; + + /// Error level. + Sql_condition::enum_warning_level level; + + /// SQLSTATE. + char sql_state[SQLSTATE_LENGTH + 1]; + + /// Text message. + char *message; + + /// 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'; + + message= strdup_root(arena->mem_root, _sql_condition->get_message_text()); + } + }; - /* - SQL handlers support. - */ +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) + { } + }; - void push_handler(struct sp_cond_type *cond, uint h, int type); +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; - void pop_handlers(uint count); + /// 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; - bool - find_handler(THD *thd, - uint sql_errno, - const char *sqlstate, - Sql_condition::enum_warning_level level, - const char *msg); +#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 - Sql_condition_info *raised_condition() const; + ///////////////////////////////////////////////////////////////////////// + // SP-variables. + ///////////////////////////////////////////////////////////////////////// - void - push_hstack(uint h); + int set_variable(THD *thd, uint var_idx, Item **value) + { return set_variable(thd, m_var_table->field[var_idx], value); } - uint - pop_hstack(); + Item *get_item(uint var_idx) const + { return m_var_items[var_idx]; } - bool - activate_handler(THD *thd, - uint *ip, - sp_instr *instr, - Query_arena *execute_arena, - Query_arena *backup_arena); + 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); - void - exit_handler(); + bool is_return_value_set() const + { return m_return_value_set; } - void - push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i); + ///////////////////////////////////////////////////////////////////////// + // SQL-handlers. + ///////////////////////////////////////////////////////////////////////// - void - pop_cursors(uint count); + /// 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); - inline void - pop_all_cursors() - { - pop_cursors(m_ccount); - } + /// 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); - inline sp_cursor * - get_cursor(uint i) + const Sql_condition_info *raised_condition() const { - return m_cstack[i]; + return m_handler_call_stack.elements() ? + (*m_handler_call_stack.back())->sql_condition : NULL; } - /* - CASE expressions support. - */ + /// 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; } - int - set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr); +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); - Item * - get_case_expr(int case_expr_id); + /// 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; - Item ** - get_case_expr_addr(int case_expr_id); + int set_variable(THD *thd, Field *field, Item **value); private: - sp_pcontext *m_root_parsing_ctx; + /// Top-level (root) parsing context for this runtime context. + const sp_pcontext *m_root_parsing_ctx; - /* Virtual table for storing variables. */ + /// 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. - */ - Item **m_var_items; + /// 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. - */ + /// 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. - */ + /// Indicates whether the return value (in m_return_value_fld) has been + /// set during execution. bool m_return_value_set; - /** - TRUE if the context is created for a sub-statement. - */ - bool in_sub_stmt; + /// Flag to tell if the runtime context is created for a sub-statement. + bool m_in_sub_stmt; - sp_handler_t *m_handler; // Visible handlers + /// Stack of visible handlers. + Dynamic_array<sp_handler_entry *> m_handlers; - /** - SQL conditions caught by each handler. - This is an array indexed by handler index. - */ - Sql_condition_info *m_raised_conditions; + /// Stack of caught SQL conditions. + Dynamic_array<Handler_call_frame *> m_handler_call_stack; - 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 + /// Stack of cursors. + Bounds_checked_array<sp_cursor *> m_cstack; - sp_cursor **m_cstack; + /// Current number of cursors in m_cstack. uint m_ccount; - Item_cache **m_case_expr_holders; - - /* Previous runtime context (NULL if none) */ - sp_rcontext *m_prev_runtime_ctx; - -private: - bool init_var_table(THD *thd); - bool init_var_items(); - - Item_cache *create_case_expr_holder(THD *thd, const Item *item); - - int set_variable(THD *thd, Field *field, Item **value); + /// 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 diff --git a/sql/sql_array.h b/sql/sql_array.h index 9ac27cca63b..baed3687215 100644 --- a/sql/sql_array.h +++ b/sql/sql_array.h @@ -100,28 +100,64 @@ template <class Elem> class Dynamic_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; } + /// @returns pointer to first element; undefined behaviour if array is empty + const Elem *front() const + { + DBUG_ASSERT(array.elements >= 1); + return (const Elem*)array.buffer; + } + + /// @returns pointer to last element; undefined behaviour if array is empty. Elem *back() { - return ((Elem*)array.buffer) + array.elements; + 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); } - bool append(Elem &el) + /** + @retval false ok + @retval true OOM, @c my_error() has been called. + */ + bool append(const Elem &el) { - return (insert_dynamic(&array, (uchar*)&el)); + return insert_dynamic(&array, &el); } /// Pops the last element. Does nothing if array is empty. @@ -135,91 +171,37 @@ public: delete_dynamic_element(&array, idx); } - int elements() + int elements() const { return array.elements; } - ~Dynamic_array() - { - delete_dynamic(&array); - } - - typedef int (*CMP_FUNC)(const Elem *el1, const Elem *el2); - - void sort(CMP_FUNC cmp_func) - { - my_qsort(array.buffer, array.elements, sizeof(Elem), (qsort_cmp)cmp_func); - } -}; - -/* - 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() + void elements(uint num_elements) { - return buffer + n_elements; + DBUG_ASSERT(num_elements <= array.max_element); + array.elements= num_elements; } - bool append(MEM_ROOT *mem_root, Elem *el) + void clear() { - 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; + elements(0); } - int elements() + void set(uint idx, const Elem &el) { - return n_elements; + set_dynamic(&array, &el, idx); } - void clear() + ~Dynamic_array() { - n_elements= 0; + delete_dynamic(&array); } - typedef int (*CMP_FUNC)(Elem * const *el1, Elem *const *el2); + typedef int (*CMP_FUNC)(const Elem *el1, const Elem *el2); void sort(CMP_FUNC cmp_func) { - my_qsort(buffer, n_elements, sizeof(Elem*), (qsort_cmp)cmp_func); + my_qsort(array.buffer, array.elements, sizeof(Elem), (qsort_cmp)cmp_func); } }; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 7ef0ea29434..4f19b18b126 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -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; diff --git a/sql/sql_class.h b/sql/sql_class.h index 81a4b52f887..f69468f0a99 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -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(); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index d0a83eac189..af6c7a2db73 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -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; diff --git a/sql/sql_error.cc b/sql/sql_error.cc index fc5beb31218..2fb16a49c2b 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -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; +} diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 8c5fb96012d..21bf07cd653 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -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; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 4649555a348..f3c6023de70 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -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; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index f3cb797226c..349d79900a0 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -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: diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc index 81e16cec774..ab5003fa86a 100644 --- a/sql/sql_signal.cc +++ b/sql/sql_signal.cc @@ -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 */ diff --git a/sql/sql_signal.h b/sql/sql_signal.h index 6c77d4cb0bb..2a508eed5bf 100644 --- a/sql/sql_signal.h +++ b/sql/sql_signal.h @@ -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) {} diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 6a1e4d745e8..fe361801c35 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -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 diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 1dd83fa8865..cf853ffaed7 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1829,7 +1829,7 @@ multi_update::~multi_update() } -int multi_update::send_data(List<Item> ¬_used_values) +bool multi_update::send_data(List<Item> ¬_used_values) { TABLE_LIST *cur_table; DBUG_ENTER("multi_update::send_data"); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 521aed2e04a..ea2c51d0bba 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -284,7 +284,7 @@ void case_stmt_action_case(LEX *lex) (Instruction 12 in the example) */ - lex->spcont->push_label((char *)"", lex->sphead->instructions()); + lex->spcont->push_label(current_thd, EMPTY_STR, lex->sphead->instructions()); } /** @@ -353,7 +353,7 @@ int case_stmt_action_when(LEX *lex, Item *when, bool simple) */ return !test(i) || - sp->push_backpatch(i, ctx->push_label((char *)"", 0)) || + sp->push_backpatch(i, ctx->push_label(current_thd, EMPTY_STR, 0)) || sp->add_cont_backpatch(i) || sp->add_instr(i); } @@ -469,7 +469,7 @@ set_system_variable(THD *thd, struct sys_var_with_base *tmp, */ static bool -set_local_variable(THD *thd, sp_variable_t *spv, Item *val) +set_local_variable(THD *thd, sp_variable *spv, Item *val) { Item *it; LEX *lex= thd->lex; @@ -477,8 +477,8 @@ set_local_variable(THD *thd, sp_variable_t *spv, Item *val) if (val) it= val; - else if (spv->dflt) - it= spv->dflt; + else if (spv->default_value) + it= spv->default_value; else { it= new (thd->mem_root) Item_null(); @@ -559,7 +559,7 @@ set_trigger_new_row(THD *thd, LEX_STRING *name, Item *val) @return An Item_splocal object representing the SP variable, or NULL on error. */ static Item_splocal* -create_item_for_sp_var(THD *thd, LEX_STRING name, sp_variable_t *spvar, +create_item_for_sp_var(THD *thd, LEX_STRING name, sp_variable *spvar, const char *start_in_q, const char *end_in_q) { Item_splocal *item; @@ -569,7 +569,7 @@ create_item_for_sp_var(THD *thd, LEX_STRING name, sp_variable_t *spvar, /* If necessary, look for the variable. */ if (spc && !spvar) - spvar= spc->find_variable(&name); + spvar= spc->find_variable(name, false); if (!spvar) { @@ -931,7 +931,7 @@ static bool sp_create_assignment_instr(THD *thd, bool no_lookahead) timestamp_type date_time_type; st_select_lex *select_lex; chooser_compare_func_creator boolfunc2creator; - struct sp_cond_type *spcondtype; + class sp_condition_value *spcondvalue; struct { int vars, conds, hndlrs, curs; } spblock; sp_name *spname; LEX *lex; @@ -1857,7 +1857,7 @@ END_OF_INPUT %type <NONE> case_stmt_specification simple_case_stmt searched_case_stmt %type <num> sp_decl_idents sp_opt_inout sp_handler_type sp_hcond_list -%type <spcondtype> sp_cond sp_hcond sqlstate signal_value opt_signal_value +%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value %type <spblock> sp_decls sp_decl %type <lex> sp_cursor_stmt %type <spname> sp_name @@ -2822,14 +2822,16 @@ sp_fdparam: LEX *lex= Lex; sp_pcontext *spc= lex->spcont; - if (spc->find_variable(&$1, TRUE)) + if (spc->find_variable($1, TRUE)) { my_error(ER_SP_DUP_PARAM, MYF(0), $1.str); MYSQL_YYABORT; } - sp_variable_t *spvar= spc->push_variable(&$1, - (enum enum_field_types)$3, - sp_param_in); + + sp_variable *spvar= spc->add_variable(YYTHD, + $1, + (enum enum_field_types) $3, + sp_variable::MODE_IN); if (lex->sphead->fill_field_definition(YYTHD, lex, (enum enum_field_types) $3, @@ -2859,14 +2861,15 @@ sp_pdparam: LEX *lex= Lex; sp_pcontext *spc= lex->spcont; - if (spc->find_variable(&$3, TRUE)) + if (spc->find_variable($3, TRUE)) { my_error(ER_SP_DUP_PARAM, MYF(0), $3.str); MYSQL_YYABORT; } - sp_variable_t *spvar= spc->push_variable(&$3, - (enum enum_field_types)$4, - (sp_param_mode_t)$1); + sp_variable *spvar= spc->add_variable(YYTHD, + $3, + (enum enum_field_types) $4, + (sp_variable::enum_mode) $1); if (lex->sphead->fill_field_definition(YYTHD, lex, (enum enum_field_types) $4, @@ -2880,10 +2883,10 @@ sp_pdparam: ; sp_opt_inout: - /* Empty */ { $$= sp_param_in; } - | IN_SYM { $$= sp_param_in; } - | OUT_SYM { $$= sp_param_out; } - | INOUT_SYM { $$= sp_param_inout; } + /* Empty */ { $$= sp_variable::MODE_IN; } + | IN_SYM { $$= sp_variable::MODE_IN; } + | OUT_SYM { $$= sp_variable::MODE_OUT; } + | INOUT_SYM { $$= sp_variable::MODE_INOUT; } ; sp_proc_stmts: @@ -2955,13 +2958,13 @@ sp_decl: for (uint i = num_vars-$2 ; i < num_vars ; i++) { uint var_idx= pctx->var_context2runtime(i); - sp_variable_t *spvar= pctx->find_variable(var_idx); + sp_variable *spvar= pctx->find_variable(var_idx); if (!spvar) MYSQL_YYABORT; spvar->type= var_type; - spvar->dflt= dflt_value_item; + spvar->default_value= dflt_value_item; if (lex->sphead->fill_field_definition(YYTHD, lex, var_type, &spvar->field_def)) @@ -2997,36 +3000,41 @@ sp_decl: LEX *lex= Lex; sp_pcontext *spc= lex->spcont; - if (spc->find_cond(&$2, TRUE)) + if (spc->find_condition($2, TRUE)) { my_error(ER_SP_DUP_COND, MYF(0), $2.str); MYSQL_YYABORT; } - if(YYTHD->lex->spcont->push_cond(&$2, $5)) + if(spc->add_condition(YYTHD, $2, $5)) MYSQL_YYABORT; $$.vars= $$.hndlrs= $$.curs= 0; $$.conds= 1; } | DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM { + THD *thd= YYTHD; LEX *lex= Lex; sp_head *sp= lex->sphead; - lex->spcont= lex->spcont->push_context(LABEL_HANDLER_SCOPE); + sp_handler *h= lex->spcont->add_handler(thd, + (sp_handler::enum_type) $2); + + lex->spcont= lex->spcont->push_context(thd, + sp_pcontext::HANDLER_SCOPE); sp_pcontext *ctx= lex->spcont; sp_instr_hpush_jump *i= - new sp_instr_hpush_jump(sp->instructions(), ctx, $2, - ctx->current_var_count()); + new sp_instr_hpush_jump(sp->instructions(), ctx, h); + if (i == NULL || sp->add_instr(i)) MYSQL_YYABORT; /* For continue handlers, mark end of handler scope. */ - if ($2 == SP_HANDLER_CONTINUE && + if ($2 == sp_handler::CONTINUE && sp->push_backpatch(i, ctx->last_label())) MYSQL_YYABORT; - if (sp->push_backpatch(i, ctx->push_label(empty_c_string, 0))) + if (sp->push_backpatch(i, ctx->push_label(thd, EMPTY_STR, 0))) MYSQL_YYABORT; } sp_hcond_list sp_proc_stmt @@ -3034,20 +3042,19 @@ sp_decl: LEX *lex= Lex; sp_head *sp= lex->sphead; sp_pcontext *ctx= lex->spcont; - sp_label_t *hlab= lex->spcont->pop_label(); /* After this hdlr */ + sp_label *hlab= lex->spcont->pop_label(); /* After this hdlr */ sp_instr_hreturn *i; - if ($2 == SP_HANDLER_CONTINUE) + if ($2 == sp_handler::CONTINUE) { - i= new sp_instr_hreturn(sp->instructions(), ctx, - ctx->current_var_count()); + i= new sp_instr_hreturn(sp->instructions(), ctx); if (i == NULL || sp->add_instr(i)) MYSQL_YYABORT; } else { /* EXIT or UNDO handler, just jump to the end of the block */ - i= new sp_instr_hreturn(sp->instructions(), ctx, 0); + i= new sp_instr_hreturn(sp->instructions(), ctx); if (i == NULL || sp->add_instr(i) || sp->push_backpatch(i, lex->spcont->last_label())) /* Block end */ @@ -3058,8 +3065,7 @@ sp_decl: lex->spcont= ctx->pop_context(); $$.vars= $$.conds= $$.curs= 0; - $$.hndlrs= $6; - lex->spcont->add_handlers($6); + $$.hndlrs= 1; } | DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt { @@ -3069,7 +3075,7 @@ sp_decl: uint offp; sp_instr_cpush *i; - if (ctx->find_cursor(&$2, &offp, TRUE)) + if (ctx->find_cursor($2, &offp, TRUE)) { my_error(ER_SP_DUP_CURS, MYF(0), $2.str); delete $5; @@ -3079,7 +3085,7 @@ sp_decl: ctx->current_cursor_count()); if (i == NULL || sp->add_instr(i) || - ctx->push_cursor(&$2)) + ctx->add_cursor($2)) MYSQL_YYABORT; $$.vars= $$.conds= $$.hndlrs= 0; $$.curs= 1; @@ -3110,9 +3116,9 @@ sp_cursor_stmt: ; sp_handler_type: - EXIT_SYM { $$= SP_HANDLER_EXIT; } - | CONTINUE_SYM { $$= SP_HANDLER_CONTINUE; } - /*| UNDO_SYM { QQ No yet } */ + EXIT_SYM { $$= sp_handler::EXIT; } + | CONTINUE_SYM { $$= sp_handler::CONTINUE; } + /*| UNDO_SYM { QQ No yet } */ ; sp_hcond_list: @@ -3129,7 +3135,7 @@ sp_hcond_element: sp_head *sp= lex->sphead; sp_pcontext *ctx= lex->spcont->parent_context(); - if (ctx->find_handler($1)) + if (ctx->check_duplicate_handler($1)) { my_message(ER_SP_DUP_HANDLER, ER(ER_SP_DUP_HANDLER), MYF(0)); MYSQL_YYABORT; @@ -3140,7 +3146,6 @@ sp_hcond_element: (sp_instr_hpush_jump *)sp->last_instruction(); i->add_condition($1); - ctx->push_handler($1); } } ; @@ -3153,11 +3158,9 @@ sp_cond: my_error(ER_WRONG_VALUE, MYF(0), "CONDITION", "0"); MYSQL_YYABORT; } - $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$= new (YYTHD->mem_root) sp_condition_value($1); if ($$ == NULL) MYSQL_YYABORT; - $$->type= sp_cond_type_t::number; - $$->mysqlerr= $1; } | sqlstate ; @@ -3165,17 +3168,22 @@ sp_cond: sqlstate: SQLSTATE_SYM opt_value TEXT_STRING_literal { /* SQLSTATE */ - if (!sp_cond_check(&$3)) + + /* + An error is triggered: + - if the specified string is not a valid SQLSTATE, + - or if it represents the completion condition -- it is not + allowed to SIGNAL, or declare a handler for the completion + condition. + */ + if (!is_sqlstate_valid(&$3) || is_sqlstate_completion($3.str)) { my_error(ER_SP_BAD_SQLSTATE, MYF(0), $3.str); MYSQL_YYABORT; } - $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$= new (YYTHD->mem_root) sp_condition_value($3.str); if ($$ == NULL) MYSQL_YYABORT; - $$->type= sp_cond_type_t::state; - memcpy($$->sqlstate, $3.str, SQLSTATE_LENGTH); - $$->sqlstate[SQLSTATE_LENGTH]= '\0'; } ; @@ -3191,7 +3199,7 @@ sp_hcond: } | ident /* CONDITION name */ { - $$= Lex->spcont->find_cond(&$1); + $$= Lex->spcont->find_condition($1, false); if ($$ == NULL) { my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str); @@ -3200,24 +3208,22 @@ sp_hcond: } | SQLWARNING_SYM /* SQLSTATEs 01??? */ { - $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$= new (YYTHD->mem_root) sp_condition_value(sp_condition_value::WARNING); if ($$ == NULL) MYSQL_YYABORT; - $$->type= sp_cond_type_t::warning; } | not FOUND_SYM /* SQLSTATEs 02??? */ { - $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$= new (YYTHD->mem_root) sp_condition_value(sp_condition_value::NOT_FOUND); if ($$ == NULL) MYSQL_YYABORT; - $$->type= sp_cond_type_t::notfound; } | SQLEXCEPTION_SYM /* All other SQLSTATEs */ { - $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$= (sp_condition_value *)YYTHD->alloc(sizeof(sp_condition_value)); + $$= new (YYTHD->mem_root) sp_condition_value(sp_condition_value::EXCEPTION); if ($$ == NULL) MYSQL_YYABORT; - $$->type= sp_cond_type_t::exception; } ; @@ -3240,20 +3246,20 @@ signal_value: ident { LEX *lex= Lex; - sp_cond_type_t *cond; + sp_condition_value *cond; if (lex->spcont == NULL) { /* SIGNAL foo cannot be used outside of stored programs */ my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str); MYSQL_YYABORT; } - cond= lex->spcont->find_cond(&$1); + cond= lex->spcont->find_condition($1, false); if (cond == NULL) { my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str); MYSQL_YYABORT; } - if (cond->type != sp_cond_type_t::state) + if (cond->type != sp_condition_value::SQLSTATE) { my_error(ER_SIGNAL_BAD_CONDITION_TYPE, MYF(0)); MYSQL_YYABORT; @@ -3530,12 +3536,15 @@ sp_decl_idents: LEX *lex= Lex; sp_pcontext *spc= lex->spcont; - if (spc->find_variable(&$1, TRUE)) + if (spc->find_variable($1, TRUE)) { my_error(ER_SP_DUP_VAR, MYF(0), $1.str); MYSQL_YYABORT; } - spc->push_variable(&$1, (enum_field_types)0, sp_param_in); + spc->add_variable(YYTHD, + $1, + MYSQL_TYPE_DECIMAL, + sp_variable::MODE_IN); $$= 1; } | sp_decl_idents ',' ident @@ -3545,12 +3554,15 @@ sp_decl_idents: LEX *lex= Lex; sp_pcontext *spc= lex->spcont; - if (spc->find_variable(&$3, TRUE)) + if (spc->find_variable($3, TRUE)) { my_error(ER_SP_DUP_VAR, MYF(0), $3.str); MYSQL_YYABORT; } - spc->push_variable(&$3, (enum_field_types)0, sp_param_in); + spc->add_variable(YYTHD, + $3, + MYSQL_TYPE_DECIMAL, + sp_variable::MODE_IN); $$= $1 + 1; } ; @@ -3672,7 +3684,9 @@ sp_proc_stmt_unlabeled: { /* Unlabeled controls get a secret label. */ LEX *lex= Lex; - lex->spcont->push_label((char *)"", lex->sphead->instructions()); + lex->spcont->push_label(YYTHD, + EMPTY_STR, + lex->sphead->instructions()); } sp_unlabeled_control { @@ -3688,7 +3702,7 @@ sp_proc_stmt_leave: LEX *lex= Lex; sp_head *sp = lex->sphead; sp_pcontext *ctx= lex->spcont; - sp_label_t *lab= ctx->find_label($2.str); + sp_label *lab= ctx->find_label($2); if (! lab) { @@ -3708,7 +3722,7 @@ sp_proc_stmt_leave: there are no hpop/cpop at the jump destination, so we should include the block context here for cleanup. */ - bool exclusive= (lab->type == SP_LAB_BEGIN); + bool exclusive= (lab->type == sp_label::BEGIN); n= ctx->diff_handlers(lab->ctx, exclusive); if (n) @@ -3741,9 +3755,9 @@ sp_proc_stmt_iterate: LEX *lex= Lex; sp_head *sp= lex->sphead; sp_pcontext *ctx= lex->spcont; - sp_label_t *lab= ctx->find_label($2.str); + sp_label *lab= ctx->find_label($2); - if (! lab || lab->type != SP_LAB_ITER) + if (! lab || lab->type != sp_label::ITERATION) { my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str); MYSQL_YYABORT; @@ -3786,7 +3800,7 @@ sp_proc_stmt_open: uint offset; sp_instr_copen *i; - if (! lex->spcont->find_cursor(&$2, &offset)) + if (! lex->spcont->find_cursor($2, &offset, false)) { my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str); MYSQL_YYABORT; @@ -3806,7 +3820,7 @@ sp_proc_stmt_fetch: uint offset; sp_instr_cfetch *i; - if (! lex->spcont->find_cursor(&$3, &offset)) + if (! lex->spcont->find_cursor($3, &offset, false)) { my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $3.str); MYSQL_YYABORT; @@ -3828,7 +3842,7 @@ sp_proc_stmt_close: uint offset; sp_instr_cclose *i; - if (! lex->spcont->find_cursor(&$2, &offset)) + if (! lex->spcont->find_cursor($2, &offset, false)) { my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str); MYSQL_YYABORT; @@ -3852,9 +3866,9 @@ sp_fetch_list: LEX *lex= Lex; sp_head *sp= lex->sphead; sp_pcontext *spc= lex->spcont; - sp_variable_t *spv; + sp_variable *spv; - if (!spc || !(spv = spc->find_variable(&$1))) + if (!spc || !(spv = spc->find_variable($1, false))) { my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); MYSQL_YYABORT; @@ -3872,9 +3886,9 @@ sp_fetch_list: LEX *lex= Lex; sp_head *sp= lex->sphead; sp_pcontext *spc= lex->spcont; - sp_variable_t *spv; + sp_variable *spv; - if (!spc || !(spv = spc->find_variable(&$3))) + if (!spc || !(spv = spc->find_variable($3, false))) { my_error(ER_SP_UNDECLARED_VAR, MYF(0), $3.str); MYSQL_YYABORT; @@ -3900,7 +3914,7 @@ sp_if: sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx, $2, lex); if (i == NULL || - sp->push_backpatch(i, ctx->push_label((char *)"", 0)) || + sp->push_backpatch(i, ctx->push_label(YYTHD, EMPTY_STR, 0)) || sp->add_cont_backpatch(i) || sp->add_instr(i)) MYSQL_YYABORT; @@ -3917,7 +3931,7 @@ sp_if: sp->add_instr(i)) MYSQL_YYABORT; sp->backpatch(ctx->pop_label()); - sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + sp->push_backpatch(i, ctx->push_label(YYTHD, EMPTY_STR, 0)); } sp_elseifs { @@ -4059,7 +4073,7 @@ sp_labeled_control: { LEX *lex= Lex; sp_pcontext *ctx= lex->spcont; - sp_label_t *lab= ctx->find_label($1.str); + sp_label *lab= ctx->find_label($1); if (lab) { @@ -4068,19 +4082,18 @@ sp_labeled_control: } else { - lab= lex->spcont->push_label($1.str, - lex->sphead->instructions()); - lab->type= SP_LAB_ITER; + lab= lex->spcont->push_label(YYTHD, $1, lex->sphead->instructions()); + lab->type= sp_label::ITERATION; } } sp_unlabeled_control sp_opt_label { LEX *lex= Lex; - sp_label_t *lab= lex->spcont->pop_label(); + sp_label *lab= lex->spcont->pop_label(); if ($5.str) { - if (my_strcasecmp(system_charset_info, $5.str, lab->name) != 0) + if (my_strcasecmp(system_charset_info, $5.str, lab->name.str) != 0) { my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str); MYSQL_YYABORT; @@ -4100,7 +4113,7 @@ sp_labeled_block: { LEX *lex= Lex; sp_pcontext *ctx= lex->spcont; - sp_label_t *lab= ctx->find_label($1.str); + sp_label *lab= ctx->find_label($1); if (lab) { @@ -4108,18 +4121,17 @@ sp_labeled_block: MYSQL_YYABORT; } - lab= lex->spcont->push_label($1.str, - lex->sphead->instructions()); - lab->type= SP_LAB_BEGIN; + lab= lex->spcont->push_label(YYTHD, $1, lex->sphead->instructions()); + lab->type= sp_label::BEGIN; } sp_block_content sp_opt_label { LEX *lex= Lex; - sp_label_t *lab= lex->spcont->pop_label(); + sp_label *lab= lex->spcont->pop_label(); if ($5.str) { - if (my_strcasecmp(system_charset_info, $5.str, lab->name) != 0) + if (my_strcasecmp(system_charset_info, $5.str, lab->name.str) != 0) { my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str); MYSQL_YYABORT; @@ -4132,8 +4144,8 @@ sp_unlabeled_block: { /* Unlabeled blocks get a secret label. */ LEX *lex= Lex; uint ip= lex->sphead->instructions(); - sp_label_t *lab= lex->spcont->push_label((char *)"", ip); - lab->type= SP_LAB_BEGIN; + sp_label *lab= lex->spcont->push_label(YYTHD, EMPTY_STR, ip); + lab->type= sp_label::BEGIN; } sp_block_content { @@ -4148,7 +4160,8 @@ sp_block_content: together. No [[NOT] ATOMIC] yet, and we need to figure out how make it coexist with the existing BEGIN COMMIT/ROLLBACK. */ LEX *lex= Lex; - lex->spcont= lex->spcont->push_context(LABEL_DEFAULT_SCOPE); + lex->spcont= lex->spcont->push_context(YYTHD, + sp_pcontext::REGULAR_SCOPE); } sp_decls sp_proc_stmts @@ -4184,7 +4197,7 @@ sp_unlabeled_control: { LEX *lex= Lex; uint ip= lex->sphead->instructions(); - sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_label *lab= lex->spcont->last_label(); /* Jumping back */ sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip); if (i == NULL || lex->sphead->add_instr(i)) @@ -4212,7 +4225,7 @@ sp_unlabeled_control: { LEX *lex= Lex; uint ip= lex->sphead->instructions(); - sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_label *lab= lex->spcont->last_label(); /* Jumping back */ sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip); if (i == NULL || lex->sphead->add_instr(i)) @@ -4225,7 +4238,7 @@ sp_unlabeled_control: { LEX *lex= Lex; uint ip= lex->sphead->instructions(); - sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_label *lab= lex->spcont->last_label(); /* Jumping back */ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont, $5, lab->ip, lex); @@ -11107,9 +11120,9 @@ limit_option: THD *thd= YYTHD; LEX *lex= thd->lex; Lex_input_stream *lip= & thd->m_parser_state->m_lip; - sp_variable_t *spv; + sp_variable *spv; sp_pcontext *spc = lex->spcont; - if (spc && (spv = spc->find_variable(&$1))) + if (spc && (spv = spc->find_variable($1, false))) { splocal= new (thd->mem_root) Item_splocal($1, spv->offset, spv->type, @@ -11329,9 +11342,9 @@ select_var_ident: | ident_or_text { LEX *lex=Lex; - sp_variable_t *t; + sp_variable *t; - if (!lex->spcont || !(t=lex->spcont->find_variable(&$1))) + if (!lex->spcont || !(t=lex->spcont->find_variable($1, false))) { my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); MYSQL_YYABORT; @@ -13255,9 +13268,9 @@ simple_ident: THD *thd= YYTHD; LEX *lex= thd->lex; Lex_input_stream *lip= YYLIP; - sp_variable_t *spv; + sp_variable *spv; sp_pcontext *spc = lex->spcont; - if (spc && (spv = spc->find_variable(&$1))) + if (spc && (spv = spc->find_variable($1, false))) { /* We're compiling a stored procedure and found a variable */ if (! lex->parsing_options.allows_variable) @@ -14224,12 +14237,11 @@ option_value_no_option_type: { THD *thd= YYTHD; LEX *lex= Lex; - LEX_STRING *name= &$1.base_name; if ($1.var == trg_new_row_fake_var) { /* We are in trigger and assigning value to field of new row */ - if (set_trigger_new_row(YYTHD, name, $3)) + if (set_trigger_new_row(YYTHD, &$1.base_name, $3)) MYSQL_YYABORT; } else if ($1.var) @@ -14241,7 +14253,7 @@ option_value_no_option_type: else { sp_pcontext *spc= lex->spcont; - sp_variable *spv= spc->find_variable(name, false); + sp_variable *spv= spc->find_variable($1.base_name, false); /* It is a local variable. */ if (set_local_variable(thd, spv, $3)) @@ -14294,7 +14306,7 @@ option_value_no_option_type: names.str= (char *)"names"; names.length= 5; - if (spc && spc->find_variable(&names, false)) + if (spc && spc->find_variable(names, false)) my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str); else my_parse_error(ER(ER_SYNTAX_ERROR)); @@ -14330,7 +14342,7 @@ option_value_no_option_type: pw.str= (char *)"password"; pw.length= 8; - if (spc && spc->find_variable(&pw, false)) + if (spc && spc->find_variable(pw, false)) { my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str); MYSQL_YYABORT; @@ -14365,10 +14377,10 @@ internal_variable_name: { THD *thd= YYTHD; sp_pcontext *spc= thd->lex->spcont; - sp_variable_t *spv; + sp_variable *spv; /* Best effort lookup for system variable. */ - if (!spc || !(spv = spc->find_variable(&$1))) + if (!spc || !(spv = spc->find_variable($1, false))) { struct sys_var_with_base tmp= {NULL, $1}; |