diff --git a/packages/orm/src/client/crud/dialects/base-dialect.ts b/packages/orm/src/client/crud/dialects/base-dialect.ts index 2ae0bb7af..1c7627547 100644 --- a/packages/orm/src/client/crud/dialects/base-dialect.ts +++ b/packages/orm/src/client/crud/dialects/base-dialect.ts @@ -1292,25 +1292,29 @@ export abstract class BaseCrudDialect { const fieldModel = fieldDef.type as GetModels; let fieldCountQuery: SelectQueryBuilder; + // Use a unique alias for the subquery to avoid ambiguous references when + // fieldModel === model (self-referential relation on a delegate model) + const subQueryAlias = tmpAlias(`${parentAlias}$_${field}$count`); + // join conditions const m2m = getManyToManyRelation(this.schema, model, field); if (m2m) { // many-to-many relation, count the join table - fieldCountQuery = this.buildModelSelect(fieldModel, fieldModel, value as any, false) + fieldCountQuery = this.buildModelSelect(fieldModel, subQueryAlias, value as any, false) .innerJoin(m2m.joinTable, (join) => join - .onRef(`${m2m.joinTable}.${m2m.otherFkName}`, '=', `${fieldModel}.${m2m.otherPKName}`) + .onRef(`${m2m.joinTable}.${m2m.otherFkName}`, '=', `${subQueryAlias}.${m2m.otherPKName}`) .onRef(`${m2m.joinTable}.${m2m.parentFkName}`, '=', `${parentAlias}.${m2m.parentPKName}`), ) .select(eb.fn.countAll().as(`_count$${field}`)); } else { // build a nested query to count the number of records in the relation - fieldCountQuery = this.buildModelSelect(fieldModel, fieldModel, value as any, false).select( + fieldCountQuery = this.buildModelSelect(fieldModel, subQueryAlias, value as any, false).select( eb.fn.countAll().as(`_count$${field}`), ); // join conditions - const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel); + const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, subQueryAlias); for (const [left, right] of joinPairs) { fieldCountQuery = fieldCountQuery.whereRef(left, '=', right); } diff --git a/tests/regression/test/issue-2452.test.ts b/tests/regression/test/issue-2452.test.ts new file mode 100644 index 000000000..01586c920 --- /dev/null +++ b/tests/regression/test/issue-2452.test.ts @@ -0,0 +1,47 @@ +import { createTestClient } from '@zenstackhq/testtools'; +import { describe, expect, it } from 'vitest'; + +// https://github.com/zenstackhq/zenstack/issues/2452 +describe('Regression for issue 2452', () => { + it('should return correct _count for self-referential relations in delegate models', async () => { + const db = await createTestClient( + ` +enum ContentType { + Post + Article + Question +} + +model Content { + id Int @id @default(autoincrement()) + type ContentType + @@delegate(type) +} + +model Post extends Content { + replies Post[] @relation("PostReplies") + parentId Int? + parent Post? @relation("PostReplies", fields: [parentId], references: [id]) +} + `, + ); + + // Create a parent post with 2 replies + const parent = await db.post.create({ + data: { + replies: { + create: [{}, {}], + }, + }, + }); + + // Query with _count should return the correct count + const result = await db.post.findFirst({ + where: { id: parent.id }, + include: { _count: { select: { replies: true } } }, + }); + + expect(result).toBeTruthy(); + expect(result._count.replies).toBe(2); + }); +});