@koishijs/plugin-database-mysql 4.0.0-beta.3 → 4.0.0-beta.7
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 +32 -49
- package/lib/index.js +191 -186
- package/lib/index.js.map +2 -2
- package/package.json +6 -5
package/lib/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
import { Pool, PoolConfig,
|
|
3
|
-
import { Context, Database, Schema } from 'koishi';
|
|
4
|
-
import {
|
|
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
|
-
|
|
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:
|
|
27
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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]]?:
|
|
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
|
-
|
|
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 `
|
|
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.
|
|
120
|
-
this.
|
|
127
|
+
this.tasks = {};
|
|
128
|
+
this._joinKeys = (keys) => {
|
|
121
129
|
return keys ? keys.map((key) => key.includes("`") ? key : `\`${key}\``).join(",") : "*";
|
|
122
130
|
};
|
|
123
|
-
this
|
|
124
|
-
return
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
178
|
+
const types = MysqlDatabase.tables[table] || {};
|
|
175
179
|
return keys.map((key) => {
|
|
176
|
-
const type =
|
|
180
|
+
const type = types[key];
|
|
177
181
|
return typeof type === "function" ? `${type()} AS ${key}` : key;
|
|
178
182
|
});
|
|
179
183
|
}
|
|
180
|
-
|
|
181
|
-
const table =
|
|
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
|
|
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 (
|
|
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
|
|
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
|
-
|
|
216
|
+
result.push(def);
|
|
214
217
|
}
|
|
215
|
-
if (!
|
|
216
|
-
|
|
218
|
+
if (!columns.length) {
|
|
219
|
+
result.push(`primary key (${createIndex(primary)})`);
|
|
217
220
|
for (const key of unique) {
|
|
218
|
-
|
|
221
|
+
result.push(`unique index (${createIndex(key)})`);
|
|
219
222
|
}
|
|
220
223
|
for (const key in foreign) {
|
|
221
224
|
const [table2, key2] = foreign[key];
|
|
222
|
-
|
|
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
|
|
228
|
+
return result;
|
|
226
229
|
}
|
|
227
|
-
async
|
|
228
|
-
|
|
229
|
-
this.
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
401
|
-
const
|
|
402
|
-
const
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
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
|
-
|
|
412
|
-
|
|
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.
|
|
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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA,
|
|
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
|
+
"version": "4.0.0-beta.7",
|
|
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.
|
|
35
|
-
"@koishijs/test-utils": "^8.0.0-beta.
|
|
34
|
+
"@koishijs/plugin-mock": "^1.0.0-beta.2",
|
|
35
|
+
"@koishijs/test-utils": "^8.0.0-beta.6"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"koishi": "^4.0.0-beta.
|
|
38
|
+
"koishi": "^4.0.0-beta.7"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@koishijs/
|
|
41
|
+
"@koishijs/orm-utils": "^1.0.0-beta.5",
|
|
42
|
+
"@koishijs/sql-utils": "^1.0.0-beta.5",
|
|
42
43
|
"@types/mysql": "^2.15.19",
|
|
43
44
|
"mysql": "^2.18.1"
|
|
44
45
|
}
|