@simplysm/sd-claude 14.0.98 → 14.0.100
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/claude/references/sd-simplysm14/README.md +16 -16
- package/claude/references/sd-simplysm14/apis/angular/README.md +81 -153
- package/claude/references/sd-simplysm14/apis/angular/controls.md +179 -205
- package/claude/references/sd-simplysm14/apis/angular/crud.md +71 -57
- package/claude/references/sd-simplysm14/apis/angular/directives.md +49 -109
- package/claude/references/sd-simplysm14/apis/angular/features.md +58 -86
- package/claude/references/sd-simplysm14/apis/angular/kanban.md +32 -40
- package/claude/references/sd-simplysm14/apis/angular/layout.md +38 -52
- package/claude/references/sd-simplysm14/apis/angular/overlay.md +86 -110
- package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +54 -86
- package/claude/references/sd-simplysm14/apis/angular/shared-data.md +82 -74
- package/claude/references/sd-simplysm14/apis/angular/sheet.md +56 -80
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +15 -15
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +21 -21
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +79 -53
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +9 -11
- package/claude/references/sd-simplysm14/apis/core-browser/README.md +15 -15
- package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +20 -20
- package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +18 -18
- package/claude/references/sd-simplysm14/apis/core-common/README.md +20 -49
- package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +66 -55
- package/claude/references/sd-simplysm14/apis/core-common/collection-ext.md +83 -56
- package/claude/references/sd-simplysm14/apis/core-common/errors.md +32 -21
- package/claude/references/sd-simplysm14/apis/core-common/obj.md +57 -39
- package/claude/references/sd-simplysm14/apis/core-common/serialization.md +36 -30
- package/claude/references/sd-simplysm14/apis/core-common/value-types.md +69 -41
- package/claude/references/sd-simplysm14/apis/core-node/README.md +4 -4
- package/claude/references/sd-simplysm14/apis/core-node/consola.md +15 -13
- package/claude/references/sd-simplysm14/apis/core-node/cpx.md +11 -7
- package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +8 -8
- package/claude/references/sd-simplysm14/apis/core-node/fsx.md +29 -20
- package/claude/references/sd-simplysm14/apis/core-node/pathx.md +14 -6
- package/claude/references/sd-simplysm14/apis/core-node/worker.md +3 -3
- package/claude/references/sd-simplysm14/apis/excel/README.md +3 -3
- package/claude/references/sd-simplysm14/apis/excel/cell.md +32 -32
- package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +23 -24
- package/claude/references/sd-simplysm14/apis/excel/style.md +24 -30
- package/claude/references/sd-simplysm14/apis/excel/utils.md +20 -23
- package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +60 -71
- package/claude/references/sd-simplysm14/apis/excel/wrapper.md +36 -36
- package/claude/references/sd-simplysm14/apis/lint/README.md +7 -9
- package/claude/references/sd-simplysm14/apis/lint/recommended.md +59 -37
- package/claude/references/sd-simplysm14/apis/lint/rules.md +81 -74
- package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -6
- package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +112 -78
- package/claude/references/sd-simplysm14/apis/orm-common/expr.md +131 -75
- package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +126 -82
- package/claude/references/sd-simplysm14/apis/orm-common/schema.md +170 -113
- package/claude/references/sd-simplysm14/apis/orm-common/types.md +102 -48
- package/claude/references/sd-simplysm14/apis/orm-node/README.md +12 -13
- package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +3 -3
- package/claude/references/sd-simplysm14/apis/sd-cli/README.md +5 -5
- package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +67 -65
- package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +130 -123
- package/claude/references/sd-simplysm14/apis/service-client/README.md +63 -63
- package/claude/references/sd-simplysm14/apis/service-client/orm.md +22 -22
- package/claude/references/sd-simplysm14/apis/service-client/transport.md +30 -26
- package/claude/references/sd-simplysm14/apis/service-common/README.md +8 -8
- package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +13 -6
- package/claude/references/sd-simplysm14/apis/service-common/protocol.md +1 -1
- package/claude/references/sd-simplysm14/apis/service-server/README.md +43 -47
- package/claude/references/sd-simplysm14/apis/service-server/built-in-services.md +35 -0
- package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +20 -19
- package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +23 -25
- package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +9 -9
- package/claude/references/sd-simplysm14/apis/storage/README.md +26 -26
- package/claude/references/sd-simplysm14/manuals/client-component.md +9 -1
- package/claude/references/sd-simplysm14/manuals/client-crud.md +1 -1
- package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -0
- package/claude/references/sd-simplysm14/manuals/client-service.md +1 -0
- package/claude/references/sd-simplysm14/manuals/client-shared-data.md +1 -0
- package/claude/references/sd-simplysm14/manuals/client-ssg.md +1 -0
- package/claude/sd-system-prompt.md +11 -26
- package/claude/skills/sd-docs/references/subagent-prompt.md +4 -3
- package/claude/skills/sd-spec/SKILL.md +87 -18
- package/claude/skills/sd-spec/references/format.md +2 -2
- package/package.json +1 -1
|
@@ -1,167 +1,224 @@
|
|
|
1
|
-
# @simplysm/orm-common —
|
|
1
|
+
# @simplysm/orm-common — schema
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
테이블·뷰·프로시저 스키마를 fluent 빌더로 정의하는 군. `Table()`/`View()`/`Procedure()` 팩토리가 빌더를 만들고, 각 빌더의 메서드는 새 인스턴스를 반환하는 불변 체이닝이다. 정의 결과는 `DbContext` 의 `queryable()`/`executable()` 에 등록해 쓰며, `$inferSelect`/`$inferInsert` 등의 phantom 필드로 컬럼·관계 타입이 추론된다. 컬럼/관계/인덱스는 각 빌더의 콜백 안에서 팩토리(`c`/`r`/`i`)로 만든다.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## TableBuilder / Table
|
|
6
6
|
|
|
7
7
|
```typescript
|
|
8
8
|
function Table<TName extends string>(name: TName): TableBuilder<TName, {}, {}>;
|
|
9
|
+
class TableBuilder<TName, TColumns, TRelations> {
|
|
10
|
+
readonly meta: { name; description?; database?; schema?; columns?; primaryKey?; relations?; indexes? };
|
|
11
|
+
readonly $inferSelect; // InferColumns & InferDeepRelations (전체 — 관계 포함)
|
|
12
|
+
readonly $inferColumns; // 컬럼만
|
|
13
|
+
readonly $inferInsert; // INSERT 입력 (autoIncrement 제외, nullable/default 는 optional)
|
|
14
|
+
readonly $inferUpdate; // UPDATE 입력 (모든 컬럼 optional)
|
|
15
|
+
description(desc: string): TableBuilder;
|
|
16
|
+
database(db: string): TableBuilder;
|
|
17
|
+
schema(schema: string): TableBuilder;
|
|
18
|
+
columns(fn: (c) => TNewColumnDefs): TableBuilder;
|
|
19
|
+
primaryKey(...columns: (keyof TColumns)[]): TableBuilder;
|
|
20
|
+
indexes(fn: (i) => IndexBuilder[]): TableBuilder;
|
|
21
|
+
relations(fn: (r) => TRelations): TableBuilder;
|
|
22
|
+
}
|
|
9
23
|
```
|
|
10
24
|
|
|
11
|
-
`Table(name)`
|
|
12
|
-
|
|
13
|
-
- `database(db)` —
|
|
14
|
-
- `schema(schema)` —
|
|
15
|
-
- `
|
|
16
|
-
- `
|
|
17
|
-
- `
|
|
18
|
-
- `
|
|
19
|
-
- `
|
|
20
|
-
|
|
21
|
-
타입 추론 필드(런타임 값 아님): `$inferSelect`(컬럼+관계), `$inferColumns`(컬럼만), `$inferInsert`(autoIncrement/nullable/default 는 optional), `$inferUpdate`(전부 optional). `Queryable` 의 결과/입력 타입이 여기서 파생된다.
|
|
25
|
+
- `Table(name)` — 테이블 빌더 시작점. `name` 은 실제 DB 테이블명이자 `$inferSelect` 의 phantom 타입 이름.
|
|
26
|
+
- `description(desc)` — 테이블 설명. DDL Comment 로 사용됨.
|
|
27
|
+
- `database(db)` — 테이블이 속한 데이터베이스명. 미지정 시 `DbContext` 의 database 옵션을 따름.
|
|
28
|
+
- `schema(schema)` — 스키마명. MSSQL(`dbo`)/PostgreSQL(`public`) 에서만 의미 있고 MySQL 은 무시.
|
|
29
|
+
- `columns(fn)` — `createColumnFactory()` 가 주입된 `c` 로 컬럼 레코드를 반환. 호출 시 `TColumns` 가 새 정의로 교체됨.
|
|
30
|
+
- `primaryKey(...columns)` — PK 컬럼명을 가변 인자로. 2개 이상 넘기면 복합 PK. 인자는 `columns()` 의 키로 타입 체크됨.
|
|
31
|
+
- `indexes(fn)` — `createIndexFactory()` 의 `i` 로 인덱스 배열을 반환.
|
|
32
|
+
- `relations(fn)` — `createRelationFactory()` 의 `r` 로 관계 레코드 반환. Table 은 `foreignKey`/`foreignKeyTarget`/`relationKey`/`relationKeyTarget` 모두 사용 가능.
|
|
33
|
+
- `meta` — 빌더가 누적한 정의 객체. DDL 생성·`queryable()` 이 읽음.
|
|
22
34
|
|
|
23
35
|
```typescript
|
|
24
36
|
const User = Table("User")
|
|
25
37
|
.database("mydb")
|
|
26
38
|
.columns((c) => ({
|
|
27
|
-
id: c.
|
|
39
|
+
id: c.bigint().autoIncrement(),
|
|
28
40
|
name: c.varchar(100),
|
|
29
41
|
email: c.varchar(200).nullable(),
|
|
30
|
-
|
|
31
|
-
companyId: c.int().nullable(),
|
|
42
|
+
status: c.varchar(20).default("active"),
|
|
32
43
|
}))
|
|
33
44
|
.primaryKey("id")
|
|
34
45
|
.indexes((i) => [i.index("email").unique()])
|
|
35
|
-
.relations((r) => ({
|
|
36
|
-
company: r.foreignKey(["companyId"], () => Company),
|
|
37
|
-
posts: r.foreignKeyTarget(() => Post, "user"),
|
|
38
|
-
}));
|
|
46
|
+
.relations((r) => ({ posts: r.foreignKeyTarget(() => Post, "author") }));
|
|
39
47
|
```
|
|
40
48
|
|
|
41
|
-
##
|
|
49
|
+
## ViewBuilder / View
|
|
42
50
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
- `binary()` — 바이너리(MySQL LONGBLOB / MSSQL VARBINARY(MAX) / PostgreSQL BYTEA). 값 타입은 `Bytes`.
|
|
56
|
-
- `boolean()` — 불리언(MySQL TINYINT(1) / MSSQL BIT / PostgreSQL BOOLEAN).
|
|
57
|
-
- `datetime()` — 날짜+시간. 값 타입 `DateTime`.
|
|
58
|
-
- `date()` — 날짜만. 값 타입 `DateOnly`.
|
|
59
|
-
- `time()` — 시간만. 값 타입 `Time`.
|
|
60
|
-
- `uuid()` — UUID(MySQL BINARY(16) / MSSQL UNIQUEIDENTIFIER / PostgreSQL UUID). 값 타입 `Uuid`.
|
|
51
|
+
```typescript
|
|
52
|
+
function View(name: string): ViewBuilder;
|
|
53
|
+
class ViewBuilder<TDbContext, TData, TRelations> {
|
|
54
|
+
readonly meta: { name; description?; database?; schema?; viewFn?; relations? };
|
|
55
|
+
readonly $inferSelect; // TData
|
|
56
|
+
description(desc: string): ViewBuilder;
|
|
57
|
+
database(db: string): ViewBuilder;
|
|
58
|
+
schema(schema: string): ViewBuilder;
|
|
59
|
+
query(viewFn: (db: TDb) => Queryable<TViewData, any>): ViewBuilder;
|
|
60
|
+
relations(fn: (r) => TRelations): ViewBuilder;
|
|
61
|
+
}
|
|
62
|
+
```
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
- `View(name)` — 뷰 빌더 시작점.
|
|
65
|
+
- `description`/`database`/`schema` — Table 과 동일 의미.
|
|
66
|
+
- `query(viewFn)` — 뷰의 데이터 소스인 SELECT Queryable 을 `db` 를 받아 반환. `viewFn` 이 `TData`(뷰 행 타입)를 결정.
|
|
67
|
+
- `relations(fn)` — 뷰의 관계 정의. 뷰는 `relationKey`/`relationKeyTarget` 만 사용 가능(DB FK 미생성). 반환 타입에 관계가 합쳐져 `$inferSelect` 에 반영됨.
|
|
63
68
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
```typescript
|
|
70
|
+
const ActiveUsers = View("ActiveUsers")
|
|
71
|
+
.database("mydb")
|
|
72
|
+
.query((db: MyDb) =>
|
|
73
|
+
db.user().where((u) => [expr.eq(u.status, "active")]).select((u) => ({ id: u.id, name: u.name })),
|
|
74
|
+
);
|
|
75
|
+
```
|
|
68
76
|
|
|
69
|
-
##
|
|
77
|
+
## ProcedureBuilder / Procedure
|
|
70
78
|
|
|
71
|
-
|
|
79
|
+
```typescript
|
|
80
|
+
function Procedure(name: string): ProcedureBuilder<never, never>;
|
|
81
|
+
class ProcedureBuilder<TParams, TReturns> {
|
|
82
|
+
readonly meta: { name; description?; database?; schema?; params?; returns?; query? };
|
|
83
|
+
readonly $params; readonly $returns;
|
|
84
|
+
description(desc: string): ProcedureBuilder;
|
|
85
|
+
database(db: string): ProcedureBuilder;
|
|
86
|
+
schema(schema: string): ProcedureBuilder;
|
|
87
|
+
params(fn: (c) => TParams): ProcedureBuilder;
|
|
88
|
+
returns(fn: (c) => TReturns): ProcedureBuilder;
|
|
89
|
+
body(sql: string): ProcedureBuilder;
|
|
90
|
+
}
|
|
91
|
+
```
|
|
72
92
|
|
|
73
|
-
- `
|
|
74
|
-
- `
|
|
75
|
-
- `
|
|
76
|
-
- `
|
|
77
|
-
- `description(desc)` — 인덱스 코멘트.
|
|
93
|
+
- `Procedure(name)` — 저장 프로시저 빌더 시작점. `executable()` 에 등록.
|
|
94
|
+
- `params(fn)` — 입력 파라미터를 컬럼 팩토리로 정의. `Executable.execute()` 의 인자 타입이 됨.
|
|
95
|
+
- `returns(fn)` — 반환 결과셋 컬럼을 정의. 결과 행 타입이 됨.
|
|
96
|
+
- `body(sql)` — 프로시저 본문 SQL. DBMS 별 파라미터 구문 차이 주의(MySQL: `userId`, MSSQL: `@userId`, PostgreSQL: `RETURN QUERY` 필요).
|
|
78
97
|
|
|
79
98
|
```typescript
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
99
|
+
const GetUserById = Procedure("GetUserById")
|
|
100
|
+
.database("mydb")
|
|
101
|
+
.params((c) => ({ userId: c.bigint() }))
|
|
102
|
+
.returns((c) => ({ id: c.bigint(), name: c.varchar(100) }))
|
|
103
|
+
.body("SELECT id, name FROM User WHERE id = userId");
|
|
84
104
|
```
|
|
85
105
|
|
|
86
|
-
##
|
|
106
|
+
## createColumnFactory (컬럼 팩토리 `c`)
|
|
107
|
+
|
|
108
|
+
`columns()`/`params()`/`returns()` 콜백에 주입되는 `c`. 각 메서드가 `ColumnBuilder` 를 반환하며 SQL 타입과 TS 원시 타입을 함께 고정한다.
|
|
109
|
+
|
|
110
|
+
| 메서드 | TS 타입 | SQL 매핑 |
|
|
111
|
+
| ------ | ------- | -------- |
|
|
112
|
+
| `c.int()` | number | INT (4바이트) |
|
|
113
|
+
| `c.bigint()` | number | BIGINT (8바이트) |
|
|
114
|
+
| `c.float()` | number | FLOAT/REAL (4바이트) |
|
|
115
|
+
| `c.double()` | number | DOUBLE (8바이트) |
|
|
116
|
+
| `c.decimal(precision, scale?)` | number | DECIMAL(precision, scale) — 고정 소수점 |
|
|
117
|
+
| `c.varchar(length)` | string | VARCHAR(length) — 가변 길이 |
|
|
118
|
+
| `c.char(length)` | string | CHAR(length) — 고정 길이 |
|
|
119
|
+
| `c.text()` | string | TEXT/LONGTEXT — 대용량 |
|
|
120
|
+
| `c.binary()` | Bytes | LONGBLOB / VARBINARY(MAX) / BYTEA |
|
|
121
|
+
| `c.boolean()` | boolean | TINYINT(1) / BIT / BOOLEAN |
|
|
122
|
+
| `c.datetime()` | DateTime | DATETIME |
|
|
123
|
+
| `c.date()` | DateOnly | DATE |
|
|
124
|
+
| `c.time()` | Time | TIME |
|
|
125
|
+
| `c.uuid()` | Uuid | BINARY(16) / UNIQUEIDENTIFIER / UUID |
|
|
126
|
+
|
|
127
|
+
### ColumnBuilder 수식 메서드
|
|
128
|
+
|
|
129
|
+
- `.autoIncrement()` — INSERT 시 자동 증가. `$inferInsert` 에서 해당 컬럼이 optional 이 됨.
|
|
130
|
+
- `.nullable()` — NULL 허용. 값 타입에 `undefined` 가 추가되고 `$inferInsert` 에서 optional.
|
|
131
|
+
- `.default(value)` — INSERT 시 미지정이면 사용할 기본값. `$inferInsert` 에서 optional. (정책: 사용자가 명시 지시한 경우에만 사용.)
|
|
132
|
+
- `.description(desc)` — 컬럼 설명. DDL Comment.
|
|
87
133
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
134
|
+
```typescript
|
|
135
|
+
.columns((c) => ({
|
|
136
|
+
id: c.bigint().autoIncrement(),
|
|
137
|
+
email: c.varchar(200).nullable(),
|
|
138
|
+
status: c.varchar(20).default("active"),
|
|
139
|
+
}))
|
|
140
|
+
```
|
|
94
141
|
|
|
95
|
-
|
|
142
|
+
## createIndexFactory (인덱스 팩토리 `i`)
|
|
96
143
|
|
|
97
|
-
|
|
98
|
-
- `single`: true — (target 계열만) 역참조를 배열이 아닌 단일 객체로 로드. 1:1 관계일 때.
|
|
144
|
+
`indexes()` 콜백에 주입되는 `i`. `i.index(...columns)` 가 `IndexBuilder` 를 반환.
|
|
99
145
|
|
|
100
146
|
```typescript
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
147
|
+
class IndexBuilder<TKeys> {
|
|
148
|
+
readonly meta: { columns; name?; unique?; orderBy?; description? };
|
|
149
|
+
name(name: string): IndexBuilder;
|
|
150
|
+
unique(): IndexBuilder;
|
|
151
|
+
orderBy(...orderBy: ("ASC" | "DESC")[]): IndexBuilder;
|
|
152
|
+
description(description: string): IndexBuilder;
|
|
153
|
+
}
|
|
107
154
|
```
|
|
108
155
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
156
|
+
- `i.index(...columns)` — 인덱스 컬럼을 가변 인자로(복합 인덱스). 컬럼명은 테이블 컬럼 키로 타입 체크.
|
|
157
|
+
- `.name(name)` — 인덱스 이름 직접 지정.
|
|
158
|
+
- `.unique()` — 유니크 인덱스로 표시.
|
|
159
|
+
- `.orderBy(...dirs)` — 컬럼별 정렬 방향. 인자 개수가 컬럼 개수와 일치해야 함.
|
|
160
|
+
- `.description(desc)` — 인덱스 설명.
|
|
112
161
|
|
|
113
162
|
```typescript
|
|
114
|
-
|
|
163
|
+
.indexes((i) => [
|
|
164
|
+
i.index("email").unique(),
|
|
165
|
+
i.index("status", "createdAt").orderBy("ASC", "DESC"),
|
|
166
|
+
])
|
|
115
167
|
```
|
|
116
168
|
|
|
117
|
-
|
|
169
|
+
## createRelationFactory (관계 팩토리 `r`)
|
|
118
170
|
|
|
119
|
-
|
|
120
|
-
- `query((db) => db.x().select(...))` — 뷰 본문 SELECT 정의. `db` 는 `DbContext`, 반환은 `Queryable`. select 결과 컬럼이 뷰 컬럼이 됨.
|
|
121
|
-
- `relations((r) => ({...}))` — 논리 관계(RelationKey 계열)만 정의 가능.
|
|
171
|
+
`relations()` 콜백에 주입되는 `r`. Table 은 4종 모두, View 는 `relationKey`/`relationKeyTarget` 만 노출. description·single 옵션은 메서드 체이닝이 아니라 `opts` 인자로 전달한다(순환 참조 시 TS7022 회피).
|
|
122
172
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
db.user().where((u) => [expr.eq(u.isActive, true)]).select((u) => ({ id: u.id, name: u.name })),
|
|
128
|
-
);
|
|
129
|
-
```
|
|
173
|
+
- `r.foreignKey(columns, targetFn, opts?)` — N:1 FK 관계(DB FK 제약 **생성**). `columns` 는 FK 컬럼 배열, `targetFn` 은 대상 테이블 지연 반환(`() => User`). `opts.description?`. → `include()` 시 단일 객체로 로드.
|
|
174
|
+
- `r.foreignKeyTarget(targetTableFn, relationName, opts?)` — 1:N FK 역참조. `relationName` 은 대상 테이블에서 이쪽을 가리키는 FK 관계 이름. `opts.single: true` 면 1:1(단일 객체), 미지정/`false` 면 배열. `opts.description?`.
|
|
175
|
+
- `r.relationKey(columns, targetFn, opts?)` — `foreignKey` 와 동일하나 DB FK 제약 **미생성**(논리적 관계). View 에서도 사용 가능.
|
|
176
|
+
- `r.relationKeyTarget(targetTableFn, relationName, opts?)` — `foreignKeyTarget` 의 DB FK 미생성 버전. `opts.single`/`description` 동일.
|
|
130
177
|
|
|
131
|
-
|
|
178
|
+
대상은 항상 지연 함수(`() => Table`)로 넘긴다 — 순환 참조(A↔B) 방지.
|
|
132
179
|
|
|
133
180
|
```typescript
|
|
134
|
-
|
|
181
|
+
const Post = Table("Post")
|
|
182
|
+
.columns((c) => ({ id: c.bigint().autoIncrement(), authorId: c.bigint() }))
|
|
183
|
+
.primaryKey("id")
|
|
184
|
+
.relations((r) => ({
|
|
185
|
+
author: r.foreignKey(["authorId"], () => User, { description: "작성자" }),
|
|
186
|
+
}));
|
|
187
|
+
|
|
188
|
+
const User = Table("User")
|
|
189
|
+
.columns((c) => ({ id: c.bigint().autoIncrement(), name: c.varchar(100) }))
|
|
190
|
+
.primaryKey("id")
|
|
191
|
+
.relations((r) => ({
|
|
192
|
+
posts: r.foreignKeyTarget(() => Post, "author"),
|
|
193
|
+
profile: r.foreignKeyTarget(() => Profile, "user", { single: true }),
|
|
194
|
+
}));
|
|
135
195
|
```
|
|
136
196
|
|
|
137
|
-
|
|
197
|
+
## 빌더 클래스 (관계)
|
|
138
198
|
|
|
139
|
-
|
|
140
|
-
- `params((c) => ({...}))` — 입력 파라미터 정의. `c` 는 column 팩토리. 키가 파라미터명.
|
|
141
|
-
- `returns((c) => ({...}))` — 반환 결과 컬럼 정의.
|
|
142
|
-
- `body(sql)` — 프로시저 본문 SQL. dialect 별 파라미터 구문 차이 주의(MySQL/PostgreSQL: `userId`, MSSQL: `@userId`).
|
|
199
|
+
`createRelationFactory` 가 반환하는 인스턴스의 클래스도 export 된다. `meta` 만 보유하는 데이터 홀더이며 메서드 체이닝은 없다(옵션은 팩토리 `opts`).
|
|
143
200
|
|
|
144
|
-
|
|
201
|
+
- `ForeignKeyBuilder` — N:1 FK(`meta: { ownerFn, columns, targetFn, description? }`).
|
|
202
|
+
- `ForeignKeyTargetBuilder` — 1:N FK 역참조(`meta: { targetTableFn, relationName, description?, isSingle? }`).
|
|
203
|
+
- `RelationKeyBuilder` / `RelationKeyTargetBuilder` — 위 둘의 DB FK 미생성 버전.
|
|
145
204
|
|
|
146
|
-
|
|
147
|
-
const GetUserById = Procedure("GetUserById")
|
|
148
|
-
.database("mydb")
|
|
149
|
-
.params((c) => ({ userId: c.bigint() }))
|
|
150
|
-
.returns((c) => ({ id: c.bigint(), name: c.varchar(100) }))
|
|
151
|
-
.body("SELECT id, name FROM User WHERE id = userId");
|
|
152
|
-
```
|
|
205
|
+
## 타입 추론 유틸리티
|
|
153
206
|
|
|
154
|
-
|
|
207
|
+
빌더의 phantom 필드 뒤에 있는 추론 타입. 직접 import 해 쓸 일은 드물지만 export 됨.
|
|
155
208
|
|
|
156
|
-
|
|
209
|
+
- `InferColumns<TBuilders>` — 컬럼 빌더 레코드 → 실제 값 타입 레코드.
|
|
210
|
+
- `InferColumnExprs<TBuilders>` — 컬럼 빌더 레코드 → `ExprInput<V>` 레코드(프로시저 파라미터 입력 타입).
|
|
211
|
+
- `InferInsertColumns<TBuilders>` — INSERT 타입(필수/optional 분리).
|
|
212
|
+
- `InferUpdateColumns<TBuilders>` — UPDATE 타입(전부 optional).
|
|
213
|
+
- `RequiredInsertKeys`/`OptionalInsertKeys` — INSERT 필수/optional 키 추출.
|
|
214
|
+
- `DataToColumnBuilderRecord<TData>` — 데이터 레코드 → 컬럼 빌더 레코드(`insertInto` 대상 타입 제약용).
|
|
215
|
+
- `InferDeepRelations<TRelations>` — 관계 정의 → 심층(중첩) 관계 타입. 모든 관계는 optional(include 전 undefined). 같은 테이블 재방문 시 순환을 끊음.
|
|
216
|
+
- `ExtractRelationTarget` / `ExtractRelationTargetResult` — 단일/배열 관계 대상 타입 추출(내부용).
|
|
217
|
+
- `ColumnBuilderRecord` / `RelationBuilderRecord` — 컬럼/관계 빌더 레코드 타입.
|
|
157
218
|
|
|
158
|
-
|
|
159
|
-
- `InferColumns<T>` — column 빌더 레코드에서 실제 값 타입 추론.
|
|
160
|
-
- `InferColumnExprs<T>` — 각 컬럼을 `ExprInput<V>` 로 추론(프로시저 파라미터 타입 등).
|
|
161
|
-
- `InferInsertColumns<T>` / `InferUpdateColumns<T>` — INSERT(필수/optional 분리)·UPDATE(전부 optional) 타입.
|
|
162
|
-
- `RequiredInsertKeys<T>` / `OptionalInsertKeys<T>` — INSERT 필수/optional 키 집합.
|
|
163
|
-
- `DataToColumnBuilderRecord<TData>` — 데이터 레코드를 column 빌더 레코드로 역변환(`insertInto` 대상 테이블 제약에 사용).
|
|
164
|
-
- `RelationBuilderRecord` — 관계 빌더 레코드 타입.
|
|
165
|
-
- `InferDeepRelations<TRelations>` / `ExtractRelationTarget<T>` / `ExtractRelationTargetResult<T>` — 관계를 통한 심층 타입 추론(관계는 `include` 전이라 모두 optional 로 추론).
|
|
219
|
+
## 주의사항
|
|
166
220
|
|
|
167
|
-
|
|
221
|
+
- 모든 빌더 메서드는 새 인스턴스를 반환하는 불변 체이닝 — 중간 결과를 변수에 담아 분기 재사용 가능.
|
|
222
|
+
- 관계 `target`/`owner` 는 반드시 지연 함수로 — 즉시 참조하면 모듈 로드 순서·순환 참조로 깨짐.
|
|
223
|
+
- View 의 관계는 `relationKey*` 만(DB FK 없음). Table 만 `foreignKey*` 로 실제 FK 제약 생성.
|
|
224
|
+
- 컬럼은 기본 `NOT NULL`. `.nullable()`/`.default()` 는 도메인 근거가 있을 때만(orm.md 정책).
|
|
@@ -1,64 +1,118 @@
|
|
|
1
|
-
# @simplysm/orm-common —
|
|
1
|
+
# @simplysm/orm-common — types
|
|
2
2
|
|
|
3
|
-
executor·
|
|
3
|
+
executor·dialect 어댑터 구현, AST 타입, dialect 별 SQL 렌더링, 결과 파싱을 다루는 군. 일반 쿼리 작성에는 직접 쓸 일이 적고, executor 구현체·SQL 검사·결과 변환 계층에서 사용한다.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 컬럼 원시 타입 / DataType
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
```typescript
|
|
8
|
+
type ColumnPrimitiveMap = { string; number; boolean; DateTime; DateOnly; Time; Uuid; Bytes };
|
|
9
|
+
type ColumnPrimitiveStr = keyof ColumnPrimitiveMap; // 타입 이름 문자열
|
|
10
|
+
type ColumnPrimitive = ColumnPrimitiveMap[ColumnPrimitiveStr] | undefined; // undefined=NULL
|
|
11
|
+
type DataType =
|
|
12
|
+
| { type: "int" } | { type: "bigint" } | { type: "float" } | { type: "double" }
|
|
13
|
+
| { type: "decimal"; precision: number; scale? } | { type: "varchar"; length: number }
|
|
14
|
+
| { type: "char"; length: number } | { type: "text" } | { type: "binary" }
|
|
15
|
+
| { type: "boolean" } | { type: "datetime" } | { type: "date" } | { type: "time" } | { type: "uuid" };
|
|
16
|
+
```
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
- `ColumnPrimitiveMap` — 타입 이름 → 실제 TS 타입 매핑. `ColumnPrimitiveStr` 은 그 키(`expr.val`/`expr.col` 의 dataType 인자 타입).
|
|
19
|
+
- `ColumnPrimitive` — 저장 가능한 모든 원시 값. `undefined` 가 SQL NULL.
|
|
20
|
+
- `DataType` — SQL 컬럼 타입의 구조화 표현(DDL·`cast` 에서 사용).
|
|
21
|
+
- `dataTypeStrToColumnPrimitiveStr` — `DataType["type"]` → `ColumnPrimitiveStr` 매핑 상수(예 `int → "number"`, `date → "DateOnly"`).
|
|
22
|
+
- `InferColumnPrimitiveFromDataType<T>` — `DataType` 으로부터 TS 타입 추론.
|
|
23
|
+
- `inferColumnPrimitiveStr(value)` — 런타임 값에서 타입 이름 추론. **NULL 이면 throw**(추론 불가).
|
|
24
|
+
- `ColumnMeta` — `{ type; dataType; autoIncrement?; nullable?; default?; description? }`. ColumnBuilder 가 생성해 보유.
|
|
17
25
|
|
|
18
|
-
|
|
19
|
-
- `ColumnPrimitiveMap` (type) — 타입명→실제 TS 타입 매핑(`string`/`number`/`boolean`/`DateTime`/`DateOnly`/`Time`/`Uuid`/`Bytes`).
|
|
20
|
-
- `ColumnPrimitiveStr` (type) — `keyof ColumnPrimitiveMap`(타입명 문자열). `ExprUnit.dataType` 의 타입.
|
|
21
|
-
- `ColumnPrimitive` (type) — 저장 가능한 모든 원시 값 `| undefined`(undefined=NULL).
|
|
22
|
-
- `ColumnMeta` (interface) — 컬럼 메타(`type`/`dataType`/`autoIncrement?`/`nullable?`/`default?`/`description?`). `ColumnBuilder.meta`.
|
|
23
|
-
- `dataTypeStrToColumnPrimitiveStr` (const) — SQL 타입명→TS 타입명 매핑 테이블(`int`→`"number"`, `datetime`→`"DateTime"` 등).
|
|
24
|
-
- `InferColumnPrimitiveFromDataType<T>` (type) — `DataType` 에서 TS 타입 추론.
|
|
25
|
-
- `inferColumnPrimitiveStr(value)` (function) — 런타임 값에서 `ColumnPrimitiveStr` 추론. NULL 이면 추론 불가로 throw. 리터럴을 ExprUnit 으로 자동 래핑할 때 사용.
|
|
26
|
+
## 데이터·executor 타입 (types/db)
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
```typescript
|
|
29
|
+
type Dialect = "mysql" | "mssql" | "postgresql";
|
|
30
|
+
const dialects: Dialect[];
|
|
31
|
+
type IsolationLevel = "READ_UNCOMMITTED" | "READ_COMMITTED" | "REPEATABLE_READ" | "SERIALIZABLE";
|
|
32
|
+
type DataRecord = { [key: string]: ColumnPrimitive | DataRecord | DataRecord[] }; // 재귀(중첩 관계)
|
|
33
|
+
interface ResultMeta { columns: Record<string, ColumnPrimitiveStr>; joins: Record<string, { isSingle: boolean }>; }
|
|
34
|
+
interface QueryBuildResult { sql: string; resultSetIndex?: number; resultSetStride?: number; }
|
|
35
|
+
```
|
|
28
36
|
|
|
29
|
-
|
|
37
|
+
- `Dialect` — 지원 DBMS(MySQL 8.0.14+, MSSQL 2012+, PostgreSQL 9.0+). `dialects` 는 전체 목록(테스트 매트릭스용).
|
|
38
|
+
- `IsolationLevel` — 트랜잭션 격리 수준(기본 `READ_COMMITTED`). `connect`/`transaction`·executor `beginTransaction` 인자.
|
|
39
|
+
- `DataRecord` — 쿼리 결과 레코드(중첩 관계는 객체/배열 중첩).
|
|
40
|
+
- `ResultMeta` — 결과 변환 메타. `columns` 는 평면 키(`posts.id`) → 타입, `joins` 는 JOIN 경로 → 단일/배열 구분. `Queryable.getResultMeta()` 가 생성.
|
|
41
|
+
- `QueryBuildResult` — `build()` 결과. `resultSetIndex`(가져올 결과셋 위치)·`resultSetStride`(N번째마다 추출) 로 다중 결과셋 처리(MySQL INSERT+SELECT, 배치 INSERT 등).
|
|
42
|
+
- `DbContextExecutor` / `Migration` — db-context.md 참조.
|
|
30
43
|
|
|
31
|
-
|
|
32
|
-
- 비교: `ExprEq`/`ExprGt`/`ExprLt`/`ExprGte`/`ExprLte`/`ExprBetween`/`ExprIsNull`/`ExprLike`/`ExprRegexp`/`ExprIn`/`ExprInQuery`/`ExprExists`.
|
|
33
|
-
- 논리: `ExprNot`/`ExprAnd`/`ExprOr`.
|
|
34
|
-
- 문자열: `ExprConcat`/`ExprLeft`/`ExprRight`/`ExprTrim`/`ExprPadStart`/`ExprReplace`/`ExprUpper`/`ExprLower`/`ExprLength`/`ExprByteLength`/`ExprSubstring`/`ExprIndexOf`.
|
|
35
|
-
- 숫자: `ExprAbs`/`ExprRound`/`ExprCeil`/`ExprFloor`.
|
|
36
|
-
- 날짜: `ExprYear`/`ExprMonth`/`ExprDay`/`ExprHour`/`ExprMinute`/`ExprSecond`/`ExprIsoWeek`/`ExprIsoWeekStartDate`/`ExprIsoYearMonth`/`ExprDateDiff`/`ExprDateAdd`/`ExprFormatDate`.
|
|
37
|
-
- 조건: `ExprCoalesce`/`ExprNullIf`/`ExprIs`/`ExprSwitch`/`ExprIf`.
|
|
38
|
-
- 집계: `ExprCount`/`ExprSum`/`ExprAvg`/`ExprMax`/`ExprMin`.
|
|
39
|
-
- 기타: `ExprGreatest`/`ExprLeast`/`ExprRowNum`/`ExprRandom`/`ExprCast`/`ExprSubquery`.
|
|
40
|
-
- 윈도우: `ExprWindow`(`fn`=`WinFn`, `spec`=`WinSpec`). `WinFn` union = `WinFnRowNumber`/`WinFnRank`/`WinFnDenseRank`/`WinFnNtile`/`WinFnLag`/`WinFnLead`/`WinFnFirstValue`/`WinFnLastValue`/`WinFnSum`/`WinFnAvg`/`WinFnCount`/`WinFnMin`/`WinFnMax`. `WinSpec`=`{ partitionBy?: Expr[]; orderBy?: [Expr, dir?][] }`.
|
|
41
|
-
- `DateUnit` (type) — `"year"|"month"|"day"|"hour"|"minute"|"second"`. dateDiff/dateAdd 단위.
|
|
44
|
+
## QueryDef AST (types/query-def)
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
`Queryable`/`DbContext` 가 만드는 dialect 독립 쿼리 정의. `QueryDef` 는 전체 union.
|
|
44
47
|
|
|
45
|
-
`
|
|
48
|
+
- `QueryDefObjectName` — `{ database?; schema?; name }`. DBMS 별 네임스페이스(MySQL `db.name`, MSSQL `db.schema.name`, PostgreSQL `schema.name`).
|
|
49
|
+
- DML: `SelectQueryDef`(+`SelectQueryDefJoin` — `isSingle?` 추가), `InsertQueryDef`(`records`/`overrideIdentity?`/`aiColName?`/`output?`), `InsertIfNotExistsQueryDef`, `InsertIntoQueryDef`, `UpdateQueryDef`, `DeleteQueryDef`, `UpsertQueryDef`. `CudOutputDef` 는 OUTPUT 절(`columns`/`pkColNames`/`aiColName?`).
|
|
50
|
+
- DDL: `CreateTableQueryDef`/`DropTableQueryDef`/`RenameTableQueryDef`/`TruncateQueryDef`, `AddColumnQueryDef`/`DropColumnQueryDef`/`ModifyColumnQueryDef`/`RenameColumnQueryDef`, `AddPrimaryKeyQueryDef`/`DropPrimaryKeyQueryDef`/`AddForeignKeyQueryDef`/`DropForeignKeyQueryDef`/`AddIndexQueryDef`/`DropIndexQueryDef`, `CreateViewQueryDef`/`DropViewQueryDef`, `CreateProcQueryDef`/`DropProcQueryDef`/`ExecProcQueryDef`, `ClearSchemaQueryDef`.
|
|
51
|
+
- Utils/Meta: `SwitchFkQueryDef`(`{ table; enabled }` — DDL 아님), `SchemaExistsQueryDef`.
|
|
52
|
+
- `DDL_TYPES` — DDL QueryDef 타입 이름 배열(상수). 트랜잭션 내 DDL 차단(`executeDefs`) 검증에 사용. `switchFk` 는 제외(트랜잭션 가능). `DdlType` 은 그 union 타입.
|
|
46
53
|
|
|
47
|
-
|
|
48
|
-
- DML: `SelectQueryDef`(from/as/select/distinct/top/lock/where/joins/orderBy/limit/groupBy/having/with), `SelectQueryDefJoin`(SelectQueryDef + `isSingle?`), `InsertQueryDef`(`records`/`overrideIdentity?`/`aiColName?`/`output?` — overrideIdentity 는 AI 컬럼에 명시값 삽입 시, aiColName 은 PostgreSQL 시퀀스 보정용), `InsertIfNotExistsQueryDef`, `InsertIntoQueryDef`, `UpdateQueryDef`, `DeleteQueryDef`, `UpsertQueryDef`.
|
|
49
|
-
- DDL: `CreateTableQueryDef`/`DropTableQueryDef`/`RenameTableQueryDef`/`TruncateQueryDef`, `AddColumnQueryDef`/`DropColumnQueryDef`/`ModifyColumnQueryDef`/`RenameColumnQueryDef`, `AddPrimaryKeyQueryDef`/`DropPrimaryKeyQueryDef`, `AddForeignKeyQueryDef`/`DropForeignKeyQueryDef`, `AddIndexQueryDef`/`DropIndexQueryDef`, `CreateViewQueryDef`/`DropViewQueryDef`, `CreateProcQueryDef`/`DropProcQueryDef`/`ExecProcQueryDef`, `ClearSchemaQueryDef`.
|
|
50
|
-
- Utils/Meta: `SwitchFkQueryDef`(`enabled` — DDL 아님, 트랜잭션 내 사용 가능), `SchemaExistsQueryDef`.
|
|
51
|
-
- `DDL_TYPES` (const) — DDL 타입 문자열 배열(`["createTable", ...]`). 트랜잭션 내 DDL 차단·검증에 사용(`switchFk` 는 제외). `DdlType` (type) = `(typeof DDL_TYPES)[number]`.
|
|
54
|
+
`SelectQueryDef` 주요 필드: `from`(객체명/서브쿼리/배열(UNION)/문자열) · `as` · `select?` · `distinct?` · `top?` · `lock?` · `where?` · `joins?` · `orderBy?` · `limit?` · `groupBy?` · `having?` · `with?`(재귀 CTE).
|
|
52
55
|
|
|
53
|
-
##
|
|
56
|
+
## Expr AST (types/expr)
|
|
54
57
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
- `
|
|
58
|
-
-
|
|
58
|
+
`expr.*` 가 만드는 표현식 AST. 각 인터페이스는 `type` 판별 필드를 가진다.
|
|
59
|
+
|
|
60
|
+
- `Expr` — 전체 표현식 union(값/문자열/숫자/날짜/조건/집계/기타/윈도우/서브쿼리).
|
|
61
|
+
- `WhereExpr` — WHERE 절 전용 union(비교 `ExprEq`/`ExprGt`/.../`ExprBetween`/`ExprIsNull`/`ExprLike`/`ExprRegexp`/`ExprIn`/`ExprInQuery`/`ExprExists` + 논리 `ExprNot`/`ExprAnd`/`ExprOr`).
|
|
62
|
+
- 값: `ExprColumn`(`path`), `ExprValue`(`value`), `ExprRaw`(`sql`/`params`).
|
|
63
|
+
- `DateUnit` — `"year"|"month"|"day"|"hour"|"minute"|"second"`(`dateDiff`/`dateAdd`).
|
|
64
|
+
- 윈도우: `WinFn`(union — `WinFnRowNumber`/`WinFnRank`/`WinFnLag` 등), `WinSpec`(`partitionBy?`/`orderBy?`), `ExprWindow`(`fn`+`spec`).
|
|
65
|
+
- 개별 표현식 인터페이스 전체(`ExprConcat`, `ExprDateDiff`, `ExprSwitch`, `ExprCount`, `ExprCast`, `ExprSubquery`, ...)도 export — 각각 `expr.md` 의 함수에 1:1 대응.
|
|
66
|
+
|
|
67
|
+
## QueryBuilder (dialect 렌더링)
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
function createQueryBuilder(dialect: Dialect): QueryBuilderBase;
|
|
71
|
+
abstract class QueryBuilderBase {
|
|
72
|
+
build(def: QueryDef): QueryBuildResult; // def.type 으로 동적 dispatch
|
|
73
|
+
}
|
|
74
|
+
abstract class ExprRendererBase {
|
|
75
|
+
render(expr: Expr | WhereExpr): string;
|
|
76
|
+
renderWhere(exprs: WhereExpr[]): string; // " AND " 결합
|
|
77
|
+
abstract wrap(name: string): string; // 식별자 감싸기 (`name` / [name] / "name")
|
|
78
|
+
abstract escapeString(value: string): string;
|
|
79
|
+
abstract escapeValue(value: unknown): string;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- `createQueryBuilder(dialect)` — dialect 에 맞는 QueryBuilder 인스턴스 반환. executor 가 `QueryDef` 를 SQL 로 만들 때 진입점.
|
|
84
|
+
- `QueryBuilderBase.build(def)` — `QueryDef` → `QueryBuildResult`. `def.type` 이름의 메서드로 dispatch(미지원 타입은 throw).
|
|
85
|
+
- `ExprRendererBase` — `Expr`/`WhereExpr` → SQL 문자열. `wrap`/`escapeString`/`escapeValue` 등 dialect 차이만 abstract.
|
|
86
|
+
- dialect 구현 클래스도 export(직접 인스턴스화보다 `createQueryBuilder` 권장): `MysqlQueryBuilder`/`MssqlQueryBuilder`/`PostgresqlQueryBuilder`, `MysqlExprRenderer`/`MssqlExprRenderer`/`PostgresqlExprRenderer`.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const builder = createQueryBuilder("mysql");
|
|
90
|
+
const { sql } = builder.build(db.user().getSelectQueryDef());
|
|
91
|
+
```
|
|
59
92
|
|
|
60
93
|
## 결과 파싱
|
|
61
94
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
95
|
+
```typescript
|
|
96
|
+
function parseQueryResult<TRecord>(
|
|
97
|
+
rawResults: Record<string, unknown>[],
|
|
98
|
+
meta: ResultMeta,
|
|
99
|
+
): Promise<TRecord[] | undefined>;
|
|
100
|
+
function pickResultSets<T>(
|
|
101
|
+
rawResults: T[][],
|
|
102
|
+
buildResult: Pick<QueryBuildResult, "resultSetIndex" | "resultSetStride">,
|
|
103
|
+
): T[];
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- `parseQueryResult(rawResults, meta)` — DB 원시 결과를 `meta` 로 타입 변환·중첩(JOIN 평면 키 → 객체/배열)한다. async 전용(대량 처리 중 100건마다 이벤트 루프 양보). 입력이 비었거나 파싱 후 전부 빈 객체면 `undefined`. 타입 파싱 실패 시 throw. `isSingle` JOIN 에 서로 다른 다중 결과가 오면 throw. (null 은 보존, undefined 키는 제거.)
|
|
107
|
+
- `pickResultSets(rawResults, buildResult)` — 다중 결과셋에서 필요한 셋만 추출. `resultSetIndex` 없으면 첫 셋, `stride` 없으면 해당 인덱스 단일, 있으면 인덱스부터 stride 간격으로 concat(MySQL 배치 INSERT;SELECT 패턴).
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const meta = { columns: { id: "number", "posts.id": "number" }, joins: { posts: { isSingle: false } } };
|
|
111
|
+
const rows = await parseQueryResult(rawResults, meta);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## 주의사항
|
|
115
|
+
|
|
116
|
+
- 이 군은 executor·SQL 검사·결과 파싱 계층용. 앱 쿼리 작성은 `queryable.md`/`expr.md` 의 고수준 API 사용.
|
|
117
|
+
- `parseQueryResult` 의 `meta` 는 필수 — 없으면 호출 자체가 불필요(입력=출력). 보통 `Queryable.getResultMeta()` 로 생성.
|
|
118
|
+
- `QueryDefObjectName` 의 네임스페이스 해석은 dialect 마다 다름(MySQL 은 schema 무시 등).
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# @simplysm/orm-node
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@simplysm/orm-common` 의 `DbContext`(query DSL)를 Node.js 환경의 실제 DB(MySQL/MSSQL/PostgreSQL) 연결에 묶어 실행하는 ORM 런타임. query 빌더·`expr` 자체는 `orm-common` 이 제공하고, 이 패키지는 연결 수립·트랜잭션 경계·드라이버별 실행을 담당. 고수준 진입 `createOrm`(트랜잭션 경계 자동 관리)과 저수준 연결 `createDbConn`/`DbConn`(raw SQL·파라미터 쿼리·bulk insert·수동 트랜잭션)을 함께 노출.
|
|
4
4
|
|
|
5
5
|
## 사용 트리거 인덱스
|
|
6
6
|
|
|
7
|
-
- **createOrm / Orm / OrmOptions** — `DbContext` 서브클래스로 ORM 인스턴스를 만들고 `connect`/`connectWithoutTransaction` 으로 트랜잭션 경계를 잡아 query 를
|
|
8
|
-
- **createDbConn / DbConn / DbConnConfig 계열 / getDialectFromConfig / NodeDbContextExecutor / DB_CONN_\* 상수** — `createOrm` 추상화를 거치지 않고 raw SQL·파라미터 쿼리·bulk insert·수동 트랜잭션을 직접 다루거나, dialect별 접속 설정 타입을 작성하거나, `DbContext`
|
|
7
|
+
- **createOrm / Orm / OrmOptions** — `DbContext` 서브클래스로 ORM 인스턴스를 만들고 `connect`/`connectWithoutTransaction` 으로 트랜잭션 경계를 잡아 query 를 실행할 때. (아래 "ORM 진입" 군)
|
|
8
|
+
- **createDbConn / DbConn / DbConnConfig 계열 / getDialectFromConfig / NodeDbContextExecutor / DB_CONN_\* 상수** — `createOrm` 추상화를 거치지 않고 raw SQL·파라미터 쿼리·bulk insert·수동 트랜잭션을 직접 다루거나, dialect별 접속 설정 타입을 작성하거나, `DbContext` executor 를 손수 조립할 때. 자세히: [db-conn.md](./db-conn.md)
|
|
9
9
|
|
|
10
10
|
## ORM 진입
|
|
11
11
|
|
|
12
|
-
`DbContext` 서브클래스와 접속 설정을 받아 연결·트랜잭션 경계를 관리하는 고수준 진입. 대부분의 작업은 이 군으로
|
|
12
|
+
`DbContext` 서브클래스와 접속 설정을 받아 연결·트랜잭션 경계를 관리하는 고수준 진입. 대부분의 작업은 이 군으로 충분하고, raw SQL·executor 커스터마이징이 필요할 때만 [db-conn.md](./db-conn.md) 계층으로 내려감.
|
|
13
13
|
|
|
14
14
|
### createOrm
|
|
15
15
|
|
|
@@ -21,10 +21,10 @@ function createOrm<T extends DbContext>(
|
|
|
21
21
|
): Orm<T>
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
`DbContext` 서브클래스를 받아 `Orm<T>` 를 반환. DB 인스턴스는 `connect`/`connectWithoutTransaction` 호출마다 새로
|
|
24
|
+
`DbContext` 서브클래스를 받아 `Orm<T>` 를 반환. DB 인스턴스는 `connect`/`connectWithoutTransaction` 호출마다 새로 생성하므로 반환된 `Orm` 객체 자체는 재사용 가능.
|
|
25
25
|
|
|
26
|
-
- DbClass — `DbContext` 를 상속한
|
|
27
|
-
- config — `DbConnConfig`(dialect별 분기 유니온, [db-conn.md](./db-conn.md) 참조). 접속
|
|
26
|
+
- DbClass — `DbContext` 를 상속한 생성자(`(executor, { database, schema? })` 시그니처 고정). `this.queryable(Entity)` 로 query 진입점을 정의한 사용자 DB 클래스를 넘김 — 어떤 엔티티 집합을 다룰지 결정하는 자리.
|
|
27
|
+
- config — `DbConnConfig`(dialect별 분기 유니온, [db-conn.md](./db-conn.md) 참조). 접속 대상·인증·기본 DB. DBMS 종류·호스트·계정이 여기서 정해짐.
|
|
28
28
|
- options? — `OrmOptions`. config 의 `database`/`schema` 를 덮어쓰는 우선 옵션. 같은 접속 정보로 DB·스키마만 바꿔 쓸 때(다중 테넌트 등) 지정.
|
|
29
29
|
|
|
30
30
|
database 해석: `options.database` → `config.database` 순으로 찾고, 둘 다 없거나 빈 문자열이면 `"database는 필수입니다"` throw. schema 해석도 `options.schema` → `config.schema` 순(없으면 `undefined` 유지).
|
|
@@ -35,9 +35,8 @@ class TestDb extends DbContext {
|
|
|
35
35
|
}
|
|
36
36
|
const orm = createOrm(TestDb, mysqlConfig, { database: "TestDb" });
|
|
37
37
|
await orm.connect(async (db) => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}); // 트랜잭션 안에서 실행 후 콜백 정상 종료 시 자동 커밋
|
|
38
|
+
return db.user().select((u) => ({ id: u.id, name: u.name })).execute();
|
|
39
|
+
}); // 트랜잭션 안에서 실행, 콜백 정상 종료 시 자동 커밋
|
|
41
40
|
```
|
|
42
41
|
|
|
43
42
|
### Orm
|
|
@@ -55,16 +54,16 @@ interface Orm<T extends DbContext> {
|
|
|
55
54
|
`createOrm` 반환 타입. 각 메서드 호출마다 DB 인스턴스를 새로 만들어 연결→콜백→정리.
|
|
56
55
|
|
|
57
56
|
- DbClass / config / options — `createOrm` 에 넘긴 값을 그대로 읽기 전용으로 보관. 같은 설정으로 재연결·진단할 때 참조.
|
|
58
|
-
- connect — 콜백을 **트랜잭션 안에서** 실행. 콜백이 정상 종료하면 커밋, throw 하면 롤백 후 그 오류를 다시 throw. 여러 쓰기를 원자적으로
|
|
57
|
+
- connect — 콜백을 **트랜잭션 안에서** 실행. 콜백이 정상 종료하면 커밋, throw 하면 롤백 후 그 오류를 다시 throw. 여러 쓰기를 원자적으로 묶을 때 기본으로 사용.
|
|
59
58
|
- isolationLevel? — `IsolationLevel`(`@simplysm/orm-common`). 트랜잭션 격리 수준. 테스트로 확인된 값: `"READ_UNCOMMITTED" | "READ_COMMITTED" | "REPEATABLE_READ" | "SERIALIZABLE"`. 미지정 시 연결의 `defaultIsolationLevel`, 그것도 없으면 `READ_UNCOMMITTED`. 더티 리드를 막아야 하면 `READ_COMMITTED` 이상으로 올림.
|
|
60
|
-
- connectWithoutTransaction — 콜백을 **트랜잭션 없이** 실행. 읽기 전용이거나,
|
|
59
|
+
- connectWithoutTransaction — 콜백을 **트랜잭션 없이** 실행. 읽기 전용이거나, 트랜잭션 밖에서 동작해야 하는 작업, 또는 콜백 내부에서 `db.transaction(...)` 으로 일부 구간만 부분 트랜잭션으로 직접 묶을 때.
|
|
61
60
|
- callback — 연결된 DbContext 인스턴스(`T`)를 받아 query 를 수행하고 임의 값 `R` 을 반환. 그 반환값이 `connect`/`connectWithoutTransaction` 의 결과가 됨.
|
|
62
61
|
|
|
63
62
|
```typescript
|
|
64
63
|
// 읽기는 트랜잭션 없이, 그 안에서 일부 쓰기만 부분 트랜잭션으로
|
|
65
64
|
await orm.connectWithoutTransaction(async (db) => {
|
|
66
65
|
await db.transaction(async () => {
|
|
67
|
-
await db.user().insert([{
|
|
66
|
+
await db.user().insert([{ name: "Alice", isActive: true }]);
|
|
68
67
|
});
|
|
69
68
|
return db.user().execute();
|
|
70
69
|
});
|