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 commit is contained in:
Sergei Golubchik 2018-03-12 18:53:59 +01:00
parent c14733f64e
commit bf1ca14ff3
2 changed files with 95 additions and 154 deletions

View file

@ -3003,11 +3003,12 @@ Item_func_case::Item_func_case(THD *thd, List<Item> &list,
Item_func_hybrid_field_type(thd), first_expr_num(-1), else_expr_num(-1),
left_cmp_type(INT_RESULT), case_item(0), m_found_types(0)
{
ncases= list.elements;
DBUG_ASSERT(list.elements % 2 == 0);
nwhens= list.elements / 2;
if (first_expr_arg)
{
first_expr_num= list.elements;
list.push_back(first_expr_arg, thd->mem_root);
first_expr_num= 0;
list.push_front(first_expr_arg, thd->mem_root);
}
if (else_expr_arg)
{
@ -3015,6 +3016,22 @@ Item_func_case::Item_func_case(THD *thd, List<Item> &list,
list.push_back(else_expr_arg, thd->mem_root);
}
set_arguments(thd, list);
/*
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.
*/
const size_t size= sizeof(Item*)*nwhens*2;
Item **arg_buffer= (Item **)my_safe_alloca(size);
memcpy(arg_buffer, args + first_expr_num + 1, size);
for (uint i= 0; i < nwhens ; i++)
{
args[first_expr_num + 1 + i]= arg_buffer[i*2];
args[first_expr_num + 1 + i + nwhens] = arg_buffer[i*2 + 1];
}
my_safe_afree(arg_buffer, size);
bzero(&cmp_items, sizeof(cmp_items));
}
@ -3045,18 +3062,17 @@ Item *Item_func_case::find_item(String *str)
if (first_expr_num == -1)
{
for (uint i=0 ; i < ncases ; i+=2)
for (uint i=0 ; i < nwhens ; i++)
{
// No expression between CASE and the first WHEN
if (args[i]->val_bool())
return args[i+1];
continue;
return args[i+nwhens];
}
}
else
{
/* Compare every WHEN argument with it and return the first match */
for (uint i=0 ; i < ncases ; i+=2)
for (uint i=1 ; i <= nwhens; i++)
{
if (args[i]->real_item()->type() == NULL_ITEM)
continue;
@ -3065,13 +3081,13 @@ Item *Item_func_case::find_item(String *str)
DBUG_ASSERT(cmp_items[(uint)cmp_type]);
if (!(value_added_map & (1U << (uint)cmp_type)))
{
cmp_items[(uint)cmp_type]->store_value(args[first_expr_num]);
if ((null_value=args[first_expr_num]->null_value))
cmp_items[(uint)cmp_type]->store_value(args[0]);
if ((null_value= args[0]->null_value))
return else_expr_num != -1 ? args[else_expr_num] : 0;
value_added_map|= 1U << (uint)cmp_type;
}
if (cmp_items[(uint)cmp_type]->cmp(args[i]) == FALSE)
return args[i + 1];
return args[i + nwhens];
}
}
// No, WHEN clauses all missed, return ELSE expression
@ -3174,9 +3190,6 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref)
*/
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*)*(ncases+1))))
return TRUE;
bool res= Item_func::fix_fields(thd, ref);
/*
Call check_stack_overrun after fix_fields to be sure that stack variable
@ -3191,31 +3204,17 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref)
/**
Check if (*place) and new_value points to different Items and call
THD::change_item_tree() if needed.
This function is a workaround for implementation deficiency in
Item_func_case. The problem there is that the 'args' attribute contains
Items from different expressions.
The function must not be used elsewhere and will be remove eventually.
*/
static void change_item_tree_if_needed(THD *thd,
Item **place,
Item *new_value)
static void change_item_tree_if_needed(THD *thd, Item **place, Item *new_value)
{
if (*place == new_value)
return;
thd->change_item_tree(place, new_value);
if (new_value && *place != new_value)
thd->change_item_tree(place, new_value);
}
void Item_func_case::fix_length_and_dec()
{
Item **agg= arg_buffer;
uint nagg;
THD *thd= current_thd;
m_found_types= 0;
if (else_expr_num == -1 || args[else_expr_num]->maybe_null)
maybe_null= 1;
@ -3224,33 +3223,17 @@ void Item_func_case::fix_length_and_dec()
Aggregate all THEN and ELSE expression types
and collations when string result
*/
for (nagg= 0 ; nagg < ncases/2 ; nagg++)
agg[nagg]= args[nagg*2+1];
if (else_expr_num != -1)
agg[nagg++]= args[else_expr_num];
set_handler_by_field_type(agg_field_type(agg, nagg, true));
Item **rets= args + first_expr_num + 1 + nwhens;
uint nrets= nwhens + (else_expr_num != -1);
set_handler_by_field_type(agg_field_type(rets, nrets, true));
if (Item_func_case::result_type() == STRING_RESULT)
{
if (count_string_result_length(Item_func_case::field_type(), agg, nagg))
if (count_string_result_length(Item_func_case::field_type(), rets, nrets))
return;
/*
Copy all THEN and ELSE items back to args[] array.
Some of the items might have been changed to Item_func_conv_charset.
*/
for (nagg= 0 ; nagg < ncases / 2 ; nagg++)
change_item_tree_if_needed(thd, &args[nagg * 2 + 1], agg[nagg]);
if (else_expr_num != -1)
change_item_tree_if_needed(thd, &args[else_expr_num], agg[nagg++]);
}
else
{
fix_attributes(agg, nagg);
}
fix_attributes(rets, nrets);
/*
Aggregate first expression and all WHEN expression types
@ -3258,25 +3241,14 @@ void Item_func_case::fix_length_and_dec()
*/
if (first_expr_num != -1)
{
uint i;
agg[0]= args[first_expr_num];
left_cmp_type= agg[0]->cmp_type();
left_cmp_type= args[0]->cmp_type();
/*
As the first 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.
*/
for (nagg= 0; nagg < ncases/2 ; nagg++)
agg[nagg+1]= args[nagg*2];
nagg++;
if (!(m_found_types= collect_cmp_types(agg, nagg)))
if (!(m_found_types= collect_cmp_types(args, nwhens + 1)))
return;
Item *date_arg= 0;
if (m_found_types & (1U << TIME_RESULT))
date_arg= find_date_time_item(args, arg_count, 0);
date_arg= find_date_time_item(args, nwhens + 1, 0);
if (m_found_types & (1U << STRING_RESULT))
{
@ -3304,25 +3276,15 @@ void Item_func_case::fix_length_and_dec()
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, nwhens + 1))
return;
/*
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[first_expr_num], agg[0]);
for (nagg= 0; nagg < ncases / 2; nagg++)
change_item_tree_if_needed(thd, &args[nagg * 2], agg[nagg + 1]);
}
for (i= 0; i <= (uint)TIME_RESULT; i++)
for (uint i= 0; i <= (uint)TIME_RESULT; i++)
{
if (m_found_types & (1U << i) && !cmp_items[i])
{
DBUG_ASSERT((Item_result)i != ROW_RESULT);
if (!(cmp_items[i]=
cmp_item::get_comparator((Item_result)i, date_arg,
cmp_collation.collation)))
@ -3342,75 +3304,59 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_
return this;
}
for (uint i= 0; i < arg_count; i++)
/*
First, replace CASE expression.
We 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 << left_cmp_type))
change_item_tree_if_needed(thd, args,
args[0]->propagate_equal_fields(thd, Context(ANY_SUBST, left_cmp_type,
cmp_collation.collation),
cond));
uint i= 1;
for (; i <= nwhens ; i++) // WHEN expressions
{
/*
Even "i" values cover items that are in a comparison context:
CASE x0 WHEN x1 .. WHEN x2 .. WHEN x3 ..
Odd "i" values cover items that are not in comparison:
CASE ... THEN y1 ... THEN y2 ... THEN y3 ... ELSE y4 END
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 ((int) i == first_expr_num) // 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 << left_cmp_type))
new_item= args[i]->propagate_equal_fields(thd,
Context(
ANY_SUBST,
left_cmp_type,
cmp_collation.collation),
cond);
}
else if ((i % 2) == 0) // 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.
*/
Item_result tmp_cmp_type= item_cmp_type(args[first_expr_num], args[i]);
new_item= args[i]->propagate_equal_fields(thd,
Context(
ANY_SUBST,
tmp_cmp_type,
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);
Item_result tmp_cmp_type= item_cmp_type(args[first_expr_num], args[i]);
change_item_tree_if_needed(thd, args + i,
args[i]->propagate_equal_fields(thd, Context(ANY_SUBST, tmp_cmp_type,
cmp_collation.collation),
cond));
}
for (; i < arg_count ; i++) // THEN expressions and optional ELSE expression
{
change_item_tree_if_needed(thd, args + i,
args[i]->propagate_equal_fields(thd, Context_identity(), cond));
}
return this;
}
@ -3419,11 +3365,8 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_
uint Item_func_case::decimal_precision() const
{
int max_int_part=0;
for (uint i=0 ; i < ncases ; i+=2)
set_if_bigger(max_int_part, args[i+1]->decimal_int_part());
if (else_expr_num != -1)
set_if_bigger(max_int_part, args[else_expr_num]->decimal_int_part());
for (uint i=first_expr_num + 1 + nwhens ; i < arg_count; i++)
set_if_bigger(max_int_part, args[i]->decimal_int_part());
return MY_MIN(max_int_part + decimals, DECIMAL_MAX_PRECISION);
}
@ -3438,15 +3381,15 @@ void Item_func_case::print(String *str, enum_query_type query_type)
str->append(STRING_WITH_LEN("case "));
if (first_expr_num != -1)
{
args[first_expr_num]->print_parenthesised(str, query_type, precedence());
args[0]->print_parenthesised(str, query_type, precedence());
str->append(' ');
}
for (uint i=0 ; i < ncases ; i+=2)
for (uint i= first_expr_num + 1 ; i < nwhens + first_expr_num + 1; i++)
{
str->append(STRING_WITH_LEN("when "));
args[i]->print_parenthesised(str, query_type, precedence());
str->append(STRING_WITH_LEN(" then "));
args[i+1]->print_parenthesised(str, query_type, precedence());
args[i+nwhens]->print_parenthesised(str, query_type, precedence());
str->append(' ');
}
if (else_expr_num != -1)

View file

@ -1558,12 +1558,11 @@ class Item_func_case :public Item_func_hybrid_field_type
int first_expr_num, else_expr_num;
enum Item_result left_cmp_type;
String tmp_value;
uint ncases;
uint nwhens;
Item_result cmp_type;
DTCollation cmp_collation;
cmp_item *cmp_items[6]; /* For all result types */
cmp_item *case_item;
Item **arg_buffer;
uint m_found_types;
public:
Item_func_case(THD *thd, List<Item> &list, Item *first_expr_arg,
@ -1593,7 +1592,6 @@ public:
if (clone)
{
clone->case_item= 0;
clone->arg_buffer= 0;
bzero(&clone->cmp_items, sizeof(cmp_items));
}
return clone;