This is an automated email from the ASF dual-hosted git repository.
xiong 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 583e73f1a3 [CALCITE-7373] FILTER_INTO_JOIN should not push Filter into
a join when the Filter contains non-deterministic function
583e73f1a3 is described below
commit 583e73f1a3723ba47d3f9a0cdbaefbe8c83c9dea
Author: Xiong Duan <[email protected]>
AuthorDate: Thu Jan 15 19:24:32 2026 +0800
[CALCITE-7373] FILTER_INTO_JOIN should not push Filter into a join when the
Filter contains non-deterministic function
---
.../apache/calcite/rel/rules/FilterJoinRule.java | 9 +++++
.../org/apache/calcite/test/RelOptRulesTest.java | 27 +++++++++++++
.../org/apache/calcite/test/RelOptRulesTest.xml | 44 ++++++++++++++++++++++
3 files changed, 80 insertions(+)
diff --git
a/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java
b/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java
index 1f80126aa8..9c6b560bfa 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java
@@ -72,6 +72,15 @@ protected FilterJoinRule(C config) {
protected void perform(RelOptRuleCall call, @Nullable Filter filter,
Join join) {
+ // Skip non-deterministic filter condition
+ if (filter != null && !RexUtil.isDeterministic(filter.getCondition())) {
+ return;
+ }
+ // Skip non-deterministic join condition
+ if (!RexUtil.isDeterministic(join.getCondition())) {
+ return;
+ }
+
List<RexNode> joinFilters =
RelOptUtil.conjunctions(join.getCondition());
final List<RexNode> origJoinFilters = ImmutableList.copyOf(joinFilters);
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 899635c38c..f46030dd8b 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -2187,6 +2187,33 @@ private void
checkSemiOrAntiJoinProjectTranspose(JoinRelType type) {
assertTrue(join.getHints().contains(noHashJoinHint));
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-7373">[CALCITE-7373]
+ * FILTER_INTO_JOIN should not push Filter into a join
+ * when the Filter contains non-deterministic function</a>. */
+ @Test void testPushFilterThroughJoinWithNonDeterministic() {
+ final String sql = "select * from (\n"
+ + " select * from dept inner join\n"
+ + " emp on dept.deptno = emp.deptno) R\n"
+ + "where 0.9 <= rand()";
+ sql(sql)
+ .withRule(CoreRules.FILTER_PROJECT_TRANSPOSE,
+ CoreRules.FILTER_INTO_JOIN,
+ CoreRules.JOIN_CONDITION_PUSH)
+ .check();
+ }
+
+ @Test void testPushJoinConditionWithNonDeterministic() {
+ final String sql = "select * from (\n"
+ + " select * from dept inner join\n"
+ + " emp on dept.deptno = emp.deptno and emp.mgr <= rand()) R\n";
+ sql(sql)
+ .withRule(CoreRules.FILTER_PROJECT_TRANSPOSE,
+ CoreRules.FILTER_INTO_JOIN,
+ CoreRules.JOIN_CONDITION_PUSH)
+ .checkUnchanged();
+ }
+
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-438">[CALCITE-438]
* Push predicates through SemiJoin</a>. */
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 193b7c87db..c7c39f82a8 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -14228,6 +14228,34 @@ LogicalProject(DNAME=[$1])
LogicalTableScan(table=[[scott, DEPT]])
LogicalFilter(condition=[=($5, 100)])
LogicalTableScan(table=[[scott, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testPushFilterThroughJoinWithNonDeterministic">
+ <Resource name="sql">
+ <![CDATA[select * from (
+ select * from dept inner join
+ emp on dept.deptno = emp.deptno) R
+where 0.9 <= rand()]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(DEPTNO=[$0], NAME=[$1], EMPNO=[$2], ENAME=[$3], JOB=[$4],
MGR=[$5], HIREDATE=[$6], SAL=[$7], COMM=[$8], DEPTNO0=[$9], SLACKER=[$10])
+ LogicalFilter(condition=[<=(CAST(0.9:DECIMAL(2, 1)):DOUBLE NOT NULL,
RAND())])
+ LogicalProject(DEPTNO=[$0], NAME=[$1], EMPNO=[$2], ENAME=[$3], JOB=[$4],
MGR=[$5], HIREDATE=[$6], SAL=[$7], COMM=[$8], DEPTNO0=[$9], SLACKER=[$10])
+ LogicalJoin(condition=[=($0, $9)], joinType=[inner])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(DEPTNO=[$0], NAME=[$1], EMPNO=[$2], ENAME=[$3], JOB=[$4],
MGR=[$5], HIREDATE=[$6], SAL=[$7], COMM=[$8], DEPTNO0=[$9], SLACKER=[$10])
+ LogicalProject(DEPTNO=[$0], NAME=[$1], EMPNO=[$2], ENAME=[$3], JOB=[$4],
MGR=[$5], HIREDATE=[$6], SAL=[$7], COMM=[$8], DEPTNO0=[$9], SLACKER=[$10])
+ LogicalFilter(condition=[<=(CAST(0.9:DECIMAL(2, 1)):DOUBLE NOT NULL,
RAND())])
+ LogicalJoin(condition=[=($0, $9)], joinType=[inner])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
</TestCase>
@@ -14337,6 +14365,22 @@ LogicalProject(DEPTNO=[$0], DEPTNO0=[$9])
LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3],
HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], $f9=[*($7, 2)])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testPushJoinConditionWithNonDeterministic">
+ <Resource name="sql">
+ <![CDATA[select * from (
+ select * from dept inner join
+ emp on dept.deptno = emp.deptno and emp.mgr <= rand()) R
+]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(DEPTNO=[$0], NAME=[$1], EMPNO=[$2], ENAME=[$3], JOB=[$4],
MGR=[$5], HIREDATE=[$6], SAL=[$7], COMM=[$8], DEPTNO0=[$9], SLACKER=[$10])
+ LogicalJoin(condition=[AND(=($0, $9), <=(CAST($5):DOUBLE, RAND()))],
joinType=[inner])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
</TestCase>