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