mm_sqlite 1.3.2 → 1.3.3

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