Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 246211
Collapse All | Expand All

(-)foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/jpa/parsing/AttributeNode.java (-4 / +9 lines)
Lines 80-87 Link Here
80
            if (context.hasMemberOfNode()) {
80
            if (context.hasMemberOfNode()) {
81
                return parentExpression.noneOf(name, new ExpressionBuilder().equal(context.getMemberOfNode().getLeftExpression()));
81
                return parentExpression.noneOf(name, new ExpressionBuilder().equal(context.getMemberOfNode().getLeftExpression()));
82
            }
82
            }
83
            return outerJoin ? parentExpression.anyOfAllowingNone(name) : 
83
            //Bug246211: need to use an outer join if specified by the context
84
                parentExpression.anyOf(name);
84
            if (  isOuterJoin() || context.shouldUseOuterJoins() ) {
85
                return parentExpression.anyOfAllowingNone(name);
86
            } else {
87
                return parentExpression.anyOf(name);
88
            }
85
        } else {
89
        } else {
86
            // check whether collection attribute is required
90
            // check whether collection attribute is required
87
            if (requiresCollectionAttribute()) {
91
            if (requiresCollectionAttribute()) {
Lines 89-96 Link Here
89
                    context.getParseTreeContext().getQueryInfo(), 
93
                    context.getParseTreeContext().getQueryInfo(), 
90
                    getLine(), getColumn(), name);
94
                    getLine(), getColumn(), name);
91
            }
95
            }
92
96
            //Bug246211: context changed to have shouldUseOuterJoins set more often. Need to check that it is a reference 
93
            if (context.shouldUseOuterJoins() || isOuterJoin()) {
97
            //  mapping before using the outer join 
98
            if (  isOuterJoin() || (context.shouldUseOuterJoins()&& this.mapping!=null && this.mapping.isForeignReferenceMapping())) {
94
                return parentExpression.getAllowingNull(name);
99
                return parentExpression.getAllowingNull(name);
95
            } else {
100
            } else {
96
                return parentExpression.get(name);
101
                return parentExpression.get(name);
(-)foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/jpa/parsing/ConstructorNode.java (-14 / +3 lines)
Lines 51-64 Link Here
51
            ReportQuery reportQuery = (ReportQuery)theQuery;
51
            ReportQuery reportQuery = (ReportQuery)theQuery;
52
            reportQuery.beginAddingConstructorArguments(
52
            reportQuery.beginAddingConstructorArguments(
53
                getConstructorClass(context.getParseTreeContext()));
53
                getConstructorClass(context.getParseTreeContext()));
54
            //bug246211: use outer joins outside the where clause, as inner joins will filter out null results
55
            selectContext.useOuterJoins();
54
            for (Iterator i = constructorItems.iterator(); i.hasNext();) {
56
            for (Iterator i = constructorItems.iterator(); i.hasNext();) {
55
                Node node = (Node)i.next();
57
                Node node = (Node)i.next();
56
                if (selectingRelationshipField(node, context)) {
57
                    selectContext.useOuterJoins();
58
                }
59
                node.applyToQuery(reportQuery, context);
58
                node.applyToQuery(reportQuery, context);
60
                selectContext.dontUseOuterJoins();
61
            }
59
            }
60
            selectContext.dontUseOuterJoins();
62
            reportQuery.endAddingToConstructorItem();
61
            reportQuery.endAddingToConstructorItem();
63
        }
62
        }
64
    }
63
    }
Lines 136-151 Link Here
136
135
137
    /**
136
    /**
138
     * INTERNAL
137
     * INTERNAL
139
     */
140
    private boolean selectingRelationshipField(Node node, GenerationContext context) {
141
        if ((node == null) || !node.isDotNode()) {
142
            return false;
143
        }
144
        return !((DotNode)node).endsWithDirectToField(context);
145
    }
146
147
    /**
148
     * INTERNAL
149
     * Get the string representation of this node.
138
     * Get the string representation of this node.
150
     */
139
     */
