@naturalcycles/mysql-lib 3.12.3 → 3.13.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.
@@ -1,3 +1,4 @@
1
+ import { DBPatch } from '@naturalcycles/db-lib';
1
2
  import { BaseCommonDB, CommonDB, CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, DBQuery, RunQueryResult } from '@naturalcycles/db-lib';
2
3
  import { AnyObjectWithId, CommonLogger, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
3
4
  import { ReadableTyped } from '@naturalcycles/nodejs-lib';
@@ -66,4 +67,5 @@ export declare class MysqlDB extends BaseCommonDB implements CommonDB {
66
67
  createTable<ROW extends ObjectWithId>(table: string, schema: JsonSchemaObject<ROW>, opt?: CommonDBCreateOptions): Promise<void>;
67
68
  getTables(): Promise<string[]>;
68
69
  getTableSchema<ROW extends ObjectWithId>(table: string): Promise<JsonSchemaRootObject<ROW>>;
70
+ updateByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: DBPatch<ROW>): Promise<number>;
69
71
  }
package/dist/mysql.db.js CHANGED
@@ -74,7 +74,6 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
74
74
  }
75
75
  async close() {
76
76
  const pool = this.pool();
77
- // eslint-disable-next-line @typescript-eslint/await-thenable
78
77
  await (0, node_util_1.promisify)(pool.end.bind(pool))();
79
78
  this.cfg.logger.log('closed');
80
79
  }
@@ -184,10 +183,15 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
184
183
  return;
185
184
  // Stringify object values
186
185
  const rows = rowsInput.map(row => (0, js_lib_1._mapValues)(row, (_k, v) => {
187
- return v && typeof v === 'object' && !Buffer.isBuffer(v) ? JSON.stringify(v) : v;
186
+ if (v && typeof v === 'object' && !Buffer.isBuffer(v)) {
187
+ // This is to avoid implicit Date stringification and mismatch: it gets saved as Date, but loaded as String
188
+ (0, js_lib_1._assert)(!(v instanceof Date), 'mysql-lib does not support Date values, please stringify them before passing');
189
+ return JSON.stringify(v);
190
+ }
191
+ return v;
188
192
  }));
189
193
  const verb = opt.saveMethod === 'insert' ? 'INSERT' : 'REPLACE';
190
- if (opt.assignGeneratedIds) {
194
+ if (opt.assignGeneratedIds && opt.saveMethod !== 'update') {
191
195
  // Insert rows one-by-one, to get their auto-generated id
192
196
  let i = -1;
193
197
  for await (const row of rows) {
@@ -200,9 +204,18 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
200
204
  return;
201
205
  }
202
206
  // inserts are split into multiple sentenses to respect the max_packet_size (1Mb usually)
203
- const sqls = (0, query_util_1.insertSQL)(table, rows, verb, this.cfg.logger);
204
- for await (const sql of sqls) {
205
- await this.runSQL({ sql });
207
+ if (opt.saveMethod === 'update') {
208
+ for await (const row of rows) {
209
+ (0, js_lib_1._assert)(row.id, 'Cannot update without providing an id');
210
+ const query = new db_lib_1.DBQuery(table).filterEq('id', row.id);
211
+ await this.updateByQuery(query, (0, js_lib_1._omit)(row, ['id']));
212
+ }
213
+ }
214
+ else {
215
+ const sqls = (0, query_util_1.insertSQL)(table, rows, verb, this.cfg.logger);
216
+ for await (const sql of sqls) {
217
+ await this.runSQL({ sql });
218
+ }
206
219
  }
207
220
  }
208
221
  // DELETE
@@ -251,6 +264,13 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
251
264
  });
252
265
  return (0, mysql_schema_util_1.mysqlTableStatsToJsonSchemaField)(table, stats, this.cfg.logger);
253
266
  }
267
+ async updateByQuery(q, patch) {
268
+ const sql = (0, query_util_1.dbQueryToSQLUpdate)(q, patch);
269
+ if (!sql)
270
+ return 0;
271
+ const { affectedRows } = await this.runSQL({ sql });
272
+ return affectedRows;
273
+ }
254
274
  }
