This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/seatunnel.git
The following commit(s) were added to refs/heads/dev by this push:
new e78be4c7a2 [Test][Transform-V2] Cover the MOD divisor type contract
and its overflow bounds (#12182)
e78be4c7a2 is described below
commit e78be4c7a20fd690388fb7e8cdca064d2aeb0ac7
Author: Sepuri Sai Krishna <[email protected]>
AuthorDate: Tue Sep 22 15:38:56 2026 +0000
[Test][Transform-V2] Cover the MOD divisor type contract and its overflow
bounds (#12182)
---
.../sql/zeta/functions/NumericFunction.java | 7 ++++++
.../transform/sql/zeta/NumericFunctionTest.java | 26 ++++++++++++++++++++
.../sql/zeta/functions/NumericFunctionTest.java | 28 ++++++++++++++++++++++
3 files changed, 61 insertions(+)
diff --git
a/seatunnel-transforms-v2/src/main/java/org/apache/seatunnel/transform/sql/zeta/functions/NumericFunction.java
b/seatunnel-transforms-v2/src/main/java/org/apache/seatunnel/transform/sql/zeta/functions/NumericFunction.java
index 19cb1f91d4..746adb7def 100644
---
a/seatunnel-transforms-v2/src/main/java/org/apache/seatunnel/transform/sql/zeta/functions/NumericFunction.java
+++
b/seatunnel-transforms-v2/src/main/java/org/apache/seatunnel/transform/sql/zeta/functions/NumericFunction.java
@@ -221,6 +221,13 @@ public class NumericFunction {
CommonErrorCodeDeprecated.UNSUPPORTED_OPERATION, "Mod by
zero");
}
BigDecimal[] res = leftBD.divideAndRemainder(rightBD);
+ // The result keeps the type of the divisor, which is what ZetaSQLType
declares for MOD.
+ // No range check is needed for the narrow types: divideAndRemainder
guarantees that
+ // |remainder| < |divisor|, so the remainder is always within the
divisor's range.
+ // In range, not lossless: a FLOAT, DOUBLE or DECIMAL dividend keeps
its fractional
+ // part, and byteValue() and shortValue() truncate it, so MOD(5.5,
(byte) 2) is 1 and
+ // not 1.5. That is deliberate and matches the type ZetaSQLType
declares, so it should
+ // not be "fixed" into rounding or into a wider return type.
if (rightValue instanceof Byte) {
return res[1].byteValue();
}
diff --git
a/seatunnel-transforms-v2/src/test/java/org/apache/seatunnel/transform/sql/zeta/NumericFunctionTest.java
b/seatunnel-transforms-v2/src/test/java/org/apache/seatunnel/transform/sql/zeta/NumericFunctionTest.java
index 66e26f4d69..1a290e0257 100644
---
a/seatunnel-transforms-v2/src/test/java/org/apache/seatunnel/transform/sql/zeta/NumericFunctionTest.java
+++
b/seatunnel-transforms-v2/src/test/java/org/apache/seatunnel/transform/sql/zeta/NumericFunctionTest.java
@@ -185,4 +185,30 @@ public class NumericFunctionTest {
new
BigDecimal("12345678901234567890.987654321"),
new BigDecimal("1"))));
}
+
+ /**
+ * ZetaSQLType derives the MOD result type from the divisor, so the row
type the planner
+ * declares has to agree with what the runtime actually produces. Only the
declared type is
+ * asserted here: the projected values for the same query shape are
already covered by {@code
+ * SQLNumericFunctionsTest#testModWithTinyIntAndSmallIntDivisors}, which
landed with #12215.
+ */
+ @Test
+ public void testModDeclaresTheDivisorTypeForTinyIntAndSmallInt() {
+ SeaTunnelRowType rowType =
+ new SeaTunnelRowType(
+ new String[] {"int_v", "tiny_v", "small_v"},
+ new SeaTunnelDataType[] {
+ BasicType.INT_TYPE, BasicType.BYTE_TYPE,
BasicType.SHORT_TYPE
+ });
+ SQLEngine sqlEngine =
SQLEngineFactory.getSQLEngine(SQLEngineFactory.EngineType.ZETA);
+ sqlEngine.init(
+ "test",
+ null,
+ rowType,
+ "select MOD(int_v, tiny_v) as tiny_mod, MOD(int_v, small_v) as
small_mod from test");
+
+ SeaTunnelRowType outRowType = sqlEngine.typeMapping(null);
+ Assertions.assertEquals(BasicType.BYTE_TYPE,
outRowType.getFieldType(0));
+ Assertions.assertEquals(BasicType.SHORT_TYPE,
outRowType.getFieldType(1));
+ }
}
diff --git
a/seatunnel-transforms-v2/src/test/java/org/apache/seatunnel/transform/sql/zeta/functions/NumericFunctionTest.java
b/seatunnel-transforms-v2/src/test/java/org/apache/seatunnel/transform/sql/zeta/functions/NumericFunctionTest.java
index 3f4c835e4e..a3324346a5 100644
---
a/seatunnel-transforms-v2/src/test/java/org/apache/seatunnel/transform/sql/zeta/functions/NumericFunctionTest.java
+++
b/seatunnel-transforms-v2/src/test/java/org/apache/seatunnel/transform/sql/zeta/functions/NumericFunctionTest.java
@@ -145,6 +145,34 @@ public class NumericFunctionTest {
Assertions.assertEquals(1, NumericFunction.mod(Arrays.asList(5, 2)));
Assertions.assertEquals(1L, NumericFunction.mod(Arrays.asList(5L,
2L)));
+ // A TINYINT or SMALLINT divisor has to carry its own type into the
result, which is what
+ // ZetaSQLType declares for MOD (added with #12215).
+ Object byteResult = NumericFunction.mod(Arrays.asList(5, (byte) 2));
+ Assertions.assertEquals(Byte.class, byteResult.getClass());
+ Assertions.assertEquals((byte) 1, byteResult);
+
+ Object shortResult = NumericFunction.mod(Arrays.asList(5, (short) 2));
+ Assertions.assertEquals(Short.class, shortResult.getClass());
+ Assertions.assertEquals((short) 1, shortResult);
+
+ // What bounds the result is the remainder, not the divisor:
divideAndRemainder gives
+ // |remainder| <= |divisor| - 1, so the extreme outputs are the ones
below. A dividend
+ // merely larger than the divisor does not reach them, so these are
the inputs that
+ // would fail first if a range guard were ever added or the narrowing
became lossy.
+ // The sign follows the dividend, which the negative cases pin.
+ Assertions.assertEquals(
+ (byte) 127, NumericFunction.mod(Arrays.asList(127,
Byte.MIN_VALUE)));
+ Assertions.assertEquals(
+ (byte) -127, NumericFunction.mod(Arrays.asList(-127,
Byte.MIN_VALUE)));
+ Assertions.assertEquals(
+ (short) 32767, NumericFunction.mod(Arrays.asList(32767,
Short.MIN_VALUE)));
+ Assertions.assertEquals(
+ (short) -32767, NumericFunction.mod(Arrays.asList(-32767,
Short.MIN_VALUE)));
+
+ // A fractional remainder is truncated to the divisor's integral type
rather than
+ // rounded, which is what ZetaSQLType declares the result to be.
+ Assertions.assertEquals((byte) 1,
NumericFunction.mod(Arrays.asList(5.5d, (byte) 2)));
+
Float floatResult = (Float) NumericFunction.mod(Arrays.asList(5.5f,
2.0f));
Assertions.assertEquals(1.5f, floatResult);