Bug #114378 Incorrect query result caused by CONVERT_TZ
Submitted: 17 Mar 2024 12:44 Modified: 18 Mar 2024 7:50
Reporter: Ye Shiyang Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: DML Severity:S3 (Non-critical)
Version:8.0, 8.0.35 OS:Any
Assigned to: CPU Architecture:Any

[17 Mar 2024 12:44] Ye Shiyang
Description:
Dear devps, Our fuzzer may found a bug.

Consider the following statements, qeury1 and query2 should reutrn the same reuslt. However, qeury1 return 18446724062687370255 while query2  return 18446724062687370215.

```
CREATE TABLE t0(c0 FLOAT);
INSERT INTO t0(c0) VALUES(-1);

-- query1
SELECT f1 FROM (SELECT ((t0.c0) ^ (CONVERT_TZ('2001-10-22 16:13:59.9', 'GMT','MET'))) AS f1 FROM t0) as t WHERE (f1) > (1); 
+----------------------+
| f1                   |
+----------------------+
| 18446724062687370255 |
+----------------------+
1 row in set (0.00 sec)

-- query2
SELECT ALL f1 FROM (SELECT ((t0.c0) ^ (CONVERT_TZ('2001-10-22 16:13:59.9', 'GMT','MET'))) AS f1, (((t0.c0) ^ (CONVERT_TZ('2001-10-22 16:13:59.9', 'GMT','MET'))) > (1)) IS TRUE AS flag FROM t0) as t WHERE flag=1;
+----------------------+
| f1                   |
+----------------------+
| 18446724062687370215 |
+----------------------+
1 row in set (0.00 sec)
```

How to repeat:
CREATE TABLE t0(c0 FLOAT);
INSERT INTO t0(c0) VALUES(-1);

SELECT f1 FROM (SELECT ((t0.c0) ^ (CONVERT_TZ('2001-10-22 16:13:59.9', 'GMT','MET'))) AS f1 FROM t0) as t WHERE (f1) > (1); 

SELECT f1 FROM (SELECT ((t0.c0) ^ (CONVERT_TZ('2001-10-22 16:13:59.9', 'GMT','MET'))) AS f1, (((t0.c0) ^ (CONVERT_TZ('2001-10-22 16:13:59.9', 'GMT','MET'))) > (1)) IS TRUE AS flag FROM t0) as t WHERE flag=1;
[18 Mar 2024 7:49] MySQL Verification Team
Hello Ye Shiyang,

Thank you for the report and feedback

regards,
Umesh
[23 Apr 2024 16:37] Dag Wanvik
Posted by developer:
 
I see the difference with the simplified repro:
CREATE TABLE t0(c0 FLOAT);
INSERT INTO t0(c0) VALUES(-1);

SELECT f1 FROM (SELECT ((t0.c0) ^ (CONVERT_TZ('2001-10-22 16:13:59.9', '-01:00','+00:00'))) AS f1 FROM t0) as t WHERE (f1) > (1);
18446724062687380255

SELECT f1 FROM (SELECT ((t0.c0) ^ (CONVERT_TZ('2001-10-22 16:13:59.9', '-01:00','+00:00'))) AS f1 FROM t0) as t;
18446724062687380215

The reason we get differents due to different kinds of rounding: in the first case, the rounding happens
indirectly: due to an intervening decimal conversion step, the rounding happens at the decimal level:

#0:Item_func_convert_tz::get_date at item_timefunc.cc:2444:10
#1:Item_datetime_func::val_date_temporal at item_timefunc.cc:943:10
#2:Item::val_temporal_by_field_type at item.h:1907:12
#3:Item_cache_datetime::cache_value_int at item.cc:9962:24
#4:Item_cache_datetime::val_decimal at item.cc:10043:26
#5:Item::val_int_from_decimal at item.cc:480:32
#6:Item_cache_datetime::val_int at item.cc:10134:50
#7:Item::val_uint at item.h:1935:51
#8:long long Item_func_bit_two_param::eval_int_op<std::__1::bit_xor<unsigned long long>>

using my_decimal2int called from val_int_from_decimal (frame #5) above. This leads to the
"2001-10-22 16:13:59".9 (it is already a decimal) being rounded up to "2001-10-22 16:13:60" essentially. Decimal rounding
doesn't understand that 13.59.0 rounds up to 14.00, rather it rounds to the integer equivalent of 13.60.

If there is now WHERE clause, the rounding happens when a datetime value is converted to int (no cached datetime here):

#0:Item_func_convert_tz::get_date at item_timefunc.cc:2444:10
#1:Item::val_int_from_datetime at item.cc:511:7
#2:Item_datetime_func::val_int at item_timefunc.h:680:40
#3:Item::val_uint at item.h:1935:51
#4:long long Item_func_bit_two_param::eval_int_op<std::__1::bit_xor<unsigned long long>>

using TIME_to_ulonglong_datetime_round called from val_int_from_datetime (frame #1). This leads to

2001-10-22 16:13:59.9 being rounded up to 2001-10-22 16:14:00.

This difference in conversion leads to the odd result.