Commit bf1ca14f 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.
parent c14733f6
......@@ -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;
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,19 +3304,9 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_
return this;
}
for (uint i= 0; i < arg_count; i++)
{
/*
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
*/
Item *new_item= 0;
if ((int) i == first_expr_num) // Then CASE (the switch) argument
{
/*
Cannot replace the CASE (the switch) argument if
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().
......@@ -3381,14 +3333,12 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_
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,
change_item_tree_if_needed(thd, args,
args[0]->propagate_equal_fields(thd, Context(ANY_SUBST, left_cmp_type,
cmp_collation.collation),
cond);
}
else if ((i % 2) == 0) // WHEN arguments
cond));
uint i= 1;
for (; i <= nwhens ; i++) // WHEN expressions
{
/*
These arguments are in comparison.
......@@ -3398,19 +3348,15 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_
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,
change_item_tree_if_needed(thd, args + i,
args[i]->propagate_equal_fields(thd, Context(ANY_SUBST, tmp_cmp_type,
cmp_collation.collation),
cond);
cond));
}
else // THEN and ELSE arguments (they are not in comparison)
for (; i < arg_count ; i++) // THEN expressions and optional ELSE expression
{
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);
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)
......
......@@ -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 @@ class Item_func_case :public Item_func_hybrid_field_type
if (clone)
{
clone->case_item= 0;
clone->arg_buffer= 0;
bzero(&clone->cmp_items, sizeof(cmp_items));
}
return clone;
......
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