Fix parsing entity names in JPQL query with package names that contain reserved words.
Resolves: #3451 Original pull request: #3457
This commit is contained in:
committed by
Mark Paluch
parent
71ca324c99
commit
4c263f15f8
@@ -616,7 +616,7 @@ identification_variable
|
||||
;
|
||||
|
||||
constructor_name
|
||||
: state_field_path_expression
|
||||
: entity_name
|
||||
;
|
||||
|
||||
literal
|
||||
@@ -696,7 +696,7 @@ collection_value_field
|
||||
;
|
||||
|
||||
entity_name
|
||||
: identification_variable ('.' identification_variable)* // Hibernate sometimes expands the entity name to FQDN when using named queries
|
||||
: reserved_word ('.' reserved_word)* // Hibernate sometimes expands the entity name to FQDN when using named queries
|
||||
;
|
||||
|
||||
result_variable
|
||||
@@ -724,6 +724,90 @@ character_valued_input_parameter
|
||||
| input_parameter
|
||||
;
|
||||
|
||||
reserved_word
|
||||
: IDENTIFICATION_VARIABLE
|
||||
| f=(ABS
|
||||
|ALL
|
||||
|AND
|
||||
|ANY
|
||||
|AS
|
||||
|ASC
|
||||
|AVG
|
||||
|BETWEEN
|
||||
|BOTH
|
||||
|BY
|
||||
|CASE
|
||||
|CEILING
|
||||
|COALESCE
|
||||
|CONCAT
|
||||
|COUNT
|
||||
|CURRENT_DATE
|
||||
|CURRENT_TIME
|
||||
|CURRENT_TIMESTAMP
|
||||
|DATE
|
||||
|DATETIME
|
||||
|DELETE
|
||||
|DESC
|
||||
|DISTINCT
|
||||
|END
|
||||
|ELSE
|
||||
|EMPTY
|
||||
|ENTRY
|
||||
|ESCAPE
|
||||
|EXISTS
|
||||
|EXP
|
||||
|EXTRACT
|
||||
|FALSE
|
||||
|FETCH
|
||||
|FLOOR
|
||||
|FUNCTION
|
||||
|IN
|
||||
|INDEX
|
||||
|INNER
|
||||
|IS
|
||||
|KEY
|
||||
|LEFT
|
||||
|LENGTH
|
||||
|LIKE
|
||||
|LN
|
||||
|LOCAL
|
||||
|LOCATE
|
||||
|LOWER
|
||||
|MAX
|
||||
|MEMBER
|
||||
|MIN
|
||||
|MOD
|
||||
|NEW
|
||||
|NOT
|
||||
|NULL
|
||||
|NULLIF
|
||||
|OBJECT
|
||||
|OF
|
||||
|ON
|
||||
|OR
|
||||
|ORDER
|
||||
|OUTER
|
||||
|POWER
|
||||
|ROUND
|
||||
|SELECT
|
||||
|SET
|
||||
|SIGN
|
||||
|SIZE
|
||||
|SOME
|
||||
|SQRT
|
||||
|SUBSTRING
|
||||
|SUM
|
||||
|THEN
|
||||
|TIME
|
||||
|TRAILING
|
||||
|TREAT
|
||||
|TRIM
|
||||
|TRUE
|
||||
|TYPE
|
||||
|UPDATE
|
||||
|UPPER
|
||||
|VALUE)
|
||||
;
|
||||
/*
|
||||
Lexer rules
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,8 @@ import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.query.JpqlParser.Reserved_wordContext;
|
||||
|
||||
/**
|
||||
* An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that renders a JPQL query without making any changes.
|
||||
*
|
||||
@@ -2143,7 +2145,7 @@ class JpqlQueryRenderer extends JpqlBaseVisitor<List<JpaQueryParsingToken>> {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
|
||||
tokens.addAll(visit(ctx.state_field_path_expression()));
|
||||
tokens.addAll(visit(ctx.entity_name()));
|
||||
NOSPACE(tokens);
|
||||
|
||||
return tokens;
|
||||
@@ -2296,8 +2298,8 @@ class JpqlQueryRenderer extends JpqlBaseVisitor<List<JpaQueryParsingToken>> {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
|
||||
ctx.identification_variable().forEach(identificationVariableContext -> {
|
||||
tokens.addAll(visit(identificationVariableContext));
|
||||
ctx.reserved_word().forEach(ctx2 -> {
|
||||
tokens.addAll(visitReserved_word(ctx2));
|
||||
NOSPACE(tokens);
|
||||
tokens.add(TOKEN_DOT);
|
||||
});
|
||||
@@ -2347,4 +2349,15 @@ class JpqlQueryRenderer extends JpqlBaseVisitor<List<JpaQueryParsingToken>> {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitReserved_word(Reserved_wordContext ctx) {
|
||||
if (ctx.IDENTIFICATION_VARIABLE() != null) {
|
||||
return List.of(new JpaQueryParsingToken(ctx.IDENTIFICATION_VARIABLE()));
|
||||
} else if (ctx.f != null) {
|
||||
return List.of(new JpaQueryParsingToken(ctx.f));
|
||||
} else {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,15 @@ package org.springframework.data.jpa.repository.query;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
/**
|
||||
@@ -54,6 +58,10 @@ class JpqlQueryRendererTests {
|
||||
return render(new JpqlQueryRenderer().visit(parsedQuery));
|
||||
}
|
||||
|
||||
public static Stream<Arguments> reservedWords() {
|
||||
return Stream.of("abs", "exp", "any", "case", "else", "index", "time").map(Arguments::of);
|
||||
}
|
||||
|
||||
private void assertQuery(String query) {
|
||||
|
||||
String slimmedDownQuery = reduceWhitespace(query);
|
||||
@@ -1017,4 +1025,14 @@ class JpqlQueryRendererTests {
|
||||
void signedExpressionsShouldWork(String query) {
|
||||
assertQuery(query);
|
||||
}
|
||||
|
||||
@ParameterizedTest // GH-3451
|
||||
@MethodSource({"reservedWords"})
|
||||
void entityNameWithPackageContainingReservedWord(String reservedWord) {
|
||||
|
||||
String source = "select new com.company.%s.thing.stuff.ClassName(e.id) from Experience e".formatted(reservedWord);
|
||||
assertQuery(source);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user