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 568f5a235..c429687b7 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 @@ -692,7 +692,7 @@ reservedWord | FOR | FORMAT | FROM -// | FULL + | FULL | FUNCTION | GROUP | GROUPS @@ -704,7 +704,7 @@ reservedWord | IN | INDEX | INDICES -// | INNER + | INNER | INSERT | INSTANT | INTERSECT @@ -714,7 +714,7 @@ reservedWord | KEY | LAST | LEADING -// | LEFT + | LEFT | LIKE | LIMIT | LIST @@ -752,7 +752,7 @@ reservedWord | OR | ORDER | OTHERS -// | OUTER + | OUTER | OVER | OVERFLOW | OVERLAY @@ -765,7 +765,7 @@ reservedWord | QUARTER | RANGE | RESPECT -// | RIGHT + | RIGHT | ROLLUP | ROW | ROWS 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 2b5cc7ca4..9bad5f4c7 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 @@ -599,6 +599,9 @@ identification_variable | ORDER // Gap in the spec requires supporting 'Order' as an entity name | COUNT // Gap in the spec requires supporting 'count' as a possible name | KEY // Gap in the sepc requires supported 'key' as a possible name + | LEFT + | INNER + | OUTER ; constructor_name diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index 56cb2c042..f70b87540 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -18,9 +18,13 @@ package org.springframework.data.jpa.repository.query; import static org.assertj.core.api.Assertions.*; import java.util.regex.Pattern; +import java.util.stream.Stream; import org.assertj.core.api.SoftAssertions; 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.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.JpaSort; @@ -808,6 +812,30 @@ class HqlQueryTransformerTests { "select count(b) FROM BookError b WHERE portal = :portal"); } + @ParameterizedTest + @MethodSource("queriesWithReservedWordsAsIdentifiers") // GH-2864 + void usingReservedWordAsRelationshipNameShouldWork(String relationshipName, String joinAlias) { + + HqlQueryParser.parseQuery(String.format(""" + select u + from UserAccountEntity u + join fetch u.lossInspectorLimitConfiguration lil + join fetch u.companyTeam ct + where exists ( + select iu + from UserAccountEntity iu + join iu.roles u2r + join u2r.role r + join r.rights r2r + join r2r.%s %s + where + %s.code = :rightCode + and iu = u + ) + and ct.id = :teamId + """, relationshipName, joinAlias, joinAlias)); + } + @Test // GH-2508 void detectAliasWithCastCorrectly() { @@ -819,6 +847,16 @@ class HqlQueryTransformerTests { .isEqualTo("u"); } + static Stream queriesWithReservedWordsAsIdentifiers() { + + return Stream.of( // + Arguments.of("right", "rt"), // + Arguments.of("left", "lt"), // + Arguments.of("outer", "ou"), // + Arguments.of("full", "full"), // + Arguments.of("inner", "inr")); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java index d63cbd26d..8ebcfa83e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java @@ -18,9 +18,13 @@ package org.springframework.data.jpa.repository.query; import static org.assertj.core.api.Assertions.*; import java.util.regex.Pattern; +import java.util.stream.Stream; import org.assertj.core.api.SoftAssertions; 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.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.JpaSort; @@ -679,6 +683,40 @@ class JpqlQueryTransformerTests { assertThat(alias("select u from User as u left join u.roles as r")).isEqualTo("u"); } + @ParameterizedTest + @MethodSource("queriesWithReservedWordsAsIdentifiers") // GH-2864 + void usingReservedWordAsRelationshipNameShouldWork(String relationshipName, String joinAlias) { + + JpqlQueryParser.parseQuery(String.format(""" + select u + from UserAccountEntity u + join u.lossInspectorLimitConfiguration lil + join u.companyTeam ct + where exists ( + select iu + from UserAccountEntity iu + join iu.roles u2r + join u2r.role r + join r.rights r2r + join r2r.inner inr + where + inr.code = :rightCode + and iu = u + ) + and ct.id = :teamId + """, relationshipName, joinAlias, joinAlias)); + } + + static Stream queriesWithReservedWordsAsIdentifiers() { + + return Stream.of( // + Arguments.of("right", "rt"), // + Arguments.of("left", "lt"), // + Arguments.of("outer", "ou"), // + Arguments.of("full", "full"), // + Arguments.of("inner", "inr")); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); }