Commit a87507ee authored by Alexander Barkov's avatar Alexander Barkov

MDEV-9712 Performance degradation of nested NULLIF

10.1 introduced a problem:
Execution time for various recursive stages
(walk, update_used_table, and propagate_equal_fields)
in NULLIF is O(recursion_level^2), because complexity is
doubled on every recursion level when we copy args[0] to args[2].

This change fixes to avoid unnecessary recursion in:
- Item_func_nullif::walk
- Item_func_nullif::update_used_tables
- Item_func_nullif::propagate_equal_fields
when possible.
parent 19c4d22a
...@@ -2507,6 +2507,23 @@ void Item_func_nullif::split_sum_func(THD *thd, Item **ref_pointer_array, ...@@ -2507,6 +2507,23 @@ void Item_func_nullif::split_sum_func(THD *thd, Item **ref_pointer_array,
} }
bool Item_func_nullif::walk(Item_processor processor,
bool walk_subquery, uchar *arg)
{
/*
No needs to iterate through args[2] when it's just a copy of args[0].
See MDEV-9712 Performance degradation of nested NULLIF
*/
uint tmp_count= arg_count == 2 || args[0] == args[2] ? 2 : 3;
for (uint i= 0; i < tmp_count; i++)
{
if (args[i]->walk(processor, walk_subquery, arg))
return true;
}
return (this->*processor)(arg);
}
void Item_func_nullif::update_used_tables() void Item_func_nullif::update_used_tables()
{ {
if (m_cache) if (m_cache)
...@@ -2517,7 +2534,14 @@ void Item_func_nullif::update_used_tables() ...@@ -2517,7 +2534,14 @@ void Item_func_nullif::update_used_tables()
} }
else else
{ {
Item_func::update_used_tables(); /*
MDEV-9712 Performance degradation of nested NULLIF
No needs to iterate through args[2] when it's just a copy of args[0].
*/
DBUG_ASSERT(arg_count == 3);
used_tables_and_const_cache_init();
used_tables_and_const_cache_update_and_join(args[0] == args[2] ? 2 : 3,
args);
} }
} }
......
...@@ -1026,6 +1026,7 @@ class Item_func_nullif :public Item_func_hybrid_field_type ...@@ -1026,6 +1026,7 @@ class Item_func_nullif :public Item_func_hybrid_field_type
String *str_op(String *str); String *str_op(String *str);
my_decimal *decimal_op(my_decimal *); my_decimal *decimal_op(my_decimal *);
void fix_length_and_dec(); void fix_length_and_dec();
bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
uint decimal_precision() const { return args[2]->decimal_precision(); } uint decimal_precision() const { return args[2]->decimal_precision(); }
const char *func_name() const { return "nullif"; } const char *func_name() const { return "nullif"; }
void print(String *str, enum_query_type query_type); void print(String *str, enum_query_type query_type);
...@@ -1037,13 +1038,21 @@ class Item_func_nullif :public Item_func_hybrid_field_type ...@@ -1037,13 +1038,21 @@ class Item_func_nullif :public Item_func_hybrid_field_type
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{ {
Context cmpctx(ANY_SUBST, cmp.compare_type(), cmp.compare_collation()); Context cmpctx(ANY_SUBST, cmp.compare_type(), cmp.compare_collation());
const Item *old0= args[0];
args[0]->propagate_equal_fields_and_change_item_tree(thd, cmpctx, args[0]->propagate_equal_fields_and_change_item_tree(thd, cmpctx,
cond, &args[0]); cond, &args[0]);
args[1]->propagate_equal_fields_and_change_item_tree(thd, cmpctx, args[1]->propagate_equal_fields_and_change_item_tree(thd, cmpctx,
cond, &args[1]); cond, &args[1]);
args[2]->propagate_equal_fields_and_change_item_tree(thd, /*
Context_identity(), MDEV-9712 Performance degradation of nested NULLIF
cond, &args[2]); ANY_SUBST is more relaxed than IDENTITY_SUBST.
If ANY_SUBST did not change args[0],
then we can skip propagation for args[2].
*/
if (old0 != args[0])
args[2]->propagate_equal_fields_and_change_item_tree(thd,
Context_identity(),
cond, &args[2]);
return this; return this;
} }
}; };
......
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