mm_sqlite 1.3.9 → 1.4.2
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 +200 -264
- package/index.js +128 -509
- package/package.json +1 -1
- package/pool.js +241 -0
- package/sql.js +311 -414
- package/test_db/test.db +0 -0
- package/test_db/test_basic.db +0 -0
- package/test_db/test_core_methods.db +0 -0
package/index.js
CHANGED
|
@@ -3,6 +3,8 @@ const {
|
|
|
3
3
|
DB
|
|
4
4
|
} = require('./db');
|
|
5
5
|
const { Base } = require('mm_expand');
|
|
6
|
+
const SQLitePool = require('./pool');
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* SQLite数据库操作类
|
|
@@ -21,23 +23,15 @@ class SQLite extends Base {
|
|
|
21
23
|
database: 'mm',
|
|
22
24
|
charset: 'utf8mb4',
|
|
23
25
|
timezone: '+08:00',
|
|
24
|
-
connect_timeout: 5000, //
|
|
25
|
-
acquire_timeout: 5000, //
|
|
26
|
-
query_timeout: 10000, //
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
max_reconnect_attempts: 0,
|
|
34
|
-
wait_for_connections: false,
|
|
35
|
-
// 连接池相关配置
|
|
36
|
-
pool_min: 1,
|
|
37
|
-
pool_max: 3, // 减少最大连接池大小
|
|
38
|
-
pool_acquire_timeout: 5000, // 减少连接池获取超时
|
|
39
|
-
pool_idle_timeout: 30000, // 减少空闲超时
|
|
40
|
-
pool_reap_interval: 5000 // 减少回收间隔
|
|
26
|
+
connect_timeout: 5000, // 连接超时时间
|
|
27
|
+
acquire_timeout: 5000, // 获取连接超时时间
|
|
28
|
+
query_timeout: 10000, // 查询超时时间
|
|
29
|
+
// 连接池配置(统一使用连接池模式)
|
|
30
|
+
pool_min: 1, // 最小连接数
|
|
31
|
+
pool_max: 3, // 最大连接数
|
|
32
|
+
pool_acquire_timeout: 5000, // 连接池获取超时
|
|
33
|
+
pool_idle_timeout: 30000, // 空闲超时
|
|
34
|
+
pool_reap_interval: 5000 // 回收间隔
|
|
41
35
|
};
|
|
42
36
|
|
|
43
37
|
/**
|
|
@@ -46,9 +40,6 @@ class SQLite extends Base {
|
|
|
46
40
|
*/
|
|
47
41
|
constructor(config) {
|
|
48
42
|
super(config);
|
|
49
|
-
this._pool_connections = [];
|
|
50
|
-
this._pool_available = [];
|
|
51
|
-
this._pool_waiting = [];
|
|
52
43
|
}
|
|
53
44
|
}
|
|
54
45
|
|
|
@@ -58,7 +49,6 @@ class SQLite extends Base {
|
|
|
58
49
|
*/
|
|
59
50
|
SQLite.prototype._preset = function () {
|
|
60
51
|
this._connection = null;
|
|
61
|
-
this._pool = null;
|
|
62
52
|
this._status = 'closed';
|
|
63
53
|
this._last_connect_time = 0;
|
|
64
54
|
this._reconnecting = false;
|
|
@@ -67,29 +57,8 @@ SQLite.prototype._preset = function () {
|
|
|
67
57
|
this._db = null;
|
|
68
58
|
this._open = false;
|
|
69
59
|
this._conn_retry_count = 0;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
this._use_pool = this.config.connection_limit > 1;
|
|
73
|
-
this._pool_reaper = null;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* 初始化服务
|
|
78
|
-
* @returns {Promise<void>}
|
|
79
|
-
*/
|
|
80
|
-
SQLite.prototype._initService = async function () {
|
|
81
|
-
if (this._is_inited) {
|
|
82
|
-
this.log('warn', 'SQLite服务已初始化');
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
this.log('debug', '初始化SQLite服务', { config: this.config });
|
|
88
|
-
this._is_inited = true;
|
|
89
|
-
this.log('debug', 'SQLite服务初始化完成');
|
|
90
|
-
} catch (error) {
|
|
91
|
-
this.log('error', 'SQLite服务初始化失败', error);
|
|
92
|
-
}
|
|
60
|
+
// 初始化连接池实例
|
|
61
|
+
this._pool = new SQLitePool(this.config, this._getDbFilename.bind(this), this.log.bind(this));
|
|
93
62
|
};
|
|
94
63
|
|
|
95
64
|
/**
|
|
@@ -106,24 +75,7 @@ SQLite.prototype._getDbFilename = function () {
|
|
|
106
75
|
};
|
|
107
76
|
|
|
108
77
|
/**
|
|
109
|
-
*
|
|
110
|
-
* @private
|
|
111
|
-
* @returns {Promise<void>}
|
|
112
|
-
*/
|
|
113
|
-
SQLite.prototype._openInternal = async function () {
|
|
114
|
-
return new Promise((resolve, reject) => {
|
|
115
|
-
this._db = new sqlite3.Database(this._getDbFilename(), (err) => {
|
|
116
|
-
if (err) {
|
|
117
|
-
reject(err);
|
|
118
|
-
} else {
|
|
119
|
-
resolve();
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* 打开数据库连接
|
|
78
|
+
* 打开数据库连接(统一使用连接池模式)
|
|
127
79
|
* @param {number} timeout - 超时时间(毫秒)
|
|
128
80
|
* @returns {Promise<boolean>}
|
|
129
81
|
* @throws {TypeError} 当timeout参数无效时
|
|
@@ -139,65 +91,16 @@ SQLite.prototype.open = async function (timeout) {
|
|
|
139
91
|
return true;
|
|
140
92
|
}
|
|
141
93
|
|
|
142
|
-
const connect_timeout = timeout || this.config.connect_timeout || 10000;
|
|
143
94
|
this._status = 'connecting';
|
|
144
95
|
|
|
145
96
|
try {
|
|
146
97
|
this.config.dir.addDir();
|
|
147
|
-
if (this._use_pool) {
|
|
148
|
-
// 使用连接池模式
|
|
149
|
-
await this._initPool();
|
|
150
|
-
this._last_connect_time = Date.now();
|
|
151
|
-
this._status = 'connected';
|
|
152
|
-
this.log('info', '数据库连接池初始化成功', {
|
|
153
|
-
dir: this.config.dir,
|
|
154
|
-
database: this.config.database,
|
|
155
|
-
pool_min: this.config.pool_min,
|
|
156
|
-
pool_max: this.config.pool_max
|
|
157
|
-
});
|
|
158
|
-
return true;
|
|
159
|
-
} else {
|
|
160
|
-
// 使用单连接模式 - 替换Promise.race为更可靠的超时控制
|
|
161
|
-
let timeout_id = null;
|
|
162
|
-
let connection_success = false;
|
|
163
|
-
|
|
164
|
-
try {
|
|
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
|
-
}
|
|
183
98
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
});
|
|
190
|
-
return true;
|
|
191
|
-
} catch (connection_err) {
|
|
192
|
-
// 确保超时定时器被清除
|
|
193
|
-
if (timeout_id) {
|
|
194
|
-
clearTimeout(timeout_id);
|
|
195
|
-
timeout_id = null;
|
|
196
|
-
}
|
|
197
|
-
this.log('error', '连接过程错误详情', connection_err);
|
|
198
|
-
throw connection_err;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
99
|
+
// 统一使用连接池模式
|
|
100
|
+
await this._pool.init();
|
|
101
|
+
this._last_connect_time = Date.now();
|
|
102
|
+
this._status = 'connected';
|
|
103
|
+
return true;
|
|
201
104
|
} catch (err) {
|
|
202
105
|
this._status = 'closed';
|
|
203
106
|
this.log('error', '数据库连接失败', err);
|
|
@@ -206,7 +109,7 @@ SQLite.prototype.open = async function (timeout) {
|
|
|
206
109
|
};
|
|
207
110
|
|
|
208
111
|
/**
|
|
209
|
-
*
|
|
112
|
+
* 关闭数据库连接(统一使用连接池模式)
|
|
210
113
|
* @returns {Promise<boolean>}
|
|
211
114
|
*/
|
|
212
115
|
SQLite.prototype.close = function () {
|
|
@@ -217,32 +120,12 @@ SQLite.prototype.close = function () {
|
|
|
217
120
|
|
|
218
121
|
return new Promise((resolve) => {
|
|
219
122
|
try {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
this.log('info', '数据库连接池已关闭');
|
|
227
|
-
resolve(true);
|
|
228
|
-
} else if (this._db) {
|
|
229
|
-
// 单连接模式
|
|
230
|
-
this.log('debug', '关闭数据库连接');
|
|
231
|
-
this._db.close((err) => {
|
|
232
|
-
if (err) {
|
|
233
|
-
this.log('error', '关闭数据库失败', err);
|
|
234
|
-
} else {
|
|
235
|
-
this.log('info', '数据库已关闭');
|
|
236
|
-
}
|
|
237
|
-
this._db = null;
|
|
238
|
-
this._open = false;
|
|
239
|
-
this._status = 'closed';
|
|
240
|
-
resolve(!err);
|
|
241
|
-
});
|
|
242
|
-
} else {
|
|
243
|
-
this._status = 'closed';
|
|
244
|
-
resolve(true);
|
|
245
|
-
}
|
|
123
|
+
// 统一关闭连接池
|
|
124
|
+
this._pool.close();
|
|
125
|
+
this._db = null;
|
|
126
|
+
this._open = false;
|
|
127
|
+
this._status = 'closed';
|
|
128
|
+
resolve(true);
|
|
246
129
|
} catch (err) {
|
|
247
130
|
this.log('error', '关闭数据库连接时发生异常', err);
|
|
248
131
|
this._status = 'closed';
|
|
@@ -252,7 +135,7 @@ SQLite.prototype.close = function () {
|
|
|
252
135
|
};
|
|
253
136
|
|
|
254
137
|
/**
|
|
255
|
-
*
|
|
138
|
+
* 获取数据库连接(统一使用连接池模式)
|
|
256
139
|
* @param {number} timeout - 超时时间(毫秒)
|
|
257
140
|
* @returns {Promise<object>}
|
|
258
141
|
* @throws {TypeError} 当timeout参数无效时
|
|
@@ -269,37 +152,8 @@ SQLite.prototype.getConn = async function (timeout) {
|
|
|
269
152
|
await this.open(acquire_timeout);
|
|
270
153
|
}
|
|
271
154
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
// 从连接池获取连接
|
|
275
|
-
return await this._getPoolConnection();
|
|
276
|
-
} else {
|
|
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
|
-
}
|
|
298
|
-
}
|
|
299
|
-
} catch (error) {
|
|
300
|
-
this.log('error', '获取连接失败', error);
|
|
301
|
-
return null;
|
|
302
|
-
}
|
|
155
|
+
// 统一从连接池获取连接
|
|
156
|
+
return await this._pool.getConnection(acquire_timeout);
|
|
303
157
|
};
|
|
304
158
|
|
|
305
159
|
/**
|
|
@@ -323,23 +177,21 @@ SQLite.prototype.run = async function (sql, params = [], timeout = 30000) {
|
|
|
323
177
|
}
|
|
324
178
|
|
|
325
179
|
this.error = null;
|
|
180
|
+
this.sql = '';
|
|
326
181
|
let conn = null;
|
|
327
182
|
const query_timeout = timeout || this.config.query_timeout || 30000;
|
|
328
|
-
|
|
183
|
+
let ret = [];
|
|
329
184
|
try {
|
|
330
185
|
// 获取连接
|
|
331
186
|
conn = await this.getConn(query_timeout);
|
|
332
|
-
|
|
333
|
-
const self = this;
|
|
334
|
-
// 替换Promise.race为更可靠的超时控制
|
|
335
187
|
let timeout_id = null;
|
|
336
|
-
|
|
337
|
-
|
|
188
|
+
|
|
189
|
+
ret = await new Promise((resolve, reject) => {
|
|
338
190
|
// 设置超时定时器
|
|
339
191
|
timeout_id = setTimeout(() => {
|
|
340
192
|
reject(new Error(`SQL查询超时: ${timeout}ms`));
|
|
341
193
|
}, timeout);
|
|
342
|
-
|
|
194
|
+
|
|
343
195
|
// 执行SQL查询
|
|
344
196
|
conn.all(sql, params || [], (error, rows) => {
|
|
345
197
|
// 清除超时定时器
|
|
@@ -347,14 +199,8 @@ SQLite.prototype.run = async function (sql, params = [], timeout = 30000) {
|
|
|
347
199
|
clearTimeout(timeout_id);
|
|
348
200
|
timeout_id = null;
|
|
349
201
|
}
|
|
350
|
-
|
|
351
202
|
if (error) {
|
|
352
|
-
|
|
353
|
-
self.error = {
|
|
354
|
-
code: error.errno,
|
|
355
|
-
message: error.message
|
|
356
|
-
};
|
|
357
|
-
// 不抛出错误,保持与MySQL一致的错误处理行为
|
|
203
|
+
reject(error);
|
|
358
204
|
resolve([]);
|
|
359
205
|
} else {
|
|
360
206
|
// 保持与MySQL兼容的返回值格式,始终返回数组
|
|
@@ -362,25 +208,19 @@ SQLite.prototype.run = async function (sql, params = [], timeout = 30000) {
|
|
|
362
208
|
}
|
|
363
209
|
});
|
|
364
210
|
});
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (this._use_pool && conn) {
|
|
377
|
-
this._releasePoolConnection(conn);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// 简化错误处理:SQLite不需要复杂的连接错误处理
|
|
381
|
-
// 直接返回空数组,保持与MySQL一致的错误处理行为
|
|
382
|
-
return [];
|
|
211
|
+
} catch (error) {
|
|
212
|
+
this.log('error', 'SQL执行失败', error);
|
|
213
|
+
this.sql = sql;
|
|
214
|
+
this.error = {
|
|
215
|
+
code: error.errno,
|
|
216
|
+
message: error.message
|
|
217
|
+
};
|
|
218
|
+
ret = [];
|
|
219
|
+
}
|
|
220
|
+
finally {
|
|
221
|
+
await releaseConn(conn);
|
|
383
222
|
}
|
|
223
|
+
return ret;
|
|
384
224
|
};
|
|
385
225
|
|
|
386
226
|
/**
|
|
@@ -404,22 +244,22 @@ SQLite.prototype.exec = async function (sql, params, timeout) {
|
|
|
404
244
|
}
|
|
405
245
|
|
|
406
246
|
this.error = null;
|
|
247
|
+
this.sql = '';
|
|
407
248
|
let conn = null;
|
|
408
249
|
timeout = timeout || this.config.query_timeout || 30000;
|
|
409
|
-
|
|
250
|
+
let ret = 0;
|
|
410
251
|
try {
|
|
411
252
|
// 获取连接
|
|
412
253
|
conn = await this.getConn(timeout);
|
|
413
|
-
|
|
254
|
+
this.sql = sql;
|
|
414
255
|
// 替换Promise.race为更可靠的超时控制
|
|
415
256
|
let timeout_id = null;
|
|
416
|
-
|
|
417
|
-
const result = await new Promise((resolve, reject) => {
|
|
257
|
+
ret = await new Promise((resolve, reject) => {
|
|
418
258
|
// 设置超时定时器
|
|
419
259
|
timeout_id = setTimeout(() => {
|
|
420
260
|
reject(new Error(`SQL执行超时: ${timeout}ms`));
|
|
421
261
|
}, timeout);
|
|
422
|
-
|
|
262
|
+
|
|
423
263
|
// 执行SQL语句
|
|
424
264
|
conn.run(sql, params || [], function (error) {
|
|
425
265
|
// 清除超时定时器
|
|
@@ -427,40 +267,28 @@ SQLite.prototype.exec = async function (sql, params, timeout) {
|
|
|
427
267
|
clearTimeout(timeout_id);
|
|
428
268
|
timeout_id = null;
|
|
429
269
|
}
|
|
430
|
-
|
|
431
270
|
if (error) {
|
|
432
|
-
|
|
433
|
-
_this.error = {
|
|
434
|
-
code: error.errno,
|
|
435
|
-
message: error.message
|
|
436
|
-
};
|
|
437
|
-
// 不抛出错误,保持与MySQL一致的错误处理行为
|
|
438
|
-
resolve(0);
|
|
271
|
+
reject(error);
|
|
439
272
|
} else {
|
|
440
|
-
// 保持与MySQL兼容的返回值格式
|
|
441
273
|
resolve(this.lastID || this.changes || 1);
|
|
442
274
|
}
|
|
443
275
|
});
|
|
444
276
|
});
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
} catch (err) {
|
|
453
|
-
this.log('error', 'SQL执行失败', err);
|
|
454
|
-
|
|
455
|
-
// 连接池模式下释放连接
|
|
456
|
-
if (this._use_pool && conn) {
|
|
457
|
-
this._releasePoolConnection(conn);
|
|
458
|
-
}
|
|
459
|
-
|
|
277
|
+
} catch (error) {
|
|
278
|
+
this.log('error', 'SQL执行失败', error);
|
|
279
|
+
this.sql = sql;
|
|
280
|
+
this.error = {
|
|
281
|
+
code: error.errno,
|
|
282
|
+
message: error.message
|
|
283
|
+
};
|
|
460
284
|
// 简化错误处理:SQLite不需要复杂的连接错误处理
|
|
461
285
|
// 返回0,保持与MySQL一致的错误处理行为
|
|
462
|
-
|
|
286
|
+
ret = 0;
|
|
463
287
|
}
|
|
288
|
+
finally {
|
|
289
|
+
await releaseConn(conn);
|
|
290
|
+
}
|
|
291
|
+
return ret;
|
|
464
292
|
};
|
|
465
293
|
|
|
466
294
|
/**
|
|
@@ -483,59 +311,53 @@ SQLite.prototype.read = async function (table, condition, options) {
|
|
|
483
311
|
throw new TypeError('options must be object');
|
|
484
312
|
}
|
|
485
313
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// 构建基础SQL查询
|
|
493
|
-
let sql = `SELECT * FROM ${table}`;
|
|
494
|
-
const params = [];
|
|
314
|
+
// 检查连接状态
|
|
315
|
+
if (this._status !== 'connected') {
|
|
316
|
+
throw new Error('数据库连接未建立');
|
|
317
|
+
}
|
|
495
318
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
for (const [field, value] of Object.entries(condition)) {
|
|
500
|
-
where_clauses.push(`${field} = ?`);
|
|
501
|
-
params.push(value);
|
|
502
|
-
}
|
|
503
|
-
sql += ` WHERE ${where_clauses.join(' AND ')}`;
|
|
504
|
-
}
|
|
319
|
+
// 构建基础SQL查询
|
|
320
|
+
let sql = `SELECT * FROM ${table}`;
|
|
321
|
+
const params = [];
|
|
505
322
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
323
|
+
// 处理条件
|
|
324
|
+
if (condition && Object.keys(condition).length > 0) {
|
|
325
|
+
const where_clauses = [];
|
|
326
|
+
for (const [field, value] of Object.entries(condition)) {
|
|
327
|
+
where_clauses.push(`${field} = ?`);
|
|
328
|
+
params.push(value);
|
|
509
329
|
}
|
|
330
|
+
sql += ` WHERE ${where_clauses.join(' AND ')}`;
|
|
331
|
+
}
|
|
510
332
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
333
|
+
// 处理排序
|
|
334
|
+
if (options && options.order_by) {
|
|
335
|
+
sql += ` ORDER BY ${options.order_by}`;
|
|
336
|
+
}
|
|
515
337
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}
|
|
338
|
+
// 处理分页
|
|
339
|
+
if (options && options.limit) {
|
|
340
|
+
sql += ` LIMIT ${options.limit}`;
|
|
341
|
+
}
|
|
519
342
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
}
|
|
343
|
+
if (options && options.offset) {
|
|
344
|
+
sql += ` OFFSET ${options.offset}`;
|
|
345
|
+
}
|
|
524
346
|
|
|
525
|
-
|
|
526
|
-
|
|
347
|
+
// 记录查询日志
|
|
348
|
+
if (this.config.debug) {
|
|
349
|
+
this.log('debug', '查询数据', { sql, params });
|
|
350
|
+
}
|
|
527
351
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
}
|
|
352
|
+
// 执行查询
|
|
353
|
+
const results = await this.run(sql, params);
|
|
531
354
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
} catch (error) {
|
|
535
|
-
this.log('error', '查询失败', error);
|
|
536
|
-
// 返回空数组作为默认值,保持返回值类型一致
|
|
537
|
-
return [];
|
|
355
|
+
if (this.config.debug) {
|
|
356
|
+
this.log('debug', '查询成功', { count: Array.isArray(results) ? results.length : 1 });
|
|
538
357
|
}
|
|
358
|
+
|
|
359
|
+
// 确保返回数组格式
|
|
360
|
+
return Array.isArray(results) ? results : [results];
|
|
539
361
|
};
|
|
540
362
|
|
|
541
363
|
/**
|
|
@@ -582,7 +404,7 @@ SQLite.prototype.db = function () {
|
|
|
582
404
|
* 开始事务
|
|
583
405
|
* @returns {Promise<object>} 事务连接对象
|
|
584
406
|
*/
|
|
585
|
-
SQLite.prototype.
|
|
407
|
+
SQLite.prototype.beginTransaction = async function () {
|
|
586
408
|
try {
|
|
587
409
|
// 检查连接状态
|
|
588
410
|
if (this._status !== 'connected') {
|
|
@@ -592,10 +414,15 @@ SQLite.prototype.beginTrans = async function () {
|
|
|
592
414
|
// 获取连接
|
|
593
415
|
const conn = await this.getConn();
|
|
594
416
|
|
|
417
|
+
// 标记连接为事务中
|
|
418
|
+
conn._in_transaction = true;
|
|
419
|
+
|
|
595
420
|
// 开始事务
|
|
596
421
|
await new Promise((resolve, reject) => {
|
|
597
422
|
conn.run('BEGIN TRANSACTION', (error) => {
|
|
598
423
|
if (error) {
|
|
424
|
+
// 事务开始失败,清除事务标记
|
|
425
|
+
conn._in_transaction = false;
|
|
599
426
|
reject(error);
|
|
600
427
|
} else {
|
|
601
428
|
resolve();
|
|
@@ -624,10 +451,9 @@ SQLite.prototype.beginTrans = async function () {
|
|
|
624
451
|
this.log('debug', '事务提交');
|
|
625
452
|
}
|
|
626
453
|
|
|
627
|
-
//
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
}
|
|
454
|
+
// 清除事务标记并释放连接
|
|
455
|
+
conn._in_transaction = false;
|
|
456
|
+
await releaseConn(conn);
|
|
631
457
|
},
|
|
632
458
|
rollback: async () => {
|
|
633
459
|
await new Promise((resolve, reject) => {
|
|
@@ -642,11 +468,9 @@ SQLite.prototype.beginTrans = async function () {
|
|
|
642
468
|
if (this.config.debug) {
|
|
643
469
|
this.log('debug', '事务回滚');
|
|
644
470
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
this._releasePoolConnection(conn);
|
|
649
|
-
}
|
|
471
|
+
// 清除事务标记并释放连接
|
|
472
|
+
conn._in_transaction = false;
|
|
473
|
+
await releaseConn(conn);
|
|
650
474
|
},
|
|
651
475
|
exec: async (sql, params) => {
|
|
652
476
|
// 在事务中执行SQL
|
|
@@ -680,7 +504,7 @@ SQLite.prototype.beginTrans = async function () {
|
|
|
680
504
|
* @returns {Promise<*>} 回调函数的返回值
|
|
681
505
|
* @throws {TypeError} 当callback参数无效时
|
|
682
506
|
*/
|
|
683
|
-
SQLite.prototype.
|
|
507
|
+
SQLite.prototype.transaction = async function (callback) {
|
|
684
508
|
// 参数校验
|
|
685
509
|
if (typeof callback !== 'function') {
|
|
686
510
|
throw new TypeError('callback must be function');
|
|
@@ -690,7 +514,7 @@ SQLite.prototype.trans = async function (callback) {
|
|
|
690
514
|
|
|
691
515
|
try {
|
|
692
516
|
// 开始事务
|
|
693
|
-
trans = await this.
|
|
517
|
+
trans = await this.beginTransaction();
|
|
694
518
|
|
|
695
519
|
// 执行回调函数,传入事务对象
|
|
696
520
|
const result = await callback(trans);
|
|
@@ -737,223 +561,6 @@ SQLite.prototype.table = function (name, key) {
|
|
|
737
561
|
return db;
|
|
738
562
|
};
|
|
739
563
|
|
|
740
|
-
/**
|
|
741
|
-
* 创建SQLite连接
|
|
742
|
-
* @private
|
|
743
|
-
* @returns {Promise<object>} 数据库连接对象
|
|
744
|
-
*/
|
|
745
|
-
SQLite.prototype._createConnection = function () {
|
|
746
|
-
return new Promise((resolve, reject) => {
|
|
747
|
-
const db = new sqlite3.Database(this._getDbFilename(), (err) => {
|
|
748
|
-
if (err) {
|
|
749
|
-
reject(err);
|
|
750
|
-
} else {
|
|
751
|
-
resolve(db);
|
|
752
|
-
}
|
|
753
|
-
});
|
|
754
|
-
});
|
|
755
|
-
};
|
|
756
|
-
|
|
757
|
-
/**
|
|
758
|
-
* 初始化连接池
|
|
759
|
-
* @private
|
|
760
|
-
* @returns {Promise<void>}
|
|
761
|
-
*/
|
|
762
|
-
SQLite.prototype._initPool = async function () {
|
|
763
|
-
if (this._pool_reaper) {
|
|
764
|
-
return; // 连接池已初始化
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
try {
|
|
768
|
-
// 创建最小连接数
|
|
769
|
-
for (let i = 0; i < this.config.pool_min; i++) {
|
|
770
|
-
const conn = await this._createConnection();
|
|
771
|
-
this._pool_connections.push(conn);
|
|
772
|
-
this._pool_available.push(conn);
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
// 启动连接回收器
|
|
776
|
-
this._pool_reaper = setInterval(() => {
|
|
777
|
-
this._reapIdleConnections();
|
|
778
|
-
}, this.config.pool_reap_interval);
|
|
779
|
-
|
|
780
|
-
this.log('debug', '连接池初始化完成', {
|
|
781
|
-
min: this.config.pool_min,
|
|
782
|
-
max: this.config.pool_max,
|
|
783
|
-
current: this._pool_connections.length
|
|
784
|
-
});
|
|
785
|
-
} catch (error) {
|
|
786
|
-
this.log('error', '连接池初始化失败', error);
|
|
787
|
-
throw error;
|
|
788
|
-
}
|
|
789
|
-
};
|
|
790
|
-
|
|
791
|
-
/**
|
|
792
|
-
* 回收空闲连接
|
|
793
|
-
* @private
|
|
794
|
-
*/
|
|
795
|
-
SQLite.prototype._reapIdleConnections = function () {
|
|
796
|
-
const now = Date.now();
|
|
797
|
-
const idle_timeout = this.config.pool_idle_timeout;
|
|
798
|
-
|
|
799
|
-
for (let i = this._pool_available.length - 1; i >= 0; i--) {
|
|
800
|
-
const conn = this._pool_available[i];
|
|
801
|
-
if (conn._last_used && now - conn._last_used > idle_timeout) {
|
|
802
|
-
// 关闭并移除空闲连接
|
|
803
|
-
conn.close((err) => {
|
|
804
|
-
if (err) {
|
|
805
|
-
this.log('error', '关闭空闲连接失败', err);
|
|
806
|
-
}
|
|
807
|
-
});
|
|
808
|
-
|
|
809
|
-
// 从连接池中移除
|
|
810
|
-
const conn_idx = this._pool_connections.indexOf(conn);
|
|
811
|
-
if (conn_idx > -1) {
|
|
812
|
-
this._pool_connections.splice(conn_idx, 1);
|
|
813
|
-
}
|
|
814
|
-
this._pool_available.splice(i, 1);
|
|
815
|
-
|
|
816
|
-
this.log('debug', '回收空闲连接');
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
};
|
|
820
|
-
|
|
821
|
-
/**
|
|
822
|
-
* 从连接池获取连接
|
|
823
|
-
* @private
|
|
824
|
-
* @returns {Promise<object>} 数据库连接
|
|
825
|
-
*/
|
|
826
|
-
SQLite.prototype._getPoolConnection = async function () {
|
|
827
|
-
const acquire_timeout = this.config.pool_acquire_timeout || 10000;
|
|
828
|
-
const start_time = Date.now();
|
|
829
|
-
|
|
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
|
-
|
|
840
|
-
const tryGetConnection = () => {
|
|
841
|
-
// 检查超时
|
|
842
|
-
if (Date.now() - start_time > acquire_timeout) {
|
|
843
|
-
cleanup();
|
|
844
|
-
reject(new Error(`获取连接超时(${acquire_timeout}ms)`));
|
|
845
|
-
return;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
// 检查是否有可用连接
|
|
849
|
-
if (this._pool_available.length > 0) {
|
|
850
|
-
const conn = this._pool_available.shift();
|
|
851
|
-
conn._last_used = Date.now();
|
|
852
|
-
cleanup();
|
|
853
|
-
resolve(conn);
|
|
854
|
-
return;
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
// 检查是否可以创建新连接
|
|
858
|
-
if (this._pool_connections.length < this.config.pool_max) {
|
|
859
|
-
this._createConnection()
|
|
860
|
-
.then(conn => {
|
|
861
|
-
this._pool_connections.push(conn);
|
|
862
|
-
conn._last_used = Date.now();
|
|
863
|
-
cleanup();
|
|
864
|
-
resolve(conn);
|
|
865
|
-
})
|
|
866
|
-
.catch(err => {
|
|
867
|
-
cleanup();
|
|
868
|
-
reject(err);
|
|
869
|
-
});
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
// 没有可用连接,等待一段时间后重试(避免无限循环)
|
|
874
|
-
timeout_id = setTimeout(tryGetConnection, 50);
|
|
875
|
-
};
|
|
876
|
-
|
|
877
|
-
tryGetConnection();
|
|
878
|
-
});
|
|
879
|
-
};
|
|
880
|
-
|
|
881
|
-
/**
|
|
882
|
-
* 释放连接回连接池
|
|
883
|
-
* @private
|
|
884
|
-
* @param {object} conn - 数据库连接
|
|
885
|
-
*/
|
|
886
|
-
SQLite.prototype._releaseConn = function (conn) {
|
|
887
|
-
if (this._pool_connections.includes(conn)) {
|
|
888
|
-
conn._last_used = Date.now();
|
|
889
|
-
this._pool_available.push(conn);
|
|
890
|
-
|
|
891
|
-
// 处理等待队列
|
|
892
|
-
if (this._pool_waiting.length > 0) {
|
|
893
|
-
const waiting = this._pool_waiting.shift();
|
|
894
|
-
waiting.resolve(conn);
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
};
|
|
898
|
-
|
|
899
|
-
/**
|
|
900
|
-
* 释放连接池连接
|
|
901
|
-
* @private
|
|
902
|
-
* @param {object} conn - 数据库连接
|
|
903
|
-
*/
|
|
904
|
-
SQLite.prototype._releasePoolConnection = function (conn) {
|
|
905
|
-
if (!conn || !this._pool_connections.includes(conn)) {
|
|
906
|
-
return;
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
// 标记连接最后使用时间
|
|
910
|
-
conn._last_used = Date.now();
|
|
911
|
-
|
|
912
|
-
// 将连接放回可用连接池
|
|
913
|
-
if (!this._pool_available.includes(conn)) {
|
|
914
|
-
this._pool_available.push(conn);
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
// 如果有等待的连接请求,立即处理
|
|
918
|
-
if (this._pool_waiting.length > 0) {
|
|
919
|
-
const waiting = this._pool_waiting.shift();
|
|
920
|
-
const next_conn = this._pool_available.shift();
|
|
921
|
-
if (next_conn && waiting.resolve) {
|
|
922
|
-
waiting.resolve(next_conn);
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
};
|
|
926
|
-
|
|
927
|
-
/**
|
|
928
|
-
* 关闭连接池
|
|
929
|
-
* @private
|
|
930
|
-
*/
|
|
931
|
-
SQLite.prototype._closePool = function () {
|
|
932
|
-
if (this._pool_reaper) {
|
|
933
|
-
clearInterval(this._pool_reaper);
|
|
934
|
-
this._pool_reaper = null;
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
// 关闭所有连接
|
|
938
|
-
this._pool_connections.forEach(conn => {
|
|
939
|
-
try {
|
|
940
|
-
conn.close((err) => {
|
|
941
|
-
if (err) {
|
|
942
|
-
this.log('error', '关闭连接池连接失败', err);
|
|
943
|
-
}
|
|
944
|
-
});
|
|
945
|
-
} catch (error) {
|
|
946
|
-
this.log('error', '关闭连接池连接异常', error);
|
|
947
|
-
}
|
|
948
|
-
});
|
|
949
|
-
|
|
950
|
-
this._pool_connections = [];
|
|
951
|
-
this._pool_available = [];
|
|
952
|
-
this._pool_waiting = [];
|
|
953
|
-
|
|
954
|
-
this.log('debug', '连接池已关闭');
|
|
955
|
-
};
|
|
956
|
-
|
|
957
564
|
/**
|
|
958
565
|
* 确保连接池对象存在
|
|
959
566
|
*/
|
|
@@ -982,6 +589,18 @@ function sqliteAdmin(scope, config) {
|
|
|
982
589
|
return obj;
|
|
983
590
|
}
|
|
984
591
|
|
|
592
|
+
/**
|
|
593
|
+
* 安全释放数据库连接
|
|
594
|
+
* @param {object} conn - 数据库连接对象
|
|
595
|
+
* @returns {Promise<void>}
|
|
596
|
+
*/
|
|
597
|
+
async function releaseConn(conn) {
|
|
598
|
+
if (!conn) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
await conn.release();
|
|
602
|
+
}
|
|
603
|
+
|
|
985
604
|
// 模块导出
|
|
986
605
|
exports.SQLite = SQLite;
|
|
987
606
|
exports.sqliteAdmin = sqliteAdmin;
|