@naturalcycles/mysql-lib 3.9.1 → 3.9.2

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/mysql.db.js CHANGED
@@ -75,7 +75,7 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
75
75
  async close() {
76
76
  const pool = this.pool();
77
77
  // eslint-disable-next-line @typescript-eslint/await-thenable
78
- await (0, util_1.promisify)(pool.end.bind(pool));
78
+ await (0, util_1.promisify)(pool.end.bind(pool))();
79
79
  this.cfg.logger.log('closed');
80
80
  }
81
81
  /**
@@ -116,14 +116,20 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
116
116
  // QUERY
117
117
  async runQuery(q, _opt = {}) {
118
118
  const sql = (0, query_util_1.dbQueryToSQLSelect)(q);
119
- const rows = await this.runSQL({ sql });
119
+ const rows = (await this.runSQL({ sql })).map(row => (0, js_lib_1._mapKeys)((0, js_lib_1._filterUndefinedValues)(row, true), k => (0, mysql_schema_util_1.mapNameFromMySQL)(k)));
120
+ // edge case where 0 fields are selected
121
+ if (q._selectedFieldNames?.length === 0) {
122
+ return {
123
+ rows: rows.map(_ => ({})),
124
+ };
125
+ }
120
126
  return {
121
- rows: rows.map(r => (0, js_lib_1._mapKeys)((0, js_lib_1._filterNullishValues)(r, true), k => (0, mysql_schema_util_1.mapNameFromMySQL)(k))),
127
+ rows,
122
128
  };
123
129
  }
124
130
  async runSQL(q) {
125
131
  if (this.cfg.logSQL)
126
- this.cfg.logger.log(q.sql, q.values);
132
+ this.cfg.logger.log(...[q.sql, q.values].filter(Boolean));
127
133
  return await new Promise((resolve, reject) => {
128
134
  this.pool().query(q, (err, res) => {
129
135
  if (err)
@@ -147,7 +153,7 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
147
153
  .pipe(new stream_1.Transform({
148
154
  objectMode: true,
149
155
  transform(row, _encoding, cb) {
150
- cb(null, (0, js_lib_1._filterNullishValues)(row, true));
156
+ cb(null, (0, js_lib_1._filterUndefinedValues)(row, true));
151
157
  },
152
158
  }));
153
159
  }
@@ -156,11 +162,11 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
156
162
  if (!_rows.length)
157
163
  return;
158
164
  // Stringify object values
159
- const rows = _rows.map(row => (0, js_lib_1._mapValues)(row, v => {
165
+ const rows = _rows.map(row => (0, js_lib_1._mapValues)(row, (_k, v) => {
160
166
  return v && typeof v === 'object' && !Buffer.isBuffer(v) ? JSON.stringify(v) : v;
161
167
  }));
162
168
  // inserts are split into multiple sentenses to respect the max_packet_size (1Mb usually)
163
- const sqls = (0, query_util_1.insertSQL)(table, rows, 'INSERT', this.cfg.logger);
169
+ const sqls = (0, query_util_1.insertSQL)(table, rows, 'REPLACE', this.cfg.logger);
164
170
  for await (const sql of sqls) {
165
171
  await this.runSQL({ sql });
166
172
  }
@@ -6,7 +6,7 @@ export declare function dbQueryToSQLDelete(q: DBQuery<any>): string;
6
6
  /**
7
7
  * Returns array of sql statements to respect the max sql size.
8
8
  */
9
- export declare function insertSQL(table: string, records: Record<any, any>[], verb?: 'INSERT' | 'REPLACE', logger?: CommonLogger): string[];
9
+ export declare function insertSQL(table: string, rows: Record<any, any>[], verb?: 'INSERT' | 'REPLACE', logger?: CommonLogger): string[];
10
10
  export declare function insertSQLSingle(table: string, record: Record<any, any>): string;
11
11
  export declare function insertSQLSetSingle(table: string, record: Record<any, any>): QueryOptions;
12
12
  export declare function dbQueryToSQLUpdate(q: DBQuery<any>, record: Record<any, any>): string;
@@ -30,12 +30,12 @@ exports.dbQueryToSQLDelete = dbQueryToSQLDelete;
30
30
  /**
31
31
  * Returns array of sql statements to respect the max sql size.
32
32
  */
