@koishijs/plugin-database-mysql 4.0.0-rc.0 → 4.1.0
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/lib/index.d.ts +12 -10
- package/lib/index.js +75 -66
- package/lib/index.js.map +2 -2
- package/package.json +8 -8
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="koishi/lib" />
|
|
2
|
-
import { Pool, PoolConfig
|
|
2
|
+
import type { Pool, PoolConfig } from 'mysql';
|
|
3
3
|
import { Context, Database, Schema, Query, Model, Tables } from 'koishi';
|
|
4
4
|
import { Builder } from '@koishijs/sql-utils';
|
|
5
5
|
declare module 'mysql' {
|
|
@@ -19,9 +19,10 @@ export declare type TableType = keyof Tables;
|
|
|
19
19
|
declare class MySQLBuilder extends Builder {
|
|
20
20
|
private model;
|
|
21
21
|
constructor(model: Model);
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
format(sql: string, values: any[], stringifyObjects?: boolean, timeZone?: string): string;
|
|
23
|
+
escapeId(value: string, forbidQualified?: boolean): string;
|
|
24
24
|
escape(value: any, table?: string, field?: string): string;
|
|
25
|
+
stringify(value: any, table?: string, field?: string): any;
|
|
25
26
|
}
|
|
26
27
|
declare class MysqlDatabase extends Database {
|
|
27
28
|
ctx: Context;
|
|
@@ -29,21 +30,22 @@ declare class MysqlDatabase extends Database {
|
|
|
29
30
|
config: MysqlDatabase.Config;
|
|
30
31
|
mysql: this;
|
|
31
32
|
sql: MySQLBuilder;
|
|
32
|
-
private
|
|
33
|
+
private _tableTasks;
|
|
34
|
+
private _queryTasks;
|
|
33
35
|
constructor(ctx: Context, config?: MysqlDatabase.Config);
|
|
34
36
|
start(): Promise<void>;
|
|
35
|
-
|
|
37
|
+
stop(): void;
|
|
36
38
|
private _getColDefs;
|
|
37
39
|
/** synchronize table schema */
|
|
38
40
|
private _syncTable;
|
|
41
|
+
_inferFields<T extends TableType>(table: T, keys: readonly string[]): (keyof Tables[T])[];
|
|
39
42
|
_createFilter(name: TableType, query: Query): string;
|
|
40
43
|
_joinKeys: (keys: readonly string[]) => string;
|
|
41
44
|
_formatValues: (table: string, data: object, keys: readonly string[]) => any[];
|
|
42
|
-
query<T = any>(
|
|
43
|
-
|
|
45
|
+
query<T = any>(sql: string, values?: any): Promise<T>;
|
|
46
|
+
queue<T = any>(sql: string, values?: any): Promise<T>;
|
|
47
|
+
private _flushTasks;
|
|
44
48
|
select<T extends {}>(table: string, fields: readonly (string & keyof T)[], conditional?: string, values?: readonly any[]): Promise<T[]>;
|
|
45
|
-
count<K extends TableType>(table: K, conditional?: string): Promise<number>;
|
|
46
|
-
stop(): void;
|
|
47
49
|
drop(): Promise<void>;
|
|
48
50
|
stats(): Promise<Query.Stats>;
|
|
49
51
|
get(name: TableType, query: Query, modifier?: Query.Modifier): Promise<any>;
|
|
@@ -51,7 +53,7 @@ declare class MysqlDatabase extends Database {
|
|
|
51
53
|
remove(name: TableType, query: Query): Promise<void>;
|
|
52
54
|
create(name: TableType, data: {}): Promise<any>;
|
|
53
55
|
upsert(name: TableType, data: any[], keys: string | string[]): Promise<void>;
|
|
54
|
-
|
|
56
|
+
eval(name: TableType, expr: any, query: Query): Promise<any>;
|
|
55
57
|
}
|
|
56
58
|
declare namespace MysqlDatabase {
|
|
57
59
|
export interface Config extends PoolConfig {
|
package/lib/index.js
CHANGED
|
@@ -44,7 +44,7 @@ var __toModule = (module2) => {
|
|
|
44
44
|
__export(exports, {
|
|
45
45
|
default: () => src_default
|
|
46
46
|
});
|
|
47
|
-
var import_mysql = __toModule(require("mysql"));
|
|
47
|
+
var import_mysql = __toModule(require("@vlasky/mysql"));
|
|
48
48
|
var import_koishi = __toModule(require("koishi"));
|
|
49
49
|
var import_orm_utils = __toModule(require("@koishijs/orm-utils"));
|
|
50
50
|
var import_sql_utils = __toModule(require("@koishijs/sql-utils"));
|
|
@@ -97,7 +97,15 @@ var MySQLBuilder = class extends import_sql_utils.Builder {
|
|
|
97
97
|
constructor(model) {
|
|
98
98
|
super();
|
|
99
99
|
this.model = model;
|
|
100
|
-
|
|
100
|
+
}
|
|
101
|
+
format(sql, values, stringifyObjects, timeZone) {
|
|
102
|
+
return (0, import_mysql.format)(sql, values, stringifyObjects, timeZone);
|
|
103
|
+
}
|
|
104
|
+
escapeId(value, forbidQualified) {
|
|
105
|
+
return (0, import_mysql.escapeId)(value, forbidQualified);
|
|
106
|
+
}
|
|
107
|
+
escape(value, table, field) {
|
|
108
|
+
return (0, import_mysql.escape)(this.stringify(value, table, field));
|
|
101
109
|
}
|
|
102
110
|
stringify(value, table, field) {
|
|
103
111
|
var _a, _b;
|
|
@@ -114,9 +122,6 @@ var MySQLBuilder = class extends import_sql_utils.Builder {
|
|
|
114
122
|
}
|
|
115
123
|
return value;
|
|
116
124
|
}
|
|
117
|
-
escape(value, table, field) {
|
|
118
|
-
return (0, import_mysql.escape)(this.stringify(value, table, field));
|
|
119
|
-
}
|
|
120
125
|
};
|
|
121
126
|
__name(MySQLBuilder, "MySQLBuilder");
|
|
122
127
|
var MysqlDatabase = class extends import_koishi.Database {
|
|
@@ -124,7 +129,8 @@ var MysqlDatabase = class extends import_koishi.Database {
|
|
|
124
129
|
super(ctx);
|
|
125
130
|
this.ctx = ctx;
|
|
126
131
|
this.mysql = this;
|
|
127
|
-
this.
|
|
132
|
+
this._tableTasks = {};
|
|
133
|
+
this._queryTasks = [];
|
|
128
134
|
this._joinKeys = (keys) => {
|
|
129
135
|
return keys ? keys.map((key) => key.includes("`") ? key : `\`${key}\``).join(",") : "*";
|
|
130
136
|
};
|
|
@@ -166,20 +172,14 @@ var MysqlDatabase = class extends import_koishi.Database {
|
|
|
166
172
|
async start() {
|
|
167
173
|
this.pool = (0, import_mysql.createPool)(this.config);
|
|
168
174
|
for (const name in this.ctx.model.config) {
|
|
169
|
-
this.
|
|
175
|
+
this._tableTasks[name] = this._syncTable(name);
|
|
170
176
|
}
|
|
171
177
|
this.ctx.on("model", (name) => {
|
|
172
|
-
this.
|
|
178
|
+
this._tableTasks[name] = this._syncTable(name);
|
|
173
179
|
});
|
|
174
180
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return;
|
|
178
|
-
const types = MysqlDatabase.tables[table] || {};
|
|
179
|
-
return keys.map((key) => {
|
|
180
|
-
const type = types[key];
|
|
181
|
-
return typeof type === "function" ? `${type()} AS ${key}` : key;
|
|
182
|
-
});
|
|
181
|
+
stop() {
|
|
182
|
+
this.pool.end();
|
|
183
183
|
}
|
|
184
184
|
_getColDefs(name, columns) {
|
|
185
185
|
const table = this.ctx.model.config[name];
|
|
@@ -187,13 +187,6 @@ var MysqlDatabase = class extends import_koishi.Database {
|
|
|
187
187
|
const fields = __spreadValues({}, table.fields);
|
|
188
188
|
const unique = [...table.unique];
|
|
189
189
|
const result = [];
|
|
190
|
-
if (name === "user") {
|
|
191
|
-
const platforms = new Set(this.ctx.bots.map((bot) => bot.platform));
|
|
192
|
-
for (const name2 of platforms) {
|
|
193
|
-
fields[name2] = { type: "string", length: 63 };
|
|
194
|
-
unique.push(name2);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
190
|
for (const key in fields) {
|
|
198
191
|
if (columns.includes(key))
|
|
199
192
|
continue;
|
|
@@ -228,61 +221,77 @@ var MysqlDatabase = class extends import_koishi.Database {
|
|
|
228
221
|
return result;
|
|
229
222
|
}
|
|
230
223
|
async _syncTable(name) {
|
|
231
|
-
await this.
|
|
232
|
-
const data = await this.
|
|
224
|
+
await this._tableTasks[name];
|
|
225
|
+
const data = await this.queue("SELECT COLUMN_NAME from information_schema.columns WHERE TABLE_SCHEMA = ? && TABLE_NAME = ?", [this.config.database, name]);
|
|
233
226
|
const columns = data.map((row) => row.COLUMN_NAME);
|
|
234
227
|
const result = this._getColDefs(name, columns);
|
|
235
228
|
if (!columns.length) {
|
|
236
229
|
logger.info("auto creating table %c", name);
|
|
237
|
-
await this.
|
|
230
|
+
await this.queue(`CREATE TABLE ?? (${result.join(",")}) COLLATE = ?`, [name, this.config.charset]);
|
|
238
231
|
} else if (result.length) {
|
|
239
232
|
logger.info("auto updating table %c", name);
|
|
240
|
-
await this.
|
|
233
|
+
await this.queue(`ALTER TABLE ?? ${result.map((def) => "ADD " + def).join(",")}`, [name]);
|
|
241
234
|
}
|
|
242
235
|
}
|
|
236
|
+
_inferFields(table, keys) {
|
|
237
|
+
if (!keys)
|
|
238
|
+
return;
|
|
239
|
+
const types = MysqlDatabase.tables[table] || {};
|
|
240
|
+
return keys.map((key) => {
|
|
241
|
+
const type = types[key];
|
|
242
|
+
return typeof type === "function" ? `${type()} AS ${key}` : key;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
243
245
|
_createFilter(name, query) {
|
|
244
246
|
return this.sql.parseQuery(this.ctx.model.resolveQuery(name, query));
|
|
245
247
|
}
|
|
246
|
-
|
|
247
|
-
if (Array.isArray(source)) {
|
|
248
|
-
if (this.config.multipleStatements) {
|
|
249
|
-
return this.query(source.join(";"), values);
|
|
250
|
-
} else {
|
|
251
|
-
const result = [];
|
|
252
|
-
for (const sql of source) {
|
|
253
|
-
result.push(await this.query(sql, values));
|
|
254
|
-
}
|
|
255
|
-
return result;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
248
|
+
query(sql, values) {
|
|
258
249
|
const error = new Error();
|
|
259
250
|
return new Promise((resolve, reject) => {
|
|
260
|
-
|
|
251
|
+
sql = (0, import_mysql.format)(sql, values);
|
|
261
252
|
logger.debug("[sql]", sql);
|
|
262
253
|
this.pool.query(sql, (err, results) => {
|
|
263
254
|
if (!err)
|
|
264
255
|
return resolve(results);
|
|
265
256
|
logger.warn(sql);
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
reject(new import_koishi.KoishiError(err.message, "database.duplicate-entry"));
|
|
269
|
-
} else {
|
|
270
|
-
reject(err);
|
|
257
|
+
if (err["code"] === "ER_DUP_ENTRY") {
|
|
258
|
+
err = new import_koishi.KoishiError(err.message, "database.duplicate-entry");
|
|
271
259
|
}
|
|
260
|
+
err.stack = err.message + error.stack.slice(5);
|
|
261
|
+
reject(err);
|
|
272
262
|
});
|
|
273
263
|
});
|
|
274
264
|
}
|
|
265
|
+
queue(sql, values) {
|
|
266
|
+
if (!this.config.multipleStatements) {
|
|
267
|
+
return this.query(sql, values);
|
|
268
|
+
}
|
|
269
|
+
sql = (0, import_mysql.format)(sql, values);
|
|
270
|
+
return new Promise((resolve, reject) => {
|
|
271
|
+
this._queryTasks.push({ sql, resolve, reject });
|
|
272
|
+
process.nextTick(() => this._flushTasks());
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
async _flushTasks() {
|
|
276
|
+
const tasks = this._queryTasks;
|
|
277
|
+
if (!tasks.length)
|
|
278
|
+
return;
|
|
279
|
+
this._queryTasks = [];
|
|
280
|
+
try {
|
|
281
|
+
let results = await this.query(tasks.map((task) => task.sql).join("; "));
|
|
282
|
+
if (tasks.length === 1)
|
|
283
|
+
results = [results];
|
|
284
|
+
tasks.forEach((task, index) => {
|
|
285
|
+
task.resolve(results[index]);
|
|
286
|
+
});
|
|
287
|
+
} catch (error) {
|
|
288
|
+
tasks.forEach((task) => task.reject(error));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
275
291
|
select(table, fields, conditional, values = []) {
|
|
276
292
|
logger.debug(`[select] ${table}: ${fields ? fields.join(", ") : "*"}`);
|
|
277
293
|
const sql = "SELECT " + this._joinKeys(fields) + (table.includes(".") ? `FROM ${table}` : " FROM `" + table + `\` _${table}`) + (conditional ? " WHERE " + conditional : "");
|
|
278
|
-
return this.
|
|
279
|
-
}
|
|
280
|
-
async count(table, conditional) {
|
|
281
|
-
const [{ "COUNT(*)": count }] = await this.query(`SELECT COUNT(*) FROM ?? ${conditional ? "WHERE " + conditional : ""}`, [table]);
|
|
282
|
-
return count;
|
|
283
|
-
}
|
|
284
|
-
stop() {
|
|
285
|
-
this.pool.end();
|
|
294
|
+
return this.queue(sql, values);
|
|
286
295
|
}
|
|
287
296
|
async drop() {
|
|
288
297
|
const data = await this.select("information_schema.tables", ["TABLE_NAME"], "TABLE_SCHEMA = ?", [this.config.database]);
|
|
@@ -300,22 +309,23 @@ var MysqlDatabase = class extends import_koishi.Database {
|
|
|
300
309
|
return stats;
|
|
301
310
|
}
|
|
302
311
|
async get(name, query, modifier) {
|
|
312
|
+
await this._tableTasks[name];
|
|
303
313
|
const filter = this._createFilter(name, query);
|
|
304
314
|
if (filter === "0")
|
|
305
315
|
return [];
|
|
306
316
|
const { fields, limit, offset, sort } = import_koishi.Query.resolveModifier(modifier);
|
|
307
317
|
const keys = this._joinKeys(this._inferFields(name, fields));
|
|
308
318
|
let sql = `SELECT ${keys} FROM ${name} _${name} WHERE ${filter}`;
|
|
319
|
+
if (sort)
|
|
320
|
+
sql += " ORDER BY " + Object.entries(sort).map(([key, order]) => `${this.sql.escapeId(key)} ${order}`).join(", ");
|
|
309
321
|
if (limit)
|
|
310
322
|
sql += " LIMIT " + limit;
|
|
311
323
|
if (offset)
|
|
312
324
|
sql += " OFFSET " + offset;
|
|
313
|
-
|
|
314
|
-
sql += " ORDER BY " + Object.entries(sort).map(([key, order]) => `${this.sql.escapeId(key)} ${order}`).join(", ");
|
|
315
|
-
return this.query(sql);
|
|
325
|
+
return this.queue(sql);
|
|
316
326
|
}
|
|
317
327
|
async set(name, query, data) {
|
|
318
|
-
await this.
|
|
328
|
+
await this._tableTasks[name];
|
|
319
329
|
const filter = this._createFilter(name, query);
|
|
320
330
|
if (filter === "0")
|
|
321
331
|
return;
|
|
@@ -331,13 +341,14 @@ var MysqlDatabase = class extends import_koishi.Database {
|
|
|
331
341
|
await this.query(`UPDATE ${name} SET ${update} WHERE ${filter}`);
|
|
332
342
|
}
|
|
333
343
|
async remove(name, query) {
|
|
344
|
+
await this._tableTasks[name];
|
|
334
345
|
const filter = this._createFilter(name, query);
|
|
335
346
|
if (filter === "0")
|
|
336
347
|
return;
|
|
337
348
|
await this.query("DELETE FROM ?? WHERE " + filter, [name]);
|
|
338
349
|
}
|
|
339
350
|
async create(name, data) {
|
|
340
|
-
await this.
|
|
351
|
+
await this._tableTasks[name];
|
|
341
352
|
data = __spreadValues(__spreadValues({}, this.ctx.model.create(name)), data);
|
|
342
353
|
const keys = Object.keys(data);
|
|
343
354
|
const header = await this.query(`INSERT INTO ?? (${this._joinKeys(keys)}) VALUES (${keys.map(() => "?").join(", ")})`, [name, ...this._formatValues(name, data, keys)]);
|
|
@@ -346,7 +357,7 @@ var MysqlDatabase = class extends import_koishi.Database {
|
|
|
346
357
|
async upsert(name, data, keys) {
|
|
347
358
|
if (!data.length)
|
|
348
359
|
return;
|
|
349
|
-
await this.
|
|
360
|
+
await this._tableTasks[name];
|
|
350
361
|
const { fields, primary } = this.ctx.model.config[name];
|
|
351
362
|
const merged = {};
|
|
352
363
|
const insertion = data.map((item) => {
|
|
@@ -402,14 +413,12 @@ var MysqlDatabase = class extends import_koishi.Database {
|
|
|
402
413
|
await this.query(`INSERT INTO ${this.sql.escapeId(name)} (${this._joinKeys(initFields)}) VALUES ${data.map(() => placeholder).join(", ")}
|
|
403
414
|
ON DUPLICATE KEY UPDATE ${update}`, [].concat(...insertion.map((item) => this._formatValues(name, item, initFields))));
|
|
404
415
|
}
|
|
405
|
-
async
|
|
406
|
-
|
|
407
|
-
if (!keys.length)
|
|
408
|
-
return {};
|
|
416
|
+
async eval(name, expr, query) {
|
|
417
|
+
await this._tableTasks[name];
|
|
409
418
|
const filter = this._createFilter(name, query);
|
|
410
|
-
const
|
|
411
|
-
const [data] = await this.
|
|
412
|
-
return data;
|
|
419
|
+
const output = this.sql.parseEval(expr);
|
|
420
|
+
const [data] = await this.queue(`SELECT ${output} AS value FROM ${name} WHERE ${filter}`);
|
|
421
|
+
return data.value;
|
|
413
422
|
}
|
|
414
423
|
};
|
|
415
424
|
__name(MysqlDatabase, "MysqlDatabase");
|
package/lib/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["import { createPool, Pool, PoolConfig, escape as mysqlEscape, escapeId, format } from 'mysql'\nimport { Context, Database, difference, Logger, makeArray, Schema, Query, Model, Tables, Dict, Time, KoishiError, pick } from 'koishi'\nimport { executeUpdate } from '@koishijs/orm-utils'\nimport { Builder } from '@koishijs/sql-utils'\nimport { OkPacket } from 'mysql'\n\ndeclare module 'mysql' {\n interface UntypedFieldInfo {\n packet: UntypedFieldInfo\n }\n}\n\ndeclare module 'koishi' {\n interface Database {\n mysql: MysqlDatabase\n }\n\n interface Modules {\n 'database-mysql': typeof import('.')\n }\n}\n\nconst logger = new Logger('mysql')\n\nexport type TableType = keyof Tables\n\nfunction getIntegerType(length = 11) {\n if (length <= 4) return 'tinyint'\n if (length <= 6) return 'smallint'\n if (length <= 9) return 'mediumint'\n if (length <= 11) return 'int'\n return 'bigint'\n}\n\nfunction getTypeDefinition({ type, length, precision, scale }: Model.Field) {\n switch (type) {\n case 'float':\n case 'double':\n case 'date':\n case 'time': return type\n case 'timestamp': return 'datetime'\n case 'integer': return getIntegerType(length)\n case 'unsigned': return `${getIntegerType(length)} unsigned`\n case 'decimal': return `decimal(${precision}, ${scale}) unsigned`\n case 'char': return `char(${length || 255})`\n case 'string': return `varchar(${length || 255})`\n case 'text': return `text(${length || 65535})`\n case 'list': return `text(${length || 65535})`\n case 'json': return `text(${length || 65535})`\n }\n}\n\nfunction createIndex(keys: string | string[]) {\n return makeArray(keys).map(key => escapeId(key)).join(', ')\n}\n\nclass MySQLBuilder extends Builder {\n constructor(private model: Model) {\n super()\n }\n\n escapeId = escapeId\n\n stringify(value: any, table?: string, field?: string) {\n const type = MysqlDatabase.tables[table]?.[field]\n if (typeof type === 'object') return type.stringify(value)\n\n const meta = this.model.config[table]?.fields[field]\n if (meta?.type === 'json') {\n return JSON.stringify(value)\n } else if (meta?.type === 'list') {\n return value.join(',')\n } else if (Model.Field.date.includes(meta?.type)) {\n return Time.template('yyyy-MM-dd hh:mm:ss', value)\n }\n\n return value\n }\n\n escape(value: any, table?: string, field?: string) {\n return mysqlEscape(this.stringify(value, table, field))\n }\n}\n\nclass MysqlDatabase extends Database {\n public pool: Pool\n public config: MysqlDatabase.Config\n\n mysql = this\n sql: MySQLBuilder\n\n private tasks: Dict<Promise<any>> = {}\n\n constructor(public ctx: Context, config?: MysqlDatabase.Config) {\n super(ctx)\n\n this.config = {\n host: 'localhost',\n port: 3306,\n user: 'root',\n database: 'koishi',\n charset: 'utf8mb4_general_ci',\n multipleStatements: true,\n typeCast: (field, next) => {\n const { orgName, orgTable } = field.packet\n const type = MysqlDatabase.tables[orgTable]?.[orgName]\n if (typeof type === 'object') return type.parse(field)\n\n const meta = this.ctx.model.config[orgTable]?.fields[orgName]\n if (meta?.type === 'string') {\n return field.string()\n } else if (meta?.type === 'json') {\n const source = field.string()\n return source ? JSON.parse(source) : meta.initial\n } else if (meta?.type === 'list') {\n const source = field.string()\n return source ? source.split(',') : []\n }\n\n if (field.type === 'BIT') {\n return Boolean(field.buffer()?.readUInt8(0))\n } else {\n return next()\n }\n },\n ...config,\n }\n\n this.sql = new MySQLBuilder(this.ctx.model)\n }\n\n async start() {\n this.pool = createPool(this.config)\n\n for (const name in this.ctx.model.config) {\n this.tasks[name] = this._syncTable(name)\n }\n\n this.ctx.on('model', (name) => {\n this.tasks[name] = this._syncTable(name)\n })\n }\n\n _inferFields<T extends TableType>(table: T, keys: readonly string[]) {\n if (!keys) return\n const types = MysqlDatabase.tables[table] || {}\n return keys.map((key) => {\n const type = types[key]\n return typeof type === 'function' ? `${type()} AS ${key}` : key\n }) as (keyof Tables[T])[]\n }\n\n private _getColDefs(name: string, columns: string[]) {\n const table = this.ctx.model.config[name]\n const { primary, foreign, autoInc } = table\n const fields = { ...table.fields }\n const unique = [...table.unique]\n const result: string[] = []\n\n // create platform rows\n if (name === 'user') {\n const platforms = new Set<string>(this.ctx.bots.map(bot => bot.platform))\n for (const name of platforms) {\n fields[name] = { type: 'string', length: 63 }\n unique.push(name)\n }\n }\n\n // orm definitions\n for (const key in fields) {\n if (columns.includes(key)) continue\n const { initial, nullable = true } = fields[key]\n let def = escapeId(key)\n if (key === primary && autoInc) {\n def += ' int unsigned not null auto_increment'\n } else {\n const typedef = getTypeDefinition(fields[key])\n def += ' ' + typedef\n if (makeArray(primary).includes(key)) {\n def += ' not null'\n } else {\n def += (nullable ? ' ' : ' not ') + 'null'\n }\n // blob, text, geometry or json columns cannot have default values\n if (initial && !typedef.startsWith('text')) {\n def += ' default ' + this.sql.escape(initial, name, key)\n }\n }\n result.push(def)\n }\n\n if (!columns.length) {\n result.push(`primary key (${createIndex(primary)})`)\n for (const key of unique) {\n result.push(`unique index (${createIndex(key)})`)\n }\n for (const key in foreign) {\n const [table, key2] = foreign[key]\n result.push(`foreign key (${escapeId(key)}) references ${escapeId(table)} (${escapeId(key2)})`)\n }\n }\n\n return result\n }\n\n /** synchronize table schema */\n private async _syncTable(name: string) {\n await this.tasks[name]\n const data = await this.query<any[]>('SELECT COLUMN_NAME from information_schema.columns WHERE TABLE_SCHEMA = ? && TABLE_NAME = ?', [this.config.database, name])\n const columns = data.map(row => row.COLUMN_NAME)\n const result = this._getColDefs(name, columns)\n if (!columns.length) {\n logger.info('auto creating table %c', name)\n await this.query(`CREATE TABLE ?? (${result.join(',')}) COLLATE = ?`, [name, this.config.charset])\n } else if (result.length) {\n logger.info('auto updating table %c', name)\n await this.query(`ALTER TABLE ?? ${result.map(def => 'ADD ' + def).join(',')}`, [name])\n }\n }\n\n _createFilter(name: TableType, query: Query) {\n return this.sql.parseQuery(this.ctx.model.resolveQuery(name, query))\n }\n\n _joinKeys = (keys: readonly string[]) => {\n return keys ? keys.map(key => key.includes('`') ? key : `\\`${key}\\``).join(',') : '*'\n }\n\n _formatValues = (table: string, data: object, keys: readonly string[]) => {\n return keys.map((key) => this.sql.stringify(data[key], table as never, key))\n }\n\n query<T = any>(source: string, values?: any): Promise<T>\n query<T = any>(source: string[], values?: any): Promise<T>\n async query<T extends {}>(source: string | string[], values?: any): Promise<T> {\n if (Array.isArray(source)) {\n if (this.config.multipleStatements) {\n return this.query(source.join(';'), values)\n } else {\n const result: any = []\n for (const sql of source) {\n result.push(await this.query(sql, values))\n }\n return result\n }\n }\n\n const error = new Error()\n return new Promise((resolve, reject) => {\n const sql = format(source, values)\n logger.debug('[sql]', sql)\n this.pool.query(sql, (err, results) => {\n if (!err) return resolve(results)\n logger.warn(sql)\n err.stack = err.message + error.stack.slice(7)\n if (err.code === 'ER_DUP_ENTRY') {\n reject(new KoishiError(err.message, 'database.duplicate-entry'))\n } else {\n reject(err)\n }\n })\n })\n }\n\n select<T extends {}>(table: string, fields: readonly (string & keyof T)[], conditional?: string, values?: readonly any[]): Promise<T[]>\n select(table: string, fields: string[], conditional?: string, values: readonly any[] = []) {\n logger.debug(`[select] ${table}: ${fields ? fields.join(', ') : '*'}`)\n const sql = 'SELECT '\n + this._joinKeys(fields)\n + (table.includes('.') ? `FROM ${table}` : ' FROM `' + table + `\\` _${table}`)\n + (conditional ? ' WHERE ' + conditional : '')\n return this.query(sql, values)\n }\n\n async count<K extends TableType>(table: K, conditional?: string) {\n const [{ 'COUNT(*)': count }] = await this.query(`SELECT COUNT(*) FROM ?? ${conditional ? 'WHERE ' + conditional : ''}`, [table])\n return count as number\n }\n\n stop() {\n this.pool.end()\n }\n\n async drop() {\n const data = await this.select('information_schema.tables', ['TABLE_NAME'], 'TABLE_SCHEMA = ?', [this.config.database])\n if (!data.length) return\n await this.query(data.map(({ TABLE_NAME }) => `DROP TABLE ${this.sql.escapeId(TABLE_NAME)}`).join('; '))\n }\n\n async stats() {\n const data = await this.select('information_schema.tables', ['TABLE_NAME', 'TABLE_ROWS', 'DATA_LENGTH'], 'TABLE_SCHEMA = ?', [this.config.database])\n const stats: Query.Stats = { size: 0 }\n stats.tables = Object.fromEntries(data.map(({ TABLE_NAME: name, TABLE_ROWS: count, DATA_LENGTH: size }) => {\n stats.size += size\n return [name, { count, size }]\n }))\n return stats\n }\n\n async get(name: TableType, query: Query, modifier?: Query.Modifier) {\n const filter = this._createFilter(name, query)\n if (filter === '0') return []\n const { fields, limit, offset, sort } = Query.resolveModifier(modifier)\n const keys = this._joinKeys(this._inferFields(name, fields))\n let sql = `SELECT ${keys} FROM ${name} _${name} WHERE ${filter}`\n if (limit) sql += ' LIMIT ' + limit\n if (offset) sql += ' OFFSET ' + offset\n if (sort) sql += ' ORDER BY ' + Object.entries(sort).map(([key, order]) => `${this.sql.escapeId(key)} ${order}`).join(', ')\n return this.query(sql)\n }\n\n async set(name: TableType, query: Query, data: {}) {\n await this.tasks[name]\n const filter = this._createFilter(name, query)\n if (filter === '0') return\n const keys = Object.keys(data)\n const update = keys.map((key) => {\n const valueExpr = this.sql.parseEval(data[key], name, key)\n const [field, ...rest] = key.split('.')\n const keyExpr = this.sql.escapeId(field)\n if (!rest.length) return `${keyExpr} = ${valueExpr}`\n return `${keyExpr} = json_set(ifnull(${keyExpr}, '{}'), '$${rest.map(key => `.\"${key}\"`).join('')}', ${valueExpr})`\n }).join(', ')\n await this.query(`UPDATE ${name} SET ${update} WHERE ${filter}`)\n }\n\n async remove(name: TableType, query: Query) {\n const filter = this._createFilter(name, query)\n if (filter === '0') return\n await this.query('DELETE FROM ?? WHERE ' + filter, [name])\n }\n\n async create(name: TableType, data: {}) {\n await this.tasks[name]\n data = { ...this.ctx.model.create(name), ...data }\n const keys = Object.keys(data)\n const header = await this.query<OkPacket>(\n `INSERT INTO ?? (${this._joinKeys(keys)}) VALUES (${keys.map(() => '?').join(', ')})`,\n [name, ...this._formatValues(name, data, keys)],\n )\n return { ...data, id: header.insertId } as any\n }\n\n async upsert(name: TableType, data: any[], keys: string | string[]) {\n if (!data.length) return\n await this.tasks[name]\n\n const { fields, primary } = this.ctx.model.config[name]\n const merged = {}\n const insertion = data.map((item) => {\n Object.assign(merged, item)\n return executeUpdate(this.ctx.model.create(name), item)\n })\n const indexFields = makeArray(keys || primary)\n const dataFields = [...new Set(Object.keys(merged).map(key => key.split('.', 1)[0]))]\n const updateFields = difference(dataFields, indexFields)\n\n const createFilter = (item: any) => this.sql.parseQuery(pick(item, indexFields))\n const createMultiFilter = (items: any[]) => {\n if (items.length === 1) {\n return createFilter(items[0])\n } else if (indexFields.length === 1) {\n const key = indexFields[0]\n return this.sql.parseQuery({ [key]: items.map(item => item[key]) })\n } else {\n return items.map(createFilter).join(' OR ')\n }\n }\n\n const update = updateFields.map((field) => {\n const escaped = this.sql.escapeId(field)\n const branches: Dict<string> = {}\n const absent = data.filter((item) => {\n // update directly\n if (field in item) {\n if (Object.keys(item[field]).some(key => key.startsWith('$'))) {\n branches[createFilter(item)] = this.sql.parseEval(item[field], name, field)\n }\n return\n }\n\n // update with json_set\n const valueInit = `ifnull(${escaped}, '{}')`\n let value = valueInit\n for (const key in item) {\n const [first, ...rest] = key.split('.')\n if (first !== field) continue\n value = `json_set(${value}, '$${rest.map(key => `.\"${key}\"`).join('')}', ${this.sql.parseEval(item[key])})`\n }\n if (value === valueInit) return true\n branches[createFilter(item)] = value\n })\n\n if (absent.length) branches[createMultiFilter(absent)] = escaped\n let value = `VALUES(${escaped})`\n for (const condition in branches) {\n value = `if(${condition}, ${branches[condition]}, ${value})`\n }\n return `${escaped} = ${value}`\n }).join(', ')\n\n const initFields = Object.keys(fields)\n const placeholder = `(${initFields.map(() => '?').join(', ')})`\n await this.query(\n `INSERT INTO ${this.sql.escapeId(name)} (${this._joinKeys(initFields)}) VALUES ${data.map(() => placeholder).join(', ')}\n ON DUPLICATE KEY UPDATE ${update}`,\n [].concat(...insertion.map(item => this._formatValues(name, item, initFields))),\n )\n }\n\n async aggregate(name: TableType, fields: {}, query: Query) {\n const keys = Object.keys(fields)\n if (!keys.length) return {}\n\n const filter = this._createFilter(name, query)\n const exprs = keys.map(key => `${this.sql.parseEval(fields[key])} AS ${this.sql.escapeId(key)}`).join(', ')\n const [data] = await this.query(`SELECT ${exprs} FROM ${name} WHERE ${filter}`)\n return data\n }\n}\n\nnamespace MysqlDatabase {\n export interface Config extends PoolConfig {}\n\n export const Config = Schema.object({\n host: Schema.string().description('要连接到的主机名。').default('localhost'),\n port: Schema.number().description('要连接到的端口号。').default(3306),\n user: Schema.string().description('要使用的用户名。').default('root'),\n password: Schema.string().description('要使用的密码。'),\n database: Schema.string().description('要访问的数据库名。').default('koishi'),\n })\n\n type Declarations = {\n [T in TableType]?: {\n [K in keyof Tables[T]]?: () => string\n }\n }\n\n /**\n * @deprecated use `import('koishi').Field` instead\n */\n export const tables: Declarations = {\n user: {},\n channel: {},\n }\n}\n\nexport default MysqlDatabase\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA,
|
|
4
|
+
"sourcesContent": ["import { createPool, escape as mysqlEscape, escapeId, format } from '@vlasky/mysql'\nimport type { Pool, PoolConfig, OkPacket } from 'mysql'\nimport { Context, Database, difference, Logger, makeArray, Schema, Query, Model, Tables, Dict, Time, KoishiError, pick } from 'koishi'\nimport { executeUpdate } from '@koishijs/orm-utils'\nimport { Builder } from '@koishijs/sql-utils'\n\ndeclare module 'mysql' {\n interface UntypedFieldInfo {\n packet: UntypedFieldInfo\n }\n}\n\ndeclare module 'koishi' {\n interface Database {\n mysql: MysqlDatabase\n }\n\n interface Modules {\n 'database-mysql': typeof import('.')\n }\n}\n\nconst logger = new Logger('mysql')\n\nexport type TableType = keyof Tables\n\nfunction getIntegerType(length = 11) {\n if (length <= 4) return 'tinyint'\n if (length <= 6) return 'smallint'\n if (length <= 9) return 'mediumint'\n if (length <= 11) return 'int'\n return 'bigint'\n}\n\nfunction getTypeDefinition({ type, length, precision, scale }: Model.Field) {\n switch (type) {\n case 'float':\n case 'double':\n case 'date':\n case 'time': return type\n case 'timestamp': return 'datetime'\n case 'integer': return getIntegerType(length)\n case 'unsigned': return `${getIntegerType(length)} unsigned`\n case 'decimal': return `decimal(${precision}, ${scale}) unsigned`\n case 'char': return `char(${length || 255})`\n case 'string': return `varchar(${length || 255})`\n case 'text': return `text(${length || 65535})`\n case 'list': return `text(${length || 65535})`\n case 'json': return `text(${length || 65535})`\n }\n}\n\nfunction createIndex(keys: string | string[]) {\n return makeArray(keys).map(key => escapeId(key)).join(', ')\n}\n\nclass MySQLBuilder extends Builder {\n constructor(private model: Model) {\n super()\n }\n\n format(sql: string, values: any[], stringifyObjects?: boolean, timeZone?: string) {\n return format(sql, values, stringifyObjects, timeZone)\n }\n\n escapeId(value: string, forbidQualified?: boolean) {\n return escapeId(value, forbidQualified)\n }\n\n escape(value: any, table?: string, field?: string) {\n return mysqlEscape(this.stringify(value, table, field))\n }\n\n stringify(value: any, table?: string, field?: string) {\n const type = MysqlDatabase.tables[table]?.[field]\n if (typeof type === 'object') return type.stringify(value)\n\n const meta = this.model.config[table]?.fields[field]\n if (meta?.type === 'json') {\n return JSON.stringify(value)\n } else if (meta?.type === 'list') {\n return value.join(',')\n } else if (Model.Field.date.includes(meta?.type)) {\n return Time.template('yyyy-MM-dd hh:mm:ss', value)\n }\n\n return value\n }\n}\n\ninterface QueryTask {\n sql: string\n resolve: (value: any) => void\n reject: (error: Error) => void\n}\n\nclass MysqlDatabase extends Database {\n public pool: Pool\n public config: MysqlDatabase.Config\n\n mysql = this\n sql: MySQLBuilder\n\n private _tableTasks: Dict<Promise<any>> = {}\n private _queryTasks: QueryTask[] = []\n\n constructor(public ctx: Context, config?: MysqlDatabase.Config) {\n super(ctx)\n\n this.config = {\n host: 'localhost',\n port: 3306,\n user: 'root',\n database: 'koishi',\n charset: 'utf8mb4_general_ci',\n multipleStatements: true,\n typeCast: (field, next) => {\n const { orgName, orgTable } = field.packet\n const type = MysqlDatabase.tables[orgTable]?.[orgName]\n if (typeof type === 'object') return type.parse(field)\n\n const meta = this.ctx.model.config[orgTable]?.fields[orgName]\n if (meta?.type === 'string') {\n return field.string()\n } else if (meta?.type === 'json') {\n const source = field.string()\n return source ? JSON.parse(source) : meta.initial\n } else if (meta?.type === 'list') {\n const source = field.string()\n return source ? source.split(',') : []\n }\n\n if (field.type === 'BIT') {\n return Boolean(field.buffer()?.readUInt8(0))\n } else {\n return next()\n }\n },\n ...config,\n }\n\n this.sql = new MySQLBuilder(this.ctx.model)\n }\n\n async start() {\n this.pool = createPool(this.config)\n\n for (const name in this.ctx.model.config) {\n this._tableTasks[name] = this._syncTable(name)\n }\n\n this.ctx.on('model', (name) => {\n this._tableTasks[name] = this._syncTable(name)\n })\n }\n\n stop() {\n this.pool.end()\n }\n\n private _getColDefs(name: string, columns: string[]) {\n const table = this.ctx.model.config[name]\n const { primary, foreign, autoInc } = table\n const fields = { ...table.fields }\n const unique = [...table.unique]\n const result: string[] = []\n\n // orm definitions\n for (const key in fields) {\n if (columns.includes(key)) continue\n const { initial, nullable = true } = fields[key]\n let def = escapeId(key)\n if (key === primary && autoInc) {\n def += ' int unsigned not null auto_increment'\n } else {\n const typedef = getTypeDefinition(fields[key])\n def += ' ' + typedef\n if (makeArray(primary).includes(key)) {\n def += ' not null'\n } else {\n def += (nullable ? ' ' : ' not ') + 'null'\n }\n // blob, text, geometry or json columns cannot have default values\n if (initial && !typedef.startsWith('text')) {\n def += ' default ' + this.sql.escape(initial, name, key)\n }\n }\n result.push(def)\n }\n\n if (!columns.length) {\n result.push(`primary key (${createIndex(primary)})`)\n for (const key of unique) {\n result.push(`unique index (${createIndex(key)})`)\n }\n for (const key in foreign) {\n const [table, key2] = foreign[key]\n result.push(`foreign key (${escapeId(key)}) references ${escapeId(table)} (${escapeId(key2)})`)\n }\n }\n\n return result\n }\n\n /** synchronize table schema */\n private async _syncTable(name: string) {\n await this._tableTasks[name]\n const data = await this.queue<any[]>('SELECT COLUMN_NAME from information_schema.columns WHERE TABLE_SCHEMA = ? && TABLE_NAME = ?', [this.config.database, name])\n const columns = data.map(row => row.COLUMN_NAME)\n const result = this._getColDefs(name, columns)\n if (!columns.length) {\n logger.info('auto creating table %c', name)\n await this.queue(`CREATE TABLE ?? (${result.join(',')}) COLLATE = ?`, [name, this.config.charset])\n } else if (result.length) {\n logger.info('auto updating table %c', name)\n await this.queue(`ALTER TABLE ?? ${result.map(def => 'ADD ' + def).join(',')}`, [name])\n }\n }\n\n _inferFields<T extends TableType>(table: T, keys: readonly string[]) {\n if (!keys) return\n const types = MysqlDatabase.tables[table] || {}\n return keys.map((key) => {\n const type = types[key]\n return typeof type === 'function' ? `${type()} AS ${key}` : key\n }) as (keyof Tables[T])[]\n }\n\n _createFilter(name: TableType, query: Query) {\n return this.sql.parseQuery(this.ctx.model.resolveQuery(name, query))\n }\n\n _joinKeys = (keys: readonly string[]) => {\n return keys ? keys.map(key => key.includes('`') ? key : `\\`${key}\\``).join(',') : '*'\n }\n\n _formatValues = (table: string, data: object, keys: readonly string[]) => {\n return keys.map((key) => this.sql.stringify(data[key], table as never, key))\n }\n\n query<T = any>(sql: string, values?: any): Promise<T> {\n const error = new Error()\n return new Promise((resolve, reject) => {\n sql = format(sql, values)\n logger.debug('[sql]', sql)\n this.pool.query(sql, (err: Error, results) => {\n if (!err) return resolve(results)\n logger.warn(sql)\n if (err['code'] === 'ER_DUP_ENTRY') {\n err = new KoishiError(err.message, 'database.duplicate-entry')\n }\n err.stack = err.message + error.stack.slice(5)\n reject(err)\n })\n })\n }\n\n queue<T = any>(sql: string, values?: any): Promise<T> {\n if (!this.config.multipleStatements) {\n return this.query(sql, values)\n }\n\n sql = format(sql, values)\n return new Promise<any>((resolve, reject) => {\n this._queryTasks.push({ sql, resolve, reject })\n process.nextTick(() => this._flushTasks())\n })\n }\n\n private async _flushTasks() {\n const tasks = this._queryTasks\n if (!tasks.length) return\n this._queryTasks = []\n\n try {\n let results = await this.query(tasks.map(task => task.sql).join('; '))\n if (tasks.length === 1) results = [results]\n tasks.forEach((task, index) => {\n task.resolve(results[index])\n })\n } catch (error) {\n tasks.forEach(task => task.reject(error))\n }\n }\n\n select<T extends {}>(table: string, fields: readonly (string & keyof T)[], conditional?: string, values?: readonly any[]): Promise<T[]>\n select(table: string, fields: string[], conditional?: string, values: readonly any[] = []) {\n logger.debug(`[select] ${table}: ${fields ? fields.join(', ') : '*'}`)\n const sql = 'SELECT '\n + this._joinKeys(fields)\n + (table.includes('.') ? `FROM ${table}` : ' FROM `' + table + `\\` _${table}`)\n + (conditional ? ' WHERE ' + conditional : '')\n return this.queue(sql, values)\n }\n\n async drop() {\n const data = await this.select('information_schema.tables', ['TABLE_NAME'], 'TABLE_SCHEMA = ?', [this.config.database])\n if (!data.length) return\n await this.query(data.map(({ TABLE_NAME }) => `DROP TABLE ${this.sql.escapeId(TABLE_NAME)}`).join('; '))\n }\n\n async stats() {\n const data = await this.select('information_schema.tables', ['TABLE_NAME', 'TABLE_ROWS', 'DATA_LENGTH'], 'TABLE_SCHEMA = ?', [this.config.database])\n const stats: Query.Stats = { size: 0 }\n stats.tables = Object.fromEntries(data.map(({ TABLE_NAME: name, TABLE_ROWS: count, DATA_LENGTH: size }) => {\n stats.size += size\n return [name, { count, size }]\n }))\n return stats\n }\n\n async get(name: TableType, query: Query, modifier?: Query.Modifier) {\n await this._tableTasks[name]\n const filter = this._createFilter(name, query)\n if (filter === '0') return []\n const { fields, limit, offset, sort } = Query.resolveModifier(modifier)\n const keys = this._joinKeys(this._inferFields(name, fields))\n let sql = `SELECT ${keys} FROM ${name} _${name} WHERE ${filter}`\n if (sort) sql += ' ORDER BY ' + Object.entries(sort).map(([key, order]) => `${this.sql.escapeId(key)} ${order}`).join(', ')\n if (limit) sql += ' LIMIT ' + limit\n if (offset) sql += ' OFFSET ' + offset\n return this.queue(sql)\n }\n\n async set(name: TableType, query: Query, data: {}) {\n await this._tableTasks[name]\n const filter = this._createFilter(name, query)\n if (filter === '0') return\n const keys = Object.keys(data)\n const update = keys.map((key) => {\n const valueExpr = this.sql.parseEval(data[key], name, key)\n const [field, ...rest] = key.split('.')\n const keyExpr = this.sql.escapeId(field)\n if (!rest.length) return `${keyExpr} = ${valueExpr}`\n return `${keyExpr} = json_set(ifnull(${keyExpr}, '{}'), '$${rest.map(key => `.\"${key}\"`).join('')}', ${valueExpr})`\n }).join(', ')\n await this.query(`UPDATE ${name} SET ${update} WHERE ${filter}`)\n }\n\n async remove(name: TableType, query: Query) {\n await this._tableTasks[name]\n const filter = this._createFilter(name, query)\n if (filter === '0') return\n await this.query('DELETE FROM ?? WHERE ' + filter, [name])\n }\n\n async create(name: TableType, data: {}) {\n await this._tableTasks[name]\n data = { ...this.ctx.model.create(name), ...data }\n const keys = Object.keys(data)\n const header = await this.query<OkPacket>(\n `INSERT INTO ?? (${this._joinKeys(keys)}) VALUES (${keys.map(() => '?').join(', ')})`,\n [name, ...this._formatValues(name, data, keys)],\n )\n return { ...data, id: header.insertId } as any\n }\n\n async upsert(name: TableType, data: any[], keys: string | string[]) {\n if (!data.length) return\n await this._tableTasks[name]\n\n const { fields, primary } = this.ctx.model.config[name]\n const merged = {}\n const insertion = data.map((item) => {\n Object.assign(merged, item)\n return executeUpdate(this.ctx.model.create(name), item)\n })\n const indexFields = makeArray(keys || primary)\n const dataFields = [...new Set(Object.keys(merged).map(key => key.split('.', 1)[0]))]\n const updateFields = difference(dataFields, indexFields)\n\n const createFilter = (item: any) => this.sql.parseQuery(pick(item, indexFields))\n const createMultiFilter = (items: any[]) => {\n if (items.length === 1) {\n return createFilter(items[0])\n } else if (indexFields.length === 1) {\n const key = indexFields[0]\n return this.sql.parseQuery({ [key]: items.map(item => item[key]) })\n } else {\n return items.map(createFilter).join(' OR ')\n }\n }\n\n const update = updateFields.map((field) => {\n const escaped = this.sql.escapeId(field)\n const branches: Dict<string> = {}\n const absent = data.filter((item) => {\n // update directly\n if (field in item) {\n if (Object.keys(item[field]).some(key => key.startsWith('$'))) {\n branches[createFilter(item)] = this.sql.parseEval(item[field], name, field)\n }\n return\n }\n\n // update with json_set\n const valueInit = `ifnull(${escaped}, '{}')`\n let value = valueInit\n for (const key in item) {\n const [first, ...rest] = key.split('.')\n if (first !== field) continue\n value = `json_set(${value}, '$${rest.map(key => `.\"${key}\"`).join('')}', ${this.sql.parseEval(item[key])})`\n }\n if (value === valueInit) return true\n branches[createFilter(item)] = value\n })\n\n if (absent.length) branches[createMultiFilter(absent)] = escaped\n let value = `VALUES(${escaped})`\n for (const condition in branches) {\n value = `if(${condition}, ${branches[condition]}, ${value})`\n }\n return `${escaped} = ${value}`\n }).join(', ')\n\n const initFields = Object.keys(fields)\n const placeholder = `(${initFields.map(() => '?').join(', ')})`\n await this.query(\n `INSERT INTO ${this.sql.escapeId(name)} (${this._joinKeys(initFields)}) VALUES ${data.map(() => placeholder).join(', ')}\n ON DUPLICATE KEY UPDATE ${update}`,\n [].concat(...insertion.map(item => this._formatValues(name, item, initFields))),\n )\n }\n\n async eval(name: TableType, expr: any, query: Query) {\n await this._tableTasks[name]\n const filter = this._createFilter(name, query)\n const output = this.sql.parseEval(expr)\n const [data] = await this.queue(`SELECT ${output} AS value FROM ${name} WHERE ${filter}`)\n return data.value\n }\n}\n\nnamespace MysqlDatabase {\n export interface Config extends PoolConfig {}\n\n export const Config = Schema.object({\n host: Schema.string().description('要连接到的主机名。').default('localhost'),\n port: Schema.number().description('要连接到的端口号。').default(3306),\n user: Schema.string().description('要使用的用户名。').default('root'),\n password: Schema.string().description('要使用的密码。'),\n database: Schema.string().description('要访问的数据库名。').default('koishi'),\n })\n\n type Declarations = {\n [T in TableType]?: {\n [K in keyof Tables[T]]?: () => string\n }\n }\n\n /**\n * @deprecated use `import('koishi').Field` instead\n */\n export const tables: Declarations = {\n user: {},\n channel: {},\n }\n}\n\nexport default MysqlDatabase\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koishijs/plugin-database-mysql",
|
|
3
3
|
"description": "MySQL support for Koishi",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.1.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -31,16 +31,16 @@
|
|
|
31
31
|
"mysql"
|
|
32
32
|
],
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@koishijs/
|
|
35
|
-
"@koishijs/
|
|
34
|
+
"@koishijs/database-tests": "^1.0.0",
|
|
35
|
+
"@koishijs/plugin-mock": "^1.0.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"koishi": "^4.
|
|
38
|
+
"koishi": "^4.1.0"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@koishijs/orm-utils": "^1.0.0
|
|
42
|
-
"@koishijs/sql-utils": "^1.0.0
|
|
43
|
-
"@types/mysql": "^2.15.
|
|
44
|
-
"mysql": "^2.18.
|
|
41
|
+
"@koishijs/orm-utils": "^1.0.0",
|
|
42
|
+
"@koishijs/sql-utils": "^1.0.0",
|
|
43
|
+
"@types/mysql": "^2.15.20",
|
|
44
|
+
"@vlasky/mysql": "^2.18.5"
|
|
45
45
|
}
|
|
46
46
|
}
|