@naturalcycles/mysql-lib 3.8.1 → 3.10.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 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; } });
@@ -1,8 +1,7 @@
1
- import { AnyObjectWithId } from '@naturalcycles/db-lib/src/db.model';
2
- import { BaseCommonDB, CommonDB, CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, DBQuery, ObjectWithId, RunQueryResult } from '@naturalcycles/db-lib';
3
- import { JsonSchemaObject, JsonSchemaRootObject } from '@naturalcycles/js-lib';
1
+ import { BaseCommonDB, CommonDB, CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, DBQuery, RunQueryResult } from '@naturalcycles/db-lib';
2
+ import { AnyObjectWithId, CommonLogger, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
4
3
  import { ReadableTyped } from '@naturalcycles/nodejs-lib';
5
- import { Connection, Pool, PoolConfig, PoolConnection, QueryOptions } from 'mysql';
4
+ import { Connection, Pool, PoolConfig, PoolConnection, QueryOptions, TypeCast } from 'mysql';
6
5
  export interface MysqlDBOptions extends CommonDBOptions {
7
6
  }
8
7
  export interface MysqlDBSaveOptions<ROW extends ObjectWithId = AnyObjectWithId> extends CommonDBSaveOptions<ROW> {
@@ -19,10 +18,17 @@ export interface MysqlDBCfg extends PoolConfig {
19
18
  * If true - will emit logs of connection events.
20
19
  */
21
20
  debugConnections?: boolean;
21
+ /**
22
+ * Default to `console`
23
+ */
24
+ logger?: CommonLogger;
22
25
  }
26
+ export declare const typeCast: TypeCast;
23
27
  export declare class MysqlDB extends BaseCommonDB implements CommonDB {
24
28
  constructor(cfg?: MysqlDBCfg);
25
- cfg: MysqlDBCfg;
29
+ cfg: MysqlDBCfg & {
30
+ logger: CommonLogger;
31
+ };
26
32
  ping(): Promise<void>;
27
33
  pool(): Pool;
28
34
  close(): Promise<void>;
package/dist/mysql.db.js CHANGED
@@ -1,17 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MysqlDB = void 0;
3
+ exports.MysqlDB = exports.typeCast = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const stream_1 = require("stream");
6
6
  const util_1 = require("util");
7
7
  const db_lib_1 = require("@naturalcycles/db-lib");
8
8
  const js_lib_1 = require("@naturalcycles/js-lib");
9
- const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
10
9
  const colors_1 = require("@naturalcycles/nodejs-lib/dist/colors");
11
10
  const mysql = require("mysql");
12
11
  const query_util_1 = require("./query.util");
13
12
  const mysql_schema_util_1 = require("./schema/mysql.schema.util");
14
- const log = (0, nodejs_lib_1.Debug)('nc:mysql-lib');
15
13
  const BOOLEAN_TYPES = new Set(['TINY', 'TINYINT', 'INT']);
16
14
  const BOOLEAN_BIT_TYPES = new Set(['BIT']);
17
15
  const typeCast = (field, next) => {
@@ -33,17 +31,19 @@ const typeCast = (field, next) => {
33
31
  }
34
32
  return next();
35
33
  };
34
+ exports.typeCast = typeCast;
36
35
  class MysqlDB extends db_lib_1.BaseCommonDB {
37
36
  constructor(cfg = {}) {
38
37
  super();
39
38
  this.cfg = {
40
- typeCast,
39
+ typeCast: exports.typeCast,
41
40
  // charset: 'utf8mb4', // for emoji support
42
41
  // host: 'localhost',
43
42
  // user: MYSQL_USER,
44
43
  // password: MYSQL_PW,
45
44
  // database: MYSQL_DB,
46
45
  ...cfg,
46
+ logger: (0, js_lib_1.commonLoggerPrefix)(cfg.logger || console, '[mysql]'),
47
47
  };
48
48
  }
49
49
  async ping() {
@@ -52,31 +52,32 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
52
52
  }
53
53
  pool() {
54
54
  const pool = mysql.createPool(this.cfg);
55
- const { host, database } = this.cfg;
56
- log(`connected to ${(0, colors_1.white)(host + '/' + database)}`);
55
+ const { host, database, logger } = this.cfg;
56
+ logger.log(`connected to ${(0, colors_1.white)(host + '/' + database)}`);
57
57
  if (this.cfg.debugConnections) {
58
58
  pool.on('acquire', con => {
59
- log(`acquire(${con.threadId})`);
59
+ logger.log(`acquire(${con.threadId})`);
60
60
  });
61
61
  pool.on('connection', con => {
62
- log(`connection(${con.threadId})`);
62
+ logger.log(`connection(${con.threadId})`);
63
63
  });
64
64
  pool.on('enqueue', () => {
65
- log(`enqueue`);
65
+ logger.log(`enqueue`);
66
66
  });
67
67
  pool.on('release', con => {
68
- log(`release(${con.threadId})`);
68
+ logger.log(`release(${con.threadId})`);
69
69
  });
70
70
  }
71
71
  pool.on('error', err => {
72
- log.error(`error`, err);
72
+ logger.error(err);
73
73
  });
74
74
  return pool;
75
75
  }
76
76
  async close() {
77
77
  const pool = this.pool();
78
78
  // eslint-disable-next-line @typescript-eslint/await-thenable
79
- await (0, util_1.promisify)(pool.end.bind(pool));
79
+ await (0, util_1.promisify)(pool.end.bind(pool))();
80
+ this.cfg.logger.log('closed');
80
81
  }
81
82
  /**
82
83
  * Be careful to always call `con.release()` when you get connection with this method.
@@ -94,14 +95,14 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
94
95
  await (0, util_1.promisify)(con.connect.bind(con))();
95
96
  const { threadId } = con;
96
97
  if (this.cfg.debugConnections) {
97
- log(`createSingleConnection(${threadId})`);
98
- con.on('connect', () => log(`createSingleConnection(${threadId}).connect`));
99
- con.on('drain', () => log(`createSingleConnection(${threadId}).drain`));
100
- con.on('enqueue', () => log(`createSingleConnection(${threadId}).enqueue`));
101
- con.on('end', () => log(`createSingleConnection(${threadId}).end`));
98
+ this.cfg.logger.log(`createSingleConnection(${threadId})`);
99
+ con.on('connect', () => this.cfg.logger.log(`createSingleConnection(${threadId}).connect`));
100
+ con.on('drain', () => this.cfg.logger.log(`createSingleConnection(${threadId}).drain`));
101
+ con.on('enqueue', () => this.cfg.logger.log(`createSingleConnection(${threadId}).enqueue`));
102
+ con.on('end', () => this.cfg.logger.log(`createSingleConnection(${threadId}).end`));
102
103
  }
103
104
  con.on('error', err => {
104
- log.error(`createSingleConnection(${threadId}).error`, err);
105
+ this.cfg.logger.error(`createSingleConnection(${threadId}).error`, err);
105
106
  });
106
107
  return con;
107
108
  }
@@ -116,14 +117,20 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
116
117
  // QUERY
117
118
  async runQuery(q, _opt = {}) {
118
119
  const sql = (0, query_util_1.dbQueryToSQLSelect)(q);
119
- const rows = await this.runSQL({ sql });
120
+ 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)));
121
+ // edge case where 0 fields are selected
122
+ if (q._selectedFieldNames?.length === 0) {
123
+ return {
124
+ rows: rows.map(_ => ({})),
125
+ };
126
+ }
120
127
  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))),
128
+ rows,
122
129
  };
123
130
  }
124
131
  async runSQL(q) {
125
132
  if (this.cfg.logSQL)
126
- log(q.sql, q.values);
133
+ this.cfg.logger.log(...[q.sql, q.values].filter(Boolean));
127
134
  return await new Promise((resolve, reject) => {
128
135
  this.pool().query(q, (err, res) => {
129
136
  if (err)
@@ -139,7 +146,7 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
139
146
  streamQuery(q, _opt = {}) {
140
147
  const sql = (0, query_util_1.dbQueryToSQLSelect)(q);
141
148
  if (this.cfg.logSQL)
142
- log(`stream: ${sql}`);
149
+ this.cfg.logger.log(`stream: ${sql}`);
143
150
  // return this.streamSQL(sql, opt)
144
151
  return this.pool()
145
152
  .query(sql)
@@ -147,7 +154,7 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
147
154
  .pipe(new stream_1.Transform({
148
155
  objectMode: true,
149
156
  transform(row, _encoding, cb) {
150
- cb(null, (0, js_lib_1._filterNullishValues)(row, true));
157
+ cb(null, (0, js_lib_1._filterUndefinedValues)(row, true));
151
158
  },
152
159
  }));
153
160
  }
@@ -156,11 +163,11 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
156
163
  if (!_rows.length)
157
164
  return;
158
165
  // Stringify object values
159
- const rows = _rows.map(row => (0, js_lib_1._mapValues)(row, v => {
166
+ const rows = _rows.map(row => (0, js_lib_1._mapValues)(row, (_k, v) => {
160
167
  return v && typeof v === 'object' && !Buffer.isBuffer(v) ? JSON.stringify(v) : v;
161
168
  }));
162
169
  // inserts are split into multiple sentenses to respect the max_packet_size (1Mb usually)
163
- const sqls = (0, query_util_1.insertSQL)(table, rows);
170
+ const sqls = (0, query_util_1.insertSQL)(table, rows, 'REPLACE', this.cfg.logger);
164
171
  for await (const sql of sqls) {
165
172
  await this.runSQL({ sql });
166
173
  }
@@ -205,7 +212,7 @@ class MysqlDB extends db_lib_1.BaseCommonDB {
205
212
  const stats = await this.runSQL({
206
213
  sql: `describe ${mysql.escapeId(table)}`,
207
214
  });
208
- return (0, mysql_schema_util_1.mysqlTableStatsToJsonSchemaField)(table, stats);
215
+ return (0, mysql_schema_util_1.mysqlTableStatsToJsonSchemaField)(table, stats, this.cfg.logger);
209
216
  }
210
217
  }
211
218
  (0, tslib_1.__decorate)([
@@ -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
+ constructor(cfg?: MysqlDBCfg);
7
+ 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,128 @@
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 = {
10
+ typeCast: mysql_db_1.typeCast,
11
+ ...cfg,
12
+ };
13
+ this.db = new mysql_db_1.MysqlDB(this.cfg);
14
+ }
15
+ async ping() {
16
+ await this.db.ping();
17
+ }
18
+ async close() {
19
+ await this.db.close();
20
+ }
21
+ async createTable(table, opt = {}) {
22
+ if (opt.dropIfExists)
23
+ await this.dropTable(table);
24
+ // On blob sizes: https://tableplus.com/blog/2019/10/tinyblob-blob-mediumblob-longblob.html
25
+ // LONGBLOB supports up to 4gb
26
+ // MEDIUMBLOB up to 16Mb
27
+ const sql = `create table ${table} (id VARCHAR(64) PRIMARY KEY, v LONGBLOB NOT NULL)`;
28
+ this.db.cfg.logger.log(sql);
29
+ await this.db.runSQL({ sql });
30
+ }
31
+ async getByIds(table, ids) {
32
+ if (!ids.length)
33
+ return [];
34
+ const sql = `SELECT id,v FROM ${table} where id in (${ids.map(id => `"${id}"`).join(',')})`;
35
+ const rows = await this.db.runSQL({ sql });
36
+ return rows.map(({ id, v }) => [id, v]);
37
+ }
38
+ /**
39
+ * Use with caution!
40
+ */
41
+ async dropTable(table) {
42
+ await this.db.runSQL({ sql: `DROP TABLE IF EXISTS ${table}` });
43
+ }
44
+ async deleteByIds(table, ids) {
45
+ const sql = `DELETE FROM ${table} WHERE id in (${ids.map(id => `"${id}"`).join(',')})`;
46
+ if (this.cfg.logSQL)
47
+ this.db.cfg.logger.log(sql);
48
+ await this.db.runSQL({ sql });
49
+ }
50
+ async saveBatch(table, entries) {
51
+ const statements = entries.map(([id, buf]) => {
52
+ return {
53
+ sql: `INSERT INTO ${table} (id, v) VALUES (?, ?)`,
54
+ values: [id, buf],
55
+ };
56
+ });
57
+ await (0, js_lib_1.pMap)(statements, async (statement) => {
58
+ if (this.cfg.debug)
59
+ this.db.cfg.logger.log(statement.sql);
60
+ await this.db.runSQL(statement);
61
+ });
62
+ }
63
+ streamIds(table, limit) {
64
+ let sql = `SELECT id FROM ${table}`;
65
+ if (limit)
66
+ sql += ` LIMIT ${limit}`;
67
+ if (this.cfg.logSQL)
68
+ this.db.cfg.logger.log(`stream: ${sql}`);
69
+ return this.db
70
+ .pool()
71
+ .query(sql)
72
+ .stream()
73
+ .pipe(new stream_1.Transform({
74
+ objectMode: true,
75
+ transform(row, _, cb) {
76
+ cb(null, row.id);
77
+ },
78
+ }));
79
+ }
80
+ streamValues(table, limit) {
81
+ let sql = `SELECT v FROM ${table}`;
82
+ if (limit)
83
+ sql += ` LIMIT ${limit}`;
84
+ if (this.cfg.logSQL)
85
+ this.db.cfg.logger.log(`stream: ${sql}`);
86
+ return this.db
87
+ .pool()
88
+ .query(sql)
89
+ .stream()
90
+ .pipe(new stream_1.Transform({
91
+ objectMode: true,
92
+ transform(row, _, cb) {
93
+ cb(null, row.v);
94
+ },
95
+ }));
96
+ }
97
+ streamEntries(table, limit) {
98
+ let sql = `SELECT id,v FROM ${table}`;
99
+ if (limit)
100
+ sql += ` LIMIT ${limit}`;
101
+ if (this.cfg.logSQL)
102
+ this.db.cfg.logger.log(`stream: ${sql}`);
103
+ return this.db
104
+ .pool()
105
+ .query(sql)
106
+ .stream()
107
+ .pipe(new stream_1.Transform({
108
+ objectMode: true,
109
+ transform(row, _, cb) {
110
+ cb(null, [row.id, row.v]);
111
+ },
112
+ }));
113
+ }
114
+ async beginTransaction() {
115
+ await this.db.runSQL({ sql: `BEGIN TRANSACTION` });
116
+ }
117
+ async endTransaction() {
118
+ await this.db.runSQL({ sql: `END TRANSACTION` });
119
+ }
120
+ async count(table) {
121
+ const sql = `SELECT count(*) as cnt FROM ${table}`;
122
+ if (this.cfg.logSQL)
123
+ this.db.cfg.logger.log(sql);
124
+ const rows = await this.db.runSQL({ sql });
125
+ return rows[0].cnt;
126
+ }
127
+ }
128
+ exports.MySQLKeyValueDB = MySQLKeyValueDB;
@@ -1,11 +1,12 @@
1
1
  import { DBQuery } from '@naturalcycles/db-lib';
2
+ import { CommonLogger } from '@naturalcycles/js-lib';
2
3
  import { QueryOptions } from 'mysql';
3
4
  export declare function dbQueryToSQLSelect(q: DBQuery<any>): string;
4
5
  export declare function dbQueryToSQLDelete(q: DBQuery<any>): string;
5
6
  /**
6
7
  * Returns array of sql statements to respect the max sql size.
7
8
  */
8
- export declare function insertSQL(table: string, records: Record<any, any>[], verb?: 'INSERT' | 'REPLACE'): string[];
9
+ export declare function insertSQL(table: string, rows: Record<any, any>[], verb?: 'INSERT' | 'REPLACE', logger?: CommonLogger): string[];
9
10
  export declare function insertSQLSingle(table: string, record: Record<any, any>): string;
10
11
  export declare function insertSQLSetSingle(table: string, record: Record<any, any>): QueryOptions;
11
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') {
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') {
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');
@@ -71,7 +71,7 @@ function insertSQL(table, records, verb = 'INSERT') {
71
71
  if (sql) {
72
72
  sqls.push(sql); // last one
73
73
  }
74
- console.log(`${(0, colors_1.white)(table)} large sql query (${(0, colors_1.yellow)((0, js_lib_1._hb)(full.length))}) was split into ${(0, colors_1.yellow)(sqls.length)} chunks`);
74
+ logger.log(`${(0, colors_1.white)(table)} large sql query (${(0, colors_1.yellow)((0, js_lib_1._hb)(full.length))}) was split into ${(0, colors_1.yellow)(sqls.length)} chunks`);
75
75
  return sqls;
76
76
  // todo: handle "upsert" later
77
77
  }
@@ -1,4 +1,4 @@
1
- import { JsonSchemaObject, JsonSchemaRootObject } from '@naturalcycles/js-lib';
1
+ import { CommonLogger, JsonSchemaObject, JsonSchemaRootObject } from '@naturalcycles/js-lib';
2
2
  export interface MySQLTableStats {
3
3
  Field: string;
4
4
  Type: string;
@@ -14,7 +14,7 @@ export interface MySQLSchemaOptions {
14
14
  * It currently skips nullability and declares everything as "DEFAULT NULL".
15
15
  */
16
16
  export declare function jsonSchemaToMySQLDDL(table: string, schema: JsonSchemaObject<any>, opt?: MySQLSchemaOptions): string;
17
- export declare function mysqlTableStatsToJsonSchemaField<T = any>(table: string, stats: MySQLTableStats[]): JsonSchemaRootObject<T>;
17
+ export declare function mysqlTableStatsToJsonSchemaField<T = any>(table: string, stats: MySQLTableStats[], logger: CommonLogger): JsonSchemaRootObject<T>;
18
18
  /**
19
19
  * Because MySQL doesn't support `.` in field names and escapes them as tableName + fieldName.
20
20
  *
@@ -56,7 +56,7 @@ function jsonSchemaToMySQLDDL(table, schema, opt = {}) {
56
56
  return lines.join('\n');
57
57
  }
58
58
  exports.jsonSchemaToMySQLDDL = jsonSchemaToMySQLDDL;
59
- function mysqlTableStatsToJsonSchemaField(table, stats) {
59
+ function mysqlTableStatsToJsonSchemaField(table, stats, logger) {
60
60
  const s = {
61
61
  $id: `${table}.schema.json`,
62
62
  type: 'object',
@@ -80,14 +80,14 @@ function mysqlTableStatsToJsonSchemaField(table, stats) {
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')) {
87
87
  s.properties[name] = { type: 'number' };
88
88
  }
89
89
  else {
90
- console.log(s);
90
+ logger.log(s);
91
91
  throw new Error(`Unknown mysql field type ${name} ${stat.Type}`);
92
92
  }
93
93
  });
package/package.json CHANGED
@@ -3,9 +3,6 @@
3
3
  "scripts": {
4
4
  "prepare": "husky install"
5
5
  },
6
- "peerDependencies": {
7
- "@naturalcycles/db-lib": ">=7.0.0"
8
- },
9
6
  "dependencies": {
10
7
  "@naturalcycles/db-lib": "^8.16.2",
11
8
  "@naturalcycles/js-lib": "^14.47.0",
@@ -15,7 +12,7 @@
15
12
  },
16
13
  "devDependencies": {
17
14
  "@naturalcycles/dev-lib": "^12.7.5",
18
- "@types/node": "^16.10.2",
15
+ "@types/node": "^17.0.5",
19
16
  "dotenv": "^10.0.0",
20
17
  "jest": "^27.2.4"
21
18
  },
@@ -39,7 +36,7 @@
39
36
  "engines": {
40
37
  "node": ">=14.15.0"
41
38
  },
42
- "version": "3.8.1",
39
+ "version": "3.10.0",
43
40
  "description": "MySQL client implementing CommonDB interface",
44
41
  "keywords": [
45
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/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
@@ -1,6 +1,5 @@
1
1
  import { Transform } from 'stream'
2
2
  import { promisify } from 'util'
3
- import { AnyObjectWithId } from '@naturalcycles/db-lib/src/db.model'
4
3
  import {
5
4
  BaseCommonDB,
6
5
  CommonDB,
@@ -8,18 +7,21 @@ import {
8
7
  CommonDBOptions,
9
8
  CommonDBSaveOptions,
10
9
  DBQuery,
11
- ObjectWithId,
12
10
  RunQueryResult,
13
11
  } from '@naturalcycles/db-lib'
14
12
  import {
15
- _filterNullishValues,
13
+ _filterUndefinedValues,
16
14
  _mapKeys,
17
15
  _mapValues,
18
16
  _Memo,
17
+ AnyObjectWithId,
18
+ CommonLogger,
19
+ commonLoggerPrefix,
19
20
  JsonSchemaObject,
20
21
  JsonSchemaRootObject,
22
+ ObjectWithId,
21
23
  } from '@naturalcycles/js-lib'
22
- import { Debug, ReadableTyped } from '@naturalcycles/nodejs-lib'
24
+ import { ReadableTyped } from '@naturalcycles/nodejs-lib'
23
25
  import { white } from '@naturalcycles/nodejs-lib/dist/colors'
24
26
  import { Connection, Pool, PoolConfig, PoolConnection, QueryOptions, TypeCast } from 'mysql'
25
27
  import * as mysql from 'mysql'
@@ -48,14 +50,17 @@ export interface MysqlDBCfg extends PoolConfig {
48
50
  * If true - will emit logs of connection events.
49
51
  */
50
52
  debugConnections?: boolean
51
- }
52
53
 
53
- const log = Debug('nc:mysql-lib')
54
+ /**
55
+ * Default to `console`
56
+ */
57
+ logger?: CommonLogger
58
+ }
54
59
 
55
60
  const BOOLEAN_TYPES = new Set(['TINY', 'TINYINT', 'INT'])
56
61
  const BOOLEAN_BIT_TYPES = new Set(['BIT'])
57
62
 
58
- const typeCast: TypeCast = (field, next) => {
63
+ export const typeCast: TypeCast = (field, next) => {
59
64
  // cast to boolean
60
65
  if (field.length === 1) {
61
66
  if (BOOLEAN_BIT_TYPES.has(field.type)) {
@@ -86,10 +91,11 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
86
91
  // password: MYSQL_PW,
87
92
  // database: MYSQL_DB,
88
93
  ...cfg,
94
+ logger: commonLoggerPrefix(cfg.logger || console, '[mysql]'),
89
95
  }
90
96
  }
91
97
 
92
- cfg!: MysqlDBCfg
98
+ cfg: MysqlDBCfg & { logger: CommonLogger }
93
99
 
94
100
  override async ping(): Promise<void> {
95
101
  const con = await this.getConnection()
@@ -99,29 +105,29 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
99
105
  @_Memo()
100
106
  pool(): Pool {
101
107
  const pool = mysql.createPool(this.cfg)
102
- const { host, database } = this.cfg
103
- log(`connected to ${white(host + '/' + database)}`)
108
+ const { host, database, logger } = this.cfg
109
+ logger.log(`connected to ${white(host + '/' + database)}`)
104
110
 
105
111
  if (this.cfg.debugConnections) {
106
112
  pool.on('acquire', con => {
107
- log(`acquire(${con.threadId})`)
113
+ logger.log(`acquire(${con.threadId})`)
108
114
  })
109
115
 
110
116
  pool.on('connection', con => {
111
- log(`connection(${con.threadId})`)
117
+ logger.log(`connection(${con.threadId})`)
112
118
  })
113
119
 
114
120
  pool.on('enqueue', () => {
115
- log(`enqueue`)
121
+ logger.log(`enqueue`)
116
122
  })
117
123
 
118
124
  pool.on('release', con => {
119
- log(`release(${con.threadId})`)
125
+ logger.log(`release(${con.threadId})`)
120
126
  })
121
127
  }
122
128
 
123
129
  pool.on('error', err => {
124
- log.error(`error`, err)
130
+ logger.error(err)
125
131
  })
126
132
 
127
133
  return pool
@@ -130,7 +136,8 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
130
136
  async close(): Promise<void> {
131
137
  const pool = this.pool()
132
138
  // eslint-disable-next-line @typescript-eslint/await-thenable
133
- await promisify(pool.end.bind(pool))
139
+ await promisify(pool.end.bind(pool))()
140
+ this.cfg.logger.log('closed')
134
141
  }
135
142
 
136
143
  /**
@@ -151,16 +158,16 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
151
158
  const { threadId } = con
152
159
 
153
160
  if (this.cfg.debugConnections) {
154
- log(`createSingleConnection(${threadId})`)
161
+ this.cfg.logger.log(`createSingleConnection(${threadId})`)
155
162
 
156
- con.on('connect', () => log(`createSingleConnection(${threadId}).connect`))
157
- con.on('drain', () => log(`createSingleConnection(${threadId}).drain`))
158
- con.on('enqueue', () => log(`createSingleConnection(${threadId}).enqueue`))
159
- con.on('end', () => log(`createSingleConnection(${threadId}).end`))
163
+ con.on('connect', () => this.cfg.logger.log(`createSingleConnection(${threadId}).connect`))
164
+ con.on('drain', () => this.cfg.logger.log(`createSingleConnection(${threadId}).drain`))
165
+ con.on('enqueue', () => this.cfg.logger.log(`createSingleConnection(${threadId}).enqueue`))
166
+ con.on('end', () => this.cfg.logger.log(`createSingleConnection(${threadId}).end`))
160
167
  }
161
168
 
162
169
  con.on('error', err => {
163
- log.error(`createSingleConnection(${threadId}).error`, err)
170
+ this.cfg.logger.error(`createSingleConnection(${threadId}).error`, err)
164
171
  })
165
172
 
166
173
  return con
@@ -184,14 +191,24 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
184
191
  _opt: MysqlDBOptions = {},
185
192
  ): Promise<RunQueryResult<OUT>> {
186
193
  const sql = dbQueryToSQLSelect(q)
187
- 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
+
188
205
  return {
189
- rows: rows.map(r => _mapKeys(_filterNullishValues(r, true), k => mapNameFromMySQL(k)) as any),
206
+ rows,
190
207
  }
191
208
  }
192
209
 
193
210
  async runSQL<RESULT>(q: QueryOptions): Promise<RESULT> {
194
- if (this.cfg.logSQL) log(q.sql, q.values)
211
+ if (this.cfg.logSQL) this.cfg.logger.log(...[q.sql, q.values].filter(Boolean))
195
212
 
196
213
  return await new Promise<RESULT>((resolve, reject) => {
197
214
  this.pool().query(q, (err, res) => {
@@ -215,7 +232,7 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
215
232
  ): ReadableTyped<OUT> {
216
233
  const sql = dbQueryToSQLSelect(q)
217
234
 
218
- if (this.cfg.logSQL) log(`stream: ${sql}`)
235
+ if (this.cfg.logSQL) this.cfg.logger.log(`stream: ${sql}`)
219
236
 
220
237
  // return this.streamSQL(sql, opt)
221
238
  return this.pool()
@@ -225,7 +242,7 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
225
242
  new Transform({
226
243
  objectMode: true,
227
244
  transform(row, _encoding, cb) {
228
- cb(null, _filterNullishValues(row, true))
245
+ cb(null, _filterUndefinedValues(row, true))
229
246
  },
230
247
  }),
231
248
  )
@@ -241,13 +258,13 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
241
258
 
242
259
  // Stringify object values
243
260
  const rows = _rows.map(row =>
244
- _mapValues(row, v => {
261
+ _mapValues(row, (_k, v) => {
245
262
  return v && typeof v === 'object' && !Buffer.isBuffer(v) ? JSON.stringify(v) : v
246
263
  }),
247
264
  )
248
265
 
249
266
  // inserts are split into multiple sentenses to respect the max_packet_size (1Mb usually)
250
- const sqls = insertSQL(table, rows)
267
+ const sqls = insertSQL(table, rows, 'REPLACE', this.cfg.logger)
251
268
 
252
269
  for await (const sql of sqls) {
253
270
  await this.runSQL({ sql })
@@ -308,6 +325,6 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
308
325
  sql: `describe ${mysql.escapeId(table)}`,
309
326
  })
310
327
 
311
- return mysqlTableStatsToJsonSchemaField<ROW>(table, stats)
328
+ return mysqlTableStatsToJsonSchemaField<ROW>(table, stats, this.cfg.logger)
312
329
  }
313
330
  }
@@ -0,0 +1,155 @@
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, typeCast } from './mysql.db'
7
+
8
+ interface KeyValueObject {
9
+ id: string
10
+ v: Buffer
11
+ }
12
+
13
+ export class MySQLKeyValueDB implements CommonKeyValueDB {
14
+ constructor(cfg: MysqlDBCfg = {}) {
15
+ this.cfg = {
16
+ typeCast,
17
+ ...cfg,
18
+ }
19
+
20
+ this.db = new MysqlDB(this.cfg)
21
+ }
22
+
23
+ cfg: MysqlDBCfg
24
+
25
+ db: MysqlDB
26
+
27
+ async ping(): Promise<void> {
28
+ await this.db.ping()
29
+ }
30
+
31
+ async close(): Promise<void> {
32
+ await this.db.close()
33
+ }
34
+
35
+ async createTable(table: string, opt: CommonDBCreateOptions = {}): Promise<void> {
36
+ if (opt.dropIfExists) await this.dropTable(table)
37
+
38
+ // On blob sizes: https://tableplus.com/blog/2019/10/tinyblob-blob-mediumblob-longblob.html
39
+ // LONGBLOB supports up to 4gb
40
+ // MEDIUMBLOB up to 16Mb
41
+ const sql = `create table ${table} (id VARCHAR(64) PRIMARY KEY, v LONGBLOB NOT NULL)`
42
+ this.db.cfg.logger.log(sql)
43
+ await this.db.runSQL({ sql })
44
+ }
45
+
46
+ async getByIds(table: string, ids: string[]): Promise<KeyValueDBTuple[]> {
47
+ if (!ids.length) return []
48
+
49
+ const sql = `SELECT id,v FROM ${table} where id in (${ids.map(id => `"${id}"`).join(',')})`
50
+
51
+ const rows = await this.db.runSQL<KeyValueObject[]>({ sql })
52
+
53
+ return rows.map(({ id, v }) => [id, v])
54
+ }
55
+
56
+ /**
57
+ * Use with caution!
58
+ */
59
+ async dropTable(table: string): Promise<void> {
60
+ await this.db.runSQL({ sql: `DROP TABLE IF EXISTS ${table}` })
61
+ }
62
+
63
+ async deleteByIds(table: string, ids: string[]): Promise<void> {
64
+ const sql = `DELETE FROM ${table} WHERE id in (${ids.map(id => `"${id}"`).join(',')})`
65
+ if (this.cfg.logSQL) this.db.cfg.logger.log(sql)
66
+ await this.db.runSQL({ sql })
67
+ }
68
+
69
+ async saveBatch(table: string, entries: KeyValueDBTuple[]): Promise<void> {
70
+ const statements: QueryOptions[] = entries.map(([id, buf]) => {
71
+ return {
72
+ sql: `INSERT INTO ${table} (id, v) VALUES (?, ?)`,
73
+ values: [id, buf],
74
+ }
75
+ })
76
+
77
+ await pMap(statements, async statement => {
78
+ if (this.cfg.debug) this.db.cfg.logger.log(statement.sql)
79
+ await this.db.runSQL(statement)
80
+ })
81
+ }
82
+
83
+ streamIds(table: string, limit?: number): ReadableTyped<string> {
84
+ let sql = `SELECT id FROM ${table}`
85
+ if (limit) sql += ` LIMIT ${limit}`
86
+ if (this.cfg.logSQL) this.db.cfg.logger.log(`stream: ${sql}`)
87
+
88
+ return this.db
89
+ .pool()
90
+ .query(sql)
91
+ .stream()
92
+ .pipe(
93
+ new Transform({
94
+ objectMode: true,
95
+ transform(row, _, cb) {
96
+ cb(null, row.id)
97
+ },
98
+ }),
99
+ )
100
+ }
101
+
102
+ streamValues(table: string, limit?: number): ReadableTyped<Buffer> {
103
+ let sql = `SELECT v FROM ${table}`
104
+ if (limit) sql += ` LIMIT ${limit}`
105
+ if (this.cfg.logSQL) this.db.cfg.logger.log(`stream: ${sql}`)
106
+
107
+ return this.db
108
+ .pool()
109
+ .query(sql)
110
+ .stream()
111
+ .pipe(
112
+ new Transform({
113
+ objectMode: true,
114
+ transform(row, _, cb) {
115
+ cb(null, row.v)
116
+ },
117
+ }),
118
+ )
119
+ }
120
+
121
+ streamEntries(table: string, limit?: number): ReadableTyped<KeyValueDBTuple> {
122
+ let sql = `SELECT id,v FROM ${table}`
123
+ if (limit) sql += ` LIMIT ${limit}`
124
+ if (this.cfg.logSQL) this.db.cfg.logger.log(`stream: ${sql}`)
125
+
126
+ return this.db
127
+ .pool()
128
+ .query(sql)
129
+ .stream()
130
+ .pipe(
131
+ new Transform({
132
+ objectMode: true,
133
+ transform(row, _, cb) {
134
+ cb(null, [row.id, row.v])
135
+ },
136
+ }),
137
+ )
138
+ }
139
+
140
+ async beginTransaction(): Promise<void> {
141
+ await this.db.runSQL({ sql: `BEGIN TRANSACTION` })
142
+ }
143
+
144
+ async endTransaction(): Promise<void> {
145
+ await this.db.runSQL({ sql: `END TRANSACTION` })
146
+ }
147
+
148
+ async count(table: string): Promise<number> {
149
+ const sql = `SELECT count(*) as cnt FROM ${table}`
150
+ if (this.cfg.logSQL) this.db.cfg.logger.log(sql)
151
+
152
+ const rows = await this.db.runSQL<{ cnt: number }[]>({ sql })
153
+ return rows[0]!.cnt
154
+ }
155
+ }
package/src/query.util.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { DBQuery } from '@naturalcycles/db-lib'
2
2
  import { DBQueryFilterOperator } from '@naturalcycles/db-lib/src/query/dbQuery'
3
- import { _hb } from '@naturalcycles/js-lib'
3
+ import { _hb, CommonLogger } from '@naturalcycles/js-lib'
4
4
  import { white, yellow } from '@naturalcycles/nodejs-lib/dist/colors'
5
5
  import { QueryOptions } from 'mysql'
6
6
  import * as mysql from 'mysql'
@@ -41,15 +41,16 @@ 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
+ logger: CommonLogger = console,
46
47
  ): string[] {
47
48
  // INSERT INTO table_name (column1, column2, column3, ...)
48
49
  // VALUES (value1, value2, value3, ...);
49
50
 
50
51
  // eslint-disable-next-line unicorn/no-array-reduce
51
- const fieldSet = records.reduce((set: Set<string>, rec) => {
52
- 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))
53
54
  return set
54
55
  }, new Set<string>())
55
56
  const fields = [...fieldSet]
@@ -62,7 +63,7 @@ export function insertSQL(
62
63
  `VALUES\n`,
63
64
  ].join(' ')
64
65
 
65
- const valueRows = records.map(rec => {
66
+ const valueRows = rows.map(rec => {
66
67
  return `(` + fields.map(k => mysql.escape(rec[k])).join(',') + `)`
67
68
  })
68
69
 
@@ -90,7 +91,7 @@ export function insertSQL(
90
91
  sqls.push(sql) // last one
91
92
  }
92
93
 
93
- console.log(
94
+ logger.log(
94
95
  `${white(table)} large sql query (${yellow(_hb(full.length))}) was split into ${yellow(
95
96
  sqls.length,
96
97
  )} chunks`,
@@ -1,4 +1,5 @@
1
1
  import {
2
+ CommonLogger,
2
3
  JsonSchemaBoolean,
3
4
  JsonSchemaNumber,
4
5
  JsonSchemaObject,
@@ -81,6 +82,7 @@ export function jsonSchemaToMySQLDDL(
81
82
  export function mysqlTableStatsToJsonSchemaField<T = any>(
82
83
  table: string,
83
84
  stats: MySQLTableStats[],
85
+ logger: CommonLogger,
84
86
  ): JsonSchemaRootObject<T> {
85
87
  const s: JsonSchemaRootObject<T> = {
86
88
  $id: `${table}.schema.json`,
@@ -104,12 +106,12 @@ export function mysqlTableStatsToJsonSchemaField<T = any>(
104
106
  s.properties[name] = { instanceof: 'Buffer' }
105
107
  } else if (t.startsWith('tinyint') || t.includes('(1)')) {
106
108
  s.properties[name] = { type: 'boolean' } as JsonSchemaBoolean
107
- } else if (t.startsWith('int(')) {
109
+ } else if (t === 'int' || t.startsWith('int(')) {
108
110
  s.properties[name] = { type: 'integer' } as JsonSchemaNumber
109
111
  } else if (t.startsWith('float')) {
110
112
  s.properties[name] = { type: 'number' } as JsonSchemaNumber
111
113
  } else {
112
- console.log(s)
114
+ logger.log(s)
113
115
  throw new Error(`Unknown mysql field type ${name} ${stat.Type}`)
114
116
  }
115
117
  })
package/CHANGELOG.md DELETED
@@ -1,205 +0,0 @@
1
- ## [3.8.1](https://github.com/NaturalCycles/mysql-lib/compare/v3.8.0...v3.8.1) (2021-10-17)
2
-
3
-
4
- ### Bug Fixes
5
-
6
- * adapt to latest db-lib ([9a368ef](https://github.com/NaturalCycles/mysql-lib/commit/9a368ef4b9d964c09520660319059e9cb67bcf76))
7
-
8
- # [3.8.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.7.1...v3.8.0) (2021-10-04)
9
-
10
-
11
- ### Features
12
-
13
- * modernize ([6e8f0a2](https://github.com/NaturalCycles/mysql-lib/commit/6e8f0a254a67902cbc185f034f58cd4f6885449d))
14
-
15
- ## [3.7.1](https://github.com/NaturalCycles/mysql-lib/compare/v3.7.0...v3.7.1) (2020-06-03)
16
-
17
-
18
- ### Bug Fixes
19
-
20
- * modernize ([517f695](https://github.com/NaturalCycles/mysql-lib/commit/517f6952feb15059730f4f7443a295bf596053fc))
21
-
22
- # [3.7.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.6.0...v3.7.0) (2020-04-08)
23
-
24
-
25
- ### Features
26
-
27
- * modernize ([e0e2890](https://github.com/NaturalCycles/mysql-lib/commit/e0e2890cb31e88fcbdae63c417d6cc5c6a52d4e8))
28
-
29
- # [3.6.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.5.2...v3.6.0) (2020-03-01)
30
-
31
-
32
- ### Features
33
-
34
- * deps ([008aa10](https://github.com/NaturalCycles/mysql-lib/commit/008aa10d059734d76c2354553a8dc35c8117b3b2))
35
-
36
- ## [3.5.2](https://github.com/NaturalCycles/mysql-lib/compare/v3.5.1...v3.5.2) (2019-11-20)
37
-
38
-
39
- ### Bug Fixes
40
-
41
- * BIT typecast to boolean correctly ([0bc815e](https://github.com/NaturalCycles/mysql-lib/commit/0bc815e025a33ab9fae5405c8b97dd53aae392ca))
42
-
43
- ## [3.5.1](https://github.com/NaturalCycles/mysql-lib/compare/v3.5.0...v3.5.1) (2019-11-20)
44
-
45
-
46
- ### Bug Fixes
47
-
48
- * correctly type-cast booleans with value=NULL ([e208b4c](https://github.com/NaturalCycles/mysql-lib/commit/e208b4ca16f700b6ed7bf95c872f68f934894992))
49
-
50
- # [3.5.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.4.0...v3.5.0) (2019-11-09)
51
-
52
-
53
- ### Features
54
-
55
- * implement getDBAdapter. Allow empty (default) configuration ([bcf12dc](https://github.com/NaturalCycles/mysql-lib/commit/bcf12dc9360f525749f9bb10965046b4c56d849a))
56
-
57
- # [3.4.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.3.1...v3.4.0) (2019-11-09)
58
-
59
-
60
- ### Features
61
-
62
- * auto JSON.stringify objects (and arrays) ([0d9aaed](https://github.com/NaturalCycles/mysql-lib/commit/0d9aaedf8f2920a51dacf13b6723eb98c61b34c3))
63
-
64
- ## [3.3.1](https://github.com/NaturalCycles/mysql-lib/compare/v3.3.0...v3.3.1) (2019-11-09)
65
-
66
-
67
- ### Bug Fixes
68
-
69
- * use LONGBLOB for binary columns ([ae4facc](https://github.com/NaturalCycles/mysql-lib/commit/ae4facc76bc03b8aafa5141199a98bef530f6913))
70
-
71
- # [3.3.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.2.1...v3.3.0) (2019-11-09)
72
-
73
-
74
- ### Features
75
-
76
- * mapNameToMySQL ([c4bf40c](https://github.com/NaturalCycles/mysql-lib/commit/c4bf40cd6aefaf78219a2588ec803cf74004bc07))
77
-
78
- ## [3.2.1](https://github.com/NaturalCycles/mysql-lib/compare/v3.2.0...v3.2.1) (2019-11-09)
79
-
80
-
81
- ### Bug Fixes
82
-
83
- * createTable() ([7983085](https://github.com/NaturalCycles/mysql-lib/commit/7983085017562a4e4a8f7dd38eb41ae6b9fc30ed))
84
-
85
- # [3.2.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.1.0...v3.2.0) (2019-11-09)
86
-
87
-
88
- ### Bug Fixes
89
-
90
- * emoji support ([1262fc4](https://github.com/NaturalCycles/mysql-lib/commit/1262fc4d9ad39c818833b5cfc31b3a65979cdea0))
91
-
92
-
93
- ### Features
94
-
95
- * auto split long sql queries ([c7c780f](https://github.com/NaturalCycles/mysql-lib/commit/c7c780f295d7ded9f418509f3160a512ec54c7e7))
96
-
97
- # [3.1.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.0.0...v3.1.0) (2019-11-08)
98
-
99
-
100
- ### Features
101
-
102
- * commonSchemaToMySQLDDL ([09168c5](https://github.com/NaturalCycles/mysql-lib/commit/09168c563cbb629ca47121a1aac6a125dd23c144))
103
- * implement getTables(), getTableSchema() ([20a7746](https://github.com/NaturalCycles/mysql-lib/commit/20a7746c8c4c88dfca8365d1e8d2decce3f47efa))
104
-
105
- # [3.0.0](https://github.com/NaturalCycles/mysql-lib/compare/v2.0.2...v3.0.0) (2019-11-02)
106
-
107
-
108
- ### Features
109
-
110
- * adapt to db-lib@3 ([7d13f54](https://github.com/NaturalCycles/mysql-lib/commit/7d13f540c92dd59b014d1a502c1bff937fad567f))
111
-
112
-
113
- ### BREAKING CHANGES
114
-
115
- * ^^^
116
-
117
- ## [2.0.2](https://github.com/NaturalCycles/mysql-lib/compare/v2.0.1...v2.0.2) (2019-10-20)
118
-
119
-
120
- ### Bug Fixes
121
-
122
- * adopt to db-lib ([6da7d78](https://github.com/NaturalCycles/mysql-lib/commit/6da7d78))
123
-
124
- ## [2.0.1](https://github.com/NaturalCycles/mysql-lib/compare/v2.0.0...v2.0.1) (2019-10-19)
125
-
126
-
127
- ### Bug Fixes
128
-
129
- * use Readable ([a0d09c1](https://github.com/NaturalCycles/mysql-lib/commit/a0d09c1))
130
-
131
- # [2.0.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.7.1...v2.0.0) (2019-10-18)
132
-
133
-
134
- ### Features
135
-
136
- * implement CommonDB 2.0 ([9de0e9f](https://github.com/NaturalCycles/mysql-lib/commit/9de0e9f))
137
-
138
-
139
- ### BREAKING CHANGES
140
-
141
- * ^^^
142
-
143
- ## [1.7.1](https://github.com/NaturalCycles/mysql-lib/compare/v1.7.0...v1.7.1) (2019-10-18)
144
-
145
-
146
- ### Bug Fixes
147
-
148
- * pin @types/hapi__joi ([3e3cf5a](https://github.com/NaturalCycles/mysql-lib/commit/3e3cf5a))
149
-
150
- # [1.7.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.6.0...v1.7.0) (2019-09-30)
151
-
152
-
153
- ### Features
154
-
155
- * tiny change to use pool.query inst of getConnection() ([6ffe1b4](https://github.com/NaturalCycles/mysql-lib/commit/6ffe1b4))
156
-
157
- # [1.6.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.5.0...v1.6.0) (2019-09-30)
158
-
159
-
160
- ### Features
161
-
162
- * offset ([78710a1](https://github.com/NaturalCycles/mysql-lib/commit/78710a1))
163
-
164
- # [1.5.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.4.0...v1.5.0) (2019-09-30)
165
-
166
-
167
- ### Features
168
-
169
- * testing simpler streamSQL impl ([0887aa3](https://github.com/NaturalCycles/mysql-lib/commit/0887aa3))
170
-
171
- # [1.4.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.3.0...v1.4.0) (2019-09-30)
172
-
173
-
174
- ### Features
175
-
176
- * stream to use it's own separate connection ([43615f3](https://github.com/NaturalCycles/mysql-lib/commit/43615f3))
177
-
178
- # [1.3.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.2.0...v1.3.0) (2019-09-30)
179
-
180
-
181
- ### Features
182
-
183
- * cfg.debugConnections ([70218ba](https://github.com/NaturalCycles/mysql-lib/commit/70218ba))
184
-
185
- # [1.2.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.1.0...v1.2.0) (2019-09-28)
186
-
187
-
188
- ### Features
189
-
190
- * add BIT cast to boolean ([899cce4](https://github.com/NaturalCycles/mysql-lib/commit/899cce4))
191
-
192
- # [1.1.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.0.0...v1.1.0) (2019-09-21)
193
-
194
-
195
- ### Features
196
-
197
- * modernize, implement saveBatch ([5595b6a](https://github.com/NaturalCycles/mysql-lib/commit/5595b6a))
198
-
199
- # 1.0.0 (2019-06-03)
200
-
201
-
202
- ### Features
203
-
204
- * first version ([02620fb](https://github.com/NaturalCycles/mysql-lib/commit/02620fb))
205
- * init project by create-module ([eee50b7](https://github.com/NaturalCycles/mysql-lib/commit/eee50b7))