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

asolimando 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 327bfcc779 [CALCITE-6340] RelBuilder drops traits when aggregating 
over duplicate projected fields
327bfcc779 is described below

commit 327bfcc7799c4413a84a5ebe0849ff64853a7dd1
Author: James Duong <[email protected]>
AuthorDate: Thu Apr 11 15:26:24 2024 -0700

    [CALCITE-6340] RelBuilder drops traits when aggregating over duplicate 
projected fields
    
    Calculate the post-pruning RelTraitSet on the projection using 
TraitSet#apply(Mapping)
    
    Co-authored-by: Alessandro Solimando <[email protected]>
---
 .../java/org/apache/calcite/tools/RelBuilder.java  |   7 +-
 .../src/main/java/org/apache/calcite/util/Bug.java |   6 +
 .../org/apache/calcite/test/RelBuilderTest.java    | 219 +++++++++++++++++++++
 3 files changed, 230 insertions(+), 2 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java 
b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index 8d8d48bd07..7456c2e1de 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -2502,9 +2502,12 @@ public class RelBuilder {
           newProjects.add(project.getProjects().get(i));
           builder.add(project.getRowType().getFieldList().get(i));
         }
+
+        // This currently does not apply mappings correctly to the 
RelCollation due to
+        // https://issues.apache.org/jira/browse/CALCITE-6391
         r =
