Skip to content

Commit fd689b5

Browse files
committed
fix(orm): handle mapping enum array (#2818)
1 parent a2fe1ce commit fd689b5

2 files changed

Lines changed: 94 additions & 6 deletions

File tree

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

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
OperationNodeTransformer,
1717
PrimitiveValueListNode,
1818
type QueryId,
19+
RawNode,
1920
ReferenceNode,
2021
ReturningNode,
2122
SelectAllNode,
@@ -763,6 +764,21 @@ export class QueryNameMapper extends OperationNodeTransformer {
763764
if (mappedValue) {
764765
return mappedValue;
765766
}
767+
} else if (
768+
this.isOperationNode(value) &&
769+
ValueNode.is(value) &&
770+
Array.isArray(value.value) &&
771+
value.value.every((v) => typeof v === 'string')
772+
) {
773+
const everyMappedValueExists = value.value.every((v) => enumValueMapping[v]);
774+
if (everyMappedValueExists) {
775+
return ValueNode.create(value.value.map((v) => enumValueMapping[v]));
776+
}
777+
} else if (Array.isArray(value) && value.every((v) => typeof v === 'string')) {
778+
const everyMappedValueExists = value.every((v) => enumValueMapping[v]);
779+
if (everyMappedValueExists) {
780+
return value.map((v) => enumValueMapping[v]);
781+
}
766782
}
767783

768784
return value;
@@ -789,23 +805,39 @@ export class QueryNameMapper extends OperationNodeTransformer {
789805
return selection;
790806
}
791807

808+
const shouldUseArray = fieldDef.array;
809+
792810
const eb = expressionBuilder();
793-
const caseBuilder = eb.case();
811+
const caseNode = shouldUseArray ? ColumnNode.create('t') : node;
794812
let caseWhen: CaseWhenBuilder<any, any, any, any> | undefined;
813+
795814
for (const [key, value] of Object.entries(enumValueMapping)) {
796815
if (!caseWhen) {
797-
caseWhen = caseBuilder.when(new ExpressionWrapper(node), '=', value).then(key);
816+
caseWhen = eb.case().when(new ExpressionWrapper(caseNode), '=', value).then(key);
798817
} else {
799-
caseWhen = caseWhen.when(new ExpressionWrapper(node), '=', value).then(key);
818+
caseWhen = caseWhen.when(new ExpressionWrapper(caseNode), '=', value).then(key);
800819
}
801820
}
802821

803822
// the explicit cast to "text" is needed to address postgres's case-when type inference issue
804-
const finalExpr = caseWhen!.else(this.dialect.castText(new ExpressionWrapper(node))).end();
823+
const caseExpr = caseWhen!.else(this.dialect.castText(new ExpressionWrapper(caseNode))).end();
824+
825+
if (!shouldUseArray) {
826+
if (aliasName) {
827+
return caseExpr.as(aliasName).toOperationNode() as SelectionNodeChild;
828+
} else {
829+
return caseExpr.toOperationNode() as SelectionNodeChild;
830+
}
831+
}
832+
833+
const arrayExpr = new ExpressionWrapper(
834+
RawNode.create(['ARRAY(SELECT ', ' FROM unnest( ', ' ) AS t)'], [caseExpr.toOperationNode(), node]),
835+
);
836+
805837
if (aliasName) {
806-
return finalExpr.as(aliasName).toOperationNode() as SelectionNodeChild;
838+
return arrayExpr.as(aliasName).toOperationNode() as SelectionNodeChild;
807839
} else {
808-
return finalExpr.toOperationNode() as SelectionNodeChild;
840+
return arrayExpr.toOperationNode() as SelectionNodeChild;
809841
}
810842
}
811843

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { createTestClient } from '@zenstackhq/testtools';
2+
import { describe, expect, it } from 'vitest';
3+
4+
// https://github.com/zenstackhq/zenstack/issues/2818
5+
describe('Regression for issue #2818', () => {
6+
it('supported enum array', async () => {
7+
const schema = `
8+
enum TestTag {
9+
INFO @map("info")
10+
WARN @map("warn")
11+
12+
@@map("post_tag")
13+
}
14+
15+
model Test {
16+
id Int @id
17+
tag TestTag?
18+
tags TestTag[]
19+
20+
@@map("test")
21+
}
22+
`;
23+
24+
const db = await createTestClient(schema, { usePrismaPush: true, provider: 'postgresql', debug: true });
25+
26+
await db.test.create({ data: { id: 0 } });
27+
await db.test.create({ data: { id: 1, tag: 'INFO' } });
28+
await db.test.create({ data: { id: 2, tags: [] } });
29+
await db.test.create({ data: { id: 3, tags: ['INFO'] } });
30+
await db.test.create({ data: { id: 4, tags: ['INFO', 'WARN'] } });
31+
32+
await expect(db.test.findMany({ orderBy: { id: 'asc' } })).resolves.toEqual([
33+
{ id: 0, tag: null, tags: [] },
34+
{ id: 1, tag: 'INFO', tags: [] },
35+
{ id: 2, tag: null, tags: [] },
36+
{ id: 3, tag: null, tags: ['INFO'] },
37+
{ id: 4, tag: null, tags: ['INFO', 'WARN'] },
38+
]);
39+
40+
await db.$qb.updateTable('test').set({ tag: 'WARN' }).where('id', '=', 2).executeTakeFirst();
41+
await db.$qb
42+
.updateTable('test')
43+
.set({ tags: ['INFO', 'WARN'] })
44+
.where('id', '=', 2)
45+
.executeTakeFirst();
46+
await db.$qb.updateTable('test').set({ tags: [] }).where('id', '=', 4).executeTakeFirst();
47+
48+
await expect(db.test.findMany({ orderBy: { id: 'asc' } })).resolves.toEqual([
49+
{ id: 0, tag: null, tags: [] },
50+
{ id: 1, tag: 'INFO', tags: [] },
51+
{ id: 2, tag: 'WARN', tags: ['INFO', 'WARN'] },
52+
{ id: 3, tag: null, tags: ['INFO'] },
53+
{ id: 4, tag: null, tags: [] },
54+
]);
55+
});
56+
});

0 commit comments

Comments
 (0)