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