This is an automated email from the ASF dual-hosted git repository. rubenql 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 cc3c5fed07 [CALCITE-5670] Assertion error in SemiJoinJoinTransposeRule when Semi-Join has keys from both tables of the bottom Join cc3c5fed07 is described below commit cc3c5fed072dad2efc8d0961e66fea38ba8f3817 Author: Roman Kondakov <rkonda...@querifylabs.com> AuthorDate: Sun Apr 23 18:35:12 2023 +0700 [CALCITE-5670] Assertion error in SemiJoinJoinTransposeRule when Semi-Join has keys from both tables of the bottom Join --- .../rel/rules/SemiJoinJoinTransposeRule.java | 9 ++++--- .../org/apache/calcite/test/RelOptRulesTest.java | 12 +++++++++ .../org/apache/calcite/test/RelOptRulesTest.xml | 31 ++++++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinJoinTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinJoinTransposeRule.java index 9ad23cc92e..d314b2819a 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinJoinTransposeRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinJoinTransposeRule.java @@ -76,7 +76,6 @@ public class SemiJoinJoinTransposeRule if (join.isSemiJoin()) { return; } - final ImmutableIntList leftKeys = semiJoin.analyzeCondition().leftKeys; // X is the left child of the join below the semi-join // Y is the right child of the join below the semi-join @@ -102,6 +101,7 @@ public class SemiJoinJoinTransposeRule // determine which operands below the semi-join are the actual // Rels that participate in the semi-join + final ImmutableIntList leftKeys = semiJoin.analyzeCondition().leftKeys; int nKeysFromX = 0; for (int leftKey : leftKeys) { if (leftKey < nFieldsX) { @@ -109,9 +109,10 @@ public class SemiJoinJoinTransposeRule } } - // the keys must all originate from either the left or right; - // otherwise, a semi-join wouldn't have been created - assert (nKeysFromX == 0) || (nKeysFromX == leftKeys.size()); + if (nKeysFromX != 0 && nKeysFromX != leftKeys.size()) { + // We can not push semi-join down if it has keys from both tables of the bottom join + return; + } // need to convert the semi-join condition and possibly the keys final RexNode newSemiJoinFilter; diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java index 4fd8e82711..841c32a700 100644 --- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java @@ -2666,6 +2666,18 @@ class RelOptRulesTest extends RelOptTestBase { .check(); } + @Test void testPushSemiJoinPastJoinRuleNotHappensJoinKeysDifferentOrigin() { + // tests the case where the semijoin is not pushed because it uses join keys from both tables + // of the bottom join. + final String sql = "select e.ename from emp e, dept d, bonus b\n" + + "where e.deptno = d.deptno and e.ename = b.ename and d.name = b.job"; + sql(sql) + .withRule(CoreRules.FILTER_INTO_JOIN, + CoreRules.JOIN_ADD_REDUNDANT_SEMI_JOIN, + CoreRules.SEMI_JOIN_JOIN_TRANSPOSE) + .check(); + } + @Test void testPushSemiJoinPastFilter() { final String sql = "select e.ename from emp e, dept d\n" + "where e.deptno = d.deptno and e.ename = 'foo'"; diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml index 91c54a496b..72567797e3 100644 --- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml +++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml @@ -10533,6 +10533,37 @@ LogicalProject(ENAME=[$1]) LogicalTableScan(table=[[CATALOG, SALES, EMP]]) LogicalTableScan(table=[[CATALOG, SALES, DEPT]]) LogicalTableScan(table=[[CATALOG, SALES, EMP]]) +]]> + </Resource> + </TestCase> + <TestCase name="testPushSemiJoinPastJoinRuleNotHappensJoinKeysDifferentOrigin"> + <Resource name="sql"> + <![CDATA[select e.ename from emp e, dept d, bonus b +where e.deptno = d.deptno and e.ename = b.ename and d.name = b.job]]> + </Resource> + <Resource name="planBefore"> + <![CDATA[ +LogicalProject(ENAME=[$1]) + LogicalFilter(condition=[AND(=($7, $9), =($1, $11), =($10, $12))]) + LogicalJoin(condition=[true], joinType=[inner]) + LogicalJoin(condition=[true], joinType=[inner]) + LogicalTableScan(table=[[CATALOG, SALES, EMP]]) + LogicalTableScan(table=[[CATALOG, SALES, DEPT]]) + LogicalTableScan(table=[[CATALOG, SALES, BONUS]]) +]]> + </Resource> + <Resource name="planAfter"> + <![CDATA[ +LogicalProject(ENAME=[$1]) + LogicalJoin(condition=[AND(=($1, $11), =($10, $12))], joinType=[inner], semiJoinDone=[true]) + LogicalJoin(condition=[AND(=($1, $11), =($10, $12))], joinType=[semi]) + LogicalJoin(condition=[=($7, $9)], joinType=[inner], semiJoinDone=[true]) + LogicalJoin(condition=[=($7, $9)], joinType=[semi]) + LogicalTableScan(table=[[CATALOG, SALES, EMP]]) + LogicalTableScan(table=[[CATALOG, SALES, DEPT]]) + LogicalTableScan(table=[[CATALOG, SALES, DEPT]]) + LogicalTableScan(table=[[CATALOG, SALES, BONUS]]) + LogicalTableScan(table=[[CATALOG, SALES, BONUS]]) ]]> </Resource> </TestCase>