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

xiong pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 56fa3089f1 [CALCITE-6981] Runtime conversion of DECIMAL ARRAY to INT 
ARRAY fails with a ClassCastException
56fa3089f1 is described below

commit 56fa3089f19f12970fe5bf79b503ff842941097e
Author: Xiong Duan <[email protected]>
AuthorDate: Wed Apr 30 16:26:15 2025 +0800

    [CALCITE-6981] Runtime conversion of DECIMAL ARRAY to INT ARRAY fails with 
a ClassCastException
---
 .../adapter/enumerable/RexToLixTranslator.java     | 12 +++
 .../org/apache/calcite/runtime/SqlFunctions.java   | 36 +++++++++
 .../org/apache/calcite/util/BuiltInMethod.java     |  1 +
 core/src/test/resources/sql/cast.iq                | 87 ++++++++++++++++++++++
 4 files changed, 136 insertions(+)

diff --git 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
index 2569d3c065..a73d8c879a 100644
--- 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
+++ 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
@@ -362,6 +362,18 @@ private Expression getConvertExpression(
     }
 
     switch (targetType.getSqlTypeName()) {
+    case ARRAY:
+      final RelDataType sourceDataType = sourceType.getComponentType();
+      final RelDataType targetDataType = targetType.getComponentType();
+      assert sourceDataType != null;
+      assert targetDataType != null;
+      final ParameterExpression parameter =
+          Expressions.parameter(typeFactory.getJavaClass(sourceDataType), 
"root");
+      Expression convert =
+          getConvertExpression(sourceDataType, targetDataType, parameter, 
format);
+      return Expressions.call(BuiltInMethod.LIST_TRANSFORM.method, operand,
+          Expressions.lambda(Function1.class, convert, parameter));
+
     case VARIANT:
       // Converting any type to a VARIANT invokes the Variant constructor
       Expression rtti = RuntimeTypeInformation.createExpression(sourceType);
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java 
b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index 3367a9a77f..768767e9d6 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -106,6 +106,7 @@
 import java.time.format.DateTimeParseException;
 import java.time.format.SignStyle;
 import java.time.temporal.ChronoField;
+import java.util.AbstractList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Base64;
@@ -116,6 +117,7 @@
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
@@ -6236,6 +6238,40 @@ public static List arrayUnion(List list1, List list2) {
     return new ArrayList<>(result);
   }
 
+  /** Transforms a list, applying a function to each element. */
+  public static <F, T> List<T> transform(List<? extends F> list,
+      Function1<? super F, ? extends T> function) {
+    return new TransformingList<>(list, function);
+  }
+
+  /** List that returns the same number of elements as a backing list,
+   * applying a transformation function to each one.
+   *
+   * @param <F> Element type of backing list
+   * @param <T> Element type of this list
+   */
+  private static class TransformingList<F, T> extends AbstractList<T> {
+    private final Function1<? super F, ? extends T> function;
+    private final List<? extends F> list;
+
+    TransformingList(List<? extends F> list, Function1<? super F, ? extends T> 
function) {
+      this.function = function;
+      this.list = list;
+    }
+
+    @Override public T get(int i) {
+      return function.apply(list.get(i));
+    }
+
+    @Override public int size() {
+      return list.size();
+    }
+
+    @Override public Iterator<T> iterator() {
+      return listIterator();
+    }
+  }
+
   /** Support the SORT_ARRAY function. */
   public static List sortArray(List list, boolean ascending) {
     Comparator comparator = ascending
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java 
b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index c1f5d7e440..ebfa61359b 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -375,6 +375,7 @@ public enum BuiltInMethod {
   LIST_CONTAINS(List.class, "contains", Object.class),
   LIST_GET(List.class, "get", int.class),
   LIST_TO_ARRAY(List.class, "toArray"),
+  LIST_TRANSFORM(SqlFunctions.class, "transform", List.class, Function1.class),
   ITERATOR_HAS_NEXT(Iterator.class, "hasNext"),
   ITERATOR_NEXT(Iterator.class, "next"),
   MATH_MAX(Math.class, "max", int.class, int.class),
diff --git a/core/src/test/resources/sql/cast.iq 
b/core/src/test/resources/sql/cast.iq
index 4ab5e9a5e3..0bfeb6e35b 100644
--- a/core/src/test/resources/sql/cast.iq
+++ b/core/src/test/resources/sql/cast.iq
@@ -1707,4 +1707,91 @@ WHERE x BETWEEN 2 AND 4
 
 !ok
 
+# [CALCITE-6981] Runtime conversion of DECIMAL ARRAY to INT ARRAY fails with a 
ClassCastException
+# Cast DECIMAL ARRAY to INT ARRAY
+SELECT CAST(ARRAY[CAST(1.1111 AS DECIMAL(2, 1)), CAST(3.06754 AS DECIMAL(2, 
1))] AS INTEGER ARRAY);
++--------+
+| EXPR$0 |
++--------+
+| [1, 3] |
++--------+
+(1 row)
+
+!ok
+
+# Cast DECIMAL ARRAY to SMALLINT ARRAY, and then convert SMALLINT ARRAY to 
INTEGER ARRAY
+SELECT CAST(CAST(ARRAY[CAST(1.1111 AS DECIMAL(2, 1)), CAST(3.06754 AS 
DECIMAL(2, 1))] AS SMALLINT ARRAY) as INTEGER ARRAY);
++--------+
+| EXPR$0 |
++--------+
+| [1, 3] |
++--------+
+(1 row)
+
+!ok
+
+EnumerableCalc(expr#0=[{inputs}], expr#1=[1.1:DECIMAL(2, 1)], 
expr#2=[3.1:DECIMAL(2, 1)], expr#3=[ARRAY($t1, $t2)], 
expr#4=[CAST($t3):SMALLINT NOT NULL ARRAY NOT NULL], expr#5=[CAST($t4):INTEGER 
NOT NULL ARRAY NOT NULL], EXPR$0=[$t5])
+  EnumerableValues(tuples=[[{ 0 }]])
+!plan
+
+
+# Cast DECIMAL ARRAY to INTEGER ARRAY, and then convert INTEGER ARRAY to 
DOUBLE ARRAY
+SELECT CAST(CAST(ARRAY[CAST(1.1111 AS DECIMAL(2, 1)), CAST(3.06754 AS 
DECIMAL(2, 1))] AS INTEGER ARRAY) as DOUBLE ARRAY);
++------------+
+| EXPR$0     |
++------------+
+| [1.0, 3.0] |
++------------+
+(1 row)
+
+!ok
+
+EnumerableCalc(expr#0=[{inputs}], expr#1=[1.1:DECIMAL(2, 1)], 
expr#2=[3.1:DECIMAL(2, 1)], expr#3=[ARRAY($t1, $t2)], expr#4=[CAST($t3):INTEGER 
NOT NULL ARRAY NOT NULL], expr#5=[CAST($t4):DOUBLE NOT NULL ARRAY NOT NULL], 
EXPR$0=[$t5])
+  EnumerableValues(tuples=[[{ 0 }]])
+!plan
+
+# Cast SMALLINT ARRAY ARRAY to INTEGER ARRAY ARRAY
+SELECT CAST(ARRAY[ARRAY[1, 2], ARRAY[3, 4]] AS INTEGER ARRAY ARRAY);
++------------------+
+| EXPR$0           |
++------------------+
+| [[1, 2], [3, 4]] |
++------------------+
+(1 row)
+
+!ok
+
+EnumerableCalc(expr#0=[{inputs}], expr#1=[1], expr#2=[2], expr#3=[ARRAY($t1, 
$t2)], expr#4=[3], expr#5=[4], expr#6=[ARRAY($t4, $t5)], expr#7=[ARRAY($t3, 
$t6)], EXPR$0=[$t7])
+  EnumerableValues(tuples=[[{ 0 }]])
+!plan
+
+# Cast DECIMAL ARRAY ARRAY to INTEGER ARRAY ARRAY
+SELECT CAST(CAST(ARRAY[ARRAY[1.11, 2.22], ARRAY[3.33, 4.44]] AS DECIMAL ARRAY 
ARRAY) as INTEGER ARRAY ARRAY);
++------------------+
+| EXPR$0           |
++------------------+
+| [[1, 2], [3, 4]] |
++------------------+
+(1 row)
+
+!ok
+EnumerableCalc(expr#0=[{inputs}], expr#1=[1.11:DECIMAL(3, 2)], 
expr#2=[2.22:DECIMAL(3, 2)], expr#3=[ARRAY($t1, $t2)], expr#4=[3.33:DECIMAL(3, 
2)], expr#5=[4.44:DECIMAL(3, 2)], expr#6=[ARRAY($t4, $t5)], expr#7=[ARRAY($t3, 
$t6)], expr#8=[CAST($t7):DECIMAL(19, 0) NOT NULL ARRAY NOT NULL ARRAY NOT 
NULL], expr#9=[CAST($t8):INTEGER NOT NULL ARRAY NOT NULL ARRAY NOT NULL], 
EXPR$0=[$t9])
+  EnumerableValues(tuples=[[{ 0 }]])
+!plan
+
+# Cast DECIMAL ARRAY ARRAY to INTEGER ARRAY ARRAY, and then convert INTEGER 
ARRAY ARRAY to DECIMAL ARRAY ARRAY
+SELECT CAST(CAST(CAST(ARRAY[ARRAY[1.11, 2.22], ARRAY[3.33, 4.44]] AS DECIMAL 
ARRAY ARRAY) as INTEGER ARRAY ARRAY) as DECIMAL(2, 1) ARRAY ARRAY);
++--------------------------+
+| EXPR$0                   |
++--------------------------+
+| [[1.0, 2.0], [3.0, 4.0]] |
++--------------------------+
+(1 row)
+
+!ok
+
+EnumerableCalc(expr#0=[{inputs}], expr#1=[1.11:DECIMAL(3, 2)], 
expr#2=[2.22:DECIMAL(3, 2)], expr#3=[ARRAY($t1, $t2)], expr#4=[3.33:DECIMAL(3, 
2)], expr#5=[4.44:DECIMAL(3, 2)], expr#6=[ARRAY($t4, $t5)], expr#7=[ARRAY($t3, 
$t6)], expr#8=[CAST($t7):DECIMAL(19, 0) NOT NULL ARRAY NOT NULL ARRAY NOT 
NULL], expr#9=[CAST($t8):INTEGER NOT NULL ARRAY NOT NULL ARRAY NOT NULL], 
expr#10=[CAST($t9):DECIMAL(2, 1) NOT NULL ARRAY NOT NULL ARRAY NOT NULL], 
EXPR$0=[$t10])
+  EnumerableValues(tuples=[[{ 0 }]])
+!plan
+
 # End cast.iq

Reply via email to