summaryrefslogtreecommitdiff
path: root/sql/item_cmpfunc.cc
diff options
context:
space:
mode:
authorSergei Golubchik <serg@mariadb.org>2018-03-12 18:53:59 +0100
committerSergei Golubchik <serg@mariadb.org>2018-03-14 12:40:00 +0100
commitbf1ca14ff3f3faa9f7a018097b25aa0f66d068cd (patch)
tree9399202e713a5a7fd455bd7ce230eeac7926bf4b /sql/item_cmpfunc.cc
parentc14733f64e0ec372b255587c0a7726e752c84d16 (diff)
downloadmariadb-git-bf1ca14ff3f3faa9f7a018097b25aa0f66d068cd.tar.gz
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.
Diffstat (limited to 'sql/item_cmpfunc.cc')
-rw-r--r--sql/item_cmpfunc.cc245
1 files changed, 94 insertions, 151 deletions
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index edaf6e87621..7d4c646abee 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -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)