@ -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 )