diff --git a/mysql-test/main/func_math.result b/mysql-test/main/func_math.result index 475485cbcae59..179b49e14d36c 100644 --- a/mysql-test/main/func_math.result +++ b/mysql-test/main/func_math.result @@ -3739,3 +3739,99 @@ cast(1 as unsigned) - cast(2 as unsigned) -1 set sql_mode=default; # End of 10.5 tests +# +# MDEV-38670 Minus string is converted to -0 +# +# Testing arithmetic operations with signed zero +SELECT -0.0 + 0.0; +-0.0 + 0.0 +0.0 +SELECT 0.0 + -0.0; +0.0 + -0.0 +0.0 +SELECT -0.0 + -0.0; +-0.0 + -0.0 +0.0 +SELECT -0.0 - 0.0; +-0.0 - 0.0 +0.0 +SELECT 0.0 - ''; +0.0 - '' +0 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: '' +SELECT -0.0; +-0.0 +0.0 +SELECT 0.0 * -1.0; +0.0 * -1.0 +0.00 +SELECT 0e0*-1e0; +0e0*-1e0 +0 +SELECT -''; +-'' +0 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: '' +SELECT ABS(-0.0); +ABS(-0.0) +0.0 +SELECT -0.0 % 1.0; +-0.0 % 1.0 +0.0 +# Testing comparison functions with signed zero +SELECT CASE WHEN -0.0=0.0 THEN 1.0 ELSE 0.0 END; +CASE WHEN -0.0=0.0 THEN 1.0 ELSE 0.0 END +1.0 +SELECT CASE WHEN -''=0.0 THEN 1.0 ELSE 0.0 END; +CASE WHEN -''=0.0 THEN 1.0 ELSE 0.0 END +1.0 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: '' +SELECT CASE WHEN 0e0*-1e0=0.0 THEN 1.0 ELSE 0.0 END; +CASE WHEN 0e0*-1e0=0.0 THEN 1.0 ELSE 0.0 END +1.0 +# Testing with table data +CREATE TABLE t1 (a DOUBLE, b DOUBLE); +INSERT INTO t1 VALUES +(0.0, 1.0), +(-0.0, 1.0), +(1.0, 0.0), +(1.0, -0.0), +(0.0, 0.0), +(-0.0, -0.0); +SELECT a, b, a + b AS sum_ab, a - b AS diff_ab, a * b AS mul_ab FROM t1; +a b sum_ab diff_ab mul_ab +0 1 1 -1 0 +0 1 1 -1 0 +1 0 1 1 0 +1 0 1 1 0 +0 0 0 0 0 +0 0 0 0 0 +SELECT a, b, IFNULL(a, b) AS ifnull_ab, NULLIF(a, b) AS nullif_ab FROM t1; +a b ifnull_ab nullif_ab +0 1 0 0 +0 1 0 0 +1 0 1 1 +1 0 1 1 +0 0 0 NULL +0 0 0 NULL +SELECT a, b, CASE WHEN a > 0 THEN a ELSE b END AS case_ab FROM t1; +a b case_ab +0 1 1 +0 1 1 +1 0 1 +1 0 1 +0 0 0 +0 0 0 +SELECT a, b, COALESCE(a, b) AS coalesce_ab FROM t1; +a b coalesce_ab +0 1 0 +0 1 0 +1 0 1 +1 0 1 +0 0 0 +0 0 0 +DROP TABLE t1; +# End of 13 tests diff --git a/mysql-test/main/func_math.test b/mysql-test/main/func_math.test index 363d84eab3e07..1b26cc6e1a983 100644 --- a/mysql-test/main/func_math.test +++ b/mysql-test/main/func_math.test @@ -1998,3 +1998,57 @@ select cast(1 as unsigned) - cast(2 as unsigned); set sql_mode=default; --echo # End of 10.5 tests + +--echo # +--echo # MDEV-38670 Minus string is converted to -0 +--echo # + +--source include/have_innodb.inc + +# Test arithmetic operations +--echo # Testing arithmetic operations with signed zero + +SELECT -0.0 + 0.0; +SELECT 0.0 + -0.0; +SELECT -0.0 + -0.0; + +SELECT -0.0 - 0.0; +SELECT 0.0 - ''; + +SELECT -0.0; +SELECT 0.0 * -1.0; +SELECT 0e0*-1e0; +SELECT -''; + + +SELECT ABS(-0.0); + +SELECT -0.0 % 1.0; + +# Test comparison functions +--echo # Testing comparison functions with signed zero + +SELECT CASE WHEN -0.0=0.0 THEN 1.0 ELSE 0.0 END; +SELECT CASE WHEN -''=0.0 THEN 1.0 ELSE 0.0 END; +SELECT CASE WHEN 0e0*-1e0=0.0 THEN 1.0 ELSE 0.0 END; + + +# Test with table data +--echo # Testing with table data + +CREATE TABLE t1 (a DOUBLE, b DOUBLE); +INSERT INTO t1 VALUES + (0.0, 1.0), + (-0.0, 1.0), + (1.0, 0.0), + (1.0, -0.0), + (0.0, 0.0), + (-0.0, -0.0); + +SELECT a, b, a + b AS sum_ab, a - b AS diff_ab, a * b AS mul_ab FROM t1; +SELECT a, b, IFNULL(a, b) AS ifnull_ab, NULLIF(a, b) AS nullif_ab FROM t1; +SELECT a, b, CASE WHEN a > 0 THEN a ELSE b END AS case_ab FROM t1; +SELECT a, b, COALESCE(a, b) AS coalesce_ab FROM t1; + +DROP TABLE t1; +--echo # End of 13 tests diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 24ff774e59cc3..7f8c0810fde85 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2496,7 +2496,7 @@ Item_func_ifnull::real_op() value= args[1]->val_real(); if ((null_value=args[1]->null_value)) return 0.0; - return value; + return normalize_signed_zero(value); } longlong @@ -3036,7 +3036,7 @@ Item_func_nullif::real_op() } value= args[2]->val_real(); null_value= args[2]->null_value; - return value; + return normalize_signed_zero(value); } longlong @@ -3250,7 +3250,7 @@ double Item_func_case::real_op() } res= item->val_real(); null_value=item->null_value; - return res; + return normalize_signed_zero(res); } @@ -3649,7 +3649,7 @@ double Item_func_coalesce::real_op() { double res= args[i]->val_real(); if (!args[i]->null_value) - return res; + return normalize_signed_zero(res); } null_value=1; return 0; diff --git a/sql/item_func.cc b/sql/item_func.cc index 0d2b43d100844..69f5e4c050546 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -1172,7 +1172,7 @@ double Item_func_plus::real_op() double value= args[0]->val_real() + args[1]->val_real(); if ((null_value=args[0]->null_value || args[1]->null_value)) return 0.0; - return check_float_overflow(value); + return normalize_signed_zero(check_float_overflow(value)); } #if defined(__powerpc64__) && GCC_VERSION >= 6003 && GCC_VERSION <= 10002 @@ -1338,7 +1338,7 @@ double Item_func_minus::real_op() double value= args[0]->val_real() - args[1]->val_real(); if ((null_value=args[0]->null_value || args[1]->null_value)) return 0.0; - return check_float_overflow(value); + return normalize_signed_zero(check_float_overflow(value)); } @@ -1440,7 +1440,7 @@ double Item_func_mul::real_op() double value= args[0]->val_real() * args[1]->val_real(); if ((null_value=args[0]->null_value || args[1]->null_value)) return 0.0; - return check_float_overflow(value); + return normalize_signed_zero(check_float_overflow(value)); } @@ -1522,7 +1522,7 @@ double Item_func_div::real_op() signal_divide_by_null(); return 0.0; } - return check_float_overflow(value/val2); + return normalize_signed_zero(check_float_overflow(value/val2)); } @@ -1712,7 +1712,7 @@ double Item_func_mod::real_op() signal_divide_by_null(); return 0.0; } - return fmod(value,val2); + return normalize_signed_zero(fmod(value,val2)); } @@ -1825,7 +1825,7 @@ double Item_func_neg::real_op() { double value= args[0]->val_real(); null_value= args[0]->null_value; - return -value; + return normalize_signed_zero(-value); } @@ -1931,7 +1931,7 @@ double Item_func_abs::real_op() { double value= args[0]->val_real(); null_value= args[0]->null_value; - return fabs(value); + return normalize_signed_zero(fabs(value)); } @@ -2409,7 +2409,7 @@ double Item_func_ceiling::real_op() */ volatile double value= args[0]->val_real(); null_value= args[0]->null_value; - return ceil(value); + return normalize_signed_zero(ceil(value)); } @@ -2473,7 +2473,7 @@ double Item_func_floor::real_op() */ volatile double value= args[0]->val_real(); null_value= args[0]->null_value; - return floor(value); + return normalize_signed_zero(floor(value)); } @@ -2741,7 +2741,7 @@ double Item_func_round::real_op() { longlong dec= args[1]->val_int(); if (!(null_value= args[1]->null_value)) - return my_double_round(value, dec, args[1]->unsigned_flag, truncate); + return normalize_signed_zero(my_double_round(value, dec, args[1]->unsigned_flag, truncate)); } return 0.0; } diff --git a/sql/item_func.h b/sql/item_func.h index 0be8cf61591b1..a6c89a6b5163f 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -285,6 +285,10 @@ class Item_func :public Item_func_or_sum print(&str, QT_NO_DATA_EXPANSION); my_error(ER_DATA_OUT_OF_RANGE, MYF(0), type_name, str.c_ptr_safe()); } + inline double normalize_signed_zero(double value) + { + return value == 0.0 ? 0.0 : value; // Converts -0.0 to +0.0 + } inline double raise_float_overflow() { raise_numeric_overflow("DOUBLE"); @@ -308,6 +312,7 @@ class Item_func :public Item_func_or_sum { return std::isfinite(value) ? value : raise_float_overflow(); } + /** Throw an error if the input BIGINT value represented by the (longlong value, bool unsigned flag) pair cannot be returned by the