1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-26 07:02:12 +03:00

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), 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) 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) if (first_expr_arg)
{ {
first_expr_num= list.elements; first_expr_num= 0;
list.push_back(first_expr_arg, thd->mem_root); list.push_front(first_expr_arg, thd->mem_root);
} }
if (else_expr_arg) 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); list.push_back(else_expr_arg, thd->mem_root);
} }
set_arguments(thd, list); 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)); bzero(&cmp_items, sizeof(cmp_items));
} }
@ -3045,18 +3062,17 @@ Item *Item_func_case::find_item(String *str)
if (first_expr_num == -1) 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 // No expression between CASE and the first WHEN
if (args[i]->val_bool()) if (args[i]->val_bool())
return args[i+1]; return args[i+nwhens];
continue;
} }
} }
else else
{ {
/* Compare every WHEN argument with it and return the first match */ /* 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) if (args[i]->real_item()->type() == NULL_ITEM)
continue; continue;
@ -3065,13 +3081,13 @@ Item *Item_func_case::find_item(String *str)
DBUG_ASSERT(cmp_items[(uint)cmp_type]); DBUG_ASSERT(cmp_items[(uint)cmp_type]);
if (!(value_added_map & (1U << (uint)cmp_type))) if (!(value_added_map & (1U << (uint)cmp_type)))
{ {
cmp_items[(uint)cmp_type]->store_value(args[first_expr_num]); cmp_items[(uint)cmp_type]->store_value(args[0]);
if ((null_value=args[first_expr_num]->null_value)) if ((null_value= args[0]->null_value))
return else_expr_num != -1 ? args[else_expr_num] : 0; return else_expr_num != -1 ? args[else_expr_num] : 0;
value_added_map|= 1U << (uint)cmp_type; value_added_map|= 1U << (uint)cmp_type;
} }
if (cmp_items[(uint)cmp_type]->cmp(args[i]) == FALSE) 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 // 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]; 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); bool res= Item_func::fix_fields(thd, ref);
/* /*
Call check_stack_overrun after fix_fields to be sure that stack variable 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 Check if (*place) and new_value points to different Items and call
THD::change_item_tree() if needed. 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, static void change_item_tree_if_needed(THD *thd, Item **place, Item *new_value)
Item **place,
Item *new_value)
{ {
if (*place == new_value) if (new_value && *place != new_value)
return; thd->change_item_tree(place, new_value);
thd->change_item_tree(place, new_value);
} }
void Item_func_case::fix_length_and_dec() void Item_func_case::fix_length_and_dec()
{ {
Item **agg= arg_buffer;
uint nagg;
THD *thd= current_thd;
m_found_types= 0; m_found_types= 0;
if (else_expr_num == -1 || args[else_expr_num]->maybe_null) if (else_expr_num == -1 || args[else_expr_num]->maybe_null)
maybe_null= 1; maybe_null= 1;
@ -3224,33 +3223,17 @@ void Item_func_case::fix_length_and_dec()
Aggregate all THEN and ELSE expression types Aggregate all THEN and ELSE expression types
and collations when string result and collations when string result
*/ */
Item **rets= args + first_expr_num + 1 + nwhens;
for (nagg= 0 ; nagg < ncases/2 ; nagg++) uint nrets= nwhens + (else_expr_num != -1);
agg[nagg]= args[nagg*2+1]; set_handler_by_field_type(agg_field_type(rets, nrets, true));
if (else_expr_num != -1)
agg[nagg++]= args[else_expr_num];
set_handler_by_field_type(agg_field_type(agg, nagg, true));
if (Item_func_case::result_type() == STRING_RESULT) 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; 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 else
{ fix_attributes(rets, nrets);
fix_attributes(agg, nagg);
}
/* /*
Aggregate first expression and all WHEN expression types 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) if (first_expr_num != -1)
{ {
uint i; left_cmp_type= args[0]->cmp_type();
agg[0]= args[first_expr_num];
left_cmp_type= agg[0]->cmp_type();
/* if (!(m_found_types= collect_cmp_types(args, nwhens + 1)))
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)))
return; return;
Item *date_arg= 0; Item *date_arg= 0;
if (m_found_types & (1U << TIME_RESULT)) 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)) 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 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; 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]) if (m_found_types & (1U << i) && !cmp_items[i])
{ {
DBUG_ASSERT((Item_result)i != ROW_RESULT); DBUG_ASSERT((Item_result)i != ROW_RESULT);
if (!(cmp_items[i]= if (!(cmp_items[i]=
cmp_item::get_comparator((Item_result)i, date_arg, cmp_item::get_comparator((Item_result)i, date_arg,
cmp_collation.collation))) cmp_collation.collation)))
@ -3342,75 +3304,59 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_
return this; 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: These arguments are in comparison.
CASE x0 WHEN x1 .. WHEN x2 .. WHEN x3 .. Allow invariants of the same value during propagation.
Odd "i" values cover items that are not in comparison: Note, as we pass ANY_SUBST, none of the WHEN arguments will be
CASE ... THEN y1 ... THEN y2 ... THEN y3 ... ELSE y4 END 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; Item_result tmp_cmp_type= item_cmp_type(args[first_expr_num], args[i]);
if ((int) i == first_expr_num) // Then CASE (the switch) argument change_item_tree_if_needed(thd, args + i,
{ args[i]->propagate_equal_fields(thd, Context(ANY_SUBST, tmp_cmp_type,
/* cmp_collation.collation),
Cannot replace the CASE (the switch) argument if cond));
there are multiple comparison types were found, or found a single }
comparison type that is not equal to args[0]->cmp_type(). for (; i < arg_count ; i++) // THEN expressions and optional ELSE expression
{
- Example: multiple comparison types, can't propagate: change_item_tree_if_needed(thd, args + i,
WHERE CASE str_column args[i]->propagate_equal_fields(thd, Context_identity(), cond));
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);
} }
return this; 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 uint Item_func_case::decimal_precision() const
{ {
int max_int_part=0; int max_int_part=0;
for (uint i=0 ; i < ncases ; i+=2) for (uint i=first_expr_num + 1 + nwhens ; i < arg_count; i++)
set_if_bigger(max_int_part, args[i+1]->decimal_int_part()); set_if_bigger(max_int_part, args[i]->decimal_int_part());
if (else_expr_num != -1)
set_if_bigger(max_int_part, args[else_expr_num]->decimal_int_part());
return MY_MIN(max_int_part + decimals, DECIMAL_MAX_PRECISION); 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 ")); str->append(STRING_WITH_LEN("case "));
if (first_expr_num != -1) 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(' '); 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 ")); str->append(STRING_WITH_LEN("when "));
args[i]->print_parenthesised(str, query_type, precedence()); args[i]->print_parenthesised(str, query_type, precedence());
str->append(STRING_WITH_LEN(" then ")); 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(' '); str->append(' ');
} }
if (else_expr_num != -1) 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; int first_expr_num, else_expr_num;
enum Item_result left_cmp_type; enum Item_result left_cmp_type;
String tmp_value; String tmp_value;
uint ncases; uint nwhens;
Item_result cmp_type; Item_result cmp_type;
DTCollation cmp_collation; DTCollation cmp_collation;
cmp_item *cmp_items[6]; /* For all result types */ cmp_item *cmp_items[6]; /* For all result types */
cmp_item *case_item; cmp_item *case_item;
Item **arg_buffer;
uint m_found_types; uint m_found_types;
public: public:
Item_func_case(THD *thd, List<Item> &list, Item *first_expr_arg, Item_func_case(THD *thd, List<Item> &list, Item *first_expr_arg,
@ -1593,7 +1592,6 @@ public:
if (clone) if (clone)
{ {
clone->case_item= 0; clone->case_item= 0;
clone->arg_buffer= 0;
bzero(&clone->cmp_items, sizeof(cmp_items)); bzero(&clone->cmp_items, sizeof(cmp_items));
} }
return clone; return clone;