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

Reply via email to