@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 +198 -0
- package/dist/dbAdapter.d.ts +2 -0
- package/dist/dbAdapter.js +9 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +9 -0
- package/dist/mysql.db.d.ts +58 -0
- package/dist/mysql.db.js +214 -0
- package/dist/query.util.d.ts +11 -0
- package/dist/query.util.js +177 -0
- package/dist/schema/mysql.schema.util.d.ts +24 -0
- package/dist/schema/mysql.schema.util.js +109 -0
- package/package.json +52 -0
- package/readme.md +14 -0
- package/src/dbAdapter.ts +6 -0
- package/src/index.ts +7 -0
- package/src/mysql.db.ts +312 -0
- package/src/query.util.ts +216 -0
- package/src/schema/mysql.schema.util.ts +131 -0
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,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;
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|
package/dist/mysql.db.js
ADDED
|
@@ -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
|
+
}
|