Skip to content

Commit 091fecc

Browse files
committed
Support HQL LIMIT/OFFSET without ordering.
Closes #3882
1 parent 1440bd8 commit 091fecc

File tree

5 files changed

+51
-44
lines changed

5 files changed

+51
-44
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ cteAttributes
8585
;
8686

8787
orderedQuery
88-
: (query | '(' queryExpression ')') queryOrder?
88+
: (query | '(' queryExpression ')') queryOrder? limitClause? offsetClause? fetchClause?
8989
;
9090

9191
query
@@ -94,7 +94,7 @@ query
9494
;
9595

9696
queryOrder
97-
: orderByClause limitClause? offsetClause? fetchClause?
97+
: orderByClause
9898
;
9999

100100
fromClause

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

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,16 @@ public QueryRendererBuilder visitOrderedQuery(HqlParser.OrderedQueryContext ctx)
6060
builder.appendExpression(nested);
6161
}
6262

63-
if (ctx.queryOrder() != null) {
64-
builder.append(visit(ctx.queryOrder()));
63+
if (ctx.limitClause() != null) {
64+
builder.appendExpression(visit(ctx.limitClause()));
65+
}
66+
67+
if (ctx.offsetClause() != null) {
68+
builder.appendExpression(visit(ctx.offsetClause()));
69+
}
70+
71+
if (ctx.fetchClause() != null) {
72+
builder.appendExpression(visit(ctx.fetchClause()));
6573
}
6674

6775
return builder;
@@ -240,26 +248,6 @@ public QueryTokenStream visitSelection(HqlParser.SelectionContext ctx) {
240248
return builder;
241249
}
242250

243-
@Override
244-
public QueryRendererBuilder visitQueryOrder(HqlParser.QueryOrderContext ctx) {
245-
246-
QueryRendererBuilder builder = QueryRenderer.builder();
247-
248-
if (ctx.limitClause() != null) {
249-
builder.appendExpression(visit(ctx.limitClause()));
250-
}
251-
252-
if (ctx.offsetClause() != null) {
253-
builder.appendExpression(visit(ctx.offsetClause()));
254-
}
255-
256-
if (ctx.fetchClause() != null) {
257-
builder.appendExpression(visit(ctx.fetchClause()));
258-
}
259-
260-
return builder;
261-
}
262-
263251
private QueryRendererBuilder visitSubQuerySelectClause(SelectClauseContext ctx, QueryRendererBuilder builder) {
264252

265253
if (ctx.DISTINCT() != null) {

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

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,18 @@ public QueryTokenStream visitOrderedQuery(HqlParser.OrderedQueryContext ctx) {
239239
builder.append(visit(ctx.queryOrder()));
240240
}
241241

242+
if (ctx.limitClause() != null) {
243+
builder.appendExpression(visit(ctx.limitClause()));
244+
}
245+
246+
if (ctx.offsetClause() != null) {
247+
builder.appendExpression(visit(ctx.offsetClause()));
248+
}
249+
250+
if (ctx.fetchClause() != null) {
251+
builder.appendExpression(visit(ctx.fetchClause()));
252+
}
253+
242254
return builder;
243255
}
244256

@@ -298,26 +310,7 @@ public QueryTokenStream visitFromQuery(HqlParser.FromQueryContext ctx) {
298310

299311
@Override
300312
public QueryTokenStream visitQueryOrder(HqlParser.QueryOrderContext ctx) {
301-
302-
if (ctx.limitClause() == null && ctx.offsetClause() == null && ctx.fetchClause() == null) {
303-
return visit(ctx.orderByClause());
304-
}
305-
306-
QueryRendererBuilder builder = QueryRenderer.builder();
307-
308-
builder.appendExpression(visit(ctx.orderByClause()));
309-
310-
if (ctx.limitClause() != null) {
311-
builder.appendExpression(visit(ctx.limitClause()));
312-
}
313-
if (ctx.offsetClause() != null) {
314-
builder.appendExpression(visit(ctx.offsetClause()));
315-
}
316-
if (ctx.fetchClause() != null) {
317-
builder.appendExpression(visit(ctx.fetchClause()));
318-
}
319-
320-
return builder;
313+
return visit(ctx.orderByClause());
321314
}
322315

323316
@Override

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,18 @@ private QueryRendererBuilder visitOrderedQuery(HqlParser.OrderedQueryContext ctx
162162
}
163163
}
164164

165+
if (ctx.limitClause() != null) {
166+
builder.appendExpression(visit(ctx.limitClause()));
167+
}
168+
169+
if (ctx.offsetClause() != null) {
170+
builder.appendExpression(visit(ctx.offsetClause()));
171+
}
172+
173+
if (ctx.fetchClause() != null) {
174+
builder.appendExpression(visit(ctx.fetchClause()));
175+
}
176+
165177
return builder;
166178
}
167179

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,6 +1676,20 @@ void orderByWithNullsFirstOrLastShouldWork() {
16761676
});
16771677
}
16781678

1679+
@Test // GH-3882
1680+
void shouldSupportLimitOffset() {
1681+
1682+
assertQuery("SELECT si from StockItem si order by si.id LIMIT 10 OFFSET 10 FETCH FIRST 10 ROWS ONLY");
1683+
assertQuery("SELECT si from StockItem si order by si.id LIMIT ? OFFSET ? FETCH FIRST ? ROWS ONLY");
1684+
assertQuery("SELECT si from StockItem si order by si.id LIMIT :l OFFSET :o");
1685+
assertQuery("SELECT si from StockItem si LIMIT :l OFFSET :o");
1686+
assertQuery("SELECT si from StockItem si order by si.id LIMIT :l");
1687+
assertQuery("SELECT si from StockItem si order by si.id OFFSET 1");
1688+
assertQuery("SELECT si from StockItem si LIMIT 1");
1689+
assertQuery("SELECT si from StockItem si OFFSET 1");
1690+
assertQuery("SELECT si from StockItem si FETCH FIRST 1 ROWS ONLY");
1691+
}
1692+
16791693
@Test // GH-2964
16801694
void roundFunctionShouldWorkLikeAnyOtherFunction() {
16811695

0 commit comments

Comments
 (0)