matrixorigin / matrixorigin/matrixone

[Bug]: CASE, IF, and IFNULL discard temporal fractional-second precision

Open
#28,232 0 comments 0 reactions 1 assignee Claimed by @jiangxinmeng1 View on GitHub
kind/bug needs-triage
Dominant language
Go
Stars
1.9k
Forks
311
Avg merge
1d 3h
Merged PRs (30d)
768

Description

## 问题

`CASE`、`IF` 和基于 `CASE` 重写的 `IFNULL` 在推导时间结果类型时丢失 fractional-seconds precision(FSP)。`DATETIME(6)`/`TIME(6)` 分支会被降为 `DATETIME(0)`,微秒被删除,且大于等于半秒的值被四舍五入到下一秒。

该问题也发生在 `CASE WHEN ... THEN datetime_col ELSE NULL END` 这种没有不同时间类型参与合流的表达式中,因此不是 DATE 到 DATETIME 转换本身的限制。

## 环境

- MatrixOne: official `main`
- commit: `e7bb0572235ec4bac81eb098eb0ad8900f0065ab`
- comparison: MySQL 8.3

## 复现

```sql
create database conditional_fsp_repro;
use conditional_fsp_repro;

create table t(
id int primary key,
d date,
dt datetime(6),
tm time(6)
);

insert into t values
(1, '2024-01-01', '2024-01-01 01:02:03.123456', '12:34:56.123456'),
(2, '2024-01-02', '2024-01-02 02:03:04.654321', '23:59:59.999999');

select id, dt, microsecond(dt) from t order by id;

select id,
if(id=1,d,dt) as v,
microsecond(if(id=1,d,dt)) as us
from t order by id;

select id,
case when id=1 then d else dt end as v,
microsecond(case when id=1 then d else dt end) as us
from t order by id;

select id,
case when id=1 then dt else null end as v,
microsecond(case when id=1 then dt else null end) as us
from t order by id;

select id,
ifnull(case when id=1 then d end,dt) as v,
microsecond(ifnull(case when id=1 then d end,dt)) as us
from t order by id;

create table copied as
select if(id=1,d,dt) as if_v,
case when id=1 then tm else dt end as case_v
from t;

select column_name,column_type,datetime_precision
from information_schema.columns
where table_schema='conditional_fsp_repro' and table_name='copied'
order by ordinal_position;
```

MatrixOne 的源列保留了 `123456` 和 `654321` 微秒;进入上述条件表达式后,结果包括:

```text
IF/CASE row 2: 2024-01-02 02:03:05, microsecond = 0
CASE datetime ELSE NULL row 1: 2024-01-01 01:02:03, microsecond = 0
CTAS if_v: DATETIME(0)
CTAS case_v: DATETIME(0)
```

MySQL 8.3 保留 `2024-01-02 02:03:04.654321` 和对应微秒,CTAS 两列均为 `DATETIME(6)`。

prepared statement 中的相同 `IF` 表达式在 MatrixOne 也返回 `2024-01-02 02:03:05`,说明不是仅限 ad-hoc SQL 的展示问题。

## 代码定位

`pkg/sql/plan/function/operatorSet.go` 的 `caseCheck` / `iffCheck` 在选择公共时间类型时由 OID 重建 `types.Type`,没有合并来源分支的 scale/FSP。随后插入的时间 CAST 使用 FSP 0,造成值被舍入。`IFNULL` 在 `pkg/sql/plan/query_builder.go` 重写为 CASE,因此继承同一问题。

## 期望

条件表达式的公共时间类型保留所有可达值分支中的最大 FSP。上述查询和 CTAS 应保留 `DATETIME(6)` 值及微秒,与 MySQL 行为一致;NULL 分支不应降低非 NULL 分支的 FSP。

以上查询、窗口外嵌函数和 prepared statement 场景重复执行 3 次,结果稳定。

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.