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
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;
......@@ -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;
......@@ -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)
{
......
......@@ -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);
}
......
......@@ -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 */
......
......@@ -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 */
......
......@@ -92,8 +92,8 @@ class Item_sum :public Item_result_field
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);
};
......
......@@ -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 */
......
......@@ -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);
}
......
......@@ -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()
......
......@@ -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 @@ class Statement
*/
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 @@ class Statement
/* 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 @@ class THD :public ilink,
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 @@ class THD :public ilink,
}
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 */
......
......@@ -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)
{
......
......@@ -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;
......
......@@ -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;
......
......@@ -88,7 +88,6 @@ class Prepared_statement: public Statement
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 @@ class Prepared_statement: public Statement
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,15 +1067,14 @@ static int mysql_test_select(Prepared_statement *stmt,
send_error(thd);
goto err_prep;
}
if (!text_protocol)
{
if (lex->describe)
{
if (!text_protocol && send_prep_stmt(stmt, 0))
if (send_prep_stmt(stmt, 0))
goto err_prep;
unit->cleanup();
}
else
{
if (!text_protocol)
{
if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0)
......@@ -1108,15 +1084,13 @@ static int mysql_test_select(Prepared_statement *stmt,
)
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;
}
......
......@@ -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)
{
*cond_value= Item::COND_TRUE;
DBUG_RETURN(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);
}
DBUG_EXECUTE("where",print_where(conds,"original"););
/* eliminate NOT operators */
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);
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_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;
select->prep_where= 0;
}
DBUG_RETURN(conds);
}
......
......@@ -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);
......
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