33
- function insertSQL(table, records, verb = 'INSERT', logger = console) {
33
+ function insertSQL(table, rows, verb = 'INSERT', logger = console) {
34
34
  // INSERT INTO table_name (column1, column2, column3, ...)
35
35
  // VALUES (value1, value2, value3, ...);
36
36
  // eslint-disable-next-line unicorn/no-array-reduce
37
- const fieldSet = records.reduce((set, rec) => {
38
- Object.keys(rec).forEach(field => set.add(field));
37
+ const fieldSet = rows.reduce((set, row) => {
38
+ Object.keys(row).forEach(field => set.add(field));
39
39
  return set;
40
40
  }, new Set());
41
41
  const fields = [...fieldSet];
@@ -46,7 +46,7 @@ function insertSQL(table, records, verb = 'INSERT', logger = console) {
46
46
  `(` + [...fields].map(f => mysql.escapeId((0, mysql_schema_util_1.mapNameToMySQL)(f))).join(',') + `)`,
47
47
  `VALUES\n`,
48
48
  ].join(' ');
49
- const valueRows = records.map(rec => {
49
+ const valueRows = rows.map(rec => {
50
50
  return `(` + fields.map(k => mysql.escape(rec[k])).join(',') + `)`;
51
51
  });
52
52
  const full = start + valueRows.join(',\n');
@@ -80,7 +80,7 @@ function mysqlTableStatsToJsonSchemaField(table, stats, logger) {
80
80
  else if (t.startsWith('tinyint') || t.includes('(1)')) {
81
81
  s.properties[name] = { type: 'boolean' };
82
82
  }
83
- else if (t.startsWith('int(')) {
83
+ else if (t === 'int' || t.startsWith('int(')) {
84
84
  s.properties[name] = { type: 'integer' };
85
85
  }
86
86
  else if (t.startsWith('float')) {
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "devDependencies": {
14
14
  "@naturalcycles/dev-lib": "^12.7.5",
15
- "@types/node": "^16.10.2",
15
+ "@types/node": "^17.0.5",
16
16
  "dotenv": "^10.0.0",
17
17
  "jest": "^27.2.4"
18
18
  },
@@ -36,7 +36,7 @@
36
36
  "engines": {
37
37
  "node": ">=14.15.0"
38
38
  },
39
- "version": "3.9.1",
39
+ "version": "3.9.2",
40
40
  "description": "MySQL client implementing CommonDB interface",
41
41
  "keywords": [
42
42
  "mysql",
package/readme.md CHANGED
@@ -3,12 +3,33 @@
3
3
  > MySQL client implementing CommonDB interface
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@naturalcycles/mysql-lib/latest.svg)](https://www.npmjs.com/package/@naturalcycles/mysql-lib)
6
- [![](https://circleci.com/gh/NaturalCycles/mysql-lib.svg?style=shield&circle-token=123)](https://circleci.com/gh/NaturalCycles/mysql-lib)
7
6
  [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
8
7
 
9
- # Packaging
8
+ # Quick start
10
9
 
11
- - `engines.node >= 10.13`: Latest Node.js LTS
12
- - `main: dist/index.js`: commonjs, es2018
13
- - `types: dist/index.d.ts`: typescript types
14
- - `/src` folder with source `*.ts` files included
10
+ todo
11
+
12
+ # Develop
13
+
14
+ Install MySQL server+client on Mac:
15
+
16
+ ```shell
17
+
18
+ brew install mysql
19
+
20
+ # start
21
+
22
+ mysql.server start
23
+
24
+ # secure
25
+
26
+ mysql_secure_installation
27
+
28
+ # connect
29
+
30
+ mysql -uroot
31
+
32
+ # Default options are read from the following files in the given order:
33
+ # /etc/my.cnf /etc/mysql/my.cnf /opt/homebrew/etc/my.cnf ~/.my.cnf
34
+
35
+ ```
package/src/mysql.db.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  RunQueryResult,
11
11
  } from '@naturalcycles/db-lib'
12
12
  import {
13
- _filterNullishValues,
13
+ _filterUndefinedValues,
14
14
  _mapKeys,
15
15
  _mapValues,
16
16
  _Memo,
@@ -136,7 +136,7 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
136
136
  async close(): Promise<void> {
137
137
  const pool = this.pool()
138
138
  // eslint-disable-next-line @typescript-eslint/await-thenable
139
- await promisify(pool.end.bind(pool))
139
+ await promisify(pool.end.bind(pool))()
140
140
  this.cfg.logger.log('closed')
141
141
  }
142
142
 
@@ -191,14 +191,24 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
191
191
  _opt: MysqlDBOptions = {},
192
192
  ): Promise<RunQueryResult<OUT>> {
193
193
  const sql = dbQueryToSQLSelect(q)
194
- const rows = await this.runSQL<ROW[]>({ sql })
194
+ const rows = (await this.runSQL<ROW[]>({ sql })).map(
195
+ row => _mapKeys(_filterUndefinedValues(row, true), k => mapNameFromMySQL(k)) as any,
196
+ )
197
+
198
+ // edge case where 0 fields are selected
199
+ if (q._selectedFieldNames?.length === 0) {
200
+ return {
201
+ rows: rows.map(_ => ({} as any)),
202
+ }
203
+ }
204
+
195
205
  return {
196
- rows: rows.map(r => _mapKeys(_filterNullishValues(r, true), k => mapNameFromMySQL(k)) as any),
206
+ rows,
197
207
  }
198
208
  }
199
209
 
200
210
  async runSQL<RESULT>(q: QueryOptions): Promise<RESULT> {
201
- if (this.cfg.logSQL) this.cfg.logger.log(q.sql, q.values)
211
+ if (this.cfg.logSQL) this.cfg.logger.log(...[q.sql, q.values].filter(Boolean))
202
212
 
203
213
  return await new Promise<RESULT>((resolve, reject) => {
204
214
  this.pool().query(q, (err, res) => {
@@ -232,7 +242,7 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
232
242
  new Transform({
233
243
  objectMode: true,
234
244
  transform(row, _encoding, cb) {
235
- cb(null, _filterNullishValues(row, true))
245
+ cb(null, _filterUndefinedValues(row, true))
236
246
  },
237
247
  }),
238
248
  )
@@ -248,13 +258,13 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
248
258
 
249
259
  // Stringify object values
250
260
  const rows = _rows.map(row =>
251
- _mapValues(row, v => {
261
+ _mapValues(row, (_k, v) => {
252
262
  return v && typeof v === 'object' && !Buffer.isBuffer(v) ? JSON.stringify(v) : v
253
263
  }),
254
264
  )
255
265
 
256
266
  // inserts are split into multiple sentenses to respect the max_packet_size (1Mb usually)
257
- const sqls = insertSQL(table, rows, 'INSERT', this.cfg.logger)
267
+ const sqls = insertSQL(table, rows, 'REPLACE', this.cfg.logger)
258
268
 
259
269
  for await (const sql of sqls) {
260
270
  await this.runSQL({ sql })
package/src/query.util.ts CHANGED
@@ -41,7 +41,7 @@ export function dbQueryToSQLDelete(q: DBQuery<any>): string {
41
41
  */
42
42
  export function insertSQL(
43
43
  table: string,
44
- records: Record<any, any>[],
44
+ rows: Record<any, any>[],
45
45
  verb: 'INSERT' | 'REPLACE' = 'INSERT',
46
46
  logger: CommonLogger = console,
47
47
  ): string[] {
@@ -49,8 +49,8 @@ export function insertSQL(
49
49
  // VALUES (value1, value2, value3, ...);
50
50
 
51
51
  // eslint-disable-next-line unicorn/no-array-reduce
52
- const fieldSet = records.reduce((set: Set<string>, rec) => {
53
- Object.keys(rec).forEach(field => set.add(field))
52
+ const fieldSet = rows.reduce((set: Set<string>, row) => {
53
+ Object.keys(row).forEach(field => set.add(field))
54
54
  return set
55
55
  }, new Set<string>())
56
56
  const fields = [...fieldSet]
@@ -63,7 +63,7 @@ export function insertSQL(
63
63
  `VALUES\n`,
64
64
  ].join(' ')
65
65
 
66
- const valueRows = records.map(rec => {
66
+ const valueRows = rows.map(rec => {
67
67
  return `(` + fields.map(k => mysql.escape(rec[k])).join(',') + `)`
68
68
  })
69
69
 
@@ -106,7 +106,7 @@ export function mysqlTableStatsToJsonSchemaField<T = any>(
106
106
  s.properties[name] = { instanceof: 'Buffer' }
107
107
  } else if (t.startsWith('tinyint') || t.includes('(1)')) {
108
108
  s.properties[name] = { type: 'boolean' } as JsonSchemaBoolean
109
- } else if (t.startsWith('int(')) {
109
+ } else if (t === 'int' || t.startsWith('int(')) {
110
110
  s.properties[name] = { type: 'integer' } as JsonSchemaNumber
111
111
  } else if (t.startsWith('float')) {
112
112
  s.properties[name] = { type: 'number' } as JsonSchemaNumber