Commit d8703464 authored by unknown's avatar unknown

fixed subquery with PS (BUG#2462)

fixed UNION preparation


sql/item.cc:
  debug output added
sql/item.h:
  debug output added
sql/item_cmpfunc.cc:
  correct cleunup() for Item_in_optimizer
sql/item_cmpfunc.h:
  correct cleunup() for Item_in_optimizer
  debug output added
sql/item_func.h:
  debug output added
sql/item_subselect.cc:
  support of prepared statemnts added - mostly memorry allocation manegement, only one trabsformatio & correct cleupup()
sql/item_subselect.h:
  support of prepared statemnts added - mostly memorry allocation manegement, only one trabsformatio & correct cleupup()
sql/item_sum.cc:
  debug output added
sql/item_sum.h:
  debug output added
sql/sql_class.cc:
  function to switch allocation arena for Items
sql/sql_class.h:
  function to switch allocation arena for Items
  pointer on current prepared statement added
sql/sql_lex.cc:
  comment fixed
sql/sql_lex.h:
  item cleanup support
sql/sql_prepare.cc:
  - fixed preparation of PS to avoid storing junk in its memory + correct work with union
  - fixed tables cleanup for UNION & subqueries
sql/sql_select.cc:
  removed condition which is always true for now
  fixed layout
sql/sql_union.cc:
  support of UNION subquery cleanup
tests/client_test.c:
  test of repeatable subqueries
  test of correct UNION initialisation
parent 4522e177
......@@ -966,8 +966,10 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
void Item_field::cleanup()
{
DBUG_ENTER("Item_field::cleanup");
Item_ident::cleanup();
field= result_field= 0;
DBUG_VOID_RETURN;
}
void Item::init_make_field(Send_field *tmp_field,
......@@ -1610,9 +1612,11 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
void Item_ref::cleanup()
{
DBUG_ENTER("Item_ref::cleanup");
Item_ident::cleanup();
if (hook_ptr)
*hook_ptr= orig_item;
DBUG_VOID_RETURN;
}
......
......@@ -128,7 +128,13 @@ class Item {
virtual ~Item() { name=0; } /*lint -e1509 */
void set_name(const char *str,uint length, CHARSET_INFO *cs);
void init_make_field(Send_field *tmp_field,enum enum_field_types type);
virtual void cleanup() { fixed=0; }
virtual void cleanup()
{
DBUG_ENTER("Item::cleanup");
DBUG_PRINT("info", ("Type: %d", (int)type()));
fixed=0;
DBUG_VOID_RETURN;
}
virtual void make_field(Send_field *field);
virtual bool fix_fields(THD *, struct st_table_list *, Item **);
virtual int save_in_field(Field *field, bool no_conversions);
......@@ -996,8 +1002,10 @@ class Item_cache_row: public Item_cache
void bring_value();
void cleanup()
{
DBUG_ENTER("Item_cache_row::cleanup");
Item_cache::cleanup();
values= 0;
DBUG_VOID_RETURN;
}
};
......@@ -1023,8 +1031,10 @@ class Item_type_holder: public Item
Field *example() { return field_example; }
void cleanup()
{
DBUG_ENTER("Item_type_holder::cleanup");
Item::cleanup();
item_type= orig_type;
DBUG_VOID_RETURN;
}
};
......
......@@ -484,6 +484,14 @@ longlong Item_in_optimizer::val_int()
return tmp;
}
void Item_in_optimizer::cleanup()
{
DBUG_ENTER("Item_in_optimizer::cleanup");
Item_bool_func::cleanup();
cache= 0;
DBUG_VOID_RETURN;
}
bool Item_in_optimizer::is_null()
{
cache->store(args[0]);
......
......@@ -105,6 +105,7 @@ class Item_in_optimizer: public Item_bool_func
Item_in_optimizer return NULL, else it evaluate Item_in_subselect.
*/
longlong val_int();
void cleanup();
const char *func_name() const { return "<in_optimizer>"; }
Item_cache **get_cache() { return &cache; }
};
......@@ -207,9 +208,11 @@ class Item_bool_rowready_func2 :public Item_bool_func2
}
void cleanup()
{
DBUG_ENTER("Item_bool_rowready_func2::cleanup");
Item_bool_func2::cleanup();
tmp_arg[0]= orig_a;
tmp_arg[1]= orig_b;
DBUG_VOID_RETURN;
}
};
......@@ -718,10 +721,12 @@ class Item_func_in :public Item_int_func
void fix_length_and_dec();
void cleanup()
{
DBUG_ENTER("Item_func_in::cleanup");
delete array;
delete in_item;
array= 0;
in_item= 0;
DBUG_VOID_RETURN;
}
optimize_type select_optimize() const
{ return array ? OPTIMIZE_KEY : OPTIMIZE_NONE; }
......
......@@ -991,6 +991,7 @@ class Item_func_match :public Item_real_func
join_key(0), ft_handler(0), table(0), master(0), concat(0) { }
void cleanup()
{
DBUG_ENTER("Item_func_match");
if (!master && ft_handler)
{
ft_handler->please->close_search(ft_handler);
......@@ -1001,6 +1002,7 @@ class Item_func_match :public Item_real_func
}
if (concat)
delete concat;
DBUG_VOID_RETURN;
}
enum Functype functype() const { return FT_FUNC; }
const char *func_name() const { return "match"; }
......
This diff is collapsed.
......@@ -26,6 +26,7 @@ class JOIN;
class select_subselect;
class subselect_engine;
class Item_bool_func2;
class Statement;
/* base class for subselects */
......@@ -35,22 +36,30 @@ 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 */
st_select_lex_unit *unit;
/* engine that perform execution of subselect (single select or union) */
subselect_engine *engine;
/* old engine if engine was changed */
subselect_engine *old_engine;
/* cache of used external tables */
table_map used_tables_cache;
/* allowed number of columns (1 for single value subqueries) */
uint max_columns;
/* work with 'substitution' */
bool have_to_be_excluded;
/* cache of constante state */
/* cache of constant state */
bool const_item_cache;
public:
/* changed engine indicator */
bool engine_changed;
/* subquery is transformed */
bool changed;
enum trans_res {RES_OK, RES_REDUCE, RES_ERROR};
enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS,
......@@ -94,6 +103,7 @@ class Item_subselect :public Item_result_field
void print(String *str);
bool change_engine(subselect_engine *eng)
{
old_engine= engine;
engine= eng;
engine_changed= 1;
return eng == 0;
......@@ -116,6 +126,7 @@ class Item_singlerow_subselect :public Item_subselect
Item_singlerow_subselect(st_select_lex *select_lex);
Item_singlerow_subselect() :Item_subselect(), value(0), row (0) {}
void cleanup();
subs_type substype() { return SINGLEROW_SUBS; }
void reset();
......@@ -200,13 +211,6 @@ class Item_in_subselect :public Item_exists_subselect
{}
void cleanup()
{
Item_exists_subselect::cleanup();
abort_on_null= 0;
transformed= 0;
upper_not= 0;
}
subs_type substype() { return IN_SUBS; }
void reset()
{
......@@ -269,7 +273,7 @@ class subselect_engine: public Sql_alloc
maybe_null= 0;
}
virtual ~subselect_engine() {}; // to satisfy compiler
virtual void cleanup() {}
virtual void cleanup()= 0;
// set_thd should be called before prepare()
void set_thd(THD *thd_arg) { thd= thd_arg; }
......@@ -318,6 +322,7 @@ class subselect_union_engine: public subselect_engine
subselect_union_engine(st_select_lex_unit *u,
select_subselect *result,
Item_subselect *item);
void cleanup();
int prepare();
void fix_length_and_dec(Item_cache** row);
int exec();
......@@ -345,6 +350,7 @@ class subselect_uniquesubquery_engine: public subselect_engine
set_thd(thd_arg);
}
~subselect_uniquesubquery_engine();
void cleanup();
int prepare();
void fix_length_and_dec(Item_cache** row);
int exec();
......
......@@ -1084,6 +1084,7 @@ int dump_leaf(byte* key, uint32 count __attribute__((unused)),
void Item_sum_count_distinct::cleanup()
{
DBUG_ENTER("Item_sum_count_distinct::cleanup");
Item_sum_int::cleanup();
/*
Free table and tree if they belong to this item (if item have not pointer
......@@ -1099,6 +1100,7 @@ void Item_sum_count_distinct::cleanup()
table= 0;
use_tree= 0;
}
DBUG_VOID_RETURN;
}
bool Item_sum_count_distinct::fix_fields(THD *thd, TABLE_LIST *tables,
......@@ -1666,6 +1668,7 @@ Item_func_group_concat::Item_func_group_concat(bool is_distinct,
void Item_func_group_concat::cleanup()
{
DBUG_ENTER("Item_func_group_concat::cleanup");
/*
Free table and tree if they belong to this item (if item have not pointer
to original item from which was made copy => it own its objects )
......@@ -1679,6 +1682,7 @@ void Item_func_group_concat::cleanup()
if (tree_mode)
delete_tree(tree);
}
DBUG_VOID_RETURN;
}
Item_func_group_concat::~Item_func_group_concat()
......
......@@ -60,8 +60,10 @@ class Item_sum :public Item_result_field
Item_sum(THD *thd, Item_sum *item);
void cleanup()
{
DBUG_ENTER("Item_sum::cleanup");
Item_result_field::cleanup();
result_field=0;
DBUG_VOID_RETURN;
}
enum Type type() const { return SUM_FUNC_ITEM; }
......
......@@ -83,7 +83,7 @@ extern "C" void free_user_var(user_var_entry *entry)
** Thread specific functions
****************************************************************************/
THD::THD():user_time(0), is_fatal_error(0),
THD::THD():user_time(0), current_statement(0), is_fatal_error(0),
last_insert_id_used(0),
insert_id_used(0), rand_used(0), in_lock_tables(0),
global_read_lock(0), bootstrap(0)
......@@ -1232,6 +1232,21 @@ void Statement::set_statement(Statement *stmt)
mem_root= stmt->mem_root;
}
void Statement::set_n_backup_item_arena(Statement *set, Statement *backup)
{
backup->mem_root= mem_root;
backup->free_list= free_list;
set_item_arena(set);
}
void Statement::set_item_arena(Statement *set)
{
mem_root= set->mem_root;
free_list= set->free_list;
}
Statement::~Statement()
{
......
......@@ -427,7 +427,7 @@ class Statement
public:
/* FIXME: must be private */
LEX main_lex;
public:
/*
Uniquely identifies each statement object in thread scope; change during
statement lifetime. FIXME: must be const
......@@ -503,6 +503,15 @@ class Statement
void set_statement(Statement *stmt);
/* return class type */
virtual Type type() const;
void set_n_backup_item_arena(Statement *set, Statement *backup);
inline void restore_backup_item_arena(Statement *backup)
{
set_item_arena(backup);
// reset backup mem_root to avoid its freeing
init_alloc_root(&backup->mem_root, 0, 0);
}
void set_item_arena(Statement *set);
};
......@@ -688,6 +697,10 @@ class THD :public ilink,
#ifdef SIGNAL_WITH_VIO_CLOSE
Vio* active_vio;
#endif
/*
Current prepared Statement if there one, or 0
*/
Statement *current_statement;
/*
next_insert_id is set on SET INSERT_ID= #. This is used as the next
generated auto_increment value in handler.cc
......
......@@ -1561,7 +1561,11 @@ void st_select_lex::print_limit(THD *thd, String *str)
/*
There are st_select_lex::add_table_to_list &
st_select_lex::set_lock_for_tables in sql_parse.cc
st_select_lex::set_lock_for_tables are in sql_parse.cc
st_select_lex::print is in sql_select.h
st_select_lex_unit::prepare, st_select_lex_unit::exec,
st_select_lex_unit::cleanup, st_select_lex_unit::reinit_exec_mechanism
are in sql_union.cc
*/
......@@ -354,6 +354,7 @@ class st_select_lex_unit: public st_select_lex_node {
int prepare(THD *thd, select_result *result, ulong additional_options);
int exec();
int cleanup();
void reinit_exec_mechanism();
void print(String *str);
......
......@@ -753,14 +753,21 @@ static bool mysql_test_select_fields(Prepared_statement *stmt,
DBUG_RETURN(1);
}
JOIN *join= new JOIN(thd, fields, select_options, result);
thd->used_tables= 0; // Updated by setup_fields
if (join->prepare(&select_lex->ref_pointer_array,
(TABLE_LIST*)select_lex->get_table_list(),
wild_num, conds, og_num, order, group, having, proc,
select_lex, unit))
Statement backup;
/*
we do not want to have in statement memory all that junk,
which will be created by preparation => substitute memory
from original thread pool
*/
thd->set_n_backup_item_arena(&thd->stmt_backup, &backup);
if ((unit->prepare(thd, result, 0)))
{
thd->restore_backup_item_arena(&backup);
DBUG_RETURN(1);
}
thd->restore_backup_item_arena(&backup);
if (send_prep_stmt(stmt, fields.elements) ||
thd->protocol_simple.send_fields(&fields, 0)
#ifndef EMBEDDED_LIBRARY
......@@ -768,7 +775,7 @@ static bool mysql_test_select_fields(Prepared_statement *stmt,
#endif
)
DBUG_RETURN(1);
join->cleanup();
unit->cleanup();
}
DBUG_RETURN(0);
}
......@@ -899,6 +906,7 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
thd->stmt_backup.set_statement(thd);
thd->set_statement(stmt);
thd->current_statement= stmt;
if (alloc_query(thd, packet, packet_length))
goto alloc_query_err;
......@@ -929,6 +937,7 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
cleanup_items(thd->free_list);
stmt->set_statement(thd);
thd->set_statement(&thd->stmt_backup);
thd->current_statement= 0;
if (init_param_items(stmt))
goto init_param_err;
......@@ -947,6 +956,7 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
thd->stmt_map.erase(stmt);
DBUG_RETURN(1);
insert_stmt_err:
thd->current_statement= 0;
delete stmt;
DBUG_RETURN(1);
}
......@@ -980,6 +990,7 @@ void mysql_stmt_execute(THD *thd, char *packet)
stmt->query_id= thd->query_id;
thd->stmt_backup.set_statement(thd);
thd->set_statement(stmt);
thd->current_statement= stmt;
thd->free_list= 0;
/*
......@@ -1009,17 +1020,21 @@ void mysql_stmt_execute(THD *thd, char *packet)
order->item= (Item **)(order+1);
for (order=(ORDER *)sl->order_list.first ; order ; order=order->next)
order->item= (Item **)(order+1);
}
/*
TODO: When the new table structure is ready, then have a status bit
to indicate the table is altered, and re-do the setup_*
and open the tables back.
*/
for (TABLE_LIST *tables= (TABLE_LIST*) stmt->lex->select_lex.table_list.first;
for (TABLE_LIST *tables= (TABLE_LIST*) sl->table_list.first;
tables;
tables= tables->next)
{
tables->table= 0; // safety - nasty init
tables->table_list= 0;
}
}
#ifndef EMBEDDED_LIBRARY
if (stmt->param_count && setup_params_data(stmt))
......@@ -1049,6 +1064,7 @@ void mysql_stmt_execute(THD *thd, char *packet)
cleanup_items(stmt->free_list);
free_root(&thd->mem_root, MYF(0));
thd->set_statement(&thd->stmt_backup);
thd->current_statement= 0;
DBUG_VOID_RETURN;
}
......
......@@ -330,8 +330,7 @@ JOIN::prepare(Item ***rref_pointer_array,
// Is it subselect
{
Item_subselect *subselect;
if ((subselect= select_lex->master_unit()->item) &&
select_lex->linkage != GLOBAL_OPTIONS_TYPE)
if ((subselect= select_lex->master_unit()->item))
{
Item_subselect::trans_res res;
if ((res= subselect->select_transformer(this)) !=
......
......@@ -468,3 +468,9 @@ int st_select_lex_unit::cleanup()
}
DBUG_RETURN(error);
}
void st_select_lex_unit::reinit_exec_mechanism()
{
prepared= optimized= executed= 0;
}
......@@ -8095,6 +8095,53 @@ static void test_bug1946()
rc= mysql_query(mysql,"DROP TABLE prepare_command");
}
static void test_subqueries()
{
MYSQL_STMT *stmt;
int rc, i;
const char *query= "SELECT (SELECT SUM(a+b) FROM t2 where t1.b=t2.b GROUP BY t1.a LIMIT 1) as scalar_s, exists (select 1 from t2 where t2.a/2=t1.a) as exists_s, a in (select a+3 from t2) as in_s, (a-1,b-1) in (select a,b from t2) as in_row_s FROM t1";
myheader("test_subquery");
rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,t2");
myquery(rc);
rc= mysql_query(mysql,"CREATE TABLE t1 (a int , b int);");
myquery(rc);
rc= mysql_query(mysql,
"insert into t1 values (1,1), (2, 2), (3,3), (4,4), (5,5);");
myquery(rc);
rc= mysql_query(mysql,"create table t2 select * from t1;");
myquery(rc);
stmt= mysql_prepare(mysql, query, strlen(query));
for (i= 0; i < 3; i++)
{
rc= mysql_execute(stmt);
mystmt(stmt, rc);
assert(5 == my_process_stmt_result(stmt));
}
mysql_stmt_close(stmt);
rc= mysql_query(mysql, "DROP TABLE t1,t2");
myquery(rc);
}
static void test_bad_union()
{
MYSQL_STMT *stmt;
const char *query= "SELECT 1, 2 union SELECT 1";
myheader("test_bad_union");
stmt= mysql_prepare(mysql, query, strlen(query));
assert(stmt == 0);
myerror(NULL);
}
/*
Read and parse arguments and MySQL options from my.cnf
......@@ -8234,6 +8281,7 @@ int main(int argc, char **argv)
test_count= 1;
start_time= time((time_t *)0);
client_query(); /* simple client query test */
#if NOT_YET_WORKING
/* Used for internal new development debugging */
......@@ -8340,6 +8388,9 @@ int main(int argc, char **argv)
test_bug1644(); /* BUG#1644 */
test_bug1946(); /* test that placeholders are allowed only in
prepared queries */
test_subqueries(); /* repeatable subqueries */
test_bad_union(); /* correct setup of UNION */
end_time= time((time_t *)0);
total_time+= difftime(end_time, start_time);
......
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