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 aa90b89709 [CALCITE-2659] Inefficient plan In natural left/right join
aa90b89709 is described below

commit aa90b897092e0d9ce4cb9cb5ed387b159dfa3355
Author: Mihai Budiu <[email protected]>
AuthorDate: Tue Jun 16 15:34:57 2026 -0700

    [CALCITE-2659] Inefficient plan In natural left/right join
    
    Signed-off-by: Mihai Budiu <[email protected]>
---
 .../calcite/sql/validate/SqlValidatorImpl.java     | 26 ++++++++++++++++------
 .../apache/calcite/test/SqlToRelConverterTest.xml  | 21 +++++++++--------
 2 files changed, 29 insertions(+), 18 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 7989b7e7d8..1e2b298808 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
@@ -7843,17 +7843,29 @@ private SqlNode expandExprFromJoin(SqlJoin join, 
SqlIdentifier identifier, Selec
 
           assert qualifiedNode.size() == 2;
 
-          final SqlCall coalesceCall =
-              SqlStdOperatorTable.COALESCE.createCall(SqlParserPos.ZERO, 
qualifiedNode.get(0),
-                  qualifiedNode.get(1));
+          // COALESCE is only needed for FULL JOIN; for RIGHT JOIN use the 
right
+          // column (always non-null), and for INNER/LEFT JOIN use the left 
column.
+          final JoinType joinType = join.getJoinType();
+          final SqlNode colRef;
+          if (joinType.generatesNullsOnLeft() && 
joinType.generatesNullsOnRight()) {
+            colRef =
+                SqlStdOperatorTable.COALESCE.createCall(
+                    qualifiedNode.get(0).getParserPosition()
+                    .plus(qualifiedNode.get(1).getParserPosition()),
+                qualifiedNode.get(0), qualifiedNode.get(1));
+          } else if (joinType.generatesNullsOnLeft()) {
+            colRef = qualifiedNode.get(1);
+          } else {
+            colRef = qualifiedNode.get(0);
+          }
 
-          // If there is an alias for the column, no need to wrap the coalesce 
with an AS operator
+          // If there is an alias for the column, no need to wrap with an AS 
operator
           boolean haveAlias = fieldAliases.contains(name);
           if (haveAlias) {
-            return coalesceCall;
+            return colRef;
           } else {
-            return SqlStdOperatorTable.AS.createCall(SqlParserPos.ZERO, 
coalesceCall,
-                new SqlIdentifier(identifier.getSimple(), SqlParserPos.ZERO));
+            return SqlStdOperatorTable.AS.createCall(SqlParserPos.ZERO, colRef,
+                new SqlIdentifier(identifier.getSimple(), 
identifier.getParserPosition()));
           }
         }
       }
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 192a0140da..0bf02c9a73 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -4042,12 +4042,11 @@ group by deptno]]>
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalProject(DEPTNO=[$0], EXPR$1=[$1])
-  LogicalAggregate(group=[{0}], EXPR$1=[COUNT()])
-    LogicalProject($f0=[$7])
-      LogicalJoin(condition=[=($7, $9)], joinType=[inner])
-        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
-        LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+LogicalAggregate(group=[{0}], EXPR$1=[COUNT()])
+  LogicalProject(DEPTNO=[$7])
+    LogicalJoin(condition=[=($7, $9)], joinType=[inner])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+      LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
 ]]>
     </Resource>
   </TestCase>
@@ -4063,7 +4062,7 @@ group by grouping sets ((deptno), (deptno, job))]]>
       <![CDATA[
 LogicalProject(DEPTNO=[$0], EXPR$1=[$2], EXPR$2=[$3], EXPR$3=[$4])
   LogicalAggregate(group=[{0, 1}], groups=[[{0, 1}, {0}]], 
EXPR$1=[GROUPING($0)], EXPR$2=[GROUPING($0, $1)], EXPR$3=[COUNT()])
-    LogicalProject($f0=[$7], JOB=[$2])
+    LogicalProject(DEPTNO=[$7], JOB=[$2])
       LogicalJoin(condition=[=($7, $9)], joinType=[inner])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
         LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
@@ -4112,7 +4111,7 @@ natural join customer.contact_peek t2]]>
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalProject(X=[COALESCE(ROW($4, $5, $6), ROW($14, $15, $16)).X])
+LogicalProject(X=[$4])
   LogicalJoin(condition=[AND(=($0, $10), =($1, $11), =($2, $12), =($3, $13), 
AND(AND(=($4, $14), =($5, $15)), =($6, $16)), AND(AND(=($7, $17), =($8, $18)), 
=($9, $19)))], joinType=[inner])
     LogicalProject(CONTACTNO=[$0], FNAME=[$1], LNAME=[$2], EMAIL=[$3], 
X=[$4.X], Y=[$4.Y], unit=[$4.unit], M=[$5.M], A=[$5.SUB.A], B=[$5.SUB.B])
       LogicalTableScan(table=[[CATALOG, CUSTOMER, CONTACT_PEEK]])
@@ -4484,7 +4483,7 @@ group by grouping sets ((deptno), (deptno, job))]]>
       <![CDATA[
 LogicalProject(DEPTNO=[$0], EXPR$1=[$2], EXPR$2=[$3], EXPR$3=[$4])
   LogicalAggregate(group=[{0, 1}], groups=[[{0, 1}, {0}]], 
EXPR$1=[GROUPING($0)], EXPR$2=[GROUPING($0, $1)], EXPR$3=[COUNT()])
-    LogicalProject($f0=[$7], JOB=[$2])
+    LogicalProject(DEPTNO=[$7], JOB=[$2])
       LogicalJoin(condition=[=($7, $9)], joinType=[inner])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
         LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
@@ -4532,7 +4531,7 @@ using (coord)]]>
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalProject(X=[COALESCE(ROW($4, $5, $6), ROW($14, $15, $16)).X])
+LogicalProject(X=[$4])
   LogicalJoin(condition=[AND(AND(=($4, $14), =($5, $15)), =($6, $16))], 
joinType=[inner])
     LogicalProject(CONTACTNO=[$0], FNAME=[$1], LNAME=[$2], EMAIL=[$3], 
X=[$4.X], Y=[$4.Y], unit=[$4.unit], M=[$5.M], A=[$5.SUB.A], B=[$5.SUB.B])
       LogicalTableScan(table=[[CATALOG, CUSTOMER, CONTACT_PEEK]])
@@ -7521,7 +7520,7 @@ FROM emp RIGHT OUTER JOIN dept using (deptno)]]>
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalProject(DEPTNO=[COALESCE($7, $9)], NAME=[$10])
+LogicalProject(DEPTNO=[$9], NAME=[$10])
   LogicalJoin(condition=[=($7, $9)], joinType=[right])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalTableScan(table=[[CATALOG, SALES, DEPT]])

Reply via email to