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

mihaibudiu 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 4c2b6d2df7 [CALCITE-7582] Type validation errors should use SQL type 
names
4c2b6d2df7 is described below

commit 4c2b6d2df7eb0b38fe3090b11b82e105dc2def0a
Author: Mihai Budiu <[email protected]>
AuthorDate: Fri Jun 5 22:18:59 2026 -0700

    [CALCITE-7582] Type validation errors should use SQL type names
    
    Signed-off-by: Mihai Budiu <[email protected]>
---
 .../main/java/org/apache/calcite/sql/SqlCall.java  |   4 +-
 .../org/apache/calcite/sql/type/OperandTypes.java  |   4 +-
 .../org/apache/calcite/sql/type/SqlTypeUtil.java   | 244 +++++++++++++++++++++
 .../org/apache/calcite/test/SqlValidatorTest.java  |  60 +++--
 core/src/test/resources/sql/misc.iq                |   2 +-
 .../org/apache/calcite/test/SqlOperatorTest.java   |  44 ++--
 6 files changed, 313 insertions(+), 45 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/sql/SqlCall.java 
b/core/src/main/java/org/apache/calcite/sql/SqlCall.java
index f3edc22a36..1747272f62 100755
--- a/core/src/main/java/org/apache/calcite/sql/SqlCall.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlCall.java
@@ -18,6 +18,7 @@
 
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.sql.util.SqlVisitor;
 import org.apache.calcite.sql.validate.SqlMoniker;
 import org.apache.calcite.sql.validate.SqlMonotonicity;
@@ -210,7 +211,8 @@ public String getCallSignature(
       if (null == argType) {
         continue;
       }
-      signatureList.add(argType.toString());
+      signatureList.add(
+          SqlTypeUtil.asSqlType(argType, 
SqlTypeUtil.NullabilityDisplay.DoNotDisplay));
     }
     return SqlUtil.getOperatorSignature(getOperator(), signatureList);
   }
diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java 
b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
index f525975c98..99c623865c 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
@@ -1535,7 +1535,7 @@ private 
RecordTypeWithOneFieldChecker(Predicate<SqlTypeName> predicate) {
 
         @Override public String getAllowedSignatures(SqlOperator op, String 
opName) {
           return SqlUtil.getAliasedSignature(op, opName,
-              ImmutableList.of("RECORDTYPE(SINGLE FIELD)"));
+              ImmutableList.of("ROW(SINGLE FIELD)"));
         }
       };
 
@@ -1561,7 +1561,7 @@ private static class MapFromEntriesOperandTypeChecker
 
     @Override public String getAllowedSignatures(SqlOperator op, String 
opName) {
       return SqlUtil.getAliasedSignature(op, opName,
-          ImmutableList.of("ARRAY<RECORDTYPE(TWO FIELDS)>"));
+          ImmutableList.of("ARRAY<ROW(TWO FIELDS)>"));
     }
   }
 
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 1be36fa18b..0298866828 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
@@ -2183,4 +2183,248 @@ public static RelDataType 
fromMeasure(RelDataTypeFactory typeFactory,
     }
     return type;
   }
