@simplysm/orm-common 14.0.51 → 14.0.52
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/exec/queryable.d.ts +1 -1
- package/dist/exec/queryable.d.ts.map +1 -1
- package/dist/exec/queryable.js +8 -2
- package/dist/exec/queryable.js.map +1 -1
- package/package.json +3 -4
- package/src/exec/queryable.ts +10 -3
- package/README.md +0 -136
- package/docs/core/db-context.md +0 -208
- package/docs/core/db-transaction-error.md +0 -64
- package/docs/expression/expr-unit.md +0 -62
- package/docs/expression/expr.md +0 -198
- package/docs/models/migration.md +0 -37
- package/docs/query-builder/create-query-builder.md +0 -80
- package/docs/queryable-executable/executable.md +0 -54
- package/docs/queryable-executable/parse-search-query.md +0 -75
- package/docs/queryable-executable/queryable.md +0 -238
- package/docs/schema-builders/column-builder.md +0 -63
- package/docs/schema-builders/foreign-key-builder.md +0 -137
- package/docs/schema-builders/index-builder.md +0 -54
- package/docs/schema-builders/procedure.md +0 -67
- package/docs/schema-builders/table.md +0 -94
- package/docs/schema-builders/view.md +0 -71
- package/docs/types/data-type.md +0 -146
- package/docs/types/dialect.md +0 -151
- package/docs/types/expr.md +0 -175
- package/docs/types/parse-query-result.md +0 -58
- package/docs/types/query-def.md +0 -224
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# Executable
|
|
2
|
-
|
|
3
|
-
Stored Procedure 실행 래퍼 클래스. `DbContext.executable()`로 등록된 프로퍼티를 호출하면 반환된다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
export class Executable<TParams extends ColumnBuilderRecord, TReturns extends ColumnBuilderRecord> {
|
|
7
|
-
constructor(
|
|
8
|
-
db: DbContextBase,
|
|
9
|
-
builder: ProcedureBuilder<TParams, TReturns>,
|
|
10
|
-
);
|
|
11
|
-
}
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Members
|
|
15
|
-
|
|
16
|
-
| Member | Kind | Type | Description |
|
|
17
|
-
|--------|------|------|-------------|
|
|
18
|
-
| `execute(params)` | method | `Promise<InferColumnExprs<TReturns>[][]>` | 프로시저 실행. 결과는 결과 셋 배열의 배열 |
|
|
19
|
-
| `getExecProcQueryDef(params?)` | method | `ExecProcQueryDef` | 실행 없이 QueryDef만 반환 |
|
|
20
|
-
|
|
21
|
-
## Related Types
|
|
22
|
-
|
|
23
|
-
### `executable` (팩토리 함수)
|
|
24
|
-
|
|
25
|
-
`DbContext.executable()`이 내부적으로 사용하는 팩토리 함수.
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
export function executable<
|
|
29
|
-
TParams extends ColumnBuilderRecord,
|
|
30
|
-
TReturns extends ColumnBuilderRecord,
|
|
31
|
-
>(
|
|
32
|
-
db: DbContextBase,
|
|
33
|
-
builder: ProcedureBuilder<TParams, TReturns>,
|
|
34
|
-
): () => Executable<TParams, TReturns>;
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Usage
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
// 프로시저 정의
|
|
41
|
-
const GetUserById = Procedure("GetUserById")
|
|
42
|
-
.database("mydb")
|
|
43
|
-
.params((c) => ({ userId: c.bigint() }))
|
|
44
|
-
.returns((c) => ({ id: c.bigint(), name: c.varchar(100) }))
|
|
45
|
-
.body("SELECT id, name FROM User WHERE id = userId");
|
|
46
|
-
|
|
47
|
-
// DbContext에 등록
|
|
48
|
-
class MyDb extends DbContext {
|
|
49
|
-
getUserById = this.executable(GetUserById);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// 실행
|
|
53
|
-
const [[user]] = await db.getUserById().execute({ userId: 1n });
|
|
54
|
-
```
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# parseSearchQuery
|
|
2
|
-
|
|
3
|
-
사용자 입력 검색 텍스트를 파싱하여 SQL LIKE 패턴으로 변환한다. `Queryable.search()`가 내부적으로 사용한다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
export function parseSearchQuery(searchText: string): ParsedSearchQuery;
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
## Parameters
|
|
10
|
-
|
|
11
|
-
| Param | Type | Description |
|
|
12
|
-
|-------|------|-------------|
|
|
13
|
-
| `searchText` | `string` | 검색 쿼리 문자열 |
|
|
14
|
-
|
|
15
|
-
## Returns
|
|
16
|
-
|
|
17
|
-
`ParsedSearchQuery` — 파싱된 검색 쿼리 객체
|
|
18
|
-
|
|
19
|
-
## Related Types
|
|
20
|
-
|
|
21
|
-
### `ParsedSearchQuery`
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
export interface ParsedSearchQuery {
|
|
25
|
-
/** 일반 검색어 (OR 조건) - LIKE 패턴 */
|
|
26
|
-
or: string[];
|
|
27
|
-
/** 필수 검색어 (AND 조건, + 접두사 또는 따옴표) - LIKE 패턴 */
|
|
28
|
-
must: string[];
|
|
29
|
-
/** 제외 검색어 (NOT 조건, - 접두사) - LIKE 패턴 */
|
|
30
|
-
not: string[];
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## 검색 구문
|
|
35
|
-
|
|
36
|
-
| 구문 | 의미 | 예시 |
|
|
37
|
-
|------|------|------|
|
|
38
|
-
| `term1 term2` | OR (하나 이상 일치) | `apple banana` |
|
|
39
|
-
| `+term` | 필수 포함 (AND) | `+apple +banana` |
|
|
40
|
-
| `-term` | 제외 (NOT) | `apple -banana` |
|
|
41
|
-
| `"exact phrase"` | 정확한 구문 일치 (필수) | `"맛있는 과일"` |
|
|
42
|
-
| `*` | 와일드카드 | `app*` → `app%` |
|
|
43
|
-
|
|
44
|
-
## 이스케이프 시퀀스
|
|
45
|
-
|
|
46
|
-
| 입력 | 의미 |
|
|
47
|
-
|------|------|
|
|
48
|
-
| `\\` | 리터럴 `\` |
|
|
49
|
-
| `\*` | 리터럴 `*` |
|
|
50
|
-
| `\%` | 리터럴 `%` |
|
|
51
|
-
| `\"` | 리터럴 `"` |
|
|
52
|
-
| `\+` | 리터럴 `+` |
|
|
53
|
-
| `\-` | 리터럴 `-` |
|
|
54
|
-
|
|
55
|
-
## Usage
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
parseSearchQuery('apple "delicious fruit" -banana +strawberry')
|
|
59
|
-
// {
|
|
60
|
-
// or: ["%apple%"],
|
|
61
|
-
// must: ["%delicious fruit%", "%strawberry%"],
|
|
62
|
-
// not: ["%banana%"]
|
|
63
|
-
// }
|
|
64
|
-
|
|
65
|
-
parseSearchQuery('app* test')
|
|
66
|
-
// {
|
|
67
|
-
// or: ["app%", "%test%"],
|
|
68
|
-
// must: [],
|
|
69
|
-
// not: []
|
|
70
|
-
// }
|
|
71
|
-
|
|
72
|
-
// Queryable.search()와 함께 사용
|
|
73
|
-
db.user()
|
|
74
|
-
.search((u) => [u.name, u.email], "John +admin -deleted")
|
|
75
|
-
```
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
# Queryable
|
|
2
|
-
|
|
3
|
-
SELECT/INSERT/UPDATE/DELETE/UPSERT 체인 빌더. 메서드 체이닝으로 쿼리를 구성하고 `execute()` 계열 메서드로 실행한다. `DbContext.queryable()`로 등록된 프로퍼티를 호출하면 반환된다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
export class Queryable<
|
|
7
|
-
TData extends DataRecord,
|
|
8
|
-
TFrom extends TableBuilder<any, any> | never,
|
|
9
|
-
> {
|
|
10
|
-
constructor(readonly meta: QueryableMeta<TData>);
|
|
11
|
-
}
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
`TFrom`은 CUD(INSERT/UPDATE/DELETE) 연산에 필요한 TableBuilder 타입이다. VIEW 기반이거나 `select()`로 변환된 경우 `never`가 되어 CUD 메서드를 사용할 수 없다.
|
|
15
|
-
|
|
16
|
-
## Members
|
|
17
|
-
|
|
18
|
-
### SELECT 옵션
|
|
19
|
-
|
|
20
|
-
| Member | Kind | Type | Description |
|
|
21
|
-
|--------|------|------|-------------|
|
|
22
|
-
| `select(fn)` | method | `Queryable<R, never>` | SELECT할 column 지정. fn이 반환한 구조로 결과 타입 변환 |
|
|
23
|
-
| `distinct()` | method | `Queryable<TData, never>` | DISTINCT 중복 제거 |
|
|
24
|
-
| `lock()` | method | `Queryable<TData, TFrom>` | FOR UPDATE 행 잠금 |
|
|
25
|
-
|
|
26
|
-
### 범위 제한
|
|
27
|
-
|
|
28
|
-
| Member | Kind | Type | Description |
|
|
29
|
-
|--------|------|------|-------------|
|
|
30
|
-
| `top(count)` | method | `Queryable<TData, TFrom>` | 상위 N개 행만 선택 |
|
|
31
|
-
| `limit(skip, take)` | method | `Queryable<TData, TFrom>` | LIMIT/OFFSET 페이지네이션. `orderBy()` 후에만 사용 가능 |
|
|
32
|
-
|
|
33
|
-
### 정렬
|
|
34
|
-
|
|
35
|
-
| Member | Kind | Type | Description |
|
|
36
|
-
|--------|------|------|-------------|
|
|
37
|
-
| `orderBy(fn, direction?)` | method | `Queryable<TData, TFrom>` | 정렬 조건 추가. 여러 번 호출 시 순서대로 적용. 함수 또는 chain path 문자열 |
|
|
38
|
-
|
|
39
|
-
### WHERE 조건
|
|
40
|
-
|
|
41
|
-
| Member | Kind | Type | Description |
|
|
42
|
-
|--------|------|------|-------------|
|
|
43
|
-
| `where(predicate)` | method | `Queryable<TData, TFrom>` | WHERE 조건 추가. 여러 번 호출 시 AND로 결합 |
|
|
44
|
-
| `search(fn, searchText)` | method | `Queryable<TData, TFrom>` | 텍스트 검색. `parseSearchQuery` 구문 지원 |
|
|
45
|
-
|
|
46
|
-
### GROUP BY / HAVING
|
|
47
|
-
|
|
48
|
-
| Member | Kind | Type | Description |
|
|
49
|
-
|--------|------|------|-------------|
|
|
50
|
-
| `groupBy(fn)` | method | `Queryable<TData, never>` | GROUP BY 절 추가 |
|
|
51
|
-
| `having(predicate)` | method | `Queryable<TData, never>` | HAVING 조건 추가 |
|
|
52
|
-
|
|
53
|
-
### JOIN
|
|
54
|
-
|
|
55
|
-
| Member | Kind | Type | Description |
|
|
56
|
-
|--------|------|------|-------------|
|
|
57
|
-
| `join(as, fn)` | method | `Queryable<TData & { [as]?: R[] }, TFrom>` | 1:N LEFT OUTER JOIN. 결과에 배열로 추가 |
|
|
58
|
-
| `joinSingle(as, fn)` | method | `Queryable<...>` | N:1 또는 1:1 LEFT OUTER JOIN. 결과에 단일 객체로 추가 |
|
|
59
|
-
| `include(fn)` | method | `Queryable<TData, TFrom>` | 테이블 관계 정의 기반 자동 JOIN. 중첩 경로 지원 (`p.user.company`) |
|
|
60
|
-
|
|
61
|
-
### 서브쿼리 / UNION
|
|
62
|
-
|
|
63
|
-
| Member | Kind | Type | Description |
|
|
64
|
-
|--------|------|------|-------------|
|
|
65
|
-
| `wrap()` | method | `Queryable<TData, never>` | 현재 쿼리를 서브쿼리로 래핑. `distinct()`/`groupBy()` 후 `count()` 사용 시 필요 |
|
|
66
|
-
| `static union(...queries)` | static | `Queryable<TData, never>` | 여러 Queryable을 UNION으로 결합 (중복 제거). 최소 2개 필요 |
|
|
67
|
-
|
|
68
|
-
### 재귀 CTE
|
|
69
|
-
|
|
70
|
-
| Member | Kind | Type | Description |
|
|
71
|
-
|--------|------|------|-------------|
|
|
72
|
-
| `recursive(fn)` | method | `Queryable<TData, never>` | WITH RECURSIVE CTE 생성. 계층 구조 쿼리에 사용 |
|
|
73
|
-
|
|
74
|
-
### SELECT 실행
|
|
75
|
-
|
|
76
|
-
| Member | Kind | Type | Description |
|
|
77
|
-
|--------|------|------|-------------|
|
|
78
|
-
| `execute()` | method | `Promise<TData[]>` | SELECT 실행, 결과 배열 반환 |
|
|
79
|
-
| `single()` | method | `Promise<TData \| undefined>` | 단일 결과 반환. 2개 이상이면 에러 |
|
|
80
|
-
| `first()` | method | `Promise<TData \| undefined>` | 첫 번째 결과만 반환 |
|
|
81
|
-
| `count(fn?)` | method | `Promise<number>` | 행 수 반환. `distinct()`/`groupBy()` 후에는 `wrap()` 필요 |
|
|
82
|
-
| `exists()` | method | `Promise<boolean>` | 조건에 일치하는 데이터 존재 여부 |
|
|
83
|
-
|
|
84
|
-
### INSERT
|
|
85
|
-
|
|
86
|
-
| Member | Kind | Type | Description |
|
|
87
|
-
|--------|------|------|-------------|
|
|
88
|
-
| `insert(records)` | method | `Promise<void>` | INSERT 실행. MSSQL 1000행 제한으로 자동 청크 분할 |
|
|
89
|
-
| `insert(records, outputColumns)` | method | `Promise<Pick<...>[]>` | INSERT 후 지정한 column 값 반환 |
|
|
90
|
-
| `insertIfNotExists(record)` | method | `Promise<void>` | WHERE 조건에 일치하는 데이터가 없을 때만 INSERT |
|
|
91
|
-
| `insertInto(targetTable)` | method | `Promise<void>` | INSERT INTO ... SELECT |
|
|
92
|
-
|
|
93
|
-
### UPDATE
|
|
94
|
-
|
|
95
|
-
| Member | Kind | Type | Description |
|
|
96
|
-
|--------|------|------|-------------|
|
|
97
|
-
| `update(recordFn)` | method | `Promise<void>` | UPDATE 실행 |
|
|
98
|
-
| `update(recordFn, outputColumns)` | method | `Promise<Pick<...>[]>` | UPDATE 후 지정한 column 값 반환 |
|
|
99
|
-
|
|
100
|
-
### DELETE
|
|
101
|
-
|
|
102
|
-
| Member | Kind | Type | Description |
|
|
103
|
-
|--------|------|------|-------------|
|
|
104
|
-
| `delete()` | method | `Promise<void>` | DELETE 실행 |
|
|
105
|
-
| `delete(outputColumns)` | method | `Promise<Pick<...>[]>` | DELETE 후 지정한 column 값 반환 |
|
|
106
|
-
|
|
107
|
-
### UPSERT
|
|
108
|
-
|
|
109
|
-
| Member | Kind | Type | Description |
|
|
110
|
-
|--------|------|------|-------------|
|
|
111
|
-
| `upsert(updateFn)` | method | `Promise<void>` | WHERE 일치 시 UPDATE, 없으면 INSERT (updateFn이 두 작업에 동일 적용) |
|
|
112
|
-
| `upsert(updateFn, insertFn)` | method | `Promise<void>` | UPDATE/INSERT 각각 다른 데이터로 UPSERT |
|
|
113
|
-
| `upsert(updateFn, outputColumns)` | method | `Promise<Pick<...>[]>` | UPSERT 후 지정한 column 값 반환 |
|
|
114
|
-
|
|
115
|
-
### QueryDef 생성기
|
|
116
|
-
|
|
117
|
-
실행 없이 `QueryDef`만 반환. 테스트나 수동 실행에 사용.
|
|
118
|
-
|
|
119
|
-
| Member | Kind | Type | Description |
|
|
120
|
-
|--------|------|------|-------------|
|
|
121
|
-
| `getSelectQueryDef()` | method | `SelectQueryDef` | SELECT QueryDef 생성 |
|
|
122
|
-
| `getResultMeta(outputColumns?)` | method | `ResultMeta` | SELECT 결과 변환용 메타데이터 생성 |
|
|
123
|
-
| `getInsertQueryDef(records, outputColumns?)` | method | `InsertQueryDef` | INSERT QueryDef 생성 |
|
|
124
|
-
| `getInsertIfNotExistsQueryDef(record)` | method | `InsertIfNotExistsQueryDef` | INSERT IF NOT EXISTS QueryDef |
|
|
125
|
-
| `getInsertIntoQueryDef(targetTable)` | method | `InsertIntoQueryDef` | INSERT INTO QueryDef |
|
|
126
|
-
| `getUpdateQueryDef(recordFn, outputColumns?)` | method | `UpdateQueryDef` | UPDATE QueryDef |
|
|
127
|
-
| `getDeleteQueryDef(outputColumns?)` | method | `DeleteQueryDef` | DELETE QueryDef |
|
|
128
|
-
| `getUpsertQueryDef(updateFn, insertFn, outputColumns?)` | method | `UpsertQueryDef` | UPSERT QueryDef |
|
|
129
|
-
| `switchFk(enabled)` | method | `Promise<void>` | FK 제약조건 활성화/비활성화 |
|
|
130
|
-
|
|
131
|
-
## Related Types
|
|
132
|
-
|
|
133
|
-
### `queryable` (팩토리 함수)
|
|
134
|
-
|
|
135
|
-
`DbContext.queryable()`이 내부적으로 사용하는 팩토리 함수. 직접 호출할 일은 드물다.
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
export function queryable<TBuilder extends TableBuilder<any, any> | ViewBuilder<any, any, any>>(
|
|
139
|
-
db: DbContextBase,
|
|
140
|
-
tableOrView: TBuilder,
|
|
141
|
-
as?: string,
|
|
142
|
-
): () => Queryable<TBuilder["$inferSelect"], ...>;
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### `getMatchedPrimaryKeys`
|
|
146
|
-
|
|
147
|
-
FK column 배열과 대상 테이블의 PK를 매칭하여 PK column 이름 배열을 반환한다.
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
export function getMatchedPrimaryKeys(
|
|
151
|
-
fkCols: string[],
|
|
152
|
-
targetTable: TableBuilder<any, any>,
|
|
153
|
-
): string[];
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
### `QueryableRecord<TData>`
|
|
157
|
-
|
|
158
|
-
Queryable 콜백 내부에서 column을 참조할 때 사용하는 프록시 레코드 타입. 각 필드가 `ExprUnit`으로 래핑된다.
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
export type QueryableRecord<TData extends DataRecord> = {
|
|
162
|
-
[K in keyof TData]: TData[K] extends ColumnPrimitive
|
|
163
|
-
? ExprUnit<TData[K]>
|
|
164
|
-
: ...;
|
|
165
|
-
};
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
### `QueryableWriteRecord<TData>`
|
|
169
|
-
|
|
170
|
-
UPDATE/UPSERT에서 레코드를 작성할 때 사용하는 타입. 각 필드가 `ExprInput<T>`(ExprUnit 또는 리터럴 값)로 받는다.
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
export type QueryableWriteRecord<TData> = {
|
|
174
|
-
[K in keyof TData]: TData[K] extends ColumnPrimitive ? ExprInput<TData[K]> : never;
|
|
175
|
-
};
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### `UnwrapQueryableRecord<R>`
|
|
179
|
-
|
|
180
|
-
`select(fn)` 반환 타입에서 `ExprUnit<T>`를 `T`로 언래핑하여 실제 데이터 타입을 추론한다.
|
|
181
|
-
|
|
182
|
-
### `PathProxy<TObject>`
|
|
183
|
-
|
|
184
|
-
`include(fn)` 에서 관계 경로를 타입 안전하게 지정하기 위한 프록시 타입. `ColumnPrimitive` 필드는 접근 불가.
|
|
185
|
-
|
|
186
|
-
```typescript
|
|
187
|
-
export type PathProxy<TObject> = {
|
|
188
|
-
[K in keyof TObject as TObject[K] extends ColumnPrimitive ? never : K]-?: PathProxy<...>;
|
|
189
|
-
};
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
## Usage
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
// SELECT
|
|
196
|
-
const users = await db.user()
|
|
197
|
-
.where((u) => [expr.eq(u.isActive, true), expr.gt(u.age, 18)])
|
|
198
|
-
.select((u) => ({ id: u.id, fullName: expr.concat(u.name, " (", u.email, ")") }))
|
|
199
|
-
.include((i) => i.company)
|
|
200
|
-
.orderBy((u) => u.name)
|
|
201
|
-
.limit(0, 20)
|
|
202
|
-
.execute();
|
|
203
|
-
|
|
204
|
-
// INSERT
|
|
205
|
-
await db.user().insert([{ name: "Alice", createdAt: DateTime.now() }]);
|
|
206
|
-
|
|
207
|
-
// INSERT with output
|
|
208
|
-
const [inserted] = await db.user().insert([{ name: "Bob" }], ["id"]);
|
|
209
|
-
|
|
210
|
-
// UPDATE (column 참조)
|
|
211
|
-
await db.product()
|
|
212
|
-
.update((p) => ({
|
|
213
|
-
price: expr.mul(p.price, expr.val("number", 1.1)),
|
|
214
|
-
}));
|
|
215
|
-
|
|
216
|
-
// UPSERT
|
|
217
|
-
await db.user()
|
|
218
|
-
.where((u) => [expr.eq(u.email, "test@test.com")])
|
|
219
|
-
.upsert(
|
|
220
|
-
() => ({ loginCount: expr.val("number", 1) }),
|
|
221
|
-
(update) => ({ ...update, email: expr.val("string", "test@test.com") }),
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
// UNION
|
|
225
|
-
const combined = Queryable.union(
|
|
226
|
-
db.user().where((u) => [expr.eq(u.type, "admin")]),
|
|
227
|
-
db.user().where((u) => [expr.eq(u.type, "manager")]),
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
// 재귀 CTE (계층 구조)
|
|
231
|
-
const tree = await db.employee()
|
|
232
|
-
.where((e) => [expr.null(e.managerId)])
|
|
233
|
-
.recursive((cte) =>
|
|
234
|
-
cte.from(Employee)
|
|
235
|
-
.where((e) => [expr.eq(e.managerId, e.self[0].id)])
|
|
236
|
-
)
|
|
237
|
-
.execute();
|
|
238
|
-
```
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# ColumnBuilder
|
|
2
|
-
|
|
3
|
-
Column 타입, nullable, autoIncrement, default, description을 정의하는 빌더. `TableBuilder.columns()` 및 `ProcedureBuilder.params()`/`returns()` 콜백에서 `createColumnFactory()`가 반환하는 팩토리 객체를 통해 생성된다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
export class ColumnBuilder<TValue extends ColumnPrimitive, TMeta extends ColumnMeta> {
|
|
7
|
-
constructor(readonly meta: TMeta);
|
|
8
|
-
}
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Members
|
|
12
|
-
|
|
13
|
-
| Member | Kind | Type | Description |
|
|
14
|
-
|--------|------|------|-------------|
|
|
15
|
-
| `meta` | property | `TMeta` | Column 메타데이터 |
|
|
16
|
-
| `autoIncrement()` | method | `ColumnBuilder<TValue, ...>` | AUTO_INCREMENT 설정. INSERT 타입에서 optional |
|
|
17
|
-
| `nullable()` | method | `ColumnBuilder<TValue \| undefined, ...>` | NULL 허용. 값 타입에 `undefined` 추가 |
|
|
18
|
-
| `default(value)` | method | `ColumnBuilder<TValue, ...>` | 기본값 설정. INSERT 타입에서 optional |
|
|
19
|
-
| `description(desc)` | method | `ColumnBuilder<TValue, ...>` | Column 설명 (DDL Comment) |
|
|
20
|
-
|
|
21
|
-
## Related Types
|
|
22
|
-
|
|
23
|
-
### `createColumnFactory`
|
|
24
|
-
|
|
25
|
-
`TableBuilder.columns()`, `ProcedureBuilder.params()`/`returns()` 콜백에서 자동으로 제공되는 팩토리.
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
export function createColumnFactory(): { ... };
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
#### 지원 Column 타입
|
|
32
|
-
|
|
33
|
-
| 메서드 | SQL 타입 | TypeScript 타입 | 비고 |
|
|
34
|
-
|--------|----------|-----------------|------|
|
|
35
|
-
| `int()` | INT | `number` | 4바이트 |
|
|
36
|
-
| `bigint()` | BIGINT | `number` | 8바이트 |
|
|
37
|
-
| `float()` | FLOAT | `number` | 4바이트 단정밀도 |
|
|
38
|
-
| `double()` | DOUBLE | `number` | 8바이트 배정밀도 |
|
|
39
|
-
| `decimal(precision, scale?)` | DECIMAL(p,s) | `number` | 고정 소수점 |
|
|
40
|
-
| `varchar(length)` | VARCHAR(n) | `string` | 가변 길이 |
|
|
41
|
-
| `char(length)` | CHAR(n) | `string` | 고정 길이 |
|
|
42
|
-
| `text()` | TEXT/LONGTEXT | `string` | 대용량 |
|
|
43
|
-
| `binary()` | LONGBLOB/VARBINARY(MAX)/BYTEA | `Bytes` | 바이너리 |
|
|
44
|
-
| `boolean()` | TINYINT(1)/BIT/BOOLEAN | `boolean` | DBMS별 다름 |
|
|
45
|
-
| `datetime()` | DATETIME | `DateTime` | |
|
|
46
|
-
| `date()` | DATE | `DateOnly` | |
|
|
47
|
-
| `time()` | TIME | `Time` | |
|
|
48
|
-
| `uuid()` | BINARY(16)/UNIQUEIDENTIFIER/UUID | `Uuid` | |
|
|
49
|
-
|
|
50
|
-
## Usage
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
Table("User")
|
|
54
|
-
.columns((c) => ({
|
|
55
|
-
id: c.bigint().autoIncrement(), // bigint, AUTO_INCREMENT, INSERT optional
|
|
56
|
-
name: c.varchar(100), // varchar(100), 필수
|
|
57
|
-
email: c.varchar(200).nullable(), // varchar(200), NULL 허용
|
|
58
|
-
status: c.varchar(20).default("active"), // varchar(20), 기본값 "active", INSERT optional
|
|
59
|
-
createdAt: c.datetime().description("생성일시"),
|
|
60
|
-
price: c.decimal(10, 2),
|
|
61
|
-
data: c.binary(),
|
|
62
|
-
}));
|
|
63
|
-
```
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
# ForeignKeyBuilder
|
|
2
|
-
|
|
3
|
-
FK/역참조/논리적 관계 빌더들. `TableBuilder.relations()` 및 `ViewBuilder.relations()` 콜백에서 `createRelationFactory()`가 반환하는 팩토리 객체를 통해 생성된다.
|
|
4
|
-
|
|
5
|
-
**주의**: description/single 설정은 반드시 팩토리 함수의 `opts` 파라미터로 전달해야 한다. 메서드 체이닝(`.description()`, `.single()`)은 TypeScript 순환 참조(TS7022)를 유발하므로 제거되었다.
|
|
6
|
-
|
|
7
|
-
## 빌더 종류
|
|
8
|
-
|
|
9
|
-
| 클래스 | 관계 방향 | DB FK 생성 | 사용 가능 위치 |
|
|
10
|
-
|--------|-----------|------------|----------------|
|
|
11
|
-
| `ForeignKeyBuilder` | N:1 | O | Table |
|
|
12
|
-
| `ForeignKeyTargetBuilder` | 1:N 또는 1:1 | O (역참조) | Table |
|
|
13
|
-
| `RelationKeyBuilder` | N:1 | X | Table, View |
|
|
14
|
-
| `RelationKeyTargetBuilder` | 1:N 또는 1:1 | X (역참조) | Table, View |
|
|
15
|
-
|
|
16
|
-
## ForeignKeyBuilder
|
|
17
|
-
|
|
18
|
-
```typescript
|
|
19
|
-
export class ForeignKeyBuilder<
|
|
20
|
-
TOwner extends TableBuilder<any, any>,
|
|
21
|
-
TTargetFn extends () => TableBuilder<any, any>,
|
|
22
|
-
> {
|
|
23
|
-
constructor(
|
|
24
|
-
readonly meta: {
|
|
25
|
-
ownerFn: () => TOwner;
|
|
26
|
-
columns: string[];
|
|
27
|
-
targetFn: TTargetFn;
|
|
28
|
-
description?: string;
|
|
29
|
-
},
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
팩토리 시그니처:
|
|
35
|
-
```typescript
|
|
36
|
-
r.foreignKey(columns, targetFn, opts?: { description?: string }): ForeignKeyBuilder<...>
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## ForeignKeyTargetBuilder
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
export class ForeignKeyTargetBuilder<
|
|
43
|
-
TTargetTableFn extends () => TableBuilder<any, any>,
|
|
44
|
-
TIsSingle extends boolean,
|
|
45
|
-
> {
|
|
46
|
-
constructor(
|
|
47
|
-
readonly meta: {
|
|
48
|
-
targetTableFn: TTargetTableFn;
|
|
49
|
-
relationName: string;
|
|
50
|
-
description?: string;
|
|
51
|
-
isSingle?: TIsSingle;
|
|
52
|
-
},
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
팩토리 시그니처 (오버로드):
|
|
58
|
-
```typescript
|
|
59
|
-
// 배열 관계 (1:N)
|
|
60
|
-
r.foreignKeyTarget(targetTableFn, relationName, opts?: { single?: false, description?: string }):
|
|
61
|
-
ForeignKeyTargetBuilder<..., false>
|
|
62
|
-
|
|
63
|
-
// 단일 관계 (1:1)
|
|
64
|
-
r.foreignKeyTarget(targetTableFn, relationName, opts: { single: true, description?: string }):
|
|
65
|
-
ForeignKeyTargetBuilder<..., true>
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## RelationKeyBuilder
|
|
69
|
-
|
|
70
|
-
`ForeignKeyBuilder`와 동일하지만 DB에 FK 제약조건을 생성하지 않는다. View에서도 사용 가능하다.
|
|
71
|
-
|
|
72
|
-
팩토리 시그니처:
|
|
73
|
-
```typescript
|
|
74
|
-
r.relationKey(columns, targetFn, opts?: { description?: string }): RelationKeyBuilder<...>
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
## RelationKeyTargetBuilder
|
|
78
|
-
|
|
79
|
-
`ForeignKeyTargetBuilder`와 동일하지만 DB에 FK를 생성하지 않는다.
|
|
80
|
-
|
|
81
|
-
팩토리 시그니처 (오버로드):
|
|
82
|
-
```typescript
|
|
83
|
-
r.relationKeyTarget(targetTableFn, relationName, opts?: { single?: false, description?: string })
|
|
84
|
-
r.relationKeyTarget(targetTableFn, relationName, opts: { single: true, description?: string })
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## Related Types
|
|
88
|
-
|
|
89
|
-
### `createRelationFactory`
|
|
90
|
-
|
|
91
|
-
```typescript
|
|
92
|
-
export function createRelationFactory<
|
|
93
|
-
TOwner extends TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
94
|
-
TColumnKey extends string,
|
|
95
|
-
>(ownerFn: () => TOwner): RelationFactory;
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
TableBuilder → FK + RelationKey 모두 사용 가능.
|
|
99
|
-
ViewBuilder → RelationKey만 사용 가능 (`foreignKey`/`foreignKeyTarget` 없음).
|
|
100
|
-
|
|
101
|
-
## Usage
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
const Post = Table("Post")
|
|
105
|
-
.columns((c) => ({
|
|
106
|
-
id: c.bigint().autoIncrement(),
|
|
107
|
-
authorId: c.bigint(),
|
|
108
|
-
title: c.varchar(200),
|
|
109
|
-
}))
|
|
110
|
-
.primaryKey("id")
|
|
111
|
-
.relations((r) => ({
|
|
112
|
-
// N:1: Post → User (DB FK 생성)
|
|
113
|
-
author: r.foreignKey(["authorId"], () => User, { description: "작성자" }),
|
|
114
|
-
}));
|
|
115
|
-
|
|
116
|
-
const User = Table("User")
|
|
117
|
-
.columns((c) => ({
|
|
118
|
-
id: c.bigint().autoIncrement(),
|
|
119
|
-
name: c.varchar(100),
|
|
120
|
-
}))
|
|
121
|
-
.primaryKey("id")
|
|
122
|
-
.relations((r) => ({
|
|
123
|
-
// 1:N 역참조 (배열)
|
|
124
|
-
posts: r.foreignKeyTarget(() => Post, "author"),
|
|
125
|
-
// 1:1 역참조 (단일 객체)
|
|
126
|
-
profile: r.foreignKeyTarget(() => Profile, "user", { single: true }),
|
|
127
|
-
// 설명 포함
|
|
128
|
-
comments: r.foreignKeyTarget(() => Comment, "user", { description: "댓글목록" }),
|
|
129
|
-
}));
|
|
130
|
-
|
|
131
|
-
// View에서는 relationKey만 사용
|
|
132
|
-
const UserSummary = View("UserSummary")
|
|
133
|
-
.query((db: MyDb) => db.user().select(...))
|
|
134
|
-
.relations((r) => ({
|
|
135
|
-
company: r.relationKey(["companyId"], () => Company, { description: "소속회사" }),
|
|
136
|
-
}));
|
|
137
|
-
```
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# IndexBuilder
|
|
2
|
-
|
|
3
|
-
Index 정의 빌더. `TableBuilder.indexes()` 콜백에서 `createIndexFactory()`가 반환하는 팩토리 객체를 통해 생성된다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
export class IndexBuilder<TKeys extends string[]> {
|
|
7
|
-
constructor(
|
|
8
|
-
readonly meta: {
|
|
9
|
-
columns: TKeys;
|
|
10
|
-
name?: string;
|
|
11
|
-
unique?: boolean;
|
|
12
|
-
orderBy?: { [K in keyof TKeys]: "ASC" | "DESC" };
|
|
13
|
-
description?: string;
|
|
14
|
-
},
|
|
15
|
-
);
|
|
16
|
-
}
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Members
|
|
20
|
-
|
|
21
|
-
| Member | Kind | Type | Description |
|
|
22
|
-
|--------|------|------|-------------|
|
|
23
|
-
| `meta` | property | `{ columns, name?, unique?, orderBy?, description? }` | Index 메타데이터 |
|
|
24
|
-
| `name(name)` | method | `IndexBuilder<TKeys>` | Index 이름 설정 |
|
|
25
|
-
| `unique()` | method | `IndexBuilder<TKeys>` | UNIQUE 인덱스 설정 |
|
|
26
|
-
| `orderBy(...orderBy)` | method | `IndexBuilder<TKeys>` | 각 컬럼별 정렬 순서 설정 |
|
|
27
|
-
| `description(desc)` | method | `IndexBuilder<TKeys>` | Index 설명 (DDL Comment) |
|
|
28
|
-
|
|
29
|
-
## Related Types
|
|
30
|
-
|
|
31
|
-
### `createIndexFactory`
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
export function createIndexFactory<TColumnKey extends string>(): {
|
|
35
|
-
index<TKeys extends TColumnKey[]>(...columns: [...TKeys]): IndexBuilder<TKeys>;
|
|
36
|
-
};
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Usage
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
Table("User")
|
|
43
|
-
.columns((c) => ({
|
|
44
|
-
id: c.bigint(),
|
|
45
|
-
email: c.varchar(200),
|
|
46
|
-
name: c.varchar(100),
|
|
47
|
-
createdAt: c.datetime(),
|
|
48
|
-
}))
|
|
49
|
-
.indexes((i) => [
|
|
50
|
-
i.index("email").unique(),
|
|
51
|
-
i.index("name", "createdAt").orderBy("ASC", "DESC"),
|
|
52
|
-
i.index("createdAt").name("IX_User_CreatedAt").orderBy("DESC"),
|
|
53
|
-
]);
|
|
54
|
-
```
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# Procedure
|
|
2
|
-
|
|
3
|
-
Fluent API로 Stored Procedure 스키마를 정의하는 빌더 팩토리 함수.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
export function Procedure(name: string): ProcedureBuilder<never, never>;
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
## Parameters
|
|
10
|
-
|
|
11
|
-
| Param | Type | Description |
|
|
12
|
-
|-------|------|-------------|
|
|
13
|
-
| `name` | `string` | Procedure 이름 |
|
|
14
|
-
|
|
15
|
-
## Returns
|
|
16
|
-
|
|
17
|
-
`ProcedureBuilder<never, never>` — 빈 ProcedureBuilder 인스턴스
|
|
18
|
-
|
|
19
|
-
## Related Types
|
|
20
|
-
|
|
21
|
-
### `ProcedureBuilder<TParams, TReturns>`
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
export class ProcedureBuilder<
|
|
25
|
-
TParams extends ColumnBuilderRecord,
|
|
26
|
-
TReturns extends ColumnBuilderRecord,
|
|
27
|
-
> {
|
|
28
|
-
readonly $params!: TParams;
|
|
29
|
-
readonly $returns!: TReturns;
|
|
30
|
-
}
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
#### 빌더 메서드
|
|
34
|
-
|
|
35
|
-
| 메서드 | 설명 |
|
|
36
|
-
|--------|------|
|
|
37
|
-
| `description(desc)` | Procedure 설명 설정 (DDL Comment) |
|
|
38
|
-
| `database(db)` | Database 이름 설정 |
|
|
39
|
-
| `schema(schema)` | Schema 이름 설정 |
|
|
40
|
-
| `params(fn)` | 입력 파라미터 정의. `fn(c) => ({ userId: c.bigint(), ... })` |
|
|
41
|
-
| `returns(fn)` | 반환 컬럼 정의. `fn(c) => ({ id: c.bigint(), ... })` |
|
|
42
|
-
| `body(sql)` | Procedure 본문 SQL 설정 |
|
|
43
|
-
|
|
44
|
-
## Usage
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
const GetUserById = Procedure("GetUserById")
|
|
48
|
-
.database("mydb")
|
|
49
|
-
.params((c) => ({
|
|
50
|
-
userId: c.bigint(),
|
|
51
|
-
}))
|
|
52
|
-
.returns((c) => ({
|
|
53
|
-
id: c.bigint(),
|
|
54
|
-
name: c.varchar(100),
|
|
55
|
-
email: c.varchar(200).nullable(),
|
|
56
|
-
}))
|
|
57
|
-
.body("SELECT id, name, email FROM User WHERE id = userId");
|
|
58
|
-
// MSSQL: .body("SELECT id, name, email FROM [User] WHERE id = @userId")
|
|
59
|
-
|
|
60
|
-
// DbContext에 등록
|
|
61
|
-
class MainDb extends DbContext {
|
|
62
|
-
getUserById = this.executable(GetUserById);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// 실행
|
|
66
|
-
const [[user]] = await db.getUserById().execute({ userId: 1n });
|
|
67
|
-
```
|