HHH-6686 fix JQL exception in face of 'empty'

This commit is contained in:
Nathan Xu 2020-01-24 23:15:59 -05:00 committed by Andrea Boriero
parent f1d1e62478
commit a6934467f7
3 changed files with 108 additions and 10 deletions

View File

@ -18,7 +18,6 @@ import java.util.Set;
import org.hibernate.QueryException; import org.hibernate.QueryException;
import org.hibernate.hql.internal.antlr.HqlBaseParser; import org.hibernate.hql.internal.antlr.HqlBaseParser;
import org.hibernate.hql.internal.antlr.HqlTokenTypes; import org.hibernate.hql.internal.antlr.HqlTokenTypes;
import org.hibernate.hql.internal.ast.util.ASTPrinter;
import org.hibernate.hql.internal.ast.util.ASTUtil; import org.hibernate.hql.internal.ast.util.ASTUtil;
import org.hibernate.hql.internal.ast.util.TokenPrinters; import org.hibernate.hql.internal.ast.util.TokenPrinters;
import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreLogging;
@ -116,7 +115,7 @@ public final class HqlParser extends HqlBaseParser {
* @param token The token. * @param token The token.
* @param ex The recognition exception. * @param ex The recognition exception.
* *
* @return AST - The new AST. * @return The new AST.
* *
* @throws antlr.RecognitionException if the substitution was not possible. * @throws antlr.RecognitionException if the substitution was not possible.
* @throws antlr.TokenStreamException if the substitution was not possible. * @throws antlr.TokenStreamException if the substitution was not possible.
@ -159,7 +158,7 @@ public final class HqlParser extends HqlBaseParser {
* *
* @param x The sub tree to transform, the parent is assumed to be NOT. * @param x The sub tree to transform, the parent is assumed to be NOT.
* *
* @return AST - The equivalent sub-tree. * @return The equivalent sub-tree.
*/ */
@Override @Override
public AST negateNode(AST x) { public AST negateNode(AST x) {
@ -286,7 +285,7 @@ public final class HqlParser extends HqlBaseParser {
* *
* @param x The equality expression. * @param x The equality expression.
* *
* @return AST - The clean sub-tree. * @return The clean sub-tree.
*/ */
@Override @Override
public AST processEqualityExpression(AST x) { public AST processEqualityExpression(AST x) {
@ -348,6 +347,10 @@ public final class HqlParser extends HqlBaseParser {
private AST createSubquery(AST node) { private AST createSubquery(AST node) {
AST ast = ASTUtil.createParent( astFactory, RANGE, "RANGE", node ); AST ast = ASTUtil.createParent( astFactory, RANGE, "RANGE", node );
ast = ASTUtil.createParent( astFactory, FROM, "from", ast ); ast = ASTUtil.createParent( astFactory, FROM, "from", ast );
AST alias = ASTUtil.createSibling( astFactory, ALIAS, "_", node );
ASTUtil.insertChild( ASTUtil.createSibling( astFactory, SELECT, "select", ast ), astFactory.create(IDENT, alias.getText() ) );
ast = ASTUtil.createParent( astFactory, SELECT_FROM, "SELECT_FROM", ast ); ast = ASTUtil.createParent( astFactory, SELECT_FROM, "SELECT_FROM", ast );
ast = ASTUtil.createParent( astFactory, QUERY, "QUERY", ast ); ast = ASTUtil.createParent( astFactory, QUERY, "QUERY", ast );
return ast; return ast;
@ -489,12 +492,12 @@ public final class HqlParser extends HqlBaseParser {
LOG.debugf( "Registering discovered request to treat(%s as %s)", path, subclassName ); LOG.debugf( "Registering discovered request to treat(%s as %s)", path, subclassName );
if ( treatMap == null ) { if ( treatMap == null ) {
treatMap = new HashMap<String, Set<String>>(); treatMap = new HashMap<>();
} }
Set<String> subclassNames = treatMap.get( path ); Set<String> subclassNames = treatMap.get( path );
if ( subclassNames == null ) { if ( subclassNames == null ) {
subclassNames = new HashSet<String>(); subclassNames = new HashSet<>();
treatMap.put( path, subclassNames ); treatMap.put( path, subclassNames );
} }
subclassNames.add( subclassName ); subclassNames.add( subclassName );
@ -512,11 +515,11 @@ public final class HqlParser extends HqlBaseParser {
} }
public Map<String, Set<String>> getTreatMap() { public Map<String, Set<String>> getTreatMap() {
return treatMap == null ? Collections.<String, Set<String>>emptyMap() : treatMap; return treatMap == null ? Collections.emptyMap() : treatMap;
} }
public static void panic() { public static void panic() {
//overriden to avoid System.exit //overridden to avoid System.exit
throw new QueryException( "Parser: panic" ); throw new QueryException( "Parser: panic" );
} }
} }

View File

@ -72,7 +72,6 @@ public class PluralAttributeExpressionsTest extends AbstractMetamodelSpecificTes
@Test @Test
@TestForIssue( jiraKey = "HHH-11225" ) @TestForIssue( jiraKey = "HHH-11225" )
@FailureExpected( jiraKey = "HHH-6686")
public void testElementMapIsEmptyHql() { public void testElementMapIsEmptyHql() {
doInJPA( this::entityManagerFactory, entityManager -> { doInJPA( this::entityManagerFactory, entityManager -> {
entityManager.createQuery( "select m from MapEntity m where m.localized is empty" ).getResultList(); entityManager.createQuery( "select m from MapEntity m where m.localized is empty" ).getResultList();
@ -81,7 +80,6 @@ public class PluralAttributeExpressionsTest extends AbstractMetamodelSpecificTes
@Test @Test
@TestForIssue( jiraKey = "HHH-11225" ) @TestForIssue( jiraKey = "HHH-11225" )
@FailureExpected( jiraKey = "HHH-6686")
public void testElementMapIsEmptyCriteria() { public void testElementMapIsEmptyCriteria() {
doInJPA( this::entityManagerFactory, entityManager -> { doInJPA( this::entityManagerFactory, entityManager -> {
final HibernateCriteriaBuilder cb = (HibernateCriteriaBuilder) entityManager.getCriteriaBuilder(); final HibernateCriteriaBuilder cb = (HibernateCriteriaBuilder) entityManager.getCriteriaBuilder();

View File

@ -0,0 +1,97 @@
package org.hibernate.query;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import javax.persistence.*;
import java.util.*;
import java.util.stream.Collectors;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
/**
* @author Nathan Xu
*/
@TestForIssue(jiraKey = "HHH-6686")
public class IsEmptyJQLTest extends BaseEntityManagerFunctionalTestCase {
private Long personWithoutNicknameId = 1L;
private Long personaWithSingleNicknameId = 2L;
private Long personWithMultipleNicknamesId = 3L;
@Override
public Class<?>[] getAnnotatedClasses() {
return new Class[] { Person.class };
}
@Test
public void testJQLContainingEmpty() {
List<Person> personWithNicknames = doInJPA( this::entityManagerFactory, entityManager -> {
return entityManager.createQuery(
"select p from Person p where p.nicknames is not empty", Person.class )
.getResultList();
});
assertEquals( new HashSet<>( Arrays.asList(personaWithSingleNicknameId, personWithMultipleNicknamesId)),
personWithNicknames.stream().map( Person::getId ).collect( Collectors.toSet() ));
List<Person> personWithOutNickname = doInJPA( this::entityManagerFactory, entityManager -> {
return entityManager.createQuery(
"select p from Person p where p.nicknames is empty", Person.class )
.getResultList();
});
assertEquals( Collections.singleton(personWithoutNicknameId),
personWithOutNickname.stream().map( Person::getId ).collect( Collectors.toSet() ));
}
@Override
protected void afterEntityManagerFactoryBuilt() {
doInJPA( this::entityManagerFactory, entityManager -> {
Person personaWithoutNickname = new Person();
personaWithoutNickname.setId(personWithoutNicknameId);
Person personaWithSingleNickname = new Person();
personaWithSingleNickname.getNicknames().add( "nickname" );
personaWithSingleNickname.setId(personaWithSingleNicknameId);
Person personWithMultipleNicknames = new Person();
personWithMultipleNicknames.getNicknames().addAll( Arrays.asList( "nickName1", "nickName2" ) );
personWithMultipleNicknames.setId(personWithMultipleNicknamesId);
entityManager.persist( personaWithoutNickname );
entityManager.persist( personaWithSingleNickname );
entityManager.persist( personWithMultipleNicknames );
} );
}
@Entity(name = "Person")
public static class Person {
@Id
private Long id;
@ElementCollection
private List<String> nicknames = new ArrayList<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<String> getNicknames() {
return nicknames;
}
public void setNicknames(List<String> nicknames) {
this.nicknames = nicknames;
}
}
}