mm_sqlite 1.4.0 → 1.4.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/db.js +200 -264
- package/index.js +43 -54
- package/package.json +3 -3
- package/pool.js +11 -16
- package/sql.js +311 -414
- package/test_db/test_basic.db +0 -0
- package/test_db/test_core_methods.db +0 -0
package/sql.js
CHANGED
|
@@ -564,46 +564,40 @@ Sql.prototype.backup = async function (file) {
|
|
|
564
564
|
throw new TypeError('表名未设置');
|
|
565
565
|
}
|
|
566
566
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
const data = await this.getSql('', '', '*');
|
|
567
|
+
// 获取所有数据
|
|
568
|
+
const data = await this.getSql('', '', '*');
|
|
570
569
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
570
|
+
if (!data || !Array.isArray(data)) {
|
|
571
|
+
throw new Error('获取数据失败');
|
|
572
|
+
}
|
|
574
573
|
|
|
575
|
-
|
|
576
|
-
|
|
574
|
+
// 构建 SQL 语句
|
|
575
|
+
let sqlContent = '';
|
|
577
576
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
577
|
+
// 添加表结构(简化版,实际应用中可能需要更复杂的表结构导出)
|
|
578
|
+
sqlContent += `-- 备份表: ${this.table}\n`;
|
|
579
|
+
sqlContent += `-- 备份时间: ${new Date().toISOString()}\n\n`;
|
|
581
580
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
581
|
+
// 添加数据插入语句
|
|
582
|
+
for (const row of data) {
|
|
583
|
+
const keys = Object.keys(row).map(key => this.escapeId(key)).join(', ');
|
|
584
|
+
const values = Object.values(row).map(value => this.escape(value)).join(', ');
|
|
585
|
+
sqlContent += `INSERT INTO \`${this.table}\` (${keys}) VALUES (${values});\n`;
|
|
586
|
+
}
|
|
588
587
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
588
|
+
// 写入文件(这里需要文件系统模块,实际使用时需要引入 fs)
|
|
589
|
+
const fs = require('fs');
|
|
590
|
+
const path = require('path');
|
|
592
591
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
592
|
+
// 确保目录存在
|
|
593
|
+
const dir = path.dirname(file);
|
|
594
|
+
if (!fs.existsSync(dir)) {
|
|
595
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
596
|
+
}
|
|
598
597
|
|
|
599
|
-
|
|
598
|
+
fs.writeFileSync(file, sqlContent, 'utf8');
|
|
600
599
|
|
|
601
|
-
|
|
602
|
-
} catch (err) {
|
|
603
|
-
this.error = err.message;
|
|
604
|
-
this.log('error', '备份数据库失败', err);
|
|
605
|
-
return false;
|
|
606
|
-
}
|
|
600
|
+
return true;
|
|
607
601
|
};
|
|
608
602
|
|
|
609
603
|
/**
|
|
@@ -620,40 +614,34 @@ Sql.prototype.restore = async function (file) {
|
|
|
620
614
|
throw new TypeError('表名未设置');
|
|
621
615
|
}
|
|
622
616
|
|
|
623
|
-
|
|
624
|
-
const fs = require('fs');
|
|
617
|
+
const fs = require('fs');
|
|
625
618
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
619
|
+
// 检查文件是否存在
|
|
620
|
+
if (!fs.existsSync(file)) {
|
|
621
|
+
throw new Error('备份文件不存在');
|
|
622
|
+
}
|
|
630
623
|
|
|
631
|
-
|
|
632
|
-
|
|
624
|
+
// 读取文件内容
|
|
625
|
+
const sqlContent = fs.readFileSync(file, 'utf8');
|
|
633
626
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
627
|
+
if (!sqlContent) {
|
|
628
|
+
throw new Error('备份文件为空');
|
|
629
|
+
}
|
|
637
630
|
|
|
638
|
-
|
|
639
|
-
|
|
631
|
+
// 解析 SQL 语句(按分号分割,但需要处理字符串中的分号)
|
|
632
|
+
const statements = this._parseSqlStatements(sqlContent);
|
|
640
633
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
634
|
+
// 执行所有 SQL 语句
|
|
635
|
+
for (const statement of statements) {
|
|
636
|
+
if (statement.trim()) {
|
|
637
|
+
// 跳过注释行
|
|
638
|
+
if (!statement.trim().startsWith('--')) {
|
|
639
|
+
await this.exec(statement);
|
|
648
640
|
}
|
|
649
641
|
}
|
|
650
|
-
|
|
651
|
-
return true;
|
|
652
|
-
} catch (err) {
|
|
653
|
-
this.error = err.message;
|
|
654
|
-
this.log('error', '恢复数据库失败', err);
|
|
655
|
-
return false;
|
|
656
642
|
}
|
|
643
|
+
|
|
644
|
+
return true;
|
|
657
645
|
};
|
|
658
646
|
|
|
659
647
|
/**
|
|
@@ -870,18 +858,11 @@ Sql.prototype.add = async function (body) {
|
|
|
870
858
|
if (!this.table || !body || typeof body !== 'object') {
|
|
871
859
|
throw new Error('表名或数据未设置');
|
|
872
860
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
var bl = await this.exec(sql);
|
|
861
|
+
var sql = this.toAddSql(body);
|
|
862
|
+
this.sql = sql;
|
|
863
|
+
var bl = await this.exec(sql);
|
|
877
864
|
|
|
878
|
-
|
|
879
|
-
} catch (err) {
|
|
880
|
-
this.error = err.message;
|
|
881
|
-
this.log('error', '添加数据失败', err);
|
|
882
|
-
// 返回默认操作结果对象,保持返回值类型一致
|
|
883
|
-
return 0;
|
|
884
|
-
}
|
|
865
|
+
return bl;
|
|
885
866
|
};
|
|
886
867
|
/**
|
|
887
868
|
* @description 删除数据
|
|
@@ -893,17 +874,10 @@ Sql.prototype.del = async function (query, like) {
|
|
|
893
874
|
if (!this.table) {
|
|
894
875
|
throw new Error('表名未设置');
|
|
895
876
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
return bl;
|
|
901
|
-
} catch (err) {
|
|
902
|
-
this.error = err.message;
|
|
903
|
-
this.log('error', '删除数据失败', err);
|
|
904
|
-
// 返回默认操作结果对象,保持返回值类型一致
|
|
905
|
-
return 0;
|
|
906
|
-
}
|
|
877
|
+
var sql = this.toDelSql(query, like);
|
|
878
|
+
this.sql = sql;
|
|
879
|
+
var bl = await this.exec(sql);
|
|
880
|
+
return bl;
|
|
907
881
|
};
|
|
908
882
|
|
|
909
883
|
/**
|
|
@@ -917,17 +891,10 @@ Sql.prototype.set = async function (query, body, like) {
|
|
|
917
891
|
if (!this.table || !body || typeof body !== 'object') {
|
|
918
892
|
throw new Error('表名或数据未设置');
|
|
919
893
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
return bl;
|
|
925
|
-
} catch (err) {
|
|
926
|
-
this.error = err.message;
|
|
927
|
-
this.log('error', '修改数据失败', err);
|
|
928
|
-
// 返回默认操作结果对象,保持返回值类型一致
|
|
929
|
-
return 0;
|
|
930
|
-
}
|
|
894
|
+
var sql = this.toSetSql(query, body, like);
|
|
895
|
+
this.sql = sql;
|
|
896
|
+
var bl = await this.exec(sql);
|
|
897
|
+
return bl;
|
|
931
898
|
};
|
|
932
899
|
|
|
933
900
|
/**
|
|
@@ -944,16 +911,9 @@ Sql.prototype.get = async function (query, sort, view, like, timeout = 5000) {
|
|
|
944
911
|
throw new Error('表名未设置');
|
|
945
912
|
}
|
|
946
913
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
return list;
|
|
951
|
-
} catch (err) {
|
|
952
|
-
this.error = err.message;
|
|
953
|
-
this.log('error', '查询数据失败', err);
|
|
954
|
-
// 返回空数组作为默认值,保持返回值类型一致
|
|
955
|
-
return [];
|
|
956
|
-
}
|
|
914
|
+
var sql = this.toGetSql(query, sort, view, like);
|
|
915
|
+
var list = await this.run(sql, [], timeout);
|
|
916
|
+
return list;
|
|
957
917
|
};
|
|
958
918
|
|
|
959
919
|
/**
|
|
@@ -967,55 +927,48 @@ Sql.prototype.addOrSet = async function (where, set, like) {
|
|
|
967
927
|
if (!this.table || !where || !set) {
|
|
968
928
|
throw new Error('表名、条件或数据未设置');
|
|
969
929
|
}
|
|
970
|
-
|
|
971
|
-
let where_str;
|
|
930
|
+
let where_str;
|
|
972
931
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
const count = await this.countSql(whereStr);
|
|
932
|
+
if (typeof where === 'object') {
|
|
933
|
+
where_str = this.toWhere(where, like);
|
|
934
|
+
} else {
|
|
935
|
+
where_str = where;
|
|
936
|
+
}
|
|
980
937
|
|
|
981
|
-
|
|
982
|
-
let key = '';
|
|
983
|
-
let value = '';
|
|
938
|
+
const count = await this.countSql(where_str);
|
|
984
939
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
key += ',' + this.escapeId(k);
|
|
997
|
-
value += ',' + this.escape(set[k]);
|
|
940
|
+
if (count === 0) {
|
|
941
|
+
let key = '';
|
|
942
|
+
let value = '';
|
|
943
|
+
|
|
944
|
+
if (typeof set === 'string') {
|
|
945
|
+
const arr = set.split(',');
|
|
946
|
+
for (const o of arr) {
|
|
947
|
+
const ar = o.split('=');
|
|
948
|
+
if (ar.length === 2) {
|
|
949
|
+
key += ',' + ar[0];
|
|
950
|
+
value += ',' + ar[1];
|
|
998
951
|
}
|
|
999
952
|
}
|
|
1000
|
-
|
|
1001
|
-
const bl = await this.addSql(key.replace(',', ''), value.replace(',', ''));
|
|
1002
|
-
|
|
1003
|
-
return bl;
|
|
1004
953
|
} else {
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
954
|
+
for (const k in set) {
|
|
955
|
+
key += ',' + this.escapeId(k);
|
|
956
|
+
value += ',' + this.escape(set[k]);
|
|
1008
957
|
}
|
|
958
|
+
}
|
|
1009
959
|
|
|
1010
|
-
|
|
960
|
+
const bl = await this.addSql(key.replace(',', ''), value.replace(',', ''));
|
|
1011
961
|
|
|
1012
|
-
|
|
962
|
+
return bl;
|
|
963
|
+
} else {
|
|
964
|
+
let set_vals = set;
|
|
965
|
+
if (typeof set === 'object') {
|
|
966
|
+
set_vals = await this.toSet(set);
|
|
1013
967
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
return 0;
|
|
968
|
+
|
|
969
|
+
const bl1 = await this.setSql(where_str, set_vals);
|
|
970
|
+
|
|
971
|
+
return bl1;
|
|
1019
972
|
}
|
|
1020
973
|
};
|
|
1021
974
|
|
|
@@ -1031,20 +984,13 @@ Sql.prototype.count = async function (query, like, timeout = 30000) {
|
|
|
1031
984
|
throw new Error('表名未设置');
|
|
1032
985
|
}
|
|
1033
986
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
const total = list && list[0] && list[0].num ? parseInt(list[0].num) : 0;
|
|
987
|
+
const where = typeof query === 'string' ? query : await this.toWhere(query, like);
|
|
988
|
+
const sql = 'SELECT COUNT(*) as num FROM `' + this.table + '`' + (where ? ' WHERE ' + where : '');
|
|
989
|
+
this.sql = sql;
|
|
990
|
+
const list = await this.run(sql);
|
|
991
|
+
const total = list && list[0] && list[0].num ? parseInt(list[0].num) : 0;
|
|
1040
992
|
|
|
1041
|
-
|
|
1042
|
-
} catch (err) {
|
|
1043
|
-
this.error = err.message;
|
|
1044
|
-
this.log('error', '统计记录数失败', err);
|
|
1045
|
-
// 返回0作为默认统计值,保持返回值类型一致
|
|
1046
|
-
return 0;
|
|
1047
|
-
}
|
|
993
|
+
return total;
|
|
1048
994
|
};
|
|
1049
995
|
|
|
1050
996
|
/**
|
|
@@ -1070,37 +1016,31 @@ Sql.prototype.addList = async function (list, batch_size = 100, timeout = 60000)
|
|
|
1070
1016
|
if (!this.table || !Array.isArray(list) || list.length === 0) {
|
|
1071
1017
|
throw new Error('表名或数据列表未设置');
|
|
1072
1018
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
// 分批处理大数据量
|
|
1081
|
-
const total = Math.ceil(list.length / batch_size);
|
|
1082
|
-
this.log('info', `开始分批添加数据,共${total}批,每批${batch_size}条`);
|
|
1019
|
+
if (list.length <= batch_size) {
|
|
1020
|
+
// 使用批量插入语法,不使用事务包装
|
|
1021
|
+
this.sql = this.toBatchAddSql(list);
|
|
1022
|
+
return await this.exec(this.sql);
|
|
1023
|
+
}
|
|
1083
1024
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
const batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
1088
|
-
this.log('debug', `处理第${i + 1}/${total}批数据,${batch.length}条`);
|
|
1025
|
+
// 分批处理大数据量
|
|
1026
|
+
const total = Math.ceil(list.length / batch_size);
|
|
1027
|
+
this.log('info', `开始分批添加数据,共${total}批,每批${batch_size}条`);
|
|
1089
1028
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1029
|
+
// 分批执行,每批一个单独的批量插入语句
|
|
1030
|
+
let final_res = null;
|
|
1031
|
+
for (let i = 0; i < total; i++) {
|
|
1032
|
+
const batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
1033
|
+
this.log('debug', `处理第${i + 1}/${total}批数据,${batch.length}条`);
|
|
1093
1034
|
|
|
1094
|
-
|
|
1095
|
-
|
|
1035
|
+
// 生成批量插入SQL
|
|
1036
|
+
const batch_sql = this.toBatchAddSql(batch);
|
|
1037
|
+
this.sql = batch_sql;
|
|
1096
1038
|
|
|
1097
|
-
this.
|
|
1098
|
-
return final_res;
|
|
1099
|
-
} catch (error) {
|
|
1100
|
-
this.log('error', '批量添加数据失败', error);
|
|
1101
|
-
// 返回默认操作结果对象,保持返回值类型一致
|
|
1102
|
-
return 0;
|
|
1039
|
+
final_res = await this.exec(batch_sql);
|
|
1103
1040
|
}
|
|
1041
|
+
|
|
1042
|
+
this.log('success', `批量添加数据完成,共${list.length}条`);
|
|
1043
|
+
return final_res;
|
|
1104
1044
|
};
|
|
1105
1045
|
|
|
1106
1046
|
/**
|
|
@@ -1156,59 +1096,53 @@ Sql.prototype.delList = async function (list, like, batch_size = 100, timeout =
|
|
|
1156
1096
|
if (!this.table || !Array.isArray(list) || list.length === 0) {
|
|
1157
1097
|
throw new Error('表名或数据列表未设置');
|
|
1158
1098
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
sql += this.toDelSql(item.query, like);
|
|
1164
|
-
}
|
|
1165
|
-
this.sql = sql;
|
|
1166
|
-
return await this.exec(sql, [], timeout);
|
|
1099
|
+
if (list.length <= batch_size) {
|
|
1100
|
+
let sql = '';
|
|
1101
|
+
for (const item of list) {
|
|
1102
|
+
sql += this.toDelSql(item.query, like);
|
|
1167
1103
|
}
|
|
1104
|
+
this.sql = sql;
|
|
1105
|
+
return await this.exec(sql, [], timeout);
|
|
1106
|
+
}
|
|
1168
1107
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
// 使用事务包装整个操作以保证原子性
|
|
1174
|
-
let final_res = null;
|
|
1175
|
-
try {
|
|
1176
|
-
// 开始事务
|
|
1177
|
-
await this.exec('BEGIN;');
|
|
1108
|
+
// 分批处理大数据量
|
|
1109
|
+
const total = Math.ceil(list.length / batch_size);
|
|
1110
|
+
this.log('info', `开始分批删除数据,共${total}批,每批${batch_size}条`);
|
|
1178
1111
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1112
|
+
// 使用事务包装整个操作以保证原子性
|
|
1113
|
+
let final_res = null;
|
|
1114
|
+
try {
|
|
1115
|
+
// 开始事务
|
|
1116
|
+
await this.exec('BEGIN;');
|
|
1183
1117
|
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
final_res = await this.exec(batch_sql, [], timeout);
|
|
1189
|
-
}
|
|
1118
|
+
// 分批处理
|
|
1119
|
+
for (let i = 0; i < total; i++) {
|
|
1120
|
+
const batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
1121
|
+
this.log('debug', `处理第${i + 1}/${total}批数据,${batch.length}条`);
|
|
1190
1122
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
// 发生错误时回滚事务
|
|
1195
|
-
try {
|
|
1196
|
-
await this.exec('ROLLBACK;', [], timeout);
|
|
1197
|
-
this.log('error', '批量删除数据失败,事务已回滚', error);
|
|
1198
|
-
} catch (rollback_err) {
|
|
1199
|
-
this.log('error', '批量删除数据失败,事务回滚也失败', rollback_err);
|
|
1123
|
+
let batch_sql = '';
|
|
1124
|
+
for (const item of batch) {
|
|
1125
|
+
batch_sql += this.toDelSql(item.query, like);
|
|
1200
1126
|
}
|
|
1201
|
-
|
|
1202
|
-
this.log('error', 'SQL语句执行失败', error);
|
|
1127
|
+
final_res = await this.exec(batch_sql, [], timeout);
|
|
1203
1128
|
}
|
|
1204
1129
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
} catch (
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1130
|
+
// 提交事务
|
|
1131
|
+
await this.exec('COMMIT;', [], timeout);
|
|
1132
|
+
} catch (error) {
|
|
1133
|
+
// 发生错误时回滚事务
|
|
1134
|
+
try {
|
|
1135
|
+
await this.exec('ROLLBACK;', [], timeout);
|
|
1136
|
+
this.log('error', '批量删除数据失败,事务已回滚', error);
|
|
1137
|
+
} catch (rollback_err) {
|
|
1138
|
+
this.log('error', '批量删除数据失败,事务回滚也失败', rollback_err);
|
|
1139
|
+
}
|
|
1140
|
+
// 记录错误日志
|
|
1141
|
+
this.log('error', 'SQL语句执行失败', error);
|
|
1211
1142
|
}
|
|
1143
|
+
|
|
1144
|
+
this.log('success', `批量删除数据完成,共${list.length}条`);
|
|
1145
|
+
return final_res;
|
|
1212
1146
|
};
|
|
1213
1147
|
/**
|
|
1214
1148
|
* 更新多条数据
|
|
@@ -1222,58 +1156,48 @@ Sql.prototype.setList = async function (list, like = false, batch_size = 100, ti
|
|
|
1222
1156
|
if (!this.table || !Array.isArray(list) || list.length === 0) {
|
|
1223
1157
|
throw new Error('表名或数据列表未设置');
|
|
1224
1158
|
}
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
sql += this.toSetSql(item.query, item.obj, like);
|
|
1230
|
-
}
|
|
1231
|
-
this.sql = sql;
|
|
1232
|
-
return await this.exec(sql, [], timeout);
|
|
1159
|
+
if (list.length <= batch_size) {
|
|
1160
|
+
let sql = '';
|
|
1161
|
+
for (const item of list) {
|
|
1162
|
+
sql += this.toSetSql(item.query, item.obj, like);
|
|
1233
1163
|
}
|
|
1164
|
+
this.sql = sql;
|
|
1165
|
+
return await this.exec(sql, [], timeout);
|
|
1166
|
+
}
|
|
1234
1167
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
// 使用事务包装整个操作以保证原子性
|
|
1240
|
-
let final_res = null;
|
|
1241
|
-
try {
|
|
1242
|
-
// 开始事务
|
|
1243
|
-
await this.exec('BEGIN;');
|
|
1168
|
+
// 分批处理大数据量
|
|
1169
|
+
const total = Math.ceil(list.length / batch_size);
|
|
1170
|
+
this.log('info', `开始分批更新数据,共${total}批,每批${batch_size}条`);
|
|
1244
1171
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1172
|
+
// 使用事务包装整个操作以保证原子性
|
|
1173
|
+
let final_res = null;
|
|
1174
|
+
try {
|
|
1175
|
+
// 开始事务
|
|
1176
|
+
await this.exec('BEGIN;');
|
|
1249
1177
|
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1178
|
+
// 分批处理
|
|
1179
|
+
for (let i = 0; i < total; i++) {
|
|
1180
|
+
const batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
1181
|
+
this.log('debug', `处理第${i + 1}/${total}批数据,${batch.length}条`);
|
|
1254
1182
|
|
|
1255
|
-
|
|
1183
|
+
let batch_sql = '';
|
|
1184
|
+
for (const item of batch) {
|
|
1185
|
+
batch_sql += this.toSetSql(item.query, item.obj, like);
|
|
1256
1186
|
}
|
|
1257
1187
|
|
|
1258
|
-
|
|
1259
|
-
await this.exec('COMMIT;', [], timeout);
|
|
1260
|
-
} catch (error) {
|
|
1261
|
-
// 发生错误时回滚事务
|
|
1262
|
-
try {
|
|
1263
|
-
await this.exec('ROLLBACK;', [], timeout);
|
|
1264
|
-
this.log('error', '批量更新数据失败,事务已回滚', error);
|
|
1265
|
-
} catch (rollback_err) {
|
|
1266
|
-
this.log('error', '批量更新数据失败,事务回滚也失败', rollback_err);
|
|
1267
|
-
}
|
|
1188
|
+
final_res = await this.exec(batch_sql, [], timeout);
|
|
1268
1189
|
}
|
|
1269
1190
|
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
} catch (
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1191
|
+
// 提交事务
|
|
1192
|
+
await this.exec('COMMIT;', [], timeout);
|
|
1193
|
+
} catch (error) {
|
|
1194
|
+
// 发生错误时回滚事务
|
|
1195
|
+
await this.exec('ROLLBACK;', [], timeout);
|
|
1196
|
+
this.log('error', '批量更新数据失败,事务已回滚', error);
|
|
1276
1197
|
}
|
|
1198
|
+
|
|
1199
|
+
this.log('success', `批量更新数据完成,共${list.length}条`);
|
|
1200
|
+
return final_res;
|
|
1277
1201
|
};
|
|
1278
1202
|
|
|
1279
1203
|
/* 辅助类 */
|
|
@@ -1490,36 +1414,30 @@ Sql.prototype.model = function (model) {
|
|
|
1490
1414
|
* @returns {Promise | object | null} 查询结果
|
|
1491
1415
|
*/
|
|
1492
1416
|
Sql.prototype.getObj = async function (query, sort, view, like, timeout = 30000) {
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
obj = this.model(obj);
|
|
1514
|
-
}
|
|
1417
|
+
// 保存当前分页设置
|
|
1418
|
+
const old_page = this.page;
|
|
1419
|
+
const size = this.size;
|
|
1420
|
+
|
|
1421
|
+
// 设置为只查询一条
|
|
1422
|
+
this.page = 1;
|
|
1423
|
+
this.size = 1;
|
|
1424
|
+
|
|
1425
|
+
// 使用优化后的get方法
|
|
1426
|
+
const list = await this.get(query, sort, view, like, timeout);
|
|
1427
|
+
|
|
1428
|
+
// 恢复分页设置
|
|
1429
|
+
this.page = old_page;
|
|
1430
|
+
this.size = size;
|
|
1431
|
+
|
|
1432
|
+
var obj = null;
|
|
1433
|
+
if (list.length > 0) {
|
|
1434
|
+
obj = list[0];
|
|
1435
|
+
if (this.key) {
|
|
1436
|
+
obj = this.model(obj);
|
|
1515
1437
|
}
|
|
1516
|
-
|
|
1517
|
-
return obj;
|
|
1518
|
-
} catch (err) {
|
|
1519
|
-
this.error = err.message;
|
|
1520
|
-
this.log('error', '查询单条数据失败', err);
|
|
1521
1438
|
}
|
|
1522
|
-
|
|
1439
|
+
|
|
1440
|
+
return obj;
|
|
1523
1441
|
};
|
|
1524
1442
|
|
|
1525
1443
|
/**
|
|
@@ -1528,65 +1446,61 @@ Sql.prototype.getObj = async function (query, sort, view, like, timeout = 30000)
|
|
|
1528
1446
|
* @returns {Promise<boolean>} 是否加载成功
|
|
1529
1447
|
*/
|
|
1530
1448
|
Sql.prototype.load = async function (file) {
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
}
|
|
1449
|
+
// 记录操作日志
|
|
1450
|
+
if (this.config && this.config.debug) {
|
|
1451
|
+
this.log('debug', `开始从文件加载数据库`, {
|
|
1452
|
+
file: file
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1538
1455
|
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1456
|
+
// 检查文件是否存在
|
|
1457
|
+
if (!file.hasFile()) {
|
|
1458
|
+
throw new Error(`SQL文件不存在: ${file}`);
|
|
1459
|
+
}
|
|
1543
1460
|
|
|
1544
|
-
|
|
1545
|
-
|
|
1461
|
+
// 读取SQL文件内容
|
|
1462
|
+
const sql_content = await $.file.readText(file);
|
|
1546
1463
|
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1464
|
+
if (!sql_content || sql_content.trim() === '') {
|
|
1465
|
+
throw new Error(`SQL文件内容为空: ${file}`);
|
|
1466
|
+
}
|
|
1550
1467
|
|
|
1551
|
-
|
|
1552
|
-
|
|
1468
|
+
// 分割SQL语句(按分号分割,但需要注意处理字符串中的分号)
|
|
1469
|
+
const sql_stmts = this._splitSqlStatements(sql_content);
|
|
1553
1470
|
|
|
1554
|
-
|
|
1555
|
-
|
|
1471
|
+
// 开始事务执行SQL语句
|
|
1472
|
+
await this.exec('BEGIN TRANSACTION');
|
|
1556
1473
|
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
}
|
|
1474
|
+
try {
|
|
1475
|
+
// 逐个执行SQL语句
|
|
1476
|
+
for (const sql of sql_stmts) {
|
|
1477
|
+
const trim_sql = sql.trim();
|
|
1478
|
+
if (trim_sql) {
|
|
1479
|
+
await this.exec(trim_sql);
|
|
1564
1480
|
}
|
|
1481
|
+
}
|
|
1565
1482
|
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
if (this.config && this.config.debug) {
|
|
1570
|
-
this.log('info', `数据库加载成功`, {
|
|
1571
|
-
file: file,
|
|
1572
|
-
stmt_num: sql_stmts.length
|
|
1573
|
-
});
|
|
1574
|
-
}
|
|
1483
|
+
// 提交事务
|
|
1484
|
+
await this.exec('COMMIT');
|
|
1575
1485
|
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
this.log('error', '事务回滚失败', rollback_err);
|
|
1486
|
+
if (this.config && this.config.debug) {
|
|
1487
|
+
this.log('info', `数据库加载成功`, {
|
|
1488
|
+
file: file,
|
|
1489
|
+
stmt_num: sql_stmts.length
|
|
1581
1490
|
});
|
|
1582
|
-
|
|
1583
|
-
// 记录错误日志
|
|
1584
|
-
this.log('error', 'SQL语句执行失败', err);
|
|
1585
1491
|
}
|
|
1586
|
-
|
|
1492
|
+
|
|
1493
|
+
return true;
|
|
1494
|
+
} catch (err) {
|
|
1495
|
+
// 回滚事务
|
|
1496
|
+
await this.exec('ROLLBACK').catch(rollback_err => {
|
|
1497
|
+
this.log('error', '事务回滚失败', rollback_err);
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1587
1500
|
// 记录错误日志
|
|
1588
|
-
this.log('error', '
|
|
1501
|
+
this.log('error', 'SQL语句执行失败', err);
|
|
1589
1502
|
}
|
|
1503
|
+
return false;
|
|
1590
1504
|
};
|
|
1591
1505
|
|
|
1592
1506
|
/**
|
|
@@ -1681,25 +1595,20 @@ Sql.prototype._splitSqlStatements = function (sql_content) {
|
|
|
1681
1595
|
* @private
|
|
1682
1596
|
*/
|
|
1683
1597
|
Sql.prototype._getCreateTableSql = async function () {
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
create_sql = create_sql.replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS');
|
|
1694
|
-
}
|
|
1695
|
-
return create_sql + ';';
|
|
1598
|
+
// 查询表结构信息(SQLite 使用 sqlite_master 表)
|
|
1599
|
+
const sql = `SELECT sql FROM sqlite_master WHERE type='table' AND name='${this.table}'`;
|
|
1600
|
+
const result = await this.run(sql);
|
|
1601
|
+
|
|
1602
|
+
if (result && result.length > 0 && result[0].sql) {
|
|
1603
|
+
// 在创建表语句中添加 IF NOT EXISTS 条件
|
|
1604
|
+
let create_sql = result[0].sql;
|
|
1605
|
+
if (create_sql.toUpperCase().startsWith('CREATE TABLE')) {
|
|
1606
|
+
create_sql = create_sql.replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS');
|
|
1696
1607
|
}
|
|
1697
|
-
|
|
1698
|
-
return '';
|
|
1699
|
-
} catch (err) {
|
|
1700
|
-
this.log('warn', '获取表结构失败,将仅备份数据', err);
|
|
1701
|
-
return '';
|
|
1608
|
+
return create_sql + ';';
|
|
1702
1609
|
}
|
|
1610
|
+
|
|
1611
|
+
return '';
|
|
1703
1612
|
};
|
|
1704
1613
|
|
|
1705
1614
|
/**
|
|
@@ -1717,54 +1626,48 @@ Sql.prototype.backup = async function (file, create = false) {
|
|
|
1717
1626
|
throw new TypeError('表名未设置');
|
|
1718
1627
|
}
|
|
1719
1628
|
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
const data = await this.getSql('', '', '*');
|
|
1629
|
+
// 获取所有数据
|
|
1630
|
+
const data = await this.getSql('', '', '*');
|
|
1723
1631
|
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1632
|
+
if (!data || !Array.isArray(data)) {
|
|
1633
|
+
throw new Error('获取数据失败');
|
|
1634
|
+
}
|
|
1727
1635
|
|
|
1728
|
-
|
|
1729
|
-
|
|
1636
|
+
// 构建 SQL 语句
|
|
1637
|
+
let sql_content = '';
|
|
1730
1638
|
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1639
|
+
// 添加表结构(简化版,实际应用中可能需要更复杂的表结构导出)
|
|
1640
|
+
sql_content += `-- 备份表: ${this.table}\n`;
|
|
1641
|
+
sql_content += `-- 备份时间: ${new Date().toISOString()}\n\n`;
|
|
1734
1642
|
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
}
|
|
1643
|
+
// 如果create为true,添加创建表语句
|
|
1644
|
+
if (create) {
|
|
1645
|
+
const create_table_sql = await this._getCreateTableSql();
|
|
1646
|
+
if (create_table_sql) {
|
|
1647
|
+
sql_content += create_table_sql + '\n\n';
|
|
1741
1648
|
}
|
|
1649
|
+
}
|
|
1742
1650
|
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1651
|
+
// 添加数据插入语句
|
|
1652
|
+
for (const row of data) {
|
|
1653
|
+
const keys = Object.keys(row).map(key => this.escapeId(key)).join(', ');
|
|
1654
|
+
const values = Object.values(row).map(value => this.escape(value)).join(', ');
|
|
1655
|
+
sql_content += `INSERT INTO \`${this.table}\` (${keys}) VALUES (${values});\n`;
|
|
1656
|
+
}
|
|
1749
1657
|
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1658
|
+
// 写入文件(这里需要文件系统模块,实际使用时需要引入 fs)
|
|
1659
|
+
const fs = require('fs');
|
|
1660
|
+
const path = require('path');
|
|
1753
1661
|
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1662
|
+
// 确保目录存在
|
|
1663
|
+
const dir = path.dirname(file);
|
|
1664
|
+
if (!fs.existsSync(dir)) {
|
|
1665
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1666
|
+
}
|
|
1759
1667
|
|
|
1760
|
-
|
|
1668
|
+
fs.writeFileSync(file, sql_content, 'utf8');
|
|
1761
1669
|
|
|
1762
|
-
|
|
1763
|
-
} catch (err) {
|
|
1764
|
-
this.error = err.message;
|
|
1765
|
-
this.log('error', '备份数据库失败', err);
|
|
1766
|
-
return false;
|
|
1767
|
-
}
|
|
1670
|
+
return true;
|
|
1768
1671
|
};
|
|
1769
1672
|
|
|
1770
1673
|
/**
|
|
@@ -1781,32 +1684,26 @@ Sql.prototype.restore = async function (file) {
|
|
|
1781
1684
|
throw new TypeError('表名未设置');
|
|
1782
1685
|
}
|
|
1783
1686
|
|
|
1784
|
-
|
|
1785
|
-
const fs = require('fs');
|
|
1687
|
+
const fs = require('fs');
|
|
1786
1688
|
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1689
|
+
if (!fs.existsSync(file)) {
|
|
1690
|
+
throw new Error('备份文件不存在');
|
|
1691
|
+
}
|
|
1790
1692
|
|
|
1791
|
-
|
|
1792
|
-
|
|
1693
|
+
// 读取 SQL 文件
|
|
1694
|
+
const sql_content = fs.readFileSync(file, 'utf8');
|
|
1793
1695
|
|
|
1794
|
-
|
|
1795
|
-
|
|
1696
|
+
// 解析 SQL 语句
|
|
1697
|
+
const stmts = this._parseSqlStatements(sql_content);
|
|
1796
1698
|
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
}
|
|
1699
|
+
// 执行每个 SQL 语句
|
|
1700
|
+
for (const stmt of stmts) {
|
|
1701
|
+
if (stmt.trim() && !stmt.startsWith('--')) {
|
|
1702
|
+
await this.exec(stmt);
|
|
1802
1703
|
}
|
|
1803
|
-
|
|
1804
|
-
return true;
|
|
1805
|
-
} catch (err) {
|
|
1806
|
-
this.error = err.message;
|
|
1807
|
-
this.log('error', '恢复数据库失败', err);
|
|
1808
|
-
return false;
|
|
1809
1704
|
}
|
|
1705
|
+
|
|
1706
|
+
return true;
|
|
1810
1707
|
};
|
|
1811
1708
|
|
|
1812
1709
|
/**
|