Skip to content

Commit e4b2baa

Browse files
xiedeyantuxuzifu666
authored andcommitted
[CALCITE-7338] Window hints are not propagated to window rel nodes
1 parent 6db44aa commit e4b2baa

File tree

8 files changed

+110
-20
lines changed

8 files changed

+110
-20
lines changed

core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,36 +102,54 @@ public LogicalWindow(RelOptCluster cluster, RelTraitSet traitSet, RelNode input,
102102

103103
@Override public LogicalWindow copy(RelTraitSet traitSet,
104104
List<RelNode> inputs) {
105-
return new LogicalWindow(getCluster(), traitSet, sole(inputs), constants,
105+
return new LogicalWindow(getCluster(), traitSet, hints, sole(inputs), constants,
106106
getRowType(), groups);
107107
}
108108

109109
@Override public Window copy(List<RexLiteral> constants) {
110-
return new LogicalWindow(getCluster(), getTraitSet(), getInput(),
110+
return new LogicalWindow(getCluster(), getTraitSet(), hints, getInput(),
111111
constants, getRowType(), groups);
112112
}
113113

114+
@Deprecated // to be removed before 2.0
115+
public static LogicalWindow create(RelTraitSet traitSet, RelNode input,
116+
List<RexLiteral> constants, RelDataType rowType, List<Group> groups) {
117+
return create(traitSet, Collections.emptyList(), input, constants, rowType, groups);
118+
}
119+
114120
/**
115121
* Creates a LogicalWindow.
116122
*
117-
* @param input Input relational expression
118123
* @param traitSet Trait set
124+
* @param hints Hints
125+
* @param input Input relational expression
119126
* @param constants List of constants that are additional inputs
120127
* @param rowType Output row type
121128
* @param groups Window groups
122129
*/
123-
public static LogicalWindow create(RelTraitSet traitSet, RelNode input,
124-
List<RexLiteral> constants, RelDataType rowType, List<Group> groups) {
125-
return new LogicalWindow(input.getCluster(), traitSet, input, constants,
130+
public static LogicalWindow create(RelTraitSet traitSet, List<RelHint> hints,
131+
RelNode input, List<RexLiteral> constants, RelDataType rowType,
132+
List<Group> groups) {
133+
return new LogicalWindow(input.getCluster(), traitSet, hints, input, constants,
126134
rowType, groups);
127135
}
128136

129137
/**
130138
* Creates a LogicalWindow by parsing a {@link RexProgram}.
131139
*/
140+
@Deprecated // to be removed before 2.0
132141
public static RelNode create(RelOptCluster cluster,
133142
RelTraitSet traitSet, RelBuilder relBuilder, RelNode child,
134143
final RexProgram program) {
144+
return create(cluster, traitSet, relBuilder, child, program, Collections.emptyList());
145+
}
146+
147+
/**
148+
* Creates a LogicalWindow by parsing a {@link RexProgram}.
149+
*/
150+
public static RelNode create(RelOptCluster cluster,
151+
RelTraitSet traitSet, RelBuilder relBuilder, RelNode child,
152+
final RexProgram program, List<RelHint> hints) {
135153
final RelDataType outRowType = program.getOutputRowType();
136154
// Build a list of distinct groups, partitions and aggregate
137155
// functions.
@@ -288,7 +306,7 @@ public static RelNode create(RelOptCluster cluster,
288306
};
289307

290308
final LogicalWindow window =
291-
LogicalWindow.create(traitSet, child, constants, intermediateRowType,
309+
LogicalWindow.create(traitSet, hints, child, constants, intermediateRowType,
292310
groups);
293311

294312
// The order that the "over" calls occur in the groups and

core/src/main/java/org/apache/calcite/rel/logical/ToLogicalConverter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ public ToLogicalConverter(RelBuilder relBuilder) {
144144
if (relNode instanceof Window) {
145145
final Window window = (Window) relNode;
146146
final RelNode input = visit(window.getInput());
147-
return LogicalWindow.create(input.getTraitSet(), input, window.constants,
148-
window.getRowType(), window.groups);
147+
return LogicalWindow.create(input.getTraitSet(), window.getHints(),
148+
input, window.constants, window.getRowType(), window.groups);
149149
}
150150

151151
if (relNode instanceof Calc) {

core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ public static RelNode fromMutable(MutableRel node, RelBuilder relBuilder) {
262262
case WINDOW: {
263263
final MutableWindow window = (MutableWindow) node;
264264
final RelNode child = fromMutable(window.getInput(), relBuilder);
265-
return LogicalWindow.create(child.getTraitSet(),
265+
return LogicalWindow.create(child.getTraitSet(), ImmutableList.of(),
266266
child, window.constants, window.rowType, window.groups);
267267
}
268268
case MATCH: {

core/src/main/java/org/apache/calcite/rel/rules/CalcRelSplitter.java

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import org.apache.calcite.plan.RelTraitSet;
2222
import org.apache.calcite.rel.RelNode;
2323
import org.apache.calcite.rel.core.Calc;
24+
import org.apache.calcite.rel.hint.Hintable;
25+
import org.apache.calcite.rel.hint.RelHint;
2426
import org.apache.calcite.rel.logical.LogicalCalc;
2527
import org.apache.calcite.rel.type.RelDataType;
2628
import org.apache.calcite.rel.type.RelDataTypeFactory;
@@ -88,6 +90,7 @@ public abstract class CalcRelSplitter {
8890
//~ Instance fields --------------------------------------------------------
8991

9092
protected final RexProgram program;
93+
protected final List<RelHint> hints;
9194
private final RelDataTypeFactory typeFactory;
9295

9396
private final List<RelType> relTypes;
@@ -108,6 +111,7 @@ public abstract class CalcRelSplitter {
108111
CalcRelSplitter(Calc calc, RelBuilder relBuilder, RelType[] relTypes) {
109112
this.relBuilder = relBuilder;
110113
this.program = calc.getProgram();
114+
this.hints = calc.getHints();
111115
this.cluster = calc.getCluster();
112116
this.traits = calc.getTraitSet();
113117
this.typeFactory = calc.getCluster().getTypeFactory();
@@ -224,8 +228,35 @@ RelNode execute() {
224228
projectExprOrdinals,
225229
conditionExprOrdinal,
226230
outputRowType);
231+
232+
// Propagate hints to each level. Since CalcRelSplitter builds a vertical stack of
233+
// relational expressions (bottom-up), the relative depth of a level from the
234+
// original top-level Calc determines how many '0's must be appended to the
235+
// hint's inheritPath to maintain correct mapping.
236+
//
237+
// Example: SELECT /*+ Hint message */ SUM(v1) OVER(P1), SUM(v2) OVER(P2) FROM t
238+
// split into 3 levels:
239+
// LogicalProject, relativeDepth = 0, path = []
240+
// LogicalWindow (P2), relativeDepth = 1, path = [0]
241+
// LogicalWindow (P1), relativeDepth = 2, path = [0, 0]
242+
final List<RelHint> levelHints;
243+
final int relativeDepth = (levelCount - 1) - level;
244+
if (hints.isEmpty() || relativeDepth == 0) {
245+
levelHints = hints;
246+
} else {
247+
levelHints = new ArrayList<>(hints.size());
248+
for (RelHint hint : hints) {
249+
List<Integer> newPath = new ArrayList<>(hint.inheritPath.size() + relativeDepth);
250+
newPath.addAll(hint.inheritPath);
251+
for (int i = 0; i < relativeDepth; i++) {
252+
newPath.add(0);
253+
}
254+
levelHints.add(hint.copy(newPath));
255+
}
256+
}
257+
227258
rel =
228-
relType.makeRel(cluster, traits, relBuilder, rel, program1);
259+
relType.makeRel(cluster, traits, relBuilder, rel, program1, levelHints);
229260

230261
// Sometimes a level's program merely projects its inputs. We don't
231262
// want these. They cause an explosion in the search space.
@@ -757,10 +788,24 @@ protected boolean supportsCondition() {
757788
return true;
758789
}
759790

791+
@Deprecated // to be removed before 2.0
760792
protected RelNode makeRel(RelOptCluster cluster,
761793
RelTraitSet traitSet, RelBuilder relBuilder, RelNode input,
762794
RexProgram program) {
763-
return LogicalCalc.create(input, program);
795+
return makeRel(cluster, traitSet, relBuilder, input, program, ImmutableList.of());
796+
}
797+
798+
protected RelNode makeRel(RelOptCluster cluster,
799+
RelTraitSet traitSet,
800+
RelBuilder relBuilder,
801+
RelNode input,
802+
RexProgram program,
803+
List<RelHint> hints) {
804+
RelNode rel = LogicalCalc.create(input, program);
805+
if (!hints.isEmpty()) {
806+
rel = ((Hintable) rel).withHints(hints);
807+
}
808+
return rel;
764809
}
765810

766811
/**

core/src/main/java/org/apache/calcite/rel/rules/ProjectToWindowRule.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.apache.calcite.rel.RelNode;
2424
import org.apache.calcite.rel.core.Calc;
2525
import org.apache.calcite.rel.core.Project;
26+
import org.apache.calcite.rel.hint.RelHint;
2627
import org.apache.calcite.rel.logical.LogicalCalc;
2728
import org.apache.calcite.rel.logical.LogicalWindow;
2829
import org.apache.calcite.rex.RexBiVisitorImpl;
@@ -156,7 +157,9 @@ public ProjectToLogicalProjectAndWindowRule(
156157
project.getRowType(),
157158
project.getCluster().getRexBuilder());
158159
// temporary LogicalCalc, never registered
159-
final LogicalCalc calc = LogicalCalc.create(input, program);
160+
final LogicalCalc calc =
161+
new LogicalCalc(project.getCluster(), project.getTraitSet(),
162+
project.getHints(), input, program);
160163
final CalcRelSplitter transform =
161164
new WindowedAggRelSplitter(calc, call.builder()) {
162165
@Override protected RelNode handle(RelNode rel) {
@@ -226,11 +229,11 @@ static class WindowedAggRelSplitter extends CalcRelSplitter {
226229

227230
@Override protected RelNode makeRel(RelOptCluster cluster,
228231
RelTraitSet traitSet, RelBuilder relBuilder, RelNode input,
229-
RexProgram program) {
232+
RexProgram program, List<RelHint> hints) {
230233
assert !program.containsAggs();
231234
program = program.normalize(cluster.getRexBuilder(), null);
232235
return super.makeRel(cluster, traitSet, relBuilder, input,
233-
program);
236+
program, hints);
234237
}
235238
},
236239
new RelType("WinAggRelType") {
@@ -255,11 +258,11 @@ static class WindowedAggRelSplitter extends CalcRelSplitter {
255258
}
256259

257260
@Override protected RelNode makeRel(RelOptCluster cluster, RelTraitSet traitSet,
258-
RelBuilder relBuilder, RelNode input, RexProgram program) {
261+
RelBuilder relBuilder, RelNode input, RexProgram program, List<RelHint> hints) {
259262
checkArgument(program.getCondition() == null,
260263
"WindowedAggregateRel cannot accept a condition");
261264
return LogicalWindow.create(cluster, traitSet, relBuilder, input,
262-
program);
265+
program, hints);
263266
}
264267
}
265268
};

core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,8 @@ public ProjectWindowTransposeRule(RelBuilderFactory relBuilderFactory) {
172172
}
173173

174174
final LogicalWindow newLogicalWindow =
175-
LogicalWindow.create(window.getTraitSet(), projectBelowWindow,
176-
window.constants, outputBuilder.build(), groups);
175+
LogicalWindow.create(window.getTraitSet(), window.getHints(),
176+
projectBelowWindow, window.constants, outputBuilder.build(), groups);
177177

178178
// Modify the top LogicalProject
179179
final List<RexNode> topProjExps =

core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,7 @@ public WindowReduceExpressionsRule(Class<? extends Window> windowClass,
625625
}
626626
if (reduced) {
627627
call.transformTo(LogicalWindow
628-
.create(window.getTraitSet(), window.getInput(),
628+
.create(window.getTraitSet(), window.getHints(), window.getInput(),
629629
window.getConstants(), window.getRowType(), groups));
630630
call.getPlanner().prune(window);
631631
}

core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import org.apache.calcite.rel.logical.LogicalUnion;
5959
import org.apache.calcite.rel.logical.LogicalValues;
6060
import org.apache.calcite.rel.rules.CoreRules;
61+
import org.apache.calcite.rel.rules.ProjectToWindowRule;
6162
import org.apache.calcite.sql.SqlDelete;
6263
import org.apache.calcite.sql.SqlInsert;
6364
import org.apache.calcite.sql.SqlMerge;
@@ -270,6 +271,29 @@ public final Fixture sql(String sql) {
270271
sql(sql).ok();
271272
}
272273

274+
/** Test case for
275+
* <a href="https://issues.apache.org/jira/browse/CALCITE-7338">[CALCITE-7338]
276+
* Window hints are not propagated to window rel nodes</a>. */
277+
@Test void testWindowHintsPropagateAfterProjectToWindowRule() {
278+
final String sql = "select /*+ mini_batch */ last_value(deptno)\n"
279+
+ "over (order by empno rows 2 following) from emp";
280+
281+
// Build rel with the same HintStrategyTable as this class
282+
final RelNode rel = sql(sql).toRel();
283+
284+
// Run the rule that materializes LogicalWindow
285+
HepProgramBuilder builder = new HepProgramBuilder();
286+
builder.addRuleClass(ProjectToWindowRule.class);
287+
HepPlanner planner = new HepPlanner(builder.build());
288+
planner.addRule(CoreRules.PROJECT_TO_LOGICAL_PROJECT_AND_WINDOW);
289+
planner.setRoot(rel);
290+
RelNode transformed = planner.findBestExp();
291+
292+
// Expect the hint to be on LogicalWindow after the rule.
293+
final RelHint expected = RelHint.builder("MINI_BATCH").inheritPath(0).build();
294+
new ValidateHintVisitor(expected, Window.class).go(transformed);
295+
}
296+
273297
@Test void testHintsInSubQueryWithDecorrelation() {
274298
final String sql = "select /*+ resource(parallelism='3'), AGG_STRATEGY(TWO_PHASE) */\n"
275299
+ "sum(e1.empno) from emp e1, dept d1\n"

0 commit comments

Comments
 (0)