diff options
Diffstat (limited to 'sql/item_cmpfunc.cc')
-rw-r--r-- | sql/item_cmpfunc.cc | 243 |
1 files changed, 110 insertions, 133 deletions
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 44dc8ff9e09..579cdc10057 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2246,50 +2246,6 @@ void Item_func_between::print(String *str, enum_query_type query_type) } -void -Item_func_case_abbreviation2::fix_length_and_dec2(Item **args) -{ - uint32 char_length; - set_handler_by_field_type(agg_field_type(args, 2, true)); - maybe_null=args[0]->maybe_null || args[1]->maybe_null; - decimals= MY_MAX(args[0]->decimals, args[1]->decimals); - unsigned_flag= args[0]->unsigned_flag && args[1]->unsigned_flag; - - if (Item_func_case_abbreviation2::result_type() == DECIMAL_RESULT || - Item_func_case_abbreviation2::result_type() == INT_RESULT) - { - int len0= args[0]->max_char_length() - args[0]->decimals - - (args[0]->unsigned_flag ? 0 : 1); - - int len1= args[1]->max_char_length() - args[1]->decimals - - (args[1]->unsigned_flag ? 0 : 1); - - char_length= MY_MAX(len0, len1) + decimals + (unsigned_flag ? 0 : 1); - } - else - char_length= MY_MAX(args[0]->max_char_length(), args[1]->max_char_length()); - - switch (Item_func_case_abbreviation2::result_type()) { - case STRING_RESULT: - if (count_string_result_length(Item_func_case_abbreviation2::field_type(), - args, 2)) - return; - break; - case DECIMAL_RESULT: - case REAL_RESULT: - break; - case INT_RESULT: - decimals= 0; - break; - case ROW_RESULT: - case TIME_RESULT: - DBUG_ASSERT(0); - } - fix_char_length(char_length); -} - - - uint Item_func_case_abbreviation2::decimal_precision2(Item **args) const { int arg0_int_part= args[0]->decimal_int_part(); @@ -2561,8 +2517,15 @@ void Item_func_nullif::update_used_tables() void Item_func_nullif::fix_length_and_dec() { - if (!args[2]) // Only false if EOM - return; + /* + If this is the first invocation of fix_length_and_dec(), create the + third argument as a copy of the first. This cannot be done before + fix_fields(), because fix_fields() might replace items, + for exampe NOT x --> x==0, or (SELECT 1) --> 1. + See also class Item_func_nullif declaration. + */ + if (arg_count == 2) + args[arg_count++]= args[0]; THD *thd= current_thd; /* @@ -2579,7 +2542,8 @@ Item_func_nullif::fix_length_and_dec() args[0] and args[2] should still point to the same original l_expr. */ DBUG_ASSERT(args[0] == args[2] || thd->stmt_arena->is_stmt_execute()); - if (args[0]->type() == SUM_FUNC_ITEM && !thd->lex->context_analysis_only) + if (args[0]->type() == SUM_FUNC_ITEM && + !thd->lex->is_ps_or_view_context_analysis()) { /* NULLIF(l_expr, r_expr) @@ -2700,7 +2664,7 @@ Item_func_nullif::fix_length_and_dec() m_cache= args[0]->cmp_type() == STRING_RESULT ? new (thd->mem_root) Item_cache_str_for_nullif(thd, args[0]) : Item_cache::get_cache(thd, args[0]); - m_cache->setup(current_thd, args[0]); + m_cache->setup(thd, args[0]); m_cache->store(args[0]); m_cache->set_used_tables(args[0]->used_tables()); thd->change_item_tree(&args[0], m_cache); @@ -2712,7 +2676,7 @@ Item_func_nullif::fix_length_and_dec() unsigned_flag= args[2]->unsigned_flag; fix_char_length(args[2]->max_char_length()); maybe_null=1; - setup_args_and_comparator(current_thd, &cmp); + setup_args_and_comparator(thd, &cmp); } @@ -2731,10 +2695,10 @@ void Item_func_nullif::print(String *str, enum_query_type query_type) Therefore, after equal field propagation args[0] and args[2] can point to different items. */ - if (!(query_type & QT_ITEM_FUNC_NULLIF_TO_CASE) || args[0] == args[2]) + if ((query_type & QT_ITEM_ORIGINAL_FUNC_NULLIF) || args[0] == args[2]) { /* - If no QT_ITEM_FUNC_NULLIF_TO_CASE is requested, + If QT_ITEM_ORIGINAL_FUNC_NULLIF is requested, that means we want the original NULLIF() representation, e.g. when we are in: SHOW CREATE {VIEW|FUNCTION|PROCEDURE} @@ -2742,15 +2706,12 @@ void Item_func_nullif::print(String *str, enum_query_type query_type) The original representation is possible only if args[0] and args[2] still point to the same Item. - The caller must pass call print() with QT_ITEM_FUNC_NULLIF_TO_CASE + The caller must never pass call print() with QT_ITEM_ORIGINAL_FUNC_NULLIF if an expression has undergone some optimization (e.g. equal field propagation done in optimize_cond()) already and NULLIF() potentially has two different representations of "a": - one "a" for comparison - another "a" for the returned value! - - Note, the EXPLAIN EXTENDED and EXPLAIN FORMAT=JSON routines - do pass QT_ITEM_FUNC_NULLIF_TO_CASE to print(). */ DBUG_ASSERT(args[0] == args[2] || current_thd->lex->context_analysis_only); str->append(func_name()); @@ -2948,7 +2909,7 @@ Item *Item_func_case::find_item(String *str) 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]) && !args[i]->null_value) + if (cmp_items[(uint)cmp_type]->cmp(args[i]) == FALSE) return args[i + 1]; } } @@ -3066,24 +3027,6 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref) } -void Item_func_case::agg_str_lengths(Item* arg) -{ - fix_char_length(MY_MAX(max_char_length(), arg->max_char_length())); - set_if_bigger(decimals, arg->decimals); - unsigned_flag= unsigned_flag && arg->unsigned_flag; -} - - -void Item_func_case::agg_num_lengths(Item *arg) -{ - uint len= my_decimal_length_to_precision(arg->max_length, arg->decimals, - arg->unsigned_flag) - arg->decimals; - set_if_bigger(max_length, len); - set_if_bigger(decimals, arg->decimals); - unsigned_flag= unsigned_flag && arg->unsigned_flag; -} - - /** Check if (*place) and new_value points to different Items and call THD::change_item_tree() if needed. @@ -3145,18 +3088,7 @@ void Item_func_case::fix_length_and_dec() } else { - collation.set_numeric(); - max_length=0; - decimals=0; - unsigned_flag= TRUE; - for (uint i= 0; i < ncases; i+= 2) - agg_num_lengths(args[i + 1]); - if (else_expr_num != -1) - agg_num_lengths(args[else_expr_num]); - max_length= my_decimal_precision_to_length_no_truncation(max_length + - decimals, - decimals, - unsigned_flag); + fix_attributes(agg, nagg); } /* @@ -3458,23 +3390,25 @@ my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value) } -void Item_func_coalesce::fix_length_and_dec() +void Item_hybrid_func::fix_attributes(Item **items, uint nitems) { - set_handler_by_field_type(agg_field_type(args, arg_count, true)); - switch (Item_func_coalesce::result_type()) { + switch (Item_hybrid_func::result_type()) { case STRING_RESULT: - if (count_string_result_length(Item_func_coalesce::field_type(), - args, arg_count)) + if (count_string_result_length(Item_hybrid_func::field_type(), + items, nitems)) return; break; case DECIMAL_RESULT: - count_decimal_length(); + collation.set_numeric(); + count_decimal_length(items, nitems); break; case REAL_RESULT: - count_real_length(); + collation.set_numeric(); + count_real_length(items, nitems); break; case INT_RESULT: - count_only_length(args, arg_count); + collation.set_numeric(); + count_only_length(items, nitems); decimals= 0; break; case ROW_RESULT: @@ -3606,11 +3540,11 @@ static int cmp_decimal(void *cmp_arg, my_decimal *a, my_decimal *b) } -int in_vector::find(Item *item) +bool in_vector::find(Item *item) { uchar *result=get_value(item); if (!result || !used_count) - return 0; // Null value + return false; // Null value uint start,end; start=0; end=used_count-1; @@ -3619,13 +3553,13 @@ int in_vector::find(Item *item) uint mid=(start+end+1)/2; int res; if ((res=(*compare)(collation, base+mid*size, result)) == 0) - return 1; + return true; if (res < 0) start=mid; else end=mid-1; } - return (int) ((*compare)(collation, base+start*size, result) == 0); + return ((*compare)(collation, base+start*size, result) == 0); } in_string::in_string(THD *thd, uint elements, qsort2_cmp cmp_func, @@ -3957,14 +3891,20 @@ int cmp_item_row::cmp(Item *arg) arg->bring_value(); for (uint i=0; i < n; i++) { - if (comparators[i]->cmp(arg->element_index(i))) + const int rc= comparators[i]->cmp(arg->element_index(i)); + switch (rc) { - if (!arg->element_index(i)->null_value) - return 1; - was_null= 1; + case UNKNOWN: + was_null= true; + break; + case TRUE: + return TRUE; + case FALSE: + break; // elements #i are equal } + arg->null_value|= arg->element_index(i)->null_value; } - return (arg->null_value= was_null); + return was_null ? UNKNOWN : FALSE; } @@ -3987,15 +3927,15 @@ void cmp_item_decimal::store_value(Item *item) /* val may be zero if item is nnull */ if (val && val != &value) my_decimal2decimal(val, &value); + m_null_value= item->null_value; } int cmp_item_decimal::cmp(Item *arg) { my_decimal tmp_buf, *tmp= arg->val_decimal(&tmp_buf); - if (arg->null_value) - return 1; - return my_decimal_cmp(&value, tmp); + return (m_null_value || arg->null_value) ? + UNKNOWN : (my_decimal_cmp(&value, tmp) != 0); } @@ -4019,12 +3959,14 @@ void cmp_item_datetime::store_value(Item *item) enum_field_types f_type= tmp_item[0]->field_type_for_temporal_comparison(warn_item); value= get_datetime_value(thd, &tmp_item, &lval_cache, f_type, &is_null); + m_null_value= item->null_value; } int cmp_item_datetime::cmp(Item *arg) { - return value != arg->val_temporal_packed(warn_item); + const bool rc= value != arg->val_temporal_packed(warn_item); + return (m_null_value || arg->null_value) ? UNKNOWN : rc; } @@ -4048,10 +3990,10 @@ bool Item_func_in::count_sargable_conds(uchar *arg) } -bool Item_func_in::nulls_in_row() +bool Item_func_in::list_contains_null() { Item **arg,**arg_end; - for (arg= args+1, arg_end= args+arg_count; arg != arg_end ; arg++) + for (arg= args + 1, arg_end= args+arg_count; arg != arg_end ; arg++) { if ((*arg)->null_inside()) return 1; @@ -4132,7 +4074,7 @@ static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y) { return cs->coll->strnncollsp(cs, (uchar *) x->ptr(),x->length(), - (uchar *) y->ptr(),y->length(), 0); + (uchar *) y->ptr(),y->length()); } void Item_func_in::fix_length_and_dec() @@ -4166,6 +4108,32 @@ void Item_func_in::fix_length_and_dec() } } + /* + First conditions for bisection to be possible: + 1. All types are similar, and + 2. All expressions in <in value list> are const + */ + bool bisection_possible= + type_cnt == 1 && // 1 + const_itm; // 2 + if (bisection_possible) + { + /* + In the presence of NULLs, the correct result of evaluating this item + must be UNKNOWN or FALSE. To achieve that: + - If type is scalar, we can use bisection and the "have_null" boolean. + - If type is ROW, we will need to scan all of <in value list> when + searching, so bisection is impossible. Unless: + 3. UNKNOWN and FALSE are equivalent results + 4. Neither left expression nor <in value list> contain any NULL value + */ + + if (m_compare_type == ROW_RESULT && + ((!is_top_level_item() || negated) && // 3 + (list_contains_null() || args[0]->maybe_null))) // 4 + bisection_possible= false; + } + if (type_cnt == 1) { if (m_compare_type == STRING_RESULT && @@ -4178,7 +4146,7 @@ void Item_func_in::fix_length_and_dec() uint cols= args[0]->cols(); cmp_item_row *cmp= 0; - if (const_itm && !nulls_in_row()) + if (bisection_possible) { array= new (thd->mem_root) in_row(thd, arg_count-1, 0); cmp= &((in_row*)array)->tmp; @@ -4207,11 +4175,8 @@ void Item_func_in::fix_length_and_dec() } } } - /* - Row item with NULLs inside can return NULL or FALSE => - they can't be processed as static - */ - if (type_cnt == 1 && const_itm && !nulls_in_row()) + + if (bisection_possible) { /* IN must compare INT columns and constants as int values (the same @@ -4267,20 +4232,25 @@ void Item_func_in::fix_length_and_dec() array= new (thd->mem_root) in_datetime(thd, date_arg, arg_count - 1); break; } - if (array && !(thd->is_fatal_error)) // If not EOM + if (!array || thd->is_fatal_error) // OOM + return; + uint j=0; + for (uint i=1 ; i < arg_count ; i++) { - uint j=0; - for (uint i=1 ; i < arg_count ; i++) + array->set(j,args[i]); + if (!args[i]->null_value) + j++; // include this cell in the array. + else { - array->set(j,args[i]); - if (!args[i]->null_value) // Skip NULL values - j++; - else - have_null= 1; + /* + We don't put NULL values in array, to avoid erronous matches in + bisection. + */ + have_null= 1; } - if ((array->used_count= j)) - array->sort(); } + if ((array->used_count= j)) + array->sort(); } else { @@ -4348,7 +4318,14 @@ longlong Item_func_in::val_int() uint value_added_map= 0; if (array) { - int tmp=array->find(args[0]); + bool tmp=array->find(args[0]); + /* + NULL on left -> UNKNOWN. + Found no match, and NULL on right -> UNKNOWN. + NULL on right can never give a match, as it is not stored in + array. + See also the 'bisection_possible' variable in fix_length_and_dec(). + */ null_value=args[0]->null_value || (!tmp && have_null); return (longlong) (!null_value && tmp != negated); } @@ -4370,13 +4347,12 @@ longlong Item_func_in::val_int() if (!(value_added_map & (1U << (uint)cmp_type))) { in_item->store_value(args[0]); - if ((null_value= args[0]->null_value)) - return 0; value_added_map|= 1U << (uint)cmp_type; } - if (!in_item->cmp(args[i]) && !args[i]->null_value) + const int rc= in_item->cmp(args[i]); + if (rc == FALSE) return (longlong) (!negated); - have_null|= args[i]->null_value; + have_null|= (rc == UNKNOWN); } null_value= have_null; @@ -5786,7 +5762,7 @@ bool Item_func_not::fix_fields(THD *thd, Item **ref) args[0]->under_not(this); if (args[0]->type() == FIELD_ITEM) { - /* replace "NOT <field>" with "<filed> == 0" */ + /* replace "NOT <field>" with "<field> == 0" */ Query_arena backup, *arena; Item *new_item; bool rc= TRUE; @@ -6498,7 +6474,8 @@ longlong Item_equal::val_int() /* Skip fields of tables that has not been read yet */ if (!field->table->status || (field->table->status & STATUS_NULL_ROW)) { - if (eval_item->cmp(item) || (null_value= item->null_value)) + const int rc= eval_item->cmp(item); + if ((rc == TRUE) || (null_value= (rc == UNKNOWN))) return 0; } } |