Skip to content

Commit 7b69aa6

Browse files
gderemetzgregturn
authored andcommitted
Detect alias for merge statement for JSqlParserQueryEnhancer.
Related: #2641.
1 parent 3a29011 commit 7b69aa6

File tree

2 files changed

+75
-37
lines changed

2 files changed

+75
-37
lines changed

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

+59-33
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,20 @@
1818
import static org.springframework.data.jpa.repository.query.JSqlParserUtils.*;
1919
import static org.springframework.data.jpa.repository.query.QueryUtils.*;
2020

21+
import java.util.ArrayList;
22+
import java.util.Collections;
23+
import java.util.HashSet;
24+
import java.util.List;
25+
import java.util.Objects;
26+
import java.util.Set;
27+
import java.util.stream.Collectors;
28+
29+
import org.springframework.data.domain.Sort;
30+
import org.springframework.lang.Nullable;
31+
import org.springframework.util.Assert;
32+
import org.springframework.util.CollectionUtils;
33+
import org.springframework.util.StringUtils;
34+
2135
import net.sf.jsqlparser.JSQLParserException;
2236
import net.sf.jsqlparser.expression.Alias;
2337
import net.sf.jsqlparser.expression.Expression;
@@ -26,8 +40,8 @@
2640
import net.sf.jsqlparser.schema.Column;
2741
import net.sf.jsqlparser.statement.Statement;
2842
import net.sf.jsqlparser.statement.delete.Delete;
29-
import net.sf.jsqlparser.statement.merge.Merge;
3043
import net.sf.jsqlparser.statement.insert.Insert;
44+
import net.sf.jsqlparser.statement.merge.Merge;
3145
import net.sf.jsqlparser.statement.select.OrderByElement;
3246
import net.sf.jsqlparser.statement.select.PlainSelect;
3347
import net.sf.jsqlparser.statement.select.Select;
@@ -39,20 +53,6 @@
3953
import net.sf.jsqlparser.statement.update.Update;
4054
import net.sf.jsqlparser.statement.values.ValuesStatement;
4155

42-
import java.util.ArrayList;
43-
import java.util.Collections;
44-
import java.util.HashSet;
45-
import java.util.List;
46-
import java.util.Objects;
47-
import java.util.Set;
48-
import java.util.stream.Collectors;
49-
50-
import org.springframework.data.domain.Sort;
51-
import org.springframework.lang.Nullable;
52-
import org.springframework.util.Assert;
53-
import org.springframework.util.CollectionUtils;
54-
import org.springframework.util.StringUtils;
55-
5656
/**
5757
* The implementation of {@link QueryEnhancer} using JSqlParser.
5858
*
@@ -304,24 +304,28 @@ public String detectAlias() {
304304
@Nullable
305305
private String detectAlias(String query) {
306306

307-
if (this.parsedType != ParsedType.SELECT) {
308-
return null;
309-
}
310-
311-
Select selectStatement = parseSelectStatement(query);
307+
if (ParsedType.MERGE.equals(this.parsedType)) {
308+
Merge mergeStatement = parseSelectStatement(query, Merge.class);
309+
return detectAlias(mergeStatement);
310+
311+
} else if (ParsedType.SELECT.equals(this.parsedType)) {
312+
Select selectStatement = parseSelectStatement(query);
313+
314+
/*
315+
For all the other types ({@link ValuesStatement} and {@link SetOperationList}) it does not make sense to provide
316+
alias since:
317+
* ValuesStatement has no alias
318+
* SetOperation can have multiple alias for each operation item
319+
*/
320+
if (!(selectStatement.getSelectBody() instanceof PlainSelect)) {
321+
return null;
322+
}
312323

313-
/*
314-
For all the other types ({@link ValuesStatement} and {@link SetOperationList}) it does not make sense to provide
315-
alias since:
316-
* ValuesStatement has no alias
317-
* SetOperation can have multiple alias for each operation item
318-
*/
319-
if (!(selectStatement.getSelectBody() instanceof PlainSelect)) {
320-
return null;
324+
PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody();
325+
return detectAlias(selectBody);
321326
}
322327

323-
PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody();
324-
return detectAlias(selectBody);
328+
return null;
325329
}
326330

