From 3ce0df59372a3598ae39423e35bab877459693ad Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 Dec 2004 17:13:19 +0200 Subject: [PATCH 1/2] new reference which refer to current value not to result used for resolving outer refernces if subqueri is not in HAVING clause (BUG#7079) and the same used for subquery transformetion mysql-test/r/subselect.result: reference on changable fields from subquery mysql-test/t/subselect.test: reference on changable fields from subquery sql/item.cc: new reference which refer to current value not to result used sql/item.h: new reference which refer to current value not to result used sql/item_subselect.cc: new reference which refer to current value not to result used --- mysql-test/r/subselect.result | 28 +++++++++++++++++++++++++ mysql-test/t/subselect.test | 28 +++++++++++++++++++++++++ sql/item.cc | 34 +++++++++++++++++++++++------- sql/item.h | 39 +++++++++++++++++++++++++++++++++++ sql/item_subselect.cc | 13 ++++++------ 5 files changed, 128 insertions(+), 14 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 956ddb655ee..9c52b25a577 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -2105,3 +2105,31 @@ s1 s1 < all (select s1 from t1) 1 0 NULL NULL drop table t1; +CREATE TABLE t1 ( +Code char(3) NOT NULL default '', +Name char(52) NOT NULL default '', +Continent enum('Asia','Europe','North America','Africa','Oceania','Antarctica','South America') NOT NULL default 'Asia', +Region char(26) NOT NULL default '', +SurfaceArea float(10,2) NOT NULL default '0.00', +IndepYear smallint(6) default NULL, +Population int(11) NOT NULL default '0', +LifeExpectancy float(3,1) default NULL, +GNP float(10,2) default NULL, +GNPOld float(10,2) default NULL, +LocalName char(45) NOT NULL default '', +GovernmentForm char(45) NOT NULL default '', +HeadOfState char(60) default NULL, +Capital int(11) default NULL, +Code2 char(2) NOT NULL default '' +) TYPE=MyISAM; +Warnings: +Warning 1287 'TYPE=storage_engine' is deprecated; use 'ENGINE=storage_engine' instead +INSERT INTO t1 VALUES ('XXX','Xxxxx','Oceania','Xxxxxx',26.00,0,0,0,0,0,'Xxxxx','Xxxxx','Xxxxx',NULL,'XX'); +INSERT INTO t1 VALUES ('ASM','American Samoa','Oceania','Polynesia',199.00,0,68000,75.1,334.00,NULL,'Amerika Samoa','US Territory','George W. Bush',54,'AS'); +INSERT INTO t1 VALUES ('ATF','French Southern territories','Antarctica','Antarctica',7780.00,0,0,NULL,0.00,NULL,'Terres australes françaises','Nonmetropolitan Territory of France','Jacques Chirac',NULL,'TF'); +INSERT INTO t1 VALUES ('UMI','United States Minor Outlying Islands','Oceania','Micronesia/Caribbean',16.00,0,0,NULL,0.00,NULL,'United States Minor Outlying Islands','Dependent Territory of the US','George W. Bush',NULL,'UM'); +/*!40000 ALTER TABLE t1 ENABLE KEYS */; +SELECT DISTINCT Continent AS c FROM t1 WHERE Code <> SOME ( SELECT Code FROM t1 WHERE Continent = c AND Population < 200); +c +Oceania +drop table t1; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index e09aa489983..b5b5de069bf 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -1358,3 +1358,31 @@ insert into t1 values (1),(null); select * from t1 where s1 < all (select s1 from t1); select s1, s1 < all (select s1 from t1) from t1; drop table t1; + +# +# reference on changable fields from subquery +# +CREATE TABLE t1 ( + Code char(3) NOT NULL default '', + Name char(52) NOT NULL default '', + Continent enum('Asia','Europe','North America','Africa','Oceania','Antarctica','South America') NOT NULL default 'Asia', + Region char(26) NOT NULL default '', + SurfaceArea float(10,2) NOT NULL default '0.00', + IndepYear smallint(6) default NULL, + Population int(11) NOT NULL default '0', + LifeExpectancy float(3,1) default NULL, + GNP float(10,2) default NULL, + GNPOld float(10,2) default NULL, + LocalName char(45) NOT NULL default '', + GovernmentForm char(45) NOT NULL default '', + HeadOfState char(60) default NULL, + Capital int(11) default NULL, + Code2 char(2) NOT NULL default '' +) TYPE=MyISAM; +INSERT INTO t1 VALUES ('XXX','Xxxxx','Oceania','Xxxxxx',26.00,0,0,0,0,0,'Xxxxx','Xxxxx','Xxxxx',NULL,'XX'); +INSERT INTO t1 VALUES ('ASM','American Samoa','Oceania','Polynesia',199.00,0,68000,75.1,334.00,NULL,'Amerika Samoa','US Territory','George W. Bush',54,'AS'); +INSERT INTO t1 VALUES ('ATF','French Southern territories','Antarctica','Antarctica',7780.00,0,0,NULL,0.00,NULL,'Terres australes françaises','Nonmetropolitan Territory of France','Jacques Chirac',NULL,'TF'); +INSERT INTO t1 VALUES ('UMI','United States Minor Outlying Islands','Oceania','Micronesia/Caribbean',16.00,0,0,NULL,0.00,NULL,'United States Minor Outlying Islands','Dependent Territory of the US','George W. Bush',NULL,'UM'); +/*!40000 ALTER TABLE t1 ENABLE KEYS */; +SELECT DISTINCT Continent AS c FROM t1 WHERE Code <> SOME ( SELECT Code FROM t1 WHERE Continent = c AND Population < 200); +drop table t1; diff --git a/sql/item.cc b/sql/item.cc index 31c35e87cd4..3fca0033be2 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -69,7 +69,7 @@ Item::Item(): } /* - Constructor used by Item_field, Item_ref & agregate (sum) functions. + Constructor used by Item_field, Item_*_ref & agregate (sum) functions. Used for duplicating lists in processing queries with temporary tables */ @@ -114,7 +114,7 @@ Item_ident::Item_ident(const char *db_name_par,const char *table_name_par, name = (char*) field_name_par; } -// Constructor used by Item_field & Item_ref (see Item comment) +// Constructor used by Item_field & Item_*_ref (see Item comment) Item_ident::Item_ident(THD *thd, Item_ident *item) :Item(thd, item), orig_db_name(item->orig_db_name), @@ -1372,6 +1372,7 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { + enum_parsing_place place; DBUG_ASSERT(fixed == 0); if (!field) // If field is not checked { @@ -1419,8 +1420,7 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) } Item_subselect *prev_subselect_item= prev_unit->item; - enum_parsing_place place= - prev_subselect_item->parsing_place; + place= prev_subselect_item->parsing_place; /* check table fields only if subquery used somewhere out of HAVING or outer SELECT do not use groupping (i.e. tables are @@ -1489,8 +1489,13 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) return -1; } - Item_ref *rf= new Item_ref(last->ref_pointer_array + counter, - (char *)table_name, (char *)field_name); + Item_ref *rf= (place == IN_HAVING ? + new Item_ref(last->ref_pointer_array + counter, + (char *)table_name, + (char *)field_name) : + new Item_direct_ref(last->ref_pointer_array + counter, + (char *)table_name, + (char *)field_name)); if (!rf) return 1; thd->change_item_tree(ref, rf); @@ -2039,6 +2044,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) { DBUG_ASSERT(fixed == 0); uint counter; + enum_parsing_place place; bool not_used; if (!ref) { @@ -2097,8 +2103,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) // it is primary INSERT st_select_lex => skip first table resolving table_list= table_list->next; } - enum_parsing_place place= - prev_subselect_item->parsing_place; + place= prev_subselect_item->parsing_place; /* check table fields only if subquery used somewhere out of HAVING or SELECT list or outer SELECT do not use groupping (i.e. tables @@ -2168,6 +2173,19 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) } mark_as_dependent(thd, last, thd->lex->current_select, this); + if (place == IN_HAVING) + { + Item_ref *rf; + if (!(rf= new Item_direct_ref(last->ref_pointer_array + counter, + (char *)table_name, + (char *)field_name))) + return 1; + ref= 0; // Safety + if (rf->fix_fields(thd, tables, ref) || rf->check_cols(1)) + return 1; + thd->change_item_tree(reference, rf); + return 0; + } ref= last->ref_pointer_array + counter; } else if (!ref) diff --git a/sql/item.h b/sql/item.h index 3c4f80e3857..3ae1da23f15 100644 --- a/sql/item.h +++ b/sql/item.h @@ -889,6 +889,45 @@ public: void print(String *str); }; + +class Item_direct_ref :public Item_ref +{ +public: + Item_direct_ref(Item **item, const char *table_name_par, + const char *field_name_par) + :Item_ref(item, table_name_par, field_name_par) {} + /* Constructor need to process subselect with temporary tables (see Item) */ + Item_direct_ref(THD *thd, Item_direct_ref *item) : Item_ref(thd, item) {} + double val() + { + double tmp=(*ref)->val(); + null_value=(*ref)->null_value; + return tmp; + } + longlong val_int() + { + longlong tmp=(*ref)->val_int(); + null_value=(*ref)->null_value; + return tmp; + } + String *val_str(String* tmp) + { + tmp=(*ref)->val_str(tmp); + null_value=(*ref)->null_value; + return tmp; + } + bool is_null() + { + (void) (*ref)->val_int(); + return (*ref)->null_value; + } + bool get_date(TIME *ltime,uint fuzzydate) + { + return (null_value=(*ref)->get_date(ltime,fuzzydate)); + } +}; + + class Item_in_subselect; class Item_ref_null_helper: public Item_ref { diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 69941b36ca0..ffa3b072801 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -797,9 +797,9 @@ Item_in_subselect::single_value_transformer(JOIN *join, 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 *)in_left_expr_name); + expr= new Item_direct_ref((Item**)optimizer->get_cache(), + (char *)"", + (char *)in_left_expr_name); unit->uncacheable|= UNCACHEABLE_DEPENDENT; } @@ -993,9 +993,10 @@ Item_in_subselect::row_value_transformer(JOIN *join) (char *) "", (char *) ""); func= - eq_creator.create(new Item_ref((*optimizer->get_cache())->addr(i), - (char *)"", - (char *)in_left_expr_name), + eq_creator.create(new Item_direct_ref((*optimizer->get_cache())-> + addr(i), + (char *)"", + (char *)in_left_expr_name), func); item= and_items(item, func); } From a9457c573df0ce1b7505ee735140322badd628c0 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Dec 2004 01:21:14 +0200 Subject: [PATCH 2/2] fixed optimized SOME subquery mysql-test/r/subselect.result: correct results of SOME subquery sql/item_cmpfunc.cc: some comments added fixed optimized SOME subquery --- mysql-test/r/subselect.result | 6 ------ sql/item_cmpfunc.cc | 8 ++++---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 9c52b25a577..fc23331ad7b 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1490,9 +1490,6 @@ Warnings: Note 1003 select test.t3.a AS `a` from test.t3 where ((test.t3.a < (select max(test.t2.b) from test.t2))) select * from t3 where a >= some (select b from t2); a -6 -7 -3 explain extended select * from t3 where a >= some (select b from t2); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t3 ALL NULL NULL NULL NULL 3 Using where @@ -1512,9 +1509,6 @@ Warnings: Note 1003 select test.t3.a AS `a` from test.t3 where ((test.t3.a < (select test.t2.b AS `b` from test.t2 group by test.t2.b))) select * from t3 where a >= some (select b from t2 group by 1); a -6 -7 -3 explain extended select * from t3 where a >= some (select b from t2 group by 1); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t3 ALL NULL NULL NULL NULL 3 Using where diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 51212418b09..88083878053 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -120,7 +120,7 @@ longlong Item_func_not_all::val_int() /* return TRUE if there was records in underlaying select in max/min - optimisation + optimisation (ALL subquery) */ if (empty_underlying_subquery()) return 1; @@ -157,11 +157,11 @@ longlong Item_func_nop_all::val_int() double value= args[0]->val(); /* - return TRUE if there was records in underlaying select in max/min - optimisation + return FALSE if there was records in underlaying select in max/min + optimisation (SAME/ANY subquery) */ if (empty_underlying_subquery()) - return 1; + return 0; null_value= args[0]->null_value; return (null_value || value == 0) ? 0 : 1;