Commit cc9cfeca authored by Alexander Barkov's avatar Alexander Barkov

MDEV-8865 Wrong field type or metadata for COALESCE(signed_int_column, unsigned_int_column)

Item_func_hybrid_field_type did not return correct field_type(), cmp_type()
and result_type() in some cases, because cached_result_type and
cached_field_type were set in independent pieces of the code and
did not properly match to each other.
Fix:
- Removing Item_func_hybrid_result_type
- Deriving Item_func_hybrid_field_type directly from Item_func
- Introducing a new class Type_handler which guarantees that
  field_type(), cmp_type() and result_type() are always properly synchronized
  and using the new class in Item_func_hybrid_field_type.
parent 09b87d62
......@@ -104,6 +104,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
../sql/sql_explain.cc ../sql/sql_explain.h
../sql/sql_analyze_stmt.cc ../sql/sql_analyze_stmt.h
../sql/compat56.cc
../sql/sql_type.cc ../sql/sql_type.h
../sql/table_cache.cc ../sql/mf_iocache_encr.cc
../sql/item_inetfunc.cc
../sql/wsrep_dummy.cc ../sql/encryption.cc
......
# "mtr --ps" returns different values in "Max length"
--disable_ps_protocol
--enable_metadata
--vertical_results
SELECT
a AS ___________a,
CASE WHEN a IS NOT NULL THEN a END AS case_______a,
CASE WHEN a IS NOT NULL THEN a ELSE a END AS case_____a_a,
COALESCE(a) AS coalesce___a,
COALESCE(a, a) AS coalesce_a_a,
IF(a IS NULL, a, a) AS if_______a_a,
IFNULL(a, a) AS ifnull___a_a,
LEAST(a, a) AS least____a_a,
GREATEST(a, a) AS greatest_a_a,
b AS ___________b,
CASE WHEN a IS NOT NULL THEN b END AS case_______b,
CASE WHEN a IS NOT NULL THEN b ELSE b END AS case_____b_b,
COALESCE(b) AS coalesce___b,
COALESCE(b, b) AS coalesce_b_b,
IF(a IS NULL, b, b) AS if_______b_b,
IFNULL(b, b) AS ifnull___b_b,
LEAST(b, b) AS least____b_b,
GREATEST(b, b) AS greatest_b_b
FROM t1;
SELECT
CASE WHEN a IS NOT NULL THEN a ELSE b END AS case_____a_b,
CASE WHEN a IS NOT NULL THEN b ELSE a END AS case_____b_a,
COALESCE(a, b) AS coalesce_a_b,
COALESCE(b, a) AS coalesce_b_a,
IF(a IS NULL, a, b) AS if_______a_b,
IF(a IS NULL, b, a) AS if_______b_a,
IFNULL(a, b) AS ifnull___a_b,
IFNULL(b, a) AS ifnull___b_a,
LEAST(a, b) AS least____a_b,
LEAST(b, a) AS least____b_a,
GREATEST(a, b) AS greatest_a_b,
GREATEST(b, a) AS greatest_b_a
FROM t1;
--horizontal_results
--disable_metadata
--enable_ps_protocol
CREATE TABLE t2 AS
SELECT
a AS ___________a,
CASE WHEN a IS NOT NULL THEN a END AS case_______a,
CASE WHEN a IS NOT NULL THEN a ELSE a END AS case_____a_a,
COALESCE(a) AS coalesce___a,
COALESCE(a, a) AS coalesce_a_a,
IF(a IS NULL, a, a) AS if_______a_a,
IFNULL(a, a) AS ifnull___a_a,
LEAST(a, a) AS least____a_a,
GREATEST(a, a) AS greatest_a_a,
b AS ___________b,
CASE WHEN a IS NOT NULL THEN b END AS case_______b,
CASE WHEN a IS NOT NULL THEN b ELSE b END AS case_____b_b,
COALESCE(b) AS coalesce___b,
COALESCE(b, b) AS coalesce_b_b,
IF(a IS NULL, b, b) AS if_______b_b,
IFNULL(b, b) AS ifnull___b_b,
LEAST(b, b) AS least____b_b,
GREATEST(b, b) AS greatest_b_b
FROM t1;
SHOW CREATE TABLE t2;
DROP TABLE t2;
CREATE TABLE t2 AS
SELECT
CASE WHEN a IS NOT NULL THEN a ELSE b END AS case_____a_b,
CASE WHEN a IS NOT NULL THEN b ELSE a END AS case_____b_a,
COALESCE(a, b) AS coalesce_a_b,
COALESCE(b, a) AS coalesce_b_a,
IF(a IS NULL, a, b) AS if_______a_b,
IF(a IS NULL, b, a) AS if_______b_a,
IFNULL(a, b) AS ifnull___a_b,
IFNULL(b, a) AS ifnull___b_a,
LEAST(a, b) AS least____a_b,
LEAST(b, a) AS least____b_a,
GREATEST(a, b) AS greatest_a_b,
GREATEST(b, a) AS greatest_b_a
FROM t1;
SHOW CREATE TABLE t2;
DROP TABLE t2;
This diff is collapsed.
--echo #
--echo # Start of 10.1 tests
--echo #
--echo #
--echo # MDEV-8865 Wrong field type or metadata for COALESCE(signed_int_column, unsigned_int_column)
--echo #
--echo #
CREATE TABLE t1 (a INT, b INT UNSIGNED);
INSERT INTO t1 VALUES (1,1);
INSERT INTO t1 VALUES (-1,1);
INSERT INTO t1 VALUES (-2147483648,4294967295);
--source include/func_hybrid_type.inc
DROP TABLE t1;
--echo #
CREATE TABLE t1 (a INT, b INT);
INSERT INTO t1 VALUES (1,1);
INSERT INTO t1 VALUES (-2147483648,2147483647);
--source include/func_hybrid_type.inc
DROP TABLE t1;
--echo #
CREATE TABLE t1 (a BIGINT, b BIGINT UNSIGNED);
INSERT INTO t1 VALUES (1,1);
INSERT INTO t1 VALUES (-9223372036854775808,0xFFFFFFFFFFFFFFFF);
--source include/func_hybrid_type.inc
DROP TABLE t1;
--echo #
CREATE TABLE t1 (a BIGINT, b BIGINT);
INSERT INTO t1 VALUES (1,1);
INSERT INTO t1 VALUES (-9223372036854775808,9223372036854775807);
--source include/func_hybrid_type.inc
DROP TABLE t1;
# Testing BIT(N) types.
# Using safe BIT(N) type and value to make sure
# that "file func_hybrid_type.test" tells "ASCII text".
--echo #
CREATE TABLE t1 (a INT, b BIT(8));
INSERT INTO t1 VALUES (-2147483648,0x32);
--source include/func_hybrid_type.inc
DROP TABLE t1;
--echo #
CREATE TABLE t1 (a INT UNSIGNED, b BIT(8));
INSERT INTO t1 VALUES (4294967295,0x32);
--source include/func_hybrid_type.inc
DROP TABLE t1;
--echo #
CREATE TABLE t1 (a BIT(7), b BIT(8));
INSERT INTO t1 VALUES (0x32,0x32);
--source include/func_hybrid_type.inc
DROP TABLE t1;
--echo #
--echo # End of 10.1 tests
--echo #
......@@ -135,6 +135,7 @@ SET (SQL_SOURCE
my_apc.cc my_apc.h mf_iocache_encr.cc
my_json_writer.cc my_json_writer.h
rpl_gtid.cc rpl_parallel.cc
sql_type.cc sql_type.h
${WSREP_SOURCES}
table_cache.cc encryption.cc
${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc
......
......@@ -28,6 +28,7 @@
#include "unireg.h" // REQUIRED: for other includes
#include "thr_malloc.h" /* sql_calloc */
#include "field.h" /* Derivation */
#include "sql_type.h"
C_MODE_START
#include <ma_dyncol.h>
......@@ -602,7 +603,9 @@ class Type_std_attributes
};
class Item: public Value_source, public Type_std_attributes
class Item: public Value_source,
public Type_std_attributes,
public Type_handler
{
Item(const Item &); /* Prevent use of these */
void operator=(Item &);
......@@ -752,12 +755,12 @@ class Item: public Value_source, public Type_std_attributes
virtual bool send(Protocol *protocol, String *str);
virtual bool eq(const Item *, bool binary_cmp) const;
/* result_type() of an item specifies how the value should be returned */
virtual Item_result result_type() const { return REAL_RESULT; }
Item_result result_type() const { return REAL_RESULT; }
/* ... while cmp_type() specifies how it should be compared */
virtual Item_result cmp_type() const;
Item_result cmp_type() const;
virtual Item_result cast_to_int_type() const { return cmp_type(); }
virtual enum_field_types string_field_type() const;
virtual enum_field_types field_type() const;
enum_field_types field_type() const;
virtual enum Type type() const =0;
/*
real_type() is the type of base item. This is same as type() for
......
......@@ -34,47 +34,6 @@
#include "sql_time.h" // make_truncated_value_warning
#include "sql_base.h" // dynamic_column_error_message
static Item_result item_store_type(Item_result a, Item *item,
my_bool unsigned_flag)
{
Item_result b= item->result_type();
if (a == STRING_RESULT || b == STRING_RESULT)
return STRING_RESULT;
else if (a == REAL_RESULT || b == REAL_RESULT)
return REAL_RESULT;
else if (a == DECIMAL_RESULT || b == DECIMAL_RESULT ||
unsigned_flag != item->unsigned_flag)
return DECIMAL_RESULT;
else
return INT_RESULT;
}
static void agg_result_type(Item_result *type, Item **items, uint nitems)
{
Item **item, **item_end;
my_bool unsigned_flag= 0;
*type= STRING_RESULT;
/* Skip beginning NULL items */
for (item= items, item_end= item + nitems; item < item_end; item++)
{
if ((*item)->type() != Item::NULL_ITEM)
{
*type= (*item)->result_type();
unsigned_flag= (*item)->unsigned_flag;
item++;
break;
}
}
/* Combine result types. Note: NULL items don't affect the result */
for (; item < item_end; item++)
{
if ((*item)->type() != Item::NULL_ITEM)
*type= item_store_type(*type, *item, unsigned_flag);
}
}
/**
find an temporal type (item) that others will be converted to
......@@ -185,6 +144,22 @@ static int agg_cmp_type(Item_result *type, Item **items, uint nitems)
@param[in] items array of items to aggregate the type from
@paran[in] nitems number of items in the array
@param[in] treat_bit_as_number - if BIT should be aggregated to a non-BIT
counterpart as a LONGLONG number or as a VARBINARY string.
Currently behaviour depends on the function:
- LEAST/GREATEST treat BIT as VARBINARY when
aggregating with a non-BIT counterpart.
Note, UNION also works this way.
- CASE, COALESCE, IF, IFNULL treat BIT as LONGLONG when
aggregating with a non-BIT counterpart;
This inconsistency may be changed in the future. See MDEV-8867.
Note, independently from "treat_bit_as_number":
- a single BIT argument gives BIT as a result
- two BIT couterparts give BIT as a result
@details This function aggregates field types from the array of items.
Found type is supposed to be used later as the result field type
......@@ -198,14 +173,50 @@ static int agg_cmp_type(Item_result *type, Item **items, uint nitems)
@return aggregated field type.
*/
enum_field_types agg_field_type(Item **items, uint nitems)
enum_field_types agg_field_type(Item **items, uint nitems,
bool treat_bit_as_number)
{
uint i;
if (!nitems || items[0]->result_type() == ROW_RESULT )
return (enum_field_types)-1;
if (!nitems || items[0]->result_type() == ROW_RESULT)
{
DBUG_ASSERT(0);
return MYSQL_TYPE_NULL;
}
enum_field_types res= items[0]->field_type();
uint unsigned_count= items[0]->unsigned_flag;
for (i= 1 ; i < nitems ; i++)
res= Field::field_type_merge(res, items[i]->field_type());
{
enum_field_types cur= items[i]->field_type();
if (treat_bit_as_number &&
((res == MYSQL_TYPE_BIT) ^ (cur == MYSQL_TYPE_BIT)))
{
if (res == MYSQL_TYPE_BIT)
res= MYSQL_TYPE_LONGLONG; // BIT + non-BIT
else
cur= MYSQL_TYPE_LONGLONG; // non-BIT + BIT
}
res= Field::field_type_merge(res, cur);
unsigned_count+= items[i]->unsigned_flag;
}
switch (res) {
case MYSQL_TYPE_TINY:
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_LONG:
case MYSQL_TYPE_LONGLONG:
case MYSQL_TYPE_INT24:
case MYSQL_TYPE_YEAR:
case MYSQL_TYPE_BIT:
if (unsigned_count != 0 && unsigned_count != nitems)
{
/*
If all arguments are of INT-alike type but have different
unsigned_flag, then convert to DECIMAL.
*/
return MYSQL_TYPE_NEWDECIMAL;
}
default:
break;
}
return res;
}
......@@ -2261,13 +2272,13 @@ void
Item_func_case_abbreviation2::fix_length_and_dec2(Item **args)
{
uint32 char_length;
agg_result_type(&cached_result_type, args, 2);
cached_field_type= agg_field_type(args, 2);
set_handler_by_field_type(agg_field_type(args, 2, true));
maybe_null=args[0]->maybe_null || args[1]->maybe_null;
decimals= MY_MAX(args[0]->decimals, args[1]->decimals);
unsigned_flag= args[0]->unsigned_flag && args[1]->unsigned_flag;
if (cached_result_type == DECIMAL_RESULT || cached_result_type == INT_RESULT)
if (Item_func_case_abbreviation2::result_type() == DECIMAL_RESULT ||
Item_func_case_abbreviation2::result_type() == INT_RESULT)
{
int len0= args[0]->max_char_length() - args[0]->decimals
- (args[0]->unsigned_flag ? 0 : 1);
......@@ -2280,9 +2291,10 @@ Item_func_case_abbreviation2::fix_length_and_dec2(Item **args)
else
char_length= MY_MAX(args[0]->max_char_length(), args[1]->max_char_length());
switch (cached_result_type) {
switch (Item_func_case_abbreviation2::result_type()) {
case STRING_RESULT:
if (count_string_result_length(cached_field_type, args, 2))
if (count_string_result_length(Item_func_case_abbreviation2::field_type(),
args, 2))
return;
break;
case DECIMAL_RESULT:
......@@ -2459,8 +2471,7 @@ void Item_func_if::fix_after_pullout(st_select_lex *new_parent, Item **ref)
void Item_func_if::cache_type_info(Item *source)
{
Type_std_attributes::set(source);
cached_field_type= source->field_type();
cached_result_type= source->result_type();
set_handler_by_field_type(source->field_type());
maybe_null= source->maybe_null;
}
......@@ -2475,7 +2486,7 @@ Item_func_if::fix_length_and_dec()
maybe_null= true;
// If both arguments are NULL, make resulting type BINARY(0).
if (args[2]->type() == NULL_ITEM)
cached_field_type= MYSQL_TYPE_STRING;
set_handler_by_field_type(MYSQL_TYPE_STRING);
return;
}
if (args[2]->type() == NULL_ITEM)
......@@ -2546,8 +2557,7 @@ Item_func_nullif::fix_length_and_dec()
if (!args[2]) // Only false if EOM
return;
cached_result_type= args[2]->result_type();
cached_field_type= args[2]->field_type();
set_handler_by_field_type(args[2]->field_type());
collation.set(args[2]->collation);
decimals= args[2]->decimals;
unsigned_flag= args[2]->unsigned_flag;
......@@ -2961,12 +2971,11 @@ void Item_func_case::fix_length_and_dec()
if (else_expr_num != -1)
agg[nagg++]= args[else_expr_num];
agg_result_type(&cached_result_type, agg, nagg);
cached_field_type= agg_field_type(agg, nagg);
set_handler_by_field_type(agg_field_type(agg, nagg, true));
if (cached_result_type == STRING_RESULT)
if (Item_func_case::result_type() == STRING_RESULT)
{
if (count_string_result_length(cached_field_type, agg, nagg))
if (count_string_result_length(Item_func_case::field_type(), agg, nagg))
return;
/*
Copy all THEN and ELSE items back to args[] array.
......@@ -3295,11 +3304,11 @@ my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value)
void Item_func_coalesce::fix_length_and_dec()
{
cached_field_type= agg_field_type(args, arg_count);
agg_result_type(&cached_result_type, args, arg_count);
switch (cached_result_type) {
set_handler_by_field_type(agg_field_type(args, arg_count, true));
switch (Item_func_coalesce::result_type()) {
case STRING_RESULT:
if (count_string_result_length(cached_field_type, args, arg_count))
if (count_string_result_length(Item_func_coalesce::field_type(),
args, arg_count))
return;
break;
case DECIMAL_RESULT:
......
......@@ -800,28 +800,28 @@ void Item_num_op::fix_length_and_dec(void)
{
count_real_length();
max_length= float_length(decimals);
cached_result_type= REAL_RESULT;
set_handler_by_result_type(REAL_RESULT);
}
else if (r0 == DECIMAL_RESULT || r1 == DECIMAL_RESULT ||
r0 == TIME_RESULT || r1 == TIME_RESULT)
{
cached_result_type= DECIMAL_RESULT;
set_handler_by_result_type(DECIMAL_RESULT);
result_precision();
fix_decimals();
if ((r0 == TIME_RESULT || r1 == TIME_RESULT) && decimals == 0)
cached_result_type= INT_RESULT;
set_handler_by_result_type(INT_RESULT);
}
else
{
DBUG_ASSERT(r0 == INT_RESULT && r1 == INT_RESULT);
cached_result_type=INT_RESULT;
set_handler_by_result_type(INT_RESULT);
result_precision();
decimals= 0;
}
DBUG_PRINT("info", ("Type: %s",
(cached_result_type == REAL_RESULT ? "REAL_RESULT" :
cached_result_type == DECIMAL_RESULT ? "DECIMAL_RESULT" :
cached_result_type == INT_RESULT ? "INT_RESULT" :
(result_type() == REAL_RESULT ? "REAL_RESULT" :
result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" :
result_type() == INT_RESULT ? "INT_RESULT" :
"--ILLEGAL!!!--")));
DBUG_VOID_RETURN;
}
......@@ -837,20 +837,22 @@ void Item_func_num1::fix_length_and_dec()
{
DBUG_ENTER("Item_func_num1::fix_length_and_dec");
DBUG_PRINT("info", ("name %s", func_name()));
switch (cached_result_type= args[0]->cast_to_int_type()) {
// Note, cast_to_int_type() can return TIME_RESULT
switch (args[0]->cast_to_int_type()) {
case INT_RESULT:
set_handler_by_result_type(INT_RESULT);
max_length= args[0]->max_length;
unsigned_flag= args[0]->unsigned_flag;
break;
case STRING_RESULT:
case REAL_RESULT:
cached_result_type= REAL_RESULT;
set_handler_by_result_type(REAL_RESULT);
decimals= args[0]->decimals; // Preserve NOT_FIXED_DEC
max_length= float_length(decimals);
break;
case TIME_RESULT:
cached_result_type= DECIMAL_RESULT;
case DECIMAL_RESULT:
set_handler_by_result_type(DECIMAL_RESULT);
decimals= args[0]->decimal_scale(); // Do not preserve NOT_FIXED_DEC
max_length= args[0]->max_length;
break;
......@@ -858,18 +860,18 @@ void Item_func_num1::fix_length_and_dec()
DBUG_ASSERT(0);
}
DBUG_PRINT("info", ("Type: %s",
(cached_result_type == REAL_RESULT ? "REAL_RESULT" :
cached_result_type == DECIMAL_RESULT ? "DECIMAL_RESULT" :
cached_result_type == INT_RESULT ? "INT_RESULT" :
(result_type() == REAL_RESULT ? "REAL_RESULT" :
result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" :
result_type() == INT_RESULT ? "INT_RESULT" :
"--ILLEGAL!!!--")));
DBUG_VOID_RETURN;
}
String *Item_func_hybrid_result_type::val_str(String *str)
String *Item_func_hybrid_field_type::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
switch (cached_result_type) {
switch (Item_func_hybrid_field_type::result_type()) {
case DECIMAL_RESULT:
{
my_decimal decimal_value, *val;
......@@ -921,10 +923,10 @@ String *Item_func_hybrid_result_type::val_str(String *str)
}
double Item_func_hybrid_result_type::val_real()
double Item_func_hybrid_field_type::val_real()
{
DBUG_ASSERT(fixed == 1);
switch (cached_result_type) {
switch (Item_func_hybrid_field_type::result_type()) {
case DECIMAL_RESULT:
{
my_decimal decimal_value, *val;
......@@ -966,10 +968,10 @@ double Item_func_hybrid_result_type::val_real()
}
longlong Item_func_hybrid_result_type::val_int()
longlong Item_func_hybrid_field_type::val_int()
{
DBUG_ASSERT(fixed == 1);
switch (cached_result_type) {
switch (Item_func_hybrid_field_type::result_type()) {
case DECIMAL_RESULT:
{
my_decimal decimal_value, *val;
......@@ -1008,11 +1010,11 @@ longlong Item_func_hybrid_result_type::val_int()
}
my_decimal *Item_func_hybrid_result_type::val_decimal(my_decimal *decimal_value)
my_decimal *Item_func_hybrid_field_type::val_decimal(my_decimal *decimal_value)
{
my_decimal *val= decimal_value;
DBUG_ASSERT(fixed == 1);
switch (cached_result_type) {
switch (Item_func_hybrid_field_type::result_type()) {
case DECIMAL_RESULT:
val= decimal_op(decimal_value);
break;
......@@ -1054,11 +1056,11 @@ my_decimal *Item_func_hybrid_result_type::val_decimal(my_decimal *decimal_value)
}
bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime,
bool Item_func_hybrid_field_type::get_date(MYSQL_TIME *ltime,
ulonglong fuzzydate)
{
DBUG_ASSERT(fixed == 1);
switch (cached_result_type) {
switch (Item_func_hybrid_field_type::result_type()) {
case DECIMAL_RESULT:
{
my_decimal value, *res;
......@@ -1812,7 +1814,7 @@ void Item_func_div::fix_length_and_dec()
DBUG_ENTER("Item_func_div::fix_length_and_dec");
prec_increment= current_thd->variables.div_precincrement;
Item_num_op::fix_length_and_dec();
switch (cached_result_type) {
switch (Item_func_div::result_type()) {
case REAL_RESULT:
{
decimals=MY_MAX(args[0]->decimals,args[1]->decimals)+prec_increment;
......@@ -1828,7 +1830,7 @@ void Item_func_div::fix_length_and_dec()
break;
}
case INT_RESULT:
cached_result_type= DECIMAL_RESULT;
set_handler_by_result_type(DECIMAL_RESULT);
DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT"));
result_precision();
break;
......@@ -2075,7 +2077,7 @@ void Item_func_neg::fix_length_and_dec()
Use val() to get value as arg_type doesn't mean that item is
Item_int or Item_float due to existence of Item_param.
*/
if (cached_result_type == INT_RESULT && args[0]->const_item())
if (Item_func_neg::result_type() == INT_RESULT && args[0]->const_item())
{
longlong val= args[0]->val_int();
if ((ulonglong) val >= (ulonglong) LONGLONG_MIN &&
......@@ -2086,7 +2088,7 @@ void Item_func_neg::fix_length_and_dec()
Ensure that result is converted to DECIMAL, as longlong can't hold
the negated number
*/
cached_result_type= DECIMAL_RESULT;
set_handler_by_result_type(DECIMAL_RESULT);
DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT"));
}
}
......@@ -2388,11 +2390,12 @@ void Item_func_int_val::fix_length_and_dec()
set_if_smaller(max_length,tmp);
decimals= 0;
switch (cached_result_type= args[0]->cast_to_int_type())
// Note, cast_to_int_type() can return TIME_RESULT
switch (args[0]->cast_to_int_type())
{
case STRING_RESULT:
case REAL_RESULT:
cached_result_type= REAL_RESULT;
set_handler_by_result_type(REAL_RESULT);
max_length= float_length(decimals);
break;
case INT_RESULT:
......@@ -2405,21 +2408,21 @@ void Item_func_int_val::fix_length_and_dec()
if ((args[0]->max_length - args[0]->decimals) >=
(DECIMAL_LONGLONG_DIGITS - 2))
{
cached_result_type= DECIMAL_RESULT;
set_handler_by_result_type(DECIMAL_RESULT);
}
else
{
unsigned_flag= args[0]->unsigned_flag;
cached_result_type= INT_RESULT;
set_handler_by_result_type(INT_RESULT);
}
break;
case ROW_RESULT:
DBUG_ASSERT(0);
}
DBUG_PRINT("info", ("Type: %s",
(cached_result_type == REAL_RESULT ? "REAL_RESULT" :
cached_result_type == DECIMAL_RESULT ? "DECIMAL_RESULT" :
cached_result_type == INT_RESULT ? "INT_RESULT" :
(result_type() == REAL_RESULT ? "REAL_RESULT" :
result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" :
result_type() == INT_RESULT ? "INT_RESULT" :
"--ILLEGAL!!!--")));
DBUG_VOID_RETURN;
......@@ -2534,10 +2537,10 @@ void Item_func_round::fix_length_and_dec()
if (args[0]->result_type() == DECIMAL_RESULT)
{
max_length++;
cached_result_type= DECIMAL_RESULT;
set_handler_by_result_type(DECIMAL_RESULT);
}
else
cached_result_type= REAL_RESULT;
set_handler_by_result_type(REAL_RESULT);
return;
}
......@@ -2555,14 +2558,14 @@ void Item_func_round::fix_length_and_dec()
{
decimals= MY_MIN(decimals_to_set, NOT_FIXED_DEC);
max_length= float_length(decimals);
cached_result_type= REAL_RESULT;
set_handler_by_result_type(REAL_RESULT);
return;
}
switch (args[0]->result_type()) {
case REAL_RESULT:
case STRING_RESULT:
cached_result_type= REAL_RESULT;
set_handler_by_result_type(REAL_RESULT);
decimals= MY_MIN(decimals_to_set, NOT_FIXED_DEC);
max_length= float_length(decimals);
break;
......@@ -2573,14 +2576,14 @@ void Item_func_round::fix_length_and_dec()
!val1_unsigned);
max_length= args[0]->max_length + length_can_increase;
/* Here we can keep INT_RESULT */
cached_result_type= INT_RESULT;
set_handler_by_result_type(INT_RESULT);
decimals= 0;
break;
}
/* fall through */
case DECIMAL_RESULT:
{
cached_result_type= DECIMAL_RESULT;
set_handler_by_result_type(DECIMAL_RESULT);
decimals_to_set= MY_MIN(DECIMAL_MAX_SCALE, decimals_to_set);
int decimals_delta= args[0]->decimals - decimals_to_set;
int precision= args[0]->decimal_precision();
......@@ -2884,7 +2887,7 @@ void Item_func_min_max::fix_length_and_dec()
set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
}
else
cached_field_type= agg_field_type(args, arg_count);
cached_field_type= agg_field_type(args, arg_count, false);
}
......
......@@ -373,29 +373,32 @@ class Item_real_func :public Item_func
};
class Item_func_hybrid_result_type: public Item_func
class Item_func_hybrid_field_type: public Item_func,
public Type_handler_hybrid_field_type
{
protected:
Item_result cached_result_type;
public:
Item_func_hybrid_result_type(THD *thd):
Item_func(thd), cached_result_type(REAL_RESULT)
Item_func_hybrid_field_type(THD *thd):
Item_func(thd)
{ collation.set_numeric(); }
Item_func_hybrid_result_type(THD *thd, Item *a):
Item_func(thd, a), cached_result_type(REAL_RESULT)
Item_func_hybrid_field_type(THD *thd, Item *a):
Item_func(thd, a)
{ collation.set_numeric(); }
Item_func_hybrid_result_type(THD *thd, Item *a, Item *b):
Item_func(thd, a, b), cached_result_type(REAL_RESULT)
Item_func_hybrid_field_type(THD *thd, Item *a, Item *b):
Item_func(thd, a, b)
{ collation.set_numeric(); }
Item_func_hybrid_result_type(THD *thd, Item *a, Item *b, Item *c):
Item_func(thd, a, b, c), cached_result_type(REAL_RESULT)
Item_func_hybrid_field_type(THD *thd, Item *a, Item *b, Item *c):
Item_func(thd, a, b, c)
{ collation.set_numeric(); }
Item_func_hybrid_result_type(THD *thd, List<Item> &list):
Item_func(thd, list), cached_result_type(REAL_RESULT)
Item_func_hybrid_field_type(THD *thd, List<Item> &list):
Item_func(thd, list)
{ collation.set_numeric(); }
enum Item_result result_type () const { return cached_result_type; }
enum_field_types field_type() const
{ return Type_handler_hybrid_field_type::field_type(); }
enum Item_result result_type () const
{ return Type_handler_hybrid_field_type::result_type(); }
enum Item_result cmp_type () const
{ return Type_handler_hybrid_field_type::cmp_type(); }
double val_real();
longlong val_int();
......@@ -449,33 +452,7 @@ class Item_func_hybrid_result_type: public Item_func
};
class Item_func_hybrid_field_type :public Item_func_hybrid_result_type
{
protected:
enum_field_types cached_field_type;
public:
Item_func_hybrid_field_type(THD *thd):
Item_func_hybrid_result_type(thd), cached_field_type(MYSQL_TYPE_DOUBLE)
{}
Item_func_hybrid_field_type(THD *thd, Item *a, Item *b):
Item_func_hybrid_result_type(thd, a, b),
cached_field_type(MYSQL_TYPE_DOUBLE)
{}
Item_func_hybrid_field_type(THD *thd, Item *a, Item *b, Item *c):
Item_func_hybrid_result_type(thd, a, b, c),
cached_field_type(MYSQL_TYPE_DOUBLE)
{}
Item_func_hybrid_field_type(THD *thd, List<Item> &list):
Item_func_hybrid_result_type(thd, list),
cached_field_type(MYSQL_TYPE_DOUBLE)
{}
enum_field_types field_type() const { return cached_field_type; }
};
class Item_func_numhybrid: public Item_func_hybrid_result_type
class Item_func_numhybrid: public Item_func_hybrid_field_type
{
protected:
......@@ -487,18 +464,18 @@ class Item_func_numhybrid: public Item_func_hybrid_result_type
}
public:
Item_func_numhybrid(THD *thd): Item_func_hybrid_result_type(thd)
Item_func_numhybrid(THD *thd): Item_func_hybrid_field_type(thd)
{ }
Item_func_numhybrid(THD *thd, Item *a): Item_func_hybrid_result_type(thd, a)
Item_func_numhybrid(THD *thd, Item *a): Item_func_hybrid_field_type(thd, a)
{ }
Item_func_numhybrid(THD *thd, Item *a, Item *b):
Item_func_hybrid_result_type(thd, a, b)
Item_func_hybrid_field_type(thd, a, b)
{ }
Item_func_numhybrid(THD *thd, Item *a, Item *b, Item *c):
Item_func_hybrid_result_type(thd, a, b, c)
Item_func_hybrid_field_type(thd, a, b, c)
{ }
Item_func_numhybrid(THD *thd, List<Item> &list):
Item_func_hybrid_result_type(thd, list)
Item_func_hybrid_field_type(thd, list)
{ }
String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
bool date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(0); return true; }
......@@ -2169,7 +2146,8 @@ class Item_func_last_value :public Item_func
Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
LEX_STRING component);
extern bool check_reserved_words(LEX_STRING *name);
extern enum_field_types agg_field_type(Item **items, uint nitems);
extern enum_field_types agg_field_type(Item **items, uint nitems,
bool treat_bit_as_number);
Item *find_date_time_item(Item **args, uint nargs, uint col);
double my_double_round(double value, longlong dec, bool dec_unsigned,
bool truncate);
......
......@@ -421,7 +421,7 @@ class Item_func_seconds_hybrid: public Item_func_numhybrid
set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
max_length=17 + (decimals ? decimals + 1 : 0);
maybe_null= true;
cached_result_type= decimals ? DECIMAL_RESULT : INT_RESULT;
set_handler_by_result_type(decimals ? DECIMAL_RESULT : INT_RESULT);
}
double real_op() { DBUG_ASSERT(0); return 0; }
String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
......
/*
Copyright (c) 2015 MariaDB Foundation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "sql_type.h"
static Type_handler_tiny type_handler_tiny;
static Type_handler_short type_handler_short;
static Type_handler_long type_handler_long;
static Type_handler_longlong type_handler_longlong;
static Type_handler_int24 type_handler_int24;
static Type_handler_year type_handler_year;
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_date type_handler_date;
static Type_handler_datetime type_handler_datetime;
static Type_handler_timestamp type_handler_timestamp;
static Type_handler_olddecimal type_handler_olddecimal;
static Type_handler_newdecimal type_handler_newdecimal;
static Type_handler_null type_handler_null;
static Type_handler_string type_handler_string;
static Type_handler_varchar type_handler_varchar;
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;
static Type_handler_geometry type_handler_geometry;
Type_handler_hybrid_field_type::Type_handler_hybrid_field_type()
:m_type_handler(&type_handler_double)
{
}
const Type_handler *
Type_handler_hybrid_field_type::get_handler_by_result_type(Item_result type)
const
{
switch (type) {
case REAL_RESULT: return &type_handler_double;
case INT_RESULT: return &type_handler_longlong;
case DECIMAL_RESULT: return &type_handler_newdecimal;
case STRING_RESULT: return &type_handler_string;
case TIME_RESULT:
case ROW_RESULT:
DBUG_ASSERT(0);
}
return &type_handler_string;
}
const Type_handler *
Type_handler_hybrid_field_type::get_handler_by_field_type(enum_field_types type)
const
{
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: return &type_handler_varchar; // Map to VARCHAR
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;
};
DBUG_ASSERT(0);
return &type_handler_string;
}
#ifndef SQL_TYPE_H_INCLUDED
#define SQL_TYPE_H_INCLUDED
/*
Copyright (c) 2015 MariaDB Foundation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
#include "mysqld.h"
class Type_handler
{
public:
virtual enum_field_types field_type() const= 0;
virtual Item_result result_type() const= 0;
virtual Item_result cmp_type() const= 0;
};
/*** Abstract classes for every XXX_RESULT */
class Type_handler_real_result: public Type_handler
{
public:
Item_result result_type() const { return REAL_RESULT; }
Item_result cmp_type() const { return REAL_RESULT; }
};
class Type_handler_decimal_result: public Type_handler
{
public:
Item_result result_type() const { return DECIMAL_RESULT; }
Item_result cmp_type() const { return DECIMAL_RESULT; }
};
class Type_handler_int_result: public Type_handler
{
public:
Item_result result_type() const { return INT_RESULT; }
Item_result cmp_type() const { return INT_RESULT; }
};
class Type_handler_temporal_result: public Type_handler
{
public:
Item_result result_type() const { return STRING_RESULT; }
Item_result cmp_type() const { return TIME_RESULT; }
};
class Type_handler_string_result: public Type_handler
{
public:
Item_result result_type() const { return STRING_RESULT; }
Item_result cmp_type() const { return STRING_RESULT; }
};
/***
Instantiable classes for every MYSQL_TYPE_XXX
There are no Type_handler_xxx for the following types:
- MYSQL_TYPE_VAR_STRING (old VARCHAR) - mapped to MYSQL_TYPE_VARSTRING
- MYSQL_TYPE_ENUM - mapped to MYSQL_TYPE_VARSTRING
- MYSQL_TYPE_SET: - mapped to MYSQL_TYPE_VARSTRING
because the functionality that currently uses Type_handler
(e.g. hybrid type functions) does not need to distinguish between
these types and VARCHAR.
For example:
CREATE TABLE t2 AS SELECT COALESCE(enum_column) FROM t1;
creates a VARCHAR column.
There most likely be Type_handler_enum and Type_handler_set later,
when the Type_handler infrastructure gets used in more pieces of the code.
*/
class Type_handler_tiny: public Type_handler_int_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_TINY; }
};
class Type_handler_short: public Type_handler_int_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_SHORT; }
};
class Type_handler_long: public Type_handler_int_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_LONG; }
};
class Type_handler_longlong: public Type_handler_int_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
};
class Type_handler_int24: public Type_handler_int_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_INT24; }
};
class Type_handler_year: public Type_handler_int_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_YEAR; }
};
class Type_handler_bit: public Type_handler_int_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_BIT; }
};
class Type_handler_float: public Type_handler_real_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_FLOAT; }
};
class Type_handler_double: public Type_handler_real_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
};
class Type_handler_time: public Type_handler_temporal_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
};
class Type_handler_date: public Type_handler_temporal_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
};
class Type_handler_datetime: public Type_handler_temporal_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
};
class Type_handler_timestamp: public Type_handler_temporal_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_TIMESTAMP; }
};
class Type_handler_olddecimal: public Type_handler_decimal_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_DECIMAL; }
};
class Type_handler_newdecimal: public Type_handler_decimal_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
};
class Type_handler_null: public Type_handler_string_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_NULL; }
};
class Type_handler_string: public Type_handler_string_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
};
class Type_handler_varchar: public Type_handler_string_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
};
class Type_handler_tiny_blob: public Type_handler_string_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_TINY_BLOB; }
};
class Type_handler_medium_blob: public Type_handler_string_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_MEDIUM_BLOB; }
};
class Type_handler_long_blob: public Type_handler_string_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_LONG_BLOB; }
};
class Type_handler_blob: public Type_handler_string_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_BLOB; }
};
class Type_handler_geometry: public Type_handler_string_result
{
public:
enum_field_types field_type() const { return MYSQL_TYPE_GEOMETRY; }
};
/**
A handler for hybrid type functions, e.g.
COALESCE(), IF(), IFNULL(), NULLIF(), CASE,
numeric operators,
UNIX_TIMESTAMP(), TIME_TO_SEC().
Makes sure that field_type(), cmp_type() and result_type()
are always in sync to each other for hybrid functions.
*/
class Type_handler_hybrid_field_type: public Type_handler
{
const Type_handler *m_type_handler;
const Type_handler *get_handler_by_result_type(Item_result type) const;
const Type_handler *get_handler_by_field_type(enum_field_types type) const;
public:
Type_handler_hybrid_field_type();
enum_field_types field_type() const { return m_type_handler->field_type(); }
Item_result result_type() const { return m_type_handler->result_type(); }
Item_result cmp_type() const { return m_type_handler->cmp_type(); }
const Type_handler *set_handler_by_result_type(Item_result type)
{
return (m_type_handler= get_handler_by_result_type(type));
}
const Type_handler *set_handler_by_field_type(enum_field_types type)
{
return (m_type_handler= get_handler_by_field_type(type));
}
};
#endif /* SQL_TYPE_H_INCLUDED */
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