@simplysm/orm-common 13.0.68 → 13.0.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -1447
- package/dist/create-db-context.d.ts +10 -10
- package/dist/create-db-context.js +9 -9
- package/dist/create-db-context.js.map +1 -1
- package/dist/ddl/column-ddl.d.ts +4 -4
- package/dist/ddl/initialize.d.ts +17 -17
- package/dist/ddl/initialize.js +2 -2
- package/dist/ddl/initialize.js.map +1 -1
- package/dist/ddl/relation-ddl.d.ts +6 -6
- package/dist/ddl/schema-ddl.d.ts +4 -4
- package/dist/ddl/table-ddl.d.ts +24 -24
- package/dist/ddl/table-ddl.js +4 -4
- package/dist/ddl/table-ddl.js.map +1 -1
- package/dist/errors/db-transaction-error.d.ts +15 -15
- package/dist/errors/db-transaction-error.d.ts.map +1 -1
- package/dist/exec/executable.d.ts +23 -23
- package/dist/exec/executable.js +3 -3
- package/dist/exec/executable.js.map +1 -1
- package/dist/exec/queryable.d.ts +160 -160
- package/dist/exec/queryable.js +119 -119
- package/dist/exec/queryable.js.map +1 -1
- package/dist/exec/search-parser.d.ts +37 -37
- package/dist/exec/search-parser.d.ts.map +1 -1
- package/dist/expr/expr-unit.d.ts +4 -4
- package/dist/expr/expr.d.ts +257 -257
- package/dist/expr/expr.js +265 -265
- package/dist/expr/expr.js.map +1 -1
- package/dist/query-builder/base/expr-renderer-base.d.ts +9 -9
- package/dist/query-builder/base/expr-renderer-base.js +2 -2
- package/dist/query-builder/base/expr-renderer-base.js.map +1 -1
- package/dist/query-builder/base/query-builder-base.d.ts +26 -26
- package/dist/query-builder/base/query-builder-base.d.ts.map +1 -1
- package/dist/query-builder/base/query-builder-base.js +22 -22
- package/dist/query-builder/base/query-builder-base.js.map +1 -1
- package/dist/query-builder/mssql/mssql-expr-renderer.d.ts +4 -4
- package/dist/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/mssql/mssql-expr-renderer.js +18 -18
- package/dist/query-builder/mssql/mssql-expr-renderer.js.map +1 -1
- package/dist/query-builder/mssql/mssql-query-builder.d.ts +2 -2
- package/dist/query-builder/mssql/mssql-query-builder.d.ts.map +1 -1
- package/dist/query-builder/mssql/mssql-query-builder.js +11 -11
- package/dist/query-builder/mssql/mssql-query-builder.js.map +1 -1
- package/dist/query-builder/mysql/mysql-expr-renderer.d.ts +4 -4
- package/dist/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/mysql/mysql-expr-renderer.js +17 -17
- package/dist/query-builder/mysql/mysql-expr-renderer.js.map +1 -1
- package/dist/query-builder/mysql/mysql-query-builder.d.ts +8 -8
- package/dist/query-builder/mysql/mysql-query-builder.d.ts.map +1 -1
- package/dist/query-builder/mysql/mysql-query-builder.js +5 -5
- package/dist/query-builder/mysql/mysql-query-builder.js.map +1 -1
- package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts +4 -4
- package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/postgresql/postgresql-expr-renderer.js +17 -17
- package/dist/query-builder/postgresql/postgresql-expr-renderer.js.map +1 -1
- package/dist/query-builder/postgresql/postgresql-query-builder.d.ts +5 -5
- package/dist/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -1
- package/dist/query-builder/postgresql/postgresql-query-builder.js +8 -8
- package/dist/query-builder/postgresql/postgresql-query-builder.js.map +1 -1
- package/dist/query-builder/query-builder.d.ts +1 -1
- package/dist/schema/factory/column-builder.d.ts +79 -79
- package/dist/schema/factory/column-builder.js +42 -42
- package/dist/schema/factory/index-builder.d.ts +39 -39
- package/dist/schema/factory/index-builder.js +26 -26
- package/dist/schema/factory/relation-builder.d.ts +99 -99
- package/dist/schema/factory/relation-builder.d.ts.map +1 -1
- package/dist/schema/factory/relation-builder.js +38 -38
- package/dist/schema/procedure-builder.d.ts +49 -49
- package/dist/schema/procedure-builder.d.ts.map +1 -1
- package/dist/schema/procedure-builder.js +33 -33
- package/dist/schema/table-builder.d.ts +59 -59
- package/dist/schema/table-builder.d.ts.map +1 -1
- package/dist/schema/table-builder.js +43 -43
- package/dist/schema/view-builder.d.ts +49 -49
- package/dist/schema/view-builder.d.ts.map +1 -1
- package/dist/schema/view-builder.js +32 -32
- package/dist/types/column.d.ts +22 -22
- package/dist/types/column.js +1 -1
- package/dist/types/column.js.map +1 -1
- package/dist/types/db.d.ts +40 -40
- package/dist/types/expr.d.ts +59 -59
- package/dist/types/expr.d.ts.map +1 -1
- package/dist/types/query-def.d.ts +44 -44
- package/dist/types/query-def.d.ts.map +1 -1
- package/dist/utils/result-parser.d.ts +11 -11
- package/dist/utils/result-parser.js +3 -3
- package/dist/utils/result-parser.js.map +1 -1
- package/package.json +5 -5
- package/src/create-db-context.ts +20 -20
- package/src/ddl/column-ddl.ts +4 -4
- package/src/ddl/initialize.ts +259 -259
- package/src/ddl/relation-ddl.ts +89 -89
- package/src/ddl/schema-ddl.ts +4 -4
- package/src/ddl/table-ddl.ts +189 -189
- package/src/errors/db-transaction-error.ts +13 -13
- package/src/exec/executable.ts +25 -25
- package/src/exec/queryable.ts +2033 -2033
- package/src/exec/search-parser.ts +57 -57
- package/src/expr/expr-unit.ts +4 -4
- package/src/expr/expr.ts +2140 -2140
- package/src/query-builder/base/expr-renderer-base.ts +237 -237
- package/src/query-builder/base/query-builder-base.ts +213 -213
- package/src/query-builder/mssql/mssql-expr-renderer.ts +607 -607
- package/src/query-builder/mssql/mssql-query-builder.ts +650 -650
- package/src/query-builder/mysql/mysql-expr-renderer.ts +613 -613
- package/src/query-builder/mysql/mysql-query-builder.ts +759 -759
- package/src/query-builder/postgresql/postgresql-expr-renderer.ts +611 -611
- package/src/query-builder/postgresql/postgresql-query-builder.ts +686 -686
- package/src/query-builder/query-builder.ts +19 -19
- package/src/schema/factory/column-builder.ts +423 -423
- package/src/schema/factory/index-builder.ts +164 -164
- package/src/schema/factory/relation-builder.ts +453 -453
- package/src/schema/procedure-builder.ts +232 -232
- package/src/schema/table-builder.ts +319 -319
- package/src/schema/view-builder.ts +221 -221
- package/src/types/column.ts +188 -188
- package/src/types/db.ts +208 -208
- package/src/types/expr.ts +697 -697
- package/src/types/query-def.ts +513 -513
- package/src/utils/result-parser.ts +458 -458
- package/tests/db-context/create-db-context.spec.ts +224 -0
- package/tests/db-context/define-db-context.spec.ts +68 -0
- package/tests/ddl/basic.expected.ts +341 -0
- package/tests/ddl/basic.spec.ts +714 -0
- package/tests/ddl/column-builder.expected.ts +310 -0
- package/tests/ddl/column-builder.spec.ts +637 -0
- package/tests/ddl/index-builder.expected.ts +38 -0
- package/tests/ddl/index-builder.spec.ts +202 -0
- package/tests/ddl/procedure-builder.expected.ts +52 -0
- package/tests/ddl/procedure-builder.spec.ts +234 -0
- package/tests/ddl/relation-builder.expected.ts +36 -0
- package/tests/ddl/relation-builder.spec.ts +372 -0
- package/tests/ddl/table-builder.expected.ts +113 -0
- package/tests/ddl/table-builder.spec.ts +433 -0
- package/tests/ddl/view-builder.expected.ts +38 -0
- package/tests/ddl/view-builder.spec.ts +176 -0
- package/tests/dml/delete.expected.ts +96 -0
- package/tests/dml/delete.spec.ts +160 -0
- package/tests/dml/insert.expected.ts +192 -0
- package/tests/dml/insert.spec.ts +288 -0
- package/tests/dml/update.expected.ts +176 -0
- package/tests/dml/update.spec.ts +318 -0
- package/tests/dml/upsert.expected.ts +215 -0
- package/tests/dml/upsert.spec.ts +242 -0
- package/tests/errors/queryable-errors.spec.ts +177 -0
- package/tests/escape.spec.ts +100 -0
- package/tests/examples/pivot.expected.ts +211 -0
- package/tests/examples/pivot.spec.ts +533 -0
- package/tests/examples/sampling.expected.ts +69 -0
- package/tests/examples/sampling.spec.ts +104 -0
- package/tests/examples/unpivot.expected.ts +120 -0
- package/tests/examples/unpivot.spec.ts +226 -0
- package/tests/exec/search-parser.spec.ts +283 -0
- package/tests/executable/basic.expected.ts +18 -0
- package/tests/executable/basic.spec.ts +54 -0
- package/tests/expr/comparison.expected.ts +282 -0
- package/tests/expr/comparison.spec.ts +400 -0
- package/tests/expr/conditional.expected.ts +134 -0
- package/tests/expr/conditional.spec.ts +276 -0
- package/tests/expr/date.expected.ts +332 -0
- package/tests/expr/date.spec.ts +526 -0
- package/tests/expr/math.expected.ts +62 -0
- package/tests/expr/math.spec.ts +106 -0
- package/tests/expr/string.expected.ts +218 -0
- package/tests/expr/string.spec.ts +356 -0
- package/tests/expr/utility.expected.ts +147 -0
- package/tests/expr/utility.spec.ts +182 -0
- package/tests/select/basic.expected.ts +322 -0
- package/tests/select/basic.spec.ts +502 -0
- package/tests/select/filter.expected.ts +357 -0
- package/tests/select/filter.spec.ts +1068 -0
- package/tests/select/group.expected.ts +169 -0
- package/tests/select/group.spec.ts +244 -0
- package/tests/select/join.expected.ts +582 -0
- package/tests/select/join.spec.ts +805 -0
- package/tests/select/order.expected.ts +150 -0
- package/tests/select/order.spec.ts +189 -0
- package/tests/select/recursive-cte.expected.ts +244 -0
- package/tests/select/recursive-cte.spec.ts +514 -0
- package/tests/select/result-meta.spec.ts +270 -0
- package/tests/select/subquery.expected.ts +363 -0
- package/tests/select/subquery.spec.ts +537 -0
- package/tests/select/view.expected.ts +155 -0
- package/tests/select/view.spec.ts +235 -0
- package/tests/select/window.expected.ts +345 -0
- package/tests/select/window.spec.ts +618 -0
- package/tests/setup/MockExecutor.ts +18 -0
- package/tests/setup/TestDbContext.ts +59 -0
- package/tests/setup/models/Company.ts +13 -0
- package/tests/setup/models/Employee.ts +10 -0
- package/tests/setup/models/MonthlySales.ts +11 -0
- package/tests/setup/models/Post.ts +16 -0
- package/tests/setup/models/Sales.ts +10 -0
- package/tests/setup/models/User.ts +19 -0
- package/tests/setup/procedure/GetAllUsers.ts +9 -0
- package/tests/setup/procedure/GetUserById.ts +12 -0
- package/tests/setup/test-utils.ts +72 -0
- package/tests/setup/views/ActiveUsers.ts +8 -0
- package/tests/setup/views/UserSummary.ts +11 -0
- package/tests/types/nullable-queryable-record.spec.ts +145 -0
- package/tests/utils/result-parser-perf.spec.ts +210 -0
- package/tests/utils/result-parser.spec.ts +701 -0
- package/docs/expressions.md +0 -172
- package/docs/queries.md +0 -444
- package/docs/schema.md +0 -245
|
@@ -1,453 +1,453 @@
|
|
|
1
|
-
import { type InferColumns } from "./column-builder";
|
|
2
|
-
import type { TableBuilder } from "../table-builder";
|
|
3
|
-
import type { ViewBuilder } from "../view-builder";
|
|
4
|
-
|
|
5
|
-
// ============================================
|
|
6
|
-
// ForeignKeyBuilder
|
|
7
|
-
// ============================================
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Foreign Key
|
|
11
|
-
*
|
|
12
|
-
* 현재
|
|
13
|
-
* DB에 실제 FK
|
|
14
|
-
*
|
|
15
|
-
* @template TOwner - 소유
|
|
16
|
-
* @template TTargetFn - 참조
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```typescript
|
|
20
|
-
* const Post = Table("Post")
|
|
21
|
-
* .columns((c) => ({
|
|
22
|
-
* id: c.bigint().autoIncrement(),
|
|
23
|
-
* authorId: c.bigint(), // FK
|
|
24
|
-
* }))
|
|
25
|
-
* .primaryKey("id")
|
|
26
|
-
* .relations((r) => ({
|
|
27
|
-
* // N:1
|
|
28
|
-
* author: r.foreignKey(["authorId"], () => User),
|
|
29
|
-
* }));
|
|
30
|
-
* ```
|
|
31
|
-
*
|
|
32
|
-
* @see {@link ForeignKeyTargetBuilder} 역참조
|
|
33
|
-
* @see {@link RelationKeyBuilder} DB FK 없는
|
|
34
|
-
*/
|
|
35
|
-
export class ForeignKeyBuilder<
|
|
36
|
-
TOwner extends TableBuilder<any, any>,
|
|
37
|
-
TTargetFn extends () => TableBuilder<any, any>,
|
|
38
|
-
> {
|
|
39
|
-
/**
|
|
40
|
-
* @param meta - FK
|
|
41
|
-
* @param meta.ownerFn - 소유
|
|
42
|
-
* @param meta.columns - FK
|
|
43
|
-
* @param meta.targetFn - 참조
|
|
44
|
-
* @param meta.description -
|
|
45
|
-
*/
|
|
46
|
-
constructor(
|
|
47
|
-
readonly meta: {
|
|
48
|
-
ownerFn: () => TOwner;
|
|
49
|
-
columns: string[];
|
|
50
|
-
targetFn: TTargetFn;
|
|
51
|
-
description?: string;
|
|
52
|
-
},
|
|
53
|
-
) {}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* @param desc -
|
|
59
|
-
* @returns 새 ForeignKeyBuilder
|
|
60
|
-
*/
|
|
61
|
-
description(desc: string): ForeignKeyBuilder<TOwner, TTargetFn> {
|
|
62
|
-
return new ForeignKeyBuilder({ ...this.meta, description: desc });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Foreign Key 역참조
|
|
68
|
-
*
|
|
69
|
-
* 다른
|
|
70
|
-
* include() 시 배열로 로드 (single() 호출 시 단일
|
|
71
|
-
*
|
|
72
|
-
* @template TTargetTableFn - 참조하는
|
|
73
|
-
* @template TIsSingle - 단일
|
|
74
|
-
*
|
|
75
|
-
* @example
|
|
76
|
-
* ```typescript
|
|
77
|
-
* const User = Table("User")
|
|
78
|
-
* .columns((c) => ({
|
|
79
|
-
* id: c.bigint().autoIncrement(),
|
|
80
|
-
* name: c.varchar(100),
|
|
81
|
-
* }))
|
|
82
|
-
* .primaryKey("id")
|
|
83
|
-
* .relations((r) => ({
|
|
84
|
-
* // 1:N
|
|
85
|
-
* posts: r.foreignKeyTarget(() => Post, "author"),
|
|
86
|
-
*
|
|
87
|
-
* // 1:1
|
|
88
|
-
* profile: r.foreignKeyTarget(() => Profile, "user").single(),
|
|
89
|
-
* }));
|
|
90
|
-
* ```
|
|
91
|
-
*
|
|
92
|
-
* @see {@link ForeignKeyBuilder} FK
|
|
93
|
-
*/
|
|
94
|
-
export class ForeignKeyTargetBuilder<
|
|
95
|
-
TTargetTableFn extends () => TableBuilder<any, any>,
|
|
96
|
-
TIsSingle extends boolean,
|
|
97
|
-
> {
|
|
98
|
-
/**
|
|
99
|
-
* @param meta - FK 역참조
|
|
100
|
-
* @param meta.targetTableFn - 참조하는
|
|
101
|
-
* @param meta.relationName - 참조하는
|
|
102
|
-
* @param meta.description -
|
|
103
|
-
* @param meta.isSingle - 단일
|
|
104
|
-
*/
|
|
105
|
-
constructor(
|
|
106
|
-
readonly meta: {
|
|
107
|
-
targetTableFn: TTargetTableFn;
|
|
108
|
-
relationName: string;
|
|
109
|
-
description?: string;
|
|
110
|
-
isSingle?: TIsSingle;
|
|
111
|
-
},
|
|
112
|
-
) {}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* @param desc -
|
|
118
|
-
* @returns 새 ForeignKeyTargetBuilder
|
|
119
|
-
*/
|
|
120
|
-
description(desc: string): ForeignKeyTargetBuilder<TTargetTableFn, TIsSingle> {
|
|
121
|
-
return new ForeignKeyTargetBuilder({ ...this.meta, description: desc });
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* 단일
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* @returns 새 ForeignKeyTargetBuilder
|
|
130
|
-
*
|
|
131
|
-
* @example
|
|
132
|
-
* ```typescript
|
|
133
|
-
* profile: r.foreignKeyTarget(() => Profile, "user").single()
|
|
134
|
-
* ```
|
|
135
|
-
*/
|
|
136
|
-
single(): ForeignKeyTargetBuilder<TTargetTableFn, true> {
|
|
137
|
-
return new ForeignKeyTargetBuilder({ ...this.meta, isSingle: true });
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// ============================================
|
|
142
|
-
// RelationKeyBuilder (FK와 동일하지만 DB에 FK 등록 안 함)
|
|
143
|
-
// ============================================
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* 논리적
|
|
147
|
-
*
|
|
148
|
-
* ForeignKeyBuilder와 동일하지만 DB에 FK 제약조건을 생성하지 않음
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
* @template TOwner - 소유
|
|
152
|
-
* @template TTargetFn - 참조
|
|
153
|
-
*
|
|
154
|
-
* @example
|
|
155
|
-
* ```typescript
|
|
156
|
-
* //
|
|
157
|
-
* const UserSummary = View("UserSummary")
|
|
158
|
-
* .query((db: MyDb) => db.user().select(...))
|
|
159
|
-
* .relations((r) => ({
|
|
160
|
-
* //
|
|
161
|
-
* company: r.relationKey(["companyId"], () => Company),
|
|
162
|
-
* }));
|
|
163
|
-
*
|
|
164
|
-
* //
|
|
165
|
-
* const Report = Table("Report")
|
|
166
|
-
* .columns((c) => ({ userId: c.bigint() }))
|
|
167
|
-
* .relations((r) => ({
|
|
168
|
-
* user: r.relationKey(["userId"], () => User),
|
|
169
|
-
* }));
|
|
170
|
-
* ```
|
|
171
|
-
*
|
|
172
|
-
* @see {@link ForeignKeyBuilder} DB FK
|
|
173
|
-
*/
|
|
174
|
-
export class RelationKeyBuilder<
|
|
175
|
-
TOwner extends TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
176
|
-
TTargetFn extends () => TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
177
|
-
> {
|
|
178
|
-
/**
|
|
179
|
-
* @param meta -
|
|
180
|
-
* @param meta.ownerFn - 소유
|
|
181
|
-
* @param meta.columns -
|
|
182
|
-
* @param meta.targetFn - 참조
|
|
183
|
-
* @param meta.description -
|
|
184
|
-
*/
|
|
185
|
-
constructor(
|
|
186
|
-
readonly meta: {
|
|
187
|
-
ownerFn: () => TOwner;
|
|
188
|
-
columns: string[];
|
|
189
|
-
targetFn: TTargetFn;
|
|
190
|
-
description?: string;
|
|
191
|
-
},
|
|
192
|
-
) {}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
* @param desc -
|
|
198
|
-
* @returns 새 RelationKeyBuilder
|
|
199
|
-
*/
|
|
200
|
-
description(desc: string): RelationKeyBuilder<TOwner, TTargetFn> {
|
|
201
|
-
return new RelationKeyBuilder({ ...this.meta, description: desc });
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* 논리적
|
|
207
|
-
*
|
|
208
|
-
* ForeignKeyTargetBuilder와 동일하지만 DB에 FK 제약조건을 생성하지 않음
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
* @template TTargetTableFn - 참조하는
|
|
212
|
-
* @template TIsSingle - 단일
|
|
213
|
-
*
|
|
214
|
-
* @example
|
|
215
|
-
* ```typescript
|
|
216
|
-
* const Company = Table("Company")
|
|
217
|
-
* .columns((c) => ({ id: c.bigint() }))
|
|
218
|
-
* .relations((r) => ({
|
|
219
|
-
* // 역참조 (FK 미생성)
|
|
220
|
-
* employees: r.relationKeyTarget(() => UserSummary, "company"),
|
|
221
|
-
* }));
|
|
222
|
-
* ```
|
|
223
|
-
*
|
|
224
|
-
* @see {@link ForeignKeyTargetBuilder} DB FK
|
|
225
|
-
*/
|
|
226
|
-
export class RelationKeyTargetBuilder<
|
|
227
|
-
TTargetTableFn extends () => TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
228
|
-
TIsSingle extends boolean,
|
|
229
|
-
> {
|
|
230
|
-
/**
|
|
231
|
-
* @param meta -
|
|
232
|
-
* @param meta.targetTableFn - 참조하는
|
|
233
|
-
* @param meta.relationName - 참조하는
|
|
234
|
-
* @param meta.description -
|
|
235
|
-
* @param meta.isSingle - 단일
|
|
236
|
-
*/
|
|
237
|
-
constructor(
|
|
238
|
-
readonly meta: {
|
|
239
|
-
targetTableFn: TTargetTableFn;
|
|
240
|
-
relationName: string;
|
|
241
|
-
description?: string;
|
|
242
|
-
isSingle?: TIsSingle;
|
|
243
|
-
},
|
|
244
|
-
) {}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
* @param desc -
|
|
250
|
-
* @returns 새 RelationKeyTargetBuilder
|
|
251
|
-
*/
|
|
252
|
-
description(desc: string): RelationKeyTargetBuilder<TTargetTableFn, TIsSingle> {
|
|
253
|
-
return new RelationKeyTargetBuilder({ ...this.meta, description: desc });
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* 단일
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
* @returns 새 RelationKeyTargetBuilder
|
|
262
|
-
*/
|
|
263
|
-
single(): RelationKeyTargetBuilder<TTargetTableFn, true> {
|
|
264
|
-
return new RelationKeyTargetBuilder({ ...this.meta, isSingle: true });
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* FK
|
|
270
|
-
*
|
|
271
|
-
* @template TOwner - 소유
|
|
272
|
-
* @template TColumnKey -
|
|
273
|
-
*/
|
|
274
|
-
type RelationFkFactory<TOwner extends TableBuilder<any, any>, TColumnKey extends string> = {
|
|
275
|
-
/** N:1 FK
|
|
276
|
-
foreignKey<TTargetFn extends () => TableBuilder<any, any>>(
|
|
277
|
-
columns: TColumnKey[],
|
|
278
|
-
targetFn: TTargetFn,
|
|
279
|
-
): ForeignKeyBuilder<TOwner, TTargetFn>;
|
|
280
|
-
/** 1:N FK 역참조
|
|
281
|
-
foreignKeyTarget<TTargetTableFn extends () => TableBuilder<any, any>>(
|
|
282
|
-
targetTableFn: TTargetTableFn,
|
|
283
|
-
relationName: string,
|
|
284
|
-
): ForeignKeyTargetBuilder<TTargetTableFn, false>;
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* 논리적
|
|
289
|
-
*
|
|
290
|
-
* @template TOwner - 소유
|
|
291
|
-
* @template TColumnKey -
|
|
292
|
-
*/
|
|
293
|
-
type RelationRkFactory<
|
|
294
|
-
TOwner extends TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
295
|
-
TColumnKey extends string,
|
|
296
|
-
> = {
|
|
297
|
-
/** N:1 논리적
|
|
298
|
-
relationKey<TTargetFn extends () => TableBuilder<any, any> | ViewBuilder<any, any, any>>(
|
|
299
|
-
columns: TColumnKey[],
|
|
300
|
-
targetFn: TTargetFn,
|
|
301
|
-
): RelationKeyBuilder<TOwner, TTargetFn>;
|
|
302
|
-
/** 1:N 논리적 역참조
|
|
303
|
-
relationKeyTarget<
|
|
304
|
-
TTargetTableFn extends () => TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
305
|
-
>(
|
|
306
|
-
targetTableFn: TTargetTableFn,
|
|
307
|
-
relationName: string,
|
|
308
|
-
): RelationKeyTargetBuilder<TTargetTableFn, false>;
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
* TableBuilder.relations() 및 ViewBuilder.relations()
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
* @template TOwner - 소유
|
|
318
|
-
* @template TColumnKey -
|
|
319
|
-
* @param ownerFn - 소유
|
|
320
|
-
* @returns
|
|
321
|
-
*
|
|
322
|
-
* @example
|
|
323
|
-
* ```typescript
|
|
324
|
-
* //
|
|
325
|
-
* const Post = Table("Post")
|
|
326
|
-
* .columns((c) => ({
|
|
327
|
-
* id: c.bigint(),
|
|
328
|
-
* authorId: c.bigint(),
|
|
329
|
-
* }))
|
|
330
|
-
* .relations((r) => ({
|
|
331
|
-
* author: r.foreignKey(["authorId"], () => User), // FK
|
|
332
|
-
* }));
|
|
333
|
-
*
|
|
334
|
-
* //
|
|
335
|
-
* const UserSummary = View("UserSummary")
|
|
336
|
-
* .query(...)
|
|
337
|
-
* .relations((r) => ({
|
|
338
|
-
* posts: r.relationKeyTarget(() => Post, "author"), // FK 미생성
|
|
339
|
-
* }));
|
|
340
|
-
* ```
|
|
341
|
-
*/
|
|
342
|
-
export function createRelationFactory<
|
|
343
|
-
TOwner extends TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
344
|
-
TColumnKey extends string,
|
|
345
|
-
>(
|
|
346
|
-
ownerFn: () => TOwner,
|
|
347
|
-
): TOwner extends TableBuilder<any, any>
|
|
348
|
-
? RelationFkFactory<TOwner, TColumnKey> & RelationRkFactory<TOwner, TColumnKey>
|
|
349
|
-
: RelationRkFactory<TOwner, TColumnKey> {
|
|
350
|
-
return {
|
|
351
|
-
foreignKey(columns, targetFn) {
|
|
352
|
-
return new ForeignKeyBuilder({
|
|
353
|
-
ownerFn: ownerFn as () => TableBuilder<any, any>,
|
|
354
|
-
columns,
|
|
355
|
-
targetFn,
|
|
356
|
-
});
|
|
357
|
-
},
|
|
358
|
-
foreignKeyTarget(targetTableFn, relationName) {
|
|
359
|
-
return new ForeignKeyTargetBuilder({ targetTableFn, relationName });
|
|
360
|
-
},
|
|
361
|
-
relationKey(columns, targetFn) {
|
|
362
|
-
return new RelationKeyBuilder({
|
|
363
|
-
ownerFn: ownerFn,
|
|
364
|
-
columns,
|
|
365
|
-
targetFn,
|
|
366
|
-
});
|
|
367
|
-
},
|
|
368
|
-
relationKeyTarget(targetTableFn, relationName) {
|
|
369
|
-
return new RelationKeyTargetBuilder({ targetTableFn, relationName });
|
|
370
|
-
},
|
|
371
|
-
} as TOwner extends TableBuilder<any, any>
|
|
372
|
-
? RelationFkFactory<TOwner, TColumnKey> & RelationRkFactory<TOwner, TColumnKey>
|
|
373
|
-
: RelationRkFactory<TOwner, TColumnKey>;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// ============================================
|
|
377
|
-
//
|
|
378
|
-
// ============================================
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
* TableBuilder.relations() 및 ViewBuilder.relations()의
|
|
384
|
-
*/
|
|
385
|
-
export type RelationBuilderRecord = Record<
|
|
386
|
-
string,
|
|
387
|
-
| ForeignKeyBuilder<any, any>
|
|
388
|
-
| ForeignKeyTargetBuilder<any, any>
|
|
389
|
-
| RelationKeyBuilder<any, any>
|
|
390
|
-
| RelationKeyTargetBuilder<any, any>
|
|
391
|
-
>;
|
|
392
|
-
|
|
393
|
-
// ============================================
|
|
394
|
-
// Infer -
|
|
395
|
-
// ============================================
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* FK/RelationKey에서 참조 대상
|
|
399
|
-
*
|
|
400
|
-
* N:1 관계의 참조 대상
|
|
401
|
-
*
|
|
402
|
-
* @template T - FK 또는 RelationKey
|
|
403
|
-
*/
|
|
404
|
-
export type ExtractRelationTarget<TRelation> = TRelation extends
|
|
405
|
-
| ForeignKeyBuilder<any, infer TTargetFn>
|
|
406
|
-
| RelationKeyBuilder<any, infer TTargetFn>
|
|
407
|
-
? ReturnType<TTargetFn> extends TableBuilder<infer TCols, infer TRels>
|
|
408
|
-
? InferColumns<TCols> & InferDeepRelations<TRels>
|
|
409
|
-
: ReturnType<TTargetFn> extends ViewBuilder<any, infer TData, infer TRels>
|
|
410
|
-
? TData & InferDeepRelations<TRels>
|
|
411
|
-
: never
|
|
412
|
-
: never;
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* FKTarget/RelationKeyTarget에서 참조 대상
|
|
416
|
-
*
|
|
417
|
-
* 1:N 관계의 참조 대상
|
|
418
|
-
* TTargetTableFn: () => Post 형태로 lazy evaluation하여 순환참조 방지
|
|
419
|
-
*
|
|
420
|
-
* @template T - FKTarget 또는 RelationKeyTarget
|
|
421
|
-
*/
|
|
422
|
-
export type ExtractRelationTargetResult<TRelation> = TRelation extends
|
|
423
|
-
| ForeignKeyTargetBuilder<infer TTargetTableFn, infer TIsSingle>
|
|
424
|
-
| RelationKeyTargetBuilder<infer TTargetTableFn, infer TIsSingle>
|
|
425
|
-
? ReturnType<TTargetTableFn> extends TableBuilder<infer TCols, infer TRels>
|
|
426
|
-
? TIsSingle extends true
|
|
427
|
-
? InferColumns<TCols> & InferDeepRelations<TRels>
|
|
428
|
-
: (InferColumns<TCols> & InferDeepRelations<TRels>)[]
|
|
429
|
-
: ReturnType<TTargetTableFn> extends ViewBuilder<any, infer TData, infer TRels>
|
|
430
|
-
? TIsSingle extends true
|
|
431
|
-
? TData & InferDeepRelations<TRels>
|
|
432
|
-
: (TData & InferDeepRelations<TRels>)[]
|
|
433
|
-
: never
|
|
434
|
-
: never;
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
* 모든 관계를 optional로 만들어 include() 없이
|
|
440
|
-
*
|
|
441
|
-
* @template TRelations -
|
|
442
|
-
*
|
|
443
|
-
* @example
|
|
444
|
-
* ```typescript
|
|
445
|
-
* type UserRelations = InferDeepRelations<typeof User.$relations>;
|
|
446
|
-
* // { posts?: Post[]; profile?: Profile; }
|
|
447
|
-
* ```
|
|
448
|
-
*/
|
|
449
|
-
export type InferDeepRelations<TRelations extends RelationBuilderRecord> = {
|
|
450
|
-
[K in keyof TRelations]?:
|
|
451
|
-
| ExtractRelationTarget<TRelations[K]>
|
|
452
|
-
| ExtractRelationTargetResult<TRelations[K]>;
|
|
453
|
-
};
|
|
1
|
+
import { type InferColumns } from "./column-builder";
|
|
2
|
+
import type { TableBuilder } from "../table-builder";
|
|
3
|
+
import type { ViewBuilder } from "../view-builder";
|
|
4
|
+
|
|
5
|
+
// ============================================
|
|
6
|
+
// ForeignKeyBuilder
|
|
7
|
+
// ============================================
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Foreign Key relationship builder (N:1)
|
|
11
|
+
*
|
|
12
|
+
* 현재 Table에서 참조 Table로의 FK relationship definition
|
|
13
|
+
* DB에 실제 FK constraint Generate
|
|
14
|
+
*
|
|
15
|
+
* @template TOwner - 소유 Table builder type
|
|
16
|
+
* @template TTargetFn - 참조 Table builder factory type
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const Post = Table("Post")
|
|
21
|
+
* .columns((c) => ({
|
|
22
|
+
* id: c.bigint().autoIncrement(),
|
|
23
|
+
* authorId: c.bigint(), // FK column
|
|
24
|
+
* }))
|
|
25
|
+
* .primaryKey("id")
|
|
26
|
+
* .relations((r) => ({
|
|
27
|
+
* // N:1 relationship - Post → User
|
|
28
|
+
* author: r.foreignKey(["authorId"], () => User),
|
|
29
|
+
* }));
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @see {@link ForeignKeyTargetBuilder} 역참조 builder
|
|
33
|
+
* @see {@link RelationKeyBuilder} DB FK 없는 relationship
|
|
34
|
+
*/
|
|
35
|
+
export class ForeignKeyBuilder<
|
|
36
|
+
TOwner extends TableBuilder<any, any>,
|
|
37
|
+
TTargetFn extends () => TableBuilder<any, any>,
|
|
38
|
+
> {
|
|
39
|
+
/**
|
|
40
|
+
* @param meta - FK Metadata
|
|
41
|
+
* @param meta.ownerFn - 소유 Table factory
|
|
42
|
+
* @param meta.columns - FK column명 array
|
|
43
|
+
* @param meta.targetFn - 참조 Table factory
|
|
44
|
+
* @param meta.description - relationship description
|
|
45
|
+
*/
|
|
46
|
+
constructor(
|
|
47
|
+
readonly meta: {
|
|
48
|
+
ownerFn: () => TOwner;
|
|
49
|
+
columns: string[];
|
|
50
|
+
targetFn: TTargetFn;
|
|
51
|
+
description?: string;
|
|
52
|
+
},
|
|
53
|
+
) {}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* relationship set description
|
|
57
|
+
*
|
|
58
|
+
* @param desc - relationship description
|
|
59
|
+
* @returns 새 ForeignKeyBuilder instance
|
|
60
|
+
*/
|
|
61
|
+
description(desc: string): ForeignKeyBuilder<TOwner, TTargetFn> {
|
|
62
|
+
return new ForeignKeyBuilder({ ...this.meta, description: desc });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Foreign Key 역참조 builder (1:N)
|
|
68
|
+
*
|
|
69
|
+
* 다른 Table에서 현재 Table을 참조하는 FK의 역참조 definition
|
|
70
|
+
* include() 시 배열로 로드 (single() 호출 시 단일 object)
|
|
71
|
+
*
|
|
72
|
+
* @template TTargetTableFn - 참조하는 Table builder factory type
|
|
73
|
+
* @template TIsSingle - 단일 object 여부
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const User = Table("User")
|
|
78
|
+
* .columns((c) => ({
|
|
79
|
+
* id: c.bigint().autoIncrement(),
|
|
80
|
+
* name: c.varchar(100),
|
|
81
|
+
* }))
|
|
82
|
+
* .primaryKey("id")
|
|
83
|
+
* .relations((r) => ({
|
|
84
|
+
* // 1:N relationship - User ← Post.author
|
|
85
|
+
* posts: r.foreignKeyTarget(() => Post, "author"),
|
|
86
|
+
*
|
|
87
|
+
* // 1:1 relationship (단일 object)
|
|
88
|
+
* profile: r.foreignKeyTarget(() => Profile, "user").single(),
|
|
89
|
+
* }));
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @see {@link ForeignKeyBuilder} FK builder
|
|
93
|
+
*/
|
|
94
|
+
export class ForeignKeyTargetBuilder<
|
|
95
|
+
TTargetTableFn extends () => TableBuilder<any, any>,
|
|
96
|
+
TIsSingle extends boolean,
|
|
97
|
+
> {
|
|
98
|
+
/**
|
|
99
|
+
* @param meta - FK 역참조 Metadata
|
|
100
|
+
* @param meta.targetTableFn - 참조하는 Table factory
|
|
101
|
+
* @param meta.relationName - 참조하는 Table의 FK 관계명
|
|
102
|
+
* @param meta.description - relationship description
|
|
103
|
+
* @param meta.isSingle - 단일 object 여부
|
|
104
|
+
*/
|
|
105
|
+
constructor(
|
|
106
|
+
readonly meta: {
|
|
107
|
+
targetTableFn: TTargetTableFn;
|
|
108
|
+
relationName: string;
|
|
109
|
+
description?: string;
|
|
110
|
+
isSingle?: TIsSingle;
|
|
111
|
+
},
|
|
112
|
+
) {}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* relationship set description
|
|
116
|
+
*
|
|
117
|
+
* @param desc - relationship description
|
|
118
|
+
* @returns 새 ForeignKeyTargetBuilder instance
|
|
119
|
+
*/
|
|
120
|
+
description(desc: string): ForeignKeyTargetBuilder<TTargetTableFn, TIsSingle> {
|
|
121
|
+
return new ForeignKeyTargetBuilder({ ...this.meta, description: desc });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 단일 object 관계로 설정 (1:1)
|
|
126
|
+
*
|
|
127
|
+
* Basic은 array (1:N), single() 호출 시 단일 object
|
|
128
|
+
*
|
|
129
|
+
* @returns 새 ForeignKeyTargetBuilder instance (isSingle=true)
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* profile: r.foreignKeyTarget(() => Profile, "user").single()
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
single(): ForeignKeyTargetBuilder<TTargetTableFn, true> {
|
|
137
|
+
return new ForeignKeyTargetBuilder({ ...this.meta, isSingle: true });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ============================================
|
|
142
|
+
// RelationKeyBuilder (FK와 동일하지만 DB에 FK 등록 안 함)
|
|
143
|
+
// ============================================
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 논리적 relationship builder (N:1) - DB FK 미생성
|
|
147
|
+
*
|
|
148
|
+
* ForeignKeyBuilder와 동일하지만 DB에 FK 제약조건을 생성하지 않음
|
|
149
|
+
* View(View)에서도 사용 가능
|
|
150
|
+
*
|
|
151
|
+
* @template TOwner - 소유 Table/View builder type
|
|
152
|
+
* @template TTargetFn - 참조 Table/View builder factory type
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```typescript
|
|
156
|
+
* // View에서 Table로 relationship definition
|
|
157
|
+
* const UserSummary = View("UserSummary")
|
|
158
|
+
* .query((db: MyDb) => db.user().select(...))
|
|
159
|
+
* .relations((r) => ({
|
|
160
|
+
* // View → Table (FK 미생성)
|
|
161
|
+
* company: r.relationKey(["companyId"], () => Company),
|
|
162
|
+
* }));
|
|
163
|
+
*
|
|
164
|
+
* // Table에서 FK 없이 relationship definition
|
|
165
|
+
* const Report = Table("Report")
|
|
166
|
+
* .columns((c) => ({ userId: c.bigint() }))
|
|
167
|
+
* .relations((r) => ({
|
|
168
|
+
* user: r.relationKey(["userId"], () => User),
|
|
169
|
+
* }));
|
|
170
|
+
* ```
|
|
171
|
+
*
|
|
172
|
+
* @see {@link ForeignKeyBuilder} DB FK Generate 버전
|
|
173
|
+
*/
|
|
174
|
+
export class RelationKeyBuilder<
|
|
175
|
+
TOwner extends TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
176
|
+
TTargetFn extends () => TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
177
|
+
> {
|
|
178
|
+
/**
|
|
179
|
+
* @param meta - relationship Metadata
|
|
180
|
+
* @param meta.ownerFn - 소유 Table/View factory
|
|
181
|
+
* @param meta.columns - relationship column명 array
|
|
182
|
+
* @param meta.targetFn - 참조 Table/View factory
|
|
183
|
+
* @param meta.description - relationship description
|
|
184
|
+
*/
|
|
185
|
+
constructor(
|
|
186
|
+
readonly meta: {
|
|
187
|
+
ownerFn: () => TOwner;
|
|
188
|
+
columns: string[];
|
|
189
|
+
targetFn: TTargetFn;
|
|
190
|
+
description?: string;
|
|
191
|
+
},
|
|
192
|
+
) {}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* relationship set description
|
|
196
|
+
*
|
|
197
|
+
* @param desc - relationship description
|
|
198
|
+
* @returns 새 RelationKeyBuilder instance
|
|
199
|
+
*/
|
|
200
|
+
description(desc: string): RelationKeyBuilder<TOwner, TTargetFn> {
|
|
201
|
+
return new RelationKeyBuilder({ ...this.meta, description: desc });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 논리적 relationship 역참조 builder (1:N) - DB FK 미생성
|
|
207
|
+
*
|
|
208
|
+
* ForeignKeyTargetBuilder와 동일하지만 DB에 FK 제약조건을 생성하지 않음
|
|
209
|
+
* View(View)에서도 사용 가능
|
|
210
|
+
*
|
|
211
|
+
* @template TTargetTableFn - 참조하는 Table/View builder factory type
|
|
212
|
+
* @template TIsSingle - 단일 object 여부
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```typescript
|
|
216
|
+
* const Company = Table("Company")
|
|
217
|
+
* .columns((c) => ({ id: c.bigint() }))
|
|
218
|
+
* .relations((r) => ({
|
|
219
|
+
* // 역참조 (FK 미생성)
|
|
220
|
+
* employees: r.relationKeyTarget(() => UserSummary, "company"),
|
|
221
|
+
* }));
|
|
222
|
+
* ```
|
|
223
|
+
*
|
|
224
|
+
* @see {@link ForeignKeyTargetBuilder} DB FK Generate 버전
|
|
225
|
+
*/
|
|
226
|
+
export class RelationKeyTargetBuilder<
|
|
227
|
+
TTargetTableFn extends () => TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
228
|
+
TIsSingle extends boolean,
|
|
229
|
+
> {
|
|
230
|
+
/**
|
|
231
|
+
* @param meta - relationship 역참조 Metadata
|
|
232
|
+
* @param meta.targetTableFn - 참조하는 Table/View factory
|
|
233
|
+
* @param meta.relationName - 참조하는 Table/View의 관계명
|
|
234
|
+
* @param meta.description - relationship description
|
|
235
|
+
* @param meta.isSingle - 단일 object 여부
|
|
236
|
+
*/
|
|
237
|
+
constructor(
|
|
238
|
+
readonly meta: {
|
|
239
|
+
targetTableFn: TTargetTableFn;
|
|
240
|
+
relationName: string;
|
|
241
|
+
description?: string;
|
|
242
|
+
isSingle?: TIsSingle;
|
|
243
|
+
},
|
|
244
|
+
) {}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* relationship set description
|
|
248
|
+
*
|
|
249
|
+
* @param desc - relationship description
|
|
250
|
+
* @returns 새 RelationKeyTargetBuilder instance
|
|
251
|
+
*/
|
|
252
|
+
description(desc: string): RelationKeyTargetBuilder<TTargetTableFn, TIsSingle> {
|
|
253
|
+
return new RelationKeyTargetBuilder({ ...this.meta, description: desc });
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 단일 object 관계로 설정 (1:1)
|
|
258
|
+
*
|
|
259
|
+
* Basic은 array (1:N), single() 호출 시 단일 object
|
|
260
|
+
*
|
|
261
|
+
* @returns 새 RelationKeyTargetBuilder instance (isSingle=true)
|
|
262
|
+
*/
|
|
263
|
+
single(): RelationKeyTargetBuilder<TTargetTableFn, true> {
|
|
264
|
+
return new RelationKeyTargetBuilder({ ...this.meta, isSingle: true });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* FK relationship factory type (table 전용)
|
|
270
|
+
*
|
|
271
|
+
* @template TOwner - 소유 Table builder type
|
|
272
|
+
* @template TColumnKey - Column key type
|
|
273
|
+
*/
|
|
274
|
+
type RelationFkFactory<TOwner extends TableBuilder<any, any>, TColumnKey extends string> = {
|
|
275
|
+
/** N:1 FK relationship definition (DB FK Create) */
|
|
276
|
+
foreignKey<TTargetFn extends () => TableBuilder<any, any>>(
|
|
277
|
+
columns: TColumnKey[],
|
|
278
|
+
targetFn: TTargetFn,
|
|
279
|
+
): ForeignKeyBuilder<TOwner, TTargetFn>;
|
|
280
|
+
/** 1:N FK 역참조 definition */
|
|
281
|
+
foreignKeyTarget<TTargetTableFn extends () => TableBuilder<any, any>>(
|
|
282
|
+
targetTableFn: TTargetTableFn,
|
|
283
|
+
relationName: string,
|
|
284
|
+
): ForeignKeyTargetBuilder<TTargetTableFn, false>;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* 논리적 relationship factory type (table/View 공용)
|
|
289
|
+
*
|
|
290
|
+
* @template TOwner - 소유 Table/View builder type
|
|
291
|
+
* @template TColumnKey - Column key type
|
|
292
|
+
*/
|
|
293
|
+
type RelationRkFactory<
|
|
294
|
+
TOwner extends TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
295
|
+
TColumnKey extends string,
|
|
296
|
+
> = {
|
|
297
|
+
/** N:1 논리적 relationship definition (DB FK 미생성) */
|
|
298
|
+
relationKey<TTargetFn extends () => TableBuilder<any, any> | ViewBuilder<any, any, any>>(
|
|
299
|
+
columns: TColumnKey[],
|
|
300
|
+
targetFn: TTargetFn,
|
|
301
|
+
): RelationKeyBuilder<TOwner, TTargetFn>;
|
|
302
|
+
/** 1:N 논리적 역참조 definition */
|
|
303
|
+
relationKeyTarget<
|
|
304
|
+
TTargetTableFn extends () => TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
305
|
+
>(
|
|
306
|
+
targetTableFn: TTargetTableFn,
|
|
307
|
+
relationName: string,
|
|
308
|
+
): RelationKeyTargetBuilder<TTargetTableFn, false>;
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* relationship builder factory Generate
|
|
313
|
+
*
|
|
314
|
+
* TableBuilder.relations() 및 ViewBuilder.relations()used in
|
|
315
|
+
* Table은 FK + RelationKey 모두 사용 가능, View는 RelationKey만 사용 가능
|
|
316
|
+
*
|
|
317
|
+
* @template TOwner - 소유 Table/View builder type
|
|
318
|
+
* @template TColumnKey - Column key type
|
|
319
|
+
* @param ownerFn - 소유 Table/View factory function
|
|
320
|
+
* @returns relationship builder factory
|
|
321
|
+
*
|
|
322
|
+
* @example
|
|
323
|
+
* ```typescript
|
|
324
|
+
* // Table - FK, RelationKey 모두 사용 가능
|
|
325
|
+
* const Post = Table("Post")
|
|
326
|
+
* .columns((c) => ({
|
|
327
|
+
* id: c.bigint(),
|
|
328
|
+
* authorId: c.bigint(),
|
|
329
|
+
* }))
|
|
330
|
+
* .relations((r) => ({
|
|
331
|
+
* author: r.foreignKey(["authorId"], () => User), // FK Generate
|
|
332
|
+
* }));
|
|
333
|
+
*
|
|
334
|
+
* // View - RelationKey만 사용 가능
|
|
335
|
+
* const UserSummary = View("UserSummary")
|
|
336
|
+
* .query(...)
|
|
337
|
+
* .relations((r) => ({
|
|
338
|
+
* posts: r.relationKeyTarget(() => Post, "author"), // FK 미생성
|
|
339
|
+
* }));
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
342
|
+
export function createRelationFactory<
|
|
343
|
+
TOwner extends TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
344
|
+
TColumnKey extends string,
|
|
345
|
+
>(
|
|
346
|
+
ownerFn: () => TOwner,
|
|
347
|
+
): TOwner extends TableBuilder<any, any>
|
|
348
|
+
? RelationFkFactory<TOwner, TColumnKey> & RelationRkFactory<TOwner, TColumnKey>
|
|
349
|
+
: RelationRkFactory<TOwner, TColumnKey> {
|
|
350
|
+
return {
|
|
351
|
+
foreignKey(columns, targetFn) {
|
|
352
|
+
return new ForeignKeyBuilder({
|
|
353
|
+
ownerFn: ownerFn as () => TableBuilder<any, any>,
|
|
354
|
+
columns,
|
|
355
|
+
targetFn,
|
|
356
|
+
});
|
|
357
|
+
},
|
|
358
|
+
foreignKeyTarget(targetTableFn, relationName) {
|
|
359
|
+
return new ForeignKeyTargetBuilder({ targetTableFn, relationName });
|
|
360
|
+
},
|
|
361
|
+
relationKey(columns, targetFn) {
|
|
362
|
+
return new RelationKeyBuilder({
|
|
363
|
+
ownerFn: ownerFn,
|
|
364
|
+
columns,
|
|
365
|
+
targetFn,
|
|
366
|
+
});
|
|
367
|
+
},
|
|
368
|
+
relationKeyTarget(targetTableFn, relationName) {
|
|
369
|
+
return new RelationKeyTargetBuilder({ targetTableFn, relationName });
|
|
370
|
+
},
|
|
371
|
+
} as TOwner extends TableBuilder<any, any>
|
|
372
|
+
? RelationFkFactory<TOwner, TColumnKey> & RelationRkFactory<TOwner, TColumnKey>
|
|
373
|
+
: RelationRkFactory<TOwner, TColumnKey>;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ============================================
|
|
377
|
+
// builder 레코드
|
|
378
|
+
// ============================================
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* relationship builder record type
|
|
382
|
+
*
|
|
383
|
+
* TableBuilder.relations() 및 ViewBuilder.relations()의 return type
|
|
384
|
+
*/
|
|
385
|
+
export type RelationBuilderRecord = Record<
|
|
386
|
+
string,
|
|
387
|
+
| ForeignKeyBuilder<any, any>
|
|
388
|
+
| ForeignKeyTargetBuilder<any, any>
|
|
389
|
+
| RelationKeyBuilder<any, any>
|
|
390
|
+
| RelationKeyTargetBuilder<any, any>
|
|
391
|
+
>;
|
|
392
|
+
|
|
393
|
+
// ============================================
|
|
394
|
+
// Infer - relationship Type inference
|
|
395
|
+
// ============================================
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* FK/RelationKey에서 참조 대상 type 추출 (단일 object)
|
|
399
|
+
*
|
|
400
|
+
* N:1 관계의 참조 대상 type
|
|
401
|
+
*
|
|
402
|
+
* @template T - FK 또는 RelationKey builder type
|
|
403
|
+
*/
|
|
404
|
+
export type ExtractRelationTarget<TRelation> = TRelation extends
|
|
405
|
+
| ForeignKeyBuilder<any, infer TTargetFn>
|
|
406
|
+
| RelationKeyBuilder<any, infer TTargetFn>
|
|
407
|
+
? ReturnType<TTargetFn> extends TableBuilder<infer TCols, infer TRels>
|
|
408
|
+
? InferColumns<TCols> & InferDeepRelations<TRels>
|
|
409
|
+
: ReturnType<TTargetFn> extends ViewBuilder<any, infer TData, infer TRels>
|
|
410
|
+
? TData & InferDeepRelations<TRels>
|
|
411
|
+
: never
|
|
412
|
+
: never;
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* FKTarget/RelationKeyTarget에서 참조 대상 type 추출 (array 또는 단일 object)
|
|
416
|
+
*
|
|
417
|
+
* 1:N 관계의 참조 대상 type (single() 호출 시 단일 object)
|
|
418
|
+
* TTargetTableFn: () => Post 형태로 lazy evaluation하여 순환참조 방지
|
|
419
|
+
*
|
|
420
|
+
* @template T - FKTarget 또는 RelationKeyTarget builder type
|
|
421
|
+
*/
|
|
422
|
+
export type ExtractRelationTargetResult<TRelation> = TRelation extends
|
|
423
|
+
| ForeignKeyTargetBuilder<infer TTargetTableFn, infer TIsSingle>
|
|
424
|
+
| RelationKeyTargetBuilder<infer TTargetTableFn, infer TIsSingle>
|
|
425
|
+
? ReturnType<TTargetTableFn> extends TableBuilder<infer TCols, infer TRels>
|
|
426
|
+
? TIsSingle extends true
|
|
427
|
+
? InferColumns<TCols> & InferDeepRelations<TRels>
|
|
428
|
+
: (InferColumns<TCols> & InferDeepRelations<TRels>)[]
|
|
429
|
+
: ReturnType<TTargetTableFn> extends ViewBuilder<any, infer TData, infer TRels>
|
|
430
|
+
? TIsSingle extends true
|
|
431
|
+
? TData & InferDeepRelations<TRels>
|
|
432
|
+
: (TData & InferDeepRelations<TRels>)[]
|
|
433
|
+
: never
|
|
434
|
+
: never;
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* relationship 정의에서 깊은 relationship Type inference
|
|
438
|
+
*
|
|
439
|
+
* 모든 관계를 optional로 만들어 include() 없이 access 시 undefined processing
|
|
440
|
+
*
|
|
441
|
+
* @template TRelations - relationship builder record type
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* ```typescript
|
|
445
|
+
* type UserRelations = InferDeepRelations<typeof User.$relations>;
|
|
446
|
+
* // { posts?: Post[]; profile?: Profile; }
|
|
447
|
+
* ```
|
|
448
|
+
*/
|
|
449
|
+
export type InferDeepRelations<TRelations extends RelationBuilderRecord> = {
|
|
450
|
+
[K in keyof TRelations]?:
|
|
451
|
+
| ExtractRelationTarget<TRelations[K]>
|
|
452
|
+
| ExtractRelationTargetResult<TRelations[K]>;
|
|
453
|
+
};
|