mm_mysql 2.4.0 → 2.4.2
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 +152 -223
- package/index.js +20 -53
- package/package.json +4 -4
- package/sql.js +303 -421
package/sql.js
CHANGED
|
@@ -605,15 +605,9 @@ Sql.prototype.add = async function (body) {
|
|
|
605
605
|
if (!this.table || !body || typeof body !== 'object') {
|
|
606
606
|
throw new Error('表名或数据未设置');
|
|
607
607
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
return await this.exec(sql);
|
|
612
|
-
} catch (err) {
|
|
613
|
-
this.error = err.message;
|
|
614
|
-
this.log('error', '添加数据失败', err);
|
|
615
|
-
}
|
|
616
|
-
return 0;
|
|
608
|
+
const sql = this.toAddSql(body);
|
|
609
|
+
this.sql = sql;
|
|
610
|
+
return await this.exec(sql);
|
|
617
611
|
};
|
|
618
612
|
|
|
619
613
|
/**
|
|
@@ -626,16 +620,10 @@ Sql.prototype.del = async function (query, like) {
|
|
|
626
620
|
if (!this.table) {
|
|
627
621
|
throw new Error('表名未设置');
|
|
628
622
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
return bl;
|
|
634
|
-
} catch (err) {
|
|
635
|
-
this.error = err.message;
|
|
636
|
-
this.log('error', '删除数据失败', err);
|
|
637
|
-
}
|
|
638
|
-
return 0;
|
|
623
|
+
const sql = this.toDelSql(query, like);
|
|
624
|
+
this.sql = sql;
|
|
625
|
+
const bl = await this.exec(sql);
|
|
626
|
+
return bl;
|
|
639
627
|
};
|
|
640
628
|
|
|
641
629
|
/**
|
|
@@ -649,16 +637,10 @@ Sql.prototype.set = async function (query, body, like) {
|
|
|
649
637
|
if (!this.table || !body || typeof body !== 'object') {
|
|
650
638
|
throw new Error('表名或数据未设置');
|
|
651
639
|
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
return bl;
|
|
657
|
-
} catch (err) {
|
|
658
|
-
this.error = err.message;
|
|
659
|
-
this.log('error', '修改数据失败', err);
|
|
660
|
-
}
|
|
661
|
-
return 0;
|
|
640
|
+
const sql = this.toSetSql(query, body, like);
|
|
641
|
+
this.sql = sql;
|
|
642
|
+
const bl = await this.exec(sql);
|
|
643
|
+
return bl;
|
|
662
644
|
};
|
|
663
645
|
|
|
664
646
|
/**
|
|
@@ -672,20 +654,14 @@ Sql.prototype.addOrSetSql = async function (where, set, like) {
|
|
|
672
654
|
if (!this.table || !where || !set) {
|
|
673
655
|
throw new Error('表名、条件或数据未设置');
|
|
674
656
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
const count = await this.countSql(where_str);
|
|
657
|
+
const where_str = await this._getWhereStr(where, like);
|
|
658
|
+
const count = await this.countSql(where_str);
|
|
678
659
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
}
|
|
684
|
-
} catch (err) {
|
|
685
|
-
this.error = err.message;
|
|
686
|
-
this.log('error', '添加或修改数据失败', err);
|
|
660
|
+
if (count === 0) {
|
|
661
|
+
return await this._addData(set);
|
|
662
|
+
} else {
|
|
663
|
+
return await this._updateData(where_str, set);
|
|
687
664
|
}
|
|
688
|
-
return 0;
|
|
689
665
|
};
|
|
690
666
|
|
|
691
667
|
Sql.prototype._getWhereStr = async function (where, like) {
|
|
@@ -749,16 +725,10 @@ Sql.prototype.get = async function (query, sort, view, like, timeout = 20000) {
|
|
|
749
725
|
throw new Error('表名未设置');
|
|
750
726
|
}
|
|
751
727
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
return list;
|
|
757
|
-
} catch (err) {
|
|
758
|
-
this.error = err.message;
|
|
759
|
-
this.log('error', '查询数据失败', err);
|
|
760
|
-
}
|
|
761
|
-
return [];
|
|
728
|
+
const sql = this.toGetSql(query, sort, view, like);
|
|
729
|
+
this.sql = sql;
|
|
730
|
+
const list = await this.run(sql);
|
|
731
|
+
return list;
|
|
762
732
|
};
|
|
763
733
|
|
|
764
734
|
/**
|
|
@@ -771,36 +741,30 @@ Sql.prototype.get = async function (query, sort, view, like, timeout = 20000) {
|
|
|
771
741
|
* @returns {Promise | object | null} 查询结果
|
|
772
742
|
*/
|
|
773
743
|
Sql.prototype.getObj = async function (query, sort, view, like, timeout = 20000) {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
obj = this.model(obj);
|
|
795
|
-
}
|
|
744
|
+
// 保存当前分页设置
|
|
745
|
+
const old_page = this.page;
|
|
746
|
+
const old_size = this.size;
|
|
747
|
+
|
|
748
|
+
// 设置为只查询一条
|
|
749
|
+
this.page = 1;
|
|
750
|
+
this.size = 1;
|
|
751
|
+
|
|
752
|
+
// 使用优化后的get方法
|
|
753
|
+
const list = await this.get(query, sort, view, like, timeout);
|
|
754
|
+
|
|
755
|
+
// 恢复分页设置
|
|
756
|
+
this.page = old_page;
|
|
757
|
+
this.size = old_size;
|
|
758
|
+
|
|
759
|
+
var obj = null;
|
|
760
|
+
if (list.length > 0) {
|
|
761
|
+
obj = list[0];
|
|
762
|
+
if (this.key) {
|
|
763
|
+
obj = this.model(obj);
|
|
796
764
|
}
|
|
797
|
-
|
|
798
|
-
return obj;
|
|
799
|
-
} catch (err) {
|
|
800
|
-
this.error = err.message;
|
|
801
|
-
this.log('error', '查询单条数据失败', err);
|
|
802
765
|
}
|
|
803
|
-
|
|
766
|
+
|
|
767
|
+
return obj;
|
|
804
768
|
};
|
|
805
769
|
|
|
806
770
|
/**
|
|
@@ -814,19 +778,12 @@ Sql.prototype.count = async function (query, like, timeout = 20000) {
|
|
|
814
778
|
if (!this.table) {
|
|
815
779
|
throw new Error('表名未设置');
|
|
816
780
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
const total = list && list[0] && list[0].num ? parseInt(list[0].num) : 0;
|
|
824
|
-
return total;
|
|
825
|
-
} catch (err) {
|
|
826
|
-
this.error = err.message;
|
|
827
|
-
this.log('error', '统计数据失败', err);
|
|
828
|
-
}
|
|
829
|
-
return 0;
|
|
781
|
+
const where = typeof query === 'string' ? query : await this.toWhere(query, like);
|
|
782
|
+
const sql = 'SELECT COUNT(*) as num FROM `' + this.table + '`' + (where ? ' WHERE ' + where : '');
|
|
783
|
+
this.sql = sql;
|
|
784
|
+
const list = await this.run(sql);
|
|
785
|
+
const total = list && list[0] && list[0].num ? parseInt(list[0].num) : 0;
|
|
786
|
+
return total;
|
|
830
787
|
};
|
|
831
788
|
|
|
832
789
|
/**
|
|
@@ -853,36 +810,31 @@ Sql.prototype.addList = async function (list, batch_size = 100, timeout = 60000)
|
|
|
853
810
|
if (!this.table || !Array.isArray(list) || list.length === 0) {
|
|
854
811
|
throw new Error('表名或数据列表未设置');
|
|
855
812
|
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
// 分批处理大数据量
|
|
865
|
-
const batches = Math.ceil(list.length / batch_size);
|
|
866
|
-
this.log('info', `开始分批添加数据,共${batches}批,每批${batch_size}条`);
|
|
813
|
+
// 如果数据量较小,
|
|
814
|
+
if (list.length <= batch_size) {
|
|
815
|
+
// 使用批量插入语法,不使用事务包装
|
|
816
|
+
this.sql = this.toBatchAddSql(list);
|
|
817
|
+
return await this.exec(this.sql, [], timeout);
|
|
818
|
+
}
|
|
867
819
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
const batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
872
|
-
this.log('debug', `处理第${i + 1}/${batches}批数据,${batch.length}条`);
|
|
820
|
+
// 分批处理大数据量
|
|
821
|
+
const batches = Math.ceil(list.length / batch_size);
|
|
822
|
+
this.log('info', `开始分批添加数据,共${batches}批,每批${batch_size}条`);
|
|
873
823
|
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
}
|
|
824
|
+
// 分批执行,每批一个单独的批量插入语句
|
|
825
|
+
let final_res = null;
|
|
826
|
+
for (let i = 0; i < batches; i++) {
|
|
827
|
+
const batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
828
|
+
this.log('debug', `处理第${i + 1}/${batches}批数据,${batch.length}条`);
|
|
879
829
|
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
this.
|
|
830
|
+
// 生成批量插入SQL
|
|
831
|
+
const batch_sql = this.toBatchAddSql(batch);
|
|
832
|
+
this.sql = batch_sql;
|
|
833
|
+
final_res = await this.exec(batch_sql);
|
|
884
834
|
}
|
|
885
|
-
|
|
835
|
+
|
|
836
|
+
this.log('info', `批量添加数据完成,共${list.length}条`);
|
|
837
|
+
return final_res;
|
|
886
838
|
};
|
|
887
839
|
|
|
888
840
|
/**
|
|
@@ -941,59 +893,49 @@ Sql.prototype.delList = async function (list, like, batch_size = 100, timeout =
|
|
|
941
893
|
if (!this.table || !Array.isArray(list) || list.length === 0) {
|
|
942
894
|
throw new Error('表名或数据列表未设置');
|
|
943
895
|
}
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
sql += this.toDelSql(item.query, like);
|
|
950
|
-
}
|
|
951
|
-
this.sql = sql;
|
|
952
|
-
return await this.exec(sql);
|
|
896
|
+
// 如果数据量较小,直接处理
|
|
897
|
+
if (list.length <= batch_size) {
|
|
898
|
+
let sql = '';
|
|
899
|
+
for (const item of list) {
|
|
900
|
+
sql += this.toDelSql(item.query, like);
|
|
953
901
|
}
|
|
902
|
+
this.sql = sql;
|
|
903
|
+
return await this.exec(sql);
|
|
904
|
+
}
|
|
954
905
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
// 使用事务包装整个操作以保证原子性
|
|
960
|
-
let final_res = null;
|
|
961
|
-
try {
|
|
962
|
-
// 开始事务
|
|
963
|
-
await this.exec('BEGIN;');
|
|
906
|
+
// 分批处理大数据量
|
|
907
|
+
const batches = Math.ceil(list.length / batch_size);
|
|
908
|
+
this.log('info', `开始分批删除数据,共${batches}批,每批${batch_size}条`);
|
|
964
909
|
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
910
|
+
// 使用事务包装整个操作以保证原子性
|
|
911
|
+
let final_res = null;
|
|
912
|
+
try {
|
|
913
|
+
// 开始事务
|
|
914
|
+
await this.exec('BEGIN;');
|
|
969
915
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
final_res = await this.exec(batch_sql);
|
|
975
|
-
}
|
|
916
|
+
// 分批处理exec删除语句
|
|
917
|
+
for (let i = 0; i < batches; i++) {
|
|
918
|
+
const batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
919
|
+
this.log('debug', `处理第${i + 1}/${batches}批数据,${batch.length}条`);
|
|
976
920
|
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
// 发生错误时回滚事务
|
|
981
|
-
try {
|
|
982
|
-
await this.exec('ROLLBACK;', [], timeout);
|
|
983
|
-
this.log('error', '批量删除数据失败,事务已回滚', error);
|
|
984
|
-
} catch (rollbackError) {
|
|
985
|
-
this.log('error', '批量删除数据失败,事务回滚也失败', rollbackError);
|
|
921
|
+
let batch_sql = '';
|
|
922
|
+
for (const item of batch) {
|
|
923
|
+
batch_sql += this.toDelSql(item.query, like);
|
|
986
924
|
}
|
|
987
|
-
|
|
925
|
+
final_res = await this.exec(batch_sql);
|
|
988
926
|
}
|
|
989
927
|
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
} catch (
|
|
993
|
-
|
|
994
|
-
this.
|
|
928
|
+
// 提交事务
|
|
929
|
+
await this.exec('COMMIT;');
|
|
930
|
+
} catch (error) {
|
|
931
|
+
// 发生错误时回滚事务
|
|
932
|
+
await this.exec('ROLLBACK;', [], timeout);
|
|
933
|
+
this.log('error', '批量删除数据失败,事务已回滚', error);
|
|
934
|
+
throw error; // 重新抛出错误
|
|
995
935
|
}
|
|
996
|
-
|
|
936
|
+
|
|
937
|
+
this.log('info', `批量删除数据完成,共${list.length}条`);
|
|
938
|
+
return final_res;
|
|
997
939
|
};
|
|
998
940
|
|
|
999
941
|
/**
|
|
@@ -1008,49 +950,38 @@ Sql.prototype.setList = async function (list, like = false, batch_size = 100, ti
|
|
|
1008
950
|
if (!this.table || !Array.isArray(list) || list.length === 0) {
|
|
1009
951
|
throw new Error('表名或数据列表未设置');
|
|
1010
952
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
this.log('info', `开始分批修改数据,共${batches}批,每批${batch_size}条`);
|
|
1015
|
-
|
|
1016
|
-
let final_res = null;
|
|
953
|
+
// 分批处理大数据量
|
|
954
|
+
const batches = Math.ceil(list.length / batch_size);
|
|
955
|
+
this.log('info', `开始分批修改数据,共${batches}批,每批${batch_size}条`);
|
|
1017
956
|
|
|
1018
|
-
|
|
1019
|
-
try {
|
|
1020
|
-
// 开始事务
|
|
1021
|
-
await this.exec('BEGIN;');
|
|
957
|
+
let final_res = null;
|
|
1022
958
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
959
|
+
// 使用事务包装整个操作以保证原子性
|
|
960
|
+
try {
|
|
961
|
+
// 开始事务
|
|
962
|
+
await this.exec('BEGIN;');
|
|
1027
963
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
964
|
+
// 逐条处理记录,保持简单可靠
|
|
965
|
+
for (let i = 0; i < list.length; i++) {
|
|
966
|
+
const item = list[i];
|
|
967
|
+
this.log('debug', `处理第${i + 1}/${list.length}条记录`);
|
|
1031
968
|
|
|
1032
|
-
|
|
1033
|
-
|
|
969
|
+
// 生成单个更新SQL
|
|
970
|
+
const sql = this.toSetSql(item.query, item.item, like);
|
|
971
|
+
this.sql = sql;
|
|
1034
972
|
|
|
1035
|
-
|
|
1036
|
-
await this.exec('COMMIT;');
|
|
1037
|
-
} catch (error) {
|
|
1038
|
-
// 发生错误时回滚事务
|
|
1039
|
-
try {
|
|
1040
|
-
await this.exec('ROLLBACK;', [], timeout);
|
|
1041
|
-
this.log('error', '批量修改数据失败,事务已回滚', error);
|
|
1042
|
-
} catch (rollbackError) {
|
|
1043
|
-
this.log('error', '批量修改数据失败,事务回滚也失败', rollbackError);
|
|
1044
|
-
}
|
|
1045
|
-
throw error; // 重新抛出错误
|
|
973
|
+
final_res = await this.exec(sql, [], timeout);
|
|
1046
974
|
}
|
|
1047
975
|
|
|
1048
|
-
|
|
976
|
+
// 提交事务
|
|
977
|
+
await this.exec('COMMIT;');
|
|
1049
978
|
} catch (error) {
|
|
1050
|
-
this.
|
|
1051
|
-
this.log('error', '
|
|
979
|
+
await this.exec('ROLLBACK;', [], timeout);
|
|
980
|
+
this.log('error', '批量修改数据失败,事务已回滚', error);
|
|
981
|
+
throw error; // 重新抛出错误
|
|
1052
982
|
}
|
|
1053
|
-
|
|
983
|
+
|
|
984
|
+
return final_res;
|
|
1054
985
|
};
|
|
1055
986
|
|
|
1056
987
|
/* 辅助类 */
|
|
@@ -1272,15 +1203,11 @@ Sql.prototype.tplBody = function (param_dt, sql_dt) {
|
|
|
1272
1203
|
* @returns {Promise<boolean>} 是否加载成功
|
|
1273
1204
|
*/
|
|
1274
1205
|
Sql.prototype.load = async function (file) {
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
} catch (error) {
|
|
1281
|
-
this.log('error', '数据库加载失败', error);
|
|
1282
|
-
}
|
|
1283
|
-
return false;
|
|
1206
|
+
|
|
1207
|
+
await this._logLoadStart(file);
|
|
1208
|
+
await this._validateFile(file);
|
|
1209
|
+
const sql_stmts = await this._readAndSplitSql(file);
|
|
1210
|
+
return await this._execSqlStmts(file, sql_stmts);
|
|
1284
1211
|
};
|
|
1285
1212
|
|
|
1286
1213
|
/**
|
|
@@ -1565,20 +1492,14 @@ Sql.prototype.addOrSet = async function (where, set, like) {
|
|
|
1565
1492
|
if (!this.table || !where || !set) {
|
|
1566
1493
|
throw new Error('表名、条件或数据未设置');
|
|
1567
1494
|
}
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
const count = await this.countSql(where_str);
|
|
1495
|
+
const where_str = await this._getWhereStr(where, like);
|
|
1496
|
+
const count = await this.countSql(where_str);
|
|
1571
1497
|
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
}
|
|
1577
|
-
} catch (err) {
|
|
1578
|
-
this.error = err.message;
|
|
1579
|
-
this.log('error', '添加或修改数据失败', err);
|
|
1498
|
+
if (count === 0) {
|
|
1499
|
+
return await this._addData(set);
|
|
1500
|
+
} else {
|
|
1501
|
+
return await this._updateData(where_str, set);
|
|
1580
1502
|
}
|
|
1581
|
-
return 0;
|
|
1582
1503
|
};
|
|
1583
1504
|
|
|
1584
1505
|
/**
|
|
@@ -1587,25 +1508,20 @@ Sql.prototype.addOrSet = async function (where, set, like) {
|
|
|
1587
1508
|
* @private
|
|
1588
1509
|
*/
|
|
1589
1510
|
Sql.prototype._getCreateTableSql = async function () {
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
create_sql = create_sql.replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS');
|
|
1600
|
-
}
|
|
1601
|
-
return create_sql + ';';
|
|
1511
|
+
// 查询表结构信息(MySQL 使用 SHOW CREATE TABLE)
|
|
1512
|
+
const sql = `SHOW CREATE TABLE \`${this.table}\``;
|
|
1513
|
+
const result = await this.run(sql);
|
|
1514
|
+
|
|
1515
|
+
if (result && result.length > 0 && result[0]['Create Table']) {
|
|
1516
|
+
// 在创建表语句中添加 IF NOT EXISTS 条件
|
|
1517
|
+
let create_sql = result[0]['Create Table'];
|
|
1518
|
+
if (create_sql.toUpperCase().startsWith('CREATE TABLE')) {
|
|
1519
|
+
create_sql = create_sql.replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS');
|
|
1602
1520
|
}
|
|
1603
|
-
|
|
1604
|
-
return '';
|
|
1605
|
-
} catch (err) {
|
|
1606
|
-
this.log('warn', '获取表结构失败,将仅备份数据', err);
|
|
1607
|
-
return '';
|
|
1521
|
+
return create_sql + ';';
|
|
1608
1522
|
}
|
|
1523
|
+
|
|
1524
|
+
return '';
|
|
1609
1525
|
};
|
|
1610
1526
|
|
|
1611
1527
|
/**
|
|
@@ -1614,27 +1530,26 @@ Sql.prototype._getCreateTableSql = async function () {
|
|
|
1614
1530
|
* @private
|
|
1615
1531
|
*/
|
|
1616
1532
|
Sql.prototype._getTableComments = async function () {
|
|
1617
|
-
|
|
1618
|
-
let commentSql = '';
|
|
1533
|
+
let commentSql = '';
|
|
1619
1534
|
|
|
1620
|
-
|
|
1621
|
-
|
|
1535
|
+
// 获取表注释
|
|
1536
|
+
const tableCommentSql = `
|
|
1622
1537
|
SELECT TABLE_COMMENT
|
|
1623
1538
|
FROM information_schema.TABLES
|
|
1624
1539
|
WHERE TABLE_SCHEMA = DATABASE()
|
|
1625
1540
|
AND TABLE_NAME = '${this.table}'
|
|
1626
1541
|
`;
|
|
1627
|
-
|
|
1542
|
+
const tableCommentResult = await this.run(tableCommentSql);
|
|
1628
1543
|
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
}
|
|
1544
|
+
if (tableCommentResult && tableCommentResult.length > 0 && tableCommentResult[0].TABLE_COMMENT) {
|
|
1545
|
+
const tableComment = tableCommentResult[0].TABLE_COMMENT;
|
|
1546
|
+
if (tableComment && tableComment.trim() !== '') {
|
|
1547
|
+
commentSql += `\n-- 表注释: ${tableComment}\n`;
|
|
1634
1548
|
}
|
|
1549
|
+
}
|
|
1635
1550
|
|
|
1636
|
-
|
|
1637
|
-
|
|
1551
|
+
// 获取列注释
|
|
1552
|
+
const columnCommentSql = `
|
|
1638
1553
|
SELECT COLUMN_NAME, COLUMN_COMMENT
|
|
1639
1554
|
FROM information_schema.COLUMNS
|
|
1640
1555
|
WHERE TABLE_SCHEMA = DATABASE()
|
|
@@ -1643,20 +1558,16 @@ Sql.prototype._getTableComments = async function () {
|
|
|
1643
1558
|
AND COLUMN_COMMENT != ''
|
|
1644
1559
|
ORDER BY ORDINAL_POSITION
|
|
1645
1560
|
`;
|
|
1646
|
-
|
|
1561
|
+
const columnCommentResult = await this.run(columnCommentSql);
|
|
1647
1562
|
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
}
|
|
1563
|
+
if (columnCommentResult && columnCommentResult.length > 0) {
|
|
1564
|
+
commentSql += '\n-- 列注释:\n';
|
|
1565
|
+
for (const column of columnCommentResult) {
|
|
1566
|
+
commentSql += `-- ${column.COLUMN_NAME}: ${column.COLUMN_COMMENT}\n`;
|
|
1653
1567
|
}
|
|
1654
|
-
|
|
1655
|
-
return commentSql;
|
|
1656
|
-
} catch (err) {
|
|
1657
|
-
this.log('warn', '获取表注释信息失败,将仅备份表结构', err);
|
|
1658
|
-
return '';
|
|
1659
1568
|
}
|
|
1569
|
+
|
|
1570
|
+
return commentSql;
|
|
1660
1571
|
};
|
|
1661
1572
|
|
|
1662
1573
|
/**
|
|
@@ -1674,60 +1585,54 @@ Sql.prototype.backup = async function (file, create = false) {
|
|
|
1674
1585
|
throw new TypeError('表名未设置');
|
|
1675
1586
|
}
|
|
1676
1587
|
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
const data = await this.getSql('', '', '*');
|
|
1588
|
+
// 获取所有数据
|
|
1589
|
+
const data = await this.getSql('', '', '*');
|
|
1680
1590
|
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1591
|
+
if (!data || !Array.isArray(data)) {
|
|
1592
|
+
throw new Error('获取数据失败');
|
|
1593
|
+
}
|
|
1684
1594
|
|
|
1685
|
-
|
|
1686
|
-
|
|
1595
|
+
// 构建 SQL 语句
|
|
1596
|
+
let sqlContent = '';
|
|
1687
1597
|
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1598
|
+
// 添加表结构(简化版,实际应用中可能需要更复杂的表结构导出)
|
|
1599
|
+
sqlContent += `-- 备份表: ${this.table}\n`;
|
|
1600
|
+
sqlContent += `-- 备份时间: ${new Date().toISOString()}\n\n`;
|
|
1691
1601
|
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1602
|
+
// 如果create为true,添加创建表语句
|
|
1603
|
+
if (create) {
|
|
1604
|
+
const createTableSql = await this._getCreateTableSql();
|
|
1605
|
+
if (createTableSql) {
|
|
1606
|
+
sqlContent += createTableSql + '\n\n';
|
|
1697
1607
|
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
}
|
|
1608
|
+
// 添加表注释信息
|
|
1609
|
+
const commentSql = await this._getTableComments();
|
|
1610
|
+
if (commentSql) {
|
|
1611
|
+
sqlContent += commentSql + '\n';
|
|
1703
1612
|
}
|
|
1704
1613
|
}
|
|
1614
|
+
}
|
|
1705
1615
|
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1616
|
+
// 添加数据插入语句
|
|
1617
|
+
for (const row of data) {
|
|
1618
|
+
const keys = Object.keys(row).map(key => this.escapeId(key)).join(', ');
|
|
1619
|
+
const values = Object.values(row).map(value => this.escape(value)).join(', ');
|
|
1620
|
+
sqlContent += `INSERT INTO \`${this.table}\` (${keys}) VALUES (${values});\n`;
|
|
1621
|
+
}
|
|
1712
1622
|
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1623
|
+
// 写入文件(这里需要文件系统模块,实际使用时需要引入 fs)
|
|
1624
|
+
const fs = require('fs');
|
|
1625
|
+
const path = require('path');
|
|
1716
1626
|
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1627
|
+
// 确保目录存在
|
|
1628
|
+
const dir = path.dirname(file);
|
|
1629
|
+
if (!fs.existsSync(dir)) {
|
|
1630
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1631
|
+
}
|
|
1722
1632
|
|
|
1723
|
-
|
|
1633
|
+
fs.writeFileSync(file, sql_content, 'utf8');
|
|
1724
1634
|
|
|
1725
|
-
|
|
1726
|
-
} catch (err) {
|
|
1727
|
-
this.error = err.message;
|
|
1728
|
-
this.log('error', '备份数据库失败', err);
|
|
1729
|
-
return false;
|
|
1730
|
-
}
|
|
1635
|
+
return true;
|
|
1731
1636
|
};
|
|
1732
1637
|
|
|
1733
1638
|
/**
|
|
@@ -1744,32 +1649,26 @@ Sql.prototype.restore = async function (file) {
|
|
|
1744
1649
|
throw new TypeError('表名未设置');
|
|
1745
1650
|
}
|
|
1746
1651
|
|
|
1747
|
-
|
|
1748
|
-
const fs = require('fs');
|
|
1652
|
+
const fs = require('fs');
|
|
1749
1653
|
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1654
|
+
if (!fs.existsSync(file)) {
|
|
1655
|
+
throw new Error('备份文件不存在');
|
|
1656
|
+
}
|
|
1753
1657
|
|
|
1754
|
-
|
|
1755
|
-
|
|
1658
|
+
// 读取 SQL 文件
|
|
1659
|
+
const sqlContent = fs.readFileSync(file, 'utf8');
|
|
1756
1660
|
|
|
1757
|
-
|
|
1758
|
-
|
|
1661
|
+
// 解析 SQL 语句
|
|
1662
|
+
const statements = this._parseSqlStatements(sqlContent);
|
|
1759
1663
|
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
}
|
|
1664
|
+
// 执行每个 SQL 语句
|
|
1665
|
+
for (const stmt of statements) {
|
|
1666
|
+
if (stmt.trim() && !stmt.startsWith('--')) {
|
|
1667
|
+
await this.exec(stmt);
|
|
1765
1668
|
}
|
|
1766
|
-
|
|
1767
|
-
return true;
|
|
1768
|
-
} catch (err) {
|
|
1769
|
-
this.error = err.message;
|
|
1770
|
-
this.log('error', '恢复数据库失败', err);
|
|
1771
|
-
return false;
|
|
1772
1669
|
}
|
|
1670
|
+
|
|
1671
|
+
return true;
|
|
1773
1672
|
};
|
|
1774
1673
|
|
|
1775
1674
|
/**
|
|
@@ -1865,54 +1764,48 @@ Sql.prototype.backup = async function (file, create = false) {
|
|
|
1865
1764
|
throw new TypeError('表名未设置');
|
|
1866
1765
|
}
|
|
1867
1766
|
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
const data = await this.getSql('', '', '*');
|
|
1767
|
+
// 获取所有数据
|
|
1768
|
+
const data = await this.getSql('', '', '*');
|
|
1871
1769
|
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1770
|
+
if (!data || !Array.isArray(data)) {
|
|
1771
|
+
throw new Error('获取数据失败');
|
|
1772
|
+
}
|
|
1875
1773
|
|
|
1876
|
-
|
|
1877
|
-
|
|
1774
|
+
// 构建 SQL 语句
|
|
1775
|
+
let sql_content = '';
|
|
1878
1776
|
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1777
|
+
// 添加表结构(简化版,实际应用中可能需要更复杂的表结构导出)
|
|
1778
|
+
sql_content += `-- 备份表: ${this.table}\n`;
|
|
1779
|
+
sql_content += `-- 备份时间: ${new Date().toISOString()}\n\n`;
|
|
1882
1780
|
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
}
|
|
1781
|
+
// 如果create为true,添加创建表语句
|
|
1782
|
+
if (create) {
|
|
1783
|
+
const create_table_sql = await this._getCreateTableSql();
|
|
1784
|
+
if (create_table_sql) {
|
|
1785
|
+
sql_content += create_table_sql + '\n\n';
|
|
1889
1786
|
}
|
|
1787
|
+
}
|
|
1890
1788
|
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1789
|
+
// 添加数据插入语句
|
|
1790
|
+
for (const row of data) {
|
|
1791
|
+
const keys = Object.keys(row).map(key => this.escapeId(key)).join(', ');
|
|
1792
|
+
const values = Object.values(row).map(value => this.escape(value)).join(', ');
|
|
1793
|
+
sql_content += `INSERT INTO \`${this.table}\` (${keys}) VALUES (${values});\n`;
|
|
1794
|
+
}
|
|
1897
1795
|
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1796
|
+
// 写入文件(这里需要文件系统模块,实际使用时需要引入 fs)
|
|
1797
|
+
const fs = require('fs');
|
|
1798
|
+
const path = require('path');
|
|
1901
1799
|
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1800
|
+
// 确保目录存在
|
|
1801
|
+
const dir = path.dirname(file);
|
|
1802
|
+
if (!fs.existsSync(dir)) {
|
|
1803
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1804
|
+
}
|
|
1907
1805
|
|
|
1908
|
-
|
|
1806
|
+
fs.writeFileSync(file, sql_content, 'utf8');
|
|
1909
1807
|
|
|
1910
|
-
|
|
1911
|
-
} catch (err) {
|
|
1912
|
-
this.error = err.message;
|
|
1913
|
-
this.log('error', '备份数据库失败', err);
|
|
1914
|
-
return false;
|
|
1915
|
-
}
|
|
1808
|
+
return true;
|
|
1916
1809
|
};
|
|
1917
1810
|
|
|
1918
1811
|
/**
|
|
@@ -1929,65 +1822,59 @@ Sql.prototype.restore = async function (file) {
|
|
|
1929
1822
|
throw new TypeError('表名未设置');
|
|
1930
1823
|
}
|
|
1931
1824
|
|
|
1932
|
-
|
|
1933
|
-
const fs = require('fs');
|
|
1934
|
-
|
|
1935
|
-
// 检查文件是否存在
|
|
1936
|
-
if (!fs.existsSync(file)) {
|
|
1937
|
-
throw new Error('备份文件不存在');
|
|
1938
|
-
}
|
|
1825
|
+
const fs = require('fs');
|
|
1939
1826
|
|
|
1940
|
-
|
|
1941
|
-
|
|
1827
|
+
// 检查文件是否存在
|
|
1828
|
+
if (!fs.existsSync(file)) {
|
|
1829
|
+
throw new Error('备份文件不存在');
|
|
1830
|
+
}
|
|
1942
1831
|
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
}
|
|
1832
|
+
// 读取文件内容
|
|
1833
|
+
const sql_content = fs.readFileSync(file, 'utf8');
|
|
1946
1834
|
|
|
1947
|
-
|
|
1948
|
-
|
|
1835
|
+
if (!sql_content) {
|
|
1836
|
+
throw new Error('备份文件为空');
|
|
1837
|
+
}
|
|
1949
1838
|
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
}
|
|
1839
|
+
// 解析 SQL 语句
|
|
1840
|
+
const sql_stmts = this._parseSqlStatements(sql_content);
|
|
1953
1841
|
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
try {
|
|
1958
|
-
// 逐个执行SQL语句
|
|
1959
|
-
for (const sql of sql_stmts) {
|
|
1960
|
-
const trim_sql = sql.trim();
|
|
1961
|
-
if (trim_sql) {
|
|
1962
|
-
await this.exec(trim_sql);
|
|
1963
|
-
}
|
|
1964
|
-
}
|
|
1842
|
+
if (!sql_stmts || sql_stmts.length === 0) {
|
|
1843
|
+
throw new Error('未找到有效的 SQL 语句');
|
|
1844
|
+
}
|
|
1965
1845
|
|
|
1966
|
-
|
|
1967
|
-
|
|
1846
|
+
// 开始事务
|
|
1847
|
+
await this.exec('START TRANSACTION');
|
|
1968
1848
|
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1849
|
+
try {
|
|
1850
|
+
// 逐个执行SQL语句
|
|
1851
|
+
for (const sql of sql_stmts) {
|
|
1852
|
+
const trim_sql = sql.trim();
|
|
1853
|
+
if (trim_sql) {
|
|
1854
|
+
await this.exec(trim_sql);
|
|
1974
1855
|
}
|
|
1856
|
+
}
|
|
1975
1857
|
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
// 回滚事务
|
|
1979
|
-
await this.exec('ROLLBACK').catch(rollback_err => {
|
|
1980
|
-
this.log('error', '事务回滚失败', rollback_err);
|
|
1981
|
-
});
|
|
1858
|
+
// 提交事务
|
|
1859
|
+
await this.exec('COMMIT');
|
|
1982
1860
|
|
|
1983
|
-
|
|
1984
|
-
this.log('
|
|
1985
|
-
|
|
1861
|
+
if (this.config && this.config.debug) {
|
|
1862
|
+
this.log('info', '数据库恢复成功', {
|
|
1863
|
+
file: file,
|
|
1864
|
+
stmt_num: sql_stmts.length
|
|
1865
|
+
});
|
|
1986
1866
|
}
|
|
1867
|
+
|
|
1868
|
+
return true;
|
|
1987
1869
|
} catch (err) {
|
|
1988
|
-
|
|
1989
|
-
this.
|
|
1990
|
-
|
|
1870
|
+
// 回滚事务
|
|
1871
|
+
await this.exec('ROLLBACK').catch(rollback_err => {
|
|
1872
|
+
this.log('error', '事务回滚失败', rollback_err);
|
|
1873
|
+
});
|
|
1874
|
+
|
|
1875
|
+
// 记录错误日志
|
|
1876
|
+
this.log('error', 'SQL语句执行失败', err);
|
|
1877
|
+
throw err;
|
|
1991
1878
|
}
|
|
1992
1879
|
};
|
|
1993
1880
|
|
|
@@ -1997,25 +1884,20 @@ Sql.prototype.restore = async function (file) {
|
|
|
1997
1884
|
* @private
|
|
1998
1885
|
*/
|
|
1999
1886
|
Sql.prototype._getCreateTableSql = async function () {
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
create_sql = create_sql.replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS');
|
|
2010
|
-
}
|
|
2011
|
-
return create_sql + ';';
|
|
1887
|
+
// 查询表结构信息(MySQL 使用 SHOW CREATE TABLE)
|
|
1888
|
+
const sql = `SHOW CREATE TABLE \`${this.table}\``;
|
|
1889
|
+
const result = await this.run(sql);
|
|
1890
|
+
|
|
1891
|
+
if (result && result.length > 0 && result[0]['Create Table']) {
|
|
1892
|
+
// 在创建表语句中添加 IF NOT EXISTS 条件
|
|
1893
|
+
let create_sql = result[0]['Create Table'];
|
|
1894
|
+
if (create_sql.toUpperCase().startsWith('CREATE TABLE')) {
|
|
1895
|
+
create_sql = create_sql.replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS');
|
|
2012
1896
|
}
|
|
2013
|
-
|
|
2014
|
-
return '';
|
|
2015
|
-
} catch (err) {
|
|
2016
|
-
this.log('warn', '获取表结构失败,将仅备份数据', err);
|
|
2017
|
-
return '';
|
|
1897
|
+
return create_sql + ';';
|
|
2018
1898
|
}
|
|
1899
|
+
|
|
1900
|
+
return '';
|
|
2019
1901
|
};
|
|
2020
1902
|
|
|
2021
1903
|
module.exports = {
|