Below is the list of changes that have just been committed into a local
5.0 repository of evgen. When evgen does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2007-04-01 02:00:09+04:00, evgen@stripped +7 -0
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 the Item_ref should be used to represent such a field.
mysql-test/r/subselect.result@stripped, 2007-04-01 01:48:48+04:00, evgen@stripped +15
-3
Added a test case for the bug#27321: Wrong subquery result in a grouping select.
Some test cases were corrected after this fix.
mysql-test/r/subselect3.result@stripped, 2007-04-01 01:50:06+04:00, evgen@stripped +2
-8
Some test cases were corrected after the fix for the bug#27321.
mysql-test/t/subselect.test@stripped, 2007-04-01 01:47:49+04:00, evgen@stripped +18
-0
Added a test case for the bug#27321: Wrong subquery result in a grouping select.
mysql-test/t/subselect3.test@stripped, 2007-04-01 01:51:06+04:00, evgen@stripped +0 -2
Some test cases were corrected after the fix for the bug#27321.
sql/item.cc@stripped, 2007-04-01 01:59:40+04:00, evgen@stripped +95 -30
Bug#27321: Wrong subquery result in a grouping select.
sql/item.h@stripped, 2007-04-01 01:59:36+04:00, evgen@stripped +29 -8
Bug#27321: Wrong subquery result in a grouping select.
sql/sql_select.cc@stripped, 2007-04-01 01:56:44+04:00, evgen@stripped +29 -9
Bug#27321: Wrong subquery result in a grouping select.
# This is a BitKeeper patch. What follows are the unified diffs for the
# set of deltas contained in the patch. The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User: evgen
# Host: moonbone.local
# Root: /mnt/gentoo64/work/27321-bug-5.0-opt-mysql
--- 1.259/sql/item.cc 2007-03-23 00:51:14 +03:00
+++ 1.260/sql/item.cc 2007-04-01 01:59:40 +04:00
@@ -3447,6 +3447,7 @@
Item **ref= (Item **) not_found_item;
SELECT_LEX *current_sel= (SELECT_LEX *) thd->lex->current_select;
Name_resolution_context *outer_context= 0;
+ SELECT_LEX *select;
/* Currently derived tables cannot be correlated */
if (current_sel->master_unit()->first_select()->linkage !=
DERIVED_TABLE_TYPE)
@@ -3455,7 +3456,7 @@
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=
last_checked_context->select_lex->master_unit()->item;
last_checked_context= outer_context;
@@ -3498,11 +3499,11 @@
}
if (*from_field != view_ref_found)
{
-
prev_subselect_item->used_tables_cache|= (*from_field)->table->map;
prev_subselect_item->const_item_cache= 0;
+ set_field(*from_field);
if (!last_checked_context->select_lex->having_fix_field &&
- !fixed_as_field)
+ select->group_list.elements && !fixed_as_field)
{
Item_outer_ref *rf;
Query_arena *arena= 0, backup;
@@ -3523,7 +3524,6 @@
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);
rf= new Item_outer_ref(context, this);
if (!rf)
@@ -3537,6 +3537,7 @@
if (arena)
thd->restore_active_arena(arena, &backup);
fixed_as_field= 1;
+ rf->in_sum_func= thd->lex->in_sum_func;
}
if (thd->lex->in_sum_func &&
thd->lex->in_sum_func->nest_level ==
@@ -3628,37 +3629,55 @@
{
Item *save;
Item_ref *rf;
-
/* Should have been checked in resolve_ref_in_select_and_group(). */
DBUG_ASSERT(*ref && (*ref)->fixed);
- /*
- Here, a subset of actions performed by Item_ref::set_properties
- is not enough. So we pass ptr to NULL into Item_[direct]_ref
- constructor, so no initialization is performed, and call
- fix_fields() below.
- */
save= *ref;
*ref= NULL; // Don't call set_properties()
- rf= (place == IN_HAVING ?
- new Item_ref(context, ref, (char*) table_name,
- (char*) field_name, alias_name_used) :
- new Item_direct_ref(context, ref, (char*) table_name,
- (char*) field_name, alias_name_used));
- *ref= save;
- if (!rf)
- return -1;
- thd->change_item_tree(reference, rf);
+ if (place == IN_HAVING || !select->group_list.elements)
+ {
+ /*
+ Here, a subset of actions performed by Item_ref::set_properties
+ is not enough. So we pass ptr to NULL into Item_[direct]_ref
+ constructor, so no initialization is performed, and call
+ fix_fields() below.
+ */
+ rf= (place == IN_HAVING) ?
+ new Item_ref(context, ref, (char*) table_name,
+ (char*) field_name, alias_name_used) :
+ new Item_direct_ref(context, ref, (char*) table_name,
+ (char*) field_name, alias_name_used);
+ if (!rf)
+ return -1;
+ thd->change_item_tree(reference, rf);
+ }
+ else
+ {
+ Query_arena *arena= 0, backup;
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+ rf= new Item_outer_ref(context, ref, (char*) table_name,
+ (char*) field_name, alias_name_used);
+ if (!rf)
+ {
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ return -1;
+ }
+ *reference= rf;
+ outer_context->select_lex->inner_refs_list.push_back((Item_outer_ref*)rf);
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ fixed_as_field= 1;
+ }
/*
rf is Item_ref => never substitute other items (in this case)
during fix_fields() => we can use rf after fix_fields()
*/
DBUG_ASSERT(!rf->fixed); // Assured by Item_ref()
+ *ref= save;
if (rf->fix_fields(thd, reference) || rf->check_cols(1))
return -1;
-
mark_as_dependent(thd, last_checked_context->select_lex,
- context->select_lex, this,
- rf);
+ context->select_lex, this, rf);
return 0;
}
else
@@ -5547,15 +5566,61 @@
bool Item_outer_ref::fix_fields(THD *thd, Item **reference)
{
- DBUG_ASSERT(*ref);
- /* outer_field->check_cols() will be made in Item_direct_ref::fix_fields */
- outer_field->fixed_as_field= 1;
- if (!outer_field->fixed &&
- (outer_field->fix_fields(thd, reference)))
+ bool err;
+ /* outer_ref->check_cols() will be made in Item_direct_ref::fix_fields */
+ if ((*ref) && !(*ref)->fixed && ((*ref)->fix_fields(thd,
reference)))
return TRUE;
- table_name= outer_field->table_name;
- return Item_direct_ref::fix_fields(thd, reference);
+ err= 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;
+}
+
+double Item_outer_ref::val_real()
+{
+ DBUG_ASSERT(fixed);
+ double tmp= direct_ref ? (*ref)->val_real() : (*ref)->val_result();
+ null_value=(*ref)->null_value;
+ return tmp;
}
+
+
+longlong Item_outer_ref::val_int()
+{
+ DBUG_ASSERT(fixed);
+ longlong tmp= direct_ref ? (*ref)->val_int() :(*ref)->val_int_result();
+ null_value=(*ref)->null_value;
+ return tmp;
+}
+
+
+bool Item_outer_ref::val_bool()
+{
+ DBUG_ASSERT(fixed);
+ bool tmp= direct_ref ? (*ref)->val_bool() : (*ref)->val_bool_result();
+ null_value= (*ref)->null_value;
+ return tmp;
+}
+
+
+String *Item_outer_ref::val_str(String* tmp)
+{
+ DBUG_ASSERT(fixed);
+ tmp= direct_ref ? (*ref)->val_str(tmp) : (*ref)->str_result(tmp);
+ null_value=(*ref)->null_value;
+ return tmp;
+}
+
+my_decimal *Item_outer_ref::val_decimal(my_decimal *decimal_value)
+{
+ my_decimal *val= direct_ref ? (*ref)->val_decimal(decimal_value) :
+ (*ref)->val_decimal_result(decimal_value);
+ null_value= (*ref)->null_value;
+ return val;
+}
+
/*
Compare two view column references for equality.
--- 1.224/sql/item.h 2007-03-22 11:21:02 +03:00
+++ 1.225/sql/item.h 2007-04-01 01:59:36 +04:00
@@ -1978,31 +1978,47 @@
virtual Ref_Type ref_type() { return VIEW_REF; }
};
-
+class Item_sum;
class Item_outer_ref :public Item_direct_ref
{
public:
- Item_field *outer_field;
+ Item *outer_ref;
+ Item_sum *in_sum_func;
+ /* To indicate which methods to use: val_xxx() or val_xxx_result() */
+ bool direct_ref;
+ /*
+ To indicate 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_field *outer_field_arg)
:Item_direct_ref(context_arg, 0, outer_field_arg->table_name,
- outer_field_arg->field_name),
- outer_field(outer_field_arg)
+ outer_field_arg->field_name),
+ outer_ref(outer_field_arg), in_sum_func(0), direct_ref(0),
+ found_in_select_list(0)
{
- ref= (Item**)&outer_field;
+ ref= &outer_ref;
set_properties();
fixed= 0;
}
+ Item_outer_ref(Name_resolution_context *context_arg, Item **item,
+ const char *table_name_arg, const char *field_name_arg,
+ bool alias_name_used_arg)
+ :Item_direct_ref(context_arg, item, table_name_arg, field_name_arg,
+ alias_name_used_arg),
+ outer_ref(0), in_sum_func(0), direct_ref(0), found_in_select_list(1)
+ {}
void cleanup()
{
- ref= (Item**)&outer_field;
+ ref= (Item**)&outer_ref;
fixed= 0;
Item_direct_ref::cleanup();
- outer_field->cleanup();
+ outer_ref->cleanup();
}
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 **);
table_map used_tables() const
@@ -2010,6 +2026,11 @@
return (*ref)->const_item() ? 0 : OUTER_REF_TABLE_BIT;
}
virtual Ref_Type ref_type() { return OUTER_REF; }
+ double val_real();
+ longlong val_int();
+ bool val_bool();
+ String *val_str(String* tmp);
+ my_decimal *val_decimal(my_decimal *decimal_value);
};
--- 1.502/sql/sql_select.cc 2007-03-26 10:44:03 +04:00
+++ 1.503/sql/sql_select.cc 2007-04-01 01:56:44 +04:00
@@ -302,25 +302,44 @@
List_iterator<Item_outer_ref> ref_it(select->inner_refs_list);
while ((ref= ref_it++))
{
- Item_field *item= ref->outer_field;
+ Item *item= ref->outer_ref;
/*
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
existing one. The change will lead to less operations for copying fields,
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;
- ref_pointer_array[el]= (Item*)item;
+ ref_pointer_array[el]= item;
/* 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
a new reference taken from ref_pointer_array.
*/
ref->ref= ref_pointer_array + el;
}
+
+ if (ref->in_sum_func)
+ {
+ if (ref->in_sum_func->nest_level > select->nest_level)
+ ref->direct_ref= TRUE;
+ else
+ {
+ Item_sum *sum_func;
+ for (sum_func= ref->in_sum_func; sum_func;
+ sum_func= sum_func->in_sum_func)
+ {
+ if (sum_func->depended_from() == select)
+ {
+ ref->direct_ref= TRUE;
+ break;
+ }
+ }
+ }
+ }
if (!ref->fixed && ref->fix_fields(thd, 0))
{
res= TRUE;
@@ -478,10 +497,6 @@
if (having && having->with_sum_func)
having->split_sum_func2(thd, ref_pointer_array, all_fields,
&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)
{
Item_sum *end=select_lex->inner_sum_func_list;
@@ -494,6 +509,10 @@
} 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 */
DBUG_RETURN(-1);
@@ -5214,7 +5233,8 @@
}
else if (keyuse->val->type() == Item::FIELD_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_outer_ref*)keyuse->val)->direct_ref) )
return new store_key_field(thd,
key_part->field,
key_buff + maybe_null,
--- 1.181/mysql-test/r/subselect.result 2007-03-23 00:48:00 +03:00
+++ 1.182/mysql-test/r/subselect.result 2007-04-01 01:48:48 +04:00
@@ -48,7 +48,7 @@
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
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 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;
@@ -330,7 +330,7 @@
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
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:
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`))
@@ -1741,7 +1741,7 @@
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
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:
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))))
@@ -3924,3 +3924,15 @@
3 3 4
1 4 2,2
DROP table t1,t2;
+CREATE TABLE t1 (a int, b INT, c CHAR(10) NOT NULL, PRIMARY KEY (a, b));
+INSERT INTO t1 VALUES (1,1,'a'), (1,2,'b'), (1,3,'c'), (1,4,'d'), (1,5,'e'),
+(2,1,'f'), (2,2,'g'), (2,3,'h'), (3,4,'i'),(3,3,'j'), (3,2,'k'), (3,1,'l'),
+(1,9,'m');
+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
+DROP TABLE t1;
--- 1.146/mysql-test/t/subselect.test 2007-03-23 00:48:00 +03:00
+++ 1.147/mysql-test/t/subselect.test 2007-04-01 01:47:49 +04:00
@@ -2782,3 +2782,21 @@
FROM t1 GROUP BY a;
DROP table t1,t2;
+
+#
+# Bug#27321: Wrong subquery result in a grouping select
+#
+CREATE TABLE t1 (a int, b INT, c CHAR(10) NOT NULL, PRIMARY KEY (a, b));
+INSERT INTO t1 VALUES (1,1,'a'), (1,2,'b'), (1,3,'c'), (1,4,'d'), (1,5,'e'),
+(2,1,'f'), (2,2,'g'), (2,3,'h'), (3,4,'i'),(3,3,'j'), (3,2,'k'), (3,1,'l'),
+(1,9,'m');
+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 GROUP BY a;
+DROP TABLE t1;
--- 1.6/mysql-test/r/subselect3.result 2007-03-20 20:49:35 +03:00
+++ 1.7/mysql-test/r/subselect3.result 2007-04-01 01:50:06 +04:00
@@ -432,7 +432,7 @@
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
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;
oref a Z
ee NULL NULL
@@ -457,7 +457,7 @@
from t2;
id select_type table type possible_keys key key_len ref rows Extra
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,
a in (select min(ie) from t1 where oref=t2.oref
group by grp having min(ie) > 1) Z
@@ -661,12 +661,6 @@
HAVING (MAX(t1.b) > (SELECT MAX(t2.b) FROM t2 WHERE t2.c < t1.c
HAVING MAX(t2.b+t1.a) < 10));
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;
a b c
1 3 c
--- 1.6/mysql-test/t/subselect3.test 2007-03-20 20:45:49 +03:00
+++ 1.7/mysql-test/t/subselect3.test 2007-04-01 01:51:06 +04:00
@@ -507,8 +507,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(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;
| Thread |
|---|
| • bk commit into 5.0 tree (evgen:1.2421) BUG#27321 | eugene | 31 Mar |