@biref/scanner 0.0.2 → 0.1.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/dist/codegen/cli.js.map +1 -1
- package/dist/index.cjs +1124 -157
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +205 -63
- package/dist/index.d.ts +205 -63
- package/dist/index.js +1118 -158
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,1089 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// src/adapters/mysql/MySQLMeta.ts
|
|
4
|
+
var MYSQL_ADAPTER_NAME = "mysql";
|
|
5
|
+
var MYSQL_URL_SCHEMES = ["mysql", "mariadb"];
|
|
6
|
+
|
|
7
|
+
// src/adapters/mysql/mapping/ConstraintMapper.ts
|
|
8
|
+
var ConstraintMapper = class _ConstraintMapper {
|
|
9
|
+
static toConstraint(raw) {
|
|
10
|
+
return {
|
|
11
|
+
name: raw.name,
|
|
12
|
+
kind: _ConstraintMapper.toKind(raw.kind),
|
|
13
|
+
fields: raw.columns,
|
|
14
|
+
expression: raw.definition
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
static toKind(kind) {
|
|
18
|
+
switch (kind) {
|
|
19
|
+
case "UNIQUE": {
|
|
20
|
+
return "unique";
|
|
21
|
+
}
|
|
22
|
+
case "CHECK": {
|
|
23
|
+
return "check";
|
|
24
|
+
}
|
|
25
|
+
default: {
|
|
26
|
+
return "custom";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// src/adapters/mysql/mapping/FieldMapper.ts
|
|
33
|
+
var ENUM_VALUES_RE = /^enum\((.+)\)$/i;
|
|
34
|
+
var SET_VALUES_RE = /^set\((.+)\)$/i;
|
|
35
|
+
var QUOTED_VALUE_RE = /'([^']*)'/g;
|
|
36
|
+
var FieldMapper = class _FieldMapper {
|
|
37
|
+
static toField(col, isIdentifier) {
|
|
38
|
+
return {
|
|
39
|
+
name: col.name,
|
|
40
|
+
type: _FieldMapper.toType(col),
|
|
41
|
+
nullable: col.nullable,
|
|
42
|
+
isIdentifier,
|
|
43
|
+
defaultValue: col.default_value,
|
|
44
|
+
description: col.description
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
static toType(col) {
|
|
48
|
+
const dataType = col.data_type.toLowerCase();
|
|
49
|
+
if (dataType === "enum") {
|
|
50
|
+
return {
|
|
51
|
+
category: "enum",
|
|
52
|
+
nativeType: col.column_type,
|
|
53
|
+
enumValues: _FieldMapper.parseEnumValues(col.column_type)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (dataType === "set") {
|
|
57
|
+
return {
|
|
58
|
+
category: "enum",
|
|
59
|
+
nativeType: col.column_type,
|
|
60
|
+
enumValues: _FieldMapper.parseSetValues(col.column_type)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (col.column_type.toLowerCase() === "tinyint(1)") {
|
|
64
|
+
return {
|
|
65
|
+
category: "boolean",
|
|
66
|
+
nativeType: col.column_type
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const base = {
|
|
70
|
+
category: _FieldMapper.categorize(dataType),
|
|
71
|
+
nativeType: col.column_type
|
|
72
|
+
};
|
|
73
|
+
if (col.numeric_precision !== null && (dataType === "decimal" || dataType === "numeric")) {
|
|
74
|
+
return {
|
|
75
|
+
...base,
|
|
76
|
+
precision: col.numeric_precision,
|
|
77
|
+
scale: col.numeric_scale ?? void 0
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (col.character_max_length !== null) {
|
|
81
|
+
return { ...base, length: col.character_max_length };
|
|
82
|
+
}
|
|
83
|
+
return base;
|
|
84
|
+
}
|
|
85
|
+
static parseEnumValues(columnType) {
|
|
86
|
+
const match = ENUM_VALUES_RE.exec(columnType);
|
|
87
|
+
if (!match) {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
return _FieldMapper.extractQuotedValues(match[1]);
|
|
91
|
+
}
|
|
92
|
+
static parseSetValues(columnType) {
|
|
93
|
+
const match = SET_VALUES_RE.exec(columnType);
|
|
94
|
+
if (!match) {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
return _FieldMapper.extractQuotedValues(match[1]);
|
|
98
|
+
}
|
|
99
|
+
static extractQuotedValues(raw) {
|
|
100
|
+
return Array.from(raw.matchAll(QUOTED_VALUE_RE), (m) => m[1]);
|
|
101
|
+
}
|
|
102
|
+
static categorize(dataType) {
|
|
103
|
+
switch (dataType) {
|
|
104
|
+
case "varchar":
|
|
105
|
+
case "char":
|
|
106
|
+
case "text":
|
|
107
|
+
case "tinytext":
|
|
108
|
+
case "mediumtext":
|
|
109
|
+
case "longtext": {
|
|
110
|
+
return "string";
|
|
111
|
+
}
|
|
112
|
+
case "tinyint":
|
|
113
|
+
case "smallint":
|
|
114
|
+
case "mediumint":
|
|
115
|
+
case "int":
|
|
116
|
+
case "bigint": {
|
|
117
|
+
return "integer";
|
|
118
|
+
}
|
|
119
|
+
case "decimal":
|
|
120
|
+
case "numeric":
|
|
121
|
+
case "float":
|
|
122
|
+
case "double":
|
|
123
|
+
case "real": {
|
|
124
|
+
return "decimal";
|
|
125
|
+
}
|
|
126
|
+
case "bit": {
|
|
127
|
+
return "binary";
|
|
128
|
+
}
|
|
129
|
+
case "date":
|
|
130
|
+
case "year": {
|
|
131
|
+
return "date";
|
|
132
|
+
}
|
|
133
|
+
case "datetime":
|
|
134
|
+
case "timestamp": {
|
|
135
|
+
return "timestamp";
|
|
136
|
+
}
|
|
137
|
+
case "time": {
|
|
138
|
+
return "time";
|
|
139
|
+
}
|
|
140
|
+
case "json": {
|
|
141
|
+
return "json";
|
|
142
|
+
}
|
|
143
|
+
case "binary":
|
|
144
|
+
case "varbinary":
|
|
145
|
+
case "blob":
|
|
146
|
+
case "tinyblob":
|
|
147
|
+
case "mediumblob":
|
|
148
|
+
case "longblob": {
|
|
149
|
+
return "binary";
|
|
150
|
+
}
|
|
151
|
+
default: {
|
|
152
|
+
return "unknown";
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// src/adapters/mysql/mysqlEnums.ts
|
|
159
|
+
var MySQLReferentialAction = {
|
|
160
|
+
Restrict: "RESTRICT",
|
|
161
|
+
Cascade: "CASCADE",
|
|
162
|
+
SetNull: "SET NULL",
|
|
163
|
+
NoAction: "NO ACTION",
|
|
164
|
+
SetDefault: "SET DEFAULT"
|
|
165
|
+
};
|
|
166
|
+
var MySQLIndexMethod = {
|
|
167
|
+
BTree: "BTREE",
|
|
168
|
+
Hash: "HASH"};
|
|
169
|
+
|
|
170
|
+
// src/adapters/mysql/mapping/IndexMapper.ts
|
|
171
|
+
var IndexMapper = class _IndexMapper {
|
|
172
|
+
static toIndex(raw) {
|
|
173
|
+
return {
|
|
174
|
+
name: raw.name,
|
|
175
|
+
fields: raw.columns,
|
|
176
|
+
unique: raw.unique,
|
|
177
|
+
kind: _IndexMapper.toKind(raw.method),
|
|
178
|
+
partial: false,
|
|
179
|
+
definition: null
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
static toKind(method) {
|
|
183
|
+
switch (method.toUpperCase()) {
|
|
184
|
+
case MySQLIndexMethod.BTree: {
|
|
185
|
+
return "btree";
|
|
186
|
+
}
|
|
187
|
+
case MySQLIndexMethod.Hash: {
|
|
188
|
+
return "hash";
|
|
189
|
+
}
|
|
190
|
+
default: {
|
|
191
|
+
return "unknown";
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// src/adapters/mysql/mapping/ReferenceMapper.ts
|
|
198
|
+
var ReferenceMapper = class _ReferenceMapper {
|
|
199
|
+
static toReference(fk) {
|
|
200
|
+
return {
|
|
201
|
+
name: fk.name,
|
|
202
|
+
fromEntity: { namespace: fk.from_namespace, name: fk.from_table },
|
|
203
|
+
fromFields: fk.from_columns,
|
|
204
|
+
toEntity: { namespace: fk.to_namespace, name: fk.to_table },
|
|
205
|
+
toFields: fk.to_columns,
|
|
206
|
+
confidence: 1,
|
|
207
|
+
onUpdate: _ReferenceMapper.toAction(fk.update_action),
|
|
208
|
+
onDelete: _ReferenceMapper.toAction(fk.delete_action)
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
static toAction(rule) {
|
|
212
|
+
switch (rule) {
|
|
213
|
+
case MySQLReferentialAction.Restrict: {
|
|
214
|
+
return "restrict";
|
|
215
|
+
}
|
|
216
|
+
case MySQLReferentialAction.Cascade: {
|
|
217
|
+
return "cascade";
|
|
218
|
+
}
|
|
219
|
+
case MySQLReferentialAction.SetNull: {
|
|
220
|
+
return "set-null";
|
|
221
|
+
}
|
|
222
|
+
case MySQLReferentialAction.SetDefault: {
|
|
223
|
+
return "set-default";
|
|
224
|
+
}
|
|
225
|
+
case MySQLReferentialAction.NoAction: {
|
|
226
|
+
return "no-action";
|
|
227
|
+
}
|
|
228
|
+
default: {
|
|
229
|
+
return "no-action";
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// src/adapters/mysql/mapping/EntityAssembler.ts
|
|
236
|
+
var EntityAssembler = class _EntityAssembler {
|
|
237
|
+
static assemble(inputs) {
|
|
238
|
+
const tableKeys = new Set(
|
|
239
|
+
inputs.tables.map((t) => _EntityAssembler.qualified(t.namespace, t.name))
|
|
240
|
+
);
|
|
241
|
+
const pkByEntity = _EntityAssembler.indexPrimaryKeys(inputs.primaryKeys);
|
|
242
|
+
const colsByEntity = _EntityAssembler.indexColumns(inputs.columns);
|
|
243
|
+
const relsByEntity = _EntityAssembler.buildRelationships(
|
|
244
|
+
inputs.foreignKeys,
|
|
245
|
+
tableKeys
|
|
246
|
+
);
|
|
247
|
+
const indexesByEntity = _EntityAssembler.buildIndexes(
|
|
248
|
+
inputs.indexes,
|
|
249
|
+
tableKeys
|
|
250
|
+
);
|
|
251
|
+
const constraintsByEntity = _EntityAssembler.buildConstraints(
|
|
252
|
+
inputs.constraints,
|
|
253
|
+
tableKeys
|
|
254
|
+
);
|
|
255
|
+
return inputs.tables.map((table) => {
|
|
256
|
+
const key = _EntityAssembler.qualified(table.namespace, table.name);
|
|
257
|
+
const pkColumns = pkByEntity.get(key) ?? [];
|
|
258
|
+
const pkSet = new Set(pkColumns);
|
|
259
|
+
const rawCols = colsByEntity.get(key) ?? [];
|
|
260
|
+
const fields = rawCols.map(
|
|
261
|
+
(col) => FieldMapper.toField(col, pkSet.has(col.name))
|
|
262
|
+
);
|
|
263
|
+
return {
|
|
264
|
+
namespace: table.namespace,
|
|
265
|
+
name: table.name,
|
|
266
|
+
fields,
|
|
267
|
+
identifier: pkColumns,
|
|
268
|
+
relationships: relsByEntity.get(key) ?? [],
|
|
269
|
+
constraints: constraintsByEntity.get(key) ?? [],
|
|
270
|
+
indexes: indexesByEntity.get(key) ?? [],
|
|
271
|
+
description: table.description
|
|
272
|
+
};
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
static indexPrimaryKeys(primaryKeys) {
|
|
276
|
+
const map = /* @__PURE__ */ new Map();
|
|
277
|
+
for (const pk of primaryKeys) {
|
|
278
|
+
map.set(
|
|
279
|
+
_EntityAssembler.qualified(pk.namespace, pk.table_name),
|
|
280
|
+
pk.columns
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
return map;
|
|
284
|
+
}
|
|
285
|
+
static indexColumns(columns) {
|
|
286
|
+
const map = /* @__PURE__ */ new Map();
|
|
287
|
+
for (const col of columns) {
|
|
288
|
+
const key = _EntityAssembler.qualified(col.namespace, col.table_name);
|
|
289
|
+
const list = map.get(key);
|
|
290
|
+
if (list) {
|
|
291
|
+
list.push(col);
|
|
292
|
+
} else {
|
|
293
|
+
map.set(key, [col]);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return map;
|
|
297
|
+
}
|
|
298
|
+
static buildRelationships(foreignKeys, tableKeys) {
|
|
299
|
+
const map = /* @__PURE__ */ new Map();
|
|
300
|
+
for (const fk of foreignKeys) {
|
|
301
|
+
const reference = ReferenceMapper.toReference(fk);
|
|
302
|
+
const fromKey = _EntityAssembler.qualified(
|
|
303
|
+
fk.from_namespace,
|
|
304
|
+
fk.from_table
|
|
305
|
+
);
|
|
306
|
+
const toKey = _EntityAssembler.qualified(fk.to_namespace, fk.to_table);
|
|
307
|
+
if (tableKeys.has(fromKey)) {
|
|
308
|
+
_EntityAssembler.push(map, fromKey, {
|
|
309
|
+
direction: "outbound",
|
|
310
|
+
reference
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
if (tableKeys.has(toKey)) {
|
|
314
|
+
_EntityAssembler.push(map, toKey, {
|
|
315
|
+
direction: "inbound",
|
|
316
|
+
reference
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return map;
|
|
321
|
+
}
|
|
322
|
+
static buildIndexes(indexes, tableKeys) {
|
|
323
|
+
const map = /* @__PURE__ */ new Map();
|
|
324
|
+
for (const idx of indexes) {
|
|
325
|
+
const key = _EntityAssembler.qualified(idx.namespace, idx.table_name);
|
|
326
|
+
if (!tableKeys.has(key)) {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
_EntityAssembler.push(map, key, IndexMapper.toIndex(idx));
|
|
330
|
+
}
|
|
331
|
+
return map;
|
|
332
|
+
}
|
|
333
|
+
static buildConstraints(constraints, tableKeys) {
|
|
334
|
+
const map = /* @__PURE__ */ new Map();
|
|
335
|
+
for (const cn of constraints) {
|
|
336
|
+
const key = _EntityAssembler.qualified(cn.namespace, cn.table_name);
|
|
337
|
+
if (!tableKeys.has(key)) {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
_EntityAssembler.push(map, key, ConstraintMapper.toConstraint(cn));
|
|
341
|
+
}
|
|
342
|
+
return map;
|
|
343
|
+
}
|
|
344
|
+
static push(map, key, value) {
|
|
345
|
+
const list = map.get(key);
|
|
346
|
+
if (list) {
|
|
347
|
+
list.push(value);
|
|
348
|
+
} else {
|
|
349
|
+
map.set(key, [value]);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
static qualified(namespace, name) {
|
|
353
|
+
return `${namespace}.${name}`;
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
// src/adapters/mysql/queries/inPlaceholders.ts
|
|
358
|
+
function inPlaceholders(count) {
|
|
359
|
+
return Array.from({ length: count }, () => "?").join(", ");
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// src/adapters/mysql/queries/ColumnsQuery.ts
|
|
363
|
+
var ColumnsQuery = class {
|
|
364
|
+
static async fetch(client, namespaces) {
|
|
365
|
+
const sql = `
|
|
366
|
+
SELECT
|
|
367
|
+
c.TABLE_SCHEMA,
|
|
368
|
+
c.TABLE_NAME,
|
|
369
|
+
c.COLUMN_NAME,
|
|
370
|
+
c.DATA_TYPE,
|
|
371
|
+
c.COLUMN_TYPE,
|
|
372
|
+
c.IS_NULLABLE,
|
|
373
|
+
c.COLUMN_DEFAULT,
|
|
374
|
+
c.COLUMN_COMMENT,
|
|
375
|
+
c.CHARACTER_MAXIMUM_LENGTH,
|
|
376
|
+
c.NUMERIC_PRECISION,
|
|
377
|
+
c.NUMERIC_SCALE
|
|
378
|
+
FROM information_schema.COLUMNS c
|
|
379
|
+
JOIN information_schema.TABLES t
|
|
380
|
+
ON t.TABLE_SCHEMA = c.TABLE_SCHEMA
|
|
381
|
+
AND t.TABLE_NAME = c.TABLE_NAME
|
|
382
|
+
WHERE t.TABLE_TYPE = 'BASE TABLE'
|
|
383
|
+
AND c.TABLE_SCHEMA IN (${inPlaceholders(namespaces.length)})
|
|
384
|
+
ORDER BY c.TABLE_SCHEMA, c.TABLE_NAME, c.ORDINAL_POSITION
|
|
385
|
+
`;
|
|
386
|
+
const [rows] = await client.execute(sql, [...namespaces]);
|
|
387
|
+
return rows.map((row) => ({
|
|
388
|
+
namespace: row.TABLE_SCHEMA,
|
|
389
|
+
table_name: row.TABLE_NAME,
|
|
390
|
+
name: row.COLUMN_NAME,
|
|
391
|
+
data_type: row.DATA_TYPE,
|
|
392
|
+
column_type: row.COLUMN_TYPE,
|
|
393
|
+
nullable: row.IS_NULLABLE === "YES",
|
|
394
|
+
default_value: row.COLUMN_DEFAULT,
|
|
395
|
+
description: row.COLUMN_COMMENT || null,
|
|
396
|
+
character_max_length: row.CHARACTER_MAXIMUM_LENGTH,
|
|
397
|
+
numeric_precision: row.NUMERIC_PRECISION,
|
|
398
|
+
numeric_scale: row.NUMERIC_SCALE
|
|
399
|
+
}));
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
// src/adapters/mysql/queries/ConstraintsQuery.ts
|
|
404
|
+
var ConstraintsQuery = class _ConstraintsQuery {
|
|
405
|
+
static async fetch(client, namespaces) {
|
|
406
|
+
const [uniques, checks] = await Promise.all([
|
|
407
|
+
_ConstraintsQuery.fetchUniques(client, namespaces),
|
|
408
|
+
_ConstraintsQuery.fetchChecks(client, namespaces)
|
|
409
|
+
]);
|
|
410
|
+
return [...uniques, ...checks];
|
|
411
|
+
}
|
|
412
|
+
static async fetchUniques(client, namespaces) {
|
|
413
|
+
const sql = `
|
|
414
|
+
SELECT
|
|
415
|
+
kcu.TABLE_SCHEMA,
|
|
416
|
+
kcu.TABLE_NAME,
|
|
417
|
+
kcu.CONSTRAINT_NAME,
|
|
418
|
+
kcu.COLUMN_NAME
|
|
419
|
+
FROM information_schema.KEY_COLUMN_USAGE kcu
|
|
420
|
+
JOIN information_schema.TABLE_CONSTRAINTS tc
|
|
421
|
+
ON tc.CONSTRAINT_SCHEMA = kcu.CONSTRAINT_SCHEMA
|
|
422
|
+
AND tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
|
|
423
|
+
AND tc.TABLE_NAME = kcu.TABLE_NAME
|
|
424
|
+
WHERE tc.CONSTRAINT_TYPE = 'UNIQUE'
|
|
425
|
+
AND kcu.TABLE_SCHEMA IN (${inPlaceholders(namespaces.length)})
|
|
426
|
+
ORDER BY kcu.TABLE_SCHEMA, kcu.TABLE_NAME, kcu.CONSTRAINT_NAME, kcu.ORDINAL_POSITION
|
|
427
|
+
`;
|
|
428
|
+
const [rows] = await client.execute(sql, [...namespaces]);
|
|
429
|
+
return _ConstraintsQuery.aggregateUniques(rows);
|
|
430
|
+
}
|
|
431
|
+
static aggregateUniques(rows) {
|
|
432
|
+
const map = /* @__PURE__ */ new Map();
|
|
433
|
+
for (const row of rows) {
|
|
434
|
+
const key = `${row.TABLE_SCHEMA}.${row.TABLE_NAME}.${row.CONSTRAINT_NAME}`;
|
|
435
|
+
const existing = map.get(key);
|
|
436
|
+
if (existing) {
|
|
437
|
+
existing.columns.push(row.COLUMN_NAME);
|
|
438
|
+
} else {
|
|
439
|
+
map.set(key, {
|
|
440
|
+
namespace: row.TABLE_SCHEMA,
|
|
441
|
+
table_name: row.TABLE_NAME,
|
|
442
|
+
name: row.CONSTRAINT_NAME,
|
|
443
|
+
kind: "UNIQUE",
|
|
444
|
+
definition: null,
|
|
445
|
+
columns: [row.COLUMN_NAME]
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return [...map.values()];
|
|
450
|
+
}
|
|
451
|
+
static async fetchChecks(client, namespaces) {
|
|
452
|
+
const sql = `
|
|
453
|
+
SELECT
|
|
454
|
+
cc.CONSTRAINT_SCHEMA,
|
|
455
|
+
tc.TABLE_NAME,
|
|
456
|
+
cc.CONSTRAINT_NAME,
|
|
457
|
+
cc.CHECK_CLAUSE
|
|
458
|
+
FROM information_schema.CHECK_CONSTRAINTS cc
|
|
459
|
+
JOIN information_schema.TABLE_CONSTRAINTS tc
|
|
460
|
+
ON tc.CONSTRAINT_SCHEMA = cc.CONSTRAINT_SCHEMA
|
|
461
|
+
AND tc.CONSTRAINT_NAME = cc.CONSTRAINT_NAME
|
|
462
|
+
WHERE tc.CONSTRAINT_TYPE = 'CHECK'
|
|
463
|
+
AND cc.CONSTRAINT_SCHEMA IN (${inPlaceholders(namespaces.length)})
|
|
464
|
+
ORDER BY cc.CONSTRAINT_SCHEMA, tc.TABLE_NAME, cc.CONSTRAINT_NAME
|
|
465
|
+
`;
|
|
466
|
+
try {
|
|
467
|
+
const [rows] = await client.execute(sql, [...namespaces]);
|
|
468
|
+
return rows.map((row) => ({
|
|
469
|
+
namespace: row.CONSTRAINT_SCHEMA,
|
|
470
|
+
table_name: row.TABLE_NAME,
|
|
471
|
+
name: row.CONSTRAINT_NAME,
|
|
472
|
+
kind: "CHECK",
|
|
473
|
+
definition: row.CHECK_CLAUSE,
|
|
474
|
+
columns: []
|
|
475
|
+
}));
|
|
476
|
+
} catch {
|
|
477
|
+
return [];
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
// src/adapters/mysql/queries/ForeignKeysQuery.ts
|
|
483
|
+
var ForeignKeysQuery = class _ForeignKeysQuery {
|
|
484
|
+
static async fetch(client, namespaces) {
|
|
485
|
+
const sql = `
|
|
486
|
+
SELECT
|
|
487
|
+
kcu.CONSTRAINT_NAME,
|
|
488
|
+
kcu.TABLE_SCHEMA,
|
|
489
|
+
kcu.TABLE_NAME,
|
|
490
|
+
kcu.COLUMN_NAME,
|
|
491
|
+
kcu.REFERENCED_TABLE_SCHEMA,
|
|
492
|
+
kcu.REFERENCED_TABLE_NAME,
|
|
493
|
+
kcu.REFERENCED_COLUMN_NAME,
|
|
494
|
+
rc.UPDATE_RULE,
|
|
495
|
+
rc.DELETE_RULE
|
|
496
|
+
FROM information_schema.KEY_COLUMN_USAGE kcu
|
|
497
|
+
JOIN information_schema.REFERENTIAL_CONSTRAINTS rc
|
|
498
|
+
ON rc.CONSTRAINT_SCHEMA = kcu.CONSTRAINT_SCHEMA
|
|
499
|
+
AND rc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
|
|
500
|
+
WHERE kcu.REFERENCED_TABLE_NAME IS NOT NULL
|
|
501
|
+
AND kcu.TABLE_SCHEMA IN (${inPlaceholders(namespaces.length)})
|
|
502
|
+
ORDER BY kcu.TABLE_SCHEMA, kcu.TABLE_NAME, kcu.CONSTRAINT_NAME, kcu.ORDINAL_POSITION
|
|
503
|
+
`;
|
|
504
|
+
const [rows] = await client.execute(sql, [...namespaces]);
|
|
505
|
+
return _ForeignKeysQuery.aggregate(rows);
|
|
506
|
+
}
|
|
507
|
+
static aggregate(rows) {
|
|
508
|
+
const map = /* @__PURE__ */ new Map();
|
|
509
|
+
for (const row of rows) {
|
|
510
|
+
const key = `${row.TABLE_SCHEMA}.${row.TABLE_NAME}.${row.CONSTRAINT_NAME}`;
|
|
511
|
+
const existing = map.get(key);
|
|
512
|
+
if (existing) {
|
|
513
|
+
existing.from_columns.push(row.COLUMN_NAME);
|
|
514
|
+
existing.to_columns.push(row.REFERENCED_COLUMN_NAME);
|
|
515
|
+
} else {
|
|
516
|
+
map.set(key, {
|
|
517
|
+
name: row.CONSTRAINT_NAME,
|
|
518
|
+
from_namespace: row.TABLE_SCHEMA,
|
|
519
|
+
from_table: row.TABLE_NAME,
|
|
520
|
+
from_columns: [row.COLUMN_NAME],
|
|
521
|
+
to_namespace: row.REFERENCED_TABLE_SCHEMA,
|
|
522
|
+
to_table: row.REFERENCED_TABLE_NAME,
|
|
523
|
+
to_columns: [row.REFERENCED_COLUMN_NAME],
|
|
524
|
+
update_action: row.UPDATE_RULE,
|
|
525
|
+
delete_action: row.DELETE_RULE
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return [...map.values()];
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
// src/adapters/mysql/queries/IndexesQuery.ts
|
|
534
|
+
var IndexesQuery = class _IndexesQuery {
|
|
535
|
+
static async fetch(client, namespaces) {
|
|
536
|
+
const sql = `
|
|
537
|
+
SELECT
|
|
538
|
+
s.TABLE_SCHEMA,
|
|
539
|
+
s.TABLE_NAME,
|
|
540
|
+
s.INDEX_NAME,
|
|
541
|
+
s.INDEX_TYPE,
|
|
542
|
+
s.NON_UNIQUE,
|
|
543
|
+
s.COLUMN_NAME
|
|
544
|
+
FROM information_schema.STATISTICS s
|
|
545
|
+
WHERE s.INDEX_NAME <> 'PRIMARY'
|
|
546
|
+
AND s.TABLE_SCHEMA IN (${inPlaceholders(namespaces.length)})
|
|
547
|
+
ORDER BY s.TABLE_SCHEMA, s.TABLE_NAME, s.INDEX_NAME, s.SEQ_IN_INDEX
|
|
548
|
+
`;
|
|
549
|
+
const [rows] = await client.execute(sql, [...namespaces]);
|
|
550
|
+
return _IndexesQuery.aggregate(rows);
|
|
551
|
+
}
|
|
552
|
+
static aggregate(rows) {
|
|
553
|
+
const map = /* @__PURE__ */ new Map();
|
|
554
|
+
for (const row of rows) {
|
|
555
|
+
const key = `${row.TABLE_SCHEMA}.${row.TABLE_NAME}.${row.INDEX_NAME}`;
|
|
556
|
+
const existing = map.get(key);
|
|
557
|
+
if (existing) {
|
|
558
|
+
existing.columns.push(row.COLUMN_NAME);
|
|
559
|
+
} else {
|
|
560
|
+
map.set(key, {
|
|
561
|
+
namespace: row.TABLE_SCHEMA,
|
|
562
|
+
table_name: row.TABLE_NAME,
|
|
563
|
+
name: row.INDEX_NAME,
|
|
564
|
+
method: row.INDEX_TYPE,
|
|
565
|
+
unique: row.NON_UNIQUE === 0,
|
|
566
|
+
partial: false,
|
|
567
|
+
definition: null,
|
|
568
|
+
columns: [row.COLUMN_NAME]
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return [...map.values()];
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
// src/adapters/mysql/queries/PrimaryKeysQuery.ts
|
|
577
|
+
var PrimaryKeysQuery = class _PrimaryKeysQuery {
|
|
578
|
+
static async fetch(client, namespaces) {
|
|
579
|
+
const sql = `
|
|
580
|
+
SELECT
|
|
581
|
+
kcu.TABLE_SCHEMA,
|
|
582
|
+
kcu.TABLE_NAME,
|
|
583
|
+
kcu.COLUMN_NAME
|
|
584
|
+
FROM information_schema.KEY_COLUMN_USAGE kcu
|
|
585
|
+
JOIN information_schema.TABLE_CONSTRAINTS tc
|
|
586
|
+
ON tc.CONSTRAINT_SCHEMA = kcu.CONSTRAINT_SCHEMA
|
|
587
|
+
AND tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
|
|
588
|
+
AND tc.TABLE_NAME = kcu.TABLE_NAME
|
|
589
|
+
WHERE tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
|
|
590
|
+
AND kcu.TABLE_SCHEMA IN (${inPlaceholders(namespaces.length)})
|
|
591
|
+
ORDER BY kcu.TABLE_SCHEMA, kcu.TABLE_NAME, kcu.ORDINAL_POSITION
|
|
592
|
+
`;
|
|
593
|
+
const [rows] = await client.execute(sql, [...namespaces]);
|
|
594
|
+
return _PrimaryKeysQuery.aggregate(rows);
|
|
595
|
+
}
|
|
596
|
+
static aggregate(rows) {
|
|
597
|
+
const map = /* @__PURE__ */ new Map();
|
|
598
|
+
for (const row of rows) {
|
|
599
|
+
const key = `${row.TABLE_SCHEMA}.${row.TABLE_NAME}`;
|
|
600
|
+
const existing = map.get(key);
|
|
601
|
+
if (existing) {
|
|
602
|
+
existing.columns.push(row.COLUMN_NAME);
|
|
603
|
+
} else {
|
|
604
|
+
map.set(key, {
|
|
605
|
+
namespace: row.TABLE_SCHEMA,
|
|
606
|
+
table_name: row.TABLE_NAME,
|
|
607
|
+
columns: [row.COLUMN_NAME]
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return [...map.values()];
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
// src/adapters/mysql/queries/TablesQuery.ts
|
|
616
|
+
var TablesQuery = class {
|
|
617
|
+
static async fetch(client, namespaces) {
|
|
618
|
+
const sql = `
|
|
619
|
+
SELECT
|
|
620
|
+
t.TABLE_SCHEMA AS TABLE_SCHEMA,
|
|
621
|
+
t.TABLE_NAME AS TABLE_NAME,
|
|
622
|
+
t.TABLE_COMMENT AS TABLE_COMMENT
|
|
623
|
+
FROM information_schema.TABLES t
|
|
624
|
+
WHERE t.TABLE_TYPE = 'BASE TABLE'
|
|
625
|
+
AND t.TABLE_SCHEMA IN (${inPlaceholders(namespaces.length)})
|
|
626
|
+
ORDER BY t.TABLE_SCHEMA, t.TABLE_NAME
|
|
627
|
+
`;
|
|
628
|
+
const [rows] = await client.execute(sql, [...namespaces]);
|
|
629
|
+
return rows.map((row) => ({
|
|
630
|
+
namespace: row.TABLE_SCHEMA,
|
|
631
|
+
name: row.TABLE_NAME,
|
|
632
|
+
description: row.TABLE_COMMENT || null
|
|
633
|
+
}));
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
// src/domain/model/DataModel.ts
|
|
638
|
+
var DataModel = class _DataModel {
|
|
639
|
+
constructor(kind, entities) {
|
|
640
|
+
this.kind = kind;
|
|
641
|
+
this.entities = entities;
|
|
642
|
+
this.index = new Map(
|
|
643
|
+
entities.map((e) => [_DataModel.qualifiedName(e.namespace, e.name), e])
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
kind;
|
|
647
|
+
entities;
|
|
648
|
+
index;
|
|
649
|
+
/**
|
|
650
|
+
* Build the lookup key for an entity. Exposed as a static helper so
|
|
651
|
+
* consumers can compute keys consistently when building their own
|
|
652
|
+
* indexes on top of a `DataModel`.
|
|
653
|
+
*/
|
|
654
|
+
static qualifiedName(namespace, name) {
|
|
655
|
+
return `${namespace}.${name}`;
|
|
656
|
+
}
|
|
657
|
+
getEntity(namespace, name) {
|
|
658
|
+
return this.index.get(_DataModel.qualifiedName(namespace, name));
|
|
659
|
+
}
|
|
660
|
+
hasEntity(namespace, name) {
|
|
661
|
+
return this.index.has(_DataModel.qualifiedName(namespace, name));
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* All relationships an entity participates in, in both directions.
|
|
665
|
+
* Returns an empty array if the entity is unknown.
|
|
666
|
+
*/
|
|
667
|
+
relationshipsOf(namespace, name) {
|
|
668
|
+
return this.getEntity(namespace, name)?.relationships ?? [];
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Outbound relationships: this entity holds the reference.
|
|
672
|
+
* In a relational store these correspond to FOREIGN KEY constraints
|
|
673
|
+
* declared by the entity itself.
|
|
674
|
+
*/
|
|
675
|
+
outboundRelationshipsOf(namespace, name) {
|
|
676
|
+
return this.relationshipsOf(namespace, name).filter(
|
|
677
|
+
(r) => r.direction === "outbound"
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Inbound relationships: other entities hold references pointing here.
|
|
682
|
+
*
|
|
683
|
+
* This is the differentiating feature of the SDK. Most introspection
|
|
684
|
+
* tools only surface outbound relationships, requiring consumers to
|
|
685
|
+
* know which other entities reference theirs ahead of time.
|
|
686
|
+
*/
|
|
687
|
+
inboundRelationshipsOf(namespace, name) {
|
|
688
|
+
return this.relationshipsOf(namespace, name).filter(
|
|
689
|
+
(r) => r.direction === "inbound"
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
// src/adapters/mysql/MySQLIntrospector.ts
|
|
695
|
+
var MySQLIntrospector = class _MySQLIntrospector {
|
|
696
|
+
constructor(client) {
|
|
697
|
+
this.client = client;
|
|
698
|
+
}
|
|
699
|
+
client;
|
|
700
|
+
name = MYSQL_ADAPTER_NAME;
|
|
701
|
+
kind = "relational";
|
|
702
|
+
async introspect(options = {}) {
|
|
703
|
+
const namespaces = await this.resolveNamespaces(options.namespaces);
|
|
704
|
+
const [tables, columns, primaryKeys, foreignKeys, indexes, constraints] = await Promise.all([
|
|
705
|
+
TablesQuery.fetch(this.client, namespaces),
|
|
706
|
+
ColumnsQuery.fetch(this.client, namespaces),
|
|
707
|
+
PrimaryKeysQuery.fetch(this.client, namespaces),
|
|
708
|
+
ForeignKeysQuery.fetch(this.client, namespaces),
|
|
709
|
+
IndexesQuery.fetch(this.client, namespaces),
|
|
710
|
+
ConstraintsQuery.fetch(this.client, namespaces)
|
|
711
|
+
]);
|
|
712
|
+
const filteredTables = _MySQLIntrospector.applyEntityFilters(
|
|
713
|
+
tables,
|
|
714
|
+
options
|
|
715
|
+
);
|
|
716
|
+
const entities = EntityAssembler.assemble({
|
|
717
|
+
tables: filteredTables,
|
|
718
|
+
columns,
|
|
719
|
+
primaryKeys,
|
|
720
|
+
foreignKeys,
|
|
721
|
+
indexes,
|
|
722
|
+
constraints
|
|
723
|
+
});
|
|
724
|
+
return new DataModel("relational", entities);
|
|
725
|
+
}
|
|
726
|
+
async resolveNamespaces(raw) {
|
|
727
|
+
if (raw === void 0) {
|
|
728
|
+
return this.currentDatabase();
|
|
729
|
+
}
|
|
730
|
+
if (raw === "all") {
|
|
731
|
+
const [rows] = await this.client.execute(
|
|
732
|
+
_MySQLIntrospector.ALL_SCHEMAS_SQL
|
|
733
|
+
);
|
|
734
|
+
return rows.map((row) => row.SCHEMA_NAME);
|
|
735
|
+
}
|
|
736
|
+
return raw;
|
|
737
|
+
}
|
|
738
|
+
async currentDatabase() {
|
|
739
|
+
const [rows] = await this.client.execute(
|
|
740
|
+
"SELECT DATABASE() AS db"
|
|
741
|
+
);
|
|
742
|
+
const db = rows[0]?.db;
|
|
743
|
+
if (!db) {
|
|
744
|
+
throw new Error(
|
|
745
|
+
"No database selected. Pass explicit namespaces or connect to a specific database."
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
return [db];
|
|
749
|
+
}
|
|
750
|
+
static ALL_SCHEMAS_SQL = `
|
|
751
|
+
SELECT s.SCHEMA_NAME
|
|
752
|
+
FROM information_schema.SCHEMATA s
|
|
753
|
+
WHERE s.SCHEMA_NAME NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')
|
|
754
|
+
ORDER BY s.SCHEMA_NAME
|
|
755
|
+
`;
|
|
756
|
+
static applyEntityFilters(tables, options) {
|
|
757
|
+
const { includeEntities: allow, excludeEntities: deny } = options;
|
|
758
|
+
if (!allow && !deny) {
|
|
759
|
+
return tables;
|
|
760
|
+
}
|
|
761
|
+
return tables.filter((t) => {
|
|
762
|
+
if (allow && !allow.includes(t.name)) {
|
|
763
|
+
return false;
|
|
764
|
+
}
|
|
765
|
+
if (deny?.includes(t.name)) {
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
return true;
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
|
|
773
|
+
// src/adapters/mysql/query/SqlIdentifier.ts
|
|
774
|
+
var SqlIdentifier = class _SqlIdentifier {
|
|
775
|
+
static quote(id) {
|
|
776
|
+
return `\`${id.replace(/`/g, "``")}\``;
|
|
777
|
+
}
|
|
778
|
+
static qualified(namespace, name) {
|
|
779
|
+
return `${_SqlIdentifier.quote(namespace)}.${_SqlIdentifier.quote(name)}`;
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
// src/adapters/mysql/query/FilterBuilder.ts
|
|
784
|
+
var FilterBuilder = class {
|
|
785
|
+
constructor(params) {
|
|
786
|
+
this.params = params;
|
|
787
|
+
}
|
|
788
|
+
params;
|
|
789
|
+
build(filter) {
|
|
790
|
+
const col = SqlIdentifier.quote(filter.field);
|
|
791
|
+
switch (filter.operator) {
|
|
792
|
+
case "eq": {
|
|
793
|
+
return `${col} = ${this.params.add(filter.value)}`;
|
|
794
|
+
}
|
|
795
|
+
case "neq": {
|
|
796
|
+
return `${col} <> ${this.params.add(filter.value)}`;
|
|
797
|
+
}
|
|
798
|
+
case "lt": {
|
|
799
|
+
return `${col} < ${this.params.add(filter.value)}`;
|
|
800
|
+
}
|
|
801
|
+
case "lte": {
|
|
802
|
+
return `${col} <= ${this.params.add(filter.value)}`;
|
|
803
|
+
}
|
|
804
|
+
case "gt": {
|
|
805
|
+
return `${col} > ${this.params.add(filter.value)}`;
|
|
806
|
+
}
|
|
807
|
+
case "gte": {
|
|
808
|
+
return `${col} >= ${this.params.add(filter.value)}`;
|
|
809
|
+
}
|
|
810
|
+
case "like": {
|
|
811
|
+
return `${col} LIKE ${this.params.add(filter.value)}`;
|
|
812
|
+
}
|
|
813
|
+
case "ilike": {
|
|
814
|
+
return `LOWER(${col}) LIKE LOWER(${this.params.add(filter.value)})`;
|
|
815
|
+
}
|
|
816
|
+
case "is-null": {
|
|
817
|
+
return `${col} IS NULL`;
|
|
818
|
+
}
|
|
819
|
+
case "is-not-null": {
|
|
820
|
+
return `${col} IS NOT NULL`;
|
|
821
|
+
}
|
|
822
|
+
case "in": {
|
|
823
|
+
return this.buildInClause(col, filter.value, "FALSE", "IN");
|
|
824
|
+
}
|
|
825
|
+
case "not-in": {
|
|
826
|
+
return this.buildInClause(col, filter.value, "TRUE", "NOT IN");
|
|
827
|
+
}
|
|
828
|
+
case "between": {
|
|
829
|
+
return this.buildBetween(col, filter.value);
|
|
830
|
+
}
|
|
831
|
+
default: {
|
|
832
|
+
const exhaustive = filter.operator;
|
|
833
|
+
throw new Error(`Unknown filter operator: ${String(exhaustive)}`);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
buildInClause(col, value, emptyFallback, keyword) {
|
|
838
|
+
if (!Array.isArray(value)) {
|
|
839
|
+
throw new Error(
|
|
840
|
+
`Operator '${keyword.toLowerCase()}' requires an array value.`
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
const values = value;
|
|
844
|
+
if (values.length === 0) {
|
|
845
|
+
return emptyFallback;
|
|
846
|
+
}
|
|
847
|
+
const placeholders = this.params.addAll(values);
|
|
848
|
+
return `${col} ${keyword} (${placeholders.join(", ")})`;
|
|
849
|
+
}
|
|
850
|
+
buildBetween(col, value) {
|
|
851
|
+
if (!Array.isArray(value) || value.length !== 2) {
|
|
852
|
+
throw new Error("Operator 'between' requires a [min, max] tuple.");
|
|
853
|
+
}
|
|
854
|
+
const tuple = value;
|
|
855
|
+
const lo = this.params.add(tuple[0]);
|
|
856
|
+
const hi = this.params.add(tuple[1]);
|
|
857
|
+
return `${col} BETWEEN ${lo} AND ${hi}`;
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
// src/adapters/mysql/query/SqlParamList.ts
|
|
862
|
+
var SqlParamList = class {
|
|
863
|
+
values = [];
|
|
864
|
+
/**
|
|
865
|
+
* Append a value and return a `?` placeholder.
|
|
866
|
+
*/
|
|
867
|
+
add(value) {
|
|
868
|
+
this.values.push(value);
|
|
869
|
+
return "?";
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Append many values and return their placeholders in order.
|
|
873
|
+
*/
|
|
874
|
+
addAll(values) {
|
|
875
|
+
return values.map((v) => this.add(v));
|
|
876
|
+
}
|
|
877
|
+
/** Number of values currently bound. */
|
|
878
|
+
get size() {
|
|
879
|
+
return this.values.length;
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Return a read-only copy of the bound values. The returned array
|
|
883
|
+
* is not backed by the internal storage.
|
|
884
|
+
*/
|
|
885
|
+
toArray() {
|
|
886
|
+
return [...this.values];
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
// src/adapters/mysql/MySQLQueryEngine.ts
|
|
891
|
+
var MySQLQueryEngine = class {
|
|
892
|
+
name = MYSQL_ADAPTER_NAME;
|
|
893
|
+
kind = "relational";
|
|
894
|
+
build(spec, model) {
|
|
895
|
+
const entity = model.getEntity(spec.namespace, spec.entity);
|
|
896
|
+
if (!entity) {
|
|
897
|
+
throw new Error(
|
|
898
|
+
`Entity "${spec.namespace}.${spec.entity}" not found in the data model.`
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
const fieldNames = new Set(entity.fields.map((f) => f.name));
|
|
902
|
+
const selectedFields = spec.select && spec.select.length > 0 ? [...spec.select] : entity.fields.map((f) => f.name);
|
|
903
|
+
for (const field of selectedFields) {
|
|
904
|
+
if (!fieldNames.has(field)) {
|
|
905
|
+
throw new Error(
|
|
906
|
+
`Field "${field}" does not exist on "${spec.namespace}.${spec.entity}".`
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
const params = new SqlParamList();
|
|
911
|
+
const filterBuilder = new FilterBuilder(params);
|
|
912
|
+
const selectClause = selectedFields.map(SqlIdentifier.quote).join(", ");
|
|
913
|
+
const fromClause = SqlIdentifier.qualified(spec.namespace, spec.entity);
|
|
914
|
+
let sql = `SELECT ${selectClause} FROM ${fromClause}`;
|
|
915
|
+
if (spec.filters && spec.filters.length > 0) {
|
|
916
|
+
const wherePieces = spec.filters.map((filter) => {
|
|
917
|
+
if (!fieldNames.has(filter.field)) {
|
|
918
|
+
throw new Error(
|
|
919
|
+
`Filter field "${filter.field}" does not exist on "${spec.namespace}.${spec.entity}".`
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
return filterBuilder.build(filter);
|
|
923
|
+
});
|
|
924
|
+
sql += ` WHERE ${wherePieces.join(" AND ")}`;
|
|
925
|
+
}
|
|
926
|
+
if (spec.orderBy && spec.orderBy.length > 0) {
|
|
927
|
+
const orderPieces = spec.orderBy.map((order) => {
|
|
928
|
+
if (!fieldNames.has(order.field)) {
|
|
929
|
+
throw new Error(
|
|
930
|
+
`Order field "${order.field}" does not exist on "${spec.namespace}.${spec.entity}".`
|
|
931
|
+
);
|
|
932
|
+
}
|
|
933
|
+
return `${SqlIdentifier.quote(order.field)} ${order.direction.toUpperCase()}`;
|
|
934
|
+
});
|
|
935
|
+
sql += ` ORDER BY ${orderPieces.join(", ")}`;
|
|
936
|
+
}
|
|
937
|
+
if (spec.limit !== void 0) {
|
|
938
|
+
sql += ` LIMIT ${Number(spec.limit)}`;
|
|
939
|
+
}
|
|
940
|
+
if (spec.offset !== void 0) {
|
|
941
|
+
sql += ` OFFSET ${Number(spec.offset)}`;
|
|
942
|
+
}
|
|
943
|
+
return {
|
|
944
|
+
command: sql,
|
|
945
|
+
params: params.toArray(),
|
|
946
|
+
metadata: {
|
|
947
|
+
engine: MYSQL_ADAPTER_NAME,
|
|
948
|
+
paramCount: params.size
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
|
|
954
|
+
// src/parsing/DefaultRecordParser.ts
|
|
955
|
+
var DefaultRecordParser = class {
|
|
956
|
+
parse(entity, row) {
|
|
957
|
+
const result = {};
|
|
958
|
+
for (const field of entity.fields) {
|
|
959
|
+
if (field.name in row) {
|
|
960
|
+
result[field.name] = this.coerce(field, row[field.name]);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
return result;
|
|
964
|
+
}
|
|
965
|
+
parseMany(entity, rows) {
|
|
966
|
+
return rows.map((row) => this.parse(entity, row));
|
|
967
|
+
}
|
|
968
|
+
coerce(field, raw) {
|
|
969
|
+
if (raw === null || raw === void 0) {
|
|
970
|
+
return null;
|
|
971
|
+
}
|
|
972
|
+
switch (field.type.category) {
|
|
973
|
+
case "string":
|
|
974
|
+
case "uuid":
|
|
975
|
+
case "enum": {
|
|
976
|
+
return String(raw);
|
|
977
|
+
}
|
|
978
|
+
case "integer": {
|
|
979
|
+
if (typeof raw === "bigint") {
|
|
980
|
+
return raw;
|
|
981
|
+
}
|
|
982
|
+
if (typeof raw === "number") {
|
|
983
|
+
return raw;
|
|
984
|
+
}
|
|
985
|
+
return Number(raw);
|
|
986
|
+
}
|
|
987
|
+
case "decimal": {
|
|
988
|
+
return typeof raw === "string" ? raw : String(raw);
|
|
989
|
+
}
|
|
990
|
+
case "boolean": {
|
|
991
|
+
return Boolean(raw);
|
|
992
|
+
}
|
|
993
|
+
case "date":
|
|
994
|
+
case "timestamp":
|
|
995
|
+
case "time": {
|
|
996
|
+
if (raw instanceof Date) {
|
|
997
|
+
return raw;
|
|
998
|
+
}
|
|
999
|
+
if (typeof raw === "string" || typeof raw === "number") {
|
|
1000
|
+
return new Date(raw);
|
|
1001
|
+
}
|
|
1002
|
+
return null;
|
|
1003
|
+
}
|
|
1004
|
+
case "json":
|
|
1005
|
+
case "binary":
|
|
1006
|
+
case "reference":
|
|
1007
|
+
case "unknown": {
|
|
1008
|
+
return raw;
|
|
1009
|
+
}
|
|
1010
|
+
case "array": {
|
|
1011
|
+
return Array.isArray(raw) ? raw : [];
|
|
1012
|
+
}
|
|
1013
|
+
default: {
|
|
1014
|
+
const exhaustive = field.type.category;
|
|
1015
|
+
return exhaustive;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
|
|
1021
|
+
// src/adapters/mysql/MySQLRecordParser.ts
|
|
1022
|
+
var BIGINT_NATIVE_TYPES = /* @__PURE__ */ new Set(["bigint"]);
|
|
1023
|
+
var MySQLRecordParser = class extends DefaultRecordParser {
|
|
1024
|
+
coerce(field, raw) {
|
|
1025
|
+
if (raw === null || raw === void 0) {
|
|
1026
|
+
return null;
|
|
1027
|
+
}
|
|
1028
|
+
if (field.type.category === "integer" && BIGINT_NATIVE_TYPES.has(field.type.nativeType.toLowerCase())) {
|
|
1029
|
+
if (typeof raw === "bigint") {
|
|
1030
|
+
return raw;
|
|
1031
|
+
}
|
|
1032
|
+
if (typeof raw === "string" || typeof raw === "number") {
|
|
1033
|
+
return BigInt(raw);
|
|
1034
|
+
}
|
|
1035
|
+
return null;
|
|
1036
|
+
}
|
|
1037
|
+
if (field.type.category === "boolean") {
|
|
1038
|
+
if (typeof raw === "number") {
|
|
1039
|
+
return raw !== 0;
|
|
1040
|
+
}
|
|
1041
|
+
return Boolean(raw);
|
|
1042
|
+
}
|
|
1043
|
+
if (field.type.category === "json" && typeof raw === "string") {
|
|
1044
|
+
try {
|
|
1045
|
+
return JSON.parse(raw);
|
|
1046
|
+
} catch {
|
|
1047
|
+
return raw;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
return super.coerce(field, raw);
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
|
|
1054
|
+
// src/adapters/mysql/MySQLRawQueryRunner.ts
|
|
1055
|
+
var MySQLRawQueryRunner = class {
|
|
1056
|
+
constructor(client) {
|
|
1057
|
+
this.client = client;
|
|
1058
|
+
}
|
|
1059
|
+
client;
|
|
1060
|
+
async run(built) {
|
|
1061
|
+
const [rows] = await this.client.execute(built.command, [
|
|
1062
|
+
...built.params
|
|
1063
|
+
]);
|
|
1064
|
+
return rows;
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1067
|
+
|
|
1068
|
+
// src/adapters/mysql/mysqlAdapter.ts
|
|
1069
|
+
var mysqlAdapter = {
|
|
1070
|
+
name: MYSQL_ADAPTER_NAME,
|
|
1071
|
+
urlSchemes: MYSQL_URL_SCHEMES,
|
|
1072
|
+
create(client) {
|
|
1073
|
+
return {
|
|
1074
|
+
name: MYSQL_ADAPTER_NAME,
|
|
1075
|
+
introspector: new MySQLIntrospector(client),
|
|
1076
|
+
engine: new MySQLQueryEngine(),
|
|
1077
|
+
runner: new MySQLRawQueryRunner(client),
|
|
1078
|
+
parser: new MySQLRecordParser(),
|
|
1079
|
+
urlSchemes: MYSQL_URL_SCHEMES
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
1083
|
+
function isMySQLAdapter(adapter) {
|
|
1084
|
+
return adapter.name === MYSQL_ADAPTER_NAME;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
3
1087
|
// src/adapters/postgres/pgEnums.ts
|
|
4
1088
|
var PgTypeCategory = {
|
|
5
1089
|
Array: "A"};
|
|
@@ -27,7 +1111,7 @@ var PgIndexMethod = {
|
|
|
27
1111
|
};
|
|
28
1112
|
|
|
29
1113
|
// src/adapters/postgres/mapping/ConstraintMapper.ts
|
|
30
|
-
var
|
|
1114
|
+
var ConstraintMapper2 = class _ConstraintMapper {
|
|
31
1115
|
static toConstraint(raw) {
|
|
32
1116
|
return {
|
|
33
1117
|
name: raw.name,
|
|
@@ -55,7 +1139,7 @@ var ConstraintMapper = class _ConstraintMapper {
|
|
|
55
1139
|
};
|
|
56
1140
|
|
|
57
1141
|
// src/adapters/postgres/mapping/FieldMapper.ts
|
|
58
|
-
var
|
|
1142
|
+
var FieldMapper2 = class _FieldMapper {
|
|
59
1143
|
static toField(col, isIdentifier) {
|
|
60
1144
|
return {
|
|
61
1145
|
name: col.name,
|
|
@@ -159,7 +1243,7 @@ var FieldMapper = class _FieldMapper {
|
|
|
159
1243
|
};
|
|
160
1244
|
|
|
161
1245
|
// src/adapters/postgres/mapping/IndexMapper.ts
|
|
162
|
-
var
|
|
1246
|
+
var IndexMapper2 = class _IndexMapper {
|
|
163
1247
|
static toIndex(raw) {
|
|
164
1248
|
return {
|
|
165
1249
|
name: raw.name,
|
|
@@ -198,7 +1282,7 @@ var IndexMapper = class _IndexMapper {
|
|
|
198
1282
|
};
|
|
199
1283
|
|
|
200
1284
|
// src/adapters/postgres/mapping/ReferenceMapper.ts
|
|
201
|
-
var
|
|
1285
|
+
var ReferenceMapper2 = class _ReferenceMapper {
|
|
202
1286
|
static toReference(fk) {
|
|
203
1287
|
return {
|
|
204
1288
|
name: fk.name,
|
|
@@ -236,7 +1320,7 @@ var ReferenceMapper = class _ReferenceMapper {
|
|
|
236
1320
|
};
|
|
237
1321
|
|
|
238
1322
|
// src/adapters/postgres/mapping/EntityAssembler.ts
|
|
239
|
-
var
|
|
1323
|
+
var EntityAssembler2 = class _EntityAssembler {
|
|
240
1324
|
static assemble(inputs) {
|
|
241
1325
|
const tableKeys = new Set(
|
|
242
1326
|
inputs.tables.map((t) => _EntityAssembler.qualified(t.namespace, t.name))
|
|
@@ -261,7 +1345,7 @@ var EntityAssembler = class _EntityAssembler {
|
|
|
261
1345
|
const pkSet = new Set(pkColumns);
|
|
262
1346
|
const rawCols = colsByEntity.get(key) ?? [];
|
|
263
1347
|
const fields = rawCols.map(
|
|
264
|
-
(col) =>
|
|
1348
|
+
(col) => FieldMapper2.toField(col, pkSet.has(col.name))
|
|
265
1349
|
);
|
|
266
1350
|
return {
|
|
267
1351
|
namespace: table.namespace,
|
|
@@ -301,7 +1385,7 @@ var EntityAssembler = class _EntityAssembler {
|
|
|
301
1385
|
static buildRelationships(foreignKeys, tableKeys) {
|
|
302
1386
|
const map = /* @__PURE__ */ new Map();
|
|
303
1387
|
for (const fk of foreignKeys) {
|
|
304
|
-
const reference =
|
|
1388
|
+
const reference = ReferenceMapper2.toReference(fk);
|
|
305
1389
|
const fromKey = _EntityAssembler.qualified(
|
|
306
1390
|
fk.from_namespace,
|
|
307
1391
|
fk.from_table
|
|
@@ -329,7 +1413,7 @@ var EntityAssembler = class _EntityAssembler {
|
|
|
329
1413
|
if (!tableKeys.has(key)) {
|
|
330
1414
|
continue;
|
|
331
1415
|
}
|
|
332
|
-
_EntityAssembler.push(map, key,
|
|
1416
|
+
_EntityAssembler.push(map, key, IndexMapper2.toIndex(idx));
|
|
333
1417
|
}
|
|
334
1418
|
return map;
|
|
335
1419
|
}
|
|
@@ -340,7 +1424,7 @@ var EntityAssembler = class _EntityAssembler {
|
|
|
340
1424
|
if (!tableKeys.has(key)) {
|
|
341
1425
|
continue;
|
|
342
1426
|
}
|
|
343
|
-
_EntityAssembler.push(map, key,
|
|
1427
|
+
_EntityAssembler.push(map, key, ConstraintMapper2.toConstraint(cn));
|
|
344
1428
|
}
|
|
345
1429
|
return map;
|
|
346
1430
|
}
|
|
@@ -365,7 +1449,7 @@ var POSTGRES_URL_SCHEMES = [
|
|
|
365
1449
|
];
|
|
366
1450
|
|
|
367
1451
|
// src/adapters/postgres/queries/ColumnsQuery.ts
|
|
368
|
-
var
|
|
1452
|
+
var ColumnsQuery2 = class _ColumnsQuery {
|
|
369
1453
|
static SQL = `
|
|
370
1454
|
SELECT
|
|
371
1455
|
n.nspname AS namespace,
|
|
@@ -418,7 +1502,7 @@ ORDER BY n.nspname, c.relname, a.attnum
|
|
|
418
1502
|
};
|
|
419
1503
|
|
|
420
1504
|
// src/adapters/postgres/queries/ConstraintsQuery.ts
|
|
421
|
-
var
|
|
1505
|
+
var ConstraintsQuery2 = class _ConstraintsQuery {
|
|
422
1506
|
static SQL = `
|
|
423
1507
|
SELECT
|
|
424
1508
|
n.nspname AS namespace,
|
|
@@ -450,7 +1534,7 @@ ORDER BY n.nspname, cls.relname, c.conname
|
|
|
450
1534
|
};
|
|
451
1535
|
|
|
452
1536
|
// src/adapters/postgres/queries/ForeignKeysQuery.ts
|
|
453
|
-
var
|
|
1537
|
+
var ForeignKeysQuery2 = class _ForeignKeysQuery {
|
|
454
1538
|
static SQL = `
|
|
455
1539
|
SELECT
|
|
456
1540
|
c.conname AS name,
|
|
@@ -487,7 +1571,7 @@ WHERE c.contype = 'f'
|
|
|
487
1571
|
};
|
|
488
1572
|
|
|
489
1573
|
// src/adapters/postgres/queries/IndexesQuery.ts
|
|
490
|
-
var
|
|
1574
|
+
var IndexesQuery2 = class _IndexesQuery {
|
|
491
1575
|
static SQL = `
|
|
492
1576
|
SELECT
|
|
493
1577
|
n.nspname AS namespace,
|
|
@@ -520,7 +1604,7 @@ ORDER BY n.nspname, t.relname, i.relname
|
|
|
520
1604
|
};
|
|
521
1605
|
|
|
522
1606
|
// src/adapters/postgres/queries/PrimaryKeysQuery.ts
|
|
523
|
-
var
|
|
1607
|
+
var PrimaryKeysQuery2 = class _PrimaryKeysQuery {
|
|
524
1608
|
static SQL = `
|
|
525
1609
|
SELECT
|
|
526
1610
|
n.nspname AS namespace,
|
|
@@ -545,7 +1629,7 @@ WHERE c.contype = 'p'
|
|
|
545
1629
|
};
|
|
546
1630
|
|
|
547
1631
|
// src/adapters/postgres/queries/TablesQuery.ts
|
|
548
|
-
var
|
|
1632
|
+
var TablesQuery2 = class _TablesQuery {
|
|
549
1633
|
static SQL = `
|
|
550
1634
|
SELECT
|
|
551
1635
|
n.nspname AS namespace,
|
|
@@ -563,63 +1647,6 @@ ORDER BY n.nspname, c.relname
|
|
|
563
1647
|
}
|
|
564
1648
|
};
|
|
565
1649
|
|
|
566
|
-
// src/domain/model/DataModel.ts
|
|
567
|
-
var DataModel = class _DataModel {
|
|
568
|
-
constructor(kind, entities) {
|
|
569
|
-
this.kind = kind;
|
|
570
|
-
this.entities = entities;
|
|
571
|
-
this.index = new Map(
|
|
572
|
-
entities.map((e) => [_DataModel.qualifiedName(e.namespace, e.name), e])
|
|
573
|
-
);
|
|
574
|
-
}
|
|
575
|
-
kind;
|
|
576
|
-
entities;
|
|
577
|
-
index;
|
|
578
|
-
/**
|
|
579
|
-
* Build the lookup key for an entity. Exposed as a static helper so
|
|
580
|
-
* consumers can compute keys consistently when building their own
|
|
581
|
-
* indexes on top of a `DataModel`.
|
|
582
|
-
*/
|
|
583
|
-
static qualifiedName(namespace, name) {
|
|
584
|
-
return `${namespace}.${name}`;
|
|
585
|
-
}
|
|
586
|
-
getEntity(namespace, name) {
|
|
587
|
-
return this.index.get(_DataModel.qualifiedName(namespace, name));
|
|
588
|
-
}
|
|
589
|
-
hasEntity(namespace, name) {
|
|
590
|
-
return this.index.has(_DataModel.qualifiedName(namespace, name));
|
|
591
|
-
}
|
|
592
|
-
/**
|
|
593
|
-
* All relationships an entity participates in, in both directions.
|
|
594
|
-
* Returns an empty array if the entity is unknown.
|
|
595
|
-
*/
|
|
596
|
-
relationshipsOf(namespace, name) {
|
|
597
|
-
return this.getEntity(namespace, name)?.relationships ?? [];
|
|
598
|
-
}
|
|
599
|
-
/**
|
|
600
|
-
* Outbound relationships: this entity holds the reference.
|
|
601
|
-
* In a relational store these correspond to FOREIGN KEY constraints
|
|
602
|
-
* declared by the entity itself.
|
|
603
|
-
*/
|
|
604
|
-
outboundRelationshipsOf(namespace, name) {
|
|
605
|
-
return this.relationshipsOf(namespace, name).filter(
|
|
606
|
-
(r) => r.direction === "outbound"
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
/**
|
|
610
|
-
* Inbound relationships: other entities hold references pointing here.
|
|
611
|
-
*
|
|
612
|
-
* This is the differentiating feature of the SDK. Most introspection
|
|
613
|
-
* tools only surface outbound relationships, requiring consumers to
|
|
614
|
-
* know which other entities reference theirs ahead of time.
|
|
615
|
-
*/
|
|
616
|
-
inboundRelationshipsOf(namespace, name) {
|
|
617
|
-
return this.relationshipsOf(namespace, name).filter(
|
|
618
|
-
(r) => r.direction === "inbound"
|
|
619
|
-
);
|
|
620
|
-
}
|
|
621
|
-
};
|
|
622
|
-
|
|
623
1650
|
// src/adapters/postgres/PostgresIntrospector.ts
|
|
624
1651
|
var DEFAULT_NAMESPACES = ["public"];
|
|
625
1652
|
var PostgresIntrospector = class _PostgresIntrospector {
|
|
@@ -632,18 +1659,18 @@ var PostgresIntrospector = class _PostgresIntrospector {
|
|
|
632
1659
|
async introspect(options = {}) {
|
|
633
1660
|
const namespaces = await this.resolveNamespaces(options.namespaces);
|
|
634
1661
|
const [tables, columns, primaryKeys, foreignKeys, indexes, constraints] = await Promise.all([
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
1662
|
+
TablesQuery2.fetch(this.client, namespaces),
|
|
1663
|
+
ColumnsQuery2.fetch(this.client, namespaces),
|
|
1664
|
+
PrimaryKeysQuery2.fetch(this.client, namespaces),
|
|
1665
|
+
ForeignKeysQuery2.fetch(this.client, namespaces),
|
|
1666
|
+
IndexesQuery2.fetch(this.client, namespaces),
|
|
1667
|
+
ConstraintsQuery2.fetch(this.client, namespaces)
|
|
641
1668
|
]);
|
|
642
1669
|
const filteredTables = _PostgresIntrospector.applyEntityFilters(
|
|
643
1670
|
tables,
|
|
644
1671
|
options
|
|
645
1672
|
);
|
|
646
|
-
const entities =
|
|
1673
|
+
const entities = EntityAssembler2.assemble({
|
|
647
1674
|
tables: filteredTables,
|
|
648
1675
|
columns,
|
|
649
1676
|
primaryKeys,
|
|
@@ -691,7 +1718,7 @@ ORDER BY n.nspname
|
|
|
691
1718
|
};
|
|
692
1719
|
|
|
693
1720
|
// src/adapters/postgres/query/SqlIdentifier.ts
|
|
694
|
-
var
|
|
1721
|
+
var SqlIdentifier2 = class _SqlIdentifier {
|
|
695
1722
|
static quote(id) {
|
|
696
1723
|
return `"${id.replace(/"/g, '""')}"`;
|
|
697
1724
|
}
|
|
@@ -701,13 +1728,13 @@ var SqlIdentifier = class _SqlIdentifier {
|
|
|
701
1728
|
};
|
|
702
1729
|
|
|
703
1730
|
// src/adapters/postgres/query/FilterBuilder.ts
|
|
704
|
-
var
|
|
1731
|
+
var FilterBuilder2 = class {
|
|
705
1732
|
constructor(params) {
|
|
706
1733
|
this.params = params;
|
|
707
1734
|
}
|
|
708
1735
|
params;
|
|
709
1736
|
build(filter) {
|
|
710
|
-
const col =
|
|
1737
|
+
const col = SqlIdentifier2.quote(filter.field);
|
|
711
1738
|
switch (filter.operator) {
|
|
712
1739
|
case "eq": {
|
|
713
1740
|
return `${col} = ${this.params.add(filter.value)}`;
|
|
@@ -779,7 +1806,7 @@ var FilterBuilder = class {
|
|
|
779
1806
|
};
|
|
780
1807
|
|
|
781
1808
|
// src/adapters/postgres/query/SqlParamList.ts
|
|
782
|
-
var
|
|
1809
|
+
var SqlParamList2 = class {
|
|
783
1810
|
values = [];
|
|
784
1811
|
/**
|
|
785
1812
|
* Append a value and return its placeholder (`$N`, where N is the
|
|
@@ -828,10 +1855,10 @@ var PostgresQueryEngine = class {
|
|
|
828
1855
|
);
|
|
829
1856
|
}
|
|
830
1857
|
}
|
|
831
|
-
const params = new
|
|
832
|
-
const filterBuilder = new
|
|
833
|
-
const selectClause = selectedFields.map(
|
|
834
|
-
const fromClause =
|
|
1858
|
+
const params = new SqlParamList2();
|
|
1859
|
+
const filterBuilder = new FilterBuilder2(params);
|
|
1860
|
+
const selectClause = selectedFields.map(SqlIdentifier2.quote).join(", ");
|
|
1861
|
+
const fromClause = SqlIdentifier2.qualified(spec.namespace, spec.entity);
|
|
835
1862
|
let sql = `SELECT ${selectClause} FROM ${fromClause}`;
|
|
836
1863
|
if (spec.filters && spec.filters.length > 0) {
|
|
837
1864
|
const wherePieces = spec.filters.map((filter) => {
|
|
@@ -851,7 +1878,7 @@ var PostgresQueryEngine = class {
|
|
|
851
1878
|
`Order field "${order.field}" does not exist on "${spec.namespace}.${spec.entity}".`
|
|
852
1879
|
);
|
|
853
1880
|
}
|
|
854
|
-
return `${
|
|
1881
|
+
return `${SqlIdentifier2.quote(order.field)} ${order.direction.toUpperCase()}`;
|
|
855
1882
|
});
|
|
856
1883
|
sql += ` ORDER BY ${orderPieces.join(", ")}`;
|
|
857
1884
|
}
|
|
@@ -872,81 +1899,14 @@ var PostgresQueryEngine = class {
|
|
|
872
1899
|
}
|
|
873
1900
|
};
|
|
874
1901
|
|
|
875
|
-
// src/parsing/DefaultRecordParser.ts
|
|
876
|
-
var DefaultRecordParser = class {
|
|
877
|
-
parse(entity, row) {
|
|
878
|
-
const result = {};
|
|
879
|
-
for (const field of entity.fields) {
|
|
880
|
-
if (field.name in row) {
|
|
881
|
-
result[field.name] = this.coerce(field, row[field.name]);
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
return result;
|
|
885
|
-
}
|
|
886
|
-
parseMany(entity, rows) {
|
|
887
|
-
return rows.map((row) => this.parse(entity, row));
|
|
888
|
-
}
|
|
889
|
-
coerce(field, raw) {
|
|
890
|
-
if (raw === null || raw === void 0) {
|
|
891
|
-
return null;
|
|
892
|
-
}
|
|
893
|
-
switch (field.type.category) {
|
|
894
|
-
case "string":
|
|
895
|
-
case "uuid":
|
|
896
|
-
case "enum": {
|
|
897
|
-
return String(raw);
|
|
898
|
-
}
|
|
899
|
-
case "integer": {
|
|
900
|
-
if (typeof raw === "bigint") {
|
|
901
|
-
return raw;
|
|
902
|
-
}
|
|
903
|
-
if (typeof raw === "number") {
|
|
904
|
-
return raw;
|
|
905
|
-
}
|
|
906
|
-
return Number(raw);
|
|
907
|
-
}
|
|
908
|
-
case "decimal": {
|
|
909
|
-
return typeof raw === "string" ? raw : String(raw);
|
|
910
|
-
}
|
|
911
|
-
case "boolean": {
|
|
912
|
-
return Boolean(raw);
|
|
913
|
-
}
|
|
914
|
-
case "date":
|
|
915
|
-
case "timestamp":
|
|
916
|
-
case "time": {
|
|
917
|
-
if (raw instanceof Date) {
|
|
918
|
-
return raw;
|
|
919
|
-
}
|
|
920
|
-
if (typeof raw === "string" || typeof raw === "number") {
|
|
921
|
-
return new Date(raw);
|
|
922
|
-
}
|
|
923
|
-
return null;
|
|
924
|
-
}
|
|
925
|
-
case "json":
|
|
926
|
-
case "binary":
|
|
927
|
-
case "reference":
|
|
928
|
-
case "unknown": {
|
|
929
|
-
return raw;
|
|
930
|
-
}
|
|
931
|
-
case "array": {
|
|
932
|
-
return Array.isArray(raw) ? raw : [];
|
|
933
|
-
}
|
|
934
|
-
default: {
|
|
935
|
-
const exhaustive = field.type.category;
|
|
936
|
-
return exhaustive;
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
};
|
|
941
|
-
|
|
942
1902
|
// src/adapters/postgres/PostgresRecordParser.ts
|
|
943
|
-
var
|
|
1903
|
+
var BIGINT_NATIVE_TYPES2 = /* @__PURE__ */ new Set(["int8", "bigint"]);
|
|
944
1904
|
var PostgresRecordParser = class extends DefaultRecordParser {
|
|
945
1905
|
coerce(field, raw) {
|
|
946
1906
|
if (raw === null || raw === void 0) {
|
|
947
1907
|
return null;
|
|
948
1908
|
}
|
|
949
|
-
if (field.type.category === "integer" &&
|
|
1909
|
+
if (field.type.category === "integer" && BIGINT_NATIVE_TYPES2.has(field.type.nativeType.toLowerCase())) {
|
|
950
1910
|
if (typeof raw === "bigint") {
|
|
951
1911
|
return raw;
|
|
952
1912
|
}
|
|
@@ -2255,6 +3215,11 @@ exports.CsvFormatter = CsvFormatter;
|
|
|
2255
3215
|
exports.DataModel = DataModel;
|
|
2256
3216
|
exports.DefaultRecordParser = DefaultRecordParser;
|
|
2257
3217
|
exports.JsonFormatter = JsonFormatter;
|
|
3218
|
+
exports.MYSQL_ADAPTER_NAME = MYSQL_ADAPTER_NAME;
|
|
3219
|
+
exports.MYSQL_URL_SCHEMES = MYSQL_URL_SCHEMES;
|
|
3220
|
+
exports.MySQLIntrospector = MySQLIntrospector;
|
|
3221
|
+
exports.MySQLQueryEngine = MySQLQueryEngine;
|
|
3222
|
+
exports.MySQLRecordParser = MySQLRecordParser;
|
|
2258
3223
|
exports.POSTGRES_ADAPTER_NAME = POSTGRES_ADAPTER_NAME;
|
|
2259
3224
|
exports.POSTGRES_URL_SCHEMES = POSTGRES_URL_SCHEMES;
|
|
2260
3225
|
exports.PostgresIntrospector = PostgresIntrospector;
|
|
@@ -2265,7 +3230,9 @@ exports.RawFormatter = RawFormatter;
|
|
|
2265
3230
|
exports.Scanner = Scanner;
|
|
2266
3231
|
exports.generateSchema = generateSchema;
|
|
2267
3232
|
exports.generateSchemaFiles = generateSchemaFiles;
|
|
3233
|
+
exports.isMySQLAdapter = isMySQLAdapter;
|
|
2268
3234
|
exports.isPostgresAdapter = isPostgresAdapter;
|
|
3235
|
+
exports.mysqlAdapter = mysqlAdapter;
|
|
2269
3236
|
exports.overridesScaffold = overridesScaffold;
|
|
2270
3237
|
exports.postgresAdapter = postgresAdapter;
|
|
2271
3238
|
exports.tsTypeFor = tsTypeFor;
|