diff --git a/packages/plugins/policy/src/expression-transformer.ts b/packages/plugins/policy/src/expression-transformer.ts index ea5d0f1ee..61d2509c5 100644 --- a/packages/plugins/policy/src/expression-transformer.ts +++ b/packages/plugins/policy/src/expression-transformer.ts @@ -1130,6 +1130,17 @@ export class ExpressionTransformer { const tableName = context.alias ?? context.modelOrType; const fieldDef = QueryUtils.requireField(this.schema, context.modelOrType, column); + + if (fieldDef.computed) { + // `@computed` fields don't physically exist as columns — inline the + // computed expression the same way every other query path does via + // `dialect.fieldRef()`, instead of emitting a bare (nonexistent) column ref. + if (fieldDef.params) { + throw new Error(`parameterized computed field "${column}" cannot be used in a policy expression`); + } + return this.dialect.fieldRef(context.modelOrType, column, tableName).toOperationNode(); + } + if (!fieldDef.originModel || fieldDef.originModel === context.modelOrType) { return ReferenceNode.create(ColumnNode.create(column), TableNode.create(tableName)); } diff --git a/tests/regression/test/issue-2888.test.ts b/tests/regression/test/issue-2888.test.ts new file mode 100644 index 000000000..aaa31a088 --- /dev/null +++ b/tests/regression/test/issue-2888.test.ts @@ -0,0 +1,34 @@ +import { createPolicyTestClient } from '@zenstackhq/testtools'; +import { describe, expect, it } from 'vitest'; + +describe('Regression for issue 2888', () => { + it('a computed field referenced in @@allow does not throw "no such column" and evaluates its expression', async () => { + const db = await createPolicyTestClient( + ` +model Model { + id Int @id @default(autoincrement()) + x Int + canEdit Boolean @computed + + @@allow('create,read', true) + @@allow('update', canEdit) +} + `, + { + provider: 'mysql', + usePrismaPush: true, + computedFields: { + model: { + canEdit: (eb: any) => eb.lit(true), + }, + }, + } as any, + ); + + await db.model.create({ data: { id: 1, x: 1 } }); + + await expect(db.model.update({ where: { id: 1 }, data: { x: 2 } })).resolves.toMatchObject({ + x: 2, + }); + }); +});