@l-v-yonsama/multi-platform-database-drivers 0.1.1

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.
Files changed (42) hide show
  1. package/.editorconfig +15 -0
  2. package/.eslintignore +1 -0
  3. package/.eslintrc.json +30 -0
  4. package/.github/workflows/npm-publish-github-packages.yml +50 -0
  5. package/.prettierrc +12 -0
  6. package/README.md +7 -0
  7. package/__tests__/db/drivers/AwsS3Driver.test.ts +97 -0
  8. package/__tests__/db/drivers/MySQLDriver.test.ts +140 -0
  9. package/__tests__/db/drivers/PostgresDriver.test.ts +139 -0
  10. package/__tests__/db/drivers/RedisDriver.test.ts +156 -0
  11. package/jest.config.js +18 -0
  12. package/package.json +76 -0
  13. package/src/db/DBError.ts +26 -0
  14. package/src/db/drivers/AwsS3Driver.ts +539 -0
  15. package/src/db/drivers/BaseDriver.ts +256 -0
  16. package/src/db/drivers/MongoDriver.js +110 -0
  17. package/src/db/drivers/MySQLDriver.ts +318 -0
  18. package/src/db/drivers/PostgresDriver.ts +363 -0
  19. package/src/db/drivers/RedisDriver.ts +277 -0
  20. package/src/db/manager.ts +95 -0
  21. package/src/db/resource/DbResource.ts +579 -0
  22. package/src/db/resource/MultipleResultSetDataHolder.ts +119 -0
  23. package/src/db/resource/ResourceUtil.ts +366 -0
  24. package/src/db/resource/ResultSetDataHolder.ts +806 -0
  25. package/src/db/resource/types/DBType.ts +43 -0
  26. package/src/db/resource/types/GeneralColumnType.ts +321 -0
  27. package/src/db/resource/types/MySQLColumnType.ts +134 -0
  28. package/src/db/resource/types/ODBCVendorType.ts +3 -0
  29. package/src/db/resource/types/PostgresColumnType.ts +94 -0
  30. package/src/db/resource/types/RedisKeyType.ts +48 -0
  31. package/src/db/resource/types/ResourceType.ts +10 -0
  32. package/src/main.ts +1 -0
  33. package/src/service/request/general_db_request.ts +20 -0
  34. package/src/service/request/redis_request.ts +21 -0
  35. package/src/service/request/s3_request.ts +15 -0
  36. package/src/service/response/GeneralReponse.ts +10 -0
  37. package/src/types/DateModifiedType.ts +75 -0
  38. package/src/types/FileKindType.ts +80 -0
  39. package/src/util/file_util.ts +178 -0
  40. package/tsconfig.json +23 -0
  41. package/tsconfig.release.json +23 -0
  42. package/unit-test.yml +41 -0
