mm_mysql 2.4.1 → 2.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 +152 -223
- package/index.js +19 -24
- package/package.json +4 -4
- package/sql.js +381 -499
- package/test2.js +381 -0
package/sql.js
CHANGED
|
@@ -301,7 +301,7 @@ Sql.prototype.getCountSql = async function (where, sort, view) {
|
|
|
301
301
|
* @returns {Promise | object} 查询到的内容列表和符合条件总数
|
|
302
302
|
*/
|
|
303
303
|
Sql.prototype.groupMathSql = async function (where, groupby, view, sort, method) {
|
|
304
|
-
|
|
304
|
+
let view_val = view || '*';
|
|
305
305
|
var view_str = '';
|
|
306
306
|
if (view_val.indexOf(',') !== -1) {
|
|
307
307
|
var arr = view_val.split(',');
|
|
@@ -428,7 +428,7 @@ Sql.prototype.groupCount = async function (query, groupby, view, sort) {
|
|
|
428
428
|
*/
|
|
429
429
|
Sql.prototype.toWhere = function (obj, like) {
|
|
430
430
|
var where = '';
|
|
431
|
-
|
|
431
|
+
let use_like = like === undefined ? this.like : like;
|
|
432
432
|
if (use_like) {
|
|
433
433
|
where = this._buildLikeWhere(obj);
|
|
434
434
|
} else {
|
|
@@ -498,7 +498,7 @@ Sql.prototype._buildExactWhere = function (obj) {
|
|
|
498
498
|
*/
|
|
499
499
|
Sql.prototype.toSet = function (obj) {
|
|
500
500
|
var set = '';
|
|
501
|
-
for (
|
|
501
|
+
for (let k in obj) {
|
|
502
502
|
|
|
503
503
|
let val = obj[k];
|
|
504
504
|
if (val === undefined || val === null) continue;
|
|
@@ -507,10 +507,10 @@ Sql.prototype.toSet = function (obj) {
|
|
|
507
507
|
val = escape(val);
|
|
508
508
|
|
|
509
509
|
if (k.endsWith('_add')) {
|
|
510
|
-
|
|
510
|
+
let k2 = escapeId(k.replace('_add', ''));
|
|
511
511
|
set += ',' + k2 + ' = ' + k2 + ' + ' + val;
|
|
512
512
|
} else if (k.endsWith('_del')) {
|
|
513
|
-
|
|
513
|
+
let k3 = escapeId(k.replace('_del', ''));
|
|
514
514
|
set += ',' + k3 + ' = ' + k3 + ' - ' + val;
|
|
515
515
|
} else {
|
|
516
516
|
set += ',' + escapeId(k) + ' = ' + val;
|
|
@@ -533,13 +533,13 @@ Sql.prototype.toAddSql = function (item) {
|
|
|
533
533
|
let key = '';
|
|
534
534
|
let value = '';
|
|
535
535
|
|
|
536
|
-
for (
|
|
536
|
+
for (let k in item) {
|
|
537
537
|
key += ',' + escapeId(k);
|
|
538
538
|
value += ',' + escape(item[k]);
|
|
539
539
|
}
|
|
540
540
|
|
|
541
541
|
// 使用直接拼接的方式,避免模板替换错误
|
|
542
|
-
|
|
542
|
+
let sql = `INSERT INTO \`${this.table}\` (${key.replace(',', '')}) VALUES (${value.replace(',', '')});`;
|
|
543
543
|
|
|
544
544
|
return sql;
|
|
545
545
|
};
|
|
@@ -554,10 +554,10 @@ Sql.prototype.toDelSql = function (query, like) {
|
|
|
554
554
|
if (!this.table) {
|
|
555
555
|
throw new Error('表名未设置');
|
|
556
556
|
}
|
|
557
|
-
|
|
557
|
+
let where = this.toWhere(query, like);
|
|
558
558
|
|
|
559
559
|
// 使用直接拼接的方式,避免模板替换错误
|
|
560
|
-
|
|
560
|
+
let sql = `DELETE FROM \`${this.table}\` WHERE ${where};`;
|
|
561
561
|
|
|
562
562
|
return sql;
|
|
563
563
|
};
|
|
@@ -573,11 +573,11 @@ Sql.prototype.toSetSql = function (query, item, like) {
|
|
|
573
573
|
if (!this.table) {
|
|
574
574
|
throw new Error('表名未设置');
|
|
575
575
|
}
|
|
576
|
-
|
|
577
|
-
|
|
576
|
+
let where = this.toWhere(query, like);
|
|
577
|
+
let set = this.toSet(item);
|
|
578
578
|
|
|
579
579
|
// 使用直接拼接的方式,避免模板替换错误
|
|
580
|
-
|
|
580
|
+
let sql = `UPDATE \`${this.table}\` SET ${set} WHERE ${where};`;
|
|
581
581
|
|
|
582
582
|
return sql;
|
|
583
583
|
};
|
|
@@ -591,7 +591,7 @@ Sql.prototype.toSetSql = function (query, item, like) {
|
|
|
591
591
|
* @returns {string} sql语句
|
|
592
592
|
*/
|
|
593
593
|
Sql.prototype.toGetSql = function (query, sort, view, like) {
|
|
594
|
-
|
|
594
|
+
let where = this.toWhere(query, like);
|
|
595
595
|
return this.toQuery(where, sort, view);
|
|
596
596
|
};
|
|
597
597
|
|
|
@@ -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
|
+
let 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
|
+
let sql = this.toDelSql(query, like);
|
|
624
|
+
this.sql = sql;
|
|
625
|
+
let 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
|
+
let sql = this.toSetSql(query, body, like);
|
|
641
|
+
this.sql = sql;
|
|
642
|
+
let 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
|
+
let where_str = await this._getWhereStr(where, like);
|
|
658
|
+
let 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) {
|
|
@@ -696,7 +672,7 @@ Sql.prototype._getWhereStr = async function (where, like) {
|
|
|
696
672
|
};
|
|
697
673
|
|
|
698
674
|
Sql.prototype._addData = async function (set) {
|
|
699
|
-
|
|
675
|
+
let { key_str, val_str } = await this._parseSetData(set);
|
|
700
676
|
return await this.addSql(key_str, val_str);
|
|
701
677
|
};
|
|
702
678
|
|
|
@@ -713,16 +689,16 @@ Sql.prototype._parseSetData = async function (set) {
|
|
|
713
689
|
let val_str = '';
|
|
714
690
|
|
|
715
691
|
if (typeof set === 'string') {
|
|
716
|
-
|
|
717
|
-
for (
|
|
718
|
-
|
|
692
|
+
let arr = set.split(',');
|
|
693
|
+
for (let o of arr) {
|
|
694
|
+
let ar = o.split('=');
|
|
719
695
|
if (ar.length === 2) {
|
|
720
696
|
key_str += ',' + ar[0];
|
|
721
697
|
val_str += ',' + ar[1];
|
|
722
698
|
}
|
|
723
699
|
}
|
|
724
700
|
} else {
|
|
725
|
-
for (
|
|
701
|
+
for (let k in set) {
|
|
726
702
|
key_str += ',' + escapeId(k);
|
|
727
703
|
let val = set[k];
|
|
728
704
|
val_str += ',' + escape(val);
|
|
@@ -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
|
+
let sql = this.toGetSql(query, sort, view, like);
|
|
729
|
+
this.sql = sql;
|
|
730
|
+
let 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
|
+
let old_page = this.page;
|
|
746
|
+
let old_size = this.size;
|
|
747
|
+
|
|
748
|
+
// 设置为只查询一条
|
|
749
|
+
this.page = 1;
|
|
750
|
+
this.size = 1;
|
|
751
|
+
|
|
752
|
+
// 使用优化后的get方法
|
|
753
|
+
let 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
|
+
let where = typeof query === 'string' ? query : await this.toWhere(query, like);
|
|
782
|
+
let sql = 'SELECT COUNT(*) as num FROM `' + this.table + '`' + (where ? ' WHERE ' + where : '');
|
|
783
|
+
this.sql = sql;
|
|
784
|
+
let list = await this.run(sql);
|
|
785
|
+
let 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
|
+
let 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
|
+
let 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
|
+
let 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
|
/**
|
|
@@ -895,32 +847,32 @@ Sql.prototype.toBatchAddSql = function (list) {
|
|
|
895
847
|
throw new Error('表名或数据列表未设置');
|
|
896
848
|
}
|
|
897
849
|
|
|
898
|
-
|
|
850
|
+
let first_obj = list[0];
|
|
899
851
|
if (!first_obj || typeof first_obj !== 'object') {
|
|
900
852
|
throw new Error('数据格式错误');
|
|
901
853
|
}
|
|
902
854
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
855
|
+
let keys = this._getKeys(first_obj);
|
|
856
|
+
let columns = keys.join(',');
|
|
857
|
+
let val_list = this._buildValueList(list, keys);
|
|
906
858
|
|
|
907
859
|
return `INSERT INTO \`${this.table}\` (${columns}) VALUES ${val_list.join(',')}`;
|
|
908
860
|
};
|
|
909
861
|
|
|
910
862
|
Sql.prototype._getKeys = function (obj) {
|
|
911
|
-
|
|
912
|
-
for (
|
|
863
|
+
let keys = [];
|
|
864
|
+
for (let k in obj) {
|
|
913
865
|
keys.push(this.escapeId(k));
|
|
914
866
|
}
|
|
915
867
|
return keys;
|
|
916
868
|
};
|
|
917
869
|
|
|
918
870
|
Sql.prototype._buildValueList = function (list, keys) {
|
|
919
|
-
|
|
920
|
-
for (
|
|
921
|
-
|
|
922
|
-
for (
|
|
923
|
-
|
|
871
|
+
let val_list = [];
|
|
872
|
+
for (let item of list) {
|
|
873
|
+
let values = [];
|
|
874
|
+
for (let k of keys) {
|
|
875
|
+
let key = k.replace(/`/g, '');
|
|
924
876
|
let val = item[key];
|
|
925
877
|
values.push(this.escape(val));
|
|
926
878
|
}
|
|
@@ -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 (let 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
|
+
let 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
|
+
let 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 (let 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}条`);
|
|
953
|
+
// 分批处理大数据量
|
|
954
|
+
let batches = Math.ceil(list.length / batch_size);
|
|
955
|
+
this.log('info', `开始分批修改数据,共${batches}批,每批${batch_size}条`);
|
|
1015
956
|
|
|
1016
|
-
|
|
957
|
+
let final_res = null;
|
|
1017
958
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
// 逐条处理记录,保持简单可靠
|
|
1024
|
-
for (let i = 0; i < list.length; i++) {
|
|
1025
|
-
const item = list[i];
|
|
1026
|
-
this.log('debug', `处理第${i + 1}/${list.length}条记录`);
|
|
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
|
+
let item = list[i];
|
|
967
|
+
this.log('debug', `处理第${i + 1}/${list.length}条记录`);
|
|
1031
968
|
|
|
1032
|
-
|
|
1033
|
-
|
|
969
|
+
// 生成单个更新SQL
|
|
970
|
+
let 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
|
/* 辅助类 */
|
|
@@ -1064,8 +995,8 @@ Sql.prototype.has = function (param_dt, sql_dt) {
|
|
|
1064
995
|
if (!param_dt || !sql_dt) {
|
|
1065
996
|
return false;
|
|
1066
997
|
}
|
|
1067
|
-
for (
|
|
1068
|
-
|
|
998
|
+
for (let key in sql_dt) {
|
|
999
|
+
let value = param_dt[key];
|
|
1069
1000
|
if (value !== undefined && value !== null && value !== '') {
|
|
1070
1001
|
return true;
|
|
1071
1002
|
}
|
|
@@ -1083,7 +1014,7 @@ Sql.prototype.not = function (param_dt, sql_dt) {
|
|
|
1083
1014
|
if (!param_dt || !sql_dt) {
|
|
1084
1015
|
return undefined;
|
|
1085
1016
|
}
|
|
1086
|
-
for (
|
|
1017
|
+
for (let key in param_dt) {
|
|
1087
1018
|
if (!sql_dt[key]) {
|
|
1088
1019
|
return key;
|
|
1089
1020
|
}
|
|
@@ -1098,11 +1029,11 @@ Sql.prototype.not = function (param_dt, sql_dt) {
|
|
|
1098
1029
|
* @returns {object} 返回过滤后的参数集合
|
|
1099
1030
|
*/
|
|
1100
1031
|
Sql.prototype.filterParams = function (param_dt, sql_dt) {
|
|
1101
|
-
|
|
1032
|
+
let dt = {};
|
|
1102
1033
|
if (!param_dt || !sql_dt) {
|
|
1103
1034
|
return dt;
|
|
1104
1035
|
}
|
|
1105
|
-
for (
|
|
1036
|
+
for (let key in param_dt) {
|
|
1106
1037
|
if (!sql_dt[key]) {
|
|
1107
1038
|
dt[key] = param_dt[key];
|
|
1108
1039
|
}
|
|
@@ -1118,16 +1049,16 @@ Sql.prototype.filterParams = function (param_dt, sql_dt) {
|
|
|
1118
1049
|
* @returns {string} 拼接的查询条件
|
|
1119
1050
|
*/
|
|
1120
1051
|
Sql.prototype._tplQueryWithSep = function (param_dt, sql_dt, l) {
|
|
1121
|
-
|
|
1122
|
-
for (
|
|
1052
|
+
let conds = [];
|
|
1053
|
+
for (let key in param_dt) {
|
|
1123
1054
|
let value = String(param_dt[key]);
|
|
1124
|
-
|
|
1125
|
-
|
|
1055
|
+
let arr = value.split(l);
|
|
1056
|
+
let tpl = sql_dt[key];
|
|
1126
1057
|
|
|
1127
1058
|
if (tpl) {
|
|
1128
1059
|
if (arr.length > 1) {
|
|
1129
|
-
|
|
1130
|
-
for (
|
|
1060
|
+
let sub_conds = [];
|
|
1061
|
+
for (let val of arr) {
|
|
1131
1062
|
// 使用直接替换方式,避免模板替换错误
|
|
1132
1063
|
sub_conds.push(tpl.replaceAll('{0}', escape(val).trim("'")));
|
|
1133
1064
|
}
|
|
@@ -1138,8 +1069,8 @@ Sql.prototype._tplQueryWithSep = function (param_dt, sql_dt, l) {
|
|
|
1138
1069
|
}
|
|
1139
1070
|
} else {
|
|
1140
1071
|
if (arr.length > 1) {
|
|
1141
|
-
|
|
1142
|
-
for (
|
|
1072
|
+
let sub_conds = [];
|
|
1073
|
+
for (let val of arr) {
|
|
1143
1074
|
sub_conds.push(escapeId(key) + ' = ' + escape(val));
|
|
1144
1075
|
}
|
|
1145
1076
|
conds.push('(' + sub_conds.join(' || ') + ')');
|
|
@@ -1158,8 +1089,8 @@ Sql.prototype._tplQueryWithSep = function (param_dt, sql_dt, l) {
|
|
|
1158
1089
|
* @returns {string} 拼接的查询条件
|
|
1159
1090
|
*/
|
|
1160
1091
|
Sql.prototype._tplQueryNoSep = function (param_dt, sql_dt) {
|
|
1161
|
-
|
|
1162
|
-
for (
|
|
1092
|
+
let conds = [];
|
|
1093
|
+
for (let key in param_dt) {
|
|
1163
1094
|
value = escape(param_dt[key]);
|
|
1164
1095
|
if (sql_dt[key]) {
|
|
1165
1096
|
// 使用直接替换方式,避免模板替换错误
|
|
@@ -1178,13 +1109,13 @@ Sql.prototype._tplQueryNoSep = function (param_dt, sql_dt) {
|
|
|
1178
1109
|
* @returns {string} 拼接的查询条件
|
|
1179
1110
|
*/
|
|
1180
1111
|
Sql.prototype._noTplQueryWithSep = function (param_dt, l) {
|
|
1181
|
-
|
|
1182
|
-
for (
|
|
1183
|
-
|
|
1184
|
-
|
|
1112
|
+
let conds = [];
|
|
1113
|
+
for (let key in param_dt) {
|
|
1114
|
+
let value = param_dt[key];
|
|
1115
|
+
let arr = String(value).split(l);
|
|
1185
1116
|
if (arr.length > 1) {
|
|
1186
|
-
|
|
1187
|
-
for (
|
|
1117
|
+
let sub_conds = [];
|
|
1118
|
+
for (let val of arr) {
|
|
1188
1119
|
sub_conds.push(escapeId(key) + ' = ' + escape(val));
|
|
1189
1120
|
}
|
|
1190
1121
|
conds.push('(' + sub_conds.join(' || ') + ')');
|
|
@@ -1201,8 +1132,8 @@ Sql.prototype._noTplQueryWithSep = function (param_dt, l) {
|
|
|
1201
1132
|
* @returns {string} 拼接的查询条件
|
|
1202
1133
|
*/
|
|
1203
1134
|
Sql.prototype._noTplQueryNoSep = function (param_dt) {
|
|
1204
|
-
|
|
1205
|
-
for (
|
|
1135
|
+
let conds = [];
|
|
1136
|
+
for (let key in param_dt) {
|
|
1206
1137
|
conds.push(escapeId(key) + ' = ' + escape(param_dt[key]));
|
|
1207
1138
|
}
|
|
1208
1139
|
return conds.join(' && ');
|
|
@@ -1219,7 +1150,7 @@ Sql.prototype.tplQuery = function (param_dt, sql_dt) {
|
|
|
1219
1150
|
if (!param_dt) {
|
|
1220
1151
|
return sql;
|
|
1221
1152
|
}
|
|
1222
|
-
|
|
1153
|
+
let l = this.config.separator;
|
|
1223
1154
|
if (sql_dt) {
|
|
1224
1155
|
if (l) {
|
|
1225
1156
|
sql = this._tplQueryWithSep(param_dt, sql_dt, l);
|
|
@@ -1246,17 +1177,17 @@ Sql.prototype.tplBody = function (param_dt, sql_dt) {
|
|
|
1246
1177
|
if (!param_dt) {
|
|
1247
1178
|
return '';
|
|
1248
1179
|
}
|
|
1249
|
-
|
|
1180
|
+
let parts = [];
|
|
1250
1181
|
if (!sql_dt || Object.keys(sql_dt).length === 0) {
|
|
1251
|
-
for (
|
|
1182
|
+
for (let key in param_dt) {
|
|
1252
1183
|
parts.push(' ' + escapeId(key) + ' = ' + escape(param_dt[key]));
|
|
1253
1184
|
}
|
|
1254
1185
|
} else {
|
|
1255
|
-
for (
|
|
1186
|
+
for (let key in param_dt) {
|
|
1256
1187
|
let value = escape(param_dt[key]);
|
|
1257
1188
|
if (sql_dt[key]) {
|
|
1258
1189
|
// 使用直接替换方式,避免模板替换错误
|
|
1259
|
-
|
|
1190
|
+
let replaced = sql_dt[key].replaceAll('{0}', value.trim("'"));
|
|
1260
1191
|
parts.push(' ' + replaced.replace(/\+ -/g, '- ').replace(/- -/g, '+ '));
|
|
1261
1192
|
} else {
|
|
1262
1193
|
parts.push(' ' + escapeId(key) + ' = ' + value);
|
|
@@ -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
|
+
let sql_stmts = await this._readAndSplitSql(file);
|
|
1210
|
+
return await this._execSqlStmts(file, sql_stmts);
|
|
1284
1211
|
};
|
|
1285
1212
|
|
|
1286
1213
|
/**
|
|
@@ -1313,7 +1240,7 @@ Sql.prototype._validateFile = async function (file) {
|
|
|
1313
1240
|
* @returns {Promise<string[]>} SQL语句数组
|
|
1314
1241
|
*/
|
|
1315
1242
|
Sql.prototype._readAndSplitSql = async function (file) {
|
|
1316
|
-
|
|
1243
|
+
let sql_content = await $.file.readText(file);
|
|
1317
1244
|
if (!sql_content || sql_content.trim() === '') {
|
|
1318
1245
|
throw new Error(`SQL文件内容为空: ${file}`);
|
|
1319
1246
|
}
|
|
@@ -1345,8 +1272,8 @@ Sql.prototype._execSqlStmts = async function (file, sql_stmts) {
|
|
|
1345
1272
|
* @returns {Promise<void>}
|
|
1346
1273
|
*/
|
|
1347
1274
|
Sql.prototype._executeStatements = async function (sql_stmts) {
|
|
1348
|
-
for (
|
|
1349
|
-
|
|
1275
|
+
for (let sql of sql_stmts) {
|
|
1276
|
+
let trimmed_sql = sql.trim();
|
|
1350
1277
|
if (trimmed_sql) {
|
|
1351
1278
|
await this.exec(trimmed_sql);
|
|
1352
1279
|
}
|
|
@@ -1459,18 +1386,18 @@ Sql.prototype._handleString = function (char, sql_content, i, in_str, str_char,
|
|
|
1459
1386
|
* @returns {Array} SQL语句数组
|
|
1460
1387
|
*/
|
|
1461
1388
|
Sql.prototype._splitSqlStatements = function (sql_content) {
|
|
1462
|
-
|
|
1389
|
+
let stmts = [];
|
|
1463
1390
|
let in_str = false;
|
|
1464
1391
|
let str_char = '';
|
|
1465
1392
|
let in_comment = false;
|
|
1466
1393
|
let cur_stmt = '';
|
|
1467
1394
|
|
|
1468
1395
|
for (let i = 0; i < sql_content.length; i++) {
|
|
1469
|
-
|
|
1470
|
-
|
|
1396
|
+
let char = sql_content[i];
|
|
1397
|
+
let next_char = i + 1 < sql_content.length ? sql_content[i + 1] : '';
|
|
1471
1398
|
|
|
1472
1399
|
// 处理注释
|
|
1473
|
-
|
|
1400
|
+
let line_comment = this._handleLineComment(char, next_char, in_str, in_comment);
|
|
1474
1401
|
if (line_comment.skip > 0) {
|
|
1475
1402
|
i += line_comment.skip;
|
|
1476
1403
|
in_comment = line_comment.in_comment;
|
|
@@ -1478,7 +1405,7 @@ Sql.prototype._splitSqlStatements = function (sql_content) {
|
|
|
1478
1405
|
}
|
|
1479
1406
|
in_comment = line_comment.in_comment;
|
|
1480
1407
|
|
|
1481
|
-
|
|
1408
|
+
let block_comment = this._handleBlockComment(char, next_char, in_str, in_comment);
|
|
1482
1409
|
if (block_comment.skip > 0) {
|
|
1483
1410
|
i += block_comment.skip;
|
|
1484
1411
|
in_comment = block_comment.in_comment;
|
|
@@ -1491,7 +1418,7 @@ Sql.prototype._splitSqlStatements = function (sql_content) {
|
|
|
1491
1418
|
}
|
|
1492
1419
|
|
|
1493
1420
|
// 处理字符串
|
|
1494
|
-
|
|
1421
|
+
let str_res = this._handleString(char, sql_content, i, in_str, str_char, in_comment);
|
|
1495
1422
|
in_str = str_res.in_str;
|
|
1496
1423
|
str_char = str_res.str_char;
|
|
1497
1424
|
|
|
@@ -1518,20 +1445,20 @@ Sql.prototype._splitSqlStatements = function (sql_content) {
|
|
|
1518
1445
|
* @returns {object} 返回监听操作的对象
|
|
1519
1446
|
*/
|
|
1520
1447
|
Sql.prototype.model = function (model) {
|
|
1521
|
-
|
|
1448
|
+
let self = this;
|
|
1522
1449
|
return new Proxy(model || {}, {
|
|
1523
1450
|
set: function (obj, prop, value) {
|
|
1524
1451
|
if (self.key && prop !== 'length' && !prop.toString().startsWith('__')) {
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
for (
|
|
1452
|
+
let query = {};
|
|
1453
|
+
let arr = self.key.split(',');
|
|
1454
|
+
for (let key of arr) {
|
|
1528
1455
|
query[key] = obj[key];
|
|
1529
1456
|
}
|
|
1530
1457
|
|
|
1531
1458
|
if (typeof value === 'number') {
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1459
|
+
let n = obj[prop] || 0;
|
|
1460
|
+
let cha = value - n;
|
|
1461
|
+
let where = self.toWhere(query);
|
|
1535
1462
|
if (cha > 0) {
|
|
1536
1463
|
self.setSql(where, '`' + prop + '` = `' + prop + '` + ' + cha);
|
|
1537
1464
|
} else if (cha < 0) {
|
|
@@ -1540,14 +1467,14 @@ Sql.prototype.model = function (model) {
|
|
|
1540
1467
|
self.setSql(where, '`' + prop + '` = ' + value);
|
|
1541
1468
|
}
|
|
1542
1469
|
} else {
|
|
1543
|
-
|
|
1470
|
+
let set = {};
|
|
1544
1471
|
set[prop] = value;
|
|
1545
1472
|
self.set(query, set, false).catch(err => {
|
|
1546
1473
|
self.log('error', '模型数据更新失败', err);
|
|
1547
1474
|
});
|
|
1548
1475
|
}
|
|
1549
1476
|
}
|
|
1550
|
-
|
|
1477
|
+
let new_obj = { ...obj };
|
|
1551
1478
|
new_obj[prop] = value;
|
|
1552
1479
|
return true;
|
|
1553
1480
|
}
|
|
@@ -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
|
+
let where_str = await this._getWhereStr(where, like);
|
|
1496
|
+
let 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
|
+
let sql = `SHOW CREATE TABLE \`${this.table}\``;
|
|
1513
|
+
let 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
|
+
let 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
|
+
let tableCommentResult = await this.run(tableCommentSql);
|
|
1628
1543
|
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
}
|
|
1544
|
+
if (tableCommentResult && tableCommentResult.length > 0 && tableCommentResult[0].TABLE_COMMENT) {
|
|
1545
|
+
let tableComment = tableCommentResult[0].TABLE_COMMENT;
|
|
1546
|
+
if (tableComment && tableComment.trim() !== '') {
|
|
1547
|
+
commentSql += `\n-- 表注释: ${tableComment}\n`;
|
|
1634
1548
|
}
|
|
1549
|
+
}
|
|
1635
1550
|
|
|
1636
|
-
|
|
1637
|
-
|
|
1551
|
+
// 获取列注释
|
|
1552
|
+
let 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
|
+
let columnCommentResult = await this.run(columnCommentSql);
|
|
1647
1562
|
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
}
|
|
1563
|
+
if (columnCommentResult && columnCommentResult.length > 0) {
|
|
1564
|
+
commentSql += '\n-- 列注释:\n';
|
|
1565
|
+
for (let 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
|
+
let 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
|
+
let createTableSql = await this._getCreateTableSql();
|
|
1605
|
+
if (createTableSql) {
|
|
1606
|
+
sqlContent += createTableSql + '\n\n';
|
|
1697
1607
|
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
}
|
|
1608
|
+
// 添加表注释信息
|
|
1609
|
+
let 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 (let row of data) {
|
|
1618
|
+
let keys = Object.keys(row).map(key => this.escapeId(key)).join(', ');
|
|
1619
|
+
let 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
|
+
let fs = require('fs');
|
|
1625
|
+
let path = require('path');
|
|
1716
1626
|
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1627
|
+
// 确保目录存在
|
|
1628
|
+
let 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
|
+
let 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
|
+
let sqlContent = fs.readFileSync(file, 'utf8');
|
|
1756
1660
|
|
|
1757
|
-
|
|
1758
|
-
|
|
1661
|
+
// 解析 SQL 语句
|
|
1662
|
+
let statements = this._parseSqlStatements(sqlContent);
|
|
1759
1663
|
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
}
|
|
1664
|
+
// 执行每个 SQL 语句
|
|
1665
|
+
for (let 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
|
/**
|
|
@@ -1779,15 +1678,15 @@ Sql.prototype.restore = async function (file) {
|
|
|
1779
1678
|
* @private
|
|
1780
1679
|
*/
|
|
1781
1680
|
Sql.prototype._parseSqlStatements = function (sql) {
|
|
1782
|
-
|
|
1681
|
+
let stmts = [];
|
|
1783
1682
|
let cur_stmt = '';
|
|
1784
1683
|
let in_str = false;
|
|
1785
1684
|
let in_cmt = false;
|
|
1786
1685
|
let quote_char = '';
|
|
1787
1686
|
|
|
1788
1687
|
for (let i = 0; i < sql.length; i++) {
|
|
1789
|
-
|
|
1790
|
-
|
|
1688
|
+
let char = sql[i];
|
|
1689
|
+
let next_char = sql[i + 1] || '';
|
|
1791
1690
|
|
|
1792
1691
|
// 处理字符串
|
|
1793
1692
|
if (!in_cmt && (char === "'" || char === '"') && (i === 0 || sql[i - 1] !== '\\')) {
|
|
@@ -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
|
+
let 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
|
+
let 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 (let row of data) {
|
|
1791
|
+
let keys = Object.keys(row).map(key => this.escapeId(key)).join(', ');
|
|
1792
|
+
let 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
|
+
let fs = require('fs');
|
|
1798
|
+
let path = require('path');
|
|
1901
1799
|
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1800
|
+
// 确保目录存在
|
|
1801
|
+
let 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
|
+
let 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
|
+
let 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
|
+
let 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 (let sql of sql_stmts) {
|
|
1852
|
+
let 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
|
+
let sql = `SHOW CREATE TABLE \`${this.table}\``;
|
|
1889
|
+
let 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 = {
|