diff --git a/core/src/main/java/org/hibernate/hql/classic/FromParser.java b/core/src/main/java/org/hibernate/hql/classic/FromParser.java index 3216882458..290f58b685 100644 --- a/core/src/main/java/org/hibernate/hql/classic/FromParser.java +++ b/core/src/main/java/org/hibernate/hql/classic/FromParser.java @@ -51,6 +51,13 @@ public class FromParser implements Parser { private boolean afterJoinType; private int joinType; private boolean afterFetch; + + //support collection member declarations + //e.g. "from Customer c, in(c.orders) as o" + private boolean memberDeclarations; + private boolean expectingPathExpression; + private boolean afterMemberDeclarations; + private String collectionName; private static final int NONE = -666; @@ -113,15 +120,33 @@ public class FromParser implements Parser { afterClass = true; } else if ( lcToken.equals( "in" ) ) { - if ( !expectingIn ) throw new QueryException( "unexpected token: in" ); - afterIn = true; - expectingIn = false; + if (alias == null ){ + memberDeclarations = true; + afterMemberDeclarations = false; + } + else if ( !expectingIn ) { + throw new QueryException( "unexpected token: in" ); + } else { + afterIn = true; + expectingIn = false; + } } else if ( lcToken.equals( "as" ) ) { if ( !expectingAs ) throw new QueryException( "unexpected token: as" ); afterAs = true; expectingAs = false; } + else if ( "(".equals( token ) ){ + if( !memberDeclarations ) throw new QueryException( "unexpected token: (" ); + //TODO alias should be null here + expectingPathExpression = true; + + } + else if ( ")".equals( token ) ){ +// memberDeclarations = false; +// expectingPathExpression = false; + afterMemberDeclarations = true; + } else { if ( afterJoinType ) throw new QueryException( "join expected: " + token ); @@ -141,6 +166,9 @@ public class FromParser implements Parser { if ( entityName != null ) { q.setAliasName( token, entityName ); } + else if( collectionName != null ) { + q.setAliasName( token, collectionName ); + } else { throw new QueryException( "unexpected: as " + token ); } @@ -148,6 +176,10 @@ public class FromParser implements Parser { expectingJoin = true; expectingAs = false; entityName = null; + collectionName = null; + memberDeclarations = false; + expectingPathExpression = false; + afterMemberDeclarations = false; } else if ( afterIn ) { @@ -180,6 +212,16 @@ public class FromParser implements Parser { afterClass = false; expectingJoin = true; } + else if( memberDeclarations && expectingPathExpression ){ + expectingAs = true; + peParser.setJoinType( JoinFragment.INNER_JOIN ); + peParser.setUseThetaStyleJoin( false ); + ParserHelper.parse( peParser, q.unalias( token ), ParserHelper.PATH_SEPARATORS, q ); + if ( !peParser.isCollectionValued() ) throw new QueryException( "path expression did not resolve to collection: " + token ); + collectionName = peParser.addFromCollection( q ); + expectingPathExpression = false; + memberDeclarations = false; + } else { // handle a path expression or class name that @@ -238,6 +280,7 @@ public class FromParser implements Parser { public void start(QueryTranslatorImpl q) { entityName = null; + collectionName = null; alias = null; afterIn = false; afterAs = false; @@ -245,10 +288,18 @@ public class FromParser implements Parser { expectingJoin = false; expectingIn = false; expectingAs = false; + memberDeclarations = false; + expectingPathExpression = false; + afterMemberDeclarations = false; joinType = NONE; } public void end(QueryTranslatorImpl q) { + if( afterMemberDeclarations ){ + //The exception throwned by the AST query translator contains the error token location, respensent by line and colum, + //but it hard to get that info here. + throw new QueryException("alias not specified for IN"); + } } } diff --git a/testsuite/src/test/java/org/hibernate/test/hql/EntityBean.java b/testsuite/src/test/java/org/hibernate/test/hql/EntityBean.java deleted file mode 100644 index cea041ef5a..0000000000 --- a/testsuite/src/test/java/org/hibernate/test/hql/EntityBean.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.hibernate.test.hql; - -import java.util.HashSet; -import java.util.Set; - -import javax.persistence.CascadeType; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.OneToMany; - -@Entity -public class EntityBean { - @Id - @GeneratedValue - private Long id; - - @OneToMany(cascade=CascadeType.ALL) - private Set items = new HashSet(); - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public Set getItems() { - return items; - } - - public void setItems(Set items) { - this.items = items; - } -} diff --git a/testsuite/src/test/java/org/hibernate/test/hql/EntityBeanTest.java b/testsuite/src/test/java/org/hibernate/test/hql/EntityBeanTest.java deleted file mode 100644 index dabac461a8..0000000000 --- a/testsuite/src/test/java/org/hibernate/test/hql/EntityBeanTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.hibernate.test.hql; - -import java.util.List; - -import org.hibernate.Query; -import org.hibernate.Session; -import org.hibernate.test.annotations.TestCase; - -public class EntityBeanTest extends TestCase { - - - @Override - protected Class[] getAnnotatedClasses() { - return new Class[]{EntityBean.class,Item.class}; - } - public void testAAA(){ - Session s=openSession(); - s.beginTransaction(); - Item item = new Item(); - item.setItemValue("1"); - EntityBean eb = new EntityBean(); - eb.getItems().add(item); - item=new Item(); - item.setItemValue("2"); - eb.getItems().add(item); - s.persist(eb); - s.getTransaction().commit(); - s.close(); - - s=openSession(); - s.beginTransaction(); - Query query = s.createQuery("SELECT o FROM EntityBean AS o, IN (o.items) l WHERE l.itemValue = '1'"); - List list = query.list(); - assertEquals(list.size(), 1); - s.getTransaction().commit(); - s.close(); - } - -} diff --git a/testsuite/src/test/java/org/hibernate/test/hql/HQLTest.java b/testsuite/src/test/java/org/hibernate/test/hql/HQLTest.java index a191beb517..7a77e69f33 100644 --- a/testsuite/src/test/java/org/hibernate/test/hql/HQLTest.java +++ b/testsuite/src/test/java/org/hibernate/test/hql/HQLTest.java @@ -144,6 +144,19 @@ public class HQLTest extends QueryTranslatorTestCase { public void testJoinFetchCollectionOfValues() { assertTranslation( "select h from Human as h join fetch h.nickNames" ); } + + public void testCollectionMemberDeclarations() { + assertTranslation( "from Customer c, in(c.orders) o" ); + assertTranslation( "from Customer c, in(c.orders) as o" ); + assertTranslation( "select c.name from Customer c, in(c.orders) as o where c.id = o.id.customerId" ); + } + public void testCollectionMemberDeclarationsFailureExpected(){ + // both these two query translators throw exeptions for this HQL since + // IN asks an alias, but the difference is that the error message from AST + // contains the error token location (by lines and columns), which is hardly + // to get from Classic query translator --stliu + assertTranslation( "from Customer c, in(c.orders)" ); + } public void testCollectionJoinsInSubselect() { // caused by some goofiness in FromElementFactory that tries to