-            project.copy(cluster.traitSet(), project.getInput(), newProjects,
-                builder.build());
+            project.copy(project.getTraitSet().apply(targetMapping), 
project.getInput(),
+                newProjects, builder.build());
       } else {
         groupSetAfterPruning = groupSet;
         groupSetsAfterPruning = groupSets;
diff --git a/core/src/main/java/org/apache/calcite/util/Bug.java 
b/core/src/main/java/org/apache/calcite/util/Bug.java
index e0eaa97a3e..331922dce5 100644
--- a/core/src/main/java/org/apache/calcite/util/Bug.java
+++ b/core/src/main/java/org/apache/calcite/util/Bug.java
@@ -215,6 +215,12 @@ public abstract class Bug {
    * Fix failing quidem tests for FORMAT in CAST</a> is fixed. */
   public static final boolean CALCITE_6375_FIXED = false;
 
+  /** Whether
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE/issues/CALCITE-6391";>
+   * [CALCITE-6391] Apply mapping to RelCompositeTrait does not apply it to 
wrapped traits</a>
+   * is fixed. */
+  public static final boolean CALCITE_6391_FIXED = false;
+
   /**
    * Use this to flag temporary code.
    */
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java 
b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index 7f8f42c51b..e71b816bc8 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -26,6 +26,7 @@ import org.apache.calcite.plan.RelTraitDef;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelDistributions;
+import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.core.Correlate;
@@ -78,6 +79,7 @@ import org.apache.calcite.tools.RelRunner;
 import org.apache.calcite.tools.RelRunners;
 import org.apache.calcite.tools.RuleSet;
 import org.apache.calcite.tools.RuleSets;
+import org.apache.calcite.util.Bug;
 import org.apache.calcite.util.Holder;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.Pair;
@@ -1450,6 +1452,223 @@ public class RelBuilderTest {
     assertThat(root, hasTree(expected));
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE/issues/CALCITE-6340";>
+   * [CALCITE-6340] RelBuilder drops traits when aggregating over duplicate 
projected fields</a>.
+   */
+  @Test void 
testPruneProjectInputOfAggregatePreservesConventionAndCollationsWhenEmpty() {
+    final RelBuilder builder = createBuilder(config -> 
config.withPruneInputOfAggregate(true));
+
+    RelNode node = builder
+        .scan("EMP")
+        .sort(builder.nullsLast(builder.desc(builder.field(1))),
+            builder.field(0))
+        .project(builder.alias(builder.field(0), "a"),
+            builder.alias(builder.field(1), "b"),
+            builder.alias(builder.field(0), "c"),
+            builder.alias(builder.field(1), "d"))
+        .build();
+
+    final RelTraitSet desiredTraits = builder.getCluster().traitSet()
+        .replace(EnumerableConvention.INSTANCE);
+
+    final RuleSet prepareRules =
+        RuleSets.ofList(EnumerableRules.ENUMERABLE_PROJECT_RULE,
+            EnumerableRules.ENUMERABLE_SORT_RULE,
+            EnumerableRules.ENUMERABLE_TABLE_SCAN_RULE);
+    final Program program = Programs.of(prepareRules);
+    node =
+        program.run(node.getCluster().getPlanner(), node, desiredTraits, 
ImmutableList.of(),
+            ImmutableList.of());
+
+    // collations are lost as the sort is on column [1, 0], but we group on 0, 
convention stays
+    node = builder.push(node)
+        .aggregate(
+            builder.groupKey(0), builder.aggregateCall(
+            SqlStdOperatorTable.SUM, builder.field(0)))
+        .build();
+
+    final RelTraitSet expectedTraitSet = builder.getCluster().traitSet()
+        .replace(EnumerableConvention.INSTANCE);
+    assertTrue(expectedTraitSet.contains(EnumerableConvention.INSTANCE));
+
+    if (Bug.CALCITE_6391_FIXED) {
+      assertThat(node.getInput(0).getTraitSet(), is(expectedTraitSet));
+    } else {
+      assertThat(node.getInput(0).getTraitSet().get(0), 
is(expectedTraitSet.get(0)));
+    }
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE/issues/CALCITE-6340";>
+   * [CALCITE-6340] RelBuilder drops traits when aggregating over duplicate 
projected fields</a>.
+   */
+  @Test void 
testPruneProjectInputOfAggregatePreservesConventionAndSingletonCollation() {
+    final RelBuilder builder = createBuilder(config -> 
config.withPruneInputOfAggregate(true));
+
+    RelNode node = builder
+        .scan("EMP")
+        .sort(builder.nullsLast(builder.desc(builder.field(1))))
+        .project(builder.alias(builder.field(0), "a"),
+            builder.alias(builder.field(1), "b"),
+            builder.alias(builder.field(0), "c"),
+            builder.alias(builder.field(1), "d"))
+        .build();
+
+    final RelTraitSet desiredTraits = builder.getCluster().traitSet()
+        .replace(EnumerableConvention.INSTANCE);
+
+    final RuleSet prepareRules =
+        RuleSets.ofList(EnumerableRules.ENUMERABLE_PROJECT_RULE,
+            EnumerableRules.ENUMERABLE_SORT_RULE,
+            EnumerableRules.ENUMERABLE_TABLE_SCAN_RULE);
+    final Program program = Programs.of(prepareRules);
+
+    // turn the logical plan into a physical plan so that a convention can be 
set
+    node =
+        program.run(node.getCluster().getPlanner(), node, desiredTraits, 
ImmutableList.of(),
+            ImmutableList.of());
+
+
+    node = builder.push(node)
+        .aggregate(
+            builder.groupKey(1), builder.aggregateCall(
+                SqlStdOperatorTable.SUM, builder.field(1)))
+        .build();
+
+    final RelTraitSet expectedTraitSet = builder.getCluster().traitSet()
+        .replace(EnumerableConvention.INSTANCE)
+        .replace(
+            RelCollations.of(
+                new RelFieldCollation(0,
+                    RelFieldCollation.Direction.DESCENDING, 
RelFieldCollation.NullDirection.LAST)));
+
+    if (Bug.CALCITE_6391_FIXED) {
+      assertThat(node.getInput(0).getTraitSet(), is(expectedTraitSet));
+    } else {
+      assertThat(node.getInput(0).getTraitSet().get(0), 
is(expectedTraitSet.get(0)));
+    }
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE/issues/CALCITE-6340";>
+   * [CALCITE-6340] RelBuilder drops traits when aggregating over duplicate 
projected fields</a>.
+   */
+  @Test void 
testPruneProjectInputOfAggregatePreservesConventionAndCompositeCollation() {
+    final RelBuilder builder = createBuilder(config -> 
config.withPruneInputOfAggregate(true));
+
+    RelNode node = builder
+        .scan("EMP")
+        .sort(builder.nullsLast(builder.desc(builder.field(1))),
+            builder.field(0))
+        .project(builder.alias(builder.field(0), "a"),
+            builder.alias(builder.field(1), "b"),
+            builder.alias(builder.field(0), "c"),
+            builder.alias(builder.field(1), "d"))
+        .build();
+
+    final RelTraitSet desiredTraits = builder.getCluster().traitSet()
+        .replace(EnumerableConvention.INSTANCE);
+
+    final RuleSet prepareRules =
+        RuleSets.ofList(EnumerableRules.ENUMERABLE_PROJECT_RULE,
+            EnumerableRules.ENUMERABLE_SORT_RULE,
+            EnumerableRules.ENUMERABLE_TABLE_SCAN_RULE);
+    final Program program = Programs.of(prepareRules);
+
+    // turn the logical plan into a physical plan so that a convention can be 
set
+    node =
+        program.run(node.getCluster().getPlanner(), node, desiredTraits, 
ImmutableList.of(),
+            ImmutableList.of());
+
+
+    node = builder.push(node)
+        .aggregate(
+            builder.groupKey(1), builder.aggregateCall(
+                SqlStdOperatorTable.SUM, builder.field(1)))
+        .build();
+
+    final RelTraitSet expectedTraitSet = builder.getCluster().traitSet()
+        .replace(EnumerableConvention.INSTANCE)
+        .replace(
+            RelCollations.of(
+                new RelFieldCollation(0,
+                    RelFieldCollation.Direction.DESCENDING, 
RelFieldCollation.NullDirection.LAST)));
+
+    if (Bug.CALCITE_6391_FIXED) {
+      assertThat(node.getInput(0).getTraitSet(), is(expectedTraitSet));
+    } else {
+      assertThat(node.getInput(0).getTraitSet().get(0), 
is(expectedTraitSet.get(0)));
+    }
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE/issues/CALCITE-6340";>
+   * [CALCITE-6340] RelBuilder drops traits when aggregating over duplicate 
projected fields</a>.
+   */
+  @Test void 
testPruneProjectInputOfAggregatePreservesConventionAndDistribution() {
+    final RelBuilder builder = createBuilder(config -> 
config.withPruneInputOfAggregate(true));
+
+    RelNode node = builder
+        .scan("EMP")
+        .project(builder.alias(builder.field(0), "a"),
+            builder.alias(builder.field(0), "b"),
+            builder.alias(builder.field(1), "c"))
+        .build();
+
+    final RelTraitSet desiredTraits = builder.getCluster().traitSet()
+        .replace(EnumerableConvention.INSTANCE);
+    final RuleSet prepareRules =
+        RuleSets.ofList(EnumerableRules.ENUMERABLE_PROJECT_RULE,
+            EnumerableRules.ENUMERABLE_TABLE_SCAN_RULE);
+    final Program program = Programs.of(prepareRules);
+
+    // turn the logical plan into a physical plan so that a distribution can 
be set
+    node =
+        program.run(node.getCluster().getPlanner(), node, desiredTraits, 
ImmutableList.of(),
+            ImmutableList.of());
+
+    // setting the distribution drops the collations
+    node = 
node.copy(desiredTraits.plus(RelDistributions.BROADCAST_DISTRIBUTED), 
node.getInputs());
+
+    node = builder.push(node)
+        .aggregate(
+            builder.groupKey(0), builder.aggregateCall(
+            SqlStdOperatorTable.SUM, builder.field(0)))
+        .build();
+
+    final RelTraitSet expectedTraitSet = builder.getCluster().traitSet()
+        .replace(EnumerableConvention.INSTANCE)
+        .plus(RelDistributions.BROADCAST_DISTRIBUTED);
+
+    assertThat(node.getInput(0).getTraitSet(), is(expectedTraitSet));
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE/issues/CALCITE-6340";>
+   * [CALCITE-6340] RelBuilder drops traits when aggregating over duplicate 
projected fields</a>.
+   */
+  @Test void testPruneProjectInputOfAggregatePreservesConvention() {
+    final RelBuilder builder = createBuilder(config -> 
config.withPruneInputOfAggregate(true));
+    final RelNode node = builder.scan("DEPT")
+        .adoptConvention(EnumerableConvention.INSTANCE)
+        .project(builder.alias(builder.field(0), "a"),
+            builder.alias(builder.field(0), "b"))
+        .aggregate(
+            builder.groupKey(0), builder.aggregateCall(
+            SqlStdOperatorTable.SUM, builder.field(0))).build();
+
+    final RelTraitSet expectedTraitSet = builder.getCluster().traitSet()
+        .replace(EnumerableConvention.INSTANCE)
+        .replace(RelCollations.of(new RelFieldCollation(0)));
+
+    if (Bug.CALCITE_6391_FIXED) {
+      assertThat(node.getInput(0).getTraitSet(), is(expectedTraitSet));
+    } else {
+      assertThat(node.getInput(0).getTraitSet().get(0), 
is(expectedTraitSet.get(0)));
+    }
+  }
+
   private RelNode buildRelWithDuplicateAggregates(
       UnaryOperator<RelBuilder.Config> transform,
       int... groupFieldOrdinals) {

Reply via email to