diff options
52 files changed, 3316 insertions, 676 deletions
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..64ceea72498 --- /dev/null +++ b/mysql-test/r/subselect.result @@ -0,0 +1,43 @@ +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 where t1.a=t2.a), a from t2; +(select a from t1 where t1.a=t2.a) a +NULL 1 +2 2 +select (select a from t1 where t1.a=t2.b), a from t2; +(select a from t1 where t1.a=t2.b) a +NULL 1 +NULL 2 +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 +select (select a from t3 where a<t2.a*4 order by 1 desc limit 1), a from t2; +(select a from t3 where a<t2.a*4 order by 1 desc limit 1) a +3 1 +7 2 +drop table t1,t2,t3,t4; diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index 7d914d029af..29b925b6746 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -88,7 +88,6 @@ explain (select a,b from t1 limit 2) union all (select a,b from t2 order by a l table type possible_keys key key_len ref rows Extra t1 ALL NULL NULL NULL NULL 4 t2 ALL NULL NULL NULL NULL 4 Using filesort -t1 ALL NULL NULL NULL NULL 4 (select sql_calc_found_rows a,b from t1 limit 2) union all (select a,b from t2 order by a) limit 2; a b 1 a diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test new file mode 100644 index 00000000000..7383608ed9e --- /dev/null +++ b/mysql-test/t/subselect.test @@ -0,0 +1,23 @@ + +#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 where t1.a=t2.a), a from t2; +select (select a from t1 where t1.a=t2.b), a from t2; +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 desc limit 1); +select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1) +union (select * from t4 order by a limit 2) limit 3; +select (select a from t3 where a<t2.a*4 order by 1 desc limit 1), a from t2; +select (select t3.a from t3 where a<8 order by 1 desc limit 1), a from +(select * from t2 where a>1) as tt; +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.cc b/sql/item.cc index d5b9487eea6..8a785ee3902 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -320,7 +320,34 @@ bool Item_field::fix_fields(THD *thd,TABLE_LIST *tables) { Field *tmp; if (!(tmp=find_field_in_tables(thd,this,tables))) - return 1; + { + /* + We can't find table field in table list of current select, + consequently we have to find it in outer subselect(s). + We can't join lists of outer & current select, because of scope + of view rules. For example if both tables (outer & current) have + field 'field' it is not mistake to refer to this field without + mention of table name, but if we join tables in one list it will + cause error ER_NON_UNIQ_ERROR in find_field_in_tables. + */ + for (SELECT_LEX *sl= thd->lex.select->outer_select(); + sl && !tmp; + sl= sl->outer_select()) + tmp=find_field_in_tables(thd, this, + (TABLE_LIST*)sl->table_list.first); + if (!tmp) + return 1; + else + if( !thd->lex.select->depended ) + { + thd->lex.select->depended= 1; //Select is depended of outer select(s) + //Tables will be reopened many times + for (TABLE_LIST *tbl= (TABLE_LIST*)thd->lex.select->table_list.first; + tbl; + tbl= tbl->next) + tbl->shared= 1; + } + } set_field(tmp); } else if (thd && thd->set_query_id && field->query_id != thd->query_id) diff --git a/sql/item.h b/sql/item.h index 927e86398c2..f9879f2f70d 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 8627ecd0d73..03ef65c352a 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -37,8 +37,12 @@ public: void left_right_max_length(); 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, default_charset_info); + 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, + default_charset_info); } }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc new file mode 100644 index 00000000000..72bbbcba5a7 --- /dev/null +++ b/sql/item_subselect.cc @@ -0,0 +1,157 @@ +/* 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) + +*/ + +#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), optimized(0), error(0) +{ + DBUG_ENTER("Item_subselect::Item_subselect"); + DBUG_PRINT("subs", ("select_lex 0x%xl", (long) select_lex)); + result= new select_subselect(this); + SELECT_LEX_UNIT *unit= select_lex->master_unit(); + unit->offset_limit_cnt= unit->global_parameters->offset_limit; + unit->select_limit_cnt= unit->global_parameters->select_limit+ + select_lex->offset_limit; + if (unit->select_limit_cnt < unit->global_parameters->select_limit) + unit->select_limit_cnt= HA_POS_ERROR; // no limit + if (unit->select_limit_cnt == HA_POS_ERROR) + select_lex->options&= ~OPTION_FOUND_ROWS; + 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->master_unit())) + return 1; + thd->lex.select= save_select; + return 0; +} + +int Item_subselect::exec() +{ + if (!optimized) + { + optimized=1; + if (join->optimize()) + { + executed= 1; + return (join->error?join->error:1); + } + } + if (join->select_lex->depended && executed) + { + if (join->reinit()) + { + error= 1; + return 1; + } + assign_null(); + executed= 0; + } + 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..e27f14fb83d --- /dev/null +++ b/sql/item_subselect.h @@ -0,0 +1,82 @@ +/* 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: + longlong int_value; + double real_value; + my_bool executed; /* simple subselect is executed */ + my_bool optimized; /* simple subselect is optimized */ + my_bool error; /* error in query */ + 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; + error= item->error; + } + 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/item_sum.cc b/sql/item_sum.cc index 2275ce4eeb6..a1ffae2ed82 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -940,6 +940,7 @@ bool Item_sum_count_distinct::fix_fields(THD *thd,TABLE_LIST *tables) bool Item_sum_count_distinct::setup(THD *thd) { List<Item> list; + SELECT_LEX *select_lex= current_lex->select; /* Create a table with an unique key over all parameters */ for (uint i=0; i < arg_count ; i++) { @@ -961,9 +962,10 @@ bool Item_sum_count_distinct::setup(THD *thd) free_tmp_table(thd, table); tmp_table_param->cleanup(); } - if (!(table=create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1, - 0, 0, - current_lex->select->options | thd->options))) + if (!(table= create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1, + 0, 0, + select_lex->options | thd->options, + select_lex->master_unit()))) return 1; table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows table->no_rows=1; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 4c71e845207..1719fe69b17 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -291,7 +291,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list); bool mysql_change_db(THD *thd,const char *name); void mysql_parse(THD *thd,char *inBuf,uint length); void mysql_init_select(LEX *lex); -bool mysql_new_select(LEX *lex); +bool mysql_new_select(LEX *lex, bool move_down); void mysql_init_multi_delete(LEX *lex); void init_max_user_conn(void); void free_max_user_conn(void); @@ -359,9 +359,10 @@ int setup_order(THD *thd,TABLE_LIST *tables, List<Item> &fields, int handle_select(THD *thd, LEX *lex, select_result *result); int mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &list,COND *conds, ORDER *order, ORDER *group,Item *having,ORDER *proc_param, - ulong select_type,select_result *result); -int mysql_union(THD *thd,LEX *lex,select_result *result); -int mysql_derived(THD *thd,LEX *lex,SELECT_LEX *s, TABLE_LIST *t); + ulong select_type,select_result *result, + SELECT_LEX_UNIT *unit); +int mysql_union(THD *thd, LEX *lex,select_result *result); +int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *s, TABLE_LIST *t); Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Item_result_field ***copy_func, Field **from_field, bool group,bool modify_item); 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/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index 105860f0e4b..8e3b6e2a318 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -234,3 +234,5 @@ "Mešanje tabela koje podržavaju transakcije i onih koje ne podržavaju transakcije je iskljuèeno", "Opcija '%s' je upotrebljena dva puta u istom iskazu", "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/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 d5d5c8fa6f9..975d36069f9 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -391,9 +391,9 @@ bool select_send::send_data(List<Item> &items) String *packet= &thd->packet; DBUG_ENTER("send_data"); - if (thd->offset_limit) + if (unit->offset_limit_cnt) { // using limit offset,count - thd->offset_limit--; + unit->offset_limit_cnt--; DBUG_RETURN(0); } packet->length(0); // Reset packet @@ -441,11 +441,12 @@ select_export::~select_export() } int -select_export::prepare(List<Item> &list) +select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u) { char path[FN_REFLEN]; uint option=4; bool blob_flag=0; + unit= u; #ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS option|=1; // Force use of db directory #endif @@ -512,9 +513,9 @@ bool select_export::send_data(List<Item> &items) String tmp(buff,sizeof(buff),default_charset_info),*res; tmp.length(0); - if (thd->offset_limit) + if (unit->offset_limit_cnt) { // using limit offset,count - thd->offset_limit--; + unit->offset_limit_cnt--; DBUG_RETURN(0); } row_count++; @@ -680,9 +681,11 @@ select_dump::~select_dump() } int -select_dump::prepare(List<Item> &list __attribute__((unused))) +select_dump::prepare(List<Item> &list __attribute__((unused)), + SELECT_LEX_UNIT *u) { uint option=4; + unit= u; #ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS option|=1; // Force use of db directory #endif @@ -721,9 +724,9 @@ bool select_dump::send_data(List<Item> &items) Item *item; DBUG_ENTER("send_data"); - if (thd->offset_limit) + if (unit->offset_limit_cnt) { // using limit offset,count - thd->offset_limit--; + unit->offset_limit_cnt--; DBUG_RETURN(0); } if (row_count++ > 1) @@ -773,3 +776,37 @@ 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<Item> &items) +{ + DBUG_ENTER("select_subselect::send_data"); + if (item->executed){ + my_printf_error(ER_SUBSELECT_NO_1_ROW, ER(ER_SUBSELECT_NO_1_ROW), MYF(0)); + DBUG_RETURN(1); + } + if (unit->offset_limit_cnt) + { // using limit offset,count + unit->offset_limit_cnt--; + DBUG_RETURN(0); + } + 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(); + } + item->executed= 1; + DBUG_RETURN(0); +} diff --git a/sql/sql_class.h b/sql/sql_class.h index 5dc761ff811..16c67375d99 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -379,7 +379,7 @@ public: #endif ulonglong next_insert_id,last_insert_id,current_insert_id, limit_found_rows; - ha_rows select_limit,offset_limit,default_select_limit,cuted_fields, + ha_rows default_select_limit,cuted_fields, max_join_size, sent_row_count, examined_row_count; table_map used_tables; UC *user_connect; @@ -548,10 +548,15 @@ void send_error(NET *net,uint sql_errno=0, const char *err=0); class select_result :public Sql_alloc { protected: THD *thd; + SELECT_LEX_UNIT *unit; public: select_result(); virtual ~select_result() {}; - virtual int prepare(List<Item> &list) { return 0; } + virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u) + { + unit= u; + return 0; + } virtual bool send_fields(List<Item> &list,uint flag)=0; virtual bool send_data(List<Item> &items)=0; virtual void initialize_tables (JOIN *join=0) {} @@ -584,7 +589,7 @@ class select_export :public select_result { public: select_export(sql_exchange *ex) :exchange(ex),file(-1),row_count(0L) {} ~select_export(); - int prepare(List<Item> &list); + int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_fields(List<Item> &list, uint flag) { return 0; } bool send_data(List<Item> &items); @@ -603,7 +608,7 @@ public: select_dump(sql_exchange *ex) :exchange(ex),file(-1),row_count(0L) { path[0]=0; } ~select_dump(); - int prepare(List<Item> &list); + int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_fields(List<Item> &list, uint flag) { return 0; } bool send_data(List<Item> &items); @@ -626,7 +631,7 @@ class select_insert :public select_result { info.handle_duplicates=duplic; } ~select_insert(); - int prepare(List<Item> &list); + int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_fields(List<Item> &list, uint flag) { return 0; } bool send_data(List<Item> &items); @@ -655,7 +660,7 @@ public: create_info(create_info_par), lock(0) {} - int prepare(List<Item> &list); + int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_data(List<Item> &values); bool send_eof(); void abort(); @@ -669,7 +674,7 @@ class select_union :public select_result { select_union(TABLE *table_par); ~select_union(); - int prepare(List<Item> &list); + int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_fields(List<Item> &list, uint flag) { return 0; } bool send_data(List<Item> &items); @@ -677,6 +682,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<Item> &list, uint flag) { return 0; }; + bool send_data(List<Item> &items); + bool send_eof() { return 0; }; + + friend class Ttem_subselect; +}; + /* Structs used when sorting */ typedef struct st_sort_field { @@ -703,19 +721,28 @@ class Table_ident :public Sql_alloc { public: LEX_STRING db; LEX_STRING table; - SELECT_LEX *sel; - inline Table_ident(LEX_STRING db_arg,LEX_STRING table_arg,bool force) - :table(table_arg), sel((SELECT_LEX *)0) + SELECT_LEX_UNIT *sel; + inline Table_ident(LEX_STRING db_arg, LEX_STRING table_arg, bool force) + :table(table_arg), sel((SELECT_LEX_UNIT *)0) { if (!force && (current_thd->client_capabilities & CLIENT_NO_SCHEMA)) db.str=0; else db= db_arg; } - inline Table_ident(LEX_STRING table_arg) :table(table_arg), sel((SELECT_LEX *)0) {db.str=0;} - inline Table_ident(SELECT_LEX *s) : sel(s) {db.str=0; table.str=(char *)""; table.length=0;} + inline Table_ident(LEX_STRING table_arg) + :table(table_arg), sel((SELECT_LEX_UNIT *)0) + { + db.str=0; + } + inline Table_ident(SELECT_LEX_UNIT *s) : sel(s) + { + db.str=0; table.str=(char *)""; table.length=0; + } inline void change_db(char *db_name) - { db.str= db_name; db.length=(uint) strlen(db_name); } + { + db.str= db_name; db.length= (uint) strlen(db_name); + } }; // this is needed for user_vars hash @@ -775,7 +802,7 @@ public: multi_delete(THD *thd, TABLE_LIST *dt, thr_lock_type lock_option_arg, uint num_of_tables); ~multi_delete(); - int prepare(List<Item> &list); + int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_fields(List<Item> &list, uint flag) { return 0; } bool send_data(List<Item> &items); @@ -804,7 +831,7 @@ public: enum enum_duplicates handle_duplicates, thr_lock_type lock_option_arg, uint num); ~multi_update(); - int prepare(List<Item> &list); + int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_fields(List<Item> &list, uint flag) { return 0; } bool send_data(List<Item> &items); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index f5a5a684fc0..2e565a59ca0 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -232,9 +232,10 @@ multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt, int -multi_delete::prepare(List<Item> &values) +multi_delete::prepare(List<Item> &values, SELECT_LEX_UNIT *u) { DBUG_ENTER("multi_delete::prepare"); + unit= u; do_delete = true; thd->proc_info="deleting from main table"; diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index af43c5dcd96..fb40a85fd91 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -28,21 +28,25 @@ static const char *any_db="*any*"; // Special symbol for check_access -int mysql_derived(THD *thd, LEX *lex,SELECT_LEX *s, TABLE_LIST *t) +int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, TABLE_LIST *t) { - SELECT_LEX *sl=s; + /* + TODO: make derived tables with union inside (now only 1 SELECT may be + procesed) + */ + SELECT_LEX *sl= unit->first_select(); List<Item> item_list; TABLE *table; int res; select_union *derived_result; - TABLE_LIST *tables=(TABLE_LIST *)sl->table_list.first; + TABLE_LIST *tables= (TABLE_LIST *)sl->table_list.first; TMP_TABLE_PARAM tmp_table_param; DBUG_ENTER("mysql_derived"); if (tables) - res=check_table_access(thd,SELECT_ACL, tables); + res= check_table_access(thd,SELECT_ACL, tables); else - res=check_access(thd, SELECT_ACL, any_db); + res= check_access(thd, SELECT_ACL, any_db); if (res) DBUG_RETURN(-1); @@ -52,7 +56,7 @@ int mysql_derived(THD *thd, LEX *lex,SELECT_LEX *s, TABLE_LIST *t) { if (cursor->derived) { - res=mysql_derived(thd,lex,(SELECT_LEX *)cursor->derived,cursor); + res=mysql_derived(thd, lex, (SELECT_LEX_UNIT *)cursor->derived, cursor); if (res) DBUG_RETURN(res); } } @@ -72,8 +76,10 @@ int mysql_derived(THD *thd, LEX *lex,SELECT_LEX *s, TABLE_LIST *t) bzero((char*) &tmp_table_param,sizeof(tmp_table_param)); tmp_table_param.field_count=item_list.elements; if (!(table=create_tmp_table(thd, &tmp_table_param, item_list, - (ORDER*) 0, 0, 1, 0, - (sl->options | thd->options | TMP_TABLE_ALL_COLUMNS)))) + (ORDER*) 0, 0, 1, 0, + (sl->options | thd->options | + TMP_TABLE_ALL_COLUMNS), + unit))) { res=-1; goto exit; @@ -81,11 +87,11 @@ int mysql_derived(THD *thd, LEX *lex,SELECT_LEX *s, TABLE_LIST *t) if ((derived_result=new select_union(table))) { - thd->offset_limit=sl->offset_limit; - thd->select_limit=sl->select_limit+sl->offset_limit; - if (thd->select_limit < sl->select_limit) - thd->select_limit= HA_POS_ERROR; - if (thd->select_limit == HA_POS_ERROR) + unit->offset_limit_cnt= sl->offset_limit; + unit->select_limit_cnt= sl->select_limit+sl->offset_limit; + if (unit->select_limit_cnt < sl->select_limit) + unit->select_limit_cnt= HA_POS_ERROR; + if (unit->select_limit_cnt == HA_POS_ERROR) sl->options&= ~OPTION_FOUND_ROWS; res=mysql_select(thd, tables, sl->item_list, @@ -93,7 +99,7 @@ int mysql_derived(THD *thd, LEX *lex,SELECT_LEX *s, TABLE_LIST *t) (ORDER*) sl->group_list.first, sl->having, (ORDER*) NULL, sl->options | thd->options | SELECT_NO_UNLOCK, - derived_result); + derived_result, unit); if (!res) { // Here we entirely fix both TABLE_LIST and list of SELECT's as there were no derived tables @@ -103,9 +109,8 @@ int mysql_derived(THD *thd, LEX *lex,SELECT_LEX *s, TABLE_LIST *t) { t->real_name=table->real_name; t->table=table; - sl->prev->next=sl->next; + sl->exclude(); t->derived=(SELECT_LEX *)0; // just in case ... - if (!sl->next) lex->last_select = sl; } } delete derived_result; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 69fc7c00955..2e6c009741e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1266,10 +1266,11 @@ bool delayed_insert::handle_inserts(void) ***************************************************************************/ int -select_insert::prepare(List<Item> &values) +select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) { DBUG_ENTER("select_insert::prepare"); + unit= u; save_time_stamp=table->time_stamp; if (check_insert_fields(thd,table,*fields,values,1)) DBUG_RETURN(1); @@ -1302,9 +1303,9 @@ select_insert::~select_insert() bool select_insert::send_data(List<Item> &values) { - if (thd->offset_limit) + if (unit->offset_limit_cnt) { // using limit offset,count - thd->offset_limit--; + unit->offset_limit_cnt--; return 0; } if (fields->elements) @@ -1380,10 +1381,11 @@ bool select_insert::send_eof() ***************************************************************************/ int -select_create::prepare(List<Item> &values) +select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) { DBUG_ENTER("select_create::prepare"); + unit= u; table=create_table_from_items(thd, create_info, db, name, extra_fields, keys, &values, &lock); if (!table) @@ -1413,9 +1415,9 @@ select_create::prepare(List<Item> &values) bool select_create::send_data(List<Item> &values) { - if (thd->offset_limit) + if (unit->offset_limit_cnt) { // using limit offset,count - thd->offset_limit--; + unit->offset_limit_cnt--; return 0; } fill_record(field,values); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 31ec6d1cecc..008ef44d83a 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -879,3 +879,199 @@ int yylex(void *arg) } } } + +/* + st_select_lex structures initialisations +*/ + +void st_select_lex_node::init_query() +{ + next= master= slave= link_next= 0; + prev= link_prev= 0; +} + +void st_select_lex_node::init_select() +{ + order_list.elements= 0; + order_list.first= 0; + order_list.next= (byte**) &order_list.first; + select_limit= HA_POS_ERROR; + offset_limit= 0; +} + +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; + offset_limit_cnt= 0; +} + +void st_select_lex::init_query() +{ + st_select_lex_node::init_query(); + table_list.elements= 0; + table_list.first= 0; + table_list.next= (byte**) &table_list.first; + item_list.empty(); +} + +void st_select_lex::init_select() +{ + st_select_lex_node::init_select(); + group_list.elements= 0; + group_list.first= 0; + group_list.next= (byte**) &group_list.first; + options= 0; + where= having= 0; + when_list.empty(); + expr_list.empty(); + interval_list.empty(); + use_index.empty(); + ftfunc_list.empty(); + linkage=UNSPECIFIED_TYPE; + depended= 0; +} + +/* + st_select_lex structures linking +*/ + +/* include on level down */ +void st_select_lex_node::include_down(st_select_lex_node *upper) +{ + if ((next= upper->slave)) + next->prev= &next; + prev= &upper->slave; + upper->slave= this; + master= upper; +} + +/* include neighbour (on same level) */ +void st_select_lex_node::include_neighbour(st_select_lex_node *before) +{ + if ((next= before->next)) + next->prev= &next; + prev= &before->next; + before->next= this; + master= before->master; +} + +/* including in global SELECT_LEX list */ +void st_select_lex_node::include_global(st_select_lex_node **plink) +{ + if ((link_next= *plink)) + link_next->link_prev= &link_next; + link_prev= plink; + *plink= this; +} + +//excluding from global list (internal function) +void st_select_lex_node::fast_exclude() +{ + if(link_prev) + { + if ((*link_prev= link_next)) + link_next->link_prev= link_prev; + // Remove slave structure + for (; slave; slave= slave->next) + slave->fast_exclude(); + } +} + +/* + excluding select_lex structure (except first (first select can't be + deleted, because it is most upper select)) +*/ +void st_select_lex_node::exclude() +{ + //exclude from global list + fast_exclude(); + //exclude from other structures + if ((*prev= next)) + next->prev= prev; + /* + We do not need following statements, because prev pointer of first + list element point to master->slave + if (master->slave == this) + master->slave= next; + */ +} + +/* + This is used for UNION & subselect to create a new table list of all used + tables. + The table_list->table entry in all used tables are set to point + to the entries in this list. +*/ + +// interface +bool st_select_lex_unit::create_total_list(THD *thd, st_lex *lex, + TABLE_LIST **result) +{ + *result= 0; + return create_total_list_n_last_return(thd, lex, &result); +} + +// list creator +bool st_select_lex_unit::create_total_list_n_last_return(THD *thd, st_lex *lex, + TABLE_LIST ***result) +{ + TABLE_LIST *slave_list_first=0, **slave_list_last= &slave_list_first; + TABLE_LIST **new_table_list= *result, *aux; + SELECT_LEX *sl= (SELECT_LEX*)slave; + for (; sl; sl= sl->next_select()) + { + // check usage of ORDER BY in union + if (sl->order_list.first && sl->next_select() && !sl->braces) + { + net_printf(&thd->net,ER_WRONG_USAGE,"UNION","ORDER BY"); + return 1; + } + for (SELECT_LEX_UNIT *inner= sl->first_inner_unit(); + inner; + inner= inner->next_unit()) + if (inner->create_total_list_n_last_return(thd, lex, + &slave_list_last)) + return 1; + if ((aux= (TABLE_LIST*) sl->table_list.first)) + { + TABLE_LIST *next; + for (; aux; aux= next) + { + TABLE_LIST *cursor; + next= aux->next; + for (cursor= **result; cursor; cursor= cursor->next) + if (!strcmp(cursor->db, aux->db) && + !strcmp(cursor->real_name, aux->real_name) && + !strcmp(cursor->name, aux->name)) + break; + if (!cursor) + { + /* Add not used table to the total table list */ + aux->lock_type= lex->lock_option; + if (!(cursor= (TABLE_LIST *) thd->memdup((char*) aux, + sizeof(*aux)))) + { + send_error(&thd->net,0); + return 1; + } + *new_table_list= cursor; + new_table_list= &cursor->next; + *new_table_list= 0; // end result list + } + else + aux->shared= 1; // Mark that it's used twice + aux->table_list= cursor; + } + } + } + if (slave_list_first) + { + *new_table_list= slave_list_first; + new_table_list= slave_list_last; + } + *result= new_table_list; + return 0; +} diff --git a/sql/sql_lex.h b/sql/sql_lex.h index e53a2e7bda8..5c113e46a2b 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -103,28 +103,180 @@ typedef struct st_lex_master_info } LEX_MASTER_INFO; -enum sub_select_type {UNSPECIFIED_TYPE,UNION_TYPE, INTERSECT_TYPE, EXCEPT_TYPE, NOT_A_SELECT, DERIVED_TABLE_TYPE}; +enum sub_select_type {UNSPECIFIED_TYPE,UNION_TYPE, INTERSECT_TYPE, + EXCEPT_TYPE, GLOBAL_OPTIONS_TYPE, DERIVED_TABLE_TYPE}; -/* The state of the lex parsing for selects */ +/* + The state of the lex parsing for selects + + All select describing structures linked with following pointers: + - list of neighbors (next/prev) (prev of first element point to slave + pointer of upper structure) + - one level units for unit (union) structure + - member of one union(unit) for ordinary select_lex + - pointer to master + - outer select_lex for unit (union) + - unit structure for ordinary select_lex + - pointer to slave + - first list element of select_lex belonged to this unit for unit + - first unit in list of units that belong to this select_lex (as + subselects or derived tables) for ordinary select_lex + - list of all select_lex (for group operation like correcting list of opened + tables) + for example for following query: -typedef struct st_select_lex { - enum sub_select_type linkage; - char *db,*db1,*table1,*db2,*table2; /* For outer join using .. */ - Item *where,*having; - ha_rows select_limit,offset_limit; + select * + from table1 + where table1.field IN (select * from table1_1_1 union + select * from table1_1_2) + union + select * + from table2 + where table2.field=(select (select f1 from table2_1_1_1_1 + where table2_1_1_1_1.f2=table2_1_1.f3) + from table2_1_1 + where table2_1_1.f1=table2.f2) + union + select * from table3; + + we will have following structure: + + + main unit + select1 select2 select3 + |^^ |^ + s||| ||master + l||| |+---------------------------------+ + a||| +---------------------------------+| + v|||master slave || + e||+-------------------------+ || + V| neighbor | V| + unit 1.1<==================>unit1.2 unit2.1 + select1.1.1 select 1.1.2 select1.2.1 select2.1.1 select2.1.2 + |^ + || + V| + unit2.1.1.1 + select2.1.1.1.1 + + + relation in main unit will be following: + + main unit + |^^^ + |||| + |||+------------------------------+ + ||+--------------+ | + slave||master | | + V| neighbor | neighbor | + select1<========>select2<========>select3 + + list of all select_lex will be following (as it will be constructed by + parser): + + select1->select2->select3->select2.1.1->select 2.1.2->select2.1.1.1.1-+ + | + +---------------------------------------------------------------------+ + | + +->select1.1.1->select1.1.2 + +*/ + +/* + Base class for st_select_lex (SELECT_LEX) & + st_select_lex_unit (SELECT_LEX_UNIT) +*/ +class st_select_lex_node { +protected: + st_select_lex_node *next, **prev, /* neighbor list */ + *master, *slave, /* vertical links */ + *link_next, **link_prev; /* list of whole SELECT_LEX */ +public: ulong options; + enum sub_select_type linkage; + //uint sort_default; + SQL_LIST order_list; /* ORDER clause */ + ha_rows select_limit, offset_limit; /* LIMIT clause parameters */ + void init_query(); + void init_select(); + void include_down(st_select_lex_node *upper); + void include_neighbour(st_select_lex_node *before); + void include_global(st_select_lex_node **plink); + void exclude(); +private: + void fast_exclude(); +}; + +/* + SELECT_LEX_UNIT - unit of selects (UNION, INTERSECT, ...) group + SELECT_LEXs +*/ +class st_lex; +class st_select_lex; +class st_select_lex_unit: public st_select_lex_node { +public: + /* + Pointer to 'last' select or pointer to unit where stored + global parameters for union + */ + st_select_lex_node *global_parameters; + /* LIMIT clause runtime counters */ + ha_rows select_limit_cnt, offset_limit_cnt; + void init_query(); + bool create_total_list(THD *thd, st_lex *lex, TABLE_LIST **result); + st_select_lex* outer_select() { return (st_select_lex*) master; } + st_select_lex* first_select() { return (st_select_lex*) slave; } + st_select_lex_unit* next_unit() { return (st_select_lex_unit*) next; } + + friend void mysql_init_query(THD *thd); +private: + bool create_total_list_n_last_return(THD *thd, st_lex *lex, + TABLE_LIST ***result); +}; +typedef struct st_select_lex_unit SELECT_LEX_UNIT; + +/* + SELECT_LEX - store information of parsed SELECT_LEX statment +*/ +class st_select_lex: public st_select_lex_node { +public: + char *db, *db1, *table1, *db2, *table2; /* For outer join using .. */ + Item *where, *having; /* WHERE & HAVING clauses */ List<List_item> expr_list; - List<List_item> when_list; - SQL_LIST order_list,table_list,group_list; - List<Item> item_list; - List<String> interval_list,use_index, *use_index_ptr, + List<List_item> when_list; /* WHEN clause */ + SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */ + List<Item> item_list; /* list of fields & expressions */ + List<String> interval_list, use_index, *use_index_ptr, ignore_index, *ignore_index_ptr; List<Item_func_match> ftfunc_list; - uint in_sum_expr, sort_default; - bool create_refs, braces; - st_select_lex *next, *prev; -} SELECT_LEX; + uint in_sum_expr; + bool create_refs, + braces, /* SELECT ... UNION (SELECT ... ) <- this braces */ + depended; /* depended from outer select subselect */ + void init_query(); + void init_select(); + st_select_lex_unit* master_unit() { return (st_select_lex_unit*) master; } + st_select_lex_unit* first_inner_unit() + { + return (st_select_lex_unit*) slave; + } + st_select_lex* outer_select() + { + return (st_select_lex*) master_unit()->outer_select(); + } + st_select_lex* next_select() { return (st_select_lex*) next; } + st_select_lex* next_select_in_list() + { + return (st_select_lex*) link_next; + } + st_select_lex_node** next_select_in_list_addr() + { + return &link_next; + } + friend void mysql_init_query(THD *thd); +}; +typedef struct st_select_lex SELECT_LEX; class Set_option :public Sql_alloc { public: @@ -137,13 +289,15 @@ public: :name(par_name), item(par_item), name_length(length), type(par_type) {} }; - /* The state of the lex parsing. This is saved in the THD struct */ typedef struct st_lex { uint yylineno,yytoklen; /* Simulate lex */ LEX_YYSTYPE yylval; - SELECT_LEX select_lex, *select, *last_select; + SELECT_LEX_UNIT unit; /* most upper unit */ + SELECT_LEX select_lex, /* first SELECT_LEX */ + /* current SELECT_LEX in parsing */ + *select; uchar *ptr,*tok_start,*tok_end,*end_of_query; char *length,*dec,*change,*name; char *backup_dir; /* For RESTORE/BACKUP */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 469de136fbb..3492854329a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -65,12 +65,11 @@ static void decrease_user_connections(UC *uc); static bool check_db_used(THD *thd,TABLE_LIST *tables); static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables); static bool check_dup(const char *db, const char *name, TABLE_LIST *tables); -static void mysql_init_query(THD *thd); +void mysql_init_query(THD *thd); static void remove_escape(char *name); static void refresh_status(void); static bool append_file_to_dir(THD *thd, char **filename_ptr, char *table_name); -static bool create_total_list(THD *thd, LEX *lex, TABLE_LIST **result); const char *any_db="*any*"; // Special symbol for check_access @@ -1203,11 +1202,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd, void mysql_execute_command(void) { - int res=0; - THD *thd=current_thd; + int res= 0; + THD *thd= current_thd; LEX *lex= &thd->lex; - TABLE_LIST *tables=(TABLE_LIST*) lex->select_lex.table_list.first; - SELECT_LEX *select_lex = lex->select; + TABLE_LIST *tables= (TABLE_LIST*) lex->select_lex.table_list.first; + SELECT_LEX *select_lex= lex->select; + SELECT_LEX_UNIT *unit= &lex->unit; DBUG_ENTER("mysql_execute_command"); if (thd->slave_thread) @@ -1240,11 +1240,14 @@ mysql_execute_command(void) { for (TABLE_LIST *cursor= tables; cursor; - cursor=cursor->next) - if (cursor->derived && mysql_derived(thd,lex,(SELECT_LEX *)cursor->derived,cursor)) + cursor= cursor->next) + if (cursor->derived && mysql_derived(thd, lex, + (SELECT_LEX_UNIT *)cursor->derived, + cursor)) DBUG_VOID_RETURN; } - if ((lex->select_lex.next && create_total_list(thd,lex,&tables)) || + if ((lex->select_lex.next_select_in_list() && + lex->unit.create_total_list(thd, lex, &tables)) || (table_rules_on && tables && thd->slave_thread && !tables_ok(thd,tables))) DBUG_VOID_RETURN; @@ -1272,11 +1275,12 @@ mysql_execute_command(void) break; // Error message is given } - thd->offset_limit=select_lex->offset_limit; - thd->select_limit=select_lex->select_limit+select_lex->offset_limit; - if (thd->select_limit < select_lex->select_limit) - thd->select_limit= HA_POS_ERROR; // no limit - if (thd->select_limit == HA_POS_ERROR) + unit->offset_limit_cnt= unit->global_parameters->offset_limit; + unit->select_limit_cnt= unit->global_parameters->select_limit+ + unit->global_parameters->offset_limit; + if (unit->select_limit_cnt < unit->global_parameters->select_limit) + unit->select_limit_cnt= HA_POS_ERROR; // no limit + if (unit->select_limit_cnt == HA_POS_ERROR) select_lex->options&= ~OPTION_FOUND_ROWS; if (lex->exchange) @@ -1501,10 +1505,11 @@ mysql_execute_command(void) for (table = tables->next ; table ; table=table->next) table->lock_type= lex->lock_option; } - thd->offset_limit=select_lex->offset_limit; - thd->select_limit=select_lex->select_limit+select_lex->offset_limit; - if (thd->select_limit < select_lex->select_limit) - thd->select_limit= HA_POS_ERROR; // No limit + unit->offset_limit_cnt= select_lex->offset_limit; + unit->select_limit_cnt= select_lex->select_limit+ + select_lex->offset_limit; + if (unit->select_limit_cnt < select_lex->select_limit) + unit->select_limit_cnt= HA_POS_ERROR; // No limit /* Skip first table, which is the table we are creating */ lex->select_lex.table_list.first= @@ -1786,13 +1791,13 @@ mysql_execute_command(void) while ((item=value_list++)) total_list.push_back(item); - res=mysql_select(thd,tables,total_list, - select_lex->where, - (ORDER *)NULL,(ORDER *)NULL,(Item *)NULL, - (ORDER *)NULL, - select_lex->options | thd->options | - SELECT_NO_JOIN_CACHE, - result); + res= mysql_select(thd, tables, total_list, + select_lex->where, + (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL, + (ORDER *)NULL, + select_lex->options | thd->options | + SELECT_NO_JOIN_CACHE, + result, unit); delete result; } else @@ -1842,10 +1847,10 @@ mysql_execute_command(void) } select_result *result; - thd->offset_limit=select_lex->offset_limit; - thd->select_limit=select_lex->select_limit+select_lex->offset_limit; - if (thd->select_limit < select_lex->select_limit) - thd->select_limit= HA_POS_ERROR; // No limit + unit->offset_limit_cnt= select_lex->offset_limit; + unit->select_limit_cnt= select_lex->select_limit+select_lex->offset_limit; + if (unit->select_limit_cnt < select_lex->select_limit) + unit->select_limit_cnt= HA_POS_ERROR; // No limit if (check_dup(tables->db, tables->real_name, tables->next)) { @@ -1938,7 +1943,7 @@ mysql_execute_command(void) goto error; } auxi->lock_type=walk->lock_type=TL_WRITE; - auxi->table= (TABLE *) walk; // Remember corresponding table + auxi->table_list= walk; // Remember corresponding table } tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); if (add_item_to_list(new Item_null())) @@ -1951,7 +1956,7 @@ mysql_execute_command(void) break; /* Fix tables-to-be-deleted-from list to point at opened tables */ for (auxi=(TABLE_LIST*) aux_tables ; auxi ; auxi=auxi->next) - auxi->table= ((TABLE_LIST*) auxi->table)->table; + auxi->table= auxi->table_list->table; if (!thd->fatal_error && (result=new multi_delete(thd,aux_tables, lex->lock_option,table_count))) { @@ -1961,7 +1966,7 @@ mysql_execute_command(void) (ORDER *)NULL, select_lex->options | thd->options | SELECT_NO_JOIN_CACHE, - result); + result, unit); delete result; } else @@ -2660,22 +2665,25 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, int *yystacksize) Initialize global thd variables needed for query ****************************************************************************/ -static void +void mysql_init_query(THD *thd) { DBUG_ENTER("mysql_init_query"); - thd->lex.select_lex.item_list.empty(); + thd->lex.unit.init_query(); + thd->lex.unit.init_select(); + thd->lex.select_lex.init_query(); + thd->lex.unit.slave= &thd->lex.select_lex; + thd->lex.unit.global_parameters= &thd->lex.select_lex; //Global limit & order + thd->lex.select_lex.master= &thd->lex.unit; + thd->lex.select_lex.prev= &thd->lex.unit.slave; thd->lex.value_list.empty(); - thd->lex.select_lex.table_list.elements=0; - thd->free_list=0; thd->lex.union_option=0; - thd->lex.select = thd->lex.last_select = &thd->lex.select_lex; - thd->lex.select_lex.table_list.first=0; - thd->lex.select_lex.table_list.next= (byte**) &thd->lex.select_lex.table_list.first; - thd->lex.select_lex.next=0; - thd->fatal_error=0; // Safety - thd->last_insert_id_used=thd->query_start_used=thd->insert_id_used=0; - thd->sent_row_count=thd->examined_row_count=0; - thd->safe_to_cache_query=1; + thd->free_list= 0; + thd->lex.union_option= 0; + thd->lex.select= &thd->lex.select_lex; + thd->fatal_error= 0; // Safety + thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0; + thd->sent_row_count= thd->examined_row_count= 0; + thd->safe_to_cache_query= 1; DBUG_VOID_RETURN; } @@ -2683,53 +2691,53 @@ void mysql_init_select(LEX *lex) { SELECT_LEX *select_lex = lex->select; - select_lex->where=select_lex->having=0; + select_lex->init_select(); select_lex->select_limit=lex->thd->default_select_limit; select_lex->offset_limit=0; - select_lex->options=0; - select_lex->linkage=UNSPECIFIED_TYPE; lex->exchange = 0; lex->proc_list.first=0; - select_lex->order_list.elements=select_lex->group_list.elements=0; - select_lex->order_list.first=0; - select_lex->order_list.next= (byte**) &select_lex->order_list.first; - select_lex->group_list.first=0; - select_lex->group_list.next= (byte**) &select_lex->group_list.first; - select_lex->next = select_lex->prev = (SELECT_LEX *)NULL; } bool -mysql_new_select(LEX *lex) +mysql_new_select(LEX *lex, bool move_down) { SELECT_LEX *select_lex = (SELECT_LEX *) lex->thd->calloc(sizeof(SELECT_LEX)); if (!select_lex) return 1; - lex->select=lex->last_select; - lex->select->next=select_lex; - lex->select=lex->last_select=select_lex; - select_lex->table_list.next= (byte**) &select_lex->table_list.first; - select_lex->item_list.empty(); - select_lex->when_list.empty(); - select_lex->expr_list.empty(); - select_lex->interval_list.empty(); - select_lex->use_index.empty(); - select_lex->ftfunc_list.empty(); + select_lex->init_query(); + select_lex->init_select(); + if (move_down) + { + /* first select_lex of subselect or derived table */ + SELECT_LEX_UNIT *unit= + (SELECT_LEX_UNIT *) lex->thd->calloc(sizeof(SELECT_LEX_UNIT)); + if (!unit) + return 1; + unit->init_query(); + unit->init_select(); + unit->include_down(lex->select); + select_lex->include_down(unit); + } + else + select_lex->include_neighbour(lex->select); + + select_lex->master_unit()->global_parameters= select_lex; + select_lex->include_global(lex->select->next_select_in_list_addr()); + lex->select= select_lex; return 0; } void mysql_init_multi_delete(LEX *lex) { - lex->sql_command = SQLCOM_DELETE_MULTI; + lex->sql_command= SQLCOM_DELETE_MULTI; mysql_init_select(lex); - lex->select->select_limit=HA_POS_ERROR; - lex->auxilliary_table_list=lex->select_lex.table_list; - lex->select->table_list.elements=0; - lex->select->table_list.first=0; - lex->select->table_list.next= (byte**) &(lex->select->table_list.first); + lex->select->select_limit= HA_POS_ERROR; + lex->auxilliary_table_list= lex->select_lex.table_list; + lex->select->init_query(); } void -mysql_parse(THD *thd,char *inBuf,uint length) +mysql_parse(THD *thd, char *inBuf, uint length) { DBUG_ENTER("mysql_parse"); @@ -3160,7 +3168,7 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, ptr->real_name_length=table->table.length; ptr->lock_type=flags; ptr->updating=updating; - ptr->derived=(SELECT_LEX *)table->sel; + ptr->derived= (SELECT_LEX_UNIT *) table->sel; if (use_index) ptr->use_index=(List<String> *) thd->memdup((gptr) use_index, sizeof(*use_index)); @@ -3187,68 +3195,6 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, } -/* -** This is used for UNION to create a new table list of all used tables -** The table_list->table entry in all used tables are set to point -** to the entries in this list. -*/ - -static bool create_total_list(THD *thd, LEX *lex, TABLE_LIST **result) -{ - /* Handle the case when we are not using union */ - if (!lex->select_lex.next) - { - *result= (TABLE_LIST*) lex->select_lex.table_list.first; - return 0; - } - - SELECT_LEX *sl; - TABLE_LIST **new_table_list= result, *aux; - - *new_table_list=0; // end result list - for (sl= &lex->select_lex; sl; sl=sl->next) - { - if (sl->order_list.first && sl->next && !sl->braces) - { - net_printf(&thd->net,ER_WRONG_USAGE,"UNION","ORDER BY"); - return 1; - } - if ((aux= (TABLE_LIST*) sl->table_list.first)) - { - TABLE_LIST *next; - for (; aux; aux=next) - { - TABLE_LIST *cursor; - next= aux->next; - for (cursor= *result; cursor; cursor=cursor->next) - if (!strcmp(cursor->db,aux->db) && - !strcmp(cursor->real_name,aux->real_name) && - !strcmp(cursor->name, aux->name)) - break; - if (!cursor) - { - /* Add not used table to the total table list */ - aux->lock_type= lex->lock_option; - if (!(cursor = (TABLE_LIST *) thd->memdup((char*) aux, - sizeof(*aux)))) - { - send_error(&thd->net,0); - return 1; - } - *new_table_list= cursor; - new_table_list= &cursor->next; - *new_table_list=0; // end result list - } - else - aux->shared=1; // Mark that it's used twice - aux->table=(TABLE *) cursor; - } - } - } - return 0; -} - - void add_join_on(TABLE_LIST *b,Item *expr) { if (!b->on_expr) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c539afb00c1..4748e857276 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -65,7 +65,8 @@ static ORDER *remove_const(JOIN *join,ORDER *first_order,COND *cond, static int return_zero_rows(select_result *res,TABLE_LIST *tables, List<Item> &fields, bool send_row, uint select_options, const char *info, - Item *having, Procedure *proc); + Item *having, Procedure *proc, + SELECT_LEX_UNIT *unit); 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); @@ -155,7 +156,20 @@ int handle_select(THD *thd, LEX *lex, select_result *result) { int res; register SELECT_LEX *select_lex = &lex->select_lex; - if (select_lex->next) + if (select_lex->next_select_in_list()) + { + /* Fix tables 'to-be-unioned-from' list to point at opened tables */ + for (SELECT_LEX *sl= select_lex; + sl; + sl= sl->next_select_in_list()) + { + for (TABLE_LIST *cursor= (TABLE_LIST *)sl->table_list.first; + cursor; + cursor=cursor->next) + cursor->table= cursor->table_list->table; + } + } + if (select_lex->next_select()) res=mysql_union(thd,lex,result); else res=mysql_select(thd,(TABLE_LIST*) select_lex->table_list.first, @@ -166,7 +180,7 @@ int handle_select(THD *thd, LEX *lex, select_result *result) select_lex->having, (ORDER*) lex->proc_list.first, select_lex->options | thd->options, - result); + result, &(lex->unit)); if (res && result) result->abort(); delete result; @@ -179,49 +193,43 @@ 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<Item> &fields,COND *conds, - ORDER *order, ORDER *group,Item *having,ORDER *proc_param, - ulong select_options,select_result *result) +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<Item> 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; + union_part= (unit->first_select()->next_select() != 0); + /* 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); @@ -234,13 +242,11 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &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<Item> it(fields); + List_iterator_fast<Item> it(fields_list); Item *item; while ((item= it++)) { @@ -256,22 +262,23 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &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 */ @@ -280,7 +287,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &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; @@ -296,51 +303,53 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &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 : - thd->select_limit); + 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)) + 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"); #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 @@ -349,110 +358,87 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &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 || !thd->select_limit) + 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); - 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); - 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"); + if (union_part) + 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); + + if (!tables_list) + { + test_function_query= 1; + DBUG_RETURN(0); } - error = -1; - join.sort_by_table=get_sort_by_table(order,group,tables); + 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); + + if (select_lex->depended) + { + /* + Just remove all const-table optimization in case of depended query + TODO: optimize + */ + const_table_map= 0; + const_tables= 0; + found_const_table_map= 0; + } + 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); - 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++) { @@ -464,97 +450,94 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &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 */ - } - 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; + 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(&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 && - (thd->select_limit == HA_POS_ERROR || - (join.select_options & OPTION_FOUND_ROWS) || + else if (select_distinct && tables - const_tables == 1 && + (unit->select_limit_cnt == HA_POS_ERROR || + (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)))) + 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 @@ -563,60 +546,149 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &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, - thd->select_limit,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 optimization (with subselect) must be here (TODO) +*/ + +int +JOIN::global_optimize() +{ + return 0; +} + +int +JOIN::reinit() +{ + DBUG_ENTER("JOIN::reinit"); + //TODO move to unit reinit + unit->offset_limit_cnt =select_lex->offset_limit; + unit->select_limit_cnt =select_lex->select_limit+select_lex->offset_limit; + if (unit->select_limit_cnt < select_lex->select_limit) + unit->select_limit_cnt= HA_POS_ERROR; // no limit + if (unit->select_limit_cnt == HA_POS_ERROR) + select_lex->options&= ~OPTION_FOUND_ROWS; + + if (setup_tables(tables_list)) + DBUG_RETURN(1); + DBUG_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 (union_part) + 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 && union_part) + 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 : thd->select_limit,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 */ @@ -628,44 +700,45 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &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))) - 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; } } @@ -676,58 +749,58 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &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], - order, thd->select_limit,0); + (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<Item> *) 0,tmp_table,0))) + thd->proc_info= "Copying to tmp table"; + if ((tmp_error= do_select(this, (List<Item> *) 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; } /* @@ -738,153 +811,196 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &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))) - 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<Item> *) 0,tmp_table2,0))) + if (make_sum_func_list(this, all_fields) || + (tmp_error=do_select(this, (List<Item> *) 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)) - 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 : thd->select_limit)) - 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<Item> &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); } @@ -2480,7 +2596,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) 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->unit->select_limit_cnt < + join->best_positions[i].records_read && !(join->select_options & OPTION_FOUND_ROWS))) { /* Join with outer join condition */ @@ -2491,7 +2608,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) (join->select_options & OPTION_FOUND_ROWS ? HA_POS_ERROR : - join->thd->select_limit)) < 0) + join->unit->select_limit_cnt)) < 0) DBUG_RETURN(1); // Impossible range sel->cond=orig_cond; } @@ -2533,7 +2650,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); @@ -2929,7 +3046,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, bool *simple_order) static int return_zero_rows(select_result *result,TABLE_LIST *tables,List<Item> &fields, bool send_row, uint select_options,const char *info, - Item *having, Procedure *procedure) + Item *having, Procedure *procedure, SELECT_LEX_UNIT *unit) { DBUG_ENTER("return_zero_rows"); @@ -2940,7 +3057,7 @@ return_zero_rows(select_result *result,TABLE_LIST *tables,List<Item> &fields, } if (procedure) { - if (result->prepare(fields)) // This hasn't been done yet + if (result->prepare(fields, unit)) // This hasn't been done yet DBUG_RETURN(-1); } if (send_row) @@ -3475,7 +3592,8 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, TABLE * create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ORDER *group, bool distinct, bool save_sum_fields, - bool allow_distinct_limit, ulong select_options) + bool allow_distinct_limit, ulong select_options, + SELECT_LEX_UNIT *unit) { TABLE *table; uint i,field_count,reclength,null_count,null_pack_length, @@ -3846,8 +3964,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, test(null_pack_length)); if (allow_distinct_limit) { - set_if_smaller(table->max_rows,thd->select_limit); - param->end_write_records=thd->select_limit; + set_if_smaller(table->max_rows, unit->select_limit_cnt); + param->end_write_records= unit->select_limit_cnt; } else param->end_write_records= HA_POS_ERROR; @@ -4892,7 +5010,8 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), 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->send_records >= join->unit->select_limit_cnt && + join->do_send_rows) { if (join->select_options & OPTION_FOUND_ROWS) { @@ -4907,8 +5026,8 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), } else { - join->do_send_rows=0; - join->thd->select_limit = HA_POS_ERROR; + join->do_send_rows= 0; + join->unit->select_limit= HA_POS_ERROR; DBUG_RETURN(0); } } @@ -4969,13 +5088,13 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), DBUG_RETURN(-1); /* purecov: inspected */ if (end_of_records) DBUG_RETURN(0); - if (!error && ++join->send_records >= join->thd->select_limit && + if (!error && ++join->send_records >= join->unit->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->thd->select_limit = HA_POS_ERROR; + join->unit->select_limit_cnt = HA_POS_ERROR; } } } @@ -5056,7 +5175,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (!(join->select_options & OPTION_FOUND_ROWS)) DBUG_RETURN(-3); join->do_send_rows=0; - join->thd->select_limit = HA_POS_ERROR; + join->unit->select_limit_cnt = HA_POS_ERROR; DBUG_RETURN(0); } } @@ -5755,7 +5874,7 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having) if (!field_count) { // only const items - join->thd->select_limit=1; // Only send first row + join->unit->select_limit_cnt= 1; // Only send first row DBUG_RETURN(0); } Field **first_field=entry->field+entry->fields - field_count; @@ -7156,7 +7275,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, result->send_error(0,NullS); } } - if (!join->thd->lex.select->next) + if (!join->thd->lex.select->next_select()) { save_lock=thd->lock; thd->lock=(MYSQL_LOCK *)0; 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<Item> &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<Item> &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<Item> &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<Item> 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<Item> *) 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<Item> *) 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<Item> *) 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<Item> *) 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<Item> &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<Item> &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<Item> &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<Item> &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<Item> &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 054d427a4e0..3062747a08f 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; @@ -173,6 +172,75 @@ class JOIN { select_result *result; TMP_TABLE_PARAM tmp_table_param; 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<Item> all_fields; + List<Item> & 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 + + my_bool union_part; // this subselect is part of union + + JOIN(THD *thd, List<Item> &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(); + int reinit(); + void exec(); + int cleanup(THD *thd); }; @@ -187,7 +255,8 @@ void TEST_join(JOIN *join); bool store_val_in_field(Field *field,Item *val); TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ORDER *group, bool distinct, bool save_sum_fields, - bool allow_distinct_limit, ulong select_options); + bool allow_distinct_limit, ulong select_options, + SELECT_LEX_UNIT *unit); void free_tmp_table(THD *thd, TABLE *entry); void count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields, bool reset_with_sum_func); 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<Item> all_fields; ++ List<Item> & 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<Item> &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<Item> &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<Item> &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<Item> &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<Item> &fields, + bool reset_with_sum_func); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index c8237f3ae9b..585b5de11ab 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -27,56 +27,26 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) { - SELECT_LEX *sl, *last_sl, *lex_sl; - ORDER *order; + SELECT_LEX *sl; + SELECT_LEX_UNIT *unit= &(lex->unit); List<Item> item_list; TABLE *table; int describe=(lex->select_lex.options & SELECT_DESCRIBE) ? 1 : 0; int res; bool found_rows_for_union=false; TABLE_LIST result_table_list; - TABLE_LIST *first_table=(TABLE_LIST *)lex->select_lex.table_list.first; TMP_TABLE_PARAM tmp_table_param; select_union *union_result; DBUG_ENTER("mysql_union"); + st_select_lex_node * global; - /* Fix tables 'to-be-unioned-from' list to point at opened tables */ - last_sl= &lex->select_lex; - for (sl= last_sl; - sl && sl->linkage != NOT_A_SELECT; - last_sl=sl, sl=sl->next) + /* Global option */ + if (((void*)(global= unit->global_parameters)) == ((void*)unit)) { - for (TABLE_LIST *cursor= (TABLE_LIST *)sl->table_list.first; - cursor; - cursor=cursor->next) - cursor->table= ((TABLE_LIST*) cursor->table)->table; - } - - /* last_sel now points at the last select where the ORDER BY is stored */ - if (sl) - { - /* - The found SL is an extra SELECT_LEX argument that contains - the ORDER BY and LIMIT parameter for the whole UNION - */ - lex_sl= sl; - order= (ORDER *) lex_sl->order_list.first; - found_rows_for_union = lex->select_lex.options & OPTION_FOUND_ROWS && !describe && sl->select_limit; + found_rows_for_union = lex->select_lex.options & OPTION_FOUND_ROWS && + !describe && global->select_limit; if (found_rows_for_union) lex->select_lex.options ^= OPTION_FOUND_ROWS; -// This is done to eliminate unnecessary slowing down of the first query - if (!order || !describe) - last_sl->next=0; // Remove this extra element - } - else if (!last_sl->braces) - { - lex_sl= last_sl; // ORDER BY is here - order= (ORDER *) lex_sl->order_list.first; - } - else - { - lex_sl=0; - order=0; } if (describe) @@ -113,11 +83,12 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) bzero((char*) &tmp_table_param,sizeof(tmp_table_param)); tmp_table_param.field_count=item_list.elements; - if (!(table=create_tmp_table(thd, &tmp_table_param, item_list, - (ORDER*) 0, !describe & !lex->union_option, - 1, 0, - (lex->select_lex.options | thd->options | - TMP_TABLE_ALL_COLUMNS)))) + if (!(table= create_tmp_table(thd, &tmp_table_param, item_list, + (ORDER*) 0, !describe & !lex->union_option, + 1, 0, + (lex->select_lex.options | thd->options | + TMP_TABLE_ALL_COLUMNS), + unit))) DBUG_RETURN(-1); table->file->extra(HA_EXTRA_WRITE_CACHE); table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); @@ -133,25 +104,28 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) } union_result->save_time_stamp=!describe; - for (sl= &lex->select_lex; sl; sl=sl->next) + for (sl= &lex->select_lex; sl; sl= sl->next_select()) { lex->select=sl; - thd->offset_limit=sl->offset_limit; - thd->select_limit=sl->select_limit+sl->offset_limit; - if (thd->select_limit < sl->select_limit) - thd->select_limit= HA_POS_ERROR; // no limit - if (thd->select_limit == HA_POS_ERROR) + unit->offset_limit_cnt= sl->offset_limit; + unit->select_limit_cnt= sl->select_limit+sl->offset_limit; + if (unit->select_limit_cnt < sl->select_limit) + unit->select_limit_cnt= HA_POS_ERROR; // no limit + if (unit->select_limit_cnt == HA_POS_ERROR) sl->options&= ~OPTION_FOUND_ROWS; - res=mysql_select(thd, (describe && sl->linkage==NOT_A_SELECT) ? first_table : (TABLE_LIST*) sl->table_list.first, - sl->item_list, - sl->where, - (sl->braces) ? (ORDER *)sl->order_list.first : (ORDER *) 0, - (ORDER*) sl->group_list.first, - sl->having, - (ORDER*) NULL, - sl->options | thd->options | SELECT_NO_UNLOCK | ((describe) ? SELECT_DESCRIBE : 0), - union_result); + res= mysql_select(thd, + (TABLE_LIST*) sl->table_list.first, + sl->item_list, + sl->where, + (sl->braces) ? + (ORDER *)sl->order_list.first : (ORDER *) 0, + (ORDER*) sl->group_list.first, + sl->having, + (ORDER*) NULL, + sl->options | thd->options | + SELECT_NO_UNLOCK | ((describe) ? SELECT_DESCRIBE : 0), + union_result, unit); if (res) goto exit; } @@ -183,26 +157,20 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) } if (!thd->fatal_error) // Check if EOM { - if (lex_sl) - { - thd->offset_limit=lex_sl->offset_limit; - thd->select_limit=lex_sl->select_limit+lex_sl->offset_limit; - if (thd->select_limit < lex_sl->select_limit) - thd->select_limit= HA_POS_ERROR; // no limit - if (thd->select_limit == HA_POS_ERROR) - thd->options&= ~OPTION_FOUND_ROWS; - } - else - { - thd->offset_limit= 0; - thd->select_limit= thd->default_select_limit; - } + st_select_lex_node * global= unit->global_parameters; + unit->offset_limit_cnt= global->offset_limit; + unit->select_limit_cnt= global->select_limit+global->offset_limit; + if (unit->select_limit_cnt < global->select_limit) + unit->select_limit_cnt= HA_POS_ERROR; // no limit + if (unit->select_limit_cnt == HA_POS_ERROR) + thd->options&= ~OPTION_FOUND_ROWS; if (describe) - thd->select_limit= HA_POS_ERROR; // no limit - res=mysql_select(thd,&result_table_list, - item_list, NULL, (describe) ? 0 : order, - (ORDER*) NULL, NULL, (ORDER*) NULL, - thd->options, result); + unit->select_limit_cnt= HA_POS_ERROR; // no limit + res= mysql_select(thd,&result_table_list, + item_list, NULL, + (describe) ? 0 : (ORDER*)global->order_list.first, + (ORDER*) NULL, NULL, (ORDER*) NULL, + thd->options, result, unit); if (found_rows_for_union && !res) thd->limit_found_rows = (ulonglong)table->file->records; } @@ -226,7 +194,7 @@ select_union::select_union(TABLE *table_par) We can always use DUP_IGNORE because the temporary table will only contain a unique key if we are using not using UNION ALL */ - info.handle_duplicates=DUP_IGNORE; + info.handle_duplicates= DUP_IGNORE; } select_union::~select_union() @@ -234,8 +202,9 @@ select_union::~select_union() } -int select_union::prepare(List<Item> &list) +int select_union::prepare(List<Item> &list, SELECT_LEX_UNIT *u) { + unit= u; if (save_time_stamp && list.elements != table->fields) { my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, @@ -247,9 +216,9 @@ int select_union::prepare(List<Item> &list) bool select_union::send_data(List<Item> &values) { - if (thd->offset_limit) + if (unit->offset_limit_cnt) { // using limit offset,count - thd->offset_limit--; + unit->offset_limit_cnt--; return 0; } fill_record(table->field,values); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 2a45798ddf8..1bb0537a731 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -379,9 +379,10 @@ multi_update::multi_update(THD *thd_arg, TABLE_LIST *ut, List<Item> &fs, } int -multi_update::prepare(List<Item> &values) +multi_update::prepare(List<Item> &values, SELECT_LEX_UNIT *u) { DBUG_ENTER("multi_update::prepare"); + unit= u; do_update = true; thd->count_cuted_fields=1; thd->cuted_fields=0L; @@ -466,15 +467,20 @@ multi_update::prepare(List<Item> &values) } if (counter) { - Field_string offset(table_ref->table->file->ref_length,false,"offset",table_ref->table,true,default_charset_info); + Field_string offset(table_ref->table->file->ref_length, false, + "offset", table_ref->table, true, + default_charset_info); temp_fields->push_front(new Item_field(((Field *)&offset))); // Here I make tmp tables int cnt=counter-1; TMP_TABLE_PARAM tmp_table_param; bzero((char*) &tmp_table_param,sizeof(tmp_table_param)); tmp_table_param.field_count=temp_fields->elements; - if (!(tmp_tables[cnt]=create_tmp_table(thd, &tmp_table_param, *temp_fields, - (ORDER*) 0, 1, 0, 0, TMP_TABLE_ALL_COLUMNS))) + if (!(tmp_tables[cnt]=create_tmp_table(thd, &tmp_table_param, + *temp_fields, + (ORDER*) 0, 1, 0, 0, + TMP_TABLE_ALL_COLUMNS, + unit))) { error = 1; // A proper error message is due here DBUG_RETURN(1); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 756afbd7a09..ebd26939ad5 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 <item_list> 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 <NONE> @@ -1472,7 +1472,14 @@ select: select_init: SELECT_SYM select_part2 { Select->braces=false; } union | - '(' SELECT_SYM select_part2 ')' { Select->braces=true;} union_opt + '(' SELECT_SYM select_part2 ')' + { + SELECT_LEX * sel=Select; + sel->braces=true; + /* select in braces, can't contain global parameters */ + sel->master_unit()->global_parameters= + sel->master_unit(); + } union_opt select_part2: @@ -1563,8 +1570,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: @@ -1704,6 +1711,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 *) @@ -2178,23 +2186,22 @@ join_table: | '(' SELECT_SYM select_part3 ')' opt_table_alias { LEX *lex=Lex; - SELECT_LEX *select_to_execute= lex->select; - lex->select=lex->select->prev; - if (!($$=add_table_to_list(new Table_ident(select_to_execute), - $5,0,TL_UNLOCK))) + SELECT_LEX_UNIT *unit= lex->select->master_unit(); + lex->select= unit->outer_select(); + if (!($$= add_table_to_list(new Table_ident(unit), + $5,0,TL_UNLOCK))) YYABORT; } select_part3: { - LEX *lex=Lex; - lex->derived_tables=true; - SELECT_LEX *tmp=lex->select; - if (lex->select->linkage == NOT_A_SELECT || mysql_new_select(lex)) + LEX *lex= Lex; + lex->derived_tables= true; + if (lex->select->linkage == GLOBAL_OPTIONS_TYPE || + mysql_new_select(lex, 1)) YYABORT; mysql_init_select(lex); - lex->select->linkage=DERIVED_TABLE_TYPE; - lex->select->prev=tmp; + lex->select->linkage= DERIVED_TABLE_TYPE; } select_options select_item_list select_intoto @@ -2318,7 +2325,7 @@ order_clause: LEX *lex=Lex; if (lex->sql_command == SQLCOM_MULTI_UPDATE) YYABORT; - lex->select->sort_default=1; + /*lex->select->sort_default=1;*/ } order_list order_list: @@ -3834,7 +3841,8 @@ union_list: net_printf(&lex->thd->net, ER_WRONG_USAGE,"UNION","INTO"); YYABORT; } - if (lex->select->linkage == NOT_A_SELECT || mysql_new_select(lex)) + if (lex->select->linkage == GLOBAL_OPTIONS_TYPE || + mysql_new_select(lex, 0)) YYABORT; lex->select->linkage=UNION_TYPE; } @@ -3849,10 +3857,15 @@ optional_order_or_limit: | { LEX *lex=Lex; - if (!lex->select->braces || mysql_new_select(lex)) + if (!lex->select->braces) YYABORT; - mysql_init_select(lex); - lex->select->linkage=NOT_A_SELECT; + lex->select->master_unit()->global_parameters= + lex->select->master_unit(); + /* + Following type conversion looks like hack, but all that need SELECT_LEX + fields always check linkage type. + */ + lex->select= (SELECT_LEX*)lex->select->master_unit(); lex->select->select_limit=lex->thd->default_select_limit; } opt_order_clause limit_clause @@ -3860,3 +3873,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 = lex->select->outer_select(); + } diff --git a/sql/table.h b/sql/table.h index 183e1b2a38f..b89701bfc8e 100644 --- a/sql/table.h +++ b/sql/table.h @@ -140,18 +140,26 @@ typedef struct st_table_list { struct st_table_list *next; char *db,*name,*real_name; uint32 db_length, real_name_length; - Item *on_expr; /* Used with outer join */ - struct st_table_list *natural_join; /* natural join on this table*/ + Item *on_expr; /* Used with outer join */ + struct st_table_list *natural_join; /* natural join on this table*/ /* ... join ... USE INDEX ... IGNORE INDEX */ - List<String> *use_index,*ignore_index; - TABLE *table; + List<String> *use_index, *ignore_index; + /* + Usually hold reference on opened table, but may hold reference + to node of complete list of tables used in UNION & subselect. + */ + union + { + TABLE *table; /* opened table */ + st_table_list *table_list; /* pointer to node of list of all tables */ + }; GRANT_INFO grant; thr_lock_type lock_type; - uint outer_join; /* Which join type */ - bool straight; /* optimize with prev table */ - bool updating; /* for replicate-do/ignore table */ - bool shared; /* Used twice in union */ - void *derived; + uint outer_join; /* Which join type */ + bool straight; /* optimize with prev table */ + bool updating; /* for replicate-do/ignore table */ + bool shared; /* Used twice in union */ + void *derived; /* SELECT_LEX_UNIT of derived table */ } TABLE_LIST; typedef struct st_changed_table_list { |