mm_sqlite 1.2.1 → 1.2.3
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/README.md +98 -348
- package/README_EN.md +293 -0
- package/db.js +742 -690
- package/eslint.config.js +235 -0
- package/index.js +676 -676
- package/link_model.js +96 -99
- package/package.json +11 -4
- package/sql.js +1341 -1239
package/index.js
CHANGED
|
@@ -1,513 +1,513 @@
|
|
|
1
1
|
const sqlite3 = require('sqlite3').verbose();
|
|
2
2
|
const {
|
|
3
|
-
|
|
3
|
+
DB
|
|
4
4
|
} = require('./db');
|
|
5
5
|
const { Base } = require('mm_expand');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* SQLite数据库操作类
|
|
9
|
-
* @class
|
|
10
|
-
* @
|
|
9
|
+
* @class SQLite
|
|
10
|
+
* @augments Base
|
|
11
11
|
*/
|
|
12
|
-
class
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
12
|
+
class SQLite extends Base {
|
|
13
|
+
/**
|
|
14
|
+
* 默认配置
|
|
15
|
+
*/
|
|
16
|
+
static default_config = {
|
|
17
|
+
dir: './db/'.fullname(),
|
|
18
|
+
user: 'root',
|
|
19
|
+
password: '',
|
|
20
|
+
database: 'mm',
|
|
21
|
+
charset: 'utf8mb4',
|
|
22
|
+
timezone: '+08:00',
|
|
23
|
+
connect_timeout: 20000,
|
|
24
|
+
acquire_timeout: 20000,
|
|
25
|
+
query_timeout: 20000,
|
|
26
|
+
connection_limit: 1,
|
|
27
|
+
queue_limit: 0,
|
|
28
|
+
enable_keep_alive: true,
|
|
29
|
+
keep_alive_initial_delay: 10000,
|
|
30
|
+
enable_reconnect: true,
|
|
31
|
+
reconnect_interval: 1000,
|
|
32
|
+
max_reconnect_attempts: 5,
|
|
33
|
+
wait_for_connections: true,
|
|
34
|
+
// 连接池相关配置
|
|
35
|
+
pool_min: 1,
|
|
36
|
+
pool_max: 5,
|
|
37
|
+
pool_acquire_timeout: 30000,
|
|
38
|
+
pool_idle_timeout: 60000,
|
|
39
|
+
pool_reap_interval: 1000
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 构造函数
|
|
44
|
+
* @param {object} config - 配置对象
|
|
45
|
+
*/
|
|
46
|
+
constructor(config) {
|
|
47
|
+
const merged_cfg = { ...SQLite.default_config, ...config || {}};
|
|
48
|
+
super(merged_cfg);
|
|
49
|
+
|
|
50
|
+
this.config = merged_cfg;
|
|
51
|
+
this._connection = null;
|
|
52
|
+
this._pool = null;
|
|
53
|
+
this._status = 'closed';
|
|
54
|
+
this._last_connect_time = 0;
|
|
55
|
+
this._reconnecting = false;
|
|
56
|
+
this._is_inited = false;
|
|
57
|
+
this._is_destroyed = false;
|
|
58
|
+
this._db = null;
|
|
59
|
+
this._open = false;
|
|
60
|
+
this._conn_retry_count = 0;
|
|
61
|
+
|
|
62
|
+
// 连接池相关属性
|
|
63
|
+
this._use_pool = this.config.connection_limit > 1;
|
|
64
|
+
this._pool_connections = [];
|
|
65
|
+
this._pool_available = [];
|
|
66
|
+
this._pool_waiting = [];
|
|
67
|
+
this._pool_reaper = null;
|
|
68
|
+
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* 初始化服务
|
|
73
73
|
* @returns {Promise<void>}
|
|
74
74
|
*/
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
75
|
+
SQLite.prototype._initService = async function () {
|
|
76
|
+
if (this._is_inited) {
|
|
77
|
+
this.log('warn', 'SQLite服务已初始化');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
this.log('debug', '初始化SQLite服务', { config: this.config });
|
|
83
|
+
this._is_inited = true;
|
|
84
|
+
this.log('debug', 'SQLite服务初始化完成');
|
|
85
|
+
} catch (error) {
|
|
86
|
+
this.log('error', 'SQLite服务初始化失败', error);
|
|
87
|
+
}
|
|
88
88
|
};
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
91
|
* 获取数据库文件名
|
|
92
92
|
* @returns {string} 数据库文件名
|
|
93
93
|
*/
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
SQLite.prototype._getDbFilename = function () {
|
|
95
|
+
var file = this.config.database;
|
|
96
|
+
if (file.indexOf('.') === -1) {
|
|
97
|
+
file += '.db';
|
|
98
|
+
}
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
}
|
|
100
|
+
return file.fullname(this.config.dir);
|
|
101
|
+
};
|
|
102
102
|
|
|
103
103
|
/**
|
|
104
104
|
* 内部打开数据库连接
|
|
105
105
|
* @private
|
|
106
106
|
* @returns {Promise<void>}
|
|
107
107
|
*/
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
});
|
|
108
|
+
SQLite.prototype._openInternal = async function () {
|
|
109
|
+
return new Promise((resolve, reject) => {
|
|
110
|
+
this._db = new sqlite3.Database(this._getDbFilename(), (err) => {
|
|
111
|
+
if (err) {
|
|
112
|
+
reject(err);
|
|
113
|
+
} else {
|
|
114
|
+
resolve();
|
|
115
|
+
}
|
|
117
116
|
});
|
|
117
|
+
});
|
|
118
118
|
};
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
121
|
* 打开数据库连接
|
|
122
|
-
* @param {
|
|
122
|
+
* @param {number} timeout - 超时时间(毫秒)
|
|
123
123
|
* @returns {Promise<boolean>}
|
|
124
124
|
* @throws {TypeError} 当timeout参数无效时
|
|
125
125
|
*/
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
126
|
+
SQLite.prototype.open = async function (timeout) {
|
|
127
|
+
// 参数校验
|
|
128
|
+
if (timeout !== undefined && typeof timeout !== 'number') {
|
|
129
|
+
throw new TypeError('timeout must be number');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (this._status === 'connected' || this._status === 'connecting') {
|
|
133
|
+
this.log('warn', '数据库连接已存在或正在连接中');
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const connect_timeout = timeout || this.config.connect_timeout || 10000;
|
|
138
|
+
this._status = 'connecting';
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
this.config.dir.addDir();
|
|
142
|
+
if (this._use_pool) {
|
|
143
|
+
// 使用连接池模式
|
|
144
|
+
await this._initPool();
|
|
145
|
+
this._last_connect_time = Date.now();
|
|
146
|
+
this._status = 'connected';
|
|
147
|
+
this.log('info', '数据库连接池初始化成功', {
|
|
148
|
+
dir: this.config.dir,
|
|
149
|
+
database: this.config.database,
|
|
150
|
+
pool_min: this.config.pool_min,
|
|
151
|
+
pool_max: this.config.pool_max
|
|
152
|
+
});
|
|
153
|
+
return true;
|
|
154
|
+
} else {
|
|
155
|
+
// 使用单连接模式
|
|
156
|
+
const conn_promise = this._openInternal();
|
|
157
|
+
const timeout_promise = new Promise((unused_resolve, reject) => {
|
|
158
|
+
setTimeout(() => {
|
|
159
|
+
reject(new Error(`数据库连接超时(${connect_timeout}ms)`));
|
|
160
|
+
}, connect_timeout);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
await Promise.race([conn_promise, timeout_promise]);
|
|
165
|
+
|
|
166
|
+
this._last_connect_time = Date.now();
|
|
167
|
+
this._status = 'connected';
|
|
168
|
+
this.log('info', '数据库连接成功', {
|
|
169
|
+
dir: this.config.dir,
|
|
170
|
+
database: this.config.database
|
|
171
|
+
});
|
|
134
172
|
return true;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
await this._initPool();
|
|
145
|
-
this._last_connect_time = Date.now();
|
|
146
|
-
this._status = 'connected';
|
|
147
|
-
this.logger('info', '数据库连接池初始化成功', {
|
|
148
|
-
dir: this.config.dir,
|
|
149
|
-
database: this.config.database,
|
|
150
|
-
pool_min: this.config.pool_min,
|
|
151
|
-
pool_max: this.config.pool_max
|
|
152
|
-
});
|
|
153
|
-
return true;
|
|
154
|
-
} else {
|
|
155
|
-
// 使用单连接模式
|
|
156
|
-
const connection_promise = this._openInternal();
|
|
157
|
-
const timeout_promise = new Promise((_, reject) => {
|
|
158
|
-
setTimeout(() => {
|
|
159
|
-
reject(new Error(`数据库连接超时(${timeout}ms)`));
|
|
160
|
-
}, timeout);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
try {
|
|
164
|
-
await Promise.race([connection_promise, timeout_promise]);
|
|
165
|
-
|
|
166
|
-
this._last_connect_time = Date.now();
|
|
167
|
-
this._status = 'connected';
|
|
168
|
-
this.logger('info', '数据库连接成功', {
|
|
169
|
-
dir: this.config.dir,
|
|
170
|
-
database: this.config.database
|
|
171
|
-
});
|
|
172
|
-
return true;
|
|
173
|
-
} catch (connection_err) {
|
|
174
|
-
this.logger('error', '连接过程错误详情', connection_err);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
} catch (err) {
|
|
178
|
-
this._status = 'closed';
|
|
179
|
-
this.logger('error', '数据库连接失败', err);
|
|
180
|
-
return false;
|
|
181
|
-
}
|
|
173
|
+
} catch (connection_err) {
|
|
174
|
+
this.log('error', '连接过程错误详情', connection_err);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} catch (err) {
|
|
178
|
+
this._status = 'closed';
|
|
179
|
+
this.log('error', '数据库连接失败', err);
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
182
|
};
|
|
183
183
|
|
|
184
184
|
/**
|
|
185
185
|
* 关闭数据库连接
|
|
186
186
|
* @returns {Promise<boolean>}
|
|
187
187
|
*/
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
188
|
+
SQLite.prototype.close = function () {
|
|
189
|
+
if (this._status !== 'connected') {
|
|
190
|
+
this.log('warn', '数据库连接未建立');
|
|
191
|
+
return Promise.resolve(false);
|
|
192
|
+
}
|
|
193
193
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
194
|
+
return new Promise((resolve) => {
|
|
195
|
+
try {
|
|
196
|
+
if (this._use_pool) {
|
|
197
|
+
// 关闭连接池
|
|
198
|
+
this._closePool();
|
|
199
|
+
this._db = null;
|
|
200
|
+
this._open = false;
|
|
201
|
+
this._status = 'closed';
|
|
202
|
+
this.log('info', '数据库连接池已关闭');
|
|
203
|
+
resolve(true);
|
|
204
|
+
} else if (this._db) {
|
|
205
|
+
// 单连接模式
|
|
206
|
+
this.log('debug', '关闭数据库连接');
|
|
207
|
+
this._db.close((err) => {
|
|
208
|
+
if (err) {
|
|
209
|
+
this.log('error', '关闭数据库失败', err);
|
|
210
|
+
} else {
|
|
211
|
+
this.log('info', '数据库已关闭');
|
|
212
|
+
}
|
|
213
|
+
this._db = null;
|
|
214
|
+
this._open = false;
|
|
215
|
+
this._status = 'closed';
|
|
216
|
+
resolve(!err);
|
|
217
|
+
});
|
|
218
|
+
} else {
|
|
219
|
+
this._status = 'closed';
|
|
220
|
+
resolve(true);
|
|
221
|
+
}
|
|
222
|
+
} catch (err) {
|
|
223
|
+
this.log('error', '关闭数据库连接时发生异常', err);
|
|
224
|
+
this._status = 'closed';
|
|
225
|
+
resolve(false);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
228
|
};
|
|
229
229
|
|
|
230
230
|
/**
|
|
231
231
|
* 获取数据库连接
|
|
232
|
-
* @param {
|
|
233
|
-
* @returns {Promise<
|
|
232
|
+
* @param {number} timeout - 超时时间(毫秒)
|
|
233
|
+
* @returns {Promise<object>}
|
|
234
234
|
* @throws {TypeError} 当timeout参数无效时
|
|
235
235
|
*/
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
236
|
+
SQLite.prototype.getConn = async function (timeout) {
|
|
237
|
+
// 参数校验
|
|
238
|
+
if (timeout !== undefined && typeof timeout !== 'number') {
|
|
239
|
+
throw new TypeError('timeout must be number');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const acquire_timeout = timeout || this.config.acquire_timeout || this.config.connect_timeout || 20000;
|
|
243
|
+
|
|
244
|
+
if (this._status !== 'connected') {
|
|
245
|
+
await this.open(acquire_timeout);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
if (this._use_pool) {
|
|
250
|
+
// 从连接池获取连接
|
|
251
|
+
return await this._getPoolConnection();
|
|
252
|
+
} else {
|
|
253
|
+
// 单连接模式
|
|
254
|
+
const conn_promise = new Promise((resolve) => {
|
|
255
|
+
resolve(this._db);
|
|
256
|
+
});
|
|
257
|
+
const timeout_promise = new Promise((unused_resolve, reject) => {
|
|
258
|
+
setTimeout(() => {
|
|
259
|
+
reject(new Error(`获取数据库连接超时(${acquire_timeout}ms)`));
|
|
260
|
+
}, acquire_timeout);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const conn = await Promise.race([conn_promise, timeout_promise]);
|
|
264
|
+
return conn;
|
|
265
|
+
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
this.log('error', '获取连接失败', error);
|
|
268
|
+
|
|
269
|
+
if (error.code && (error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT')) {
|
|
270
|
+
this._handleConnectionError(error);
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
274
|
};
|
|
275
275
|
|
|
276
276
|
/**
|
|
277
277
|
* 执行SQL查询
|
|
278
|
-
* @param {
|
|
278
|
+
* @param {string} sql - SQL语句
|
|
279
279
|
* @param {Array} params - 参数数组
|
|
280
|
-
* @param {
|
|
281
|
-
* @returns {Promise<
|
|
280
|
+
* @param {number} timeout - 超时时间(毫秒)
|
|
281
|
+
* @returns {Promise<object>}
|
|
282
282
|
* @throws {TypeError} 当sql参数无效时
|
|
283
283
|
*/
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
284
|
+
SQLite.prototype.run = async function (sql, params, timeout) {
|
|
285
|
+
// 参数校验
|
|
286
|
+
if (typeof sql !== 'string' || sql.trim() === '') {
|
|
287
|
+
throw new TypeError('sql must be non-empty string');
|
|
288
|
+
}
|
|
289
|
+
if (params !== undefined && !Array.isArray(params)) {
|
|
290
|
+
throw new TypeError('params must be array');
|
|
291
|
+
}
|
|
292
|
+
if (timeout !== undefined && typeof timeout !== 'number') {
|
|
293
|
+
throw new TypeError('timeout must be number');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
this.error = null;
|
|
297
|
+
let conn = null;
|
|
298
|
+
const query_timeout = timeout || this.config.query_timeout || 30000;
|
|
299
|
+
|
|
300
|
+
try {
|
|
301
|
+
// 获取连接
|
|
302
|
+
conn = await this.getConn(query_timeout);
|
|
303
|
+
|
|
304
|
+
const self = this;
|
|
305
|
+
// 直接在方法内部实现超时控制
|
|
306
|
+
const query_promise = new Promise((resolve, reject) => {
|
|
307
|
+
conn.all(sql, params || [], (error, rows) => {
|
|
308
|
+
if (error) {
|
|
309
|
+
self.sql = sql;
|
|
310
|
+
self.error = {
|
|
311
|
+
code: error.errno,
|
|
312
|
+
message: error.message
|
|
313
|
+
};
|
|
314
|
+
reject(error);
|
|
315
|
+
} else {
|
|
316
|
+
// 保持与MySQL兼容的返回值格式,始终返回数组
|
|
317
|
+
resolve(rows);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
321
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
322
|
+
const timeout_promise = new Promise((unused_resolve, reject) => {
|
|
323
|
+
setTimeout(() => {
|
|
324
|
+
reject(new Error(`SQL查询超时: ${timeout}ms`));
|
|
325
|
+
}, timeout);
|
|
326
|
+
});
|
|
327
327
|
|
|
328
|
-
|
|
328
|
+
const result = await Promise.race([query_promise, timeout_promise]);
|
|
329
329
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
330
|
+
// 连接池模式下释放连接
|
|
331
|
+
if (this._use_pool && conn) {
|
|
332
|
+
this._releasePoolConnection(conn);
|
|
333
|
+
}
|
|
334
334
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
335
|
+
return result;
|
|
336
|
+
} catch (err) {
|
|
337
|
+
this.log('error', 'SQL执行失败', err);
|
|
338
338
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
339
|
+
// 连接池模式下释放连接
|
|
340
|
+
if (this._use_pool && conn) {
|
|
341
|
+
this._releasePoolConnection(conn);
|
|
342
|
+
}
|
|
343
343
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
// 返回空数组作为默认值,保持返回值类型一致
|
|
349
|
-
return [];
|
|
344
|
+
// 处理连接错误,触发重连
|
|
345
|
+
if (err.code && (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT')) {
|
|
346
|
+
this._handleConnectionError(err);
|
|
350
347
|
}
|
|
348
|
+
// 返回空数组作为默认值,保持返回值类型一致
|
|
349
|
+
return [];
|
|
350
|
+
}
|
|
351
351
|
};
|
|
352
352
|
|
|
353
353
|
/**
|
|
354
354
|
* 执行SQL语句(用于执行非查询语句如INSERT/UPDATE/DELETE)
|
|
355
|
-
* @param {
|
|
355
|
+
* @param {string} sql - SQL语句
|
|
356
356
|
* @param {Array} params - 参数数组
|
|
357
|
-
* @param {
|
|
358
|
-
* @returns {Promise<
|
|
357
|
+
* @param {number} timeout - 超时时间(毫秒)
|
|
358
|
+
* @returns {Promise<object>}
|
|
359
359
|
* @throws {TypeError} 当sql参数无效时
|
|
360
360
|
*/
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
361
|
+
SQLite.prototype.exec = async function (sql, params, timeout) {
|
|
362
|
+
// 参数校验
|
|
363
|
+
if (typeof sql !== 'string' || sql.trim() === '') {
|
|
364
|
+
throw new TypeError('sql must be non-empty string');
|
|
365
|
+
}
|
|
366
|
+
if (params !== undefined && !Array.isArray(params)) {
|
|
367
|
+
throw new TypeError('params must be array');
|
|
368
|
+
}
|
|
369
|
+
if (timeout !== undefined && typeof timeout !== 'number') {
|
|
370
|
+
throw new TypeError('timeout must be number');
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
this.error = null;
|
|
374
|
+
let conn = null;
|
|
375
|
+
timeout = timeout || this.config.query_timeout || 30000;
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
// 获取连接
|
|
379
|
+
conn = await this.getConn(timeout);
|
|
380
|
+
var _this = this;
|
|
381
|
+
// 直接在方法内部实现超时控制
|
|
382
|
+
const query_promise = new Promise((resolve, reject) => {
|
|
383
|
+
conn.run(sql, params || [], function (error) {
|
|
384
|
+
if (error) {
|
|
385
|
+
_this.sql = sql;
|
|
386
|
+
_this.error = {
|
|
387
|
+
code: error.errno,
|
|
388
|
+
message: error.message
|
|
389
|
+
};
|
|
390
|
+
reject(error);
|
|
391
|
+
resolve(0);
|
|
392
|
+
} else {
|
|
393
|
+
// 保持与MySQL兼容的返回值格式
|
|
394
|
+
resolve(this.lastID || this.changes || 1);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
398
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
399
|
+
const timeout_promise = new Promise((unused_resolve, reject) => {
|
|
400
|
+
setTimeout(() => {
|
|
401
|
+
reject(new Error(`SQL执行超时: ${timeout}ms`));
|
|
402
|
+
}, timeout);
|
|
403
|
+
});
|
|
404
404
|
|
|
405
|
-
|
|
405
|
+
const result = await Promise.race([query_promise, timeout_promise]);
|
|
406
406
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
407
|
+
// 连接池模式下释放连接
|
|
408
|
+
if (this._use_pool && conn) {
|
|
409
|
+
this._releasePoolConnection(conn);
|
|
410
|
+
}
|
|
411
411
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
412
|
+
return result;
|
|
413
|
+
} catch (err) {
|
|
414
|
+
this.log('error', 'SQL执行失败', err);
|
|
415
415
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
416
|
+
// 连接池模式下释放连接
|
|
417
|
+
if (this._use_pool && conn) {
|
|
418
|
+
this._releasePoolConnection(conn);
|
|
419
|
+
}
|
|
420
420
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
}
|
|
425
|
-
// 返回默认操作结果对象,保持返回值类型一致
|
|
426
|
-
return 0;
|
|
421
|
+
// 处理连接错误,触发重连
|
|
422
|
+
if (err.code && (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT')) {
|
|
423
|
+
this._handleConnectionError(err);
|
|
427
424
|
}
|
|
425
|
+
// 返回默认操作结果对象,保持返回值类型一致
|
|
426
|
+
return 0;
|
|
427
|
+
}
|
|
428
428
|
};
|
|
429
429
|
|
|
430
430
|
/**
|
|
431
431
|
* 读取整张表的数据
|
|
432
|
-
* @param {
|
|
433
|
-
* @param {
|
|
434
|
-
* @param {
|
|
432
|
+
* @param {string} table - 表名
|
|
433
|
+
* @param {object} condition - 查询条件
|
|
434
|
+
* @param {object} options - 选项(order_by, limit, offset等)
|
|
435
435
|
* @returns {Promise<Array>}
|
|
436
436
|
* @throws {TypeError} 当table参数无效时
|
|
437
437
|
*/
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
438
|
+
SQLite.prototype.read = async function (table, condition, options) {
|
|
439
|
+
// 参数校验
|
|
440
|
+
if (typeof table !== 'string' || table.trim() === '') {
|
|
441
|
+
throw new TypeError('table must be non-empty string');
|
|
442
|
+
}
|
|
443
|
+
if (condition !== undefined && (typeof condition !== 'object' || Array.isArray(condition))) {
|
|
444
|
+
throw new TypeError('condition must be object');
|
|
445
|
+
}
|
|
446
|
+
if (options !== undefined && (typeof options !== 'object' || Array.isArray(options))) {
|
|
447
|
+
throw new TypeError('options must be object');
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
// 检查连接状态
|
|
452
|
+
if (this._status !== 'connected') {
|
|
453
|
+
throw new Error('数据库连接未建立');
|
|
448
454
|
}
|
|
449
455
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
throw new Error('数据库连接未建立');
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// 构建基础SQL查询
|
|
457
|
-
let sql = `SELECT * FROM ${table}`;
|
|
458
|
-
const params = [];
|
|
456
|
+
// 构建基础SQL查询
|
|
457
|
+
let sql = `SELECT * FROM ${table}`;
|
|
458
|
+
const params = [];
|
|
459
459
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
// 处理排序
|
|
471
|
-
if (options && options.order_by) {
|
|
472
|
-
sql += ` ORDER BY ${options.order_by}`;
|
|
473
|
-
}
|
|
460
|
+
// 处理条件
|
|
461
|
+
if (condition && Object.keys(condition).length > 0) {
|
|
462
|
+
const where_clauses = [];
|
|
463
|
+
for (const [field, value] of Object.entries(condition)) {
|
|
464
|
+
where_clauses.push(`${field} = ?`);
|
|
465
|
+
params.push(value);
|
|
466
|
+
}
|
|
467
|
+
sql += ` WHERE ${where_clauses.join(' AND ')}`;
|
|
468
|
+
}
|
|
474
469
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
470
|
+
// 处理排序
|
|
471
|
+
if (options && options.order_by) {
|
|
472
|
+
sql += ` ORDER BY ${options.order_by}`;
|
|
473
|
+
}
|
|
479
474
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
475
|
+
// 处理分页
|
|
476
|
+
if (options && options.limit) {
|
|
477
|
+
sql += ` LIMIT ${options.limit}`;
|
|
478
|
+
}
|
|
483
479
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
}
|
|
480
|
+
if (options && options.offset) {
|
|
481
|
+
sql += ` OFFSET ${options.offset}`;
|
|
482
|
+
}
|
|
488
483
|
|
|
489
|
-
|
|
490
|
-
|
|
484
|
+
// 记录查询日志
|
|
485
|
+
if (this.config.debug) {
|
|
486
|
+
this.log('debug', '查询数据', { sql, params });
|
|
487
|
+
}
|
|
491
488
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
}
|
|
489
|
+
// 执行查询
|
|
490
|
+
const results = await this.run(sql, params);
|
|
495
491
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
} catch (error) {
|
|
499
|
-
this.logger('error', '查询失败', error);
|
|
500
|
-
// 返回空数组作为默认值,保持返回值类型一致
|
|
501
|
-
return [];
|
|
492
|
+
if (this.config.debug) {
|
|
493
|
+
this.log('info', '查询成功', { count: Array.isArray(results) ? results.length : 1 });
|
|
502
494
|
}
|
|
495
|
+
|
|
496
|
+
// 确保返回数组格式
|
|
497
|
+
return Array.isArray(results) ? results : [results];
|
|
498
|
+
} catch (error) {
|
|
499
|
+
this.log('error', '查询失败', error);
|
|
500
|
+
// 返回空数组作为默认值,保持返回值类型一致
|
|
501
|
+
return [];
|
|
502
|
+
}
|
|
503
503
|
};
|
|
504
504
|
|
|
505
505
|
/**
|
|
506
506
|
* 获取数据库管理器(保持兼容性)
|
|
507
|
-
* @returns {
|
|
507
|
+
* @returns {object} DB实例
|
|
508
508
|
*/
|
|
509
|
-
|
|
510
|
-
|
|
509
|
+
SQLite.prototype.db = function () {
|
|
510
|
+
return new DB(this);
|
|
511
511
|
};
|
|
512
512
|
|
|
513
513
|
// /**
|
|
@@ -516,7 +516,7 @@ Sqlite.prototype.db = function () {
|
|
|
516
516
|
// * @returns {Object} 数据库操作实例
|
|
517
517
|
// * @throws {TypeError} 当database参数无效时
|
|
518
518
|
// */
|
|
519
|
-
//
|
|
519
|
+
// SQLite.prototype.db = function (database) {
|
|
520
520
|
// // 参数校验
|
|
521
521
|
// if (database !== undefined && typeof database !== 'string') {
|
|
522
522
|
// throw new TypeError('database must be string');
|
|
@@ -544,98 +544,98 @@ Sqlite.prototype.db = function () {
|
|
|
544
544
|
|
|
545
545
|
/**
|
|
546
546
|
* 开始事务
|
|
547
|
-
* @returns {Promise<
|
|
547
|
+
* @returns {Promise<object>} 事务连接对象
|
|
548
548
|
*/
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
549
|
+
SQLite.prototype.beginTrans = async function () {
|
|
550
|
+
try {
|
|
551
|
+
// 检查连接状态
|
|
552
|
+
if (this._status !== 'connected') {
|
|
553
|
+
throw new Error('数据库连接未建立');
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// 获取连接
|
|
557
|
+
const conn = await this.getConn();
|
|
558
|
+
|
|
559
|
+
// 开始事务
|
|
560
|
+
await new Promise((resolve, reject) => {
|
|
561
|
+
conn.run('BEGIN TRANSACTION', (error) => {
|
|
562
|
+
if (error) {
|
|
563
|
+
reject(error);
|
|
564
|
+
} else {
|
|
565
|
+
resolve();
|
|
554
566
|
}
|
|
567
|
+
});
|
|
568
|
+
});
|
|
555
569
|
|
|
556
|
-
|
|
557
|
-
|
|
570
|
+
if (this.config.debug) {
|
|
571
|
+
this.log('debug', '事务开始');
|
|
572
|
+
}
|
|
558
573
|
|
|
559
|
-
|
|
574
|
+
// 返回事务连接对象,包含提交和回滚方法
|
|
575
|
+
return {
|
|
576
|
+
conn,
|
|
577
|
+
commit: async () => {
|
|
560
578
|
await new Promise((resolve, reject) => {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
579
|
+
conn.run('COMMIT', (error) => {
|
|
580
|
+
if (error) {
|
|
581
|
+
reject(error);
|
|
582
|
+
} else {
|
|
583
|
+
resolve();
|
|
584
|
+
}
|
|
585
|
+
});
|
|
568
586
|
});
|
|
587
|
+
if (this.config.debug) {
|
|
588
|
+
this.log('debug', '事务提交');
|
|
589
|
+
}
|
|
569
590
|
|
|
591
|
+
// 连接池模式下释放连接
|
|
592
|
+
if (this._use_pool) {
|
|
593
|
+
this._releasePoolConnection(conn);
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
rollback: async () => {
|
|
597
|
+
await new Promise((resolve, reject) => {
|
|
598
|
+
conn.run('ROLLBACK', (error) => {
|
|
599
|
+
if (error) {
|
|
600
|
+
reject(error);
|
|
601
|
+
} else {
|
|
602
|
+
resolve();
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
});
|
|
570
606
|
if (this.config.debug) {
|
|
571
|
-
|
|
607
|
+
this.log('debug', '事务回滚');
|
|
572
608
|
}
|
|
573
609
|
|
|
574
|
-
//
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
this._releasePoolConnection(connection);
|
|
594
|
-
}
|
|
595
|
-
},
|
|
596
|
-
rollback: async () => {
|
|
597
|
-
await new Promise((resolve, reject) => {
|
|
598
|
-
connection.run('ROLLBACK', (error) => {
|
|
599
|
-
if (error) {
|
|
600
|
-
reject(error);
|
|
601
|
-
} else {
|
|
602
|
-
resolve();
|
|
603
|
-
}
|
|
604
|
-
});
|
|
605
|
-
});
|
|
606
|
-
if (this.config.debug) {
|
|
607
|
-
this.logger('debug', '事务回滚');
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
// 连接池模式下释放连接
|
|
611
|
-
if (this._use_pool) {
|
|
612
|
-
this._releasePoolConnection(connection);
|
|
613
|
-
}
|
|
614
|
-
},
|
|
615
|
-
exec: async (sql, params) => {
|
|
616
|
-
// 在事务中执行SQL
|
|
617
|
-
return new Promise((resolve, reject) => {
|
|
618
|
-
connection.run(sql, params || [], function (error) {
|
|
619
|
-
if (error) {
|
|
620
|
-
this.sql = error.sql;
|
|
621
|
-
this.error = {
|
|
622
|
-
code: error.errno,
|
|
623
|
-
message: error.message
|
|
624
|
-
}
|
|
625
|
-
reject(error);
|
|
626
|
-
resolve(0);
|
|
627
|
-
} else {
|
|
628
|
-
resolve(this.lastID || this.changes || 1);
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
});
|
|
610
|
+
// 连接池模式下释放连接
|
|
611
|
+
if (this._use_pool) {
|
|
612
|
+
this._releasePoolConnection(conn);
|
|
613
|
+
}
|
|
614
|
+
},
|
|
615
|
+
exec: async (sql, params) => {
|
|
616
|
+
// 在事务中执行SQL
|
|
617
|
+
return new Promise((resolve, reject) => {
|
|
618
|
+
conn.run(sql, params || [], function (error) {
|
|
619
|
+
if (error) {
|
|
620
|
+
this.sql = error.sql;
|
|
621
|
+
this.error = {
|
|
622
|
+
code: error.errno,
|
|
623
|
+
message: error.message
|
|
624
|
+
};
|
|
625
|
+
reject(error);
|
|
626
|
+
resolve(0);
|
|
627
|
+
} else {
|
|
628
|
+
resolve(this.lastID || this.changes || 1);
|
|
632
629
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
630
|
+
});
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
} catch (error) {
|
|
635
|
+
this.log('error', '事务开始失败', error);
|
|
636
|
+
// 重新抛出异常,因为调用方需要事务对象
|
|
637
|
+
throw error;
|
|
638
|
+
}
|
|
639
639
|
};
|
|
640
640
|
|
|
641
641
|
/**
|
|
@@ -644,78 +644,78 @@ Sqlite.prototype.beginTransaction = async function () {
|
|
|
644
644
|
* @returns {Promise<*>} 回调函数的返回值
|
|
645
645
|
* @throws {TypeError} 当callback参数无效时
|
|
646
646
|
*/
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
647
|
+
SQLite.prototype.trans = async function (callback) {
|
|
648
|
+
// 参数校验
|
|
649
|
+
if (typeof callback !== 'function') {
|
|
650
|
+
throw new TypeError('callback must be function');
|
|
651
|
+
}
|
|
652
652
|
|
|
653
|
-
|
|
653
|
+
let trans = null;
|
|
654
654
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
655
|
+
try {
|
|
656
|
+
// 开始事务
|
|
657
|
+
trans = await this.beginTrans();
|
|
658
658
|
|
|
659
|
-
|
|
660
|
-
|
|
659
|
+
// 执行回调函数,传入事务对象
|
|
660
|
+
const result = await callback(trans);
|
|
661
661
|
|
|
662
|
-
|
|
663
|
-
|
|
662
|
+
// 提交事务
|
|
663
|
+
await trans.commit();
|
|
664
664
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
this.logger('error', '事务执行失败', error);
|
|
675
|
-
// 重新抛出异常,因为调用方需要事务执行结果
|
|
676
|
-
throw error;
|
|
665
|
+
return result;
|
|
666
|
+
} catch (error) {
|
|
667
|
+
// 如果有事务,回滚
|
|
668
|
+
if (trans) {
|
|
669
|
+
await trans.rollback().catch(err => {
|
|
670
|
+
this.log('error', '事务回滚失败', err);
|
|
671
|
+
});
|
|
677
672
|
}
|
|
673
|
+
|
|
674
|
+
this.log('error', '事务执行失败', error);
|
|
675
|
+
// 重新抛出异常,因为调用方需要事务执行结果
|
|
676
|
+
throw error;
|
|
677
|
+
}
|
|
678
678
|
};
|
|
679
679
|
|
|
680
680
|
/**
|
|
681
681
|
* 设置当前操作的表名
|
|
682
|
-
* @param {
|
|
683
|
-
* @param {
|
|
684
|
-
* @returns {
|
|
682
|
+
* @param {string} name - 表名
|
|
683
|
+
* @param {string} key - 主键
|
|
684
|
+
* @returns {object} 数据库操作实例
|
|
685
685
|
* @throws {TypeError} 当name参数无效时
|
|
686
686
|
*/
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
687
|
+
SQLite.prototype.table = function (name, key) {
|
|
688
|
+
// 参数校验
|
|
689
|
+
if (typeof name !== 'string' || name.trim() === '') {
|
|
690
|
+
throw new TypeError('name must be non-empty string');
|
|
691
|
+
}
|
|
692
|
+
if (key !== undefined && typeof key !== 'string') {
|
|
693
|
+
throw new TypeError('key must be string');
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
var db = this.db();
|
|
697
|
+
db.table = name;
|
|
698
|
+
if (key) {
|
|
699
|
+
db.key = key;
|
|
700
|
+
}
|
|
701
|
+
return db;
|
|
702
702
|
};
|
|
703
703
|
|
|
704
704
|
/**
|
|
705
705
|
* 创建SQLite连接
|
|
706
706
|
* @private
|
|
707
|
-
* @returns {Promise<
|
|
707
|
+
* @returns {Promise<object>} 数据库连接对象
|
|
708
708
|
*/
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
});
|
|
709
|
+
SQLite.prototype._createConnection = function () {
|
|
710
|
+
return new Promise((resolve, reject) => {
|
|
711
|
+
const db = new sqlite3.Database(this._getDbFilename(), (err) => {
|
|
712
|
+
if (err) {
|
|
713
|
+
reject(err);
|
|
714
|
+
} else {
|
|
715
|
+
resolve(db);
|
|
716
|
+
}
|
|
718
717
|
});
|
|
718
|
+
});
|
|
719
719
|
};
|
|
720
720
|
|
|
721
721
|
/**
|
|
@@ -723,186 +723,186 @@ Sqlite.prototype._createConnection = function () {
|
|
|
723
723
|
* @private
|
|
724
724
|
* @returns {Promise<void>}
|
|
725
725
|
*/
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
726
|
+
SQLite.prototype._initPool = async function () {
|
|
727
|
+
if (this._pool_reaper) {
|
|
728
|
+
return; // 连接池已初始化
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
try {
|
|
732
|
+
// 创建最小连接数
|
|
733
|
+
for (let i = 0; i < this.config.pool_min; i++) {
|
|
734
|
+
const conn = await this._createConnection();
|
|
735
|
+
this._pool_connections.push(conn);
|
|
736
|
+
this._pool_available.push(conn);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// 启动连接回收器
|
|
740
|
+
this._pool_reaper = setInterval(() => {
|
|
741
|
+
this._reapIdleConnections();
|
|
742
|
+
}, this.config.pool_reap_interval);
|
|
743
|
+
|
|
744
|
+
this.log('debug', '连接池初始化完成', {
|
|
745
|
+
min: this.config.pool_min,
|
|
746
|
+
max: this.config.pool_max,
|
|
747
|
+
current: this._pool_connections.length
|
|
748
|
+
});
|
|
749
|
+
} catch (error) {
|
|
750
|
+
this.log('error', '连接池初始化失败', error);
|
|
751
|
+
throw error;
|
|
752
|
+
}
|
|
753
753
|
};
|
|
754
754
|
|
|
755
755
|
/**
|
|
756
756
|
* 回收空闲连接
|
|
757
757
|
* @private
|
|
758
758
|
*/
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
}
|
|
771
|
-
});
|
|
772
|
-
|
|
773
|
-
// 从连接池中移除
|
|
774
|
-
const conn_index = this._pool_connections.indexOf(conn);
|
|
775
|
-
if (conn_index > -1) {
|
|
776
|
-
this._pool_connections.splice(conn_index, 1);
|
|
777
|
-
}
|
|
778
|
-
this._pool_available.splice(i, 1);
|
|
779
|
-
|
|
780
|
-
this.logger('debug', '回收空闲连接');
|
|
759
|
+
SQLite.prototype._reapIdleConnections = function () {
|
|
760
|
+
const now = Date.now();
|
|
761
|
+
const idle_timeout = this.config.pool_idle_timeout;
|
|
762
|
+
|
|
763
|
+
for (let i = this._pool_available.length - 1; i >= 0; i--) {
|
|
764
|
+
const conn = this._pool_available[i];
|
|
765
|
+
if (conn._last_used && now - conn._last_used > idle_timeout) {
|
|
766
|
+
// 关闭并移除空闲连接
|
|
767
|
+
conn.close((err) => {
|
|
768
|
+
if (err) {
|
|
769
|
+
this.log('error', '关闭空闲连接失败', err);
|
|
781
770
|
}
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
// 从连接池中移除
|
|
774
|
+
const conn_idx = this._pool_connections.indexOf(conn);
|
|
775
|
+
if (conn_idx > -1) {
|
|
776
|
+
this._pool_connections.splice(conn_idx, 1);
|
|
777
|
+
}
|
|
778
|
+
this._pool_available.splice(i, 1);
|
|
779
|
+
|
|
780
|
+
this.log('debug', '回收空闲连接');
|
|
782
781
|
}
|
|
782
|
+
}
|
|
783
783
|
};
|
|
784
784
|
|
|
785
785
|
/**
|
|
786
786
|
* 从连接池获取连接
|
|
787
787
|
* @private
|
|
788
|
-
* @returns {Promise<
|
|
788
|
+
* @returns {Promise<object>} 数据库连接
|
|
789
789
|
*/
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
790
|
+
SQLite.prototype._getPoolConnection = async function () {
|
|
791
|
+
const acquire_timeout = this.config.pool_acquire_timeout;
|
|
792
|
+
const start_time = Date.now();
|
|
793
|
+
|
|
794
|
+
return new Promise((resolve, reject) => {
|
|
795
|
+
const tryGetConnection = () => {
|
|
796
|
+
// 检查超时
|
|
797
|
+
if (Date.now() - start_time > acquire_timeout) {
|
|
798
|
+
reject(new Error(`获取连接超时(${acquire_timeout}ms)`));
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
801
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
802
|
+
// 检查是否有可用连接
|
|
803
|
+
if (this._pool_available.length > 0) {
|
|
804
|
+
const conn = this._pool_available.shift();
|
|
805
|
+
conn._last_used = Date.now();
|
|
806
|
+
resolve(conn);
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
809
|
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
810
|
+
// 检查是否可以创建新连接
|
|
811
|
+
if (this._pool_connections.length < this.config.pool_max) {
|
|
812
|
+
this._createConnection()
|
|
813
|
+
.then(conn => {
|
|
814
|
+
this._pool_connections.push(conn);
|
|
815
|
+
conn._last_used = Date.now();
|
|
816
|
+
resolve(conn);
|
|
817
|
+
})
|
|
818
|
+
.catch(reject);
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
821
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
822
|
+
// 没有可用连接,等待一段时间后重试
|
|
823
|
+
setTimeout(tryGetConnection, 100);
|
|
824
|
+
};
|
|
825
825
|
|
|
826
|
-
|
|
827
|
-
|
|
826
|
+
tryGetConnection();
|
|
827
|
+
});
|
|
828
828
|
};
|
|
829
829
|
|
|
830
830
|
/**
|
|
831
831
|
* 释放连接回连接池
|
|
832
832
|
* @private
|
|
833
|
-
* @param {
|
|
833
|
+
* @param {object} conn - 数据库连接
|
|
834
834
|
*/
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
835
|
+
SQLite.prototype._releaseConn = function (conn) {
|
|
836
|
+
if (this._pool_connections.includes(conn)) {
|
|
837
|
+
conn._last_used = Date.now();
|
|
838
|
+
this._pool_available.push(conn);
|
|
839
839
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
}
|
|
840
|
+
// 处理等待队列
|
|
841
|
+
if (this._pool_waiting.length > 0) {
|
|
842
|
+
const waiting = this._pool_waiting.shift();
|
|
843
|
+
waiting.resolve(conn);
|
|
845
844
|
}
|
|
845
|
+
}
|
|
846
846
|
};
|
|
847
847
|
|
|
848
848
|
/**
|
|
849
849
|
* 关闭连接池
|
|
850
850
|
* @private
|
|
851
851
|
*/
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
}
|
|
865
|
-
});
|
|
866
|
-
} catch (error) {
|
|
867
|
-
this.logger('error', '关闭连接池连接异常', error);
|
|
852
|
+
SQLite.prototype._closePool = function () {
|
|
853
|
+
if (this._pool_reaper) {
|
|
854
|
+
clearInterval(this._pool_reaper);
|
|
855
|
+
this._pool_reaper = null;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// 关闭所有连接
|
|
859
|
+
this._pool_connections.forEach(conn => {
|
|
860
|
+
try {
|
|
861
|
+
conn.close((err) => {
|
|
862
|
+
if (err) {
|
|
863
|
+
this.log('error', '关闭连接池连接失败', err);
|
|
868
864
|
}
|
|
869
|
-
|
|
865
|
+
});
|
|
866
|
+
} catch (error) {
|
|
867
|
+
this.log('error', '关闭连接池连接异常', error);
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
870
|
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
871
|
+
this._pool_connections = [];
|
|
872
|
+
this._pool_available = [];
|
|
873
|
+
this._pool_waiting = [];
|
|
874
874
|
|
|
875
|
-
|
|
875
|
+
this.log('debug', '连接池已关闭');
|
|
876
876
|
};
|
|
877
877
|
|
|
878
878
|
/**
|
|
879
879
|
* 确保连接池对象存在
|
|
880
880
|
*/
|
|
881
881
|
if (!$.pool) {
|
|
882
|
-
|
|
882
|
+
$.pool = {};
|
|
883
883
|
}
|
|
884
884
|
if (!$.pool.sqlite) {
|
|
885
|
-
|
|
885
|
+
$.pool.sqlite = {};
|
|
886
886
|
}
|
|
887
887
|
|
|
888
888
|
/**
|
|
889
889
|
* Sqlite管理器,用于创建缓存
|
|
890
|
-
* @param {
|
|
891
|
-
* @param {
|
|
892
|
-
* @
|
|
890
|
+
* @param {string} scope 作用域
|
|
891
|
+
* @param {object} config 配置参数
|
|
892
|
+
* @returns {object} 返回一个Sqlite类实例
|
|
893
893
|
*/
|
|
894
894
|
function sqliteAdmin(scope, config) {
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
895
|
+
if (!scope) {
|
|
896
|
+
scope = 'sys';
|
|
897
|
+
}
|
|
898
|
+
var obj = $.pool.sqlite[scope];
|
|
899
|
+
if (!obj) {
|
|
900
|
+
$.pool.sqlite[scope] = new Sqlite(config);
|
|
901
|
+
obj = $.pool.sqlite[scope];
|
|
902
|
+
}
|
|
903
|
+
return obj;
|
|
904
904
|
}
|
|
905
905
|
|
|
906
906
|
// 模块导出
|
|
907
|
-
exports.
|
|
907
|
+
exports.SQLite = SQLite;
|
|
908
908
|
exports.sqliteAdmin = sqliteAdmin;
|