matrixorigin / matrixorigin/matrixone
[Bug]: DEGREES exposes and persists infinity for finite DOUBLE input
- 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.
#28137 covers the deliberately selected row-local `NULL` contract for arithmetic failures in `POWER`, `EXP`, and `COT`. No issue was found for non-finite output from `DEGREES`.
### Branch Name
main
### Commit ID
`f0c31cd4b830be32442cf329e0a3fb08aa9c16c3`
### Other Environment Information
- Local standalone MatrixOne built from the commit above
- Reference: MySQL 8.3.0
### Actual Behavior
`DEGREES()` multiplies a finite `DOUBLE` by `180/pi` without checking whether the result is finite. Inputs above approximately `3.137e306` therefore produce SQL-visible positive or negative infinity:
```sql
SELECT DEGREES(CAST('3.14e306' AS DOUBLE));
-- MatrixOne: +Inf
-- MySQL: ERROR 1690, DOUBLE value is out of range
SELECT DEGREES(CAST('-3.14e306' AS DOUBLE));
-- MatrixOne: -Inf
-- MySQL: ERROR 1690, DOUBLE value is out of range
```
The value is not classified as `NULL`, compares equal to itself, and can be persisted by CTAS:
```sql
CREATE TABLE t AS
SELECT DEGREES(CAST('3.14e306' AS DOUBLE)) AS v;
SELECT v, v IS NULL, v=v, CAST(v AS CHAR) FROM t;
-- MatrixOne: +Inf, 0, 1, '+Inf'
```
MySQL aborts the CTAS and does not create the table. In MatrixOne, the infinity also propagates through `SUM`, `AVG`, and `MAX`, so later relational processing treats it as an ordinary value.
The same result occurs with a `DOUBLE` column and through SQL PREPARE. Positive and negative forms, both CTAS forms, and the prepared forms were each repeated three times.
### Expected Behavior
`DEGREES()` must not expose or persist a non-finite `DOUBLE` produced from finite SQL input. MySQL raises error 1690. If MatrixOne intentionally applies the row-local arithmetic-failure policy selected for #28137, the overflow row should become `NULL`; either behavior avoids materializing `+Inf/-Inf` as ordinary SQL values.
### Steps to Reproduce
```sql
DROP DATABASE IF EXISTS codex_degrees_overflow;
CREATE DATABASE codex_degrees_overflow;
USE codex_degrees_overflow;
SELECT DEGREES(CAST('3e306' AS DOUBLE)); -- finite control
SELECT DEGREES(CAST('3.14e306' AS DOUBLE)); -- +Inf
SELECT DEGREES(CAST('-3.14e306' AS DOUBLE)); -- -Inf
CREATE TABLE src(id INT PRIMARY KEY, x DOUBLE);
INSERT INTO src VALUES (1,3e306),(2,3.14e306),(3,-3.14e306),(4,NULL);
SELECT id,DEGREES(x) FROM src ORDER BY id;
SELECT SUM(DEGREES(x)),AVG(DEGREES(x)),MAX(DEGREES(x)) FROM src;
PREPARE p FROM 'SELECT DEGREES(?)';
SET @x=CAST('3.14e306' AS DOUBLE);
EXECUTE p USING @x;
DEALLOCATE PREPARE p;
CREATE TABLE t AS
SELECT DEGREES(CAST('3.14e306' AS DOUBLE)) AS v;
SELECT v,v IS NULL,v=v,CAST(v AS CHAR) FROM t;
```
### Controls
- `DEGREES(1)`, zero, `NULL`, and finite results agree with MySQL.
- `DEGREES(CAST('3e306' AS DOUBLE))` remains finite in both engines.
- `RADIANS(CAST('1e308' AS DOUBLE))` remains finite and agrees with MySQL.
- The server and connection remain healthy after all executions.
### Root Cause
`builtInDegrees` in `pkg/sql/plan/function/func_builtin.go` uses `opUnaryFixedToFixed` and directly returns `v * (180.0 / math.Pi)`. It does not check `math.IsInf`/`math.IsNaN`. By contrast, the arithmetic-failure implementations for `EXP` and `POWER` use `*WithNullOnError` helpers and reject non-finite intermediate results before appending them.
Contributor guide
Assessment
This issue has not been assessed yet.