fix(query-builder): handle empty result set when offset exceeds total (#11634)

* fix(query-builder): handle empty result set when offset exceeds total

* fix: lazy count can still be trusted when the result set is empty
This commit is contained in:
jeremyteyssedre 2025-09-06 00:36:38 +02:00 committed by GitHub
parent 6965fa2082
commit 1f904675f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 61 additions and 0 deletions

View File

@ -1948,6 +1948,12 @@ export class SelectQueryBuilder<Entity extends ObjectLiteral>
this.expressionMap.offset !== null &&
this.expressionMap.offset > 0
if (entitiesAndRaw.entities.length === 0 && (hasSkip || hasOffset)) {
// when skip or offset were used and no results found, we need to execute a full count
// (the given offset may have exceeded the actual number of rows)
return undefined
}
// offset overrides skip when no join is defined
const previousResults: number = hasOffset
? this.expressionMap.offset!

View File

@ -266,6 +266,61 @@ describe("other issues > lazy count", () => {
}),
))
it("run count query when joining a relation with skip is greater than total entities", () =>
Promise.all(
connections.map(async function (connection) {
await savePostEntities(connection, 5)
const afterQuery = connection
.subscribers[0] as AfterQuerySubscriber
afterQuery.clear()
const [entities, count] = await connection.manager
.createQueryBuilder(Post, "post")
.innerJoin("post.comments", "comments")
.take(10)
.skip(20)
.orderBy("post.id")
.getManyAndCount()
expect(count).to.be.equal(5)
expect(entities.length).to.be.equal(0)
expect(
afterQuery
.getCalledQueries()
.filter((query) => query.match(/(count|cnt)/i)),
).not.to.be.empty
}),
))
it("run count query when offset is greater than total entities", () =>
Promise.all(
connections.map(async function (connection) {
await savePostEntities(connection, 5)
const afterQuery = connection
.subscribers[0] as AfterQuerySubscriber
afterQuery.clear()
const [entities, count] = await connection.manager
.createQueryBuilder(Post, "post")
.limit(10)
.offset(20)
.orderBy("post.id")
.getManyAndCount()
expect(count).to.be.equal(5)
expect(entities.length).to.be.equal(0)
expect(
afterQuery
.getCalledQueries()
.filter((query) => query.match(/(count|cnt)/i)),
).not.to.be.empty
}),
))
async function savePostEntities(connection: DataSource, count: number) {
for (let i = 1; i <= count; i++) {
const post = new Post()