@naturalcycles/mysql-lib 3.9.2 → 3.11.1
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/dist/index.d.ts +2 -1
- package/dist/index.js +3 -1
- package/dist/mysql.db.d.ts +7 -2
- package/dist/mysql.db.js +14 -1
- package/dist/mysqlKeyValueDB.d.ts +25 -0
- package/dist/mysqlKeyValueDB.js +125 -0
- package/dist/query.util.js +17 -9
- package/package.json +2 -2
- package/src/index.ts +2 -1
- package/src/mysql.db.ts +21 -2
- package/src/mysqlKeyValueDB.ts +148 -0
- package/src/query.util.ts +23 -8
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getDBAdapter } from './dbAdapter';
|
|
2
2
|
import { MysqlDB, MysqlDBCfg, MysqlDBOptions, MysqlDBSaveOptions } from './mysql.db';
|
|
3
|
+
import { MySQLKeyValueDB } from './mysqlKeyValueDB';
|
|
3
4
|
import { jsonSchemaToMySQLDDL } from './schema/mysql.schema.util';
|
|
4
5
|
export type { MysqlDBCfg, MysqlDBOptions, MysqlDBSaveOptions };
|
|
5
|
-
export { MysqlDB, jsonSchemaToMySQLDDL, getDBAdapter };
|
|
6
|
+
export { MysqlDB, MySQLKeyValueDB, jsonSchemaToMySQLDDL, getDBAdapter };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getDBAdapter = exports.jsonSchemaToMySQLDDL = exports.MysqlDB = void 0;
|
|
3
|
+
exports.getDBAdapter = exports.jsonSchemaToMySQLDDL = exports.MySQLKeyValueDB = exports.MysqlDB = void 0;
|
|
4
4
|
const dbAdapter_1 = require("./dbAdapter");
|
|
5
5
|
Object.defineProperty(exports, "getDBAdapter", { enumerable: true, get: function () { return dbAdapter_1.getDBAdapter; } });
|
|
6
6
|
const mysql_db_1 = require("./mysql.db");
|
|
7
7
|
Object.defineProperty(exports, "MysqlDB", { enumerable: true, get: function () { return mysql_db_1.MysqlDB; } });
|
|
8
|
+
const mysqlKeyValueDB_1 = require("./mysqlKeyValueDB");
|
|
9
|
+
Object.defineProperty(exports, "MySQLKeyValueDB", { enumerable: true, get: function () { return mysqlKeyValueDB_1.MySQLKeyValueDB; } });
|
|
8
10
|
const mysql_schema_util_1 = require("./schema/mysql.schema.util");
|
|
9
11
|
Object.defineProperty(exports, "jsonSchemaToMySQLDDL", { enumerable: true, get: function () { return mysql_schema_util_1.jsonSchemaToMySQLDDL; } });
|
package/dist/mysql.db.d.ts
CHANGED
|
@@ -40,16 +40,21 @@ export declare class MysqlDB extends BaseCommonDB implements CommonDB {
|
|
|
40
40
|
* Be careful to manage this connection yourself (open, release, etc).
|
|
41
41
|
*/
|
|
42
42
|
createSingleConnection(): Promise<Connection>;
|
|
43
|
-
getByIds<ROW extends ObjectWithId>(table: string, ids:
|
|
43
|
+
getByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][], opt?: MysqlDBOptions): Promise<ROW[]>;
|
|
44
44
|
runQuery<ROW extends ObjectWithId, OUT = ROW>(q: DBQuery<ROW>, _opt?: MysqlDBOptions): Promise<RunQueryResult<OUT>>;
|
|
45
45
|
runSQL<RESULT>(q: QueryOptions): Promise<RESULT>;
|
|
46
|
+
/**
|
|
47
|
+
* Allows to run semicolon-separated "SQL file".
|
|
48
|
+
* E.g "ddl reset script".
|
|
49
|
+
*/
|
|
50
|
+
runSQLString(s: string): Promise<void>;
|
|
46
51
|
runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
|
|
47
52
|
streamQuery<ROW extends ObjectWithId, OUT = ROW>(q: DBQuery<ROW>, _opt?: MysqlDBOptions): ReadableTyped<OUT>;
|
|
48
53
|
saveBatch<ROW extends ObjectWithId>(table: string, _rows: ROW[], _opt?: MysqlDBSaveOptions<ROW>): Promise<void>;
|
|
49
54
|
/**
|
|
50
55
|
* Limitation: always returns [], regardless of which rows are actually deleted
|
|
51
56
|
*/
|
|
52
|
-
deleteByIds(table: string, ids:
|
|
57
|
+
deleteByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][], _opt?: MysqlDBOptions): Promise<number>;
|
|
53
58
|
deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
|
|
54
59
|
/**
|
|
55
60
|
* Use with caution!
|
package/dist/mysql.db.js
CHANGED
|
@@ -138,6 +138,19 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
|
|
|
138
138
|
});
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Allows to run semicolon-separated "SQL file".
|
|
143
|
+
* E.g "ddl reset script".
|
|
144
|
+
*/
|
|
145
|
+
async runSQLString(s) {
|
|
146
|
+
const queries = s
|
|
147
|
+
.split(';')
|
|
148
|
+
.map(s => s.trim())
|
|
149
|
+
.filter(Boolean);
|
|
150
|
+
for await (const sql of queries) {
|
|
151
|
+
await this.runSQL({ sql });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
141
154
|
async runQueryCount(q, _opt) {
|
|
142
155
|
const { rows } = await this.runQuery(q.select(['count(*) as _count']));
|
|
143
156
|
return rows[0]._count;
|
|
@@ -214,7 +227,7 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
|
|
|
214
227
|
return (0, mysql_schema_util_1.mysqlTableStatsToJsonSchemaField)(table, stats, this.cfg.logger);
|
|
215
228
|
}
|
|
216
229
|
}
|
|
217
|
-
|
|
230
|
+
tslib_1.__decorate([
|
|
218
231
|
(0, js_lib_1._Memo)()
|
|
219
232
|
], MysqlDB.prototype, "pool", null);
|
|
220
233
|
exports.MysqlDB = MysqlDB;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { CommonDBCreateOptions, CommonKeyValueDB, KeyValueDBTuple } from '@naturalcycles/db-lib';
|
|
3
|
+
import { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
4
|
+
import { MysqlDB, MysqlDBCfg } from './mysql.db';
|
|
5
|
+
export declare class MySQLKeyValueDB implements CommonKeyValueDB {
|
|
6
|
+
cfg: MysqlDBCfg;
|
|
7
|
+
constructor(cfg: MysqlDBCfg);
|
|
8
|
+
db: MysqlDB;
|
|
9
|
+
ping(): Promise<void>;
|
|
10
|
+
close(): Promise<void>;
|
|
11
|
+
createTable(table: string, opt?: CommonDBCreateOptions): Promise<void>;
|
|
12
|
+
getByIds(table: string, ids: string[]): Promise<KeyValueDBTuple[]>;
|
|
13
|
+
/**
|
|
14
|
+
* Use with caution!
|
|
15
|
+
*/
|
|
16
|
+
dropTable(table: string): Promise<void>;
|
|
17
|
+
deleteByIds(table: string, ids: string[]): Promise<void>;
|
|
18
|
+
saveBatch(table: string, entries: KeyValueDBTuple[]): Promise<void>;
|
|
19
|
+
streamIds(table: string, limit?: number): ReadableTyped<string>;
|
|
20
|
+
streamValues(table: string, limit?: number): ReadableTyped<Buffer>;
|
|
21
|
+
streamEntries(table: string, limit?: number): ReadableTyped<KeyValueDBTuple>;
|
|
22
|
+
beginTransaction(): Promise<void>;
|
|
23
|
+
endTransaction(): Promise<void>;
|
|
24
|
+
count(table: string): Promise<number>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MySQLKeyValueDB = void 0;
|
|
4
|
+
const stream_1 = require("stream");
|
|
5
|
+
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
6
|
+
const mysql_db_1 = require("./mysql.db");
|
|
7
|
+
class MySQLKeyValueDB {
|
|
8
|
+
constructor(cfg) {
|
|
9
|
+
this.cfg = cfg;
|
|
10
|
+
this.db = new mysql_db_1.MysqlDB(this.cfg);
|
|
11
|
+
}
|
|
12
|
+
async ping() {
|
|
13
|
+
await this.db.ping();
|
|
14
|
+
}
|
|
15
|
+
async close() {
|
|
16
|
+
await this.db.close();
|
|
17
|
+
}
|
|
18
|
+
async createTable(table, opt = {}) {
|
|
19
|
+
if (opt.dropIfExists)
|
|
20
|
+
await this.dropTable(table);
|
|
21
|
+
// On blob sizes: https://tableplus.com/blog/2019/10/tinyblob-blob-mediumblob-longblob.html
|
|
22
|
+
// LONGBLOB supports up to 4gb
|
|
23
|
+
// MEDIUMBLOB up to 16Mb
|
|
24
|
+
const sql = `create table ${table} (id VARCHAR(64) PRIMARY KEY, v LONGBLOB NOT NULL)`;
|
|
25
|
+
this.db.cfg.logger.log(sql);
|
|
26
|
+
await this.db.runSQL({ sql });
|
|
27
|
+
}
|
|
28
|
+
async getByIds(table, ids) {
|
|
29
|
+
if (!ids.length)
|
|
30
|
+
return [];
|
|
31
|
+
const sql = `SELECT id,v FROM ${table} where id in (${ids.map(id => `"${id}"`).join(',')})`;
|
|
32
|
+
const rows = await this.db.runSQL({ sql });
|
|
33
|
+
return rows.map(({ id, v }) => [id, v]);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Use with caution!
|
|
37
|
+
*/
|
|
38
|
+
async dropTable(table) {
|
|
39
|
+
await this.db.runSQL({ sql: `DROP TABLE IF EXISTS ${table}` });
|
|
40
|
+
}
|
|
41
|
+
async deleteByIds(table, ids) {
|
|
42
|
+
const sql = `DELETE FROM ${table} WHERE id in (${ids.map(id => `"${id}"`).join(',')})`;
|
|
43
|
+
if (this.cfg.logSQL)
|
|
44
|
+
this.db.cfg.logger.log(sql);
|
|
45
|
+
await this.db.runSQL({ sql });
|
|
46
|
+
}
|
|
47
|
+
async saveBatch(table, entries) {
|
|
48
|
+
const statements = entries.map(([id, buf]) => {
|
|
49
|
+
return {
|
|
50
|
+
sql: `INSERT INTO ${table} (id, v) VALUES (?, ?)`,
|
|
51
|
+
values: [id, buf],
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
await (0, js_lib_1.pMap)(statements, async (statement) => {
|
|
55
|
+
if (this.cfg.debug)
|
|
56
|
+
this.db.cfg.logger.log(statement.sql);
|
|
57
|
+
await this.db.runSQL(statement);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
streamIds(table, limit) {
|
|
61
|
+
let sql = `SELECT id FROM ${table}`;
|
|
62
|
+
if (limit)
|
|
63
|
+
sql += ` LIMIT ${limit}`;
|
|
64
|
+
if (this.cfg.logSQL)
|
|
65
|
+
this.db.cfg.logger.log(`stream: ${sql}`);
|
|
66
|
+
return this.db
|
|
67
|
+
.pool()
|
|
68
|
+
.query(sql)
|
|
69
|
+
.stream()
|
|
70
|
+
.pipe(new stream_1.Transform({
|
|
71
|
+
objectMode: true,
|
|
72
|
+
transform(row, _, cb) {
|
|
73
|
+
cb(null, row.id);
|
|
74
|
+
},
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
streamValues(table, limit) {
|
|
78
|
+
let sql = `SELECT v FROM ${table}`;
|
|
79
|
+
if (limit)
|
|
80
|
+
sql += ` LIMIT ${limit}`;
|
|
81
|
+
if (this.cfg.logSQL)
|
|
82
|
+
this.db.cfg.logger.log(`stream: ${sql}`);
|
|
83
|
+
return this.db
|
|
84
|
+
.pool()
|
|
85
|
+
.query(sql)
|
|
86
|
+
.stream()
|
|
87
|
+
.pipe(new stream_1.Transform({
|
|
88
|
+
objectMode: true,
|
|
89
|
+
transform(row, _, cb) {
|
|
90
|
+
cb(null, row.v);
|
|
91
|
+
},
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
streamEntries(table, limit) {
|
|
95
|
+
let sql = `SELECT id,v FROM ${table}`;
|
|
96
|
+
if (limit)
|
|
97
|
+
sql += ` LIMIT ${limit}`;
|
|
98
|
+
if (this.cfg.logSQL)
|
|
99
|
+
this.db.cfg.logger.log(`stream: ${sql}`);
|
|
100
|
+
return this.db
|
|
101
|
+
.pool()
|
|
102
|
+
.query(sql)
|
|
103
|
+
.stream()
|
|
104
|
+
.pipe(new stream_1.Transform({
|
|
105
|
+
objectMode: true,
|
|
106
|
+
transform(row, _, cb) {
|
|
107
|
+
cb(null, [row.id, row.v]);
|
|
108
|
+
},
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
async beginTransaction() {
|
|
112
|
+
await this.db.runSQL({ sql: `BEGIN TRANSACTION` });
|
|
113
|
+
}
|
|
114
|
+
async endTransaction() {
|
|
115
|
+
await this.db.runSQL({ sql: `END TRANSACTION` });
|
|
116
|
+
}
|
|
117
|
+
async count(table) {
|
|
118
|
+
const sql = `SELECT count(*) as cnt FROM ${table}`;
|
|
119
|
+
if (this.cfg.logSQL)
|
|
120
|
+
this.db.cfg.logger.log(sql);
|
|
121
|
+
const rows = await this.db.runSQL({ sql });
|
|
122
|
+
return rows[0].cnt;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.MySQLKeyValueDB = MySQLKeyValueDB;
|
package/dist/query.util.js
CHANGED
|
@@ -12,7 +12,7 @@ function dbQueryToSQLSelect(q) {
|
|
|
12
12
|
// filters
|
|
13
13
|
tokens.push(...whereTokens(q));
|
|
14
14
|
// order
|
|
15
|
-
tokens.push(...
|
|
15
|
+
tokens.push(...groupOrderTokens(q));
|
|
16
16
|
// offset/limit
|
|
17
17
|
tokens.push(...offsetLimitTokens(q));
|
|
18
18
|
return tokens.join(' ');
|
|
@@ -124,7 +124,13 @@ function selectTokens(q) {
|
|
|
124
124
|
fields = q._selectedFieldNames.length ? q._selectedFieldNames : ['id'];
|
|
125
125
|
}
|
|
126
126
|
// We don't do `escapeId` cause it'll ruin e.g SELECT `count *` FROM ...
|
|
127
|
-
return [
|
|
127
|
+
return [
|
|
128
|
+
`SELECT`,
|
|
129
|
+
q._distinct && 'DISTINCT',
|
|
130
|
+
fields.map(f => (0, mysql_schema_util_1.mapNameToMySQL)(f)).join(', '),
|
|
131
|
+
`FROM`,
|
|
132
|
+
mysql.escapeId(q.table),
|
|
133
|
+
].filter(Boolean);
|
|
128
134
|
}
|
|
129
135
|
function offsetLimitTokens(q) {
|
|
130
136
|
const tokens = [];
|
|
@@ -137,13 +143,15 @@ function offsetLimitTokens(q) {
|
|
|
137
143
|
}
|
|
138
144
|
return tokens;
|
|
139
145
|
}
|
|
140
|
-
function
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
function groupOrderTokens(q) {
|
|
147
|
+
const t = [];
|
|
148
|
+
if (q._groupByFieldNames?.length) {
|
|
149
|
+
t.push(`GROUP BY`, q._groupByFieldNames.map(c => `\`${c}\``).join(', '));
|
|
150
|
+
}
|
|
151
|
+
if (q._orders.length) {
|
|
152
|
+
t.push(`ORDER BY`, q._orders.map(o => `\`${o.name}\` ${o.descending ? 'DESC' : 'ASC'}`).join(', '));
|
|
153
|
+
}
|
|
154
|
+
return t;
|
|
147
155
|
}
|
|
148
156
|
const OP_MAP = {
|
|
149
157
|
'==': '=',
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"@naturalcycles/dev-lib": "^12.7.5",
|
|
15
15
|
"@types/node": "^17.0.5",
|
|
16
|
-
"dotenv": "^
|
|
16
|
+
"dotenv": "^16.0.0",
|
|
17
17
|
"jest": "^27.2.4"
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"engines": {
|
|
37
37
|
"node": ">=14.15.0"
|
|
38
38
|
},
|
|
39
|
-
"version": "3.
|
|
39
|
+
"version": "3.11.1",
|
|
40
40
|
"description": "MySQL client implementing CommonDB interface",
|
|
41
41
|
"keywords": [
|
|
42
42
|
"mysql",
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { getDBAdapter } from './dbAdapter'
|
|
2
2
|
import { MysqlDB, MysqlDBCfg, MysqlDBOptions, MysqlDBSaveOptions } from './mysql.db'
|
|
3
|
+
import { MySQLKeyValueDB } from './mysqlKeyValueDB'
|
|
3
4
|
import { jsonSchemaToMySQLDDL } from './schema/mysql.schema.util'
|
|
4
5
|
|
|
5
6
|
export type { MysqlDBCfg, MysqlDBOptions, MysqlDBSaveOptions }
|
|
6
7
|
|
|
7
|
-
export { MysqlDB, jsonSchemaToMySQLDDL, getDBAdapter }
|
|
8
|
+
export { MysqlDB, MySQLKeyValueDB, jsonSchemaToMySQLDDL, getDBAdapter }
|
package/src/mysql.db.ts
CHANGED
|
@@ -176,7 +176,7 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
|
|
|
176
176
|
// GET
|
|
177
177
|
override async getByIds<ROW extends ObjectWithId>(
|
|
178
178
|
table: string,
|
|
179
|
-
ids:
|
|
179
|
+
ids: ROW['id'][],
|
|
180
180
|
opt: MysqlDBOptions = {},
|
|
181
181
|
): Promise<ROW[]> {
|
|
182
182
|
if (!ids.length) return []
|
|
@@ -218,6 +218,21 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
|
|
|
218
218
|
})
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Allows to run semicolon-separated "SQL file".
|
|
223
|
+
* E.g "ddl reset script".
|
|
224
|
+
*/
|
|
225
|
+
async runSQLString(s: string): Promise<void> {
|
|
226
|
+
const queries = s
|
|
227
|
+
.split(';')
|
|
228
|
+
.map(s => s.trim())
|
|
229
|
+
.filter(Boolean)
|
|
230
|
+
|
|
231
|
+
for await (const sql of queries) {
|
|
232
|
+
await this.runSQL({ sql })
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
221
236
|
override async runQueryCount<ROW extends ObjectWithId>(
|
|
222
237
|
q: DBQuery<ROW>,
|
|
223
238
|
_opt?: CommonDBOptions,
|
|
@@ -275,7 +290,11 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
|
|
|
275
290
|
/**
|
|
276
291
|
* Limitation: always returns [], regardless of which rows are actually deleted
|
|
277
292
|
*/
|
|
278
|
-
override async deleteByIds
|
|
293
|
+
override async deleteByIds<ROW extends ObjectWithId>(
|
|
294
|
+
table: string,
|
|
295
|
+
ids: ROW['id'][],
|
|
296
|
+
_opt?: MysqlDBOptions,
|
|
297
|
+
): Promise<number> {
|
|
279
298
|
if (!ids.length) return 0
|
|
280
299
|
const sql = dbQueryToSQLDelete(new DBQuery(table).filterEq('id', ids))
|
|
281
300
|
const res = await this.runSQL<any>({ sql })
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { Transform } from 'stream'
|
|
2
|
+
import { CommonDBCreateOptions, CommonKeyValueDB, KeyValueDBTuple } from '@naturalcycles/db-lib'
|
|
3
|
+
import { pMap } from '@naturalcycles/js-lib'
|
|
4
|
+
import { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
5
|
+
import { QueryOptions } from 'mysql'
|
|
6
|
+
import { MysqlDB, MysqlDBCfg } from './mysql.db'
|
|
7
|
+
|
|
8
|
+
interface KeyValueObject {
|
|
9
|
+
id: string
|
|
10
|
+
v: Buffer
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class MySQLKeyValueDB implements CommonKeyValueDB {
|
|
14
|
+
constructor(public cfg: MysqlDBCfg) {
|
|
15
|
+
this.db = new MysqlDB(this.cfg)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
db: MysqlDB
|
|
19
|
+
|
|
20
|
+
async ping(): Promise<void> {
|
|
21
|
+
await this.db.ping()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async close(): Promise<void> {
|
|
25
|
+
await this.db.close()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async createTable(table: string, opt: CommonDBCreateOptions = {}): Promise<void> {
|
|
29
|
+
if (opt.dropIfExists) await this.dropTable(table)
|
|
30
|
+
|
|
31
|
+
// On blob sizes: https://tableplus.com/blog/2019/10/tinyblob-blob-mediumblob-longblob.html
|
|
32
|
+
// LONGBLOB supports up to 4gb
|
|
33
|
+
// MEDIUMBLOB up to 16Mb
|
|
34
|
+
const sql = `create table ${table} (id VARCHAR(64) PRIMARY KEY, v LONGBLOB NOT NULL)`
|
|
35
|
+
this.db.cfg.logger.log(sql)
|
|
36
|
+
await this.db.runSQL({ sql })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async getByIds(table: string, ids: string[]): Promise<KeyValueDBTuple[]> {
|
|
40
|
+
if (!ids.length) return []
|
|
41
|
+
|
|
42
|
+
const sql = `SELECT id,v FROM ${table} where id in (${ids.map(id => `"${id}"`).join(',')})`
|
|
43
|
+
|
|
44
|
+
const rows = await this.db.runSQL<KeyValueObject[]>({ sql })
|
|
45
|
+
|
|
46
|
+
return rows.map(({ id, v }) => [id, v])
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Use with caution!
|
|
51
|
+
*/
|
|
52
|
+
async dropTable(table: string): Promise<void> {
|
|
53
|
+
await this.db.runSQL({ sql: `DROP TABLE IF EXISTS ${table}` })
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async deleteByIds(table: string, ids: string[]): Promise<void> {
|
|
57
|
+
const sql = `DELETE FROM ${table} WHERE id in (${ids.map(id => `"${id}"`).join(',')})`
|
|
58
|
+
if (this.cfg.logSQL) this.db.cfg.logger.log(sql)
|
|
59
|
+
await this.db.runSQL({ sql })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async saveBatch(table: string, entries: KeyValueDBTuple[]): Promise<void> {
|
|
63
|
+
const statements: QueryOptions[] = entries.map(([id, buf]) => {
|
|
64
|
+
return {
|
|
65
|
+
sql: `INSERT INTO ${table} (id, v) VALUES (?, ?)`,
|
|
66
|
+
values: [id, buf],
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
await pMap(statements, async statement => {
|
|
71
|
+
if (this.cfg.debug) this.db.cfg.logger.log(statement.sql)
|
|
72
|
+
await this.db.runSQL(statement)
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
streamIds(table: string, limit?: number): ReadableTyped<string> {
|
|
77
|
+
let sql = `SELECT id FROM ${table}`
|
|
78
|
+
if (limit) sql += ` LIMIT ${limit}`
|
|
79
|
+
if (this.cfg.logSQL) this.db.cfg.logger.log(`stream: ${sql}`)
|
|
80
|
+
|
|
81
|
+
return this.db
|
|
82
|
+
.pool()
|
|
83
|
+
.query(sql)
|
|
84
|
+
.stream()
|
|
85
|
+
.pipe(
|
|
86
|
+
new Transform({
|
|
87
|
+
objectMode: true,
|
|
88
|
+
transform(row, _, cb) {
|
|
89
|
+
cb(null, row.id)
|
|
90
|
+
},
|
|
91
|
+
}),
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
streamValues(table: string, limit?: number): ReadableTyped<Buffer> {
|
|
96
|
+
let sql = `SELECT v FROM ${table}`
|
|
97
|
+
if (limit) sql += ` LIMIT ${limit}`
|
|
98
|
+
if (this.cfg.logSQL) this.db.cfg.logger.log(`stream: ${sql}`)
|
|
99
|
+
|
|
100
|
+
return this.db
|
|
101
|
+
.pool()
|
|
102
|
+
.query(sql)
|
|
103
|
+
.stream()
|
|
104
|
+
.pipe(
|
|
105
|
+
new Transform({
|
|
106
|
+
objectMode: true,
|
|
107
|
+
transform(row, _, cb) {
|
|
108
|
+
cb(null, row.v)
|
|
109
|
+
},
|
|
110
|
+
}),
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
streamEntries(table: string, limit?: number): ReadableTyped<KeyValueDBTuple> {
|
|
115
|
+
let sql = `SELECT id,v FROM ${table}`
|
|
116
|
+
if (limit) sql += ` LIMIT ${limit}`
|
|
117
|
+
if (this.cfg.logSQL) this.db.cfg.logger.log(`stream: ${sql}`)
|
|
118
|
+
|
|
119
|
+
return this.db
|
|
120
|
+
.pool()
|
|
121
|
+
.query(sql)
|
|
122
|
+
.stream()
|
|
123
|
+
.pipe(
|
|
124
|
+
new Transform({
|
|
125
|
+
objectMode: true,
|
|
126
|
+
transform(row, _, cb) {
|
|
127
|
+
cb(null, [row.id, row.v])
|
|
128
|
+
},
|
|
129
|
+
}),
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async beginTransaction(): Promise<void> {
|
|
134
|
+
await this.db.runSQL({ sql: `BEGIN TRANSACTION` })
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async endTransaction(): Promise<void> {
|
|
138
|
+
await this.db.runSQL({ sql: `END TRANSACTION` })
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async count(table: string): Promise<number> {
|
|
142
|
+
const sql = `SELECT count(*) as cnt FROM ${table}`
|
|
143
|
+
if (this.cfg.logSQL) this.db.cfg.logger.log(sql)
|
|
144
|
+
|
|
145
|
+
const rows = await this.db.runSQL<{ cnt: number }[]>({ sql })
|
|
146
|
+
return rows[0]!.cnt
|
|
147
|
+
}
|
|
148
|
+
}
|
package/src/query.util.ts
CHANGED
|
@@ -16,7 +16,7 @@ export function dbQueryToSQLSelect(q: DBQuery<any>): string {
|
|
|
16
16
|
tokens.push(...whereTokens(q))
|
|
17
17
|
|
|
18
18
|
// order
|
|
19
|
-
tokens.push(...
|
|
19
|
+
tokens.push(...groupOrderTokens(q))
|
|
20
20
|
|
|
21
21
|
// offset/limit
|
|
22
22
|
tokens.push(...offsetLimitTokens(q))
|
|
@@ -155,7 +155,13 @@ function selectTokens(q: DBQuery): string[] {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
// We don't do `escapeId` cause it'll ruin e.g SELECT `count *` FROM ...
|
|
158
|
-
return [
|
|
158
|
+
return [
|
|
159
|
+
`SELECT`,
|
|
160
|
+
q._distinct && ('DISTINCT' as any),
|
|
161
|
+
fields.map(f => mapNameToMySQL(f)).join(', '),
|
|
162
|
+
`FROM`,
|
|
163
|
+
mysql.escapeId(q.table),
|
|
164
|
+
].filter(Boolean)
|
|
159
165
|
}
|
|
160
166
|
|
|
161
167
|
function offsetLimitTokens(q: DBQuery<any>): string[] {
|
|
@@ -173,12 +179,21 @@ function offsetLimitTokens(q: DBQuery<any>): string[] {
|
|
|
173
179
|
return tokens
|
|
174
180
|
}
|
|
175
181
|
|
|
176
|
-
function
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
q.
|
|
181
|
-
|
|
182
|
+
function groupOrderTokens(q: DBQuery): string[] {
|
|
183
|
+
const t: string[] = []
|
|
184
|
+
|
|
185
|
+
if (q._groupByFieldNames?.length) {
|
|
186
|
+
t.push(`GROUP BY`, q._groupByFieldNames.map(c => `\`${c}\``).join(', '))
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (q._orders.length) {
|
|
190
|
+
t.push(
|
|
191
|
+
`ORDER BY`,
|
|
192
|
+
q._orders.map(o => `\`${o.name}\` ${o.descending ? 'DESC' : 'ASC'}`).join(', '),
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return t
|
|
182
197
|
}
|
|
183
198
|
|
|
184
199
|
const OP_MAP: Partial<Record<DBQueryFilterOperator, string>> = {
|