Commit 568c6e85 authored by konstantin@mysql.com's avatar konstantin@mysql.com

Fix for bug#4912 "mysqld crashs in case a statement is executed

 a second time". The bug was caused by incompatibility of
negations elimination algorithm and PS: during first statement 
execute a subtree with negation was replaced with equivalent 
subtree without NOTs.
The problem was that although this transformation was permanent, 
items of the new subtree were created in execute-local memory.
The patch adds means to check if it is the first execute of a
prepared statement, and if this is the case, to allocate items
in memory of the prepared statement.
The implementation:
- backports Item_arena from 5.0
- adds Item_arena::is_stmt_prepare(), 
  Item_arena::is_first_stmt_execute().
- deletes THD::allocate_temporary_pool_for_ps_preparing(),
  THD::free_temporary_pool_for_ps_preparing(); they
  were redundant.
and adds a few invariants:
- thd->free_list never contains junk (= freed items)
- thd->current_arena is never null. If there is no
  prepared statement, it points at the thd. 
The rest of the patch contains mainly mechanical changes and
cleanups.
parent b9dbef8e
...@@ -219,3 +219,10 @@ Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length I ...@@ -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 t1 MyISAM 9 Dynamic 0 0 0 4294967295 1024 0 NULL # # # latin1_swedish_ci NULL
deallocate prepare stmt1 ; deallocate prepare stmt1 ;
drop table t1; 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;
...@@ -206,3 +206,15 @@ execute stmt1; ...@@ -206,3 +206,15 @@ execute stmt1;
show table status from test like 't1%' ; show table status from test like 't1%' ;
deallocate prepare stmt1 ; deallocate prepare stmt1 ;
drop table t1; 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;
...@@ -589,10 +589,8 @@ bool Item_in_optimizer::fix_left(THD *thd, ...@@ -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 => 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. 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]); cache->store(args[0]);
if (cache->cols() == 1) if (cache->cols() == 1)
{ {
......
...@@ -125,7 +125,6 @@ bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref) ...@@ -125,7 +125,6 @@ bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref)
{ {
DBUG_ASSERT(fixed == 0); DBUG_ASSERT(fixed == 0);
engine->set_thd((thd= thd_param)); engine->set_thd((thd= thd_param));
stmt= thd->current_statement;
char const *save_where= thd->where; char const *save_where= thd->where;
int res; int res;
...@@ -306,7 +305,10 @@ Item_singlerow_subselect::select_transformer(JOIN *join) ...@@ -306,7 +305,10 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
return RES_OK; return RES_OK;
SELECT_LEX *select_lex= join->select_lex; 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() && if (!select_lex->master_unit()->first_select()->next_select() &&
!select_lex->table_list.elements && !select_lex->table_list.elements &&
...@@ -341,8 +343,8 @@ Item_singlerow_subselect::select_transformer(JOIN *join) ...@@ -341,8 +343,8 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
if (join->conds || join->having) if (join->conds || join->having)
{ {
Item *cond; Item *cond;
if (stmt) if (arena->is_stmt_prepare())
thd->set_n_backup_item_arena(stmt, &backup); thd->set_n_backup_item_arena(arena, &backup);
if (!join->having) if (!join->having)
cond= join->conds; cond= join->conds;
...@@ -355,15 +357,15 @@ Item_singlerow_subselect::select_transformer(JOIN *join) ...@@ -355,15 +357,15 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
new Item_null()))) new Item_null())))
goto err; goto err;
} }
if (stmt) if (arena->is_stmt_prepare())
thd->restore_backup_item_arena(stmt, &backup); thd->restore_backup_item_arena(arena, &backup);
return RES_REDUCE; return RES_REDUCE;
} }
return RES_OK; return RES_OK;
err: err:
if (stmt) if (arena->is_stmt_prepare())
thd->restore_backup_item_arena(stmt, &backup); thd->restore_backup_item_arena(arena, &backup);
return RES_ERROR; return RES_ERROR;
} }
...@@ -640,11 +642,11 @@ Item_in_subselect::single_value_transformer(JOIN *join, ...@@ -640,11 +642,11 @@ Item_in_subselect::single_value_transformer(JOIN *join,
} }
SELECT_LEX *select_lex= join->select_lex; SELECT_LEX *select_lex= join->select_lex;
Statement backup; Item_arena *arena= join->thd->current_arena, backup;
thd->where= "scalar IN/ALL/ANY subquery"; thd->where= "scalar IN/ALL/ANY subquery";
if (stmt) if (arena->is_stmt_prepare())
thd->set_n_backup_item_arena(stmt, &backup); thd->set_n_backup_item_arena(arena, &backup);
if (select_lex->item_list.elements > 1) if (select_lex->item_list.elements > 1)
{ {
...@@ -857,21 +859,21 @@ Item_in_subselect::single_value_transformer(JOIN *join, ...@@ -857,21 +859,21 @@ Item_in_subselect::single_value_transformer(JOIN *join,
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_SELECT_REDUCED, warn_buff); ER_SELECT_REDUCED, warn_buff);
} }
if (stmt) if (arena->is_stmt_prepare())
thd->restore_backup_item_arena(stmt, &backup); thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(RES_REDUCE); DBUG_RETURN(RES_REDUCE);
} }
} }
} }
ok: ok:
if (stmt) if (arena->is_stmt_prepare())
thd->restore_backup_item_arena(stmt, &backup); thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(RES_OK); DBUG_RETURN(RES_OK);
err: err:
if (stmt) if (arena->is_stmt_prepare())
thd->restore_backup_item_arena(stmt, &backup); thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(RES_ERROR); DBUG_RETURN(RES_ERROR);
} }
...@@ -885,12 +887,12 @@ Item_in_subselect::row_value_transformer(JOIN *join) ...@@ -885,12 +887,12 @@ Item_in_subselect::row_value_transformer(JOIN *join)
{ {
DBUG_RETURN(RES_OK); DBUG_RETURN(RES_OK);
} }
Statement backup; Item_arena *arena= join->thd->current_arena, backup;
Item *item= 0; Item *item= 0;
thd->where= "row IN/ALL/ANY subquery"; thd->where= "row IN/ALL/ANY subquery";
if (stmt) if (arena->is_stmt_prepare())
thd->set_n_backup_item_arena(stmt, &backup); thd->set_n_backup_item_arena(arena, &backup);
SELECT_LEX *select_lex= join->select_lex; SELECT_LEX *select_lex= join->select_lex;
...@@ -974,13 +976,13 @@ Item_in_subselect::row_value_transformer(JOIN *join) ...@@ -974,13 +976,13 @@ Item_in_subselect::row_value_transformer(JOIN *join)
if (join->conds->fix_fields(thd, join->tables_list, 0)) if (join->conds->fix_fields(thd, join->tables_list, 0))
goto err; goto err;
} }
if (stmt) if (arena->is_stmt_prepare())
thd->restore_backup_item_arena(stmt, &backup); thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(RES_OK); DBUG_RETURN(RES_OK);
err: err:
if (stmt) if (arena->is_stmt_prepare())
thd->restore_backup_item_arena(stmt, &backup); thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(RES_ERROR); DBUG_RETURN(RES_ERROR);
} }
......
...@@ -36,8 +36,6 @@ class Item_subselect :public Item_result_field ...@@ -36,8 +36,6 @@ class Item_subselect :public Item_result_field
protected: protected:
/* thread handler, will be assigned in fix_fields only */ /* thread handler, will be assigned in fix_fields only */
THD *thd; THD *thd;
/* prepared statement, or 0 */
Statement *stmt;
/* substitution instead of subselect in case of optimization */ /* substitution instead of subselect in case of optimization */
Item *substitution; Item *substitution;
/* unit of subquery */ /* unit of subquery */
......
...@@ -64,28 +64,28 @@ Item_sum::Item_sum(THD *thd, Item_sum *item): ...@@ -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()) (arguments can be rewritten in get_tmp_table_item())
SYNOPSIS SYNOPSIS
Item_sum::save_args_for_prepared_statements() Item_sum::save_args_for_prepared_statement()
thd - thread handler thd - thread handler
RETURN RETURN
0 - OK 0 - OK
1 - Error 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) if (thd->current_arena->is_stmt_prepare())
return save_args(thd->current_statement); return save_args(thd->current_arena);
return 0; 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; return 1;
memcpy(args_copy, args, sizeof(Item*)*arg_count); memcpy(args_copy, args, sizeof(Item*)*arg_count);
return 0; return 0;
...@@ -214,7 +214,7 @@ Item_sum_num::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) ...@@ -214,7 +214,7 @@ Item_sum_num::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
{ {
DBUG_ASSERT(fixed == 0); DBUG_ASSERT(fixed == 0);
if (save_args_for_prepared_statements(thd)) if (save_args_for_prepared_statement(thd))
return 1; return 1;
if (!thd->allow_sum_func) if (!thd->allow_sum_func)
...@@ -248,7 +248,7 @@ Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) ...@@ -248,7 +248,7 @@ Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
{ {
DBUG_ASSERT(fixed == 0); DBUG_ASSERT(fixed == 0);
if (save_args_for_prepared_statements(thd)) if (save_args_for_prepared_statement(thd))
return 1; return 1;
Item *item= args[0]; Item *item= args[0];
...@@ -1947,7 +1947,7 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) ...@@ -1947,7 +1947,7 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
{ {
DBUG_ASSERT(fixed == 0); DBUG_ASSERT(fixed == 0);
if (save_args_for_prepared_statements(thd)) if (save_args_for_prepared_statement(thd))
return 1; return 1;
uint i; /* for loop variable */ uint i; /* for loop variable */
......
...@@ -92,8 +92,8 @@ class Item_sum :public Item_result_field ...@@ -92,8 +92,8 @@ class Item_sum :public Item_result_field
virtual bool setup(THD *thd) {return 0;} virtual bool setup(THD *thd) {return 0;}
virtual void make_unique() {} virtual void make_unique() {}
Item *get_tmp_table_item(THD *thd); Item *get_tmp_table_item(THD *thd);
bool save_args_for_prepared_statements(THD *); bool save_args_for_prepared_statement(THD *);
bool save_args(Statement* stmt); bool save_args(Item_arena *arena);
bool walk (Item_processor processor, byte *argument); bool walk (Item_processor processor, byte *argument);
}; };
......
...@@ -295,7 +295,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout); ...@@ -295,7 +295,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout);
struct st_table; struct st_table;
class THD; class THD;
class Statement; class Item_arena;
/* Struct to handle simple linked lists */ /* Struct to handle simple linked lists */
......
...@@ -2188,14 +2188,15 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, ...@@ -2188,14 +2188,15 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
{ {
if (!wild_num) if (!wild_num)
return 0; 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 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 temporary mem_root to statement mem root to save changes of SELECT list
*/ */
if (stmt) if (arena->is_stmt_prepare())
thd->set_n_backup_item_arena(stmt, &backup); thd->set_n_backup_item_arena(arena, &backup);
reg2 Item *item; reg2 Item *item;
List_iterator<Item> it(fields); List_iterator<Item> it(fields);
while ( wild_num && (item= it++)) while ( wild_num && (item= it++))
...@@ -2219,8 +2220,8 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, ...@@ -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, else if (insert_fields(thd,tables,((Item_field*) item)->db_name,
((Item_field*) item)->table_name, &it)) ((Item_field*) item)->table_name, &it))
{ {
if (stmt) if (arena->is_stmt_prepare())
thd->restore_backup_item_arena(stmt, &backup); thd->restore_backup_item_arena(arena, &backup);
return (-1); return (-1);
} }
if (sum_func_list) if (sum_func_list)
...@@ -2235,8 +2236,8 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, ...@@ -2235,8 +2236,8 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
wild_num--; wild_num--;
} }
} }
if (stmt) if (arena->is_stmt_prepare())
thd->restore_backup_item_arena(stmt, &backup); thd->restore_backup_item_arena(arena, &backup);
return 0; return 0;
} }
...@@ -2449,7 +2450,7 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, ...@@ -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) int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
{ {
table_map not_null_tables= 0; table_map not_null_tables= 0;
Statement *stmt= thd->current_statement, backup; Item_arena *arena= thd->current_arena, backup;
DBUG_ENTER("setup_conds"); DBUG_ENTER("setup_conds");
thd->set_query_id=1; thd->set_query_id=1;
...@@ -2488,12 +2489,12 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) ...@@ -2488,12 +2489,12 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
!(specialflag & SPECIAL_NO_NEW_FUNC))) !(specialflag & SPECIAL_NO_NEW_FUNC)))
{ {
table->outer_join= 0; table->outer_join= 0;
if (stmt) if (arena->is_stmt_prepare())
thd->set_n_backup_item_arena(stmt, &backup); thd->set_n_backup_item_arena(arena, &backup);
*conds= and_conds(*conds, table->on_expr); *conds= and_conds(*conds, table->on_expr);
table->on_expr=0; table->on_expr=0;
if (stmt) if (arena->is_stmt_prepare())
thd->restore_backup_item_arena(stmt, &backup); thd->restore_backup_item_arena(arena, &backup);
if ((*conds) && !(*conds)->fixed && if ((*conds) && !(*conds)->fixed &&
(*conds)->fix_fields(thd, tables, conds)) (*conds)->fix_fields(thd, tables, conds))
DBUG_RETURN(1); DBUG_RETURN(1);
...@@ -2501,8 +2502,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) ...@@ -2501,8 +2502,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
} }
if (table->natural_join) if (table->natural_join)
{ {
if (stmt) if (arena->is_stmt_prepare())
thd->set_n_backup_item_arena(stmt, &backup); thd->set_n_backup_item_arena(arena, &backup);
/* Make a join of all fields with have the same name */ /* Make a join of all fields with have the same name */
TABLE *t1= table->table; TABLE *t1= table->table;
TABLE *t2= table->natural_join->table; TABLE *t2= table->natural_join->table;
...@@ -2543,8 +2544,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) ...@@ -2543,8 +2544,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
{ {
*conds= and_conds(*conds, cond_and); *conds= and_conds(*conds, cond_and);
// fix_fields() should be made with temporary memory pool // fix_fields() should be made with temporary memory pool
if (stmt) if (arena->is_stmt_prepare())
thd->restore_backup_item_arena(stmt, &backup); thd->restore_backup_item_arena(arena, &backup);
if (*conds && !(*conds)->fixed) if (*conds && !(*conds)->fixed)
{ {
if ((*conds)->fix_fields(thd, tables, conds)) if ((*conds)->fix_fields(thd, tables, conds))
...@@ -2555,8 +2556,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **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); table->on_expr= and_conds(table->on_expr, cond_and);
// fix_fields() should be made with temporary memory pool // fix_fields() should be made with temporary memory pool
if (stmt) if (arena->is_stmt_prepare())
thd->restore_backup_item_arena(stmt, &backup); thd->restore_backup_item_arena(arena, &backup);
if (table->on_expr && !table->on_expr->fixed) if (table->on_expr && !table->on_expr->fixed)
{ {
if (table->on_expr->fix_fields(thd, tables, &table->on_expr)) 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) ...@@ -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 We are in prepared statement preparation code => we should store
...@@ -2580,8 +2581,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) ...@@ -2580,8 +2581,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
DBUG_RETURN(test(thd->net.report_error)); DBUG_RETURN(test(thd->net.report_error));
err: err:
if (stmt) if (arena->is_stmt_prepare())
thd->restore_backup_item_arena(stmt, &backup); thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(1); DBUG_RETURN(1);
} }
......
...@@ -155,7 +155,7 @@ bool foreign_key_prefix(Key *a, Key *b) ...@@ -155,7 +155,7 @@ bool foreign_key_prefix(Key *a, Key *b)
** Thread specific functions ** 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), last_insert_id_used(0),
insert_id_used(0), rand_used(0), time_zone_used(0), insert_id_used(0), rand_used(0), time_zone_used(0),
in_lock_tables(0), global_read_lock(0), bootstrap(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) ...@@ -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 functions
*/ */
Statement::Statement(THD *thd) Statement::Statement(THD *thd)
:id(++thd->statement_id_counter), :Item_arena(thd),
id(++thd->statement_id_counter),
set_query_id(1), set_query_id(1),
allow_sum_func(0), allow_sum_func(0),
lex(&main_lex), lex(&main_lex),
query(0), query(0),
query_length(0), query_length(0)
free_list(0)
{ {
name.str= NULL; 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() ...@@ -1332,14 +1368,12 @@ Statement::Statement()
allow_sum_func(0), /* initialized later */ allow_sum_func(0), /* initialized later */
lex(&main_lex), lex(&main_lex),
query(0), /* these two are set */ query(0), /* these two are set */
query_length(0), /* in alloc_query() */ query_length(0) /* in alloc_query() */
free_list(0)
{ {
bzero((char *) &mem_root, sizeof(mem_root));
} }
Statement::Type Statement::type() const Item_arena::Type Statement::type() const
{ {
return STATEMENT; return STATEMENT;
} }
...@@ -1356,14 +1390,29 @@ void Statement::set_statement(Statement *stmt) ...@@ -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); backup->set_item_arena(this);
set_item_arena(set); 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->set_item_arena(this);
set_item_arena(backup); set_item_arena(backup);
...@@ -1371,10 +1420,11 @@ void Statement::restore_backup_item_arena(Statement *set, Statement *backup) ...@@ -1371,10 +1420,11 @@ void Statement::restore_backup_item_arena(Statement *set, Statement *backup)
init_alloc_root(&backup->mem_root, 0, 0); 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; mem_root= set->mem_root;
free_list= set->free_list; free_list= set->free_list;
state= set->state;
} }
Statement::~Statement() Statement::~Statement()
......
...@@ -418,6 +418,61 @@ struct system_variables ...@@ -418,6 +418,61 @@ struct system_variables
void free_tmp_table(THD *thd, TABLE *entry); 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. State of a single command executed against this connection.
One connection can contain a lot of simultaneously running statements, One connection can contain a lot of simultaneously running statements,
...@@ -432,7 +487,7 @@ void free_tmp_table(THD *thd, TABLE *entry); ...@@ -432,7 +487,7 @@ void free_tmp_table(THD *thd, TABLE *entry);
be used explicitly. be used explicitly.
*/ */
class Statement class Statement: public Item_arena
{ {
Statement(const Statement &rhs); /* not implemented: */ Statement(const Statement &rhs); /* not implemented: */
Statement &operator=(const Statement &rhs); /* non-copyable */ Statement &operator=(const Statement &rhs); /* non-copyable */
...@@ -474,20 +529,8 @@ class Statement ...@@ -474,20 +529,8 @@ class Statement
*/ */
char *query; char *query;
uint32 query_length; // current query length 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: 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: This constructor is called when statement is a subobject of THD:
...@@ -500,34 +543,10 @@ class Statement ...@@ -500,34 +543,10 @@ class Statement
/* Assign execution context (note: not all members) of given stmt to self */ /* Assign execution context (note: not all members) of given stmt to self */
void set_statement(Statement *stmt); 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 */ /* return class type */
virtual Type type() const; 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 @@ class THD :public ilink, ...@@ -760,9 +779,9 @@ class THD :public ilink,
Vio* active_vio; Vio* active_vio;
#endif #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 next_insert_id is set on SET INSERT_ID= #. This is used as the next
generated auto_increment value in handler.cc generated auto_increment value in handler.cc
...@@ -983,33 +1002,6 @@ class THD :public ilink, ...@@ -983,33 +1002,6 @@ class THD :public ilink,
} }
inline CHARSET_INFO *charset() { return variables.character_set_client; } inline CHARSET_INFO *charset() { return variables.character_set_client; }
void update_charset(); 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 */ /* Flags for the THD::system_thread (bitmap) variable */
......
...@@ -151,7 +151,7 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, ...@@ -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 if it is preparation PS only then we do not need real data and we
can skip execution (and parameters is not defined, too) can skip execution (and parameters is not defined, too)
*/ */
if (!thd->current_statement) if (! thd->current_arena->is_stmt_prepare())
{ {
if (is_union) if (is_union)
{ {
......
...@@ -1527,9 +1527,9 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) ...@@ -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 We have to create array in prepared statement memory if it is
prepared statement prepared statement
*/ */
Statement *stmt= thd->current_statement ? thd->current_statement : thd; Item_arena *arena= thd->current_arena;
return (ref_pointer_array= return (ref_pointer_array=
(Item **)stmt->alloc(sizeof(Item*) * (Item **)arena->alloc(sizeof(Item*) *
(item_list.elements + (item_list.elements +
select_n_having_items + select_n_having_items +
order_group_num)* 5)) == 0; order_group_num)* 5)) == 0;
......
...@@ -1543,6 +1543,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ...@@ -1543,6 +1543,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break; break;
mysqld_list_fields(thd,&table_list,fields); mysqld_list_fields(thd,&table_list,fields);
free_items(thd->free_list); free_items(thd->free_list);
thd->free_list= 0;
break; break;
} }
#endif #endif
...@@ -4047,6 +4048,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) ...@@ -4047,6 +4048,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
} }
thd->proc_info="freeing items"; thd->proc_info="freeing items";
free_items(thd->free_list); /* Free strings used by items */ free_items(thd->free_list); /* Free strings used by items */
thd->free_list= 0;
lex_end(lex); lex_end(lex);
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
...@@ -4073,6 +4075,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) ...@@ -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)) all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
error= 1; /* Ignore question */ error= 1; /* Ignore question */
free_items(thd->free_list); /* Free strings used by items */ free_items(thd->free_list); /* Free strings used by items */
thd->free_list= 0;
lex_end(lex); lex_end(lex);
return error; return error;
......
...@@ -88,7 +88,6 @@ class Prepared_statement: public Statement ...@@ -88,7 +88,6 @@ class Prepared_statement: public Statement
uint param_count; uint param_count;
uint last_errno; uint last_errno;
char last_error[MYSQL_ERRMSG_SIZE]; char last_error[MYSQL_ERRMSG_SIZE];
bool get_longdata_error;
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end, bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end,
uchar *read_pos, String *expanded_query); uchar *read_pos, String *expanded_query);
...@@ -102,7 +101,7 @@ class Prepared_statement: public Statement ...@@ -102,7 +101,7 @@ class Prepared_statement: public Statement
Prepared_statement(THD *thd_arg); Prepared_statement(THD *thd_arg);
virtual ~Prepared_statement(); virtual ~Prepared_statement();
void setup_set_params(); void setup_set_params();
virtual Statement::Type type() const; virtual Item_arena::Type type() const;
}; };
static void execute_stmt(THD *thd, Prepared_statement *stmt, static void execute_stmt(THD *thd, Prepared_statement *stmt,
...@@ -133,7 +132,7 @@ find_prepared_statement(THD *thd, ulong id, const char *where, ...@@ -133,7 +132,7 @@ find_prepared_statement(THD *thd, ulong id, const char *where,
{ {
Statement *stmt= thd->stmt_map.find(id); 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]; char llbuf[22];
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), 22, llstr(id, llbuf), where); 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, ...@@ -894,10 +893,8 @@ static int mysql_test_insert(Prepared_statement *stmt,
open temporary memory pool for temporary data allocated by derived open temporary memory pool for temporary data allocated by derived
tables & preparation procedure tables & preparation procedure
*/ */
thd->allocate_temporary_memory_pool_for_ps_preparing();
if (open_and_lock_tables(thd, table_list)) if (open_and_lock_tables(thd, table_list))
{ {
thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
...@@ -932,7 +929,6 @@ static int mysql_test_insert(Prepared_statement *stmt, ...@@ -932,7 +929,6 @@ static int mysql_test_insert(Prepared_statement *stmt,
res= 0; res= 0;
error: error:
lex->unit.cleanup(); lex->unit.cleanup();
thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(res); DBUG_RETURN(res);
} }
...@@ -961,12 +957,6 @@ static int mysql_test_update(Prepared_statement *stmt, ...@@ -961,12 +957,6 @@ static int mysql_test_update(Prepared_statement *stmt,
if ((res= update_precheck(thd, table_list))) if ((res= update_precheck(thd, table_list)))
DBUG_RETURN(res); 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)) if (open_and_lock_tables(thd, table_list))
res= -1; res= -1;
else else
...@@ -986,7 +976,6 @@ static int mysql_test_update(Prepared_statement *stmt, ...@@ -986,7 +976,6 @@ static int mysql_test_update(Prepared_statement *stmt,
} }
stmt->lex->unit.cleanup(); stmt->lex->unit.cleanup();
} }
thd->free_temporary_memory_pool_for_ps_preparing();
/* TODO: here we should send types of placeholders to the client. */ /* TODO: here we should send types of placeholders to the client. */
DBUG_RETURN(res); DBUG_RETURN(res);
} }
...@@ -1016,12 +1005,6 @@ static int mysql_test_delete(Prepared_statement *stmt, ...@@ -1016,12 +1005,6 @@ static int mysql_test_delete(Prepared_statement *stmt,
if ((res= delete_precheck(thd, table_list))) if ((res= delete_precheck(thd, table_list)))
DBUG_RETURN(res); 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)) if (open_and_lock_tables(thd, table_list))
res= -1; res= -1;
else else
...@@ -1029,7 +1012,6 @@ static int mysql_test_delete(Prepared_statement *stmt, ...@@ -1029,7 +1012,6 @@ static int mysql_test_delete(Prepared_statement *stmt,
res= mysql_prepare_delete(thd, table_list, &lex->select_lex.where); res= mysql_prepare_delete(thd, table_list, &lex->select_lex.where);
lex->unit.cleanup(); lex->unit.cleanup();
} }
thd->free_temporary_memory_pool_for_ps_preparing();
/* TODO: here we should send types of placeholders to the client. */ /* TODO: here we should send types of placeholders to the client. */
DBUG_RETURN(res); DBUG_RETURN(res);
} }
...@@ -1071,11 +1053,6 @@ static int mysql_test_select(Prepared_statement *stmt, ...@@ -1071,11 +1053,6 @@ static int mysql_test_select(Prepared_statement *stmt,
DBUG_RETURN(1); DBUG_RETURN(1);
#endif #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)) if (open_and_lock_tables(thd, tables))
{ {
send_error(thd); send_error(thd);
...@@ -1090,33 +1067,30 @@ static int mysql_test_select(Prepared_statement *stmt, ...@@ -1090,33 +1067,30 @@ static int mysql_test_select(Prepared_statement *stmt,
send_error(thd); send_error(thd);
goto err_prep; goto err_prep;
} }
if (lex->describe) if (!text_protocol)
{
if (!text_protocol && send_prep_stmt(stmt, 0))
goto err_prep;
unit->cleanup();
}
else
{ {
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) || 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 #ifndef EMBEDDED_LIBRARY
|| net_flush(&thd->net) || net_flush(&thd->net)
#endif #endif
) )
goto err_prep; goto err_prep;
} }
unit->cleanup();
} }
thd->free_temporary_memory_pool_for_ps_preparing(); unit->cleanup();
DBUG_RETURN(0); DBUG_RETURN(0);
err_prep: err_prep:
unit->cleanup(); unit->cleanup();
err: err:
thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(1); DBUG_RETURN(1);
} }
...@@ -1145,19 +1119,13 @@ static int mysql_test_do_fields(Prepared_statement *stmt, ...@@ -1145,19 +1119,13 @@ static int mysql_test_do_fields(Prepared_statement *stmt,
int res= 0; int res= 0;
if (tables && (res= check_table_access(thd, SELECT_ACL, tables, 0))) if (tables && (res= check_table_access(thd, SELECT_ACL, tables, 0)))
DBUG_RETURN(res); 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))) if (tables && (res= open_and_lock_tables(thd, tables)))
{ {
thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(res); DBUG_RETURN(res);
} }
res= setup_fields(thd, 0, 0, *values, 0, 0, 0); res= setup_fields(thd, 0, 0, *values, 0, 0, 0);
stmt->lex->unit.cleanup(); stmt->lex->unit.cleanup();
thd->free_temporary_memory_pool_for_ps_preparing();
if (res) if (res)
DBUG_RETURN(-1); DBUG_RETURN(-1);
DBUG_RETURN(0); DBUG_RETURN(0);
...@@ -1190,11 +1158,7 @@ static int mysql_test_set_fields(Prepared_statement *stmt, ...@@ -1190,11 +1158,7 @@ static int mysql_test_set_fields(Prepared_statement *stmt,
if (tables && (res= check_table_access(thd, SELECT_ACL, tables, 0))) if (tables && (res= check_table_access(thd, SELECT_ACL, tables, 0)))
DBUG_RETURN(res); 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))) if (tables && (res= open_and_lock_tables(thd, tables)))
goto error; goto error;
while ((var= it++)) while ((var= it++))
...@@ -1208,7 +1172,6 @@ static int mysql_test_set_fields(Prepared_statement *stmt, ...@@ -1208,7 +1172,6 @@ static int mysql_test_set_fields(Prepared_statement *stmt,
} }
error: error:
stmt->lex->unit.cleanup(); stmt->lex->unit.cleanup();
thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(res); DBUG_RETURN(res);
} }
...@@ -1233,11 +1196,7 @@ static int select_like_statement_test(Prepared_statement *stmt, ...@@ -1233,11 +1196,7 @@ static int select_like_statement_test(Prepared_statement *stmt,
THD *thd= stmt->thd; THD *thd= stmt->thd;
LEX *lex= stmt->lex; LEX *lex= stmt->lex;
int res= 0; 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))) if (tables && (res= open_and_lock_tables(thd, tables)))
goto end; goto end;
...@@ -1250,7 +1209,6 @@ static int select_like_statement_test(Prepared_statement *stmt, ...@@ -1250,7 +1209,6 @@ static int select_like_statement_test(Prepared_statement *stmt,
} }
end: end:
lex->unit.cleanup(); lex->unit.cleanup();
thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(res); DBUG_RETURN(res);
} }
...@@ -1594,17 +1552,13 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, ...@@ -1594,17 +1552,13 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
DBUG_RETURN(1); DBUG_RETURN(1);
} }
thd->stmt_backup.set_statement(thd); thd->set_n_backup_statement(stmt, &thd->stmt_backup);
thd->stmt_backup.set_item_arena(thd); thd->set_n_backup_item_arena(stmt, &thd->stmt_backup);
thd->set_statement(stmt);
thd->set_item_arena(stmt);
if (alloc_query(thd, packet, packet_length)) if (alloc_query(thd, packet, packet_length))
{ {
stmt->set_statement(thd); thd->restore_backup_statement(stmt, &thd->stmt_backup);
stmt->set_item_arena(thd); thd->restore_backup_item_arena(stmt, &thd->stmt_backup);
thd->set_statement(&thd->stmt_backup);
thd->set_item_arena(&thd->stmt_backup);
/* Statement map deletes statement on erase */ /* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt); thd->stmt_map.erase(stmt);
send_error(thd, ER_OUT_OF_RESOURCES); send_error(thd, ER_OUT_OF_RESOURCES);
...@@ -1613,24 +1567,36 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, ...@@ -1613,24 +1567,36 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
mysql_log.write(thd, COM_PREPARE, "%s", packet); 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); mysql_init_query(thd, (uchar *) thd->query, thd->query_length);
lex= thd->lex; lex= thd->lex;
lex->safe_to_cache_query= 0; lex->safe_to_cache_query= 0;
error= yyparse((void *)thd) || thd->is_fatal_error || error= yyparse((void *)thd) || thd->is_fatal_error ||
init_param_array(stmt) || init_param_array(stmt);
send_prepare_results(stmt, test(name)); /*
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 */ /* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),WAIT_PRIOR); my_pthread_setprio(pthread_self(),WAIT_PRIOR);
lex_end(lex); lex_end(lex);
stmt->set_statement(thd); thd->restore_backup_statement(stmt, &thd->stmt_backup);
stmt->set_item_arena(thd); cleanup_items(stmt->free_list);
thd->set_statement(&thd->stmt_backup); close_thread_tables(thd);
thd->set_item_arena(&thd->stmt_backup); free_items(thd->free_list);
thd->current_statement= 0; thd->free_list= 0;
thd->current_arena= thd;
if (error) if (error)
{ {
...@@ -1651,7 +1617,7 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, ...@@ -1651,7 +1617,7 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
{ {
sl->prep_where= sl->where; sl->prep_where= sl->where;
} }
stmt->state= Prepared_statement::PREPARED;
} }
DBUG_RETURN(!stmt); DBUG_RETURN(!stmt);
...@@ -1765,7 +1731,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) ...@@ -1765,7 +1731,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
DBUG_PRINT("exec_query:", ("%s", stmt->query)); DBUG_PRINT("exec_query:", ("%s", stmt->query));
/* Check if we got an error when sending long data */ /* 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); send_error(thd, stmt->last_errno, stmt->last_error);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
...@@ -1789,6 +1755,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) ...@@ -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)) if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query))
goto set_params_data_err; goto set_params_data_err;
#endif #endif
DBUG_ASSERT(thd->free_list == NULL);
thd->protocol= &thd->protocol_prep; // Switch to binary protocol thd->protocol= &thd->protocol_prep; // Switch to binary protocol
execute_stmt(thd, stmt, &expanded_query, true); execute_stmt(thd, stmt, &expanded_query, true);
thd->protocol= &thd->protocol_simple; // Use normal protocol thd->protocol= &thd->protocol_simple; // Use normal protocol
...@@ -1832,9 +1799,9 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) ...@@ -1832,9 +1799,9 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
thd->free_list= NULL; DBUG_ASSERT(thd->free_list == NULL);
thd->stmt_backup.set_statement(thd);
thd->set_statement(stmt); thd->set_n_backup_statement(stmt, &thd->stmt_backup);
if (stmt->set_params_from_vars(stmt, if (stmt->set_params_from_vars(stmt,
thd->stmt_backup.lex->prepared_stmt_params, thd->stmt_backup.lex->prepared_stmt_params,
&expanded_query)) &expanded_query))
...@@ -1866,11 +1833,7 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, ...@@ -1866,11 +1833,7 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt,
{ {
DBUG_ENTER("execute_stmt"); DBUG_ENTER("execute_stmt");
if (set_context) if (set_context)
{ thd->set_n_backup_statement(stmt, &thd->stmt_backup);
thd->free_list= NULL;
thd->stmt_backup.set_statement(thd);
thd->set_statement(stmt);
}
reset_stmt_for_execute(stmt); reset_stmt_for_execute(stmt);
if (expanded_query->length() && if (expanded_query->length() &&
...@@ -1880,6 +1843,13 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, ...@@ -1880,6 +1843,13 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt,
my_error(ER_OUTOFMEMORY, 0, expanded_query->length()); my_error(ER_OUTOFMEMORY, 0, expanded_query->length());
DBUG_VOID_RETURN; 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)) if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),QUERY_PRIOR); my_pthread_setprio(pthread_self(),QUERY_PRIOR);
...@@ -1890,6 +1860,12 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, ...@@ -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 that were created during this execution of the PS. */
free_items(thd->free_list); 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); cleanup_items(stmt->free_list);
reset_stmt_params(stmt); reset_stmt_params(stmt);
close_thread_tables(thd); // to close derived tables close_thread_tables(thd); // to close derived tables
...@@ -1927,7 +1903,7 @@ void mysql_stmt_reset(THD *thd, char *packet) ...@@ -1927,7 +1903,7 @@ void mysql_stmt_reset(THD *thd, char *packet)
SEND_ERROR))) SEND_ERROR)))
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
stmt->get_longdata_error= 0; stmt->state= Item_arena::PREPARED;
/* /*
Clear parameters from data which could be set by 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) ...@@ -2015,7 +1991,7 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
if (param_number >= stmt->param_count) if (param_number >= stmt->param_count)
{ {
/* Error will be sent in execute call */ /* Error will be sent in execute call */
stmt->get_longdata_error= 1; stmt->state= Item_arena::ERROR;
stmt->last_errno= ER_WRONG_ARGUMENTS; stmt->last_errno= ER_WRONG_ARGUMENTS;
sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS), sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS),
"mysql_stmt_send_long_data"); "mysql_stmt_send_long_data");
...@@ -2026,10 +2002,15 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) ...@@ -2026,10 +2002,15 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
param= stmt->param_array[param_number]; param= stmt->param_array[param_number];
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
param->set_longdata(packet, (ulong) (packet_end - packet)); if (param->set_longdata(packet, (ulong) (packet_end - packet)))
#else #else
param->set_longdata(thd->extra_data, thd->extra_length); if (param->set_longdata(thd->extra_data, thd->extra_length))
#endif #endif
{
stmt->state= Item_arena::ERROR;
stmt->last_errno= ER_OUTOFMEMORY;
sprintf(stmt->last_error, ER(ER_OUTOFMEMORY), 0);
}
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -2039,8 +2020,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg) ...@@ -2039,8 +2020,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
thd(thd_arg), thd(thd_arg),
param_array(0), param_array(0),
param_count(0), param_count(0),
last_errno(0), last_errno(0)
get_longdata_error(0)
{ {
*last_error= '\0'; *last_error= '\0';
} }
...@@ -2074,7 +2054,7 @@ Prepared_statement::~Prepared_statement() ...@@ -2074,7 +2054,7 @@ Prepared_statement::~Prepared_statement()
} }
Statement::Type Prepared_statement::type() const Item_arena::Type Prepared_statement::type() const
{ {
return PREPARED_STATEMENT; return PREPARED_STATEMENT;
} }
......
...@@ -4377,25 +4377,39 @@ COND *eliminate_not_funcs(THD *thd, COND *cond) ...@@ -4377,25 +4377,39 @@ COND *eliminate_not_funcs(THD *thd, COND *cond)
static COND * static COND *
optimize_cond(THD *thd, COND *conds, Item::cond_result *cond_value) optimize_cond(THD *thd, COND *conds, Item::cond_result *cond_value)
{ {
SELECT_LEX *select= thd->lex->current_select;
DBUG_ENTER("optimize_cond"); 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; *cond_value= Item::COND_TRUE;
DBUG_RETURN(conds); select->prep_where= 0;
} }
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"););
DBUG_RETURN(conds); DBUG_RETURN(conds);
} }
......
...@@ -287,24 +287,23 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, ...@@ -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; thd_arg->lex->current_select= lex_select_save;
if (!item_list.elements) if (!item_list.elements)
{ {
Statement *stmt= thd->current_statement; Item_arena *arena= thd->current_arena, backup;
Statement backup; if (arena->is_stmt_prepare())
if (stmt) thd->set_n_backup_item_arena(arena, &backup);
thd->set_n_backup_item_arena(stmt, &backup);
Field **field; Field **field;
for (field= table->field; *field; field++) for (field= table->field; *field; field++)
{ {
Item_field *item= new Item_field(*field); Item_field *item= new Item_field(*field);
if (!item || item_list.push_back(item)) if (!item || item_list.push_back(item))
{ {
if (stmt) if (arena->is_stmt_prepare())
thd->restore_backup_item_arena(stmt, &backup); thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(-1); 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 */ /* prepare fake select to initialize it correctly */
ulong options_tmp= init_prepare_fake_select_lex(thd); ulong options_tmp= init_prepare_fake_select_lex(thd);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment