Skip to content

Commit 3d28143

Browse files
committed
Fix QueryUtils regex parsing field and function aliases.
Remove leading space requirement, simplify group nesting and replace character class with non-capturing group to avoid a, s and | (pipe) matching. Closes #3911
1 parent 98e801c commit 3d28143

File tree

2 files changed

+47
-22
lines changed

2 files changed

+47
-22
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -195,17 +195,15 @@ public abstract class QueryUtils {
195195
// any function call including parameters within the brackets
196196
builder.append("\\w+\\s*\\([\\w\\.,\\s'=:;\\\\?]+\\)");
197197
// the potential alias
198-
builder.append("\\s+[as|AS]+\\s+(([\\w\\.]+))");
198+
builder.append("\\s+(?:as|AS)+\\s+([\\w\\.]+)");
199199

200200
FUNCTION_PATTERN = compile(builder.toString());
201201

202202
builder = new StringBuilder();
203-
builder.append("\\s+"); // at least one space
204203
builder.append("[^\\s\\(\\)]+"); // No white char no bracket
205-
builder.append("\\s+[as|AS]+\\s+(([\\w\\.]+))"); // the potential alias
204+
builder.append("\\s+(?:as)+\\s+([\\w\\.]+)"); // the potential alias
206205

207206
FIELD_ALIAS_PATTERN = compile(builder.toString());
208-
209207
}
210208

