mm_sqlite 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +351 -1
- package/db/data/example.db.db +0 -0
- package/db/db/test_all_methods.db.db +0 -0
- package/db/db/test_core_methods.db.db +0 -0
- package/db/mm.db +0 -0
- package/db.js +562 -138
- package/index.js +687 -187
- package/package.json +16 -11
- package/sql.js +854 -125
- package/sql_builder.js +375 -0
- package/test.js +75 -648
- package/test_all_methods.js +115 -0
- package/index_/345/217/202/350/200/203.js +0 -299
package/index.js
CHANGED
|
@@ -1,224 +1,724 @@
|
|
|
1
|
-
const resolve = require('path').resolve;
|
|
2
1
|
const sqlite3 = require('sqlite3').verbose();
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
* @description 查询sql
|
|
59
|
-
* @param {String} sql 查询参
|
|
60
|
-
* @param {Array} val 替换值
|
|
61
|
-
* @return {Promise|Array} 异步构造器, 当await时返回执行结果
|
|
62
|
-
*/
|
|
63
|
-
this.run = function(sql, val) {
|
|
64
|
-
sql = sql.replaceAll("&&", "AND").replaceAll("||", "OR").replaceAll("varchar", "text").replaceAll(" int", " integer");
|
|
65
|
-
var _this = this;
|
|
66
|
-
this.sql = sql;
|
|
67
|
-
|
|
68
|
-
// 返回一个 Promise
|
|
69
|
-
return new Promise((resolve, reject) => {
|
|
70
|
-
$this.conn.all(sql, val, function(err, rows) {
|
|
71
|
-
if (err) {
|
|
72
|
-
// reject(err);
|
|
73
|
-
_this.error = {
|
|
74
|
-
code: err.errno,
|
|
75
|
-
message: $.info(err).between('Error: ', ']')
|
|
76
|
-
};
|
|
77
|
-
resolve(rows || []);
|
|
78
|
-
} else {
|
|
79
|
-
_this.error = undefined;
|
|
80
|
-
resolve(rows || []);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* @description 增删改sql
|
|
88
|
-
* @param {String} sql 查询参
|
|
89
|
-
* @param {Array} val 替换值
|
|
90
|
-
* @return {Promise|Array} 异步构造器, 当await时返回执行结果
|
|
91
|
-
*/
|
|
92
|
-
this.exec = function(sql) {
|
|
93
|
-
sql = sql.replaceAll("&&", "AND").replaceAll("||", "OR").replaceAll("varchar", "text").replaceAll(" int", " integer");
|
|
94
|
-
var _this = this;
|
|
95
|
-
this.sql = sql;
|
|
96
|
-
// 返回一个 Promise
|
|
97
|
-
return new Promise((resolve, reject) => {
|
|
98
|
-
$this.conn.run(sql, function(err, rows) {
|
|
99
|
-
if (err) {
|
|
100
|
-
// reject(err);
|
|
101
|
-
_this.error = {
|
|
102
|
-
code: err.errno,
|
|
103
|
-
message: $.info(err).between('Error: ', ']')
|
|
104
|
-
};
|
|
105
|
-
resolve(0);
|
|
106
|
-
} else {
|
|
107
|
-
_this.error = undefined;
|
|
108
|
-
resolve(1);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* @description 获取数据库管理器
|
|
116
|
-
*/
|
|
117
|
-
this.db = function() {
|
|
118
|
-
return new DB($this);
|
|
119
|
-
};
|
|
120
|
-
}
|
|
2
|
+
const DB = require('./db');
|
|
3
|
+
const { BaseService } = require('mm_base_service');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SQLite数据库操作类
|
|
7
|
+
* @class Sqlite
|
|
8
|
+
* @extends BaseService
|
|
9
|
+
*/
|
|
10
|
+
class Sqlite extends BaseService {
|
|
11
|
+
/**
|
|
12
|
+
* 默认配置
|
|
13
|
+
*/
|
|
14
|
+
static default_config = {
|
|
15
|
+
host: "/db/".fullname(),
|
|
16
|
+
port: 3306,
|
|
17
|
+
user: "root",
|
|
18
|
+
password: "",
|
|
19
|
+
database: "mm",
|
|
20
|
+
charset: "utf8mb4",
|
|
21
|
+
timezone: "+08:00",
|
|
22
|
+
connect_timeout: 20000,
|
|
23
|
+
acquire_timeout: 20000,
|
|
24
|
+
query_timeout: 20000,
|
|
25
|
+
connection_limit: 1,
|
|
26
|
+
queue_limit: 0,
|
|
27
|
+
enable_keep_alive: true,
|
|
28
|
+
keep_alive_initial_delay: 10000,
|
|
29
|
+
enable_reconnect: true,
|
|
30
|
+
reconnect_interval: 1000,
|
|
31
|
+
max_reconnect_attempts: 5,
|
|
32
|
+
wait_for_connections: true
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 构造函数
|
|
37
|
+
* @param {Object} config - 配置对象
|
|
38
|
+
*/
|
|
39
|
+
constructor(config) {
|
|
40
|
+
const merged_config = Object.assign({}, Sqlite.default_config, config || {});
|
|
41
|
+
super(merged_config);
|
|
42
|
+
|
|
43
|
+
this.config = merged_config;
|
|
44
|
+
this._connection = null;
|
|
45
|
+
this._pool = null;
|
|
46
|
+
this._status = 'closed';
|
|
47
|
+
this._last_connect_time = 0;
|
|
48
|
+
this._reconnecting = false;
|
|
49
|
+
this._is_inited = false;
|
|
50
|
+
this._is_destroyed = false;
|
|
51
|
+
this._db = null;
|
|
52
|
+
this._open = false;
|
|
53
|
+
this._conn_retry_count = 0;
|
|
54
|
+
}
|
|
121
55
|
}
|
|
122
56
|
|
|
123
57
|
/**
|
|
124
|
-
*
|
|
125
|
-
* @
|
|
58
|
+
* 初始化服务
|
|
59
|
+
* @returns {Promise<void>}
|
|
126
60
|
*/
|
|
127
|
-
Sqlite.prototype.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
61
|
+
Sqlite.prototype._initService = async function() {
|
|
62
|
+
if (this._is_inited) {
|
|
63
|
+
$.log.warn('SQLite服务已初始化');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
$.log.debug('[Sqlite] [_initService]', '初始化SQLite服务', { config: this.config });
|
|
69
|
+
this._is_inited = true;
|
|
70
|
+
$.log.debug('[Sqlite] [_initService]', 'SQLite服务初始化完成');
|
|
71
|
+
} catch (error) {
|
|
72
|
+
$.log.error('[Sqlite] [_initService]', 'SQLite服务初始化失败', { error: error.message });
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 打开数据库连接
|
|
79
|
+
* @param {Number} timeout - 超时时间(毫秒)
|
|
80
|
+
* @returns {Promise<boolean>}
|
|
81
|
+
*/
|
|
82
|
+
Sqlite.prototype.open = async function(timeout) {
|
|
83
|
+
if (this._status === 'connected' || this._status === 'connecting') {
|
|
84
|
+
$.log.warn('数据库连接已存在或正在连接中');
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
timeout = timeout || this.config.connect_timeout || 10000;
|
|
89
|
+
this._status = 'connecting';
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
// $.log.debug(`[${this.constructor.name}] [open]`, '开始创建数据库连接', {
|
|
93
|
+
// host: this.config.host,
|
|
94
|
+
// port: this.config.port,
|
|
95
|
+
// database: this.config.database,
|
|
96
|
+
// user: this.config.user,
|
|
97
|
+
// timeout: timeout
|
|
98
|
+
// });
|
|
99
|
+
|
|
100
|
+
// 创建连接
|
|
101
|
+
const connection_promise = this._openInternal();
|
|
102
|
+
const timeout_promise = new Promise((_, reject) => {
|
|
103
|
+
setTimeout(() => {
|
|
104
|
+
reject(new Error(`数据库连接超时(${timeout}ms)`));
|
|
105
|
+
}, timeout);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
await Promise.race([connection_promise, timeout_promise]);
|
|
110
|
+
|
|
111
|
+
this._last_connect_time = Date.now();
|
|
112
|
+
this._status = 'connected';
|
|
113
|
+
$.log.info(`[${this.constructor.name}] [open]`, '数据库连接成功', {
|
|
114
|
+
host: this.config.host,
|
|
115
|
+
port: this.config.port,
|
|
116
|
+
database: this.config.database
|
|
117
|
+
});
|
|
118
|
+
return true;
|
|
119
|
+
} catch (connection_err) {
|
|
120
|
+
$.log.error(`[${this.constructor.name}] [open]`, '连接过程错误详情', {
|
|
121
|
+
error: connection_err.message,
|
|
122
|
+
code: connection_err.code,
|
|
123
|
+
syscall: connection_err.syscall,
|
|
124
|
+
address: connection_err.address,
|
|
125
|
+
port: connection_err.port,
|
|
126
|
+
stack: connection_err.stack
|
|
127
|
+
});
|
|
128
|
+
throw connection_err;
|
|
129
|
+
}
|
|
130
|
+
} catch (err) {
|
|
131
|
+
this._status = 'closed';
|
|
132
|
+
$.log.error(`[${this.constructor.name}] [open]`, '数据库连接失败', {
|
|
133
|
+
error: err.message,
|
|
134
|
+
host: this.config.host,
|
|
135
|
+
port: this.config.port,
|
|
136
|
+
database: this.config.database
|
|
137
|
+
});
|
|
138
|
+
throw err;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 内部连接方法
|
|
144
|
+
* @private
|
|
145
|
+
* @returns {Promise<void>}
|
|
146
|
+
*/
|
|
147
|
+
Sqlite.prototype._openInternal = function() {
|
|
148
|
+
return new Promise((resolve, reject) => {
|
|
149
|
+
try {
|
|
150
|
+
const fs = require('fs');
|
|
151
|
+
const path = require('path');
|
|
152
|
+
|
|
153
|
+
// 确保数据库目录存在
|
|
154
|
+
const db_path = this._getDatabasePath();
|
|
155
|
+
const dir = path.dirname(db_path);
|
|
156
|
+
|
|
157
|
+
if (!fs.existsSync(dir)) {
|
|
158
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 创建数据库连接
|
|
162
|
+
this._db = new sqlite3.Database(db_path);
|
|
163
|
+
this._open = true;
|
|
164
|
+
this._conn_retry_count = 0;
|
|
165
|
+
|
|
166
|
+
// 设置连接错误处理
|
|
167
|
+
this._db.on('error', (err) => {
|
|
168
|
+
$.log.error('SQLite数据库连接错误:', err);
|
|
169
|
+
this._handleConnectionError(err);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
$.log.debug(`SQLite数据库已打开: ${db_path}`);
|
|
173
|
+
resolve();
|
|
174
|
+
} catch (error) {
|
|
175
|
+
reject(error);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
158
178
|
};
|
|
159
179
|
|
|
160
180
|
/**
|
|
161
|
-
*
|
|
181
|
+
* 获取数据库路径
|
|
182
|
+
* @private
|
|
183
|
+
* @returns {String}
|
|
184
|
+
*/
|
|
185
|
+
Sqlite.prototype._getDatabasePath = function() {
|
|
186
|
+
const db_name = this.config.database || 'mm';
|
|
187
|
+
const db_dir = this.config.host || '/db/';
|
|
188
|
+
return `${db_dir}${db_name}.db`;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* 关闭数据库连接
|
|
193
|
+
* @returns {Promise<boolean>}
|
|
162
194
|
*/
|
|
163
195
|
Sqlite.prototype.close = function() {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
196
|
+
if (this._status !== 'connected') {
|
|
197
|
+
$.log.warn('数据库连接未建立');
|
|
198
|
+
return Promise.resolve(false);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return new Promise((resolve) => {
|
|
202
|
+
try {
|
|
203
|
+
if (this._db) {
|
|
204
|
+
$.log.debug('关闭数据库连接');
|
|
205
|
+
this._db.close((err) => {
|
|
206
|
+
if (err) {
|
|
207
|
+
$.log.error('关闭数据库失败', { error: err.message });
|
|
208
|
+
} else {
|
|
209
|
+
$.log.info('数据库已关闭');
|
|
210
|
+
}
|
|
211
|
+
this._db = null;
|
|
212
|
+
this._open = false;
|
|
213
|
+
this._status = 'closed';
|
|
214
|
+
resolve(!err);
|
|
215
|
+
});
|
|
216
|
+
} else {
|
|
217
|
+
this._status = 'closed';
|
|
218
|
+
resolve(true);
|
|
219
|
+
}
|
|
220
|
+
} catch (err) {
|
|
221
|
+
$.log.error('关闭数据库连接时发生异常', { error: err.message });
|
|
222
|
+
this._status = 'closed';
|
|
223
|
+
resolve(false);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
168
226
|
};
|
|
169
227
|
|
|
170
228
|
/**
|
|
171
|
-
*
|
|
172
|
-
* @param {
|
|
173
|
-
* @
|
|
174
|
-
* @param {Boolean} clear_prefix 清除前缀
|
|
175
|
-
* @param {Array} arr_table 关联的数据表
|
|
176
|
-
* @return {Object} 管理模型
|
|
229
|
+
* 获取数据库连接
|
|
230
|
+
* @param {Number} timeout - 超时时间(毫秒)
|
|
231
|
+
* @returns {Promise<Object>}
|
|
177
232
|
*/
|
|
178
|
-
Sqlite.prototype.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
233
|
+
Sqlite.prototype.getConn = async function(timeout) {
|
|
234
|
+
timeout = timeout || this.config.acquire_timeout || this.config.connect_timeout || 20000;
|
|
235
|
+
|
|
236
|
+
// $.log.debug(`[${this.constructor.name}] [getConn] 使用超时时间: ${timeout}ms`);
|
|
237
|
+
|
|
238
|
+
if (this._status !== 'connected') {
|
|
239
|
+
throw new Error('数据库连接未建立');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
const connection_promise = new Promise((resolve) => {
|
|
244
|
+
resolve(this._db);
|
|
245
|
+
});
|
|
246
|
+
const timeout_promise = new Promise((_, reject) => {
|
|
247
|
+
setTimeout(() => {
|
|
248
|
+
reject(new Error(`获取数据库连接超时(${timeout}ms)`));
|
|
249
|
+
}, timeout);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
const conn = await Promise.race([connection_promise, timeout_promise]);
|
|
253
|
+
|
|
254
|
+
// $.log.debug(`[${this.constructor.name}] [getConn] 成功获取数据库连接`);
|
|
255
|
+
|
|
256
|
+
// 处理连接错误
|
|
257
|
+
conn.on('error', (err) => {
|
|
258
|
+
if (err.code && (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT')) {
|
|
259
|
+
this._handleConnectionError(err);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
return conn;
|
|
264
|
+
} catch (error) {
|
|
265
|
+
$.log.error(`[${this.constructor.name}] [getConn] 获取连接失败`, {
|
|
266
|
+
error: error.message
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
if (error.code && (error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT')) {
|
|
270
|
+
this._handleConnectionError(error);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
189
275
|
};
|
|
190
276
|
|
|
191
277
|
/**
|
|
192
|
-
*
|
|
278
|
+
* 处理连接错误
|
|
279
|
+
* @private
|
|
280
|
+
* @param {Error} err - 错误对象
|
|
193
281
|
*/
|
|
194
|
-
|
|
282
|
+
Sqlite.prototype._handleConnectionError = function(err) {
|
|
283
|
+
if (this._reconnecting) {
|
|
284
|
+
$.log.debug('重连已在进行中');
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (this.config.enable_reconnect) {
|
|
289
|
+
$.log.info('开始自动重连...');
|
|
290
|
+
this._reconnect(this.config.max_reconnect_attempts || 3)
|
|
291
|
+
.catch(reconnect_err => {
|
|
292
|
+
$.log.error('重连失败', { error: reconnect_err.message });
|
|
293
|
+
this._status = 'closed';
|
|
294
|
+
});
|
|
295
|
+
} else {
|
|
296
|
+
$.log.warn('自动重连已禁用');
|
|
297
|
+
this._status = 'closed';
|
|
298
|
+
}
|
|
299
|
+
};
|
|
195
300
|
|
|
196
301
|
/**
|
|
197
|
-
*
|
|
302
|
+
* 重新连接数据库
|
|
303
|
+
* @private
|
|
304
|
+
* @param {Number} max_retries - 最大重试次数
|
|
305
|
+
* @returns {Promise<boolean>}
|
|
198
306
|
*/
|
|
307
|
+
Sqlite.prototype._reconnect = async function(max_retries) {
|
|
308
|
+
if (this._reconnecting) {
|
|
309
|
+
$.log.warn('重连已在进行中');
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
this._reconnecting = true;
|
|
314
|
+
let retry_count = 0;
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
// 先关闭现有连接
|
|
318
|
+
if (this._status === 'connected') {
|
|
319
|
+
await this.close();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
while (retry_count < max_retries) {
|
|
323
|
+
retry_count++;
|
|
324
|
+
$.log.info(`第${retry_count}/${max_retries}次尝试重连`);
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
const reconnect_timeout = this.config.connect_timeout || this.config.acquire_timeout || 20000;
|
|
328
|
+
$.log.debug(`重连超时设置为: ${reconnect_timeout}ms`);
|
|
329
|
+
await this.open(reconnect_timeout);
|
|
330
|
+
$.log.info('重连成功');
|
|
331
|
+
return true;
|
|
332
|
+
} catch (err) {
|
|
333
|
+
$.log.error(`重连失败(${retry_count}/${max_retries})`, { error: err.message });
|
|
334
|
+
|
|
335
|
+
if (retry_count < max_retries) {
|
|
336
|
+
const wait_time = this.config.reconnect_interval || 1000;
|
|
337
|
+
$.log.debug(`等待${wait_time}ms后重试`);
|
|
338
|
+
await this._sleep(wait_time);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
$.log.error(`达到最大重试次数(${max_retries}),重连失败`);
|
|
344
|
+
throw new Error(`重连失败:达到最大重试次数(${max_retries})`);
|
|
345
|
+
} finally {
|
|
346
|
+
this._reconnecting = false;
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* 休眠函数
|
|
352
|
+
* @private
|
|
353
|
+
* @param {Number} ms - 休眠时间(毫秒)
|
|
354
|
+
* @returns {Promise<void>}
|
|
355
|
+
*/
|
|
356
|
+
Sqlite.prototype._sleep = function(ms) {
|
|
357
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* 执行SQL查询
|
|
362
|
+
* @param {String} sql - SQL语句
|
|
363
|
+
* @param {Array} params - 参数数组
|
|
364
|
+
* @param {Number} timeout - 超时时间(毫秒)
|
|
365
|
+
* @returns {Promise<Object>}
|
|
366
|
+
*/
|
|
367
|
+
Sqlite.prototype.run = async function(sql, params, timeout) {
|
|
368
|
+
let conn = null;
|
|
369
|
+
timeout = timeout || this.config.query_timeout || 30000;
|
|
370
|
+
|
|
371
|
+
try {
|
|
372
|
+
// 获取连接
|
|
373
|
+
conn = await this.getConn(timeout);
|
|
374
|
+
|
|
375
|
+
// 直接在方法内部实现超时控制
|
|
376
|
+
const query_promise = new Promise((resolve, reject) => {
|
|
377
|
+
conn.all(sql, params || [], (error, rows) => {
|
|
378
|
+
if (error) {
|
|
379
|
+
reject(error);
|
|
380
|
+
} else {
|
|
381
|
+
// 保持与MySQL兼容的返回值格式
|
|
382
|
+
// 如果是SELECT查询且只有一行结果,返回第一行
|
|
383
|
+
// 否则返回完整结果数组
|
|
384
|
+
if (sql.trim().toUpperCase().startsWith('SELECT') && rows.length === 1) {
|
|
385
|
+
resolve(rows[0]);
|
|
386
|
+
} else {
|
|
387
|
+
resolve(rows);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
const timeout_promise = new Promise((_, reject) => {
|
|
394
|
+
setTimeout(() => {
|
|
395
|
+
reject(new Error(`SQL查询超时: ${timeout}ms`));
|
|
396
|
+
}, timeout);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const result = await Promise.race([query_promise, timeout_promise]);
|
|
400
|
+
|
|
401
|
+
return result;
|
|
402
|
+
} catch (err) {
|
|
403
|
+
$.log.error('[Sqlite] [run]', 'SQL执行失败', {
|
|
404
|
+
error: err.message,
|
|
405
|
+
sql: typeof sql === 'string' ? sql.substring(0, 200) : sql,
|
|
406
|
+
params: params
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// 处理连接错误,触发重连
|
|
410
|
+
if (err.code && (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT')) {
|
|
411
|
+
this._handleConnectionError(err);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
throw err;
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* 执行SQL语句(用于执行非查询语句如INSERT/UPDATE/DELETE)
|
|
420
|
+
* @param {String} sql - SQL语句
|
|
421
|
+
* @param {Array} params - 参数数组
|
|
422
|
+
* @param {Number} timeout - 超时时间(毫秒)
|
|
423
|
+
* @returns {Promise<Object>}
|
|
424
|
+
*/
|
|
425
|
+
Sqlite.prototype.exec = async function(sql, params, timeout) {
|
|
426
|
+
let conn = null;
|
|
427
|
+
timeout = timeout || this.config.query_timeout || 30000;
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
// 获取连接
|
|
431
|
+
conn = await this.getConn(timeout);
|
|
432
|
+
|
|
433
|
+
// 直接在方法内部实现超时控制
|
|
434
|
+
const query_promise = new Promise((resolve, reject) => {
|
|
435
|
+
conn.run(sql, params || [], function(error) {
|
|
436
|
+
if (error) {
|
|
437
|
+
reject(error);
|
|
438
|
+
} else {
|
|
439
|
+
// 保持与MySQL兼容的返回值格式
|
|
440
|
+
resolve({
|
|
441
|
+
affected_rows: this.changes || 0,
|
|
442
|
+
insert_id: this.lastID || 0,
|
|
443
|
+
changed_rows: this.changes || 0
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
const timeout_promise = new Promise((_, reject) => {
|
|
450
|
+
setTimeout(() => {
|
|
451
|
+
reject(new Error(`SQL执行超时: ${timeout}ms`));
|
|
452
|
+
}, timeout);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
const result = await Promise.race([query_promise, timeout_promise]);
|
|
456
|
+
|
|
457
|
+
return result;
|
|
458
|
+
} catch (err) {
|
|
459
|
+
$.log.error('[Sqlite] [exec]', 'SQL执行失败', {
|
|
460
|
+
error: err.message,
|
|
461
|
+
sql: sql.substring(0, 200),
|
|
462
|
+
params: params
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// 处理连接错误,触发重连
|
|
466
|
+
if (err.code && (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT')) {
|
|
467
|
+
this._handleConnectionError(err);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
throw err;
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* 读取整张表的数据
|
|
476
|
+
* @param {String} table - 表名
|
|
477
|
+
* @param {Object} condition - 查询条件
|
|
478
|
+
* @param {Object} options - 选项(order_by, limit, offset等)
|
|
479
|
+
* @returns {Promise<Array>}
|
|
480
|
+
*/
|
|
481
|
+
Sqlite.prototype.read = async function(table, condition, options) {
|
|
482
|
+
try {
|
|
483
|
+
// 检查连接状态
|
|
484
|
+
if (this._status !== 'connected') {
|
|
485
|
+
throw new Error('数据库连接未建立');
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// 构建基础SQL查询
|
|
489
|
+
let sql = `SELECT * FROM ${table}`;
|
|
490
|
+
const params = [];
|
|
491
|
+
|
|
492
|
+
// 处理条件
|
|
493
|
+
if (condition && Object.keys(condition).length > 0) {
|
|
494
|
+
const where_clauses = [];
|
|
495
|
+
for (const [field, value] of Object.entries(condition)) {
|
|
496
|
+
where_clauses.push(`${field} = ?`);
|
|
497
|
+
params.push(value);
|
|
498
|
+
}
|
|
499
|
+
sql += ` WHERE ${where_clauses.join(' AND ')}`;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// 处理排序
|
|
503
|
+
if (options && options.order_by) {
|
|
504
|
+
sql += ` ORDER BY ${options.order_by}`;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// 处理分页
|
|
508
|
+
if (options && options.limit) {
|
|
509
|
+
sql += ` LIMIT ${options.limit}`;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (options && options.offset) {
|
|
513
|
+
sql += ` OFFSET ${options.offset}`;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// 记录查询日志
|
|
517
|
+
if (this.config.debug) {
|
|
518
|
+
$.log.debug(`[${this.constructor.name}] [read] 查询数据`, { sql, params });
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// 执行查询
|
|
522
|
+
const results = await this.run(sql, params);
|
|
523
|
+
|
|
524
|
+
if (this.config.debug) {
|
|
525
|
+
$.log.info(`[${this.constructor.name}] [read] 查询成功`, { count: Array.isArray(results) ? results.length : 1 });
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// 确保返回数组格式
|
|
529
|
+
return Array.isArray(results) ? results : [results];
|
|
530
|
+
} catch (error) {
|
|
531
|
+
// 记录错误日志
|
|
532
|
+
$.log.error(`[${this.constructor.name}] [read] 查询失败`, {
|
|
533
|
+
error: error.message,
|
|
534
|
+
table: table,
|
|
535
|
+
condition: condition
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
// 抛出错误
|
|
539
|
+
throw error;
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* 获取数据库操作实例
|
|
545
|
+
* @param {String} database - 数据库名称
|
|
546
|
+
* @returns {Object} 数据库操作实例
|
|
547
|
+
*/
|
|
548
|
+
Sqlite.prototype.db = function(database) {
|
|
549
|
+
if (this._status !== 'connected') {
|
|
550
|
+
throw new Error('数据库连接未建立');
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// 优化:缓存DB实例,避免重复创建
|
|
554
|
+
if (!this._db_instance) {
|
|
555
|
+
this._db_instance = new DB(this);
|
|
556
|
+
|
|
557
|
+
// 为DB实例添加必要的方法,确保与MySQL接口兼容
|
|
558
|
+
this._db_instance.run = this.run.bind(this);
|
|
559
|
+
this._db_instance.exec = this.exec.bind(this);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (database) {
|
|
563
|
+
this._db_instance.database = database;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return this._db_instance;
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* 获取数据库管理器模型
|
|
571
|
+
* @returns {Object} 数据库管理器模型
|
|
572
|
+
*/
|
|
573
|
+
Sqlite.prototype.dbs = function() {
|
|
574
|
+
return this.db();
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* 开始事务
|
|
579
|
+
* @returns {Promise<Object>} 事务连接对象
|
|
580
|
+
*/
|
|
581
|
+
Sqlite.prototype.beginTransaction = async function() {
|
|
582
|
+
try {
|
|
583
|
+
// 检查连接状态
|
|
584
|
+
if (this._status !== 'connected') {
|
|
585
|
+
throw new Error('数据库连接未建立');
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// 获取连接
|
|
589
|
+
const connection = await this.getConn();
|
|
590
|
+
|
|
591
|
+
// 开始事务
|
|
592
|
+
await new Promise((resolve, reject) => {
|
|
593
|
+
connection.run('BEGIN TRANSACTION', (error) => {
|
|
594
|
+
if (error) {
|
|
595
|
+
reject(error);
|
|
596
|
+
} else {
|
|
597
|
+
resolve();
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
if (this.config.debug) {
|
|
603
|
+
$.log.debug(`[${this.constructor.name}] [beginTransaction] 事务开始`);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// 返回事务连接对象,包含提交和回滚方法
|
|
607
|
+
return {
|
|
608
|
+
connection,
|
|
609
|
+
commit: async () => {
|
|
610
|
+
await new Promise((resolve, reject) => {
|
|
611
|
+
connection.run('COMMIT', (error) => {
|
|
612
|
+
if (error) {
|
|
613
|
+
reject(error);
|
|
614
|
+
} else {
|
|
615
|
+
resolve();
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
if (this.config.debug) {
|
|
620
|
+
$.log.debug(`[${this.constructor.name}] [beginTransaction] 事务提交`);
|
|
621
|
+
}
|
|
622
|
+
},
|
|
623
|
+
rollback: async () => {
|
|
624
|
+
await new Promise((resolve, reject) => {
|
|
625
|
+
connection.run('ROLLBACK', (error) => {
|
|
626
|
+
if (error) {
|
|
627
|
+
reject(error);
|
|
628
|
+
} else {
|
|
629
|
+
resolve();
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
});
|
|
633
|
+
if (this.config.debug) {
|
|
634
|
+
$.log.debug(`[${this.constructor.name}] [beginTransaction] 事务回滚`);
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
exec: async (sql, params) => {
|
|
638
|
+
// 在事务中执行SQL
|
|
639
|
+
return new Promise((resolve, reject) => {
|
|
640
|
+
connection.run(sql, params || [], function(error) {
|
|
641
|
+
if (error) {
|
|
642
|
+
reject(error);
|
|
643
|
+
} else {
|
|
644
|
+
resolve({
|
|
645
|
+
affected_rows: this.changes,
|
|
646
|
+
insert_id: this.lastID,
|
|
647
|
+
changed_rows: this.changes,
|
|
648
|
+
last_id: this.lastID,
|
|
649
|
+
changes: this.changes
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
} catch (error) {
|
|
657
|
+
$.log.error(`[${this.constructor.name}] [beginTransaction] 事务开始失败`, { error: error.message });
|
|
658
|
+
throw error;
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* 在事务中执行多个操作
|
|
664
|
+
* @param {Function} callback - 包含事务操作的回调函数
|
|
665
|
+
* @returns {Promise<*>} 回调函数的返回值
|
|
666
|
+
*/
|
|
667
|
+
Sqlite.prototype.transaction = async function(callback) {
|
|
668
|
+
let transaction = null;
|
|
669
|
+
|
|
670
|
+
try {
|
|
671
|
+
// 开始事务
|
|
672
|
+
transaction = await this.beginTransaction();
|
|
673
|
+
|
|
674
|
+
// 执行回调函数,传入事务对象
|
|
675
|
+
const result = await callback(transaction);
|
|
676
|
+
|
|
677
|
+
// 提交事务
|
|
678
|
+
await transaction.commit();
|
|
679
|
+
|
|
680
|
+
return result;
|
|
681
|
+
} catch (error) {
|
|
682
|
+
// 如果有事务,回滚
|
|
683
|
+
if (transaction) {
|
|
684
|
+
await transaction.rollback().catch(err => {
|
|
685
|
+
$.log.error(`[${this.constructor.name}] [transaction] 事务回滚失败`, { error: err.message });
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
$.log.error(`[${this.constructor.name}] [transaction] 事务执行失败`, { error: error.message });
|
|
690
|
+
throw error;
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* 确保连接池对象存在
|
|
696
|
+
*/
|
|
697
|
+
if (!$.pool) {
|
|
698
|
+
$.pool = {};
|
|
699
|
+
}
|
|
199
700
|
if (!$.pool.sqlite) {
|
|
200
701
|
$.pool.sqlite = {};
|
|
201
702
|
}
|
|
202
703
|
|
|
203
704
|
/**
|
|
204
|
-
*
|
|
705
|
+
* Sqlite管理器,用于创建缓存
|
|
205
706
|
* @param {String} scope 作用域
|
|
206
|
-
* @param {
|
|
207
|
-
* @return {Object}
|
|
707
|
+
* @param {Object} config 配置参数
|
|
708
|
+
* @return {Object} 返回一个Sqlite类实例
|
|
208
709
|
*/
|
|
209
|
-
function
|
|
710
|
+
function sqliteAdmin(scope, config) {
|
|
210
711
|
if (!scope) {
|
|
211
|
-
scope =
|
|
712
|
+
scope = 'sys';
|
|
212
713
|
}
|
|
213
714
|
var obj = $.pool.sqlite[scope];
|
|
214
715
|
if (!obj) {
|
|
215
|
-
$.pool.sqlite[scope] = new Sqlite(
|
|
716
|
+
$.pool.sqlite[scope] = new Sqlite(config);
|
|
216
717
|
obj = $.pool.sqlite[scope];
|
|
217
718
|
}
|
|
218
719
|
return obj;
|
|
219
720
|
}
|
|
220
721
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
exports.sqlite_admin = sqlite_admin;
|
|
722
|
+
// 模块导出
|
|
723
|
+
exports.Sqlite = Sqlite;
|
|
724
|
+
exports.sqliteAdmin = sqliteAdmin;
|