[ https://issues.apache.org/jira/browse/CALCITE-4054?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Ruben Q L resolved CALCITE-4054. -------------------------------- Resolution: Fixed > RepeatUnion containing a Correlate with a transientScan on its RHS causes NPE > ----------------------------------------------------------------------------- > > Key: CALCITE-4054 > URL: https://issues.apache.org/jira/browse/CALCITE-4054 > Project: Calcite > Issue Type: Bug > Components: core > Affects Versions: 1.23.0 > Reporter: Ruben Q L > Assignee: Ruben Q L > Priority: Minor > Labels: pull-request-available > Fix For: 1.30.0 > > > This is not a very urgent problem, since this is an exception on an operator > tagged as "Experimental", and several "not-so-usual" circumstances must occur > in order to reach the NPE: > - We need a "recursive plan", i.e. a RepeatUnion with a TransientScan (see > examples in EnumerableRepeatUnionTest.java). > - Inside the repeat union, there must be a Correlate, or a Join that gets > implemented as a Correlate due to JoinToCorrelateRule. > - We have the TransientScan (i.e. the scan on the transient table) on the > right-hand-side of the correlate/join. Normally, transientScan always appear > on the LHS of a correlate/join (because when the plan is built they need the > top of the RelBuilder's stack to get its rowType); however, it can get on the > RHS due to JoinCommuteRule. > We can force these conditions and reproduce the issue with the following unit > test (in EnumerableRepeatUnionTest.java): > {code:java} > @Test void testRepeatUnionWithCorrelateWithTransientScanOnItsRight() { > CalciteAssert.that() > .with(CalciteConnectionProperty.LEX, Lex.JAVA) > .with(CalciteConnectionProperty.FORCE_DECORRELATE, false) > .withSchema("s", new ReflectiveSchema(new HierarchySchema())) > .query("?") > .withHook(Hook.PLANNER, (Consumer<RelOptPlanner>) planner -> { > planner.addRule(JoinToCorrelateRule.INSTANCE); > planner.removeRule(JoinCommuteRule.INSTANCE); > planner.removeRule(EnumerableRules.ENUMERABLE_MERGE_JOIN_RULE); > planner.removeRule(EnumerableRules.ENUMERABLE_JOIN_RULE); > }) > .withRel(builder -> { > builder > // WITH RECURSIVE delta(empid, name) as ( > // SELECT empid, name FROM emps WHERE empid = 2 > // UNION ALL > // SELECT e.empid, e.name FROM delta d > // JOIN hierarchies h ON d.empid = > h.managerid > // JOIN emps e ON > h.subordinateid = e.empid > // ) > // SELECT empid, name FROM delta > .scan("s", "emps") > .filter( > builder.equals( > builder.field("empid"), > builder.literal(2))) > .project( > builder.field("emps", "empid"), > builder.field("emps", "name")) > .transientScan("#DELTA#"); > RelNode transientScan = builder.build(); // pop the transientScan > to use it later > builder > .scan("s", "hierarchies") > .push(transientScan) // use the transientScan as right input of > the join > .join( > JoinRelType.INNER, > builder.equals( > builder.field(2, "#DELTA#", "empid"), > builder.field(2, "hierarchies", "managerid"))) > .scan("s", "emps") > .join( > JoinRelType.INNER, > builder.equals( > builder.field(2, "hierarchies", "subordinateid"), > builder.field(2, "emps", "empid"))) > .project( > builder.field("emps", "empid"), > builder.field("emps", "name")) > .repeatUnion("#DELTA#", true); > return builder.build(); > }) > .explainHookMatches("" > + "EnumerableRepeatUnion(all=[true])\n" > + " EnumerableTableSpool(readType=[LAZY], writeType=[LAZY], > table=[[#DELTA#]])\n" > + " EnumerableCalc(expr#0..4=[{inputs}], expr#5=[2], > expr#6=[=($t0, $t5)], empid=[$t0], name=[$t2], $condition=[$t6])\n" > + " EnumerableTableScan(table=[[s, emps]])\n" > + " EnumerableTableSpool(readType=[LAZY], writeType=[LAZY], > table=[[#DELTA#]])\n" > + " EnumerableCalc(expr#0..8=[{inputs}], empid=[$t4], > name=[$t6])\n" > + " EnumerableCorrelate(correlation=[$cor1], > joinType=[inner], requiredColumns=[{1}])\n" > // It is important to have EnumerableCorrelate + #DELTA# table > scan on its right > + " EnumerableCorrelate(correlation=[$cor0], > joinType=[inner], requiredColumns=[{0}])\n" > + " EnumerableTableScan(table=[[s, hierarchies]])\n" > + " EnumerableCalc(expr#0..1=[{inputs}], expr#2=[$cor0], > expr#3=[$t2.managerid], expr#4=[=($t0, $t3)], proj#0..1=[{exprs}], > $condition=[$t4])\n" > + " EnumerableInterpreter\n" > + " BindableTableScan(table=[[#DELTA#]])\n" > + " EnumerableCalc(expr#0..4=[{inputs}], expr#5=[$cor1], > expr#6=[$t5.subordinateid], expr#7=[=($t6, $t0)], proj#0..4=[{exprs}], > $condition=[$t7])\n" > + " EnumerableTableScan(table=[[s, emps]])\n") > .returnsUnordered("" > + "empid=2; name=Emp2\n" > + "empid=3; name=Emp3\n" > + "empid=5; name=Emp5"); > } > {code} > Which fails with NPE on the dynamic generated code: > {code} > Error while executing SQL "?": null > java.sql.SQLException: Error while executing SQL "?": null > at org.apache.calcite.avatica.Helper.createException(Helper.java:56) > at org.apache.calcite.avatica.Helper.createException(Helper.java:41) > at > org.apache.calcite.avatica.AvaticaStatement.executeInternal(AvaticaStatement.java:163) > at > org.apache.calcite.avatica.AvaticaStatement.executeQuery(AvaticaStatement.java:227) > at > org.apache.calcite.test.CalciteAssert.assertQuery(CalciteAssert.java:532) > at > org.apache.calcite.test.CalciteAssert$AssertQuery.lambda$returns$1(CalciteAssert.java:1511) > at > org.apache.calcite.test.CalciteAssert$AssertQuery.withConnection(CalciteAssert.java:1450) > at > org.apache.calcite.test.CalciteAssert$AssertQuery.returns(CalciteAssert.java:1509) > at > org.apache.calcite.test.CalciteAssert$AssertQuery.returns(CalciteAssert.java:1499) > at > org.apache.calcite.test.CalciteAssert$AssertQuery.returnsUnordered(CalciteAssert.java:1521) > at > org.apache.calcite.test.enumerable.EnumerableRepeatUnionTest.testRepeatUnionWithCorrelateWithTransientScanOnItsRight(EnumerableRepeatUnionTest.java:296) > at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) > at > sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) > at > sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) > at java.lang.reflect.Method.invoke(Method.java:498) > at > org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675) > at > org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) > at > org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125) > at > org.junit.jupiter.engine.extension.TimeoutInvocation.proceed(TimeoutInvocation.java:46) > at > org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:139) > at > org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:131) > at > org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:81) > at > org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115) > at > org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105) > at > org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:104) > at > org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62) > at > org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43) > at > org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35) > at > org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104) > at > org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98) > at > org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:202) > at > org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) > at > org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198) > at > org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135) > at > org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135) > at > org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) > at > org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) > at > org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) > at > org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:171) > at > org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:115) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) > at > org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) > at > org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) > at > org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) > at > org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:171) > at > org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:115) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) > at > org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) > at > org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) > at > org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) > at > org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) > at > org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:171) > at java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:189) > at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) > at > java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056) > at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692) > at > java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157) > Caused by: java.lang.NullPointerException > at Baz.bind(Unknown Source) > at > org.apache.calcite.jdbc.CalcitePrepare$CalciteSignature.enumerable(CalcitePrepare.java:355) > at > org.apache.calcite.jdbc.CalciteConnectionImpl.enumerable(CalciteConnectionImpl.java:315) > at > org.apache.calcite.jdbc.CalciteMetaImpl._createIterable(CalciteMetaImpl.java:507) > at > org.apache.calcite.jdbc.CalciteMetaImpl.createIterable(CalciteMetaImpl.java:498) > at > org.apache.calcite.avatica.AvaticaResultSet.execute(AvaticaResultSet.java:182) > at > org.apache.calcite.jdbc.CalciteResultSet.execute(CalciteResultSet.java:64) > at > org.apache.calcite.jdbc.CalciteResultSet.execute(CalciteResultSet.java:43) > at > org.apache.calcite.avatica.AvaticaConnection$1.execute(AvaticaConnection.java:667) > at > org.apache.calcite.jdbc.CalciteMetaImpl.prepareAndExecute(CalciteMetaImpl.java:567) > at > org.apache.calcite.avatica.AvaticaConnection.prepareAndExecuteInternal(AvaticaConnection.java:675) > at > org.apache.calcite.avatica.AvaticaStatement.executeInternal(AvaticaStatement.java:156) > ... 66 more > {code} -- This message was sent by Atlassian Jira (v8.20.1#820001)