mm_sqlite 1.3.3 → 1.3.4
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/index.js +2 -4
- package/package.json +1 -1
- package/sql.js +75 -105
- package/test_db/test_basic.db +0 -0
- package/test_db/test_core_methods.db +0 -0
- package/test_db/test_delete_verification.db +0 -0
- package/test_db/test_original_timeout.db +0 -0
- package/test_db/test_timeout_fix.db +0 -0
- package/test_backup.js +0 -78
- package/test_sql_functions.js +0 -86
- package/test_sqlite.js +0 -104
package/index.js
CHANGED
|
@@ -14,6 +14,7 @@ class SQLite extends Base {
|
|
|
14
14
|
* 默认配置
|
|
15
15
|
*/
|
|
16
16
|
static config = {
|
|
17
|
+
name: 'default',
|
|
17
18
|
dir: './db/'.fullname(),
|
|
18
19
|
user: 'root',
|
|
19
20
|
password: '',
|
|
@@ -44,10 +45,7 @@ class SQLite extends Base {
|
|
|
44
45
|
* @param {object} config - 配置对象
|
|
45
46
|
*/
|
|
46
47
|
constructor(config) {
|
|
47
|
-
|
|
48
|
-
super(merged_cfg);
|
|
49
|
-
|
|
50
|
-
this.config = merged_cfg;
|
|
48
|
+
super(config);
|
|
51
49
|
this._connection = null;
|
|
52
50
|
this._pool = null;
|
|
53
51
|
this._status = 'closed';
|
package/package.json
CHANGED
package/sql.js
CHANGED
|
@@ -196,10 +196,10 @@ Sql.prototype.toQuery = function (where, sort, view) {
|
|
|
196
196
|
if (!view) {
|
|
197
197
|
view = '*';
|
|
198
198
|
}
|
|
199
|
-
|
|
199
|
+
|
|
200
200
|
// 使用直接拼接的方式,避免模板替换错误
|
|
201
201
|
var sql = `SELECT ${view} FROM \`${this.table}\``;
|
|
202
|
-
|
|
202
|
+
|
|
203
203
|
if (where) {
|
|
204
204
|
sql += ' WHERE ' + where;
|
|
205
205
|
}
|
|
@@ -345,7 +345,7 @@ Sql.prototype.groupMathSql = async function (where, groupby, view, sort, method)
|
|
|
345
345
|
}
|
|
346
346
|
} else {
|
|
347
347
|
viewStr = ',' + method.toUpperCase() + '(' + escapeId(view) + ') ' + method.toLowerCase() + '_' +
|
|
348
|
-
|
|
348
|
+
view.replace(/`/g, '');
|
|
349
349
|
}
|
|
350
350
|
var sql = 'SELECT ' + (groupby ? escapeId(groupby) : '') + viewStr + ' FROM `' + this.table + '`';
|
|
351
351
|
if (where) {
|
|
@@ -463,11 +463,11 @@ Sql.prototype.sum = async function (field, query) {
|
|
|
463
463
|
if (!field || typeof field !== 'string') {
|
|
464
464
|
throw new TypeError('字段名必须为字符串');
|
|
465
465
|
}
|
|
466
|
-
|
|
466
|
+
|
|
467
467
|
if (!this.table) {
|
|
468
468
|
throw new TypeError('表名未设置');
|
|
469
469
|
}
|
|
470
|
-
|
|
470
|
+
|
|
471
471
|
const result = await this.groupSum(query, '', field, '');
|
|
472
472
|
const fieldKey = 'sum_' + field.replace(/`/g, '');
|
|
473
473
|
return result && result.length > 0 ? Number(result[0][fieldKey] || result[0].sum_age) : 0;
|
|
@@ -483,11 +483,11 @@ Sql.prototype.max = async function (field, query) {
|
|
|
483
483
|
if (!field || typeof field !== 'string') {
|
|
484
484
|
throw new TypeError('字段名必须为字符串');
|
|
485
485
|
}
|
|
486
|
-
|
|
486
|
+
|
|
487
487
|
if (!this.table) {
|
|
488
488
|
throw new TypeError('表名未设置');
|
|
489
489
|
}
|
|
490
|
-
|
|
490
|
+
|
|
491
491
|
const result = await this.groupMath(query, '', field, '', 'MAX');
|
|
492
492
|
const fieldKey = 'max_' + field.replace(/`/g, '');
|
|
493
493
|
return result && result.length > 0 ? Number(result[0][fieldKey] || result[0].max_age) : 0;
|
|
@@ -503,11 +503,11 @@ Sql.prototype.min = async function (field, query) {
|
|
|
503
503
|
if (!field || typeof field !== 'string') {
|
|
504
504
|
throw new TypeError('字段名必须为字符串');
|
|
505
505
|
}
|
|
506
|
-
|
|
506
|
+
|
|
507
507
|
if (!this.table) {
|
|
508
508
|
throw new TypeError('表名未设置');
|
|
509
509
|
}
|
|
510
|
-
|
|
510
|
+
|
|
511
511
|
const result = await this.groupMath(query, '', field, '', 'MIN');
|
|
512
512
|
const fieldKey = 'min_' + field.replace(/`/g, '');
|
|
513
513
|
return result && result.length > 0 ? Number(result[0][fieldKey] || result[0].min_age) : 0;
|
|
@@ -523,11 +523,11 @@ Sql.prototype.avg = async function (field, query) {
|
|
|
523
523
|
if (!field || typeof field !== 'string') {
|
|
524
524
|
throw new TypeError('字段名必须为字符串');
|
|
525
525
|
}
|
|
526
|
-
|
|
526
|
+
|
|
527
527
|
if (!this.table) {
|
|
528
528
|
throw new TypeError('表名未设置');
|
|
529
529
|
}
|
|
530
|
-
|
|
530
|
+
|
|
531
531
|
const result = await this.groupMath(query, '', field, '', 'AVG');
|
|
532
532
|
const fieldKey = 'avg_' + field.replace(/`/g, '');
|
|
533
533
|
return result && result.length > 0 ? Number(result[0][fieldKey] || result[0].avg_age) : 0;
|
|
@@ -544,7 +544,7 @@ Sql.prototype.toStr = function (...args) {
|
|
|
544
544
|
if (args.length === 0) {
|
|
545
545
|
return '';
|
|
546
546
|
}
|
|
547
|
-
|
|
547
|
+
|
|
548
548
|
// 将所有参数转换为字符串并用空格连接
|
|
549
549
|
return args.map(arg => String(arg)).join(' ');
|
|
550
550
|
};
|
|
@@ -555,30 +555,30 @@ Sql.prototype.toStr = function (...args) {
|
|
|
555
555
|
* @param {string} file 备份文件路径
|
|
556
556
|
* @returns {Promise<boolean>} 备份是否成功
|
|
557
557
|
*/
|
|
558
|
-
Sql.prototype.backup = async function(file) {
|
|
558
|
+
Sql.prototype.backup = async function (file) {
|
|
559
559
|
if (!file || typeof file !== 'string') {
|
|
560
560
|
throw new TypeError('文件路径必须为字符串');
|
|
561
561
|
}
|
|
562
|
-
|
|
562
|
+
|
|
563
563
|
if (!this.table) {
|
|
564
564
|
throw new TypeError('表名未设置');
|
|
565
565
|
}
|
|
566
|
-
|
|
566
|
+
|
|
567
567
|
try {
|
|
568
568
|
// 获取所有数据
|
|
569
569
|
const data = await this.getSql('', '', '*');
|
|
570
|
-
|
|
570
|
+
|
|
571
571
|
if (!data || !Array.isArray(data)) {
|
|
572
572
|
throw new Error('获取数据失败');
|
|
573
573
|
}
|
|
574
574
|
|
|
575
575
|
// 构建 SQL 语句
|
|
576
576
|
let sqlContent = '';
|
|
577
|
-
|
|
577
|
+
|
|
578
578
|
// 添加表结构(简化版,实际应用中可能需要更复杂的表结构导出)
|
|
579
579
|
sqlContent += `-- 备份表: ${this.table}\n`;
|
|
580
580
|
sqlContent += `-- 备份时间: ${new Date().toISOString()}\n\n`;
|
|
581
|
-
|
|
581
|
+
|
|
582
582
|
// 添加数据插入语句
|
|
583
583
|
for (const row of data) {
|
|
584
584
|
const keys = Object.keys(row).map(key => this.escapeId(key)).join(', ');
|
|
@@ -589,15 +589,15 @@ Sql.prototype.backup = async function(file) {
|
|
|
589
589
|
// 写入文件(这里需要文件系统模块,实际使用时需要引入 fs)
|
|
590
590
|
const fs = require('fs');
|
|
591
591
|
const path = require('path');
|
|
592
|
-
|
|
592
|
+
|
|
593
593
|
// 确保目录存在
|
|
594
594
|
const dir = path.dirname(file);
|
|
595
595
|
if (!fs.existsSync(dir)) {
|
|
596
596
|
fs.mkdirSync(dir, { recursive: true });
|
|
597
597
|
}
|
|
598
|
-
|
|
598
|
+
|
|
599
599
|
fs.writeFileSync(file, sqlContent, 'utf8');
|
|
600
|
-
|
|
600
|
+
|
|
601
601
|
return true;
|
|
602
602
|
} catch (err) {
|
|
603
603
|
this.error = err.message;
|
|
@@ -611,33 +611,33 @@ Sql.prototype.backup = async function(file) {
|
|
|
611
611
|
* @param {string} file 恢复文件路径
|
|
612
612
|
* @returns {Promise<boolean>} 恢复是否成功
|
|
613
613
|
*/
|
|
614
|
-
Sql.prototype.restore = async function(file) {
|
|
614
|
+
Sql.prototype.restore = async function (file) {
|
|
615
615
|
if (!file || typeof file !== 'string') {
|
|
616
616
|
throw new TypeError('文件路径必须为字符串');
|
|
617
617
|
}
|
|
618
|
-
|
|
618
|
+
|
|
619
619
|
if (!this.table) {
|
|
620
620
|
throw new TypeError('表名未设置');
|
|
621
621
|
}
|
|
622
|
-
|
|
622
|
+
|
|
623
623
|
try {
|
|
624
624
|
const fs = require('fs');
|
|
625
|
-
|
|
625
|
+
|
|
626
626
|
// 检查文件是否存在
|
|
627
627
|
if (!fs.existsSync(file)) {
|
|
628
628
|
throw new Error('备份文件不存在');
|
|
629
629
|
}
|
|
630
|
-
|
|
630
|
+
|
|
631
631
|
// 读取文件内容
|
|
632
632
|
const sqlContent = fs.readFileSync(file, 'utf8');
|
|
633
|
-
|
|
633
|
+
|
|
634
634
|
if (!sqlContent) {
|
|
635
635
|
throw new Error('备份文件为空');
|
|
636
636
|
}
|
|
637
|
-
|
|
637
|
+
|
|
638
638
|
// 解析 SQL 语句(按分号分割,但需要处理字符串中的分号)
|
|
639
639
|
const statements = this._parseSqlStatements(sqlContent);
|
|
640
|
-
|
|
640
|
+
|
|
641
641
|
// 执行所有 SQL 语句
|
|
642
642
|
for (const statement of statements) {
|
|
643
643
|
if (statement.trim()) {
|
|
@@ -647,7 +647,7 @@ Sql.prototype.restore = async function(file) {
|
|
|
647
647
|
}
|
|
648
648
|
}
|
|
649
649
|
}
|
|
650
|
-
|
|
650
|
+
|
|
651
651
|
return true;
|
|
652
652
|
} catch (err) {
|
|
653
653
|
this.error = err.message;
|
|
@@ -662,22 +662,22 @@ Sql.prototype.restore = async function(file) {
|
|
|
662
662
|
* @returns {string[]} SQL 语句数组
|
|
663
663
|
* @private
|
|
664
664
|
*/
|
|
665
|
-
Sql.prototype._parseSqlStatements = function(sql) {
|
|
665
|
+
Sql.prototype._parseSqlStatements = function (sql) {
|
|
666
666
|
const statements = [];
|
|
667
667
|
let currentStatement = '';
|
|
668
668
|
let inString = false;
|
|
669
669
|
let stringChar = '';
|
|
670
|
-
|
|
670
|
+
|
|
671
671
|
for (let i = 0; i < sql.length; i++) {
|
|
672
672
|
const char = sql[i];
|
|
673
|
-
|
|
673
|
+
|
|
674
674
|
if (char === "'" || char === '"') {
|
|
675
675
|
if (!inString) {
|
|
676
676
|
inString = true;
|
|
677
677
|
stringChar = char;
|
|
678
678
|
} else if (char === stringChar) {
|
|
679
679
|
// 检查是否是转义字符
|
|
680
|
-
if (i > 0 && sql[i-1] === '\\') {
|
|
680
|
+
if (i > 0 && sql[i - 1] === '\\') {
|
|
681
681
|
// 转义字符,继续字符串
|
|
682
682
|
} else {
|
|
683
683
|
inString = false;
|
|
@@ -685,7 +685,7 @@ Sql.prototype._parseSqlStatements = function(sql) {
|
|
|
685
685
|
}
|
|
686
686
|
}
|
|
687
687
|
}
|
|
688
|
-
|
|
688
|
+
|
|
689
689
|
if (!inString && char === ';') {
|
|
690
690
|
statements.push(currentStatement.trim());
|
|
691
691
|
currentStatement = '';
|
|
@@ -693,12 +693,12 @@ Sql.prototype._parseSqlStatements = function(sql) {
|
|
|
693
693
|
currentStatement += char;
|
|
694
694
|
}
|
|
695
695
|
}
|
|
696
|
-
|
|
696
|
+
|
|
697
697
|
// 添加最后一个语句(如果没有分号结尾)
|
|
698
698
|
if (currentStatement.trim()) {
|
|
699
699
|
statements.push(currentStatement.trim());
|
|
700
700
|
}
|
|
701
|
-
|
|
701
|
+
|
|
702
702
|
return statements.filter(stmt => stmt.length > 0);
|
|
703
703
|
};
|
|
704
704
|
|
|
@@ -717,9 +717,6 @@ Sql.prototype.toWhere = function (obj, like) {
|
|
|
717
717
|
if (like) {
|
|
718
718
|
for (var k in obj) {
|
|
719
719
|
var val = obj[k];
|
|
720
|
-
if (val && typeof (val) === 'string') {
|
|
721
|
-
val = val.trim("'");
|
|
722
|
-
}
|
|
723
720
|
val = escape(val);
|
|
724
721
|
if (k.endsWith('_min')) {
|
|
725
722
|
where += ' and ' + escapeId(k.replace('_min', '')) + ' >= ' + val;
|
|
@@ -729,7 +726,7 @@ Sql.prototype.toWhere = function (obj, like) {
|
|
|
729
726
|
where += ' and ' + escapeId(k.replace('_not', '')) + ' != ' + val;
|
|
730
727
|
} else if (k.endsWith('_has')) {
|
|
731
728
|
var vals = val.trim("'").split(',').map((o) => {
|
|
732
|
-
return
|
|
729
|
+
return escape(o);
|
|
733
730
|
});
|
|
734
731
|
where += ' and ' + escapeId(k.replace('_has', '')) + ' in (' + vals.join(',') + ')';
|
|
735
732
|
} else if (k.endsWith('_like')) {
|
|
@@ -743,9 +740,6 @@ Sql.prototype.toWhere = function (obj, like) {
|
|
|
743
740
|
} else {
|
|
744
741
|
for (var k in obj) {
|
|
745
742
|
var val = obj[k];
|
|
746
|
-
if (val && typeof (val) === 'string') {
|
|
747
|
-
val = val.trim("'");
|
|
748
|
-
}
|
|
749
743
|
val = escape(val);
|
|
750
744
|
if (k.endsWith('_min')) {
|
|
751
745
|
where += ' and ' + escapeId(k.replace('_min', '')) + ' >= ' + val;
|
|
@@ -755,7 +749,7 @@ Sql.prototype.toWhere = function (obj, like) {
|
|
|
755
749
|
where += ' and ' + escapeId(k.replace('_not', '')) + ' != ' + val;
|
|
756
750
|
} else if (k.endsWith('_has')) {
|
|
757
751
|
var vals = val.trim("'").split(',').map((o) => {
|
|
758
|
-
return
|
|
752
|
+
return escape(o);
|
|
759
753
|
});
|
|
760
754
|
where += ' and ' + escapeId(k.replace('_has', '')) + ' in (' + vals.join(',') + ')';
|
|
761
755
|
} else {
|
|
@@ -774,14 +768,8 @@ Sql.prototype.toWhere = function (obj, like) {
|
|
|
774
768
|
Sql.prototype.toSet = function (obj) {
|
|
775
769
|
var set = '';
|
|
776
770
|
for (var k in obj) {
|
|
777
|
-
if (!Object.prototype.hasOwnProperty.call(obj, k)) continue;
|
|
778
|
-
|
|
779
771
|
var val = obj[k];
|
|
780
772
|
if (val === undefined || val === null) continue;
|
|
781
|
-
|
|
782
|
-
if (typeof val === 'string') {
|
|
783
|
-
val = val.trim("'");
|
|
784
|
-
}
|
|
785
773
|
val = escape(val);
|
|
786
774
|
|
|
787
775
|
if (k.endsWith('_add')) {
|
|
@@ -811,13 +799,8 @@ Sql.prototype.toAddSql = function (item) {
|
|
|
811
799
|
let value = '';
|
|
812
800
|
|
|
813
801
|
for (const k in item) {
|
|
814
|
-
if (!Object.prototype.hasOwnProperty.call(item, k)) continue;
|
|
815
|
-
|
|
816
802
|
key += ',' + escapeId(k);
|
|
817
803
|
let val = item[k];
|
|
818
|
-
if (typeof val === 'string') {
|
|
819
|
-
val = val.trim("'");
|
|
820
|
-
}
|
|
821
804
|
value += ',' + escape(val);
|
|
822
805
|
}
|
|
823
806
|
|
|
@@ -838,10 +821,10 @@ Sql.prototype.toDelSql = function (query, like) {
|
|
|
838
821
|
throw new Error('表名未设置');
|
|
839
822
|
}
|
|
840
823
|
const where = this.toWhere(query, like);
|
|
841
|
-
|
|
824
|
+
|
|
842
825
|
// 使用直接拼接的方式,避免模板替换错误
|
|
843
826
|
const sql = `DELETE FROM \`${this.table}\` WHERE ${where};`;
|
|
844
|
-
|
|
827
|
+
|
|
845
828
|
return sql;
|
|
846
829
|
};
|
|
847
830
|
|
|
@@ -858,10 +841,10 @@ Sql.prototype.toSetSql = function (query, item, like) {
|
|
|
858
841
|
}
|
|
859
842
|
const where = this.toWhere(query, like);
|
|
860
843
|
const set = this.toSet(item);
|
|
861
|
-
|
|
844
|
+
|
|
862
845
|
// 使用直接拼接的方式,避免模板替换错误
|
|
863
846
|
const sql = `UPDATE \`${this.table}\` SET ${set} WHERE ${where};`;
|
|
864
|
-
|
|
847
|
+
|
|
865
848
|
return sql;
|
|
866
849
|
};
|
|
867
850
|
|
|
@@ -967,7 +950,7 @@ Sql.prototype.get = async function (query, sort, view, like, timeout = 30000) {
|
|
|
967
950
|
const timeout_promise = new Promise((unused_resolve, reject) => {
|
|
968
951
|
setTimeout(() => reject(new Error('查询操作超时')), timeout);
|
|
969
952
|
});
|
|
970
|
-
|
|
953
|
+
|
|
971
954
|
return await Promise.race([
|
|
972
955
|
(async () => {
|
|
973
956
|
// 生成SQL并执行
|
|
@@ -1022,14 +1005,8 @@ Sql.prototype.addOrSet = async function (where, set, like) {
|
|
|
1022
1005
|
}
|
|
1023
1006
|
} else {
|
|
1024
1007
|
for (const k in set) {
|
|
1025
|
-
if (!Object.prototype.hasOwnProperty.call(set, k)) continue;
|
|
1026
|
-
|
|
1027
1008
|
key += ',' + this.escapeId(k);
|
|
1028
|
-
|
|
1029
|
-
if (typeof val === 'string') {
|
|
1030
|
-
val = val.trim("'");
|
|
1031
|
-
}
|
|
1032
|
-
value += ',' + this.escape(val);
|
|
1009
|
+
value += ',' + this.escape(set[k]);
|
|
1033
1010
|
}
|
|
1034
1011
|
}
|
|
1035
1012
|
|
|
@@ -1184,9 +1161,7 @@ Sql.prototype.toBatchAddSql = function (list) {
|
|
|
1184
1161
|
|
|
1185
1162
|
let keys = [];
|
|
1186
1163
|
for (const k in first_obj) {
|
|
1187
|
-
|
|
1188
|
-
keys.push(this.escapeId(k));
|
|
1189
|
-
}
|
|
1164
|
+
keys.push(this.escapeId(k));
|
|
1190
1165
|
}
|
|
1191
1166
|
|
|
1192
1167
|
// 构建列名部分
|
|
@@ -1199,9 +1174,6 @@ Sql.prototype.toBatchAddSql = function (list) {
|
|
|
1199
1174
|
for (const k of keys) {
|
|
1200
1175
|
const key = k.replace(/`/g, '');
|
|
1201
1176
|
let val = item[key];
|
|
1202
|
-
if (typeof val === 'string') {
|
|
1203
|
-
val = val.trim("'");
|
|
1204
|
-
}
|
|
1205
1177
|
values.push(this.escape(val));
|
|
1206
1178
|
}
|
|
1207
1179
|
values.push(`(${values.join(',')})`);
|
|
@@ -1386,15 +1358,13 @@ Sql.prototype.setList = async function (list, like = false, batch_size = 100, ti
|
|
|
1386
1358
|
* @param {object} sql_dt sql模板集合
|
|
1387
1359
|
* @returns {boolean} 有则返回true,没有则返回false
|
|
1388
1360
|
*/
|
|
1389
|
-
Sql.prototype.
|
|
1361
|
+
Sql.prototype.has = function (param_dt, sql_dt) {
|
|
1390
1362
|
var bl = false;
|
|
1391
1363
|
for (var key in sql_dt) {
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
break;
|
|
1397
|
-
}
|
|
1364
|
+
var value = param_dt[key];
|
|
1365
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
1366
|
+
bl = true;
|
|
1367
|
+
break;
|
|
1398
1368
|
}
|
|
1399
1369
|
}
|
|
1400
1370
|
return bl;
|
|
@@ -1456,14 +1426,14 @@ Sql.prototype.tplQuery = function (param_dt, sql_dt) {
|
|
|
1456
1426
|
for (var i = 0; i < len; i++) {
|
|
1457
1427
|
// 使用直接替换方式,避免模板替换错误
|
|
1458
1428
|
const escaped_val = this.escape(arr[i]);
|
|
1459
|
-
sl += ' || ' + tpl.replaceAll('{0}', escaped_val);
|
|
1429
|
+
sl += ' || ' + tpl.replaceAll('{0}', escaped_val.trim("'"));
|
|
1460
1430
|
}
|
|
1461
1431
|
sl = sl.replace(' || ', '') + ')';
|
|
1462
1432
|
sql += ' && ' + sl;
|
|
1463
1433
|
} else {
|
|
1464
1434
|
// 使用直接替换方式,避免模板替换错误
|
|
1465
1435
|
const escaped_val = this.escape(value);
|
|
1466
|
-
sql += ' && ' + tpl.replaceAll('{0}', escaped_val);
|
|
1436
|
+
sql += ' && ' + tpl.replaceAll('{0}', escaped_val.trim("'"));
|
|
1467
1437
|
}
|
|
1468
1438
|
} else {
|
|
1469
1439
|
if (arr.length > 1) {
|
|
@@ -1485,7 +1455,7 @@ Sql.prototype.tplQuery = function (param_dt, sql_dt) {
|
|
|
1485
1455
|
var value = this.escape(param_dt[key]);
|
|
1486
1456
|
if (sql_dt[key]) {
|
|
1487
1457
|
// 使用直接替换方式,避免模板替换错误
|
|
1488
|
-
sql += ' && ' + sql_dt[key].replaceAll('{0}', value);
|
|
1458
|
+
sql += ' && ' + sql_dt[key].replaceAll('{0}', value.trim("'"));
|
|
1489
1459
|
} else {
|
|
1490
1460
|
sql += ' && ' + this.escapeId(key) + ' = ' + value;
|
|
1491
1461
|
}
|
|
@@ -1539,7 +1509,7 @@ Sql.prototype.tplBody = function (param_dt, sql_dt) {
|
|
|
1539
1509
|
var value = this.escape(param_dt[key]);
|
|
1540
1510
|
if (sql_dt[key]) {
|
|
1541
1511
|
// 使用直接替换方式,避免模板替换错误
|
|
1542
|
-
const replaced = sql_dt[key].
|
|
1512
|
+
const replaced = sql_dt[key].replaceAll('{0}', value.trim("'"));
|
|
1543
1513
|
sql += ' , ' + replaced.replace('+ -', '- ').replace('- -', '+ ');
|
|
1544
1514
|
} else {
|
|
1545
1515
|
sql += ' , ' + this.escapeId(key) + ' = ' + value;
|
|
@@ -1636,7 +1606,7 @@ Sql.prototype.load = async function (file) {
|
|
|
1636
1606
|
try {
|
|
1637
1607
|
// 记录操作日志
|
|
1638
1608
|
if (this.config && this.config.debug) {
|
|
1639
|
-
|
|
1609
|
+
this.log('debug', `开始从文件加载数据库`, {
|
|
1640
1610
|
file: file
|
|
1641
1611
|
});
|
|
1642
1612
|
}
|
|
@@ -1672,9 +1642,9 @@ Sql.prototype.load = async function (file) {
|
|
|
1672
1642
|
await this.exec('COMMIT');
|
|
1673
1643
|
|
|
1674
1644
|
if (this.config && this.config.debug) {
|
|
1675
|
-
|
|
1645
|
+
this.log('info', `数据库加载成功`, {
|
|
1676
1646
|
file: file,
|
|
1677
|
-
stmt_num:
|
|
1647
|
+
stmt_num: sql_stmts.length
|
|
1678
1648
|
});
|
|
1679
1649
|
}
|
|
1680
1650
|
|
|
@@ -1785,12 +1755,12 @@ Sql.prototype._splitSqlStatements = function (sql_content) {
|
|
|
1785
1755
|
* @returns {Promise<string>} 创建表的 SQL 语句
|
|
1786
1756
|
* @private
|
|
1787
1757
|
*/
|
|
1788
|
-
Sql.prototype._getCreateTableSql = async function() {
|
|
1758
|
+
Sql.prototype._getCreateTableSql = async function () {
|
|
1789
1759
|
try {
|
|
1790
1760
|
// 查询表结构信息(SQLite 使用 sqlite_master 表)
|
|
1791
1761
|
const sql = `SELECT sql FROM sqlite_master WHERE type='table' AND name='${this.table}'`;
|
|
1792
1762
|
const result = await this.run(sql);
|
|
1793
|
-
|
|
1763
|
+
|
|
1794
1764
|
if (result && result.length > 0 && result[0].sql) {
|
|
1795
1765
|
// 在创建表语句中添加 IF NOT EXISTS 条件
|
|
1796
1766
|
let create_sql = result[0].sql;
|
|
@@ -1799,7 +1769,7 @@ Sql.prototype._getCreateTableSql = async function() {
|
|
|
1799
1769
|
}
|
|
1800
1770
|
return create_sql + ';';
|
|
1801
1771
|
}
|
|
1802
|
-
|
|
1772
|
+
|
|
1803
1773
|
return '';
|
|
1804
1774
|
} catch (err) {
|
|
1805
1775
|
this.log('warn', '获取表结构失败,将仅备份数据', err);
|
|
@@ -1813,7 +1783,7 @@ Sql.prototype._getCreateTableSql = async function() {
|
|
|
1813
1783
|
* @param {boolean} create 是否导出创建表语句,默认为false
|
|
1814
1784
|
* @returns {Promise<boolean>} 备份是否成功
|
|
1815
1785
|
*/
|
|
1816
|
-
Sql.prototype.backup = async function(file, create = false) {
|
|
1786
|
+
Sql.prototype.backup = async function (file, create = false) {
|
|
1817
1787
|
if (!file || typeof file !== 'string') {
|
|
1818
1788
|
throw new TypeError('文件路径必须为字符串');
|
|
1819
1789
|
}
|
|
@@ -1825,18 +1795,18 @@ Sql.prototype.backup = async function(file, create = false) {
|
|
|
1825
1795
|
try {
|
|
1826
1796
|
// 获取所有数据
|
|
1827
1797
|
const data = await this.getSql('', '', '*');
|
|
1828
|
-
|
|
1798
|
+
|
|
1829
1799
|
if (!data || !Array.isArray(data)) {
|
|
1830
1800
|
throw new Error('获取数据失败');
|
|
1831
1801
|
}
|
|
1832
1802
|
|
|
1833
1803
|
// 构建 SQL 语句
|
|
1834
1804
|
let sql_content = '';
|
|
1835
|
-
|
|
1805
|
+
|
|
1836
1806
|
// 添加表结构(简化版,实际应用中可能需要更复杂的表结构导出)
|
|
1837
1807
|
sql_content += `-- 备份表: ${this.table}\n`;
|
|
1838
1808
|
sql_content += `-- 备份时间: ${new Date().toISOString()}\n\n`;
|
|
1839
|
-
|
|
1809
|
+
|
|
1840
1810
|
// 如果create为true,添加创建表语句
|
|
1841
1811
|
if (create) {
|
|
1842
1812
|
const create_table_sql = await this._getCreateTableSql();
|
|
@@ -1844,7 +1814,7 @@ Sql.prototype.backup = async function(file, create = false) {
|
|
|
1844
1814
|
sql_content += create_table_sql + '\n\n';
|
|
1845
1815
|
}
|
|
1846
1816
|
}
|
|
1847
|
-
|
|
1817
|
+
|
|
1848
1818
|
// 添加数据插入语句
|
|
1849
1819
|
for (const row of data) {
|
|
1850
1820
|
const keys = Object.keys(row).map(key => this.escapeId(key)).join(', ');
|
|
@@ -1855,15 +1825,15 @@ Sql.prototype.backup = async function(file, create = false) {
|
|
|
1855
1825
|
// 写入文件(这里需要文件系统模块,实际使用时需要引入 fs)
|
|
1856
1826
|
const fs = require('fs');
|
|
1857
1827
|
const path = require('path');
|
|
1858
|
-
|
|
1828
|
+
|
|
1859
1829
|
// 确保目录存在
|
|
1860
1830
|
const dir = path.dirname(file);
|
|
1861
1831
|
if (!fs.existsSync(dir)) {
|
|
1862
1832
|
fs.mkdirSync(dir, { recursive: true });
|
|
1863
1833
|
}
|
|
1864
|
-
|
|
1834
|
+
|
|
1865
1835
|
fs.writeFileSync(file, sql_content, 'utf8');
|
|
1866
|
-
|
|
1836
|
+
|
|
1867
1837
|
return true;
|
|
1868
1838
|
} catch (err) {
|
|
1869
1839
|
this.error = err.message;
|
|
@@ -1877,7 +1847,7 @@ Sql.prototype.backup = async function(file, create = false) {
|
|
|
1877
1847
|
* @param {string} file 备份文件路径
|
|
1878
1848
|
* @returns {Promise<boolean>} 恢复是否成功
|
|
1879
1849
|
*/
|
|
1880
|
-
Sql.prototype.restore = async function(file) {
|
|
1850
|
+
Sql.prototype.restore = async function (file) {
|
|
1881
1851
|
if (!file || typeof file !== 'string') {
|
|
1882
1852
|
throw new TypeError('文件路径必须为字符串');
|
|
1883
1853
|
}
|
|
@@ -1888,24 +1858,24 @@ Sql.prototype.restore = async function(file) {
|
|
|
1888
1858
|
|
|
1889
1859
|
try {
|
|
1890
1860
|
const fs = require('fs');
|
|
1891
|
-
|
|
1861
|
+
|
|
1892
1862
|
if (!fs.existsSync(file)) {
|
|
1893
1863
|
throw new Error('备份文件不存在');
|
|
1894
1864
|
}
|
|
1895
1865
|
|
|
1896
1866
|
// 读取 SQL 文件
|
|
1897
1867
|
const sql_content = fs.readFileSync(file, 'utf8');
|
|
1898
|
-
|
|
1868
|
+
|
|
1899
1869
|
// 解析 SQL 语句
|
|
1900
1870
|
const stmts = this._parseSqlStatements(sql_content);
|
|
1901
|
-
|
|
1871
|
+
|
|
1902
1872
|
// 执行每个 SQL 语句
|
|
1903
1873
|
for (const stmt of stmts) {
|
|
1904
1874
|
if (stmt.trim() && !stmt.startsWith('--')) {
|
|
1905
1875
|
await this.exec(stmt);
|
|
1906
1876
|
}
|
|
1907
1877
|
}
|
|
1908
|
-
|
|
1878
|
+
|
|
1909
1879
|
return true;
|
|
1910
1880
|
} catch (err) {
|
|
1911
1881
|
this.error = err.message;
|
|
@@ -1920,7 +1890,7 @@ Sql.prototype.restore = async function(file) {
|
|
|
1920
1890
|
* @returns {string[]} SQL 语句数组
|
|
1921
1891
|
* @private
|
|
1922
1892
|
*/
|
|
1923
|
-
Sql.prototype._parseSqlStatements = function(sql) {
|
|
1893
|
+
Sql.prototype._parseSqlStatements = function (sql) {
|
|
1924
1894
|
const stmts = [];
|
|
1925
1895
|
let cur_stmt = '';
|
|
1926
1896
|
let in_str = false;
|
package/test_db/test_basic.db
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
package/test_backup.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileOverview 备份恢复功能测试脚本
|
|
3
|
-
* @version 1.0
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const {Sql} = require('./sql');
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// 模拟数据库操作函数
|
|
10
|
-
async function mockRun(sql) {
|
|
11
|
-
console.log('执行查询SQL:', sql);
|
|
12
|
-
|
|
13
|
-
// 模拟返回数据
|
|
14
|
-
if (sql.includes('SELECT')) {
|
|
15
|
-
return [
|
|
16
|
-
{ id: 1, name: '张三', age: 25 },
|
|
17
|
-
{ id: 2, name: '李四', age: 30 },
|
|
18
|
-
{ id: 3, name: '王五', age: 28 }
|
|
19
|
-
];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (sql.includes('count')) {
|
|
23
|
-
return [{ count: 3 }];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return [];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async function mockExec(sql) {
|
|
30
|
-
console.log('执行更新SQL:', sql);
|
|
31
|
-
return { affectedRows: 1, insertId: 1 };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async function testBackupRestore() {
|
|
35
|
-
console.log('=== 测试备份恢复功能 ===');
|
|
36
|
-
|
|
37
|
-
// 创建 SQL 实例
|
|
38
|
-
const sql = new Sql(mockRun, mockExec);
|
|
39
|
-
sql.table = 'test_table';
|
|
40
|
-
|
|
41
|
-
const backupFile = './backup/test_backup.sql';
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
// 测试备份
|
|
45
|
-
console.log('\n1. 测试备份功能...');
|
|
46
|
-
const backupResult = await sql.backup(backupFile);
|
|
47
|
-
console.log('备份结果:', backupResult ? '成功' : '失败');
|
|
48
|
-
|
|
49
|
-
if (backupResult) {
|
|
50
|
-
// 读取备份文件内容
|
|
51
|
-
const fs = require('fs');
|
|
52
|
-
if (fs.existsSync(backupFile)) {
|
|
53
|
-
const content = fs.readFileSync(backupFile, 'utf8');
|
|
54
|
-
console.log('备份文件内容:');
|
|
55
|
-
console.log(content);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// 测试恢复
|
|
60
|
-
console.log('\n2. 测试恢复功能...');
|
|
61
|
-
const restoreResult = await sql.restore(backupFile);
|
|
62
|
-
console.log('恢复结果:', restoreResult ? '成功' : '失败');
|
|
63
|
-
|
|
64
|
-
console.log('\n=== 测试完成 ===');
|
|
65
|
-
|
|
66
|
-
} catch (error) {
|
|
67
|
-
console.error('测试出错:', error.message);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// 运行测试
|
|
72
|
-
if (require.main === module) {
|
|
73
|
-
testBackupRestore();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
module.exports = {
|
|
77
|
-
testBackupRestore
|
|
78
|
-
};
|
package/test_sql_functions.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SQLite SQL函数测试文件
|
|
3
|
-
* @file test_sql_functions.js
|
|
4
|
-
* @description 直接测试Sql类的SQL生成函数,不涉及数据库连接
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// 引入依赖
|
|
8
|
-
const { Sql } = require('./sql.js');
|
|
9
|
-
|
|
10
|
-
function test() {
|
|
11
|
-
console.log('=== 测试SQLite SQL函数 ===\n');
|
|
12
|
-
|
|
13
|
-
// 创建Sql实例
|
|
14
|
-
const db = new Sql(null, null);
|
|
15
|
-
db.table = 'test_table';
|
|
16
|
-
|
|
17
|
-
// 测试1: 测试toQuery函数
|
|
18
|
-
console.log('1. 测试toQuery函数:');
|
|
19
|
-
const query_sql = db.toQuery('`id` = 1', '`id` DESC', 'title, regex_field');
|
|
20
|
-
console.log(' toQuery生成的SQL:', query_sql);
|
|
21
|
-
|
|
22
|
-
// 测试2: 测试包含特殊字符的查询
|
|
23
|
-
console.log('\n2. 测试包含特殊字符的查询:');
|
|
24
|
-
const special_query = db.toQuery('`title` LIKE \'%test%\'', null, '*');
|
|
25
|
-
console.log(' 特殊字符查询SQL:', special_query);
|
|
26
|
-
|
|
27
|
-
// 测试3: 测试toAddSql函数
|
|
28
|
-
console.log('\n3. 测试toAddSql函数:');
|
|
29
|
-
const add_sql = db.toAddSql({
|
|
30
|
-
title: '测试添加标题',
|
|
31
|
-
content: '测试添加内容',
|
|
32
|
-
regex_field: '~\\d+$',
|
|
33
|
-
special_chars: "'test' AND 1=1"
|
|
34
|
-
});
|
|
35
|
-
console.log(' toAddSql生成的SQL:', add_sql);
|
|
36
|
-
|
|
37
|
-
// 测试4: 测试toSetSql函数
|
|
38
|
-
console.log('\n4. 测试toSetSql函数:');
|
|
39
|
-
const set_sql = db.toSetSql(
|
|
40
|
-
{ id: 1 },
|
|
41
|
-
{ title: '更新标题', regex_field: '~\\d+$' }
|
|
42
|
-
);
|
|
43
|
-
console.log(' toSetSql生成的SQL:', set_sql);
|
|
44
|
-
|
|
45
|
-
// 测试5: 测试toDelSql函数
|
|
46
|
-
console.log('\n5. 测试toDelSql函数:');
|
|
47
|
-
const del_sql = db.toDelSql({ id: 999 });
|
|
48
|
-
console.log(' toDelSql生成的SQL:', del_sql);
|
|
49
|
-
|
|
50
|
-
// 测试6: 测试toWhere函数
|
|
51
|
-
console.log('\n6. 测试toWhere函数:');
|
|
52
|
-
const where_sql = db.toWhere({
|
|
53
|
-
id: 1,
|
|
54
|
-
title_like: 'test',
|
|
55
|
-
regex_field: '~\\d+$'
|
|
56
|
-
});
|
|
57
|
-
console.log(' toWhere生成的SQL:', where_sql);
|
|
58
|
-
|
|
59
|
-
// 测试7: 测试toSet函数
|
|
60
|
-
console.log('\n7. 测试toSet函数:');
|
|
61
|
-
const set_str = db.toSet({
|
|
62
|
-
title: '新标题',
|
|
63
|
-
content: '新内容',
|
|
64
|
-
regex_field: '~\\d+$'
|
|
65
|
-
});
|
|
66
|
-
console.log(' toSet生成的SQL:', set_str);
|
|
67
|
-
|
|
68
|
-
// 测试8: 测试addSql函数(只生成SQL,不执行)
|
|
69
|
-
console.log('\n8. 测试addSql函数:');
|
|
70
|
-
const add_sql_str = `INSERT INTO \`${db.table}\` (\`title\`, \`content\`, \`regex_field\`) VALUES ('测试标题', '测试内容', '~\\\\d+$');`;
|
|
71
|
-
console.log(' addSql生成的SQL:', add_sql_str);
|
|
72
|
-
|
|
73
|
-
// 测试9: 测试setSql函数(只生成SQL,不执行)
|
|
74
|
-
console.log('\n9. 测试setSql函数:');
|
|
75
|
-
const set_sql_str = `UPDATE \`${db.table}\` SET \`title\` = '更新标题', \`regex_field\` = '~\\\\d+$' WHERE \`id\` = 1;`;
|
|
76
|
-
console.log(' setSql生成的SQL:', set_sql_str);
|
|
77
|
-
|
|
78
|
-
// 测试10: 测试delSql函数(只生成SQL,不执行)
|
|
79
|
-
console.log('\n10. 测试delSql函数:');
|
|
80
|
-
const del_sql_str = `DELETE FROM \`${db.table}\` WHERE \`id\` = 999;`;
|
|
81
|
-
console.log(' delSql生成的SQL:', del_sql_str);
|
|
82
|
-
|
|
83
|
-
console.log('\n=== 所有SQL函数测试完成 ===');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
test();
|
package/test_sqlite.js
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SQLite数据库操作类测试文件
|
|
3
|
-
* @file test_sqlite.js
|
|
4
|
-
* @description 测试Sqlite类的各项功能,特别是正则表达式写入
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// 引入依赖
|
|
8
|
-
const { SQLite } = require('./index.js');
|
|
9
|
-
|
|
10
|
-
async function test() {
|
|
11
|
-
const sqlite = new SQLite({
|
|
12
|
-
file: ':memory:', // 使用内存数据库进行测试
|
|
13
|
-
debug: true
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
await sqlite.open();
|
|
17
|
-
console.log('SQLite数据库连接成功');
|
|
18
|
-
|
|
19
|
-
// 创建测试表
|
|
20
|
-
await sqlite.exec(`
|
|
21
|
-
CREATE TABLE IF NOT EXISTS test_table (
|
|
22
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
23
|
-
title TEXT,
|
|
24
|
-
content TEXT,
|
|
25
|
-
regex_field TEXT,
|
|
26
|
-
special_chars TEXT
|
|
27
|
-
)
|
|
28
|
-
`);
|
|
29
|
-
console.log('测试表创建成功');
|
|
30
|
-
|
|
31
|
-
const db = sqlite.db();
|
|
32
|
-
db.table = 'test_table';
|
|
33
|
-
|
|
34
|
-
console.log('=== 测试SQLite数据库操作 ===\n');
|
|
35
|
-
|
|
36
|
-
// 测试1: 普通字符串写入
|
|
37
|
-
console.log('1. 测试普通字符串写入:');
|
|
38
|
-
const result1 = await db.set({
|
|
39
|
-
id: 1
|
|
40
|
-
}, {
|
|
41
|
-
title: "测试标题",
|
|
42
|
-
content: "测试内容"
|
|
43
|
-
});
|
|
44
|
-
console.log(' 普通字符串更新结果:', result1, db.error);
|
|
45
|
-
|
|
46
|
-
// 测试2: 正则表达式字符串写入
|
|
47
|
-
console.log('\n2. 测试正则表达式字符串写入:');
|
|
48
|
-
const regex_str = "~\\d+$";
|
|
49
|
-
console.log(' 正则表达式字符串:', regex_str);
|
|
50
|
-
|
|
51
|
-
const result2 = await db.set({
|
|
52
|
-
id: 1
|
|
53
|
-
}, {
|
|
54
|
-
title: regex_str,
|
|
55
|
-
regex_field: regex_str
|
|
56
|
-
});
|
|
57
|
-
console.log(' 正则表达式更新结果:', result2, db.error);
|
|
58
|
-
|
|
59
|
-
// 测试3: 查看当前数据库中的值
|
|
60
|
-
console.log('\n3. 查看当前数据:');
|
|
61
|
-
const data = await db.get({
|
|
62
|
-
id: 1
|
|
63
|
-
});
|
|
64
|
-
console.log(' 当前数据:', data);
|
|
65
|
-
|
|
66
|
-
// 测试4: 测试toQuery函数
|
|
67
|
-
console.log('\n4. 测试toQuery函数:');
|
|
68
|
-
const query_sql = db.toQuery('`id` = 1', '`id` DESC', 'title, regex_field');
|
|
69
|
-
console.log(' toQuery生成的SQL:', query_sql);
|
|
70
|
-
|
|
71
|
-
// 测试5: 测试包含特殊字符的查询
|
|
72
|
-
console.log('\n5. 测试包含特殊字符的查询:');
|
|
73
|
-
const special_query = db.toQuery('`title` LIKE \'%test%\'', null, '*');
|
|
74
|
-
console.log(' 特殊字符查询SQL:', special_query);
|
|
75
|
-
|
|
76
|
-
// 测试6: 测试toAddSql函数
|
|
77
|
-
console.log('\n6. 测试toAddSql函数:');
|
|
78
|
-
const add_sql = db.toAddSql({
|
|
79
|
-
title: '测试添加标题',
|
|
80
|
-
content: '测试添加内容',
|
|
81
|
-
regex_field: '~\\d+$',
|
|
82
|
-
special_chars: "'test' AND 1=1"
|
|
83
|
-
});
|
|
84
|
-
console.log(' toAddSql生成的SQL:', add_sql);
|
|
85
|
-
|
|
86
|
-
// 测试7: 测试toSetSql函数
|
|
87
|
-
console.log('\n7. 测试toSetSql函数:');
|
|
88
|
-
const set_sql = db.toSetSql(
|
|
89
|
-
{ id: 1 },
|
|
90
|
-
{ title: '更新标题', regex_field: '~\\d+$' }
|
|
91
|
-
);
|
|
92
|
-
console.log(' toSetSql生成的SQL:', set_sql);
|
|
93
|
-
|
|
94
|
-
// 测试8: 测试toDelSql函数
|
|
95
|
-
console.log('\n8. 测试toDelSql函数:');
|
|
96
|
-
const del_sql = db.toDelSql({ id: 999 });
|
|
97
|
-
console.log(' toDelSql生成的SQL:', del_sql);
|
|
98
|
-
|
|
99
|
-
console.log('\n=== 所有SQLite函数测试完成 ===');
|
|
100
|
-
|
|
101
|
-
await sqlite.close();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
test().catch(console.error);
|