[ 
https://issues.apache.org/jira/browse/DERBY-3872?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12638190#action_12638190
 ] 

A B commented on DERBY-3872:
----------------------------

> Just wanted to summarize in brief my findings on this jira entry. 

Thanks for all of the *great* information on this one, Mamta!  I spent some 
time looking at this and your detailed comments were extremely useful in 
helping me get up to speed.

> if anyone has any pointers/ideas, I can definitely use them.

I did some tracing based on your earlier observation that "the ResultColumn 
associated with the HAVING clause is the same object that is associated with 
the join node".  When comparing a successful query with an unsuccessful one, I 
noticed that in *both* cases the HAVING clause includes a pointer to a 
ResultColumn that is also referenced by the JoinNode. So that in itself is not 
necessarily a problem.

However, it turns out that for the successful query, the ResultColumn reference 
in question is *buried* beneath a chain of VirtualColumn-to-ResultColumn nodes 
that exist on the right side of the equality (q3."EMPID" = q1."DEPTNO") in the 
HAVING subquery.  In the failing query, though, the ResultColumn reference is 
the immediate child of a ColumnReference that is the right side of the 
equality--there are no intervening VirtualColumn nodes nor any other 
ResultColumns.

I think what this means is that, in the failing query, when we set the result 
set number for the ResultColumns associated with the JoinNode, that result set 
number inadvertently gets picked up by the equality in the HAVING subquery, 
which ultimately leads to the NPE, as you discovered.  For the successful 
query, though, the intervening VirtualColumnNodes and ResultColumns have their 
*own* (correct) result set numbers, and those effectively "hide" the result set 
number that was set from the JoinNode.

That said, I did some tracing to try to find out where the intervening 
VirtualColumnNodes come from in the successful query.  It turns out that 
GroupByNode.addUnAggColumns() creates the nodes, and then it uses a Visitor 
implementation to "substitute" those nodes into the query tree at the right 
place(s).  For a successful query the Visitor performs the substitution in the 
base table, as it should; but for the failing query, the substitution never 
happens.  Upon further investigation I noticed that the failing query includes 
an IndexToBaseRowNode whose child is a FromBaseTable, while the successful 
query simply includes a FromBaseTable directly.  With that difference in mind I 
set a breakpoint in the "addUnAggColumns()" method at the line:

%    havingClause.accept(
%        (SubstituteExpressionVisitor)havingRefsToSubstitute.get(r));

and traced from there.  One thing I quickly noticed was that the 
IndexToBaseRowNode class does *not* define a "visit()" method, which means it 
defaults to the "visit()" method of ResultSetNode.  That's a problem because 
ResultSetNode does not know about the FromBaseTable child that exists beneath 
IndexToBaseRowNode--so ResultSetNode.visit() correctly visits the 
IndexToBaseRowNode itself, but does *NOT* visit the underlying FromBaseTable.  
As luck would have it, the substitution that the code in addUnAggColumns() is 
trying to make is intended for the FromBaseTable (and esp. for the 
"restriction" predicate that is contained within it), but since that node is 
never visited, the substitution never happens.

As an experiment I added a "visit()" method to IndexToBaseRowNode.java that was 
almost identical to the method as it exists today in 
SingleChildResultSetNode--I just changed "childResult" to "source" and that was 
it.  When I did that, I could see the substitution in addUnAggColumns() start 
to take effect for the failing query, which led to insertion of what appear to 
be the proper VirtualColumn nodes beneath the HAVING subquery's predicate, and 
in the end, the query ran to completion with no error.

I didn't do *any* other testing of any sort, nor have I looked at the possible 
side effects of adding a visit() method to IndexToBaseRowNode (I can't think of 
any offhand).  But at first glance it seems like the "right" thing to do, and 
it made the repro for this issue run without error, so if you're looking for a 
"what next" step, you could perhaps investigate that angle a bit more...?

Thanks again for your work continued efforts on this issue!

