diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 9ffb056b2a2..879115d9b3b 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1126,36 +1126,11 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) if ((!item->fixed && item->fix_fields(thd, args)) || (item= args[0])->check_cols(1)) return TRUE; - Type_std_attributes::set(args[0]); with_subselect= args[0]->with_subselect; - Item *item2= item->real_item(); - if (item2->type() == Item::FIELD_ITEM) - set_handler_by_field_type(((Item_field*) item2)->field->type()); - else if (item->cmp_type() == TIME_RESULT) - set_handler_by_field_type(item2->field_type()); - else - set_handler_by_result_type(item2->result_type(), - max_length, collation.collation); - - switch (Item_sum_hybrid::result_type()) { - case INT_RESULT: - case DECIMAL_RESULT: - case STRING_RESULT: - break; - case REAL_RESULT: - max_length= float_length(decimals); - break; - case ROW_RESULT: - case TIME_RESULT: - DBUG_ASSERT(0); - }; - setup_hybrid(thd, args[0], NULL); - /* MIN/MAX can return NULL for empty set indepedent of the used column */ - maybe_null= 1; - result_field=0; - null_value=1; fix_length_and_dec(); + setup_hybrid(thd, args[0], NULL); + result_field=0; if (check_sum_func(thd, ref)) return TRUE; @@ -1166,6 +1141,14 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) } +void Item_sum_hybrid::fix_length_and_dec() +{ + DBUG_ASSERT(args[0]->field_type() == args[0]->real_item()->field_type()); + DBUG_ASSERT(args[0]->result_type() == args[0]->real_item()->result_type()); + (void) args[0]->type_handler()->Item_sum_hybrid_fix_length_and_dec(this); +} + + /** MIN/MAX function setup. @@ -1201,7 +1184,6 @@ void Item_sum_hybrid::setup_hybrid(THD *thd, Item *item, Item *value_arg) cmp= new Arg_comparator(); if (cmp) cmp->set_cmp_func(this, (Item**)&arg_cache, (Item**)&value, FALSE); - collation.set(item->collation); } diff --git a/sql/item_sum.h b/sql/item_sum.h index 76cc983ad9e..59d75871354 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -1006,6 +1006,7 @@ protected: cmp_sign(item->cmp_sign), was_values(item->was_values) { } bool fix_fields(THD *, Item **); + void fix_length_and_dec(); void setup_hybrid(THD *thd, Item *item, Item *value_arg); void clear(); double val_real(); diff --git a/sql/sql_type.cc b/sql/sql_type.cc index f94e18d065a..12fd73d4336 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -761,3 +761,101 @@ Type_handler_temporal_result::Item_get_cache(THD *thd, const Item *item) const /*************************************************************************/ +/** + MAX/MIN for the traditional numeric types preserve the exact data type + from Fields, but do not preserve the exact type from Items: + MAX(float_field) -> FLOAT + MAX(smallint_field) -> LONGLONG + MAX(COALESCE(float_field)) -> DOUBLE + MAX(COALESCE(smallint_field)) -> LONGLONG + QQ: Items should probably be fixed to preserve the exact type. +*/ +bool Type_handler_numeric:: + Item_sum_hybrid_fix_length_and_dec_numeric(Item_sum_hybrid *func, + const Type_handler *handler) + const +{ + Item *item= func->arguments()[0]; + Item *item2= item->real_item(); + func->Type_std_attributes::set(item); + /* MIN/MAX can return NULL for empty set indepedent of the used column */ + func->maybe_null= func->null_value= true; + if (item2->type() == Item::FIELD_ITEM) + func->set_handler_by_field_type(item2->field_type()); + else + func->set_handler(handler); + return false; +} + + +bool Type_handler_int_result:: + Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const +{ + return Item_sum_hybrid_fix_length_and_dec_numeric(func, + &type_handler_longlong); +} + + +bool Type_handler_real_result:: + Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const +{ + (void) Item_sum_hybrid_fix_length_and_dec_numeric(func, + &type_handler_double); + func->max_length= func->float_length(func->decimals); + return false; +} + + +bool Type_handler_decimal_result:: + Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const +{ + return Item_sum_hybrid_fix_length_and_dec_numeric(func, + &type_handler_newdecimal); +} + + +/** + MAX(str_field) converts ENUM/SET to CHAR, and preserve all other types + for Fields. + QQ: This works differently from UNION, which preserve the exact data + type for ENUM/SET if the joined ENUM/SET fields are equally defined. + Perhaps should be fixed. + MAX(str_item) chooses the best suitable string type. +*/ +bool Type_handler_string_result:: + Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const +{ + Item *item= func->arguments()[0]; + Item *item2= item->real_item(); + func->Type_std_attributes::set(item); + func->maybe_null= func->null_value= true; + if (item2->type() == Item::FIELD_ITEM) + { + // Fields: convert ENUM/SET to CHAR, preserve the type otherwise. + func->set_handler_by_field_type(item->field_type()); + } + else + { + // Items: choose VARCHAR/BLOB/MEDIUMBLOB/LONGBLOB, depending on length. + func->set_handler(type_handler_varchar. + type_handler_adjusted_to_max_octet_length(func->max_length, + func->collation.collation)); + } + return false; +} + + +/** + Traditional temporal types always preserve the type of the argument. +*/ +bool Type_handler_temporal_result:: + Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const +{ + Item *item= func->arguments()[0]; + func->Type_std_attributes::set(item); + func->maybe_null= func->null_value= true; + func->set_handler(item->type_handler()); + return false; +} + +/*************************************************************************/ diff --git a/sql/sql_type.h b/sql/sql_type.h index 87cd5c4c101..ef85f3d8d75 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -26,6 +26,7 @@ class Field; class Item; class Item_cache; +class Item_sum_hybrid; class Type_std_attributes; class Sort_param; class Arg_comparator; @@ -289,6 +290,7 @@ public: bool no_conversions) const= 0; virtual Item_cache *Item_get_cache(THD *thd, const Item *item) const= 0; virtual bool set_comparator_func(Arg_comparator *cmp) const= 0; + virtual bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const= 0; }; @@ -342,12 +344,31 @@ public: } Item_cache *Item_get_cache(THD *thd, const Item *item) const; bool set_comparator_func(Arg_comparator *cmp) const; + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const + { + DBUG_ASSERT(0); + return true; + } +}; + + +/* + A common parent class for numeric data type handlers +*/ +class Type_handler_numeric: public Type_handler +{ +protected: + bool Item_sum_hybrid_fix_length_and_dec_numeric(Item_sum_hybrid *func, + const Type_handler *handler) + const; +public: + virtual ~Type_handler_numeric() { } }; /*** Abstract classes for every XXX_RESULT */ -class Type_handler_real_result: public Type_handler +class Type_handler_real_result: public Type_handler_numeric { public: Item_result result_type() const { return REAL_RESULT; } @@ -361,10 +382,11 @@ public: int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; bool set_comparator_func(Arg_comparator *cmp) const; + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; }; -class Type_handler_decimal_result: public Type_handler +class Type_handler_decimal_result: public Type_handler_numeric { public: Item_result result_type() const { return DECIMAL_RESULT; } @@ -379,10 +401,11 @@ public: int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; bool set_comparator_func(Arg_comparator *cmp) const; + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; }; -class Type_handler_int_result: public Type_handler +class Type_handler_int_result: public Type_handler_numeric { public: Item_result result_type() const { return INT_RESULT; } @@ -397,6 +420,7 @@ public: int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; bool set_comparator_func(Arg_comparator *cmp) const; + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; }; @@ -413,6 +437,7 @@ public: SORT_FIELD_ATTR *attr) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; bool set_comparator_func(Arg_comparator *cmp) const; + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; }; @@ -433,6 +458,7 @@ public: int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; bool set_comparator_func(Arg_comparator *cmp) const; + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; };