#At file:///work/bzr_trees/34384-bug-5.1-bugteam/ based on revid:kristofer.pettersson@stripped
3147 Evgeny Potemkin 2009-10-08
Bug#34384: Slow down on constant conversion.
When values of different types are compared they're converted to a type that
allows correct comparison. This conversion is done for each comparison and
takes some time. When a constant is being compared it's possible to cache the
value after conversion to speedup comparison.
A test case isn't provided because all changes are internal and aren't visible
outside.
A flag named uncacheable is added to the Item class. It denies caching of the
item.
A flag named late_caching is added to the Item_cache class. It's set to TRUE
when we need to postpone caching till the first use of cached value.
Functions named init_late_caching and cache_value() are added to the Item_cache
flag. Their purpose is to check state of the late_caching flag and cache value
accordingly.
Item_cache_xxx::store functions are changed to call init_late_caching prior to
store cached value.
Item_cache_xxx::val_xxx functions are changed to call cache_value prior to
return cached value if late_caching is enabled.
The Arg_comparator::set_cmp_func function is changed to cache a value being
compared if it's a constant and needs type conversion.
Now the Item_func_like::fix_fields function denies caching arguments of the LIKE
function.
The Item_cache::get_cache function is overloaded to allow setting of the
late_caching flag and cache type.
The cache_converted_constant function is added to the Arg_comparator class.
It checks whether a value can and should be cached and if so caches it.
@ sql/item.cc
Bug#34384: Slow down on constant conversion.
A flag named uncacheable is added to the Item class. It denies caching of the item.
The Item_cache::get_cache function is overloaded to allow setting of the
late_caching flag and cache type.
Item_cache_xxx::val_xxx functions are changed to call cache_value prior to return a value.
Item_cache_xxx::store functions are changed to call init_late_caching
prior to store cached value.
@ sql/item.h
Bug#34384: Slow down on constant conversion.
A flag named uncacheable is added to the Item class. It denies caching of the
item.
A flag named late_caching is added to the Item_cache class. It's set to TRUE
when we need to postpone caching till the first use of cached value.
The Item_cache::get_cache function is overloaded to allow setting of the
late_caching flag and cache type.
Functions named init_late_caching and cache_value() are added to the Item_cache
flag. Their purpose is to check state of the late_caching flag and cache value
accordingly.
@ sql/item_cmpfunc.cc
Bug#34384: Slow down on constant conversion.
A helper function cache_converted_constant is added to thhe Arg_comparator class.
It checks whether a given item can and should be cached and caches it if so.
@ sql/item_cmpfunc.h
Bug#34384: Slow down on constant conversion.
The cache_converted_constant function is added to the Arg_comparator class.
It checks whether a value can and should be cached and if so caches it.
@ sql/item_xmlfunc.cc
Bug#34384: Slow down on constant conversion.
modified:
sql/item.cc
sql/item.h
sql/item_cmpfunc.cc
sql/item_cmpfunc.h
sql/item_xmlfunc.cc
=== modified file 'sql/item.cc'
--- a/sql/item.cc 2009-09-29 02:23:38 +0000
+++ b/sql/item.cc 2009-10-08 11:19:15 +0000
@@ -377,7 +377,8 @@ int Item::save_str_value_in_field(Field
Item::Item():
rsize(0), name(0), orig_name(0), name_length(0), fixed(0),
is_autogenerated_name(TRUE),
- collation(&my_charset_bin, DERIVATION_COERCIBLE)
+ collation(&my_charset_bin, DERIVATION_COERCIBLE),
+ uncacheable(0)
{
marker= 0;
maybe_null=null_value=with_sum_func=unsigned_flag=0;
@@ -6938,17 +6939,34 @@ int stored_field_cmp_to_item(Field *fiel
Item_cache* Item_cache::get_cache(const Item *item)
{
- switch (item->result_type()) {
+ return get_cache(item->result_type(), item, FALSE);
+}
+
+
+/**
+ Get a cache item of desired type.
+
+ @param type desired type of cache
+ @param item value to be cached
+ @param late_caching TRUE <=> postpone caching till the first usage of cached
+ value.
+
+ @return cache item
+*/
+
+Item_cache* Item_cache::get_cache(const Item_result type, const Item *item, bool late_caching)
+{
+ switch (type) {
case INT_RESULT:
- return new Item_cache_int();
+ return new Item_cache_int(late_caching);
case REAL_RESULT:
- return new Item_cache_real();
+ return new Item_cache_real(late_caching);
case DECIMAL_RESULT:
- return new Item_cache_decimal();
+ return new Item_cache_decimal(late_caching);
case STRING_RESULT:
- return new Item_cache_str(item);
+ return new Item_cache_str(item,late_caching);
case ROW_RESULT:
- return new Item_cache_row();
+ return new Item_cache_row(late_caching);
default:
// should never be in real life
DBUG_ASSERT(0);
@@ -6967,9 +6985,25 @@ void Item_cache::print(String *str, enum
str->append(')');
}
+inline bool Item_cache::init_late_caching(Item *item)
+{
+ if (!late_caching)
+ return FALSE;
+ if (item)
+ example= item;
+ return TRUE;
+}
+
+inline void Item_cache::cache_value()
+{
+ late_caching= FALSE;
+ store(example);
+}
void Item_cache_int::store(Item *item)
{
+ if (init_late_caching(item))
+ return;
value= item->val_int_result();
null_value= item->null_value;
unsigned_flag= item->unsigned_flag;
@@ -6978,6 +7012,8 @@ void Item_cache_int::store(Item *item)
void Item_cache_int::store(Item *item, longlong val_arg)
{
+ if (init_late_caching(item))
+ return;
value= val_arg;
null_value= item->null_value;
unsigned_flag= item->unsigned_flag;
@@ -6987,6 +7023,8 @@ void Item_cache_int::store(Item *item, l
String *Item_cache_int::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
+ if (late_caching)
+ cache_value();
str->set(value, default_charset());
return str;
}
@@ -6995,21 +7033,50 @@ String *Item_cache_int::val_str(String *
my_decimal *Item_cache_int::val_decimal(my_decimal *decimal_val)
{
DBUG_ASSERT(fixed == 1);
+ if (late_caching)
+ cache_value();
int2my_decimal(E_DEC_FATAL_ERROR, value, unsigned_flag, decimal_val);
return decimal_val;
}
+double Item_cache_int::val_real()
+{
+ DBUG_ASSERT(fixed == 1);
+ if (late_caching)
+ cache_value();
+ return (double) value;
+}
+
+longlong Item_cache_int::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ if (late_caching)
+ cache_value();
+ return value;
+}
void Item_cache_real::store(Item *item)
{
+ if (init_late_caching(item))
+ return;
value= item->val_result();
null_value= item->null_value;
}
+double Item_cache_real::val_real()
+{
+ DBUG_ASSERT(fixed == 1);
+ if (late_caching)
+ cache_value();
+ return value;
+}
+
longlong Item_cache_real::val_int()
{
DBUG_ASSERT(fixed == 1);
+ if (late_caching)
+ cache_value();
return (longlong) rint(value);
}
@@ -7017,6 +7084,8 @@ longlong Item_cache_real::val_int()
String* Item_cache_real::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
+ if (late_caching)
+ cache_value();
str->set_real(value, decimals, default_charset());
return str;
}
@@ -7025,6 +7094,8 @@ String* Item_cache_real::val_str(String
my_decimal *Item_cache_real::val_decimal(my_decimal *decimal_val)
{
DBUG_ASSERT(fixed == 1);
+ if (late_caching)
+ cache_value();
double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val);
return decimal_val;
}
@@ -7032,6 +7103,8 @@ my_decimal *Item_cache_real::val_decimal
void Item_cache_decimal::store(Item *item)
{
+ if (init_late_caching(item))
+ return;
my_decimal *val= item->val_decimal_result(&decimal_value);
if (!(null_value= item->null_value) && val != &decimal_value)
my_decimal2decimal(val, &decimal_value);
@@ -7041,6 +7114,8 @@ double Item_cache_decimal::val_real()
{
DBUG_ASSERT(fixed);
double res;
+ if (late_caching)
+ cache_value();
my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &res);
return res;
}
@@ -7049,6 +7124,8 @@ longlong Item_cache_decimal::val_int()
{
DBUG_ASSERT(fixed);
longlong res;
+ if (late_caching)
+ cache_value();
my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &res);
return res;
}
@@ -7056,6 +7133,8 @@ longlong Item_cache_decimal::val_int()
String* Item_cache_decimal::val_str(String *str)
{
DBUG_ASSERT(fixed);
+ if (late_caching)
+ cache_value();
my_decimal_round(E_DEC_FATAL_ERROR, &decimal_value, decimals, FALSE,
&decimal_value);
my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, str);
@@ -7065,12 +7144,16 @@ String* Item_cache_decimal::val_str(Stri
my_decimal *Item_cache_decimal::val_decimal(my_decimal *val)
{
DBUG_ASSERT(fixed);
+ if (late_caching)
+ cache_value();
return &decimal_value;
}
void Item_cache_str::store(Item *item)
{
+ if (init_late_caching(item))
+ return;
value_buff.set(buffer, sizeof(buffer), item->collation.collation);
value= item->str_result(&value_buff);
if ((null_value= item->null_value))
@@ -7095,6 +7178,8 @@ double Item_cache_str::val_real()
DBUG_ASSERT(fixed == 1);
int err_not_used;
char *end_not_used;
+ if (late_caching)
+ cache_value();
if (value)
return my_strntod(value->charset(), (char*) value->ptr(),
value->length(), &end_not_used, &err_not_used);
@@ -7106,6 +7191,8 @@ longlong Item_cache_str::val_int()
{
DBUG_ASSERT(fixed == 1);
int err;
+ if (late_caching)
+ cache_value();
if (value)
return my_strntoll(value->charset(), value->ptr(),
value->length(), 10, (char**) 0, &err);
@@ -7113,9 +7200,21 @@ longlong Item_cache_str::val_int()
return (longlong)0;
}
+
+String* Item_cache_str::val_str(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (late_caching)
+ cache_value();
+ return value;
+}
+
+
my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val)
{
DBUG_ASSERT(fixed == 1);
+ if (late_caching)
+ cache_value();
if (value)
string2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val);
else
@@ -7126,6 +7225,8 @@ my_decimal *Item_cache_str::val_decimal(
int Item_cache_str::save_in_field(Field *field, bool no_conversions)
{
+ if (late_caching)
+ cache_value();
int res= Item_cache::save_in_field(field, no_conversions);
return (is_varbinary && field->type() == MYSQL_TYPE_STRING &&
value->length() < field->field_length) ? 1 : res;
@@ -7143,6 +7244,8 @@ bool Item_cache_row::allocate(uint num)
bool Item_cache_row::setup(Item * item)
{
+ if (init_late_caching(item))
+ return FALSE;
example= item;
if (!values && allocate(item->cols()))
return 1;
@@ -7160,6 +7263,8 @@ bool Item_cache_row::setup(Item * item)
void Item_cache_row::store(Item * item)
{
+ if (init_late_caching(item))
+ return;
null_value= 0;
item->bring_value();
for (uint i= 0; i < item_count; i++)
=== modified file 'sql/item.h'
--- a/sql/item.h 2009-08-28 10:55:59 +0000
+++ b/sql/item.h 2009-10-08 11:19:15 +0000
@@ -523,6 +523,9 @@ public:
of its arguments is or contains a
subselect */
Item_result cmp_context; /* Comparison context */
+ bool uncacheable; /* Whether item can be cached by
+ Arg_comparator. See description of
+ Arg_comparator::cache_converted_constant.*/
// alloc & destruct is done as start of select using sql_alloc
Item();
/*
@@ -2890,15 +2893,26 @@ protected:
*/
Field *cached_field;
enum enum_field_types cached_field_type;
-public:
- Item_cache():
- example(0), used_table_map(0), cached_field(0), cached_field_type(MYSQL_TYPE_STRING)
+ /*
+ TRUE <=> cache value on the first val_xxx call.
+ In some cases it's not possible to get value of the item to be cached at
+ the moment of cached being created & initialized. This flag tells Item_cache
+ only to save the item to be cached without actually caching it.
+ On the first call of val_xxx function the value of the item will be
+ retrieved, cached and returned to the caller.
+ */
+ bool late_caching;
+public:
+ Item_cache(bool late_caching_arg= FALSE):
+ example(0), used_table_map(0), cached_field(0), cached_field_type(MYSQL_TYPE_STRING),
+ late_caching(late_caching_arg)
{
fixed= 1;
null_value= 1;
}
Item_cache(enum_field_types field_type_arg):
- example(0), used_table_map(0), cached_field(0), cached_field_type(field_type_arg)
+ example(0), used_table_map(0), cached_field(0), cached_field_type(field_type_arg),
+ late_caching(0)
{
fixed= 1;
null_value= 1;
@@ -2922,6 +2936,8 @@ public:
enum Type type() const { return CACHE_ITEM; }
enum_field_types field_type() const { return cached_field_type; }
static Item_cache* get_cache(const Item *item);
+ static Item_cache* get_cache(const Item_result type, const Item* item,
+ bool late_caching);
table_map used_tables() const { return used_table_map; }
virtual void keep_array() {}
virtual void print(String *str, enum_query_type query_type);
@@ -2933,6 +2949,8 @@ public:
{
return this == item;
}
+ inline bool init_late_caching(Item *item);
+ inline void cache_value();
};
@@ -2941,14 +2959,15 @@ class Item_cache_int: public Item_cache
protected:
longlong value;
public:
- Item_cache_int(): Item_cache(), value(0) {}
+ Item_cache_int(bool late_caching_arg= FALSE): Item_cache(late_caching_arg),
+ value(0) {}
Item_cache_int(enum_field_types field_type_arg):
Item_cache(field_type_arg), value(0) {}
void store(Item *item);
void store(Item *item, longlong val_arg);
- double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; }
- longlong val_int() { DBUG_ASSERT(fixed == 1); return value; }
+ double val_real();
+ longlong val_int();
String* val_str(String *str);
my_decimal *val_decimal(my_decimal *);
enum Item_result result_type() const { return INT_RESULT; }
@@ -2960,10 +2979,11 @@ class Item_cache_real: public Item_cache
{
double value;
public:
- Item_cache_real(): Item_cache(), value(0) {}
+ Item_cache_real(bool late_caching_arg= FALSE): Item_cache(late_caching_arg),
+ value(0) {}
void store(Item *item);
- double val_real() { DBUG_ASSERT(fixed == 1); return value; }
+ double val_real();
longlong val_int();
String* val_str(String *str);
my_decimal *val_decimal(my_decimal *);
@@ -2976,7 +2996,7 @@ class Item_cache_decimal: public Item_ca
protected:
my_decimal decimal_value;
public:
- Item_cache_decimal(): Item_cache() {}
+ Item_cache_decimal(bool late_caching_arg= FALSE): Item_cache(late_caching_arg) {}
void store(Item *item);
double val_real();
@@ -2994,8 +3014,8 @@ class Item_cache_str: public Item_cache
bool is_varbinary;
public:
- Item_cache_str(const Item *item) :
- Item_cache(), value(0),
+ Item_cache_str(const Item *item, bool late_caching_arg= FALSE) :
+ Item_cache(late_caching_arg), value(0),
is_varbinary(item->type() == FIELD_ITEM &&
((const Item_field *) item)->field->type() ==
MYSQL_TYPE_VARCHAR &&
@@ -3004,7 +3024,7 @@ public:
void store(Item *item);
double val_real();
longlong val_int();
- String* val_str(String *) { DBUG_ASSERT(fixed == 1); return value; }
+ String* val_str(String *);
my_decimal *val_decimal(my_decimal *);
enum Item_result result_type() const { return STRING_RESULT; }
CHARSET_INFO *charset() const { return value->charset(); };
@@ -3017,8 +3037,9 @@ class Item_cache_row: public Item_cache
uint item_count;
bool save_array;
public:
- Item_cache_row()
- :Item_cache(), values(0), item_count(2), save_array(0) {}
+ Item_cache_row(bool late_caching_arg= FALSE)
+ :Item_cache(late_caching_arg), values(0), item_count(2),
+ save_array(0) {}
/*
'allocate' used only in row transformer, to preallocate space for row
=== modified file 'sql/item_cmpfunc.cc'
--- a/sql/item_cmpfunc.cc 2009-10-05 05:27:36 +0000
+++ b/sql/item_cmpfunc.cc 2009-10-08 11:19:15 +0000
@@ -532,6 +532,13 @@ void Item_bool_func2::fix_length_and_dec
set_cmp_func();
}
+void Item_bool_func2::set_cmp_func()
+{
+ /* Don't need cache if doing context analysis only. */
+ if (current_thd->is_context_analysis_only())
+ tmp_arg[0]->uncacheable= tmp_arg[1]->uncacheable= TRUE;
+ cmp.set_cmp_func(this, tmp_arg, tmp_arg+1);
+}
int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type)
{
@@ -914,9 +921,42 @@ int Arg_comparator::set_cmp_func(Item_bo
return 1;
}
+ a= cache_converted_constant(a, &a_cache, type);
+ b= cache_converted_constant(b, &b_cache, type);
return set_compare_func(owner_arg, type);
}
+/**
+ Convert and cache a constant.
+
+ @param value [in] An item to cache
+ @param cache_item [out] Placeholder for the cache item
+ @param type [in] Comparison type
+
+ @details
+ When given item is a constant and its type differs from comparison type
+ then cache its value to avoid type conversion of this constant on each
+ evaluation. In this case the value is cached and the reference to the cache
+ is returned.
+ Original value is returned otherwise.
+
+ @return cache item or original value.
+*/
+
+Item** Arg_comparator::cache_converted_constant(Item **value,
+ Item **cache_item,
+ Item_result type)
+{
+ if ((*value)->const_item() && !(*value)->uncacheable &&
+ type != (*value)->result_type())
+ {
+ Item_cache *cache= Item_cache::get_cache(type, *value, TRUE);
+ cache->store(*value);
+ *cache_item= cache;
+ return cache_item;
+ }
+ return value;
+}
void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1)
{
@@ -4372,6 +4412,11 @@ Item_func::optimize_type Item_func_like:
bool Item_func_like::fix_fields(THD *thd, Item **ref)
{
DBUG_ASSERT(fixed == 0);
+
+ /* LIKE doesn't compare arguments directly, thus no reason to cache them. */
+ for (int i= 0; i < arg_count; i++)
+ args[i]->uncacheable= TRUE;
+
if (Item_bool_func2::fix_fields(thd, ref) ||
escape_item->fix_fields(thd, &escape_item))
return TRUE;
=== modified file 'sql/item_cmpfunc.h'
--- a/sql/item_cmpfunc.h 2008-12-12 11:13:11 +0000
+++ b/sql/item_cmpfunc.h 2009-10-08 11:19:15 +0000
@@ -94,6 +94,8 @@ public:
ulonglong *const_val_arg);
void set_datetime_cmp_func(Item **a1, Item **b1);
+ Item** cache_converted_constant(Item **value, Item **cache,
+ Item_result type);
static arg_cmp_func comparator_matrix [5][2];
friend class Item_func;
@@ -331,10 +333,7 @@ public:
Item_bool_func2(Item *a,Item *b)
:Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1), abort_on_null(FALSE) {}
void fix_length_and_dec();
- void set_cmp_func()
- {
- cmp.set_cmp_func(this, tmp_arg, tmp_arg+1);
- }
+ void set_cmp_func();
optimize_type select_optimize() const { return OPTIMIZE_OP; }
virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; }
bool have_rev_func() const { return rev_functype() != UNKNOWN_FUNC; }
=== modified file 'sql/item_xmlfunc.cc'
--- a/sql/item_xmlfunc.cc 2009-09-23 13:21:29 +0000
+++ b/sql/item_xmlfunc.cc 2009-10-08 11:19:15 +0000
@@ -942,6 +942,8 @@ static Item *create_comparator(MY_XPATH
*/
Item *fake= new Item_string("", 0, xpath->cs);
+ /* Don't cache fake because its value will be changed during comparison.*/
+ fake->uncacheable= TRUE;
Item_nodeset_func *nodeset;
Item *scalar, *comp;
if (a->type() == Item::XPATH_NODESET)
Attachment: [text/bzr-bundle] bzr/epotemkin@mysql.com-20091008111915-z4sq02qolra9mu6f.bundle