Repository: cayenne
Updated Branches:
  refs/heads/STABLE-4.0 9717b704d -> 77fc8b79d


CAY-2356 EJBQL: Incorrect COUNT() on outer joined table
(cherry picked from commit e44f2fa)


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/77fc8b79
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/77fc8b79
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/77fc8b79

Branch: refs/heads/STABLE-4.0
Commit: 77fc8b79d1f8055cec8d768e46188450c91bf667
Parents: 9717b70
Author: Nikita Timofeev <stari...@gmail.com>
Authored: Wed Aug 30 17:39:18 2017 +0300
Committer: Nikita Timofeev <stari...@gmail.com>
Committed: Wed Aug 30 18:00:54 2017 +0300

----------------------------------------------------------------------
 .../ejbql/EJBQLAggregateColumnTranslator.java   | 38 +++++++++--
 .../org/apache/cayenne/query/EJBQLQueryIT.java  | 69 ++++++++++++++++++++
 docs/doc/src/main/resources/RELEASE-NOTES.txt   |  1 +
 3 files changed, 104 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/77fc8b79/cayenne-server/src/main/java/org/apache/cayenne/access/translator/ejbql/EJBQLAggregateColumnTranslator.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/ejbql/EJBQLAggregateColumnTranslator.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/ejbql/EJBQLAggregateColumnTranslator.java
index d3bc0e3..7a5da20 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/ejbql/EJBQLAggregateColumnTranslator.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/ejbql/EJBQLAggregateColumnTranslator.java
@@ -25,11 +25,12 @@ import org.apache.cayenne.ejbql.EJBQLException;
 import org.apache.cayenne.ejbql.EJBQLExpression;
 import org.apache.cayenne.ejbql.EJBQLExpressionVisitor;
 import org.apache.cayenne.ejbql.parser.EJBQLAggregateColumn;