327331
/**
@@ -332,7 +336,7 @@ For all the other types ({@link ValuesStatement} and {@link SetOperationList}) i
332336
* @return Might return {@literal null}.
333337
*/
334338
@Nullable
335-
private static String detectAlias(PlainSelect selectBody) {
339+
private String detectAlias(PlainSelect selectBody) {
336340

337341
if (selectBody.getFromItem() == null) {
338342
return null;
@@ -342,6 +346,18 @@ private static String detectAlias(PlainSelect selectBody) {
342346
return alias == null ? null : alias.getName();
343347
}
344348

349+
/**
350+
* Resolves the alias for the given {@link Merge} statement.
351+
*
352+
* @param mergeStatement must not be {@literal null}.
353+
* @return Might return {@literal null}.
354+
*/
355+
@Nullable
356+
private String detectAlias(Merge mergeStatement) {
357+
Alias alias = mergeStatement.getUsingAlias();
358+
return alias == null ? null : alias.getName();
359+
}
360+
345361
@Override
346362
public String createCountQueryFor(@Nullable String countProjection) {
347363

@@ -449,15 +465,25 @@ public Set<String> getJoinAliases() {
449465
* @param query the query to parse
450466
* @return the parsed query
451467
*/
452-
private static Select parseSelectStatement(String query) {
468+
private <T extends Statement> T parseSelectStatement(String query, Class<T> classOfT) {
453469

454470
try {
455-
return (Select) CCJSqlParserUtil.parse(query);
471+
return classOfT.cast(CCJSqlParserUtil.parse(query));
456472
} catch (JSQLParserException e) {
457473
throw new IllegalArgumentException("The query you provided is not a valid SQL Query", e);
458474
}
459475
}
460476

477+
/**
478+
* Parses a query string with JSqlParser.
479+
*
480+
* @param query the query to parse
481+
* @return the parsed query
482+
*/
483+
private Select parseSelectStatement(String query) {
484+
return parseSelectStatement(query, Select.class);
485+
}
486+
461487
/**
462488
* Checks whether a given projection only contains a single column definition (aka without functions, etc.)
463489
*

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

+16-4
Original file line numberDiff line numberDiff line change
@@ -921,14 +921,17 @@ void insertStatementIsProcessedSameAsDefault(String insertQuery) {
921921
assertThat(queryEnhancer.hasConstructorExpression()).isFalse();
922922
}
923923

924-
@Test // GH-2641
925-
void mergeStatementWorksWithJSqlParser() {
926-
String query = "merge into a using (select id, value from b) query on (a.id = query.id) when matched then update set a.value = value";
924+
@ParameterizedTest // GH-2641
925+
@MethodSource("mergeStatementWorksWithJSqlParserSource")
926+
void mergeStatementWorksWithJSqlParser(String query, String alias) {
927927
StringQuery stringQuery = new StringQuery(query, true);
928928
QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery);
929929

930+
assertThat(queryEnhancer.detectAlias()).isEqualTo(alias);
931+
assertThat(QueryUtils.detectAlias(query)).isNull();
932+
930933
assertThat(queryEnhancer.getJoinAliases()).isEmpty();
931-
assertThat(queryEnhancer.detectAlias()).isNull();
934+
assertThat(queryEnhancer.detectAlias()).isEqualTo(alias);
932935
assertThat(queryEnhancer.getProjection()).isEmpty();
933936
assertThat(queryEnhancer.hasConstructorExpression()).isFalse();
934937
}
@@ -940,6 +943,15 @@ public static Stream<Arguments> insertStatementIsProcessedSameAsDefaultSource()
940943
);
941944
}
942945

946+
public static Stream<Arguments> mergeStatementWorksWithJSqlParserSource() {
947+
return Stream.of(Arguments.of(
948+
"merge into a using (select id, value from b) query on (a.id = query.id) when matched then update set a.value = value",
949+
"query"),
950+
Arguments.of(
951+
"merge into a using (select id2, value from b) on (id = id2) when matched then update set a.value = value",
952+
null));
953+
}
954+
943955
public static Stream<Arguments> detectsJoinAliasesCorrectlySource() {
944956

945957
return Stream.of( //

0 commit comments

Comments
 (0)