graphile-build-pg 4.9.1 → 4.11.2

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 (76) hide show
  1. package/README.md +1 -0
  2. package/node8plus/plugins/PageInfoStartEndCursor.js +2 -2
  3. package/node8plus/plugins/PageInfoStartEndCursor.js.flow +6 -2
  4. package/node8plus/plugins/PageInfoStartEndCursor.js.map +1 -1
  5. package/node8plus/plugins/PgAllRows.js +1 -1
  6. package/node8plus/plugins/PgAllRows.js.flow +6 -3
  7. package/node8plus/plugins/PgAllRows.js.map +1 -1
  8. package/node8plus/plugins/PgBackwardRelationPlugin.js +2 -2
  9. package/node8plus/plugins/PgBackwardRelationPlugin.js.flow +8 -2
  10. package/node8plus/plugins/PgBackwardRelationPlugin.js.map +1 -1
  11. package/node8plus/plugins/PgBasicsPlugin.js +1 -1
  12. package/node8plus/plugins/PgBasicsPlugin.js.flow +1 -1
  13. package/node8plus/plugins/PgBasicsPlugin.js.map +1 -1
  14. package/node8plus/plugins/PgConditionComputedColumnPlugin.js +1 -1
  15. package/node8plus/plugins/PgConditionComputedColumnPlugin.js.flow +4 -1
  16. package/node8plus/plugins/PgConditionComputedColumnPlugin.js.map +1 -1
  17. package/node8plus/plugins/PgConnectionArgCondition.js +3 -3
  18. package/node8plus/plugins/PgConnectionArgCondition.js.flow +11 -3
  19. package/node8plus/plugins/PgConnectionArgCondition.js.map +1 -1
  20. package/node8plus/plugins/PgConnectionArgFirstLastBeforeAfter.js +5 -5
  21. package/node8plus/plugins/PgConnectionArgFirstLastBeforeAfter.js.flow +20 -7
  22. package/node8plus/plugins/PgConnectionArgFirstLastBeforeAfter.js.map +1 -1
  23. package/node8plus/plugins/PgConnectionArgOrderBy.js +2 -2
  24. package/node8plus/plugins/PgConnectionArgOrderBy.js.flow +8 -2
  25. package/node8plus/plugins/PgConnectionArgOrderBy.js.map +1 -1
  26. package/node8plus/plugins/PgConnectionTotalCount.js +1 -1
  27. package/node8plus/plugins/PgConnectionTotalCount.js.flow +4 -1
  28. package/node8plus/plugins/PgConnectionTotalCount.js.map +1 -1
  29. package/node8plus/plugins/PgForwardRelationPlugin.js +1 -1
  30. package/node8plus/plugins/PgForwardRelationPlugin.js.flow +4 -1
  31. package/node8plus/plugins/PgForwardRelationPlugin.js.map +1 -1
  32. package/node8plus/plugins/PgIntrospectionPlugin.d.ts +1 -0
  33. package/node8plus/plugins/PgIntrospectionPlugin.js +182 -159
  34. package/node8plus/plugins/PgIntrospectionPlugin.js.flow +218 -198
  35. package/node8plus/plugins/PgIntrospectionPlugin.js.map +1 -1
  36. package/node8plus/plugins/PgJWTPlugin.js +1 -1
  37. package/node8plus/plugins/PgJWTPlugin.js.flow +3 -1
  38. package/node8plus/plugins/PgJWTPlugin.js.map +1 -1
  39. package/node8plus/plugins/PgMutationCreatePlugin.js +7 -7
  40. package/node8plus/plugins/PgMutationCreatePlugin.js.flow +26 -7
  41. package/node8plus/plugins/PgMutationCreatePlugin.js.map +1 -1
  42. package/node8plus/plugins/PgMutationPayloadEdgePlugin.js +2 -2
  43. package/node8plus/plugins/PgMutationPayloadEdgePlugin.js.flow +8 -2
  44. package/node8plus/plugins/PgMutationPayloadEdgePlugin.js.map +1 -1
  45. package/node8plus/plugins/PgMutationUpdateDeletePlugin.js +11 -11
  46. package/node8plus/plugins/PgMutationUpdateDeletePlugin.js.flow +40 -11
  47. package/node8plus/plugins/PgMutationUpdateDeletePlugin.js.map +1 -1
  48. package/node8plus/plugins/PgRecordFunctionConnectionPlugin.js +6 -6
  49. package/node8plus/plugins/PgRecordFunctionConnectionPlugin.js.flow +24 -6
  50. package/node8plus/plugins/PgRecordFunctionConnectionPlugin.js.map +1 -1
  51. package/node8plus/plugins/PgRecordReturnTypesPlugin.js +1 -1
  52. package/node8plus/plugins/PgRecordReturnTypesPlugin.js.flow +6 -3
  53. package/node8plus/plugins/PgRecordReturnTypesPlugin.js.map +1 -1
  54. package/node8plus/plugins/PgRowNode.js +2 -2
  55. package/node8plus/plugins/PgRowNode.js.flow +8 -2
  56. package/node8plus/plugins/PgRowNode.js.map +1 -1
  57. package/node8plus/plugins/PgScalarFunctionConnectionPlugin.js +6 -6
  58. package/node8plus/plugins/PgScalarFunctionConnectionPlugin.js.flow +24 -6
  59. package/node8plus/plugins/PgScalarFunctionConnectionPlugin.js.map +1 -1
  60. package/node8plus/plugins/PgTablesPlugin.js +13 -13
  61. package/node8plus/plugins/PgTablesPlugin.js.flow +56 -20
  62. package/node8plus/plugins/PgTablesPlugin.js.map +1 -1
  63. package/node8plus/plugins/PgTypesPlugin.js +62 -78
  64. package/node8plus/plugins/PgTypesPlugin.js.flow +193 -76
  65. package/node8plus/plugins/PgTypesPlugin.js.map +1 -1
  66. package/node8plus/plugins/makeProcField.js +3 -3
  67. package/node8plus/plugins/makeProcField.js.flow +16 -7
  68. package/node8plus/plugins/makeProcField.js.map +1 -1
  69. package/node8plus/plugins/viaTemporaryTable.js +1 -1
  70. package/node8plus/plugins/viaTemporaryTable.js.flow +8 -4
  71. package/node8plus/plugins/viaTemporaryTable.js.map +1 -1
  72. package/node8plus/postgresInterval.js +12 -33
  73. package/node8plus/postgresInterval.js.flow +22 -51
  74. package/node8plus/postgresInterval.js.map +1 -1
  75. package/package.json +9 -9
  76. package/res/watch-fixtures.sql +1 -1
