diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 5c57ea87f21..873fe780ef9 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -46,7 +46,7 @@ SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; 1 1 SELECT (SELECT 1), a; -Unknown column 'a' in 'field list' +Unknown column 'a' in 'checking transformed subquery' SELECT 1 as a FROM (SELECT 1) as b HAVING (SELECT a)=1; a 1 @@ -672,7 +672,6 @@ id EXPLAIN SELECT * FROM t2 WHERE id IN (SELECT 1+(select 1)); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ref id id 5 const 1 Using where; Using index -3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: Note 1247 Select 3 was reduced during optimisation Note 1247 Select 2 was reduced during optimisation @@ -1012,7 +1011,7 @@ CREATE TABLE t1 SELECT (SELECT 1 as a UNION SELECT 1+1 limit 1,1) as a; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(1) NOT NULL default '0' + `a` bigint(17) NOT NULL default '0' ) TYPE=MyISAM CHARSET=latin1 drop table t1; create table t1 (a int); diff --git a/sql/item.cc b/sql/item.cc index 053a94cb695..623f16990fb 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1323,17 +1323,15 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) } } -/* - * The following conditional is changed as to correctly identify - * incorrect references in group functions or forward references - * with sub-select's / derived tables, while it prevents this - * check when Item_ref is created in an expression involving - * summing function, which is to be placed in the user variable. - * - */ - + /* + The following conditional is changed as to correctly identify + incorrect references in group functions or forward references + with sub-select's / derived tables, while it prevents this + check when Item_ref is created in an expression involving + summing function, which is to be placed in the user variable. + */ if (((*ref)->with_sum_func && name && - (depended_from || + (depended_from || !(thd->lex.current_select->linkage != GLOBAL_OPTIONS_TYPE && thd->lex.current_select->select_lex()->having_fix_field))) || !(*ref)->fixed) diff --git a/sql/item.h b/sql/item.h index 111892df688..3c2e0b8b32f 100644 --- a/sql/item.h +++ b/sql/item.h @@ -100,7 +100,7 @@ public: virtual bool get_time(TIME *ltime); virtual bool get_date_result(TIME *ltime,bool fuzzydate) { return get_date(ltime,fuzzydate); } - virtual bool is_null() { return 0; }; + virtual bool is_null() { return 0; } virtual void top_level_item() {} virtual void set_result_field(Field *field) {} virtual bool is_result_field() { return 0; } @@ -560,6 +560,15 @@ public: longlong val_int(); String* val_str(String* s); bool get_date(TIME *ltime, bool fuzzydate); + void print(String *str) + { + str->append("ref_null_helper("); + if (ref && *ref) + (*ref)->print(str); + else + str->append('?'); + str->append(')'); + } }; @@ -605,6 +614,15 @@ public: {} bool fix_fields(THD *, struct st_table_list *, Item ** ref); Item **storage() {return &item;} + void print(String *str) + { + str->append("ref_null_helper('"); + if (item) + item->print(str); + else + str->append('?'); + str->append(')'); + } }; /* diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 7cc07690fcc..36558ef8c20 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -318,16 +318,24 @@ int Arg_comparator::compare_e_row() return 1; } -bool Item_in_optimizer::preallocate_row() + +bool Item_in_optimizer::fix_left(THD *thd, + struct st_table_list *tables, + Item **ref) { - return (!(cache= Item_cache::get_cache(ROW_RESULT))); + if (args[0]->fix_fields(thd, tables, ref) || + (!cache && !(cache= Item_cache::get_cache(args[0]->result_type())))) + return 1; + cache->setup(args[0]); + return 0; } + bool Item_in_optimizer::fix_fields(THD *thd, struct st_table_list *tables, Item ** ref) { - if (args[0]->fix_fields(thd, tables, args)) + if (fix_left(thd, tables, ref)) return 1; if (args[0]->maybe_null) maybe_null=1; @@ -340,9 +348,6 @@ bool Item_in_optimizer::fix_fields(THD *thd, struct st_table_list *tables, with_sum_func= args[0]->with_sum_func; used_tables_cache= args[0]->used_tables(); const_item_cache= args[0]->const_item(); - if (!cache && !(cache= Item_cache::get_cache(args[0]->result_type()))) - return 1; - cache->setup(args[0]); if (cache->cols() == 1) { if (args[0]->used_tables()) @@ -361,7 +366,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, struct st_table_list *tables, ((Item_cache *)cache->el(i))->set_used_tables(0); } } - if (args[1]->fix_fields(thd, tables, args)) + if (!args[1]->fixed && args[1]->fix_fields(thd, tables, args)) return 1; Item_in_subselect * sub= (Item_in_subselect *)args[1]; if (args[0]->cols() != sub->engine->cols()) @@ -1592,7 +1597,8 @@ Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) } if (abort_on_null) item->top_level_item(); - if (item->fix_fields(thd, tables, li.ref()) || item->check_cols(1)) + if ((!item->fixed && + item->fix_fields(thd, tables, li.ref())) || item->check_cols(1)) return 1; /* purecov: inspected */ used_tables_cache|=item->used_tables(); with_sum_func= with_sum_func || item->with_sum_func; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 5e246e3e285..a49eeb12ec1 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -91,9 +91,8 @@ protected: public: Item_in_optimizer(Item *a, Item_in_subselect *b): Item_bool_func(a, (Item *)b), cache(0) {} - // used by row in transformer - bool preallocate_row(); bool fix_fields(THD *, struct st_table_list *, Item **); + bool fix_left(THD *thd, struct st_table_list *tables, Item **ref); bool is_null(); /* Item_in_optimizer item is special boolean function. On value request @@ -103,7 +102,7 @@ public: Item_in_optimizer return NULL, else it evaluate Item_in_subselect. */ longlong val_int(); - + const char *func_name() const { return "IN_OPTIMIZER"; } Item_cache **get_cache() { return &cache; } }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index c749fba616f..c1e6c23d1a7 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -56,7 +56,6 @@ void Item_subselect::init(THD *thd, st_select_lex *select_lex, DBUG_ENTER("Item_subselect::init"); DBUG_PRINT("subs", ("select_lex 0x%xl", (ulong) select_lex)); - select_transformer(thd, select_lex->master_unit()); if (select_lex->next_select()) engine= new subselect_union_engine(thd, select_lex->master_unit(), result, this); @@ -72,36 +71,39 @@ Item_subselect::~Item_subselect() delete engine; } -void Item_subselect::select_transformer(THD *thd, st_select_lex_unit *unit) +Item_subselect::trans_res +Item_subselect::select_transformer(THD *thd, + JOIN *join) { DBUG_ENTER("Item_subselect::select_transformer"); - DBUG_VOID_RETURN; + DBUG_RETURN(OK); } bool Item_subselect::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { - if (substitution) - { - (*ref)= substitution; - substitution->name= name; - if (have_to_be_excluded) - engine->exclude(); - substitution= 0; - int ret= (*ref)->fix_fields(thd, tables, ref); - // We can't substitute aggregate functions (like (SELECT (max(i))) - if ((*ref)->with_sum_func) - { - my_error(ER_INVALID_GROUP_FUNC_USE, MYF(0)); - return 1; - } - return ret; - } - char const *save_where= thd->where; int res= engine->prepare(); if (!res) { + if (substitution) + { + (*ref)= substitution; + substitution->name= name; + if (have_to_be_excluded) + engine->exclude(); + substitution= 0; + fixed= 1; + thd->where= "checking transformed subquery"; + int ret= (*ref)->fix_fields(thd, tables, ref); + // We can't substitute aggregate functions (like (SELECT (max(i))) + if ((*ref)->with_sum_func) + { + my_error(ER_INVALID_GROUP_FUNC_USE, MYF(0)); + return 1; + } + return ret; + } // Is it one field subselect? if (engine->cols() > max_columns) { @@ -150,12 +152,14 @@ void Item_singlerow_subselect::reset() value->null_value= 1; } -void Item_singlerow_subselect::select_transformer(THD *thd, - st_select_lex_unit *unit) +Item_subselect::trans_res +Item_singlerow_subselect::select_transformer(THD *thd, + JOIN *join) { - SELECT_LEX *select_lex= unit->first_select(); + SELECT_LEX *select_lex= join->select_lex; - if (!select_lex->next_select() && !select_lex->table_list.elements && + if (!select_lex->master_unit()->first_select()->next_select() && + !select_lex->table_list.elements && select_lex->item_list.elements == 1 && /* We cant change name of Item_field or Item_ref, because it will @@ -183,18 +187,20 @@ void Item_singlerow_subselect::select_transformer(THD *thd, if (select_lex->where || select_lex->having) { Item *cond; - if (!select_lex->having) - cond= select_lex->where; - else if (!select_lex->where) - cond= select_lex->having; + if (!join->having) + cond= join->conds; + else if (!join->conds) + cond= join->having; else - if (!(cond= new Item_cond_and(select_lex->having, select_lex->where))) - return; + if (!(cond= new Item_cond_and(join->conds, join->having))) + return ERROR; if (!(substitution= new Item_func_if(cond, substitution, new Item_null()))) - return; + return ERROR; } + return REDUCE; } + return OK; } void Item_singlerow_subselect::store(uint i, Item *item) @@ -313,6 +319,37 @@ Item_exists_subselect::Item_exists_subselect(THD *thd, DBUG_VOID_RETURN; } +bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit) +{ + SELECT_LEX_NODE *global= unit->global_parameters; + if (global->select_limit != HA_POS_ERROR) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "LIMIT & IN/ALL/ANY/SOME subquery"); + return(1); + } + SELECT_LEX *sl= unit->first_select(); + for (; sl; sl= sl->next_select()) + { + if (sl->select_limit != HA_POS_ERROR) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "LIMIT & IN/ALL/ANY/SOME subquery"); + return(1); + } + // We need only 1 row to determinate existence + sl->select_limit= 1; + // no sense in ORDER BY without LIMIT + sl->order_list.empty(); + } + // no sense in ORDER BY without LIMIT + global->order_list.empty(); + // We need only 1 row to determinate existence + global->select_limit= 1; + + return(0); +} + Item_in_subselect::Item_in_subselect(THD *thd, Item * left_exp, st_select_lex *select_lex): Item_exists_subselect() @@ -322,25 +359,25 @@ Item_in_subselect::Item_in_subselect(THD *thd, Item * left_exp, init(thd, select_lex, new select_exists_subselect(this)); max_columns= UINT_MAX; maybe_null= 1; + abort_on_null= 0; reset(); - // We need only 1 row to determinate existence - select_lex->master_unit()->global_parameters->select_limit= 1; + test_limit(select_lex->master_unit()); DBUG_VOID_RETURN; } Item_allany_subselect::Item_allany_subselect(THD *thd, Item * left_exp, - compare_func_creator f, + compare_func_creator fn, st_select_lex *select_lex): Item_in_subselect() { DBUG_ENTER("Item_in_subselect::Item_in_subselect"); left_expr= left_exp; - func= f; + func= fn; init(thd, select_lex, new select_exists_subselect(this)); max_columns= 1; + abort_on_null= 0; reset(); - // We need only 1 row to determinate existence - select_lex->master_unit()->global_parameters->select_limit= 1; + test_limit(select_lex->master_unit()); DBUG_VOID_RETURN; } @@ -430,6 +467,7 @@ Item_in_subselect::Item_in_subselect(Item_in_subselect *item): Item_exists_subselect(item) { left_expr= item->left_expr; + abort_on_null= item->abort_on_null; } Item_allany_subselect::Item_allany_subselect(Item_allany_subselect *item): @@ -438,227 +476,258 @@ Item_allany_subselect::Item_allany_subselect(Item_allany_subselect *item): func= item->func; } -void Item_in_subselect::single_value_transformer(THD *thd, - st_select_lex_unit *unit, - Item *left_expr, - compare_func_creator func) +Item_subselect::trans_res +Item_in_subselect::single_value_transformer(THD *thd, + JOIN *join, + Item *left_expr, + compare_func_creator func) { DBUG_ENTER("Item_in_subselect::single_value_transformer"); - if (unit->global_parameters->select_limit != HA_POS_ERROR) + SELECT_LEX *select_lex= join->select_lex; + + thd->where= "scalar IN/ALL/ANY subquery"; + + if (!substitution) { - my_error(ER_NOT_SUPPORTED_YET, MYF(0), - "LIMIT & IN/ALL/ANY/SOME subquery"); - DBUG_VOID_RETURN; + //first call for this unit + SELECT_LEX_UNIT *unit= select_lex->master_unit(); + substitution= optimizer= new Item_in_optimizer(left_expr, this); + + SELECT_LEX_NODE *current= thd->lex.current_select, *up; + thd->lex.current_select= up= current->return_after_parsing(); + //optimizer never use Item **ref => we can pass 0 as parameter + if (!optimizer || optimizer->fix_left(thd, up->get_table_list(), 0)) + { + thd->lex.current_select= current; + DBUG_RETURN(ERROR); + } + thd->lex.current_select= current; + + /* + As far as Item_ref_in_optimizer do not substitude itself on fix_fields + we can use same item for all selects. + */ + expr= new Item_ref((Item**)optimizer->get_cache(), + (char *)"", + (char *)""); + + unit->dependent= 1; } - // no sense in ORDER BY without LIMIT - unit->global_parameters->order_list.empty(); - Item_in_optimizer *optimizer; - substitution= optimizer= new Item_in_optimizer(left_expr, this); - if (!optimizer) - DBUG_VOID_RETURN; - - /* - As far as Item_ref_in_optimizer do not substitude itself on fix_fields - we can use same item for all selects. - */ - Item *expr= new Item_ref((Item**)optimizer->get_cache(), - (char *)"", - (char*)""); - unit->dependent= 1; - for (SELECT_LEX * sl= unit->first_select(); sl; sl= sl->next_select()) + select_lex->dependent= 1; + Item *item; + if (select_lex->item_list.elements > 1) { - if (sl->select_limit != HA_POS_ERROR) - { - my_error(ER_NOT_SUPPORTED_YET, MYF(0), - "LIMIT & IN/ALL/ANY/SOME subquery"); - DBUG_VOID_RETURN; - } + my_error(ER_CARDINALITY_COL, MYF(0), 1); + DBUG_RETURN(ERROR); + } + else + item= (Item*) select_lex->item_list.head(); - sl->dependent= 1; - Item *item; - if (sl->item_list.elements > 1) + if (join->having || select_lex->with_sum_func || + select_lex->group_list.elements) + { + item= (*func)(expr, + new Item_ref_null_helper(this, + select_lex->ref_pointer_array, + (char *)"", + this->full_name())); + join->having= and_items(join->having, item); + select_lex->having_fix_field= 1; + if (join->having->fix_fields(thd, join->tables_list, &join->having)) { - my_error(ER_CARDINALITY_COL, MYF(0), 1); - DBUG_VOID_RETURN; + select_lex->having_fix_field= 0; + DBUG_RETURN(ERROR); } - else - item= (Item*) sl->item_list.pop(); - - sl->order_list.empty(); // no sense in ORDER BY without LIMIT - - if (sl->having || sl->with_sum_func || sl->group_list.elements) + select_lex->having_fix_field= 0; + } + else + { + select_lex->item_list.empty(); + select_lex->item_list.push_back(new Item_int("Not_used", + (longlong) 1, 21)); + select_lex->ref_pointer_array[0]= select_lex->item_list.head(); + if (select_lex->table_list.elements) { - sl->item_list.push_back(item); - setup_ref_array(thd, &sl->ref_pointer_array, - 1 + sl->with_sum_func + - sl->order_list.elements + sl->group_list.elements); - // To prevent crash on Item_ref_null_helper destruction in case of error - sl->ref_pointer_array[0]= 0; - item= (*func)(expr, new Item_ref_null_helper(this, - sl->ref_pointer_array, - (char *)"", - this->full_name())); - sl->having= and_items(sl->having, item); - } - else - { - sl->item_list.empty(); - sl->item_list.push_back(new Item_int("Not_used", (longlong) 1, 21)); - if (sl->table_list.elements) + Item *having= item, *isnull= item; + if (item->type() == Item::FIELD_ITEM && + ((Item_field*) item)->field_name[0] == '*') { - Item *having= item, *isnull= item; - if (item->type() == Item::FIELD_ITEM && - ((Item_field*) item)->field_name[0] == '*') + Item_asterisk_remover *remover; + item= remover= new Item_asterisk_remover(this, item, + (char *)"", + (char *)""); + if (!abort_on_null) { - Item_asterisk_remover *remover; - item= remover= new Item_asterisk_remover(this, item, - (char*)"", - (char*)""); having= new Item_is_not_null_test(this, new Item_ref(remover->storage(), - (char*)"", - (char*)"")); + (char *)"", + (char *)"")); isnull= new Item_is_not_null_test(this, new Item_ref(remover->storage(), - (char*)"", - (char*)"")); + (char *)"", + (char *)"")); } + } + item= (*func)(expr, item); + if (!abort_on_null) + { having= new Item_is_not_null_test(this, having); - sl->having= (sl->having ? - new Item_cond_and(having, sl->having) : - having); - item= new Item_cond_or((*func)(expr, item), + join->having= (join->having ? + new Item_cond_and(having, join->having) : + having); + select_lex->having_fix_field= 1; + if (join->having->fix_fields(thd, join->tables_list, &join->having)) + { + select_lex->having_fix_field= 0; + DBUG_RETURN(ERROR); + } + select_lex->having_fix_field= 0; + item= new Item_cond_or(item, new Item_func_isnull(isnull)); - sl->where= and_items(sl->where, item); + } + join->conds= and_items(join->conds, item); + if (join->conds->fix_fields(thd, join->tables_list, &join->conds)) + DBUG_RETURN(ERROR); + } + else + { + if (item->type() == Item::FIELD_ITEM && + ((Item_field*) item)->field_name[0] == '*') + { + my_error(ER_NO_TABLES_USED, MYF(0)); + DBUG_RETURN(ERROR); + } + if (select_lex->master_unit()->first_select()->next_select()) + { + /* + It is in union => we should perform it. + Item_asterisk_remover used only as wrapper to receine NULL value + */ + join->having= (*func)(expr, + new Item_asterisk_remover(this, item, + (char *)"", + (char *)"")); + select_lex->having_fix_field= 1; + if (join->having->fix_fields(thd, join->tables_list, &join->having)) + { + select_lex->having_fix_field= 0; + DBUG_RETURN(ERROR); + } + select_lex->having_fix_field= 0; } else { - if (item->type() == Item::FIELD_ITEM && - ((Item_field*) item)->field_name[0] == '*') + // it is single select without tables => possible optimization + item= (*func)(left_expr, item); + // fix_field of item will be done in time of substituting + substitution= item; + have_to_be_excluded= 1; + if (thd->lex.describe) { - my_error(ER_NO_TABLES_USED, MYF(0)); - DBUG_VOID_RETURN; - } - if (unit->first_select()->next_select()) - { - /* - It is in union => we should perform it. - Item_asterisk_remover used only as wrapper to receine NULL value - */ - sl->having= (*func)(expr, - new Item_asterisk_remover(this, item, - (char *)"", - (char*)"")); - } - else - { - // it is single select without tables => possible optimization - item= (*func)(left_expr, item); - substitution= item; - have_to_be_excluded= 1; - if (thd->lex.describe) - { - char warn_buff[MYSQL_ERRMSG_SIZE]; - sprintf(warn_buff, ER(ER_SELECT_REDUCED), sl->select_number); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_SELECT_REDUCED, warn_buff); - } + char warn_buff[MYSQL_ERRMSG_SIZE]; + sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_SELECT_REDUCED, warn_buff); } + DBUG_RETURN(REDUCE); } } } - DBUG_VOID_RETURN; + DBUG_RETURN(OK); } -void Item_in_subselect::row_value_transformer(THD *thd, - st_select_lex_unit *unit, +Item_subselect::trans_res +Item_in_subselect::row_value_transformer(THD *thd, + JOIN *join, Item *left_expr) { DBUG_ENTER("Item_in_subselect::row_value_transformer"); - if (unit->global_parameters->select_limit != - HA_POS_ERROR) + + thd->where= "row IN/ALL/ANY subquery"; + + SELECT_LEX *select_lex= join->select_lex; + + if (!substitution) { - /* - Because we do the following (not exactly, following is just explenation) - transformation - SELECT * from t1 WHERE t1.a IN (SELECT t2.a FROM t2) - -> - SELECT * from t1 WHERE EXISTS(SELECT 1 FROM t2 t1.a = t2.a LIMIT 1) + //first call for this unit + SELECT_LEX_UNIT *unit= select_lex->master_unit(); + substitution= optimizer= new Item_in_optimizer(left_expr, this); - it's impossible to support limit in the sub select. - */ - my_error(ER_NOT_SUPPORTED_YET, MYF(0), - "LIMIT & IN/ALL/ANY/SOME subquery"); - DBUG_VOID_RETURN; + SELECT_LEX_NODE *current= thd->lex.current_select, *up; + thd->lex.current_select= up= current->return_after_parsing(); + //optimizer never use Item **ref => we can pass 0 as parameter + if (!optimizer || optimizer->fix_left(thd, up->get_table_list(), 0)) + { + thd->lex.current_select= current; + DBUG_RETURN(ERROR); + } + thd->lex.current_select= current; + + unit->dependent= 1; } - // no sense in ORDER BY without LIMIT - unit->global_parameters->order_list.empty(); - Item_in_optimizer *optimizer; - substitution= optimizer= new Item_in_optimizer(left_expr, this); - if (!optimizer) - DBUG_VOID_RETURN; - - unit->dependent= 1; uint n= left_expr->cols(); - if (optimizer->preallocate_row() || (*optimizer->get_cache())->allocate(n)) - DBUG_VOID_RETURN; - for (SELECT_LEX * sl= unit->first_select(); sl; sl= sl->next_select()) + + select_lex->dependent= 1; + + Item *item= 0; + List_iterator_fast li(select_lex->item_list); + for (uint i= 0; i < n; i++) { - if (sl->select_limit != HA_POS_ERROR) - { - my_error(ER_NOT_SUPPORTED_YET, MYF(0), - "LIMIT & IN/ALL/ANY/SOME subquery"); - DBUG_VOID_RETURN; - } - sl->order_list.empty(); // no sense in ORDER BY without LIMIT - - sl->dependent= 1; - - Item *item= 0; - List_iterator_fast li(sl->item_list); - for (uint i= 0; i < n; i++) - { - Item *func= - new Item_ref_on_list_position(this, sl, i, - (char *) "", - (char *) ""); - func= - Item_bool_func2::eq_creator(new Item_ref((*optimizer->get_cache())-> - addr(i), - (char *)"", - (char *)""), - func); - item= and_items(item, func); - } - - if (sl->having || sl->with_sum_func || sl->group_list.first || - !sl->table_list.elements) - sl->having= and_items(sl->having, item); - else - sl->where= and_items(sl->where, item); + Item *func= + new Item_ref_on_list_position(this, select_lex, i, + (char *) "", + (char *) ""); + func= + Item_bool_func2::eq_creator(new Item_ref((*optimizer->get_cache())-> + addr(i), + (char *)"", + (char *)""), + func); + item= and_items(item, func); } - DBUG_VOID_RETURN; + + if (join->having || select_lex->with_sum_func || + select_lex->group_list.first || + !select_lex->table_list.elements) + { + join->having= and_items(join->having, item); + select_lex->having_fix_field= 1; + if (join->having->fix_fields(thd, join->tables_list, &join->having)) + { + select_lex->having_fix_field= 0; + DBUG_RETURN(ERROR); + } + select_lex->having_fix_field= 0; + } + else + { + join->conds= and_items(join->conds, item); + if (join->conds->fix_fields(thd, join->tables_list, &join->having)) + DBUG_RETURN(ERROR); + } + DBUG_RETURN(OK); } - -void Item_in_subselect::select_transformer(THD *thd, st_select_lex_unit *unit) +Item_subselect::trans_res +Item_in_subselect::select_transformer(THD *thd, JOIN *join) { if (left_expr->cols() == 1) - single_value_transformer(thd, unit, left_expr, - &Item_bool_func2::eq_creator); + return single_value_transformer(thd, join, left_expr, + &Item_bool_func2::eq_creator); else - row_value_transformer(thd, unit, left_expr); + return row_value_transformer(thd, join, left_expr); } -void Item_allany_subselect::select_transformer(THD *thd, - st_select_lex_unit *unit) +Item_subselect::trans_res +Item_allany_subselect::select_transformer(THD *thd, + JOIN *join) { - single_value_transformer(thd, unit, left_expr, func); + return single_value_transformer(thd, join, left_expr, func); } subselect_single_select_engine::subselect_single_select_engine(THD *thd, diff --git a/sql/item_subselect.h b/sql/item_subselect.h index fc4dad5a6b3..3e8dd0e02dc 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -46,6 +46,8 @@ protected: bool have_to_be_excluded; public: + enum trans_res {OK, REDUCE, ERROR}; + Item_subselect(); Item_subselect(Item_subselect *item) { @@ -71,7 +73,7 @@ public: { null_value= 1; } - virtual void select_transformer(THD *thd, st_select_lex_unit *unit); + virtual trans_res select_transformer(THD *thd, JOIN *join); bool assigned() { return value_assigned; } void assigned(bool a) { value_assigned= a; } enum Type type() const; @@ -83,6 +85,13 @@ public: bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref); virtual void fix_length_and_dec(); table_map used_tables() const; + void print(String *str) + { + if (name) + str->append(name); + else + str->append("-subselect-"); + } friend class select_subselect; friend class Item_in_optimizer; @@ -105,7 +114,7 @@ public: decimals= item->decimals; } void reset(); - void select_transformer(THD *thd, st_select_lex_unit *unit); + trans_res select_transformer(THD *thd, JOIN *join); void store(uint i, Item* item); double val(); longlong val_int (); @@ -161,25 +170,35 @@ class Item_in_subselect :public Item_exists_subselect { protected: Item * left_expr; + /* + expr & optinizer used in subselect rewriting to store Item for + all JOIN in UNION + */ + Item *expr; + Item_in_optimizer *optimizer; bool was_null; + bool abort_on_null; public: Item_in_subselect(THD *thd, Item * left_expr, st_select_lex *select_lex); Item_in_subselect(Item_in_subselect *item); - Item_in_subselect(): Item_exists_subselect() {} + Item_in_subselect(): Item_exists_subselect(), abort_on_null(0) {} void reset() { value= 0; null_value= 0; was_null= 0; } - virtual void select_transformer(THD *thd, st_select_lex_unit *unit); - void single_value_transformer(THD *thd, st_select_lex_unit *unit, - Item *left_expr, compare_func_creator func); - void row_value_transformer(THD *thd, st_select_lex_unit *unit, - Item *left_expr); + trans_res select_transformer(THD *thd, JOIN *join); + trans_res single_value_transformer(THD *thd, JOIN *join, + Item *left_expr, + compare_func_creator func); + trans_res row_value_transformer(THD *thd, JOIN * join, + Item *left_expr); longlong val_int(); double val(); String *val_str(String*); + void top_level_item() { abort_on_null=1; } + bool test_limit(st_select_lex_unit *unit); friend class Item_asterisk_remover; friend class Item_ref_null_helper; @@ -196,7 +215,7 @@ public: Item_allany_subselect(THD *thd, Item * left_expr, compare_func_creator f, st_select_lex *select_lex); Item_allany_subselect(Item_allany_subselect *item); - virtual void select_transformer(THD *thd, st_select_lex_unit *unit); + trans_res select_transformer(THD *thd, JOIN *join); }; class subselect_engine: public Sql_alloc diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 7f555f37d40..f4b4097ed90 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -196,7 +196,8 @@ int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, if (tables) { for (TABLE_LIST *cursor= tables; cursor; cursor= cursor->next) - cursor->table_list->table=cursor->table; + if (cursor->table_list) + cursor->table_list->table=cursor->table; } } else diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 9bc4dfc74e7..329ea6eb281 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1302,11 +1302,13 @@ bool st_select_lex_unit::create_total_list_n_last_return(THD *thd, st_lex *lex, return 1; } *new_table_list= cursor; + cursor->table_list= aux; //to be able mark this table as shared new_table_list= &cursor->next; *new_table_list= 0; // end result list } else - aux->shared= 1; // Mark that it's used twice + // Mark that it's used twice + cursor->table_list->shared= aux->shared= 1; aux->table_list= cursor; } } @@ -1403,6 +1405,7 @@ ulong st_select_lex::get_table_join_options() } st_select_lex::st_select_lex(struct st_lex *lex) + :fake_select(0) { select_number= ++lex->thd->select_number; init_query(); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index e03814bcd2f..ff5f829e4f3 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -352,6 +352,8 @@ public: bool braces; /* SELECT ... UNION (SELECT ... ) <- this braces */ /* TRUE when having fix field called in processing of this SELECT */ bool having_fix_field; + /* TRUE for fake select, which used in UNION processing */ + bool fake_select; void init_query(); void init_select(); @@ -405,7 +407,7 @@ public: friend void mysql_init_query(THD *thd); st_select_lex(struct st_lex *lex); - st_select_lex() {} + st_select_lex() :fake_select(0) {} void make_empty_select(st_select_lex *last_select) { select_number=INT_MAX; diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc index a5f164e1e38..ef7bf013be8 100644 --- a/sql/sql_olap.cc +++ b/sql/sql_olap.cc @@ -150,7 +150,8 @@ int handle_olaps(LEX *lex, SELECT_LEX *select_lex) { if (cursor->do_redirect) { - cursor->table= ((TABLE_LIST*) cursor->table)->table; + //Sinisa TODO: there are function for this purpose: fix_tables_pointers + cursor->table= cursor->table_list->table; cursor->do_redirect= 0; } } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0603470d0c6..f9353152754 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -201,7 +201,8 @@ void relink_tables(SELECT_LEX *select_lex) for (TABLE_LIST *cursor= (TABLE_LIST *) select_lex->table_list.first; cursor; cursor=cursor->next) - cursor->table= cursor->table_list->table; + if (cursor->table_list) + cursor->table= cursor->table_list->table; } @@ -314,6 +315,19 @@ JOIN::prepare(Item ***rref_pointer_array, having->split_sum_func(ref_pointer_array, all_fields); } + // Is it subselect + { + Item_subselect *subselect; + if ((subselect= select_lex->master_unit()->item) && + !select_lex->fake_select) + { + Item_subselect::trans_res res; + if ((res= subselect->select_transformer(thd, this)) != + Item_subselect::OK) + DBUG_RETURN((res == Item_subselect::ERROR)); + } + } + if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */ DBUG_RETURN(-1); /* diff --git a/sql/sql_union.cc b/sql/sql_union.cc index fe4ca49da14..44ee4898433 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -188,42 +188,33 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result, union_result->not_describe=1; union_result->tmp_table_param=tmp_table_param; - /* - The following piece of code is placed here solely for the purpose of - getting correct results with EXPLAIN when UNION is withing a sub-select - or derived table ... - */ - - if (thd->lex.describe) + for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select()) { - for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select()) - { - JOIN *join= new JOIN(thd, sl->item_list, - sl->options | thd->options | SELECT_NO_UNLOCK, - union_result); - thd->lex.current_select= sl; - offset_limit_cnt= sl->offset_limit; - select_limit_cnt= sl->select_limit+sl->offset_limit; - if (select_limit_cnt < sl->select_limit) - select_limit_cnt= HA_POS_ERROR; // no limit - if (select_limit_cnt == HA_POS_ERROR) - sl->options&= ~OPTION_FOUND_ROWS; - - res= join->prepare(&sl->ref_pointer_array, - (TABLE_LIST*) sl->table_list.first, sl->with_wild, - sl->where, - ((sl->braces) ? sl->order_list.elements : 0) + - sl->group_list.elements, - (sl->braces) ? - (ORDER *)sl->order_list.first : (ORDER *) 0, - (ORDER*) sl->group_list.first, - sl->having, - (ORDER*) NULL, - sl, this, t_and_f); - t_and_f= 0; - if (res || thd->is_fatal_error) - goto err; - } + JOIN *join= new JOIN(thd, sl->item_list, + sl->options | thd->options | SELECT_NO_UNLOCK, + union_result); + thd->lex.current_select= sl; + offset_limit_cnt= sl->offset_limit; + select_limit_cnt= sl->select_limit+sl->offset_limit; + if (select_limit_cnt < sl->select_limit) + select_limit_cnt= HA_POS_ERROR; // no limit + if (select_limit_cnt == HA_POS_ERROR) + sl->options&= ~OPTION_FOUND_ROWS; + + res= join->prepare(&sl->ref_pointer_array, + (TABLE_LIST*) sl->table_list.first, sl->with_wild, + sl->where, + ((sl->braces) ? sl->order_list.elements : 0) + + sl->group_list.elements, + (sl->braces) ? + (ORDER *)sl->order_list.first : (ORDER *) 0, + (ORDER*) sl->group_list.first, + sl->having, + (ORDER*) NULL, + sl, this, t_and_f); + t_and_f= 0; + if (res || thd->is_fatal_error) + goto err; } item_list.empty(); @@ -268,14 +259,12 @@ int st_select_lex_unit::exec() } for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select()) { + thd->lex.current_select= sl; + if (optimized) res= sl->join->reinit(); else { - JOIN *join= new JOIN(thd, sl->item_list, - sl->options | thd->options | SELECT_NO_UNLOCK, - union_result); - thd->lex.current_select= sl; offset_limit_cnt= sl->offset_limit; select_limit_cnt= sl->select_limit+sl->offset_limit; if (select_limit_cnt < sl->select_limit) @@ -283,22 +272,36 @@ int st_select_lex_unit::exec() if (select_limit_cnt == HA_POS_ERROR) sl->options&= ~OPTION_FOUND_ROWS; - res= join->prepare(&sl->ref_pointer_array, - (TABLE_LIST*) sl->table_list.first, sl->with_wild, - sl->where, - ((sl->braces) ? sl->order_list.elements : 0) + - sl->group_list.elements, - (sl->braces) ? - (ORDER *)sl->order_list.first : (ORDER *) 0, - (ORDER*) sl->group_list.first, - sl->having, - (ORDER*) NULL, - sl, this, t_and_f); - t_and_f=0; - if (res | thd->is_fatal_error) + /* + As far as union share table space we should reassign table map, + which can be spoiled by 'prepare' of JOIN of other UNION parts + if it use same tables + */ + uint tablenr=0; + for (TABLE_LIST *table_list= (TABLE_LIST*) sl->table_list.first; + table_list; + table_list= table_list->next, tablenr++) { - thd->lex.current_select= lex_select_save; - DBUG_RETURN(res); + if (table_list->shared) + { + /* + review notes: Check it carefully. I still can't understand + why I should not touch table->used_keys. For my point of + view we should do here same procedura as it was done by + setup_table + */ + DBUG_PRINT("SUBS", ("shared %s", table_list->real_name)); + TABLE *table= table_list->table; + table->used_fields=0; + table->const_table=0; + table->outer_join=table->null_row=0; + table->status=STATUS_NO_RECORD; + table->keys_in_use_for_query= table->keys_in_use; + table->maybe_null=test(table->outer_join=table_list->outer_join); + table->tablenr=tablenr; + table->map= (table_map) 1 << tablenr; + table->force_index= table_list->force_index; + } } res= sl->join->optimize(); } @@ -334,6 +337,7 @@ int st_select_lex_unit::exec() if (!thd->is_fatal_error) // Check if EOM { SELECT_LEX *fake_select = new SELECT_LEX(&thd->lex); + fake_select->fake_select= 1; offset_limit_cnt= (select_cursor->braces ? global_parameters->offset_limit : 0); select_limit_cnt= (select_cursor->braces ? diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 9314767e7c9..ab2b92ffbab 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5009,7 +5009,9 @@ subselect_start: { LEX *lex=Lex; if (((int)lex->sql_command >= (int)SQLCOM_HA_OPEN && - lex->sql_command <= (int)SQLCOM_HA_READ) || lex->sql_command == (int)SQLCOM_KILL) { + lex->sql_command <= (int)SQLCOM_HA_READ) || + lex->sql_command == (int)SQLCOM_KILL) + { send_error(lex->thd, ER_SYNTAX_ERROR); YYABORT; } diff --git a/sql/table.h b/sql/table.h index 2aefe23cb2f..abe867246dc 100644 --- a/sql/table.h +++ b/sql/table.h @@ -163,15 +163,8 @@ typedef struct st_table_list struct st_table_list *natural_join; /* natural join on this table*/ /* ... join ... USE INDEX ... IGNORE INDEX */ List *use_index, *ignore_index; - /* - Usually hold reference on opened table, but may hold reference - to node of complete list of tables used in UNION & subselect. - */ - union - { - TABLE *table; /* opened table */ - st_table_list *table_list; /* pointer to node of list of all tables */ - }; + TABLE *table; /* opened table */ + st_table_list *table_list; /* pointer to node of list of all tables */ class st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */ GRANT_INFO grant; thr_lock_type lock_type;