Description and expected behavior
When a model inherits @computed fields via a mixin type (the with keyword) and is fetched as a nested relation via an explicit include, ZenStack generates invalid SQL that references the computed field as a raw column that does not exist in the query, causing PostgreSQL to fail with:
error: column "$$tN.fieldName" does not exist
The bug does not occur when:
- the
@computed field is declared directly on the model (not via a mixin)
- the model is queried directly without
include
- the relation is not explicitly included by the parent query
Minimal reproduction
Schema:
type ParentRelated {
parentCode String? @computed
}
model Parent {
id Int @id @default(autoincrement())
code String
children Child[]
}
model Child with ParentRelated {
id Int @id @default(autoincrement())
name String
parentId Int
parent Parent @relation(fields: [parentId], references: [id])
}
computedFields config:
computedFields: {
Child: {
parentCode: (eb) =>
eb.selectFrom('Parent')
.select('Parent.code')
.whereRef('Parent.id', '=', 'parentId')
.limit(1),
},
}
Query that fails:
db.parent.findFirst({
where: { id: 1 },
include: { children: true },
})
Generated SQL (simplified):
SELECT "Parent"."id", "Parent"."code", "$$t1"."$data" AS "children"
FROM "Parent"
LEFT JOIN LATERAL (
SELECT coalesce(jsonb_agg(jsonb_build_object(
'parentCode', "$$t2"."parentCode", -- ❌ column does not exist
'id', "$$t2"."id",
'name', "$$t2"."name",
'parentId', "$$t2"."parentId"
)), '[]'::jsonb) AS "$data"
FROM "Child" AS "$$t2" -- ✅ flat join, no computed column selected
WHERE "$$t2"."parentId" = "Parent"."id"
) AS "$$t1" ON true
Root cause
canJoinWithoutNestedSelect guards whether the lateral join needs to wrap the child model in a nested subquery (which would include the computed field in its SELECT via buildSelectAllFields). The guard checks modelDef.computedFields, but when @computed fields are inherited from a mixin type, they are stored on fieldDef.originModel rather than directly on the model definition. The guard therefore returns true (no nested subquery needed), the computed field is never selected into the child subquery, and the outer jsonb_build_object references $$tN.parentCode which does not exist.
When the @computed field is declared directly on the model, modelDef.computedFields is populated and the guard correctly returns false, producing a nested subquery that includes the computed field.
Screenshots
N/A
Environment:
- ZenStack version: 3.4.4
- Database type: PostgreSQL
- Node.js version: 23.10.0
- Package manager: npm 10.9.2
Additional context
Here is a test case
#2539
Description and expected behavior
When a model inherits
@computedfields via a mixin type (thewithkeyword) and is fetched as a nested relation via an explicitinclude, ZenStack generates invalid SQL that references the computed field as a raw column that does not exist in the query, causing PostgreSQL to fail with:The bug does not occur when:
@computedfield is declared directly on the model (not via a mixin)includeMinimal reproduction
Schema:
computedFieldsconfig:Query that fails:
Generated SQL (simplified):
Root cause
canJoinWithoutNestedSelectguards whether the lateral join needs to wrap the child model in a nested subquery (which would include the computed field in its SELECT viabuildSelectAllFields). The guard checksmodelDef.computedFields, but when@computedfields are inherited from a mixin type, they are stored onfieldDef.originModelrather than directly on the model definition. The guard therefore returnstrue(no nested subquery needed), the computed field is never selected into the child subquery, and the outerjsonb_build_objectreferences$$tN.parentCodewhich does not exist.When the
@computedfield is declared directly on the model,modelDef.computedFieldsis populated and the guard correctly returnsfalse, producing a nested subquery that includes the computed field.Screenshots
N/A
Environment:
Additional context
Here is a test case
#2539