@primate/mysql 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/private/MySQL.d.ts +17 -5
- package/lib/private/MySQL.js +56 -8
- package/package.json +14 -10
package/lib/private/MySQL.d.ts
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
1
|
-
import type { As, DataDict,
|
|
1
|
+
import type { As, DataDict, ExtractPlaceholders, Schema, Sort, SQLDB, With, CheckPlaceholders } from "@primate/core/db";
|
|
2
2
|
import type { Dict } from "@rcompat/type";
|
|
3
|
-
|
|
3
|
+
import type { Pool } from "mysql2/promise";
|
|
4
|
+
import type { ObjectType, Parsed } from "pema";
|
|
5
|
+
declare const schema: ObjectType<import("pema").NormalizeSchemaObject<{
|
|
4
6
|
readonly host: import("pema").DefaultType<import("pema").StringType, "localhost">;
|
|
5
|
-
readonly port: import("pema").DefaultType<import("pema").UintType<"u32">, 3306>;
|
|
7
|
+
readonly port: import("pema").DefaultType<import("pema").UintType<"u32", undefined>, 3306>;
|
|
6
8
|
readonly database: import("pema").StringType;
|
|
7
9
|
readonly password: import("pema").OptionalType<import("pema").StringType>;
|
|
8
10
|
readonly username: import("pema").OptionalType<import("pema").StringType>;
|
|
9
|
-
|
|
11
|
+
readonly jsonStrings: import("pema").DefaultType<import("pema").BooleanType<undefined>, true>;
|
|
12
|
+
}>, undefined, {
|
|
10
13
|
host: string;
|
|
11
14
|
port: number;
|
|
12
15
|
database: string;
|
|
13
16
|
password: string | undefined;
|
|
14
17
|
username: string | undefined;
|
|
18
|
+
jsonStrings: boolean;
|
|
15
19
|
}>;
|
|
16
|
-
export default class MySQL implements
|
|
20
|
+
export default class MySQL implements SQLDB<Pool> {
|
|
17
21
|
#private;
|
|
18
22
|
static config: typeof schema.input;
|
|
19
23
|
constructor(config?: typeof schema.input, options?: {
|
|
20
24
|
debug?: boolean;
|
|
21
25
|
});
|
|
26
|
+
get client(): Pool;
|
|
22
27
|
get explain(): Dict<{
|
|
23
28
|
query: string;
|
|
24
29
|
plans: string[];
|
|
@@ -45,6 +50,13 @@ export default class MySQL implements DB {
|
|
|
45
50
|
delete(as: As, args: {
|
|
46
51
|
where: DataDict;
|
|
47
52
|
}): Promise<number>;
|
|
53
|
+
sql<Q extends string, I extends ObjectType<{
|
|
54
|
+
[K in ExtractPlaceholders<Q>]: Parsed<unknown>;
|
|
55
|
+
}> | undefined, O extends Parsed<unknown> | undefined>(options: {
|
|
56
|
+
input?: I extends ObjectType<any> ? CheckPlaceholders<I, Q> : I;
|
|
57
|
+
query: Q;
|
|
58
|
+
output?: O;
|
|
59
|
+
}): (args: I extends ObjectType<any> ? I["infer"] : void) => Promise<O extends Parsed<unknown> ? O["infer"] : void>;
|
|
48
60
|
}
|
|
49
61
|
export {};
|
|
50
62
|
//# sourceMappingURL=MySQL.d.ts.map
|
package/lib/private/MySQL.js
CHANGED
|
@@ -15,14 +15,14 @@ function is_bigint_key(k) {
|
|
|
15
15
|
return k === "u64" || k === "u128" || k === "i64" || k === "i128";
|
|
16
16
|
}
|
|
17
17
|
function order_by(types, sort, alias) {
|
|
18
|
-
if (sort
|
|
18
|
+
if (is.undefined(sort))
|
|
19
19
|
return "";
|
|
20
20
|
const entries = Object.entries(sort);
|
|
21
21
|
if (entries.length === 0)
|
|
22
22
|
return "";
|
|
23
23
|
const parts = entries.map(([k, dir]) => {
|
|
24
24
|
const quoted = sql.quote(k);
|
|
25
|
-
const base = alias
|
|
25
|
+
const base = is.defined(alias) ? `${alias}.${quoted}` : quoted;
|
|
26
26
|
const expression = is_bigint_key(types[k]) ? bigint_cast(base) : base;
|
|
27
27
|
return `${expression} ${dir.toLowerCase() === "desc" ? "DESC" : "ASC"}`;
|
|
28
28
|
});
|
|
@@ -78,6 +78,7 @@ const schema = p({
|
|
|
78
78
|
database: p.string,
|
|
79
79
|
password: p.string.optional(),
|
|
80
80
|
username: p.string.optional(),
|
|
81
|
+
jsonStrings: p.boolean.default(true),
|
|
81
82
|
});
|
|
82
83
|
export default class MySQL {
|
|
83
84
|
static config;
|
|
@@ -96,12 +97,16 @@ export default class MySQL {
|
|
|
96
97
|
namedPlaceholders: true,
|
|
97
98
|
bigNumberStrings: true,
|
|
98
99
|
supportBigNumbers: true,
|
|
100
|
+
jsonStrings: parsed.jsonStrings,
|
|
99
101
|
});
|
|
100
102
|
this.#debug = options?.debug ?? false;
|
|
101
103
|
}
|
|
102
104
|
get #db() {
|
|
103
105
|
return this.#client ??= this.#factory();
|
|
104
106
|
}
|
|
107
|
+
get client() {
|
|
108
|
+
return this.#db;
|
|
109
|
+
}
|
|
105
110
|
async #sql(query, params) {
|
|
106
111
|
const [rows] = await this.#db.query(query, params);
|
|
107
112
|
return rows;
|
|
@@ -208,7 +213,9 @@ export default class MySQL {
|
|
|
208
213
|
const numeric = is_bigint_key(datatype);
|
|
209
214
|
const lhs = numeric ? bigint_cast(quoted) : quoted;
|
|
210
215
|
const rhs = numeric ? bigint_cast(ph) : ph;
|
|
211
|
-
|
|
216
|
+
if (op !== "$in") {
|
|
217
|
+
binds[bind_key] = await bind_value(datatype, op_value);
|
|
218
|
+
}
|
|
212
219
|
switch (op) {
|
|
213
220
|
case "$like":
|
|
214
221
|
parts.push(`${quoted} LIKE BINARY ${ph}`);
|
|
@@ -229,6 +236,19 @@ export default class MySQL {
|
|
|
229
236
|
parts.push(`${lhs} ${sql_op} ${rhs}`);
|
|
230
237
|
break;
|
|
231
238
|
}
|
|
239
|
+
case "$in": {
|
|
240
|
+
const array = op_value;
|
|
241
|
+
const placeholders = [];
|
|
242
|
+
for (let i = 0; i < array.length; i++) {
|
|
243
|
+
const key = `${sql.bindKey(field, `in${i}`)}`;
|
|
244
|
+
binds[key] = await bind_value(datatype, array[i]);
|
|
245
|
+
const ph = `${BIND_BY}${key}`;
|
|
246
|
+
placeholders.push(is_bigint_key(datatype) ? bigint_cast(ph) : ph);
|
|
247
|
+
}
|
|
248
|
+
const lhs = is_bigint_key(datatype) ? bigint_cast(quoted) : quoted;
|
|
249
|
+
parts.push(`${lhs} IN (${placeholders.join(", ")})`);
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
232
252
|
default:
|
|
233
253
|
throw E.operator_unknown(field, op);
|
|
234
254
|
}
|
|
@@ -340,15 +360,16 @@ export default class MySQL {
|
|
|
340
360
|
return Number(rows[0]?.n ?? 0);
|
|
341
361
|
}
|
|
342
362
|
async #base_query(as, args) {
|
|
343
|
-
const SELECT = args.fields
|
|
363
|
+
const SELECT = is.undefined(args.fields)
|
|
344
364
|
? "*"
|
|
345
365
|
: args.fields.map(sql.quote).join(", ");
|
|
346
366
|
const [WHERE, binds] = await this.#where(as, args.where);
|
|
347
367
|
const ORDER_BY = order_by(as.types, args.sort);
|
|
348
|
-
const LIMIT =
|
|
368
|
+
const LIMIT = is.defined(args.limit) ? ` LIMIT ${args.limit}` : "";
|
|
369
|
+
const OFFSET = is.defined(args.offset) ? ` OFFSET ${args.offset}` : "";
|
|
349
370
|
const table = sql.quote(as.table);
|
|
350
371
|
return [
|
|
351
|
-
`SELECT ${SELECT} FROM ${table} ${WHERE}${ORDER_BY}${LIMIT}`,
|
|
372
|
+
`SELECT ${SELECT} FROM ${table} ${WHERE}${ORDER_BY}${LIMIT}${OFFSET}`,
|
|
352
373
|
binds,
|
|
353
374
|
];
|
|
354
375
|
}
|
|
@@ -424,7 +445,7 @@ export default class MySQL {
|
|
|
424
445
|
: [in_part];
|
|
425
446
|
const WHERE = `WHERE ${where_parts.join(" AND ")}`;
|
|
426
447
|
const all_columns = Object.keys(args.as.types);
|
|
427
|
-
const fields = args.fields
|
|
448
|
+
const fields = is.defined(args.fields) && is.uint(args.fields.length)
|
|
428
449
|
? args.fields
|
|
429
450
|
: all_columns;
|
|
430
451
|
const select_fields = [...new Set([...fields, args.by])];
|
|
@@ -437,7 +458,7 @@ export default class MySQL {
|
|
|
437
458
|
: ` ORDER BY ${base_order}`;
|
|
438
459
|
const table = sql.quote(args.as.table);
|
|
439
460
|
let query;
|
|
440
|
-
if (per_parent
|
|
461
|
+
if (is.defined(per_parent)) {
|
|
441
462
|
const partition = numeric ? cast : Q `${args.by}`;
|
|
442
463
|
const rn_order = user_order ? ` ORDER BY ${user_order}` : "";
|
|
443
464
|
query = `
|
|
@@ -524,5 +545,32 @@ export default class MySQL {
|
|
|
524
545
|
const result = await this.#execute(query, binds);
|
|
525
546
|
return result.affectedRows;
|
|
526
547
|
}
|
|
548
|
+
sql(options) {
|
|
549
|
+
if (is.defined(options.input)) {
|
|
550
|
+
const placeholders = new Set([...options.query.matchAll(/:(\w+)/g)].map(m => m[1]));
|
|
551
|
+
const input_keys = new Set(Object.keys(options.input.properties));
|
|
552
|
+
for (const key of input_keys) {
|
|
553
|
+
if (!placeholders.has(key))
|
|
554
|
+
throw E.sql_placeholder_missing(key);
|
|
555
|
+
}
|
|
556
|
+
for (const key of placeholders) {
|
|
557
|
+
if (!input_keys.has(key))
|
|
558
|
+
throw E.sql_input_missing(key);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
return async (args) => {
|
|
562
|
+
const input = is.defined(options.input)
|
|
563
|
+
? options.input.parse(args)
|
|
564
|
+
: undefined;
|
|
565
|
+
const binds = is.defined(input)
|
|
566
|
+
? await this.#create_params(Object.fromEntries(Object.entries(options.input.properties)
|
|
567
|
+
.map(([k, v]) => [k, v.datatype])), input)
|
|
568
|
+
: {};
|
|
569
|
+
const rows = await this.#sql(options.query, binds);
|
|
570
|
+
return (is.defined(options.output)
|
|
571
|
+
? options.output.parse(rows)
|
|
572
|
+
: undefined);
|
|
573
|
+
};
|
|
574
|
+
}
|
|
527
575
|
}
|
|
528
576
|
//# sourceMappingURL=MySQL.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primate/mysql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "MySQL databases for Primate",
|
|
5
5
|
"homepage": "https://primate.run/docs/database/mysql",
|
|
6
6
|
"bugs": "https://github.com/primate-run/primate/issues",
|
|
@@ -17,17 +17,17 @@
|
|
|
17
17
|
"!/**/*.spec.*"
|
|
18
18
|
],
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@rcompat/type": "^0.
|
|
20
|
+
"@rcompat/type": "^0.17.0"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@rcompat/assert": "^0.
|
|
24
|
-
"@rcompat/is": "^0.
|
|
25
|
-
"mysql2": "^3.
|
|
26
|
-
"@primate/core": "^0.
|
|
27
|
-
"pema": "0.
|
|
23
|
+
"@rcompat/assert": "^0.13.0",
|
|
24
|
+
"@rcompat/is": "^0.11.0",
|
|
25
|
+
"mysql2": "^3.22.4",
|
|
26
|
+
"@primate/core": "^0.8.0",
|
|
27
|
+
"pema": "0.8.0"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"primate": "^0.
|
|
30
|
+
"primate": "^0.39.0"
|
|
31
31
|
},
|
|
32
32
|
"imports": {
|
|
33
33
|
"#*": {
|
|
@@ -42,9 +42,13 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
|
-
"build": "npm run clean &&
|
|
45
|
+
"build": "npm run clean && tsgo",
|
|
46
46
|
"clean": "rm -rf ./lib",
|
|
47
47
|
"lint": "eslint .",
|
|
48
|
-
"
|
|
48
|
+
"mysql": "scripts/mysql.sh",
|
|
49
|
+
"mariadb": "scripts/mariadb.sh",
|
|
50
|
+
"test:mysql": "npm run mysql && npx proby",
|
|
51
|
+
"test:mariadb": "npm run mariadb && npx proby",
|
|
52
|
+
"test": "npm run test:mysql"
|
|
49
53
|
}
|
|
50
54
|
}
|