151
    public String getAsString() {
140
    public String getAsString() {
(-)foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/jpa/parsing/GroupByNode.java (+4 lines)
Lines 76-85 Link Here
76
    public void addGroupingToQuery(ObjectLevelReadQuery theQuery, GenerationContext context) {
76
    public void addGroupingToQuery(ObjectLevelReadQuery theQuery, GenerationContext context) {
77
        if (theQuery.isReportQuery()) {
77
        if (theQuery.isReportQuery()) {
78
            Iterator iter = getGroupByItems().iterator();
78
            Iterator iter = getGroupByItems().iterator();
79
            SelectGenerationContext selectContext = (SelectGenerationContext)context;
80
            //bug246211: outer joins are used outside the where clause, as inner joins will filter null results
81
            selectContext.useOuterJoins();
79
            while (iter.hasNext()) {
82
            while (iter.hasNext()) {
80
                Node nextNode = (Node)iter.next();
83
                Node nextNode = (Node)iter.next();
81
                ((ReportQuery)theQuery).addGrouping(nextNode.generateExpression(context));
84
                ((ReportQuery)theQuery).addGrouping(nextNode.generateExpression(context));
82
            }
85
            }
86
            selectContext.dontUseOuterJoins();
83
        }
87
        }
84
    }
88
    }
85
89
(-)foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/jpa/parsing/HavingNode.java (+4 lines)
Lines 52-59 Link Here
52
     */
52
     */
53
    public void addHavingToQuery(ObjectLevelReadQuery theQuery, GenerationContext context) {
53
    public void addHavingToQuery(ObjectLevelReadQuery theQuery, GenerationContext context) {
54
        if (theQuery.isReportQuery()) {
54
        if (theQuery.isReportQuery()) {
55
            //bug246211: outer joins are used outside the where clause, as inner joins will filter null results
56
            SelectGenerationContext selectContext = (SelectGenerationContext)context;
57
            selectContext.useOuterJoins();
55
            Expression havingExpression = getHaving().generateExpression(context);
58
            Expression havingExpression = getHaving().generateExpression(context);
56
            ((ReportQuery)theQuery).setHavingExpression(havingExpression);
59
            ((ReportQuery)theQuery).setHavingExpression(havingExpression);
60
            selectContext.dontUseOuterJoins();
57
        }
61
        }
58
    }
62
    }
59
63
(-)foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/jpa/parsing/SelectNode.java (-16 / +4 lines)
Lines 160-170 Link Here
160
            }
160
            }
161
        }
161
        }
162
        SelectGenerationContext selectContext = (SelectGenerationContext)context;
162
        SelectGenerationContext selectContext = (SelectGenerationContext)context;
163
        //bug246211: outer joins are used on all nodes in the select, allowing nested DotNodes to each use an outer join 
164
        selectContext.useOuterJoins();
163
        for (int i=0;i<selectExpressions.size();i++){
165
        for (int i=0;i<selectExpressions.size();i++){
164
            Node node = (Node)selectExpressions.get(i);
166
            Node node = (Node)selectExpressions.get(i);
165
            if (selectingRelationshipField(node, context)) {
167
166
                selectContext.useOuterJoins();
167
            }
168
            if (node.isAliasableNode() && identifiers != null){
168
            if (node.isAliasableNode() && identifiers != null){
169
                String alias = (String)identifiers.get(i);
169
                String alias = (String)identifiers.get(i);
170
                if (alias != null){
170
                if (alias != null){
Lines 172-180 Link Here
172
                }
172
                }
173
            }
173
            }
174
            node.applyToQuery(readQuery, context); 
174
            node.applyToQuery(readQuery, context); 
175
            selectContext.dontUseOuterJoins();
176
        }
175
        }
177
176
        selectContext.dontUseOuterJoins();
178
        //indicate on the query if "return null if primary key null"
177
        //indicate on the query if "return null if primary key null"
179
        //This means we want nulls returned if we expect an outer join
178
        //This means we want nulls returned if we expect an outer join
180
        readQuery.setShouldBuildNullForNullPk(this.hasOneToOneSelected(context));
179
        readQuery.setShouldBuildNullForNullPk(this.hasOneToOneSelected(context));
Lines 371-387 Link Here
371
        return false;
370
        return false;
372
    }
371
    }
373
372
374
    private boolean selectingRelationshipField(Node node, GenerationContext context) {
375
        if ((node == null) || !node.isDotNode()) {
376
            return false;
377
        }
378
        TypeHelper typeHelper = context.getParseTreeContext().getTypeHelper();
379
        Node path = node.getLeft();
380
        AttributeNode attribute = (AttributeNode)node.getRight();
381
        return typeHelper.isRelationship(path.getType(), 
382
                                         attribute.getAttributeName());
383
    }
