diff options
author | Sergei Golubchik <serg@mariadb.org> | 2018-03-28 20:16:13 +0200 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2018-03-30 09:45:03 +0200 |
commit | 7a903784b780ea127d534e510a521b4126c3a655 (patch) | |
tree | 618a1fa57864298a432ce8e0bf94fee3c7c970e7 | |
parent | 4c77ef36c69b2919f8d788c4de9e1f2da07410d7 (diff) | |
download | mariadb-git-7a903784b780ea127d534e510a521b4126c3a655.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.
this is a 10.3 version of bf1ca14ff3f
-rw-r--r-- | mysql-test/main/func_debug.result | 8 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 275 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 18 |
3 files changed, 126 insertions, 175 deletions
diff --git a/mysql-test/main/func_debug.result b/mysql-test/main/func_debug.result index f1453344e0e..7fcf522abba 100644 --- a/mysql-test/main/func_debug.result +++ b/mysql-test/main/func_debug.result @@ -1630,7 +1630,7 @@ c NULL Warnings: Note 1105 DBUG: [0] arg=1 handler=0 (bigint) -Note 1105 DBUG: [1] arg=3 handler=1 (decimal) +Note 1105 DBUG: [1] arg=2 handler=1 (decimal) DROP TABLE t1; # # MDEV-11555 CASE with a mixture of TIME and DATETIME returns a wrong result @@ -1649,9 +1649,9 @@ good was_bad_now_good one one Warnings: Note 1105 DBUG: [0] arg=1 handler=0 (time) -Note 1105 DBUG: [1] arg=3 handler=0 (time) +Note 1105 DBUG: [1] arg=2 handler=0 (time) Note 1105 DBUG: [0] arg=1 handler=0 (time) -Note 1105 DBUG: [1] arg=3 handler=0 (time) -Note 1105 DBUG: [2] arg=5 handler=2 (datetime) +Note 1105 DBUG: [1] arg=2 handler=0 (time) +Note 1105 DBUG: [2] arg=3 handler=2 (datetime) SET SESSION debug_dbug="-d,Predicant_to_list_comparator"; SET SESSION debug_dbug="-d,Item_func_in"; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 89aa307486e..9bc40bd429f 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2890,6 +2890,28 @@ Item_func_nullif::is_null() return (null_value= (!compare() ? 1 : args[2]->null_value)); } +void Item_func_case::reorder_args(uint start) +{ + /* + 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. + + We reorder an even number of arguments, starting from start. + */ + uint count = (arg_count - start) / 2; + const size_t size= sizeof(Item*) * count * 2; + Item **arg_buffer= (Item **)my_safe_alloca(size); + memcpy(arg_buffer, &args[start], size); + for (uint i= 0; i < count; i++) + { + args[start + i]= arg_buffer[i*2]; + args[start + i + count]= arg_buffer[i*2 + 1]; + } + my_safe_afree(arg_buffer, size); +} + + /** Find and return matching items for CASE or ELSE item if all compares @@ -2917,8 +2939,8 @@ Item *Item_func_case_searched::find_item() uint count= when_count(); for (uint i= 0 ; i < count ; i++) { - if (args[2 * i]->val_bool()) - return args[2 * i + 1]; + if (args[i]->val_bool()) + return args[i + count]; } Item **pos= Item_func_case_searched::else_expr_addr(); return pos ? pos[0] : 0; @@ -2930,7 +2952,7 @@ Item *Item_func_case_simple::find_item() /* Compare every WHEN argument with it and return the first match */ uint idx; if (!Predicant_to_list_comparator::cmp(this, &idx, NULL)) - return args[idx + 1]; + return args[idx + when_count()]; Item **pos= Item_func_case_simple::else_expr_addr(); return pos ? pos[0] : 0; } @@ -2940,7 +2962,7 @@ Item *Item_func_decode_oracle::find_item() { uint idx; if (!Predicant_to_list_comparator::cmp_nulls_equal(this, &idx)) - return args[idx + 1]; + return args[idx + when_count()]; Item **pos= Item_func_decode_oracle::else_expr_addr(); return pos ? pos[0] : 0; } @@ -3038,27 +3060,11 @@ bool Item_func_case::time_op(MYSQL_TIME *ltime) bool Item_func_case::fix_fields(THD *thd, Item **ref) { - /* - buff should match stack usage from - Item_func_case::val_int() -> Item_func_case::find_item() - */ - 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*)*(arg_count)))) - return TRUE; - bool res= Item_func::fix_fields(thd, ref); Item **pos= else_expr_addr(); if (!pos || pos[0]->maybe_null) maybe_null= 1; - - /* - Call check_stack_overrun after fix_fields to be sure that stack variable - is not optimized away - */ - if (check_stack_overrun(thd, STACK_MIN_SIZE, buff)) - return TRUE; // Fatal error flag is set! return res; } @@ -3068,8 +3074,11 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref) THD::change_item_tree() if needed. */ -static void change_item_tree_if_needed(THD *thd, Item **place, Item *new_value) +static void propagate_and_change_item_tree(THD *thd, Item **place, + COND_EQUAL *cond, + const Item::Context &ctx) { + Item *new_value= (*place)->propagate_equal_fields(thd, ctx, cond); if (new_value && *place != new_value) thd->change_item_tree(place, new_value); } @@ -3087,8 +3096,8 @@ bool Item_func_case_simple::prepare_predicant_and_values(THD *thd, for (uint i= 0 ; i < ncases; i++) { if (nulls_equal ? - add_value("case..when", this, i * 2 + 1) : - add_value_skip_null("case..when", this, i * 2 + 1, &have_null)) + add_value("case..when", this, i + 1) : + add_value_skip_null("case..when", this, i + 1, &have_null)) return true; } all_values_added(&tmp, &type_cnt, &m_found_types); @@ -3102,16 +3111,14 @@ bool Item_func_case_simple::prepare_predicant_and_values(THD *thd, void Item_func_case_searched::fix_length_and_dec() { THD *thd= current_thd; - Item **else_ptr= Item_func_case_searched::else_expr_addr(); - aggregate_then_and_else_arguments(thd, &args[1], when_count(), else_ptr); + aggregate_then_and_else_arguments(thd, when_count()); } void Item_func_case_simple::fix_length_and_dec() { THD *thd= current_thd; - Item **else_ptr= Item_func_case_simple::else_expr_addr(); - if (!aggregate_then_and_else_arguments(thd, &args[2], when_count(), else_ptr)) + if (!aggregate_then_and_else_arguments(thd, when_count() + 1)) aggregate_switch_and_when_arguments(thd, false); } @@ -3119,8 +3126,7 @@ void Item_func_case_simple::fix_length_and_dec() void Item_func_decode_oracle::fix_length_and_dec() { THD *thd= current_thd; - Item **else_ptr= Item_func_decode_oracle::else_expr_addr(); - if (!aggregate_then_and_else_arguments(thd, &args[2], when_count(), else_ptr)) + if (!aggregate_then_and_else_arguments(thd, when_count() + 1)) aggregate_switch_and_when_arguments(thd, true); } @@ -3130,40 +3136,16 @@ void Item_func_decode_oracle::fix_length_and_dec() and collations when string result @param THD - current thd - @param them_expr - the pointer to the leftmost THEN argument in args[] - @param count - the number or THEN..ELSE pairs - @param else_epxr - the pointer to the ELSE arguments in args[] - (or NULL is there is not ELSE) + @param start - an element in args to start aggregating from */ -bool Item_func_case::aggregate_then_and_else_arguments(THD *thd, - Item **then_expr, - uint count, - Item **else_expr) +bool Item_func_case::aggregate_then_and_else_arguments(THD *thd, uint start) { - Item **agg= arg_buffer; - uint nagg; - - for (nagg= 0 ; nagg < count ; nagg++) - agg[nagg]= then_expr[nagg * 2]; - - if (else_expr) - agg[nagg++]= *else_expr; - - if (aggregate_for_result(func_name(), agg, nagg, true)) + if (aggregate_for_result(func_name(), args + start, arg_count - start, true)) return true; - if (fix_attributes(agg, nagg)) + if (fix_attributes(args + start, arg_count - start)) return true; - /* - Copy all modified THEN and ELSE items back to then_expr[] array. - Some of the items might have been changed to Item_func_conv_charset. - */ - for (nagg= 0 ; nagg < count ; nagg++) - change_item_tree_if_needed(thd, &then_expr[nagg * 2], agg[nagg]); - - if (else_expr) - change_item_tree_if_needed(thd, else_expr, agg[nagg++]); return false; } @@ -3175,8 +3157,6 @@ bool Item_func_case::aggregate_then_and_else_arguments(THD *thd, bool Item_func_case_simple::aggregate_switch_and_when_arguments(THD *thd, bool nulls_eq) { - Item **agg= arg_buffer; - uint nagg; uint ncases= when_count(); m_found_types= 0; if (prepare_predicant_and_values(thd, &m_found_types, nulls_eq)) @@ -3190,17 +3170,7 @@ bool Item_func_case_simple::aggregate_switch_and_when_arguments(THD *thd, return true; } - /* - As the predicant 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. - */ - agg[0]= args[0]; // The predicant - for (nagg= 0; nagg < ncases ; nagg++) - agg[nagg+1]= args[nagg * 2 + 1]; - nagg++; - if (!(m_found_types= collect_cmp_types(agg, nagg))) + if (!(m_found_types= collect_cmp_types(args, ncases + 1))) return true; if (m_found_types & (1U << STRING_RESULT)) @@ -3229,17 +3199,8 @@ bool Item_func_case_simple::aggregate_switch_and_when_arguments(THD *thd, 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, ncases + 1)) return true; - /* - 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[0], agg[0]); - - for (nagg= 0; nagg < ncases; nagg++) - change_item_tree_if_needed(thd, &args[nagg * 2 + 1], agg[nagg + 1]); } if (make_unique_cmp_items(thd, cmp_collation.collation)) @@ -3256,78 +3217,57 @@ Item* Item_func_case_simple::propagate_equal_fields(THD *thd, const Type_handler *first_expr_cmp_handler; first_expr_cmp_handler= args[0]->type_handler_for_comparison(); - for (uint i= 0; i < arg_count; i++) + /* + 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 << first_expr_cmp_handler->cmp_type())) + propagate_and_change_item_tree(thd, &args[0], cond, + Context(ANY_SUBST, first_expr_cmp_handler, cmp_collation.collation)); + + /* + 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. + */ + uint i, count= when_count(); + for (i= 1; i <= count; i++) { - /* - 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 (i == 0) // 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 << first_expr_cmp_handler->cmp_type())) - new_item= args[i]->propagate_equal_fields(thd, - Context( - ANY_SUBST, - first_expr_cmp_handler, - cmp_collation.collation), - cond); - } - else if ((i % 2) == 1 && i != arg_count - 1) // 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. - */ - Type_handler_hybrid_field_type tmp(first_expr_cmp_handler); - if (!tmp.aggregate_for_comparison(args[i]->type_handler_for_comparison())) - new_item= args[i]->propagate_equal_fields(thd, - Context( - ANY_SUBST, - tmp.type_handler(), - 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); + Type_handler_hybrid_field_type tmp(first_expr_cmp_handler); + if (!tmp.aggregate_for_comparison(args[i]->type_handler_for_comparison())) + propagate_and_change_item_tree(thd, &args[i], cond, + Context(ANY_SUBST, tmp.type_handler(), cmp_collation.collation)); } + + // THEN and ELSE arguments (they are not in comparison) + for (; i < arg_count; i++) + propagate_and_change_item_tree(thd, &args[i], cond, Context_identity()); + return this; } @@ -3339,9 +3279,9 @@ void Item_func_case::print_when_then_arguments(String *str, for (uint i=0 ; i < count ; i++) { str->append(STRING_WITH_LEN("when ")); - items[i * 2]->print_parenthesised(str, query_type, precedence()); + items[i]->print_parenthesised(str, query_type, precedence()); str->append(STRING_WITH_LEN(" then ")); - items[i * 2 + 1]->print_parenthesised(str, query_type, precedence()); + items[i + count]->print_parenthesised(str, query_type, precedence()); str->append(' '); } } @@ -3381,6 +3321,28 @@ void Item_func_case_simple::print(String *str, enum_query_type query_type) } +void Item_func_decode_oracle::print(String *str, enum_query_type query_type) +{ + str->append(func_name()); + str->append('('); + args[0]->print(str, query_type); + for (uint i= 1, count= when_count() ; i <= count; i++) + { + str->append(','); + args[i]->print(str, query_type); + str->append(','); + args[i+count]->print(str, query_type); + } + Item **else_expr= Item_func_case_simple::else_expr_addr(); + if (else_expr) + { + str->append(','); + (*else_expr)->print(str, query_type); + } + str->append(')'); +} + + /** Coalesce - return first not NULL argument. */ @@ -4910,17 +4872,14 @@ Item *Item_cond::propagate_equal_fields(THD *thd, DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare()); DBUG_ASSERT(arg_count == 0); List_iterator<Item> li(list); - Item *item; - while ((item= li++)) + while (li++) { /* - The exact value of the second parameter to propagate_equal_fields() + The exact value of the last parameter to propagate_and_change_item_tree() is not important at this point. Item_func derivants will create and pass their own context to the arguments. */ - Item *new_item= item->propagate_equal_fields(thd, Context_boolean(), cond); - if (new_item && new_item != item) - thd->change_item_tree(li.ref(), new_item); + propagate_and_change_item_tree(thd, li.ref(), cond, Context_boolean()); } return this; } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 3d11a226d07..d10bac0fced 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -2104,15 +2104,13 @@ class Item_func_case :public Item_func_case_expression protected: String tmp_value; DTCollation cmp_collation; - Item **arg_buffer; - bool aggregate_then_and_else_arguments(THD *thd, - Item **items, uint count, - Item **else_expr); + bool aggregate_then_and_else_arguments(THD *thd, uint count); virtual Item **else_expr_addr() const= 0; virtual Item *find_item()= 0; void print_when_then_arguments(String *str, enum_query_type query_type, Item **items, uint count); void print_else_argument(String *str, enum_query_type query_type, Item *item); + void reorder_args(uint start); public: Item_func_case(THD *thd, List<Item> &list) :Item_func_case_expression(thd, list) @@ -2129,13 +2127,6 @@ public: enum precedence precedence() const { return BETWEEN_PRECEDENCE; } CHARSET_INFO *compare_collation() const { return cmp_collation.collation; } bool need_parentheses_in_default() { return true; } - Item *build_clone(THD *thd) - { - Item_func_case *clone= (Item_func_case *) Item_func::build_clone(thd); - if (clone) - clone->arg_buffer= 0; - return clone; - } }; @@ -2156,6 +2147,7 @@ public: :Item_func_case(thd, list) { DBUG_ASSERT(arg_count >= 2); + reorder_args(0); } void print(String *str, enum_query_type query_type); void fix_length_and_dec(); @@ -2200,6 +2192,7 @@ public: m_found_types(0) { DBUG_ASSERT(arg_count >= 3); + reorder_args(1); } void cleanup() { @@ -2233,8 +2226,7 @@ public: :Item_func_case_simple(thd, list) { } const char *func_name() const { return "decode_oracle"; } - void print(String *str, enum_query_type query_type) - { Item_func::print(str, query_type); } + void print(String *str, enum_query_type query_type); void fix_length_and_dec(); Item *find_item(); Item *get_copy(THD *thd) |