Skip to content

Commit 5743888

Browse files
Use early exit in QueryTransformers and introspectors.
Avoid iterating tokens when things had been discovered already and reduce visibility of StringQuery to previous value. See: #3309
1 parent 0a5a56f commit 5743888

File tree

7 files changed

+176
-146
lines changed

7 files changed

+176
-146
lines changed

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

+29-27
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
*/
1616
package org.springframework.data.jpa.repository.query;
1717

18-
import static org.springframework.data.jpa.repository.query.QueryTokens.*;
18+
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_CLOSE_PAREN;
19+
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_COMMA;
20+
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_COUNT_FUNC;
1921

2022
import org.springframework.data.jpa.repository.query.QueryRenderer.QueryRendererBuilder;
2123
import org.springframework.data.jpa.repository.query.QueryTransformers.CountSelectionTokenStream;
@@ -27,6 +29,7 @@
2729
*
2830
* @author Greg Turnquist
2931
* @author Mark Paluch
32+
* @author Christoph Strobl
3033
* @since 3.4
3134
*/
3235
@SuppressWarnings("ConstantValue")
@@ -36,6 +39,7 @@ class EqlCountQueryTransformer extends EqlQueryRenderer {
3639
private final @Nullable String primaryFromAlias;
3740

3841
EqlCountQueryTransformer(@Nullable String countProjection, @Nullable String primaryFromAlias) {
42+
3943
this.countProjection = countProjection;
4044
this.primaryFromAlias = primaryFromAlias;
4145
}
@@ -64,47 +68,45 @@ public QueryRendererBuilder visitSelect_statement(EqlParser.Select_statementCont
6468
@Override
6569
public QueryTokenStream visitSelect_clause(EqlParser.Select_clauseContext ctx) {
6670

71+
boolean usesDistinct = ctx.DISTINCT() != null;
72+
6773
QueryRendererBuilder builder = QueryRenderer.builder();
6874

6975
builder.append(QueryTokens.expression(ctx.SELECT()));
7076
builder.append(TOKEN_COUNT_FUNC);
7177

72-
if (countProjection != null) {
73-
builder.append(QueryTokens.token(countProjection));
74-
}
75-
7678
QueryRendererBuilder nested = QueryRenderer.builder();
77-
78-
if (ctx.DISTINCT() != null) {
79-
nested.append(QueryTokens.expression(ctx.DISTINCT()));
80-
}
81-
8279
if (countProjection == null) {
83-
84-
if (ctx.DISTINCT() != null) {
85-
86-
QueryTokenStream selectionListbuilder = QueryTokenStream.concat(ctx.select_item(), this::visit,
87-
TOKEN_COMMA);
88-
89-
CountSelectionTokenStream countSelection = QueryTransformers
90-
.filterCountSelection(selectionListbuilder);
91-
92-
if (countSelection.requiresPrimaryAlias()) {
93-
// constructor
94-
nested.append(QueryTokens.token(primaryFromAlias));
95-
} else {
96-
// keep all the select items to distinct against
97-
nested.append(countSelection);
98-
}
80+
if (usesDistinct) {
81+
nested.append(QueryTokens.expression(ctx.DISTINCT()));
82+
nested.append(getDistinctCountSelection(QueryTokenStream.concat(ctx.select_item(), this::visit, TOKEN_COMMA)));
9983
} else {
10084
nested.append(QueryTokens.token(primaryFromAlias));
10185
}
86+
} else {
87+
builder.append(QueryTokens.token(countProjection));
88+
if (usesDistinct) {
89+
nested.append(QueryTokens.expression(ctx.DISTINCT()));
90+
}
10291
}
10392

10493
builder.appendInline(nested);
10594
builder.append(TOKEN_CLOSE_PAREN);
106-
10795
return builder;
10896
}
10997

98+
private QueryRendererBuilder getDistinctCountSelection(QueryTokenStream selectionListbuilder) {
99+
100+
QueryRendererBuilder nested = new QueryRendererBuilder();
101+
CountSelectionTokenStream countSelection = QueryTransformers.filterCountSelection(selectionListbuilder);
102+
103+
if (countSelection.requiresPrimaryAlias()) {
104+
// constructor
105+
nested.append(QueryTokens.token(primaryFromAlias));
106+
} else {
107+
// keep all the select items to distinct against
108+
nested.append(countSelection);
109+
}
110+
return nested;
111+
}
110112
}

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

+24-17
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,20 @@
1515
*/
1616
package org.springframework.data.jpa.repository.query;
1717

18-
import static org.springframework.data.jpa.repository.query.QueryTokens.*;
18+
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_COMMA;
1919

2020
import java.util.ArrayList;
2121
import java.util.Collections;
2222
import java.util.List;
2323

24+
import org.springframework.data.jpa.repository.query.EqlParser.Range_variable_declarationContext;
2425
import org.springframework.lang.Nullable;
2526

2627
/**
2728
* {@link ParsedQueryIntrospector} for EQL queries.
2829
*
2930
* @author Mark Paluch
31+
* @author Christoph Strobl
3032
*/
3133
@SuppressWarnings("UnreachableCode")
3234
class EqlQueryIntrospector extends EqlBaseVisitor<Void> implements ParsedQueryIntrospector {
@@ -56,20 +58,8 @@ public boolean hasConstructorExpression() {
5658
@Override
5759
public Void visitSelect_clause(EqlParser.Select_clauseContext ctx) {
5860

59-
List<EqlParser.Select_itemContext> selections = ctx.select_item();
60-
List<QueryToken> selectItemTokens = new ArrayList<>(selections.size() * 2);
61-
62-
for (EqlParser.Select_itemContext selection : selections) {
63-
64-
if (!selectItemTokens.isEmpty()) {
65-
selectItemTokens.add(TOKEN_COMMA);
66-
}
67-
68-
selectItemTokens.add(QueryTokens.token(QueryRenderer.from(renderer.visitSelect_item(selection)).render()));
69-
}
70-
7161
if (!projectionProcessed) {
72-
projection = selectItemTokens;
62+
projection = captureSelectItems(ctx.select_item(), renderer);
7363
projectionProcessed = true;
7464
}
7565

@@ -80,8 +70,7 @@ public Void visitSelect_clause(EqlParser.Select_clauseContext ctx) {
8070
public Void visitRange_variable_declaration(EqlParser.Range_variable_declarationContext ctx) {
8171

8272
if (primaryFromAlias == null) {
83-
primaryFromAlias = ctx.identification_variable() != null ? ctx.identification_variable().getText()
84-
: ctx.entity_name().getText();
73+
primaryFromAlias = capturePrimaryAlias(ctx);
8574
}
8675

8776
return super.visitRange_variable_declaration(ctx);
@@ -91,8 +80,26 @@ public Void visitRange_variable_declaration(EqlParser.Range_variable_declaration
9180
public Void visitConstructor_expression(EqlParser.Constructor_expressionContext ctx) {
9281

9382
hasConstructorExpression = true;
94-
9583
return super.visitConstructor_expression(ctx);
9684
}
9785

86+
private static String capturePrimaryAlias(Range_variable_declarationContext ctx) {
87+
return ctx.identification_variable() != null ? ctx.identification_variable().getText()
88+
: ctx.entity_name().getText();
89+
}
90+
91+
private static List<QueryToken> captureSelectItems(List<EqlParser.Select_itemContext> selections,
92+
EqlQueryRenderer itemRenderer) {
93+
94+
List<QueryToken> selectItemTokens = new ArrayList<>(selections.size() * 2);
95+
for (EqlParser.Select_itemContext selection : selections) {
96+
97+
if (!selectItemTokens.isEmpty()) {
98+
selectItemTokens.add(TOKEN_COMMA);
99+
}
100+
101+
selectItemTokens.add(QueryTokens.token(QueryRenderer.from(itemRenderer.visitSelect_item(selection)).render()));
102+
}
103+
return selectItemTokens;
104+
}
98105
}

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

+48-41
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,14 @@
1515
*/
1616
package org.springframework.data.jpa.repository.query;
1717

18-
import static org.springframework.data.jpa.repository.query.QueryTokens.*;
19-
18+
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_AS;
19+
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_CLOSE_PAREN;
20+
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_COUNT_FUNC;
21+
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_DOUBLE_UNDERSCORE;
22+
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_OPEN_PAREN;
23+
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_SELECT_COUNT;
24+
25+
import org.springframework.data.jpa.repository.query.HqlParser.SelectClauseContext;
2026
import org.springframework.data.jpa.repository.query.QueryRenderer.QueryRendererBuilder;
2127
import org.springframework.data.jpa.repository.query.QueryTransformers.CountSelectionTokenStream;
2228
import org.springframework.lang.Nullable;
@@ -175,53 +181,30 @@ public QueryTokenStream visitSelectClause(HqlParser.SelectClauseContext ctx) {
175181
QueryRendererBuilder builder = QueryRenderer.builder();
176182
builder.append(QueryTokens.expression(ctx.SELECT()));
177183

178-
QueryTokenStream selectionListbuilder = visit(ctx.selectionList());
179-
180-
if (!isSubquery(ctx)) {
181-
182-
builder.append(TOKEN_COUNT_FUNC);
183-
184-
if (countProjection != null) {
185-
builder.append(QueryTokens.token(countProjection));
186-
}
184+
if (isSubquery(ctx)) {
185+
return visitSubQuerySelectClause(ctx, builder);
186+
}
187187

188-
QueryRendererBuilder nested = QueryRenderer.builder();
188+
builder.append(TOKEN_COUNT_FUNC);
189+
boolean usesDistinct = ctx.DISTINCT() != null;
190+
QueryRendererBuilder nested = QueryRenderer.builder();
191+
if (countProjection == null) {
192+
if (usesDistinct) {
189193

190-
if (ctx.DISTINCT() != null) {
191194
nested.append(QueryTokens.expression(ctx.DISTINCT()));
195+
nested.append(getDistinctCountSelection(visit(ctx.selectionList())));
196+
} else {
197+
nested.append(QueryTokens.token(primaryFromAlias));
192198
}
193-
194-
if (countProjection == null) {
195-
196-
if (ctx.DISTINCT() != null) {
197-
198-
CountSelectionTokenStream countSelection = QueryTransformers
199-
.filterCountSelection(selectionListbuilder);
200-
201-
if (countSelection.requiresPrimaryAlias()) {
202-
// constructor
203-
nested.append(QueryTokens.token(primaryFromAlias));
204-
} else {
205-
// keep all the select items to distinct against
206-
nested.append(countSelection);
207-
}
208-
} else {
209-
nested.append(QueryTokens.token(primaryFromAlias));
210-
}
211-
}
212-
213-
builder.appendInline(nested);
214-
builder.append(TOKEN_CLOSE_PAREN);
215-
216199
} else {
217-
218-
if (ctx.DISTINCT() != null) {
219-
builder.append(QueryTokens.expression(ctx.DISTINCT()));
200+
builder.append(QueryTokens.token(countProjection));
201+
if (usesDistinct) {
202+
nested.append(QueryTokens.expression(ctx.DISTINCT()));
220203
}
221-
222-
builder.append(selectionListbuilder);
223204
}
224205

206+
builder.appendInline(nested);
207+
builder.append(TOKEN_CLOSE_PAREN);
225208
return builder;
226209
}
227210

@@ -245,4 +228,28 @@ public QueryRendererBuilder visitQueryOrder(HqlParser.QueryOrderContext ctx) {
245228
return builder;
246229
}
247230

231+
private QueryRendererBuilder visitSubQuerySelectClause(SelectClauseContext ctx, QueryRendererBuilder builder) {
232+
if (ctx.DISTINCT() != null) {
233+
builder.append(QueryTokens.expression(ctx.DISTINCT()));
234+
}
235+
236+
builder.append(visit(ctx.selectionList()));
237+
return builder;
238+
}
239+
240+
private QueryRendererBuilder getDistinctCountSelection(QueryTokenStream selectionListbuilder) {
241+
242+
QueryRendererBuilder nested = new QueryRendererBuilder();
243+
CountSelectionTokenStream countSelection = QueryTransformers.filterCountSelection(selectionListbuilder);
244+
245+
if (countSelection.requiresPrimaryAlias()) {
246+
// constructor
247+
nested.append(QueryTokens.token(primaryFromAlias));
248+
} else {
249+
// keep all the select items to distinct against
250+
nested.append(countSelection);
251+
}
252+
return nested;
253+
}
254+
248255
}

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

+23-18
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@
1515
*/
1616
package org.springframework.data.jpa.repository.query;
1717

18-
import static org.springframework.data.jpa.repository.query.QueryTokens.*;
18+
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_COMMA;
1919

2020
import java.util.ArrayList;
2121
import java.util.Collections;
2222
import java.util.List;
2323

24+
import org.springframework.data.jpa.repository.query.HqlParser.VariableContext;
2425
import org.springframework.lang.Nullable;
2526

2627
/**
@@ -56,20 +57,8 @@ public boolean hasConstructorExpression() {
5657
@Override
5758
public Void visitSelectClause(HqlParser.SelectClauseContext ctx) {
5859

59-
List<HqlParser.SelectionContext> selections = ctx.selectionList().selection();
60-
List<QueryToken> selectItemTokens = new ArrayList<>(selections.size() * 2);
61-
62-
for (HqlParser.SelectionContext selection : selections) {
63-
64-
if (!selectItemTokens.isEmpty()) {
65-
selectItemTokens.add(TOKEN_COMMA);
66-
}
67-
68-
selectItemTokens.add(QueryTokens.token(QueryRenderer.from(renderer.visitSelection(selection)).render()));
69-
}
70-
7160
if (!projectionProcessed) {
72-
projection = selectItemTokens;
61+
projection = captureSelectItems(ctx.selectionList().selection(), renderer);
7362
projectionProcessed = true;
7463
}
7564

@@ -80,12 +69,10 @@ public Void visitSelectClause(HqlParser.SelectClauseContext ctx) {
8069
public Void visitFromRoot(HqlParser.FromRootContext ctx) {
8170

8271
if (primaryFromAlias == null && ctx.variable() != null && !HqlQueryRenderer.isSubquery(ctx)) {
83-
84-
primaryFromAlias = (ctx.variable().reservedWord() != null ? ctx.variable().reservedWord()
85-
: ctx.variable().identifier().reservedWord()).getText();
72+
primaryFromAlias = capturePrimaryAlias(ctx.variable());
8673
}
8774

88-
return null;
75+
return super.visitFromRoot(ctx);
8976
}
9077

9178
@Override
@@ -96,4 +83,22 @@ public Void visitInstantiation(HqlParser.InstantiationContext ctx) {
9683
return super.visitInstantiation(ctx);
9784
}
9885

86+
private static String capturePrimaryAlias(VariableContext ctx) {
87+
return ((ctx).reservedWord() != null ? ctx.reservedWord() : ctx.identifier().reservedWord()).getText();
88+
}
89+
90+
private static List<QueryToken> captureSelectItems(List<HqlParser.SelectionContext> selections,
91+
HqlQueryRenderer itemRenderer) {
92+
93+
List<QueryToken> selectItemTokens = new ArrayList<>(selections.size() * 2);
94+
for (HqlParser.SelectionContext selection : selections) {
95+
96+
if (!selectItemTokens.isEmpty()) {
97+
selectItemTokens.add(TOKEN_COMMA);
98+
}
99+
100+
selectItemTokens.add(QueryTokens.token(QueryRenderer.from(itemRenderer.visitSelection(selection)).render()));
101+
}
102+
return selectItemTokens;
103+
}
99104
}

0 commit comments

Comments
 (0)