This is an automated email from the ASF dual-hosted git repository.
tanner 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 2cb8167d5f [CALCITE-5862] Incorrect semantics of ARRAY function (Spark
library) when elements have Numeric and Character types
2cb8167d5f is described below
commit 2cb8167d5fa9375c35546bd9c827d7d4d8071b7a
Author: Ran Tao <[email protected]>
AuthorDate: Tue Jul 18 22:04:45 2023 +0800
[CALCITE-5862] Incorrect semantics of ARRAY function (Spark library) when
elements have Numeric and Character types
---
.../calcite/sql/fun/SqlLibraryOperators.java | 44 ++++++++++++++++++++--
.../org/apache/calcite/test/SqlOperatorTest.java | 8 ++++
2 files changed, 48 insertions(+), 4 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
index 98ecf893c2..43f5f6b9b4 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
@@ -1011,10 +1011,46 @@ public abstract class SqlLibraryOperators {
.withKind(SqlKind.CONCAT_WS_MSSQL);
private static RelDataType arrayReturnType(SqlOperatorBinding opBinding) {
- RelDataType type =
- opBinding.getOperandCount() > 0
- ? ReturnTypes.LEAST_RESTRICTIVE.inferReturnType(opBinding)
- : opBinding.getTypeFactory().createUnknownType();
+ final List<RelDataType> operandTypes = opBinding.collectOperandTypes();
+
+ // only numeric & character types check
+ boolean hasNumeric = false;
+ boolean hasCharacter = false;
+ boolean hasOthers = false;
+ for (RelDataType type : operandTypes) {
+ SqlTypeFamily family = type.getSqlTypeName().getFamily();
+ requireNonNull(family, "array element type family");
+ switch (family) {
+ case NUMERIC:
+ hasNumeric = true;
+ break;
+ case CHARACTER:
+ hasCharacter = true;
+ break;
+ case NULL:
+ // skip it becase we allow null
+ break;
+ default:
+ hasOthers = true;
+ break;
+ }
+ }
+
+ RelDataType type;
+ boolean useCharacterTypes = hasNumeric && hasCharacter && !hasOthers;
+ if (useCharacterTypes) {
+ List<RelDataType> characterTypes =
+ // may include NULL literal
+ operandTypes.stream().filter(
+ t -> t.getSqlTypeName().getFamily() != SqlTypeFamily.NUMERIC)
+ .collect(Collectors.toList());
+ type = opBinding.getTypeFactory().leastRestrictive(characterTypes);
+ } else {
+ type =
+ opBinding.getOperandCount() > 0
+ ? ReturnTypes.LEAST_RESTRICTIVE.inferReturnType(opBinding)
+ : opBinding.getTypeFactory().createUnknownType();
+ }
requireNonNull(type, "inferred array element type");
return SqlTypeUtil.createArrayType(opBinding.getTypeFactory(), type,
false);
}
diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
index f2ad858e95..68f3ff3896 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -9941,6 +9941,14 @@ public class SqlOperatorTest {
"[null, foo]", "CHAR(3) ARRAY NOT NULL");
f2.checkScalar("array(null)",
"[null]", "NULL ARRAY NOT NULL");
+ f2.checkScalar("array(1, 2, 'Hi')",
+ "[1, 2, Hi]", "CHAR(2) NOT NULL ARRAY NOT NULL");
+ f2.checkScalar("array(1, 2, 'Hi', 'Hello')",
+ "[1, 2, Hi, Hello]", "CHAR(5) NOT NULL ARRAY NOT NULL");
+ f2.checkScalar("array(1, 2, 'Hi', null)",
+ "[1, 2, Hi, null]", "CHAR(2) ARRAY NOT NULL");
+ f2.checkScalar("array(1, 2, 'Hi', cast(null as char(10)))",
+ "[1, 2, Hi, null]", "CHAR(10) ARRAY NOT NULL");
}
/**