diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item.cc | 28 | ||||
-rw-r--r-- | sql/item_subselect.cc | 14 | ||||
-rw-r--r-- | sql/item_sum.cc | 64 | ||||
-rw-r--r-- | sql/item_sum.h | 7 | ||||
-rw-r--r-- | sql/mysql_priv.h | 7 | ||||
-rw-r--r-- | sql/sql_lex.cc | 3 | ||||
-rw-r--r-- | sql/sql_lex.h | 11 | ||||
-rw-r--r-- | sql/sql_select.cc | 34 |
8 files changed, 132 insertions, 36 deletions
diff --git a/sql/item.cc b/sql/item.cc index 6f0119393e7..85756dcb124 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3925,9 +3925,9 @@ bool Item_field::fix_fields(THD *thd, Item **reference) } if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) goto error; - else if (!ret) - return FALSE; outer_fixed= TRUE; + if (!ret) + goto mark_non_agg_field; } else if (!from_field) goto error; @@ -3939,9 +3939,9 @@ bool Item_field::fix_fields(THD *thd, Item **reference) int ret; if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) goto error; - else if (!ret) - return FALSE; outer_fixed= 1; + if (!ret) + goto mark_non_agg_field; } /* @@ -4007,6 +4007,26 @@ bool Item_field::fix_fields(THD *thd, Item **reference) thd->lex->current_select->non_agg_fields.push_back(this); marker= thd->lex->current_select->cur_pos_in_select_list; } +mark_non_agg_field: + if (fixed && thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) + { + /* + Mark selects according to presence of non aggregated fields. + Fields from outer selects added to the aggregate function + outer_fields list as its unknown at the moment whether it's + aggregated or not. + */ + if (!thd->lex->in_sum_func) + cached_table->select_lex->full_group_by_flag|= NON_AGG_FIELD_USED; + else + { + if (outer_fixed) + thd->lex->in_sum_func->outer_fields.push_back(this); + else if (thd->lex->in_sum_func->nest_level != + thd->lex->current_select->nest_level) + cached_table->select_lex->full_group_by_flag|= NON_AGG_FIELD_USED; + } + } return FALSE; error: diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index b3710841dfb..1c81a4ac3a1 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1434,6 +1434,19 @@ Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func) DBUG_ENTER("Item_in_subselect::select_in_like_transformer"); + { + /* + IN/SOME/ALL/ANY subqueries aren't support LIMIT clause. Without it + ORDER BY clause becomes meaningless thus we drop it here. + */ + SELECT_LEX *sl= current->master_unit()->first_select(); + for (; sl; sl= sl->next_select()) + { + if (sl->join) + sl->join->order= 0; + } + } + if (changed) { DBUG_RETURN(RES_OK); @@ -1468,6 +1481,7 @@ Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func) transformed= 1; arena= thd->activate_stmt_arena_if_needed(&backup); + /* Both transformers call fix_fields() only for Items created inside them, and all that items do not make permanent changes in current item arena diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 3d6d46ab3f4..42c934261b0 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -66,6 +66,7 @@ bool Item_sum::init_sum_func_check(THD *thd) aggr_sel= NULL; max_arg_level= -1; max_sum_func_level= -1; + outer_fields.empty(); return FALSE; } @@ -175,6 +176,7 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) MYF(0)); return TRUE; } + if (in_sum_func) { /* @@ -195,6 +197,68 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) set_if_bigger(in_sum_func->max_sum_func_level, aggr_level); set_if_bigger(in_sum_func->max_sum_func_level, max_sum_func_level); } + + /* + Check that non-aggregated fields and sum functions aren't mixed in the + same select in the ONLY_FULL_GROUP_BY mode. + */ + if (outer_fields.elements) + { + Item_field *field; + /* + Here we compare the nesting level of the select to which an outer field + belongs to with the aggregation level of the sum function. All fields in + the outer_fields list are checked. + + If the nesting level is equal to the aggregation level then the field is + aggregated by this sum function. + If the nesting level is less than the aggregation level then the field + belongs to an outer select. In this case if there is an embedding sum + function add current field to functions outer_fields list. If there is + no embedding function then the current field treated as non aggregated + and the select it belongs to is marked accordingly. + If the nesting level is greater than the aggregation level then it means + that this field was added by an inner sum function. + Consider an example: + + select avg ( <-- we are here, checking outer.f1 + select ( + select sum(outer.f1 + inner.f1) from inner + ) from outer) + from most_outer; + + In this case we check that no aggregate functions are used in the + select the field belongs to. If there are some then an error is + raised. + */ + List_iterator<Item_field> of(outer_fields); + while ((field= of++)) + { + SELECT_LEX *sel= field->cached_table->select_lex; + if (sel->nest_level < aggr_level) + { + if (in_sum_func) + { + /* + Let upper function decide whether this field is a non + aggregated one. + */ + in_sum_func->outer_fields.push_back(field); + } + else + sel->full_group_by_flag|= NON_AGG_FIELD_USED; + } + if (sel->nest_level > aggr_level && + (sel->full_group_by_flag & SUM_FUNC_USED) && + !sel->group_list.elements) + { + my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS, + ER(ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0)); + return TRUE; + } + } + } + aggr_sel->full_group_by_flag|= SUM_FUNC_USED; update_used_tables(); thd->lex->in_sum_func= in_sum_func; return FALSE; diff --git a/sql/item_sum.h b/sql/item_sum.h index bf0abe53eea..d39fc96e254 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -239,6 +239,13 @@ public: int8 max_arg_level; /* max level of unbound column references */ int8 max_sum_func_level;/* max level of aggregation for embedded functions */ bool quick_group; /* If incremental update of fields */ + /* + This list is used by the check for mixing non aggregated fields and + sum functions in the ONLY_FULL_GROUP_BY_MODE. We save all outer fields + directly or indirectly used under this function it as it's unclear + at the moment of fixing outer field whether it's aggregated or not. + */ + List<Item_field> outer_fields; protected: table_map used_tables_cache; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 59ec18e1a3d..6352647a154 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1045,6 +1045,13 @@ SQL_SELECT *make_select(TABLE *head, table_map const_tables, extern Item **not_found_item; /* + A set of constants used for checking non aggregated fields and sum + functions mixture in the ONLY_FULL_GROUP_BY_MODE. +*/ +#define NON_AGG_FIELD_USED 1 +#define SUM_FUNC_USED 2 + +/* This enumeration type is used only by the function find_item_in_list to return the info on how an item has been resolved against a list of possibly aliased items. diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index f1b4ffc949d..6bfcd982b04 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1253,6 +1253,7 @@ void st_select_lex::init_select() non_agg_fields.empty(); cond_value= having_value= Item::COND_UNDEF; inner_refs_list.empty(); + full_group_by_flag= 0; } /* @@ -1491,8 +1492,6 @@ bool st_select_lex::test_limit() "LIMIT & IN/ALL/ANY/SOME subquery"); return(1); } - // no sense in ORDER BY without LIMIT - order_list.empty(); return(0); } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index f216f51b0e4..cde4c3a97b3 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -611,7 +611,16 @@ public: joins on the right. */ List<String> *prev_join_using; - + /* + Bitmap used in the ONLY_FULL_GROUP_BY_MODE to prevent mixture of aggregate + functions and non aggregated fields when GROUP BY list is absent. + Bits: + 0 - non aggregated fields are used in this select, + defined as NON_AGG_FIELD_USED. + 1 - aggregate functions are used in this select, + defined as SUM_FUNC_USED. + */ + uint8 full_group_by_flag; void init_query(); void init_select(); st_select_lex_unit* master_unit(); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6392f7c4299..8c5d09f4e16 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -568,37 +568,13 @@ JOIN::prepare(Item ***rref_pointer_array, /* Check if there are references to un-aggregated columns when computing aggregate functions with implicit grouping (there is no GROUP BY). - TODO: Add check of calculation of GROUP functions and fields: - SELECT COUNT(*)+table.col1 from table1; */ - if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) + if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && !group_list && + select_lex->full_group_by_flag == (NON_AGG_FIELD_USED | SUM_FUNC_USED)) { - if (!group_list) - { - uint flag=0; - List_iterator_fast<Item> it(fields_list); - Item *item; - while ((item= it++)) - { - if (item->with_sum_func) - flag|=1; - else if (!(flag & 2) && !item->const_during_execution()) - flag|=2; - } - if (having) - { - if (having->with_sum_func) - flag |= 1; - else if (!having->const_during_execution()) - flag |= 2; - } - if (flag == 3) - { - my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS, - ER(ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0)); - DBUG_RETURN(-1); - } - } + my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS, + ER(ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0)); + DBUG_RETURN(-1); } { /* Caclulate the number of groups */ |