mm_mysql 2.2.0 → 2.2.1

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.
Files changed (7) hide show
  1. package/README.md +100 -34
  2. package/db.js +144 -170
  3. package/index.js +403 -158
  4. package/package.json +2 -2
  5. package/sql.js +190 -349
  6. package/test.js +218 -128
  7. package/test_create_table.js +0 -193
package/db.js CHANGED
@@ -1,4 +1,6 @@
1
- const Sql = require('./sql');
1
+ const {
2
+ Sql
3
+ } = require('./sql');
2
4
 
3
5
  /**
4
6
  * @class 数据库操作类
@@ -10,7 +12,11 @@ class DB extends Sql {
10
12
  * @param {Object} mysql - MySQL实例
11
13
  */
12
14
  constructor(mysql) {
13
- super(mysql.run, mysql.exec);
15
+ // 正确绑定runexec方法的上下文
16
+ super(
17
+ mysql.run ? mysql.run.bind(mysql) : null,
18
+ mysql.exec ? mysql.exec.bind(mysql) : null
19
+ );
14
20
  // 保存mysql实例引用
15
21
  this._mysql = mysql;
16
22
  // 事务中
@@ -72,157 +78,139 @@ DB.prototype.new = function (table, key) {
72
78
 
73
79
  /**
74
80
  * 执行SQL查询
75
- * @param {string} sql SQL语句
76
- * @param {Array} params 参数数组
77
- * @param {Number} timeout 超时时间(毫秒)
78
- * @returns {Promise<Object>} 查询结果
81
+ * @param {String} sql - SQL语句
82
+ * @param {Array} params - 参数数组
83
+ * @returns {Promise<Array>}
84
+ * @throws {TypeError} 当sql参数无效时
79
85
  */
80
- DB.prototype.run = async function (sql, params = [], timeout = 30000) {
81
- if (!this._mysql) {
82
- throw new Error('MySQL实例未初始化');
83
- }
84
- try {
85
- // 使用Promise.race实现超时控制
86
- return await Promise.race([
87
- this._mysql.run(sql, params),
88
- this._createTimeoutPromise(timeout, `查询执行超时: ${sql.substring(0, 100)}...`)
89
- ]);
90
- } catch (error) {
91
- $.log.error(`[DB] [run] 查询执行失败`, {
92
- error: error.message,
93
- sql: sql.substring(0, 200),
94
- params: JSON.stringify(params.slice(0, 5))
95
- });
96
- throw error;
97
- }
86
+ DB.prototype.run = async function(sql, params = []) {
87
+ // 参数校验
88
+ if (typeof sql !== 'string' || sql.trim() === '') {
89
+ throw new TypeError('sql must be non-empty string');
90
+ }
91
+ if (!Array.isArray(params)) {
92
+ throw new TypeError('params must be array');
93
+ }
94
+
95
+ try {
96
+ return await this._mysql.run(sql, params);
97
+ } catch (error) {
98
+ this.logger('error', 'SQL执行失败', error);
99
+ // 返回空数组作为默认值,保持返回值类型一致
100
+ return [];
101
+ }
98
102
  };
99
103
 
100
104
  /**
101
- * 执行SQL命令
102
- * @param {string} sql SQL语句
103
- * @param {Number} timeout 超时时间(毫秒)
104
- * @returns {Promise<Object>} 执行结果
105
+ * 执行SQL语句
106
+ * @param {String} sql - SQL语句
107
+ * @param {Array} params - 参数数组
108
+ * @returns {Promise<Object>}
109
+ * @throws {TypeError} 当sql参数无效时
105
110
  */
106
- DB.prototype.exec = async function (sql, timeout = 60000) {
107
- if (!this._mysql) {
108
- throw new Error('MySQL实例未初始化');
109
- }
110
- try {
111
- // 增加超时时间到60秒,避免连接超时问题
112
- return await this._mysql.exec(sql, []);
113
- } catch (error) {
114
- $.log.error(`[DB] [exec] 命令执行失败`, {
115
- error: error.message,
116
- sql: sql.substring(0, 200)
117
- });
118
- throw error;
119
- }
111
+ DB.prototype.exec = async function(sql, params = []) {
112
+ // 参数校验
113
+ if (typeof sql !== 'string' || sql.trim() === '') {
114
+ throw new TypeError('sql must be non-empty string');
115
+ }
116
+ if (!Array.isArray(params)) {
117
+ throw new TypeError('params must be array');
118
+ }
119
+
120
+ try {
121
+ return await this._mysql.exec(sql, params);
122
+ } catch (error) {
123
+ this.logger('error', 'SQL执行失败', error);
124
+ // 返回空数组作为默认值,保持返回值类型一致
125
+ return 0;
126
+ }
120
127
  };
121
128
 
122
129
  /**
123
130
  * 获取数据库连接
124
- * @param {Number} timeout 超时时间(毫秒)
125
- * @returns {Promise<Object>} 数据库连接对象
131
+ * @returns {Promise<Object>}
126
132
  */
127
- DB.prototype.getConn = async function (timeout = 60000) {
128
- if (!this._mysql) {
129
- throw new Error('MySQL实例未初始化');
130
- }
131
- try {
132
- // 增加超时时间到60秒,直接调用mysql实例的getConn方法
133
- return await this._mysql.getConn();
134
- } catch (error) {
135
- $.log.error(`[DB] [getConn] 获取连接失败`, {
136
- error: error.message
137
- });
138
- throw error;
139
- }
133
+ DB.prototype.getConn = async function() {
134
+ try {
135
+ return await this._mysql.getConn();
136
+ } catch (error) {
137
+ this.logger('error', '获取连接失败', error);
138
+ // 返回null作为默认值,保持返回值类型一致
139
+ return null;
140
+ }
140
141
  };
141
142
 
142
143
  /**
143
- * 事务开始
144
- * @param {String} identifier 事务标识符
145
- * @param {Number} timeout 超时时间(毫秒)
146
- * @return {Promise<Object>} 执行结果
144
+ * 开始事务
145
+ * @returns {Promise<Object>}
147
146
  */
148
- DB.prototype.start = async function (identifier = "point_1", timeout = 15000) {
149
- this.task = 1;
150
- try {
151
- return await this.exec("SET AUTOCOMMIT=0;BEGIN;", timeout);
152
- } catch (err) {
153
- this.task = 0;
154
- $.log.error(`[DB] [start] 事务开始失败: ${err.message}`);
155
- throw err;
156
- }
147
+ DB.prototype.start = async function() {
148
+ try {
149
+ return await this._mysql.beginTransaction();
150
+ } catch (error) {
151
+ this.logger('error', '开始事务失败', error);
152
+ // 返回null作为默认值,保持返回值类型一致
153
+ return null;
154
+ }
157
155
  };
158
156
 
159
157
  /**
160
- * 提交
161
- * @param {Number} timeout 超时时间(毫秒)
162
- * @return {Promise<Object>} 执行结果
158
+ * 提交事务
159
+ * @param {Object} transaction - 事务对象
160
+ * @returns {Promise<void>}
161
+ * @throws {TypeError} 当transaction参数无效时
163
162
  */
164
- DB.prototype.commit = async function (timeout = 15000) {
165
- if (this.task !== 1) {
166
- $.log.warn("[DB] [commit] 没有活跃的事务可提交");
167
- return 0;
168
- }
169
- this.task = 2;
170
- try {
171
- const result = await this.exec("COMMIT;", timeout);
172
- // 重置事务状态
173
- this.task = 0;
174
- this.task_sql = '';
175
- return result;
176
- } catch (err) {
177
- $.log.error(`[DB] [commit] 事务提交失败: ${err.message}`);
178
- // 尝试回滚
179
- try {
180
- await this.exec("ROLLBACK;", timeout);
181
- } catch (rollbackErr) {
182
- $.log.error(`[DB] [commit] 事务回滚也失败: ${rollbackErr.message}`);
183
- }
184
- this.task = 0;
185
- this.task_sql = '';
186
- throw err;
187
- }
163
+ DB.prototype.commit = async function(transaction) {
164
+ // 参数校验
165
+ if (!transaction || typeof transaction !== 'object') {
166
+ throw new TypeError('transaction must be object');
167
+ }
168
+
169
+ try {
170
+ await transaction.commit();
171
+ } catch (error) {
172
+ this.logger('error', '提交事务失败', error);
173
+ }
188
174
  };
189
175
 
190
176
  /**
191
- * 事务结束
177
+ * 回滚事务
178
+ * @param {Object} transaction - 事务对象
179
+ * @returns {Promise<void>}
180
+ * @throws {TypeError} 当transaction参数无效时
192
181
  */
193
- DB.prototype.end = function () {
194
- // 确保事务状态被重置
195
- this.task = 0;
196
- this.task_sql = '';
182
+ DB.prototype.back = async function(transaction) {
183
+ // 参数校验
184
+ if (!transaction || typeof transaction !== 'object') {
185
+ throw new TypeError('transaction must be object');
186
+ }
187
+
188
+ try {
189
+ await transaction.rollback();
190
+ } catch (error) {
191
+ this.logger('error', '回滚事务失败', error);
192
+ }
197
193
  };
198
194
 
199
195
  /**
200
- * 滚回
201
- * @param {String} identifier 事务标识符
202
- * @param {Number} timeout 超时时间(毫秒)
203
- * @return {Promise<Object>} 执行结果
196
+ * 在事务中执行多个操作
197
+ * @param {Function} callback - 包含事务操作的回调函数
198
+ * @returns {Promise<*>}
199
+ * @throws {TypeError} 当callback参数无效时
204
200
  */
205
- DB.prototype.back = async function (identifier = "point_1", timeout = 15000) {
206
- if (this.task !== 1) {
207
- $.log.warn("[DB] [back] 没有活跃的事务可回滚");
208
- this.task = 0;
209
- this.task_sql = '';
210
- return 0;
211
- }
212
- this.task = 3;
213
- try {
214
- await this.exec("ROLLBACK;", timeout);
215
- const result = await this.exec("SET AUTOCOMMIT=1;", timeout);
216
- // 重置事务状态
217
- this.task = 0;
218
- this.task_sql = '';
219
- return result;
220
- } catch (err) {
221
- $.log.error(`[DB] [back] 事务回滚失败: ${err.message}`);
222
- this.task = 0;
223
- this.task_sql = '';
224
- throw err;
225
- }
201
+ DB.prototype.transaction = async function(callback) {
202
+ // 参数校验
203
+ if (typeof callback !== 'function') {
204
+ throw new TypeError('callback must be function');
205
+ }
206
+
207
+ try {
208
+ return await this._mysql.transaction(callback);
209
+ } catch (error) {
210
+ this.logger('error', '事务执行失败', error);
211
+ // 返回null作为默认值,保持返回值类型一致
212
+ return null;
213
+ }
226
214
  };
227
215
 
228
216
  /**
@@ -249,7 +237,7 @@ DB.prototype.tables = async function (table, timeout = 15000) {
249
237
  // 使用原生方法提取表名
250
238
  return list.map(item => item[key]);
251
239
  } catch (err) {
252
- $.log.error(`[DB] [tables] 获取表名失败: ${err.message}`);
240
+ this.logger('error', '获取表名失败', err);
253
241
  return [];
254
242
  }
255
243
  };
@@ -265,8 +253,7 @@ DB.prototype.fields = async function (table, field_name, timeout = 15000) {
265
253
  try {
266
254
  const targetTable = table || this.table;
267
255
  if (!targetTable) {
268
- $.log.error("[DB] [fields] 未指定表名");
269
- return [];
256
+ throw new TypeError('table must be specified');
270
257
  }
271
258
  const field = 'COLUMN_NAME as `name`,ORDINAL_POSITION as `cid`,COLUMN_DEFAULT as `dflt_value`,IS_NULLABLE as `notnull`,COLUMN_TYPE as `type`,COLUMN_KEY as `pk`,EXTRA as `auto`,COLUMN_COMMENT as `note`';
272
259
  let sql = "select " + field + " from information_schema.COLUMNS where `table_name` = '" + targetTable +
@@ -282,7 +269,7 @@ DB.prototype.fields = async function (table, field_name, timeout = 15000) {
282
269
  }
283
270
  return list;
284
271
  } catch (err) {
285
- $.log.error(`[DB] [fields] 获取字段信息失败: ${err.message}`);
272
+ this.logger('error', '获取字段信息失败', err);
286
273
  return [];
287
274
  }
288
275
  };
@@ -300,8 +287,7 @@ DB.prototype.fields = async function (table, field_name, timeout = 15000) {
300
287
  DB.prototype.addTable = async function (table, field, type = 'int', auto = true, commit = '', timeout = 15000) {
301
288
  try {
302
289
  if (!table || typeof table !== 'string') {
303
- $.log.error("[DB] [addTable] 表名无效");
304
- return 0;
290
+ throw new TypeError('table must be a valid string');
305
291
  }
306
292
  if (!field) {
307
293
  field = "id";
@@ -324,9 +310,9 @@ DB.prototype.addTable = async function (table, field, type = 'int', auto = true,
324
310
  const result = await this.exec(sql, timeout);
325
311
  // 设置实例的表名属性,以便后续操作使用
326
312
  this.table = table;
327
- return result && result.affectedRows !== undefined ? 1 : 0;
313
+ return result;
328
314
  } catch (err) {
329
- $.log.error(`[DB] [addTable] 创建表失败: ${err.message}`);
315
+ this.logger('error', '创建表失败', err);
330
316
  return 0;
331
317
  }
332
318
  };
@@ -339,8 +325,7 @@ DB.prototype.addTable = async function (table, field, type = 'int', auto = true,
339
325
  */
340
326
  DB.prototype.dropTable = function (table, timeout = 15000) {
341
327
  if (!table || typeof table !== 'string') {
342
- $.log.error("[DB] [dropTable] 表名无效");
343
- return 0;
328
+ throw new TypeError('table must be a valid string');
344
329
  }
345
330
  return this.exec("DROP TABLE IF EXISTS \`" + table + "\`;", timeout);
346
331
  };
@@ -354,8 +339,7 @@ DB.prototype.dropTable = function (table, timeout = 15000) {
354
339
  */
355
340
  DB.prototype.renameTable = function (table, new_table, timeout = 15000) {
356
341
  if (!table || !new_table) {
357
- $.log.error("[DB] [renameTable] 表名参数不完整");
358
- return 0;
342
+ throw new TypeError('table and new_table must be specified');
359
343
  }
360
344
  return this.exec("RENAME TABLE \`" + table + "\` TO \`" + new_table + "\`;", timeout);
361
345
  };
@@ -375,8 +359,7 @@ DB.prototype.addField = async function (field, type, value = '', not_null = true
375
359
  try {
376
360
  // 确保表名已设置
377
361
  if (!this.table || !field || !type) {
378
- $.log.error("[DB] [addField] 表名、字段名或类型未指定");
379
- return 0;
362
+ throw new TypeError('table, field, and type must be specified');
380
363
  }
381
364
 
382
365
  // 构建字段定义
@@ -409,9 +392,9 @@ DB.prototype.addField = async function (field, type, value = '', not_null = true
409
392
  // 使用ADD COLUMN而不是CHANGE COLUMN
410
393
  const sql = `ALTER TABLE \`${this.table}\` ADD COLUMN ${fieldDef};`;
411
394
  const result = await this.exec(sql, timeout);
412
- return result && result.affectedRows !== undefined ? 1 : 0;
395
+ return result
413
396
  } catch (err) {
414
- $.log.error(`[DB] [addField] 添加字段失败: ${err.message}`);
397
+ this.logger('error', '添加字段失败', err);
415
398
  return 0;
416
399
  }
417
400
  };
@@ -431,8 +414,7 @@ DB.prototype.addField = async function (field, type, value = '', not_null = true
431
414
  DB.prototype.setField = async function (field, type, value = '', not_null = true, auto = false, isKey = false, new_name = '', timeout = 15000) {
432
415
  try {
433
416
  if (!field || !type || !this.table) {
434
- $.log.error("[DB] [setField] 参数不完整或表名未设置");
435
- return 0;
417
+ throw new TypeError('field, type, and table must be specified');
436
418
  }
437
419
  const targetName = new_name || field;
438
420
  let fieldDef = `\`${targetName}\` ${type}`;
@@ -455,9 +437,9 @@ DB.prototype.setField = async function (field, type, value = '', not_null = true
455
437
  }
456
438
  sql += ";";
457
439
  const result = await this.exec(sql, timeout);
458
- return result && result.affectedRows !== undefined ? 1 : 0;
440
+ return result;
459
441
  } catch (err) {
460
- $.log.error(`[DB] [setField] 修改字段失败: ${err.message}`);
442
+ this.logger('error', '修改字段失败', err);
461
443
  return 0;
462
444
  }
463
445
  };
@@ -472,8 +454,7 @@ DB.prototype.setField = async function (field, type, value = '', not_null = true
472
454
  */
473
455
  DB.prototype.editField = function (table, field, type, timeout = 15000) {
474
456
  if (!table || !field || !type) {
475
- $.log.error("[DB] [editField] 参数不完整");
476
- return 0;
457
+ throw new TypeError('table, field, and type must be specified');
477
458
  }
478
459
  return this.exec("ALTER TABLE \`" + table + "\` MODIFY COLUMN \`" + field + "\` " + type + ";", timeout);
479
460
  };
@@ -487,8 +468,7 @@ DB.prototype.editField = function (table, field, type, timeout = 15000) {
487
468
  */
488
469
  DB.prototype.delField = function (table, field, timeout = 15000) {
489
470
  if (!table || !field) {
490
- $.log.error("[DB] [delField] 参数不完整");
491
- return 0;
471
+ throw new TypeError('table and field must be specified');
492
472
  }
493
473
  return this.exec("ALTER TABLE \`" + table + "\` DROP COLUMN \`" + field + "\`;", timeout);
494
474
  };
@@ -504,8 +484,7 @@ DB.prototype.delField = function (table, field, timeout = 15000) {
504
484
  */
505
485
  DB.prototype.renameField = function (table, field, new_field, type, timeout = 15000) {
506
486
  if (!table || !field || !new_field) {
507
- $.log.error("[DB] [renameField] 参数不完整");
508
- return 0;
487
+ throw new TypeError('table, field, and new_field must be specified');
509
488
  }
510
489
  return this.exec("ALTER TABLE \`" + table + "\` CHANGE \`" + field + "\` \`" + new_field + "\` " + type + ";", timeout);
511
490
  };
@@ -518,8 +497,7 @@ DB.prototype.renameField = function (table, field, new_field, type, timeout = 15
518
497
  */
519
498
  DB.prototype.getCreateTable = async function (table, timeout = 15000) {
520
499
  if (!table) {
521
- $.log.error("[DB] [getCreateTable] 表名不能为空");
522
- return '';
500
+ throw new TypeError('table must be specified');
523
501
  }
524
502
  try {
525
503
  const list = await this.run("SHOW CREATE TABLE \`" + table + "\`;", [], timeout);
@@ -528,7 +506,7 @@ DB.prototype.getCreateTable = async function (table, timeout = 15000) {
528
506
  }
529
507
  return '';
530
508
  } catch (err) {
531
- $.log.error(`[DB] [getCreateTable] 获取表结构失败: ${err.message}`);
509
+ this.logger('error', '获取表结构失败', err);
532
510
  return '';
533
511
  }
534
512
  };
@@ -542,8 +520,7 @@ DB.prototype.getCreateTable = async function (table, timeout = 15000) {
542
520
  */
543
521
  DB.prototype.getTableData = async function (table, batchSize = 100, timeout = 60000) {
544
522
  if (!table) {
545
- $.log.error("[DB] [getTableData] 表名不能为空");
546
- return '';
523
+ throw new TypeError('table must be specified');
547
524
  }
548
525
  try {
549
526
  // 获取表数据
@@ -579,15 +556,15 @@ DB.prototype.getTableData = async function (table, batchSize = 100, timeout = 60
579
556
  }).join(',');
580
557
  sql += `INSERT INTO \`${table}\` VALUES (${rowValues});\n`;
581
558
  } catch (rowErr) {
582
- $.log.error(`[DB] [getTableData] 处理行数据失败: ${rowErr.message}`);
583
- }
559
+ this.logger('error', '处理行数据失败', rowErr);
560
+ }
584
561
  }
585
562
  }
586
563
  sql += '\n';
587
564
  }
588
565
  return sql;
589
566
  } catch (err) {
590
- $.log.error(`[DB] [getTableData] 获取表数据失败: ${err.message}`);
567
+ this.logger('error', '获取表数据失败', err);
591
568
  return '';
592
569
  }
593
570
  };
@@ -600,8 +577,7 @@ DB.prototype.getTableData = async function (table, batchSize = 100, timeout = 60
600
577
  */
601
578
  DB.prototype.emptyTable = function (table, timeout = 15000) {
602
579
  if (!table || typeof table !== 'string') {
603
- $.log.error("[DB] [emptyTable] 表名无效");
604
- return 0;
580
+ throw new TypeError('table must be a valid string');
605
581
  }
606
582
  return this.exec("TRUNCATE TABLE \`" + table + "\`;", timeout);
607
583
  };
@@ -620,7 +596,7 @@ DB.prototype.hasTable = async function (table, timeout = 15000) {
620
596
  const list = await this.run("SHOW TABLES LIKE '" + table + "'", [], timeout);
621
597
  return list && list.length > 0;
622
598
  } catch (err) {
623
- $.log.error(`[DB] [hasTable] 检查表是否存在失败: ${err.message}`);
599
+ this.logger('error', '检查表是否存在失败', err);
624
600
  return false;
625
601
  }
626
602
  };
@@ -640,7 +616,7 @@ DB.prototype.hasField = async function (table, field, timeout = 15000) {
640
616
  const list = await this.fields(table, field, timeout);
641
617
  return list && list.length > 0;
642
618
  } catch (err) {
643
- $.log.error(`[DB] [hasField] 检查字段是否存在失败: ${err.message}`);
619
+ this.logger('error', '检查字段是否存在失败', err);
644
620
  return false;
645
621
  }
646
622
  };
@@ -654,8 +630,7 @@ DB.prototype.hasField = async function (table, field, timeout = 15000) {
654
630
  */
655
631
  DB.prototype.backupTable = function (table, backup, timeout = 60000) {
656
632
  if (!table || !backup) {
657
- $.log.error("[DB] [backupTable] 参数不完整");
658
- return 0;
633
+ throw new TypeError('table and backup must be specified');
659
634
  }
660
635
  return this.exec("CREATE TABLE \`" + backup + "\` LIKE \`" + table + "\`; INSERT INTO \`" + backup + "\` SELECT * FROM \`" + table + "\`;", timeout);
661
636
  };
@@ -670,8 +645,7 @@ DB.prototype.backupTable = function (table, backup, timeout = 60000) {
670
645
  */
671
646
  DB.prototype.createTable = function (table, model, key = 'id', timeout = 15000) {
672
647
  if (!table || !model || !model.fields) {
673
- $.log.error("[DB] [createTable] 参数不完整");
674
- return 0;
648
+ throw new TypeError('table, model, and model.fields must be specified');
675
649
  }
676
650
  var fields = '';
677
651
  for (const field in model.fields) {
@@ -743,4 +717,4 @@ DB.prototype.createTable = function (table, model, key = 'id', timeout = 15000)
743
717
 
744
718
  module.exports = {
745
719
  DB
746
- };
720
+ };