mm_sqlite 1.3.4 → 1.3.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/db.js +30 -30
- package/index.js +3 -3
- package/package.json +3 -3
- package/sql.js +116 -190
- package/test_db/performance_test.db +0 -0
- package/test_db/test_basic.db +0 -0
- package/test_db/test_core_methods.db +0 -0
- package/test_db/test_simple.db +0 -0
- package/test_db/test_transaction.db +0 -0
package/db.js
CHANGED
|
@@ -11,8 +11,6 @@ class DB extends Sql {
|
|
|
11
11
|
*/
|
|
12
12
|
constructor(sqlite) {
|
|
13
13
|
super(sqlite.run, sqlite.exec);
|
|
14
|
-
// 保存sqlite实例引用
|
|
15
|
-
this._sqlite = sqlite;
|
|
16
14
|
// 事务中
|
|
17
15
|
this.task = 0;
|
|
18
16
|
|
|
@@ -72,20 +70,14 @@ DB.prototype.new = function (table, key) {
|
|
|
72
70
|
};
|
|
73
71
|
|
|
74
72
|
/**
|
|
75
|
-
*
|
|
73
|
+
* 获取数据库连接(修复死锁版)
|
|
76
74
|
* @param {number} timeout 超时时间(毫秒)
|
|
77
75
|
* @returns {Promise<object>} 数据库连接对象
|
|
78
76
|
*/
|
|
79
77
|
DB.prototype.getConn = async function (timeout = 15000) {
|
|
80
|
-
if (!this._sqlite) {
|
|
81
|
-
throw new Error('SQLite实例未初始化');
|
|
82
|
-
}
|
|
83
78
|
try {
|
|
84
|
-
//
|
|
85
|
-
return await
|
|
86
|
-
this._sqlite.getConn(),
|
|
87
|
-
this._createTimeoutPromise(timeout, '获取数据库连接超时')
|
|
88
|
-
]);
|
|
79
|
+
// 直接使用SQLite实例的getConn方法,避免Promise.race导致的死锁
|
|
80
|
+
return await this.parent().getConn(timeout);
|
|
89
81
|
} catch (error) {
|
|
90
82
|
this.log('error', '获取连接失败', error);
|
|
91
83
|
// 重新抛出异常,因为调用方需要连接对象
|
|
@@ -100,7 +92,7 @@ DB.prototype.getConn = async function (timeout = 15000) {
|
|
|
100
92
|
DB.prototype.start = async function() {
|
|
101
93
|
this.task = 1;
|
|
102
94
|
try {
|
|
103
|
-
await this.exec('BEGIN;'
|
|
95
|
+
await this.exec('BEGIN;');
|
|
104
96
|
// 返回事务对象
|
|
105
97
|
return {
|
|
106
98
|
commit: async () => {
|
|
@@ -265,7 +257,7 @@ DB.prototype.fields = async function (table, field_name, timeout = 15000) {
|
|
|
265
257
|
var len = list.length;
|
|
266
258
|
for (var i = 0; i < len; i++) {
|
|
267
259
|
list[i].pk = list[i].pk ? true : false;
|
|
268
|
-
list[i].
|
|
260
|
+
list[i].not_null = list[i].not_null ? true : false;
|
|
269
261
|
}
|
|
270
262
|
|
|
271
263
|
// 如果指定了字段名,过滤结果
|
|
@@ -273,7 +265,16 @@ DB.prototype.fields = async function (table, field_name, timeout = 15000) {
|
|
|
273
265
|
list = list.filter(field => field.name === field_name);
|
|
274
266
|
}
|
|
275
267
|
|
|
276
|
-
return list
|
|
268
|
+
return list.map(item => ({
|
|
269
|
+
name: item.name,
|
|
270
|
+
cid: item.cid,
|
|
271
|
+
default_value: item.dflt_value,
|
|
272
|
+
not_null: item.notnull,
|
|
273
|
+
type: item.type,
|
|
274
|
+
pk: item.pk,
|
|
275
|
+
auto: item.auto,
|
|
276
|
+
note: item.note
|
|
277
|
+
}));
|
|
277
278
|
} catch (err) {
|
|
278
279
|
this.log('error', '获取字段信息失败', err);
|
|
279
280
|
return [];
|
|
@@ -472,7 +473,7 @@ DB.prototype.emptyTable = function (table, timeout = 15000) {
|
|
|
472
473
|
if (!table || typeof table !== 'string') {
|
|
473
474
|
throw new TypeError('table must be a valid string');
|
|
474
475
|
}
|
|
475
|
-
return this.exec('DELETE FROM `' + table + '`;', timeout);
|
|
476
|
+
return this.exec('DELETE FROM `' + table + '`;', [], timeout);
|
|
476
477
|
};
|
|
477
478
|
|
|
478
479
|
/**
|
|
@@ -563,11 +564,11 @@ DB.prototype.setField = async function (field, type, value = '', not_null = true
|
|
|
563
564
|
} else {
|
|
564
565
|
sql += ';';
|
|
565
566
|
}
|
|
566
|
-
await this.exec(sql, timeout);
|
|
567
|
+
await this.exec(sql, [], timeout);
|
|
567
568
|
|
|
568
569
|
sql = 'UPDATE TABLE `{0}` SET `{2}` = `{1}`;';
|
|
569
570
|
sql = sql.replace('{0}', this.table).replace('{1}', field).replace('{2}', new_name);
|
|
570
|
-
var ret = await this.exec(sql, timeout);
|
|
571
|
+
var ret = await this.exec(sql, [], timeout);
|
|
571
572
|
|
|
572
573
|
if (field !== new_name) {
|
|
573
574
|
// sql = "ALTER TABLE `{0}` DROP COLUMN `{1}`;";
|
|
@@ -611,22 +612,22 @@ DB.prototype.delField = async function (table, field, timeout = 15000) {
|
|
|
611
612
|
DB.prototype.sqlField = function (fd) {
|
|
612
613
|
var sql = '`{0}`'.replace('{0}', fd.name);
|
|
613
614
|
sql += ' ' + fd.type;
|
|
614
|
-
if (fd.
|
|
615
|
+
if (fd.not_null) {
|
|
615
616
|
sql += ' NOT NULL';
|
|
616
617
|
}
|
|
617
618
|
if (fd.auto) {
|
|
618
|
-
if (fd.
|
|
619
|
-
if (fd.
|
|
620
|
-
fd.
|
|
619
|
+
if (fd.default_value) {
|
|
620
|
+
if (fd.default_value === '0000-00-00 00:00:00') {
|
|
621
|
+
fd.default_value = 'CURRENT_TIMESTAMP';
|
|
621
622
|
}
|
|
622
|
-
sql += ' DEFAULT ' + fd.
|
|
623
|
+
sql += ' DEFAULT ' + fd.default_value;
|
|
623
624
|
}
|
|
624
625
|
sql += ' ' + fd.auto;
|
|
625
|
-
} else if (fd.
|
|
626
|
-
if (fd.
|
|
627
|
-
fd.
|
|
626
|
+
} else if (fd.default_value) {
|
|
627
|
+
if (fd.default_value === '0000-00-00 00:00:00') {
|
|
628
|
+
fd.default_value = '1970-01-01 00:00:00';
|
|
628
629
|
}
|
|
629
|
-
sql += ' DEFAULT ' + fd.
|
|
630
|
+
sql += ' DEFAULT ' + fd.default_value;
|
|
630
631
|
}
|
|
631
632
|
if (fd.note) {
|
|
632
633
|
sql += " COMMENT '" + fd.note + "'";
|
|
@@ -802,12 +803,11 @@ DB.prototype.getCreateTable = async function (table, timeout = 15000) {
|
|
|
802
803
|
/**
|
|
803
804
|
* @description 获取表数据的SQL插入语句
|
|
804
805
|
* @param {string} table 表名
|
|
805
|
-
* @param {number}
|
|
806
|
-
* @param
|
|
807
|
-
* @param {number} timeout 超时时间(毫秒)
|
|
806
|
+
* @param {number} batch_size 批量处理大小
|
|
807
|
+
* @param {number} timeout - 超时时间(毫秒)
|
|
808
808
|
* @returns {Promise | string} SQL插入语句
|
|
809
809
|
*/
|
|
810
|
-
DB.prototype.getTableData = async function (table,
|
|
810
|
+
DB.prototype.getTableData = async function (table, batch_size = 100, timeout = 60000) {
|
|
811
811
|
if (!table) {
|
|
812
812
|
throw new TypeError('table must be specified');
|
|
813
813
|
}
|
package/index.js
CHANGED
|
@@ -853,15 +853,15 @@ SQLite.prototype._releasePoolConnection = function (conn) {
|
|
|
853
853
|
if (!conn || !this._pool_connections.includes(conn)) {
|
|
854
854
|
return;
|
|
855
855
|
}
|
|
856
|
-
|
|
856
|
+
|
|
857
857
|
// 标记连接最后使用时间
|
|
858
858
|
conn._last_used = Date.now();
|
|
859
|
-
|
|
859
|
+
|
|
860
860
|
// 将连接放回可用连接池
|
|
861
861
|
if (!this._pool_available.includes(conn)) {
|
|
862
862
|
this._pool_available.push(conn);
|
|
863
863
|
}
|
|
864
|
-
|
|
864
|
+
|
|
865
865
|
// 如果有等待的连接请求,立即处理
|
|
866
866
|
if (this._pool_waiting.length > 0) {
|
|
867
867
|
const waiting = this._pool_waiting.shift();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mm_sqlite",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.7",
|
|
4
4
|
"description": "高性能SQLite数据库操作模块,提供与mm_mysql完全兼容的API接口,支持异步操作、事务处理和批量操作",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
"node": ">=14.0.0"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"mm_expand": "^2.
|
|
41
|
+
"mm_expand": "^2.1.9",
|
|
42
42
|
"sqlite3": "^6.0.1",
|
|
43
43
|
"sqlstring": "^2.3.3"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"eslint": "^10.
|
|
46
|
+
"eslint": "^10.2.0",
|
|
47
47
|
"eslint-plugin-jsdoc": "^62.9.0",
|
|
48
48
|
"mm_eslint": "^1.7.1"
|
|
49
49
|
}
|
package/sql.js
CHANGED
|
@@ -932,34 +932,23 @@ Sql.prototype.set = async function (query, body, like) {
|
|
|
932
932
|
};
|
|
933
933
|
|
|
934
934
|
/**
|
|
935
|
-
* @description
|
|
935
|
+
* @description 查询数据(优化版)
|
|
936
936
|
* @param {object} query 查询条件
|
|
937
937
|
* @param {string} sort 排序
|
|
938
938
|
* @param {string} view 返回的字段
|
|
939
939
|
* @param {boolean} like 是否使用like匹配, 为空使用默认方式
|
|
940
|
-
* @param {number} timeout 超时时间(毫秒),默认
|
|
940
|
+
* @param {number} timeout 超时时间(毫秒),默认5000ms
|
|
941
941
|
* @returns {Promise|Array} 查询结果
|
|
942
942
|
*/
|
|
943
|
-
Sql.prototype.get = async function (query, sort, view, like, timeout =
|
|
943
|
+
Sql.prototype.get = async function (query, sort, view, like, timeout = 5000) {
|
|
944
944
|
if (!this.table) {
|
|
945
945
|
throw new Error('表名未设置');
|
|
946
946
|
}
|
|
947
947
|
|
|
948
948
|
try {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
});
|
|
953
|
-
|
|
954
|
-
return await Promise.race([
|
|
955
|
-
(async () => {
|
|
956
|
-
// 生成SQL并执行
|
|
957
|
-
var sql = this.toGetSql(query, sort, view, like);
|
|
958
|
-
var list = await this.run(sql);
|
|
959
|
-
return list;
|
|
960
|
-
})(),
|
|
961
|
-
timeout_promise
|
|
962
|
-
]);
|
|
949
|
+
var sql = this.toGetSql(query, sort, view, like);
|
|
950
|
+
var list = await this.run(sql, [], timeout);
|
|
951
|
+
return list;
|
|
963
952
|
} catch (err) {
|
|
964
953
|
this.error = err.message;
|
|
965
954
|
this.log('error', '查询数据失败', err);
|
|
@@ -1044,24 +1033,13 @@ Sql.prototype.count = async function (query, like, timeout = 30000) {
|
|
|
1044
1033
|
}
|
|
1045
1034
|
|
|
1046
1035
|
try {
|
|
1047
|
-
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
// 正确生成count SQL
|
|
1055
|
-
const where = typeof query === 'string' ? query : await this.toWhere(query, like);
|
|
1056
|
-
const sql = 'SELECT COUNT(*) as num FROM `' + this.table + '`' + (where ? ' WHERE ' + where : '');
|
|
1057
|
-
this.sql = sql;
|
|
1058
|
-
const list = await this.run(sql);
|
|
1059
|
-
const total = list && list[0] && list[0].num ? parseInt(list[0].num) : 0;
|
|
1060
|
-
|
|
1061
|
-
return total;
|
|
1062
|
-
})(),
|
|
1063
|
-
timeout_promise
|
|
1064
|
-
]);
|
|
1036
|
+
const where = typeof query === 'string' ? query : await this.toWhere(query, like);
|
|
1037
|
+
const sql = 'SELECT COUNT(*) as num FROM `' + this.table + '`' + (where ? ' WHERE ' + where : '');
|
|
1038
|
+
this.sql = sql;
|
|
1039
|
+
const list = await this.run(sql);
|
|
1040
|
+
const total = list && list[0] && list[0].num ? parseInt(list[0].num) : 0;
|
|
1041
|
+
|
|
1042
|
+
return total;
|
|
1065
1043
|
} catch (err) {
|
|
1066
1044
|
this.error = err.message;
|
|
1067
1045
|
this.log('error', '统计记录数失败', err);
|
|
@@ -1094,48 +1072,31 @@ Sql.prototype.addList = async function (list, batch_size = 100, timeout = 60000)
|
|
|
1094
1072
|
throw new Error('表名或数据列表未设置');
|
|
1095
1073
|
}
|
|
1096
1074
|
try {
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
return await Promise.race([
|
|
1103
|
-
(async () => {
|
|
1104
|
-
// 如果数据量较小,直接处理
|
|
1105
|
-
if (list.length <= batch_size) {
|
|
1106
|
-
// 使用批量插入语法,不使用事务包装
|
|
1107
|
-
this.sql = this.toBatchAddSql(list);
|
|
1108
|
-
return await this.exec(this.sql);
|
|
1109
|
-
}
|
|
1075
|
+
if (list.length <= batch_size) {
|
|
1076
|
+
// 使用批量插入语法,不使用事务包装
|
|
1077
|
+
this.sql = this.toBatchAddSql(list);
|
|
1078
|
+
return await this.exec(this.sql);
|
|
1079
|
+
}
|
|
1110
1080
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
this.exec(batch_sql),
|
|
1128
|
-
new Promise((unused_resolve, reject) => {
|
|
1129
|
-
setTimeout(() => reject(new Error(`第${i + 1}批数据处理超时`)), timeout / total);
|
|
1130
|
-
})
|
|
1131
|
-
]);
|
|
1132
|
-
}
|
|
1081
|
+
// 分批处理大数据量
|
|
1082
|
+
const total = Math.ceil(list.length / batch_size);
|
|
1083
|
+
this.log('info', `开始分批添加数据,共${total}批,每批${batch_size}条`);
|
|
1084
|
+
|
|
1085
|
+
// 分批执行,每批一个单独的批量插入语句
|
|
1086
|
+
let final_res = null;
|
|
1087
|
+
for (let i = 0; i < total; i++) {
|
|
1088
|
+
const batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
1089
|
+
this.log('debug', `处理第${i + 1}/${total}批数据,${batch.length}条`);
|
|
1090
|
+
|
|
1091
|
+
// 生成批量插入SQL
|
|
1092
|
+
const batch_sql = this.toBatchAddSql(batch);
|
|
1093
|
+
this.sql = batch_sql;
|
|
1094
|
+
|
|
1095
|
+
final_res = await this.exec(batch_sql);
|
|
1096
|
+
}
|
|
1133
1097
|
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
})(),
|
|
1137
|
-
timeout_promise
|
|
1138
|
-
]);
|
|
1098
|
+
this.log('success', `批量添加数据完成,共${list.length}条`);
|
|
1099
|
+
return final_res;
|
|
1139
1100
|
} catch (error) {
|
|
1140
1101
|
this.log('error', '批量添加数据失败', error);
|
|
1141
1102
|
// 返回默认操作结果对象,保持返回值类型一致
|
|
@@ -1197,71 +1158,53 @@ Sql.prototype.delList = async function (list, like, batch_size = 100, timeout =
|
|
|
1197
1158
|
throw new Error('表名或数据列表未设置');
|
|
1198
1159
|
}
|
|
1199
1160
|
try {
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
if (list.length <= batch_size) {
|
|
1209
|
-
let sql = '';
|
|
1210
|
-
for (const item of list) {
|
|
1211
|
-
sql += this.toDelSql(item.query, like);
|
|
1212
|
-
}
|
|
1213
|
-
this.sql = sql;
|
|
1214
|
-
return await this.exec(sql);
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
// 分批处理大数据量
|
|
1218
|
-
const total = Math.ceil(list.length / batch_size);
|
|
1219
|
-
$.log.info(`开始分批删除数据,共${total}批,每批${batch_size}条`);
|
|
1220
|
-
|
|
1221
|
-
// 使用事务包装整个操作以保证原子性
|
|
1222
|
-
let final_res = null;
|
|
1223
|
-
try {
|
|
1224
|
-
// 开始事务
|
|
1225
|
-
await this.exec('BEGIN;', 15000);
|
|
1161
|
+
if (list.length <= batch_size) {
|
|
1162
|
+
let sql = '';
|
|
1163
|
+
for (const item of list) {
|
|
1164
|
+
sql += this.toDelSql(item.query, like);
|
|
1165
|
+
}
|
|
1166
|
+
this.sql = sql;
|
|
1167
|
+
return await this.exec(sql, [], timeout);
|
|
1168
|
+
}
|
|
1226
1169
|
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
$.log.debug(`处理第${i + 1}/${total}批数据,${batch.length}条`);
|
|
1170
|
+
// 分批处理大数据量
|
|
1171
|
+
const total = Math.ceil(list.length / batch_size);
|
|
1172
|
+
this.log('info', `开始分批删除数据,共${total}批,每批${batch_size}条`);
|
|
1231
1173
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1174
|
+
// 使用事务包装整个操作以保证原子性
|
|
1175
|
+
let final_res = null;
|
|
1176
|
+
try {
|
|
1177
|
+
// 开始事务
|
|
1178
|
+
await this.exec('BEGIN;');
|
|
1236
1179
|
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
setTimeout(() => reject(new Error(`第${i + 1}批数据删除超时`)), timeout / total);
|
|
1242
|
-
})
|
|
1243
|
-
]);
|
|
1244
|
-
}
|
|
1180
|
+
// 分批处理
|
|
1181
|
+
for (let i = 0; i < total; i++) {
|
|
1182
|
+
const batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
1183
|
+
this.log('debug', `处理第${i + 1}/${total}批数据,${batch.length}条`);
|
|
1245
1184
|
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
// 发生错误时回滚事务
|
|
1250
|
-
try {
|
|
1251
|
-
await this.exec('ROLLBACK;', 15000);
|
|
1252
|
-
this.log('error', '批量删除数据失败,事务已回滚', error);
|
|
1253
|
-
} catch (rollback_err) {
|
|
1254
|
-
this.log('error', '批量删除数据失败,事务回滚也失败', rollback_err);
|
|
1255
|
-
}
|
|
1256
|
-
// 记录错误日志
|
|
1257
|
-
this.log('error', 'SQL语句执行失败', error);
|
|
1185
|
+
let batch_sql = '';
|
|
1186
|
+
for (const item of batch) {
|
|
1187
|
+
batch_sql += this.toDelSql(item.query, like);
|
|
1258
1188
|
}
|
|
1189
|
+
final_res = await this.exec(batch_sql, [], timeout);
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
// 提交事务
|
|
1193
|
+
await this.exec('COMMIT;', [], timeout);
|
|
1194
|
+
} catch (error) {
|
|
1195
|
+
// 发生错误时回滚事务
|
|
1196
|
+
try {
|
|
1197
|
+
await this.exec('ROLLBACK;', [], timeout);
|
|
1198
|
+
this.log('error', '批量删除数据失败,事务已回滚', error);
|
|
1199
|
+
} catch (rollback_err) {
|
|
1200
|
+
this.log('error', '批量删除数据失败,事务回滚也失败', rollback_err);
|
|
1201
|
+
}
|
|
1202
|
+
// 记录错误日志
|
|
1203
|
+
this.log('error', 'SQL语句执行失败', error);
|
|
1204
|
+
}
|
|
1259
1205
|
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
})(),
|
|
1263
|
-
timeout_promise
|
|
1264
|
-
]);
|
|
1206
|
+
this.log('success', `批量删除数据完成,共${list.length}条`);
|
|
1207
|
+
return final_res;
|
|
1265
1208
|
} catch (err) {
|
|
1266
1209
|
this.log('error', '批量删除数据失败', err);
|
|
1267
1210
|
// 返回默认操作结果对象,保持返回值类型一致
|
|
@@ -1281,69 +1224,52 @@ Sql.prototype.setList = async function (list, like = false, batch_size = 100, ti
|
|
|
1281
1224
|
throw new Error('表名或数据列表未设置');
|
|
1282
1225
|
}
|
|
1283
1226
|
try {
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
if (list.length <= batch_size) {
|
|
1293
|
-
let sql = '';
|
|
1294
|
-
for (const item of list) {
|
|
1295
|
-
sql += this.toSetSql(item.query, item.obj, like);
|
|
1296
|
-
}
|
|
1297
|
-
this.sql = sql;
|
|
1298
|
-
return await this.exec(sql);
|
|
1299
|
-
}
|
|
1227
|
+
if (list.length <= batch_size) {
|
|
1228
|
+
let sql = '';
|
|
1229
|
+
for (const item of list) {
|
|
1230
|
+
sql += this.toSetSql(item.query, item.obj, like);
|
|
1231
|
+
}
|
|
1232
|
+
this.sql = sql;
|
|
1233
|
+
return await this.exec(sql, [], timeout);
|
|
1234
|
+
}
|
|
1300
1235
|
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1236
|
+
// 分批处理大数据量
|
|
1237
|
+
const total = Math.ceil(list.length / batch_size);
|
|
1238
|
+
this.log('info', `开始分批更新数据,共${total}批,每批${batch_size}条`);
|
|
1304
1239
|
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1240
|
+
// 使用事务包装整个操作以保证原子性
|
|
1241
|
+
let final_res = null;
|
|
1242
|
+
try {
|
|
1243
|
+
// 开始事务
|
|
1244
|
+
await this.exec('BEGIN;');
|
|
1310
1245
|
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1246
|
+
// 分批处理
|
|
1247
|
+
for (let i = 0; i < total; i++) {
|
|
1248
|
+
const batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
1249
|
+
this.log('debug', `处理第${i + 1}/${total}批数据,${batch.length}条`);
|
|
1315
1250
|
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1251
|
+
let batch_sql = '';
|
|
1252
|
+
for (const item of batch) {
|
|
1253
|
+
batch_sql += this.toSetSql(item.query, item.obj, like);
|
|
1254
|
+
}
|
|
1320
1255
|
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
this.exec(batch_sql),
|
|
1324
|
-
new Promise((unused_resolve, reject) => {
|
|
1325
|
-
setTimeout(() => reject(new Error(`第${i + 1}批数据更新超时`)), timeout / total);
|
|
1326
|
-
})
|
|
1327
|
-
]);
|
|
1328
|
-
}
|
|
1256
|
+
final_res = await this.exec(batch_sql, [], timeout);
|
|
1257
|
+
}
|
|
1329
1258
|
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1259
|
+
// 提交事务
|
|
1260
|
+
await this.exec('COMMIT;', [], timeout);
|
|
1261
|
+
} catch (error) {
|
|
1262
|
+
// 发生错误时回滚事务
|
|
1263
|
+
try {
|
|
1264
|
+
await this.exec('ROLLBACK;', [], timeout);
|
|
1265
|
+
this.log('error', '批量更新数据失败,事务已回滚', error);
|
|
1266
|
+
} catch (rollback_err) {
|
|
1267
|
+
this.log('error', '批量更新数据失败,事务回滚也失败', rollback_err);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1341
1270
|
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
})(),
|
|
1345
|
-
timeout_promise
|
|
1346
|
-
]);
|
|
1271
|
+
this.log('success', `批量更新数据完成,共${list.length}条`);
|
|
1272
|
+
return final_res;
|
|
1347
1273
|
} catch (err) {
|
|
1348
1274
|
this.log('error', '批量更新数据失败', err);
|
|
1349
1275
|
// 返回默认操作结果对象,保持返回值类型一致
|
|
Binary file
|
package/test_db/test_basic.db
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|