diff --git a/hibernate-core/src/main/antlr/hql-sql.g b/hibernate-core/src/main/antlr/hql-sql.g index 51437f6a21..3a4c382056 100644 --- a/hibernate-core/src/main/antlr/hql-sql.g +++ b/hibernate-core/src/main/antlr/hql-sql.g @@ -409,6 +409,7 @@ selectExpr | collectionFunction // elements() or indices() | literal | arithmeticExpr + | logicalExpr | parameter | query ; diff --git a/hibernate-core/src/main/antlr/sql-gen.g b/hibernate-core/src/main/antlr/sql-gen.g index 40eeb94d48..29279301e7 100644 --- a/hibernate-core/src/main/antlr/sql-gen.g +++ b/hibernate-core/src/main/antlr/sql-gen.g @@ -246,6 +246,7 @@ selectExpr | aggregate | c:constant { out(c); } | arithmeticExpr + | selectBooleanExpr[false] | parameter | sn:SQL_NODE { out(sn); } | { out("("); } selectStatement { out(")"); } @@ -306,6 +307,11 @@ booleanOp[ boolean parens ] | #(NOT { out(" not ("); } booleanExpr[false] { out(")"); } ) ; +selectBooleanExpr[ boolean parens ] + : booleanOp [ parens ] + | comparisonExpr [ parens ] + ; + booleanExpr[ boolean parens ] : booleanOp [ parens ] | comparisonExpr [ parens ] @@ -387,6 +393,7 @@ simpleExpr | count | parameter | arithmeticExpr + | selectBooleanExpr[false] ; constant diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/BinaryLogicOperatorNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/BinaryLogicOperatorNode.java index 0b8f40561d..be9f09765b 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/BinaryLogicOperatorNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/BinaryLogicOperatorNode.java @@ -29,6 +29,7 @@ import org.hibernate.HibernateException; import org.hibernate.TypeMismatchException; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes; +import org.hibernate.hql.internal.ast.util.ColumnHelper; import org.hibernate.internal.util.StringHelper; import org.hibernate.param.ParameterSpecification; import org.hibernate.type.OneToOneType; @@ -43,7 +44,7 @@ import antlr.collections.AST; * * @author Steve Ebersole */ -public class BinaryLogicOperatorNode extends HqlSqlWalkerNode implements BinaryOperatorNode { +public class BinaryLogicOperatorNode extends AbstractSelectExpression implements BinaryOperatorNode { /** * Performs the operator node initialization by seeking out any parameter * nodes and setting their expected type, if possible. @@ -268,4 +269,8 @@ public class BinaryLogicOperatorNode extends HqlSqlWalkerNode implements BinaryO public Node getRightHandOperand() { return (Node) getFirstChild().getNextSibling(); } + + public void setScalarColumnText(int i) throws SemanticException { + ColumnHelper.generateSingleScalarColumn( this, i ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/UnaryLogicOperatorNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/UnaryLogicOperatorNode.java index 197237dfad..02842d0526 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/UnaryLogicOperatorNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/UnaryLogicOperatorNode.java @@ -23,6 +23,9 @@ */ package org.hibernate.hql.internal.ast.tree; +import antlr.SemanticException; + +import org.hibernate.hql.internal.ast.util.ColumnHelper; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.Type; @@ -31,7 +34,7 @@ import org.hibernate.type.Type; * * @author Steve Ebersole */ -public class UnaryLogicOperatorNode extends HqlSqlWalkerNode implements UnaryOperatorNode { +public class UnaryLogicOperatorNode extends AbstractSelectExpression implements UnaryOperatorNode { public Node getOperand() { return (Node) getFirstChild(); } @@ -45,4 +48,8 @@ public class UnaryLogicOperatorNode extends HqlSqlWalkerNode implements UnaryOpe // logic operators by definition resolve to booleans return StandardBasicTypes.BOOLEAN; } + + public void setScalarColumnText(int i) throws SemanticException { + ColumnHelper.generateSingleScalarColumn( this, i ); + } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java index 26cb5bf005..d6aed4a5b6 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java @@ -133,6 +133,7 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase { "hql/Image.hbm.xml", "hql/ComponentContainer.hbm.xml", "hql/VariousKeywordPropertyEntity.hbm.xml", + "hql/Constructor.hbm.xml", "batchfetch/ProductLine.hbm.xml", "cid/Customer.hbm.xml", "cid/Order.hbm.xml", @@ -212,6 +213,31 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase { session.close(); } + @Test + @TestForIssue( jiraKey = "HHH-8699" ) + public void testBooleanPredicate() { + final Session session = openSession(); + + session.getTransaction().begin(); + final Constructor constructor = new Constructor(); + session.save( constructor ); + session.getTransaction().commit(); + + session.clear(); + Constructor.resetConstructorExecutionCount(); + + session.getTransaction().begin(); + final Constructor result = (Constructor) session.createQuery( + "select new Constructor( c.id, c.id is not null, c.id = c.id, c.id + 1, concat( c.id, 'foo' ) ) from Constructor c where c.id = :id" + ).setParameter( "id", constructor.getId() ).uniqueResult(); + session.getTransaction().commit(); + + assertEquals( 1, Constructor.getConstructorExecutionCount() ); + assertEquals( new Constructor( constructor.getId(), true, true, constructor.getId() + 1, constructor.getId() + "foo" ), result ); + + session.close(); + } + @Test public void testJpaTypeOperator() { // just checking syntax here... diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/Constructor.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/hql/Constructor.hbm.xml new file mode 100644 index 0000000000..d9d56f576a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/Constructor.hbm.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/Constructor.java b/hibernate-core/src/test/java/org/hibernate/test/hql/Constructor.java new file mode 100644 index 0000000000..557827a80c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/Constructor.java @@ -0,0 +1,125 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.hql; + +import java.io.Serializable; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +public class Constructor implements Serializable { + private static int CONSTRUCTOR_EXECUTION_COUNT = 0; + + private long id; + private String someString; + private Number someNumber; + private boolean someBoolean; + private boolean anotherBoolean; + + public Constructor() { + } + + public Constructor(long id, boolean someBoolean, boolean anotherBoolean, Number someNumber, String someString) { + this.id = id; + this.someBoolean = someBoolean; + this.anotherBoolean = anotherBoolean; + this.someNumber = someNumber; + this.someString = someString; + ++CONSTRUCTOR_EXECUTION_COUNT; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof Constructor ) ) return false; + + Constructor that = (Constructor) o; + + if ( anotherBoolean != that.anotherBoolean ) return false; + if ( id != that.id ) return false; + if ( someBoolean != that.someBoolean ) return false; + if ( someNumber != null ? !someNumber.equals( that.someNumber ) : that.someNumber != null ) return false; + if ( someString != null ? !someString.equals( that.someString ) : that.someString != null ) return false; + + return true; + } + + @Override + public int hashCode() { + int result = (int) (id ^ (id >>> 32)); + result = 31 * result + (someString != null ? someString.hashCode() : 0); + result = 31 * result + (someNumber != null ? someNumber.hashCode() : 0); + result = 31 * result + (someBoolean ? 1 : 0); + result = 31 * result + (anotherBoolean ? 1 : 0); + return result; + } + + public boolean isSomeBoolean() { + return someBoolean; + } + + public void setSomeBoolean(boolean someBoolean) { + this.someBoolean = someBoolean; + } + + public Number getSomeNumber() { + return someNumber; + } + + public void setSomeNumber(Number someNumber) { + this.someNumber = someNumber; + } + + public String getSomeString() { + return someString; + } + + public void setSomeString(String someString) { + this.someString = someString; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public boolean isAnotherBoolean() { + return anotherBoolean; + } + + public void setAnotherBoolean(boolean anotherBoolean) { + this.anotherBoolean = anotherBoolean; + } + + public static int getConstructorExecutionCount() { + return CONSTRUCTOR_EXECUTION_COUNT; + } + + public static void resetConstructorExecutionCount() { + CONSTRUCTOR_EXECUTION_COUNT = 0; + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/QueryBuilderTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/QueryBuilderTest.java index d4200b4b2b..ffdbba50fd 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/QueryBuilderTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/QueryBuilderTest.java @@ -52,6 +52,8 @@ import org.hibernate.jpa.test.metamodel.ShelfLife; import org.hibernate.jpa.test.metamodel.Spouse; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; +import org.hibernate.testing.TestForIssue; + import static org.junit.Assert.assertEquals; /** @@ -214,6 +216,26 @@ public class QueryBuilderTest extends BaseEntityManagerFunctionalTestCase { em.close(); } + @Test + @TestForIssue( jiraKey = "HHH-8699" ) + public void testMultiselectWithPredicates() { + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + + CriteriaBuilderImpl cb = (CriteriaBuilderImpl) em.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery( Customer.class ); + Root r = cq.from( Customer.class ); + cq.multiselect( + r.get( Customer_.id ), r.get( Customer_.name ), + cb.concat( "Hello ", r.get( Customer_.name ) ), cb.isNotNull( r.get( Customer_.age ) ) + ); + TypedQuery tq = em.createQuery( cq ); + tq.getResultList(); + + em.getTransaction().commit(); + em.close(); + } + @Test public void testDateTimeFunctions() { EntityManager em = getOrCreateEntityManager(); diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Customer.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Customer.java index 81b1047614..907434ce34 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Customer.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Customer.java @@ -62,6 +62,12 @@ public class Customer implements java.io.Serializable { this.name = name; } + // Used by test case for HHH-8699. + public Customer(String id, String name, String greeting, Boolean something) { + this.id = id; + this.name = name; + } + public Customer(String id, String name, Country country) { this.id = id; this.name = name;