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

mbudiu 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 01cfeeea8e [CALCITE-6736] Validator accepts comparisons between 
arrays, multisets, maps without regard to element types
01cfeeea8e is described below

commit 01cfeeea8e41f97e40ef0ffa8a082b58414702ff
Author: Mihai Budiu <[email protected]>
AuthorDate: Wed Dec 18 11:31:29 2024 -0800

    [CALCITE-6736] Validator accepts comparisons between arrays, multisets, 
maps without regard to element types
    
    Signed-off-by: Mihai Budiu <[email protected]>
---
 .../org/apache/calcite/sql/type/SqlTypeUtil.java   | 51 +++++++++++++++++++---
 .../validate/implicit/AbstractTypeCoercion.java    | 38 ++++++++++++++++
 .../org/apache/calcite/test/SqlValidatorTest.java  | 13 ++++++
 .../java/org/apache/calcite/test/PigRelOpTest.java |  6 +--
 4 files changed, 99 insertions(+), 9 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java 
b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
index 245756824e..b3836d17d8 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
@@ -595,17 +595,34 @@ public abstract class SqlTypeUtil {
       }
       return true;
     }
-    RelDataType comp1 = t1.getComponentType();
-    RelDataType comp2 = t2.getComponentType();
-    if ((comp1 != null) || (comp2 != null)) {
-      if ((comp1 == null) || (comp2 == null)) {
+    SqlTypeName t1Name = t1.getSqlTypeName();
+    SqlTypeName t2Name = t2.getSqlTypeName();
+    if (t1Name == SqlTypeName.ARRAY || t1Name == SqlTypeName.MULTISET) {
+      if (t1Name != t2Name) {
         return false;
       }
-      if (!sameNamedType(comp1, comp2)) {
+
+      RelDataType comp1 = requireNonNull(t1.getComponentType());
+      RelDataType comp2 = requireNonNull(t2.getComponentType());
+      return sameNamedType(comp1, comp2);
+    }
+
+    if (t1Name == SqlTypeName.MAP) {
+      if (t1Name != t2Name) {
+        return false;
+      }
+
+      RelDataType keyType1 = requireNonNull(t1.getKeyType());
+      RelDataType keyType2 = requireNonNull(t2.getKeyType());
+      if (!sameNamedType(keyType1, keyType2)) {
         return false;
       }
+      RelDataType valueType1 = requireNonNull(t1.getValueType());
+      RelDataType valueType2 = requireNonNull(t2.getValueType());
+      return sameNamedType(valueType1, valueType2);
     }
-    return t1.getSqlTypeName() == t2.getSqlTypeName();
+
+    return t1Name == t2Name;
   }
 
   /**
@@ -1504,6 +1521,28 @@ public abstract class SqlTypeUtil {
       return true;
     }
 
+    SqlTypeName type1Name = type1.getSqlTypeName();
+    SqlTypeName type2Name = type2.getSqlTypeName();
+    if (type1Name == SqlTypeName.ARRAY || type1Name == SqlTypeName.MULTISET) {
+      if (type2Name != type1Name) {
+        return false;
+      }
+      RelDataType elementType1 = requireNonNull(type1.getComponentType());
+      RelDataType elementType2 = requireNonNull(type2.getComponentType());
+      return isComparable(elementType1, elementType2);
+    }
+
+    if (type1Name == SqlTypeName.MAP) {
+      if (type2Name != type1Name) {
+        return false;
+      }
+      RelDataType keyType1 = requireNonNull(type1.getKeyType());
+      RelDataType keyType2 = requireNonNull(type2.getKeyType());
+      RelDataType valueType1 = requireNonNull(type1.getValueType());
+      RelDataType valueType2 = requireNonNull(type2.getValueType());
+      return isComparable(keyType1, keyType2) && isComparable(valueType1, 
valueType2);
+    }
+
     final RelDataTypeFamily family1 = family(type1);
     final RelDataTypeFamily family2 = family(type2);
     if (family1 == family2) {
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java
 
b/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java
index 87106eebb8..2d5970440d 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java
@@ -504,6 +504,7 @@ public abstract class AbstractTypeCoercion implements 
TypeCoercion {
     SqlTypeName typeName1 = type1.getSqlTypeName();
     SqlTypeName typeName2 = type2.getSqlTypeName();
 
+    // The following can never be true, but there is no harm leaving it here.
     if (typeName1 == null || typeName2 == null) {
       return null;
     }
@@ -629,6 +630,43 @@ public abstract class AbstractTypeCoercion implements 
TypeCoercion {
       }
     }
 
+    if (typeName1 == SqlTypeName.ARRAY || typeName1 == SqlTypeName.MULTISET) {
+      if (typeName2 != typeName1) {
+        return null;
+      }
+      RelDataType elementType1 = type1.getComponentType();
+      RelDataType elementType2 = type2.getComponentType();
+      RelDataType type = commonTypeForBinaryComparison(elementType1, 
elementType2);
+      if (type == null) {
+        return null;
+      }
+      // The only maxCardinality that seems to be supported is -1, i.e., 
unlimited.
+      RelDataType resultType = factory.createArrayType(type, -1);
+      return factory.createTypeWithNullability(
+          resultType, type1.isNullable() || type2.isNullable());
+    }
+
+    if (typeName1 == SqlTypeName.MAP) {
+      if (typeName2 != SqlTypeName.MAP) {
+        return null;
+      }
+      RelDataType keyType1 = type1.getKeyType();
+      RelDataType keyType2 = type2.getKeyType();
+      RelDataType keyType = commonTypeForBinaryComparison(keyType1, keyType2);
+      if (keyType == null) {
+        return null;
+      }
+      RelDataType valueType1 = type1.getValueType();
+      RelDataType valueType2 = type2.getValueType();
+      RelDataType valueType = commonTypeForBinaryComparison(valueType1, 
valueType2);
+      if (valueType == null) {
+        return null;
+      }
+      RelDataType resultType = factory.createMapType(keyType, valueType);
+      return factory.createTypeWithNullability(
+          resultType, type1.isNullable() || type2.isNullable());
+    }
+
     return null;
   }
 
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index 1e9ae827a7..37a7050701 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -501,6 +501,19 @@ public class SqlValidatorTest extends SqlValidatorTestCase 
{
     expr("^x'abcd'<>1^")
         .fails("(?s).*Cannot apply '<>' to arguments of type "
             + "'<BINARY.2.> <> <INTEGER>'.*");
+    // Test cases for [CALCITE-6736] Validator accepts comparisons between 
arrays, multisets, maps
+    // without regard to element types
+    expr("^array[x'a4'] = array[1]^")
+        .fails("(?s).*Cannot apply '=' to arguments of type "
+            + "'<BINARY.1. ARRAY> = <INTEGER ARRAY>'.*");
+    expr("^MAP[x'a4', 1] = MAP[1, 1]^")
+        .fails("(?s).*Cannot apply '=' to arguments of type "
+            + "'<.BINARY.1., INTEGER. MAP> = <.INTEGER, INTEGER. MAP>'.*");
+    expr("^array[x'a4'] = 1^")
+        .fails("(?s).*Cannot apply '=' to arguments of type '<BINARY.1. ARRAY> 
= <INTEGER>'.*");
+    expr("^multiset[x'a4'] = multiset[1]^")
+        .fails("(?s).*Cannot apply '=' to arguments of type "
+            + "'<BINARY.1. MULTISET> = <INTEGER MULTISET>'.*");
   }
 
   @Test void testBinaryString() {
diff --git a/piglet/src/test/java/org/apache/calcite/test/PigRelOpTest.java 
b/piglet/src/test/java/org/apache/calcite/test/PigRelOpTest.java
index 95de883788..ed6301ee0c 100644
--- a/piglet/src/test/java/org/apache/calcite/test/PigRelOpTest.java
+++ b/piglet/src/test/java/org/apache/calcite/test/PigRelOpTest.java
@@ -1531,7 +1531,7 @@ class PigRelOpTest extends PigRelTestBase {
     final String plan = ""
         + "LogicalSort(sort0=[$6], dir0=[ASC])\n"
         + "  LogicalProject(DEPTNO=[$0.DEPTNO], MGR=[$0.MGR], 
HIREDATE=[$0.HIREDATE], "
-        + "$f3=[COUNT(PIG_BAG($1))], newCol=[1:BIGINT], 
comArray=[MULTISET_PROJECTION($1, 6)], "
+        + "$f3=[COUNT(PIG_BAG($1))], newCol=[1:BIGINT], 
comArray=[CAST(MULTISET_PROJECTION($1, 6)):RecordType(DECIMAL(19, 0) COMM) 
MULTISET], "
         + "salSum=[BigDecimalSum(PIG_BAG(MULTISET_PROJECTION($1, 5)))])\n"
         + "    LogicalProject(group=[ROW($0, $1, $2)], A=[$3])\n"
         + "      LogicalAggregate(group=[{0, 1, 2}], A=[COLLECT($3)])\n"
@@ -1541,14 +1541,14 @@ class PigRelOpTest extends PigRelTestBase {
     final String optimizedPlan = ""
         + "LogicalSort(sort0=[$6], dir0=[ASC])\n"
         + "  LogicalProject(DEPTNO=[$0], MGR=[$1], HIREDATE=[$2], 
$f3=[CAST($3):BIGINT], "
-        + "newCol=[1:BIGINT], comArray=[$4], salSum=[CAST($5):DECIMAL(19, 
0)])\n"
+        + "newCol=[1:BIGINT], comArray=[CAST($4):RecordType(DECIMAL(19, 0) 
COMM) MULTISET], salSum=[CAST($5):DECIMAL(19, 0)])\n"
         + "    LogicalAggregate(group=[{0, 1, 2}], agg#0=[COUNT()], 
agg#1=[COLLECT($3)], "
         + "agg#2=[SUM($4)])\n"
         + "      LogicalProject(DEPTNO=[$7], MGR=[$3], HIREDATE=[$4], 
COMM=[$6], SAL=[$5])\n"
         + "        LogicalTableScan(table=[[scott, EMP]])\n";
     final String sql = ""
         + "SELECT DEPTNO, MGR, HIREDATE, CAST(COUNT(*) AS BIGINT) AS $f3, 1 AS 
newCol, "
-        + "COLLECT(COMM) AS comArray, CAST(SUM(SAL) AS DECIMAL(19, 0)) AS 
salSum\n"
+        + "CAST(COLLECT(COMM) AS ROW(COMM DECIMAL(19, 0)) MULTISET) AS 
comArray, CAST(SUM(SAL) AS DECIMAL(19, 0)) AS salSum\n"
         + "FROM scott.EMP\n"
         + "GROUP BY DEPTNO, MGR, HIREDATE\n"
         + "ORDER BY 7";

Reply via email to