@@ -171,7 +171,7 @@ function smartCommentConstraints(introspectionResults) {
171
171
 
172
172
  if (klass.tags.unique) {
173
173
  if (Array.isArray(klass.tags.unique)) {
174
- klass.tags.unique.forEach(key => addKey(key, true));
174
+ klass.tags.unique.forEach(key => addKey(key));
175
175
  } else {
176
176
  addKey(klass.tags.unique);
177
177
  }
@@ -256,6 +256,142 @@ function smartCommentConstraints(introspectionResults) {
256
256
  }
257
257
  });
258
258
  }
259
+
260
+ function isEnumConstraint(klass, con, isEnumTable) {
261
+ if (con.classId === klass.id) {
262
+ const isPrimaryKey = con.type === "p";
263
+ const isUniqueConstraint = con.type === "u";
264
+
265
+ if (isPrimaryKey || isUniqueConstraint) {
266
+ const isExplicitEnumConstraint = con.tags.enum === true || typeof con.tags.enum === "string";
267
+ const isPrimaryKeyOfEnumTableConstraint = con.type === "p" && isEnumTable;
268
+
269
+ if (isExplicitEnumConstraint || isPrimaryKeyOfEnumTableConstraint) {
270
+ const hasExactlyOneColumn = con.keyAttributeNums.length === 1;
271
+
272
+ if (!hasExactlyOneColumn) {
273
+ throw new Error(`Enum table "${klass.namespaceName}"."${klass.name}" enum constraint '${con.name}' is composite; it should have exactly one column (found: ${con.keyAttributeNums.length})`);
274
+ }
275
+
276
+ return true;
277
+ }
278
+ }
279
+ }
280
+
281
+ return false;
282
+ }
283
+
284
+ function enumTables(introspectionResults) {
285
+ introspectionResults.class.map(async klass => {
286
+ const isEnumTable = klass.tags.enum === true || typeof klass.tags.enum === "string";
287
+
288
+ if (isEnumTable) {
289
+ // Prevent the table being recognised as a table
290
+ // eslint-disable-next-line require-atomic-updates
291
+ klass.tags.omit = true; // eslint-disable-next-line require-atomic-updates
292
+
293
+ klass.isSelectable = false; // eslint-disable-next-line require-atomic-updates
294
+
295
+ klass.isInsertable = false; // eslint-disable-next-line require-atomic-updates
296
+
297
+ klass.isUpdatable = false; // eslint-disable-next-line require-atomic-updates
298
+
299
+ klass.isDeletable = false;
300
+ } // By this point, even views should have "fake" constraints we can use
301
+ // (e.g. `@primaryKey`)
302
+
303
+
304
+ const enumConstraints = introspectionResults.constraint.filter(con => isEnumConstraint(klass, con, isEnumTable)); // Get all the columns
305
+
306
+ const enumTableColumns = introspectionResults.attribute.filter(attr => attr.classId === klass.id); // Get description column
307
+
308
+ const descriptionColumn = enumTableColumns.find(attr => attr.name === "description" || attr.tags.enumDescription);
309
+ const allData = klass._internalEnumData || [];
310
+ enumConstraints.forEach(constraint => {
311
+ const col = enumTableColumns.find(col => col.num === constraint.keyAttributeNums[0]);
312
+
313
+ if (!col) {
314
+ // Should never happen
315
+ throw new Error("Graphile Engine error - could not find column for enum constraint");
316
+ }
317
+
318
+ const data = allData.filter(row => row[col.name] != null);
319
+
320
+ if (data.length < 1) {
321
+ throw new Error(`Enum table "${klass.namespaceName}"."${klass.name}" contains no entries for enum constraint '${constraint.name}'.`);
322
+ } // Create fake enum type
323
+
324
+
325
+ const constraintIdent = constraint.type === "p" ? "" : `_${constraint.name}`;
326
+ const enumTypeArray = {
327
+ kind: "type",
328
+ isFake: true,
329
+ id: `FAKE_ENUM_${klass.namespaceName}_${klass.name}${constraintIdent}_list`,
330
+ name: `_${klass.name}${constraintIdent}`,
331
+ description: null,
332
+ tags: {},
333
+ namespaceId: klass.namespaceId,
334
+ namespaceName: klass.namespaceName,
335
+ type: "b",
336
+ category: "A",
337
+ domainIsNotNull: null,
338
+ arrayItemTypeId: null,
339
+ typeLength: -1,
340
+ isPgArray: true,
341
+ classId: null,
342
+ domainBaseTypeId: null,
343
+ domainTypeModifier: null,
344
+ domainHasDefault: false,
345
+ enumVariants: null,
346
+ enumDescriptions: null,
347
+ rangeSubTypeId: null
348
+ };
349
+ const enumType = {
350
+ kind: "type",
351
+ isFake: true,
352
+ id: `FAKE_ENUM_${klass.namespaceName}_${klass.name}${constraintIdent}`,
353
+ name: `${klass.name}${constraintIdent}`,
354
+ description: klass.description,
355
+ tags: { ...klass.tags,
356
+ ...constraint.tags
357
+ },
358
+ namespaceId: klass.namespaceId,
359
+ namespaceName: klass.namespaceName,
360
+ type: "e",
361
+ category: "E",
362
+ domainIsNotNull: null,
363
+ arrayItemTypeId: enumTypeArray.id,
364
+ typeLength: 4,
365
+ // ???
366
+ isPgArray: false,
367
+ classId: null,
368
+ domainBaseTypeId: null,
369
+ domainTypeModifier: null,
370
+ domainHasDefault: false,
371
+ enumVariants: data.map(r => r[col.name]),
372
+ enumDescriptions: descriptionColumn ? data.map(r => r[descriptionColumn.name]) : null,
373
+ // TODO: enumDescriptions
374
+ rangeSubTypeId: null
375
+ };
376
+ introspectionResults.type.push(enumType, enumTypeArray);
377
+ introspectionResults.typeById[enumType.id] = enumType;
378
+ introspectionResults.typeById[enumTypeArray.id] = enumTypeArray; // Change type of all attributes that reference this table to
379
+ // reference this enum type
380
+
381
+ introspectionResults.constraint.forEach(c => {
382
+ if (c.type === "f" && c.foreignClassId === klass.id && c.foreignKeyAttributeNums.length === 1 && c.foreignKeyAttributeNums[0] === col.num) {
383
+ // Get the attribute
384
+ const fkattr = introspectionResults.attribute.find(attr => attr.classId === c.classId && attr.num === c.keyAttributeNums[0]);
385
+
386
+ if (fkattr) {
387
+ // Override the detected type to pretend to be our enum
388
+ fkattr.typeId = enumType.id;
389
+ }
390
+ }
391
+ });
392
+ });
393
+ });
394
+ }
259
395
  /* The argument to this must not contain cyclic references! */
260
396
 
261
397
 
@@ -340,74 +476,29 @@ var PgIntrospectionPlugin = async function PgIntrospectionPlugin(builder, {
340
476
  const extensionConfigurationClassIds = (0, _flatMap.default)(result.extension, e => e.configurationClassIds);
341
477
  result.class.forEach(klass => {
342
478
  klass.isExtensionConfigurationTable = extensionConfigurationClassIds.indexOf(klass.id) >= 0;
343
- });
344
- await Promise.all(result.class.map(async klass => {
345
- const isEnumTable = klass.tags.enum === true || typeof klass.tags.enum === "string";
346
-
347
- if (isEnumTable) {
348
- // Prevent the table being recognised as a table
349
- // eslint-disable-next-line require-atomic-updates
350
- klass.tags.omit = true; // eslint-disable-next-line require-atomic-updates
351
-
352
- klass.isSelectable = false; // eslint-disable-next-line require-atomic-updates
353
-
354
- klass.isInsertable = false; // eslint-disable-next-line require-atomic-updates
355
-
356
- klass.isUpdatable = false; // eslint-disable-next-line require-atomic-updates
479
+ }); // Assert the columns are text
357
480
 
358
- klass.isDeletable = false;
481
+ const VARCHAR_ID = "1043";
482
+ const TEXT_ID = "25";
483
+ const CHAR_ID = "18";
484
+ const BPCHAR_ID = "1042";
485
+ const VALID_TYPE_IDS = [VARCHAR_ID, TEXT_ID, CHAR_ID, BPCHAR_ID];
486
+ await Promise.all(result.class.map(async klass => {
487
+ if (!schemas.includes(klass.namespaceName)) {
488
+ // Only support enums in public tables/views
489
+ return;
359
490
  }
360
491
 
361
- const enumConstraints = result.constraint.filter(con => {
362
- if (con.classId === klass.id) {
363
- const isPrimaryKey = con.type === "p";
364
- const isUniqueConstraint = con.type === "u";
365
-
366
- if (isPrimaryKey || isUniqueConstraint) {
367
- const isExplicitEnumConstraint = con.tags.enum === true || typeof con.tags.enum === "string";
368
- const isPrimaryKeyOfEnumTableConstraint = con.type === "p" && isEnumTable;
369
-
370
- if (isExplicitEnumConstraint || isPrimaryKeyOfEnumTableConstraint) {
371
- const hasExactlyOneColumn = con.keyAttributeNums.length === 1;
372
-
373
- if (!hasExactlyOneColumn) {
374
- throw new Error(`Enum table "${klass.namespaceName}"."${klass.name}" enum constraint '${con.name}' is composite; it should have exactly one column (found: ${con.keyAttributeNums.length})`);
375
- }
492
+ const isEnumTable = klass.tags.enum === true || typeof klass.tags.enum === "string"; // NOTE: this only matches on tables (not views, since they don't
493
+ // have constraints), which is why we repeat the isEnumTable check below.
376
494
 
377
- return true;
378
- }
379
- }
380
- }
381
-
382
- return false;
383
- });
495
+ const hasEnumConstraints = result.constraint.some(con => isEnumConstraint(klass, con, isEnumTable));
384
496
 
385
- if (enumConstraints.length > 0) {
386
- // Get all the columns
387
- const enumTableColumns = result.attribute.filter(attr => attr.classId === klass.id).sort((a, z) => a.num - z.num); // Get description column
497
+ if (isEnumTable || hasEnumConstraints) {
498
+ // Get the list of columns enums are defined for
499
+ const enumTableColumns = result.attribute.filter(attr => attr.classId === klass.id && VALID_TYPE_IDS.includes(attr.typeId)).sort((a, z) => a.num - z.num); // Load data from the table/view.
388
500
 
389
- const descriptionColumn = enumTableColumns.find(attr => attr.name === "description" || attr.tags.enumDescription); // Assert the columns are text
390
-
391
- const VARCHAR_ID = "1043";
392
- const TEXT_ID = "25";
393
- const CHAR_ID = "18";
394
- const BPCHAR_ID = "1042"; // Get the list of columns enums are defined for
395
-
396
- const enumColumns = enumConstraints.map(con => {
397
- const attr = enumTableColumns.find(attr => attr.num === con.keyAttributeNums[0]);
398
-
399
- if (!attr) {
400
- throw new Error(`Enum table "${klass.namespaceName}"."${klass.name}" enum column '${con.keyAttributeNums[0]}' couldn't be found`);
401
- }
402
-
403
- if (attr.typeId !== VARCHAR_ID && attr.typeId !== TEXT_ID && attr.typeId !== CHAR_ID && attr.typeId !== BPCHAR_ID) {
404
- throw new Error(`Enum table "${klass.namespaceName}"."${klass.name}" enum column '${attr.name}' must be 'text', 'char' or 'varchar' (actual type OID: ${attr.typeId})`);
405
- }
406
-
407
- return attr;
408
- }); // Load data from the table.
409
-
410
- const query = pgSql.compile(pgSql.fragment`select ${pgSql.join(enumColumns.map(col => pgSql.identifier(col.name)), ", ")}${descriptionColumn ? pgSql.fragment`, ${pgSql.identifier(descriptionColumn.name)}` : pgSql.blank} from ${pgSql.identifier(klass.namespaceName, klass.name)};`);
501
+ const query = pgSql.compile(pgSql.fragment`select ${pgSql.join(enumTableColumns.map(col => pgSql.identifier(col.name)), ", ")} from ${pgSql.identifier(klass.namespaceName, klass.name)};`);
411
502
  let allData;
412
503
 
413
504
  try {
@@ -425,7 +516,10 @@ var PgIntrospectionPlugin = async function PgIntrospectionPlugin(builder, {
425
516
  } = await pgClient.query("select user;");
426
517
  role = user;
427
518
  } catch (e) {
428
- /* ignore */
519
+ /*
520
+ * Ignore; this is likely a 25P02 (transaction aborted)
521
+ * error caused by the statement above failing.
522
+ */
429
523
  }
430
524
 
431
525
  throw new Error(`Introspection could not read from enum table "${klass.namespaceName}"."${klass.name}", perhaps you need to grant access:
@@ -435,88 +529,9 @@ var PgIntrospectionPlugin = async function PgIntrospectionPlugin(builder, {
435
529
 
436
530
  Original error: ${e.message}
437
531
  `);
438
- } // Assert there's at least one value
439
-
440
-
441
- enumConstraints.forEach(constraint => {
442
- const col = enumColumns.find(col => col.num === constraint.keyAttributeNums[0]);
443
-
444
- if (!col) {
445
- // Should never happen
446
- throw new Error("Graphile Engine error - could not find column for enum constraint");
447
- }
532
+ }
448
533
 
449
- const data = allData.filter(row => row[col.name] != null);
450
-
451
- if (data.length < 1) {
452
- throw new Error(`Enum table "${klass.namespaceName}"."${klass.name}" contains no entries for enum constraint '${constraint.name}'.`);
453
- } // Create fake enum type
454
-
455
-
456
- const constraintIdent = constraint.type === "p" ? "" : `_${constraint.name}`;
457
- const enumTypeArray = {
458
- kind: "type",
459
- id: `FAKE_ENUM_${klass.namespaceName}_${klass.name}${constraintIdent}_list`,
460
- name: `_${klass.name}${constraintIdent}`,
461
- description: null,
462
- tags: {},
463
- namespaceId: klass.namespaceId,
464
- namespaceName: klass.namespaceName,
465
- type: "b",
466
- category: "A",
467
- domainIsNotNull: null,
468
- arrayItemTypeId: null,
469
- typeLength: -1,
470
- isPgArray: true,
471
- classId: null,
472
- domainBaseTypeId: null,
473
- domainTypeModifier: null,
474
- domainHasDefault: false,
475
- enumVariants: null,
476
- enumDescriptions: null,
477
- rangeSubTypeId: null
478
- };
479
- const enumType = {
480
- kind: "type",
481
- id: `FAKE_ENUM_${klass.namespaceName}_${klass.name}${constraintIdent}`,
482
- name: `${klass.name}${constraintIdent}`,
483
- description: klass.description,
484
- tags: { ...klass.tags,
485
- ...constraint.tags
486
- },
487
- namespaceId: klass.namespaceId,
488
- namespaceName: klass.namespaceName,
489
- type: "e",
490
- category: "E",
491
- domainIsNotNull: null,
492
- arrayItemTypeId: enumTypeArray.id,
493
- typeLength: 4,
494
- // ???
495
- isPgArray: false,
496
- classId: null,
497
- domainBaseTypeId: null,
498
- domainTypeModifier: null,
499
- domainHasDefault: false,
500
- enumVariants: data.map(r => r[col.name]),
501
- enumDescriptions: descriptionColumn ? data.map(r => r[descriptionColumn.name]) : null,
502
- // TODO: enumDescriptions
503
- rangeSubTypeId: null
504
- };
505
- result.type.push(enumType, enumTypeArray); // Change type of all attributes that reference this table to
506
- // reference this enum type
507
-
508
- result.constraint.forEach(c => {
509
- if (c.type === "f" && c.foreignClassId === klass.id && c.foreignKeyAttributeNums.length === 1 && c.foreignKeyAttributeNums[0] === col.num) {
510
- // Get the attribute
511
- const fkattr = result.attribute.find(attr => attr.classId === c.classId && attr.num === c.keyAttributeNums[0]);
512
-
513
- if (fkattr) {
514
- // Override the detected type to pretend to be our enum
515
- fkattr.typeId = enumType.id;
516
- }
517
- }
518
- });
519
- });
534
+ klass._internalEnumData = allData;
520
535
  }
521
536
  }));
522
537
  ["namespace", "class", "attribute", "type", "constraint", "procedure", "extension", "index"].forEach(k => {
@@ -586,7 +601,7 @@ Original error: ${e.message}
586
601
  return;
587
602
  }
588
603
 
589
- throw new Error(`Could not look up '${newAttr}' by '${lookupAttr}' on '${JSON.stringify(entry)}'`);
604
+ throw new Error(`Could not look up '${newAttr}' by '${lookupAttr}' (= '${key}') on '${JSON.stringify(entry)}'`);
590
605
  }
591
606
 
592
607
  entry[newAttr] = result;
@@ -595,7 +610,7 @@ Original error: ${e.message}
595
610
  };
596
611
 
597
612
  const augment = introspectionResults => {
598
- [pgAugmentIntrospectionResults, smartCommentConstraints].forEach(fn => fn ? fn(introspectionResults) : null);
613
+ [pgAugmentIntrospectionResults, smartCommentConstraints, enumTables].forEach(fn => fn ? fn(introspectionResults) : null);
599
614
  };
600
615
 
601
616
  augment(introspectionResultsByKind);
@@ -754,9 +769,7 @@ Original error: ${e.message}
754
769
  }
755
770
 
756
771
  _handleClientError(e) {
757
- // Client is already cleaned up
758
- this.client = null;
759
- this._reallyReleaseClient = null;
772
+ this._releaseClient(false);
760
773
 
761
774
  this._reconnect(e);
762
775
  }
@@ -816,28 +829,38 @@ Original error: ${e.message}
816
829
 
817
830
  async stop() {
818
831
  this.stopped = true;
832
+
833
+ this._handleChange.cancel();
834
+
819
835
  await this._releaseClient();
820
836
  }
837
+ /**
838
+ * Only pass `false` to this function if you know the client is going to be
839
+ * terminated; otherwise we risk leaving listeners running.
840
+ */
821
841
 
822
- async _releaseClient() {
823
- // $FlowFixMe
824
- this._handleChange.cancel();
825
842
 
843
+ async _releaseClient(clientIsStillViable = true) {
844
+ // $FlowFixMe
826
845
  const pgClient = this.client;
827
846
  const reallyReleaseClient = this._reallyReleaseClient;
828
847
  this.client = null;
829
848
  this._reallyReleaseClient = null;
830
849
 
831
850
  if (pgClient) {
832
- pgClient.query("unlisten postgraphile_watch").catch(e => {
833
- debug(`Error occurred trying to unlisten watch: ${e}`);
834
- });
851
+ // Don't attempt to run a query after a client has errored.
852
+ if (clientIsStillViable) {
853
+ pgClient.query("unlisten postgraphile_watch").catch(e => {
854
+ debug(`Error occurred trying to unlisten watch: ${e}`);
855
+ });
856
+ }
857
+
835
858
  pgClient.removeListener("notification", this._listener);
836
859
  pgClient.removeListener("error", this._handleClientError);
860
+ }
837
861
 
838
- if (reallyReleaseClient) {
839
- await reallyReleaseClient();
840
- }
862
+ if (reallyReleaseClient) {
863
+ await reallyReleaseClient();
841
864
  }
842
865
  }
843
866