diff options
author | Sergei Golubchik <serg@mariadb.org> | 2018-03-12 18:53:59 +0100 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2018-03-14 12:40:00 +0100 |
commit | bf1ca14ff3f3faa9f7a018097b25aa0f66d068cd (patch) | |
tree | 9399202e713a5a7fd455bd7ce230eeac7926bf4b /sql/item_cmpfunc.cc | |
parent | c14733f64e0ec372b255587c0a7726e752c84d16 (diff) | |
download | mariadb-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.cc | 245 |
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) |