+
+  private static void appendPrecisionScale(RelDataType type, StringBuilder 
builder) {
+    builder
+        .append("(")
+        .append(type.getPrecision())
+        .append(", ")
+        .append(type.getScale())
+        .append(")");
+  }
+
+  private static void appendPrecision(RelDataType type, StringBuilder builder, 
boolean omitZero) {
+    if (type.getPrecision() != RelDataType.PRECISION_NOT_SPECIFIED) {
+      if (omitZero && type.getPrecision() == 0) {
+        return;
+      }
+      builder
+          .append("(")
+          .append(type.getPrecision())
+          .append(")");
+    }
+  }
+
+  /** How to display type nullability. */
+  public enum NullabilityDisplay {
+    /** Display NULL if type is nullable. */
+    DisplayNullability,
+    /** Display NOT NULL if type is not nullable. */
+    DisplayNonNullability,
+    /** Do not display nullability information at all. */
+    DoNotDisplay
+  }
+
+  /** Produce a string which describes the {@code type} using Calcite's SQL 
syntax for types. */
+  public static String asSqlType(RelDataType type, NullabilityDisplay 
nullDisplay) {
+    StringBuilder builder = new StringBuilder();
+    switch (type.getSqlTypeName()) {
+    case BOOLEAN:
+      builder.append("BOOLEAN");
+      break;
+    case TINYINT:
+      builder.append("TINYINT");
+      break;
+    case SMALLINT:
+      builder.append("SMALLINT");
+      break;
+    case INTEGER:
+      builder.append("INTEGER");
+      break;
+    case BIGINT:
+      builder.append("BIGINT");
+      break;
+    case UTINYINT:
+      builder.append("TINYINT UNSIGNED");
+      break;
+    case USMALLINT:
+      builder.append("SMALLINT UNSIGNED");
+      break;
+    case UINTEGER:
+      builder.append("INTEGER UNSIGNED");
+      break;
+    case UBIGINT:
+      builder.append("BIGINT UNSIGNED");
+      break;
+    case DECIMAL:
+      builder.append("DECIMAL");
+      appendPrecisionScale(type, builder);
+      break;
+    case FLOAT:
+      builder.append("FLOAT");
+      break;
+    case REAL:
+      builder.append("REAL");
+      break;
+    case DOUBLE:
+      builder.append("DOUBLE");
+      break;
+    case DATE:
+      builder.append("DATE");
+      break;
+    case TIME:
+      builder.append("TIME");
+      appendPrecision(type, builder, false);
+      break;
+    case TIME_WITH_LOCAL_TIME_ZONE:
+      builder.append("TIME WITH LOCAL TIME ZONE");
+      appendPrecision(type, builder, false);
+      break;
+    case TIME_TZ:
+      builder.append("TIME WITH TIME ZONE");
+      appendPrecision(type, builder, false);
+      break;
+    case TIMESTAMP:
+      builder.append("TIMESTAMP");
+      appendPrecision(type, builder, false);
+      break;
+    case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+      builder.append("TIMESTAMP WITH LOCAL TIME ZONE");
+      appendPrecision(type, builder, false);
+      break;
+    case TIMESTAMP_TZ:
+      builder.append("TIMESTAMP WITH TIME ZONE");
+      appendPrecision(type, builder, false);
+      break;
+    case INTERVAL_YEAR:
+    case INTERVAL_YEAR_MONTH:
+    case INTERVAL_MONTH:
+    case INTERVAL_DAY:
+    case INTERVAL_DAY_HOUR:
+    case INTERVAL_DAY_MINUTE:
+    case INTERVAL_DAY_SECOND:
+    case INTERVAL_HOUR:
+    case INTERVAL_HOUR_MINUTE:
+    case INTERVAL_HOUR_SECOND:
+    case INTERVAL_MINUTE:
+    case INTERVAL_MINUTE_SECOND:
+    case INTERVAL_SECOND:
+      builder.append("INTERVAL ");
+      builder.append(type.getIntervalQualifier());
+      break;
+    case CHAR:
+      builder.append("CHAR");
+      appendPrecision(type, builder, false);
+      break;
+    case VARCHAR:
+      builder.append("VARCHAR");
+      appendPrecision(type, builder, false);
+      break;
+    case BINARY:
+      builder.append("BINARY");
+      appendPrecision(type, builder, false);
+      break;
+    case VARBINARY:
+      builder.append("VARBINARY");
+      appendPrecision(type, builder, false);
+      break;
+    case NULL:
+      // No suffix needed
+      return "NULL";
+    case UNKNOWN:
+      return "UNKNOWN";
+    case ANY:
+      return "ANY";
+    case MULTISET: {
+      String elementType =
+          asSqlType(requireNonNull(type.getComponentType(), "componentType"),
+              NullabilityDisplay.DoNotDisplay);
+      builder.append(elementType);
+      builder.append(" MULTISET");
+      break;
+    }
+    case ARRAY: {
+      String elementType =
+          asSqlType(requireNonNull(type.getComponentType(), "componentType"),
+              NullabilityDisplay.DoNotDisplay);
+      builder.append(elementType);
+      builder.append(" ARRAY");
+      break;
+    }
+    case MAP:
+      builder.append("MAP<");
+      String keyType =
+          asSqlType(requireNonNull(type.getKeyType(), "keyType"),
+              NullabilityDisplay.DoNotDisplay);
+      String valueType =
+          asSqlType(requireNonNull(type.getValueType(), "valueType"),
+              NullabilityDisplay.DoNotDisplay);
+      builder.append(keyType)
+          .append(", ")
+          .append(valueType)
+          .append(">");
+      break;
+    case ROW: {
+      builder.append("ROW(");
+      boolean first = true;
+      for (RelDataTypeField field : type.getFieldList()) {
+        if (!first) {
+          builder.append(", ");
+        }
+        first = false;
+        String fieldType = asSqlType(field.getType(), 
NullabilityDisplay.DisplayNullability);
+        builder.append(fieldType)
+            .append(" ")
+            .append(field.getName());
+      }
+      builder.append(")");
+      break;
+    }
+    case GEOMETRY:
+      break;
+    case MEASURE:
+      builder.append("MEASURE");
+      break;
+    case FUNCTION: {
+      FunctionSqlType function = (FunctionSqlType) type;
+      builder.append("FUNCTION(");
+      // Parameter is expected to always have a ROW type
+      boolean first = true;
+      for (RelDataTypeField field : 
function.getParameterTypes().getFieldList()) {
+        if (!first) {
+          builder.append(", ");
+        }
+        first = false;
+        String fieldType = asSqlType(field.getType(), 
NullabilityDisplay.DoNotDisplay);
+        builder.append(fieldType);
+      }
+      builder.append(") -> ");
+      String result = asSqlType(function.getReturnType(), 
NullabilityDisplay.DoNotDisplay);
+      builder.append(result);
+      break;
+    }
+    case CURSOR:
+      return "CURSOR";
+    case COLUMN_LIST:
+      return "COLUMN_LIST";
+    case SYMBOL:
+    case DISTINCT:
+    case STRUCTURED:
+    case OTHER:
+    case DYNAMIC_STAR:
+    case SARG:
+      return "";
+    case UUID:
+      builder.append("UUID");
+      break;
+    case VARIANT:
+      builder.append("VARIANT");
+      break;
+    }
+    switch (nullDisplay) {
+    case DoNotDisplay:
+      break;
+    case DisplayNonNullability:
+      if (!type.isNullable()) {
+        builder.append(" NOT NULL");
+      }
+      break;
+    case DisplayNullability:
+      if (type.isNullable()) {
+        builder.append(" NULL");
+      }
+      break;
+    }
+    return builder.toString();
+  }
 }
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 9a3fe9b833..8e4e76378b 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -540,7 +540,7 @@ static SqlOperatorTable operatorTableFor(SqlLibrary 
library) {
             + "'<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>'.*");
