This is an automated email from the ASF dual-hosted git repository.
snuyanzin 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 c63f0be38e [CALCITE-5381] Add `convertCorrelateToJoin` config property
to RelBuilder
c63f0be38e is described below
commit c63f0be38e2de8d52b2ef09dc43588d0cfe48b1b
Author: Sergey Nuyanzin <[email protected]>
AuthorDate: Sun Mar 5 18:40:00 2023 +0100
[CALCITE-5381] Add `convertCorrelateToJoin` config property to RelBuilder
---
.../java/org/apache/calcite/tools/RelBuilder.java | 13 ++-
.../org/apache/calcite/test/RelBuilderTest.java | 103 ++++++++++++++++++---
2 files changed, 102 insertions(+), 14 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 66814f9cb5..23ca4fdda8 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -3889,11 +3889,14 @@ public class RelBuilder {
* @throws IllegalArgumentException if the {@link CorrelationId} is used by
left side or if the a
* {@link CorrelationId} is present and the {@link JoinRelType} is FULL or
RIGHT.
*/
- private static boolean checkIfCorrelated(Set<CorrelationId> variablesSet,
+ private boolean checkIfCorrelated(Set<CorrelationId> variablesSet,
JoinRelType joinType, RelNode leftNode, RelNode rightRel) {
if (variablesSet.size() != 1) {
return false;
}
+ if (!config.convertCorrelateToJoin()) {
+ return true;
+ }
CorrelationId id = Iterables.getOnlyElement(variablesSet);
if (!RelOptUtil.notContainsCorrelation(leftNode, id, Litmus.IGNORE)) {
throw new IllegalArgumentException("variable " + id
@@ -4688,6 +4691,14 @@ public class RelBuilder {
/** Sets {@link #aggregateUnique()}. */
Config withAggregateUnique(boolean aggregateUnique);
+
+ /** Whether to convert Correlate to Join if correlation variable is
unused. */
+ @Value.Default default boolean convertCorrelateToJoin() {
+ return true;
+ }
+
+ /** Sets {@link #convertCorrelateToJoin()}. */
+ Config withConvertCorrelateToJoin(boolean convertCorrelateToJoin);
}
}
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 796e008550..b47b101877 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -96,6 +96,8 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.hamcrest.Matcher;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import java.lang.reflect.Method;
import java.sql.Connection;
@@ -4202,8 +4204,24 @@ public class RelBuilderTest {
root, hasTree(expected));
}
- @Test void testSemiCorrelatedViaJoin() {
- RelNode root = buildCorrelateWithJoin(JoinRelType.SEMI);
+ @Test void testSimpleSemiCorrelateViaJoinWithoutConvertCorrelateToJoin() {
+ final Function<RelBuilder, RelNode> f = b ->
+ buildSimpleCorrelateWithJoin(JoinRelType.SEMI, b);
+ final String expected = ""
+ + "LogicalCorrelate(correlation=[$cor0], joinType=[semi],
requiredColumns=[{7}])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n"
+ + " LogicalFilter(condition=[=($cor0.DEPTNO, $0)])\n"
+ + " LogicalTableScan(table=[[scott, DEPT]])\n";
+ assertThat(
+ "Join with correlate id but the id never used should be simplified to
a join.",
+ f.apply(createBuilder(c -> c.withConvertCorrelateToJoin(false))),
hasTree(expected));
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void testSemiCorrelatedViaJoin(boolean convertCorrelateToJoin) {
+ final Function<RelBuilder, RelNode> f = b ->
+ buildCorrelateWithJoin(JoinRelType.SEMI, b);
final String expected = ""
+ "LogicalCorrelate(correlation=[$cor0], joinType=[semi],
requiredColumns=[{0, 7}])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n"
@@ -4212,7 +4230,8 @@ public class RelBuilderTest {
+ " LogicalTableScan(table=[[scott, DEPT]])\n";
assertThat(
"Correlated semi joins should emmit a correlate with a filter on the
right side.",
- root, hasTree(expected));
+ f.apply(createBuilder(c ->
c.withConvertCorrelateToJoin(convertCorrelateToJoin))),
+ hasTree(expected));
}
@Test void testSimpleAntiCorrelateViaJoin() {
@@ -4226,8 +4245,24 @@ public class RelBuilderTest {
root, hasTree(expected));
}
- @Test void testAntiCorrelateViaJoin() {
- RelNode root = buildCorrelateWithJoin(JoinRelType.ANTI);
+ @Test void testSimpleAntiCorrelateViaJoinWithoutConvertCorrelateToJoin() {
+ final Function<RelBuilder, RelNode> f = b ->
+ buildSimpleCorrelateWithJoin(JoinRelType.ANTI, b);
+ final String expected = ""
+ + "LogicalCorrelate(correlation=[$cor0], joinType=[anti],
requiredColumns=[{7}])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n"
+ + " LogicalFilter(condition=[=($cor0.DEPTNO, $0)])\n"
+ + " LogicalTableScan(table=[[scott, DEPT]])\n";
+ assertThat(
+ "Join with correlate id but the id never used should be simplified to
a join.",
+ f.apply(createBuilder(c -> c.withConvertCorrelateToJoin(false))),
hasTree(expected));
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void testAntiCorrelateViaJoin(boolean convertCorrelateToJoin) {
+ final Function<RelBuilder, RelNode> f = b ->
+ buildCorrelateWithJoin(JoinRelType.ANTI, b);
final String expected = ""
+ "LogicalCorrelate(correlation=[$cor0], joinType=[anti],
requiredColumns=[{0, 7}])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n"
@@ -4236,7 +4271,9 @@ public class RelBuilderTest {
+ " LogicalTableScan(table=[[scott, DEPT]])\n";
assertThat(
"Correlated anti joins should emmit a correlate with a filter on the
right side.",
- root, hasTree(expected)); }
+ f.apply(createBuilder(c ->
c.withConvertCorrelateToJoin(convertCorrelateToJoin))),
+ hasTree(expected));
+ }
@Test void testSimpleLeftCorrelateViaJoin() {
RelNode root = buildSimpleCorrelateWithJoin(JoinRelType.LEFT);
@@ -4249,8 +4286,26 @@ public class RelBuilderTest {
root, hasTree(expected));
}
- @Test void testLeftCorrelateViaJoin() {
- RelNode root = buildCorrelateWithJoin(JoinRelType.LEFT);
+ @Test void testSimpleLeftCorrelateViaJoinWithoutConvertCorrelateToJoin() {
+ final Function<RelBuilder, RelNode> f = b ->
+ buildSimpleCorrelateWithJoin(JoinRelType.LEFT, b);
+ final String expected = ""
+ + "LogicalCorrelate(correlation=[$cor0], joinType=[left],
requiredColumns=[{7}])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n"
+ + " LogicalFilter(condition=[=($cor0.DEPTNO, $0)])\n"
+ + " LogicalTableScan(table=[[scott, DEPT]])\n";
+ assertThat(
+ "Join with correlate id but the id never used should be simplified to
a join.",
+ f.apply(createBuilder(c -> c.withConvertCorrelateToJoin(false))),
hasTree(expected));
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void testLeftCorrelateViaJoin(boolean convertCorrelateToJoin) {
+ RelNode root =
+ buildCorrelateWithJoin(
+ JoinRelType.LEFT,
+ createBuilder(c ->
c.withConvertCorrelateToJoin(convertCorrelateToJoin)));
final String expected = ""
+ "LogicalCorrelate(correlation=[$cor0], joinType=[left],
requiredColumns=[{0, 7}])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n"
@@ -4272,8 +4327,23 @@ public class RelBuilderTest {
root, hasTree(expected));
}
- @Test void testInnerCorrelateViaJoin() {
- RelNode root = buildCorrelateWithJoin(JoinRelType.INNER);
+ @Test void testSimpleInnerCorrelateViaJoinWithoutConvertCorrelateToJoin() {
+ final Function<RelBuilder, RelNode> f = b ->
+ buildSimpleCorrelateWithJoin(JoinRelType.INNER, b);
+ final String expected = ""
+ + "LogicalFilter(condition=[=($7, $8)])\n"
+ + " LogicalCorrelate(correlation=[$cor0], joinType=[inner],
requiredColumns=[{}])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n"
+ + " LogicalTableScan(table=[[scott, DEPT]])\n";
+ assertThat("Join with correlate id but never used should be simplified to
a join.",
+ f.apply(createBuilder(c -> c.withConvertCorrelateToJoin(false))),
hasTree(expected));
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void testInnerCorrelateViaJoin(boolean convertCorrelateToJoin) {
+ final Function<RelBuilder, RelNode> f = b ->
+ buildCorrelateWithJoin(JoinRelType.INNER, b);
final String expected = ""
+ "LogicalFilter(condition=[=($7, $8)])\n"
+ " LogicalCorrelate(correlation=[$cor0], joinType=[inner],
requiredColumns=[{0}])\n"
@@ -4282,7 +4352,8 @@ public class RelBuilderTest {
+ " LogicalTableScan(table=[[scott, DEPT]])\n";
assertThat(
"Correlated inner joins should emmit a correlate with a filter on
top.",
- root, hasTree(expected));
+ f.apply(createBuilder(c ->
c.withConvertCorrelateToJoin(convertCorrelateToJoin))),
+ hasTree(expected));
}
@Test void testSimpleRightCorrelateViaJoinThrowsException() {
@@ -4310,7 +4381,10 @@ public class RelBuilderTest {
}
private static RelNode buildSimpleCorrelateWithJoin(JoinRelType type) {
- final RelBuilder builder = RelBuilder.create(config().build());
+ return buildSimpleCorrelateWithJoin(type,
RelBuilder.create(config().build()));
+ }
+
+ private static RelNode buildSimpleCorrelateWithJoin(JoinRelType type,
RelBuilder builder) {
final Holder<@Nullable RexCorrelVariable> v = Holder.empty();
return builder
.scan("EMP")
@@ -4324,7 +4398,10 @@ public class RelBuilderTest {
}
private static RelNode buildCorrelateWithJoin(JoinRelType type) {
- final RelBuilder builder = RelBuilder.create(config().build());
+ return buildCorrelateWithJoin(type, RelBuilder.create(config().build()));
+ }
+
+ private static RelNode buildCorrelateWithJoin(JoinRelType type, RelBuilder
builder) {
final RexBuilder rexBuilder = builder.getRexBuilder();
final Holder<@Nullable RexCorrelVariable> v = Holder.empty();
return builder