Commit 321726e8 authored by unknown's avatar unknown

Bug#27321: Wrong subquery result in a grouping select.

The Item_outer_ref class based on the Item_direct_ref class was always used
to represent an outer field. But if the outer select is a grouping one and the 
outer field isn't under an aggregate function which is aggregated in that
outer select an Item_ref object should be used to represent such a field.
If the outer select in which the outer field is resolved isn't grouping then
the Item_field class should be used to represent such a field.
This logic also should be used for an outer field resolved through its alias
name.

Now the Item_field::fix_outer_field() uses Item_outer_field objects to
represent aliased and non-aliased outer fields for grouping outer selects
only.
Now the fix_inner_refs() function chooses which class to use to access outer
field - the Item_ref or the Item_direct_ref. An object of the chosen class
substitutes the original field in the Item_outer_ref object.
The direct_ref and the found_in_select_list fields were added to the
Item_outer_ref class.


mysql-test/t/subselect3.test:
  Some test cases were corrected after the fix for the bug#27321.
mysql-test/r/subselect3.result:
  Some test cases were corrected after the fix for the bug#27321.
mysql-test/t/subselect.test:
  Added a test case for the bug#27321: Wrong subquery result in a grouping select.
mysql-test/r/subselect.result:
  Added a test case for the bug#27321: Wrong subquery result in a grouping select.
  Some test cases were corrected after this fix.
sql/sql_union.cc:
  Bug#27321: Wrong subquery result in a grouping select.
  Cleanup of the inner_refs_list.
sql/sql_select.cc:
  Bug#27321: Wrong subquery result in a grouping select.
  Now the fix_inner_refs() function chooses which class to use to access outer
  field - the Item_ref or the Item_direct_ref. An object of the chosen class
  substitutes the original field in the Item_outer_ref object.
  A comment is corrected.
sql/item.cc:
  Bug#27321: Wrong subquery result in a grouping select.
  Now the Item_field::fix_outer_field() uses Item_outer_field objects to
  represent aliased and non-aliased outer fields for grouping outer selects
  only.
sql/item.h:
  Bug#27321: Wrong subquery result in a grouping select.
  The direct_ref and the found_in_select_list fields were added to the
  Item_outer_ref class.