+            + "'<MAP<BINARY.1., INTEGER>> = <MAP<INTEGER, INTEGER>>'.*");
     expr("^array[x'a4'] = 1^")
         .fails("(?s).*Cannot apply '=' to arguments of type '<BINARY.1. ARRAY> 
= <INTEGER>'.*");
     expr("^multiset[x'a4'] = multiset[1]^")
@@ -6513,7 +6513,7 @@ void testReturnsCorrectRowTypeOnCombinedJoin() {
     final String sql = "select * from emp as e join dept d\n"
         + "on d.deptno = ^(select 1, 2 from emp where deptno < e.deptno)^";
     final String expected = "(?s)Cannot apply '\\$SCALAR_QUERY' to arguments "
-        + "of type '\\$SCALAR_QUERY\\(<RECORDTYPE\\(INTEGER EXPR\\$0, INTEGER "
+        + "of type '\\$SCALAR_QUERY\\(<ROW\\(INTEGER EXPR\\$0, INTEGER "
         + "EXPR\\$1\\)>\\)'\\. Supported form\\(s\\).*";
     sql(sql).fails(expected);
   }
@@ -9779,9 +9779,9 @@ void testGroupExpressionEquivalenceParams() {
     sql("SELECT  ename,(select name from dept where deptno=1) FROM emp").ok();
     sql("SELECT ename,^(select losal, hisal from salgrade where grade=1)^ FROM 
emp")
         .fails("Cannot apply '\\$SCALAR_QUERY' to arguments of type "
-            + "'\\$SCALAR_QUERY\\(<RECORDTYPE\\(INTEGER LOSAL, "
+            + "'\\$SCALAR_QUERY\\(<ROW\\(INTEGER LOSAL, "
             + "INTEGER HISAL\\)>\\)'\\. Supported form\\(s\\): "
-            + "'\\$SCALAR_QUERY\\(<RECORDTYPE\\(SINGLE FIELD\\)>\\)'");
+            + "'\\$SCALAR_QUERY\\(<ROW\\(SINGLE FIELD\\)>\\)'");
 
     // Note that X is a field (not a record) and is nullable even though
     // EMP.NAME is NOT NULL.
@@ -12134,7 +12134,7 @@ private void checkCustomColumnResolving(String table) {
     sql("select rowtime, productid, orderid, 'window_start', 'window_end'\n"
         + "from table(\n"
         + "^tumble(table orders, descriptor(productid), interval '2' hour)^)")
-        .fails("Cannot apply 'TUMBLE' to arguments of type 
'TUMBLE\\(<RECORDTYPE\\"
+        .fails("Cannot apply 'TUMBLE' to arguments of type 'TUMBLE\\(<ROW\\"
             + "(TIMESTAMP\\(0\\) ROWTIME, INTEGER PRODUCTID, INTEGER 
ORDERID\\)>, <COLUMN_LIST>, "
             + "<INTERVAL HOUR>\\)'\\. Supported form\\(s\\): TUMBLE\\(TABLE 
table_name, "
             + "DESCRIPTOR\\(timecol\\), datetime interval\\[, datetime 
interval\\]\\)");
@@ -12144,7 +12144,7 @@ private void checkCustomColumnResolving(String table) {
         + "data => table orders,\n"
         + "timecol => descriptor(productid),\n"
         + "size => interval '2' hour)^)")
-        .fails("Cannot apply 'TUMBLE' to arguments of type 
'TUMBLE\\(<RECORDTYPE\\"
+        .fails("Cannot apply 'TUMBLE' to arguments of type 'TUMBLE\\(<ROW\\"
             + "(TIMESTAMP\\(0\\) ROWTIME, INTEGER PRODUCTID, INTEGER 
ORDERID\\)>, <COLUMN_LIST>, "
             + "<INTERVAL HOUR>\\)'\\. Supported form\\(s\\): TUMBLE\\(TABLE 
table_name, "
             + "DESCRIPTOR\\(timecol\\), datetime interval\\[, datetime 
interval\\]\\)");
@@ -12159,21 +12159,21 @@ private void checkCustomColumnResolving(String table) 
{
         .fails("Invalid number of arguments to function 'TUMBLE'. Was 
expecting 3 arguments");
     sql("select * from table(\n"
         + "^tumble(table orders, descriptor(rowtime), 'test')^)")
-        .fails("Cannot apply 'TUMBLE' to arguments of type 
'TUMBLE\\(<RECORDTYPE\\"
+        .fails("Cannot apply 'TUMBLE' to arguments of type 'TUMBLE\\(<ROW\\"
             + "(TIMESTAMP\\(0\\) ROWTIME, INTEGER PRODUCTID, INTEGER 
ORDERID\\)>, <COLUMN_LIST>,"
             + " <CHAR\\(4\\)>\\)'\\. Supported form\\(s\\): TUMBLE\\(TABLE "
             + "table_name, DESCRIPTOR\\(timecol\\), datetime interval"
             + "\\[, datetime interval\\]\\)");
     sql("select * from table(\n"
         + "^tumble(table orders, 'test', interval '2' hour)^)")
-        .fails("Cannot apply 'TUMBLE' to arguments of type 
'TUMBLE\\(<RECORDTYPE\\"
+        .fails("Cannot apply 'TUMBLE' to arguments of type 'TUMBLE\\(<ROW\\"
             + "(TIMESTAMP\\(0\\) ROWTIME, INTEGER PRODUCTID, INTEGER 
ORDERID\\)>, <CHAR\\"
             + "(4\\)>, <INTERVAL HOUR>\\)'\\. Supported form\\(s\\): 
TUMBLE\\(TABLE "
             + "table_name, DESCRIPTOR\\(timecol\\), datetime interval"
             + "\\[, datetime interval\\]\\)");
     sql("select rowtime, productid, orderid, 'window_start', 'window_end' from 
table(\n"
         + "^tumble(table orders, descriptor(rowtime), interval '2' hour, 
'test')^)")
-        .fails("Cannot apply 'TUMBLE' to arguments of type 
'TUMBLE\\(<RECORDTYPE\\"
+        .fails("Cannot apply 'TUMBLE' to arguments of type 'TUMBLE\\(<ROW\\"
             + "(TIMESTAMP\\(0\\) ROWTIME, INTEGER PRODUCTID, INTEGER 
ORDERID\\)>, <COLUMN_LIST>,"
             + " <INTERVAL HOUR>, <CHAR\\(4\\)>\\)'\\. Supported form\\(s\\): 
TUMBLE\\(TABLE "
             + "table_name, DESCRIPTOR\\(timecol\\), datetime interval"
@@ -12221,7 +12221,7 @@ private void checkCustomColumnResolving(String table) {
     sql("select rowtime, productid, orderid, 'window_start', 'window_end'\n"
         + "from table(\n"
         + "^hop(table orders, descriptor(productid), interval '2' hour, 
interval '1' hour)^)")
-        .fails("Cannot apply 'HOP' to arguments of type 'HOP\\(<RECORDTYPE\\"
+        .fails("Cannot apply 'HOP' to arguments of type 'HOP\\(<ROW\\"
             + "(TIMESTAMP\\(0\\) ROWTIME, INTEGER PRODUCTID, INTEGER 
ORDERID\\)>, <COLUMN_LIST>, "
             + "<INTERVAL HOUR>, <INTERVAL HOUR>\\)'\\. Supported form\\(s\\): "
             + "HOP\\(TABLE table_name, DESCRIPTOR\\(timecol\\), "
@@ -12233,7 +12233,7 @@ private void checkCustomColumnResolving(String table) {
         + "timecol => descriptor(productid),\n"
         + "size => interval '2' hour,\n"
         + "slide => interval '1' hour)^)")
-        .fails("Cannot apply 'HOP' to arguments of type 'HOP\\(<RECORDTYPE\\"
+        .fails("Cannot apply 'HOP' to arguments of type 'HOP\\(<ROW\\"
             + "(TIMESTAMP\\(0\\) ROWTIME, INTEGER PRODUCTID, INTEGER 
ORDERID\\)>, <COLUMN_LIST>, "
             + "<INTERVAL HOUR>, <INTERVAL HOUR>\\)'\\. Supported form\\(s\\): "
             + "HOP\\(TABLE table_name, DESCRIPTOR\\(timecol\\), "
@@ -12249,25 +12249,25 @@ private void checkCustomColumnResolving(String table) 
{
         .fails("Invalid number of arguments to function 'HOP'. Was expecting 4 
arguments");
     sql("select * from table(\n"
         + "^hop(table orders, descriptor(rowtime), interval '2' hour, 
'test')^)")
-        .fails("Cannot apply 'HOP' to arguments of type 
'HOP\\(<RECORDTYPE\\(TIMESTAMP\\(0\\) "
+        .fails("Cannot apply 'HOP' to arguments of type 
'HOP\\(<ROW\\(TIMESTAMP\\(0\\) "
             + "ROWTIME, INTEGER PRODUCTID, INTEGER ORDERID\\)>, <COLUMN_LIST>, 
<INTERVAL HOUR>, "
             + "<CHAR\\(4\\)>\\)'. Supported form\\(s\\): HOP\\(TABLE 
table_name, DESCRIPTOR\\("
             + "timecol\\), datetime interval, datetime interval\\[, datetime 
interval\\]\\)");
     sql("select * from table(\n"
         + "^hop(table orders, descriptor(rowtime), 'test', interval '2' 
hour)^)")
-        .fails("Cannot apply 'HOP' to arguments of type 
'HOP\\(<RECORDTYPE\\(TIMESTAMP\\(0\\) "
+        .fails("Cannot apply 'HOP' to arguments of type 
'HOP\\(<ROW\\(TIMESTAMP\\(0\\) "
             + "ROWTIME, INTEGER PRODUCTID, INTEGER ORDERID\\)>, <COLUMN_LIST>, 
<CHAR\\(4\\)>, "
             + "<INTERVAL HOUR>\\)'. Supported form\\(s\\): HOP\\(TABLE 
table_name, DESCRIPTOR\\("
             + "timecol\\), datetime interval, datetime interval\\[, datetime 
interval\\]\\)");
     sql("select * from table(\n"
         + "^hop(table orders, 'test', interval '2' hour, interval '2' hour)^)")
-        .fails("Cannot apply 'HOP' to arguments of type 
'HOP\\(<RECORDTYPE\\(TIMESTAMP\\(0\\) "
+        .fails("Cannot apply 'HOP' to arguments of type 
'HOP\\(<ROW\\(TIMESTAMP\\(0\\) "
             + "ROWTIME, INTEGER PRODUCTID, INTEGER ORDERID\\)>, <CHAR\\(4\\)>, 
<INTERVAL HOUR>, "
             + "<INTERVAL HOUR>\\)'. Supported form\\(s\\): HOP\\(TABLE 
table_name, DESCRIPTOR\\("
             + "timecol\\), datetime interval, datetime interval\\[, datetime 
interval\\]\\)");
     sql("select * from table(\n"
         + "^hop(table orders, descriptor(rowtime), interval '2' hour, interval 
'1' hour, 'test')^)")
-        .fails("Cannot apply 'HOP' to arguments of type 
'HOP\\(<RECORDTYPE\\(TIMESTAMP\\(0\\) "
+        .fails("Cannot apply 'HOP' to arguments of type 
'HOP\\(<ROW\\(TIMESTAMP\\(0\\) "
             + "ROWTIME, INTEGER PRODUCTID, INTEGER ORDERID\\)>, <COLUMN_LIST>, 
<INTERVAL HOUR>, "
             + "<INTERVAL HOUR>, <CHAR\\(4\\)>\\)'. Supported form\\(s\\): 
HOP\\(TABLE table_name, "
             + "DESCRIPTOR\\(timecol\\), datetime interval, datetime 
interval\\[, datetime interval\\]\\)");
@@ -12308,25 +12308,25 @@ private void checkCustomColumnResolving(String table) 
{
         + "data => table orders,\n"
         + "key => descriptor(productid),\n"
         + "size => interval '1' hour)^)")
-        .fails("Cannot apply 'SESSION' to arguments of type 
'SESSION\\(<RECORDTYPE\\(TIMESTAMP\\("
+        .fails("Cannot apply 'SESSION' to arguments of type 
'SESSION\\(<ROW\\(TIMESTAMP\\("
             + "0\\) ROWTIME, INTEGER PRODUCTID, INTEGER ORDERID\\)>, 
<COLUMN_LIST>, "
             + "<INTERVAL HOUR>\\)'. Supported form\\(s\\): SESSION\\(TABLE 
table_name, DESCRIPTOR\\("
             + "timecol\\), DESCRIPTOR\\(key\\) optional, datetime 
interval\\)");
     sql("select * from table(\n"
         + "^session(table orders, descriptor(rowtime), descriptor(productid), 
'test')^)")
-        .fails("Cannot apply 'SESSION' to arguments of type 
'SESSION\\(<RECORDTYPE\\(TIMESTAMP\\("
+        .fails("Cannot apply 'SESSION' to arguments of type 
'SESSION\\(<ROW\\(TIMESTAMP\\("
             + "0\\) ROWTIME, INTEGER PRODUCTID, INTEGER ORDERID\\)>, 
<COLUMN_LIST>, <COLUMN_LIST>, "
             + "<CHAR\\(4\\)>\\)'. Supported form\\(s\\): SESSION\\(TABLE 
table_name, DESCRIPTOR\\("
             + "timecol\\), DESCRIPTOR\\(key\\) optional, datetime 
interval\\)");
     sql("select * from table(\n"
         + "^session(table orders, descriptor(rowtime), 'test', interval '2' 
hour)^)")
-        .fails("Cannot apply 'SESSION' to arguments of type 
'SESSION\\(<RECORDTYPE\\(TIMESTAMP\\("
+        .fails("Cannot apply 'SESSION' to arguments of type 
'SESSION\\(<ROW\\(TIMESTAMP\\("
             + "0\\) ROWTIME, INTEGER PRODUCTID, INTEGER ORDERID\\)>, 
<COLUMN_LIST>, <CHAR\\(4\\)>, "
             + "<INTERVAL HOUR>\\)'. Supported form\\(s\\): SESSION\\(TABLE 
table_name, DESCRIPTOR\\("
             + "timecol\\), DESCRIPTOR\\(key\\) optional, datetime 
interval\\)");
     sql("select * from table(\n"
         + "^session(table orders, 'test', descriptor(productid), interval '2' 
hour)^)")
-        .fails("Cannot apply 'SESSION' to arguments of type 
'SESSION\\(<RECORDTYPE\\(TIMESTAMP\\("
+        .fails("Cannot apply 'SESSION' to arguments of type 
'SESSION\\(<ROW\\(TIMESTAMP\\("
             + "0\\) ROWTIME, INTEGER PRODUCTID, INTEGER ORDERID\\)>, 
<CHAR\\(4\\)>, <COLUMN_LIST>, "
             + "<INTERVAL HOUR>\\)'. Supported form\\(s\\): SESSION\\(TABLE 
table_name, DESCRIPTOR\\("
             + "timecol\\), DESCRIPTOR\\(key\\) optional, datetime 
interval\\)");
@@ -14295,4 +14295,26 @@ private static SqlIdentifier 
rewriteIdentifier(SqlIdentifier sqlIdentifier) {
       }
     }
   }
+
+  /** Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-7582";>[CALCITE-7582]
+   * Type validation errors should use SQL type names</a>. */
+  @Test public void testErrorMessage() {
+    sql("SELECT ^(DATE '2020-02-02', DATE '2021-01-01') CONTAINS TIME 
'10:00:00'^")
+        .fails(".*Cannot apply 'CONTAINS' to arguments of type '<ROW\\(DATE 
EXPR\\$0, "
+              + "DATE EXPR\\$1\\)> "
+              + "CONTAINS <TIME\\(0\\)>'\\. Supported form\\(s\\): '\\(<DT>, 
<DT>\\) "
+              + "CONTAINS \\(<DT>, <DT>\\)'\\n"
+              + "'\\(<DT>, <DT>\\) CONTAINS \\(<DT>, <INTERVAL>\\)'\n"
+              + "'\\(<DT>, <INTERVAL>\\) CONTAINS \\(<DT>, <DT>\\)'\n"
+              + "'\\(<DT>, <INTERVAL>\\) CONTAINS \\(<DT>, <INTERVAL>\\)'\n"
+              + "'\\(<DT>, <DT>\\) CONTAINS <DT>'\n"
+              + "'\\(<DT>, <INTERVAL>\\) CONTAINS <DT>'\\n"
+              + "Where 'DT' is one of 'DATE', 'TIME', or 'TIMESTAMP', "
+              + "the same for all arguments\\.");
+    sql("SELECT ^MAP['x', ARRAY[3]] = "
+        + "MAP[CAST(ROW(1, 2) AS ROW(X INT, Y BIGINT)), ROW(ARRAY['a'])]^")
+        .fails("Cannot apply '=' to arguments of type '<MAP<CHAR\\(1\\), 
INTEGER ARRAY>> = "
+            + "<MAP<ROW\\(INTEGER X, BIGINT Y\\), ROW\\(CHAR\\(1\\) ARRAY 
EXPR\\$0\\)>>'\\. "
+            + "Supported form\\(s\\): '<COMPARABLE_TYPE> = 
<COMPARABLE_TYPE>'");
+  }
 }
diff --git a/core/src/test/resources/sql/misc.iq 
b/core/src/test/resources/sql/misc.iq
index 05697b8118..f5457a79c7 100644
--- a/core/src/test/resources/sql/misc.iq
+++ b/core/src/test/resources/sql/misc.iq
@@ -175,7 +175,7 @@ SELECT ROW('1') > ROW(0) AS C;
 # [CALCITE-6735] Type coercion for comparisons does not coerce ROW types
 # These are compared in the same way as x'10' > 0, which throws
 SELECT ROW(x'10') > ROW(0) AS C;
-java.sql.SQLException: Error while executing SQL "SELECT ROW(x'10') > ROW(0) 
AS C": From line 1, column 11 to line 1, column 26: Cannot apply '>' to 
arguments of type '<RECORDTYPE(BINARY(1) EXPR$0)> > <RECORDTYPE(INTEGER 
EXPR$0)>'.
+java.sql.SQLException: Error while executing SQL "SELECT ROW(x'10') > ROW(0) 
AS C": From line 1, column 11 to line 1, column 26: Cannot apply '>' to 
arguments of type '<ROW(BINARY(1) EXPR$0)> > <ROW(INTEGER EXPR$0)>'.
 !error
 
 # [CALCITE-6733] Type inferred by coercion for comparisons with decimal is too 
narrow
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 c40f077d8d..d399890342 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -3321,26 +3321,26 @@ static void checkOverlaps(OverlapChecker c) {
     final SqlOperatorFixture f = fixture();
     f.checkFails("^(DATE '2020-10-10', DATE '2021-10-10') CONTAINS TIME 
'10:00:00'^",
         "Cannot apply 'CONTAINS' to arguments of type "
-            + "'<RECORDTYPE\\(DATE EXPR\\$0, DATE EXPR\\$1\\)> CONTAINS 
<TIME\\(0\\)>'\\. "
+            + "'<ROW\\(DATE EXPR\\$0, DATE EXPR\\$1\\)> CONTAINS 
<TIME\\(0\\)>'\\. "
             + containsError, false);
     f.checkFails("^(DATE '2020-10-10', DATE '2021-10-10') CONTAINS "
             + "TIMESTAMP '2010-01-01 10:00:00'^",
         "Cannot apply 'CONTAINS' to arguments of type "
-            + "'<RECORDTYPE\\(DATE EXPR\\$0, DATE EXPR\\$1\\)> CONTAINS 
<TIMESTAMP\\(0\\)>'\\. "
+            + "'<ROW\\(DATE EXPR\\$0, DATE EXPR\\$1\\)> CONTAINS 
<TIMESTAMP\\(0\\)>'\\. "
             + containsError, false);
     f.checkFails("^(DATE '2020-10-10', TIMESTAMP '2021-10-10 00:00:00') "
             + "CONTAINS TIMESTAMP '2010-01-01 10:00:00'^",
         "Cannot apply 'CONTAINS' to arguments of type "
-            + "'<RECORDTYPE\\(DATE EXPR\\$0, TIMESTAMP\\(0\\) EXPR\\$1\\)> "
+            + "'<ROW\\(DATE EXPR\\$0, TIMESTAMP\\(0\\) EXPR\\$1\\)> "
             + "CONTAINS <TIMESTAMP\\(0\\)>'\\. "
             + containsError, false);
     f.checkFails("^(TIME '10:10:10', DATE '2021-10-10') CONTAINS TIME 
'10:00:00'^",
         "Cannot apply 'CONTAINS' to arguments of type "
-            + "'<RECORDTYPE\\(TIME\\(0\\) EXPR\\$0, DATE EXPR\\$1\\)> CONTAINS 
<TIME\\(0\\)>'\\. "
+            + "'<ROW\\(TIME\\(0\\) EXPR\\$0, DATE EXPR\\$1\\)> CONTAINS 
<TIME\\(0\\)>'\\. "
             + containsError, false);
     f.checkFails("^(TIME '10:10:10', DATE '2021-10-10') CONTAINS TIMESTAMP 
'2010-02-02 10:00:00'^",
         "Cannot apply 'CONTAINS' to arguments of type "
-            + "'<RECORDTYPE\\(TIME\\(0\\) EXPR\\$0, DATE EXPR\\$1\\)> "
+            + "'<ROW\\(TIME\\(0\\) EXPR\\$0, DATE EXPR\\$1\\)> "
             + "CONTAINS <TIMESTAMP\\(0\\)>'\\. "
             + containsError, false);
     final String overlapsError = "Supported form\\(s\\): "
@@ -3352,21 +3352,21 @@ static void checkOverlaps(OverlapChecker c) {
     f.checkFails("^(TIME '10:10:10', DATE '2021-10-10') OVERLAPS "
             + "(TIMESTAMP '2010-02-02 10:00:00', TIME '10:00:00')^",
         "Cannot apply 'OVERLAPS' to arguments of type "
-            + "'<RECORDTYPE\\(TIME\\(0\\) EXPR\\$0, DATE EXPR\\$1\\)> "
-            + "OVERLAPS <RECORDTYPE\\(TIMESTAMP\\(0\\) EXPR\\$0, TIME\\(0\\) 
EXPR\\$1\\)>'\\. "
+            + "'<ROW\\(TIME\\(0\\) EXPR\\$0, DATE EXPR\\$1\\)> "
+            + "OVERLAPS <ROW\\(TIMESTAMP\\(0\\) EXPR\\$0, TIME\\(0\\) 
EXPR\\$1\\)>'\\. "
             + overlapsError, false);
     f.checkFails("^(TIME '10:10:10', DATE '2021-10-10') "
             + "OVERLAPS (TIME '10:00:00', DATE '2020-01-01')^",
         "Cannot apply 'OVERLAPS' to arguments of type "
-            + "'<RECORDTYPE\\(TIME\\(0\\) EXPR\\$0, DATE EXPR\\$1\\)> "
-            + "OVERLAPS <RECORDTYPE\\(TIME\\(0\\) EXPR\\$0, DATE 
EXPR\\$1\\)>'\\. "
+            + "'<ROW\\(TIME\\(0\\) EXPR\\$0, DATE EXPR\\$1\\)> "
+            + "OVERLAPS <ROW\\(TIME\\(0\\) EXPR\\$0, DATE EXPR\\$1\\)>'\\. "
             + overlapsError, false);
     final String precedesError = overlapsError.replace("OVERLAPS", "PRECEDES");
     f.checkFails("^(TIME '10:10:10', DATE '2021-10-10') "
             + "PRECEDES (TIME '10:00:00', TIME '10:10:10')^",
         "Cannot apply 'PRECEDES' to arguments of type "
-            + "'<RECORDTYPE\\(TIME\\(0\\) EXPR\\$0, DATE EXPR\\$1\\)> "
-            + "PRECEDES <RECORDTYPE\\(TIME\\(0\\) EXPR\\$0, TIME\\(0\\) 
EXPR\\$1\\)>'\\. "
+            + "'<ROW\\(TIME\\(0\\) EXPR\\$0, DATE EXPR\\$1\\)> "
+            + "PRECEDES <ROW\\(TIME\\(0\\) EXPR\\$0, TIME\\(0\\) 
EXPR\\$1\\)>'\\. "
             + precedesError, false);
   }
 
@@ -9553,12 +9553,12 @@ void checkArrayReverseFunc(SqlOperatorFixture f0, 
SqlFunction function,
     f.checkFails("^map_from_entries(array[1])^",
         "Cannot apply 'MAP_FROM_ENTRIES' to arguments of type 
'MAP_FROM_ENTRIES\\("
             + "<INTEGER ARRAY>\\)'. Supported form\\(s\\): 
'MAP_FROM_ENTRIES\\("
-            + "<ARRAY<RECORDTYPE\\(TWO FIELDS\\)>>\\)'",
+            + "<ARRAY<ROW\\(TWO FIELDS\\)>>\\)'",
         false);
     f.checkFails("^map_from_entries(array[row(1, 'a', 2)])^",
         "Cannot apply 'MAP_FROM_ENTRIES' to arguments of type 
'MAP_FROM_ENTRIES\\("
-            + "<RECORDTYPE\\(INTEGER EXPR\\$0, CHAR\\(1\\) EXPR\\$1, INTEGER 
EXPR\\$2\\) ARRAY>\\)'. "
-            + "Supported form\\(s\\): 
'MAP_FROM_ENTRIES\\(<ARRAY<RECORDTYPE\\(TWO FIELDS\\)>>\\)'",
+            + "<ROW\\(INTEGER EXPR\\$0, CHAR\\(1\\) EXPR\\$1, INTEGER 
EXPR\\$2\\) ARRAY>\\)'. "
+            + "Supported form\\(s\\): 'MAP_FROM_ENTRIES\\(<ARRAY<ROW\\(TWO 
FIELDS\\)>>\\)'",
         false);
   }
 
@@ -13741,8 +13741,8 @@ private static void 
checkArrayConcatAggFuncFails(SqlOperatorFixture t) {
     f.checkFails("ARRAY[2,4,6][OFFSET(5)]",
         "Array index 5 is out of bounds", true);
     f.checkFails("^map['foo', 3, 'bar', 7][offset('bar')]^",
-        "Cannot apply 'OFFSET' to arguments of type 'OFFSET\\(<\\(CHAR\\(3\\)"
-            + ", INTEGER\\) MAP>, <CHAR\\(3\\)>\\)'\\. Supported form\\(s\\): "
+        "Cannot apply 'OFFSET' to arguments of type 'OFFSET\\(<MAP<CHAR\\(3\\)"
+            + ", INTEGER>>, <CHAR\\(3\\)>\\)'\\. Supported form\\(s\\): "
             + "<ARRAY>\\[OFFSET\\(<INTEGER>\\)\\]", false);
   }
 
@@ -13761,8 +13761,8 @@ private static void 
checkArrayConcatAggFuncFails(SqlOperatorFixture t) {
     f.checkFails("ARRAY[2,4,6][ORDINAL(5)]",
         "Array index 5 is out of bounds", true);
     f.checkFails("^map['foo', 3, 'bar', 7][ordinal('bar')]^",
-        "Cannot apply 'ORDINAL' to arguments of type 
'ORDINAL\\(<\\(CHAR\\(3\\)"
-            + ", INTEGER\\) MAP>, <CHAR\\(3\\)>\\)'\\. Supported form\\(s\\): "
+        "Cannot apply 'ORDINAL' to arguments of type 
'ORDINAL\\(<MAP<CHAR\\(3\\)"
+            + ", INTEGER>>, <CHAR\\(3\\)>\\)'\\. Supported form\\(s\\): "
             + "<ARRAY>\\[ORDINAL\\(<INTEGER>\\)\\]", false);
   }
 
@@ -13779,8 +13779,8 @@ private static void 
checkArrayConcatAggFuncFails(SqlOperatorFixture t) {
     f.checkScalar("ARRAY[2,4,6][SAFE_OFFSET(5)]", isNullValue(), "INTEGER");
     f.checkNull("ARRAY[2,4,6][SAFE_OFFSET(null)]");
     f.checkFails("^map['foo', 3, 'bar', 7][safe_offset('bar')]^",
-        "Cannot apply 'SAFE_OFFSET' to arguments of type 
'SAFE_OFFSET\\(<\\(CHAR\\(3\\)"
-            + ", INTEGER\\) MAP>, <CHAR\\(3\\)>\\)'\\. Supported form\\(s\\): "
+        "Cannot apply 'SAFE_OFFSET' to arguments of type 
'SAFE_OFFSET\\(<MAP<CHAR\\(3\\)"
+            + ", INTEGER>>, <CHAR\\(3\\)>\\)'\\. Supported form\\(s\\): "
             + "<ARRAY>\\[SAFE_OFFSET\\(<INTEGER>\\)\\]", false);
   }
 
@@ -13797,8 +13797,8 @@ private static void 
checkArrayConcatAggFuncFails(SqlOperatorFixture t) {
     f.checkScalar("ARRAY[2,4,6][SAFE_ORDINAL(5)]", isNullValue(), "INTEGER");
     f.checkNull("ARRAY[2,4,6][SAFE_ORDINAL(null)]");
     f.checkFails("^map['foo', 3, 'bar', 7][safe_ordinal('bar')]^",
-        "Cannot apply 'SAFE_ORDINAL' to arguments of type 
'SAFE_ORDINAL\\(<\\(CHAR\\(3\\)"
-            + ", INTEGER\\) MAP>, <CHAR\\(3\\)>\\)'\\. Supported form\\(s\\): "
+        "Cannot apply 'SAFE_ORDINAL' to arguments of type 
'SAFE_ORDINAL\\(<MAP<CHAR\\(3\\)"
+            + ", INTEGER>>, <CHAR\\(3\\)>\\)'\\. Supported form\\(s\\): "
             + "<ARRAY>\\[SAFE_ORDINAL\\(<INTEGER>\\)\\]", false);
   }
 

Reply via email to