This is an automated email from the ASF dual-hosted git repository.

zabetak 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 29452b34cc [CALCITE-6877] Generate LogicalProject in RelRoot.project() 
when mapping is not name trivial
29452b34cc is described below

commit 29452b34cc5cb9a2cdf1e4eacd3b935aa6e926e6
Author: Niels Pardon <[email protected]>
AuthorDate: Fri Mar 7 14:48:31 2025 +0100

    [CALCITE-6877] Generate LogicalProject in RelRoot.project() when mapping is 
not name trivial
    
    Signed-off-by: Niels Pardon <[email protected]>
    
    Close apache/calcite#4230
---
 .../main/java/org/apache/calcite/rel/RelRoot.java  |  2 +-
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java | 87 ++++++++++++++++++++++
 2 files changed, 88 insertions(+), 1 deletion(-)

diff --git a/core/src/main/java/org/apache/calcite/rel/RelRoot.java 
b/core/src/main/java/org/apache/calcite/rel/RelRoot.java
index b7c5e0a1d3..fe15a39047 100644
--- a/core/src/main/java/org/apache/calcite/rel/RelRoot.java
+++ b/core/src/main/java/org/apache/calcite/rel/RelRoot.java
@@ -164,7 +164,7 @@ public RelNode project(boolean force) {
     if (isRefTrivial()
         && (SqlKind.DML.contains(kind)
             || !force
-            || rel instanceof LogicalProject)) {
+            || (rel instanceof LogicalProject && isNameTrivial()))) {
       return rel;
     }
     final List<RexNode> projects = new ArrayList<>(fields.size());
diff --git 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index 4f74a8e332..d63c1c46c1 100644
--- 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -27,12 +27,14 @@
 import org.apache.calcite.rel.RelFieldCollation.Direction;
 import org.apache.calcite.rel.RelFieldCollation.NullDirection;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.core.JoinRelType;
 import org.apache.calcite.rel.hint.HintPredicates;
 import org.apache.calcite.rel.hint.HintStrategyTable;
 import org.apache.calcite.rel.hint.RelHint;
 import org.apache.calcite.rel.logical.LogicalAggregate;
 import org.apache.calcite.rel.logical.LogicalFilter;
+import org.apache.calcite.rel.logical.LogicalProject;
 import org.apache.calcite.rel.rules.AggregateJoinTransposeRule;
 import org.apache.calcite.rel.rules.AggregateProjectMergeRule;
 import org.apache.calcite.rel.rules.CoreRules;
@@ -42,14 +44,18 @@
 import org.apache.calcite.rel.rules.PruneEmptyRules;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rel.type.RelDataTypeFieldImpl;
 import org.apache.calcite.rel.type.RelDataTypeSystem;
 import org.apache.calcite.rel.type.RelDataTypeSystemImpl;
+import org.apache.calcite.rel.type.RelRecordType;
 import org.apache.calcite.runtime.FlatLists;
 import org.apache.calcite.runtime.Hook;
 import org.apache.calcite.schema.SchemaPlus;
 import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlDialect;
 import org.apache.calcite.sql.SqlDialect.DatabaseProduct;
+import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlSelect;
 import org.apache.calcite.sql.SqlWriter;
@@ -99,6 +105,7 @@
 
 import java.math.BigDecimal;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -114,7 +121,10 @@
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.hasToString;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
@@ -9315,6 +9325,83 @@ private void checkLiteral2(String expression, String 
expected) {
   }
 
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6877";>[CALCITE-6877]
+   * Generate LogicalProject in RelRoot.project() when mapping is not name 
trivial</a>. */
+  @Test void testRelRootProjectForceNonNameTrivial() {
+    final SchemaPlus rootSchema = Frameworks.createRootSchema(true);
+    final SchemaPlus defaultSchema =
+        CalciteAssert.addSchema(rootSchema, CalciteAssert.SchemaSpec.HR);
+    final FrameworkConfig frameworkConfig = RelBuilderTest.config()
+        .defaultSchema(defaultSchema)
+        .build();
+    final RelBuilder relBuilder = RelBuilder.create(frameworkConfig);
+    final RelNode inputRel = relBuilder.scan("emps")
+        
.project(relBuilder.fields(Collections.singletonList("empid"))).build();
+
+    final List<RelDataTypeField> fields =
+        Collections.singletonList(
+            // rename empid to empno via RelRoot
+            new RelDataTypeFieldImpl("empno",
+                inputRel.getRowType().getFieldList().get(0).getIndex(),
+                inputRel.getRowType().getFieldList().get(0).getType()));
+
+    final RelRoot root = RelRoot.of(inputRel, new RelRecordType(fields), 
SqlKind.SELECT);
+
+    // inner LogicalProject selects one field and RelRoot only has one field
+    assertTrue(root.isRefTrivial());
+
+    // inner LogicalProject has different field name than RelRoot
+    assertFalse(root.isNameTrivial());
+
+    final RelNode project = root.project();
+    assertEquals(inputRel, project);
+
+    // regular project() and force project() are different
+    final RelNode forceProject = root.project(true);
+    assertNotEquals(project, forceProject);
+
+    // new LogicalProject on top of inputRel
+    assertInstanceOf(LogicalProject.class, forceProject);
+    assertEquals(inputRel, forceProject.getInput(0));
+
+    // new LogicalProject renames field
+    if (forceProject instanceof LogicalProject) {
+      assertEquals("empno",
+          ((LogicalProject) 
forceProject).getNamedProjects().get(0).getValue());
+    }
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6877";>[CALCITE-6877]
+   * Generate LogicalProject in RelRoot.project() when mapping is not name 
trivial</a>. */
+  @Test void testRelRootProjectForceNameTrivial() {
+    final SchemaPlus rootSchema = Frameworks.createRootSchema(true);
+    final SchemaPlus defaultSchema =
+        CalciteAssert.addSchema(rootSchema, CalciteAssert.SchemaSpec.HR);
+    final FrameworkConfig frameworkConfig = RelBuilderTest.config()
+        .defaultSchema(defaultSchema)
+        .build();
+    final RelBuilder relBuilder = RelBuilder.create(frameworkConfig);
+    final RelNode inputRel = relBuilder.scan("emps")
+        
.project(relBuilder.fields(Collections.singletonList("empid"))).build();
+
+    final RelRoot root = RelRoot.of(inputRel, SqlKind.SELECT);
+
+    // inner LogicalProject selects one field and RelRoot only has one field
+    assertTrue(root.isRefTrivial());
+
+    // inner LogicalProject has same field name as RelRoot
+    assertTrue(root.isNameTrivial());
+
+    final RelNode project = root.project();
+    assertEquals(inputRel, project);
+
+    // regular project() and force project() are the same
+    final RelNode forceProject = root.project(true);
+    assertEquals(project, forceProject);
+  }
+
   /** Fluid interface to run tests. */
   static class Sql {
     private final CalciteAssert.SchemaSpec schemaSpec;

Reply via email to