mm_mysql 2.2.4 → 2.2.6
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/README.md +3 -4
- package/README_EN.md +478 -0
- package/db.js +497 -496
- package/eslint.config.js +235 -0
- package/index.js +601 -601
- package/package.json +12 -5
- package/sql.js +1390 -1276
- package/test.js +481 -485
package/db.js
CHANGED
|
@@ -1,145 +1,145 @@
|
|
|
1
1
|
const {
|
|
2
|
-
|
|
2
|
+
Sql
|
|
3
3
|
} = require('./sql');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @class 数据库操作类
|
|
7
|
-
* @
|
|
7
|
+
* @augments Sql
|
|
8
8
|
*/
|
|
9
9
|
class DB extends Sql {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
10
|
+
/**
|
|
11
|
+
* @description 数据库管理器
|
|
12
|
+
* @param {object} mysql - MySQL实例
|
|
13
|
+
*/
|
|
14
|
+
constructor(mysql) {
|
|
15
|
+
// 正确绑定run和exec方法的上下文
|
|
16
|
+
super(
|
|
17
|
+
mysql.run,
|
|
18
|
+
mysql.exec
|
|
19
|
+
);
|
|
20
|
+
// 保存mysql实例引用
|
|
21
|
+
this._mysql = mysql;
|
|
22
|
+
// 事务中
|
|
23
|
+
this.task = 0;
|
|
24
|
+
|
|
25
|
+
// 事务SQL
|
|
26
|
+
this.task_sql = '';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 获取上级
|
|
30
|
+
* @returns {object} 返回上级管理器
|
|
31
|
+
*/
|
|
32
|
+
this.parent = function () {
|
|
33
|
+
return mysql;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* 创建超时Promise
|
|
40
40
|
* @private
|
|
41
|
-
* @param {
|
|
42
|
-
* @param {
|
|
41
|
+
* @param {number} timeout - 超时时间(毫秒)
|
|
42
|
+
* @param {string} message - 错误信息
|
|
43
43
|
* @returns {Promise<Error>}
|
|
44
44
|
*/
|
|
45
45
|
DB.prototype._createTimeoutPromise = function (timeout, message) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
return new Promise((_, reject) => {
|
|
47
|
+
setTimeout(() => {
|
|
48
|
+
reject(new Error(message));
|
|
49
|
+
}, timeout);
|
|
50
|
+
});
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
* 获取数据库名
|
|
55
|
-
* @
|
|
55
|
+
* @returns {string} 数据库
|
|
56
56
|
*/
|
|
57
57
|
DB.prototype.database = function () {
|
|
58
|
-
|
|
58
|
+
return this.parent().config.database;
|
|
59
59
|
};
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* 构建新管理器
|
|
63
|
-
* @param {
|
|
64
|
-
* @param {
|
|
65
|
-
* @
|
|
63
|
+
* @param {string} table 表名
|
|
64
|
+
* @param {string} key 键名
|
|
65
|
+
* @returns {object} 返回管理器
|
|
66
66
|
*/
|
|
67
67
|
DB.prototype.new = function (table, key) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
68
|
+
const db = this.parent().db();
|
|
69
|
+
db.table = table;
|
|
70
|
+
if (key) {
|
|
71
|
+
db.key = key;
|
|
72
|
+
} else {
|
|
73
|
+
const arr = table.split('_');
|
|
74
|
+
db.key = arr[arr.length - 1] + '_id';
|
|
75
|
+
}
|
|
76
|
+
return db;
|
|
77
77
|
};
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
80
|
* 获取数据库连接
|
|
81
|
-
* @returns {Promise<
|
|
81
|
+
* @returns {Promise<object>}
|
|
82
82
|
*/
|
|
83
83
|
DB.prototype.getConn = async function() {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
try {
|
|
85
|
+
return await this._mysql.getConn();
|
|
86
|
+
} catch (error) {
|
|
87
|
+
this.log('error', '获取连接失败', error);
|
|
88
|
+
// 返回null作为默认值,保持返回值类型一致
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
91
|
};
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
94
|
* 开始事务
|
|
95
|
-
* @returns {Promise<
|
|
95
|
+
* @returns {Promise<object>}
|
|
96
96
|
*/
|
|
97
97
|
DB.prototype.start = async function() {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
try {
|
|
99
|
+
return await this._mysql.beginTransaction();
|
|
100
|
+
} catch (error) {
|
|
101
|
+
this.log('error', '开始事务失败', error);
|
|
102
|
+
// 返回null作为默认值,保持返回值类型一致
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
105
|
};
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
108
|
* 提交事务
|
|
109
|
-
* @param {
|
|
109
|
+
* @param {object} transaction - 事务对象
|
|
110
110
|
* @returns {Promise<void>}
|
|
111
111
|
* @throws {TypeError} 当transaction参数无效时
|
|
112
112
|
*/
|
|
113
113
|
DB.prototype.commit = async function(transaction) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
// 参数校验
|
|
115
|
+
if (!transaction || typeof transaction !== 'object') {
|
|
116
|
+
throw new TypeError('transaction must be object');
|
|
117
|
+
}
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
try {
|
|
120
|
+
await transaction.commit();
|
|
121
|
+
} catch (error) {
|
|
122
|
+
this.log('error', '提交事务失败', error);
|
|
123
|
+
}
|
|
124
124
|
};
|
|
125
125
|
|
|
126
126
|
/**
|
|
127
127
|
* 回滚事务
|
|
128
|
-
* @param {
|
|
128
|
+
* @param {object} transaction - 事务对象
|
|
129
129
|
* @returns {Promise<void>}
|
|
130
130
|
* @throws {TypeError} 当transaction参数无效时
|
|
131
131
|
*/
|
|
132
132
|
DB.prototype.back = async function(transaction) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
133
|
+
// 参数校验
|
|
134
|
+
if (!transaction || typeof transaction !== 'object') {
|
|
135
|
+
throw new TypeError('transaction must be object');
|
|
136
|
+
}
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
try {
|
|
139
|
+
await transaction.rollback();
|
|
140
|
+
} catch (error) {
|
|
141
|
+
this.log('error', '回滚事务失败', error);
|
|
142
|
+
}
|
|
143
143
|
};
|
|
144
144
|
|
|
145
145
|
/**
|
|
@@ -149,522 +149,523 @@ DB.prototype.back = async function(transaction) {
|
|
|
149
149
|
* @throws {TypeError} 当callback参数无效时
|
|
150
150
|
*/
|
|
151
151
|
DB.prototype.transaction = async function(callback) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
// 参数校验
|
|
153
|
+
if (typeof callback !== 'function') {
|
|
154
|
+
throw new TypeError('callback must be function');
|
|
155
|
+
}
|
|
156
156
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
157
|
+
try {
|
|
158
|
+
return await this._mysql.transaction(callback);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
this.log('error', '事务执行失败', error);
|
|
161
|
+
// 返回null作为默认值,保持返回值类型一致
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
164
|
};
|
|
165
165
|
|
|
166
166
|
/**
|
|
167
167
|
* @description 获取所有表名
|
|
168
|
-
* @param {
|
|
169
|
-
* @param {
|
|
170
|
-
* @
|
|
168
|
+
* @param {string} table 表名关键词, 可以 *table*包含、*table后缀、table*前缀 查询
|
|
169
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
170
|
+
* @returns {Promise|Array} 表名数组
|
|
171
171
|
*/
|
|
172
172
|
DB.prototype.tables = async function (table, timeout = 15000) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
173
|
+
try {
|
|
174
|
+
const list = await this.run('show tables', [], timeout);
|
|
175
|
+
const key = 'Tables_in_' + this.database();
|
|
176
|
+
if (table) {
|
|
177
|
+
// 简单的过滤实现
|
|
178
|
+
const filtered = [];
|
|
179
|
+
for (const item of list) {
|
|
180
|
+
const value = item[key];
|
|
181
|
+
if (value && value.includes(table)) {
|
|
182
|
+
filtered.push(item);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return filtered.map(item => item[key]);
|
|
186
|
+
}
|
|
187
|
+
// 使用原生方法提取表名
|
|
188
|
+
return list.map(item => item[key]);
|
|
189
|
+
} catch (err) {
|
|
190
|
+
this.log('error', '获取表名失败', err);
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
193
|
};
|
|
194
194
|
|
|
195
195
|
/**
|
|
196
196
|
* @description 获取所有表字段
|
|
197
|
-
* @param {
|
|
198
|
-
* @param {
|
|
199
|
-
* @param {
|
|
200
|
-
* @
|
|
197
|
+
* @param {string} table 表名
|
|
198
|
+
* @param {string} field_name 字段名,可选
|
|
199
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
200
|
+
* @returns {Promise|Array} 字段信息列表
|
|
201
201
|
*/
|
|
202
202
|
DB.prototype.fields = async function (table, field_name, timeout = 15000) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
203
|
+
try {
|
|
204
|
+
const targetTable = table || this.table;
|
|
205
|
+
if (!targetTable) {
|
|
206
|
+
throw new TypeError('table must be specified');
|
|
207
|
+
}
|
|
208
|
+
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`';
|
|
209
|
+
let sql = 'select ' + field + " from information_schema.COLUMNS where `table_name` = '" + targetTable +
|
|
210
210
|
"' and `table_schema` = '" + this.database() + "'";
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
211
|
+
if (field_name) {
|
|
212
|
+
sql += " AND COLUMN_NAME='" + field_name + "'";
|
|
213
|
+
}
|
|
214
|
+
const list = await this.run(sql, [], timeout);
|
|
215
|
+
const len = list.length;
|
|
216
|
+
for (let i = 0; i < len; i++) {
|
|
217
|
+
list[i].pk = !!list[i].pk;
|
|
218
|
+
list[i].notnull = list[i].notnull === 'NO';
|
|
219
|
+
}
|
|
220
|
+
return list;
|
|
221
|
+
} catch (err) {
|
|
222
|
+
this.log('error', '获取字段信息失败', err);
|
|
223
|
+
return [];
|
|
224
|
+
}
|
|
225
225
|
};
|
|
226
226
|
|
|
227
227
|
/**
|
|
228
228
|
* @description 创建数据表
|
|
229
|
-
* @param {
|
|
230
|
-
* @param {
|
|
231
|
-
* @param {
|
|
232
|
-
* @param {
|
|
233
|
-
* @param {
|
|
234
|
-
* @param {
|
|
235
|
-
* @
|
|
229
|
+
* @param {string} table 表名
|
|
230
|
+
* @param {string} field 主键字段名
|
|
231
|
+
* @param {string} type 类型名,常用类型 mediumint, int, varchar
|
|
232
|
+
* @param {boolean} auto 是否自增字段, 默认为自增字段
|
|
233
|
+
* @param {string} commit 注释
|
|
234
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
235
|
+
* @returns {Promise | number} 创建成功返回1,失败返回0
|
|
236
236
|
*/
|
|
237
237
|
DB.prototype.addTable = async function (table, field, type = 'int', auto = true, commit = '', timeout = 15000) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
238
|
+
try {
|
|
239
|
+
if (!table || typeof table !== 'string') {
|
|
240
|
+
throw new TypeError('table must be a valid string');
|
|
241
|
+
}
|
|
242
|
+
if (!field) {
|
|
243
|
+
field = 'id';
|
|
244
|
+
}
|
|
245
|
+
// 构建完整的字段定义
|
|
246
|
+
let fieldDef = `\`${field}\` ${type}(11)`;
|
|
247
|
+
fieldDef += ' NOT NULL';
|
|
248
|
+
if (auto) {
|
|
249
|
+
fieldDef += ' AUTO_INCREMENT';
|
|
250
|
+
}
|
|
251
|
+
if (commit) {
|
|
252
|
+
fieldDef += " COMMENT '" + commit + "'";
|
|
253
|
+
}
|
|
254
|
+
fieldDef += ' PRIMARY KEY';
|
|
255
|
+
|
|
256
|
+
let sql = 'CREATE TABLE IF NOT EXISTS \`' + table + '\` (' + fieldDef + ')';
|
|
257
|
+
sql += ' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;';
|
|
258
|
+
|
|
259
|
+
// 执行SQL并设置表名
|
|
260
|
+
const result = await this.exec(sql, timeout);
|
|
261
|
+
// 设置实例的表名属性,以便后续操作使用
|
|
262
|
+
this.table = table;
|
|
263
|
+
return result;
|
|
264
|
+
} catch (err) {
|
|
265
|
+
this.log('error', '创建表失败', err);
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
268
268
|
};
|
|
269
269
|
|
|
270
270
|
/**
|
|
271
271
|
* @description 删除表
|
|
272
|
-
* @param {
|
|
273
|
-
* @param {
|
|
274
|
-
* @
|
|
272
|
+
* @param {string} table 表名
|
|
273
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
274
|
+
* @returns {Promise | number} 操作结果
|
|
275
275
|
*/
|
|
276
276
|
DB.prototype.dropTable = function (table, timeout = 15000) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
if (!table || typeof table !== 'string') {
|
|
278
|
+
throw new TypeError('table must be a valid string');
|
|
279
|
+
}
|
|
280
|
+
return this.exec('DROP TABLE IF EXISTS \`' + table + '\`;', timeout);
|
|
281
281
|
};
|
|
282
282
|
|
|
283
283
|
/**
|
|
284
284
|
* @description 修改表名
|
|
285
|
-
* @param {
|
|
286
|
-
* @param {
|
|
287
|
-
* @param {
|
|
288
|
-
* @
|
|
285
|
+
* @param {string} table 旧表名
|
|
286
|
+
* @param {string} new_table 新表名
|
|
287
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
288
|
+
* @returns {Promise | number} 操作结果
|
|
289
289
|
*/
|
|
290
290
|
DB.prototype.renameTable = function (table, new_table, timeout = 15000) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
291
|
+
if (!table || !new_table) {
|
|
292
|
+
throw new TypeError('table and new_table must be specified');
|
|
293
|
+
}
|
|
294
|
+
return this.exec('RENAME TABLE \`' + table + '\` TO \`' + new_table + '\`;', timeout);
|
|
295
295
|
};
|
|
296
296
|
|
|
297
297
|
/**
|
|
298
298
|
* @description 添加字段
|
|
299
|
-
* @param {
|
|
300
|
-
* @param {
|
|
301
|
-
* @param {
|
|
302
|
-
* @param {
|
|
303
|
-
* @param {
|
|
304
|
-
* @param {
|
|
305
|
-
* @param {
|
|
306
|
-
* @
|
|
299
|
+
* @param {string} field 字段名
|
|
300
|
+
* @param {string} type 类型名
|
|
301
|
+
* @param {string} value 默认值
|
|
302
|
+
* @param {boolean} not_null 是否非空
|
|
303
|
+
* @param {boolean} auto 是否自增
|
|
304
|
+
* @param {string} comment 注释
|
|
305
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
306
|
+
* @returns {Promise | number} 添加成功返回1,失败返回0
|
|
307
307
|
*/
|
|
308
308
|
DB.prototype.addField = async function (field, type, value = '', not_null = true, auto = false, comment = '', timeout = 15000) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
309
|
+
try {
|
|
310
|
+
// 确保表名已设置
|
|
311
|
+
if (!this.table || !field || !type) {
|
|
312
|
+
throw new TypeError('table, field, and type must be specified');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// 构建字段定义
|
|
316
|
+
let fieldDef = `\`${field}\` ${type}`;
|
|
317
|
+
|
|
318
|
+
// 添加非空约束
|
|
319
|
+
if (not_null) {
|
|
320
|
+
fieldDef += ' NOT NULL';
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// 添加自增属性
|
|
324
|
+
if (auto) {
|
|
325
|
+
fieldDef += ' AUTO_INCREMENT';
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// 添加默认值
|
|
329
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
330
|
+
if (typeof value === 'string') {
|
|
331
|
+
fieldDef += " DEFAULT '" + value + "'";
|
|
332
|
+
} else {
|
|
333
|
+
fieldDef += ' DEFAULT ' + value;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// 添加注释
|
|
338
|
+
if (comment) {
|
|
339
|
+
fieldDef += " COMMENT '" + comment + "'";
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// 使用ADD COLUMN而不是CHANGE COLUMN
|
|
343
|
+
const sql = `ALTER TABLE \`${this.table}\` ADD COLUMN ${fieldDef};`;
|
|
344
|
+
const result = await this.exec(sql, timeout);
|
|
345
|
+
return result;
|
|
346
|
+
} catch (err) {
|
|
347
|
+
this.log('error', '添加字段失败', err);
|
|
348
|
+
return 0;
|
|
349
|
+
}
|
|
350
350
|
};
|
|
351
351
|
|
|
352
352
|
/**
|
|
353
353
|
* @description 修改字段
|
|
354
|
-
* @param {
|
|
355
|
-
* @param {
|
|
356
|
-
* @param {
|
|
357
|
-
* @param {
|
|
358
|
-
* @param {
|
|
359
|
-
* @param {
|
|
360
|
-
* @param {
|
|
361
|
-
* @param {
|
|
362
|
-
* @
|
|
354
|
+
* @param {string} field 字段名
|
|
355
|
+
* @param {string} type 类型名
|
|
356
|
+
* @param {string} value 默认值
|
|
357
|
+
* @param {boolean} not_null 是否非空
|
|
358
|
+
* @param {boolean} auto 是否自增
|
|
359
|
+
* @param {boolean} isKey 是否主键
|
|
360
|
+
* @param {string} new_name 新字段名
|
|
361
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
362
|
+
* @returns {Promise | number} 修改成功返回1,失败返回0
|
|
363
363
|
*/
|
|
364
364
|
DB.prototype.setField = async function (field, type, value = '', not_null = true, auto = false, isKey = false, new_name = '', timeout = 15000) {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
365
|
+
try {
|
|
366
|
+
if (!field || !type || !this.table) {
|
|
367
|
+
throw new TypeError('field, type, and table must be specified');
|
|
368
|
+
}
|
|
369
|
+
const targetName = new_name || field;
|
|
370
|
+
let fieldDef = `\`${targetName}\` ${type}`;
|
|
371
|
+
if (not_null) {
|
|
372
|
+
fieldDef += ' NOT NULL';
|
|
373
|
+
}
|
|
374
|
+
if (auto) {
|
|
375
|
+
fieldDef += ' AUTO_INCREMENT';
|
|
376
|
+
}
|
|
377
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
378
|
+
if (typeof value === 'string') {
|
|
379
|
+
fieldDef += " DEFAULT '" + value + "'";
|
|
380
|
+
} else {
|
|
381
|
+
fieldDef += ' DEFAULT ' + value;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
let sql = `ALTER TABLE \`${this.table}\` CHANGE COLUMN \`${field}\` ${fieldDef}`;
|
|
385
|
+
if (isKey) {
|
|
386
|
+
sql += ', ADD PRIMARY KEY (\`' + targetName + '\`)';
|
|
387
|
+
}
|
|
388
|
+
sql += ';';
|
|
389
|
+
const result = await this.exec(sql, timeout);
|
|
390
|
+
return result;
|
|
391
|
+
} catch (err) {
|
|
392
|
+
this.log('error', '修改字段失败', err);
|
|
393
|
+
return 0;
|
|
394
|
+
}
|
|
395
395
|
};
|
|
396
396
|
|
|
397
397
|
/**
|
|
398
398
|
* @description 修改字段类型
|
|
399
|
-
* @param {
|
|
400
|
-
* @param {
|
|
401
|
-
* @param {
|
|
402
|
-
* @param {
|
|
403
|
-
* @
|
|
399
|
+
* @param {string} table 表名
|
|
400
|
+
* @param {string} field 字段名
|
|
401
|
+
* @param {string} type 字段类型
|
|
402
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
403
|
+
* @returns {Promise | number} 操作结果
|
|
404
404
|
*/
|
|
405
405
|
DB.prototype.editField = function (table, field, type, timeout = 15000) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
406
|
+
if (!table || !field || !type) {
|
|
407
|
+
throw new TypeError('table, field, and type must be specified');
|
|
408
|
+
}
|
|
409
|
+
return this.exec('ALTER TABLE \`' + table + '\` MODIFY COLUMN \`' + field + '\` ' + type + ';', timeout);
|
|
410
410
|
};
|
|
411
411
|
|
|
412
412
|
/**
|
|
413
413
|
* @description 删除字段
|
|
414
|
-
* @param {
|
|
415
|
-
* @param {
|
|
416
|
-
* @param {
|
|
417
|
-
* @
|
|
414
|
+
* @param {string} table 表名
|
|
415
|
+
* @param {string} field 字段名
|
|
416
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
417
|
+
* @returns {Promise | number} 操作结果
|
|
418
418
|
*/
|
|
419
419
|
DB.prototype.delField = function (table, field, timeout = 15000) {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
420
|
+
if (!table || !field) {
|
|
421
|
+
throw new TypeError('table and field must be specified');
|
|
422
|
+
}
|
|
423
|
+
return this.exec('ALTER TABLE \`' + table + '\` DROP COLUMN \`' + field + '\`;', timeout);
|
|
424
424
|
};
|
|
425
425
|
|
|
426
426
|
/**
|
|
427
427
|
* @description 修改字段名
|
|
428
|
-
* @param {
|
|
429
|
-
* @param {
|
|
430
|
-
* @param {
|
|
431
|
-
* @param {
|
|
432
|
-
* @param {
|
|
433
|
-
* @
|
|
428
|
+
* @param {string} table 表名
|
|
429
|
+
* @param {string} field 旧字段名
|
|
430
|
+
* @param {string} new_field 新字段名
|
|
431
|
+
* @param {string} type 字段类型
|
|
432
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
433
|
+
* @returns {Promise | number} 操作结果
|
|
434
434
|
*/
|
|
435
435
|
DB.prototype.renameField = function (table, field, new_field, type, timeout = 15000) {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
436
|
+
if (!table || !field || !new_field) {
|
|
437
|
+
throw new TypeError('table, field, and new_field must be specified');
|
|
438
|
+
}
|
|
439
|
+
return this.exec('ALTER TABLE \`' + table + '\` CHANGE \`' + field + '\` \`' + new_field + '\` ' + type + ';', timeout);
|
|
440
440
|
};
|
|
441
441
|
|
|
442
442
|
/**
|
|
443
443
|
* @description 获取表创建语句
|
|
444
|
-
* @param {
|
|
445
|
-
* @param {
|
|
446
|
-
* @
|
|
444
|
+
* @param {string} table 表名
|
|
445
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
446
|
+
* @returns {Promise | string} 创建语句
|
|
447
447
|
*/
|
|
448
448
|
DB.prototype.getCreateTable = async function (table, timeout = 15000) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
449
|
+
if (!table) {
|
|
450
|
+
throw new TypeError('table must be specified');
|
|
451
|
+
}
|
|
452
|
+
try {
|
|
453
|
+
const list = await this.run('SHOW CREATE TABLE \`' + table + '\`;', [], timeout);
|
|
454
|
+
if (list && list.length > 0) {
|
|
455
|
+
return list[0]['Create Table'] || list[0]['create table'];
|
|
456
|
+
}
|
|
457
|
+
return '';
|
|
458
|
+
} catch (err) {
|
|
459
|
+
this.log('error', '获取表结构失败', err);
|
|
460
|
+
return '';
|
|
461
|
+
}
|
|
462
462
|
};
|
|
463
463
|
|
|
464
464
|
/**
|
|
465
465
|
* @description 获取表数据的SQL插入语句
|
|
466
|
-
* @param {
|
|
467
|
-
* @param {
|
|
468
|
-
* @param {
|
|
469
|
-
* @
|
|
466
|
+
* @param {string} table 表名
|
|
467
|
+
* @param {number} batchSize 批量处理大小
|
|
468
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
469
|
+
* @returns {Promise | string} SQL插入语句
|
|
470
470
|
*/
|
|
471
471
|
DB.prototype.getTableData = async function (table, batchSize = 100, timeout = 60000) {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
472
|
+
if (!table) {
|
|
473
|
+
throw new TypeError('table must be specified');
|
|
474
|
+
}
|
|
475
|
+
try {
|
|
476
|
+
// 获取表数据
|
|
477
|
+
const rows = await this.run(`SELECT * FROM \`${table}\``, [], timeout);
|
|
478
|
+
let sql = '';
|
|
479
|
+
|
|
480
|
+
if (rows && rows.length > 0) {
|
|
481
|
+
sql += `-- 表数据: ${table}\n`;
|
|
482
|
+
|
|
483
|
+
// 批量处理大数据量
|
|
484
|
+
for (let j = 0; j < rows.length; j += batchSize) {
|
|
485
|
+
const batch = rows.slice(j, j + batchSize);
|
|
486
|
+
for (const row of batch) {
|
|
487
|
+
try {
|
|
488
|
+
const rowValues = Object.values(row)
|
|
489
|
+
.map(value => {
|
|
490
|
+
if (value === null) return 'NULL';
|
|
491
|
+
if (typeof value === 'boolean') return value ? 1 : 0;
|
|
492
|
+
if (typeof value === 'number') return value;
|
|
493
|
+
if (value instanceof Date) {
|
|
494
|
+
return "'" + value.toISOString().slice(0, 19).replace('T', ' ') + "'";
|
|
495
|
+
}
|
|
496
|
+
// 处理字符串,转义特殊字符
|
|
497
|
+
if (typeof value === 'string') {
|
|
498
|
+
return "'" + value
|
|
499
|
+
.replace(/[\\']/g, '\\$&')
|
|
500
|
+
.replace(/\n/g, '\\n')
|
|
501
|
+
.replace(/\r/g, '\\r')
|
|
502
|
+
.replace(/\t/g, '\\t')
|
|
503
|
+
.replace(/\u0000/g, '\\0') + "'";
|
|
504
|
+
}
|
|
505
|
+
return "'" + String(value || '') + "'";
|
|
506
|
+
}).join(',');
|
|
507
|
+
sql += `INSERT INTO \`${table}\` VALUES (${rowValues});\n`;
|
|
508
|
+
} catch (rowErr) {
|
|
509
|
+
this.log('error', '处理行数据失败', rowErr);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
sql += '\n';
|
|
514
|
+
}
|
|
515
|
+
return sql;
|
|
516
|
+
} catch (err) {
|
|
517
|
+
this.log('error', '获取表数据失败', err);
|
|
518
|
+
return '';
|
|
519
|
+
}
|
|
520
520
|
};
|
|
521
521
|
|
|
522
522
|
/**
|
|
523
523
|
* @description 清空表数据
|
|
524
|
-
* @param {
|
|
525
|
-
* @param {
|
|
526
|
-
* @
|
|
524
|
+
* @param {string} table 表名
|
|
525
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
526
|
+
* @returns {Promise | number} 操作结果
|
|
527
527
|
*/
|
|
528
528
|
DB.prototype.emptyTable = function (table, timeout = 15000) {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
529
|
+
if (!table || typeof table !== 'string') {
|
|
530
|
+
throw new TypeError('table must be a valid string');
|
|
531
|
+
}
|
|
532
|
+
return this.exec('TRUNCATE TABLE \`' + table + '\`;', timeout);
|
|
533
533
|
};
|
|
534
534
|
|
|
535
535
|
/**
|
|
536
536
|
* @description 检查表是否存在
|
|
537
|
-
* @param {
|
|
538
|
-
* @param {
|
|
539
|
-
* @
|
|
537
|
+
* @param {string} table 表名
|
|
538
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
539
|
+
* @returns {Promise | boolean} 是否存在
|
|
540
540
|
*/
|
|
541
541
|
DB.prototype.hasTable = async function (table, timeout = 15000) {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
542
|
+
if (!table || typeof table !== 'string') {
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
try {
|
|
546
|
+
const list = await this.run("SHOW TABLES LIKE '" + table + "'", [], timeout);
|
|
547
|
+
return list && list.length > 0;
|
|
548
|
+
} catch (err) {
|
|
549
|
+
this.log('error', '检查表是否存在失败', err);
|
|
550
|
+
return false;
|
|
551
|
+
}
|
|
552
552
|
};
|
|
553
553
|
|
|
554
554
|
/**
|
|
555
555
|
* @description 检查字段是否存在
|
|
556
|
-
* @param {
|
|
557
|
-
* @param {
|
|
558
|
-
* @param {
|
|
559
|
-
* @
|
|
556
|
+
* @param {string} table 表名
|
|
557
|
+
* @param {string} field 字段名
|
|
558
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
559
|
+
* @returns {Promise | boolean} 是否存在
|
|
560
560
|
*/
|
|
561
561
|
DB.prototype.hasField = async function (table, field, timeout = 15000) {
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
562
|
+
if (!table || !field) {
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
try {
|
|
566
|
+
const list = await this.fields(table, field, timeout);
|
|
567
|
+
return list && list.length > 0;
|
|
568
|
+
} catch (err) {
|
|
569
|
+
this.log('error', '检查字段是否存在失败', err);
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
572
|
};
|
|
573
573
|
|
|
574
574
|
/**
|
|
575
575
|
* @description 备份表
|
|
576
|
-
* @param {
|
|
577
|
-
* @param {
|
|
578
|
-
* @param {
|
|
579
|
-
* @
|
|
576
|
+
* @param {string} table 表名
|
|
577
|
+
* @param {string} backup 备份表名
|
|
578
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
579
|
+
* @returns {Promise | number} 操作结果
|
|
580
580
|
*/
|
|
581
581
|
DB.prototype.backupTable = function (table, backup, timeout = 60000) {
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
582
|
+
if (!table || !backup) {
|
|
583
|
+
throw new TypeError('table and backup must be specified');
|
|
584
|
+
}
|
|
585
|
+
return this.exec('CREATE TABLE \`' + backup + '\` LIKE \`' + table + '\`; INSERT INTO \`' + backup + '\` SELECT * FROM \`' + table + '\`;', timeout);
|
|
586
586
|
};
|
|
587
587
|
|
|
588
588
|
|
|
589
589
|
/**
|
|
590
590
|
* @description 创建表
|
|
591
|
-
* @param {
|
|
592
|
-
* @param {
|
|
593
|
-
* @param {
|
|
594
|
-
* @
|
|
591
|
+
* @param {string} table 表名
|
|
592
|
+
* @param {object} model 表模型,键值对,根据值类型创建字段类型,根据键名创建字段名
|
|
593
|
+
* @param {string} key 主键
|
|
594
|
+
* @param timeout
|
|
595
|
+
* @returns {Promise | number} 操作结果
|
|
595
596
|
*/
|
|
596
597
|
DB.prototype.createTable = function (table, model, key = 'id', timeout = 15000) {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
}
|
|
598
|
+
if (!table || !model) {
|
|
599
|
+
throw new TypeError('table, model must be specified');
|
|
600
|
+
}
|
|
601
|
+
var fields = '';
|
|
602
|
+
for (const field in model) {
|
|
603
|
+
const value = model[field];
|
|
604
|
+
let type = '';
|
|
605
|
+
if (field === key) {
|
|
606
|
+
type = 'INT AUTO_INCREMENT PRIMARY KEY';
|
|
607
|
+
} else if (typeof value === 'number') {
|
|
608
|
+
if (value % 1 === 0) {
|
|
609
|
+
// 判断是否时间戳(10位或13位数字)
|
|
610
|
+
const now = Date.now();
|
|
611
|
+
const isTimestamp = (value >= 1000000000 && value <= now * 1.1); // 10位或13位时间戳
|
|
612
|
+
if (isTimestamp) {
|
|
613
|
+
type = 'DATETIME';
|
|
614
|
+
} else {
|
|
615
|
+
type = 'INT';
|
|
616
|
+
}
|
|
617
|
+
} else {
|
|
618
|
+
type = 'FLOAT';
|
|
619
|
+
}
|
|
620
|
+
} else if (typeof value === 'string') {
|
|
621
|
+
type = 'TEXT';
|
|
622
|
+
if (value.length <= 255) {
|
|
623
|
+
// 正则判断是否日期时间型
|
|
624
|
+
if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(value)) {
|
|
625
|
+
type = 'DATETIME';
|
|
626
|
+
}
|
|
627
|
+
// 正则判断是否日期型
|
|
628
|
+
else if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
629
|
+
type = 'DATE';
|
|
630
|
+
}
|
|
631
|
+
// 正则判断是否时间型
|
|
632
|
+
else if (/^\d{2}:\d{2}:\d{2}$/.test(value)) {
|
|
633
|
+
type = 'TIME';
|
|
634
|
+
}
|
|
635
|
+
// 正则判断是否JSON字符串
|
|
636
|
+
else if (/^[{[]/.test(value) && /[}]]$/.test(value)) {
|
|
637
|
+
type = 'TEXT';
|
|
638
|
+
}
|
|
639
|
+
// 正则判断是否html
|
|
640
|
+
else if (/^<[a-z][\s\S]*>/i.test(value)) {
|
|
641
|
+
type = 'TEXT';
|
|
642
|
+
}
|
|
643
|
+
// 正则判断是否xml
|
|
644
|
+
else if (/^<\?xml[\s\S]*\?>/.test(value)) {
|
|
645
|
+
type = 'TEXT';
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
type = 'VARCHAR(255)';
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
else if (typeof value === 'boolean') {
|
|
653
|
+
type = 'tinyint(1)';
|
|
654
|
+
}
|
|
655
|
+
else if (value instanceof Date) {
|
|
656
|
+
// 判断是否Date对象,类型为datetime
|
|
657
|
+
type = 'DATETIME';
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
type = 'BLOB';
|
|
661
|
+
}
|
|
662
|
+
fields += `\`${field}\` ${type}, `;
|
|
663
|
+
}
|
|
664
|
+
fields = fields.slice(0, -2);
|
|
665
|
+
var sql = `CREATE TABLE IF NOT EXISTS \`${table}\` (${fields});`;
|
|
666
|
+
return this.exec(sql, [], timeout);
|
|
667
|
+
};
|
|
667
668
|
|
|
668
669
|
module.exports = {
|
|
669
|
-
|
|
670
|
+
DB
|
|
670
671
|
};
|