matrixorigin / matrixorigin/matrixone

[Bug]: prepared numeric CAST truncates fractional parameters instead of rounding

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

Description

## 现象

MatrixOne `main`(`fc621e3616d229c7a29d0c80e73ed8eb1997459c`)对 PreparedStatement 参数执行 `CAST(? AS SIGNED/UNSIGNED)` 时,把带小数的 numeric 参数按字符串整数前缀截断;同一值作为 DECIMAL/DOUBLE 字面量、列或 user variable 直接 CAST 时则按数值规则舍入。

```sql
PREPARE p FROM 'SELECT CAST(? AS SIGNED),CAST(? AS UNSIGNED)';
SET @a=1.5,@b=1.5;
EXECUTE p USING @a,@b;

SET @a=-1.5,@b=-1.5;
EXECUTE p USING @a,@b;
```

MatrixOne:

```text
1 1
-1 18446744073709551615
```

MySQL 8.0.46:

```text
2 2
-2 18446744073709551614
```

MatrixOne 自身的非 PreparedStatement 控制组也说明参数路径改变了值语义:

```sql
SELECT CAST(1.5 AS SIGNED),CAST(-1.5 AS SIGNED);
-- 2, -2

CREATE TABLE src(d DECIMAL(10,1),f DOUBLE,v VARCHAR(10));
INSERT INTO src VALUES (1.5,1.5,'1.5'),(-1.5,-1.5,'-1.5');
SELECT CAST(d AS SIGNED),CAST(f AS SIGNED),CAST(v AS SIGNED) FROM src;
-- DECIMAL/DOUBLE: ±2;VARCHAR: ±1
```

在 SQL-level `PREPARE/EXECUTE USING` 与真实 MySQL binary prepared protocol 中均可复现。使用 Go MySQL driver 发送 `float64(±1.5)` 时,MatrixOne 仍返回 ±1,MySQL 返回 ±2;整数和 NULL 参数控制组一致。

该问题会进入写路径:

```sql
CREATE TABLE dst(id INT PRIMARY KEY,d DECIMAL(10,1));
PREPARE pi FROM
'INSERT INTO dst VALUES (?,CAST(? AS SIGNED))';
SET @id=1,@d=1.5;
EXECUTE pi USING @id,@d;
```

MatrixOne 写入 `1.0`,MySQL 写入 `2.0`。正负 `1.5`、`1.6`、显式 DECIMAL、DOUBLE、字符串、整数、NULL 和重复执行均表现稳定。

## 期望

PreparedStatement 应保留参数的 numeric runtime kind。DECIMAL/DOUBLE 参数进入显式整数 CAST 时应使用与直接 numeric CAST 一致的舍入规则,不能因为参数传输使用文本载体而退化为字符串前缀截断。

## 代码定位

- frontend/process 已为参数维护 `PrepareParamInteger`、`PrepareParamFloat`、`PrepareParamDecimal` 等 runtime kind。
- `pkg/sql/plan/function/func_cast.go` 的 `NewExplicitCast` / `newCast` 主要根据参数 vector 的物理 `fromType` 分派;typed DECIMAL/DOUBLE 能进入 `decimalToInt64Explicit` / `floatToInt64Explicit` 并执行舍入。
- Prepared 参数的载体仍为 text/varlen 时,执行落入 `parseSignedExplicitCastString` / `parseUnsignedExplicitCastString`,`1.5` 被当作整数前缀 `1`,没有利用已有的 `PrepareParamKind` 恢复 numeric cast 路径。

## 复现信息

- MatrixOne main:`fc621e3616d229c7a29d0c80e73ed8eb1997459c`
- MySQL:8.0.46
- SQL PREPARE 与 Go MySQL driver binary prepare 均重复执行 3 次;正负 half、非 half、DECIMAL/DOUBLE/string/integer/NULL、direct variable、列和 INSERT 路径结果一致

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.