Commit 7a903784 authored by Sergei Golubchik's avatar Sergei Golubchik

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 bf1ca14f
parent 4c77ef36
......@@ -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";
......@@ -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;
}
......
......@@ -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 @@ class Item_func_case :public Item_func_case_expression
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 @@ class Item_func_case_searched: public Item_func_case
: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 @@ class Item_func_case_simple: public Item_func_case,
m_found_types(0)
{
DBUG_ASSERT(arg_count >= 3);
reorder_args(1);
}
void cleanup()
{
......@@ -2233,8 +2226,7 @@ class Item_func_decode_oracle: public Item_func_case_simple
: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)
......
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