uql-orm 0.63.0 → 0.65.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/bunSql/bunSqlQuerier.d.ts +3 -10
- package/dist/bunSql/bunSqlQuerier.js +2 -16
- package/dist/bunSql/bunSqlQuerierPool.d.ts +0 -2
- package/dist/bunSql/bunSqlQuerierPool.js +14 -16
- package/dist/d1/d1Querier.d.ts +13 -34
- package/dist/d1/d1Querier.js +1 -1
- package/dist/d1/d1QuerierPool.d.ts +3 -3
- package/dist/dialect/abstractDialect.d.ts +4 -9
- package/dist/dialect/abstractDialect.js +4 -5
- package/dist/dialect/abstractSqlDialect.d.ts +2 -2
- package/dist/dialect/index.d.ts +0 -1
- package/dist/dialect/index.js +2 -3
- package/dist/dialect/mysqlLikeSqlDialect.js +0 -3
- package/dist/dialect/pgLikeSqlDialect.d.ts +9 -1
- package/dist/dialect/pgLikeSqlDialect.js +8 -5
- package/dist/dialect/queryContext.d.ts +3 -3
- package/dist/entity/metadata/definition.d.ts +6 -0
- package/dist/entity/metadata/definition.js +109 -76
- package/dist/migrate/builder/expressions.js +1 -15
- package/dist/migrate/builder/migrationBuilder.d.ts +6 -28
- package/dist/migrate/builder/migrationBuilder.js +9 -83
- package/dist/migrate/builder/types.d.ts +14 -24
- package/dist/migrate/cli.d.ts +2 -7
- package/dist/migrate/cli.js +22 -43
- package/dist/migrate/codegen/entityCodeGenerator.d.ts +5 -0
- package/dist/migrate/codegen/entityCodeGenerator.js +29 -7
- package/dist/migrate/codegen/indexDecoratorSource.js +1 -5
- package/dist/migrate/codegen/migrationFile.d.ts +9 -1
- package/dist/migrate/codegen/migrationFile.js +2 -1
- package/dist/migrate/codegen/sourceLiteral.d.ts +2 -0
- package/dist/migrate/codegen/sourceLiteral.js +4 -0
- package/dist/migrate/ddl/indexDdl.d.ts +4 -2
- package/dist/migrate/ddl/indexDdl.js +16 -15
- package/dist/migrate/generator/mongoCommand.d.ts +9 -9
- package/dist/migrate/generator/mongoCommand.js +1 -1
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +21 -26
- package/dist/migrate/generator/mongoSchemaGenerator.js +103 -80
- package/dist/migrate/index.d.ts +2 -2
- package/dist/migrate/index.js +2 -2
- package/dist/migrate/indexPredicate.d.ts +8 -0
- package/dist/migrate/indexPredicate.js +52 -0
- package/dist/migrate/introspection/registry.d.ts +2 -2
- package/dist/migrate/introspection/registry.js +6 -11
- package/dist/migrate/migrationTarget.d.ts +23 -0
- package/dist/migrate/migrationTarget.js +50 -0
- package/dist/migrate/migrator.d.ts +18 -50
- package/dist/migrate/migrator.js +79 -199
- package/dist/migrate/schemaGenerator.d.ts +11 -14
- package/dist/migrate/schemaGenerator.js +42 -12
- package/dist/mongo/mongoDialect.d.ts +7 -2
- package/dist/mongo/mongoDialect.js +1 -4
- package/dist/mssql/mssqlDialect.js +0 -3
- package/dist/pglite/pgliteQuerier.d.ts +2 -6
- package/dist/pglite/pgliteQuerier.js +2 -9
- package/dist/postgres/index.d.ts +0 -1
- package/dist/postgres/index.js +0 -1
- package/dist/querier/abstractSharedHandleQuerierPool.d.ts +2 -5
- package/dist/querier/abstractSharedHandleQuerierPool.js +2 -5
- package/dist/querier/abstractSqlQuerier.d.ts +2 -3
- package/dist/querier/abstractSqlQuerier.js +8 -4
- package/dist/querier/cursorStream.d.ts +13 -0
- package/dist/{postgres/pgCursorStream.js → querier/cursorStream.js} +4 -13
- package/dist/schema/canonicalType.js +1 -1
- package/dist/schema/schemaASTBuilder.d.ts +2 -0
- package/dist/schema/schemaASTBuilder.js +43 -62
- package/dist/sqlite/abstractSqliteQuerier.d.ts +7 -3
- package/dist/sqlite/abstractSqliteQuerier.js +18 -4
- package/dist/sqlite/hranaQuerier.d.ts +1 -1
- package/dist/sqlite/localSqliteQuerierPool.d.ts +7 -0
- package/dist/sqlite/localSqliteQuerierPool.js +19 -0
- package/dist/sqlite/nodeSqliteQuerierPool.js +2 -3
- package/dist/sqlite/sqliteDialect.js +0 -3
- package/dist/sqlite/sqliteQuerier.d.ts +8 -5
- package/dist/sqlite/sqliteQuerier.js +4 -13
- package/dist/sqlite/sqliteQuerierPool.js +4 -4
- package/dist/turso/tursoSessionQuerier.d.ts +2 -2
- package/dist/turso/tursoSessionQuerier.js +16 -18
- package/dist/type/dialect.d.ts +25 -35
- package/dist/type/entity.d.ts +26 -28
- package/dist/type/migration.d.ts +11 -40
- package/dist/type/migratorDialect.d.ts +0 -9
- package/dist/type/migratorDialect.js +1 -16
- package/dist/type/querierPool.d.ts +0 -4
- package/dist/type/queryRaw.d.ts +2 -2
- package/dist/type/universalQuerier.d.ts +2 -2
- package/dist/util/ddlExpression.util.d.ts +3 -1
- package/dist/util/ddlExpression.util.js +14 -0
- package/dist/util/sqlLiteral.js +5 -3
- package/package.json +1 -3
- package/dist/migrate/schemaGeneratorAsync.d.ts +0 -7
- package/dist/migrate/schemaGeneratorAsync.js +0 -12
- package/dist/postgres/pgCursorStream.d.ts +0 -20
- package/dist/postgres/postgresWireDriverCapabilities.d.ts +0 -21
- package/dist/postgres/postgresWireDriverCapabilities.js +0 -21
- package/dist/sqlite/bunSqliteAdapter.bun.d.ts +0 -27
- package/dist/sqlite/bunSqliteAdapter.bun.js +0 -25
- package/dist/sqlite/nodeSqliteAdapter.d.ts +0 -33
- package/dist/sqlite/nodeSqliteAdapter.js +0 -25
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SOFT_DELETE_FILTER } from '../../type/index.js';
|
|
2
2
|
import { isInlinedExpression } from '../../util/field.util.js';
|
|
3
|
-
import {
|
|
3
|
+
import { entitySql, entityWhere, fieldOptionConflict, getKeys, hasKeys, isToManyRelation, lowerFirst, memberRefs, normalizeIndexColumn, upperFirst, definedEntries, } from '../../util/index.js';
|
|
4
4
|
import { ownRegistrations } from '../decorator/bag.js';
|
|
5
5
|
/**
|
|
6
6
|
* A map held on `globalThis` through the global symbol registry, so a single one survives multiple
|
|
@@ -54,10 +54,11 @@ export function defineRelation(entity, key, opts) {
|
|
|
54
54
|
* exist, and the registry holds data alone.
|
|
55
55
|
*/
|
|
56
56
|
export function relationRegistration({ mappedBy, references, ...opts }) {
|
|
57
|
+
const joined = references?.(keyMap(), keyMap());
|
|
57
58
|
return {
|
|
58
59
|
...opts,
|
|
59
60
|
...(mappedBy && { mappedBy: mappedBy(keyMap()) }),
|
|
60
|
-
...(
|
|
61
|
+
...(joined && { references: typeof joined === 'string' ? joined : [...joined] }),
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
64
|
/** Every entity's key map: a callback only reads one property off it, and that property is its own key. */
|
|
@@ -70,8 +71,8 @@ function addRelation(entity, key, registration) {
|
|
|
70
71
|
throw new TypeError(`'${entity.name}.${key}' needs an 'entity' getter, e.g. '@ManyToOne({ entity: () => Company })'.`);
|
|
71
72
|
}
|
|
72
73
|
if (registration.through && registration.references) {
|
|
73
|
-
throw new TypeError(`'${entity.name}.${key}' joins through a junction, whose
|
|
74
|
-
"pairs the declaring entity's columns with the target's instead.");
|
|
74
|
+
throw new TypeError(`'${entity.name}.${key}' joins through a junction, whose column referencing each side is the join; ` +
|
|
75
|
+
"'references' pairs the declaring entity's columns with the target's instead.");
|
|
75
76
|
}
|
|
76
77
|
const meta = ensureWritableMeta(entity);
|
|
77
78
|
// Registration writes into a map declared as resolved: `getMeta` runs `fillRelations`, which settles
|
|
@@ -312,51 +313,62 @@ export function getMeta(entity) {
|
|
|
312
313
|
if (meta.processedAt === meta.revision) {
|
|
313
314
|
return meta;
|
|
314
315
|
}
|
|
315
|
-
// Stamped before finalizing
|
|
316
|
-
//
|
|
317
|
-
//
|
|
316
|
+
// Stamped before finalizing: `fillInverseSide` reads the other side through `getMeta`, and with each
|
|
317
|
+
// side mapped by the other that recursion has to find this half-filled meta rather than run again.
|
|
318
|
+
// Unstamped when finalizing throws, so the next read reports the same mistake; running it again is
|
|
319
|
+
// harmless, since every step skips what it settled.
|
|
318
320
|
meta.processedAt = meta.revision;
|
|
319
|
-
|
|
321
|
+
try {
|
|
322
|
+
return fillRelations(meta);
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
meta.processedAt = undefined;
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
320
328
|
}
|
|
321
329
|
function fillRelations(meta) {
|
|
322
330
|
for (const [relKey, relation] of definedEntries(meta.relations)) {
|
|
323
|
-
// The registered view: `references` may be unset until this settles it.
|
|
331
|
+
// The registered view: `references` may be unset, or the one column a to-one names, until this settles it.
|
|
324
332
|
const relOpts = relation;
|
|
325
333
|
const at = `'${meta.entity.name}.${relKey}'`;
|
|
326
|
-
|
|
327
|
-
fillInverseSide(at, meta, relOpts, relOpts.mappedBy)
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
fillOwningSide(at, meta, relKey, relOpts);
|
|
331
|
-
}
|
|
332
|
-
if (!relOpts.references?.length) {
|
|
334
|
+
const references = relOpts.mappedBy
|
|
335
|
+
? fillInverseSide(at, meta, relOpts, relOpts.mappedBy)
|
|
336
|
+
: (pairedReferences(at, relOpts) ?? fillOwningSide(at, meta, relKey, relOpts));
|
|
337
|
+
if (!references.length) {
|
|
333
338
|
throw new TypeError(`${at} has no columns to join on.`);
|
|
334
339
|
}
|
|
335
|
-
// Hand-written `references` land here too: naming the columns says which they are, not that they exist.
|
|
336
|
-
if (relOpts.through) {
|
|
337
|
-
const junction = getMeta(relOpts.through());
|
|
338
|
-
for (const { local } of relOpts.references) {
|
|
339
|
-
if (junction.fields[local])
|
|
340
|
-
continue;
|
|
341
|
-
throw new TypeError(`${at} joins through '${junction.entity.name}', which has no '${local}' field: a junction's ` +
|
|
342
|
-
'columns are named after the entities it joins. Declare it.');
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
340
|
}
|
|
346
|
-
|
|
341
|
+
// A column `references` names is a foreign key with or without a relation over it, and one cannot point
|
|
342
|
+
// at a composite key: refused on first read, as a relation that cannot join is, not at the schema build.
|
|
343
|
+
foreignKeysOf(meta);
|
|
347
344
|
return meta;
|
|
348
345
|
}
|
|
346
|
+
/**
|
|
347
|
+
* The pairs a relation joins on, with the one column a to-one names paired with the target's primary key:
|
|
348
|
+
* on first read rather than at registration, which can run before the target has a key to pair with.
|
|
349
|
+
*/
|
|
350
|
+
function pairedReferences(at, relOpts) {
|
|
351
|
+
const { references } = relOpts;
|
|
352
|
+
if (typeof references !== 'string') {
|
|
353
|
+
return references;
|
|
354
|
+
}
|
|
355
|
+
const target = ensureMeta(relOpts.entity());
|
|
356
|
+
if (relOpts.mappedBy || isToManyRelation(relOpts) || target.ids.length > 1) {
|
|
357
|
+
throw new TypeError(`${at} names one column, '${references}', which only a to-one holding a foreign key to a one-column key ` +
|
|
358
|
+
'can: pair the columns, [{ local, foreign }].');
|
|
359
|
+
}
|
|
360
|
+
relOpts.references = [{ local: references, foreign: soleIdOf(target, 'a foreign key') }];
|
|
361
|
+
return relOpts.references;
|
|
362
|
+
}
|
|
349
363
|
function fillOwningSide(at, meta, relKey, relOpts) {
|
|
350
364
|
const relMeta = ensureMeta(relOpts.entity());
|
|
351
365
|
if (relOpts.through) {
|
|
352
366
|
// Both columns live on the junction, whatever the cardinality: `deleteRelations` and every dialect
|
|
353
367
|
// read them as junction columns. A composite key contributes one pair per column of it, which is
|
|
354
368
|
// what makes the join address a whole key rather than part.
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
];
|
|
359
|
-
return;
|
|
369
|
+
const junction = getMeta(relOpts.through());
|
|
370
|
+
relOpts.references = [...junctionReferences(at, junction, meta), ...junctionReferences(at, junction, relMeta)];
|
|
371
|
+
return relOpts.references;
|
|
360
372
|
}
|
|
361
373
|
if (isToManyRelation(relOpts)) {
|
|
362
374
|
throw new TypeError(`${at} is a to-many relation with no way to join: it needs 'mappedBy' (the field on the other side), ` +
|
|
@@ -366,16 +378,23 @@ function fillOwningSide(at, meta, relKey, relOpts) {
|
|
|
366
378
|
// property, so both are spelled from the referenced *property* - a column name is what the naming
|
|
367
379
|
// strategy makes of this afterwards.
|
|
368
380
|
const sole = relMeta.ids.length === 1;
|
|
369
|
-
|
|
381
|
+
const references = relMeta.ids.map((key) => ({
|
|
370
382
|
local: sole ? `${relKey}Id` : `${relKey}${upperFirst(key)}`,
|
|
371
383
|
foreign: key,
|
|
372
384
|
}));
|
|
385
|
+
// A column the entity declares would be joined by its name alone, so renaming either one would leave
|
|
386
|
+
// the other behind, still compiling.
|
|
387
|
+
const fields = meta.fields;
|
|
388
|
+
if (references.some(({ local }) => fields[local])) {
|
|
389
|
+
const own = lowerFirst(meta.entity.name);
|
|
390
|
+
throw new TypeError(`${at} joins ${references.map(({ local }) => `'${local}'`).join(', ')} by name, which a rename does not ` +
|
|
391
|
+
`follow: link them with ${sole ? `'references: (${own}) => ${own}.${references[0].local}'` : "'references' pairs"}.`);
|
|
392
|
+
}
|
|
373
393
|
// `typeFromReference` so schema generation resolves the referenced primary key's exact type
|
|
374
394
|
// (columnType, length, chained keys) rather than trusting the fallback, as it does for an
|
|
375
395
|
// explicit `@Field({ references })`.
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
fields[local] ??= {
|
|
396
|
+
for (const { local, foreign } of references) {
|
|
397
|
+
fields[local] = {
|
|
379
398
|
name: local,
|
|
380
399
|
type: fieldOf(relMeta, foreign).type ?? Number,
|
|
381
400
|
references: relOpts.entity,
|
|
@@ -383,12 +402,15 @@ function fillOwningSide(at, meta, relKey, relOpts) {
|
|
|
383
402
|
typeFromReference: true,
|
|
384
403
|
};
|
|
385
404
|
}
|
|
405
|
+
relOpts.references = references;
|
|
406
|
+
return references;
|
|
386
407
|
}
|
|
387
408
|
function fillInverseSide(at, meta, relOpts, mappedBy) {
|
|
388
409
|
const relEntity = relOpts.entity();
|
|
389
410
|
const relMeta = getMeta(relEntity);
|
|
390
|
-
|
|
391
|
-
|
|
411
|
+
const own = pairedReferences(at, relOpts);
|
|
412
|
+
if (own)
|
|
413
|
+
return own;
|
|
392
414
|
if (relMeta.fields[mappedBy]) {
|
|
393
415
|
if (meta.ids.length > 1) {
|
|
394
416
|
throw new TypeError(`${at} is mapped by '${relEntity.name}.${mappedBy}', one column, but the primary key of ` +
|
|
@@ -397,7 +419,7 @@ function fillInverseSide(at, meta, relOpts, mappedBy) {
|
|
|
397
419
|
}
|
|
398
420
|
// `local` is this entity's own key, as in every other pair.
|
|
399
421
|
relOpts.references = [{ local: meta.ids[0], foreign: mappedBy }];
|
|
400
|
-
return;
|
|
422
|
+
return relOpts.references;
|
|
401
423
|
}
|
|
402
424
|
// Authored view again: with each side mapped by the other, the target is still mid-resolution here and
|
|
403
425
|
// its own `references` are unset, which is what the second throw reports.
|
|
@@ -405,7 +427,8 @@ function fillInverseSide(at, meta, relOpts, mappedBy) {
|
|
|
405
427
|
if (!owner) {
|
|
406
428
|
throw new TypeError(`${at} is mapped by '${mappedBy}', which is neither a field nor a relation of '${relEntity.name}'.`);
|
|
407
429
|
}
|
|
408
|
-
|
|
430
|
+
const ownerReferences = pairedReferences(`'${relEntity.name}.${mappedBy}'`, owner);
|
|
431
|
+
if (!ownerReferences?.length) {
|
|
409
432
|
throw new TypeError(`${at} is mapped by '${relEntity.name}.${mappedBy}', an inverse side too, so neither owns the foreign key.`);
|
|
410
433
|
}
|
|
411
434
|
// Two different flips: a junction's pairs are the owner's group followed by ours, so the two groups
|
|
@@ -413,50 +436,55 @@ function fillInverseSide(at, meta, relOpts, mappedBy) {
|
|
|
413
436
|
// plain foreign key is one pair per key whose ends swap.
|
|
414
437
|
relOpts.references =
|
|
415
438
|
relOpts.cardinality === 'm1' || relOpts.cardinality === 'mm'
|
|
416
|
-
? [...
|
|
417
|
-
:
|
|
439
|
+
? [...ownerReferences.slice(relMeta.ids.length), ...ownerReferences.slice(0, relMeta.ids.length)]
|
|
440
|
+
: ownerReferences.map(({ local, foreign }) => ({ local: foreign, foreign: local }));
|
|
418
441
|
relOpts.through = owner.through;
|
|
442
|
+
return relOpts.references;
|
|
419
443
|
}
|
|
420
444
|
/**
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
*
|
|
424
|
-
* only because some *other* entity pointed `through` at it. Gaps only, so a declared relation keeps its
|
|
425
|
-
* own cardinality and `cascade`.
|
|
445
|
+
* The foreign keys an entity holds: each owning to-one's columns, and each `@Field({ references })` no
|
|
446
|
+
* relation joins on, as the many-to-one it describes, once its target has registered a key. What the
|
|
447
|
+
* schema build constrains and a junction joins by.
|
|
426
448
|
*/
|
|
427
|
-
function
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
449
|
+
export function foreignKeysOf(meta) {
|
|
450
|
+
const owning = definedEntries(meta.relations)
|
|
451
|
+
.map(([, relation]) => relation)
|
|
452
|
+
.filter(({ cardinality, mappedBy }) => cardinality === 'm1' || (cardinality === '11' && !mappedBy));
|
|
453
|
+
const joined = new Set(owning.flatMap(({ references }) => references.map(({ local }) => local)));
|
|
454
|
+
const columns = definedEntries(meta.fields).flatMap(([key, field]) => {
|
|
455
|
+
if (!field.references || joined.has(key))
|
|
456
|
+
return [];
|
|
457
|
+
const target = ensureMeta(field.references());
|
|
434
458
|
if (!target.ids.length)
|
|
435
|
-
|
|
459
|
+
return [];
|
|
436
460
|
if (target.ids.length > 1) {
|
|
437
|
-
throw new TypeError(`'${meta.entity.name}.${
|
|
461
|
+
throw new TypeError(`'${meta.entity.name}.${key}' cannot reference '${target.entity.name}', whose primary key is composite ` +
|
|
438
462
|
`(${target.ids.join(', ')}): a column points at one. Use ` +
|
|
439
463
|
`'@ManyToOne({ entity: () => ${target.entity.name} })', which declares one column per key.`);
|
|
440
464
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
465
|
+
return [{ entity: field.references, cardinality: 'm1', references: [{ local: key, foreign: target.ids[0] }] }];
|
|
466
|
+
});
|
|
467
|
+
return [...owning, ...columns];
|
|
468
|
+
}
|
|
469
|
+
/** Each key of `side`, paired with the one column of `junction` referencing it: a rename of either follows. */
|
|
470
|
+
function junctionReferences(at, junction, side) {
|
|
471
|
+
const pairs = foreignKeysOf(junction).flatMap(({ entity, references }) => entity() === side.entity ? references : []);
|
|
472
|
+
return side.ids.map((key) => {
|
|
473
|
+
const [pair, ...others] = pairs.filter(({ foreign }) => foreign === key);
|
|
474
|
+
const referenced = `'${side.entity.name}.${key}'`;
|
|
475
|
+
if (!pair) {
|
|
476
|
+
const declare = side.ids.length > 1
|
|
477
|
+
? `@ManyToOne({ entity: () => ${side.entity.name} })`
|
|
478
|
+
: `@Field({ references: () => ${side.entity.name} })`;
|
|
479
|
+
throw new TypeError(`${at} joins through '${junction.entity.name}', which has no column referencing ${referenced}: declare one, '${declare}'.`);
|
|
480
|
+
}
|
|
481
|
+
if (others.length) {
|
|
482
|
+
const columns = [pair, ...others].map(({ local }) => `'${local}'`).join(' and ');
|
|
483
|
+
throw new TypeError(`${at} joins through '${junction.entity.name}', where ${columns} each reference ${referenced}: a junction ` +
|
|
484
|
+
'needs exactly one column per key of each side.');
|
|
485
|
+
}
|
|
486
|
+
return { local: pair.local, foreign: key };
|
|
487
|
+
});
|
|
460
488
|
}
|
|
461
489
|
/** Every key the entity marks, in declaration order. More than one is a composite primary key. */
|
|
462
490
|
function getIdKeys(meta) {
|
|
@@ -490,7 +518,12 @@ function extendMeta(target, source) {
|
|
|
490
518
|
}
|
|
491
519
|
}
|
|
492
520
|
target.fields = { ...sourceFields, ...target.fields };
|
|
493
|
-
|
|
521
|
+
// A copy of each relation per entity: resolving one writes the columns it joins on into it, and those
|
|
522
|
+
// columns are the resolving entity's, so one shared object left every later entity without them.
|
|
523
|
+
target.relations = {
|
|
524
|
+
...Object.fromEntries(definedEntries(source.relations).map(([key, relation]) => [key, { ...relation }])),
|
|
525
|
+
...target.relations,
|
|
526
|
+
};
|
|
494
527
|
// Inherit user-defined filters from the parent (child overrides by name). The built-in soft-delete
|
|
495
528
|
// filter + `meta.softDelete` are (re)derived from the merged fields in `defineEntity`.
|
|
496
529
|
if (source.filters) {
|
|
@@ -99,21 +99,7 @@ function defaultLiteral(value, dialect) {
|
|
|
99
99
|
if (typeof value === 'boolean') {
|
|
100
100
|
return dialect.booleanLiteral === 'native' ? (value ? 'TRUE' : 'FALSE') : value ? '1' : '0';
|
|
101
101
|
}
|
|
102
|
-
|
|
103
|
-
return dialect.escape(ddlTimestamp(value));
|
|
104
|
-
}
|
|
105
|
-
if (typeof value === 'object') {
|
|
106
|
-
return dialect.escape(JSON.stringify(value));
|
|
107
|
-
}
|
|
108
|
-
return dialect.escape(value);
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* `YYYY-MM-DD HH:mm:ss.SSS` in UTC. Not `toISOString`, whose `T` and `Z` MySQL rejects outright
|
|
112
|
-
* ("Invalid default value"), and not `escape`'s local-time form, which would make the DDL depend on
|
|
113
|
-
* the machine that generated it.
|
|
114
|
-
*/
|
|
115
|
-
function ddlTimestamp(date) {
|
|
116
|
-
return date.toISOString().replace('T', ' ').replace('Z', '');
|
|
102
|
+
return dialect.escape(typeof value === 'object' && !(value instanceof Date) ? JSON.stringify(value) : value);
|
|
117
103
|
}
|
|
118
104
|
function expressionSql(expression, dialect) {
|
|
119
105
|
const { expressions } = DIALECT_DEFAULTS[dialect.dialectName];
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Migration Builder
|
|
3
|
-
*
|
|
4
|
-
* Provides type-safe migration operations with two modes:
|
|
5
|
-
* - OperationRecorder: Record operations only (for code generation)
|
|
6
|
-
* - MigrationBuilder: Execute DDL operations (for integration tests/runtime)
|
|
7
|
-
*/
|
|
8
1
|
import type { ForeignKeyAction } from '../../schema/types.js';
|
|
9
2
|
import type { IndexColumnInput, IndexOptions } from '../../type/index.js';
|
|
10
|
-
import type {
|
|
3
|
+
import type { SchemaGenerator } from '../../type/migration.js';
|
|
11
4
|
import type { AnyMigrationOperation, IAlterTableBuilder, IColumnBuilder, IColumnFactory, IMigrationBuilder, ITableBuilder } from './types.js';
|
|
12
5
|
type ForeignKeyTarget = {
|
|
13
6
|
table: string;
|
|
@@ -49,28 +42,13 @@ export declare class OperationRecorder implements IMigrationBuilder {
|
|
|
49
42
|
getOperations(): AnyMigrationOperation[];
|
|
50
43
|
}
|
|
51
44
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* @example
|
|
56
|
-
* ```typescript
|
|
57
|
-
* const builder = new MigrationBuilder(querier);
|
|
58
|
-
*
|
|
59
|
-
* await builder.createTable('users', (t) => {
|
|
60
|
-
* t.id();
|
|
61
|
-
* t.string('name');
|
|
62
|
-
* t.timestamps();
|
|
63
|
-
* });
|
|
64
|
-
* ```
|
|
45
|
+
* Records each operation, then runs the statements `generator` writes for it through `run`. Build one
|
|
46
|
+
* for a querier with `migrationBuilderFor`.
|
|
65
47
|
*/
|
|
66
48
|
export declare class MigrationBuilder extends OperationRecorder {
|
|
67
|
-
private readonly
|
|
68
|
-
private readonly
|
|
69
|
-
constructor(
|
|
70
|
-
/** The recorder's sink, plus the statements the operation turns into. */
|
|
49
|
+
private readonly generator;
|
|
50
|
+
private readonly run;
|
|
51
|
+
constructor(generator: SchemaGenerator, run: (statement: string) => Promise<unknown>);
|
|
71
52
|
protected record(operation: AnyMigrationOperation): Promise<void>;
|
|
72
|
-
private getCreateTableStatements;
|
|
73
|
-
private execute;
|
|
74
|
-
private operationToSql;
|
|
75
53
|
}
|
|
76
54
|
export {};
|
|
@@ -1,14 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Migration Builder
|
|
3
|
-
*
|
|
4
|
-
* Provides type-safe migration operations with two modes:
|
|
5
|
-
* - OperationRecorder: Record operations only (for code generation)
|
|
6
|
-
* - MigrationBuilder: Execute DDL operations (for integration tests/runtime)
|
|
7
|
-
*/
|
|
8
1
|
import { indexNameParts, normalizeIndexColumn } from '../../util/index.js';
|
|
9
2
|
import { derivedIndexName } from '../../util/sql.util.js';
|
|
10
|
-
import { createSchemaGenerator } from '../schemaGenerator.js';
|
|
11
|
-
import { splitSqlStatements } from './splitSqlStatements.js';
|
|
12
3
|
import { TableBuilder } from './tableBuilder.js';
|
|
13
4
|
/**
|
|
14
5
|
* One `createIndex` operation. Shared because the alter-table builder, the recorder and the
|
|
@@ -239,86 +230,21 @@ export class OperationRecorder {
|
|
|
239
230
|
}
|
|
240
231
|
}
|
|
241
232
|
/**
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
* @example
|
|
246
|
-
* ```typescript
|
|
247
|
-
* const builder = new MigrationBuilder(querier);
|
|
248
|
-
*
|
|
249
|
-
* await builder.createTable('users', (t) => {
|
|
250
|
-
* t.id();
|
|
251
|
-
* t.string('name');
|
|
252
|
-
* t.timestamps();
|
|
253
|
-
* });
|
|
254
|
-
* ```
|
|
233
|
+
* Records each operation, then runs the statements `generator` writes for it through `run`. Build one
|
|
234
|
+
* for a querier with `migrationBuilderFor`.
|
|
255
235
|
*/
|
|
256
236
|
export class MigrationBuilder extends OperationRecorder {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
constructor(
|
|
237
|
+
generator;
|
|
238
|
+
run;
|
|
239
|
+
constructor(generator, run) {
|
|
260
240
|
super();
|
|
261
|
-
this.
|
|
262
|
-
|
|
263
|
-
if (!generator) {
|
|
264
|
-
throw new TypeError(`Could not find a schema generator for dialect: ${querier.dialect.dialectName}`);
|
|
265
|
-
}
|
|
266
|
-
this.sqlGenerator = generator;
|
|
241
|
+
this.generator = generator;
|
|
242
|
+
this.run = run;
|
|
267
243
|
}
|
|
268
|
-
/** The recorder's sink, plus the statements the operation turns into. */
|
|
269
244
|
async record(operation) {
|
|
270
245
|
await super.record(operation);
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
getCreateTableStatements(operation) {
|
|
274
|
-
return this.sqlGenerator.generateCreateTableFromDefinition(operation.table);
|
|
275
|
-
}
|
|
276
|
-
async execute(operation) {
|
|
277
|
-
if (operation.type === 'createTable') {
|
|
278
|
-
for (const statement of this.getCreateTableStatements(operation)) {
|
|
279
|
-
await this.querier.run(statement);
|
|
280
|
-
}
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
const sql = this.operationToSql(operation);
|
|
284
|
-
if (sql) {
|
|
285
|
-
for (const statement of splitSqlStatements(sql)) {
|
|
286
|
-
await this.querier.run(statement);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
operationToSql(operation) {
|
|
291
|
-
switch (operation.type) {
|
|
292
|
-
case 'createTable':
|
|
293
|
-
// One line per statement in preview; execute() runs each separately (#87).
|
|
294
|
-
return this.getCreateTableStatements(operation).join('\n');
|
|
295
|
-
case 'dropTable':
|
|
296
|
-
return this.sqlGenerator.generateDropTable(operation.tableName, {
|
|
297
|
-
ifExists: operation.ifExists,
|
|
298
|
-
cascade: operation.cascade,
|
|
299
|
-
});
|
|
300
|
-
case 'renameTable':
|
|
301
|
-
return this.sqlGenerator.generateRenameTableSql(operation.oldName, operation.newName);
|
|
302
|
-
case 'addColumn':
|
|
303
|
-
return this.sqlGenerator.generateAddColumnSql(operation.tableName, operation.column);
|
|
304
|
-
case 'dropColumn':
|
|
305
|
-
return this.sqlGenerator.generateDropColumnSql(operation.tableName, operation.columnName);
|
|
306
|
-
case 'renameColumn':
|
|
307
|
-
return this.sqlGenerator.generateRenameColumnSql(operation.tableName, operation.oldName, operation.newName);
|
|
308
|
-
case 'alterColumn':
|
|
309
|
-
return this.sqlGenerator.generateAlterColumnSql(operation.tableName, operation.columnName, operation.changes);
|
|
310
|
-
case 'createIndex':
|
|
311
|
-
return this.sqlGenerator.generateCreateIndexFromDefinition(operation.tableName, operation.index);
|
|
312
|
-
case 'dropIndex':
|
|
313
|
-
return this.sqlGenerator.generateDropIndex(operation.tableName, operation.indexName);
|
|
314
|
-
case 'addForeignKey':
|
|
315
|
-
return this.sqlGenerator.generateAddForeignKeySql(operation.tableName, operation.foreignKey);
|
|
316
|
-
case 'dropForeignKey':
|
|
317
|
-
return this.sqlGenerator.generateDropForeignKeySql(operation.tableName, operation.constraintName);
|
|
318
|
-
case 'raw':
|
|
319
|
-
return operation.sql;
|
|
320
|
-
default:
|
|
321
|
-
return undefined;
|
|
246
|
+
for (const statement of this.generator.generateOperation(operation)) {
|
|
247
|
+
await this.run(statement);
|
|
322
248
|
}
|
|
323
249
|
}
|
|
324
250
|
}
|
|
@@ -116,20 +116,10 @@ export interface TableDefinition {
|
|
|
116
116
|
/** Table comment */
|
|
117
117
|
comment?: string;
|
|
118
118
|
}
|
|
119
|
-
/**
|
|
120
|
-
* Type of migration operation.
|
|
121
|
-
*/
|
|
122
|
-
export type MigrationOperationType = 'createTable' | 'dropTable' | 'renameTable' | 'alterTable' | 'addColumn' | 'dropColumn' | 'alterColumn' | 'renameColumn' | 'createIndex' | 'dropIndex' | 'addForeignKey' | 'dropForeignKey' | 'raw';
|
|
123
|
-
/**
|
|
124
|
-
* Base migration operation.
|
|
125
|
-
*/
|
|
126
|
-
export interface MigrationOperation {
|
|
127
|
-
type: MigrationOperationType;
|
|
128
|
-
}
|
|
129
119
|
/**
|
|
130
120
|
* Create table operation.
|
|
131
121
|
*/
|
|
132
|
-
export interface CreateTableOperation
|
|
122
|
+
export interface CreateTableOperation {
|
|
133
123
|
type: 'createTable';
|
|
134
124
|
table: TableDefinition;
|
|
135
125
|
ifNotExists?: boolean;
|
|
@@ -137,7 +127,7 @@ export interface CreateTableOperation extends MigrationOperation {
|
|
|
137
127
|
/**
|
|
138
128
|
* Drop table operation.
|
|
139
129
|
*/
|
|
140
|
-
export interface DropTableOperation
|
|
130
|
+
export interface DropTableOperation {
|
|
141
131
|
type: 'dropTable';
|
|
142
132
|
tableName: string;
|
|
143
133
|
ifExists?: boolean;
|
|
@@ -146,7 +136,7 @@ export interface DropTableOperation extends MigrationOperation {
|
|
|
146
136
|
/**
|
|
147
137
|
* Rename table operation.
|
|
148
138
|
*/
|
|
149
|
-
export interface RenameTableOperation
|
|
139
|
+
export interface RenameTableOperation {
|
|
150
140
|
type: 'renameTable';
|
|
151
141
|
oldName: string;
|
|
152
142
|
newName: string;
|
|
@@ -154,7 +144,7 @@ export interface RenameTableOperation extends MigrationOperation {
|
|
|
154
144
|
/**
|
|
155
145
|
* Add column operation.
|
|
156
146
|
*/
|
|
157
|
-
export interface AddColumnOperation
|
|
147
|
+
export interface AddColumnOperation {
|
|
158
148
|
type: 'addColumn';
|
|
159
149
|
tableName: string;
|
|
160
150
|
column: FullColumnDefinition;
|
|
@@ -162,7 +152,7 @@ export interface AddColumnOperation extends MigrationOperation {
|
|
|
162
152
|
/**
|
|
163
153
|
* Drop column operation.
|
|
164
154
|
*/
|
|
165
|
-
export interface DropColumnOperation
|
|
155
|
+
export interface DropColumnOperation {
|
|
166
156
|
type: 'dropColumn';
|
|
167
157
|
tableName: string;
|
|
168
158
|
columnName: string;
|
|
@@ -170,7 +160,7 @@ export interface DropColumnOperation extends MigrationOperation {
|
|
|
170
160
|
/**
|
|
171
161
|
* Alter column operation.
|
|
172
162
|
*/
|
|
173
|
-
export interface AlterColumnOperation
|
|
163
|
+
export interface AlterColumnOperation {
|
|
174
164
|
type: 'alterColumn';
|
|
175
165
|
tableName: string;
|
|
176
166
|
columnName: string;
|
|
@@ -179,7 +169,7 @@ export interface AlterColumnOperation extends MigrationOperation {
|
|
|
179
169
|
/**
|
|
180
170
|
* Rename column operation.
|
|
181
171
|
*/
|
|
182
|
-
export interface RenameColumnOperation
|
|
172
|
+
export interface RenameColumnOperation {
|
|
183
173
|
type: 'renameColumn';
|
|
184
174
|
tableName: string;
|
|
185
175
|
oldName: string;
|
|
@@ -188,7 +178,7 @@ export interface RenameColumnOperation extends MigrationOperation {
|
|
|
188
178
|
/**
|
|
189
179
|
* Create index operation.
|
|
190
180
|
*/
|
|
191
|
-
export interface CreateIndexOperation
|
|
181
|
+
export interface CreateIndexOperation {
|
|
192
182
|
type: 'createIndex';
|
|
193
183
|
tableName: string;
|
|
194
184
|
index: IndexDefinition;
|
|
@@ -197,7 +187,7 @@ export interface CreateIndexOperation extends MigrationOperation {
|
|
|
197
187
|
/**
|
|
198
188
|
* Drop index operation.
|
|
199
189
|
*/
|
|
200
|
-
export interface DropIndexOperation
|
|
190
|
+
export interface DropIndexOperation {
|
|
201
191
|
type: 'dropIndex';
|
|
202
192
|
tableName: string;
|
|
203
193
|
indexName: string;
|
|
@@ -206,7 +196,7 @@ export interface DropIndexOperation extends MigrationOperation {
|
|
|
206
196
|
/**
|
|
207
197
|
* Add foreign key operation.
|
|
208
198
|
*/
|
|
209
|
-
export interface AddForeignKeyOperation
|
|
199
|
+
export interface AddForeignKeyOperation {
|
|
210
200
|
type: 'addForeignKey';
|
|
211
201
|
tableName: string;
|
|
212
202
|
foreignKey: ForeignKeySchema;
|
|
@@ -214,7 +204,7 @@ export interface AddForeignKeyOperation extends MigrationOperation {
|
|
|
214
204
|
/**
|
|
215
205
|
* Drop foreign key operation.
|
|
216
206
|
*/
|
|
217
|
-
export interface DropForeignKeyOperation
|
|
207
|
+
export interface DropForeignKeyOperation {
|
|
218
208
|
type: 'dropForeignKey';
|
|
219
209
|
tableName: string;
|
|
220
210
|
constraintName: string;
|
|
@@ -222,7 +212,7 @@ export interface DropForeignKeyOperation extends MigrationOperation {
|
|
|
222
212
|
/**
|
|
223
213
|
* Raw SQL operation (escape hatch).
|
|
224
214
|
*/
|
|
225
|
-
export interface RawSqlOperation
|
|
215
|
+
export interface RawSqlOperation {
|
|
226
216
|
type: 'raw';
|
|
227
217
|
sql: string;
|
|
228
218
|
}
|
|
@@ -387,7 +377,7 @@ export interface IAlterTableBuilder {
|
|
|
387
377
|
* Interface for the main migration builder.
|
|
388
378
|
*/
|
|
389
379
|
export interface IMigrationBuilder {
|
|
390
|
-
/** Create a
|
|
380
|
+
/** Create a table as `callback` declares it; on MongoDB a collection, whose callback declares only indexes. */
|
|
391
381
|
createTable(name: string, callback: (table: ITableBuilder) => void): Promise<void>;
|
|
392
382
|
/** Drop a table */
|
|
393
383
|
dropTable(name: string, options?: {
|
|
@@ -420,6 +410,6 @@ export interface IMigrationBuilder {
|
|
|
420
410
|
}): Promise<void>;
|
|
421
411
|
/** Drop a foreign key */
|
|
422
412
|
dropForeignKey(tableName: string, constraintName: string): Promise<void>;
|
|
423
|
-
/** Execute raw SQL */
|
|
413
|
+
/** Execute raw SQL, the escape hatch for anything the builder does not model. */
|
|
424
414
|
raw(sql: string): Promise<void>;
|
|
425
415
|
}
|
package/dist/migrate/cli.d.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import type {
|
|
3
|
-
import type { Config, MigratorDialect } from '../type/index.js';
|
|
2
|
+
import type { Config } from '../type/index.js';
|
|
4
3
|
import { Migrator } from './migrator.js';
|
|
5
|
-
import { createSchemaGeneratorAsync } from './schemaGeneratorAsync.js';
|
|
6
|
-
/** Sync helper for SQL dialects only; returns `undefined` for MongoDB - use {@link createSchemaGeneratorAsync}. */
|
|
7
|
-
export declare function getSchemaGenerator(dialect: MigratorDialect, defaultForeignKeyAction?: ForeignKeyAction): import("./schemaGenerator.js").SqlSchemaGenerator | undefined;
|
|
8
|
-
export { createSchemaGeneratorAsync };
|
|
9
4
|
export declare function main(args?: string[]): Promise<void>;
|
|
10
5
|
export declare function runUp(migrator: Migrator, args: string[]): Promise<void>;
|
|
11
6
|
export declare function runDown(migrator: Migrator, args: string[]): Promise<void>;
|
|
@@ -19,5 +14,5 @@ export declare function runGenerateFromEntities(migrator: Migrator, args: string
|
|
|
19
14
|
*/
|
|
20
15
|
export declare function runTypes(migrator: Migrator, args: string[]): void;
|
|
21
16
|
export declare function runSync(migrator: Migrator, args: string[], config: Partial<Config>): Promise<void>;
|
|
22
|
-
export declare function runGenerateFromDb(migrator: Migrator, args: string[]
|
|
17
|
+
export declare function runGenerateFromDb(migrator: Migrator, args: string[]): Promise<void>;
|
|
23
18
|
export declare function runDriftCheck(migrator: Migrator, config: Partial<Config>): Promise<void>;
|