summaryrefslogtreecommitdiff
path: root/sql/sp_head.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sp_head.cc')
-rw-r--r--sql/sp_head.cc251
1 files changed, 105 insertions, 146 deletions
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 29cee6da4d3..272456d8c8e 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -117,7 +117,7 @@ sp_prepare_func_item(THD* thd, Item **it_addr)
DBUG_ENTER("sp_prepare_func_item");
it_addr= it->this_item_addr(thd, it_addr);
- if (!it->fixed && (*it_addr)->fix_fields(thd, 0, it_addr))
+ if (!it->fixed && (*it_addr)->fix_fields(thd, it_addr))
{
DBUG_PRINT("info", ("fix_fields() failed"));
DBUG_RETURN(NULL);
@@ -242,8 +242,11 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type,
void
sp_name::init_qname(THD *thd)
{
- m_qname.length= m_db.length+m_name.length+1;
- m_qname.str= thd->alloc(m_qname.length+1);
+ m_sroutines_key.length= m_db.length + m_name.length + 2;
+ if (!(m_sroutines_key.str= thd->alloc(m_sroutines_key.length + 1)))
+ return;
+ m_qname.length= m_sroutines_key.length - 1;
+ m_qname.str= m_sroutines_key.str + 1;
sprintf(m_qname.str, "%*s.%*s",
m_db.length, (m_db.length ? m_db.str : ""),
m_name.length, m_name.str);
@@ -312,19 +315,17 @@ sp_head::operator delete(void *ptr, size_t size)
sp_head::sp_head()
:Query_arena(&main_mem_root, INITIALIZED_FOR_SP),
m_returns_cs(NULL), m_has_return(FALSE),
- m_simple_case(FALSE), m_multi_results(FALSE), m_in_handler(FALSE)
+ m_simple_case(FALSE), m_multi_results(FALSE), m_in_handler(FALSE),
+ m_is_invoked(FALSE)
{
extern byte *
sp_table_key(const byte *ptr, uint *plen, my_bool first);
- extern byte
- *sp_lex_sp_key(const byte *ptr, uint *plen, my_bool first);
DBUG_ENTER("sp_head::sp_head");
m_backpatch.empty();
m_lex.empty();
hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
- hash_init(&m_spfuns, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
- hash_init(&m_spprocs, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
+ hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0);
DBUG_VOID_RETURN;
}
@@ -509,7 +510,7 @@ sp_head::destroy()
delete i;
delete_dynamic(&m_instr);
m_pcont->destroy();
- free_items(free_list);
+ free_items();
/*
If we have non-empty LEX stack then we just came out of parser with
@@ -527,8 +528,7 @@ sp_head::destroy()
}
hash_free(&m_sptabs);
- hash_free(&m_spfuns);
- hash_free(&m_spprocs);
+ hash_free(&m_sroutines);
DBUG_VOID_RETURN;
}
@@ -587,6 +587,28 @@ sp_head::execute(THD *thd)
DBUG_RETURN(-1);
}
+ if (m_is_invoked)
+ {
+ /*
+ We have to disable recursion for stored routines since in
+ many cases LEX structure and many Item's can't be used in
+ reentrant way now.
+
+ TODO: We can circumvent this problem by using separate
+ sp_head instances for each recursive invocation.
+
+ NOTE: Theoretically arguments of procedure can be evaluated
+ before its invocation so there should be no problem with
+ recursion. But since we perform cleanup for CALL statement
+ as for any other statement only after its execution, its LEX
+ structure is not reusable for recursive calls. Thus we have
+ to prohibit recursion for stored procedures too.
+ */
+ my_error(ER_SP_NO_RECURSION, MYF(0));
+ DBUG_RETURN(-1);
+ }
+ m_is_invoked= TRUE;
+
dbchanged= FALSE;
if (m_db.length &&
(ret= sp_use_new_db(thd, m_db.str, olddb, sizeof(olddb), 0, &dbchanged)))
@@ -596,7 +618,6 @@ sp_head::execute(THD *thd)
ctx->clear_handler();
thd->query_error= 0;
old_arena= thd->current_arena;
- thd->current_arena= this;
/*
We have to save/restore this info when we are changing call level to
@@ -636,23 +657,24 @@ sp_head::execute(THD *thd)
break;
DBUG_PRINT("execute", ("Instruction %u", ip));
thd->set_time(); // Make current_time() et al work
- {
- /*
- We have to substitute free_list of executing statement to
- current_arena to store there all new items created during execution
- (for example '*' expanding, or items made during permanent subquery
- transformation)
- Note: Every statement have to have all its items listed in free_list
- for correct cleaning them up
- */
- Item *save_free_list= thd->current_arena->free_list;
- thd->current_arena->free_list= i->free_list;
- ret= i->execute(thd, &ip);
- i->free_list= thd->current_arena->free_list;
- thd->current_arena->free_list= save_free_list;
- }
+ /*
+ We have to set thd->current_arena before executing the instruction
+ to store in the instruction free_list all new items, created
+ during the first execution (for example expanding of '*' or the
+ items made during other permanent subquery transformations).
+ */
+ thd->current_arena= i;
+ ret= i->execute(thd, &ip);
+ /*
+ If this SP instruction have sent eof, it has caused no_send_error to be
+ set. Clear it back to allow the next instruction to send error. (multi-
+ statement execution code clears no_send_error between statements too)
+ */
+ thd->net.no_send_error= 0;
if (i->free_list)
cleanup_items(i->free_list);
+ i->state= Query_arena::EXECUTED;
+
// 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.
@@ -694,7 +716,6 @@ sp_head::execute(THD *thd)
DBUG_ASSERT(!thd->derived_tables);
thd->derived_tables= old_derived_tables;
- cleanup_items(thd->current_arena->free_list);
thd->current_arena= old_arena;
state= EXECUTED;
@@ -711,6 +732,7 @@ sp_head::execute(THD *thd)
if (! thd->killed)
ret= sp_change_db(thd, olddb, 0);
}
+ m_is_invoked= FALSE;
DBUG_RETURN(ret);
}
@@ -728,8 +750,8 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
sp_rcontext *nctx = NULL;
uint i;
int ret;
- MEM_ROOT *old_mem_root, call_mem_root;
- Item *old_free_list, *call_free_list;
+ MEM_ROOT call_mem_root;
+ Query_arena call_arena(&call_mem_root, INITIALIZED_FOR_SP), backup_arena;
if (argcount != params)
{
@@ -741,14 +763,11 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
}
init_alloc_root(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
- old_mem_root= thd->mem_root;
- thd->mem_root= &call_mem_root;
- old_free_list= thd->free_list; // Keep the old list
- thd->free_list= NULL; // Start a new one
+
// QQ Should have some error checking here? (types, etc...)
nctx= new sp_rcontext(csize, hmax, cmax);
- nctx->callers_mem_root= old_mem_root;
+ nctx->callers_mem_root= thd->mem_root;
for (i= 0 ; i < argcount ; i++)
{
sp_pvar_t *pvar = m_pcont->find_pvar(i);
@@ -774,15 +793,16 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
}
}
thd->spcont= nctx;
+ thd->set_n_backup_item_arena(&call_arena, &backup_arena);
+ /* mem_root was moved to backup_arena */
+ DBUG_ASSERT(nctx->callers_mem_root == backup_arena.mem_root);
ret= execute(thd);
// Partially restore context now.
// We still need the call mem root and free list for processing
// of the result.
- call_free_list= thd->free_list;
- thd->free_list= old_free_list;
- thd->mem_root= old_mem_root;
+ thd->restore_backup_item_arena(&call_arena, &backup_arena);
if (m_type == TYPE_ENUM_FUNCTION && ret == 0)
{
@@ -802,8 +822,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
thd->spcont= octx;
// Now get rid of the rest of the callee context
- cleanup_items(call_free_list);
- free_items(call_free_list);
+ call_arena.free_items();
free_root(&call_mem_root, MYF(0));
DBUG_RETURN(ret);
@@ -835,8 +854,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
sp_rcontext *octx = thd->spcont;
sp_rcontext *nctx = NULL;
my_bool tmp_octx = FALSE; // True if we have allocated a temporary octx
- MEM_ROOT *old_mem_root, call_mem_root;
- Item *old_free_list, *call_free_list;
+ MEM_ROOT call_mem_root;
+ Query_arena call_arena(&call_mem_root, INITIALIZED_FOR_SP), backup_arena;
if (args->elements != params)
{
@@ -846,10 +865,6 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
}
init_alloc_root(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
- old_mem_root= thd->mem_root;
- thd->mem_root= &call_mem_root;
- old_free_list= thd->free_list; // Keep the old list
- thd->free_list= NULL; // Start a new one
if (csize > 0 || hmax > 0 || cmax > 0)
{
@@ -914,14 +929,11 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
}
if (! ret)
+ {
+ thd->set_n_backup_item_arena(&call_arena, &backup_arena);
ret= execute(thd);
-
- // Partially restore context now.
- // We still need the call mem root and free list for processing
- // of out parameters.
- call_free_list= thd->free_list;
- thd->free_list= old_free_list;
- thd->mem_root= old_mem_root;
+ thd->restore_backup_item_arena(&call_arena, &backup_arena);
+ }
if (!ret && csize > 0)
{
@@ -979,7 +991,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
we do not check suv->fixed, because it can't be fixed after
creation
*/
- suv->fix_fields(thd, NULL, &item);
+ suv->fix_fields(thd, &item);
suv->fix_length_and_dec();
suv->check();
suv->update();
@@ -996,8 +1008,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
thd->spcont= octx;
// Now get rid of the rest of the callee context
- cleanup_items(call_free_list);
- free_items(call_free_list);
+ call_arena.free_items();
thd->lex->unit.cleanup();
free_root(&call_mem_root, MYF(0));
@@ -1052,11 +1063,10 @@ sp_head::restore_lex(THD *thd)
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
/*
- Add routines which are used by statement to respective sets for
- this routine
+ Add routines which are used by statement to respective set for
+ this routine.
*/
- sp_merge_hash(&m_spfuns, &sublex->spfuns);
- sp_merge_hash(&m_spprocs, &sublex->spprocs);
+ sp_update_sp_used_routines(&m_sroutines, &sublex->sroutines);
/*
Merge tables used by this statement (but not by its functions or
procedures) to multiset of tables used by this routine.
@@ -1291,6 +1301,13 @@ void sp_head::add_instr(sp_instr *instr)
{
instr->free_list= m_thd->free_list;
m_thd->free_list= 0;
+ /*
+ Memory root of every instruction is designated for permanent
+ transformations (optimizations) made on the parsed tree during
+ the first execution. It points to the memory root of the
+ entire stored procedure, as their life span is equal.
+ */
+ instr->mem_root= &main_mem_root;
insert_dynamic(&m_instr, (gptr)&instr);
}
@@ -1485,13 +1502,6 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
they want to store some value in local variable, pass return value and
etc... So their life time should be longer than one instruction.
- Probably we can call destructors for most of them then we are leaving
- routine. But this won't help much as they are allocated in main query
- MEM_ROOT anyway. So they all go to global thd->free_list.
-
- May be we can use some other MEM_ROOT for this purprose ???
-
- What else should we do for cleanup ?
cleanup_items() is called in sp_head::execute()
*/
return res;
@@ -1593,16 +1603,22 @@ sp_instr_set::print(String *str)
int
sp_instr_set_trigger_field::execute(THD *thd, uint *nextp)
{
- int res= 0;
-
DBUG_ENTER("sp_instr_set_trigger_field::execute");
- /* QQ: Still unsure what should we return in case of error 1 or -1 ? */
- if (!value->fixed && value->fix_fields(thd, 0, &value) ||
- trigger_field->fix_fields(thd, 0, 0) ||
- (value->save_in_field(trigger_field->field, 0) < 0))
+ DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
+}
+
+
+int
+sp_instr_set_trigger_field::exec_core(THD *thd, uint *nextp)
+{
+ int res= 0;
+ Item *it= sp_prepare_func_item(thd, &value);
+ if (!it ||
+ !trigger_field->fixed && trigger_field->fix_fields(thd, 0) ||
+ (it->save_in_field(trigger_field->field, 0) < 0))
res= -1;
- *nextp= m_ip + 1;
- DBUG_RETURN(res);
+ *nextp = m_ip+1;
+ return res;
}
void
@@ -1953,7 +1969,7 @@ int
sp_instr_cpush::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_cpush::execute");
- thd->spcont->push_cursor(&m_lex_keeper);
+ thd->spcont->push_cursor(&m_lex_keeper, this);
*nextp= m_ip+1;
DBUG_RETURN(0);
}
@@ -2012,12 +2028,24 @@ sp_instr_copen::execute(THD *thd, uint *nextp)
}
else
{
+ Query_arena *old_arena= thd->current_arena;
+
+ /*
+ Get the Query_arena from the cpush instruction, which contains
+ the free_list of the query, so new items (if any) are stored in
+ the right free_list, and we can cleanup after each open.
+ */
+ thd->current_arena= c->get_instr();
res= lex_keeper->reset_lex_and_exec_core(thd, nextp, FALSE, this);
+ /* Cleanup the query's items */
+ if (thd->current_arena->free_list)
+ cleanup_items(thd->current_arena->free_list);
+ thd->current_arena= old_arena;
/*
Work around the fact that errors in selects are not returned properly
(but instead converted into a warning), so if a condition handler
caught, we have lost the result code.
- */
+ */
if (!res)
{
uint dummy1, dummy2;
@@ -2414,72 +2442,3 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
return table;
}
-
-/*
- Auxilary function for adding tables used by routines used in query
- to table lists.
-
- SYNOPSIS
- sp_add_sp_tables_to_table_list_aux()
- thd - thread context
- lex - LEX to which table list tables will be added
- func_hash - routines for which tables should be added
- func_cache- SP cache in which this routines should be looked up
-
- NOTE
- See sp_add_sp_tables_to_table_list() for more info.
-
- RETURN VALUE
- TRUE - some tables were added
- FALSE - no tables were added.
-*/
-
-static bool
-sp_add_sp_tables_to_table_list_aux(THD *thd, LEX *lex, HASH *func_hash,
- sp_cache **func_cache)
-{
- uint i;
- bool result= FALSE;
-
- for (i= 0 ; i < func_hash->records ; i++)
- {
- sp_head *sp;
- LEX_STRING *ls= (LEX_STRING *)hash_element(func_hash, i);
- sp_name name(*ls);
-
- name.m_qname= *ls;
- if ((sp= sp_cache_lookup(func_cache, &name)))
- result|= sp->add_used_tables_to_table_list(thd, &lex->query_tables_last);
- }
-
- return result;
-}
-
-
-/*
- Add tables used by routines used in query to table list.
-
- SYNOPSIS
- sp_add_sp_tables_to_table_list()
- thd - thread context
- lex - LEX to which table list tables will be added
- func_lex - LEX for which functions we get tables
- (useful for adding tables used by view routines)
-
- NOTE
- Elements of list will be allocated in PS memroot, so this
- list will be persistent between PS execetutions.
-
- RETURN VALUE
- TRUE - some tables were added
- FALSE - no tables were added.
-*/
-
-bool
-sp_add_sp_tables_to_table_list(THD *thd, LEX *lex, LEX *func_lex)
-{
- return (sp_add_sp_tables_to_table_list_aux(thd, lex, &func_lex->spfuns,
- &thd->sp_func_cache) |
- sp_add_sp_tables_to_table_list_aux(thd, lex, &func_lex->spprocs,
- &thd->sp_proc_cache));
-}