@simplysm/orm-common 14.0.49 → 14.0.51
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 +39 -172
- package/dist/db-context.d.ts +6 -6
- package/dist/db-context.d.ts.map +1 -1
- package/dist/db-context.js.map +1 -1
- package/dist/ddl/initialize.js.map +1 -1
- package/dist/ddl/table-ddl.d.ts +3 -3
- package/dist/ddl/table-ddl.d.ts.map +1 -1
- package/dist/ddl/table-ddl.js.map +1 -1
- package/dist/exec/queryable.d.ts +9 -9
- package/dist/exec/queryable.d.ts.map +1 -1
- package/dist/exec/queryable.js.map +1 -1
- package/dist/models/system-migration.d.ts +1 -1
- package/dist/schema/factory/relation-builder.d.ts +17 -17
- package/dist/schema/factory/relation-builder.d.ts.map +1 -1
- package/dist/schema/factory/relation-builder.js.map +1 -1
- package/dist/schema/table-builder.d.ts +12 -12
- package/dist/schema/table-builder.d.ts.map +1 -1
- package/dist/schema/table-builder.js +1 -1
- package/dist/schema/table-builder.js.map +1 -1
- package/dist/types/db-context-def.d.ts +4 -4
- package/dist/types/db-context-def.d.ts.map +1 -1
- package/dist/utils/result-parser.js.map +1 -1
- package/docs/core/db-context.md +208 -0
- package/docs/core/db-transaction-error.md +64 -0
- package/docs/expression/expr-unit.md +62 -0
- package/docs/expression/expr.md +198 -0
- package/docs/models/migration.md +37 -0
- package/docs/query-builder/create-query-builder.md +80 -0
- package/docs/queryable-executable/executable.md +54 -0
- package/docs/queryable-executable/parse-search-query.md +75 -0
- package/docs/queryable-executable/queryable.md +238 -0
- package/docs/schema-builders/column-builder.md +63 -0
- package/docs/schema-builders/foreign-key-builder.md +137 -0
- package/docs/schema-builders/index-builder.md +54 -0
- package/docs/schema-builders/procedure.md +67 -0
- package/docs/schema-builders/table.md +94 -0
- package/docs/schema-builders/view.md +71 -0
- package/docs/types/data-type.md +146 -0
- package/docs/types/dialect.md +151 -0
- package/docs/types/expr.md +175 -0
- package/docs/types/parse-query-result.md +58 -0
- package/docs/types/query-def.md +224 -0
- package/package.json +2 -2
- package/src/db-context.ts +8 -8
- package/src/ddl/initialize.ts +4 -4
- package/src/ddl/table-ddl.ts +3 -3
- package/src/exec/queryable.ts +19 -19
- package/src/schema/factory/relation-builder.ts +45 -38
- package/src/schema/table-builder.ts +13 -12
- package/src/types/db-context-def.ts +4 -4
- package/src/utils/result-parser.ts +57 -57
- package/docs/core.md +0 -188
- package/docs/expression.md +0 -190
- package/docs/models.md +0 -17
- package/docs/query-builder.md +0 -97
- package/docs/queryable-executable.md +0 -311
- package/docs/schema-builders.md +0 -364
- package/docs/types.md +0 -537
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Table
|
|
2
|
+
|
|
3
|
+
Fluent API로 Database Table 스키마를 정의하는 빌더 팩토리 함수. 반환된 `TableBuilder`에 컬럼, PK, 인덱스, 관계를 메서드 체이닝으로 정의한다. 각 메서드는 새 인스턴스를 반환하므로 불변(immutable)이다.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
export function Table(name: string): TableBuilder<never, never>;
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Parameters
|
|
10
|
+
|
|
11
|
+
| Param | Type | Description |
|
|
12
|
+
|-------|------|-------------|
|
|
13
|
+
| `name` | `string` | 테이블 이름 |
|
|
14
|
+
|
|
15
|
+
## Returns
|
|
16
|
+
|
|
17
|
+
`TableBuilder<never, never>` — 빈 TableBuilder 인스턴스
|
|
18
|
+
|
|
19
|
+
## Related Types
|
|
20
|
+
|
|
21
|
+
### `TableBuilder<TColumns, TRelations>`
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
export class TableBuilder<
|
|
25
|
+
TColumns extends ColumnBuilderRecord,
|
|
26
|
+
TRelations extends RelationBuilderRecord,
|
|
27
|
+
> {
|
|
28
|
+
readonly $inferSelect!: InferColumns<TColumns> & InferDeepRelations<TRelations>;
|
|
29
|
+
readonly $inferColumns!: InferColumns<TColumns>;
|
|
30
|
+
readonly $inferInsert!: InferInsertColumns<TColumns>;
|
|
31
|
+
readonly $inferUpdate!: InferUpdateColumns<TColumns>;
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
#### 타입 추론 프로퍼티
|
|
36
|
+
|
|
37
|
+
| 프로퍼티 | 설명 |
|
|
38
|
+
|----------|------|
|
|
39
|
+
| `$inferSelect` | SELECT 결과 타입 (컬럼 + 관계 포함) |
|
|
40
|
+
| `$inferColumns` | 컬럼만의 타입 |
|
|
41
|
+
| `$inferInsert` | INSERT 입력 타입 (autoIncrement 제외, nullable/default는 optional) |
|
|
42
|
+
| `$inferUpdate` | UPDATE 입력 타입 (모든 필드 optional) |
|
|
43
|
+
|
|
44
|
+
#### 빌더 메서드
|
|
45
|
+
|
|
46
|
+
| 메서드 | 설명 |
|
|
47
|
+
|--------|------|
|
|
48
|
+
| `description(desc)` | 테이블 설명 설정 (DDL Comment) |
|
|
49
|
+
| `database(db)` | Database 이름 설정 |
|
|
50
|
+
| `schema(schema)` | Schema 이름 설정 (MSSQL: dbo, PostgreSQL: public) |
|
|
51
|
+
| `columns(fn)` | 컬럼 정의. `fn(c) => ({ name: c.varchar(100), ... })` |
|
|
52
|
+
| `primaryKey(...columns)` | PK 설정. 복합 PK는 여러 인자 전달 |
|
|
53
|
+
| `indexes(fn)` | 인덱스 정의. `fn(i) => [i.index("email").unique(), ...]` |
|
|
54
|
+
| `relations(fn)` | 관계 정의. `fn(r) => ({ company: r.foreignKey(...), ... })` |
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const User = Table("User")
|
|
60
|
+
.database("mydb")
|
|
61
|
+
.columns((c) => ({
|
|
62
|
+
id: c.bigint().autoIncrement(),
|
|
63
|
+
name: c.varchar(100),
|
|
64
|
+
email: c.varchar(200).nullable(),
|
|
65
|
+
isActive: c.boolean().default(true),
|
|
66
|
+
companyId: c.bigint().nullable(),
|
|
67
|
+
createdAt: c.datetime(),
|
|
68
|
+
}))
|
|
69
|
+
.primaryKey("id")
|
|
70
|
+
.indexes((i) => [
|
|
71
|
+
i.index("email").unique(),
|
|
72
|
+
i.index("name", "createdAt").orderBy("ASC", "DESC"),
|
|
73
|
+
])
|
|
74
|
+
.relations((r) => ({
|
|
75
|
+
company: r.foreignKey(["companyId"], () => Company, { description: "소속회사" }),
|
|
76
|
+
posts: r.foreignKeyTarget(() => Post, "user"),
|
|
77
|
+
profile: r.foreignKeyTarget(() => Profile, "user", { single: true }),
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
// 복합 PK
|
|
81
|
+
const UserRole = Table("UserRole")
|
|
82
|
+
.columns((c) => ({
|
|
83
|
+
userId: c.bigint(),
|
|
84
|
+
roleId: c.bigint(),
|
|
85
|
+
}))
|
|
86
|
+
.primaryKey("userId", "roleId");
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 권장사항
|
|
90
|
+
|
|
91
|
+
### `isDeleted` 컬럼
|
|
92
|
+
|
|
93
|
+
- **기초정보(마스터 데이터) 테이블**: `isDeleted: c.boolean().default(false)` 컬럼을 포함한다. 삭제 시 `isDeleted: true`로 soft-delete하고, 복구 기능을 제공한다.
|
|
94
|
+
- **일반 데이터(트랜잭션 데이터 등) 테이블**: `isDeleted` 컬럼을 두지 않는다. 삭제 시 물리 삭제(row DELETE)로 처리한다.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# View
|
|
2
|
+
|
|
3
|
+
Fluent API로 Database View 스키마를 정의하는 빌더 팩토리 함수. 반환된 `ViewBuilder`에 쿼리와 관계를 메서드 체이닝으로 정의한다.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
export function View(name: string): ViewBuilder<never, never, never>;
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Parameters
|
|
10
|
+
|
|
11
|
+
| Param | Type | Description |
|
|
12
|
+
|-------|------|-------------|
|
|
13
|
+
| `name` | `string` | View 이름 |
|
|
14
|
+
|
|
15
|
+
## Returns
|
|
16
|
+
|
|
17
|
+
`ViewBuilder<never, never, never>` — 빈 ViewBuilder 인스턴스
|
|
18
|
+
|
|
19
|
+
## Related Types
|
|
20
|
+
|
|
21
|
+
### `ViewBuilder<TDbContext, TData, TRelations>`
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
export class ViewBuilder<
|
|
25
|
+
TDbContext extends DbContextBase,
|
|
26
|
+
TData extends DataRecord,
|
|
27
|
+
TRelations extends RelationBuilderRecord,
|
|
28
|
+
> {
|
|
29
|
+
readonly $inferSelect!: TData;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
#### 빌더 메서드
|
|
34
|
+
|
|
35
|
+
| 메서드 | 설명 |
|
|
36
|
+
|--------|------|
|
|
37
|
+
| `description(desc)` | View 설명 설정 (DDL Comment) |
|
|
38
|
+
| `database(db)` | Database 이름 설정 |
|
|
39
|
+
| `schema(schema)` | Schema 이름 설정 |
|
|
40
|
+
| `query(viewFn)` | View 쿼리 정의. `(db: TDb) => Queryable<TViewData, any>` |
|
|
41
|
+
| `relations(fn)` | 관계 정의 (`relationKey`/`relationKeyTarget`만 사용 가능) |
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// 단순 필터 View
|
|
47
|
+
const ActiveUsers = View("ActiveUsers")
|
|
48
|
+
.database("mydb")
|
|
49
|
+
.query((db: MainDb) =>
|
|
50
|
+
db.user()
|
|
51
|
+
.where((u) => [expr.eq(u.isActive, true)])
|
|
52
|
+
.select((u) => ({ id: u.id, name: u.name }))
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// 집계 View
|
|
56
|
+
const UserStats = View("UserStats")
|
|
57
|
+
.database("mydb")
|
|
58
|
+
.query((db: MainDb) =>
|
|
59
|
+
db.user()
|
|
60
|
+
.groupBy((u) => [u.companyId])
|
|
61
|
+
.select((u) => ({
|
|
62
|
+
companyId: u.companyId,
|
|
63
|
+
userCount: expr.count(u.id),
|
|
64
|
+
}))
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// DbContext에 등록
|
|
68
|
+
class MainDb extends DbContext {
|
|
69
|
+
activeUsers = this.queryable(ActiveUsers);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# DataType
|
|
2
|
+
|
|
3
|
+
SQL 데이터 타입 및 TypeScript 타입 매핑 관련 타입/상수 모음.
|
|
4
|
+
|
|
5
|
+
## `DataType`
|
|
6
|
+
|
|
7
|
+
SQL 데이터 타입 정의 discriminated union.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
export type DataType =
|
|
11
|
+
| { type: "int" }
|
|
12
|
+
| { type: "bigint" }
|
|
13
|
+
| { type: "float" }
|
|
14
|
+
| { type: "double" }
|
|
15
|
+
| { type: "decimal"; precision: number; scale?: number }
|
|
16
|
+
| { type: "varchar"; length: number }
|
|
17
|
+
| { type: "char"; length: number }
|
|
18
|
+
| { type: "text" }
|
|
19
|
+
| { type: "binary" }
|
|
20
|
+
| { type: "boolean" }
|
|
21
|
+
| { type: "datetime" }
|
|
22
|
+
| { type: "date" }
|
|
23
|
+
| { type: "time" }
|
|
24
|
+
| { type: "uuid" };
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
| `type` | MySQL | MSSQL | PostgreSQL | TypeScript |
|
|
28
|
+
|--------|-------|-------|------------|------------|
|
|
29
|
+
| `int` | INT | INT | INTEGER | `number` |
|
|
30
|
+
| `bigint` | BIGINT | BIGINT | BIGINT | `number` |
|
|
31
|
+
| `float` | FLOAT | REAL | REAL | `number` |
|
|
32
|
+
| `double` | DOUBLE | FLOAT | DOUBLE PRECISION | `number` |
|
|
33
|
+
| `decimal` | DECIMAL(p,s) | DECIMAL(p,s) | NUMERIC(p,s) | `number` |
|
|
34
|
+
| `varchar` | VARCHAR(n) | NVARCHAR(n) | VARCHAR(n) | `string` |
|
|
35
|
+
| `char` | CHAR(n) | NCHAR(n) | CHAR(n) | `string` |
|
|
36
|
+
| `text` | LONGTEXT | NVARCHAR(MAX) | TEXT | `string` |
|
|
37
|
+
| `binary` | LONGBLOB | VARBINARY(MAX) | BYTEA | `Bytes` |
|
|
38
|
+
| `boolean` | TINYINT(1) | BIT | BOOLEAN | `boolean` |
|
|
39
|
+
| `datetime` | DATETIME | DATETIME2 | TIMESTAMP | `DateTime` |
|
|
40
|
+
| `date` | DATE | DATE | DATE | `DateOnly` |
|
|
41
|
+
| `time` | TIME | TIME | TIME | `Time` |
|
|
42
|
+
| `uuid` | BINARY(16) | UNIQUEIDENTIFIER | UUID | `Uuid` |
|
|
43
|
+
|
|
44
|
+
## `ColumnPrimitiveMap`
|
|
45
|
+
|
|
46
|
+
TypeScript 타입 이름(문자열) → 실제 TypeScript 타입 매핑.
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
export type ColumnPrimitiveMap = {
|
|
50
|
+
string: string;
|
|
51
|
+
number: number;
|
|
52
|
+
boolean: boolean;
|
|
53
|
+
DateTime: DateTime;
|
|
54
|
+
DateOnly: DateOnly;
|
|
55
|
+
Time: Time;
|
|
56
|
+
Uuid: Uuid;
|
|
57
|
+
Bytes: Bytes;
|
|
58
|
+
};
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## `ColumnPrimitiveStr`
|
|
62
|
+
|
|
63
|
+
Column 원시 타입 이름 문자열. `keyof ColumnPrimitiveMap`.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
export type ColumnPrimitiveStr = keyof ColumnPrimitiveMap;
|
|
67
|
+
// "string" | "number" | "boolean" | "DateTime" | "DateOnly" | "Time" | "Uuid" | "Bytes"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## `ColumnPrimitive`
|
|
71
|
+
|
|
72
|
+
Column에 저장 가능한 모든 원시 타입. `undefined`는 NULL을 나타낸다.
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
export type ColumnPrimitive = ColumnPrimitiveMap[ColumnPrimitiveStr] | undefined;
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## `dataTypeStrToColumnPrimitiveStr`
|
|
79
|
+
|
|
80
|
+
SQL DataType의 `type` 문자열 → `ColumnPrimitiveStr` 매핑 상수.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
export const dataTypeStrToColumnPrimitiveStr: {
|
|
84
|
+
int: "number";
|
|
85
|
+
bigint: "number";
|
|
86
|
+
float: "number";
|
|
87
|
+
double: "number";
|
|
88
|
+
decimal: "number";
|
|
89
|
+
varchar: "string";
|
|
90
|
+
char: "string";
|
|
91
|
+
text: "string";
|
|
92
|
+
binary: "Bytes";
|
|
93
|
+
boolean: "boolean";
|
|
94
|
+
datetime: "DateTime";
|
|
95
|
+
date: "DateOnly";
|
|
96
|
+
time: "Time";
|
|
97
|
+
uuid: "Uuid";
|
|
98
|
+
};
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## `InferColumnPrimitiveFromDataType<T>`
|
|
102
|
+
|
|
103
|
+
`DataType`으로부터 TypeScript 타입을 추론하는 제네릭 타입.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
export type InferColumnPrimitiveFromDataType<TDataType extends DataType> =
|
|
107
|
+
ColumnPrimitiveMap[(typeof dataTypeStrToColumnPrimitiveStr)[TDataType["type"]]];
|
|
108
|
+
|
|
109
|
+
// 예시
|
|
110
|
+
type IntType = InferColumnPrimitiveFromDataType<{ type: "int" }>; // number
|
|
111
|
+
type VarcharType = InferColumnPrimitiveFromDataType<{ type: "varchar"; length: 100 }>; // string
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## `inferColumnPrimitiveStr`
|
|
115
|
+
|
|
116
|
+
런타임 값에서 `ColumnPrimitiveStr`을 추론하는 함수.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
export function inferColumnPrimitiveStr(value: ColumnPrimitive): ColumnPrimitiveStr;
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- `undefined`/`null`이면 에러를 던진다.
|
|
123
|
+
|
|
124
|
+
## `ColumnMeta`
|
|
125
|
+
|
|
126
|
+
`ColumnBuilder`에서 생성되어 `TableBuilder`에 전달되는 Column 메타데이터.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
export interface ColumnMeta {
|
|
130
|
+
type: ColumnPrimitiveStr;
|
|
131
|
+
dataType: DataType;
|
|
132
|
+
autoIncrement?: boolean;
|
|
133
|
+
nullable?: boolean;
|
|
134
|
+
default?: ColumnPrimitive;
|
|
135
|
+
description?: string;
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
| 필드 | 타입 | 설명 |
|
|
140
|
+
|------|------|------|
|
|
141
|
+
| `type` | `ColumnPrimitiveStr` | TypeScript 타입 이름 |
|
|
142
|
+
| `dataType` | `DataType` | SQL 데이터 타입 |
|
|
143
|
+
| `autoIncrement` | `boolean \| undefined` | AUTO_INCREMENT 여부 |
|
|
144
|
+
| `nullable` | `boolean \| undefined` | NULL 허용 여부 |
|
|
145
|
+
| `default` | `ColumnPrimitive \| undefined` | 기본값 |
|
|
146
|
+
| `description` | `string \| undefined` | Column 설명 (DDL Comment) |
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Dialect
|
|
2
|
+
|
|
3
|
+
지원하는 DB 방언 타입 및 관련 런타임 타입 모음.
|
|
4
|
+
|
|
5
|
+
## `Dialect`
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
export type Dialect = "mysql" | "mssql" | "postgresql";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
| 값 | 지원 버전 |
|
|
12
|
+
|----|-----------|
|
|
13
|
+
| `"mysql"` | MySQL 8.0.14+ |
|
|
14
|
+
| `"mssql"` | Microsoft SQL Server 2012+ |
|
|
15
|
+
| `"postgresql"` | PostgreSQL 9.0+ |
|
|
16
|
+
|
|
17
|
+
## `dialects`
|
|
18
|
+
|
|
19
|
+
모든 dialect 목록 배열. 테스트에서 dialect별 검증에 사용한다.
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
export const dialects: Dialect[] = ["mysql", "mssql", "postgresql"];
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## `IsolationLevel`
|
|
26
|
+
|
|
27
|
+
트랜잭션 격리 수준.
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
export type IsolationLevel =
|
|
31
|
+
| "READ_UNCOMMITTED"
|
|
32
|
+
| "READ_COMMITTED"
|
|
33
|
+
| "REPEATABLE_READ"
|
|
34
|
+
| "SERIALIZABLE";
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
| 값 | 설명 |
|
|
38
|
+
|----|------|
|
|
39
|
+
| `READ_UNCOMMITTED` | 커밋되지 않은 데이터 읽기 가능 (Dirty Read) |
|
|
40
|
+
| `READ_COMMITTED` | 커밋된 데이터만 읽기 (기본값) |
|
|
41
|
+
| `REPEATABLE_READ` | 트랜잭션 내 동일 쿼리가 동일 결과 보장 |
|
|
42
|
+
| `SERIALIZABLE` | 완전 직렬화 (가장 엄격) |
|
|
43
|
+
|
|
44
|
+
## `DataRecord`
|
|
45
|
+
|
|
46
|
+
쿼리 결과 데이터 레코드 타입. 재귀적 구조로 중첩 관계(`include`) 결과를 표현한다.
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
export type DataRecord = {
|
|
50
|
+
[key: string]: ColumnPrimitive | DataRecord | DataRecord[];
|
|
51
|
+
};
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## `DbContextExecutor`
|
|
55
|
+
|
|
56
|
+
실제 DB 연결과 쿼리 실행을 담당하는 인터페이스. `orm-node`의 `NodeDbContextExecutor` 또는 서비스 클라이언트 구현체가 이 인터페이스를 구현한다.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
export interface DbContextExecutor {
|
|
60
|
+
connect(): Promise<void>;
|
|
61
|
+
close(): Promise<void>;
|
|
62
|
+
beginTransaction(isolationLevel?: IsolationLevel): Promise<void>;
|
|
63
|
+
commitTransaction(): Promise<void>;
|
|
64
|
+
rollbackTransaction(): Promise<void>;
|
|
65
|
+
executeDefs<T = DataRecord>(
|
|
66
|
+
defs: QueryDef[],
|
|
67
|
+
resultMetas?: (ResultMeta | undefined)[],
|
|
68
|
+
): Promise<T[][]>;
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
| 메서드 | 설명 |
|
|
73
|
+
|--------|------|
|
|
74
|
+
| `connect()` | DB 연결 수립 |
|
|
75
|
+
| `close()` | DB 연결 종료 |
|
|
76
|
+
| `beginTransaction(isolationLevel?)` | 트랜잭션 시작 |
|
|
77
|
+
| `commitTransaction()` | 트랜잭션 커밋 |
|
|
78
|
+
| `rollbackTransaction()` | 트랜잭션 롤백 |
|
|
79
|
+
| `executeDefs(defs, resultMetas?)` | QueryDef 배열 실행, 결과 배열 반환 |
|
|
80
|
+
|
|
81
|
+
## `QueryBuildResult`
|
|
82
|
+
|
|
83
|
+
`QueryBuilderBase.build()` 반환 타입.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
export interface QueryBuildResult {
|
|
87
|
+
sql: string;
|
|
88
|
+
resultSetIndex?: number;
|
|
89
|
+
resultSetStride?: number;
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
| 필드 | 타입 | 설명 |
|
|
94
|
+
|------|------|------|
|
|
95
|
+
| `sql` | `string` | 빌드된 SQL 문자열 |
|
|
96
|
+
| `resultSetIndex` | `number \| undefined` | 결과를 가져올 결과 셋 인덱스. 기본값 0 |
|
|
97
|
+
| `resultSetStride` | `number \| undefined` | stride 간격으로 다중 결과 셋 수집. MySQL 배치 INSERT에 사용 |
|
|
98
|
+
|
|
99
|
+
## `ResultMeta`
|
|
100
|
+
|
|
101
|
+
SELECT 결과를 TypeScript 객체로 변환할 때 사용하는 메타데이터.
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
export interface ResultMeta {
|
|
105
|
+
columns: Record<string, ColumnPrimitiveStr>;
|
|
106
|
+
joins: Record<string, { isSingle: boolean }>;
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
| 필드 | 타입 | 설명 |
|
|
111
|
+
|------|------|------|
|
|
112
|
+
| `columns` | `Record<string, ColumnPrimitiveStr>` | 컬럼 이름 → 타입 이름 매핑 |
|
|
113
|
+
| `joins` | `Record<string, { isSingle: boolean }>` | JOIN 별칭 → 단일/배열 구분 |
|
|
114
|
+
|
|
115
|
+
## `Migration`
|
|
116
|
+
|
|
117
|
+
DB 마이그레이션 정의.
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
export interface Migration {
|
|
121
|
+
name: string;
|
|
122
|
+
up: (db: DbContextBase & DbContextDdlMethods) => Promise<void>;
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
| 필드 | 타입 | 설명 |
|
|
127
|
+
|------|------|------|
|
|
128
|
+
| `name` | `string` | 고유 마이그레이션 이름. 타임스탬프 형식 권장 |
|
|
129
|
+
| `up` | `(db) => Promise<void>` | 마이그레이션 실행 함수 |
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const migrations: Migration[] = [
|
|
133
|
+
{
|
|
134
|
+
name: "20260105_001_create_user_table",
|
|
135
|
+
up: async (db) => {
|
|
136
|
+
await db.createTable(User);
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## `pickResultSets`
|
|
143
|
+
|
|
144
|
+
다중 결과 셋에서 `QueryBuildResult` 메타데이터에 따라 필요한 결과만 추출한다. MySQL 배치 INSERT 후 OUTPUT 추출에 사용된다.
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
export function pickResultSets<T>(
|
|
148
|
+
rawResults: T[][],
|
|
149
|
+
buildResult: Pick<QueryBuildResult, "resultSetIndex" | "resultSetStride">,
|
|
150
|
+
): T[];
|
|
151
|
+
```
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Expr
|
|
2
|
+
|
|
3
|
+
SQL 표현식 AST 타입 모음. `expr.*` 함수들이 내부적으로 생성하며, `QueryBuilderBase`가 SQL 문자열로 변환한다.
|
|
4
|
+
|
|
5
|
+
## `Expr`
|
|
6
|
+
|
|
7
|
+
모든 SELECT 표현식 AST의 유니온 타입.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
export type Expr =
|
|
11
|
+
| ExprColumn
|
|
12
|
+
| ExprValue
|
|
13
|
+
| ExprRaw
|
|
14
|
+
| ExprConcat
|
|
15
|
+
| ExprLeft
|
|
16
|
+
| ExprRight
|
|
17
|
+
| ExprTrim
|
|
18
|
+
| ExprPadStart
|
|
19
|
+
| ExprReplace
|
|
20
|
+
| ExprUpper
|
|
21
|
+
| ExprLower
|
|
22
|
+
| ExprLength
|
|
23
|
+
| ExprByteLength
|
|
24
|
+
| ExprSubstring
|
|
25
|
+
| ExprIndexOf
|
|
26
|
+
| ExprAbs
|
|
27
|
+
| ExprRound
|
|
28
|
+
| ExprCeil
|
|
29
|
+
| ExprFloor
|
|
30
|
+
| ExprAdd
|
|
31
|
+
| ExprSub
|
|
32
|
+
| ExprMul
|
|
33
|
+
| ExprDiv
|
|
34
|
+
| ExprMod
|
|
35
|
+
| ExprYear
|
|
36
|
+
| ExprMonth
|
|
37
|
+
| ExprDay
|
|
38
|
+
| ExprHour
|
|
39
|
+
| ExprMinute
|
|
40
|
+
| ExprSecond
|
|
41
|
+
| ExprDateDiff
|
|
42
|
+
| ExprDateAdd
|
|
43
|
+
| ExprCount
|
|
44
|
+
| ExprSum
|
|
45
|
+
| ExprAvg
|
|
46
|
+
| ExprMax
|
|
47
|
+
| ExprMin
|
|
48
|
+
| ExprRowNumber
|
|
49
|
+
| ExprRank
|
|
50
|
+
| ExprDenseRank
|
|
51
|
+
| ExprLag
|
|
52
|
+
| ExprLead
|
|
53
|
+
| ExprSumOver
|
|
54
|
+
| ExprCountOver
|
|
55
|
+
| ExprAvgOver
|
|
56
|
+
| ExprMaxOver
|
|
57
|
+
| ExprMinOver
|
|
58
|
+
| ExprFirstValue
|
|
59
|
+
| ExprLastValue
|
|
60
|
+
| ExprNtile
|
|
61
|
+
| ExprSubquery
|
|
62
|
+
| ExprIf
|
|
63
|
+
| ExprSwitch
|
|
64
|
+
| ExprCoalesce
|
|
65
|
+
| ExprNullIf
|
|
66
|
+
| ExprCast
|
|
67
|
+
| ExprToDateOnly
|
|
68
|
+
| ExprToDateTime;
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## `WhereExpr`
|
|
72
|
+
|
|
73
|
+
WHERE 절 표현식 AST 유니온 타입.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
export type WhereExpr =
|
|
77
|
+
| ExprEq
|
|
78
|
+
| ExprGt
|
|
79
|
+
| ExprLt
|
|
80
|
+
| ExprGte
|
|
81
|
+
| ExprLte
|
|
82
|
+
| ExprBetween
|
|
83
|
+
| ExprIsNull
|
|
84
|
+
| ExprLike
|
|
85
|
+
| ExprRegexp
|
|
86
|
+
| ExprIn
|
|
87
|
+
| ExprInQuery
|
|
88
|
+
| ExprExists
|
|
89
|
+
| ExprNot
|
|
90
|
+
| ExprAnd
|
|
91
|
+
| ExprOr;
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## `DateUnit`
|
|
95
|
+
|
|
96
|
+
날짜 연산 단위. `expr.dateDiff()`, `expr.dateAdd()`에서 사용.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
export type DateUnit = "year" | "month" | "day" | "hour" | "minute" | "second";
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## `WinSpec`
|
|
103
|
+
|
|
104
|
+
윈도우 함수 스펙.
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
export interface WinSpec {
|
|
108
|
+
partitionBy?: Expr[];
|
|
109
|
+
orderBy?: [Expr, ("ASC" | "DESC")?][];
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## 값 표현식 인터페이스
|
|
114
|
+
|
|
115
|
+
| 인터페이스 | `type` 값 | 설명 |
|
|
116
|
+
|------------|-----------|------|
|
|
117
|
+
| `ExprColumn` | `"column"` | 컬럼 참조. `path: string[]` |
|
|
118
|
+
| `ExprValue` | `"value"` | 리터럴 값. `value: ColumnPrimitive` |
|
|
119
|
+
| `ExprRaw` | `"raw"` | Raw SQL. `sql: string`, `params: Expr[]` |
|
|
120
|
+
|
|
121
|
+
## 비교 표현식 인터페이스 (WhereExpr)
|
|
122
|
+
|
|
123
|
+
| 인터페이스 | `type` 값 | 설명 |
|
|
124
|
+
|------------|-----------|------|
|
|
125
|
+
| `ExprEq` | `"eq"` | `=` (NULL 안전). `source`, `target` |
|
|
126
|
+
| `ExprGt` | `"gt"` | `>`. `source`, `target` |
|
|
127
|
+
| `ExprLt` | `"lt"` | `<`. `source`, `target` |
|
|
128
|
+
| `ExprGte` | `"gte"` | `>=`. `source`, `target` |
|
|
129
|
+
| `ExprLte` | `"lte"` | `<=`. `source`, `target` |
|
|
130
|
+
| `ExprBetween` | `"between"` | `BETWEEN`. `source`, `from?`, `to?` |
|
|
131
|
+
| `ExprIsNull` | `"null"` | `IS NULL`. `arg` |
|
|
132
|
+
| `ExprLike` | `"like"` | `LIKE`. `source`, `pattern` |
|
|
133
|
+
| `ExprRegexp` | `"regexp"` | 정규식. `source`, `pattern` |
|
|
134
|
+
| `ExprIn` | `"in"` | `IN (...)`. `source`, `values: Expr[]` |
|
|
135
|
+
| `ExprInQuery` | `"inQuery"` | `IN (SELECT ...)`. `source`, `query: SelectQueryDef` |
|
|
136
|
+
| `ExprExists` | `"exists"` | `EXISTS (SELECT ...)`. `query: SelectQueryDef` |
|
|
137
|
+
| `ExprNot` | `"not"` | `NOT`. `arg: WhereExpr` |
|
|
138
|
+
| `ExprAnd` | `"and"` | `AND`. `conditions: WhereExpr[]` |
|
|
139
|
+
| `ExprOr` | `"or"` | `OR`. `conditions: WhereExpr[]` |
|
|
140
|
+
|
|
141
|
+
## 집계/윈도우 함수 인터페이스
|
|
142
|
+
|
|
143
|
+
| 인터페이스 | `type` 값 | 설명 |
|
|
144
|
+
|------------|-----------|------|
|
|
145
|
+
| `ExprCount` | `"count"` | `COUNT(*)` 또는 `COUNT(arg)`. `arg?: Expr` |
|
|
146
|
+
| `ExprSum` | `"sum"` | `SUM(arg)`. `arg: Expr` |
|
|
147
|
+
| `ExprAvg` | `"avg"` | `AVG(arg)`. `arg: Expr` |
|
|
148
|
+
| `ExprMax` | `"max"` | `MAX(arg)`. `arg: Expr` |
|
|
149
|
+
| `ExprMin` | `"min"` | `MIN(arg)`. `arg: Expr` |
|
|
150
|
+
| `ExprRowNumber` | `"rowNumber"` | `ROW_NUMBER() OVER (...)`. `winSpec: WinSpec` |
|
|
151
|
+
| `ExprRank` | `"rank"` | `RANK() OVER (...)`. `winSpec: WinSpec` |
|
|
152
|
+
| `ExprDenseRank` | `"denseRank"` | `DENSE_RANK() OVER (...)`. `winSpec: WinSpec` |
|
|
153
|
+
| `ExprLag` | `"lag"` | `LAG(...) OVER (...)`. `source`, `offset?`, `default?`, `winSpec?` |
|
|
154
|
+
| `ExprLead` | `"lead"` | `LEAD(...) OVER (...)`. `source`, `offset?`, `default?`, `winSpec?` |
|
|
155
|
+
| `ExprSumOver` | `"sumOver"` | `SUM(...) OVER (...)` |
|
|
156
|
+
| `ExprCountOver` | `"countOver"` | `COUNT(...) OVER (...)` |
|
|
157
|
+
| `ExprAvgOver` | `"avgOver"` | `AVG(...) OVER (...)` |
|
|
158
|
+
| `ExprMaxOver` | `"maxOver"` | `MAX(...) OVER (...)` |
|
|
159
|
+
| `ExprMinOver` | `"minOver"` | `MIN(...) OVER (...)` |
|
|
160
|
+
| `ExprFirstValue` | `"firstValue"` | `FIRST_VALUE(...) OVER (...)` |
|
|
161
|
+
| `ExprLastValue` | `"lastValue"` | `LAST_VALUE(...) OVER (...)` |
|
|
162
|
+
| `ExprNtile` | `"ntile"` | `NTILE(n) OVER (...)` |
|
|
163
|
+
|
|
164
|
+
## 조건부/변환 인터페이스
|
|
165
|
+
|
|
166
|
+
| 인터페이스 | `type` 값 | 설명 |
|
|
167
|
+
|------------|-----------|------|
|
|
168
|
+
| `ExprIf` | `"if"` | `IF(cond, then, else)`. `condition: WhereExpr`, `then: Expr`, `else_: Expr` |
|
|
169
|
+
| `ExprSwitch` | `"switch"` | `CASE WHEN`. `cases: { condition, then }[]`, `default?: Expr` |
|
|
170
|
+
| `ExprCoalesce` | `"coalesce"` | `COALESCE(...)`. `args: Expr[]` |
|
|
171
|
+
| `ExprNullIf` | `"nullIf"` | `NULLIF(source, target)` |
|
|
172
|
+
| `ExprCast` | `"cast"` | `CAST(source AS type)`. `source: Expr`, `dataType: DataType` |
|
|
173
|
+
| `ExprToDateOnly` | `"toDateOnly"` | DateTime → DateOnly 변환 |
|
|
174
|
+
| `ExprToDateTime` | `"toDateTime"` | DateOnly → DateTime 변환 |
|
|
175
|
+
| `ExprSubquery` | `"subquery"` | 단일 값 반환 서브쿼리. `dataType: ColumnPrimitiveStr`, `query: SelectQueryDef` |
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# parseQueryResult
|
|
2
|
+
|
|
3
|
+
DB 쿼리 원시 결과(`Record<string, unknown>[]`)를 `ResultMeta`를 이용해 타입 변환하고, JOIN 결과를 중첩 TypeScript 객체로 재구성한다. `Queryable.execute()` 내부에서 자동 호출되며, 직접 호출은 커스텀 executor 구현 시에만 필요하다.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
export async function parseQueryResult<TRecord>(
|
|
7
|
+
rawResults: Record<string, unknown>[],
|
|
8
|
+
meta: ResultMeta,
|
|
9
|
+
): Promise<TRecord[] | undefined>;
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Parameters
|
|
13
|
+
|
|
14
|
+
| Param | Type | Description |
|
|
15
|
+
|-------|------|-------------|
|
|
16
|
+
| `rawResults` | `Record<string, unknown>[]` | DB에서 반환된 플랫 원시 결과 배열 |
|
|
17
|
+
| `meta` | `ResultMeta` | 컬럼 타입 정보 및 JOIN 구조 정보 |
|
|
18
|
+
|
|
19
|
+
## Returns
|
|
20
|
+
|
|
21
|
+
`Promise<TRecord[] | undefined>` — 타입 변환 및 중첩 구조로 재구성된 결과 배열. 입력이 비어 있거나 파싱 후 모든 레코드가 빈 객체이면 `undefined` 반환.
|
|
22
|
+
|
|
23
|
+
## 동작
|
|
24
|
+
|
|
25
|
+
1. **단순 타입 파싱** (JOIN 없는 경우): `meta.columns`에 따라 각 컬럼 값을 TypeScript 타입으로 변환한다. 예를 들어 `"1"` → `1`, `"2026-01-07T10:00:00.000Z"` → `DateTime`.
|
|
26
|
+
2. **JOIN 결과 중첩** (JOIN 있는 경우): `meta.joins`에 따라 `"posts.id"` 등 점 구분 플랫 키를 중첩 객체/배열로 그룹핑한다. `isSingle: false`이면 배열, `isSingle: true`이면 단일 객체로 조합한다.
|
|
27
|
+
3. **비동기 양보**: 100개 레코드마다 이벤트 루프에 양보하여 대규모 결과 처리 시 UI/다른 작업을 차단하지 않는다.
|
|
28
|
+
|
|
29
|
+
## 주의사항
|
|
30
|
+
|
|
31
|
+
- `meta` 없이는 이 함수를 호출할 필요가 없다 (입력 = 출력).
|
|
32
|
+
- 브라우저/Node.js 양쪽에서 동작한다 (`setImmediate` 또는 `setTimeout` 폴백).
|
|
33
|
+
- 타입 파싱 실패(숫자 변환 실패 등) 시 에러를 던진다.
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// 단순 타입 파싱
|
|
39
|
+
const raw = [{ id: "1", createdAt: "2026-01-07T10:00:00.000Z" }];
|
|
40
|
+
const meta: ResultMeta = {
|
|
41
|
+
columns: { id: "number", createdAt: "DateTime" },
|
|
42
|
+
joins: {},
|
|
43
|
+
};
|
|
44
|
+
const result = await parseQueryResult(raw, meta);
|
|
45
|
+
// [{ id: 1, createdAt: DateTime(...) }]
|
|
46
|
+
|
|
47
|
+
// JOIN 결과 중첩
|
|
48
|
+
const raw = [
|
|
49
|
+
{ id: 1, name: "User1", "posts.id": 10, "posts.title": "Post1" },
|
|
50
|
+
{ id: 1, name: "User1", "posts.id": 11, "posts.title": "Post2" },
|
|
51
|
+
];
|
|
52
|
+
const meta: ResultMeta = {
|
|
53
|
+
columns: { id: "number", name: "string", "posts.id": "number", "posts.title": "string" },
|
|
54
|
+
joins: { posts: { isSingle: false } },
|
|
55
|
+
};
|
|
56
|
+
const result = await parseQueryResult(raw, meta);
|
|
57
|
+
// [{ id: 1, name: "User1", posts: [{ id: 10, title: "Post1" }, { id: 11, title: "Post2" }] }]
|
|
58
|
+
```
|