diff --git a/hibernate-core/src/main/antlr/hql-sql.g b/hibernate-core/src/main/antlr/hql-sql.g index 90fb1e32d6..d3dc9814b7 100644 --- a/hibernate-core/src/main/antlr/hql-sql.g +++ b/hibernate-core/src/main/antlr/hql-sql.g @@ -2,6 +2,8 @@ header { package org.hibernate.hql.internal.antlr; +import java.util.Stack; + import org.hibernate.internal.CoreMessageLogger; import org.jboss.logging.Logger; } @@ -75,6 +77,7 @@ tokens private int currentClauseType; private int currentTopLevelClauseType; private int currentStatementType; + private Stack parentClauses = new Stack(); public final boolean isSubQuery() { return level > 1; @@ -153,12 +156,17 @@ tokens } private void handleClauseStart(int clauseType) { + parentClauses.push(currentClauseType); currentClauseType = clauseType; if ( level == 1 ) { currentTopLevelClauseType = clauseType; } } + private void handleClauseEnd() { + currentClauseType = parentClauses.pop(); + } + /////////////////////////////////////////////////////////////////////////// // NOTE: The real implementations for the following are in the subclass. @@ -300,6 +308,7 @@ intoClause! { } : #( INTO { handleClauseStart( INTO ); } (p=path) ps:insertablePropertySpec ) { #intoClause = createIntoClause(p, ps); + handleClauseEnd(); } ; @@ -308,7 +317,9 @@ insertablePropertySpec ; setClause - : #( SET { handleClauseStart( SET ); } (assignment)* ) + : #( SET { handleClauseStart( SET ); } (assignment)* ) { + handleClauseEnd(); + } ; assignment @@ -346,7 +357,9 @@ query! ; orderClause - : #(ORDER { handleClauseStart( ORDER ); } orderExprs) + : #(ORDER { handleClauseStart( ORDER ); } orderExprs) { + handleClauseEnd(); + } ; orderExprs @@ -376,12 +389,15 @@ resultVariableRef! ; groupClause - : #(GROUP { handleClauseStart( GROUP ); } (expr)+ ( #(HAVING logicalExpr) )? ) + : #(GROUP { handleClauseStart( GROUP ); } (expr)+ ( #(HAVING logicalExpr) )? ) { + handleClauseEnd(); + } ; selectClause! : #(SELECT { handleClauseStart( SELECT ); beforeSelectClause(); } (d:DISTINCT)? x:selectExprList ) { #selectClause = #([SELECT_CLAUSE,"{select clause}"], #d, #x); + handleClauseEnd(); } ; @@ -439,7 +455,9 @@ fromClause { // the ouput AST (#fromClause) has not been built yet. prepareFromClauseInputTree(#fromClause_in); } - : #(f:FROM { pushFromClause(#fromClause,f); handleClauseStart( FROM ); } fromElementList ) + : #(f:FROM { pushFromClause(#fromClause,f); handleClauseStart( FROM ); } fromElementList ) { + handleClauseEnd(); + } ; fromElementList { @@ -530,6 +548,7 @@ withClause // rule during recognition... : #(w:WITH { handleClauseStart( WITH ); } b:logicalExpr ) { #withClause = #(w , #b); + handleClauseEnd(); } ; @@ -537,6 +556,7 @@ whereClause : #(w:WHERE { handleClauseStart( WHERE ); } b:logicalExpr ) { // Use the *output* AST for the boolean expression! #whereClause = #(w , #b); + handleClauseEnd(); } ; diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/CompositeId.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/CompositeId.java index b8565ca66b..9a9bc83a72 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/CompositeId.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/CompositeId.java @@ -18,6 +18,14 @@ public class CompositeId implements Serializable { private int id1; private int id2; + public CompositeId() { + } + + public CompositeId(int id1, int id2) { + this.id1 = id1; + this.id2 = id2; + } + public int getId1() { return id1; } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/CompositeIdRowValueTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/CompositeIdRowValueTest.java new file mode 100644 index 0000000000..d0be100528 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/CompositeIdRowValueTest.java @@ -0,0 +1,46 @@ +package org.hibernate.jpa.test; + +import javax.persistence.EntityManager; +import javax.persistence.Query; + +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.H2Dialect; +import org.junit.Test; + +import org.hibernate.testing.TestForIssue; + +import static org.junit.Assert.assertThat; +import static org.hamcrest.core.Is.is; + +@TestForIssue( jiraKey = "HHH-9029") +public class CompositeIdRowValueTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Dialect getDialect() { + return new RowValueConstructorDialect(); + } + + @Test + public void testTupleAfterSubQuery() { + EntityManager em = getOrCreateEntityManager(); + Query q = em.createQuery("SELECT e FROM EntityWithCompositeId e " + + "WHERE EXISTS (SELECT 1 FROM EntityWithCompositeId) " + + "AND e.id = :id"); + + q.setParameter("id", new CompositeId(1, 2)); + + assertThat(q.getResultList().size(), is(0)); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] {EntityWithCompositeId.class, CompositeId.class}; + } + + public static class RowValueConstructorDialect extends H2Dialect { + @Override + public boolean supportsRowValueConstructorSyntax() { + return true; + } + } +}