summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergei Golubchik <serg@mariadb.org>2018-03-28 20:16:13 +0200
committerSergei Golubchik <serg@mariadb.org>2018-03-30 09:45:03 +0200
commit7a903784b780ea127d534e510a521b4126c3a655 (patch)
tree618a1fa57864298a432ce8e0bf94fee3c7c970e7
parent4c77ef36c69b2919f8d788c4de9e1f2da07410d7 (diff)
downloadmariadb-git-7a903784b780ea127d534e510a521b4126c3a655.tar.gz
cleanup: Item_func_case
reorder items in args[] array. Instead of when1,then1,when2,then2,...[,case][,else] sort them as [case,]when1,when2,...,then1,then2,...[,else] in this case all items used for comparison take a continuous part of the array and can be aggregated directly. and all items that can be returned take a continuous part of the array and can be aggregated directly. Old code had to copy them to a temporary array before aggreation, and then copy back (thd->change_item_tree) everything that was changed. this is a 10.3 version of bf1ca14ff3f
-rw-r--r--mysql-test/main/func_debug.result8
-rw-r--r--sql/item_cmpfunc.cc275
-rw-r--r--sql/item_cmpfunc.h18
3 files changed, 126 insertions, 175 deletions
diff --git a/mysql-test/main/func_debug.result b/mysql-test/main/func_debug.result
index f1453344e0e..7fcf522abba 100644
--- a/mysql-test/main/func_debug.result
+++ b/mysql-test/main/func_debug.result
@@ -1630,7 +1630,7 @@ c
NULL
Warnings:
Note 1105 DBUG: [0] arg=1 handler=0 (bigint)
-Note 1105 DBUG: [1] arg=3 handler=1 (decimal)
+Note 1105 DBUG: [1] arg=2 handler=1 (decimal)
DROP TABLE t1;
#
# MDEV-11555 CASE with a mixture of TIME and DATETIME returns a wrong result
@@ -1649,9 +1649,9 @@ good was_bad_now_good
one one
Warnings:
Note 1105 DBUG: [0] arg=1 handler=0 (time)
-Note 1105 DBUG: [1] arg=3 handler=0 (time)
+Note 1105 DBUG: [1] arg=2 handler=0 (time)
Note 1105 DBUG: [0] arg=1 handler=0 (time)
-Note 1105 DBUG: [1] arg=3 handler=0 (time)
-Note 1105 DBUG: [2] arg=5 handler=2 (datetime)
+Note 1105 DBUG: [1] arg=2 handler=0 (time)
+Note 1105 DBUG: [2] arg=3 handler=2 (datetime)
SET SESSION debug_dbug="-d,Predicant_to_list_comparator";
SET SESSION debug_dbug="-d,Item_func_in";
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 89aa307486e..9bc40bd429f 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -2890,6 +2890,28 @@ Item_func_nullif::is_null()
return (null_value= (!compare() ? 1 : args[2]->null_value));
}
+void Item_func_case::reorder_args(uint start)
+{
+ /*
+ Reorder args, to have at first the optional CASE expression, then all WHEN
+ expressions, then all THEN expressions. And the optional ELSE expression
+ at the end.
+
+ We reorder an even number of arguments, starting from start.
+ */
+ uint count = (arg_count - start) / 2;
+ const size_t size= sizeof(Item*) * count * 2;
+ Item **arg_buffer= (Item **)my_safe_alloca(size);
+ memcpy(arg_buffer, &args[start], size);
+ for (uint i= 0; i < count; i++)
+ {
+ args[start + i]= arg_buffer[i*2];
+ args[start + i + count]= arg_buffer[i*2 + 1];
+ }
+ my_safe_afree(arg_buffer, size);
+}
+
+
/**
Find and return matching items for CASE or ELSE item if all compares
@@ -2917,8 +2939,8 @@ Item *Item_func_case_searched::find_item()
uint count= when_count();
for (uint i= 0 ; i < count ; i++)
{
- if (args[2 * i]->val_bool())
- return args[2 * i + 1];
+ if (args[i]->val_bool())
+ return args[i + count];
}
Item **pos= Item_func_case_searched::else_expr_addr();
return pos ? pos[0] : 0;
@@ -2930,7 +2952,7 @@ Item *Item_func_case_simple::find_item()
/* Compare every WHEN argument with it and return the first match */
uint idx;
if (!Predicant_to_list_comparator::cmp(this, &idx, NULL))
- return args[idx + 1];
+ return args[idx + when_count()];
Item **pos= Item_func_case_simple::else_expr_addr();
return pos ? pos[0] : 0;
}
@@ -2940,7 +2962,7 @@ Item *Item_func_decode_oracle::find_item()
{
uint idx;
if (!Predicant_to_list_comparator::cmp_nulls_equal(this, &idx))
- return args[idx + 1];
+ return args[idx + when_count()];
Item **pos= Item_func_decode_oracle::else_expr_addr();
return pos ? pos[0] : 0;
}
@@ -3038,27 +3060,11 @@ bool Item_func_case::time_op(MYSQL_TIME *ltime)
bool Item_func_case::fix_fields(THD *thd, Item **ref)
{
- /*
- buff should match stack usage from
- Item_func_case::val_int() -> Item_func_case::find_item()
- */
- uchar buff[MAX_FIELD_WIDTH*2+sizeof(String)*2+sizeof(String*)*2+sizeof(double)*2+sizeof(longlong)*2];
-
- if (!(arg_buffer= (Item**) thd->alloc(sizeof(Item*)*(arg_count))))
- return TRUE;
-
bool res= Item_func::fix_fields(thd, ref);
Item **pos= else_expr_addr();
if (!pos || pos[0]->maybe_null)
maybe_null= 1;
-
- /*
- Call check_stack_overrun after fix_fields to be sure that stack variable
- is not optimized away
- */
- if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
- return TRUE; // Fatal error flag is set!
return res;
}
@@ -3068,8 +3074,11 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref)
THD::change_item_tree() if needed.
*/
-static void change_item_tree_if_needed(THD *thd, Item **place, Item *new_value)
+static void propagate_and_change_item_tree(THD *thd, Item **place,
+ COND_EQUAL *cond,
+ const Item::Context &ctx)
{
+ Item *new_value= (*place)->propagate_equal_fields(thd, ctx, cond);
if (new_value && *place != new_value)
thd->change_item_tree(place, new_value);
}
@@ -3087,8 +3096,8 @@ bool Item_func_case_simple::prepare_predicant_and_values(THD *thd,
for (uint i= 0 ; i < ncases; i++)
{
if (nulls_equal ?
- add_value("case..when", this, i * 2 + 1) :
- add_value_skip_null("case..when", this, i * 2 + 1, &have_null))
+ add_value("case..when", this, i + 1) :
+ add_value_skip_null("case..when", this, i + 1, &have_null))
return true;
}
all_values_added(&tmp, &type_cnt, &m_found_types);
@@ -3102,16 +3111,14 @@ bool Item_func_case_simple::prepare_predicant_and_values(THD *thd,
void Item_func_case_searched::fix_length_and_dec()
{
THD *thd= current_thd;
- Item **else_ptr= Item_func_case_searched::else_expr_addr();
- aggregate_then_and_else_arguments(thd, &args[1], when_count(), else_ptr);
+ aggregate_then_and_else_arguments(thd, when_count());
}
void Item_func_case_simple::fix_length_and_dec()
{
THD *thd= current_thd;
- Item **else_ptr= Item_func_case_simple::else_expr_addr();
- if (!aggregate_then_and_else_arguments(thd, &args[2], when_count(), else_ptr))
+ if (!aggregate_then_and_else_arguments(thd, when_count() + 1))
aggregate_switch_and_when_arguments(thd, false);
}
@@ -3119,8 +3126,7 @@ void Item_func_case_simple::fix_length_and_dec()
void Item_func_decode_oracle::fix_length_and_dec()
{
THD *thd= current_thd;
- Item **else_ptr= Item_func_decode_oracle::else_expr_addr();
- if (!aggregate_then_and_else_arguments(thd, &args[2], when_count(), else_ptr))
+ if (!aggregate_then_and_else_arguments(thd, when_count() + 1))
aggregate_switch_and_when_arguments(thd, true);
}
@@ -3130,40 +3136,16 @@ void Item_func_decode_oracle::fix_length_and_dec()
and collations when string result
@param THD - current thd
- @param them_expr - the pointer to the leftmost THEN argument in args[]
- @param count - the number or THEN..ELSE pairs
- @param else_epxr - the pointer to the ELSE arguments in args[]
- (or NULL is there is not ELSE)
+ @param start - an element in args to start aggregating from
*/
-bool Item_func_case::aggregate_then_and_else_arguments(THD *thd,
- Item **then_expr,
- uint count,
- Item **else_expr)
+bool Item_func_case::aggregate_then_and_else_arguments(THD *thd, uint start)
{
- Item **agg= arg_buffer;
- uint nagg;
-
- for (nagg= 0 ; nagg < count ; nagg++)
- agg[nagg]= then_expr[nagg * 2];
-
- if (else_expr)
- agg[nagg++]= *else_expr;
-
- if (aggregate_for_result(func_name(), agg, nagg, true))
+ if (aggregate_for_result(func_name(), args + start, arg_count - start, true))
return true;
- if (fix_attributes(agg, nagg))
+ if (fix_attributes(args + start, arg_count - start))
return true;
- /*
- Copy all modified THEN and ELSE items back to then_expr[] array.
- Some of the items might have been changed to Item_func_conv_charset.
- */
- for (nagg= 0 ; nagg < count ; nagg++)
- change_item_tree_if_needed(thd, &then_expr[nagg * 2], agg[nagg]);
-
- if (else_expr)
- change_item_tree_if_needed(thd, else_expr, agg[nagg++]);
return false;
}
@@ -3175,8 +3157,6 @@ bool Item_func_case::aggregate_then_and_else_arguments(THD *thd,
bool Item_func_case_simple::aggregate_switch_and_when_arguments(THD *thd,
bool nulls_eq)
{
- Item **agg= arg_buffer;
- uint nagg;
uint ncases= when_count();
m_found_types= 0;
if (prepare_predicant_and_values(thd, &m_found_types, nulls_eq))
@@ -3190,17 +3170,7 @@ bool Item_func_case_simple::aggregate_switch_and_when_arguments(THD *thd,
return true;
}
- /*
- As the predicant expression and WHEN expressions
- are intermixed in args[] array THEN and ELSE items,
- extract the first expression and all WHEN expressions into
- a temporary array, to process them easier.
- */
- agg[0]= args[0]; // The predicant
- for (nagg= 0; nagg < ncases ; nagg++)
- agg[nagg+1]= args[nagg * 2 + 1];
- nagg++;
- if (!(m_found_types= collect_cmp_types(agg, nagg)))
+ if (!(m_found_types= collect_cmp_types(args, ncases + 1)))
return true;
if (m_found_types & (1U << STRING_RESULT))
@@ -3229,17 +3199,8 @@ bool Item_func_case_simple::aggregate_switch_and_when_arguments(THD *thd,
CASE utf16_item WHEN CONVERT(latin1_item USING utf16) THEN ... END
*/
- if (agg_arg_charsets_for_comparison(cmp_collation, agg, nagg))
+ if (agg_arg_charsets_for_comparison(cmp_collation, args, ncases + 1))
return true;
- /*
- Now copy first expression and all WHEN expressions back to args[]
- arrray, because some of the items might have been changed to converters
- (e.g. Item_func_conv_charset, or Item_string for constants).
- */
- change_item_tree_if_needed(thd, &args[0], agg[0]);
-
- for (nagg= 0; nagg < ncases; nagg++)
- change_item_tree_if_needed(thd, &args[nagg * 2 + 1], agg[nagg + 1]);
}
if (make_unique_cmp_items(thd, cmp_collation.collation))
@@ -3256,78 +3217,57 @@ Item* Item_func_case_simple::propagate_equal_fields(THD *thd,
const Type_handler *first_expr_cmp_handler;
first_expr_cmp_handler= args[0]->type_handler_for_comparison();
- for (uint i= 0; i < arg_count; i++)
+ /*
+ Cannot replace the CASE (the switch) argument if
+ there are multiple comparison types were found, or found a single
+ comparison type that is not equal to args[0]->cmp_type().
+
+ - Example: multiple comparison types, can't propagate:
+ WHERE CASE str_column
+ WHEN 'string' THEN TRUE
+ WHEN 1 THEN TRUE
+ ELSE FALSE END;
+
+ - Example: a single incompatible comparison type, can't propagate:
+ WHERE CASE str_column
+ WHEN DATE'2001-01-01' THEN TRUE
+ ELSE FALSE END;
+
+ - Example: a single incompatible comparison type, can't propagate:
+ WHERE CASE str_column
+ WHEN 1 THEN TRUE
+ ELSE FALSE END;
+
+ - Example: a single compatible comparison type, ok to propagate:
+ WHERE CASE str_column
+ WHEN 'str1' THEN TRUE
+ WHEN 'str2' THEN TRUE
+ ELSE FALSE END;
+ */
+ if (m_found_types == (1UL << first_expr_cmp_handler->cmp_type()))
+ propagate_and_change_item_tree(thd, &args[0], cond,
+ Context(ANY_SUBST, first_expr_cmp_handler, cmp_collation.collation));
+
+ /*
+ These arguments are in comparison.
+ Allow invariants of the same value during propagation.
+ Note, as we pass ANY_SUBST, none of the WHEN arguments will be
+ replaced to zero-filled constants (only IDENTITY_SUBST allows this).
+ Such a change for WHEN arguments would require rebuilding cmp_items.
+ */
+ uint i, count= when_count();
+ for (i= 1; i <= count; i++)
{
- /*
- These arguments are in comparison.
- Allow invariants of the same value during propagation.
- Note, as we pass ANY_SUBST, none of the WHEN arguments will be
- replaced to zero-filled constants (only IDENTITY_SUBST allows this).
- Such a change for WHEN arguments would require rebuilding cmp_items.
- */
- Item *new_item= 0;
- if (i == 0) // Then CASE (the switch) argument
- {
- /*
- Cannot replace the CASE (the switch) argument if
- there are multiple comparison types were found, or found a single
- comparison type that is not equal to args[0]->cmp_type().
-
- - Example: multiple comparison types, can't propagate:
- WHERE CASE str_column
- WHEN 'string' THEN TRUE
- WHEN 1 THEN TRUE
- ELSE FALSE END;
-
- - Example: a single incompatible comparison type, can't propagate:
- WHERE CASE str_column
- WHEN DATE'2001-01-01' THEN TRUE
- ELSE FALSE END;
-
- - Example: a single incompatible comparison type, can't propagate:
- WHERE CASE str_column
- WHEN 1 THEN TRUE
- ELSE FALSE END;
-
- - Example: a single compatible comparison type, ok to propagate:
- WHERE CASE str_column
- WHEN 'str1' THEN TRUE
- WHEN 'str2' THEN TRUE
- ELSE FALSE END;
- */
- if (m_found_types == (1UL << first_expr_cmp_handler->cmp_type()))
- new_item= args[i]->propagate_equal_fields(thd,
- Context(
- ANY_SUBST,
- first_expr_cmp_handler,
- cmp_collation.collation),
- cond);
- }
- else if ((i % 2) == 1 && i != arg_count - 1) // WHEN arguments
- {
- /*
- These arguments are in comparison.
- Allow invariants of the same value during propagation.
- Note, as we pass ANY_SUBST, none of the WHEN arguments will be
- replaced to zero-filled constants (only IDENTITY_SUBST allows this).
- Such a change for WHEN arguments would require rebuilding cmp_items.
- */
- Type_handler_hybrid_field_type tmp(first_expr_cmp_handler);
- if (!tmp.aggregate_for_comparison(args[i]->type_handler_for_comparison()))
- new_item= args[i]->propagate_equal_fields(thd,
- Context(
- ANY_SUBST,
- tmp.type_handler(),
- cmp_collation.collation),
- cond);
- }
- else // THEN and ELSE arguments (they are not in comparison)
- {
- new_item= args[i]->propagate_equal_fields(thd, Context_identity(), cond);
- }
- if (new_item && new_item != args[i])
- thd->change_item_tree(&args[i], new_item);
+ Type_handler_hybrid_field_type tmp(first_expr_cmp_handler);
+ if (!tmp.aggregate_for_comparison(args[i]->type_handler_for_comparison()))
+ propagate_and_change_item_tree(thd, &args[i], cond,
+ Context(ANY_SUBST, tmp.type_handler(), cmp_collation.collation));
}
+
+ // THEN and ELSE arguments (they are not in comparison)
+ for (; i < arg_count; i++)
+ propagate_and_change_item_tree(thd, &args[i], cond, Context_identity());
+
return this;
}
@@ -3339,9 +3279,9 @@ void Item_func_case::print_when_then_arguments(String *str,
for (uint i=0 ; i < count ; i++)
{
str->append(STRING_WITH_LEN("when "));
- items[i * 2]->print_parenthesised(str, query_type, precedence());
+ items[i]->print_parenthesised(str, query_type, precedence());
str->append(STRING_WITH_LEN(" then "));
- items[i * 2 + 1]->print_parenthesised(str, query_type, precedence());
+ items[i + count]->print_parenthesised(str, query_type, precedence());
str->append(' ');
}
}
@@ -3381,6 +3321,28 @@ void Item_func_case_simple::print(String *str, enum_query_type query_type)
}
+void Item_func_decode_oracle::print(String *str, enum_query_type query_type)
+{
+ str->append(func_name());
+ str->append('(');
+ args[0]->print(str, query_type);
+ for (uint i= 1, count= when_count() ; i <= count; i++)
+ {
+ str->append(',');
+ args[i]->print(str, query_type);
+ str->append(',');
+ args[i+count]->print(str, query_type);
+ }
+ Item **else_expr= Item_func_case_simple::else_expr_addr();
+ if (else_expr)
+ {
+ str->append(',');
+ (*else_expr)->print(str, query_type);
+ }
+ str->append(')');
+}
+
+
/**
Coalesce - return first not NULL argument.
*/
@@ -4910,17 +4872,14 @@ Item *Item_cond::propagate_equal_fields(THD *thd,
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
DBUG_ASSERT(arg_count == 0);
List_iterator<Item> li(list);
- Item *item;
- while ((item= li++))
+ while (li++)
{
/*
- The exact value of the second parameter to propagate_equal_fields()
+ The exact value of the last parameter to propagate_and_change_item_tree()
is not important at this point. Item_func derivants will create and
pass their own context to the arguments.
*/
- Item *new_item= item->propagate_equal_fields(thd, Context_boolean(), cond);
- if (new_item && new_item != item)
- thd->change_item_tree(li.ref(), new_item);
+ propagate_and_change_item_tree(thd, li.ref(), cond, Context_boolean());
}
return this;
}
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 3d11a226d07..d10bac0fced 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -2104,15 +2104,13 @@ class Item_func_case :public Item_func_case_expression
protected:
String tmp_value;
DTCollation cmp_collation;
- Item **arg_buffer;
- bool aggregate_then_and_else_arguments(THD *thd,
- Item **items, uint count,
- Item **else_expr);
+ bool aggregate_then_and_else_arguments(THD *thd, uint count);
virtual Item **else_expr_addr() const= 0;
virtual Item *find_item()= 0;
void print_when_then_arguments(String *str, enum_query_type query_type,
Item **items, uint count);
void print_else_argument(String *str, enum_query_type query_type, Item *item);
+ void reorder_args(uint start);
public:
Item_func_case(THD *thd, List<Item> &list)
:Item_func_case_expression(thd, list)
@@ -2129,13 +2127,6 @@ public:
enum precedence precedence() const { return BETWEEN_PRECEDENCE; }
CHARSET_INFO *compare_collation() const { return cmp_collation.collation; }
bool need_parentheses_in_default() { return true; }
- Item *build_clone(THD *thd)
- {
- Item_func_case *clone= (Item_func_case *) Item_func::build_clone(thd);
- if (clone)
- clone->arg_buffer= 0;
- return clone;
- }
};
@@ -2156,6 +2147,7 @@ public:
:Item_func_case(thd, list)
{
DBUG_ASSERT(arg_count >= 2);
+ reorder_args(0);
}
void print(String *str, enum_query_type query_type);
void fix_length_and_dec();
@@ -2200,6 +2192,7 @@ public:
m_found_types(0)
{
DBUG_ASSERT(arg_count >= 3);
+ reorder_args(1);
}
void cleanup()
{
@@ -2233,8 +2226,7 @@ public:
:Item_func_case_simple(thd, list)
{ }
const char *func_name() const { return "decode_oracle"; }
- void print(String *str, enum_query_type query_type)
- { Item_func::print(str, query_type); }
+ void print(String *str, enum_query_type query_type);
void fix_length_and_dec();
Item *find_item();
Item *get_copy(THD *thd)