Bug #45640: optimizer bug produces wrong results
Grouping by a subquery in a query with a distinct aggregate function lead to a wrong result (wrong and unordered grouping values). There are two related problems: 1) The query like this: SELECT (SELECT t1.a) aa, COUNT(DISTINCT b) c FROM t1 GROUP BY aa returned wrong result, because the outer reference "t1.a" in the subquery was substituted with the Item_ref item. The Item_ref item obtains data from the result_field object that refreshes once after the end of each group. This data is not applicable to filesort since filesort() doesn't care about groups (and doesn't update result_field objects with copy_fields() and so on). Also that data is not applicable to group separation algorithm: end_send_group() checks every record with test_if_group_changed() that evaluates Item_ref items, but it refreshes those Item_ref-s only after the end of group, that is a vicious circle and the grouped column values in the output are shifted. Fix: if a) we grouping by a subquery and b) that subquery has outer references to FROM list of the grouping query, then we substitute these outer references with Item_direct_ref like references under aggregate functions: Item_direct_ref obtains data directly from the current record. 2) The query with a non-trivial grouping expression like: SELECT (SELECT t1.a) aa, COUNT(DISTINCT b) c FROM t1 GROUP BY aa+0 also returned wrong result, since JOIN::exec() substitutes references to top-level aliases in SELECT list with Item_copy caching items. Item_copy items have same refreshing policy as Item_ref items, so the whole groping expression with Item_copy inside returns wrong result in filesort() and end_send_group(). Fix: include aliased items into GROUP BY item tree instead of Item_ref references to them. mysql-test/r/group_by.result: Test case for bug #45640 mysql-test/t/group_by.test: Test case for bug #45640 sql/item.cc: Bug #45640: optimizer bug produces wrong results Item_field::fix_fields() has been modified to resolve aliases in GROUP BY item trees into aliased items instead of Item_ref items. sql/item.h: Bug #45640: optimizer bug produces wrong results - Item::find_item_processor() has been introduced. - Item_ref::walk() has been modified to apply processors to itself too (not only to referenced item). sql/mysql_priv.h: Bug #45640: optimizer bug produces wrong results fix_inner_refs() has been modified to accept group_list parameter. sql/sql_lex.cc: Bug #45640: optimizer bug produces wrong results Initialization of st_select_lex::group_fix_field has been added. sql/sql_lex.h: Bug #45640: optimizer bug produces wrong results The st_select_lex::group_fix_field field has been introduced to control alias resolution in Itef_fied::fix_fields. sql/sql_select.cc: Bug #45640: optimizer bug produces wrong results - The fix_inner_refs function has been modified to treat subquery outer references like outer fields under aggregate functions, if they are included in GROUP BY item tree. - The find_order_in_list function has been modified to fix Item_field alias fields included in the GROUP BY item trees in a special manner.
This commit is contained in:
parent
3ad5d21ebc
commit
994c0f8308
@ -1703,3 +1703,91 @@ COUNT(i)
|
|||||||
1
|
1
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
SET @@sql_mode = @old_sql_mode;
|
SET @@sql_mode = @old_sql_mode;
|
||||||
|
#
|
||||||
|
# Bug #45640: optimizer bug produces wrong results
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (a INT, b INT);
|
||||||
|
INSERT INTO t1 VALUES (4, 40), (1, 10), (2, 20), (2, 20), (3, 30);
|
||||||
|
# should return 4 ordered records:
|
||||||
|
SELECT (SELECT t1.a) aa, COUNT(DISTINCT b) FROM t1 GROUP BY aa;
|
||||||
|
aa COUNT(DISTINCT b)
|
||||||
|
1 1
|
||||||
|
2 1
|
||||||
|
3 1
|
||||||
|
4 1
|
||||||
|
SELECT (SELECT (SELECT t1.a)) aa, COUNT(DISTINCT b) FROM t1 GROUP BY aa;
|
||||||
|
aa COUNT(DISTINCT b)
|
||||||
|
1 1
|
||||||
|
2 1
|
||||||
|
3 1
|
||||||
|
4 1
|
||||||
|
SELECT (SELECT t1.a) aa, COUNT(DISTINCT b) FROM t1 GROUP BY aa+0;
|
||||||
|
aa COUNT(DISTINCT b)
|
||||||
|
1 1
|
||||||
|
2 1
|
||||||
|
3 1
|
||||||
|
4 1
|
||||||
|
# should return the same result in a reverse order:
|
||||||
|
SELECT (SELECT t1.a) aa, COUNT(DISTINCT b) FROM t1 GROUP BY -aa;
|
||||||
|
aa COUNT(DISTINCT b)
|
||||||
|
4 1
|
||||||
|
3 1
|
||||||
|
2 1
|
||||||
|
1 1
|
||||||
|
# execution plan should not use temporary table:
|
||||||
|
EXPLAIN EXTENDED
|
||||||
|
SELECT (SELECT t1.a) aa, COUNT(DISTINCT b) FROM t1 GROUP BY aa+0;
|
||||||
|
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||||
|
1 PRIMARY t1 ALL NULL NULL NULL NULL 5 100.00 Using filesort
|
||||||
|
2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
|
||||||
|
Warnings:
|
||||||
|
Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1
|
||||||
|
Note 1003 select (select `test`.`t1`.`a` AS `a`) AS `aa`,count(distinct `test`.`t1`.`b`) AS `COUNT(DISTINCT b)` from `test`.`t1` group by ((select `test`.`t1`.`a` AS `a`) + 0)
|
||||||
|
EXPLAIN EXTENDED
|
||||||
|
SELECT (SELECT t1.a) aa, COUNT(DISTINCT b) FROM t1 GROUP BY -aa;
|
||||||
|
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||||
|
1 PRIMARY t1 ALL NULL NULL NULL NULL 5 100.00 Using filesort
|
||||||
|
2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
|
||||||
|
Warnings:
|
||||||
|
Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1
|
||||||
|
Note 1003 select (select `test`.`t1`.`a` AS `a`) AS `aa`,count(distinct `test`.`t1`.`b`) AS `COUNT(DISTINCT b)` from `test`.`t1` group by -((select `test`.`t1`.`a` AS `a`))
|
||||||
|
# should return only one record
|
||||||
|
SELECT (SELECT tt.a FROM t1 tt LIMIT 1) aa, COUNT(DISTINCT b) FROM t1
|
||||||
|
GROUP BY aa;
|
||||||
|
aa COUNT(DISTINCT b)
|
||||||
|
4 4
|
||||||
|
CREATE TABLE t2 SELECT DISTINCT a FROM t1;
|
||||||
|
# originally reported queries (1st two columns of next two query
|
||||||
|
# results should be same):
|
||||||
|
SELECT (SELECT t2.a FROM t2 WHERE t2.a = t1.a) aa, b, COUNT(DISTINCT b)
|
||||||
|
FROM t1 GROUP BY aa, b;
|
||||||
|
aa b COUNT(DISTINCT b)
|
||||||
|
1 10 1
|
||||||
|
2 20 1
|
||||||
|
3 30 1
|
||||||
|
4 40 1
|
||||||
|
SELECT (SELECT t2.a FROM t2 WHERE t2.a = t1.a) aa, b, COUNT( b)
|
||||||
|
FROM t1 GROUP BY aa, b;
|
||||||
|
aa b COUNT( b)
|
||||||
|
1 10 1
|
||||||
|
2 20 2
|
||||||
|
3 30 1
|
||||||
|
4 40 1
|
||||||
|
# ORDER BY for sure:
|
||||||
|
SELECT (SELECT t2.a FROM t2 WHERE t2.a = t1.a) aa, b, COUNT(DISTINCT b)
|
||||||
|
FROM t1 GROUP BY aa, b ORDER BY -aa, -b;
|
||||||
|
aa b COUNT(DISTINCT b)
|
||||||
|
4 40 1
|
||||||
|
3 30 1
|
||||||
|
2 20 1
|
||||||
|
1 10 1
|
||||||
|
SELECT (SELECT t2.a FROM t2 WHERE t2.a = t1.a) aa, b, COUNT( b)
|
||||||
|
FROM t1 GROUP BY aa, b ORDER BY -aa, -b;
|
||||||
|
aa b COUNT( b)
|
||||||
|
4 40 1
|
||||||
|
3 30 1
|
||||||
|
2 20 2
|
||||||
|
1 10 1
|
||||||
|
DROP TABLE t1, t2;
|
||||||
|
#
|
||||||
|
# End of 5.1 tests
|
||||||
|
@ -1158,3 +1158,53 @@ SELECT COUNT(i) FROM t1 WHERE i > 1;
|
|||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
SET @@sql_mode = @old_sql_mode;
|
SET @@sql_mode = @old_sql_mode;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Bug #45640: optimizer bug produces wrong results
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a INT, b INT);
|
||||||
|
INSERT INTO t1 VALUES (4, 40), (1, 10), (2, 20), (2, 20), (3, 30);
|
||||||
|
|
||||||
|
--echo # should return 4 ordered records:
|
||||||
|
SELECT (SELECT t1.a) aa, COUNT(DISTINCT b) FROM t1 GROUP BY aa;
|
||||||
|
|
||||||
|
SELECT (SELECT (SELECT t1.a)) aa, COUNT(DISTINCT b) FROM t1 GROUP BY aa;
|
||||||
|
|
||||||
|
SELECT (SELECT t1.a) aa, COUNT(DISTINCT b) FROM t1 GROUP BY aa+0;
|
||||||
|
|
||||||
|
--echo # should return the same result in a reverse order:
|
||||||
|
SELECT (SELECT t1.a) aa, COUNT(DISTINCT b) FROM t1 GROUP BY -aa;
|
||||||
|
|
||||||
|
--echo # execution plan should not use temporary table:
|
||||||
|
EXPLAIN EXTENDED
|
||||||
|
SELECT (SELECT t1.a) aa, COUNT(DISTINCT b) FROM t1 GROUP BY aa+0;
|
||||||
|
|
||||||
|
EXPLAIN EXTENDED
|
||||||
|
SELECT (SELECT t1.a) aa, COUNT(DISTINCT b) FROM t1 GROUP BY -aa;
|
||||||
|
|
||||||
|
--echo # should return only one record
|
||||||
|
SELECT (SELECT tt.a FROM t1 tt LIMIT 1) aa, COUNT(DISTINCT b) FROM t1
|
||||||
|
GROUP BY aa;
|
||||||
|
|
||||||
|
CREATE TABLE t2 SELECT DISTINCT a FROM t1;
|
||||||
|
|
||||||
|
--echo # originally reported queries (1st two columns of next two query
|
||||||
|
--echo # results should be same):
|
||||||
|
|
||||||
|
SELECT (SELECT t2.a FROM t2 WHERE t2.a = t1.a) aa, b, COUNT(DISTINCT b)
|
||||||
|
FROM t1 GROUP BY aa, b;
|
||||||
|
SELECT (SELECT t2.a FROM t2 WHERE t2.a = t1.a) aa, b, COUNT( b)
|
||||||
|
FROM t1 GROUP BY aa, b;
|
||||||
|
|
||||||
|
--echo # ORDER BY for sure:
|
||||||
|
|
||||||
|
SELECT (SELECT t2.a FROM t2 WHERE t2.a = t1.a) aa, b, COUNT(DISTINCT b)
|
||||||
|
FROM t1 GROUP BY aa, b ORDER BY -aa, -b;
|
||||||
|
SELECT (SELECT t2.a FROM t2 WHERE t2.a = t1.a) aa, b, COUNT( b)
|
||||||
|
FROM t1 GROUP BY aa, b ORDER BY -aa, -b;
|
||||||
|
|
||||||
|
DROP TABLE t1, t2;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
--echo # End of 5.1 tests
|
||||||
|
26
sql/item.cc
26
sql/item.cc
@ -4300,17 +4300,33 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
|
|||||||
It's not an Item_field in the select list so we must make a new
|
It's not an Item_field in the select list so we must make a new
|
||||||
Item_ref to point to the Item in the select list and replace the
|
Item_ref to point to the Item in the select list and replace the
|
||||||
Item_field created by the parser with the new Item_ref.
|
Item_field created by the parser with the new Item_ref.
|
||||||
|
|
||||||
|
NOTE: If we are fixing an alias reference inside ORDER/GROUP BY
|
||||||
|
item tree, then we use new Item_ref as an intermediate value
|
||||||
|
to resolve referenced item only.
|
||||||
|
In this case the new Item_ref item is unused.
|
||||||
*/
|
*/
|
||||||
Item_ref *rf= new Item_ref(context, db_name,table_name,field_name);
|
Item_ref *rf= new Item_ref(context, db_name,table_name,field_name);
|
||||||
if (!rf)
|
if (!rf)
|
||||||
return 1;
|
return 1;
|
||||||
thd->change_item_tree(reference, rf);
|
|
||||||
|
bool save_group_fix_field= thd->lex->current_select->group_fix_field;
|
||||||
/*
|
/*
|
||||||
Because Item_ref never substitutes itself with other items
|
No need for recursive resolving of aliases.
|
||||||
in Item_ref::fix_fields(), we can safely use the original
|
|
||||||
pointer to it even after fix_fields()
|
|
||||||
*/
|
*/
|
||||||
return rf->fix_fields(thd, reference) || rf->check_cols(1);
|
thd->lex->current_select->group_fix_field= 0;
|
||||||
|
|
||||||
|
bool ret= rf->fix_fields(thd, (Item **) &rf) || rf->check_cols(1);
|
||||||
|
thd->lex->current_select->group_fix_field= save_group_fix_field;
|
||||||
|
if (ret)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if (save_group_fix_field && alias_name_used)
|
||||||
|
thd->change_item_tree(reference, *rf->ref);
|
||||||
|
else
|
||||||
|
thd->change_item_tree(reference, rf);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -901,6 +901,7 @@ public:
|
|||||||
virtual bool change_context_processor(uchar *context) { return 0; }
|
virtual bool change_context_processor(uchar *context) { return 0; }
|
||||||
virtual bool reset_query_id_processor(uchar *query_id_arg) { return 0; }
|
virtual bool reset_query_id_processor(uchar *query_id_arg) { return 0; }
|
||||||
virtual bool is_expensive_processor(uchar *arg) { return 0; }
|
virtual bool is_expensive_processor(uchar *arg) { return 0; }
|
||||||
|
virtual bool find_item_processor(uchar *arg) { return this == (void *) arg; }
|
||||||
virtual bool register_field_in_read_map(uchar *arg) { return 0; }
|
virtual bool register_field_in_read_map(uchar *arg) { return 0; }
|
||||||
/*
|
/*
|
||||||
Check if a partition function is allowed
|
Check if a partition function is allowed
|
||||||
@ -2275,7 +2276,10 @@ public:
|
|||||||
return ref ? (*ref)->real_item() : this;
|
return ref ? (*ref)->real_item() : this;
|
||||||
}
|
}
|
||||||
bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
|
bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
|
||||||
{ return (*ref)->walk(processor, walk_subquery, arg); }
|
{
|
||||||
|
return (*ref)->walk(processor, walk_subquery, arg) ||
|
||||||
|
(this->*processor)(arg);
|
||||||
|
}
|
||||||
virtual void print(String *str, enum_query_type query_type);
|
virtual void print(String *str, enum_query_type query_type);
|
||||||
bool result_as_longlong()
|
bool result_as_longlong()
|
||||||
{
|
{
|
||||||
|
@ -1192,7 +1192,7 @@ int setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
|
|||||||
List<Item> &fields, List<Item> &all_fields, ORDER *order,
|
List<Item> &fields, List<Item> &all_fields, ORDER *order,
|
||||||
bool *hidden_group_fields);
|
bool *hidden_group_fields);
|
||||||
bool fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
|
bool fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
|
||||||
Item **ref_pointer_array);
|
Item **ref_pointer_array, ORDER *group_list= NULL);
|
||||||
|
|
||||||
bool handle_select(THD *thd, LEX *lex, select_result *result,
|
bool handle_select(THD *thd, LEX *lex, select_result *result,
|
||||||
ulong setup_tables_done_option);
|
ulong setup_tables_done_option);
|
||||||
|
@ -1592,6 +1592,7 @@ void st_select_lex::init_query()
|
|||||||
having= prep_having= where= prep_where= 0;
|
having= prep_having= where= prep_where= 0;
|
||||||
olap= UNSPECIFIED_OLAP_TYPE;
|
olap= UNSPECIFIED_OLAP_TYPE;
|
||||||
having_fix_field= 0;
|
having_fix_field= 0;
|
||||||
|
group_fix_field= 0;
|
||||||
context.select_lex= this;
|
context.select_lex= this;
|
||||||
context.init();
|
context.init();
|
||||||
/*
|
/*
|
||||||
|
@ -647,6 +647,8 @@ public:
|
|||||||
bool braces; /* SELECT ... UNION (SELECT ... ) <- this braces */
|
bool braces; /* SELECT ... UNION (SELECT ... ) <- this braces */
|
||||||
/* TRUE when having fix field called in processing of this SELECT */
|
/* TRUE when having fix field called in processing of this SELECT */
|
||||||
bool having_fix_field;
|
bool having_fix_field;
|
||||||
|
/* TRUE when GROUP BY fix field called in processing of this SELECT */
|
||||||
|
bool group_fix_field;
|
||||||
/* List of references to fields referenced from inner selects */
|
/* List of references to fields referenced from inner selects */
|
||||||
List<Item_outer_ref> inner_refs_list;
|
List<Item_outer_ref> inner_refs_list;
|
||||||
/* Number of Item_sum-derived objects in this SELECT */
|
/* Number of Item_sum-derived objects in this SELECT */
|
||||||
|
@ -287,6 +287,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
|
|||||||
all_fields List of all fields used in select
|
all_fields List of all fields used in select
|
||||||
select Current select
|
select Current select
|
||||||
ref_pointer_array Array of references to Items used in current select
|
ref_pointer_array Array of references to Items used in current select
|
||||||
|
group_list GROUP BY list (is NULL by default)
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
The function serves 3 purposes - adds fields referenced from inner
|
The function serves 3 purposes - adds fields referenced from inner
|
||||||
@ -305,6 +306,8 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
|
|||||||
function is aggregated in the select where the outer field was
|
function is aggregated in the select where the outer field was
|
||||||
resolved or in some more inner select then the Item_direct_ref
|
resolved or in some more inner select then the Item_direct_ref
|
||||||
class should be used.
|
class should be used.
|
||||||
|
Also it should be used if we are grouping by a subquery containing
|
||||||
|
the outer field.
|
||||||
The resolution is done here and not at the fix_fields() stage as
|
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
|
it can be done only after sum functions are fixed and pulled up to
|
||||||
selects where they are have to be aggregated.
|
selects where they are have to be aggregated.
|
||||||
@ -321,7 +324,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
|
fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
|
||||||
Item **ref_pointer_array)
|
Item **ref_pointer_array, ORDER *group_list)
|
||||||
{
|
{
|
||||||
Item_outer_ref *ref;
|
Item_outer_ref *ref;
|
||||||
bool res= FALSE;
|
bool res= FALSE;
|
||||||
@ -371,6 +374,22 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Check if GROUP BY item trees contain the outer ref:
|
||||||
|
in this case we have to use Item_direct_ref instead of Item_ref.
|
||||||
|
*/
|
||||||
|
for (ORDER *group= group_list; group; group= group->next)
|
||||||
|
{
|
||||||
|
if ((*group->item)->walk(&Item::find_item_processor, TRUE,
|
||||||
|
(uchar *) ref))
|
||||||
|
{
|
||||||
|
direct_ref= TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
new_ref= direct_ref ?
|
new_ref= direct_ref ?
|
||||||
new Item_direct_ref(ref->context, item_ref, ref->table_name,
|
new Item_direct_ref(ref->context, item_ref, ref->table_name,
|
||||||
ref->field_name, ref->alias_name_used) :
|
ref->field_name, ref->alias_name_used) :
|
||||||
@ -577,7 +596,8 @@ JOIN::prepare(Item ***rref_pointer_array,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (select_lex->inner_refs_list.elements &&
|
if (select_lex->inner_refs_list.elements &&
|
||||||
fix_inner_refs(thd, all_fields, select_lex, ref_pointer_array))
|
fix_inner_refs(thd, all_fields, select_lex, ref_pointer_array,
|
||||||
|
group_list))
|
||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
|
|
||||||
if (group_list)
|
if (group_list)
|
||||||
@ -14547,11 +14567,29 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
|
|||||||
|
|
||||||
We check order_item->fixed because Item_func_group_concat can put
|
We check order_item->fixed because Item_func_group_concat can put
|
||||||
arguments for which fix_fields already was called.
|
arguments for which fix_fields already was called.
|
||||||
|
|
||||||
|
group_fix_field= TRUE is to resolve aliases from the SELECT list
|
||||||
|
without creating of Item_ref-s: JOIN::exec() wraps aliased items
|
||||||
|
in SELECT list with Item_copy items. To re-evaluate such a tree
|
||||||
|
that includes Item_copy items we have to refresh Item_copy caches,
|
||||||
|
but:
|
||||||
|
- filesort() never refresh Item_copy items,
|
||||||
|
- end_send_group() checks every record for group boundary by the
|
||||||
|
test_if_group_changed function that obtain data from these
|
||||||
|
Item_copy items, but the copy_fields function that
|
||||||
|
refreshes Item copy items is called after group boundaries only -
|
||||||
|
that is a vicious circle.
|
||||||
|
So we prevent inclusion of Item_copy items.
|
||||||
*/
|
*/
|
||||||
if (!order_item->fixed &&
|
bool save_group_fix_field= thd->lex->current_select->group_fix_field;
|
||||||
|
if (is_group_field)
|
||||||
|
thd->lex->current_select->group_fix_field= TRUE;
|
||||||
|
bool ret= (!order_item->fixed &&
|
||||||
(order_item->fix_fields(thd, order->item) ||
|
(order_item->fix_fields(thd, order->item) ||
|
||||||
(order_item= *order->item)->check_cols(1) ||
|
(order_item= *order->item)->check_cols(1) ||
|
||||||
thd->is_fatal_error))
|
thd->is_fatal_error));
|
||||||
|
thd->lex->current_select->group_fix_field= save_group_fix_field;
|
||||||
|
if (ret)
|
||||||
return TRUE; /* Wrong field. */
|
return TRUE; /* Wrong field. */
|
||||||
|
|
||||||
uint el= all_fields.elements;
|
uint el= all_fields.elements;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user