Author: kmarsden Date: Tue Nov 4 09:19:03 2008 New Revision: 711321 URL: http://svn.apache.org/viewvc?rev=711321&view=rev Log: DERBY-3880 NPE on a query with having clause involving a join remap expression for AggregateNode operand if the JoinNode has been flattened. Fix contributed by Army Brown Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AggregateNode.java db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/GroupByTest.java Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AggregateNode.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AggregateNode.java?rev=711321&r1=711320&r2=711321&view=diff ============================================================================== --- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AggregateNode.java (original) +++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AggregateNode.java Tue Nov 4 09:19:03 2008 @@ -539,6 +539,27 @@ this.getNewNullResultExpression() : operand; + + /* The operand for this aggregate node was initialized at bind + * time. Between then and now it's possible that certain changes + * have been made to the query tree which affect this operand. In + * particular, if the operand was pointing to a result column in + * a JoinNode and then that JoinNode was flattened during pre- + * processing, all of the references to that JoinNode--including + * this aggregate's operand--need to be updated to reflect the + * fact that the Join Node no longer exists. So check to see if + * the operand is a column reference, and if so, make a call to + * remap it to its underlying expression. If nothing has happened + * then this will be a no-op; but if something has changed to void + * out the result column to which the operand points, the result + * column will be marked "redundant" and the following call should + * remap as appropriate. DERBY-3880. + */ + if (operand instanceof ColumnReference) + { + ((ColumnReference)operand).remapColumnReferencesToExpressions(); + } + return (ResultColumn) getNodeFactory().getNode( C_NodeTypes.RESULT_COLUMN, "##aggregate expression", Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/GroupByTest.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/GroupByTest.java?rev=711321&r1=711320&r2=711321&view=diff ============================================================================== --- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/GroupByTest.java (original) +++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/GroupByTest.java Tue Nov 4 09:19:03 2008 @@ -276,6 +276,16 @@ st.executeUpdate( "insert into EMPTAB values( 1, 1000, 1 )"); + + // tables for DERBY-3880 testing + st.executeUpdate("CREATE TABLE T1_D3880(i int, c varchar(20))"); + st.executeUpdate("create table t2_D3880(i int, c2 varchar(20), i2 int)"); + st.executeUpdate("insert into t1_D3880 values(1, 'abc')"); + st.executeUpdate("insert into t1_D3880 values(2, 'abc')"); + st.executeUpdate("insert into t2_D3880 values(1, 'xyz', 10)"); + st.executeUpdate("insert into t2_D3880 values(1, 'aaa', 20)"); + st.executeUpdate("insert into t2_D3880 values(2, 'xxx', 30)"); + } /** @@ -1446,6 +1456,16 @@ JDBC.assertEmpty(s.executeQuery("select orderID from session.ztemp group by orderID")); } + public void testHavingWithInnerJoinDerby3880() throws SQLException { + Statement s = createStatement(); + ResultSet rs = s.executeQuery("select t1_D3880.i, avg(t2_D3880.i2) from t1_D3880 " + + "inner join t2_D3880 on (t1_D3880.i = t2_D3880.i) group by t1_D3880.i having " + + "avg(t2_D3880.i2) > 0"); + String[][] expRs = new String[][] {{"1","15"},{"2","30"}}; + JDBC.assertFullResultSet(rs,expRs); + + } + /** * DERBY-280: Wrong result from select when aliasing to same name as used * in group by