From c7396f8d207510456e37143a3f3e1d0ae7c1a305 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Thu, 20 May 2004 02:02:49 +0300 Subject: [PATCH] PS and SP made compatible in mechanism used for preparing query for rexecutions (Bug #2266) --- mysql-test/r/sp.result | 10 +++ mysql-test/t/sp.test | 12 ++++ sql/item_subselect.cc | 42 ++++++------- sql/item_subselect.h | 6 +- sql/item_sum.cc | 8 +-- sql/item_sum.h | 4 +- sql/mysql_priv.h | 1 + sql/sp_head.cc | 136 ++++++++++------------------------------- sql/sp_head.h | 4 +- sql/sql_base.cc | 67 ++++++++++++-------- sql/sql_class.cc | 63 +++++++++++++------ sql/sql_class.h | 93 ++++++++++++++++------------ sql/sql_delete.cc | 8 ++- sql/sql_derived.cc | 2 +- sql/sql_insert.cc | 1 + sql/sql_lex.cc | 16 +++-- sql/sql_lex.h | 8 ++- sql/sql_parse.cc | 2 + sql/sql_prepare.cc | 53 +++++++--------- sql/sql_select.cc | 12 ++++ sql/sql_union.cc | 21 ++++--- sql/sql_update.cc | 12 +++- 22 files changed, 306 insertions(+), 275 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index cd5c2aa67c1..f8dcc114990 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -1305,3 +1305,13 @@ test bar PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINE drop procedure bar| drop table t1; drop table t2; +create procedure p1 () select (select s1 from t1) from t1; +create table t1 (s1 int); +call p1(); +(select s1 from t1) +insert into t1 values (1); +call p1(); +(select s1 from t1) +1 +drop procedure p1; +drop table t1; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index abd3901f234..af481258b3d 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -1444,3 +1444,15 @@ drop procedure bar| delimiter ;| drop table t1; drop table t2; + +# +# rexecution +# +create procedure p1 () select (select s1 from t1) from t1; +create table t1 (s1 int); +call p1(); +insert into t1 values (1); +call p1(); +drop procedure p1; +drop table t1; + diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index a84d75a5895..dc773de158b 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -104,7 +104,7 @@ bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref) { DBUG_ASSERT(fixed == 0); engine->set_thd((thd= thd_param)); - stmt= thd->current_statement; + arena= thd->current_arena; char const *save_where= thd->where; int res= engine->prepare(); @@ -316,8 +316,8 @@ Item_singlerow_subselect::select_transformer(JOIN *join) if (join->conds || join->having) { Item *cond; - if (stmt) - thd->set_n_backup_item_arena(stmt, &backup); + if (arena) + thd->set_n_backup_item_arena(arena, &backup); if (!join->having) cond= join->conds; @@ -330,15 +330,15 @@ Item_singlerow_subselect::select_transformer(JOIN *join) new Item_null()))) goto err; } - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena) + thd->restore_backup_item_arena(arena, &backup); return RES_REDUCE; } return RES_OK; err: - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena) + thd->restore_backup_item_arena(arena, &backup); return RES_ERROR; } @@ -618,8 +618,8 @@ Item_in_subselect::single_value_transformer(JOIN *join, Statement backup; thd->where= "scalar IN/ALL/ANY subquery"; - if (stmt) - thd->set_n_backup_item_arena(stmt, &backup); + if (arena) + thd->set_n_backup_item_arena(arena, &backup); if (select_lex->item_list.elements > 1) { @@ -823,21 +823,21 @@ Item_in_subselect::single_value_transformer(JOIN *join, push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SELECT_REDUCED, warn_buff); } - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(RES_REDUCE); } } } ok: - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(RES_OK); err: - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(RES_ERROR); } @@ -855,8 +855,8 @@ Item_in_subselect::row_value_transformer(JOIN *join) Item *item= 0; thd->where= "row IN/ALL/ANY subquery"; - if (stmt) - thd->set_n_backup_item_arena(stmt, &backup); + if (arena) + thd->set_n_backup_item_arena(arena, &backup); SELECT_LEX *select_lex= join->select_lex; @@ -940,13 +940,13 @@ Item_in_subselect::row_value_transformer(JOIN *join) if (join->conds->fix_fields(thd, join->tables_list, 0)) goto err; } - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(RES_OK); err: - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(RES_ERROR); } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 6d8f5353695..f9c570d5e25 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -26,7 +26,7 @@ class JOIN; class select_subselect; class subselect_engine; class Item_bool_func2; -class Statement; +class Item_arena; /* base class for subselects */ @@ -36,8 +36,8 @@ class Item_subselect :public Item_result_field protected: /* thread handler, will be assigned in fix_fields only */ THD *thd; - /* prepared statement, or 0 */ - Statement *stmt; + /* Item_arena used or 0 */ + Item_arena *arena; /* substitution instead of subselect in case of optimization */ Item *substitution; /* unit of subquery */ diff --git a/sql/item_sum.cc b/sql/item_sum.cc index c68754f6cae..94f791a06cc 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -77,15 +77,15 @@ Item_sum::Item_sum(THD *thd, Item_sum *item): */ bool Item_sum::save_args_for_prepared_statements(THD *thd) { - if (thd->current_statement) - return save_args(thd->current_statement); + if (thd->current_arena && args_copy == 0) + return save_args(thd->current_arena); return 0; } -bool Item_sum::save_args(Statement* stmt) +bool Item_sum::save_args(Item_arena* arena) { - if (!(args_copy= (Item**) stmt->alloc(sizeof(Item*)*arg_count))) + if (!(args_copy= (Item**) arena->alloc(sizeof(Item*)*arg_count))) return 1; memcpy(args_copy, args, sizeof(Item*)*arg_count); return 0; diff --git a/sql/item_sum.h b/sql/item_sum.h index 953ff76b65d..4da8db11cb0 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -23,6 +23,8 @@ #include +class Item_arena; + class Item_sum :public Item_result_field { public: @@ -93,7 +95,7 @@ public: virtual void make_unique() {} Item *get_tmp_table_item(THD *thd); bool save_args_for_prepared_statements(THD *); - bool save_args(Statement* stmt); + bool save_args(Item_arena* stmt); bool walk (Item_processor processor, byte *argument); }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 444c335d7a6..9cd4c65118d 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -681,6 +681,7 @@ void mysql_stmt_reset(THD *thd, char *packet); void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); int check_insert_fields(THD *thd,TABLE *table,List &fields, List &values, ulong counter); +void reset_stmt_for_execute(THD *thd, LEX *lex); /* sql_error.cc */ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, uint code, diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 96e340fa745..f2838a25a81 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -186,8 +186,8 @@ sp_head::operator new(size_t size) bzero((char *)&own_root, sizeof(own_root)); init_alloc_root(&own_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); sp= (sp_head *)alloc_root(&own_root, size); - sp->m_mem_root= own_root; - + sp->mem_root= own_root; + DBUG_PRINT("info", ("mem_root 0x%lx", (ulong) &sp->mem_root)); DBUG_RETURN(sp); } @@ -198,16 +198,18 @@ sp_head::operator delete(void *ptr, size_t size) MEM_ROOT own_root; sp_head *sp= (sp_head *)ptr; - DBUG_PRINT("info", ("root: %lx", &sp->m_mem_root)); - memcpy(&own_root, (const void *)&sp->m_mem_root, sizeof(MEM_ROOT)); + memcpy(&own_root, (const void *)&sp->mem_root, sizeof(MEM_ROOT)); + DBUG_PRINT("info", ("mem_root 0x%lx moved to 0x%lx", + (ulong) &sp->mem_root, (ulong) &own_root)); free_root(&own_root, MYF(0)); DBUG_VOID_RETURN; } + sp_head::sp_head() - : Sql_alloc(), m_returns_cs(NULL), m_has_return(FALSE), m_simple_case(FALSE), - m_multi_results(FALSE), m_free_list(NULL) + :Item_arena((bool)FALSE), m_returns_cs(NULL), m_has_return(FALSE), + m_simple_case(FALSE), m_multi_results(FALSE) { DBUG_ENTER("sp_head::sp_head"); @@ -216,6 +218,7 @@ sp_head::sp_head() DBUG_VOID_RETURN; } + void sp_head::init(LEX *lex) { @@ -359,7 +362,7 @@ sp_head::destroy() delete i; delete_dynamic(&m_instr); m_pcont->destroy(); - free_items(m_free_list); + free_items(free_list); while ((lex= (LEX *)m_lex.pop())) { if (lex != &m_thd->main_lex) // We got interrupted and have lex'es left @@ -392,6 +395,7 @@ sp_head::execute(THD *thd) if (ctx) ctx->clear_handler(); thd->query_error= 0; + thd->current_arena= this; do { sp_instr *i; @@ -430,6 +434,9 @@ sp_head::execute(THD *thd) done: DBUG_PRINT("info", ("ret=%d killed=%d query_error=%d", ret, thd->killed, thd->query_error)); + if (thd->current_arena) + cleanup_items(thd->current_arena->free_list); + thd->current_arena= 0; if (thd->killed || thd->query_error || thd->net.report_error) ret= -1; /* If the DB has changed, the pointer has changed too, but the @@ -687,21 +694,6 @@ sp_head::restore_lex(THD *thd) // Update some state in the old one first oldlex->ptr= sublex->ptr; oldlex->next_state= sublex->next_state; - for (sl= sublex->all_selects_list ; - sl ; - sl= sl->next_select_in_list()) - { - // Save WHERE clause pointers to avoid damaging by optimisation - sl->prep_where= sl->where; - if (sl->with_wild) - { - // Copy item_list. We will restore it before calling the - // sub-statement, so it's ok to pop them. - sl->item_list_copy.empty(); - while (Item *it= sl->item_list.pop()) - sl->item_list_copy.push_back(it); - } - } // Collect some data from the sub statement lex. sp_merge_funs(oldlex, sublex); @@ -792,19 +784,19 @@ sp_head::set_info(char *definer, uint definerlen, if (! p) p= definer; // Weird... len= p-definer; - m_definer_user.str= strmake_root(&m_mem_root, definer, len); + m_definer_user.str= strmake_root(&mem_root, definer, len); m_definer_user.length= len; len= definerlen-len-1; - m_definer_host.str= strmake_root(&m_mem_root, p+1, len); + m_definer_host.str= strmake_root(&mem_root, p+1, len); m_definer_host.length= len; m_created= created; m_modified= modified; - m_chistics= (st_sp_chistics *)alloc_root(&m_mem_root, sizeof(st_sp_chistics)); + m_chistics= (st_sp_chistics *)alloc_root(&mem_root, sizeof(st_sp_chistics)); memcpy(m_chistics, chistics, sizeof(st_sp_chistics)); if (m_chistics->comment.length == 0) m_chistics->comment.str= 0; else - m_chistics->comment.str= strmake_root(&m_mem_root, + m_chistics->comment.str= strmake_root(&mem_root, m_chistics->comment.str, m_chistics->comment.length); } @@ -812,26 +804,33 @@ sp_head::set_info(char *definer, uint definerlen, void sp_head::reset_thd_mem_root(THD *thd) { + DBUG_ENTER("sp_head::reset_thd_mem_root"); m_thd_root= thd->mem_root; - thd->mem_root= m_mem_root; - m_free_list= thd->free_list; // Keep the old list + thd->mem_root= mem_root; + DBUG_PRINT("info", ("mem_root 0x%lx moved to thd mem root 0x%lx", + (ulong) &mem_root, (ulong) &thd->mem_root)); + free_list= thd->free_list; // Keep the old list thd->free_list= NULL; // Start a new one /* Copy the db, since substatements will point to it */ m_thd_db= thd->db; thd->db= strmake_root(&thd->mem_root, thd->db, thd->db_length); m_thd= thd; + DBUG_VOID_RETURN; } void sp_head::restore_thd_mem_root(THD *thd) { - Item *flist= m_free_list; // The old list - m_free_list= thd->free_list; // Get the new one + DBUG_ENTER("sp_head::restore_thd_mem_root"); + Item *flist= free_list; // The old list + set_item_arena(thd); // Get new fre_list and mem_root + DBUG_PRINT("info", ("mem_root 0x%lx returned from thd mem root 0x%lx", + (ulong) &mem_root, (ulong) &thd->mem_root)); thd->free_list= flist; // Restore the old one thd->db= m_thd_db; // Restore the original db pointer - m_mem_root= thd->mem_root; thd->mem_root= m_thd_root; m_thd= NULL; + DBUG_VOID_RETURN; } @@ -919,7 +918,6 @@ int sp_instr_stmt::exec_stmt(THD *thd, LEX *lex) { LEX *olex; // The other lex - Item *freelist; SELECT_LEX *sl; int res; @@ -927,94 +925,24 @@ sp_instr_stmt::exec_stmt(THD *thd, LEX *lex) thd->lex= lex; // Use my own lex thd->lex->thd = thd; // QQ Not reentrant! thd->lex->unit.thd= thd; // QQ Not reentrant - freelist= thd->free_list; thd->free_list= NULL; VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id= query_id++; VOID(pthread_mutex_unlock(&LOCK_thread_count)); - // Copy WHERE clause pointers to avoid damaging by optimisation - // Also clear ref_pointer_arrays. - for (sl= lex->all_selects_list ; - sl ; - sl= sl->next_select_in_list()) - { - if (lex->sql_command == SQLCOM_CREATE_TABLE || - lex->sql_command == SQLCOM_INSERT_SELECT) - { // Destroys sl->table_list.first - sl->table_list_first_copy= sl->table_list.first; - } - if (sl->with_wild) - { - // Restore item_list - // Note: We have to do this before executing the sub-statement, - // to make sure that the list nodes are in the right - // memroot. - List_iterator_fast li(sl->item_list_copy); - - sl->item_list.empty(); - while (Item *it= li++) - sl->item_list.push_back(it); - } - sl->ref_pointer_array= 0; - if (sl->prep_where) - sl->where= sl->prep_where->copy_andor_structure(thd); - for (ORDER *order= (ORDER *)sl->order_list.first ; - order ; - order= order->next) - { - order->item_copy= order->item; - } - for (ORDER *group= (ORDER *)sl->group_list.first ; - group ; - group= group->next) - { - group->item_copy= group->item; - } - } + reset_stmt_for_execute(thd, lex); res= mysql_execute_command(thd); + lex->unit.cleanup(); if (thd->lock || thd->open_tables || thd->derived_tables) { thd->proc_info="closing tables"; close_thread_tables(thd); /* Free tables */ } - for (sl= lex->all_selects_list ; - sl ; - sl= sl->next_select_in_list()) - { - TABLE_LIST *tabs; - - if (lex->sql_command == SQLCOM_CREATE_TABLE || - lex->sql_command == SQLCOM_INSERT_SELECT) - { // Restore sl->table_list.first - sl->table_list.first= sl->table_list_first_copy; - } - // We have closed all tables, get rid of pointers to them - for (tabs=(TABLE_LIST *)sl->table_list.first ; - tabs ; - tabs= tabs->next) - { - tabs->table= NULL; - } - for (ORDER *order= (ORDER *)sl->order_list.first ; - order ; - order= order->next) - { - order->item= order->item_copy; - } - for (ORDER *group= (ORDER *)sl->group_list.first ; - group ; - group= group->next) - { - group->item= group->item_copy; - } - } thd->lex= olex; // Restore the other lex - thd->free_list= freelist; return res; } diff --git a/sql/sp_head.h b/sql/sp_head.h index cb2e56adabe..3906d8c2bc8 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -70,7 +70,7 @@ sp_name * sp_name_current_db_new(THD *thd, LEX_STRING name); -class sp_head : public Sql_alloc +class sp_head :private Item_arena { sp_head(const sp_head &); /* Prevent use of these */ void operator=(sp_head &); @@ -206,9 +206,7 @@ public: private: - MEM_ROOT m_mem_root; // My own mem_root MEM_ROOT m_thd_root; // Temp. store for thd's mem_root - Item *m_free_list; // Where the items go THD *m_thd; // Set if we have reset mem_root char *m_thd_db; // Original thd->db pointer diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 190e930deed..1c8c310e36a 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2158,14 +2158,14 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List &fields, { if (!wild_num) return 0; - Statement *stmt= thd->current_statement, backup; + Item_arena *arena= thd->current_arena, backup; /* If we are in preparing prepared statement phase then we have change temporary mem_root to statement mem root to save changes of SELECT list */ - if (stmt) - thd->set_n_backup_item_arena(stmt, &backup); + if (arena) + thd->set_n_backup_item_arena(arena, &backup); reg2 Item *item; List_iterator it(fields); while ( wild_num && (item= it++)) @@ -2178,8 +2178,8 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List &fields, if (insert_fields(thd,tables,((Item_field*) item)->db_name, ((Item_field*) item)->table_name, &it)) { - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena) + thd->restore_backup_item_arena(arena, &backup); return (-1); } if (sum_func_list) @@ -2194,8 +2194,15 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List &fields, wild_num--; } } - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena) + { + /* make * substituting permanent */ + SELECT_LEX *select_lex= thd->lex->current_select; + select_lex->with_wild= 0; + select_lex->item_list= fields; + + thd->restore_backup_item_arena(arena, &backup); + } return 0; } @@ -2408,12 +2415,17 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) { table_map not_null_tables= 0; - Statement *stmt= thd->current_statement, backup; - + SELECT_LEX *select_lex= thd->lex->current_select; + Item_arena *arena= ((thd->current_arena && + !select_lex->conds_processed_with_permanent_arena) ? + thd->current_arena : + 0); + Item_arena backup; DBUG_ENTER("setup_conds"); + thd->set_query_id=1; - thd->lex->current_select->cond_count= 0; + select_lex->cond_count= 0; if (*conds) { thd->where="where clause"; @@ -2436,7 +2448,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) table->on_expr->fix_fields(thd, tables, &table->on_expr) || table->on_expr->check_cols(1)) DBUG_RETURN(1); - thd->lex->current_select->cond_count++; + select_lex->cond_count++; /* If it's a normal join or a LEFT JOIN which can be optimized away @@ -2447,12 +2459,12 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) !(specialflag & SPECIAL_NO_NEW_FUNC))) { table->outer_join= 0; - if (stmt) - thd->set_n_backup_item_arena(stmt, &backup); + if (arena) + thd->set_n_backup_item_arena(arena, &backup); *conds= and_conds(*conds, table->on_expr); table->on_expr=0; - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena) + thd->restore_backup_item_arena(arena, &backup); if ((*conds) && !(*conds)->fixed && (*conds)->fix_fields(thd, tables, conds)) DBUG_RETURN(1); @@ -2460,8 +2472,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) } if (table->natural_join) { - if (stmt) - thd->set_n_backup_item_arena(stmt, &backup); + if (arena) + thd->set_n_backup_item_arena(arena, &backup); /* Make a join of all fields with have the same name */ TABLE *t1= table->table; TABLE *t2= table->natural_join->table; @@ -2491,7 +2503,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) t2->used_keys.intersect(t2_field->part_of_key); } } - thd->lex->current_select->cond_count+= cond_and->list.elements; + select_lex->cond_count+= cond_and->list.elements; // to prevent natural join processing during PS re-execution table->natural_join= 0; @@ -2500,8 +2512,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) { *conds= and_conds(*conds, cond_and); // fix_fields() should be made with temporary memory pool - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena) + thd->restore_backup_item_arena(arena, &backup); if (*conds && !(*conds)->fixed) { if ((*conds)->fix_fields(thd, tables, conds)) @@ -2512,8 +2524,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) { table->on_expr= and_conds(table->on_expr, cond_and); // fix_fields() should be made with temporary memory pool - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena) + thd->restore_backup_item_arena(arena, &backup); if (table->on_expr && !table->on_expr->fixed) { if (table->on_expr->fix_fields(thd, tables, &table->on_expr)) @@ -2523,21 +2535,22 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) } } - if (stmt) + if (arena) { /* We are in prepared statement preparation code => we should store WHERE clause changing for next executions. - We do this ON -> WHERE transformation only once per PS statement. + We do this ON -> WHERE transformation only once per PS/SP statement. */ - thd->lex->current_select->where= *conds; + select_lex->where= *conds; + select_lex->conds_processed_with_permanent_arena= 1; } DBUG_RETURN(test(thd->net.report_error)); err: - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(1); } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index f8b2efe911c..00a9c44d8d9 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -86,7 +86,7 @@ extern "C" void free_user_var(user_var_entry *entry) ** Thread specific functions ****************************************************************************/ -THD::THD():user_time(0), current_statement(0), is_fatal_error(0), +THD::THD():user_time(0), current_arena(0), is_fatal_error(0), last_insert_id_used(0), insert_id_used(0), rand_used(0), in_lock_tables(0), global_read_lock(0), bootstrap(0), spcont(NULL) @@ -1210,24 +1210,48 @@ int select_dumpvar::prepare(List &list, SELECT_LEX_UNIT *u) } -/* - Statement functions -*/ - -Statement::Statement(THD *thd) - :id(++thd->statement_id_counter), - set_query_id(1), - allow_sum_func(0), - lex(&main_lex), - query(0), - query_length(0), - free_list(0) +Item_arena::Item_arena(THD* thd) + :free_list(0) { init_sql_alloc(&mem_root, thd->variables.query_alloc_block_size, thd->variables.query_prealloc_size); } + +Item_arena::Item_arena() + :free_list(0) +{ + bzero((char *) &mem_root, sizeof(mem_root)); +} + + +Item_arena::Item_arena(bool init_mem_root) + :free_list(0) +{ + if (init_mem_root) + bzero((char *) &mem_root, sizeof(mem_root)); +} + + +Item_arena::~Item_arena() +{} + + +/* + Statement functions +*/ + +Statement::Statement(THD *thd) + :Item_arena(thd), + id(++thd->statement_id_counter), + set_query_id(1), + allow_sum_func(0), + lex(&main_lex), + query(0), + query_length(0) +{} + /* This constructor is called when statement is a subobject of THD: Some variables are initialized in THD::init due to locking problems @@ -1235,15 +1259,14 @@ Statement::Statement(THD *thd) */ Statement::Statement() - :id(0), + :Item_arena(), + id(0), set_query_id(1), allow_sum_func(0), /* initialized later */ lex(&main_lex), query(0), /* these two are set */ - query_length(0), /* in alloc_query() */ - free_list(0) + query_length(0) /* in alloc_query() */ { - bzero((char *) &mem_root, sizeof(mem_root)); } @@ -1264,14 +1287,14 @@ void Statement::set_statement(Statement *stmt) } -void Statement::set_n_backup_item_arena(Statement *set, Statement *backup) +void Item_arena::set_n_backup_item_arena(Item_arena *set, Item_arena *backup) { backup->set_item_arena(this); set_item_arena(set); } -void Statement::restore_backup_item_arena(Statement *set, Statement *backup) +void Item_arena::restore_backup_item_arena(Item_arena *set, Item_arena *backup) { set->set_item_arena(this); set_item_arena(backup); @@ -1279,7 +1302,7 @@ void Statement::restore_backup_item_arena(Statement *set, Statement *backup) init_alloc_root(&backup->mem_root, 0, 0); } -void Statement::set_item_arena(Statement *set) +void Item_arena::set_item_arena(Item_arena *set) { mem_root= set->mem_root; free_list= set->free_list; diff --git a/sql/sql_class.h b/sql/sql_class.h index cb8da885b77..d7dd0e69bb2 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -434,6 +434,48 @@ struct system_variables void free_tmp_table(THD *thd, TABLE *entry); +class Item_arena +{ +public: + /* + List of items created in the parser for this query. Every item puts + itself to the list on creation (see Item::Item() for details)) + */ + Item *free_list; + MEM_ROOT mem_root; + + Item_arena(THD *thd); + Item_arena(); + Item_arena(bool init_mem_root); + ~Item_arena(); + + inline gptr alloc(unsigned int size) { return alloc_root(&mem_root,size); } + inline gptr calloc(unsigned int size) + { + gptr ptr; + if ((ptr=alloc_root(&mem_root,size))) + bzero((char*) ptr,size); + return ptr; + } + inline char *strdup(const char *str) + { return strdup_root(&mem_root,str); } + inline char *strmake(const char *str, uint size) + { return strmake_root(&mem_root,str,size); } + inline char *memdup(const char *str, uint size) + { return memdup_root(&mem_root,str,size); } + inline char *memdup_w_gap(const char *str, uint size, uint gap) + { + gptr ptr; + if ((ptr=alloc_root(&mem_root,size+gap))) + memcpy(ptr,str,size); + return ptr; + } + + void set_n_backup_item_arena(Item_arena *set, Item_arena *backup); + void restore_backup_item_arena(Item_arena *set, Item_arena *backup); + void set_item_arena(Item_arena *set); +}; + /* State of a single command executed against this connection. One connection can contain a lot of simultaneously running statements, @@ -448,7 +490,7 @@ void free_tmp_table(THD *thd, TABLE *entry); be used explicitly. */ -class Statement +class Statement: public Item_arena { Statement(const Statement &rhs); /* not implemented: */ Statement &operator=(const Statement &rhs); /* non-copyable */ @@ -489,12 +531,6 @@ public: */ char *query; uint32 query_length; // current query length - /* - List of items created in the parser for this query. Every item puts - itself to the list on creation (see Item::Item() for details)) - */ - Item *free_list; - MEM_ROOT mem_root; public: /* We build without RTTI, so dynamic_cast can't be used. */ @@ -518,31 +554,6 @@ public: /* return class type */ virtual Type type() const; - inline gptr alloc(unsigned int size) { return alloc_root(&mem_root,size); } - inline gptr calloc(unsigned int size) - { - gptr ptr; - if ((ptr=alloc_root(&mem_root,size))) - bzero((char*) ptr,size); - return ptr; - } - inline char *strdup(const char *str) - { return strdup_root(&mem_root,str); } - inline char *strmake(const char *str, uint size) - { return strmake_root(&mem_root,str,size); } - inline char *memdup(const char *str, uint size) - { return memdup_root(&mem_root,str,size); } - inline char *memdup_w_gap(const char *str, uint size, uint gap) - { - gptr ptr; - if ((ptr=alloc_root(&mem_root,size+gap))) - memcpy(ptr,str,size); - return ptr; - } - - void set_n_backup_item_arena(Statement *set, Statement *backup); - void restore_backup_item_arena(Statement *set, Statement *backup); - void set_item_arena(Statement *set); }; @@ -746,9 +757,9 @@ public: Vio* active_vio; #endif /* - Current prepared Statement if there one, or 0 + Current prepared Item_arena if there one, or 0 */ - Statement *current_statement; + Item_arena *current_arena; /* next_insert_id is set on SET INSERT_ID= #. This is used as the next generated auto_increment value in handler.cc @@ -969,7 +980,7 @@ public: inline void allocate_temporary_memory_pool_for_ps_preparing() { - DBUG_ASSERT(current_statement!=0); + DBUG_ASSERT(current_arena!=0); /* We do not want to have in PS memory all that junk, which will be created by preparation => substitute memory @@ -978,7 +989,7 @@ public: We know that PS memory pool is now copied to THD, we move it back to allow some code use it. */ - current_statement->set_item_arena(this); + current_arena->set_item_arena(this); init_sql_alloc(&mem_root, variables.query_alloc_block_size, variables.query_prealloc_size); @@ -986,12 +997,16 @@ public: } inline void free_temporary_memory_pool_for_ps_preparing() { - DBUG_ASSERT(current_statement!=0); - cleanup_items(current_statement->free_list); + DBUG_ASSERT(current_arena!=0); + cleanup_items(current_arena->free_list); free_items(free_list); close_thread_tables(this); // to close derived tables free_root(&mem_root, MYF(0)); - set_item_arena(current_statement); + set_item_arena(current_arena); + } + inline bool only_prepare() + { + return command == COM_PREPARE; } }; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 9f725c7e17f..b9769e3a940 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -264,10 +264,11 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) { TABLE_LIST *delete_table_list= ((TABLE_LIST*) thd->lex-> select_lex.table_list.first); + SELECT_LEX *select_lex= &thd->lex->select_lex; DBUG_ENTER("mysql_prepare_delete"); if (setup_conds(thd, delete_table_list, conds) || - setup_ftfuncs(&thd->lex->select_lex)) + setup_ftfuncs(select_lex)) DBUG_RETURN(-1); if (find_real_table_in_list(table_list->next, table_list->db, table_list->real_name)) @@ -275,6 +276,11 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); DBUG_RETURN(-1); } + if (thd->current_arena && select_lex->first_execution) + { + select_lex->prep_where= select_lex->where; + select_lex->first_execution= 0; + } DBUG_RETURN(0); } diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 2e2ad6786fc..46794f45f3c 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -152,7 +152,7 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, if it is preparation PS only then we do not need real data and we can skip execution (and parameters is not defined, too) */ - if (!thd->current_statement) + if (!thd->only_prepare()) { if (is_union) { diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 5fb525aa9a9..b9a1f0cebd9 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -460,6 +460,7 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); DBUG_RETURN(-1); } + thd->lex->select_lex.first_execution= 0; DBUG_RETURN(0); } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 7e38941f344..1a3558b5eeb 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1011,10 +1011,12 @@ void st_select_lex::init_query() having_fix_field= 0; resolve_mode= NOMATTER_MODE; cond_count= with_wild= 0; + conds_processed_with_permanent_arena= 0; ref_pointer_array= 0; select_n_having_items= 0; prep_where= 0; explicit_limit= 0; + first_execution= 1; } void st_select_lex::init_select() @@ -1414,7 +1416,9 @@ bool st_select_lex::add_order_to_list(THD *thd, Item *item, bool asc) bool st_select_lex::add_item_to_list(THD *thd, Item *item) { - return item_list.push_back(item); + DBUG_ENTER("st_select_lex::add_item_to_list"); + DBUG_PRINT("info", ("Item: %p", item)); + DBUG_RETURN(item_list.push_back(item)); } @@ -1500,12 +1504,12 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) We have to create array in prepared statement memory if it is prepared statement */ - Statement *stmt= thd->current_statement ? thd->current_statement : thd; + Item_arena *arena= thd->current_arena ? thd->current_arena : thd; return (ref_pointer_array= - (Item **)stmt->alloc(sizeof(Item*) * - (item_list.elements + - select_n_having_items + - order_group_num)* 5)) == 0; + (Item **)arena->alloc(sizeof(Item*) * + (item_list.elements + + select_n_having_items + + order_group_num)* 5)) == 0; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 60965209cc9..0f0c39ab493 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -405,8 +405,6 @@ public: enum olap_type olap; SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */ List item_list; /* list of fields & expressions */ - List item_list_copy; /* For SPs */ - byte *table_list_first_copy; /* For SPs */ List interval_list, use_index, *use_index_ptr, ignore_index, *ignore_index_ptr; /* @@ -435,6 +433,11 @@ public: uint cond_count; /* number of arguments of and/or/xor in where/having */ enum_parsing_place parsing_place; /* where we are parsing expression */ bool with_sum_func; /* sum function indicator */ + /* + PS or SP cond natural joins was alredy processed with permanent + arena and all additional items which we need alredy stored in it + */ + bool conds_processed_with_permanent_arena; ulong table_join_options; uint in_sum_expr; @@ -445,6 +448,7 @@ public: bool having_fix_field; /* explicit LIMIT clause was used */ bool explicit_limit; + bool first_execution; /* first execution in SP or PS */ /* SELECT for SELECT command st_select_lex. Used to privent scaning diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 598f131985c..bd36880433f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4182,12 +4182,14 @@ void mysql_parse(THD *thd, char *inBuf, uint length) query_cache_end_of_result(thd); } } + lex->unit.cleanup(); } else { DBUG_PRINT("info",("Command aborted. Fatal_error: %d", thd->is_fatal_error)); query_cache_abort(&thd->net); + lex->unit.cleanup(); if (thd->lex->sphead) { /* Clean up after failed stored procedure/function */ diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index f3ae4052bb4..a6e6e1deae2 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1357,7 +1357,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) mysql_log.write(thd, COM_PREPARE, "%s", packet); - thd->current_statement= stmt; + thd->current_arena= stmt; lex= lex_start(thd, (uchar *) thd->query, thd->query_length); mysql_init_query(thd); lex->safe_to_cache_query= 0; @@ -1381,7 +1381,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) stmt->set_item_arena(thd); thd->set_statement(&thd->stmt_backup); thd->set_item_arena(&thd->stmt_backup); - thd->current_statement= 0; + thd->current_arena= 0; if (error) { @@ -1389,43 +1389,33 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) thd->stmt_map.erase(stmt); /* error is sent inside yyparse/send_prepare_results */ } - else - { - SELECT_LEX *sl= stmt->lex->all_selects_list; - /* - Save WHERE clause pointers, because they may be changed during query - optimisation. - */ - for (; sl; sl= sl->next_select_in_list()) - { - sl->prep_where= sl->where; - } - } DBUG_VOID_RETURN; } /* Reinit statement before execution */ -static void reset_stmt_for_execute(Prepared_statement *stmt) +void reset_stmt_for_execute(THD *thd, LEX *lex) { - THD *thd= stmt->thd; - SELECT_LEX *sl= stmt->lex->all_selects_list; + SELECT_LEX *sl= lex->all_selects_list; for (; sl; sl= sl->next_select_in_list()) { - /* - Copy WHERE clause pointers to avoid damaging they by optimisation - */ - if (sl->prep_where) - sl->where= sl->prep_where->copy_andor_structure(thd); - DBUG_ASSERT(sl->join == 0); - ORDER *order; - /* Fix GROUP list */ - for (order= (ORDER *)sl->group_list.first; order; order= order->next) - order->item= &order->item_ptr; - /* Fix ORDER list */ - for (order= (ORDER *)sl->order_list.first; order; order= order->next) - order->item= &order->item_ptr; + if (!sl->first_execution) + { + /* + Copy WHERE clause pointers to avoid damaging they by optimisation + */ + if (sl->prep_where) + sl->where= sl->prep_where->copy_andor_structure(thd); + DBUG_ASSERT(sl->join == 0); + ORDER *order; + /* Fix GROUP list */ + for (order= (ORDER *)sl->group_list.first; order; order= order->next) + order->item= &order->item_ptr; + /* Fix ORDER list */ + for (order= (ORDER *)sl->order_list.first; order; order= order->next) + order->item= &order->item_ptr; + } /* TODO: When the new table structure is ready, then have a status bit @@ -1443,7 +1433,6 @@ static void reset_stmt_for_execute(Prepared_statement *stmt) tables->table= 0; tables->table_list= 0; } - { SELECT_LEX_UNIT *unit= sl->master_unit(); unit->unclean(); @@ -1506,7 +1495,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) thd->stmt_backup.set_statement(thd); thd->set_statement(stmt); - reset_stmt_for_execute(stmt); + reset_stmt_for_execute(thd, stmt->lex); #ifndef EMBEDDED_LIBRARY if (stmt->param_count) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 824edfa93d0..36fc71e23e2 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -367,7 +367,14 @@ JOIN::prepare(Item ***rref_pointer_array, Item_subselect::trans_res res; if ((res= subselect->select_transformer(this)) != Item_subselect::RES_OK) + { + if (thd->current_arena && select_lex->first_execution) + { + select_lex->prep_where= select_lex->where; + select_lex->first_execution= 0; + } DBUG_RETURN((res == Item_subselect::RES_ERROR)); + } } } @@ -470,6 +477,11 @@ JOIN::prepare(Item ***rref_pointer_array, if (alloc_func_list()) goto err; + if (thd->current_arena && select_lex->first_execution) + { + select_lex->prep_where= select_lex->where; + select_lex->first_execution= 0; + } DBUG_RETURN(0); // All OK err: diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 8136ce44d1d..794fbc74c73 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -262,27 +262,32 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, thd_arg->lex->current_select= lex_select_save; if (!item_list.elements) { - Statement *stmt= thd->current_statement; - Statement backup; - if (stmt) - thd->set_n_backup_item_arena(stmt, &backup); + Item_arena *arena= thd->current_arena; + Item_arena backup; + if (arena) + thd->set_n_backup_item_arena(arena, &backup); Field **field; for (field= table->field; *field; field++) { Item_field *item= new Item_field(*field); if (!item || item_list.push_back(item)) { - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(-1); } } - if (stmt) + if (arena) { - thd->restore_backup_item_arena(stmt, &backup); + thd->restore_backup_item_arena(arena, &backup); /* prepare fake select to initialize it correctly */ ulong options_tmp= init_prepare_fake_select_lex(thd); + /* + it should be done only once (because item_list builds only onece + per statement) + */ + DBUG_ASSERT(fake_select_lex->join == 0); if (!(fake_select_lex->join= new JOIN(thd, item_list, thd->options, result))) { diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 918a4c769b4..5c6ed023485 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -425,6 +425,7 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list, TABLE *table= table_list->table; TABLE_LIST tables; List all_fields; + SELECT_LEX *select_lex= &thd->lex->select_lex; DBUG_ENTER("mysql_prepare_update"); #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -437,10 +438,10 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list, if (setup_tables(update_table_list) || setup_conds(thd, update_table_list, conds) || - thd->lex->select_lex.setup_ref_array(thd, order_num) || - setup_order(thd, thd->lex->select_lex.ref_pointer_array, + select_lex->setup_ref_array(thd, order_num) || + setup_order(thd, select_lex->ref_pointer_array, update_table_list, all_fields, all_fields, order) || - setup_ftfuncs(&thd->lex->select_lex)) + setup_ftfuncs(select_lex)) DBUG_RETURN(-1); /* Check that we are not using table that we are updating in a sub select */ @@ -450,6 +451,11 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list, my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); DBUG_RETURN(-1); } + if (thd->current_arena && select_lex->first_execution) + { + select_lex->prep_where= select_lex->where; + select_lex->first_execution= 0; + } DBUG_RETURN(0); }