mysql2-core 0.2.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/src/index.ts ADDED
@@ -0,0 +1,877 @@
1
+ import { Connection, createPool as createPool2, FieldPacket, format, OkPacket, Pool, PoolConnection, ResultSetHeader, RowDataPacket, } from 'mysql2';
2
+ import { buildToSave, buildToSaveBatch, Metadata } from './build';
3
+ import { Attribute, Attributes, Manager, Statement, StringMap } from './metadata';
4
+
5
+ export * from './metadata';
6
+ export * from './build';
7
+
8
+ // tslint:disable-next-line:class-name
9
+ export class resource {
10
+ static string?: boolean;
11
+ }
12
+ export interface Config {
13
+ host?: string | undefined;
14
+ port?: number;
15
+ server?: string | undefined;
16
+ database?: string | undefined;
17
+ user?: string | undefined;
18
+ password?: string | undefined;
19
+ multipleStatements?: boolean | undefined;
20
+ connectionLimit?: number | undefined;
21
+ max?: number | undefined;
22
+ min?: number | undefined;
23
+ idleTimeoutMillis?: number | undefined;
24
+ }
25
+ export function createPool(conf: Config): Pool {
26
+ if (conf.max && conf.max > 0 && !conf.connectionLimit) {
27
+ conf.connectionLimit = conf.max;
28
+ }
29
+ const pool = createPool2({...conf, rowsAsArray: true, multipleStatements: true});
30
+ return pool;
31
+ }
32
+ /*
33
+ // tslint:disable-next-line:max-classes-per-file
34
+ export class PoolManager implements Manager {
35
+ constructor(public pool: Pool) {
36
+ this.param = this.param.bind(this);
37
+ this.exec = this.exec.bind(this);
38
+ this.execBatch = this.execBatch.bind(this);
39
+ this.query = this.query.bind(this);
40
+ this.queryOne = this.queryOne.bind(this);
41
+ this.execScalar = this.execScalar.bind(this);
42
+ this.count = this.count.bind(this);
43
+ }
44
+ driver = 'mysql';
45
+ param(i: number): string {
46
+ return '?';
47
+ }
48
+ exec(sql: string, args?: any[], ctx?: any): Promise<number> {
49
+ const p = (ctx ? ctx : this.pool);
50
+ return exec(p, sql, args);
51
+ }
52
+ execBatch(statements: Statement[], firstSuccess?: boolean, ctx?: any): Promise<number> {
53
+ const p = (ctx ? ctx : this.pool);
54
+ return execBatch(p, statements, firstSuccess);
55
+ }
56
+ query<T>(sql: string, args?: any[], m?: StringMap, bools?: Attribute[], ctx?: any): Promise<T[]> {
57
+ const p = (ctx ? ctx : this.pool);
58
+ return query(p, sql, args, m, bools);
59
+ }
60
+ queryOne<T>(sql: string, args?: any[], m?: StringMap, bools?: Attribute[], ctx?: any): Promise<T | null> {
61
+ const p = (ctx ? ctx : this.pool);
62
+ return queryOne(p, sql, args, m, bools);
63
+ }
64
+ execScalar<T>(sql: string, args?: any[], ctx?: any): Promise<T> {
65
+ const p = (ctx ? ctx : this.pool);
66
+ return execScalar<T>(p, sql, args);
67
+ }
68
+ count(sql: string, args?: any[], ctx?: any): Promise<number> {
69
+ const p = (ctx ? ctx : this.pool);
70
+ return count(p, sql, args);
71
+ }
72
+ }
73
+ */
74
+ export function execBatch(pool: Pool, statements: Statement[], firstSuccess?: boolean): Promise<number> {
75
+ if (!statements || statements.length === 0) {
76
+ return Promise.resolve(0);
77
+ } else if (statements.length === 1) {
78
+ return exec(pool, statements[0].query, statements[0].params);
79
+ }
80
+ if (firstSuccess) {
81
+ return new Promise<number>((resolve, reject) => {
82
+ pool.getConnection((er0, connection) => {
83
+ if (er0) {
84
+ return reject(er0);
85
+ }
86
+ connection.beginTransaction(er1 => {
87
+ if (er1) {
88
+ connection.rollback(() => {
89
+ return reject(er1);
90
+ });
91
+ } else {
92
+ let query0 = '';
93
+ const queries: string[] = [];
94
+ const l = statements.length;
95
+ for (let j = 0; j < l; j++) {
96
+ const item = statements[j];
97
+ if (j === 0) {
98
+ query0 = format(item.query, toArray(item.params));
99
+ } else {
100
+ if (item.query.endsWith(';')) {
101
+ queries.push(format(item.query, toArray(item.params)));
102
+ } else {
103
+ queries.push(format(item.query + ';', toArray(item.params)));
104
+ }
105
+ }
106
+ }
107
+ connection.query<ResultSetHeader>(query0, (er2a, results0) => {
108
+ if (er2a) {
109
+ connection.rollback(() => {
110
+ return reject(er2a);
111
+ });
112
+ } else {
113
+ if (results0 && results0.affectedRows === 0) {
114
+ return 0;
115
+ } else {
116
+ connection.query<OkPacket[]>(queries.join(''), (er2, results) => {
117
+ if (er2) {
118
+ connection.rollback(() => {
119
+ return reject(er2);
120
+ });
121
+ } else {
122
+ connection.commit((er3) => {
123
+ if (er3) {
124
+ connection.rollback(() => {
125
+ return reject(er3);
126
+ });
127
+ }
128
+ });
129
+ let c = 0;
130
+ c += results0.affectedRows + results.reduce((prev,item) => prev + item.affectedRows, 0);
131
+ return resolve(c);
132
+ }
133
+ });
134
+ }
135
+ }
136
+ });
137
+ }
138
+ });
139
+ });
140
+ });
141
+ } else {
142
+ return new Promise<number>((resolve, reject) => {
143
+ pool.getConnection((er0, connection) => {
144
+ if (er0) {
145
+ return reject(er0);
146
+ }
147
+ connection.beginTransaction(er1 => {
148
+ if (er1) {
149
+ connection.rollback(() => {
150
+ return reject(er1);
151
+ });
152
+ } else {
153
+ const queries: string[] = [];
154
+ statements.forEach(item => {
155
+ if (item.query.endsWith(';')) {
156
+ queries.push(format(item.query, toArray(item.params)));
157
+ } else {
158
+ queries.push(format(item.query + ';', toArray(item.params)));
159
+ }
160
+ });
161
+ connection.query<OkPacket[]>(queries.join(''), (er2, results) => {
162
+ if (er2) {
163
+ connection.rollback(() => {
164
+ buildError(er2);
165
+ return reject(er2);
166
+ });
167
+ } else {
168
+ connection.commit((er3) => {
169
+ if (er3) {
170
+ connection.rollback(() => {
171
+ return reject(er3);
172
+ });
173
+ }
174
+ });
175
+ return resolve(results.reduce((prev,item) => prev + item.affectedRows, 0));
176
+ }
177
+ });
178
+ }
179
+ });
180
+ });
181
+ });
182
+ }
183
+ }
184
+ function buildError(err: any): any {
185
+ if (err.errno === 1062 && err.code === 'ER_DUP_ENTRY') {
186
+ err.error = 'duplicate';
187
+ }
188
+ return err;
189
+ }
190
+
191
+ export async function exec(pool: Pool, sql: string, args?: any[]): Promise<number> {
192
+ const p = toArray(args);
193
+ return new Promise<number>((resolve, reject) => {
194
+ return pool.execute<ResultSetHeader>(sql, p, (err, res) => {
195
+ if (err) {
196
+ buildError(err);
197
+ return reject(err);
198
+ } else {
199
+ return resolve(res.affectedRows);
200
+ }
201
+ });
202
+ });
203
+ }
204
+ export async function query<T>(pool: Pool | PoolConnection, sql: string, args?: any[], m?: StringMap, bools?: Attribute[]): Promise<T[]> {
205
+ const p = toArray(args);
206
+ return new Promise((resolve, reject) => {
207
+ return pool.query<T[] & ResultSetHeader>(sql, p, (err, results, fields) => {
208
+ if (err) {
209
+ return reject(err);
210
+ } else {
211
+ if(results.length > 0) {
212
+ const arrayResult = results.map(item => {
213
+ return formatData<T>(fields, item);
214
+ });
215
+ return resolve(handleResults(arrayResult, m, bools));
216
+ } else {
217
+ resolve([])
218
+ }
219
+ }
220
+ });
221
+ });
222
+ }
223
+
224
+ export function queryOne<T>(pool: Pool, sql: string, args?: any[], m?: StringMap, bools?: Attribute[]): Promise<T | null> {
225
+ return query<T>(pool, sql, args, m, bools).then(r => {
226
+ return (r && r.length > 0 ? r[0] : null);
227
+ });
228
+ }
229
+ export function execScalar<T>(pool: Pool, sql: string, args?: any[]): Promise<T> {
230
+ return queryOne<T>(pool, sql, args).then(r => {
231
+ if (!r) {
232
+ return null;
233
+ } else {
234
+ const keys = Object.keys(r);
235
+ return (r as any)[keys[0]];
236
+ }
237
+ });
238
+ }
239
+
240
+ export function count(pool: Pool, sql: string, args?: any[]): Promise<number> {
241
+ return execScalar<number>(pool, sql, args);
242
+ }
243
+
244
+ export function save<T>(pool: Pool | ((sql: string, args?: any[]) => Promise<number>), obj: T, table: string, attrs: Attributes, ver?: string, buildParam?: (i: number) => string, i?: number): Promise<number> {
245
+ const s = buildToSave(obj, table, attrs, ver, buildParam);
246
+ if (!s) {
247
+ return Promise.resolve(-1);
248
+ }
249
+ if (typeof pool === 'function') {
250
+ return pool(s.query, s.params);
251
+ } else {
252
+ return exec(pool, s.query, s.params);
253
+ }
254
+ }
255
+ export function saveBatch<T>(pool: Pool | ((statements: Statement[]) => Promise<number>), objs: T[], table: string, attrs: Attributes, ver?: string, buildParam?: (i: number) => string): Promise<number> {
256
+ const s = buildToSaveBatch(objs, table, attrs, ver, buildParam);
257
+ if (typeof pool === 'function') {
258
+ return pool(s);
259
+ } else {
260
+ return execBatch(pool, s);
261
+ }
262
+ }
263
+ export function toArray(arr?: any[]): any[] {
264
+ if (!arr || arr.length === 0) {
265
+ return [];
266
+ }
267
+ const p: any[] = [];
268
+ const l = arr.length;
269
+ for (let i = 0; i < l; i++) {
270
+ if (arr[i] === undefined || arr[i] == null) {
271
+ p.push(null);
272
+ } else {
273
+ if (typeof arr[i] === 'object') {
274
+ if (arr[i] instanceof Date) {
275
+ p.push(arr[i]);
276
+ } else {
277
+ if (resource.string) {
278
+ const s: string = JSON.stringify(arr[i]);
279
+ p.push(s);
280
+ } else {
281
+ p.push(arr[i]);
282
+ }
283
+ }
284
+ } else {
285
+ p.push(arr[i]);
286
+ }
287
+ }
288
+ }
289
+ return p;
290
+ }
291
+ export function handleResults<T>(r: T[], m?: StringMap, bools?: Attribute[]) {
292
+ if (m) {
293
+ const res = mapArray(r, m);
294
+ if (bools && bools.length > 0) {
295
+ return handleBool(res, bools);
296
+ } else {
297
+ return res;
298
+ }
299
+ } else {
300
+ if (bools && bools.length > 0) {
301
+ return handleBool(r, bools);
302
+ } else {
303
+ return r;
304
+ }
305
+ }
306
+ }
307
+ export function handleBool<T>(objs: T[], bools: Attribute[]) {
308
+ if (!bools || bools.length === 0 || !objs) {
309
+ return objs;
310
+ }
311
+ for (const obj of objs) {
312
+ const o: any = obj;
313
+ for (const field of bools) {
314
+ if (field.name) {
315
+ const v = o[field.name];
316
+ if (typeof v !== 'boolean' && v != null && v !== undefined) {
317
+ const b = field.true;
318
+ if (b == null || b === undefined) {
319
+ // tslint:disable-next-line:triple-equals
320
+ o[field.name] = ('1' == v || 'T' == v || 'Y' == v || 'true' == v || 'on' == v);
321
+ } else {
322
+ // tslint:disable-next-line:triple-equals
323
+ o[field.name] = (v == b ? true : false);
324
+ }
325
+ }
326
+ }
327
+ }
328
+ }
329
+ return objs;
330
+ }
331
+ export function formatData<T>(nameColumn: FieldPacket[], data: any, m?: StringMap): T {
332
+ const result: any = {};
333
+ nameColumn.forEach((item, index) => {
334
+ let key = item.name;
335
+ if (m) {
336
+ key = m[item.name];
337
+ }
338
+ result[key] = data[index];
339
+ });
340
+ return result;
341
+ }
342
+ export function map<T>(obj: T, m?: StringMap): any {
343
+ if (!m) {
344
+ return obj;
345
+ }
346
+ const mkeys = Object.keys(m);
347
+ if (mkeys.length === 0) {
348
+ return obj;
349
+ }
350
+ const o: any = {};
351
+ const keys = Object.keys(obj as any);
352
+ for (const key of keys) {
353
+ let k0 = m[key];
354
+ if (!k0) {
355
+ k0 = key;
356
+ }
357
+ o[k0] = (obj as any)[key];
358
+ }
359
+ return o;
360
+ }
361
+ export function mapArray<T>(results: T[], m?: StringMap): T[] {
362
+ if (!m) {
363
+ return results;
364
+ }
365
+ const mkeys = Object.keys(m);
366
+ if (mkeys.length === 0) {
367
+ return results;
368
+ }
369
+ const objs = [];
370
+ const length = results.length;
371
+ for (let i = 0; i < length; i++) {
372
+ const obj = results[i];
373
+ const obj2: any = {};
374
+ const keys = Object.keys(obj as any);
375
+ for (const key of keys) {
376
+ let k0 = m[key];
377
+ if (!k0) {
378
+ k0 = key;
379
+ }
380
+ obj2[k0] = (obj as any)[key];
381
+ }
382
+ objs.push(obj2);
383
+ }
384
+ return objs;
385
+ }
386
+ export function getFields(fields: string[], all?: string[]): string[] | undefined {
387
+ if (!fields || fields.length === 0) {
388
+ return undefined;
389
+ }
390
+ const ext: string[] = [];
391
+ if (all) {
392
+ for (const s of fields) {
393
+ if (all.includes(s)) {
394
+ ext.push(s);
395
+ }
396
+ }
397
+ if (ext.length === 0) {
398
+ return undefined;
399
+ } else {
400
+ return ext;
401
+ }
402
+ } else {
403
+ return fields;
404
+ }
405
+ }
406
+ export function buildFields(fields: string[], all?: string[]): string {
407
+ const s = getFields(fields, all);
408
+ if (!s || s.length === 0) {
409
+ return '*';
410
+ } else {
411
+ return s.join(',');
412
+ }
413
+ }
414
+ export function getMapField(name: string, mp?: StringMap): string {
415
+ if (!mp) {
416
+ return name;
417
+ }
418
+ const x = mp[name];
419
+ if (!x) {
420
+ return name;
421
+ }
422
+ if (typeof x === 'string') {
423
+ return x;
424
+ }
425
+ return name;
426
+ }
427
+ export function isEmpty(s: string): boolean {
428
+ return !(s && s.length > 0);
429
+ }
430
+ // tslint:disable-next-line:max-classes-per-file
431
+ export class StringService {
432
+ constructor(protected pool: Pool, public table: string, public column: string) {
433
+ this.load = this.load.bind(this);
434
+ this.save = this.save.bind(this);
435
+ }
436
+ load(key: string, max: number): Promise<string[]> {
437
+ const s = `select ${this.column} from ${this.table} where ${this.column} like ? order by ${this.column} limit ${max}`;
438
+ return query(this.pool, s, ['' + key + '%']).then(arr => {
439
+ return arr.map(i => (i as any)[this.column] as string);
440
+ });
441
+ }
442
+ save(values: string[]): Promise<number> {
443
+ if (!values || values.length === 0) {
444
+ return Promise.resolve(0);
445
+ }
446
+ const arr: string[] = [];
447
+ for (let i = 1; i <= values.length; i++) {
448
+ arr.push('(?)');
449
+ }
450
+ const s = `insert ignore into ${this.table}(${this.column})values${arr.join(',')}`;
451
+ return exec(this.pool, s, values);
452
+ }
453
+ }
454
+
455
+ export function version(attrs: Attributes): Attribute | undefined {
456
+ const ks = Object.keys(attrs);
457
+ for (const k of ks) {
458
+ const attr = attrs[k];
459
+ if (attr.version) {
460
+ attr.name = k;
461
+ return attr;
462
+ }
463
+ }
464
+ return undefined;
465
+ }
466
+ // tslint:disable-next-line:max-classes-per-file
467
+ export class MySQLWriter<T> {
468
+ pool?: Pool;
469
+ version?: string;
470
+ exec?: (sql: string, args?: any[]) => Promise<number>;
471
+ map?: (v: T) => T;
472
+ param?: (i: number) => string;
473
+ constructor(pool: Pool | ((sql: string, args?: any[]) => Promise<number>), public table: string, public attributes: Attributes, public oneIfSuccess?: boolean, toDB?: (v: T) => T, buildParam?: (i: number) => string) {
474
+ this.write = this.write.bind(this);
475
+ if (typeof pool === 'function') {
476
+ this.exec = pool;
477
+ } else {
478
+ this.pool = pool;
479
+ }
480
+ this.param = buildParam;
481
+ this.map = toDB;
482
+ const x = version(attributes);
483
+ if (x) {
484
+ this.version = x.name;
485
+ }
486
+ }
487
+ write(obj: T): Promise<number> {
488
+ if (!obj) {
489
+ return Promise.resolve(0);
490
+ }
491
+ let obj2: NonNullable<T> | T = obj;
492
+ if (this.map) {
493
+ obj2 = this.map(obj);
494
+ }
495
+ const stmt = buildToSave(obj2, this.table, this.attributes, this.version, this.param);
496
+ if (stmt) {
497
+ if (this.exec) {
498
+ if (this.oneIfSuccess) {
499
+ return this.exec(stmt.query, stmt.params).then(ct => ct > 0 ? 1 : 0);
500
+ } else {
501
+ return this.exec(stmt.query, stmt.params);
502
+ }
503
+ } else {
504
+ if (this.oneIfSuccess) {
505
+ return exec(this.pool as any, stmt.query, stmt.params).then(ct => ct > 0 ? 1 : 0);
506
+ } else {
507
+ return exec(this.pool as any, stmt.query, stmt.params);
508
+ }
509
+ }
510
+ } else {
511
+ return Promise.resolve(0);
512
+ }
513
+ }
514
+ }
515
+ // tslint:disable-next-line:max-classes-per-file
516
+ export class MySQLStreamWriter<T> {
517
+ list: T[] = [];
518
+ size = 0;
519
+ pool?: Pool;
520
+ version?: string;
521
+ execBatch?: (statements: Statement[]) => Promise<number>;
522
+ map?: (v: T) => T;
523
+ param?: (i: number) => string;
524
+ constructor(pool: Pool | ((statements: Statement[]) => Promise<number>), public table: string, public attributes: Attributes, size?: number, toDB?: (v: T) => T, buildParam?: (i: number) => string) {
525
+ this.write = this.write.bind(this);
526
+ this.flush = this.flush.bind(this);
527
+ if (typeof pool === 'function') {
528
+ this.execBatch = pool;
529
+ } else {
530
+ this.pool = pool;
531
+ }
532
+ this.param = buildParam;
533
+ this.map = toDB;
534
+ const x = version(attributes);
535
+ if (x) {
536
+ this.version = x.name;
537
+ }
538
+ if (size) {
539
+ this.size = size;
540
+ }
541
+ }
542
+ write(obj: T): Promise<number> {
543
+ if (!obj) {
544
+ return Promise.resolve(0);
545
+ }
546
+ let obj2: NonNullable<T> | T = obj;
547
+ if (this.map) {
548
+ obj2 = this.map(obj);
549
+ this.list.push(obj2);
550
+ } else {
551
+ this.list.push(obj);
552
+ }
553
+ if (this.list.length < this.size) {
554
+ return Promise.resolve(0);
555
+ } else {
556
+ return this.flush();
557
+ }
558
+ }
559
+ flush(): Promise<number> {
560
+ if (!this.list || this.list.length === 0) {
561
+ return Promise.resolve(0);
562
+ } else {
563
+ const total = this.list.length;
564
+ const stmt = buildToSaveBatch(this.list, this.table, this.attributes, this.version, this.param);
565
+ if (stmt) {
566
+ if (this.execBatch) {
567
+ return this.execBatch(stmt).then(r => {
568
+ this.list = [];
569
+ return total;
570
+ });
571
+ } else {
572
+ return execBatch(this.pool as any, stmt).then(r => {
573
+ this.list = [];
574
+ return total;
575
+ });
576
+ }
577
+ } else {
578
+ return Promise.resolve(0);
579
+ }
580
+ }
581
+ }
582
+ }
583
+ // tslint:disable-next-line:max-classes-per-file
584
+ export class MySQLBatchWriter<T> {
585
+ pool?: Pool;
586
+ version?: string;
587
+ execute?: (statements: Statement[]) => Promise<number>;
588
+ map?: (v: T) => T;
589
+ param?: (i: number) => string;
590
+ constructor(pool: Pool | ((statements: Statement[]) => Promise<number>), public table: string, public attributes: Attributes, public oneIfSuccess?: boolean, toDB?: (v: T) => T, buildParam?: (i: number) => string) {
591
+ this.write = this.write.bind(this);
592
+ if (typeof pool === 'function') {
593
+ this.execute = pool;
594
+ } else {
595
+ this.pool = pool;
596
+ }
597
+ this.param = buildParam;
598
+ this.map = toDB;
599
+ const x = version(attributes);
600
+ if (x) {
601
+ this.version = x.name;
602
+ }
603
+ }
604
+ write(objs: T[]): Promise<number> {
605
+ if (!objs || objs.length === 0) {
606
+ return Promise.resolve(0);
607
+ }
608
+ let list = objs;
609
+ if (this.map) {
610
+ list = [];
611
+ for (const obj of objs) {
612
+ const obj2 = this.map(obj);
613
+ list.push(obj2);
614
+ }
615
+ }
616
+ const stmts = buildToSaveBatch(list, this.table, this.attributes, this.version, this.param);
617
+ if (stmts && stmts.length > 0) {
618
+ if (this.execute) {
619
+ if (this.oneIfSuccess) {
620
+ return this.execute(stmts).then(ct => stmts.length);
621
+ } else {
622
+ return this.execute(stmts);
623
+ }
624
+ } else {
625
+ if (this.oneIfSuccess) {
626
+ return execBatch(this.pool as any, stmts).then(ct => stmts.length);
627
+ } else {
628
+ return execBatch(this.pool as any, stmts);
629
+ }
630
+ }
631
+ } else {
632
+ return Promise.resolve(0);
633
+ }
634
+ }
635
+ }
636
+
637
+ export interface AnyMap {
638
+ [key: string]: any;
639
+ }
640
+ // tslint:disable-next-line:max-classes-per-file
641
+ export class MySQLChecker {
642
+ timeout: number;
643
+ service: string;
644
+ constructor(private pool: Pool, service?: string, timeout?: number) {
645
+ this.timeout = (timeout ? timeout : 4200);
646
+ this.service = (service ? service : 'mysql');
647
+ this.check = this.check.bind(this);
648
+ this.name = this.name.bind(this);
649
+ this.build = this.build.bind(this);
650
+ }
651
+ async check(): Promise<AnyMap> {
652
+ const obj = {} as AnyMap;
653
+ const promise = new Promise<any>((resolve, reject) => {
654
+ this.pool.query('select current_time', (err, result) => {
655
+ if (err) {
656
+ return reject(err);
657
+ } else {
658
+ resolve(obj);
659
+ }
660
+ });
661
+ });
662
+ if (this.timeout > 0) {
663
+ return promiseTimeOut(this.timeout, promise);
664
+ } else {
665
+ return promise;
666
+ }
667
+ }
668
+ name(): string {
669
+ return this.service;
670
+ }
671
+ build(data: AnyMap, err: any): AnyMap {
672
+ if (err) {
673
+ if (!data) {
674
+ data = {} as AnyMap;
675
+ }
676
+ data['error'] = err;
677
+ }
678
+ return data;
679
+ }
680
+ }
681
+
682
+ function promiseTimeOut(timeoutInMilliseconds: number, promise: Promise<any>): Promise<any> {
683
+ return Promise.race([
684
+ promise,
685
+ new Promise((resolve, reject) => {
686
+ setTimeout(() => {
687
+ reject(`Timed out in: ${timeoutInMilliseconds} milliseconds!`);
688
+ }, timeoutInMilliseconds);
689
+ })
690
+ ]);
691
+ }
692
+ export interface Formatter<T> {
693
+ format: (row: T) => string;
694
+ }
695
+ export interface FileWriter {
696
+ write(chunk: string): boolean;
697
+ flush?(cb?: () => void): void;
698
+ end?(cb?: () => void): void;
699
+ }
700
+ export interface QueryBuilder {
701
+ build(ctx?: any): Promise<Statement>;
702
+ }
703
+ // tslint:disable-next-line:max-classes-per-file
704
+ export class Exporter<T> {
705
+ constructor(
706
+ public connection: Connection,
707
+ public buildQuery: (ctx?: any) => Promise<Statement>,
708
+ ft: (row: T) => string,
709
+ public write: (chunk: string) => boolean,
710
+ public end: (cb?: () => void) => void,
711
+ public attributes?: Attributes
712
+ ) {
713
+ this.format = ft;
714
+ if (attributes) {
715
+ this.map = buildMap(attributes);
716
+ }
717
+ this.export = this.export.bind(this);
718
+ }
719
+ map?: StringMap;
720
+ format: (row: T) => string;
721
+ async export(ctx?: any): Promise<number> {
722
+ let idx = 0;
723
+ const stmt = await this.buildQuery(ctx);
724
+ const reader = this.connection.query(stmt.query, stmt.params);
725
+ let er: any;
726
+ reader.on('error', (err) => er = err);
727
+ // (D2) WRITE ROW-BY-ROW
728
+ if (this.map) {
729
+ reader.on('result', async (row: any) => {
730
+ ++idx;
731
+ this.connection.pause();
732
+ const obj = mapOne<T>(row, this.map);
733
+ const data = this.format(obj);
734
+ this.write(data);
735
+ this.connection.resume();
736
+ });
737
+ } else {
738
+ reader.on('result', async (row: any) => {
739
+ ++idx;
740
+ this.connection.pause();
741
+ const data = this.format(row as T);
742
+ this.write(data);
743
+ this.connection.resume();
744
+ });
745
+ }
746
+ // (D3) CLOSE CONNECTION + FILE
747
+ return new Promise<number>((resolve, reject) => {
748
+ reader.on('end', () => {
749
+ this.end();
750
+ if (er) {
751
+ reject(er);
752
+ } else {
753
+ this.connection.end((err) => {
754
+ if (err) {
755
+ reject(err);
756
+ } else {
757
+ resolve(idx);
758
+ }
759
+ });
760
+ }
761
+ });
762
+ });
763
+ }
764
+ }
765
+ // tslint:disable-next-line:max-classes-per-file
766
+ export class ExportService<T> {
767
+ constructor(
768
+ public connection: Connection,
769
+ public queryBuilder: QueryBuilder,
770
+ public formatter: Formatter<T>,
771
+ public writer: FileWriter,
772
+ public attributes?: Attributes
773
+ ) {
774
+ if (attributes) {
775
+ this.map = buildMap(attributes);
776
+ }
777
+ this.export = this.export.bind(this);
778
+ }
779
+ map?: StringMap;
780
+ async export(ctx?: any): Promise<number> {
781
+ let idx = 0;
782
+ const stmt = await this.queryBuilder.build(ctx);
783
+ const reader = this.connection.query(stmt.query, stmt.params);
784
+ let er: any;
785
+ reader.on('error', (err) => er = err);
786
+ // (D2) WRITE ROW-BY-ROW
787
+ if (this.map) {
788
+ reader.on('result', async (row: any) => {
789
+ ++idx;
790
+ this.connection.pause();
791
+ const obj = mapOne<T>(row, this.map);
792
+ const data = this.formatter.format(obj);
793
+ this.writer.write(data);
794
+ this.connection.resume();
795
+ });
796
+ } else {
797
+ reader.on('result', async (row: any) => {
798
+ ++idx;
799
+ this.connection.pause();
800
+ const data = this.formatter.format(row as T);
801
+ this.writer.write(data);
802
+ this.connection.resume();
803
+ });
804
+ }
805
+ // (D3) CLOSE CONNECTION + FILE
806
+ return new Promise<number>((resolve, reject) => {
807
+ reader.on('end', () => {
808
+ if (this.writer.end) {
809
+ this.writer.end();
810
+ } else if (this.writer.flush) {
811
+ this.writer.flush();
812
+ }
813
+ if (er) {
814
+ reject(er);
815
+ } else {
816
+ this.connection.end((err) => {
817
+ if (err) {
818
+ reject(err);
819
+ } else {
820
+ resolve(idx);
821
+ }
822
+ });
823
+ }
824
+ });
825
+ });
826
+ }
827
+ }
828
+ export function mapOne<T>(results: any, m?: StringMap): T {
829
+ const obj: any = results;
830
+ if (!m) {
831
+ return obj;
832
+ }
833
+ const mkeys = Object.keys(m as any);
834
+ if (mkeys.length === 0) {
835
+ return obj;
836
+ }
837
+ const obj2: any = {};
838
+ const keys = Object.keys(obj);
839
+ for (const key of keys) {
840
+ let k0 = m[key];
841
+ if (!k0) {
842
+ k0 = key;
843
+ }
844
+ obj2[k0] = (obj)[key];
845
+ }
846
+ return obj2;
847
+ }
848
+ export function buildMap(attrs: Attributes): StringMap|undefined {
849
+ const mp: StringMap = {};
850
+ const ks = Object.keys(attrs);
851
+ let isMap = false;
852
+ for (const k of ks) {
853
+ const attr = attrs[k];
854
+ attr.name = k;
855
+ const field = (attr.column ? attr.column : k);
856
+ const s = field.toLowerCase();
857
+ if (s !== k) {
858
+ mp[s] = k;
859
+ isMap = true;
860
+ }
861
+ }
862
+ if (isMap) {
863
+ return mp;
864
+ }
865
+ return undefined;
866
+ }
867
+ export function select(table: string, attrs: Attributes): string {
868
+ const cols: string[] = [];
869
+ const ks = Object.keys(attrs);
870
+ for (const k of ks) {
871
+ const attr = attrs[k];
872
+ attr.name = k;
873
+ const field = (attr.column ? attr.column : k);
874
+ cols.push(field);
875
+ }
876
+ return `select ${cols.join(',')} from ${table}`;
877
+ }