db-model-router 1.0.0
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/.env +7 -0
- package/LICENSE +201 -0
- package/README.md +505 -0
- package/docker-compose.yml +141 -0
- package/docs/README.md +208 -0
- package/docs/SKILL.md +202 -0
- package/docs/adapters/cockroachdb.md +49 -0
- package/docs/adapters/dynamodb.md +53 -0
- package/docs/adapters/mongodb.md +56 -0
- package/docs/adapters/mssql.md +55 -0
- package/docs/adapters/oracle.md +52 -0
- package/docs/adapters/postgres.md +50 -0
- package/docs/adapters/redis.md +53 -0
- package/docs/adapters/sqlite3.md +43 -0
- package/package.json +109 -0
- package/src/cli/generate-app.js +359 -0
- package/src/cli/generate-model.js +760 -0
- package/src/cli/generate-openapi.js +237 -0
- package/src/cli/generate-route.js +346 -0
- package/src/cockroachdb/db.js +563 -0
- package/src/commons/function.js +165 -0
- package/src/commons/model.js +444 -0
- package/src/commons/route.js +214 -0
- package/src/commons/validator.js +172 -0
- package/src/dynamodb/db.js +552 -0
- package/src/index.js +57 -0
- package/src/mongodb/db.js +381 -0
- package/src/mssql/db.js +461 -0
- package/src/mysql/db.js +527 -0
- package/src/oracle/db.js +855 -0
- package/src/oracle/sql_translator.js +406 -0
- package/src/postgres/db.js +666 -0
- package/src/postgres/ddl_translator.js +69 -0
- package/src/postgres/sql_translator.js +396 -0
- package/src/redis/db.js +448 -0
- package/src/serve.js +90 -0
- package/src/sqlite3/db.js +346 -0
package/src/mssql/db.js
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
const sql = require("mssql");
|
|
2
|
+
const { jsonSafeParse } = require("../commons/function");
|
|
3
|
+
|
|
4
|
+
let pool = null;
|
|
5
|
+
const WHERE_INVALID = "Invalid filter object";
|
|
6
|
+
|
|
7
|
+
const RETRYABLE_ERRORS = [
|
|
8
|
+
"ECONNREFUSED",
|
|
9
|
+
"ECONNRESET",
|
|
10
|
+
"EPIPE",
|
|
11
|
+
"ETIMEOUT",
|
|
12
|
+
"ESOCKET",
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const ERROR_MAP = {
|
|
16
|
+
2627: "ER_DUP_ENTRY", // Violation of PRIMARY KEY / UNIQUE constraint
|
|
17
|
+
2601: "ER_DUP_ENTRY", // Cannot insert duplicate key row
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function mapMssqlError(err) {
|
|
21
|
+
const num = err.number || err.originalError?.number || 0;
|
|
22
|
+
if (ERROR_MAP[num]) {
|
|
23
|
+
err.code = ERROR_MAP[num];
|
|
24
|
+
err.sqlMessage = err.message;
|
|
25
|
+
return err;
|
|
26
|
+
}
|
|
27
|
+
if (!err.sqlMessage) err.sqlMessage = err.message;
|
|
28
|
+
return err;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isRetryable(err) {
|
|
32
|
+
const code = err.code || "";
|
|
33
|
+
return RETRYABLE_ERRORS.includes(code);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function connect(config) {
|
|
37
|
+
const mssqlConfig = {
|
|
38
|
+
server: config.server || config.host || process.env.DB_HOST || "localhost",
|
|
39
|
+
port: parseInt(config.port || process.env.DB_PORT || 1433),
|
|
40
|
+
database: config.database || process.env.DB_NAME,
|
|
41
|
+
user: config.user || process.env.DB_USER,
|
|
42
|
+
password: config.password || process.env.DB_PASS,
|
|
43
|
+
pool: {
|
|
44
|
+
max: parseInt(config.connectionLimit || process.env.DB_POOL_MAX || 50),
|
|
45
|
+
min: 0,
|
|
46
|
+
idleTimeoutMillis: 30000,
|
|
47
|
+
},
|
|
48
|
+
options: {
|
|
49
|
+
encrypt: config.options?.encrypt ?? false,
|
|
50
|
+
trustServerCertificate: config.options?.trustServerCertificate ?? true,
|
|
51
|
+
enableArithAbort: true,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
pool = await sql.connect(mssqlConfig);
|
|
56
|
+
return pool;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function executeWithRetry(fn) {
|
|
60
|
+
try {
|
|
61
|
+
return await fn();
|
|
62
|
+
} catch (err) {
|
|
63
|
+
if (isRetryable(err)) {
|
|
64
|
+
return await fn();
|
|
65
|
+
}
|
|
66
|
+
throw mapMssqlError(err);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function query(sqlStr, parameter = []) {
|
|
71
|
+
return executeWithRetry(async () => {
|
|
72
|
+
const request = pool.request();
|
|
73
|
+
for (let i = 0; i < parameter.length; i++) {
|
|
74
|
+
request.input("param" + i, parameter[i]);
|
|
75
|
+
}
|
|
76
|
+
// Replace positional @paramN placeholders if not already present
|
|
77
|
+
const result = await request.query(sqlStr);
|
|
78
|
+
return result.recordset || { affectedRows: result.rowsAffected?.[0] || 0 };
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function sort_builder(sort) {
|
|
83
|
+
if (!sort || sort.length < 1) {
|
|
84
|
+
return { query: "", value: [] };
|
|
85
|
+
}
|
|
86
|
+
let items = [];
|
|
87
|
+
for (const item of sort) {
|
|
88
|
+
if (item[0] === "-") {
|
|
89
|
+
items.push(escapeId(item.replace("-", "")) + " DESC");
|
|
90
|
+
} else {
|
|
91
|
+
items.push(escapeId(item) + " ASC");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { query: "ORDER BY " + items.join(","), value: [] };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function where(filter, safeDelete = null) {
|
|
98
|
+
if (filter !== null && filter !== "" && !Array.isArray(filter)) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
if (
|
|
103
|
+
filter === null ||
|
|
104
|
+
filter === "" ||
|
|
105
|
+
filter.length === 0 ||
|
|
106
|
+
(Array.isArray(filter[0]) && filter[0].length === 0) ||
|
|
107
|
+
(Array.isArray(filter[0]) &&
|
|
108
|
+
Array.isArray(filter[0][0]) &&
|
|
109
|
+
filter[0][0].length === 0)
|
|
110
|
+
) {
|
|
111
|
+
if (safeDelete === null) {
|
|
112
|
+
return { query: "", value: [] };
|
|
113
|
+
} else {
|
|
114
|
+
filter = [[]];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (safeDelete !== null) {
|
|
122
|
+
for (const filterItem of filter) {
|
|
123
|
+
filterItem.push([safeDelete, "=", 0]);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const valid_conditionals = [
|
|
128
|
+
"=",
|
|
129
|
+
"like",
|
|
130
|
+
"not like",
|
|
131
|
+
"in",
|
|
132
|
+
"not in",
|
|
133
|
+
"<",
|
|
134
|
+
">",
|
|
135
|
+
"<=",
|
|
136
|
+
">=",
|
|
137
|
+
"!=",
|
|
138
|
+
];
|
|
139
|
+
let conditionOr = [];
|
|
140
|
+
let value = [];
|
|
141
|
+
let bindIdx = 0;
|
|
142
|
+
|
|
143
|
+
for (const i of filter) {
|
|
144
|
+
let conditionAnd = [];
|
|
145
|
+
for (const j of i) {
|
|
146
|
+
if (!valid_conditionals.includes(j[1])) return null;
|
|
147
|
+
if ((j[1] === "in" || j[1] === "not in") && !Array.isArray(j[2]))
|
|
148
|
+
return null;
|
|
149
|
+
|
|
150
|
+
if (j[1] === "in" || j[1] === "not in") {
|
|
151
|
+
const placeholders = j[2]
|
|
152
|
+
.map(() => {
|
|
153
|
+
const p = "@param" + bindIdx;
|
|
154
|
+
bindIdx++;
|
|
155
|
+
return p;
|
|
156
|
+
})
|
|
157
|
+
.join(",");
|
|
158
|
+
conditionAnd.push(`${escapeId(j[0])} ${j[1]} (${placeholders})`);
|
|
159
|
+
value.push(...j[2]);
|
|
160
|
+
} else if (j[1] === "like" || j[1] === "not like") {
|
|
161
|
+
const p = "@param" + bindIdx;
|
|
162
|
+
bindIdx++;
|
|
163
|
+
conditionAnd.push(`${escapeId(j[0])} ${j[1]} ${p}`);
|
|
164
|
+
value.push("%" + j[2] + "%");
|
|
165
|
+
} else {
|
|
166
|
+
const p = "@param" + bindIdx;
|
|
167
|
+
bindIdx++;
|
|
168
|
+
conditionAnd.push(`${escapeId(j[0])} ${j[1]} ${p}`);
|
|
169
|
+
value.push(j[2]);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
conditionOr.push(conditionAnd.join(" AND "));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
query: "WHERE ((" + conditionOr.join(") OR (") + "))",
|
|
177
|
+
value,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function get(table, filter = [], sort = [], safeDelete = null) {
|
|
182
|
+
const whereData = where(filter, safeDelete);
|
|
183
|
+
const sortData = sort_builder(sort);
|
|
184
|
+
if (whereData == null) throw new Error(WHERE_INVALID);
|
|
185
|
+
|
|
186
|
+
const sqlStr = `SELECT * FROM ${escapeId(table)} ${whereData.query} ${sortData.query}`;
|
|
187
|
+
const request = pool.request();
|
|
188
|
+
for (let i = 0; i < whereData.value.length; i++) {
|
|
189
|
+
request.input("param" + i, whereData.value[i]);
|
|
190
|
+
}
|
|
191
|
+
const result = await request.query(sqlStr);
|
|
192
|
+
const rows = jsonSafeParse(result.recordset || []);
|
|
193
|
+
const count = await qcount(table, filter, safeDelete);
|
|
194
|
+
return { data: rows, count };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function list(
|
|
198
|
+
table,
|
|
199
|
+
filter = [],
|
|
200
|
+
sort = [],
|
|
201
|
+
safeDelete = null,
|
|
202
|
+
page = 0,
|
|
203
|
+
limit = 30,
|
|
204
|
+
) {
|
|
205
|
+
const whereData = where(filter, safeDelete);
|
|
206
|
+
const sortData = sort_builder(sort);
|
|
207
|
+
if (whereData == null) throw new Error(WHERE_INVALID);
|
|
208
|
+
|
|
209
|
+
const offset = page * limit;
|
|
210
|
+
// MSSQL requires ORDER BY for OFFSET/FETCH. Use sort or default to (SELECT NULL)
|
|
211
|
+
const orderClause = sortData.query || "ORDER BY (SELECT NULL)";
|
|
212
|
+
const sqlStr = `SELECT * FROM ${escapeId(table)} ${whereData.query} ${orderClause} OFFSET ${offset} ROWS FETCH NEXT ${limit} ROWS ONLY`;
|
|
213
|
+
|
|
214
|
+
const request = pool.request();
|
|
215
|
+
for (let i = 0; i < whereData.value.length; i++) {
|
|
216
|
+
request.input("param" + i, whereData.value[i]);
|
|
217
|
+
}
|
|
218
|
+
const result = await request.query(sqlStr);
|
|
219
|
+
const rows = jsonSafeParse(result.recordset || []);
|
|
220
|
+
const count = await qcount(table, filter, safeDelete);
|
|
221
|
+
return { data: rows, count };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function qcount(table, filter, safeDelete = null) {
|
|
225
|
+
const whereData = where(filter, safeDelete);
|
|
226
|
+
if (whereData == null) return 0;
|
|
227
|
+
const sqlStr = `SELECT COUNT(*) AS number FROM ${escapeId(table)} ${whereData.query}`;
|
|
228
|
+
try {
|
|
229
|
+
const request = pool.request();
|
|
230
|
+
for (let i = 0; i < whereData.value.length; i++) {
|
|
231
|
+
request.input("param" + i, whereData.value[i]);
|
|
232
|
+
}
|
|
233
|
+
const result = await request.query(sqlStr);
|
|
234
|
+
return result.recordset?.[0]?.number || 0;
|
|
235
|
+
} catch {
|
|
236
|
+
return 0;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function remove(table, filter, safeDelete = null) {
|
|
241
|
+
const whereData = where(filter);
|
|
242
|
+
if (whereData == null) throw new Error(WHERE_INVALID);
|
|
243
|
+
if (whereData.value.length < 1) {
|
|
244
|
+
throw new Error("unable to remove as there are no filter attributes");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
let sqlStr;
|
|
248
|
+
if (safeDelete != null) {
|
|
249
|
+
sqlStr = `UPDATE ${escapeId(table)} SET ${escapeId(safeDelete)} = 1 ${whereData.query}`;
|
|
250
|
+
} else {
|
|
251
|
+
sqlStr = `DELETE FROM ${escapeId(table)} ${whereData.query}`;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const request = pool.request();
|
|
255
|
+
for (let i = 0; i < whereData.value.length; i++) {
|
|
256
|
+
request.input("param" + i, whereData.value[i]);
|
|
257
|
+
}
|
|
258
|
+
const result = await request.query(sqlStr);
|
|
259
|
+
const rows = result.rowsAffected?.[0] || 0;
|
|
260
|
+
return { message: rows + " " + table + (rows > 1 ? "s" : "") + " removed" };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function namify(text) {
|
|
264
|
+
return text
|
|
265
|
+
.replace("_", " ")
|
|
266
|
+
.replace(/(^\w{1})|(\s+\w{1})/g, (letter) => letter.toUpperCase());
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function escapeId(name) {
|
|
270
|
+
return "[" + name.replace(/\]/g, "]]") + "]";
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async function upsert(table, data, uniqueKeys = []) {
|
|
274
|
+
if (!uniqueKeys || uniqueKeys.length === 0) {
|
|
275
|
+
return insert(table, data, uniqueKeys);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
let array = Array.isArray(data) ? [...data] : [data];
|
|
279
|
+
const total = array.length;
|
|
280
|
+
const columns = Object.keys(array[0]);
|
|
281
|
+
const nonUniqueColumns = columns.filter((c) => !uniqueKeys.includes(c));
|
|
282
|
+
let lastId = 0;
|
|
283
|
+
|
|
284
|
+
for (const row of array) {
|
|
285
|
+
const request = pool.request();
|
|
286
|
+
let paramIdx = 0;
|
|
287
|
+
|
|
288
|
+
// Build source VALUES
|
|
289
|
+
const valuePlaceholders = columns.map((c) => {
|
|
290
|
+
const p = "@param" + paramIdx;
|
|
291
|
+
request.input("param" + paramIdx, row[c]);
|
|
292
|
+
paramIdx++;
|
|
293
|
+
return p;
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const colList = columns.map(escapeId).join(",");
|
|
297
|
+
const onClause = uniqueKeys
|
|
298
|
+
.map((k) => `target.${escapeId(k)} = source.${escapeId(k)}`)
|
|
299
|
+
.join(" AND ");
|
|
300
|
+
|
|
301
|
+
let mergeSql = `MERGE INTO ${escapeId(table)} AS target`;
|
|
302
|
+
mergeSql += ` USING (VALUES (${valuePlaceholders.join(",")})) AS source (${colList})`;
|
|
303
|
+
mergeSql += ` ON ${onClause}`;
|
|
304
|
+
|
|
305
|
+
if (nonUniqueColumns.length > 0) {
|
|
306
|
+
const updateSet = nonUniqueColumns
|
|
307
|
+
.map((c) => `target.${escapeId(c)} = source.${escapeId(c)}`)
|
|
308
|
+
.join(",");
|
|
309
|
+
mergeSql += ` WHEN MATCHED THEN UPDATE SET ${updateSet}`;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const insertCols = nonUniqueColumns.map((c) => escapeId(c)).join(",");
|
|
313
|
+
const insertVals = nonUniqueColumns
|
|
314
|
+
.map((c) => `source.${escapeId(c)}`)
|
|
315
|
+
.join(",");
|
|
316
|
+
mergeSql += ` WHEN NOT MATCHED THEN INSERT (${insertCols}) VALUES (${insertVals})`;
|
|
317
|
+
mergeSql += ` OUTPUT INSERTED.*;`;
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
const result = await request.query(mergeSql);
|
|
321
|
+
if (result.recordset && result.recordset.length > 0) {
|
|
322
|
+
const firstRow = result.recordset[0];
|
|
323
|
+
lastId = firstRow.id || firstRow[Object.keys(firstRow)[0]] || 0;
|
|
324
|
+
}
|
|
325
|
+
} catch (e) {
|
|
326
|
+
throw mapMssqlError(e);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const response = {
|
|
331
|
+
rows: total,
|
|
332
|
+
message:
|
|
333
|
+
(total === 1
|
|
334
|
+
? `1 ${namify(table)} is `
|
|
335
|
+
: `${total} ${namify(table)}s are `) + "saved",
|
|
336
|
+
type: "success",
|
|
337
|
+
};
|
|
338
|
+
if (total === 1) response.id = lastId;
|
|
339
|
+
return response;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async function insert(table, data, uniqueKeys = []) {
|
|
343
|
+
let array = Array.isArray(data) ? [...data] : [data];
|
|
344
|
+
const total = array.length;
|
|
345
|
+
const columns = Object.keys(array[0]);
|
|
346
|
+
|
|
347
|
+
// Only use MERGE path if all unique key columns are present in the data
|
|
348
|
+
const hasAllUniqueKeys =
|
|
349
|
+
uniqueKeys &&
|
|
350
|
+
uniqueKeys.length > 0 &&
|
|
351
|
+
uniqueKeys.every((k) => columns.includes(k));
|
|
352
|
+
|
|
353
|
+
if (total === 1) {
|
|
354
|
+
const row = array[0];
|
|
355
|
+
const request = pool.request();
|
|
356
|
+
const colList = columns.map(escapeId).join(",");
|
|
357
|
+
const valuePlaceholders = columns.map((c, i) => {
|
|
358
|
+
request.input("param" + i, row[c]);
|
|
359
|
+
return "@param" + i;
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
let sqlStr;
|
|
363
|
+
if (hasAllUniqueKeys) {
|
|
364
|
+
// INSERT with conflict ignore: use TRY/CATCH or check existence
|
|
365
|
+
// For MSSQL, we use a MERGE with WHEN NOT MATCHED only
|
|
366
|
+
const onClause = uniqueKeys
|
|
367
|
+
.map((k) => `target.${escapeId(k)} = source.${escapeId(k)}`)
|
|
368
|
+
.join(" AND ");
|
|
369
|
+
const insertCols = columns.map(escapeId).join(",");
|
|
370
|
+
const insertVals = columns.map((c) => `source.${escapeId(c)}`).join(",");
|
|
371
|
+
|
|
372
|
+
sqlStr = `MERGE INTO ${escapeId(table)} AS target`;
|
|
373
|
+
sqlStr += ` USING (VALUES (${valuePlaceholders.join(",")})) AS source (${colList})`;
|
|
374
|
+
sqlStr += ` ON ${onClause}`;
|
|
375
|
+
sqlStr += ` WHEN NOT MATCHED THEN INSERT (${insertCols}) VALUES (${insertVals})`;
|
|
376
|
+
sqlStr += ` OUTPUT INSERTED.*;`;
|
|
377
|
+
} else {
|
|
378
|
+
sqlStr = `INSERT INTO ${escapeId(table)} (${colList}) OUTPUT INSERTED.* VALUES (${valuePlaceholders.join(",")})`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
try {
|
|
382
|
+
const result = await request.query(sqlStr);
|
|
383
|
+
const insertedRow = result.recordset?.[0];
|
|
384
|
+
const insertId = insertedRow
|
|
385
|
+
? insertedRow.id || insertedRow[Object.keys(insertedRow)[0]] || 0
|
|
386
|
+
: 0;
|
|
387
|
+
return {
|
|
388
|
+
rows: 1,
|
|
389
|
+
message: `1 ${namify(table)} is saved`,
|
|
390
|
+
type: "success",
|
|
391
|
+
id: insertId,
|
|
392
|
+
};
|
|
393
|
+
} catch (e) {
|
|
394
|
+
throw mapMssqlError(e);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Bulk insert
|
|
399
|
+
for (const row of array) {
|
|
400
|
+
const request = pool.request();
|
|
401
|
+
const colList = columns.map(escapeId).join(",");
|
|
402
|
+
const valuePlaceholders = columns.map((c, i) => {
|
|
403
|
+
request.input("param" + i, row[c]);
|
|
404
|
+
return "@param" + i;
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
let sqlStr;
|
|
408
|
+
if (hasAllUniqueKeys) {
|
|
409
|
+
const onClause = uniqueKeys
|
|
410
|
+
.map((k) => `target.${escapeId(k)} = source.${escapeId(k)}`)
|
|
411
|
+
.join(" AND ");
|
|
412
|
+
const insertCols = columns.map(escapeId).join(",");
|
|
413
|
+
const insertVals = columns.map((c) => `source.${escapeId(c)}`).join(",");
|
|
414
|
+
|
|
415
|
+
sqlStr = `MERGE INTO ${escapeId(table)} AS target`;
|
|
416
|
+
sqlStr += ` USING (VALUES (${valuePlaceholders.join(",")})) AS source (${colList})`;
|
|
417
|
+
sqlStr += ` ON ${onClause}`;
|
|
418
|
+
sqlStr += ` WHEN NOT MATCHED THEN INSERT (${insertCols}) VALUES (${insertVals});`;
|
|
419
|
+
} else {
|
|
420
|
+
sqlStr = `INSERT INTO ${escapeId(table)} (${colList}) VALUES (${valuePlaceholders.join(",")})`;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
try {
|
|
424
|
+
await request.query(sqlStr);
|
|
425
|
+
} catch (e) {
|
|
426
|
+
throw mapMssqlError(e);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return {
|
|
431
|
+
rows: total,
|
|
432
|
+
message:
|
|
433
|
+
(total === 1
|
|
434
|
+
? `1 ${namify(table)} is `
|
|
435
|
+
: `${total} ${namify(table)}s are `) + "saved",
|
|
436
|
+
type: "success",
|
|
437
|
+
id: 0,
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
async function disconnect() {
|
|
442
|
+
if (pool) {
|
|
443
|
+
await pool.close();
|
|
444
|
+
pool = null;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
module.exports = {
|
|
449
|
+
connect,
|
|
450
|
+
get,
|
|
451
|
+
list,
|
|
452
|
+
where,
|
|
453
|
+
query,
|
|
454
|
+
qcount,
|
|
455
|
+
remove,
|
|
456
|
+
upsert,
|
|
457
|
+
change: upsert,
|
|
458
|
+
insert,
|
|
459
|
+
disconnect,
|
|
460
|
+
close: disconnect,
|
|
461
|
+
};
|