@koishijs/plugin-database-mysql 4.0.0-beta.1 → 4.0.0-beta.5

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 CHANGED
@@ -1,6 +1,5 @@
1
- import * as Koishi from 'koishi';
2
- import { Pool, PoolConfig, TypeCast } from 'mysql';
3
- import { Context, Database, Schema } from 'koishi';
1
+ import { Pool, PoolConfig, escapeId, TypeCast } from 'mysql';
2
+ import { Context, Database, Schema, Query, Model, Tables as KoishiTables } from 'koishi';
4
3
  import { SQLBuilder } from '@koishijs/sql-utils';
5
4
  declare module 'mysql' {
6
5
  interface UntypedFieldInfo {
@@ -16,29 +15,43 @@ declare module 'koishi' {
16
15
  }
17
16
  }
18
17
  export type TableType = keyof Tables;
19
- export interface Tables extends Koishi.Tables {
18
+ export interface Tables extends KoishiTables {
19
+ }
20
+ declare class MySQLBuilder extends SQLBuilder {
21
+ private model;
22
+ constructor(model: Model);
23
+ escapeId: typeof escapeId;
24
+ stringify(value: any, table?: string, field?: string): any;
25
+ escape(value: any, table?: string, field?: string): string;
20
26
  }
21
27
  declare class MysqlDatabase extends Database {
22
28
  ctx: Context;
23
29
  pool: Pool;
24
30
  config: MysqlDatabase.Config;
25
31
  mysql: this;
26
- sql: SQLBuilder;
27
- escape: (value: any, table?: TableType, field?: string) => string;
28
- escapeId: (value: string) => string;
32
+ sql: MySQLBuilder;
33
+ private tasks;
29
34
  inferFields<T extends TableType>(table: T, keys: readonly string[]): (keyof Tables[T])[];
30
35
  constructor(ctx: Context, config?: MysqlDatabase.Config);
31
- private columns;
32
36
  private getColDefs;
33
37
  start(): Promise<void>;
38
+ /** synchronize table schema */
39
+ private _syncTable;
34
40
  joinKeys: (keys: readonly string[]) => string;
35
41
  $in: (table: TableType, key: string, values: readonly any[]) => string;
36
42
  formatValues: (table: string, data: object, keys: readonly string[]) => any[];
37
- query<T extends {}>(source: string, values?: any): Promise<T>;
38
- query<T extends {}>(source: string[], values?: any): Promise<T>;
43
+ query<T = any>(source: string, values?: any): Promise<T>;
44
+ query<T = any>(source: string[], values?: any): Promise<T>;
39
45
  select<T extends {}>(table: string, fields: readonly (string & keyof T)[], conditional?: string, values?: readonly any[]): Promise<T[]>;
40
46
  count<K extends TableType>(table: K, conditional?: string): Promise<number>;
41
47
  stop(): void;
48
+ drop(name: TableType): Promise<void>;
49
+ get(name: TableType, query: Query, modifier?: Query.Modifier): Promise<any>;
50
+ set(name: TableType, query: Query, data: {}): Promise<void>;
51
+ remove(name: TableType, query: Query): Promise<void>;
52
+ create(name: TableType, data: {}): Promise<any>;
53
+ upsert(name: TableType, data: any[], _keys: string | string[]): Promise<void>;
54
+ aggregate(name: TableType, fields: {}, query: Query): Promise<any>;
42
55
  }
43
56
  declare namespace MysqlDatabase {
44
57
  export interface Config extends PoolConfig {
@@ -49,6 +62,16 @@ declare namespace MysqlDatabase {
49
62
  user?: string;
50
63
  password?: string;
51
64
  database?: string;
65
+ } & {
66
+ [x: string]: any;
67
+ }, {
68
+ host?: string;
69
+ port?: number;
70
+ user?: string;
71
+ password?: string;
72
+ database?: string;
73
+ } & {
74
+ [x: string]: any;
52
75
  }>;
53
76
  type Declarations = {
54
77
  [T in TableType]?: {
package/lib/index.js CHANGED
@@ -47,27 +47,7 @@ __export(exports, {
47
47
  var import_mysql = __toModule(require("mysql"));
48
48
  var import_koishi = __toModule(require("koishi"));
49
49
  var import_sql_utils = __toModule(require("@koishijs/sql-utils"));
50
- var Koishi = __toModule(require("koishi"));
51
- var import_util = __toModule(require("util"));
52
50
  var logger = new import_koishi.Logger("mysql");
53
- function stringify(value, table, field) {
54
- var _a, _b;
55
- const type = (_a = MysqlDatabase.tables[table]) == null ? void 0 : _a[field];
56
- if (typeof type === "object")
57
- return type.stringify(value);
58
- const meta = (_b = Koishi.Tables.config[table]) == null ? void 0 : _b.fields[field];
59
- if ((meta == null ? void 0 : meta.type) === "json") {
60
- return JSON.stringify(value);
61
- } else if ((meta == null ? void 0 : meta.type) === "list") {
62
- return value.join(",");
63
- }
64
- return value;
65
- }
66
- __name(stringify, "stringify");
67
- function escape(value, table, field) {
68
- return (0, import_mysql.escape)(stringify(value, table, field));
69
- }
70
- __name(escape, "escape");
71
51
  function getIntegerType(length = 11) {
72
52
  if (length <= 4)
73
53
  return "tinyint";
@@ -111,24 +91,46 @@ function createIndex(keys) {
111
91
  return (0, import_koishi.makeArray)(keys).map((key) => (0, import_mysql.escapeId)(key)).join(", ");
112
92
  }
113
93
  __name(createIndex, "createIndex");
94
+ var MySQLBuilder = class extends import_sql_utils.SQLBuilder {
95
+ constructor(model) {
96
+ super();
97
+ this.model = model;
98
+ this.escapeId = import_mysql.escapeId;
99
+ }
100
+ stringify(value, table, field) {
101
+ var _a, _b;
102
+ const type = (_a = MysqlDatabase.tables[table]) == null ? void 0 : _a[field];
103
+ if (typeof type === "object")
104
+ return type.stringify(value);
105
+ const meta = (_b = this.model.config[table]) == null ? void 0 : _b.fields[field];
106
+ if ((meta == null ? void 0 : meta.type) === "json") {
107
+ return JSON.stringify(value);
108
+ } else if ((meta == null ? void 0 : meta.type) === "list") {
109
+ return value.join(",");
110
+ } else if (import_koishi.Model.Field.date.includes(meta == null ? void 0 : meta.type)) {
111
+ return import_koishi.Time.template("yyyy-MM-dd hh:mm:ss", value);
112
+ }
113
+ return value;
114
+ }
115
+ escape(value, table, field) {
116
+ return (0, import_mysql.escape)(this.stringify(value, table, field));
117
+ }
118
+ };
119
+ __name(MySQLBuilder, "MySQLBuilder");
114
120
  var MysqlDatabase = class extends import_koishi.Database {
115
121
  constructor(ctx, config) {
116
122
  super(ctx);
117
123
  this.ctx = ctx;
118
124
  this.mysql = this;
119
- this.columns = {};
125
+ this.tasks = {};
120
126
  this.joinKeys = (keys) => {
121
127
  return keys ? keys.map((key) => key.includes("`") ? key : `\`${key}\``).join(",") : "*";
122
128
  };
123
129
  this.$in = (table, key, values) => {
124
- return `${this.escapeId(key)} IN (${values.map((val) => this.escape(val, table, key)).join(", ")})`;
130
+ return `${this.sql.escapeId(key)} IN (${values.map((val) => this.sql.escape(val, table, key)).join(", ")})`;
125
131
  };
126
132
  this.formatValues = (table, data, keys) => {
127
- return keys.map((key) => {
128
- if (typeof data[key] !== "object" || import_util.types.isDate(data[key]))
129
- return data[key];
130
- return stringify(data[key], table, key);
131
- });
133
+ return keys.map((key) => this.sql.stringify(data[key], table, key));
132
134
  };
133
135
  this.config = __spreadValues({
134
136
  host: "localhost",
@@ -143,11 +145,12 @@ var MysqlDatabase = class extends import_koishi.Database {
143
145
  const type = (_a = MysqlDatabase.tables[orgTable]) == null ? void 0 : _a[orgName];
144
146
  if (typeof type === "object")
145
147
  return type.parse(field);
146
- const meta = (_b = Koishi.Tables.config[orgTable]) == null ? void 0 : _b.fields[orgName];
148
+ const meta = (_b = this.ctx.model.config[orgTable]) == null ? void 0 : _b.fields[orgName];
147
149
  if ((meta == null ? void 0 : meta.type) === "string") {
148
150
  return field.string();
149
151
  } else if ((meta == null ? void 0 : meta.type) === "json") {
150
- return JSON.parse(field.string()) || meta.initial;
152
+ const source = field.string();
153
+ return source ? JSON.parse(source) : meta.initial;
151
154
  } else if ((meta == null ? void 0 : meta.type) === "list") {
152
155
  const source = field.string();
153
156
  return source ? source.split(",") : [];
@@ -159,29 +162,23 @@ var MysqlDatabase = class extends import_koishi.Database {
159
162
  }
160
163
  }
161
164
  }, config);
162
- this.sql = new class extends import_sql_utils.SQLBuilder {
163
- constructor() {
164
- super(...arguments);
165
- this.escape = escape;
166
- this.escapeId = import_mysql.escapeId;
167
- }
168
- }();
165
+ this.sql = new MySQLBuilder(this.ctx.model);
169
166
  }
170
167
  inferFields(table, keys) {
171
168
  if (!keys)
172
169
  return;
173
- const types2 = MysqlDatabase.tables[table] || {};
170
+ const types = MysqlDatabase.tables[table] || {};
174
171
  return keys.map((key) => {
175
- const type = types2[key];
172
+ const type = types[key];
176
173
  return typeof type === "function" ? `${type()} AS ${key}` : key;
177
174
  });
178
175
  }
179
- getColDefs(name, cols = []) {
180
- const table = Koishi.Tables.config[name];
176
+ getColDefs(name, columns) {
177
+ const table = this.ctx.model.config[name];
181
178
  const { primary, foreign, autoInc } = table;
182
179
  const fields = __spreadValues({}, table.fields);
183
180
  const unique = [...table.unique];
184
- const keys = this.columns[name] || [];
181
+ const result = [];
185
182
  if (name === "user") {
186
183
  const platforms = new Set(this.ctx.bots.map((bot) => bot.platform));
187
184
  for (const name2 of platforms) {
@@ -191,14 +188,14 @@ var MysqlDatabase = class extends import_koishi.Database {
191
188
  }
192
189
  for (const key in MysqlDatabase.tables[name]) {
193
190
  const value = MysqlDatabase.tables[name][key];
194
- if (keys.includes(key) || typeof value === "function")
191
+ if (columns.includes(key) || typeof value === "function")
195
192
  continue;
196
- cols.push(`${(0, import_mysql.escapeId)(key)} ${MysqlDatabase.Domain.definition(value)}`);
193
+ result.push(`${(0, import_mysql.escapeId)(key)} ${MysqlDatabase.Domain.definition(value)}`);
197
194
  }
198
195
  for (const key in fields) {
199
- if (keys.includes(key))
196
+ if (columns.includes(key))
200
197
  continue;
201
- const { initial, nullable = initial === void 0 || initial === null } = fields[key];
198
+ const { initial, nullable = true } = fields[key];
202
199
  let def = (0, import_mysql.escapeId)(key);
203
200
  if (key === primary && autoInc) {
204
201
  def += " int unsigned not null auto_increment";
@@ -206,39 +203,43 @@ var MysqlDatabase = class extends import_koishi.Database {
206
203
  const typedef = getTypeDefinition(fields[key]);
207
204
  def += " " + typedef + (nullable ? " " : " not ") + "null";
208
205
  if (initial && !typedef.startsWith("text")) {
209
- def += " default " + escape(initial, name, key);
206
+ def += " default " + this.sql.escape(initial, name, key);
210
207
  }
211
208
  }
212
- cols.push(def);
209
+ result.push(def);
213
210
  }
214
- if (!keys.length) {
215
- cols.push(`primary key (${createIndex(primary)})`);
211
+ if (!columns.length) {
212
+ result.push(`primary key (${createIndex(primary)})`);
216
213
  for (const key of unique) {
217
- cols.push(`unique index (${createIndex(key)})`);
214
+ result.push(`unique index (${createIndex(key)})`);
218
215
  }
219
216
  for (const key in foreign) {
220
217
  const [table2, key2] = foreign[key];
221
- cols.push(`foreign key (${(0, import_mysql.escapeId)(key)}) references ${(0, import_mysql.escapeId)(table2)} (${(0, import_mysql.escapeId)(key2)})`);
218
+ result.push(`foreign key (${(0, import_mysql.escapeId)(key)}) references ${(0, import_mysql.escapeId)(table2)} (${(0, import_mysql.escapeId)(key2)})`);
222
219
  }
223
220
  }
224
- return cols;
221
+ return result;
225
222
  }
226
223
  async start() {
227
- var _a;
228
224
  this.pool = (0, import_mysql.createPool)(this.config);
229
- const data = await this.query("SELECT TABLE_NAME, COLUMN_NAME from information_schema.columns WHERE TABLE_SCHEMA = ?", [this.config.database]);
230
- for (const { TABLE_NAME, COLUMN_NAME } of data) {
231
- ((_a = this.columns)[TABLE_NAME] || (_a[TABLE_NAME] = [])).push(COLUMN_NAME);
225
+ for (const name in this.ctx.model.config) {
226
+ this.tasks[name] = this._syncTable(name);
232
227
  }
233
- for (const name in Koishi.Tables.config) {
234
- const cols = this.getColDefs(name);
235
- if (!this.columns[name]) {
236
- logger.info("auto creating table %c", name);
237
- await this.query(`CREATE TABLE ?? (${cols.join(",")}) COLLATE = ?`, [name, this.config.charset]);
238
- } else if (cols.length) {
239
- logger.info("auto updating table %c", name);
240
- await this.query(`ALTER TABLE ?? ${cols.map((def) => "ADD " + def).join(",")}`, [name]);
241
- }
228
+ this.ctx.on("model", (name) => {
229
+ this.tasks[name] = this._syncTable(name);
230
+ });
231
+ }
232
+ async _syncTable(name) {
233
+ await this.tasks[name];
234
+ const data = await this.query("SELECT COLUMN_NAME from information_schema.columns WHERE TABLE_SCHEMA = ? && TABLE_NAME = ?", [this.config.database, name]);
235
+ const columns = data.map((row) => row.COLUMN_NAME);
236
+ const result = this.getColDefs(name, columns);
237
+ if (!columns.length) {
238
+ logger.info("auto creating table %c", name);
239
+ await this.query(`CREATE TABLE ?? (${result.join(",")}) COLLATE = ?`, [name, this.config.charset]);
240
+ } else if (result.length) {
241
+ logger.info("auto updating table %c", name);
242
+ await this.query(`ALTER TABLE ?? ${result.map((def) => "ADD " + def).join(",")}`, [name]);
242
243
  }
243
244
  }
244
245
  async query(source, values) {
@@ -281,18 +282,98 @@ var MysqlDatabase = class extends import_koishi.Database {
281
282
  stop() {
282
283
  this.pool.end();
283
284
  }
285
+ async drop(name) {
286
+ if (name) {
287
+ await this.query(`DROP TABLE ${this.sql.escapeId(name)}`);
288
+ } else {
289
+ const data = await this.select("information_schema.tables", ["TABLE_NAME"], "TABLE_SCHEMA = ?", [this.config.database]);
290
+ if (!data.length)
291
+ return;
292
+ await this.query(data.map(({ TABLE_NAME }) => `DROP TABLE ${this.sql.escapeId(TABLE_NAME)}`).join("; "));
293
+ }
294
+ }
295
+ async get(name, query, modifier) {
296
+ const filter = this.sql.parseQuery(this.ctx.model.resolveQuery(name, query));
297
+ if (filter === "0")
298
+ return [];
299
+ const { fields, limit, offset } = import_koishi.Query.resolveModifier(modifier);
300
+ const keys = this.joinKeys(this.inferFields(name, fields));
301
+ let sql = `SELECT ${keys} FROM ${name} _${name} WHERE ${filter}`;
302
+ if (limit)
303
+ sql += " LIMIT " + limit;
304
+ if (offset)
305
+ sql += " OFFSET " + offset;
306
+ return this.query(sql);
307
+ }
308
+ async set(name, query, data) {
309
+ await this.tasks[name];
310
+ const filter = this.sql.parseQuery(this.ctx.model.resolveQuery(name, query));
311
+ if (filter === "0")
312
+ return;
313
+ const keys = Object.keys(data);
314
+ const update = keys.map((key) => {
315
+ return `${this.sql.escapeId(key)} = ${this.sql.escape(data[key], name, key)}`;
316
+ }).join(", ");
317
+ await this.query(`UPDATE ${name} SET ${update} WHERE ${filter}`);
318
+ }
319
+ async remove(name, query) {
320
+ const filter = this.sql.parseQuery(this.ctx.model.resolveQuery(name, query));
321
+ if (filter === "0")
322
+ return;
323
+ await this.query("DELETE FROM ?? WHERE " + filter, [name]);
324
+ }
325
+ async create(name, data) {
326
+ await this.tasks[name];
327
+ data = __spreadValues(__spreadValues({}, this.ctx.model.create(name)), data);
328
+ const keys = Object.keys(data);
329
+ const header = await this.query(`INSERT INTO ?? (${this.joinKeys(keys)}) VALUES (${keys.map(() => "?").join(", ")})`, [name, ...this.formatValues(name, data, keys)]);
330
+ return __spreadProps(__spreadValues({}, data), { id: header.insertId });
331
+ }
332
+ async upsert(name, data, _keys) {
333
+ if (!data.length)
334
+ return;
335
+ await this.tasks[name];
336
+ const { fields, primary } = this.ctx.model.config[name];
337
+ const fallback = this.ctx.model.create(name);
338
+ const initKeys = Object.keys(fields);
339
+ const merged = {};
340
+ const newData = data.map((item) => {
341
+ Object.assign(merged, item);
342
+ return __spreadValues(__spreadValues({}, fallback), item);
343
+ });
344
+ const keys = (0, import_koishi.makeArray)(_keys || primary);
345
+ const placeholder = `(${initKeys.map(() => "?").join(", ")})`;
346
+ const update = (0, import_koishi.difference)(Object.keys(merged), keys).map((key) => {
347
+ const conditions = data.filter((item) => !(key in item)).map((item) => {
348
+ return keys.map((key2) => `${this.sql.escapeId(key2)} = ${this.sql.stringify(item[key2], name, key2)}`).join(" AND ");
349
+ }).join(" OR ");
350
+ key = this.sql.escapeId(key);
351
+ if (!conditions.length)
352
+ return `${key} = VALUES(${key})`;
353
+ return `${key} = IF(${conditions}, ${key}, VALUES(${key}))`;
354
+ }).join(", ");
355
+ await this.query(`INSERT INTO ${this.sql.escapeId(name)} (${this.joinKeys(initKeys)}) VALUES ${data.map(() => placeholder).join(", ")}
356
+ ON DUPLICATE KEY UPDATE ${update}`, [].concat(...newData.map((item) => this.formatValues(name, item, initKeys))));
357
+ }
358
+ async aggregate(name, fields, query) {
359
+ const keys = Object.keys(fields);
360
+ if (!keys.length)
361
+ return {};
362
+ const filter = this.sql.parseQuery(this.ctx.model.resolveQuery(name, query));
363
+ const exprs = keys.map((key) => `${this.sql.parseEval(fields[key])} AS ${this.sql.escapeId(key)}`).join(", ");
364
+ const [data] = await this.query(`SELECT ${exprs} FROM ${name} WHERE ${filter}`);
365
+ return data;
366
+ }
284
367
  };
285
368
  __name(MysqlDatabase, "MysqlDatabase");
286
- MysqlDatabase.prototype.escape = escape;
287
- MysqlDatabase.prototype.escapeId = import_mysql.escapeId;
288
369
  (function(MysqlDatabase2) {
289
370
  MysqlDatabase2.Config = import_koishi.Schema.object({
290
- host: import_koishi.Schema.string("要连接到的主机名。").default("localhost"),
291
- port: import_koishi.Schema.number("要连接到的端口号。").default(3306),
292
- user: import_koishi.Schema.string("要使用的用户名。").default("root"),
293
- password: import_koishi.Schema.string("要使用的密码。"),
294
- database: import_koishi.Schema.string("要访问的数据库名。").default("koishi")
295
- }, true);
371
+ host: import_koishi.Schema.string().description("要连接到的主机名。").default("localhost"),
372
+ port: import_koishi.Schema.number().description("要连接到的端口号。").default(3306),
373
+ user: import_koishi.Schema.string().description("要使用的用户名。").default("root"),
374
+ password: import_koishi.Schema.string().description("要使用的密码。"),
375
+ database: import_koishi.Schema.string().description("要访问的数据库名。").default("koishi")
376
+ });
296
377
  MysqlDatabase2.tables = {
297
378
  user: {},
298
379
  channel: {}
@@ -347,79 +428,6 @@ MysqlDatabase.prototype.escapeId = import_mysql.escapeId;
347
428
  Domain2.Json = Json;
348
429
  })(Domain = MysqlDatabase2.Domain || (MysqlDatabase2.Domain = {}));
349
430
  })(MysqlDatabase || (MysqlDatabase = {}));
350
- import_koishi.Database.extend(MysqlDatabase, {
351
- async drop(name) {
352
- if (name) {
353
- await this.query(`DROP TABLE ${this.escapeId(name)}`);
354
- } else {
355
- const data = await this.select("information_schema.tables", ["TABLE_NAME"], "TABLE_SCHEMA = ?", [this.config.database]);
356
- if (!data.length)
357
- return;
358
- await this.query(data.map(({ TABLE_NAME }) => `DROP TABLE ${this.escapeId(TABLE_NAME)}`).join("; "));
359
- }
360
- },
361
- async get(name, query, modifier) {
362
- const filter = this.sql.parseQuery(import_koishi.Query.resolve(name, query));
363
- if (filter === "0")
364
- return [];
365
- const { fields, limit, offset } = import_koishi.Query.resolveModifier(modifier);
366
- const keys = this.joinKeys(this.inferFields(name, fields));
367
- let sql = `SELECT ${keys} FROM ${name} _${name} WHERE ${filter}`;
368
- if (limit)
369
- sql += " LIMIT " + limit;
370
- if (offset)
371
- sql += " OFFSET " + offset;
372
- return this.query(sql);
373
- },
374
- async set(name, query, data) {
375
- const filter = this.sql.parseQuery(import_koishi.Query.resolve(name, query));
376
- if (filter === "0")
377
- return;
378
- const keys = Object.keys(data);
379
- const update = keys.map((key) => {
380
- return `${this.escapeId(key)} = ${this.escape(data[key], name, key)}`;
381
- }).join(", ");
382
- await this.query(`UPDATE ${name} SET ${update} WHERE ${filter}`);
383
- },
384
- async remove(name, query) {
385
- const filter = this.sql.parseQuery(import_koishi.Query.resolve(name, query));
386
- if (filter === "0")
387
- return;
388
- await this.query("DELETE FROM ?? WHERE " + filter, [name]);
389
- },
390
- async create(name, data) {
391
- data = __spreadValues(__spreadValues({}, Koishi.Tables.create(name)), data);
392
- const keys = Object.keys(data);
393
- const header = await this.query(`INSERT INTO ?? (${this.joinKeys(keys)}) VALUES (${keys.map(() => "?").join(", ")})`, [name, ...this.formatValues(name, data, keys)]);
394
- return __spreadProps(__spreadValues({}, data), { id: header.insertId });
395
- },
396
- async upsert(name, data, keys) {
397
- if (!data.length)
398
- return;
399
- const { fields, primary } = Koishi.Tables.config[name];
400
- const fallback = Koishi.Tables.create(name);
401
- const initKeys = Object.keys(fields);
402
- const updateKeys = Object.keys(data[0]);
403
- data = data.map((item) => __spreadValues(__spreadValues({}, fallback), item));
404
- keys = (0, import_koishi.makeArray)(keys || primary);
405
- const placeholder = `(${initKeys.map(() => "?").join(", ")})`;
406
- const update = (0, import_koishi.difference)(updateKeys, keys).map((key) => {
407
- key = this.escapeId(key);
408
- return `${key} = VALUES(${key})`;
409
- }).join(", ");
410
- await this.query(`INSERT INTO ${this.escapeId(name)} (${this.joinKeys(initKeys)}) VALUES ${data.map(() => placeholder).join(", ")}
411
- ON DUPLICATE KEY UPDATE ${update}`, [].concat(...data.map((data2) => this.formatValues(name, data2, initKeys))));
412
- },
413
- async aggregate(name, fields, query) {
414
- const keys = Object.keys(fields);
415
- if (!keys.length)
416
- return {};
417
- const filter = this.sql.parseQuery(import_koishi.Query.resolve(name, query));
418
- const exprs = keys.map((key) => `${this.sql.parseEval(fields[key])} AS ${this.escapeId(key)}`).join(", ");
419
- const [data] = await this.query(`SELECT ${exprs} FROM ${name} WHERE ${filter}`);
420
- return data;
421
- }
422
- });
423
431
  var src_default = MysqlDatabase;
424
432
  // Annotate the CommonJS export names for ESM import in node:
425
433
  0 && (module.exports = {});
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, TypeCast } from 'mysql'\nimport { Context, Database, difference, Logger, makeArray, Schema, Query } from 'koishi'\nimport { SQLBuilder } from '@koishijs/sql-utils'\nimport * as Koishi from 'koishi'\nimport { types } from 'util'\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\nexport interface Tables extends Koishi.Tables {}\n\nfunction 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 = Koishi.Tables.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 }\n\n return value\n}\n\nfunction escape(value: any, table?: string, field?: string) {\n return mysqlEscape(stringify(value, table, field))\n}\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 }: Koishi.Tables.Field) {\n switch (type) {\n case 'float':\n case 'double':\n case 'date':\n case 'time':\n case 'timestamp': return type\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 `char(${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 MysqlDatabase extends Database {\n public pool: Pool\n public config: MysqlDatabase.Config\n\n mysql = this\n sql: SQLBuilder\n\n escape: (value: any, table?: TableType, field?: string) => string\n escapeId: (value: string) => string\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 constructor(public ctx: Context, config?: MysqlDatabase.Config) {\n super(ctx)\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 = Koishi.Tables.config[orgTable]?.fields[orgName]\n if (meta?.type === 'string') {\n return field.string()\n } else if (meta?.type === 'json') {\n return JSON.parse(field.string()) || 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 class extends SQLBuilder {\n escape = escape\n escapeId = escapeId\n }()\n }\n\n private columns: Record<string, string[]> = {}\n\n private getColDefs(name: string, cols: string[] = []) {\n const table = Koishi.Tables.config[name]\n const { primary, foreign, autoInc } = table\n const fields = { ...table.fields }\n const unique = [...table.unique]\n const keys = this.columns[name] || []\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 // mysql definitions (FIXME: remove in v4)\n for (const key in MysqlDatabase.tables[name]) {\n const value = MysqlDatabase.tables[name][key]\n if (keys.includes(key) || typeof value === 'function') continue\n cols.push(`${escapeId(key)} ${MysqlDatabase.Domain.definition(value)}`)\n }\n\n // orm definitions\n for (const key in fields) {\n if (keys.includes(key)) continue\n const { initial, nullable = initial === undefined || initial === null } = 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 + (nullable ? ' ' : ' not ') + 'null'\n // blob, text, geometry or json columns cannot have default values\n if (initial && !typedef.startsWith('text')) {\n def += ' default ' + escape(initial, name, key)\n }\n }\n cols.push(def)\n }\n\n if (!keys.length) {\n cols.push(`primary key (${createIndex(primary)})`)\n for (const key of unique) {\n cols.push(`unique index (${createIndex(key)})`)\n }\n for (const key in foreign) {\n const [table, key2] = foreign[key]\n cols.push(`foreign key (${escapeId(key)}) references ${escapeId(table)} (${escapeId(key2)})`)\n }\n }\n\n return cols\n }\n\n async start() {\n this.pool = createPool(this.config)\n const data = await this.query<any[]>('SELECT TABLE_NAME, COLUMN_NAME from information_schema.columns WHERE TABLE_SCHEMA = ?', [this.config.database])\n for (const { TABLE_NAME, COLUMN_NAME } of data) {\n (this.columns[TABLE_NAME] ||= []).push(COLUMN_NAME)\n }\n\n for (const name in Koishi.Tables.config) {\n const cols = this.getColDefs(name)\n if (!this.columns[name]) {\n logger.info('auto creating table %c', name)\n await this.query(`CREATE TABLE ?? (${cols.join(',')}) COLLATE = ?`, [name, this.config.charset])\n } else if (cols.length) {\n logger.info('auto updating table %c', name)\n await this.query(`ALTER TABLE ?? ${cols.map(def => 'ADD ' + def).join(',')}`, [name])\n }\n }\n }\n\n joinKeys = (keys: readonly string[]) => {\n return keys ? keys.map(key => key.includes('`') ? key : `\\`${key}\\``).join(',') : '*'\n }\n\n $in = (table: TableType, key: string, values: readonly any[]) => {\n return `${this.escapeId(key)} IN (${values.map(val => this.escape(val, table, key)).join(', ')})`\n }\n\n formatValues = (table: string, data: object, keys: readonly string[]) => {\n return keys.map((key) => {\n if (typeof data[key] !== 'object' || types.isDate(data[key])) return data[key]\n return stringify(data[key], table as never, key)\n })\n }\n\n query<T extends {}>(source: string, values?: any): Promise<T>\n query<T extends {}>(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 err[Symbol.for('koishi.error-type')] = 'duplicate-entry'\n }\n reject(err)\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\nMysqlDatabase.prototype.escape = escape\nMysqlDatabase.prototype.escapeId = escapeId\n\nnamespace MysqlDatabase {\n export interface Config extends PoolConfig {}\n\n export const Config = Schema.object({\n host: Schema.string('要连接到的主机名。').default('localhost'),\n port: Schema.number('要连接到的端口号。').default(3306),\n user: Schema.string('要使用的用户名。').default('root'),\n password: Schema.string('要使用的密码。'),\n database: Schema.string('要访问的数据库名。').default('koishi'),\n }, true)\n\n type Declarations = {\n [T in TableType]?: {\n [K in keyof Tables[T]]?: string | (() => string) | Domain<Tables[T][K]>\n }\n }\n\n /**\n * @deprecated use `import('koishi').Field` instead\n */\n export const tables: Declarations = {\n user: {},\n channel: {},\n }\n\n type FieldInfo = Parameters<Exclude<TypeCast, boolean>>[0]\n\n export interface Domain<T = any> {\n definition: string\n parse(source: FieldInfo): T\n stringify(value: T): string\n }\n\n /**\n * @deprecated use `import('koishi').Field` instead\n */\n export namespace Domain {\n export function definition(domain: string | Domain) {\n return typeof domain === 'string' ? domain : domain.definition\n }\n\n export class String implements Domain<string> {\n constructor(public definition = 'TEXT') {}\n\n parse(field: FieldInfo) {\n return field.string()\n }\n\n stringify(value: any) {\n return value\n }\n }\n\n export class Array implements Domain<string[]> {\n constructor(public definition = 'TEXT') {}\n\n parse(field: FieldInfo) {\n const source = field.string()\n return source ? source.split(',') : []\n }\n\n stringify(value: string[]) {\n return value.join(',')\n }\n }\n\n export class Json implements Domain {\n // mysql does not support text column with default value\n constructor(public definition = 'text', private defaultValue?: any) {}\n\n parse(field: FieldInfo) {\n return JSON.parse(field.string()) || this.defaultValue\n }\n\n stringify(value: any) {\n return JSON.stringify(value)\n }\n }\n }\n}\n\nDatabase.extend(MysqlDatabase, {\n async drop(name) {\n if (name) {\n await this.query(`DROP TABLE ${this.escapeId(name)}`)\n } else {\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.escapeId(TABLE_NAME)}`).join('; '))\n }\n },\n\n async get(name, query, modifier) {\n const filter = this.sql.parseQuery(Query.resolve(name, query))\n if (filter === '0') return []\n const { fields, limit, offset } = 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 return this.query(sql)\n },\n\n async set(name, query, data) {\n const filter = this.sql.parseQuery(Query.resolve(name, query))\n if (filter === '0') return\n const keys = Object.keys(data)\n const update = keys.map((key) => {\n return `${this.escapeId(key)} = ${this.escape(data[key], name, key)}`\n }).join(', ')\n await this.query(`UPDATE ${name} SET ${update} WHERE ${filter}`)\n },\n\n async remove(name, query) {\n const filter = this.sql.parseQuery(Query.resolve(name, query))\n if (filter === '0') return\n await this.query('DELETE FROM ?? WHERE ' + filter, [name])\n },\n\n async create(name, data) {\n data = { ...Koishi.Tables.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, data, keys: string | string[]) {\n if (!data.length) return\n const { fields, primary } = Koishi.Tables.config[name]\n const fallback = Koishi.Tables.create(name)\n const initKeys = Object.keys(fields)\n const updateKeys = Object.keys(data[0])\n data = data.map(item => ({ ...fallback, ...item }))\n keys = makeArray(keys || primary)\n const placeholder = `(${initKeys.map(() => '?').join(', ')})`\n const update = difference(updateKeys, keys).map((key) => {\n key = this.escapeId(key)\n return `${key} = VALUES(${key})`\n }).join(', ')\n await this.query(\n `INSERT INTO ${this.escapeId(name)} (${this.joinKeys(initKeys)}) VALUES ${data.map(() => placeholder).join(', ')}\n ON DUPLICATE KEY UPDATE ${update}`,\n [].concat(...data.map(data => this.formatValues(name, data, initKeys))),\n )\n },\n\n async aggregate(name, fields, query) {\n const keys = Object.keys(fields)\n if (!keys.length) return {}\n\n const filter = this.sql.parseQuery(Query.resolve(name, query))\n const exprs = keys.map(key => `${this.sql.parseEval(fields[key])} AS ${this.escapeId(key)}`).join(', ')\n const [data] = await this.query(`SELECT ${exprs} FROM ${name} WHERE ${filter}`)\n return data\n },\n})\n\nexport default MysqlDatabase\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { createPool, Pool, PoolConfig, escape as mysqlEscape, escapeId, format, TypeCast } from 'mysql'\nimport { Context, Database, difference, Logger, makeArray, Schema, Query, Model, Tables as KoishiTables, Dict, Time } from 'koishi'\nimport { SQLBuilder } 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\nexport interface Tables extends KoishiTables {}\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':\n case 'timestamp': return type\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 `char(${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 SQLBuilder {\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 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 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 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 // mysql definitions (FIXME: remove in v4)\n for (const key in MysqlDatabase.tables[name]) {\n const value = MysqlDatabase.tables[name][key]\n if (columns.includes(key) || typeof value === 'function') continue\n result.push(`${escapeId(key)} ${MysqlDatabase.Domain.definition(value)}`)\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 + (nullable ? ' ' : ' not ') + 'null'\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 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 /** 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 joinKeys = (keys: readonly string[]) => {\n return keys ? keys.map(key => key.includes('`') ? key : `\\`${key}\\``).join(',') : '*'\n }\n\n $in = (table: TableType, key: string, values: readonly any[]) => {\n return `${this.sql.escapeId(key)} IN (${values.map(val => this.sql.escape(val, table, 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 err[Symbol.for('koishi.error-type')] = 'duplicate-entry'\n }\n reject(err)\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(name: TableType) {\n if (name) {\n await this.query(`DROP TABLE ${this.sql.escapeId(name)}`)\n } else {\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\n async get(name: TableType, query: Query, modifier?: Query.Modifier) {\n const filter = this.sql.parseQuery(this.ctx.model.resolveQuery(name, query))\n if (filter === '0') return []\n const { fields, limit, offset } = 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 return this.query(sql)\n }\n\n async set(name: TableType, query: Query, data: {}) {\n await this.tasks[name]\n const filter = this.sql.parseQuery(this.ctx.model.resolveQuery(name, query))\n if (filter === '0') return\n const keys = Object.keys(data)\n const update = keys.map((key) => {\n return `${this.sql.escapeId(key)} = ${this.sql.escape(data[key], name, key)}`\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.sql.parseQuery(this.ctx.model.resolveQuery(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 const { fields, primary } = this.ctx.model.config[name]\n const fallback = this.ctx.model.create(name)\n const initKeys = Object.keys(fields)\n const merged = {}\n const newData = data.map((item) => {\n Object.assign(merged, item)\n return { ...fallback, ...item }\n })\n const keys = makeArray(_keys || primary)\n const placeholder = `(${initKeys.map(() => '?').join(', ')})`\n const update = difference(Object.keys(merged), keys).map((key) => {\n const conditions = data.filter(item => !(key in item)).map((item) => {\n return keys.map(key => `${this.sql.escapeId(key)} = ${this.sql.stringify(item[key], name, key)}`).join(' AND ')\n }).join(' OR ')\n key = this.sql.escapeId(key)\n if (!conditions.length) return `${key} = VALUES(${key})`\n return `${key} = IF(${conditions}, ${key}, VALUES(${key}))`\n }).join(', ')\n await this.query(\n `INSERT INTO ${this.sql.escapeId(name)} (${this.joinKeys(initKeys)}) VALUES ${data.map(() => placeholder).join(', ')}\n ON DUPLICATE KEY UPDATE ${update}`,\n [].concat(...newData.map(item => this.formatValues(name, item, initKeys))),\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.sql.parseQuery(this.ctx.model.resolveQuery(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 | (() => string) | Domain<Tables[T][K]>\n }\n }\n\n /**\n * @deprecated use `import('koishi').Field` instead\n */\n export const tables: Declarations = {\n user: {},\n channel: {},\n }\n\n type FieldInfo = Parameters<Exclude<TypeCast, boolean>>[0]\n\n export interface Domain<T = any> {\n definition: string\n parse(source: FieldInfo): T\n stringify(value: T): string\n }\n\n /**\n * @deprecated use `import('koishi').Field` instead\n */\n export namespace Domain {\n export function definition(domain: string | Domain) {\n return typeof domain === 'string' ? domain : domain.definition\n }\n\n export class String implements Domain<string> {\n constructor(public definition = 'TEXT') {}\n\n parse(field: FieldInfo) {\n return field.string()\n }\n\n stringify(value: any) {\n return value\n }\n }\n\n export class Array implements Domain<string[]> {\n constructor(public definition = 'TEXT') {}\n\n parse(field: FieldInfo) {\n const source = field.string()\n return source ? source.split(',') : []\n }\n\n stringify(value: string[]) {\n return value.join(',')\n }\n }\n\n export class Json implements Domain {\n // mysql does not support text column with default value\n constructor(public definition = 'text', private defaultValue?: any) {}\n\n parse(field: FieldInfo) {\n return JSON.parse(field.string()) || this.defaultValue\n }\n\n stringify(value: any) {\n return JSON.stringify(value)\n }\n }\n }\n}\n\nexport default MysqlDatabase\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@koishijs/plugin-database-mysql",
3
3
  "description": "MySQL support for Koishi",
4
- "version": "4.0.0-beta.1",
4
+ "version": "4.0.0-beta.5",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
8
8
  "lib"
9
9
  ],
10
- "author": "Shigma <1700011071@pku.edu.cn>",
10
+ "author": "Shigma <shigma10826@gmail.com>",
11
11
  "license": "MIT",
12
12
  "scripts": {
13
13
  "lint": "eslint src --ext .ts"
@@ -30,11 +30,15 @@
30
30
  "database",
31
31
  "mysql"
32
32
  ],
33
+ "devDependencies": {
34
+ "@koishijs/plugin-mock": "^1.0.0-beta.1",
35
+ "@koishijs/test-utils": "^8.0.0-beta.5"
36
+ },
33
37
  "peerDependencies": {
34
- "koishi": "^4.0.0-beta.2"
38
+ "koishi": "^4.0.0-beta.5"
35
39
  },
36
40
  "dependencies": {
37
- "@koishijs/sql-utils": "^1.0.0-beta.1",
41
+ "@koishijs/sql-utils": "^1.0.0-beta.3",
38
42
  "@types/mysql": "^2.15.19",
39
43
  "mysql": "^2.18.1"
40
44
  }