true-pg 0.7.0 → 0.8.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/lib/extractor/adapter.d.ts +8 -5
- package/lib/extractor/adapter.js +27 -12
- package/lib/extractor/canonicalise/composite.d.ts +7 -0
- package/lib/extractor/canonicalise/composite.js +53 -0
- package/lib/extractor/canonicalise/domain.d.ts +12 -0
- package/lib/extractor/canonicalise/domain.js +77 -0
- package/lib/extractor/canonicalise/enum.d.ts +8 -0
- package/lib/extractor/canonicalise/enum.js +22 -0
- package/lib/extractor/canonicalise/index.d.ts +11 -0
- package/lib/extractor/canonicalise/index.js +148 -0
- package/lib/extractor/canonicalise/parse.d.ts +43 -0
- package/lib/extractor/canonicalise/parse.js +50 -0
- package/lib/extractor/canonicalise/range.d.ts +11 -0
- package/lib/extractor/canonicalise/range.js +36 -0
- package/lib/extractor/canonicalise/resolve.d.ts +19 -0
- package/lib/extractor/canonicalise/resolve.js +59 -0
- package/lib/extractor/canonicalise/types.d.ts +70 -0
- package/lib/extractor/canonicalise/types.js +13 -0
- package/lib/extractor/index.d.ts +5 -2
- package/lib/extractor/index.js +5 -7
- package/lib/extractor/kinds/composite.d.ts +1 -1
- package/lib/extractor/kinds/composite.js +1 -1
- package/lib/extractor/kinds/domain.d.ts +1 -1
- package/lib/extractor/kinds/domain.js +1 -1
- package/lib/extractor/kinds/function.d.ts +1 -1
- package/lib/extractor/kinds/function.js +5 -7
- package/lib/extractor/kinds/materialized-view.d.ts +1 -1
- package/lib/extractor/kinds/materialized-view.js +4 -7
- package/lib/extractor/kinds/range.d.ts +1 -1
- package/lib/extractor/kinds/range.js +1 -2
- package/lib/extractor/kinds/table.d.ts +1 -1
- package/lib/extractor/kinds/table.js +2 -7
- package/lib/extractor/kinds/view.d.ts +1 -1
- package/lib/extractor/kinds/view.js +4 -7
- package/lib/imports.test.js +6 -14
- package/lib/index.js +2 -2
- package/lib/util.d.ts +3 -6
- package/lib/util.js +15 -11
- package/package.json +1 -1
- package/lib/extractor/canonicalise.d.ts +0 -110
- package/lib/extractor/canonicalise.js +0 -384
@@ -1,384 +0,0 @@
|
|
1
|
-
import { Deferred, unreachable } from "../util.js";
|
2
|
-
import { DbAdapter } from "./adapter.js";
|
3
|
-
const removeNulls = (o) => {
|
4
|
-
for (const key in o)
|
5
|
-
if (o[key] == null)
|
6
|
-
delete o[key];
|
7
|
-
return o;
|
8
|
-
};
|
9
|
-
/**
|
10
|
-
* Parses a PostgreSQL type name string to extract its base name,
|
11
|
-
* modifiers, and dimensions from explicit '[]' brackets.
|
12
|
-
*
|
13
|
-
* Examples:
|
14
|
-
*
|
15
|
-
* - `parseTypeName("varchar(50)")`
|
16
|
-
*
|
17
|
-
* `⤷ { baseTypeName: "varchar", modifiers: "50", dimensions: 0, originalTypeName: "varchar(50)" }`
|
18
|
-
*
|
19
|
-
* - `parseTypeName("int[]")`
|
20
|
-
*
|
21
|
-
* `⤷ { baseTypeName: "int", modifiers: null, dimensions: 1, originalTypeName: "int[]" }`
|
22
|
-
*
|
23
|
-
* - `parseTypeName("public.my_table[][]")`
|
24
|
-
*
|
25
|
-
* `⤷ { baseTypeName: "public.my_table", modifiers: null, dimensions: 2, originalTypeName: "public.my_table[][]" }`
|
26
|
-
*
|
27
|
-
* - `parseTypeName("numeric(10, 2)[]")`
|
28
|
-
*
|
29
|
-
* `⤷ { baseTypeName: "numeric", modifiers: "10, 2", dimensions: 1, originalTypeName: "numeric(10, 2)[]" }`
|
30
|
-
*
|
31
|
-
* - `parseTypeName("geometry(Point, 4326)")`
|
32
|
-
*
|
33
|
-
* `⤷ { baseTypeName: "geometry", modifiers: "Point, 4326", dimensions: 0, originalTypeName: "geometry(Point, 4326)" }`
|
34
|
-
*
|
35
|
-
* - `parseTypeName("_text")`
|
36
|
-
*
|
37
|
-
* `⤷ { baseTypeName: "_text", modifiers: null, dimensions: 0, originalTypeName: "_text" }`
|
38
|
-
*
|
39
|
-
* Internal arrays aren't handled here
|
40
|
-
*/
|
41
|
-
export function parseTypeName(type) {
|
42
|
-
let base = type;
|
43
|
-
let modifiers = null;
|
44
|
-
let dimensions = 0;
|
45
|
-
// 1. Extract modifiers (content within the last parentheses)
|
46
|
-
const modifierMatch = base.match(/\(([^)]*)\)$/);
|
47
|
-
if (modifierMatch) {
|
48
|
-
modifiers = modifierMatch[1];
|
49
|
-
base = base.substring(0, modifierMatch.index).trim();
|
50
|
-
}
|
51
|
-
// 2. Count and remove explicit array brackets '[]'
|
52
|
-
// Repeatedly remove '[]' from the end and count dimensions
|
53
|
-
while (base.endsWith("[]")) {
|
54
|
-
dimensions++;
|
55
|
-
base = base.slice(0, -2);
|
56
|
-
}
|
57
|
-
return { original: type, base, modifiers, dimensions };
|
58
|
-
}
|
59
|
-
export var Canonical;
|
60
|
-
(function (Canonical) {
|
61
|
-
let Kind;
|
62
|
-
(function (Kind) {
|
63
|
-
Kind["Base"] = "base";
|
64
|
-
Kind["Composite"] = "composite";
|
65
|
-
Kind["Domain"] = "domain";
|
66
|
-
Kind["Enum"] = "enum";
|
67
|
-
Kind["Range"] = "range";
|
68
|
-
Kind["Pseudo"] = "pseudo";
|
69
|
-
Kind["Unknown"] = "unknown";
|
70
|
-
})(Kind = Canonical.Kind || (Canonical.Kind = {}));
|
71
|
-
})(Canonical || (Canonical = {}));
|
72
|
-
/**
|
73
|
-
* Takes base type names (without modifiers/brackets), resolves them to their ultimate base type OID
|
74
|
-
* and internal array dimensions, and fetches basic kind information
|
75
|
-
*/
|
76
|
-
async function resolveBasicInfo(db, types) {
|
77
|
-
const query = `
|
78
|
-
WITH RECURSIVE
|
79
|
-
input(base_type_name, seq) AS (
|
80
|
-
SELECT * FROM unnest($1::text[], $2::int[])
|
81
|
-
),
|
82
|
-
type_resolution(seq, current_oid, level) AS (
|
83
|
-
-- Base case: Look up the initial base type name
|
84
|
-
SELECT i.seq, t.oid, 1
|
85
|
-
FROM input i JOIN pg_type t ON t.oid = i.base_type_name::regtype
|
86
|
-
UNION ALL
|
87
|
-
-- Recursive step: Follow typelem for standard arrays (_)
|
88
|
-
SELECT r.seq, t.typelem, r.level + 1
|
89
|
-
FROM type_resolution r JOIN pg_type t ON r.current_oid = t.oid
|
90
|
-
WHERE t.typelem != 0 AND left(t.typname, 1) = '_'
|
91
|
-
),
|
92
|
-
final_resolution AS (
|
93
|
-
-- Get the OID and max level (depth) for each sequence number
|
94
|
-
SELECT DISTINCT ON (seq) seq, current_oid AS base_type_oid, level
|
95
|
-
FROM type_resolution ORDER BY seq, level DESC
|
96
|
-
)
|
97
|
-
-- Combine resolution with basic type info fetching
|
98
|
-
SELECT
|
99
|
-
fr.seq,
|
100
|
-
fr.base_type_oid AS oid,
|
101
|
-
(fr.level - 1) AS internal_dimensions,
|
102
|
-
n.nspname AS schema,
|
103
|
-
t.typname AS name,
|
104
|
-
n.nspname || '.' || t.typname AS canonical_name,
|
105
|
-
CASE t.typtype
|
106
|
-
WHEN 'b' THEN 'base'::text WHEN 'c' THEN 'composite'::text WHEN 'd' THEN 'domain'::text
|
107
|
-
WHEN 'e' THEN 'enum'::text WHEN 'p' THEN 'pseudo'::text WHEN 'r' THEN 'range'::text
|
108
|
-
ELSE 'unknown'::text
|
109
|
-
END AS kind,
|
110
|
-
t.typrelid,
|
111
|
-
t.typbasetype,
|
112
|
-
COALESCE(r.rngsubtype, 0) AS rngsubtype
|
113
|
-
FROM final_resolution fr
|
114
|
-
JOIN pg_type t ON t.oid = fr.base_type_oid
|
115
|
-
JOIN pg_namespace n ON t.typnamespace = n.oid
|
116
|
-
LEFT JOIN pg_range r ON t.oid = r.rngtypid AND t.typtype = 'r'
|
117
|
-
ORDER BY fr.seq;
|
118
|
-
`;
|
119
|
-
// Need to handle the string 'kind' coming back from the DB
|
120
|
-
const results = await db.query(query, [
|
121
|
-
types.map(t => t.parsed.base),
|
122
|
-
types.map(t => t.seq),
|
123
|
-
]);
|
124
|
-
return results;
|
125
|
-
}
|
126
|
-
async function resolveBasicInfo1(db, type) {
|
127
|
-
const query = `
|
128
|
-
WITH RECURSIVE
|
129
|
-
input(base_type_name) AS (
|
130
|
-
SELECT $1::text
|
131
|
-
),
|
132
|
-
type_resolution(current_oid, level) AS (
|
133
|
-
-- Base case: Look up the initial base type name
|
134
|
-
SELECT t.oid, 1
|
135
|
-
FROM input i JOIN pg_type t ON t.oid = i.base_type_name::regtype
|
136
|
-
UNION ALL
|
137
|
-
-- Recursive step: Follow typelem for standard arrays (_)
|
138
|
-
SELECT t.typelem, r.level + 1
|
139
|
-
FROM type_resolution r JOIN pg_type t ON r.current_oid = t.oid
|
140
|
-
WHERE t.typelem != 0 AND left(t.typname, 1) = '_'
|
141
|
-
),
|
142
|
-
final_resolution AS (
|
143
|
-
-- Get the OID and max level (depth) for each sequence number
|
144
|
-
SELECT DISTINCT ON (current_oid) current_oid AS base_type_oid, level
|
145
|
-
FROM type_resolution ORDER BY current_oid, level DESC
|
146
|
-
)
|
147
|
-
-- Combine resolution with basic type info fetching
|
148
|
-
SELECT
|
149
|
-
fr.base_type_oid AS oid,
|
150
|
-
(fr.level - 1) AS internal_dimensions,
|
151
|
-
n.nspname AS schema,
|
152
|
-
t.typname AS name,
|
153
|
-
n.nspname || '.' || t.typname AS canonical_name,
|
154
|
-
CASE t.typtype
|
155
|
-
WHEN 'b' THEN 'base'::text WHEN 'c' THEN 'composite'::text WHEN 'd' THEN 'domain'::text
|
156
|
-
WHEN 'e' THEN 'enum'::text WHEN 'p' THEN 'pseudo'::text WHEN 'r' THEN 'range'::text
|
157
|
-
ELSE 'unknown'::text
|
158
|
-
END AS kind,
|
159
|
-
t.typrelid,
|
160
|
-
t.typbasetype,
|
161
|
-
COALESCE(r.rngsubtype, 0) AS rngsubtype
|
162
|
-
FROM final_resolution fr
|
163
|
-
JOIN pg_type t ON t.oid = fr.base_type_oid
|
164
|
-
JOIN pg_namespace n ON t.typnamespace = n.oid
|
165
|
-
LEFT JOIN pg_range r ON t.oid = r.rngtypid AND t.typtype = 'r';
|
166
|
-
`;
|
167
|
-
// Need to handle the string 'kind' coming back from the DB
|
168
|
-
const results = await db.query(query, [type.base]);
|
169
|
-
return results[0];
|
170
|
-
}
|
171
|
-
/** Fetches enum values for given enum type OIDs */
|
172
|
-
async function getEnumValues(db, oid) {
|
173
|
-
const query = `
|
174
|
-
SELECT array_agg(e.enumlabel ORDER BY e.enumsortorder) AS values
|
175
|
-
FROM pg_enum e
|
176
|
-
WHERE e.enumtypid = $1::oid
|
177
|
-
GROUP BY e.enumtypid;
|
178
|
-
`;
|
179
|
-
const results = await db.query(query, [oid]);
|
180
|
-
return results[0]?.values ?? [];
|
181
|
-
}
|
182
|
-
/** Fetches composite attributes for given composite type OIDs (typrelid) */
|
183
|
-
async function getCompositeAttributes(db, relid) {
|
184
|
-
const query = `
|
185
|
-
SELECT
|
186
|
-
a.attrelid AS relid,
|
187
|
-
jsonb_agg(
|
188
|
-
jsonb_build_object(
|
189
|
-
'name', a.attname,
|
190
|
-
'index', a.attnum,
|
191
|
-
'type_oid', a.atttypid,
|
192
|
-
'type_name', format_type(a.atttypid, null),
|
193
|
-
'comment', col_description(a.attrelid, a.attnum::int),
|
194
|
-
'defaultValue', pg_get_expr(d.adbin, d.adrelid),
|
195
|
-
'isNullable', NOT a.attnotnull,
|
196
|
-
'isIdentity', a.attidentity IS NOT NULL AND a.attidentity != '',
|
197
|
-
'generated', CASE WHEN a.attidentity = 'a' THEN 'ALWAYS' WHEN a.attidentity = 'd' THEN 'BY DEFAULT' WHEN a.attgenerated = 's' THEN 'ALWAYS' ELSE 'NEVER' END
|
198
|
-
) ORDER BY a.attnum
|
199
|
-
) AS attributes
|
200
|
-
FROM pg_attribute a
|
201
|
-
LEFT JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
|
202
|
-
WHERE a.attrelid = $1::oid AND a.attnum > 0 AND NOT a.attisdropped
|
203
|
-
GROUP BY a.attrelid;
|
204
|
-
`;
|
205
|
-
const results = await db.query(query, [relid]);
|
206
|
-
return results[0]?.attributes ?? [];
|
207
|
-
}
|
208
|
-
/** Recursive helper to find the ultimate base type OID for a domain */
|
209
|
-
async function findUltimateDomainBaseOid(db, oid) {
|
210
|
-
const query = `
|
211
|
-
WITH RECURSIVE domain_chain(oid, base_oid, level) AS (
|
212
|
-
SELECT $1::oid, t.typbasetype, 1
|
213
|
-
FROM pg_type t WHERE t.oid = $1::oid AND t.typtype = 'd'
|
214
|
-
UNION ALL
|
215
|
-
SELECT t.oid, t.typbasetype, dc.level + 1
|
216
|
-
FROM domain_chain dc JOIN pg_type t ON dc.base_oid = t.oid
|
217
|
-
WHERE t.typtype = 'd'
|
218
|
-
)
|
219
|
-
SELECT base_oid FROM domain_chain ORDER BY level DESC LIMIT 1;
|
220
|
-
`;
|
221
|
-
const result = await db.query(query, [oid]);
|
222
|
-
return result[0]?.base_oid ?? oid; // Return original if not a domain or chain ends
|
223
|
-
}
|
224
|
-
/** Fetches the canonical name of the ultimate base type for given domain OIDs */
|
225
|
-
async function getDomainBaseTypeName(db, typbasetype) {
|
226
|
-
const ultimateBaseOid = await findUltimateDomainBaseOid(db, typbasetype);
|
227
|
-
const query = `
|
228
|
-
SELECT t.oid, format('%I.%I', n.nspname, t.typname) AS name
|
229
|
-
FROM pg_type t JOIN pg_namespace n ON t.typnamespace = n.oid
|
230
|
-
WHERE t.oid = $1::oid
|
231
|
-
`;
|
232
|
-
const results = await db.query(query, [ultimateBaseOid]);
|
233
|
-
return results[0]?.name ?? "";
|
234
|
-
}
|
235
|
-
/** Fetches the canonical name of the subtype for given range OIDs */
|
236
|
-
async function getRangeSubtypeName(db, oid) {
|
237
|
-
const query = `
|
238
|
-
SELECT t.oid, format('%I.%I', n.nspname, t.typname) AS name
|
239
|
-
FROM pg_type t JOIN pg_namespace n ON t.typnamespace = n.oid
|
240
|
-
WHERE t.oid = $1::oid
|
241
|
-
`;
|
242
|
-
const results = await db.query(query, [oid]);
|
243
|
-
return results[0]?.name ?? "";
|
244
|
-
}
|
245
|
-
async function canonicaliseType(db, basic, rawTypeCache, canonicalCache) {
|
246
|
-
switch (basic.kind) {
|
247
|
-
case Canonical.Kind.Base:
|
248
|
-
return { kind: Canonical.Kind.Base };
|
249
|
-
case Canonical.Kind.Enum:
|
250
|
-
const enumValues = await getEnumValues(db, basic.oid);
|
251
|
-
if (enumValues.length === 0) {
|
252
|
-
throw new Error(`Enum ${basic.canonical_name} (OID: ${basic.oid}) lacks values.`);
|
253
|
-
}
|
254
|
-
return { kind: Canonical.Kind.Enum, enum_values: enumValues };
|
255
|
-
case Canonical.Kind.Composite: {
|
256
|
-
const rawAttributes = await getCompositeAttributes(db, basic.typrelid);
|
257
|
-
const attributeTypes = rawAttributes.map(attr => attr.type_name);
|
258
|
-
const canonicalAttributeTypes = await canonicalise(db, attributeTypes, rawTypeCache, canonicalCache); // Recursive call
|
259
|
-
const attributes = await Promise.all(rawAttributes.map(async (attr, index) => {
|
260
|
-
return removeNulls({
|
261
|
-
name: attr.name,
|
262
|
-
index: attr.index,
|
263
|
-
type: canonicalAttributeTypes[index],
|
264
|
-
comment: attr.comment,
|
265
|
-
defaultValue: attr.defaultValue,
|
266
|
-
isNullable: attr.isNullable,
|
267
|
-
isIdentity: attr.isIdentity,
|
268
|
-
generated: attr.generated,
|
269
|
-
});
|
270
|
-
}));
|
271
|
-
return { kind: Canonical.Kind.Composite, attributes };
|
272
|
-
}
|
273
|
-
case Canonical.Kind.Domain: {
|
274
|
-
const baseTypeName = await getDomainBaseTypeName(db, basic.typbasetype);
|
275
|
-
if (!baseTypeName) {
|
276
|
-
throw new Error(`Domain ${basic.canonical_name} (OID: ${basic.oid}) lacks a resolved base type name.`);
|
277
|
-
}
|
278
|
-
const canonicalBaseType = await canonicalise(db, [baseTypeName], rawTypeCache, canonicalCache); // Recursive call
|
279
|
-
return { kind: Canonical.Kind.Domain, domain_base_type: canonicalBaseType[0] };
|
280
|
-
}
|
281
|
-
case Canonical.Kind.Range: {
|
282
|
-
const subtypeName = await getRangeSubtypeName(db, basic.rngsubtype);
|
283
|
-
if (!subtypeName) {
|
284
|
-
throw new Error(`Range ${basic.canonical_name} (OID: ${basic.oid}) lacks a resolved subtype name.`);
|
285
|
-
}
|
286
|
-
const canonicalSubtype = await canonicalise(db, [subtypeName], rawTypeCache, canonicalCache); // Recursive call
|
287
|
-
return { kind: Canonical.Kind.Range, range_subtype: canonicalSubtype[0] };
|
288
|
-
}
|
289
|
-
case Canonical.Kind.Pseudo:
|
290
|
-
return { kind: Canonical.Kind.Pseudo };
|
291
|
-
case Canonical.Kind.Unknown:
|
292
|
-
throw new Error(`Canonicalising "${basic.original_type}" resulted in unknown kind: ${basic.canonical_name}`);
|
293
|
-
default:
|
294
|
-
return unreachable(basic.kind);
|
295
|
-
}
|
296
|
-
}
|
297
|
-
export const canonicalise = async (db, types, rawTypeCache, canonicalCache) => {
|
298
|
-
if (types.length === 0)
|
299
|
-
return [];
|
300
|
-
const withSeq = types.map((type, seq) => ({ type, seq }));
|
301
|
-
// final list of resolved canonical types
|
302
|
-
const results = [];
|
303
|
-
// unresolved types, awaiting resolution
|
304
|
-
const unresolved = [];
|
305
|
-
for (const { type, seq } of withSeq) {
|
306
|
-
// if the type is already resolved, add it to the results
|
307
|
-
const cached = rawTypeCache.get(type);
|
308
|
-
if (cached)
|
309
|
-
results.push(cached);
|
310
|
-
else {
|
311
|
-
// if the type is not resolved, create a deferred promise and add it to the unresolved list
|
312
|
-
const deferred = new Deferred();
|
313
|
-
rawTypeCache.set(type, deferred.promise);
|
314
|
-
results.push(deferred.promise);
|
315
|
-
const parsed = parseTypeName(type);
|
316
|
-
unresolved.push({ seq, parsed, deferred });
|
317
|
-
}
|
318
|
-
}
|
319
|
-
const resolved = await resolveBasicInfo(db, unresolved);
|
320
|
-
Promise.all(resolved.map(async (info, index) => {
|
321
|
-
const { parsed, deferred } = unresolved[index];
|
322
|
-
try {
|
323
|
-
const dimensions = parsed.dimensions + info.internal_dimensions;
|
324
|
-
const common = {
|
325
|
-
kind: info.kind,
|
326
|
-
oid: info.oid,
|
327
|
-
typrelid: info.typrelid,
|
328
|
-
typbasetype: info.typbasetype,
|
329
|
-
rngsubtype: info.rngsubtype,
|
330
|
-
canonical_name: info.canonical_name,
|
331
|
-
schema: info.schema,
|
332
|
-
name: info.name,
|
333
|
-
original_type: parsed.original,
|
334
|
-
modifiers: parsed.modifiers,
|
335
|
-
dimensions,
|
336
|
-
};
|
337
|
-
let cached = canonicalCache.get(info.canonical_name);
|
338
|
-
if (cached) {
|
339
|
-
const exclusive = await cached;
|
340
|
-
const result = { ...common, ...exclusive };
|
341
|
-
deferred.resolve(result);
|
342
|
-
}
|
343
|
-
else {
|
344
|
-
const deferred2 = new Deferred();
|
345
|
-
canonicalCache.set(info.canonical_name, deferred2.promise);
|
346
|
-
cached = deferred2.promise;
|
347
|
-
const exclusive = await canonicaliseType(db, common, rawTypeCache, canonicalCache);
|
348
|
-
deferred2.resolve(exclusive);
|
349
|
-
const result = { ...common, ...exclusive };
|
350
|
-
deferred.resolve(result);
|
351
|
-
}
|
352
|
-
}
|
353
|
-
catch (error) {
|
354
|
-
deferred.reject(error);
|
355
|
-
}
|
356
|
-
}));
|
357
|
-
const ret = await Promise.all(results);
|
358
|
-
return ret;
|
359
|
-
};
|
360
|
-
export const oidsToQualifiedNames = async (db, oids) => {
|
361
|
-
if (oids.length === 0)
|
362
|
-
return [];
|
363
|
-
const query = `
|
364
|
-
SELECT
|
365
|
-
input.ord,
|
366
|
-
format('%I.%I', n.nspname, t.typname) AS qualified_name
|
367
|
-
-- Use unnest WITH ORDINALITY because SQL doesn't guarantee order of SELECT results
|
368
|
-
FROM unnest($1::oid[]) WITH ORDINALITY AS input(oid, ord)
|
369
|
-
JOIN pg_type t ON t.oid = input.oid
|
370
|
-
JOIN pg_namespace n ON t.typnamespace = n.oid
|
371
|
-
ORDER BY input.ord;
|
372
|
-
`;
|
373
|
-
const results = await db.query(query, [oids]);
|
374
|
-
return results.map(r => r.qualified_name);
|
375
|
-
};
|
376
|
-
export const canonicaliseFromOids = async (db, oids) => {
|
377
|
-
if (oids.length === 0)
|
378
|
-
return [];
|
379
|
-
const types = await oidsToQualifiedNames(db, oids);
|
380
|
-
const unknown = types.filter(name => name == undefined);
|
381
|
-
if (unknown.length > 0)
|
382
|
-
throw new Error(`Failed to resolve OIDs to type names: ${unknown.join(", ")}`);
|
383
|
-
return db.canonicalise(types);
|
384
|
-
};
|