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

xuzifu666 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 e4b2baa8b4 [CALCITE-7338] Window hints are not propagated to window 
rel nodes
e4b2baa8b4 is described below

commit e4b2baa8b4aa5d399a808a3ac32fd77c537ac59d
Author: Zhen Chen <[email protected]>
AuthorDate: Sat Dec 20 09:48:10 2025 +0800

    [CALCITE-7338] Window hints are not propagated to window rel nodes
---
 .../apache/calcite/rel/logical/LogicalWindow.java  | 32 ++++++++++----
 .../calcite/rel/logical/ToLogicalConverter.java    |  4 +-
 .../apache/calcite/rel/mutable/MutableRels.java    |  2 +-
 .../apache/calcite/rel/rules/CalcRelSplitter.java  | 49 +++++++++++++++++++++-
 .../calcite/rel/rules/ProjectToWindowRule.java     | 13 +++---
 .../rel/rules/ProjectWindowTransposeRule.java      |  4 +-
 .../calcite/rel/rules/ReduceExpressionsRule.java   |  2 +-
 .../apache/calcite/test/SqlHintsConverterTest.java | 24 +++++++++++
 8 files changed, 110 insertions(+), 20 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java 
b/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
index d17b8aef85..815b8f8ca5 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
@@ -102,36 +102,54 @@ public LogicalWindow(RelOptCluster cluster, RelTraitSet 
traitSet, RelNode input,
 
   @Override public LogicalWindow copy(RelTraitSet traitSet,
       List<RelNode> inputs) {
-    return new LogicalWindow(getCluster(), traitSet, sole(inputs), constants,
+    return new LogicalWindow(getCluster(), traitSet, hints, sole(inputs), 
constants,
       getRowType(), groups);
   }
 
   @Override public Window copy(List<RexLiteral> constants) {
-    return new LogicalWindow(getCluster(), getTraitSet(), getInput(),
+    return new LogicalWindow(getCluster(), getTraitSet(), hints, getInput(),
         constants, getRowType(), groups);
   }
 
+  @Deprecated // to be removed before 2.0
+  public static LogicalWindow create(RelTraitSet traitSet, RelNode input,
+      List<RexLiteral> constants, RelDataType rowType, List<Group> groups) {
+    return create(traitSet, Collections.emptyList(), input, constants, 
rowType, groups);
+  }
+
   /**
    * Creates a LogicalWindow.
    *
-   * @param input   Input relational expression
    * @param traitSet Trait set
+   * @param hints Hints
+   * @param input Input relational expression
    * @param constants List of constants that are additional inputs
    * @param rowType Output row type
    * @param groups Window groups
    */
-  public static LogicalWindow create(RelTraitSet traitSet, RelNode input,
-      List<RexLiteral> constants, RelDataType rowType, List<Group> groups) {
-    return new LogicalWindow(input.getCluster(), traitSet, input, constants,
+  public static LogicalWindow create(RelTraitSet traitSet, List<RelHint> hints,
+      RelNode input, List<RexLiteral> constants, RelDataType rowType,
+      List<Group> groups) {
+    return new LogicalWindow(input.getCluster(), traitSet, hints, input, 
constants,
         rowType, groups);
   }
 
   /**
    * Creates a LogicalWindow by parsing a {@link RexProgram}.
    */
+  @Deprecated // to be removed before 2.0
   public static RelNode create(RelOptCluster cluster,
       RelTraitSet traitSet, RelBuilder relBuilder, RelNode child,
       final RexProgram program) {
+    return create(cluster, traitSet, relBuilder, child, program, 
Collections.emptyList());
+  }
+
+  /**
+   * Creates a LogicalWindow by parsing a {@link RexProgram}.
+   */
+  public static RelNode create(RelOptCluster cluster,
+      RelTraitSet traitSet, RelBuilder relBuilder, RelNode child,
+      final RexProgram program, List<RelHint> hints) {
     final RelDataType outRowType = program.getOutputRowType();
     // Build a list of distinct groups, partitions and aggregate
     // functions.
@@ -288,7 +306,7 @@ public static RelNode create(RelOptCluster cluster,
         };
 
     final LogicalWindow window =
-        LogicalWindow.create(traitSet, child, constants, intermediateRowType,
+        LogicalWindow.create(traitSet, hints, child, constants, 
intermediateRowType,
             groups);
 
     // The order that the "over" calls occur in the groups and
diff --git 
a/core/src/main/java/org/apache/calcite/rel/logical/ToLogicalConverter.java 
b/core/src/main/java/org/apache/calcite/rel/logical/ToLogicalConverter.java
index eaf8513314..86d08f5641 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/ToLogicalConverter.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/ToLogicalConverter.java
@@ -144,8 +144,8 @@ public ToLogicalConverter(RelBuilder relBuilder) {
     if (relNode instanceof Window) {
       final Window window = (Window) relNode;
       final RelNode input = visit(window.getInput());
-      return LogicalWindow.create(input.getTraitSet(), input, window.constants,
-          window.getRowType(), window.groups);
+      return LogicalWindow.create(input.getTraitSet(), window.getHints(),
+          input, window.constants, window.getRowType(), window.groups);
     }
 
     if (relNode instanceof Calc) {
diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java 
b/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java
index 54761eeef0..ed509b6d60 100644
--- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java
+++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java
@@ -262,7 +262,7 @@ public static RelNode fromMutable(MutableRel node, 
RelBuilder relBuilder) {
     case WINDOW: {
       final MutableWindow window = (MutableWindow) node;
       final RelNode child = fromMutable(window.getInput(), relBuilder);
-      return LogicalWindow.create(child.getTraitSet(),
+      return LogicalWindow.create(child.getTraitSet(), ImmutableList.of(),
           child, window.constants, window.rowType, window.groups);
     }
     case MATCH: {
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/CalcRelSplitter.java 
b/core/src/main/java/org/apache/calcite/rel/rules/CalcRelSplitter.java
index fde19567e8..67bbbae246 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/CalcRelSplitter.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/CalcRelSplitter.java
@@ -21,6 +21,8 @@
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Calc;
+import org.apache.calcite.rel.hint.Hintable;
+import org.apache.calcite.rel.hint.RelHint;
 import org.apache.calcite.rel.logical.LogicalCalc;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
@@ -88,6 +90,7 @@ public abstract class CalcRelSplitter {
   //~ Instance fields --------------------------------------------------------
 
   protected final RexProgram program;
+  protected final List<RelHint> hints;
   private final RelDataTypeFactory typeFactory;
 
   private final List<RelType> relTypes;
@@ -108,6 +111,7 @@ public abstract class CalcRelSplitter {
   CalcRelSplitter(Calc calc, RelBuilder relBuilder, RelType[] relTypes) {
     this.relBuilder = relBuilder;
     this.program = calc.getProgram();
+    this.hints = calc.getHints();
     this.cluster = calc.getCluster();
     this.traits = calc.getTraitSet();
     this.typeFactory = calc.getCluster().getTypeFactory();
@@ -224,8 +228,35 @@ RelNode execute() {
               projectExprOrdinals,
               conditionExprOrdinal,
               outputRowType);
+
+      // Propagate hints to each level. Since CalcRelSplitter builds a 
vertical stack of
+      // relational expressions (bottom-up), the relative depth of a level 
from the
+      // original top-level Calc determines how many '0's must be appended to 
the
+      // hint's inheritPath to maintain correct mapping.
+      //
+      // Example: SELECT /*+ Hint message */ SUM(v1) OVER(P1), SUM(v2) 
OVER(P2) FROM t
+      // split into 3 levels:
+      // LogicalProject, relativeDepth = 0, path = []
+      //   LogicalWindow (P2), relativeDepth = 1, path = [0]
+      //     LogicalWindow (P1), relativeDepth = 2, path = [0, 0]
+      final List<RelHint> levelHints;
+      final int relativeDepth = (levelCount - 1) - level;
+      if (hints.isEmpty() || relativeDepth == 0) {
+        levelHints = hints;
+      } else {
+        levelHints = new ArrayList<>(hints.size());
+        for (RelHint hint : hints) {
+          List<Integer> newPath = new ArrayList<>(hint.inheritPath.size() + 
relativeDepth);
+          newPath.addAll(hint.inheritPath);
+          for (int i = 0; i < relativeDepth; i++) {
+            newPath.add(0);
+          }
+          levelHints.add(hint.copy(newPath));
+        }
+      }
+
       rel =
-          relType.makeRel(cluster, traits, relBuilder, rel, program1);
+          relType.makeRel(cluster, traits, relBuilder, rel, program1, 
levelHints);
 
       // Sometimes a level's program merely projects its inputs. We don't
       // want these. They cause an explosion in the search space.
@@ -757,10 +788,24 @@ protected boolean supportsCondition() {
       return true;
     }
 
+    @Deprecated  // to be removed before 2.0
     protected RelNode makeRel(RelOptCluster cluster,
         RelTraitSet traitSet, RelBuilder relBuilder, RelNode input,
         RexProgram program) {
-      return LogicalCalc.create(input, program);
+      return makeRel(cluster, traitSet, relBuilder, input, program, 
ImmutableList.of());
+    }
+
+    protected RelNode makeRel(RelOptCluster cluster,
+        RelTraitSet traitSet,
+        RelBuilder relBuilder,
+        RelNode input,
+        RexProgram program,
+        List<RelHint> hints) {
+      RelNode rel = LogicalCalc.create(input, program);
+      if (!hints.isEmpty()) {
+        rel = ((Hintable) rel).withHints(hints);
+      }
+      return rel;
     }
 
     /**
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/ProjectToWindowRule.java 
b/core/src/main/java/org/apache/calcite/rel/rules/ProjectToWindowRule.java
index 37ef7b888c..931169135f 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ProjectToWindowRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ProjectToWindowRule.java
@@ -23,6 +23,7 @@
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Calc;
 import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.hint.RelHint;
 import org.apache.calcite.rel.logical.LogicalCalc;
 import org.apache.calcite.rel.logical.LogicalWindow;
 import org.apache.calcite.rex.RexBiVisitorImpl;
@@ -156,7 +157,9 @@ public ProjectToLogicalProjectAndWindowRule(
               project.getRowType(),
               project.getCluster().getRexBuilder());
       // temporary LogicalCalc, never registered
-      final LogicalCalc calc = LogicalCalc.create(input, program);
+      final LogicalCalc calc =
+          new LogicalCalc(project.getCluster(), project.getTraitSet(),
+              project.getHints(), input, program);
       final CalcRelSplitter transform =
           new WindowedAggRelSplitter(calc, call.builder()) {
             @Override protected RelNode handle(RelNode rel) {
@@ -226,11 +229,11 @@ static class WindowedAggRelSplitter extends 
CalcRelSplitter {
 
             @Override protected RelNode makeRel(RelOptCluster cluster,
                 RelTraitSet traitSet, RelBuilder relBuilder, RelNode input,
-                RexProgram program) {
+                RexProgram program, List<RelHint> hints) {
               assert !program.containsAggs();
               program = program.normalize(cluster.getRexBuilder(), null);
               return super.makeRel(cluster, traitSet, relBuilder, input,
-                  program);
+                  program, hints);
             }
         },
         new RelType("WinAggRelType") {
@@ -255,11 +258,11 @@ static class WindowedAggRelSplitter extends 
CalcRelSplitter {
           }
 
           @Override protected RelNode makeRel(RelOptCluster cluster, 
RelTraitSet traitSet,
-              RelBuilder relBuilder, RelNode input, RexProgram program) {
+              RelBuilder relBuilder, RelNode input, RexProgram program, 
List<RelHint> hints) {
             checkArgument(program.getCondition() == null,
                 "WindowedAggregateRel cannot accept a condition");
             return LogicalWindow.create(cluster, traitSet, relBuilder, input,
-                program);
+                program, hints);
           }
         }
     };
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
index fac0f459ac..e606fc8b72 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
@@ -172,8 +172,8 @@ public ProjectWindowTransposeRule(RelBuilderFactory 
relBuilderFactory) {
     }
 
     final LogicalWindow newLogicalWindow =
-        LogicalWindow.create(window.getTraitSet(), projectBelowWindow,
-            window.constants, outputBuilder.build(), groups);
+        LogicalWindow.create(window.getTraitSet(), window.getHints(),
+            projectBelowWindow, window.constants, outputBuilder.build(), 
groups);
 
     // Modify the top LogicalProject
     final List<RexNode> topProjExps =
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java 
b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
index fa6864ac42..88ffaf72fc 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
@@ -625,7 +625,7 @@ public WindowReduceExpressionsRule(Class<? extends Window> 
windowClass,
       }
       if (reduced) {
         call.transformTo(LogicalWindow
-            .create(window.getTraitSet(), window.getInput(),
+            .create(window.getTraitSet(), window.getHints(), window.getInput(),
                 window.getConstants(), window.getRowType(), groups));
         call.getPlanner().prune(window);
       }
diff --git 
a/core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java
index 9a75c50ada..cff0c20474 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java
@@ -58,6 +58,7 @@
 import org.apache.calcite.rel.logical.LogicalUnion;
 import org.apache.calcite.rel.logical.LogicalValues;
 import org.apache.calcite.rel.rules.CoreRules;
+import org.apache.calcite.rel.rules.ProjectToWindowRule;
 import org.apache.calcite.sql.SqlDelete;
 import org.apache.calcite.sql.SqlInsert;
 import org.apache.calcite.sql.SqlMerge;
@@ -270,6 +271,29 @@ public final Fixture sql(String sql) {
     sql(sql).ok();
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7338";>[CALCITE-7338]
+   * Window hints are not propagated to window rel nodes</a>. */
+  @Test void testWindowHintsPropagateAfterProjectToWindowRule() {
+    final String sql = "select /*+ mini_batch */ last_value(deptno)\n"
+        + "over (order by empno rows 2 following) from emp";
+
+    // Build rel with the same HintStrategyTable as this class
+    final RelNode rel = sql(sql).toRel();
+
+    // Run the rule that materializes LogicalWindow
+    HepProgramBuilder builder = new HepProgramBuilder();
+    builder.addRuleClass(ProjectToWindowRule.class);
+    HepPlanner planner = new HepPlanner(builder.build());
+    planner.addRule(CoreRules.PROJECT_TO_LOGICAL_PROJECT_AND_WINDOW);
+    planner.setRoot(rel);
+    RelNode transformed = planner.findBestExp();
+
+    // Expect the hint to be on LogicalWindow after the rule.
+    final RelHint expected = 
RelHint.builder("MINI_BATCH").inheritPath(0).build();
+    new ValidateHintVisitor(expected, Window.class).go(transformed);
+  }
+
   @Test void testHintsInSubQueryWithDecorrelation() {
     final String sql = "select /*+ resource(parallelism='3'), 
AGG_STRATEGY(TWO_PHASE) */\n"
         + "sum(e1.empno) from emp e1, dept d1\n"

Reply via email to