@@ -0,0 +1,363 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import BaseDriver, { RequestSqlOptions } from './BaseDriver';
3
+ import ResultSetDataHolder, { RdhKey } from '../resource/ResultSetDataHolder';
4
+ import {
5
+ DbConnection,
6
+ DbResource,
7
+ DbDatabase,
8
+ DbSchema,
9
+ DbTable,
10
+ DbColumn,
11
+ ColumnResolver,
12
+ TableRows,
13
+ SchemaAndTableHints,
14
+ } from '../resource/DbResource';
15
+ import { default as pg } from 'pg';
16
+ import { PostgresColumnType } from '../resource/types/PostgresColumnType';
17
+ import { EnumValues } from 'enum-values';
18
+ import { GeneralColumnType } from '../resource/types/GeneralColumnType';
19
+
20
+ export default class PostgresDriver extends BaseDriver {
21
+ private pool: pg.Pool;
22
+
23
+ constructor(conRes: DbConnection) {
24
+ super(conRes);
25
+ }
26
+
27
+ // name: 'name',
28
+ // tableID: 12822,
29
+ // columnID: 2,
30
+ // dataTypeID: 1043,
31
+ // dataTypeSize: -1,
32
+ // dataTypeModifier: -1,
33
+ // format: 'text' }
34
+ fieldInfo2Key(fieldInfo: pg.FieldDef, resolver?: ColumnResolver): RdhKey {
35
+ if (fieldInfo.name.startsWith('c_')) {
36
+ console.log(
37
+ `★ ${fieldInfo.name.substring(2).toUpperCase()} = ${
38
+ fieldInfo.dataTypeID
39
+ }`,
40
+ );
41
+ }
42
+ const name = EnumValues.getNameFromValue(
43
+ PostgresColumnType,
44
+ PostgresColumnType.parse(fieldInfo.dataTypeID),
45
+ );
46
+ const key = new RdhKey(
47
+ fieldInfo.name,
48
+ GeneralColumnType.parse(name),
49
+ super.resolveColumnComment(fieldInfo.name, resolver),
50
+ );
51
+ return key;
52
+ }
53
+
54
+ async connectSub(): Promise<string> {
55
+ let errorReason = '';
56
+
57
+ const options = Object.assign(
58
+ {
59
+ port: 5432,
60
+ host: '127.0.0.1',
61
+ database: 'postgres',
62
+ },
63
+ {
64
+ max: 1,
65
+ idleTimeoutMillis: 3000,
66
+ connectionTimeoutMillis: 1000,
67
+ port: this.conRes.port,
68
+ host: this.conRes.host,
69
+ user: this.conRes.user,
70
+ password: this.conRes.password,
71
+ database: this.conRes.database,
72
+ },
73
+ );
74
+
75
+ this.pool = new pg.Pool(options);
76
+ // the pool with emit an error on behalf of any idle clients
77
+ // it contains if a backend error or network partition happens
78
+ // this.pool.on('error', (err, client) => {
79
+ // // log.error('Unexpected error on idle client', err);
80
+ // });
81
+ // this.pool.on('acquire', function (client) {
82
+ // // log.info('acquire', client);
83
+ // });
84
+ // this.pool.on('connect', function (client) {
85
+ // // log.info('connect', client);
86
+ // });
87
+ errorReason = await this.test();
88
+
89
+ return errorReason;
90
+ }
91
+
92
+ async test(with_connect = false): Promise<string> {
93
+ let errorReason = '';
94
+ try {
95
+ if (with_connect) {
96
+ errorReason = await this.asyncConnect();
97
+ }
98
+ const rdh = await this.requestSql('SELECT NOW()');
99
+ if (rdh && rdh.errorMessage) {
100
+ errorReason = rdh.errorMessage;
101
+ }
102
+ } catch (e) {
103
+ errorReason = e.message;
104
+ } finally {
105
+ if (with_connect) {
106
+ await this.asyncClose();
107
+ }
108
+ }
109
+ return errorReason;
110
+ }
111
+
112
+ // public
113
+ async requestSql(
114
+ sql: string,
115
+ options?: RequestSqlOptions,
116
+ ): Promise<ResultSetDataHolder> {
117
+ // log.info("sql2=", sql);
118
+ let rdh = new ResultSetDataHolder([]);
119
+
120
+ try {
121
+ let binds: string[] = [];
122
+ if (options && options.binds) {
123
+ binds = options.binds;
124
+ }
125
+ const results = await this.pool.query(sql, binds);
126
+ // command: 'SELECT',
127
+ // rowCount: 5,
128
+ // oid: null,
129
+ // rows:
130
+ // [ anonymous { name: 'pg_catalog' },
131
+ // anonymous { name: 'pg_temp_1' },
132
+ // anonymous { name: 'pg_toast' },
133
+ // anonymous { name: 'pg_toast_temp_1' },
134
+ // anonymous { name: 'public' } ],
135
+ // fields: [ ],
136
+ // console.log('done.', results.fields)
137
+ if (results) {
138
+ let resolver: ColumnResolver;
139
+ if (options && options.needs_column_resolve === true) {
140
+ resolver = this.createColumnResolver(sql);
141
+ }
142
+ const fields = results.fields;
143
+ rdh = new ResultSetDataHolder(
144
+ fields === undefined
145
+ ? []
146
+ : fields.map((f) => this.fieldInfo2Key(f, resolver)),
147
+ );
148
+ if (results.rows) {
149
+ results.rows.forEach((result: any) => {
150
+ rdh.addRow(result);
151
+ });
152
+ }
153
+ }
154
+ } catch (err) {
155
+ rdh = ResultSetDataHolder.create(err);
156
+ }
157
+
158
+ return rdh;
159
+ }
160
+
161
+ async countTables(
162
+ tables: SchemaAndTableHints,
163
+ options: any,
164
+ ): Promise<TableRows[]> {
165
+ const list = new Array<TableRows>();
166
+ let counter = 1;
167
+ for (const st of tables.list) {
168
+ let prefix = '';
169
+ if (st.schema) {
170
+ prefix = st.schema + '.';
171
+ }
172
+ if (options && options.progress_callback) {
173
+ if (counter % 5 === 0) {
174
+ options.progress_callback(`Count ${prefix}${st.table}`, 70);
175
+ }
176
+ }
177
+ const sql = `SELECT COUNT(*) as count FROM ${prefix}${st.table}`;
178
+ try {
179
+ const results = await this.pool.query(sql, []);
180
+ if (results && results.rows && results.rows.length > 0) {
181
+ const row = results.rows[0];
182
+ const obj: TableRows = Object.assign({ count: row.count }, st);
183
+ list.push(obj);
184
+ }
185
+ // eslint-disable-next-line no-empty
186
+ } catch (e) {}
187
+ counter++;
188
+ }
189
+ return list;
190
+ }
191
+
192
+ async getResouces(options: {
193
+ progress_callback?: Function | undefined;
194
+ params?: any;
195
+ }): Promise<Array<DbResource>> {
196
+ if (!this.conRes) {
197
+ return [];
198
+ }
199
+ const dbResources = new Array<DbResource>();
200
+ const db_list = await this.asyncGetDatabases(this.conRes.database);
201
+ db_list.forEach((db) => dbResources.push(db));
202
+ const dbDatabase = db_list.find((d) => d.name === this.conRes.database);
203
+
204
+ const dbSchemas = await this.getSchemas(dbDatabase);
205
+ dbSchemas.forEach((res) => {
206
+ dbDatabase.addChild(res);
207
+ });
208
+ // const parallels = [];
209
+ for (const dbSchema of dbSchemas) {
210
+ const dbTables = await this.getTables(dbSchema);
211
+ dbTables.forEach((res) => dbSchema.addChild(res));
212
+ }
213
+ for (const dbSchema of dbSchemas) {
214
+ for (const dbTable of dbSchema.getChildren()) {
215
+ const dbColumns = await this.getColumns(<DbTable>dbTable);
216
+ dbColumns.forEach((res) => dbTable.addChild(res));
217
+ }
218
+ }
219
+ return dbResources;
220
+ }
221
+
222
+ async asyncGetDatabases(
223
+ connection_database: string,
224
+ ): Promise<Array<DbDatabase>> {
225
+ const rdh = await this
226
+ .requestSql(`SELECT datname AS name, pg_encoding_to_char(encoding) AS comment
227
+ FROM pg_database ORDER BY datname`);
228
+ return rdh.rows.map((r) => {
229
+ const res = new DbDatabase(r.values.name);
230
+ res.comment = r.values.comment;
231
+ res.disabled = res.name !== connection_database;
232
+ return res;
233
+ });
234
+ }
235
+
236
+ async getSchemas(dbDatabase: DbDatabase): Promise<Array<DbSchema>> {
237
+ const rdh = await this.requestSql(`SELECT SCHEMA_NAME AS name
238
+ FROM INFORMATION_SCHEMA.SCHEMATA
239
+ WHERE LOWER(SCHEMA_NAME) NOT IN ('information_schema', 'sys', 'performance_schema', 'pg_catalog', 'pg_toast', 'pg_temp_1', 'pg_toast_temp_1')
240
+ ORDER BY name`);
241
+
242
+ return rdh.rows.map((r) => {
243
+ const res = new DbSchema(r.values.name);
244
+ return res;
245
+ });
246
+ }
247
+
248
+ async getTables(dbSchema: DbSchema): Promise<Array<DbTable>> {
249
+ let rdh = await this
250
+ .requestSql(`select quote_ident(m.relname) as qname, COALESCE(d.description, '') as comment
251
+ from pg_stat_all_tables as m
252
+ LEFT JOIN pg_description as d ON (m.relid = d.objoid AND d.objsubid=0)
253
+ WHERE m.schemaname='${dbSchema.getName()}'
254
+ ORDER BY m.relname`);
255
+
256
+ const list = rdh.rows.map((r) => {
257
+ const res = new DbTable(r.values.qname, 'TABLE', r.values.comment);
258
+ return res;
259
+ });
260
+
261
+ rdh = await this
262
+ .requestSql(`select quote_ident(viewname) as qname, definition from pg_catalog.pg_views
263
+ where schemaname = '${dbSchema.getName()}'
264
+ order by viewname`);
265
+
266
+ return list.concat(
267
+ rdh.rows.map((r) => {
268
+ const res = new DbTable(r.values.qname, 'VIEW', '');
269
+ return res;
270
+ }),
271
+ );
272
+ }
273
+
274
+ async getColumns(dbTable: DbTable): Promise<Array<DbColumn>> {
275
+ const binds = [dbTable.getParent().getName(), dbTable.getName()];
276
+ const rdh = await this.requestSql(
277
+ `select
278
+ col.COLUMN_NAME as name,
279
+ quote_ident(col.COLUMN_NAME) as qname,
280
+ data_type as col_type,
281
+ case
282
+ when IS_NULLABLE = 'YES' then 1
283
+ else 0
284
+ end as nullable,
285
+ case
286
+ when pk.column_name is not null then 'PRI'
287
+ else null
288
+ end as col_key,
289
+ COLUMN_DEFAULT as col_default,
290
+ null as col_extra,
291
+ (
292
+ select
293
+ pg_catalog.col_description(oid,
294
+ col.ordinal_position::int)
295
+ from
296
+ pg_catalog.pg_class c
297
+ where
298
+ c.relname = col.table_name) as comment
299
+ from
300
+ INFORMATION_SCHEMA.columns col
301
+ left join (
302
+ select
303
+ tc.table_catalog,
304
+ tc.table_schema,
305
+ tc.table_name,
306
+ ccu.column_name
307
+ from
308
+ information_schema.table_constraints tc
309
+ inner join
310
+ information_schema.constraint_column_usage ccu
311
+ on
312
+ (tc.table_catalog = ccu.table_catalog
313
+ and
314
+ tc.table_schema = ccu.table_schema
315
+ and
316
+ tc.table_name = ccu.table_name
317
+ and
318
+ tc.constraint_name = ccu.constraint_name)
319
+ where
320
+ tc.constraint_type = 'PRIMARY KEY'
321
+ ) pk on
322
+ (col.table_catalog = pk.table_catalog
323
+ and col.table_schema = pk.table_schema
324
+ and col.table_name = pk.table_name
325
+ and col.column_name = pk.column_name)
326
+ where
327
+ col.table_schema = $1 AND quote_ident(col.table_name) = $2
328
+ order by
329
+ col.ordinal_position`,
330
+ { binds },
331
+ );
332
+
333
+ return rdh.rows.map((r) => {
334
+ const type_name = EnumValues.getNameFromValue(
335
+ PostgresColumnType,
336
+ PostgresColumnType.parse(r.values.col_type),
337
+ );
338
+ const res = new DbColumn(
339
+ r.values.qname,
340
+ GeneralColumnType.parse(type_name),
341
+ {
342
+ nullable: r.values.nullable === 1,
343
+ key: r.values.col_key,
344
+ default: r.values.col_default,
345
+ extra: r.values.col_extra,
346
+ },
347
+ r.values.comment,
348
+ );
349
+ return res;
350
+ });
351
+ }
352
+
353
+ async closeSub(): Promise<string> {
354
+ try {
355
+ if (this.pool) {
356
+ await this.pool.end();
357
+ }
358
+ return '';
359
+ } catch (e) {
360
+ return e.message;
361
+ }
362
+ }
363
+ }
@@ -0,0 +1,277 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import BaseDriver, { RequestSqlOptions } from './BaseDriver';
3
+ import {
4
+ DbConnection,
5
+ DbResource,
6
+ RedisDatabase,
7
+ DbKey,
8
+ SchemaAndTableHints,
9
+ TableRows,
10
+ } from '../resource/DbResource';
11
+ import { Redis } from 'ioredis';
12
+ import ResultSetDataHolder from '../resource/ResultSetDataHolder';
13
+ import { RedisKeyType } from '../resource/types/RedisKeyType';
14
+ import {
15
+ RedisCommandType,
16
+ RedisRequest,
17
+ } from '../../service/request/redis_request';
18
+
19
+ export default class RedisDriver extends BaseDriver {
20
+ client: Redis | undefined;
21
+ databases = 16;
22
+
23
+ constructor(conRes: DbConnection) {
24
+ super(conRes);
25
+ }
26
+
27
+ async connectSub(): Promise<string> {
28
+ try {
29
+ const options: any = Object.assign(
30
+ {
31
+ port: 6379, // Redis port
32
+ host: '127.0.0.1', // Redis host
33
+ password: 'auth',
34
+ db: 0,
35
+ },
36
+ {
37
+ port: this.conRes.port,
38
+ host: this.conRes.host,
39
+ password: this.conRes.password,
40
+ db: this.conRes.database,
41
+ retryStrategy: function () {
42
+ return 'No!';
43
+ },
44
+ },
45
+ );
46
+ if (this.isNeedsSsh()) {
47
+ options.host = '127.0.0.1';
48
+ options.port = this.sshLocalPort;
49
+ }
50
+ options.connectTimeout = 5_000;
51
+ if (this.conRes.hasUrl()) {
52
+ // Connect to 127.0.0.1:6380, db 4, using password "authpassword":
53
+ // "redis://:authpassword@127.0.0.1:6380/4"
54
+ this.client = new Redis(this.conRes.url);
55
+ } else {
56
+ this.client = new Redis(options);
57
+ }
58
+ // dbs= [ [ null, '# Serverxxxx' ], [ null, [ 'databases', '16' ] ] ]
59
+ try {
60
+ // ReplyError: EXECABORT Transaction discarded because of previous errors.
61
+ const dbs = await this.client
62
+ .multi()
63
+ .info()
64
+ .config('GET', 'databases')
65
+ .exec();
66
+ if (dbs && dbs.length > 1 && dbs[1].length > 1) {
67
+ const dbs2 = dbs[1][1] as any; // dbs2= [ 'databases', '16' ]
68
+ if (dbs2 && dbs2.length > 1) {
69
+ this.databases = parseInt(dbs2[1], 10);
70
+ }
71
+ }
72
+ } catch (e) {
73
+ this.databases = 16;
74
+ }
75
+ } catch (e) {
76
+ return e.message;
77
+ }
78
+
79
+ return '';
80
+ }
81
+
82
+ async test(with_connect = false): Promise<string> {
83
+ let errorReason = '';
84
+ try {
85
+ if (with_connect) {
86
+ const con_result = await this.asyncConnect();
87
+ if (con_result) {
88
+ return con_result;
89
+ }
90
+ }
91
+ await this.client.ping();
92
+ if (with_connect) {
93
+ await this.asyncClose();
94
+ }
95
+ } catch (e) {
96
+ errorReason = e.message;
97
+ }
98
+ return errorReason;
99
+ }
100
+
101
+ async requestSql(
102
+ sql: string,
103
+ options?: RequestSqlOptions,
104
+ ): Promise<ResultSetDataHolder> {
105
+ return ResultSetDataHolder.createEmpty();
106
+ }
107
+ async countTables(
108
+ tables: SchemaAndTableHints,
109
+ options: any,
110
+ ): Promise<TableRows[]> {
111
+ return new Array<TableRows>();
112
+ }
113
+
114
+ async executeCommand(req: RedisRequest): Promise<DbKey | string | number> {
115
+ let ret: DbKey | string | number = '';
116
+ if (!this.client) {
117
+ return ret;
118
+ }
119
+ await this.client.select(req.index);
120
+ let r: any = '';
121
+ switch (req.command) {
122
+ case RedisCommandType.GetValue:
123
+ {
124
+ r = await this.getValueByKey(this.client, req.key, req.type);
125
+ const ttl = await this.client.ttl(req.key);
126
+ ret = new DbKey(req.key, req.type, ttl);
127
+ if (ret.ttl > 0) {
128
+ ret.ttl_confirmation_datetime = new Date().getTime();
129
+ }
130
+ ret.val = r;
131
+ }
132
+ break;
133
+ case RedisCommandType.SetValue:
134
+ if (req.options) {
135
+ await this.client.set(req.key, req.options.val);
136
+ }
137
+ break;
138
+ case RedisCommandType.Flushall:
139
+ ret = await this.client.flushall();
140
+ break;
141
+ case RedisCommandType.Flushdb:
142
+ ret = await this.client.flushdb();
143
+ break;
144
+ case RedisCommandType.Dbsize:
145
+ ret = await this.client.dbsize();
146
+ break;
147
+ case RedisCommandType.Info:
148
+ ret = await this.client.info(<string>req.options.section);
149
+ break;
150
+ default:
151
+ console.error('undefined.', req.command);
152
+ break;
153
+ }
154
+ return ret;
155
+ }
156
+
157
+ scan(
158
+ db_index: number,
159
+ key: string,
160
+ count: number,
161
+ with_value: boolean,
162
+ progressCallback: Function,
163
+ ): Promise<Array<DbKey>> {
164
+ return new Promise((resolve) => {
165
+ const currentClient = this.client;
166
+ this.client.select(db_index);
167
+ const stream = this.client.scanStream({
168
+ match: key,
169
+ count,
170
+ });
171
+ const ret = new Array<DbKey>();
172
+ stream.on('data', (resultKeys: string[]) => {
173
+ resultKeys.forEach((key) => {
174
+ ret.push(new DbKey(key));
175
+ });
176
+ progressCallback(
177
+ 'Key scanning now.',
178
+ Math.round((ret.length * 100) / count),
179
+ );
180
+ if (ret.length > count) {
181
+ (<any>stream).close(); // ScanStream.close()
182
+ }
183
+ });
184
+ stream.on('end', () => {
185
+ progressCallback('Key scanning done. get type and ttl.', 80);
186
+ if (ret.length > count) {
187
+ ret.splice(count - 1, ret.length - count);
188
+ }
189
+ const parallels: any[] = [];
190
+ ret.forEach((keyRes) => {
191
+ parallels.push(
192
+ (async (): Promise<void> => {
193
+ keyRes.type = RedisKeyType.parse(
194
+ await currentClient.type(keyRes.getName()),
195
+ );
196
+ keyRes.ttl = await currentClient.ttl(keyRes.getName());
197
+ if (keyRes.ttl > 0) {
198
+ keyRes.ttl_confirmation_datetime = new Date().getTime();
199
+ }
200
+ if (with_value) {
201
+ keyRes.val = await this.getValueByKey(
202
+ currentClient,
203
+ keyRes.getName(),
204
+ keyRes.type,
205
+ );
206
+ }
207
+ })(),
208
+ );
209
+ });
210
+ (async (): Promise<void> => {
211
+ await Promise.all(parallels);
212
+ progressCallback('Key scanning done.', 100);
213
+ resolve(ret);
214
+ })();
215
+ });
216
+ });
217
+ }
218
+ async getValueByKey(
219
+ client: Redis,
220
+ key: string,
221
+ type: RedisKeyType,
222
+ ): Promise<any> {
223
+ switch (type) {
224
+ case RedisKeyType.string:
225
+ return await client.get(key);
226
+ case RedisKeyType.list:
227
+ return await client.lrange(key, 0, -1);
228
+ case RedisKeyType.set:
229
+ return await client.smembers(key);
230
+ case RedisKeyType.zset:
231
+ return await client.zrange(key, 0, -1);
232
+ case RedisKeyType.hash:
233
+ return await client.hgetall(key);
234
+ default:
235
+ console.log('whattype??', type);
236
+ }
237
+ return undefined;
238
+ }
239
+ async getResouces(options: {
240
+ progress_callback?: Function | undefined;
241
+ params?: any;
242
+ }): Promise<Array<DbResource>> {
243
+ if (!this.conRes) {
244
+ return [];
245
+ }
246
+ const dbResources = new Array<DbResource>();
247
+
248
+ const keyspace = await this.client.info('keyspace');
249
+ // db0:keys=7,expires=0,avg_ttl=0
250
+ // db3:keys=1,expires=1,avg_ttl=4996199
251
+ const re = /db([0-9]+):keys=([0-9]+),expires=([0-9]+),avg_ttl=([0-9]+)/g;
252
+ let m: string[];
253
+ while ((m = re.exec(keyspace))) {
254
+ const db = m[1];
255
+ const keys = parseInt(m[2], 10);
256
+ const expires = parseInt(m[3], 10);
257
+ const avg_ttl = parseInt(m[4], 10);
258
+ const dbRes = new RedisDatabase(db, keys, expires, avg_ttl);
259
+ dbResources.push(dbRes);
260
+ }
261
+ for (let i = 0; i < this.databases; i++) {
262
+ const name = `${i}`;
263
+ if (!dbResources.some((r) => r.getName() === name)) {
264
+ const dbRes = new RedisDatabase(name, 0, 0, 0);
265
+ dbResources.push(dbRes);
266
+ }
267
+ }
268
+
269
+ return dbResources;
270
+ }
271
+ async closeSub(): Promise<string> {
272
+ if (this.client) {
273
+ await this.client.quit();
274
+ }
275
+ return '';
276
+ }
277
+ }