From 45730fb11e08571bbe4023cd7a7e8d1168ceb008 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 13 Apr 2017 06:50:00 +0400 Subject: [PATCH] MDEV-12238 Add Type_handler::Item_func_{plus|minus|mul|div|mod}_fix_length_and_dec() --- mysql-test/r/gis-debug.result | 53 ++++++ mysql-test/r/gis.result | 65 +++++++ mysql-test/t/gis-debug.test | 65 +++++++ mysql-test/t/gis.test | 75 ++++++++ sql/item_func.cc | 189 ++++++++++---------- sql/item_func.h | 62 ++++++- sql/sql_type.cc | 313 ++++++++++++++++++++++++++++++++++ sql/sql_type.h | 92 ++++++++-- 8 files changed, 811 insertions(+), 103 deletions(-) diff --git a/mysql-test/r/gis-debug.result b/mysql-test/r/gis-debug.result index 0f63509bb993a..889ee5c9513c3 100644 --- a/mysql-test/r/gis-debug.result +++ b/mysql-test/r/gis-debug.result @@ -352,3 +352,56 @@ Note 1105 DBUG: types_compatible=yes bisect=yes DROP TABLE t1; SET SESSION debug_dbug="-d,Predicant_to_list_comparator"; SET SESSION debug_dbug="-d,Item_func_in"; +# +# MDEV-12238 Add Type_handler::Item_func_{plus|minus|mul|div|mod}_fix_length_and_dec() +# +SET debug_dbug='+d,num_op'; +CREATE TABLE t1 AS SELECT +POINT(0,0)+POINT(0,0), +POINT(0,0)-POINT(0,0), +POINT(0,0)*POINT(0,0), +POINT(0,0)/POINT(0,0), +POINT(0,0) MOD POINT(0,0) LIMIT 0; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `POINT(0,0)+POINT(0,0)` geometry DEFAULT NULL, + `POINT(0,0)-POINT(0,0)` geometry DEFAULT NULL, + `POINT(0,0)*POINT(0,0)` geometry DEFAULT NULL, + `POINT(0,0)/POINT(0,0)` geometry DEFAULT NULL, + `POINT(0,0) MOD POINT(0,0)` geometry DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE TABLE t1 AS SELECT +POINT(0,0)+'0', +POINT(0,0)-'0', +POINT(0,0)*'0', +POINT(0,0)/'0', +POINT(0,0) MOD '0' LIMIT 0; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `POINT(0,0)+'0'` longtext DEFAULT NULL, + `POINT(0,0)-'0'` longtext DEFAULT NULL, + `POINT(0,0)*'0'` longtext DEFAULT NULL, + `POINT(0,0)/'0'` longtext DEFAULT NULL, + `POINT(0,0) MOD '0'` longtext DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE TABLE t1 AS SELECT +'0'+POINT(0,0), +'0'*POINT(0,0) LIMIT 0; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `'0'+POINT(0,0)` longtext DEFAULT NULL, + `'0'*POINT(0,0)` longtext DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE TABLE t1 AS SELECT '0'-POINT(0,0) LIMIT 0; +ERROR HY000: Illegal parameter data types varchar and geometry for operation '-' +CREATE TABLE t1 AS SELECT '0'/POINT(0,0) LIMIT 0; +ERROR HY000: Illegal parameter data types varchar and geometry for operation '/' +CREATE TABLE t1 AS SELECT '0' MOD POINT(0,0) LIMIT 0; +ERROR HY000: Illegal parameter data types varchar and geometry for operation '%' +SET debug_dbug='-d,num_op'; diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result index ea1736d7aa347..e713233180603 100644 --- a/mysql-test/r/gis.result +++ b/mysql-test/r/gis.result @@ -3837,5 +3837,70 @@ SELECT LENGTH(CAST(COALESCE(a) AS BINARY)) FROM t1; LENGTH(CAST(COALESCE(a) AS BINARY)) DROP TABLE t1; # +# MDEV-12238 Add Type_handler::Item_func_{plus|minus|mul|div|mod}_fix_length_and_dec() +# +CREATE TABLE t1 (a GEOMETRY); +SELECT POINT(1,1) + 1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '+' +SELECT POINT(1,1) - 1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '-' +SELECT POINT(1,1) * 1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '*' +SELECT POINT(1,1) / 1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '/' +SELECT POINT(1,1) MOD 1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '%' +SELECT 1 + POINT(1,1); +ERROR HY000: Illegal parameter data types bigint and geometry for operation '+' +SELECT 1 - POINT(1,1); +ERROR HY000: Illegal parameter data types bigint and geometry for operation '-' +SELECT 1 * POINT(1,1); +ERROR HY000: Illegal parameter data types bigint and geometry for operation '*' +SELECT 1 / POINT(1,1); +ERROR HY000: Illegal parameter data types bigint and geometry for operation '/' +SELECT 1 MOD POINT(1,1); +ERROR HY000: Illegal parameter data types bigint and geometry for operation '%' +SELECT a + 1 FROM t1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '+' +SELECT a - 1 FROM t1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '-' +SELECT a * 1 FROM t1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '*' +SELECT a / 1 FROM t1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '/' +SELECT a MOD 1 FROM t1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '%' +SELECT 1 + a FROM t1; +ERROR HY000: Illegal parameter data types bigint and geometry for operation '+' +SELECT 1 - a FROM t1; +ERROR HY000: Illegal parameter data types bigint and geometry for operation '-' +SELECT 1 * a FROM t1; +ERROR HY000: Illegal parameter data types bigint and geometry for operation '*' +SELECT 1 / a FROM t1; +ERROR HY000: Illegal parameter data types bigint and geometry for operation '/' +SELECT 1 MOD a FROM t1; +ERROR HY000: Illegal parameter data types bigint and geometry for operation '%' +SELECT COALESCE(a) + 1 FROM t1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '+' +SELECT COALESCE(a) - 1 FROM t1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '-' +SELECT COALESCE(a) * 1 FROM t1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '*' +SELECT COALESCE(a) / 1 FROM t1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '/' +SELECT COALESCE(a) MOD 1 FROM t1; +ERROR HY000: Illegal parameter data types geometry and bigint for operation '%' +SELECT 1 + COALESCE(a) FROM t1; +ERROR HY000: Illegal parameter data types bigint and geometry for operation '+' +SELECT 1 - COALESCE(a) FROM t1; +ERROR HY000: Illegal parameter data types bigint and geometry for operation '-' +SELECT 1 * COALESCE(a) FROM t1; +ERROR HY000: Illegal parameter data types bigint and geometry for operation '*' +SELECT 1 / COALESCE(a) FROM t1; +ERROR HY000: Illegal parameter data types bigint and geometry for operation '/' +SELECT 1 MOD COALESCE(a) FROM t1; +ERROR HY000: Illegal parameter data types bigint and geometry for operation '%' +DROP TABLE t1; +# # End of 10.3 tests # diff --git a/mysql-test/t/gis-debug.test b/mysql-test/t/gis-debug.test index a34dd6223122b..588bc70637004 100644 --- a/mysql-test/t/gis-debug.test +++ b/mysql-test/t/gis-debug.test @@ -46,3 +46,68 @@ DROP TABLE t1; SET SESSION debug_dbug="-d,Predicant_to_list_comparator"; SET SESSION debug_dbug="-d,Item_func_in"; + + +--echo # +--echo # MDEV-12238 Add Type_handler::Item_func_{plus|minus|mul|div|mod}_fix_length_and_dec() +--echo # + +# This tests is to check that operators '+' and '*' are commutative, +# while operators '/', '-' and 'MOD' are not commutative. +# +# It forces substitution of type_aggregator_for_{plus|minus|mul|div|mod} to +# type_aggregator_for_result / type_aggregator_non_commutative_test, +# which have pairs: +# (GEOMETRY,GEOMETRY)->GEOMETRY +# (GEOMETRY,VARCHAR)->GEOMETRY +# Note, they don't not have a pair: +# (VARCHAR,GEOMETRY)->GEOMETRY +# +# Commutative operators should work for all these argument type combinations: +# (GEOMETRY,GEOMETRY), (GEOMETRY,VARCHAR), (VARCHAR,GEOMETRY). +# Non-commutative operators should work for: +# (GEOMETRY,GEOMETRY), (GEOMETRY,VARCHAR), +# but should fail for (VARCHAR,GEOMETRY). +# +# Note, LIMIT 0 is needed to avoid calling str_op(), which does DBUG_ASSERT(0). + +SET debug_dbug='+d,num_op'; + +# (GEOMETRY,GEOMETRY) gives GEOMETRY for all operators +CREATE TABLE t1 AS SELECT + POINT(0,0)+POINT(0,0), + POINT(0,0)-POINT(0,0), + POINT(0,0)*POINT(0,0), + POINT(0,0)/POINT(0,0), + POINT(0,0) MOD POINT(0,0) LIMIT 0; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +# (GEOMETRY,VARCHAR) gives GEOMETRY for all operators +CREATE TABLE t1 AS SELECT + POINT(0,0)+'0', + POINT(0,0)-'0', + POINT(0,0)*'0', + POINT(0,0)/'0', + POINT(0,0) MOD '0' LIMIT 0; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +# (VARCHAR,GEOMETRY) gives GEOMETRY for commutative operators +CREATE TABLE t1 AS SELECT + '0'+POINT(0,0), + '0'*POINT(0,0) LIMIT 0; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +# (VARCHAR,GEOMETRY) gives an error for non-commutative operators +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +CREATE TABLE t1 AS SELECT '0'-POINT(0,0) LIMIT 0; + +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +CREATE TABLE t1 AS SELECT '0'/POINT(0,0) LIMIT 0; + +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +CREATE TABLE t1 AS SELECT '0' MOD POINT(0,0) LIMIT 0; + +SET debug_dbug='-d,num_op'; diff --git a/mysql-test/t/gis.test b/mysql-test/t/gis.test index ee074c8721535..a19de83f0466b 100644 --- a/mysql-test/t/gis.test +++ b/mysql-test/t/gis.test @@ -2016,6 +2016,81 @@ SELECT LENGTH(CAST(COALESCE(a) AS BINARY)) FROM t1; DROP TABLE t1; +--echo # +--echo # MDEV-12238 Add Type_handler::Item_func_{plus|minus|mul|div|mod}_fix_length_and_dec() +--echo # + +CREATE TABLE t1 (a GEOMETRY); + +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT POINT(1,1) + 1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT POINT(1,1) - 1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT POINT(1,1) * 1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT POINT(1,1) / 1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT POINT(1,1) MOD 1; + +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 + POINT(1,1); +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 - POINT(1,1); +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 * POINT(1,1); +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 / POINT(1,1); +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 MOD POINT(1,1); + +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT a + 1 FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT a - 1 FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT a * 1 FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT a / 1 FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT a MOD 1 FROM t1; + +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 + a FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 - a FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 * a FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 / a FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 MOD a FROM t1; + +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT COALESCE(a) + 1 FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT COALESCE(a) - 1 FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT COALESCE(a) * 1 FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT COALESCE(a) / 1 FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT COALESCE(a) MOD 1 FROM t1; + +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 + COALESCE(a) FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 - COALESCE(a) FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 * COALESCE(a) FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 / COALESCE(a) FROM t1; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT 1 MOD COALESCE(a) FROM t1; + +DROP TABLE t1; + + --echo # --echo # End of 10.3 tests --echo # diff --git a/sql/item_func.cc b/sql/item_func.cc index 8b52ed3c613ab..03d0b1068a27f 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -729,47 +729,31 @@ bool Item_func_connection_id::fix_fields(THD *thd, Item **ref) } -/** - Check arguments here to determine result's type for a numeric - function of two arguments. -*/ - -void Item_num_op::fix_length_and_dec(void) +bool Item_num_op::fix_type_handler(const Type_aggregator *aggregator) { - DBUG_ENTER("Item_num_op::fix_length_and_dec"); - DBUG_PRINT("info", ("name %s", func_name())); DBUG_ASSERT(arg_count == 2); - Item_result r0= args[0]->cast_to_int_type_handler()->cmp_type(); - Item_result r1= args[1]->cast_to_int_type_handler()->cmp_type(); + const Type_handler *h0= args[0]->cast_to_int_type_handler(); + const Type_handler *h1= args[1]->cast_to_int_type_handler(); + if (!aggregate_for_num_op(aggregator, h0, h1)) + return false; + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), + h0->name().ptr(), h1->name().ptr(), func_name()); + return true; +} - if (r0 == REAL_RESULT || r1 == REAL_RESULT || - r0 == STRING_RESULT || r1 ==STRING_RESULT) - { - count_real_length(args, arg_count); - max_length= float_length(decimals); - set_handler_by_result_type(REAL_RESULT); - } - else if (r0 == DECIMAL_RESULT || r1 == DECIMAL_RESULT || - r0 == TIME_RESULT || r1 == TIME_RESULT) - { - set_handler_by_result_type(DECIMAL_RESULT); - result_precision(); - fix_decimals(); - if ((r0 == TIME_RESULT || r1 == TIME_RESULT) && decimals == 0) - set_handler_by_result_type(INT_RESULT); - } - else + +void Item_func_plus::fix_length_and_dec(void) +{ + DBUG_ENTER("Item_func_plus::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); + const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_plus; + DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_for_result;); + DBUG_ASSERT(aggregator->is_commutative()); + if (!fix_type_handler(aggregator)) { - DBUG_ASSERT(r0 == INT_RESULT && r1 == INT_RESULT); - set_handler_by_result_type(INT_RESULT); - result_precision(); - decimals= 0; + Item_func_plus::type_handler()->Item_func_plus_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); } - DBUG_PRINT("info", ("Type: %s", - (result_type() == REAL_RESULT ? "REAL_RESULT" : - result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" : - result_type() == INT_RESULT ? "INT_RESULT" : - "--ILLEGAL!!!--"))); DBUG_VOID_RETURN; } @@ -1299,11 +1283,6 @@ void Item_func_additive_op::result_precision() DBUG_ASSERT(arg1_int >= 0); DBUG_ASSERT(arg2_int >= 0); - /* Integer operations keep unsigned_flag if one of arguments is unsigned */ - if (result_type() == INT_RESULT) - unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; - else - unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, unsigned_flag); } @@ -1313,16 +1292,30 @@ void Item_func_additive_op::result_precision() The following function is here to allow the user to force subtraction of UNSIGNED BIGINT to return negative values. */ - -void Item_func_minus::fix_length_and_dec() +void Item_func_minus::fix_unsigned_flag() { - Item_num_op::fix_length_and_dec(); if (unsigned_flag && (current_thd->variables.sql_mode & MODE_NO_UNSIGNED_SUBTRACTION)) unsigned_flag=0; } +void Item_func_minus::fix_length_and_dec() +{ + DBUG_ENTER("Item_func_minus::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); + const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_minus; + DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_non_commutative_test;); + DBUG_ASSERT(!aggregator->is_commutative()); + if (!fix_type_handler(aggregator)) + { + Item_func_minus::type_handler()->Item_func_minus_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); + } + DBUG_VOID_RETURN; +} + + double Item_func_minus::real_op() { double value= args[0]->val_real() - args[1]->val_real(); @@ -1530,13 +1523,8 @@ my_decimal *Item_func_mul::decimal_op(my_decimal *decimal_value) void Item_func_mul::result_precision() { - /* Integer operations keep unsigned_flag if one of arguments is unsigned */ - if (result_type() == INT_RESULT) - unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; - else - unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; decimals= MY_MIN(args[0]->decimal_scale() + args[1]->decimal_scale(), - DECIMAL_MAX_SCALE); + DECIMAL_MAX_SCALE); uint est_prec = args[0]->decimal_precision() + args[1]->decimal_precision(); uint precision= MY_MIN(est_prec, DECIMAL_MAX_PRECISION); max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, @@ -1544,6 +1532,22 @@ void Item_func_mul::result_precision() } +void Item_func_mul::fix_length_and_dec(void) +{ + DBUG_ENTER("Item_func_mul::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); + const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_mul; + DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_for_result;); + DBUG_ASSERT(aggregator->is_commutative()); + if (!fix_type_handler(aggregator)) + { + Item_func_mul::type_handler()->Item_func_mul_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); + } + DBUG_VOID_RETURN; +} + + double Item_func_div::real_op() { DBUG_ASSERT(fixed == 1); @@ -1605,53 +1609,51 @@ void Item_func_div::result_precision() uint precision=MY_MIN(args[0]->decimal_precision() + args[1]->divisor_precision_increment() + prec_increment, DECIMAL_MAX_PRECISION); - - /* Integer operations keep unsigned_flag if one of arguments is unsigned */ - if (result_type() == INT_RESULT) - unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; - else - unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; decimals= MY_MIN(args[0]->decimal_scale() + prec_increment, DECIMAL_MAX_SCALE); max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, unsigned_flag); } -void Item_func_div::fix_length_and_dec() +void Item_func_div::fix_length_and_dec_double(void) +{ + Item_num_op::fix_length_and_dec_double(); + decimals= MY_MAX(args[0]->decimals, args[1]->decimals) + prec_increment; + set_if_smaller(decimals, NOT_FIXED_DEC); + uint tmp= float_length(decimals); + if (decimals == NOT_FIXED_DEC) + max_length= tmp; + else + { + max_length=args[0]->max_length - args[0]->decimals + decimals; + set_if_smaller(max_length, tmp); + } +} + + +void Item_func_div::fix_length_and_dec_int(void) +{ + set_handler(&type_handler_newdecimal); + DBUG_PRINT("info", ("Type changed: %s", type_handler()->name().ptr())); + Item_num_op::fix_length_and_dec_decimal(); +} + + +void Item_func_div::fix_length_and_dec(void) { DBUG_ENTER("Item_func_div::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); prec_increment= current_thd->variables.div_precincrement; - Item_num_op::fix_length_and_dec(); - switch (Item_func_div::result_type()) { - case REAL_RESULT: + maybe_null= 1; // devision by zero + + const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_div; + DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_non_commutative_test;); + DBUG_ASSERT(!aggregator->is_commutative()); + if (!fix_type_handler(aggregator)) { - decimals=MY_MAX(args[0]->decimals,args[1]->decimals)+prec_increment; - set_if_smaller(decimals, NOT_FIXED_DEC); - uint tmp=float_length(decimals); - if (decimals == NOT_FIXED_DEC) - max_length= tmp; - else - { - max_length=args[0]->max_length - args[0]->decimals + decimals; - set_if_smaller(max_length,tmp); - } - break; - } - case INT_RESULT: - set_handler_by_result_type(DECIMAL_RESULT); - DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT")); - result_precision(); - break; - case DECIMAL_RESULT: - result_precision(); - fix_decimals(); - break; - case STRING_RESULT: - case ROW_RESULT: - case TIME_RESULT: - DBUG_ASSERT(0); + Item_func_div::type_handler()->Item_func_div_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); } - maybe_null= 1; // devision by zero DBUG_VOID_RETURN; } @@ -1823,9 +1825,18 @@ void Item_func_mod::result_precision() void Item_func_mod::fix_length_and_dec() { - Item_num_op::fix_length_and_dec(); - maybe_null= 1; - unsigned_flag= args[0]->unsigned_flag; + DBUG_ENTER("Item_func_mod::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); + maybe_null= true; // division by zero + const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_mod; + DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_non_commutative_test;); + DBUG_ASSERT(!aggregator->is_commutative()); + if (!fix_type_handler(aggregator)) + { + Item_func_mod::type_handler()->Item_func_mod_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); + } + DBUG_VOID_RETURN; } diff --git a/sql/item_func.h b/sql/item_func.h index 0b398adb9371c..7f9321f017d21 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -695,7 +695,31 @@ class Item_num_op :public Item_func_numhybrid { print_op(str, query_type); } - void fix_length_and_dec(); + bool fix_type_handler(const Type_aggregator *aggregator); + void fix_length_and_dec_double() + { + count_real_length(args, arg_count); + max_length= float_length(decimals); + } + void fix_length_and_dec_decimal() + { + unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; + result_precision(); + fix_decimals(); + } + void fix_length_and_dec_int() + { + unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; + result_precision(); + decimals= 0; + } + void fix_length_and_dec_temporal() + { + set_handler(&type_handler_newdecimal); + fix_length_and_dec_decimal(); + if (decimals == 0) + set_handler(&type_handler_longlong); + } bool need_parentheses_in_default() { return true; } }; @@ -949,6 +973,7 @@ class Item_func_plus :public Item_func_additive_op Item_func_additive_op(thd, a, b) {} const char *func_name() const { return "+"; } enum precedence precedence() const { return ADD_PRECEDENCE; } + void fix_length_and_dec(); longlong int_op(); double real_op(); my_decimal *decimal_op(my_decimal *); @@ -967,6 +992,22 @@ class Item_func_minus :public Item_func_additive_op double real_op(); my_decimal *decimal_op(my_decimal *); void fix_length_and_dec(); + void fix_unsigned_flag(); + void fix_length_and_dec_double() + { + Item_func_additive_op::fix_length_and_dec_double(); + fix_unsigned_flag(); + } + void fix_length_and_dec_decimal() + { + Item_func_additive_op::fix_length_and_dec_decimal(); + fix_unsigned_flag(); + } + void fix_length_and_dec_int() + { + Item_func_additive_op::fix_length_and_dec_int(); + fix_unsigned_flag(); + } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy(thd, mem_root, this); } }; @@ -983,6 +1024,7 @@ class Item_func_mul :public Item_num_op double real_op(); my_decimal *decimal_op(my_decimal *); void result_precision(); + void fix_length_and_dec(); bool check_partition_func_processor(void *int_arg) {return FALSE;} bool check_vcol_func_processor(void *arg) { return FALSE;} Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -1001,6 +1043,8 @@ class Item_func_div :public Item_num_op const char *func_name() const { return "/"; } enum precedence precedence() const { return MUL_PRECEDENCE; } void fix_length_and_dec(); + void fix_length_and_dec_double(); + void fix_length_and_dec_int(); void result_precision(); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy(thd, mem_root, this); } @@ -1040,6 +1084,22 @@ class Item_func_mod :public Item_num_op enum precedence precedence() const { return MUL_PRECEDENCE; } void result_precision(); void fix_length_and_dec(); + void fix_length_and_dec_double() + { + Item_num_op::fix_length_and_dec_double(); + unsigned_flag= args[0]->unsigned_flag; + } + void fix_length_and_dec_decimal() + { + Item_num_op::fix_length_and_dec_decimal(); + unsigned_flag= args[0]->unsigned_flag; + } + void fix_length_and_dec_int() + { + max_length= MY_MAX(args[0]->max_length, args[1]->max_length); + decimals= 0; + unsigned_flag= args[0]->unsigned_flag; + } bool check_partition_func_processor(void *int_arg) {return FALSE;} bool check_vcol_func_processor(void *arg) { return FALSE;} Item *get_copy(THD *thd, MEM_ROOT *mem_root) diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 055a89697872e..337111f6e535e 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -60,6 +60,17 @@ Type_handler_geometry type_handler_geometry; bool Type_handler_data::init() { #ifdef HAVE_SPATIAL + +#ifndef DBUG_OFF + if (m_type_aggregator_non_commutative_test.add(&type_handler_geometry, + &type_handler_geometry, + &type_handler_geometry) || + m_type_aggregator_non_commutative_test.add(&type_handler_geometry, + &type_handler_varchar, + &type_handler_long_blob)) + return true; +#endif + return m_type_aggregator_for_result.add(&type_handler_geometry, &type_handler_null, @@ -466,6 +477,63 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h) } +const Type_handler * +Type_handler::aggregate_for_num_op_traditional(const Type_handler *h0, + const Type_handler *h1) +{ + Item_result r0= h0->cmp_type(); + Item_result r1= h1->cmp_type(); + + if (r0 == REAL_RESULT || r1 == REAL_RESULT || + r0 == STRING_RESULT || r1 ==STRING_RESULT) + return &type_handler_double; + + if (r0 == TIME_RESULT || r1 == TIME_RESULT) + return &type_handler_datetime; + + if (r0 == DECIMAL_RESULT || r1 == DECIMAL_RESULT) + return &type_handler_newdecimal; + + DBUG_ASSERT(r0 == INT_RESULT && r1 == INT_RESULT); + return &type_handler_longlong; +} + + +const Type_aggregator::Pair* +Type_aggregator::find_pair(const Type_handler *handler1, + const Type_handler *handler2) const +{ + for (uint i= 0; i < m_array.elements(); i++) + { + const Pair& el= m_array.at(i); + if (el.eq(handler1, handler2) || + (m_is_commutative && el.eq(handler2, handler1))) + return ⪙ + } + return NULL; +} + + +bool +Type_handler_hybrid_field_type::aggregate_for_num_op(const Type_aggregator *agg, + const Type_handler *h0, + const Type_handler *h1) +{ + const Type_handler *hres; + if (h0->is_traditional_type() && h1->is_traditional_type()) + { + set_handler(Type_handler::aggregate_for_num_op_traditional(h0, h1)); + return false; + } + if ((hres= agg->find_handler(h0, h1))) + { + set_handler(hres); + return false; + } + return true; +} + + /***************************************************************************/ const Type_handler * @@ -2709,3 +2777,248 @@ bool Type_handler_geometry:: #endif /* HAVE_SPATIAL */ /***************************************************************************/ + +bool Type_handler_row:: + Item_func_plus_fix_length_and_dec(Item_func_plus *item) const +{ + DBUG_ASSERT(0); + return true; +} + + +bool Type_handler_int_result:: + Item_func_plus_fix_length_and_dec(Item_func_plus *item) const +{ + item->fix_length_and_dec_int(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_plus_fix_length_and_dec(Item_func_plus *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_func_plus_fix_length_and_dec(Item_func_plus *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_func_plus_fix_length_and_dec(Item_func_plus *item) const +{ + item->fix_length_and_dec_temporal(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_plus_fix_length_and_dec(Item_func_plus *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + +/***************************************************************************/ + +bool Type_handler_row:: + Item_func_minus_fix_length_and_dec(Item_func_minus *item) const +{ + DBUG_ASSERT(0); + return true; +} + + +bool Type_handler_int_result:: + Item_func_minus_fix_length_and_dec(Item_func_minus *item) const +{ + item->fix_length_and_dec_int(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_minus_fix_length_and_dec(Item_func_minus *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_func_minus_fix_length_and_dec(Item_func_minus *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_func_minus_fix_length_and_dec(Item_func_minus *item) const +{ + item->fix_length_and_dec_temporal(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_minus_fix_length_and_dec(Item_func_minus *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + +/***************************************************************************/ + +bool Type_handler_row:: + Item_func_mul_fix_length_and_dec(Item_func_mul *item) const +{ + DBUG_ASSERT(0); + return true; +} + + +bool Type_handler_int_result:: + Item_func_mul_fix_length_and_dec(Item_func_mul *item) const +{ + item->fix_length_and_dec_int(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_mul_fix_length_and_dec(Item_func_mul *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_func_mul_fix_length_and_dec(Item_func_mul *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_func_mul_fix_length_and_dec(Item_func_mul *item) const +{ + item->fix_length_and_dec_temporal(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_mul_fix_length_and_dec(Item_func_mul *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + +/***************************************************************************/ + +bool Type_handler_row:: + Item_func_div_fix_length_and_dec(Item_func_div *item) const +{ + DBUG_ASSERT(0); + return true; +} + + +bool Type_handler_int_result:: + Item_func_div_fix_length_and_dec(Item_func_div *item) const +{ + item->fix_length_and_dec_int(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_div_fix_length_and_dec(Item_func_div *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_func_div_fix_length_and_dec(Item_func_div *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_func_div_fix_length_and_dec(Item_func_div *item) const +{ + item->fix_length_and_dec_temporal(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_div_fix_length_and_dec(Item_func_div *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + +/***************************************************************************/ + +bool Type_handler_row:: + Item_func_mod_fix_length_and_dec(Item_func_mod *item) const +{ + DBUG_ASSERT(0); + return true; +} + + +bool Type_handler_int_result:: + Item_func_mod_fix_length_and_dec(Item_func_mod *item) const +{ + item->fix_length_and_dec_int(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_mod_fix_length_and_dec(Item_func_mod *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_func_mod_fix_length_and_dec(Item_func_mod *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_func_mod_fix_length_and_dec(Item_func_mod *item) const +{ + item->fix_length_and_dec_temporal(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_mod_fix_length_and_dec(Item_func_mod *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + +/***************************************************************************/ diff --git a/sql/sql_type.h b/sql/sql_type.h index 053132626a3b4..891c785fef20e 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -51,6 +51,11 @@ class Item_char_typecast; class Item_time_typecast; class Item_date_typecast; class Item_datetime_typecast; +class Item_func_plus; +class Item_func_minus; +class Item_func_mul; +class Item_func_div; +class Item_func_mod; class cmp_item; class in_vector; class Type_std_attributes; @@ -303,6 +308,9 @@ class Type_handler static const Type_handler *aggregate_for_result_traditional(const Type_handler *h1, const Type_handler *h2); + static const + Type_handler *aggregate_for_num_op_traditional(const Type_handler *h1, + const Type_handler *h2); virtual const Name name() const= 0; virtual enum_field_types field_type() const= 0; @@ -497,6 +505,17 @@ class Type_handler Item_date_typecast_fix_length_and_dec(Item_date_typecast *item) const; virtual bool Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *item) const; + + virtual bool + Item_func_plus_fix_length_and_dec(Item_func_plus *func) const= 0; + virtual bool + Item_func_minus_fix_length_and_dec(Item_func_minus *func) const= 0; + virtual bool + Item_func_mul_fix_length_and_dec(Item_func_mul *func) const= 0; + virtual bool + Item_func_div_fix_length_and_dec(Item_func_div *func) const= 0; + virtual bool + Item_func_mod_fix_length_and_dec(Item_func_mod *func) const= 0; }; @@ -708,6 +727,12 @@ class Type_handler_row: public Type_handler DBUG_ASSERT(0); return true; } + + bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const; + bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const; + bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const; + bool Item_func_div_fix_length_and_dec(Item_func_div *) const; + bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const; }; @@ -784,6 +809,11 @@ class Type_handler_real_result: public Type_handler_numeric bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const; bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const; + bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const; + bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const; + bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const; + bool Item_func_div_fix_length_and_dec(Item_func_div *) const; + bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const; }; @@ -833,6 +863,11 @@ class Type_handler_decimal_result: public Type_handler_numeric bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const; bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const; + bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const; + bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const; + bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const; + bool Item_func_div_fix_length_and_dec(Item_func_div *) const; + bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const; }; @@ -881,6 +916,11 @@ class Type_handler_int_result: public Type_handler_numeric bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const; bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const; + bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const; + bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const; + bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const; + bool Item_func_div_fix_length_and_dec(Item_func_div *) const; + bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const; }; @@ -933,6 +973,11 @@ class Type_handler_temporal_result: public Type_handler bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const; bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const; + bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const; + bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const; + bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const; + bool Item_func_div_fix_length_and_dec(Item_func_div *) const; + bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const; }; @@ -999,6 +1044,11 @@ class Type_handler_string_result: public Type_handler bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const; bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const; + bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const; + bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const; + bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const; + bool Item_func_div_fix_length_and_dec(Item_func_div *) const; + bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const; }; @@ -1528,6 +1578,8 @@ class Type_handler_hybrid_field_type bool aggregate_for_result(const Type_handler *other); bool aggregate_for_result(const char *funcname, Item **item, uint nitems, bool treat_bit_as_number); + bool aggregate_for_num_op(const class Type_aggregator *aggregator, + const Type_handler *h0, const Type_handler *h1); }; @@ -1547,6 +1599,7 @@ extern Type_handler_set type_handler_set; class Type_aggregator { + bool m_is_commutative; class Pair { public: @@ -1566,18 +1619,10 @@ class Type_aggregator }; Dynamic_array m_array; const Pair* find_pair(const Type_handler *handler1, - const Type_handler *handler2) const - { - for (uint i= 0; i < m_array.elements(); i++) - { - const Pair& el= m_array.at(i); - if (el.eq(handler1, handler2) || el.eq(handler2, handler1)) - return ⪙ - } - return NULL; - } + const Type_handler *handler2) const; public: - Type_aggregator() + Type_aggregator(bool is_commutative= false) + :m_is_commutative(is_commutative) { } bool add(const Type_handler *handler1, const Type_handler *handler2, @@ -1591,14 +1636,35 @@ class Type_aggregator const Pair* el= find_pair(handler1, handler2); return el ? el->m_result : NULL; } + bool is_commutative() const { return m_is_commutative; } +}; + + +class Type_aggregator_commutative: public Type_aggregator +{ +public: + Type_aggregator_commutative() + :Type_aggregator(true) + { } }; class Type_handler_data { public: - Type_aggregator m_type_aggregator_for_result; - Type_aggregator m_type_aggregator_for_comparison; + Type_aggregator_commutative m_type_aggregator_for_result; + Type_aggregator_commutative m_type_aggregator_for_comparison; + + Type_aggregator_commutative m_type_aggregator_for_plus; + Type_aggregator_commutative m_type_aggregator_for_mul; + + Type_aggregator m_type_aggregator_for_minus; + Type_aggregator m_type_aggregator_for_div; + Type_aggregator m_type_aggregator_for_mod; +#ifndef DBUG_OFF + // This is used for mtr purposes in debug builds + Type_aggregator m_type_aggregator_non_commutative_test; +#endif bool init(); };