diff --git a/pom.xml b/pom.xml index 17776328bc..304d3ef310 100755 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.3.0-SNAPSHOT + 3.3.x-3451-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 470678d048..d33f60279a 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.3.0-SNAPSHOT + 3.3.x-3451-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.3.0-SNAPSHOT + 3.3.x-3451-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 6bd074181c..f3c95c2f7b 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.3.0-SNAPSHOT + 3.3.x-3451-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 67ad4cabb2..cb787d9275 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.3.0-SNAPSHOT + 3.3.x-3451-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.3.0-SNAPSHOT + 3.3.x-3451-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Eql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Eql.g4 index 46748477a0..3ed025efb5 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Eql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Eql.g4 @@ -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 */ diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 6d6f7567d6..7984d4881b 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -737,6 +737,7 @@ reservedWord | EXCEPT | EXCLUDE | EXISTS + | EXP | EXTRACT | FETCH | FILTER diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 index b80e499ffa..a7f319b793 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -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 */ diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java index 22a10faee4..f3ecafe01e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java @@ -2331,7 +2331,7 @@ public List visitConstructor_name(EqlParser.Constructor_na List 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 @@ public List visitEntity_name(EqlParser.Entity_nameContext List 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 @@ public List visitCharacter_valued_input_parameter( return List.of(); } } + + @Override + public List 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(); + } + } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java index 1061e102c4..492215e48f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java @@ -20,6 +20,8 @@ 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 @@ public List visitConstructor_name(JpqlParser.Constructor_n List 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 @@ public List visitEntity_name(JpqlParser.Entity_nameContext List 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 @@ public List visitCharacter_valued_input_parameter( return List.of(); } } + + @Override + public List 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(); + } + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java index 9fd8a3a79b..ec11428a80 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java @@ -18,11 +18,15 @@ 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; /** @@ -53,6 +57,10 @@ private static String parseWithoutChanges(String query) { return render(new EqlQueryRenderer().visit(parsedQuery)); } + static Stream reservedWords() { + return Stream.of("abs", "exp", "any", "case", "else", "index", "time").map(Arguments::of); + } + private void assertQuery(String query) { String slimmedDownQuery = reduceWhitespace(query); @@ -995,25 +1003,25 @@ void powerShouldBeLegalInAQuery() { } @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); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index c879c26d82..6f617314ba 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -18,11 +18,15 @@ 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 @@ private static String parseWithoutChanges(String query) { return render(new HqlQueryRenderer().visit(parsedQuery)); } + static Stream reservedWords() { + return Stream.of("abs", "exp", "any", "case", "else", "index", "time").map(Arguments::of); + } + private void assertQuery(String query) { String slimmedDownQuery = reduceWhitespace(query); @@ -1638,25 +1646,25 @@ group by extract(epoch from departureTime) } @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") + void entityNameWithPackageContainingReservedWord(String reservedWord) { + + String source = "select new com.company.%s.thing.stuff.ClassName(e.id) from Experience e".formatted(reservedWord); + assertQuery(source); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index b8e2a55830..71836ed390 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -18,11 +18,15 @@ 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 @@ private static String parseWithoutChanges(String query) { return render(new JpqlQueryRenderer().visit(parsedQuery)); } + static Stream reservedWords() { + return Stream.of("abs", "exp", "any", "case", "else", "index", "time").map(Arguments::of); + } + private void assertQuery(String query) { String slimmedDownQuery = reduceWhitespace(query); @@ -996,25 +1004,26 @@ void powerShouldBeLegalInAQuery() { } @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") + void entityNameWithPackageContainingReservedWord(String reservedWord) { + + String source = "select new com.company.%s.thing.stuff.ClassName(e.id) from Experience e".formatted(reservedWord); + assertQuery(source); + } + }