255
275
  tslib_1.__decorate([
256
276
  (0, js_lib_1._Memo)()
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MySQLKeyValueDB = void 0;
4
- const stream_1 = require("stream");
4
+ const node_stream_1 = require("node:stream");
5
5
  const js_lib_1 = require("@naturalcycles/js-lib");
6
6
  const mysql_db_1 = require("./mysql.db");
7
7
  class MySQLKeyValueDB {
@@ -67,7 +67,7 @@ class MySQLKeyValueDB {
67
67
  .pool()
68
68
  .query(sql)
69
69
  .stream()
70
- .pipe(new stream_1.Transform({
70
+ .pipe(new node_stream_1.Transform({
71
71
  objectMode: true,
72
72
  transform(row, _, cb) {
73
73
  cb(null, row.id);
@@ -84,7 +84,7 @@ class MySQLKeyValueDB {
84
84
  .pool()
85
85
  .query(sql)
86
86
  .stream()
87
- .pipe(new stream_1.Transform({
87
+ .pipe(new node_stream_1.Transform({
88
88
  objectMode: true,
89
89
  transform(row, _, cb) {
90
90
  cb(null, row.v);
@@ -101,7 +101,7 @@ class MySQLKeyValueDB {
101
101
  .pool()
102
102
  .query(sql)
103
103
  .stream()
104
- .pipe(new stream_1.Transform({
104
+ .pipe(new node_stream_1.Transform({
105
105
  objectMode: true,
106
106
  transform(row, _, cb) {
107
107
  cb(null, [row.id, row.v]);
package/package.json CHANGED
@@ -34,9 +34,9 @@
34
34
  "url": "https://github.com/NaturalCycles/mysql-lib"
35
35
  },
36
36
  "engines": {
37
- "node": ">=14.15.0"
37
+ "node": ">=16.15.0"
38
38
  },
39
- "version": "3.12.3",
39
+ "version": "3.13.0",
40
40
  "description": "MySQL client implementing CommonDB interface",
41
41
  "keywords": [
42
42
  "mysql",
package/src/mysql.db.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Readable, Transform } from 'node:stream'
2
2
  import { promisify } from 'node:util'
3
+ import { DBPatch } from '@naturalcycles/db-lib'
3
4
  import {
4
5
  BaseCommonDB,
5
6
  CommonDB,
@@ -10,10 +11,12 @@ import {
10
11
  RunQueryResult,
11
12
  } from '@naturalcycles/db-lib'
12
13
  import {
14
+ _assert,
13
15
  _filterUndefinedValues,
14
16
  _mapKeys,
15
17
  _mapValues,
16
18
  _Memo,
19
+ _omit,
17
20
  AnyObjectWithId,
18
21
  CommonLogger,
19
22
  commonLoggerPrefix,
@@ -33,7 +36,7 @@ import {
33
36
  TypeCast,
34
37
  } from 'mysql'
35
38
  import * as mysql from 'mysql'
36
- import { dbQueryToSQLDelete, dbQueryToSQLSelect, insertSQL } from './query.util'
39
+ import { dbQueryToSQLDelete, dbQueryToSQLSelect, dbQueryToSQLUpdate, insertSQL } from './query.util'
37
40
  import {
38
41
  jsonSchemaToMySQLDDL,
39
42
  mapNameFromMySQL,
@@ -143,7 +146,7 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
143
146
 
144
147
  async close(): Promise<void> {
145
148
  const pool = this.pool()
146
- // eslint-disable-next-line @typescript-eslint/await-thenable
149
+
147
150
  await promisify(pool.end.bind(pool))()
148
151
  this.cfg.logger.log('closed')
149
152
  }
@@ -291,13 +294,21 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
291
294
  // Stringify object values
292
295
  const rows = rowsInput.map(row =>
293
296
  _mapValues(row, (_k, v) => {
294
- return v && typeof v === 'object' && !Buffer.isBuffer(v) ? JSON.stringify(v) : v
297
+ if (v && typeof v === 'object' && !Buffer.isBuffer(v)) {
298
+ // This is to avoid implicit Date stringification and mismatch: it gets saved as Date, but loaded as String
299
+ _assert(
300
+ !(v instanceof Date),
301
+ 'mysql-lib does not support Date values, please stringify them before passing',
302
+ )
303
+ return JSON.stringify(v)
304
+ }
305
+ return v
295
306
  }),
296
307
  )
297
308
 
298
309
  const verb = opt.saveMethod === 'insert' ? 'INSERT' : 'REPLACE'
299
310
 
300
- if (opt.assignGeneratedIds) {
311
+ if (opt.assignGeneratedIds && opt.saveMethod !== 'update') {
301
312
  // Insert rows one-by-one, to get their auto-generated id
302
313
 
303
314
  let i = -1
@@ -314,10 +325,18 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
314
325
  }
315
326
 
316
327
  // inserts are split into multiple sentenses to respect the max_packet_size (1Mb usually)
317
- const sqls = insertSQL(table, rows, verb, this.cfg.logger)
328
+ if (opt.saveMethod === 'update') {
329
+ for await (const row of rows) {
330
+ _assert(row.id, 'Cannot update without providing an id')
331
+ const query = new DBQuery(table).filterEq('id', row.id)
332
+ await this.updateByQuery(query, _omit(row, ['id']))
333
+ }
334
+ } else {
335
+ const sqls = insertSQL(table, rows, verb, this.cfg.logger)
318
336
 
319
- for await (const sql of sqls) {
320
- await this.runSQL({ sql })
337
+ for await (const sql of sqls) {
338
+ await this.runSQL({ sql })
339
+ }
321
340
  }
322
341
  }
323
342
 
@@ -384,4 +403,15 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
384
403
 
385
404
  return mysqlTableStatsToJsonSchemaField<ROW>(table, stats, this.cfg.logger)
386
405
  }
406
+
407
+ override async updateByQuery<ROW extends ObjectWithId>(
408
+ q: DBQuery<ROW>,
409
+ patch: DBPatch<ROW>,
410
+ ): Promise<number> {
411
+ const sql = dbQueryToSQLUpdate(q, patch)
412
+ if (!sql) return 0
413
+
414
+ const { affectedRows } = await this.runSQL<OkPacket>({ sql })
415
+ return affectedRows
416
+ }
387
417
  }
@@ -1,4 +1,4 @@
1
- import { Transform } from 'stream'
1
+ import { Transform } from 'node:stream'
2
2
  import { CommonDBCreateOptions, CommonKeyValueDB, KeyValueDBTuple } from '@naturalcycles/db-lib'
3
3
  import { pMap } from '@naturalcycles/js-lib'
4
4
  import { ReadableTyped } from '@naturalcycles/nodejs-lib'