graphile-build-pg 4.13.0 → 4.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -13
- package/node8plus/GraphQLJSON.js +0 -11
- package/node8plus/GraphQLJSON.js.map +1 -1
- package/node8plus/PgLiveProvider.js +4 -11
- package/node8plus/PgLiveProvider.js.map +1 -1
- package/node8plus/QueryBuilder.js +42 -176
- package/node8plus/QueryBuilder.js.map +1 -1
- package/node8plus/index.js +8 -47
- package/node8plus/index.js.map +1 -1
- package/node8plus/inflections.js +9 -61
- package/node8plus/inflections.js.map +1 -1
- package/node8plus/omit.js +7 -22
- package/node8plus/omit.js.map +1 -1
- package/node8plus/parseIdentifier.js +0 -1
- package/node8plus/parseIdentifier.js.map +1 -1
- package/node8plus/pgPrepareAndRun.js +2 -14
- package/node8plus/pgPrepareAndRun.js.map +1 -1
- package/node8plus/plugins/PageInfoStartEndCursor.js +0 -4
- package/node8plus/plugins/PageInfoStartEndCursor.js.map +1 -1
- package/node8plus/plugins/PgAllRows.js +0 -29
- package/node8plus/plugins/PgAllRows.js.map +1 -1
- package/node8plus/plugins/PgBackwardRelationPlugin.js +2 -41
- package/node8plus/plugins/PgBackwardRelationPlugin.js.map +1 -1
- package/node8plus/plugins/PgBasicsPlugin.js +20 -147
- package/node8plus/plugins/PgBasicsPlugin.js.map +1 -1
- package/node8plus/plugins/PgColumnDeprecationPlugin.js +2 -5
- package/node8plus/plugins/PgColumnDeprecationPlugin.js.map +1 -1
- package/node8plus/plugins/PgColumnsPlugin.js +4 -17
- package/node8plus/plugins/PgColumnsPlugin.js.map +1 -1
- package/node8plus/plugins/PgComputedColumnsPlugin.js +4 -19
- package/node8plus/plugins/PgComputedColumnsPlugin.js.map +1 -1
- package/node8plus/plugins/PgConditionComputedColumnPlugin.js +12 -23
- package/node8plus/plugins/PgConditionComputedColumnPlugin.js.map +1 -1
- package/node8plus/plugins/PgConnectionArgCondition.js +1 -9
- package/node8plus/plugins/PgConnectionArgCondition.js.map +1 -1
- package/node8plus/plugins/PgConnectionArgFirstLastBeforeAfter.js +0 -12
- package/node8plus/plugins/PgConnectionArgFirstLastBeforeAfter.js.map +1 -1
- package/node8plus/plugins/PgConnectionArgOrderBy.js +2 -19
- package/node8plus/plugins/PgConnectionArgOrderBy.js.map +1 -1
- package/node8plus/plugins/PgConnectionArgOrderByDefaultValue.js +0 -6
- package/node8plus/plugins/PgConnectionArgOrderByDefaultValue.js.map +1 -1
- package/node8plus/plugins/PgConnectionTotalCount.js +0 -8
- package/node8plus/plugins/PgConnectionTotalCount.js.map +1 -1
- package/node8plus/plugins/PgForwardRelationPlugin.js +2 -25
- package/node8plus/plugins/PgForwardRelationPlugin.js.map +1 -1
- package/node8plus/plugins/PgIntrospectionPlugin.js +91 -180
- package/node8plus/plugins/PgIntrospectionPlugin.js.flow +30 -2
- package/node8plus/plugins/PgIntrospectionPlugin.js.map +1 -1
- package/node8plus/plugins/PgJWTPlugin.js +2 -18
- package/node8plus/plugins/PgJWTPlugin.js.map +1 -1
- package/node8plus/plugins/PgMutationCreatePlugin.js +0 -18
- package/node8plus/plugins/PgMutationCreatePlugin.js.map +1 -1
- package/node8plus/plugins/PgMutationPayloadEdgePlugin.js +4 -25
- package/node8plus/plugins/PgMutationPayloadEdgePlugin.js.map +1 -1
- package/node8plus/plugins/PgMutationProceduresPlugin.js +0 -6
- package/node8plus/plugins/PgMutationProceduresPlugin.js.map +1 -1
- package/node8plus/plugins/PgMutationUpdateDeletePlugin.js +14 -49
- package/node8plus/plugins/PgMutationUpdateDeletePlugin.js.flow +5 -2
- package/node8plus/plugins/PgMutationUpdateDeletePlugin.js.map +1 -1
- package/node8plus/plugins/PgNodeAliasPostGraphile.js +0 -6
- package/node8plus/plugins/PgNodeAliasPostGraphile.js.map +1 -1
- package/node8plus/plugins/PgOrderAllColumnsPlugin.js +0 -4
- package/node8plus/plugins/PgOrderAllColumnsPlugin.js.map +1 -1
- package/node8plus/plugins/PgOrderByPrimaryKeyPlugin.js +0 -6
- package/node8plus/plugins/PgOrderByPrimaryKeyPlugin.js.map +1 -1
- package/node8plus/plugins/PgOrderComputedColumnsPlugin.js +10 -14
- package/node8plus/plugins/PgOrderComputedColumnsPlugin.js.map +1 -1
- package/node8plus/plugins/PgQueryProceduresPlugin.js +4 -16
- package/node8plus/plugins/PgQueryProceduresPlugin.js.map +1 -1
- package/node8plus/plugins/PgRecordFunctionConnectionPlugin.js +3 -20
- package/node8plus/plugins/PgRecordFunctionConnectionPlugin.js.map +1 -1
- package/node8plus/plugins/PgRecordReturnTypesPlugin.js +11 -16
- package/node8plus/plugins/PgRecordReturnTypesPlugin.js.map +1 -1
- package/node8plus/plugins/PgRowByUniqueConstraint.js +4 -25
- package/node8plus/plugins/PgRowByUniqueConstraint.js.map +1 -1
- package/node8plus/plugins/PgRowNode.js +0 -32
- package/node8plus/plugins/PgRowNode.js.map +1 -1
- package/node8plus/plugins/PgScalarFunctionConnectionPlugin.js +3 -17
- package/node8plus/plugins/PgScalarFunctionConnectionPlugin.js.map +1 -1
- package/node8plus/plugins/PgTablesPlugin.js +7 -59
- package/node8plus/plugins/PgTablesPlugin.js.map +1 -1
- package/node8plus/plugins/PgTypesPlugin.js +140 -240
- package/node8plus/plugins/PgTypesPlugin.js.flow +76 -23
- package/node8plus/plugins/PgTypesPlugin.js.map +1 -1
- package/node8plus/plugins/addStartEndCursor.js +2 -5
- package/node8plus/plugins/addStartEndCursor.js.map +1 -1
- package/node8plus/plugins/debugSql.js +1 -16
- package/node8plus/plugins/debugSql.js.map +1 -1
- package/node8plus/plugins/introspectionQuery.js +3 -4
- package/node8plus/plugins/introspectionQuery.js.flow +11 -3
- package/node8plus/plugins/introspectionQuery.js.map +1 -1
- package/node8plus/plugins/makeProcField.js +40 -90
- package/node8plus/plugins/makeProcField.js.map +1 -1
- package/node8plus/plugins/pgField.js +2 -7
- package/node8plus/plugins/pgField.js.map +1 -1
- package/node8plus/plugins/viaTemporaryTable.js +6 -12
- package/node8plus/plugins/viaTemporaryTable.js.map +1 -1
- package/node8plus/postgresInterval.js +4 -4
- package/node8plus/postgresInterval.js.map +1 -1
- package/node8plus/queryFromResolveDataFactory.js +33 -77
- package/node8plus/queryFromResolveDataFactory.js.map +1 -1
- package/node8plus/utils.js +8 -9
- package/node8plus/utils.js.map +1 -1
- package/node8plus/withPgClient.js +4 -26
- package/node8plus/withPgClient.js.map +1 -1
- package/package.json +4 -4
|
@@ -4,38 +4,33 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = exports.PgEntityKind = void 0;
|
|
7
|
-
|
|
8
7
|
var _withPgClient = _interopRequireWildcard(require("../withPgClient"));
|
|
9
|
-
|
|
10
8
|
var _utils = require("../utils");
|
|
11
|
-
|
|
12
9
|
var _fs = require("fs");
|
|
13
|
-
|
|
14
10
|
var _debug = _interopRequireDefault(require("debug"));
|
|
15
|
-
|
|
16
11
|
var _chalk = _interopRequireDefault(require("chalk"));
|
|
17
|
-
|
|
18
12
|
var _throttle = _interopRequireDefault(require("lodash/throttle"));
|
|
19
|
-
|
|
20
13
|
var _flatMap = _interopRequireDefault(require("lodash/flatMap"));
|
|
21
|
-
|
|
22
14
|
var _introspectionQuery = require("./introspectionQuery");
|
|
23
|
-
|
|
24
15
|
var pgSql = _interopRequireWildcard(require("pg-sql2"));
|
|
25
|
-
|
|
26
16
|
var _package = require("../../package.json");
|
|
27
|
-
|
|
28
17
|
var _queryFromResolveDataFactory = _interopRequireDefault(require("../queryFromResolveDataFactory"));
|
|
29
|
-
|
|
30
18
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
31
|
-
|
|
32
19
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
33
|
-
|
|
34
20
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
35
|
-
|
|
36
21
|
const debug = (0, _debug.default)("graphile-build-pg");
|
|
37
|
-
const WATCH_FIXTURES_PATH = `${__dirname}/../../res/watch-fixtures.sql`;
|
|
22
|
+
const WATCH_FIXTURES_PATH = `${__dirname}/../../res/watch-fixtures.sql`;
|
|
38
23
|
|
|
24
|
+
// Ref: https://github.com/graphile/postgraphile/tree/master/src/postgres/introspection/object
|
|
25
|
+
|
|
26
|
+
const fakeEnumIdentifier = (namespaceName, name, identifierExtra = "", list = false) => {
|
|
27
|
+
const baseEnumIdentifier = `FAKE_ENUM_${namespaceName}_${name}${identifierExtra}`;
|
|
28
|
+
if (list) {
|
|
29
|
+
return `${baseEnumIdentifier}_list`;
|
|
30
|
+
} else {
|
|
31
|
+
return baseEnumIdentifier;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
39
34
|
function readFile(filename, encoding) {
|
|
40
35
|
return new Promise((resolve, reject) => {
|
|
41
36
|
(0, _fs.readFile)(filename, encoding, (err, res) => {
|
|
@@ -43,39 +38,31 @@ function readFile(filename, encoding) {
|
|
|
43
38
|
});
|
|
44
39
|
});
|
|
45
40
|
}
|
|
46
|
-
|
|
47
41
|
const removeQuotes = str => {
|
|
48
42
|
const trimmed = str.trim();
|
|
49
|
-
|
|
50
43
|
if (trimmed[0] === '"') {
|
|
51
44
|
if (trimmed[trimmed.length - 1] !== '"') {
|
|
52
45
|
throw new Error(`We failed to parse a quoted identifier '${str}'. Please avoid putting quotes or commas in smart comment identifiers (or file a PR to fix the parser).`);
|
|
53
46
|
}
|
|
54
|
-
|
|
55
47
|
return trimmed.slice(1, -1);
|
|
56
48
|
} else {
|
|
57
49
|
// PostgreSQL lower-cases unquoted columns, so we should too.
|
|
58
50
|
return trimmed.toLowerCase();
|
|
59
51
|
}
|
|
60
52
|
};
|
|
61
|
-
|
|
62
53
|
const parseSqlColumnArray = str => {
|
|
63
54
|
if (!str) {
|
|
64
55
|
throw new Error(`Cannot parse '${str}'`);
|
|
65
56
|
}
|
|
66
|
-
|
|
67
57
|
const parts = str.split(",");
|
|
68
58
|
return parts.map(removeQuotes);
|
|
69
59
|
};
|
|
70
|
-
|
|
71
60
|
const parseSqlColumnString = str => {
|
|
72
61
|
if (!str) {
|
|
73
62
|
throw new Error(`Cannot parse '${str}'`);
|
|
74
63
|
}
|
|
75
|
-
|
|
76
64
|
return removeQuotes(str);
|
|
77
65
|
};
|
|
78
|
-
|
|
79
66
|
function parseConstraintSpec(rawSpec) {
|
|
80
67
|
const [spec, ...tagComponents] = rawSpec.split(/\|/);
|
|
81
68
|
const parsed = (0, _utils.parseTags)(tagComponents.join("\n"));
|
|
@@ -85,51 +72,40 @@ function parseConstraintSpec(rawSpec) {
|
|
|
85
72
|
description: parsed.text
|
|
86
73
|
};
|
|
87
74
|
}
|
|
88
|
-
|
|
89
75
|
function smartCommentConstraints(introspectionResults) {
|
|
90
76
|
const attributesByNames = (tbl, cols, debugStr) => {
|
|
91
77
|
const attributes = introspectionResults.attribute.filter(a => a.classId === tbl.id).sort((a, b) => a.num - b.num);
|
|
92
|
-
|
|
93
78
|
if (!cols) {
|
|
94
79
|
const pk = introspectionResults.constraint.find(c => c.classId == tbl.id && c.type === "p");
|
|
95
|
-
|
|
96
80
|
if (pk) {
|
|
97
81
|
return pk.keyAttributeNums.map(n => attributes.find(a => a.num === n));
|
|
98
82
|
} else {
|
|
99
83
|
throw new Error(`No columns specified for '${tbl.namespaceName}.${tbl.name}' (oid: ${tbl.id}) and no PK found (${debugStr}).`);
|
|
100
84
|
}
|
|
101
85
|
}
|
|
102
|
-
|
|
103
86
|
return cols.map(colName => {
|
|
104
87
|
const attr = attributes.find(a => a.name === colName);
|
|
105
|
-
|
|
106
88
|
if (!attr) {
|
|
107
89
|
throw new Error(`Could not find attribute '${colName}' in '${tbl.namespaceName}.${tbl.name}'`);
|
|
108
90
|
}
|
|
109
|
-
|
|
110
91
|
return attr;
|
|
111
92
|
});
|
|
112
|
-
};
|
|
113
|
-
|
|
93
|
+
};
|
|
114
94
|
|
|
95
|
+
// First: primary and unique keys
|
|
115
96
|
introspectionResults.class.forEach(klass => {
|
|
116
97
|
const namespace = introspectionResults.namespace.find(n => n.id === klass.namespaceId);
|
|
117
|
-
|
|
118
98
|
if (!namespace) {
|
|
119
99
|
return;
|
|
120
100
|
}
|
|
121
|
-
|
|
122
101
|
function addKey(key, isPrimary = false) {
|
|
123
102
|
const tag = isPrimary ? "@primaryKey" : "@unique";
|
|
124
|
-
|
|
125
103
|
if (typeof key !== "string") {
|
|
126
104
|
if (isPrimary) {
|
|
127
105
|
throw new Error(`${tag} configuration of '${klass.namespaceName}.${klass.name}' is invalid; please specify just once "${tag} col1,col2"`);
|
|
128
106
|
}
|
|
129
|
-
|
|
130
107
|
throw new Error(`${tag} configuration of '${klass.namespaceName}.${klass.name}' is invalid; expected ${isPrimary ? "a string" : "a string or string array"} but found ${typeof key}`);
|
|
131
108
|
}
|
|
132
|
-
|
|
133
109
|
const {
|
|
134
110
|
spec: keySpec,
|
|
135
111
|
tags,
|
|
@@ -137,15 +113,13 @@ function smartCommentConstraints(introspectionResults) {
|
|
|
137
113
|
} = parseConstraintSpec(key);
|
|
138
114
|
const columns = parseSqlColumnArray(keySpec);
|
|
139
115
|
const attributes = attributesByNames(klass, columns, `${tag} ${key}`);
|
|
140
|
-
|
|
141
116
|
if (isPrimary) {
|
|
142
117
|
attributes.forEach(attr => {
|
|
143
118
|
attr.tags.notNull = true;
|
|
144
119
|
});
|
|
145
120
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
121
|
+
const keyAttributeNums = attributes.map(a => a.num);
|
|
122
|
+
// Now we need to fake a constraint for this:
|
|
149
123
|
const fakeConstraint = {
|
|
150
124
|
kind: "constraint",
|
|
151
125
|
isFake: true,
|
|
@@ -164,11 +138,9 @@ function smartCommentConstraints(introspectionResults) {
|
|
|
164
138
|
};
|
|
165
139
|
introspectionResults.constraint.push(fakeConstraint);
|
|
166
140
|
}
|
|
167
|
-
|
|
168
141
|
if (klass.tags.primaryKey) {
|
|
169
142
|
addKey(klass.tags.primaryKey, true);
|
|
170
143
|
}
|
|
171
|
-
|
|
172
144
|
if (klass.tags.unique) {
|
|
173
145
|
if (Array.isArray(klass.tags.unique)) {
|
|
174
146
|
klass.tags.unique.forEach(key => addKey(key));
|
|
@@ -176,42 +148,33 @@ function smartCommentConstraints(introspectionResults) {
|
|
|
176
148
|
addKey(klass.tags.unique);
|
|
177
149
|
}
|
|
178
150
|
}
|
|
179
|
-
});
|
|
180
|
-
|
|
151
|
+
});
|
|
152
|
+
// Now primary keys are in place, we can apply foreign keys
|
|
181
153
|
introspectionResults.class.forEach(klass => {
|
|
182
154
|
const namespace = introspectionResults.namespace.find(n => n.id === klass.namespaceId);
|
|
183
|
-
|
|
184
155
|
if (!namespace) {
|
|
185
156
|
return;
|
|
186
157
|
}
|
|
187
|
-
|
|
188
158
|
const getType = () => introspectionResults.type.find(t => t.id === klass.typeId);
|
|
189
|
-
|
|
190
159
|
const foreignKey = klass.tags.foreignKey || getType().tags.foreignKey;
|
|
191
|
-
|
|
192
160
|
if (foreignKey) {
|
|
193
161
|
const foreignKeys = typeof foreignKey === "string" ? [foreignKey] : foreignKey;
|
|
194
|
-
|
|
195
162
|
if (!Array.isArray(foreignKeys)) {
|
|
196
163
|
throw new Error(`Invalid foreign key smart comment specified on '${klass.namespaceName}.${klass.name}'`);
|
|
197
164
|
}
|
|
198
|
-
|
|
199
165
|
foreignKeys.forEach((fkSpecRaw, index) => {
|
|
200
166
|
if (typeof fkSpecRaw !== "string") {
|
|
201
167
|
throw new Error(`Invalid foreign key spec (${index}) on '${klass.namespaceName}.${klass.name}'`);
|
|
202
168
|
}
|
|
203
|
-
|
|
204
169
|
const {
|
|
205
170
|
spec: fkSpec,
|
|
206
171
|
tags,
|
|
207
172
|
description
|
|
208
173
|
} = parseConstraintSpec(fkSpecRaw);
|
|
209
174
|
const matches = fkSpec.match(/^\(([^()]+)\) references ([^().]+)(?:\.([^().]+))?(?:\s*\(([^()]+)\))?$/i);
|
|
210
|
-
|
|
211
175
|
if (!matches) {
|
|
212
176
|
throw new Error(`Invalid foreignKey syntax for '${klass.namespaceName}.${klass.name}'; expected something like "(col1,col2) references schema.table (c1, c2)", you passed '${fkSpecRaw}'`);
|
|
213
177
|
}
|
|
214
|
-
|
|
215
178
|
const [, rawColumns, rawSchemaOrTable, rawTableOnly, rawForeignColumns] = matches;
|
|
216
179
|
const rawSchema = rawTableOnly ? rawSchemaOrTable : `"${klass.namespaceName}"`;
|
|
217
180
|
const rawTable = rawTableOnly || rawSchemaOrTable;
|
|
@@ -220,20 +183,17 @@ function smartCommentConstraints(introspectionResults) {
|
|
|
220
183
|
const foreignTable = parseSqlColumnString(rawTable);
|
|
221
184
|
const foreignColumns = rawForeignColumns ? parseSqlColumnArray(rawForeignColumns) : null;
|
|
222
185
|
const foreignKlass = introspectionResults.class.find(k => k.name === foreignTable && k.namespaceName === foreignSchema);
|
|
223
|
-
|
|
224
186
|
if (!foreignKlass) {
|
|
225
187
|
throw new Error(`@foreignKey smart comment referenced non-existant table/view '${foreignSchema}'.'${foreignTable}'. Note that this reference must use *database names* (i.e. it does not respect @name). (${fkSpecRaw})`);
|
|
226
188
|
}
|
|
227
|
-
|
|
228
189
|
const foreignNamespace = introspectionResults.namespace.find(n => n.id === foreignKlass.namespaceId);
|
|
229
|
-
|
|
230
190
|
if (!foreignNamespace) {
|
|
231
191
|
return;
|
|
232
192
|
}
|
|
233
|
-
|
|
234
193
|
const keyAttributeNums = attributesByNames(klass, columns, `@foreignKey ${fkSpecRaw}`).map(a => a.num);
|
|
235
|
-
const foreignKeyAttributeNums = attributesByNames(foreignKlass, foreignColumns, `@foreignKey ${fkSpecRaw}`).map(a => a.num);
|
|
194
|
+
const foreignKeyAttributeNums = attributesByNames(foreignKlass, foreignColumns, `@foreignKey ${fkSpecRaw}`).map(a => a.num);
|
|
236
195
|
|
|
196
|
+
// Now we need to fake a constraint for this:
|
|
237
197
|
const fakeConstraint = {
|
|
238
198
|
kind: "constraint",
|
|
239
199
|
isFake: true,
|
|
@@ -256,77 +216,68 @@ function smartCommentConstraints(introspectionResults) {
|
|
|
256
216
|
}
|
|
257
217
|
});
|
|
258
218
|
}
|
|
259
|
-
|
|
260
219
|
function isEnumConstraint(klass, con, isEnumTable) {
|
|
261
220
|
if (con.classId === klass.id) {
|
|
262
221
|
const isPrimaryKey = con.type === "p";
|
|
263
222
|
const isUniqueConstraint = con.type === "u";
|
|
264
|
-
|
|
265
223
|
if (isPrimaryKey || isUniqueConstraint) {
|
|
266
224
|
const isExplicitEnumConstraint = con.tags.enum === true || typeof con.tags.enum === "string";
|
|
267
225
|
const isPrimaryKeyOfEnumTableConstraint = con.type === "p" && isEnumTable;
|
|
268
|
-
|
|
269
226
|
if (isExplicitEnumConstraint || isPrimaryKeyOfEnumTableConstraint) {
|
|
270
227
|
const hasExactlyOneColumn = con.keyAttributeNums.length === 1;
|
|
271
|
-
|
|
272
228
|
if (!hasExactlyOneColumn) {
|
|
273
229
|
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
230
|
}
|
|
275
|
-
|
|
276
231
|
return true;
|
|
277
232
|
}
|
|
278
233
|
}
|
|
279
234
|
}
|
|
280
|
-
|
|
281
235
|
return false;
|
|
282
236
|
}
|
|
283
|
-
|
|
284
237
|
function enumTables(introspectionResults) {
|
|
285
238
|
introspectionResults.class.map(async klass => {
|
|
286
239
|
const isEnumTable = klass.tags.enum === true || typeof klass.tags.enum === "string";
|
|
287
|
-
|
|
288
240
|
if (isEnumTable) {
|
|
289
241
|
// Prevent the table being recognised as a table
|
|
290
242
|
// eslint-disable-next-line require-atomic-updates
|
|
291
|
-
klass.tags.omit = true;
|
|
292
|
-
|
|
293
|
-
klass.isSelectable = false;
|
|
294
|
-
|
|
295
|
-
klass.isInsertable = false;
|
|
296
|
-
|
|
297
|
-
klass.isUpdatable = false;
|
|
298
|
-
|
|
243
|
+
klass.tags.omit = true;
|
|
244
|
+
// eslint-disable-next-line require-atomic-updates
|
|
245
|
+
klass.isSelectable = false;
|
|
246
|
+
// eslint-disable-next-line require-atomic-updates
|
|
247
|
+
klass.isInsertable = false;
|
|
248
|
+
// eslint-disable-next-line require-atomic-updates
|
|
249
|
+
klass.isUpdatable = false;
|
|
250
|
+
// eslint-disable-next-line require-atomic-updates
|
|
299
251
|
klass.isDeletable = false;
|
|
300
|
-
}
|
|
301
|
-
// (e.g. `@primaryKey`)
|
|
302
|
-
|
|
252
|
+
}
|
|
303
253
|
|
|
304
|
-
|
|
254
|
+
// By this point, even views should have "fake" constraints we can use
|
|
255
|
+
// (e.g. `@primaryKey`)
|
|
256
|
+
const enumConstraints = introspectionResults.constraint.filter(con => isEnumConstraint(klass, con, isEnumTable));
|
|
305
257
|
|
|
306
|
-
|
|
258
|
+
// Get all the columns
|
|
259
|
+
const enumTableColumns = introspectionResults.attribute.filter(attr => attr.classId === klass.id);
|
|
307
260
|
|
|
261
|
+
// Get description column
|
|
308
262
|
const descriptionColumn = enumTableColumns.find(attr => attr.name === "description" || attr.tags.enumDescription);
|
|
309
263
|
const allData = klass._internalEnumData || [];
|
|
310
264
|
enumConstraints.forEach(constraint => {
|
|
311
265
|
const col = enumTableColumns.find(col => col.num === constraint.keyAttributeNums[0]);
|
|
312
|
-
|
|
313
266
|
if (!col) {
|
|
314
267
|
// Should never happen
|
|
315
268
|
throw new Error("Graphile Engine error - could not find column for enum constraint");
|
|
316
269
|
}
|
|
317
|
-
|
|
318
270
|
const data = allData.filter(row => row[col.name] != null);
|
|
319
|
-
|
|
320
271
|
if (data.length < 1) {
|
|
321
272
|
throw new Error(`Enum table "${klass.namespaceName}"."${klass.name}" contains no visible entries for enum constraint '${constraint.name}'. Check that the table contains at least one row and that the rows are not hidden by row-level security policies.`);
|
|
322
|
-
}
|
|
323
|
-
|
|
273
|
+
}
|
|
324
274
|
|
|
275
|
+
// Create fake enum type
|
|
325
276
|
const constraintIdent = constraint.type === "p" ? "" : `_${constraint.name}`;
|
|
326
277
|
const enumTypeArray = {
|
|
327
278
|
kind: "type",
|
|
328
279
|
isFake: true,
|
|
329
|
-
id:
|
|
280
|
+
id: fakeEnumIdentifier(klass.namespaceName, klass.name, constraintIdent, true),
|
|
330
281
|
name: `_${klass.name}${constraintIdent}`,
|
|
331
282
|
description: null,
|
|
332
283
|
tags: {},
|
|
@@ -349,10 +300,11 @@ function enumTables(introspectionResults) {
|
|
|
349
300
|
const enumType = {
|
|
350
301
|
kind: "type",
|
|
351
302
|
isFake: true,
|
|
352
|
-
id:
|
|
303
|
+
id: fakeEnumIdentifier(klass.namespaceName, klass.name, constraintIdent, false),
|
|
353
304
|
name: `${klass.name}${constraintIdent}`,
|
|
354
305
|
description: klass.description,
|
|
355
|
-
tags: {
|
|
306
|
+
tags: {
|
|
307
|
+
...klass.tags,
|
|
356
308
|
...constraint.tags
|
|
357
309
|
},
|
|
358
310
|
namespaceId: klass.namespaceId,
|
|
@@ -375,14 +327,14 @@ function enumTables(introspectionResults) {
|
|
|
375
327
|
};
|
|
376
328
|
introspectionResults.type.push(enumType, enumTypeArray);
|
|
377
329
|
introspectionResults.typeById[enumType.id] = enumType;
|
|
378
|
-
introspectionResults.typeById[enumTypeArray.id] = enumTypeArray;
|
|
379
|
-
// reference this enum type
|
|
330
|
+
introspectionResults.typeById[enumTypeArray.id] = enumTypeArray;
|
|
380
331
|
|
|
332
|
+
// Change type of all attributes that reference this table to
|
|
333
|
+
// reference this enum type
|
|
381
334
|
introspectionResults.constraint.forEach(c => {
|
|
382
335
|
if (c.type === "f" && c.foreignClassId === klass.id && c.foreignKeyAttributeNums.length === 1 && c.foreignKeyAttributeNums[0] === col.num) {
|
|
383
336
|
// Get the attribute
|
|
384
337
|
const fkattr = introspectionResults.attribute.find(attr => attr.classId === c.classId && attr.num === c.keyAttributeNums[0]);
|
|
385
|
-
|
|
386
338
|
if (fkattr) {
|
|
387
339
|
// Override the detected type to pretend to be our enum
|
|
388
340
|
fkattr.typeId = enumType.id;
|
|
@@ -392,9 +344,8 @@ function enumTables(introspectionResults) {
|
|
|
392
344
|
});
|
|
393
345
|
});
|
|
394
346
|
}
|
|
395
|
-
/* The argument to this must not contain cyclic references! */
|
|
396
|
-
|
|
397
347
|
|
|
348
|
+
/* The argument to this must not contain cyclic references! */
|
|
398
349
|
const deepClone = value => {
|
|
399
350
|
if (Array.isArray(value)) {
|
|
400
351
|
return value.map(val => deepClone(val));
|
|
@@ -407,7 +358,6 @@ const deepClone = value => {
|
|
|
407
358
|
return value;
|
|
408
359
|
}
|
|
409
360
|
};
|
|
410
|
-
|
|
411
361
|
var PgIntrospectionPlugin = async function PgIntrospectionPlugin(builder, {
|
|
412
362
|
pgConfig,
|
|
413
363
|
pgSchemas: schemas,
|
|
@@ -418,7 +368,8 @@ var PgIntrospectionPlugin = async function PgIntrospectionPlugin(builder, {
|
|
|
418
368
|
pgLegacyFunctionsOnly = false,
|
|
419
369
|
pgIgnoreRBAC = true,
|
|
420
370
|
pgSkipInstallingWatchFixtures = false,
|
|
421
|
-
pgOwnerConnectionString
|
|
371
|
+
pgOwnerConnectionString,
|
|
372
|
+
pgUsePartitionedParent = false
|
|
422
373
|
}) {
|
|
423
374
|
/**
|
|
424
375
|
* Introspect database and get the table/view/constraints.
|
|
@@ -428,14 +379,14 @@ var PgIntrospectionPlugin = async function PgIntrospectionPlugin(builder, {
|
|
|
428
379
|
if (!Array.isArray(schemas)) {
|
|
429
380
|
throw new Error("Argument 'schemas' (array) is required");
|
|
430
381
|
}
|
|
431
|
-
|
|
432
382
|
const cacheKey = `PgIntrospectionPlugin-introspectionResultsByKind-v${_package.version}`;
|
|
433
383
|
const introspectionResultsByKind = deepClone(await persistentMemoizeWithKey(cacheKey, () => (0, _withPgClient.default)(pgConfig, async pgClient => {
|
|
434
384
|
const versionResult = await pgClient.query("show server_version_num;");
|
|
435
385
|
const serverVersionNum = parseInt(versionResult.rows[0].server_version_num, 10);
|
|
436
386
|
const introspectionQuery = (0, _introspectionQuery.makeIntrospectionQuery)(serverVersionNum, {
|
|
437
387
|
pgLegacyFunctionsOnly,
|
|
438
|
-
pgIgnoreRBAC
|
|
388
|
+
pgIgnoreRBAC,
|
|
389
|
+
pgUsePartitionedParent
|
|
439
390
|
});
|
|
440
391
|
const {
|
|
441
392
|
rows
|
|
@@ -451,19 +402,17 @@ var PgIntrospectionPlugin = async function PgIntrospectionPlugin(builder, {
|
|
|
451
402
|
extension: [],
|
|
452
403
|
index: []
|
|
453
404
|
};
|
|
454
|
-
|
|
455
405
|
for (const {
|
|
456
406
|
object
|
|
457
407
|
} of rows) {
|
|
458
408
|
result[object.kind].push(object);
|
|
459
|
-
}
|
|
460
|
-
|
|
409
|
+
}
|
|
461
410
|
|
|
411
|
+
// Parse tags from comments
|
|
462
412
|
["namespace", "class", "attribute", "type", "constraint", "procedure", "extension", "index"].forEach(kind => {
|
|
463
413
|
result[kind].forEach(object => {
|
|
464
414
|
// Keep a copy of the raw comment
|
|
465
415
|
object.comment = object.description;
|
|
466
|
-
|
|
467
416
|
if (pgEnableTags && object.description) {
|
|
468
417
|
const parsed = (0, _utils.parseTags)(object.description);
|
|
469
418
|
object.tags = parsed.tags;
|
|
@@ -476,8 +425,9 @@ var PgIntrospectionPlugin = async function PgIntrospectionPlugin(builder, {
|
|
|
476
425
|
const extensionConfigurationClassIds = (0, _flatMap.default)(result.extension, e => e.configurationClassIds);
|
|
477
426
|
result.class.forEach(klass => {
|
|
478
427
|
klass.isExtensionConfigurationTable = extensionConfigurationClassIds.indexOf(klass.id) >= 0;
|
|
479
|
-
});
|
|
428
|
+
});
|
|
480
429
|
|
|
430
|
+
// Assert the columns are text
|
|
481
431
|
const VARCHAR_ID = "1043";
|
|
482
432
|
const TEXT_ID = "25";
|
|
483
433
|
const CHAR_ID = "18";
|
|
@@ -488,26 +438,24 @@ var PgIntrospectionPlugin = async function PgIntrospectionPlugin(builder, {
|
|
|
488
438
|
// Only support enums in public tables/views
|
|
489
439
|
return;
|
|
490
440
|
}
|
|
441
|
+
const isEnumTable = klass.tags.enum === true || typeof klass.tags.enum === "string";
|
|
491
442
|
|
|
492
|
-
|
|
443
|
+
// NOTE: this only matches on tables (not views, since they don't
|
|
493
444
|
// have constraints), which is why we repeat the isEnumTable check below.
|
|
494
|
-
|
|
495
445
|
const hasEnumConstraints = result.constraint.some(con => isEnumConstraint(klass, con, isEnumTable));
|
|
496
|
-
|
|
497
446
|
if (isEnumTable || hasEnumConstraints) {
|
|
498
447
|
// 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);
|
|
448
|
+
const enumTableColumns = result.attribute.filter(attr => attr.classId === klass.id && VALID_TYPE_IDS.includes(attr.typeId)).sort((a, z) => a.num - z.num);
|
|
500
449
|
|
|
450
|
+
// Load data from the table/view.
|
|
501
451
|
const query = pgSql.compile(pgSql.fragment`select ${pgSql.join(enumTableColumns.map(col => pgSql.identifier(col.name)), ", ")} from ${pgSql.identifier(klass.namespaceName, klass.name)};`);
|
|
502
452
|
let allData;
|
|
503
|
-
|
|
504
453
|
try {
|
|
505
454
|
({
|
|
506
455
|
rows: allData
|
|
507
456
|
} = await pgClient.query(query));
|
|
508
457
|
} catch (e) {
|
|
509
458
|
let role = "RELEVANT_POSTGRES_USER";
|
|
510
|
-
|
|
511
459
|
try {
|
|
512
460
|
const {
|
|
513
461
|
rows: [{
|
|
@@ -521,7 +469,6 @@ var PgIntrospectionPlugin = async function PgIntrospectionPlugin(builder, {
|
|
|
521
469
|
* error caused by the statement above failing.
|
|
522
470
|
*/
|
|
523
471
|
}
|
|
524
|
-
|
|
525
472
|
throw new Error(`Introspection could not read from enum table "${klass.namespaceName}"."${klass.name}", perhaps you need to grant access:
|
|
526
473
|
|
|
527
474
|
GRANT USAGE ON SCHEMA "${klass.namespaceName}" TO "${role}";
|
|
@@ -530,7 +477,6 @@ var PgIntrospectionPlugin = async function PgIntrospectionPlugin(builder, {
|
|
|
530
477
|
Original error: ${e.message}
|
|
531
478
|
`);
|
|
532
479
|
}
|
|
533
|
-
|
|
534
480
|
klass._internalEnumData = allData;
|
|
535
481
|
}
|
|
536
482
|
}));
|
|
@@ -541,10 +487,8 @@ Original error: ${e.message}
|
|
|
541
487
|
})));
|
|
542
488
|
const knownSchemas = introspectionResultsByKind.namespace.map(n => n.name);
|
|
543
489
|
const missingSchemas = schemas.filter(s => knownSchemas.indexOf(s) < 0);
|
|
544
|
-
|
|
545
490
|
if (missingSchemas.length) {
|
|
546
491
|
const errorMessage = `You requested to use schema '${schemas.join("', '")}'; however we couldn't find some of those! Missing schemas are: '${missingSchemas.join("', '")}'`;
|
|
547
|
-
|
|
548
492
|
if (pgThrowOnMissingSchema) {
|
|
549
493
|
throw new Error(errorMessage);
|
|
550
494
|
} else {
|
|
@@ -554,68 +498,55 @@ Original error: ${e.message}
|
|
|
554
498
|
|
|
555
499
|
return introspectionResultsByKind;
|
|
556
500
|
}
|
|
557
|
-
|
|
558
501
|
function introspectionResultsFromRaw(rawResults, pgAugmentIntrospectionResults) {
|
|
559
502
|
const introspectionResultsByKind = deepClone(rawResults);
|
|
560
|
-
|
|
561
503
|
const xByY = (arrayOfX, attrKey) => arrayOfX.reduce((memo, x) => {
|
|
562
504
|
memo[x[attrKey]] = x;
|
|
563
505
|
return memo;
|
|
564
506
|
}, {});
|
|
565
|
-
|
|
566
507
|
const xByYAndZ = (arrayOfX, attrKey, attrKey2) => arrayOfX.reduce((memo, x) => {
|
|
567
508
|
if (!memo[x[attrKey]]) memo[x[attrKey]] = {};
|
|
568
509
|
memo[x[attrKey]][x[attrKey2]] = x;
|
|
569
510
|
return memo;
|
|
570
511
|
}, {});
|
|
571
|
-
|
|
572
512
|
introspectionResultsByKind.namespaceById = xByY(introspectionResultsByKind.namespace, "id");
|
|
573
513
|
introspectionResultsByKind.classById = xByY(introspectionResultsByKind.class, "id");
|
|
574
514
|
introspectionResultsByKind.typeById = xByY(introspectionResultsByKind.type, "id");
|
|
575
515
|
introspectionResultsByKind.attributeByClassIdAndNum = xByYAndZ(introspectionResultsByKind.attribute, "classId", "num");
|
|
576
516
|
introspectionResultsByKind.extensionById = xByY(introspectionResultsByKind.extension, "id");
|
|
577
|
-
|
|
578
517
|
const relate = (array, newAttr, lookupAttr, lookup, missingOk = false) => {
|
|
579
518
|
array.forEach(entry => {
|
|
580
519
|
const key = entry[lookupAttr];
|
|
581
|
-
|
|
582
520
|
if (Array.isArray(key)) {
|
|
583
521
|
entry[newAttr] = key.map(innerKey => {
|
|
584
522
|
const result = lookup[innerKey];
|
|
585
|
-
|
|
586
523
|
if (innerKey && !result) {
|
|
587
524
|
if (missingOk) {
|
|
588
525
|
return;
|
|
589
526
|
}
|
|
590
|
-
|
|
591
527
|
throw new Error(`Could not look up '${newAttr}' by '${lookupAttr}' ('${innerKey}') on '${JSON.stringify(entry)}'`);
|
|
592
528
|
}
|
|
593
|
-
|
|
594
529
|
return result;
|
|
595
530
|
}).filter(_ => _);
|
|
596
531
|
} else {
|
|
597
532
|
const result = lookup[key];
|
|
598
|
-
|
|
599
533
|
if (key && !result) {
|
|
600
534
|
if (missingOk) {
|
|
601
535
|
return;
|
|
602
536
|
}
|
|
603
|
-
|
|
604
537
|
throw new Error(`Could not look up '${newAttr}' by '${lookupAttr}' (= '${key}') on '${JSON.stringify(entry)}'`);
|
|
605
538
|
}
|
|
606
|
-
|
|
607
539
|
entry[newAttr] = result;
|
|
608
540
|
}
|
|
609
541
|
});
|
|
610
542
|
};
|
|
611
|
-
|
|
612
543
|
const augment = introspectionResults => {
|
|
613
544
|
[pgAugmentIntrospectionResults, smartCommentConstraints, enumTables].forEach(fn => fn ? fn(introspectionResults) : null);
|
|
614
545
|
};
|
|
615
|
-
|
|
616
546
|
augment(introspectionResultsByKind);
|
|
617
547
|
relate(introspectionResultsByKind.class, "namespace", "namespaceId", introspectionResultsByKind.namespaceById, true // Because it could be a type defined in a different namespace - which is fine so long as we don't allow querying it directly
|
|
618
548
|
);
|
|
549
|
+
|
|
619
550
|
relate(introspectionResultsByKind.class, "type", "typeId", introspectionResultsByKind.typeById);
|
|
620
551
|
relate(introspectionResultsByKind.attribute, "class", "classId", introspectionResultsByKind.classById);
|
|
621
552
|
relate(introspectionResultsByKind.attribute, "type", "typeId", introspectionResultsByKind.typeById);
|
|
@@ -623,57 +554,65 @@ Original error: ${e.message}
|
|
|
623
554
|
relate(introspectionResultsByKind.type, "class", "classId", introspectionResultsByKind.classById, true);
|
|
624
555
|
relate(introspectionResultsByKind.type, "domainBaseType", "domainBaseTypeId", introspectionResultsByKind.typeById, true // Because not all types are domains
|
|
625
556
|
);
|
|
557
|
+
|
|
626
558
|
relate(introspectionResultsByKind.type, "arrayItemType", "arrayItemTypeId", introspectionResultsByKind.typeById, true // Because not all types are arrays
|
|
627
559
|
);
|
|
560
|
+
|
|
628
561
|
relate(introspectionResultsByKind.constraint, "class", "classId", introspectionResultsByKind.classById);
|
|
629
562
|
relate(introspectionResultsByKind.constraint, "foreignClass", "foreignClassId", introspectionResultsByKind.classById, true // Because many constraints don't apply to foreign classes
|
|
630
563
|
);
|
|
564
|
+
|
|
631
565
|
relate(introspectionResultsByKind.extension, "namespace", "namespaceId", introspectionResultsByKind.namespaceById, true // Because the extension could be a defined in a different namespace
|
|
632
566
|
);
|
|
567
|
+
|
|
633
568
|
relate(introspectionResultsByKind.extension, "configurationClasses", "configurationClassIds", introspectionResultsByKind.classById, true // Because the configuration table could be a defined in a different namespace
|
|
634
569
|
);
|
|
635
|
-
relate(introspectionResultsByKind.index, "class", "classId", introspectionResultsByKind.classById); // Reverse arrayItemType -> arrayType
|
|
636
570
|
|
|
571
|
+
relate(introspectionResultsByKind.index, "class", "classId", introspectionResultsByKind.classById);
|
|
572
|
+
|
|
573
|
+
// Reverse arrayItemType -> arrayType
|
|
637
574
|
introspectionResultsByKind.type.forEach(type => {
|
|
638
575
|
if (type.arrayItemType) {
|
|
639
576
|
type.arrayItemType.arrayType = type;
|
|
640
577
|
}
|
|
641
|
-
});
|
|
578
|
+
});
|
|
642
579
|
|
|
580
|
+
// Table/type columns / constraints
|
|
643
581
|
introspectionResultsByKind.class.forEach(klass => {
|
|
644
582
|
klass.attributes = introspectionResultsByKind.attribute.filter(attr => attr.classId === klass.id);
|
|
645
583
|
klass.canUseAsterisk = !klass.attributes.some(attr => attr.columnLevelSelectGrant);
|
|
646
584
|
klass.constraints = introspectionResultsByKind.constraint.filter(constraint => constraint.classId === klass.id);
|
|
647
585
|
klass.foreignConstraints = introspectionResultsByKind.constraint.filter(constraint => constraint.foreignClassId === klass.id);
|
|
648
586
|
klass.primaryKeyConstraint = klass.constraints.find(constraint => constraint.type === "p");
|
|
649
|
-
});
|
|
587
|
+
});
|
|
650
588
|
|
|
589
|
+
// Constraint attributes
|
|
651
590
|
introspectionResultsByKind.constraint.forEach(constraint => {
|
|
652
591
|
if (constraint.keyAttributeNums && constraint.class) {
|
|
653
592
|
constraint.keyAttributes = constraint.keyAttributeNums.map(nr => constraint.class.attributes.find(attr => attr.num === nr));
|
|
654
593
|
} else {
|
|
655
594
|
constraint.keyAttributes = [];
|
|
656
595
|
}
|
|
657
|
-
|
|
658
596
|
if (constraint.foreignKeyAttributeNums && constraint.foreignClass) {
|
|
659
597
|
constraint.foreignKeyAttributes = constraint.foreignKeyAttributeNums.map(nr => constraint.foreignClass.attributes.find(attr => attr.num === nr));
|
|
660
598
|
} else {
|
|
661
599
|
constraint.foreignKeyAttributes = [];
|
|
662
600
|
}
|
|
663
|
-
});
|
|
601
|
+
});
|
|
664
602
|
|
|
603
|
+
// Detect which columns and constraints are indexed
|
|
665
604
|
introspectionResultsByKind.index.forEach(index => {
|
|
666
|
-
const columns = index.attributeNums.map(nr => index.class.attributes.find(attr => attr.num === nr));
|
|
605
|
+
const columns = index.attributeNums.map(nr => index.class.attributes.find(attr => attr.num === nr));
|
|
667
606
|
|
|
607
|
+
// Indexed column (for orderBy / filter):
|
|
668
608
|
if (columns[0]) {
|
|
669
609
|
columns[0].isIndexed = true;
|
|
670
610
|
}
|
|
671
|
-
|
|
672
611
|
if (columns[0] && columns.length === 1 && index.isUnique) {
|
|
673
612
|
columns[0].isUnique = true;
|
|
674
|
-
}
|
|
675
|
-
|
|
613
|
+
}
|
|
676
614
|
|
|
615
|
+
// Indexed constraints (for reverse relations):
|
|
677
616
|
index.class.constraints.filter(constraint => constraint.type === "f").forEach(constraint => {
|
|
678
617
|
if (constraint.keyAttributeNums.every((nr, idx) => index.attributeNums[idx] === nr)) {
|
|
679
618
|
constraint.isIndexed = true;
|
|
@@ -682,16 +621,13 @@ Original error: ${e.message}
|
|
|
682
621
|
});
|
|
683
622
|
return introspectionResultsByKind;
|
|
684
623
|
}
|
|
685
|
-
|
|
686
624
|
let rawIntrospectionResultsByKind = await introspect();
|
|
687
625
|
let listener;
|
|
688
|
-
|
|
689
626
|
class Listener {
|
|
690
627
|
constructor(triggerRebuild) {
|
|
691
628
|
this.stopped = false;
|
|
692
629
|
this._handleChange = (0, _throttle.default)(async () => {
|
|
693
630
|
debug(`Schema change detected: re-inspecting schema...`);
|
|
694
|
-
|
|
695
631
|
try {
|
|
696
632
|
rawIntrospectionResultsByKind = await introspect();
|
|
697
633
|
debug(`Schema change detected: re-inspecting schema complete`);
|
|
@@ -706,33 +642,30 @@ Original error: ${e.message}
|
|
|
706
642
|
});
|
|
707
643
|
this._listener = this._listener.bind(this);
|
|
708
644
|
this._handleClientError = this._handleClientError.bind(this);
|
|
709
|
-
|
|
710
645
|
this._start();
|
|
711
646
|
}
|
|
712
|
-
|
|
713
647
|
async _start(isReconnect = false) {
|
|
714
648
|
if (this.stopped) {
|
|
715
649
|
return;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
|
|
650
|
+
}
|
|
651
|
+
// Connect to DB
|
|
719
652
|
try {
|
|
720
653
|
const {
|
|
721
654
|
pgClient,
|
|
722
655
|
releasePgClient
|
|
723
656
|
} = await (0, _withPgClient.getPgClientAndReleaserFromConfig)(pgConfig);
|
|
724
|
-
this.client = pgClient;
|
|
725
|
-
|
|
657
|
+
this.client = pgClient;
|
|
658
|
+
// $FlowFixMe: hack property
|
|
726
659
|
this._reallyReleaseClient = releasePgClient;
|
|
727
660
|
pgClient.on("notification", this._listener);
|
|
728
661
|
pgClient.on("error", this._handleClientError);
|
|
729
|
-
|
|
730
662
|
if (this.stopped) {
|
|
731
663
|
// In case watch mode was cancelled in the interrim.
|
|
732
664
|
return this._releaseClient();
|
|
733
665
|
} else {
|
|
734
|
-
await pgClient.query("listen postgraphile_watch");
|
|
666
|
+
await pgClient.query("listen postgraphile_watch");
|
|
735
667
|
|
|
668
|
+
// Install the watch fixtures.
|
|
736
669
|
if (!pgSkipInstallingWatchFixtures) {
|
|
737
670
|
const watchSqlInner = await readFile(WATCH_FIXTURES_PATH, "utf8");
|
|
738
671
|
const sql = `begin; ${watchSqlInner};`;
|
|
@@ -743,7 +676,6 @@ Original error: ${e.message}
|
|
|
743
676
|
if (!this._haveDisplayedError) {
|
|
744
677
|
this._haveDisplayedError = true;
|
|
745
678
|
/* eslint-disable no-console */
|
|
746
|
-
|
|
747
679
|
console.warn(`${_chalk.default.bold.yellow("Failed to setup watch fixtures in Postgres database")} ️️⚠️`);
|
|
748
680
|
console.warn(_chalk.default.yellow("This is likely because the PostgreSQL user in the connection string does not have sufficient privileges; you can solve this by passing the 'owner' connection string via '--owner-connection' / 'ownerConnectionString'. If the fixtures already exist, the watch functionality may still work."));
|
|
749
681
|
console.warn(_chalk.default.yellow("Enable DEBUG='graphile-build-pg' to see the error"));
|
|
@@ -755,9 +687,9 @@ Original error: ${e.message}
|
|
|
755
687
|
await pgClient.query("commit;");
|
|
756
688
|
}
|
|
757
689
|
});
|
|
758
|
-
}
|
|
759
|
-
|
|
690
|
+
}
|
|
760
691
|
|
|
692
|
+
// Trigger re-introspection on server reconnect
|
|
761
693
|
if (isReconnect) {
|
|
762
694
|
this._handleChange();
|
|
763
695
|
}
|
|
@@ -767,19 +699,15 @@ Original error: ${e.message}
|
|
|
767
699
|
this._reconnect(e);
|
|
768
700
|
}
|
|
769
701
|
}
|
|
770
|
-
|
|
771
702
|
_handleClientError(e) {
|
|
772
703
|
this._releaseClient(false);
|
|
773
|
-
|
|
774
704
|
this._reconnect(e);
|
|
775
705
|
}
|
|
776
|
-
|
|
777
706
|
async _reconnect(e) {
|
|
778
707
|
if (this.stopped) {
|
|
779
708
|
return;
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
|
|
709
|
+
}
|
|
710
|
+
// eslint-disable-next-line no-console
|
|
783
711
|
console.error("Error occurred for PG watching client; reconnecting in 2 seconds.", e.message);
|
|
784
712
|
await this._releaseClient();
|
|
785
713
|
setTimeout(() => {
|
|
@@ -788,32 +716,29 @@ Original error: ${e.message}
|
|
|
788
716
|
this._start(true);
|
|
789
717
|
}
|
|
790
718
|
}, 2000);
|
|
791
|
-
}
|
|
719
|
+
}
|
|
792
720
|
|
|
721
|
+
// eslint-disable-next-line flowtype/no-weak-types
|
|
793
722
|
|
|
794
723
|
// eslint-disable-next-line flowtype/no-weak-types
|
|
795
724
|
async _listener(notification) {
|
|
796
725
|
if (notification.channel !== "postgraphile_watch") {
|
|
797
726
|
return;
|
|
798
727
|
}
|
|
799
|
-
|
|
800
728
|
try {
|
|
801
729
|
const payload = JSON.parse(notification.payload);
|
|
802
730
|
payload.payload = payload.payload || [];
|
|
803
|
-
|
|
804
731
|
if (payload.type === "ddl") {
|
|
805
732
|
const commands = payload.payload.filter(({
|
|
806
733
|
schema
|
|
807
734
|
}) => schema == null || schemas.indexOf(schema) >= 0).map(({
|
|
808
735
|
command
|
|
809
736
|
}) => command);
|
|
810
|
-
|
|
811
737
|
if (commands.length) {
|
|
812
738
|
this._handleChange();
|
|
813
739
|
}
|
|
814
740
|
} else if (payload.type === "drop") {
|
|
815
741
|
const affectsOurSchemas = payload.payload.some(schemaName => schemas.indexOf(schemaName) >= 0);
|
|
816
|
-
|
|
817
742
|
if (affectsOurSchemas) {
|
|
818
743
|
this._handleChange();
|
|
819
744
|
}
|
|
@@ -826,27 +751,22 @@ Original error: ${e.message}
|
|
|
826
751
|
debug(`Error occurred parsing notification payload: ${e}`);
|
|
827
752
|
}
|
|
828
753
|
}
|
|
829
|
-
|
|
830
754
|
async stop() {
|
|
831
755
|
this.stopped = true;
|
|
832
|
-
|
|
833
756
|
this._handleChange.cancel();
|
|
834
|
-
|
|
835
757
|
await this._releaseClient();
|
|
836
758
|
}
|
|
759
|
+
|
|
837
760
|
/**
|
|
838
761
|
* Only pass `false` to this function if you know the client is going to be
|
|
839
762
|
* terminated; otherwise we risk leaving listeners running.
|
|
840
763
|
*/
|
|
841
|
-
|
|
842
|
-
|
|
843
764
|
async _releaseClient(clientIsStillViable = true) {
|
|
844
765
|
// $FlowFixMe
|
|
845
766
|
const pgClient = this.client;
|
|
846
767
|
const reallyReleaseClient = this._reallyReleaseClient;
|
|
847
768
|
this.client = null;
|
|
848
769
|
this._reallyReleaseClient = null;
|
|
849
|
-
|
|
850
770
|
if (pgClient) {
|
|
851
771
|
// Don't attempt to run a query after a client has errored.
|
|
852
772
|
if (clientIsStillViable) {
|
|
@@ -854,38 +774,31 @@ Original error: ${e.message}
|
|
|
854
774
|
debug(`Error occurred trying to unlisten watch: ${e}`);
|
|
855
775
|
});
|
|
856
776
|
}
|
|
857
|
-
|
|
858
777
|
pgClient.removeListener("notification", this._listener);
|
|
859
778
|
pgClient.removeListener("error", this._handleClientError);
|
|
860
779
|
}
|
|
861
|
-
|
|
862
780
|
if (reallyReleaseClient) {
|
|
863
781
|
await reallyReleaseClient();
|
|
864
782
|
}
|
|
865
783
|
}
|
|
866
|
-
|
|
867
784
|
}
|
|
868
|
-
|
|
869
785
|
builder.registerWatcher(async triggerRebuild => {
|
|
870
786
|
// In case we started listening before, clean up
|
|
871
787
|
if (listener) {
|
|
872
788
|
await listener.stop();
|
|
873
|
-
}
|
|
789
|
+
}
|
|
790
|
+
// We're not worried about a race condition here.
|
|
874
791
|
// eslint-disable-next-line require-atomic-updates
|
|
875
|
-
|
|
876
|
-
|
|
877
792
|
listener = new Listener(triggerRebuild);
|
|
878
793
|
}, async () => {
|
|
879
794
|
const l = listener;
|
|
880
795
|
listener = null;
|
|
881
|
-
|
|
882
796
|
if (l) {
|
|
883
797
|
await l.stop();
|
|
884
798
|
}
|
|
885
799
|
});
|
|
886
800
|
builder.hook("build", build => {
|
|
887
801
|
const introspectionResultsByKind = introspectionResultsFromRaw(rawIntrospectionResultsByKind, build.pgAugmentIntrospectionResults);
|
|
888
|
-
|
|
889
802
|
if (introspectionResultsByKind.__pgVersion < 90500) {
|
|
890
803
|
// TODO:v5: remove this workaround
|
|
891
804
|
// This is a bit of a hack, but until we have plugin priorities it's the
|
|
@@ -894,14 +807,12 @@ Original error: ${e.message}
|
|
|
894
807
|
supportsJSONB: false
|
|
895
808
|
});
|
|
896
809
|
}
|
|
897
|
-
|
|
898
810
|
return build.extend(build, {
|
|
899
|
-
pgIntrospectionResultsByKind: introspectionResultsByKind
|
|
811
|
+
pgIntrospectionResultsByKind: introspectionResultsByKind,
|
|
812
|
+
getPgFakeEnumIdentifier: fakeEnumIdentifier
|
|
900
813
|
});
|
|
901
814
|
}, ["PgIntrospection"], [], ["PgBasics"]);
|
|
902
815
|
}; // TypeScript compatibility
|
|
903
|
-
|
|
904
|
-
|
|
905
816
|
exports.default = PgIntrospectionPlugin;
|
|
906
817
|
const PgEntityKind = {
|
|
907
818
|
NAMESPACE: "namespace",
|