parent fbf7748f
...@@ -48,7 +48,7 @@ id select_type table type possible_keys key key_len ref rows Extra ...@@ -48,7 +48,7 @@ id select_type table type possible_keys key key_len ref rows Extra
3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used
2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used 2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings: Warnings:
Note 1276 Field or reference 'a' of SELECT #3 was resolved in SELECT #1 Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1
Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1 Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1
Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having ((select '1' AS `a`) = 1) Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having ((select '1' AS `a`) = 1)
SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1; SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1;
...@@ -330,7 +330,7 @@ patient_uq clinic_uq ...@@ -330,7 +330,7 @@ patient_uq clinic_uq
explain extended select * from t6 where exists (select * from t7 where uq = clinic_uq); explain extended select * from t6 where exists (select * from t7 where uq = clinic_uq);
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t6 ALL NULL NULL NULL NULL 4 Using where 1 PRIMARY t6 ALL NULL NULL NULL NULL 4 Using where
2 DEPENDENT SUBQUERY t7 eq_ref PRIMARY PRIMARY 4 t6.clinic_uq 1 Using where; Using index 2 DEPENDENT SUBQUERY t7 eq_ref PRIMARY PRIMARY 4 test.t6.clinic_uq 1 Using index
Warnings: Warnings:
Note 1276 Field or reference 'test.t6.clinic_uq' of SELECT #2 was resolved in SELECT #1 Note 1276 Field or reference 'test.t6.clinic_uq' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`t6`.`patient_uq` AS `patient_uq`,`test`.`t6`.`clinic_uq` AS `clinic_uq` from `test`.`t6` where exists(select 1 AS `Not_used` from `test`.`t7` where (`test`.`t7`.`uq` = `test`.`t6`.`clinic_uq`)) Note 1003 select `test`.`t6`.`patient_uq` AS `patient_uq`,`test`.`t6`.`clinic_uq` AS `clinic_uq` from `test`.`t6` where exists(select 1 AS `Not_used` from `test`.`t7` where (`test`.`t7`.`uq` = `test`.`t6`.`clinic_uq`))
...@@ -1741,7 +1741,7 @@ Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `tes ...@@ -1741,7 +1741,7 @@ Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `tes
explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null); explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null);
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY tt ALL NULL NULL NULL NULL 12 Using where 1 PRIMARY tt ALL NULL NULL NULL NULL 12 Using where
2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 tt.id 1 Using where; Using index 2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 Using where; Using index
Warnings: Warnings:
Note 1276 Field or reference 'test.tt.id' of SELECT #2 was resolved in SELECT #1 Note 1276 Field or reference 'test.tt.id' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where (not(exists(select `test`.`t1`.`id` AS `id` from `test`.`t1` where ((`test`.`t1`.`id` < 8) and (`test`.`t1`.`id` = `test`.`tt`.`id`)) having (`test`.`t1`.`id` is not null)))) Note 1003 select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where (not(exists(select `test`.`t1`.`id` AS `id` from `test`.`t1` where ((`test`.`t1`.`id` < 8) and (`test`.`t1`.`id` = `test`.`tt`.`id`)) having (`test`.`t1`.`id` is not null))))
...@@ -3924,3 +3924,68 @@ c a (SELECT GROUP_CONCAT(COUNT(a)+1) FROM t2 WHERE m = a) ...@@ -3924,3 +3924,68 @@ c a (SELECT GROUP_CONCAT(COUNT(a)+1) FROM t2 WHERE m = a)
3 3 4 3 3 4
1 4 2,2 1 4 2,2
DROP table t1,t2; DROP table t1,t2;
CREATE TABLE t1 (a int, b INT, d INT, c CHAR(10) NOT NULL, PRIMARY KEY (a, b));
INSERT INTO t1 VALUES (1,1,0,'a'), (1,2,0,'b'), (1,3,0,'c'), (1,4,0,'d'),
(1,5,0,'e'), (2,1,0,'f'), (2,2,0,'g'), (2,3,0,'h'), (3,4,0,'i'), (3,3,0,'j'),
(3,2,0,'k'), (3,1,0,'l'), (1,9,0,'m'), (1,0,10,'n'), (2,0,5,'o'), (3,0,7,'p');
SELECT a, MAX(b),
(SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.b=MAX(t1.b + 0)) as test
FROM t1 GROUP BY a;
a MAX(b) test
1 9 m
2 3 h
3 4 i
SELECT a x, MAX(b),
(SELECT t.c FROM t1 AS t WHERE x=t.a AND t.b=MAX(t1.b + 0)) as test
FROM t1 GROUP BY a;
x MAX(b) test
1 9 m
2 3 h
3 4 i
SELECT a, AVG(b),
(SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.b=AVG(t1.b)) AS test
FROM t1 WHERE t1.d=0 GROUP BY a;
a AVG(b) test
1 4.0000 d
2 2.0000 g
3 2.5000 NULL
SELECT tt.a,
(SELECT (SELECT c FROM t1 as t WHERE t1.a=t.a AND t.d=MAX(t1.b + tt.a)
LIMIT 1) FROM t1 WHERE t1.a=tt.a GROUP BY a LIMIT 1) as test
FROM t1 as tt;
a test
1 n
1 n
1 n
1 n
1 n
1 n
1 n
2 o
2 o
2 o
2 o
3 p
3 p
3 p
3 p
3 p
SELECT tt.a,
(SELECT (SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.d=MAX(t1.b + tt.a)
LIMIT 1)
FROM t1 WHERE t1.a=tt.a GROUP BY a LIMIT 1) as test
FROM t1 as tt GROUP BY tt.a;
a test
1 n
2 o
3 p
SELECT tt.a, MAX(
(SELECT (SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.d=MAX(t1.b + tt.a)
LIMIT 1)
FROM t1 WHERE t1.a=tt.a GROUP BY a LIMIT 1)) as test
FROM t1 as tt GROUP BY tt.a;
a test
1 n
2 o
3 p
DROP TABLE t1;
...@@ -432,7 +432,7 @@ alter table t1 add index idx(oref,ie); ...@@ -432,7 +432,7 @@ alter table t1 add index idx(oref,ie);
explain select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; explain select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 7 1 PRIMARY t2 ALL NULL NULL NULL NULL 7
2 DEPENDENT SUBQUERY t1 ref_or_null idx idx 10 t2.oref,func 4 Using where; Using index; Full scan on NULL key 2 DEPENDENT SUBQUERY t1 ref_or_null idx idx 10 test.t2.oref,func 4 Using where; Using index; Full scan on NULL key
select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2;
oref a Z oref a Z
ee NULL NULL ee NULL NULL
...@@ -457,7 +457,7 @@ group by grp having min(ie) > 1) Z ...@@ -457,7 +457,7 @@ group by grp having min(ie) > 1) Z
from t2; from t2;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 7 1 PRIMARY t2 ALL NULL NULL NULL NULL 7
2 DEPENDENT SUBQUERY t1 ref idx idx 5 t2.oref 2 Using where; Using temporary; Using filesort 2 DEPENDENT SUBQUERY t1 ref idx idx 5 test.t2.oref 2 Using where; Using temporary; Using filesort
select oref, a, select oref, a,
a in (select min(ie) from t1 where oref=t2.oref a in (select min(ie) from t1 where oref=t2.oref
group by grp having min(ie) > 1) Z group by grp having min(ie) > 1) Z
...@@ -661,12 +661,6 @@ SELECT * FROM t1 GROUP by t1.a ...@@ -661,12 +661,6 @@ SELECT * FROM t1 GROUP by t1.a
HAVING (MAX(t1.b) > (SELECT MAX(t2.b) FROM t2 WHERE t2.c < t1.c HAVING (MAX(t1.b) > (SELECT MAX(t2.b) FROM t2 WHERE t2.c < t1.c
HAVING MAX(t2.b+t1.a) < 10)); HAVING MAX(t2.b+t1.a) < 10));
a b c a b c
SELECT a, AVG(b), (SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.b=AVG(t1.b))
AS test FROM t1 GROUP BY a;
a AVG(b) test
1 4.0000 NULL
2 2.0000 k
3 2.5000 NULL
SELECT a,b,c FROM t1 WHERE b in (9,3,4) ORDER BY b,c; SELECT a,b,c FROM t1 WHERE b in (9,3,4) ORDER BY b,c;
a b c a b c
1 3 c 1 3 c
......
...@@ -2782,3 +2782,40 @@ SELECT COUNT(*) c, a, ...@@ -2782,3 +2782,40 @@ SELECT COUNT(*) c, a,
FROM t1 GROUP BY a; FROM t1 GROUP BY a;
DROP table t1,t2; DROP table t1,t2;
#
# Bug#27321: Wrong subquery result in a grouping select
#
CREATE TABLE t1 (a int, b INT, d INT, c CHAR(10) NOT NULL, PRIMARY KEY (a, b));
INSERT INTO t1 VALUES (1,1,0,'a'), (1,2,0,'b'), (1,3,0,'c'), (1,4,0,'d'),
(1,5,0,'e'), (2,1,0,'f'), (2,2,0,'g'), (2,3,0,'h'), (3,4,0,'i'), (3,3,0,'j'),
(3,2,0,'k'), (3,1,0,'l'), (1,9,0,'m'), (1,0,10,'n'), (2,0,5,'o'), (3,0,7,'p');
SELECT a, MAX(b),
(SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.b=MAX(t1.b + 0)) as test
FROM t1 GROUP BY a;
SELECT a x, MAX(b),
(SELECT t.c FROM t1 AS t WHERE x=t.a AND t.b=MAX(t1.b + 0)) as test
FROM t1 GROUP BY a;
SELECT a, AVG(b),
(SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.b=AVG(t1.b)) AS test
FROM t1 WHERE t1.d=0 GROUP BY a;
SELECT tt.a,
(SELECT (SELECT c FROM t1 as t WHERE t1.a=t.a AND t.d=MAX(t1.b + tt.a)
LIMIT 1) FROM t1 WHERE t1.a=tt.a GROUP BY a LIMIT 1) as test
FROM t1 as tt;
SELECT tt.a,
(SELECT (SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.d=MAX(t1.b + tt.a)
LIMIT 1)
FROM t1 WHERE t1.a=tt.a GROUP BY a LIMIT 1) as test
FROM t1 as tt GROUP BY tt.a;
SELECT tt.a, MAX(
(SELECT (SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.d=MAX(t1.b + tt.a)
LIMIT 1)
FROM t1 WHERE t1.a=tt.a GROUP BY a LIMIT 1)) as test
FROM t1 as tt GROUP BY tt.a;
DROP TABLE t1;
...@@ -507,8 +507,6 @@ SELECT a, MAX(b), (SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.b=MAX(t1.b)) ...@@ -507,8 +507,6 @@ SELECT a, MAX(b), (SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.b=MAX(t1.b))
SELECT * FROM t1 GROUP by t1.a SELECT * FROM t1 GROUP by t1.a
HAVING (MAX(t1.b) > (SELECT MAX(t2.b) FROM t2 WHERE t2.c < t1.c HAVING (MAX(t1.b) > (SELECT MAX(t2.b) FROM t2 WHERE t2.c < t1.c
HAVING MAX(t2.b+t1.a) < 10)); HAVING MAX(t2.b+t1.a) < 10));
SELECT a, AVG(b), (SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.b=AVG(t1.b))
AS test FROM t1 GROUP BY a;
SELECT a,b,c FROM t1 WHERE b in (9,3,4) ORDER BY b,c; SELECT a,b,c FROM t1 WHERE b in (9,3,4) ORDER BY b,c;
......
...@@ -1610,7 +1610,7 @@ void Item_ident_for_show::make_field(Send_field *tmp_field) ...@@ -1610,7 +1610,7 @@ void Item_ident_for_show::make_field(Send_field *tmp_field)
Item_field::Item_field(Field *f) Item_field::Item_field(Field *f)
:Item_ident(0, NullS, *f->table_name, f->field_name), :Item_ident(0, NullS, *f->table_name, f->field_name),
item_equal(0), no_const_subst(0), item_equal(0), no_const_subst(0),
have_privileges(0), any_privileges(0), fixed_as_field(0) have_privileges(0), any_privileges(0)
{ {
set_field(f); set_field(f);
/* /*
...@@ -1624,7 +1624,7 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, ...@@ -1624,7 +1624,7 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg,
Field *f) Field *f)
:Item_ident(context_arg, f->table->s->db, *f->table_name, f->field_name), :Item_ident(context_arg, f->table->s->db, *f->table_name, f->field_name),
item_equal(0), no_const_subst(0), item_equal(0), no_const_subst(0),
have_privileges(0), any_privileges(0), fixed_as_field(0) have_privileges(0), any_privileges(0)
{ {
/* /*
We always need to provide Item_field with a fully qualified field We always need to provide Item_field with a fully qualified field
...@@ -1663,7 +1663,7 @@ Item_field::Item_field(Name_resolution_context *context_arg, ...@@ -1663,7 +1663,7 @@ Item_field::Item_field(Name_resolution_context *context_arg,
const char *field_name_arg) const char *field_name_arg)
:Item_ident(context_arg, db_arg,table_name_arg,field_name_arg), :Item_ident(context_arg, db_arg,table_name_arg,field_name_arg),
field(0), result_field(0), item_equal(0), no_const_subst(0), field(0), result_field(0), item_equal(0), no_const_subst(0),
have_privileges(0), any_privileges(0), fixed_as_field(0) have_privileges(0), any_privileges(0)
{ {
SELECT_LEX *select= current_thd->lex->current_select; SELECT_LEX *select= current_thd->lex->current_select;
collation.set(DERIVATION_IMPLICIT); collation.set(DERIVATION_IMPLICIT);
...@@ -1679,8 +1679,7 @@ Item_field::Item_field(THD *thd, Item_field *item) ...@@ -1679,8 +1679,7 @@ Item_field::Item_field(THD *thd, Item_field *item)
item_equal(item->item_equal), item_equal(item->item_equal),
no_const_subst(item->no_const_subst), no_const_subst(item->no_const_subst),
have_privileges(item->have_privileges), have_privileges(item->have_privileges),
any_privileges(item->any_privileges), any_privileges(item->any_privileges)
fixed_as_field(item->fixed_as_field)
{ {
collation.set(DERIVATION_IMPLICIT); collation.set(DERIVATION_IMPLICIT);
} }
...@@ -3447,6 +3446,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) ...@@ -3447,6 +3446,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
Item **ref= (Item **) not_found_item; Item **ref= (Item **) not_found_item;
SELECT_LEX *current_sel= (SELECT_LEX *) thd->lex->current_select; SELECT_LEX *current_sel= (SELECT_LEX *) thd->lex->current_select;
Name_resolution_context *outer_context= 0; Name_resolution_context *outer_context= 0;
SELECT_LEX *select;
/* Currently derived tables cannot be correlated */ /* Currently derived tables cannot be correlated */
if (current_sel->master_unit()->first_select()->linkage != if (current_sel->master_unit()->first_select()->linkage !=
DERIVED_TABLE_TYPE) DERIVED_TABLE_TYPE)
...@@ -3455,7 +3455,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) ...@@ -3455,7 +3455,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
outer_context; outer_context;
outer_context= outer_context->outer_context) outer_context= outer_context->outer_context)
{ {
SELECT_LEX *select= outer_context->select_lex; select= outer_context->select_lex;
Item_subselect *prev_subselect_item= Item_subselect *prev_subselect_item=
last_checked_context->select_lex->master_unit()->item; last_checked_context->select_lex->master_unit()->item;
last_checked_context= outer_context; last_checked_context= outer_context;
...@@ -3498,45 +3498,28 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) ...@@ -3498,45 +3498,28 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
} }
if (*from_field != view_ref_found) if (*from_field != view_ref_found)
{ {
prev_subselect_item->used_tables_cache|= (*from_field)->table->map; prev_subselect_item->used_tables_cache|= (*from_field)->table->map;
prev_subselect_item->const_item_cache= 0; prev_subselect_item->const_item_cache= 0;
set_field(*from_field);
if (!last_checked_context->select_lex->having_fix_field && if (!last_checked_context->select_lex->having_fix_field &&
!fixed_as_field) select->group_list.elements)
{ {
Item_outer_ref *rf; Item_outer_ref *rf;
Query_arena *arena= 0, backup;
/* /*
Each outer field is replaced for an Item_outer_ref object. If an outer field is resolved in a grouping select then it
This is done in order to get correct results when the outer is replaced for an Item_outer_ref object. Otherwise an
select employs a temporary table. Item_field object is used.
The original fields are saved in the inner_fields_list of the The new Item_outer_ref object is saved in the inner_refs_list of
outer select. This list is created by the following reasons: the outer select. Here it is only created. It can be fixed only
1. We can't add field items to the outer select list directly after the original field has been fixed and this is done in the
because the outer select hasn't been fully fixed yet. fix_inner_refs() function.
2. We need a location to refer to in the Item_ref object
so the inner_fields_list is used as such temporary
reference storage.
The new Item_outer_ref object replaces the original field and is
also saved in the inner_refs_list of the outer select. Here
it is only created. It can be fixed only after the original
field has been fixed and this is done in the fix_inner_refs()
function.
*/ */
set_field(*from_field); ;
arena= thd->activate_stmt_arena_if_needed(&backup); if (!(rf= new Item_outer_ref(context, this)))
rf= new Item_outer_ref(context, this);
if (!rf)
{
if (arena)
thd->restore_active_arena(arena, &backup);
return -1; return -1;
} thd->change_item_tree(reference, rf);
*reference= rf;
select->inner_refs_list.push_back(rf); select->inner_refs_list.push_back(rf);
if (arena) rf->in_sum_func= thd->lex->in_sum_func;
thd->restore_active_arena(arena, &backup);
fixed_as_field= 1;
} }
if (thd->lex->in_sum_func && if (thd->lex->in_sum_func &&
thd->lex->in_sum_func->nest_level == thd->lex->in_sum_func->nest_level ==
...@@ -3642,11 +3625,20 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) ...@@ -3642,11 +3625,20 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
rf= (place == IN_HAVING ? rf= (place == IN_HAVING ?
new Item_ref(context, ref, (char*) table_name, new Item_ref(context, ref, (char*) table_name,
(char*) field_name, alias_name_used) : (char*) field_name, alias_name_used) :
(!select->group_list.elements ?
new Item_direct_ref(context, ref, (char*) table_name, new Item_direct_ref(context, ref, (char*) table_name,
(char*) field_name, alias_name_used)); (char*) field_name, alias_name_used) :
new Item_outer_ref(context, ref, (char*) table_name,
(char*) field_name, alias_name_used)));
*ref= save; *ref= save;
if (!rf) if (!rf)
return -1; return -1;
if (place != IN_HAVING && select->group_list.elements)
{
outer_context->select_lex->inner_refs_list.push_back((Item_outer_ref*)rf);
((Item_outer_ref*)rf)->in_sum_func= thd->lex->in_sum_func;
}
thd->change_item_tree(reference, rf); thd->change_item_tree(reference, rf);
/* /*
rf is Item_ref => never substitute other items (in this case) rf is Item_ref => never substitute other items (in this case)
...@@ -5547,16 +5539,19 @@ bool Item_direct_view_ref::fix_fields(THD *thd, Item **reference) ...@@ -5547,16 +5539,19 @@ bool Item_direct_view_ref::fix_fields(THD *thd, Item **reference)
bool Item_outer_ref::fix_fields(THD *thd, Item **reference) bool Item_outer_ref::fix_fields(THD *thd, Item **reference)
{ {
DBUG_ASSERT(*ref); bool err;
/* outer_field->check_cols() will be made in Item_direct_ref::fix_fields */ /* outer_ref->check_cols() will be made in Item_direct_ref::fix_fields */
outer_field->fixed_as_field= 1; if ((*ref) && !(*ref)->fixed && ((*ref)->fix_fields(thd, reference)))
if (!outer_field->fixed &&
(outer_field->fix_fields(thd, reference)))
return TRUE; return TRUE;
table_name= outer_field->table_name; err= Item_direct_ref::fix_fields(thd, reference);
return Item_direct_ref::fix_fields(thd, reference); if (!outer_ref)
outer_ref= *ref;
if ((*ref)->type() == Item::FIELD_ITEM)
table_name= ((Item_field*)outer_ref)->table_name;
return err;
} }
/* /*
Compare two view column references for equality. Compare two view column references for equality.
......
...@@ -1221,7 +1221,6 @@ class Item_field :public Item_ident ...@@ -1221,7 +1221,6 @@ class Item_field :public Item_ident
uint have_privileges; uint have_privileges;
/* field need any privileges (for VIEW creation) */ /* field need any privileges (for VIEW creation) */
bool any_privileges; bool any_privileges;
bool fixed_as_field;
Item_field(Name_resolution_context *context_arg, Item_field(Name_resolution_context *context_arg,
const char *db_arg,const char *table_name_arg, const char *db_arg,const char *table_name_arg,
const char *field_name_arg); const char *field_name_arg);
...@@ -1979,30 +1978,49 @@ class Item_direct_view_ref :public Item_direct_ref ...@@ -1979,30 +1978,49 @@ class Item_direct_view_ref :public Item_direct_ref
}; };
/*
Class for outer fields.
An object of this class is created when the select where the outer field was
resolved is a grouping one. After it has been fixed the ref field will point
to either an Item_ref or an Item_direct_ref object which will be used to
access the field.
See also comments for the fix_inner_refs() and the
Item_field::fix_outer_field() functions.
*/
class Item_sum;
class Item_outer_ref :public Item_direct_ref class Item_outer_ref :public Item_direct_ref
{ {
public: public:
Item_field *outer_field; Item *outer_ref;
/* The aggregate function under which this outer ref is used, if any. */
Item_sum *in_sum_func;
/*
TRUE <=> that the outer_ref is already present in the select list
of the outer select.
*/
bool found_in_select_list;
Item_outer_ref(Name_resolution_context *context_arg, Item_outer_ref(Name_resolution_context *context_arg,
Item_field *outer_field_arg) Item_field *outer_field_arg)
:Item_direct_ref(context_arg, 0, outer_field_arg->table_name, :Item_direct_ref(context_arg, 0, outer_field_arg->table_name,
outer_field_arg->field_name), outer_field_arg->field_name),
outer_field(outer_field_arg) outer_ref(outer_field_arg), in_sum_func(0),
found_in_select_list(0)
{ {
ref= (Item**)&outer_field; ref= &outer_ref;
set_properties(); set_properties();
fixed= 0; fixed= 0;
} }
void cleanup() Item_outer_ref(Name_resolution_context *context_arg, Item **item,
{ const char *table_name_arg, const char *field_name_arg,
ref= (Item**)&outer_field; bool alias_name_used_arg)
fixed= 0; :Item_direct_ref(context_arg, item, table_name_arg, field_name_arg,
Item_direct_ref::cleanup(); alias_name_used_arg),
outer_field->cleanup(); outer_ref(0), in_sum_func(0), found_in_select_list(1)
} {}
void save_in_result_field(bool no_conversions) void save_in_result_field(bool no_conversions)
{ {
outer_field->save_org_in_field(result_field); outer_ref->save_org_in_field(result_field);
} }
bool fix_fields(THD *, Item **); bool fix_fields(THD *, Item **);
table_map used_tables() const table_map used_tables() const
......
...@@ -278,15 +278,30 @@ bool handle_select(THD *thd, LEX *lex, select_result *result, ...@@ -278,15 +278,30 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
ref_pointer_array Array of references to Items used in current select ref_pointer_array Array of references to Items used in current select
DESCRIPTION DESCRIPTION
The function fixes fields referenced from inner selects and The function serves 3 purposes - adds fields referenced from inner
also fixes references (Item_ref objects) to these fields. Each field selects to the current select list, resolves which class to use
is fixed as a usual hidden field of the current select - it is added to access referenced item (Item_ref of Item_direct_ref) and fixes
to the all_fields list and the pointer to it is saved in the references (Item_ref objects) to these fields.
ref_pointer_array if latter is provided.
After the field has been fixed we proceed with fixing references If a field isn't already in the select list and the ref_pointer_array
(Item_ref objects) to this field from inner subqueries. If the is provided then it is added to the all_fields list and the pointer to
ref_pointer_array is provided then Item_ref objects is set to it is saved in the ref_pointer_array.
reference element in that array with the pointer to the field.
The class to access the outer field is determined by the following rules:
1. If the outer field isn't used under an aggregate function
then the Item_ref class should be used.
2. If the outer field is used under an aggregate function and this
function is aggregated in the select where the outer field was
resolved or in some more inner select then the Item_direct_ref
class should be used.
The resolution is done here and not at the fix_fields() stage as
it can be done only after sum functions are fixed and pulled up to
selects where they are have to be aggregated.
When the class is chosen it substitutes the original field in the
Item_outer_ref object.
After this we proceed with fixing references (Item_outer_ref objects) to
this field from inner subqueries.
RETURN RETURN
TRUE an error occured TRUE an error occured
...@@ -299,33 +314,64 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select, ...@@ -299,33 +314,64 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
{ {
Item_outer_ref *ref; Item_outer_ref *ref;
bool res= FALSE; bool res= FALSE;
bool direct_ref= FALSE;
List_iterator<Item_outer_ref> ref_it(select->inner_refs_list); List_iterator<Item_outer_ref> ref_it(select->inner_refs_list);
while ((ref= ref_it++)) while ((ref= ref_it++))
{ {
Item_field *item= ref->outer_field; Item *item= ref->outer_ref;
Item **item_ref= ref->ref;
Item_ref *new_ref;
/* /*
TODO: this field item already might be present in the select list. TODO: this field item already might be present in the select list.
In this case instead of adding new field item we could use an In this case instead of adding new field item we could use an
existing one. The change will lead to less operations for copying fields, existing one. The change will lead to less operations for copying fields,
smaller temporary tables and less data passed through filesort. smaller temporary tables and less data passed through filesort.
*/ */
if (ref_pointer_array) if (ref_pointer_array && !ref->found_in_select_list)
{ {
int el= all_fields.elements; int el= all_fields.elements;
ref_pointer_array[el]= (Item*)item; ref_pointer_array[el]= item;
/* Add the field item to the select list of the current select. */ /* Add the field item to the select list of the current select. */
all_fields.push_front((Item*)item); all_fields.push_front(item);
/* /*
If it's needed reset each Item_ref item that refers this field with If it's needed reset each Item_ref item that refers this field with
a new reference taken from ref_pointer_array. a new reference taken from ref_pointer_array.
*/ */
ref->ref= ref_pointer_array + el; item_ref= ref_pointer_array + el;
} }
if (!ref->fixed && ref->fix_fields(thd, 0))
if (ref->in_sum_func)
{ {
res= TRUE; Item_sum *sum_func;
break; if (ref->in_sum_func->nest_level > select->nest_level)
direct_ref= TRUE;
else
{
for (sum_func= ref->in_sum_func; sum_func &&
sum_func->aggr_level >= select->nest_level;
sum_func= sum_func->in_sum_func)
{
if (sum_func->aggr_level == select->nest_level)
{
direct_ref= TRUE;
break;
}
}
}
} }
new_ref= direct_ref ?
new Item_direct_ref(ref->context, item_ref, ref->field_name,
ref->table_name, ref->alias_name_used) :
new Item_ref(ref->context, item_ref, ref->field_name,
ref->table_name, ref->alias_name_used);
if (!new_ref)
return TRUE;
ref->outer_ref= new_ref;
ref->ref= &ref->outer_ref;
if (!ref->fixed && ref->fix_fields(thd, 0))
return TRUE;
thd->used_tables|= item->used_tables(); thd->used_tables|= item->used_tables();
} }
return res; return res;
...@@ -478,10 +524,6 @@ JOIN::prepare(Item ***rref_pointer_array, ...@@ -478,10 +524,6 @@ JOIN::prepare(Item ***rref_pointer_array,
if (having && having->with_sum_func) if (having && having->with_sum_func)
having->split_sum_func2(thd, ref_pointer_array, all_fields, having->split_sum_func2(thd, ref_pointer_array, all_fields,
&having, TRUE); &having, TRUE);
if (select_lex->inner_refs_list.elements &&
fix_inner_refs(thd, all_fields, select_lex, ref_pointer_array))
DBUG_RETURN(-1);
if (select_lex->inner_sum_func_list) if (select_lex->inner_sum_func_list)
{ {
Item_sum *end=select_lex->inner_sum_func_list; Item_sum *end=select_lex->inner_sum_func_list;
...@@ -494,6 +536,10 @@ JOIN::prepare(Item ***rref_pointer_array, ...@@ -494,6 +536,10 @@ JOIN::prepare(Item ***rref_pointer_array,
} while (item_sum != end); } while (item_sum != end);
} }
if (select_lex->inner_refs_list.elements &&
fix_inner_refs(thd, all_fields, select_lex, ref_pointer_array))
DBUG_RETURN(-1);
if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */ if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */
DBUG_RETURN(-1); DBUG_RETURN(-1);
...@@ -5214,7 +5260,9 @@ get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables, ...@@ -5214,7 +5260,9 @@ get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables,
} }
else if (keyuse->val->type() == Item::FIELD_ITEM || else if (keyuse->val->type() == Item::FIELD_ITEM ||
(keyuse->val->type() == Item::REF_ITEM && (keyuse->val->type() == Item::REF_ITEM &&
((Item_ref*)keyuse->val)->ref_type() == Item_ref::OUTER_REF) ) ((Item_ref*)keyuse->val)->ref_type() == Item_ref::OUTER_REF &&
(*(Item_ref**)((Item_ref*)keyuse->val)->ref)->ref_type() ==
Item_ref::DIRECT_REF) )
return new store_key_field(thd, return new store_key_field(thd,
key_part->field, key_part->field,
key_buff + maybe_null, key_buff + maybe_null,
......
...@@ -743,6 +743,7 @@ bool st_select_lex::cleanup() ...@@ -743,6 +743,7 @@ bool st_select_lex::cleanup()
error= (bool) ((uint) error | (uint) lex_unit->cleanup()); error= (bool) ((uint) error | (uint) lex_unit->cleanup());
} }
non_agg_fields.empty(); non_agg_fields.empty();
inner_refs_list.empty();
DBUG_RETURN(error); DBUG_RETURN(error);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment