From 0b74668294249ea34b58aae6c44579daa9181192 Mon Sep 17 00:00:00 2001 From: shikhae6 Date: Tue, 26 May 2026 00:09:33 +0530 Subject: [PATCH] [CALCITE-7549] Add Programs.of overload that accepts RelOptListener --- .../org/apache/calcite/tools/Programs.java | 38 ++++++++++++++ .../apache/calcite/test/HepPlannerTest.java | 52 +++++++++++++++++++ .../apache/calcite/test/HepPlannerTest.xml | 10 ++++ 3 files changed, 100 insertions(+) diff --git a/core/src/main/java/org/apache/calcite/tools/Programs.java b/core/src/main/java/org/apache/calcite/tools/Programs.java index 83f6834d2850..4d795d554902 100644 --- a/core/src/main/java/org/apache/calcite/tools/Programs.java +++ b/core/src/main/java/org/apache/calcite/tools/Programs.java @@ -21,6 +21,7 @@ import org.apache.calcite.config.CalciteSystemProperty; import org.apache.calcite.plan.RelOptCostImpl; import org.apache.calcite.plan.RelOptLattice; +import org.apache.calcite.plan.RelOptListener; import org.apache.calcite.plan.RelOptMaterialization; import org.apache.calcite.plan.RelOptPlanner; import org.apache.calcite.plan.RelOptRule; @@ -53,6 +54,8 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Arrays; import java.util.List; import java.util.function.Predicate; @@ -190,6 +193,41 @@ public static Program of(final HepProgram hepProgram, final boolean noDag, return hepPlanner.findBestExp(); }; } + /** Creates a program that executes a {@link HepProgram}. + * + *

If {@code listener} is not null, it is + * {@linkplain HepPlanner#addListener(RelOptListener) attached} + * to the planner before execution. */ + @SuppressWarnings("deprecation") + public static Program of(final HepProgram hepProgram, final boolean noDag, + final RelMetadataProvider metadataProvider, + final @Nullable RelOptListener listener) { + requireNonNull(metadataProvider, "metadataProvider"); + if (listener == null) { + return of(hepProgram, noDag, metadataProvider); + } + return (planner, rel, requiredOutputTraits, materializations, lattices) -> { + final HepPlanner hepPlanner = + new HepPlanner(hepProgram, null, noDag, null, RelOptCostImpl.FACTORY); + + hepPlanner.addListener(listener); + + List list = Lists.newArrayList(metadataProvider); + hepPlanner.registerMetadataProviders(list); + for (RelOptMaterialization materialization : materializations) { + hepPlanner.addMaterialization(materialization); + } + for (RelOptLattice lattice : lattices) { + hepPlanner.addLattice(lattice); + } + RelMetadataProvider plannerChain = + ChainedRelMetadataProvider.of(list); + rel.getCluster().setMetadataProvider(plannerChain); + + hepPlanner.setRoot(rel); + return hepPlanner.findBestExp(); + }; + } /** Creates a program that invokes heuristic join-order optimization * (via {@link org.apache.calcite.rel.rules.JoinToMultiJoinRule}, diff --git a/core/src/test/java/org/apache/calcite/test/HepPlannerTest.java b/core/src/test/java/org/apache/calcite/test/HepPlannerTest.java index 1729817e368d..696364dfa320 100644 --- a/core/src/test/java/org/apache/calcite/test/HepPlannerTest.java +++ b/core/src/test/java/org/apache/calcite/test/HepPlannerTest.java @@ -29,10 +29,14 @@ import org.apache.calcite.rel.logical.LogicalIntersect; import org.apache.calcite.rel.logical.LogicalUnion; import org.apache.calcite.rel.logical.LogicalValues; +import org.apache.calcite.rel.metadata.DefaultRelMetadataProvider; +import org.apache.calcite.rel.metadata.RelMetadataProvider; import org.apache.calcite.rel.rules.CoerceInputsRule; import org.apache.calcite.rel.rules.CoreRules; import org.apache.calcite.sql.SqlExplainLevel; import org.apache.calcite.sql.fun.SqlStdOperatorTable; +import org.apache.calcite.tools.Program; +import org.apache.calcite.tools.Programs; import org.apache.calcite.tools.RelBuilder; import com.google.common.collect.ImmutableList; @@ -563,4 +567,52 @@ long getApplyTimes() { final RelNode result = planner.findBestExp(); assertThat(result, is(instanceOf(LogicalValues.class))); } + + /** Test case for + * [CALCITE-7549] + * Add Programs.of overload accepting RelOptListener. + * + *

Verifies that a {@link RelOptListener} passed to + * {@link Programs#of(HepProgram, boolean, RelMetadataProvider, RelOptListener)} + * receives rule-attempted events during execution. */ + @Test void testProgramsOfWithListener() { + final HepTestListener listener = new HepTestListener(0); + + final HepProgram program = new HepProgramBuilder() + .addRuleInstance(CoreRules.FILTER_TO_CALC) + .build(); + + final Program p = + Programs.of(program, true, DefaultRelMetadataProvider.INSTANCE, listener); + + final String sql = "select 1 from dept where abs(-1) = 20"; + final RelNode rel = sql(sql).toRel(); + + final RelNode result = + p.run(rel.getCluster().getPlanner(), rel, rel.getTraitSet(), + ImmutableList.of(), ImmutableList.of()); + + // The listener must have been notified at least once + assertThat(listener.getApplyTimes() > 0, is(true)); + } + + /** Tests that {@link Programs#of(HepProgram, boolean, RelMetadataProvider, RelOptListener)} + * with a null listener behaves identically to the overload without a listener. */ + @Test void testProgramsOfWithNullListener() { + final HepProgram program = new HepProgramBuilder() + .addRuleInstance(CoreRules.FILTER_TO_CALC) + .build(); + + final Program p = + Programs.of(program, true, DefaultRelMetadataProvider.INSTANCE, null); + + final String sql = "select 1 from dept where abs(-1) = 20"; + final RelNode rel = sql(sql).toRel(); + + // Should not throw; null listener is safely ignored + final RelNode result = + p.run(rel.getCluster().getPlanner(), rel, rel.getTraitSet(), + ImmutableList.of(), ImmutableList.of()); + assertThat(result, is(notNullValue())); + } } diff --git a/core/src/test/resources/org/apache/calcite/test/HepPlannerTest.xml b/core/src/test/resources/org/apache/calcite/test/HepPlannerTest.xml index ce092dcedcb2..aa504480f32a 100644 --- a/core/src/test/resources/org/apache/calcite/test/HepPlannerTest.xml +++ b/core/src/test/resources/org/apache/calcite/test/HepPlannerTest.xml @@ -147,6 +147,16 @@ LogicalAggregate(group=[{0}]) + + + + + + + + + +