mm_mysql 2.4.2 → 2.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +2 -3
- package/package.json +4 -4
- package/sql.js +145 -145
- package/test2.js +381 -0
package/index.js
CHANGED
|
@@ -124,8 +124,6 @@ Mysql.prototype.open = async function () {
|
|
|
124
124
|
// 测试连接池连接
|
|
125
125
|
let conn = await this._pool.getConnection();
|
|
126
126
|
conn.release();
|
|
127
|
-
|
|
128
|
-
this.log('info', '数据库连接池创建成功');
|
|
129
127
|
return true;
|
|
130
128
|
} catch (error) {
|
|
131
129
|
this.log('error', '数据库连接池创建失败', error);
|
|
@@ -195,6 +193,7 @@ Mysql.prototype.run = async function (sql, params = []) {
|
|
|
195
193
|
let [rows] = await conn.query(sql, params);
|
|
196
194
|
ret = rows;
|
|
197
195
|
} catch (err) {
|
|
196
|
+
console.error('SQL执行失败', err);
|
|
198
197
|
this.sql = err.sql;
|
|
199
198
|
this.error = {
|
|
200
199
|
code: err.errno,
|
|
@@ -233,7 +232,7 @@ Mysql.prototype.exec = async function (sql, params = []) {
|
|
|
233
232
|
let [result] = await conn.execute(sql, params);
|
|
234
233
|
ret = result.insertId || result.affectedRows || result.changedRows || 1;
|
|
235
234
|
} catch (error) {
|
|
236
|
-
|
|
235
|
+
console.error('SQL执行失败', error);
|
|
237
236
|
this.sql = error.sql;
|
|
238
237
|
this.error = {
|
|
239
238
|
code: error.errno,
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mm_mysql",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.4",
|
|
4
4
|
"description": "这是超级美眉mysql帮助函数模块,用于便捷操作mysql,使用await方式,可以避免嵌套函数",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"mm_expand": "^2.3.
|
|
7
|
+
"mm_expand": "^2.3.4",
|
|
8
8
|
"mysql2": "^3.22.3",
|
|
9
9
|
"sqlstring": "^2.3.3"
|
|
10
10
|
},
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"node": ">=12.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"eslint": "^10.
|
|
40
|
-
"eslint-plugin-jsdoc": "^
|
|
39
|
+
"eslint": "^10.4.0",
|
|
40
|
+
"eslint-plugin-jsdoc": "^63.0.0",
|
|
41
41
|
"mm_eslint": "^1.7.1"
|
|
42
42
|
}
|
|
43
43
|
}
|
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,13 +428,13 @@ 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 {
|
|
435
435
|
where = this._buildExactWhere(obj);
|
|
436
436
|
}
|
|
437
|
-
return where.replace(
|
|
437
|
+
return where.replace(/^ and /, '');
|
|
438
438
|
};
|
|
439
439
|
|
|
440
440
|
Sql.prototype._buildLikeWhere = function (obj) {
|
|
@@ -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,17 +507,17 @@ 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;
|
|
517
517
|
}
|
|
518
518
|
}
|
|
519
519
|
|
|
520
|
-
return set.replace(
|
|
520
|
+
return set.replace(/^,/, '');
|
|
521
521
|
};
|
|
522
522
|
|
|
523
523
|
/**
|
|
@@ -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,7 +605,7 @@ Sql.prototype.add = async function (body) {
|
|
|
605
605
|
if (!this.table || !body || typeof body !== 'object') {
|
|
606
606
|
throw new Error('表名或数据未设置');
|
|
607
607
|
}
|
|
608
|
-
|
|
608
|
+
let sql = this.toAddSql(body);
|
|
609
609
|
this.sql = sql;
|
|
610
610
|
return await this.exec(sql);
|
|
611
611
|
};
|
|
@@ -620,9 +620,9 @@ Sql.prototype.del = async function (query, like) {
|
|
|
620
620
|
if (!this.table) {
|
|
621
621
|
throw new Error('表名未设置');
|
|
622
622
|
}
|
|
623
|
-
|
|
623
|
+
let sql = this.toDelSql(query, like);
|
|
624
624
|
this.sql = sql;
|
|
625
|
-
|
|
625
|
+
let bl = await this.exec(sql);
|
|
626
626
|
return bl;
|
|
627
627
|
};
|
|
628
628
|
|
|
@@ -637,9 +637,9 @@ Sql.prototype.set = async function (query, body, like) {
|
|
|
637
637
|
if (!this.table || !body || typeof body !== 'object') {
|
|
638
638
|
throw new Error('表名或数据未设置');
|
|
639
639
|
}
|
|
640
|
-
|
|
640
|
+
let sql = this.toSetSql(query, body, like);
|
|
641
641
|
this.sql = sql;
|
|
642
|
-
|
|
642
|
+
let bl = await this.exec(sql);
|
|
643
643
|
return bl;
|
|
644
644
|
};
|
|
645
645
|
|
|
@@ -654,8 +654,8 @@ Sql.prototype.addOrSetSql = async function (where, set, like) {
|
|
|
654
654
|
if (!this.table || !where || !set) {
|
|
655
655
|
throw new Error('表名、条件或数据未设置');
|
|
656
656
|
}
|
|
657
|
-
|
|
658
|
-
|
|
657
|
+
let where_str = await this._getWhereStr(where, like);
|
|
658
|
+
let count = await this.countSql(where_str);
|
|
659
659
|
|
|
660
660
|
if (count === 0) {
|
|
661
661
|
return await this._addData(set);
|
|
@@ -672,7 +672,7 @@ Sql.prototype._getWhereStr = async function (where, like) {
|
|
|
672
672
|
};
|
|
673
673
|
|
|
674
674
|
Sql.prototype._addData = async function (set) {
|
|
675
|
-
|
|
675
|
+
let { key_str, val_str } = await this._parseSetData(set);
|
|
676
676
|
return await this.addSql(key_str, val_str);
|
|
677
677
|
};
|
|
678
678
|
|
|
@@ -689,16 +689,16 @@ Sql.prototype._parseSetData = async function (set) {
|
|
|
689
689
|
let val_str = '';
|
|
690
690
|
|
|
691
691
|
if (typeof set === 'string') {
|
|
692
|
-
|
|
693
|
-
for (
|
|
694
|
-
|
|
692
|
+
let arr = set.split(',');
|
|
693
|
+
for (let o of arr) {
|
|
694
|
+
let ar = o.split('=');
|
|
695
695
|
if (ar.length === 2) {
|
|
696
696
|
key_str += ',' + ar[0];
|
|
697
697
|
val_str += ',' + ar[1];
|
|
698
698
|
}
|
|
699
699
|
}
|
|
700
700
|
} else {
|
|
701
|
-
for (
|
|
701
|
+
for (let k in set) {
|
|
702
702
|
key_str += ',' + escapeId(k);
|
|
703
703
|
let val = set[k];
|
|
704
704
|
val_str += ',' + escape(val);
|
|
@@ -725,9 +725,9 @@ Sql.prototype.get = async function (query, sort, view, like, timeout = 20000) {
|
|
|
725
725
|
throw new Error('表名未设置');
|
|
726
726
|
}
|
|
727
727
|
|
|
728
|
-
|
|
728
|
+
let sql = this.toGetSql(query, sort, view, like);
|
|
729
729
|
this.sql = sql;
|
|
730
|
-
|
|
730
|
+
let list = await this.run(sql);
|
|
731
731
|
return list;
|
|
732
732
|
};
|
|
733
733
|
|
|
@@ -742,15 +742,15 @@ Sql.prototype.get = async function (query, sort, view, like, timeout = 20000) {
|
|
|
742
742
|
*/
|
|
743
743
|
Sql.prototype.getObj = async function (query, sort, view, like, timeout = 20000) {
|
|
744
744
|
// 保存当前分页设置
|
|
745
|
-
|
|
746
|
-
|
|
745
|
+
let old_page = this.page;
|
|
746
|
+
let old_size = this.size;
|
|
747
747
|
|
|
748
748
|
// 设置为只查询一条
|
|
749
749
|
this.page = 1;
|
|
750
750
|
this.size = 1;
|
|
751
751
|
|
|
752
752
|
// 使用优化后的get方法
|
|
753
|
-
|
|
753
|
+
let list = await this.get(query, sort, view, like, timeout);
|
|
754
754
|
|
|
755
755
|
// 恢复分页设置
|
|
756
756
|
this.page = old_page;
|
|
@@ -778,11 +778,11 @@ Sql.prototype.count = async function (query, like, timeout = 20000) {
|
|
|
778
778
|
if (!this.table) {
|
|
779
779
|
throw new Error('表名未设置');
|
|
780
780
|
}
|
|
781
|
-
|
|
782
|
-
|
|
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
783
|
this.sql = sql;
|
|
784
|
-
|
|
785
|
-
|
|
784
|
+
let list = await this.run(sql);
|
|
785
|
+
let total = list && list[0] && list[0].num ? parseInt(list[0].num) : 0;
|
|
786
786
|
return total;
|
|
787
787
|
};
|
|
788
788
|
|
|
@@ -818,17 +818,17 @@ Sql.prototype.addList = async function (list, batch_size = 100, timeout = 60000)
|
|
|
818
818
|
}
|
|
819
819
|
|
|
820
820
|
// 分批处理大数据量
|
|
821
|
-
|
|
821
|
+
let batches = Math.ceil(list.length / batch_size);
|
|
822
822
|
this.log('info', `开始分批添加数据,共${batches}批,每批${batch_size}条`);
|
|
823
823
|
|
|
824
824
|
// 分批执行,每批一个单独的批量插入语句
|
|
825
825
|
let final_res = null;
|
|
826
826
|
for (let i = 0; i < batches; i++) {
|
|
827
|
-
|
|
827
|
+
let batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
828
828
|
this.log('debug', `处理第${i + 1}/${batches}批数据,${batch.length}条`);
|
|
829
829
|
|
|
830
830
|
// 生成批量插入SQL
|
|
831
|
-
|
|
831
|
+
let batch_sql = this.toBatchAddSql(batch);
|
|
832
832
|
this.sql = batch_sql;
|
|
833
833
|
final_res = await this.exec(batch_sql);
|
|
834
834
|
}
|
|
@@ -847,32 +847,32 @@ Sql.prototype.toBatchAddSql = function (list) {
|
|
|
847
847
|
throw new Error('表名或数据列表未设置');
|
|
848
848
|
}
|
|
849
849
|
|
|
850
|
-
|
|
850
|
+
let first_obj = list[0];
|
|
851
851
|
if (!first_obj || typeof first_obj !== 'object') {
|
|
852
852
|
throw new Error('数据格式错误');
|
|
853
853
|
}
|
|
854
854
|
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
855
|
+
let keys = this._getKeys(first_obj);
|
|
856
|
+
let columns = keys.join(',');
|
|
857
|
+
let val_list = this._buildValueList(list, keys);
|
|
858
858
|
|
|
859
859
|
return `INSERT INTO \`${this.table}\` (${columns}) VALUES ${val_list.join(',')}`;
|
|
860
860
|
};
|
|
861
861
|
|
|
862
862
|
Sql.prototype._getKeys = function (obj) {
|
|
863
|
-
|
|
864
|
-
for (
|
|
863
|
+
let keys = [];
|
|
864
|
+
for (let k in obj) {
|
|
865
865
|
keys.push(this.escapeId(k));
|
|
866
866
|
}
|
|
867
867
|
return keys;
|
|
868
868
|
};
|
|
869
869
|
|
|
870
870
|
Sql.prototype._buildValueList = function (list, keys) {
|
|
871
|
-
|
|
872
|
-
for (
|
|
873
|
-
|
|
874
|
-
for (
|
|
875
|
-
|
|
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, '');
|
|
876
876
|
let val = item[key];
|
|
877
877
|
values.push(this.escape(val));
|
|
878
878
|
}
|
|
@@ -896,7 +896,7 @@ Sql.prototype.delList = async function (list, like, batch_size = 100, timeout =
|
|
|
896
896
|
// 如果数据量较小,直接处理
|
|
897
897
|
if (list.length <= batch_size) {
|
|
898
898
|
let sql = '';
|
|
899
|
-
for (
|
|
899
|
+
for (let item of list) {
|
|
900
900
|
sql += this.toDelSql(item.query, like);
|
|
901
901
|
}
|
|
902
902
|
this.sql = sql;
|
|
@@ -904,7 +904,7 @@ Sql.prototype.delList = async function (list, like, batch_size = 100, timeout =
|
|
|
904
904
|
}
|
|
905
905
|
|
|
906
906
|
// 分批处理大数据量
|
|
907
|
-
|
|
907
|
+
let batches = Math.ceil(list.length / batch_size);
|
|
908
908
|
this.log('info', `开始分批删除数据,共${batches}批,每批${batch_size}条`);
|
|
909
909
|
|
|
910
910
|
// 使用事务包装整个操作以保证原子性
|
|
@@ -915,11 +915,11 @@ Sql.prototype.delList = async function (list, like, batch_size = 100, timeout =
|
|
|
915
915
|
|
|
916
916
|
// 分批处理exec删除语句
|
|
917
917
|
for (let i = 0; i < batches; i++) {
|
|
918
|
-
|
|
918
|
+
let batch = list.slice(i * batch_size, (i + 1) * batch_size);
|
|
919
919
|
this.log('debug', `处理第${i + 1}/${batches}批数据,${batch.length}条`);
|
|
920
920
|
|
|
921
921
|
let batch_sql = '';
|
|
922
|
-
for (
|
|
922
|
+
for (let item of batch) {
|
|
923
923
|
batch_sql += this.toDelSql(item.query, like);
|
|
924
924
|
}
|
|
925
925
|
final_res = await this.exec(batch_sql);
|
|
@@ -951,7 +951,7 @@ Sql.prototype.setList = async function (list, like = false, batch_size = 100, ti
|
|
|
951
951
|
throw new Error('表名或数据列表未设置');
|
|
952
952
|
}
|
|
953
953
|
// 分批处理大数据量
|
|
954
|
-
|
|
954
|
+
let batches = Math.ceil(list.length / batch_size);
|
|
955
955
|
this.log('info', `开始分批修改数据,共${batches}批,每批${batch_size}条`);
|
|
956
956
|
|
|
957
957
|
let final_res = null;
|
|
@@ -963,11 +963,11 @@ Sql.prototype.setList = async function (list, like = false, batch_size = 100, ti
|
|
|
963
963
|
|
|
964
964
|
// 逐条处理记录,保持简单可靠
|
|
965
965
|
for (let i = 0; i < list.length; i++) {
|
|
966
|
-
|
|
966
|
+
let item = list[i];
|
|
967
967
|
this.log('debug', `处理第${i + 1}/${list.length}条记录`);
|
|
968
968
|
|
|
969
969
|
// 生成单个更新SQL
|
|
970
|
-
|
|
970
|
+
let sql = this.toSetSql(item.query, item.item, like);
|
|
971
971
|
this.sql = sql;
|
|
972
972
|
|
|
973
973
|
final_res = await this.exec(sql, [], timeout);
|
|
@@ -995,8 +995,8 @@ Sql.prototype.has = function (param_dt, sql_dt) {
|
|
|
995
995
|
if (!param_dt || !sql_dt) {
|
|
996
996
|
return false;
|
|
997
997
|
}
|
|
998
|
-
for (
|
|
999
|
-
|
|
998
|
+
for (let key in sql_dt) {
|
|
999
|
+
let value = param_dt[key];
|
|
1000
1000
|
if (value !== undefined && value !== null && value !== '') {
|
|
1001
1001
|
return true;
|
|
1002
1002
|
}
|
|
@@ -1014,7 +1014,7 @@ Sql.prototype.not = function (param_dt, sql_dt) {
|
|
|
1014
1014
|
if (!param_dt || !sql_dt) {
|
|
1015
1015
|
return undefined;
|
|
1016
1016
|
}
|
|
1017
|
-
for (
|
|
1017
|
+
for (let key in param_dt) {
|
|
1018
1018
|
if (!sql_dt[key]) {
|
|
1019
1019
|
return key;
|
|
1020
1020
|
}
|
|
@@ -1029,11 +1029,11 @@ Sql.prototype.not = function (param_dt, sql_dt) {
|
|
|
1029
1029
|
* @returns {object} 返回过滤后的参数集合
|
|
1030
1030
|
*/
|
|
1031
1031
|
Sql.prototype.filterParams = function (param_dt, sql_dt) {
|
|
1032
|
-
|
|
1032
|
+
let dt = {};
|
|
1033
1033
|
if (!param_dt || !sql_dt) {
|
|
1034
1034
|
return dt;
|
|
1035
1035
|
}
|
|
1036
|
-
for (
|
|
1036
|
+
for (let key in param_dt) {
|
|
1037
1037
|
if (!sql_dt[key]) {
|
|
1038
1038
|
dt[key] = param_dt[key];
|
|
1039
1039
|
}
|
|
@@ -1049,16 +1049,16 @@ Sql.prototype.filterParams = function (param_dt, sql_dt) {
|
|
|
1049
1049
|
* @returns {string} 拼接的查询条件
|
|
1050
1050
|
*/
|
|
1051
1051
|
Sql.prototype._tplQueryWithSep = function (param_dt, sql_dt, l) {
|
|
1052
|
-
|
|
1053
|
-
for (
|
|
1052
|
+
let conds = [];
|
|
1053
|
+
for (let key in param_dt) {
|
|
1054
1054
|
let value = String(param_dt[key]);
|
|
1055
|
-
|
|
1056
|
-
|
|
1055
|
+
let arr = value.split(l);
|
|
1056
|
+
let tpl = sql_dt[key];
|
|
1057
1057
|
|
|
1058
1058
|
if (tpl) {
|
|
1059
1059
|
if (arr.length > 1) {
|
|
1060
|
-
|
|
1061
|
-
for (
|
|
1060
|
+
let sub_conds = [];
|
|
1061
|
+
for (let val of arr) {
|
|
1062
1062
|
// 使用直接替换方式,避免模板替换错误
|
|
1063
1063
|
sub_conds.push(tpl.replaceAll('{0}', escape(val).trim("'")));
|
|
1064
1064
|
}
|
|
@@ -1069,8 +1069,8 @@ Sql.prototype._tplQueryWithSep = function (param_dt, sql_dt, l) {
|
|
|
1069
1069
|
}
|
|
1070
1070
|
} else {
|
|
1071
1071
|
if (arr.length > 1) {
|
|
1072
|
-
|
|
1073
|
-
for (
|
|
1072
|
+
let sub_conds = [];
|
|
1073
|
+
for (let val of arr) {
|
|
1074
1074
|
sub_conds.push(escapeId(key) + ' = ' + escape(val));
|
|
1075
1075
|
}
|
|
1076
1076
|
conds.push('(' + sub_conds.join(' || ') + ')');
|
|
@@ -1089,8 +1089,8 @@ Sql.prototype._tplQueryWithSep = function (param_dt, sql_dt, l) {
|
|
|
1089
1089
|
* @returns {string} 拼接的查询条件
|
|
1090
1090
|
*/
|
|
1091
1091
|
Sql.prototype._tplQueryNoSep = function (param_dt, sql_dt) {
|
|
1092
|
-
|
|
1093
|
-
for (
|
|
1092
|
+
let conds = [];
|
|
1093
|
+
for (let key in param_dt) {
|
|
1094
1094
|
value = escape(param_dt[key]);
|
|
1095
1095
|
if (sql_dt[key]) {
|
|
1096
1096
|
// 使用直接替换方式,避免模板替换错误
|
|
@@ -1109,13 +1109,13 @@ Sql.prototype._tplQueryNoSep = function (param_dt, sql_dt) {
|
|
|
1109
1109
|
* @returns {string} 拼接的查询条件
|
|
1110
1110
|
*/
|
|
1111
1111
|
Sql.prototype._noTplQueryWithSep = function (param_dt, l) {
|
|
1112
|
-
|
|
1113
|
-
for (
|
|
1114
|
-
|
|
1115
|
-
|
|
1112
|
+
let conds = [];
|
|
1113
|
+
for (let key in param_dt) {
|
|
1114
|
+
let value = param_dt[key];
|
|
1115
|
+
let arr = String(value).split(l);
|
|
1116
1116
|
if (arr.length > 1) {
|
|
1117
|
-
|
|
1118
|
-
for (
|
|
1117
|
+
let sub_conds = [];
|
|
1118
|
+
for (let val of arr) {
|
|
1119
1119
|
sub_conds.push(escapeId(key) + ' = ' + escape(val));
|
|
1120
1120
|
}
|
|
1121
1121
|
conds.push('(' + sub_conds.join(' || ') + ')');
|
|
@@ -1132,8 +1132,8 @@ Sql.prototype._noTplQueryWithSep = function (param_dt, l) {
|
|
|
1132
1132
|
* @returns {string} 拼接的查询条件
|
|
1133
1133
|
*/
|
|
1134
1134
|
Sql.prototype._noTplQueryNoSep = function (param_dt) {
|
|
1135
|
-
|
|
1136
|
-
for (
|
|
1135
|
+
let conds = [];
|
|
1136
|
+
for (let key in param_dt) {
|
|
1137
1137
|
conds.push(escapeId(key) + ' = ' + escape(param_dt[key]));
|
|
1138
1138
|
}
|
|
1139
1139
|
return conds.join(' && ');
|
|
@@ -1150,7 +1150,7 @@ Sql.prototype.tplQuery = function (param_dt, sql_dt) {
|
|
|
1150
1150
|
if (!param_dt) {
|
|
1151
1151
|
return sql;
|
|
1152
1152
|
}
|
|
1153
|
-
|
|
1153
|
+
let l = this.config.separator;
|
|
1154
1154
|
if (sql_dt) {
|
|
1155
1155
|
if (l) {
|
|
1156
1156
|
sql = this._tplQueryWithSep(param_dt, sql_dt, l);
|
|
@@ -1177,17 +1177,17 @@ Sql.prototype.tplBody = function (param_dt, sql_dt) {
|
|
|
1177
1177
|
if (!param_dt) {
|
|
1178
1178
|
return '';
|
|
1179
1179
|
}
|
|
1180
|
-
|
|
1180
|
+
let parts = [];
|
|
1181
1181
|
if (!sql_dt || Object.keys(sql_dt).length === 0) {
|
|
1182
|
-
for (
|
|
1182
|
+
for (let key in param_dt) {
|
|
1183
1183
|
parts.push(' ' + escapeId(key) + ' = ' + escape(param_dt[key]));
|
|
1184
1184
|
}
|
|
1185
1185
|
} else {
|
|
1186
|
-
for (
|
|
1186
|
+
for (let key in param_dt) {
|
|
1187
1187
|
let value = escape(param_dt[key]);
|
|
1188
1188
|
if (sql_dt[key]) {
|
|
1189
1189
|
// 使用直接替换方式,避免模板替换错误
|
|
1190
|
-
|
|
1190
|
+
let replaced = sql_dt[key].replaceAll('{0}', value.trim("'"));
|
|
1191
1191
|
parts.push(' ' + replaced.replace(/\+ -/g, '- ').replace(/- -/g, '+ '));
|
|
1192
1192
|
} else {
|
|
1193
1193
|
parts.push(' ' + escapeId(key) + ' = ' + value);
|
|
@@ -1206,7 +1206,7 @@ Sql.prototype.load = async function (file) {
|
|
|
1206
1206
|
|
|
1207
1207
|
await this._logLoadStart(file);
|
|
1208
1208
|
await this._validateFile(file);
|
|
1209
|
-
|
|
1209
|
+
let sql_stmts = await this._readAndSplitSql(file);
|
|
1210
1210
|
return await this._execSqlStmts(file, sql_stmts);
|
|
1211
1211
|
};
|
|
1212
1212
|
|
|
@@ -1240,7 +1240,7 @@ Sql.prototype._validateFile = async function (file) {
|
|
|
1240
1240
|
* @returns {Promise<string[]>} SQL语句数组
|
|
1241
1241
|
*/
|
|
1242
1242
|
Sql.prototype._readAndSplitSql = async function (file) {
|
|
1243
|
-
|
|
1243
|
+
let sql_content = await $.file.readText(file);
|
|
1244
1244
|
if (!sql_content || sql_content.trim() === '') {
|
|
1245
1245
|
throw new Error(`SQL文件内容为空: ${file}`);
|
|
1246
1246
|
}
|
|
@@ -1272,8 +1272,8 @@ Sql.prototype._execSqlStmts = async function (file, sql_stmts) {
|
|
|
1272
1272
|
* @returns {Promise<void>}
|
|
1273
1273
|
*/
|
|
1274
1274
|
Sql.prototype._executeStatements = async function (sql_stmts) {
|
|
1275
|
-
for (
|
|
1276
|
-
|
|
1275
|
+
for (let sql of sql_stmts) {
|
|
1276
|
+
let trimmed_sql = sql.trim();
|
|
1277
1277
|
if (trimmed_sql) {
|
|
1278
1278
|
await this.exec(trimmed_sql);
|
|
1279
1279
|
}
|
|
@@ -1386,18 +1386,18 @@ Sql.prototype._handleString = function (char, sql_content, i, in_str, str_char,
|
|
|
1386
1386
|
* @returns {Array} SQL语句数组
|
|
1387
1387
|
*/
|
|
1388
1388
|
Sql.prototype._splitSqlStatements = function (sql_content) {
|
|
1389
|
-
|
|
1389
|
+
let stmts = [];
|
|
1390
1390
|
let in_str = false;
|
|
1391
1391
|
let str_char = '';
|
|
1392
1392
|
let in_comment = false;
|
|
1393
1393
|
let cur_stmt = '';
|
|
1394
1394
|
|
|
1395
1395
|
for (let i = 0; i < sql_content.length; i++) {
|
|
1396
|
-
|
|
1397
|
-
|
|
1396
|
+
let char = sql_content[i];
|
|
1397
|
+
let next_char = i + 1 < sql_content.length ? sql_content[i + 1] : '';
|
|
1398
1398
|
|
|
1399
1399
|
// 处理注释
|
|
1400
|
-
|
|
1400
|
+
let line_comment = this._handleLineComment(char, next_char, in_str, in_comment);
|
|
1401
1401
|
if (line_comment.skip > 0) {
|
|
1402
1402
|
i += line_comment.skip;
|
|
1403
1403
|
in_comment = line_comment.in_comment;
|
|
@@ -1405,7 +1405,7 @@ Sql.prototype._splitSqlStatements = function (sql_content) {
|
|
|
1405
1405
|
}
|
|
1406
1406
|
in_comment = line_comment.in_comment;
|
|
1407
1407
|
|
|
1408
|
-
|
|
1408
|
+
let block_comment = this._handleBlockComment(char, next_char, in_str, in_comment);
|
|
1409
1409
|
if (block_comment.skip > 0) {
|
|
1410
1410
|
i += block_comment.skip;
|
|
1411
1411
|
in_comment = block_comment.in_comment;
|
|
@@ -1418,7 +1418,7 @@ Sql.prototype._splitSqlStatements = function (sql_content) {
|
|
|
1418
1418
|
}
|
|
1419
1419
|
|
|
1420
1420
|
// 处理字符串
|
|
1421
|
-
|
|
1421
|
+
let str_res = this._handleString(char, sql_content, i, in_str, str_char, in_comment);
|
|
1422
1422
|
in_str = str_res.in_str;
|
|
1423
1423
|
str_char = str_res.str_char;
|
|
1424
1424
|
|
|
@@ -1445,20 +1445,20 @@ Sql.prototype._splitSqlStatements = function (sql_content) {
|
|
|
1445
1445
|
* @returns {object} 返回监听操作的对象
|
|
1446
1446
|
*/
|
|
1447
1447
|
Sql.prototype.model = function (model) {
|
|
1448
|
-
|
|
1448
|
+
let self = this;
|
|
1449
1449
|
return new Proxy(model || {}, {
|
|
1450
1450
|
set: function (obj, prop, value) {
|
|
1451
1451
|
if (self.key && prop !== 'length' && !prop.toString().startsWith('__')) {
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
for (
|
|
1452
|
+
let query = {};
|
|
1453
|
+
let arr = self.key.split(',');
|
|
1454
|
+
for (let key of arr) {
|
|
1455
1455
|
query[key] = obj[key];
|
|
1456
1456
|
}
|
|
1457
1457
|
|
|
1458
1458
|
if (typeof value === 'number') {
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1459
|
+
let n = obj[prop] || 0;
|
|
1460
|
+
let cha = value - n;
|
|
1461
|
+
let where = self.toWhere(query);
|
|
1462
1462
|
if (cha > 0) {
|
|
1463
1463
|
self.setSql(where, '`' + prop + '` = `' + prop + '` + ' + cha);
|
|
1464
1464
|
} else if (cha < 0) {
|
|
@@ -1467,14 +1467,14 @@ Sql.prototype.model = function (model) {
|
|
|
1467
1467
|
self.setSql(where, '`' + prop + '` = ' + value);
|
|
1468
1468
|
}
|
|
1469
1469
|
} else {
|
|
1470
|
-
|
|
1470
|
+
let set = {};
|
|
1471
1471
|
set[prop] = value;
|
|
1472
1472
|
self.set(query, set, false).catch(err => {
|
|
1473
1473
|
self.log('error', '模型数据更新失败', err);
|
|
1474
1474
|
});
|
|
1475
1475
|
}
|
|
1476
1476
|
}
|
|
1477
|
-
|
|
1477
|
+
let new_obj = { ...obj };
|
|
1478
1478
|
new_obj[prop] = value;
|
|
1479
1479
|
return true;
|
|
1480
1480
|
}
|
|
@@ -1492,8 +1492,8 @@ Sql.prototype.addOrSet = async function (where, set, like) {
|
|
|
1492
1492
|
if (!this.table || !where || !set) {
|
|
1493
1493
|
throw new Error('表名、条件或数据未设置');
|
|
1494
1494
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1495
|
+
let where_str = await this._getWhereStr(where, like);
|
|
1496
|
+
let count = await this.countSql(where_str);
|
|
1497
1497
|
|
|
1498
1498
|
if (count === 0) {
|
|
1499
1499
|
return await this._addData(set);
|
|
@@ -1509,8 +1509,8 @@ Sql.prototype.addOrSet = async function (where, set, like) {
|
|
|
1509
1509
|
*/
|
|
1510
1510
|
Sql.prototype._getCreateTableSql = async function () {
|
|
1511
1511
|
// 查询表结构信息(MySQL 使用 SHOW CREATE TABLE)
|
|
1512
|
-
|
|
1513
|
-
|
|
1512
|
+
let sql = `SHOW CREATE TABLE \`${this.table}\``;
|
|
1513
|
+
let result = await this.run(sql);
|
|
1514
1514
|
|
|
1515
1515
|
if (result && result.length > 0 && result[0]['Create Table']) {
|
|
1516
1516
|
// 在创建表语句中添加 IF NOT EXISTS 条件
|
|
@@ -1533,23 +1533,23 @@ Sql.prototype._getTableComments = async function () {
|
|
|
1533
1533
|
let commentSql = '';
|
|
1534
1534
|
|
|
1535
1535
|
// 获取表注释
|
|
1536
|
-
|
|
1536
|
+
let tableCommentSql = `
|
|
1537
1537
|
SELECT TABLE_COMMENT
|
|
1538
1538
|
FROM information_schema.TABLES
|
|
1539
1539
|
WHERE TABLE_SCHEMA = DATABASE()
|
|
1540
1540
|
AND TABLE_NAME = '${this.table}'
|
|
1541
1541
|
`;
|
|
1542
|
-
|
|
1542
|
+
let tableCommentResult = await this.run(tableCommentSql);
|
|
1543
1543
|
|
|
1544
1544
|
if (tableCommentResult && tableCommentResult.length > 0 && tableCommentResult[0].TABLE_COMMENT) {
|
|
1545
|
-
|
|
1545
|
+
let tableComment = tableCommentResult[0].TABLE_COMMENT;
|
|
1546
1546
|
if (tableComment && tableComment.trim() !== '') {
|
|
1547
1547
|
commentSql += `\n-- 表注释: ${tableComment}\n`;
|
|
1548
1548
|
}
|
|
1549
1549
|
}
|
|
1550
1550
|
|
|
1551
1551
|
// 获取列注释
|
|
1552
|
-
|
|
1552
|
+
let columnCommentSql = `
|
|
1553
1553
|
SELECT COLUMN_NAME, COLUMN_COMMENT
|
|
1554
1554
|
FROM information_schema.COLUMNS
|
|
1555
1555
|
WHERE TABLE_SCHEMA = DATABASE()
|
|
@@ -1558,11 +1558,11 @@ Sql.prototype._getTableComments = async function () {
|
|
|
1558
1558
|
AND COLUMN_COMMENT != ''
|
|
1559
1559
|
ORDER BY ORDINAL_POSITION
|
|
1560
1560
|
`;
|
|
1561
|
-
|
|
1561
|
+
let columnCommentResult = await this.run(columnCommentSql);
|
|
1562
1562
|
|
|
1563
1563
|
if (columnCommentResult && columnCommentResult.length > 0) {
|
|
1564
1564
|
commentSql += '\n-- 列注释:\n';
|
|
1565
|
-
for (
|
|
1565
|
+
for (let column of columnCommentResult) {
|
|
1566
1566
|
commentSql += `-- ${column.COLUMN_NAME}: ${column.COLUMN_COMMENT}\n`;
|
|
1567
1567
|
}
|
|
1568
1568
|
}
|
|
@@ -1586,7 +1586,7 @@ Sql.prototype.backup = async function (file, create = false) {
|
|
|
1586
1586
|
}
|
|
1587
1587
|
|
|
1588
1588
|
// 获取所有数据
|
|
1589
|
-
|
|
1589
|
+
let data = await this.getSql('', '', '*');
|
|
1590
1590
|
|
|
1591
1591
|
if (!data || !Array.isArray(data)) {
|
|
1592
1592
|
throw new Error('获取数据失败');
|
|
@@ -1601,12 +1601,12 @@ Sql.prototype.backup = async function (file, create = false) {
|
|
|
1601
1601
|
|
|
1602
1602
|
// 如果create为true,添加创建表语句
|
|
1603
1603
|
if (create) {
|
|
1604
|
-
|
|
1604
|
+
let createTableSql = await this._getCreateTableSql();
|
|
1605
1605
|
if (createTableSql) {
|
|
1606
1606
|
sqlContent += createTableSql + '\n\n';
|
|
1607
1607
|
|
|
1608
1608
|
// 添加表注释信息
|
|
1609
|
-
|
|
1609
|
+
let commentSql = await this._getTableComments();
|
|
1610
1610
|
if (commentSql) {
|
|
1611
1611
|
sqlContent += commentSql + '\n';
|
|
1612
1612
|
}
|
|
@@ -1614,18 +1614,18 @@ Sql.prototype.backup = async function (file, create = false) {
|
|
|
1614
1614
|
}
|
|
1615
1615
|
|
|
1616
1616
|
// 添加数据插入语句
|
|
1617
|
-
for (
|
|
1618
|
-
|
|
1619
|
-
|
|
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
1620
|
sqlContent += `INSERT INTO \`${this.table}\` (${keys}) VALUES (${values});\n`;
|
|
1621
1621
|
}
|
|
1622
1622
|
|
|
1623
1623
|
// 写入文件(这里需要文件系统模块,实际使用时需要引入 fs)
|
|
1624
|
-
|
|
1625
|
-
|
|
1624
|
+
let fs = require('fs');
|
|
1625
|
+
let path = require('path');
|
|
1626
1626
|
|
|
1627
1627
|
// 确保目录存在
|
|
1628
|
-
|
|
1628
|
+
let dir = path.dirname(file);
|
|
1629
1629
|
if (!fs.existsSync(dir)) {
|
|
1630
1630
|
fs.mkdirSync(dir, { recursive: true });
|
|
1631
1631
|
}
|
|
@@ -1649,20 +1649,20 @@ Sql.prototype.restore = async function (file) {
|
|
|
1649
1649
|
throw new TypeError('表名未设置');
|
|
1650
1650
|
}
|
|
1651
1651
|
|
|
1652
|
-
|
|
1652
|
+
let fs = require('fs');
|
|
1653
1653
|
|
|
1654
1654
|
if (!fs.existsSync(file)) {
|
|
1655
1655
|
throw new Error('备份文件不存在');
|
|
1656
1656
|
}
|
|
1657
1657
|
|
|
1658
1658
|
// 读取 SQL 文件
|
|
1659
|
-
|
|
1659
|
+
let sqlContent = fs.readFileSync(file, 'utf8');
|
|
1660
1660
|
|
|
1661
1661
|
// 解析 SQL 语句
|
|
1662
|
-
|
|
1662
|
+
let statements = this._parseSqlStatements(sqlContent);
|
|
1663
1663
|
|
|
1664
1664
|
// 执行每个 SQL 语句
|
|
1665
|
-
for (
|
|
1665
|
+
for (let stmt of statements) {
|
|
1666
1666
|
if (stmt.trim() && !stmt.startsWith('--')) {
|
|
1667
1667
|
await this.exec(stmt);
|
|
1668
1668
|
}
|
|
@@ -1678,15 +1678,15 @@ Sql.prototype.restore = async function (file) {
|
|
|
1678
1678
|
* @private
|
|
1679
1679
|
*/
|
|
1680
1680
|
Sql.prototype._parseSqlStatements = function (sql) {
|
|
1681
|
-
|
|
1681
|
+
let stmts = [];
|
|
1682
1682
|
let cur_stmt = '';
|
|
1683
1683
|
let in_str = false;
|
|
1684
1684
|
let in_cmt = false;
|
|
1685
1685
|
let quote_char = '';
|
|
1686
1686
|
|
|
1687
1687
|
for (let i = 0; i < sql.length; i++) {
|
|
1688
|
-
|
|
1689
|
-
|
|
1688
|
+
let char = sql[i];
|
|
1689
|
+
let next_char = sql[i + 1] || '';
|
|
1690
1690
|
|
|
1691
1691
|
// 处理字符串
|
|
1692
1692
|
if (!in_cmt && (char === "'" || char === '"') && (i === 0 || sql[i - 1] !== '\\')) {
|
|
@@ -1765,7 +1765,7 @@ Sql.prototype.backup = async function (file, create = false) {
|
|
|
1765
1765
|
}
|
|
1766
1766
|
|
|
1767
1767
|
// 获取所有数据
|
|
1768
|
-
|
|
1768
|
+
let data = await this.getSql('', '', '*');
|
|
1769
1769
|
|
|
1770
1770
|
if (!data || !Array.isArray(data)) {
|
|
1771
1771
|
throw new Error('获取数据失败');
|
|
@@ -1780,25 +1780,25 @@ Sql.prototype.backup = async function (file, create = false) {
|
|
|
1780
1780
|
|
|
1781
1781
|
// 如果create为true,添加创建表语句
|
|
1782
1782
|
if (create) {
|
|
1783
|
-
|
|
1783
|
+
let create_table_sql = await this._getCreateTableSql();
|
|
1784
1784
|
if (create_table_sql) {
|
|
1785
1785
|
sql_content += create_table_sql + '\n\n';
|
|
1786
1786
|
}
|
|
1787
1787
|
}
|
|
1788
1788
|
|
|
1789
1789
|
// 添加数据插入语句
|
|
1790
|
-
for (
|
|
1791
|
-
|
|
1792
|
-
|
|
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
1793
|
sql_content += `INSERT INTO \`${this.table}\` (${keys}) VALUES (${values});\n`;
|
|
1794
1794
|
}
|
|
1795
1795
|
|
|
1796
1796
|
// 写入文件(这里需要文件系统模块,实际使用时需要引入 fs)
|
|
1797
|
-
|
|
1798
|
-
|
|
1797
|
+
let fs = require('fs');
|
|
1798
|
+
let path = require('path');
|
|
1799
1799
|
|
|
1800
1800
|
// 确保目录存在
|
|
1801
|
-
|
|
1801
|
+
let dir = path.dirname(file);
|
|
1802
1802
|
if (!fs.existsSync(dir)) {
|
|
1803
1803
|
fs.mkdirSync(dir, { recursive: true });
|
|
1804
1804
|
}
|
|
@@ -1822,7 +1822,7 @@ Sql.prototype.restore = async function (file) {
|
|
|
1822
1822
|
throw new TypeError('表名未设置');
|
|
1823
1823
|
}
|
|
1824
1824
|
|
|
1825
|
-
|
|
1825
|
+
let fs = require('fs');
|
|
1826
1826
|
|
|
1827
1827
|
// 检查文件是否存在
|
|
1828
1828
|
if (!fs.existsSync(file)) {
|
|
@@ -1830,14 +1830,14 @@ Sql.prototype.restore = async function (file) {
|
|
|
1830
1830
|
}
|
|
1831
1831
|
|
|
1832
1832
|
// 读取文件内容
|
|
1833
|
-
|
|
1833
|
+
let sql_content = fs.readFileSync(file, 'utf8');
|
|
1834
1834
|
|
|
1835
1835
|
if (!sql_content) {
|
|
1836
1836
|
throw new Error('备份文件为空');
|
|
1837
1837
|
}
|
|
1838
1838
|
|
|
1839
1839
|
// 解析 SQL 语句
|
|
1840
|
-
|
|
1840
|
+
let sql_stmts = this._parseSqlStatements(sql_content);
|
|
1841
1841
|
|
|
1842
1842
|
if (!sql_stmts || sql_stmts.length === 0) {
|
|
1843
1843
|
throw new Error('未找到有效的 SQL 语句');
|
|
@@ -1848,8 +1848,8 @@ Sql.prototype.restore = async function (file) {
|
|
|
1848
1848
|
|
|
1849
1849
|
try {
|
|
1850
1850
|
// 逐个执行SQL语句
|
|
1851
|
-
for (
|
|
1852
|
-
|
|
1851
|
+
for (let sql of sql_stmts) {
|
|
1852
|
+
let trim_sql = sql.trim();
|
|
1853
1853
|
if (trim_sql) {
|
|
1854
1854
|
await this.exec(trim_sql);
|
|
1855
1855
|
}
|
|
@@ -1885,8 +1885,8 @@ Sql.prototype.restore = async function (file) {
|
|
|
1885
1885
|
*/
|
|
1886
1886
|
Sql.prototype._getCreateTableSql = async function () {
|
|
1887
1887
|
// 查询表结构信息(MySQL 使用 SHOW CREATE TABLE)
|
|
1888
|
-
|
|
1889
|
-
|
|
1888
|
+
let sql = `SHOW CREATE TABLE \`${this.table}\``;
|
|
1889
|
+
let result = await this.run(sql);
|
|
1890
1890
|
|
|
1891
1891
|
if (result && result.length > 0 && result[0]['Create Table']) {
|
|
1892
1892
|
// 在创建表语句中添加 IF NOT EXISTS 条件
|
package/test2.js
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MySQL错误处理测试文件
|
|
3
|
+
* @file test2.js
|
|
4
|
+
* @description 测试Mysql类的错误日志输出功能
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { Mysql } = require('./index.js');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 错误测试类
|
|
11
|
+
* @class ErrorTestMysql
|
|
12
|
+
*/
|
|
13
|
+
class ErrorTestMysql {
|
|
14
|
+
/**
|
|
15
|
+
* 构造函数
|
|
16
|
+
* @param {object} config - 数据库配置
|
|
17
|
+
*/
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.config = {
|
|
20
|
+
host: '127.0.0.1',
|
|
21
|
+
port: 3306,
|
|
22
|
+
user: 'root',
|
|
23
|
+
password: 'Asd159357',
|
|
24
|
+
database: 'test_db',
|
|
25
|
+
debug: true,
|
|
26
|
+
...config || {}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
this._mysql = null;
|
|
30
|
+
this._test_results = [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 初始化测试
|
|
35
|
+
* @returns {Promise<boolean>}
|
|
36
|
+
*/
|
|
37
|
+
async _init() {
|
|
38
|
+
try {
|
|
39
|
+
this._mysql = new Mysql(this.config);
|
|
40
|
+
await this._mysql.open();
|
|
41
|
+
console.log('=== 错误处理测试初始化完成 ===');
|
|
42
|
+
return true;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error('初始化失败:', error.message);
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 记录测试结果
|
|
51
|
+
* @param {string} test_name - 测试名称
|
|
52
|
+
* @param {boolean} success - 是否成功
|
|
53
|
+
* @param {string} message - 测试信息
|
|
54
|
+
*/
|
|
55
|
+
_recordResult(test_name, success, message) {
|
|
56
|
+
this._test_results.push({
|
|
57
|
+
test_name,
|
|
58
|
+
success,
|
|
59
|
+
message,
|
|
60
|
+
timestamp: new Date().toISOString()
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const status = success ? '✓' : '✗';
|
|
64
|
+
console.log(`${status} ${test_name}: ${message}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 测试无效表查询错误
|
|
69
|
+
* @returns {Promise<boolean>}
|
|
70
|
+
*/
|
|
71
|
+
async testInvalidTable() {
|
|
72
|
+
try {
|
|
73
|
+
console.log('\n--- 测试1: 查询不存在的表 ---');
|
|
74
|
+
|
|
75
|
+
// 执行无效SQL
|
|
76
|
+
const invalid_sql = 'SELECT * FROM non_existent_table_xyz';
|
|
77
|
+
const result = await this._mysql.run(invalid_sql);
|
|
78
|
+
|
|
79
|
+
// 检查错误信息
|
|
80
|
+
if (this._mysql.error) {
|
|
81
|
+
console.log('错误信息:', this._mysql.error);
|
|
82
|
+
this._recordResult('无效表查询', true, `成功捕获错误: ${this._mysql.error.message}`);
|
|
83
|
+
return true;
|
|
84
|
+
} else {
|
|
85
|
+
this._recordResult('无效表查询', false, '未捕获到预期的错误');
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
this._recordResult('无效表查询', false, `异常错误: ${error.message}`);
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 测试无效SQL语法错误
|
|
96
|
+
* @returns {Promise<boolean>}
|
|
97
|
+
*/
|
|
98
|
+
async testInvalidSyntax() {
|
|
99
|
+
try {
|
|
100
|
+
console.log('\n--- 测试2: 无效SQL语法 ---');
|
|
101
|
+
|
|
102
|
+
// 执行语法错误的SQL
|
|
103
|
+
const invalid_sql = 'SELECT FROM test_users';
|
|
104
|
+
const result = await this._mysql.run(invalid_sql);
|
|
105
|
+
|
|
106
|
+
// 检查错误信息
|
|
107
|
+
if (this._mysql.error) {
|
|
108
|
+
console.log('错误信息:', this._mysql.error);
|
|
109
|
+
this._recordResult('无效SQL语法', true, `成功捕获错误: ${this._mysql.error.message}`);
|
|
110
|
+
return true;
|
|
111
|
+
} else {
|
|
112
|
+
this._recordResult('无效SQL语法', false, '未捕获到预期的错误');
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
this._recordResult('无效SQL语法', false, `异常错误: ${error.message}`);
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 测试exec方法错误处理
|
|
123
|
+
* @returns {Promise<boolean>}
|
|
124
|
+
*/
|
|
125
|
+
async testExecError() {
|
|
126
|
+
try {
|
|
127
|
+
console.log('\n--- 测试3: exec方法错误处理 ---');
|
|
128
|
+
|
|
129
|
+
// 执行无效INSERT语句
|
|
130
|
+
const invalid_sql = 'INSERT INTO non_existent_table (name) VALUES (?)';
|
|
131
|
+
const result = await this._mysql.exec(invalid_sql, ['test']);
|
|
132
|
+
|
|
133
|
+
// 检查错误信息
|
|
134
|
+
if (this._mysql.error) {
|
|
135
|
+
console.log('错误信息:', this._mysql.error);
|
|
136
|
+
this._recordResult('exec方法错误', true, `成功捕获错误: ${this._mysql.error.message}`);
|
|
137
|
+
return true;
|
|
138
|
+
} else {
|
|
139
|
+
this._recordResult('exec方法错误', false, '未捕获到预期的错误');
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
} catch (error) {
|
|
143
|
+
this._recordResult('exec方法错误', false, `异常错误: ${error.message}`);
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 测试DB类错误处理
|
|
150
|
+
* @returns {Promise<boolean>}
|
|
151
|
+
*/
|
|
152
|
+
async testDbClassError() {
|
|
153
|
+
try {
|
|
154
|
+
console.log('\n--- 测试4: DB类错误处理 ---');
|
|
155
|
+
|
|
156
|
+
// 获取DB实例并尝试操作不存在的表
|
|
157
|
+
const db = this._mysql.db().new('non_existent_table_db', 'id');
|
|
158
|
+
|
|
159
|
+
// 尝试获取数据
|
|
160
|
+
const result = await db.get({ id: 1 });
|
|
161
|
+
|
|
162
|
+
// 检查错误信息
|
|
163
|
+
if (this._mysql.error) {
|
|
164
|
+
console.log('错误信息:', this._mysql.error);
|
|
165
|
+
this._recordResult('DB类错误', true, `成功捕获错误: ${this._mysql.error.message}`);
|
|
166
|
+
return true;
|
|
167
|
+
} else {
|
|
168
|
+
this._recordResult('DB类错误', false, '未捕获到预期的错误');
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
} catch (error) {
|
|
172
|
+
this._recordResult('DB类错误', false, `异常错误: ${error.message}`);
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 测试连接失败错误(使用错误配置)
|
|
179
|
+
* @returns {Promise<boolean>}
|
|
180
|
+
*/
|
|
181
|
+
async testConnectionError() {
|
|
182
|
+
try {
|
|
183
|
+
console.log('\n--- 测试5: 连接失败错误 ---');
|
|
184
|
+
|
|
185
|
+
// 使用错误配置创建连接
|
|
186
|
+
const bad_config = {
|
|
187
|
+
host: '127.0.0.1',
|
|
188
|
+
port: 3307, // 错误端口
|
|
189
|
+
user: 'root',
|
|
190
|
+
password: 'wrong_password',
|
|
191
|
+
database: 'test_db',
|
|
192
|
+
debug: true
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const bad_mysql = new Mysql(bad_config);
|
|
196
|
+
const result = await bad_mysql.open();
|
|
197
|
+
|
|
198
|
+
// 连接应该失败
|
|
199
|
+
if (result === false) {
|
|
200
|
+
console.log('连接失败(预期行为)');
|
|
201
|
+
this._recordResult('连接失败错误', true, '成功检测到连接失败');
|
|
202
|
+
return true;
|
|
203
|
+
} else {
|
|
204
|
+
this._recordResult('连接失败错误', false, '连接意外成功,测试失败');
|
|
205
|
+
await bad_mysql.close();
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.log('捕获到异常:', error.message);
|
|
210
|
+
this._recordResult('连接失败错误', true, `成功捕获连接异常: ${error.message}`);
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* 测试参数验证错误
|
|
217
|
+
* @returns {Promise<boolean>}
|
|
218
|
+
*/
|
|
219
|
+
async testParameterValidation() {
|
|
220
|
+
try {
|
|
221
|
+
console.log('\n--- 测试6: 参数验证错误 ---');
|
|
222
|
+
|
|
223
|
+
// 测试无效参数类型
|
|
224
|
+
let error_count = 0;
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
await this._mysql.run(null);
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.log('参数验证错误1:', error.message);
|
|
230
|
+
error_count++;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
await this._mysql.run('SELECT 1', 'invalid');
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.log('参数验证错误2:', error.message);
|
|
237
|
+
error_count++;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (error_count === 2) {
|
|
241
|
+
this._recordResult('参数验证错误', true, '成功捕获2个参数验证错误');
|
|
242
|
+
return true;
|
|
243
|
+
} else {
|
|
244
|
+
this._recordResult('参数验证错误', false, `只捕获到 ${error_count} 个错误,预期2个`);
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
} catch (error) {
|
|
248
|
+
this._recordResult('参数验证错误', false, `异常错误: ${error.message}`);
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* 运行所有错误测试
|
|
255
|
+
* @returns {Promise<object>}
|
|
256
|
+
*/
|
|
257
|
+
async runAllTests() {
|
|
258
|
+
console.log('\n=== 开始运行MySQL错误处理测试 ===\n');
|
|
259
|
+
|
|
260
|
+
// 初始化测试
|
|
261
|
+
const init_success = await this._init();
|
|
262
|
+
if (!init_success) {
|
|
263
|
+
console.log('测试初始化失败');
|
|
264
|
+
return this._getTestSummary();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 定义测试用例
|
|
268
|
+
const test_cases = [
|
|
269
|
+
{ name: '无效表查询', method: 'testInvalidTable' },
|
|
270
|
+
{ name: '无效SQL语法', method: 'testInvalidSyntax' },
|
|
271
|
+
{ name: 'exec方法错误', method: 'testExecError' },
|
|
272
|
+
{ name: 'DB类错误', method: 'testDbClassError' },
|
|
273
|
+
{ name: '连接失败错误', method: 'testConnectionError' },
|
|
274
|
+
{ name: '参数验证错误', method: 'testParameterValidation' }
|
|
275
|
+
];
|
|
276
|
+
|
|
277
|
+
// 执行测试
|
|
278
|
+
for (const test_case of test_cases) {
|
|
279
|
+
try {
|
|
280
|
+
console.log(`执行测试: ${test_case.name}`);
|
|
281
|
+
await this[test_case.method]();
|
|
282
|
+
} catch (error) {
|
|
283
|
+
console.error(`测试异常: ${test_case.name}`, error);
|
|
284
|
+
this._recordResult(test_case.name, false, `测试执行异常: ${error.message}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// 关闭连接
|
|
289
|
+
if (this._mysql) {
|
|
290
|
+
await this._mysql.close();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return this._getTestSummary();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* 获取测试摘要
|
|
298
|
+
* @returns {object}
|
|
299
|
+
*/
|
|
300
|
+
_getTestSummary() {
|
|
301
|
+
const total = this._test_results.length;
|
|
302
|
+
const passed = this._test_results.filter(r => r.success).length;
|
|
303
|
+
const failed = total - passed;
|
|
304
|
+
|
|
305
|
+
console.log('\n=== 测试结果摘要 ===');
|
|
306
|
+
console.log(`总测试数: ${total}`);
|
|
307
|
+
console.log(`通过: ${passed}`);
|
|
308
|
+
console.log(`失败: ${failed}`);
|
|
309
|
+
console.log(`通过率: ${((passed / total) * 100).toFixed(1)}%`);
|
|
310
|
+
|
|
311
|
+
// 显示失败的测试
|
|
312
|
+
const failed_tests = this._test_results.filter(r => !r.success);
|
|
313
|
+
if (failed_tests.length > 0) {
|
|
314
|
+
console.log('\n失败的测试:');
|
|
315
|
+
failed_tests.forEach(test => {
|
|
316
|
+
console.log(` ✗ ${test.test_name}: ${test.message}`);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
total,
|
|
322
|
+
passed,
|
|
323
|
+
failed,
|
|
324
|
+
results: this._test_results
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* 主测试函数
|
|
330
|
+
* @returns {Promise<void>}
|
|
331
|
+
*/
|
|
332
|
+
async main() {
|
|
333
|
+
try {
|
|
334
|
+
const summary = await this.runAllTests();
|
|
335
|
+
|
|
336
|
+
if (summary.failed > 0) {
|
|
337
|
+
console.log('\n❌ 部分测试失败');
|
|
338
|
+
process.exit(1);
|
|
339
|
+
} else {
|
|
340
|
+
console.log('\n✅ 所有测试通过!');
|
|
341
|
+
process.exit(0);
|
|
342
|
+
}
|
|
343
|
+
} catch (error) {
|
|
344
|
+
console.error('测试执行异常:', error);
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// 模块导出
|
|
351
|
+
exports.ErrorTestMysql = ErrorTestMysql;
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* 快速测试函数
|
|
355
|
+
* @param {object} config - 数据库配置
|
|
356
|
+
*/
|
|
357
|
+
const quick_test = async function(config) {
|
|
358
|
+
const tester = new ErrorTestMysql(config);
|
|
359
|
+
await tester.main();
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
// 如果直接运行此文件,执行测试
|
|
363
|
+
if (require.main === module) {
|
|
364
|
+
const test_config = {
|
|
365
|
+
host: process.env.DB_HOST || '127.0.0.1',
|
|
366
|
+
port: parseInt(process.env.DB_PORT) || 3306,
|
|
367
|
+
user: process.env.DB_USER || 'root',
|
|
368
|
+
password: process.env.DB_PASSWORD || 'Asd159357',
|
|
369
|
+
database: process.env.DB_NAME || 'test_db',
|
|
370
|
+
debug: process.env.DEBUG === 'true'
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
console.log('使用配置:', {
|
|
374
|
+
...test_config,
|
|
375
|
+
password: '***'
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
quick_test(test_config);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
exports.quick_test = quick_test;
|