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 c1d9f50bf6 [CALCITE-7085] JOIN USING with unqualified common column 
fails in a conformance where allowQualifyingCommonColumn is false (e.g. Oracle, 
Presto)
c1d9f50bf6 is described below

commit c1d9f50bf6931ac2b499692e121112c4de4b5b40
Author: Terran <[email protected]>
AuthorDate: Wed Apr 29 16:23:32 2026 +0800

    [CALCITE-7085] JOIN USING with unqualified common column fails in a 
conformance where allowQualifyingCommonColumn is false (e.g. Oracle, Presto)
---
 .../calcite/sql/validate/SqlValidatorImpl.java     | 62 ++++++++++++++++++++--
 .../apache/calcite/test/SqlToRelConverterTest.java | 41 ++++++++++++++
 .../apache/calcite/test/SqlToRelConverterTest.xml  | 56 +++++++++++++++++++
 3 files changed, 156 insertions(+), 3 deletions(-)

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 e21d62aecb..1fcbc24c36 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
@@ -643,6 +643,31 @@ private static void validateQualifiedCommonColumn(SqlJoin 
join,
     }
   }
 
+  /** Validates that a SQL node tree does not contain qualified references
+   * to common columns in a JOIN USING or NATURAL JOIN context.
+   * This is called before identifier expansion to catch user-written
+   * qualified common columns in conformances where they are disallowed
+   * (e.g. Oracle, Presto).
+   *
+   * @param nodeList The list of SQL nodes to check
+   * @param join     The JOIN node containing USING/NATURAL condition
+   * @param scope    The select scope for resolving identifiers
+   */
+  private void validateNoQualifiedCommonColumns(SqlNodeList nodeList,
+      SqlJoin join, SelectScope scope) {
+    for (SqlNode item : nodeList) {
+      item.accept(new SqlShuttle() {
+        @Override public SqlNode visit(SqlIdentifier id) {
+          if (!id.isSimple()) {
+            validateQualifiedCommonColumn(join, id, scope,
+                SqlValidatorImpl.this);
+          }
+          return id;
+        }
+      });
+    }
+  }
+
   private boolean expandStar(List<SqlNode> selectItems, Set<String> aliases,
       PairList<String, RelDataType> fields, boolean includeSystemVars,
       SelectScope scope, SqlNode node) {
@@ -5253,6 +5278,17 @@ protected void validateGroupClause(SqlSelect select) {
 
     // expand the expression in group list.
     List<SqlNode> expandedList = new ArrayList<>();
+    // Validate that GROUP BY items do not qualify common columns
+    // in conformances where it is disallowed (e.g. Oracle, Presto).
+    // This must run before expansion, because expansion generates
+    // qualified identifiers that should not trigger this validation.
+    if (!config.conformance().allowQualifyingCommonColumn()) {
+      final SqlNode from = select.getFrom();
+      if (from instanceof SqlJoin) {
+        validateNoQualifiedCommonColumns(groupList,
+            (SqlJoin) from, getRawSelectScopeNonNull(select));
+      }
+    }
     for (SqlNode groupItem : groupList) {
       SqlNode expandedItem =
           extendedExpand(groupItem, groupScope, select, Clause.GROUP_BY);
@@ -5404,6 +5440,19 @@ protected void validateHavingClause(SqlSelect select) {
     }
   }
 
+  /** Validates that SELECT items do not qualify common columns
+   * in conformances where it is disallowed (e.g. Oracle, Presto). */
+  private void validateSelectCommonColumns(SqlNodeList selectItems,
+      SqlSelect select) {
+    if (!config().conformance().allowQualifyingCommonColumn()) {
+      final SqlNode from = select.getFrom();
+      if (from instanceof SqlJoin) {
+        validateNoQualifiedCommonColumns(selectItems,
+            (SqlJoin) from, getRawSelectScopeNonNull(select));
+      }
+    }
+  }
+
   protected RelDataType validateSelectList(final SqlNodeList selectItems,
       SqlSelect select, RelDataType targetRowType) {
     // First pass, ensure that aliases are unique. "*" and "TABLE.*" items
@@ -5417,6 +5466,12 @@ protected RelDataType validateSelectList(final 
SqlNodeList selectItems,
     // Populated during select expansion when SqlConformance.isSelectAlias != 
UNSUPPORTED
     final Map<String, SqlNode> expansions = new HashMap<>();
 
+    // Validate that SELECT items do not qualify common columns
+    // in conformances where it is disallowed (e.g. Oracle, Presto).
+    // This must run before expansion, because expansion generates
+    // qualified identifiers that should not trigger this validation.
+    validateSelectCommonColumns(selectItems, select);
+
     for (SqlNode selectItem : selectItems) {
       if (selectItem instanceof SqlSelect) {
         handleScalarSubQuery(select, (SqlSelect) selectItem,
@@ -7672,9 +7727,10 @@ protected SqlNode expandCommonColumn(SqlSelect 
sqlSelect, SqlNode selectItem,
 
       final SqlIdentifier identifier = (SqlIdentifier) selectItem;
       if (!identifier.isSimple()) {
-        if (!validator.config().conformance().allowQualifyingCommonColumn()) {
-          validateQualifiedCommonColumn((SqlJoin) from, identifier, scope, 
validator);
-        }
+        // Qualified identifiers (e.g. t1.col) are returned unchanged.
+        // Validation of qualified common columns is performed before 
expansion,
+        // in validateSelectCommonColumns, where the original user-written
+        // identifier (with its source position) is still available.
         return selectItem;
       }
 
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 4bc62e7c3d..ebad0e9450 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -6092,4 +6092,45 @@ void checkUserDefinedOrderByOver(NullCollation 
nullCollation) {
     final String sql = "select distinct deptno, deptno, empno, 1, 'a' from emp 
order by rand(), 1";
     sql(sql).ok();
   }
+
+  /** Test case of
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7085";>[CALCITE-7085]
+   * JOIN USING with unqualified common column fails in a conformance where
+   * allowQualifyingCommonColumn is false (e.g. Oracle, Presto)</a>. */
+  @Test void testJoinUsingWithConformanceOracle() {
+    final String sql = "SELECT deptno, name\n"
+        + "FROM emp JOIN dept using (deptno)";
+    sql(sql).withConformance(SqlConformanceEnum.ORACLE_10).ok();
+  }
+
+
+  /** Test case of
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7085";>[CALCITE-7085]
+   * JOIN USING with unqualified common column fails in a conformance where
+   * allowQualifyingCommonColumn is false (e.g. Oracle, Presto)</a>. */
+  @Test void testLeftJoinUsingWithConformanceOracle() {
+    final String sql = "SELECT deptno, name\n"
+        + "FROM emp LEFT OUTER JOIN dept using (deptno)";
+    sql(sql).withConformance(SqlConformanceEnum.ORACLE_10).ok();
+  }
+
+  /** Test case of
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7085";>[CALCITE-7085]
+   * JOIN USING with unqualified common column fails in a conformance where
+   * allowQualifyingCommonColumn is false (e.g. Oracle, Presto)</a>. */
+  @Test void testRightJoinUsingWithConformanceOracle() {
+    final String sql = "SELECT deptno, name\n"
+        + "FROM emp RIGHT OUTER JOIN dept using (deptno)";
+    sql(sql).withConformance(SqlConformanceEnum.ORACLE_10).ok();
+  }
+
+  /** Test case of
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7085";>[CALCITE-7085]
+   * JOIN USING with unqualified common column fails in a conformance where
+   * allowQualifyingCommonColumn is false (e.g. Oracle, Presto)</a>. */
+  @Test void testJoinUsingWithConformancePresto() {
+    final String sql = "SELECT deptno, name\n"
+        + "FROM emp JOIN dept using (deptno)";
+    sql(sql).withConformance(SqlConformanceEnum.PRESTO).ok();
+  }
 }
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 53662875e0..56f169629a 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -4368,6 +4368,34 @@ LogicalProject(DEPTNO=[$0], EXPR$1=[$1])
       LogicalJoin(condition=[=($7, $9)], joinType=[full])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
         LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJoinUsingWithConformanceOracle">
+    <Resource name="sql">
+      <![CDATA[SELECT deptno, name
+FROM emp JOIN dept using (deptno)]]>
+    </Resource>
+    <Resource name="plan">
+      <![CDATA[
+LogicalProject(DEPTNO=[$7], NAME=[$10])
+  LogicalJoin(condition=[=($7, $9)], joinType=[inner])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJoinUsingWithConformancePresto">
+    <Resource name="sql">
+      <![CDATA[SELECT deptno, name
+FROM emp JOIN dept using (deptno)]]>
+    </Resource>
+    <Resource name="plan">
+      <![CDATA[
+LogicalProject(DEPTNO=[$7], NAME=[$10])
+  LogicalJoin(condition=[=($7, $9)], joinType=[inner])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
 ]]>
     </Resource>
   </TestCase>
@@ -5000,6 +5028,20 @@ LogicalProject(C=[$0], N=[$3])
             LogicalAggregate(group=[{0}])
               LogicalProject($f2=[+($0, 1)])
                 LogicalValues(tuples=[[{ 4 }]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testLeftJoinUsingWithConformanceOracle">
+    <Resource name="sql">
+      <![CDATA[SELECT deptno, name
+FROM emp LEFT OUTER JOIN dept using (deptno)]]>
+    </Resource>
+    <Resource name="plan">
+      <![CDATA[
+LogicalProject(DEPTNO=[$7], NAME=[$10])
+  LogicalJoin(condition=[=($7, $9)], joinType=[left])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
 ]]>
     </Resource>
   </TestCase>
@@ -7396,6 +7438,20 @@ GROUP BY ROLLUP(deptno)]]>
 LogicalAggregate(group=[{0}], groups=[[{0}, {}]], EXPR$1=[COUNT(DISTINCT $1)])
   LogicalProject(DEPTNO=[$7], EMPNO=[$0])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testRightJoinUsingWithConformanceOracle">
+    <Resource name="sql">
+      <![CDATA[SELECT deptno, name
+FROM emp RIGHT OUTER JOIN dept using (deptno)]]>
+    </Resource>
+    <Resource name="plan">
+      <![CDATA[
+LogicalProject(DEPTNO=[COALESCE($7, $9)], NAME=[$10])
+  LogicalJoin(condition=[=($7, $9)], joinType=[right])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
 ]]>
     </Resource>
   </TestCase>

Reply via email to