pogi 2.10.2 → 3.0.0-beta2

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 (83) hide show
  1. package/.vscode/launch.json +47 -15
  2. package/CHANGELOG.md +11 -0
  3. package/docs/API/PgDb.md +25 -0
  4. package/docs/notification.md +19 -0
  5. package/jest.config.js +23 -0
  6. package/lib/bin/generateInterface.js +3 -3
  7. package/lib/bin/generateInterface.js.map +1 -1
  8. package/lib/connectionOptions.d.ts +10 -0
  9. package/lib/index.d.ts +1 -1
  10. package/lib/pgConverters.d.ts +9 -8
  11. package/lib/pgConverters.js +46 -32
  12. package/lib/pgConverters.js.map +1 -1
  13. package/lib/pgConverters.test.d.ts +1 -0
  14. package/lib/pgConverters.test.js +13 -0
  15. package/lib/pgConverters.test.js.map +1 -0
  16. package/lib/pgDb.d.ts +27 -27
  17. package/lib/pgDb.js +293 -100
  18. package/lib/pgDb.js.map +1 -1
  19. package/lib/pgDb.test.d.ts +1 -0
  20. package/lib/pgDb.test.js +1126 -0
  21. package/lib/pgDb.test.js.map +1 -0
  22. package/lib/pgDbInterface.d.ts +53 -0
  23. package/lib/pgDbInterface.js +11 -0
  24. package/lib/pgDbInterface.js.map +1 -0
  25. package/lib/pgDbOperators.d.ts +3 -3
  26. package/lib/pgDbOperators.js +4 -7
  27. package/lib/pgDbOperators.js.map +1 -1
  28. package/lib/pgDbOperators.test.d.ts +1 -0
  29. package/lib/pgDbOperators.test.js +313 -0
  30. package/lib/pgDbOperators.test.js.map +1 -0
  31. package/lib/pgSchema.d.ts +10 -9
  32. package/lib/pgSchema.js.map +1 -1
  33. package/lib/pgSchemaInterface.d.ts +12 -0
  34. package/lib/pgSchemaInterface.js +3 -0
  35. package/lib/pgSchemaInterface.js.map +1 -0
  36. package/lib/pgTable.d.ts +15 -40
  37. package/lib/pgTable.js +54 -54
  38. package/lib/pgTable.js.map +1 -1
  39. package/lib/pgTableInterface.d.ts +102 -0
  40. package/lib/pgTableInterface.js +4 -0
  41. package/lib/pgTableInterface.js.map +1 -0
  42. package/lib/pgUtils.d.ts +16 -6
  43. package/lib/pgUtils.js +162 -31
  44. package/lib/pgUtils.js.map +1 -1
  45. package/lib/queryAble.d.ts +20 -53
  46. package/lib/queryAble.js +149 -80
  47. package/lib/queryAble.js.map +1 -1
  48. package/lib/queryAbleInterface.d.ts +55 -0
  49. package/lib/queryAbleInterface.js +7 -0
  50. package/lib/queryAbleInterface.js.map +1 -0
  51. package/lib/queryWhere.d.ts +2 -2
  52. package/lib/queryWhere.js +19 -23
  53. package/lib/queryWhere.js.map +1 -1
  54. package/mkdocs.yml +1 -0
  55. package/package.json +21 -11
  56. package/src/bin/generateInterface.ts +2 -2
  57. package/src/connectionOptions.ts +48 -13
  58. package/src/index.d.ts +7 -0
  59. package/src/index.ts +1 -1
  60. package/src/pgConverters.test.ts +10 -0
  61. package/src/pgConverters.ts +34 -22
  62. package/src/pgDb.test.ts +1324 -0
  63. package/src/pgDb.ts +318 -122
  64. package/src/pgDbInterface.ts +57 -0
  65. package/src/pgDbOperators.test.ts +478 -0
  66. package/src/pgDbOperators.ts +45 -22
  67. package/src/pgSchema.ts +10 -9
  68. package/src/pgSchemaInterface.ts +12 -0
  69. package/src/pgTable.ts +66 -98
  70. package/src/pgTableInterface.ts +131 -0
  71. package/src/pgUtils.ts +166 -42
  72. package/src/queryAble.ts +167 -125
  73. package/src/queryAbleInterface.ts +104 -0
  74. package/src/queryWhere.ts +42 -43
  75. package/{spec/resources → src/test}/init.sql +23 -0
  76. package/src/test/pgServiceRestartTest.ts +1500 -0
  77. package/{spec/resources → src/test}/throw_exception.sql +0 -0
  78. package/{spec/resources → src/test}/tricky.sql +0 -0
  79. package/{src/tsconfig.json → tsconfig.json} +12 -11
  80. package/spec/run.js +0 -5
  81. package/spec/support/jasmine.json +0 -9
  82. package/src/test/pgDbOperatorSpec.ts +0 -492
  83. package/src/test/pgDbSpec.ts +0 -994
package/lib/pgDb.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PgDb = exports.TranzactionIsolationLevel = exports.FieldType = void 0;
3
+ exports.PgDb = exports.FieldType = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const queryAble_1 = require("./queryAble");
6
6
  const pgTable_1 = require("./pgTable");
@@ -32,13 +32,20 @@ FROM pg_proc p
32
32
  WHERE n.nspname NOT IN ('pg_catalog', 'pg_constraint', 'information_schema')
33
33
  AND tr.oid is null;`;
34
34
  const GET_CURRENT_SCHEMAS = "SELECT current_schemas(false)";
35
- const LIST_SPECIAL_TYPE_FIELDS = `SELECT c.nspname as schema_name, b.relname as table_name, a.attname as column_name, a.atttypid as typid
35
+ const LIST_SPECIAL_TYPE_FIELDS = `SELECT c.nspname as schema_name, b.relname as table_name, a.attname as column_name, a.atttypid as typid, t.typcategory
36
36
  FROM pg_attribute a
37
37
  JOIN pg_class b ON (a.attrelid = b.oid)
38
38
  JOIN pg_type t ON (a.atttypid = t.oid)
39
39
  JOIN pg_namespace c ON (b.relnamespace=c.oid)
40
40
  WHERE (a.atttypid in (114, 3802, 1082, 1083, 1114, 1184, 1266, 3614) or t.typcategory='A')
41
41
  AND reltype>0 `;
42
+ const TYPE2OID = `SELECT t.oid, typname FROM pg_catalog.pg_type t WHERE typname in (
43
+ '_bool',
44
+ 'int8','_int2','_int4','_int8','_float4','float8','_float8',
45
+ '_text','_varchar',
46
+ 'json','jsonb', '_json','_jsonb',
47
+ 'date','time','timestamp','timestamptz','timetz','_date','_time','_timestamp','_timestamptz','_timetz',
48
+ 'tsvector')`;
42
49
  var FieldType;
43
50
  (function (FieldType) {
44
51
  FieldType[FieldType["JSON"] = 0] = "JSON";
@@ -46,25 +53,29 @@ var FieldType;
46
53
  FieldType[FieldType["TIME"] = 2] = "TIME";
47
54
  FieldType[FieldType["TSVECTOR"] = 3] = "TSVECTOR";
48
55
  })(FieldType = exports.FieldType || (exports.FieldType = {}));
49
- var TranzactionIsolationLevel;
50
- (function (TranzactionIsolationLevel) {
51
- TranzactionIsolationLevel["serializable"] = "SERIALIZABLE";
52
- TranzactionIsolationLevel["repeatableRead"] = "REPEATABLE READ";
53
- TranzactionIsolationLevel["readCommitted"] = "READ COMMITTED";
54
- TranzactionIsolationLevel["readUncommitted"] = "READ UNCOMMITTED";
55
- })(TranzactionIsolationLevel = exports.TranzactionIsolationLevel || (exports.TranzactionIsolationLevel = {}));
56
56
  class PgDb extends queryAble_1.QueryAble {
57
- constructor(pgdb = {}) {
57
+ constructor(pgdb) {
58
58
  super();
59
+ this.connection = null;
59
60
  this.tables = {};
60
61
  this.fn = {};
61
62
  this.pgdbTypeParsers = {};
63
+ this.knownOids = {};
62
64
  this.listeners = new EventEmitter();
65
+ this.connectionForListen = null;
66
+ this._needToRestartConnectionForListen = false;
67
+ this.restartConnectionForListen = null;
68
+ this.notificationListener = (notification) => this.listeners.emit(notification.channel, notification);
69
+ this.errorListener = (e) => {
70
+ this._needToRestartConnectionForListen = true;
71
+ this.tryToFixConnectionForListenActively();
72
+ };
63
73
  this.schemas = {};
64
- this.config = pgdb.config;
74
+ this.config = pgdb.config || {};
65
75
  this.pool = pgdb.pool;
66
76
  this.postProcessResult = pgdb.postProcessResult;
67
77
  this.pgdbTypeParsers = pgdb.pgdbTypeParsers || {};
78
+ this.knownOids = pgdb.knownOids || {};
68
79
  this.db = this;
69
80
  if (pgdb.getLogger) {
70
81
  this.setLogger(pgdb.getLogger());
@@ -80,14 +91,14 @@ class PgDb extends queryAble_1.QueryAble {
80
91
  schema[tableName] = schema.tables[tableName];
81
92
  }
82
93
  }
83
- this.defaultSchemas = pgdb.defaultSchemas;
94
+ this.defaultSchemas = pgdb.defaultSchemas || [];
84
95
  this.setDefaultTablesAndFunctions();
85
96
  }
86
97
  setPostProcessResult(f) {
87
98
  this.postProcessResult = f;
88
99
  }
89
100
  static getInstance(config) {
90
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
101
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
91
102
  if (config.connectionString) {
92
103
  let res = CONNECTION_URL_REGEXP.exec(config.connectionString);
93
104
  if (res) {
@@ -102,7 +113,7 @@ class PgDb extends queryAble_1.QueryAble {
102
113
  if (!PgDb.instances) {
103
114
  PgDb.instances = {};
104
115
  }
105
- if (PgDb.instances[connectionString]) {
116
+ if (PgDb.instances[connectionString] != null) {
106
117
  return PgDb.instances[connectionString];
107
118
  }
108
119
  else {
@@ -113,18 +124,18 @@ class PgDb extends queryAble_1.QueryAble {
113
124
  });
114
125
  }
115
126
  close() {
116
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
127
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
117
128
  for (let cs in PgDb.instances) {
118
129
  let db = yield PgDb.instances[cs];
119
130
  if (db.pool == this.pool) {
120
131
  delete PgDb.instances[cs];
121
132
  }
122
133
  }
123
- yield this.pool.end();
134
+ yield new Promise((resolve) => this.pool.end(resolve));
124
135
  });
125
136
  }
126
137
  static connect(config) {
127
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
138
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
128
139
  if (config.connectionString) {
129
140
  let res = CONNECTION_URL_REGEXP.exec(config.connectionString);
130
141
  if (res) {
@@ -141,7 +152,7 @@ class PgDb extends queryAble_1.QueryAble {
141
152
  });
142
153
  }
143
154
  init() {
144
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
155
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
145
156
  this.pool = new pg.Pool(_.omit(this.config, ['logger', 'skipUndefined']));
146
157
  if (this.config.logger)
147
158
  this.setLogger(this.config.logger);
@@ -154,16 +165,16 @@ class PgDb extends queryAble_1.QueryAble {
154
165
  });
155
166
  }
156
167
  reload() {
157
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
168
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
158
169
  yield this.initSchemasAndTables();
159
170
  yield this.initFieldTypes();
160
171
  });
161
172
  }
162
173
  initSchemasAndTables() {
163
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
174
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
164
175
  let schemasAndTables = yield this.query(LIST_SCHEMAS_TABLES);
165
176
  let functions = yield this.query(GET_SCHEMAS_PROCEDURES);
166
- this.defaultSchemas = PgConverters.arraySplit(yield this.queryOneField(GET_CURRENT_SCHEMAS));
177
+ this.defaultSchemas = yield this.queryOneField(GET_CURRENT_SCHEMAS);
167
178
  let oldSchemaNames = Object.keys(this.schemas);
168
179
  for (let sc of oldSchemaNames) {
169
180
  if (this[sc] === this.schemas[sc])
@@ -203,145 +214,225 @@ class PgDb extends queryAble_1.QueryAble {
203
214
  }
204
215
  }
205
216
  initFieldTypes() {
206
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
217
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
207
218
  let schemaNames = "'" + Object.keys(this.schemas).join("', '") + "'";
208
219
  if (schemaNames == "''") {
209
220
  this.getLogger(true).error("No readable schema found!");
210
221
  return;
211
222
  }
223
+ let type2oid = {};
224
+ let res = yield this.query(TYPE2OID);
225
+ for (let tt of res || []) {
226
+ type2oid[tt.typname] = +tt.oid;
227
+ }
212
228
  let specialTypeFields = yield this.query(LIST_SPECIAL_TYPE_FIELDS + ' AND c.nspname in (' + schemaNames + ')');
213
229
  for (let r of specialTypeFields) {
214
230
  if (this.schemas[r.schema_name][r.table_name]) {
215
231
  this.schemas[r.schema_name][r.table_name].fieldTypes[r.column_name] =
216
- ([3802, 114].indexOf(r.typid) > -1) ? FieldType.JSON :
217
- ([3614].indexOf(r.typid) > -1) ? FieldType.TSVECTOR :
218
- ([1082, 1083, 1114, 1184, 1266].indexOf(r.typid) > -1) ? FieldType.TIME :
232
+ ([type2oid['json'], type2oid['jsonb']].indexOf(r.typid) > -1) ? FieldType.JSON :
233
+ ([type2oid['tsvector']].indexOf(r.typid) > -1) ? FieldType.TSVECTOR :
234
+ ([type2oid['date'], type2oid['time'], type2oid['timestamp'], type2oid['timestamptz'], type2oid['timetz']].indexOf(r.typid) > -1) ? FieldType.TIME :
219
235
  FieldType.ARRAY;
220
236
  }
221
237
  }
238
+ let builtInArrayTypeParsers = [
239
+ {
240
+ oidList: [
241
+ type2oid['_bool']
242
+ ],
243
+ parser: PgConverters.parseBooleanArray
244
+ },
245
+ {
246
+ oidList: [
247
+ type2oid['_int2'],
248
+ type2oid['_int4'],
249
+ type2oid['_float4'],
250
+ ],
251
+ parser: PgConverters.parseNumberArray
252
+ },
253
+ {
254
+ oidList: [
255
+ type2oid['_text'],
256
+ type2oid['_varchar']
257
+ ],
258
+ parser: PgConverters.parseArray
259
+ },
260
+ {
261
+ oidList: [
262
+ type2oid['_json'],
263
+ type2oid['_jsonb']
264
+ ],
265
+ parser: PgConverters.parseJsonArray
266
+ },
267
+ {
268
+ oidList: [
269
+ type2oid['_date'],
270
+ type2oid['_time'],
271
+ type2oid['_timetz'],
272
+ type2oid['_timestamp'],
273
+ type2oid['_timestamptz'],
274
+ ],
275
+ parser: PgConverters.parseDateArray
276
+ }
277
+ ];
278
+ builtInArrayTypeParsers.forEach(parserObj => {
279
+ parserObj.oidList.forEach(oid => {
280
+ pg.types.setTypeParser(oid, parserObj.parser);
281
+ delete this.pgdbTypeParsers[oid];
282
+ this.knownOids[oid] = true;
283
+ });
284
+ });
222
285
  for (let r of specialTypeFields) {
286
+ if (this.knownOids[r.typid] && !this.pgdbTypeParsers[r.typid]) {
287
+ continue;
288
+ }
223
289
  switch (r.typid) {
224
- case 114:
225
- case 3802:
226
- case 1082:
227
- case 1083:
228
- case 1114:
229
- case 1184:
230
- case 1266:
231
- case 3614:
232
- break;
233
- case 1005:
234
- case 1007:
235
- case 1021:
236
- pg.types.setTypeParser(r.typid, PgConverters.arraySplitToNum);
237
- break;
238
- case 1009:
239
- case 1015:
240
- pg.types.setTypeParser(r.typid, PgConverters.arraySplit);
241
- break;
242
- case 3807:
243
- pg.types.setTypeParser(r.typid, PgConverters.arraySplitToJson);
244
- break;
245
- case 1016:
246
- case 1022:
247
- break;
248
- case 1115:
249
- case 1182:
250
- case 1183:
251
- case 1185:
252
- case 1270:
253
- pg.types.setTypeParser(r.typid, PgConverters.arraySplitToDate);
290
+ case type2oid['json']:
291
+ case type2oid['jsonb']:
292
+ case type2oid['date']:
293
+ case type2oid['time']:
294
+ case type2oid['timetz']:
295
+ case type2oid['timestamp']:
296
+ case type2oid['timestamptz']:
297
+ case type2oid['tsvector']:
298
+ case type2oid['_int8']:
299
+ case type2oid['_float8']:
254
300
  break;
255
301
  default:
256
- pg.types.setTypeParser(r.typid, PgConverters.arraySplit);
302
+ pg.types.setTypeParser(r.typid, PgConverters.parseArray);
303
+ delete this.pgdbTypeParsers[r.typid];
257
304
  }
258
305
  }
259
- yield this.setPgDbTypeParser('int8', PgConverters.numWithValidation);
260
- yield this.setPgDbTypeParser('float8', PgConverters.numWithValidation);
261
- yield this.setPgDbTypeParser('_int8', PgConverters.stringArrayToNumWithValidation);
262
- yield this.setPgDbTypeParser('_float8', PgConverters.stringArrayToNumWithValidation);
306
+ this.pgdbTypeParsers[type2oid['int8']] = PgConverters.parseNumberWithValidation;
307
+ this.pgdbTypeParsers[type2oid['float8']] = PgConverters.parseNumberWithValidation;
308
+ this.pgdbTypeParsers[type2oid['_int8']] = PgConverters.parseNumberArrayWithValidation;
309
+ this.pgdbTypeParsers[type2oid['_float8']] = PgConverters.parseNumberArrayWithValidation;
310
+ this.knownOids[type2oid['int8']] = true;
311
+ this.knownOids[type2oid['float8']] = true;
312
+ this.knownOids[type2oid['_int8']] = true;
313
+ this.knownOids[type2oid['_float8']] = true;
314
+ let allUsedTypeFields = yield this.queryOneColumn(`
315
+ SELECT distinct a.atttypid as typid
316
+ FROM pg_attribute a
317
+ JOIN pg_class b ON (a.attrelid = b.oid)
318
+ JOIN pg_type t ON (a.atttypid = t.oid)
319
+ JOIN pg_namespace c ON (b.relnamespace=c.oid)
320
+ WHERE
321
+ reltype>0 AND
322
+ c.nspname in (${schemaNames})`);
323
+ allUsedTypeFields.forEach(oid => this.knownOids[oid] = true);
263
324
  });
264
325
  }
265
326
  setTypeParser(typeName, parser, schemaName) {
266
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
327
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
267
328
  try {
268
329
  if (schemaName) {
269
330
  let oid = yield this.queryOneField(GET_OID_FOR_COLUMN_TYPE_FOR_SCHEMA, { typeName, schemaName });
270
331
  pg.types.setTypeParser(oid, parser);
271
332
  delete this.pgdbTypeParsers[oid];
333
+ this.knownOids[oid] = true;
272
334
  }
273
335
  else {
274
336
  let list = yield this.queryOneColumn(GET_OID_FOR_COLUMN_TYPE, { typeName });
275
337
  list.forEach(oid => {
276
338
  pg.types.setTypeParser(oid, parser);
277
339
  delete this.pgdbTypeParsers[oid];
340
+ this.knownOids[oid] = true;
278
341
  });
279
342
  }
280
343
  }
281
344
  catch (e) {
282
- throw Error('Not existing type: ' + typeName);
345
+ throw new Error('Not existing type: ' + typeName);
283
346
  }
284
347
  });
285
348
  }
286
349
  setPgDbTypeParser(typeName, parser, schemaName) {
287
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
350
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
288
351
  try {
289
352
  if (schemaName) {
290
353
  let oid = yield this.queryOneField(GET_OID_FOR_COLUMN_TYPE_FOR_SCHEMA, { typeName, schemaName });
291
354
  this.pgdbTypeParsers[oid] = parser;
355
+ this.knownOids[oid] = true;
292
356
  }
293
357
  else {
294
358
  let list = yield this.queryOneColumn(GET_OID_FOR_COLUMN_TYPE, { typeName });
295
- list.forEach(oid => this.pgdbTypeParsers[oid] = parser);
359
+ list.forEach(oid => {
360
+ this.pgdbTypeParsers[oid] = parser;
361
+ this.knownOids[oid] = true;
362
+ });
296
363
  }
297
364
  }
298
365
  catch (e) {
299
- throw Error('Not existing type: ' + typeName);
366
+ throw new Error('Not existing type: ' + typeName);
367
+ }
368
+ });
369
+ }
370
+ resetMissingParsers(connection, oidList) {
371
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
372
+ let unknownOids = oidList.filter(oid => !this.knownOids[oid]);
373
+ if (unknownOids.length) {
374
+ let fieldsData = yield connection.query(`select oid, typcategory from pg_type where oid = ANY($1)`, [unknownOids]);
375
+ fieldsData.rows.forEach(fieldData => {
376
+ if (fieldData.typcategory == 'A') {
377
+ this.pgdbTypeParsers[fieldData.oid] = PgConverters.parseArray;
378
+ }
379
+ this.knownOids[fieldData.oid] = true;
380
+ });
300
381
  }
301
382
  });
302
383
  }
303
384
  dedicatedConnectionBegin() {
304
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
385
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
386
+ if (this.needToFixConnectionForListen()) {
387
+ yield this.runRestartConnectionForListen();
388
+ }
305
389
  let pgDb = new PgDb(this);
306
390
  pgDb.connection = yield this.pool.connect();
391
+ pgDb.connection.on('error', queryAble_1.QueryAble.connectionErrorListener);
307
392
  return pgDb;
308
393
  });
309
394
  }
310
395
  dedicatedConnectionEnd() {
311
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
396
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
312
397
  if (this.connection) {
313
- yield this.connection.release();
398
+ this.connection.off('error', queryAble_1.QueryAble.connectionErrorListener);
399
+ try {
400
+ yield this.connection.release();
401
+ }
402
+ catch (err) {
403
+ this.getLogger().error('Error while dedicated connection end.', err);
404
+ }
314
405
  this.connection = null;
315
406
  }
316
407
  return this;
317
408
  });
318
409
  }
319
410
  savePoint(name) {
320
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
411
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
321
412
  if (this.isTransactionActive()) {
322
413
  name = (name || '').replace(/"/g, '');
323
414
  yield this.query(`SAVEPOINT "${name}"`);
324
415
  }
325
416
  else {
326
- throw Error('No active transaction');
417
+ throw new Error('No active transaction');
327
418
  }
328
419
  return this;
329
420
  });
