Commit 987e1466 authored by Evgeny Potemkin's avatar Evgeny Potemkin

Bug#33546: Slowdown on re-evaluation of constant expressions.

      
Constant expressions in WHERE/HAVING/ON clauses aren't cached and evaluated
for each row. This causes slowdown of query execution especially if constant
UDF/SP function are used.
      
Now WHERE/HAVING/ON expressions are analyzed in the top-bottom direction with
help of the compile function. When analyzer meets a constant item it
sets a flag for the tree transformer to cache the item and doesn't allow tree
walker to go deeper. Thus, the topmost item of a constant expression if
cached. This is done after all other optimizations were applied to
WHERE/HAVING/ON expressions
      
A helper function called cache_const_exprs is added to the JOIN class.
It calls compile method with caching analyzer and transformer on WHERE,
HAVING, ON expressions if they're present.
The cache_const_expr_analyzer and cache_const_expr_transformer functions are
added to the Item class. The first one check if the item can be cached and
the second caches it if so.
A new Item_cache_datetime class is derived from the Item_cache class.
It caches both int and string values of the underlying item independently to
avoid DATETIME aware int-to-string conversion. Thus it completely relies on
the ability of the underlying item to correctly convert DATETIME value from
int to string and vice versa.


mysql-test/r/func_like.result:
  A test case result is corrected after fixing bug#33546.
mysql-test/r/func_time.result:
  A test case result is corrected after fixing bug#33546.
mysql-test/r/select.result:
  Added a test case for the bug#33546.
mysql-test/r/subselect.result:
  A test case result is corrected after fixing bug#33546.
mysql-test/r/udf.result:
  Added a test case for the bug#33546.
mysql-test/t/select.test:
  Added a test case for the bug#33546.
mysql-test/t/udf.test:
  Added a test case for the bug#33546.
sql/item.cc:
  Bug#33546: Slowdown on re-evaluation of constant expressions.
  The cache_const_expr_analyzer and cache_const_expr_transformer functions are
  added to the Item class. The first one check if the item can be cached and
  the second caches it if so.
  Item_cache_datetime class implementation is added.
sql/item.h:
  Bug#33546: Slowdown on re-evaluation of constant expressions.
  Item_ref and Item_cache classes now returns basic_const_item
  from underlying item.
  The cache_const_expr_analyzer and cache_const_expr_transformer functions are
  added to the Item class.
sql/sql_select.cc:
  Bug#33546: Slowdown on re-evaluation of constant expressions.
          
  A helper function called cache_const_exprs is added to the JOIN class.
  It calls compile method with caching analyzer and transformer on WHERE,
  HAVING, ON expressions if they're present.
sql/sql_select.h:
  Bug#33546: Slowdown on re-evaluation of constant expressions.
  A helper function called cache_const_exprs is added to the JOIN class.
