This is an automated email from the ASF dual-hosted git repository.
rubenql pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push:
new 7c2f677 [CALCITE-4055] RelFieldTrimmer loses hints
7c2f677 is described below
commit 7c2f6772ee6bbfe4fefc2cf35152d6c71cc7a361
Author: rubenada <[email protected]>
AuthorDate: Tue Jun 9 15:11:34 2020 +0200
[CALCITE-4055] RelFieldTrimmer loses hints
---
.../apache/calcite/sql2rel/RelFieldTrimmer.java | 32 +++--
.../java/org/apache/calcite/tools/RelBuilder.java | 7 +-
.../calcite/sql2rel/RelFieldTrimmerTest.java | 137 +++++++++++++++++++--
3 files changed, 161 insertions(+), 15 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
index 2da00c0..0a4ec01 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
@@ -390,7 +390,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
// Some parts of the system can't handle rows with zero fields, so
// pretend that one field is used.
if (fieldsUsed.cardinality() == 0) {
- return dummyProject(fieldCount, newInput);
+ return dummyProject(fieldCount, newInput, project);
}
// Build new project expressions, and populate the mapping.
@@ -417,7 +417,8 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
relBuilder.push(newInput);
relBuilder.project(newProjects, newRowType.getFieldNames());
- return result(relBuilder.build(), mapping);
+ final RelNode newProject = RelOptUtil.propagateRelHints(project,
relBuilder.build());
+ return result(newProject, mapping);
}
/** Creates a project with a dummy column, to protect the parts of the system
@@ -425,9 +426,21 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
*
* @param fieldCount Number of fields in the original relational expression
* @param input Trimmed input
- * @return Dummy project, or null if no dummy is required
+ * @return Dummy project
*/
protected TrimResult dummyProject(int fieldCount, RelNode input) {
+ return dummyProject(fieldCount, input, null);
+ }
+
+ /** Creates a project with a dummy column, to protect the parts of the system
+ * that cannot handle a relational expression with no columns.
+ *
+ * @param fieldCount Number of fields in the original relational expression
+ * @param input Trimmed input
+ * @param originalRelNode Source RelNode for hint propagation (or null if no
propagation needed)
+ * @return Dummy project
+ */
+ protected TrimResult dummyProject(int fieldCount, RelNode input, RelNode
originalRelNode) {
final RelOptCluster cluster = input.getCluster();
final Mapping mapping =
Mappings.create(MappingType.INVERSE_SURJECTION, fieldCount, 1);
@@ -439,8 +452,12 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
final RexLiteral expr =
cluster.getRexBuilder().makeExactLiteral(BigDecimal.ZERO);
relBuilder.push(input);
- relBuilder.project(ImmutableList.<RexNode>of(expr),
ImmutableList.of("DUMMY"));
- return result(relBuilder.build(), mapping);
+ relBuilder.project(ImmutableList.of(expr), ImmutableList.of("DUMMY"));
+ RelNode newProject = relBuilder.build();
+ if (originalRelNode != null) {
+ newProject = RelOptUtil.propagateRelHints(originalRelNode, newProject);
+ }
+ return result(newProject, mapping);
}
/**
@@ -773,7 +790,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
default:
relBuilder.join(join.getJoinType(), newConditionExpr);
}
-
+ relBuilder.hints(join.getHints());
return result(relBuilder.build(), mapping);
}
@@ -971,7 +988,8 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
(Iterable<ImmutableBitSet>) newGroupSets);
relBuilder.aggregate(groupKey, newAggCallList);
- return result(relBuilder.build(), mapping);
+ final RelNode newAggregate = RelOptUtil.propagateRelHints(aggregate,
relBuilder.build());
+ return result(newAggregate, mapping);
}
/**
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 77e10c0..3362a24 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -2718,11 +2718,16 @@ public class RelBuilder {
*/
public RelBuilder hints(Iterable<RelHint> hints) {
Objects.requireNonNull(hints);
+ final List<RelHint> relHintList = hints instanceof List ? (List<RelHint>)
hints
+ : Lists.newArrayList(hints);
+ if (relHintList.isEmpty()) {
+ return this;
+ }
final Frame frame = peek_();
assert frame != null : "There is no relational expression to attach the
hints";
assert frame.rel instanceof Hintable : "The top relational expression is
not a Hintable";
Hintable hintable = (Hintable) frame.rel;
- replaceTop(hintable.attachHints(ImmutableList.copyOf(hints)));
+ replaceTop(hintable.attachHints(relHintList));
return this;
}
diff --git
a/core/src/test/java/org/apache/calcite/sql2rel/RelFieldTrimmerTest.java
b/core/src/test/java/org/apache/calcite/sql2rel/RelFieldTrimmerTest.java
index 2ce2a8c..7c57f07 100644
--- a/core/src/test/java/org/apache/calcite/sql2rel/RelFieldTrimmerTest.java
+++ b/core/src/test/java/org/apache/calcite/sql2rel/RelFieldTrimmerTest.java
@@ -20,6 +20,13 @@ import org.apache.calcite.plan.RelTraitDef;
import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelDistributions;
import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+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.schema.SchemaPlus;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.test.CalciteAssert;
@@ -36,6 +43,7 @@ import java.util.List;
import static org.apache.calcite.test.Matchers.hasTree;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertTrue;
class RelFieldTrimmerTest {
public static Frameworks.ConfigBuilder config() {
@@ -48,7 +56,7 @@ class RelFieldTrimmerTest {
.programs(Programs.heuristicJoinOrder(Programs.RULE_SET, true, 2));
}
- @Test public void testSortExchangeFieldTrimmer() {
+ @Test void testSortExchangeFieldTrimmer() {
final RelBuilder builder = RelBuilder.create(config().build());
final RelNode root =
builder.scan("EMP")
@@ -67,7 +75,7 @@ class RelFieldTrimmerTest {
assertThat(trimmed, hasTree(expected));
}
- @Test public void testSortExchangeFieldTrimmerWhenProjectCannotBeMerged() {
+ @Test void testSortExchangeFieldTrimmerWhenProjectCannotBeMerged() {
final RelBuilder builder = RelBuilder.create(config().build());
final RelNode root =
builder.scan("EMP")
@@ -87,7 +95,7 @@ class RelFieldTrimmerTest {
assertThat(trimmed, hasTree(expected));
}
- @Test public void testSortExchangeFieldTrimmerWithEmptyCollation() {
+ @Test void testSortExchangeFieldTrimmerWithEmptyCollation() {
final RelBuilder builder = RelBuilder.create(config().build());
final RelNode root =
builder.scan("EMP")
@@ -106,7 +114,7 @@ class RelFieldTrimmerTest {
assertThat(trimmed, hasTree(expected));
}
- @Test public void testSortExchangeFieldTrimmerWithSingletonDistribution() {
+ @Test void testSortExchangeFieldTrimmerWithSingletonDistribution() {
final RelBuilder builder = RelBuilder.create(config().build());
final RelNode root =
builder.scan("EMP")
@@ -125,7 +133,7 @@ class RelFieldTrimmerTest {
assertThat(trimmed, hasTree(expected));
}
- @Test public void testExchangeFieldTrimmer() {
+ @Test void testExchangeFieldTrimmer() {
final RelBuilder builder = RelBuilder.create(config().build());
final RelNode root =
builder.scan("EMP")
@@ -144,7 +152,7 @@ class RelFieldTrimmerTest {
assertThat(trimmed, hasTree(expected));
}
- @Test public void testExchangeFieldTrimmerWhenProjectCannotBeMerged() {
+ @Test void testExchangeFieldTrimmerWhenProjectCannotBeMerged() {
final RelBuilder builder = RelBuilder.create(config().build());
final RelNode root =
builder.scan("EMP")
@@ -164,7 +172,7 @@ class RelFieldTrimmerTest {
assertThat(trimmed, hasTree(expected));
}
- @Test public void testExchangeFieldTrimmerWithSingletonDistribution() {
+ @Test void testExchangeFieldTrimmerWithSingletonDistribution() {
final RelBuilder builder = RelBuilder.create(config().build());
final RelNode root =
builder.scan("EMP")
@@ -183,4 +191,119 @@ class RelFieldTrimmerTest {
assertThat(trimmed, hasTree(expected));
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-4055">[CALCITE-4055]
+ * RelFieldTrimmer loses hints</a>. */
+ @Test void testJoinWithHints() {
+ final RelHint noHashJoinHint = RelHint.builder("no_hash_join").build();
+ final RelBuilder builder = RelBuilder.create(config().build());
+ builder.getCluster().setHintStrategies(
+ HintStrategyTable.builder()
+ .hintStrategy("no_hash_join", HintPredicates.JOIN)
+ .build());
+ final RelNode original =
+ builder.scan("EMP")
+ .scan("DEPT")
+ .join(JoinRelType.INNER,
+ builder.equals(
+ builder.field(2, 0, "DEPTNO"),
+ builder.field(2, 1, "DEPTNO")))
+ .hints(noHashJoinHint)
+ .project(
+ builder.field("ENAME"),
+ builder.field("DNAME"))
+ .build();
+
+ final RelFieldTrimmer fieldTrimmer = new RelFieldTrimmer(null, builder);
+ final RelNode trimmed = fieldTrimmer.trim(original);
+
+ final String expected = ""
+ + "LogicalProject(ENAME=[$1], DNAME=[$4])\n"
+ + " LogicalJoin(condition=[=($2, $3)], joinType=[inner])\n"
+ + " LogicalProject(EMPNO=[$0], ENAME=[$1], DEPTNO=[$7])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n"
+ + " LogicalProject(DEPTNO=[$0], DNAME=[$1])\n"
+ + " LogicalTableScan(table=[[scott, DEPT]])\n";
+ assertThat(trimmed, hasTree(expected));
+
+ assertTrue(original.getInput(0) instanceof Join);
+ final Join originalJoin = (Join) original.getInput(0);
+ assertTrue(originalJoin.getHints().contains(noHashJoinHint));
+
+ assertTrue(trimmed.getInput(0) instanceof Join);
+ final Join join = (Join) trimmed.getInput(0);
+ assertTrue(join.getHints().contains(noHashJoinHint));
+ }
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-4055">[CALCITE-4055]
+ * RelFieldTrimmer loses hints</a>. */
+ @Test void testAggregateWithHints() {
+ final RelHint aggHint = RelHint.builder("resource").build();
+ final RelBuilder builder = RelBuilder.create(config().build());
+ builder.getCluster().setHintStrategies(
+ HintStrategyTable.builder().hintStrategy("resource",
HintPredicates.AGGREGATE).build());
+ final RelNode original =
+ builder.scan("EMP")
+ .aggregate(
+ builder.groupKey(builder.field("DEPTNO")),
+ builder.count(false, "C", builder.field("EMPNO")))
+ .hints(aggHint)
+ .build();
+
+ final RelFieldTrimmer fieldTrimmer = new RelFieldTrimmer(null, builder);
+ final RelNode trimmed = fieldTrimmer.trim(original);
+
+ final String expected = ""
+ + "LogicalAggregate(group=[{1}], C=[COUNT($0)])\n"
+ + " LogicalProject(EMPNO=[$0], DEPTNO=[$7])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ assertThat(trimmed, hasTree(expected));
+
+ assertTrue(original instanceof Aggregate);
+ final Aggregate originalAggregate = (Aggregate) original;
+ assertTrue(originalAggregate.getHints().contains(aggHint));
+
+ assertTrue(trimmed instanceof Aggregate);
+ final Aggregate aggregate = (Aggregate) trimmed;
+ assertTrue(aggregate.getHints().contains(aggHint));
+ }
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-4055">[CALCITE-4055]
+ * RelFieldTrimmer loses hints</a>. */
+ @Test void testProjectWithHints() {
+ final RelHint projectHint = RelHint.builder("resource").build();
+ final RelBuilder builder = RelBuilder.create(config().build());
+ builder.getCluster().setHintStrategies(
+ HintStrategyTable.builder().hintStrategy("resource",
HintPredicates.PROJECT).build());
+ final RelNode original =
+ builder.scan("EMP")
+ .project(
+ builder.field("EMPNO"),
+ builder.field("ENAME"),
+ builder.field("DEPTNO")
+ ).hints(projectHint)
+ .sort(builder.field("EMPNO"))
+ .project(builder.field("EMPNO"))
+ .build();
+
+ final RelFieldTrimmer fieldTrimmer = new RelFieldTrimmer(null, builder);
+ final RelNode trimmed = fieldTrimmer.trim(original);
+
+ final String expected = ""
+ + "LogicalSort(sort0=[$0], dir0=[ASC])\n"
+ + " LogicalProject(EMPNO=[$0])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ assertThat(trimmed, hasTree(expected));
+
+ assertTrue(original.getInput(0).getInput(0) instanceof Project);
+ final Project originalProject = (Project) original.getInput(0).getInput(0);
+ assertTrue(originalProject.getHints().contains(projectHint));
+
+ assertTrue(trimmed.getInput(0) instanceof Project);
+ final Project project = (Project) trimmed.getInput(0);
+ assertTrue(project.getHints().contains(projectHint));
+ }
+
}