211209
/**
@@ -391,7 +389,7 @@ static Set<String> getOuterJoinAliases(String query) {
391389
* @param query a {@literal String} containing a query. Must not be {@literal null}.
392390
* @return a {@literal Set} containing all found aliases. Guaranteed to be not {@literal null}.
393391
*/
394-
private static Set<String> getFieldAliases(String query) {
392+
static Set<String> getFieldAliases(String query) {
395393

396394
Set<String> result = new HashSet<>();
397395
Matcher matcher = FIELD_ALIAS_PATTERN.matcher(query);

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
* @author Erik Pellizzon
5252
* @author Pranav HS
5353
* @author Eduard Dudar
54+
* @author Mark Paluch
5455
*/
5556
class QueryUtilsUnitTests {
5657

@@ -130,13 +131,13 @@ void detectsAliasCorrectly() {
130131
.isEqualTo("u");
131132
assertThat(detectAlias(
132133
"from Foo f left join f.bar b with type(b) = BarChild where (f.id = (select max(f.id) from Foo f2 where type(f2) = FooChild) or 1 <> 1) and 1=1"))
133-
.isEqualTo("f");
134+
.isEqualTo("f");
134135
assertThat(detectAlias(
135136
"(from Foo f max(f) ((((select * from Foo f2 (from Foo f3) max(*)) (from Foo f4)) max(f5)) (f6)) (from Foo f7))"))
136-
.isEqualTo("f");
137+
.isEqualTo("f");
137138
assertThat(detectAlias(
138139
"SELECT e FROM DbEvent e WHERE (CAST(:modifiedFrom AS date) IS NULL OR e.modificationDate >= :modifiedFrom)"))
139-
.isEqualTo("e");
140+
.isEqualTo("e");
140141
assertThat(detectAlias("from User u where (cast(:effective as date) is null) OR :effective >= u.createdAt"))
141142
.isEqualTo("u");
142143
assertThat(detectAlias("from User u where (cast(:effectiveDate as date) is null) OR :effectiveDate >= u.createdAt"))
@@ -145,7 +146,7 @@ void detectsAliasCorrectly() {
145146
.isEqualTo("u");
146147
assertThat(
147148
detectAlias("from User u where (cast(:e1f2f3ectiveFrom as date) is null) OR :effectiveFrom >= u.createdAt"))
148-
.isEqualTo("u");
149+
.isEqualTo("u");
149150
}
150151

151152
@Test // GH-2260
@@ -175,13 +176,13 @@ void testRemoveSubqueries() throws Exception {
175176
.isEqualTo("(select u from User u where not exists )");
176177
assertThat(normalizeWhitespace(
177178
removeSubqueries("select u from User u where not exists (from User u2 where not exists (from User u3))")))
178-
.isEqualTo("select u from User u where not exists");
179+
.isEqualTo("select u from User u where not exists");
179180
assertThat(normalizeWhitespace(
180181
removeSubqueries("select u from User u where not exists ((from User u2 where not exists (from User u3)))")))
181-
.isEqualTo("select u from User u where not exists ( )");
182+
.isEqualTo("select u from User u where not exists ( )");
182183
assertThat(normalizeWhitespace(
183184
removeSubqueries("(select u from User u where not exists ((from User u2 where not exists (from User u3))))")))
184-
.isEqualTo("(select u from User u where not exists ( ))");
185+
.isEqualTo("(select u from User u where not exists ( ))");
185186
}
186187

187188
@Test // GH-2581
@@ -543,6 +544,32 @@ void doesNotPrefixAliasedFunctionCallNameWhenQueryStringContainsMultipleWhiteSpa
543544
assertThat(applySorting(query, sort, "m")).endsWith("order by avgPrice asc");
544545
}
545546

547+
@Test // GH-3911
548+
void discoversFunctionAliasesCorrectly() {
549+
550+
assertThat(getFunctionAliases("SELECT COUNT(1) a alias1,2 s alias2")).isEmpty();
551+
assertThat(getFunctionAliases("SELECT COUNT(1) as alias1,2 as alias2")).containsExactly("alias1");
552+
assertThat(getFunctionAliases("SELECT COUNT(1) as alias1,COUNT(2) as alias2")).contains("alias1", "alias2");
553+
assertThat(getFunctionAliases("SELECT COUNT(1) as alias1, 2 as alias2")).containsExactly("alias1");
554+
assertThat(getFunctionAliases("SELECT COUNT(1) as alias1, COUNT(2) as alias2")).contains("alias1", "alias2");
555+
assertThat(getFunctionAliases("COUNT(1) as alias1,COUNT(2) as alias2")).contains("alias1", "alias2");
556+
assertThat(getFunctionAliases("COUNT(1) as alias1,COUNT(2) as alias2")).contains("alias1", "alias2");
557+
assertThat(getFunctionAliases("1 as alias1, COUNT(2) as alias2")).containsExactly("alias2");
558+
assertThat(getFunctionAliases("1 as alias1, COUNT(2) as alias2")).containsExactly("alias2");
559+
assertThat(getFunctionAliases("COUNT(1) as alias1,2 as alias2")).containsExactly("alias1");
560+
assertThat(getFunctionAliases("COUNT(1) as alias1, 2 as alias2")).containsExactly("alias1");
561+
}
562+
563+
@Test // GH-3911
564+
void discoversFieldAliasesCorrectly() {
565+
566+
assertThat(getFieldAliases("SELECT 1 a alias1,2 s alias2")).isEmpty();
567+
assertThat(getFieldAliases("SELECT 1 as alias1,2 as alias2")).contains("alias1", "alias2");
568+
assertThat(getFieldAliases("SELECT 1 as alias1,2 as alias2")).contains("alias1", "alias2");
569+
assertThat(getFieldAliases("1 as alias1,2 as alias2")).contains("alias1", "alias2");
570+
assertThat(getFieldAliases("1 as alias1, 2 as alias2")).contains("alias1", "alias2");
571+
}
572+
546573
@Test // DATAJPA-1000
547574
void discoversCorrectAliasForJoinFetch() {
548575

@@ -564,7 +591,7 @@ void discoversAliasWithComplexFunction() {
564591

565592
assertThat(
566593
QueryUtils.getFunctionAliases("select new MyDto(sum(case when myEntity.prop3=0 then 1 else 0 end) as myAlias")) //
567-
.contains("myAlias");
594+
.contains("myAlias");
568595
}
569596

570597
@Test // DATAJPA-1506
@@ -784,18 +811,19 @@ void applySortingAccountsForNativeWindowFunction() {
784811
// order by in over clause + at the end
785812
assertThat(
786813
QueryUtils.applySorting("select dense_rank() over (order by lastname) from user u order by u.lastname", sort))
787-
.isEqualTo("select dense_rank() over (order by lastname) from user u order by u.lastname, u.age desc");
814+
.isEqualTo("select dense_rank() over (order by lastname) from user u order by u.lastname, u.age desc");
788815

789816
// partition by + order by in over clause
790-
assertThat(QueryUtils.applySorting(
791-
"select dense_rank() over (partition by active, age order by lastname) from user u", sort)).isEqualTo(
817+
assertThat(QueryUtils
818+
.applySorting("select dense_rank() over (partition by active, age order by lastname) from user u", sort))
819+
.isEqualTo(
792820
"select dense_rank() over (partition by active, age order by lastname) from user u order by u.age desc");
793821

794822
// partition by + order by in over clause + order by at the end
795823
assertThat(QueryUtils.applySorting(
796824
"select dense_rank() over (partition by active, age order by lastname) from user u order by active", sort))
797-
.isEqualTo(
798-
"select dense_rank() over (partition by active, age order by lastname) from user u order by active, u.age desc");
825+
.isEqualTo(
826+
"select dense_rank() over (partition by active, age order by lastname) from user u order by active, u.age desc");
799827

800828
// partition by + order by in over clause + frame clause
801829
assertThat(QueryUtils.applySorting(
@@ -812,8 +840,7 @@ void applySortingAccountsForNativeWindowFunction() {
812840
// order by in subselect (select expression)
813841
assertThat(
814842
QueryUtils.applySorting("select lastname, (select i.id from item i order by i.id limit 1) from user u", sort))
815-
.isEqualTo(
816-
"select lastname, (select i.id from item i order by i.id limit 1) from user u order by u.age desc");
843+
.isEqualTo("select lastname, (select i.id from item i order by i.id limit 1) from user u order by u.age desc");
817844

818845
// order by in subselect (select expression) + at the end
819846
assertThat(QueryUtils.applySorting(
@@ -949,7 +976,7 @@ select q.specialist_id, listagg(q.points, '%s') as points
949976

950977
@Test // GH-3324
951978
void createCountQueryForSimpleQuery() {
952-
assertCountQuery("select * from User","select count(*) from User");
953-
assertCountQuery("select * from User u","select count(u) from User u");
979+
assertCountQuery("select * from User", "select count(*) from User");
980+
assertCountQuery("select * from User u", "select count(u) from User u");
954981
}
955982
}

0 commit comments

Comments
 (0)