@naturalcycles/mysql-lib 3.16.2 → 4.0.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/dist/index.d.ts +3 -3
- package/dist/index.js +3 -8
- package/dist/mysql.db.d.ts +5 -4
- package/dist/mysql.db.js +58 -50
- package/dist/mysqlKeyValueDB.d.ts +5 -4
- package/dist/mysqlKeyValueDB.js +26 -16
- package/dist/query.util.d.ts +3 -3
- package/dist/query.util.js +18 -27
- package/dist/schema/mysql.schema.util.d.ts +1 -1
- package/dist/schema/mysql.schema.util.js +5 -12
- package/package.json +14 -11
- package/src/index.ts +3 -3
- package/src/mysql.db.ts +35 -19
- package/src/mysqlKeyValueDB.ts +24 -8
- package/src/query.util.ts +5 -4
- package/src/schema/mysql.schema.util.ts +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './mysql.db';
|
|
2
|
-
export * from './mysqlKeyValueDB';
|
|
3
|
-
export { jsonSchemaToMySQLDDL } from './schema/mysql.schema.util';
|
|
1
|
+
export * from './mysql.db.js';
|
|
2
|
+
export * from './mysqlKeyValueDB.js';
|
|
3
|
+
export { jsonSchemaToMySQLDDL } from './schema/mysql.schema.util.js';
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
tslib_1.__exportStar(require("./mysql.db"), exports);
|
|
6
|
-
tslib_1.__exportStar(require("./mysqlKeyValueDB"), exports);
|
|
7
|
-
var mysql_schema_util_1 = require("./schema/mysql.schema.util");
|
|
8
|
-
Object.defineProperty(exports, "jsonSchemaToMySQLDDL", { enumerable: true, get: function () { return mysql_schema_util_1.jsonSchemaToMySQLDDL; } });
|
|
1
|
+
export * from './mysql.db.js';
|
|
2
|
+
export * from './mysqlKeyValueDB.js';
|
|
3
|
+
export { jsonSchemaToMySQLDDL } from './schema/mysql.schema.util.js';
|
package/dist/mysql.db.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import type { CommonDB, CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, CommonDBSupport, RunQueryResult } from '@naturalcycles/db-lib';
|
|
2
|
+
import { BaseCommonDB, CommonDBType, DBQuery } from '@naturalcycles/db-lib';
|
|
3
|
+
import type { CommonLogger, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
|
|
4
|
+
import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
5
|
+
import type { Connection, Pool, PoolConfig, PoolConnection, QueryOptions } from 'mysql';
|
|
5
6
|
export interface MysqlDBOptions extends CommonDBOptions {
|
|
6
7
|
}
|
|
7
8
|
export interface MysqlDBSaveOptions<ROW extends ObjectWithId> extends CommonDBSaveOptions<ROW> {
|
package/dist/mysql.db.js
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const mysql = tslib_1.__importStar(require("mysql"));
|
|
11
|
-
const query_util_1 = require("./query.util");
|
|
12
|
-
const mysql_schema_util_1 = require("./schema/mysql.schema.util");
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { Readable, Transform } from 'node:stream';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
4
|
+
import { BaseCommonDB, commonDBFullSupport, CommonDBType, DBQuery } from '@naturalcycles/db-lib';
|
|
5
|
+
import { _assert, _filterUndefinedValues, _mapKeys, _mapValues, _Memo, _omit, commonLoggerPrefix, } from '@naturalcycles/js-lib';
|
|
6
|
+
import { white } from '@naturalcycles/nodejs-lib';
|
|
7
|
+
import * as mysql from 'mysql';
|
|
8
|
+
import { dbQueryToSQLDelete, dbQueryToSQLSelect, dbQueryToSQLUpdate, insertSQL, } from './query.util.js';
|
|
9
|
+
import { jsonSchemaToMySQLDDL, mapNameFromMySQL, mysqlTableStatsToJsonSchemaField, } from './schema/mysql.schema.util.js';
|
|
13
10
|
const BOOLEAN_TYPES = new Set(['TINY', 'TINYINT', 'INT']);
|
|
14
11
|
const BOOLEAN_BIT_TYPES = new Set(['BIT']);
|
|
15
12
|
const typeCast = (field, next) => {
|
|
@@ -31,16 +28,16 @@ const typeCast = (field, next) => {
|
|
|
31
28
|
}
|
|
32
29
|
return next();
|
|
33
30
|
};
|
|
34
|
-
class MysqlDB extends
|
|
31
|
+
export class MysqlDB extends BaseCommonDB {
|
|
32
|
+
dbType = CommonDBType.relational;
|
|
33
|
+
support = {
|
|
34
|
+
...commonDBFullSupport,
|
|
35
|
+
updateSaveMethod: false, // todo: can be implemented
|
|
36
|
+
transactions: false, // todo: can be implemented
|
|
37
|
+
increment: false, // todo: can be implemented
|
|
38
|
+
};
|
|
35
39
|
constructor(cfg = {}) {
|
|
36
40
|
super();
|
|
37
|
-
this.dbType = db_lib_1.CommonDBType.relational;
|
|
38
|
-
this.support = {
|
|
39
|
-
...db_lib_1.commonDBFullSupport,
|
|
40
|
-
updateSaveMethod: false, // todo: can be implemented
|
|
41
|
-
transactions: false, // todo: can be implemented
|
|
42
|
-
increment: false, // todo: can be implemented
|
|
43
|
-
};
|
|
44
41
|
this.cfg = {
|
|
45
42
|
typeCast,
|
|
46
43
|
// charset: 'utf8mb4', // for emoji support
|
|
@@ -49,9 +46,10 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
|
|
|
49
46
|
// password: MYSQL_PW,
|
|
50
47
|
// database: MYSQL_DB,
|
|
51
48
|
...cfg,
|
|
52
|
-
logger:
|
|
49
|
+
logger: commonLoggerPrefix(cfg.logger || console, '[mysql]'),
|
|
53
50
|
};
|
|
54
51
|
}
|
|
52
|
+
cfg;
|
|
55
53
|
async ping() {
|
|
56
54
|
const con = await this.getConnection();
|
|
57
55
|
con.release();
|
|
@@ -59,7 +57,7 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
|
|
|
59
57
|
pool() {
|
|
60
58
|
const pool = mysql.createPool(this.cfg);
|
|
61
59
|
const { host, database, logger } = this.cfg;
|
|
62
|
-
logger.log(`connected to ${
|
|
60
|
+
logger.log(`connected to ${white(host + '/' + database)}`);
|
|
63
61
|
if (this.cfg.debugConnections) {
|
|
64
62
|
pool.on('acquire', con => {
|
|
65
63
|
logger.log(`acquire(${con.threadId})`);
|
|
@@ -81,7 +79,7 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
|
|
|
81
79
|
}
|
|
82
80
|
async close() {
|
|
83
81
|
const pool = this.pool();
|
|
84
|
-
await
|
|
82
|
+
await promisify(pool.end.bind(pool))();
|
|
85
83
|
this.cfg.logger.log('closed');
|
|
86
84
|
}
|
|
87
85
|
/**
|
|
@@ -89,7 +87,7 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
|
|
|
89
87
|
*/
|
|
90
88
|
async getConnection() {
|
|
91
89
|
const pool = this.pool();
|
|
92
|
-
return await
|
|
90
|
+
return await promisify(pool.getConnection.bind(pool))();
|
|
93
91
|
}
|
|
94
92
|
/**
|
|
95
93
|
* Manually create a single (not pool) connection.
|
|
@@ -97,7 +95,7 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
|
|
|
97
95
|
*/
|
|
98
96
|
async createSingleConnection() {
|
|
99
97
|
const con = mysql.createConnection(this.cfg);
|
|
100
|
-
await
|
|
98
|
+
await promisify(con.connect.bind(con))();
|
|
101
99
|
const { threadId } = con;
|
|
102
100
|
if (this.cfg.debugConnections) {
|
|
103
101
|
this.cfg.logger.log(`createSingleConnection(${threadId})`);
|
|
@@ -115,19 +113,19 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
|
|
|
115
113
|
async getByIds(table, ids, opt = {}) {
|
|
116
114
|
if (!ids.length)
|
|
117
115
|
return [];
|
|
118
|
-
const q = new
|
|
116
|
+
const q = new DBQuery(table).filterEq('id', ids);
|
|
119
117
|
const { rows } = await this.runQuery(q, opt);
|
|
120
|
-
return rows.map(r =>
|
|
118
|
+
return rows.map(r => _mapKeys(r, k => mapNameFromMySQL(k)));
|
|
121
119
|
}
|
|
122
120
|
// QUERY
|
|
123
121
|
async runQuery(q, _opt = {}) {
|
|
124
|
-
const sql =
|
|
122
|
+
const sql = dbQueryToSQLSelect(q);
|
|
125
123
|
if (!sql) {
|
|
126
124
|
return {
|
|
127
125
|
rows: [],
|
|
128
126
|
};
|
|
129
127
|
}
|
|
130
|
-
const rows = (await this.runSQL({ sql })).map(row =>
|
|
128
|
+
const rows = (await this.runSQL({ sql })).map(row => _mapKeys(_filterUndefinedValues(row, true), k => mapNameFromMySQL(k)));
|
|
131
129
|
// edge case where 0 fields are selected
|
|
132
130
|
if (q._selectedFieldNames?.length === 0) {
|
|
133
131
|
return {
|
|
@@ -167,24 +165,35 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
|
|
|
167
165
|
return rows[0]._count;
|
|
168
166
|
}
|
|
169
167
|
streamQuery(q, _opt = {}) {
|
|
170
|
-
const sql =
|
|
168
|
+
const sql = dbQueryToSQLSelect(q);
|
|
171
169
|
if (!sql) {
|
|
172
|
-
return
|
|
170
|
+
return Readable.from([]);
|
|
173
171
|
}
|
|
174
172
|
if (this.cfg.logSQL)
|
|
175
173
|
this.cfg.logger.log(`stream: ${sql}`);
|
|
176
|
-
//
|
|
177
|
-
return this.pool().query(sql).stream().map(row =>
|
|
174
|
+
// todo: this is nice, but `mysql` package uses `readable-stream@2` which is not compatible with `node:stream` iterable helpers
|
|
175
|
+
// return (this.pool().query(sql).stream() as ReadableTyped<ROW>).map(row =>
|
|
176
|
+
// _filterUndefinedValues(row, true),
|
|
177
|
+
// )
|
|
178
|
+
return this.pool()
|
|
179
|
+
.query(sql)
|
|
180
|
+
.stream()
|
|
181
|
+
.pipe(new Transform({
|
|
182
|
+
objectMode: true,
|
|
183
|
+
transform(row, _encoding, cb) {
|
|
184
|
+
cb(null, _filterUndefinedValues(row, true));
|
|
185
|
+
},
|
|
186
|
+
}));
|
|
178
187
|
}
|
|
179
188
|
// SAVE
|
|
180
189
|
async saveBatch(table, rowsInput, opt = {}) {
|
|
181
190
|
if (!rowsInput.length)
|
|
182
191
|
return;
|
|
183
192
|
// Stringify object values
|
|
184
|
-
const rows = rowsInput.map(row =>
|
|
193
|
+
const rows = rowsInput.map(row => _mapValues(row, (_k, v) => {
|
|
185
194
|
if (v && typeof v === 'object' && !Buffer.isBuffer(v)) {
|
|
186
195
|
// This is to avoid implicit Date stringification and mismatch: it gets saved as Date, but loaded as String
|
|
187
|
-
|
|
196
|
+
_assert(!(v instanceof Date), 'mysql-lib does not support Date values, please stringify them before passing');
|
|
188
197
|
return JSON.stringify(v);
|
|
189
198
|
}
|
|
190
199
|
return v;
|
|
@@ -196,12 +205,12 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
|
|
|
196
205
|
i++;
|
|
197
206
|
if (row.id) {
|
|
198
207
|
// Update already existing
|
|
199
|
-
const query = new
|
|
200
|
-
await this.patchByQuery(query,
|
|
208
|
+
const query = new DBQuery(table).filterEq('id', row.id);
|
|
209
|
+
await this.patchByQuery(query, _omit(row, ['id']));
|
|
201
210
|
}
|
|
202
211
|
else {
|
|
203
212
|
// Create new
|
|
204
|
-
const sql =
|
|
213
|
+
const sql = insertSQL(table, [row], 'INSERT', this.cfg.logger)[0];
|
|
205
214
|
const { insertId } = await this.runSQL({ sql });
|
|
206
215
|
// Mutate the input row with insertIt
|
|
207
216
|
rowsInput[i].id = insertId; // this is because we no longer support number ids in CommonDB
|
|
@@ -213,15 +222,15 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
|
|
|
213
222
|
// TODO: This fails if a combination of entities with id and without id are parsed
|
|
214
223
|
for (const row of rows) {
|
|
215
224
|
// Update already existing
|
|
216
|
-
|
|
217
|
-
const query = new
|
|
218
|
-
await this.patchByQuery(query,
|
|
225
|
+
_assert(row.id, 'id is required for updating');
|
|
226
|
+
const query = new DBQuery(table).filterEq('id', row.id);
|
|
227
|
+
await this.patchByQuery(query, _omit(row, ['id']));
|
|
219
228
|
}
|
|
220
229
|
return;
|
|
221
230
|
}
|
|
222
231
|
const verb = opt.saveMethod === 'insert' ? 'INSERT' : 'REPLACE';
|
|
223
232
|
// inserts are split into multiple sentenses to respect the max_packet_size (1Mb usually)
|
|
224
|
-
const sqls =
|
|
233
|
+
const sqls = insertSQL(table, rows, verb, this.cfg.logger);
|
|
225
234
|
for (const sql of sqls) {
|
|
226
235
|
await this.runSQL({ sql });
|
|
227
236
|
}
|
|
@@ -233,14 +242,14 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
|
|
|
233
242
|
async deleteByIds(table, ids, _opt) {
|
|
234
243
|
if (!ids.length)
|
|
235
244
|
return 0;
|
|
236
|
-
const sql =
|
|
245
|
+
const sql = dbQueryToSQLDelete(new DBQuery(table).filterEq('id', ids));
|
|
237
246
|
if (!sql)
|
|
238
247
|
return 0;
|
|
239
248
|
const { affectedRows } = await this.runSQL({ sql });
|
|
240
249
|
return affectedRows;
|
|
241
250
|
}
|
|
242
251
|
async deleteByQuery(q, _opt) {
|
|
243
|
-
const sql =
|
|
252
|
+
const sql = dbQueryToSQLDelete(q);
|
|
244
253
|
if (!sql)
|
|
245
254
|
return 0;
|
|
246
255
|
const { affectedRows } = await this.runSQL({ sql });
|
|
@@ -258,7 +267,7 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
|
|
|
258
267
|
async createTable(table, schema, opt = {}) {
|
|
259
268
|
if (opt.dropIfExists)
|
|
260
269
|
await this.dropTable(table);
|
|
261
|
-
const sql =
|
|
270
|
+
const sql = jsonSchemaToMySQLDDL(table, schema);
|
|
262
271
|
await this.runSQL({ sql });
|
|
263
272
|
}
|
|
264
273
|
async getTables() {
|
|
@@ -270,17 +279,16 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
|
|
|
270
279
|
const stats = await this.runSQL({
|
|
271
280
|
sql: `describe ${mysql.escapeId(table)}`,
|
|
272
281
|
});
|
|
273
|
-
return
|
|
282
|
+
return mysqlTableStatsToJsonSchemaField(table, stats, this.cfg.logger);
|
|
274
283
|
}
|
|
275
284
|
async patchByQuery(q, patch) {
|
|
276
|
-
const sql =
|
|
285
|
+
const sql = dbQueryToSQLUpdate(q, patch);
|
|
277
286
|
if (!sql)
|
|
278
287
|
return 0;
|
|
279
288
|
const { affectedRows } = await this.runSQL({ sql });
|
|
280
289
|
return affectedRows;
|
|
281
290
|
}
|
|
282
291
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
(0, js_lib_1._Memo)()
|
|
292
|
+
__decorate([
|
|
293
|
+
_Memo()
|
|
286
294
|
], MysqlDB.prototype, "pool", null);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { CommonDBCreateOptions, CommonKeyValueDB, KeyValueDBTuple } from '@naturalcycles/db-lib';
|
|
2
|
-
import { IncrementTuple } from '@naturalcycles/db-lib/dist/kv/commonKeyValueDB';
|
|
3
|
-
import { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
4
|
-
import {
|
|
1
|
+
import type { CommonDBCreateOptions, CommonKeyValueDB, KeyValueDBTuple } from '@naturalcycles/db-lib';
|
|
2
|
+
import type { IncrementTuple } from '@naturalcycles/db-lib/dist/kv/commonKeyValueDB.js';
|
|
3
|
+
import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
4
|
+
import type { MysqlDBCfg } from './mysql.db.js';
|
|
5
|
+
import { MysqlDB } from './mysql.db.js';
|
|
5
6
|
export declare class MySQLKeyValueDB implements CommonKeyValueDB {
|
|
6
7
|
cfg: MysqlDBCfg;
|
|
7
8
|
constructor(cfg: MysqlDBCfg);
|
package/dist/mysqlKeyValueDB.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class MySQLKeyValueDB {
|
|
1
|
+
import { Transform } from 'node:stream';
|
|
2
|
+
import { commonKeyValueDBFullSupport } from '@naturalcycles/db-lib';
|
|
3
|
+
import { AppError, pMap } from '@naturalcycles/js-lib';
|
|
4
|
+
import { MysqlDB } from './mysql.db.js';
|
|
5
|
+
export class MySQLKeyValueDB {
|
|
6
|
+
cfg;
|
|
8
7
|
constructor(cfg) {
|
|
9
8
|
this.cfg = cfg;
|
|
10
|
-
this.
|
|
11
|
-
...db_lib_1.commonKeyValueDBFullSupport,
|
|
12
|
-
increment: false,
|
|
13
|
-
};
|
|
14
|
-
this.db = new mysql_db_1.MysqlDB(this.cfg);
|
|
9
|
+
this.db = new MysqlDB(this.cfg);
|
|
15
10
|
}
|
|
11
|
+
db;
|
|
12
|
+
support = {
|
|
13
|
+
...commonKeyValueDBFullSupport,
|
|
14
|
+
increment: false,
|
|
15
|
+
};
|
|
16
16
|
async ping() {
|
|
17
17
|
await this.db.ping();
|
|
18
18
|
}
|
|
@@ -55,7 +55,7 @@ class MySQLKeyValueDB {
|
|
|
55
55
|
values: [id, buf],
|
|
56
56
|
};
|
|
57
57
|
});
|
|
58
|
-
await
|
|
58
|
+
await pMap(statements, async (statement) => {
|
|
59
59
|
if (this.cfg.debug)
|
|
60
60
|
this.db.cfg.logger.log(statement.sql);
|
|
61
61
|
await this.db.runSQL(statement);
|
|
@@ -67,7 +67,18 @@ class MySQLKeyValueDB {
|
|
|
67
67
|
sql += ` LIMIT ${limit}`;
|
|
68
68
|
if (this.cfg.logSQL)
|
|
69
69
|
this.db.cfg.logger.log(`stream: ${sql}`);
|
|
70
|
-
|
|
70
|
+
// todo: this is nice, but `mysql` package uses `readable-stream@2` which is not compatible with `node:stream` iterable helpers
|
|
71
|
+
// return (this.db.pool().query(sql).stream() as ReadableTyped<ObjectWithId>).map(row => row.id)
|
|
72
|
+
return this.db
|
|
73
|
+
.pool()
|
|
74
|
+
.query(sql)
|
|
75
|
+
.stream()
|
|
76
|
+
.pipe(new Transform({
|
|
77
|
+
objectMode: true,
|
|
78
|
+
transform(row, _encoding, cb) {
|
|
79
|
+
cb(null, row.id);
|
|
80
|
+
},
|
|
81
|
+
}));
|
|
71
82
|
}
|
|
72
83
|
streamValues(table, limit) {
|
|
73
84
|
let sql = `SELECT v FROM ${table}`;
|
|
@@ -102,7 +113,6 @@ class MySQLKeyValueDB {
|
|
|
102
113
|
return rows[0].cnt;
|
|
103
114
|
}
|
|
104
115
|
async incrementBatch(_table, _entries) {
|
|
105
|
-
throw new
|
|
116
|
+
throw new AppError('MySQLKeyValueDB.incrementBatch() is not implemented');
|
|
106
117
|
}
|
|
107
118
|
}
|
|
108
|
-
exports.MySQLKeyValueDB = MySQLKeyValueDB;
|
package/dist/query.util.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { DBQuery } from '@naturalcycles/db-lib';
|
|
2
|
-
import { CommonLogger } from '@naturalcycles/js-lib';
|
|
3
|
-
import { QueryOptions } from 'mysql';
|
|
1
|
+
import type { DBQuery } from '@naturalcycles/db-lib';
|
|
2
|
+
import type { CommonLogger } from '@naturalcycles/js-lib';
|
|
3
|
+
import type { QueryOptions } from 'mysql';
|
|
4
4
|
/**
|
|
5
5
|
* Returns `null` if it detects that 0 rows will be returned,
|
|
6
6
|
* e.g when `IN ()` (empty array) is used.
|
package/dist/query.util.js
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
exports.insertSQL = insertSQL;
|
|
6
|
-
exports.insertSQLSingle = insertSQLSingle;
|
|
7
|
-
exports.insertSQLSetSingle = insertSQLSetSingle;
|
|
8
|
-
exports.dbQueryToSQLUpdate = dbQueryToSQLUpdate;
|
|
9
|
-
const tslib_1 = require("tslib");
|
|
10
|
-
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
11
|
-
const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
|
|
12
|
-
const mysql = tslib_1.__importStar(require("mysql"));
|
|
13
|
-
const mysql_schema_util_1 = require("./schema/mysql.schema.util");
|
|
1
|
+
import { _hb } from '@naturalcycles/js-lib';
|
|
2
|
+
import { white, yellow } from '@naturalcycles/nodejs-lib';
|
|
3
|
+
import * as mysql from 'mysql';
|
|
4
|
+
import { mapNameToMySQL } from './schema/mysql.schema.util.js';
|
|
14
5
|
const MAX_PACKET_SIZE = 1024 * 1024; // 1Mb
|
|
15
6
|
const MAX_ROW_SIZE = 800 * 1024; // 1Mb - margin
|
|
16
7
|
/**
|
|
17
8
|
* Returns `null` if it detects that 0 rows will be returned,
|
|
18
9
|
* e.g when `IN ()` (empty array) is used.
|
|
19
10
|
*/
|
|
20
|
-
function dbQueryToSQLSelect(q) {
|
|
11
|
+
export function dbQueryToSQLSelect(q) {
|
|
21
12
|
const tokens = selectTokens(q);
|
|
22
13
|
// filters
|
|
23
14
|
const whereTokens = getWhereTokens(q);
|
|
@@ -33,7 +24,7 @@ function dbQueryToSQLSelect(q) {
|
|
|
33
24
|
/**
|
|
34
25
|
* Returns null in "0 rows" case.
|
|
35
26
|
*/
|
|
36
|
-
function dbQueryToSQLDelete(q) {
|
|
27
|
+
export function dbQueryToSQLDelete(q) {
|
|
37
28
|
const tokens = [`DELETE FROM`, mysql.escapeId(q.table)];
|
|
38
29
|
// filters
|
|
39
30
|
const whereTokens = getWhereTokens(q);
|
|
@@ -47,7 +38,7 @@ function dbQueryToSQLDelete(q) {
|
|
|
47
38
|
/**
|
|
48
39
|
* Returns array of sql statements to respect the max sql size.
|
|
49
40
|
*/
|
|
50
|
-
function insertSQL(table, rows, verb = 'INSERT', logger = console) {
|
|
41
|
+
export function insertSQL(table, rows, verb = 'INSERT', logger = console) {
|
|
51
42
|
// INSERT INTO table_name (column1, column2, column3, ...)
|
|
52
43
|
// VALUES (value1, value2, value3, ...);
|
|
53
44
|
// eslint-disable-next-line unicorn/no-array-reduce
|
|
@@ -60,7 +51,7 @@ function insertSQL(table, rows, verb = 'INSERT', logger = console) {
|
|
|
60
51
|
verb,
|
|
61
52
|
`INTO`,
|
|
62
53
|
mysql.escapeId(table),
|
|
63
|
-
`(` + [...fields].map(f => mysql.escapeId(
|
|
54
|
+
`(` + [...fields].map(f => mysql.escapeId(mapNameToMySQL(f))).join(',') + `)`,
|
|
64
55
|
`VALUES\n`,
|
|
65
56
|
].join(' ');
|
|
66
57
|
const valueRows = rows.map(rec => {
|
|
@@ -88,11 +79,11 @@ function insertSQL(table, rows, verb = 'INSERT', logger = console) {
|
|
|
88
79
|
if (sql) {
|
|
89
80
|
sqls.push(sql); // last one
|
|
90
81
|
}
|
|
91
|
-
logger.log(`${
|
|
82
|
+
logger.log(`${white(table)} large sql query (${yellow(_hb(full.length))}) was split into ${yellow(sqls.length)} chunks`);
|
|
92
83
|
return sqls;
|
|
93
84
|
// todo: handle "upsert" later
|
|
94
85
|
}
|
|
95
|
-
function insertSQLSingle(table, record) {
|
|
86
|
+
export function insertSQLSingle(table, record) {
|
|
96
87
|
// INSERT INTO table_name (column1, column2, column3, ...)
|
|
97
88
|
// VALUES (value1, value2, value3, ...);
|
|
98
89
|
const tokens = [
|
|
@@ -100,7 +91,7 @@ function insertSQLSingle(table, record) {
|
|
|
100
91
|
mysql.escapeId(table),
|
|
101
92
|
`(` +
|
|
102
93
|
Object.keys(record)
|
|
103
|
-
.map(f => mysql.escapeId(
|
|
94
|
+
.map(f => mysql.escapeId(mapNameToMySQL(f)))
|
|
104
95
|
.join(',') +
|
|
105
96
|
`)`,
|
|
106
97
|
`\nVALUES`,
|
|
@@ -112,13 +103,13 @@ function insertSQLSingle(table, record) {
|
|
|
112
103
|
];
|
|
113
104
|
return tokens.join(' ');
|
|
114
105
|
}
|
|
115
|
-
function insertSQLSetSingle(table, record) {
|
|
106
|
+
export function insertSQLSetSingle(table, record) {
|
|
116
107
|
return {
|
|
117
108
|
sql: `INSERT INTO ${mysql.escapeId(table)} SET ?`,
|
|
118
109
|
values: [record],
|
|
119
110
|
};
|
|
120
111
|
}
|
|
121
|
-
function dbQueryToSQLUpdate(q, patch) {
|
|
112
|
+
export function dbQueryToSQLUpdate(q, patch) {
|
|
122
113
|
// var sql = mysql.format('UPDATE posts SET modified = ? WHERE id = ?', [CURRENT_TIMESTAMP, 42]);
|
|
123
114
|
const whereTokens = getWhereTokens(q);
|
|
124
115
|
if (!whereTokens)
|
|
@@ -128,7 +119,7 @@ function dbQueryToSQLUpdate(q, patch) {
|
|
|
128
119
|
mysql.escapeId(q.table),
|
|
129
120
|
`SET`,
|
|
130
121
|
Object.keys(patch)
|
|
131
|
-
.map(f => mysql.escapeId(
|
|
122
|
+
.map(f => mysql.escapeId(mapNameToMySQL(f)) + ' = ?')
|
|
132
123
|
.join(', '),
|
|
133
124
|
...whereTokens,
|
|
134
125
|
];
|
|
@@ -143,7 +134,7 @@ function selectTokens(q) {
|
|
|
143
134
|
return [
|
|
144
135
|
`SELECT`,
|
|
145
136
|
q._distinct && 'DISTINCT',
|
|
146
|
-
fields.map(f =>
|
|
137
|
+
fields.map(f => mapNameToMySQL(f)).join(', '),
|
|
147
138
|
`FROM`,
|
|
148
139
|
mysql.escapeId(q.table),
|
|
149
140
|
].filter(Boolean);
|
|
@@ -186,7 +177,7 @@ function getWhereTokens(q) {
|
|
|
186
177
|
if (f.val === null || f.val === undefined) {
|
|
187
178
|
// special treatment
|
|
188
179
|
return [
|
|
189
|
-
mysql.escapeId(
|
|
180
|
+
mysql.escapeId(mapNameToMySQL(f.name)),
|
|
190
181
|
f.op === '==' ? 'IS NULL' : 'IS NOT NULL',
|
|
191
182
|
].join(' ');
|
|
192
183
|
}
|
|
@@ -194,10 +185,10 @@ function getWhereTokens(q) {
|
|
|
194
185
|
// special case for arrays
|
|
195
186
|
if (!f.val.length)
|
|
196
187
|
returnNull = true;
|
|
197
|
-
return `${mysql.escapeId(
|
|
188
|
+
return `${mysql.escapeId(mapNameToMySQL(f.name))} IN (${mysql.escape(f.val)})`;
|
|
198
189
|
}
|
|
199
190
|
return [
|
|
200
|
-
mysql.escapeId(
|
|
191
|
+
mysql.escapeId(mapNameToMySQL(f.name)),
|
|
201
192
|
OP_MAP[f.op] || f.op,
|
|
202
193
|
mysql.escape(f.val),
|
|
203
194
|
].join(' ');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnyObject, CommonLogger, JsonSchemaObject, JsonSchemaRootObject } from '@naturalcycles/js-lib';
|
|
1
|
+
import type { AnyObject, CommonLogger, JsonSchemaObject, JsonSchemaRootObject } from '@naturalcycles/js-lib';
|
|
2
2
|
export interface MySQLTableStats {
|
|
3
3
|
Field: string;
|
|
4
4
|
Type: string;
|
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.jsonSchemaToMySQLDDL = jsonSchemaToMySQLDDL;
|
|
4
|
-
exports.mysqlTableStatsToJsonSchemaField = mysqlTableStatsToJsonSchemaField;
|
|
5
|
-
exports.mapNameToMySQL = mapNameToMySQL;
|
|
6
|
-
exports.mapNameFromMySQL = mapNameFromMySQL;
|
|
7
|
-
const tslib_1 = require("tslib");
|
|
8
|
-
const mysql = tslib_1.__importStar(require("mysql"));
|
|
1
|
+
import * as mysql from 'mysql';
|
|
9
2
|
/**
|
|
10
3
|
* It currently skips nullability and declares everything as "DEFAULT NULL".
|
|
11
4
|
*/
|
|
12
|
-
function jsonSchemaToMySQLDDL(table, schema, opt = {}) {
|
|
5
|
+
export function jsonSchemaToMySQLDDL(table, schema, opt = {}) {
|
|
13
6
|
const { engine = 'InnoDB' } = opt;
|
|
14
7
|
const lines = [`CREATE TABLE ${mysql.escapeId(table)} (`];
|
|
15
8
|
const innerLines = Object.entries(schema.properties).map(([k, s]) => {
|
|
@@ -59,7 +52,7 @@ function jsonSchemaToMySQLDDL(table, schema, opt = {}) {
|
|
|
59
52
|
lines.push(`) ENGINE=${engine}`);
|
|
60
53
|
return lines.join('\n');
|
|
61
54
|
}
|
|
62
|
-
function mysqlTableStatsToJsonSchemaField(table, stats, logger) {
|
|
55
|
+
export function mysqlTableStatsToJsonSchemaField(table, stats, logger) {
|
|
63
56
|
const s = {
|
|
64
57
|
$id: `${table}.schema.json`,
|
|
65
58
|
type: 'object',
|
|
@@ -101,9 +94,9 @@ function mysqlTableStatsToJsonSchemaField(table, stats, logger) {
|
|
|
101
94
|
*
|
|
102
95
|
* @param name
|
|
103
96
|
*/
|
|
104
|
-
function mapNameToMySQL(name) {
|
|
97
|
+
export function mapNameToMySQL(name) {
|
|
105
98
|
return name.replaceAll('.', '_dot_');
|
|
106
99
|
}
|
|
107
|
-
function mapNameFromMySQL(name) {
|
|
100
|
+
export function mapNameFromMySQL(name) {
|
|
108
101
|
return name.replaceAll('_dot_', '.');
|
|
109
102
|
}
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/mysql-lib",
|
|
3
|
+
"type": "module",
|
|
3
4
|
"scripts": {
|
|
4
5
|
"prepare": "husky",
|
|
5
6
|
"build": "dev-lib build",
|
|
@@ -9,17 +10,19 @@
|
|
|
9
10
|
"lbt": "dev-lib lbt"
|
|
10
11
|
},
|
|
11
12
|
"dependencies": {
|
|
12
|
-
"@naturalcycles/db-lib": "^
|
|
13
|
-
"@naturalcycles/js-lib": "^14
|
|
14
|
-
"@naturalcycles/nodejs-lib": "^13
|
|
15
|
-
"@types/mysql": "^2
|
|
16
|
-
"mysql": "^2
|
|
13
|
+
"@naturalcycles/db-lib": "^10",
|
|
14
|
+
"@naturalcycles/js-lib": "^14",
|
|
15
|
+
"@naturalcycles/nodejs-lib": "^13",
|
|
16
|
+
"@types/mysql": "^2",
|
|
17
|
+
"mysql": "^2"
|
|
17
18
|
},
|
|
18
19
|
"devDependencies": {
|
|
19
|
-
"@naturalcycles/dev-lib": "^
|
|
20
|
-
"@types/node": "^22
|
|
21
|
-
"
|
|
22
|
-
"
|
|
20
|
+
"@naturalcycles/dev-lib": "^17",
|
|
21
|
+
"@types/node": "^22",
|
|
22
|
+
"@vitest/coverage-v8": "^3",
|
|
23
|
+
"dotenv": "^16",
|
|
24
|
+
"tsx": "^4",
|
|
25
|
+
"vitest": "^3"
|
|
23
26
|
},
|
|
24
27
|
"files": [
|
|
25
28
|
"dist",
|
|
@@ -39,9 +42,9 @@
|
|
|
39
42
|
"url": "https://github.com/NaturalCycles/mysql-lib"
|
|
40
43
|
},
|
|
41
44
|
"engines": {
|
|
42
|
-
"node": ">=
|
|
45
|
+
"node": ">=22.12.0"
|
|
43
46
|
},
|
|
44
|
-
"version": "
|
|
47
|
+
"version": "4.0.0",
|
|
45
48
|
"description": "MySQL client implementing CommonDB interface",
|
|
46
49
|
"keywords": [
|
|
47
50
|
"mysql",
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './mysql.db'
|
|
2
|
-
export * from './mysqlKeyValueDB'
|
|
3
|
-
export { jsonSchemaToMySQLDDL } from './schema/mysql.schema.util'
|
|
1
|
+
export * from './mysql.db.js'
|
|
2
|
+
export * from './mysqlKeyValueDB.js'
|
|
3
|
+
export { jsonSchemaToMySQLDDL } from './schema/mysql.schema.util.js'
|
package/src/mysql.db.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import { Readable } from 'node:stream'
|
|
1
|
+
import { Readable, Transform } from 'node:stream'
|
|
2
2
|
import { promisify } from 'node:util'
|
|
3
|
-
import {
|
|
4
|
-
BaseCommonDB,
|
|
3
|
+
import type {
|
|
5
4
|
CommonDB,
|
|
6
5
|
CommonDBCreateOptions,
|
|
7
|
-
commonDBFullSupport,
|
|
8
6
|
CommonDBOptions,
|
|
9
7
|
CommonDBSaveOptions,
|
|
10
8
|
CommonDBSupport,
|
|
11
|
-
CommonDBType,
|
|
12
|
-
DBQuery,
|
|
13
9
|
RunQueryResult,
|
|
14
10
|
} from '@naturalcycles/db-lib'
|
|
11
|
+
import { BaseCommonDB, commonDBFullSupport, CommonDBType, DBQuery } from '@naturalcycles/db-lib'
|
|
12
|
+
import type {
|
|
13
|
+
CommonLogger,
|
|
14
|
+
JsonSchemaObject,
|
|
15
|
+
JsonSchemaRootObject,
|
|
16
|
+
ObjectWithId,
|
|
17
|
+
} from '@naturalcycles/js-lib'
|
|
15
18
|
import {
|
|
16
19
|
_assert,
|
|
17
20
|
_filterUndefinedValues,
|
|
@@ -19,14 +22,11 @@ import {
|
|
|
19
22
|
_mapValues,
|
|
20
23
|
_Memo,
|
|
21
24
|
_omit,
|
|
22
|
-
CommonLogger,
|
|
23
25
|
commonLoggerPrefix,
|
|
24
|
-
JsonSchemaObject,
|
|
25
|
-
JsonSchemaRootObject,
|
|
26
|
-
ObjectWithId,
|
|
27
26
|
} from '@naturalcycles/js-lib'
|
|
28
|
-
import { ReadableTyped
|
|
29
|
-
import {
|
|
27
|
+
import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
28
|
+
import { white } from '@naturalcycles/nodejs-lib'
|
|
29
|
+
import type {
|
|
30
30
|
Connection,
|
|
31
31
|
OkPacket,
|
|
32
32
|
Pool,
|
|
@@ -36,13 +36,18 @@ import {
|
|
|
36
36
|
TypeCast,
|
|
37
37
|
} from 'mysql'
|
|
38
38
|
import * as mysql from 'mysql'
|
|
39
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
dbQueryToSQLDelete,
|
|
41
|
+
dbQueryToSQLSelect,
|
|
42
|
+
dbQueryToSQLUpdate,
|
|
43
|
+
insertSQL,
|
|
44
|
+
} from './query.util.js'
|
|
45
|
+
import type { MySQLTableStats } from './schema/mysql.schema.util.js'
|
|
40
46
|
import {
|
|
41
47
|
jsonSchemaToMySQLDDL,
|
|
42
48
|
mapNameFromMySQL,
|
|
43
|
-
MySQLTableStats,
|
|
44
49
|
mysqlTableStatsToJsonSchemaField,
|
|
45
|
-
} from './schema/mysql.schema.util'
|
|
50
|
+
} from './schema/mysql.schema.util.js'
|
|
46
51
|
|
|
47
52
|
export interface MysqlDBOptions extends CommonDBOptions {}
|
|
48
53
|
export interface MysqlDBSaveOptions<ROW extends ObjectWithId> extends CommonDBSaveOptions<ROW> {}
|
|
@@ -277,10 +282,21 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
|
|
|
277
282
|
|
|
278
283
|
if (this.cfg.logSQL) this.cfg.logger.log(`stream: ${sql}`)
|
|
279
284
|
|
|
280
|
-
//
|
|
281
|
-
return (this.pool().query(sql).stream() as ReadableTyped<ROW>).map(row =>
|
|
282
|
-
|
|
283
|
-
)
|
|
285
|
+
// todo: this is nice, but `mysql` package uses `readable-stream@2` which is not compatible with `node:stream` iterable helpers
|
|
286
|
+
// return (this.pool().query(sql).stream() as ReadableTyped<ROW>).map(row =>
|
|
287
|
+
// _filterUndefinedValues(row, true),
|
|
288
|
+
// )
|
|
289
|
+
return this.pool()
|
|
290
|
+
.query(sql)
|
|
291
|
+
.stream()
|
|
292
|
+
.pipe(
|
|
293
|
+
new Transform({
|
|
294
|
+
objectMode: true,
|
|
295
|
+
transform(row: ROW, _encoding, cb) {
|
|
296
|
+
cb(null, _filterUndefinedValues(row, true))
|
|
297
|
+
},
|
|
298
|
+
}),
|
|
299
|
+
)
|
|
284
300
|
}
|
|
285
301
|
|
|
286
302
|
// SAVE
|
package/src/mysqlKeyValueDB.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Transform } from 'node:stream'
|
|
2
|
+
import type {
|
|
2
3
|
CommonDBCreateOptions,
|
|
3
4
|
CommonKeyValueDB,
|
|
4
|
-
commonKeyValueDBFullSupport,
|
|
5
5
|
KeyValueDBTuple,
|
|
6
6
|
} from '@naturalcycles/db-lib'
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
7
|
+
import { commonKeyValueDBFullSupport } from '@naturalcycles/db-lib'
|
|
8
|
+
import type { IncrementTuple } from '@naturalcycles/db-lib/dist/kv/commonKeyValueDB.js'
|
|
9
|
+
import type { ObjectWithId } from '@naturalcycles/js-lib'
|
|
10
|
+
import { AppError, pMap } from '@naturalcycles/js-lib'
|
|
11
|
+
import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
12
|
+
import type { QueryOptions } from 'mysql'
|
|
13
|
+
import type { MysqlDBCfg } from './mysql.db.js'
|
|
14
|
+
import { MysqlDB } from './mysql.db.js'
|
|
12
15
|
|
|
13
16
|
interface KeyValueObject {
|
|
14
17
|
id: string
|
|
@@ -88,7 +91,20 @@ export class MySQLKeyValueDB implements CommonKeyValueDB {
|
|
|
88
91
|
if (limit) sql += ` LIMIT ${limit}`
|
|
89
92
|
if (this.cfg.logSQL) this.db.cfg.logger.log(`stream: ${sql}`)
|
|
90
93
|
|
|
91
|
-
|
|
94
|
+
// todo: this is nice, but `mysql` package uses `readable-stream@2` which is not compatible with `node:stream` iterable helpers
|
|
95
|
+
// return (this.db.pool().query(sql).stream() as ReadableTyped<ObjectWithId>).map(row => row.id)
|
|
96
|
+
return this.db
|
|
97
|
+
.pool()
|
|
98
|
+
.query(sql)
|
|
99
|
+
.stream()
|
|
100
|
+
.pipe(
|
|
101
|
+
new Transform({
|
|
102
|
+
objectMode: true,
|
|
103
|
+
transform(row: ObjectWithId, _encoding, cb) {
|
|
104
|
+
cb(null, row.id)
|
|
105
|
+
},
|
|
106
|
+
}),
|
|
107
|
+
)
|
|
92
108
|
}
|
|
93
109
|
|
|
94
110
|
streamValues(table: string, limit?: number): ReadableTyped<Buffer> {
|
package/src/query.util.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { DBQuery, DBQueryFilterOperator } from '@naturalcycles/db-lib'
|
|
2
|
-
import {
|
|
1
|
+
import type { DBQuery, DBQueryFilterOperator } from '@naturalcycles/db-lib'
|
|
2
|
+
import type { AnyObjectWithId, CommonLogger } from '@naturalcycles/js-lib'
|
|
3
|
+
import { _hb } from '@naturalcycles/js-lib'
|
|
3
4
|
import { white, yellow } from '@naturalcycles/nodejs-lib'
|
|
4
|
-
import { QueryOptions } from 'mysql'
|
|
5
|
+
import type { QueryOptions } from 'mysql'
|
|
5
6
|
import * as mysql from 'mysql'
|
|
6
|
-
import { mapNameToMySQL } from './schema/mysql.schema.util'
|
|
7
|
+
import { mapNameToMySQL } from './schema/mysql.schema.util.js'
|
|
7
8
|
|
|
8
9
|
const MAX_PACKET_SIZE = 1024 * 1024 // 1Mb
|
|
9
10
|
const MAX_ROW_SIZE = 800 * 1024 // 1Mb - margin
|