This is an automated email from the ASF dual-hosted git repository.
strongduanmu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new bd5bce8e592 Fix correlated project pushdown for SQL federation IN
subqueries (#38405)
bd5bce8e592 is described below
commit bd5bce8e5920e8ed393aa25113d9d036e9b18377
Author: ym0506 <[email protected]>
AuthorDate: Tue Jun 9 14:55:00 2026 +0900
Fix correlated project pushdown for SQL federation IN subqueries (#38405)
* sql-federation: skip correlated project pushdown
* test: add sql federation e2e regression case
* test: narrow sql federation e2e regression case
---
.../transformation/PushProjectIntoScanRule.java | 21 ++++++++++++++++++++-
.../compiler/it/SQLStatementCompilerIT.java | 11 +++++++++++
.../transformation/PushProjectIntoScanRuleTest.java | 11 +++++++++++
...l-select-sub-query-for-db-tbl-sql-federation.xml | 5 +++++
4 files changed, 47 insertions(+), 1 deletion(-)
diff --git
a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushProjectIntoScanRule.java
b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushProjectIntoScanRule.java
index 614c4fb20ea..482524ba1e0 100644
---
a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushProjectIntoScanRule.java
+++
b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushProjectIntoScanRule.java
@@ -23,6 +23,8 @@ import org.apache.calcite.plan.RelRule;
import org.apache.calcite.rel.logical.LogicalProject;
import org.apache.calcite.rel.rules.TransformationRule;
import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexCorrelVariable;
+import org.apache.calcite.rex.RexFieldAccess;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexSubQuery;
import
org.apache.shardingsphere.sqlfederation.compiler.rel.operator.logical.LogicalScan;
@@ -55,13 +57,30 @@ public final class PushProjectIntoScanRule extends
RelRule<PushProjectIntoScanRu
}
LogicalProject logicalProject = call.rel(0);
for (RexNode each : logicalProject.getProjects()) {
- if (each instanceof RexSubQuery || containsCastFunction(each)) {
+ if (each instanceof RexSubQuery || containsCorrelate(each) ||
containsCastFunction(each)) {
return false;
}
}
return true;
}
+ private boolean containsCorrelate(final RexNode rexNode) {
+ if (rexNode instanceof RexCorrelVariable) {
+ return true;
+ }
+ if (rexNode instanceof RexFieldAccess) {
+ return containsCorrelate(((RexFieldAccess)
rexNode).getReferenceExpr());
+ }
+ if (rexNode instanceof RexCall) {
+ for (RexNode each : ((RexCall) rexNode).getOperands()) {
+ if (containsCorrelate(each)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private boolean containsCastFunction(final RexNode rexNode) {
return rexNode instanceof RexCall &&
CASE_FUNCTION_NAME.equalsIgnoreCase(((RexCall)
rexNode).getOperator().getName());
}
diff --git
a/kernel/sql-federation/compiler/src/test/java/org/apache/shardingsphere/sqlfederation/compiler/compiler/it/SQLStatementCompilerIT.java
b/kernel/sql-federation/compiler/src/test/java/org/apache/shardingsphere/sqlfederation/compiler/compiler/it/SQLStatementCompilerIT.java
index 02d6a730d99..bfd4bc8212b 100644
---
a/kernel/sql-federation/compiler/src/test/java/org/apache/shardingsphere/sqlfederation/compiler/compiler/it/SQLStatementCompilerIT.java
+++
b/kernel/sql-federation/compiler/src/test/java/org/apache/shardingsphere/sqlfederation/compiler/compiler/it/SQLStatementCompilerIT.java
@@ -37,6 +37,7 @@ import
org.apache.shardingsphere.sqlfederation.compiler.metadata.schema.SQLFeder
import
org.apache.shardingsphere.sqlfederation.compiler.rel.converter.SQLFederationRelConverter;
import
org.apache.shardingsphere.sqlfederation.compiler.sql.function.mysql.MySQLOperatorTable;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@@ -54,7 +55,9 @@ import java.util.Properties;
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.mockito.Mockito.mock;
class SQLStatementCompilerIT {
@@ -241,6 +244,14 @@ class SQLStatementCompilerIT {
Collections.emptyList(), Collections.emptyList());
}
+ @Test
+ void assertCompileWhenCorrelatedInSubqueryProjectsOuterColumn() {
+ SQLStatement sqlStatement =
sqlParserRule.getSQLParserEngine(TypedSPILoader.getService(DatabaseType.class,
"MySQL"))
+ .parse("SELECT o.order_id FROM t_order o WHERE o.user_id IN
(SELECT o.user_id FROM t_order_item i)", false);
+ String actual = assertDoesNotThrow(() ->
sqlStatementCompiler.compile(sqlStatement,
"MySQL").getPhysicalPlan().explain().replaceAll(System.lineSeparator(), " "));
+ assertThat(actual,
containsString("EnumerableScan(table=[[federate_jdbc, t_order_item]],
sql=[SELECT * FROM `federate_jdbc`.`t_order_item`]"));
+ }
+
@ParameterizedTest(name = "{0}")
@ArgumentsSource(TestCaseArgumentsProvider.class)
void assertCompile(final TestCase testcase) {
diff --git
a/kernel/sql-federation/compiler/src/test/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushProjectIntoScanRuleTest.java
b/kernel/sql-federation/compiler/src/test/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushProjectIntoScanRuleTest.java
index f6b88fa152f..13ea5544ed2 100644
---
a/kernel/sql-federation/compiler/src/test/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushProjectIntoScanRuleTest.java
+++
b/kernel/sql-federation/compiler/src/test/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushProjectIntoScanRuleTest.java
@@ -21,6 +21,8 @@ import org.apache.calcite.plan.RelOptRuleCall;
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.rel.logical.LogicalProject;
import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexCorrelVariable;
+import org.apache.calcite.rex.RexFieldAccess;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexSubQuery;
import org.apache.calcite.sql.SqlOperator;
@@ -76,6 +78,15 @@ class PushProjectIntoScanRuleTest {
assertFalse(rule.matches(call));
}
+ @Test
+ void assertNotMatchWhenProjectContainsCorrelate() {
+ mockQualifiedName("public", "t_order");
+ RexFieldAccess rexFieldAccess = mock(RexFieldAccess.class);
+
when(rexFieldAccess.getReferenceExpr()).thenReturn(mock(RexCorrelVariable.class));
+
when(logicalProject.getProjects()).thenReturn(Collections.singletonList(rexFieldAccess));
+ assertFalse(rule.matches(call));
+ }
+
@Test
void assertNotMatchWhenProjectContainsCastFunction() {
mockQualifiedName("public", "t_order");
diff --git
a/test/e2e/sql/src/test/resources/cases/dql/cases/db_tbl_sql_federation/e2e-dql-select-sub-query-for-db-tbl-sql-federation.xml
b/test/e2e/sql/src/test/resources/cases/dql/cases/db_tbl_sql_federation/e2e-dql-select-sub-query-for-db-tbl-sql-federation.xml
index dfad979093d..6fe6f5320ea 100644
---
a/test/e2e/sql/src/test/resources/cases/dql/cases/db_tbl_sql_federation/e2e-dql-select-sub-query-for-db-tbl-sql-federation.xml
+++
b/test/e2e/sql/src/test/resources/cases/dql/cases/db_tbl_sql_federation/e2e-dql-select-sub-query-for-db-tbl-sql-federation.xml
@@ -25,6 +25,11 @@
<assertion parameters="1200:int"
expected-data-source-name="read_dataset" />
</test-case>-->
+ <test-case sql="SELECT o.order_id FROM (SELECT order_id, user_id FROM
t_order WHERE order_id = ?) o WHERE o.user_id IN (SELECT o.user_id FROM
t_order_item i) ORDER BY order_id"
+ db-types="MySQL,PostgreSQL,openGauss"
scenario-types="db_tbl_sql_federation">
+ <assertion parameters="1000:int"
expected-data-source-name="read_dataset" />
+ </test-case>
+
<test-case sql="SELECT * FROM t_order o WHERE o.order_id IN (SELECT
i.order_id FROM t_order_item i INNER JOIN t_product p ON i.product_id =
p.product_id WHERE p.product_id = ?) ORDER BY order_id"
db-types="MySQL,PostgreSQL,openGauss" scenario-types="db_tbl_sql_federation">
<assertion parameters="10:int"
expected-data-source-name="read_dataset" />
</test-case>