SEPURI-SAI-KRISHNA opened a new issue, #11935: URL: https://github.com/apache/seatunnel/issues/11935
### Search before asking - [X] I had searched in the [issues](https://github.com/apache/seatunnel/issues?q=is%3Aissue+label%3A%22bug%22) and found no similar issues. ### What happened Three Zeta SQL numeric functions dispatch on the runtime type of their argument with an incomplete set of branches. `TINYINT` (`Byte`) is missing everywhere, `SMALLINT` (`Short`) is missing from two of them. Depending on the function, the result is either a silently wrong answer or a hard failure on a documented type. **1. `ROUND` / `CEIL` / `CEILING` / `FLOOR` / `TRUNC` silently ignore `TINYINT`** The shared `round(Number, Number, RoundingMode)` helper (`NumericFunction.java:232`) switches on the argument's class simple name with cases `INTEGER`, `SHORT`, `LONG`, `BIGDECIMAL`, `DOUBLE`, `FLOAT`. There is **no `BYTE` case and no `default`**, so a `Byte` argument falls straight through the switch and the method returns its input unchanged. Run through the real `SQLTransform` on `dev`: | Expression | Column type | Actual | Expected | |---|---|---|---| | `ROUND(tiny_v, -1)`, `tiny_v = 44` | `TINYINT` | **`44`** | `40` | | `CEIL(tiny_v, -1)`, `tiny_v = 44` | `TINYINT` | **`44`** | `50` | | `ROUND(small_v, -2)`, `small_v = 1234` | `SMALLINT` | `1200` | `1200` ✅ | The `SMALLINT` row is the control: the identical expression one type up works correctly. That is what makes this a missing branch rather than a deliberate carve-out. No exception, no log line — the value is simply not rounded. Note `44` is chosen deliberately: it rounds to `40`, which fits a `TINYINT` comfortably. This defect is independent of any overflow concern. **2. `ABS` and `SIGN` reject `TINYINT` and `SMALLINT` outright** Both use an `instanceof` chain covering `Integer`, `Long`, `Float`, `Double`, `BigDecimal` and then throw. Neither has a `Byte` or `Short` branch: | Expression | Column type | Result | |---|---|---| | `ABS(tiny_v)` | `TINYINT` | `TransformException` — `Unsupported arg type java.lang.Byte of function ABS` | | `ABS(small_v)` | `SMALLINT` | `TransformException` — `Unsupported arg type java.lang.Short of function ABS` | | `SIGN(tiny_v)` | `TINYINT` | `TransformException` — `Unsupported function SIGN() argument type: java.lang.Byte` | | `SIGN(small_v)` | `SMALLINT` | `TransformException` — `Unsupported function SIGN() argument type: java.lang.Short` | `ABS` on a `SMALLINT` column failing is the most surprising of these, because the `ABS` documentation explicitly discusses `SMALLINT` as a supported argument type. **3. `SIGN` loses the sign of a `BigDecimal` too small for `double` (minor)** `sign` converts `BigDecimal` via `doubleValue()` before testing, so any magnitude below `Double.MIN_VALUE` underflows to `0.0` and a non-zero input reports as zero: ```java NumericFunction.sign(singletonList(new BigDecimal("1E-400"))) // 0, expected 1 NumericFunction.sign(singletonList(new BigDecimal("-1E-400"))) // 0, expected -1 NumericFunction.sign(singletonList(new BigDecimal("1E-38"))) // 1, correct ``` **In fairness this one is not reachable through a declared column today** — SeaTunnel's `DECIMAL(p, s)` caps precision at 38, so no declared column can carry a value that small, and I could not construct an end-to-end pipeline that triggers it. I am listing it because it lives in the same `instanceof` chain as defect 2 and the fix is strictly better on every axis: `BigDecimal.signum()` is exact, cheaper, and allocation-free. It should be corrected while that chain is being touched, not tracked as a user-facing bug. ### Why `TINYINT` and `SMALLINT` are reachable `ZetaSQLType.isNumberType` accepts everything from `SqlType.TINYINT` to `SqlType.DECIMAL` inclusive: ```java // ZetaSQLType.java:254-256 public boolean isNumberType(SqlType type) { return type.compareTo(SqlType.TINYINT) >= 0 && type.compareTo(SqlType.DECIMAL) <= 0; } ``` and `BasicType.BYTE_TYPE` maps `Byte` to `SqlType.TINYINT` (`BasicType.java:30`). For `ABS`/`ROUND`/`CEIL`/`CEILING`/`FLOOR`/`TRUNC`/`TRUNCATE`, `getFunctionType` returns the type of the first argument, so a `TINYINT` column stays `TINYINT` all the way into the function. These are not theoretical types. **The same class already handles them deliberately elsewhere** — `NumericFunction.toBigDecimal` has an explicit branch for both: ```java // NumericFunction.java:162-172 private static BigDecimal toBigDecimal(Number value) { if (value instanceof BigDecimal) { return (BigDecimal) value; } if (value instanceof Byte || value instanceof Short || value instanceof Integer || value instanceof Long) { return BigDecimal.valueOf(value.longValue()); } ... ``` So `Byte` and `Short` are known to flow through this class. They were simply omitted from these three functions. `docs/en/transforms/sql-functions.md` documents `ROUND(numeric[, digitsInt]) -> NUMERIC (same type)`, `CEIL | CEILING (numeric) -> NUMERIC (same type, scale 0)`, `FLOOR(numeric) -> NUMERIC (same type, scale 0)`, `TRUNC | TRUNCATE(numeric[, digitsInt]) -> NUMERIC (same type)` and `SIGN(numeric) -> INT`, with no carve-out excluding `TINYINT` or `SMALLINT`. The `ABS` section names `TINYINT` and `SMALLINT` among the types it applies to. ### What you expected to happen - `ROUND`/`CEIL`/`CEILING`/`FLOOR`/`TRUNC` handle `TINYINT` like the other integral types, and the `switch` gains a `default` so any unhandled numeric type fails loudly instead of silently returning its input. - `ABS` and `SIGN` accept `TINYINT` and `SMALLINT`. - `SIGN` uses `BigDecimal.signum()` rather than `doubleValue()`. Adding the `BYTE` case to the rounding family depends on the overflow handling in #11926 / #11927, since `ROUND(TINYINT 127, -1)` is `130`, which does not fit a `TINYINT`. I raised that gap in #11927 as a known follow-up; this issue is that follow-up. Part of this was also found independently while reviewing #11927 (@DanielLeens, minor observation 2): "`NumericFunction.abs()` still has no case for `Short` (`SMALLINT`) input — it falls through to the generic 'Unsupported arg type' exception. This is a pre-existing gap on `dev`, not touched or worsened by this PR, so it's out of scope here, just flagging for a separate follow-up." That covers the `ABS`/`SMALLINT` half of claim 2 above; the `TINYINT` rounding gap and the `SIGN` cases are additional. ### SeaTunnel Version dev (3.0.0-SNAPSHOT), reproduced at `4ba2895` ### SeaTunnel Config ```conf env { parallelism = 1 job.mode = "BATCH" } source { FakeSource { plugin_output = "fake" schema = { fields { tiny_v = "tinyint" small_v = "smallint" } } rows = [ { kind = INSERT fields = [44, 1234] } ] } } transform { Sql { plugin_input = "fake" plugin_output = "out" # r_tiny is silently NOT rounded; r_small on the next type up is correct. # Swap in ABS(tiny_v) or SIGN(small_v) to hit the hard failure instead. query = "select ROUND(tiny_v, -1) as r_tiny, ROUND(small_v, -2) as r_small from fake" } } sink { Console { plugin_input = "out" } } ``` ### Running Command ```shell ./bin/seatunnel.sh --config ./config/repro.conf -e local ``` ### Error Exception ```log For the ROUND query above there is no exception - the job reports success: r_tiny = 44 (expected 40; silently not rounded) r_small = 1200 (correct - the same expression one type up works) Replacing the query with ABS(tiny_v) or SIGN(small_v) instead produces: ErrorCode:[TRANSFORM_COMMON-06], ErrorDescription:[The expression 'ABS(tiny_v)' of SQL transform execute failed] caused by: ErrorCode:[COMMON-05], ErrorDescription:[Unsupported operation] - Unsupported arg type java.lang.Byte of function ABS ErrorCode:[TRANSFORM_COMMON-06], ErrorDescription:[The expression 'SIGN(small_v)' of SQL transform execute failed] caused by: ErrorCode:[COMMON-05], ErrorDescription:[Unsupported operation] - Unsupported function SIGN() argument type: java.lang.Short ``` ### Zeta or Flink or Spark Version Zeta (engine-independent — the defect is in the transform) ### Java or Scala Version Java 17 ### Screenshots _No response_ ### Are you willing to submit PR? - [X] Yes I am willing to submit a PR! ### Code of Conduct - [X] I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
