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.
- package/README.md +1 -0
- package/node8plus/plugins/PageInfoStartEndCursor.js +2 -2
- package/node8plus/plugins/PageInfoStartEndCursor.js.flow +6 -2
- package/node8plus/plugins/PageInfoStartEndCursor.js.map +1 -1
- package/node8plus/plugins/PgAllRows.js +1 -1
- package/node8plus/plugins/PgAllRows.js.flow +6 -3
- package/node8plus/plugins/PgAllRows.js.map +1 -1
- package/node8plus/plugins/PgBackwardRelationPlugin.js +2 -2
- package/node8plus/plugins/PgBackwardRelationPlugin.js.flow +8 -2
- package/node8plus/plugins/PgBackwardRelationPlugin.js.map +1 -1
- package/node8plus/plugins/PgBasicsPlugin.js +1 -1
- package/node8plus/plugins/PgBasicsPlugin.js.flow +1 -1
- package/node8plus/plugins/PgBasicsPlugin.js.map +1 -1
- package/node8plus/plugins/PgConditionComputedColumnPlugin.js +1 -1
- package/node8plus/plugins/PgConditionComputedColumnPlugin.js.flow +4 -1
- package/node8plus/plugins/PgConditionComputedColumnPlugin.js.map +1 -1
- package/node8plus/plugins/PgConnectionArgCondition.js +3 -3
- package/node8plus/plugins/PgConnectionArgCondition.js.flow +11 -3
- package/node8plus/plugins/PgConnectionArgCondition.js.map +1 -1
- package/node8plus/plugins/PgConnectionArgFirstLastBeforeAfter.js +5 -5
- package/node8plus/plugins/PgConnectionArgFirstLastBeforeAfter.js.flow +20 -7
- package/node8plus/plugins/PgConnectionArgFirstLastBeforeAfter.js.map +1 -1
- package/node8plus/plugins/PgConnectionArgOrderBy.js +2 -2
- package/node8plus/plugins/PgConnectionArgOrderBy.js.flow +8 -2
- package/node8plus/plugins/PgConnectionArgOrderBy.js.map +1 -1
- package/node8plus/plugins/PgConnectionTotalCount.js +1 -1
- package/node8plus/plugins/PgConnectionTotalCount.js.flow +4 -1
- package/node8plus/plugins/PgConnectionTotalCount.js.map +1 -1
- package/node8plus/plugins/PgForwardRelationPlugin.js +1 -1
- package/node8plus/plugins/PgForwardRelationPlugin.js.flow +4 -1
- package/node8plus/plugins/PgForwardRelationPlugin.js.map +1 -1
- package/node8plus/plugins/PgIntrospectionPlugin.d.ts +1 -0
- package/node8plus/plugins/PgIntrospectionPlugin.js +182 -159
- package/node8plus/plugins/PgIntrospectionPlugin.js.flow +218 -198
- package/node8plus/plugins/PgIntrospectionPlugin.js.map +1 -1
- package/node8plus/plugins/PgJWTPlugin.js +1 -1
- package/node8plus/plugins/PgJWTPlugin.js.flow +3 -1
- package/node8plus/plugins/PgJWTPlugin.js.map +1 -1
- package/node8plus/plugins/PgMutationCreatePlugin.js +7 -7
- package/node8plus/plugins/PgMutationCreatePlugin.js.flow +26 -7
- package/node8plus/plugins/PgMutationCreatePlugin.js.map +1 -1
- package/node8plus/plugins/PgMutationPayloadEdgePlugin.js +2 -2
- package/node8plus/plugins/PgMutationPayloadEdgePlugin.js.flow +8 -2
- package/node8plus/plugins/PgMutationPayloadEdgePlugin.js.map +1 -1
- package/node8plus/plugins/PgMutationUpdateDeletePlugin.js +11 -11
- package/node8plus/plugins/PgMutationUpdateDeletePlugin.js.flow +40 -11
- package/node8plus/plugins/PgMutationUpdateDeletePlugin.js.map +1 -1
- package/node8plus/plugins/PgRecordFunctionConnectionPlugin.js +6 -6
- package/node8plus/plugins/PgRecordFunctionConnectionPlugin.js.flow +24 -6
- package/node8plus/plugins/PgRecordFunctionConnectionPlugin.js.map +1 -1
- package/node8plus/plugins/PgRecordReturnTypesPlugin.js +1 -1
- package/node8plus/plugins/PgRecordReturnTypesPlugin.js.flow +6 -3
- package/node8plus/plugins/PgRecordReturnTypesPlugin.js.map +1 -1
- package/node8plus/plugins/PgRowNode.js +2 -2
- package/node8plus/plugins/PgRowNode.js.flow +8 -2
- package/node8plus/plugins/PgRowNode.js.map +1 -1
- package/node8plus/plugins/PgScalarFunctionConnectionPlugin.js +6 -6
- package/node8plus/plugins/PgScalarFunctionConnectionPlugin.js.flow +24 -6
- package/node8plus/plugins/PgScalarFunctionConnectionPlugin.js.map +1 -1
- package/node8plus/plugins/PgTablesPlugin.js +13 -13
- package/node8plus/plugins/PgTablesPlugin.js.flow +56 -20
- package/node8plus/plugins/PgTablesPlugin.js.map +1 -1
- package/node8plus/plugins/PgTypesPlugin.js +62 -78
- package/node8plus/plugins/PgTypesPlugin.js.flow +193 -76
- package/node8plus/plugins/PgTypesPlugin.js.map +1 -1
- package/node8plus/plugins/makeProcField.js +3 -3
- package/node8plus/plugins/makeProcField.js.flow +16 -7
- package/node8plus/plugins/makeProcField.js.map +1 -1
- package/node8plus/plugins/viaTemporaryTable.js +1 -1
- package/node8plus/plugins/viaTemporaryTable.js.flow +8 -4
- package/node8plus/plugins/viaTemporaryTable.js.map +1 -1
- package/node8plus/postgresInterval.js +12 -33
- package/node8plus/postgresInterval.js.flow +22 -51
- package/node8plus/postgresInterval.js.map +1 -1
- package/package.json +9 -9
- package/res/watch-fixtures.sql +1 -1
|
@@ -81,6 +81,9 @@ export type PgClass = {
|
|
|
81
81
|
aclUpdatable: boolean,
|
|
82
82
|
aclDeletable: boolean,
|
|
83
83
|
canUseAsterisk: boolean,
|
|
84
|
+
|
|
85
|
+
// eslint-disable-next-line flowtype/no-weak-types
|
|
86
|
+
_internalEnumData?: any[], // This is Graphile internal, do not use this.
|
|
84
87
|
};
|
|
85
88
|
|
|
86
89
|
export type PgType = {
|
|
@@ -109,6 +112,7 @@ export type PgType = {
|
|
|
109
112
|
enumDescriptions: ?(string[]),
|
|
110
113
|
rangeSubTypeId: ?string,
|
|
111
114
|
tags: { [string]: string },
|
|
115
|
+
isFake: ?boolean,
|
|
112
116
|
};
|
|
113
117
|
|
|
114
118
|
export type PgAttribute = {
|
|
@@ -357,7 +361,7 @@ function smartCommentConstraints(introspectionResults) {
|
|
|
357
361
|
}
|
|
358
362
|
if (klass.tags.unique) {
|
|
359
363
|
if (Array.isArray(klass.tags.unique)) {
|
|
360
|
-
klass.tags.unique.forEach(key => addKey(key
|
|
364
|
+
klass.tags.unique.forEach(key => addKey(key));
|
|
361
365
|
} else {
|
|
362
366
|
addKey(klass.tags.unique);
|
|
363
367
|
}
|
|
@@ -465,6 +469,165 @@ function smartCommentConstraints(introspectionResults) {
|
|
|
465
469
|
});
|
|
466
470
|
}
|
|
467
471
|
|
|
472
|
+
function isEnumConstraint(
|
|
473
|
+
klass: PgClass,
|
|
474
|
+
con: PgConstraint,
|
|
475
|
+
isEnumTable: boolean
|
|
476
|
+
) {
|
|
477
|
+
if (con.classId === klass.id) {
|
|
478
|
+
const isPrimaryKey = con.type === "p";
|
|
479
|
+
const isUniqueConstraint = con.type === "u";
|
|
480
|
+
if (isPrimaryKey || isUniqueConstraint) {
|
|
481
|
+
const isExplicitEnumConstraint =
|
|
482
|
+
con.tags.enum === true || typeof con.tags.enum === "string";
|
|
483
|
+
const isPrimaryKeyOfEnumTableConstraint = con.type === "p" && isEnumTable;
|
|
484
|
+
if (isExplicitEnumConstraint || isPrimaryKeyOfEnumTableConstraint) {
|
|
485
|
+
const hasExactlyOneColumn = con.keyAttributeNums.length === 1;
|
|
486
|
+
if (!hasExactlyOneColumn) {
|
|
487
|
+
throw new Error(
|
|
488
|
+
`Enum table "${klass.namespaceName}"."${klass.name}" enum constraint '${con.name}' is composite; it should have exactly one column (found: ${con.keyAttributeNums.length})`
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function enumTables(introspectionResults) {
|
|
499
|
+
introspectionResults.class.map(async klass => {
|
|
500
|
+
const isEnumTable =
|
|
501
|
+
klass.tags.enum === true || typeof klass.tags.enum === "string";
|
|
502
|
+
|
|
503
|
+
if (isEnumTable) {
|
|
504
|
+
// Prevent the table being recognised as a table
|
|
505
|
+
// eslint-disable-next-line require-atomic-updates
|
|
506
|
+
klass.tags.omit = true;
|
|
507
|
+
// eslint-disable-next-line require-atomic-updates
|
|
508
|
+
klass.isSelectable = false;
|
|
509
|
+
// eslint-disable-next-line require-atomic-updates
|
|
510
|
+
klass.isInsertable = false;
|
|
511
|
+
// eslint-disable-next-line require-atomic-updates
|
|
512
|
+
klass.isUpdatable = false;
|
|
513
|
+
// eslint-disable-next-line require-atomic-updates
|
|
514
|
+
klass.isDeletable = false;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// By this point, even views should have "fake" constraints we can use
|
|
518
|
+
// (e.g. `@primaryKey`)
|
|
519
|
+
const enumConstraints = introspectionResults.constraint.filter(con =>
|
|
520
|
+
isEnumConstraint(klass, con, isEnumTable)
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
// Get all the columns
|
|
524
|
+
const enumTableColumns = introspectionResults.attribute.filter(
|
|
525
|
+
attr => attr.classId === klass.id
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
// Get description column
|
|
529
|
+
const descriptionColumn = enumTableColumns.find(
|
|
530
|
+
attr => attr.name === "description" || attr.tags.enumDescription
|
|
531
|
+
);
|
|
532
|
+
const allData = klass._internalEnumData || [];
|
|
533
|
+
|
|
534
|
+
enumConstraints.forEach(constraint => {
|
|
535
|
+
const col = enumTableColumns.find(
|
|
536
|
+
col => col.num === constraint.keyAttributeNums[0]
|
|
537
|
+
);
|
|
538
|
+
if (!col) {
|
|
539
|
+
// Should never happen
|
|
540
|
+
throw new Error(
|
|
541
|
+
"Graphile Engine error - could not find column for enum constraint"
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
const data = allData.filter(row => row[col.name] != null);
|
|
545
|
+
if (data.length < 1) {
|
|
546
|
+
throw new Error(
|
|
547
|
+
`Enum table "${klass.namespaceName}"."${klass.name}" contains no entries for enum constraint '${constraint.name}'.`
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Create fake enum type
|
|
552
|
+
const constraintIdent =
|
|
553
|
+
constraint.type === "p" ? "" : `_${constraint.name}`;
|
|
554
|
+
const enumTypeArray = {
|
|
555
|
+
kind: "type",
|
|
556
|
+
isFake: true,
|
|
557
|
+
id: `FAKE_ENUM_${klass.namespaceName}_${klass.name}${constraintIdent}_list`,
|
|
558
|
+
name: `_${klass.name}${constraintIdent}`,
|
|
559
|
+
description: null,
|
|
560
|
+
tags: {},
|
|
561
|
+
namespaceId: klass.namespaceId,
|
|
562
|
+
namespaceName: klass.namespaceName,
|
|
563
|
+
type: "b",
|
|
564
|
+
category: "A",
|
|
565
|
+
domainIsNotNull: null,
|
|
566
|
+
arrayItemTypeId: null,
|
|
567
|
+
typeLength: -1,
|
|
568
|
+
isPgArray: true,
|
|
569
|
+
classId: null,
|
|
570
|
+
domainBaseTypeId: null,
|
|
571
|
+
domainTypeModifier: null,
|
|
572
|
+
domainHasDefault: false,
|
|
573
|
+
enumVariants: null,
|
|
574
|
+
enumDescriptions: null,
|
|
575
|
+
rangeSubTypeId: null,
|
|
576
|
+
};
|
|
577
|
+
const enumType = {
|
|
578
|
+
kind: "type",
|
|
579
|
+
isFake: true,
|
|
580
|
+
id: `FAKE_ENUM_${klass.namespaceName}_${klass.name}${constraintIdent}`,
|
|
581
|
+
name: `${klass.name}${constraintIdent}`,
|
|
582
|
+
description: klass.description,
|
|
583
|
+
tags: { ...klass.tags, ...constraint.tags },
|
|
584
|
+
namespaceId: klass.namespaceId,
|
|
585
|
+
namespaceName: klass.namespaceName,
|
|
586
|
+
type: "e",
|
|
587
|
+
category: "E",
|
|
588
|
+
domainIsNotNull: null,
|
|
589
|
+
arrayItemTypeId: enumTypeArray.id,
|
|
590
|
+
typeLength: 4, // ???
|
|
591
|
+
isPgArray: false,
|
|
592
|
+
classId: null,
|
|
593
|
+
domainBaseTypeId: null,
|
|
594
|
+
domainTypeModifier: null,
|
|
595
|
+
domainHasDefault: false,
|
|
596
|
+
enumVariants: data.map(r => r[col.name]),
|
|
597
|
+
enumDescriptions: descriptionColumn
|
|
598
|
+
? data.map(r => r[descriptionColumn.name])
|
|
599
|
+
: null,
|
|
600
|
+
// TODO: enumDescriptions
|
|
601
|
+
rangeSubTypeId: null,
|
|
602
|
+
};
|
|
603
|
+
introspectionResults.type.push(enumType, enumTypeArray);
|
|
604
|
+
introspectionResults.typeById[enumType.id] = enumType;
|
|
605
|
+
introspectionResults.typeById[enumTypeArray.id] = enumTypeArray;
|
|
606
|
+
|
|
607
|
+
// Change type of all attributes that reference this table to
|
|
608
|
+
// reference this enum type
|
|
609
|
+
introspectionResults.constraint.forEach(c => {
|
|
610
|
+
if (
|
|
611
|
+
c.type === "f" &&
|
|
612
|
+
c.foreignClassId === klass.id &&
|
|
613
|
+
c.foreignKeyAttributeNums.length === 1 &&
|
|
614
|
+
c.foreignKeyAttributeNums[0] === col.num
|
|
615
|
+
) {
|
|
616
|
+
// Get the attribute
|
|
617
|
+
const fkattr = introspectionResults.attribute.find(
|
|
618
|
+
attr =>
|
|
619
|
+
attr.classId === c.classId && attr.num === c.keyAttributeNums[0]
|
|
620
|
+
);
|
|
621
|
+
if (fkattr) {
|
|
622
|
+
// Override the detected type to pretend to be our enum
|
|
623
|
+
fkattr.typeId = enumType.id;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
|
|
468
631
|
/* The argument to this must not contain cyclic references! */
|
|
469
632
|
const deepClone = value => {
|
|
470
633
|
if (Array.isArray(value)) {
|
|
@@ -570,108 +733,46 @@ export default (async function PgIntrospectionPlugin(
|
|
|
570
733
|
extensionConfigurationClassIds.indexOf(klass.id) >= 0;
|
|
571
734
|
});
|
|
572
735
|
|
|
736
|
+
// Assert the columns are text
|
|
737
|
+
const VARCHAR_ID = "1043";
|
|
738
|
+
const TEXT_ID = "25";
|
|
739
|
+
const CHAR_ID = "18";
|
|
740
|
+
const BPCHAR_ID = "1042";
|
|
741
|
+
|
|
742
|
+
const VALID_TYPE_IDS = [VARCHAR_ID, TEXT_ID, CHAR_ID, BPCHAR_ID];
|
|
743
|
+
|
|
573
744
|
await Promise.all(
|
|
574
745
|
result.class.map(async klass => {
|
|
746
|
+
if (!schemas.includes(klass.namespaceName)) {
|
|
747
|
+
// Only support enums in public tables/views
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
575
750
|
const isEnumTable =
|
|
576
751
|
klass.tags.enum === true || typeof klass.tags.enum === "string";
|
|
577
752
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
klass
|
|
582
|
-
// eslint-disable-next-line require-atomic-updates
|
|
583
|
-
klass.isSelectable = false;
|
|
584
|
-
// eslint-disable-next-line require-atomic-updates
|
|
585
|
-
klass.isInsertable = false;
|
|
586
|
-
// eslint-disable-next-line require-atomic-updates
|
|
587
|
-
klass.isUpdatable = false;
|
|
588
|
-
// eslint-disable-next-line require-atomic-updates
|
|
589
|
-
klass.isDeletable = false;
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
const enumConstraints = result.constraint.filter(
|
|
593
|
-
(con: PgConstraint) => {
|
|
594
|
-
if (con.classId === klass.id) {
|
|
595
|
-
const isPrimaryKey = con.type === "p";
|
|
596
|
-
const isUniqueConstraint = con.type === "u";
|
|
597
|
-
if (isPrimaryKey || isUniqueConstraint) {
|
|
598
|
-
const isExplicitEnumConstraint =
|
|
599
|
-
con.tags.enum === true ||
|
|
600
|
-
typeof con.tags.enum === "string";
|
|
601
|
-
const isPrimaryKeyOfEnumTableConstraint =
|
|
602
|
-
con.type === "p" && isEnumTable;
|
|
603
|
-
if (
|
|
604
|
-
isExplicitEnumConstraint ||
|
|
605
|
-
isPrimaryKeyOfEnumTableConstraint
|
|
606
|
-
) {
|
|
607
|
-
const hasExactlyOneColumn =
|
|
608
|
-
con.keyAttributeNums.length === 1;
|
|
609
|
-
if (!hasExactlyOneColumn) {
|
|
610
|
-
throw new Error(
|
|
611
|
-
`Enum table "${klass.namespaceName}"."${klass.name}" enum constraint '${con.name}' is composite; it should have exactly one column (found: ${con.keyAttributeNums.length})`
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
return true;
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
return false;
|
|
619
|
-
}
|
|
753
|
+
// NOTE: this only matches on tables (not views, since they don't
|
|
754
|
+
// have constraints), which is why we repeat the isEnumTable check below.
|
|
755
|
+
const hasEnumConstraints = result.constraint.some(con =>
|
|
756
|
+
isEnumConstraint(klass, con, isEnumTable)
|
|
620
757
|
);
|
|
621
|
-
if (
|
|
622
|
-
// Get
|
|
758
|
+
if (isEnumTable || hasEnumConstraints) {
|
|
759
|
+
// Get the list of columns enums are defined for
|
|
623
760
|
const enumTableColumns = result.attribute
|
|
624
|
-
.filter(
|
|
761
|
+
.filter(
|
|
762
|
+
attr =>
|
|
763
|
+
attr.classId === klass.id &&
|
|
764
|
+
VALID_TYPE_IDS.includes(attr.typeId)
|
|
765
|
+
)
|
|
625
766
|
.sort((a, z) => a.num - z.num);
|
|
626
767
|
|
|
627
|
-
//
|
|
628
|
-
const descriptionColumn = enumTableColumns.find(
|
|
629
|
-
attr =>
|
|
630
|
-
attr.name === "description" || attr.tags.enumDescription
|
|
631
|
-
);
|
|
632
|
-
|
|
633
|
-
// Assert the columns are text
|
|
634
|
-
const VARCHAR_ID = "1043";
|
|
635
|
-
const TEXT_ID = "25";
|
|
636
|
-
const CHAR_ID = "18";
|
|
637
|
-
const BPCHAR_ID = "1042";
|
|
638
|
-
|
|
639
|
-
// Get the list of columns enums are defined for
|
|
640
|
-
const enumColumns = enumConstraints.map(con => {
|
|
641
|
-
const attr = enumTableColumns.find(
|
|
642
|
-
attr => attr.num === con.keyAttributeNums[0]
|
|
643
|
-
);
|
|
644
|
-
if (!attr) {
|
|
645
|
-
throw new Error(
|
|
646
|
-
`Enum table "${klass.namespaceName}"."${klass.name}" enum column '${con.keyAttributeNums[0]}' couldn't be found`
|
|
647
|
-
);
|
|
648
|
-
}
|
|
649
|
-
if (
|
|
650
|
-
attr.typeId !== VARCHAR_ID &&
|
|
651
|
-
attr.typeId !== TEXT_ID &&
|
|
652
|
-
attr.typeId !== CHAR_ID &&
|
|
653
|
-
attr.typeId !== BPCHAR_ID
|
|
654
|
-
) {
|
|
655
|
-
throw new Error(
|
|
656
|
-
`Enum table "${klass.namespaceName}"."${klass.name}" enum column '${attr.name}' must be 'text', 'char' or 'varchar' (actual type OID: ${attr.typeId})`
|
|
657
|
-
);
|
|
658
|
-
}
|
|
659
|
-
return attr;
|
|
660
|
-
});
|
|
661
|
-
|
|
662
|
-
// Load data from the table.
|
|
768
|
+
// Load data from the table/view.
|
|
663
769
|
const query = pgSql.compile(
|
|
664
770
|
pgSql.fragment`select ${pgSql.join(
|
|
665
|
-
|
|
771
|
+
enumTableColumns.map(col => pgSql.identifier(col.name)),
|
|
666
772
|
", "
|
|
667
|
-
)}${
|
|
668
|
-
descriptionColumn
|
|
669
|
-
? pgSql.fragment`, ${pgSql.identifier(
|
|
670
|
-
descriptionColumn.name
|
|
671
|
-
)}`
|
|
672
|
-
: pgSql.blank
|
|
673
|
-
} from ${pgSql.identifier(klass.namespaceName, klass.name)};`
|
|
773
|
+
)} from ${pgSql.identifier(klass.namespaceName, klass.name)};`
|
|
674
774
|
);
|
|
775
|
+
|
|
675
776
|
let allData;
|
|
676
777
|
try {
|
|
677
778
|
({ rows: allData } = await pgClient.query(query));
|
|
@@ -683,7 +784,10 @@ export default (async function PgIntrospectionPlugin(
|
|
|
683
784
|
} = await pgClient.query("select user;");
|
|
684
785
|
role = user;
|
|
685
786
|
} catch (e) {
|
|
686
|
-
/*
|
|
787
|
+
/*
|
|
788
|
+
* Ignore; this is likely a 25P02 (transaction aborted)
|
|
789
|
+
* error caused by the statement above failing.
|
|
790
|
+
*/
|
|
687
791
|
}
|
|
688
792
|
throw new Error(`Introspection could not read from enum table "${klass.namespaceName}"."${klass.name}", perhaps you need to grant access:
|
|
689
793
|
|
|
@@ -694,98 +798,7 @@ Original error: ${e.message}
|
|
|
694
798
|
`);
|
|
695
799
|
}
|
|
696
800
|
|
|
697
|
-
|
|
698
|
-
enumConstraints.forEach(constraint => {
|
|
699
|
-
const col = enumColumns.find(
|
|
700
|
-
col => col.num === constraint.keyAttributeNums[0]
|
|
701
|
-
);
|
|
702
|
-
if (!col) {
|
|
703
|
-
// Should never happen
|
|
704
|
-
throw new Error(
|
|
705
|
-
"Graphile Engine error - could not find column for enum constraint"
|
|
706
|
-
);
|
|
707
|
-
}
|
|
708
|
-
const data = allData.filter(row => row[col.name] != null);
|
|
709
|
-
if (data.length < 1) {
|
|
710
|
-
throw new Error(
|
|
711
|
-
`Enum table "${klass.namespaceName}"."${klass.name}" contains no entries for enum constraint '${constraint.name}'.`
|
|
712
|
-
);
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
// Create fake enum type
|
|
716
|
-
const constraintIdent =
|
|
717
|
-
constraint.type === "p" ? "" : `_${constraint.name}`;
|
|
718
|
-
const enumTypeArray = {
|
|
719
|
-
kind: "type",
|
|
720
|
-
id: `FAKE_ENUM_${klass.namespaceName}_${klass.name}${constraintIdent}_list`,
|
|
721
|
-
name: `_${klass.name}${constraintIdent}`,
|
|
722
|
-
description: null,
|
|
723
|
-
tags: {},
|
|
724
|
-
namespaceId: klass.namespaceId,
|
|
725
|
-
namespaceName: klass.namespaceName,
|
|
726
|
-
type: "b",
|
|
727
|
-
category: "A",
|
|
728
|
-
domainIsNotNull: null,
|
|
729
|
-
arrayItemTypeId: null,
|
|
730
|
-
typeLength: -1,
|
|
731
|
-
isPgArray: true,
|
|
732
|
-
classId: null,
|
|
733
|
-
domainBaseTypeId: null,
|
|
734
|
-
domainTypeModifier: null,
|
|
735
|
-
domainHasDefault: false,
|
|
736
|
-
enumVariants: null,
|
|
737
|
-
enumDescriptions: null,
|
|
738
|
-
rangeSubTypeId: null,
|
|
739
|
-
};
|
|
740
|
-
const enumType = {
|
|
741
|
-
kind: "type",
|
|
742
|
-
id: `FAKE_ENUM_${klass.namespaceName}_${klass.name}${constraintIdent}`,
|
|
743
|
-
name: `${klass.name}${constraintIdent}`,
|
|
744
|
-
description: klass.description,
|
|
745
|
-
tags: { ...klass.tags, ...constraint.tags },
|
|
746
|
-
namespaceId: klass.namespaceId,
|
|
747
|
-
namespaceName: klass.namespaceName,
|
|
748
|
-
type: "e",
|
|
749
|
-
category: "E",
|
|
750
|
-
domainIsNotNull: null,
|
|
751
|
-
arrayItemTypeId: enumTypeArray.id,
|
|
752
|
-
typeLength: 4, // ???
|
|
753
|
-
isPgArray: false,
|
|
754
|
-
classId: null,
|
|
755
|
-
domainBaseTypeId: null,
|
|
756
|
-
domainTypeModifier: null,
|
|
757
|
-
domainHasDefault: false,
|
|
758
|
-
enumVariants: data.map(r => r[col.name]),
|
|
759
|
-
enumDescriptions: descriptionColumn
|
|
760
|
-
? data.map(r => r[descriptionColumn.name])
|
|
761
|
-
: null,
|
|
762
|
-
// TODO: enumDescriptions
|
|
763
|
-
rangeSubTypeId: null,
|
|
764
|
-
};
|
|
765
|
-
result.type.push(enumType, enumTypeArray);
|
|
766
|
-
|
|
767
|
-
// Change type of all attributes that reference this table to
|
|
768
|
-
// reference this enum type
|
|
769
|
-
result.constraint.forEach(c => {
|
|
770
|
-
if (
|
|
771
|
-
c.type === "f" &&
|
|
772
|
-
c.foreignClassId === klass.id &&
|
|
773
|
-
c.foreignKeyAttributeNums.length === 1 &&
|
|
774
|
-
c.foreignKeyAttributeNums[0] === col.num
|
|
775
|
-
) {
|
|
776
|
-
// Get the attribute
|
|
777
|
-
const fkattr = result.attribute.find(
|
|
778
|
-
attr =>
|
|
779
|
-
attr.classId === c.classId &&
|
|
780
|
-
attr.num === c.keyAttributeNums[0]
|
|
781
|
-
);
|
|
782
|
-
if (fkattr) {
|
|
783
|
-
// Override the detected type to pretend to be our enum
|
|
784
|
-
fkattr.typeId = enumType.id;
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
});
|
|
788
|
-
});
|
|
801
|
+
klass._internalEnumData = allData;
|
|
789
802
|
}
|
|
790
803
|
})
|
|
791
804
|
);
|
|
@@ -891,7 +904,7 @@ Original error: ${e.message}
|
|
|
891
904
|
return;
|
|
892
905
|
}
|
|
893
906
|
throw new Error(
|
|
894
|
-
`Could not look up '${newAttr}' by '${lookupAttr}' on '${JSON.stringify(
|
|
907
|
+
`Could not look up '${newAttr}' by '${lookupAttr}' (= '${key}') on '${JSON.stringify(
|
|
895
908
|
entry
|
|
896
909
|
)}'`
|
|
897
910
|
);
|
|
@@ -902,9 +915,11 @@ Original error: ${e.message}
|
|
|
902
915
|
};
|
|
903
916
|
|
|
904
917
|
const augment = introspectionResults => {
|
|
905
|
-
[
|
|
906
|
-
|
|
907
|
-
|
|
918
|
+
[
|
|
919
|
+
pgAugmentIntrospectionResults,
|
|
920
|
+
smartCommentConstraints,
|
|
921
|
+
enumTables,
|
|
922
|
+
].forEach(fn => (fn ? fn(introspectionResults) : null));
|
|
908
923
|
};
|
|
909
924
|
augment(introspectionResultsByKind);
|
|
910
925
|
|
|
@@ -1189,9 +1204,7 @@ Original error: ${e.message}
|
|
|
1189
1204
|
|
|
1190
1205
|
_handleClientError: (e: Error) => void;
|
|
1191
1206
|
_handleClientError(e) {
|
|
1192
|
-
|
|
1193
|
-
this.client = null;
|
|
1194
|
-
this._reallyReleaseClient = null;
|
|
1207
|
+
this._releaseClient(false);
|
|
1195
1208
|
this._reconnect(e);
|
|
1196
1209
|
}
|
|
1197
1210
|
async _reconnect(e) {
|
|
@@ -1250,25 +1263,32 @@ Original error: ${e.message}
|
|
|
1250
1263
|
|
|
1251
1264
|
async stop() {
|
|
1252
1265
|
this.stopped = true;
|
|
1266
|
+
this._handleChange.cancel();
|
|
1253
1267
|
await this._releaseClient();
|
|
1254
1268
|
}
|
|
1255
1269
|
|
|
1256
|
-
|
|
1270
|
+
/**
|
|
1271
|
+
* Only pass `false` to this function if you know the client is going to be
|
|
1272
|
+
* terminated; otherwise we risk leaving listeners running.
|
|
1273
|
+
*/
|
|
1274
|
+
async _releaseClient(clientIsStillViable = true) {
|
|
1257
1275
|
// $FlowFixMe
|
|
1258
|
-
this._handleChange.cancel();
|
|
1259
1276
|
const pgClient = this.client;
|
|
1260
1277
|
const reallyReleaseClient = this._reallyReleaseClient;
|
|
1261
1278
|
this.client = null;
|
|
1262
1279
|
this._reallyReleaseClient = null;
|
|
1263
1280
|
if (pgClient) {
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1281
|
+
// Don't attempt to run a query after a client has errored.
|
|
1282
|
+
if (clientIsStillViable) {
|
|
1283
|
+
pgClient.query("unlisten postgraphile_watch").catch(e => {
|
|
1284
|
+
debug(`Error occurred trying to unlisten watch: ${e}`);
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1267
1287
|
pgClient.removeListener("notification", this._listener);
|
|
1268
1288
|
pgClient.removeListener("error", this._handleClientError);
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1289
|
+
}
|
|
1290
|
+
if (reallyReleaseClient) {
|
|
1291
|
+
await reallyReleaseClient();
|
|
1272
1292
|
}
|
|
1273
1293
|
}
|
|
1274
1294
|
}
|