diff options
author | unknown <anozdrin@mysql.com> | 2005-12-07 17:17:42 +0300 |
---|---|---|
committer | unknown <anozdrin@mysql.com> | 2005-12-07 17:17:42 +0300 |
commit | 943edab9a545c3a7bb3c55542b77f073ea21e91a (patch) | |
tree | 763144b950b19c3f90be07331782b4e4629cfc1e /sql/sp_head.cc | |
parent | 9c8773f56338ad22419f8bb4587f935850814b9b (diff) | |
parent | 6b2f13098a59d9ad520828dca4cb63da8c86b5e3 (diff) | |
download | mariadb-git-943edab9a545c3a7bb3c55542b77f073ea21e91a.tar.gz |
Merge bk-internal.mysql.com:/home/bk/mysql-5.0
into mysql.com:/home/alik/Documents/AllProgs/MySQL/devel/5.0-sp-vars-merge-2
mysql-test/r/sp.result:
Auto merged
mysql-test/t/sp.test:
Auto merged
sql/field.cc:
Auto merged
sql/item.cc:
Auto merged
sql/item.h:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/sql_parse.cc:
Auto merged
sql/sql_yacc.yy:
Auto merged
sql/sp_head.cc:
Merge.
Diffstat (limited to 'sql/sp_head.cc')
-rw-r--r-- | sql/sp_head.cc | 937 |
1 files changed, 521 insertions, 416 deletions
diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 38a59b0f383..cc847a0134c 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -27,8 +27,7 @@ Item_result sp_map_result_type(enum enum_field_types type) { - switch (type) - { + switch (type) { case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: @@ -46,6 +45,81 @@ sp_map_result_type(enum enum_field_types type) } } + +Item::Type +sp_map_item_type(enum enum_field_types type) +{ + switch (type) { + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + return Item::INT_ITEM; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return Item::DECIMAL_ITEM; + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + return Item::REAL_ITEM; + default: + return Item::STRING_ITEM; + } +} + + +/* + Return a string representation of the Item value. + + NOTE: this is a legacy-compatible implementation. It fails if the value + contains non-ordinary symbols, which should be escaped. + + SYNOPSIS + item a pointer to the Item + str string buffer for representation of the value + + RETURN + NULL on error + a pointer to valid a valid string on success +*/ + +static String * +sp_get_item_value(Item *item, String *str) +{ + Item_result result_type= item->result_type(); + + switch (item->result_type()) { + case REAL_RESULT: + case INT_RESULT: + case DECIMAL_RESULT: + return item->val_str(str); + + case STRING_RESULT: + { + char buf_holder[STRING_BUFFER_USUAL_SIZE]; + String buf(buf_holder, sizeof(buf_holder), &my_charset_latin1); + String *result= item->val_str(str); + + if (!result) + return NULL; + + buf.append('_'); + buf.append(result->charset()->csname); + buf.append('\''); + buf.append(*result); + buf.append('\''); + str->copy(buf); + + return str; + } + + case ROW_RESULT: + default: + return NULL; + } +} + + /* SYNOPSIS sp_get_flags_for_command() @@ -177,7 +251,7 @@ sp_get_flags_for_command(LEX *lex) /* - Prepare Item for execution (call of fix_fields) + Prepare an Item for evaluation (call of fix_fields). SYNOPSIS sp_prepare_func_item() @@ -189,14 +263,15 @@ sp_get_flags_for_command(LEX *lex) prepared item */ -static Item * +Item * sp_prepare_func_item(THD* thd, Item **it_addr) { - Item *it= *it_addr; DBUG_ENTER("sp_prepare_func_item"); - it_addr= it->this_item_addr(thd, it_addr); + it_addr= (*it_addr)->this_item_addr(thd, it_addr); - if (!it->fixed && (*it_addr)->fix_fields(thd, it_addr)) + if (!(*it_addr)->fixed && + ((*it_addr)->fix_fields(thd, it_addr) || + (*it_addr)->check_cols(1))) { DBUG_PRINT("info", ("fix_fields() failed")); DBUG_RETURN(NULL); @@ -205,202 +280,62 @@ sp_prepare_func_item(THD* thd, Item **it_addr) } -/* Macro to switch arena in sp_eval_func_item */ -#define CREATE_ON_CALLERS_ARENA(new_command, condition, backup_arena) \ - do \ - { \ - if (condition) \ - thd->set_n_backup_active_arena(thd->spcont->callers_arena, \ - backup_arena); \ - new_command; \ - if (condition) \ - thd->restore_active_arena(thd->spcont->callers_arena, \ - backup_arena); \ - } while(0) - /* - Evaluate an item and store it in the returned item + Evaluate an expression and store the result in the field. SYNOPSIS - sp_eval_func_item() - name - current thread object - it_addr - pointer to the item to evaluate - type - type of the item we evaluating - reuse - used if we would like to reuse existing item - instead of allocation of the new one - use_callers_arena - TRUE if we want to use caller's arena - rather then current one. - DESCRIPTION - We use this function to evaluate result for stored functions - and stored procedure parameters. It is also used to evaluate and - (re) allocate variables. + sp_eval_expr() + thd - current thread object + expr_item - the root item of the expression + result_field - the field to store the result RETURN VALUES - Evaluated item is returned + FALSE on success + TRUE on error */ -Item * -sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, - Item *reuse, bool use_callers_arena) +bool +sp_eval_expr(THD *thd, Field *result_field, Item *expr_item) { - DBUG_ENTER("sp_eval_func_item"); - Item *it= sp_prepare_func_item(thd, it_addr); - uint rsize; - Query_arena backup_arena; - Item *old_item_next, *old_free_list, **p_free_list; - DBUG_PRINT("info", ("type: %d", type)); + DBUG_ENTER("sp_eval_expr"); - if (!it) - DBUG_RETURN(NULL); + if (!(expr_item= sp_prepare_func_item(thd, &expr_item))) + DBUG_RETURN(TRUE); - if (reuse) - { - old_item_next= reuse->next; - p_free_list= use_callers_arena ? &thd->spcont->callers_arena->free_list : - &thd->free_list; - old_free_list= *p_free_list; - } + bool err_status= FALSE; - switch (sp_map_result_type(type)) { - case INT_RESULT: - { - longlong i= it->val_int(); - - if (it->null_value) - { - DBUG_PRINT("info", ("INT_RESULT: null")); - goto return_null_item; - } - DBUG_PRINT("info", ("INT_RESULT: %d", i)); - CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_int(i), - use_callers_arena, &backup_arena); - break; - } - case REAL_RESULT: - { - double d= it->val_real(); - uint8 decimals; - uint32 max_length; - - if (it->null_value) - { - DBUG_PRINT("info", ("REAL_RESULT: null")); - goto return_null_item; - } + /* + Set THD flags to emit warnings/errors in case of overflow/type errors + during saving the item into the field. - /* - There's some difference between Item::new_item() and the - constructor; the former crashes, the latter works... weird. - */ - decimals= it->decimals; - max_length= it->max_length; - DBUG_PRINT("info", ("REAL_RESULT: %g", d)); - CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_float(d), - use_callers_arena, &backup_arena); - it->decimals= decimals; - it->max_length= max_length; - break; - } - case DECIMAL_RESULT: - { - my_decimal value, *val= it->val_decimal(&value); - if (it->null_value) - goto return_null_item; - CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_decimal(val), - use_callers_arena, &backup_arena); -#ifndef DBUG_OFF - { - char dbug_buff[DECIMAL_MAX_STR_LENGTH+1]; - DBUG_PRINT("info", ("DECIMAL_RESULT: %s", - dbug_decimal_as_string(dbug_buff, val))); - } -#endif - break; - } - case STRING_RESULT: - { - char buffer[MAX_FIELD_WIDTH]; - String tmp(buffer, sizeof(buffer), it->collation.collation); - String *s= it->val_str(&tmp); + Save original values and restore them after save. + */ + + enum_check_fields save_count_cuted_fields= thd->count_cuted_fields; + bool save_abort_on_warning= thd->abort_on_warning; + bool save_no_trans_update= thd->no_trans_update; - if (type == MYSQL_TYPE_NULL || it->null_value) - { - DBUG_PRINT("info", ("STRING_RESULT: null")); - goto return_null_item; - } - DBUG_PRINT("info",("STRING_RESULT: %.*s", - s->length(), s->c_ptr_quick())); - /* - Reuse mechanism in sp_eval_func_item() is only employed for assignments - to local variables and OUT/INOUT SP parameters repsesented by - Item_splocal. Usually we have some expression, which needs - to be calculated and stored into the local variable. However in the - case if "it" equals to "reuse", there is no "calculation" step. So, - no reason to employ reuse mechanism to save variable into itself. - */ - if (it == reuse) - DBUG_RETURN(it); + thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL; + thd->abort_on_warning= + thd->variables.sql_mode & + (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES); + thd->no_trans_update= 0; - /* - For some functions, 's' is now pointing to an argument of the - function, which might be a local variable that is to be reused. - In this case, new(reuse, &rsize) below will call the destructor - and 's' ends up pointing to freed memory. - A somewhat ugly fix is to simply copy the string to our local one - (which is unused by most functions anyway), but only if 's' is - pointing somewhere else than to 'tmp' or 'it->str_value'. - */ - if (reuse && s != &tmp && s != &it->str_value) - { - if (tmp.copy((const String)(*s))) - DBUG_RETURN(NULL); - s= &tmp; - } + /* Save the value in the field. Convert the value if needed. */ - CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) - Item_string(it->collation.collation), - use_callers_arena, &backup_arena); - /* - We have to use special constructor and allocate string - on system heap here. This is because usual Item_string - constructor would allocate memory in the callers arena. - This would lead to the memory leak in SP loops. - See Bug #11333 "Stored Procedure: Memory blow up on - repeated SELECT ... INTO query" for sample of such SP. - TODO: Usage of the system heap gives significant overhead, - however usual "reuse" mechanism does not work here, as - Item_string has no max size. That is, if we have a loop, which - has string variable with constantly increasing size, we would have - to allocate new pieces of memory again and again on each iteration. - In future we should probably reserve some area of memory for - not-very-large strings and reuse it. But for large strings - we would have to use system heap anyway. - */ - ((Item_string*) it)->set_str_with_copy(s->ptr(), s->length()); - break; - } - case ROW_RESULT: - default: - DBUG_ASSERT(0); - } - goto end; + expr_item->save_in_field(result_field, 0); -return_null_item: - CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_null(), - use_callers_arena, &backup_arena); -end: - it->rsize= rsize; + thd->count_cuted_fields= save_count_cuted_fields; + thd->abort_on_warning= save_abort_on_warning; + thd->no_trans_update= save_no_trans_update; - if (reuse && it == reuse) + if (thd->net.report_error) { - /* - The Item constructor registered itself in the arena free list, - while the item slot is reused, so we have to restore the list. - */ - it->next= old_item_next; - *p_free_list= old_free_list; + /* Return error status if something went wrong. */ + err_status= TRUE; } - DBUG_RETURN(it); + + DBUG_RETURN(err_status); } @@ -485,9 +420,11 @@ sp_head::operator delete(void *ptr, size_t size) sp_head::sp_head() :Query_arena(&main_mem_root, INITIALIZED_FOR_SP), - m_flags(0), m_returns_cs(NULL), m_recursion_level(0), m_next_cached_sp(0), + m_flags(0), m_recursion_level(0), m_next_cached_sp(0), m_first_instance(this), m_first_free_instance(this), m_last_cached_sp(this) { + m_return_field_def.charset = NULL; + extern byte * sp_table_key(const byte *ptr, uint *plen, my_bool first); DBUG_ENTER("sp_head::sp_head"); @@ -506,6 +443,7 @@ sp_head::init(LEX *lex) DBUG_ENTER("sp_head::init"); lex->spcont= m_pcont= new sp_pcontext(NULL); + /* Altough trg_table_fields list is used only in triggers we init for all types of stored procedures to simplify reset_lex()/restore_lex() code. @@ -517,7 +455,7 @@ sp_head::init(LEX *lex) m_body.str= m_defstr.str= 0; m_qname.length= m_db.length= m_name.length= m_params.length= m_body.length= m_defstr.length= 0; - m_returns_cs= NULL; + m_return_field_def.charset= NULL; DBUG_VOID_RETURN; } @@ -576,12 +514,13 @@ sp_head::init_strings(THD *thd, LEX *lex, sp_name *name) DBUG_VOID_RETURN; } -TYPELIB * -sp_head::create_typelib(List<String> *src) + +static TYPELIB * +create_typelib(MEM_ROOT *mem_root, create_field *field_def, List<String> *src) { TYPELIB *result= NULL; - CHARSET_INFO *cs= m_returns_cs; - DBUG_ENTER("sp_head::clone_typelib"); + CHARSET_INFO *cs= field_def->charset; + DBUG_ENTER("create_typelib"); if (src->elements) { result= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB)); @@ -626,6 +565,7 @@ sp_head::create_typelib(List<String> *src) return result; } + int sp_head::create(THD *thd) { @@ -713,17 +653,30 @@ sp_head::destroy() */ Field * -sp_head::make_field(uint max_length, const char *name, TABLE *dummy) +sp_head::create_result_field(uint field_max_length, const char *field_name, + TABLE *table) { + uint field_length; Field *field; - DBUG_ENTER("sp_head::make_field"); - - field= ::make_field((char *)0, - !m_returns_len ? max_length : m_returns_len, - (uchar *)"", 0, m_returns_pack, m_returns, m_returns_cs, - m_geom_returns, Field::NONE, - m_returns_typelib, - name ? name : (const char *)m_name.str, dummy); + + DBUG_ENTER("sp_head::create_result_field"); + + field_length= !m_return_field_def.length ? + field_max_length : m_return_field_def.length; + + field= ::make_field((char*) 0, /* field ptr */ + field_length, /* field [max] length */ + (uchar*) "", /* null ptr */ + 0, /* null bit */ + m_return_field_def.pack_flag, + m_return_field_def.sql_type, + m_return_field_def.charset, + m_return_field_def.geom_type, + Field::NONE, /* unreg check */ + m_return_field_def.interval, + field_name ? field_name : (const char *) m_name.str, + table); + DBUG_RETURN(field); } @@ -821,12 +774,14 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b) variables with NAME_CONST('sp_var_name', value) calls. RETURN - 0 Ok, thd->query{_length} either has been appropriately replaced or - there is no need for replacements. - 1 Out of memory error. + FALSE on success + thd->query{_length} either has been appropriately replaced or there + is no need for replacements. + TRUE out of memory error. */ -static bool subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) +static bool +subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) { DBUG_ENTER("subst_spvars"); if (thd->prelocked_mode == NON_PRELOCKED && mysql_bin_log.is_open()) @@ -836,7 +791,7 @@ static bool subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) String qbuf(buffer, sizeof(buffer), &my_charset_bin); int prev_pos, res; - /* Find all instances of item_splocal used in this statement */ + /* Find all instances of Item_splocal used in this statement */ for (Item *item= instr->free_list; item; item= item->next) { if (item->is_splocal()) @@ -847,7 +802,7 @@ static bool subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) } } if (!sp_vars_uses.elements()) - DBUG_RETURN(0); + DBUG_RETURN(FALSE); /* Sort SP var refs by their occurences in the query */ sp_vars_uses.sort(cmp_splocal_locations); @@ -863,7 +818,12 @@ static bool subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) splocal < sp_vars_uses.back(); splocal++) { Item *val; - (*splocal)->thd= thd; // fix_fields() is not yet done + + char str_buffer[STRING_BUFFER_USUAL_SIZE]; + String str_value_holder(str_buffer, sizeof(str_buffer), + &my_charset_latin1); + String *str_value; + /* append the text between sp ref occurences */ res|= qbuf.append(cur + prev_pos, (*splocal)->pos_in_query - prev_pos); prev_pos= (*splocal)->pos_in_query + (*splocal)->m_name.length; @@ -872,24 +832,33 @@ static bool subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) res|= qbuf.append(STRING_WITH_LEN(" NAME_CONST('")); res|= qbuf.append((*splocal)->m_name.str, (*splocal)->m_name.length); res|= qbuf.append(STRING_WITH_LEN("',")); + res|= (*splocal)->fix_fields(thd, (Item **) splocal); + + if (res) + break; + val= (*splocal)->this_item(); DBUG_PRINT("info", ("print %p", val)); - val->print(&qbuf); + str_value= sp_get_item_value(val, &str_value_holder); + if (str_value) + res|= qbuf.append(*str_value); + else + res|= qbuf.append(STRING_WITH_LEN("NULL")); res|= qbuf.append(')'); if (res) break; } res|= qbuf.append(cur + prev_pos, query_str->length - prev_pos); if (res) - DBUG_RETURN(1); + DBUG_RETURN(TRUE); if (!(pbuf= thd->strmake(qbuf.ptr(), qbuf.length()))) - DBUG_RETURN(1); + DBUG_RETURN(TRUE); thd->query= pbuf; thd->query_length= qbuf.length(); } - DBUG_RETURN(0); + DBUG_RETURN(FALSE); } @@ -923,17 +892,19 @@ void sp_head::recursion_level_error() Assume the parameters already set. RETURN - -1 on error + FALSE on success + TRUE on error */ -int sp_head::execute(THD *thd) +bool +sp_head::execute(THD *thd) { DBUG_ENTER("sp_head::execute"); char olddb[128]; bool dbchanged; sp_rcontext *ctx; - int ret= 0; + bool err_status= FALSE; uint ip= 0; ulong save_sql_mode; Query_arena *old_arena; @@ -949,9 +920,7 @@ int sp_head::execute(THD *thd) /* Use some extra margin for possible SP recursion and functions */ if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (char*)&old_packet)) - { - DBUG_RETURN(-1); - } + DBUG_RETURN(TRUE); /* init per-instruction memroot */ init_alloc_root(&execute_mem_root, MEM_ROOT_BLOCK_SIZE, 0); @@ -979,7 +948,8 @@ int sp_head::execute(THD *thd) dbchanged= FALSE; if (m_db.length && - (ret= sp_use_new_db(thd, m_db.str, olddb, sizeof(olddb), 0, &dbchanged))) + (err_status= sp_use_new_db(thd, m_db.str, olddb, sizeof(olddb), 0, + &dbchanged))) goto done; if ((ctx= thd->spcont)) @@ -1057,7 +1027,7 @@ int sp_head::execute(THD *thd) if (thd->prelocked_mode == NON_PRELOCKED) thd->user_var_events_alloc= thd->mem_root; - ret= i->execute(thd, &ip); + err_status= i->execute(thd, &ip); /* If this SP instruction have sent eof, it has caused no_send_error to be @@ -1085,11 +1055,10 @@ int sp_head::execute(THD *thd) /* Check if an exception has occurred and a handler has been found - Note: We havo to check even if ret==0, since warnings (and some - errors don't return a non-zero value. - We also have to check even if thd->killed != 0, since some - errors return with this even when a handler has been found - (e.g. "bad data"). + Note: We have to check even if err_status == FALSE, since warnings (and + some errors) don't return a non-zero value. We also have to check even + if thd->killed != 0, since some errors return with this even when a + handler has been found (e.g. "bad data"). */ if (ctx) { @@ -1100,13 +1069,12 @@ int sp_head::execute(THD *thd) break; case SP_HANDLER_CONTINUE: thd->restore_active_arena(&execute_arena, &backup_arena); - ctx->save_variables(hf); thd->set_n_backup_active_arena(&execute_arena, &backup_arena); ctx->push_hstack(ip); // Fall through default: ip= hip; - ret= 0; + err_status= FALSE; ctx->clear_handler(); ctx->enter_handler(hip); thd->clear_error(); @@ -1114,7 +1082,7 @@ int sp_head::execute(THD *thd) continue; } } - } while (ret == 0 && !thd->killed); + } while (!err_status && !thd->killed); thd->restore_active_arena(&execute_arena, &backup_arena); @@ -1135,11 +1103,11 @@ int sp_head::execute(THD *thd) state= EXECUTED; done: - DBUG_PRINT("info", ("ret=%d killed=%d query_error=%d", - ret, thd->killed, thd->query_error)); + DBUG_PRINT("info", ("err_status=%d killed=%d query_error=%d", + err_status, thd->killed, thd->query_error)); if (thd->killed) - ret= -1; + err_status= TRUE; /* If the DB has changed, the pointer has changed too, but the original thd->db will then have been freed */ if (dbchanged) @@ -1149,7 +1117,7 @@ int sp_head::execute(THD *thd) (It would generate an error from mysql_change_db() when olddb=="") */ if (! thd->killed) - ret|= (int) mysql_change_db(thd, olddb, 1); + err_status|= mysql_change_db(thd, olddb, 1); } m_flags&= ~IS_INVOKED; DBUG_PRINT("info", ("first free for 0x%lx --: 0x%lx->0x%lx, level: %lu, flags %x", @@ -1175,7 +1143,7 @@ int sp_head::execute(THD *thd) m_first_instance->m_first_free_instance->m_recursion_level == m_recursion_level + 1)); m_first_instance->m_first_free_instance= this; - DBUG_RETURN(ret); + DBUG_RETURN(err_status); } @@ -1187,33 +1155,41 @@ int sp_head::execute(THD *thd) SYNOPSIS sp_head::execute_function() - thd Thread handle - argp Passed arguments (these are items from containing statement?) - argcount Number of passed arguments. We need to check if this is - correct. - resp OUT Put result item here (q: is it a constant Item always?) + thd Thread handle + argp Passed arguments (these are items from containing + statement?) + argcount Number of passed arguments. We need to check if this is + correct. + return_value_fld Save result here. RETURN - 0 on OK - other on error + FALSE on success + TRUE on error */ -int -sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) +bool +sp_head::execute_function(THD *thd, Item **argp, uint argcount, + Field *return_value_fld) { - Item **param_values; + Item_cache **param_values; ulonglong binlog_save_options; bool need_binlog_call; - DBUG_ENTER("sp_head::execute_function"); - DBUG_PRINT("info", ("function %s", m_name.str)); - uint csize = m_pcont->max_pvars(); - uint params = m_pcont->current_pvars(); - uint hmax = m_pcont->max_handlers(); - uint cmax = m_pcont->max_cursors(); + uint params; sp_rcontext *octx = thd->spcont; sp_rcontext *nctx = NULL; - uint i; - int ret= -1; // Assume error + bool err_status= FALSE; + + DBUG_ENTER("sp_head::execute_function"); + DBUG_PRINT("info", ("function %s", m_name.str)); + + params = m_pcont->context_pvars(); + + /* + Check that the function is called with all specified arguments. + + If it is not, use my_error() to report an error, or it will not terminate + the invoking query properly. + */ if (argcount != params) { @@ -1223,37 +1199,56 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) */ my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "FUNCTION", m_qname.str, params, argcount); - goto end; + DBUG_RETURN(TRUE); } - if (!(param_values= (Item**)thd->alloc(sizeof(Item*)*argcount))) - DBUG_RETURN(-1); + /* Allocate param_values to be used for dumping the call into binlog. */ + + if (!(param_values= (Item_cache**)thd->alloc(sizeof(Item_cache*)*argcount))) + DBUG_RETURN(TRUE); // QQ Should have some error checking here? (types, etc...) - if (!(nctx= new sp_rcontext(octx, csize, hmax, cmax))) - goto end; + + if (!(nctx= new sp_rcontext(m_pcont, return_value_fld, octx)) || + nctx->init(thd)) + { + delete nctx; /* Delete nctx if it was init() that failed. */ + DBUG_RETURN(TRUE); + } + #ifndef DBUG_OFF - nctx->owner= this; + nctx->sp= this; #endif - for (i= 0 ; i < argcount ; i++) + + /* Pass arguments. */ + { - sp_pvar_t *pvar = m_pcont->find_pvar(i); - Item *it= sp_eval_func_item(thd, argp++, pvar->type, NULL, FALSE); - param_values[i]= it; + uint i; + + for (i= 0 ; i < argcount ; i++) + { + if (!argp[i]->fixed && argp[i]->fix_fields(thd, &argp[i])) + { + err_status= TRUE; + break; + } - if (!it) - goto end; // EOM error - nctx->push_item(it); - } + param_values[i]= Item_cache::get_cache(argp[i]->result_type()); + param_values[i]->store(argp[i]); + if (nctx->set_variable(thd, i, param_values[i])) + { + err_status= TRUE; + break; + } + } + } - /* - The rest of the frame are local variables which are all IN. - Push NULLs to get the right size (and make the reuse mechanism work) - - the will be initialized by set instructions in each frame. - */ - for (; i < csize ; i++) - nctx->push_item(NULL); + if (err_status) + { + delete nctx; + DBUG_RETURN(TRUE); + } thd->spcont= nctx; @@ -1266,7 +1261,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) } thd->options&= ~OPTION_BIN_LOG; - ret= execute(thd); + err_status= execute(thd); thd->options= binlog_save_options; if (need_binlog_call) @@ -1282,9 +1277,18 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) bufstr.append('('); for (uint i=0; i < argcount; i++) { + String str_value_holder; + String *str_value; + if (i) bufstr.append(','); - param_values[i]->print(&bufstr); + + str_value= sp_get_item_value(param_values[i], &str_value_holder); + + if (str_value) + bufstr.append(*str_value); + else + bufstr.append(STRING_WITH_LEN("NULL")); } bufstr.append(')'); @@ -1300,26 +1304,22 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) reset_dynamic(&thd->user_var_events); } - if (m_type == TYPE_ENUM_FUNCTION && ret == 0) + if (m_type == TYPE_ENUM_FUNCTION && !err_status) { /* We need result only in function but not in trigger */ - Item *it= nctx->get_result(); - if (it) - *resp= sp_eval_func_item(thd, &it, m_returns, NULL, FALSE); - else + if (!nctx->is_return_value_set()) { my_error(ER_SP_NORETURNEND, MYF(0), m_name.str); - ret= -1; + err_status= TRUE; } } nctx->pop_all_cursors(); // To avoid memory leaks after an error - delete nctx; // Doesn't do anything + delete nctx; thd->spcont= octx; -end: - DBUG_RETURN(ret); + DBUG_RETURN(err_status); } @@ -1351,17 +1351,15 @@ static Item_func_get_user_var *item_is_user_var(Item *it) - copy back values of INOUT and OUT parameters RETURN - 0 Ok - -1 Error + FALSE on success + TRUE on error */ -int sp_head::execute_procedure(THD *thd, List<Item> *args) +bool +sp_head::execute_procedure(THD *thd, List<Item> *args) { - int ret= 0; - uint csize = m_pcont->max_pvars(); - uint params = m_pcont->current_pvars(); - uint hmax = m_pcont->max_handlers(); - uint cmax = m_pcont->max_cursors(); + bool err_status= FALSE; + uint params = m_pcont->context_pvars(); sp_rcontext *save_spcont, *octx; sp_rcontext *nctx = NULL; DBUG_ENTER("sp_head::execute_procedure"); @@ -1371,16 +1369,21 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args) { my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "PROCEDURE", m_qname.str, params, args->elements); - DBUG_RETURN(-1); + DBUG_RETURN(TRUE); } save_spcont= octx= thd->spcont; if (! octx) { // Create a temporary old context - if (!(octx= new sp_rcontext(octx, csize, hmax, cmax))) - DBUG_RETURN(-1); + 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); + } + #ifndef DBUG_OFF - octx->owner= 0; + octx->sp= 0; #endif thd->spcont= octx; @@ -1388,63 +1391,62 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args) thd->spcont->callers_arena= thd; } - if (!(nctx= new sp_rcontext(octx, csize, hmax, cmax))) + if (!(nctx= new sp_rcontext(m_pcont, NULL, octx)) || + nctx->init(thd)) { + delete nctx; /* Delete nctx if it was init() that failed. */ thd->spcont= save_spcont; - DBUG_RETURN(-1); + DBUG_RETURN(TRUE); } #ifndef DBUG_OFF - nctx->owner= this; + nctx->sp= this; #endif - if (csize > 0 || hmax > 0 || cmax > 0) + if (params > 0) { - Item_null *nit= NULL; // Re-use this, and only create if needed - uint i; - List_iterator<Item> li(*args); - Item *it; + List_iterator<Item> it_args(*args); - /* Evaluate SP arguments (i.e. get the values passed as parameters) */ - // QQ: Should do type checking? DBUG_PRINT("info",(" %.*s: eval args", m_name.length, m_name.str)); - for (i = 0 ; (it= li++) && i < params ; i++) + + for (uint i= 0 ; i < params ; i++) { + Item *arg_item= it_args++; sp_pvar_t *pvar= m_pcont->find_pvar(i); - if (pvar) + if (!arg_item) + break; + + if (!pvar) + continue; + + if (pvar->mode != sp_param_in) { - if (pvar->mode != sp_param_in) - { - if (!it->is_splocal() && !item_is_user_var(it)) - { - my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, m_qname.str); - ret= -1; - break; - } - } - if (pvar->mode == sp_param_out) - { - if (! nit) - { - if (!(nit= new Item_null())) - { - ret= -1; - break; - } - } - nctx->push_item(nit); // OUT - } - else - { - Item *it2= sp_eval_func_item(thd, li.ref(), pvar->type, NULL, FALSE); - - if (!it2) - { - ret= -1; // Eval failed - break; - } - nctx->push_item(it2); // IN or INOUT - } + if (!arg_item->is_splocal() && !item_is_user_var(arg_item)) + { + my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, m_qname.str); + err_status= TRUE; + break; + } + } + + if (pvar->mode == sp_param_out) + { + Item_null *null_item= new Item_null(); + + if (!null_item || + nctx->set_variable(thd, i, null_item)) + { + err_status= TRUE; + break; + } + } + else + { + if (nctx->set_variable(thd, i, *it_args.ref())) + { + err_status= TRUE; + break; + } } } @@ -1457,20 +1459,12 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args) close_thread_tables(thd, 0, 0); DBUG_PRINT("info",(" %.*s: eval args done", m_name.length, m_name.str)); - - /* - The rest of the frame are local variables which are all IN. - Push NULLs to get the right size (and make the reuse mechanism work) - - the will be initialized by set instructions in each frame. - */ - for (; i < csize ; i++) - nctx->push_item(NULL); } thd->spcont= nctx; - if (! ret) - ret= execute(thd); + if (!err_status) + err_status= execute(thd); /* In the case when we weren't able to employ reuse mechanism for @@ -1480,74 +1474,67 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args) */ thd->spcont->callers_arena= octx->callers_arena; - if (!ret && csize > 0) + if (!err_status && params > 0) { - List_iterator<Item> li(*args); - Item *it; + List_iterator<Item> it_args(*args); /* Copy back all OUT or INOUT values to the previous frame, or set global user variables */ - for (uint i = 0 ; (it= li++) && i < params ; i++) + for (uint i= 0 ; i < params ; i++) { + Item *arg_item= it_args++; + + if (!arg_item) + break; + sp_pvar_t *pvar= m_pcont->find_pvar(i); - if (pvar->mode != sp_param_in) + if (pvar->mode == sp_param_in) + continue; + + if (arg_item->is_splocal()) { - if (it->is_splocal()) - { - // Have to copy the item to the caller's mem_root - Item *copy; - uint offset= static_cast<Item_splocal *>(it)->get_offset(); - Item *val= nctx->get_item(i); - Item *orig= octx->get_item(offset); - - /* - We might need to allocate new item if we weren't able to - employ reuse mechanism. Then we should do it on the callers arena. - */ - copy= sp_eval_func_item(thd, &val, pvar->type, orig, TRUE); // Copy - - if (!copy) - { - ret= -1; - break; - } - if (copy != orig) - octx->set_item(offset, copy); - } - else + if (octx->set_variable(thd, + ((Item_splocal*) arg_item)->get_var_idx(), + nctx->get_item(i))) + { + err_status= TRUE; + break; + } + } + else + { + Item_func_get_user_var *guv= item_is_user_var(arg_item); + + if (guv) { - Item_func_get_user_var *guv= item_is_user_var(it); - - if (guv) - { - Item *item= nctx->get_item(i); - Item_func_set_user_var *suv; - - suv= new Item_func_set_user_var(guv->get_name(), item); - /* - Item_func_set_user_var is not fixed after construction, - call fix_fields(). - */ - if ((ret= test(!suv || suv->fix_fields(thd, &item) || - suv->check() || suv->update()))) - break; - } + Item *item= nctx->get_item(i); + Item_func_set_user_var *suv; + + suv= new Item_func_set_user_var(guv->get_name(), item); + /* + Item_func_set_user_var is not fixed after construction, + call fix_fields(). + */ + if ((ret= test(!suv || suv->fix_fields(thd, &item) || + suv->check() || suv->update()))) + break; } } + } } if (!save_spcont) - delete octx; // Does nothing + delete octx; nctx->pop_all_cursors(); // To avoid memory leaks after an error - delete nctx; // Does nothing + delete nctx; thd->spcont= save_spcont; - DBUG_RETURN(ret); + DBUG_RETURN(err_status); } @@ -1583,6 +1570,15 @@ sp_head::reset_lex(THD *thd) sublex->trg_chistics= oldlex->trg_chistics; sublex->trg_table_fields.empty(); sublex->sp_lex_in_use= FALSE; + + /* Reset type info. */ + + sublex->charset= NULL; + sublex->length= NULL; + sublex->dec= NULL; + sublex->interval_list.empty(); + sublex->type= 0; + DBUG_VOID_RETURN; } @@ -1677,6 +1673,55 @@ sp_head::check_backpatch(THD *thd) return 0; } + +/* + Prepare an instance of create_field for field creation (fill all necessary + attributes). + + SYNOPSIS + sp_head::fill_field_definition() + thd [IN] Thread handle + lex [IN] Yacc parsing context + field_type [IN] Field type + field_def [OUT] An instance of create_field to be filled + + RETURN + FALSE on success + TRUE on error +*/ + +bool +sp_head::fill_field_definition(THD *thd, LEX *lex, + enum enum_field_types field_type, + create_field *field_def) +{ + LEX_STRING cmt = { 0, 0 }; + uint unused1= 0; + int unused2= 0; + + if (field_def->init(thd, (char*) "", field_type, lex->length, lex->dec, + lex->type, (Item*) 0, (Item*) 0, &cmt, 0, + &lex->interval_list, + (lex->charset ? lex->charset : default_charset_info), + lex->uint_geom_type)) + return TRUE; + + if (field_def->interval_list.elements) + field_def->interval= create_typelib(mem_root, field_def, + &field_def->interval_list); + + sp_prepare_create_field(thd, field_def); + + if (prepare_create_field(field_def, &unused1, &unused2, &unused2, + HA_CAN_GEOMETRY)) + { + return TRUE; + } + + return FALSE; +} + + void sp_head::set_info(longlong created, longlong modified, st_sp_chistics *chistics, ulong sql_mode) @@ -2219,28 +2264,28 @@ sp_instr_set::execute(THD *thd, uint *nextp) int sp_instr_set::exec_core(THD *thd, uint *nextp) { - int res= thd->spcont->set_item_eval(thd, m_offset, &m_value, m_type); + int res= thd->spcont->set_variable(thd, m_offset, m_value); - if (res < 0 && - thd->spcont->get_item(m_offset) == NULL && - thd->spcont->found_handler_here()) + if (res && thd->spcont->found_handler_here()) { /* - Failed to evaluate the value, the variable is still not initialized, - and a handler has been found. Set to null so we can continue. + Failed to evaluate the value, and a handler has been found. Reset the + variable to NULL. */ - Item *it= new Item_null(); - if (!it || thd->spcont->set_item_eval(thd, m_offset, &it, m_type) < 0) - { /* If this also failed, we have to abort */ - sp_rcontext *spcont= thd->spcont; + if (thd->spcont->set_variable(thd, m_offset, 0)) + { + /* If this also failed, let's abort. */ + sp_rcontext *spcont= thd->spcont; + thd->spcont= 0; /* Avoid handlers */ my_error(ER_OUT_OF_RESOURCES, MYF(0)); spcont->clear_handler(); thd->spcont= spcont; } } + *nextp = m_ip+1; return res; } @@ -2509,20 +2554,22 @@ sp_instr_freturn::execute(THD *thd, uint *nextp) int sp_instr_freturn::exec_core(THD *thd, uint *nextp) { - Item *it; - int res; + /* + Change <next instruction pointer>, so that this will be the last + instruction in the stored function. + */ - it= sp_eval_func_item(thd, &m_value, m_type, NULL, TRUE); - if (! it) - res= -1; - else - { - res= 0; - thd->spcont->set_result(it); - } *nextp= UINT_MAX; - return res; + /* + Evaluate the value of return expression and store it in current runtime + context. + + NOTE: It's necessary to evaluate result item right here, because we must + do it in scope of execution the current context/block. + */ + + return thd->spcont->set_return_value(thd, m_value); } void @@ -2643,7 +2690,6 @@ sp_instr_hreturn::execute(THD *thd, uint *nextp) *nextp= m_dest; else { - thd->spcont->restore_variables(m_frame); *nextp= thd->spcont->pop_hstack(); } thd->spcont->exit_handler(); @@ -2961,6 +3007,65 @@ sp_instr_error::print(String *str) } +/************************************************************************** + sp_instr_set_case_expr class implementation +**************************************************************************/ + +int +sp_instr_set_case_expr::execute(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_set_case_expr::execute"); + + DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this)); +} + + +int +sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp) +{ + int res= thd->spcont->set_case_expr(thd, m_case_expr_id, m_case_expr); + + if (res && + !thd->spcont->get_case_expr(m_case_expr_id) && + thd->spcont->found_handler_here()) + { + /* + Failed to evaluate the value, the case expression is still not + initialized, and a handler has been found. Set to NULL so we can continue. + */ + + Item *null_item= new Item_null(); + + if (!null_item || + thd->spcont->set_case_expr(thd, m_case_expr_id, null_item)) + { + /* If this also failed, we have to abort. */ + + sp_rcontext *spcont= thd->spcont; + + thd->spcont= 0; /* Avoid handlers */ + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + spcont->clear_handler(); + thd->spcont= spcont; + } + } + + *nextp = m_ip+1; + + return res; /* no error */ +} + + +void +sp_instr_set_case_expr::print(String *str) +{ + str->append(STRING_WITH_LEN("set_case_expr ")); + str->qs_append(m_case_expr_id); + str->append(' '); + m_case_expr->print(str); +} + + /* ------------------------------------------------------------------ */ /* |