Commit 4ee2e07c authored by evgen@moonbone.local's avatar evgen@moonbone.local

Fixed bug#19862: Sort with filesort by function evaluates function twice

When there is no index defined filesort is used to sort the result of a
query. If there is a function in the select list and the result set should be
ordered by it's value then this function will be evaluated twice. First time to
get the value of the sort key and second time to send its value to a user.
This happens because filesort when sorts a table remembers only values of its
fields but not values of functions.
All functions are affected. But taking into account that SP and UDF functions
can be both expensive and non-deterministic a temporary table should be used 
to store their results and then sort it to avoid twice SP evaluation and to 
get a correct result.

If an expression referenced in an ORDER clause contains a SP or UDF 
function, force the use of a temporary table.

A new Item_processor function called func_type_checker_processor is added
to check whether the expression contains a function of a particular type.
parent 8b7fc77a
...@@ -5057,4 +5057,23 @@ concat('data was: /', var1, '/') ...@@ -5057,4 +5057,23 @@ concat('data was: /', var1, '/')
data was: /1/ data was: /1/
drop table t3| drop table t3|
drop procedure bug15217| drop procedure bug15217|
drop procedure if exists bug19862|
CREATE TABLE t11 (a INT)|
CREATE TABLE t12 (a INT)|
CREATE FUNCTION bug19862(x INT) RETURNS INT
BEGIN
INSERT INTO t11 VALUES (x);
RETURN x+1;
END|
INSERT INTO t12 VALUES (1), (2)|
SELECT bug19862(a) FROM t12 ORDER BY 1|
bug19862(a)
2
3
SELECT * FROM t11|
a
1
2
DROP TABLE t11, t12|
DROP FUNCTION bug19862|
drop table t1,t2; drop table t1,t2;
...@@ -93,6 +93,12 @@ NULL ...@@ -93,6 +93,12 @@ NULL
0R 0R
FR FR
DROP TABLE bug19904; DROP TABLE bug19904;
create table t1(f1 int);
insert into t1 values(1),(2);
explain select myfunc_int(f1) from t1 order by 1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using temporary; Using filesort
drop table t1;
End of 5.0 tests. End of 5.0 tests.
DROP FUNCTION metaphon; DROP FUNCTION metaphon;
DROP FUNCTION myfunc_double; DROP FUNCTION myfunc_double;
......
...@@ -5962,6 +5962,24 @@ call bug15217()| ...@@ -5962,6 +5962,24 @@ call bug15217()|
drop table t3| drop table t3|
drop procedure bug15217| drop procedure bug15217|
#
# BUG#19862: Sort with filesort by function evaluates function twice
#
--disable_warnings
drop procedure if exists bug19862|
--enable_warnings
CREATE TABLE t11 (a INT)|
CREATE TABLE t12 (a INT)|
CREATE FUNCTION bug19862(x INT) RETURNS INT
BEGIN
INSERT INTO t11 VALUES (x);
RETURN x+1;
END|
INSERT INTO t12 VALUES (1), (2)|
SELECT bug19862(a) FROM t12 ORDER BY 1|
SELECT * FROM t11|
DROP TABLE t11, t12|
DROP FUNCTION bug19862|
# #
# BUG#NNNN: New bug synopsis # BUG#NNNN: New bug synopsis
# #
......
...@@ -109,6 +109,13 @@ SELECT myfunc_double(n) AS f FROM bug19904; ...@@ -109,6 +109,13 @@ SELECT myfunc_double(n) AS f FROM bug19904;
SELECT metaphon(v) AS f FROM bug19904; SELECT metaphon(v) AS f FROM bug19904;
DROP TABLE bug19904; DROP TABLE bug19904;
#
# Bug#19862: Sort with filesort by function evaluates function twice
#
create table t1(f1 int);
insert into t1 values(1),(2);
explain select myfunc_int(f1) from t1 order by 1;
drop table t1;
--echo End of 5.0 tests. --echo End of 5.0 tests.
# #
......
...@@ -752,6 +752,7 @@ public: ...@@ -752,6 +752,7 @@ public:
virtual bool find_item_in_field_list_processor(byte *arg) { return 0; } virtual bool find_item_in_field_list_processor(byte *arg) { return 0; }
virtual bool change_context_processor(byte *context) { return 0; } virtual bool change_context_processor(byte *context) { return 0; }
virtual bool reset_query_id_processor(byte *query_id) { return 0; } virtual bool reset_query_id_processor(byte *query_id) { return 0; }
virtual bool func_type_checker_processor(byte *arg) { return 0; }
virtual Item *equal_fields_propagator(byte * arg) { return this; } virtual Item *equal_fields_propagator(byte * arg) { return this; }
virtual Item *set_no_const_sub(byte *arg) { return this; } virtual Item *set_no_const_sub(byte *arg) { return this; }
......
...@@ -398,6 +398,13 @@ Field *Item_func::tmp_table_field(TABLE *t_arg) ...@@ -398,6 +398,13 @@ Field *Item_func::tmp_table_field(TABLE *t_arg)
return res; return res;
} }
bool Item_func::func_type_checker_processor(byte *arg)
{
return *((Functype*)arg) == functype();
}
my_decimal *Item_func::val_decimal(my_decimal *decimal_value) my_decimal *Item_func::val_decimal(my_decimal *decimal_value)
{ {
DBUG_ASSERT(fixed); DBUG_ASSERT(fixed);
......
...@@ -55,7 +55,7 @@ public: ...@@ -55,7 +55,7 @@ public:
NOT_FUNC, NOT_ALL_FUNC, NOT_FUNC, NOT_ALL_FUNC,
NOW_FUNC, TRIG_COND_FUNC, NOW_FUNC, TRIG_COND_FUNC,
GUSERVAR_FUNC, COLLATE_FUNC, GUSERVAR_FUNC, COLLATE_FUNC,
EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP }; EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC };
enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL, enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL,
OPTIMIZE_EQUAL }; OPTIMIZE_EQUAL };
enum Type type() const { return FUNC_ITEM; } enum Type type() const { return FUNC_ITEM; }
...@@ -186,6 +186,7 @@ public: ...@@ -186,6 +186,7 @@ public:
Item *transform(Item_transformer transformer, byte *arg); Item *transform(Item_transformer transformer, byte *arg);
void traverse_cond(Cond_traverser traverser, void traverse_cond(Cond_traverser traverser,
void * arg, traverse_order order); void * arg, traverse_order order);
bool func_type_checker_processor(byte *arg);
}; };
...@@ -930,6 +931,7 @@ public: ...@@ -930,6 +931,7 @@ public:
Item_udf_func(udf_func *udf_arg, List<Item> &list) Item_udf_func(udf_func *udf_arg, List<Item> &list)
:Item_func(list), udf(udf_arg) {} :Item_func(list), udf(udf_arg) {}
const char *func_name() const { return udf.name(); } const char *func_name() const { return udf.name(); }
enum Functype functype() const { return UDF_FUNC; }
bool fix_fields(THD *thd, Item **ref) bool fix_fields(THD *thd, Item **ref)
{ {
DBUG_ASSERT(fixed == 0); DBUG_ASSERT(fixed == 0);
......
...@@ -1064,6 +1064,26 @@ JOIN::optimize() ...@@ -1064,6 +1064,26 @@ JOIN::optimize()
{ {
need_tmp=1; simple_order=simple_group=0; // Force tmp table without sort need_tmp=1; simple_order=simple_group=0; // Force tmp table without sort
} }
if (order)
{
/*
Force using of tmp table if sorting by a SP or UDF function due to
their expensive and probably non-deterministic nature.
*/
for (ORDER *tmp_order= order; tmp_order ; tmp_order=tmp_order->next)
{
Item *item= *tmp_order->item;
Item_func::Functype type=Item_func::FUNC_SP;
Item_func::Functype type1=Item_func::UDF_FUNC;
if (item->walk(&Item::func_type_checker_processor,(byte*)&type) ||
item->walk(&Item::func_type_checker_processor,(byte*)&type1))
{
/* Force tmp table without sort */
need_tmp=1; simple_order=simple_group=0;
break;
}
}
}
} }
tmp_having= having; tmp_having= having;
......
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