+import org.apache.cayenne.ejbql.parser.EJBQLIntegerLiteral;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.ObjAttribute;
-import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.reflect.ClassDescriptor;
 
 /**
  * @since 3.0
@@ -93,6 +94,20 @@ class EJBQLAggregateColumnTranslator extends 
EJBQLBaseVisitor {
         }
     }
 
+    /**
+     * This method tries to find mandatory PK attribute, if no such exists 
returns first PK attribute.
+     * Used to translate COUNT(entity) expressions into COUNT(alias.pk_column).
+     */
+    private DbAttribute getPk(DbEntity dbEntity) {
+        for(DbAttribute attribute : dbEntity.getPrimaryKeys()) {
+            if(attribute.isMandatory()) {
+                return attribute;
+            }
+        }
+
+        return dbEntity.getPrimaryKeys().iterator().next();
+    }
+
     class FieldPathTranslator extends EJBQLPathTranslator {
 
         FieldPathTranslator() {
@@ -124,12 +139,14 @@ class EJBQLAggregateColumnTranslator extends 
EJBQLBaseVisitor {
 
         @Override
         protected void processTerminatingRelationship(ObjRelationship 
relationship) {
-            Collection<DbAttribute> dbAttr = ((ObjEntity) 
relationship.getTargetEntity()).getDbEntity().getAttributes();
+            Collection<DbAttribute> dbAttr = 
relationship.getTargetEntity().getDbEntity().getAttributes();
 
             if (dbAttr.size() > 0) {
                 resolveJoin();
             }
-            context.append('*');
+
+            DbAttribute pk = 
getPk(relationship.getTargetEntity().getDbEntity());
+            context.append(lastAlias).append('.').append(pk.getName());
         }
     }
 
@@ -142,8 +159,21 @@ class EJBQLAggregateColumnTranslator extends 
EJBQLBaseVisitor {
         }
 
         @Override
+        public boolean visitIntegerLiteral(EJBQLIntegerLiteral expression) {
+            // this allows to use COUNT(1) in EJBQL
+            context.append(expression.getText());
+            return false;
+        }
+
+        @Override
         public boolean visitIdentifier(EJBQLExpression expression) {
-            context.append('*');
+            ClassDescriptor classDescriptor = 
context.getCompiledExpression().getEntityDescriptor(expression.getText());
+            if(classDescriptor == null) {
+                throw new EJBQLException("Unmapped id variable: " + 
expression.getText());
+            }
+            String alias = context.getTableAlias(expression.getText(), 
classDescriptor.getEntity().getDbEntityName());
+            DbAttribute pk = getPk(classDescriptor.getEntity().getDbEntity());
+            context.append(alias).append('.').append(pk.getName());
             return false;
         }
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/77fc8b79/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java 
b/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
index b1b2495..8e4f989 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/EJBQLQueryIT.java
@@ -556,4 +556,73 @@ public class EJBQLQueryIT extends ServerCase {
                assertEquals("a0", ((Artist) 
artistDesc.get(0)[0]).getArtistName());
                assertEquals("a1", ((Artist) 
artistDesc.get(1)[0]).getArtistName());
        }
+
+    @Test
+    public void testOuterJoinCountByIdentifier() throws Exception {
+        tArtist.insert(1, "a0");
+        tArtist.insert(2, "a1");
+        tArtist.insert(3, "a2");
+
+        tPainting.insert(3, 1, "title0");
+        tPainting.insert(2, 1, "title1");
+        tPainting.insert(1, 2, "title2");
+
+        EJBQLQuery asc = new EJBQLQuery("select a, count(p) from Artist a LEFT 
JOIN a.paintingArray p " +
+                "GROUP BY a order by count(p) DESC");
+        List<Object[]> artistAsc = context.performQuery(asc);
+        assertEquals(3, artistAsc.size());
+        assertEquals("a0", ((Artist) artistAsc.get(0)[0]).getArtistName());
+        assertEquals("a1", ((Artist) artistAsc.get(1)[0]).getArtistName());
+        assertEquals("a2", ((Artist) artistAsc.get(2)[0]).getArtistName());
+
+        assertEquals(2L, artistAsc.get(0)[1]);
+        assertEquals(1L, artistAsc.get(1)[1]);
+        assertEquals(0L, artistAsc.get(2)[1]);
+    }
+
+    @Test
+    public void testOuterJoinCountAll() throws Exception {
+        tArtist.insert(1, "a0");
+        tArtist.insert(2, "a1");
+        tArtist.insert(3, "a2");
+
+        tPainting.insert(3, 1, "title0");
+        tPainting.insert(2, 1, "title1");
+        tPainting.insert(1, 2, "title2");
+
+        EJBQLQuery asc = new EJBQLQuery("SELECT a, count(1) FROM Artist a LEFT 
JOIN a.paintingArray p " +
+                "GROUP BY a ORDER BY count(1) DESC, a.artistName");
+        List<Object[]> artistAsc = context.performQuery(asc);
+        assertEquals(3, artistAsc.size());
+        assertEquals("a0", ((Artist) artistAsc.get(0)[0]).getArtistName());
+        assertEquals("a1", ((Artist) artistAsc.get(1)[0]).getArtistName());
+        assertEquals("a2", ((Artist) artistAsc.get(2)[0]).getArtistName());
+
+        assertEquals(2L, artistAsc.get(0)[1]);
+        assertEquals(1L, artistAsc.get(1)[1]);
+        assertEquals(1L, artistAsc.get(2)[1]); // here is a difference with 
other cases
+    }
+
+    @Test
+    public void testOuterJoinCountByPath() throws Exception {
+        tArtist.insert(1, "a0");
+        tArtist.insert(2, "a1");
+        tArtist.insert(3, "a2");
+
+        tPainting.insert(3, 1, "title0");
+        tPainting.insert(2, 1, "title1");
+        tPainting.insert(1, 2, "title2");
+
+        EJBQLQuery asc = new EJBQLQuery("select a, count(a.paintingArray+) 
from Artist a " +
+                "GROUP BY a order by count(a.paintingArray+) DESC");
+        List<Object[]> artistAsc = context.performQuery(asc);
+        assertEquals(3, artistAsc.size());
+        assertEquals("a0", ((Artist) artistAsc.get(0)[0]).getArtistName());
+        assertEquals("a1", ((Artist) artistAsc.get(1)[0]).getArtistName());
+        assertEquals("a2", ((Artist) artistAsc.get(2)[0]).getArtistName());
+
+        assertEquals(2L, artistAsc.get(0)[1]);
+        assertEquals(1L, artistAsc.get(1)[1]);
+        assertEquals(0L, artistAsc.get(2)[1]);
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/77fc8b79/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt 
b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index bda40a7..04490b2 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -23,6 +23,7 @@ CAY-2349 Cache issue: 'SelectQuery' with prefetches loses 
relationships
 CAY-2350 Expression: NotIn with empty collection returns empty result
 CAY-2353 Broken paginated column select with only one entity in the result
 CAY-2354 DbGenerator.runGenerator must commit its connection
+CAY-2356 EJBQL: Incorrect COUNT() on outer joined table
 
 ----------------------------------
 Release: 4.0.B1

Reply via email to