graphile-scoped-introspection 0.2.0
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/LICENSE +23 -0
- package/README.md +141 -0
- package/esm/index.d.ts +4 -0
- package/esm/index.js +2 -0
- package/esm/plugin.d.ts +21 -0
- package/esm/plugin.js +212 -0
- package/esm/scoped-introspection-query.d.ts +37 -0
- package/esm/scoped-introspection-query.js +705 -0
- package/index.d.ts +4 -0
- package/index.js +11 -0
- package/package.json +57 -0
- package/plugin.d.ts +21 -0
- package/plugin.js +215 -0
- package/scoped-introspection-query.d.ts +37 -0
- package/scoped-introspection-query.js +711 -0
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Scoped catalog SQL adapted from Graphile Crystal's
|
|
4
|
+
* utils/pg-introspection/src/scopedIntrospection.ts at commit
|
|
5
|
+
* 441cef73b0a12ed24343b7f38241af28b6454280, which is MIT licensed
|
|
6
|
+
* (Copyright © 2023 Benjie Gillam). This is a static, CNC-owned query
|
|
7
|
+
* generator: it does not inspect or rewrite the upstream query at runtime.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.makeSchemaScopedIntrospectionQuery = exports.makeSchemaScopedIntrospectionPlan = void 0;
|
|
11
|
+
exports.validateSchemaScopedIntrospection = validateSchemaScopedIntrospection;
|
|
12
|
+
const SCOPED_CTES = `recursive
|
|
13
|
+
requested_schema_names(schema_name) as (
|
|
14
|
+
select distinct requested.schema_name
|
|
15
|
+
from pg_catalog.unnest($1::text[]) as requested(schema_name)
|
|
16
|
+
),
|
|
17
|
+
|
|
18
|
+
capability_extension_names(extension_name) as (
|
|
19
|
+
select distinct capability.extension_name
|
|
20
|
+
from pg_catalog.unnest($2::text[]) as capability(extension_name)
|
|
21
|
+
),
|
|
22
|
+
|
|
23
|
+
requested_namespaces as (
|
|
24
|
+
select pg_namespace.oid as _id, pg_namespace.nspname
|
|
25
|
+
from pg_catalog.pg_namespace
|
|
26
|
+
inner join requested_schema_names
|
|
27
|
+
on requested_schema_names.schema_name = pg_namespace.nspname
|
|
28
|
+
),
|
|
29
|
+
|
|
30
|
+
root_objects(object_class, object_id) as (
|
|
31
|
+
select 'pg_catalog.pg_class'::regclass::oid, pg_class.oid
|
|
32
|
+
from pg_catalog.pg_class
|
|
33
|
+
where pg_class.relnamespace in (select requested_namespaces._id from requested_namespaces)
|
|
34
|
+
|
|
35
|
+
union
|
|
36
|
+
|
|
37
|
+
select 'pg_catalog.pg_constraint'::regclass::oid, pg_constraint.oid
|
|
38
|
+
from pg_catalog.pg_constraint
|
|
39
|
+
where pg_constraint.connamespace in (select requested_namespaces._id from requested_namespaces)
|
|
40
|
+
|
|
41
|
+
union
|
|
42
|
+
|
|
43
|
+
select 'pg_catalog.pg_proc'::regclass::oid, pg_proc.oid
|
|
44
|
+
from pg_catalog.pg_proc
|
|
45
|
+
where pg_proc.pronamespace in (select requested_namespaces._id from requested_namespaces)
|
|
46
|
+
and pg_proc.prorettype operator(pg_catalog.<>) 2279
|
|
47
|
+
|
|
48
|
+
union
|
|
49
|
+
|
|
50
|
+
select 'pg_catalog.pg_type'::regclass::oid, pg_type.oid
|
|
51
|
+
from pg_catalog.pg_type
|
|
52
|
+
where pg_type.typnamespace in (select requested_namespaces._id from requested_namespaces)
|
|
53
|
+
),
|
|
54
|
+
|
|
55
|
+
object_closure(object_class, object_id) as (
|
|
56
|
+
select root_objects.object_class, root_objects.object_id
|
|
57
|
+
from root_objects
|
|
58
|
+
|
|
59
|
+
union
|
|
60
|
+
|
|
61
|
+
select dependency.object_class, dependency.object_id
|
|
62
|
+
from object_closure
|
|
63
|
+
cross join lateral (
|
|
64
|
+
select
|
|
65
|
+
'pg_catalog.pg_type'::regclass::oid as object_class,
|
|
66
|
+
pg_class.reltype as object_id
|
|
67
|
+
from pg_catalog.pg_class
|
|
68
|
+
where object_closure.object_class = 'pg_catalog.pg_class'::regclass
|
|
69
|
+
and pg_class.oid = object_closure.object_id
|
|
70
|
+
|
|
71
|
+
union all
|
|
72
|
+
|
|
73
|
+
select 'pg_catalog.pg_type'::regclass::oid, pg_class.reloftype
|
|
74
|
+
from pg_catalog.pg_class
|
|
75
|
+
where object_closure.object_class = 'pg_catalog.pg_class'::regclass
|
|
76
|
+
and pg_class.oid = object_closure.object_id
|
|
77
|
+
|
|
78
|
+
union all
|
|
79
|
+
|
|
80
|
+
select 'pg_catalog.pg_type'::regclass::oid, pg_attribute.atttypid
|
|
81
|
+
from pg_catalog.pg_attribute
|
|
82
|
+
where object_closure.object_class = 'pg_catalog.pg_class'::regclass
|
|
83
|
+
and pg_attribute.attrelid = object_closure.object_id
|
|
84
|
+
|
|
85
|
+
union all
|
|
86
|
+
|
|
87
|
+
select 'pg_catalog.pg_constraint'::regclass::oid, pg_constraint.oid
|
|
88
|
+
from pg_catalog.pg_constraint
|
|
89
|
+
where object_closure.object_class = 'pg_catalog.pg_class'::regclass
|
|
90
|
+
and pg_constraint.conrelid = object_closure.object_id
|
|
91
|
+
|
|
92
|
+
union all
|
|
93
|
+
|
|
94
|
+
select 'pg_catalog.pg_class'::regclass::oid, pg_index.indexrelid
|
|
95
|
+
from pg_catalog.pg_index
|
|
96
|
+
where object_closure.object_class = 'pg_catalog.pg_class'::regclass
|
|
97
|
+
and pg_index.indrelid = object_closure.object_id
|
|
98
|
+
|
|
99
|
+
union all
|
|
100
|
+
|
|
101
|
+
select 'pg_catalog.pg_class'::regclass::oid, pg_inherits.inhparent
|
|
102
|
+
from pg_catalog.pg_inherits
|
|
103
|
+
where object_closure.object_class = 'pg_catalog.pg_class'::regclass
|
|
104
|
+
and pg_inherits.inhrelid = object_closure.object_id
|
|
105
|
+
|
|
106
|
+
union all
|
|
107
|
+
|
|
108
|
+
select 'pg_catalog.pg_class'::regclass::oid, constraint_class.oid
|
|
109
|
+
from pg_catalog.pg_constraint
|
|
110
|
+
cross join lateral pg_catalog.unnest(
|
|
111
|
+
array[
|
|
112
|
+
pg_constraint.conrelid,
|
|
113
|
+
pg_constraint.confrelid,
|
|
114
|
+
pg_constraint.conindid
|
|
115
|
+
]::oid[]
|
|
116
|
+
) as constraint_class(oid)
|
|
117
|
+
where object_closure.object_class = 'pg_catalog.pg_constraint'::regclass
|
|
118
|
+
and pg_constraint.oid = object_closure.object_id
|
|
119
|
+
|
|
120
|
+
union all
|
|
121
|
+
|
|
122
|
+
select 'pg_catalog.pg_type'::regclass::oid, pg_constraint.contypid
|
|
123
|
+
from pg_catalog.pg_constraint
|
|
124
|
+
where object_closure.object_class = 'pg_catalog.pg_constraint'::regclass
|
|
125
|
+
and pg_constraint.oid = object_closure.object_id
|
|
126
|
+
|
|
127
|
+
union all
|
|
128
|
+
|
|
129
|
+
select 'pg_catalog.pg_constraint'::regclass::oid, pg_constraint.conparentid
|
|
130
|
+
from pg_catalog.pg_constraint
|
|
131
|
+
where object_closure.object_class = 'pg_catalog.pg_constraint'::regclass
|
|
132
|
+
and pg_constraint.oid = object_closure.object_id
|
|
133
|
+
|
|
134
|
+
union all
|
|
135
|
+
|
|
136
|
+
select 'pg_catalog.pg_type'::regclass::oid, procedure_type.oid
|
|
137
|
+
from pg_catalog.pg_proc
|
|
138
|
+
cross join lateral pg_catalog.unnest(
|
|
139
|
+
coalesce(pg_proc.proallargtypes, pg_proc.proargtypes::oid[])
|
|
140
|
+
|| array[pg_proc.prorettype]::oid[]
|
|
141
|
+
) as procedure_type(oid)
|
|
142
|
+
where object_closure.object_class = 'pg_catalog.pg_proc'::regclass
|
|
143
|
+
and pg_proc.oid = object_closure.object_id
|
|
144
|
+
|
|
145
|
+
union all
|
|
146
|
+
|
|
147
|
+
select 'pg_catalog.pg_type'::regclass::oid, dependency_type.oid
|
|
148
|
+
from pg_catalog.pg_type
|
|
149
|
+
cross join lateral pg_catalog.unnest(
|
|
150
|
+
array[
|
|
151
|
+
pg_type.typbasetype,
|
|
152
|
+
pg_type.typelem,
|
|
153
|
+
pg_type.typarray
|
|
154
|
+
]::oid[]
|
|
155
|
+
) as dependency_type(oid)
|
|
156
|
+
where object_closure.object_class = 'pg_catalog.pg_type'::regclass
|
|
157
|
+
and pg_type.oid = object_closure.object_id
|
|
158
|
+
|
|
159
|
+
union all
|
|
160
|
+
|
|
161
|
+
select 'pg_catalog.pg_class'::regclass::oid, pg_type.typrelid
|
|
162
|
+
from pg_catalog.pg_type
|
|
163
|
+
where object_closure.object_class = 'pg_catalog.pg_type'::regclass
|
|
164
|
+
and pg_type.oid = object_closure.object_id
|
|
165
|
+
|
|
166
|
+
union all
|
|
167
|
+
|
|
168
|
+
select 'pg_catalog.pg_constraint'::regclass::oid, pg_constraint.oid
|
|
169
|
+
from pg_catalog.pg_constraint
|
|
170
|
+
where object_closure.object_class = 'pg_catalog.pg_type'::regclass
|
|
171
|
+
and pg_constraint.contypid = object_closure.object_id
|
|
172
|
+
|
|
173
|
+
union all
|
|
174
|
+
|
|
175
|
+
select 'pg_catalog.pg_type'::regclass::oid, range_type.oid
|
|
176
|
+
from pg_catalog.pg_range
|
|
177
|
+
cross join lateral pg_catalog.unnest(
|
|
178
|
+
array[
|
|
179
|
+
pg_range.rngtypid,
|
|
180
|
+
pg_range.rngsubtype,
|
|
181
|
+
pg_range.rngmultitypid
|
|
182
|
+
]::oid[]
|
|
183
|
+
) as range_type(oid)
|
|
184
|
+
where object_closure.object_class = 'pg_catalog.pg_type'::regclass
|
|
185
|
+
and object_closure.object_id in (pg_range.rngtypid, pg_range.rngmultitypid)
|
|
186
|
+
) as dependency
|
|
187
|
+
where dependency.object_id operator(pg_catalog.<>) 0
|
|
188
|
+
),
|
|
189
|
+
|
|
190
|
+
retained_index_metadata(indexrelid, indclass, indcollation) as (
|
|
191
|
+
select pg_index.indexrelid, pg_index.indclass, pg_index.indcollation
|
|
192
|
+
from object_closure
|
|
193
|
+
inner join pg_catalog.pg_class retained_index
|
|
194
|
+
on object_closure.object_class = 'pg_catalog.pg_class'::regclass
|
|
195
|
+
and retained_index.oid = object_closure.object_id
|
|
196
|
+
and retained_index.relkind in ('i', 'I')
|
|
197
|
+
inner join pg_catalog.pg_index
|
|
198
|
+
on pg_index.indexrelid = retained_index.oid
|
|
199
|
+
),
|
|
200
|
+
|
|
201
|
+
retained_index_opclasses(_id, opcfamily) as (
|
|
202
|
+
select pg_opclass.oid, pg_opclass.opcfamily
|
|
203
|
+
from retained_index_metadata
|
|
204
|
+
cross join lateral pg_catalog.unnest(
|
|
205
|
+
retained_index_metadata.indclass::oid[]
|
|
206
|
+
) as index_opclass(_id)
|
|
207
|
+
inner join pg_catalog.pg_opclass
|
|
208
|
+
on pg_opclass.oid = index_opclass._id
|
|
209
|
+
),
|
|
210
|
+
|
|
211
|
+
retained_index_support_objects(object_class, object_id) as (
|
|
212
|
+
select 'pg_catalog.pg_opclass'::regclass::oid, retained_index_opclasses._id
|
|
213
|
+
from retained_index_opclasses
|
|
214
|
+
|
|
215
|
+
union
|
|
216
|
+
|
|
217
|
+
select 'pg_catalog.pg_opfamily'::regclass::oid, retained_index_opclasses.opcfamily
|
|
218
|
+
from retained_index_opclasses
|
|
219
|
+
|
|
220
|
+
union
|
|
221
|
+
|
|
222
|
+
select 'pg_catalog.pg_operator'::regclass::oid, pg_amop.amopopr
|
|
223
|
+
from retained_index_opclasses
|
|
224
|
+
inner join pg_catalog.pg_amop
|
|
225
|
+
on pg_amop.amopfamily = retained_index_opclasses.opcfamily
|
|
226
|
+
|
|
227
|
+
union
|
|
228
|
+
|
|
229
|
+
select 'pg_catalog.pg_proc'::regclass::oid, pg_amproc.amproc
|
|
230
|
+
from retained_index_opclasses
|
|
231
|
+
inner join pg_catalog.pg_amproc
|
|
232
|
+
on pg_amproc.amprocfamily = retained_index_opclasses.opcfamily
|
|
233
|
+
|
|
234
|
+
union
|
|
235
|
+
|
|
236
|
+
select 'pg_catalog.pg_collation'::regclass::oid, index_collation._id
|
|
237
|
+
from retained_index_metadata
|
|
238
|
+
cross join lateral pg_catalog.unnest(
|
|
239
|
+
retained_index_metadata.indcollation::oid[]
|
|
240
|
+
) as index_collation(_id)
|
|
241
|
+
where index_collation._id operator(pg_catalog.<>) 0
|
|
242
|
+
),
|
|
243
|
+
|
|
244
|
+
installed_extensions(_id, extnamespace) as (
|
|
245
|
+
select pg_extension.oid, pg_extension.extnamespace
|
|
246
|
+
from pg_catalog.pg_extension
|
|
247
|
+
where pg_extension.extname in (
|
|
248
|
+
select capability_extension_names.extension_name
|
|
249
|
+
from capability_extension_names
|
|
250
|
+
)
|
|
251
|
+
or exists (
|
|
252
|
+
select 1
|
|
253
|
+
from object_closure
|
|
254
|
+
inner join pg_catalog.pg_depend
|
|
255
|
+
on pg_depend.classid = object_closure.object_class
|
|
256
|
+
and pg_depend.objid = object_closure.object_id
|
|
257
|
+
and pg_depend.refclassid = 'pg_catalog.pg_extension'::regclass
|
|
258
|
+
and pg_depend.refobjid = pg_extension.oid
|
|
259
|
+
and pg_depend.deptype = 'e'
|
|
260
|
+
)
|
|
261
|
+
or exists (
|
|
262
|
+
select 1
|
|
263
|
+
from retained_index_support_objects
|
|
264
|
+
inner join pg_catalog.pg_depend
|
|
265
|
+
on pg_depend.classid = retained_index_support_objects.object_class
|
|
266
|
+
and pg_depend.objid = retained_index_support_objects.object_id
|
|
267
|
+
and pg_depend.refclassid = 'pg_catalog.pg_extension'::regclass
|
|
268
|
+
and pg_depend.refobjid = pg_extension.oid
|
|
269
|
+
and pg_depend.deptype = 'e'
|
|
270
|
+
)
|
|
271
|
+
or exists (
|
|
272
|
+
select 1
|
|
273
|
+
from object_closure
|
|
274
|
+
inner join pg_catalog.pg_class retained_index
|
|
275
|
+
on object_closure.object_class = 'pg_catalog.pg_class'::regclass
|
|
276
|
+
and retained_index.oid = object_closure.object_id
|
|
277
|
+
and retained_index.relkind = 'i'
|
|
278
|
+
inner join pg_catalog.pg_depend
|
|
279
|
+
on pg_depend.classid = 'pg_catalog.pg_am'::regclass
|
|
280
|
+
and pg_depend.objid = retained_index.relam
|
|
281
|
+
and pg_depend.refclassid = 'pg_catalog.pg_extension'::regclass
|
|
282
|
+
and pg_depend.refobjid = pg_extension.oid
|
|
283
|
+
and pg_depend.deptype = 'e'
|
|
284
|
+
)
|
|
285
|
+
),
|
|
286
|
+
|
|
287
|
+
scoped_namespaces(_id) as (
|
|
288
|
+
select requested_namespaces._id
|
|
289
|
+
from requested_namespaces
|
|
290
|
+
|
|
291
|
+
union
|
|
292
|
+
|
|
293
|
+
select pg_class.relnamespace
|
|
294
|
+
from object_closure
|
|
295
|
+
inner join pg_catalog.pg_class
|
|
296
|
+
on object_closure.object_class = 'pg_catalog.pg_class'::regclass
|
|
297
|
+
and pg_class.oid = object_closure.object_id
|
|
298
|
+
|
|
299
|
+
union
|
|
300
|
+
|
|
301
|
+
select pg_constraint.connamespace
|
|
302
|
+
from object_closure
|
|
303
|
+
inner join pg_catalog.pg_constraint
|
|
304
|
+
on object_closure.object_class = 'pg_catalog.pg_constraint'::regclass
|
|
305
|
+
and pg_constraint.oid = object_closure.object_id
|
|
306
|
+
|
|
307
|
+
union
|
|
308
|
+
|
|
309
|
+
select pg_proc.pronamespace
|
|
310
|
+
from object_closure
|
|
311
|
+
inner join pg_catalog.pg_proc
|
|
312
|
+
on object_closure.object_class = 'pg_catalog.pg_proc'::regclass
|
|
313
|
+
and pg_proc.oid = object_closure.object_id
|
|
314
|
+
|
|
315
|
+
union
|
|
316
|
+
|
|
317
|
+
select pg_type.typnamespace
|
|
318
|
+
from object_closure
|
|
319
|
+
inner join pg_catalog.pg_type
|
|
320
|
+
on object_closure.object_class = 'pg_catalog.pg_type'::regclass
|
|
321
|
+
and pg_type.oid = object_closure.object_id
|
|
322
|
+
|
|
323
|
+
union
|
|
324
|
+
|
|
325
|
+
select installed_extensions.extnamespace
|
|
326
|
+
from installed_extensions
|
|
327
|
+
where installed_extensions.extnamespace operator(pg_catalog.<>) 0
|
|
328
|
+
|
|
329
|
+
union
|
|
330
|
+
|
|
331
|
+
select pg_namespace.oid
|
|
332
|
+
from pg_catalog.pg_namespace
|
|
333
|
+
where pg_namespace.nspname = 'pg_catalog'
|
|
334
|
+
),
|
|
335
|
+
|
|
336
|
+
`;
|
|
337
|
+
// We might want this to take options in future, so we've made it a function.
|
|
338
|
+
/**
|
|
339
|
+
* Builds a PostgreSQL introspection SQL query to return an object with the same shape as `Introspection` above.
|
|
340
|
+
*/
|
|
341
|
+
const buildIntrospectionQuery = (scope) => `\
|
|
342
|
+
with
|
|
343
|
+
${scope?.ctes ?? ''}\
|
|
344
|
+
database as (
|
|
345
|
+
select pg_database.oid as _id, *
|
|
346
|
+
from pg_catalog.pg_database
|
|
347
|
+
where datname = current_database()
|
|
348
|
+
),
|
|
349
|
+
|
|
350
|
+
namespaces as (
|
|
351
|
+
select pg_namespace.oid as _id, *
|
|
352
|
+
from pg_catalog.pg_namespace
|
|
353
|
+
where ${scope.namespacePredicate}
|
|
354
|
+
),
|
|
355
|
+
|
|
356
|
+
classes as (
|
|
357
|
+
select pg_class.oid as _id, *,
|
|
358
|
+
pg_catalog.pg_relation_is_updatable(oid, true)::bit(8)::int4 as "updatable_mask"
|
|
359
|
+
from pg_catalog.pg_class
|
|
360
|
+
where ${scope.classPredicate}
|
|
361
|
+
),
|
|
362
|
+
|
|
363
|
+
attributes as (
|
|
364
|
+
select *
|
|
365
|
+
from pg_catalog.pg_attribute
|
|
366
|
+
where attrelid in (select classes._id from classes) AND attnum > 0
|
|
367
|
+
),
|
|
368
|
+
|
|
369
|
+
constraints as (
|
|
370
|
+
select pg_constraint.oid as _id, *
|
|
371
|
+
from pg_catalog.pg_constraint
|
|
372
|
+
where ${scope.constraintPredicate}
|
|
373
|
+
),
|
|
374
|
+
|
|
375
|
+
procs as (
|
|
376
|
+
select pg_proc.oid as _id, *
|
|
377
|
+
from pg_catalog.pg_proc
|
|
378
|
+
where ${scope.procPredicate}
|
|
379
|
+
and prorettype operator(pg_catalog.<>) 2279
|
|
380
|
+
),
|
|
381
|
+
|
|
382
|
+
roles as (
|
|
383
|
+
select pg_roles.oid as _id, *
|
|
384
|
+
from pg_catalog.pg_roles
|
|
385
|
+
${scope?.rolePredicate
|
|
386
|
+
? ` where ${scope.rolePredicate}
|
|
387
|
+
`
|
|
388
|
+
: ''}\
|
|
389
|
+
),
|
|
390
|
+
|
|
391
|
+
auth_members as (
|
|
392
|
+
select *
|
|
393
|
+
from pg_catalog.pg_auth_members
|
|
394
|
+
where ${scope.authMemberPredicate ?? 'roleid in (select roles._id from roles)'}
|
|
395
|
+
),
|
|
396
|
+
|
|
397
|
+
types as (
|
|
398
|
+
select pg_type.oid as _id, *
|
|
399
|
+
from pg_catalog.pg_type
|
|
400
|
+
where ${scope.typePredicate}
|
|
401
|
+
),
|
|
402
|
+
|
|
403
|
+
enums as (
|
|
404
|
+
select pg_enum.oid as _id, *
|
|
405
|
+
from pg_catalog.pg_enum
|
|
406
|
+
where enumtypid in (select types._id from types)
|
|
407
|
+
),
|
|
408
|
+
|
|
409
|
+
extensions as (
|
|
410
|
+
select pg_extension.oid as _id, *
|
|
411
|
+
from pg_catalog.pg_extension
|
|
412
|
+
${scope?.extensionPredicate
|
|
413
|
+
? ` where ${scope.extensionPredicate}
|
|
414
|
+
`
|
|
415
|
+
: ''}\
|
|
416
|
+
),
|
|
417
|
+
|
|
418
|
+
indexes as (
|
|
419
|
+
select *
|
|
420
|
+
from pg_catalog.pg_index
|
|
421
|
+
where indrelid in (select classes._id from classes)
|
|
422
|
+
),
|
|
423
|
+
|
|
424
|
+
inherits as (
|
|
425
|
+
select *
|
|
426
|
+
from pg_catalog.pg_inherits
|
|
427
|
+
where inhrelid in (select classes._id from classes)
|
|
428
|
+
),
|
|
429
|
+
|
|
430
|
+
languages as (
|
|
431
|
+
select pg_language.oid as _id, *
|
|
432
|
+
from pg_catalog.pg_language
|
|
433
|
+
${scope?.languagePredicate
|
|
434
|
+
? ` where ${scope.languagePredicate}
|
|
435
|
+
`
|
|
436
|
+
: ''}\
|
|
437
|
+
),
|
|
438
|
+
|
|
439
|
+
policies as (
|
|
440
|
+
select *
|
|
441
|
+
from pg_catalog.pg_policy
|
|
442
|
+
where polrelid in (select classes._id from classes)
|
|
443
|
+
),
|
|
444
|
+
|
|
445
|
+
ranges as (
|
|
446
|
+
select *
|
|
447
|
+
from pg_catalog.pg_range
|
|
448
|
+
where rngtypid in (select types._id from types)
|
|
449
|
+
),
|
|
450
|
+
|
|
451
|
+
depends as (
|
|
452
|
+
select *
|
|
453
|
+
from pg_catalog.pg_depend
|
|
454
|
+
where deptype IN ('a', 'e') and (
|
|
455
|
+
(classid = 'pg_catalog.pg_namespace'::regclass and objid in (select namespaces._id from namespaces))
|
|
456
|
+
or (classid = 'pg_catalog.pg_class'::regclass and objid in (select classes._id from classes))
|
|
457
|
+
or (classid = 'pg_catalog.pg_attribute'::regclass and objid in (select classes._id from classes) and objsubid > 0)
|
|
458
|
+
or (classid = 'pg_catalog.pg_constraint'::regclass and objid in (select constraints._id from constraints))
|
|
459
|
+
or (classid = 'pg_catalog.pg_proc'::regclass and objid in (select procs._id from procs))
|
|
460
|
+
or (classid = 'pg_catalog.pg_type'::regclass and objid in (select types._id from types))
|
|
461
|
+
or (classid = 'pg_catalog.pg_enum'::regclass and objid in (select enums._id from enums))
|
|
462
|
+
or (classid = 'pg_catalog.pg_extension'::regclass and objid in (select extensions._id from extensions))
|
|
463
|
+
)
|
|
464
|
+
),
|
|
465
|
+
|
|
466
|
+
descriptions as (
|
|
467
|
+
select *
|
|
468
|
+
from pg_catalog.pg_description
|
|
469
|
+
where (
|
|
470
|
+
(classoid = 'pg_catalog.pg_namespace'::regclass and objoid in (select namespaces._id from namespaces))
|
|
471
|
+
or (classoid = 'pg_catalog.pg_class'::regclass and objoid in (select classes._id from classes))
|
|
472
|
+
or (classoid = 'pg_catalog.pg_attribute'::regclass and objoid in (select classes._id from classes) and objsubid > 0)
|
|
473
|
+
or (classoid = 'pg_catalog.pg_constraint'::regclass and objoid in (select constraints._id from constraints))
|
|
474
|
+
or (classoid = 'pg_catalog.pg_proc'::regclass and objoid in (select procs._id from procs))
|
|
475
|
+
or (classoid = 'pg_catalog.pg_type'::regclass and objoid in (select types._id from types))
|
|
476
|
+
or (classoid = 'pg_catalog.pg_enum'::regclass and objoid in (select enums._id from enums))
|
|
477
|
+
or (classoid = 'pg_catalog.pg_extension'::regclass and objoid in (select extensions._id from extensions))
|
|
478
|
+
)
|
|
479
|
+
),
|
|
480
|
+
|
|
481
|
+
am as (
|
|
482
|
+
select pg_am.oid as _id, *
|
|
483
|
+
from pg_catalog.pg_am
|
|
484
|
+
where ${scope.accessMethodPredicate}
|
|
485
|
+
)
|
|
486
|
+
select json_build_object(
|
|
487
|
+
'database',
|
|
488
|
+
(select row_to_json(database) from database),
|
|
489
|
+
|
|
490
|
+
'namespaces',
|
|
491
|
+
(select coalesce((select json_agg(row_to_json(namespaces) order by nspname) from namespaces), '[]'::json)),
|
|
492
|
+
|
|
493
|
+
'classes',
|
|
494
|
+
(select coalesce((select json_agg(row_to_json(classes) order by relnamespace, relname) from classes), '[]'::json)),
|
|
495
|
+
|
|
496
|
+
'attributes',
|
|
497
|
+
(select coalesce((select json_agg(row_to_json(attributes) order by attrelid, attnum) from attributes), '[]'::json)),
|
|
498
|
+
|
|
499
|
+
'constraints',
|
|
500
|
+
(select coalesce((select json_agg(row_to_json(constraints) order by connamespace, conrelid, conname) from constraints), '[]'::json)),
|
|
501
|
+
|
|
502
|
+
'procs',
|
|
503
|
+
(select coalesce((select json_agg(row_to_json(procs) order by pronamespace, proname, pg_get_function_identity_arguments(procs._id)) from procs), '[]'::json)),
|
|
504
|
+
|
|
505
|
+
'roles',
|
|
506
|
+
(select coalesce((select json_agg(row_to_json(roles) order by rolname) from roles), '[]'::json)),
|
|
507
|
+
|
|
508
|
+
'auth_members',
|
|
509
|
+
(select coalesce((select json_agg(row_to_json(auth_members) order by roleid, member, grantor) from auth_members), '[]'::json)),
|
|
510
|
+
|
|
511
|
+
'types',
|
|
512
|
+
(select coalesce((select json_agg(row_to_json(types) order by typnamespace, typname) from types), '[]'::json)),
|
|
513
|
+
|
|
514
|
+
'enums',
|
|
515
|
+
(select coalesce((select json_agg(row_to_json(enums) order by enumtypid, enumsortorder) from enums), '[]'::json)),
|
|
516
|
+
|
|
517
|
+
'extensions',
|
|
518
|
+
(select coalesce((select json_agg(row_to_json(extensions) order by extname) from extensions), '[]'::json)),
|
|
519
|
+
|
|
520
|
+
'indexes',
|
|
521
|
+
(select coalesce((select json_agg(row_to_json(indexes) order by indrelid, indexrelid) from indexes), '[]'::json)),
|
|
522
|
+
|
|
523
|
+
'inherits',
|
|
524
|
+
(select coalesce((select json_agg(row_to_json(inherits) order by inhrelid, inhseqno) from inherits), '[]'::json)),
|
|
525
|
+
|
|
526
|
+
'languages',
|
|
527
|
+
(select coalesce((select json_agg(row_to_json(languages) order by lanname) from languages), '[]'::json)),
|
|
528
|
+
|
|
529
|
+
'policies',
|
|
530
|
+
(select coalesce((select json_agg(row_to_json(policies) order by polrelid, polname) from policies), '[]'::json)),
|
|
531
|
+
|
|
532
|
+
'ranges',
|
|
533
|
+
(select coalesce((select json_agg(row_to_json(ranges) order by rngtypid) from ranges), '[]'::json)),
|
|
534
|
+
|
|
535
|
+
'depends',
|
|
536
|
+
(select coalesce((select json_agg(row_to_json(depends) order by classid, objid, objsubid, refclassid, refobjid, refobjsubid) from depends), '[]'::json)),
|
|
537
|
+
|
|
538
|
+
'descriptions',
|
|
539
|
+
(select coalesce((select json_agg(row_to_json(descriptions) order by objoid, classoid, objsubid) from descriptions), '[]'::json)),
|
|
540
|
+
|
|
541
|
+
'am',
|
|
542
|
+
(select coalesce((select json_agg(row_to_json(am) order by amname) from am), '[]'::json)),
|
|
543
|
+
|
|
544
|
+
'catalog_by_oid',
|
|
545
|
+
(
|
|
546
|
+
select json_object_agg(oid::text, relname order by relname asc)
|
|
547
|
+
from pg_class
|
|
548
|
+
where relnamespace = (
|
|
549
|
+
select oid
|
|
550
|
+
from pg_namespace
|
|
551
|
+
where nspname = 'pg_catalog'
|
|
552
|
+
)
|
|
553
|
+
and relkind = 'r'
|
|
554
|
+
),
|
|
555
|
+
|
|
556
|
+
'current_user',
|
|
557
|
+
current_user,
|
|
558
|
+
'pg_version',
|
|
559
|
+
version(),
|
|
560
|
+
'introspection_version',
|
|
561
|
+
1
|
|
562
|
+
)::text as introspection
|
|
563
|
+
`;
|
|
564
|
+
/**
|
|
565
|
+
* Builds a parameterized introspection plan scoped to the requested schemas
|
|
566
|
+
* and the transitive object dependencies required by their objects.
|
|
567
|
+
*/
|
|
568
|
+
const makeSchemaScopedIntrospectionPlan = (schemas, options = {}) => {
|
|
569
|
+
if (!Array.isArray(schemas) || schemas.length === 0) {
|
|
570
|
+
throw new Error('Schema-scoped introspection requires at least one schema');
|
|
571
|
+
}
|
|
572
|
+
if (options === null ||
|
|
573
|
+
typeof options !== 'object' ||
|
|
574
|
+
Array.isArray(options)) {
|
|
575
|
+
throw new Error('Schema-scoped introspection options must be an object');
|
|
576
|
+
}
|
|
577
|
+
const unsupportedOptions = Object.keys(options).filter((key) => key !== 'catalogTypes' && key !== 'capabilityExtensions');
|
|
578
|
+
if (unsupportedOptions.length > 0) {
|
|
579
|
+
throw new Error(`Unsupported schema-scoped introspection option(s): ${unsupportedOptions.join(', ')}`);
|
|
580
|
+
}
|
|
581
|
+
const catalogTypes = options.catalogTypes ?? 'all';
|
|
582
|
+
if (catalogTypes !== 'all' && catalogTypes !== 'dependency-closure') {
|
|
583
|
+
throw new Error(`Unsupported schema-scoped catalog type policy '${catalogTypes}'`);
|
|
584
|
+
}
|
|
585
|
+
const capabilityExtensions = options.capabilityExtensions ?? [];
|
|
586
|
+
if (!Array.isArray(capabilityExtensions)) {
|
|
587
|
+
throw new Error('Schema-scoped introspection capabilityExtensions must be an array');
|
|
588
|
+
}
|
|
589
|
+
const normalizedCapabilityExtensions = Array.from(new Set(capabilityExtensions.map((extension) => {
|
|
590
|
+
if (typeof extension !== 'string' ||
|
|
591
|
+
extension.length === 0 ||
|
|
592
|
+
extension.trim() !== extension ||
|
|
593
|
+
extension.includes('\0')) {
|
|
594
|
+
throw new Error('Schema-scoped introspection capabilityExtensions must contain exact non-empty extension names');
|
|
595
|
+
}
|
|
596
|
+
return extension;
|
|
597
|
+
})));
|
|
598
|
+
const normalized = Array.from(new Set(schemas.map((schema) => {
|
|
599
|
+
if (typeof schema !== 'string' || schema.length === 0) {
|
|
600
|
+
throw new Error('Schema-scoped introspection schemas must be non-empty strings');
|
|
601
|
+
}
|
|
602
|
+
if (schema.includes('\0')) {
|
|
603
|
+
throw new Error('Schema-scoped introspection schemas must not contain NUL bytes');
|
|
604
|
+
}
|
|
605
|
+
if (schema === 'information_schema' || schema.startsWith('pg_')) {
|
|
606
|
+
throw new Error(`Schema-scoped introspection cannot expose system schema '${schema}'`);
|
|
607
|
+
}
|
|
608
|
+
return schema;
|
|
609
|
+
})));
|
|
610
|
+
const dependencyClosureTypePredicate = "pg_type.oid = any (array(select object_id from object_closure where object_class = 'pg_catalog.pg_type'::regclass))";
|
|
611
|
+
const query = {
|
|
612
|
+
text: buildIntrospectionQuery({
|
|
613
|
+
ctes: SCOPED_CTES,
|
|
614
|
+
namespacePredicate: 'pg_namespace.oid = any (array(select scoped_namespaces._id from scoped_namespaces))',
|
|
615
|
+
classPredicate: "pg_class.oid = any (array(select object_id from object_closure where object_class = 'pg_catalog.pg_class'::regclass))",
|
|
616
|
+
constraintPredicate: "pg_constraint.oid = any (array(select object_id from object_closure where object_class = 'pg_catalog.pg_constraint'::regclass))",
|
|
617
|
+
procPredicate: "pg_proc.oid = any (array(select object_id from object_closure where object_class = 'pg_catalog.pg_proc'::regclass))",
|
|
618
|
+
typePredicate: catalogTypes === 'all'
|
|
619
|
+
? `${dependencyClosureTypePredicate} or pg_type.typnamespace = 'pg_catalog'::regnamespace`
|
|
620
|
+
: dependencyClosureTypePredicate,
|
|
621
|
+
extensionPredicate: 'pg_extension.oid = any (array(select installed_extensions._id from installed_extensions))',
|
|
622
|
+
languagePredicate: 'true',
|
|
623
|
+
accessMethodPredicate: 'true',
|
|
624
|
+
}),
|
|
625
|
+
values: [normalized, normalizedCapabilityExtensions],
|
|
626
|
+
};
|
|
627
|
+
return {
|
|
628
|
+
query,
|
|
629
|
+
scope: {
|
|
630
|
+
schemas: normalized,
|
|
631
|
+
catalogTypes,
|
|
632
|
+
capabilityExtensions: normalizedCapabilityExtensions,
|
|
633
|
+
},
|
|
634
|
+
};
|
|
635
|
+
};
|
|
636
|
+
exports.makeSchemaScopedIntrospectionPlan = makeSchemaScopedIntrospectionPlan;
|
|
637
|
+
/**
|
|
638
|
+
* Builds only the query portion of a schema-scoped introspection plan.
|
|
639
|
+
*
|
|
640
|
+
* Prefer `makeSchemaScopedIntrospectionPlan()` when the results will be
|
|
641
|
+
* parsed and validated by this package.
|
|
642
|
+
*/
|
|
643
|
+
const makeSchemaScopedIntrospectionQuery = (schemas, options = {}) => (0, exports.makeSchemaScopedIntrospectionPlan)(schemas, options).query;
|
|
644
|
+
exports.makeSchemaScopedIntrospectionQuery = makeSchemaScopedIntrospectionQuery;
|
|
645
|
+
function assertScopedNamespaces(introspection, requiredSchemas) {
|
|
646
|
+
const found = new Set(introspection.namespaces.map((namespace) => namespace.nspname));
|
|
647
|
+
const missing = requiredSchemas.filter((schema) => !found.has(schema));
|
|
648
|
+
if (missing.length > 0) {
|
|
649
|
+
throw new Error(`Schema-scoped introspection did not find required schema(s): ${missing.join(', ')}`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
function assertDependencyClosureTypes(introspection) {
|
|
653
|
+
const retainedTypeOids = new Set(introspection.types.map((type) => String(type._id)));
|
|
654
|
+
const introspectionLookups = introspection._lookups;
|
|
655
|
+
const requireType = (oid, objectKind, objectContext, field) => {
|
|
656
|
+
if (oid === null || oid === undefined || String(oid) === '0')
|
|
657
|
+
return;
|
|
658
|
+
const normalizedOid = String(oid);
|
|
659
|
+
// pg-introspection removes extension-owned composite resources from its
|
|
660
|
+
// public arrays after lookup hydration; the lookup remains available.
|
|
661
|
+
const resolves = retainedTypeOids.has(normalizedOid) ||
|
|
662
|
+
introspectionLookups?.typeById?.has(normalizedOid) === true;
|
|
663
|
+
if (!resolves) {
|
|
664
|
+
throw new Error(`Dependency-closure introspection retained ${objectKind} '${objectContext}' field '${field}' referencing missing pg_type OID '${normalizedOid}'`);
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
const requireTypes = (oids, objectKind, objectContext, field) => {
|
|
668
|
+
for (const oid of oids ?? []) {
|
|
669
|
+
requireType(oid, objectKind, objectContext, field);
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
for (const entity of introspection.classes) {
|
|
673
|
+
const context = `${entity.relname} (${entity._id})`;
|
|
674
|
+
requireType(entity.reltype, 'pg_class', context, 'reltype');
|
|
675
|
+
requireType(entity.reloftype, 'pg_class', context, 'reloftype');
|
|
676
|
+
}
|
|
677
|
+
for (const entity of introspection.attributes) {
|
|
678
|
+
requireType(entity.atttypid, 'pg_attribute', `${entity.attrelid}.${entity.attname}`, 'atttypid');
|
|
679
|
+
}
|
|
680
|
+
for (const entity of introspection.constraints) {
|
|
681
|
+
requireType(entity.contypid, 'pg_constraint', `${entity.conname} (${entity._id})`, 'contypid');
|
|
682
|
+
}
|
|
683
|
+
for (const entity of introspection.procs) {
|
|
684
|
+
const context = `${entity.proname} (${entity._id})`;
|
|
685
|
+
requireType(entity.prorettype, 'pg_proc', context, 'prorettype');
|
|
686
|
+
requireTypes(entity.proargtypes, 'pg_proc', context, 'proargtypes');
|
|
687
|
+
requireTypes(entity.proallargtypes, 'pg_proc', context, 'proallargtypes');
|
|
688
|
+
}
|
|
689
|
+
for (const entity of introspection.types) {
|
|
690
|
+
const context = `${entity.typname} (${entity._id})`;
|
|
691
|
+
requireType(entity.typbasetype, 'pg_type', context, 'typbasetype');
|
|
692
|
+
requireType(entity.typelem, 'pg_type', context, 'typelem');
|
|
693
|
+
requireType(entity.typarray, 'pg_type', context, 'typarray');
|
|
694
|
+
}
|
|
695
|
+
for (const entity of introspection.enums) {
|
|
696
|
+
requireType(entity.enumtypid, 'pg_enum', `${entity.enumlabel} (${entity._id})`, 'enumtypid');
|
|
697
|
+
}
|
|
698
|
+
for (const entity of introspection.ranges) {
|
|
699
|
+
const context = `range ${entity.rngtypid ?? 'unknown'}`;
|
|
700
|
+
requireType(entity.rngtypid, 'pg_range', context, 'rngtypid');
|
|
701
|
+
requireType(entity.rngsubtype, 'pg_range', context, 'rngsubtype');
|
|
702
|
+
requireType(entity.rngmultitypid, 'pg_range', context, 'rngmultitypid');
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
/** Validates that an introspection result satisfies its scoped query plan. */
|
|
706
|
+
function validateSchemaScopedIntrospection(introspection, plan) {
|
|
707
|
+
assertScopedNamespaces(introspection, plan.scope.schemas);
|
|
708
|
+
if (plan.scope.catalogTypes === 'dependency-closure') {
|
|
709
|
+
assertDependencyClosureTypes(introspection);
|
|
710
|
+
}
|
|
711
|
+
}
|