Commit c39a7446 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-13864 (final) Change Item_func_case to store the predicant in args[0]

parent e12390a3
......@@ -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;
......@@ -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";
......@@ -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;
This diff is collapsed.
......@@ -2016,79 +2016,129 @@ class Predicant_to_list_comparator
/*
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<Item> &list, Item *first_expr_arg,
Item *else_expr_arg);
Item_func_case(THD *thd, List<Item> &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; }
bool need_parentheses_in_default() { return true; }
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;
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<Item> &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<Item_func_case_searched>(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<Item> &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::cleanup");
DBUG_ENTER("Item_func_case_simple::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<Item_func_case>(thd, mem_root, this); }
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 *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;
}
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<Item_func_case_simple>(thd, mem_root, this); }
};
......
......@@ -1716,7 +1716,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <item>
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 <item_list>
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 <sp_cursor_stmt>
......@@ -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 <table reference> in the SQL:2003 standard. */
/* Warning - may return NULL in case of incomplete SELECT */
table_ref:
......
......@@ -1133,7 +1133,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <item>
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 <item_list>
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<Item> tmp;
List_iterator_fast<Item> 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
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment