@simplysm/orm-node 13.0.0-beta.11

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 (162) hide show
  1. package/README.md +418 -0
  2. package/dist/connections/mssql-db-conn.js +386 -0
  3. package/dist/connections/mssql-db-conn.js.map +7 -0
  4. package/dist/connections/mysql-db-conn.js +227 -0
  5. package/dist/connections/mysql-db-conn.js.map +7 -0
  6. package/dist/connections/postgresql-db-conn.js +191 -0
  7. package/dist/connections/postgresql-db-conn.js.map +7 -0
  8. package/dist/core-common/src/common.types.d.ts +74 -0
  9. package/dist/core-common/src/common.types.d.ts.map +1 -0
  10. package/dist/core-common/src/env.d.ts +6 -0
  11. package/dist/core-common/src/env.d.ts.map +1 -0
  12. package/dist/core-common/src/errors/argument-error.d.ts +25 -0
  13. package/dist/core-common/src/errors/argument-error.d.ts.map +1 -0
  14. package/dist/core-common/src/errors/not-implemented-error.d.ts +29 -0
  15. package/dist/core-common/src/errors/not-implemented-error.d.ts.map +1 -0
  16. package/dist/core-common/src/errors/sd-error.d.ts +27 -0
  17. package/dist/core-common/src/errors/sd-error.d.ts.map +1 -0
  18. package/dist/core-common/src/errors/timeout-error.d.ts +31 -0
  19. package/dist/core-common/src/errors/timeout-error.d.ts.map +1 -0
  20. package/dist/core-common/src/extensions/arr-ext.d.ts +15 -0
  21. package/dist/core-common/src/extensions/arr-ext.d.ts.map +1 -0
  22. package/dist/core-common/src/extensions/arr-ext.helpers.d.ts +19 -0
  23. package/dist/core-common/src/extensions/arr-ext.helpers.d.ts.map +1 -0
  24. package/dist/core-common/src/extensions/arr-ext.types.d.ts +215 -0
  25. package/dist/core-common/src/extensions/arr-ext.types.d.ts.map +1 -0
  26. package/dist/core-common/src/extensions/map-ext.d.ts +57 -0
  27. package/dist/core-common/src/extensions/map-ext.d.ts.map +1 -0
  28. package/dist/core-common/src/extensions/set-ext.d.ts +36 -0
  29. package/dist/core-common/src/extensions/set-ext.d.ts.map +1 -0
  30. package/dist/core-common/src/features/debounce-queue.d.ts +53 -0
  31. package/dist/core-common/src/features/debounce-queue.d.ts.map +1 -0
  32. package/dist/core-common/src/features/event-emitter.d.ts +66 -0
  33. package/dist/core-common/src/features/event-emitter.d.ts.map +1 -0
  34. package/dist/core-common/src/features/serial-queue.d.ts +47 -0
  35. package/dist/core-common/src/features/serial-queue.d.ts.map +1 -0
  36. package/dist/core-common/src/index.d.ts +32 -0
  37. package/dist/core-common/src/index.d.ts.map +1 -0
  38. package/dist/core-common/src/types/date-only.d.ts +152 -0
  39. package/dist/core-common/src/types/date-only.d.ts.map +1 -0
  40. package/dist/core-common/src/types/date-time.d.ts +96 -0
  41. package/dist/core-common/src/types/date-time.d.ts.map +1 -0
  42. package/dist/core-common/src/types/lazy-gc-map.d.ts +80 -0
  43. package/dist/core-common/src/types/lazy-gc-map.d.ts.map +1 -0
  44. package/dist/core-common/src/types/time.d.ts +68 -0
  45. package/dist/core-common/src/types/time.d.ts.map +1 -0
  46. package/dist/core-common/src/types/uuid.d.ts +35 -0
  47. package/dist/core-common/src/types/uuid.d.ts.map +1 -0
  48. package/dist/core-common/src/utils/bytes.d.ts +51 -0
  49. package/dist/core-common/src/utils/bytes.d.ts.map +1 -0
  50. package/dist/core-common/src/utils/date-format.d.ts +90 -0
  51. package/dist/core-common/src/utils/date-format.d.ts.map +1 -0
  52. package/dist/core-common/src/utils/json.d.ts +34 -0
  53. package/dist/core-common/src/utils/json.d.ts.map +1 -0
  54. package/dist/core-common/src/utils/num.d.ts +60 -0
  55. package/dist/core-common/src/utils/num.d.ts.map +1 -0
  56. package/dist/core-common/src/utils/obj.d.ts +258 -0
  57. package/dist/core-common/src/utils/obj.d.ts.map +1 -0
  58. package/dist/core-common/src/utils/path.d.ts +23 -0
  59. package/dist/core-common/src/utils/path.d.ts.map +1 -0
  60. package/dist/core-common/src/utils/primitive.d.ts +18 -0
  61. package/dist/core-common/src/utils/primitive.d.ts.map +1 -0
  62. package/dist/core-common/src/utils/str.d.ts +103 -0
  63. package/dist/core-common/src/utils/str.d.ts.map +1 -0
  64. package/dist/core-common/src/utils/template-strings.d.ts +84 -0
  65. package/dist/core-common/src/utils/template-strings.d.ts.map +1 -0
  66. package/dist/core-common/src/utils/transferable.d.ts +47 -0
  67. package/dist/core-common/src/utils/transferable.d.ts.map +1 -0
  68. package/dist/core-common/src/utils/wait.d.ts +19 -0
  69. package/dist/core-common/src/utils/wait.d.ts.map +1 -0
  70. package/dist/core-common/src/utils/xml.d.ts +36 -0
  71. package/dist/core-common/src/utils/xml.d.ts.map +1 -0
  72. package/dist/core-common/src/zip/sd-zip.d.ts +80 -0
  73. package/dist/core-common/src/zip/sd-zip.d.ts.map +1 -0
  74. package/dist/db-conn-factory.js +88 -0
  75. package/dist/db-conn-factory.js.map +7 -0
  76. package/dist/index.js +9 -0
  77. package/dist/index.js.map +7 -0
  78. package/dist/node-db-context-executor.js +129 -0
  79. package/dist/node-db-context-executor.js.map +7 -0
  80. package/dist/orm-common/src/db-context.d.ts +669 -0
  81. package/dist/orm-common/src/db-context.d.ts.map +1 -0
  82. package/dist/orm-common/src/errors/db-transaction-error.d.ts +51 -0
  83. package/dist/orm-common/src/errors/db-transaction-error.d.ts.map +1 -0
  84. package/dist/orm-common/src/exec/executable.d.ts +79 -0
  85. package/dist/orm-common/src/exec/executable.d.ts.map +1 -0
  86. package/dist/orm-common/src/exec/queryable.d.ts +708 -0
  87. package/dist/orm-common/src/exec/queryable.d.ts.map +1 -0
  88. package/dist/orm-common/src/exec/search-parser.d.ts +72 -0
  89. package/dist/orm-common/src/exec/search-parser.d.ts.map +1 -0
  90. package/dist/orm-common/src/expr/expr-unit.d.ts +25 -0
  91. package/dist/orm-common/src/expr/expr-unit.d.ts.map +1 -0
  92. package/dist/orm-common/src/expr/expr.d.ts +1369 -0
  93. package/dist/orm-common/src/expr/expr.d.ts.map +1 -0
  94. package/dist/orm-common/src/index.d.ts +32 -0
  95. package/dist/orm-common/src/index.d.ts.map +1 -0
  96. package/dist/orm-common/src/models/system-migration.d.ts +10 -0
  97. package/dist/orm-common/src/models/system-migration.d.ts.map +1 -0
  98. package/dist/orm-common/src/query-builder/base/expr-renderer-base.d.ts +95 -0
  99. package/dist/orm-common/src/query-builder/base/expr-renderer-base.d.ts.map +1 -0
  100. package/dist/orm-common/src/query-builder/base/query-builder-base.d.ts +66 -0
  101. package/dist/orm-common/src/query-builder/base/query-builder-base.d.ts.map +1 -0
  102. package/dist/orm-common/src/query-builder/mssql/mssql-expr-renderer.d.ts +84 -0
  103. package/dist/orm-common/src/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -0
  104. package/dist/orm-common/src/query-builder/mssql/mssql-query-builder.d.ts +45 -0
  105. package/dist/orm-common/src/query-builder/mssql/mssql-query-builder.d.ts.map +1 -0
  106. package/dist/orm-common/src/query-builder/mysql/mysql-expr-renderer.d.ts +84 -0
  107. package/dist/orm-common/src/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -0
  108. package/dist/orm-common/src/query-builder/mysql/mysql-query-builder.d.ts +54 -0
  109. package/dist/orm-common/src/query-builder/mysql/mysql-query-builder.d.ts.map +1 -0
  110. package/dist/orm-common/src/query-builder/postgresql/postgresql-expr-renderer.d.ts +84 -0
  111. package/dist/orm-common/src/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -0
  112. package/dist/orm-common/src/query-builder/postgresql/postgresql-query-builder.d.ts +52 -0
  113. package/dist/orm-common/src/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -0
  114. package/dist/orm-common/src/query-builder/query-builder.d.ts +7 -0
  115. package/dist/orm-common/src/query-builder/query-builder.d.ts.map +1 -0
  116. package/dist/orm-common/src/schema/factory/column-builder.d.ts +394 -0
  117. package/dist/orm-common/src/schema/factory/column-builder.d.ts.map +1 -0
  118. package/dist/orm-common/src/schema/factory/index-builder.d.ts +151 -0
  119. package/dist/orm-common/src/schema/factory/index-builder.d.ts.map +1 -0
  120. package/dist/orm-common/src/schema/factory/relation-builder.d.ts +337 -0
  121. package/dist/orm-common/src/schema/factory/relation-builder.d.ts.map +1 -0
  122. package/dist/orm-common/src/schema/procedure-builder.d.ts +202 -0
  123. package/dist/orm-common/src/schema/procedure-builder.d.ts.map +1 -0
  124. package/dist/orm-common/src/schema/table-builder.d.ts +259 -0
  125. package/dist/orm-common/src/schema/table-builder.d.ts.map +1 -0
  126. package/dist/orm-common/src/schema/view-builder.d.ts +183 -0
  127. package/dist/orm-common/src/schema/view-builder.d.ts.map +1 -0
  128. package/dist/orm-common/src/types/column.d.ts +172 -0
  129. package/dist/orm-common/src/types/column.d.ts.map +1 -0
  130. package/dist/orm-common/src/types/db.d.ts +175 -0
  131. package/dist/orm-common/src/types/db.d.ts.map +1 -0
  132. package/dist/orm-common/src/types/expr.d.ts +474 -0
  133. package/dist/orm-common/src/types/expr.d.ts.map +1 -0
  134. package/dist/orm-common/src/types/query-def.d.ts +351 -0
  135. package/dist/orm-common/src/types/query-def.d.ts.map +1 -0
  136. package/dist/orm-common/src/utils/result-parser.d.ts +38 -0
  137. package/dist/orm-common/src/utils/result-parser.d.ts.map +1 -0
  138. package/dist/orm-node/src/connections/mssql-db-conn.d.ts +44 -0
  139. package/dist/orm-node/src/connections/mssql-db-conn.d.ts.map +1 -0
  140. package/dist/orm-node/src/connections/mysql-db-conn.d.ts +38 -0
  141. package/dist/orm-node/src/connections/mysql-db-conn.d.ts.map +1 -0
  142. package/dist/orm-node/src/connections/postgresql-db-conn.d.ts +39 -0
  143. package/dist/orm-node/src/connections/postgresql-db-conn.d.ts.map +1 -0
  144. package/dist/orm-node/src/db-conn-factory.d.ts +25 -0
  145. package/dist/orm-node/src/db-conn-factory.d.ts.map +1 -0
  146. package/dist/orm-node/src/index.d.ts +9 -0
  147. package/dist/orm-node/src/index.d.ts.map +1 -0
  148. package/dist/orm-node/src/node-db-context-executor.d.ts +77 -0
  149. package/dist/orm-node/src/node-db-context-executor.d.ts.map +1 -0
  150. package/dist/orm-node/src/pooled-db-conn.d.ts +79 -0
  151. package/dist/orm-node/src/pooled-db-conn.d.ts.map +1 -0
  152. package/dist/orm-node/src/sd-orm.d.ts +78 -0
  153. package/dist/orm-node/src/sd-orm.d.ts.map +1 -0
  154. package/dist/orm-node/src/types/db-conn.d.ts +159 -0
  155. package/dist/orm-node/src/types/db-conn.d.ts.map +1 -0
  156. package/dist/pooled-db-conn.js +134 -0
  157. package/dist/pooled-db-conn.js.map +7 -0
  158. package/dist/sd-orm.js +44 -0
  159. package/dist/sd-orm.js.map +7 -0
  160. package/dist/types/db-conn.js +17 -0
  161. package/dist/types/db-conn.js.map +7 -0
  162. package/package.json +53 -0
