Skip to content

Commit 26f19d0

Browse files
lukasz-antoniakbrmeyer
authored andcommitted
HHH-8699 - Multiselect with boolean predicate
1 parent 94753ea commit 26f19d0

File tree

9 files changed

+217
-2
lines changed

9 files changed

+217
-2
lines changed

hibernate-core/src/main/antlr/hql-sql.g

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ selectExpr
409409
| collectionFunction // elements() or indices()
410410
| literal
411411
| arithmeticExpr
412+
| logicalExpr
412413
| parameter
413414
| query
414415
;

hibernate-core/src/main/antlr/sql-gen.g

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ selectExpr
246246
| aggregate
247247
| c:constant { out(c); }
248248
| arithmeticExpr
249+
| selectBooleanExpr[false]
249250
| parameter
250251
| sn:SQL_NODE { out(sn); }
251252
| { out("("); } selectStatement { out(")"); }
@@ -306,6 +307,11 @@ booleanOp[ boolean parens ]
306307
| #(NOT { out(" not ("); } booleanExpr[false] { out(")"); } )
307308
;
308309
310+
selectBooleanExpr[ boolean parens ]
311+
: booleanOp [ parens ]
312+
| comparisonExpr [ parens ]
313+
;
314+
309315
booleanExpr[ boolean parens ]
310316
: booleanOp [ parens ]
311317
| comparisonExpr [ parens ]
@@ -387,6 +393,7 @@ simpleExpr
387393
| count
388394
| parameter
389395
| arithmeticExpr
396+
| selectBooleanExpr[false]
390397
;
391398
392399
constant

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/BinaryLogicOperatorNode.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.hibernate.TypeMismatchException;
3030
import org.hibernate.engine.spi.SessionFactoryImplementor;
3131
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
32+
import org.hibernate.hql.internal.ast.util.ColumnHelper;
3233
import org.hibernate.internal.util.StringHelper;
3334
import org.hibernate.param.ParameterSpecification;
3435
import org.hibernate.type.OneToOneType;
@@ -43,7 +44,7 @@
4344
*
4445
* @author Steve Ebersole
4546
*/
46-
public class BinaryLogicOperatorNode extends HqlSqlWalkerNode implements BinaryOperatorNode {
47+
public class BinaryLogicOperatorNode extends AbstractSelectExpression implements BinaryOperatorNode {
4748
/**
4849
* Performs the operator node initialization by seeking out any parameter
4950
* nodes and setting their expected type, if possible.
@@ -268,4 +269,8 @@ public Node getLeftHandOperand() {
268269
public Node getRightHandOperand() {
269270
return (Node) getFirstChild().getNextSibling();
270271
}
272+
273+
public void setScalarColumnText(int i) throws SemanticException {
274+
ColumnHelper.generateSingleScalarColumn( this, i );
275+
}
271276
}

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/UnaryLogicOperatorNode.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
*/
2424
package org.hibernate.hql.internal.ast.tree;
2525

26+
import antlr.SemanticException;
27+
28+
import org.hibernate.hql.internal.ast.util.ColumnHelper;
2629
import org.hibernate.type.StandardBasicTypes;
2730
import org.hibernate.type.Type;
2831

@@ -31,7 +34,7 @@
3134
*
3235
* @author Steve Ebersole
3336
*/
34-
public class UnaryLogicOperatorNode extends HqlSqlWalkerNode implements UnaryOperatorNode {
37+
public class UnaryLogicOperatorNode extends AbstractSelectExpression implements UnaryOperatorNode {
3538
public Node getOperand() {
3639
return (Node) getFirstChild();
3740
}
@@ -45,4 +48,8 @@ public Type getDataType() {
4548
// logic operators by definition resolve to booleans
4649
return StandardBasicTypes.BOOLEAN;
4750
}
51+
52+
public void setScalarColumnText(int i) throws SemanticException {
53+
ColumnHelper.generateSingleScalarColumn( this, i );
54+
}
4855
}

hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ public String[] getMappings() {
133133
"hql/Image.hbm.xml",
134134
"hql/ComponentContainer.hbm.xml",
135135
"hql/VariousKeywordPropertyEntity.hbm.xml",
136+
"hql/Constructor.hbm.xml",
136137
"batchfetch/ProductLine.hbm.xml",
137138
"cid/Customer.hbm.xml",
138139
"cid/Order.hbm.xml",
@@ -212,6 +213,31 @@ public void testExpandListParameter() {
212213
session.close();
213214
}
214215

216+
@Test
217+
@TestForIssue( jiraKey = "HHH-8699" )
218+
public void testBooleanPredicate() {
219+
final Session session = openSession();
220+
221+
session.getTransaction().begin();
222+
final Constructor constructor = new Constructor();
223+
session.save( constructor );
224+
session.getTransaction().commit();
225+
226+
session.clear();
227+
Constructor.resetConstructorExecutionCount();
228+
229+
session.getTransaction().begin();
230+
final Constructor result = (Constructor) session.createQuery(
231+
"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"
232+
).setParameter( "id", constructor.getId() ).uniqueResult();
233+
session.getTransaction().commit();
234+
235+
assertEquals( 1, Constructor.getConstructorExecutionCount() );
236+
assertEquals( new Constructor( constructor.getId(), true, true, constructor.getId() + 1, constructor.getId() + "foo" ), result );
237+
238+
session.close();
239+
}
240+
215241
@Test
216242
public void testJpaTypeOperator() {
217243
// just checking syntax here...
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0"?>
2+
<!DOCTYPE hibernate-mapping PUBLIC
3+
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
4+
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
5+
<hibernate-mapping package="org.hibernate.test.hql">
6+
<class name="Constructor">
7+
<id name="id" column="ID" type="long">
8+
<generator class="increment"/>
9+
</id>
10+
<property name="someString" type="string"/>
11+
<property name="someNumber" type="int"/>
12+
<property name="someBoolean" type="boolean"/>
13+
<property name="anotherBoolean" type="boolean"/>
14+
</class>
15+
</hibernate-mapping>
16+
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
5+
* indicated by the @author tags or express copyright attribution
6+
* statements applied by the authors. All third-party contributions are
7+
* distributed under license by Red Hat Inc.
8+
*
9+
* This copyrighted material is made available to anyone wishing to use, modify,
10+
* copy, or redistribute it subject to the terms and conditions of the GNU
11+
* Lesser General Public License, as published by the Free Software Foundation.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15+
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16+
* for more details.
17+
*
18+
* You should have received a copy of the GNU Lesser General Public License
19+
* along with this distribution; if not, write to:
20+
* Free Software Foundation, Inc.
21+
* 51 Franklin Street, Fifth Floor
22+
* Boston, MA 02110-1301 USA
23+
*/
24+
package org.hibernate.test.hql;
25+
26+
import java.io.Serializable;
27+
28+
/**
29+
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
30+
*/
31+
public class Constructor implements Serializable {
32+
private static int CONSTRUCTOR_EXECUTION_COUNT = 0;
33+
34+
private long id;
35+
private String someString;
36+
private Number someNumber;
37+
private boolean someBoolean;
38+
private boolean anotherBoolean;
39+
40+
public Constructor() {
41+
}
42+
43+
public Constructor(long id, boolean someBoolean, boolean anotherBoolean, Number someNumber, String someString) {
44+
this.id = id;
45+
this.someBoolean = someBoolean;
46+
this.anotherBoolean = anotherBoolean;
47+
this.someNumber = someNumber;
48+
this.someString = someString;
49+
++CONSTRUCTOR_EXECUTION_COUNT;
50+
}
51+
52+
@Override
53+
public boolean equals(Object o) {
54+
if ( this == o ) return true;
55+
if ( !( o instanceof Constructor ) ) return false;
56+
57+
Constructor that = (Constructor) o;
58+
59+
if ( anotherBoolean != that.anotherBoolean ) return false;
60+
if ( id != that.id ) return false;
61+
if ( someBoolean != that.someBoolean ) return false;
62+
if ( someNumber != null ? !someNumber.equals( that.someNumber ) : that.someNumber != null ) return false;
63+
if ( someString != null ? !someString.equals( that.someString ) : that.someString != null ) return false;
64+
65+
return true;
66+
}
67+
68+
@Override
69+
public int hashCode() {
70+
int result = (int) (id ^ (id >>> 32));
71+
result = 31 * result + (someString != null ? someString.hashCode() : 0);
72+
result = 31 * result + (someNumber != null ? someNumber.hashCode() : 0);
73+
result = 31 * result + (someBoolean ? 1 : 0);
74+
result = 31 * result + (anotherBoolean ? 1 : 0);
75+
return result;
76+
}
77+
78+
public boolean isSomeBoolean() {
79+
return someBoolean;
80+
}
81+
82+
public void setSomeBoolean(boolean someBoolean) {
83+
this.someBoolean = someBoolean;
84+
}
85+
86+
public Number getSomeNumber() {
87+
return someNumber;
88+
}
89+
90+
public void setSomeNumber(Number someNumber) {
91+
this.someNumber = someNumber;
92+
}
93+
94+
public String getSomeString() {
95+
return someString;
96+
}
97+
98+
public void setSomeString(String someString) {
99+
this.someString = someString;
100+
}
101+
102+
public long getId() {
103+
return id;
104+
}
105+
106+
public void setId(long id) {
107+
this.id = id;
108+
}
109+
110+
public boolean isAnotherBoolean() {
111+
return anotherBoolean;
112+
}
113+
114+
public void setAnotherBoolean(boolean anotherBoolean) {
115+
this.anotherBoolean = anotherBoolean;
116+
}
117+
118+
public static int getConstructorExecutionCount() {
119+
return CONSTRUCTOR_EXECUTION_COUNT;
120+
}
121+
122+
public static void resetConstructorExecutionCount() {
123+
CONSTRUCTOR_EXECUTION_COUNT = 0;
124+
}
125+
}

hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/QueryBuilderTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
import org.hibernate.jpa.test.metamodel.Spouse;
5353
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
5454

55+
import org.hibernate.testing.TestForIssue;
56+
5557
import static org.junit.Assert.assertEquals;
5658

5759
/**
@@ -214,6 +216,26 @@ public void testConstructor() {
214216
em.close();
215217
}
216218

219+
@Test
220+
@TestForIssue( jiraKey = "HHH-8699" )
221+
public void testMultiselectWithPredicates() {
222+
EntityManager em = getOrCreateEntityManager();
223+
em.getTransaction().begin();
224+
225+
CriteriaBuilderImpl cb = (CriteriaBuilderImpl) em.getCriteriaBuilder();
226+
CriteriaQuery<Customer> cq = cb.createQuery( Customer.class );
227+
Root<Customer> r = cq.from( Customer.class );
228+
cq.multiselect(
229+
r.get( Customer_.id ), r.get( Customer_.name ),
230+
cb.concat( "Hello ", r.get( Customer_.name ) ), cb.isNotNull( r.get( Customer_.age ) )
231+
);
232+
TypedQuery<Customer> tq = em.createQuery( cq );
233+
tq.getResultList();
234+
235+
em.getTransaction().commit();
236+
em.close();
237+
}
238+
217239
@Test
218240
public void testDateTimeFunctions() {
219241
EntityManager em = getOrCreateEntityManager();

hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Customer.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ public Customer(String id, String name) {
6262
this.name = name;
6363
}
6464

65+
// Used by test case for HHH-8699.
66+
public Customer(String id, String name, String greeting, Boolean something) {
67+
this.id = id;
68+
this.name = name;
69+
}
70+
6571
public Customer(String id, String name, Country country) {
6672
this.id = id;
6773
this.name = name;

0 commit comments

Comments
 (0)