@naturalcycles/mysql-lib 3.8.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/CHANGELOG.md ADDED
@@ -0,0 +1,198 @@
1
+ # [3.8.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.7.1...v3.8.0) (2021-10-04)
2
+
3
+
4
+ ### Features
5
+
6
+ * modernize ([6e8f0a2](https://github.com/NaturalCycles/mysql-lib/commit/6e8f0a254a67902cbc185f034f58cd4f6885449d))
7
+
8
+ ## [3.7.1](https://github.com/NaturalCycles/mysql-lib/compare/v3.7.0...v3.7.1) (2020-06-03)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * modernize ([517f695](https://github.com/NaturalCycles/mysql-lib/commit/517f6952feb15059730f4f7443a295bf596053fc))
14
+
15
+ # [3.7.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.6.0...v3.7.0) (2020-04-08)
16
+
17
+
18
+ ### Features
19
+
20
+ * modernize ([e0e2890](https://github.com/NaturalCycles/mysql-lib/commit/e0e2890cb31e88fcbdae63c417d6cc5c6a52d4e8))
21
+
22
+ # [3.6.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.5.2...v3.6.0) (2020-03-01)
23
+
24
+
25
+ ### Features
26
+
27
+ * deps ([008aa10](https://github.com/NaturalCycles/mysql-lib/commit/008aa10d059734d76c2354553a8dc35c8117b3b2))
28
+
29
+ ## [3.5.2](https://github.com/NaturalCycles/mysql-lib/compare/v3.5.1...v3.5.2) (2019-11-20)
30
+
31
+
32
+ ### Bug Fixes
33
+
34
+ * BIT typecast to boolean correctly ([0bc815e](https://github.com/NaturalCycles/mysql-lib/commit/0bc815e025a33ab9fae5405c8b97dd53aae392ca))
35
+
36
+ ## [3.5.1](https://github.com/NaturalCycles/mysql-lib/compare/v3.5.0...v3.5.1) (2019-11-20)
37
+
38
+
39
+ ### Bug Fixes
40
+
41
+ * correctly type-cast booleans with value=NULL ([e208b4c](https://github.com/NaturalCycles/mysql-lib/commit/e208b4ca16f700b6ed7bf95c872f68f934894992))
42
+
43
+ # [3.5.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.4.0...v3.5.0) (2019-11-09)
44
+
45
+
46
+ ### Features
47
+
48
+ * implement getDBAdapter. Allow empty (default) configuration ([bcf12dc](https://github.com/NaturalCycles/mysql-lib/commit/bcf12dc9360f525749f9bb10965046b4c56d849a))
49
+
50
+ # [3.4.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.3.1...v3.4.0) (2019-11-09)
51
+
52
+
53
+ ### Features
54
+
55
+ * auto JSON.stringify objects (and arrays) ([0d9aaed](https://github.com/NaturalCycles/mysql-lib/commit/0d9aaedf8f2920a51dacf13b6723eb98c61b34c3))
56
+
57
+ ## [3.3.1](https://github.com/NaturalCycles/mysql-lib/compare/v3.3.0...v3.3.1) (2019-11-09)
58
+
59
+
60
+ ### Bug Fixes
61
+
62
+ * use LONGBLOB for binary columns ([ae4facc](https://github.com/NaturalCycles/mysql-lib/commit/ae4facc76bc03b8aafa5141199a98bef530f6913))
63
+
64
+ # [3.3.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.2.1...v3.3.0) (2019-11-09)
65
+
66
+
67
+ ### Features
68
+
69
+ * mapNameToMySQL ([c4bf40c](https://github.com/NaturalCycles/mysql-lib/commit/c4bf40cd6aefaf78219a2588ec803cf74004bc07))
70
+
71
+ ## [3.2.1](https://github.com/NaturalCycles/mysql-lib/compare/v3.2.0...v3.2.1) (2019-11-09)
72
+
73
+
74
+ ### Bug Fixes
75
+
76
+ * createTable() ([7983085](https://github.com/NaturalCycles/mysql-lib/commit/7983085017562a4e4a8f7dd38eb41ae6b9fc30ed))
77
+
78
+ # [3.2.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.1.0...v3.2.0) (2019-11-09)
79
+
80
+
81
+ ### Bug Fixes
82
+
83
+ * emoji support ([1262fc4](https://github.com/NaturalCycles/mysql-lib/commit/1262fc4d9ad39c818833b5cfc31b3a65979cdea0))
84
+
85
+
86
+ ### Features
87
+
88
+ * auto split long sql queries ([c7c780f](https://github.com/NaturalCycles/mysql-lib/commit/c7c780f295d7ded9f418509f3160a512ec54c7e7))
89
+
90
+ # [3.1.0](https://github.com/NaturalCycles/mysql-lib/compare/v3.0.0...v3.1.0) (2019-11-08)
91
+
92
+
93
+ ### Features
94
+
95
+ * commonSchemaToMySQLDDL ([09168c5](https://github.com/NaturalCycles/mysql-lib/commit/09168c563cbb629ca47121a1aac6a125dd23c144))
96
+ * implement getTables(), getTableSchema() ([20a7746](https://github.com/NaturalCycles/mysql-lib/commit/20a7746c8c4c88dfca8365d1e8d2decce3f47efa))
97
+
98
+ # [3.0.0](https://github.com/NaturalCycles/mysql-lib/compare/v2.0.2...v3.0.0) (2019-11-02)
99
+
100
+
101
+ ### Features
102
+
103
+ * adapt to db-lib@3 ([7d13f54](https://github.com/NaturalCycles/mysql-lib/commit/7d13f540c92dd59b014d1a502c1bff937fad567f))
104
+
105
+
106
+ ### BREAKING CHANGES
107
+
108
+ * ^^^
109
+
110
+ ## [2.0.2](https://github.com/NaturalCycles/mysql-lib/compare/v2.0.1...v2.0.2) (2019-10-20)
111
+
112
+
113
+ ### Bug Fixes
114
+
115
+ * adopt to db-lib ([6da7d78](https://github.com/NaturalCycles/mysql-lib/commit/6da7d78))
116
+
117
+ ## [2.0.1](https://github.com/NaturalCycles/mysql-lib/compare/v2.0.0...v2.0.1) (2019-10-19)
118
+
119
+
120
+ ### Bug Fixes
121
+
122
+ * use Readable ([a0d09c1](https://github.com/NaturalCycles/mysql-lib/commit/a0d09c1))
123
+
124
+ # [2.0.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.7.1...v2.0.0) (2019-10-18)
125
+
126
+
127
+ ### Features
128
+
129
+ * implement CommonDB 2.0 ([9de0e9f](https://github.com/NaturalCycles/mysql-lib/commit/9de0e9f))
130
+
131
+
132
+ ### BREAKING CHANGES
133
+
134
+ * ^^^
135
+
136
+ ## [1.7.1](https://github.com/NaturalCycles/mysql-lib/compare/v1.7.0...v1.7.1) (2019-10-18)
137
+
138
+
139
+ ### Bug Fixes
140
+
141
+ * pin @types/hapi__joi ([3e3cf5a](https://github.com/NaturalCycles/mysql-lib/commit/3e3cf5a))
142
+
143
+ # [1.7.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.6.0...v1.7.0) (2019-09-30)
144
+
145
+
146
+ ### Features
147
+
148
+ * tiny change to use pool.query inst of getConnection() ([6ffe1b4](https://github.com/NaturalCycles/mysql-lib/commit/6ffe1b4))
149
+
150
+ # [1.6.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.5.0...v1.6.0) (2019-09-30)
151
+
152
+
153
+ ### Features
154
+
155
+ * offset ([78710a1](https://github.com/NaturalCycles/mysql-lib/commit/78710a1))
156
+
157
+ # [1.5.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.4.0...v1.5.0) (2019-09-30)
158
+
159
+
160
+ ### Features
161
+
162
+ * testing simpler streamSQL impl ([0887aa3](https://github.com/NaturalCycles/mysql-lib/commit/0887aa3))
163
+
164
+ # [1.4.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.3.0...v1.4.0) (2019-09-30)
165
+
166
+
167
+ ### Features
168
+
169
+ * stream to use it's own separate connection ([43615f3](https://github.com/NaturalCycles/mysql-lib/commit/43615f3))
170
+
171
+ # [1.3.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.2.0...v1.3.0) (2019-09-30)
172
+
173
+
174
+ ### Features
175
+
176
+ * cfg.debugConnections ([70218ba](https://github.com/NaturalCycles/mysql-lib/commit/70218ba))
177
+
178
+ # [1.2.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.1.0...v1.2.0) (2019-09-28)
179
+
180
+
181
+ ### Features
182
+
183
+ * add BIT cast to boolean ([899cce4](https://github.com/NaturalCycles/mysql-lib/commit/899cce4))
184
+
185
+ # [1.1.0](https://github.com/NaturalCycles/mysql-lib/compare/v1.0.0...v1.1.0) (2019-09-21)
186
+
187
+
188
+ ### Features
189
+
190
+ * modernize, implement saveBatch ([5595b6a](https://github.com/NaturalCycles/mysql-lib/commit/5595b6a))
191
+
192
+ # 1.0.0 (2019-06-03)
193
+
194
+
195
+ ### Features
196
+
197
+ * first version ([02620fb](https://github.com/NaturalCycles/mysql-lib/commit/02620fb))
198
+ * init project by create-module ([eee50b7](https://github.com/NaturalCycles/mysql-lib/commit/eee50b7))
@@ -0,0 +1,2 @@
1
+ import { MysqlDB } from './mysql.db';
2
+ export declare function getDBAdapter(cfgStr?: string): MysqlDB;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDBAdapter = void 0;
4
+ const mysql_db_1 = require("./mysql.db");
5
+ function getDBAdapter(cfgStr = '{}') {
6
+ const cfg = JSON.parse(cfgStr);
7
+ return new mysql_db_1.MysqlDB(cfg);
8
+ }
9
+ exports.getDBAdapter = getDBAdapter;
@@ -0,0 +1,5 @@
1
+ import { getDBAdapter } from './dbAdapter';
2
+ import { MysqlDB, MysqlDBCfg, MysqlDBOptions, MysqlDBSaveOptions } from './mysql.db';
3
+ import { jsonSchemaToMySQLDDL } from './schema/mysql.schema.util';
4
+ export type { MysqlDBCfg, MysqlDBOptions, MysqlDBSaveOptions };
5
+ export { MysqlDB, jsonSchemaToMySQLDDL, getDBAdapter };
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDBAdapter = exports.jsonSchemaToMySQLDDL = exports.MysqlDB = void 0;
4
+ const dbAdapter_1 = require("./dbAdapter");
5
+ Object.defineProperty(exports, "getDBAdapter", { enumerable: true, get: function () { return dbAdapter_1.getDBAdapter; } });
6
+ const mysql_db_1 = require("./mysql.db");
7
+ Object.defineProperty(exports, "MysqlDB", { enumerable: true, get: function () { return mysql_db_1.MysqlDB; } });
8
+ const mysql_schema_util_1 = require("./schema/mysql.schema.util");
9
+ Object.defineProperty(exports, "jsonSchemaToMySQLDDL", { enumerable: true, get: function () { return mysql_schema_util_1.jsonSchemaToMySQLDDL; } });
@@ -0,0 +1,58 @@
1
+ import { BaseCommonDB, BaseDBEntity, CommonDB, CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, DBQuery, ObjectWithId, RunQueryResult } from '@naturalcycles/db-lib';
2
+ import { JsonSchemaObject, JsonSchemaRootObject } from '@naturalcycles/js-lib';
3
+ import { ReadableTyped } from '@naturalcycles/nodejs-lib';
4
+ import { Connection, Pool, PoolConfig, PoolConnection, QueryOptions } from 'mysql';
5
+ export interface MysqlDBOptions extends CommonDBOptions {
6
+ }
7
+ export interface MysqlDBSaveOptions extends CommonDBSaveOptions {
8
+ }
9
+ /**
10
+ * @default false / undefined
11
+ */
12
+ export interface MysqlDBCfg extends PoolConfig {
13
+ /**
14
+ * If true - will log all produced SQL queries.
15
+ */
16
+ logSQL?: boolean;
17
+ /**
18
+ * If true - will emit logs of connection events.
19
+ */
20
+ debugConnections?: boolean;
21
+ }
22
+ export declare class MysqlDB extends BaseCommonDB implements CommonDB {
23
+ constructor(cfg?: MysqlDBCfg);
24
+ cfg: MysqlDBCfg;
25
+ ping(): Promise<void>;
26
+ pool(): Pool;
27
+ close(): Promise<void>;
28
+ /**
29
+ * Be careful to always call `con.release()` when you get connection with this method.
30
+ */
31
+ getConnection(): Promise<PoolConnection>;
32
+ /**
33
+ * Manually create a single (not pool) connection.
34
+ * Be careful to manage this connection yourself (open, release, etc).
35
+ */
36
+ createSingleConnection(): Promise<Connection>;
37
+ getByIds<ROW extends ObjectWithId>(table: string, ids: string[], opt?: MysqlDBOptions): Promise<ROW[]>;
38
+ runQuery<ROW extends ObjectWithId, OUT = ROW>(q: DBQuery<ROW>, _opt?: MysqlDBOptions): Promise<RunQueryResult<OUT>>;
39
+ runSQL<RESULT>(q: QueryOptions): Promise<RESULT>;
40
+ runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
41
+ streamQuery<ROW extends ObjectWithId, OUT = ROW>(q: DBQuery<ROW>, _opt?: MysqlDBOptions): ReadableTyped<OUT>;
42
+ saveBatch<ROW extends BaseDBEntity>(table: string, _rows: ROW[], _opt?: MysqlDBSaveOptions): Promise<void>;
43
+ /**
44
+ * Limitation: always returns [], regardless of which rows are actually deleted
45
+ */
46
+ deleteByIds(table: string, ids: string[], _opt?: MysqlDBOptions): Promise<number>;
47
+ deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
48
+ /**
49
+ * Use with caution!
50
+ */
51
+ dropTable(table: string): Promise<void>;
52
+ /**
53
+ * dropIfExists=true needed as a safety check
54
+ */
55
+ createTable(table: string, schema: JsonSchemaObject, opt?: CommonDBCreateOptions): Promise<void>;
56
+ getTables(): Promise<string[]>;
57
+ getTableSchema<ROW extends ObjectWithId>(table: string): Promise<JsonSchemaRootObject<ROW>>;
58
+ }
@@ -0,0 +1,214 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MysqlDB = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const stream_1 = require("stream");
6
+ const util_1 = require("util");
7
+ const db_lib_1 = require("@naturalcycles/db-lib");
8
+ const js_lib_1 = require("@naturalcycles/js-lib");
9
+ const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
10
+ const colors_1 = require("@naturalcycles/nodejs-lib/dist/colors");
11
+ const mysql = require("mysql");
12
+ const query_util_1 = require("./query.util");
13
+ const mysql_schema_util_1 = require("./schema/mysql.schema.util");
14
+ const log = (0, nodejs_lib_1.Debug)('nc:mysql-lib');
15
+ const BOOLEAN_TYPES = new Set(['TINY', 'TINYINT', 'INT']);
16
+ const BOOLEAN_BIT_TYPES = new Set(['BIT']);
17
+ const typeCast = (field, next) => {
18
+ // cast to boolean
19
+ if (field.length === 1) {
20
+ if (BOOLEAN_BIT_TYPES.has(field.type)) {
21
+ const b = field.buffer();
22
+ if (b === null || b === undefined)
23
+ return undefined; // null = undefined
24
+ return b[0] === 1; // 1 = true, 0 = false
25
+ }
26
+ if (BOOLEAN_TYPES.has(field.type)) {
27
+ const s = field.string();
28
+ // console.log(field.name, field.type, s, s?.charCodeAt(0))
29
+ if (s === null || s === undefined)
30
+ return undefined; // null = undefined
31
+ return s === '1'; // 1 = true, 0 = false
32
+ }
33
+ }
34
+ return next();
35
+ };
36
+ class MysqlDB extends db_lib_1.BaseCommonDB {
37
+ constructor(cfg = {}) {
38
+ super();
39
+ this.cfg = {
40
+ typeCast,
41
+ // charset: 'utf8mb4', // for emoji support
42
+ // host: 'localhost',
43
+ // user: MYSQL_USER,
44
+ // password: MYSQL_PW,
45
+ // database: MYSQL_DB,
46
+ ...cfg,
47
+ };
48
+ }
49
+ async ping() {
50
+ const con = await this.getConnection();
51
+ con.release();
52
+ }
53
+ pool() {
54
+ const pool = mysql.createPool(this.cfg);
55
+ const { host, database } = this.cfg;
56
+ log(`connected to ${(0, colors_1.white)(host + '/' + database)}`);
57
+ if (this.cfg.debugConnections) {
58
+ pool.on('acquire', con => {
59
+ log(`acquire(${con.threadId})`);
60
+ });
61
+ pool.on('connection', con => {
62
+ log(`connection(${con.threadId})`);
63
+ });
64
+ pool.on('enqueue', () => {
65
+ log(`enqueue`);
66
+ });
67
+ pool.on('release', con => {
68
+ log(`release(${con.threadId})`);
69
+ });
70
+ }
71
+ pool.on('error', err => {
72
+ log.error(`error`, err);
73
+ });
74
+ return pool;
75
+ }
76
+ async close() {
77
+ const pool = this.pool();
78
+ // eslint-disable-next-line @typescript-eslint/await-thenable
79
+ await (0, util_1.promisify)(pool.end.bind(pool));
80
+ }
81
+ /**
82
+ * Be careful to always call `con.release()` when you get connection with this method.
83
+ */
84
+ async getConnection() {
85
+ const pool = this.pool();
86
+ return await (0, util_1.promisify)(pool.getConnection.bind(pool))();
87
+ }
88
+ /**
89
+ * Manually create a single (not pool) connection.
90
+ * Be careful to manage this connection yourself (open, release, etc).
91
+ */
92
+ async createSingleConnection() {
93
+ const con = mysql.createConnection(this.cfg);
94
+ await (0, util_1.promisify)(con.connect.bind(con))();
95
+ const { threadId } = con;
96
+ 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`));
102
+ }
103
+ con.on('error', err => {
104
+ log.error(`createSingleConnection(${threadId}).error`, err);
105
+ });
106
+ return con;
107
+ }
108
+ // GET
109
+ async getByIds(table, ids, opt = {}) {
110
+ if (!ids.length)
111
+ return [];
112
+ const q = new db_lib_1.DBQuery(table).filterEq('id', ids);
113
+ const { rows } = await this.runQuery(q, opt);
114
+ return rows.map(r => (0, js_lib_1._mapKeys)(r, k => (0, mysql_schema_util_1.mapNameFromMySQL)(k)));
115
+ }
116
+ // QUERY
117
+ async runQuery(q, _opt = {}) {
118
+ const sql = (0, query_util_1.dbQueryToSQLSelect)(q);
119
+ const rows = await this.runSQL({ sql });
120
+ 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))),
122
+ };
123
+ }
124
+ async runSQL(q) {
125
+ if (this.cfg.logSQL)
126
+ log(q.sql, q.values);
127
+ return await new Promise((resolve, reject) => {
128
+ this.pool().query(q, (err, res) => {
129
+ if (err)
130
+ return reject(err);
131
+ resolve(res);
132
+ });
133
+ });
134
+ }
135
+ async runQueryCount(q, _opt) {
136
+ const { rows } = await this.runQuery(q.select(['count(*) as _count']));
137
+ return rows[0]._count;
138
+ }
139
+ streamQuery(q, _opt = {}) {
140
+ const sql = (0, query_util_1.dbQueryToSQLSelect)(q);
141
+ if (this.cfg.logSQL)
142
+ log(`stream: ${sql}`);
143
+ // return this.streamSQL(sql, opt)
144
+ return this.pool()
145
+ .query(sql)
146
+ .stream()
147
+ .pipe(new stream_1.Transform({
148
+ objectMode: true,
149
+ transform(row, _encoding, cb) {
150
+ cb(null, (0, js_lib_1._filterNullishValues)(row, true));
151
+ },
152
+ }));
153
+ }
154
+ // SAVE
155
+ async saveBatch(table, _rows, _opt) {
156
+ if (!_rows.length)
157
+ return;
158
+ // Stringify object values
159
+ const rows = _rows.map(row => (0, js_lib_1._mapValues)(row, v => {
160
+ return v && typeof v === 'object' && !Buffer.isBuffer(v) ? JSON.stringify(v) : v;
161
+ }));
162
+ // inserts are split into multiple sentenses to respect the max_packet_size (1Mb usually)
163
+ const sqls = (0, query_util_1.insertSQL)(table, rows);
164
+ for await (const sql of sqls) {
165
+ await this.runSQL({ sql });
166
+ }
167
+ }
168
+ // DELETE
169
+ /**
170
+ * Limitation: always returns [], regardless of which rows are actually deleted
171
+ */
172
+ async deleteByIds(table, ids, _opt) {
173
+ if (!ids.length)
174
+ return 0;
175
+ const sql = (0, query_util_1.dbQueryToSQLDelete)(new db_lib_1.DBQuery(table).filterEq('id', ids));
176
+ const res = await this.runSQL({ sql });
177
+ return res.affectedRows;
178
+ }
179
+ async deleteByQuery(q, _opt) {
180
+ const sql = (0, query_util_1.dbQueryToSQLDelete)(q);
181
+ const res = await this.runSQL({ sql });
182
+ return res.affectedRows;
183
+ }
184
+ /**
185
+ * Use with caution!
186
+ */
187
+ async dropTable(table) {
188
+ await this.runSQL({ sql: `DROP TABLE IF EXISTS ${table}` });
189
+ }
190
+ /**
191
+ * dropIfExists=true needed as a safety check
192
+ */
193
+ async createTable(table, schema, opt = {}) {
194
+ if (opt.dropIfExists)
195
+ await this.dropTable(table);
196
+ const sql = (0, mysql_schema_util_1.jsonSchemaToMySQLDDL)(table, schema);
197
+ await this.runSQL({ sql });
198
+ }
199
+ async getTables() {
200
+ return (await this.runSQL({ sql: `show tables` }))
201
+ .map(r => Object.values(r)[0])
202
+ .filter(Boolean);
203
+ }
204
+ async getTableSchema(table) {
205
+ const stats = await this.runSQL({
206
+ sql: `describe ${mysql.escapeId(table)}`,
207
+ });
208
+ return (0, mysql_schema_util_1.mysqlTableStatsToJsonSchemaField)(table, stats);
209
+ }
210
+ }
211
+ (0, tslib_1.__decorate)([
212
+ (0, js_lib_1._Memo)()
213
+ ], MysqlDB.prototype, "pool", null);
214
+ exports.MysqlDB = MysqlDB;
@@ -0,0 +1,11 @@
1
+ import { DBQuery } from '@naturalcycles/db-lib';
2
+ import { QueryOptions } from 'mysql';
3
+ export declare function dbQueryToSQLSelect(q: DBQuery<any>): string;
4
+ export declare function dbQueryToSQLDelete(q: DBQuery<any>): string;
5
+ /**
6
+ * Returns array of sql statements to respect the max sql size.
7
+ */
8
+ export declare function insertSQL(table: string, records: Record<any, any>[], verb?: 'INSERT' | 'REPLACE'): string[];
9
+ export declare function insertSQLSingle(table: string, record: Record<any, any>): string;
10
+ export declare function insertSQLSetSingle(table: string, record: Record<any, any>): QueryOptions;
11
+ export declare function dbQueryToSQLUpdate(q: DBQuery<any>, record: Record<any, any>): string;
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dbQueryToSQLUpdate = exports.insertSQLSetSingle = exports.insertSQLSingle = exports.insertSQL = exports.dbQueryToSQLDelete = exports.dbQueryToSQLSelect = void 0;
4
+ const js_lib_1 = require("@naturalcycles/js-lib");
5
+ const colors_1 = require("@naturalcycles/nodejs-lib/dist/colors");
6
+ const mysql = require("mysql");
7
+ const mysql_schema_util_1 = require("./schema/mysql.schema.util");
8
+ const MAX_PACKET_SIZE = 1024 * 1024; // 1Mb
9
+ const MAX_ROW_SIZE = 800 * 1024; // 1Mb - margin
10
+ function dbQueryToSQLSelect(q) {
11
+ const tokens = selectTokens(q);
12
+ // filters
13
+ tokens.push(...whereTokens(q));
14
+ // order
15
+ tokens.push(...orderTokens(q));
16
+ // offset/limit
17
+ tokens.push(...offsetLimitTokens(q));
18
+ return tokens.join(' ');
19
+ }
20
+ exports.dbQueryToSQLSelect = dbQueryToSQLSelect;
21
+ function dbQueryToSQLDelete(q) {
22
+ const tokens = [`DELETE FROM`, mysql.escapeId(q.table)];
23
+ // filters
24
+ tokens.push(...whereTokens(q));
25
+ // offset/limit
26
+ tokens.push(...offsetLimitTokens(q));
27
+ return tokens.join(' ');
28
+ }
29
+ exports.dbQueryToSQLDelete = dbQueryToSQLDelete;
30
+ /**
31
+ * Returns array of sql statements to respect the max sql size.
32
+ */
33
+ function insertSQL(table, records, verb = 'INSERT') {
34
+ // INSERT INTO table_name (column1, column2, column3, ...)
35
+ // VALUES (value1, value2, value3, ...);
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));
39
+ return set;
40
+ }, new Set());
41
+ const fields = [...fieldSet];
42
+ const start = [
43
+ verb,
44
+ `INTO`,
45
+ mysql.escapeId(table),
46
+ `(` + [...fields].map(f => mysql.escapeId((0, mysql_schema_util_1.mapNameToMySQL)(f))).join(',') + `)`,
47
+ `VALUES\n`,
48
+ ].join(' ');
49
+ const valueRows = records.map(rec => {
50
+ return `(` + fields.map(k => mysql.escape(rec[k])).join(',') + `)`;
51
+ });
52
+ const full = start + valueRows.join(',\n');
53
+ if (full.length < MAX_PACKET_SIZE)
54
+ return [full];
55
+ const sqls = [];
56
+ let sql;
57
+ valueRows.forEach(vrow => {
58
+ if (!sql) {
59
+ sql = start + vrow;
60
+ }
61
+ else {
62
+ if (sql.length + vrow.length >= MAX_ROW_SIZE) {
63
+ sqls.push(sql);
64
+ sql = start + vrow; // reset
65
+ }
66
+ else {
67
+ sql += ',\n ' + vrow; // add
68
+ }
69
+ }
70
+ });
71
+ if (sql) {
72
+ sqls.push(sql); // last one
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`);
75
+ return sqls;
76
+ // todo: handle "upsert" later
77
+ }
78
+ exports.insertSQL = insertSQL;
79
+ function insertSQLSingle(table, record) {
80
+ // INSERT INTO table_name (column1, column2, column3, ...)
81
+ // VALUES (value1, value2, value3, ...);
82
+ const tokens = [
83
+ `INSERT INTO`,
84
+ mysql.escapeId(table),
85
+ `(` +
86
+ Object.keys(record)
87
+ .map(f => mysql.escapeId((0, mysql_schema_util_1.mapNameToMySQL)(f)))
88
+ .join(',') +
89
+ `)`,
90
+ `\nVALUES`,
91
+ `(` +
92
+ Object.values(record)
93
+ .map(v => mysql.escapeId(v))
94
+ .join(',') +
95
+ `)`,
96
+ ];
97
+ return tokens.join(' ');
98
+ }
99
+ exports.insertSQLSingle = insertSQLSingle;
100
+ function insertSQLSetSingle(table, record) {
101
+ return {
102
+ sql: `INSERT INTO ${mysql.escapeId(table)} SET ?`,
103
+ values: [record],
104
+ };
105
+ }
106
+ exports.insertSQLSetSingle = insertSQLSetSingle;
107
+ function dbQueryToSQLUpdate(q, record) {
108
+ // var sql = mysql.format('UPDATE posts SET modified = ? WHERE id = ?', [CURRENT_TIMESTAMP, 42]);
109
+ const tokens = [
110
+ `UPDATE`,
111
+ mysql.escapeId(q.table),
112
+ `SET`,
113
+ Object.keys(record)
114
+ .map(f => mysql.escapeId((0, mysql_schema_util_1.mapNameToMySQL)(f)) + ' = ?')
115
+ .join(', '),
116
+ ...whereTokens(q),
117
+ ];
118
+ return mysql.format(tokens.join(' '), Object.values(record));
119
+ }
120
+ exports.dbQueryToSQLUpdate = dbQueryToSQLUpdate;
121
+ function selectTokens(q) {
122
+ let fields = ['*'];
123
+ if (q._selectedFieldNames) {
124
+ fields = q._selectedFieldNames.length ? q._selectedFieldNames : ['id'];
125
+ }
126
+ // We don't do `escapeId` cause it'll ruin e.g SELECT `count *` FROM ...
127
+ return [`SELECT`, fields.map(f => (0, mysql_schema_util_1.mapNameToMySQL)(f)).join(', '), `FROM`, mysql.escapeId(q.table)];
128
+ }
129
+ function offsetLimitTokens(q) {
130
+ const tokens = [];
131
+ if (q._limitValue) {
132
+ tokens.push(`LIMIT`, String(q._limitValue));
133
+ // In SQL OFFSET is only allowed if LIMIT is set
134
+ if (q._offsetValue) {
135
+ tokens.push(`OFFSET`, String(q._offsetValue));
136
+ }
137
+ }
138
+ return tokens;
139
+ }
140
+ function orderTokens(q) {
141
+ if (!q._orders.length)
142
+ return [];
143
+ return [
144
+ `ORDER BY`,
145
+ q._orders.map(o => `\`${o.name}\` ${o.descending ? 'DESC' : 'ASC'}`).join(', '),
146
+ ];
147
+ }
148
+ const OP_MAP = {
149
+ '==': '=',
150
+ };
151
+ function whereTokens(q) {
152
+ if (!q._filters.length)
153
+ return [];
154
+ return [
155
+ `WHERE`,
156
+ q._filters
157
+ .map(f => {
158
+ if (f.val === null || f.val === undefined) {
159
+ // special treatment
160
+ return [
161
+ mysql.escapeId((0, mysql_schema_util_1.mapNameToMySQL)(f.name)),
162
+ f.op === '==' ? 'IS NULL' : 'IS NOT NULL',
163
+ ].join(' ');
164
+ }
165
+ if (Array.isArray(f.val)) {
166
+ // special case for arrays
167
+ return `${mysql.escapeId((0, mysql_schema_util_1.mapNameToMySQL)(f.name))} IN (${mysql.escape(f.val)})`;
168
+ }
169
+ return [
170
+ mysql.escapeId((0, mysql_schema_util_1.mapNameToMySQL)(f.name)),
171
+ OP_MAP[f.op] || f.op,
172
+ mysql.escape(f.val),
173
+ ].join(' ');
174
+ })
175
+ .join(' AND '),
176
+ ];
177
+ }