@koishijs/plugin-database-mysql 4.0.0 → 4.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +4 -4
- package/lib/index.js +25 -18
- package/lib/index.js.map +2 -2
- package/package.json +8 -7
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="koishi/lib" />
|
|
2
|
-
import { Pool, PoolConfig
|
|
3
|
-
import { Context, Database,
|
|
2
|
+
import type { Pool, PoolConfig } from 'mysql';
|
|
3
|
+
import { Context, Database, Model, Query, Schema, Tables } from 'koishi';
|
|
4
4
|
import { Builder } from '@koishijs/sql-utils';
|
|
5
5
|
declare module 'mysql' {
|
|
6
6
|
interface UntypedFieldInfo {
|
|
@@ -19,8 +19,8 @@ export declare type TableType = keyof Tables;
|
|
|
19
19
|
declare class MySQLBuilder extends Builder {
|
|
20
20
|
private model;
|
|
21
21
|
constructor(model: Model);
|
|
22
|
-
format:
|
|
23
|
-
escapeId:
|
|
22
|
+
format(sql: string, values: any[], stringifyObjects?: boolean, timeZone?: string): string;
|
|
23
|
+
escapeId(value: string, forbidQualified?: boolean): string;
|
|
24
24
|
escape(value: any, table?: string, field?: string): string;
|
|
25
25
|
stringify(value: any, table?: string, field?: string): any;
|
|
26
26
|
}
|
package/lib/index.js
CHANGED
|
@@ -44,11 +44,12 @@ var __toModule = (module2) => {
|
|
|
44
44
|
__export(exports, {
|
|
45
45
|
default: () => src_default
|
|
46
46
|
});
|
|
47
|
-
var import_mysql = __toModule(require("mysql"));
|
|
47
|
+
var import_mysql = __toModule(require("@vlasky/mysql"));
|
|
48
48
|
var import_koishi = __toModule(require("koishi"));
|
|
49
49
|
var import_orm_utils = __toModule(require("@koishijs/orm-utils"));
|
|
50
50
|
var import_sql_utils = __toModule(require("@koishijs/sql-utils"));
|
|
51
51
|
var logger = new import_koishi.Logger("mysql");
|
|
52
|
+
var DEFAULT_DATE = new Date("1970-01-01");
|
|
52
53
|
function getIntegerType(length = 11) {
|
|
53
54
|
if (length <= 4)
|
|
54
55
|
return "tinyint";
|
|
@@ -97,8 +98,12 @@ var MySQLBuilder = class extends import_sql_utils.Builder {
|
|
|
97
98
|
constructor(model) {
|
|
98
99
|
super();
|
|
99
100
|
this.model = model;
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
}
|
|
102
|
+
format(sql, values, stringifyObjects, timeZone) {
|
|
103
|
+
return (0, import_mysql.format)(sql, values, stringifyObjects, timeZone);
|
|
104
|
+
}
|
|
105
|
+
escapeId(value, forbidQualified) {
|
|
106
|
+
return (0, import_mysql.escapeId)(value, forbidQualified);
|
|
102
107
|
}
|
|
103
108
|
escape(value, table, field) {
|
|
104
109
|
return (0, import_mysql.escape)(this.stringify(value, table, field));
|
|
@@ -155,6 +160,16 @@ var MysqlDatabase = class extends import_koishi.Database {
|
|
|
155
160
|
} else if ((meta == null ? void 0 : meta.type) === "list") {
|
|
156
161
|
const source = field.string();
|
|
157
162
|
return source ? source.split(",") : [];
|
|
163
|
+
} else if ((meta == null ? void 0 : meta.type) === "time") {
|
|
164
|
+
const source = field.string();
|
|
165
|
+
if (!source)
|
|
166
|
+
return meta.initial;
|
|
167
|
+
const time = new Date(DEFAULT_DATE);
|
|
168
|
+
const [h, m, s] = source.split(":");
|
|
169
|
+
time.setHours(parseInt(h));
|
|
170
|
+
time.setMinutes(parseInt(m));
|
|
171
|
+
time.setSeconds(parseInt(s));
|
|
172
|
+
return time;
|
|
158
173
|
}
|
|
159
174
|
if (field.type === "BIT") {
|
|
160
175
|
return Boolean((_c = field.buffer()) == null ? void 0 : _c.readUInt8(0));
|
|
@@ -183,13 +198,6 @@ var MysqlDatabase = class extends import_koishi.Database {
|
|
|
183
198
|
const fields = __spreadValues({}, table.fields);
|
|
184
199
|
const unique = [...table.unique];
|
|
185
200
|
const result = [];
|
|
186
|
-
if (name === "user") {
|
|
187
|
-
const platforms = new Set(this.ctx.bots.map((bot) => bot.platform));
|
|
188
|
-
for (const name2 of platforms) {
|
|
189
|
-
fields[name2] = { type: "string", length: 63 };
|
|
190
|
-
unique.push(name2);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
201
|
for (const key in fields) {
|
|
194
202
|
if (columns.includes(key))
|
|
195
203
|
continue;
|
|
@@ -257,18 +265,17 @@ var MysqlDatabase = class extends import_koishi.Database {
|
|
|
257
265
|
if (!err)
|
|
258
266
|
return resolve(results);
|
|
259
267
|
logger.warn(sql);
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
reject(new import_koishi.KoishiError(err.message, "database.duplicate-entry"));
|
|
263
|
-
} else {
|
|
264
|
-
reject(err);
|
|
268
|
+
if (err["code"] === "ER_DUP_ENTRY") {
|
|
269
|
+
err = new import_koishi.KoishiError(err.message, "database.duplicate-entry");
|
|
265
270
|
}
|
|
271
|
+
err.stack = err.message + error.stack.slice(5);
|
|
272
|
+
reject(err);
|
|
266
273
|
});
|
|
267
274
|
});
|
|
268
275
|
}
|
|
269
276
|
queue(sql, values) {
|
|
270
277
|
if (!this.config.multipleStatements) {
|
|
271
|
-
return this.query(sql);
|
|
278
|
+
return this.query(sql, values);
|
|
272
279
|
}
|
|
273
280
|
sql = (0, import_mysql.format)(sql, values);
|
|
274
281
|
return new Promise((resolve, reject) => {
|
|
@@ -429,9 +436,9 @@ __name(MysqlDatabase, "MysqlDatabase");
|
|
|
429
436
|
(function(MysqlDatabase2) {
|
|
430
437
|
MysqlDatabase2.Config = import_koishi.Schema.object({
|
|
431
438
|
host: import_koishi.Schema.string().description("要连接到的主机名。").default("localhost"),
|
|
432
|
-
port: import_koishi.Schema.
|
|
439
|
+
port: import_koishi.Schema.natural().max(65535).description("要连接到的端口号。").default(3306),
|
|
433
440
|
user: import_koishi.Schema.string().description("要使用的用户名。").default("root"),
|
|
434
|
-
password: import_koishi.Schema.string().description("要使用的密码。"),
|
|
441
|
+
password: import_koishi.Schema.string().description("要使用的密码。").role("secret"),
|
|
435
442
|
database: import_koishi.Schema.string().description("要访问的数据库名。").default("koishi")
|
|
436
443
|
});
|
|
437
444
|
MysqlDatabase2.tables = {
|
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, OkPacket } from 'mysql'\nimport { Context, Database, difference, Logger, makeArray, Schema, Query, Model, Tables, Dict, Time, KoishiError, pick } from 'koishi'\nimport { executeUpdate } from '@koishijs/orm-utils'\nimport { Builder } from '@koishijs/sql-utils'\n\ndeclare module 'mysql' {\n interface UntypedFieldInfo {\n packet: UntypedFieldInfo\n }\n}\n\ndeclare module 'koishi' {\n interface Database {\n mysql: MysqlDatabase\n }\n\n interface Modules {\n 'database-mysql': typeof import('.')\n }\n}\n\nconst logger = new Logger('mysql')\n\nexport type TableType = keyof Tables\n\nfunction getIntegerType(length = 11) {\n if (length <= 4) return 'tinyint'\n if (length <= 6) return 'smallint'\n if (length <= 9) return 'mediumint'\n if (length <= 11) return 'int'\n return 'bigint'\n}\n\nfunction getTypeDefinition({ type, length, precision, scale }: Model.Field) {\n switch (type) {\n case 'float':\n case 'double':\n case 'date':\n case 'time': return type\n case 'timestamp': return 'datetime'\n case 'integer': return getIntegerType(length)\n case 'unsigned': return `${getIntegerType(length)} unsigned`\n case 'decimal': return `decimal(${precision}, ${scale}) unsigned`\n case 'char': return `char(${length || 255})`\n case 'string': return `varchar(${length || 255})`\n case 'text': return `text(${length || 65535})`\n case 'list': return `text(${length || 65535})`\n case 'json': return `text(${length || 65535})`\n }\n}\n\nfunction createIndex(keys: string | string[]) {\n return makeArray(keys).map(key => escapeId(key)).join(', ')\n}\n\nclass MySQLBuilder extends Builder {\n constructor(private model: Model) {\n super()\n }\n\n format = format\n\n escapeId = escapeId\n\n escape(value: any, table?: string, field?: string) {\n return mysqlEscape(this.stringify(value, table, field))\n }\n\n stringify(value: any, table?: string, field?: string) {\n const type = MysqlDatabase.tables[table]?.[field]\n if (typeof type === 'object') return type.stringify(value)\n\n const meta = this.model.config[table]?.fields[field]\n if (meta?.type === 'json') {\n return JSON.stringify(value)\n } else if (meta?.type === 'list') {\n return value.join(',')\n } else if (Model.Field.date.includes(meta?.type)) {\n return Time.template('yyyy-MM-dd hh:mm:ss', value)\n }\n\n return value\n }\n}\n\ninterface QueryTask {\n sql: string\n resolve: (value: any) => void\n reject: (error: Error) => void\n}\n\nclass MysqlDatabase extends Database {\n public pool: Pool\n public config: MysqlDatabase.Config\n\n mysql = this\n sql: MySQLBuilder\n\n private _tableTasks: Dict<Promise<any>> = {}\n private _queryTasks: QueryTask[] = []\n\n constructor(public ctx: Context, config?: MysqlDatabase.Config) {\n super(ctx)\n\n this.config = {\n host: 'localhost',\n port: 3306,\n user: 'root',\n database: 'koishi',\n charset: 'utf8mb4_general_ci',\n multipleStatements: true,\n typeCast: (field, next) => {\n const { orgName, orgTable } = field.packet\n const type = MysqlDatabase.tables[orgTable]?.[orgName]\n if (typeof type === 'object') return type.parse(field)\n\n const meta = this.ctx.model.config[orgTable]?.fields[orgName]\n if (meta?.type === 'string') {\n return field.string()\n } else if (meta?.type === 'json') {\n const source = field.string()\n return source ? JSON.parse(source) : meta.initial\n } else if (meta?.type === 'list') {\n const source = field.string()\n return source ? source.split(',') : []\n }\n\n if (field.type === 'BIT') {\n return Boolean(field.buffer()?.readUInt8(0))\n } else {\n return next()\n }\n },\n ...config,\n }\n\n this.sql = new MySQLBuilder(this.ctx.model)\n }\n\n async start() {\n this.pool = createPool(this.config)\n\n for (const name in this.ctx.model.config) {\n this._tableTasks[name] = this._syncTable(name)\n }\n\n this.ctx.on('model', (name) => {\n this._tableTasks[name] = this._syncTable(name)\n })\n }\n\n stop() {\n this.pool.end()\n }\n\n private _getColDefs(name: string, columns: string[]) {\n const table = this.ctx.model.config[name]\n const { primary, foreign, autoInc } = table\n const fields = { ...table.fields }\n const unique = [...table.unique]\n const result: string[] = []\n\n // 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._tableTasks[name]\n const data = await this.queue<any[]>('SELECT COLUMN_NAME from information_schema.columns WHERE TABLE_SCHEMA = ? && TABLE_NAME = ?', [this.config.database, name])\n const columns = data.map(row => row.COLUMN_NAME)\n const result = this._getColDefs(name, columns)\n if (!columns.length) {\n logger.info('auto creating table %c', name)\n await this.queue(`CREATE TABLE ?? (${result.join(',')}) COLLATE = ?`, [name, this.config.charset])\n } else if (result.length) {\n logger.info('auto updating table %c', name)\n await this.queue(`ALTER TABLE ?? ${result.map(def => 'ADD ' + def).join(',')}`, [name])\n }\n }\n\n _inferFields<T extends TableType>(table: T, keys: readonly string[]) {\n if (!keys) return\n const types = MysqlDatabase.tables[table] || {}\n return keys.map((key) => {\n const type = types[key]\n return typeof type === 'function' ? `${type()} AS ${key}` : key\n }) as (keyof Tables[T])[]\n }\n\n _createFilter(name: TableType, query: Query) {\n return this.sql.parseQuery(this.ctx.model.resolveQuery(name, query))\n }\n\n _joinKeys = (keys: readonly string[]) => {\n return keys ? keys.map(key => key.includes('`') ? key : `\\`${key}\\``).join(',') : '*'\n }\n\n _formatValues = (table: string, data: object, keys: readonly string[]) => {\n return keys.map((key) => this.sql.stringify(data[key], table as never, key))\n }\n\n query<T = any>(sql: string, values?: any): Promise<T> {\n const error = new Error()\n return new Promise((resolve, reject) => {\n sql = format(sql, values)\n logger.debug('[sql]', sql)\n this.pool.query(sql, (err, results) => {\n if (!err) return resolve(results)\n logger.warn(sql)\n err.stack = err.message + error.stack.slice(5)\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 queue<T = any>(sql: string, values?: any): Promise<T> {\n if (!this.config.multipleStatements) {\n return this.query(sql)\n }\n\n sql = format(sql, values)\n return new Promise<any>((resolve, reject) => {\n this._queryTasks.push({ sql, resolve, reject })\n process.nextTick(() => this._flushTasks())\n })\n }\n\n private async _flushTasks() {\n const tasks = this._queryTasks\n if (!tasks.length) return\n this._queryTasks = []\n\n try {\n let results = await this.query(tasks.map(task => task.sql).join('; '))\n if (tasks.length === 1) results = [results]\n tasks.forEach((task, index) => {\n task.resolve(results[index])\n })\n } catch (error) {\n tasks.forEach(task => task.reject(error))\n }\n }\n\n select<T extends {}>(table: string, fields: readonly (string & keyof T)[], conditional?: string, values?: readonly any[]): Promise<T[]>\n select(table: string, fields: string[], conditional?: string, values: readonly any[] = []) {\n logger.debug(`[select] ${table}: ${fields ? fields.join(', ') : '*'}`)\n const sql = 'SELECT '\n + this._joinKeys(fields)\n + (table.includes('.') ? `FROM ${table}` : ' FROM `' + table + `\\` _${table}`)\n + (conditional ? ' WHERE ' + conditional : '')\n return this.queue(sql, values)\n }\n\n async drop() {\n const data = await this.select('information_schema.tables', ['TABLE_NAME'], 'TABLE_SCHEMA = ?', [this.config.database])\n if (!data.length) return\n await this.query(data.map(({ TABLE_NAME }) => `DROP TABLE ${this.sql.escapeId(TABLE_NAME)}`).join('; '))\n }\n\n async stats() {\n const data = await this.select('information_schema.tables', ['TABLE_NAME', 'TABLE_ROWS', 'DATA_LENGTH'], 'TABLE_SCHEMA = ?', [this.config.database])\n const stats: Query.Stats = { size: 0 }\n stats.tables = Object.fromEntries(data.map(({ TABLE_NAME: name, TABLE_ROWS: count, DATA_LENGTH: size }) => {\n stats.size += size\n return [name, { count, size }]\n }))\n return stats\n }\n\n async get(name: TableType, query: Query, modifier?: Query.Modifier) {\n await this._tableTasks[name]\n const filter = this._createFilter(name, query)\n if (filter === '0') return []\n const { fields, limit, offset, sort } = Query.resolveModifier(modifier)\n const keys = this._joinKeys(this._inferFields(name, fields))\n let sql = `SELECT ${keys} FROM ${name} _${name} WHERE ${filter}`\n if (sort) sql += ' ORDER BY ' + Object.entries(sort).map(([key, order]) => `${this.sql.escapeId(key)} ${order}`).join(', ')\n if (limit) sql += ' LIMIT ' + limit\n if (offset) sql += ' OFFSET ' + offset\n return this.queue(sql)\n }\n\n async set(name: TableType, query: Query, data: {}) {\n await this._tableTasks[name]\n const filter = this._createFilter(name, query)\n if (filter === '0') return\n const keys = Object.keys(data)\n const update = keys.map((key) => {\n const valueExpr = this.sql.parseEval(data[key], name, key)\n const [field, ...rest] = key.split('.')\n const keyExpr = this.sql.escapeId(field)\n if (!rest.length) return `${keyExpr} = ${valueExpr}`\n return `${keyExpr} = json_set(ifnull(${keyExpr}, '{}'), '$${rest.map(key => `.\"${key}\"`).join('')}', ${valueExpr})`\n }).join(', ')\n await this.query(`UPDATE ${name} SET ${update} WHERE ${filter}`)\n }\n\n async remove(name: TableType, query: Query) {\n await this._tableTasks[name]\n const filter = this._createFilter(name, query)\n if (filter === '0') return\n await this.query('DELETE FROM ?? WHERE ' + filter, [name])\n }\n\n async create(name: TableType, data: {}) {\n await this._tableTasks[name]\n data = { ...this.ctx.model.create(name), ...data }\n const keys = Object.keys(data)\n const header = await this.query<OkPacket>(\n `INSERT INTO ?? (${this._joinKeys(keys)}) VALUES (${keys.map(() => '?').join(', ')})`,\n [name, ...this._formatValues(name, data, keys)],\n )\n return { ...data, id: header.insertId } as any\n }\n\n async upsert(name: TableType, data: any[], keys: string | string[]) {\n if (!data.length) return\n await this._tableTasks[name]\n\n const { fields, primary } = this.ctx.model.config[name]\n const merged = {}\n const insertion = data.map((item) => {\n Object.assign(merged, item)\n return executeUpdate(this.ctx.model.create(name), item)\n })\n const indexFields = makeArray(keys || primary)\n const dataFields = [...new Set(Object.keys(merged).map(key => key.split('.', 1)[0]))]\n const updateFields = difference(dataFields, indexFields)\n\n const createFilter = (item: any) => this.sql.parseQuery(pick(item, indexFields))\n const createMultiFilter = (items: any[]) => {\n if (items.length === 1) {\n return createFilter(items[0])\n } else if (indexFields.length === 1) {\n const key = indexFields[0]\n return this.sql.parseQuery({ [key]: items.map(item => item[key]) })\n } else {\n return items.map(createFilter).join(' OR ')\n }\n }\n\n const update = updateFields.map((field) => {\n const escaped = this.sql.escapeId(field)\n const branches: Dict<string> = {}\n const absent = data.filter((item) => {\n // update directly\n if (field in item) {\n if (Object.keys(item[field]).some(key => key.startsWith('$'))) {\n branches[createFilter(item)] = this.sql.parseEval(item[field], name, field)\n }\n return\n }\n\n // update with json_set\n const valueInit = `ifnull(${escaped}, '{}')`\n let value = valueInit\n for (const key in item) {\n const [first, ...rest] = key.split('.')\n if (first !== field) continue\n value = `json_set(${value}, '$${rest.map(key => `.\"${key}\"`).join('')}', ${this.sql.parseEval(item[key])})`\n }\n if (value === valueInit) return true\n branches[createFilter(item)] = value\n })\n\n if (absent.length) branches[createMultiFilter(absent)] = escaped\n let value = `VALUES(${escaped})`\n for (const condition in branches) {\n value = `if(${condition}, ${branches[condition]}, ${value})`\n }\n return `${escaped} = ${value}`\n }).join(', ')\n\n const initFields = Object.keys(fields)\n const placeholder = `(${initFields.map(() => '?').join(', ')})`\n await this.query(\n `INSERT INTO ${this.sql.escapeId(name)} (${this._joinKeys(initFields)}) VALUES ${data.map(() => placeholder).join(', ')}\n ON DUPLICATE KEY UPDATE ${update}`,\n [].concat(...insertion.map(item => this._formatValues(name, item, initFields))),\n )\n }\n\n async eval(name: TableType, expr: any, query: Query) {\n await this._tableTasks[name]\n const filter = this._createFilter(name, query)\n const output = this.sql.parseEval(expr)\n const [data] = await this.queue(`SELECT ${output} AS value FROM ${name} WHERE ${filter}`)\n return data.value\n }\n}\n\nnamespace MysqlDatabase {\n export interface Config extends PoolConfig {}\n\n export const Config = Schema.object({\n host: Schema.string().description('要连接到的主机名。').default('localhost'),\n port: Schema.number().description('要连接到的端口号。').default(3306),\n user: Schema.string().description('要使用的用户名。').default('root'),\n password: Schema.string().description('要使用的密码。'),\n database: Schema.string().description('要访问的数据库名。').default('koishi'),\n })\n\n type Declarations = {\n [T in TableType]?: {\n [K in keyof Tables[T]]?: () => string\n }\n }\n\n /**\n * @deprecated use `import('koishi').Field` instead\n */\n export const tables: Declarations = {\n user: {},\n channel: {},\n }\n}\n\nexport default MysqlDatabase\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA,
|
|
4
|
+
"sourcesContent": ["import { createPool, escapeId, format, escape as mysqlEscape } from '@vlasky/mysql'\nimport type { OkPacket, Pool, PoolConfig } from 'mysql'\nimport { Context, Database, Dict, difference, KoishiError, Logger, makeArray, Model, pick, Query, Schema, Tables, Time } from 'koishi'\nimport { executeUpdate } from '@koishijs/orm-utils'\nimport { Builder } from '@koishijs/sql-utils'\n\ndeclare module 'mysql' {\n interface UntypedFieldInfo {\n packet: UntypedFieldInfo\n }\n}\n\ndeclare module 'koishi' {\n interface Database {\n mysql: MysqlDatabase\n }\n\n interface Modules {\n 'database-mysql': typeof import('.')\n }\n}\n\nconst logger = new Logger('mysql')\n\nconst DEFAULT_DATE = new Date('1970-01-01')\n\nexport type TableType = keyof Tables\n\nfunction getIntegerType(length = 11) {\n if (length <= 4) return 'tinyint'\n if (length <= 6) return 'smallint'\n if (length <= 9) return 'mediumint'\n if (length <= 11) return 'int'\n return 'bigint'\n}\n\nfunction getTypeDefinition({ type, length, precision, scale }: Model.Field) {\n switch (type) {\n case 'float':\n case 'double':\n case 'date':\n case 'time': return type\n case 'timestamp': return 'datetime'\n case 'integer': return getIntegerType(length)\n case 'unsigned': return `${getIntegerType(length)} unsigned`\n case 'decimal': return `decimal(${precision}, ${scale}) unsigned`\n case 'char': return `char(${length || 255})`\n case 'string': return `varchar(${length || 255})`\n case 'text': return `text(${length || 65535})`\n case 'list': return `text(${length || 65535})`\n case 'json': return `text(${length || 65535})`\n }\n}\n\nfunction createIndex(keys: string | string[]) {\n return makeArray(keys).map(key => escapeId(key)).join(', ')\n}\n\nclass MySQLBuilder extends Builder {\n constructor(private model: Model) {\n super()\n }\n\n format(sql: string, values: any[], stringifyObjects?: boolean, timeZone?: string) {\n return format(sql, values, stringifyObjects, timeZone)\n }\n\n escapeId(value: string, forbidQualified?: boolean) {\n return escapeId(value, forbidQualified)\n }\n\n escape(value: any, table?: string, field?: string) {\n return mysqlEscape(this.stringify(value, table, field))\n }\n\n stringify(value: any, table?: string, field?: string) {\n const type = MysqlDatabase.tables[table]?.[field]\n if (typeof type === 'object') return type.stringify(value)\n\n const meta = this.model.config[table]?.fields[field]\n if (meta?.type === 'json') {\n return JSON.stringify(value)\n } else if (meta?.type === 'list') {\n return value.join(',')\n } else if (Model.Field.date.includes(meta?.type)) {\n return Time.template('yyyy-MM-dd hh:mm:ss', value)\n }\n\n return value\n }\n}\n\ninterface QueryTask {\n sql: string\n resolve: (value: any) => void\n reject: (error: Error) => void\n}\n\nclass MysqlDatabase extends Database {\n public pool: Pool\n public config: MysqlDatabase.Config\n\n mysql = this\n sql: MySQLBuilder\n\n private _tableTasks: Dict<Promise<any>> = {}\n private _queryTasks: QueryTask[] = []\n\n constructor(public ctx: Context, config?: MysqlDatabase.Config) {\n super(ctx)\n\n this.config = {\n host: 'localhost',\n port: 3306,\n user: 'root',\n database: 'koishi',\n charset: 'utf8mb4_general_ci',\n multipleStatements: true,\n typeCast: (field, next) => {\n const { orgName, orgTable } = field.packet\n const type = MysqlDatabase.tables[orgTable]?.[orgName]\n if (typeof type === 'object') return type.parse(field)\n\n const meta = this.ctx.model.config[orgTable]?.fields[orgName]\n if (meta?.type === 'string') {\n return field.string()\n } else if (meta?.type === 'json') {\n const source = field.string()\n return source ? JSON.parse(source) : meta.initial\n } else if (meta?.type === 'list') {\n const source = field.string()\n return source ? source.split(',') : []\n } else if (meta?.type === 'time') {\n const source = field.string()\n if (!source) return meta.initial\n const time = new Date(DEFAULT_DATE)\n const [h, m, s] = source.split(':')\n time.setHours(parseInt(h))\n time.setMinutes(parseInt(m))\n time.setSeconds(parseInt(s))\n return time\n }\n\n if (field.type === 'BIT') {\n return Boolean(field.buffer()?.readUInt8(0))\n } else {\n return next()\n }\n },\n ...config,\n }\n\n this.sql = new MySQLBuilder(this.ctx.model)\n }\n\n async start() {\n this.pool = createPool(this.config)\n\n for (const name in this.ctx.model.config) {\n this._tableTasks[name] = this._syncTable(name)\n }\n\n this.ctx.on('model', (name) => {\n this._tableTasks[name] = this._syncTable(name)\n })\n }\n\n stop() {\n this.pool.end()\n }\n\n private _getColDefs(name: string, columns: string[]) {\n const table = this.ctx.model.config[name]\n const { primary, foreign, autoInc } = table\n const fields = { ...table.fields }\n const unique = [...table.unique]\n const result: string[] = []\n\n // orm definitions\n for (const key in fields) {\n if (columns.includes(key)) continue\n const { initial, nullable = true } = fields[key]\n let def = escapeId(key)\n if (key === primary && autoInc) {\n def += ' int unsigned not null auto_increment'\n } else {\n const typedef = getTypeDefinition(fields[key])\n def += ' ' + typedef\n if (makeArray(primary).includes(key)) {\n def += ' not null'\n } else {\n def += (nullable ? ' ' : ' not ') + 'null'\n }\n // blob, text, geometry or json columns cannot have default values\n if (initial && !typedef.startsWith('text')) {\n def += ' default ' + this.sql.escape(initial, name, key)\n }\n }\n result.push(def)\n }\n\n if (!columns.length) {\n result.push(`primary key (${createIndex(primary)})`)\n for (const key of unique) {\n result.push(`unique index (${createIndex(key)})`)\n }\n for (const key in foreign) {\n const [table, key2] = foreign[key]\n result.push(`foreign key (${escapeId(key)}) references ${escapeId(table)} (${escapeId(key2)})`)\n }\n }\n\n return result\n }\n\n /** synchronize table schema */\n private async _syncTable(name: string) {\n await this._tableTasks[name]\n // eslint-disable-next-line max-len\n const data = await this.queue<any[]>('SELECT COLUMN_NAME from information_schema.columns WHERE TABLE_SCHEMA = ? && TABLE_NAME = ?', [this.config.database, name])\n const columns = data.map(row => row.COLUMN_NAME)\n const result = this._getColDefs(name, columns)\n if (!columns.length) {\n logger.info('auto creating table %c', name)\n await this.queue(`CREATE TABLE ?? (${result.join(',')}) COLLATE = ?`, [name, this.config.charset])\n } else if (result.length) {\n logger.info('auto updating table %c', name)\n await this.queue(`ALTER TABLE ?? ${result.map(def => 'ADD ' + def).join(',')}`, [name])\n }\n }\n\n _inferFields<T extends TableType>(table: T, keys: readonly string[]) {\n if (!keys) return\n const types = MysqlDatabase.tables[table] || {}\n return keys.map((key) => {\n const type = types[key]\n return typeof type === 'function' ? `${type()} AS ${key}` : key\n }) as (keyof Tables[T])[]\n }\n\n _createFilter(name: TableType, query: Query) {\n return this.sql.parseQuery(this.ctx.model.resolveQuery(name, query))\n }\n\n _joinKeys = (keys: readonly string[]) => {\n return keys ? keys.map(key => key.includes('`') ? key : `\\`${key}\\``).join(',') : '*'\n }\n\n _formatValues = (table: string, data: object, keys: readonly string[]) => {\n return keys.map((key) => this.sql.stringify(data[key], table as never, key))\n }\n\n query<T = any>(sql: string, values?: any): Promise<T> {\n const error = new Error()\n return new Promise((resolve, reject) => {\n sql = format(sql, values)\n logger.debug('[sql]', sql)\n this.pool.query(sql, (err: Error, results) => {\n if (!err) return resolve(results)\n logger.warn(sql)\n if (err['code'] === 'ER_DUP_ENTRY') {\n err = new KoishiError(err.message, 'database.duplicate-entry')\n }\n err.stack = err.message + error.stack.slice(5)\n reject(err)\n })\n })\n }\n\n queue<T = any>(sql: string, values?: any): Promise<T> {\n if (!this.config.multipleStatements) {\n return this.query(sql, values)\n }\n\n sql = format(sql, values)\n return new Promise<any>((resolve, reject) => {\n this._queryTasks.push({ sql, resolve, reject })\n process.nextTick(() => this._flushTasks())\n })\n }\n\n private async _flushTasks() {\n const tasks = this._queryTasks\n if (!tasks.length) return\n this._queryTasks = []\n\n try {\n let results = await this.query(tasks.map(task => task.sql).join('; '))\n if (tasks.length === 1) results = [results]\n tasks.forEach((task, index) => {\n task.resolve(results[index])\n })\n } catch (error) {\n tasks.forEach(task => task.reject(error))\n }\n }\n\n select<T extends {}>(table: string, fields: readonly (string & keyof T)[], conditional?: string, values?: readonly any[]): Promise<T[]>\n select(table: string, fields: string[], conditional?: string, values: readonly any[] = []) {\n logger.debug(`[select] ${table}: ${fields ? fields.join(', ') : '*'}`)\n const sql = 'SELECT '\n + this._joinKeys(fields)\n + (table.includes('.') ? `FROM ${table}` : ' FROM `' + table + `\\` _${table}`)\n + (conditional ? ' WHERE ' + conditional : '')\n return this.queue(sql, values)\n }\n\n async drop() {\n const data = await this.select('information_schema.tables', ['TABLE_NAME'], 'TABLE_SCHEMA = ?', [this.config.database])\n if (!data.length) return\n await this.query(data.map(({ TABLE_NAME }) => `DROP TABLE ${this.sql.escapeId(TABLE_NAME)}`).join('; '))\n }\n\n async stats() {\n const data = await this.select('information_schema.tables', ['TABLE_NAME', 'TABLE_ROWS', 'DATA_LENGTH'], 'TABLE_SCHEMA = ?', [this.config.database])\n const stats: Query.Stats = { size: 0 }\n stats.tables = Object.fromEntries(data.map(({ TABLE_NAME: name, TABLE_ROWS: count, DATA_LENGTH: size }) => {\n stats.size += size\n return [name, { count, size }]\n }))\n return stats\n }\n\n async get(name: TableType, query: Query, modifier?: Query.Modifier) {\n await this._tableTasks[name]\n const filter = this._createFilter(name, query)\n if (filter === '0') return []\n const { fields, limit, offset, sort } = Query.resolveModifier(modifier)\n const keys = this._joinKeys(this._inferFields(name, fields))\n let sql = `SELECT ${keys} FROM ${name} _${name} WHERE ${filter}`\n if (sort) sql += ' ORDER BY ' + Object.entries(sort).map(([key, order]) => `${this.sql.escapeId(key)} ${order}`).join(', ')\n if (limit) sql += ' LIMIT ' + limit\n if (offset) sql += ' OFFSET ' + offset\n return this.queue(sql)\n }\n\n async set(name: TableType, query: Query, data: {}) {\n await this._tableTasks[name]\n const filter = this._createFilter(name, query)\n if (filter === '0') return\n const keys = Object.keys(data)\n const update = keys.map((key) => {\n const valueExpr = this.sql.parseEval(data[key], name, key)\n const [field, ...rest] = key.split('.')\n const keyExpr = this.sql.escapeId(field)\n if (!rest.length) return `${keyExpr} = ${valueExpr}`\n return `${keyExpr} = json_set(ifnull(${keyExpr}, '{}'), '$${rest.map(key => `.\"${key}\"`).join('')}', ${valueExpr})`\n }).join(', ')\n await this.query(`UPDATE ${name} SET ${update} WHERE ${filter}`)\n }\n\n async remove(name: TableType, query: Query) {\n await this._tableTasks[name]\n const filter = this._createFilter(name, query)\n if (filter === '0') return\n await this.query('DELETE FROM ?? WHERE ' + filter, [name])\n }\n\n async create(name: TableType, data: {}) {\n await this._tableTasks[name]\n data = { ...this.ctx.model.create(name), ...data }\n const keys = Object.keys(data)\n const header = await this.query<OkPacket>(\n `INSERT INTO ?? (${this._joinKeys(keys)}) VALUES (${keys.map(() => '?').join(', ')})`,\n [name, ...this._formatValues(name, data, keys)],\n )\n return { ...data, id: header.insertId } as any\n }\n\n async upsert(name: TableType, data: any[], keys: string | string[]) {\n if (!data.length) return\n await this._tableTasks[name]\n\n const { fields, primary } = this.ctx.model.config[name]\n const merged = {}\n const insertion = data.map((item) => {\n Object.assign(merged, item)\n return executeUpdate(this.ctx.model.create(name), item)\n })\n const indexFields = makeArray(keys || primary)\n const dataFields = [...new Set(Object.keys(merged).map(key => key.split('.', 1)[0]))]\n const updateFields = difference(dataFields, indexFields)\n\n const createFilter = (item: any) => this.sql.parseQuery(pick(item, indexFields))\n const createMultiFilter = (items: any[]) => {\n if (items.length === 1) {\n return createFilter(items[0])\n } else if (indexFields.length === 1) {\n const key = indexFields[0]\n return this.sql.parseQuery({ [key]: items.map(item => item[key]) })\n } else {\n return items.map(createFilter).join(' OR ')\n }\n }\n\n const update = updateFields.map((field) => {\n const escaped = this.sql.escapeId(field)\n const branches: Dict<string> = {}\n const absent = data.filter((item) => {\n // update directly\n if (field in item) {\n if (Object.keys(item[field]).some(key => key.startsWith('$'))) {\n branches[createFilter(item)] = this.sql.parseEval(item[field], name, field)\n }\n return\n }\n\n // update with json_set\n const valueInit = `ifnull(${escaped}, '{}')`\n let value = valueInit\n for (const key in item) {\n const [first, ...rest] = key.split('.')\n if (first !== field) continue\n value = `json_set(${value}, '$${rest.map(key => `.\"${key}\"`).join('')}', ${this.sql.parseEval(item[key])})`\n }\n if (value === valueInit) return true\n branches[createFilter(item)] = value\n })\n\n if (absent.length) branches[createMultiFilter(absent)] = escaped\n let value = `VALUES(${escaped})`\n for (const condition in branches) {\n value = `if(${condition}, ${branches[condition]}, ${value})`\n }\n return `${escaped} = ${value}`\n }).join(', ')\n\n const initFields = Object.keys(fields)\n const placeholder = `(${initFields.map(() => '?').join(', ')})`\n await this.query(\n `INSERT INTO ${this.sql.escapeId(name)} (${this._joinKeys(initFields)}) VALUES ${data.map(() => placeholder).join(', ')}\n ON DUPLICATE KEY UPDATE ${update}`,\n [].concat(...insertion.map(item => this._formatValues(name, item, initFields))),\n )\n }\n\n async eval(name: TableType, expr: any, query: Query) {\n await this._tableTasks[name]\n const filter = this._createFilter(name, query)\n const output = this.sql.parseEval(expr)\n const [data] = await this.queue(`SELECT ${output} AS value FROM ${name} WHERE ${filter}`)\n return data.value\n }\n}\n\nnamespace MysqlDatabase {\n export interface Config extends PoolConfig {}\n\n export const Config = Schema.object({\n host: Schema.string().description('要连接到的主机名。').default('localhost'),\n port: Schema.natural().max(65535).description('要连接到的端口号。').default(3306),\n user: Schema.string().description('要使用的用户名。').default('root'),\n password: Schema.string().description('要使用的密码。').role('secret'),\n database: Schema.string().description('要访问的数据库名。').default('koishi'),\n })\n\n type Declarations = {\n [T in TableType]?: {\n [K in keyof Tables[T]]?: () => string\n }\n }\n\n /**\n * @deprecated use `import('koishi').Field` instead\n */\n export const tables: Declarations = {\n user: {},\n channel: {},\n }\n}\n\nexport default MysqlDatabase\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koishijs/plugin-database-mysql",
|
|
3
3
|
"description": "MySQL support for Koishi",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.1.3",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -28,19 +28,20 @@
|
|
|
28
28
|
"chatbot",
|
|
29
29
|
"koishi",
|
|
30
30
|
"database",
|
|
31
|
-
"mysql"
|
|
31
|
+
"mysql",
|
|
32
|
+
"impl:database"
|
|
32
33
|
],
|
|
33
34
|
"devDependencies": {
|
|
34
|
-
"@koishijs/
|
|
35
|
-
"@koishijs/
|
|
35
|
+
"@koishijs/database-tests": "^1.0.0",
|
|
36
|
+
"@koishijs/plugin-mock": "^1.0.2"
|
|
36
37
|
},
|
|
37
38
|
"peerDependencies": {
|
|
38
|
-
"koishi": "^4.
|
|
39
|
+
"koishi": "^4.2.2"
|
|
39
40
|
},
|
|
40
41
|
"dependencies": {
|
|
41
42
|
"@koishijs/orm-utils": "^1.0.0",
|
|
42
43
|
"@koishijs/sql-utils": "^1.0.0",
|
|
43
|
-
"@types/mysql": "^2.15.
|
|
44
|
-
"mysql": "^2.18.
|
|
44
|
+
"@types/mysql": "^2.15.20",
|
|
45
|
+
"@vlasky/mysql": "^2.18.5"
|
|
45
46
|
}
|
|
46
47
|
}
|