This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/dev/pr-12182-de345265d0bc6e9fcbd5e2a7096ae33faff4fb6e
in repository https://gitbox.apache.org/repos/asf/seatunnel.git

commit c2f88ba3776d4751a427e448aa68f2541162eaa8
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);
 

Reply via email to