This is an automated email from the ASF dual-hosted git repository. alexpl pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push: new ecd554c1d89 IGNITE-18186 SQL Calcite: Push not correlated filter part to the table scan - Fixes #10383. ecd554c1d89 is described below commit ecd554c1d89957280ccd56dd68da742851016427 Author: Aleksey Plekhanov <plehanov.a...@gmail.com> AuthorDate: Fri Nov 18 13:14:38 2022 +0300 IGNITE-18186 SQL Calcite: Push not correlated filter part to the table scan - Fixes #10383. Signed-off-by: Aleksey Plekhanov <plehanov.a...@gmail.com> --- .../calcite/rule/logical/FilterScanMergeRule.java | 50 +++++++++++++++++++--- .../calcite/planner/HashIndexSpoolPlannerTest.java | 18 ++++++++ 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/FilterScanMergeRule.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/FilterScanMergeRule.java index 76459398d86..7254435b7ed 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/FilterScanMergeRule.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/FilterScanMergeRule.java @@ -16,10 +16,13 @@ */ package org.apache.ignite.internal.processors.query.calcite.rule.logical; +import java.util.ArrayList; +import java.util.List; import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.plan.RelOptPredicateList; import org.apache.calcite.plan.RelOptRule; import org.apache.calcite.plan.RelOptRuleCall; +import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.plan.RelRule; import org.apache.calcite.plan.RelTraitSet; import org.apache.calcite.rel.RelNode; @@ -71,8 +74,32 @@ public abstract class FilterScanMergeRule<T extends ProjectableFilterableTableSc RelOptCluster cluster = scan.getCluster(); RexBuilder builder = RexUtils.builder(cluster); + RexNode remainCondition = null; RexNode condition = filter.getCondition(); + if (config.isSkipCorrelated() && RexUtils.hasCorrelation(condition)) { + RexNode cnf = RexUtil.toCnf(builder, condition); + List<RexNode> conjunctions = RelOptUtil.conjunctions(cnf); + + List<RexNode> correlated = new ArrayList<>(); + List<RexNode> notCorrelated = new ArrayList<>(); + + for (RexNode node : conjunctions) { + if (RexUtils.hasCorrelation(node)) + correlated.add(node); + else + notCorrelated.add(node); + } + + if (notCorrelated.isEmpty()) + return; + + if (!correlated.isEmpty()) { + remainCondition = RexUtil.composeConjunction(builder, correlated); + condition = RexUtil.composeConjunction(builder, notCorrelated); + } + } + if (scan.projects() != null) { RexShuttle shuttle = new RexShuttle() { @Override public RexNode visitInputRef(RexInputRef ref) { @@ -102,6 +129,9 @@ public abstract class FilterScanMergeRule<T extends ProjectableFilterableTableSc if (res == null) return; + if (remainCondition != null) + res = call.builder().push(res).filter(remainCondition).build(); + call.transformTo(res); } @@ -163,28 +193,36 @@ public abstract class FilterScanMergeRule<T extends ProjectableFilterableTableSc /** */ Config TABLE_SCAN = DEFAULT - .withScanRuleConfig(IgniteLogicalTableScan.class, "FilterTableScanMergeRule", false); + .withScanRuleConfig(IgniteLogicalTableScan.class, "FilterTableScanMergeRule"); /** */ Config TABLE_SCAN_SKIP_CORRELATED = DEFAULT - .withScanRuleConfig(IgniteLogicalTableScan.class, "FilterTableScanMergeSkipCorrelatedRule", true); + .withScanRuleConfig(IgniteLogicalTableScan.class, "FilterTableScanMergeSkipCorrelatedRule") + .withSkipCorrelated(true); /** */ Config INDEX_SCAN = DEFAULT .withRuleFactory(FilterIndexScanMergeRule::new) - .withScanRuleConfig(IgniteLogicalIndexScan.class, "FilterIndexScanMergeRule", false); + .withScanRuleConfig(IgniteLogicalIndexScan.class, "FilterIndexScanMergeRule"); /** */ default Config withScanRuleConfig( Class<? extends ProjectableFilterableTableScan> scanCls, - String desc, - boolean skipCorrelated + String desc ) { return withDescription(desc) .withOperandSupplier(b -> b.operand(LogicalFilter.class) - .predicate(p -> !skipCorrelated || !RexUtils.hasCorrelation(p.getCondition())) .oneInput(b1 -> b1.operand(scanCls).noInputs())) .as(Config.class); } + + /** Whether to split correlated and not correlated conditions and do not push correlated into scan. */ + @Value.Default + default boolean isSkipCorrelated() { + return false; + } + + /** Sets {@link #isSkipCorrelated()}. */ + Config withSkipCorrelated(boolean skipCorrelated); } } diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/HashIndexSpoolPlannerTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/HashIndexSpoolPlannerTest.java index 49cb57cd692..b0b0ccb447c 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/HashIndexSpoolPlannerTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/HashIndexSpoolPlannerTest.java @@ -24,6 +24,7 @@ import org.apache.calcite.rex.RexFieldAccess; import org.apache.calcite.rex.RexNode; import org.apache.ignite.internal.processors.query.calcite.rel.IgniteHashIndexSpool; import org.apache.ignite.internal.processors.query.calcite.rel.IgniteRel; +import org.apache.ignite.internal.processors.query.calcite.rel.IgniteTableScan; import org.apache.ignite.internal.processors.query.calcite.schema.IgniteSchema; import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistribution; import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions; @@ -227,4 +228,21 @@ public class HashIndexSpoolPlannerTest extends AbstractPlannerTest { assertTrue(searchRow.get(1) instanceof RexFieldAccess); assertNull(searchRow.get(2)); } + + /** + * Test that hash spool can be used with not correlated condition (condition is pushed below the spool). + */ + @Test + public void testCorrelatedFilterSplit() throws Exception { + TestTable tbl = createTable("TBL", IgniteDistributions.random(), "ID", Integer.class); + IgniteSchema publicSchema = createSchema(tbl); + + String sql = "SELECT (SELECT id FROM tbl AS t2 WHERE t2.id < 50 AND t2.id = t1.id) FROM tbl AS t1"; + + assertPlan(sql, publicSchema, + hasChildThat(isInstanceOf(IgniteHashIndexSpool.class) + .and(s -> "=($0, $cor0.ID)".equals(s.condition().toString())) + .and(hasChildThat(isInstanceOf(IgniteTableScan.class) + .and(t -> "<($t0, 50)".equals(t.condition().toString())))))); + } }