Skip to content

Commit d9c102e

Browse files
committed
Make identification variables and the SELECT clause in JPQL optional.
Original pull request: #3903 Closes #3902
1 parent 4837601 commit d9c102e

19 files changed

+769
-190
lines changed

spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Eql.g4

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@ grammar Eql;
1818
@header {
1919
/**
2020
* Implementation of EclipseLink Query Language (EQL)
21-
* See:
22-
* * https://eclipse.dev/eclipselink/documentation/3.0/jpa/extensions/jpql.htm
23-
* * https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL
2421
*
22+
* @see https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL
23+
* @see https://eclipse.dev/eclipselink/documentation/3.0/jpa/extensions/jpql.htm
2524
* @author Greg Turnquist
2625
* @author Christoph Strobl
2726
* @since 3.2
@@ -43,7 +42,8 @@ ql_statement
4342
;
4443

4544
select_statement
46-
: select_clause from_clause (where_clause)? (groupby_clause)? (having_clause)? (orderby_clause)? (set_fuction)?
45+
: select_clause from_clause (where_clause)? (groupby_clause)? (having_clause)? (orderby_clause)? (set_fuction)? # SelectQuery
46+
| from_clause (where_clause)? (groupby_clause)? (having_clause)? (orderby_clause)? (set_fuction)? # FromQuery
4747
;
4848

4949
setOperator
@@ -80,7 +80,7 @@ identification_variable_declaration
8080
;
8181

8282
range_variable_declaration
83-
: (entity_name|function_invocation) AS? identification_variable
83+
: (entity_name|function_invocation) AS? identification_variable?
8484
;
8585

8686
join
@@ -246,14 +246,15 @@ orderby_clause
246246
: ORDER BY orderby_item (',' orderby_item)*
247247
;
248248

249-
// TODO Error in spec BNF, correctly shown elsewhere in spec.
250249
orderby_item
251-
: state_field_path_expression (ASC | DESC)? nullsPrecedence?
252-
| general_identification_variable (ASC | DESC)? nullsPrecedence?
253-
| result_variable (ASC | DESC)? nullsPrecedence?
254-
| string_expression (ASC | DESC)? nullsPrecedence?
255-
| scalar_expression (ASC | DESC)? nullsPrecedence?
256-
|
250+
: orderby_expression (ASC | DESC)? nullsPrecedence?
251+
;
252+
253+
orderby_expression
254+
: state_field_path_expression
255+
| general_identification_variable
256+
| string_expression
257+
| scalar_expression
257258
;
258259

259260
nullsPrecedence

spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ ql_statement
4343
;
4444

4545
select_statement
46-
: select_clause from_clause (where_clause)? (groupby_clause)? (having_clause)? (orderby_clause)? (set_fuction)?
46+
: select_clause from_clause (where_clause)? (groupby_clause)? (having_clause)? (orderby_clause)? (set_fuction)? # SelectQuery
47+
| from_clause (where_clause)? (groupby_clause)? (having_clause)? (orderby_clause)? (set_fuction)? # FromQuery
4748
;
4849

4950
setOperator
@@ -72,18 +73,19 @@ from_clause
7273
identificationVariableDeclarationOrCollectionMemberDeclaration
7374
: identification_variable_declaration
7475
| collection_member_declaration
76+
| '(' subquery ')' identification_variable
7577
;
7678

7779
identification_variable_declaration
7880
: range_variable_declaration (join | fetch_join)*
7981
;
8082

8183
range_variable_declaration
82-
: entity_name AS? identification_variable
84+
: entity_name AS? identification_variable?
8385
;
8486

8587
join
86-
: join_spec join_association_path_expression AS? identification_variable (join_condition)?
88+
: join_spec join_association_path_expression AS? identification_variable? (join_condition)?
8789
;
8890

8991
fetch_join
@@ -106,11 +108,11 @@ join_association_path_expression
106108
;
107109

108110
join_collection_valued_path_expression
109-
: identification_variable '.' (single_valued_embeddable_object_field '.')* collection_valued_field
111+
: (identification_variable '.')? (single_valued_embeddable_object_field '.')* collection_valued_field
110112
;
111113

112114
join_single_valued_path_expression
113-
: identification_variable '.' (single_valued_embeddable_object_field '.')* single_valued_object_field
115+
: (identification_variable '.')? (single_valued_embeddable_object_field '.')* single_valued_object_field
114116
;
115117

116118
collection_member_declaration
@@ -244,9 +246,15 @@ orderby_clause
244246
: ORDER BY orderby_item (',' orderby_item)*
245247
;
246248

247-
// TODO Error in spec BNF, correctly shown elsewhere in spec.
248249
orderby_item
249-
: (state_field_path_expression | general_identification_variable | result_variable ) (ASC | DESC)? nullsPrecedence?
250+
: orderby_expression (ASC | DESC)? nullsPrecedence?
251+
;
252+
253+
orderby_expression
254+
: state_field_path_expression
255+
| general_identification_variable
256+
| string_expression
257+
| scalar_expression
250258
;
251259

252260
nullsPrecedence

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

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.springframework.data.jpa.repository.query.QueryRenderer.QueryRendererBuilder;
2323
import org.springframework.data.jpa.repository.query.QueryTransformers.CountSelectionTokenStream;
24+
import org.springframework.util.StringUtils;
2425

2526
/**
2627
* An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that transforms a parsed EQL query into a
@@ -43,7 +44,7 @@ class EqlCountQueryTransformer extends EqlQueryRenderer {
4344
}
4445

4546
@Override
46-
public QueryTokenStream visitSelect_statement(EqlParser.Select_statementContext ctx) {
47+
public QueryTokenStream visitSelectQuery(EqlParser.SelectQueryContext ctx) {
4748

4849
QueryRendererBuilder builder = QueryRenderer.builder();
4950

@@ -63,6 +64,49 @@ public QueryTokenStream visitSelect_statement(EqlParser.Select_statementContext
6364
return builder;
6465
}
6566

67+
@Override
68+
public QueryTokenStream visitFromQuery(EqlParser.FromQueryContext ctx) {
69+
70+
QueryRendererBuilder builder = QueryRenderer.builder();
71+
72+
QueryRendererBuilder countBuilder = QueryRenderer.builder();
73+
countBuilder.append(TOKEN_SELECT_COUNT);
74+
75+
if (countProjection != null) {
76+
countBuilder.append(QueryTokens.token(countProjection));
77+
} else {
78+
if (primaryFromAlias == null) {
79+
countBuilder.append(TOKEN_DOUBLE_UNDERSCORE);
80+
} else {
81+
countBuilder.append(QueryTokens.token(primaryFromAlias));
82+
}
83+
}
84+
85+
countBuilder.append(TOKEN_CLOSE_PAREN);
86+
87+
builder.appendExpression(countBuilder);
88+
89+
if (ctx.from_clause() != null) {
90+
builder.appendExpression(visit(ctx.from_clause()));
91+
if (primaryFromAlias == null) {
92+
builder.append(TOKEN_AS);
93+
builder.append(TOKEN_DOUBLE_UNDERSCORE);
94+
}
95+
}
96+
97+
if (ctx.where_clause() != null) {
98+
builder.appendExpression(visit(ctx.where_clause()));
99+
}
100+
if (ctx.groupby_clause() != null) {
101+
builder.appendExpression(visit(ctx.groupby_clause()));
102+
}
103+
if (ctx.having_clause() != null) {
104+
builder.appendExpression(visit(ctx.having_clause()));
105+
}
106+
107+
return builder;
108+
}
109+
66110
@Override
67111
public QueryTokenStream visitSelect_clause(EqlParser.Select_clauseContext ctx) {
68112

@@ -78,14 +122,21 @@ public QueryTokenStream visitSelect_clause(EqlParser.Select_clauseContext ctx) {
78122
if (usesDistinct) {
79123
nested.append(QueryTokens.expression(ctx.DISTINCT()));
80124
nested.append(getDistinctCountSelection(QueryTokenStream.concat(ctx.select_item(), this::visit, TOKEN_COMMA)));
81-
} else {
125+
} else if (StringUtils.hasText(primaryFromAlias)) {
82126
nested.append(QueryTokens.token(primaryFromAlias));
127+
} else {
128+
if (ctx.select_item().isEmpty()) {
129+
// cannot happen as per grammar, but you never know…
130+
nested.append(QueryTokens.token("1"));
131+
} else {
132+
nested.append(visit(ctx.select_item().get(0)));
133+
}
83134
}
84135
} else {
85-
builder.append(QueryTokens.token(countProjection));
86136
if (usesDistinct) {
87137
nested.append(QueryTokens.expression(ctx.DISTINCT()));
88138
}
139+
nested.append(QueryTokens.token(countProjection));
89140
}
90141

91142
builder.appendInline(nested);

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ public Void visitSelect_clause(EqlParser.Select_clauseContext ctx) {
6161
@Override
6262
public Void visitRange_variable_declaration(EqlParser.Range_variable_declarationContext ctx) {
6363

64-
if (primaryFromAlias == null) {
65-
primaryFromAlias = capturePrimaryAlias(ctx);
64+
if (primaryFromAlias == null && ctx.identification_variable() != null && !EqlQueryRenderer.isSubquery(ctx)
65+
&& !EqlQueryRenderer.isSetQuery(ctx)) {
66+
primaryFromAlias = ctx.identification_variable().getText();
6667
}
6768

6869
return super.visitRange_variable_declaration(ctx);
@@ -75,11 +76,6 @@ public Void visitConstructor_expression(EqlParser.Constructor_expressionContext
7576
return super.visitConstructor_expression(ctx);
7677
}
7778

78-
private static String capturePrimaryAlias(Range_variable_declarationContext ctx) {
79-
return ctx.identification_variable() != null ? ctx.identification_variable().getText()
80-
: ctx.entity_name().getText();
81-
}
82-
8379
private static List<QueryToken> captureSelectItems(List<EqlParser.Select_itemContext> selections,
8480
EqlQueryRenderer itemRenderer) {
8581

@@ -94,4 +90,5 @@ private static List<QueryToken> captureSelectItems(List<EqlParser.Select_itemCon
9490
}
9591
return selectItemTokens;
9692
}
93+
9794
}

0 commit comments

Comments
 (0)