From b785e980bd2436e5892db2b9502a66841ae438e7 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Thu, 19 Dec 2002 07:38:32 +0200 Subject: [PATCH 1/4] optimized single value subselect (to be used as row) (SCRUM) --- sql/item.h | 101 ++++++++++++++++++++++++++++++++++++++---- sql/item_subselect.cc | 61 +++++++++++++++++++++---- sql/item_subselect.h | 32 ++++--------- sql/sql_class.cc | 26 +---------- 4 files changed, 154 insertions(+), 66 deletions(-) diff --git a/sql/item.h b/sql/item.h index 1ea76731fd3..461a3805080 100644 --- a/sql/item.h +++ b/sql/item.h @@ -31,12 +31,12 @@ public: static void *operator new(size_t size) {return (void*) sql_alloc((uint) size); } static void operator delete(void *ptr,size_t size) {} /*lint -e715 */ - enum Type {FIELD_ITEM,FUNC_ITEM,SUM_FUNC_ITEM,STRING_ITEM, - INT_ITEM,REAL_ITEM,NULL_ITEM,VARBIN_ITEM, - COPY_STR_ITEM,FIELD_AVG_ITEM, DEFAULT_ITEM, - PROC_ITEM,COND_ITEM,REF_ITEM,FIELD_STD_ITEM, - FIELD_VARIANCE_ITEM,CONST_ITEM, - SUBSELECT_ITEM, ROW_ITEM}; + enum Type {FIELD_ITEM, FUNC_ITEM, SUM_FUNC_ITEM, STRING_ITEM, + INT_ITEM, REAL_ITEM, NULL_ITEM, VARBIN_ITEM, + COPY_STR_ITEM, FIELD_AVG_ITEM, DEFAULT_ITEM, + PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM, + FIELD_VARIANCE_ITEM, CONST_ITEM, + SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM}; enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE }; String str_value; /* used to store value */ @@ -381,7 +381,8 @@ public: name=(char*) str_value.ptr(); decimals=NOT_FIXED_DEC; } - Item_string(const char *name_par,const char *str,uint length,CHARSET_INFO *cs) + Item_string(const char *name_par, const char *str, uint length, + CHARSET_INFO *cs) { str_value.set(str,length,cs); max_length=length; @@ -392,11 +393,13 @@ public: enum Type type() const { return STRING_ITEM; } double val() { - return my_strntod(str_value.charset(),str_value.ptr(),str_value.length(),(char**)NULL); + return my_strntod(str_value.charset(), str_value.ptr(), + str_value.length(), (char**) 0); } longlong val_int() { - return my_strntoll(str_value.charset(),str_value.ptr(),str_value.length(),(char**) 0,10); + return my_strntoll(str_value.charset(), str_value.ptr(), + str_value.length(), (char**) 0, 10); } String *val_str(String*) { return (String*) &str_value; } int save_in_field(Field *field, bool no_conversions); @@ -706,6 +709,86 @@ public: bool cmp(void); }; +class Item_cache: public Item +{ +public: + virtual void store(Item *)= 0; + void set_len_n_dec(uint32 max_len, uint8 dec) + { + max_length= max_len; + decimals= dec; + } + enum Type type() const { return CACHE_ITEM; } +}; + +class Item_cache_int: public Item_cache +{ + longlong value; +public: + Item_cache_int() { fixed= 1; null_value= 1; } + + void store(Item *item) + { + value= item->val_int_result(); + null_value= item->null_value; + } + double val() { return (double) value; } + longlong val_int() { return value; } + String* val_str(String *str) { str->set(value, thd_charset()); return str; } + enum Item_result result_type() const { return INT_RESULT; } +}; + +class Item_cache_real: public Item_cache +{ + double value; +public: + Item_cache_real() { fixed= 1; null_value= 1; } + + void store(Item *item) + { + value= item->val_result(); + null_value= item->null_value; + } + double val() { return value; } + longlong val_int() { return (longlong) (value+(value > 0 ? 0.5 : -0.5)); } + String* val_str(String *str) + { + str->set(value, decimals, thd_charset()); + return str; + } + enum Item_result result_type() const { return REAL_RESULT; } +}; + +class Item_cache_str: public Item_cache +{ + char buffer[80]; + String *value; +public: + Item_cache_str() { fixed= 1; null_value= 1; } + + void store(Item *item) + { + str_value.set(buffer, sizeof(buffer), item->charset()); + value= item->str_result(&str_value); + // TODO remove if str_value charset have no side effect for now + str_value.set_charset(value->charset()); + null_value= item->null_value; + } + double val() + { + return my_strntod(value->charset(), value->ptr(), + value->length(), (char**)0); + } + longlong val_int() + { + return my_strntoll(value->charset(), value->ptr(), + value->length(), (char**) 0, 10); + } + String* val_str(String *) { return value; } + enum Item_result result_type() const { return STRING_RESULT; } + CHARSET_INFO *charset() const { return value->charset(); }; +}; + extern Item_buff *new_Item_buff(Item *item); extern Item_result item_cmp_type(Item_result a,Item_result b); extern Item *resolve_const_item(Item *item,Item *cmp_item); diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index a8bbc433b7a..588be06a7e7 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -124,7 +124,7 @@ inline table_map Item_subselect::used_tables() const Item_singleval_subselect::Item_singleval_subselect(THD *thd, st_select_lex *select_lex): - Item_subselect() + Item_subselect(), value(0) { DBUG_ENTER("Item_singleval_subselect::Item_singleval_subselect"); init(thd, select_lex, new select_singleval_subselect(this)); @@ -133,10 +133,43 @@ Item_singleval_subselect::Item_singleval_subselect(THD *thd, DBUG_VOID_RETURN; } +void Item_singleval_subselect::reset() +{ + null_value= 1; + if (value) + value->null_value= 1; +} + +void Item_singleval_subselect::store(Item *item) +{ + value->store(item); +} + +enum Item_result Item_singleval_subselect::result_type() const +{ + return engine->type(); +} + void Item_singleval_subselect::fix_length_and_dec() { engine->fix_length_and_dec(); - res_type= engine->type(); + switch (engine->type()) + { + case INT_RESULT: + value= new Item_cache_int(); + break; + case REAL_RESULT: + value= new Item_cache_real(); + break; + case STRING_RESULT: + value= new Item_cache_str(); + break; + default: + // should never be in real life + DBUG_ASSERT(0); + return; + } + value->set_len_n_dec(max_length, decimals); } Item::Type Item_subselect::type() const @@ -146,32 +179,44 @@ Item::Type Item_subselect::type() const double Item_singleval_subselect::val () { - if (engine->exec()) + if (!engine->exec() && !value->null_value) + { + null_value= 0; + return value->val(); + } + else { reset(); return 0; } - return real_value; } longlong Item_singleval_subselect::val_int () { - if (engine->exec()) + if (!engine->exec() && !value->null_value) + { + null_value= 0; + return value->val_int(); + } + else { reset(); return 0; } - return int_value; } String *Item_singleval_subselect::val_str (String *str) { - if (engine->exec() || null_value) + if (!engine->exec() && !value->null_value) + { + null_value= 0; + return value->val_str(str); + } + else { reset(); return 0; } - return &string_value; } Item_exists_subselect::Item_exists_subselect(THD *thd, diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 0e6f939803d..a43caca22f2 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -90,44 +90,28 @@ public: /* single value subselect */ +class Item_cache; class Item_singleval_subselect :public Item_subselect { protected: - longlong int_value; /* Here stored integer value of this item */ - double real_value; /* Here stored real value of this item */ - /* - Here stored string value of this item. - (str_value used only as temporary buffer, because it can be changed - by Item::save_field) - */ - String string_value; - enum Item_result res_type; /* type of results */ + Item_cache *value; public: Item_singleval_subselect(THD *thd, st_select_lex *select_lex); Item_singleval_subselect(Item_singleval_subselect *item): Item_subselect(item) { - int_value= item->int_value; - real_value= item->real_value; - string_value.set(item->string_value, 0, item->string_value.length()); + value= item->value; max_length= item->max_length; decimals= item->decimals; - res_type= item->res_type; } - virtual void reset() - { - null_value= 1; - int_value= 0; - real_value= 0; - max_length= 4; - res_type= STRING_RESULT; - } - double val (); + void reset(); + void store(Item* item); + double val(); longlong val_int (); String *val_str (String *); Item *new_item() { return new Item_singleval_subselect(this); } - enum Item_result result_type() const { return res_type; } + enum Item_result result_type() const; void fix_length_and_dec(); friend class select_singleval_subselect; @@ -149,7 +133,7 @@ public: } Item_exists_subselect(): Item_subselect() {} - virtual void reset() + void reset() { value= 0; } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 9e0489edb28..50164aed342 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -932,31 +932,7 @@ bool select_singleval_subselect::send_data(List &items) } List_iterator_fast li(items); Item *val_item= li++; // Only one (single value subselect) - /* - Following val() call have to be first, because function AVG() & STD() - calculate value on it & determinate "is it NULL?". - */ - it->real_value= val_item->val_result(); - if ((it->null_value= val_item->null_value)) - { - it->reset(); - } - else - { - it->max_length= val_item->max_length; - it->decimals= val_item->decimals; - it->set_charset(val_item->charset()); - it->int_value= val_item->val_int_result(); - String *s= val_item->str_result(&it->string_value); - if (s != &it->string_value) - { - it->string_value.set(*s, 0, s->length()); - } - // TODO: remove when correct charset handling appeared for Item - it->str_value.set(*s, 0, s->length()); // store charset - - it->res_type= val_item->result_type(); - } + it->store(val_item); it->assigned(1); DBUG_RETURN(0); } From 4cfb6d97b54a12ea4e5d9e2eb0b671e602ee99f2 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Thu, 19 Dec 2002 07:38:33 +0200 Subject: [PATCH 2/4] support of simple row subselects (SCRUM) --- mysql-test/r/subselect.result | 70 +++++++++++++- mysql-test/t/subselect.test | 27 +++++- sql/item.cc | 54 +++++++++++ sql/item.h | 26 ++---- sql/item_cmpfunc.cc | 7 ++ sql/item_subselect.cc | 169 ++++++++++++++++++++++++++-------- sql/item_subselect.h | 18 ++-- sql/sql_class.cc | 5 +- 8 files changed, 307 insertions(+), 69 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 493768ece4a..d45b7b24585 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -52,6 +52,54 @@ a SELECT (SELECT 1) as a FROM (SELECT 1) b WHERE (SELECT a) IS NOT NULL; a 1 +SELECT (SELECT 1,2,3) = ROW(1,2,3); +(SELECT 1,2,3) = ROW(1,2,3) +1 +SELECT (SELECT 1,2,3) = ROW(1,2,1); +(SELECT 1,2,3) = ROW(1,2,1) +0 +SELECT (SELECT 1,2,3) < ROW(1,2,1); +(SELECT 1,2,3) < ROW(1,2,1) +0 +SELECT (SELECT 1,2,3) > ROW(1,2,1); +(SELECT 1,2,3) > ROW(1,2,1) +1 +SELECT (SELECT 1,2,3) = ROW(1,2,NULL); +(SELECT 1,2,3) = ROW(1,2,NULL) +NULL +SELECT ROW(1,2,3) = (SELECT 1,2,3); +ROW(1,2,3) = (SELECT 1,2,3) +1 +SELECT ROW(1,2,3) = (SELECT 1,2,1); +ROW(1,2,3) = (SELECT 1,2,1) +0 +SELECT ROW(1,2,3) < (SELECT 1,2,1); +ROW(1,2,3) < (SELECT 1,2,1) +0 +SELECT ROW(1,2,3) > (SELECT 1,2,1); +ROW(1,2,3) > (SELECT 1,2,1) +1 +SELECT ROW(1,2,3) = (SELECT 1,2,NULL); +ROW(1,2,3) = (SELECT 1,2,NULL) +NULL +SELECT (SELECT 1.5,2,'a') = ROW(1.5,2,'a'); +(SELECT 1.5,2,'a') = ROW(1.5,2,'a') +1 +SELECT (SELECT 1.5,2,'a') = ROW(1.5,2,'b'); +(SELECT 1.5,2,'a') = ROW(1.5,2,'b') +0 +SELECT (SELECT 1.5,2,'a') = ROW('b',2,'b'); +(SELECT 1.5,2,'a') = ROW('b',2,'b') +0 +SELECT (SELECT 'b',2,'a') = ROW(1.5,2,'a'); +(SELECT 'b',2,'a') = ROW(1.5,2,'a') +0 +SELECT (SELECT 1.5,2,'a') = ROW(1.5,'c','a'); +(SELECT 1.5,2,'a') = ROW(1.5,'c','a') +0 +SELECT (SELECT 1.5,'c','a') = ROW(1.5,2,'a'); +(SELECT 1.5,'c','a') = ROW(1.5,2,'a') +0 drop table if exists t1,t2,t3,t4,t5,t6,t7,t8; create table t1 (a int); create table t2 (a int, b int); @@ -602,7 +650,7 @@ CREATE TABLE t1 (id int(11) default NULL, KEY id (id)) TYPE=MyISAM CHARSET=latin INSERT INTO t1 values (1),(1); UPDATE t SET id=(SELECT * FROM t1); Subselect returns more than 1 record -drop table t; +drop table t, t1; create table t (a int); insert into t values (1),(2),(3); select 1 IN (SELECT * from t); @@ -705,3 +753,23 @@ select 10.5 > ANY (SELECT * from t); 10.5 > ANY (SELECT * from t) 1 drop table t; +create table t1 (a int, b int, c varchar(10)); +create table t2 (a int); +insert into t1 values (1,2,'a'),(2,3,'b'),(3,4,'c'); +insert into t2 values (1),(2),(NULL); +select a, (select a,b,c from t1 where t1.a=t2.a) = ROW(a,2,'a'),(select c from t1 where a=t2.a) from t2; +a (select a,b,c from t1 where t1.a=t2.a) = ROW(a,2,'a') (select c from t1 where a=t2.a) +1 1 a +2 0 b +NULL NULL NULL +select a, (select a,b,c from t1 where t1.a=t2.a) = ROW(a,3,'b'),(select c from t1 where a=t2.a) from t2; +a (select a,b,c from t1 where t1.a=t2.a) = ROW(a,3,'b') (select c from t1 where a=t2.a) +1 0 a +2 1 b +NULL NULL NULL +select a, (select a,b,c from t1 where t1.a=t2.a) = ROW(a,4,'c'),(select c from t1 where a=t2.a) from t2; +a (select a,b,c from t1 where t1.a=t2.a) = ROW(a,4,'c') (select c from t1 where a=t2.a) +1 0 a +2 0 b +NULL NULL NULL +drop table t1,t2; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 0527d6a2001..ee3d31e66af 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -26,6 +26,22 @@ select (SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE(1)); SELECT 1 FROM (SELECT 1) a PROCEDURE ANALYSE((SELECT 1)); SELECT (SELECT 1) as a FROM (SELECT 1) b WHERE (SELECT a) IS NULL; SELECT (SELECT 1) as a FROM (SELECT 1) b WHERE (SELECT a) IS NOT NULL; +SELECT (SELECT 1,2,3) = ROW(1,2,3); +SELECT (SELECT 1,2,3) = ROW(1,2,1); +SELECT (SELECT 1,2,3) < ROW(1,2,1); +SELECT (SELECT 1,2,3) > ROW(1,2,1); +SELECT (SELECT 1,2,3) = ROW(1,2,NULL); +SELECT ROW(1,2,3) = (SELECT 1,2,3); +SELECT ROW(1,2,3) = (SELECT 1,2,1); +SELECT ROW(1,2,3) < (SELECT 1,2,1); +SELECT ROW(1,2,3) > (SELECT 1,2,1); +SELECT ROW(1,2,3) = (SELECT 1,2,NULL); +SELECT (SELECT 1.5,2,'a') = ROW(1.5,2,'a'); +SELECT (SELECT 1.5,2,'a') = ROW(1.5,2,'b'); +SELECT (SELECT 1.5,2,'a') = ROW('b',2,'b'); +SELECT (SELECT 'b',2,'a') = ROW(1.5,2,'a'); +SELECT (SELECT 1.5,2,'a') = ROW(1.5,'c','a'); +SELECT (SELECT 1.5,'c','a') = ROW(1.5,2,'a'); drop table if exists t1,t2,t3,t4,t5,t6,t7,t8; create table t1 (a int); @@ -363,7 +379,7 @@ CREATE TABLE t1 (id int(11) default NULL, KEY id (id)) TYPE=MyISAM CHARSET=latin INSERT INTO t1 values (1),(1); -- error 1240 UPDATE t SET id=(SELECT * FROM t1); -drop table t; +drop table t, t1; #NULL test @@ -409,3 +425,12 @@ select 10.5 > ALL (SELECT * from t); select 1.5 > ANY (SELECT * from t); select 10.5 > ANY (SELECT * from t); drop table t; + +create table t1 (a int, b int, c varchar(10)); +create table t2 (a int); +insert into t1 values (1,2,'a'),(2,3,'b'),(3,4,'c'); +insert into t2 values (1),(2),(NULL); +select a, (select a,b,c from t1 where t1.a=t2.a) = ROW(a,2,'a'),(select c from t1 where a=t2.a) from t2; +select a, (select a,b,c from t1 where t1.a=t2.a) = ROW(a,3,'b'),(select c from t1 where a=t2.a) from t2; +select a, (select a,b,c from t1 where t1.a=t2.a) = ROW(a,4,'c'),(select c from t1 where a=t2.a) from t2; +drop table t1,t2; diff --git a/sql/item.cc b/sql/item.cc index fbe0e27a01c..44851d26ef1 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1217,6 +1217,60 @@ bool field_is_equal_to_item(Field *field,Item *item) return result == field->val_real(); } +Item_cache* Item_cache::get_cache(Item_result type) +{ + switch (type) + { + case INT_RESULT: + return new Item_cache_int(); + case REAL_RESULT: + return new Item_cache_real(); + case STRING_RESULT: + return new Item_cache_str(); + default: + // should never be in real life + DBUG_ASSERT(0); + return 0; + } +} + +void Item_cache_str::store(Item *item) +{ + str_value.set(buffer, sizeof(buffer), item->charset()); + value= item->str_result(&str_value); + if ((null_value= item->null_value)) + value= 0; + else if (value != &str_value) + { + /* + We copy string value to avoid changing value if 'item' is table field + in queries like following (where t1.c is varchar): + select a, + (select a,b,c from t1 where t1.a=t2.a) = ROW(a,2,'a'), + (select c from t1 where a=t2.a) + from t2; + */ + str_value.copy(*value); + value= &str_value; + } + +} +double Item_cache_str::val() +{ + if (value) + return my_strntod(value->charset(), value->ptr(), + value->length(), (char**)0); + else + return (double)0; +} +longlong Item_cache_str::val_int() +{ + if (value) + return my_strntoll(value->charset(), value->ptr(), + value->length(), (char**) 0, 10); + else + return (longlong)0; +} /***************************************************************************** ** Instantiate templates diff --git a/sql/item.h b/sql/item.h index 461a3805080..d11148f8b3f 100644 --- a/sql/item.h +++ b/sql/item.h @@ -103,7 +103,9 @@ public: virtual Item** addr(uint i) { return 0; } virtual bool check_cols(uint c); // It is not row => null inside is impossible - virtual bool null_inside() { return 0; }; + virtual bool null_inside() { return 0; } + // used in row subselects to get value of elements + virtual void bring_value() {} }; @@ -719,6 +721,7 @@ public: decimals= dec; } enum Type type() const { return CACHE_ITEM; } + static Item_cache* get_cache(Item_result type); }; class Item_cache_int: public Item_cache @@ -766,24 +769,9 @@ class Item_cache_str: public Item_cache public: Item_cache_str() { fixed= 1; null_value= 1; } - void store(Item *item) - { - str_value.set(buffer, sizeof(buffer), item->charset()); - value= item->str_result(&str_value); - // TODO remove if str_value charset have no side effect for now - str_value.set_charset(value->charset()); - null_value= item->null_value; - } - double val() - { - return my_strntod(value->charset(), value->ptr(), - value->length(), (char**)0); - } - longlong val_int() - { - return my_strntoll(value->charset(), value->ptr(), - value->length(), (char**) 0, 10); - } + void store(Item *item); + double val(); + longlong val_int(); String* val_str(String *) { return value; } enum Item_result result_type() const { return STRING_RESULT; } CHARSET_INFO *charset() const { return value->charset(); }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 851a591bae9..39840fc90fd 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -247,6 +247,8 @@ int Arg_comparator::compare_e_int() int Arg_comparator::compare_row() { int res= 0; + (*a)->bring_value(); + (*b)->bring_value(); uint n= (*a)->cols(); for (uint i= 0; ibring_value(); + (*b)->bring_value(); uint n= (*a)->cols(); for (uint i= 0; icols(); if ((comparators= (cmp_item **) thd->alloc(sizeof(cmp_item *)*n))) { + item->bring_value(); item->null_value= 0; for (uint i=0; i < n; i++) if ((comparators[i]= cmp_item::get_comparator(item->el(i)))) @@ -1252,6 +1257,7 @@ void cmp_item_row::store_value_by_template(cmp_item *t, Item *item) n= tmpl->n; if ((comparators= (cmp_item **) sql_alloc(sizeof(cmp_item *)*n))) { + item->bring_value(); item->null_value= 0; for (uint i=0; i < n; i++) if ((comparators[i]= tmpl->comparators[i]->make_same())) @@ -1284,6 +1290,7 @@ int cmp_item_row::cmp(Item *arg) return 1; } bool was_null= 0; + arg->bring_value(); for (uint i=0; i < n; i++) if (comparators[i]->cmp(arg->el(i))) { diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 588be06a7e7..972c265a16b 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -112,9 +112,14 @@ bool Item_subselect::check_loop(uint id) DBUG_RETURN(engine->check_loop(id)); } +Item::Type Item_subselect::type() const +{ + return SUBSELECT_ITEM; +} + void Item_subselect::fix_length_and_dec() { - engine->fix_length_and_dec(); + engine->fix_length_and_dec(0); } inline table_map Item_subselect::used_tables() const @@ -130,6 +135,7 @@ Item_singleval_subselect::Item_singleval_subselect(THD *thd, init(thd, select_lex, new select_singleval_subselect(this)); max_columns= 1; maybe_null= 1; + max_columns= UINT_MAX; DBUG_VOID_RETURN; } @@ -140,9 +146,9 @@ void Item_singleval_subselect::reset() value->null_value= 1; } -void Item_singleval_subselect::store(Item *item) +void Item_singleval_subselect::store(uint i, Item *item) { - value->store(item); + row[i]->store(item); } enum Item_result Item_singleval_subselect::result_type() const @@ -152,29 +158,58 @@ enum Item_result Item_singleval_subselect::result_type() const void Item_singleval_subselect::fix_length_and_dec() { - engine->fix_length_and_dec(); - switch (engine->type()) + if ((max_columns= engine->cols()) == 1) { - case INT_RESULT: - value= new Item_cache_int(); - break; - case REAL_RESULT: - value= new Item_cache_real(); - break; - case STRING_RESULT: - value= new Item_cache_str(); - break; - default: - // should never be in real life - DBUG_ASSERT(0); - return; + engine->fix_length_and_dec(row= &value); + if (!(value= Item_cache::get_cache(engine->type()))) + { + my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); + current_thd->fatal_error= 1; + return; + } + } + else + { + THD *thd= current_thd; + if (!(row= (Item_cache**)thd->alloc(sizeof(Item_cache*)*max_columns))) + { + my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); + thd->fatal_error= 1; + return; + } + engine->fix_length_and_dec(row); + value= *row; } - value->set_len_n_dec(max_length, decimals); } -Item::Type Item_subselect::type() const +uint Item_singleval_subselect::cols() { - return SUBSELECT_ITEM; + return engine->cols(); +} + +bool Item_singleval_subselect::check_cols(uint c) +{ + if (c != engine->cols()) + { + my_error(ER_CARDINALITY_COL, MYF(0), c); + return 1; + } + return 0; +} + +bool Item_singleval_subselect::null_inside() +{ + for (uint i= 0; i < max_columns ; i++) + { + if (row[i]->null_value) + return 1; + } + return 0; +} + +void Item_singleval_subselect::bring_value() +{ + engine->exec(); } double Item_singleval_subselect::val () @@ -268,7 +303,7 @@ Item_allany_subselect::Item_allany_subselect(THD *thd, Item * left_exp, void Item_exists_subselect::fix_length_and_dec() { - decimals=0; + decimals= 0; max_length= 1; } @@ -540,31 +575,85 @@ int subselect_union_engine::prepare() return unit->prepare(thd, result); } -void subselect_single_select_engine::fix_length_and_dec() +static Item_result set_row(SELECT_LEX *select_lex, Item * item, + Item_cache **row) { + Item_result res_type= STRING_RESULT; + Item *sel_item; List_iterator_fast li(select_lex->item_list); - Item *sel_item= li++; - item->max_length= sel_item->max_length; - res_type= sel_item->result_type(); - item->decimals= sel_item->decimals; + for (uint i= 0; (sel_item= li++); i++) + { + item->max_length= sel_item->max_length; + res_type= sel_item->result_type(); + item->decimals= sel_item->decimals; + if (row) + { + if (!(row[i]= Item_cache::get_cache(res_type))) + { + my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); + current_thd->fatal_error= 1; + return STRING_RESULT; // we should return something + } + row[i]->set_len_n_dec(sel_item->max_length, sel_item->decimals); + } + } + if (select_lex->item_list.elements > 1) + res_type= ROW_RESULT; + return res_type; } -void subselect_union_engine::fix_length_and_dec() +void subselect_single_select_engine::fix_length_and_dec(Item_cache **row) { - uint32 mlen= 0, len; - Item *sel_item= 0; - for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + DBUG_ASSERT(row || select_lex->item_list.elements==1); + res_type= set_row(select_lex, item, row); +} + +void subselect_union_engine::fix_length_and_dec(Item_cache **row) +{ + DBUG_ASSERT(row || unit->first_select()->item_list.elements==1); + + if (unit->first_select()->item_list.elements == 1) { - List_iterator_fast li(sl->item_list); - Item *s_item= li++; - if ((len= s_item->max_length)) - mlen= len; - if (!sel_item) - sel_item= s_item; + uint32 mlen= 0, len; + Item *sel_item= 0; + for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + { + List_iterator_fast li(sl->item_list); + Item *s_item= li++; + if ((len= s_item->max_length) > mlen) + mlen= len; + if (!sel_item) + sel_item= s_item; + } + item->max_length= mlen; + res_type= sel_item->result_type(); + item->decimals= sel_item->decimals; + if (row) + { + if (!(row[0]= Item_cache::get_cache(res_type))) + { + my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); + current_thd->fatal_error= 1; + return; + } + row[0]->set_len_n_dec(mlen, sel_item->decimals); + } + } + else + { + SELECT_LEX *sl= unit->first_select(); + res_type= set_row(sl, item, row); + for(sl= sl->next_select(); sl; sl->next_select()) + { + List_iterator_fast li(sl->item_list); + Item *sel_item; + for (uint i= 0; (sel_item= li++); i++) + { + if (sel_item->max_length > row[i]->max_length) + row[i]->max_length= sel_item->max_length; + } + } } - item->max_length= mlen; - res_type= sel_item->result_type(); - item->decimals= sel_item->decimals; } int subselect_single_select_engine::exec() diff --git a/sql/item_subselect.h b/sql/item_subselect.h index a43caca22f2..3cb68cb3875 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -94,8 +94,7 @@ class Item_cache; class Item_singleval_subselect :public Item_subselect { protected: - Item_cache *value; - + Item_cache *value, **row; public: Item_singleval_subselect(THD *thd, st_select_lex *select_lex); Item_singleval_subselect(Item_singleval_subselect *item): @@ -106,7 +105,7 @@ public: decimals= item->decimals; } void reset(); - void store(Item* item); + void store(uint i, Item* item); double val(); longlong val_int (); String *val_str (String *); @@ -114,6 +113,13 @@ public: enum Item_result result_type() const; void fix_length_and_dec(); + uint cols(); + Item* el(uint i) { return (Item*)row[i]; } + Item** addr(uint i) { return (Item**)row + i; } + bool check_cols(uint c); + bool null_inside(); + void bring_value(); + friend class select_singleval_subselect; }; @@ -212,7 +218,7 @@ public: } virtual int prepare()= 0; - virtual void fix_length_and_dec()= 0; + virtual void fix_length_and_dec(Item_cache** row)= 0; virtual int exec()= 0; virtual uint cols()= 0; /* return number of columnss in select */ virtual bool depended()= 0; /* depended from outer select */ @@ -233,7 +239,7 @@ public: select_subselect *result, Item_subselect *item); int prepare(); - void fix_length_and_dec(); + void fix_length_and_dec(Item_cache** row); int exec(); uint cols(); bool depended(); @@ -250,7 +256,7 @@ public: select_subselect *result, Item_subselect *item); int prepare(); - void fix_length_and_dec(); + void fix_length_and_dec(Item_cache** row); int exec(); uint cols(); bool depended(); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 50164aed342..0b12a34ebfb 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -931,8 +931,9 @@ bool select_singleval_subselect::send_data(List &items) DBUG_RETURN(0); } List_iterator_fast li(items); - Item *val_item= li++; // Only one (single value subselect) - it->store(val_item); + Item *val_item; + for (uint i= 0; (val_item= li++); i++) + it->store(i, val_item); it->assigned(1); DBUG_RETURN(0); } From 54c7afd2a58c0c808b32da95091191f00179a727 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Thu, 19 Dec 2002 13:35:12 +0200 Subject: [PATCH 3/4] Item_in_optimizer is changed to be used with row IN subselect (SCRUM) removed unused items --- sql/item.cc | 31 ----------------- sql/item.h | 72 --------------------------------------- sql/item_cmpfunc.cc | 78 ++++++++++++++++++++----------------------- sql/item_cmpfunc.h | 19 ++++------- sql/item_subselect.cc | 5 +-- 5 files changed, 46 insertions(+), 159 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index 44851d26ef1..340eb2c985c 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -47,11 +47,6 @@ Item::Item(): loop_id= 0; } -Item_ref_in_optimizer::Item_ref_in_optimizer(Item_in_optimizer *master, - char *table_name_par, - char *field_name_par): - Item_ref(master->args, table_name_par, field_name_par), owner(master) {} - bool Item::check_loop(uint id) { @@ -437,20 +432,6 @@ String *Item_copy_string::val_str(String *str) return &str_value; } -double Item_ref_in_optimizer::val() -{ - return owner->get_cache(); -} -longlong Item_ref_in_optimizer::val_int() -{ - return owner->get_cache_int(); -} -String* Item_ref_in_optimizer::val_str(String* s) -{ - return owner->get_cache_str(s); -} - - /* Functions to convert item to field (for send_fields) */ @@ -464,18 +445,6 @@ 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) diff --git a/sql/item.h b/sql/item.h index d11148f8b3f..21087cfe0e2 100644 --- a/sql/item.h +++ b/sql/item.h @@ -109,60 +109,6 @@ 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(); } - enum_field_types field_type() const { return item->field_type(); } - double val() { return item->val(); } - longlong val_int() { return item->val_int(); } - String* val_str(String* s) { return item->val_str(s); } - bool check_cols(uint col) { return item->check_cols(col); } - bool eq(const Item *item, bool binary_cmp) const - { return item->eq(item, binary_cmp); } - bool is_null() - { - item->val_int(); - return item->null_value; - } - bool get_date(TIME *ltime, bool fuzzydate) - { - return (null_value=item->get_date(ltime, fuzzydate)); - } - bool send(Protocol *prot, String *tmp) { return item->send(prot, tmp); } - int save_in_field(Field *field, bool no_conversions) - { - return item->save_in_field(field, no_conversions); - } - void save_org_in_field(Field *field) { item->save_org_in_field(field); } - enum Item_result result_type () const { return item->result_type(); } - table_map used_tables() const { return item->used_tables(); } -}; - - -/* - Save context of name resolution for Item, used in subselect transformer. -*/ -class Item_outer_select_context_saver :public Item_wrapper -{ -public: - Item_outer_select_context_saver(Item *it) - { - item= it; - } - bool fix_fields(THD *, struct st_table_list *, Item ** ref); -}; - class st_select_lex; class Item_ident :public Item { @@ -579,24 +525,6 @@ public: bool fix_fields(THD *, struct st_table_list *, Item ** ref); }; -class Item_in_optimizer; -class Item_ref_in_optimizer: public Item_ref -{ -protected: - Item_in_optimizer* owner; -public: - Item_ref_in_optimizer(Item_in_optimizer* master, - char *table_name_par,char *field_name_par); - double val(); - longlong val_int(); - String* val_str(String* s); - bool fix_fields(THD *, struct st_table_list *, Item ** ref) - { - fixed= 1; - return 0; - } -}; - /* The following class is used to optimize comparing of date columns We need to save the original item, to be able to set the field to the diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 39840fc90fd..be25a2609b0 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -274,13 +274,41 @@ int Arg_comparator::compare_e_row() return 1; } +bool Item_in_optimizer::fix_fields(THD *thd, struct st_table_list *tables, + Item ** ref) +{ + + if (args[0]->check_cols(allowed_arg_cols) || + args[0]->fix_fields(thd, tables, args)) + return 1; + if (args[0]->maybe_null) + maybe_null=1; + if (args[0]->binary()) + set_charset(my_charset_bin); + with_sum_func= args[0]->with_sum_func; + used_tables_cache= args[0]->used_tables(); + const_item_cache= args[0]->const_item(); + if (!(cache= Item_cache::get_cache(args[0]->result_type()))) + { + my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); + thd->fatal_error= 1; + return 1; + } + if (args[1]->check_cols(allowed_arg_cols) || + args[1]->fix_fields(thd, tables, args)) + return 1; + if (args[1]->maybe_null) + maybe_null=1; + with_sum_func= with_sum_func || args[1]->with_sum_func; + used_tables_cache|= args[1]->used_tables(); + const_item_cache&= args[1]->const_item(); + return 0; +} + longlong Item_in_optimizer::val_int() { - int_cache_ok= 1; - flt_cache_ok= 0; - str_cache_ok= 0; - int_cache= args[0]->val_int_result(); - if (args[0]->null_value) + cache->store(args[0]); + if (cache->null_value) { null_value= 1; return 0; @@ -290,44 +318,10 @@ longlong Item_in_optimizer::val_int() return tmp; } -longlong Item_in_optimizer::get_cache_int() +bool Item_in_optimizer::is_null() { - if (!int_cache_ok) - { - int_cache_ok= 1; - flt_cache_ok= 0; - str_cache_ok= 0; - int_cache= args[0]->val_int_result(); - null_value= args[0]->null_value; - } - return int_cache; -} - -double Item_in_optimizer::get_cache() -{ - if (!flt_cache_ok) - { - flt_cache_ok= 1; - int_cache_ok= 0; - str_cache_ok= 0; - flt_cache= args[0]->val_result(); - null_value= args[0]->null_value; - } - return flt_cache; -} - -String *Item_in_optimizer::get_cache_str(String *s) -{ - if (!str_cache_ok) - { - str_cache_ok= 1; - int_cache_ok= 0; - flt_cache_ok= 0; - str_value.set(buffer, sizeof(buffer), s->charset()); - str_cache= args[0]->str_result(&str_value); - null_value= args[0]->null_value; - } - return str_cache; + cache->store(args[0]); + return (null_value= (cache->null_value || args[1]->is_null())); } longlong Item_func_eq::val_int() diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 994e51ef89f..41171da1338 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -85,25 +85,20 @@ public: void fix_length_and_dec() { decimals=0; max_length=1; } }; +class Item_cache; class Item_in_optimizer: public Item_bool_func { protected: - char buffer[80]; - longlong int_cache; - double flt_cache; - String *str_cache; - bool int_cache_ok, flt_cache_ok, str_cache_ok; + Item_cache *cache; public: Item_in_optimizer(Item *a,Item *b): - Item_bool_func(a,b), int_cache_ok(0), flt_cache_ok(0), str_cache_ok(0) {} - bool is_null() { return test(args[0]->is_null() || args[1]->is_null()); } + Item_bool_func(a,b), cache(0) {} + bool fix_fields(THD *, struct st_table_list *, Item **); + bool is_null(); longlong val_int(); + + Item **get_cache() { return (Item**)&cache; } - double get_cache(); - longlong get_cache_int(); - String *get_cache_str(String *s); - - friend class Item_ref_in_optimizer; }; class Item_bool_func2 :public Item_int_func diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 972c265a16b..c4a97731625 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -409,8 +409,9 @@ void Item_in_subselect::single_value_transformer(st_select_lex *select_lex, 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_in_optimizer(optimizer, (char *)"", - (char*)""); + Item *expr= new Item_ref(optimizer->get_cache(), + (char *)"", + (char*)""); select_lex->master_unit()->dependent= 1; for (SELECT_LEX * sl= select_lex; sl; sl= sl->next_select()) { From b63f2303468097b780abe011cb1f1565ff8b45e2 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Thu, 19 Dec 2002 21:15:09 +0200 Subject: [PATCH 4/4] row IN subselects (SCRUM) --- mysql-test/r/subselect.result | 31 ++++++++++ mysql-test/t/subselect.test | 14 +++++ sql/item.cc | 109 ++++++++++++++++++++++++++++++++++ sql/item.h | 70 +++++++++++++++++++++- sql/item_cmpfunc.cc | 27 ++++++--- sql/item_cmpfunc.h | 9 +-- sql/item_row.cc | 9 ++- sql/item_row.h | 1 + sql/item_subselect.cc | 97 ++++++++++++++++++++++++------ sql/item_subselect.h | 12 ++-- sql/sql_class.cc | 6 +- sql/sql_class.h | 4 +- sql/sql_yacc.yy | 12 ++-- 13 files changed, 353 insertions(+), 48 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index d45b7b24585..f983825a56b 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -773,3 +773,34 @@ a (select a,b,c from t1 where t1.a=t2.a) = ROW(a,4,'c') (select c from t1 where 2 0 b NULL NULL NULL drop table t1,t2; +drop table if exists t; +create table t (a int, b real, c varchar(10)); +insert into t values (1, 1, 'a'), (2,2,'b'), (NULL, 2, 'b'); +select ROW(1, 1, 'a') IN (select a,b,c from t); +ROW(1, 1, 'a') IN (select a,b,c from t) +1 +select ROW(1, 2, 'a') IN (select a,b,c from t); +ROW(1, 2, 'a') IN (select a,b,c from t) +NULL +select ROW(1, 1, 'a') IN (select b,a,c from t); +ROW(1, 1, 'a') IN (select b,a,c from t) +1 +select ROW(1, 1, 'a') IN (select a,b,c from t where a is not null); +ROW(1, 1, 'a') IN (select a,b,c from t where a is not null) +1 +select ROW(1, 2, 'a') IN (select a,b,c from t where a is not null); +ROW(1, 2, 'a') IN (select a,b,c from t where a is not null) +0 +select ROW(1, 1, 'a') IN (select b,a,c from t where a is not null); +ROW(1, 1, 'a') IN (select b,a,c from t where a is not null) +1 +select ROW(1, 1, 'a') IN (select a,b,c from t where c='b' or c='a'); +ROW(1, 1, 'a') IN (select a,b,c from t where c='b' or c='a') +1 +select ROW(1, 2, 'a') IN (select a,b,c from t where c='b' or c='a'); +ROW(1, 2, 'a') IN (select a,b,c from t where c='b' or c='a') +NULL +select ROW(1, 1, 'a') IN (select b,a,c from t where c='b' or c='a'); +ROW(1, 1, 'a') IN (select b,a,c from t where c='b' or c='a') +1 +drop table if exists t; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index ee3d31e66af..863c5f0ad9c 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -434,3 +434,17 @@ select a, (select a,b,c from t1 where t1.a=t2.a) = ROW(a,2,'a'),(select c from t select a, (select a,b,c from t1 where t1.a=t2.a) = ROW(a,3,'b'),(select c from t1 where a=t2.a) from t2; select a, (select a,b,c from t1 where t1.a=t2.a) = ROW(a,4,'c'),(select c from t1 where a=t2.a) from t2; drop table t1,t2; + +drop table if exists t; +create table t (a int, b real, c varchar(10)); +insert into t values (1, 1, 'a'), (2,2,'b'), (NULL, 2, 'b'); +select ROW(1, 1, 'a') IN (select a,b,c from t); +select ROW(1, 2, 'a') IN (select a,b,c from t); +select ROW(1, 1, 'a') IN (select b,a,c from t); +select ROW(1, 1, 'a') IN (select a,b,c from t where a is not null); +select ROW(1, 2, 'a') IN (select a,b,c from t where a is not null); +select ROW(1, 1, 'a') IN (select b,a,c from t where a is not null); +select ROW(1, 1, 'a') IN (select a,b,c from t where c='b' or c='a'); +select ROW(1, 2, 'a') IN (select a,b,c from t where c='b' or c='a'); +select ROW(1, 1, 'a') IN (select b,a,c from t where c='b' or c='a'); +drop table if exists t; diff --git a/sql/item.cc b/sql/item.cc index 340eb2c985c..37cc9939bde 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -498,6 +498,27 @@ bool Item_asterisk_remover::fix_fields(THD *thd, DBUG_RETURN(res); } +bool Item_ref_on_list_position::fix_fields(THD *thd, + struct st_table_list *tables, + Item ** reference) +{ + ref= 0; + List_iterator li(list); + Item *item; + uint i= 0; + for (; (item= li++) && i < pos; i++); + if (i == pos) + { + ref= li.ref(); + return Item_ref_null_helper::fix_fields(thd, tables, reference); + } + else + { + my_error(ER_CARDINALITY_COL, MYF(0), pos); + return 1; + } +} + double Item_ref_null_helper::val() { double tmp= (*ref)->val_result(); @@ -1196,6 +1217,8 @@ Item_cache* Item_cache::get_cache(Item_result type) return new Item_cache_real(); case STRING_RESULT: return new Item_cache_str(); + case ROW_RESULT: + return new Item_cache_row(); default: // should never be in real life DBUG_ASSERT(0); @@ -1241,6 +1264,92 @@ longlong Item_cache_str::val_int() return (longlong)0; } +bool Item_cache_row::allocate(uint num) +{ + n= num; + THD *thd= current_thd; + if (!(values= (Item_cache **) thd->calloc(sizeof(Item_cache *)*n))) + { + my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); + thd->fatal_error= 1; + return 1; + } + return 0; +} + +bool Item_cache_row::setup(Item * item) +{ + if (!values && allocate(item->cols())) + return 1; + for(uint i= 0; i < n; i++) + { + if (!(values[i]= Item_cache::get_cache(item->el(i)->result_type()))) + { + my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); + current_thd->fatal_error= 1; + return 1; + } + values[i]->setup(item->el(i)); + } + return 0; +} + +void Item_cache_row::store(Item * item) +{ + null_value= 0; + item->bring_value(); + for(uint i= 0; i < n; i++) + { + values[i]->store(item->el(i)); + null_value|= values[i]->null_value; + } +} + +void Item_cache_row::illegal_method_call(const char *method) +{ + DBUG_ENTER("Item_cache_row::illegal_method_call"); + DBUG_PRINT("error", ("!!! %s method was called for row item", method)); + DBUG_ASSERT(0); + my_error(ER_CARDINALITY_COL, MYF(0), 1); + DBUG_VOID_RETURN; +} + +bool Item_cache_row::check_cols(uint c) +{ + if (c != n) + { + my_error(ER_CARDINALITY_COL, MYF(0), c); + return 1; + } + return 0; +} + +bool Item_cache_row::null_inside() +{ + for (uint i= 0; i < n; i++) + { + if (values[i]->cols() > 1) + { + if (values[i]->null_inside()) + return 1; + } + else + { + values[i]->val_int(); + if (values[i]->null_value) + return 1; + } + } + return 0; +} + +void Item_cache_row::bring_value() +{ + for (uint i= 0; i < n; i++) + values[i]->bring_value(); + return; +} + /***************************************************************************** ** Instantiate templates *****************************************************************************/ diff --git a/sql/item.h b/sql/item.h index 21087cfe0e2..4e3c5f597ab 100644 --- a/sql/item.h +++ b/sql/item.h @@ -501,7 +501,7 @@ protected: Item_in_subselect* owner; public: Item_ref_null_helper(Item_in_subselect* master, Item **item, - char *table_name_par,char *field_name_par): + char *table_name_par, char *field_name_par): Item_ref(item, table_name_par, field_name_par), owner(master) {} double val(); longlong val_int(); @@ -509,6 +509,24 @@ public: bool get_date(TIME *ltime, bool fuzzydate); }; + +/* + Used to find item in list of select items after '*' items processing. +*/ +class Item_ref_on_list_position: public Item_ref_null_helper +{ +protected: + List &list; + uint pos; +public: + Item_ref_on_list_position(Item_in_subselect* master, + List &li, uint num, + char *table_name, char *field_name): + Item_ref_null_helper(master, 0, table_name, field_name), + list(li), pos(num) {} + bool fix_fields(THD *, struct st_table_list *, Item ** ref); +}; + /* To resolve '*' field moved to condition and register NULL values @@ -642,6 +660,8 @@ public: class Item_cache: public Item { public: + virtual bool allocate(uint i) { return 0; }; + virtual bool setup(Item *) { return 0; }; virtual void store(Item *)= 0; void set_len_n_dec(uint32 max_len, uint8 dec) { @@ -705,6 +725,54 @@ public: CHARSET_INFO *charset() const { return value->charset(); }; }; +class Item_cache_row: public Item_cache +{ + Item_cache **values; + uint n; +public: + Item_cache_row(): values(0), n(2) { fixed= 1; null_value= 1; } + + /* + 'allocate' used only in row transformer, to preallocate space for row + cache. + */ + bool allocate(uint num); + /* + 'setup' is needed only by row => it not called by simple row subselect + (only by IN subselect (in subselect optimizer)) + */ + bool setup(Item *item); + void store(Item *item); + void illegal_method_call(const char *); + void make_field(Send_field *) + { + illegal_method_call((const char*)"make_field"); + }; + double val() + { + illegal_method_call((const char*)"val"); + return 0; + }; + longlong val_int() + { + illegal_method_call((const char*)"val_int"); + return 0; + }; + String *val_str(String *) + { + illegal_method_call((const char*)"val_str"); + return 0; + }; + enum Item_result result_type() const { return ROW_RESULT; } + + uint cols() { return n; } + Item* el(uint i) { return values[i]; } + Item** addr(uint i) { return (Item **) (values + i); } + bool check_cols(uint c); + bool null_inside(); + void bring_value(); +}; + extern Item_buff *new_Item_buff(Item *item); extern Item_result item_cmp_type(Item_result a,Item_result b); extern Item *resolve_const_item(Item *item,Item *cmp_item); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index be25a2609b0..df949a910bd 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -274,12 +274,19 @@ int Arg_comparator::compare_e_row() return 1; } +bool Item_in_optimizer::preallocate_row() +{ + if ((cache= Item_cache::get_cache(ROW_RESULT))) + return 0; + my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); + current_thd->fatal_error= 1; + return 1; +} + bool Item_in_optimizer::fix_fields(THD *thd, struct st_table_list *tables, Item ** ref) { - - if (args[0]->check_cols(allowed_arg_cols) || - args[0]->fix_fields(thd, tables, args)) + if (args[0]->fix_fields(thd, tables, args)) return 1; if (args[0]->maybe_null) maybe_null=1; @@ -288,15 +295,21 @@ 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= Item_cache::get_cache(args[0]->result_type()))) + if (!cache && !(cache= Item_cache::get_cache(args[0]->result_type()))) { my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); thd->fatal_error= 1; return 1; } - if (args[1]->check_cols(allowed_arg_cols) || - args[1]->fix_fields(thd, tables, args)) + cache->setup(args[0]); + if (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()) + { + my_error(ER_CARDINALITY_COL, MYF(0), args[0]->cols()); + return 1; + } if (args[1]->maybe_null) maybe_null=1; with_sum_func= with_sum_func || args[1]->with_sum_func; @@ -1215,7 +1228,7 @@ void cmp_item_row::store_value(Item *item) { THD *thd= current_thd; n= item->cols(); - if ((comparators= (cmp_item **) thd->alloc(sizeof(cmp_item *)*n))) + if ((comparators= (cmp_item **) thd->calloc(sizeof(cmp_item *)*n))) { item->bring_value(); item->null_value= 0; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 41171da1338..e3d8eb7746d 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -91,14 +91,15 @@ class Item_in_optimizer: public Item_bool_func protected: Item_cache *cache; public: - Item_in_optimizer(Item *a,Item *b): - Item_bool_func(a,b), cache(0) {} + 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 is_null(); longlong val_int(); - Item **get_cache() { return (Item**)&cache; } - + Item_cache **get_cache() { return &cache; } }; class Item_bool_func2 :public Item_int_func diff --git a/sql/item_row.cc b/sql/item_row.cc index 85a81a50256..9d605e05242 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -41,7 +41,7 @@ void Item_row::illegal_method_call(const char *method) DBUG_ENTER("Item_row::illegal_method_call"); DBUG_PRINT("error", ("!!! %s method was called for row item", method)); DBUG_ASSERT(0); - my_error(ER_CARDINALITY_COL, MYF(0), arg_count); + my_error(ER_CARDINALITY_COL, MYF(0), 1); DBUG_VOID_RETURN; } @@ -100,3 +100,10 @@ bool Item_row::null_inside() } return 0; } + +void Item_row::bring_value() +{ + for (uint i= 0; i < arg_count; i++) + items[i]->bring_value(); + return; +} diff --git a/sql/item_row.h b/sql/item_row.h index 4767d19d08f..cf67567c3ed 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -70,4 +70,5 @@ public: Item** addr(uint i) { return items + i; } bool check_cols(uint c); bool null_inside(); + void bring_value(); }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index c4a97731625..bb7a497a15c 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -127,36 +127,36 @@ inline table_map Item_subselect::used_tables() const return (table_map) engine->depended() ? 1L : 0L; } -Item_singleval_subselect::Item_singleval_subselect(THD *thd, +Item_singlerow_subselect::Item_singlerow_subselect(THD *thd, st_select_lex *select_lex): Item_subselect(), value(0) { - DBUG_ENTER("Item_singleval_subselect::Item_singleval_subselect"); - init(thd, select_lex, new select_singleval_subselect(this)); + DBUG_ENTER("Item_singlerow_subselect::Item_singlerow_subselect"); + init(thd, select_lex, new select_singlerow_subselect(this)); max_columns= 1; maybe_null= 1; max_columns= UINT_MAX; DBUG_VOID_RETURN; } -void Item_singleval_subselect::reset() +void Item_singlerow_subselect::reset() { null_value= 1; if (value) value->null_value= 1; } -void Item_singleval_subselect::store(uint i, Item *item) +void Item_singlerow_subselect::store(uint i, Item *item) { row[i]->store(item); } -enum Item_result Item_singleval_subselect::result_type() const +enum Item_result Item_singlerow_subselect::result_type() const { return engine->type(); } -void Item_singleval_subselect::fix_length_and_dec() +void Item_singlerow_subselect::fix_length_and_dec() { if ((max_columns= engine->cols()) == 1) { @@ -182,12 +182,12 @@ void Item_singleval_subselect::fix_length_and_dec() } } -uint Item_singleval_subselect::cols() +uint Item_singlerow_subselect::cols() { return engine->cols(); } -bool Item_singleval_subselect::check_cols(uint c) +bool Item_singlerow_subselect::check_cols(uint c) { if (c != engine->cols()) { @@ -197,7 +197,7 @@ bool Item_singleval_subselect::check_cols(uint c) return 0; } -bool Item_singleval_subselect::null_inside() +bool Item_singlerow_subselect::null_inside() { for (uint i= 0; i < max_columns ; i++) { @@ -207,12 +207,12 @@ bool Item_singleval_subselect::null_inside() return 0; } -void Item_singleval_subselect::bring_value() +void Item_singlerow_subselect::bring_value() { engine->exec(); } -double Item_singleval_subselect::val () +double Item_singlerow_subselect::val () { if (!engine->exec() && !value->null_value) { @@ -226,7 +226,7 @@ double Item_singleval_subselect::val () } } -longlong Item_singleval_subselect::val_int () +longlong Item_singlerow_subselect::val_int () { if (!engine->exec() && !value->null_value) { @@ -240,7 +240,7 @@ longlong Item_singleval_subselect::val_int () } } -String *Item_singleval_subselect::val_str (String *str) +String *Item_singlerow_subselect::val_str (String *str) { if (!engine->exec() && !value->null_value) { @@ -293,7 +293,7 @@ Item_allany_subselect::Item_allany_subselect(THD *thd, Item * left_exp, left_expr= left_exp; func= f; init(thd, select_lex, new select_exists_subselect(this)); - max_columns= UINT_MAX; + max_columns= 1; reset(); // We need only 1 row to determinate existence select_lex->master_unit()->global_parameters->select_limit= 1; @@ -305,6 +305,7 @@ void Item_exists_subselect::fix_length_and_dec() { decimals= 0; max_length= 1; + max_columns= engine->cols(); } double Item_exists_subselect::val () @@ -409,7 +410,7 @@ void Item_in_subselect::single_value_transformer(st_select_lex *select_lex, 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(optimizer->get_cache(), + Item *expr= new Item_ref((Item**)optimizer->get_cache(), (char *)"", (char*)""); select_lex->master_unit()->dependent= 1; @@ -440,7 +441,7 @@ void Item_in_subselect::single_value_transformer(st_select_lex *select_lex, sl->having= item; else if (sl->where) - sl->where= new Item_cond_and(sl->having, item); + sl->where= new Item_cond_and(sl->where, item); else sl->where= item; } @@ -498,10 +499,68 @@ void Item_in_subselect::single_value_transformer(st_select_lex *select_lex, DBUG_VOID_RETURN; } +void Item_in_subselect::row_value_transformer(st_select_lex *select_lex, + Item *left_expr) +{ + DBUG_ENTER("Item_in_subselect::row_value_transformer"); + Item_in_optimizer *optimizer; + substitution= optimizer= new Item_in_optimizer(left_expr, this); + if (!optimizer) + { + current_thd->fatal_error= 1; + DBUG_VOID_RETURN; + } + select_lex->master_unit()->dependent= 1; + uint n= left_expr->cols(); + if (optimizer->preallocate_row() || (*optimizer->get_cache())->allocate(n)) + DBUG_VOID_RETURN; + for (SELECT_LEX * sl= select_lex; sl; sl= sl->next_select()) + { + select_lex->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->item_list, i, + (char *) "", + (char *) ""); + func= + Item_bool_func2::eq_creator(new Item_ref((*optimizer->get_cache())-> + addr(i), + (char *)"", + (char *)""), + func); + if (!item) + item= func; + else + item= new Item_cond_and(item, func); + } + + if (sl->having || sl->with_sum_func || sl->group_list.first || + !sl->table_list.elements) + if (sl->having) + sl->having= new Item_cond_and(sl->having, item); + else + sl->having= item; + else + if (sl->where) + sl->where= new Item_cond_and(sl->where, item); + else + sl->where= item; + } + DBUG_VOID_RETURN; +} + + void Item_in_subselect::select_transformer(st_select_lex *select_lex) { - single_value_transformer(select_lex, left_expr, - &Item_bool_func2::eq_creator); + if (left_expr->cols() == 1) + single_value_transformer(select_lex, left_expr, + &Item_bool_func2::eq_creator); + else + row_value_transformer(select_lex, left_expr); } void Item_allany_subselect::select_transformer(st_select_lex *select_lex) diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 3cb68cb3875..04f0e6f3c83 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -86,18 +86,19 @@ public: bool check_loop(uint id); friend class select_subselect; + friend class Item_in_optimizer; }; /* single value subselect */ class Item_cache; -class Item_singleval_subselect :public Item_subselect +class Item_singlerow_subselect :public Item_subselect { protected: Item_cache *value, **row; public: - Item_singleval_subselect(THD *thd, st_select_lex *select_lex); - Item_singleval_subselect(Item_singleval_subselect *item): + Item_singlerow_subselect(THD *thd, st_select_lex *select_lex); + Item_singlerow_subselect(Item_singlerow_subselect *item): Item_subselect(item) { value= item->value; @@ -109,7 +110,7 @@ public: double val(); longlong val_int (); String *val_str (String *); - Item *new_item() { return new Item_singleval_subselect(this); } + Item *new_item() { return new Item_singlerow_subselect(this); } enum Item_result result_type() const; void fix_length_and_dec(); @@ -120,7 +121,7 @@ public: bool null_inside(); void bring_value(); - friend class select_singleval_subselect; + friend class select_singlerow_subselect; }; /* exists subselect */ @@ -174,6 +175,7 @@ public: virtual void select_transformer(st_select_lex *select_lex); void single_value_transformer(st_select_lex *select_lex, Item *left_expr, compare_func_creator func); + void row_value_transformer(st_select_lex *select_lex, Item *left_expr); longlong val_int(); double val(); String *val_str(String*); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0b12a34ebfb..061bf12b71c 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -916,10 +916,10 @@ select_subselect::select_subselect(Item_subselect *item) this->item=item; } -bool select_singleval_subselect::send_data(List &items) +bool select_singlerow_subselect::send_data(List &items) { - DBUG_ENTER("select_singleval_subselect::send_data"); - Item_singleval_subselect *it= (Item_singleval_subselect *)item; + DBUG_ENTER("select_singlerow_subselect::send_data"); + Item_singlerow_subselect *it= (Item_singlerow_subselect *)item; if (it->assigned()) { my_message(ER_SUBSELECT_NO_1_ROW, ER(ER_SUBSELECT_NO_1_ROW), MYF(0)); diff --git a/sql/sql_class.h b/sql/sql_class.h index 7ca66d9ffb7..3abcf12e4b7 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -824,10 +824,10 @@ public: }; /* Single value subselect interface class */ -class select_singleval_subselect :public select_subselect +class select_singlerow_subselect :public select_subselect { public: - select_singleval_subselect(Item_subselect *item):select_subselect(item){} + select_singlerow_subselect(Item_subselect *item):select_subselect(item){} bool send_data(List &items); }; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 55f165c0739..d160c5281a8 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -587,7 +587,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr table_wild opt_pad no_in_expr expr_expr simple_expr no_and_expr using_list expr_or_default set_expr_or_default - param_marker singleval_subselect singleval_subselect_init + param_marker singlerow_subselect singlerow_subselect_init exists_subselect exists_subselect_init %type @@ -2017,7 +2017,7 @@ simple_expr: $$= new Item_row(*$5); } | EXISTS exists_subselect { $$= $2; } - | singleval_subselect { $$= $1; } + | singlerow_subselect { $$= $1; } | '{' ident expr '}' { $$= $3; } | MATCH ident_list_arg AGAINST '(' expr ')' { Select->add_ftfunc_to_list((Item_func_match *) @@ -4487,17 +4487,17 @@ union_option: /* empty */ {} | ALL {Select->master_unit()->union_option= 1;}; -singleval_subselect: - subselect_start singleval_subselect_init +singlerow_subselect: + subselect_start singlerow_subselect_init subselect_end { $$= $2; }; -singleval_subselect_init: +singlerow_subselect_init: select_init2 { - $$= new Item_singleval_subselect(YYTHD, + $$= new Item_singlerow_subselect(YYTHD, Lex->current_select->master_unit()-> first_select()); };