mm_sqlite 1.3.5 → 1.3.8
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 +13 -22
- package/index.js +141 -89
- package/package.json +3 -3
- package/sql.js +116 -191
- 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
|
|
|
@@ -25,7 +23,7 @@ class DB extends Sql {
|
|
|
25
23
|
* 获取上级
|
|
26
24
|
* @returns {object} 返回上级管理器
|
|
27
25
|
*/
|
|
28
|
-
this.
|
|
26
|
+
this.getParent = function () {
|
|
29
27
|
return sqlite;
|
|
30
28
|
};
|
|
31
29
|
}
|
|
@@ -50,7 +48,7 @@ DB.prototype._createTimeoutPromise = function (timeout, message) {
|
|
|
50
48
|
* @returns {string} 数据库
|
|
51
49
|
*/
|
|
52
50
|
DB.prototype.database = function () {
|
|
53
|
-
return this.
|
|
51
|
+
return this.getParent().config.database;
|
|
54
52
|
};
|
|
55
53
|
|
|
56
54
|
/**
|
|
@@ -60,7 +58,7 @@ DB.prototype.database = function () {
|
|
|
60
58
|
* @returns {object} 返回管理器
|
|
61
59
|
*/
|
|
62
60
|
DB.prototype.new = function (table, key) {
|
|
63
|
-
var db = this.
|
|
61
|
+
var db = this.getParent().db();
|
|
64
62
|
db.table = table;
|
|
65
63
|
if (key) {
|
|
66
64
|
db.key = key;
|
|
@@ -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.getParent().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 () => {
|
|
@@ -481,7 +473,7 @@ DB.prototype.emptyTable = function (table, timeout = 15000) {
|
|
|
481
473
|
if (!table || typeof table !== 'string') {
|
|
482
474
|
throw new TypeError('table must be a valid string');
|
|
483
475
|
}
|
|
484
|
-
return this.exec('DELETE FROM `' + table + '`;', timeout);
|
|
476
|
+
return this.exec('DELETE FROM `' + table + '`;', [], timeout);
|
|
485
477
|
};
|
|
486
478
|
|
|
487
479
|
/**
|
|
@@ -572,11 +564,11 @@ DB.prototype.setField = async function (field, type, value = '', not_null = true
|
|
|
572
564
|
} else {
|
|
573
565
|
sql += ';';
|
|
574
566
|
}
|
|
575
|
-
await this.exec(sql, timeout);
|
|
567
|
+
await this.exec(sql, [], timeout);
|
|
576
568
|
|
|
577
569
|
sql = 'UPDATE TABLE `{0}` SET `{2}` = `{1}`;';
|
|
578
570
|
sql = sql.replace('{0}', this.table).replace('{1}', field).replace('{2}', new_name);
|
|
579
|
-
var ret = await this.exec(sql, timeout);
|
|
571
|
+
var ret = await this.exec(sql, [], timeout);
|
|
580
572
|
|
|
581
573
|
if (field !== new_name) {
|
|
582
574
|
// sql = "ALTER TABLE `{0}` DROP COLUMN `{1}`;";
|
|
@@ -811,12 +803,11 @@ DB.prototype.getCreateTable = async function (table, timeout = 15000) {
|
|
|
811
803
|
/**
|
|
812
804
|
* @description 获取表数据的SQL插入语句
|
|
813
805
|
* @param {string} table 表名
|
|
814
|
-
* @param {number}
|
|
815
|
-
* @param
|
|
816
|
-
* @param {number} timeout 超时时间(毫秒)
|
|
806
|
+
* @param {number} batch_size 批量处理大小
|
|
807
|
+
* @param {number} timeout - 超时时间(毫秒)
|
|
817
808
|
* @returns {Promise | string} SQL插入语句
|
|
818
809
|
*/
|
|
819
|
-
DB.prototype.getTableData = async function (table,
|
|
810
|
+
DB.prototype.getTableData = async function (table, batch_size = 100, timeout = 60000) {
|
|
820
811
|
if (!table) {
|
|
821
812
|
throw new TypeError('table must be specified');
|
|
822
813
|
}
|
package/index.js
CHANGED
|
@@ -21,23 +21,23 @@ class SQLite extends Base {
|
|
|
21
21
|
database: 'mm',
|
|
22
22
|
charset: 'utf8mb4',
|
|
23
23
|
timezone: '+08:00',
|
|
24
|
-
connect_timeout:
|
|
25
|
-
acquire_timeout:
|
|
26
|
-
query_timeout:
|
|
27
|
-
connection_limit:
|
|
24
|
+
connect_timeout: 5000, // 减少连接超时时间
|
|
25
|
+
acquire_timeout: 5000, // 减少获取连接超时时间
|
|
26
|
+
query_timeout: 10000, // 减少查询超时时间
|
|
27
|
+
connection_limit: 10, // 减少最大连接数
|
|
28
28
|
queue_limit: 0,
|
|
29
|
-
enable_keep_alive:
|
|
30
|
-
keep_alive_initial_delay:
|
|
31
|
-
enable_reconnect:
|
|
32
|
-
reconnect_interval:
|
|
33
|
-
max_reconnect_attempts:
|
|
34
|
-
wait_for_connections:
|
|
29
|
+
enable_keep_alive: false, // SQLite不需要保持连接
|
|
30
|
+
keep_alive_initial_delay: 0,
|
|
31
|
+
enable_reconnect: false, // SQLite不需要重连
|
|
32
|
+
reconnect_interval: 0,
|
|
33
|
+
max_reconnect_attempts: 0,
|
|
34
|
+
wait_for_connections: false,
|
|
35
35
|
// 连接池相关配置
|
|
36
36
|
pool_min: 1,
|
|
37
|
-
pool_max:
|
|
38
|
-
pool_acquire_timeout:
|
|
39
|
-
pool_idle_timeout:
|
|
40
|
-
pool_reap_interval:
|
|
37
|
+
pool_max: 3, // 减少最大连接池大小
|
|
38
|
+
pool_acquire_timeout: 5000, // 减少连接池获取超时
|
|
39
|
+
pool_idle_timeout: 30000, // 减少空闲超时
|
|
40
|
+
pool_reap_interval: 5000 // 减少回收间隔
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -46,26 +46,33 @@ class SQLite extends Base {
|
|
|
46
46
|
*/
|
|
47
47
|
constructor(config) {
|
|
48
48
|
super(config);
|
|
49
|
-
this._connection = null;
|
|
50
|
-
this._pool = null;
|
|
51
|
-
this._status = 'closed';
|
|
52
|
-
this._last_connect_time = 0;
|
|
53
|
-
this._reconnecting = false;
|
|
54
|
-
this._is_inited = false;
|
|
55
|
-
this._is_destroyed = false;
|
|
56
|
-
this._db = null;
|
|
57
|
-
this._open = false;
|
|
58
|
-
this._conn_retry_count = 0;
|
|
59
|
-
|
|
60
|
-
// 连接池相关属性
|
|
61
|
-
this._use_pool = this.config.connection_limit > 1;
|
|
62
49
|
this._pool_connections = [];
|
|
63
50
|
this._pool_available = [];
|
|
64
51
|
this._pool_waiting = [];
|
|
65
|
-
this._pool_reaper = null;
|
|
66
52
|
}
|
|
67
53
|
}
|
|
68
54
|
|
|
55
|
+
/**
|
|
56
|
+
* 预初始化数据库连接
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
SQLite.prototype._preset = function () {
|
|
60
|
+
this._connection = null;
|
|
61
|
+
this._pool = null;
|
|
62
|
+
this._status = 'closed';
|
|
63
|
+
this._last_connect_time = 0;
|
|
64
|
+
this._reconnecting = false;
|
|
65
|
+
this._is_inited = false;
|
|
66
|
+
this._is_destroyed = false;
|
|
67
|
+
this._db = null;
|
|
68
|
+
this._open = false;
|
|
69
|
+
this._conn_retry_count = 0;
|
|
70
|
+
|
|
71
|
+
// 连接池相关属性
|
|
72
|
+
this._use_pool = this.config.connection_limit > 1;
|
|
73
|
+
this._pool_reaper = null;
|
|
74
|
+
};
|
|
75
|
+
|
|
69
76
|
/**
|
|
70
77
|
* 初始化服务
|
|
71
78
|
* @returns {Promise<void>}
|
|
@@ -150,16 +157,29 @@ SQLite.prototype.open = async function (timeout) {
|
|
|
150
157
|
});
|
|
151
158
|
return true;
|
|
152
159
|
} else {
|
|
153
|
-
// 使用单连接模式
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
reject(new Error(`数据库连接超时(${connect_timeout}ms)`));
|
|
158
|
-
}, connect_timeout);
|
|
159
|
-
});
|
|
160
|
-
|
|
160
|
+
// 使用单连接模式 - 替换Promise.race为更可靠的超时控制
|
|
161
|
+
let timeout_id = null;
|
|
162
|
+
let connection_success = false;
|
|
163
|
+
|
|
161
164
|
try {
|
|
162
|
-
|
|
165
|
+
// 设置超时控制
|
|
166
|
+
const timeout_promise = new Promise((resolve, reject) => {
|
|
167
|
+
timeout_id = setTimeout(() => {
|
|
168
|
+
if (!connection_success) {
|
|
169
|
+
reject(new Error(`数据库连接超时(${connect_timeout}ms)`));
|
|
170
|
+
}
|
|
171
|
+
}, connect_timeout);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// 执行连接
|
|
175
|
+
await this._openInternal();
|
|
176
|
+
connection_success = true;
|
|
177
|
+
|
|
178
|
+
// 清除超时定时器
|
|
179
|
+
if (timeout_id) {
|
|
180
|
+
clearTimeout(timeout_id);
|
|
181
|
+
timeout_id = null;
|
|
182
|
+
}
|
|
163
183
|
|
|
164
184
|
this._last_connect_time = Date.now();
|
|
165
185
|
this._status = 'connected';
|
|
@@ -169,7 +189,13 @@ SQLite.prototype.open = async function (timeout) {
|
|
|
169
189
|
});
|
|
170
190
|
return true;
|
|
171
191
|
} catch (connection_err) {
|
|
192
|
+
// 确保超时定时器被清除
|
|
193
|
+
if (timeout_id) {
|
|
194
|
+
clearTimeout(timeout_id);
|
|
195
|
+
timeout_id = null;
|
|
196
|
+
}
|
|
172
197
|
this.log('error', '连接过程错误详情', connection_err);
|
|
198
|
+
throw connection_err;
|
|
173
199
|
}
|
|
174
200
|
}
|
|
175
201
|
} catch (err) {
|
|
@@ -248,25 +274,30 @@ SQLite.prototype.getConn = async function (timeout) {
|
|
|
248
274
|
// 从连接池获取连接
|
|
249
275
|
return await this._getPoolConnection();
|
|
250
276
|
} else {
|
|
251
|
-
// 单连接模式
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
277
|
+
// 单连接模式 - 替换Promise.race为更可靠的超时控制
|
|
278
|
+
let timeout_id = null;
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
return await new Promise((resolve, reject) => {
|
|
282
|
+
// 设置超时定时器
|
|
283
|
+
timeout_id = setTimeout(() => {
|
|
284
|
+
reject(new Error(`获取数据库连接超时(${acquire_timeout}ms)`));
|
|
285
|
+
}, acquire_timeout);
|
|
286
|
+
|
|
287
|
+
// 直接返回连接,因为这是单连接模式
|
|
288
|
+
resolve(this._db);
|
|
289
|
+
});
|
|
290
|
+
} catch (error) {
|
|
291
|
+
// 确保超时定时器被清除
|
|
292
|
+
if (timeout_id) {
|
|
293
|
+
clearTimeout(timeout_id);
|
|
294
|
+
timeout_id = null;
|
|
295
|
+
}
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
263
298
|
}
|
|
264
299
|
} catch (error) {
|
|
265
300
|
this.log('error', '获取连接失败', error);
|
|
266
|
-
|
|
267
|
-
if (error.code && (error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT')) {
|
|
268
|
-
this._handleConnectionError(error);
|
|
269
|
-
}
|
|
270
301
|
return null;
|
|
271
302
|
}
|
|
272
303
|
};
|
|
@@ -300,9 +331,23 @@ SQLite.prototype.run = async function (sql, params = [], timeout = 30000) {
|
|
|
300
331
|
conn = await this.getConn(query_timeout);
|
|
301
332
|
|
|
302
333
|
const self = this;
|
|
303
|
-
//
|
|
304
|
-
|
|
334
|
+
// 替换Promise.race为更可靠的超时控制
|
|
335
|
+
let timeout_id = null;
|
|
336
|
+
|
|
337
|
+
const result = await new Promise((resolve, reject) => {
|
|
338
|
+
// 设置超时定时器
|
|
339
|
+
timeout_id = setTimeout(() => {
|
|
340
|
+
reject(new Error(`SQL查询超时: ${timeout}ms`));
|
|
341
|
+
}, timeout);
|
|
342
|
+
|
|
343
|
+
// 执行SQL查询
|
|
305
344
|
conn.all(sql, params || [], (error, rows) => {
|
|
345
|
+
// 清除超时定时器
|
|
346
|
+
if (timeout_id) {
|
|
347
|
+
clearTimeout(timeout_id);
|
|
348
|
+
timeout_id = null;
|
|
349
|
+
}
|
|
350
|
+
|
|
306
351
|
if (error) {
|
|
307
352
|
self.sql = sql;
|
|
308
353
|
self.error = {
|
|
@@ -318,14 +363,6 @@ SQLite.prototype.run = async function (sql, params = [], timeout = 30000) {
|
|
|
318
363
|
});
|
|
319
364
|
});
|
|
320
365
|
|
|
321
|
-
const timeout_promise = new Promise((unused_resolve, reject) => {
|
|
322
|
-
setTimeout(() => {
|
|
323
|
-
reject(new Error(`SQL查询超时: ${timeout}ms`));
|
|
324
|
-
}, timeout);
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
const result = await Promise.race([query_promise, timeout_promise]);
|
|
328
|
-
|
|
329
366
|
// 连接池模式下释放连接
|
|
330
367
|
if (this._use_pool && conn) {
|
|
331
368
|
this._releasePoolConnection(conn);
|
|
@@ -340,11 +377,8 @@ SQLite.prototype.run = async function (sql, params = [], timeout = 30000) {
|
|
|
340
377
|
this._releasePoolConnection(conn);
|
|
341
378
|
}
|
|
342
379
|
|
|
343
|
-
//
|
|
344
|
-
|
|
345
|
-
this._handleConnectionError(err);
|
|
346
|
-
}
|
|
347
|
-
// 返回空数组,保持与MySQL一致的错误处理行为
|
|
380
|
+
// 简化错误处理:SQLite不需要复杂的连接错误处理
|
|
381
|
+
// 直接返回空数组,保持与MySQL一致的错误处理行为
|
|
348
382
|
return [];
|
|
349
383
|
}
|
|
350
384
|
};
|
|
@@ -377,9 +411,23 @@ SQLite.prototype.exec = async function (sql, params, timeout) {
|
|
|
377
411
|
// 获取连接
|
|
378
412
|
conn = await this.getConn(timeout);
|
|
379
413
|
var _this = this;
|
|
380
|
-
//
|
|
381
|
-
|
|
414
|
+
// 替换Promise.race为更可靠的超时控制
|
|
415
|
+
let timeout_id = null;
|
|
416
|
+
|
|
417
|
+
const result = await new Promise((resolve, reject) => {
|
|
418
|
+
// 设置超时定时器
|
|
419
|
+
timeout_id = setTimeout(() => {
|
|
420
|
+
reject(new Error(`SQL执行超时: ${timeout}ms`));
|
|
421
|
+
}, timeout);
|
|
422
|
+
|
|
423
|
+
// 执行SQL语句
|
|
382
424
|
conn.run(sql, params || [], function (error) {
|
|
425
|
+
// 清除超时定时器
|
|
426
|
+
if (timeout_id) {
|
|
427
|
+
clearTimeout(timeout_id);
|
|
428
|
+
timeout_id = null;
|
|
429
|
+
}
|
|
430
|
+
|
|
383
431
|
if (error) {
|
|
384
432
|
_this.sql = sql;
|
|
385
433
|
_this.error = {
|
|
@@ -395,14 +443,6 @@ SQLite.prototype.exec = async function (sql, params, timeout) {
|
|
|
395
443
|
});
|
|
396
444
|
});
|
|
397
445
|
|
|
398
|
-
const timeout_promise = new Promise((unused_resolve, reject) => {
|
|
399
|
-
setTimeout(() => {
|
|
400
|
-
reject(new Error(`SQL执行超时: ${timeout}ms`));
|
|
401
|
-
}, timeout);
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
const result = await Promise.race([query_promise, timeout_promise]);
|
|
405
|
-
|
|
406
446
|
// 连接池模式下释放连接
|
|
407
447
|
if (this._use_pool && conn) {
|
|
408
448
|
this._releasePoolConnection(conn);
|
|
@@ -417,10 +457,7 @@ SQLite.prototype.exec = async function (sql, params, timeout) {
|
|
|
417
457
|
this._releasePoolConnection(conn);
|
|
418
458
|
}
|
|
419
459
|
|
|
420
|
-
//
|
|
421
|
-
if (err.code && (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT')) {
|
|
422
|
-
this._handleConnectionError(err);
|
|
423
|
-
}
|
|
460
|
+
// 简化错误处理:SQLite不需要复杂的连接错误处理
|
|
424
461
|
// 返回0,保持与MySQL一致的错误处理行为
|
|
425
462
|
return 0;
|
|
426
463
|
}
|
|
@@ -787,13 +824,23 @@ SQLite.prototype._reapIdleConnections = function () {
|
|
|
787
824
|
* @returns {Promise<object>} 数据库连接
|
|
788
825
|
*/
|
|
789
826
|
SQLite.prototype._getPoolConnection = async function () {
|
|
790
|
-
const acquire_timeout = this.config.pool_acquire_timeout;
|
|
827
|
+
const acquire_timeout = this.config.pool_acquire_timeout || 10000;
|
|
791
828
|
const start_time = Date.now();
|
|
792
829
|
|
|
793
830
|
return new Promise((resolve, reject) => {
|
|
831
|
+
let timeout_id = null;
|
|
832
|
+
|
|
833
|
+
const cleanup = () => {
|
|
834
|
+
if (timeout_id) {
|
|
835
|
+
clearTimeout(timeout_id);
|
|
836
|
+
timeout_id = null;
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
|
|
794
840
|
const tryGetConnection = () => {
|
|
795
841
|
// 检查超时
|
|
796
842
|
if (Date.now() - start_time > acquire_timeout) {
|
|
843
|
+
cleanup();
|
|
797
844
|
reject(new Error(`获取连接超时(${acquire_timeout}ms)`));
|
|
798
845
|
return;
|
|
799
846
|
}
|
|
@@ -802,6 +849,7 @@ SQLite.prototype._getPoolConnection = async function () {
|
|
|
802
849
|
if (this._pool_available.length > 0) {
|
|
803
850
|
const conn = this._pool_available.shift();
|
|
804
851
|
conn._last_used = Date.now();
|
|
852
|
+
cleanup();
|
|
805
853
|
resolve(conn);
|
|
806
854
|
return;
|
|
807
855
|
}
|
|
@@ -812,14 +860,18 @@ SQLite.prototype._getPoolConnection = async function () {
|
|
|
812
860
|
.then(conn => {
|
|
813
861
|
this._pool_connections.push(conn);
|
|
814
862
|
conn._last_used = Date.now();
|
|
863
|
+
cleanup();
|
|
815
864
|
resolve(conn);
|
|
816
865
|
})
|
|
817
|
-
.catch(
|
|
866
|
+
.catch(err => {
|
|
867
|
+
cleanup();
|
|
868
|
+
reject(err);
|
|
869
|
+
});
|
|
818
870
|
return;
|
|
819
871
|
}
|
|
820
872
|
|
|
821
|
-
//
|
|
822
|
-
setTimeout(tryGetConnection,
|
|
873
|
+
// 没有可用连接,等待一段时间后重试(避免无限循环)
|
|
874
|
+
timeout_id = setTimeout(tryGetConnection, 50);
|
|
823
875
|
};
|
|
824
876
|
|
|
825
877
|
tryGetConnection();
|
|
@@ -853,15 +905,15 @@ SQLite.prototype._releasePoolConnection = function (conn) {
|
|
|
853
905
|
if (!conn || !this._pool_connections.includes(conn)) {
|
|
854
906
|
return;
|
|
855
907
|
}
|
|
856
|
-
|
|
908
|
+
|
|
857
909
|
// 标记连接最后使用时间
|
|
858
910
|
conn._last_used = Date.now();
|
|
859
|
-
|
|
911
|
+
|
|
860
912
|
// 将连接放回可用连接池
|
|
861
913
|
if (!this._pool_available.includes(conn)) {
|
|
862
914
|
this._pool_available.push(conn);
|
|
863
915
|
}
|
|
864
|
-
|
|
916
|
+
|
|
865
917
|
// 如果有等待的连接请求,立即处理
|
|
866
918
|
if (this._pool_waiting.length > 0) {
|
|
867
919
|
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.8",
|
|
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.2.8",
|
|
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
|
@@ -897,7 +897,6 @@ Sql.prototype.del = async function (query, like) {
|
|
|
897
897
|
var sql = this.toDelSql(query, like);
|
|
898
898
|
this.sql = sql;
|
|
899
899
|
var bl = await this.exec(sql);
|
|
900
|
-
|
|
901
900
|
return bl;
|
|
902
901
|
} catch (err) {
|
|
903
902
|
this.error = err.message;
|
|
@@ -932,34 +931,23 @@ Sql.prototype.set = async function (query, body, like) {
|
|
|
932
931
|
};
|
|
933
932
|
|
|
934
933
|
/**
|
|
935
|
-
* @description
|
|
934
|
+
* @description 查询数据(优化版)
|
|
936
935
|
* @param {object} query 查询条件
|
|
937
936
|
* @param {string} sort 排序
|
|
938
937
|
* @param {string} view 返回的字段
|
|
939
938
|
* @param {boolean} like 是否使用like匹配, 为空使用默认方式
|
|
940
|
-
* @param {number} timeout 超时时间(毫秒),默认
|
|
939
|
+
* @param {number} timeout 超时时间(毫秒),默认5000ms
|
|
941
940
|
* @returns {Promise|Array} 查询结果
|
|
942
941
|
*/
|
|
943
|
-
Sql.prototype.get = async function (query, sort, view, like, timeout =
|
|
942
|
+
Sql.prototype.get = async function (query, sort, view, like, timeout = 5000) {
|
|
944
943
|
if (!this.table) {
|
|
945
944
|
throw new Error('表名未设置');
|
|
946
945
|
}
|
|
947
946
|
|
|
948
947
|
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
|
-
]);
|
|
948
|
+
var sql = this.toGetSql(query, sort, view, like);
|
|
949
|
+
var list = await this.run(sql, [], timeout);
|
|
950
|
+
return list;
|
|
963
951
|
} catch (err) {
|
|
964
952
|
this.error = err.message;
|
|
965
953
|
this.log('error', '查询数据失败', err);
|
|
@@ -1044,24 +1032,13 @@ Sql.prototype.count = async function (query, like, timeout = 30000) {
|
|
|
1044
1032
|
}
|
|
1045
1033
|
|
|
1046
1034
|
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
|
-
]);
|
|
1035
|
+
const where = typeof query === 'string' ? query : await this.toWhere(query, like);
|
|
1036
|
+
const sql = 'SELECT COUNT(*) as num FROM `' + this.table + '`' + (where ? ' WHERE ' + where : '');
|
|
1037
|
+
this.sql = sql;
|
|
1038
|
+
const list = await this.run(sql);
|
|
1039
|
+
const total = list && list[0] && list[0].num ? parseInt(list[0].num) : 0;
|
|
1040
|
+
|
|
1041
|
+
return total;
|
|
1065
1042
|
} catch (err) {
|
|
1066
1043
|
this.error = err.message;
|
|
1067
1044
|
this.log('error', '统计记录数失败', err);
|
|
@@ -1094,48 +1071,31 @@ Sql.prototype.addList = async function (list, batch_size = 100, timeout = 60000)
|
|
|
1094
1071
|
throw new Error('表名或数据列表未设置');
|
|
1095
1072
|
}
|
|
1096
1073
|
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
|
-
}
|
|
1074
|
+
if (list.length <= batch_size) {
|
|
1075
|
+
// 使用批量插入语法,不使用事务包装
|
|
1076
|
+
this.sql = this.toBatchAddSql(list);
|
|
1077
|
+
return await this.exec(this.sql);
|
|
1078
|
+
}
|
|
1110
1079
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
// 生成批量插入SQL
|
|
1122
|
-
const batch_sql = this.toBatchAddSql(batch);
|
|
1123
|
-
this.sql = batch_sql;
|
|
1124
|
-
|
|
1125
|
-
// 为每批操作添加超时控制
|
|
1126
|
-
final_res = await Promise.race([
|
|
1127
|
-
this.exec(batch_sql),
|
|
1128
|
-
new Promise((unused_resolve, reject) => {
|
|
1129
|
-
setTimeout(() => reject(new Error(`第${i + 1}批数据处理超时`)), timeout / total);
|
|
1130
|
-
})
|
|
1131
|
-
]);
|
|
1132
|
-
}
|
|
1080
|
+
// 分批处理大数据量
|
|
1081
|
+
const total = Math.ceil(list.length / batch_size);
|
|
1082
|
+
this.log('info', `开始分批添加数据,共${total}批,每批${batch_size}条`);
|
|
1083
|
+
|
|
1084
|
+
// 分批执行,每批一个单独的批量插入语句
|
|
1085
|
+
let final_res = null;
|
|
1086
|
+
for (let i = 0; i < total; i++) {
|
|
1087
|
+
const batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
1088
|
+
this.log('debug', `处理第${i + 1}/${total}批数据,${batch.length}条`);
|
|
1133
1089
|
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1090
|
+
// 生成批量插入SQL
|
|
1091
|
+
const batch_sql = this.toBatchAddSql(batch);
|
|
1092
|
+
this.sql = batch_sql;
|
|
1093
|
+
|
|
1094
|
+
final_res = await this.exec(batch_sql);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
this.log('success', `批量添加数据完成,共${list.length}条`);
|
|
1098
|
+
return final_res;
|
|
1139
1099
|
} catch (error) {
|
|
1140
1100
|
this.log('error', '批量添加数据失败', error);
|
|
1141
1101
|
// 返回默认操作结果对象,保持返回值类型一致
|
|
@@ -1197,71 +1157,53 @@ Sql.prototype.delList = async function (list, like, batch_size = 100, timeout =
|
|
|
1197
1157
|
throw new Error('表名或数据列表未设置');
|
|
1198
1158
|
}
|
|
1199
1159
|
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
|
-
this.log('info', `开始分批删除数据,共${total}批,每批${batch_size}条`);
|
|
1220
|
-
|
|
1221
|
-
// 使用事务包装整个操作以保证原子性
|
|
1222
|
-
let final_res = null;
|
|
1223
|
-
try {
|
|
1224
|
-
// 开始事务
|
|
1225
|
-
await this.exec('BEGIN;', 15000);
|
|
1160
|
+
if (list.length <= batch_size) {
|
|
1161
|
+
let sql = '';
|
|
1162
|
+
for (const item of list) {
|
|
1163
|
+
sql += this.toDelSql(item.query, like);
|
|
1164
|
+
}
|
|
1165
|
+
this.sql = sql;
|
|
1166
|
+
return await this.exec(sql, [], timeout);
|
|
1167
|
+
}
|
|
1226
1168
|
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
this.log('debug', `处理第${i + 1}/${total}批数据,${batch.length}条`);
|
|
1169
|
+
// 分批处理大数据量
|
|
1170
|
+
const total = Math.ceil(list.length / batch_size);
|
|
1171
|
+
this.log('info', `开始分批删除数据,共${total}批,每批${batch_size}条`);
|
|
1231
1172
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1173
|
+
// 使用事务包装整个操作以保证原子性
|
|
1174
|
+
let final_res = null;
|
|
1175
|
+
try {
|
|
1176
|
+
// 开始事务
|
|
1177
|
+
await this.exec('BEGIN;');
|
|
1236
1178
|
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
setTimeout(() => reject(new Error(`第${i + 1}批数据删除超时`)), timeout / total);
|
|
1242
|
-
})
|
|
1243
|
-
]);
|
|
1244
|
-
}
|
|
1179
|
+
// 分批处理
|
|
1180
|
+
for (let i = 0; i < total; i++) {
|
|
1181
|
+
const batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
1182
|
+
this.log('debug', `处理第${i + 1}/${total}批数据,${batch.length}条`);
|
|
1245
1183
|
|
|
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);
|
|
1184
|
+
let batch_sql = '';
|
|
1185
|
+
for (const item of batch) {
|
|
1186
|
+
batch_sql += this.toDelSql(item.query, like);
|
|
1258
1187
|
}
|
|
1188
|
+
final_res = await this.exec(batch_sql, [], timeout);
|
|
1189
|
+
}
|
|
1259
1190
|
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1191
|
+
// 提交事务
|
|
1192
|
+
await this.exec('COMMIT;', [], timeout);
|
|
1193
|
+
} catch (error) {
|
|
1194
|
+
// 发生错误时回滚事务
|
|
1195
|
+
try {
|
|
1196
|
+
await this.exec('ROLLBACK;', [], timeout);
|
|
1197
|
+
this.log('error', '批量删除数据失败,事务已回滚', error);
|
|
1198
|
+
} catch (rollback_err) {
|
|
1199
|
+
this.log('error', '批量删除数据失败,事务回滚也失败', rollback_err);
|
|
1200
|
+
}
|
|
1201
|
+
// 记录错误日志
|
|
1202
|
+
this.log('error', 'SQL语句执行失败', error);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
this.log('success', `批量删除数据完成,共${list.length}条`);
|
|
1206
|
+
return final_res;
|
|
1265
1207
|
} catch (err) {
|
|
1266
1208
|
this.log('error', '批量删除数据失败', err);
|
|
1267
1209
|
// 返回默认操作结果对象,保持返回值类型一致
|
|
@@ -1281,69 +1223,52 @@ Sql.prototype.setList = async function (list, like = false, batch_size = 100, ti
|
|
|
1281
1223
|
throw new Error('表名或数据列表未设置');
|
|
1282
1224
|
}
|
|
1283
1225
|
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
|
-
}
|
|
1226
|
+
if (list.length <= batch_size) {
|
|
1227
|
+
let sql = '';
|
|
1228
|
+
for (const item of list) {
|
|
1229
|
+
sql += this.toSetSql(item.query, item.obj, like);
|
|
1230
|
+
}
|
|
1231
|
+
this.sql = sql;
|
|
1232
|
+
return await this.exec(sql, [], timeout);
|
|
1233
|
+
}
|
|
1300
1234
|
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1235
|
+
// 分批处理大数据量
|
|
1236
|
+
const total = Math.ceil(list.length / batch_size);
|
|
1237
|
+
this.log('info', `开始分批更新数据,共${total}批,每批${batch_size}条`);
|
|
1304
1238
|
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1239
|
+
// 使用事务包装整个操作以保证原子性
|
|
1240
|
+
let final_res = null;
|
|
1241
|
+
try {
|
|
1242
|
+
// 开始事务
|
|
1243
|
+
await this.exec('BEGIN;');
|
|
1310
1244
|
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1245
|
+
// 分批处理
|
|
1246
|
+
for (let i = 0; i < total; i++) {
|
|
1247
|
+
const batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
1248
|
+
this.log('debug', `处理第${i + 1}/${total}批数据,${batch.length}条`);
|
|
1315
1249
|
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1250
|
+
let batch_sql = '';
|
|
1251
|
+
for (const item of batch) {
|
|
1252
|
+
batch_sql += this.toSetSql(item.query, item.obj, like);
|
|
1253
|
+
}
|
|
1320
1254
|
|
|
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
|
-
}
|
|
1255
|
+
final_res = await this.exec(batch_sql, [], timeout);
|
|
1256
|
+
}
|
|
1329
1257
|
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1258
|
+
// 提交事务
|
|
1259
|
+
await this.exec('COMMIT;', [], timeout);
|
|
1260
|
+
} catch (error) {
|
|
1261
|
+
// 发生错误时回滚事务
|
|
1262
|
+
try {
|
|
1263
|
+
await this.exec('ROLLBACK;', [], timeout);
|
|
1264
|
+
this.log('error', '批量更新数据失败,事务已回滚', error);
|
|
1265
|
+
} catch (rollback_err) {
|
|
1266
|
+
this.log('error', '批量更新数据失败,事务回滚也失败', rollback_err);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1341
1269
|
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
})(),
|
|
1345
|
-
timeout_promise
|
|
1346
|
-
]);
|
|
1270
|
+
this.log('success', `批量更新数据完成,共${list.length}条`);
|
|
1271
|
+
return final_res;
|
|
1347
1272
|
} catch (err) {
|
|
1348
1273
|
this.log('error', '批量更新数据失败', err);
|
|
1349
1274
|
// 返回默认操作结果对象,保持返回值类型一致
|
|
Binary file
|
package/test_db/test_basic.db
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|