[
https://issues.apache.org/jira/browse/CALCITE-7854?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Vladimir Sitnikov updated CALCITE-7854:
---------------------------------------
Affects Version/s: 1.42.0
> FilterProjectTransposeRule throws NullPointerException when the Filter has a
> distribution trait and the Project's input has none
> --------------------------------------------------------------------------------------------------------------------------------
>
> Key: CALCITE-7854
> URL: https://issues.apache.org/jira/browse/CALCITE-7854
> Project: Calcite
> Issue Type: Bug
> Components: core
> Affects Versions: 1.42.0
> Reporter: Vladimir Sitnikov
> Priority: Major
>
> Applying {{FilterProjectTransposeRule}} (for example
> {{CoreRules.FILTER_PROJECT_TRANSPOSE}}, which copies the Filter) throws
> {{NullPointerException}} when the Filter's trait set contains a distribution
> trait and the trait set of the Project's input does not. Trait sets differ in
> this way when the planner does not register {{RelDistributionTraitDef}} and a
> distribution is added to the Filter with {{RelTraitSet#plus}}, as a
> user-defined rule or Filter subclass can do.
> Reproducer (for {{RelOptRulesTest}}; {{RelBuilderTest.config()}} uses a
> planner that does not register {{RelDistributionTraitDef}}):
> {code:java}
> @Test void testFilterProjectTransposeRuleInputWithoutDistribution() {
> final RelBuilder b = RelBuilder.create(RelBuilderTest.config().build());
> final Filter filter = (Filter) b
> .scan("EMP")
> .project(b.field("SAL"))
> .filter(b.equals(b.field(0), b.literal(11500)))
> .build();
> final RelNode in =
> filter.copy(filter.getTraitSet().plus(RelDistributions.SINGLETON),
> filter.getInput(), filter.getCondition());
> final HepProgram program = new HepProgramBuilder()
> .addRuleInstance(CoreRules.FILTER_PROJECT_TRANSPOSE)
> .build();
> final HepPlanner hepPlanner = new HepPlanner(program);
> hepPlanner.setRoot(in);
> final RelNode result = hepPlanner.findBestExp();
> final RelNode pushedFilter = result.getInput(0);
> assertThat(
> pushedFilter.getTraitSet().getTrait(RelDistributionTraitDef.INSTANCE),
> is(RelDistributions.ANY));
> }
> {code}
> Actual result on main (commit 8f27e24a02; 1.42.0 has the same code):
> {noformat}
> java.lang.NullPointerException: Cannot invoke
> "org.apache.calcite.plan.RelTrait.getClass()" because "trait" is null
> at org.apache.calcite.plan.RelTraitDef.canonize(RelTraitDef.java:105)
> at org.apache.calcite.plan.RelCompositeTrait.of(RelCompositeTrait.java:64)
> at org.apache.calcite.plan.RelTraitSet.replaceIfs(RelTraitSet.java:264)
> at
> org.apache.calcite.rel.rules.FilterProjectTransposeRule.onMatch(FilterProjectTransposeRule.java:188)
> at
> org.apache.calcite.plan.AbstractRelOptPlanner.fireRule(AbstractRelOptPlanner.java:380)
> at org.apache.calcite.plan.hep.HepPlanner.applyRule(HepPlanner.java:697)
> {noformat}
> Expected result: the rule pushes the Filter below the Project, and the pushed
> Filter has the default distribution, {{RelDistributions.ANY}}. That is what
> {{RelTraitSet#replaceIf}} and {{RelTraitSet#replaceIfs}} produce when their
> supplier returns null, and the rule already relies on that fallback for the
> collation trait.
> Cause: when {{copyFilter}} is true, the rule copies the input's distribution
> with {{replaceIfs(RelDistributionTraitDef.INSTANCE, () ->
> Collections.singletonList(input.getTraitSet().getTrait(RelDistributionTraitDef.INSTANCE)))}}.
> {{getTrait}} returns null when the input's trait set has no distribution
> trait, so the supplier returns a list that holds null rather than null
> itself, and {{RelCompositeTrait.of}} fails on the null element. Calling
> {{replaceIf}}, which falls back to the default when the supplier returns
> null, fixes it.
> Found while reviewing CALCITE-7736 ([PR
> #5213|https://github.com/apache/calcite/pull/5213]), which carries this fix
> inside the nullness migration.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)