From f3ff8b35299855b9e89696c73bcdd9945a4311fc Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 3 Dec 2015 10:10:59 +0400 Subject: [PATCH] MDEV-9215 Detect cmp_type() and result_type() from field_type() Part6: Deriving Item_type_holder from Type_handler_hybrid_real_field_type --- sql/item.cc | 78 ++++++++++++++++++++++++----------------- sql/item.h | 27 +++++++++++--- sql/sql_type.cc | 91 ++++++++++++++++++++++++++++++++++++++++++------ sql/sql_type.h | 81 ++++++++++++++++++++++++++++++++++++++++++ sql/sql_union.cc | 2 +- 5 files changed, 233 insertions(+), 46 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index a7245313097..18a047d123a 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2977,9 +2977,9 @@ default_set_param_func(Item_param *param, Item_param::Item_param(THD *thd, uint pos_in_query_arg): - Type_handler_hybrid_field_type(MYSQL_TYPE_VARCHAR), Item_basic_value(thd), Rewritable_query_parameter(pos_in_query_arg, 1), + Type_handler_hybrid_field_type(MYSQL_TYPE_VARCHAR), state(NO_VALUE), /* Don't pretend to be a literal unless value for this item is set. */ item_type(PARAM_ITEM), @@ -9188,14 +9188,28 @@ void Item_cache_row::set_null() Item_type_holder::Item_type_holder(THD *thd, Item *item) - :Item(thd, item), enum_set_typelib(0), fld_type(get_real_type(item)) + :Item(thd, item), + Type_handler_hybrid_real_field_type(get_real_type(item)), + enum_set_typelib(0) { DBUG_ASSERT(item->fixed); maybe_null= item->maybe_null; collation.set(item->collation); get_full_info(item); + /** + Field::result_merge_type(real_field_type()) should be equal to + result_type(), with one exception when "this" is a Item_field for + a BIT field: + - Field_bit::result_type() returns INT_RESULT, so does its Item_field. + - Field::result_merge_type(MYSQL_TYPE_BIT) returns STRING_RESULT. + Perhaps we need a new method in Type_handler to cover these type + merging rules for UNION. + */ + DBUG_ASSERT(real_field_type() == MYSQL_TYPE_BIT || + Item_type_holder::result_type() == + Field::result_merge_type(Item_type_holder::real_field_type())); /* fix variable decimals which always is NOT_FIXED_DEC */ - if (Field::result_merge_type(fld_type) == INT_RESULT) + if (Field::result_merge_type(real_field_type()) == INT_RESULT) decimals= 0; prev_decimal_int_part= item->decimal_int_part(); #ifdef HAVE_SPATIAL @@ -9205,19 +9219,6 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item) } -/** - Return expression type of Item_type_holder. - - @return - Item_result (type of internal MySQL expression result) -*/ - -Item_result Item_type_holder::result_type() const -{ - return Field::result_merge_type(fld_type); -} - - /** Find real field type of item. @@ -9268,7 +9269,7 @@ enum_field_types Item_type_holder::get_real_type(Item *item) */ switch (item->result_type()) { case STRING_RESULT: - return MYSQL_TYPE_VAR_STRING; + return MYSQL_TYPE_VARCHAR; case INT_RESULT: return MYSQL_TYPE_LONGLONG; case REAL_RESULT: @@ -9278,10 +9279,21 @@ enum_field_types Item_type_holder::get_real_type(Item *item) case ROW_RESULT: case TIME_RESULT: DBUG_ASSERT(0); - return MYSQL_TYPE_VAR_STRING; + return MYSQL_TYPE_VARCHAR; } } break; + case TYPE_HOLDER: + /* + Item_type_holder and Item_blob should not appear in this context. + In case they for some reasons do, returning field_type() is wrong anyway. + They must return Item_type_holder::real_field_type() instead, to make + the code in sql_type.cc and sql_type.h happy, as it expectes + Field::real_type()-compatible rather than Field::field_type()-compatible + valies in some places, and may in the future add some asserts preventing + use of field_type() instead of real_type() and the other way around. + */ + DBUG_ASSERT(0); default: break; } @@ -9307,25 +9319,26 @@ bool Item_type_holder::join_types(THD *thd, Item *item) uint decimals_orig= decimals; DBUG_ENTER("Item_type_holder::join_types"); DBUG_PRINT("info:", ("was type %d len %d, dec %d name %s", - fld_type, max_length, decimals, + real_field_type(), max_length, decimals, (name ? name : ""))); DBUG_PRINT("info:", ("in type %d len %d, dec %d", get_real_type(item), item->max_length, item->decimals)); - fld_type= Field::field_type_merge(fld_type, get_real_type(item)); + set_handler_by_real_type(Field::field_type_merge(real_field_type(), + get_real_type(item))); { uint item_decimals= item->decimals; /* fix variable decimals which always is NOT_FIXED_DEC */ - if (Field::result_merge_type(fld_type) == INT_RESULT) + if (Field::result_merge_type(real_field_type()) == INT_RESULT) item_decimals= 0; decimals= MY_MAX(decimals, item_decimals); } - if (fld_type == FIELD_TYPE_GEOMETRY) + if (Item_type_holder::field_type() == FIELD_TYPE_GEOMETRY) geometry_type= Field_geom::geometry_type_merge(geometry_type, item->get_geometry_type()); - if (Field::result_merge_type(fld_type) == DECIMAL_RESULT) + if (Field::result_merge_type(real_field_type()) == DECIMAL_RESULT) { decimals= MY_MIN(MY_MAX(decimals, item->decimals), DECIMAL_MAX_SCALE); int item_int_part= item->decimal_int_part(); @@ -9337,7 +9350,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item) unsigned_flag); } - switch (Field::result_merge_type(fld_type)) + switch (Field::result_merge_type(real_field_type())) { case STRING_RESULT: { @@ -9384,12 +9397,14 @@ bool Item_type_holder::join_types(THD *thd, Item *item) int delta1= max_length_orig - decimals_orig; int delta2= item->max_length - item->decimals; max_length= MY_MAX(delta1, delta2) + decimals; - if (fld_type == MYSQL_TYPE_FLOAT && max_length > FLT_DIG + 2) + if (Item_type_holder::real_field_type() == MYSQL_TYPE_FLOAT && + max_length > FLT_DIG + 2) { max_length= MAX_FLOAT_STR_LENGTH; decimals= NOT_FIXED_DEC; } - else if (fld_type == MYSQL_TYPE_DOUBLE && max_length > DBL_DIG + 2) + else if (Item_type_holder::real_field_type() == MYSQL_TYPE_DOUBLE && + max_length > DBL_DIG + 2) { max_length= MAX_DOUBLE_STR_LENGTH; decimals= NOT_FIXED_DEC; @@ -9397,7 +9412,8 @@ bool Item_type_holder::join_types(THD *thd, Item *item) } } else - max_length= (fld_type == MYSQL_TYPE_FLOAT) ? FLT_DIG+6 : DBL_DIG+7; + max_length= (Item_type_holder::field_type() == MYSQL_TYPE_FLOAT) ? + FLT_DIG+6 : DBL_DIG+7; break; } default: @@ -9409,7 +9425,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item) /* Remember decimal integer part to be used in DECIMAL_RESULT handleng */ prev_decimal_int_part= decimal_int_part(); DBUG_PRINT("info", ("become type: %d len: %u dec: %u", - (int) fld_type, max_length, (uint) decimals)); + (int) real_field_type(), max_length, (uint) decimals)); DBUG_RETURN(FALSE); } @@ -9490,7 +9506,7 @@ Field *Item_type_holder::make_field_by_type(TABLE *table) uchar *null_ptr= maybe_null ? (uchar*) "" : 0; Field *field; - switch (fld_type) { + switch (Item_type_holder::real_field_type()) { case MYSQL_TYPE_ENUM: DBUG_ASSERT(enum_set_typelib); field= new Field_enum((uchar *) 0, max_length, null_ptr, 0, @@ -9526,8 +9542,8 @@ Field *Item_type_holder::make_field_by_type(TABLE *table) */ void Item_type_holder::get_full_info(Item *item) { - if (fld_type == MYSQL_TYPE_ENUM || - fld_type == MYSQL_TYPE_SET) + if (Item_type_holder::real_field_type() == MYSQL_TYPE_ENUM || + Item_type_holder::real_field_type() == MYSQL_TYPE_SET) { if (item->type() == Item::SUM_FUNC_ITEM && (((Item_sum*)item)->sum_func() == Item_sum::MAX_FUNC || diff --git a/sql/item.h b/sql/item.h index 9432310290d..4feeca5affa 100644 --- a/sql/item.h +++ b/sql/item.h @@ -5265,11 +5265,11 @@ public: Item_type_holder do not need cleanup() because its time of live limited by single SP/PS execution. */ -class Item_type_holder: public Item +class Item_type_holder: public Item, + public Type_handler_hybrid_real_field_type { protected: TYPELIB *enum_set_typelib; - enum_field_types fld_type; Field::geometry_type geometry_type; void get_full_info(Item *item); @@ -5279,8 +5279,27 @@ protected: public: Item_type_holder(THD*, Item*); - Item_result result_type() const; - enum_field_types field_type() const { return fld_type; }; + enum_field_types field_type() const + { return Type_handler_hybrid_real_field_type::field_type(); } + enum_field_types real_field_type() const + { return Type_handler_hybrid_real_field_type::real_field_type(); } + enum Item_result result_type () const + { + /* + In 10.1 Item_type_holder::result_type() returned + Field::result_merge_type(field_type()), which returned STRING_RESULT + for the BIT data type. In 10.2 it returns INT_RESULT, similar + to what Field_bit::result_type() does. This should not be + important because Item_type_holder is a limited purpose Item + and its result_type() should not be called from outside of + Item_type_holder. It's called only internally from decimal_int_part() + from join_types(), to calculate "decimals" of the result data type. + As soon as we get BIT as one of the joined types, the result field + type cannot be numeric: it's either BIT, or VARBINARY. + */ + return Type_handler_hybrid_real_field_type::result_type(); + } + enum Type type() const { return TYPE_HOLDER; } double val_real(); longlong val_int(); diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 6d1de5bbabc..113a242b01e 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -27,9 +27,13 @@ static Type_handler_bit type_handler_bit; static Type_handler_float type_handler_float; static Type_handler_double type_handler_double; static Type_handler_time type_handler_time; +static Type_handler_time2 type_handler_time2; static Type_handler_date type_handler_date; +static Type_handler_newdate type_handler_newdate; static Type_handler_datetime type_handler_datetime; +static Type_handler_datetime2 type_handler_datetime2; static Type_handler_timestamp type_handler_timestamp; +static Type_handler_timestamp2 type_handler_timestamp2; static Type_handler_olddecimal type_handler_olddecimal; static Type_handler_newdecimal type_handler_newdecimal; static Type_handler_null type_handler_null; @@ -39,7 +43,11 @@ static Type_handler_tiny_blob type_handler_tiny_blob; static Type_handler_medium_blob type_handler_medium_blob; static Type_handler_long_blob type_handler_long_blob; static Type_handler_blob type_handler_blob; +#ifdef HAVE_SPATIAL static Type_handler_geometry type_handler_geometry; +#endif +static Type_handler_enum type_handler_enum; +static Type_handler_set type_handler_set; /** @@ -126,7 +134,7 @@ Type_handler::get_handler_by_field_type(enum_field_types type) case MYSQL_TYPE_FLOAT: return &type_handler_float; case MYSQL_TYPE_DOUBLE: return &type_handler_double; case MYSQL_TYPE_NULL: return &type_handler_null; - case MYSQL_TYPE_VARCHAR: return &type_handler_varchar; + case MYSQL_TYPE_VARCHAR: return &type_handler_varchar; case MYSQL_TYPE_TINY_BLOB: return &type_handler_tiny_blob; case MYSQL_TYPE_MEDIUM_BLOB: return &type_handler_medium_blob; case MYSQL_TYPE_LONG_BLOB: return &type_handler_long_blob; @@ -135,15 +143,78 @@ Type_handler::get_handler_by_field_type(enum_field_types type) case MYSQL_TYPE_STRING: return &type_handler_string; case MYSQL_TYPE_ENUM: return &type_handler_varchar; // Map to VARCHAR case MYSQL_TYPE_SET: return &type_handler_varchar; // Map to VARCHAR - case MYSQL_TYPE_GEOMETRY: return &type_handler_geometry; - case MYSQL_TYPE_TIMESTAMP: return &type_handler_timestamp; - case MYSQL_TYPE_TIMESTAMP2: return &type_handler_timestamp; - case MYSQL_TYPE_DATE: return &type_handler_date; - case MYSQL_TYPE_TIME: return &type_handler_time; - case MYSQL_TYPE_TIME2: return &type_handler_time; - case MYSQL_TYPE_DATETIME: return &type_handler_datetime; - case MYSQL_TYPE_DATETIME2: return &type_handler_datetime; - case MYSQL_TYPE_NEWDATE: return &type_handler_date; + case MYSQL_TYPE_GEOMETRY: +#ifdef HAVE_SPATIAL + return &type_handler_geometry; +#else + return NULL; +#endif + case MYSQL_TYPE_TIMESTAMP: return &type_handler_timestamp2;// Map to timestamp2 + case MYSQL_TYPE_TIMESTAMP2: return &type_handler_timestamp2; + case MYSQL_TYPE_DATE: return &type_handler_newdate; // Map to newdate + case MYSQL_TYPE_TIME: return &type_handler_time2; // Map to time2 + case MYSQL_TYPE_TIME2: return &type_handler_time2; + case MYSQL_TYPE_DATETIME: return &type_handler_datetime2; // Map to datetime2 + case MYSQL_TYPE_DATETIME2: return &type_handler_datetime2; + case MYSQL_TYPE_NEWDATE: + /* + NEWDATE is actually a real_type(), not a field_type(), + but it's used around the code in field_type() context. + We should probably clean up the code not to use MYSQL_TYPE_NEWDATE + in field_type() context and add DBUG_ASSERT(0) here. + */ + return &type_handler_newdate; + }; + DBUG_ASSERT(0); + return &type_handler_string; +} + + +const Type_handler * +Type_handler::get_handler_by_real_type(enum_field_types type) +{ + switch (type) { + case MYSQL_TYPE_DECIMAL: return &type_handler_olddecimal; + case MYSQL_TYPE_NEWDECIMAL: return &type_handler_newdecimal; + case MYSQL_TYPE_TINY: return &type_handler_tiny; + case MYSQL_TYPE_SHORT: return &type_handler_short; + case MYSQL_TYPE_LONG: return &type_handler_long; + case MYSQL_TYPE_LONGLONG: return &type_handler_longlong; + case MYSQL_TYPE_INT24: return &type_handler_int24; + case MYSQL_TYPE_YEAR: return &type_handler_year; + case MYSQL_TYPE_BIT: return &type_handler_bit; + case MYSQL_TYPE_FLOAT: return &type_handler_float; + case MYSQL_TYPE_DOUBLE: return &type_handler_double; + case MYSQL_TYPE_NULL: return &type_handler_null; + case MYSQL_TYPE_VARCHAR: return &type_handler_varchar; + case MYSQL_TYPE_TINY_BLOB: return &type_handler_tiny_blob; + case MYSQL_TYPE_MEDIUM_BLOB: return &type_handler_medium_blob; + case MYSQL_TYPE_LONG_BLOB: return &type_handler_long_blob; + case MYSQL_TYPE_BLOB: return &type_handler_blob; + case MYSQL_TYPE_VAR_STRING: + /* + VAR_STRING is actually a field_type(), not a real_type(), + but it's used around the code in real_type() context. + We should clean up the code and add DBUG_ASSERT(0) here. + */ + return &type_handler_string; + case MYSQL_TYPE_STRING: return &type_handler_string; + case MYSQL_TYPE_ENUM: return &type_handler_enum; + case MYSQL_TYPE_SET: return &type_handler_set; + case MYSQL_TYPE_GEOMETRY: +#ifdef HAVE_SPATIAL + return &type_handler_geometry; +#else + return NULL; +#endif + case MYSQL_TYPE_TIMESTAMP: return &type_handler_timestamp; + case MYSQL_TYPE_TIMESTAMP2: return &type_handler_timestamp2; + case MYSQL_TYPE_DATE: return &type_handler_date; + case MYSQL_TYPE_TIME: return &type_handler_time; + case MYSQL_TYPE_TIME2: return &type_handler_time2; + case MYSQL_TYPE_DATETIME: return &type_handler_datetime; + case MYSQL_TYPE_DATETIME2: return &type_handler_datetime2; + case MYSQL_TYPE_NEWDATE: return &type_handler_newdate; }; DBUG_ASSERT(0); return &type_handler_string; diff --git a/sql/sql_type.h b/sql/sql_type.h index f78a9a2abce..d2f85384d28 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -29,7 +29,9 @@ protected: const Type_handler *string_type_handler(uint max_octet_length) const; public: static const Type_handler *get_handler_by_field_type(enum_field_types type); + static const Type_handler *get_handler_by_real_type(enum_field_types type); virtual enum_field_types field_type() const= 0; + virtual enum_field_types real_field_type() const { return field_type(); } virtual Item_result result_type() const= 0; virtual Item_result cmp_type() const= 0; virtual const Type_handler* @@ -190,6 +192,15 @@ public: }; +class Type_handler_time2: public Type_handler_temporal_result +{ +public: + virtual ~Type_handler_time2() {} + enum_field_types field_type() const { return MYSQL_TYPE_TIME; } + enum_field_types real_field_type() const { return MYSQL_TYPE_TIME2; } +}; + + class Type_handler_date: public Type_handler_temporal_result { public: @@ -198,6 +209,15 @@ public: }; +class Type_handler_newdate: public Type_handler_temporal_result +{ +public: + virtual ~Type_handler_newdate() {} + enum_field_types field_type() const { return MYSQL_TYPE_DATE; } + enum_field_types real_field_type() const { return MYSQL_TYPE_NEWDATE; } +}; + + class Type_handler_datetime: public Type_handler_temporal_result { public: @@ -206,6 +226,15 @@ public: }; +class Type_handler_datetime2: public Type_handler_temporal_result +{ +public: + virtual ~Type_handler_datetime2() {} + enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } + enum_field_types real_field_type() const { return MYSQL_TYPE_DATETIME2; } +}; + + class Type_handler_timestamp: public Type_handler_temporal_result { public: @@ -214,6 +243,15 @@ public: }; +class Type_handler_timestamp2: public Type_handler_temporal_result +{ +public: + virtual ~Type_handler_timestamp2() {} + enum_field_types field_type() const { return MYSQL_TYPE_TIMESTAMP; } + enum_field_types real_field_type() const { return MYSQL_TYPE_TIMESTAMP2; } +}; + + class Type_handler_olddecimal: public Type_handler_decimal_result { public: @@ -294,6 +332,24 @@ public: }; +class Type_handler_enum: public Type_handler_string_result +{ +public: + virtual ~Type_handler_enum() {} + enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } + virtual enum_field_types real_field_type() const { return MYSQL_TYPE_ENUM; } +}; + + +class Type_handler_set: public Type_handler_string_result +{ +public: + virtual ~Type_handler_set() {} + enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } + virtual enum_field_types real_field_type() const { return MYSQL_TYPE_SET; } +}; + + /** A handler for hybrid type functions, e.g. COALESCE(), IF(), IFNULL(), NULLIF(), CASE, @@ -309,6 +365,9 @@ class Type_handler_hybrid_field_type: public Type_handler const Type_handler *get_handler_by_result_type(Item_result type) const; public: Type_handler_hybrid_field_type(); + Type_handler_hybrid_field_type(const Type_handler *handler) + :m_type_handler(handler) + { } Type_handler_hybrid_field_type(enum_field_types type) :m_type_handler(get_handler_by_field_type(type)) { } @@ -316,6 +375,10 @@ public: :m_type_handler(other->m_type_handler) { } enum_field_types field_type() const { return m_type_handler->field_type(); } + enum_field_types real_field_type() const + { + return m_type_handler->real_field_type(); + } Item_result result_type() const { return m_type_handler->result_type(); } Item_result cmp_type() const { return m_type_handler->cmp_type(); } void set_handler(const Type_handler *other) @@ -339,6 +402,10 @@ public: { return (m_type_handler= get_handler_by_field_type(type)); } + const Type_handler *set_handler_by_real_type(enum_field_types type) + { + return (m_type_handler= get_handler_by_real_type(type)); + } const Type_handler * type_handler_adjusted_to_max_octet_length(uint max_octet_length, CHARSET_INFO *cs) const @@ -349,4 +416,18 @@ public: } }; + +/** + This class is used for Item_type_holder, which preserves real_type. +*/ +class Type_handler_hybrid_real_field_type: + public Type_handler_hybrid_field_type +{ +public: + Type_handler_hybrid_real_field_type(enum_field_types type) + :Type_handler_hybrid_field_type(get_handler_by_real_type(type)) + { } +}; + + #endif /* SQL_TYPE_H_INCLUDED */ diff --git a/sql/sql_union.cc b/sql/sql_union.cc index c8350838ee8..069eadc7519 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -534,7 +534,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, while ((type= tp++)) { - if (type->result_type() == STRING_RESULT && + if (type->cmp_type() == STRING_RESULT && type->collation.derivation == DERIVATION_NONE) { my_error(ER_CANT_AGGREGATE_NCOLLATIONS, MYF(0), "UNION");