@simplysm/orm-node 13.0.0-beta.6

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.
Files changed (172) hide show
  1. package/.cache/typecheck-node.tsbuildinfo +1 -0
  2. package/README.md +418 -0
  3. package/dist/connections/mssql-db-conn.js +386 -0
  4. package/dist/connections/mssql-db-conn.js.map +7 -0
  5. package/dist/connections/mysql-db-conn.js +227 -0
  6. package/dist/connections/mysql-db-conn.js.map +7 -0
  7. package/dist/connections/postgresql-db-conn.js +191 -0
  8. package/dist/connections/postgresql-db-conn.js.map +7 -0
  9. package/dist/core-common/src/common.types.d.ts +74 -0
  10. package/dist/core-common/src/common.types.d.ts.map +1 -0
  11. package/dist/core-common/src/env.d.ts +6 -0
  12. package/dist/core-common/src/env.d.ts.map +1 -0
  13. package/dist/core-common/src/errors/argument-error.d.ts +25 -0
  14. package/dist/core-common/src/errors/argument-error.d.ts.map +1 -0
  15. package/dist/core-common/src/errors/not-implemented-error.d.ts +29 -0
  16. package/dist/core-common/src/errors/not-implemented-error.d.ts.map +1 -0
  17. package/dist/core-common/src/errors/sd-error.d.ts +27 -0
  18. package/dist/core-common/src/errors/sd-error.d.ts.map +1 -0
  19. package/dist/core-common/src/errors/timeout-error.d.ts +31 -0
  20. package/dist/core-common/src/errors/timeout-error.d.ts.map +1 -0
  21. package/dist/core-common/src/extensions/arr-ext.d.ts +15 -0
  22. package/dist/core-common/src/extensions/arr-ext.d.ts.map +1 -0
  23. package/dist/core-common/src/extensions/arr-ext.helpers.d.ts +19 -0
  24. package/dist/core-common/src/extensions/arr-ext.helpers.d.ts.map +1 -0
  25. package/dist/core-common/src/extensions/arr-ext.types.d.ts +215 -0
  26. package/dist/core-common/src/extensions/arr-ext.types.d.ts.map +1 -0
  27. package/dist/core-common/src/extensions/map-ext.d.ts +57 -0
  28. package/dist/core-common/src/extensions/map-ext.d.ts.map +1 -0
  29. package/dist/core-common/src/extensions/set-ext.d.ts +36 -0
  30. package/dist/core-common/src/extensions/set-ext.d.ts.map +1 -0
  31. package/dist/core-common/src/features/debounce-queue.d.ts +53 -0
  32. package/dist/core-common/src/features/debounce-queue.d.ts.map +1 -0
  33. package/dist/core-common/src/features/event-emitter.d.ts +66 -0
  34. package/dist/core-common/src/features/event-emitter.d.ts.map +1 -0
  35. package/dist/core-common/src/features/serial-queue.d.ts +47 -0
  36. package/dist/core-common/src/features/serial-queue.d.ts.map +1 -0
  37. package/dist/core-common/src/index.d.ts +32 -0
  38. package/dist/core-common/src/index.d.ts.map +1 -0
  39. package/dist/core-common/src/types/date-only.d.ts +152 -0
  40. package/dist/core-common/src/types/date-only.d.ts.map +1 -0
  41. package/dist/core-common/src/types/date-time.d.ts +96 -0
  42. package/dist/core-common/src/types/date-time.d.ts.map +1 -0
  43. package/dist/core-common/src/types/lazy-gc-map.d.ts +80 -0
  44. package/dist/core-common/src/types/lazy-gc-map.d.ts.map +1 -0
  45. package/dist/core-common/src/types/time.d.ts +68 -0
  46. package/dist/core-common/src/types/time.d.ts.map +1 -0
  47. package/dist/core-common/src/types/uuid.d.ts +35 -0
  48. package/dist/core-common/src/types/uuid.d.ts.map +1 -0
  49. package/dist/core-common/src/utils/bytes.d.ts +51 -0
  50. package/dist/core-common/src/utils/bytes.d.ts.map +1 -0
  51. package/dist/core-common/src/utils/date-format.d.ts +90 -0
  52. package/dist/core-common/src/utils/date-format.d.ts.map +1 -0
  53. package/dist/core-common/src/utils/json.d.ts +34 -0
  54. package/dist/core-common/src/utils/json.d.ts.map +1 -0
  55. package/dist/core-common/src/utils/num.d.ts +60 -0
  56. package/dist/core-common/src/utils/num.d.ts.map +1 -0
  57. package/dist/core-common/src/utils/obj.d.ts +258 -0
  58. package/dist/core-common/src/utils/obj.d.ts.map +1 -0
  59. package/dist/core-common/src/utils/path.d.ts +23 -0
  60. package/dist/core-common/src/utils/path.d.ts.map +1 -0
  61. package/dist/core-common/src/utils/primitive.d.ts +18 -0
  62. package/dist/core-common/src/utils/primitive.d.ts.map +1 -0
  63. package/dist/core-common/src/utils/str.d.ts +103 -0
  64. package/dist/core-common/src/utils/str.d.ts.map +1 -0
  65. package/dist/core-common/src/utils/template-strings.d.ts +84 -0
  66. package/dist/core-common/src/utils/template-strings.d.ts.map +1 -0
  67. package/dist/core-common/src/utils/transferable.d.ts +47 -0
  68. package/dist/core-common/src/utils/transferable.d.ts.map +1 -0
  69. package/dist/core-common/src/utils/wait.d.ts +19 -0
  70. package/dist/core-common/src/utils/wait.d.ts.map +1 -0
  71. package/dist/core-common/src/utils/xml.d.ts +36 -0
  72. package/dist/core-common/src/utils/xml.d.ts.map +1 -0
  73. package/dist/core-common/src/zip/sd-zip.d.ts +80 -0
  74. package/dist/core-common/src/zip/sd-zip.d.ts.map +1 -0
  75. package/dist/db-conn-factory.js +88 -0
  76. package/dist/db-conn-factory.js.map +7 -0
  77. package/dist/index.js +9 -0
  78. package/dist/index.js.map +7 -0
  79. package/dist/node-db-context-executor.js +129 -0
  80. package/dist/node-db-context-executor.js.map +7 -0
  81. package/dist/orm-common/src/db-context.d.ts +669 -0
  82. package/dist/orm-common/src/db-context.d.ts.map +1 -0
  83. package/dist/orm-common/src/errors/db-transaction-error.d.ts +51 -0
  84. package/dist/orm-common/src/errors/db-transaction-error.d.ts.map +1 -0
  85. package/dist/orm-common/src/exec/executable.d.ts +79 -0
  86. package/dist/orm-common/src/exec/executable.d.ts.map +1 -0
  87. package/dist/orm-common/src/exec/queryable.d.ts +708 -0
  88. package/dist/orm-common/src/exec/queryable.d.ts.map +1 -0
  89. package/dist/orm-common/src/exec/search-parser.d.ts +72 -0
  90. package/dist/orm-common/src/exec/search-parser.d.ts.map +1 -0
  91. package/dist/orm-common/src/expr/expr-unit.d.ts +25 -0
  92. package/dist/orm-common/src/expr/expr-unit.d.ts.map +1 -0
  93. package/dist/orm-common/src/expr/expr.d.ts +1369 -0
  94. package/dist/orm-common/src/expr/expr.d.ts.map +1 -0
  95. package/dist/orm-common/src/index.d.ts +32 -0
  96. package/dist/orm-common/src/index.d.ts.map +1 -0
  97. package/dist/orm-common/src/models/system-migration.d.ts +10 -0
  98. package/dist/orm-common/src/models/system-migration.d.ts.map +1 -0
  99. package/dist/orm-common/src/query-builder/base/expr-renderer-base.d.ts +95 -0
  100. package/dist/orm-common/src/query-builder/base/expr-renderer-base.d.ts.map +1 -0
  101. package/dist/orm-common/src/query-builder/base/query-builder-base.d.ts +66 -0
  102. package/dist/orm-common/src/query-builder/base/query-builder-base.d.ts.map +1 -0
  103. package/dist/orm-common/src/query-builder/mssql/mssql-expr-renderer.d.ts +84 -0
  104. package/dist/orm-common/src/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -0
  105. package/dist/orm-common/src/query-builder/mssql/mssql-query-builder.d.ts +45 -0
  106. package/dist/orm-common/src/query-builder/mssql/mssql-query-builder.d.ts.map +1 -0
  107. package/dist/orm-common/src/query-builder/mysql/mysql-expr-renderer.d.ts +84 -0
  108. package/dist/orm-common/src/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -0
  109. package/dist/orm-common/src/query-builder/mysql/mysql-query-builder.d.ts +54 -0
  110. package/dist/orm-common/src/query-builder/mysql/mysql-query-builder.d.ts.map +1 -0
  111. package/dist/orm-common/src/query-builder/postgresql/postgresql-expr-renderer.d.ts +84 -0
  112. package/dist/orm-common/src/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -0
  113. package/dist/orm-common/src/query-builder/postgresql/postgresql-query-builder.d.ts +52 -0
  114. package/dist/orm-common/src/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -0
  115. package/dist/orm-common/src/query-builder/query-builder.d.ts +7 -0
  116. package/dist/orm-common/src/query-builder/query-builder.d.ts.map +1 -0
  117. package/dist/orm-common/src/schema/factory/column-builder.d.ts +394 -0
  118. package/dist/orm-common/src/schema/factory/column-builder.d.ts.map +1 -0
  119. package/dist/orm-common/src/schema/factory/index-builder.d.ts +151 -0
  120. package/dist/orm-common/src/schema/factory/index-builder.d.ts.map +1 -0
  121. package/dist/orm-common/src/schema/factory/relation-builder.d.ts +337 -0
  122. package/dist/orm-common/src/schema/factory/relation-builder.d.ts.map +1 -0
  123. package/dist/orm-common/src/schema/procedure-builder.d.ts +202 -0
  124. package/dist/orm-common/src/schema/procedure-builder.d.ts.map +1 -0
  125. package/dist/orm-common/src/schema/table-builder.d.ts +259 -0
  126. package/dist/orm-common/src/schema/table-builder.d.ts.map +1 -0
  127. package/dist/orm-common/src/schema/view-builder.d.ts +183 -0
  128. package/dist/orm-common/src/schema/view-builder.d.ts.map +1 -0
  129. package/dist/orm-common/src/types/column.d.ts +172 -0
  130. package/dist/orm-common/src/types/column.d.ts.map +1 -0
  131. package/dist/orm-common/src/types/db.d.ts +175 -0
  132. package/dist/orm-common/src/types/db.d.ts.map +1 -0
  133. package/dist/orm-common/src/types/expr.d.ts +474 -0
  134. package/dist/orm-common/src/types/expr.d.ts.map +1 -0
  135. package/dist/orm-common/src/types/query-def.d.ts +351 -0
  136. package/dist/orm-common/src/types/query-def.d.ts.map +1 -0
  137. package/dist/orm-common/src/utils/result-parser.d.ts +38 -0
  138. package/dist/orm-common/src/utils/result-parser.d.ts.map +1 -0
  139. package/dist/orm-node/src/connections/mssql-db-conn.d.ts +44 -0
  140. package/dist/orm-node/src/connections/mssql-db-conn.d.ts.map +1 -0
  141. package/dist/orm-node/src/connections/mysql-db-conn.d.ts +38 -0
  142. package/dist/orm-node/src/connections/mysql-db-conn.d.ts.map +1 -0
  143. package/dist/orm-node/src/connections/postgresql-db-conn.d.ts +39 -0
  144. package/dist/orm-node/src/connections/postgresql-db-conn.d.ts.map +1 -0
  145. package/dist/orm-node/src/db-conn-factory.d.ts +25 -0
  146. package/dist/orm-node/src/db-conn-factory.d.ts.map +1 -0
  147. package/dist/orm-node/src/index.d.ts +9 -0
  148. package/dist/orm-node/src/index.d.ts.map +1 -0
  149. package/dist/orm-node/src/node-db-context-executor.d.ts +77 -0
  150. package/dist/orm-node/src/node-db-context-executor.d.ts.map +1 -0
  151. package/dist/orm-node/src/pooled-db-conn.d.ts +79 -0
  152. package/dist/orm-node/src/pooled-db-conn.d.ts.map +1 -0
  153. package/dist/orm-node/src/sd-orm.d.ts +78 -0
  154. package/dist/orm-node/src/sd-orm.d.ts.map +1 -0
  155. package/dist/orm-node/src/types/db-conn.d.ts +159 -0
  156. package/dist/orm-node/src/types/db-conn.d.ts.map +1 -0
  157. package/dist/pooled-db-conn.js +134 -0
  158. package/dist/pooled-db-conn.js.map +7 -0
  159. package/dist/sd-orm.js +44 -0
  160. package/dist/sd-orm.js.map +7 -0
  161. package/dist/types/db-conn.js +17 -0
  162. package/dist/types/db-conn.js.map +7 -0
  163. package/package.json +50 -0
  164. package/src/connections/mssql-db-conn.ts +483 -0
  165. package/src/connections/mysql-db-conn.ts +299 -0
  166. package/src/connections/postgresql-db-conn.ts +254 -0
  167. package/src/db-conn-factory.ts +114 -0
  168. package/src/index.ts +13 -0
  169. package/src/node-db-context-executor.ts +162 -0
  170. package/src/pooled-db-conn.ts +175 -0
  171. package/src/sd-orm.ts +102 -0
  172. package/src/types/db-conn.ts +196 -0
