uql-orm 0.25.1 → 0.26.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/README.md +9 -8
- package/dist/browser/uql-browser.min.js.map +1 -1
- package/dist/cockroachdb/cockroachDialect.js +1 -1
- package/dist/dialect/indexSqlDialect.d.ts +3 -2
- package/dist/dialect/indexSqlDialect.js +10 -8
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +0 -2
- package/dist/dialect/mysqlLikeSqlDialect.js +0 -7
- package/dist/dialect/pgLikeSqlDialect.js +1 -0
- package/dist/maria/mariaDialect.js +1 -1
- package/dist/migrate/builder/migrationBuilder.js +1 -1
- package/dist/migrate/builder/tableBuilder.js +2 -2
- package/dist/migrate/cli.js +4 -6
- package/dist/migrate/codegen/entityCodeGenerator.d.ts +5 -0
- package/dist/migrate/codegen/entityCodeGenerator.js +22 -25
- package/dist/migrate/codegen/fieldOptionsSource.d.ts +1 -1
- package/dist/migrate/codegen/fieldOptionsSource.js +6 -1
- package/dist/migrate/codegen/indexDecoratorSource.d.ts +14 -0
- package/dist/migrate/codegen/indexDecoratorSource.js +105 -0
- package/dist/migrate/drift/driftDetector.d.ts +5 -50
- package/dist/migrate/drift/driftDetector.js +215 -224
- package/dist/migrate/drift/index.d.ts +1 -1
- package/dist/migrate/drift/index.js +1 -1
- package/dist/migrate/generator/definitionToNode.d.ts +9 -0
- package/dist/migrate/generator/definitionToNode.js +79 -0
- package/dist/migrate/generator/indexNodeToSchema.js +4 -2
- package/dist/migrate/generator/mongoSchemaGenerator.js +3 -3
- package/dist/migrate/introspection/baseSqlIntrospector.d.ts +3 -0
- package/dist/migrate/introspection/baseSqlIntrospector.js +13 -5
- package/dist/migrate/introspection/mongoIntrospector.d.ts +3 -0
- package/dist/migrate/introspection/mongoIntrospector.js +5 -10
- package/dist/migrate/introspection/mysqlIntrospector.js +1 -1
- package/dist/migrate/introspection/postgresIntrospector.d.ts +57 -5
- package/dist/migrate/introspection/postgresIntrospector.js +93 -10
- package/dist/migrate/introspection/sqliteIntrospector.js +1 -1
- package/dist/migrate/migrator.js +3 -2
- package/dist/migrate/schemaGenerator.d.ts +26 -3
- package/dist/migrate/schemaGenerator.js +53 -92
- package/dist/schema/index.d.ts +3 -3
- package/dist/schema/index.js +2 -2
- package/dist/schema/indexColumns.d.ts +10 -0
- package/dist/schema/indexColumns.js +11 -0
- package/dist/schema/indexDifferences.d.ts +22 -0
- package/dist/schema/indexDifferences.js +49 -0
- package/dist/schema/schemaAST.js +2 -8
- package/dist/schema/schemaASTBuilder.d.ts +6 -59
- package/dist/schema/schemaASTBuilder.js +208 -236
- package/dist/schema/schemaASTDiffer.d.ts +9 -56
- package/dist/schema/schemaASTDiffer.js +229 -393
- package/dist/schema/types.d.ts +5 -12
- package/dist/schema/types.js +15 -0
- package/dist/type/dialect.d.ts +7 -2
- package/dist/type/dialect.js +1 -0
- package/dist/type/migration.d.ts +14 -1
- package/dist/util/string.util.js +6 -1
- package/package.json +1 -1
package/dist/schema/index.js
CHANGED
|
@@ -21,6 +21,6 @@ export async function introspectSchema(introspector) {
|
|
|
21
21
|
// SchemaAST class
|
|
22
22
|
export { SchemaAST } from './schemaAST.js';
|
|
23
23
|
// Builder
|
|
24
|
-
export {
|
|
24
|
+
export { buildSchemaAST } from './schemaASTBuilder.js';
|
|
25
25
|
// Differ
|
|
26
|
-
export { diffSchemas
|
|
26
|
+
export { diffSchemas } from './schemaASTDiffer.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ColumnNode, IndexNode } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The table columns an index resolves to, in order.
|
|
4
|
+
*
|
|
5
|
+
* Derived rather than stored: an index is defined by its entries, and a second field repeating them
|
|
6
|
+
* as columns is a second thing to keep in step. It went out of step - one introspector rebuilt the
|
|
7
|
+
* entries from the columns it had just built from the entries, and every fixture had to write both.
|
|
8
|
+
* An expression entry resolves to no column at all, which is why the two were never the same list.
|
|
9
|
+
*/
|
|
10
|
+
export declare function indexColumns(index: IndexNode): ColumnNode[];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The table columns an index resolves to, in order.
|
|
3
|
+
*
|
|
4
|
+
* Derived rather than stored: an index is defined by its entries, and a second field repeating them
|
|
5
|
+
* as columns is a second thing to keep in step. It went out of step - one introspector rebuilt the
|
|
6
|
+
* entries from the columns it had just built from the entries, and every fixture had to write both.
|
|
7
|
+
* An expression entry resolves to no column at all, which is why the two were never the same list.
|
|
8
|
+
*/
|
|
9
|
+
export function indexColumns(index) {
|
|
10
|
+
return index.entries.flatMap((entry) => (entry.expression ? [] : (index.table.columns.get(entry.column) ?? [])));
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { IndexNode } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* What an introspector can report about an index, and so all that diffing may compare. Anything it
|
|
4
|
+
* cannot report is skipped: the entity declares it, the database never mentions it, and no migration
|
|
5
|
+
* could ever make the two agree.
|
|
6
|
+
*
|
|
7
|
+
* Deliberately separate from the dialect's `IndexFeature`, which says what an engine can *emit* and
|
|
8
|
+
* rejects outright. The two look alike and are not: Postgres emits an expression index and reads one
|
|
9
|
+
* back, MySQL emits one it cannot describe afterwards.
|
|
10
|
+
*/
|
|
11
|
+
export type IndexFacet = 'order' | 'nulls' | 'opsClass' | 'accessMethod' | 'include';
|
|
12
|
+
/**
|
|
13
|
+
* Everything an index differs by, named, or nothing when the two match.
|
|
14
|
+
*
|
|
15
|
+
* Only what both sides can state *structurally* is compared. SQL text is not: a database reprints an
|
|
16
|
+
* expression and a predicate from its parse tree, so `status IN ('a','b')` reads back as
|
|
17
|
+
* `status = ANY (ARRAY['a'::text, 'b'::text])`, `LIKE` as `~~`, and a date literal with its time zone
|
|
18
|
+
* spelled out. Folding that back needs a SQL parser, and every near-miss reports drift that no
|
|
19
|
+
* migration can settle. So a partial index's predicate is never compared, and an index over an
|
|
20
|
+
* expression has its entries left alone while the rest of it still compares.
|
|
21
|
+
*/
|
|
22
|
+
export declare function describeIndexDifferences(source: IndexNode, target: IndexNode, facets: ReadonlySet<IndexFacet>): string[];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything an index differs by, named, or nothing when the two match.
|
|
3
|
+
*
|
|
4
|
+
* Only what both sides can state *structurally* is compared. SQL text is not: a database reprints an
|
|
5
|
+
* expression and a predicate from its parse tree, so `status IN ('a','b')` reads back as
|
|
6
|
+
* `status = ANY (ARRAY['a'::text, 'b'::text])`, `LIKE` as `~~`, and a date literal with its time zone
|
|
7
|
+
* spelled out. Folding that back needs a SQL parser, and every near-miss reports drift that no
|
|
8
|
+
* migration can settle. So a partial index's predicate is never compared, and an index over an
|
|
9
|
+
* expression has its entries left alone while the rest of it still compares.
|
|
10
|
+
*/
|
|
11
|
+
export function describeIndexDifferences(source, target, facets) {
|
|
12
|
+
const differences = [];
|
|
13
|
+
const comparableEntries = ![...source.entries, ...target.entries].some((entry) => entry.expression);
|
|
14
|
+
if (comparableEntries) {
|
|
15
|
+
const [sourceColumns, targetColumns] = [source.entries, target.entries].map((entries) => entries.map((entry) => entrySignature(entry, facets)).join(', '));
|
|
16
|
+
if (sourceColumns !== targetColumns) {
|
|
17
|
+
differences.push(`columns: (${targetColumns}) → (${sourceColumns})`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if ((source.unique ?? false) !== (target.unique ?? false)) {
|
|
21
|
+
differences.push(`unique: ${target.unique ?? false} → ${source.unique ?? false}`);
|
|
22
|
+
}
|
|
23
|
+
if (facets.has('accessMethod') && (source.type ?? 'btree') !== (target.type ?? 'btree')) {
|
|
24
|
+
differences.push(`type: ${target.type ?? 'btree'} → ${source.type ?? 'btree'}`);
|
|
25
|
+
}
|
|
26
|
+
if (facets.has('include')) {
|
|
27
|
+
// Order carries no meaning in an `INCLUDE` list, so it is compared as a set.
|
|
28
|
+
const [sourceInclude, targetInclude] = [source.include ?? [], target.include ?? []].map((columns) => [...columns].sort().join(', '));
|
|
29
|
+
if (sourceInclude !== targetInclude) {
|
|
30
|
+
differences.push(`include: (${targetInclude}) → (${sourceInclude})`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return differences;
|
|
34
|
+
}
|
|
35
|
+
function entrySignature(entry, facets) {
|
|
36
|
+
const parts = [entry.column];
|
|
37
|
+
if (facets.has('order')) {
|
|
38
|
+
parts.push(entry.order ?? 'asc');
|
|
39
|
+
}
|
|
40
|
+
if (facets.has('nulls')) {
|
|
41
|
+
// Postgres states this on every entry, so an entity that omits it has asked for Postgres's own
|
|
42
|
+
// default: nulls sort opposite to the direction.
|
|
43
|
+
parts.push(`nulls ${entry.nulls ?? ((entry.order ?? 'asc') === 'desc' ? 'first' : 'last')}`);
|
|
44
|
+
}
|
|
45
|
+
if (facets.has('opsClass') && entry.opsClass) {
|
|
46
|
+
parts.push(entry.opsClass);
|
|
47
|
+
}
|
|
48
|
+
return parts.join(' ');
|
|
49
|
+
}
|
package/dist/schema/schemaAST.js
CHANGED
|
@@ -400,13 +400,7 @@ export class SchemaAST {
|
|
|
400
400
|
const table = clone.tables.get(idx.table.name);
|
|
401
401
|
if (!table)
|
|
402
402
|
continue;
|
|
403
|
-
|
|
404
|
-
const clonedIdx = {
|
|
405
|
-
...idx,
|
|
406
|
-
table,
|
|
407
|
-
columns,
|
|
408
|
-
};
|
|
409
|
-
clone.addIndex(clonedIdx);
|
|
403
|
+
clone.addIndex({ ...idx, table });
|
|
410
404
|
}
|
|
411
405
|
return clone;
|
|
412
406
|
}
|
|
@@ -442,7 +436,7 @@ export class SchemaAST {
|
|
|
442
436
|
})),
|
|
443
437
|
indexes: t.indexes.map((i) => ({
|
|
444
438
|
name: i.name,
|
|
445
|
-
columns: i.
|
|
439
|
+
columns: i.entries.map((entry) => entry.column),
|
|
446
440
|
unique: i.unique,
|
|
447
441
|
})),
|
|
448
442
|
})),
|
|
@@ -12,7 +12,7 @@ import type { ForeignKeyAction } from './types.js';
|
|
|
12
12
|
/**
|
|
13
13
|
* Options for building SchemaAST from entities.
|
|
14
14
|
*/
|
|
15
|
-
export interface
|
|
15
|
+
export interface BuildSchemaASTOptions {
|
|
16
16
|
/** Custom table name resolver */
|
|
17
17
|
resolveTableName?: (entity: Type<unknown>, meta: EntityMeta<unknown>) => string;
|
|
18
18
|
/** Custom column name resolver */
|
|
@@ -23,62 +23,9 @@ export interface BuildFromEntitiesOptions {
|
|
|
23
23
|
defaultForeignKeyAction?: ForeignKeyAction;
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
26
|
+
* Build a SchemaAST from entity classes (decorated with `@Entity`, `@Field`, etc.).
|
|
27
|
+
*
|
|
28
|
+
* Three passes, because each needs the one before it to have finished for *every* entity: a relation
|
|
29
|
+
* resolves against a table another entity declares, and an index against the columns of its own.
|
|
27
30
|
*/
|
|
28
|
-
export declare
|
|
29
|
-
private readonly namingStrategy?;
|
|
30
|
-
private readonly defaultForeignKeyAction;
|
|
31
|
-
private ast;
|
|
32
|
-
constructor(namingStrategy?: NamingStrategy | undefined, defaultForeignKeyAction?: ForeignKeyAction);
|
|
33
|
-
/**
|
|
34
|
-
* Reset the builder for a new schema.
|
|
35
|
-
*/
|
|
36
|
-
reset(): this;
|
|
37
|
-
/**
|
|
38
|
-
* Get the built AST.
|
|
39
|
-
*/
|
|
40
|
-
getAST(): SchemaAST;
|
|
41
|
-
/**
|
|
42
|
-
* Build AST from entity classes (decorated with @Entity, @Field, etc.)
|
|
43
|
-
*/
|
|
44
|
-
fromEntities(entities: readonly Type<unknown>[], options?: BuildFromEntitiesOptions): SchemaAST;
|
|
45
|
-
/**
|
|
46
|
-
* Resolve the canonical type for a field, inheriting from the referenced
|
|
47
|
-
* entity's primary key when the field is a foreign-key reference
|
|
48
|
-
* (`@Field({ references: () => SomeEntity })`) with no explicit type of its
|
|
49
|
-
* own.
|
|
50
|
-
*
|
|
51
|
-
* Without this, a field like `creatorId?: UUID` (a bare TypeScript alias for
|
|
52
|
-
* `string`, erased at runtime) falls back to the generic string inference in
|
|
53
|
-
* {@link fieldOptionsToCanonical} and gets typed as TEXT/VARCHAR - producing a
|
|
54
|
-
* foreign key column whose type doesn't match the UUID primary key it
|
|
55
|
-
* references, which Postgres (and most databases) reject outright.
|
|
56
|
-
*
|
|
57
|
-
* `field.typeFromReference` (set by `defineField`, see entity/metadata/definition.ts)
|
|
58
|
-
* is what distinguishes "no type was given" from "the decorator explicitly set
|
|
59
|
-
* a type" - including explicit constructor overrides like `type: BigInt`, which
|
|
60
|
-
* a value-based check (e.g. `typeof field.type === 'string'`) would miss since
|
|
61
|
-
* reflection also produces constructor values like `String`/`Number`.
|
|
62
|
-
* `columnType` remains the unambiguous, always-respected explicit override.
|
|
63
|
-
*/
|
|
64
|
-
private resolveColumnCanonicalType;
|
|
65
|
-
/**
|
|
66
|
-
* Add a table from entity metadata.
|
|
67
|
-
*/
|
|
68
|
-
private addTableFromEntity;
|
|
69
|
-
/**
|
|
70
|
-
* Add relationships from entity relation decorators.
|
|
71
|
-
*/
|
|
72
|
-
private addRelationshipsFromEntity;
|
|
73
|
-
/**
|
|
74
|
-
* Add indexes from field options (`@Field({ index })`) and from `@Index([...])`, which have nothing
|
|
75
|
-
* in common beyond their target table.
|
|
76
|
-
*/
|
|
77
|
-
private addIndexesFromEntity;
|
|
78
|
-
/**
|
|
79
|
-
* One `@Index([...])`. Its entries keep the authored form (expression, prefix length, order) with
|
|
80
|
-
* names resolved, so the generator renders exactly what was declared; `columns` is the resolvable
|
|
81
|
-
* subset, which is what diffing and introspection compare.
|
|
82
|
-
*/
|
|
83
|
-
private addCompositeIndex;
|
|
84
|
-
}
|
|
31
|
+
export declare function buildSchemaAST(entities: readonly Type<unknown>[], options?: BuildSchemaASTOptions): SchemaAST;
|