384
385
    /**
373
    /**
386
     * Answer true if the SELECT ends in a direct-to-field.
374
     * Answer true if the SELECT ends in a direct-to-field.
387
     * true: SELECT phone.areaCode
375
     * true: SELECT phone.areaCode
(-)foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/jpa/parsing/TypeHelperImpl.java (-4 / +1 lines)
Lines 143-152 Link Here
143
    public boolean isRelationship(Object ownerClass, String attribute) {
143
    public boolean isRelationship(Object ownerClass, String attribute) {
144
        DatabaseMapping mapping = 
144
        DatabaseMapping mapping = 
145
            resolveAttributeMapping(ownerClass, attribute);
145
            resolveAttributeMapping(ownerClass, attribute);
146
        return (mapping != null) && 
146
        return (mapping != null) && mapping.isForeignReferenceMapping();
147
               (mapping.isObjectReferenceMapping() || 
148
                mapping.isOneToManyMapping() || 
149
                mapping.isManyToManyMapping());
150
    }
147
    }
151
148
152
    /** Returns true if the specified attribute denotes a single valued
149
    /** Returns true if the specified attribute denotes a single valued
(-)jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/jpql/JUnitJPQLComplexTestSuite.java (+75 lines)
Lines 164-169 Link Here
164
        suite.addTest(new JUnitJPQLComplexTestSuite("complexNavigatingTwoLevelOfEmbeddeds"));
164
        suite.addTest(new JUnitJPQLComplexTestSuite("complexNavigatingTwoLevelOfEmbeddeds"));
165
        suite.addTest(new JUnitJPQLComplexTestSuite("complexNamedQueryResultPropertiesTest"));
165
        suite.addTest(new JUnitJPQLComplexTestSuite("complexNamedQueryResultPropertiesTest"));
166
        suite.addTest(new JUnitJPQLComplexTestSuite("complexOuterJoinQuery"));
166
        suite.addTest(new JUnitJPQLComplexTestSuite("complexOuterJoinQuery"));
167
        suite.addTest(new JUnitJPQLComplexTestSuite("complexCountOuterJoinTest"));
168
        suite.addTest(new JUnitJPQLComplexTestSuite("complexCountNestedOuterJoinTest"));
169
        suite.addTest(new JUnitJPQLComplexTestSuite("complexOuterJoinGroupByTest"));
167
        
170
        
168
        suite.addTest(new JUnitJPQLComplexTestSuite("complexInheritanceTest"));
171
        suite.addTest(new JUnitJPQLComplexTestSuite("complexInheritanceTest"));
169
        suite.addTest(new JUnitJPQLComplexTestSuite("complexInheritanceUsingNamedQueryTest"));
172
        suite.addTest(new JUnitJPQLComplexTestSuite("complexInheritanceUsingNamedQueryTest"));
Lines 1711-1716 Link Here
1711
        Assert.assertNull  ("Complex outer join query (2): unexpected result value (1, 3):", result1[3]);
1714
        Assert.assertNull  ("Complex outer join query (2): unexpected result value (1, 3):", result1[3]);
1712
                
1715
                
1713
    }
1716
    }
1717
    
1718
    /**
1719
     * Test bug 246211: Inner join used outside of the where clause causes unintentional filtering of results.
1720
     * Ie "Select count(a.b) from A a" can never return 0 since all As with 0 Bs get filtered out if an inner join
1721
     * is used.  
1722
     */
1723
    public void complexCountOuterJoinTest()
1724
    {
1725
        EntityManager em = createEntityManager();
1726
        String jpql = "Select e.lastName, count(e.department) from Employee e group by e.lastName";
1727
        Query q = em.createQuery(jpql);
1728
        List results = q.getResultList();
1729
        boolean passed = false;
1730
        Iterator i = results.iterator();
1731
        //check all rows until a row with count=0 is found
1732
        while (!passed && i.hasNext() ){
1733
            Object[] row = (Object[])i.next();
1734
            if (((Number)row[1]).intValue() == 0) {
1735
                passed = true;
1736
            }
1737
        }
1738
        
1739
        this.assertTrue("complexCountOuterJoinTest did not return results rows with a count of 0", passed);      
1740
    }
1741
    
1742
    /**
1743
     * Test bug 246211: Inner join used outside of the where clause causes unintentional filtering of results.
1744
     * Ie "Select count(a.b.c) from A a" can never return 0 since all As with 0 Bs get filtered out if an inner join
1745
     * is used.  This test tests that an outer join is used from A->B as well as B->C
1746
     */
1747
    public void complexCountNestedOuterJoinTest()
1748
    {
1749
        EntityManager em = createEntityManager();
1750
        String jpql = "Select e.lastName, count(e.department.managers) from Employee e group by e.lastName";
1751
        Query q = em.createQuery(jpql);
1752
        List results = q.getResultList();
1753
        
1754
        boolean passed = false;
1755
        Iterator i = results.iterator();
1756
        //check all rows until a row with count=0 is found
1757
        while (!passed && i.hasNext() ){
1758
            Object[] row = (Object[])i.next();
1759
            if (((Number)row[1]).intValue() == 0) {
1760
                passed = true;
1761
            }
1762
        }
1763
        
1764
        this.assertTrue("complexCountNestedOuterJoinTest did not return results rows with a count of 0", passed);      
1765
    }
1766
    
1767
    /**
1768
     * Test bug 246211: Inner join used outside of the where clause causes unintentional filtering of results.
1769
     * This tests verifies that the group by statement uses an outer join
1770
     */
1771
    public void complexOuterJoinGroupByTest(){
1772
        EntityManager em = createEntityManager();
1773
        //Employee to department is a many to one relation.  
1774
        String jpql = "Select count(e), e.department from Employee e group by e.department";
1775
        Query q = em.createQuery(jpql);
1776
        List results = q.getResultList();Employee e;
1777
        boolean passed = false;
1778
        Iterator i = results.iterator();
1779
        //check all rows until a row with null departments is found
1780
        while (!passed && i.hasNext() ){
1781
            Object[] row = (Object[])i.next();
1782
            if ( row[1]  == null ){
1783
                //verify that employees with a null department are included in the results
1784
                passed = true;
1785
            }
1786
        }
1787
        this.assertTrue("complexOuterJoinCountTest did not return results rows with a count of 0", passed);  
1788
    }
1714
1789
1715
    // Helper methods and classes for constructor query test cases
1790
    // Helper methods and classes for constructor query test cases
1716
1791

Return to bug 246211