diff options
author | unknown <anozdrin@mysql.com> | 2005-12-07 17:01:17 +0300 |
---|---|---|
committer | unknown <anozdrin@mysql.com> | 2005-12-07 17:01:17 +0300 |
commit | 6b2f13098a59d9ad520828dca4cb63da8c86b5e3 (patch) | |
tree | 82814a574624a8e813cc8dfad8c31d3fa4adf898 /sql/sp_rcontext.cc | |
parent | 29eac312cda3bebddeb7f5ab7a2af6b81ea89205 (diff) | |
download | mariadb-git-6b2f13098a59d9ad520828dca4cb63da8c86b5e3.tar.gz |
Patch for WL#2894: Make stored routine variables work
according to the standard.
The idea is to use Field-classes to implement stored routines
variables. Also, we should provide facade to Item-hierarchy
by Item_field class (it is necessary, since SRVs take part
in expressions).
The patch fixes the following bugs:
- BUG#8702: Stored Procedures: No Error/Warning shown for inappropriate data
type matching;
- BUG#8768: Functions: For any unsigned data type, -ve values can be passed
and returned;
- BUG#8769: Functions: For Int datatypes, out of range values can be passed
and returned;
- BUG#9078: STORED PROCDURE: Decimal digits are not displayed when we use
DECIMAL datatype;
- BUG#9572: Stored procedures: variable type declarations ignored;
- BUG#12903: upper function does not work inside a function;
- BUG#13705: parameters to stored procedures are not verified;
- BUG#13808: ENUM type stored procedure parameter accepts non-enumerated
data;
- BUG#13909: Varchar Stored Procedure Parameter always BINARY string (ignores
CHARACTER SET);
- BUG#14161: Stored procedure cannot retrieve bigint unsigned;
- BUG#14188: BINARY variables have no 0x00 padding;
- BUG#15148: Stored procedure variables accept non-scalar values;
mysql-test/r/ctype_ujis.result:
Explicitly specify correct charset.
mysql-test/r/schema.result:
Drop our test database to not affect this test if some test
left it cause of failure.
mysql-test/r/show_check.result:
Drop our test database to not affect this test if some test
left it cause of failure.
mysql-test/r/skip_name_resolve.result:
Ignore columns with unpredictable values.
mysql-test/r/sp-big.result:
Add cleanup statement.
mysql-test/r/sp-dynamic.result:
Add cleanup statements.
mysql-test/r/sp.result:
Update result file.
mysql-test/r/sum_distinct-big.result:
Update result file.
mysql-test/r/type_newdecimal-big.result:
Update result file.
mysql-test/t/ctype_ujis.test:
Explicitly specify correct charset.
mysql-test/t/schema.test:
Drop our test database to not affect this test if some test
left it cause of failure.
mysql-test/t/show_check.test:
Drop our test database to not affect this test if some test
left it cause of failure.
mysql-test/t/skip_name_resolve.test:
Ignore columns with unpredictable values.
mysql-test/t/sp-big.test:
Add cleanup statement.
mysql-test/t/sp-dynamic.test:
Add cleanup statements.
mysql-test/t/sp.test:
Non-scalar values prohibited for assignment to SP-vars;
polishing.
mysql-test/t/type_newdecimal-big.test:
Update type specification so that the variables
can contain the large values used in the test.
sql/field.cc:
Extract create_field::init() to initialize an existing
instance of create_field from new_create_field().
sql/field.h:
Extract create_field::init() to initialize an existing
instance of create_field from new_create_field().
sql/item.cc:
- Introduce a new class: Item_sp_variable -- a base class
of stored-routine-variables classes;
- Introduce Item_case_expr -- an Item, which is used to access
to the expression of CASE statement;
sql/item.h:
- Introduce a new class: Item_sp_variable -- a base class
of stored-routine-variables classes;
- Introduce Item_case_expr -- an Item, which is used to access
to the expression of CASE statement;
sql/item_func.cc:
Pass the Field (instead of Item) for the return value of
a function to the function execution routine.
sql/item_func.h:
Pass the Field (instead of Item) for the return value of
a function to the function execution routine.
sql/mysql_priv.h:
Move create_virtual_tmp_table() out of sql_select.h.
sql/sp.cc:
Use create_result_field() instead of make_field().
sql/sp_head.cc:
- Add a function to map enum_field_types to Item::Type;
- Add sp_instr_push_case_expr instruction -- an instruction
to push CASE expression into the active running context;
- Add sp_instr_pop_case_expr instruction -- an instruction
to pop CASE expression from the active running context;
- Adapt the SP-execution code to using Fields instead of Items
for SP-vars;
- Use create_field structure for field description instead of
a set of members.
sql/sp_head.h:
- Add a function to map enum_field_types to Item::Type;
- Add sp_instr_push_case_expr instruction -- an instruction
to push CASE expression into the active running context;
- Add sp_instr_pop_case_expr instruction -- an instruction
to pop CASE expression from the active running context;
- Adapt the SP-execution code to using Fields instead of Items
for SP-vars;
- Use create_field structure for field description instead of
a set of members.
sql/sp_pcontext.cc:
- Change rules to assign an index of SP-variable: use
transparent index;
- Add an operation to retrieve a list of defined SP-vars
from the processing context recursively.
sql/sp_pcontext.h:
- Change rules to assign an index of SP-variable: use
transparent index;
- Add an operation to retrieve a list of defined SP-vars
from the processing context recursively.
sql/sp_rcontext.cc:
- Change rules to assign an index of SP-variable: use
transparent index;
- Use a tmp virtual table to store SP-vars instead of Items;
- Provide operations to work with CASE expresion.
sql/sp_rcontext.h:
- Change rules to assign an index of SP-variable: use
transparent index;
- Use a tmp virtual table to store SP-vars instead of Items;
- Provide operations to work with CASE expresion.
sql/sql_class.cc:
- Reflect Item_splocal ctor changes;
- Item_splocal::get_offset() has been renamed to get_var_idx().
sql/sql_class.h:
Polishing.
sql/sql_parse.cc:
Extract create_field::init() to initialize an existing
instance of create_field from new_create_field().
sql/sql_select.cc:
Take care of BLOB columns in create_virtual_tmp_table().
sql/sql_select.h:
Move create_virtual_tmp_table() out of sql_select.h.
sql/sql_trigger.cc:
Use boolean constants for boolean type instead of numerical ones.
sql/sql_yacc.yy:
Provide an instance of create_field for each SP-var.
mysql-test/include/sp-vars.inc:
The definitions of common-procedures, which are created
under different circumstances.
mysql-test/r/sp-vars.result:
Result file for the SP-vars test.
mysql-test/sp-vars.test:
A new test for checking SP-vars functionality.
Diffstat (limited to 'sql/sp_rcontext.cc')
-rw-r--r-- | sql/sp_rcontext.cc | 306 |
1 files changed, 256 insertions, 50 deletions
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index ccb38358049..eca87e69f8e 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -29,41 +29,137 @@ #include "sp_rcontext.h" #include "sp_pcontext.h" -sp_rcontext::sp_rcontext(sp_rcontext *prev, uint fsize, uint hmax, uint cmax) - : m_count(0), m_fsize(fsize), m_result(NULL), m_hcount(0), m_hsp(0), - m_ihsp(0), m_hfound(-1), m_ccount(0), m_prev_ctx(prev) + +sp_rcontext::sp_rcontext(sp_pcontext *root_parsing_ctx, + Field *return_value_fld, + sp_rcontext *prev_runtime_ctx) + :m_root_parsing_ctx(root_parsing_ctx), + m_var_table(0), + m_var_items(0), + m_return_value_fld(return_value_fld), + m_return_value_set(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_frame= (Item **)sql_alloc(fsize * sizeof(Item*)); - m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t)); - m_hstack= (uint *)sql_alloc(hmax * sizeof(uint)); - m_in_handler= (uint *)sql_alloc(hmax * sizeof(uint)); - m_cstack= (sp_cursor **)sql_alloc(cmax * sizeof(sp_cursor *)); - m_saved.empty(); } -int -sp_rcontext::set_item_eval(THD *thd, uint idx, Item **item_addr, - enum_field_types type) +sp_rcontext::~sp_rcontext() +{ + if (m_var_table) + free_blobs(m_var_table); +} + + +/* + Initialize sp_rcontext instance. + + SYNOPSIS + thd Thread handle + RETURN + FALSE on success + TRUE on error +*/ + +bool sp_rcontext::init(THD *thd) +{ + if (init_var_table(thd) || init_var_items()) + return TRUE; + + return + !(m_handler= + (sp_handler_t*)thd->alloc(m_root_parsing_ctx->max_handlers() * + sizeof(sp_handler_t))) || + !(m_hstack= + (uint*)thd->alloc(m_root_parsing_ctx->max_handlers() * + sizeof(uint))) || + !(m_in_handler= + (uint*)thd->alloc(m_root_parsing_ctx->max_handlers() * + sizeof(uint))) || + !(m_cstack= + (sp_cursor**)thd->alloc(m_root_parsing_ctx->max_cursors() * + sizeof(sp_cursor*))) || + !(m_case_expr_holders= + (Item_cache**)thd->calloc(m_root_parsing_ctx->get_num_case_exprs() * + sizeof (Item_cache*))); +} + + +/* + Create and initialize a table to store SP-vars. + + SYNOPSIS + thd Thread handler. + RETURN + FALSE on success + TRUE on error +*/ + +bool +sp_rcontext::init_var_table(THD *thd) { - Item *it; - Item *reuse_it; - /* sp_eval_func_item will use callers_arena */ - int res; - - reuse_it= get_item(idx); - it= sp_eval_func_item(thd, item_addr, type, reuse_it, TRUE); - if (! it) - res= -1; - else + List<create_field> field_def_lst; + + if (!m_root_parsing_ctx->total_pvars()) + return FALSE; + + m_root_parsing_ctx->retrieve_field_definitions(&field_def_lst); + + DBUG_ASSERT(field_def_lst.elements == m_root_parsing_ctx->total_pvars()); + + if (!(m_var_table= create_virtual_tmp_table(thd, field_def_lst))) + return TRUE; + + m_var_table->copy_blobs= TRUE; + m_var_table->alias= ""; + + 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() +{ + uint idx; + uint num_vars= m_root_parsing_ctx->total_pvars(); + + if (!(m_var_items= (Item**) sql_alloc(num_vars * sizeof (Item *)))) + return TRUE; + + for (idx = 0; idx < num_vars; ++idx) { - res= 0; - set_item(idx, it); + if (!(m_var_items[idx]= new Item_field(m_var_table->field[idx]))) + return TRUE; } - return res; + return FALSE; } + +bool +sp_rcontext::set_return_value(THD *thd, Item *return_value_item) +{ + DBUG_ASSERT(m_return_value_fld); + + m_return_value_set = TRUE; + + return sp_eval_expr(thd, m_return_value_fld, return_value_item); +} + + bool sp_rcontext::find_handler(uint sql_errno, MYSQL_ERROR::enum_warning_level level) @@ -117,32 +213,14 @@ sp_rcontext::find_handler(uint sql_errno, } if (found < 0) { - if (m_prev_ctx) - return m_prev_ctx->find_handler(sql_errno, level); + if (m_prev_runtime_ctx) + return m_prev_runtime_ctx->find_handler(sql_errno, level); return FALSE; } m_hfound= found; return TRUE; } -void -sp_rcontext::save_variables(uint fp) -{ - while (fp < m_count) - { - m_saved.push_front(m_frame[fp]); - m_frame[fp++]= NULL; // Prevent reuse - } -} - -void -sp_rcontext::restore_variables(uint fp) -{ - uint i= m_count; - - while (i-- > fp) - m_frame[i]= m_saved.pop(); -} void sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i) @@ -150,6 +228,7 @@ sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i) m_cstack[m_ccount++]= new sp_cursor(lex_keeper, i); } + void sp_rcontext::pop_cursors(uint count) { @@ -160,6 +239,40 @@ sp_rcontext::pop_cursors(uint count) } +int +sp_rcontext::set_variable(THD *thd, uint var_idx, Item *value) +{ + return set_variable(thd, m_var_table->field[var_idx], value); +} + + +int +sp_rcontext::set_variable(THD *thd, Field *field, Item *value) +{ + if (!value) + { + field->set_null(); + return 0; + } + + return sp_eval_expr(thd, field, value); +} + + +Item * +sp_rcontext::get_item(uint var_idx) +{ + return m_var_items[var_idx]; +} + + +Item ** +sp_rcontext::get_item_addr(uint var_idx) +{ + return m_var_items + var_idx; +} + + /* * * sp_cursor @@ -263,6 +376,102 @@ sp_cursor::fetch(THD *thd, List<struct sp_pvar> *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, Item_result result_type) +{ + Item_cache *holder; + Query_arena current_arena; + + thd->set_n_backup_active_arena(thd->spcont->callers_arena, ¤t_arena); + + holder= Item_cache::get_cache(result_type); + + 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 + + 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) +{ + if (!(case_expr_item= sp_prepare_func_item(thd, &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->result_type()); + } + + m_case_expr_holders[case_expr_id]->store(case_expr_item); + + 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 ****************************************************************************/ @@ -294,11 +503,8 @@ bool Select_fetch_into_spvars::send_data(List<Item> &items) */ for (; pv= pv_iter++, item= item_iter++; ) { - Item *reuse= thd->spcont->get_item(pv->offset); - /* Evaluate a new item on the arena of the calling instruction */ - Item *it= sp_eval_func_item(thd, &item, pv->type, reuse, TRUE); - - thd->spcont->set_item(pv->offset, it); + if (thd->spcont->set_variable(thd, pv->offset, item)) + return TRUE; } return FALSE; } |