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/.gitattributes +2 -0
- package/LICENSE +21 -0
- package/README.md +199 -0
- package/README_EN.md +293 -0
- package/db.js +890 -161
- package/eslint.config.js +235 -0
- package/index.js +910 -169
- package/link_model.js +129 -0
- package/package.json +50 -36
- package/sql.js +1972 -0
- package/sql.json +56 -0
- package/test_backup.js +78 -0
- package/test_db/test_basic.db +0 -0
- package/test_db/test_core_methods.db +0 -0
- package/test_db/test_db.db +0 -0
- package/test_db/test_sql.db +0 -0
- package/test_sql_functions.js +86 -0
- package/test_sqlite.js +104 -0
- package/config.json +0 -0
- package/db/mm.db +0 -0
- package/test.js +0 -84
package/db.js
CHANGED
|
@@ -1,190 +1,919 @@
|
|
|
1
|
-
const Sql = require('
|
|
1
|
+
const { Sql } = require('./sql');
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @class 数据库操作类
|
|
5
|
-
* @
|
|
5
|
+
* @augments Sql
|
|
6
6
|
*/
|
|
7
7
|
class DB extends Sql {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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 {
|
|
25
|
-
* @
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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 {
|
|
38
|
-
* @
|
|
253
|
+
* @param {string} table 表名
|
|
254
|
+
* @param {number} timeout 超时时间(毫秒)
|
|
255
|
+
* @returns {Promise|Array} 字段信息列表
|
|
39
256
|
*/
|
|
40
|
-
DB.prototype.fields = async function(table) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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 {
|
|
56
|
-
* @param {
|
|
57
|
-
* @
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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 {
|
|
95
|
-
* @param {
|
|
96
|
-
* @param {
|
|
97
|
-
* @param {
|
|
98
|
-
* @
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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 {
|
|
118
|
-
* @param {
|
|
119
|
-
* @param {
|
|
120
|
-
* @
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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 {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
+
};
|