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