@@ -0,0 +1,386 @@
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 { DB_CONN_DEFAULT_TIMEOUT, DB_CONN_ERRORS } from "../types/db-conn";
14
+ const logger = createConsola().withTag("mssql-db-conn");
15
+ class MssqlDbConn extends EventEmitter {
16
+ constructor(_tedious, config) {
17
+ super();
18
+ this._tedious = _tedious;
19
+ this.config = config;
20
+ }
21
+ _timeout = DB_CONN_DEFAULT_TIMEOUT;
22
+ _conn;
23
+ _connTimeout;
24
+ _requests = [];
25
+ isConnected = false;
26
+ isOnTransaction = false;
27
+ async connect() {
28
+ if (this.isConnected) {
29
+ throw new SdError(DB_CONN_ERRORS.ALREADY_CONNECTED);
30
+ }
31
+ const conn = new this._tedious.Connection({
32
+ server: this.config.host,
33
+ authentication: {
34
+ type: "default",
35
+ options: {
36
+ userName: this.config.username,
37
+ password: this.config.password
38
+ }
39
+ },
40
+ options: {
41
+ database: this.config.database,
42
+ port: this.config.port,
43
+ rowCollectionOnDone: true,
44
+ useUTC: false,
45
+ encrypt: this.config.dialect === "mssql-azure",
46
+ requestTimeout: this._timeout,
47
+ trustServerCertificate: true,
48
+ connectTimeout: this._timeout * 5
49
+ }
50
+ });
51
+ conn.on("infoMessage", (info) => {
52
+ logger.debug("info", info.message);
53
+ });
54
+ conn.on("errorMessage", (error) => {
55
+ logger.error("errorMessage", error.message);
56
+ });
57
+ conn.on("error", (error) => {
58
+ logger.error("error", error.message);
59
+ });
60
+ conn.on("end", () => {
61
+ this.emit("close");
62
+ this._resetState();
63
+ });
64
+ await new Promise((resolve, reject) => {
65
+ conn.connect((err) => {
66
+ if (err != null) {
67
+ reject(new SdError(err));
68
+ return;
69
+ }
70
+ this._startTimeout();
71
+ this.isConnected = true;
72
+ this.isOnTransaction = false;
73
+ resolve();
74
+ });
75
+ });
76
+ this._conn = conn;
77
+ }
78
+ async close() {
79
+ this._stopTimeout();
80
+ if (this._conn == null || !this.isConnected) {
81
+ return;
82
+ }
83
+ const conn = this._conn;
84
+ conn.cancel();
85
+ await waitUntil(() => this._requests.length < 1, 3e4, 100);
86
+ await new Promise((resolve) => {
87
+ conn.on("end", () => {
88
+ waitUntil(() => this._conn == null, 3e4, 100).then(() => resolve()).catch(() => resolve());
89
+ });
90
+ conn.close();
91
+ });
92
+ }
93
+ async beginTransaction(isolationLevel) {
94
+ this._assertConnected();
95
+ this._startTimeout();
96
+ const conn = this._conn;
97
+ await new Promise((resolve, reject) => {
98
+ conn.beginTransaction(
99
+ (err) => {
100
+ if (err != null) {
101
+ reject(new SdError(err));
102
+ return;
103
+ }
104
+ this.isOnTransaction = true;
105
+ resolve();
106
+ },
107
+ "",
108
+ this._tedious.ISOLATION_LEVEL[isolationLevel ?? this.config.defaultIsolationLevel ?? "READ_UNCOMMITTED"]
109
+ );
110
+ });
111
+ }
112
+ async commitTransaction() {
113
+ this._assertConnected();
114
+ this._startTimeout();
115
+ const conn = this._conn;
116
+ await new Promise((resolve, reject) => {
117
+ conn.commitTransaction((err) => {
118
+ if (err != null) {
119
+ reject(new SdError(err));
120
+ return;
121
+ }
122
+ this.isOnTransaction = false;
123
+ resolve();
124
+ });
125
+ });
126
+ }
127
+ async rollbackTransaction() {
128
+ this._assertConnected();
129
+ this._startTimeout();
130
+ const conn = this._conn;
131
+ await new Promise((resolve, reject) => {
132
+ conn.rollbackTransaction((err) => {
133
+ if (err != null) {
134
+ reject(new SdError(err));
135
+ return;
136
+ }
137
+ this.isOnTransaction = false;
138
+ resolve();
139
+ });
140
+ });
141
+ }
142
+ async execute(queries) {
143
+ const results = [];
144
+ for (const query of queries.filter((item) => !strIsNullOrEmpty(item))) {
145
+ const resultItems = await this.executeParametrized(query);
146
+ results.push(...resultItems);
147
+ }
148
+ return results;
149
+ }
150
+ async executeParametrized(query, params) {
151
+ this._assertConnected();
152
+ this._startTimeout();
153
+ const conn = this._conn;
154
+ const results = [];
155
+ logger.debug("\uCFFC\uB9AC \uC2E4\uD589", { queryLength: query.length, params });
156
+ await new Promise((resolve, reject) => {
157
+ let rejected = false;
158
+ const queryRequest = new this._tedious.Request(query, (err) => {
159
+ if (err != null) {
160
+ rejected = true;
161
+ this._requests = this._requests.filter((r) => r !== queryRequest);
162
+ const errRec = err;
163
+ if (errRec["code"] === "ECANCEL") {
164
+ reject(new SdError(err, "\uCFFC\uB9AC\uAC00 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
165
+ } else {
166
+ const lineNumber = errRec["lineNumber"];
167
+ if (lineNumber != null && lineNumber > 0) {
168
+ const splitQuery = query.split("\n");
169
+ splitQuery[lineNumber - 1] = "==> " + splitQuery[lineNumber - 1];
170
+ reject(new SdError(err, `\uCFFC\uB9AC \uC218\uD589\uC911 \uC624\uB958\uBC1C\uC0DD
171
+ -- query
172
+ ${splitQuery.join("\n")}
173
+ --`));
174
+ } else {
175
+ reject(new SdError(err, `\uCFFC\uB9AC \uC218\uD589\uC911 \uC624\uB958\uBC1C\uC0DD
176
+ -- query
177
+ ${query}
178
+ --`));
179
+ }
180
+ }
181
+ }
182
+ });
183
+ queryRequest.on("done", (_rowCount, _more, rst) => {
184
+ this._startTimeout();
185
+ if (rejected) {
186
+ return;
187
+ }
188
+ results.push(this._parseRowsToRecords(rst));
189
+ }).on("doneInProc", (_rowCount, _more, rst) => {
190
+ this._startTimeout();
191
+ if (rejected) {
192
+ return;
193
+ }
194
+ results.push(this._parseRowsToRecords(rst));
195
+ }).on("error", (err) => {
196
+ this._startTimeout();
197
+ if (rejected) {
198
+ return;
199
+ }
200
+ rejected = true;
201
+ this._requests = this._requests.filter((r) => r !== queryRequest);
202
+ reject(new SdError(err, `\uCFFC\uB9AC \uC218\uD589\uC911 \uC624\uB958\uBC1C\uC0DD
203
+ -- query
204
+ ${query}
205
+ --`));
206
+ }).on("requestCompleted", () => {
207
+ this._startTimeout();
208
+ if (rejected) {
209
+ return;
210
+ }
211
+ this._requests = this._requests.filter((r) => r !== queryRequest);
212
+ resolve();
213
+ });
214
+ this._requests.push(queryRequest);
215
+ if (params != null) {
216
+ for (let i = 0; i < params.length; i++) {
217
+ const paramValue = params[i];
218
+ const paramName = `p${i}`;
219
+ const type = this._guessTediousType(paramValue);
220
+ queryRequest.addParameter(paramName, type, paramValue);
221
+ }
222
+ conn.execSql(queryRequest);
223
+ } else {
224
+ conn.execSqlBatch(queryRequest);
225
+ }
226
+ });
227
+ return results;
228
+ }
229
+ async bulkInsert(tableName, columnMetas, records) {
230
+ if (records.length === 0) return;
231
+ this._assertConnected();
232
+ this._startTimeout();
233
+ const tediousColumnDefs = Object.entries(columnMetas).map(
234
+ ([name, meta]) => this._convertColumnMetaToTediousBulkColumnDef(name, meta)
235
+ );
236
+ await new Promise((resolve, reject) => {
237
+ const bulkLoad = this._conn.newBulkLoad(tableName, (err) => {
238
+ if (err != null) {
239
+ reject(
240
+ new SdError(
241
+ err,
242
+ `Bulk Insert \uC624\uB958\uBC1C\uC0DD
243
+ ${jsonStringify(tediousColumnDefs)}
244
+ -- data
245
+ ${jsonStringify(records).substring(0, 1e4)}...
246
+ --`
247
+ )
248
+ );
249
+ return;
250
+ }
251
+ resolve();
252
+ });
253
+ const colNames = Object.keys(columnMetas);
254
+ for (const tediousColumnDef of tediousColumnDefs) {
255
+ bulkLoad.addColumn(tediousColumnDef.name, tediousColumnDef.type, tediousColumnDef.options);
256
+ }
257
+ const rows = records.map(
258
+ (record) => colNames.map((colName) => {
259
+ const val = record[colName];
260
+ if (val instanceof Uuid) return val.toString();
261
+ if (val instanceof Uint8Array) return Buffer.from(val);
262
+ if (val instanceof DateTime) return val.date;
263
+ if (val instanceof DateOnly) return val.date;
264
+ if (val instanceof Time) return val.toFormatString("HH:mm:ss");
265
+ return val;
266
+ })
267
+ );
268
+ this._conn.execBulkLoad(bulkLoad, rows);
269
+ });
270
+ }
271
+ // ─────────────────────────────────────────────
272
+ // Private helpers
273
+ // ─────────────────────────────────────────────
274
+ _assertConnected() {
275
+ if (this._conn == null || !this.isConnected) {
276
+ throw new SdError(DB_CONN_ERRORS.NOT_CONNECTED);
277
+ }
278
+ }
279
+ _parseRowsToRecords(rows) {
280
+ return (rows ?? []).map((item) => {
281
+ const resultItem = {};
282
+ for (const col of item) {
283
+ resultItem[col.metadata.colName] = col.value;
284
+ }
285
+ return resultItem;
286
+ });
287
+ }
288
+ _resetState() {
289
+ this.isConnected = false;
290
+ this.isOnTransaction = false;
291
+ this._conn = void 0;
292
+ this._requests = [];
293
+ }
294
+ _stopTimeout() {
295
+ if (this._connTimeout != null) {
296
+ clearTimeout(this._connTimeout);
297
+ }
298
+ }
299
+ _startTimeout() {
300
+ this._stopTimeout();
301
+ this._connTimeout = setTimeout(() => {
302
+ this.close().catch((err) => {
303
+ logger.error("close error", err instanceof Error ? err.message : String(err));
304
+ });
305
+ }, this._timeout * 2);
306
+ }
307
+ _convertColumnMetaToTediousBulkColumnDef(name, meta) {
308
+ const tediousDataType = this._convertDataTypeToTediousBulkColumnType(meta.dataType);
309
+ return {
310
+ name,
311
+ type: tediousDataType.type,
312
+ options: {
313
+ length: tediousDataType.length,
314
+ nullable: meta.nullable ?? false,
315
+ precision: tediousDataType.precision,
316
+ scale: tediousDataType.scale
317
+ }
318
+ };
319
+ }
320
+ _convertDataTypeToTediousBulkColumnType(dataType) {
321
+ switch (dataType.type) {
322
+ case "int":
323
+ return { type: this._tedious.TYPES.Int };
324
+ case "bigint":
325
+ return { type: this._tedious.TYPES.BigInt };
326
+ case "float":
327
+ return { type: this._tedious.TYPES.Real };
328
+ case "double":
329
+ return { type: this._tedious.TYPES.Float };
330
+ case "decimal":
331
+ return {
332
+ type: this._tedious.TYPES.Decimal,
333
+ precision: dataType.precision,
334
+ scale: dataType.scale
335
+ };
336
+ case "varchar":
337
+ return { type: this._tedious.TYPES.NVarChar, length: dataType.length };
338
+ case "char":
339
+ return { type: this._tedious.TYPES.NChar, length: dataType.length };
340
+ case "text":
341
+ return { type: this._tedious.TYPES.NText };
342
+ case "binary":
343
+ return { type: this._tedious.TYPES.VarBinary, length: Infinity };
344
+ case "boolean":
345
+ return { type: this._tedious.TYPES.Bit };
346
+ case "datetime":
347
+ return { type: this._tedious.TYPES.DateTime2 };
348
+ case "date":
349
+ return { type: this._tedious.TYPES.Date };
350
+ case "time":
351
+ return { type: this._tedious.TYPES.Time };
352
+ case "uuid":
353
+ return { type: this._tedious.TYPES.UniqueIdentifier };
354
+ default:
355
+ throw new SdError(`\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 DataType: ${JSON.stringify(dataType)}`);
356
+ }
357
+ }
358
+ /**
359
+ * 값의 타입을 추론하여 Tedious 데이터 타입 반환
360
+ *
361
+ * @param value - 타입을 추론할 값 (null/undefined 전달 시 오류 발생)
362
+ * @throws null/undefined가 전달되면 오류 발생
363
+ */
364
+ _guessTediousType(value) {
365
+ if (value == null) {
366
+ throw new SdError("_guessTediousType: null/undefined \uAC12\uC740 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
367
+ }
368
+ if (typeof value === "string") {
369
+ return this._tedious.TYPES.NVarChar;
370
+ }
371
+ if (typeof value === "number") {
372
+ return Number.isInteger(value) ? this._tedious.TYPES.BigInt : this._tedious.TYPES.Decimal;
373
+ }
374
+ if (typeof value === "boolean") return this._tedious.TYPES.Bit;
375
+ if (value instanceof DateTime) return this._tedious.TYPES.DateTime2;
376
+ if (value instanceof DateOnly) return this._tedious.TYPES.Date;
377
+ if (value instanceof Time) return this._tedious.TYPES.Time;
378
+ if (value instanceof Uuid) return this._tedious.TYPES.UniqueIdentifier;
379
+ if (value instanceof Uint8Array) return this._tedious.TYPES.VarBinary;
380
+ throw new SdError(`\uC54C \uC218 \uC5C6\uB294 \uAC12 \uD0C0\uC785: ${typeof value}`);
381
+ }
382
+ }
383
+ export {
384
+ MssqlDbConn
385
+ };
386
+ //# sourceMappingURL=mssql-db-conn.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/connections/mssql-db-conn.ts"],
4
+ "sourcesContent": ["import { createConsola } from \"consola\";\nimport {\n DateOnly,\n DateTime,\n jsonStringify,\n SdError,\n EventEmitter,\n strIsNullOrEmpty,\n Time,\n Uuid,\n waitUntil,\n} from \"@simplysm/core-common\";\nimport type { ColumnMeta, DataType, IsolationLevel } from \"@simplysm/orm-common\";\nimport { DB_CONN_DEFAULT_TIMEOUT, DB_CONN_ERRORS, type DbConn, type MssqlDbConnConfig } from \"../types/db-conn\";\nimport type tediousType from \"tedious\";\nimport type { DataType as TediousDataType } from \"tedious/lib/data-type\";\n\nconst logger = createConsola().withTag(\"mssql-db-conn\");\n\n/**\n * MSSQL \uB370\uC774\uD130\uBCA0\uC774\uC2A4 \uC5F0\uACB0 \uD074\uB798\uC2A4\n *\n * tedious \uB77C\uC774\uBE0C\uB7EC\uB9AC\uB97C \uC0AC\uC6A9\uD558\uC5EC MSSQL/Azure SQL \uC5F0\uACB0\uC744 \uAD00\uB9AC\uD569\uB2C8\uB2E4.\n */\nexport class MssqlDbConn extends EventEmitter<{ close: void }> implements DbConn {\n private readonly _timeout = DB_CONN_DEFAULT_TIMEOUT;\n\n private _conn?: tediousType.Connection;\n private _connTimeout?: ReturnType<typeof setTimeout>;\n private _requests: tediousType.Request[] = [];\n\n isConnected = false;\n isOnTransaction = false;\n\n constructor(\n private readonly _tedious: typeof import(\"tedious\"),\n readonly config: MssqlDbConnConfig,\n ) {\n super();\n }\n\n async connect(): Promise<void> {\n if (this.isConnected) {\n throw new SdError(DB_CONN_ERRORS.ALREADY_CONNECTED);\n }\n\n const conn = new this._tedious.Connection({\n server: this.config.host,\n authentication: {\n type: \"default\",\n options: {\n userName: this.config.username,\n password: this.config.password,\n },\n },\n options: {\n database: this.config.database,\n port: this.config.port,\n rowCollectionOnDone: true,\n useUTC: false,\n encrypt: this.config.dialect === \"mssql-azure\",\n requestTimeout: this._timeout,\n trustServerCertificate: true,\n connectTimeout: this._timeout * 5,\n } as tediousType.ConnectionOptions,\n });\n\n conn.on(\"infoMessage\", (info) => {\n logger.debug(\"info\", info.message);\n });\n\n conn.on(\"errorMessage\", (error) => {\n logger.error(\"errorMessage\", error.message);\n });\n\n conn.on(\"error\", (error) => {\n logger.error(\"error\", error.message);\n });\n\n conn.on(\"end\", () => {\n this.emit(\"close\");\n this._resetState();\n });\n\n await new Promise<void>((resolve, reject) => {\n conn.connect((err: Error | undefined) => {\n if (err != null) {\n reject(new SdError(err));\n return;\n }\n\n this._startTimeout();\n this.isConnected = true;\n this.isOnTransaction = false;\n resolve();\n });\n });\n\n this._conn = conn;\n }\n\n async close(): Promise<void> {\n this._stopTimeout();\n\n if (this._conn == null || !this.isConnected) {\n return;\n }\n\n const conn = this._conn;\n\n // \uC9C4\uD589 \uC911\uC778 \uC694\uCCAD \uCDE8\uC18C\n conn.cancel();\n await waitUntil(() => this._requests.length < 1, 30000, 100);\n\n // \uC5F0\uACB0 \uC885\uB8CC \uB300\uAE30\n await new Promise<void>((resolve) => {\n conn.on(\"end\", () => {\n waitUntil(() => this._conn == null, 30000, 100)\n .then(() => resolve())\n .catch(() => resolve());\n });\n conn.close();\n });\n }\n\n async beginTransaction(isolationLevel?: IsolationLevel): Promise<void> {\n this._assertConnected();\n this._startTimeout();\n\n const conn = this._conn!;\n\n await new Promise<void>((resolve, reject) => {\n conn.beginTransaction(\n (err) => {\n if (err != null) {\n reject(new SdError(err));\n return;\n }\n\n this.isOnTransaction = true;\n resolve();\n },\n \"\",\n this._tedious.ISOLATION_LEVEL[isolationLevel ?? this.config.defaultIsolationLevel ?? \"READ_UNCOMMITTED\"],\n );\n });\n }\n\n async commitTransaction(): Promise<void> {\n this._assertConnected();\n this._startTimeout();\n\n const conn = this._conn!;\n\n await new Promise<void>((resolve, reject) => {\n conn.commitTransaction((err) => {\n if (err != null) {\n reject(new SdError(err));\n return;\n }\n\n this.isOnTransaction = false;\n resolve();\n });\n });\n }\n\n async rollbackTransaction(): Promise<void> {\n this._assertConnected();\n this._startTimeout();\n\n const conn = this._conn!;\n\n await new Promise<void>((resolve, reject) => {\n conn.rollbackTransaction((err) => {\n if (err != null) {\n reject(new SdError(err));\n return;\n }\n\n this.isOnTransaction = false;\n resolve();\n });\n });\n }\n\n async execute(queries: string[]): Promise<unknown[][]> {\n const results: unknown[][] = [];\n for (const query of queries.filter((item) => !strIsNullOrEmpty(item))) {\n const resultItems = await this.executeParametrized(query);\n results.push(...resultItems);\n }\n\n return results;\n }\n\n async executeParametrized(query: string, params?: unknown[]): Promise<unknown[][]> {\n this._assertConnected();\n this._startTimeout();\n\n const conn = this._conn!;\n\n const results: unknown[][] = [];\n\n logger.debug(\"\uCFFC\uB9AC \uC2E4\uD589\", { queryLength: query.length, params });\n await new Promise<void>((resolve, reject) => {\n let rejected = false;\n const queryRequest = new this._tedious.Request(query, (err) => {\n if (err != null) {\n rejected = true;\n this._requests = this._requests.filter((r) => r !== queryRequest);\n\n const errRec = err as unknown as Record<string, unknown>;\n if (errRec[\"code\"] === \"ECANCEL\") {\n reject(new SdError(err, \"\uCFFC\uB9AC\uAC00 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\"));\n } else {\n const lineNumber = errRec[\"lineNumber\"] as number | undefined;\n if (lineNumber != null && lineNumber > 0) {\n const splitQuery = query.split(\"\\n\");\n splitQuery[lineNumber - 1] = \"==> \" + splitQuery[lineNumber - 1];\n reject(new SdError(err, `\uCFFC\uB9AC \uC218\uD589\uC911 \uC624\uB958\uBC1C\uC0DD\\n-- query\\n${splitQuery.join(\"\\n\")}\\n--`));\n } else {\n reject(new SdError(err, `\uCFFC\uB9AC \uC218\uD589\uC911 \uC624\uB958\uBC1C\uC0DD\\n-- query\\n${query}\\n--`));\n }\n }\n }\n });\n\n queryRequest\n .on(\"done\", (_rowCount, _more, rst) => {\n this._startTimeout();\n\n if (rejected) {\n return;\n }\n\n results.push(this._parseRowsToRecords(rst));\n })\n .on(\"doneInProc\", (_rowCount, _more, rst) => {\n this._startTimeout();\n\n if (rejected) {\n return;\n }\n\n results.push(this._parseRowsToRecords(rst));\n })\n .on(\"error\", (err) => {\n this._startTimeout();\n\n if (rejected) {\n return;\n }\n\n rejected = true;\n this._requests = this._requests.filter((r) => r !== queryRequest);\n reject(new SdError(err, `\uCFFC\uB9AC \uC218\uD589\uC911 \uC624\uB958\uBC1C\uC0DD\\n-- query\\n${query}\\n--`));\n })\n .on(\"requestCompleted\", () => {\n this._startTimeout();\n\n if (rejected) {\n return;\n }\n\n this._requests = this._requests.filter((r) => r !== queryRequest);\n resolve();\n });\n\n this._requests.push(queryRequest);\n\n if (params != null) {\n for (let i = 0; i < params.length; i++) {\n const paramValue = params[i];\n const paramName = `p${i}`;\n const type = this._guessTediousType(paramValue);\n\n queryRequest.addParameter(paramName, type, paramValue);\n }\n\n conn.execSql(queryRequest);\n } else {\n conn.execSqlBatch(queryRequest);\n }\n });\n\n return results;\n }\n\n async bulkInsert(\n tableName: string,\n columnMetas: Record<string, ColumnMeta>,\n records: Record<string, unknown>[],\n ): Promise<void> {\n if (records.length === 0) return;\n\n this._assertConnected();\n this._startTimeout();\n\n const tediousColumnDefs = Object.entries(columnMetas).map(([name, meta]) =>\n this._convertColumnMetaToTediousBulkColumnDef(name, meta),\n );\n\n await new Promise<void>((resolve, reject) => {\n const bulkLoad = this._conn!.newBulkLoad(tableName, (err) => {\n if (err != null) {\n reject(\n new SdError(\n err,\n `Bulk Insert \uC624\uB958\uBC1C\uC0DD\\n${jsonStringify(tediousColumnDefs)}\\n-- data\\n${jsonStringify(records).substring(0, 10000)}...\\n--`,\n ),\n );\n return;\n }\n resolve();\n });\n\n const colNames = Object.keys(columnMetas);\n\n for (const tediousColumnDef of tediousColumnDefs) {\n bulkLoad.addColumn(tediousColumnDef.name, tediousColumnDef.type, tediousColumnDef.options);\n }\n\n // \uB808\uCF54\uB4DC\uB97C row \uBC30\uC5F4\uB85C \uBCC0\uD658 (\uCEEC\uB7FC \uC21C\uC11C \uC720\uC9C0, \uAC12 \uBCC0\uD658 \uD3EC\uD568)\n const rows = records.map((record) =>\n colNames.map((colName) => {\n const val = record[colName];\n if (val instanceof Uuid) return val.toString();\n // eslint-disable-next-line no-restricted-globals -- tedious \uB77C\uC774\uBE0C\uB7EC\uB9AC\uAC00 Buffer\uB97C \uC694\uAD6C\uD568\n if (val instanceof Uint8Array) return Buffer.from(val);\n if (val instanceof DateTime) return val.date;\n if (val instanceof DateOnly) return val.date;\n if (val instanceof Time) return val.toFormatString(\"HH:mm:ss\");\n return val;\n }),\n );\n\n this._conn!.execBulkLoad(bulkLoad, rows);\n });\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Private helpers\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private _assertConnected(): void {\n if (this._conn == null || !this.isConnected) {\n throw new SdError(DB_CONN_ERRORS.NOT_CONNECTED);\n }\n }\n\n private _parseRowsToRecords(\n rows: Array<Array<{ metadata: { colName: string }; value: unknown }>> | undefined,\n ): Record<string, unknown>[] {\n return (rows ?? []).map((item) => {\n const resultItem: Record<string, unknown> = {};\n for (const col of item) {\n resultItem[col.metadata.colName] = col.value;\n }\n return resultItem;\n });\n }\n\n private _resetState(): void {\n this.isConnected = false;\n this.isOnTransaction = false;\n this._conn = undefined;\n this._requests = [];\n }\n\n private _stopTimeout(): void {\n if (this._connTimeout != null) {\n clearTimeout(this._connTimeout);\n }\n }\n\n private _startTimeout(): void {\n this._stopTimeout();\n this._connTimeout = setTimeout(() => {\n this.close().catch((err) => {\n logger.error(\"close error\", err instanceof Error ? err.message : String(err));\n });\n }, this._timeout * 2);\n }\n\n private _convertColumnMetaToTediousBulkColumnDef(\n name: string,\n meta: ColumnMeta,\n ): {\n name: string;\n type: TediousDataType;\n options: TediousColumnOptions;\n } {\n const tediousDataType = this._convertDataTypeToTediousBulkColumnType(meta.dataType);\n return {\n name,\n type: tediousDataType.type,\n options: {\n length: tediousDataType.length,\n nullable: meta.nullable ?? false,\n precision: tediousDataType.precision,\n scale: tediousDataType.scale,\n },\n };\n }\n\n private _convertDataTypeToTediousBulkColumnType(dataType: DataType): {\n type: TediousDataType;\n length?: number;\n precision?: number;\n scale?: number;\n } {\n switch (dataType.type) {\n case \"int\":\n return { type: this._tedious.TYPES.Int };\n case \"bigint\":\n return { type: this._tedious.TYPES.BigInt };\n case \"float\":\n return { type: this._tedious.TYPES.Real };\n case \"double\":\n return { type: this._tedious.TYPES.Float };\n case \"decimal\":\n return {\n type: this._tedious.TYPES.Decimal,\n precision: dataType.precision,\n scale: dataType.scale,\n };\n case \"varchar\":\n return { type: this._tedious.TYPES.NVarChar, length: dataType.length };\n case \"char\":\n return { type: this._tedious.TYPES.NChar, length: dataType.length };\n case \"text\":\n return { type: this._tedious.TYPES.NText };\n case \"binary\":\n return { type: this._tedious.TYPES.VarBinary, length: Infinity };\n case \"boolean\":\n return { type: this._tedious.TYPES.Bit };\n case \"datetime\":\n return { type: this._tedious.TYPES.DateTime2 };\n case \"date\":\n return { type: this._tedious.TYPES.Date };\n case \"time\":\n return { type: this._tedious.TYPES.Time };\n case \"uuid\":\n return { type: this._tedious.TYPES.UniqueIdentifier };\n default:\n throw new SdError(`\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 DataType: ${JSON.stringify(dataType)}`);\n }\n }\n\n /**\n * \uAC12\uC758 \uD0C0\uC785\uC744 \uCD94\uB860\uD558\uC5EC Tedious \uB370\uC774\uD130 \uD0C0\uC785 \uBC18\uD658\n *\n * @param value - \uD0C0\uC785\uC744 \uCD94\uB860\uD560 \uAC12 (null/undefined \uC804\uB2EC \uC2DC \uC624\uB958 \uBC1C\uC0DD)\n * @throws null/undefined\uAC00 \uC804\uB2EC\uB418\uBA74 \uC624\uB958 \uBC1C\uC0DD\n */\n private _guessTediousType(value: unknown): TediousDataType {\n if (value == null) {\n throw new SdError(\"_guessTediousType: null/undefined \uAC12\uC740 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.\");\n }\n if (typeof value === \"string\") {\n return this._tedious.TYPES.NVarChar;\n }\n if (typeof value === \"number\") {\n return Number.isInteger(value) ? this._tedious.TYPES.BigInt : this._tedious.TYPES.Decimal;\n }\n if (typeof value === \"boolean\") return this._tedious.TYPES.Bit;\n if (value instanceof DateTime) return this._tedious.TYPES.DateTime2;\n if (value instanceof DateOnly) return this._tedious.TYPES.Date;\n if (value instanceof Time) return this._tedious.TYPES.Time;\n if (value instanceof Uuid) return this._tedious.TYPES.UniqueIdentifier;\n if (value instanceof Uint8Array) return this._tedious.TYPES.VarBinary;\n\n throw new SdError(`\uC54C \uC218 \uC5C6\uB294 \uAC12 \uD0C0\uC785: ${typeof value}`);\n }\n}\n\ninterface TediousColumnOptions {\n length?: number;\n precision?: number;\n scale?: number;\n nullable?: boolean;\n}\n"],
5
+ "mappings": "AAAA,SAAS,qBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,yBAAyB,sBAA2D;AAI7F,MAAM,SAAS,cAAc,EAAE,QAAQ,eAAe;AAO/C,MAAM,oBAAoB,aAAgD;AAAA,EAU/E,YACmB,UACR,QACT;AACA,UAAM;AAHW;AACR;AAAA,EAGX;AAAA,EAdiB,WAAW;AAAA,EAEpB;AAAA,EACA;AAAA,EACA,YAAmC,CAAC;AAAA,EAE5C,cAAc;AAAA,EACd,kBAAkB;AAAA,EASlB,MAAM,UAAyB;AAC7B,QAAI,KAAK,aAAa;AACpB,YAAM,IAAI,QAAQ,eAAe,iBAAiB;AAAA,IACpD;AAEA,UAAM,OAAO,IAAI,KAAK,SAAS,WAAW;AAAA,MACxC,QAAQ,KAAK,OAAO;AAAA,MACpB,gBAAgB;AAAA,QACd,MAAM;AAAA,QACN,SAAS;AAAA,UACP,UAAU,KAAK,OAAO;AAAA,UACtB,UAAU,KAAK,OAAO;AAAA,QACxB;AAAA,MACF;AAAA,MACA,SAAS;AAAA,QACP,UAAU,KAAK,OAAO;AAAA,QACtB,MAAM,KAAK,OAAO;AAAA,QAClB,qBAAqB;AAAA,QACrB,QAAQ;AAAA,QACR,SAAS,KAAK,OAAO,YAAY;AAAA,QACjC,gBAAgB,KAAK;AAAA,QACrB,wBAAwB;AAAA,QACxB,gBAAgB,KAAK,WAAW;AAAA,MAClC;AAAA,IACF,CAAC;AAED,SAAK,GAAG,eAAe,CAAC,SAAS;AAC/B,aAAO,MAAM,QAAQ,KAAK,OAAO;AAAA,IACnC,CAAC;AAED,SAAK,GAAG,gBAAgB,CAAC,UAAU;AACjC,aAAO,MAAM,gBAAgB,MAAM,OAAO;AAAA,IAC5C,CAAC;AAED,SAAK,GAAG,SAAS,CAAC,UAAU;AAC1B,aAAO,MAAM,SAAS,MAAM,OAAO;AAAA,IACrC,CAAC;AAED,SAAK,GAAG,OAAO,MAAM;AACnB,WAAK,KAAK,OAAO;AACjB,WAAK,YAAY;AAAA,IACnB,CAAC;AAED,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,WAAK,QAAQ,CAAC,QAA2B;AACvC,YAAI,OAAO,MAAM;AACf,iBAAO,IAAI,QAAQ,GAAG,CAAC;AACvB;AAAA,QACF;AAEA,aAAK,cAAc;AACnB,aAAK,cAAc;AACnB,aAAK,kBAAkB;AACvB,gBAAQ;AAAA,MACV,CAAC;AAAA,IACH,CAAC;AAED,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,aAAa;AAElB,QAAI,KAAK,SAAS,QAAQ,CAAC,KAAK,aAAa;AAC3C;AAAA,IACF;AAEA,UAAM,OAAO,KAAK;AAGlB,SAAK,OAAO;AACZ,UAAM,UAAU,MAAM,KAAK,UAAU,SAAS,GAAG,KAAO,GAAG;AAG3D,UAAM,IAAI,QAAc,CAAC,YAAY;AACnC,WAAK,GAAG,OAAO,MAAM;AACnB,kBAAU,MAAM,KAAK,SAAS,MAAM,KAAO,GAAG,EAC3C,KAAK,MAAM,QAAQ,CAAC,EACpB,MAAM,MAAM,QAAQ,CAAC;AAAA,MAC1B,CAAC;AACD,WAAK,MAAM;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,iBAAiB,gBAAgD;AACrE,SAAK,iBAAiB;AACtB,SAAK,cAAc;AAEnB,UAAM,OAAO,KAAK;AAElB,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,WAAK;AAAA,QACH,CAAC,QAAQ;AACP,cAAI,OAAO,MAAM;AACf,mBAAO,IAAI,QAAQ,GAAG,CAAC;AACvB;AAAA,UACF;AAEA,eAAK,kBAAkB;AACvB,kBAAQ;AAAA,QACV;AAAA,QACA;AAAA,QACA,KAAK,SAAS,gBAAgB,kBAAkB,KAAK,OAAO,yBAAyB,kBAAkB;AAAA,MACzG;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,oBAAmC;AACvC,SAAK,iBAAiB;AACtB,SAAK,cAAc;AAEnB,UAAM,OAAO,KAAK;AAElB,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,WAAK,kBAAkB,CAAC,QAAQ;AAC9B,YAAI,OAAO,MAAM;AACf,iBAAO,IAAI,QAAQ,GAAG,CAAC;AACvB;AAAA,QACF;AAEA,aAAK,kBAAkB;AACvB,gBAAQ;AAAA,MACV,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,sBAAqC;AACzC,SAAK,iBAAiB;AACtB,SAAK,cAAc;AAEnB,UAAM,OAAO,KAAK;AAElB,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,WAAK,oBAAoB,CAAC,QAAQ;AAChC,YAAI,OAAO,MAAM;AACf,iBAAO,IAAI,QAAQ,GAAG,CAAC;AACvB;AAAA,QACF;AAEA,aAAK,kBAAkB;AACvB,gBAAQ;AAAA,MACV,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,QAAQ,SAAyC;AACrD,UAAM,UAAuB,CAAC;AAC9B,eAAW,SAAS,QAAQ,OAAO,CAAC,SAAS,CAAC,iBAAiB,IAAI,CAAC,GAAG;AACrE,YAAM,cAAc,MAAM,KAAK,oBAAoB,KAAK;AACxD,cAAQ,KAAK,GAAG,WAAW;AAAA,IAC7B;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,oBAAoB,OAAe,QAA0C;AACjF,SAAK,iBAAiB;AACtB,SAAK,cAAc;AAEnB,UAAM,OAAO,KAAK;AAElB,UAAM,UAAuB,CAAC;AAE9B,WAAO,MAAM,6BAAS,EAAE,aAAa,MAAM,QAAQ,OAAO,CAAC;AAC3D,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,UAAI,WAAW;AACf,YAAM,eAAe,IAAI,KAAK,SAAS,QAAQ,OAAO,CAAC,QAAQ;AAC7D,YAAI,OAAO,MAAM;AACf,qBAAW;AACX,eAAK,YAAY,KAAK,UAAU,OAAO,CAAC,MAAM,MAAM,YAAY;AAEhE,gBAAM,SAAS;AACf,cAAI,OAAO,MAAM,MAAM,WAAW;AAChC,mBAAO,IAAI,QAAQ,KAAK,gEAAc,CAAC;AAAA,UACzC,OAAO;AACL,kBAAM,aAAa,OAAO,YAAY;AACtC,gBAAI,cAAc,QAAQ,aAAa,GAAG;AACxC,oBAAM,aAAa,MAAM,MAAM,IAAI;AACnC,yBAAW,aAAa,CAAC,IAAI,SAAS,WAAW,aAAa,CAAC;AAC/D,qBAAO,IAAI,QAAQ,KAAK;AAAA;AAAA,EAA0B,WAAW,KAAK,IAAI,CAAC;AAAA,GAAM,CAAC;AAAA,YAChF,OAAO;AACL,qBAAO,IAAI,QAAQ,KAAK;AAAA;AAAA,EAA0B,KAAK;AAAA,GAAM,CAAC;AAAA,YAChE;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAED,mBACG,GAAG,QAAQ,CAAC,WAAW,OAAO,QAAQ;AACrC,aAAK,cAAc;AAEnB,YAAI,UAAU;AACZ;AAAA,QACF;AAEA,gBAAQ,KAAK,KAAK,oBAAoB,GAAG,CAAC;AAAA,MAC5C,CAAC,EACA,GAAG,cAAc,CAAC,WAAW,OAAO,QAAQ;AAC3C,aAAK,cAAc;AAEnB,YAAI,UAAU;AACZ;AAAA,QACF;AAEA,gBAAQ,KAAK,KAAK,oBAAoB,GAAG,CAAC;AAAA,MAC5C,CAAC,EACA,GAAG,SAAS,CAAC,QAAQ;AACpB,aAAK,cAAc;AAEnB,YAAI,UAAU;AACZ;AAAA,QACF;AAEA,mBAAW;AACX,aAAK,YAAY,KAAK,UAAU,OAAO,CAAC,MAAM,MAAM,YAAY;AAChE,eAAO,IAAI,QAAQ,KAAK;AAAA;AAAA,EAA0B,KAAK;AAAA,GAAM,CAAC;AAAA,MAChE,CAAC,EACA,GAAG,oBAAoB,MAAM;AAC5B,aAAK,cAAc;AAEnB,YAAI,UAAU;AACZ;AAAA,QACF;AAEA,aAAK,YAAY,KAAK,UAAU,OAAO,CAAC,MAAM,MAAM,YAAY;AAChE,gBAAQ;AAAA,MACV,CAAC;AAEH,WAAK,UAAU,KAAK,YAAY;AAEhC,UAAI,UAAU,MAAM;AAClB,iBAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,gBAAM,aAAa,OAAO,CAAC;AAC3B,gBAAM,YAAY,IAAI,CAAC;AACvB,gBAAM,OAAO,KAAK,kBAAkB,UAAU;AAE9C,uBAAa,aAAa,WAAW,MAAM,UAAU;AAAA,QACvD;AAEA,aAAK,QAAQ,YAAY;AAAA,MAC3B,OAAO;AACL,aAAK,aAAa,YAAY;AAAA,MAChC;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WACJ,WACA,aACA,SACe;AACf,QAAI,QAAQ,WAAW,EAAG;AAE1B,SAAK,iBAAiB;AACtB,SAAK,cAAc;AAEnB,UAAM,oBAAoB,OAAO,QAAQ,WAAW,EAAE;AAAA,MAAI,CAAC,CAAC,MAAM,IAAI,MACpE,KAAK,yCAAyC,MAAM,IAAI;AAAA,IAC1D;AAEA,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,YAAM,WAAW,KAAK,MAAO,YAAY,WAAW,CAAC,QAAQ;AAC3D,YAAI,OAAO,MAAM;AACf;AAAA,YACE,IAAI;AAAA,cACF;AAAA,cACA;AAAA,EAAqB,cAAc,iBAAiB,CAAC;AAAA;AAAA,EAAc,cAAc,OAAO,EAAE,UAAU,GAAG,GAAK,CAAC;AAAA;AAAA,YAC/G;AAAA,UACF;AACA;AAAA,QACF;AACA,gBAAQ;AAAA,MACV,CAAC;AAED,YAAM,WAAW,OAAO,KAAK,WAAW;AAExC,iBAAW,oBAAoB,mBAAmB;AAChD,iBAAS,UAAU,iBAAiB,MAAM,iBAAiB,MAAM,iBAAiB,OAAO;AAAA,MAC3F;AAGA,YAAM,OAAO,QAAQ;AAAA,QAAI,CAAC,WACxB,SAAS,IAAI,CAAC,YAAY;AACxB,gBAAM,MAAM,OAAO,OAAO;AAC1B,cAAI,eAAe,KAAM,QAAO,IAAI,SAAS;AAE7C,cAAI,eAAe,WAAY,QAAO,OAAO,KAAK,GAAG;AACrD,cAAI,eAAe,SAAU,QAAO,IAAI;AACxC,cAAI,eAAe,SAAU,QAAO,IAAI;AACxC,cAAI,eAAe,KAAM,QAAO,IAAI,eAAe,UAAU;AAC7D,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAEA,WAAK,MAAO,aAAa,UAAU,IAAI;AAAA,IACzC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAMQ,mBAAyB;AAC/B,QAAI,KAAK,SAAS,QAAQ,CAAC,KAAK,aAAa;AAC3C,YAAM,IAAI,QAAQ,eAAe,aAAa;AAAA,IAChD;AAAA,EACF;AAAA,EAEQ,oBACN,MAC2B;AAC3B,YAAQ,QAAQ,CAAC,GAAG,IAAI,CAAC,SAAS;AAChC,YAAM,aAAsC,CAAC;AAC7C,iBAAW,OAAO,MAAM;AACtB,mBAAW,IAAI,SAAS,OAAO,IAAI,IAAI;AAAA,MACzC;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEQ,cAAoB;AAC1B,SAAK,cAAc;AACnB,SAAK,kBAAkB;AACvB,SAAK,QAAQ;AACb,SAAK,YAAY,CAAC;AAAA,EACpB;AAAA,EAEQ,eAAqB;AAC3B,QAAI,KAAK,gBAAgB,MAAM;AAC7B,mBAAa,KAAK,YAAY;AAAA,IAChC;AAAA,EACF;AAAA,EAEQ,gBAAsB;AAC5B,SAAK,aAAa;AAClB,SAAK,eAAe,WAAW,MAAM;AACnC,WAAK,MAAM,EAAE,MAAM,CAAC,QAAQ;AAC1B,eAAO,MAAM,eAAe,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAC9E,CAAC;AAAA,IACH,GAAG,KAAK,WAAW,CAAC;AAAA,EACtB;AAAA,EAEQ,yCACN,MACA,MAKA;AACA,UAAM,kBAAkB,KAAK,wCAAwC,KAAK,QAAQ;AAClF,WAAO;AAAA,MACL;AAAA,MACA,MAAM,gBAAgB;AAAA,MACtB,SAAS;AAAA,QACP,QAAQ,gBAAgB;AAAA,QACxB,UAAU,KAAK,YAAY;AAAA,QAC3B,WAAW,gBAAgB;AAAA,QAC3B,OAAO,gBAAgB;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,wCAAwC,UAK9C;AACA,YAAQ,SAAS,MAAM;AAAA,MACrB,KAAK;AACH,eAAO,EAAE,MAAM,KAAK,SAAS,MAAM,IAAI;AAAA,MACzC,KAAK;AACH,eAAO,EAAE,MAAM,KAAK,SAAS,MAAM,OAAO;AAAA,MAC5C,KAAK;AACH,eAAO,EAAE,MAAM,KAAK,SAAS,MAAM,KAAK;AAAA,MAC1C,KAAK;AACH,eAAO,EAAE,MAAM,KAAK,SAAS,MAAM,MAAM;AAAA,MAC3C,KAAK;AACH,eAAO;AAAA,UACL,MAAM,KAAK,SAAS,MAAM;AAAA,UAC1B,WAAW,SAAS;AAAA,UACpB,OAAO,SAAS;AAAA,QAClB;AAAA,MACF,KAAK;AACH,eAAO,EAAE,MAAM,KAAK,SAAS,MAAM,UAAU,QAAQ,SAAS,OAAO;AAAA,MACvE,KAAK;AACH,eAAO,EAAE,MAAM,KAAK,SAAS,MAAM,OAAO,QAAQ,SAAS,OAAO;AAAA,MACpE,KAAK;AACH,eAAO,EAAE,MAAM,KAAK,SAAS,MAAM,MAAM;AAAA,MAC3C,KAAK;AACH,eAAO,EAAE,MAAM,KAAK,SAAS,MAAM,WAAW,QAAQ,SAAS;AAAA,MACjE,KAAK;AACH,eAAO,EAAE,MAAM,KAAK,SAAS,MAAM,IAAI;AAAA,MACzC,KAAK;AACH,eAAO,EAAE,MAAM,KAAK,SAAS,MAAM,UAAU;AAAA,MAC/C,KAAK;AACH,eAAO,EAAE,MAAM,KAAK,SAAS,MAAM,KAAK;AAAA,MAC1C,KAAK;AACH,eAAO,EAAE,MAAM,KAAK,SAAS,MAAM,KAAK;AAAA,MAC1C,KAAK;AACH,eAAO,EAAE,MAAM,KAAK,SAAS,MAAM,iBAAiB;AAAA,MACtD;AACE,cAAM,IAAI,QAAQ,mDAAqB,KAAK,UAAU,QAAQ,CAAC,EAAE;AAAA,IACrE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,kBAAkB,OAAiC;AACzD,QAAI,SAAS,MAAM;AACjB,YAAM,IAAI,QAAQ,mGAAiD;AAAA,IACrE;AACA,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO,KAAK,SAAS,MAAM;AAAA,IAC7B;AACA,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO,OAAO,UAAU,KAAK,IAAI,KAAK,SAAS,MAAM,SAAS,KAAK,SAAS,MAAM;AAAA,IACpF;AACA,QAAI,OAAO,UAAU,UAAW,QAAO,KAAK,SAAS,MAAM;AAC3D,QAAI,iBAAiB,SAAU,QAAO,KAAK,SAAS,MAAM;AAC1D,QAAI,iBAAiB,SAAU,QAAO,KAAK,SAAS,MAAM;AAC1D,QAAI,iBAAiB,KAAM,QAAO,KAAK,SAAS,MAAM;AACtD,QAAI,iBAAiB,KAAM,QAAO,KAAK,SAAS,MAAM;AACtD,QAAI,iBAAiB,WAAY,QAAO,KAAK,SAAS,MAAM;AAE5D,UAAM,IAAI,QAAQ,mDAAgB,OAAO,KAAK,EAAE;AAAA,EAClD;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,227 @@
1
+ import { randomUUID } from "crypto";
2
+ import fs from "fs";
3
+ import os from "os";
4
+ import path from "path";
5
+ import { createConsola } from "consola";
6
+ import {
7
+ bytesToHex,
8
+ DateOnly,
9
+ DateTime,
10
+ SdError,
11
+ EventEmitter,
12
+ strIsNullOrEmpty,
13
+ Time,
14
+ Uuid
15
+ } from "@simplysm/core-common";
16
+ import { DB_CONN_DEFAULT_TIMEOUT, DB_CONN_ERRORS } from "../types/db-conn";
17
+ const logger = createConsola().withTag("mysql-db-conn");
18
+ class MysqlDbConn extends EventEmitter {
19
+ constructor(_mysql2, config) {
20
+ super();
21
+ this._mysql2 = _mysql2;
22
+ this.config = config;
23
+ }
24
+ static _ROOT_USER = "root";
25
+ _timeout = DB_CONN_DEFAULT_TIMEOUT;
26
+ _conn;
27
+ _connTimeout;
28
+ isConnected = false;
29
+ isOnTransaction = false;
30
+ async connect() {
31
+ if (this.isConnected) {
32
+ throw new SdError(DB_CONN_ERRORS.ALREADY_CONNECTED);
33
+ }
34
+ const conn = await this._mysql2.createConnection({
35
+ host: this.config.host,
36
+ port: this.config.port,
37
+ user: this.config.username,
38
+ password: this.config.password,
39
+ // root 사용자는 특정 database에 바인딩되지 않고 연결하여
40
+ // 모든 데이터베이스에 접근할 수 있도록 함 (관리 작업용)
41
+ database: this.config.username === MysqlDbConn._ROOT_USER ? void 0 : this.config.database,
42
+ multipleStatements: true,
43
+ charset: "utf8mb4",
44
+ infileStreamFactory: (filePath) => fs.createReadStream(filePath)
45
+ // LOAD DATA LOCAL INFILE 지원
46
+ });
47
+ conn.on("end", () => {
48
+ this.emit("close");
49
+ this._resetState();
50
+ });
51
+ conn.on("error", (error) => {
52
+ logger.error("DB \uC5F0\uACB0 \uC624\uB958", error.message);
53
+ });
54
+ this._conn = conn;
55
+ this._startTimeout();
56
+ this.isConnected = true;
57
+ }
58
+ async close() {
59
+ this._stopTimeout();
60
+ if (this._conn == null || !this.isConnected) {
61
+ return;
62
+ }
63
+ await this._conn.end();
64
+ this.emit("close");
65
+ this._resetState();
66
+ }
67
+ async beginTransaction(isolationLevel) {
68
+ const conn = this._assertConnected();
69
+ const level = (isolationLevel ?? this.config.defaultIsolationLevel ?? "READ_UNCOMMITTED").replace(/_/g, " ");
70
+ await conn.query({
71
+ sql: `SET SESSION TRANSACTION ISOLATION LEVEL ${level}`,
72
+ timeout: this._timeout
73
+ });
74
+ await conn.beginTransaction();
75
+ this.isOnTransaction = true;
76
+ }
77
+ async commitTransaction() {
78
+ const conn = this._assertConnected();
79
+ await conn.commit();
80
+ this.isOnTransaction = false;
81
+ }
82
+ async rollbackTransaction() {
83
+ const conn = this._assertConnected();
84
+ await conn.rollback();
85
+ this.isOnTransaction = false;
86
+ }
87
+ async execute(queries) {
88
+ const results = [];
89
+ for (const query of queries.filter((item) => !strIsNullOrEmpty(item))) {
90
+ const resultItems = await this.executeParametrized(query);
91
+ results.push(...resultItems);
92
+ }
93
+ return results;
94
+ }
95
+ async executeParametrized(query, params) {
96
+ const conn = this._assertConnected();
97
+ logger.debug("\uCFFC\uB9AC \uC2E4\uD589", { queryLength: query.length, params });
98
+ try {
99
+ const [queryResults] = await conn.query({
100
+ sql: query,
101
+ timeout: this._timeout,
102
+ values: params
103
+ });
104
+ this._startTimeout();
105
+ const result = [];
106
+ if (queryResults instanceof Array) {
107
+ for (const queryResult of queryResults.filter(
108
+ (item) => !(typeof item === "object" && item !== null && "affectedRows" in item && "fieldCount" in item)
109
+ )) {
110
+ result.push(queryResult);
111
+ }
112
+ }
113
+ return [result];
114
+ } catch (err) {
115
+ this._startTimeout();
116
+ const error = err;
117
+ throw new SdError(
118
+ error,
119
+ "\uCFFC\uB9AC \uC218\uD589\uC911 \uC624\uB958\uBC1C\uC0DD" + (error.sql != null ? "\n-- query\n" + error.sql.trim() + "\n--" : "")
120
+ );
121
+ }
122
+ }
123
+ async bulkInsert(tableName, columnMetas, records) {
124
+ const conn = this._assertConnected();
125
+ if (records.length === 0) return;
126
+ const colNames = Object.keys(columnMetas);
127
+ const tmpDir = os.tmpdir();
128
+ const tmpFile = path.join(tmpDir, `mysql_bulk_${randomUUID()}.csv`);
129
+ try {
130
+ const csvLines = [];
131
+ for (const record of records) {
132
+ const row = colNames.map((colName) => this._escapeForCsv(record[colName], columnMetas[colName].dataType));
133
+ csvLines.push(row.join(" "));
134
+ }
135
+ const csvContent = csvLines.join("\n");
136
+ await fs.promises.writeFile(tmpFile, csvContent, "utf8");
137
+ const binaryColNames = colNames.filter((c) => {
138
+ const dt = columnMetas[c].dataType.type;
139
+ return dt === "uuid" || dt === "binary";
140
+ });
141
+ const normalCols = colNames.map((c) => {
142
+ if (binaryColNames.includes(c)) return `@_${c}`;
143
+ return `\`${c}\``;
144
+ });
145
+ const setClauses = binaryColNames.map((c) => `\`${c}\` = UNHEX(@_${c})`);
146
+ let query = `LOAD DATA LOCAL INFILE ? INTO TABLE ${tableName} FIELDS TERMINATED BY '\\t' LINES TERMINATED BY '\\n' (${normalCols.join(", ")})`;
147
+ if (setClauses.length > 0) {
148
+ query += ` SET ${setClauses.join(", ")}`;
149
+ }
150
+ await conn.query({ sql: query, timeout: this._timeout, values: [tmpFile] });
151
+ } finally {
152
+ try {
153
+ await fs.promises.unlink(tmpFile);
154
+ } catch {
155
+ }
156
+ }
157
+ }
158
+ // ─────────────────────────────────────────────
159
+ // Private helpers
160
+ // ─────────────────────────────────────────────
161
+ /**
162
+ * MySQL LOAD DATA INFILE용 값 이스케이프
163
+ */
164
+ _escapeForCsv(value, dataType) {
165
+ if (value == null) {
166
+ return "\\N";
167
+ }
168
+ switch (dataType.type) {
169
+ case "int":
170
+ case "bigint":
171
+ case "float":
172
+ case "double":
173
+ case "decimal":
174
+ return String(value);
175
+ case "boolean":
176
+ return value ? "1" : "0";
177
+ case "varchar":
178
+ case "char":
179
+ case "text": {
180
+ const str = value;
181
+ return str.replace(/\\/g, "\\\\").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
182
+ }
183
+ case "datetime":
184
+ return value.toFormatString("yyyy-MM-dd HH:mm:ss.fff");
185
+ case "date":
186
+ return value.toFormatString("yyyy-MM-dd");
187
+ case "time":
188
+ return value.toFormatString("HH:mm:ss");
189
+ case "uuid":
190
+ return value.toString().replace(/-/g, "");
191
+ // BINARY(16) 저장용 hex
192
+ case "binary":
193
+ return bytesToHex(value);
194
+ default:
195
+ throw new SdError(`\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 DataType: ${JSON.stringify(dataType)}`);
196
+ }
197
+ }
198
+ _assertConnected() {
199
+ if (this._conn == null || !this.isConnected) {
200
+ throw new SdError(DB_CONN_ERRORS.NOT_CONNECTED);
201
+ }
202
+ this._startTimeout();
203
+ return this._conn;
204
+ }
205
+ _resetState() {
206
+ this.isConnected = false;
207
+ this.isOnTransaction = false;
208
+ this._conn = void 0;
209
+ }
210
+ _stopTimeout() {
211
+ if (this._connTimeout != null) {
212
+ clearTimeout(this._connTimeout);
213
+ }
214
+ }
215
+ _startTimeout() {
216
+ this._stopTimeout();
217
+ this._connTimeout = setTimeout(() => {
218
+ this.close().catch((err) => {
219
+ logger.error("close error", err instanceof Error ? err.message : String(err));
220
+ });
221
+ }, this._timeout * 2);
222
+ }
223
+ }
224
+ export {
225
+ MysqlDbConn
226
+ };
227
+ //# sourceMappingURL=mysql-db-conn.js.map