mm_sqlite 1.0.5 → 1.0.7
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/LICENSE +201 -0
- package/README.md +352 -0
- package/config.json +8 -0
- package/db/data/example.db.db +0 -0
- package/db/db/test_all_methods.db.db +0 -0
- package/db/db/test_core_methods.db.db +0 -0
- package/db/mm.db +0 -0
- package/db.js +791 -117
- package/index.js +694 -164
- package/link_model.js +132 -0
- package/package.json +43 -36
- package/sql.js +1080 -127
- package/sql.json +56 -0
- package/sql_builder.js +375 -0
- package/test.js +76 -137
- package/test_all_methods.js +115 -0
package/sql.js
CHANGED
|
@@ -3,7 +3,16 @@
|
|
|
3
3
|
* @author <a href="http://qww.elins.cn">邱文武</a>
|
|
4
4
|
* @version 1.2
|
|
5
5
|
*/
|
|
6
|
-
require('
|
|
6
|
+
require('mm_logs');
|
|
7
|
+
const SqlString = require('sqlstring');
|
|
8
|
+
|
|
9
|
+
function escape(value, stringifyObjects, timeZone) {
|
|
10
|
+
return SqlString.escape(value, stringifyObjects, timeZone);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function escapeId(value, forbidQualified) {
|
|
14
|
+
return SqlString.escapeId(value, forbidQualified);
|
|
15
|
+
}
|
|
7
16
|
|
|
8
17
|
/**
|
|
9
18
|
* @class 数据库语法通用类
|
|
@@ -24,6 +33,23 @@ class Sql {
|
|
|
24
33
|
* 更改函数 用于增删改
|
|
25
34
|
*/
|
|
26
35
|
this.exec = exec;
|
|
36
|
+
/**
|
|
37
|
+
* 规避SQL注入函数
|
|
38
|
+
* @param {Object} value 值
|
|
39
|
+
* @return {String} 返回执行结果
|
|
40
|
+
*/
|
|
41
|
+
this.escape = function(value) {
|
|
42
|
+
return escape(value);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 规避排序、SQL注入函数
|
|
47
|
+
* @param {String} key 键
|
|
48
|
+
* @return {String} 返回执行结果
|
|
49
|
+
*/
|
|
50
|
+
this.escapeId = function(key) {
|
|
51
|
+
return escapeId(key);
|
|
52
|
+
};
|
|
27
53
|
|
|
28
54
|
/**
|
|
29
55
|
* sql语句
|
|
@@ -65,6 +91,11 @@ class Sql {
|
|
|
65
91
|
*/
|
|
66
92
|
this.orderby = "";
|
|
67
93
|
|
|
94
|
+
/**
|
|
95
|
+
* 查询分组
|
|
96
|
+
*/
|
|
97
|
+
this.groupby = "";
|
|
98
|
+
|
|
68
99
|
/**
|
|
69
100
|
* 是否统计查询结果数
|
|
70
101
|
*/
|
|
@@ -112,25 +143,30 @@ class Sql {
|
|
|
112
143
|
"count_ret": "count_ret"
|
|
113
144
|
}
|
|
114
145
|
};
|
|
146
|
+
|
|
147
|
+
this.like = false;
|
|
115
148
|
}
|
|
116
149
|
}
|
|
117
150
|
|
|
118
151
|
/**
|
|
119
|
-
* @description
|
|
152
|
+
* @description 清空查询条件
|
|
153
|
+
* @return {Object} 返回当前对象
|
|
120
154
|
*/
|
|
121
|
-
Sql.prototype.clear =
|
|
122
|
-
this.
|
|
123
|
-
this.
|
|
155
|
+
Sql.prototype.clear = function() {
|
|
156
|
+
this.where = "";
|
|
157
|
+
this.set = "";
|
|
158
|
+
this.order = "";
|
|
159
|
+
this.view = "";
|
|
124
160
|
this.sql = "";
|
|
125
|
-
this.error;
|
|
126
|
-
this.
|
|
127
|
-
this.
|
|
161
|
+
this.error = "";
|
|
162
|
+
this.like = true;
|
|
163
|
+
this.config = {};
|
|
164
|
+
this.param = {};
|
|
128
165
|
this.page = 0;
|
|
129
|
-
this.size =
|
|
130
|
-
this.
|
|
131
|
-
this.
|
|
132
|
-
this
|
|
133
|
-
this.count_ret = "false";
|
|
166
|
+
this.size = 0;
|
|
167
|
+
this.run = null;
|
|
168
|
+
this.exec = null;
|
|
169
|
+
return this;
|
|
134
170
|
};
|
|
135
171
|
|
|
136
172
|
/**
|
|
@@ -164,7 +200,7 @@ Sql.prototype.toQuery = function(where, sort, view) {
|
|
|
164
200
|
sql += " WHERE " + where;
|
|
165
201
|
}
|
|
166
202
|
if (sort) {
|
|
167
|
-
sql += " ORDER BY " + sort;
|
|
203
|
+
sql += " ORDER BY " + sort.replace(/;/, '');
|
|
168
204
|
}
|
|
169
205
|
sql = sql.replace("{0}", this.table).replace("{1}", view);
|
|
170
206
|
if (this.size && this.page) {
|
|
@@ -224,7 +260,7 @@ Sql.prototype.getSql = function(where, sort, view) {
|
|
|
224
260
|
* @param {String} set 修改的键值
|
|
225
261
|
* @return {Promise|Object} 执行结果
|
|
226
262
|
*/
|
|
227
|
-
Sql.prototype.addOrSetSql = async function(where, set) {
|
|
263
|
+
Sql.prototype.addOrSetSql = async function(where, set, like) {
|
|
228
264
|
if (!where || !set) {
|
|
229
265
|
return -1;
|
|
230
266
|
}
|
|
@@ -233,13 +269,14 @@ Sql.prototype.addOrSetSql = async function(where, set) {
|
|
|
233
269
|
var arr = set.split(',');
|
|
234
270
|
var key = "";
|
|
235
271
|
var value = "";
|
|
236
|
-
arr.
|
|
272
|
+
for (var i = 0; i < arr.length; i++) {
|
|
273
|
+
var o = arr[i];
|
|
237
274
|
var ar = o.split('=');
|
|
238
275
|
if (ar.length === 2) {
|
|
239
276
|
key += "," + ar[0];
|
|
240
277
|
value += "," + ar[1];
|
|
241
278
|
}
|
|
242
|
-
}
|
|
279
|
+
}
|
|
243
280
|
return await this.addSql(key.replace(',', ''), value.replace(',', ''));
|
|
244
281
|
}
|
|
245
282
|
return await this.setSql(where, set);
|
|
@@ -262,6 +299,7 @@ Sql.prototype.countSql = async function(where) {
|
|
|
262
299
|
}
|
|
263
300
|
return n;
|
|
264
301
|
};
|
|
302
|
+
|
|
265
303
|
/**
|
|
266
304
|
* @description 查询数据并返回符合条件总数
|
|
267
305
|
* @param {String} where 查询条件
|
|
@@ -281,20 +319,197 @@ Sql.prototype.getCountSql = async function(where, sort, view) {
|
|
|
281
319
|
};
|
|
282
320
|
return ret;
|
|
283
321
|
};
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* @description 统计学
|
|
325
|
+
* @param {String} where 查询条件
|
|
326
|
+
* @param {String} groupby 分组的字段
|
|
327
|
+
* @param {String} view 返回的字段
|
|
328
|
+
* @param {String} sort 排序方式
|
|
329
|
+
* @return {Promise|Object} 查询到的内容列表和符合条件总数
|
|
330
|
+
*/
|
|
331
|
+
Sql.prototype.groupMathSql = async function(where, groupby, view, sort, method) {
|
|
332
|
+
if (!view) {
|
|
333
|
+
view = "*"
|
|
334
|
+
}
|
|
335
|
+
var viewStr = "";
|
|
336
|
+
if (view.indexOf(",") !== -1) {
|
|
337
|
+
var arr = view.split(",");
|
|
338
|
+
for (var i = 0; i < arr.length; i++) {
|
|
339
|
+
var str = escapeId(arr[i]);
|
|
340
|
+
viewStr += "," + method.toUpperCase() + "(" + str + ") " + method.toLowerCase() + "_" + str.replace(
|
|
341
|
+
/`/g, "")
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
viewStr = "," + method.toUpperCase() + "(" + escapeId(view) + ") " + method.toLowerCase() + "_" +
|
|
345
|
+
view.replace(/`/g, "")
|
|
346
|
+
}
|
|
347
|
+
var sql = "SELECT " + (groupby ? escapeId(groupby) : "") + viewStr + " FROM `" + this.table + "`";
|
|
348
|
+
if (where) {
|
|
349
|
+
sql += ' WHERE ' + where;
|
|
350
|
+
}
|
|
351
|
+
if (groupby) {
|
|
352
|
+
sql += " GROUP BY " + escapeId(groupby);
|
|
353
|
+
}
|
|
354
|
+
if (sort) {
|
|
355
|
+
sql += " ORDER BY " + sort;
|
|
356
|
+
}
|
|
357
|
+
if (this.size && this.page) {
|
|
358
|
+
var start = this.size * (this.page - 1);
|
|
359
|
+
sql += " limit " + start + ',' + this.size;
|
|
360
|
+
}
|
|
361
|
+
return await this.run(sql);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* @description 分组求平均值
|
|
367
|
+
* @param {String} where 查询条件
|
|
368
|
+
* @param {String} groupby 分组的字段
|
|
369
|
+
* @param {String} view 返回的字段
|
|
370
|
+
* @param {String} sort 排序方式
|
|
371
|
+
* @return {Promise|Object} 查询到的内容列表和符合条件总数
|
|
372
|
+
*/
|
|
373
|
+
Sql.prototype.groupAvgSql = async function(where, groupby, view, sort = "") {
|
|
374
|
+
return await this.groupMathSql(where, groupby, view, sort, "AVG");
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* @description 分组合计数值
|
|
379
|
+
* @param {String} where 查询条件
|
|
380
|
+
* @param {String} groupby 分组的字段
|
|
381
|
+
* @param {String} view 返回的字段
|
|
382
|
+
* @param {String} sort 排序方式
|
|
383
|
+
* @return {Promise|Object} 查询到的内容列表和符合条件总数
|
|
384
|
+
*/
|
|
385
|
+
Sql.prototype.groupSumSql = async function(where, groupby, view, sort = "") {
|
|
386
|
+
return await this.groupMathSql(where, groupby, view, sort, "SUM");
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* @description 分组合计不同条数
|
|
391
|
+
* @param {String} where 查询条件
|
|
392
|
+
* @param {String} groupby 分组的字段
|
|
393
|
+
* @param {String} view 返回的字段
|
|
394
|
+
* @return {Promise|Object} 查询到的内容列表和符合条件总数
|
|
395
|
+
*/
|
|
396
|
+
Sql.prototype.groupCountSql = async function(where, groupby, view, sort = "") {
|
|
397
|
+
return await this.groupMathSql(where, groupby, view, sort, "COUNT");
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* @description 统计学
|
|
403
|
+
* @param {Object} query 查询条件
|
|
404
|
+
* @param {String} groupby 分组的字段
|
|
405
|
+
* @param {String} view 返回的字段
|
|
406
|
+
* @param {String} sort 排序方式
|
|
407
|
+
* @return {Promise|Object} 查询到的内容列表和符合条件总数
|
|
408
|
+
*/
|
|
409
|
+
Sql.prototype.groupMath = async function(query, groupby, view, sort, method) {
|
|
410
|
+
var where = this.toWhere(query, this.like);
|
|
411
|
+
return await this.groupMathSql(where, groupby, view, sort, method);
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* @description 分组求平均值
|
|
416
|
+
* @param {Object} query 查询条件
|
|
417
|
+
* @param {String} groupby 分组的字段
|
|
418
|
+
* @param {String} view 返回的字段
|
|
419
|
+
* @param {String} sort 排序方式
|
|
420
|
+
* @return {Promise|Object} 查询到的内容列表和符合条件总数
|
|
421
|
+
*/
|
|
422
|
+
Sql.prototype.groupAvg = async function(query, groupby, view, sort) {
|
|
423
|
+
return await this.groupMath(query, groupby, view, sort, "AVG");
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* @description 分组合计数值
|
|
428
|
+
* @param {Object} query 查询条件
|
|
429
|
+
* @param {String} groupby 分组的字段
|
|
430
|
+
* @param {String} view 返回的字段
|
|
431
|
+
* @param {String} sort 排序方式
|
|
432
|
+
* @return {Promise|Object} 查询到的内容列表和符合条件总数
|
|
433
|
+
*/
|
|
434
|
+
Sql.prototype.groupSum = async function(query, groupby, view, sort) {
|
|
435
|
+
return await this.groupMath(query, groupby, view, sort, "SUM");
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* @description 分组合计不同条数
|
|
440
|
+
* @param {Object} query 查询条件
|
|
441
|
+
* @param {String} groupby 分组的字段
|
|
442
|
+
* @param {String} view 返回的字段
|
|
443
|
+
* @return {Promise|Object} 查询到的内容列表和符合条件总数
|
|
444
|
+
*/
|
|
445
|
+
Sql.prototype.groupCount = async function(query, groupby, view, sort) {
|
|
446
|
+
return await this.groupMath(query, groupby, view, sort, "COUNT");
|
|
447
|
+
};
|
|
448
|
+
|
|
284
449
|
/* === sql语句拼接函数 === */
|
|
285
|
-
///
|
|
286
450
|
/**
|
|
287
451
|
* @description 转为where语句
|
|
288
452
|
* @param {Object} obj 用作拼接的对象
|
|
453
|
+
* @param {Boolean} like 是否使用like匹配, 默认不使用
|
|
289
454
|
* @return {String} where格式sql语句字符串
|
|
290
455
|
*/
|
|
291
|
-
Sql.prototype.toWhere = function(obj) {
|
|
456
|
+
Sql.prototype.toWhere = function(obj, like) {
|
|
292
457
|
var where = "";
|
|
293
|
-
|
|
294
|
-
|
|
458
|
+
if (like === undefined) {
|
|
459
|
+
like = this.like;
|
|
460
|
+
}
|
|
461
|
+
if (like) {
|
|
462
|
+
for (var k in obj) {
|
|
463
|
+
var val = obj[k];
|
|
464
|
+
if (val && typeof(val) === "string") {
|
|
465
|
+
val = val.trim("'");
|
|
466
|
+
}
|
|
467
|
+
val = escape(val);
|
|
468
|
+
if (k.endWith('_min')) {
|
|
469
|
+
where += " and " + escapeId(k.replace('_min', '')) + " >= " + val;
|
|
470
|
+
} else if (k.endWith('_max')) {
|
|
471
|
+
where += " and " + escapeId(k.replace('_max', '')) + " <= " + val;
|
|
472
|
+
} else if (k.endWith('_not')) {
|
|
473
|
+
where += " and " + escapeId(k.replace('_not', '')) + " != " + val;
|
|
474
|
+
} else if (k.endWith('_has')) {
|
|
475
|
+
var vals = val.trim("'").split(',').map((o) => {
|
|
476
|
+
return "'" + o + "'"
|
|
477
|
+
});
|
|
478
|
+
where += " and " + escapeId(k.replace('_has', '')) + " in (" + vals.join(',') + ")";
|
|
479
|
+
} else if (k.endWith('_like')) {
|
|
480
|
+
where += " and " + escapeId(k.replace('_like', '')) + " LIKE '%" + val.trim("'") + "%'";
|
|
481
|
+
} else if (typeof(val) === "string" && !/^[0-9]+$/.test(val)) {
|
|
482
|
+
where += " and " + escapeId(k) + " LIKE '%" + val.trim("'") + "%'"
|
|
483
|
+
} else {
|
|
484
|
+
where += " and " + escapeId(k) + " = " + val;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
} else {
|
|
488
|
+
for (var k in obj) {
|
|
489
|
+
var val = obj[k];
|
|
490
|
+
if (val && typeof(val) === "string") {
|
|
491
|
+
val = val.trim("'");
|
|
492
|
+
}
|
|
493
|
+
val = escape(val);
|
|
494
|
+
if (k.endWith('_min')) {
|
|
495
|
+
where += " and " + escapeId(k.replace('_min', '')) + " >= " + val;
|
|
496
|
+
} else if (k.endWith('_max')) {
|
|
497
|
+
where += " and " + escapeId(k.replace('_max', '')) + " <= " + val;
|
|
498
|
+
} else if (k.endWith('_not')) {
|
|
499
|
+
where += " and " + escapeId(k.replace('_not', '')) + " != " + val;
|
|
500
|
+
} else if (k.endWith('_has')) {
|
|
501
|
+
var vals = val.trim("'").split(',').map((o) => {
|
|
502
|
+
return "'" + o + "'"
|
|
503
|
+
});
|
|
504
|
+
where += " and " + escapeId(k.replace('_has', '')) + " in (" + vals.join(',') + ")";
|
|
505
|
+
} else {
|
|
506
|
+
where += " and " + escapeId(k) + " = " + val;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
295
509
|
}
|
|
296
510
|
return where.replace(" and ", "");
|
|
297
511
|
};
|
|
512
|
+
|
|
298
513
|
/**
|
|
299
514
|
* @description 转为set语句
|
|
300
515
|
* @param {Object} obj 用作拼接的对象
|
|
@@ -303,7 +518,25 @@ Sql.prototype.toWhere = function(obj) {
|
|
|
303
518
|
Sql.prototype.toSet = function(obj) {
|
|
304
519
|
var set = "";
|
|
305
520
|
for (var k in obj) {
|
|
306
|
-
|
|
521
|
+
if (!Object.prototype.hasOwnProperty.call(obj, k)) continue;
|
|
522
|
+
|
|
523
|
+
var val = obj[k];
|
|
524
|
+
if (val === undefined || val === null) continue;
|
|
525
|
+
|
|
526
|
+
if (typeof val === "string") {
|
|
527
|
+
val = val.trim("'");
|
|
528
|
+
}
|
|
529
|
+
val = escape(val);
|
|
530
|
+
|
|
531
|
+
if (k.endWith('_add')) {
|
|
532
|
+
var k2 = escapeId(k.replace('_add', ''));
|
|
533
|
+
set += "," + k2 + " = " + k2 + " + " + val;
|
|
534
|
+
} else if (k.endWith('_del')) {
|
|
535
|
+
var k3 = escapeId(k.replace('_del', ''));
|
|
536
|
+
set += "," + k3 + " = " + k3 + " - " + val;
|
|
537
|
+
} else {
|
|
538
|
+
set += "," + escapeId(k) + " = " + val;
|
|
539
|
+
}
|
|
307
540
|
}
|
|
308
541
|
return set.replace(",", "");
|
|
309
542
|
};
|
|
@@ -314,11 +547,21 @@ Sql.prototype.toSet = function(obj) {
|
|
|
314
547
|
* @return {String} sql语句
|
|
315
548
|
*/
|
|
316
549
|
Sql.prototype.toAddSql = function(item) {
|
|
550
|
+
if (!this.table || !item || typeof item !== 'object') {
|
|
551
|
+
throw new Error('表名或数据未设置');
|
|
552
|
+
}
|
|
553
|
+
|
|
317
554
|
var key = "";
|
|
318
555
|
var val = "";
|
|
319
556
|
for (var k in item) {
|
|
320
|
-
|
|
321
|
-
|
|
557
|
+
if (!Object.prototype.hasOwnProperty.call(item, k)) continue;
|
|
558
|
+
|
|
559
|
+
key += "," + escapeId(k);
|
|
560
|
+
var value = item[k];
|
|
561
|
+
if (typeof value === "string") {
|
|
562
|
+
value = value.trim("'");
|
|
563
|
+
}
|
|
564
|
+
val += "," + escape(value);
|
|
322
565
|
}
|
|
323
566
|
var sql = "INSERT INTO `{0}` ({1}) VALUES ({2});";
|
|
324
567
|
return sql.replace("{0}", this.table).replace("{1}", key.replace(",", "")).replace("{2}", val.replace(",", ""));
|
|
@@ -327,10 +570,14 @@ Sql.prototype.toAddSql = function(item) {
|
|
|
327
570
|
/**
|
|
328
571
|
* @description 转删除sql语句
|
|
329
572
|
* @param {Object} query 查询键值
|
|
573
|
+
* @param {Boolean} like 是否使用like匹配, 为空使用默认方式
|
|
330
574
|
* @return {String} sql语句
|
|
331
575
|
*/
|
|
332
|
-
Sql.prototype.toDelSql = function(query) {
|
|
333
|
-
|
|
576
|
+
Sql.prototype.toDelSql = function(query, like) {
|
|
577
|
+
if (!this.table) {
|
|
578
|
+
throw new Error('表名未设置');
|
|
579
|
+
}
|
|
580
|
+
var where = this.toWhere(query, like);
|
|
334
581
|
var sql = "DELETE FROM `{0}` WHERE {1};";
|
|
335
582
|
return sql.replace("{0}", this.table).replace("{1}", where);
|
|
336
583
|
};
|
|
@@ -339,10 +586,14 @@ Sql.prototype.toDelSql = function(query) {
|
|
|
339
586
|
* @description 转修改sql语句
|
|
340
587
|
* @param {Object} query 查询的键值集合
|
|
341
588
|
* @param {Object} item 修改的键值集合
|
|
589
|
+
* @param {Boolean} like 是否使用like匹配, 为空使用默认方式
|
|
342
590
|
* @return {String} sql语句
|
|
343
591
|
*/
|
|
344
|
-
Sql.prototype.toSetSql = function(query, item) {
|
|
345
|
-
|
|
592
|
+
Sql.prototype.toSetSql = function(query, item, like) {
|
|
593
|
+
if (!this.table) {
|
|
594
|
+
throw new Error('表名未设置');
|
|
595
|
+
}
|
|
596
|
+
var where = this.toWhere(query, like);
|
|
346
597
|
var set = this.toSet(item);
|
|
347
598
|
var sql = "UPDATE `{0}` SET {1} WHERE {2};";
|
|
348
599
|
return sql.replace("{0}", this.table).replace("{1}", set).replace("{2}", where);
|
|
@@ -353,42 +604,110 @@ Sql.prototype.toSetSql = function(query, item) {
|
|
|
353
604
|
* @param {Object} query 查询键值集合
|
|
354
605
|
* @param {String} sort 排序规则
|
|
355
606
|
* @param {String} view 显示的字段
|
|
607
|
+
* @param {Boolean} like 是否使用like匹配, 为空使用默认方式
|
|
356
608
|
* @return {String} sql语句
|
|
357
609
|
*/
|
|
358
|
-
Sql.prototype.toGetSql = function(query, sort, view) {
|
|
359
|
-
var where = this.toWhere(query);
|
|
360
|
-
|
|
361
|
-
return sql;
|
|
610
|
+
Sql.prototype.toGetSql = function(query, sort, view, like) {
|
|
611
|
+
var where = this.toWhere(query, like);
|
|
612
|
+
return this.toQuery(where, sort, view);
|
|
362
613
|
};
|
|
363
614
|
/* === 传入对象操作 === */
|
|
364
615
|
/**
|
|
365
616
|
* @description 增加数据
|
|
366
|
-
* @param {Object}
|
|
617
|
+
* @param {Object} body 添加的对象
|
|
367
618
|
* @return {Promise|Object} 执行结果
|
|
368
619
|
*/
|
|
369
|
-
Sql.prototype.add = function(
|
|
370
|
-
|
|
371
|
-
|
|
620
|
+
Sql.prototype.add = async function(body) {
|
|
621
|
+
if (!this.table || !body || typeof body !== 'object') {
|
|
622
|
+
throw new Error('表名或数据未设置');
|
|
623
|
+
}
|
|
624
|
+
try {
|
|
625
|
+
// 触发前置事件
|
|
626
|
+
if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
|
|
627
|
+
await $.eventer.run("sqlite_add_before:" + this.table, { body });
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
var sql = this.toAddSql(body);
|
|
631
|
+
this.sql = sql;
|
|
632
|
+
var bl = await this.exec(sql);
|
|
633
|
+
|
|
634
|
+
// 触发后置事件
|
|
635
|
+
if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
|
|
636
|
+
await $.eventer.run("sqlite_add_after:" + this.table, { body, sql: this.sql, error: this.error, bl });
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
return bl;
|
|
640
|
+
} catch (err) {
|
|
641
|
+
this.error = err.message;
|
|
642
|
+
$.log.error(`添加数据失败: ${err.message}`);
|
|
643
|
+
throw err;
|
|
644
|
+
}
|
|
372
645
|
};
|
|
373
646
|
/**
|
|
374
647
|
* @description 删除数据
|
|
375
648
|
* @param {Object} query 查询条件集合
|
|
649
|
+
* @param {Boolean} like 是否使用like匹配, 为空使用默认方式
|
|
376
650
|
* @return {Promise|Object} 执行结果
|
|
377
651
|
*/
|
|
378
|
-
Sql.prototype.del = function(query) {
|
|
379
|
-
|
|
380
|
-
|
|
652
|
+
Sql.prototype.del = async function(query, like) {
|
|
653
|
+
if (!this.table) {
|
|
654
|
+
throw new Error('表名未设置');
|
|
655
|
+
}
|
|
656
|
+
try {
|
|
657
|
+
// 触发前置事件
|
|
658
|
+
if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
|
|
659
|
+
await $.eventer.run("sqlite_del_before:" + this.table, { query, like });
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
var sql = this.toDelSql(query, like);
|
|
663
|
+
this.sql = sql;
|
|
664
|
+
var bl = await this.exec(sql);
|
|
665
|
+
|
|
666
|
+
// 触发后置事件
|
|
667
|
+
if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
|
|
668
|
+
await $.eventer.run("sqlite_del_after:" + this.table, { query, like, sql: this.sql, error: this.error, bl });
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
return bl;
|
|
672
|
+
} catch (err) {
|
|
673
|
+
this.error = err.message;
|
|
674
|
+
$.log.error(`删除数据失败: ${err.message}`);
|
|
675
|
+
throw err;
|
|
676
|
+
}
|
|
381
677
|
};
|
|
382
678
|
|
|
383
679
|
/**
|
|
384
680
|
* @description 修改数据
|
|
385
681
|
* @param {Object} query 查询条件集合
|
|
386
|
-
* @param {Object}
|
|
682
|
+
* @param {Object} body 修改的键值集合
|
|
683
|
+
* @param {Boolean} like 是否使用like匹配, 为空使用默认方式
|
|
387
684
|
* @return {Promise|Object} 执行结果
|
|
388
685
|
*/
|
|
389
|
-
Sql.prototype.set = function(query,
|
|
390
|
-
|
|
391
|
-
|
|
686
|
+
Sql.prototype.set = async function(query, body, like) {
|
|
687
|
+
if (!this.table || !body || typeof body !== 'object') {
|
|
688
|
+
throw new Error('表名或数据未设置');
|
|
689
|
+
}
|
|
690
|
+
try {
|
|
691
|
+
// 触发前置事件
|
|
692
|
+
if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
|
|
693
|
+
await $.eventer.run("sqlite_set_before:" + this.table, { query, body, like, page: this.page, size: this.size });
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
var sql = this.toSetSql(query, body, like);
|
|
697
|
+
this.sql = sql;
|
|
698
|
+
var bl = await this.exec(sql);
|
|
699
|
+
|
|
700
|
+
// 触发后置事件
|
|
701
|
+
if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
|
|
702
|
+
await $.eventer.run("sqlite_set_after:" + this.table, { query, body, like, page: this.page, size: this.size, sql: this.sql, error: this.error, bl });
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return bl;
|
|
706
|
+
} catch (err) {
|
|
707
|
+
this.error = err.message;
|
|
708
|
+
$.log.error(`修改数据失败: ${err.message}`);
|
|
709
|
+
throw err;
|
|
710
|
+
}
|
|
392
711
|
};
|
|
393
712
|
|
|
394
713
|
/**
|
|
@@ -396,82 +715,535 @@ Sql.prototype.set = function(query, item) {
|
|
|
396
715
|
* @param {Object} query 查询条件
|
|
397
716
|
* @param {String} sort 排序
|
|
398
717
|
* @param {String} view 返回的字段
|
|
718
|
+
* @param {Boolean} like 是否使用like匹配, 为空使用默认方式
|
|
719
|
+
* @param {Number} timeout 超时时间(毫秒),默认30000ms
|
|
399
720
|
* @return {Promise|Array} 查询结果
|
|
400
721
|
*/
|
|
401
|
-
Sql.prototype.get = function(query, sort, view) {
|
|
402
|
-
|
|
403
|
-
|
|
722
|
+
Sql.prototype.get = async function(query, sort, view, like, timeout = 30000) {
|
|
723
|
+
if (!this.table) {
|
|
724
|
+
throw new Error('表名未设置');
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
try {
|
|
728
|
+
// 添加超时控制的Promise
|
|
729
|
+
var timeoutPromise = new Promise((_, reject) => {
|
|
730
|
+
setTimeout(() => reject(new Error('查询操作超时')), timeout);
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
// 使用Promise.race实现超时控制
|
|
734
|
+
return await Promise.race([
|
|
735
|
+
(async () => {
|
|
736
|
+
// 安全触发事件,避免事件系统问题影响核心功能
|
|
737
|
+
if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
|
|
738
|
+
try {
|
|
739
|
+
// 添加超时保护的事件触发
|
|
740
|
+
await Promise.race([
|
|
741
|
+
$.eventer.run("sqlite_get_before:" + this.table, {
|
|
742
|
+
query,
|
|
743
|
+
sort,
|
|
744
|
+
view,
|
|
745
|
+
like,
|
|
746
|
+
page: this.page,
|
|
747
|
+
size: this.size
|
|
748
|
+
}),
|
|
749
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('事件处理超时')), 5000))
|
|
750
|
+
]);
|
|
751
|
+
} catch (eventError) {
|
|
752
|
+
$.log.warn(`事件处理失败,但不影响查询: ${eventError.message}`);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// 生成SQL并执行
|
|
757
|
+
var sql = this.toGetSql(query, sort, view, like);
|
|
758
|
+
this.sql = sql;
|
|
759
|
+
var list = await this.run(sql);
|
|
760
|
+
|
|
761
|
+
// 安全触发后置事件
|
|
762
|
+
if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
|
|
763
|
+
try {
|
|
764
|
+
// 添加超时保护的事件触发
|
|
765
|
+
await Promise.race([
|
|
766
|
+
$.eventer.run("sqlite_get_after:" + this.table, {
|
|
767
|
+
query,
|
|
768
|
+
sort,
|
|
769
|
+
view,
|
|
770
|
+
like,
|
|
771
|
+
page: this.page,
|
|
772
|
+
size: this.size,
|
|
773
|
+
sql: this.sql,
|
|
774
|
+
error: this.error,
|
|
775
|
+
list
|
|
776
|
+
}),
|
|
777
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('事件处理超时')), 5000))
|
|
778
|
+
]);
|
|
779
|
+
} catch (eventError) {
|
|
780
|
+
$.log.warn(`事件处理失败,但不影响查询返回: ${eventError.message}`);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
return list;
|
|
785
|
+
})(),
|
|
786
|
+
timeoutPromise
|
|
787
|
+
]);
|
|
788
|
+
} catch (err) {
|
|
789
|
+
this.error = err.message;
|
|
790
|
+
$.log.error(`查询数据失败: ${err.message}`);
|
|
791
|
+
throw err;
|
|
792
|
+
}
|
|
404
793
|
};
|
|
405
794
|
|
|
406
795
|
/**
|
|
407
796
|
* @description 添加或修改
|
|
408
797
|
* @param {Object} where 查询条件集合
|
|
409
798
|
* @param {Object} set 修改的键值
|
|
799
|
+
* @param {Boolean} like 是否使用like匹配, 默认不使用
|
|
410
800
|
* @return {Promise|Object} 执行结果
|
|
411
801
|
*/
|
|
412
|
-
|
|
413
|
-
|
|
802
|
+
/**
|
|
803
|
+
* 添加或修改数据(存在则修改,不存在则添加)
|
|
804
|
+
* @param {Object|String} where 查询条件
|
|
805
|
+
* @param {Object|String} set 要设置的数据
|
|
806
|
+
* @param {Boolean} like 是否使用like匹配, 为空使用默认方式
|
|
807
|
+
* @return {Promise<Object>} 执行结果
|
|
808
|
+
*/
|
|
809
|
+
Sql.prototype.addOrSet = async function(where, set, like) {
|
|
810
|
+
if (!this.table || !where || !set) {
|
|
811
|
+
throw new Error('表名、条件或数据未设置');
|
|
812
|
+
}
|
|
813
|
+
try {
|
|
814
|
+
let query = where;
|
|
815
|
+
let body = set;
|
|
816
|
+
let whereStr;
|
|
817
|
+
|
|
818
|
+
if (typeof where === "object") {
|
|
819
|
+
whereStr = await this.toWhere(where, like);
|
|
820
|
+
} else {
|
|
821
|
+
whereStr = where;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
const count = await this.countSql(whereStr);
|
|
825
|
+
|
|
826
|
+
if (count === 0) {
|
|
827
|
+
let key = "";
|
|
828
|
+
let value = "";
|
|
829
|
+
|
|
830
|
+
if (typeof set === "string") {
|
|
831
|
+
const arr = set.split(",");
|
|
832
|
+
for (const o of arr) {
|
|
833
|
+
const ar = o.split('=');
|
|
834
|
+
if (ar.length === 2) {
|
|
835
|
+
key += "," + ar[0];
|
|
836
|
+
value += "," + ar[1];
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
} else {
|
|
840
|
+
// 触发前置事件
|
|
841
|
+
if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function' && typeof body === "object") {
|
|
842
|
+
await $.eventer.run("sqlite_add_before:" + this.table, { body });
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
for (const k in set) {
|
|
846
|
+
if (!Object.prototype.hasOwnProperty.call(set, k)) continue;
|
|
847
|
+
|
|
848
|
+
key += "," + this.escapeId(k);
|
|
849
|
+
let val = set[k];
|
|
850
|
+
if (typeof val === "string") {
|
|
851
|
+
val = val.trim("'");
|
|
852
|
+
}
|
|
853
|
+
value += "," + this.escape(val);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
const bl = await this.addSql(key.replace(",", ""), value.replace(",", ""));
|
|
858
|
+
|
|
859
|
+
// 触发后置事件
|
|
860
|
+
if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function' && typeof body === "object") {
|
|
861
|
+
await $.eventer.run("sqlite_add_after:" + this.table, { body, sql: this.sql, error: this.error, bl });
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
return bl;
|
|
865
|
+
} else {
|
|
866
|
+
// 触发前置事件
|
|
867
|
+
if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function' && typeof set === "object") {
|
|
868
|
+
await $.eventer.run("sqlite_set_before:" + this.table, { query, body, like, page: this.page, size: this.size, sql: this.sql });
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (typeof set === "object") {
|
|
872
|
+
set = await this.toSet(set);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
const bl1 = await this.setSql(whereStr, set);
|
|
876
|
+
|
|
877
|
+
// 触发后置事件
|
|
878
|
+
if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function' && typeof body === "object") {
|
|
879
|
+
await $.eventer.run("sqlite_set_after:" + this.table, { query, body, like, page: this.page, size: this.size, sql: this.sql, error: this.error, bl: bl1 });
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
return bl1;
|
|
883
|
+
}
|
|
884
|
+
} catch (err) {
|
|
885
|
+
this.error = err.message;
|
|
886
|
+
$.log.error(`添加或修改数据失败: ${err.message}`);
|
|
887
|
+
throw err;
|
|
888
|
+
}
|
|
414
889
|
};
|
|
415
890
|
|
|
416
891
|
/**
|
|
417
|
-
*
|
|
418
|
-
* @param {Object} query
|
|
419
|
-
* @
|
|
892
|
+
* 统计记录数
|
|
893
|
+
* @param {Object} query 查询条件
|
|
894
|
+
* @param {Boolean} like 是否使用like匹配, 为空使用默认方式
|
|
895
|
+
* @param {Number} timeout 超时时间(毫秒),默认30000ms
|
|
896
|
+
* @return {Promise|Number} 记录数
|
|
420
897
|
*/
|
|
421
|
-
Sql.prototype.count = function(query) {
|
|
422
|
-
|
|
898
|
+
Sql.prototype.count = async function(query, like, timeout = 30000) {
|
|
899
|
+
if (!this.table) {
|
|
900
|
+
throw new Error('表名未设置');
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
try {
|
|
904
|
+
// 添加超时控制
|
|
905
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
906
|
+
setTimeout(() => reject(new Error('统计操作超时')), timeout);
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
return await Promise.race([
|
|
910
|
+
(async () => {
|
|
911
|
+
// 安全触发事件
|
|
912
|
+
if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
|
|
913
|
+
try {
|
|
914
|
+
await Promise.race([
|
|
915
|
+
$.eventer.run("sqlite_count_before:" + this.table, { query, like }),
|
|
916
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('事件处理超时')), 5000))
|
|
917
|
+
]);
|
|
918
|
+
} catch (eventError) {
|
|
919
|
+
$.log.warn(`事件处理失败,但不影响统计: ${eventError.message}`);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// 正确生成count SQL
|
|
924
|
+
const where = typeof query === 'string' ? query : await this.toWhere(query, like);
|
|
925
|
+
const sql = "SELECT COUNT(*) as num FROM `" + this.table + "`" + (where ? " WHERE " + where : "");
|
|
926
|
+
this.sql = sql;
|
|
927
|
+
const list = await this.run(sql);
|
|
928
|
+
const total = list && list[0] && list[0].num ? parseInt(list[0].num) : 0;
|
|
929
|
+
|
|
930
|
+
// 安全触发事件
|
|
931
|
+
if (typeof $.eventer === 'object' && typeof $.eventer.run === 'function') {
|
|
932
|
+
try {
|
|
933
|
+
await Promise.race([
|
|
934
|
+
$.eventer.run("sqlite_count_after:" + this.table, {
|
|
935
|
+
query,
|
|
936
|
+
like,
|
|
937
|
+
sql: this.sql,
|
|
938
|
+
error: this.error,
|
|
939
|
+
total
|
|
940
|
+
}),
|
|
941
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('事件处理超时')), 5000))
|
|
942
|
+
]);
|
|
943
|
+
} catch (eventError) {
|
|
944
|
+
$.log.warn(`事件处理失败,但不影响统计返回: ${eventError.message}`);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
return total;
|
|
949
|
+
})(),
|
|
950
|
+
timeoutPromise
|
|
951
|
+
]);
|
|
952
|
+
} catch (err) {
|
|
953
|
+
this.error = err.message;
|
|
954
|
+
$.log.error(`统计记录数失败: ${err.message}`, { sql: this.sql });
|
|
955
|
+
throw err;
|
|
956
|
+
}
|
|
423
957
|
};
|
|
424
958
|
|
|
425
959
|
/**
|
|
426
|
-
*
|
|
960
|
+
* 查询数据并返回符合条件总数
|
|
427
961
|
* @param {Object} query 查询条件
|
|
428
|
-
* @param {
|
|
429
|
-
* @param {
|
|
430
|
-
* @return {Promise
|
|
962
|
+
* @param {Boolean} like 是否使用like匹配, 为空使用默认方式
|
|
963
|
+
* @param {Number} timeout 超时时间(毫秒),默认30000ms
|
|
964
|
+
* @return {Promise<Number>} 记录数
|
|
431
965
|
*/
|
|
432
|
-
Sql.prototype.getCount = async function(query,
|
|
433
|
-
return this.
|
|
966
|
+
Sql.prototype.getCount = async function(query, like, timeout = 30000) {
|
|
967
|
+
return await this.count(query, like, timeout);
|
|
434
968
|
};
|
|
435
969
|
|
|
436
970
|
/* === 传入数组操作 === */
|
|
437
971
|
/**
|
|
438
972
|
* @description 添加多条数据
|
|
439
973
|
* @param {Array} list 对象数组
|
|
974
|
+
* @param {Boolean} lock 是否锁定
|
|
975
|
+
* @param {Number} batchSize 批量大小,默认100
|
|
976
|
+
* @param {Number} timeout 超时时间,默认60000毫秒
|
|
440
977
|
* @return {Promise|Object} 执行结果
|
|
441
978
|
*/
|
|
442
|
-
Sql.prototype.addList = function(list) {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
979
|
+
Sql.prototype.addList = async function(list, lock = true, batchSize = 100, timeout = 60000) {
|
|
980
|
+
if (!this.table || !Array.isArray(list) || list.length === 0) {
|
|
981
|
+
throw new Error('表名或数据列表未设置');
|
|
982
|
+
}
|
|
983
|
+
try {
|
|
984
|
+
// 添加整体操作超时控制
|
|
985
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
986
|
+
setTimeout(() => reject(new Error('批量添加数据操作超时')), timeout);
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
return await Promise.race([
|
|
990
|
+
(async () => {
|
|
991
|
+
// 如果数据量较小,直接处理
|
|
992
|
+
if (list.length <= batchSize) {
|
|
993
|
+
// 使用批量插入语法,不使用事务包装
|
|
994
|
+
this.sql = this.toBatchAddSql(list);
|
|
995
|
+
return await this.exec(this.sql);
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// 分批处理大数据量
|
|
999
|
+
const totalBatches = Math.ceil(list.length / batchSize);
|
|
1000
|
+
$.log.info(`开始分批添加数据,共${totalBatches}批,每批${batchSize}条`);
|
|
1001
|
+
|
|
1002
|
+
// 分批执行,每批一个单独的批量插入语句
|
|
1003
|
+
let finalResult = null;
|
|
1004
|
+
for (let i = 0; i < totalBatches; i++) {
|
|
1005
|
+
const batch = list.slice(i * batchSize, (i + 1) * batchSize);
|
|
1006
|
+
$.log.debug(`处理第${i + 1}/${totalBatches}批数据,${batch.length}条`);
|
|
1007
|
+
|
|
1008
|
+
// 生成批量插入SQL
|
|
1009
|
+
const batchSql = this.toBatchAddSql(batch);
|
|
1010
|
+
this.sql = batchSql;
|
|
1011
|
+
|
|
1012
|
+
// 为每批操作添加超时控制
|
|
1013
|
+
finalResult = await Promise.race([
|
|
1014
|
+
this.exec(batchSql),
|
|
1015
|
+
new Promise((_, reject) => {
|
|
1016
|
+
setTimeout(() => reject(new Error(`第${i + 1}批数据处理超时`)), timeout / totalBatches);
|
|
1017
|
+
})
|
|
1018
|
+
]);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
$.log.success(`批量添加数据完成,共${list.length}条`);
|
|
1022
|
+
return finalResult;
|
|
1023
|
+
})(),
|
|
1024
|
+
timeoutPromise
|
|
1025
|
+
]);
|
|
1026
|
+
} catch (error) {
|
|
1027
|
+
$.log.error(`[${this.constructor.name}] [addList]`, '批量添加数据失败', {
|
|
1028
|
+
error: error.message,
|
|
1029
|
+
table: this.table,
|
|
1030
|
+
list_length: list.length
|
|
1031
|
+
});
|
|
1032
|
+
throw error;
|
|
447
1033
|
}
|
|
448
|
-
return this.exec(sql);
|
|
449
1034
|
};
|
|
1035
|
+
|
|
450
1036
|
/**
|
|
451
|
-
*
|
|
1037
|
+
* 生成批量插入SQL语句
|
|
1038
|
+
* @param {Array} list 数据列表
|
|
1039
|
+
* @return {String} 批量插入SQL语句
|
|
1040
|
+
*/
|
|
1041
|
+
Sql.prototype.toBatchAddSql = function(list) {
|
|
1042
|
+
if (!this.table || !Array.isArray(list) || list.length === 0) {
|
|
1043
|
+
throw new Error('表名或数据列表未设置');
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// 获取第一个对象的键名作为列名
|
|
1047
|
+
const firstItem = list[0];
|
|
1048
|
+
if (!firstItem || typeof firstItem !== 'object') {
|
|
1049
|
+
throw new Error('数据格式错误');
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
let keys = [];
|
|
1053
|
+
for (const k in firstItem) {
|
|
1054
|
+
if (Object.prototype.hasOwnProperty.call(firstItem, k)) {
|
|
1055
|
+
keys.push(this.escapeId(k));
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// 构建列名部分
|
|
1060
|
+
const columns = keys.join(',');
|
|
1061
|
+
|
|
1062
|
+
// 构建值部分
|
|
1063
|
+
const valuesList = [];
|
|
1064
|
+
for (const item of list) {
|
|
1065
|
+
const values = [];
|
|
1066
|
+
for (const k of keys) {
|
|
1067
|
+
const unescapedKey = k.replace(/`/g, '');
|
|
1068
|
+
let val = item[unescapedKey];
|
|
1069
|
+
if (typeof val === "string") {
|
|
1070
|
+
val = val.trim("'");
|
|
1071
|
+
}
|
|
1072
|
+
values.push(this.escape(val));
|
|
1073
|
+
}
|
|
1074
|
+
valuesList.push(`(${values.join(',')})`);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// 生成最终SQL
|
|
1078
|
+
const sql = `INSERT INTO \`${this.table}\` (${columns}) VALUES ${valuesList.join(',')}`;
|
|
1079
|
+
return sql;
|
|
1080
|
+
};
|
|
1081
|
+
|
|
1082
|
+
/**
|
|
1083
|
+
* 删除多条数据
|
|
452
1084
|
* @param {Array} list 对象数组
|
|
453
|
-
* @
|
|
1085
|
+
* @param {Boolean} like 是否使用like匹配, 为空使用默认方式
|
|
1086
|
+
* @param {Number} batchSize 每批处理数量,默认100
|
|
1087
|
+
* @param {Number} timeout 超时时间(毫秒),默认60000
|
|
1088
|
+
* @return {Promise<Object>} 执行结果
|
|
454
1089
|
*/
|
|
455
|
-
Sql.prototype.delList = function(list) {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
1090
|
+
Sql.prototype.delList = async function(list, like, batchSize = 100, timeout = 60000) {
|
|
1091
|
+
if (!this.table || !Array.isArray(list) || list.length === 0) {
|
|
1092
|
+
throw new Error('表名或数据列表未设置');
|
|
1093
|
+
}
|
|
1094
|
+
try {
|
|
1095
|
+
// 添加整体操作超时控制
|
|
1096
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1097
|
+
setTimeout(() => reject(new Error('批量删除数据操作超时')), timeout);
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
return await Promise.race([
|
|
1101
|
+
(async () => {
|
|
1102
|
+
// 如果数据量较小,直接处理
|
|
1103
|
+
if (list.length <= batchSize) {
|
|
1104
|
+
let sql = "";
|
|
1105
|
+
for (const item of list) {
|
|
1106
|
+
sql += this.toDelSql(item.query, like);
|
|
1107
|
+
}
|
|
1108
|
+
this.sql = sql;
|
|
1109
|
+
return await this.exec(sql);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// 分批处理大数据量
|
|
1113
|
+
const totalBatches = Math.ceil(list.length / batchSize);
|
|
1114
|
+
$.log.info(`开始分批删除数据,共${totalBatches}批,每批${batchSize}条`);
|
|
1115
|
+
|
|
1116
|
+
// 使用事务包装整个操作以保证原子性
|
|
1117
|
+
let finalResult = null;
|
|
1118
|
+
try {
|
|
1119
|
+
// 开始事务
|
|
1120
|
+
await this.exec("BEGIN;", 15000);
|
|
1121
|
+
|
|
1122
|
+
// 分批处理
|
|
1123
|
+
for (let i = 0; i < totalBatches; i++) {
|
|
1124
|
+
const batch = list.slice(i * batchSize, (i + 1) * batchSize);
|
|
1125
|
+
$.log.debug(`处理第${i + 1}/${totalBatches}批数据,${batch.length}条`);
|
|
1126
|
+
|
|
1127
|
+
let batchSql = "";
|
|
1128
|
+
for (const item of batch) {
|
|
1129
|
+
batchSql += this.toDelSql(item.query, like);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
// 为每批操作添加超时控制
|
|
1133
|
+
finalResult = await Promise.race([
|
|
1134
|
+
this.exec(batchSql),
|
|
1135
|
+
new Promise((_, reject) => {
|
|
1136
|
+
setTimeout(() => reject(new Error(`第${i + 1}批数据删除超时`)), timeout / totalBatches);
|
|
1137
|
+
})
|
|
1138
|
+
]);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// 提交事务
|
|
1142
|
+
await this.exec("COMMIT;", 15000);
|
|
1143
|
+
} catch (error) {
|
|
1144
|
+
// 发生错误时回滚事务
|
|
1145
|
+
try {
|
|
1146
|
+
await this.exec("ROLLBACK;", 15000);
|
|
1147
|
+
$.log.error(`批量删除数据失败,事务已回滚: ${error.message}`);
|
|
1148
|
+
} catch (rollbackError) {
|
|
1149
|
+
$.log.error(`批量删除数据失败,事务回滚也失败: ${rollbackError.message}`);
|
|
1150
|
+
}
|
|
1151
|
+
throw error;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
$.log.success(`批量删除数据完成,共${list.length}条`);
|
|
1155
|
+
return finalResult;
|
|
1156
|
+
})(),
|
|
1157
|
+
timeoutPromise
|
|
1158
|
+
]);
|
|
1159
|
+
} catch (err) {
|
|
1160
|
+
this.error = err.message;
|
|
1161
|
+
$.log.error(`批量删除数据失败: ${err.message}`);
|
|
1162
|
+
throw err;
|
|
460
1163
|
}
|
|
461
|
-
return this.exec(sql);
|
|
462
1164
|
};
|
|
463
1165
|
/**
|
|
464
|
-
*
|
|
1166
|
+
* 更新多条数据
|
|
465
1167
|
* @param {Array} list 对象数组
|
|
466
|
-
* @
|
|
1168
|
+
* @param {Boolean} like 是否使用like匹配, 为空使用默认方式
|
|
1169
|
+
* @param {Number} batchSize 每批处理数量,默认100
|
|
1170
|
+
* @param {Number} timeout 超时时间(毫秒),默认60000
|
|
1171
|
+
* @return {Promise<Object>} 执行结果
|
|
467
1172
|
*/
|
|
468
|
-
Sql.prototype.setList = function(list) {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
1173
|
+
Sql.prototype.setList = async function(list, like = false, batchSize = 100, timeout = 60000) {
|
|
1174
|
+
if (!this.table || !Array.isArray(list) || list.length === 0) {
|
|
1175
|
+
throw new Error('表名或数据列表未设置');
|
|
1176
|
+
}
|
|
1177
|
+
try {
|
|
1178
|
+
// 添加整体操作超时控制
|
|
1179
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1180
|
+
setTimeout(() => reject(new Error('批量更新数据操作超时')), timeout);
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
return await Promise.race([
|
|
1184
|
+
(async () => {
|
|
1185
|
+
// 如果数据量较小,直接处理
|
|
1186
|
+
if (list.length <= batchSize) {
|
|
1187
|
+
let sql = "";
|
|
1188
|
+
for (const item of list) {
|
|
1189
|
+
sql += this.toSetSql(item.query, item.obj, like);
|
|
1190
|
+
}
|
|
1191
|
+
this.sql = sql;
|
|
1192
|
+
return await this.exec(sql);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
// 分批处理大数据量
|
|
1196
|
+
const totalBatches = Math.ceil(list.length / batchSize);
|
|
1197
|
+
$.log.info(`开始分批更新数据,共${totalBatches}批,每批${batchSize}条`);
|
|
1198
|
+
|
|
1199
|
+
// 使用事务包装整个操作以保证原子性
|
|
1200
|
+
let finalResult = null;
|
|
1201
|
+
try {
|
|
1202
|
+
// 开始事务
|
|
1203
|
+
await this.exec("BEGIN;", 15000);
|
|
1204
|
+
|
|
1205
|
+
// 分批处理
|
|
1206
|
+
for (let i = 0; i < totalBatches; i++) {
|
|
1207
|
+
const batch = list.slice(i * batchSize, (i + 1) * batchSize);
|
|
1208
|
+
$.log.debug(`处理第${i + 1}/${totalBatches}批数据,${batch.length}条`);
|
|
1209
|
+
|
|
1210
|
+
let batchSql = "";
|
|
1211
|
+
for (const item of batch) {
|
|
1212
|
+
batchSql += this.toSetSql(item.query, item.obj, like);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// 为每批操作添加超时控制
|
|
1216
|
+
finalResult = await Promise.race([
|
|
1217
|
+
this.exec(batchSql),
|
|
1218
|
+
new Promise((_, reject) => {
|
|
1219
|
+
setTimeout(() => reject(new Error(`第${i + 1}批数据更新超时`)), timeout / totalBatches);
|
|
1220
|
+
})
|
|
1221
|
+
]);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
// 提交事务
|
|
1225
|
+
await this.exec("COMMIT;", 15000);
|
|
1226
|
+
} catch (error) {
|
|
1227
|
+
// 发生错误时回滚事务
|
|
1228
|
+
try {
|
|
1229
|
+
await this.exec("ROLLBACK;", 15000);
|
|
1230
|
+
$.log.error(`批量更新数据失败,事务已回滚: ${error.message}`);
|
|
1231
|
+
} catch (rollbackError) {
|
|
1232
|
+
$.log.error(`批量更新数据失败,事务回滚也失败: ${rollbackError.message}`);
|
|
1233
|
+
}
|
|
1234
|
+
throw error;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
$.log.success(`批量更新数据完成,共${list.length}条`);
|
|
1238
|
+
return finalResult;
|
|
1239
|
+
})(),
|
|
1240
|
+
timeoutPromise
|
|
1241
|
+
]);
|
|
1242
|
+
} catch (err) {
|
|
1243
|
+
this.error = err.message;
|
|
1244
|
+
$.log.error(`批量更新数据失败: ${err.message}`);
|
|
1245
|
+
throw err;
|
|
473
1246
|
}
|
|
474
|
-
return this.exec(sql);
|
|
475
1247
|
};
|
|
476
1248
|
|
|
477
1249
|
/* 辅助类 */
|
|
@@ -481,10 +1253,11 @@ Sql.prototype.setList = function(list) {
|
|
|
481
1253
|
* @param {Object} sqlDt sql模板集合
|
|
482
1254
|
* @return {Bool} 有则返回true,没有则返回false
|
|
483
1255
|
*/
|
|
484
|
-
Sql.prototype.
|
|
1256
|
+
Sql.prototype.hasParam = function(paramDt, sqlDt) {
|
|
485
1257
|
var bl = false;
|
|
486
1258
|
for (var key in sqlDt) {
|
|
487
|
-
|
|
1259
|
+
var value = paramDt[key];
|
|
1260
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
488
1261
|
bl = true;
|
|
489
1262
|
break;
|
|
490
1263
|
}
|
|
@@ -498,7 +1271,7 @@ Sql.prototype.has_param = function(paramDt, sqlDt) {
|
|
|
498
1271
|
* @param {Object} sqlDt sql模板集合
|
|
499
1272
|
* @return {Bool} 没有模板则返回名称,都有则返回undefined
|
|
500
1273
|
*/
|
|
501
|
-
Sql.prototype.
|
|
1274
|
+
Sql.prototype.notParam = function(paramDt, sqlDt) {
|
|
502
1275
|
var name;
|
|
503
1276
|
for (var key in paramDt) {
|
|
504
1277
|
if (!sqlDt[key]) {
|
|
@@ -515,7 +1288,7 @@ Sql.prototype.not_param = function(paramDt, sqlDt) {
|
|
|
515
1288
|
* @param {Object} sqlDt sql模板集合
|
|
516
1289
|
* @return {Object} 返回过滤后的参数集合
|
|
517
1290
|
*/
|
|
518
|
-
Sql.prototype.
|
|
1291
|
+
Sql.prototype.filterParam = function(paramDt, sqlDt) {
|
|
519
1292
|
var dt = [];
|
|
520
1293
|
for (var key in paramDt) {
|
|
521
1294
|
if (!sqlDt[key]) {
|
|
@@ -531,9 +1304,9 @@ Sql.prototype.filter_param = function(paramDt, sqlDt) {
|
|
|
531
1304
|
* @param {Object} sqlDt 模板集合
|
|
532
1305
|
* @return {String} 返回拼接的查询参数
|
|
533
1306
|
*/
|
|
534
|
-
Sql.prototype.
|
|
1307
|
+
Sql.prototype.tplQuery = function(paramDt, sqlDt) {
|
|
535
1308
|
var sql = "";
|
|
536
|
-
if(sqlDt){
|
|
1309
|
+
if (sqlDt) {
|
|
537
1310
|
var l = this.config.separator;
|
|
538
1311
|
if (l) {
|
|
539
1312
|
for (var key in paramDt) {
|
|
@@ -546,12 +1319,12 @@ Sql.prototype.tpl_query = function(paramDt, sqlDt) {
|
|
|
546
1319
|
var sl = "(";
|
|
547
1320
|
var len = arr.length;
|
|
548
1321
|
for (var i = 0; i < len; i++) {
|
|
549
|
-
sl += " || " + tpl.replaceAll("{0}", arr[i]);
|
|
1322
|
+
sl += " || " + tpl.replaceAll("{0}", this.escape(arr[i]).trim("'"));
|
|
550
1323
|
}
|
|
551
1324
|
sl = sl.replace(" || ", "") + ")";
|
|
552
1325
|
sql += " && " + sl;
|
|
553
1326
|
} else {
|
|
554
|
-
sql += " && " + tpl.replaceAll("{0}", value);
|
|
1327
|
+
sql += " && " + tpl.replaceAll("{0}", this.escape(value).trim("'"));
|
|
555
1328
|
}
|
|
556
1329
|
} else {
|
|
557
1330
|
if (arr.length > 1) {
|
|
@@ -559,49 +1332,50 @@ Sql.prototype.tpl_query = function(paramDt, sqlDt) {
|
|
|
559
1332
|
var sl = "(";
|
|
560
1333
|
var len = arr.length;
|
|
561
1334
|
for (var i = 0; i < len; i++) {
|
|
562
|
-
sl += " ||
|
|
1335
|
+
sl += " || " + this.escapeId(key) + " = " + this.escape(arr[i]);
|
|
563
1336
|
}
|
|
564
1337
|
sl = sl.replace(" || ", "") + ")";
|
|
565
1338
|
sql += " && " + sl;
|
|
566
1339
|
} else {
|
|
567
|
-
sql += " &&
|
|
1340
|
+
sql += " && " + this.escapeId(key) + " = " + this.escape(value);
|
|
568
1341
|
}
|
|
569
1342
|
}
|
|
570
1343
|
}
|
|
571
1344
|
} else {
|
|
572
1345
|
for (var key in paramDt) {
|
|
1346
|
+
var value = this.escape(paramDt[key]);
|
|
573
1347
|
if (sqlDt[key]) {
|
|
574
|
-
sql += " && " + sqlDt[key].replaceAll("{0}",
|
|
1348
|
+
sql += " && " + sqlDt[key].replaceAll("{0}", value.trim("'"));
|
|
575
1349
|
} else {
|
|
576
|
-
sql += " &&
|
|
1350
|
+
sql += " && " + this.escapeId(key) + " = " + value;
|
|
577
1351
|
}
|
|
578
1352
|
}
|
|
579
1353
|
}
|
|
580
|
-
}
|
|
581
|
-
else {
|
|
1354
|
+
} else {
|
|
582
1355
|
// 如果没有模板,则直接拼接参数
|
|
583
1356
|
var l = this.config.separator;
|
|
584
1357
|
if (l) {
|
|
585
1358
|
// 使用分隔数组拼接
|
|
586
1359
|
for (var key in paramDt) {
|
|
587
|
-
var
|
|
1360
|
+
var value = paramDt[key];
|
|
1361
|
+
var arr = value.split(l);
|
|
588
1362
|
if (arr.length > 1) {
|
|
589
1363
|
// 如果数量大于0,则增加多条件
|
|
590
1364
|
var sl = "(";
|
|
591
1365
|
var len = arr.length;
|
|
592
1366
|
for (var i = 0; i < len; i++) {
|
|
593
|
-
sl += " ||
|
|
1367
|
+
sl += " || " + this.escapeId(key) + " = " + this.escape(arr[i]);
|
|
594
1368
|
}
|
|
595
1369
|
sl = sl.replace(" || ", "") + ")";
|
|
596
1370
|
sql += " && " + sl;
|
|
597
1371
|
} else {
|
|
598
|
-
sql += " &&
|
|
1372
|
+
sql += " && " + this.escapeId(key) + " = " + this.escape(value);
|
|
599
1373
|
}
|
|
600
1374
|
}
|
|
601
1375
|
} else {
|
|
602
1376
|
// 直接拼接
|
|
603
1377
|
for (var key in paramDt) {
|
|
604
|
-
sql += " &&
|
|
1378
|
+
sql += " && " + this.escapeId(key) + " = " + this.escape(paramDt[key]);
|
|
605
1379
|
}
|
|
606
1380
|
}
|
|
607
1381
|
}
|
|
@@ -614,18 +1388,19 @@ Sql.prototype.tpl_query = function(paramDt, sqlDt) {
|
|
|
614
1388
|
* @param {Object} sqlDt 模板集合
|
|
615
1389
|
* @return {String} 返回拼接的查询参数
|
|
616
1390
|
*/
|
|
617
|
-
Sql.prototype.
|
|
1391
|
+
Sql.prototype.tplBody = function(paramDt, sqlDt) {
|
|
618
1392
|
var sql = "";
|
|
619
1393
|
if (!sqlDt || sqlDt.length === 0) {
|
|
620
1394
|
for (var key in paramDt) {
|
|
621
|
-
sql += " ,
|
|
1395
|
+
sql += " , " + this.escapeId(key) + " = " + this.escape(val[key]);
|
|
622
1396
|
}
|
|
623
1397
|
} else {
|
|
624
1398
|
for (var key in paramDt) {
|
|
1399
|
+
var value = this.escape(paramDt[key]);
|
|
625
1400
|
if (sqlDt[key]) {
|
|
626
|
-
sql += " , " + sqlDt[key].replace("{0}",
|
|
1401
|
+
sql += " , " + sqlDt[key].replace("{0}", value).replace('+ -', '- ').replace('- -', '+ ');
|
|
627
1402
|
} else {
|
|
628
|
-
sql += " ,
|
|
1403
|
+
sql += " , " + this.escapeId(key) + " = " + value;
|
|
629
1404
|
}
|
|
630
1405
|
}
|
|
631
1406
|
}
|
|
@@ -645,11 +1420,14 @@ Sql.prototype.model = function(model) {
|
|
|
645
1420
|
var n = obj[prop];
|
|
646
1421
|
var cha = value - n;
|
|
647
1422
|
if (cha > 0) {
|
|
648
|
-
_this.setSql("`" + _this.key + "`=" + obj[_this.key]
|
|
1423
|
+
_this.setSql("`" + _this.key + "`=" + obj[_this.key], "`" + prop + "`=`" +
|
|
1424
|
+
prop + "` + " + cha);
|
|
649
1425
|
} else if (cha < 0) {
|
|
650
|
-
_this.setSql("`" + _this.key + "`=" + obj[_this.key]
|
|
1426
|
+
_this.setSql("`" + _this.key + "`=" + obj[_this.key], "`" + prop + "`=`" +
|
|
1427
|
+
prop + "` - " + (-cha));
|
|
651
1428
|
} else {
|
|
652
|
-
_this.setSql("`" + _this.key + "`=" + obj[_this.key]
|
|
1429
|
+
_this.setSql("`" + _this.key + "`=" + obj[_this.key], "`" + prop + "`=" +
|
|
1430
|
+
value);
|
|
653
1431
|
}
|
|
654
1432
|
} else {
|
|
655
1433
|
var query = {};
|
|
@@ -665,32 +1443,207 @@ Sql.prototype.model = function(model) {
|
|
|
665
1443
|
};
|
|
666
1444
|
|
|
667
1445
|
/**
|
|
668
|
-
*
|
|
1446
|
+
* 查询单条数据
|
|
669
1447
|
* @param {Object} query 查询条件
|
|
670
1448
|
* @param {String} sort 排序
|
|
671
1449
|
* @param {String} view 返回的字段
|
|
672
|
-
* @
|
|
1450
|
+
* @param {Boolean} like 是否使用like匹配, 为空使用默认方式
|
|
1451
|
+
* @param {Number} timeout 超时时间(毫秒),默认30000ms
|
|
1452
|
+
* @return {Promise|Object|null} 查询结果
|
|
673
1453
|
*/
|
|
674
|
-
Sql.prototype.getObj = async function(query, sort, view) {
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
1454
|
+
Sql.prototype.getObj = async function(query, sort, view, like, timeout = 30000) {
|
|
1455
|
+
try {
|
|
1456
|
+
// 保存当前分页设置
|
|
1457
|
+
const oldPage = this.page;
|
|
1458
|
+
const oldSize = this.size;
|
|
1459
|
+
|
|
1460
|
+
// 设置为只查询一条
|
|
1461
|
+
this.page = 1;
|
|
1462
|
+
this.size = 1;
|
|
1463
|
+
|
|
1464
|
+
// 使用优化后的get方法
|
|
1465
|
+
const list = await this.get(query, sort, view, like, timeout);
|
|
1466
|
+
|
|
1467
|
+
// 恢复分页设置
|
|
1468
|
+
this.page = oldPage;
|
|
1469
|
+
this.size = oldSize;
|
|
1470
|
+
|
|
1471
|
+
var obj = null;
|
|
1472
|
+
if (list.length > 0) {
|
|
1473
|
+
obj = list[0];
|
|
1474
|
+
if (this.key) {
|
|
1475
|
+
obj = this.model(obj);
|
|
1476
|
+
}
|
|
690
1477
|
}
|
|
691
|
-
|
|
692
|
-
return
|
|
1478
|
+
|
|
1479
|
+
return obj;
|
|
1480
|
+
} catch (err) {
|
|
1481
|
+
this.error = err.message;
|
|
1482
|
+
$.log.error(`查询单条数据失败: ${err.message}`, { sql: this.sql });
|
|
1483
|
+
throw err;
|
|
693
1484
|
}
|
|
694
1485
|
};
|
|
695
1486
|
|
|
696
|
-
|
|
1487
|
+
|
|
1488
|
+
|
|
1489
|
+
/**
|
|
1490
|
+
* 从SQL文件加载数据库
|
|
1491
|
+
* @param {String} file - SQL文件路径
|
|
1492
|
+
* @returns {Promise<boolean>} 是否加载成功
|
|
1493
|
+
*/
|
|
1494
|
+
Sql.prototype.load = async function (file) {
|
|
1495
|
+
try {
|
|
1496
|
+
// 记录操作日志
|
|
1497
|
+
if (this.config && this.config.debug) {
|
|
1498
|
+
$.log.debug(`[${this.constructor.name}] [load] 开始从文件加载数据库`, {
|
|
1499
|
+
file: file
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
// 检查文件是否存在
|
|
1504
|
+
if (!file.hasFile()) {
|
|
1505
|
+
throw new Error(`SQL文件不存在: ${file}`);
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
// 读取SQL文件内容
|
|
1509
|
+
const sqlContent = await $.file.readText(file);
|
|
1510
|
+
|
|
1511
|
+
if (!sqlContent || sqlContent.trim() === '') {
|
|
1512
|
+
throw new Error(`SQL文件内容为空: ${file}`);
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
// 分割SQL语句(按分号分割,但需要注意处理字符串中的分号)
|
|
1516
|
+
const sqlStatements = this._splitSqlStatements(sqlContent);
|
|
1517
|
+
|
|
1518
|
+
// 开始事务执行SQL语句
|
|
1519
|
+
await this.exec('BEGIN TRANSACTION');
|
|
1520
|
+
|
|
1521
|
+
try {
|
|
1522
|
+
// 逐个执行SQL语句
|
|
1523
|
+
for (const sql of sqlStatements) {
|
|
1524
|
+
const trimmedSql = sql.trim();
|
|
1525
|
+
if (trimmedSql) {
|
|
1526
|
+
await this.exec(trimmedSql);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
// 提交事务
|
|
1531
|
+
await this.exec('COMMIT');
|
|
1532
|
+
|
|
1533
|
+
if (this.config && this.config.debug) {
|
|
1534
|
+
$.log.info(`[${this.constructor.name}] [load] 数据库加载成功`, {
|
|
1535
|
+
file: file,
|
|
1536
|
+
statementCount: sqlStatements.length
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
return true;
|
|
1541
|
+
} catch (err) {
|
|
1542
|
+
// 回滚事务
|
|
1543
|
+
await this.exec('ROLLBACK').catch(rollbackErr => {
|
|
1544
|
+
$.log.error(`[${this.constructor.name}] [load] 事务回滚失败`, {
|
|
1545
|
+
error: rollbackErr.message
|
|
1546
|
+
});
|
|
1547
|
+
});
|
|
1548
|
+
|
|
1549
|
+
throw err;
|
|
1550
|
+
}
|
|
1551
|
+
} catch (error) {
|
|
1552
|
+
// 记录错误日志
|
|
1553
|
+
$.log.error(`[${this.constructor.name}] [load] 数据库加载失败`, {
|
|
1554
|
+
error: error.message,
|
|
1555
|
+
file: file
|
|
1556
|
+
});
|
|
1557
|
+
|
|
1558
|
+
// 抛出错误
|
|
1559
|
+
throw error;
|
|
1560
|
+
}
|
|
1561
|
+
};
|
|
1562
|
+
|
|
1563
|
+
/**
|
|
1564
|
+
* 分割SQL语句
|
|
1565
|
+
* @private
|
|
1566
|
+
* @param {String} sqlContent - SQL内容
|
|
1567
|
+
* @returns {Array} SQL语句数组
|
|
1568
|
+
*/
|
|
1569
|
+
Sql.prototype._splitSqlStatements = function(sqlContent) {
|
|
1570
|
+
const statements = [];
|
|
1571
|
+
let inString = false;
|
|
1572
|
+
let stringChar = '';
|
|
1573
|
+
let inComment = false;
|
|
1574
|
+
let currentStatement = '';
|
|
1575
|
+
|
|
1576
|
+
for (let i = 0; i < sqlContent.length; i++) {
|
|
1577
|
+
const char = sqlContent[i];
|
|
1578
|
+
const nextChar = i + 1 < sqlContent.length ? sqlContent[i + 1] : '';
|
|
1579
|
+
|
|
1580
|
+
// 处理注释
|
|
1581
|
+
if (!inString && !inComment && char === '-' && nextChar === '-') {
|
|
1582
|
+
inComment = true;
|
|
1583
|
+
i++; // 跳过第二个'-'
|
|
1584
|
+
continue;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
if (inComment && char === '\n') {
|
|
1588
|
+
inComment = false;
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
if (inComment) {
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
// 处理多行注释
|
|
1597
|
+
if (!inString && !inComment && char === '/' && nextChar === '*') {
|
|
1598
|
+
inComment = true;
|
|
1599
|
+
i++; // 跳过'*'
|
|
1600
|
+
continue;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
if (inComment && char === '*' && nextChar === '/') {
|
|
1604
|
+
inComment = false;
|
|
1605
|
+
i++; // 跳过'/'
|
|
1606
|
+
continue;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
// 处理字符串
|
|
1610
|
+
if (!inComment && (char === "'" || char === '"') && (!inString || stringChar === char)) {
|
|
1611
|
+
// 检查是否是转义的引号
|
|
1612
|
+
let escaped = false;
|
|
1613
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
1614
|
+
if (sqlContent[j] === '\\') {
|
|
1615
|
+
escaped = !escaped;
|
|
1616
|
+
} else {
|
|
1617
|
+
break;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
if (!escaped) {
|
|
1622
|
+
if (inString && stringChar === char) {
|
|
1623
|
+
inString = false;
|
|
1624
|
+
stringChar = '';
|
|
1625
|
+
} else if (!inString) {
|
|
1626
|
+
inString = true;
|
|
1627
|
+
stringChar = char;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
// 分割语句
|
|
1633
|
+
if (!inString && !inComment && char === ';') {
|
|
1634
|
+
statements.push(currentStatement.trim());
|
|
1635
|
+
currentStatement = '';
|
|
1636
|
+
} else {
|
|
1637
|
+
currentStatement += char;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
// 添加最后一个语句(如果有)
|
|
1642
|
+
if (currentStatement.trim()) {
|
|
1643
|
+
statements.push(currentStatement.trim());
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
return statements;
|
|
1647
|
+
};
|
|
1648
|
+
|
|
1649
|
+
module.exports = Sql;
|