mm_sqlite 1.0.6 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/db.js CHANGED
@@ -7,26 +7,44 @@ const Sql = require('./sql');
7
7
  class DB extends Sql {
8
8
  /**
9
9
  * @description 数据库管理器
10
- * @param {String} database 数据库名称
10
+ * @param {Object} mysql MySQL实例
11
11
  */
12
- constructor(sqlite) {
13
- super(sqlite.run, sqlite.exec);
12
+ constructor(mysql) {
13
+ super(mysql.run, mysql.exec);
14
+ // 保存mysql实例引用
15
+ this._mysql = mysql;
14
16
  // 事务中
15
17
  this.task = 0;
16
18
 
17
19
  // 事务SQL
18
20
  this.task_sql = "";
19
-
21
+ // 表名
22
+ this.table = "";
23
+
20
24
  /**
21
25
  * 获取上级
22
26
  * @return {Object} 返回上级管理器
23
27
  */
24
28
  this.parent = function() {
25
- return sqlite;
29
+ return mysql;
26
30
  };
27
31
  }
28
32
  }
29
33
 
34
+ /**
35
+ * 创建超时Promise
36
+ * @param {Number} timeout - 超时时间(毫秒)
37
+ * @param {String} message - 超时提示信息
38
+ * @return {Promise} 超时Promise
39
+ */
40
+ DB.prototype._createTimeoutPromise = function(timeout, message) {
41
+ return new Promise((_, reject) => {
42
+ setTimeout(() => {
43
+ reject(new Error(message));
44
+ }, timeout);
45
+ });
46
+ };
47
+
30
48
  /**
31
49
  * 获取数据库名
32
50
  * @return {String} 数据库
@@ -53,68 +71,221 @@ DB.prototype.new = function(table, key) {
53
71
  return db;
54
72
  };
55
73
 
74
+ /**
75
+ * 执行SQL查询
76
+ * @param {string} sql SQL语句
77
+ * @param {Array} params 参数数组
78
+ * @param {Number} timeout 超时时间(毫秒)
79
+ * @returns {Promise<Object>} 查询结果
80
+ */
81
+ DB.prototype.run = async function(sql, params = [], timeout = 30000) {
82
+ if (!this._mysql) {
83
+ throw new Error('SQLite实例未初始化');
84
+ }
85
+ try {
86
+ // 使用Promise.race实现超时控制
87
+ return await Promise.race([
88
+ this._mysql.run(sql, params),
89
+ this._createTimeoutPromise(timeout, `查询执行超时: ${sql.substring(0, 100)}...`)
90
+ ]);
91
+ } catch (error) {
92
+ $.log.error(`[DB] [run] 查询执行失败`, {
93
+ error: error.message,
94
+ sql: sql.substring(0, 200),
95
+ params: JSON.stringify(params.slice(0, 5))
96
+ });
97
+ throw error;
98
+ }
99
+ };
100
+
101
+ /**
102
+ * 执行SQL命令
103
+ * @param {string} sql SQL语句
104
+ * @param {Number} timeout 超时时间(毫秒)
105
+ * @returns {Promise<Object>} 执行结果
106
+ */
107
+ DB.prototype.exec = async function(sql, timeout = 30000) {
108
+ if (!this._mysql) {
109
+ throw new Error('SQLite实例未初始化');
110
+ }
111
+ try {
112
+ // 使用Promise.race实现超时控制
113
+ return await Promise.race([
114
+ this._mysql.exec(sql),
115
+ this._createTimeoutPromise(timeout, `命令执行超时: ${sql.substring(0, 100)}...`)
116
+ ]);
117
+ } catch (error) {
118
+ $.log.error(`[DB] [exec] 命令执行失败`, {
119
+ error: error.message,
120
+ sql: sql.substring(0, 200)
121
+ });
122
+ throw error;
123
+ }
124
+ };
125
+
126
+ /**
127
+ * 获取数据库连接
128
+ * @param {Number} timeout 超时时间(毫秒)
129
+ * @returns {Promise<Object>} 数据库连接对象
130
+ */
131
+ DB.prototype.getConn = async function(timeout = 15000) {
132
+ if (!this._mysql) {
133
+ throw new Error('SQLite实例未初始化');
134
+ }
135
+ try {
136
+ // 使用Promise.race实现超时控制
137
+ return await Promise.race([
138
+ this._mysql.getConn(),
139
+ this._createTimeoutPromise(timeout, '获取数据库连接超时')
140
+ ]);
141
+ } catch (error) {
142
+ $.log.error(`[DB] [getConn] 获取连接失败`, {
143
+ error: error.message
144
+ });
145
+ throw error;
146
+ }
147
+ };
148
+
56
149
  /**
57
150
  * 事务开始
151
+ * @param {String} identifier 事务标识符
152
+ * @param {Number} timeout 超时时间(毫秒)
153
+ * @return {Promise<Object>} 执行结果
58
154
  */
59
- DB.prototype.start = async function(identifier = "point_1") {
155
+ DB.prototype.start = async function(identifier = "point_1", timeout = 15000) {
60
156
  this.task = 1;
61
- return await this.exec("SET AUTOCOMMIT=0;BEGIN;");
157
+ try {
158
+ return await this.exec("BEGIN;", timeout);
159
+ } catch (err) {
160
+ this.task = 0;
161
+ $.log.error(`[DB] [start] 事务开始失败: ${err.message}`);
162
+ throw err;
163
+ }
62
164
  };
63
165
 
64
166
  /**
65
167
  * 提交
168
+ * @param {Number} timeout 超时时间(毫秒)
169
+ * @return {Promise<Object>} 执行结果
66
170
  */
67
- DB.prototype.commit = async function() {
171
+ DB.prototype.commit = async function(timeout = 15000) {
172
+ if (this.task !== 1) {
173
+ $.log.warn("[DB] [commit] 没有活跃的事务可提交");
174
+ return 0;
175
+ }
68
176
  this.task = 2;
69
- return this.exec("COMMIT;");
177
+ try {
178
+ const result = await this.exec("COMMIT;", timeout);
179
+ // 重置事务状态
180
+ this.task = 0;
181
+ this.task_sql = '';
182
+ return result;
183
+ } catch (err) {
184
+ $.log.error(`[DB] [commit] 事务提交失败: ${err.message}`);
185
+ // 尝试回滚
186
+ try {
187
+ await this.exec("ROLLBACK;", timeout);
188
+ } catch (rollbackErr) {
189
+ $.log.error(`[DB] [commit] 事务回滚也失败: ${rollbackErr.message}`);
190
+ }
191
+ this.task = 0;
192
+ this.task_sql = '';
193
+ throw err;
194
+ }
70
195
  };
71
196
 
72
197
  /**
73
198
  * 事务结束
74
199
  */
75
- DB.prototype.end = async function() {
76
- this.task = 3;
200
+ DB.prototype.end = function() {
201
+ // 确保事务状态被重置
202
+ this.task = 0;
203
+ this.task_sql = '';
77
204
  };
78
205
 
79
206
  /**
80
207
  * 滚回
208
+ * @param {String} identifier 事务标识符
209
+ * @param {Number} timeout 超时时间(毫秒)
210
+ * @return {Promise<Object>} 执行结果
81
211
  */
82
- DB.prototype.back = async function(identifier = "point_1") {
212
+ DB.prototype.back = async function(identifier = "point_1", timeout = 15000) {
213
+ if (this.task !== 1) {
214
+ $.log.warn("[DB] [back] 没有活跃的事务可回滚");
215
+ this.task = 0;
216
+ this.task_sql = '';
217
+ return 0;
218
+ }
83
219
  this.task = 3;
84
- await this.exec("ROLLBACK;");
85
- return this.exec("SET AUTOCOMMIT=1;");
220
+ try {
221
+ await this.exec("ROLLBACK;", timeout);
222
+ const result = await this.exec("COMMIT;", timeout);
223
+ // 重置事务状态
224
+ this.task = 0;
225
+ this.task_sql = '';
226
+ return result;
227
+ } catch (err) {
228
+ $.log.error(`[DB] [back] 事务回滚失败: ${err.message}`);
229
+ this.task = 0;
230
+ this.task_sql = '';
231
+ throw err;
232
+ }
86
233
  };
87
234
 
88
235
  /**
89
236
  * @description 获取所有表名
90
237
  * @param {String} table 表名关键词, 可以 *table*包含、*table后缀、table*前缀 查询
238
+ * @param {Number} timeout 超时时间(毫秒)
91
239
  * @return {Promise|Array} 表名数组
92
240
  */
93
- DB.prototype.tables = async function(table) {
94
- var list = await this.run("SELECT `name` FROM sqlite_master WHERE type='table';");
95
- if (table) {
96
- list = list.search(table, 'name');
241
+ DB.prototype.tables = async function(table, timeout = 15000) {
242
+ try {
243
+ const list = await this.run("SELECT `name` FROM sqlite_master WHERE type='table';", [], timeout);
244
+ if (table) {
245
+ // 简单的过滤实现
246
+ const filtered = [];
247
+ for (const item of list) {
248
+ const value = item.name;
249
+ if (value && value.includes(table)) {
250
+ filtered.push(item);
251
+ }
252
+ }
253
+ return filtered.map(item => item.name);
254
+ }
255
+ // 使用原生方法提取表名
256
+ return list.map(item => item.name);
257
+ } catch (err) {
258
+ $.log.error(`[DB] [tables] 获取表名失败: ${err.message}`);
259
+ return [];
97
260
  }
98
- return list.getArr('name');
99
261
  };
100
262
 
101
263
  /**
102
264
  * @description 获取所有表字段
103
265
  * @param {String} table 表名
266
+ * @param {String} field_name 字段名,可选
267
+ * @param {Number} timeout 超时时间(毫秒)
104
268
  * @return {Promise|Array} 字段信息列表
105
269
  */
106
- DB.prototype.fields = async function(table, field_name) {
107
- if (!table) {
108
- table = this.table;
109
- }
110
- var sql = "PRAGMA table_info(`{0}`);".replace('{0}', table);
111
- var list = await this.run(sql);
112
- var len = list.length;
113
- for (var i = 0; i < len; i++) {
114
- list[i].pk = list[i].pk ? true : false;
115
- list[i].notnull = list[i].notnull ? true : false;
270
+ DB.prototype.fields = async function(table, field_name, timeout = 15000) {
271
+ try {
272
+ const targetTable = table || this.table;
273
+ if (!targetTable) {
274
+ $.log.error("[DB] [fields] 未指定表名");
275
+ return [];
276
+ }
277
+ var sql = "PRAGMA table_info(`{0}`);".replace('{0}', targetTable);
278
+ var list = await this.run(sql, [], timeout);
279
+ var len = list.length;
280
+ for (var i = 0; i < len; i++) {
281
+ list[i].pk = list[i].pk ? true : false;
282
+ list[i].notnull = list[i].notnull ? true : false;
283
+ }
284
+ return list;
285
+ } catch (err) {
286
+ $.log.error(`[DB] [fields] 获取字段信息失败: ${err.message}`);
287
+ return [];
116
288
  }
117
- return list;
118
289
  };
119
290
 
120
291
  /**
@@ -253,147 +424,195 @@ DB.prototype.setType = function(field, type, value, not_null, auto) {
253
424
  * @description 创建数据表
254
425
  * @param {String} table 表名
255
426
  * @param {String} field 主键字段名
256
- * @param {String} type 类型名,常用类型 mediumint, int, varchar
427
+ * @param {String} type 类型名,常用类型 mediumint, int, varchar
257
428
  * @param {Boolean} auto 是否自增字段, 默认为自增字段
258
429
  * @param {String} commit 注释
430
+ * @param {Number} timeout 超时时间(毫秒)
259
431
  * @return {Promise|Number} 创建成功返回1,失败返回0
260
432
  */
261
- DB.prototype.addTable = async function(table, field, type, auto, commit = '') {
262
- if (!field) {
263
- field = "id";
264
- }
265
- var sql = "CREATE TABLE IF NOT EXISTS `{0}` (`{1}` {2})".replace('{0}', table).replace(
266
- '{1}', field).replace('{2}', this.setType(field, type, null, true, auto) + ' PRIMARY KEY');
267
- if (commit) {
268
- sql += " COMMENT = '" + commit + "';"
269
- } else {
270
- sql += ";"
433
+ DB.prototype.addTable = async function(table, field, type = 'int', auto = true, commit = '', timeout = 15000) {
434
+ try {
435
+ if (!table || typeof table !== 'string') {
436
+ $.log.error("[DB] [addTable] 表名无效");
437
+ return 0;
438
+ }
439
+ if (!field) {
440
+ field = "id";
441
+ }
442
+ // SQLite的自增主键要求字段类型必须是INTEGER(大写且不带长度)
443
+ let fieldType;
444
+ if (auto) {
445
+ // 自增字段使用简单的INTEGER类型,由后面的PRIMARY KEY AUTOINCREMENT决定自增行为
446
+ fieldType = "INTEGER";
447
+ } else {
448
+ // 非自增字段使用正常的类型设置
449
+ fieldType = this.setType(field, type, null, true, auto);
450
+ }
451
+ // 根据是否自增来构造不同的SQL语句
452
+ let sqlTemplate;
453
+ if (auto) {
454
+ // 自增字段:使用INTEGER PRIMARY KEY AUTOINCREMENT语法
455
+ sqlTemplate = "CREATE TABLE IF NOT EXISTS `{0}` (`{1}` {2} PRIMARY KEY AUTOINCREMENT)";
456
+ } else {
457
+ // 非自增字段:使用正常的字段类型和主键定义
458
+ sqlTemplate = "CREATE TABLE IF NOT EXISTS `{0}` (`{1}` {2} PRIMARY KEY)";
459
+ }
460
+ var sql = sqlTemplate.replace('{0}', table).replace(
461
+ '{1}', field).replace('{2}', fieldType);
462
+ if (commit) {
463
+ sql += " COMMENT = '" + commit + "';"
464
+ } else {
465
+ sql += ";"
466
+ }
467
+ var bl = await this.exec(sql, timeout);
468
+ // 设置实例的表名属性,以便后续操作使用
469
+ this.table = table;
470
+ return bl && bl.affectedRows !== undefined ? 1 : 0;
471
+ } catch (err) {
472
+ $.log.error(`[DB] [addTable] 创建表失败: ${err.message}`);
473
+ return 0;
271
474
  }
272
- var bl = await this.exec(sql);
273
- return bl;
274
475
  };
275
476
 
276
477
  /**
277
478
  * @description 清空数据表
278
479
  * @param {Boolean} reset 是否重置自增ID
480
+ * @param {String} table 表名
481
+ * @param {Number} timeout - 超时时间(毫秒)
279
482
  * @return {Promise|Number} 清空成功返回1,失败返回0
280
483
  */
281
- DB.prototype.clearTable = async function(reset = true, table = '') {
484
+ DB.prototype.clearTable = async function(reset = true, table = '', timeout = 15000) {
282
485
  var sql = reset ? "TRUNCATE table `{0}`;" : "DELETE FROM `{0}`";
283
- var bl = await this.exec(sql.replace('{0}', table || this.table));
486
+ var bl = await this.exec(sql.replace('{0}', table || this.table), [], timeout);
284
487
  return bl;
285
488
  };
286
489
 
287
490
  /**
288
491
  * @description 添加字段
289
492
  * @param {String} field 字段名
290
- * @param {String} type 类型名,常用类型 mediumint, int, float, double, varchar, tinyint, text, date, datetime, time, timestamp
291
- * @param {String|Number} value 默认值
292
- * @param {Boolean} not_null 是否非空字段 true为非空,false为可空
293
- * @param {Boolean} auto 是否自动(如果为数字类型则自增增段,如果为时间类型则默认事件)
294
- * @param {Boolean} isKey 是否主键
493
+ * @param {String} type 类型名
494
+ * @param {String} value 默认值
495
+ * @param {Boolean} not_null 是否非空
496
+ * @param {Boolean} auto 是否自增
497
+ * @param {String} comment 注释
498
+ * @param {Number} timeout 超时时间(毫秒)
295
499
  * @return {Promise|Number} 添加成功返回1,失败返回0
296
500
  */
297
- DB.prototype.field_add = async function(field, type, value, not_null, auto, isKey) {
298
- var sql =
299
- "select count(*) as `count` from sqlite_master where `type` = 'table' and `name` = '{1}' and `sql` like '%{2}%'";
300
- sql = sql.replace('{1}', this.table).replace('{2}', field);
301
- var arr = await this.run(sql);
302
- if (arr && arr.length > 0) {
303
- if (arr[0].count == 0) {
304
- var type = this.setType(field, type, value, not_null, auto);
305
- var sql = "ALTER Table `{0}` ADD `{1}` {2}";
306
- sql = sql.replace('{0}', this.table).replace('{1}', field).replace('{2}', type);
307
- if (isKey) {
308
- sql += " , ADD PRIMARY KEY (`" + field + "`)";
309
- } else {
501
+ DB.prototype.addField = async function(field, type, value = '', not_null = true, auto = false, comment = '', timeout = 15000) {
502
+ try {
503
+ // 确保表名已设置
504
+ if (!this.table || !field || !type) {
505
+ $.log.error("[DB] [addField] 表名、字段名或类型未指定");
506
+ return 0;
507
+ }
508
+
509
+ var sql =
510
+ "select count(*) as `count` from sqlite_master where `type` = 'table' and `name` = '{1}' and `sql` like '%{2}%'";
511
+ sql = sql.replace('{1}', this.table).replace('{2}', field);
512
+ var arr = await this.run(sql, [], timeout);
513
+ if (arr && arr.length > 0) {
514
+ if (arr[0].count == 0) {
515
+ var type = this.setType(field, type, value, not_null, auto);
516
+ var sql = "ALTER Table `{0}` ADD `{1}` {2}";
517
+ sql = sql.replace('{0}', this.table).replace('{1}', field).replace('{2}', type);
518
+ if (comment) {
519
+ sql += " COMMENT '" + comment + "'";
520
+ }
310
521
  sql += ";";
522
+ return await this.exec(sql, timeout);
311
523
  }
312
- return await this.exec(sql);
313
524
  }
525
+ return 0;
526
+ } catch (err) {
527
+ $.log.error(`[DB] [addField] 添加字段失败: ${err.message}`);
528
+ return 0;
314
529
  }
315
- return 0;
316
530
  };
317
531
 
318
532
  /**
319
533
  * @description 修改字段
320
534
  * @param {String} field 字段名
321
- * @param {String} type 类型名,常用类型 mediumint, int, float, double, varchar, tinyint, text, date, datetime, time, timestamp
322
- * @param {String|Number} value 默认值
323
- * @param {Boolean} not_null 是否非空字段 true为非空,false为可空
324
- * @param {Boolean} auto 是否自动(如果为数字类型则自增增段,如果为时间类型则默认事件)
535
+ * @param {String} type 类型名
536
+ * @param {String} value 默认值
537
+ * @param {Boolean} not_null 是否非空
538
+ * @param {Boolean} auto 是否自增
325
539
  * @param {Boolean} isKey 是否主键
326
- * @param {String} new_name 新名称
540
+ * @param {String} new_name 新字段名
541
+ * @param {Number} timeout 超时时间(毫秒)
327
542
  * @return {Promise|Number} 修改成功返回1,失败返回0
328
543
  */
329
- DB.prototype.field_set = async function(field, type, value, not_null, auto, isKey, new_name) {
330
- var sql =
331
- "select count(*) as `count` from sqlite_master where `type` = 'table' and `name` = '{1}' and `sql` like '%{2}%'";
332
- sql = sql.replace('{1}', this.table).replace('{2}', field);
333
- console.log("来", sql);
334
- var arr = await this.run(sql);
335
- if (arr && arr.length > 0) {
336
- if (arr[0].count == 0) {
544
+ DB.prototype.setField = async function(field, type, value = '', not_null = true, auto = false, isKey = false, new_name = '', timeout = 15000) {
545
+ try {
546
+ if (!field || !type || !this.table) {
547
+ $.log.error("[DB] [setField] 参数不完整或表名未设置");
337
548
  return 0;
338
549
  }
339
- }
550
+
551
+ var sql =
552
+ "select count(*) as `count` from sqlite_master where `type` = 'table' and `name` = '{1}' and `sql` like '%{2}%'";
553
+ sql = sql.replace('{1}', this.table).replace('{2}', field);
554
+ var arr = await this.run(sql, [], timeout);
555
+ if (arr && arr.length > 0) {
556
+ if (arr[0].count == 0) {
557
+ return 0;
558
+ }
559
+ }
340
560
 
341
- var type = this.setType(field, type, value, not_null, auto);
342
- if (type.has('text')) {
343
- type = type.replace('NOT NULL', '').replace("DEFAULT ''", "");
344
- }
561
+ var type = this.setType(field, type, value, not_null, auto);
562
+ if (type.has('text')) {
563
+ type = type.replace('NOT NULL', '').replace("DEFAULT ''", "");
564
+ }
345
565
 
346
- if (!new_name) {
347
- new_name = field;
348
- }
566
+ if (!new_name) {
567
+ new_name = field;
568
+ }
349
569
 
350
- sql = "ALTER TABLE `{0}` ADD COLUMN `{2}` {3}";
351
- sql = sql.replace('{0}', this.table).replace('{2}', new_name).replace('{3}', type);
352
- if (isKey) {
353
- sql += ", DROP PRIMARY KEY, ADD PRIMARY KEY (" + new_name + ") USING BTREE;"
354
- } else {
355
- sql += ";";
356
- }
357
- await this.exec(sql);
358
-
359
- sql = "UPDATE TABLE `{0}` SET `{2}` = `{1}`;";
360
- sql = sql.replace('{0}', this.table).replace('{1}', field).replace('{2}', new_name);
361
- var ret = await this.exec(sql);
362
-
363
- if (field !== new_name) {
364
- // sql = "ALTER TABLE `{0}` DROP COLUMN `{1}`;";
365
- // sql = sql.replace('{0}', this.table).replace('{1}', field);
366
- // ret = await this.exec(sql);
367
- // console.log("来了3", this.sql);
368
- }
369
- return ret;
370
- // sql = "ALTER TABLE `{0}` CHANGE COLUMN `{1}` `{2}` {3}";
371
- // sql = sql.replace('{0}', this.table).replace('{1}', field).replace('{2}', new_name).replace('{3}', type);
372
- // if (isKey) {
373
- // sql += ", DROP PRIMARY KEY, ADD PRIMARY KEY (" + new_name + ") USING BTREE;"
374
- // } else {
375
- // sql += ";";
376
- // }
377
- // return await this.exec(sql);
570
+ sql = "ALTER TABLE `{0}` ADD COLUMN `{2}` {3}";
571
+ sql = sql.replace('{0}', this.table).replace('{2}', new_name).replace('{3}', type);
572
+ if (isKey) {
573
+ sql += ", DROP PRIMARY KEY, ADD PRIMARY KEY (" + new_name + ") USING BTREE;"
574
+ } else {
575
+ sql += ";";
576
+ }
577
+ await this.exec(sql, timeout);
578
+
579
+ sql = "UPDATE TABLE `{0}` SET `{2}` = `{1}`;";
580
+ sql = sql.replace('{0}', this.table).replace('{1}', field).replace('{2}', new_name);
581
+ var ret = await this.exec(sql, timeout);
582
+
583
+ if (field !== new_name) {
584
+ // sql = "ALTER TABLE `{0}` DROP COLUMN `{1}`;";
585
+ // sql = sql.replace('{0}', this.table).replace('{1}', field);
586
+ // ret = await this.exec(sql);
587
+ }
588
+ return ret;
589
+ } catch (err) {
590
+ $.log.error(`[DB] [setField] 修改字段失败: ${err.message}`);
591
+ return 0;
592
+ }
378
593
  };
379
594
 
380
595
  /**
381
596
  * @description 删除字段
597
+ * @param {String} table 表名
382
598
  * @param {String} field 字段名
599
+ * @param {Number} timeout 超时时间(毫秒)
383
600
  * @return {Promise|Number} 删除成功返回1,失败返回0
384
601
  */
385
- DB.prototype.field_del = async function(field) {
386
- var sql =
387
- "select count(*) as `count` from sqlite_master where `type` = 'table' and `name` = '{1}' and `sql` like '%{2}%'";
388
- sql = sql.replace('{1}', this.table).replace('{2}', field);
389
- var arr = await this.run(sql);
390
- if (arr && arr.length > 0) {
391
- if (arr[0].count > 0) {
392
- sql = "ALTER Table `{0}` rename to `{1}`;".replace('{0}', this.table).replace('{1}', field);
393
- return await this.exec(sql);
602
+ DB.prototype.delField = async function(table, field, timeout = 15000) {
603
+ try {
604
+ if (!table || !field) {
605
+ $.log.error("[DB] [delField] 参数不完整");
606
+ return 0;
394
607
  }
608
+
609
+ var sql = "ALTER TABLE `{0}` DROP COLUMN `{1}`;";
610
+ sql = sql.replace('{0}', table).replace('{1}', field);
611
+ return await this.exec(sql, timeout);
612
+ } catch (err) {
613
+ $.log.error(`[DB] [delField] 删除字段失败: ${err.message}`);
614
+ return 0;
395
615
  }
396
- return 0;
397
616
  };
398
617
 
399
618
  /**
@@ -401,7 +620,7 @@ DB.prototype.field_del = async function(field) {
401
620
  * @param {Object} fd 字段信息
402
621
  * @return {String} sql语段
403
622
  */
404
- DB.prototype.field_sql = function(fd) {
623
+ DB.prototype.sqlField = function(fd) {
405
624
  var sql = "`{0}`".replace('{0}', fd.name);
406
625
  sql += " " + fd.type;
407
626
  if (fd.notnull) {
@@ -431,24 +650,249 @@ DB.prototype.field_sql = function(fd) {
431
650
  };
432
651
 
433
652
  /**
434
- * @description 修改字段名称
653
+ * @description 重命名字段
654
+ * @param {String} table 表名
655
+ * @param {String} field 字段名
656
+ * @param {String} new_field 新字段名
657
+ * @param {String} type 字段类型
658
+ * @param {Number} timeout 超时时间(毫秒)
659
+ * @return {Promise|Number} 重命名成功返回1,失败返回0
660
+ */
661
+ DB.prototype.renameField = async function(table, field, new_field, type, timeout = 15000) {
662
+ try {
663
+ if (!table || !field || !new_field) {
664
+ $.log.error("[DB] [renameField] 参数不完整");
665
+ return 0;
666
+ }
667
+
668
+ var sql = "ALTER TABLE `{0}` RENAME COLUMN `{1}` TO `{2}`;";
669
+ sql = sql.replace('{0}', table).replace('{1}', field).replace('{2}', new_field);
670
+ return await this.exec(sql, timeout);
671
+ } catch (err) {
672
+ $.log.error(`[DB] [renameField] 重命名字段失败: ${err.message}`);
673
+ return 0;
674
+ }
675
+ };
676
+
677
+ /**
678
+ * @description 修改字段类型
679
+ * @param {String} table 表名
435
680
  * @param {String} field 字段名
436
- * @param {String} name 变更后名称
437
- * @return {Promise|Number} 修改成功返回1,失败返回0
681
+ * @param {String} type 字段类型
682
+ * @param {Number} timeout 超时时间(毫秒)
683
+ * @return {Promise|Number} 操作结果
684
+ */
685
+ DB.prototype.editField = function(table, field, type, timeout = 15000) {
686
+ try {
687
+ if (!table || !field || !type) {
688
+ $.log.error("[DB] [editField] 参数不完整");
689
+ return 0;
690
+ }
691
+ return this.exec("ALTER TABLE `" + table + "` MODIFY COLUMN `" + field + "` " + type + ";", timeout);
692
+ } catch (err) {
693
+ $.log.error(`[DB] [editField] 修改字段类型失败: ${err.message}`);
694
+ return 0;
695
+ }
696
+ };
697
+
698
+ /**
699
+ * @description 删除数据表
700
+ * @param {String} table 表名
701
+ * @param {Number} timeout - 超时时间(毫秒)
702
+ * @return {Promise|Number} 删除成功返回1,失败返回0
703
+ */
704
+ DB.prototype.dropTable = function(table, timeout = 15000) {
705
+ if (!table) {
706
+ $.log.error("[DB] [dropTable] 表名无效");
707
+ return 0;
708
+ }
709
+ return this.exec("DROP TABLE IF EXISTS `" + table + "`;", [], timeout);
710
+ };
711
+
712
+ /**
713
+ * @description 重命名数据表
714
+ * @param {String} table 原表名
715
+ * @param {String} new_table 新表名
716
+ * @param {Number} timeout - 超时时间(毫秒)
717
+ * @return {Promise|Number} 重命名成功返回1,失败返回0
718
+ */
719
+ DB.prototype.renameTable = function(table, new_table, timeout = 15000) {
720
+ if (!table || !new_table) {
721
+ $.log.error("[DB] [renameTable] 表名参数不完整");
722
+ return 0;
723
+ }
724
+ return this.exec("ALTER TABLE `" + table + "` RENAME TO `" + new_table + "`;", [], timeout);
725
+ };
726
+
727
+ /**
728
+ * @description 清空数据表(保留表结构)
729
+ * @param {String} table 表名
730
+ * @param {Number} timeout - 超时时间(毫秒)
731
+ * @return {Promise|Number} 清空成功返回1,失败返回0
732
+ */
733
+ DB.prototype.emptyTable = function(table, timeout = 15000) {
734
+ if (!table) {
735
+ $.log.error("[DB] [emptyTable] 表名无效");
736
+ return 0;
737
+ }
738
+ return this.exec("DELETE FROM `" + table + "`;", [], timeout);
739
+ };
740
+
741
+ /**
742
+ * @description 检查表是否存在
743
+ * @param {String} table 表名
744
+ * @param {Number} timeout - 超时时间(毫秒)
745
+ * @return {Promise|Boolean} 存在返回true,不存在返回false
746
+ */
747
+ DB.prototype.hasTable = async function(table, timeout = 15000) {
748
+ try {
749
+ if (!table) {
750
+ return false;
751
+ }
752
+ var list = await this.run("SELECT name FROM sqlite_master WHERE type='table' AND name=?;", [table], timeout);
753
+ return list && list.length > 0;
754
+ } catch (err) {
755
+ $.log.error(`[DB] [hasTable] 检查表是否存在失败: ${err.message}`);
756
+ return false;
757
+ }
758
+ };
759
+
760
+ /**
761
+ * @description 检查字段是否存在
762
+ * @param {String} table 表名
763
+ * @param {String} field 字段名
764
+ * @param {Number} timeout - 超时时间(毫秒)
765
+ * @return {Promise|Boolean} 存在返回true,不存在返回false
766
+ */
767
+ DB.prototype.hasField = async function(table, field, timeout = 15000) {
768
+ try {
769
+ if (!table || !field) {
770
+ return false;
771
+ }
772
+ var fields = await this.fields(table, field, timeout);
773
+ return fields && fields.some(f => f.name === field);
774
+ } catch (err) {
775
+ $.log.error(`[DB] [hasField] 检查字段是否存在失败: ${err.message}`);
776
+ return false;
777
+ }
778
+ };
779
+
780
+ /**
781
+ * @description 备份数据表
782
+ * @param {String} table 原表名
783
+ * @param {String} backup 备份表名
784
+ * @param {Number} timeout - 超时时间(毫秒)
785
+ * @return {Promise|Number} 备份成功返回1,失败返回0
786
+ */
787
+ DB.prototype.backupTable = function(table, backup, timeout = 60000) {
788
+ if (!table || !backup) {
789
+ $.log.error("[DB] [backupTable] 参数不完整");
790
+ return 0;
791
+ }
792
+ var sql = "CREATE TABLE IF NOT EXISTS `" + backup + "` AS SELECT * FROM `" + table + "`;";
793
+ return this.exec(sql, [], timeout);
794
+ };
795
+
796
+ /**
797
+ * @description 获取表创建语句
798
+ * @param {String} table 表名
799
+ * @param {Number} timeout 超时时间(毫秒)
800
+ * @return {Promise|String} 创建语句
438
801
  */
439
- DB.prototype.field_name = async function(field, name) {
440
- var fields = await this.fields(this.table, field);
441
- if (!fields) {
442
- return -1;
802
+ DB.prototype.getCreateTable = async function(table, timeout = 15000) {
803
+ try {
804
+ if (!table) {
805
+ $.log.error("[DB] [getCreateTable] 表名不能为空");
806
+ return '';
807
+ }
808
+ const list = await this.run("SELECT sql FROM sqlite_master WHERE type='table' AND name=?;", [table], timeout);
809
+ if (list && list.length > 0) {
810
+ return list[0].sql || '';
811
+ }
812
+ return '';
813
+ } catch (err) {
814
+ $.log.error(`[DB] [getCreateTable] 获取表结构失败: ${err.message}`);
815
+ return '';
443
816
  }
444
- if (fields.length === 0) {
817
+ };
818
+
819
+ /**
820
+ * @description 获取表数据的SQL插入语句
821
+ * @param {String} table 表名
822
+ * @param {Number} batchSize 批量处理大小
823
+ * @param {Number} timeout 超时时间(毫秒)
824
+ * @return {Promise|String} SQL插入语句
825
+ */
826
+ DB.prototype.getTableData = async function(table, batchSize = 100, timeout = 60000) {
827
+ try {
828
+ if (!table) {
829
+ $.log.error("[DB] [getTableData] 表名不能为空");
830
+ return '';
831
+ }
832
+ // 获取表数据
833
+ const rows = await this.run(`SELECT * FROM \`${table}\``, [], timeout);
834
+ let sql = '';
835
+
836
+ if (rows && rows.length > 0) {
837
+ sql += `-- 表数据: ${table}\n`;
838
+
839
+ // 批量处理大数据量
840
+ for (let j = 0; j < rows.length; j += batchSize) {
841
+ const batch = rows.slice(j, j + batchSize);
842
+ for (const row of batch) {
843
+ try {
844
+ const rowValues = Object.values(row)
845
+ .map(value => {
846
+ if (value === null) return 'NULL';
847
+ if (typeof value === 'boolean') return value ? 1 : 0;
848
+ if (typeof value === 'number') return value;
849
+ if (value instanceof Date) {
850
+ return "'" + value.toISOString().slice(0, 19).replace('T', ' ') + "'";
851
+ }
852
+ // 处理字符串,转义特殊字符
853
+ if (typeof value === 'string') {
854
+ return "'" + value
855
+ .replace(/[\\']/g, '\\$&')
856
+ .replace(/\n/g, '\\n')
857
+ .replace(/\r/g, '\\r')
858
+ .replace(/\t/g, '\\t')
859
+ .replace(/\u0000/g, '\\0') + "'";
860
+ }
861
+ return "'" + String(value || '') + "'";
862
+ }).join(',');
863
+ sql += `INSERT INTO \`${table}\` VALUES (${rowValues});\n`;
864
+ } catch (rowErr) {
865
+ $.log.error(`[DB] [getTableData] 处理行数据失败: ${rowErr.message}`);
866
+ }
867
+ }
868
+ }
869
+ sql += '\n';
870
+ }
871
+ return sql;
872
+ } catch (err) {
873
+ $.log.error(`[DB] [getTableData] 获取表数据失败: ${err.message}`);
874
+ return '';
875
+ }
876
+ };
877
+
878
+ /**
879
+ * @description 备份表
880
+ * @param {String} table 表名
881
+ * @param {String} backup 备份表名
882
+ * @param {Number} timeout 超时时间(毫秒)
883
+ * @return {Promise|Number} 操作结果
884
+ */
885
+ DB.prototype.backup = function(table, backup, timeout = 60000) {
886
+ try {
887
+ if (!table || !backup) {
888
+ $.log.error("[DB] [backup] 参数不完整");
889
+ return 0;
890
+ }
891
+ return this.backupTable(table, backup, timeout);
892
+ } catch (err) {
893
+ $.log.error(`[DB] [backup] 备份表失败: ${err.message}`);
445
894
  return 0;
446
895
  }
447
- var sql_sub = this.field_sql(fields[0]);
448
- sql_sub = sql_sub.replace("`" + field + "`", "`" + name + "`").replace(' PRIMARY KEY', '');
449
- var sql = "alter table `{0}` change `{1}` " + sql_sub;
450
- sql = sql.replace('{0}', this.table).replace('{1}', field);
451
- return await this.exec(sql);
452
896
  };
453
897
 
454
- exports.DB = DB;
898
+ module.exports = DB;