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