Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using the by keyword on a duration object causes a BadJpqlGrammarException #3757

Closed
rhpkegel opened this issue Jan 29, 2025 · 2 comments
Closed
Assignees
Labels
type: regression A regression from a previous release

Comments

@rhpkegel
Copy link

rhpkegel commented Jan 29, 2025

We recently migrated from Spring boot 3.3.5 to 3.4.0, which upgrades the underlying Spring Data and Hibernate dependencies. However, we encounter a BadJpqlGrammarException when booting up the application, as the following method in a JPARepository (simplified) fails to parse:

@Query(value = "SELECT a FROM afspraak a WHERE (cast(a.startDatumTijd as date) - CURRENT_DATE) BY day - 2 = 0") Set<Afspraak> springDataParserError();

A minimal example of the entity in question:

@Entity(name = "afspraak")
public class Afspraak
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private Date startDatumTijd;
}

A truncated stack trace for convenience:

Caused by: org.springframework.data.jpa.repository.query.BadJpqlGrammarException: Line 1:79 mismatched input 'BY' expecting {')', '+', '-', '/', '||', AND, '*', BY, DAY, EPOCH, HOUR, MINUTE, MONTH, NANOSECOND, OR, QUARTER, SECOND, WEEK, YEAR}; Bad JPQL grammar [SELECT a FROM afspraak a WHERE (cast(a.startDatumTijd as date) - CURRENT_DATE) BY day - 2 = 0]
	at org.springframework.data.jpa.repository.query.BadJpqlGrammarErrorListener.syntaxError(BadJpqlGrammarErrorListener.java:39) ~[spring-data-jpa-3.4.0.jar:3.4.0]
	at org.antlr.v4.runtime.ProxyErrorListener.syntaxError(ProxyErrorListener.java:41) ~[antlr4-runtime-4.13.0.jar:4.13.0]
	at org.antlr.v4.runtime.Parser.notifyErrorListeners(Parser.java:544) ~[antlr4-runtime-4.13.0.jar:4.13.0]
	at org.antlr.v4.runtime.DefaultErrorStrategy.reportInputMismatch(DefaultErrorStrategy.java:327) ~[antlr4-runtime-4.13.0.jar:4.13.0]
	at org.antlr.v4.runtime.DefaultErrorStrategy.reportError(DefaultErrorStrategy.java:139) ~[antlr4-runtime-4.13.0.jar:4.13.0]
	at org.springframework.data.jpa.repository.query.HqlParser.start(HqlParser.java:266) ~[spring-data-jpa-3.4.0.jar:3.4.0]
	at org.springframework.data.jpa.repository.query.JpaQueryEnhancer.parse(JpaQueryEnhancer.java:76) ~[spring-data-jpa-3.4.0.jar:3.4.0]
	at org.springframework.data.jpa.repository.query.JpaQueryEnhancer$HqlQueryParser.<init>(JpaQueryEnhancer.java:240) ~[spring-data-jpa-3.4.0.jar:3.4.0]
	at org.springframework.data.jpa.repository.query.JpaQueryEnhancer$HqlQueryParser.parseQuery(JpaQueryEnhancer.java:252) ~[spring-data-jpa-3.4.0.jar:3.4.0]
	at org.springframework.data.jpa.repository.query.JpaQueryEnhancer.forHql(JpaQueryEnhancer.java:122) ~[spring-data-jpa-3.4.0.jar:3.4.0]
	at org.springframework.data.jpa.repository.query.QueryEnhancerFactory.forQuery(QueryEnhancerFactory.java:68) ~[spring-data-jpa-3.4.0.jar:3.4.0]
	at org.springframework.data.jpa.repository.query.StringQuery.<init>(StringQuery.java:88) ~[spring-data-jpa-3.4.0.jar:3.4.0]
	at org.springframework.data.jpa.repository.query.ExpressionBasedStringQuery.<init>(ExpressionBasedStringQuery.java:65) ~[spring-data-jpa-3.4.0.jar:3.4.0]
	at org.springframework.data.jpa.repository.query.AbstractStringBasedJpaQuery.<init>(AbstractStringBasedJpaQuery.java:84) ~[spring-data-jpa-3.4.0.jar:3.4.0]
	at org.springframework.data.jpa.repository.query.SimpleJpaQuery.<init>(SimpleJpaQuery.java:65) ~[spring-data-jpa-3.4.0.jar:3.4.0]
	at org.springframework.data.jpa.repository.query.JpaQueryFactory.fromMethodWithQueryString(JpaQueryFactory.java:49) ~[spring-data-jpa-3.4.0.jar:3.4.0]

According to the documentation at https://docs.jboss.org/hibernate/orm/6.6/querylanguage/html_single/Hibernate_Query_Language.html#Datetime-arithmetic this should still be valid syntax according to Hibernate 6.6, and it did in fact compile and run on Spring Boot 3.3.5.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jan 29, 2025
@mp911de mp911de self-assigned this Jan 29, 2025
@mp911de mp911de added type: regression A regression from a previous release and removed status: waiting-for-triage An issue we've not yet triaged labels Jan 29, 2025
@mp911de
Copy link
Member

mp911de commented Jan 29, 2025

This is a problem on our side as we're parsing declared queries much earlier now, resulting in early parsing errors in case our HQL parser isn't fully aligned (which is the case here). Thanks for the report.

@mp911de
Copy link
Member

mp911de commented Jan 30, 2025

It turns out that some internal issues with grammar are causing this issue. They have some ambiguity leading to parser ambiguities in the SLL mode, which is fast, but it doesn't consider contextual parsing information to resolve ambiguities. These then lead parser failures.

Trying to parse the query with a slower, but context-aware LL model fixes the issue. However, there is nothing really you can do so we need to ship a fix.

@mp911de mp911de added this to the 3.4.3 (2024.1.3) milestone Jan 30, 2025
mp911de added a commit that referenced this issue Jan 30, 2025
Refine HQL rendering for duration expressions.

Retain whitespace for error message reconstruction, refine error handling.

See #3757
mp911de added a commit that referenced this issue Jan 30, 2025
…tion.

Due to grammar ambiguities, we fall back to LL prediction considering contextual ambiguity resolution.

Closes #3757
mp911de added a commit that referenced this issue Jan 30, 2025
Refine HQL rendering for duration expressions.

Retain whitespace for error message reconstruction, refine error handling.

See #3757
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: regression A regression from a previous release
Projects
None yet
Development

No branches or pull requests

3 participants