diff --git a/mysql-test/r/case.result b/mysql-test/r/case.result index 3214f52e765..ee2db76e517 100644 --- a/mysql-test/r/case.result +++ b/mysql-test/r/case.result @@ -447,3 +447,51 @@ EXECUTE stmt; good was_bad_now_good one one DEALLOCATE PREPARE stmt; +# +# MDEV-13864 Change Item_func_case to store the predicant in args[0] +# +SET NAMES latin1; +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET latin1); +INSERT INTO t1 VALUES ('a'),('b'),('c'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE a WHEN 'a' THEN 'a' ELSE 'a' END='a'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN a THEN 'a' ELSE 'a' END='a'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN a ELSE 'a' END='a'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' and (case 'a' when 'a' then `test`.`t1`.`a` else 'a' end) = 'a' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN 'a' ELSE a END='a'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' and (case 'a' when 'a' then 'a' else `test`.`t1`.`a` end) = 'a' +ALTER TABLE t1 MODIFY a VARBINARY(10); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE a WHEN 'a' THEN 'a' ELSE 'a' END='a'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN a THEN 'a' ELSE 'a' END='a'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN a ELSE 'a' END='a'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN 'a' ELSE a END='a'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' +DROP TABLE t1; diff --git a/mysql-test/r/func_debug.result b/mysql-test/r/func_debug.result index f55bfca80f5..f1453344e0e 100644 --- a/mysql-test/r/func_debug.result +++ b/mysql-test/r/func_debug.result @@ -1629,8 +1629,8 @@ WHEN -9223372036854775808 THEN 'one' c NULL Warnings: -Note 1105 DBUG: [0] arg=0 handler=0 (bigint) -Note 1105 DBUG: [1] arg=2 handler=1 (decimal) +Note 1105 DBUG: [0] arg=1 handler=0 (bigint) +Note 1105 DBUG: [1] arg=3 handler=1 (decimal) DROP TABLE t1; # # MDEV-11555 CASE with a mixture of TIME and DATETIME returns a wrong result @@ -1648,10 +1648,10 @@ CASE TIME'10:20:30' good was_bad_now_good one one Warnings: -Note 1105 DBUG: [0] arg=0 handler=0 (time) -Note 1105 DBUG: [1] arg=2 handler=0 (time) -Note 1105 DBUG: [0] arg=0 handler=0 (time) -Note 1105 DBUG: [1] arg=2 handler=0 (time) -Note 1105 DBUG: [2] arg=4 handler=2 (datetime) +Note 1105 DBUG: [0] arg=1 handler=0 (time) +Note 1105 DBUG: [1] arg=3 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) SET SESSION debug_dbug="-d,Predicant_to_list_comparator"; SET SESSION debug_dbug="-d,Item_func_in"; diff --git a/mysql-test/t/case.test b/mysql-test/t/case.test index 87f12cd9e38..52db7588855 100644 --- a/mysql-test/t/case.test +++ b/mysql-test/t/case.test @@ -330,3 +330,31 @@ PREPARE stmt FROM "SELECT EXECUTE stmt; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +--echo # +--echo # MDEV-13864 Change Item_func_case to store the predicant in args[0] +--echo # + +SET NAMES latin1; +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET latin1); +INSERT INTO t1 VALUES ('a'),('b'),('c'); + +# should propagate the predicant and the WHEN arguments (they are in comparison and use ANY_SUBST) +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE a WHEN 'a' THEN 'a' ELSE 'a' END='a'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN a THEN 'a' ELSE 'a' END='a'; + +# should not propagate the THEN and the ELSE arguments (they are not in comparison and use IDENTITY_SUBST) +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN a ELSE 'a' END='a'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN 'a' ELSE a END='a'; + +ALTER TABLE t1 MODIFY a VARBINARY(10); + +# with VARBINARY it should propagate all arguments +# as IDENTITY_SUBST for VARBINARY allows substitution +# of even those arguments that are not in comparison +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE a WHEN 'a' THEN 'a' ELSE 'a' END='a'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN a THEN 'a' ELSE 'a' END='a'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN a ELSE 'a' END='a'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN 'a' ELSE a END='a'; + +DROP TABLE t1; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 7b9604b46a8..f3dafec0020 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2814,27 +2814,6 @@ Item_func_nullif::is_null() } -Item_func_case::Item_func_case(THD *thd, List &list, - Item *first_expr_arg, Item *else_expr_arg): - Item_func_case_expression(thd), - Predicant_to_list_comparator(thd, list.elements/*QQ*/), - first_expr_num(-1), else_expr_num(-1), - m_found_types(0) -{ - ncases= list.elements; - if (first_expr_arg) - { - first_expr_num= list.elements; - list.push_back(first_expr_arg, thd->mem_root); - } - if (else_expr_arg) - { - else_expr_num= list.elements; - list.push_back(else_expr_arg, thd->mem_root); - } - set_arguments(thd, list); -} - /** Find and return matching items for CASE or ELSE item if all compares are failed or NULL if ELSE item isn't defined. @@ -2856,26 +2835,27 @@ Item_func_case::Item_func_case(THD *thd, List &list, failed */ - -Item *Item_func_case::find_item_searched() +Item *Item_func_case_searched::find_item() { - uint count= arg_count / 2; + uint count= when_count(); for (uint i= 0 ; i < count ; i++) { if (args[2 * i]->val_bool()) return args[2 * i + 1]; } - return else_expr_num != -1 ? args[else_expr_num] : 0; + Item **pos= Item_func_case_searched::else_expr_addr(); + return pos ? pos[0] : 0; } -Item *Item_func_case::find_item_simple() +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 else_expr_num != -1 ? args[else_expr_num] : 0; + Item **pos= Item_func_case_simple::else_expr_addr(); + return pos ? pos[0] : 0; } @@ -2966,12 +2946,13 @@ 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)))) + if (!(arg_buffer= (Item**) thd->alloc(sizeof(Item*)*(arg_count)))) return TRUE; bool res= Item_func::fix_fields(thd, ref); - if (else_expr_num == -1 || args[else_expr_num]->maybe_null) + Item **pos= else_expr_addr(); + if (!pos || pos[0]->maybe_null) maybe_null= 1; /* @@ -3006,15 +2987,17 @@ static void change_item_tree_if_needed(THD *thd, } -bool Item_func_case::prepare_predicant_and_values(THD *thd, uint *found_types) +bool Item_func_case_simple::prepare_predicant_and_values(THD *thd, + uint *found_types) { bool have_null= false; uint type_cnt; Type_handler_hybrid_field_type tmp; - add_predicant(this, (uint) first_expr_num); - for (uint i= 0 ; i < ncases / 2; i++) + uint ncases= when_count(); + add_predicant(this, 0); + for (uint i= 0 ; i < ncases; i++) { - if (add_value_skip_null("case..when", this, i * 2, &have_null)) + if (add_value_skip_null("case..when", this, i * 2 + 1, &have_null)) return true; } all_values_added(&tmp, &type_cnt, &m_found_types); @@ -3025,12 +3008,19 @@ bool Item_func_case::prepare_predicant_and_values(THD *thd, uint *found_types) } -void Item_func_case::fix_length_and_dec() +void Item_func_case_searched::fix_length_and_dec() { THD *thd= current_thd; - m_found_types= 0; - if (!aggregate_then_and_else_arguments(thd) && - first_expr_num != -1) + Item **else_ptr= Item_func_case_searched::else_expr_addr(); + aggregate_then_and_else_arguments(thd, &args[1], when_count(), else_ptr); +} + + +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)) aggregate_switch_and_when_arguments(thd); } @@ -3039,20 +3029,25 @@ void Item_func_case::fix_length_and_dec() Aggregate all THEN and ELSE expression types and collations when string result - @param THD - current thd - @param offs - the offset of the leftmost THEN argument - @paran count - the number or THEN..ELSE pairs + @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) */ -bool Item_func_case::aggregate_then_and_else_arguments(THD *thd) +bool Item_func_case::aggregate_then_and_else_arguments(THD *thd, + Item **then_expr, + uint count, + Item **else_expr) { Item **agg= arg_buffer; uint nagg; - for (nagg= 0 ; nagg < ncases/2 ; nagg++) - agg[nagg]= args[nagg*2+1]; + for (nagg= 0 ; nagg < count ; nagg++) + agg[nagg]= then_expr[nagg * 2]; - if (else_expr_num != -1) - agg[nagg++]= args[else_expr_num]; + if (else_expr) + agg[nagg++]= *else_expr; if (aggregate_for_result(func_name(), agg, nagg, true)) return true; @@ -3061,14 +3056,14 @@ bool Item_func_case::aggregate_then_and_else_arguments(THD *thd) return true; /* - Copy all modified THEN and ELSE items back to args[] array. + 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 < ncases / 2 ; nagg++) - change_item_tree_if_needed(thd, &args[nagg * 2 + 1], agg[nagg]); + for (nagg= 0 ; nagg < count ; nagg++) + change_item_tree_if_needed(thd, &then_expr[nagg * 2], agg[nagg]); - if (else_expr_num != -1) - change_item_tree_if_needed(thd, &args[else_expr_num], agg[nagg++]); + if (else_expr) + change_item_tree_if_needed(thd, else_expr, agg[nagg++]); return false; } @@ -3077,10 +3072,12 @@ bool Item_func_case::aggregate_then_and_else_arguments(THD *thd) Aggregate the predicant expression and all WHEN expression types and collations when string comparison */ -bool Item_func_case::aggregate_switch_and_when_arguments(THD *thd) +bool Item_func_case_simple::aggregate_switch_and_when_arguments(THD *thd) { Item **agg= arg_buffer; uint nagg; + uint ncases= when_count(); + m_found_types= 0; if (prepare_predicant_and_values(thd, &m_found_types)) { /* @@ -3098,9 +3095,9 @@ bool Item_func_case::aggregate_switch_and_when_arguments(THD *thd) extract the first expression and all WHEN expressions into a temporary array, to process them easier. */ - agg[0]= args[first_expr_num]; // The predicant - for (nagg= 0; nagg < ncases/2 ; nagg++) - agg[nagg+1]= args[nagg*2]; + 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))) return true; @@ -3138,10 +3135,10 @@ bool Item_func_case::aggregate_switch_and_when_arguments(THD *thd) 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]); + change_item_tree_if_needed(thd, &args[0], agg[0]); - for (nagg= 0; nagg < ncases / 2; nagg++) - change_item_tree_if_needed(thd, &args[nagg * 2], agg[nagg + 1]); + 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)) @@ -3151,17 +3148,13 @@ bool Item_func_case::aggregate_switch_and_when_arguments(THD *thd) } -Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) +Item* Item_func_case_simple::propagate_equal_fields(THD *thd, + const Context &ctx, + COND_EQUAL *cond) { const Type_handler *first_expr_cmp_handler; - if (first_expr_num == -1) - { - // None of the arguments are in a comparison context - Item_args::propagate_equal_fields(thd, Context_identity(), cond); - return this; - } - first_expr_cmp_handler= args[first_expr_num]->type_handler_for_comparison(); + first_expr_cmp_handler= args[0]->type_handler_for_comparison(); for (uint i= 0; i < arg_count; i++) { /* @@ -3171,7 +3164,7 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_ CASE ... THEN y1 ... THEN y2 ... THEN y3 ... ELSE y4 END */ Item *new_item= 0; - if ((int) i == first_expr_num) // Then CASE (the switch) argument + if (i == 0) // Then CASE (the switch) argument { /* Cannot replace the CASE (the switch) argument if @@ -3208,7 +3201,7 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_ cmp_collation.collation), cond); } - else if ((i % 2) == 0) // WHEN arguments + else if ((i % 2) == 1 && i != arg_count - 1) // WHEN arguments { /* These arguments are in comparison. @@ -3262,17 +3255,26 @@ void Item_func_case::print_else_argument(String *str, } -void Item_func_case::print(String *str, enum_query_type query_type) +void Item_func_case_searched::print(String *str, enum_query_type query_type) { + Item **pos; str->append(STRING_WITH_LEN("case ")); - if (first_expr_num != -1) - { - args[first_expr_num]->print_parenthesised(str, query_type, precedence()); - str->append(' '); - } - print_when_then_arguments(str, query_type, &args[0], ncases / 2); - if (else_expr_num != -1) - print_else_argument(str, query_type, args[else_expr_num]); + print_when_then_arguments(str, query_type, &args[0], when_count()); + if ((pos= Item_func_case_searched::else_expr_addr())) + print_else_argument(str, query_type, pos[0]); + str->append(STRING_WITH_LEN("end")); +} + + +void Item_func_case_simple::print(String *str, enum_query_type query_type) +{ + Item **pos; + str->append(STRING_WITH_LEN("case ")); + args[0]->print_parenthesised(str, query_type, precedence()); + str->append(' '); + print_when_then_arguments(str, query_type, &args[1], when_count()); + if ((pos= Item_func_case_simple::else_expr_addr())) + print_else_argument(str, query_type, pos[0]); str->append(STRING_WITH_LEN("end")); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 147f23720ec..ac992ee03cc 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -2016,79 +2016,129 @@ public: /* The class Item_func_case is the CASE ... WHEN ... THEN ... END function implementation. - - When there is no expression between CASE and the first WHEN - (the CASE expression) then this function simple checks all WHEN expressions - one after another. When some WHEN expression evaluated to TRUE then the - value of the corresponding THEN expression is returned. - - When the CASE expression is specified then it is compared to each WHEN - expression individually. When an equal WHEN expression is found - corresponding THEN expression is returned. - In order to do correct comparisons several comparators are used. One for - each result type. Different result types that are used in particular - CASE ... END expression are collected in the fix_length_and_dec() member - function and only comparators for there result types are used. */ -class Item_func_case :public Item_func_case_expression, - public Predicant_to_list_comparator +class Item_func_case :public Item_func_case_expression { - int first_expr_num, else_expr_num; +protected: String tmp_value; - uint ncases; DTCollation cmp_collation; Item **arg_buffer; - uint m_found_types; - bool prepare_predicant_and_values(THD *thd, uint *found_types); - bool aggregate_then_and_else_arguments(THD *thd); - bool aggregate_switch_and_when_arguments(THD *thd); - Item *find_item_searched(); - Item *find_item_simple(); - Item *find_item() - { - return first_expr_num == -1 ? find_item_searched() : find_item_simple(); - } + bool aggregate_then_and_else_arguments(THD *thd, + Item **items, uint count, + Item **else_expr); + 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); public: - Item_func_case(THD *thd, List &list, Item *first_expr_arg, - Item *else_expr_arg); + Item_func_case(THD *thd, List &list) + :Item_func_case_expression(thd, list) + { } double real_op(); longlong int_op(); String *str_op(String *); my_decimal *decimal_op(my_decimal *); bool date_op(MYSQL_TIME *ltime, uint fuzzydate); bool fix_fields(THD *thd, Item **ref); - void fix_length_and_dec(); table_map not_null_tables() const { return 0; } const char *func_name() const { return "case"; } enum precedence precedence() const { return BETWEEN_PRECEDENCE; } - virtual void print(String *str, enum_query_type query_type); CHARSET_INFO *compare_collation() const { return cmp_collation.collation; } - void cleanup() - { - DBUG_ENTER("Item_func_case::cleanup"); - Item_func::cleanup(); - Predicant_to_list_comparator::cleanup(); - DBUG_VOID_RETURN; - } - Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond); bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy(thd, mem_root, this); } Item *build_clone(THD *thd, MEM_ROOT *mem_root) { Item_func_case *clone= (Item_func_case *) Item_func::build_clone(thd, mem_root); if (clone) - { clone->arg_buffer= 0; - if (clone->Predicant_to_list_comparator::init_clone(thd, ncases)) - return NULL; - } + return clone; + } +}; + + +/* + CASE WHEN cond THEN res [WHEN cond THEN res...] [ELSE res] END + + Searched CASE checks all WHEN expressions one after another. + When some WHEN expression evaluated to TRUE then the + value of the corresponding THEN expression is returned. +*/ +class Item_func_case_searched: public Item_func_case +{ + uint when_count() const { return arg_count / 2; } + bool with_else() const { return arg_count % 2; } + Item **else_expr_addr() const { return with_else() ? &args[arg_count - 1] : 0; } +public: + Item_func_case_searched(THD *thd, List &list) + :Item_func_case(thd, list) + { + DBUG_ASSERT(arg_count >= 2); + } + void print(String *str, enum_query_type query_type); + void fix_length_and_dec(); + Item *propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) + { + // None of the arguments are in a comparison context + Item_args::propagate_equal_fields(thd, Context_identity(), cond); + return this; + } + Item *find_item(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + + +/* + CASE pred WHEN value THEN res [WHEN value THEN res...] [ELSE res] END + + When the predicant expression is specified then it is compared to each WHEN + expression individually. When an equal WHEN expression is found + the corresponding THEN expression is returned. + In order to do correct comparisons several comparators are used. One for + each result type. Different result types that are used in particular + CASE ... END expression are collected in the fix_length_and_dec() member + function and only comparators for there result types are used. +*/ +class Item_func_case_simple: public Item_func_case, + public Predicant_to_list_comparator +{ + uint m_found_types; + uint when_count() const { return (arg_count - 1) / 2; } + bool with_else() const { return arg_count % 2 == 0; } + Item **else_expr_addr() const { return with_else() ? &args[arg_count - 1] : 0; } + bool aggregate_switch_and_when_arguments(THD *thd); + bool prepare_predicant_and_values(THD *thd, uint *found_types); +public: + Item_func_case_simple(THD *thd, List &list) + :Item_func_case(thd, list), + Predicant_to_list_comparator(thd, arg_count), + m_found_types(0) + { + DBUG_ASSERT(arg_count >= 3); + } + void cleanup() + { + DBUG_ENTER("Item_func_case_simple::cleanup"); + Item_func::cleanup(); + Predicant_to_list_comparator::cleanup(); + DBUG_VOID_RETURN; + } + void print(String *str, enum_query_type query_type); + void fix_length_and_dec(); + Item *propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond); + Item *find_item(); + Item *build_clone(THD *thd, MEM_ROOT *mem_root) + { + Item_func_case_simple *clone= (Item_func_case_simple *) + Item_func_case::build_clone(thd, mem_root); + uint ncases= when_count(); + if (clone && clone->Predicant_to_list_comparator::init_clone(thd, ncases)) + return NULL; return clone; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } }; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c47e881a82c..7e89ad91a17 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1716,7 +1716,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type literal text_literal insert_ident order_ident temporal_literal - simple_ident expr opt_expr opt_else sum_expr in_sum_expr + simple_ident expr sum_expr in_sum_expr variable variable_aux bool_pri predicate bit_expr parenthesized_expr table_wild simple_expr column_default_non_parenthesized_expr udf_expr @@ -1744,7 +1744,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); NUM_literal %type - expr_list opt_udf_expr_list udf_expr_list when_list + expr_list opt_udf_expr_list udf_expr_list when_list when_list_opt_else ident_list ident_list_arg opt_expr_list %type @@ -9419,10 +9419,15 @@ column_default_non_parenthesized_expr: if (!($$= $5.create_typecast_item(thd, $3, Lex->charset))) MYSQL_YYABORT; } - | CASE_SYM opt_expr when_list opt_else END + | CASE_SYM when_list_opt_else END { - $$= new (thd->mem_root) Item_func_case(thd, *$3, $2, $4); - if ($$ == NULL) + if (!($$= new(thd->mem_root) Item_func_case_searched(thd, *$2))) + MYSQL_YYABORT; + } + | CASE_SYM expr when_list_opt_else END + { + $3->push_front($2, thd->mem_root); + if (!($$= new (thd->mem_root) Item_func_case_simple(thd, *$3))) MYSQL_YYABORT; } | CONVERT_SYM '(' expr ',' cast_type ')' @@ -10844,16 +10849,6 @@ ident_list: } ; -opt_expr: - /* empty */ { $$= NULL; } - | expr { $$= $1; } - ; - -opt_else: - /* empty */ { $$= NULL; } - | ELSE expr { $$= $2; } - ; - when_list: WHEN_SYM expr THEN_SYM expr { @@ -10871,6 +10866,15 @@ when_list: } ; +when_list_opt_else: + when_list + | when_list ELSE expr + { + $1->push_back($3, thd->mem_root); + $$= $1; + } + ; + /* Equivalent to in the SQL:2003 standard. */ /* Warning - may return NULL in case of incomplete SELECT */ table_ref: diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 90d509dc775..a475914d369 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -1133,7 +1133,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type literal text_literal insert_ident order_ident temporal_literal - simple_ident expr opt_expr opt_else sum_expr in_sum_expr + simple_ident expr sum_expr in_sum_expr variable variable_aux bool_pri predicate bit_expr parenthesized_expr table_wild simple_expr column_default_non_parenthesized_expr udf_expr @@ -1163,7 +1163,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); NUM_literal %type - expr_list opt_udf_expr_list udf_expr_list when_list + expr_list opt_udf_expr_list udf_expr_list when_list when_list_opt_else ident_list ident_list_arg opt_expr_list decode_when_list @@ -9440,10 +9440,15 @@ column_default_non_parenthesized_expr: if (!($$= $5.create_typecast_item(thd, $3, Lex->charset))) MYSQL_YYABORT; } - | CASE_SYM opt_expr when_list opt_else END + | CASE_SYM when_list_opt_else END { - $$= new (thd->mem_root) Item_func_case(thd, *$3, $2, $4); - if ($$ == NULL) + if (!($$= new(thd->mem_root) Item_func_case_searched(thd, *$2))) + MYSQL_YYABORT; + } + | CASE_SYM expr when_list_opt_else END + { + $3->push_front($2, thd->mem_root); + if (!($$= new (thd->mem_root) Item_func_case_simple(thd, *$3))) MYSQL_YYABORT; } | CONVERT_SYM '(' expr ',' cast_type ')' @@ -9459,32 +9464,8 @@ column_default_non_parenthesized_expr: } | DECODE_SYM '(' expr ',' decode_when_list ')' { - if (($5->elements % 2) == 0) - { - // No default expression - $$= new (thd->mem_root) Item_func_case(thd, *$5, $3, NULL); - } - else - { - /* - There is a default expression at the end of the list $5. - Create a new list without the default expression. - */ - List tmp; - List_iterator_fast it(*$5); - for (uint i= 0; i < $5->elements - 1; i++) // copy all but last - { - Item *item= it++; - tmp.push_back(item); - } - /* - Now the new list "tmp" contains only WHEN-THEN pairs, - The default expression is pointed by the iterator "it" - and will be returned by the next call for it++ below. - */ - $$= new (thd->mem_root) Item_func_case(thd, tmp, $3, it++); - } - if ($$ == NULL) + $5->push_front($3, thd->mem_root); + if (!($$= new (thd->mem_root) Item_func_case_simple(thd, *$5))) MYSQL_YYABORT; } | DEFAULT '(' simple_ident ')' @@ -10907,16 +10888,6 @@ ident_list: } ; -opt_expr: - /* empty */ { $$= NULL; } - | expr { $$= $1; } - ; - -opt_else: - /* empty */ { $$= NULL; } - | ELSE expr { $$= $2; } - ; - when_list: WHEN_SYM expr THEN_SYM expr { @@ -10935,6 +10906,15 @@ when_list: ; +when_list_opt_else: + when_list + | when_list ELSE expr + { + $1->push_back($3, thd->mem_root); + $$= $1; + } + ; + decode_when_list: expr ',' expr {