diff --git a/include/mysqld_error.h b/include/mysqld_error.h index c910078331e..4c08bc77e8f 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -243,4 +243,6 @@ #define ER_MIXING_NOT_ALLOWED 1224 #define ER_DUP_ARGUMENT 1225 #define ER_USER_LIMIT_REACHED 1226 -#define ER_ERROR_MESSAGES 227 +#define ER_SUBSELECT_NO_1_COL 1227 +#define ER_SUBSELECT_NO_1_ROW 1228 +#define ER_ERROR_MESSAGES 229 diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index c98e1c7e973..ee35011db3a 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -43,7 +43,8 @@ sqlsources = convert.cc derror.cc field.cc field_conv.cc filesort.cc \ hostname.cc init.cc \ item.cc item_buff.cc item_cmpfunc.cc item_create.cc \ item_func.cc item_strfunc.cc item_sum.cc item_timefunc.cc \ - item_uniq.cc key.cc lock.cc log.cc log_event.cc mf_iocache.cc\ + item_uniq.cc item_subselect.cc \ + key.cc lock.cc log.cc log_event.cc mf_iocache.cc\ mini_client.cc net_pkg.cc net_serv.cc opt_ft.cc opt_range.cc \ opt_sum.cc procedure.cc records.cc sql_acl.cc \ repl_failsafe.cc slave.cc \ diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result new file mode 100644 index 00000000000..ba705504970 --- /dev/null +++ b/mysql-test/r/subselect.result @@ -0,0 +1,31 @@ +drop table if exists t1,t2,t3,t4; +create table t1 (a int); +create table t2 (a int, b int); +create table t3 (a int); +create table t4 (a int, b int); +insert into t1 values (2); +insert into t2 values (1,7),(2,7); +insert into t4 values (4,8),(3,8),(5,9); +select (select a from t1), a from t2; +(select a from t1) a +2 1 +2 2 +select (select a from t3), a from t2; +(select a from t3) a +NULL 1 +NULL 2 +select * from t2 where t2.a=(select a from t1); +a b +2 7 +insert into t3 values (6),(7),(3); +select * from t2 where t2.b=(select a from t3 order by 1 limit 1); +a b +1 7 +2 7 +select * from t2 where t2.b=(select a from t3 order by 1 limit 1) +union (select * from t4 order by a limit 2) limit 3; +a b +1 7 +2 7 +3 8 +drop table t1,t2,t3,t4; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test new file mode 100644 index 00000000000..3bd92ef41de --- /dev/null +++ b/mysql-test/t/subselect.test @@ -0,0 +1,18 @@ + +#select (select 2); +drop table if exists t1,t2,t3,t4; +create table t1 (a int); +create table t2 (a int, b int); +create table t3 (a int); +create table t4 (a int, b int); +insert into t1 values (2); +insert into t2 values (1,7),(2,7); +insert into t4 values (4,8),(3,8),(5,9); +select (select a from t1), a from t2; +select (select a from t3), a from t2; +select * from t2 where t2.a=(select a from t1); +insert into t3 values (6),(7),(3); +select * from t2 where t2.b=(select a from t3 order by 1 limit 1); +select * from t2 where t2.b=(select a from t3 order by 1 limit 1) +union (select * from t4 order by a limit 2) limit 3; +drop table t1,t2,t3,t4; diff --git a/sql/Makefile.am b/sql/Makefile.am index f58075358b6..bd626ea10b7 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -46,7 +46,7 @@ mysqld_LDADD = @MYSQLD_EXTRA_LDFLAGS@ \ $(LDADD) $(CXXLDFLAGS) $(WRAPLIBS) @LIBDL@ @openssl_libs@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ item_strfunc.h item_timefunc.h item_uniq.h \ - item_create.h mysql_priv.h \ + item_create.h item_subselect.h mysql_priv.h \ procedure.h sql_class.h sql_lex.h sql_list.h \ sql_manager.h sql_map.h sql_string.h unireg.h \ field.h handler.h \ @@ -60,7 +60,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ mysqld_SOURCES = sql_lex.cc sql_handler.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \ - thr_malloc.cc item_create.cc \ + thr_malloc.cc item_create.cc item_subselect.cc\ field.cc key.cc sql_class.cc sql_list.cc \ net_serv.cc net_pkg.cc lock.cc my_lock.c \ sql_string.cc sql_manager.cc sql_map.cc \ diff --git a/sql/item.h b/sql/item.h index 97f2862bb8b..2bd1cb99bf5 100644 --- a/sql/item.h +++ b/sql/item.h @@ -32,7 +32,8 @@ public: 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, - PROC_ITEM,COND_ITEM,REF_ITEM,FIELD_STD_ITEM, CONST_ITEM}; + PROC_ITEM,COND_ITEM,REF_ITEM,FIELD_STD_ITEM, CONST_ITEM, + SUBSELECT_ITEM}; enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE }; String str_value; /* used to store value */ @@ -46,7 +47,6 @@ public: my_bool unsigned_flag; my_bool with_sum_func; - // alloc & destruct is done as start of select using sql_alloc Item(); virtual ~Item() { name=0; } /*lint -e1509 */ @@ -371,6 +371,7 @@ public: #include "item_strfunc.h" #include "item_timefunc.h" #include "item_uniq.h" +#include "item_subselect.h" class Item_copy_string :public Item { @@ -458,3 +459,4 @@ extern Item_result item_cmp_type(Item_result a,Item_result b); extern Item *resolve_const_item(Item *item,Item *cmp_item); extern bool field_is_equal_to_item(Field *field,Item *item); Item *get_system_var(LEX_STRING name); + diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 091289fd040..350c4b3d793 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -38,7 +38,9 @@ public: Field *tmp_table_field(TABLE *t_arg) { if (!t_arg) return result_field; - return (max_length > 255) ? (Field *)new Field_blob(max_length,maybe_null, name,t_arg, binary) : (Field *) new Field_string(max_length,maybe_null, name,t_arg, binary); + return (max_length > 255) ? + (Field *) new Field_blob(max_length,maybe_null, name,t_arg, binary) : + (Field *) new Field_string(max_length,maybe_null, name,t_arg, binary); } }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc new file mode 100644 index 00000000000..286c29fec7a --- /dev/null +++ b/sql/item_subselect.cc @@ -0,0 +1,136 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + subselect Item + +SUBSELECT TODO: + - add function from mysql_select that use JOIN* as parameter to JOIN methods + (sql_select.h/sql_select.cc) + - remove double 'having' & 'having_list' from JOIN + (sql_select.h/sql_select.cc) + + - add subselect union select (sql_union.cc) + - depended from outer select subselects + +*/ + +#ifdef __GNUC__ +#pragma implementation // gcc: Class implementation +#endif + +#include "mysql_priv.h" +#include "sql_select.h" + +Item_subselect::Item_subselect(THD *thd, st_select_lex *select_lex): + executed(0) +{ + DBUG_ENTER("Item_subselect::Item_subselect"); + DBUG_PRINT("subs", ("select_lex 0x%xl", (long) select_lex)); + result= new select_subselect(this); + join= new JOIN(thd, select_lex->item_list, select_lex->options, result); + this->select_lex= select_lex; + maybe_null= 1; + /* + item value is NULL if select_subselect not changed this value + (i.e. some rows will be found returned) + */ + assign_null(); + DBUG_VOID_RETURN; +} + +Item::Type Item_subselect::type() const +{ + return SUBSELECT_ITEM; +} + +double Item_subselect::val () +{ + if (exec()) + return 0; + return real_value; +} + +longlong Item_subselect::val_int () +{ + if (exec()) + return 0; + return int_value; +} + +String *Item_subselect::val_str (String *str) +{ + if (exec() || null_value) + return 0; + return &str_value; +} + +void Item_subselect::make_field (Send_field *tmp_field) +{ + if (null_value) + { + init_make_field(tmp_field,FIELD_TYPE_NULL); + tmp_field->length=4; + } else { + init_make_field(tmp_field, ((result_type() == STRING_RESULT) ? + FIELD_TYPE_VAR_STRING : + (result_type() == INT_RESULT) ? + FIELD_TYPE_LONGLONG : FIELD_TYPE_DOUBLE)); + } +} + +bool Item_subselect::fix_fields(THD *thd,TABLE_LIST *tables) +{ + // Is it one field subselect? + if (select_lex->item_list.elements != 1) + { + my_printf_error(ER_SUBSELECT_NO_1_COL, ER(ER_SUBSELECT_NO_1_COL), MYF(0)); + return 1; + } + SELECT_LEX *save_select= thd->lex.select; + thd->lex.select= select_lex; + if(join->prepare((TABLE_LIST*) select_lex->table_list.first, + select_lex->where, + (ORDER*) select_lex->order_list.first, + (ORDER*) select_lex->group_list.first, + select_lex->having, + (ORDER*) 0, select_lex, + (SELECT_LEX_UNIT*) select_lex->master)) + return 1; + if (join->optimize()) + { + executed= 1; + return 1; + } + thd->lex.select= save_select; + return 0; +} + +int Item_subselect::exec() +{ + if (!executed) + { + SELECT_LEX *save_select= join->thd->lex.select; + join->thd->lex.select= select_lex; + join->exec(); + join->thd->lex.select= save_select; + if (!executed) + //No rows returned => value is null (returned as inited) + executed= 1; + return join->error; + } + return 0; +} diff --git a/sql/item_subselect.h b/sql/item_subselect.h new file mode 100644 index 00000000000..096da68600c --- /dev/null +++ b/sql/item_subselect.h @@ -0,0 +1,79 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* subselect Item */ + +#ifdef __GNUC__ +#pragma interface /* gcc class implementation */ +#endif + +struct st_select_lex; +class JOIN; +class select_subselect; + +/* simple (not depended of covered select ) subselect */ + +class Item_subselect :public Item +{ +protected: + my_bool executed; /* simple subselect is executed */ + longlong int_value; + double real_value; + enum Item_result res_type; + + int exec(); + void assign_null() + { + null_value= 1; + int_value= 0; + real_value= 0; + max_length= 4; + res_type= STRING_RESULT; + } +public: + st_select_lex *select_lex; + JOIN *join; + select_subselect *result; + + Item_subselect(THD *thd, st_select_lex *select_lex); + Item_subselect(Item_subselect *item) + { + null_value= item->null_value; + int_value= item->int_value; + real_value= item->real_value; + max_length= item->max_length; + decimals= item->decimals; + res_type= item->res_type; + executed= item->executed; + select_lex= item->select_lex; + join= item->join; + result= item->result; + name= item->name; + } + enum Type type() const; + double val (); + longlong val_int (); + String *val_str (String *); + bool is_null() { return null_value; } + void make_field (Send_field *); + bool fix_fields(THD *thd, TABLE_LIST *tables); + Item *new_item() { return new Item_subselect(this); } + enum Item_result result_type() const { return res_type; } + + friend class select_subselect; +}; + + diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 950ca4f6623..f654f2a2240 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -237,3 +237,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index d87ed4ee629..cb9912d5783 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -231,3 +231,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index c8b47cb3c19..be4cd7d7896 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -236,3 +236,5 @@ "Het combineren van transactionele en niet-transactionele tabellen is uitgeschakeld.", "Optie '%s' tweemaal gebruikt in opdracht", "Gebruiker '%-64s' heeft het maximale gebruik van de '%s' faciliteit overschreden (huidige waarde: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 5033449c266..533a305cd1d 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -228,3 +228,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index 6a83468eae5..d303cf22102 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -233,3 +233,5 @@ "Transaktsioone toetavate ning mittetoetavate tabelite kooskasutamine ei ole lubatud", "Määrangut '%s' on lauses kasutatud topelt", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index cf3e3e845e4..cae31a7c799 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -228,3 +228,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 19d46fabab8..8f3b59da035 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -231,3 +231,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index f9b4f137f82..f6c92f7c27c 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -228,3 +228,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index 38877371243..1dd72efc63a 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -230,3 +230,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index e8cfd5a63a9..e658bc2975e 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -228,3 +228,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index 98bc099954f..55fe7d79768 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -230,3 +230,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index f6cc890cb39..38d9416edc9 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -228,3 +228,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index adffc27949f..c84e8242778 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -230,3 +230,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index 09a1ea4684c..dae9cf927c5 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -230,3 +230,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 12a9bd358b5..312ae153cbe 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -232,3 +232,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index b7feb0a7b0d..6dca23872e2 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -228,3 +228,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 8e48cabfc39..6e89a0119e2 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -232,3 +232,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index 8ed33ec21a0..5df743b0dbf 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -231,3 +231,5 @@ "ïÄÎÏ×ÒÅÍÅÎÎÏÅ ÉÓÐÏÌØÚÏ×ÁÎÉÅ transactional É non-transactional ÔÁÂÌÉà ÏÔËÌÀÞÅÎÏ", "ïÐÃÉÑ '%s' ÉÓÐÏÌØÚÏ×ÁÎÁ Ä×ÁÖÄÙ", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"ðÏÄÚÁÐÒÏÓ ×ÏÚ×ÒÁÝÁÅÔ ÂÏÌÅÅ ÏÄÎÏÇÏ ÐÏÌÑ", +"ðÏÄÚÁÐÒÏÓ ×ÏÚ×ÒÁÝÁÅÔ ÂÏÌÅÅ ÏÄÎÏÊ ÚÁÐÉÓÉ", diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index 06503cdf69e..37a2d30e1ae 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -236,3 +236,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index 4240581c5b8..2e371e75cbd 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -229,3 +229,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index e774f4a2c5c..58c8e5af54f 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -228,3 +228,5 @@ "Blandning av transaktionella och icke-transaktionella tabeller är inaktiverat", "Option '%s' användes två gånger", "Användare '%-64s' har överskridit '%s' (nuvarande värde: %ld)", +"Subselect return more than 1 field", +"Subselect return more than 1 record", diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index c4c89433331..4dad29345e5 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -233,3 +233,5 @@ "Mixing of transactional and non-transactional tables is disabled", "Option '%s' used twice in statement", "User '%-64s' has exceeded the '%s' resource (current value: %ld)", +"ðiÄÚÁÐÉÔ ÐÏ×ÅÒÔÁ¤ ÂiÌØÛ ÎiÖ 1 ÓÔÏ×ÂÅÃØ", +"ðiÄÚÁÐÉÔ ÐÏ×ÅÒÔÁ¤ ÂiÌØÛ ÎiÖ 1 ÚÁÐÉÓ", diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 6be0e46679b..95c10112a9b 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -774,3 +774,30 @@ bool select_dump::send_eof() file= -1; return error; } + +select_subselect::select_subselect(Item_subselect *item) +{ + this->item=item; +} + +bool select_subselect::send_data(List &items) +{ + if (item->executed){ + my_printf_error(ER_SUBSELECT_NO_1_ROW, ER(ER_SUBSELECT_NO_1_ROW), MYF(0)); + return 1; + } + Item *val_item= (Item *)item->select_lex->item_list.head(); + if ((item->null_value= val_item->is_null())) + { + item->assign_null(); + } else { + item->max_length= val_item->max_length; + item->decimals= val_item->decimals; + item->binary= val_item->binary; + val_item->val_str(&item->str_value); + item->int_value= val_item->val_int(); + item->real_value= val_item->val(); + item->res_type= val_item->result_type(); + } + return 0; +} diff --git a/sql/sql_class.h b/sql/sql_class.h index d4bd65674f1..10d6bf84b22 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -685,6 +685,19 @@ class select_union :public select_result { bool flush(); }; +/* Single value subselect interface class */ +class select_subselect :public select_result +{ + Item_subselect *item; +public: + select_subselect(Item_subselect *item); + bool send_fields(List &list, uint flag) { return 0; }; + bool send_data(List &items); + bool send_eof() { return 0; }; + + friend class Ttem_subselect; +}; + /* Structs used when sorting */ typedef struct st_sort_field { diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index dfa60918665..134e776a15a 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -901,6 +901,7 @@ void st_select_lex_node::init_select() void st_select_lex_unit::init_query() { + linkage= GLOBAL_OPTIONS_TYPE; st_select_lex_node::init_query(); global_parameters= this; select_limit_cnt= HA_POS_ERROR; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d8d9c854652..c5e5e971e33 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -156,6 +156,19 @@ int handle_select(THD *thd, LEX *lex, select_result *result) { int res; register SELECT_LEX *select_lex = &lex->select_lex; + if (select_lex->link_next) + { + /* Fix tables 'to-be-unioned-from' list to point at opened tables */ + for (SELECT_LEX *sl= select_lex; + sl; + sl= (SELECT_LEX*)sl->link_next) + { + for (TABLE_LIST *cursor= (TABLE_LIST *)sl->table_list.first; + cursor; + cursor=cursor->next) + cursor->table= cursor->table_list->table; + } + } if (select_lex->next) res=mysql_union(thd,lex,result); else @@ -180,50 +193,42 @@ int handle_select(THD *thd, LEX *lex, select_result *result) ** mysql_select assumes that all tables are already opened *****************************************************************************/ +/* + Prepare of whole select (including subselect in future). + return -1 on error + 0 on success +*/ int -mysql_select(THD *thd,TABLE_LIST *tables,List &fields,COND *conds, - ORDER *order, ORDER *group,Item *having,ORDER *proc_param, - ulong select_options,select_result *result, - SELECT_LEX_UNIT *unit) +JOIN::prepare(TABLE_LIST *tables_init, + COND *conds_init, ORDER *order_init, ORDER *group_init, + Item *having_init, + ORDER *proc_param_init, SELECT_LEX *select, SELECT_LEX_UNIT *unit) { - TABLE *tmp_table; - int error, tmp_error; - bool need_tmp,hidden_group_fields; - bool simple_order,simple_group,no_order, skip_sort_order, buffer_result; - Item::cond_result cond_value; - SQL_SELECT *select; - DYNAMIC_ARRAY keyuse; - JOIN join; - Procedure *procedure; - List all_fields(fields); - bool select_distinct; - SELECT_LEX *select_lex= &(thd->lex.select_lex); - SELECT_LEX *cur_sel= thd->lex.select; - DBUG_ENTER("mysql_select"); + DBUG_ENTER("JOIN::prepare"); + + conds= conds_init; + order= order_init; + group_list= group_init; + having= having_init; + proc_param= proc_param_init; + tables_list= tables_init; + select_lex= select; /* Check that all tables, fields, conds and order are ok */ - select_distinct=test(select_options & SELECT_DISTINCT); - buffer_result=test(select_options & OPTION_BUFFER_RESULT) && !test(select_options & OPTION_FOUND_ROWS); - tmp_table=0; - select=0; - no_order=skip_sort_order=0; - bzero((char*) &keyuse,sizeof(keyuse)); - thd->proc_info="init"; - thd->used_tables=0; // Updated by setup_fields - - if (setup_tables(tables) || - setup_fields(thd,tables,fields,1,&all_fields,1) || - setup_conds(thd,tables,&conds) || - setup_order(thd,tables,fields,all_fields,order) || - setup_group(thd,tables,fields,all_fields,group,&hidden_group_fields)) + if (setup_tables(tables_list) || + setup_fields(thd,tables_list,fields_list,1,&all_fields,1) || + setup_conds(thd,tables_list,&conds) || + setup_order(thd,tables_list,fields_list,all_fields,order) || + setup_group(thd,tables_list,fields_list,all_fields,group_list, + &hidden_group_fields)) DBUG_RETURN(-1); /* purecov: inspected */ if (having) { thd->where="having clause"; thd->allow_sum_func=1; - if (having->fix_fields(thd,tables) || thd->fatal_error) + if (having->fix_fields(thd,tables_list) || thd->fatal_error) DBUG_RETURN(-1); /* purecov: inspected */ if (having->with_sum_func) having->split_sum_func(all_fields); @@ -236,13 +241,11 @@ mysql_select(THD *thd,TABLE_LIST *tables,List &fields,COND *conds, TODO: Add check of calculation of GROUP functions and fields: SELECT COUNT(*)+table.col1 from table1; */ - join.table=0; - join.tables=0; { - if (!group) + if (!group_list) { uint flag=0; - List_iterator_fast it(fields); + List_iterator_fast it(fields_list); Item *item; while ((item= it++)) { @@ -258,22 +261,23 @@ mysql_select(THD *thd,TABLE_LIST *tables,List &fields,COND *conds, } } TABLE_LIST *table; - for (table=tables ; table ; table=table->next) - join.tables++; + for (table=tables_list ; table ; table=table->next) + tables++; } - procedure=setup_procedure(thd,proc_param,result,fields,&error); + procedure=setup_procedure(thd,proc_param,result,fields_list,&error); if (error) DBUG_RETURN(-1); /* purecov: inspected */ if (procedure) { - if (setup_new_fields(thd,tables,fields,all_fields,procedure->param_fields)) + if (setup_new_fields(thd, tables_list, fields_list, all_fields, + procedure->param_fields)) { /* purecov: inspected */ delete procedure; /* purecov: inspected */ DBUG_RETURN(-1); /* purecov: inspected */ } if (procedure->group) { - if (!test_if_subpart(procedure->group,group)) + if (!test_if_subpart(procedure->group,group_list)) { /* purecov: inspected */ my_message(0,"Can't handle procedures with differents groups yet", MYF(0)); /* purecov: inspected */ @@ -282,7 +286,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List &fields,COND *conds, } } #ifdef NOT_NEEDED - else if (!group && procedure->flags & PROC_GROUP) + else if (!group_list && procedure->flags & PROC_GROUP) { my_message(0,"Select must have a group with this procedure",MYF(0)); delete procedure; @@ -298,52 +302,54 @@ mysql_select(THD *thd,TABLE_LIST *tables,List &fields,COND *conds, } /* Init join struct */ - join.thd=thd; - join.lock=thd->lock; - join.join_tab=0; - join.tmp_table_param.copy_field=0; - join.sum_funcs=0; - join.send_records=join.found_records=join.examined_rows=0; - join.tmp_table_param.end_write_records= HA_POS_ERROR; - join.first_record=join.sort_and_group=0; - join.select_options=select_options; - join.result=result; - count_field_types(&join.tmp_table_param,all_fields,0); - join.const_tables=0; - join.having=0; - join.do_send_rows = 1; - join.group= group != 0; - join.row_limit= ((select_distinct || order || group) ? HA_POS_ERROR : - unit->select_limit_cnt); - join.unit= unit; + count_field_types(&tmp_table_param, all_fields, 0); + this->group= group_list != 0; + row_limit= ((select_distinct || order || group_list) ? HA_POS_ERROR : + unit->select_limit_cnt); + this->unit= unit; #ifdef RESTRICTED_GROUP - if (join.sum_func_count && !group && (join.func_count || join.field_count)) + if (sum_func_count && !group_list && (func_count || field_count)) { my_message(ER_WRONG_SUM_SELECT,ER(ER_WRONG_SUM_SELECT),MYF(0)); delete procedure; DBUG_RETURN(-1); } #endif - if (!procedure && result->prepare(fields, unit)) + if (!procedure && result->prepare(fields_list, unit)) { /* purecov: inspected */ DBUG_RETURN(-1); /* purecov: inspected */ } + DBUG_RETURN(0); // All OK +} + +/* + global select optimisation. + return 0 - success + 1 - go out + -1 - go out with cleaning + error code saved in field 'error' +*/ +int +JOIN::optimize() +{ + DBUG_ENTER("JOIN::optimize"); + SELECT_LEX *select_lex = &(thd->lex.select_lex); #ifdef HAVE_REF_TO_FIELDS // Not done yet /* Add HAVING to WHERE if possible */ - if (having && !group && ! join.sum_func_count) + if (having && !group_list && ! sum_func_count) { if (!conds) { - conds=having; - having=0; + conds= having; + having= 0; } else if ((conds=new Item_cond_and(conds,having))) { - conds->fix_fields(thd,tables); - conds->change_ref_to_fields(thd,tables); - having=0; + conds->fix_fields(thd, tables_list); + conds->change_ref_to_fields(thd, tables_list); + having= 0; } } #endif @@ -352,110 +358,76 @@ mysql_select(THD *thd,TABLE_LIST *tables,List &fields,COND *conds, if (thd->fatal_error) // Out of memory { delete procedure; - DBUG_RETURN(0); + error = 0; + DBUG_RETURN(1); } if (cond_value == Item::COND_FALSE || !unit->select_limit_cnt) { /* Impossible cond */ - if (select_options & SELECT_DESCRIBE && select_lex->next) - select_describe(&join,false,false,false,"Impossible WHERE"); - else - error=return_zero_rows(result, tables, fields, - join.tmp_table_param.sum_func_count != 0 && !group, - select_options,"Impossible WHERE",having, - procedure, unit); - delete procedure; - DBUG_RETURN(error); + zero_result_cause= "Impossible WHERE"; + DBUG_RETURN(0); } /* Optimize count(*), min() and max() */ - if (tables && join.tmp_table_param.sum_func_count && ! group) + if (tables_list && tmp_table_param.sum_func_count && ! group_list) { int res; - if ((res=opt_sum_query(tables, all_fields, conds))) + if ((res=opt_sum_query(tables_list, all_fields, conds))) { if (res < 0) { - if (select_options & SELECT_DESCRIBE && select_lex->next) - select_describe(&join,false,false,false,"No matching min/max row"); - else - error=return_zero_rows(result, tables, fields, !group, - select_options, "No matching min/max row", - having, procedure, unit); - delete procedure; - DBUG_RETURN(error); + zero_result_cause= "No matching min/max row"; + DBUG_RETURN(0); } if (select_options & SELECT_DESCRIBE) { if (select_lex->next) - select_describe(&join,false,false,false,"Select tables optimized away"); + select_describe(this, false, false, false, + "Select tables optimized away"); else - describe_info(thd,"Select tables optimized away"); + describe_info(thd, "Select tables optimized away"); delete procedure; - DBUG_RETURN(error); + DBUG_RETURN(1); } - tables=0; // All tables resolved + tables_list= 0; // All tables resolved } } - if (!tables) - { // Only test of functions - error=0; - if (select_options & SELECT_DESCRIBE) - { - if (select_lex->next) - select_describe(&join,false,false,false,"No tables used"); - else - describe_info(thd,"No tables used"); - } - else - { - result->send_fields(fields,1); - if (!having || having->val_int()) - { - if (join.do_send_rows && result->send_data(fields)) - { - result->send_error(0,NullS); /* purecov: inspected */ - error=1; - } - else - error=(int) result->send_eof(); - } - else - error=(int) result->send_eof(); - } - delete procedure; - DBUG_RETURN(error); - } - error = -1; - join.sort_by_table=get_sort_by_table(order,group,tables); + if (!tables_list) + { + test_function_query= 1; + DBUG_RETURN(0); + } + + error= -1; + sort_by_table= get_sort_by_table(order, group_list, tables_list); /* Calculate how to do the join */ - thd->proc_info="statistics"; - if (make_join_statistics(&join,tables,conds,&keyuse) || thd->fatal_error) - goto err; - thd->proc_info="preparing"; - result->initialize_tables(&join); - if (join.const_table_map != join.found_const_table_map && + thd->proc_info= "statistics"; + if (make_join_statistics(this, tables_list, conds, &keyuse) || + thd->fatal_error) + DBUG_RETURN(-1); + thd->proc_info= "preparing"; + result->initialize_tables(this); + if (const_table_map != found_const_table_map && !(select_options & SELECT_DESCRIBE)) { - error= return_zero_rows(result, tables, fields, - join.tmp_table_param.sum_func_count != 0 && - !group, 0, "", having, procedure, unit); - goto err; + zero_result_cause= ""; + select_options= 0; //TODO why option in return_zero_rows was droped + DBUG_RETURN(0); } if (!(thd->options & OPTION_BIG_SELECTS) && - join.best_read > (double) thd->max_join_size && + best_read > (double) thd->max_join_size && !(select_options & SELECT_DESCRIBE)) { /* purecov: inspected */ result->send_error(ER_TOO_BIG_SELECT,ER(ER_TOO_BIG_SELECT)); /* purecov: inspected */ error= 1; /* purecov: inspected */ - goto err; /* purecov: inspected */ + DBUG_RETURN(-1); } - if (join.const_tables && !thd->locked_tables && + if (const_tables && !thd->locked_tables && !(select_options & SELECT_NO_UNLOCK)) { TABLE **table, **end; - for (table=join.table, end=table + join.const_tables ; + for (table=this->table, end=table + const_tables ; table != end; table++) { @@ -467,98 +439,94 @@ mysql_select(THD *thd,TABLE_LIST *tables,List &fields,COND *conds, } (*table)->file->index_end(); } - mysql_unlock_some_tables(thd, join.table,join.const_tables); + mysql_unlock_some_tables(thd, this->table, const_tables); } - if (!conds && join.outer_join) + if (!conds && outer_join) { /* Handle the case where we have an OUTER JOIN without a WHERE */ conds=new Item_int((longlong) 1,1); // Always true } - select=make_select(*join.table, join.const_table_map, - join.const_table_map,conds,&error); + select=make_select(*table, const_table_map, + const_table_map, conds, &error); if (error) { /* purecov: inspected */ error= -1; /* purecov: inspected */ - goto err; /* purecov: inspected */ + DBUG_RETURN(-1); } - if (make_join_select(&join,select,conds)) + if (make_join_select(this, select, conds)) { - if (select_options & SELECT_DESCRIBE && select_lex->next) - select_describe(&join,false,false,false,"Impossible WHERE noticed after reading const tables"); - else - error= return_zero_rows(result,tables,fields, - join.tmp_table_param.sum_func_count != 0 && - !group, - select_options, - "Impossible WHERE noticed after reading const tables", - having, procedure, unit); - goto err; + zero_result_cause= + "Impossible WHERE noticed after reading const tables"; + DBUG_RETURN(0); } error= -1; /* if goto err */ /* Optimize distinct away if possible */ - order=remove_const(&join,order,conds,&simple_order); - if (group || join.tmp_table_param.sum_func_count) + order= remove_const(this, order, conds, &simple_order); + if (group_list || tmp_table_param.sum_func_count) { if (! hidden_group_fields) select_distinct=0; } - else if (select_distinct && join.tables - join.const_tables == 1 && + else if (select_distinct && tables - const_tables == 1 && (unit->select_limit_cnt == HA_POS_ERROR || - (join.select_options & OPTION_FOUND_ROWS) || + (select_options & OPTION_FOUND_ROWS) || order && !(skip_sort_order= - test_if_skip_sort_order(&join.join_tab[join.const_tables], - order, unit->select_limit_cnt,1)))) + test_if_skip_sort_order(&join_tab[const_tables], + order, + unit->select_limit_cnt, + 1)))) { - if ((group=create_distinct_group(order,fields))) + if ((group_list= create_distinct_group(order, fields_list))) { - select_distinct=0; + select_distinct= 0; no_order= !order; - join.group=1; // For end_write_group + group= 1; // For end_write_group } else if (thd->fatal_error) // End of memory - goto err; + DBUG_RETURN(-1); } - group=remove_const(&join,group,conds,&simple_group); - if (!group && join.group) + group_list= remove_const(this, group_list, conds, &simple_group); + if (!group_list && group) { order=0; // The output has only one row simple_order=1; } - calc_group_buffer(&join,group); - join.send_group_parts=join.tmp_table_param.group_parts; /* Save org parts */ + calc_group_buffer(this, group_list); + send_group_parts= tmp_table_param.group_parts; /* Save org parts */ if (procedure && procedure->group) { - group=procedure->group=remove_const(&join,procedure->group,conds, - &simple_group); - calc_group_buffer(&join,group); + group_list= procedure->group= remove_const(this, procedure->group, conds, + &simple_group); + calc_group_buffer(this, group_list); } - if (test_if_subpart(group,order) || - (!group && join.tmp_table_param.sum_func_count)) + if (test_if_subpart(group_list, order) || + (!group_list && tmp_table_param.sum_func_count)) order=0; // Can't use sort on head table if using row cache - if (join.full_join) + if (full_join) { - if (group) + if (group_list) simple_group=0; if (order) simple_order=0; } - need_tmp= (join.const_tables != join.tables && + need_tmp= (const_tables != tables && ((select_distinct || !simple_order || !simple_group) || - (group && order) || buffer_result)); + (group_list && order) || buffer_result)); // No cache for MATCH - make_join_readinfo(&join, + make_join_readinfo(this, (select_options & (SELECT_DESCRIBE | SELECT_NO_JOIN_CACHE)) | - (cur_sel->ftfunc_list.elements ? SELECT_NO_JOIN_CACHE : 0)); + (thd->lex.select->ftfunc_list.elements ? + SELECT_NO_JOIN_CACHE : 0)); /* Need to tell Innobase that to play it safe, it should fetch all columns of the tables: this is because MySQL @@ -567,60 +535,132 @@ mysql_select(THD *thd,TABLE_LIST *tables,List &fields,COND *conds, by MySQL. */ #ifdef HAVE_INNOBASE_DB - if (need_tmp || select_distinct || group || order) + if (need_tmp || select_distinct || group_list || order) { - for (uint i_h = join.const_tables; i_h < join.tables; i_h++) + for (uint i_h = const_tables; i_h < tables; i_h++) { - TABLE* table_h = join.join_tab[i_h].table; + TABLE* table_h = join_tab[i_h].table; if (table_h->db_type == DB_TYPE_INNODB) table_h->file->extra(HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE); } } #endif - DBUG_EXECUTE("info",TEST_join(&join);); + DBUG_EXECUTE("info",TEST_join(this);); /* Because filesort always does a full table scan or a quick range scan we must add the removed reference to the select for the table. We only need to do this when we have a simple_order or simple_group as in other cases the join is done before the sort. */ - if ((order || group) && join.join_tab[join.const_tables].type != JT_ALL && - join.join_tab[join.const_tables].type != JT_FT && - (order && simple_order || group && simple_group)) + if ((order || group_list) && join_tab[const_tables].type != JT_ALL && + join_tab[const_tables].type != JT_FT && + (order && simple_order || group_list && simple_group)) { - if (add_ref_to_table_cond(thd,&join.join_tab[join.const_tables])) - goto err; + if (add_ref_to_table_cond(thd,&join_tab[const_tables])) + DBUG_RETURN(-1); } if (!(select_options & SELECT_BIG_RESULT) && - ((group && join.const_tables != join.tables && + ((group_list && const_tables != tables && (!simple_group || - !test_if_skip_sort_order(&join.join_tab[join.const_tables], group, - unit->select_limit_cnt, 0))) || + !test_if_skip_sort_order(&join_tab[const_tables], group_list, + unit->select_limit_cnt, + 0))) || select_distinct) && - join.tmp_table_param.quick_group && !procedure) + tmp_table_param.quick_group && !procedure) { need_tmp=1; simple_order=simple_group=0; // Force tmp table without sort } + DBUG_RETURN(0); +} + +/* + global uptimisation (with subselect) must be here (TODO) +*/ + +int +JOIN::global_optimize() +{ + return 0; +} + +/* + exec select +*/ +void +JOIN::exec() +{ + int tmp_error; + + DBUG_ENTER("JOIN::exec"); + + if (test_function_query) + { // Only test of functions + error=0; + if (select_options & SELECT_DESCRIBE) + { + if (select_lex->next) + select_describe(this, false, false, false, "No tables used"); + else + describe_info(thd, "No tables used"); + } + else + { + result->send_fields(fields_list,1); + if (!having || having->val_int()) + { + if (do_send_rows && result->send_data(fields_list)) + { + result->send_error(0,NullS); /* purecov: inspected */ + error=1; + } + else + error=(int) result->send_eof(); + } + else + error=(int) result->send_eof(); + } + delete procedure; + DBUG_VOID_RETURN; + } + + if (zero_result_cause) + { + if (select_options & SELECT_DESCRIBE && select_lex->next) + select_describe(this, false, false, false, zero_result_cause); + else + error=return_zero_rows(result, tables_list, fields_list, + tmp_table_param.sum_func_count != 0 && + !group_list, + select_options, + zero_result_cause, + having,procedure, + unit); + DBUG_VOID_RETURN; + } + + Item *having_list = having; + having = 0; if (select_options & SELECT_DESCRIBE) { if (!order && !no_order) - order=group; + order=group_list; if (order && - (join.const_tables == join.tables || + (const_tables == tables || (simple_order && - test_if_skip_sort_order(&join.join_tab[join.const_tables], order, - (join.const_tables != join.tables - 1 || - (join.select_options & OPTION_FOUND_ROWS)) ? - HA_POS_ERROR : unit->select_limit_cnt, 0)))) + test_if_skip_sort_order(&join_tab[const_tables], order, + (const_tables != tables - 1 || + (select_options & OPTION_FOUND_ROWS)) ? + HA_POS_ERROR : unit->select_limit_cnt, + 0)))) order=0; - select_describe(&join,need_tmp, + select_describe(this, need_tmp, order != 0 && !skip_sort_order, select_distinct); error=0; - goto err; + DBUG_VOID_RETURN; } /* Perform FULLTEXT search before all regular searches */ @@ -632,44 +672,45 @@ mysql_select(THD *thd,TABLE_LIST *tables,List &fields,COND *conds, DBUG_PRINT("info",("Creating tmp table")); thd->proc_info="Creating tmp table"; - if (!(tmp_table = - create_tmp_table(thd,&join.tmp_table_param,all_fields, + if (!(exec_tmp_table = + create_tmp_table(thd, &tmp_table_param, all_fields, ((!simple_group && !procedure && !(test_flags & TEST_NO_KEY_GROUP)) ? - group : (ORDER*) 0), - group ? 0 : select_distinct, - group && simple_group, + group_list : (ORDER*) 0), + group_list ? 0 : select_distinct, + group_list && simple_group, (order == 0 || skip_sort_order) && - !(join.select_options & OPTION_FOUND_ROWS), - join.select_options, unit))) - goto err; /* purecov: inspected */ + !(select_options & OPTION_FOUND_ROWS), + select_options, unit))) + DBUG_VOID_RETURN; - if (having && (join.sort_and_group || (tmp_table->distinct && !group))) - join.having=having; + if (having_list && + (sort_and_group || (exec_tmp_table->distinct && !group_list))) + having=having_list; /* if group or order on first table, sort first */ - if (group && simple_group) + if (group_list && simple_group) { DBUG_PRINT("info",("Sorting for group")); thd->proc_info="Sorting for group"; - if (create_sort_index(&join.join_tab[join.const_tables],group, + if (create_sort_index(&join_tab[const_tables], group_list, HA_POS_ERROR) || - make_sum_func_list(&join,all_fields) || - alloc_group_fields(&join,group)) - goto err; - group=0; + make_sum_func_list(this, all_fields) || + alloc_group_fields(this, group_list)) + DBUG_VOID_RETURN; + group_list=0; } else { - if (make_sum_func_list(&join,all_fields)) - goto err; - if (!group && ! tmp_table->distinct && order && simple_order) + if (make_sum_func_list(this, all_fields)) + DBUG_VOID_RETURN; + if (!group_list && ! exec_tmp_table->distinct && order && simple_order) { DBUG_PRINT("info",("Sorting for order")); thd->proc_info="Sorting for order"; - if (create_sort_index(&join.join_tab[join.const_tables],order, - HA_POS_ERROR)) - goto err; /* purecov: inspected */ + if (create_sort_index(&join_tab[const_tables], order, + HA_POS_ERROR)) + DBUG_VOID_RETURN; order=0; } } @@ -680,58 +721,58 @@ mysql_select(THD *thd,TABLE_LIST *tables,List &fields,COND *conds, In this case we can stop scanning t2 when we have found one t1.a */ - if (tmp_table->distinct) + if (exec_tmp_table->distinct) { table_map used_tables= thd->used_tables; - JOIN_TAB *join_tab=join.join_tab+join.tables-1; + JOIN_TAB *join_tab= this->join_tab+tables-1; do { if (used_tables & join_tab->table->map) break; join_tab->not_used_in_distinct=1; - } while (join_tab-- != join.join_tab); + } while (join_tab-- != this->join_tab); /* Optimize "select distinct b from t1 order by key_part_1 limit #" */ if (order && skip_sort_order) { - (void) test_if_skip_sort_order(&join.join_tab[join.const_tables], + (void) test_if_skip_sort_order(&this->join_tab[const_tables], order, unit->select_limit_cnt, 0); order=0; } } /* Copy data to the temporary table */ - thd->proc_info="Copying to tmp table"; - if ((tmp_error=do_select(&join,(List *) 0,tmp_table,0))) + thd->proc_info= "Copying to tmp table"; + if ((tmp_error= do_select(this, (List *) 0, exec_tmp_table, 0))) { - error=tmp_error; - goto err; /* purecov: inspected */ + error= tmp_error; + DBUG_VOID_RETURN; } - if (join.having) - join.having=having=0; // Allready done + if (having) + having= having_list= 0; // Allready done /* Change sum_fields reference to calculated fields in tmp_table */ - if (join.sort_and_group || tmp_table->group) + if (sort_and_group || exec_tmp_table->group) { if (change_to_use_tmp_fields(all_fields)) - goto err; - join.tmp_table_param.field_count+=join.tmp_table_param.sum_func_count+ - join.tmp_table_param.func_count; - join.tmp_table_param.sum_func_count=join.tmp_table_param.func_count=0; + DBUG_VOID_RETURN; + tmp_table_param.field_count+= tmp_table_param.sum_func_count+ + tmp_table_param.func_count; + tmp_table_param.sum_func_count= tmp_table_param.func_count= 0; } else { if (change_refs_to_tmp_fields(thd,all_fields)) - goto err; - join.tmp_table_param.field_count+=join.tmp_table_param.func_count; - join.tmp_table_param.func_count=0; + DBUG_VOID_RETURN; + tmp_table_param.field_count+= tmp_table_param.func_count; + tmp_table_param.func_count= 0; } if (procedure) procedure->update_refs(); - if (tmp_table->group) + if (exec_tmp_table->group) { // Already grouped if (!order && !no_order) - order=group; /* order by group */ - group=0; + order= group_list; /* order by group */ + group_list= 0; } /* @@ -742,153 +783,196 @@ mysql_select(THD *thd,TABLE_LIST *tables,List &fields,COND *conds, ** like SEC_TO_TIME(SUM(...)). */ - if (group && (!test_if_subpart(group,order) || select_distinct) || + if (group_list && (!test_if_subpart(group_list,order) || + select_distinct) || (select_distinct && - join.tmp_table_param.using_indirect_summary_function)) + tmp_table_param.using_indirect_summary_function)) { /* Must copy to another table */ TABLE *tmp_table2; DBUG_PRINT("info",("Creating group table")); /* Free first data from old join */ - join_free(&join); - if (make_simple_join(&join,tmp_table)) - goto err; - calc_group_buffer(&join,group); - count_field_types(&join.tmp_table_param,all_fields, - select_distinct && !group); - join.tmp_table_param.hidden_field_count=(all_fields.elements- - fields.elements); + join_free(this); + if (make_simple_join(this, exec_tmp_table)) + DBUG_VOID_RETURN; + calc_group_buffer(this, group_list); + count_field_types(&tmp_table_param, all_fields, + select_distinct && !group_list); + tmp_table_param.hidden_field_count= (all_fields.elements- + fields_list.elements); /* group data to new table */ - if (!(tmp_table2 = create_tmp_table(thd,&join.tmp_table_param,all_fields, + if (!(tmp_table2 = create_tmp_table(thd, &tmp_table_param, all_fields, (ORDER*) 0, - select_distinct && !group, + select_distinct && !group_list, 1, 0, - join.select_options, unit))) - goto err; /* purecov: inspected */ - if (group) + select_options, unit))) + DBUG_VOID_RETURN; + if (group_list) { thd->proc_info="Creating sort index"; - if (create_sort_index(join.join_tab,group,HA_POS_ERROR) || - alloc_group_fields(&join,group)) + if (create_sort_index(join_tab, group_list, HA_POS_ERROR) || + alloc_group_fields(this, group_list)) { free_tmp_table(thd,tmp_table2); /* purecov: inspected */ - goto err; /* purecov: inspected */ + DBUG_VOID_RETURN; } - group=0; + group_list= 0; } thd->proc_info="Copying to group table"; tmp_error= -1; - if (make_sum_func_list(&join,all_fields) || - (tmp_error=do_select(&join,(List *) 0,tmp_table2,0))) + if (make_sum_func_list(this, all_fields) || + (tmp_error=do_select(this, (List *) 0,tmp_table2,0))) { error=tmp_error; free_tmp_table(thd,tmp_table2); - goto err; /* purecov: inspected */ + DBUG_VOID_RETURN; } - end_read_record(&join.join_tab->read_record); - free_tmp_table(thd,tmp_table); - join.const_tables=join.tables; // Mark free for join_free() - tmp_table=tmp_table2; - join.join_tab[0].table=0; // Table is freed + end_read_record(&join_tab->read_record); + free_tmp_table(thd,exec_tmp_table); + const_tables= tables; // Mark free for join_free() + exec_tmp_table= tmp_table2; + join_tab[0].table= 0; // Table is freed if (change_to_use_tmp_fields(all_fields)) // No sum funcs anymore - goto err; - join.tmp_table_param.field_count+=join.tmp_table_param.sum_func_count; - join.tmp_table_param.sum_func_count=0; + DBUG_VOID_RETURN; + tmp_table_param.field_count+= tmp_table_param.sum_func_count; + tmp_table_param.sum_func_count= 0; } - if (tmp_table->distinct) + if (exec_tmp_table->distinct) select_distinct=0; /* Each row is unique */ - join_free(&join); /* Free quick selects */ - if (select_distinct && ! group) + join_free(this); /* Free quick selects */ + if (select_distinct && ! group_list) { thd->proc_info="Removing duplicates"; - if (having) - having->update_used_tables(); - if (remove_duplicates(&join,tmp_table,fields, having)) - goto err; /* purecov: inspected */ - having=0; + if (having_list) + having_list->update_used_tables(); + if (remove_duplicates(this, exec_tmp_table, fields_list, having_list)) + DBUG_VOID_RETURN; + having_list=0; select_distinct=0; } - tmp_table->reginfo.lock_type=TL_UNLOCK; - if (make_simple_join(&join,tmp_table)) - goto err; - calc_group_buffer(&join,group); - count_field_types(&join.tmp_table_param,all_fields,0); + exec_tmp_table->reginfo.lock_type=TL_UNLOCK; + if (make_simple_join(this, exec_tmp_table)) + DBUG_VOID_RETURN; + calc_group_buffer(this, group_list); + count_field_types(&tmp_table_param, all_fields, 0); } if (procedure) { - if (procedure->change_columns(fields) || - result->prepare(fields, unit)) - goto err; - count_field_types(&join.tmp_table_param, all_fields, 0); + if (procedure->change_columns(fields_list) || + result->prepare(fields_list, unit)) + DBUG_VOID_RETURN; + count_field_types(&tmp_table_param, all_fields, 0); } - if (join.group || join.tmp_table_param.sum_func_count || + if (group || tmp_table_param.sum_func_count || (procedure && (procedure->flags & PROC_GROUP))) { - alloc_group_fields(&join,group); - setup_copy_fields(thd, &join.tmp_table_param,all_fields); - if (make_sum_func_list(&join,all_fields) || thd->fatal_error) - goto err; /* purecov: inspected */ + alloc_group_fields(this, group_list); + setup_copy_fields(thd, &tmp_table_param,all_fields); + if (make_sum_func_list(this, all_fields) || thd->fatal_error) + DBUG_VOID_RETURN; } - if (group || order) + if (group_list || order) { DBUG_PRINT("info",("Sorting for send_fields")); thd->proc_info="Sorting result"; /* If we have already done the group, add HAVING to sorted table */ - if (having && ! group && ! join.sort_and_group) + if (having_list && ! group_list && ! sort_and_group) { - having->update_used_tables(); // Some tables may have been const - JOIN_TAB *table=&join.join_tab[join.const_tables]; - table_map used_tables= join.const_table_map | table->table->map; + having_list->update_used_tables(); // Some tables may have been const + JOIN_TAB *table= &join_tab[const_tables]; + table_map used_tables= const_table_map | table->table->map; - Item* sort_table_cond=make_cond_for_table(having,used_tables,used_tables); + Item* sort_table_cond= make_cond_for_table(having_list, used_tables, + used_tables); if (sort_table_cond) { if (!table->select) if (!(table->select=new SQL_SELECT)) - goto err; + DBUG_VOID_RETURN; if (!table->select->cond) table->select->cond=sort_table_cond; else // This should never happen if (!(table->select->cond=new Item_cond_and(table->select->cond, sort_table_cond))) - goto err; + DBUG_VOID_RETURN; table->select_cond=table->select->cond; DBUG_EXECUTE("where",print_where(table->select->cond, "select and having");); - having=make_cond_for_table(having,~ (table_map) 0,~used_tables); + having_list= make_cond_for_table(having_list, ~ (table_map) 0, + ~used_tables); DBUG_EXECUTE("where",print_where(conds,"having after sort");); } } - if (create_sort_index(&join.join_tab[join.const_tables], - group ? group : order, - (having || group || - join.const_tables != join.tables - 1 || - (join.select_options & OPTION_FOUND_ROWS)) ? - HA_POS_ERROR : unit->select_limit_cnt)) - goto err; /* purecov: inspected */ + if (create_sort_index(&join_tab[const_tables], + group_list ? group_list : order, + (having_list || group_list || + const_tables != tables - 1 || + (select_options & OPTION_FOUND_ROWS)) ? + HA_POS_ERROR : unit->select_limit_cnt)) + DBUG_VOID_RETURN; } - join.having=having; // Actually a parameter + having=having_list; // Actually a parameter thd->proc_info="Sending data"; - error=do_select(&join,&fields,NULL,procedure); + error=do_select(this, &fields_list, NULL, procedure); + DBUG_VOID_RETURN; +} -err: - thd->limit_found_rows = join.send_records; - thd->examined_row_count = join.examined_rows; - thd->proc_info="end"; - join.lock=0; // It's faster to unlock later - join_free(&join); - thd->proc_info="end2"; // QQ - if (tmp_table) - free_tmp_table(thd,tmp_table); - thd->proc_info="end3"; // QQ +/* + Clean up join. Return error that hold JOIN. +*/ + +int +JOIN::cleanup(THD *thd) +{ + lock=0; // It's faster to unlock later + join_free(this); + if (exec_tmp_table) + free_tmp_table(thd, exec_tmp_table); delete select; delete_dynamic(&keyuse); delete procedure; - thd->proc_info="end4"; // QQ + return error; +} + +int +mysql_select(THD *thd, TABLE_LIST *tables, List &fields, COND *conds, + ORDER *order, ORDER *group,Item *having, ORDER *proc_param, + ulong select_options, select_result *result, SELECT_LEX_UNIT *unit) +{ + JOIN *join = new JOIN(thd, fields, select_options, result); + + DBUG_ENTER("mysql_select"); + thd->proc_info="init"; + thd->used_tables=0; // Updated by setup_fields + + if (join->prepare(tables, conds, order, group, having, proc_param, + &(thd->lex.select_lex), unit)) + { + DBUG_RETURN(-1); + } + switch(join->optimize()) + { + case 1: + DBUG_RETURN(join->error); + case -1: + goto err; + } + + if(join->global_optimize()) + goto err; + + join->exec(); + +err: + thd->limit_found_rows = join->send_records; + thd->examined_row_count = join->examined_rows; + thd->proc_info="end"; + int error= join->cleanup(thd); + delete join; DBUG_RETURN(error); } @@ -2538,7 +2622,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) static void -make_join_readinfo(JOIN *join,uint options) +make_join_readinfo(JOIN *join, uint options) { uint i; SELECT_LEX *select_lex = &(join->thd->lex.select_lex); diff --git a/sql/sql_select.cc.rej b/sql/sql_select.cc.rej new file mode 100644 index 00000000000..e5be98e9859 --- /dev/null +++ b/sql/sql_select.cc.rej @@ -0,0 +1,1576 @@ +*************** +*** 65,71 **** + static int return_zero_rows(select_result *res,TABLE_LIST *tables, + List &fields, bool send_row, + uint select_options, const char *info, +- Item *having, Procedure *proc); + static COND *optimize_cond(COND *conds,Item::cond_result *cond_value); + static COND *remove_eq_conds(COND *cond,Item::cond_result *cond_value); + static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item); +--- 65,72 ---- + static int return_zero_rows(select_result *res,TABLE_LIST *tables, + List &fields, bool send_row, + uint select_options, const char *info, ++ Item *having, Procedure *proc, ++ SELECT_LEX *select_lex); + static COND *optimize_cond(COND *conds,Item::cond_result *cond_value); + static COND *remove_eq_conds(COND *cond,Item::cond_result *cond_value); + static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item); +*************** +*** 180,228 **** + ** mysql_select assumes that all tables are already opened + *****************************************************************************/ + + int +- mysql_select(THD *thd,TABLE_LIST *tables,List &fields,COND *conds, +- ORDER *order, ORDER *group,Item *having,ORDER *proc_param, +- ulong select_options,select_result *result) +- { +- TABLE *tmp_table; +- int error, tmp_error; +- bool need_tmp,hidden_group_fields; +- bool simple_order,simple_group,no_order, skip_sort_order, buffer_result; +- Item::cond_result cond_value; +- SQL_SELECT *select; +- DYNAMIC_ARRAY keyuse; +- JOIN join; +- Procedure *procedure; +- List all_fields(fields); +- bool select_distinct; +- SELECT_LEX *select_lex = &(thd->lex.select_lex); +- SELECT_LEX *cur_sel = thd->lex.select; +- DBUG_ENTER("mysql_select"); + +- /* Check that all tables, fields, conds and order are ok */ + +- select_distinct=test(select_options & SELECT_DISTINCT); +- buffer_result=test(select_options & OPTION_BUFFER_RESULT) && !test(select_options & OPTION_FOUND_ROWS); +- tmp_table=0; +- select=0; +- no_order=skip_sort_order=0; +- bzero((char*) &keyuse,sizeof(keyuse)); +- thd->proc_info="init"; +- thd->used_tables=0; // Updated by setup_fields + +- if (setup_tables(tables) || +- setup_fields(thd,tables,fields,1,&all_fields,1) || +- setup_conds(thd,tables,&conds) || +- setup_order(thd,tables,fields,all_fields,order) || +- setup_group(thd,tables,fields,all_fields,group,&hidden_group_fields)) + DBUG_RETURN(-1); /* purecov: inspected */ + + if (having) + { + thd->where="having clause"; + thd->allow_sum_func=1; +- if (having->fix_fields(thd,tables) || thd->fatal_error) + DBUG_RETURN(-1); /* purecov: inspected */ + if (having->with_sum_func) + having->split_sum_func(all_fields); +--- 195,237 ---- + ** mysql_select assumes that all tables are already opened + *****************************************************************************/ + ++ /* ++ Prepare of whole select (including subselect in future). ++ return -1 on error ++ 0 on success ++ */ + int + ++ JOIN::prepare(TABLE_LIST *tables_init, ++ COND *conds_init, ORDER *order_init, ORDER *group_init, ++ Item *having_init, ++ ORDER *proc_param_init, SELECT_LEX *select) ++ { ++ DBUG_ENTER("JOIN::prepare"); ++ ++ conds= conds_init; ++ order= order_init; ++ group_list= group_init; ++ having= having_init; ++ proc_param= proc_param_init; ++ tables_list= tables_init; ++ select_lex= select; + ++ /* Check that all tables, fields, conds and order are ok */ + ++ if (setup_tables(tables_list) || ++ setup_fields(thd,tables_list,fields_list,1,&all_fields,1) || ++ setup_conds(thd,tables_list,&conds) || ++ setup_order(thd,tables_list,fields_list,all_fields,order) || ++ setup_group(thd,tables_list,fields_list,all_fields,group_list, ++ &hidden_group_fields)) + DBUG_RETURN(-1); /* purecov: inspected */ + + if (having) + { + thd->where="having clause"; + thd->allow_sum_func=1; ++ if (having->fix_fields(thd,tables_list) || thd->fatal_error) + DBUG_RETURN(-1); /* purecov: inspected */ + if (having->with_sum_func) + having->split_sum_func(all_fields); +*************** +*** 297,347 **** + } + + /* Init join struct */ +- join.thd=thd; +- join.lock=thd->lock; +- join.join_tab=0; +- join.tmp_table_param.copy_field=0; +- join.sum_funcs=0; +- join.send_records=join.found_records=join.examined_rows=0; +- join.tmp_table_param.end_write_records= HA_POS_ERROR; +- join.first_record=join.sort_and_group=0; +- join.select_options=select_options; +- join.result=result; +- count_field_types(&join.tmp_table_param,all_fields,0); +- join.const_tables=0; +- join.having=0; +- join.do_send_rows = 1; +- join.group= group != 0; +- join.row_limit= ((select_distinct || order || group) ? HA_POS_ERROR : +- thd->select_limit); + + #ifdef RESTRICTED_GROUP +- if (join.sum_func_count && !group && (join.func_count || join.field_count)) + { + my_message(ER_WRONG_SUM_SELECT,ER(ER_WRONG_SUM_SELECT),MYF(0)); + delete procedure; + DBUG_RETURN(-1); + } + #endif +- if (!procedure && result->prepare(fields)) + { /* purecov: inspected */ + DBUG_RETURN(-1); /* purecov: inspected */ + } + + #ifdef HAVE_REF_TO_FIELDS // Not done yet + /* Add HAVING to WHERE if possible */ +- if (having && !group && ! join.sum_func_count) + { + if (!conds) + { +- conds=having; +- having=0; + } + else if ((conds=new Item_cond_and(conds,having))) + { +- conds->fix_fields(thd,tables); +- conds->change_ref_to_fields(thd,tables); +- having=0; + } + } + #endif +--- 305,358 ---- + } + + /* Init join struct */ ++ count_field_types(&tmp_table_param, all_fields, 0); ++ this->group= group_list != 0; ++ row_limit= ((select_distinct || order || group_list) ? HA_POS_ERROR : ++ select_lex->first_in_union->select_limit_cnt); + + #ifdef RESTRICTED_GROUP ++ if (sum_func_count && !group_list && (func_count || field_count)) + { + my_message(ER_WRONG_SUM_SELECT,ER(ER_WRONG_SUM_SELECT),MYF(0)); + delete procedure; + DBUG_RETURN(-1); + } + #endif ++ if (!procedure && result->prepare(fields_list, select_lex->first_in_union)) + { /* purecov: inspected */ + DBUG_RETURN(-1); /* purecov: inspected */ + } ++ DBUG_RETURN(0); // All OK ++ } ++ ++ /* ++ global select optimisation. ++ return 0 - success ++ 1 - go out ++ -1 - go out with cleaning ++ ++ error code saved in field 'error' ++ */ ++ int ++ JOIN::optimize() ++ { ++ DBUG_ENTER("JOIN::optimize"); ++ SELECT_LEX *select_lex = &(thd->lex.select_lex); + + #ifdef HAVE_REF_TO_FIELDS // Not done yet + /* Add HAVING to WHERE if possible */ ++ if (having && !group_list && ! sum_func_count) + { + if (!conds) + { ++ conds= having; ++ having= 0; + } + else if ((conds=new Item_cond_and(conds,having))) + { ++ conds->fix_fields(thd, tables_list); ++ conds->change_ref_to_fields(thd, tables_list); ++ having= 0; + } + } + #endif +*************** +*** 350,459 **** + if (thd->fatal_error) // Out of memory + { + delete procedure; +- DBUG_RETURN(0); + } +- if (cond_value == Item::COND_FALSE || !thd->select_limit) + { /* Impossible cond */ +- if (select_options & SELECT_DESCRIBE && select_lex->next) +- select_describe(&join,false,false,false,"Impossible WHERE"); +- else +- error=return_zero_rows(result, tables, fields, +- join.tmp_table_param.sum_func_count != 0 && !group, +- select_options,"Impossible WHERE",having, +- procedure); +- delete procedure; +- DBUG_RETURN(error); + } + + /* Optimize count(*), min() and max() */ +- if (tables && join.tmp_table_param.sum_func_count && ! group) + { + int res; +- if ((res=opt_sum_query(tables, all_fields, conds))) + { + if (res < 0) + { +- if (select_options & SELECT_DESCRIBE && select_lex->next) +- select_describe(&join,false,false,false,"No matching min/max row"); +- else +- error=return_zero_rows(result, tables, fields, !group, +- select_options,"No matching min/max row", +- having,procedure); +- delete procedure; +- DBUG_RETURN(error); + } + if (select_options & SELECT_DESCRIBE) + { + if (select_lex->next) +- select_describe(&join,false,false,false,"Select tables optimized away"); + else +- describe_info(thd,"Select tables optimized away"); + delete procedure; +- DBUG_RETURN(error); + } +- tables=0; // All tables resolved + } + } +- if (!tables) +- { // Only test of functions +- error=0; +- if (select_options & SELECT_DESCRIBE) +- { +- if (select_lex->next) +- select_describe(&join,false,false,false,"No tables used"); +- else +- describe_info(thd,"No tables used"); +- } +- else +- { +- result->send_fields(fields,1); +- if (!having || having->val_int()) +- { +- if (join.do_send_rows && result->send_data(fields)) +- { +- result->send_error(0,NullS); /* purecov: inspected */ +- error=1; +- } +- else +- error=(int) result->send_eof(); +- } +- else +- error=(int) result->send_eof(); +- } +- delete procedure; +- DBUG_RETURN(error); + } + +- error = -1; +- join.sort_by_table=get_sort_by_table(order,group,tables); + + /* Calculate how to do the join */ +- thd->proc_info="statistics"; +- if (make_join_statistics(&join,tables,conds,&keyuse) || thd->fatal_error) +- goto err; +- thd->proc_info="preparing"; +- result->initialize_tables(&join); +- if (join.const_table_map != join.found_const_table_map && + !(select_options & SELECT_DESCRIBE)) + { +- error=return_zero_rows(result,tables,fields, +- join.tmp_table_param.sum_func_count != 0 && +- !group,0,"",having,procedure); +- goto err; + } + if (!(thd->options & OPTION_BIG_SELECTS) && +- join.best_read > (double) thd->max_join_size && + !(select_options & SELECT_DESCRIBE)) + { /* purecov: inspected */ + result->send_error(ER_TOO_BIG_SELECT,ER(ER_TOO_BIG_SELECT)); /* purecov: inspected */ + error= 1; /* purecov: inspected */ +- goto err; /* purecov: inspected */ + } +- if (join.const_tables && !thd->locked_tables && + !(select_options & SELECT_NO_UNLOCK)) + { + TABLE **table, **end; +- for (table=join.table, end=table + join.const_tables ; + table != end; + table++) + { +--- 361,436 ---- + if (thd->fatal_error) // Out of memory + { + delete procedure; ++ error = 0; ++ DBUG_RETURN(1); + } ++ if (cond_value == Item::COND_FALSE || ++ !select_lex->first_in_union->select_limit_cnt) + { /* Impossible cond */ ++ zero_result_cause= "Impossible WHERE"; ++ DBUG_RETURN(0); + } + + /* Optimize count(*), min() and max() */ ++ if (tables_list && tmp_table_param.sum_func_count && ! group_list) + { + int res; ++ if ((res=opt_sum_query(tables_list, all_fields, conds))) + { + if (res < 0) + { ++ zero_result_cause= "No matching min/max row"; ++ DBUG_RETURN(0); + } + if (select_options & SELECT_DESCRIBE) + { + if (select_lex->next) ++ select_describe(this, false, false, false, ++ "Select tables optimized away"); + else ++ describe_info(thd, "Select tables optimized away"); + delete procedure; ++ DBUG_RETURN(1); + } ++ tables_list=0; // All tables resolved + } + } ++ if (!tables_list) ++ { ++ test_function_query= 1; ++ DBUG_RETURN(0); + } + ++ error= -1; ++ sort_by_table= get_sort_by_table(order, group_list, tables_list); + + /* Calculate how to do the join */ ++ thd->proc_info= "statistics"; ++ if (make_join_statistics(this, tables_list, conds, &keyuse) || ++ thd->fatal_error) ++ DBUG_RETURN(-1); ++ thd->proc_info= "preparing"; ++ result->initialize_tables(this); ++ if (const_table_map != found_const_table_map && + !(select_options & SELECT_DESCRIBE)) + { ++ zero_result_cause= ""; ++ select_options= 0; //TODO why option in return_zero_rows was droped ++ DBUG_RETURN(0); + } + if (!(thd->options & OPTION_BIG_SELECTS) && ++ best_read > (double) thd->max_join_size && + !(select_options & SELECT_DESCRIBE)) + { /* purecov: inspected */ + result->send_error(ER_TOO_BIG_SELECT,ER(ER_TOO_BIG_SELECT)); /* purecov: inspected */ + error= 1; /* purecov: inspected */ ++ DBUG_RETURN(-1); + } ++ if (const_tables && !thd->locked_tables && + !(select_options & SELECT_NO_UNLOCK)) + { + TABLE **table, **end; ++ for (table=this->table, end=table + const_tables ; + table != end; + table++) + { +*************** +*** 465,561 **** + } + (*table)->file->index_end(); + } +- mysql_unlock_some_tables(thd, join.table,join.const_tables); + } +- if (!conds && join.outer_join) + { + /* Handle the case where we have an OUTER JOIN without a WHERE */ + conds=new Item_int((longlong) 1,1); // Always true + } +- select=make_select(*join.table, join.const_table_map, +- join.const_table_map,conds,&error); + if (error) + { /* purecov: inspected */ + error= -1; /* purecov: inspected */ +- goto err; /* purecov: inspected */ + } +- if (make_join_select(&join,select,conds)) + { +- if (select_options & SELECT_DESCRIBE && select_lex->next) +- select_describe(&join,false,false,false,"Impossible WHERE noticed after reading const tables"); +- else +- error=return_zero_rows(result,tables,fields, +- join.tmp_table_param.sum_func_count != 0 && !group, +- select_options, +- "Impossible WHERE noticed after reading const tables", +- having,procedure); +- goto err; + } + + error= -1; /* if goto err */ + + /* Optimize distinct away if possible */ +- order=remove_const(&join,order,conds,&simple_order); +- if (group || join.tmp_table_param.sum_func_count) + { + if (! hidden_group_fields) + select_distinct=0; + } +- else if (select_distinct && join.tables - join.const_tables == 1 && +- (thd->select_limit == HA_POS_ERROR || +- (join.select_options & OPTION_FOUND_ROWS) || + order && + !(skip_sort_order= +- test_if_skip_sort_order(&join.join_tab[join.const_tables], +- order, thd->select_limit,1)))) + { +- if ((group=create_distinct_group(order,fields))) + { + select_distinct=0; + no_order= !order; +- join.group=1; // For end_write_group + } + else if (thd->fatal_error) // End of memory +- goto err; + } +- group=remove_const(&join,group,conds,&simple_group); +- if (!group && join.group) + { + order=0; // The output has only one row + simple_order=1; + } + +- calc_group_buffer(&join,group); +- join.send_group_parts=join.tmp_table_param.group_parts; /* Save org parts */ + if (procedure && procedure->group) + { +- group=procedure->group=remove_const(&join,procedure->group,conds, +- &simple_group); +- calc_group_buffer(&join,group); + } + +- if (test_if_subpart(group,order) || +- (!group && join.tmp_table_param.sum_func_count)) + order=0; + + // Can't use sort on head table if using row cache +- if (join.full_join) + { +- if (group) + simple_group=0; + if (order) + simple_order=0; + } + +- need_tmp= (join.const_tables != join.tables && + ((select_distinct || !simple_order || !simple_group) || +- (group && order) || buffer_result)); + + // No cache for MATCH +- make_join_readinfo(&join, + (select_options & (SELECT_DESCRIBE | + SELECT_NO_JOIN_CACHE)) | +- (cur_sel->ftfunc_list.elements ? SELECT_NO_JOIN_CACHE : 0)); + + /* Need to tell Innobase that to play it safe, it should fetch all + columns of the tables: this is because MySQL +--- 442,535 ---- + } + (*table)->file->index_end(); + } ++ mysql_unlock_some_tables(thd, this->table, const_tables); + } ++ if (!conds && outer_join) + { + /* Handle the case where we have an OUTER JOIN without a WHERE */ + conds=new Item_int((longlong) 1,1); // Always true + } ++ select=make_select(*table, const_table_map, ++ const_table_map, conds, &error); + if (error) + { /* purecov: inspected */ + error= -1; /* purecov: inspected */ ++ DBUG_RETURN(-1); + } ++ if (make_join_select(this, select, conds)) + { ++ zero_result_cause= ++ "Impossible WHERE noticed after reading const tables"; ++ DBUG_RETURN(0); + } + + error= -1; /* if goto err */ + + /* Optimize distinct away if possible */ ++ order=remove_const(this,order,conds,&simple_order); ++ if (group_list || tmp_table_param.sum_func_count) + { + if (! hidden_group_fields) + select_distinct=0; + } ++ else if (select_distinct && tables - const_tables == 1 && ++ (select_lex->first_in_union->select_limit_cnt == HA_POS_ERROR || ++ (select_options & OPTION_FOUND_ROWS) || + order && + !(skip_sort_order= ++ test_if_skip_sort_order(&join_tab[const_tables], ++ order, ++ select_lex->first_in_union->select_limit_cnt, ++ 1)))) + { ++ if ((group_list=create_distinct_group(order, fields_list))) + { + select_distinct=0; + no_order= !order; ++ group=1; // For end_write_group + } + else if (thd->fatal_error) // End of memory ++ DBUG_RETURN(-1); + } ++ group_list= remove_const(this, group_list, conds, &simple_group); ++ if (!group_list && group) + { + order=0; // The output has only one row + simple_order=1; + } + ++ calc_group_buffer(this, group_list); ++ send_group_parts=tmp_table_param.group_parts; /* Save org parts */ + if (procedure && procedure->group) + { ++ group_list= procedure->group= remove_const(this, procedure->group, conds, ++ &simple_group); ++ calc_group_buffer(this, group_list); + } + ++ if (test_if_subpart(group_list, order) || ++ (!group_list && tmp_table_param.sum_func_count)) + order=0; + + // Can't use sort on head table if using row cache ++ if (full_join) + { ++ if (group_list) + simple_group=0; + if (order) + simple_order=0; + } + ++ need_tmp= (const_tables != tables && + ((select_distinct || !simple_order || !simple_group) || ++ (group_list && order) || buffer_result)); + + // No cache for MATCH ++ make_join_readinfo(this, + (select_options & (SELECT_DESCRIBE | + SELECT_NO_JOIN_CACHE)) | ++ (thd->lex.select->ftfunc_list.elements ? ++ SELECT_NO_JOIN_CACHE : 0)); + + /* Need to tell Innobase that to play it safe, it should fetch all + columns of the tables: this is because MySQL +*************** +*** 564,624 **** + by MySQL. */ + + #ifdef HAVE_INNOBASE_DB +- if (need_tmp || select_distinct || group || order) + { +- for (uint i_h = join.const_tables; i_h < join.tables; i_h++) + { +- TABLE* table_h = join.join_tab[i_h].table; + if (table_h->db_type == DB_TYPE_INNODB) + table_h->file->extra(HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE); + } + } + #endif + +- DBUG_EXECUTE("info",TEST_join(&join);); + /* + Because filesort always does a full table scan or a quick range scan + we must add the removed reference to the select for the table. + We only need to do this when we have a simple_order or simple_group + as in other cases the join is done before the sort. + */ +- if ((order || group) && join.join_tab[join.const_tables].type != JT_ALL && +- join.join_tab[join.const_tables].type != JT_FT && +- (order && simple_order || group && simple_group)) + { +- if (add_ref_to_table_cond(thd,&join.join_tab[join.const_tables])) +- goto err; + } + + if (!(select_options & SELECT_BIG_RESULT) && +- ((group && join.const_tables != join.tables && + (!simple_group || +- !test_if_skip_sort_order(&join.join_tab[join.const_tables], group, +- thd->select_limit,0))) || + select_distinct) && +- join.tmp_table_param.quick_group && !procedure) + { + need_tmp=1; simple_order=simple_group=0; // Force tmp table without sort + } + + if (select_options & SELECT_DESCRIBE) + { + if (!order && !no_order) +- order=group; + if (order && +- (join.const_tables == join.tables || + (simple_order && +- test_if_skip_sort_order(&join.join_tab[join.const_tables], order, +- (join.const_tables != join.tables - 1 || +- (join.select_options & OPTION_FOUND_ROWS)) ? +- HA_POS_ERROR : thd->select_limit,0)))) + order=0; +- select_describe(&join,need_tmp, + (order != 0 && +- (!need_tmp || order != group || simple_group)), + select_distinct); + error=0; +- goto err; + } + + /* Perform FULLTEXT search before all regular searches */ +--- 538,672 ---- + by MySQL. */ + + #ifdef HAVE_INNOBASE_DB ++ if (need_tmp || select_distinct || group_list || order) + { ++ for (uint i_h = const_tables; i_h < tables; i_h++) + { ++ TABLE* table_h = join_tab[i_h].table; + if (table_h->db_type == DB_TYPE_INNODB) + table_h->file->extra(HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE); + } + } + #endif + ++ DBUG_EXECUTE("info",TEST_join(this);); + /* + Because filesort always does a full table scan or a quick range scan + we must add the removed reference to the select for the table. + We only need to do this when we have a simple_order or simple_group + as in other cases the join is done before the sort. + */ ++ if ((order || group_list) && join_tab[const_tables].type != JT_ALL && ++ join_tab[const_tables].type != JT_FT && ++ (order && simple_order || group_list && simple_group)) + { ++ if (add_ref_to_table_cond(thd,&join_tab[const_tables])) ++ DBUG_RETURN(-1); + } + + if (!(select_options & SELECT_BIG_RESULT) && ++ ((group_list && const_tables != tables && + (!simple_group || ++ !test_if_skip_sort_order(&join_tab[const_tables], group_list, ++ select_lex->first_in_union->select_limit_cnt, ++ 0))) || + select_distinct) && ++ tmp_table_param.quick_group && !procedure) + { + need_tmp=1; simple_order=simple_group=0; // Force tmp table without sort + } ++ DBUG_RETURN(0); ++ } ++ ++ /* ++ global uptimisation (with subselect) must be here (TODO) ++ */ ++ ++ int ++ JOIN::global_optimize() ++ { ++ return 0; ++ } ++ ++ /* ++ exec select ++ */ ++ ++ void ++ JOIN::exec() ++ { ++ int tmp_error; ++ ++ DBUG_ENTER("JOIN::exec"); ++ ++ if (test_function_query) ++ { // Only test of functions ++ error=0; ++ if (select_options & SELECT_DESCRIBE) ++ { ++ if (select_lex->next) ++ select_describe(this, false, false, false, "No tables used"); ++ else ++ describe_info(thd, "No tables used"); ++ } ++ else ++ { ++ result->send_fields(fields_list,1); ++ if (!having || having->val_int()) ++ { ++ if (do_send_rows && result->send_data(fields_list)) ++ { ++ result->send_error(0,NullS); /* purecov: inspected */ ++ error=1; ++ } ++ else ++ error=(int) result->send_eof(); ++ } ++ else ++ error=(int) result->send_eof(); ++ } ++ delete procedure; ++ DBUG_VOID_RETURN; ++ } ++ ++ if (zero_result_cause) ++ { ++ if (select_options & SELECT_DESCRIBE && select_lex->next) ++ select_describe(this, false, false, false, zero_result_cause); ++ else ++ error=return_zero_rows(result, tables_list, fields_list, ++ tmp_table_param.sum_func_count != 0 && ++ !group_list, ++ select_options, ++ zero_result_cause, ++ having,procedure, ++ select_lex->first_in_union); ++ DBUG_VOID_RETURN; ++ } ++ ++ Item *having_list = having; ++ having = 0; + + if (select_options & SELECT_DESCRIBE) + { + if (!order && !no_order) ++ order=group_list; + if (order && ++ (const_tables == tables || + (simple_order && ++ test_if_skip_sort_order(&join_tab[const_tables], order, ++ (const_tables != tables - 1 || ++ (select_options & OPTION_FOUND_ROWS)) ? ++ HA_POS_ERROR : ++ select_lex->first_in_union->select_limit_cnt, ++ 0)))) + order=0; ++ select_describe(this, need_tmp, + (order != 0 && ++ (!need_tmp || order != group_list || simple_group)), + select_distinct); + error=0; ++ DBUG_VOID_RETURN; + } + + /* Perform FULLTEXT search before all regular searches */ +*************** +*** 630,673 **** + DBUG_PRINT("info",("Creating tmp table")); + thd->proc_info="Creating tmp table"; + +- if (!(tmp_table = +- create_tmp_table(thd,&join.tmp_table_param,all_fields, + ((!simple_group && !procedure && + !(test_flags & TEST_NO_KEY_GROUP)) ? +- group : (ORDER*) 0), +- group ? 0 : select_distinct, +- group && simple_group, + (order == 0 || skip_sort_order) && +- !(join.select_options & OPTION_FOUND_ROWS), +- join.select_options))) +- goto err; /* purecov: inspected */ +- +- if (having && (join.sort_and_group || (tmp_table->distinct && !group))) +- join.having=having; + + /* if group or order on first table, sort first */ +- if (group && simple_group) + { + DBUG_PRINT("info",("Sorting for group")); + thd->proc_info="Sorting for group"; +- if (create_sort_index(&join.join_tab[join.const_tables],group, + HA_POS_ERROR) || +- make_sum_func_list(&join,all_fields) || +- alloc_group_fields(&join,group)) +- goto err; +- group=0; + } + else + { +- if (make_sum_func_list(&join,all_fields)) +- goto err; +- if (!group && ! tmp_table->distinct && order && simple_order) + { + DBUG_PRINT("info",("Sorting for order")); + thd->proc_info="Sorting for order"; +- if (create_sort_index(&join.join_tab[join.const_tables],order, + HA_POS_ERROR)) +- goto err; /* purecov: inspected */ + order=0; + } + } +--- 678,722 ---- + DBUG_PRINT("info",("Creating tmp table")); + thd->proc_info="Creating tmp table"; + ++ if (!(exec_tmp_table = ++ create_tmp_table(thd,&tmp_table_param,all_fields, + ((!simple_group && !procedure && + !(test_flags & TEST_NO_KEY_GROUP)) ? ++ group_list : (ORDER*) 0), ++ group_list ? 0 : select_distinct, ++ group_list && simple_group, + (order == 0 || skip_sort_order) && ++ !(select_options & OPTION_FOUND_ROWS), ++ select_options, select_lex->first_in_union))) ++ DBUG_VOID_RETURN; ++ ++ if (having_list && ++ (sort_and_group || (exec_tmp_table->distinct && !group_list))) ++ having=having_list; + + /* if group or order on first table, sort first */ ++ if (group_list && simple_group) + { + DBUG_PRINT("info",("Sorting for group")); + thd->proc_info="Sorting for group"; ++ if (create_sort_index(&join_tab[const_tables],group_list, + HA_POS_ERROR) || ++ make_sum_func_list(this, all_fields) || ++ alloc_group_fields(this, group_list)) ++ DBUG_VOID_RETURN; ++ group_list=0; + } + else + { ++ if (make_sum_func_list(this, all_fields)) ++ DBUG_VOID_RETURN; ++ if (!group_list && ! exec_tmp_table->distinct && order && simple_order) + { + DBUG_PRINT("info",("Sorting for order")); + thd->proc_info="Sorting for order"; ++ if (create_sort_index(&join_tab[const_tables], order, + HA_POS_ERROR)) ++ DBUG_VOID_RETURN; + order=0; + } + } +*************** +*** 678,735 **** + In this case we can stop scanning t2 when we have found one t1.a + */ + +- if (tmp_table->distinct) + { + table_map used_tables= thd->used_tables; +- JOIN_TAB *join_tab=join.join_tab+join.tables-1; + do + { + if (used_tables & join_tab->table->map) + break; + join_tab->not_used_in_distinct=1; +- } while (join_tab-- != join.join_tab); + /* Optimize "select distinct b from t1 order by key_part_1 limit #" */ + if (order && skip_sort_order) + { +- (void) test_if_skip_sort_order(&join.join_tab[join.const_tables], +- order, thd->select_limit,0); + order=0; + } + } + + /* Copy data to the temporary table */ + thd->proc_info="Copying to tmp table"; +- if ((tmp_error=do_select(&join,(List *) 0,tmp_table,0))) + { +- error=tmp_error; +- goto err; /* purecov: inspected */ + } +- if (join.having) +- join.having=having=0; // Allready done + + /* Change sum_fields reference to calculated fields in tmp_table */ +- if (join.sort_and_group || tmp_table->group) + { + if (change_to_use_tmp_fields(all_fields)) +- goto err; +- join.tmp_table_param.field_count+=join.tmp_table_param.sum_func_count+ +- join.tmp_table_param.func_count; +- join.tmp_table_param.sum_func_count=join.tmp_table_param.func_count=0; + } + else + { + if (change_refs_to_tmp_fields(thd,all_fields)) +- goto err; +- join.tmp_table_param.field_count+=join.tmp_table_param.func_count; +- join.tmp_table_param.func_count=0; + } + if (procedure) + procedure->update_refs(); +- if (tmp_table->group) + { // Already grouped + if (!order && !no_order) +- order=group; /* order by group */ +- group=0; + } + + /* +--- 727,786 ---- + In this case we can stop scanning t2 when we have found one t1.a + */ + ++ if (exec_tmp_table->distinct) + { + table_map used_tables= thd->used_tables; ++ JOIN_TAB *join_tab= this->join_tab+tables-1; + do + { + if (used_tables & join_tab->table->map) + break; + join_tab->not_used_in_distinct=1; ++ } while (join_tab-- != this->join_tab); + /* Optimize "select distinct b from t1 order by key_part_1 limit #" */ + if (order && skip_sort_order) + { ++ (void) test_if_skip_sort_order(&this->join_tab[const_tables], ++ order, ++ select_lex->first_in_union->select_limit_cnt, ++ 0); + order=0; + } + } + + /* Copy data to the temporary table */ + thd->proc_info="Copying to tmp table"; ++ if ((tmp_error=do_select(this, (List *) 0, exec_tmp_table, 0))) + { ++ error= tmp_error; ++ DBUG_VOID_RETURN; + } ++ if (having) ++ having= having_list= 0; // Allready done + + /* Change sum_fields reference to calculated fields in tmp_table */ ++ if (sort_and_group || exec_tmp_table->group) + { + if (change_to_use_tmp_fields(all_fields)) ++ DBUG_VOID_RETURN; ++ tmp_table_param.field_count+= tmp_table_param.sum_func_count+ ++ tmp_table_param.func_count; ++ tmp_table_param.sum_func_count= tmp_table_param.func_count= 0; + } + else + { + if (change_refs_to_tmp_fields(thd,all_fields)) ++ DBUG_VOID_RETURN; ++ tmp_table_param.field_count+= tmp_table_param.func_count; ++ tmp_table_param.func_count= 0; + } + if (procedure) + procedure->update_refs(); ++ if (exec_tmp_table->group) + { // Already grouped + if (!order && !no_order) ++ order= group_list; /* order by group */ ++ group_list= 0; + } + + /* +*************** +*** 740,892 **** + ** like SEC_TO_TIME(SUM(...)). + */ + +- if (group && (!test_if_subpart(group,order) || select_distinct) || + (select_distinct && +- join.tmp_table_param.using_indirect_summary_function)) + { /* Must copy to another table */ + TABLE *tmp_table2; + DBUG_PRINT("info",("Creating group table")); + + /* Free first data from old join */ +- join_free(&join); +- if (make_simple_join(&join,tmp_table)) +- goto err; +- calc_group_buffer(&join,group); +- count_field_types(&join.tmp_table_param,all_fields, +- select_distinct && !group); +- join.tmp_table_param.hidden_field_count=(all_fields.elements- +- fields.elements); + + /* group data to new table */ +- if (!(tmp_table2 = create_tmp_table(thd,&join.tmp_table_param,all_fields, + (ORDER*) 0, +- select_distinct && !group, + 1, 0, +- join.select_options))) +- goto err; /* purecov: inspected */ +- if (group) + { + thd->proc_info="Creating sort index"; +- if (create_sort_index(join.join_tab,group,HA_POS_ERROR) || +- alloc_group_fields(&join,group)) + { + free_tmp_table(thd,tmp_table2); /* purecov: inspected */ +- goto err; /* purecov: inspected */ + } +- group=0; + } + thd->proc_info="Copying to group table"; + tmp_error= -1; +- if (make_sum_func_list(&join,all_fields) || +- (tmp_error=do_select(&join,(List *) 0,tmp_table2,0))) + { + error=tmp_error; + free_tmp_table(thd,tmp_table2); +- goto err; /* purecov: inspected */ + } +- end_read_record(&join.join_tab->read_record); +- free_tmp_table(thd,tmp_table); +- join.const_tables=join.tables; // Mark free for join_free() +- tmp_table=tmp_table2; +- join.join_tab[0].table=0; // Table is freed + + if (change_to_use_tmp_fields(all_fields)) // No sum funcs anymore +- goto err; +- join.tmp_table_param.field_count+=join.tmp_table_param.sum_func_count; +- join.tmp_table_param.sum_func_count=0; + } + +- if (tmp_table->distinct) + select_distinct=0; /* Each row is unique */ + +- join_free(&join); /* Free quick selects */ +- if (select_distinct && ! group) + { + thd->proc_info="Removing duplicates"; +- if (having) +- having->update_used_tables(); +- if (remove_duplicates(&join,tmp_table,fields, having)) +- goto err; /* purecov: inspected */ +- having=0; + select_distinct=0; + } +- tmp_table->reginfo.lock_type=TL_UNLOCK; +- if (make_simple_join(&join,tmp_table)) +- goto err; +- calc_group_buffer(&join,group); +- count_field_types(&join.tmp_table_param,all_fields,0); + } + if (procedure) + { +- if (procedure->change_columns(fields) || +- result->prepare(fields)) +- goto err; +- count_field_types(&join.tmp_table_param,all_fields,0); + } +- if (join.group || join.tmp_table_param.sum_func_count || + (procedure && (procedure->flags & PROC_GROUP))) + { +- alloc_group_fields(&join,group); +- setup_copy_fields(thd, &join.tmp_table_param,all_fields); +- if (make_sum_func_list(&join,all_fields) || thd->fatal_error) +- goto err; /* purecov: inspected */ + } +- if (group || order) + { + DBUG_PRINT("info",("Sorting for send_fields")); + thd->proc_info="Sorting result"; + /* If we have already done the group, add HAVING to sorted table */ +- if (having && ! group && ! join.sort_and_group) + { +- having->update_used_tables(); // Some tables may have been const +- JOIN_TAB *table=&join.join_tab[join.const_tables]; +- table_map used_tables= join.const_table_map | table->table->map; + +- Item* sort_table_cond=make_cond_for_table(having,used_tables,used_tables); + if (sort_table_cond) + { + if (!table->select) + if (!(table->select=new SQL_SELECT)) +- goto err; + if (!table->select->cond) + table->select->cond=sort_table_cond; + else // This should never happen + if (!(table->select->cond=new Item_cond_and(table->select->cond, + sort_table_cond))) +- goto err; + table->select_cond=table->select->cond; + DBUG_EXECUTE("where",print_where(table->select->cond, + "select and having");); +- having=make_cond_for_table(having,~ (table_map) 0,~used_tables); + DBUG_EXECUTE("where",print_where(conds,"having after sort");); + } + } +- if (create_sort_index(&join.join_tab[join.const_tables], +- group ? group : order, +- (having || group || +- join.const_tables != join.tables - 1 || +- (join.select_options & OPTION_FOUND_ROWS)) ? +- HA_POS_ERROR : thd->select_limit)) +- goto err; /* purecov: inspected */ + } +- join.having=having; // Actually a parameter + thd->proc_info="Sending data"; +- error=do_select(&join,&fields,NULL,procedure); + +- err: +- thd->limit_found_rows = join.send_records; +- thd->examined_row_count = join.examined_rows; +- thd->proc_info="end"; +- join.lock=0; // It's faster to unlock later +- join_free(&join); +- thd->proc_info="end2"; // QQ +- if (tmp_table) +- free_tmp_table(thd,tmp_table); +- thd->proc_info="end3"; // QQ + delete select; + delete_dynamic(&keyuse); + delete procedure; +- thd->proc_info="end4"; // QQ + DBUG_RETURN(error); + } + +--- 791,987 ---- + ** like SEC_TO_TIME(SUM(...)). + */ + ++ if (group_list && (!test_if_subpart(group_list,order) || select_distinct) || + (select_distinct && ++ tmp_table_param.using_indirect_summary_function)) + { /* Must copy to another table */ + TABLE *tmp_table2; + DBUG_PRINT("info",("Creating group table")); + + /* Free first data from old join */ ++ join_free(this); ++ if (make_simple_join(this, exec_tmp_table)) ++ DBUG_VOID_RETURN; ++ calc_group_buffer(this, group_list); ++ count_field_types(&tmp_table_param, all_fields, ++ select_distinct && !group_list); ++ tmp_table_param.hidden_field_count= (all_fields.elements- ++ fields_list.elements); + + /* group data to new table */ ++ if (!(tmp_table2 = create_tmp_table(thd,&tmp_table_param, all_fields, + (ORDER*) 0, ++ select_distinct && !group_list, + 1, 0, ++ select_options, ++ select_lex->first_in_union))) ++ DBUG_VOID_RETURN; ++ if (group_list) + { + thd->proc_info="Creating sort index"; ++ if (create_sort_index(join_tab,group_list,HA_POS_ERROR) || ++ alloc_group_fields(this, group_list)) + { + free_tmp_table(thd,tmp_table2); /* purecov: inspected */ ++ DBUG_VOID_RETURN; + } ++ group_list=0; + } + thd->proc_info="Copying to group table"; + tmp_error= -1; ++ if (make_sum_func_list(this, all_fields) || ++ (tmp_error=do_select(this, (List *) 0,tmp_table2,0))) + { + error=tmp_error; + free_tmp_table(thd,tmp_table2); ++ DBUG_VOID_RETURN; + } ++ end_read_record(&join_tab->read_record); ++ free_tmp_table(thd,exec_tmp_table); ++ const_tables= tables; // Mark free for join_free() ++ exec_tmp_table= tmp_table2; ++ join_tab[0].table= 0; // Table is freed + + if (change_to_use_tmp_fields(all_fields)) // No sum funcs anymore ++ DBUG_VOID_RETURN; ++ tmp_table_param.field_count+= tmp_table_param.sum_func_count; ++ tmp_table_param.sum_func_count=0; + } + ++ if (exec_tmp_table->distinct) + select_distinct=0; /* Each row is unique */ + ++ join_free(this); /* Free quick selects */ ++ if (select_distinct && ! group_list) + { + thd->proc_info="Removing duplicates"; ++ if (having_list) ++ having_list->update_used_tables(); ++ if (remove_duplicates(this, exec_tmp_table, fields_list, having_list)) ++ DBUG_VOID_RETURN; ++ having_list=0; + select_distinct=0; + } ++ exec_tmp_table->reginfo.lock_type=TL_UNLOCK; ++ if (make_simple_join(this, exec_tmp_table)) ++ DBUG_VOID_RETURN; ++ calc_group_buffer(this, group_list); ++ count_field_types(&tmp_table_param, all_fields, 0); + } + if (procedure) + { ++ if (procedure->change_columns(fields_list) || ++ result->prepare(fields_list, select_lex->first_in_union)) ++ DBUG_VOID_RETURN; ++ count_field_types(&tmp_table_param,all_fields,0); + } ++ if (group || tmp_table_param.sum_func_count || + (procedure && (procedure->flags & PROC_GROUP))) + { ++ alloc_group_fields(this, group_list); ++ setup_copy_fields(thd, &tmp_table_param,all_fields); ++ if (make_sum_func_list(this, all_fields) || thd->fatal_error) ++ DBUG_VOID_RETURN; + } ++ if (group_list || order) + { + DBUG_PRINT("info",("Sorting for send_fields")); + thd->proc_info="Sorting result"; + /* If we have already done the group, add HAVING to sorted table */ ++ if (having_list && ! group_list && ! sort_and_group) + { ++ having_list->update_used_tables(); // Some tables may have been const ++ JOIN_TAB *table=&join_tab[const_tables]; ++ table_map used_tables= const_table_map | table->table->map; + ++ Item* sort_table_cond=make_cond_for_table(having_list, used_tables, ++ used_tables); + if (sort_table_cond) + { + if (!table->select) + if (!(table->select=new SQL_SELECT)) ++ DBUG_VOID_RETURN; + if (!table->select->cond) + table->select->cond=sort_table_cond; + else // This should never happen + if (!(table->select->cond=new Item_cond_and(table->select->cond, + sort_table_cond))) ++ DBUG_VOID_RETURN; + table->select_cond=table->select->cond; + DBUG_EXECUTE("where",print_where(table->select->cond, + "select and having");); ++ having_list= make_cond_for_table(having_list, ~ (table_map) 0, ++ ~used_tables); + DBUG_EXECUTE("where",print_where(conds,"having after sort");); + } + } ++ if (create_sort_index(&join_tab[const_tables], ++ group_list ? group_list : order, ++ (having_list || group_list || ++ const_tables != tables - 1 || ++ (select_options & OPTION_FOUND_ROWS)) ? ++ HA_POS_ERROR : ++ select_lex->first_in_union->select_limit_cnt)) ++ DBUG_VOID_RETURN; + } ++ having=having_list; // Actually a parameter + thd->proc_info="Sending data"; ++ error=do_select(this, &fields_list, NULL, procedure); ++ DBUG_VOID_RETURN; ++ } + ++ /* ++ Clean up join. Return error that hold JOIN. ++ */ ++ ++ int ++ JOIN::cleanup(THD *thd) ++ { ++ lock=0; // It's faster to unlock later ++ join_free(this); ++ if (exec_tmp_table) ++ free_tmp_table(thd, exec_tmp_table); + delete select; + delete_dynamic(&keyuse); + delete procedure; ++ return error; ++ } ++ ++ int ++ mysql_select(THD *thd,TABLE_LIST *tables,List &fields,COND *conds, ++ ORDER *order, ORDER *group,Item *having,ORDER *proc_param, ++ ulong select_options,select_result *result) ++ { ++ JOIN *join = new JOIN(thd, fields, select_options, result); ++ ++ DBUG_ENTER("mysql_select"); ++ thd->proc_info="init"; ++ thd->used_tables=0; // Updated by setup_fields ++ ++ if (join->prepare(tables, conds, order, group, having, proc_param, ++ &(thd->lex.select_lex))) ++ { ++ DBUG_RETURN(-1); ++ } ++ switch(join->optimize()) ++ { ++ case 1: ++ DBUG_RETURN(join->error); ++ case -1: ++ goto err; ++ } ++ ++ if(join->global_optimize()) ++ goto err; ++ ++ join->exec(); ++ ++ err: ++ thd->limit_found_rows = join->send_records; ++ thd->examined_row_count = join->examined_rows; ++ thd->proc_info="end"; ++ int error= join->cleanup(thd); ++ delete join; + DBUG_RETURN(error); + } + +*************** +*** 2480,2486 **** + + if ((tab->keys & ~ tab->const_keys && i > 0) || + (tab->const_keys && i == join->const_tables && +- join->thd->select_limit < join->best_positions[i].records_read && + !(join->select_options & OPTION_FOUND_ROWS))) + { + /* Join with outer join condition */ +--- 2575,2582 ---- + + if ((tab->keys & ~ tab->const_keys && i > 0) || + (tab->const_keys && i == join->const_tables && ++ join->select_lex->first_in_union->select_limit_cnt < ++ join->best_positions[i].records_read && + !(join->select_options & OPTION_FOUND_ROWS))) + { + /* Join with outer join condition */ +*************** +*** 2491,2497 **** + (join->select_options & + OPTION_FOUND_ROWS ? + HA_POS_ERROR : +- join->thd->select_limit)) < 0) + DBUG_RETURN(1); // Impossible range + sel->cond=orig_cond; + } +--- 2587,2593 ---- + (join->select_options & + OPTION_FOUND_ROWS ? + HA_POS_ERROR : ++ join->select_lex->first_in_union->select_limit_cnt)) < 0) + DBUG_RETURN(1); // Impossible range + sel->cond=orig_cond; + } +*************** +*** 2933,2939 **** + static int + return_zero_rows(select_result *result,TABLE_LIST *tables,List &fields, + bool send_row, uint select_options,const char *info, +- Item *having, Procedure *procedure) + { + DBUG_ENTER("return_zero_rows"); + +--- 3029,3035 ---- + static int + return_zero_rows(select_result *result,TABLE_LIST *tables,List &fields, + bool send_row, uint select_options,const char *info, ++ Item *having, Procedure *procedure, SELECT_LEX *select_lex) + { + DBUG_ENTER("return_zero_rows"); + +*************** +*** 2944,2950 **** + } + if (procedure) + { +- if (result->prepare(fields)) // This hasn't been done yet + DBUG_RETURN(-1); + } + if (send_row) +--- 3040,3047 ---- + } + if (procedure) + { ++ if (result->prepare(fields, ++ select_lex->first_in_union))//This hasn't been done yet + DBUG_RETURN(-1); + } + if (send_row) +*************** +*** 3479,3485 **** + TABLE * + create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, + ORDER *group, bool distinct, bool save_sum_fields, +- bool allow_distinct_limit, ulong select_options) + { + TABLE *table; + uint i,field_count,reclength,null_count,null_pack_length, +--- 3576,3583 ---- + TABLE * + create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, + ORDER *group, bool distinct, bool save_sum_fields, ++ bool allow_distinct_limit, ulong select_options, ++ SELECT_LEX *first_select) + { + TABLE *table; + uint i,field_count,reclength,null_count,null_pack_length, +*************** +*** 3850,3857 **** + test(null_pack_length)); + if (allow_distinct_limit) + { +- set_if_smaller(table->max_rows,thd->select_limit); +- param->end_write_records=thd->select_limit; + } + else + param->end_write_records= HA_POS_ERROR; +--- 3948,3955 ---- + test(null_pack_length)); + if (allow_distinct_limit) + { ++ set_if_smaller(table->max_rows,first_select->select_limit_cnt); ++ param->end_write_records=first_select->select_limit_cnt; + } + else + param->end_write_records= HA_POS_ERROR; +*************** +*** 4896,4902 **** + error=join->result->send_data(*join->fields); + if (error) + DBUG_RETURN(-1); /* purecov: inspected */ +- if (++join->send_records >= join->thd->select_limit && join->do_send_rows) + { + if (join->select_options & OPTION_FOUND_ROWS) + { +--- 4994,5002 ---- + error=join->result->send_data(*join->fields); + if (error) + DBUG_RETURN(-1); /* purecov: inspected */ ++ if (++join->send_records >= ++ join->select_lex->first_in_union->select_limit_cnt && ++ join->do_send_rows) + { + if (join->select_options & OPTION_FOUND_ROWS) + { +*************** +*** 4912,4918 **** + else + { + join->do_send_rows=0; +- join->thd->select_limit = HA_POS_ERROR; + DBUG_RETURN(0); + } + } +--- 5012,5018 ---- + else + { + join->do_send_rows=0; ++ join->select_lex->first_in_union->select_limit_cnt = HA_POS_ERROR; + DBUG_RETURN(0); + } + } +*************** +*** 4973,4985 **** + DBUG_RETURN(-1); /* purecov: inspected */ + if (end_of_records) + DBUG_RETURN(0); +- if (!error && ++join->send_records >= join->thd->select_limit && + join->do_send_rows) + { + if (!(join->select_options & OPTION_FOUND_ROWS)) + DBUG_RETURN(-3); // Abort nicely + join->do_send_rows=0; +- join->thd->select_limit = HA_POS_ERROR; + } + } + } +--- 5073,5086 ---- + DBUG_RETURN(-1); /* purecov: inspected */ + if (end_of_records) + DBUG_RETURN(0); ++ if (!error && ++join->send_records >= ++ join->select_lex->first_in_union->select_limit_cnt && + join->do_send_rows) + { + if (!(join->select_options & OPTION_FOUND_ROWS)) + DBUG_RETURN(-3); // Abort nicely + join->do_send_rows=0; ++ join->select_lex->first_in_union->select_limit_cnt = HA_POS_ERROR; + } + } + } +*************** +*** 5060,5066 **** + if (!(join->select_options & OPTION_FOUND_ROWS)) + DBUG_RETURN(-3); + join->do_send_rows=0; +- join->thd->select_limit = HA_POS_ERROR; + DBUG_RETURN(0); + } + } +--- 5161,5167 ---- + if (!(join->select_options & OPTION_FOUND_ROWS)) + DBUG_RETURN(-3); + join->do_send_rows=0; ++ join->select_lex->first_in_union->select_limit_cnt = HA_POS_ERROR; + DBUG_RETURN(0); + } + } +*************** +*** 5743,5749 **** + + if (!field_count) + { // only const items +- join->thd->select_limit=1; // Only send first row + DBUG_RETURN(0); + } + Field **first_field=entry->field+entry->fields - field_count; +--- 5844,5850 ---- + + if (!field_count) + { // only const items ++ join->select_lex->first_in_union->select_limit_cnt=1;// Only send first row + DBUG_RETURN(0); + } + Field **first_field=entry->field+entry->fields - field_count; diff --git a/sql/sql_select.h b/sql/sql_select.h index 5466974f587..ba27c5b4b3b 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -149,8 +149,7 @@ class TMP_TABLE_PARAM { } }; - -class JOIN { +class JOIN :public Sql_alloc{ public: JOIN_TAB *join_tab,**best_ref,**map2table; TABLE **table,**all_tables,*sort_by_table; @@ -175,6 +174,70 @@ class JOIN { MYSQL_LOCK *lock; // unit structure (with global parameters) for this select SELECT_LEX_UNIT *unit; + // select that processed + SELECT_LEX *select_lex; + + bool select_distinct, //Is select distinct? + no_order, simple_order, simple_group, + skip_sort_order, need_tmp, + hidden_group_fields, + buffer_result; + DYNAMIC_ARRAY keyuse; + Item::cond_result cond_value; + List all_fields; + List & fields_list; // hold field list passed to mysql_select + int error; + + ORDER *order, *group_list, *proc_param; //hold parameters of mysql_select + COND *conds; // ---"--- + TABLE_LIST *tables_list; //hold 'tables' parameter of mysql_selec + SQL_SELECT *select; //created in optimisation phase + TABLE *exec_tmp_table; //used in 'exec' to hold temporary + + my_bool test_function_query; // need to return select items 1 row + const char *zero_result_cause; // not 0 if exec must return zero result + + JOIN(THD *thd, List &fields, + ulong select_options, select_result *result): + join_tab(0), + table(0), + tables(0), const_tables(0), + sort_and_group(0), first_record(0), + do_send_rows(1), + send_records(0), found_records(0), examined_rows(0), + thd(thd), + sum_funcs(0), + having(0), + select_options(select_options), + result(result), + lock(thd->lock), + select_lex(0), //for safety + select_distinct(test(select_options & SELECT_DISTINCT)), + no_order(0), simple_order(0), simple_group(0), skip_sort_order(0), + need_tmp(0), + hidden_group_fields (0), /*safety*/ + buffer_result(test(select_options & OPTION_BUFFER_RESULT) && + !test(select_options & OPTION_FOUND_ROWS)), + all_fields(fields), + fields_list(fields), + select(0), + exec_tmp_table(0), + test_function_query(0), + zero_result_cause(0) + { + fields_list = fields; + bzero((char*) &keyuse,sizeof(keyuse)); + tmp_table_param.copy_field=0; + tmp_table_param.end_write_records= HA_POS_ERROR; + } + + int prepare(TABLE_LIST *tables, + COND *conds, ORDER *order, ORDER *group, Item *having, + ORDER *proc_param, SELECT_LEX *select, SELECT_LEX_UNIT *unit); + int optimize(); + int global_optimize(); + void exec(); + int cleanup(THD *thd); }; diff --git a/sql/sql_select.h.rej b/sql/sql_select.h.rej new file mode 100644 index 00000000000..07b1c4403f9 --- /dev/null +++ b/sql/sql_select.h.rej @@ -0,0 +1,96 @@ +*************** +*** 173,178 **** + select_result *result; + TMP_TABLE_PARAM tmp_table_param; + MYSQL_LOCK *lock; + }; + + +--- 172,240 ---- + select_result *result; + TMP_TABLE_PARAM tmp_table_param; + MYSQL_LOCK *lock; ++ ++ bool select_distinct, //Is select distinct? ++ no_order, simple_order, simple_group, ++ skip_sort_order, need_tmp, ++ hidden_group_fields, ++ buffer_result; ++ DYNAMIC_ARRAY keyuse; ++ Item::cond_result cond_value; ++ List all_fields; ++ List & fields_list; // hold field list passed to mysql_select ++ int error; ++ ++ ORDER *order, *group_list, *proc_param; //hold parameters of mysql_select ++ COND *conds; // ---"--- ++ TABLE_LIST *tables_list; //hold 'tables' parameter of mysql_select ++ SQL_SELECT *select; //created in optimisation phase ++ TABLE *exec_tmp_table; //used in 'exec' to hold temporary table ++ SELECT_LEX *select_lex; //corresponding select_lex ++ ++ my_bool test_function_query; // need to return select items 1 row ++ const char *zero_result_cause; // not 0 if exec must return zero result ++ ++ JOIN(THD *thd, List &fields, ++ ulong select_options, select_result *result): ++ join_tab(0), ++ table(0), ++ tables(0), const_tables(0), ++ sort_and_group(0), first_record(0), ++ do_send_rows(1), ++ send_records(0), found_records(0), examined_rows(0), ++ thd(thd), ++ sum_funcs(0), ++ having(0), ++ select_options(select_options), ++ result(result), ++ lock(thd->lock), ++ select_distinct(test(select_options & SELECT_DISTINCT)), ++ no_order(0), simple_order(0), simple_group(0), skip_sort_order(0), ++ need_tmp(0), ++ hidden_group_fields (0), /*safety*/ ++ buffer_result(test(select_options & OPTION_BUFFER_RESULT) && ++ !test(select_options & OPTION_FOUND_ROWS)), ++ all_fields(fields), ++ fields_list(fields), ++ select(0), ++ exec_tmp_table(0), ++ select_lex(0), //for safety ++ test_function_query(0), ++ zero_result_cause(0) ++ { ++ fields_list = fields; ++ bzero((char*) &keyuse,sizeof(keyuse)); ++ tmp_table_param.copy_field=0; ++ tmp_table_param.end_write_records= HA_POS_ERROR; ++ } ++ ++ int prepare(TABLE_LIST *tables, ++ COND *conds, ORDER *order, ORDER *group, Item *having, ++ ORDER *proc_param, SELECT_LEX *select); ++ int optimize(); ++ int global_optimize(); ++ void exec(); ++ int cleanup(THD *thd); + }; + + +*************** +*** 187,193 **** + bool store_val_in_field(Field *field,Item *val); + TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, + ORDER *group, bool distinct, bool save_sum_fields, +- bool allow_distinct_limit, ulong select_options); + void free_tmp_table(THD *thd, TABLE *entry); + void count_field_types(TMP_TABLE_PARAM *param, List &fields, + bool reset_with_sum_func); +--- 249,256 ---- + bool store_val_in_field(Field *field,Item *val); + TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, + ORDER *group, bool distinct, bool save_sum_fields, ++ bool allow_distinct_limit, ulong select_options, ++ SELECT_LEX *first_select); + void free_tmp_table(THD *thd, TABLE *entry); + void count_field_types(TMP_TABLE_PARAM *param, List &fields, + bool reset_with_sum_func); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index ef34af6fe1e..b5db593a234 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -40,17 +40,6 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) DBUG_ENTER("mysql_union"); st_select_lex_node * global; - /* Fix tables 'to-be-unioned-from' list to point at opened tables */ - for (sl= &lex->select_lex; - sl; - sl= (SELECT_LEX *) sl->next) - { - for (TABLE_LIST *cursor= (TABLE_LIST *)sl->table_list.first; - cursor; - cursor=cursor->next) - cursor->table= cursor->table_list->table; - } - /* Global option */ if (((void*)(global= unit->global_parameters)) == ((void*)unit)) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index bd7a5ff3b48..8d981f05a1f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -544,7 +544,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); literal text_literal insert_ident order_ident 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 + using_list subselect subselect_init %type expr_list udf_expr_list when_list ident_list ident_list_arg @@ -612,7 +612,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); table_to_table_list table_to_table opt_table_list opt_as handler_rkey_function handler_read_or_scan single_multi table_wild_list table_wild_one opt_wild union union_list - precision union_option + precision union_option subselect_start subselect_end END_OF_INPUT %type @@ -1547,8 +1547,8 @@ optional_braces: | '(' ')' {} /* all possible expressions */ -expr: expr_expr {$$ = $1; } - | simple_expr {$$ = $1; } +expr: expr_expr { $$= $1; } + | simple_expr { $$= $1; } /* expressions that begin with 'expr' */ expr_expr: @@ -1688,6 +1688,7 @@ simple_expr: | NOT expr %prec NEG { $$= new Item_func_not($2); } | '!' expr %prec NEG { $$= new Item_func_not($2); } | '(' expr ')' { $$= $2; } + | subselect { $$= $1; } | '{' ident expr '}' { $$= $3; } | MATCH ident_list_arg AGAINST '(' expr ')' { Select->ftfunc_list.push_back((Item_func_match *) @@ -3849,3 +3850,30 @@ optional_order_or_limit: union_option: /* empty */ {} | ALL {Lex->union_option=1;} + +subselect: + subselect_start subselect_init + subselect_end + { + $$= $2; + } + +subselect_init: + select_init + { + $$= new Item_subselect(current_thd, Lex->select); + } + +subselect_start: + '(' + { + if (mysql_new_select(Lex, 1)) + YYABORT; + } + +subselect_end: + ')' + { + LEX *lex=Lex; + lex->select = (SELECT_LEX*)lex->select->master->master; + }