Commit f3ff8b35 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-9215 Detect cmp_type() and result_type() from field_type()

Part6: Deriving Item_type_holder from Type_handler_hybrid_real_field_type
parent 7d54d823
......@@ -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 : "<NULL>")));
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 ||
......
......@@ -5265,11 +5265,11 @@ class Item_cache_row: public Item_cache
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 @@ class Item_type_holder: public Item
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();
......
......@@ -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_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_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_time;
case MYSQL_TYPE_TIME2: return &type_handler_time2;
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_DATETIME2: return &type_handler_datetime2;
case MYSQL_TYPE_NEWDATE: return &type_handler_newdate;
};
DBUG_ASSERT(0);
return &type_handler_string;
......
......@@ -29,7 +29,9 @@ class Type_handler
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 @@ class Type_handler_time: public Type_handler_temporal_result
};
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 @@ class Type_handler_date: public Type_handler_temporal_result
};
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 @@ class Type_handler_datetime: public Type_handler_temporal_result
};
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 @@ class Type_handler_timestamp: public Type_handler_temporal_result
};
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 @@ class Type_handler_geometry: public Type_handler_string_result
};
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 @@ class Type_handler_hybrid_field_type: public Type_handler
: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 @@ class Type_handler_hybrid_field_type: public Type_handler
{
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 @@ class Type_handler_hybrid_field_type: public Type_handler
}
};
/**
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 */
......@@ -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");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment