mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01: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:
parent
c14733f64e
commit
bf1ca14ff3
2 changed files with 95 additions and 154 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue