pogi 2.11.0 → 3.0.0-beta3
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/.env +5 -0
- package/.vscode/launch.json +47 -15
- package/jest.config.js +23 -0
- package/lib/bin/generateInterface.js +3 -3
- package/lib/bin/generateInterface.js.map +1 -1
- package/lib/connectionOptions.d.ts +7 -0
- package/lib/index.d.ts +1 -1
- package/lib/pgConverters.d.ts +9 -10
- package/lib/pgConverters.js +44 -39
- package/lib/pgConverters.js.map +1 -1
- package/lib/pgConverters.test.d.ts +1 -0
- package/lib/pgConverters.test.js +13 -0
- package/lib/pgConverters.test.js.map +1 -0
- package/lib/pgDb.d.ts +23 -31
- package/lib/pgDb.js +122 -103
- package/lib/pgDb.js.map +1 -1
- package/lib/pgDb.test.d.ts +1 -0
- package/lib/pgDb.test.js +1126 -0
- package/lib/pgDb.test.js.map +1 -0
- package/lib/pgDbInterface.d.ts +22 -0
- package/lib/pgDbInterface.js +11 -0
- package/lib/pgDbInterface.js.map +1 -0
- package/lib/pgDbOperators.d.ts +3 -3
- package/lib/pgDbOperators.js +4 -7
- package/lib/pgDbOperators.js.map +1 -1
- package/lib/pgDbOperators.test.d.ts +1 -0
- package/lib/pgDbOperators.test.js +313 -0
- package/lib/pgDbOperators.test.js.map +1 -0
- package/lib/pgSchema.d.ts +2 -3
- package/lib/pgSchema.js.map +1 -1
- package/lib/pgSchemaInterface.d.ts +0 -0
- package/lib/pgSchemaInterface.js +2 -0
- package/lib/pgSchemaInterface.js.map +1 -0
- package/lib/pgTable.d.ts +13 -38
- package/lib/pgTable.js +54 -54
- package/lib/pgTable.js.map +1 -1
- package/lib/pgTableInterface.d.ts +28 -0
- package/lib/pgTableInterface.js +4 -0
- package/lib/pgTableInterface.js.map +1 -0
- package/lib/pgUtils.d.ts +16 -6
- package/lib/pgUtils.js +162 -31
- package/lib/pgUtils.js.map +1 -1
- package/lib/queryAble.d.ts +16 -53
- package/lib/queryAble.js +58 -50
- package/lib/queryAble.js.map +1 -1
- package/lib/queryAbleInterface.d.ts +55 -0
- package/lib/queryAbleInterface.js +7 -0
- package/lib/queryAbleInterface.js.map +1 -0
- package/lib/queryWhere.d.ts +2 -2
- package/lib/queryWhere.js +19 -23
- package/lib/queryWhere.js.map +1 -1
- package/lib/test/pgDbOperatorSpec.d.ts +1 -0
- package/lib/test/pgDbOperatorSpec.js +326 -0
- package/lib/test/pgDbOperatorSpec.js.map +1 -0
- package/lib/test/pgDbSpec.d.ts +1 -0
- package/lib/test/pgDbSpec.js +1139 -0
- package/lib/test/pgDbSpec.js.map +1 -0
- package/lib/test/pgServiceRestartTest.d.ts +1 -0
- package/lib/test/pgServiceRestartTest.js +1532 -0
- package/lib/test/pgServiceRestartTest.js.map +1 -0
- package/package.json +21 -14
- package/{src/tsconfig.json → tsconfig.json} +11 -11
- package/spec/resources/init.sql +0 -122
- package/spec/resources/throw_exception.sql +0 -5
- package/spec/resources/tricky.sql +0 -13
- package/spec/run.js +0 -5
- package/spec/support/jasmine.json +0 -9
- package/src/bin/generateInterface.ts +0 -54
- package/src/connectionOptions.ts +0 -42
- package/src/index.ts +0 -6
- package/src/pgConverters.ts +0 -55
- package/src/pgDb.ts +0 -820
- package/src/pgDbLogger.ts +0 -13
- package/src/pgDbOperators.ts +0 -62
- package/src/pgSchema.ts +0 -15
- package/src/pgTable.ts +0 -401
- package/src/pgUtils.ts +0 -176
- package/src/queryAble.ts +0 -393
- package/src/queryWhere.ts +0 -326
- package/src/test/pgDbOperatorSpec.ts +0 -492
- package/src/test/pgDbSpec.ts +0 -1339
- package/src/test/pgServiceRestartTest.ts +0 -1500
package/src/pgDb.ts
DELETED
|
@@ -1,820 +0,0 @@
|
|
|
1
|
-
import { QueryAble, ResultFieldType } from "./queryAble";
|
|
2
|
-
import { PgTable } from "./pgTable";
|
|
3
|
-
import { PgSchema } from "./pgSchema";
|
|
4
|
-
import * as PgConverters from "./pgConverters";
|
|
5
|
-
import { pgUtils } from "./pgUtils";
|
|
6
|
-
import * as _ from 'lodash';
|
|
7
|
-
import * as pg from 'pg';
|
|
8
|
-
import * as readline from 'readline';
|
|
9
|
-
import * as fs from 'fs';
|
|
10
|
-
import { PgDbLogger } from './pgDbLogger';
|
|
11
|
-
import { ConnectionOptions } from './connectionOptions';
|
|
12
|
-
import * as EventEmitter from 'events';
|
|
13
|
-
|
|
14
|
-
const CONNECTION_URL_REGEXP = /^postgres:\/\/(?:([^:]+)(?::([^@]*))?@)?([^\/:]+)?(?::([^\/]+))?\/(.*)$/;
|
|
15
|
-
const SQL_TOKENIZER_REGEXP = /''|'|""|"|;|\$|--|\/\*|\*\/|(.+?)/g;
|
|
16
|
-
const SQL_$_ESCAPE_REGEXP = /\$[^$]*\$/g;
|
|
17
|
-
|
|
18
|
-
/** looks like we only get back those that we have access to */
|
|
19
|
-
const LIST_SCHEMAS_TABLES =
|
|
20
|
-
`SELECT table_schema as schema, table_name as name
|
|
21
|
-
FROM information_schema.tables
|
|
22
|
-
WHERE table_schema NOT IN ('pg_catalog', 'pg_constraint', 'information_schema')`;
|
|
23
|
-
const GET_OID_FOR_COLUMN_TYPE_FOR_SCHEMA = "SELECT t.oid FROM pg_catalog.pg_type t, pg_namespace n WHERE typname=:typeName and n.oid=t.typnamespace and n.nspname=:schemaName;";
|
|
24
|
-
const GET_OID_FOR_COLUMN_TYPE = "SELECT t.oid FROM pg_catalog.pg_type t WHERE typname=:typeName";
|
|
25
|
-
|
|
26
|
-
/** looks like we only get back those that we have access to */
|
|
27
|
-
const GET_SCHEMAS_PROCEDURES = `SELECT
|
|
28
|
-
n.nspname as "schema",
|
|
29
|
-
p.proname as "name",
|
|
30
|
-
(not p.proretset) as "return_single_row",
|
|
31
|
-
(t.typtype in ('b', 'd', 'e', 'r')) as "return_single_value"
|
|
32
|
-
FROM pg_proc p
|
|
33
|
-
inner join pg_namespace n on (p.pronamespace = n.oid)
|
|
34
|
-
inner join pg_type t on (p.prorettype = t.oid)
|
|
35
|
-
left outer join pg_trigger tr on (tr.tgfoid = p.oid)
|
|
36
|
-
WHERE n.nspname NOT IN ('pg_catalog', 'pg_constraint', 'information_schema')
|
|
37
|
-
AND tr.oid is null;`;
|
|
38
|
-
const GET_CURRENT_SCHEMAS = "SELECT current_schemas(false)";
|
|
39
|
-
//const LIST_ARRAY_TYPE_FIELDS = 'SELECT a.atttypid as oid FROM pg_attribute a WHERE a.attndims>0 AND a.atttypid>200000';
|
|
40
|
-
//const LIST_ARRAY_TYPE_FIELDS = 'SELECT a.atttypid as type_oid FROM pg_attribute a WHERE a.tttypid in (1005,1007,1016,1021,1022) OR (a.attndims>0 AND a.atttypid>200000)';
|
|
41
|
-
|
|
42
|
-
/*
|
|
43
|
-
SELECT c.nspname as schema_name, b.relname as table_name, a.attname as column_name, a.atttypid as type_oid, format_type(a.atttypid, a.atttypmod)
|
|
44
|
-
FROM pg_attribute a
|
|
45
|
-
JOIN pg_class b ON (a.attrelid = b.relfilenode)
|
|
46
|
-
JOIN pg_namespace c ON (b.relnamespace=c.oid)
|
|
47
|
-
WHERE a.attndims>0 AND a.atttypid>200000;
|
|
48
|
-
*/
|
|
49
|
-
|
|
50
|
-
/*
|
|
51
|
-
SELECT * FROM pg_catalog.pg_type t where t.typname like '%tz';
|
|
52
|
-
SELECT t.oid FROM pg_catalog.pg_type t WHERE t.typname in ('timestamptz', 'timetz');
|
|
53
|
-
|
|
54
|
-
reltype -> only include the table columns (this is zero for indexes)
|
|
55
|
-
a.attndims>0 -> not reliable (truncate/create table like.. not set it correctly)
|
|
56
|
-
*/
|
|
57
|
-
/** We get back fields as well that we don't have access to, thus we need to filter those schemas that we have permission for
|
|
58
|
-
* ... TODO check it for tables */
|
|
59
|
-
const LIST_SPECIAL_TYPE_FIELDS =
|
|
60
|
-
`SELECT c.nspname as schema_name, b.relname as table_name, a.attname as column_name, a.atttypid as typid
|
|
61
|
-
FROM pg_attribute a
|
|
62
|
-
JOIN pg_class b ON (a.attrelid = b.oid)
|
|
63
|
-
JOIN pg_type t ON (a.atttypid = t.oid)
|
|
64
|
-
JOIN pg_namespace c ON (b.relnamespace=c.oid)
|
|
65
|
-
WHERE (a.atttypid in (114, 3802, 1082, 1083, 1114, 1184, 1266, 3614) or t.typcategory='A')
|
|
66
|
-
AND reltype>0 `;
|
|
67
|
-
//AND c.nspname not in ('pg_catalog', 'pg_constraint', 'information_schema')
|
|
68
|
-
|
|
69
|
-
export enum FieldType { JSON, ARRAY, TIME, TSVECTOR }
|
|
70
|
-
|
|
71
|
-
export enum TranzactionIsolationLevel {
|
|
72
|
-
serializable = 'SERIALIZABLE',
|
|
73
|
-
repeatableRead = 'REPEATABLE READ',
|
|
74
|
-
readCommitted = 'READ COMMITTED',
|
|
75
|
-
readUncommitted = 'READ UNCOMMITTED'
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export type PostProcessResultFunc = (res: any[], fields: ResultFieldType[], logger: PgDbLogger) => void;
|
|
79
|
-
|
|
80
|
-
/** LISTEN callback parameter */
|
|
81
|
-
export interface Notification {
|
|
82
|
-
processId: number,
|
|
83
|
-
channel: string,
|
|
84
|
-
payload?: string
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export class PgDb extends QueryAble {
|
|
88
|
-
protected static instances: { [index: string]: Promise<PgDb> };
|
|
89
|
-
/*protected*/
|
|
90
|
-
pool;
|
|
91
|
-
connection;
|
|
92
|
-
|
|
93
|
-
/*protected*/
|
|
94
|
-
config: ConnectionOptions;
|
|
95
|
-
/*protected*/
|
|
96
|
-
defaultSchemas; // for this.tables and this.fn
|
|
97
|
-
|
|
98
|
-
db;
|
|
99
|
-
schemas: { [name: string]: PgSchema };
|
|
100
|
-
tables: { [name: string]: PgTable<any> } = {};
|
|
101
|
-
fn: { [name: string]: (...any) => any } = {};
|
|
102
|
-
[name: string]: any | PgSchema;
|
|
103
|
-
/* protected */
|
|
104
|
-
pgdbTypeParsers = {};
|
|
105
|
-
/* protected */
|
|
106
|
-
knownOids: Record<number, boolean> = {};
|
|
107
|
-
/* protected */
|
|
108
|
-
postProcessResult: PostProcessResultFunc;
|
|
109
|
-
|
|
110
|
-
private constructor(pgdb: { defaultSchemas?, config?, schemas?, pool?, pgdbTypeParsers?, knownOids?, getLogger?: () => any, postProcessResult?: PostProcessResultFunc } = {}) {
|
|
111
|
-
super();
|
|
112
|
-
this.schemas = {};
|
|
113
|
-
this.config = pgdb.config;
|
|
114
|
-
this.pool = pgdb.pool;
|
|
115
|
-
this.postProcessResult = pgdb.postProcessResult;
|
|
116
|
-
this.pgdbTypeParsers = pgdb.pgdbTypeParsers || {};
|
|
117
|
-
this.knownOids = pgdb.knownOids || {};
|
|
118
|
-
this.db = this;
|
|
119
|
-
if (pgdb.getLogger) {
|
|
120
|
-
this.setLogger(pgdb.getLogger());
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
for (let schemaName in pgdb.schemas) {
|
|
124
|
-
let schema = new PgSchema(this, schemaName);
|
|
125
|
-
this.schemas[schemaName] = schema;
|
|
126
|
-
if (!(schemaName in this))
|
|
127
|
-
this[schemaName] = schema;
|
|
128
|
-
for (let tableName in pgdb.schemas[schemaName].tables) {
|
|
129
|
-
schema.tables[tableName] = new PgTable(schema, pgdb.schemas[schemaName][tableName].desc, pgdb.schemas[schemaName][tableName].fieldTypes);
|
|
130
|
-
if (!(tableName in schema))
|
|
131
|
-
schema[tableName] = schema.tables[tableName];
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
this.defaultSchemas = pgdb.defaultSchemas;
|
|
136
|
-
this.setDefaultTablesAndFunctions();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
setPostProcessResult(f: (res: any[], fields: ResultFieldType[], logger: PgDbLogger) => void) {
|
|
140
|
-
this.postProcessResult = f;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/** If planned to used as a static singleton */
|
|
144
|
-
static async getInstance(config: ConnectionOptions): Promise<PgDb> {
|
|
145
|
-
if (config.connectionString) {
|
|
146
|
-
let res = CONNECTION_URL_REGEXP.exec(config.connectionString);
|
|
147
|
-
if (res) {
|
|
148
|
-
config.user = res[1];
|
|
149
|
-
config.password = res[2] ? res[2] : '';
|
|
150
|
-
config.host = res[3] ? res[3] : 'localhost';
|
|
151
|
-
config.port = res[4] ? +res[4] : 5432;
|
|
152
|
-
config.database = res[5];
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
let connectionString = `postgres://${config.user}@${config.host}:${config.port}/${config.database}`; // without password!
|
|
156
|
-
if (!PgDb.instances) {
|
|
157
|
-
PgDb.instances = {};
|
|
158
|
-
}
|
|
159
|
-
if (PgDb.instances[connectionString]) {
|
|
160
|
-
return PgDb.instances[connectionString];
|
|
161
|
-
} else {
|
|
162
|
-
let pgdb = new PgDb({ config: config });
|
|
163
|
-
PgDb.instances[connectionString] = pgdb.init();
|
|
164
|
-
return PgDb.instances[connectionString];
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
async close() {
|
|
169
|
-
for (let cs in PgDb.instances) {
|
|
170
|
-
let db = await PgDb.instances[cs];
|
|
171
|
-
if (db.pool == this.pool) {
|
|
172
|
-
delete PgDb.instances[cs];
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
await this.pool.end((err: Error) => { });
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
static async connect(config: ConnectionOptions): Promise<PgDb> {
|
|
179
|
-
if (config.connectionString) {
|
|
180
|
-
let res = CONNECTION_URL_REGEXP.exec(config.connectionString);
|
|
181
|
-
if (res) {
|
|
182
|
-
config.user = res[1];
|
|
183
|
-
if (res[2]) config.password = res[2];
|
|
184
|
-
config.host = res[3] ? res[3] : 'localhost';
|
|
185
|
-
config.port = res[4] ? +res[4] : 5432;
|
|
186
|
-
config.database = res[5];
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
let pgdb = new PgDb({ config: config });
|
|
190
|
-
return pgdb.init();
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
private async init(): Promise<PgDb> {
|
|
194
|
-
this.pool = new pg.Pool(_.omit(this.config, ['logger', 'skipUndefined']));
|
|
195
|
-
if (this.config.logger)
|
|
196
|
-
this.setLogger(this.config.logger);
|
|
197
|
-
|
|
198
|
-
this.pool.on('error', (e, client) => {
|
|
199
|
-
// if a client is idle in the pool
|
|
200
|
-
// and receives an error - for example when your PostgreSQL server restarts
|
|
201
|
-
// the pool will catch the error & let you handle it here
|
|
202
|
-
this.getLogger(true).error('pool error', e);
|
|
203
|
-
});
|
|
204
|
-
await this.reload();
|
|
205
|
-
this.getLogger().log('Successfully connected to Db');
|
|
206
|
-
return this;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
async reload() {
|
|
210
|
-
await this.initSchemasAndTables();
|
|
211
|
-
await this.initFieldTypes();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
private async initSchemasAndTables() {
|
|
215
|
-
let schemasAndTables = await this.query(LIST_SCHEMAS_TABLES);
|
|
216
|
-
let functions = await this.query(GET_SCHEMAS_PROCEDURES);
|
|
217
|
-
|
|
218
|
-
this.defaultSchemas = await this.queryOneField(GET_CURRENT_SCHEMAS);
|
|
219
|
-
|
|
220
|
-
let oldSchemaNames = Object.keys(this.schemas);
|
|
221
|
-
for (let sc of oldSchemaNames) {
|
|
222
|
-
if (this[sc] === this.schemas[sc])
|
|
223
|
-
delete this[sc];
|
|
224
|
-
}
|
|
225
|
-
this.schemas = {};
|
|
226
|
-
for (let r of schemasAndTables) {
|
|
227
|
-
let schema = this.schemas[r.schema] = this.schemas[r.schema] || new PgSchema(this, r.schema);
|
|
228
|
-
if (!(r.schema in this))
|
|
229
|
-
this[r.schema] = schema;
|
|
230
|
-
schema.tables[r.name] = new PgTable(schema, r);
|
|
231
|
-
if (!(r.name in schema))
|
|
232
|
-
schema[r.name] = schema.tables[r.name];
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
for (let r of functions) {
|
|
236
|
-
let schema = this.schemas[r.schema] = this.schemas[r.schema] || new PgSchema(this, r.schema);
|
|
237
|
-
if (!(r.schema in this))
|
|
238
|
-
this[r.schema] = schema;
|
|
239
|
-
schema.fn[r.name] = pgUtils.createFunctionCaller(schema, r);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// this.getLogger(true).log('defaultSchemas: ' + defaultSchemas);
|
|
243
|
-
this.setDefaultTablesAndFunctions();
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
private setDefaultTablesAndFunctions() {
|
|
247
|
-
this.tables = {};
|
|
248
|
-
this.fn = {};
|
|
249
|
-
|
|
250
|
-
if (!this.defaultSchemas) return;
|
|
251
|
-
for (let sc of this.defaultSchemas) {
|
|
252
|
-
let schema = this.schemas[sc];
|
|
253
|
-
// this.getLogger(true).log('copy schame to default', sc, schema && Object.keys(schema.tables), schema && Object.keys(schema.fn));
|
|
254
|
-
if (!schema)
|
|
255
|
-
continue;
|
|
256
|
-
for (let table in schema.tables)
|
|
257
|
-
this.tables[table] = this.tables[table] || schema.tables[table];
|
|
258
|
-
for (let fn in schema.fn)
|
|
259
|
-
this.fn[fn] = this.fn[fn] || schema.fn[fn];
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
private async initFieldTypes() {
|
|
264
|
-
//--- init field types -------------------------------------------
|
|
265
|
-
let schemaNames = "'" + Object.keys(this.schemas).join("', '") + "'";
|
|
266
|
-
if (schemaNames == "''") {
|
|
267
|
-
this.getLogger(true).error("No readable schema found!");
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
let specialTypeFields: { schema_name: string, table_name: string, column_name: string, typid: number }[]
|
|
271
|
-
= await this.query(LIST_SPECIAL_TYPE_FIELDS + ' AND c.nspname in (' + schemaNames + ')');
|
|
272
|
-
|
|
273
|
-
for (let r of specialTypeFields) {
|
|
274
|
-
if (this.schemas[r.schema_name][r.table_name]) {
|
|
275
|
-
this.schemas[r.schema_name][r.table_name].fieldTypes[r.column_name] =
|
|
276
|
-
([3802, 114].indexOf(r.typid) > -1) ? FieldType.JSON :
|
|
277
|
-
([3614].indexOf(r.typid) > -1) ? FieldType.TSVECTOR :
|
|
278
|
-
([1082, 1083, 1114, 1184, 1266].indexOf(r.typid) > -1) ? FieldType.TIME :
|
|
279
|
-
FieldType.ARRAY;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// https://web.archive.org/web/20160613215445/https://doxygen.postgresql.org/include_2catalog_2pg__type_8h_source.html
|
|
284
|
-
// https://github.com/lib/pq/blob/master/oid/types.go
|
|
285
|
-
|
|
286
|
-
let builtInArrayTypeParsers: { oidList: number[], parser: (string) => any }[] = [
|
|
287
|
-
{
|
|
288
|
-
oidList: [
|
|
289
|
-
1000 // bool[]
|
|
290
|
-
],
|
|
291
|
-
parser: PgConverters.arraySplitToBool
|
|
292
|
-
},
|
|
293
|
-
{
|
|
294
|
-
oidList: [
|
|
295
|
-
1005, // smallInt[] int2[]
|
|
296
|
-
1007, // integer[] int4[]
|
|
297
|
-
1021 // real[] float4[]
|
|
298
|
-
],
|
|
299
|
-
parser: PgConverters.arraySplitToNum
|
|
300
|
-
},
|
|
301
|
-
{
|
|
302
|
-
oidList: [
|
|
303
|
-
1009, // text[]
|
|
304
|
-
1015 // varchar[]
|
|
305
|
-
],
|
|
306
|
-
parser: PgConverters.arraySplit
|
|
307
|
-
},
|
|
308
|
-
{
|
|
309
|
-
oidList: [
|
|
310
|
-
199, // json[]
|
|
311
|
-
3807 // jsonb[]
|
|
312
|
-
],
|
|
313
|
-
parser: PgConverters.arraySplitToJson
|
|
314
|
-
},
|
|
315
|
-
{
|
|
316
|
-
oidList: [
|
|
317
|
-
1115, // timestamp[]
|
|
318
|
-
1182, // date[]
|
|
319
|
-
1183, // time[]
|
|
320
|
-
1185, // timestamptz[]
|
|
321
|
-
1270 // timetz[]
|
|
322
|
-
],
|
|
323
|
-
parser: PgConverters.arraySplitToDate
|
|
324
|
-
}
|
|
325
|
-
];
|
|
326
|
-
|
|
327
|
-
builtInArrayTypeParsers.forEach(parserObj => {
|
|
328
|
-
parserObj.oidList.forEach(oid => {
|
|
329
|
-
pg.types.setTypeParser(oid, parserObj.parser);
|
|
330
|
-
delete this.pgdbTypeParsers[oid];
|
|
331
|
-
this.knownOids[oid] = true;
|
|
332
|
-
});
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
for (let r of specialTypeFields) {
|
|
336
|
-
if (this.knownOids[r.typid] && !this.pgdbTypeParsers[r.typid]) {
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
switch (r.typid) {
|
|
340
|
-
case 114: // json
|
|
341
|
-
case 3802: // jsonb
|
|
342
|
-
case 1082: // date
|
|
343
|
-
case 1083: // time
|
|
344
|
-
case 1114: // timestamp
|
|
345
|
-
case 1184: // timestamptz
|
|
346
|
-
case 1266: // timetz
|
|
347
|
-
case 3614: // tsvector
|
|
348
|
-
break;
|
|
349
|
-
case 1016: // bigInt[] int8[]
|
|
350
|
-
case 1022: // double[] float8[]
|
|
351
|
-
//pg.types.setTypeParser(r.typid, arraySplitToNumWithValidation);
|
|
352
|
-
//delete this.pgdbTypeParsers[r.typid];
|
|
353
|
-
break;
|
|
354
|
-
default:
|
|
355
|
-
//best guess otherwise user need to specify
|
|
356
|
-
pg.types.setTypeParser(r.typid, PgConverters.arraySplit);
|
|
357
|
-
delete this.pgdbTypeParsers[r.typid];
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
//has to set outside of pgjs as it doesnt support exceptions (stop everything immediately)
|
|
362
|
-
await this.setPgDbTypeParser('int8', PgConverters.numWithValidation); //int8 - 20
|
|
363
|
-
await this.setPgDbTypeParser('float8', PgConverters.numWithValidation); //float8 - 701
|
|
364
|
-
await this.setPgDbTypeParser('_int8', PgConverters.stringArrayToNumWithValidation);
|
|
365
|
-
await this.setPgDbTypeParser('_float8', PgConverters.stringArrayToNumWithValidation);
|
|
366
|
-
|
|
367
|
-
let allUsedTypeFields = await this.queryOneColumn(`
|
|
368
|
-
SELECT a.atttypid as typid
|
|
369
|
-
FROM pg_attribute a
|
|
370
|
-
JOIN pg_class b ON (a.attrelid = b.oid)
|
|
371
|
-
JOIN pg_type t ON (a.atttypid = t.oid)
|
|
372
|
-
JOIN pg_namespace c ON (b.relnamespace=c.oid)
|
|
373
|
-
WHERE
|
|
374
|
-
reltype>0 AND
|
|
375
|
-
c.nspname in (${schemaNames})`
|
|
376
|
-
);
|
|
377
|
-
allUsedTypeFields.forEach(oid => this.knownOids[oid] = true);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* if schemaName is null, it will be applied for all schemas
|
|
382
|
-
*/
|
|
383
|
-
async setTypeParser(typeName: string, parser: (string) => any, schemaName?: string): Promise<void> {
|
|
384
|
-
try {
|
|
385
|
-
if (schemaName) {
|
|
386
|
-
let oid = await this.queryOneField(GET_OID_FOR_COLUMN_TYPE_FOR_SCHEMA, { typeName, schemaName });
|
|
387
|
-
pg.types.setTypeParser(oid, parser);
|
|
388
|
-
delete this.pgdbTypeParsers[oid];
|
|
389
|
-
this.knownOids[oid] = true;
|
|
390
|
-
} else {
|
|
391
|
-
let list = await this.queryOneColumn(GET_OID_FOR_COLUMN_TYPE, { typeName });
|
|
392
|
-
list.forEach(oid => {
|
|
393
|
-
pg.types.setTypeParser(oid, parser);
|
|
394
|
-
delete this.pgdbTypeParsers[oid];
|
|
395
|
-
this.knownOids[oid] = true;
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
} catch (e) {
|
|
399
|
-
throw Error('Not existing type: ' + typeName);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
async setPgDbTypeParser(typeName: string, parser: (string) => any, schemaName?: string): Promise<void> {
|
|
404
|
-
try {
|
|
405
|
-
if (schemaName) {
|
|
406
|
-
let oid = await this.queryOneField(GET_OID_FOR_COLUMN_TYPE_FOR_SCHEMA, { typeName, schemaName });
|
|
407
|
-
this.pgdbTypeParsers[oid] = parser;
|
|
408
|
-
this.knownOids[oid] = true;
|
|
409
|
-
} else {
|
|
410
|
-
let list = await this.queryOneColumn(GET_OID_FOR_COLUMN_TYPE, { typeName });
|
|
411
|
-
list.forEach(oid => {
|
|
412
|
-
this.pgdbTypeParsers[oid] = parser;
|
|
413
|
-
this.knownOids[oid] = true;
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
} catch (e) {
|
|
417
|
-
throw Error('Not existing type: ' + typeName);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
async resetMissingParsers(connection, oidList: number[]): Promise<void> {
|
|
422
|
-
let unknownOids = oidList.filter(oid => !this.knownOids[oid]);
|
|
423
|
-
if (unknownOids.length) {
|
|
424
|
-
let fieldsData = await connection.query(
|
|
425
|
-
`select oid, typcategory from pg_type where oid = ANY($1)`,
|
|
426
|
-
[unknownOids]
|
|
427
|
-
);
|
|
428
|
-
|
|
429
|
-
fieldsData.rows.forEach(fieldData => {
|
|
430
|
-
if (fieldData.typcategory == 'A') {
|
|
431
|
-
this.pgdbTypeParsers[fieldData.oid] = PgConverters.arraySplit;
|
|
432
|
-
}
|
|
433
|
-
this.knownOids[fieldData.oid] = true;
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
async dedicatedConnectionBegin(): Promise<PgDb> {
|
|
439
|
-
if (this.needToFixConnectionForListen()) {
|
|
440
|
-
await this.runRestartConnectionForListen();
|
|
441
|
-
}
|
|
442
|
-
let pgDb = new PgDb(this);
|
|
443
|
-
pgDb.connection = await this.pool.connect();
|
|
444
|
-
pgDb.connection.on('error', () => { }); //When there is an error, then the actual called query will throw exception
|
|
445
|
-
return pgDb;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
async dedicatedConnectionEnd(): Promise<PgDb> {
|
|
449
|
-
if (this.connection) {
|
|
450
|
-
try {
|
|
451
|
-
await this.connection.release();
|
|
452
|
-
} catch (err) {
|
|
453
|
-
this.getLogger().error('Error while dedicated connection end.', err);
|
|
454
|
-
}
|
|
455
|
-
this.connection = null;
|
|
456
|
-
}
|
|
457
|
-
return this;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* transaction save point
|
|
462
|
-
* https://www.postgresql.org/docs/current/sql-savepoint.html
|
|
463
|
-
*/
|
|
464
|
-
async savePoint(name: string): Promise<PgDb> {
|
|
465
|
-
if (this.isTransactionActive()) {
|
|
466
|
-
name = (name || '').replace(/"/g, '');
|
|
467
|
-
await this.query(`SAVEPOINT "${name}"`);
|
|
468
|
-
} else {
|
|
469
|
-
throw Error('No active transaction');
|
|
470
|
-
}
|
|
471
|
-
return this;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* "RELEASE SAVEPOINT" - remove transaction save point
|
|
476
|
-
* https://www.postgresql.org/docs/current/sql-savepoint.html
|
|
477
|
-
*/
|
|
478
|
-
async savePointRelease(name: string): Promise<PgDb> {
|
|
479
|
-
if (this.isTransactionActive()) {
|
|
480
|
-
name = (name || '').replace(/"/g, '');
|
|
481
|
-
await this.query(`RELEASE SAVEPOINT "${name}"`);
|
|
482
|
-
} else {
|
|
483
|
-
throw Error('No active transaction');
|
|
484
|
-
}
|
|
485
|
-
return this;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
async transactionBegin(options?: { isolationLevel?: TranzactionIsolationLevel, deferrable?: boolean, readOnly?: boolean }): Promise<PgDb> {
|
|
489
|
-
let pgDb = this.connection ? this : await this.dedicatedConnectionBegin();
|
|
490
|
-
let q = 'BEGIN'
|
|
491
|
-
if (options?.isolationLevel) {
|
|
492
|
-
q += ' ISOLATION LEVEL ' + options.isolationLevel;
|
|
493
|
-
}
|
|
494
|
-
if (options?.readOnly) {
|
|
495
|
-
q += ' READ ONLY';
|
|
496
|
-
}
|
|
497
|
-
if (options?.deferrable) {
|
|
498
|
-
q += ' DEFERRABLE ';
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
await pgDb.query(q);
|
|
502
|
-
return pgDb;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
async transactionCommit(): Promise<PgDb> {
|
|
506
|
-
await this.query('COMMIT');
|
|
507
|
-
return this.dedicatedConnectionEnd();
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
async transactionRollback(options?: { savePoint?: string }): Promise<PgDb> {
|
|
511
|
-
if (options?.savePoint) {
|
|
512
|
-
let name = (options.savePoint || '').replace(/"/g, '');
|
|
513
|
-
await this.query(`ROLLBACK TO SAVEPOINT "${name}"`);
|
|
514
|
-
return this;
|
|
515
|
-
} else {
|
|
516
|
-
await this.query('ROLLBACK');
|
|
517
|
-
return this.dedicatedConnectionEnd();
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
isTransactionActive(): boolean {
|
|
522
|
-
return this.connection != null;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
async execute(fileName: string, statementTransformerFunction?: (string) => string): Promise<void> {
|
|
526
|
-
let isTransactionInPlace = this.isTransactionActive();
|
|
527
|
-
// statements must be run in a dedicated connection
|
|
528
|
-
let pgdb = isTransactionInPlace ? this : await this.dedicatedConnectionBegin();
|
|
529
|
-
|
|
530
|
-
/** run statements one after the other */
|
|
531
|
-
let runStatementList = (statementList) => {
|
|
532
|
-
//this.getLogger(true).log('consumer start', commands.length);
|
|
533
|
-
return new Promise((resolve, reject) => {
|
|
534
|
-
let currentStatement = 0;
|
|
535
|
-
let runStatement = () => {
|
|
536
|
-
//this.getLogger(true).log('commnads length', commands.length, i);
|
|
537
|
-
if (statementList.length == currentStatement) {
|
|
538
|
-
resolve(undefined);
|
|
539
|
-
} else {
|
|
540
|
-
let statement = statementList[currentStatement++];
|
|
541
|
-
if (statementTransformerFunction) {
|
|
542
|
-
statement = statementTransformerFunction(statement);
|
|
543
|
-
}
|
|
544
|
-
this.getLogger(true).log('run', statementList[currentStatement - 1]);
|
|
545
|
-
pgdb.query(statement)
|
|
546
|
-
.then(() => runStatement(), reject)
|
|
547
|
-
.catch(reject);
|
|
548
|
-
}
|
|
549
|
-
};
|
|
550
|
-
runStatement();
|
|
551
|
-
}).catch((e) => {
|
|
552
|
-
this.getLogger(true).error(e);
|
|
553
|
-
throw e;
|
|
554
|
-
});
|
|
555
|
-
};
|
|
556
|
-
|
|
557
|
-
let lineCounter = 0;
|
|
558
|
-
let promise = new Promise<void>((resolve, reject) => {
|
|
559
|
-
let statementList = [];
|
|
560
|
-
let tmp = '', t: RegExpExecArray;
|
|
561
|
-
let consumer;
|
|
562
|
-
let inQuotedString: string;
|
|
563
|
-
let rl = readline.createInterface({
|
|
564
|
-
input: fs.createReadStream(fileName),
|
|
565
|
-
terminal: false
|
|
566
|
-
}).on('line', (line) => {
|
|
567
|
-
lineCounter++;
|
|
568
|
-
try {
|
|
569
|
-
//console.log('Line: ' + line);
|
|
570
|
-
while (t = SQL_TOKENIZER_REGEXP.exec(line)) {
|
|
571
|
-
if (!inQuotedString && (t[0] == '"' || t[0] == "'") || inQuotedString == '"' || inQuotedString == "'") {
|
|
572
|
-
if (!inQuotedString) {
|
|
573
|
-
inQuotedString = t[0];
|
|
574
|
-
} else if (inQuotedString == t[0]) {
|
|
575
|
-
inQuotedString = null;
|
|
576
|
-
}
|
|
577
|
-
tmp += t[0];
|
|
578
|
-
} else if (!inQuotedString && t[0] == '$' || inQuotedString && inQuotedString[0] == '$') {
|
|
579
|
-
if (!inQuotedString) {
|
|
580
|
-
let s = line.slice(SQL_TOKENIZER_REGEXP.lastIndex - 1);
|
|
581
|
-
let token = s.match(SQL_$_ESCAPE_REGEXP);
|
|
582
|
-
if (!token) {
|
|
583
|
-
throw Error('Invalid sql in line: ' + line);
|
|
584
|
-
}
|
|
585
|
-
inQuotedString = token[0];
|
|
586
|
-
SQL_TOKENIZER_REGEXP.lastIndex += inQuotedString.length - 1;
|
|
587
|
-
tmp += inQuotedString;
|
|
588
|
-
} else {
|
|
589
|
-
tmp += t[0];
|
|
590
|
-
if (tmp.endsWith(inQuotedString)) {
|
|
591
|
-
inQuotedString = null;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
} else if (!inQuotedString && t[0] == '/*' || inQuotedString == '/*') {
|
|
595
|
-
if (!inQuotedString) {
|
|
596
|
-
inQuotedString = t[0];
|
|
597
|
-
} else if (t[0] == '*/') {
|
|
598
|
-
inQuotedString = null;
|
|
599
|
-
}
|
|
600
|
-
} else if (!inQuotedString && t[0] == '--') {
|
|
601
|
-
line = '';
|
|
602
|
-
} else if (!inQuotedString && t[0] == ';') {
|
|
603
|
-
//console.log('push ' + tmp);
|
|
604
|
-
if (tmp.trim() != '') {
|
|
605
|
-
statementList.push(tmp);
|
|
606
|
-
if (!consumer) {
|
|
607
|
-
consumer = runStatementList(statementList).then(() => {
|
|
608
|
-
// console.log('consumer done');
|
|
609
|
-
consumer = null;
|
|
610
|
-
statementList.length = 0;
|
|
611
|
-
rl.resume();
|
|
612
|
-
}, reject);
|
|
613
|
-
rl.pause();
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
tmp = '';
|
|
617
|
-
} else {
|
|
618
|
-
tmp += t[0];
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
if (tmp && line) {
|
|
622
|
-
tmp += '\n';
|
|
623
|
-
}
|
|
624
|
-
} catch (e) {
|
|
625
|
-
reject(e);
|
|
626
|
-
}
|
|
627
|
-
}).on('close', () => {
|
|
628
|
-
if (inQuotedString) {
|
|
629
|
-
reject(Error('Invalid SQL, unterminated string'));
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
//if the last statement did't have ';'
|
|
633
|
-
if (tmp.trim() != '') {
|
|
634
|
-
statementList.push(tmp);
|
|
635
|
-
}
|
|
636
|
-
if (!consumer) {
|
|
637
|
-
if (statementList.length) {
|
|
638
|
-
consumer = runStatementList(statementList).catch(reject);
|
|
639
|
-
} else {
|
|
640
|
-
resolve();
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
if (consumer) {
|
|
644
|
-
consumer = consumer.then(resolve, reject);
|
|
645
|
-
}
|
|
646
|
-
});
|
|
647
|
-
});
|
|
648
|
-
let error;
|
|
649
|
-
return promise
|
|
650
|
-
.catch((e) => {
|
|
651
|
-
error = e;
|
|
652
|
-
this.getLogger(true).error('Error at line ' + lineCounter + ' in ' + fileName + '. ' + e);
|
|
653
|
-
})
|
|
654
|
-
.then(() => {
|
|
655
|
-
// finally
|
|
656
|
-
//if transaction was in place, don't touch it
|
|
657
|
-
if (!isTransactionInPlace) {
|
|
658
|
-
return pgdb.dedicatedConnectionEnd();
|
|
659
|
-
}
|
|
660
|
-
}).catch((e) => {
|
|
661
|
-
this.getLogger(true).error(e);
|
|
662
|
-
}).then(() => {
|
|
663
|
-
if (error) {
|
|
664
|
-
throw error;
|
|
665
|
-
}
|
|
666
|
-
// console.log('connection released');
|
|
667
|
-
});
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
private listeners = new EventEmitter();
|
|
671
|
-
private connectionForListen;
|
|
672
|
-
private _needToRestartConnectionForListen = false;
|
|
673
|
-
private restartConnectionForListen: Promise<Error> = null;
|
|
674
|
-
|
|
675
|
-
/**
|
|
676
|
-
* LISTEN to a channel for a NOTIFY (https://www.postgresql.org/docs/current/sql-listen.html)
|
|
677
|
-
* One connection will be dedicated for listening if there are any listeners.
|
|
678
|
-
* When there is no other callback for a channel, LISTEN command is executed
|
|
679
|
-
*/
|
|
680
|
-
async listen(channel: string, callback: (notification: Notification) => void) {
|
|
681
|
-
let restartConnectionError: Error = null;
|
|
682
|
-
if (this.needToFixConnectionForListen()) {
|
|
683
|
-
restartConnectionError = await this.runRestartConnectionForListen();
|
|
684
|
-
}
|
|
685
|
-
if (this.listeners.listenerCount(channel)) {
|
|
686
|
-
this.listeners.on(channel, callback);
|
|
687
|
-
} else {
|
|
688
|
-
if (restartConnectionError) {
|
|
689
|
-
throw restartConnectionError;
|
|
690
|
-
}
|
|
691
|
-
try {
|
|
692
|
-
if (!this.connectionForListen) {
|
|
693
|
-
await this.initConnectionForListen();
|
|
694
|
-
}
|
|
695
|
-
await this.connectionForListen.query(`LISTEN "${channel}"`);
|
|
696
|
-
} catch (err) {
|
|
697
|
-
this._needToRestartConnectionForListen = true;
|
|
698
|
-
throw err;
|
|
699
|
-
}
|
|
700
|
-
this.listeners.on(channel, callback);
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
/**
|
|
705
|
-
* Remove a callback which listening on a channel
|
|
706
|
-
* When all callback is removed from a channel UNLISTEN command is executed
|
|
707
|
-
* When all callback is removed from all channel, dedicated connection is released
|
|
708
|
-
*/
|
|
709
|
-
async unlisten(channel: string, callback?: (Notification) => void) {
|
|
710
|
-
let restartConnectionError: Error = null;
|
|
711
|
-
if (this.needToFixConnectionForListen()) {
|
|
712
|
-
restartConnectionError = await this.runRestartConnectionForListen();
|
|
713
|
-
}
|
|
714
|
-
if (callback && this.listeners.listenerCount(channel) > 1) {
|
|
715
|
-
this.listeners.removeListener(channel, callback);
|
|
716
|
-
} else {
|
|
717
|
-
if (restartConnectionError) {
|
|
718
|
-
throw restartConnectionError;
|
|
719
|
-
}
|
|
720
|
-
try {
|
|
721
|
-
await this.internalQuery({ connection: this.connectionForListen, sql: `UNLISTEN "${channel}"` });
|
|
722
|
-
if (this.listeners.eventNames().length == 1) {
|
|
723
|
-
this.connectionForListen.removeAllListeners('notification');
|
|
724
|
-
this.connectionForListen.release();
|
|
725
|
-
this.connectionForListen = null;
|
|
726
|
-
}
|
|
727
|
-
} catch (err) {
|
|
728
|
-
this._needToRestartConnectionForListen = true;
|
|
729
|
-
throw err;
|
|
730
|
-
}
|
|
731
|
-
this.listeners.removeAllListeners(channel);
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
/**
|
|
736
|
-
* Notify a channel (https://www.postgresql.org/docs/current/sql-notify.html)
|
|
737
|
-
*/
|
|
738
|
-
async notify(channel: string, payload?: string) {
|
|
739
|
-
if (this.needToFixConnectionForListen()) {
|
|
740
|
-
let restartConnectionError = await this.runRestartConnectionForListen();
|
|
741
|
-
if (restartConnectionError) {
|
|
742
|
-
throw restartConnectionError;
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
let hasConnectionForListen = !!this.connectionForListen;
|
|
746
|
-
let connection = this.connectionForListen || this.connection;
|
|
747
|
-
//let sql = 'NOTIFY ' + channel + ', :payload';
|
|
748
|
-
let sql = 'SELECT pg_notify(:channel, :payload)';
|
|
749
|
-
let params = { channel, payload };
|
|
750
|
-
try {
|
|
751
|
-
return this.internalQuery({ connection, sql, params });
|
|
752
|
-
} catch (err) {
|
|
753
|
-
if (hasConnectionForListen) {
|
|
754
|
-
this._needToRestartConnectionForListen = true;
|
|
755
|
-
}
|
|
756
|
-
throw err;
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
async runRestartConnectionForListen(): Promise<Error> {
|
|
761
|
-
if (!this._needToRestartConnectionForListen) {
|
|
762
|
-
return null;
|
|
763
|
-
}
|
|
764
|
-
let errorResult: Error = null;
|
|
765
|
-
if (!this.restartConnectionForListen) {
|
|
766
|
-
this.restartConnectionForListen = (async () => {
|
|
767
|
-
let eventNames = this.listeners.eventNames();
|
|
768
|
-
try {
|
|
769
|
-
await this.connectionForListen.release();
|
|
770
|
-
} catch (e) {
|
|
771
|
-
}
|
|
772
|
-
this.connectionForListen = null;
|
|
773
|
-
let error: Error;
|
|
774
|
-
if (eventNames.length) {
|
|
775
|
-
try {
|
|
776
|
-
await this.initConnectionForListen();
|
|
777
|
-
for (let channel of eventNames) {
|
|
778
|
-
await this.connectionForListen.query(`LISTEN "${channel as string}"`);
|
|
779
|
-
}
|
|
780
|
-
} catch (err) {
|
|
781
|
-
error = err;
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
return error;
|
|
785
|
-
})();
|
|
786
|
-
errorResult = await this.restartConnectionForListen;
|
|
787
|
-
this.restartConnectionForListen = null;
|
|
788
|
-
} else {
|
|
789
|
-
errorResult = await this.restartConnectionForListen;
|
|
790
|
-
}
|
|
791
|
-
if (!errorResult) {
|
|
792
|
-
this._needToRestartConnectionForListen = false;
|
|
793
|
-
}
|
|
794
|
-
return errorResult;
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
needToFixConnectionForListen(): boolean {
|
|
798
|
-
return this._needToRestartConnectionForListen;
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
private async tryToFixConnectionForListenActively() {
|
|
802
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
803
|
-
let error = await this.runRestartConnectionForListen();
|
|
804
|
-
if (error) {
|
|
805
|
-
await this.tryToFixConnectionForListenActively();
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
private async initConnectionForListen() {
|
|
810
|
-
this.connectionForListen = await this.pool.connect();
|
|
811
|
-
this.connectionForListen.on('notification', (notification: Notification) => this.listeners.emit(notification.channel, notification));
|
|
812
|
-
this.connectionForListen.on('error', (e) => {
|
|
813
|
-
this._needToRestartConnectionForListen = true;
|
|
814
|
-
this.tryToFixConnectionForListenActively();
|
|
815
|
-
});
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
export default PgDb;
|