> NullPoinerException thrown when INTEGER function used as a predicate in a 
> WHERE clause of a SELECT .. GROUP BY ..  HAVING statement
> -----------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: DERBY-3872
>                 URL: https://issues.apache.org/jira/browse/DERBY-3872
>             Project: Derby
>          Issue Type: Bug
>    Affects Versions: 10.3.3.0
>            Reporter: Stan Bradbury
>            Assignee: Mamta A. Satoor
>         Attachments: NPE_Reproduction.sql, QueryWithoutTruePred.out, 
> QueryWithTruePred.out
>
>
> Use attached SQL script to create two tables , execute the following SQL and 
> throw the exception and stack trace below.  NOTE:  removing the 'always true' 
> clause '.. ( integer (1.1) = 1) .." from the SQL and the query does not fail. 
>  Releated??
> select  q1."DEPTNO" from DEPTTAB q1, EMPTAB q2 where  ( integer (1.1) = 1)  
> and  ( q2."DEPT_DEPTNO" =  q1."DEPTNO")  
> GROUP BY q1."DEPTNO" 
> HAVING  max( q2."SALARY") >=  ( select  q3."SALARY" from EMPTAB q3 where  ( 
> q3."EMPID" =  q1."DEPTNO") ) 
> ERROR 38000: The exception 'java.lang.NullPointerException' was thrown while 
> evaluating an expression.
> . . .derby.iapi.error.StandardException.newException
> . . .derby.iapi.error.StandardException.unexpectedUserException
> . . .derby.impl.services.reflect.DirectCall.invoke
> . . .derby.impl.sql.execute.ProjectRestrictResultSet.getNextRowCore
> . . .derby.impl.sql.execute.BasicNoPutResultSetImpl.getNextRow
> . . .derby.impl.jdbc.EmbedResultSet.movePosition
> . . .derby.impl.jdbc.EmbedResultSet.next
> . . .derby.tools.JDBCDisplayUtil.indent_DisplayResults
> . . .derby.tools.JDBCDisplayUtil.indent_DisplayResults
> . . .derby.tools.JDBCDisplayUtil.indent_DisplayResults
> . . .derby.tools.JDBCDisplayUtil.DisplayResults
> . . .derby.impl.tools.ij.utilMain.displayResult
> . . .derby.impl.tools.ij.utilMain.doCatch
> . . .derby.impl.tools.ij.utilMain.runScriptGuts
> . . .derby.impl.tools.ij.utilMain.go
> . . .derby.impl.tools.ij.Main.go
> . . .derby.impl.tools.ij.Main.mainCore
> . . .derby.impl.tools.ij.Main14.main
> . . .derby.tools.ij.main
> Caused by: java.lang.NullPointerException
> . . .derby.iapi.types.NumberDataType.compare
> . . .derby.impl.store.access.btree.ControlRow.compareIndexRowFromPageToKey
> . . .derby.impl.store.access.btree.ControlRow.searchForEntry
> . . .derby.impl.store.access.btree.LeafControlRow.search
> . . .derby.impl.store.access.btree.BTreeScan.positionAtStartForForwardScan
> . . .derby.impl.store.access.btree.BTreeForwardScan.positionAtStartPosition
> . . .derby.impl.store.access.btree.BTreeForwardScan.fetchRows
> . . .derby.impl.store.access.btree.BTreeScan.fetchNext
> . . .derby.impl.sql.execute.TableScanResultSet.getNextRowCore
> . . .derby.impl.sql.execute.IndexRowToBaseRowResultSet.getNextRowCore
> . . .derby.impl.sql.execute.ProjectRestrictResultSet.getNextRowCore
> . . .derby.impl.sql.execute.OnceResultSet.getNextRowCore
> . . .derby.exe.ac601a400fx011cx480cx5eacx00000010d8100.g0
> . . .derby.exe.ac601a400fx011cx480cx5eacx00000010d8100.e6
>       ... 17 more
> ============= begin nested exception, level (1) ===========
> java.lang.NullPointerException
> . . .derby.iapi.types.NumberDataType.compare
> . . .derby.impl.store.access.btree.ControlRow.compareIndexRowFromPageToKey
> . . .derby.impl.store.access.btree.ControlRow.searchForEntry
> . . .derby.impl.store.access.btree.LeafControlRow.search
> . . .derby.impl.store.access.btree.BTreeScan.positionAtStartForForwardScan
> . . .derby.impl.store.access.btree.BTreeForwardScan.positionAtStartPosition
> . . .derby.impl.store.access.btree.BTreeForwardScan.fetchRows
> . . .derby.impl.store.access.btree.BTreeScan.fetchNext
> . . .derby.impl.sql.execute.TableScanResultSet.getNextRowCore
> . . .derby.impl.sql.execute.IndexRowToBaseRowResultSet.getNextRowCore
> . . .derby.impl.sql.execute.ProjectRestrictResultSet.getNextRowCore
> . . .derby.impl.sql.execute.OnceResultSet.getNextRowCore
> . . .derby.exe.ac601a400fx011cx480cx5eacx00000010d8100.g0
> . . .derby.exe.ac601a400fx011cx480cx5eacx00000010d8100.e6
> . . .derby.impl.services.reflect.DirectCall.invoke
> . . .derby.impl.sql.execute.ProjectRestrictResultSet.getNextRowCore
> . . .derby.impl.sql.execute.BasicNoPutResultSetImpl.getNextRow
> . . .derby.impl.jdbc.EmbedResultSet.movePosition
> . . .derby.impl.jdbc.EmbedResultSet.next
> . . .derby.tools.JDBCDisplayUtil.indent_DisplayResults
> . . .derby.tools.JDBCDisplayUtil.indent_DisplayResults
> . . .derby.tools.JDBCDisplayUtil.indent_DisplayResults
> . . .derby.tools.JDBCDisplayUtil.DisplayResults
> . . .derby.impl.tools.ij.utilMain.displayResult
> . . .derby.impl.tools.ij.utilMain.doCatch
> . . .derby.impl.tools.ij.utilMain.runScriptGuts
> . . .derby.impl.tools.ij.utilMain.go
> . . .derby.impl.tools.ij.Main.go
> . . .derby.impl.tools.ij.Main.mainCore
> . . .derby.impl.tools.ij.Main14.main
> . . .derby.tools.ij.main
> ============= end nested exception, level (1) ===========
> Cleanup action completed

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to