From 36d1f39620ac7c6cde7ee3a084a2658e4f5e851d Mon Sep 17 00:00:00 2001 From: iwanttobepowerful <745778074@qq.com> Date: Sat, 6 Jun 2026 09:50:36 +0800 Subject: [PATCH] Add test case for CALCITE-709 --- .../calcite/sql2rel/RelDecorrelatorTest.java | 68 +++++++++++++++++++ core/src/test/resources/sql/sub-query.iq | 12 ++++ 2 files changed, 80 insertions(+) diff --git a/core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java b/core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java index 245b248157b..240f9a36ae0 100644 --- a/core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java +++ b/core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java @@ -1429,6 +1429,74 @@ public static Frameworks.ConfigBuilder config() { assertThat(after, hasTree(planAfter)); } + /** + * Test case for + * [CALCITE-709] + * LIMIT inside scalar sub-query. + */ + @Test void test709() { + final FrameworkConfig frameworkConfig = config().build(); + final RelBuilder builder = RelBuilder.create(frameworkConfig); + final RelOptCluster cluster = builder.getCluster(); + final Planner planner = Frameworks.getPlanner(frameworkConfig); + final String sql = "" + + "SELECT E1.DEPTNO\n" + + "FROM EMP E1\n" + + "WHERE E1.SAL > (SELECT B1.COMM FROM BONUS B1 WHERE E1.ENAME = B1.ENAME LIMIT 2)"; + final RelNode originalRel; + try { + final SqlNode parse = planner.parse(sql); + final SqlNode validate = planner.validate(parse); + originalRel = planner.rel(validate).rel; + } catch (Exception e) { + throw TestUtil.rethrow(e); + } + + final HepProgram hepProgram = HepProgram.builder() + .addRuleCollection( + ImmutableList.of( + // SubQuery program rules + CoreRules.FILTER_SUB_QUERY_TO_CORRELATE, + CoreRules.PROJECT_SUB_QUERY_TO_CORRELATE, + CoreRules.JOIN_SUB_QUERY_TO_CORRELATE)) + .build(); + final Program program = + Programs.of(hepProgram, true, + requireNonNull(cluster.getMetadataProvider())); + final RelNode before = + program.run(cluster.getPlanner(), originalRel, cluster.traitSet(), + Collections.emptyList(), Collections.emptyList()); + final String planBefore = "" + + "LogicalProject(DEPTNO=[$7])\n" + + " LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7])\n" + + " LogicalFilter(condition=[>($5, $8)])\n" + + " LogicalCorrelate(correlation=[$cor0], joinType=[left], requiredColumns=[{1}])\n" + + " LogicalTableScan(table=[[scott, EMP]])\n" + + " LogicalAggregate(group=[{}], agg#0=[SINGLE_VALUE($0)])\n" + + " LogicalSort(fetch=[2])\n" + + " LogicalProject(COMM=[$3])\n" + + " LogicalFilter(condition=[=($cor0.ENAME, $0)])\n" + + " LogicalTableScan(table=[[scott, BONUS]])\n"; + assertThat(before, hasTree(planBefore)); + + // Decorrelate without any rules, just "purely" decorrelation algorithm on RelDecorrelator + final RelNode after = + RelDecorrelator.decorrelateQuery(before, builder, RuleSets.ofList(Collections.emptyList()), + RuleSets.ofList(Collections.emptyList())); + // Verify plan + final String planAfter = "" + + "LogicalProject(DEPTNO=[$7])\n" + + " LogicalJoin(condition=[AND(=($1, $8), >($5, $9))], joinType=[inner])\n" + + " LogicalTableScan(table=[[scott, EMP]])\n" + + " LogicalAggregate(group=[{0}], agg#0=[SINGLE_VALUE($1)])\n" + + " LogicalProject(ENAME=[$1], COMM=[$0])\n" + + " LogicalFilter(condition=[<=($2, 2)])\n" + + " LogicalProject(COMM=[$3], ENAME=[$0], rn=[ROW_NUMBER() OVER (PARTITION BY $0)])\n" + + " LogicalFilter(condition=[IS NOT NULL($0)])\n" + + " LogicalTableScan(table=[[scott, BONUS]])\n"; + assertThat(after, hasTree(planAfter)); + } + /** Test case for [CALCITE-7257] * Subqueries cannot be decorrelated if join condition contains RexFieldAccess. */ @Test void testJoinConditionContainsRexFieldAccess() { diff --git a/core/src/test/resources/sql/sub-query.iq b/core/src/test/resources/sql/sub-query.iq index 7c7e9fb0535..b4b8806aeba 100644 --- a/core/src/test/resources/sql/sub-query.iq +++ b/core/src/test/resources/sql/sub-query.iq @@ -9404,5 +9404,17 @@ INNER JOIN emp e +--------+------------+----------+-------+--------+----------+------+------------+---------+---------+---------+ (8 rows) +!ok + +# [CALCITE-709] LIMIT inside scalar sub-query +SELECT E1.DEPTNO +FROM EMP E1 +WHERE E1.SAL > (SELECT B1.COMM FROM BONUS B1 WHERE E1.ENAME = B1.ENAME LIMIT 2); ++--------+ +| DEPTNO | ++--------+ ++--------+ +(0 rows) + !ok # End sub-query.iq