From dc714695e4c0d5435a281577a093ec829becd0d3 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 23 Feb 2017 09:31:48 +0000 Subject: [PATCH] HHH-11511 - QuerySyntaxException when sorting by a column using a JPQL reserved keyword --- hibernate-core/src/main/antlr/hql.g | 9 ++- .../hibernate/hql/internal/ast/HqlParser.java | 15 ++++ .../UseReservedKeywordInOrderByTest.java | 77 +++++++++++++++++++ 3 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/ordered/UseReservedKeywordInOrderByTest.java diff --git a/hibernate-core/src/main/antlr/hql.g b/hibernate-core/src/main/antlr/hql.g index 82cd8cb518..c15fe56f50 100644 --- a/hibernate-core/src/main/antlr/hql.g +++ b/hibernate-core/src/main/antlr/hql.g @@ -200,6 +200,9 @@ tokens public void firstPathTokenWeakKeywords() throws TokenStreamException { } + public void handlePrimaryExpressionDotIdent() throws TokenStreamException { + } + /** * Manages the case of an optional FROM allowing the path to start with the "from" keyword */ @@ -456,7 +459,7 @@ propertyFetch //## GROUP_BY path ( COMMA path )*; groupByClause - : GROUP^ + : GROUP^ "by"! expression ( COMMA! expression )* (havingClause)? ; @@ -706,7 +709,7 @@ quantifiedExpression // * method call ( '.' ident '(' exprList ') ) // * function : differentiated from method call via explicit keyword atom - : primaryExpression + : {handlePrimaryExpressionDotIdent();} primaryExpression ( DOT^ identifier ( options { greedy=true; } : @@ -785,7 +788,7 @@ vectorExpr // the method looks a head to find keywords after DOT and turns them into identifiers. identPrimary : i:identPrimaryBase { handleDotIdent(); } - ( options { greedy=true; } : DOT^ ( identifier | ELEMENTS | o:OBJECT { #o.setType(IDENT); } ) )* + ( options { greedy=true; } : DOT^ ( identifier { handleDotIdent(); } | ELEMENTS | o:OBJECT { #o.setType(IDENT); } ) )* ( options { greedy=true; } : ( op:OPEN^ { #op.setType(METHOD_CALL);} e:exprList CLOSE! ) { AST path = #e.getFirstChild(); diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlParser.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlParser.java index 932a136744..58836558ba 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlParser.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlParser.java @@ -389,6 +389,21 @@ public final class HqlParser extends HqlBaseParser { } } + @Override + public void handlePrimaryExpressionDotIdent() throws TokenStreamException { + if ( LA( 2 ) == DOT && LA( 3 ) != IDENT ) { + // See if the second lookahead token can be an identifier. + HqlToken t = (HqlToken) LT( 3 ); + if ( t.isPossibleID() ) { + // Set it! + t.setType( IDENT ); + if ( LOG.isDebugEnabled() ) { + LOG.debugf( "handleDotIdent() : new LT(3) token - %s", LT( 1 ) ); + } + } + } + } + @Override public void weakKeywords() throws TokenStreamException { diff --git a/hibernate-core/src/test/java/org/hibernate/test/ordered/UseReservedKeywordInOrderByTest.java b/hibernate-core/src/test/java/org/hibernate/test/ordered/UseReservedKeywordInOrderByTest.java new file mode 100644 index 0000000000..b270b1541b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/ordered/UseReservedKeywordInOrderByTest.java @@ -0,0 +1,77 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.ordered; + +import java.util.Date; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToOne; +import javax.persistence.Version; + +import org.hibernate.Session; + +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +/** + * @author Andrea Boriero + */ +public class UseReservedKeywordInOrderByTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[]{Person.class, Location.class}; + } + + @Test + public void testOrderBy(){ + try(Session s = openSession();){ + s.createQuery( "from Person p order by p.update" ); + } + } + + @Test + public void testMultipleOrderBy(){ + try(Session s = openSession();){ + s.createQuery( "from Person p order by p.name,p.update" ); + } + } + + @Test + public void testOrderByOfAssociationEntityField(){ + try(Session s = openSession();){ + s.createQuery( "from Person p order by p.location.update" ); + } + } + + @Entity(name = "Person") + public static class Person { + @Id + private Integer id; + + private String name; + + @Column(name = "update_date") + private Date update; + + @OneToOne + Location location; + } + + @Entity(name = "Location") + public static class Location{ + @Id + private Integer id; + + private String city; + + @Column(name = "update_date") + private Date update; + + } +}