Skip to content

Commit 260d202

Browse files
committed
Support HQL LIMIT/OFFSET without ordering.
Closes #3882
1 parent 16493ff commit 260d202

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
@@ -61,8 +61,16 @@ public QueryRendererBuilder visitOrderedQuery(HqlParser.OrderedQueryContext ctx)
6161
builder.appendExpression(nested);
6262
}
6363

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

6876
return builder;
@@ -235,26 +243,6 @@ public QueryTokenStream visitSelection(HqlParser.SelectionContext ctx) {
235243
return builder;
236244
}
237245

238-
@Override
239-
public QueryRendererBuilder visitQueryOrder(HqlParser.QueryOrderContext ctx) {
240-
241-
QueryRendererBuilder builder = QueryRenderer.builder();
242-
243-
if (ctx.limitClause() != null) {
244-
builder.appendExpression(visit(ctx.limitClause()));
245-
}
246-
247-
if (ctx.offsetClause() != null) {
248-
builder.appendExpression(visit(ctx.offsetClause()));
249-
}
250-
251-
if (ctx.fetchClause() != null) {
252-
builder.appendExpression(visit(ctx.fetchClause()));
253-
}
254-
255-
return builder;
256-
}
257-
258246
private QueryRendererBuilder visitSubQuerySelectClause(SelectClauseContext ctx, QueryRendererBuilder builder) {
259247

260248
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
@@ -179,6 +179,18 @@ private QueryRendererBuilder visitOrderedQuery(HqlParser.OrderedQueryContext ctx
179179
}
180180
}
181181

182+
if (ctx.limitClause() != null) {
183+
builder.appendExpression(visit(ctx.limitClause()));
184+
}
185+
186+
if (ctx.offsetClause() != null) {
187+
builder.appendExpression(visit(ctx.offsetClause()));
188+
}
189+
190+
if (ctx.fetchClause() != null) {
191+
builder.appendExpression(visit(ctx.fetchClause()));
192+
}
193+
182194
return builder;
183195
}
184196

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)