@@ -0,0 +1,483 @@
1
+ import { createConsola } from "consola";
2
+ import {
3
+ DateOnly,
4
+ DateTime,
5
+ jsonStringify,
6
+ SdError,
7
+ EventEmitter,
8
+ strIsNullOrEmpty,
9
+ Time,
10
+ Uuid,
11
+ waitUntil,
12
+ } from "@simplysm/core-common";
13
+ import type { ColumnMeta, DataType, IsolationLevel } from "@simplysm/orm-common";
14
+ import { DB_CONN_DEFAULT_TIMEOUT, DB_CONN_ERRORS, type DbConn, type MssqlDbConnConfig } from "../types/db-conn";
15
+ import type tediousType from "tedious";
16
+ import type { DataType as TediousDataType } from "tedious/lib/data-type";
17
+
18
+ const logger = createConsola().withTag("mssql-db-conn");
19
+
20
+ /**
21
+ * MSSQL 데이터베이스 연결 클래스
22
+ *
23
+ * tedious 라이브러리를 사용하여 MSSQL/Azure SQL 연결을 관리합니다.
24
+ */
25
+ export class MssqlDbConn extends EventEmitter<{ close: void }> implements DbConn {
26
+ private readonly _timeout = DB_CONN_DEFAULT_TIMEOUT;
27
+
28
+ private _conn?: tediousType.Connection;
29
+ private _connTimeout?: ReturnType<typeof setTimeout>;
30
+ private _requests: tediousType.Request[] = [];
31
+
32
+ isConnected = false;
33
+ isOnTransaction = false;
34
+
35
+ constructor(
36
+ private readonly _tedious: typeof import("tedious"),
37
+ readonly config: MssqlDbConnConfig,
38
+ ) {
39
+ super();
40
+ }
41
+
42
+ async connect(): Promise<void> {
43
+ if (this.isConnected) {
44
+ throw new SdError(DB_CONN_ERRORS.ALREADY_CONNECTED);
45
+ }
46
+
47
+ const conn = new this._tedious.Connection({
48
+ server: this.config.host,
49
+ authentication: {
50
+ type: "default",
51
+ options: {
52
+ userName: this.config.username,
53
+ password: this.config.password,
54
+ },
55
+ },
56
+ options: {
57
+ database: this.config.database,
58
+ port: this.config.port,
59
+ rowCollectionOnDone: true,
60
+ useUTC: false,
61
+ encrypt: this.config.dialect === "mssql-azure",
62
+ requestTimeout: this._timeout,
63
+ trustServerCertificate: true,
64
+ connectTimeout: this._timeout * 5,
65
+ } as tediousType.ConnectionOptions,
66
+ });
67
+
68
+ conn.on("infoMessage", (info) => {
69
+ logger.debug("info", info.message);
70
+ });
71
+
72
+ conn.on("errorMessage", (error) => {
73
+ logger.error("errorMessage", error.message);
74
+ });
75
+
76
+ conn.on("error", (error) => {
77
+ logger.error("error", error.message);
78
+ });
79
+
80
+ conn.on("end", () => {
81
+ this.emit("close");
82
+ this._resetState();
83
+ });
84
+
85
+ await new Promise<void>((resolve, reject) => {
86
+ conn.connect((err: Error | undefined) => {
87
+ if (err != null) {
88
+ reject(new SdError(err));
89
+ return;
90
+ }
91
+
92
+ this._startTimeout();
93
+ this.isConnected = true;
94
+ this.isOnTransaction = false;
95
+ resolve();
96
+ });
97
+ });
98
+
99
+ this._conn = conn;
100
+ }
101
+
102
+ async close(): Promise<void> {
103
+ this._stopTimeout();
104
+
105
+ if (this._conn == null || !this.isConnected) {
106
+ return;
107
+ }
108
+
109
+ const conn = this._conn;
110
+
111
+ // 진행 중인 요청 취소
112
+ conn.cancel();
113
+ await waitUntil(() => this._requests.length < 1, 30000, 100);
114
+
115
+ // 연결 종료 대기
116
+ await new Promise<void>((resolve) => {
117
+ conn.on("end", () => {
118
+ waitUntil(() => this._conn == null, 30000, 100)
119
+ .then(() => resolve())
120
+ .catch(() => resolve());
121
+ });
122
+ conn.close();
123
+ });
124
+ }
125
+
126
+ async beginTransaction(isolationLevel?: IsolationLevel): Promise<void> {
127
+ this._assertConnected();
128
+ this._startTimeout();
129
+
130
+ const conn = this._conn!;
131
+
132
+ await new Promise<void>((resolve, reject) => {
133
+ conn.beginTransaction(
134
+ (err) => {
135
+ if (err != null) {
136
+ reject(new SdError(err));
137
+ return;
138
+ }
139
+
140
+ this.isOnTransaction = true;
141
+ resolve();
142
+ },
143
+ "",
144
+ this._tedious.ISOLATION_LEVEL[isolationLevel ?? this.config.defaultIsolationLevel ?? "READ_UNCOMMITTED"],
145
+ );
146
+ });
147
+ }
148
+
149
+ async commitTransaction(): Promise<void> {
150
+ this._assertConnected();
151
+ this._startTimeout();
152
+
153
+ const conn = this._conn!;
154
+
155
+ await new Promise<void>((resolve, reject) => {
156
+ conn.commitTransaction((err) => {
157
+ if (err != null) {
158
+ reject(new SdError(err));
159
+ return;
160
+ }
161
+
162
+ this.isOnTransaction = false;
163
+ resolve();
164
+ });
165
+ });
166
+ }
167
+
168
+ async rollbackTransaction(): Promise<void> {
169
+ this._assertConnected();
170
+ this._startTimeout();
171
+
172
+ const conn = this._conn!;
173
+
174
+ await new Promise<void>((resolve, reject) => {
175
+ conn.rollbackTransaction((err) => {
176
+ if (err != null) {
177
+ reject(new SdError(err));
178
+ return;
179
+ }
180
+
181
+ this.isOnTransaction = false;
182
+ resolve();
183
+ });
184
+ });
185
+ }
186
+
187
+ async execute(queries: string[]): Promise<unknown[][]> {
188
+ const results: unknown[][] = [];
189
+ for (const query of queries.filter((item) => !strIsNullOrEmpty(item))) {
190
+ const resultItems = await this.executeParametrized(query);
191
+ results.push(...resultItems);
192
+ }
193
+
194
+ return results;
195
+ }
196
+
197
+ async executeParametrized(query: string, params?: unknown[]): Promise<unknown[][]> {
198
+ this._assertConnected();
199
+ this._startTimeout();
200
+
201
+ const conn = this._conn!;
202
+
203
+ const results: unknown[][] = [];
204
+
205
+ logger.debug("쿼리 실행", { queryLength: query.length, params });
206
+ await new Promise<void>((resolve, reject) => {
207
+ let rejected = false;
208
+ const queryRequest = new this._tedious.Request(query, (err) => {
209
+ if (err != null) {
210
+ rejected = true;
211
+ this._requests = this._requests.filter((r) => r !== queryRequest);
212
+
213
+ const errRec = err as unknown as Record<string, unknown>;
214
+ if (errRec["code"] === "ECANCEL") {
215
+ reject(new SdError(err, "쿼리가 취소되었습니다."));
216
+ } else {
217
+ const lineNumber = errRec["lineNumber"] as number | undefined;
218
+ if (lineNumber != null && lineNumber > 0) {
219
+ const splitQuery = query.split("\n");
220
+ splitQuery[lineNumber - 1] = "==> " + splitQuery[lineNumber - 1];
221
+ reject(new SdError(err, `쿼리 수행중 오류발생\n-- query\n${splitQuery.join("\n")}\n--`));
222
+ } else {
223
+ reject(new SdError(err, `쿼리 수행중 오류발생\n-- query\n${query}\n--`));
224
+ }
225
+ }
226
+ }
227
+ });
228
+
229
+ queryRequest
230
+ .on("done", (_rowCount, _more, rst) => {
231
+ this._startTimeout();
232
+
233
+ if (rejected) {
234
+ return;
235
+ }
236
+
237
+ results.push(this._parseRowsToRecords(rst));
238
+ })
239
+ .on("doneInProc", (_rowCount, _more, rst) => {
240
+ this._startTimeout();
241
+
242
+ if (rejected) {
243
+ return;
244
+ }
245
+
246
+ results.push(this._parseRowsToRecords(rst));
247
+ })
248
+ .on("error", (err) => {
249
+ this._startTimeout();
250
+
251
+ if (rejected) {
252
+ return;
253
+ }
254
+
255
+ rejected = true;
256
+ this._requests = this._requests.filter((r) => r !== queryRequest);
257
+ reject(new SdError(err, `쿼리 수행중 오류발생\n-- query\n${query}\n--`));
258
+ })
259
+ .on("requestCompleted", () => {
260
+ this._startTimeout();
261
+
262
+ if (rejected) {
263
+ return;
264
+ }
265
+
266
+ this._requests = this._requests.filter((r) => r !== queryRequest);
267
+ resolve();
268
+ });
269
+
270
+ this._requests.push(queryRequest);
271
+
272
+ if (params != null) {
273
+ for (let i = 0; i < params.length; i++) {
274
+ const paramValue = params[i];
275
+ const paramName = `p${i}`;
276
+ const type = this._guessTediousType(paramValue);
277
+
278
+ queryRequest.addParameter(paramName, type, paramValue);
279
+ }
280
+
281
+ conn.execSql(queryRequest);
282
+ } else {
283
+ conn.execSqlBatch(queryRequest);
284
+ }
285
+ });
286
+
287
+ return results;
288
+ }
289
+
290
+ async bulkInsert(
291
+ tableName: string,
292
+ columnMetas: Record<string, ColumnMeta>,
293
+ records: Record<string, unknown>[],
294
+ ): Promise<void> {
295
+ if (records.length === 0) return;
296
+
297
+ this._assertConnected();
298
+ this._startTimeout();
299
+
300
+ const tediousColumnDefs = Object.entries(columnMetas).map(([name, meta]) =>
301
+ this._convertColumnMetaToTediousBulkColumnDef(name, meta),
302
+ );
303
+
304
+ await new Promise<void>((resolve, reject) => {
305
+ const bulkLoad = this._conn!.newBulkLoad(tableName, (err) => {
306
+ if (err != null) {
307
+ reject(
308
+ new SdError(
309
+ err,
310
+ `Bulk Insert 오류발생\n${jsonStringify(tediousColumnDefs)}\n-- data\n${jsonStringify(records).substring(0, 10000)}...\n--`,
311
+ ),
312
+ );
313
+ return;
314
+ }
315
+ resolve();
316
+ });
317
+
318
+ const colNames = Object.keys(columnMetas);
319
+
320
+ for (const tediousColumnDef of tediousColumnDefs) {
321
+ bulkLoad.addColumn(tediousColumnDef.name, tediousColumnDef.type, tediousColumnDef.options);
322
+ }
323
+
324
+ // 레코드를 row 배열로 변환 (컬럼 순서 유지, 값 변환 포함)
325
+ const rows = records.map((record) =>
326
+ colNames.map((colName) => {
327
+ const val = record[colName];
328
+ if (val instanceof Uuid) return val.toString();
329
+ // eslint-disable-next-line no-restricted-globals -- tedious 라이브러리가 Buffer를 요구함
330
+ if (val instanceof Uint8Array) return Buffer.from(val);
331
+ if (val instanceof DateTime) return val.date;
332
+ if (val instanceof DateOnly) return val.date;
333
+ if (val instanceof Time) return val.toFormatString("HH:mm:ss");
334
+ return val;
335
+ }),
336
+ );
337
+
338
+ this._conn!.execBulkLoad(bulkLoad, rows);
339
+ });
340
+ }
341
+
342
+ // ─────────────────────────────────────────────
343
+ // Private helpers
344
+ // ─────────────────────────────────────────────
345
+
346
+ private _assertConnected(): void {
347
+ if (this._conn == null || !this.isConnected) {
348
+ throw new SdError(DB_CONN_ERRORS.NOT_CONNECTED);
349
+ }
350
+ }
351
+
352
+ private _parseRowsToRecords(
353
+ rows: Array<Array<{ metadata: { colName: string }; value: unknown }>> | undefined,
354
+ ): Record<string, unknown>[] {
355
+ return (rows ?? []).map((item) => {
356
+ const resultItem: Record<string, unknown> = {};
357
+ for (const col of item) {
358
+ resultItem[col.metadata.colName] = col.value;
359
+ }
360
+ return resultItem;
361
+ });
362
+ }
363
+
364
+ private _resetState(): void {
365
+ this.isConnected = false;
366
+ this.isOnTransaction = false;
367
+ this._conn = undefined;
368
+ this._requests = [];
369
+ }
370
+
371
+ private _stopTimeout(): void {
372
+ if (this._connTimeout != null) {
373
+ clearTimeout(this._connTimeout);
374
+ }
375
+ }
376
+
377
+ private _startTimeout(): void {
378
+ this._stopTimeout();
379
+ this._connTimeout = setTimeout(() => {
380
+ this.close().catch((err) => {
381
+ logger.error("close error", err instanceof Error ? err.message : String(err));
382
+ });
383
+ }, this._timeout * 2);
384
+ }
385
+
386
+ private _convertColumnMetaToTediousBulkColumnDef(
387
+ name: string,
388
+ meta: ColumnMeta,
389
+ ): {
390
+ name: string;
391
+ type: TediousDataType;
392
+ options: TediousColumnOptions;
393
+ } {
394
+ const tediousDataType = this._convertDataTypeToTediousBulkColumnType(meta.dataType);
395
+ return {
396
+ name,
397
+ type: tediousDataType.type,
398
+ options: {
399
+ length: tediousDataType.length,
400
+ nullable: meta.nullable ?? false,
401
+ precision: tediousDataType.precision,
402
+ scale: tediousDataType.scale,
403
+ },
404
+ };
405
+ }
406
+
407
+ private _convertDataTypeToTediousBulkColumnType(dataType: DataType): {
408
+ type: TediousDataType;
409
+ length?: number;
410
+ precision?: number;
411
+ scale?: number;
412
+ } {
413
+ switch (dataType.type) {
414
+ case "int":
415
+ return { type: this._tedious.TYPES.Int };
416
+ case "bigint":
417
+ return { type: this._tedious.TYPES.BigInt };
418
+ case "float":
419
+ return { type: this._tedious.TYPES.Real };
420
+ case "double":
421
+ return { type: this._tedious.TYPES.Float };
422
+ case "decimal":
423
+ return {
424
+ type: this._tedious.TYPES.Decimal,
425
+ precision: dataType.precision,
426
+ scale: dataType.scale,
427
+ };
428
+ case "varchar":
429
+ return { type: this._tedious.TYPES.NVarChar, length: dataType.length };
430
+ case "char":
431
+ return { type: this._tedious.TYPES.NChar, length: dataType.length };
432
+ case "text":
433
+ return { type: this._tedious.TYPES.NText };
434
+ case "binary":
435
+ return { type: this._tedious.TYPES.VarBinary, length: Infinity };
436
+ case "boolean":
437
+ return { type: this._tedious.TYPES.Bit };
438
+ case "datetime":
439
+ return { type: this._tedious.TYPES.DateTime2 };
440
+ case "date":
441
+ return { type: this._tedious.TYPES.Date };
442
+ case "time":
443
+ return { type: this._tedious.TYPES.Time };
444
+ case "uuid":
445
+ return { type: this._tedious.TYPES.UniqueIdentifier };
446
+ default:
447
+ throw new SdError(`지원하지 않는 DataType: ${JSON.stringify(dataType)}`);
448
+ }
449
+ }
450
+
451
+ /**
452
+ * 값의 타입을 추론하여 Tedious 데이터 타입 반환
453
+ *
454
+ * @param value - 타입을 추론할 값 (null/undefined 전달 시 오류 발생)
455
+ * @throws null/undefined가 전달되면 오류 발생
456
+ */
457
+ private _guessTediousType(value: unknown): TediousDataType {
458
+ if (value == null) {
459
+ throw new SdError("_guessTediousType: null/undefined 값은 지원하지 않습니다.");
460
+ }
461
+ if (typeof value === "string") {
462
+ return this._tedious.TYPES.NVarChar;
463
+ }
464
+ if (typeof value === "number") {
465
+ return Number.isInteger(value) ? this._tedious.TYPES.BigInt : this._tedious.TYPES.Decimal;
466
+ }
467
+ if (typeof value === "boolean") return this._tedious.TYPES.Bit;
468
+ if (value instanceof DateTime) return this._tedious.TYPES.DateTime2;
469
+ if (value instanceof DateOnly) return this._tedious.TYPES.Date;
470
+ if (value instanceof Time) return this._tedious.TYPES.Time;
471
+ if (value instanceof Uuid) return this._tedious.TYPES.UniqueIdentifier;
472
+ if (value instanceof Uint8Array) return this._tedious.TYPES.VarBinary;
473
+
474
+ throw new SdError(`알 수 없는 값 타입: ${typeof value}`);
475
+ }
476
+ }
477
+
478
+ interface TediousColumnOptions {
479
+ length?: number;
480
+ precision?: number;
481
+ scale?: number;
482
+ nullable?: boolean;
483
+ }