Skip to content
This repository was archived by the owner on Mar 1, 2026. It is now read-only.

Commit 7a40cba

Browse files
committed
fix: resolve field's model from scope instead of relying on table qualification
1 parent ce999fd commit 7a40cba

2 files changed

Lines changed: 74 additions & 34 deletions

File tree

packages/orm/src/client/executor/name-mapper.ts

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -184,39 +184,51 @@ export class QueryNameMapper extends OperationNodeTransformer {
184184
return ColumnNode.create(mappedName);
185185
}
186186

187-
protected override transformBinaryOperation(node: BinaryOperationNode): BinaryOperationNode {
187+
protected override transformBinaryOperation(node: BinaryOperationNode) {
188+
// transform enum name mapping for enum values used inside binary operations
189+
// 1. simple value: column = EnumValue
190+
// 2. list value: column IN [EnumValue, EnumValue2]
191+
192+
// note: Kysely only allows column ref on the left side of a binary operation
193+
188194
if (
189195
ReferenceNode.is(node.leftOperand) &&
190196
ColumnNode.is(node.leftOperand.column) &&
191-
node.leftOperand.table &&
192-
TableNode.is(node.leftOperand.table) &&
193-
// { enumColumn: Enum.VALUE }
194-
(ValueNode.is(node.rightOperand) ||
195-
// { enumColumn: { in: [Enum.VALUE] } }
196-
PrimitiveValueListNode.is(node.rightOperand))
197+
(ValueNode.is(node.rightOperand) || PrimitiveValueListNode.is(node.rightOperand))
197198
) {
198-
const tableName = node.leftOperand.table.table.identifier.name;
199199
const columnNode = node.leftOperand.column;
200-
const valueNode = node.rightOperand;
201-
202-
let resultValue: OperationNode = valueNode;
203-
204-
if (ValueNode.is(valueNode)) {
205-
resultValue = this.processEnumMappingForValue(tableName, columnNode, valueNode) as OperationNode;
206-
} else if (PrimitiveValueListNode.is(valueNode)) {
207-
resultValue = PrimitiveValueListNode.create(
208-
this.processEnumMappingForValues(
209-
tableName,
210-
valueNode.values.map(() => columnNode),
211-
valueNode.values,
212-
),
213-
);
214-
}
215200

216-
return super.transformBinaryOperation({
217-
...node,
218-
rightOperand: resultValue,
219-
});
201+
// resolve field from scope in case it's not directly qualified with a table name
202+
const resolvedScope = this.resolveFieldFromScopes(
203+
columnNode.column.name,
204+
node.leftOperand.table?.table.identifier.name,
205+
);
206+
207+
if (resolvedScope?.model) {
208+
const valueNode = node.rightOperand;
209+
let resultValue: OperationNode = valueNode;
210+
211+
if (ValueNode.is(valueNode)) {
212+
resultValue = this.processEnumMappingForValue(
213+
resolvedScope.model,
214+
columnNode,
215+
valueNode,
216+
) as OperationNode;
217+
} else if (PrimitiveValueListNode.is(valueNode)) {
218+
resultValue = PrimitiveValueListNode.create(
219+
this.processEnumMappingForValues(
220+
resolvedScope.model,
221+
valueNode.values.map(() => columnNode),
222+
valueNode.values,
223+
),
224+
);
225+
}
226+
227+
return super.transformBinaryOperation({
228+
...node,
229+
rightOperand: resultValue,
230+
});
231+
}
220232
}
221233

222234
return super.transformBinaryOperation(node);

tests/e2e/orm/client-api/name-mapping.test.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ describe('Name mapping tests', () => {
88
let db: ClientContract<SchemaType>;
99

1010
beforeEach(async () => {
11-
db = (await createTestClient(schema, {
11+
db = await createTestClient(schema, {
1212
usePrismaPush: true,
1313
schemaFile: path.join(__dirname, '../schemas/name-mapping/schema.zmodel'),
14-
})) as any;
14+
});
1515
});
1616

1717
afterEach(async () => {
@@ -214,11 +214,7 @@ describe('Name mapping tests', () => {
214214
await expect(
215215
db.user.findMany({
216216
where: {
217-
AND: [
218-
{ role: { in: ['USER'] } },
219-
{ role: { in: ['USER'] } },
220-
{ OR: [{ role: { in: ['USER'] } }] },
221-
],
217+
AND: [{ role: { in: ['USER'] } }, { role: { in: ['USER'] } }, { OR: [{ role: { in: ['USER'] } }] }],
222218
},
223219
select: {
224220
email: true,
@@ -266,6 +262,38 @@ describe('Name mapping tests', () => {
266262
role: 'USER',
267263
});
268264

265+
// name mapping for enum value in where clause, with unqualified column name
266+
await expect(
267+
db.$qb.selectFrom('User').select(['User.email', 'User.role']).where('role', '=', 'USER').executeTakeFirst(),
268+
).resolves.toMatchObject({
269+
email: 'u1@test.com',
270+
role: 'USER',
271+
});
272+
273+
// name mapping for enum value in simple where clause, with qualified column name
274+
await expect(
275+
db.$qb
276+
.selectFrom('User as u')
277+
.select(['u.email', 'u.role'])
278+
.where('u.role', '=', 'USER')
279+
.executeTakeFirst(),
280+
).resolves.toMatchObject({
281+
email: 'u1@test.com',
282+
role: 'USER',
283+
});
284+
285+
// enum value in list
286+
await expect(
287+
db.$qb
288+
.selectFrom('User')
289+
.select(['User.email', 'User.role'])
290+
.where('role', 'in', ['USER', 'ADMIN'])
291+
.executeTakeFirst(),
292+
).resolves.toMatchObject({
293+
email: 'u1@test.com',
294+
role: 'USER',
295+
});
296+
269297
await expect(
270298
db.$qb
271299
.selectFrom('User')

0 commit comments

Comments
 (0)