@uql/core 3.7.14 → 3.8.2
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/CHANGELOG.md +17 -1
- package/README.md +12 -5
- package/dist/browser/uql-browser.min.js +16 -15
- package/dist/browser/uql-browser.min.js.map +1 -1
- package/dist/dialect/abstractDialect.d.ts +7 -2
- package/dist/dialect/abstractDialect.d.ts.map +1 -1
- package/dist/dialect/abstractDialect.js +9 -1
- package/dist/dialect/abstractDialect.js.map +1 -1
- package/dist/dialect/abstractSqlDialect.d.ts +6 -6
- package/dist/dialect/abstractSqlDialect.d.ts.map +1 -1
- package/dist/dialect/abstractSqlDialect.js +14 -13
- package/dist/dialect/abstractSqlDialect.js.map +1 -1
- package/dist/dialect/dialectConfig.d.ts +23 -0
- package/dist/dialect/dialectConfig.d.ts.map +1 -0
- package/dist/dialect/dialectConfig.js +96 -0
- package/dist/dialect/dialectConfig.js.map +1 -0
- package/dist/dialect/index.d.ts +1 -0
- package/dist/dialect/index.d.ts.map +1 -1
- package/dist/dialect/index.js +1 -0
- package/dist/dialect/index.js.map +1 -1
- package/dist/entity/decorator/definition.d.ts.map +1 -1
- package/dist/entity/decorator/definition.js +2 -1
- package/dist/entity/decorator/definition.js.map +1 -1
- package/dist/entity/decorator/index-decorator.d.ts +36 -0
- package/dist/entity/decorator/index-decorator.d.ts.map +1 -0
- package/dist/entity/decorator/index-decorator.js +51 -0
- package/dist/entity/decorator/index-decorator.js.map +1 -0
- package/dist/entity/decorator/index.d.ts +1 -0
- package/dist/entity/decorator/index.d.ts.map +1 -1
- package/dist/entity/decorator/index.js +1 -0
- package/dist/entity/decorator/index.js.map +1 -1
- package/dist/maria/mariaDialect.d.ts +2 -1
- package/dist/maria/mariaDialect.d.ts.map +1 -1
- package/dist/maria/mariaDialect.js +3 -0
- package/dist/maria/mariaDialect.js.map +1 -1
- package/dist/migrate/builder/columnBuilder.d.ts +79 -0
- package/dist/migrate/builder/columnBuilder.d.ts.map +1 -0
- package/dist/migrate/builder/columnBuilder.js +159 -0
- package/dist/migrate/builder/columnBuilder.js.map +1 -0
- package/dist/migrate/builder/expressions.d.ts +87 -0
- package/dist/migrate/builder/expressions.d.ts.map +1 -0
- package/dist/migrate/builder/expressions.js +150 -0
- package/dist/migrate/builder/expressions.js.map +1 -0
- package/dist/migrate/builder/index.d.ts +6 -0
- package/dist/migrate/builder/index.d.ts.map +1 -0
- package/dist/migrate/builder/index.js +6 -0
- package/dist/migrate/builder/index.js.map +1 -0
- package/dist/migrate/builder/migrationBuilder.d.ts +109 -0
- package/dist/migrate/builder/migrationBuilder.d.ts.map +1 -0
- package/dist/migrate/builder/migrationBuilder.js +453 -0
- package/dist/migrate/builder/migrationBuilder.js.map +1 -0
- package/dist/migrate/builder/tableBuilder.d.ts +51 -0
- package/dist/migrate/builder/tableBuilder.d.ts.map +1 -0
- package/dist/migrate/builder/tableBuilder.js +291 -0
- package/dist/migrate/builder/tableBuilder.js.map +1 -0
- package/dist/migrate/builder/types.d.ts +466 -0
- package/dist/migrate/builder/types.d.ts.map +1 -0
- package/dist/migrate/builder/types.js +8 -0
- package/dist/migrate/builder/types.js.map +1 -0
- package/dist/migrate/cli.d.ts +4 -6
- package/dist/migrate/cli.d.ts.map +1 -1
- package/dist/migrate/cli.js +166 -23
- package/dist/migrate/cli.js.map +1 -1
- package/dist/migrate/codegen/entityCodeGenerator.d.ts +137 -0
- package/dist/migrate/codegen/entityCodeGenerator.d.ts.map +1 -0
- package/dist/migrate/codegen/entityCodeGenerator.js +401 -0
- package/dist/migrate/codegen/entityCodeGenerator.js.map +1 -0
- package/dist/migrate/codegen/entityMerger.d.ts +111 -0
- package/dist/migrate/codegen/entityMerger.d.ts.map +1 -0
- package/dist/migrate/codegen/entityMerger.js +291 -0
- package/dist/migrate/codegen/entityMerger.js.map +1 -0
- package/dist/migrate/codegen/index.d.ts +10 -0
- package/dist/migrate/codegen/index.d.ts.map +1 -0
- package/dist/migrate/codegen/index.js +14 -0
- package/dist/migrate/codegen/index.js.map +1 -0
- package/dist/migrate/codegen/migrationCodeGenerator.d.ts +62 -0
- package/dist/migrate/codegen/migrationCodeGenerator.d.ts.map +1 -0
- package/dist/migrate/codegen/migrationCodeGenerator.js +356 -0
- package/dist/migrate/codegen/migrationCodeGenerator.js.map +1 -0
- package/dist/migrate/codegen/smartRelationDetector.d.ts +48 -0
- package/dist/migrate/codegen/smartRelationDetector.d.ts.map +1 -0
- package/dist/migrate/codegen/smartRelationDetector.js +135 -0
- package/dist/migrate/codegen/smartRelationDetector.js.map +1 -0
- package/dist/migrate/drift/driftDetector.d.ts +81 -0
- package/dist/migrate/drift/driftDetector.d.ts.map +1 -0
- package/dist/migrate/drift/driftDetector.js +248 -0
- package/dist/migrate/drift/driftDetector.js.map +1 -0
- package/dist/migrate/drift/index.d.ts +7 -0
- package/dist/migrate/drift/index.d.ts.map +1 -0
- package/dist/migrate/drift/index.js +7 -0
- package/dist/migrate/drift/index.js.map +1 -0
- package/dist/migrate/generator/index.d.ts +1 -3
- package/dist/migrate/generator/index.d.ts.map +1 -1
- package/dist/migrate/generator/index.js +1 -3
- package/dist/migrate/generator/index.js.map +1 -1
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +29 -3
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts.map +1 -1
- package/dist/migrate/generator/mongoSchemaGenerator.js +86 -4
- package/dist/migrate/generator/mongoSchemaGenerator.js.map +1 -1
- package/dist/migrate/index.d.ts +7 -8
- package/dist/migrate/index.d.ts.map +1 -1
- package/dist/migrate/index.js +11 -9
- package/dist/migrate/index.js.map +1 -1
- package/dist/migrate/introspection/baseSqlIntrospector.d.ts +28 -0
- package/dist/migrate/introspection/baseSqlIntrospector.d.ts.map +1 -0
- package/dist/migrate/introspection/baseSqlIntrospector.js +135 -0
- package/dist/migrate/introspection/baseSqlIntrospector.js.map +1 -0
- package/dist/migrate/introspection/mongoIntrospector.d.ts +6 -0
- package/dist/migrate/introspection/mongoIntrospector.d.ts.map +1 -1
- package/dist/migrate/introspection/mongoIntrospector.js +54 -0
- package/dist/migrate/introspection/mongoIntrospector.js.map +1 -1
- package/dist/migrate/introspection/mysqlIntrospector.d.ts +3 -1
- package/dist/migrate/introspection/mysqlIntrospector.d.ts.map +1 -1
- package/dist/migrate/introspection/mysqlIntrospector.js +20 -11
- package/dist/migrate/introspection/mysqlIntrospector.js.map +1 -1
- package/dist/migrate/introspection/postgresIntrospector.d.ts +3 -2
- package/dist/migrate/introspection/postgresIntrospector.d.ts.map +1 -1
- package/dist/migrate/introspection/postgresIntrospector.js +23 -14
- package/dist/migrate/introspection/postgresIntrospector.js.map +1 -1
- package/dist/migrate/introspection/sqliteIntrospector.d.ts +2 -2
- package/dist/migrate/introspection/sqliteIntrospector.d.ts.map +1 -1
- package/dist/migrate/introspection/sqliteIntrospector.js +15 -13
- package/dist/migrate/introspection/sqliteIntrospector.js.map +1 -1
- package/dist/migrate/migrator.d.ts +29 -0
- package/dist/migrate/migrator.d.ts.map +1 -1
- package/dist/migrate/migrator.js +31 -23
- package/dist/migrate/migrator.js.map +1 -1
- package/dist/migrate/schemaGenerator.d.ts +72 -22
- package/dist/migrate/schemaGenerator.d.ts.map +1 -1
- package/dist/migrate/schemaGenerator.js +389 -153
- package/dist/migrate/schemaGenerator.js.map +1 -1
- package/dist/migrate/sync/index.d.ts +7 -0
- package/dist/migrate/sync/index.d.ts.map +1 -0
- package/dist/migrate/sync/index.js +7 -0
- package/dist/migrate/sync/index.js.map +1 -0
- package/dist/migrate/sync/schemaSync.d.ts +132 -0
- package/dist/migrate/sync/schemaSync.d.ts.map +1 -0
- package/dist/migrate/sync/schemaSync.js +260 -0
- package/dist/migrate/sync/schemaSync.js.map +1 -0
- package/dist/mongo/mongoDialect.d.ts +2 -1
- package/dist/mongo/mongoDialect.d.ts.map +1 -1
- package/dist/mongo/mongoDialect.js +3 -0
- package/dist/mongo/mongoDialect.js.map +1 -1
- package/dist/mysql/mysqlDialect.d.ts +2 -0
- package/dist/mysql/mysqlDialect.d.ts.map +1 -1
- package/dist/mysql/mysqlDialect.js +3 -0
- package/dist/mysql/mysqlDialect.js.map +1 -1
- package/dist/postgres/postgresDialect.js +1 -1
- package/dist/postgres/postgresDialect.js.map +1 -1
- package/dist/schema/canonicalType.d.ts +42 -0
- package/dist/schema/canonicalType.d.ts.map +1 -0
- package/dist/schema/canonicalType.js +524 -0
- package/dist/schema/canonicalType.js.map +1 -0
- package/dist/schema/index.d.ts +28 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +29 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/schemaAST.d.ts +155 -0
- package/dist/schema/schemaAST.d.ts.map +1 -0
- package/dist/schema/schemaAST.js +496 -0
- package/dist/schema/schemaAST.js.map +1 -0
- package/dist/schema/schemaASTBuilder.d.ts +58 -0
- package/dist/schema/schemaASTBuilder.d.ts.map +1 -0
- package/dist/schema/schemaASTBuilder.js +193 -0
- package/dist/schema/schemaASTBuilder.js.map +1 -0
- package/dist/schema/schemaASTDiffer.d.ts +84 -0
- package/dist/schema/schemaASTDiffer.d.ts.map +1 -0
- package/dist/schema/schemaASTDiffer.js +431 -0
- package/dist/schema/schemaASTDiffer.js.map +1 -0
- package/dist/schema/types.d.ts +347 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +11 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/sqlite/sqliteDialect.js +1 -1
- package/dist/sqlite/sqliteDialect.js.map +1 -1
- package/dist/sqlite/sqliteQuerierPool.js.map +1 -1
- package/dist/type/config.d.ts +6 -0
- package/dist/type/config.d.ts.map +1 -1
- package/dist/type/entity.d.ts +24 -0
- package/dist/type/entity.d.ts.map +1 -1
- package/dist/type/migration.d.ts +54 -4
- package/dist/type/migration.d.ts.map +1 -1
- package/dist/type/querier.d.ts +2 -5
- package/dist/type/querier.d.ts.map +1 -1
- package/dist/type/querier.js.map +1 -1
- package/dist/type/query.d.ts +8 -0
- package/dist/type/query.d.ts.map +1 -1
- package/dist/util/field.util.d.ts +0 -1
- package/dist/util/field.util.d.ts.map +1 -1
- package/dist/util/field.util.js +8 -2
- package/dist/util/field.util.js.map +1 -1
- package/dist/util/logger.d.ts.map +1 -1
- package/dist/util/logger.js +2 -1
- package/dist/util/logger.js.map +1 -1
- package/dist/util/string.util.d.ts +24 -0
- package/dist/util/string.util.d.ts.map +1 -1
- package/dist/util/string.util.js +57 -0
- package/dist/util/string.util.js.map +1 -1
- package/package.json +2 -2
- package/dist/migrate/generator/mysqlSchemaGenerator.d.ts +0 -15
- package/dist/migrate/generator/mysqlSchemaGenerator.d.ts.map +0 -1
- package/dist/migrate/generator/mysqlSchemaGenerator.js +0 -88
- package/dist/migrate/generator/mysqlSchemaGenerator.js.map +0 -1
- package/dist/migrate/generator/postgresSchemaGenerator.d.ts +0 -19
- package/dist/migrate/generator/postgresSchemaGenerator.d.ts.map +0 -1
- package/dist/migrate/generator/postgresSchemaGenerator.js +0 -115
- package/dist/migrate/generator/postgresSchemaGenerator.js.map +0 -1
- package/dist/migrate/generator/sqliteSchemaGenerator.d.ts +0 -16
- package/dist/migrate/generator/sqliteSchemaGenerator.d.ts.map +0 -1
- package/dist/migrate/generator/sqliteSchemaGenerator.js +0 -74
- package/dist/migrate/generator/sqliteSchemaGenerator.js.map +0 -1
- package/dist/migrate/type.d.ts +0 -2
- package/dist/migrate/type.d.ts.map +0 -1
- package/dist/migrate/type.js +0 -2
- package/dist/migrate/type.js.map +0 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema AST Module
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified graph representation of database schema for:
|
|
5
|
+
* - Schema diffing and migration generation
|
|
6
|
+
* - Entity code generation from database
|
|
7
|
+
* - Drift detection
|
|
8
|
+
* - Smart relation inference
|
|
9
|
+
*/
|
|
10
|
+
// Dialect configuration
|
|
11
|
+
export { DIALECT_CONFIG, getDialectConfig } from '../dialect/index.js';
|
|
12
|
+
// Canonical type utilities
|
|
13
|
+
export { areTypesEqual, canonicalToColumnType, canonicalToSql, canonicalToTypeScript, fieldOptionsToCanonical, isBreakingTypeChange, sqlToCanonical, } from './canonicalType.js';
|
|
14
|
+
/**
|
|
15
|
+
* Introspect the database and build a SchemaAST from it.
|
|
16
|
+
*
|
|
17
|
+
* @param introspector - The schema introspector to use
|
|
18
|
+
* @returns The SchemaAST representing the database schema
|
|
19
|
+
*/
|
|
20
|
+
export async function introspectSchema(introspector) {
|
|
21
|
+
return introspector.introspect();
|
|
22
|
+
}
|
|
23
|
+
// SchemaAST class
|
|
24
|
+
export { SchemaAST } from './schemaAST.js';
|
|
25
|
+
// Builder
|
|
26
|
+
export { SchemaASTBuilder } from './schemaASTBuilder.js';
|
|
27
|
+
// Differ
|
|
28
|
+
export { diffSchemas, SchemaASTDiffer } from './schemaASTDiffer.js';
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,wBAAwB;AACxB,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvE,2BAA2B;AAC3B,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAE5B;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,YAAgC;IACrE,OAAO,YAAY,CAAC,UAAU,EAAE,CAAC;AACnC,CAAC;AACD,kBAAkB;AAClB,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,UAAU;AACV,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGzD,SAAS;AACT,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SchemaAST Class
|
|
3
|
+
*
|
|
4
|
+
* The main class for working with schema graphs.
|
|
5
|
+
* Provides graph operations like navigation, validation, and topological sorting.
|
|
6
|
+
*/
|
|
7
|
+
import type { ColumnNode, IndexNode, SchemaAST as ISchemaAST, RelationshipNode, RelationshipType, TableNode, ValidationError } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Schema AST - A graph representation of a database schema.
|
|
10
|
+
*
|
|
11
|
+
* Enables:
|
|
12
|
+
* - Graph navigation (dependencies, dependents)
|
|
13
|
+
* - Circular dependency detection
|
|
14
|
+
* - Topological sorting for correct DDL order
|
|
15
|
+
* - Smart relation inference
|
|
16
|
+
* - Schema validation
|
|
17
|
+
*/
|
|
18
|
+
export declare class SchemaAST implements ISchemaAST {
|
|
19
|
+
readonly tables: Map<string, TableNode>;
|
|
20
|
+
readonly relationships: RelationshipNode[];
|
|
21
|
+
readonly indexes: IndexNode[];
|
|
22
|
+
/**
|
|
23
|
+
* Get a table by name.
|
|
24
|
+
*/
|
|
25
|
+
getTable(name: string): TableNode | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Add a table to the schema.
|
|
28
|
+
*/
|
|
29
|
+
addTable(table: TableNode): void;
|
|
30
|
+
/**
|
|
31
|
+
* Remove a table from the schema.
|
|
32
|
+
*/
|
|
33
|
+
removeTable(name: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Get all table nodes.
|
|
36
|
+
*/
|
|
37
|
+
getTables(): TableNode[];
|
|
38
|
+
/**
|
|
39
|
+
* Get all table names.
|
|
40
|
+
*/
|
|
41
|
+
getTableNames(): string[];
|
|
42
|
+
/**
|
|
43
|
+
* Get all tables that depend on this table (have FKs pointing to it).
|
|
44
|
+
* These are tables that reference this table's primary key.
|
|
45
|
+
*/
|
|
46
|
+
getDependentTables(table: TableNode): TableNode[];
|
|
47
|
+
/**
|
|
48
|
+
* Get all tables this table depends on (has FKs to).
|
|
49
|
+
* These are tables that this table references.
|
|
50
|
+
*/
|
|
51
|
+
getDependencies(table: TableNode): TableNode[];
|
|
52
|
+
/**
|
|
53
|
+
* Get the relationship between two tables (if any).
|
|
54
|
+
*/
|
|
55
|
+
getRelationship(from: TableNode, to: TableNode): RelationshipNode | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Get all relationships for a table.
|
|
58
|
+
*/
|
|
59
|
+
getTableRelationships(table: TableNode): RelationshipNode[];
|
|
60
|
+
/**
|
|
61
|
+
* Get the column that a foreign key column references.
|
|
62
|
+
*/
|
|
63
|
+
getReferencedColumn(fkColumn: ColumnNode): ColumnNode | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Detect circular foreign key dependencies.
|
|
66
|
+
* Returns arrays of tables that form cycles.
|
|
67
|
+
*/
|
|
68
|
+
detectCircularDependencies(): TableNode[][];
|
|
69
|
+
/**
|
|
70
|
+
* Check if there are any circular dependencies.
|
|
71
|
+
*/
|
|
72
|
+
hasCircularDependencies(): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Get tables in correct order for CREATE (dependencies first).
|
|
75
|
+
* Tables with no dependencies come first, then tables that depend on them, etc.
|
|
76
|
+
*/
|
|
77
|
+
getCreateOrder(): TableNode[];
|
|
78
|
+
/**
|
|
79
|
+
* Get tables in correct order for DROP (dependents first).
|
|
80
|
+
* Tables that depend on others come first, then the tables they depend on.
|
|
81
|
+
*/
|
|
82
|
+
getDropOrder(): TableNode[];
|
|
83
|
+
/**
|
|
84
|
+
* Topological sort respecting FK dependencies.
|
|
85
|
+
* Uses Kahn's algorithm for stable ordering.
|
|
86
|
+
*/
|
|
87
|
+
private topologicalSort;
|
|
88
|
+
/**
|
|
89
|
+
* Validate schema integrity.
|
|
90
|
+
* Checks for:
|
|
91
|
+
* - Missing FK targets
|
|
92
|
+
* - Circular dependencies
|
|
93
|
+
* - Orphan columns
|
|
94
|
+
* - Duplicate indexes
|
|
95
|
+
*/
|
|
96
|
+
validate(): ValidationError[];
|
|
97
|
+
/**
|
|
98
|
+
* Check if the schema is valid (no validation errors).
|
|
99
|
+
*/
|
|
100
|
+
isValid(): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Check if a table looks like a junction table (ManyToMany through).
|
|
103
|
+
* Junction tables typically have:
|
|
104
|
+
* - Exactly 2 foreign keys
|
|
105
|
+
* - Few other columns (id, maybe timestamps)
|
|
106
|
+
* - Primary key might be composite of the FKs
|
|
107
|
+
*/
|
|
108
|
+
isJunctionTable(table: TableNode): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Infer relation type from schema structure.
|
|
111
|
+
*/
|
|
112
|
+
inferRelationType(rel: RelationshipNode): RelationshipType;
|
|
113
|
+
/**
|
|
114
|
+
* Get the inverse relation type.
|
|
115
|
+
*/
|
|
116
|
+
getInverseRelationType(type: RelationshipType): RelationshipType;
|
|
117
|
+
/**
|
|
118
|
+
* Add an index to the schema.
|
|
119
|
+
*/
|
|
120
|
+
addIndex(index: IndexNode): void;
|
|
121
|
+
/**
|
|
122
|
+
* Get all indexes for a table.
|
|
123
|
+
*/
|
|
124
|
+
getTableIndexes(tableName: string): IndexNode[];
|
|
125
|
+
/**
|
|
126
|
+
* Find an index by name.
|
|
127
|
+
*/
|
|
128
|
+
getIndex(name: string): IndexNode | undefined;
|
|
129
|
+
/**
|
|
130
|
+
* Add a relationship to the schema.
|
|
131
|
+
*/
|
|
132
|
+
addRelationship(rel: RelationshipNode): void;
|
|
133
|
+
/**
|
|
134
|
+
* Remove a relationship from the schema.
|
|
135
|
+
*/
|
|
136
|
+
removeRelationship(name: string): boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Create a deep clone of this schema.
|
|
139
|
+
*/
|
|
140
|
+
clone(): SchemaAST;
|
|
141
|
+
/**
|
|
142
|
+
* Get statistics about the schema.
|
|
143
|
+
*/
|
|
144
|
+
getStats(): {
|
|
145
|
+
tableCount: number;
|
|
146
|
+
columnCount: number;
|
|
147
|
+
relationshipCount: number;
|
|
148
|
+
indexCount: number;
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Convert schema to a plain object for serialization/debugging.
|
|
152
|
+
*/
|
|
153
|
+
toJSON(): object;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=schemaAST.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaAST.d.ts","sourceRoot":"","sources":["../../src/schema/schemaAST.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,SAAS,EACT,SAAS,IAAI,UAAU,EACvB,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;GASG;AACH,qBAAa,SAAU,YAAW,UAAU;IAC1C,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAa;IACpD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,EAAE,CAAM;IAChD,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,CAAM;IAMnC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAI7C;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAKhC;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAsBlC;;OAEG;IACH,SAAS,IAAI,SAAS,EAAE;IAIxB;;OAEG;IACH,aAAa,IAAI,MAAM,EAAE;IAQzB;;;OAGG;IACH,kBAAkB,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,EAAE;IAIjD;;;OAGG;IACH,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,EAAE;IAI9C;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,GAAG,gBAAgB,GAAG,SAAS;IAI7E;;OAEG;IACH,qBAAqB,CAAC,KAAK,EAAE,SAAS,GAAG,gBAAgB,EAAE;IAI3D;;OAEG;IACH,mBAAmB,CAAC,QAAQ,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS;IAQjE;;;OAGG;IACH,0BAA0B,IAAI,SAAS,EAAE,EAAE;IAgC3C;;OAEG;IACH,uBAAuB,IAAI,OAAO;IAIlC;;;OAGG;IACH,cAAc,IAAI,SAAS,EAAE;IAI7B;;;OAGG;IACH,YAAY,IAAI,SAAS,EAAE;IAI3B;;;OAGG;IACH,OAAO,CAAC,eAAe;IA4BvB;;;;;;;OAOG;IACH,QAAQ,IAAI,eAAe,EAAE;IA0C7B;;OAEG;IACH,OAAO,IAAI,OAAO;IAQlB;;;;;;OAMG;IACH,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO;IA0B1C;;OAEG;IACH,iBAAiB,CAAC,GAAG,EAAE,gBAAgB,GAAG,gBAAgB;IAiB1D;;OAEG;IACH,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,GAAG,gBAAgB;IAiBhE;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAOhC;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE;IAK/C;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAQ7C;;OAEG;IACH,eAAe,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI;IAgB5C;;OAEG;IACH,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAgCzC;;OAEG;IACH,KAAK,IAAI,SAAS;IAyFlB;;OAEG;IACH,QAAQ,IAAI;QACV,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,UAAU,EAAE,MAAM,CAAC;KACpB;IAcD;;OAEG;IACH,MAAM,IAAI,MAAM;CA4BjB"}
|
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SchemaAST Class
|
|
3
|
+
*
|
|
4
|
+
* The main class for working with schema graphs.
|
|
5
|
+
* Provides graph operations like navigation, validation, and topological sorting.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Schema AST - A graph representation of a database schema.
|
|
9
|
+
*
|
|
10
|
+
* Enables:
|
|
11
|
+
* - Graph navigation (dependencies, dependents)
|
|
12
|
+
* - Circular dependency detection
|
|
13
|
+
* - Topological sorting for correct DDL order
|
|
14
|
+
* - Smart relation inference
|
|
15
|
+
* - Schema validation
|
|
16
|
+
*/
|
|
17
|
+
export class SchemaAST {
|
|
18
|
+
tables = new Map();
|
|
19
|
+
relationships = [];
|
|
20
|
+
indexes = [];
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Table Operations
|
|
23
|
+
// ============================================================================
|
|
24
|
+
/**
|
|
25
|
+
* Get a table by name.
|
|
26
|
+
*/
|
|
27
|
+
getTable(name) {
|
|
28
|
+
return this.tables.get(name);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Add a table to the schema.
|
|
32
|
+
*/
|
|
33
|
+
addTable(table) {
|
|
34
|
+
table.schema = this;
|
|
35
|
+
this.tables.set(table.name, table);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Remove a table from the schema.
|
|
39
|
+
*/
|
|
40
|
+
removeTable(name) {
|
|
41
|
+
const table = this.tables.get(name);
|
|
42
|
+
if (!table)
|
|
43
|
+
return false;
|
|
44
|
+
// Remove all relationships involving this table
|
|
45
|
+
for (let i = this.relationships.length - 1; i >= 0; i--) {
|
|
46
|
+
const rel = this.relationships[i];
|
|
47
|
+
if (rel.from.table === table || rel.to.table === table) {
|
|
48
|
+
this.relationships.splice(i, 1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Remove all indexes for this table
|
|
52
|
+
for (let i = this.indexes.length - 1; i >= 0; i--) {
|
|
53
|
+
if (this.indexes[i].table === table) {
|
|
54
|
+
this.indexes.splice(i, 1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return this.tables.delete(name);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get all table nodes.
|
|
61
|
+
*/
|
|
62
|
+
getTables() {
|
|
63
|
+
return Array.from(this.tables.values());
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get all table names.
|
|
67
|
+
*/
|
|
68
|
+
getTableNames() {
|
|
69
|
+
return Array.from(this.tables.keys());
|
|
70
|
+
}
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// Graph Navigation
|
|
73
|
+
// ============================================================================
|
|
74
|
+
/**
|
|
75
|
+
* Get all tables that depend on this table (have FKs pointing to it).
|
|
76
|
+
* These are tables that reference this table's primary key.
|
|
77
|
+
*/
|
|
78
|
+
getDependentTables(table) {
|
|
79
|
+
return table.incomingRelations.map((r) => r.from.table);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get all tables this table depends on (has FKs to).
|
|
83
|
+
* These are tables that this table references.
|
|
84
|
+
*/
|
|
85
|
+
getDependencies(table) {
|
|
86
|
+
return table.outgoingRelations.map((r) => r.to.table);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the relationship between two tables (if any).
|
|
90
|
+
*/
|
|
91
|
+
getRelationship(from, to) {
|
|
92
|
+
return this.relationships.find((r) => r.from.table === from && r.to.table === to);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get all relationships for a table.
|
|
96
|
+
*/
|
|
97
|
+
getTableRelationships(table) {
|
|
98
|
+
return this.relationships.filter((r) => r.from.table === table || r.to.table === table);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get the column that a foreign key column references.
|
|
102
|
+
*/
|
|
103
|
+
getReferencedColumn(fkColumn) {
|
|
104
|
+
return fkColumn.references?.to.columns[0];
|
|
105
|
+
}
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// Graph Analysis
|
|
108
|
+
// ============================================================================
|
|
109
|
+
/**
|
|
110
|
+
* Detect circular foreign key dependencies.
|
|
111
|
+
* Returns arrays of tables that form cycles.
|
|
112
|
+
*/
|
|
113
|
+
detectCircularDependencies() {
|
|
114
|
+
const cycles = [];
|
|
115
|
+
const visited = new Set();
|
|
116
|
+
const stack = new Set();
|
|
117
|
+
const dfs = (table, path) => {
|
|
118
|
+
if (stack.has(table)) {
|
|
119
|
+
const cycleStart = path.indexOf(table);
|
|
120
|
+
if (cycleStart !== -1) {
|
|
121
|
+
cycles.push(path.slice(cycleStart));
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (visited.has(table))
|
|
126
|
+
return;
|
|
127
|
+
visited.add(table);
|
|
128
|
+
stack.add(table);
|
|
129
|
+
for (const dep of this.getDependencies(table)) {
|
|
130
|
+
dfs(dep, [...path, table]);
|
|
131
|
+
}
|
|
132
|
+
stack.delete(table);
|
|
133
|
+
};
|
|
134
|
+
for (const table of this.tables.values()) {
|
|
135
|
+
dfs(table, []);
|
|
136
|
+
}
|
|
137
|
+
return cycles;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Check if there are any circular dependencies.
|
|
141
|
+
*/
|
|
142
|
+
hasCircularDependencies() {
|
|
143
|
+
return this.detectCircularDependencies().length > 0;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Get tables in correct order for CREATE (dependencies first).
|
|
147
|
+
* Tables with no dependencies come first, then tables that depend on them, etc.
|
|
148
|
+
*/
|
|
149
|
+
getCreateOrder() {
|
|
150
|
+
return this.topologicalSort();
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get tables in correct order for DROP (dependents first).
|
|
154
|
+
* Tables that depend on others come first, then the tables they depend on.
|
|
155
|
+
*/
|
|
156
|
+
getDropOrder() {
|
|
157
|
+
return this.topologicalSort().reverse();
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Topological sort respecting FK dependencies.
|
|
161
|
+
* Uses Kahn's algorithm for stable ordering.
|
|
162
|
+
*/
|
|
163
|
+
topologicalSort() {
|
|
164
|
+
const result = [];
|
|
165
|
+
const visited = new Set();
|
|
166
|
+
const visit = (table) => {
|
|
167
|
+
if (visited.has(table))
|
|
168
|
+
return;
|
|
169
|
+
visited.add(table);
|
|
170
|
+
// Visit dependencies first (tables this table references)
|
|
171
|
+
for (const dep of this.getDependencies(table)) {
|
|
172
|
+
visit(dep);
|
|
173
|
+
}
|
|
174
|
+
result.push(table);
|
|
175
|
+
};
|
|
176
|
+
// Visit all tables
|
|
177
|
+
for (const table of this.tables.values()) {
|
|
178
|
+
visit(table);
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
// ============================================================================
|
|
183
|
+
// Validation
|
|
184
|
+
// ============================================================================
|
|
185
|
+
/**
|
|
186
|
+
* Validate schema integrity.
|
|
187
|
+
* Checks for:
|
|
188
|
+
* - Missing FK targets
|
|
189
|
+
* - Circular dependencies
|
|
190
|
+
* - Orphan columns
|
|
191
|
+
* - Duplicate indexes
|
|
192
|
+
*/
|
|
193
|
+
validate() {
|
|
194
|
+
const errors = [];
|
|
195
|
+
// Check all FK targets exist
|
|
196
|
+
for (const rel of this.relationships) {
|
|
197
|
+
if (!this.tables.has(rel.to.table.name)) {
|
|
198
|
+
errors.push({
|
|
199
|
+
type: 'missing_fk_target',
|
|
200
|
+
message: `FK target table "${rel.to.table.name}" does not exist`,
|
|
201
|
+
relationship: rel,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Check for circular dependencies
|
|
206
|
+
const cycles = this.detectCircularDependencies();
|
|
207
|
+
for (const cycle of cycles) {
|
|
208
|
+
errors.push({
|
|
209
|
+
type: 'circular_dependency',
|
|
210
|
+
message: `Circular FK: ${cycle.map((t) => t.name).join(' -> ')}`,
|
|
211
|
+
tables: cycle,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
// Check for duplicate index names within same table
|
|
215
|
+
for (const table of this.tables.values()) {
|
|
216
|
+
const indexNames = new Set();
|
|
217
|
+
for (const index of table.indexes) {
|
|
218
|
+
if (indexNames.has(index.name)) {
|
|
219
|
+
errors.push({
|
|
220
|
+
type: 'duplicate_index',
|
|
221
|
+
message: `Duplicate index name "${index.name}" in table "${table.name}"`,
|
|
222
|
+
table,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
indexNames.add(index.name);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return errors;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Check if the schema is valid (no validation errors).
|
|
232
|
+
*/
|
|
233
|
+
isValid() {
|
|
234
|
+
return this.validate().length === 0;
|
|
235
|
+
}
|
|
236
|
+
// ============================================================================
|
|
237
|
+
// Smart Relation Detection
|
|
238
|
+
// ============================================================================
|
|
239
|
+
/**
|
|
240
|
+
* Check if a table looks like a junction table (ManyToMany through).
|
|
241
|
+
* Junction tables typically have:
|
|
242
|
+
* - Exactly 2 foreign keys
|
|
243
|
+
* - Few other columns (id, maybe timestamps)
|
|
244
|
+
* - Primary key might be composite of the FKs
|
|
245
|
+
*/
|
|
246
|
+
isJunctionTable(table) {
|
|
247
|
+
const fkCount = table.outgoingRelations.length;
|
|
248
|
+
const columnCount = table.columns.size;
|
|
249
|
+
// Must have exactly 2 FKs
|
|
250
|
+
if (fkCount !== 2) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
// Should have few columns (typically: id + 2 FKs + maybe timestamps)
|
|
254
|
+
if (columnCount > 6) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
// Check if name suggests a junction (contains both related table names)
|
|
258
|
+
const relatedTables = table.outgoingRelations.map((r) => r.to.table.name.toLowerCase());
|
|
259
|
+
const tableName = table.name.toLowerCase();
|
|
260
|
+
// Common patterns: user_roles, post_tags, etc.
|
|
261
|
+
const containsBothNames = relatedTables.every((name) => tableName.includes(name.replace(/s$/, '')) || tableName.includes(name));
|
|
262
|
+
return containsBothNames || columnCount <= 5;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Infer relation type from schema structure.
|
|
266
|
+
*/
|
|
267
|
+
inferRelationType(rel) {
|
|
268
|
+
const fromCol = rel.from.columns[0];
|
|
269
|
+
// Check if source is junction table -> ManyToMany
|
|
270
|
+
if (this.isJunctionTable(rel.from.table)) {
|
|
271
|
+
return 'ManyToMany';
|
|
272
|
+
}
|
|
273
|
+
// Unique FK -> OneToOne
|
|
274
|
+
if (fromCol?.isUnique) {
|
|
275
|
+
return 'OneToOne';
|
|
276
|
+
}
|
|
277
|
+
// Default: ManyToOne (many rows can reference same target)
|
|
278
|
+
return 'ManyToOne';
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Get the inverse relation type.
|
|
282
|
+
*/
|
|
283
|
+
getInverseRelationType(type) {
|
|
284
|
+
switch (type) {
|
|
285
|
+
case 'OneToOne':
|
|
286
|
+
return 'OneToOne';
|
|
287
|
+
case 'OneToMany':
|
|
288
|
+
return 'ManyToOne';
|
|
289
|
+
case 'ManyToOne':
|
|
290
|
+
return 'OneToMany';
|
|
291
|
+
case 'ManyToMany':
|
|
292
|
+
return 'ManyToMany';
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// ============================================================================
|
|
296
|
+
// Index Operations
|
|
297
|
+
// ============================================================================
|
|
298
|
+
/**
|
|
299
|
+
* Add an index to the schema.
|
|
300
|
+
*/
|
|
301
|
+
addIndex(index) {
|
|
302
|
+
this.indexes.push(index);
|
|
303
|
+
if (!index.table.indexes.includes(index)) {
|
|
304
|
+
index.table.indexes.push(index);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Get all indexes for a table.
|
|
309
|
+
*/
|
|
310
|
+
getTableIndexes(tableName) {
|
|
311
|
+
const table = this.tables.get(tableName);
|
|
312
|
+
return table?.indexes ?? [];
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Find an index by name.
|
|
316
|
+
*/
|
|
317
|
+
getIndex(name) {
|
|
318
|
+
return this.indexes.find((i) => i.name === name);
|
|
319
|
+
}
|
|
320
|
+
// ============================================================================
|
|
321
|
+
// Relationship Operations
|
|
322
|
+
// ============================================================================
|
|
323
|
+
/**
|
|
324
|
+
* Add a relationship to the schema.
|
|
325
|
+
*/
|
|
326
|
+
addRelationship(rel) {
|
|
327
|
+
this.relationships.push(rel);
|
|
328
|
+
// Update table links
|
|
329
|
+
rel.from.table.outgoingRelations.push(rel);
|
|
330
|
+
rel.to.table.incomingRelations.push(rel);
|
|
331
|
+
// Update column links
|
|
332
|
+
for (const col of rel.from.columns) {
|
|
333
|
+
col.references = rel;
|
|
334
|
+
}
|
|
335
|
+
for (const col of rel.to.columns) {
|
|
336
|
+
col.referencedBy.push(rel);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Remove a relationship from the schema.
|
|
341
|
+
*/
|
|
342
|
+
removeRelationship(name) {
|
|
343
|
+
const index = this.relationships.findIndex((r) => r.name === name);
|
|
344
|
+
if (index === -1)
|
|
345
|
+
return false;
|
|
346
|
+
const rel = this.relationships[index];
|
|
347
|
+
// Remove from table links
|
|
348
|
+
const fromIdx = rel.from.table.outgoingRelations.indexOf(rel);
|
|
349
|
+
if (fromIdx !== -1)
|
|
350
|
+
rel.from.table.outgoingRelations.splice(fromIdx, 1);
|
|
351
|
+
const toIdx = rel.to.table.incomingRelations.indexOf(rel);
|
|
352
|
+
if (toIdx !== -1)
|
|
353
|
+
rel.to.table.incomingRelations.splice(toIdx, 1);
|
|
354
|
+
// Remove from column links
|
|
355
|
+
for (const col of rel.from.columns) {
|
|
356
|
+
if (col.references === rel) {
|
|
357
|
+
col.references = undefined;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
for (const col of rel.to.columns) {
|
|
361
|
+
const refIdx = col.referencedBy.indexOf(rel);
|
|
362
|
+
if (refIdx !== -1)
|
|
363
|
+
col.referencedBy.splice(refIdx, 1);
|
|
364
|
+
}
|
|
365
|
+
this.relationships.splice(index, 1);
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
// ============================================================================
|
|
369
|
+
// Utility Methods
|
|
370
|
+
// ============================================================================
|
|
371
|
+
/**
|
|
372
|
+
* Create a deep clone of this schema.
|
|
373
|
+
*/
|
|
374
|
+
clone() {
|
|
375
|
+
const clone = new SchemaAST();
|
|
376
|
+
// First pass: clone tables and columns (without links)
|
|
377
|
+
for (const [name, table] of this.tables) {
|
|
378
|
+
const clonedColumns = new Map();
|
|
379
|
+
for (const [colName, col] of table.columns) {
|
|
380
|
+
const clonedCol = {
|
|
381
|
+
...col,
|
|
382
|
+
table: undefined, // Will be set below
|
|
383
|
+
referencedBy: [],
|
|
384
|
+
references: undefined,
|
|
385
|
+
};
|
|
386
|
+
clonedColumns.set(colName, clonedCol);
|
|
387
|
+
}
|
|
388
|
+
const clonedTable = {
|
|
389
|
+
name,
|
|
390
|
+
columns: clonedColumns,
|
|
391
|
+
primaryKey: [],
|
|
392
|
+
indexes: [],
|
|
393
|
+
comment: table.comment,
|
|
394
|
+
schema: clone,
|
|
395
|
+
incomingRelations: [],
|
|
396
|
+
outgoingRelations: [],
|
|
397
|
+
};
|
|
398
|
+
// Link columns to table
|
|
399
|
+
for (const col of clonedColumns.values()) {
|
|
400
|
+
col.table = clonedTable;
|
|
401
|
+
}
|
|
402
|
+
// Set primary key
|
|
403
|
+
clonedTable.primaryKey = table.primaryKey
|
|
404
|
+
.map((pk) => clonedColumns.get(pk.name))
|
|
405
|
+
.filter(Boolean);
|
|
406
|
+
clone.tables.set(name, clonedTable);
|
|
407
|
+
}
|
|
408
|
+
// Second pass: clone relationships
|
|
409
|
+
for (const rel of this.relationships) {
|
|
410
|
+
const fromTable = clone.tables.get(rel.from.table.name);
|
|
411
|
+
const toTable = clone.tables.get(rel.to.table.name);
|
|
412
|
+
if (!fromTable || !toTable)
|
|
413
|
+
continue;
|
|
414
|
+
const fromColumns = rel.from.columns
|
|
415
|
+
.map((c) => fromTable.columns.get(c.name))
|
|
416
|
+
.filter((c) => c !== undefined);
|
|
417
|
+
const toColumns = rel.to.columns
|
|
418
|
+
.map((c) => toTable.columns.get(c.name))
|
|
419
|
+
.filter((c) => c !== undefined);
|
|
420
|
+
const clonedRel = {
|
|
421
|
+
...rel,
|
|
422
|
+
from: {
|
|
423
|
+
table: fromTable,
|
|
424
|
+
columns: fromColumns,
|
|
425
|
+
},
|
|
426
|
+
to: {
|
|
427
|
+
table: toTable,
|
|
428
|
+
columns: toColumns,
|
|
429
|
+
},
|
|
430
|
+
through: rel.through ? clone.tables.get(rel.through.name) : undefined,
|
|
431
|
+
};
|
|
432
|
+
clone.addRelationship(clonedRel);
|
|
433
|
+
}
|
|
434
|
+
// Third pass: clone indexes
|
|
435
|
+
for (const idx of this.indexes) {
|
|
436
|
+
const table = clone.tables.get(idx.table.name);
|
|
437
|
+
if (!table)
|
|
438
|
+
continue;
|
|
439
|
+
const columns = idx.columns.map((c) => table.columns.get(c.name)).filter((c) => c !== undefined);
|
|
440
|
+
const clonedIdx = {
|
|
441
|
+
...idx,
|
|
442
|
+
table,
|
|
443
|
+
columns,
|
|
444
|
+
};
|
|
445
|
+
clone.addIndex(clonedIdx);
|
|
446
|
+
}
|
|
447
|
+
return clone;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Get statistics about the schema.
|
|
451
|
+
*/
|
|
452
|
+
getStats() {
|
|
453
|
+
let columnCount = 0;
|
|
454
|
+
for (const table of this.tables.values()) {
|
|
455
|
+
columnCount += table.columns.size;
|
|
456
|
+
}
|
|
457
|
+
return {
|
|
458
|
+
tableCount: this.tables.size,
|
|
459
|
+
columnCount,
|
|
460
|
+
relationshipCount: this.relationships.length,
|
|
461
|
+
indexCount: this.indexes.length,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Convert schema to a plain object for serialization/debugging.
|
|
466
|
+
*/
|
|
467
|
+
toJSON() {
|
|
468
|
+
return {
|
|
469
|
+
tables: Array.from(this.tables.values()).map((t) => ({
|
|
470
|
+
name: t.name,
|
|
471
|
+
columns: Array.from(t.columns.values()).map((c) => ({
|
|
472
|
+
name: c.name,
|
|
473
|
+
type: c.type,
|
|
474
|
+
nullable: c.nullable,
|
|
475
|
+
isPrimaryKey: c.isPrimaryKey,
|
|
476
|
+
isAutoIncrement: c.isAutoIncrement,
|
|
477
|
+
isUnique: c.isUnique,
|
|
478
|
+
})),
|
|
479
|
+
indexes: t.indexes.map((i) => ({
|
|
480
|
+
name: i.name,
|
|
481
|
+
columns: i.columns.map((c) => c.name),
|
|
482
|
+
unique: i.unique,
|
|
483
|
+
})),
|
|
484
|
+
})),
|
|
485
|
+
relationships: this.relationships.map((r) => ({
|
|
486
|
+
name: r.name,
|
|
487
|
+
type: r.type,
|
|
488
|
+
from: `${r.from.table.name}.${r.from.columns.map((c) => c.name).join(',')}`,
|
|
489
|
+
to: `${r.to.table.name}.${r.to.columns.map((c) => c.name).join(',')}`,
|
|
490
|
+
onDelete: r.onDelete,
|
|
491
|
+
onUpdate: r.onUpdate,
|
|
492
|
+
})),
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
//# sourceMappingURL=schemaAST.js.map
|