From 07727576b8179eab503bcdaa2f3eafd64ffacf22 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Sun, 27 Oct 2002 23:27:00 +0200 Subject: [PATCH] IN subselect --- mysql-test/r/subselect.result | 11 ++++++ mysql-test/t/subselect.test | 6 +++ sql/item.cc | 47 ++++++++++++++++++++++ sql/item.h | 49 +++++++++++++++++++++++ sql/item_subselect.cc | 74 ++++++++++++++++++++++++++++++++--- sql/item_subselect.h | 21 ++++++++-- sql/sql_yacc.yy | 37 ++++++++++++++++-- 7 files changed, 232 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 5c5b8984102..b232061585d 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -96,6 +96,17 @@ select * from t3 where not exists (select * from t2 where t2.b=t3.a); a 6 3 +select * from t3 where a in (select b from t2); +a +7 +select * from t3 where a not in (select b from t2); +a +6 +3 +select * from t3 where a in (select a,b from t2); +Subselect returns more than 1 field +select * from t3 where a in (select * from t2); +Subselect returns more than 1 field insert into t4 values (12,7),(1,7),(10,9),(9,6),(7,6),(3,9); select b,max(a) as ma from t4 group by b having b < (select max(t2.a) from t2 where t2.b=t4.b); diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index c74922d3d9e..83c81fbbfc5 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -34,6 +34,12 @@ select b,(select avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a)) from t2 explain select b,(select avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a)) from t2) from t4; select * from t3 where exists (select * from t2 where t2.b=t3.a); select * from t3 where not exists (select * from t2 where t2.b=t3.a); +select * from t3 where a in (select b from t2); +select * from t3 where a not in (select b from t2); +-- error 1239 +select * from t3 where a in (select a,b from t2); +-- error 1239 +select * from t3 where a in (select * from t2); insert into t4 values (12,7),(1,7),(10,9),(9,6),(7,6),(3,9); select b,max(a) as ma from t4 group by b having b < (select max(t2.a) from t2 where t2.b=t4.b); diff --git a/sql/item.cc b/sql/item.cc index f8404f40421..97c09207e38 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -424,6 +424,53 @@ bool Item::fix_fields(THD *thd, return 0; } +bool Item_outer_select_context_saver::fix_fields(THD *thd, + struct st_table_list *list, + Item ** ref) +{ + DBUG_ENTER("Item_outer_select_context_saver::fix_fields"); + bool res= item->fix_fields(thd, + 0, // do not show current subselect fields + &item); + *ref= item; + DBUG_RETURN(res); +} + +bool Item_asterisk_remover::fix_fields(THD *thd, + struct st_table_list *list, + Item ** ref) +{ + DBUG_ENTER("Item_asterisk_remover::fix_fields"); + + bool res; + if (item) + if (item->type() == Item::FIELD_ITEM && + ((Item_field*) item)->field_name[0] == '*') + { + List fields; + fields.push_back(item); + List_iterator it(fields); + it++; + uint elem=fields.elements; + if (insert_fields(thd, list, ((Item_field*) item)->db_name, + ((Item_field*) item)->table_name, &it)) + res= -1; + else + if (fields.elements > 1) + { + my_message(ER_SUBSELECT_NO_1_COL, ER(ER_SUBSELECT_NO_1_COL), MYF(0)); + res= -1; + } + } + else + res= item->fix_fields(thd, list, &item); + else + res= -1; + *ref= item; + DBUG_RETURN(res); +} + + bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { if (!field) // If field is not checked diff --git a/sql/item.h b/sql/item.h index fc36148e443..2b92d7c9ed8 100644 --- a/sql/item.h +++ b/sql/item.h @@ -90,6 +90,55 @@ public: }; +/* + Wrapper base class +*/ + +class Item_wrapper :public Item +{ +protected: + Item *item; +public: + /* + Following methods should not be used, because fix_fields exclude this + item (it assign '*ref' with field 'item' in derived classes) + */ + enum Type type() const { return item->type(); } + double val() { return item->val(); } + longlong val_int() { return item->val_int(); } + String* val_str(String* s) { return item->val_str(s); } + void make_field(Send_field* f) { item->make_field(f); } +}; + + +/* + Save context of name resolution for Item, used in subselect transformer. +*/ +class Item_outer_select_context_saver :public Item_wrapper +{ + Item *item; +public: + Item_outer_select_context_saver(Item *i): + item(i) + { + } + bool fix_fields(THD *, struct st_table_list *, Item ** ref); +}; + +/* + To resolve '*' field moved to condition +*/ +class Item_asterisk_remover :public Item_wrapper +{ + Item *item; +public: + Item_asterisk_remover(Item *i): + item(i) + { + } + bool fix_fields(THD *, struct st_table_list *, Item ** ref); +}; + class st_select_lex; class Item_ident :public Item { diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 456ce5f22ba..66238934e29 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -44,12 +44,13 @@ Item_subselect::Item_subselect(): } void Item_subselect::init(THD *thd, st_select_lex *select_lex, - select_subselect *result) + select_subselect *result, Item *left_expr) { DBUG_ENTER("Item_subselect::init"); - DBUG_PRINT("subs", ("select_lex 0x%xl", (long) select_lex)); + DBUG_PRINT("subs", ("select_lex 0x%xl", (ulong) select_lex)); + select_transformer(select_lex, left_expr); if (select_lex->next_select()) engine= new subselect_union_engine(thd, select_lex->master_unit(), result, this); @@ -65,6 +66,14 @@ Item_subselect::~Item_subselect() delete engine; } +void Item_subselect::select_transformer(st_select_lex *select_lex, + Item *left_expr) +{ + DBUG_ENTER("Item_subselect::select_transformer"); + DBUG_VOID_RETURN; +} + + void Item_subselect::make_field (Send_field *tmp_field) { if (null_value) @@ -109,9 +118,11 @@ Item_singleval_subselect::Item_singleval_subselect(THD *thd, st_select_lex *select_lex): Item_subselect() { - init(thd, select_lex, new select_singleval_subselect(this)); + DBUG_ENTER("Item_singleval_subselect::Item_singleval_subselect"); + init(thd, select_lex, new select_singleval_subselect(this), 0); max_columns= 1; maybe_null= 1; + DBUG_VOID_RETURN; } void Item_singleval_subselect::fix_length_and_dec() @@ -156,17 +167,37 @@ String *Item_singleval_subselect::val_str (String *str) } Item_exists_subselect::Item_exists_subselect(THD *thd, - st_select_lex *select_lex): + st_select_lex *select_lex, + Item *left_expr): Item_subselect() { - init(thd, select_lex, new select_exists_subselect(this)); + DBUG_ENTER("Item_exists_subselect::Item_exists_subselect"); + init(thd, select_lex, new select_exists_subselect(this), left_expr); max_columns= UINT_MAX; null_value= 0; //can't be NULL maybe_null= 0; //can't be NULL value= 0; - select_lex->select_limit= 1; // we need only 1 row to determinate existence + // We need only 1 row to determinate existence + select_lex->master_unit()->global_parameters->select_limit= 1; + DBUG_VOID_RETURN; } +Item_in_subselect::Item_in_subselect(THD *thd, Item * left_expr, + st_select_lex *select_lex): + Item_exists_subselect() +{ + DBUG_ENTER("Item_in_subselect::Item_in_subselect"); + init(thd, select_lex, new select_exists_subselect(this), left_expr); + max_columns= UINT_MAX; + null_value= 0; //can't be NULL + maybe_null= 0; //can't be NULL + value= 0; + // We need only 1 row to determinate existence + select_lex->master_unit()->global_parameters->select_limit= 1; + DBUG_VOID_RETURN; +} + + void Item_exists_subselect::fix_length_and_dec() { max_length= 1; @@ -204,6 +235,37 @@ String *Item_exists_subselect::val_str(String *str) return str; } +Item_in_subselect::Item_in_subselect(Item_in_subselect *item): + Item_exists_subselect(item) +{ +} + +void Item_in_subselect::select_transformer(st_select_lex *select_lex, + Item *left_expr) +{ + DBUG_ENTER("Item_in_subselect::select_transformer"); + for(SELECT_LEX * sl= select_lex; sl; sl= sl->next_select()) + { + Item *item; + if (sl->item_list.elements > 1) + { + my_message(ER_SUBSELECT_NO_1_COL, ER(ER_SUBSELECT_NO_1_COL), MYF(0)); + item= 0; // Item_asterisk_remover mast fail + } + else + item= (Item*) sl->item_list.pop(); + sl->item_list.empty(); + sl->item_list.push_back(new Item_int(1)); + left_expr= new Item_outer_select_context_saver(left_expr); + item= new Item_asterisk_remover(item); + if (sl->where) + sl->where= new Item_cond_and(sl->where, + new Item_func_eq(item, left_expr)); + else + sl->where= new Item_func_eq(item, left_expr); + } + DBUG_VOID_RETURN; +} subselect_single_select_engine::subselect_single_select_engine(THD *thd, st_select_lex *select, diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 33f82982708..5a5f7bd58d8 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -55,13 +55,16 @@ public: pointer in constructor initialization list, but we need pass pointer to subselect Item class to select_subselect classes constructor. */ - void init (THD *thd, st_select_lex *select_lex, select_subselect *result); + virtual void init (THD *thd, st_select_lex *select_lex, + select_subselect *result, + Item *left_expr= 0); ~Item_subselect(); virtual void assign_null() { null_value= 1; } + virtual void select_transformer(st_select_lex *select_lex, Item *left_expr); bool assigned() { return value_assigned; } void assigned(bool a) { value_assigned= a; } enum Type type() const; @@ -74,7 +77,6 @@ public: friend class select_subselect; }; - /* single value subselect */ class Item_singleval_subselect :public Item_subselect @@ -127,12 +129,15 @@ protected: longlong value; /* value of this item (boolean: exists/not-exists) */ public: - Item_exists_subselect(THD *thd, st_select_lex *select_lex); + Item_exists_subselect(THD *thd, st_select_lex *select_lex, + Item *left_expr= 0); Item_exists_subselect(Item_exists_subselect *item): Item_subselect(item) { value= item->value; } + Item_exists_subselect(): Item_subselect() {} + virtual void assign_null() { value= 0; @@ -147,6 +152,16 @@ public: friend class select_exists_subselect; }; +/* IN subselect */ + +class Item_in_subselect :public Item_exists_subselect +{ +public: + Item_in_subselect(THD *thd, Item * left_expr, st_select_lex *select_lex); + Item_in_subselect(Item_in_subselect *item); + virtual void select_transformer(st_select_lex *select_lex, Item *left_exp); +}; + class subselect_engine { protected: diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 7d68278e39f..81ee899cfd4 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -70,6 +70,7 @@ inline Item *or_or_concat(Item* A, Item* B) enum Item_udftype udf_type; CHARSET_INFO *charset; interval_type interval; + st_select_lex *select_lex; } %{ @@ -528,7 +529,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %type IDENT TEXT_STRING REAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM LEX_HOSTNAME - ULONGLONG_NUM field_ident select_alias ident ident_or_text UNDERSCORE_CHARSET + ULONGLONG_NUM field_ident select_alias ident ident_or_text + UNDERSCORE_CHARSET %type opt_table_alias @@ -610,6 +612,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %type internal_variable_name +%type in_subselect in_subselect_init + %type query verb_clause create change select do drop insert replace insert2 insert_values update delete truncate rename @@ -1706,10 +1710,16 @@ expr: expr_expr { $$= $1; } /* expressions that begin with 'expr' */ expr_expr: - expr IN_SYM '(' expr_list ')' + expr IN_SYM '(' expr_list ')' { $$= new Item_func_in($1,*$4); } | expr NOT IN_SYM '(' expr_list ')' { $$= new Item_func_not(new Item_func_in($1,*$5)); } + | expr IN_SYM in_subselect + { $$= new Item_in_subselect(current_thd, $1, $3); } + | expr NOT IN_SYM in_subselect + { + $$= new Item_func_not(new Item_in_subselect(current_thd, $1, $4)); + } | expr BETWEEN_SYM no_and_expr AND expr { $$= new Item_func_between($1,$3,$5); } | expr NOT BETWEEN_SYM no_and_expr AND expr @@ -1789,10 +1799,16 @@ no_in_expr: /* expressions that begin with 'expr' that does NOT follow AND */ no_and_expr: - no_and_expr IN_SYM '(' expr_list ')' - { $$= new Item_func_in($1,*$4); } + no_and_expr IN_SYM '(' expr_list ')' + { $$= new Item_func_in($1,*$4); } | no_and_expr NOT IN_SYM '(' expr_list ')' { $$= new Item_func_not(new Item_func_in($1,*$5)); } + | no_and_expr IN_SYM in_subselect + { $$= new Item_in_subselect(current_thd, $1, $3); } + | no_and_expr NOT IN_SYM in_subselect + { + $$= new Item_func_not(new Item_in_subselect(current_thd, $1, $4)); + } | no_and_expr BETWEEN_SYM no_and_expr AND expr { $$= new Item_func_between($1,$3,$5); } | no_and_expr NOT BETWEEN_SYM no_and_expr AND expr @@ -4180,6 +4196,19 @@ exists_subselect_init: Lex->select->master_unit()->first_select()); }; +in_subselect: + subselect_start in_subselect_init + subselect_end + { + $$= $2; + }; + +in_subselect_init: + select_init + { + $$= Lex->select->master_unit()->first_select(); + }; + subselect_start: '(' {