This is an automated email from the ASF dual-hosted git repository.
dmsysolyatin 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 2826a1c5c5 [CALCITE-5779] Implicit column alias for single-column
table function should work
2826a1c5c5 is described below
commit 2826a1c5c5df691a29427ba3eba6c6715e103045
Author: dssysolyatin <[email protected]>
AuthorDate: Wed Jun 14 13:43:52 2023 +0300
[CALCITE-5779] Implicit column alias for single-column table function
should work
Close apache/calcite#3270
---
.../rel/type/SingleColumnAliasRelDataType.java | 139 +++++++++++++++++++++
.../calcite/sql/validate/AliasNamespace.java | 5 +-
.../calcite/sql/validate/SqlValidatorImpl.java | 4 +-
.../apache/calcite/test/SqlToRelConverterTest.java | 17 +++
.../org/apache/calcite/test/SqlValidatorTest.java | 12 ++
.../apache/calcite/test/SqlToRelConverterTest.xml | 21 ++++
core/src/test/resources/sql/functions.iq | 17 +++
.../org/apache/calcite/test/CalciteAssert.java | 3 +
.../java/org/apache/calcite/test/QuidemTest.java | 4 +
.../main/java/org/apache/calcite/util/Smalls.java | 22 ++++
10 files changed, 241 insertions(+), 3 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/rel/type/SingleColumnAliasRelDataType.java
b/core/src/main/java/org/apache/calcite/rel/type/SingleColumnAliasRelDataType.java
new file mode 100644
index 0000000000..b09196b577
--- /dev/null
+++
b/core/src/main/java/org/apache/calcite/rel/type/SingleColumnAliasRelDataType.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel.type;
+
+import org.apache.calcite.sql.SqlCollation;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlIntervalQualifier;
+import org.apache.calcite.sql.type.SqlTypeName;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.nio.charset.Charset;
+import java.util.List;
+
+/**
+ * Specific type of RelDataType that corresponds to a single column table,
+ * where column can have alias.
+ *
+ * <p>For instance:
+ * <blockquote><pre>select rmp, rmp.i from table(ramp(3)) as
rmp;</pre></blockquote>
+ *
+ * @see org.apache.calcite.sql.validate.AliasNamespace
+ */
+public class SingleColumnAliasRelDataType implements RelDataType {
+ private final RelDataType original;
+ private final RelDataType alias;
+
+ public SingleColumnAliasRelDataType(RelDataType original, RelDataType alias)
{
+ assert original.isStruct() && original.getFieldCount() == 1;
+ assert alias.isStruct() && alias.getFieldCount() == 1;
+ this.original = original;
+ this.alias = alias;
+ }
+
+ @Override public boolean isStruct() {
+ return true;
+ }
+
+ @Override public List<RelDataTypeField> getFieldList() {
+ return original.getFieldList();
+ }
+
+ @Override public List<String> getFieldNames() {
+ return original.getFieldNames();
+ }
+
+ @Override public int getFieldCount() {
+ return 1;
+ }
+
+ @Override public StructKind getStructKind() {
+ return original.getStructKind();
+ }
+
+ @Override public @Nullable RelDataTypeField getField(final String fieldName,
+ final boolean caseSensitive, final boolean elideRecord) {
+ RelDataTypeField originalField = original.getField(fieldName,
caseSensitive, elideRecord);
+ return originalField == null
+ ? alias.getField(fieldName, caseSensitive, elideRecord) :
originalField;
+ }
+
+ @Override public boolean isNullable() {
+ return original.isNullable();
+ }
+
+ @Override public @Nullable RelDataType getComponentType() {
+ return original.getComponentType();
+ }
+
+ @Override public @Nullable RelDataType getKeyType() {
+ return original.getKeyType();
+ }
+
+ @Override public @Nullable RelDataType getValueType() {
+ return original.getValueType();
+ }
+
+ @Override public @Nullable Charset getCharset() {
+ return original.getCharset();
+ }
+
+ @Override public @Nullable SqlCollation getCollation() {
+ return original.getCollation();
+ }
+
+ @Override public @Nullable SqlIntervalQualifier getIntervalQualifier() {
+ return original.getIntervalQualifier();
+ }
+
+ @Override public int getPrecision() {
+ return original.getPrecision();
+ }
+
+ @Override public int getScale() {
+ return original.getScale();
+ }
+
+ @Override public SqlTypeName getSqlTypeName() {
+ return original.getSqlTypeName();
+ }
+
+ @Override public @Nullable SqlIdentifier getSqlIdentifier() {
+ return original.getSqlIdentifier();
+ }
+
+ @Override public String getFullTypeString() {
+ return original.getFullTypeString();
+ }
+
+ @Override public RelDataTypeFamily getFamily() {
+ return original.getFamily();
+ }
+
+ @Override public RelDataTypePrecedenceList getPrecedenceList() {
+ return original.getPrecedenceList();
+ }
+
+ @Override public RelDataTypeComparability getComparability() {
+ return original.getComparability();
+ }
+
+ @Override public boolean isDynamicStruct() {
+ return original.isDynamicStruct();
+ }
+}
diff --git
a/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java
b/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java
index f3f7b343c3..04296d9817 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java
@@ -19,6 +19,7 @@ package org.apache.calcite.sql.validate;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactoryImpl;
import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rel.type.SingleColumnAliasRelDataType;
import org.apache.calcite.sql.SqlBasicCall;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlIdentifier;
@@ -90,11 +91,13 @@ public class AliasNamespace extends AbstractNamespace {
// and the sub-query has one column,
// then the namespace's sole column is named after the alias.
if (rowType.getFieldCount() == 1) {
- aliasedType = validator.getTypeFactory().builder()
+ final RelDataType singleColumnAlias =
validator.getTypeFactory().builder()
.kind(rowType.getStructKind())
.add(((SqlIdentifier) operands.get(1)).getSimple(),
rowType.getFieldList().get(0).getType())
.build();
+ aliasedType = node.getKind() == SqlKind.COLLECTION_TABLE
+ ? new SingleColumnAliasRelDataType(rowType, singleColumnAlias) :
singleColumnAlias;
// If the sub-query is UNNEST with ordinality
// and the sub-query has two columns: data column, ordinality column
// then the namespace's sole column is named after the alias.
diff --git
a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index 3ca5925ec2..432699123a 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -2382,8 +2382,8 @@ public class SqlValidatorImpl implements
SqlValidatorWithHints {
}
expr = call.operand(0);
final boolean needAliasNamespace = call.operandCount() > 2
- || expr.getKind() == SqlKind.VALUES
- || expr.getKind() == SqlKind.UNNEST;
+ || expr.getKind() == SqlKind.VALUES || expr.getKind() ==
SqlKind.UNNEST
+ || expr.getKind() == SqlKind.COLLECTION_TABLE;
newExpr =
registerFrom(
parentScope,
diff --git
a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index 5c825720ef..19b36f4aea 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -2287,6 +2287,23 @@ class SqlToRelConverterTest extends SqlToRelTestBase {
sql(sql).ok();
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-5779">[CALCITE-5779]
+ * Implicit column alias for single-column table function should work</a>. */
+ @Test void testTableFunctionSingleColumnAlias() {
+ String sql = "select rmp1.i, rmp1, rmp2, rmp3.i, j\n"
+ + "from table(ramp(1)) as rmp1,\n"
+ + "table(ramp(1)) as rmp2,\n"
+ + "table(ramp(1)) as rmp3,\n"
+ + "table(ramp(1)) as rmp4(j)";
+ fixture()
+ .withFactory(c ->
+ c.withOperatorTable(t -> MockSqlOperatorTable.standard().extend()))
+ .withCatalogReader(MockCatalogReaderExtended::create)
+ .withSql(sql)
+ .ok();
+ }
+
@Test void testTableFunctionSessionWithSubQueryParam() {
final String sql = "select *\n"
+ "from table(session((select * from Shipments), descriptor(rowtime), "
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 6c132175f8..16c267dd72 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -7676,6 +7676,18 @@ public class SqlValidatorTest extends
SqlValidatorTestCase {
.fails("Cannot call table function here: 'RAMP'");
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-5779">[CALCITE-5779]
+ * Implicit column alias for single-column table function should work</a>. */
+ @Test void testTableFunctionSingleColumnAlias() {
+ final SqlValidatorFixture s = fixture()
+ .withOperatorTable(MockSqlOperatorTable.standard().extend());
+ s.withSql("select rmp from table(ramp(3)) as rmp").ok();
+ s.withSql("select rmp.i from table(ramp(3)) as rmp").ok();
+ s.withSql("select rmp.i, rmp from table(ramp(3)) as rmp").ok();
+ s.withSql("select l from table(ramp(3)) as rmp(l)").ok();
+ }
+
/** Test case for
* <a
href="https://issues.apache.org/jira/browse/CALCITE-1309">[CALCITE-1309]
* Support LATERAL TABLE</a>. */
diff --git
a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index b48f9015ad..6a8d48867d 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -7339,6 +7339,27 @@ LogicalProject(ORDERID=[$0], ROWTIME=[$1],
window_start=[$2], window_end=[$3])
LogicalTableFunctionScan(invocation=[SESSION(DESCRIPTOR($1), DESCRIPTOR($0),
600000:INTERVAL MINUTE)], rowType=[RecordType(INTEGER ORDERID, TIMESTAMP(0)
ROWTIME, TIMESTAMP(3) window_start, TIMESTAMP(3) window_end)])
LogicalProject(ORDERID=[$0], ROWTIME=[$1])
LogicalTableScan(table=[[CATALOG, SALES, SHIPMENTS]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testTableFunctionSingleColumnAlias">
+ <Resource name="sql">
+ <![CDATA[select rmp1.i, rmp1, rmp2, rmp3.i, j
+from table(ramp(1)) as rmp1,
+table(ramp(1)) as rmp2,
+table(ramp(1)) as rmp3,
+table(ramp(1)) as rmp4(j)]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalProject(I=[$0], RMP1=[$0], RMP2=[$1], I0=[$2], J=[$3])
+ LogicalJoin(condition=[true], joinType=[inner])
+ LogicalJoin(condition=[true], joinType=[inner])
+ LogicalJoin(condition=[true], joinType=[inner])
+ LogicalTableFunctionScan(invocation=[RAMP(1)],
rowType=[RecordType(INTEGER I)])
+ LogicalTableFunctionScan(invocation=[RAMP(1)],
rowType=[RecordType(INTEGER I)])
+ LogicalTableFunctionScan(invocation=[RAMP(1)],
rowType=[RecordType(INTEGER I)])
+ LogicalTableFunctionScan(invocation=[RAMP(1)], rowType=[RecordType(INTEGER
I)])
]]>
</Resource>
</TestCase>
diff --git a/core/src/test/resources/sql/functions.iq
b/core/src/test/resources/sql/functions.iq
index e6a4e9f86a..2c0bf3cb0a 100644
--- a/core/src/test/resources/sql/functions.iq
+++ b/core/src/test/resources/sql/functions.iq
@@ -755,4 +755,21 @@ limit 3;
!ok
+!use aux
+# Test case for [CALCITE-5779] Implicit column alias for single-column table
function should work.
+select t1, t1."i", t2, v
+from
+table(AUX.TBLFUN_IDENTITY(1)) as t1,
+table(AUX.TBLFUN_IDENTITY(2)) as t2,
+table(AUX.TBLFUN_IDENTITY(3)) as t3(v);
++----+---+----+---+
+| T1 | i | T2 | V |
++----+---+----+---+
+| 1 | 1 | 2 | 3 |
++----+---+----+---+
+(1 row)
+
+!ok
+
+
# End functions.iq
diff --git a/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java
b/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java
index 1f0c0ee2cd..87a1d75a0b 100644
--- a/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java
+++ b/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java
@@ -1016,6 +1016,9 @@ public class CalciteAssert {
TableFunction tableFunction =
TableFunctionImpl.create(Smalls.SimpleTableFunction.class, "eval");
aux.add("TBLFUN", tableFunction);
+ TableFunction tableFunctionIdentity =
+ TableFunctionImpl.create(Smalls.IdentityTableFunction.class, "eval");
+ aux.add("TBLFUN_IDENTITY", tableFunctionIdentity);
final String simpleSql = "select *\n"
+ "from (values\n"
+ " ('ABC', 1),\n"
diff --git a/testkit/src/main/java/org/apache/calcite/test/QuidemTest.java
b/testkit/src/main/java/org/apache/calcite/test/QuidemTest.java
index 224f7a5e3f..6a8f01abde 100644
--- a/testkit/src/main/java/org/apache/calcite/test/QuidemTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/QuidemTest.java
@@ -253,6 +253,10 @@ public abstract class QuidemTest {
case "hr":
return CalciteAssert.hr()
.connect();
+ case "aux":
+ return CalciteAssert.hr()
+ .with(CalciteAssert.Config.AUX)
+ .connect();
case "foodmart":
return CalciteAssert.that()
.with(CalciteAssert.Config.FOODMART_CLONE)
diff --git a/testkit/src/main/java/org/apache/calcite/util/Smalls.java
b/testkit/src/main/java/org/apache/calcite/util/Smalls.java
index 6be22f3afe..984de925a6 100644
--- a/testkit/src/main/java/org/apache/calcite/util/Smalls.java
+++ b/testkit/src/main/java/org/apache/calcite/util/Smalls.java
@@ -140,6 +140,21 @@ public class Smalls {
private Smalls() {}
+ private static QueryableTable identity(Integer i) {
+ final Enumerable<Integer> enumerable =
Linq4j.asEnumerable(ImmutableList.of(i));
+ return new AbstractQueryableTable(Integer.class) {
+ @Override public <E> Queryable<E> asQueryable(
+ QueryProvider queryProvider, SchemaPlus schema, String tableName) {
+ //noinspection unchecked
+ return (Queryable<E>) enumerable.asQueryable();
+ }
+
+ @Override public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+ return typeFactory.builder().add("i", SqlTypeName.INTEGER).build();
+ }
+ };
+ }
+
private static QueryableTable oneThreePlus(String s) {
List<Integer> items;
// Argument is null in case SQL contains function call with expression.
@@ -1060,6 +1075,13 @@ public class Smalls {
}
}
+ /** A table function that returns its input value. */
+ public static class IdentityTableFunction {
+ public static QueryableTable eval(Integer i) {
+ return identity(i);
+ }
+ }
+
/** The real MazeTable may be found in example/function. This is a cut-down
* version to support a test. */
public static class MazeTable extends AbstractTable