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:
Mark Paluch
2024-04-29 14:17:26 +02:00
parent f3918d8349
commit 3eb9c4b096
5 changed files with 134 additions and 48 deletions

View File

@@ -638,7 +638,7 @@ identification_variable
;
constructor_name
: state_field_path_expression
: entity_name
;
literal
@@ -720,7 +720,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
@@ -748,6 +748,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
*/

View File

@@ -2331,7 +2331,7 @@ class EqlQueryRenderer extends EqlBaseVisitor<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;
@@ -2492,8 +2492,8 @@ class EqlQueryRenderer extends EqlBaseVisitor<List<JpaQueryParsingToken>> {
List<JpaQueryParsingToken> tokens = new ArrayList<>();
ctx.identification_variable().forEach(identificationVariableContext -> {
tokens.addAll(visit(identificationVariableContext));
ctx.reserved_word().forEach(identificationVariableContext -> {
tokens.addAll(visitReserved_word(identificationVariableContext));
NOSPACE(tokens);
tokens.add(TOKEN_DOT);
});
@@ -2543,4 +2543,15 @@ class EqlQueryRenderer extends EqlBaseVisitor<List<JpaQueryParsingToken>> {
return List.of();
}
}
@Override
public List<JpaQueryParsingToken> visitReserved_word(EqlParser.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();
}
}
}

View File

@@ -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;
/**
@@ -55,6 +59,10 @@ class EqlQueryRendererTests {
return render(new EqlQueryRenderer().visit(parsedQuery));
}
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);
@@ -997,25 +1005,25 @@ class EqlQueryRendererTests {
}
@ParameterizedTest // GH-3342
@ValueSource(strings = {
"select 1 from User u",
"select -1 from User u",
"select +1 from User u",
"select +1*-100 from User u",
"select count(u)*-0.7f from User u",
@ValueSource(strings = { "select 1 from User u", "select -1 from User u", "select +1 from User u",
"select +1*-100 from User u", "select count(u)*-0.7f from User u",
"select count(oi) + (-100) as perc from StockOrderItem oi",
"select p from Payment p where length(p.cardNumber) between +16 and -20"
})
"select p from Payment p where length(p.cardNumber) between +16 and -20" })
void signedLiteralShouldWork(String query) {
assertQuery(query);
}
@ParameterizedTest // GH-3342
@ValueSource(strings = {
"select -count(u) from User u",
"select +1*(-count(u)) from User u"
})
@ValueSource(strings = { "select -count(u) from User u", "select +1*(-count(u)) from User u" })
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);
}
}

View File

@@ -61,7 +61,7 @@ class HqlQueryRendererTests {
return render(new HqlQueryRenderer().visit(parsedQuery));
}
public static Stream<Arguments> reservedWords() {
static Stream<Arguments> reservedWords() {
return Stream.of("abs", "exp", "any", "case", "else", "index", "time").map(Arguments::of);
}
@@ -1648,30 +1648,22 @@ class HqlQueryRendererTests {
}
@ParameterizedTest // GH-3342
@ValueSource(strings = {
"select 1 from User",
"select -1 from User",
"select +1 from User",
"select +1*-100 from User",
"select count(u)*-0.7f from User u",
"select count(oi) + (-100) as perc from StockOrderItem oi",
"select p from Payment p where length(p.cardNumber) between +16 and -20"
})
@ValueSource(
strings = { "select 1 from User", "select -1 from User", "select +1 from User", "select +1*-100 from User",
"select count(u)*-0.7f from User u", "select count(oi) + (-100) as perc from StockOrderItem oi",
"select p from Payment p where length(p.cardNumber) between +16 and -20" })
void signedLiteralShouldWork(String query) {
assertQuery(query);
}
@ParameterizedTest // GH-3342
@ValueSource(strings = {
"select -count(u) from User u",
"select +1*(-count(u)) from User u"
})
@ValueSource(strings = { "select -count(u) from User u", "select +1*(-count(u)) from User u" })
void signedExpressionsShouldWork(String query) {
assertQuery(query);
}
@ParameterizedTest // GH-3451
@MethodSource({"reservedWords"})
@MethodSource("reservedWords")
void entityNameWithPackageContainingReservedWord(String reservedWord) {
String source = "select new com.company.%s.thing.stuff.ClassName(e.id) from Experience e".formatted(reservedWord);

View File

@@ -60,7 +60,7 @@ class JpqlQueryRendererTests {
return render(new JpqlQueryRenderer().visit(parsedQuery));
}
public static Stream<Arguments> reservedWords() {
static Stream<Arguments> reservedWords() {
return Stream.of("abs", "exp", "any", "case", "else", "index", "time").map(Arguments::of);
}
@@ -1006,35 +1006,26 @@ class JpqlQueryRendererTests {
}
@ParameterizedTest // GH-3342
@ValueSource(strings = {
"select 1 as value from User u",
"select -1 as value from User u",
"select +1 as value from User u",
"select +1*-100 as value from User u",
"select count(u)*-0.7f as value from User u",
"select count(oi) + (-100) as perc from StockOrderItem oi",
"select p from Payment p where length(p.cardNumber) between +16 and -20"
})
@ValueSource(strings = { "select 1 as value from User u", "select -1 as value from User u",
"select +1 as value from User u", "select +1*-100 as value from User u",
"select count(u)*-0.7f as value from User u", "select count(oi) + (-100) as perc from StockOrderItem oi",
"select p from Payment p where length(p.cardNumber) between +16 and -20" })
void signedLiteralShouldWork(String query) {
assertQuery(query);
}
@ParameterizedTest // GH-3342
@ValueSource(strings = {
"select -count(u) from User u",
"select +1*(-count(u)) from User u"
})
@ValueSource(strings = { "select -count(u) from User u", "select +1*(-count(u)) from User u" })
void signedExpressionsShouldWork(String query) {
assertQuery(query);
}
@ParameterizedTest // GH-3451
@MethodSource({"reservedWords"})
@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);
}
}