matrixorigin / matrixorigin/matrixone
[Bug]: unquoted DECIMAL256 literals lose fractional digits before evaluation
- Dominant language
- Go
- Stars
- 1.9k
- Forks
- 311
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 768
Description
## Description
An unquoted decimal literal that requires the `DECIMAL256` domain silently loses fractional digits before evaluation. The loss starts at 39 significant digits: a literal with the same spelling as a stored `DECIMAL(39,1)`, `DECIMAL(40,2)`, or `DECIMAL(65,30)` value does not compare equal to it.
This is distinct from #26897. That issue fixed exact coercion for text string constants paired with DECIMAL. Here the failing operand is an unquoted numeric literal; the text-literal and explicit-DECIMAL controls are correct on the same revision.
## Environment
- Branch: `main`
- Commit: `4f9d170a22deaabc2eee6b239a51a8cb40a39f9f`
- Deployment: local one Log service, one TN service, two CN services, launched with `etc/launch-multi-cn/launch.toml`; direct MySQL-wire connection to CN on port 16001.
## Steps to reproduce
```sql
CREATE DATABASE decimal256_unquoted_literal;
USE decimal256_unquoted_literal;
CREATE TABLE t (id INT PRIMARY KEY, v DECIMAL(65,30));
INSERT INTO t VALUES
(1, 12345678901234567890123456789012345.123456789012345678901234567890);
SELECT 12345678901234567890123456789012345.123456789012345678901234567890;
SELECT id FROM t
WHERE v = 12345678901234567890123456789012345.123456789012345678901234567890;
SELECT id FROM t
WHERE v = CAST(
'12345678901234567890123456789012345.123456789012345678901234567890'
AS DECIMAL(65,30)
);
```
## Actual behavior
The scalar literal is returned as:
```text
12345678901234567890123456789012345.123
```
The equality predicate returns no rows. `EXPLAIN` records the already-truncated literal:
```text
Filter Cond: (t.v = cast(12345678901234567890123456789012345.123 AS DECIMAL256))
```
The same outcome occurs with exact direct literals at `DECIMAL(39,1)`, `DECIMAL(40,2)`, and `DECIMAL(65,30)`.
## Expected behavior
The unquoted literal must preserve its complete numeric value. It has the same value as the inserted `DECIMAL(65,30)` value, so the equality predicate must return `id = 1`. Direct scalar evaluation must retain all 30 fractional digits.
## Stability and controls
- The full boundary matrix reproduced identically in 3/3 independent databases on the stated revision.
- `DECIMAL(38,18)` is a passing control: its same-spelling unquoted literal returns `id = 1`.
- At `DECIMAL(39,1)`, `DECIMAL(40,2)`, and `DECIMAL(65,30)`, the same-spelling unquoted literal returns no row while the explicit `CAST(... AS DECIMAL(M,D))` control returns `id = 1`.
- A quoted literal containing the same 65-digit decimal spelling also returns `id = 1` for the `DECIMAL(65,30)` table.
- The statements are read-only after fixture insertion; rows remained unchanged after every repetition.
## Evidence
The saved local matrix is `evidence/types/decimal256_numeric_literal_boundary.sql`; its three current-main outputs are `decimal256_numeric_literal_boundary_main_4f9d170_run7.out` through `run9.out`. After replacing only the isolated database names, all outputs are byte-identical.
## Code analysis
The failing public path enters `baseBinder.bindNumVal` in `pkg/sql/plan/base_binder.go` for `tree.P_decimal`, then selects `makePlan2DecimalExprWithType` in `pkg/sql/plan/make.go` once `types.Parse128` cannot represent the value. `makePlan2DecimalExprWithType` constructs a `DECIMAL256` cast from the literal's original string, while direct `Parse256` and explicit-cast controls retain the full value. The exact point at which this bind/execution path reduces the effective scale is not yet confirmed; the `EXPLAIN` output proves the reduction happens before the predicate is executed.
## Regression coverage
Add a deterministic decimal planner/executor regression after the fix. It should assert scalar value and equality row set for 38-digit `DECIMAL128` and 39-, 40-, and 65-digit `DECIMAL256` unquoted literals, with explicit-CAST and text-literal controls.
## Related
- #26897: text string literal DECIMAL coercion, fixed separately
- #27088: closed tracking issue for earlier DECIMAL coercion and prepared-runtime manifestations
Contributor guide
Assessment
This issue has not been assessed yet.