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