From 8ddfe3ee94db50aac6d4e17d79e36b36c8b692f5 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 10 Mar 2026 15:52:19 -0700 Subject: [PATCH 1/5] fix(orm): cast fields with @db.* native type annotations to base SQL type When a field has a native database type annotation like @db.Uuid, PostgreSQL stores it as that native type. Comparing such a field with a plain String field (e.g., in policy expressions like `id == x`) causes a type mismatch error. This fix casts fields with @db.* attributes back to their ZModel base SQL type in fieldRef(), which affects both SELECT and WHERE clauses. - Add abstract `getSqlType()` to BaseCrudDialect with implementations in PostgreSQL, SQLite, and MySQL dialects - Add `hasNativeTypeAttribute()` helper to detect @db.* attributes - Modify `fieldRef()` to apply CAST for fields with native type annotations - Update policy expression transformer to use dialect's fieldRef() Fixes #2394 Co-Authored-By: Claude Opus 4.6 --- .../src/client/crud/dialects/base-dialect.ts | 40 ++++++++++++++++- .../orm/src/client/crud/dialects/mysql.ts | 19 +++++++- .../src/client/crud/dialects/postgresql.ts | 43 +++++++++++-------- .../orm/src/client/crud/dialects/sqlite.ts | 18 ++++++++ .../policy/src/expression-transformer.ts | 2 +- pnpm-lock.yaml | 10 +++-- tests/regression/package.json | 11 ++--- tests/regression/test/issue-2394.test.ts | 24 +++++++++++ 8 files changed, 136 insertions(+), 31 deletions(-) create mode 100644 tests/regression/test/issue-2394.test.ts diff --git a/packages/orm/src/client/crud/dialects/base-dialect.ts b/packages/orm/src/client/crud/dialects/base-dialect.ts index 1f5102121..736b05560 100644 --- a/packages/orm/src/client/crud/dialects/base-dialect.ts +++ b/packages/orm/src/client/crud/dialects/base-dialect.ts @@ -84,6 +84,22 @@ export abstract class BaseCrudDialect { // #endregion + // #region type mapping + + /** + * Maps a ZModel type to the corresponding SQL type for this dialect. + */ + protected abstract getSqlType(zmodelType: string): string | undefined; + + /** + * Checks if a field has a native database type attribute (e.g., `@db.Uuid`). + */ + protected hasNativeTypeAttribute(fieldDef: FieldDef): boolean { + return !!fieldDef.attributes?.some((a) => a.name.startsWith('@db.')); + } + + // #endregion + // #region value transformation /** @@ -1143,7 +1159,16 @@ export abstract class BaseCrudDialect { ) { continue; } - jsonObject[field] = eb.ref(`${subModel.name}.${field}`); + const subFieldDef = requireField(this.schema, subModel.name, field); + const castSqlType = this.hasNativeTypeAttribute(subFieldDef) + ? this.getSqlType(subFieldDef.type) + : undefined; + if (castSqlType) { + jsonObject[field] = + sql`CAST(${sql.ref(`${subModel.name}.${field}`)} AS ${sql.raw(castSqlType)})`; + } else { + jsonObject[field] = eb.ref(`${subModel.name}.${field}`); + } } return this.buildJsonObject(jsonObject).as(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`); }); @@ -1344,7 +1369,18 @@ export abstract class BaseCrudDialect { if (!fieldDef.computed) { // regular field - return this.eb.ref(modelAlias ? `${modelAlias}.${field}` : field); + const ref = modelAlias ? `${modelAlias}.${field}` : field; + + // if the field has a native database type annotation (e.g., @db.Uuid), cast it + // back to the base SQL type to avoid type mismatch in comparisons + if (this.hasNativeTypeAttribute(fieldDef)) { + const sqlType = this.getSqlType(fieldDef.type); + if (sqlType) { + return sql`CAST(${sql.ref(ref)} AS ${sql.raw(sqlType)})`; + } + } + + return this.eb.ref(ref); } else { // computed field if (!inlineComputedField) { diff --git a/packages/orm/src/client/crud/dialects/mysql.ts b/packages/orm/src/client/crud/dialects/mysql.ts index 6e444227b..c1f452894 100644 --- a/packages/orm/src/client/crud/dialects/mysql.ts +++ b/packages/orm/src/client/crud/dialects/mysql.ts @@ -16,7 +16,7 @@ import type { BuiltinType, FieldDef, SchemaDef } from '../../../schema'; import type { SortOrder } from '../../crud-types'; import { createInvalidInputError, createNotSupportedError } from '../../errors'; import type { ClientOptions } from '../../options'; -import { isTypeDef } from '../../query-utils'; +import { isEnum, isTypeDef } from '../../query-utils'; import { LateralJoinDialectBase } from './lateral-join-dialect-base'; export class MySqlCrudDialect extends LateralJoinDialectBase { @@ -318,6 +318,23 @@ export class MySqlCrudDialect extends LateralJoinDiale ); } + protected override getSqlType(zmodelType: string) { + if (isEnum(this.schema, zmodelType)) { + return 'varchar(191)'; + } + return match(zmodelType) + .with('String', () => 'varchar(191)') + .with('Boolean', () => 'tinyint(1)') + .with('Int', () => 'signed') + .with('BigInt', () => 'bigint') + .with('Float', () => 'double') + .with('Decimal', () => 'decimal(65,30)') + .with('DateTime', () => 'datetime(3)') + .with('Bytes', () => 'longblob') + .with('Json', () => 'json') + .otherwise(() => undefined); + } + override getStringCasingBehavior() { // MySQL LIKE is case-insensitive by default (depends on collation), no ILIKE support return { supportsILike: false, likeCaseSensitive: false }; diff --git a/packages/orm/src/client/crud/dialects/postgresql.ts b/packages/orm/src/client/crud/dialects/postgresql.ts index 5f962dbb5..2ace46364 100644 --- a/packages/orm/src/client/crud/dialects/postgresql.ts +++ b/packages/orm/src/client/crud/dialects/postgresql.ts @@ -281,7 +281,11 @@ export class PostgresCrudDialect extends LateralJoinDi override buildArrayValue(values: Expression[], elemType: string): AliasableExpression { const arr = sql`ARRAY[${sql.join(values, sql.raw(','))}]`; const mappedType = this.getSqlType(elemType); - return this.eb.cast(arr, sql`${sql.raw(mappedType)}[]`); + if (mappedType) { + return this.eb.cast(arr, sql`${sql.raw(mappedType)}[]`); + } else { + return arr; + } } override buildArrayContains( @@ -293,7 +297,7 @@ export class PostgresCrudDialect extends LateralJoinDi const arrayExpr = sql`ARRAY[${value}]`; if (elemType) { const mappedType = this.getSqlType(elemType); - const typedArray = this.eb.cast(arrayExpr, sql`${sql.raw(mappedType)}[]`); + const typedArray = mappedType ? this.eb.cast(arrayExpr, sql`${sql.raw(mappedType)}[]`) : arrayExpr; return this.eb(field, '@>', typedArray); } else { return this.eb(field, '@>', arrayExpr); @@ -357,25 +361,22 @@ export class PostgresCrudDialect extends LateralJoinDi ); } - private getSqlType(zmodelType: string) { + protected override getSqlType(zmodelType: string) { if (isEnum(this.schema, zmodelType)) { // reduce enum to text for type compatibility return 'text'; } else { - return ( - match(zmodelType) - .with('String', () => 'text') - .with('Boolean', () => 'boolean') - .with('Int', () => 'integer') - .with('BigInt', () => 'bigint') - .with('Float', () => 'double precision') - .with('Decimal', () => 'decimal') - .with('DateTime', () => 'timestamp') - .with('Bytes', () => 'bytea') - .with('Json', () => 'jsonb') - // fallback to text - .otherwise(() => 'text') - ); + return match(zmodelType) + .with('String', () => 'text') + .with('Boolean', () => 'boolean') + .with('Int', () => 'integer') + .with('BigInt', () => 'bigint') + .with('Float', () => 'double precision') + .with('Decimal', () => 'decimal(65,30)') + .with('DateTime', () => 'timestamp(3)') + .with('Bytes', () => 'bytea') + .with('Json', () => 'jsonb') + .otherwise(() => undefined); } } @@ -414,8 +415,12 @@ export class PostgresCrudDialect extends LateralJoinDi .select( fields.map((f, i) => { const mappedType = this.getSqlType(f.type); - const castType = f.array ? sql`${sql.raw(mappedType)}[]` : sql.raw(mappedType); - return this.eb.cast(sql.ref(`$values.column${i + 1}`), castType).as(f.name); + if (mappedType) { + const castType = f.array ? sql`${sql.raw(mappedType)}[]` : sql.raw(mappedType); + return this.eb.cast(sql.ref(`$values.column${i + 1}`), castType).as(f.name); + } else { + return sql.ref(`$values.column${i + 1}`).as(f.name); + } }), ); } diff --git a/packages/orm/src/client/crud/dialects/sqlite.ts b/packages/orm/src/client/crud/dialects/sqlite.ts index 95e2910f8..73712dd57 100644 --- a/packages/orm/src/client/crud/dialects/sqlite.ts +++ b/packages/orm/src/client/crud/dialects/sqlite.ts @@ -22,6 +22,7 @@ import { getDelegateDescendantModels, getManyToManyRelation, getRelationForeignKeyFieldPairs, + isEnum, requireField, requireIdFields, requireModel, @@ -488,6 +489,23 @@ export class SqliteCrudDialect extends BaseCrudDialect return this.eb.fn('trim', [expression, sql.lit('"')]) as unknown as T; } + protected override getSqlType(zmodelType: string) { + if (isEnum(this.schema, zmodelType)) { + return 'text'; + } + return match(zmodelType) + .with('String', () => 'text') + .with('Boolean', () => 'integer') + .with('Int', () => 'integer') + .with('BigInt', () => 'integer') + .with('Float', () => 'real') + .with('Decimal', () => 'decimal') + .with('DateTime', () => 'numeric') + .with('Bytes', () => 'blob') + .with('Json', () => 'jsonb') + .otherwise(() => undefined); + } + override getStringCasingBehavior() { // SQLite `LIKE` is case-insensitive, and there is no `ILIKE` return { supportsILike: false, likeCaseSensitive: false }; diff --git a/packages/plugins/policy/src/expression-transformer.ts b/packages/plugins/policy/src/expression-transformer.ts index 2a237c5cb..c9d9ed8c7 100644 --- a/packages/plugins/policy/src/expression-transformer.ts +++ b/packages/plugins/policy/src/expression-transformer.ts @@ -908,7 +908,7 @@ export class ExpressionTransformer { const fieldDef = QueryUtils.requireField(this.schema, context.modelOrType, column); if (!fieldDef.originModel || fieldDef.originModel === context.modelOrType) { - return ReferenceNode.create(ColumnNode.create(column), TableNode.create(tableName)); + return this.dialect.fieldRef(context.modelOrType, column, tableName, false).toOperationNode(); } return this.buildDelegateBaseFieldSelect(context.modelOrType, tableName, column, fieldDef.originModel); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5ac83d9e..a28d48448 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1151,6 +1151,9 @@ importers: decimal.js: specifier: 'catalog:' version: 10.6.0 + uuid: + specifier: ^11.0.5 + version: 11.0.5 devDependencies: '@types/node': specifier: 'catalog:' @@ -7360,6 +7363,7 @@ packages: prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} + deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. hasBin: true prelude-ls@1.2.1: @@ -13234,7 +13238,7 @@ snapshots: eslint: 9.29.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-react-hooks: 7.0.1(eslint@9.29.0(jiti@2.6.1)) @@ -13267,7 +13271,7 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -13282,7 +13286,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 diff --git a/tests/regression/package.json b/tests/regression/package.json index cdd511a7e..707c54afd 100644 --- a/tests/regression/package.json +++ b/tests/regression/package.json @@ -13,17 +13,18 @@ }, "dependencies": { "@zenstackhq/testtools": "workspace:*", - "decimal.js": "catalog:" + "decimal.js": "catalog:", + "uuid": "^11.0.5" }, "devDependencies": { + "@types/node": "catalog:", "@zenstackhq/cli": "workspace:*", "@zenstackhq/language": "workspace:*", - "@zenstackhq/schema": "workspace:*", "@zenstackhq/orm": "workspace:*", - "@zenstackhq/sdk": "workspace:*", "@zenstackhq/plugin-policy": "workspace:*", + "@zenstackhq/schema": "workspace:*", + "@zenstackhq/sdk": "workspace:*", "@zenstackhq/typescript-config": "workspace:*", - "@zenstackhq/vitest-config": "workspace:*", - "@types/node": "catalog:" + "@zenstackhq/vitest-config": "workspace:*" } } diff --git a/tests/regression/test/issue-2394.test.ts b/tests/regression/test/issue-2394.test.ts new file mode 100644 index 000000000..d08a8aba4 --- /dev/null +++ b/tests/regression/test/issue-2394.test.ts @@ -0,0 +1,24 @@ +import { createPolicyTestClient } from '@zenstackhq/testtools'; +import { v4 as uuid } from 'uuid'; +import { describe, expect, it } from 'vitest'; + +describe('Regression for issue #2394', () => { + const UUID_SCHEMA = ` +model Foo { + id String @id @db.Uuid @default(dbgenerated("gen_random_uuid()")) + x String + + @@allow('all', id == x) +} +`; + + it('works with policies', async () => { + const db = await createPolicyTestClient(UUID_SCHEMA, { + provider: 'postgresql', + usePrismaPush: true, + }); + + await db.$unuseAll().foo.create({ data: { x: uuid() } }); + await expect(db.foo.findMany()).toResolveTruthy(); + }); +}); From b48b5535fe46d02e53c9f5b965ebda9d6abab514 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 10 Mar 2026 17:32:09 -0700 Subject: [PATCH 2/5] fix(orm): cast delegate base fields via dialect.fieldRef in policy transformer Use dialect.fieldRef() for the selection in buildDelegateBaseFieldSelect so inherited fields with @db.* attributes get the same CAST treatment as same-model fields. Co-Authored-By: Claude Opus 4.6 --- packages/orm/src/client/crud/dialects/mysql.ts | 8 ++++---- packages/plugins/policy/src/expression-transformer.ts | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/orm/src/client/crud/dialects/mysql.ts b/packages/orm/src/client/crud/dialects/mysql.ts index c1f452894..1954ff2d7 100644 --- a/packages/orm/src/client/crud/dialects/mysql.ts +++ b/packages/orm/src/client/crud/dialects/mysql.ts @@ -323,14 +323,14 @@ export class MySqlCrudDialect extends LateralJoinDiale return 'varchar(191)'; } return match(zmodelType) - .with('String', () => 'varchar(191)') - .with('Boolean', () => 'tinyint(1)') + .with('String', () => 'char') + .with('Boolean', () => 'unsigned') .with('Int', () => 'signed') - .with('BigInt', () => 'bigint') + .with('BigInt', () => 'signed') .with('Float', () => 'double') .with('Decimal', () => 'decimal(65,30)') .with('DateTime', () => 'datetime(3)') - .with('Bytes', () => 'longblob') + .with('Bytes', () => 'binary') .with('Json', () => 'json') .otherwise(() => undefined); } diff --git a/packages/plugins/policy/src/expression-transformer.ts b/packages/plugins/policy/src/expression-transformer.ts index c9d9ed8c7..afa299be2 100644 --- a/packages/plugins/policy/src/expression-transformer.ts +++ b/packages/plugins/policy/src/expression-transformer.ts @@ -936,7 +936,9 @@ export class ExpressionTransformer { kind: 'SelectQueryNode', from: FromNode.create([TableNode.create(baseModel)]), selections: [ - SelectionNode.create(ReferenceNode.create(ColumnNode.create(field), TableNode.create(baseModel))), + SelectionNode.create( + this.dialect.fieldRef(baseModel, field, baseModel, false).toOperationNode() + ), ], where: WhereNode.create( conjunction( From be227e1669ef90066fa0047af437f7cc5a836302 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 10 Mar 2026 17:37:49 -0700 Subject: [PATCH 3/5] fix(orm): respect field arrayness when casting @db.* fields When a field with a @db.* attribute is also an array, the CAST type should use the array form (e.g., `text[]` instead of `text`). Co-Authored-By: Claude Opus 4.6 --- packages/orm/src/client/crud/dialects/base-dialect.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/orm/src/client/crud/dialects/base-dialect.ts b/packages/orm/src/client/crud/dialects/base-dialect.ts index 736b05560..cdd720600 100644 --- a/packages/orm/src/client/crud/dialects/base-dialect.ts +++ b/packages/orm/src/client/crud/dialects/base-dialect.ts @@ -1164,8 +1164,11 @@ export abstract class BaseCrudDialect { ? this.getSqlType(subFieldDef.type) : undefined; if (castSqlType) { + const castType = subFieldDef.array + ? sql`${sql.raw(castSqlType)}[]` + : sql.raw(castSqlType); jsonObject[field] = - sql`CAST(${sql.ref(`${subModel.name}.${field}`)} AS ${sql.raw(castSqlType)})`; + sql`CAST(${sql.ref(`${subModel.name}.${field}`)} AS ${castType})`; } else { jsonObject[field] = eb.ref(`${subModel.name}.${field}`); } @@ -1376,7 +1379,8 @@ export abstract class BaseCrudDialect { if (this.hasNativeTypeAttribute(fieldDef)) { const sqlType = this.getSqlType(fieldDef.type); if (sqlType) { - return sql`CAST(${sql.ref(ref)} AS ${sql.raw(sqlType)})`; + const castType = fieldDef.array ? sql`${sql.raw(sqlType)}[]` : sql.raw(sqlType); + return sql`CAST(${sql.ref(ref)} AS ${castType})`; } } From d1a1b17bd34f40e76b4ba0eb61482506debb69cc Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 10 Mar 2026 17:54:07 -0700 Subject: [PATCH 4/5] fix(policy): use dialect.fieldRef for post-update before-join on id fields The post-update policy check joins the current table with a VALUES table ($before) on id fields. When an id field has @db.Uuid, the raw column is uuid while the VALUES table casts to text, causing a type mismatch. Use dialect.fieldRef() for both sides of the join so the uuid column gets cast to text. Co-Authored-By: Claude Opus 4.6 --- packages/plugins/policy/src/policy-handler.ts | 13 +++++++++++- tests/regression/test/issue-2394.test.ts | 20 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/plugins/policy/src/policy-handler.ts b/packages/plugins/policy/src/policy-handler.ts index 968703957..c52732ab4 100644 --- a/packages/plugins/policy/src/policy-handler.ts +++ b/packages/plugins/policy/src/policy-handler.ts @@ -311,7 +311,18 @@ export class PolicyHandler extends OperationNodeTransf () => new ExpressionWrapper(beforeUpdateTable!).as('$before'), (join) => { const idFields = QueryUtils.requireIdFields(this.client.$schema, model); - return idFields.reduce((acc, f) => acc.onRef(`${model}.${f}`, '=', `$before.${f}`), join); + const eb = expressionBuilder(); + return idFields.reduce( + (acc, f) => + acc.on(() => + eb( + this.dialect.fieldRef(model, f, model, false), + '=', + this.dialect.fieldRef(model, f, '$before', false), + ), + ), + join, + ); }, ), ); diff --git a/tests/regression/test/issue-2394.test.ts b/tests/regression/test/issue-2394.test.ts index d08a8aba4..fe79d899f 100644 --- a/tests/regression/test/issue-2394.test.ts +++ b/tests/regression/test/issue-2394.test.ts @@ -21,4 +21,24 @@ model Foo { await db.$unuseAll().foo.create({ data: { x: uuid() } }); await expect(db.foo.findMany()).toResolveTruthy(); }); + + it('works with post-update policies', async () => { + const db = await createPolicyTestClient( + ` +model ExchangeRequest { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + status String + + @@allow('all', true) + @@deny('post-update', before().status == status) // triggers buildValuesTableSelect +} +`, + { provider: 'postgresql', usePrismaPush: true, debug: true }, + ); + + const request = await db.exchangeRequest.create({ data: { status: 'pending' } }); + await expect( + db.exchangeRequest.update({ where: { id: request.id }, data: { status: 'done' } }), + ).toResolveTruthy(); + }); }); From c737037e4e083d219b007b1112cd6d35577dbc92 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 10 Mar 2026 17:55:44 -0700 Subject: [PATCH 5/5] addressing PR comments --- .../orm/src/client/crud/dialects/base-dialect.ts | 16 ++-------------- tests/regression/test/issue-2394.test.ts | 2 +- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/packages/orm/src/client/crud/dialects/base-dialect.ts b/packages/orm/src/client/crud/dialects/base-dialect.ts index cdd720600..adb88bd6c 100644 --- a/packages/orm/src/client/crud/dialects/base-dialect.ts +++ b/packages/orm/src/client/crud/dialects/base-dialect.ts @@ -1150,7 +1150,7 @@ export abstract class BaseCrudDialect { const descendants = getDelegateDescendantModels(this.schema, model); for (const subModel of descendants) { result = this.buildDelegateJoin(model, modelAlias, subModel.name, result); - result = result.select((eb) => { + result = result.select(() => { const jsonObject: Record> = {}; for (const field of Object.keys(subModel.fields)) { if ( @@ -1159,19 +1159,7 @@ export abstract class BaseCrudDialect { ) { continue; } - const subFieldDef = requireField(this.schema, subModel.name, field); - const castSqlType = this.hasNativeTypeAttribute(subFieldDef) - ? this.getSqlType(subFieldDef.type) - : undefined; - if (castSqlType) { - const castType = subFieldDef.array - ? sql`${sql.raw(castSqlType)}[]` - : sql.raw(castSqlType); - jsonObject[field] = - sql`CAST(${sql.ref(`${subModel.name}.${field}`)} AS ${castType})`; - } else { - jsonObject[field] = eb.ref(`${subModel.name}.${field}`); - } + jsonObject[field] = this.fieldRef(subModel.name, field, subModel.name); } return this.buildJsonObject(jsonObject).as(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`); }); diff --git a/tests/regression/test/issue-2394.test.ts b/tests/regression/test/issue-2394.test.ts index fe79d899f..0b4b5df22 100644 --- a/tests/regression/test/issue-2394.test.ts +++ b/tests/regression/test/issue-2394.test.ts @@ -33,7 +33,7 @@ model ExchangeRequest { @@deny('post-update', before().status == status) // triggers buildValuesTableSelect } `, - { provider: 'postgresql', usePrismaPush: true, debug: true }, + { provider: 'postgresql', usePrismaPush: true }, ); const request = await db.exchangeRequest.create({ data: { status: 'pending' } });