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);
}