diff options
Diffstat (limited to 'sql/item_cmpfunc.cc')
-rw-r--r-- | sql/item_cmpfunc.cc | 1630 |
1 files changed, 999 insertions, 631 deletions
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index d03c68b3560..5415d6f4f8a 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -14,7 +14,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* This file defines all compare functions */ +/** + @file + + @brief + This file defines all compare functions +*/ #ifdef USE_PRAGMA_IMPLEMENTATION #pragma implementation // gcc: Class implementation @@ -24,8 +29,10 @@ #include <m_ctype.h> #include "sql_select.h" -static bool convert_constant_item(THD *thd, Item_field *field_item, - Item **item); +static bool convert_constant_item(THD *, Item_field *, Item **); +static longlong +get_year_value(THD *thd, Item ***item_arg, Item **cache_arg, + Item *warn_item, bool *is_null); static Item_result item_store_type(Item_result a, Item *item, my_bool unsigned_flag) @@ -105,12 +112,11 @@ static int cmp_row_type(Item* item1, Item* item2) } -/* +/** Aggregates result types from the array of items. SYNOPSIS: agg_cmp_type() - thd thread handle type [out] the aggregated type items array of items to aggregate the type from nitems number of items in the array @@ -119,15 +125,17 @@ static int cmp_row_type(Item* item1, Item* item2) This function aggregates result types from the array of items. Found type supposed to be used later for comparison of values of these items. Aggregation itself is performed by the item_cmp_type() function. - The function also checks compatibility of row signatures for the - submitted items (see the spec for the cmp_row_type function). + @param[out] type the aggregated type + @param items array of items to aggregate the type from + @param nitems number of items in the array - RETURN VALUES + @retval 1 type incompatibility has been detected + @retval 0 otherwise */ -static int agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) +static int agg_cmp_type(Item_result *type, Item **items, uint nitems) { uint i; type[0]= items[0]->result_type(); @@ -177,6 +185,50 @@ enum_field_types agg_field_type(Item **items, uint nitems) return res; } +/* + Collects different types for comparison of first item with each other items + + SYNOPSIS + collect_cmp_types() + items Array of items to collect types from + nitems Number of items in the array + skip_nulls Don't collect types of NULL items if TRUE + + DESCRIPTION + This function collects different result types for comparison of the first + item in the list with each of the remaining items in the 'items' array. + + RETURN + 0 - if row type incompatibility has been detected (see cmp_row_type) + Bitmap of collected types - otherwise +*/ + +static uint collect_cmp_types(Item **items, uint nitems, bool skip_nulls= FALSE) +{ + uint i; + uint found_types; + Item_result left_result= items[0]->result_type(); + DBUG_ASSERT(nitems > 1); + found_types= 0; + for (i= 1; i < nitems ; i++) + { + if (skip_nulls && items[i]->type() == Item::NULL_ITEM) + continue; // Skip NULL constant items + if ((left_result == ROW_RESULT || + items[i]->result_type() == ROW_RESULT) && + cmp_row_type(items[0], items[i])) + return 0; + found_types|= 1<< (uint)item_cmp_type(left_result, + items[i]->result_type()); + } + /* + Even if all right-hand items are NULLs and we are skipping them all, we need + at least one type bit in the found_type bitmask. + */ + if (skip_nulls && !found_types) + found_types= 1 << (uint)left_result; + return found_types; +} static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, const char *fname) @@ -246,17 +298,18 @@ longlong Item_func_not::val_int() higher than the precedence of NOT. */ -void Item_func_not::print(String *str) +void Item_func_not::print(String *str, enum_query_type query_type) { str->append('('); - Item_func::print(str); + Item_func::print(str, query_type); str->append(')'); } -/* - special NOT for ALL subquery +/** + special NOT for ALL subquery. */ + longlong Item_func_not_all::val_int() { DBUG_ASSERT(fixed == 1); @@ -280,19 +333,22 @@ bool Item_func_not_all::empty_underlying_subquery() (test_sub_item && !test_sub_item->any_value())); } -void Item_func_not_all::print(String *str) +void Item_func_not_all::print(String *str, enum_query_type query_type) { if (show) - Item_func::print(str); + Item_func::print(str, query_type); else - args[0]->print(str); + args[0]->print(str, query_type); } -/* - Special NOP (No OPeration) for ALL subquery it is like Item_func_not_all - (return TRUE if underlying subquery do not return rows) but if subquery - returns some rows it return same value as argument (TRUE/FALSE). +/** + Special NOP (No OPeration) for ALL subquery. It is like + Item_func_not_all. + + @return + (return TRUE if underlying subquery do not return rows) but if subquery + returns some rows it return same value as argument (TRUE/FALSE). */ longlong Item_func_nop_all::val_int() @@ -312,16 +368,9 @@ longlong Item_func_nop_all::val_int() } -/* - Convert a constant item to an int and replace the original item - - SYNOPSIS - convert_constant_item() - thd thread handle - field_item item will be converted using the type of this field - item [in/out] reference to the item to convert +/** + Convert a constant item to an int and replace the original item. - DESCRIPTION The function converts a constant expression or string to an integer. On successful conversion the original item is substituted for the result of the item evaluation. @@ -329,16 +378,21 @@ longlong Item_func_nop_all::val_int() also when comparing bigint to strings (in which case strings are converted to bigints). - NOTES + @param thd thread handle + @param field item will be converted using the type of this field + @param[in,out] item reference to the item to convert + + @note This function is called only at prepare stage. As all derived tables are filled only after all derived tables are prepared we do not evaluate items with subselects here because they can contain derived tables and thus we may attempt to use a table that has not been populated yet. - RESULT VALUES - 0 Can't convert item - 1 Item was replaced with an integer version of the item + @retval + 0 Can't convert item + @retval + 1 Item was replaced with an integer version of the item */ static bool convert_constant_item(THD *thd, Item_field *field_item, @@ -349,29 +403,44 @@ static bool convert_constant_item(THD *thd, Item_field *field_item, if (!(*item)->with_subselect && (*item)->const_item()) { - /* For comparison purposes allow invalid dates like 2000-01-32 */ + TABLE *table= field->table; ulong orig_sql_mode= thd->variables.sql_mode; enum_check_fields orig_count_cuted_fields= thd->count_cuted_fields; + my_bitmap_map *old_maps[2]; ulonglong UNINIT_VAR(orig_field_val); /* original field value if valid */ + + LINT_INIT(old_maps[0]); + LINT_INIT(old_maps[1]); + + if (table) + dbug_tmp_use_all_columns(table, old_maps, + table->read_set, table->write_set); + /* For comparison purposes allow invalid dates like 2000-01-32 */ thd->variables.sql_mode= (orig_sql_mode & ~MODE_NO_ZERO_DATE) | MODE_INVALID_DATES; thd->count_cuted_fields= CHECK_FIELD_IGNORE; + /* - Store the value of the field if it references an outer field because - the call to save_in_field below overrides that value. + Store the value of the field/constant if it references an outer field + because the call to save_in_field below overrides that value. + Don't save field value if no data has been read yet. + Outer constant values are always saved. */ - if (field_item->depended_from) + bool save_field_value= (field_item->depended_from && + (field_item->const_item() || + !(field->table->status & STATUS_NO_RECORD))); + if (save_field_value) orig_field_val= field->val_int(); if (!(*item)->is_null() && !(*item)->save_in_field(field, 1)) { - Item *tmp=new Item_int_with_ref(field->val_int(), *item, - test(field->flags & UNSIGNED_FLAG)); + Item *tmp= new Item_int_with_ref(field->val_int(), *item, + test(field->flags & UNSIGNED_FLAG)); if (tmp) thd->change_item_tree(item, tmp); result= 1; // Item was replaced } /* Restore the original field value. */ - if (field_item->depended_from) + if (save_field_value) { result= field->store(orig_field_val, TRUE); /* orig_field_val must be a valid value that can be restored back. */ @@ -379,6 +448,8 @@ static bool convert_constant_item(THD *thd, Item_field *field_item, } thd->variables.sql_mode= orig_sql_mode; thd->count_cuted_fields= orig_count_cuted_fields; + if (table) + dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_maps); } return result; } @@ -465,11 +536,12 @@ void Item_bool_func2::fix_length_and_dec() } -int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) +int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type) { owner= item; func= comparator_matrix[type] - [test(owner->functype() == Item_func::EQUAL_FUNC)]; + [is_owner_equal_func()]; + switch (type) { case ROW_RESULT: { @@ -489,7 +561,8 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols()); return 1; } - if (comparators[i].set_cmp_func(owner, (*a)->addr(i), (*b)->addr(i))) + if (comparators[i].set_cmp_func(owner, (*a)->addr(i), (*b)->addr(i), + set_null)) return 1; } break; @@ -503,7 +576,8 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) if (cmp_collation.set((*a)->collation, (*b)->collation) || cmp_collation.derivation == DERIVATION_NONE) { - my_coll_agg_error((*a)->collation, (*b)->collation, owner->func_name()); + my_coll_agg_error((*a)->collation, (*b)->collation, + owner->func_name()); return 1; } if (cmp_collation.collation == &my_charset_bin) @@ -525,8 +599,8 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) which would be transformed to: WHERE col= 'j' */ - (*a)->walk(&Item::set_no_const_sub, (byte*) 0); - (*b)->walk(&Item::set_no_const_sub, (byte*) 0); + (*a)->walk(&Item::set_no_const_sub, FALSE, (uchar*) 0); + (*b)->walk(&Item::set_no_const_sub, FALSE, (uchar*) 0); } break; } @@ -568,56 +642,51 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) return 0; } - /** - @brief Convert date provided in a string to the int representation. - - @param[in] thd thread handle - @param[in] str a string to convert - @param[in] warn_type type of the timestamp for issuing the warning - @param[in] warn_name field name for issuing the warning - @param[out] error_arg could not extract a DATE or DATETIME - - @details Convert date provided in the string str to the int - representation. If the string contains wrong date or doesn't - contain it at all then a warning is issued. The warn_type and - the warn_name arguments are used as the name and the type of the - field when issuing the warning. If any input was discarded - (trailing or non-timestampy characters), was_cut will be non-zero. - was_type will return the type str_to_datetime() could correctly - extract. - - @return - converted value. 0 on error and on zero-dates -- check 'failure' + Parse date provided in a string to a MYSQL_TIME. + + @param[in] thd Thread handle + @param[in] str A string to convert + @param[in] warn_type Type of the timestamp for issuing the warning + @param[in] warn_name Field name for issuing the warning + @param[out] l_time The MYSQL_TIME objects is initialized. + + Parses a date provided in the string str into a MYSQL_TIME object. If the + string contains an incorrect date or doesn't correspond to a date at all + then a warning is issued. The warn_type and the warn_name arguments are used + as the name and the type of the field when issuing the warning. If any input + was discarded (trailing or non-timestamp-y characters), return value will be + TRUE. + + @return Status flag + @retval FALSE Success. + @retval True Indicates failure. */ -static ulonglong -get_date_from_str(THD *thd, String *str, timestamp_type warn_type, - char *warn_name, bool *error_arg) +bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type, + const char *warn_name, MYSQL_TIME *l_time) { - ulonglong value= 0; + bool value; int error; - MYSQL_TIME l_time; - enum_mysql_timestamp_type ret; + enum_mysql_timestamp_type timestamp_type; - ret= str_to_datetime(str->ptr(), str->length(), &l_time, - (TIME_FUZZY_DATE | MODE_INVALID_DATES | - (thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE))), - &error); + timestamp_type= + str_to_datetime(str->ptr(), str->length(), l_time, + (TIME_FUZZY_DATE | MODE_INVALID_DATES | + (thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE))), + &error); - if (ret == MYSQL_TIMESTAMP_DATETIME || ret == MYSQL_TIMESTAMP_DATE) - { + if (timestamp_type == MYSQL_TIMESTAMP_DATETIME || + timestamp_type == MYSQL_TIMESTAMP_DATE) /* Do not return yet, we may still want to throw a "trailing garbage" warning. */ - *error_arg= FALSE; - value= TIME_to_ulonglong_datetime(&l_time); - } + value= FALSE; else { - *error_arg= TRUE; + value= TRUE; error= 1; /* force warning */ } @@ -630,6 +699,37 @@ get_date_from_str(THD *thd, String *str, timestamp_type warn_type, } +/** + @brief Convert date provided in a string to the int representation. + + @param[in] thd thread handle + @param[in] str a string to convert + @param[in] warn_type type of the timestamp for issuing the warning + @param[in] warn_name field name for issuing the warning + @param[out] error_arg could not extract a DATE or DATETIME + + @details Convert date provided in the string str to the int + representation. If the string contains wrong date or doesn't + contain it at all then a warning is issued. The warn_type and + the warn_name arguments are used as the name and the type of the + field when issuing the warning. + + @return + converted value. 0 on error and on zero-dates -- check 'failure' +*/ +static ulonglong get_date_from_str(THD *thd, String *str, + timestamp_type warn_type, + const char *warn_name, bool *error_arg) +{ + MYSQL_TIME l_time; + *error_arg= get_mysql_time_from_str(thd, str, warn_type, warn_name, &l_time); + + if (*error_arg) + return 0; + return TIME_to_ulonglong_datetime(&l_time); +} + + /* Check whether compare_datetime() can be used to compare items. @@ -691,15 +791,21 @@ Arg_comparator::can_compare_as_dates(Item *a, Item *b, ulonglong *const_value) if (cmp_type != CMP_DATE_DFLT) { + THD *thd= current_thd; /* Do not cache GET_USER_VAR() function as its const_item() may return TRUE for the current thread but it still may change during the execution. + Don't use cache while in the context analysis mode only (i.e. for + EXPLAIN/CREATE VIEW and similar queries). Cache is useless in such + cases and can cause problems. For example evaluating subqueries can + confuse storage engines since in context analysis mode tables + aren't locked. */ - if (cmp_type != CMP_DATE_WITH_DATE && str_arg->const_item() && + if (!thd->is_context_analysis_only() && + cmp_type != CMP_DATE_WITH_DATE && str_arg->const_item() && (str_arg->type() != Item::FUNC_ITEM || ((Item_func*)str_arg)->functype() != Item_func::GUSERVAR_FUNC)) { - THD *thd= current_thd; ulonglong value; bool error; String tmp, *str_val= 0; @@ -781,19 +887,21 @@ get_time_value(THD *thd, Item ***item_arg, Item **cache_arg, } -int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, +int Arg_comparator::set_cmp_func(Item_result_field *owner_arg, Item **a1, Item **a2, Item_result type) { enum enum_date_cmp_type cmp_type; ulonglong const_value= (ulonglong)-1; + thd= current_thd; + owner= owner_arg; a= a1; b= a2; + owner= owner_arg; + thd= current_thd; if ((cmp_type= can_compare_as_dates(*a, *b, &const_value))) { - thd= current_thd; - owner= owner_arg; a_type= (*a)->field_type(); b_type= (*b)->field_type(); a_cache= 0; @@ -801,6 +909,10 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, if (const_value != (ulonglong)-1) { + /* + cache_converted_constant can't be used here because it can't + correctly convert a DATETIME value from string to int representation. + */ Item_cache_int *cache= new Item_cache_int(); /* Mark the cache as non-const to prevent re-caching. */ cache->set_used_tables(1); @@ -817,22 +929,22 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, b= (Item **)&b_cache; } } - is_nulls_eq= test(owner && owner->functype() == Item_func::EQUAL_FUNC); + is_nulls_eq= is_owner_equal_func(); func= &Arg_comparator::compare_datetime; - get_value_func= &get_datetime_value; + get_value_a_func= &get_datetime_value; + get_value_b_func= &get_datetime_value; return 0; } else if (type == STRING_RESULT && (*a)->field_type() == MYSQL_TYPE_TIME && (*b)->field_type() == MYSQL_TYPE_TIME) { /* Compare TIME values as integers. */ - thd= current_thd; - owner= owner_arg; a_cache= 0; b_cache= 0; - is_nulls_eq= test(owner && owner->functype() == Item_func::EQUAL_FUNC); + is_nulls_eq= is_owner_equal_func(); func= &Arg_comparator::compare_datetime; - get_value_func= &get_time_value; + get_value_a_func= &get_time_value; + get_value_b_func= &get_time_value; return 0; } else if (type == STRING_RESULT && @@ -841,20 +953,89 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, { DTCollation coll; coll.set((*a)->collation.collation); - if (agg_item_set_converter(coll, owner_arg->func_name(), + if (agg_item_set_converter(coll, owner->func_name(), b, 1, MY_COLL_CMP_CONV, 1)) return 1; + } else if (type != ROW_RESULT && ((*a)->field_type() == MYSQL_TYPE_YEAR || + (*b)->field_type() == MYSQL_TYPE_YEAR)) + { + is_nulls_eq= is_owner_equal_func(); + year_as_datetime= FALSE; + + if ((*a)->is_datetime()) + { + year_as_datetime= TRUE; + get_value_a_func= &get_datetime_value; + } else if ((*a)->field_type() == MYSQL_TYPE_YEAR) + get_value_a_func= &get_year_value; + else + { + /* + Because convert_constant_item is called only for EXECUTE in PS mode + the value of get_value_x_func set in PREPARE might be not + valid for EXECUTE. + */ + get_value_a_func= NULL; + } + + if ((*b)->is_datetime()) + { + year_as_datetime= TRUE; + get_value_b_func= &get_datetime_value; + } else if ((*b)->field_type() == MYSQL_TYPE_YEAR) + get_value_b_func= &get_year_value; + else + get_value_b_func= NULL; + + func= &Arg_comparator::compare_year; + return 0; } + a= cache_converted_constant(thd, a, &a_cache, type); + b= cache_converted_constant(thd, b, &b_cache, type); return set_compare_func(owner_arg, type); } -void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1) +/** + Convert and cache a constant. + + @param value [in] An item to cache + @param cache_item [out] Placeholder for the cache item + @param type [in] Comparison type + + @details + When given item is a constant and its type differs from comparison type + then cache its value to avoid type conversion of this constant on each + evaluation. In this case the value is cached and the reference to the cache + is returned. + Original value is returned otherwise. + + @return cache item or original value. +*/ + +Item** Arg_comparator::cache_converted_constant(THD *thd, Item **value, + Item **cache_item, + Item_result type) +{ + /* Don't need cache if doing context analysis only. */ + if (!thd->is_context_analysis_only() && + (*value)->const_item() && type != (*value)->result_type()) + { + Item_cache *cache= Item_cache::get_cache(*value, type); + cache->store(*value); + *cache_item= cache; + return cache_item; + } + return value; +} + + +void Arg_comparator::set_datetime_cmp_func(Item_result_field *owner_arg, + Item **a1, Item **b1) { thd= current_thd; - /* A caller will handle null values by itself. */ - owner= NULL; + owner= owner_arg; a= a1; b= b1; a_type= (*a)->field_type(); @@ -863,7 +1044,8 @@ void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1) b_cache= 0; is_nulls_eq= FALSE; func= &Arg_comparator::compare_datetime; - get_value_func= &get_datetime_value; + get_value_a_func= &get_datetime_value; + get_value_b_func= &get_datetime_value; } @@ -908,12 +1090,15 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, { value= item->val_int(); *is_null= item->null_value; + enum_field_types f_type= item->field_type(); /* Item_date_add_interval may return MYSQL_TYPE_STRING as the result field type. To detect that the DATE value has been returned we - compare it with 1000000L - any DATE value should be less than it. + compare it with 100000000L - any DATE value should be less than it. + Don't shift cached DATETIME values up for the second time. */ - if (item->field_type() == MYSQL_TYPE_DATE || value < 100000000L) + if (f_type == MYSQL_TYPE_DATE || + (f_type != MYSQL_TYPE_DATETIME && value < 100000000L)) value*= 1000000L; } else @@ -922,7 +1107,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, *is_null= item->null_value; } if (*is_null) - return -1; + return ~(ulonglong) 0; /* Convert strings to the integer DATE/DATETIME representation. Even if both dates provided in strings we can't compare them directly as @@ -950,7 +1135,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, if (item->const_item() && cache_arg && (item->type() != Item::FUNC_ITEM || ((Item_func*)item)->functype() != Item_func::GUSERVAR_FUNC)) { - Item_cache_int *cache= new Item_cache_int(); + Item_cache_int *cache= new Item_cache_int(MYSQL_TYPE_DATETIME); /* Mark the cache as non-const to prevent re-caching. */ cache->set_used_tables(1); cache->store(item, value); @@ -960,6 +1145,51 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, return value; } + +/* + Retrieves YEAR value of 19XX form from given item. + + SYNOPSIS + get_year_value() + thd thread handle + item_arg [in/out] item to retrieve YEAR value from + cache_arg [in/out] pointer to place to store the caching item to + warn_item [in] item for issuing the conversion warning + is_null [out] TRUE <=> the item_arg is null + + DESCRIPTION + Retrieves the YEAR value of 19XX form from given item for comparison by the + compare_year() function. + + RETURN + obtained value +*/ + +static longlong +get_year_value(THD *thd, Item ***item_arg, Item **cache_arg, + Item *warn_item, bool *is_null) +{ + longlong value= 0; + Item *item= **item_arg; + + value= item->val_int(); + *is_null= item->null_value; + if (*is_null) + return ~(ulonglong) 0; + + /* + Coerce value to the 19XX form in order to correctly compare + YEAR(2) & YEAR(4) types. + */ + if (value < 70) + value+= 100; + if (value <= 1900) + value+= 1900; + + return value; +} + + /* Compare items values as dates. @@ -992,25 +1222,25 @@ int Arg_comparator::compare_datetime() longlong a_value, b_value; /* Get DATE/DATETIME/TIME value of the 'a' item. */ - a_value= (*get_value_func)(thd, &a, &a_cache, *b, &a_is_null); + a_value= (*get_value_a_func)(thd, &a, &a_cache, *b, &a_is_null); if (!is_nulls_eq && a_is_null) { - if (owner) + if (set_null) owner->null_value= 1; return -1; } /* Get DATE/DATETIME/TIME value of the 'b' item. */ - b_value= (*get_value_func)(thd, &b, &b_cache, *a, &b_is_null); + b_value= (*get_value_b_func)(thd, &b, &b_cache, *a, &b_is_null); if (a_is_null || b_is_null) { - if (owner) + if (set_null) owner->null_value= is_nulls_eq ? 0 : 1; return is_nulls_eq ? (a_is_null == b_is_null) : -1; } /* Here we have two not-NULL values. */ - if (owner) + if (set_null) owner->null_value= 0; /* Compare values. */ @@ -1023,56 +1253,63 @@ int Arg_comparator::compare_datetime() int Arg_comparator::compare_string() { String *res1,*res2; - if ((res1= (*a)->val_str(&owner->tmp_value1))) + if ((res1= (*a)->val_str(&value1))) { - if ((res2= (*b)->val_str(&owner->tmp_value2))) + if ((res2= (*b)->val_str(&value2))) { - owner->null_value= 0; + if (set_null) + owner->null_value= 0; return sortcmp(res1,res2,cmp_collation.collation); } } - owner->null_value= 1; + if (set_null) + owner->null_value= 1; return -1; } -/* +/** Compare strings byte by byte. End spaces are also compared. - RETURN - < 0 *a < *b - 0 *b == *b - > 0 *a > *b + @retval + <0 *a < *b + @retval + 0 *b == *b + @retval + >0 *a > *b */ int Arg_comparator::compare_binary_string() { String *res1,*res2; - if ((res1= (*a)->val_str(&owner->tmp_value1))) + if ((res1= (*a)->val_str(&value1))) { - if ((res2= (*b)->val_str(&owner->tmp_value2))) + if ((res2= (*b)->val_str(&value2))) { - owner->null_value= 0; + if (set_null) + owner->null_value= 0; uint res1_length= res1->length(); uint res2_length= res2->length(); int cmp= memcmp(res1->ptr(), res2->ptr(), min(res1_length,res2_length)); return cmp ? cmp : (int) (res1_length - res2_length); } } - owner->null_value= 1; + if (set_null) + owner->null_value= 1; return -1; } -/* - Compare strings, but take into account that NULL == NULL +/** + Compare strings, but take into account that NULL == NULL. */ + int Arg_comparator::compare_e_string() { String *res1,*res2; - res1= (*a)->val_str(&owner->tmp_value1); - res2= (*b)->val_str(&owner->tmp_value2); + res1= (*a)->val_str(&value1); + res2= (*b)->val_str(&value2); if (!res1 || !res2) return test(res1 == res2); return test(sortcmp(res1, res2, cmp_collation.collation) == 0); @@ -1082,8 +1319,8 @@ int Arg_comparator::compare_e_string() int Arg_comparator::compare_e_binary_string() { String *res1,*res2; - res1= (*a)->val_str(&owner->tmp_value1); - res2= (*b)->val_str(&owner->tmp_value2); + res1= (*a)->val_str(&value1); + res2= (*b)->val_str(&value2); if (!res1 || !res2) return test(res1 == res2); return test(stringcmp(res1, res2) == 0); @@ -1104,13 +1341,15 @@ int Arg_comparator::compare_real() val2= (*b)->val_real(); if (!(*b)->null_value) { - owner->null_value= 0; + if (set_null) + owner->null_value= 0; if (val1 < val2) return -1; if (val1 == val2) return 0; return 1; } } - owner->null_value= 1; + if (set_null) + owner->null_value= 1; return -1; } @@ -1124,11 +1363,13 @@ int Arg_comparator::compare_decimal() my_decimal *val2= (*b)->val_decimal(&value2); if (!(*b)->null_value) { - owner->null_value= 0; + if (set_null) + owner->null_value= 0; return my_decimal_cmp(val1, val2); } } - owner->null_value= 1; + if (set_null) + owner->null_value= 1; return -1; } @@ -1166,7 +1407,8 @@ int Arg_comparator::compare_real_fixed() val2= (*b)->val_real(); if (!(*b)->null_value) { - owner->null_value= 0; + if (set_null) + owner->null_value= 0; if (val1 == val2 || fabs(val1 - val2) < precision) return 0; if (val1 < val2) @@ -1174,7 +1416,8 @@ int Arg_comparator::compare_real_fixed() return 1; } } - owner->null_value= 1; + if (set_null) + owner->null_value= 1; return -1; } @@ -1197,18 +1440,20 @@ int Arg_comparator::compare_int_signed() longlong val2= (*b)->val_int(); if (!(*b)->null_value) { - owner->null_value= 0; + if (set_null) + owner->null_value= 0; if (val1 < val2) return -1; if (val1 == val2) return 0; return 1; } } - owner->null_value= 1; + if (set_null) + owner->null_value= 1; return -1; } -/* +/** Compare values as BIGINT UNSIGNED. */ @@ -1220,18 +1465,20 @@ int Arg_comparator::compare_int_unsigned() ulonglong val2= (*b)->val_int(); if (!(*b)->null_value) { - owner->null_value= 0; + if (set_null) + owner->null_value= 0; if (val1 < val2) return -1; if (val1 == val2) return 0; return 1; } } - owner->null_value= 1; + if (set_null) + owner->null_value= 1; return -1; } -/* +/** Compare signed (*a) with unsigned (*B) */ @@ -1243,7 +1490,8 @@ int Arg_comparator::compare_int_signed_unsigned() ulonglong uval2= (ulonglong)(*b)->val_int(); if (!(*b)->null_value) { - owner->null_value= 0; + if (set_null) + owner->null_value= 0; if (sval1 < 0 || (ulonglong)sval1 < uval2) return -1; if ((ulonglong)sval1 == uval2) @@ -1251,12 +1499,13 @@ int Arg_comparator::compare_int_signed_unsigned() return 1; } } - owner->null_value= 1; + if (set_null) + owner->null_value= 1; return -1; } -/* +/** Compare unsigned (*a) with signed (*B) */ @@ -1268,7 +1517,8 @@ int Arg_comparator::compare_int_unsigned_signed() longlong sval2= (*b)->val_int(); if (!(*b)->null_value) { - owner->null_value= 0; + if (set_null) + owner->null_value= 0; if (sval2 < 0) return 1; if (uval1 < (ulonglong)sval2) @@ -1278,7 +1528,8 @@ int Arg_comparator::compare_int_unsigned_signed() return 1; } } - owner->null_value= 1; + if (set_null) + owner->null_value= 1; return -1; } @@ -1292,7 +1543,7 @@ int Arg_comparator::compare_e_int() return test(val1 == val2); } -/* +/** Compare unsigned *a with signed *b or signed *a with unsigned *b. */ int Arg_comparator::compare_e_int_diff_signedness() @@ -1314,10 +1565,11 @@ int Arg_comparator::compare_row() for (uint i= 0; i<n; i++) { res= comparators[i].compare(); - if (owner->null_value) + /* Aggregate functions don't need special null handling. */ + if (owner->null_value && owner->type() == Item::FUNC_ITEM) { // NULL was compared - switch (owner->functype()) { + switch (((Item_func*)owner)->functype()) { case Item_func::NE_FUNC: break; // NE never aborts on NULL even if abort_on_null is set case Item_func::LT_FUNC: @@ -1326,7 +1578,7 @@ int Arg_comparator::compare_row() case Item_func::GE_FUNC: return -1; // <, <=, > and >= always fail on NULL default: // EQ_FUNC - if (owner->abort_on_null) + if (((Item_bool_func2*)owner)->abort_on_null) return -1; // We do not need correct NULL returning } was_null= 1; @@ -1363,6 +1615,67 @@ int Arg_comparator::compare_e_row() } +/** + Compare values as YEAR. + + @details + Compare items as YEAR for EQUAL_FUNC and for other comparison functions. + The YEAR values of form 19XX are obtained with help of the get_year_value() + function. + If one of arguments is of DATE/DATETIME type its value is obtained + with help of the get_datetime_value function. In this case YEAR values + prior to comparison are converted to the ulonglong YYYY-00-00 00:00:00 + DATETIME form. + If an argument type neither YEAR nor DATE/DATEIME then val_int function + is used to obtain value for comparison. + + RETURN + If is_nulls_eq is TRUE: + 1 if items are equal or both are null + 0 otherwise + If is_nulls_eq is FALSE: + -1 a < b + 0 a == b or at least one of items is null + 1 a > b + See the table: + is_nulls_eq | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | + a_is_null | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | + b_is_null | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | + result | 1 | 0 | 0 |0/1| 0 | 0 | 0 |-1/0/1| +*/ + +int Arg_comparator::compare_year() +{ + bool a_is_null, b_is_null; + ulonglong val1= get_value_a_func ? + (*get_value_a_func)(thd, &a, &a_cache, *b, &a_is_null) : + (*a)->val_int(); + ulonglong val2= get_value_b_func ? + (*get_value_b_func)(thd, &b, &b_cache, *a, &b_is_null) : + (*b)->val_int(); + if (!(*a)->null_value) + { + if (!(*b)->null_value) + { + if (set_null) + owner->null_value= 0; + /* Convert year to DATETIME of form YYYY-00-00 00:00:00 when necessary. */ + if((*a)->field_type() == MYSQL_TYPE_YEAR && year_as_datetime) + val1*= 10000000000LL; + if((*b)->field_type() == MYSQL_TYPE_YEAR && year_as_datetime) + val2*= 10000000000LL; + + if (val1 < val2) return is_nulls_eq ? 0 : -1; + if (val1 == val2) return is_nulls_eq ? 1 : 0; + return is_nulls_eq ? 0 : 1; + } + } + if (set_null) + owner->null_value= is_nulls_eq ? 0 : 1; + return (is_nulls_eq && (*a)->null_value == (*b)->null_value) ? 1 : 0; +} + + void Item_func_truth::fix_length_and_dec() { maybe_null= 0; @@ -1372,10 +1685,10 @@ void Item_func_truth::fix_length_and_dec() } -void Item_func_truth::print(String *str) +void Item_func_truth::print(String *str, enum_query_type query_type) { str->append('('); - args[0]->print(str); + args[0]->print(str, query_type); str->append(STRING_WITH_LEN(" is ")); if (! affirmative) str->append(STRING_WITH_LEN("not ")); @@ -1482,64 +1795,77 @@ longlong Item_in_optimizer::val_int() bool tmp; DBUG_ASSERT(fixed == 1); cache->store(args[0]); + cache->cache_value(); if (cache->null_value) { + /* + We're evaluating + "<outer_value_list> [NOT] IN (SELECT <inner_value_list>...)" + where one or more of the outer values is NULL. + */ if (((Item_in_subselect*)args[1])->is_top_level_item()) { /* - We're evaluating "NULL IN (SELECT ...)". The result can be NULL or - FALSE, and we can return one instead of another. Just return NULL. + We're evaluating a top level item, e.g. + "<outer_value_list> IN (SELECT <inner_value_list>...)", + and in this case a NULL value in the outer_value_list means + that the result shall be NULL/FALSE (makes no difference for + top level items). The cached value is NULL, so just return + NULL. */ null_value= 1; } else { - if (!((Item_in_subselect*)args[1])->is_correlated && - result_for_null_param != UNKNOWN) + /* + We're evaluating an item where a NULL value in either the + outer or inner value list does not automatically mean that we + can return NULL/FALSE. An example of such a query is + "<outer_value_list> NOT IN (SELECT <inner_value_list>...)" + The result when there is at least one NULL value is: NULL if the + SELECT evaluated over the non-NULL values produces at least + one row, FALSE otherwise + */ + Item_in_subselect *item_subs=(Item_in_subselect*)args[1]; + bool all_left_cols_null= true; + const uint ncols= cache->cols(); + + /* + Turn off the predicates that are based on column compares for + which the left part is currently NULL + */ + for (uint i= 0; i < ncols; i++) { - /* Use cached value from previous execution */ - null_value= result_for_null_param; + if (cache->element_index(i)->null_value) + item_subs->set_cond_guard_var(i, FALSE); + else + all_left_cols_null= false; } - else + + if (!((Item_in_subselect*)args[1])->is_correlated && + all_left_cols_null && result_for_null_param != UNKNOWN) { - /* - We're evaluating "NULL IN (SELECT ...)". The result is: - FALSE if SELECT produces an empty set, or - NULL otherwise. - We disable the predicates we've pushed down into subselect, run the - subselect and see if it has produced any rows. + /* + This is a non-correlated subquery, all values in the outer + value list are NULL, and we have already evaluated the + subquery for all NULL values: Return the same result we + did last time without evaluating the subquery. */ - Item_in_subselect *item_subs=(Item_in_subselect*)args[1]; - if (cache->cols() == 1) - { - item_subs->set_cond_guard_var(0, FALSE); - (void) args[1]->val_bool_result(); - result_for_null_param= null_value= !item_subs->engine->no_rows(); - item_subs->set_cond_guard_var(0, TRUE); - } - else - { - uint i; - uint ncols= cache->cols(); - /* - Turn off the predicates that are based on column compares for - which the left part is currently NULL - */ - for (i= 0; i < ncols; i++) - { - if (cache->element_index(i)->null_value) - item_subs->set_cond_guard_var(i, FALSE); - } - - (void) args[1]->val_bool_result(); - result_for_null_param= null_value= !item_subs->engine->no_rows(); - - /* Turn all predicates back on */ - for (i= 0; i < ncols; i++) - item_subs->set_cond_guard_var(i, TRUE); - } + null_value= result_for_null_param; + } + else + { + /* The subquery has to be evaluated */ + (void) args[1]->val_bool_result(); + null_value= !item_subs->engine->no_rows(); + if (all_left_cols_null) + result_for_null_param= null_value; } + + /* Turn all predicates back on */ + for (uint i= 0; i < ncols; i++) + item_subs->set_cond_guard_var(i, TRUE); } return 0; } @@ -1568,8 +1894,8 @@ void Item_in_optimizer::cleanup() bool Item_in_optimizer::is_null() { - cache->store(args[0]); - return (null_value= (cache->null_value || args[1]->is_null())); + val_int(); + return null_value; } @@ -1581,7 +1907,7 @@ longlong Item_func_eq::val_int() } -/* Same as Item_func_eq, but NULL = NULL */ +/** Same as Item_func_eq, but NULL = NULL. */ void Item_func_equal::fix_length_and_dec() { @@ -1637,8 +1963,8 @@ longlong Item_func_lt::val_int() longlong Item_func_strcmp::val_int() { DBUG_ASSERT(fixed == 1); - String *a=args[0]->val_str(&tmp_value1); - String *b=args[1]->val_str(&tmp_value2); + String *a=args[0]->val_str(&cmp.value1); + String *b=args[1]->val_str(&cmp.value2); if (!a || !b) { null_value=1; @@ -1674,8 +2000,10 @@ void Item_func_interval::fix_length_and_dec() { uint rows= row->cols(); - use_decimal_comparison= (row->element_index(0)->result_type() == DECIMAL_RESULT) || - (row->element_index(0)->result_type() == INT_RESULT); + use_decimal_comparison= ((row->element_index(0)->result_type() == + DECIMAL_RESULT) || + (row->element_index(0)->result_type() == + INT_RESULT)); if (rows > 8) { bool not_null_consts= TRUE; @@ -1733,21 +2061,18 @@ void Item_func_interval::fix_length_and_dec() } -/* - Execute Item_func_interval() - - SYNOPSIS - Item_func_interval::val_int() +/** + Execute Item_func_interval(). - NOTES - If we are doing a decimal comparison, we are - evaluating the first item twice. + @note + If we are doing a decimal comparison, we are evaluating the first + item twice. - RETURN - -1 if null value, - 0 if lower than lowest - 1 - arg_count-1 if between args[n] and args[n+1] - arg_count if higher than biggest argument + @return + - -1 if null value, + - 0 if lower than lowest + - 1 - arg_count-1 if between args[n] and args[n+1] + - arg_count if higher than biggest argument */ longlong Item_func_interval::val_int() @@ -1829,32 +2154,31 @@ longlong Item_func_interval::val_int() } -/* - Perform context analysis of a BETWEEN item tree - - SYNOPSIS: - fix_fields() - thd reference to the global context of the query thread - tables list of all open tables involved in the query - ref pointer to Item* variable where pointer to resulting "fixed" - item is to be assigned +/** + Perform context analysis of a BETWEEN item tree. - DESCRIPTION This function performs context analysis (name resolution) and calculates various attributes of the item tree with Item_func_between as its root. The function saves in ref the pointer to the item or to a newly created item that is considered as a replacement for the original one. - NOTES + @param thd reference to the global context of the query thread + @param ref pointer to Item* variable where pointer to resulting "fixed" + item is to be assigned + + @note Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on a predicate/function level. Then it's easy to show that: + @verbatim T0(e BETWEEN e1 AND e2) = union(T1(e),T1(e1),T1(e2)) T1(e BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2))) T0(e NOT BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2))) T1(e NOT BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2))) + @endverbatim - RETURN + @retval 0 ok + @retval 1 got error */ @@ -1881,11 +2205,11 @@ bool Item_func_between::fix_fields(THD *thd, Item **ref) void Item_func_between::fix_length_and_dec() { max_length= 1; - THD *thd= current_thd; int i; bool datetime_found= FALSE; int time_items_found= 0; compare_as_dates= TRUE; + THD *thd= current_thd; /* As some compare functions are generated after sql_yacc, @@ -1893,7 +2217,7 @@ void Item_func_between::fix_length_and_dec() */ if (!args[0] || !args[1] || !args[2]) return; - if ( agg_cmp_type(thd, &cmp_type, args, 3)) + if ( agg_cmp_type(&cmp_type, args, 3)) return; if (cmp_type == STRING_RESULT && agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV, 1)) @@ -1923,8 +2247,8 @@ void Item_func_between::fix_length_and_dec() if (compare_as_dates) { - ge_cmp.set_datetime_cmp_func(args, args + 1); - le_cmp.set_datetime_cmp_func(args, args + 2); + ge_cmp.set_datetime_cmp_func(this, args, args + 1); + le_cmp.set_datetime_cmp_func(this, args, args + 2); } else if (time_items_found == 3) { @@ -2061,16 +2385,16 @@ longlong Item_func_between::val_int() } -void Item_func_between::print(String *str) +void Item_func_between::print(String *str, enum_query_type query_type) { str->append('('); - args[0]->print(str); + args[0]->print(str, query_type); if (negated) str->append(STRING_WITH_LEN(" not")); str->append(STRING_WITH_LEN(" between ")); - args[1]->print(str); + args[1]->print(str, query_type); str->append(STRING_WITH_LEN(" and ")); - args[2]->print(str); + args[2]->print(str, query_type); str->append(')'); } @@ -2130,7 +2454,7 @@ enum_field_types Item_func_ifnull::field_type() const Field *Item_func_ifnull::tmp_table_field(TABLE *table) { - return tmp_table_field_from_field_type(table); + return tmp_table_field_from_field_type(table, 0); } double @@ -2201,30 +2525,29 @@ Item_func_ifnull::str_op(String *str) } -/* - Perform context analysis of an IF item tree - - SYNOPSIS: - fix_fields() - thd reference to the global context of the query thread - tables list of all open tables involved in the query - ref pointer to Item* variable where pointer to resulting "fixed" - item is to be assigned +/** + Perform context analysis of an IF item tree. - DESCRIPTION This function performs context analysis (name resolution) and calculates various attributes of the item tree with Item_func_if as its root. The function saves in ref the pointer to the item or to a newly created item that is considered as a replacement for the original one. - NOTES + @param thd reference to the global context of the query thread + @param ref pointer to Item* variable where pointer to resulting "fixed" + item is to be assigned + + @note Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on a predicate/function level. Then it's easy to show that: + @verbatim T0(IF(e,e1,e2) = T1(IF(e,e1,e2)) T1(IF(e,e1,e2)) = intersection(T1(e1),T1(e2)) + @endverbatim - RETURN + @retval 0 ok + @retval 1 got error */ @@ -2370,11 +2693,14 @@ Item_func_nullif::fix_length_and_dec() } -/* - nullif () returns NULL if arguments are equal, else it returns the - first argument. +/** + @note Note that we have to evaluate the first argument twice as the compare may have been done with a different type than return value + @return + NULL if arguments are equal + @return + the first argument if not equal */ double @@ -2445,88 +2771,59 @@ Item_func_nullif::is_null() return (null_value= (!cmp.compare() ? 1 : args[0]->null_value)); } -/* - CASE expression - Return the matching ITEM or NULL if all compares (including else) failed + +/** + Find and return matching items for CASE or ELSE item if all compares + are failed or NULL if ELSE item isn't defined. + + IMPLEMENTATION + In order to do correct comparisons of the CASE expression (the expression + between CASE and the first WHEN) with each WHEN expression several + comparators are used. One for each result type. CASE expression can be + evaluated up to # of different result types are used. To check whether + the CASE expression already was evaluated for a particular result type + a bit mapped variable value_added_map is used. Result types are mapped + to it according to their int values i.e. STRING_RESULT is mapped to bit + 0, REAL_RESULT to bit 1, so on. + + @retval + NULL Nothing found and there is no ELSE expression defined + @retval + item Found item or ELSE item if defined and all comparisons are + failed */ Item *Item_func_case::find_item(String *str) { - String *UNINIT_VAR(first_expr_str), *tmp; - my_decimal *UNINIT_VAR(first_expr_dec), first_expr_dec_val; - longlong UNINIT_VAR(first_expr_int); - double UNINIT_VAR(first_expr_real); - char buff[MAX_FIELD_WIDTH]; - String buff_str(buff,sizeof(buff),default_charset()); + uint value_added_map= 0; - if (first_expr_num != -1) + if (first_expr_num == -1) { - switch (cmp_type) - { - case STRING_RESULT: - // We can't use 'str' here as this may be overwritten - if (!(first_expr_str= args[first_expr_num]->val_str(&buff_str))) - return else_expr_num != -1 ? args[else_expr_num] : 0; // Impossible - break; - case INT_RESULT: - first_expr_int= args[first_expr_num]->val_int(); - if (args[first_expr_num]->null_value) - return else_expr_num != -1 ? args[else_expr_num] : 0; - break; - case REAL_RESULT: - first_expr_real= args[first_expr_num]->val_real(); - if (args[first_expr_num]->null_value) - return else_expr_num != -1 ? args[else_expr_num] : 0; - break; - case DECIMAL_RESULT: - first_expr_dec= args[first_expr_num]->val_decimal(&first_expr_dec_val); - if (args[first_expr_num]->null_value) - return else_expr_num != -1 ? args[else_expr_num] : 0; - break; - case ROW_RESULT: - default: - // This case should never be chosen - DBUG_ASSERT(0); - break; - } - } - - // Compare every WHEN argument with it and return the first match - for (uint i=0 ; i < ncases ; i+=2) - { - if (first_expr_num == -1) + for (uint i=0 ; i < ncases ; i+=2) { // No expression between CASE and the first WHEN if (args[i]->val_bool()) return args[i+1]; continue; } - switch (cmp_type) { - case STRING_RESULT: - if ((tmp=args[i]->val_str(str))) // If not null - if (sortcmp(tmp,first_expr_str,cmp_collation.collation)==0) - return args[i+1]; - break; - case INT_RESULT: - if (args[i]->val_int()==first_expr_int && !args[i]->null_value) - return args[i+1]; - break; - case REAL_RESULT: - if (args[i]->val_real() == first_expr_real && !args[i]->null_value) - return args[i+1]; - break; - case DECIMAL_RESULT: + } + else + { + /* Compare every WHEN argument with it and return the first match */ + for (uint i=0 ; i < ncases ; i+=2) { - my_decimal value; - if (my_decimal_cmp(args[i]->val_decimal(&value), first_expr_dec) == 0) - return args[i+1]; - break; - } - case ROW_RESULT: - default: - // This case should never be chosen - DBUG_ASSERT(0); - break; + cmp_type= item_cmp_type(left_result_type, args[i]->result_type()); + DBUG_ASSERT(cmp_type != ROW_RESULT); + DBUG_ASSERT(cmp_items[(uint)cmp_type]); + if (!(value_added_map & (1<<(uint)cmp_type))) + { + cmp_items[(uint)cmp_type]->store_value(args[first_expr_num]); + if ((null_value=args[first_expr_num]->null_value)) + return else_expr_num != -1 ? args[else_expr_num] : 0; + value_added_map|= 1<<(uint)cmp_type; + } + if (!cmp_items[(uint)cmp_type]->cmp(args[i]) && !args[i]->null_value) + return args[i + 1]; } } // No, WHEN clauses all missed, return ELSE expression @@ -2616,7 +2913,7 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref) Item_func_case::val_int() -> Item_func_case::find_item() */ #ifndef EMBEDDED_LIBRARY - char buff[MAX_FIELD_WIDTH*2+sizeof(String)*2+sizeof(String*)*2+sizeof(double)*2+sizeof(longlong)*2]; + uchar buff[MAX_FIELD_WIDTH*2+sizeof(String)*2+sizeof(String*)*2+sizeof(double)*2+sizeof(longlong)*2]; #endif bool res= Item_func::fix_fields(thd, ref); /* @@ -2651,8 +2948,7 @@ void Item_func_case::fix_length_and_dec() { Item **agg; uint nagg; - THD *thd= current_thd; - + uint found_types= 0; if (!(agg= (Item**) sql_alloc(sizeof(Item*)*(ncases+1)))) return; @@ -2679,17 +2975,32 @@ void Item_func_case::fix_length_and_dec() */ if (first_expr_num != -1) { + uint i; agg[0]= args[first_expr_num]; + left_result_type= agg[0]->result_type(); + for (nagg= 0; nagg < ncases/2 ; nagg++) agg[nagg+1]= args[nagg*2]; nagg++; - if (agg_cmp_type(thd, &cmp_type, agg, nagg)) - return; - if ((cmp_type == STRING_RESULT) && - agg_arg_charsets(cmp_collation, agg, nagg, MY_COLL_CMP_CONV, 1)) + if (!(found_types= collect_cmp_types(agg, nagg))) return; + + for (i= 0; i <= (uint)DECIMAL_RESULT; i++) + { + if (found_types & (1 << i) && !cmp_items[i]) + { + DBUG_ASSERT((Item_result)i != ROW_RESULT); + if ((Item_result)i == STRING_RESULT && + agg_arg_charsets(cmp_collation, agg, nagg, MY_COLL_CMP_CONV, 1)) + return; + if (!(cmp_items[i]= + cmp_item::get_comparator((Item_result)i, + cmp_collation.collation))) + return; + } + } } - + if (else_expr_num == -1 || args[else_expr_num]->maybe_null) maybe_null=1; @@ -2709,8 +3020,9 @@ void Item_func_case::fix_length_and_dec() 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(max_length + decimals, decimals, - unsigned_flag); + max_length= my_decimal_precision_to_length_no_truncation(max_length + + decimals, decimals, + unsigned_flag); } } @@ -2727,34 +3039,52 @@ uint Item_func_case::decimal_precision() const } -/* TODO: Fix this so that it prints the whole CASE expression */ +/** + @todo + Fix this so that it prints the whole CASE expression +*/ -void Item_func_case::print(String *str) +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(str); + args[first_expr_num]->print(str, query_type); str->append(' '); } for (uint i=0 ; i < ncases ; i+=2) { str->append(STRING_WITH_LEN("when ")); - args[i]->print(str); + args[i]->print(str, query_type); str->append(STRING_WITH_LEN(" then ")); - args[i+1]->print(str); + args[i+1]->print(str, query_type); str->append(' '); } if (else_expr_num != -1) { str->append(STRING_WITH_LEN("else ")); - args[else_expr_num]->print(str); + args[else_expr_num]->print(str, query_type); str->append(' '); } str->append(STRING_WITH_LEN("end)")); } -/* + +void Item_func_case::cleanup() +{ + uint i; + DBUG_ENTER("Item_func_case::cleanup"); + Item_func::cleanup(); + for (i= 0; i <= (uint)DECIMAL_RESULT; i++) + { + delete cmp_items[i]; + cmp_items[i]= 0; + } + DBUG_VOID_RETURN; +} + + +/** Coalesce - return first not NULL argument. */ @@ -2967,7 +3297,7 @@ static int cmp_decimal(void *cmp_arg, my_decimal *a, my_decimal *b) int in_vector::find(Item *item) { - byte *result=get_value(item); + uchar *result=get_value(item); if (!result || !used_count) return 0; // Null value @@ -3025,9 +3355,9 @@ void in_string::set(uint pos,Item *item) } -byte *in_string::get_value(Item *item) +uchar *in_string::get_value(Item *item) { - return (byte*) item->val_str(&tmp); + return (uchar*) item->val_str(&tmp); } in_row::in_row(uint elements, Item * item) @@ -3049,12 +3379,12 @@ in_row::~in_row() delete [] (cmp_item_row*) base; } -byte *in_row::get_value(Item *item) +uchar *in_row::get_value(Item *item) { tmp.store_value(item); if (item->is_null()) return 0; - return (byte *)&tmp; + return (uchar *)&tmp; } void in_row::set(uint pos, Item *item) @@ -3077,26 +3407,26 @@ void in_longlong::set(uint pos,Item *item) buff->unsigned_flag= item->unsigned_flag; } -byte *in_longlong::get_value(Item *item) +uchar *in_longlong::get_value(Item *item) { tmp.val= item->val_int(); if (item->null_value) return 0; tmp.unsigned_flag= item->unsigned_flag; - return (byte*) &tmp; + return (uchar*) &tmp; } void in_datetime::set(uint pos,Item *item) { - Item **tmp= &item; + Item **tmp_item= &item; bool is_null; struct packed_longlong *buff= &((packed_longlong*) base)[pos]; - buff->val= get_datetime_value(thd, &tmp, 0, warn_item, &is_null); + buff->val= get_datetime_value(thd, &tmp_item, 0, warn_item, &is_null); buff->unsigned_flag= 1L; } -byte *in_datetime::get_value(Item *item) +uchar *in_datetime::get_value(Item *item) { bool is_null; Item **tmp_item= lval_cache ? &lval_cache : &item; @@ -3104,7 +3434,7 @@ byte *in_datetime::get_value(Item *item) if (item->null_value) return 0; tmp.unsigned_flag= 1L; - return (byte*) &tmp; + return (uchar*) &tmp; } in_double::in_double(uint elements) @@ -3116,12 +3446,12 @@ void in_double::set(uint pos,Item *item) ((double*) base)[pos]= item->val_real(); } -byte *in_double::get_value(Item *item) +uchar *in_double::get_value(Item *item) { tmp= item->val_real(); if (item->null_value) return 0; /* purecov: inspected */ - return (byte*) &tmp; + return (uchar*) &tmp; } @@ -3143,12 +3473,12 @@ void in_decimal::set(uint pos, Item *item) } -byte *in_decimal::get_value(Item *item) +uchar *in_decimal::get_value(Item *item) { my_decimal *result= item->val_decimal(&val); if (item->null_value) return 0; - return (byte *)result; + return (uchar *)result; } @@ -3376,32 +3706,31 @@ bool Item_func_in::nulls_in_row() } -/* - Perform context analysis of an IN item tree - - SYNOPSIS: - fix_fields() - thd reference to the global context of the query thread - tables list of all open tables involved in the query - ref pointer to Item* variable where pointer to resulting "fixed" - item is to be assigned +/** + Perform context analysis of an IN item tree. - DESCRIPTION This function performs context analysis (name resolution) and calculates various attributes of the item tree with Item_func_in as its root. The function saves in ref the pointer to the item or to a newly created item that is considered as a replacement for the original one. - NOTES + @param thd reference to the global context of the query thread + @param ref pointer to Item* variable where pointer to resulting "fixed" + item is to be assigned + + @note Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on a predicate/function level. Then it's easy to show that: + @verbatim T0(e IN(e1,...,en)) = union(T1(e),intersection(T1(ei))) T1(e IN(e1,...,en)) = union(T1(e),intersection(T1(ei))) T0(e NOT IN(e1,...,en)) = union(T1(e),union(T1(ei))) T1(e NOT IN(e1,...,en)) = union(T1(e),intersection(T1(ei))) + @endverbatim - RETURN + @retval 0 ok + @retval 1 got error */ @@ -3437,21 +3766,20 @@ static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y) void Item_func_in::fix_length_and_dec() { Item **arg, **arg_end; - uint const_itm= 1; + bool const_itm= 1; THD *thd= current_thd; bool datetime_found= FALSE; /* TRUE <=> arguments values will be compared as DATETIMEs. */ bool compare_as_datetime= FALSE; Item *date_arg= 0; - - if (agg_cmp_type(thd, &cmp_type, args, arg_count)) - return; - - if (cmp_type == STRING_RESULT && - agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV, 1)) + uint found_types= 0; + uint type_cnt= 0, i; + Item_result cmp_type= STRING_RESULT; + left_result_type= args[0]->result_type(); + if (!(found_types= collect_cmp_types(args, arg_count, true))) return; - - 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[0]->const_item()) { @@ -3459,92 +3787,111 @@ void Item_func_in::fix_length_and_dec() break; } } - /* - When comparing rows create the row comparator object beforehand to ease - the DATETIME comparison detection procedure. - */ - if (cmp_type == ROW_RESULT) + for (i= 0; i <= (uint)DECIMAL_RESULT; i++) { - cmp_item_row *cmp= 0; - if (const_itm && !nulls_in_row()) - { - array= new in_row(arg_count-1, 0); - cmp= &((in_row*)array)->tmp; - } - else + if (found_types & 1 << i) { - if (!(cmp= new cmp_item_row)) - return; - in_item= cmp; + (type_cnt)++; + cmp_type= (Item_result) i; } - cmp->n= args[0]->cols(); - cmp->alloc_comparators(); } - /* All DATE/DATETIME fields/functions has the STRING result type. */ - if (cmp_type == STRING_RESULT || cmp_type == ROW_RESULT) - { - uint col, cols= args[0]->cols(); - for (col= 0; col < cols; col++) + if (type_cnt == 1) + { + if (cmp_type == STRING_RESULT && + agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV, 1)) + return; + arg_types_compatible= TRUE; + } + if (type_cnt == 1) + { + /* + When comparing rows create the row comparator object beforehand to ease + the DATETIME comparison detection procedure. + */ + if (cmp_type == ROW_RESULT) { - bool skip_column= FALSE; - /* - Check that all items to be compared has the STRING result type and at - least one of them is a DATE/DATETIME item. - */ - for (arg= args, arg_end= args + arg_count; arg != arg_end ; arg++) + cmp_item_row *cmp= 0; + if (const_itm && !nulls_in_row()) { - Item *itm= ((cmp_type == STRING_RESULT) ? arg[0] : - arg[0]->element_index(col)); - if (itm->result_type() != STRING_RESULT) - { - skip_column= TRUE; - break; - } - else if (itm->is_datetime()) + array= new in_row(arg_count-1, 0); + cmp= &((in_row*)array)->tmp; + } + else + { + if (!(cmp= new cmp_item_row)) + return; + cmp_items[ROW_RESULT]= cmp; + } + cmp->n= args[0]->cols(); + cmp->alloc_comparators(); + } + /* All DATE/DATETIME fields/functions has the STRING result type. */ + if (cmp_type == STRING_RESULT || cmp_type == ROW_RESULT) + { + uint col, cols= args[0]->cols(); + + for (col= 0; col < cols; col++) + { + bool skip_column= FALSE; + /* + Check that all items to be compared has the STRING result type and at + least one of them is a DATE/DATETIME item. + */ + for (arg= args, arg_end= args + arg_count; arg != arg_end ; arg++) { - datetime_found= TRUE; - /* - Internally all DATE/DATETIME values are converted to the DATETIME - type. So try to find a DATETIME item to issue correct warnings. - */ - if (!date_arg) - date_arg= itm; - else if (itm->field_type() == MYSQL_TYPE_DATETIME) + Item *itm= ((cmp_type == STRING_RESULT) ? arg[0] : + arg[0]->element_index(col)); + if (itm->result_type() != STRING_RESULT) { - date_arg= itm; - /* All arguments are already checked to have the STRING result. */ - if (cmp_type == STRING_RESULT) - break; + skip_column= TRUE; + break; + } + else if (itm->is_datetime()) + { + datetime_found= TRUE; + /* + Internally all DATE/DATETIME values are converted to the DATETIME + type. So try to find a DATETIME item to issue correct warnings. + */ + if (!date_arg) + date_arg= itm; + else if (itm->field_type() == MYSQL_TYPE_DATETIME) + { + date_arg= itm; + /* All arguments are already checked to have the STRING result. */ + if (cmp_type == STRING_RESULT) + break; + } } } - } - if (skip_column) - continue; - if (datetime_found) - { - if (cmp_type == ROW_RESULT) + if (skip_column) + continue; + if (datetime_found) { - cmp_item **cmp= 0; - if (array) - cmp= ((in_row*)array)->tmp.comparators + col; + if (cmp_type == ROW_RESULT) + { + cmp_item **cmp= 0; + if (array) + cmp= ((in_row*)array)->tmp.comparators + col; + else + cmp= ((cmp_item_row*)cmp_items[ROW_RESULT])->comparators + col; + *cmp= new cmp_item_datetime(date_arg); + /* Reset variables for the next column. */ + date_arg= 0; + datetime_found= FALSE; + } else - cmp= ((cmp_item_row*)in_item)->comparators + col; - *cmp= new cmp_item_datetime(date_arg); - /* Reset variables for the next column. */ - date_arg= 0; - datetime_found= FALSE; + compare_as_datetime= TRUE; } - else - compare_as_datetime= TRUE; } } } /* - Row item with NULLs inside can return NULL or FALSE => + Row item with NULLs inside can return NULL or FALSE => they can't be processed as static */ - if (const_itm && !nulls_in_row()) + if (type_cnt == 1 && const_itm && !nulls_in_row()) { if (compare_as_datetime) array= new in_datetime(date_arg, arg_count - 1); @@ -3607,69 +3954,111 @@ void Item_func_in::fix_length_and_dec() uint j=0; for (uint i=1 ; i < arg_count ; i++) { - array->set(j,args[i]); if (!args[i]->null_value) // Skip NULL values + { + array->set(j,args[i]); j++; + } else have_null= 1; } - if ((array->used_count=j)) + if ((array->used_count= j)) array->sort(); } } else { - if (in_item) + if (compare_as_datetime) + cmp_items[STRING_RESULT]= new cmp_item_datetime(date_arg); + else { - /* - The row comparator was created at the beginning but only DATETIME - items comparators were initialized. Call store_value() to setup - others. - */ - in_item->store_value(args[0]); + for (i= 0; i <= (uint) DECIMAL_RESULT; i++) + { + if (found_types & (1 << i) && !cmp_items[i]) + { + if ((Item_result)i == STRING_RESULT && + agg_arg_charsets(cmp_collation, args, arg_count, + MY_COLL_CMP_CONV, 1)) + return; + if (!cmp_items[i] && !(cmp_items[i]= + cmp_item::get_comparator((Item_result)i, + cmp_collation.collation))) + return; + } + } } - else if (compare_as_datetime) - in_item= new cmp_item_datetime(date_arg); - else - in_item= cmp_item::get_comparator(cmp_type, cmp_collation.collation); - if (cmp_type == STRING_RESULT) - in_item->cmp_charset= cmp_collation.collation; } max_length= 1; } -void Item_func_in::print(String *str) +void Item_func_in::print(String *str, enum_query_type query_type) { str->append('('); - args[0]->print(str); + args[0]->print(str, query_type); if (negated) str->append(STRING_WITH_LEN(" not")); str->append(STRING_WITH_LEN(" in (")); - print_args(str, 1); + print_args(str, 1, query_type); str->append(STRING_WITH_LEN("))")); } +/* + Evaluate the function and return its value. + + SYNOPSIS + val_int() + + DESCRIPTION + Evaluate the function and return its value. + + IMPLEMENTATION + If the array object is defined then the value of the function is + calculated by means of this array. + Otherwise several cmp_item objects are used in order to do correct + comparison of left expression and an expression from the values list. + One cmp_item object correspond to one used comparison type. Left + expression can be evaluated up to number of different used comparison + types. A bit mapped variable value_added_map is used to check whether + the left expression already was evaluated for a particular result type. + Result types are mapped to it according to their integer values i.e. + STRING_RESULT is mapped to bit 0, REAL_RESULT to bit 1, so on. + + RETURN + Value of the function +*/ + longlong Item_func_in::val_int() { + cmp_item *in_item; DBUG_ASSERT(fixed == 1); + uint value_added_map= 0; if (array) { int tmp=array->find(args[0]); null_value=args[0]->null_value || (!tmp && have_null); return (longlong) (!null_value && tmp != negated); } - in_item->store_value(args[0]); - if ((null_value=args[0]->null_value)) - return 0; + have_null= 0; - for (uint i=1 ; i < arg_count ; i++) + for (uint i= 1 ; i < arg_count ; i++) { + Item_result cmp_type= item_cmp_type(left_result_type, args[i]->result_type()); + in_item= cmp_items[(uint)cmp_type]; + DBUG_ASSERT(in_item); + if (!(value_added_map & (1 << (uint)cmp_type))) + { + in_item->store_value(args[0]); + if ((null_value= args[0]->null_value)) + return 0; + value_added_map|= 1 << (uint)cmp_type; + } if (!in_item->cmp(args[i]) && !args[i]->null_value) return (longlong) (!negated); have_null|= args[i]->null_value; } + null_value= have_null; return (longlong) (!null_value && negated); } @@ -3740,7 +4129,7 @@ Item_cond::fix_fields(THD *thd, Item **ref) List_iterator<Item> li(list); Item *item; #ifndef EMBEDDED_LIBRARY - char buff[sizeof(char*)]; // Max local vars in function + uchar buff[sizeof(char*)]; // Max local vars in function #endif not_null_tables_cache= used_tables_cache= 0; const_item_cache= 1; @@ -3807,27 +4196,20 @@ Item_cond::fix_fields(THD *thd, Item **ref) return FALSE; } -bool Item_cond::walk(Item_processor processor, byte *arg) +bool Item_cond::walk(Item_processor processor, bool walk_subquery, uchar *arg) { List_iterator_fast<Item> li(list); Item *item; while ((item= li++)) - if (item->walk(processor, arg)) + if (item->walk(processor, walk_subquery, arg)) return 1; - return Item_func::walk(processor, arg); + return Item_func::walk(processor, walk_subquery, arg); } -/* - Transform an Item_cond object with a transformer callback function - - SYNOPSIS - transform() - transformer the transformer callback function to be applied to the nodes - of the tree of the object - arg parameter to be passed to the transformer +/** + Transform an Item_cond object with a transformer callback function. - DESCRIPTION The function recursively applies the transform method to each member item of the condition list. If the call of the method for a member item returns a new item @@ -3835,11 +4217,15 @@ bool Item_cond::walk(Item_processor processor, byte *arg) After this the transformer is applied to the root node of the Item_cond object. - RETURN VALUES + @param transformer the transformer callback function to be applied to + the nodes of the tree of the object + @param arg parameter to be passed to the transformer + + @return Item returned as the result of transformation of the root node */ -Item *Item_cond::transform(Item_transformer transformer, byte *arg) +Item *Item_cond::transform(Item_transformer transformer, uchar *arg) { DBUG_ASSERT(!current_thd->is_stmt_prepare()); @@ -3864,19 +4250,10 @@ Item *Item_cond::transform(Item_transformer transformer, byte *arg) } -/* - Compile Item_cond object with a processor and a transformer callback functions - - SYNOPSIS - compile() - analyzer the analyzer callback function to be applied to the nodes - of the tree of the object - arg_p in/out parameter to be passed to the analyzer - transformer the transformer callback function to be applied to the nodes - of the tree of the object - arg_t parameter to be passed to the transformer +/** + Compile Item_cond object with a processor and a transformer + callback functions. - DESCRIPTION First the function applies the analyzer to the root node of the Item_func object. Then if the analyzer succeeeds (returns TRUE) the function recursively applies the compile method to member @@ -3886,12 +4263,19 @@ Item *Item_cond::transform(Item_transformer transformer, byte *arg) After this the transformer is applied to the root node of the Item_cond object. - RETURN VALUES + @param analyzer the analyzer callback function to be applied to the + nodes of the tree of the object + @param[in,out] arg_p parameter to be passed to the analyzer + @param transformer the transformer callback function to be applied to the + nodes of the tree of the object + @param arg_t parameter to be passed to the transformer + + @return Item returned as the result of transformation of the root node */ -Item *Item_cond::compile(Item_analyzer analyzer, byte **arg_p, - Item_transformer transformer, byte *arg_t) +Item *Item_cond::compile(Item_analyzer analyzer, uchar **arg_p, + Item_transformer transformer, uchar *arg_t) { if (!(this->*analyzer)(arg_p)) return 0; @@ -3904,7 +4288,7 @@ Item *Item_cond::compile(Item_analyzer analyzer, byte **arg_p, The same parameter value of arg_p must be passed to analyze any argument of the condition formula. */ - byte *arg_v= *arg_p; + uchar *arg_v= *arg_p; Item *new_item= item->compile(analyzer, &arg_v, transformer, arg_t); if (new_item && new_item != item) current_thd->change_item_tree(li.ref(), new_item); @@ -3936,23 +4320,21 @@ void Item_cond::traverse_cond(Cond_traverser traverser, } } -/* - Move SUM items out from item tree and replace with reference +/** + Move SUM items out from item tree and replace with reference. - SYNOPSIS - split_sum_func() - thd Thread handler - ref_pointer_array Pointer to array of reference fields - fields All fields in select - - NOTES - This function is run on all expression (SELECT list, WHERE, HAVING etc) - that have or refer (HAVING) to a SUM expression. - - The split is done to get an unique item for each SUM function - so that we can easily find and calculate them. - (Calculation done by update_sum_func() and copy_sum_funcs() in - sql_select.cc) + The split is done to get an unique item for each SUM function + so that we can easily find and calculate them. + (Calculation done by update_sum_func() and copy_sum_funcs() in + sql_select.cc) + + @param thd Thread handler + @param ref_pointer_array Pointer to array of reference fields + @param fields All fields in select + + @note + This function is run on all expression (SELECT list, WHERE, HAVING etc) + that have or refer (HAVING) to a SUM expression. */ void Item_cond::split_sum_func(THD *thd, Item **ref_pointer_array, @@ -3988,19 +4370,19 @@ void Item_cond::update_used_tables() } -void Item_cond::print(String *str) +void Item_cond::print(String *str, enum_query_type query_type) { str->append('('); List_iterator_fast<Item> li(list); Item *item; if ((item=li++)) - item->print(str); + item->print(str, query_type); while ((item=li++)) { str->append(' '); str->append(func_name()); str->append(' '); - item->print(str); + item->print(str, query_type); } str->append(')'); } @@ -4023,20 +4405,22 @@ void Item_cond::neg_arguments(THD *thd) } -/* - Evaluation of AND(expr, expr, expr ...) +/** + Evaluation of AND(expr, expr, expr ...). - NOTES: + @note abort_if_null is set for AND expressions for which we don't care if the result is NULL or 0. This is set for: - WHERE clause - HAVING clause - IF(expression) - RETURN VALUES + @retval 1 If all expressions are true + @retval 0 If all expressions are false or if we find a NULL expression and 'abort_on_null' is set. + @retval NULL if all expression are either 1 or NULL */ @@ -4078,24 +4462,23 @@ longlong Item_cond_or::val_int() return 0; } -/* - Create an AND expression from two expressions +/** + Create an AND expression from two expressions. - SYNOPSIS - and_expressions() - a expression or NULL - b expression. - org_item Don't modify a if a == *org_item - If a == NULL, org_item is set to point at b, - to ensure that future calls will not modify b. - - NOTES + @param a expression or NULL + @param b expression. + @param org_item Don't modify a if a == *org_item. + If a == NULL, org_item is set to point at b, + to ensure that future calls will not modify b. + + @note This will not modify item pointed to by org_item or b The idea is that one can call this in a loop and create and 'and' over all items without modifying any of the original items. - RETURN + @retval NULL Error + @retval Item */ @@ -4140,7 +4523,7 @@ longlong Item_is_not_null_test::val_int() if (!used_tables_cache && !with_subselect) { owner->was_null|= (!cached_value); - DBUG_PRINT("info", ("cached :%ld", (long) cached_value)); + DBUG_PRINT("info", ("cached: %ld", (long) cached_value)); DBUG_RETURN(cached_value); } if (args[0]->is_null()) @@ -4153,7 +4536,9 @@ longlong Item_is_not_null_test::val_int() DBUG_RETURN(1); } -/* Optimize case of not_null_column IS NULL */ +/** + Optimize case of not_null_column IS NULL. +*/ void Item_is_not_null_test::update_used_tables() { if (!args[0]->maybe_null) @@ -4180,10 +4565,10 @@ longlong Item_func_isnotnull::val_int() } -void Item_func_isnotnull::print(String *str) +void Item_func_isnotnull::print(String *str, enum_query_type query_type) { str->append('('); - args[0]->print(str); + args[0]->print(str, query_type); str->append(STRING_WITH_LEN(" is not null)")); } @@ -4191,13 +4576,13 @@ void Item_func_isnotnull::print(String *str) longlong Item_func_like::val_int() { DBUG_ASSERT(fixed == 1); - String* res = args[0]->val_str(&tmp_value1); + String* res = args[0]->val_str(&cmp.value1); if (args[0]->null_value) { null_value=1; return 0; } - String* res2 = args[1]->val_str(&tmp_value2); + String* res2 = args[1]->val_str(&cmp.value2); if (args[1]->null_value) { null_value=1; @@ -4213,13 +4598,15 @@ longlong Item_func_like::val_int() } -/* We can optimize a where if first character isn't a wildcard */ +/** + We can optimize a where if first character isn't a wildcard +*/ Item_func::optimize_type Item_func_like::select_optimize() const { if (args[1]->const_item()) { - String* res2= args[1]->val_str((String *)&tmp_value2); + String* res2= args[1]->val_str((String *)&cmp.value2); if (!res2) return OPTIMIZE_NONE; @@ -4250,7 +4637,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) if (escape_item->const_item()) { /* If we are on execution stage */ - String *escape_str= escape_item->val_str(&tmp_value1); + String *escape_str= escape_item->val_str(&cmp.value1); if (escape_str) { if (escape_used_in_parsing && ( @@ -4305,7 +4692,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) if (args[1]->const_item() && !use_strnxfrm(collation.collation) && !(specialflag & SPECIAL_NO_NEW_FUNC)) { - String* res2 = args[1]->val_str(&tmp_value2); + String* res2 = args[1]->val_str(&cmp.value2); if (!res2) return FALSE; // Null argument @@ -4497,6 +4884,7 @@ void Item_func_regex::cleanup() { my_regfree(&preg); regex_compiled=0; + prev_regexp.length(0); } DBUG_VOID_RETURN; } @@ -4512,10 +4900,9 @@ void Item_func_regex::cleanup() #endif -/********************************************************************** - turboBM_compute_suffixes() +/** Precomputation dependent only on pattern_len. -**********************************************************************/ +*/ void Item_func_like::turboBM_compute_suffixes(int *suff) { @@ -4569,10 +4956,9 @@ void Item_func_like::turboBM_compute_suffixes(int *suff) } -/********************************************************************** - turboBM_compute_good_suffix_shifts() - Precomputation dependent only on pattern_len. -**********************************************************************/ +/** + Precomputation dependent only on pattern_len. +*/ void Item_func_like::turboBM_compute_good_suffix_shifts(int *suff) { @@ -4614,10 +5000,9 @@ void Item_func_like::turboBM_compute_good_suffix_shifts(int *suff) } -/********************************************************************** - turboBM_compute_bad_character_shifts() +/** Precomputation dependent on pattern_len. -**********************************************************************/ +*/ void Item_func_like::turboBM_compute_bad_character_shifts() { @@ -4643,10 +5028,12 @@ void Item_func_like::turboBM_compute_bad_character_shifts() } -/********************************************************************** - turboBM_matches() - Search for pattern in text, returns true/false for match/no match -**********************************************************************/ +/** + Search for pattern in text. + + @return + returns true/false for match/no match +*/ bool Item_func_like::turboBM_matches(const char* text, int text_len) const { @@ -4726,24 +5113,20 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const } -/* +/** Make a logical XOR of the arguments. - SYNOPSIS - val_int() - - DESCRIPTION If either operator is NULL, return NULL. - NOTE - As we don't do any index optimization on XOR this is not going to be - very fast to use. - - TODO (low priority) - Change this to be optimized as: - A XOR B -> (A) == 1 AND (B) <> 1) OR (A <> 1 AND (B) == 1) + @todo + (low priority) Change this to be optimized as: @n + A XOR B -> (A) == 1 AND (B) <> 1) OR (A <> 1 AND (B) == 1) @n To be able to do this, we would however first have to extend the MySQL range optimizer to handle OR better. + + @note + As we don't do any index optimization on XOR this is not going to be + very fast to use. */ longlong Item_cond_xor::val_int() @@ -4765,15 +5148,12 @@ longlong Item_cond_xor::val_int() return (longlong) result; } -/* +/** Apply NOT transformation to the item and return a new one. - SYNOPSIS - neg_transformer() - thd thread handler - DESCRIPTION Transform the item using next rules: + @verbatim a AND b AND ... -> NOT(a) OR NOT(b) OR ... a OR b OR ... -> NOT(a) AND NOT(b) AND ... NOT(a) -> a @@ -4785,8 +5165,11 @@ longlong Item_cond_xor::val_int() a <= b -> a > b IS NULL(a) -> IS NOT NULL(a) IS NOT NULL(a) -> IS NULL(a) + @endverbatim - RETURN + @param thd thread handler + + @return New item or NULL if we cannot apply NOT transformation (see Item::neg_transformer()). */ @@ -4804,7 +5187,9 @@ Item *Item_bool_rowready_func2::neg_transformer(THD *thd) } -/* a IS NULL -> a IS NOT NULL */ +/** + a IS NULL -> a IS NOT NULL. +*/ Item *Item_func_isnull::neg_transformer(THD *thd) { Item *item= new Item_func_isnotnull(args[0]); @@ -4812,7 +5197,9 @@ Item *Item_func_isnull::neg_transformer(THD *thd) } -/* a IS NOT NULL -> a IS NULL */ +/** + a IS NOT NULL -> a IS NULL. +*/ Item *Item_func_isnotnull::neg_transformer(THD *thd) { Item *item= new Item_func_isnull(args[0]); @@ -4895,7 +5282,9 @@ Item *Item_func_le::negated_item() /* a <= b -> a > b */ return new Item_func_gt(args[0], args[1]); } -// just fake method, should never be called +/** + just fake method, should never be called. +*/ Item *Item_bool_rowready_func2::negated_item() { DBUG_ASSERT(0); @@ -4960,18 +5349,16 @@ uint Item_equal::members() } -/* - Check whether a field is referred in the multiple equality +/** + Check whether a field is referred in the multiple equality. - SYNOPSIS - contains() - field field whose occurrence is to be checked - - DESCRIPTION - The function checks whether field is occurred in the Item_equal object - - RETURN VALUES + The function checks whether field is occurred in the Item_equal object . + + @param field field whose occurrence is to be checked + + @retval 1 if nultiple equality contains a reference to field + @retval 0 otherwise */ @@ -4988,22 +5375,15 @@ bool Item_equal::contains(Field *field) } -/* - Join members of another Item_equal object - - SYNOPSIS - merge() - item multiple equality whose members are to be joined +/** + Join members of another Item_equal object. - DESCRIPTION The function actually merges two multiple equalities. After this operation the Item_equal object additionally contains the field items of another item of the type Item_equal. If the optional constant items are not equal the cond_false flag is set to 1. - - RETURN VALUES - none + @param item multiple equality whose members are to be joined */ void Item_equal::merge(Item_equal *item) @@ -5023,28 +5403,21 @@ void Item_equal::merge(Item_equal *item) } -/* - Order field items in multiple equality according to a sorting criteria +/** + Order field items in multiple equality according to a sorting criteria. - SYNOPSIS - sort() - cmp function to compare field item - arg context extra parameter for the cmp function - - DESCRIPTION - The function perform ordering of the field items in the Item_equal - object according to the criteria determined by the cmp callback parameter. - If cmp(item_field1,item_field2,arg)<0 than item_field1 must be - placed after item_fiel2. + The function perform ordering of the field items in the Item_equal + object according to the criteria determined by the cmp callback parameter. + If cmp(item_field1,item_field2,arg)<0 than item_field1 must be + placed after item_fiel2. - IMPLEMENTATION - The function sorts field items by the exchange sort algorithm. - The list of field items is looked through and whenever two neighboring - members follow in a wrong order they are swapped. This is performed - again and again until we get all members in a right order. - - RETURN VALUES - None + The function sorts field items by the exchange sort algorithm. + The list of field items is looked through and whenever two neighboring + members follow in a wrong order they are swapped. This is performed + again and again until we get all members in a right order. + + @param cmp function to compare field item + @param arg context extra parameter for the cmp function */ void Item_equal::sort(Item_field_cmpfunc cmp, void *arg) @@ -5079,21 +5452,14 @@ void Item_equal::sort(Item_field_cmpfunc cmp, void *arg) } -/* - Check appearance of new constant items in the multiple equality object +/** + Check appearance of new constant items in the multiple equality object. - SYNOPSIS - update_const() - - DESCRIPTION - The function checks appearance of new constant items among - the members of multiple equalities. Each new constant item is - compared with the designated constant item if there is any in the - multiple equality. If there is none the first new constant item - becomes designated. - - RETURN VALUES - none + The function checks appearance of new constant items among + the members of multiple equalities. Each new constant item is + compared with the designated constant item if there is any in the + multiple equality. If there is none the first new constant item + becomes designated. */ void Item_equal::update_const() @@ -5174,17 +5540,19 @@ void Item_equal::fix_length_and_dec() item->collation.collation); } -bool Item_equal::walk(Item_processor processor, byte *arg) +bool Item_equal::walk(Item_processor processor, bool walk_subquery, uchar *arg) { List_iterator_fast<Item_field> it(fields); Item *item; while ((item= it++)) - if (item->walk(processor, arg)) + { + if (item->walk(processor, walk_subquery, arg)) return 1; - return Item_func::walk(processor, arg); + } + return Item_func::walk(processor, walk_subquery, arg); } -Item *Item_equal::transform(Item_transformer transformer, byte *arg) +Item *Item_equal::transform(Item_transformer transformer, uchar *arg) { DBUG_ASSERT(!current_thd->is_stmt_prepare()); @@ -5208,24 +5576,24 @@ Item *Item_equal::transform(Item_transformer transformer, byte *arg) return Item_func::transform(transformer, arg); } -void Item_equal::print(String *str) +void Item_equal::print(String *str, enum_query_type query_type) { str->append(func_name()); str->append('('); List_iterator_fast<Item_field> it(fields); Item *item; if (const_item) - const_item->print(str); + const_item->print(str, query_type); else { item= it++; - item->print(str); + item->print(str, query_type); } while ((item= it++)) { str->append(','); str->append(' '); - item->print(str); + item->print(str, query_type); } str->append(')'); } |