@simplysm/orm-common 13.0.28 → 13.0.29
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/dist/create-db-context.d.ts.map +1 -1
- package/dist/create-db-context.js +7 -2
- package/dist/create-db-context.js.map +1 -1
- package/dist/ddl/column-ddl.d.ts.map +1 -1
- package/dist/ddl/column-ddl.js.map +1 -1
- package/dist/ddl/initialize.d.ts.map +1 -1
- package/dist/ddl/initialize.js.map +1 -1
- package/dist/ddl/relation-ddl.d.ts.map +1 -1
- package/dist/ddl/relation-ddl.js.map +1 -1
- package/dist/ddl/schema-ddl.d.ts.map +1 -1
- package/dist/ddl/schema-ddl.js.map +1 -1
- package/dist/ddl/table-ddl.d.ts.map +1 -1
- package/dist/ddl/table-ddl.js.map +1 -1
- package/dist/define-db-context.d.ts.map +1 -1
- package/dist/define-db-context.js.map +1 -1
- package/dist/exec/executable.d.ts.map +1 -1
- package/dist/exec/executable.js.map +1 -1
- package/dist/exec/queryable.d.ts.map +1 -1
- package/dist/exec/queryable.js +19 -6
- package/dist/exec/queryable.js.map +1 -1
- package/dist/exec/search-parser.js.map +1 -1
- package/dist/expr/expr.d.ts.map +1 -1
- package/dist/expr/expr.js.map +1 -1
- package/dist/query-builder/base/query-builder-base.d.ts.map +1 -1
- package/dist/query-builder/base/query-builder-base.js +3 -1
- package/dist/query-builder/base/query-builder-base.js.map +1 -1
- package/dist/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/mssql/mssql-expr-renderer.js +3 -1
- package/dist/query-builder/mssql/mssql-expr-renderer.js.map +1 -1
- package/dist/query-builder/mssql/mssql-query-builder.d.ts.map +1 -1
- package/dist/query-builder/mssql/mssql-query-builder.js +9 -3
- package/dist/query-builder/mssql/mssql-query-builder.js.map +1 -1
- package/dist/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/mysql/mysql-expr-renderer.js +3 -1
- package/dist/query-builder/mysql/mysql-expr-renderer.js.map +1 -1
- package/dist/query-builder/mysql/mysql-query-builder.d.ts.map +1 -1
- package/dist/query-builder/mysql/mysql-query-builder.js +12 -4
- package/dist/query-builder/mysql/mysql-query-builder.js.map +1 -1
- package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/postgresql/postgresql-expr-renderer.js +3 -1
- package/dist/query-builder/postgresql/postgresql-expr-renderer.js.map +1 -1
- package/dist/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -1
- package/dist/query-builder/postgresql/postgresql-query-builder.js +21 -7
- package/dist/query-builder/postgresql/postgresql-query-builder.js.map +1 -1
- package/dist/schema/factory/column-builder.d.ts.map +1 -1
- package/dist/schema/factory/column-builder.js.map +1 -1
- package/dist/schema/factory/relation-builder.d.ts.map +1 -1
- package/dist/schema/factory/relation-builder.js.map +1 -1
- package/dist/schema/procedure-builder.d.ts.map +1 -1
- package/dist/schema/procedure-builder.js.map +1 -1
- package/dist/schema/table-builder.d.ts.map +1 -1
- package/dist/schema/table-builder.js +3 -1
- package/dist/schema/table-builder.js.map +1 -1
- package/dist/schema/view-builder.d.ts.map +1 -1
- package/dist/schema/view-builder.js +3 -1
- package/dist/schema/view-builder.js.map +1 -1
- package/dist/types/db-context-def.d.ts.map +1 -1
- package/dist/types/db.d.ts.map +1 -1
- package/dist/utils/result-parser.js +3 -1
- package/dist/utils/result-parser.js.map +1 -1
- package/package.json +2 -2
- package/src/create-db-context.ts +53 -15
- package/src/ddl/column-ddl.ts +4 -1
- package/src/ddl/initialize.ts +13 -3
- package/src/ddl/relation-ddl.ts +4 -1
- package/src/ddl/schema-ddl.ts +8 -2
- package/src/ddl/table-ddl.ts +12 -3
- package/src/define-db-context.ts +3 -1
- package/src/exec/executable.ts +4 -1
- package/src/exec/queryable.ts +91 -26
- package/src/exec/search-parser.ts +5 -1
- package/src/expr/expr.ts +56 -15
- package/src/query-builder/base/query-builder-base.ts +3 -1
- package/src/query-builder/mssql/mssql-expr-renderer.ts +6 -2
- package/src/query-builder/mssql/mssql-query-builder.ts +15 -5
- package/src/query-builder/mysql/mysql-expr-renderer.ts +6 -2
- package/src/query-builder/mysql/mysql-query-builder.ts +19 -6
- package/src/query-builder/postgresql/postgresql-expr-renderer.ts +6 -2
- package/src/query-builder/postgresql/postgresql-query-builder.ts +35 -13
- package/src/schema/factory/column-builder.ts +16 -5
- package/src/schema/factory/relation-builder.ts +14 -4
- package/src/schema/procedure-builder.ts +4 -1
- package/src/schema/table-builder.ts +14 -3
- package/src/schema/view-builder.ts +5 -1
- package/src/types/db-context-def.ts +37 -8
- package/src/types/db.ts +9 -2
- package/src/utils/result-parser.ts +10 -3
package/src/create-db-context.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
DbContextDef,
|
|
3
|
+
DbContextBase,
|
|
4
|
+
DbContextStatus,
|
|
5
|
+
DbContextInstance,
|
|
6
|
+
} from "./types/db-context-def";
|
|
2
7
|
import type { DataRecord, DbContextExecutor, IsolationLevel, ResultMeta } from "./types/db";
|
|
3
8
|
import type { QueryDef, QueryDefObjectName } from "./types/query-def";
|
|
4
9
|
import { DDL_TYPES } from "./types/query-def";
|
|
@@ -15,7 +20,10 @@ import * as tableDdl from "./ddl/table-ddl";
|
|
|
15
20
|
import * as columnDdl from "./ddl/column-ddl";
|
|
16
21
|
import * as relationDdl from "./ddl/relation-ddl";
|
|
17
22
|
import * as schemaDdl from "./ddl/schema-ddl";
|
|
18
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
initialize as initializeImpl,
|
|
25
|
+
validateRelations as validateRelationsImpl,
|
|
26
|
+
} from "./ddl/initialize";
|
|
19
27
|
|
|
20
28
|
import type { ColumnBuilder } from "./schema/factory/column-builder";
|
|
21
29
|
import type { ForeignKeyBuilder } from "./schema/factory/relation-builder";
|
|
@@ -77,13 +85,21 @@ export function createDbContext<TDef extends DbContextDef<any, any, any>>(
|
|
|
77
85
|
resetAliasCounter() {
|
|
78
86
|
aliasCounter = 0;
|
|
79
87
|
},
|
|
80
|
-
executeDefs<T = DataRecord>(
|
|
81
|
-
|
|
88
|
+
executeDefs<T = DataRecord>(
|
|
89
|
+
defs: QueryDef[],
|
|
90
|
+
resultMetas?: (ResultMeta | undefined)[],
|
|
91
|
+
): Promise<T[][]> {
|
|
92
|
+
if (
|
|
93
|
+
status === "transact" &&
|
|
94
|
+
defs.some((d) => (DDL_TYPES as readonly string[]).includes(d.type))
|
|
95
|
+
) {
|
|
82
96
|
throw new Error("TRANSACTION 상태에서는 DDL을 실행할 수 없습니다.");
|
|
83
97
|
}
|
|
84
98
|
return executor.executeDefs(defs, resultMetas);
|
|
85
99
|
},
|
|
86
|
-
getQueryDefObjectName(
|
|
100
|
+
getQueryDefObjectName(
|
|
101
|
+
tableOrView: TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
102
|
+
): QueryDefObjectName {
|
|
87
103
|
return objClearUndefined({
|
|
88
104
|
database: tableOrView.meta.database ?? opt.database,
|
|
89
105
|
schema: tableOrView.meta.schema ?? opt.schema,
|
|
@@ -123,7 +139,10 @@ export function createDbContext<TDef extends DbContextDef<any, any, any>>(
|
|
|
123
139
|
* 연결 -> 트랜잭션 시작 -> 콜백 실행 -> 커밋 -> 연결 종료
|
|
124
140
|
* 에러 발생 시 자동 롤백 후 연결 종료
|
|
125
141
|
*/
|
|
126
|
-
async connect<TResult>(
|
|
142
|
+
async connect<TResult>(
|
|
143
|
+
fn: () => Promise<TResult>,
|
|
144
|
+
isolationLevel?: IsolationLevel,
|
|
145
|
+
): Promise<TResult> {
|
|
127
146
|
validateRelationsImpl(def);
|
|
128
147
|
base.resetAliasCounter();
|
|
129
148
|
|
|
@@ -198,7 +217,10 @@ export function createDbContext<TDef extends DbContextDef<any, any, any>>(
|
|
|
198
217
|
*
|
|
199
218
|
* connectWithoutTransaction 내에서 부분적으로 트랜잭션이 필요할 때 사용
|
|
200
219
|
*/
|
|
201
|
-
async trans<TResult>(
|
|
220
|
+
async trans<TResult>(
|
|
221
|
+
fn: () => Promise<TResult>,
|
|
222
|
+
isolationLevel?: IsolationLevel,
|
|
223
|
+
): Promise<TResult> {
|
|
202
224
|
if (status === "transact") {
|
|
203
225
|
throw new Error("이미 TRANSACTION 상태입니다.");
|
|
204
226
|
}
|
|
@@ -258,13 +280,21 @@ export function createDbContext<TDef extends DbContextDef<any, any, any>>(
|
|
|
258
280
|
async dropProc(procedure: QueryDefObjectName): Promise<void> {
|
|
259
281
|
await base.executeDefs([tableDdl.getDropProcQueryDef(procedure)]);
|
|
260
282
|
},
|
|
261
|
-
async addColumn(
|
|
283
|
+
async addColumn(
|
|
284
|
+
table: QueryDefObjectName,
|
|
285
|
+
columnName: string,
|
|
286
|
+
column: ColumnBuilder<any, any>,
|
|
287
|
+
): Promise<void> {
|
|
262
288
|
await base.executeDefs([columnDdl.getAddColumnQueryDef(table, columnName, column)]);
|
|
263
289
|
},
|
|
264
290
|
async dropColumn(table: QueryDefObjectName, column: string): Promise<void> {
|
|
265
291
|
await base.executeDefs([columnDdl.getDropColumnQueryDef(table, column)]);
|
|
266
292
|
},
|
|
267
|
-
async modifyColumn(
|
|
293
|
+
async modifyColumn(
|
|
294
|
+
table: QueryDefObjectName,
|
|
295
|
+
columnName: string,
|
|
296
|
+
column: ColumnBuilder<any, any>,
|
|
297
|
+
): Promise<void> {
|
|
268
298
|
await base.executeDefs([columnDdl.getModifyColumnQueryDef(table, columnName, column)]);
|
|
269
299
|
},
|
|
270
300
|
async renameColumn(table: QueryDefObjectName, column: string, newName: string): Promise<void> {
|
|
@@ -281,7 +311,9 @@ export function createDbContext<TDef extends DbContextDef<any, any, any>>(
|
|
|
281
311
|
relationName: string,
|
|
282
312
|
relationDef: ForeignKeyBuilder<any, any>,
|
|
283
313
|
): Promise<void> {
|
|
284
|
-
await base.executeDefs([
|
|
314
|
+
await base.executeDefs([
|
|
315
|
+
relationDdl.getAddFkQueryDef(base, table, relationName, relationDef),
|
|
316
|
+
]);
|
|
285
317
|
},
|
|
286
318
|
async addIdx(table: QueryDefObjectName, indexBuilder: IndexBuilder<string[]>): Promise<void> {
|
|
287
319
|
await base.executeDefs([relationDdl.getAddIdxQueryDef(table, indexBuilder)]);
|
|
@@ -310,9 +342,12 @@ export function createDbContext<TDef extends DbContextDef<any, any, any>>(
|
|
|
310
342
|
|
|
311
343
|
//#region ========== DDL QueryDef generators ==========
|
|
312
344
|
|
|
313
|
-
getCreateTableQueryDef: (table: TableBuilder<any, any>) =>
|
|
314
|
-
|
|
315
|
-
|
|
345
|
+
getCreateTableQueryDef: (table: TableBuilder<any, any>) =>
|
|
346
|
+
tableDdl.getCreateTableQueryDef(base, table),
|
|
347
|
+
getCreateViewQueryDef: (view: ViewBuilder<any, any, any>) =>
|
|
348
|
+
tableDdl.getCreateViewQueryDef(db as any, view),
|
|
349
|
+
getCreateProcQueryDef: (procedure: ProcedureBuilder<any, any>) =>
|
|
350
|
+
tableDdl.getCreateProcQueryDef(base, procedure),
|
|
316
351
|
getCreateObjectQueryDef: (
|
|
317
352
|
builder: TableBuilder<any, any> | ViewBuilder<any, any, any> | ProcedureBuilder<any, any>,
|
|
318
353
|
) => tableDdl.getCreateObjectQueryDef(db as any, builder),
|
|
@@ -326,8 +361,11 @@ export function createDbContext<TDef extends DbContextDef<any, any, any>>(
|
|
|
326
361
|
getRenameColumnQueryDef: columnDdl.getRenameColumnQueryDef,
|
|
327
362
|
getAddPkQueryDef: relationDdl.getAddPkQueryDef,
|
|
328
363
|
getDropPkQueryDef: relationDdl.getDropPkQueryDef,
|
|
329
|
-
getAddFkQueryDef: (
|
|
330
|
-
|
|
364
|
+
getAddFkQueryDef: (
|
|
365
|
+
table: QueryDefObjectName,
|
|
366
|
+
relationName: string,
|
|
367
|
+
relationDef: ForeignKeyBuilder<any, any>,
|
|
368
|
+
) => relationDdl.getAddFkQueryDef(base, table, relationName, relationDef),
|
|
331
369
|
getAddIdxQueryDef: relationDdl.getAddIdxQueryDef,
|
|
332
370
|
getDropFkQueryDef: relationDdl.getDropFkQueryDef,
|
|
333
371
|
getDropIdxQueryDef: relationDdl.getDropIdxQueryDef,
|
package/src/ddl/column-ddl.ts
CHANGED
|
@@ -31,7 +31,10 @@ export function getAddColumnQueryDef(
|
|
|
31
31
|
/**
|
|
32
32
|
* DROP COLUMN QueryDef 생성
|
|
33
33
|
*/
|
|
34
|
-
export function getDropColumnQueryDef(
|
|
34
|
+
export function getDropColumnQueryDef(
|
|
35
|
+
table: QueryDefObjectName,
|
|
36
|
+
column: string,
|
|
37
|
+
): DropColumnQueryDef {
|
|
35
38
|
return { type: "dropColumn", table, column };
|
|
36
39
|
}
|
|
37
40
|
|
package/src/ddl/initialize.ts
CHANGED
|
@@ -104,7 +104,10 @@ export async function initialize(
|
|
|
104
104
|
/**
|
|
105
105
|
* 전체 객체 생성 (테이블/뷰/프로시저/FK/Index)
|
|
106
106
|
*/
|
|
107
|
-
async function createAllObjects(
|
|
107
|
+
async function createAllObjects(
|
|
108
|
+
db: DbContextBase,
|
|
109
|
+
def: DbContextDef<any, any, any>,
|
|
110
|
+
): Promise<void> {
|
|
108
111
|
// 1. 테이블/뷰/프로시저 생성
|
|
109
112
|
const builders = getBuilders(def);
|
|
110
113
|
const createDefs: QueryDef[] = [];
|
|
@@ -155,7 +158,11 @@ async function createAllObjects(db: DbContextBase, def: DbContextDef<any, any, a
|
|
|
155
158
|
function getBuilders(
|
|
156
159
|
def: DbContextDef<any, any, any>,
|
|
157
160
|
): (TableBuilder<any, any> | ViewBuilder<any, any, any> | ProcedureBuilder<any, any>)[] {
|
|
158
|
-
const builders: (
|
|
161
|
+
const builders: (
|
|
162
|
+
| TableBuilder<any, any>
|
|
163
|
+
| ViewBuilder<any, any, any>
|
|
164
|
+
| ProcedureBuilder<any, any>
|
|
165
|
+
)[] = [];
|
|
159
166
|
|
|
160
167
|
// Tables
|
|
161
168
|
const tables: TableBuilder<any, any>[] = Object.values(def.meta.tables);
|
|
@@ -191,7 +198,10 @@ export function validateRelations(def: DbContextDef<any, any, any>): void {
|
|
|
191
198
|
if (relations == null) continue;
|
|
192
199
|
|
|
193
200
|
for (const [relName, relDef] of Object.entries(relations)) {
|
|
194
|
-
if (
|
|
201
|
+
if (
|
|
202
|
+
!(relDef instanceof ForeignKeyTargetBuilder) &&
|
|
203
|
+
!(relDef instanceof RelationKeyTargetBuilder)
|
|
204
|
+
) {
|
|
195
205
|
continue;
|
|
196
206
|
}
|
|
197
207
|
|
package/src/ddl/relation-ddl.ts
CHANGED
|
@@ -54,7 +54,10 @@ export function getAddFkQueryDef(
|
|
|
54
54
|
/**
|
|
55
55
|
* ADD INDEX QueryDef 생성
|
|
56
56
|
*/
|
|
57
|
-
export function getAddIdxQueryDef(
|
|
57
|
+
export function getAddIdxQueryDef(
|
|
58
|
+
table: QueryDefObjectName,
|
|
59
|
+
indexBuilder: IndexBuilder<string[]>,
|
|
60
|
+
): QueryDef {
|
|
58
61
|
const indexMeta = indexBuilder.meta;
|
|
59
62
|
|
|
60
63
|
return {
|
package/src/ddl/schema-ddl.ts
CHANGED
|
@@ -9,7 +9,10 @@ import type {
|
|
|
9
9
|
/**
|
|
10
10
|
* CLEAR SCHEMA QueryDef 생성
|
|
11
11
|
*/
|
|
12
|
-
export function getClearSchemaQueryDef(params: {
|
|
12
|
+
export function getClearSchemaQueryDef(params: {
|
|
13
|
+
database: string;
|
|
14
|
+
schema?: string;
|
|
15
|
+
}): ClearSchemaQueryDef {
|
|
13
16
|
return { type: "clearSchema", database: params.database, schema: params.schema };
|
|
14
17
|
}
|
|
15
18
|
|
|
@@ -30,6 +33,9 @@ export function getTruncateQueryDef(table: QueryDefObjectName): TruncateQueryDef
|
|
|
30
33
|
/**
|
|
31
34
|
* SWITCH FK QueryDef 생성
|
|
32
35
|
*/
|
|
33
|
-
export function getSwitchFkQueryDef(
|
|
36
|
+
export function getSwitchFkQueryDef(
|
|
37
|
+
table: QueryDefObjectName,
|
|
38
|
+
switch_: "on" | "off",
|
|
39
|
+
): SwitchFkQueryDef {
|
|
34
40
|
return { type: "switchFk", table, switch: switch_ };
|
|
35
41
|
}
|
package/src/ddl/table-ddl.ts
CHANGED
|
@@ -72,7 +72,10 @@ export function getCreateTableQueryDef(db: DbContextBase, table: TableBuilder<an
|
|
|
72
72
|
* @returns CREATE VIEW QueryDef
|
|
73
73
|
* @throws {Error} 뷰에 viewFn이 없을 때
|
|
74
74
|
*/
|
|
75
|
-
export function getCreateViewQueryDef(
|
|
75
|
+
export function getCreateViewQueryDef(
|
|
76
|
+
db: DbContextBase,
|
|
77
|
+
view: ViewBuilder<any, any, any>,
|
|
78
|
+
): QueryDef {
|
|
76
79
|
if (view.meta.viewFn == null) {
|
|
77
80
|
throw new Error(`뷰 '${view.meta.name}'에 viewFn이 없습니다.`);
|
|
78
81
|
}
|
|
@@ -99,7 +102,10 @@ export function getCreateViewQueryDef(db: DbContextBase, view: ViewBuilder<any,
|
|
|
99
102
|
* @returns CREATE PROCEDURE QueryDef
|
|
100
103
|
* @throws {Error} 프로시저에 본문이 없을 때
|
|
101
104
|
*/
|
|
102
|
-
export function getCreateProcQueryDef(
|
|
105
|
+
export function getCreateProcQueryDef(
|
|
106
|
+
db: DbContextBase,
|
|
107
|
+
procedure: ProcedureBuilder<any, any>,
|
|
108
|
+
): QueryDef {
|
|
103
109
|
if (procedure.meta.query == null) {
|
|
104
110
|
throw new Error(`프로시저 '${procedure.meta.name}'에 본문이 없습니다.`);
|
|
105
111
|
}
|
|
@@ -143,7 +149,10 @@ export function getDropTableQueryDef(table: QueryDefObjectName): DropTableQueryD
|
|
|
143
149
|
/**
|
|
144
150
|
* RENAME TABLE QueryDef 생성
|
|
145
151
|
*/
|
|
146
|
-
export function getRenameTableQueryDef(
|
|
152
|
+
export function getRenameTableQueryDef(
|
|
153
|
+
table: QueryDefObjectName,
|
|
154
|
+
newName: string,
|
|
155
|
+
): RenameTableQueryDef {
|
|
147
156
|
return { type: "renameTable", table, newName };
|
|
148
157
|
}
|
|
149
158
|
|
package/src/define-db-context.ts
CHANGED
|
@@ -17,7 +17,9 @@ export function defineDbContext<
|
|
|
17
17
|
}): DbContextDef<TTables & { _migration: typeof _Migration }, TViews, TProcedures> {
|
|
18
18
|
return {
|
|
19
19
|
meta: {
|
|
20
|
-
tables: { ...(config.tables ?? {}), _migration: _Migration } as TTables & {
|
|
20
|
+
tables: { ...(config.tables ?? {}), _migration: _Migration } as TTables & {
|
|
21
|
+
_migration: typeof _Migration;
|
|
22
|
+
},
|
|
21
23
|
views: (config.views ?? {}) as TViews,
|
|
22
24
|
procedures: (config.procedures ?? {}) as TProcedures,
|
|
23
25
|
migrations: config.migrations ?? [],
|
package/src/exec/executable.ts
CHANGED
|
@@ -103,7 +103,10 @@ export class Executable<TParams extends ColumnBuilderRecord, TReturns extends Co
|
|
|
103
103
|
* @see {@link Executable} 실행 클래스
|
|
104
104
|
* @see {@link ProcedureBuilder} 프로시저 정의
|
|
105
105
|
*/
|
|
106
|
-
export function executable<
|
|
106
|
+
export function executable<
|
|
107
|
+
TParams extends ColumnBuilderRecord,
|
|
108
|
+
TReturns extends ColumnBuilderRecord,
|
|
109
|
+
>(
|
|
107
110
|
db: DbContextBase,
|
|
108
111
|
builder: ProcedureBuilder<TParams, TReturns>,
|
|
109
112
|
): () => Executable<TParams, TReturns> {
|
package/src/exec/queryable.ts
CHANGED
|
@@ -14,7 +14,10 @@ import type {
|
|
|
14
14
|
UpsertQueryDef,
|
|
15
15
|
} from "../types/query-def";
|
|
16
16
|
import type { DbContextBase } from "../types/db-context-def";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
type ColumnBuilderRecord,
|
|
19
|
+
type DataToColumnBuilderRecord,
|
|
20
|
+
} from "../schema/factory/column-builder";
|
|
18
21
|
import type { ColumnPrimitive, ColumnPrimitiveStr } from "../types/column";
|
|
19
22
|
import type { WhereExprUnit } from "../expr/expr-unit";
|
|
20
23
|
import { ExprUnit } from "../expr/expr-unit";
|
|
@@ -110,7 +113,9 @@ class RecursiveQueryable<TBaseData extends DataRecord> {
|
|
|
110
113
|
* @param table - 재귀할 대상 테이블
|
|
111
114
|
* @returns self 속성이 추가된 Queryable (자기 참조용)
|
|
112
115
|
*/
|
|
113
|
-
from<T extends TableBuilder<any, any>>(
|
|
116
|
+
from<T extends TableBuilder<any, any>>(
|
|
117
|
+
table: T,
|
|
118
|
+
): Queryable<T["$infer"] & { self?: TBaseData[] }, T> {
|
|
114
119
|
const selfAlias = `${this._cteName}.self`;
|
|
115
120
|
|
|
116
121
|
return queryable(this._baseQr.meta.db, table, this._cteName)().join(
|
|
@@ -132,7 +137,9 @@ class RecursiveQueryable<TBaseData extends DataRecord> {
|
|
|
132
137
|
* @param columns - 커스텀 컬럼 정의
|
|
133
138
|
* @returns self 속성이 추가된 Queryable
|
|
134
139
|
*/
|
|
135
|
-
select<R extends DataRecord>(
|
|
140
|
+
select<R extends DataRecord>(
|
|
141
|
+
columns: QueryableRecord<R>,
|
|
142
|
+
): Queryable<R & { self?: TBaseData[] }, never> {
|
|
136
143
|
const selfAlias = `${this._cteName}.self`;
|
|
137
144
|
|
|
138
145
|
return new Queryable<R, never>({
|
|
@@ -240,7 +247,9 @@ export class Queryable<
|
|
|
240
247
|
* }))
|
|
241
248
|
* ```
|
|
242
249
|
*/
|
|
243
|
-
select<R extends DataRecord>(
|
|
250
|
+
select<R extends DataRecord>(
|
|
251
|
+
fn: (columns: QueryableRecord<TData>) => QueryableRecord<R>,
|
|
252
|
+
): Queryable<R, never> {
|
|
244
253
|
if (Array.isArray(this.meta.from)) {
|
|
245
254
|
const newFroms = this.meta.from.map((from) => from.select(fn));
|
|
246
255
|
return new Queryable({
|
|
@@ -507,7 +516,9 @@ export class Queryable<
|
|
|
507
516
|
conditions.push(expr.or(columnMatches));
|
|
508
517
|
} else if (parsed.or.length > 1) {
|
|
509
518
|
const orConditions = parsed.or.map((pattern) => {
|
|
510
|
-
const columnMatches = columns.map((col) =>
|
|
519
|
+
const columnMatches = columns.map((col) =>
|
|
520
|
+
expr.like(expr.lower(col), pattern.toLowerCase()),
|
|
521
|
+
);
|
|
511
522
|
return expr.or(columnMatches);
|
|
512
523
|
});
|
|
513
524
|
conditions.push(expr.or(orConditions));
|
|
@@ -552,7 +563,9 @@ export class Queryable<
|
|
|
552
563
|
* .groupBy((o) => [o.userId])
|
|
553
564
|
* ```
|
|
554
565
|
*/
|
|
555
|
-
groupBy(
|
|
566
|
+
groupBy(
|
|
567
|
+
fn: (columns: QueryableRecord<TData>) => ExprUnit<ColumnPrimitive>[],
|
|
568
|
+
): Queryable<TData, never> {
|
|
556
569
|
if (Array.isArray(this.meta.from)) {
|
|
557
570
|
const newFroms = this.meta.from.map((from) => from.groupBy(fn));
|
|
558
571
|
return new Queryable({
|
|
@@ -677,7 +690,10 @@ export class Queryable<
|
|
|
677
690
|
joinSingle<A extends string, R extends DataRecord>(
|
|
678
691
|
as: A,
|
|
679
692
|
fwd: (qr: JoinQueryable, cols: QueryableRecord<TData>) => Queryable<R, any>,
|
|
680
|
-
): Queryable<
|
|
693
|
+
): Queryable<
|
|
694
|
+
{ [K in keyof TData as K extends A ? never : K]: TData[K] } & { [K in A]?: R },
|
|
695
|
+
TFrom
|
|
696
|
+
> {
|
|
681
697
|
if (Array.isArray(this.meta.from)) {
|
|
682
698
|
const newFroms = this.meta.from.map((from) => from.joinSingle(as, fwd));
|
|
683
699
|
return new Queryable({
|
|
@@ -797,7 +813,9 @@ export class Queryable<
|
|
|
797
813
|
|
|
798
814
|
// FKT join은 배열로 저장되므로 배열인 경우 첫 번째 요소 사용
|
|
799
815
|
const srcColsRaw = parentChain ? parentCols[parentChain] : parentCols;
|
|
800
|
-
const srcCols = (
|
|
816
|
+
const srcCols = (
|
|
817
|
+
Array.isArray(srcColsRaw) ? srcColsRaw[0] : srcColsRaw
|
|
818
|
+
) as QueryableRecord<any>;
|
|
801
819
|
const conditions: WhereExprUnit[] = [];
|
|
802
820
|
|
|
803
821
|
for (let i = 0; i < fkColKeys.length; i++) {
|
|
@@ -811,7 +829,10 @@ export class Queryable<
|
|
|
811
829
|
});
|
|
812
830
|
|
|
813
831
|
currentTable = targetTable;
|
|
814
|
-
} else if (
|
|
832
|
+
} else if (
|
|
833
|
+
relationDef instanceof ForeignKeyTargetBuilder ||
|
|
834
|
+
relationDef instanceof RelationKeyTargetBuilder
|
|
835
|
+
) {
|
|
815
836
|
// FKT/RelationKeyTarget (1:N 또는 1:1): User.posts → Post[]
|
|
816
837
|
// 조건: Post.userId = User.id
|
|
817
838
|
const targetTable = relationDef.meta.targetTableFn();
|
|
@@ -834,7 +855,9 @@ export class Queryable<
|
|
|
834
855
|
|
|
835
856
|
// FKT join은 배열로 저장되므로 배열인 경우 첫 번째 요소 사용
|
|
836
857
|
const srcColsRaw = parentChain ? parentCols[parentChain] : parentCols;
|
|
837
|
-
const srcCols = (
|
|
858
|
+
const srcCols = (
|
|
859
|
+
Array.isArray(srcColsRaw) ? srcColsRaw[0] : srcColsRaw
|
|
860
|
+
) as QueryableRecord<any>;
|
|
838
861
|
const conditions: WhereExprUnit[] = [];
|
|
839
862
|
|
|
840
863
|
for (let i = 0; i < fkColKeys.length; i++) {
|
|
@@ -905,7 +928,9 @@ export class Queryable<
|
|
|
905
928
|
* );
|
|
906
929
|
* ```
|
|
907
930
|
*/
|
|
908
|
-
static union<TData extends DataRecord>(
|
|
931
|
+
static union<TData extends DataRecord>(
|
|
932
|
+
...queries: Queryable<TData, any>[]
|
|
933
|
+
): Queryable<TData, never> {
|
|
909
934
|
if (queries.length < 2) {
|
|
910
935
|
throw new ArgumentError("union은 최소 2개의 queryable이 필요합니다.", {
|
|
911
936
|
provided: queries.length,
|
|
@@ -946,7 +971,9 @@ export class Queryable<
|
|
|
946
971
|
* )
|
|
947
972
|
* ```
|
|
948
973
|
*/
|
|
949
|
-
recursive(
|
|
974
|
+
recursive(
|
|
975
|
+
fwd: (qr: RecursiveQueryable<TData>) => Queryable<TData, any>,
|
|
976
|
+
): Queryable<TData, never> {
|
|
950
977
|
if (Array.isArray(this.meta.from)) {
|
|
951
978
|
const newFroms = this.meta.from.map((from) => from.recursive(fwd));
|
|
952
979
|
return new Queryable({
|
|
@@ -994,7 +1021,10 @@ export class Queryable<
|
|
|
994
1021
|
* ```
|
|
995
1022
|
*/
|
|
996
1023
|
async result(): Promise<TData[]> {
|
|
997
|
-
const results = await this.meta.db.executeDefs<TData>(
|
|
1024
|
+
const results = await this.meta.db.executeDefs<TData>(
|
|
1025
|
+
[this.getSelectQueryDef()],
|
|
1026
|
+
[this.getResultMeta()],
|
|
1027
|
+
);
|
|
998
1028
|
return results[0];
|
|
999
1029
|
}
|
|
1000
1030
|
|
|
@@ -1126,7 +1156,12 @@ export class Queryable<
|
|
|
1126
1156
|
});
|
|
1127
1157
|
}
|
|
1128
1158
|
|
|
1129
|
-
private _buildFromDef():
|
|
1159
|
+
private _buildFromDef():
|
|
1160
|
+
| QueryDefObjectName
|
|
1161
|
+
| SelectQueryDef
|
|
1162
|
+
| SelectQueryDef[]
|
|
1163
|
+
| string
|
|
1164
|
+
| undefined {
|
|
1130
1165
|
const from = this.meta.from;
|
|
1131
1166
|
|
|
1132
1167
|
if (from instanceof TableBuilder || from instanceof ViewBuilder) {
|
|
@@ -1440,7 +1475,9 @@ export class Queryable<
|
|
|
1440
1475
|
* }));
|
|
1441
1476
|
* ```
|
|
1442
1477
|
*/
|
|
1443
|
-
async update(
|
|
1478
|
+
async update(
|
|
1479
|
+
recordFwd: (cols: QueryableRecord<TData>) => QueryableRecord<TFrom["$inferUpdate"]>,
|
|
1480
|
+
): Promise<void>;
|
|
1444
1481
|
async update<K extends keyof TFrom["$columns"] & string>(
|
|
1445
1482
|
recordFwd: (cols: QueryableRecord<TData>) => QueryableRecord<TFrom["$inferUpdate"]>,
|
|
1446
1483
|
outputColumns: K[],
|
|
@@ -1479,7 +1516,9 @@ export class Queryable<
|
|
|
1479
1516
|
* ```
|
|
1480
1517
|
*/
|
|
1481
1518
|
async delete(): Promise<void>;
|
|
1482
|
-
async delete<K extends keyof TFrom["$columns"] & string>(
|
|
1519
|
+
async delete<K extends keyof TFrom["$columns"] & string>(
|
|
1520
|
+
outputColumns: K[],
|
|
1521
|
+
): Promise<Pick<TFrom["$columns"], K>[]>;
|
|
1483
1522
|
async delete<K extends keyof TFrom["$columns"] & string>(
|
|
1484
1523
|
outputColumns?: K[],
|
|
1485
1524
|
): Promise<Pick<TFrom["$columns"], K>[] | void> {
|
|
@@ -1574,7 +1613,9 @@ export class Queryable<
|
|
|
1574
1613
|
* );
|
|
1575
1614
|
* ```
|
|
1576
1615
|
*/
|
|
1577
|
-
async upsert(
|
|
1616
|
+
async upsert(
|
|
1617
|
+
updateFwd: (cols: QueryableRecord<TData>) => QueryableRecord<TFrom["$inferUpdate"]>,
|
|
1618
|
+
): Promise<void>;
|
|
1578
1619
|
async upsert<K extends keyof TFrom["$inferColumns"] & string>(
|
|
1579
1620
|
insertFwd: (cols: QueryableRecord<TData>) => QueryableRecord<TFrom["$inferInsert"]>,
|
|
1580
1621
|
outputColumns?: K[],
|
|
@@ -1583,12 +1624,18 @@ export class Queryable<
|
|
|
1583
1624
|
updateFwd: (cols: QueryableRecord<TData>) => U,
|
|
1584
1625
|
insertFwd: (updateRecord: U) => QueryableRecord<TFrom["$inferInsert"]>,
|
|
1585
1626
|
): Promise<void>;
|
|
1586
|
-
async upsert<
|
|
1627
|
+
async upsert<
|
|
1628
|
+
U extends QueryableRecord<TFrom["$inferUpdate"]>,
|
|
1629
|
+
K extends keyof TFrom["$inferColumns"] & string,
|
|
1630
|
+
>(
|
|
1587
1631
|
updateFwd: (cols: QueryableRecord<TData>) => U,
|
|
1588
1632
|
insertFwd: (updateRecord: U) => QueryableRecord<TFrom["$inferInsert"]>,
|
|
1589
1633
|
outputColumns?: K[],
|
|
1590
1634
|
): Promise<Pick<TFrom["$inferColumns"], K>[]>;
|
|
1591
|
-
async upsert<
|
|
1635
|
+
async upsert<
|
|
1636
|
+
U extends QueryableRecord<TFrom["$inferUpdate"]>,
|
|
1637
|
+
K extends keyof TFrom["$inferColumns"] & string,
|
|
1638
|
+
>(
|
|
1592
1639
|
updateFwdOrInsertFwd:
|
|
1593
1640
|
| ((cols: QueryableRecord<TData>) => U)
|
|
1594
1641
|
| ((cols: QueryableRecord<TData>) => QueryableRecord<TFrom["$inferInsert"]>),
|
|
@@ -1601,7 +1648,8 @@ export class Queryable<
|
|
|
1601
1648
|
insertFwdOrOutputColumns instanceof Function ? insertFwdOrOutputColumns : updateFwdOrInsertFwd
|
|
1602
1649
|
) as (updateRecord: U) => QueryableRecord<TFrom["$inferInsert"]>;
|
|
1603
1650
|
|
|
1604
|
-
const realOutputColumns =
|
|
1651
|
+
const realOutputColumns =
|
|
1652
|
+
insertFwdOrOutputColumns instanceof Function ? outputColumns : insertFwdOrOutputColumns;
|
|
1605
1653
|
|
|
1606
1654
|
const results = await this.meta.db.executeDefs<Pick<TFrom["$inferColumns"], K>>(
|
|
1607
1655
|
[this.getUpsertQueryDef(updateRecordFwd, insertRecordFwd, realOutputColumns)],
|
|
@@ -1662,7 +1710,9 @@ export class Queryable<
|
|
|
1662
1710
|
async switchFk(switch_: "on" | "off"): Promise<void> {
|
|
1663
1711
|
const from = this.meta.from;
|
|
1664
1712
|
if (!(from instanceof TableBuilder) && !(from instanceof ViewBuilder)) {
|
|
1665
|
-
throw new Error(
|
|
1713
|
+
throw new Error(
|
|
1714
|
+
"switchFk는 TableBuilder 또는 ViewBuilder 기반 queryable에서만 사용할 수 있습니다.",
|
|
1715
|
+
);
|
|
1666
1716
|
}
|
|
1667
1717
|
await this.meta.db.switchFk(this.meta.db.getQueryDefObjectName(from), switch_);
|
|
1668
1718
|
}
|
|
@@ -1711,7 +1761,10 @@ export class Queryable<
|
|
|
1711
1761
|
* @returns 매칭된 PK 컬럼명 배열
|
|
1712
1762
|
* @throws FK/PK 컬럼 수 불일치 시
|
|
1713
1763
|
*/
|
|
1714
|
-
export function getMatchedPrimaryKeys(
|
|
1764
|
+
export function getMatchedPrimaryKeys(
|
|
1765
|
+
fkCols: string[],
|
|
1766
|
+
targetTable: TableBuilder<any, any>,
|
|
1767
|
+
): string[] {
|
|
1715
1768
|
const pk = targetTable.meta.primaryKey;
|
|
1716
1769
|
if (pk == null || fkCols.length !== pk.length) {
|
|
1717
1770
|
throw new Error(
|
|
@@ -1749,7 +1802,9 @@ function transformColumnsAlias<TRecord extends DataRecord>(
|
|
|
1749
1802
|
result[key] = expr.col(value.dataType, alias, fullKey);
|
|
1750
1803
|
} else if (Array.isArray(value)) {
|
|
1751
1804
|
if (value.length > 0) {
|
|
1752
|
-
result[key] = [
|
|
1805
|
+
result[key] = [
|
|
1806
|
+
transformColumnsAlias(value[0] as QueryableRecord<DataRecord>, alias, fullKey),
|
|
1807
|
+
];
|
|
1753
1808
|
}
|
|
1754
1809
|
} else if (typeof value === "object" && value != null) {
|
|
1755
1810
|
result[key] = transformColumnsAlias(value as QueryableRecord<DataRecord>, alias, fullKey);
|
|
@@ -1767,7 +1822,12 @@ function transformColumnsAlias<TRecord extends DataRecord>(
|
|
|
1767
1822
|
|
|
1768
1823
|
interface QueryableMeta<TData extends DataRecord> {
|
|
1769
1824
|
db: DbContextBase;
|
|
1770
|
-
from?:
|
|
1825
|
+
from?:
|
|
1826
|
+
| TableBuilder<any, any>
|
|
1827
|
+
| ViewBuilder<any, any, any>
|
|
1828
|
+
| Queryable<any, any>
|
|
1829
|
+
| Queryable<TData, any>[]
|
|
1830
|
+
| string;
|
|
1771
1831
|
as: string;
|
|
1772
1832
|
columns: QueryableRecord<TData>;
|
|
1773
1833
|
isCustomColumns?: boolean;
|
|
@@ -1835,7 +1895,9 @@ const PATH_SYMBOL = Symbol("path");
|
|
|
1835
1895
|
* include()용 타입 안전 경로 프록시
|
|
1836
1896
|
*/
|
|
1837
1897
|
export type PathProxy<TObject> = {
|
|
1838
|
-
[K in keyof TObject as TObject[K] extends ColumnPrimitive ? never : K]-?: PathProxy<
|
|
1898
|
+
[K in keyof TObject as TObject[K] extends ColumnPrimitive ? never : K]-?: PathProxy<
|
|
1899
|
+
UnwrapArray<TObject[K]>
|
|
1900
|
+
>;
|
|
1839
1901
|
} & { readonly [PATH_SYMBOL]: string[] };
|
|
1840
1902
|
|
|
1841
1903
|
/**
|
|
@@ -1898,7 +1960,10 @@ export function queryable<TBuilder extends TableBuilder<any, any> | ViewBuilder<
|
|
|
1898
1960
|
from: tableOrView,
|
|
1899
1961
|
as: finalAs,
|
|
1900
1962
|
columns: Object.fromEntries(
|
|
1901
|
-
Object.entries(columnDefs).map(([key, colDef]) => [
|
|
1963
|
+
Object.entries(columnDefs).map(([key, colDef]) => [
|
|
1964
|
+
key,
|
|
1965
|
+
expr.col(colDef.meta.type, finalAs, key),
|
|
1966
|
+
]),
|
|
1902
1967
|
),
|
|
1903
1968
|
}) as any;
|
|
1904
1969
|
}
|
|
@@ -164,7 +164,11 @@ function termToLikePattern(term: string): string {
|
|
|
164
164
|
let pattern = term.replace(/\*/g, WILDCARD);
|
|
165
165
|
|
|
166
166
|
// SQL LIKE 특수문자 이스케이프 (\ → \\, % → \%, _ → \_, [ → \[)
|
|
167
|
-
pattern = pattern
|
|
167
|
+
pattern = pattern
|
|
168
|
+
.replace(/\\/g, "\\\\")
|
|
169
|
+
.replace(/%/g, "\\%")
|
|
170
|
+
.replace(/_/g, "\\_")
|
|
171
|
+
.replace(/\[/g, "\\[");
|
|
168
172
|
|
|
169
173
|
// 와일드카드 마커 → % (SQL 와일드카드)
|
|
170
174
|
pattern = pattern.replaceAll(WILDCARD, "%");
|