diff options
-rw-r--r-- | mysql-test/r/ps.result | 7 | ||||
-rw-r--r-- | mysql-test/t/ps.test | 12 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 4 | ||||
-rw-r--r-- | sql/item_subselect.cc | 50 | ||||
-rw-r--r-- | sql/item_subselect.h | 2 | ||||
-rw-r--r-- | sql/item_sum.cc | 20 | ||||
-rw-r--r-- | sql/item_sum.h | 4 | ||||
-rw-r--r-- | sql/mysql_priv.h | 2 | ||||
-rw-r--r-- | sql/sql_base.cc | 43 | ||||
-rw-r--r-- | sql/sql_class.cc | 78 | ||||
-rw-r--r-- | sql/sql_class.h | 128 | ||||
-rw-r--r-- | sql/sql_derived.cc | 2 | ||||
-rw-r--r-- | sql/sql_lex.cc | 4 | ||||
-rw-r--r-- | sql/sql_parse.cc | 3 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 158 | ||||
-rw-r--r-- | sql/sql_select.cc | 46 | ||||
-rw-r--r-- | sql/sql_union.cc | 15 |
17 files changed, 317 insertions, 261 deletions
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 27f4ce7f815..0523143f91d 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -219,3 +219,10 @@ Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length I t1 MyISAM 9 Dynamic 0 0 0 4294967295 1024 0 NULL # # # latin1_swedish_ci NULL deallocate prepare stmt1 ; drop table t1; +create table t1(a varchar(2), b varchar(3)); +prepare stmt1 from "select a, b from t1 where (not (a='aa' and b < 'zzz'))"; +execute stmt1; +a b +execute stmt1; +a b +deallocate prepare stmt1; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 35f9b193fe4..9d23c795e05 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -206,3 +206,15 @@ execute stmt1; show table status from test like 't1%' ; deallocate prepare stmt1 ; drop table t1; + +# +# Bug#4912 "mysqld crashs in case a statement is executed a second time": +# negation elimination should and prepared statemens +# + +create table t1(a varchar(2), b varchar(3)); +prepare stmt1 from "select a, b from t1 where (not (a='aa' and b < 'zzz'))"; +execute stmt1; +execute stmt1; +deallocate prepare stmt1; + diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 23bdad1aae5..75dbbecf187 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -589,10 +589,8 @@ bool Item_in_optimizer::fix_left(THD *thd, /* If it is preparation PS only then we do not know values of parameters => cant't get there values and do not need that values. - - TODO: during merge with 5.0 it should be changed on !thd->only_prepare() */ - if (!thd->current_statement) + if (! thd->current_arena->is_stmt_prepare()) cache->store(args[0]); if (cache->cols() == 1) { diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 8c4dae92ddc..68bc144d518 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -125,7 +125,6 @@ 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; char const *save_where= thd->where; int res; @@ -306,7 +305,10 @@ Item_singlerow_subselect::select_transformer(JOIN *join) return RES_OK; SELECT_LEX *select_lex= join->select_lex; - Statement backup; + + /* Juggle with current arena only if we're in prepared statement prepare */ + Item_arena *arena= join->thd->current_arena; + Item_arena backup; if (!select_lex->master_unit()->first_select()->next_select() && !select_lex->table_list.elements && @@ -341,8 +343,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->is_stmt_prepare()) + thd->set_n_backup_item_arena(arena, &backup); if (!join->having) cond= join->conds; @@ -355,15 +357,15 @@ Item_singlerow_subselect::select_transformer(JOIN *join) new Item_null()))) goto err; } - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena->is_stmt_prepare()) + 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->is_stmt_prepare()) + thd->restore_backup_item_arena(arena, &backup); return RES_ERROR; } @@ -640,11 +642,11 @@ Item_in_subselect::single_value_transformer(JOIN *join, } SELECT_LEX *select_lex= join->select_lex; - Statement backup; + Item_arena *arena= join->thd->current_arena, backup; thd->where= "scalar IN/ALL/ANY subquery"; - if (stmt) - thd->set_n_backup_item_arena(stmt, &backup); + if (arena->is_stmt_prepare()) + thd->set_n_backup_item_arena(arena, &backup); if (select_lex->item_list.elements > 1) { @@ -857,21 +859,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->is_stmt_prepare()) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(RES_REDUCE); } } } ok: - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena->is_stmt_prepare()) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(RES_OK); err: - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena->is_stmt_prepare()) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(RES_ERROR); } @@ -885,12 +887,12 @@ Item_in_subselect::row_value_transformer(JOIN *join) { DBUG_RETURN(RES_OK); } - Statement backup; + Item_arena *arena= join->thd->current_arena, backup; Item *item= 0; thd->where= "row IN/ALL/ANY subquery"; - if (stmt) - thd->set_n_backup_item_arena(stmt, &backup); + if (arena->is_stmt_prepare()) + thd->set_n_backup_item_arena(arena, &backup); SELECT_LEX *select_lex= join->select_lex; @@ -974,13 +976,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->is_stmt_prepare()) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(RES_OK); err: - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena->is_stmt_prepare()) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(RES_ERROR); } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 6b8b8b0b3a7..1ce3144f660 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -36,8 +36,6 @@ 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; /* 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 c256055d5bb..cbb4cd41046 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -64,28 +64,28 @@ Item_sum::Item_sum(THD *thd, Item_sum *item): /* - Save copy of arguments if we are prepare prepared statement + Save copy of arguments if we prepare prepared statement (arguments can be rewritten in get_tmp_table_item()) SYNOPSIS - Item_sum::save_args_for_prepared_statements() + Item_sum::save_args_for_prepared_statement() thd - thread handler RETURN 0 - OK 1 - Error */ -bool Item_sum::save_args_for_prepared_statements(THD *thd) +bool Item_sum::save_args_for_prepared_statement(THD *thd) { - if (thd->current_statement) - return save_args(thd->current_statement); + if (thd->current_arena->is_stmt_prepare()) + 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; @@ -214,7 +214,7 @@ Item_sum_num::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { DBUG_ASSERT(fixed == 0); - if (save_args_for_prepared_statements(thd)) + if (save_args_for_prepared_statement(thd)) return 1; if (!thd->allow_sum_func) @@ -248,7 +248,7 @@ Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { DBUG_ASSERT(fixed == 0); - if (save_args_for_prepared_statements(thd)) + if (save_args_for_prepared_statement(thd)) return 1; Item *item= args[0]; @@ -1947,7 +1947,7 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { DBUG_ASSERT(fixed == 0); - if (save_args_for_prepared_statements(thd)) + if (save_args_for_prepared_statement(thd)) return 1; uint i; /* for loop variable */ diff --git a/sql/item_sum.h b/sql/item_sum.h index fcace9e322a..5081d592654 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -92,8 +92,8 @@ public: virtual bool setup(THD *thd) {return 0;} 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_for_prepared_statement(THD *); + bool save_args(Item_arena *arena); bool walk (Item_processor processor, byte *argument); }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 9ada2fba164..b7c19ad2c06 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -295,7 +295,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout); struct st_table; class THD; -class Statement; +class Item_arena; /* Struct to handle simple linked lists */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 4efdd3edbcd..eed7012966d 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2188,14 +2188,15 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &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->is_stmt_prepare()) + thd->set_n_backup_item_arena(arena, &backup); + reg2 Item *item; List_iterator<Item> it(fields); while ( wild_num && (item= it++)) @@ -2219,8 +2220,8 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, else 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->is_stmt_prepare()) + thd->restore_backup_item_arena(arena, &backup); return (-1); } if (sum_func_list) @@ -2235,8 +2236,8 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, wild_num--; } } - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena->is_stmt_prepare()) + thd->restore_backup_item_arena(arena, &backup); return 0; } @@ -2449,7 +2450,7 @@ 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; + Item_arena *arena= thd->current_arena, backup; DBUG_ENTER("setup_conds"); thd->set_query_id=1; @@ -2488,12 +2489,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->is_stmt_prepare()) + 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->is_stmt_prepare()) + thd->restore_backup_item_arena(arena, &backup); if ((*conds) && !(*conds)->fixed && (*conds)->fix_fields(thd, tables, conds)) DBUG_RETURN(1); @@ -2501,8 +2502,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->is_stmt_prepare()) + 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; @@ -2543,8 +2544,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->is_stmt_prepare()) + thd->restore_backup_item_arena(arena, &backup); if (*conds && !(*conds)->fixed) { if ((*conds)->fix_fields(thd, tables, conds)) @@ -2555,8 +2556,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->is_stmt_prepare()) + 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)) @@ -2567,7 +2568,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) } } - if (stmt) + if (arena->is_stmt_prepare()) { /* We are in prepared statement preparation code => we should store @@ -2580,8 +2581,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) DBUG_RETURN(test(thd->net.report_error)); err: - if (stmt) - thd->restore_backup_item_arena(stmt, &backup); + if (arena->is_stmt_prepare()) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(1); } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 73a180078cf..23fef44c964 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -155,7 +155,7 @@ bool foreign_key_prefix(Key *a, Key *b) ** Thread specific functions ****************************************************************************/ -THD::THD():user_time(0), current_statement(0), is_fatal_error(0), +THD::THD():user_time(0), current_arena(this), is_fatal_error(0), last_insert_id_used(0), insert_id_used(0), rand_used(0), time_zone_used(0), in_lock_tables(0), global_read_lock(0), bootstrap(0) @@ -1301,23 +1301,59 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u) } +Item_arena::Item_arena(THD* thd) + :free_list(0), + state(INITIALIZED) +{ + init_sql_alloc(&mem_root, + thd->variables.query_alloc_block_size, + thd->variables.query_prealloc_size); +} + + +/* This constructor is called when Item_arena is a subobject of THD */ + +Item_arena::Item_arena() + :free_list(0), + state(CONVENTIONAL_EXECUTION) +{ + clear_alloc_root(&mem_root); +} + + +Item_arena::Item_arena(bool init_mem_root) + :free_list(0), + state(INITIALIZED) +{ + if (init_mem_root) + clear_alloc_root(&mem_root); +} + +Item_arena::Type Item_arena::type() const +{ + DBUG_ASSERT("Item_arena::type()" == "abstract"); + return STATEMENT; +} + + +Item_arena::~Item_arena() +{} + + /* Statement functions */ Statement::Statement(THD *thd) - :id(++thd->statement_id_counter), + :Item_arena(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) + query_length(0) { name.str= NULL; - init_sql_alloc(&mem_root, - thd->variables.query_alloc_block_size, - thd->variables.query_prealloc_size); } /* @@ -1332,14 +1368,12 @@ Statement::Statement() 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)); } -Statement::Type Statement::type() const +Item_arena::Type Statement::type() const { return STATEMENT; } @@ -1356,14 +1390,29 @@ void Statement::set_statement(Statement *stmt) } -void Statement::set_n_backup_item_arena(Statement *set, Statement *backup) +void +Statement::set_n_backup_statement(Statement *stmt, Statement *backup) +{ + backup->set_statement(this); + set_statement(stmt); +} + + +void Statement::restore_backup_statement(Statement *stmt, Statement *backup) +{ + stmt->set_statement(this); + 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); @@ -1371,10 +1420,11 @@ 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; + state= set->state; } Statement::~Statement() diff --git a/sql/sql_class.h b/sql/sql_class.h index 59ac8ff0483..61b8a8281da 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -418,6 +418,61 @@ 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; + static const int INITIALIZED= 0, PREPARED= 1, EXECUTED= 3, + CONVENTIONAL_EXECUTION= 2, ERROR= -1; + int state; + + /* We build without RTTI, so dynamic_cast can't be used. */ + enum Type + { + STATEMENT, PREPARED_STATEMENT, STORED_PROCEDURE + }; + + Item_arena(THD *thd); + Item_arena(); + Item_arena(bool init_mem_root); + virtual Type type() const; + virtual ~Item_arena(); + + inline bool is_stmt_prepare() const { return state < PREPARED; } + inline bool is_first_stmt_execute() const { return state == PREPARED; } + 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, @@ -432,7 +487,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 */ @@ -474,20 +529,8 @@ 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. */ - enum Type - { - STATEMENT, - PREPARED_STATEMENT - }; /* This constructor is called when statement is a subobject of THD: @@ -500,34 +543,10 @@ public: /* Assign execution context (note: not all members) of given stmt to self */ void set_statement(Statement *stmt); + void set_n_backup_statement(Statement *stmt, Statement *backup); + void restore_backup_statement(Statement *stmt, Statement *backup); /* 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); }; @@ -760,9 +779,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 @@ -983,33 +1002,6 @@ public: } inline CHARSET_INFO *charset() { return variables.character_set_client; } void update_charset(); - - inline void allocate_temporary_memory_pool_for_ps_preparing() - { - DBUG_ASSERT(current_statement!=0); - /* - We do not want to have in PS memory all that junk, - which will be created by preparation => substitute memory - from original thread pool. - - 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); - init_sql_alloc(&mem_root, - variables.query_alloc_block_size, - variables.query_prealloc_size); - free_list= 0; - } - inline void free_temporary_memory_pool_for_ps_preparing() - { - DBUG_ASSERT(current_statement!=0); - cleanup_items(current_statement->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); - } }; /* Flags for the THD::system_thread (bitmap) variable */ diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 01459d3fc7a..30b06e91082 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -151,7 +151,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->current_arena->is_stmt_prepare()) { if (is_union) { diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 2b6a307092c..be1b7c3377e 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1527,9 +1527,9 @@ 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; return (ref_pointer_array= - (Item **)stmt->alloc(sizeof(Item*) * + (Item **)arena->alloc(sizeof(Item*) * (item_list.elements + select_n_having_items + order_group_num)* 5)) == 0; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 57e4022719e..7c275aa6e6f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1543,6 +1543,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; mysqld_list_fields(thd,&table_list,fields); free_items(thd->free_list); + thd->free_list= 0; break; } #endif @@ -4047,6 +4048,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) } thd->proc_info="freeing items"; free_items(thd->free_list); /* Free strings used by items */ + thd->free_list= 0; lex_end(lex); } DBUG_VOID_RETURN; @@ -4073,6 +4075,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) error= 1; /* Ignore question */ free_items(thd->free_list); /* Free strings used by items */ + thd->free_list= 0; lex_end(lex); return error; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index db904d24bf7..57bb96946ff 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -88,7 +88,6 @@ public: uint param_count; uint last_errno; char last_error[MYSQL_ERRMSG_SIZE]; - bool get_longdata_error; #ifndef EMBEDDED_LIBRARY bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end, uchar *read_pos, String *expanded_query); @@ -102,7 +101,7 @@ public: Prepared_statement(THD *thd_arg); virtual ~Prepared_statement(); void setup_set_params(); - virtual Statement::Type type() const; + virtual Item_arena::Type type() const; }; static void execute_stmt(THD *thd, Prepared_statement *stmt, @@ -133,7 +132,7 @@ find_prepared_statement(THD *thd, ulong id, const char *where, { Statement *stmt= thd->stmt_map.find(id); - if (stmt == 0 || stmt->type() != Statement::PREPARED_STATEMENT) + if (stmt == 0 || stmt->type() != Item_arena::PREPARED_STATEMENT) { char llbuf[22]; my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), 22, llstr(id, llbuf), where); @@ -894,10 +893,8 @@ static int mysql_test_insert(Prepared_statement *stmt, open temporary memory pool for temporary data allocated by derived tables & preparation procedure */ - thd->allocate_temporary_memory_pool_for_ps_preparing(); if (open_and_lock_tables(thd, table_list)) { - thd->free_temporary_memory_pool_for_ps_preparing(); DBUG_RETURN(-1); } @@ -932,7 +929,6 @@ static int mysql_test_insert(Prepared_statement *stmt, res= 0; error: lex->unit.cleanup(); - thd->free_temporary_memory_pool_for_ps_preparing(); DBUG_RETURN(res); } @@ -961,12 +957,6 @@ static int mysql_test_update(Prepared_statement *stmt, if ((res= update_precheck(thd, table_list))) DBUG_RETURN(res); - /* - open temporary memory pool for temporary data allocated by derived - tables & preparation procedure - */ - thd->allocate_temporary_memory_pool_for_ps_preparing(); - if (open_and_lock_tables(thd, table_list)) res= -1; else @@ -986,7 +976,6 @@ static int mysql_test_update(Prepared_statement *stmt, } stmt->lex->unit.cleanup(); } - thd->free_temporary_memory_pool_for_ps_preparing(); /* TODO: here we should send types of placeholders to the client. */ DBUG_RETURN(res); } @@ -1016,12 +1005,6 @@ static int mysql_test_delete(Prepared_statement *stmt, if ((res= delete_precheck(thd, table_list))) DBUG_RETURN(res); - /* - open temporary memory pool for temporary data allocated by derived - tables & preparation procedure - */ - thd->allocate_temporary_memory_pool_for_ps_preparing(); - if (open_and_lock_tables(thd, table_list)) res= -1; else @@ -1029,7 +1012,6 @@ static int mysql_test_delete(Prepared_statement *stmt, res= mysql_prepare_delete(thd, table_list, &lex->select_lex.where); lex->unit.cleanup(); } - thd->free_temporary_memory_pool_for_ps_preparing(); /* TODO: here we should send types of placeholders to the client. */ DBUG_RETURN(res); } @@ -1071,11 +1053,6 @@ static int mysql_test_select(Prepared_statement *stmt, DBUG_RETURN(1); #endif - /* - open temporary memory pool for temporary data allocated by derived - tables & preparation procedure - */ - thd->allocate_temporary_memory_pool_for_ps_preparing(); if (open_and_lock_tables(thd, tables)) { send_error(thd); @@ -1090,33 +1067,30 @@ static int mysql_test_select(Prepared_statement *stmt, send_error(thd); goto err_prep; } - if (lex->describe) - { - if (!text_protocol && send_prep_stmt(stmt, 0)) - goto err_prep; - unit->cleanup(); - } - else + if (!text_protocol) { - if (!text_protocol) + if (lex->describe) + { + if (send_prep_stmt(stmt, 0)) + goto err_prep; + } + else { if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) || - thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0) + thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0) #ifndef EMBEDDED_LIBRARY || net_flush(&thd->net) #endif ) goto err_prep; } - unit->cleanup(); } - thd->free_temporary_memory_pool_for_ps_preparing(); + unit->cleanup(); DBUG_RETURN(0); err_prep: unit->cleanup(); err: - thd->free_temporary_memory_pool_for_ps_preparing(); DBUG_RETURN(1); } @@ -1145,19 +1119,13 @@ static int mysql_test_do_fields(Prepared_statement *stmt, int res= 0; if (tables && (res= check_table_access(thd, SELECT_ACL, tables, 0))) DBUG_RETURN(res); - /* - open temporary memory pool for temporary data allocated by derived - tables & preparation procedure - */ - thd->allocate_temporary_memory_pool_for_ps_preparing(); + if (tables && (res= open_and_lock_tables(thd, tables))) { - thd->free_temporary_memory_pool_for_ps_preparing(); DBUG_RETURN(res); } res= setup_fields(thd, 0, 0, *values, 0, 0, 0); stmt->lex->unit.cleanup(); - thd->free_temporary_memory_pool_for_ps_preparing(); if (res) DBUG_RETURN(-1); DBUG_RETURN(0); @@ -1190,11 +1158,7 @@ static int mysql_test_set_fields(Prepared_statement *stmt, if (tables && (res= check_table_access(thd, SELECT_ACL, tables, 0))) DBUG_RETURN(res); - /* - open temporary memory pool for temporary data allocated by derived - tables & preparation procedure - */ - thd->allocate_temporary_memory_pool_for_ps_preparing(); + if (tables && (res= open_and_lock_tables(thd, tables))) goto error; while ((var= it++)) @@ -1208,7 +1172,6 @@ static int mysql_test_set_fields(Prepared_statement *stmt, } error: stmt->lex->unit.cleanup(); - thd->free_temporary_memory_pool_for_ps_preparing(); DBUG_RETURN(res); } @@ -1233,11 +1196,7 @@ static int select_like_statement_test(Prepared_statement *stmt, THD *thd= stmt->thd; LEX *lex= stmt->lex; int res= 0; - /* - open temporary memory pool for temporary data allocated by derived - tables & preparation procedure - */ - thd->allocate_temporary_memory_pool_for_ps_preparing(); + if (tables && (res= open_and_lock_tables(thd, tables))) goto end; @@ -1250,7 +1209,6 @@ static int select_like_statement_test(Prepared_statement *stmt, } end: lex->unit.cleanup(); - thd->free_temporary_memory_pool_for_ps_preparing(); DBUG_RETURN(res); } @@ -1594,17 +1552,13 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, DBUG_RETURN(1); } - thd->stmt_backup.set_statement(thd); - thd->stmt_backup.set_item_arena(thd); - thd->set_statement(stmt); - thd->set_item_arena(stmt); + thd->set_n_backup_statement(stmt, &thd->stmt_backup); + thd->set_n_backup_item_arena(stmt, &thd->stmt_backup); if (alloc_query(thd, packet, packet_length)) { - stmt->set_statement(thd); - stmt->set_item_arena(thd); - thd->set_statement(&thd->stmt_backup); - thd->set_item_arena(&thd->stmt_backup); + thd->restore_backup_statement(stmt, &thd->stmt_backup); + thd->restore_backup_item_arena(stmt, &thd->stmt_backup); /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); send_error(thd, ER_OUT_OF_RESOURCES); @@ -1613,24 +1567,36 @@ int 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; mysql_init_query(thd, (uchar *) thd->query, thd->query_length); lex= thd->lex; lex->safe_to_cache_query= 0; error= yyparse((void *)thd) || thd->is_fatal_error || - init_param_array(stmt) || - send_prepare_results(stmt, test(name)); + init_param_array(stmt); + /* + While doing context analysis of the query (in send_prepare_results) we + allocate a lot of additional memory: for open tables, JOINs, derived + tables, etc. Let's save a snapshot of current parse tree to the + statement and restore original THD. In cases when some tree + transformation can be reused on execute, we set again thd->mem_root from + stmt->mem_root (see setup_wild for one place where we do that). + */ + thd->restore_backup_item_arena(stmt, &thd->stmt_backup); + + if (!error) + error= send_prepare_results(stmt, test(name)); /* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */ if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),WAIT_PRIOR); lex_end(lex); - stmt->set_statement(thd); - stmt->set_item_arena(thd); - thd->set_statement(&thd->stmt_backup); - thd->set_item_arena(&thd->stmt_backup); - thd->current_statement= 0; + thd->restore_backup_statement(stmt, &thd->stmt_backup); + cleanup_items(stmt->free_list); + close_thread_tables(thd); + free_items(thd->free_list); + thd->free_list= 0; + thd->current_arena= thd; if (error) { @@ -1651,7 +1617,7 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, { sl->prep_where= sl->where; } - + stmt->state= Prepared_statement::PREPARED; } DBUG_RETURN(!stmt); @@ -1765,7 +1731,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) DBUG_PRINT("exec_query:", ("%s", stmt->query)); /* Check if we got an error when sending long data */ - if (stmt->get_longdata_error) + if (stmt->state == Item_arena::ERROR) { send_error(thd, stmt->last_errno, stmt->last_error); DBUG_VOID_RETURN; @@ -1789,6 +1755,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query)) goto set_params_data_err; #endif + DBUG_ASSERT(thd->free_list == NULL); thd->protocol= &thd->protocol_prep; // Switch to binary protocol execute_stmt(thd, stmt, &expanded_query, true); thd->protocol= &thd->protocol_simple; // Use normal protocol @@ -1832,9 +1799,9 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) DBUG_VOID_RETURN; } - thd->free_list= NULL; - thd->stmt_backup.set_statement(thd); - thd->set_statement(stmt); + DBUG_ASSERT(thd->free_list == NULL); + + thd->set_n_backup_statement(stmt, &thd->stmt_backup); if (stmt->set_params_from_vars(stmt, thd->stmt_backup.lex->prepared_stmt_params, &expanded_query)) @@ -1866,11 +1833,7 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, { DBUG_ENTER("execute_stmt"); if (set_context) - { - thd->free_list= NULL; - thd->stmt_backup.set_statement(thd); - thd->set_statement(stmt); - } + thd->set_n_backup_statement(stmt, &thd->stmt_backup); reset_stmt_for_execute(stmt); if (expanded_query->length() && @@ -1880,6 +1843,13 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, my_error(ER_OUTOFMEMORY, 0, expanded_query->length()); DBUG_VOID_RETURN; } + /* + At first execution of prepared statement we will perform logical + transformations of the query tree (i.e. negations elimination). + This should be done permanently on the parse tree of this statement. + */ + if (stmt->state == Item_arena::PREPARED) + thd->current_arena= stmt; if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); @@ -1890,6 +1860,12 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, /* Free Items that were created during this execution of the PS. */ free_items(thd->free_list); + thd->free_list= 0; + if (stmt->state == Item_arena::PREPARED) + { + thd->current_arena= thd; + stmt->state= Item_arena::EXECUTED; + } cleanup_items(stmt->free_list); reset_stmt_params(stmt); close_thread_tables(thd); // to close derived tables @@ -1927,7 +1903,7 @@ void mysql_stmt_reset(THD *thd, char *packet) SEND_ERROR))) DBUG_VOID_RETURN; - stmt->get_longdata_error= 0; + stmt->state= Item_arena::PREPARED; /* Clear parameters from data which could be set by @@ -2015,7 +1991,7 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) if (param_number >= stmt->param_count) { /* Error will be sent in execute call */ - stmt->get_longdata_error= 1; + stmt->state= Item_arena::ERROR; stmt->last_errno= ER_WRONG_ARGUMENTS; sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS), "mysql_stmt_send_long_data"); @@ -2026,10 +2002,15 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) param= stmt->param_array[param_number]; #ifndef EMBEDDED_LIBRARY - param->set_longdata(packet, (ulong) (packet_end - packet)); + if (param->set_longdata(packet, (ulong) (packet_end - packet))) #else - param->set_longdata(thd->extra_data, thd->extra_length); + if (param->set_longdata(thd->extra_data, thd->extra_length)) #endif + { + stmt->state= Item_arena::ERROR; + stmt->last_errno= ER_OUTOFMEMORY; + sprintf(stmt->last_error, ER(ER_OUTOFMEMORY), 0); + } DBUG_VOID_RETURN; } @@ -2039,8 +2020,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg) thd(thd_arg), param_array(0), param_count(0), - last_errno(0), - get_longdata_error(0) + last_errno(0) { *last_error= '\0'; } @@ -2074,7 +2054,7 @@ Prepared_statement::~Prepared_statement() } -Statement::Type Prepared_statement::type() const +Item_arena::Type Prepared_statement::type() const { return PREPARED_STATEMENT; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c56645e06b9..2478ec0eb7b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4377,25 +4377,39 @@ COND *eliminate_not_funcs(THD *thd, COND *cond) static COND * optimize_cond(THD *thd, COND *conds, Item::cond_result *cond_value) { + SELECT_LEX *select= thd->lex->current_select; DBUG_ENTER("optimize_cond"); - if (!conds) + if (conds) + { + DBUG_EXECUTE("where", print_where(conds, "original");); + /* Eliminate NOT operators; in case of PS/SP do it once */ + if (thd->current_arena->is_first_stmt_execute()) + { + Item_arena *arena= thd->current_arena, backup; + thd->set_n_backup_item_arena(arena, &backup); + conds= eliminate_not_funcs(thd, conds); + select->prep_where= conds->copy_andor_structure(thd); + thd->restore_backup_item_arena(arena, &backup); + } + else + conds= eliminate_not_funcs(thd, conds); + DBUG_EXECUTE("where", print_where(conds, "after negation elimination");); + + /* change field = field to field = const for each found field = const */ + propagate_cond_constants((I_List<COND_CMP> *) 0, conds, conds); + /* + Remove all instances of item == item + Remove all and-levels where CONST item != CONST item + */ + DBUG_EXECUTE("where", print_where(conds, "after const change");); + conds= remove_eq_conds(thd, conds, cond_value); + DBUG_EXECUTE("info", print_where(conds, "after remove");); + } + else { *cond_value= Item::COND_TRUE; - DBUG_RETURN(conds); - } - DBUG_EXECUTE("where",print_where(conds,"original");); - /* eliminate NOT operators */ - conds= eliminate_not_funcs(thd, conds); - DBUG_EXECUTE("where", print_where(conds, "after negation elimination");); - /* change field = field to field = const for each found field = const */ - propagate_cond_constants((I_List<COND_CMP> *) 0,conds,conds); - /* - Remove all instances of item == item - Remove all and-levels where CONST item != CONST item - */ - DBUG_EXECUTE("where",print_where(conds,"after const change");); - conds= remove_eq_conds(thd, conds, cond_value) ; - DBUG_EXECUTE("info",print_where(conds,"after remove");); + select->prep_where= 0; + } DBUG_RETURN(conds); } diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 70c05489f82..1e8c6576dec 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -287,24 +287,23 @@ 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, backup; + if (arena->is_stmt_prepare()) + 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->is_stmt_prepare()) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(-1); } } - if (stmt) + if (arena->is_stmt_prepare()) { - 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); |