parent 9e5d1bb6
......@@ -10,7 +10,7 @@ explain extended select * from t1 where a like concat('abc','%');
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 index a a 13 NULL 5 20.00 Using where; Using index
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` like concat('abc','%'))
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` like <cache>(concat('abc','%')))
select * from t1 where a like "abc%";
a
abc
......
......@@ -928,7 +928,6 @@ select f1 from t1 where cast("2006-1-1" as date) between f1 and cast('zzz' as da
f1
Warnings:
Warning 1292 Incorrect datetime value: 'zzz'
Warning 1292 Incorrect datetime value: 'zzz'
select f1 from t1 where makedate(2006,1) between date(f1) and date(f3);
f1
2006-01-01
......
......@@ -4423,9 +4423,14 @@ CREATE TABLE t1(a INT);
INSERT INTO t1 VALUES (2),(3);
# Should not crash
SELECT 1 FROM t1 WHERE a <> 1 AND NOT
ROW(a,a) <=> ROW((SELECT 1 FROM t1 WHERE 1=2),(SELECT 1 FROM t1))
ROW(1,a) <=> ROW(1,(SELECT 1 FROM t1))
INTO @var0;
ERROR 21000: Subquery returns more than 1 row
SELECT 1 FROM t1 WHERE a <> 1 AND NOT
ROW(a,a) <=> ROW((SELECT 1 FROM t1 WHERE 1=2),(SELECT 1 FROM t1));
1
1
1
DROP TABLE t1;
End of 5.0 tests
create table t1(a INT, KEY (a));
......@@ -4577,4 +4582,70 @@ field2
15:13:38
drop table A,AA,B,BB;
#end of test for bug#45266
#
# Bug#33546: Slowdown on re-evaluation of constant expressions.
#
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
CREATE TABLE t2 (b INT);
INSERT INTO t2 VALUES (2);
SELECT * FROM t1 WHERE a = 1 + 1;
a
2
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a = 1 + 1;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 100.00 Using where
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` = <cache>((1 + 1)))
SELECT * FROM t1 HAVING a = 1 + 1;
a
2
EXPLAIN EXTENDED SELECT * FROM t1 HAVING a = 1 + 1;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 100.00
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` having (`test`.`t1`.`a` = <cache>((1 + 1)))
SELECT * FROM t1, t2 WHERE a = b + (1 + 1);
a b
4 2
EXPLAIN EXTENDED SELECT * FROM t1, t2 WHERE a = b + (1 + 1);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t2 system NULL NULL NULL NULL 1 100.00
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 100.00 Using where
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a`,'2' AS `b` from `test`.`t1` where (`test`.`t1`.`a` = <cache>(('2' + (1 + 1))))
SELECT * FROM t2 LEFT JOIN t1 ON a = b + 1;
b a
2 3
EXPLAIN EXTENDED SELECT * FROM t2 LEFT JOIN t1 ON a = b + 1;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t2 system NULL NULL NULL NULL 1 100.00
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 100.00
Warnings:
Note 1003 select '2' AS `b`,`test`.`t1`.`a` AS `a` from `test`.`t1` where 1
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a > UNIX_TIMESTAMP('2009-03-10 00:00:00');
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 100.00 Using where
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` > <cache>(unix_timestamp('2009-03-10 00:00:00')))
CREATE FUNCTION f1() RETURNS INT DETERMINISTIC
BEGIN
SET @cnt := @cnt + 1;
RETURN 1;
END;|
SET @cnt := 0;
SELECT * FROM t1 WHERE a = f1();
a
1
SELECT @cnt;
@cnt
1
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a = f1();
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 100.00 Using where
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` = <cache>(`f1`()))
DROP TABLE t1, t2;
DROP FUNCTION f1;
# End of bug#33546
End of 5.1 tests
......@@ -735,7 +735,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
Warnings:
Note 1249 Select 3 was reduced during optimization
Note 1249 Select 2 was reduced during optimization
Note 1003 select `test`.`t2`.`id` AS `id` from `test`.`t2` where (`test`.`t2`.`id` = (1 + 1))
Note 1003 select `test`.`t2`.`id` AS `id` from `test`.`t2` where (`test`.`t2`.`id` = <cache>((1 + 1)))
EXPLAIN EXTENDED SELECT * FROM t2 WHERE id IN (SELECT 1 UNION SELECT 3);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t2 index NULL id 5 NULL 2 100.00 Using where; Using index
......
......@@ -409,3 +409,18 @@ b
1
DROP TABLE t1;
End of 5.0 tests.
#
# Bug#33546: Slowdown on re-evaluation of constant expressions.
#
CREATE TABLE t1 (f1 INT);
INSERT INTO t1 VALUES(1),(50);
CREATE FUNCTION myfunc_double RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB";
EXPLAIN EXTENDED SELECT 1 FROM t1 WHERE f1=1 + myfunc_double(1);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where
Warnings:
Note 1003 select 1 AS `1` from `test`.`t1` where (`test`.`t1`.`f1` = <cache>((1 + myfunc_double(1 AS `1`))))
DROP FUNCTION myfunc_double;
DROP TABLE t1;
#
End of 5.1 tests.
......@@ -3767,9 +3767,14 @@ INSERT INTO t1 VALUES (2),(3);
--echo # Should not crash
--error ER_SUBQUERY_NO_1_ROW
SELECT 1 FROM t1 WHERE a <> 1 AND NOT
ROW(a,a) <=> ROW((SELECT 1 FROM t1 WHERE 1=2),(SELECT 1 FROM t1))
ROW(1,a) <=> ROW(1,(SELECT 1 FROM t1))
INTO @var0;
# Query correctly return 2 rows since comparison a <=> fisrt_subquery is
# always false, thus the second query is never executed.
SELECT 1 FROM t1 WHERE a <> 1 AND NOT
ROW(a,a) <=> ROW((SELECT 1 FROM t1 WHERE 1=2),(SELECT 1 FROM t1));
DROP TABLE t1;
......@@ -3918,4 +3923,38 @@ SELECT table1 .`time_key` field2 FROM B table1 LEFT JOIN BB JOIN A table5 ON
drop table A,AA,B,BB;
--echo #end of test for bug#45266
--echo #
--echo # Bug#33546: Slowdown on re-evaluation of constant expressions.
--echo #
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
CREATE TABLE t2 (b INT);
INSERT INTO t2 VALUES (2);
SELECT * FROM t1 WHERE a = 1 + 1;
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a = 1 + 1;
SELECT * FROM t1 HAVING a = 1 + 1;
EXPLAIN EXTENDED SELECT * FROM t1 HAVING a = 1 + 1;
SELECT * FROM t1, t2 WHERE a = b + (1 + 1);
EXPLAIN EXTENDED SELECT * FROM t1, t2 WHERE a = b + (1 + 1);
SELECT * FROM t2 LEFT JOIN t1 ON a = b + 1;
EXPLAIN EXTENDED SELECT * FROM t2 LEFT JOIN t1 ON a = b + 1;
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a > UNIX_TIMESTAMP('2009-03-10 00:00:00');
delimiter |;
CREATE FUNCTION f1() RETURNS INT DETERMINISTIC
BEGIN
SET @cnt := @cnt + 1;
RETURN 1;
END;|
delimiter ;|
SET @cnt := 0;
SELECT * FROM t1 WHERE a = f1();
SELECT @cnt;
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a = f1();
DROP TABLE t1, t2;
DROP FUNCTION f1;
--echo # End of bug#33546
--echo End of 5.1 tests
......@@ -449,3 +449,17 @@ SELECT IF( a = 1, a, a ) AS `b` FROM t1 ORDER BY field( `b`, 1 );
DROP TABLE t1;
--echo End of 5.0 tests.
--echo #
--echo # Bug#33546: Slowdown on re-evaluation of constant expressions.
--echo #
CREATE TABLE t1 (f1 INT);
INSERT INTO t1 VALUES(1),(50);
--replace_result $UDF_EXAMPLE_LIB UDF_EXAMPLE_LIB
eval CREATE FUNCTION myfunc_double RETURNS INTEGER SONAME "$UDF_EXAMPLE_LIB";
EXPLAIN EXTENDED SELECT 1 FROM t1 WHERE f1=1 + myfunc_double(1);
DROP FUNCTION myfunc_double;
DROP TABLE t1;
--echo #
--echo End of 5.1 tests.
......@@ -5766,6 +5766,67 @@ bool Item::send(Protocol *protocol, String *buffer)
}
/**
Check if an item is a constant one and can be cached.
@param arg [out] TRUE <=> Cache this item.
@return TRUE Go deeper in item tree.
@return FALSE Don't go deeper in item tree.
*/
bool Item::cache_const_expr_analyzer(uchar **arg)
{
bool *cache_flag= (bool*)*arg;
if (!*cache_flag)
{
Item *item= real_item();
/*
Cache constant items unless it's a basic constant, constant field or
a subselect (they use their own cache).
*/
if (const_item() &&
!(item->basic_const_item() || item->type() == Item::FIELD_ITEM ||
item->type() == SUBSELECT_ITEM ||
/*
Do not cache GET_USER_VAR() function as its const_item() may
return TRUE for the current thread but it still may change
during the execution.
*/
(item->type() == Item::FUNC_ITEM &&
((Item_func*)item)->functype() == Item_func::GUSERVAR_FUNC)))
*cache_flag= TRUE;
return TRUE;
}
return FALSE;
}
/**
Cache item if needed.
@param arg TRUE <=> Cache this item.
@return cache if cache needed.
@return this otherwise.
*/
Item* Item::cache_const_expr_transformer(uchar *arg)
{
if (*(bool*)arg)
{
*((bool*)arg)= FALSE;
Item_cache *cache= Item_cache::get_cache(this);
if (!cache)
return NULL;
cache->setup(this);
cache->store(this);
return cache;
}
return this;
}
bool Item_field::send(Protocol *protocol, String *buffer)
{
return protocol->store(result_field);
......@@ -7127,6 +7188,10 @@ Item_cache* Item_cache::get_cache(const Item *item, const Item_result type)
case DECIMAL_RESULT:
return new Item_cache_decimal();
case STRING_RESULT:
if (item->field_type() == MYSQL_TYPE_DATE ||
item->field_type() == MYSQL_TYPE_DATETIME ||
item->field_type() == MYSQL_TYPE_TIME)
return new Item_cache_datetime(item->field_type());
return new Item_cache_str(item);
case ROW_RESULT:
return new Item_cache_row();
......@@ -7208,6 +7273,72 @@ longlong Item_cache_int::val_int()
return value;
}
void Item_cache_datetime::cache_value_int()
{
value_cached= TRUE;
/* Assume here that the underlying item will do correct conversion.*/
int_value= example->val_int_result();
null_value= example->null_value;
unsigned_flag= example->unsigned_flag;
}
void Item_cache_datetime::cache_value()
{
str_value_cached= TRUE;
/* Assume here that the underlying item will do correct conversion.*/
String *res= example->str_result(&str_value);
if (res && res != &str_value)
str_value.copy(*res);
null_value= example->null_value;
unsigned_flag= example->unsigned_flag;
}
void Item_cache_datetime::store(Item *item, longlong val_arg)
{
/* An explicit values is given, save it. */
value_cached= TRUE;
int_value= val_arg;
null_value= item->null_value;
unsigned_flag= item->unsigned_flag;
}
String *Item_cache_datetime::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
if (!str_value_cached)
cache_value();
return &str_value;
}
my_decimal *Item_cache_datetime::val_decimal(my_decimal *decimal_val)
{
DBUG_ASSERT(fixed == 1);
if (!value_cached)
cache_value_int();
int2my_decimal(E_DEC_FATAL_ERROR, int_value, unsigned_flag, decimal_val);
return decimal_val;
}
double Item_cache_datetime::val_real()
{
DBUG_ASSERT(fixed == 1);
if (!value_cached)
cache_value_int();
return (double) int_value;
}
longlong Item_cache_datetime::val_int()
{
DBUG_ASSERT(fixed == 1);
if (!value_cached)
cache_value_int();
return int_value;
}
void Item_cache_real::cache_value()
{
value_cached= TRUE;
......
......@@ -903,6 +903,9 @@ public:
virtual bool reset_query_id_processor(uchar *query_id_arg) { return 0; }
virtual bool is_expensive_processor(uchar *arg) { return 0; }
virtual bool register_field_in_read_map(uchar *arg) { return 0; }
virtual bool cache_const_expr_analyzer(uchar **arg);
virtual Item* cache_const_expr_transformer(uchar *arg);
/*
Check if a partition function is allowed
SYNOPSIS
......@@ -2292,6 +2295,7 @@ public:
if (ref && result_type() == ROW_RESULT)
(*ref)->bring_value();
}
bool basic_const_item() { return (*ref)->basic_const_item(); }
};
......@@ -2977,6 +2981,8 @@ public:
}
virtual void store(Item *item);
virtual void cache_value()= 0;
bool basic_const_item() const
{ return test(example && example->basic_const_item());}
};
......@@ -3127,6 +3133,38 @@ public:
};
class Item_cache_datetime: public Item_cache
{
protected:
String str_value;
ulonglong int_value;
bool str_value_cached;
public:
Item_cache_datetime(enum_field_types field_type_arg):
Item_cache(field_type_arg), int_value(0), str_value_cached(0)
{
cmp_context= STRING_RESULT;
}
void store(Item *item, longlong val_arg);
double val_real();
longlong val_int();
String* val_str(String *str);
my_decimal *val_decimal(my_decimal *);
enum Item_result result_type() const { return STRING_RESULT; }
bool result_as_longlong() { return TRUE; }
/*
In order to avoid INT <-> STRING conversion of a DATETIME value
two cache_value functions are introduced. One (cache_value) caches STRING
value, another (cache_value_int) - INT value. Thus this cache item
completely relies on the ability of the underlying item to do the
correct conversion.
*/
void cache_value_int();
void cache_value();
};
/*
Item_type_holder used to store type. name, length of Item for UNIONS &
derived tables.
......
......@@ -1076,6 +1076,10 @@ JOIN::optimize()
{
conds=new Item_int((longlong) 0,1); // Always false
}
/* Cache constant expressions in WHERE, HAVING, ON clauses. */
cache_const_exprs();
if (make_join_select(this, select, conds))
{
zero_result_cause=
......@@ -17141,6 +17145,41 @@ bool JOIN::change_result(select_result *res)
DBUG_RETURN(FALSE);
}
/**
Cache constant expressions in WHERE, HAVING, ON conditions.
*/
void JOIN::cache_const_exprs()
{
bool cache_flag= FALSE;
bool *analyzer_arg= &cache_flag;
/* No need in cache if all tables are constant. */
if (const_tables == tables)
return;
if (conds)
conds->compile(&Item::cache_const_expr_analyzer, (uchar **)&analyzer_arg,
&Item::cache_const_expr_transformer, (uchar *)&cache_flag);
cache_flag= FALSE;
if (having)
having->compile(&Item::cache_const_expr_analyzer, (uchar **)&analyzer_arg,
&Item::cache_const_expr_transformer, (uchar *)&cache_flag);
for (JOIN_TAB *tab= join_tab + const_tables; tab < join_tab + tables ; tab++)
{
if (*tab->on_expr_ref)
{
cache_flag= FALSE;
(*tab->on_expr_ref)->compile(&Item::cache_const_expr_analyzer,
(uchar **)&analyzer_arg,
&Item::cache_const_expr_transformer,
(uchar *)&cache_flag);
}
}
}
/**
@} (end of group Query_Optimizer)
*/
......@@ -555,6 +555,7 @@ public:
return (unit == &thd->lex->unit && (unit->fake_select_lex == 0 ||
select_lex == unit->fake_select_lex));
}
void cache_const_exprs();
private:
/**
TRUE if the query contains an aggregate function but has no GROUP
......
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