330
421
  }
331
422
  savePointRelease(name) {
332
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
423
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
333
424
  if (this.isTransactionActive()) {
334
425
  name = (name || '').replace(/"/g, '');
335
426
  yield this.query(`RELEASE SAVEPOINT "${name}"`);
336
427
  }
337
428
  else {
338
- throw Error('No active transaction');
429
+ throw new Error('No active transaction');
339
430
  }
340
431
  return this;
341
432
  });
342
433
  }
343
434
  transactionBegin(options) {
344
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
435
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
345
436
  let pgDb = this.connection ? this : yield this.dedicatedConnectionBegin();
346
437
  let q = 'BEGIN';
347
438
  if (options === null || options === void 0 ? void 0 : options.isolationLevel) {
@@ -358,13 +449,13 @@ class PgDb extends queryAble_1.QueryAble {
358
449
  });
359
450
  }
360
451
  transactionCommit() {
361
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
452
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
362
453
  yield this.query('COMMIT');
363
454
  return this.dedicatedConnectionEnd();
364
455
  });
365
456
  }
366
457
  transactionRollback(options) {
367
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
458
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
368
459
  if (options === null || options === void 0 ? void 0 : options.savePoint) {
369
460
  let name = (options.savePoint || '').replace(/"/g, '');
370
461
  yield this.query(`ROLLBACK TO SAVEPOINT "${name}"`);
@@ -380,7 +471,7 @@ class PgDb extends queryAble_1.QueryAble {
380
471
  return this.connection != null;
381
472
  }
382
473
  execute(fileName, statementTransformerFunction) {
383
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
474
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
384
475
  let isTransactionInPlace = this.isTransactionActive();
385
476
  let pgdb = isTransactionInPlace ? this : yield this.dedicatedConnectionBegin();
386
477
  let runStatementList = (statementList) => {
@@ -434,7 +525,7 @@ class PgDb extends queryAble_1.QueryAble {
434
525
  let s = line.slice(SQL_TOKENIZER_REGEXP.lastIndex - 1);
435
526
  let token = s.match(SQL_$_ESCAPE_REGEXP);
436
527
  if (!token) {
437
- throw Error('Invalid sql in line: ' + line);
528
+ throw new Error('Invalid sql in line: ' + line);
438
529
  }
439
530
  inQuotedString = token[0];
440
531
  SQL_TOKENIZER_REGEXP.lastIndex += inQuotedString.length - 1;
@@ -523,46 +614,148 @@ class PgDb extends queryAble_1.QueryAble {
523
614
  });
524
615
  }
525
616
  listen(channel, callback) {
526
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
527
- if (!this.connectionForListen) {
528
- this.connectionForListen = yield this.pool.connect();
529
- this.connectionForListen.on('notification', (notification) => this.listeners.emit(notification.channel, notification));
617
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
618
+ let restartConnectionError = null;
619
+ if (this.needToFixConnectionForListen()) {
620
+ restartConnectionError = yield this.runRestartConnectionForListen();
530
621
  }
531
- if (!this.listeners.listenerCount(channel)) {
532
- yield this.connectionForListen.query('LISTEN ' + channel);
622
+ if (this.listeners.listenerCount(channel)) {
623
+ this.listeners.on(channel, callback);
624
+ }
625
+ else {
626
+ if (restartConnectionError) {
627
+ throw restartConnectionError;
628
+ }
629
+ try {
630
+ if (!this.connectionForListen) {
631
+ yield this.initConnectionForListen();
632
+ }
633
+ yield this.connectionForListen.query(`LISTEN "${channel}"`);
634
+ }
635
+ catch (err) {
636
+ this._needToRestartConnectionForListen = true;
637
+ throw err;
638
+ }
639
+ this.listeners.on(channel, callback);
533
640
  }
534
- this.listeners.on(channel, callback);
535
641
  });
536
642
  }
537
643
  unlisten(channel, callback) {
538
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
539
- if (!this.connectionForListen) {
540
- this.listeners.removeAllListeners();
541
- return;
644
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
645
+ let restartConnectionError = null;
646
+ if (this.needToFixConnectionForListen()) {
647
+ restartConnectionError = yield this.runRestartConnectionForListen();
542
648
  }
543
- if (callback) {
649
+ if (callback && this.listeners.listenerCount(channel) > 1) {
544
650
  this.listeners.removeListener(channel, callback);
545
651
  }
546
652
  else {
653
+ if (restartConnectionError) {
654
+ throw restartConnectionError;
655
+ }
656
+ try {
657
+ yield this.internalQuery({ connection: this.connectionForListen, sql: `UNLISTEN "${channel}"` });
658
+ if (this.connectionForListen && this.listeners.eventNames().length == 1) {
659
+ this.connectionForListen.removeAllListeners('notification');
660
+ this.connectionForListen.release();
661
+ this.connectionForListen = null;
662
+ }
663
+ }
664
+ catch (err) {
665
+ this._needToRestartConnectionForListen = true;
666
+ throw err;
667
+ }
547
668
  this.listeners.removeAllListeners(channel);
548
669
  }
549
- if (!this.listeners.listenerCount(channel)) {
550
- yield this.internalQuery({ connection: this.connectionForListen, sql: 'UNLISTEN ' + channel });
551
- }
552
- let allListeners = this.listeners.eventNames().reduce((sum, ename) => sum + this.listeners.listenerCount(ename), 0);
553
- if (!allListeners && this.connectionForListen) {
554
- this.connectionForListen.removeAllListeners('notification');
555
- this.connectionForListen.release();
556
- this.connectionForListen = null;
557
- }
558
670
  });
559
671
  }
560
672
  notify(channel, payload) {
561
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
673
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
674
+ if (this.needToFixConnectionForListen()) {
675
+ let restartConnectionError = yield this.runRestartConnectionForListen();
676
+ if (restartConnectionError) {
677
+ throw restartConnectionError;
678
+ }
679
+ }
680
+ let hasConnectionForListen = !!this.connectionForListen;
562
681
  let connection = this.connectionForListen || this.connection;
563
682
  let sql = 'SELECT pg_notify(:channel, :payload)';
564
683
  let params = { channel, payload };
565
- return this.internalQuery({ connection, sql, params });
684
+ try {
685
+ return this.internalQuery({ connection, sql, params });
686
+ }
687
+ catch (err) {
688
+ if (hasConnectionForListen) {
689
+ this._needToRestartConnectionForListen = true;
690
+ }
691
+ throw err;
692
+ }
693
+ });
694
+ }
695
+ runRestartConnectionForListen() {
696
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
697
+ if (!this._needToRestartConnectionForListen) {
698
+ return null;
699
+ }
700
+ let errorResult = null;
701
+ if (!this.restartConnectionForListen) {
702
+ this.restartConnectionForListen = (() => (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
703
+ let eventNames = this.listeners.eventNames();
704
+ if (this.connectionForListen) {
705
+ try {
706
+ this.connectionForListen.on('notification', this.notificationListener);
707
+ this.connectionForListen.on('error', this.errorListener);
708
+ }
709
+ catch (e) { }
710
+ try {
711
+ yield this.connectionForListen.release();
712
+ }
713
+ catch (e) { }
714
+ this.connectionForListen = null;
715
+ }
716
+ let error = null;
717
+ if (eventNames.length) {
718
+ try {
719
+ yield this.initConnectionForListen();
720
+ for (let channel of eventNames) {
721
+ yield this.connectionForListen.query(`LISTEN "${channel}"`);
722
+ }
723
+ }
724
+ catch (err) {
725
+ error = err;
726
+ }
727
+ }
728
+ return error;
729
+ }))();
730
+ errorResult = yield this.restartConnectionForListen;
731
+ this.restartConnectionForListen = null;
732
+ }
733
+ else {
734
+ errorResult = yield this.restartConnectionForListen;
735
+ }
736
+ if (!errorResult) {
737
+ this._needToRestartConnectionForListen = false;
738
+ }
739
+ return errorResult;
740
+ });
741
+ }
742
+ needToFixConnectionForListen() {
743
+ return this._needToRestartConnectionForListen;
744
+ }
745
+ tryToFixConnectionForListenActively() {
746
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
747
+ yield new Promise(r => setTimeout(r, 1000));
748
+ let error = yield this.runRestartConnectionForListen();
749
+ if (error) {
750
+ yield this.tryToFixConnectionForListenActively();
751
+ }
752
+ });
753
+ }
754
+ initConnectionForListen() {
755
+ return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
756
+ this.connectionForListen = yield this.pool.connect();
757
+ this.connectionForListen.on('notification', this.notificationListener);
758
+ this.connectionForListen.on('error', this.errorListener);
566
759
  });
567
760
  }
568
761
  }