matrixorigin / matrixorigin/matrixone
[Compatibility]: exact math functions misparse character numeric operands
- Dominant language
- Go
- Stars
- 1.9k
- Forks
- 311
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 768
Description
### Is there an existing issue for the same bug?
- [x] I have checked open and closed issues.
#28396 covered character-to-integer conversion for string position/count functions and is closed. No issue was found for character operands of mathematical functions.
### Branch Name
main
### Commit ID
`f0c31cd4b830be32442cf329e0a3fb08aa9c16c3`
### Other Environment Information
- Local standalone MatrixOne built from the commit above
- Reference: MySQL 8.3.0
### Actual Behavior
`ABS`, `CEIL`/`CEILING`, `FLOOR`, `MOD`, `ROUND`, `SIGN`, and `TRUNCATE` do not apply MySQL's character-to-number conversion contract consistently.
For `ABS`, `MOD`, `ROUND`, `SIGN`, and `TRUNCATE`, a direct character operand is resolved through a strict integer conversion. Valid character decimals, signs, leading whitespace, and numeric prefixes are rejected:
```sql
SELECT ABS('1.5'), MOD('-1.5',2), ROUND('1.5'),
SIGN('+1.5'), TRUNCATE('1.5tail',2);
-- MatrixOne: invalid argument cast to int
-- MySQL: 1.5, -1.5, 2, 1, 1.5
```
`CEIL`, `CEILING`, and `FLOOR` have a character overload, but it calls strict `strconv.ParseFloat`. It therefore rejects leading whitespace, a valid numeric prefix followed by text, nonnumeric text, and the empty string:
```sql
SELECT CEIL(' 1.5'), FLOOR('1.5tail'), CEILING('abc');
-- MatrixOne: ParseFloat errors
-- MySQL: 2, 1, 0
```
MySQL converts a nonnumeric or empty character operand to zero for every function in this set. MatrixOne instead reports a cast/parse error in direct expressions and `VARCHAR` columns.
SQL PREPARE takes a third, inconsistent path. Numeric-prefix parameters work for several functions where the same `VARCHAR` column fails, while `SIGN` and `ABS` still reject them. Nonnumeric prepared parameters are accepted by `ABS` but rejected by the other functions. The result therefore depends on whether the same character value is a literal, column, or prepared parameter.
All literal values, single-row column cases, and prepared parameters were executed three times. The full value matrix contained pure integers, positive and negative decimals, leading whitespace, leading plus, numeric-prefix text, nonnumeric text, empty strings, and NULL.
### Expected Behavior
These mathematical functions should use the same MySQL-compatible character-to-number conversion regardless of whether the value is a literal, `VARCHAR` column, or prepared parameter:
- consume leading whitespace and sign;
- parse a valid decimal/numeric prefix;
- convert a character value with no numeric prefix to zero;
- preserve NULL.
### Steps to Reproduce
```sql
DROP DATABASE IF EXISTS codex_exact_math_string;
CREATE DATABASE codex_exact_math_string;
USE codex_exact_math_string;
SELECT ABS('1.5');
SELECT MOD('-1.5',2);
SELECT ROUND('1.5');
SELECT SIGN('+1.5');
SELECT TRUNCATE('1.5tail',2);
SELECT CEIL(' 1.5');
SELECT FLOOR('1.5tail');
SELECT CEILING('abc');
CREATE TABLE src(id INT PRIMARY KEY,s VARCHAR(30));
INSERT INTO src VALUES
(1,'1'),(2,'1.5'),(3,'-1.5'),(4,' 1.5'),
(5,'+1.5'),(6,'1.5tail'),(7,'abc'),(8,''),(9,NULL);
SELECT id,ABS(s) FROM src WHERE id=2;
SELECT id,CEIL(s) FROM src WHERE id=4;
SELECT id,FLOOR(s) FROM src WHERE id=6;
SELECT id,ROUND(s) FROM src WHERE id=7;
PREPARE p FROM 'SELECT SIGN(?)';
SET @v='1.5tail';
EXECUTE p USING @v;
DEALLOCATE PREPARE p;
```
### Controls
- Native integer, floating-point, and DECIMAL operands produce the expected values.
- Pure integer character operands work in direct expressions.
- Floating transcendental functions such as `SQRT`, `LOG`, `ACOS`, and `DEGREES` already consume numeric prefixes and nonnumeric character input consistently with MySQL, so the compatibility behavior exists elsewhere in the math family.
- The separate `FLOOR(VARCHAR NULL)` implementation defect is tracked independently because it violates NULL propagation even without a MySQL comparison.
### Root Cause
The affected functions use inconsistent overload strategies. `ABS`, `MOD`, `ROUND`, `SIGN`, and `TRUNCATE` rely on `fixedTypeMatch` with only numeric overloads, and character inputs can be routed to a strict integer cast. `CEIL`/`CEILING` and `FLOOR` expose dedicated `VARCHAR` overloads, but `CeilStr` and `FloorStr` call `strconv.ParseFloat` on the entire string instead of the engine's MySQL-compatible numeric-prefix conversion. Prepared markers are inferred through yet another conversion path, producing the literal/column/prepared inconsistency.
Contributor guide
Assessment
This issue has not been assessed yet.