mm_sqlite 1.4.8 → 1.5.0
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 +0 -13
- package/index.js +109 -120
- package/link_model.js +3 -3
- package/package.json +6 -6
- package/pool.js +31 -38
- package/sql.js +88 -73
- package/test_db/test_basic.db +0 -0
- package/test_db/test_core_methods.db +0 -0
package/db.js
CHANGED
|
@@ -672,19 +672,6 @@ DB.prototype.renameTable = function (table, new_table, timeout = 15000) {
|
|
|
672
672
|
return this.exec('ALTER TABLE `' + table + '` RENAME TO `' + new_table + '`;', [], timeout);
|
|
673
673
|
};
|
|
674
674
|
|
|
675
|
-
/**
|
|
676
|
-
* @description 清空数据表(保留表结构)
|
|
677
|
-
* @param {string} table 表名
|
|
678
|
-
* @param {number} timeout - 超时时间(毫秒)
|
|
679
|
-
* @returns {Promise | number} 清空成功返回1,失败返回0
|
|
680
|
-
*/
|
|
681
|
-
DB.prototype.emptyTable = function (table, timeout = 15000) {
|
|
682
|
-
if (!table) {
|
|
683
|
-
throw new TypeError('table must be specified');
|
|
684
|
-
}
|
|
685
|
-
return this.exec('DELETE FROM `' + table + '`;', [], timeout);
|
|
686
|
-
};
|
|
687
|
-
|
|
688
675
|
/**
|
|
689
676
|
* @description 检查表是否存在
|
|
690
677
|
* @param {string} table 表名
|
package/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const sqlite3 = require('sqlite3').verbose();
|
|
2
1
|
const {
|
|
3
2
|
DB
|
|
4
3
|
} = require('./db');
|
|
@@ -112,26 +111,24 @@ SQLite.prototype.open = async function (timeout) {
|
|
|
112
111
|
* 关闭数据库连接(统一使用连接池模式)
|
|
113
112
|
* @returns {Promise<boolean>}
|
|
114
113
|
*/
|
|
115
|
-
SQLite.prototype.close = function () {
|
|
114
|
+
SQLite.prototype.close = async function () {
|
|
116
115
|
if (this._status !== 'connected') {
|
|
117
116
|
this.log('warn', '数据库连接未建立');
|
|
118
|
-
return
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
});
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
// 统一关闭连接池
|
|
122
|
+
this._pool.close();
|
|
123
|
+
this._db = null;
|
|
124
|
+
this._open = false;
|
|
125
|
+
this._status = 'closed';
|
|
126
|
+
return true;
|
|
127
|
+
} catch (err) {
|
|
128
|
+
this.log('error', '关闭数据库连接时发生异常', err);
|
|
129
|
+
this._status = 'closed';
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
135
132
|
};
|
|
136
133
|
|
|
137
134
|
/**
|
|
@@ -184,40 +181,19 @@ SQLite.prototype.run = async function (sql, params = [], timeout = 30000) {
|
|
|
184
181
|
try {
|
|
185
182
|
// 获取连接
|
|
186
183
|
conn = await this.getConn(query_timeout);
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
timeout_id = setTimeout(() => {
|
|
192
|
-
reject(new Error(`SQL查询超时: ${timeout}ms`));
|
|
193
|
-
}, timeout);
|
|
194
|
-
|
|
195
|
-
// 执行SQL查询
|
|
196
|
-
conn.all(sql, params || [], (error, rows) => {
|
|
197
|
-
// 清除超时定时器
|
|
198
|
-
if (timeout_id) {
|
|
199
|
-
clearTimeout(timeout_id);
|
|
200
|
-
timeout_id = null;
|
|
201
|
-
}
|
|
202
|
-
if (error) {
|
|
203
|
-
reject(error);
|
|
204
|
-
resolve([]);
|
|
205
|
-
} else {
|
|
206
|
-
// 保持与MySQL兼容的返回值格式,始终返回数组
|
|
207
|
-
resolve(rows);
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
});
|
|
184
|
+
|
|
185
|
+
// better-sqlite3 同步查询,无需 Promise 包装
|
|
186
|
+
const stmt = conn.prepare(sql);
|
|
187
|
+
ret = params.length > 0 ? stmt.all(params) : stmt.all();
|
|
211
188
|
} catch (error) {
|
|
212
189
|
console.error('SQL执行失败', error);
|
|
213
190
|
this.sql = sql;
|
|
214
191
|
this.error = {
|
|
215
|
-
code: error.errno,
|
|
192
|
+
code: error.code || error.errno,
|
|
216
193
|
message: error.message
|
|
217
194
|
};
|
|
218
195
|
ret = [];
|
|
219
|
-
}
|
|
220
|
-
finally {
|
|
196
|
+
} finally {
|
|
221
197
|
await releaseConn(conn);
|
|
222
198
|
}
|
|
223
199
|
return ret;
|
|
@@ -231,7 +207,7 @@ SQLite.prototype.run = async function (sql, params = [], timeout = 30000) {
|
|
|
231
207
|
* @returns {Promise<object>}
|
|
232
208
|
* @throws {TypeError} 当sql参数无效时
|
|
233
209
|
*/
|
|
234
|
-
SQLite.prototype.exec = async function (sql, params, timeout) {
|
|
210
|
+
SQLite.prototype.exec = async function (sql, params = [], timeout) {
|
|
235
211
|
// 参数校验
|
|
236
212
|
if (typeof sql !== 'string' || sql.trim() === '') {
|
|
237
213
|
throw new TypeError('sql must be non-empty string');
|
|
@@ -252,40 +228,28 @@ SQLite.prototype.exec = async function (sql, params, timeout) {
|
|
|
252
228
|
// 获取连接
|
|
253
229
|
conn = await this.getConn(timeout);
|
|
254
230
|
this.sql = sql;
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
conn.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
timeout_id = null;
|
|
269
|
-
}
|
|
270
|
-
if (error) {
|
|
271
|
-
reject(error);
|
|
272
|
-
} else {
|
|
273
|
-
resolve(this.lastID || this.changes || 1);
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
});
|
|
231
|
+
|
|
232
|
+
const trimmed = sql.trim().toUpperCase().replace(/;+$/, '');
|
|
233
|
+
let info;
|
|
234
|
+
// 事务命令不能作为预编译语句,需用 exec() 直接执行
|
|
235
|
+
if (trimmed === 'BEGIN' || trimmed === 'BEGIN TRANSACTION' ||
|
|
236
|
+
trimmed === 'COMMIT' || trimmed === 'ROLLBACK') {
|
|
237
|
+
conn.exec(sql);
|
|
238
|
+
info = { changes: 0, lastInsertRowid: 0 };
|
|
239
|
+
} else {
|
|
240
|
+
const stmt = conn.prepare(sql);
|
|
241
|
+
info = params && params.length > 0 ? stmt.run(params) : stmt.run();
|
|
242
|
+
}
|
|
243
|
+
ret = info.lastInsertRowid || info.changes || 1;
|
|
277
244
|
} catch (error) {
|
|
278
245
|
console.error('SQL执行失败', error);
|
|
279
246
|
this.sql = sql;
|
|
280
247
|
this.error = {
|
|
281
|
-
code: error.errno,
|
|
248
|
+
code: error.code || error.errno,
|
|
282
249
|
message: error.message
|
|
283
250
|
};
|
|
284
|
-
// 简化错误处理:SQLite不需要复杂的连接错误处理
|
|
285
|
-
// 返回0,保持与MySQL一致的错误处理行为
|
|
286
251
|
ret = 0;
|
|
287
|
-
}
|
|
288
|
-
finally {
|
|
252
|
+
} finally {
|
|
289
253
|
await releaseConn(conn);
|
|
290
254
|
}
|
|
291
255
|
return ret;
|
|
@@ -299,7 +263,7 @@ SQLite.prototype.exec = async function (sql, params, timeout) {
|
|
|
299
263
|
* @returns {Promise<Array>}
|
|
300
264
|
* @throws {TypeError} 当table参数无效时
|
|
301
265
|
*/
|
|
302
|
-
SQLite.prototype.read = async function (table, condition, options) {
|
|
266
|
+
SQLite.prototype.read = async function (table, condition = {}, options = {}) {
|
|
303
267
|
// 参数校验
|
|
304
268
|
if (typeof table !== 'string' || table.trim() === '') {
|
|
305
269
|
throw new TypeError('table must be non-empty string');
|
|
@@ -368,6 +332,69 @@ SQLite.prototype.db = function () {
|
|
|
368
332
|
return new DB(this);
|
|
369
333
|
};
|
|
370
334
|
|
|
335
|
+
/**
|
|
336
|
+
* 初始化数据库服务(保持兼容性)
|
|
337
|
+
* @returns {Promise<void>}
|
|
338
|
+
*/
|
|
339
|
+
SQLite.prototype.init = async function () {
|
|
340
|
+
await this.open();
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* 获取连接池状态信息
|
|
345
|
+
* @returns {object} 连接池状态对象
|
|
346
|
+
*/
|
|
347
|
+
SQLite.prototype.getPoolStats = function () {
|
|
348
|
+
if (!this._pool || !this._pool._is_initialized) {
|
|
349
|
+
return {
|
|
350
|
+
status: 'not_initialized',
|
|
351
|
+
message: '连接池未初始化'
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return {
|
|
356
|
+
status: 'healthy',
|
|
357
|
+
...this._pool.getStats()
|
|
358
|
+
};
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* 健康检查
|
|
363
|
+
* @returns {Promise<object>} 健康检查结果
|
|
364
|
+
*/
|
|
365
|
+
SQLite.prototype.healthCheck = async function () {
|
|
366
|
+
try {
|
|
367
|
+
if (this._status !== 'connected') {
|
|
368
|
+
return {
|
|
369
|
+
status: 'error',
|
|
370
|
+
message: '数据库连接未初始化'
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// 执行简单的查询来检查连接状态
|
|
375
|
+
let result = await this.run('SELECT 1 as health_check');
|
|
376
|
+
|
|
377
|
+
if (result && result.length > 0 && result[0].health_check === 1) {
|
|
378
|
+
return {
|
|
379
|
+
status: 'healthy',
|
|
380
|
+
message: '数据库连接正常',
|
|
381
|
+
...this.getPoolStats()
|
|
382
|
+
};
|
|
383
|
+
} else {
|
|
384
|
+
return {
|
|
385
|
+
status: 'error',
|
|
386
|
+
message: '健康检查查询失败'
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
} catch (error) {
|
|
390
|
+
this.log('error', '健康检查失败', error);
|
|
391
|
+
return {
|
|
392
|
+
status: 'error',
|
|
393
|
+
message: `健康检查失败: ${error.message}`
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
|
|
371
398
|
// /**
|
|
372
399
|
// * 获取数据库操作实例
|
|
373
400
|
// * @param {String} database - 数据库名称
|
|
@@ -417,18 +444,8 @@ SQLite.prototype.beginTransaction = async function () {
|
|
|
417
444
|
// 标记连接为事务中
|
|
418
445
|
conn._in_transaction = true;
|
|
419
446
|
|
|
420
|
-
//
|
|
421
|
-
|
|
422
|
-
conn.run('BEGIN TRANSACTION', (error) => {
|
|
423
|
-
if (error) {
|
|
424
|
-
// 事务开始失败,清除事务标记
|
|
425
|
-
conn._in_transaction = false;
|
|
426
|
-
reject(error);
|
|
427
|
-
} else {
|
|
428
|
-
resolve();
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
});
|
|
447
|
+
// 开始事务(better-sqlite3 同步执行)
|
|
448
|
+
conn.exec('BEGIN TRANSACTION');
|
|
432
449
|
|
|
433
450
|
if (this.config.debug) {
|
|
434
451
|
this.log('debug', '事务开始');
|
|
@@ -438,15 +455,7 @@ SQLite.prototype.beginTransaction = async function () {
|
|
|
438
455
|
return {
|
|
439
456
|
conn,
|
|
440
457
|
commit: async () => {
|
|
441
|
-
|
|
442
|
-
conn.run('COMMIT', (error) => {
|
|
443
|
-
if (error) {
|
|
444
|
-
reject(error);
|
|
445
|
-
} else {
|
|
446
|
-
resolve();
|
|
447
|
-
}
|
|
448
|
-
});
|
|
449
|
-
});
|
|
458
|
+
conn.exec('COMMIT');
|
|
450
459
|
if (this.config.debug) {
|
|
451
460
|
this.log('debug', '事务提交');
|
|
452
461
|
}
|
|
@@ -456,15 +465,7 @@ SQLite.prototype.beginTransaction = async function () {
|
|
|
456
465
|
await releaseConn(conn);
|
|
457
466
|
},
|
|
458
467
|
rollback: async () => {
|
|
459
|
-
|
|
460
|
-
conn.run('ROLLBACK', (error) => {
|
|
461
|
-
if (error) {
|
|
462
|
-
reject(error);
|
|
463
|
-
} else {
|
|
464
|
-
resolve();
|
|
465
|
-
}
|
|
466
|
-
});
|
|
467
|
-
});
|
|
468
|
+
conn.exec('ROLLBACK');
|
|
468
469
|
if (this.config.debug) {
|
|
469
470
|
this.log('debug', '事务回滚');
|
|
470
471
|
}
|
|
@@ -474,21 +475,9 @@ SQLite.prototype.beginTransaction = async function () {
|
|
|
474
475
|
},
|
|
475
476
|
exec: async (sql, params) => {
|
|
476
477
|
// 在事务中执行SQL
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
this.sql = error.sql;
|
|
481
|
-
this.error = {
|
|
482
|
-
code: error.errno,
|
|
483
|
-
message: error.message
|
|
484
|
-
};
|
|
485
|
-
reject(error);
|
|
486
|
-
resolve(0);
|
|
487
|
-
} else {
|
|
488
|
-
resolve(this.lastID || this.changes || 1);
|
|
489
|
-
}
|
|
490
|
-
});
|
|
491
|
-
});
|
|
478
|
+
const stmt = conn.prepare(sql);
|
|
479
|
+
const info = params && params.length > 0 ? stmt.run(params) : stmt.run();
|
|
480
|
+
return info.lastInsertRowid || info.changes || 1;
|
|
492
481
|
}
|
|
493
482
|
};
|
|
494
483
|
} catch (error) {
|
package/link_model.js
CHANGED
|
@@ -38,7 +38,7 @@ class LinkModel {
|
|
|
38
38
|
* @param {string} table 表名
|
|
39
39
|
* @returns {object}
|
|
40
40
|
*/
|
|
41
|
-
|
|
41
|
+
LinkModel.prototype.add = async function(table) {
|
|
42
42
|
var {
|
|
43
43
|
key,
|
|
44
44
|
value,
|
|
@@ -77,7 +77,7 @@ Link_model.prototype.add = async function(table) {
|
|
|
77
77
|
* @param {object} sql 数据管理器
|
|
78
78
|
* @returns {object} 当前对象
|
|
79
79
|
*/
|
|
80
|
-
|
|
80
|
+
LinkModel.prototype.init = function(sql) {
|
|
81
81
|
this.sql = sql;
|
|
82
82
|
return this;
|
|
83
83
|
};
|
|
@@ -88,7 +88,7 @@ Link_model.prototype.init = function(sql) {
|
|
|
88
88
|
* @param {object} value
|
|
89
89
|
* @returns {object} 当前对象
|
|
90
90
|
*/
|
|
91
|
-
|
|
91
|
+
LinkModel.prototype.update = function(key, value) {
|
|
92
92
|
var m = this.model;
|
|
93
93
|
for (var k in m) {
|
|
94
94
|
var o = m[k];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mm_sqlite",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "高性能SQLite数据库操作模块,提供与mm_mysql完全兼容的API接口,支持异步操作、事务处理和批量操作",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -38,13 +38,13 @@
|
|
|
38
38
|
"node": ">=14.0.0"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"
|
|
42
|
-
"
|
|
41
|
+
"better-sqlite3": "^13.0.2",
|
|
42
|
+
"mm_expand": "^2.4.7",
|
|
43
43
|
"sqlstring": "^2.3.3"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"eslint": "^10.
|
|
47
|
-
"eslint-plugin-jsdoc": "^63.
|
|
46
|
+
"eslint": "^10.8.0",
|
|
47
|
+
"eslint-plugin-jsdoc": "^63.3.3",
|
|
48
48
|
"mm_eslint": "^1.7.5"
|
|
49
49
|
}
|
|
50
|
-
}
|
|
50
|
+
}
|
package/pool.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const Database = require('better-sqlite3');
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* SQLite连接池类
|
|
@@ -33,9 +33,9 @@ class SQLitePool {
|
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
//
|
|
36
|
+
// 创建最小连接数(better-sqlite3是同步的,无需await)
|
|
37
37
|
for (let i = 0; i < this.config.pool_min; i++) {
|
|
38
|
-
const conn =
|
|
38
|
+
const conn = this._createConnection();
|
|
39
39
|
this._pool_connections.push(conn);
|
|
40
40
|
this._pool_available.push(conn);
|
|
41
41
|
}
|
|
@@ -86,18 +86,16 @@ class SQLitePool {
|
|
|
86
86
|
|
|
87
87
|
// 检查是否可以创建新连接
|
|
88
88
|
if (this._pool_connections.length < this.config.pool_max) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
reject(err);
|
|
100
|
-
});
|
|
89
|
+
try {
|
|
90
|
+
const conn = this._createConnection();
|
|
91
|
+
this._pool_connections.push(conn);
|
|
92
|
+
conn._last_used = Date.now();
|
|
93
|
+
cleanup();
|
|
94
|
+
resolve(conn);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
cleanup();
|
|
97
|
+
reject(err);
|
|
98
|
+
}
|
|
101
99
|
return;
|
|
102
100
|
}
|
|
103
101
|
|
|
@@ -159,14 +157,10 @@ class SQLitePool {
|
|
|
159
157
|
this._pool_reaper = null;
|
|
160
158
|
}
|
|
161
159
|
|
|
162
|
-
//
|
|
160
|
+
// 关闭所有连接(better-sqlite3的close是同步的)
|
|
163
161
|
this._pool_connections.forEach(conn => {
|
|
164
162
|
try {
|
|
165
|
-
conn.close(
|
|
166
|
-
if (err) {
|
|
167
|
-
this.log('error', '关闭连接池连接失败', err);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
163
|
+
conn.close();
|
|
170
164
|
} catch (error) {
|
|
171
165
|
this.log('error', '关闭连接池连接异常', error);
|
|
172
166
|
}
|
|
@@ -192,21 +186,20 @@ class SQLitePool {
|
|
|
192
186
|
}
|
|
193
187
|
|
|
194
188
|
/**
|
|
195
|
-
* 创建SQLite
|
|
189
|
+
* 创建SQLite连接(better-sqlite3,同步创建)
|
|
196
190
|
* @private
|
|
197
|
-
* @returns {
|
|
191
|
+
* @returns {object} 数据库连接对象
|
|
198
192
|
*/
|
|
199
193
|
_createConnection() {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (err) {
|
|
203
|
-
reject(err);
|
|
204
|
-
} else {
|
|
205
|
-
db.release = () => this.releaseConn(db);
|
|
206
|
-
resolve(db);
|
|
207
|
-
}
|
|
208
|
-
});
|
|
194
|
+
const db = new Database(this._getDbFilename(), {
|
|
195
|
+
timeout: this.config.connect_timeout || 5000
|
|
209
196
|
});
|
|
197
|
+
// 启用WAL模式,提升并发读取性能
|
|
198
|
+
db.pragma('journal_mode = WAL');
|
|
199
|
+
// 添加release方法,兼容原有API
|
|
200
|
+
db.release = () => this.releaseConn(db);
|
|
201
|
+
db._last_used = Date.now();
|
|
202
|
+
return db;
|
|
210
203
|
}
|
|
211
204
|
|
|
212
205
|
/**
|
|
@@ -221,11 +214,11 @@ class SQLitePool {
|
|
|
221
214
|
const conn = this._pool_available[i];
|
|
222
215
|
if (conn._last_used && now - conn._last_used > idle_timeout) {
|
|
223
216
|
// 关闭并移除空闲连接
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
217
|
+
try {
|
|
218
|
+
conn.close();
|
|
219
|
+
} catch (error) {
|
|
220
|
+
this.log('error', '关闭空闲连接失败', error);
|
|
221
|
+
}
|
|
229
222
|
|
|
230
223
|
// 从连接池中移除
|
|
231
224
|
const conn_idx = this._pool_connections.indexOf(conn);
|
|
@@ -238,4 +231,4 @@ class SQLitePool {
|
|
|
238
231
|
}
|
|
239
232
|
}
|
|
240
233
|
|
|
241
|
-
module.exports = SQLitePool;
|
|
234
|
+
module.exports = SQLitePool;
|
package/sql.js
CHANGED
|
@@ -7,6 +7,10 @@ const SqlString = require('sqlstring');
|
|
|
7
7
|
const { Base } = require('mm_expand');
|
|
8
8
|
|
|
9
9
|
function escape(value, stringifyObjects, timeZone) {
|
|
10
|
+
if (typeof value === 'string') {
|
|
11
|
+
// SQLite使用双单引号('')转义,不使用反斜杠转义
|
|
12
|
+
return "'" + value.replace(/'/g, "''") + "'";
|
|
13
|
+
}
|
|
10
14
|
return SqlString.escape(value, stringifyObjects, timeZone);
|
|
11
15
|
}
|
|
12
16
|
|
|
@@ -19,6 +23,48 @@ function escapeId(value, forbidQualified) {
|
|
|
19
23
|
* @property {Function} filter 设置并过滤参数
|
|
20
24
|
*/
|
|
21
25
|
class Sql extends Base {
|
|
26
|
+
/**
|
|
27
|
+
* 过滤查询参数字典
|
|
28
|
+
*/
|
|
29
|
+
static config = {
|
|
30
|
+
/**
|
|
31
|
+
* 分隔符 用于查询时的多条件处理
|
|
32
|
+
*/
|
|
33
|
+
'separator': '|',
|
|
34
|
+
/**
|
|
35
|
+
* 过滤
|
|
36
|
+
*/
|
|
37
|
+
'filter': {
|
|
38
|
+
/**
|
|
39
|
+
* 表名
|
|
40
|
+
*/
|
|
41
|
+
'table': 'table',
|
|
42
|
+
/**
|
|
43
|
+
* 查询的页码
|
|
44
|
+
*/
|
|
45
|
+
'page': 'page',
|
|
46
|
+
/**
|
|
47
|
+
* 查询每页条数
|
|
48
|
+
*/
|
|
49
|
+
'size': 'size',
|
|
50
|
+
/**
|
|
51
|
+
* 操作方式: 传入参数method=add, 支持参数 add增、del删、set改、get查,为空则为get
|
|
52
|
+
*/
|
|
53
|
+
'method': 'method',
|
|
54
|
+
/**
|
|
55
|
+
* 排序
|
|
56
|
+
*/
|
|
57
|
+
'orderby': 'orderby',
|
|
58
|
+
/**
|
|
59
|
+
* 查询显示的字段
|
|
60
|
+
*/
|
|
61
|
+
'field': 'field',
|
|
62
|
+
/**
|
|
63
|
+
* 统计结果: 统计符合条件的结果数,只有当page等于1或0时才会统计
|
|
64
|
+
*/
|
|
65
|
+
'count_ret': 'count_ret'
|
|
66
|
+
}
|
|
67
|
+
};
|
|
22
68
|
/**
|
|
23
69
|
* @description 数据库管理器
|
|
24
70
|
* @param {Function} run 查询函数
|
|
@@ -102,49 +148,6 @@ class Sql extends Base {
|
|
|
102
148
|
*/
|
|
103
149
|
this.count_ret = 'false';
|
|
104
150
|
|
|
105
|
-
/**
|
|
106
|
-
* 过滤查询参数字典
|
|
107
|
-
*/
|
|
108
|
-
this.config = {
|
|
109
|
-
/**
|
|
110
|
-
* 分隔符 用于查询时的多条件处理
|
|
111
|
-
*/
|
|
112
|
-
'separator': '|',
|
|
113
|
-
/**
|
|
114
|
-
* 过滤
|
|
115
|
-
*/
|
|
116
|
-
'filter': {
|
|
117
|
-
/**
|
|
118
|
-
* 表名
|
|
119
|
-
*/
|
|
120
|
-
'table': 'table',
|
|
121
|
-
/**
|
|
122
|
-
* 查询的页码
|
|
123
|
-
*/
|
|
124
|
-
'page': 'page',
|
|
125
|
-
/**
|
|
126
|
-
* 查询每页条数
|
|
127
|
-
*/
|
|
128
|
-
'size': 'size',
|
|
129
|
-
/**
|
|
130
|
-
* 操作方式: 传入参数method=add, 支持参数 add增、del删、set改、get查,为空则为get
|
|
131
|
-
*/
|
|
132
|
-
'method': 'method',
|
|
133
|
-
/**
|
|
134
|
-
* 排序
|
|
135
|
-
*/
|
|
136
|
-
'orderby': 'orderby',
|
|
137
|
-
/**
|
|
138
|
-
* 查询显示的字段
|
|
139
|
-
*/
|
|
140
|
-
'field': 'field',
|
|
141
|
-
/**
|
|
142
|
-
* 统计结果: 统计符合条件的结果数,只有当page等于1或0时才会统计
|
|
143
|
-
*/
|
|
144
|
-
'count_ret': 'count_ret'
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
|
|
148
151
|
this.like = false;
|
|
149
152
|
}
|
|
150
153
|
}
|
|
@@ -154,20 +157,17 @@ class Sql extends Base {
|
|
|
154
157
|
* @returns {object} 返回当前对象
|
|
155
158
|
*/
|
|
156
159
|
Sql.prototype.clear = function () {
|
|
157
|
-
|
|
158
|
-
this.set = '';
|
|
159
|
-
this.order = '';
|
|
160
|
-
this.view = '';
|
|
160
|
+
// 不重置run和exec方法引用,保留构造函数中设置的函数
|
|
161
161
|
this.sql = '';
|
|
162
|
-
this.error =
|
|
163
|
-
this.
|
|
164
|
-
this.
|
|
165
|
-
this.param = {};
|
|
162
|
+
this.error = null;
|
|
163
|
+
this.results = [];
|
|
164
|
+
this.table = '';
|
|
166
165
|
this.page = 0;
|
|
167
|
-
this.size =
|
|
168
|
-
this.
|
|
169
|
-
this.
|
|
170
|
-
|
|
166
|
+
this.size = 30;
|
|
167
|
+
this.method = '';
|
|
168
|
+
this.field = '';
|
|
169
|
+
this.orderby = '';
|
|
170
|
+
this.count_ret = 'false';
|
|
171
171
|
};
|
|
172
172
|
|
|
173
173
|
/**
|
|
@@ -705,6 +705,11 @@ Sql.prototype.toWhere = function (obj, like) {
|
|
|
705
705
|
if (like) {
|
|
706
706
|
for (var k in obj) {
|
|
707
707
|
var val = obj[k];
|
|
708
|
+
if (Array.isArray(val)) {
|
|
709
|
+
var vals = val.map(function(o) { return escape(o); });
|
|
710
|
+
where += ' and ' + escapeId(k) + ' in (' + vals.join(', ') + ')';
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
708
713
|
val = escape(val);
|
|
709
714
|
if (k.endsWith('_min')) {
|
|
710
715
|
where += ' and ' + escapeId(k.replace('_min', '')) + ' >= ' + val;
|
|
@@ -713,7 +718,7 @@ Sql.prototype.toWhere = function (obj, like) {
|
|
|
713
718
|
} else if (k.endsWith('_not')) {
|
|
714
719
|
where += ' and ' + escapeId(k.replace('_not', '')) + ' != ' + val;
|
|
715
720
|
} else if (k.endsWith('_has')) {
|
|
716
|
-
var vals = val.trim("'").split(',').map((o)
|
|
721
|
+
var vals = val.trim("'").split(',').map(function(o) {
|
|
717
722
|
return escape(o);
|
|
718
723
|
});
|
|
719
724
|
where += ' and ' + escapeId(k.replace('_has', '')) + ' in (' + vals.join(',') + ')';
|
|
@@ -728,6 +733,11 @@ Sql.prototype.toWhere = function (obj, like) {
|
|
|
728
733
|
} else {
|
|
729
734
|
for (var k in obj) {
|
|
730
735
|
var val = obj[k];
|
|
736
|
+
if (Array.isArray(val)) {
|
|
737
|
+
var vals = val.map(function(o) { return escape(o); });
|
|
738
|
+
where += ' and ' + escapeId(k) + ' in (' + vals.join(', ') + ')';
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
731
741
|
val = escape(val);
|
|
732
742
|
if (k.endsWith('_min')) {
|
|
733
743
|
where += ' and ' + escapeId(k.replace('_min', '')) + ' >= ' + val;
|
|
@@ -736,7 +746,7 @@ Sql.prototype.toWhere = function (obj, like) {
|
|
|
736
746
|
} else if (k.endsWith('_not')) {
|
|
737
747
|
where += ' and ' + escapeId(k.replace('_not', '')) + ' != ' + val;
|
|
738
748
|
} else if (k.endsWith('_has')) {
|
|
739
|
-
var vals = val.trim("'").split(',').map((o)
|
|
749
|
+
var vals = val.trim("'").split(',').map(function(o) {
|
|
740
750
|
return escape(o);
|
|
741
751
|
});
|
|
742
752
|
where += ' and ' + escapeId(k.replace('_has', '')) + ' in (' + vals.join(',') + ')';
|
|
@@ -906,7 +916,7 @@ Sql.prototype.set = async function (query, body, like) {
|
|
|
906
916
|
* @param {number} timeout 超时时间(毫秒),默认5000ms
|
|
907
917
|
* @returns {Promise|Array} 查询结果
|
|
908
918
|
*/
|
|
909
|
-
Sql.prototype.get = async function (query, sort, view, like, timeout =
|
|
919
|
+
Sql.prototype.get = async function (query, sort, view, like, timeout = 20000) {
|
|
910
920
|
if (!this.table) {
|
|
911
921
|
throw new Error('表名未设置');
|
|
912
922
|
}
|
|
@@ -979,7 +989,7 @@ Sql.prototype.addOrSet = async function (where, set, like) {
|
|
|
979
989
|
* @param {number} timeout 超时时间(毫秒),默认30000ms
|
|
980
990
|
* @returns {Promise | number} 记录数
|
|
981
991
|
*/
|
|
982
|
-
Sql.prototype.count = async function (query, like, timeout =
|
|
992
|
+
Sql.prototype.count = async function (query, like, timeout = 20000) {
|
|
983
993
|
if (!this.table) {
|
|
984
994
|
throw new Error('表名未设置');
|
|
985
995
|
}
|
|
@@ -1068,7 +1078,7 @@ Sql.prototype.toBatchAddSql = function (list) {
|
|
|
1068
1078
|
let columns = keys.join(',');
|
|
1069
1079
|
|
|
1070
1080
|
// 构建值部分
|
|
1071
|
-
let
|
|
1081
|
+
let val_list = [];
|
|
1072
1082
|
for (let item of list) {
|
|
1073
1083
|
let values = [];
|
|
1074
1084
|
for (let k of keys) {
|
|
@@ -1076,11 +1086,11 @@ Sql.prototype.toBatchAddSql = function (list) {
|
|
|
1076
1086
|
let val = item[key];
|
|
1077
1087
|
values.push(this.escape(val));
|
|
1078
1088
|
}
|
|
1079
|
-
|
|
1089
|
+
val_list.push(`(${values.join(',')})`);
|
|
1080
1090
|
}
|
|
1081
1091
|
|
|
1082
1092
|
// 生成最终SQL
|
|
1083
|
-
let sql = `INSERT INTO \`${this.table}\` (${columns}) VALUES ${
|
|
1093
|
+
let sql = `INSERT INTO \`${this.table}\` (${columns}) VALUES ${val_list.join(',')}`;
|
|
1084
1094
|
return sql;
|
|
1085
1095
|
};
|
|
1086
1096
|
|
|
@@ -1159,7 +1169,7 @@ Sql.prototype.setList = async function (list, like = false, batch_size = 100, ti
|
|
|
1159
1169
|
if (list.length <= batch_size) {
|
|
1160
1170
|
let sql = '';
|
|
1161
1171
|
for (let item of list) {
|
|
1162
|
-
sql += this.toSetSql(item.query, item.
|
|
1172
|
+
sql += this.toSetSql(item.query, item.item, like);
|
|
1163
1173
|
}
|
|
1164
1174
|
this.sql = sql;
|
|
1165
1175
|
return await this.exec(sql, [], timeout);
|
|
@@ -1182,7 +1192,7 @@ Sql.prototype.setList = async function (list, like = false, batch_size = 100, ti
|
|
|
1182
1192
|
|
|
1183
1193
|
let batch_sql = '';
|
|
1184
1194
|
for (let item of batch) {
|
|
1185
|
-
batch_sql += this.toSetSql(item.query, item.
|
|
1195
|
+
batch_sql += this.toSetSql(item.query, item.item, like);
|
|
1186
1196
|
}
|
|
1187
1197
|
|
|
1188
1198
|
final_res = await this.exec(batch_sql, [], timeout);
|
|
@@ -1225,7 +1235,7 @@ Sql.prototype.has = function (param_dt, sql_dt) {
|
|
|
1225
1235
|
* @param {object} sql_dt sql模板集合
|
|
1226
1236
|
* @returns {string|undefined} 没有模板则返回名称,都有则返回undefined
|
|
1227
1237
|
*/
|
|
1228
|
-
Sql.prototype.
|
|
1238
|
+
Sql.prototype.not = function (param_dt, sql_dt) {
|
|
1229
1239
|
var name;
|
|
1230
1240
|
for (var key in param_dt) {
|
|
1231
1241
|
if (!sql_dt[key]) {
|
|
@@ -1242,11 +1252,14 @@ Sql.prototype.noPrm = function (param_dt, sql_dt) {
|
|
|
1242
1252
|
* @param {object} sql_dt sql模板集合
|
|
1243
1253
|
* @returns {object} 返回过滤后的参数集合
|
|
1244
1254
|
*/
|
|
1245
|
-
Sql.prototype.
|
|
1246
|
-
var dt =
|
|
1255
|
+
Sql.prototype.filterParams = function (param_dt, sql_dt) {
|
|
1256
|
+
var dt = {};
|
|
1257
|
+
if (!param_dt || !sql_dt) {
|
|
1258
|
+
return dt;
|
|
1259
|
+
}
|
|
1247
1260
|
for (var key in param_dt) {
|
|
1248
1261
|
if (!sql_dt[key]) {
|
|
1249
|
-
dt
|
|
1262
|
+
dt[key] = param_dt[key];
|
|
1250
1263
|
}
|
|
1251
1264
|
}
|
|
1252
1265
|
return dt;
|
|
@@ -1302,9 +1315,10 @@ Sql.prototype.tplQuery = function (param_dt, sql_dt) {
|
|
|
1302
1315
|
} else {
|
|
1303
1316
|
for (var key in param_dt) {
|
|
1304
1317
|
var value = this.escape(param_dt[key]);
|
|
1305
|
-
|
|
1318
|
+
let sql = sql_dt[key];
|
|
1319
|
+
if (sql) {
|
|
1306
1320
|
// 使用直接替换方式,避免模板替换错误
|
|
1307
|
-
sql += ' && ' +
|
|
1321
|
+
sql += ' && ' + sql.replaceAll('{0}', value.trim("'"));
|
|
1308
1322
|
} else {
|
|
1309
1323
|
sql += ' && ' + this.escapeId(key) + ' = ' + value;
|
|
1310
1324
|
}
|
|
@@ -1356,9 +1370,10 @@ Sql.prototype.tplBody = function (param_dt, sql_dt) {
|
|
|
1356
1370
|
} else {
|
|
1357
1371
|
for (var key in param_dt) {
|
|
1358
1372
|
var value = this.escape(param_dt[key]);
|
|
1359
|
-
|
|
1373
|
+
let sql = sql_dt[key];
|
|
1374
|
+
if (sql) {
|
|
1360
1375
|
// 使用直接替换方式,避免模板替换错误
|
|
1361
|
-
let replaced =
|
|
1376
|
+
let replaced = sql.replaceAll('{0}', value.trim("'"));
|
|
1362
1377
|
sql += ', ' + replaced.replace('+ -', '- ').replace('- -', '+ ');
|
|
1363
1378
|
} else {
|
|
1364
1379
|
sql += ', ' + this.escapeId(key) + ' = ' + value;
|
|
@@ -1413,7 +1428,7 @@ Sql.prototype.model = function (model) {
|
|
|
1413
1428
|
* @param {number} timeout 超时时间(毫秒),默认30000ms
|
|
1414
1429
|
* @returns {Promise | object | null} 查询结果
|
|
1415
1430
|
*/
|
|
1416
|
-
Sql.prototype.getObj = async function (query, sort, view, like, timeout =
|
|
1431
|
+
Sql.prototype.getObj = async function (query, sort, view, like, timeout = 20000) {
|
|
1417
1432
|
// 保存当前分页设置
|
|
1418
1433
|
let old_page = this.page;
|
|
1419
1434
|
let size = this.size;
|
package/test_db/test_basic.db
CHANGED
|
Binary file
|
|
Binary file
|