Support HQL LIMIT/OFFSET without ordering.

Closes #3882
This commit is contained in:
Mark Paluch
2025-05-12 14:40:50 +02:00
parent 1440bd863a
commit 091fecc4e0
5 changed files with 51 additions and 44 deletions

View File

@@ -85,7 +85,7 @@ cteAttributes
;
orderedQuery
: (query | '(' queryExpression ')') queryOrder?
: (query | '(' queryExpression ')') queryOrder? limitClause? offsetClause? fetchClause?
;
query
@@ -94,7 +94,7 @@ query
;
queryOrder
: orderByClause limitClause? offsetClause? fetchClause?
: orderByClause
;
fromClause

View File

@@ -60,8 +60,16 @@ class HqlCountQueryTransformer extends HqlQueryRenderer {
builder.appendExpression(nested);
}
if (ctx.queryOrder() != null) {
builder.append(visit(ctx.queryOrder()));
if (ctx.limitClause() != null) {
builder.appendExpression(visit(ctx.limitClause()));
}
if (ctx.offsetClause() != null) {
builder.appendExpression(visit(ctx.offsetClause()));
}
if (ctx.fetchClause() != null) {
builder.appendExpression(visit(ctx.fetchClause()));
}
return builder;
@@ -240,26 +248,6 @@ class HqlCountQueryTransformer extends HqlQueryRenderer {
return builder;
}
@Override
public QueryRendererBuilder visitQueryOrder(HqlParser.QueryOrderContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.limitClause() != null) {
builder.appendExpression(visit(ctx.limitClause()));
}
if (ctx.offsetClause() != null) {
builder.appendExpression(visit(ctx.offsetClause()));
}
if (ctx.fetchClause() != null) {
builder.appendExpression(visit(ctx.fetchClause()));
}
return builder;
}
private QueryRendererBuilder visitSubQuerySelectClause(SelectClauseContext ctx, QueryRendererBuilder builder) {
if (ctx.DISTINCT() != null) {

View File

@@ -239,6 +239,18 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
builder.append(visit(ctx.queryOrder()));
}
if (ctx.limitClause() != null) {
builder.appendExpression(visit(ctx.limitClause()));
}
if (ctx.offsetClause() != null) {
builder.appendExpression(visit(ctx.offsetClause()));
}
if (ctx.fetchClause() != null) {
builder.appendExpression(visit(ctx.fetchClause()));
}
return builder;
}
@@ -298,26 +310,7 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
@Override
public QueryTokenStream visitQueryOrder(HqlParser.QueryOrderContext ctx) {
if (ctx.limitClause() == null && ctx.offsetClause() == null && ctx.fetchClause() == null) {
return visit(ctx.orderByClause());
}
QueryRendererBuilder builder = QueryRenderer.builder();
builder.appendExpression(visit(ctx.orderByClause()));
if (ctx.limitClause() != null) {
builder.appendExpression(visit(ctx.limitClause()));
}
if (ctx.offsetClause() != null) {
builder.appendExpression(visit(ctx.offsetClause()));
}
if (ctx.fetchClause() != null) {
builder.appendExpression(visit(ctx.fetchClause()));
}
return builder;
return visit(ctx.orderByClause());
}
@Override

View File

@@ -162,6 +162,18 @@ class HqlSortedQueryTransformer extends HqlQueryRenderer {
}
}
if (ctx.limitClause() != null) {
builder.appendExpression(visit(ctx.limitClause()));
}
if (ctx.offsetClause() != null) {
builder.appendExpression(visit(ctx.offsetClause()));
}
if (ctx.fetchClause() != null) {
builder.appendExpression(visit(ctx.fetchClause()));
}
return builder;
}

View File

@@ -1676,6 +1676,20 @@ class HqlQueryRendererTests {
});
}
@Test // GH-3882
void shouldSupportLimitOffset() {
assertQuery("SELECT si from StockItem si order by si.id LIMIT 10 OFFSET 10 FETCH FIRST 10 ROWS ONLY");
assertQuery("SELECT si from StockItem si order by si.id LIMIT ? OFFSET ? FETCH FIRST ? ROWS ONLY");
assertQuery("SELECT si from StockItem si order by si.id LIMIT :l OFFSET :o");
assertQuery("SELECT si from StockItem si LIMIT :l OFFSET :o");
assertQuery("SELECT si from StockItem si order by si.id LIMIT :l");
assertQuery("SELECT si from StockItem si order by si.id OFFSET 1");
assertQuery("SELECT si from StockItem si LIMIT 1");
assertQuery("SELECT si from StockItem si OFFSET 1");
assertQuery("SELECT si from StockItem si FETCH FIRST 1 ROWS ONLY");
}
@Test // GH-2964
void roundFunctionShouldWorkLikeAnyOtherFunction() {