@uql/core 3.7.13 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -2
- package/README.md +16 -8
- 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 +5 -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 +17 -0
- package/dist/dialect/dialectConfig.d.ts.map +1 -0
- package/dist/dialect/dialectConfig.js +71 -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 +75 -0
- package/dist/migrate/builder/columnBuilder.d.ts.map +1 -0
- package/dist/migrate/builder/columnBuilder.js +149 -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 +73 -0
- package/dist/migrate/builder/migrationBuilder.d.ts.map +1 -0
- package/dist/migrate/builder/migrationBuilder.js +326 -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 +278 -0
- package/dist/migrate/builder/tableBuilder.js.map +1 -0
- package/dist/migrate/builder/types.d.ts +462 -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 +2 -1
- package/dist/migrate/introspection/mysqlIntrospector.d.ts.map +1 -1
- package/dist/migrate/introspection/mysqlIntrospector.js +8 -6
- package/dist/migrate/introspection/mysqlIntrospector.js.map +1 -1
- package/dist/migrate/introspection/postgresIntrospector.d.ts +2 -1
- package/dist/migrate/introspection/postgresIntrospector.d.ts.map +1 -1
- package/dist/migrate/introspection/postgresIntrospector.js +3 -1
- 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 +3 -4
- 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 +385 -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 +50 -4
- package/dist/type/migration.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
|
@@ -1,27 +1,54 @@
|
|
|
1
1
|
import { AbstractDialect } from '../dialect/index.js';
|
|
2
2
|
import { getMeta } from '../entity/index.js';
|
|
3
|
-
import {
|
|
3
|
+
import { areTypesEqual, canonicalToSql, fieldOptionsToCanonical, sqlToCanonical } from '../schema/canonicalType.js';
|
|
4
|
+
import { escapeSqlId, getKeys, isAutoIncrement } from '../util/index.js';
|
|
5
|
+
import { formatDefaultValue } from './builder/expressions.js';
|
|
4
6
|
/**
|
|
5
|
-
*
|
|
7
|
+
* Unified SQL schema generator.
|
|
8
|
+
* Parameterized by dialect to handle Postgres, MySQL, MariaDB, and SQLite.
|
|
6
9
|
*/
|
|
7
|
-
export class
|
|
8
|
-
escapeIdChar;
|
|
9
|
-
constructor(namingStrategy, escapeIdChar = '`') {
|
|
10
|
-
super(namingStrategy);
|
|
11
|
-
this.escapeIdChar = escapeIdChar;
|
|
12
|
-
}
|
|
10
|
+
export class SqlSchemaGenerator extends AbstractDialect {
|
|
13
11
|
/**
|
|
14
12
|
* Escape an identifier (table name, column name, etc.)
|
|
15
13
|
*/
|
|
16
14
|
escapeId(identifier) {
|
|
17
|
-
return escapeSqlId(identifier, this.
|
|
15
|
+
return escapeSqlId(identifier, this.config.quoteChar);
|
|
18
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Primary key type for auto-increment integer IDs
|
|
19
|
+
*/
|
|
20
|
+
get serialPrimaryKeyType() {
|
|
21
|
+
return this.config.serialPrimaryKey;
|
|
22
|
+
}
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// CanonicalType Integration (Unified Type System)
|
|
25
|
+
// ============================================================================
|
|
26
|
+
/**
|
|
27
|
+
* Convert FieldOptions to CanonicalType using the unified type system.
|
|
28
|
+
*/
|
|
29
|
+
getCanonicalType(field, fieldType) {
|
|
30
|
+
return fieldOptionsToCanonical(field, fieldType);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Convert CanonicalType to SQL type string for this dialect.
|
|
34
|
+
* Also handles legacy string types for backward compatibility.
|
|
35
|
+
*/
|
|
36
|
+
canonicalTypeToSql(type) {
|
|
37
|
+
// Handle legacy string types
|
|
38
|
+
if (typeof type === 'string') {
|
|
39
|
+
return type;
|
|
40
|
+
}
|
|
41
|
+
return canonicalToSql(type, this.dialect);
|
|
42
|
+
}
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// SchemaGenerator Implementation
|
|
45
|
+
// ============================================================================
|
|
19
46
|
generateCreateTable(entity, options = {}) {
|
|
20
47
|
const meta = getMeta(entity);
|
|
21
48
|
const tableName = this.resolveTableName(entity, meta);
|
|
22
49
|
const columns = this.generateColumnDefinitions(meta);
|
|
23
50
|
const constraints = this.generateTableConstraints(meta);
|
|
24
|
-
const ifNotExists = options.ifNotExists ? 'IF NOT EXISTS ' : '';
|
|
51
|
+
const ifNotExists = options.ifNotExists && this.config.supportsIfNotExists ? 'IF NOT EXISTS ' : '';
|
|
25
52
|
let sql = `CREATE TABLE ${ifNotExists}${this.escapeId(tableName)} (\n`;
|
|
26
53
|
sql += columns.map((col) => ` ${col}`).join(',\n');
|
|
27
54
|
if (constraints.length > 0) {
|
|
@@ -99,8 +126,6 @@ export class AbstractSchemaGenerator extends AbstractDialect {
|
|
|
99
126
|
statements.push(this.generateDropIndex(diff.tableName, index.name));
|
|
100
127
|
}
|
|
101
128
|
}
|
|
102
|
-
// Note: columnsToDrop and indexesToDrop cannot be auto-reversed
|
|
103
|
-
// because we don't store the original schema. These require manual down migrations.
|
|
104
129
|
if (diff.columnsToDrop?.length || diff.indexesToDrop?.length) {
|
|
105
130
|
statements.push(`-- TODO: Manual reversal needed for dropped columns/indexes`);
|
|
106
131
|
}
|
|
@@ -109,9 +134,13 @@ export class AbstractSchemaGenerator extends AbstractDialect {
|
|
|
109
134
|
generateCreateIndex(tableName, index) {
|
|
110
135
|
const unique = index.unique ? 'UNIQUE ' : '';
|
|
111
136
|
const columns = index.columns.map((c) => this.escapeId(c)).join(', ');
|
|
112
|
-
|
|
137
|
+
const ifNotExists = this.config.supportsIfNotExists ? 'IF NOT EXISTS ' : '';
|
|
138
|
+
return `CREATE ${unique}INDEX ${ifNotExists}${this.escapeId(index.name)} ON ${this.escapeId(tableName)} (${columns});`;
|
|
113
139
|
}
|
|
114
140
|
generateDropIndex(tableName, indexName) {
|
|
141
|
+
if (this.dialect === 'mysql' || this.dialect === 'mariadb') {
|
|
142
|
+
return `DROP INDEX ${this.escapeId(indexName)} ON ${this.escapeId(tableName)};`;
|
|
143
|
+
}
|
|
115
144
|
return `DROP INDEX IF EXISTS ${this.escapeId(indexName)};`;
|
|
116
145
|
}
|
|
117
146
|
/**
|
|
@@ -123,7 +152,7 @@ export class AbstractSchemaGenerator extends AbstractDialect {
|
|
|
123
152
|
for (const key of fieldKeys) {
|
|
124
153
|
const field = meta.fields[key];
|
|
125
154
|
if (field?.virtual)
|
|
126
|
-
continue;
|
|
155
|
+
continue;
|
|
127
156
|
const colDef = this.generateColumnDefinition(key, field, meta);
|
|
128
157
|
columns.push(colDef);
|
|
129
158
|
}
|
|
@@ -134,53 +163,30 @@ export class AbstractSchemaGenerator extends AbstractDialect {
|
|
|
134
163
|
*/
|
|
135
164
|
generateColumnDefinition(fieldKey, field, meta) {
|
|
136
165
|
const column = this.fieldToColumnSchema(fieldKey, field, meta);
|
|
137
|
-
|
|
138
|
-
const sqlType = column.isAutoIncrement ? this.serialPrimaryKeyType : column.type;
|
|
139
|
-
let definition = `${this.escapeId(column.name)} ${sqlType}`;
|
|
140
|
-
// PRIMARY KEY constraint (for non-serial types)
|
|
141
|
-
if (column.isPrimaryKey && !sqlType.includes('PRIMARY KEY')) {
|
|
142
|
-
definition += ' PRIMARY KEY';
|
|
143
|
-
}
|
|
144
|
-
// NULL/NOT NULL and UNIQUE
|
|
145
|
-
if (!column.isPrimaryKey) {
|
|
146
|
-
if (!column.nullable)
|
|
147
|
-
definition += ' NOT NULL';
|
|
148
|
-
if (column.isUnique)
|
|
149
|
-
definition += ' UNIQUE';
|
|
150
|
-
}
|
|
151
|
-
// DEFAULT value
|
|
152
|
-
if (column.defaultValue !== undefined) {
|
|
153
|
-
definition += ` DEFAULT ${this.formatDefaultValue(column.defaultValue)}`;
|
|
154
|
-
}
|
|
155
|
-
// COMMENT (if supported)
|
|
156
|
-
if (column.comment) {
|
|
157
|
-
definition += this.generateColumnComment(tableName, column.name, column.comment);
|
|
158
|
-
}
|
|
159
|
-
return definition;
|
|
166
|
+
return this.generateColumnDefinitionFromSchema(column);
|
|
160
167
|
}
|
|
161
168
|
/**
|
|
162
169
|
* Generate column definition from a ColumnSchema object
|
|
163
170
|
*/
|
|
164
171
|
generateColumnDefinitionFromSchema(column, options = {}) {
|
|
165
172
|
const { includePrimaryKey = true, includeUnique = true } = options;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
else if (column.precision !== undefined && !type.includes('(')) {
|
|
177
|
-
if (column.scale !== undefined) {
|
|
178
|
-
type = `${type}(${column.precision}, ${column.scale})`;
|
|
173
|
+
let type = column.type;
|
|
174
|
+
if (!type.includes('(')) {
|
|
175
|
+
if (column.precision !== undefined) {
|
|
176
|
+
if (column.scale !== undefined) {
|
|
177
|
+
type += `(${column.precision}, ${column.scale})`;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
type += `(${column.precision})`;
|
|
181
|
+
}
|
|
179
182
|
}
|
|
180
|
-
else {
|
|
181
|
-
type
|
|
183
|
+
else if (column.length !== undefined) {
|
|
184
|
+
type += `(${column.length})`;
|
|
182
185
|
}
|
|
183
186
|
}
|
|
187
|
+
if (!includePrimaryKey) {
|
|
188
|
+
type = type.replace(/\s+PRIMARY\s+KEY/i, '');
|
|
189
|
+
}
|
|
184
190
|
let definition = `${this.escapeId(column.name)} ${type}`;
|
|
185
191
|
if (includePrimaryKey && column.isPrimaryKey && !type.includes('PRIMARY KEY')) {
|
|
186
192
|
definition += ' PRIMARY KEY';
|
|
@@ -194,6 +200,9 @@ export class AbstractSchemaGenerator extends AbstractDialect {
|
|
|
194
200
|
if (column.defaultValue !== undefined) {
|
|
195
201
|
definition += ` DEFAULT ${this.formatDefaultValue(column.defaultValue)}`;
|
|
196
202
|
}
|
|
203
|
+
if (column.comment) {
|
|
204
|
+
definition += this.generateColumnComment(column.name, column.comment);
|
|
205
|
+
}
|
|
197
206
|
return definition;
|
|
198
207
|
}
|
|
199
208
|
/**
|
|
@@ -215,8 +224,9 @@ export class AbstractSchemaGenerator extends AbstractDialect {
|
|
|
215
224
|
// Generate foreign key constraints from references
|
|
216
225
|
for (const key of fieldKeys) {
|
|
217
226
|
const field = meta.fields[key];
|
|
218
|
-
|
|
219
|
-
|
|
227
|
+
const reference = field?.references ?? field?.reference;
|
|
228
|
+
if (reference && field.foreignKey !== false) {
|
|
229
|
+
const refEntity = reference();
|
|
220
230
|
const refMeta = getMeta(refEntity);
|
|
221
231
|
const refIdField = refMeta.fields[refMeta.id];
|
|
222
232
|
const columnName = this.resolveColumnName(key, field);
|
|
@@ -230,73 +240,92 @@ export class AbstractSchemaGenerator extends AbstractDialect {
|
|
|
230
240
|
return constraints;
|
|
231
241
|
}
|
|
232
242
|
getSqlType(field, fieldType) {
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
// Inherit type from referenced field if applicable
|
|
238
|
-
if (field.reference) {
|
|
239
|
-
const refEntity = field.reference();
|
|
243
|
+
// If field has a reference, inherit type from the target primary key
|
|
244
|
+
const reference = field.references ?? field.reference;
|
|
245
|
+
if (reference) {
|
|
246
|
+
const refEntity = reference();
|
|
240
247
|
const refMeta = getMeta(refEntity);
|
|
241
248
|
const refIdField = refMeta.fields[refMeta.id];
|
|
242
|
-
|
|
243
|
-
return this.getSqlType({ ...refIdField, reference: undefined }, refIdField.type);
|
|
249
|
+
return this.getSqlType({ ...refIdField, references: undefined, reference: undefined, isId: undefined, autoIncrement: false }, refIdField.type);
|
|
244
250
|
}
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return this.mapColumnType(type, field);
|
|
251
|
+
// Get canonical type and convert to SQL
|
|
252
|
+
const canonical = this.getCanonicalType(field, fieldType);
|
|
253
|
+
// Special case for serial primary keys
|
|
254
|
+
if (isAutoIncrement(field, field.isId === true)) {
|
|
255
|
+
return this.serialPrimaryKeyType;
|
|
251
256
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
257
|
+
return this.canonicalTypeToSql(canonical);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Get the boolean type for this database
|
|
261
|
+
*/
|
|
262
|
+
getBooleanType() {
|
|
263
|
+
return this.canonicalTypeToSql({ category: 'boolean' });
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Generate ALTER COLUMN statements (database-specific)
|
|
267
|
+
*/
|
|
268
|
+
generateAlterColumnStatements(tableName, column, newDefinition) {
|
|
269
|
+
const table = this.escapeId(tableName);
|
|
270
|
+
const colName = this.escapeId(column.name);
|
|
271
|
+
if (this.config.alterColumnSyntax === 'none') {
|
|
272
|
+
throw new Error(`${this.dialect}: Cannot alter column "${column.name}" - you must recreate the table. ` +
|
|
273
|
+
`This database does not support ALTER COLUMN.`);
|
|
274
|
+
}
|
|
275
|
+
if (this.dialect === 'postgres') {
|
|
276
|
+
const statements = [];
|
|
277
|
+
// PostgreSQL uses separate ALTER COLUMN clauses for different changes
|
|
278
|
+
// 1. Change type
|
|
279
|
+
statements.push(`ALTER TABLE ${table} ALTER COLUMN ${colName} TYPE ${column.type};`);
|
|
280
|
+
// 2. Change nullability
|
|
281
|
+
if (column.nullable) {
|
|
282
|
+
statements.push(`ALTER TABLE ${table} ALTER COLUMN ${colName} DROP NOT NULL;`);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
statements.push(`ALTER TABLE ${table} ALTER COLUMN ${colName} SET NOT NULL;`);
|
|
286
|
+
}
|
|
287
|
+
// 3. Change default value
|
|
288
|
+
if (column.defaultValue !== undefined) {
|
|
289
|
+
statements.push(`ALTER TABLE ${table} ALTER COLUMN ${colName} SET DEFAULT ${this.formatDefaultValue(column.defaultValue)};`);
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
statements.push(`ALTER TABLE ${table} ALTER COLUMN ${colName} DROP DEFAULT;`);
|
|
293
|
+
}
|
|
294
|
+
return statements;
|
|
263
295
|
}
|
|
264
|
-
return this.
|
|
296
|
+
return [`ALTER TABLE ${table} ${this.config.alterColumnSyntax} ${newDefinition};`];
|
|
265
297
|
}
|
|
266
298
|
/**
|
|
267
299
|
* Get table options (e.g., ENGINE for MySQL)
|
|
268
300
|
*/
|
|
269
301
|
getTableOptions(meta) {
|
|
302
|
+
return this.config.tableOptions ? ` ${this.config.tableOptions}` : '';
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Generate column comment clause (if supported)
|
|
306
|
+
*/
|
|
307
|
+
generateColumnComment(columnName, comment) {
|
|
308
|
+
if (this.dialect === 'mysql' || this.dialect === 'mariadb') {
|
|
309
|
+
const escapedComment = comment.replace(/'/g, "''");
|
|
310
|
+
return ` COMMENT '${escapedComment}'`;
|
|
311
|
+
}
|
|
270
312
|
return '';
|
|
271
313
|
}
|
|
272
314
|
/**
|
|
273
315
|
* Format a default value for SQL
|
|
274
316
|
*/
|
|
275
317
|
formatDefaultValue(value) {
|
|
276
|
-
if (value ===
|
|
277
|
-
return '
|
|
278
|
-
}
|
|
279
|
-
if (typeof value === 'string') {
|
|
280
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
318
|
+
if (this.dialect === 'sqlite' && typeof value === 'boolean') {
|
|
319
|
+
return value ? '1' : '0';
|
|
281
320
|
}
|
|
282
|
-
|
|
283
|
-
return value ? 'TRUE' : 'FALSE';
|
|
284
|
-
}
|
|
285
|
-
if (typeof value === 'number' || typeof value === 'bigint') {
|
|
286
|
-
return String(value);
|
|
287
|
-
}
|
|
288
|
-
if (value instanceof Date) {
|
|
289
|
-
return `'${value.toISOString()}'`;
|
|
290
|
-
}
|
|
291
|
-
return String(value);
|
|
321
|
+
return formatDefaultValue(value);
|
|
292
322
|
}
|
|
293
323
|
/**
|
|
294
|
-
* Compare
|
|
324
|
+
* Compare an entity with a database table node and return the differences.
|
|
295
325
|
*/
|
|
296
|
-
diffSchema(entity,
|
|
326
|
+
diffSchema(entity, currentTable) {
|
|
297
327
|
const meta = getMeta(entity);
|
|
298
|
-
if (!
|
|
299
|
-
// Table doesn't exist, need to create
|
|
328
|
+
if (!currentTable) {
|
|
300
329
|
return {
|
|
301
330
|
tableName: this.resolveTableName(entity, meta),
|
|
302
331
|
type: 'create',
|
|
@@ -305,9 +334,8 @@ export class AbstractSchemaGenerator extends AbstractDialect {
|
|
|
305
334
|
const columnsToAdd = [];
|
|
306
335
|
const columnsToAlter = [];
|
|
307
336
|
const columnsToDrop = [];
|
|
308
|
-
const currentColumns = new Map(
|
|
337
|
+
const currentColumns = new Map(currentTable.columns);
|
|
309
338
|
const fieldKeys = getKeys(meta.fields);
|
|
310
|
-
// Check for new or altered columns
|
|
311
339
|
for (const key of fieldKeys) {
|
|
312
340
|
const field = meta.fields[key];
|
|
313
341
|
if (field?.virtual)
|
|
@@ -315,24 +343,22 @@ export class AbstractSchemaGenerator extends AbstractDialect {
|
|
|
315
343
|
const columnName = this.resolveColumnName(key, field);
|
|
316
344
|
const currentColumn = currentColumns.get(columnName);
|
|
317
345
|
if (!currentColumn) {
|
|
318
|
-
// Column needs to be added
|
|
319
346
|
columnsToAdd.push(this.fieldToColumnSchema(key, field, meta));
|
|
320
347
|
}
|
|
321
348
|
else {
|
|
322
|
-
// Check if column needs alteration
|
|
323
349
|
const desiredColumn = this.fieldToColumnSchema(key, field, meta);
|
|
324
|
-
|
|
325
|
-
|
|
350
|
+
const currentColumnSchema = this.columnNodeToSchema(currentColumn);
|
|
351
|
+
if (this.columnsNeedAlteration(currentColumnSchema, desiredColumn)) {
|
|
352
|
+
columnsToAlter.push({ from: currentColumnSchema, to: desiredColumn });
|
|
326
353
|
}
|
|
327
354
|
}
|
|
328
355
|
currentColumns.delete(columnName);
|
|
329
356
|
}
|
|
330
|
-
// Remaining columns in currentColumns should be dropped
|
|
331
357
|
for (const [name] of currentColumns) {
|
|
332
358
|
columnsToDrop.push(name);
|
|
333
359
|
}
|
|
334
360
|
if (columnsToAdd.length === 0 && columnsToAlter.length === 0 && columnsToDrop.length === 0) {
|
|
335
|
-
return undefined;
|
|
361
|
+
return undefined;
|
|
336
362
|
}
|
|
337
363
|
return {
|
|
338
364
|
tableName: this.resolveTableName(entity, meta),
|
|
@@ -342,6 +368,18 @@ export class AbstractSchemaGenerator extends AbstractDialect {
|
|
|
342
368
|
columnsToDrop: columnsToDrop.length > 0 ? columnsToDrop : undefined,
|
|
343
369
|
};
|
|
344
370
|
}
|
|
371
|
+
columnNodeToSchema(col) {
|
|
372
|
+
return {
|
|
373
|
+
name: col.name,
|
|
374
|
+
type: this.canonicalTypeToSql(col.type),
|
|
375
|
+
nullable: col.nullable,
|
|
376
|
+
defaultValue: col.defaultValue,
|
|
377
|
+
isPrimaryKey: col.isPrimaryKey,
|
|
378
|
+
isAutoIncrement: col.isAutoIncrement,
|
|
379
|
+
isUnique: col.isUnique,
|
|
380
|
+
comment: col.comment,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
345
383
|
/**
|
|
346
384
|
* Convert field options to ColumnSchema
|
|
347
385
|
*/
|
|
@@ -365,9 +403,6 @@ export class AbstractSchemaGenerator extends AbstractDialect {
|
|
|
365
403
|
* Check if two columns differ enough to require alteration
|
|
366
404
|
*/
|
|
367
405
|
columnsNeedAlteration(current, desired) {
|
|
368
|
-
// If both are primary keys, we skip alteration by default.
|
|
369
|
-
// Altering primary keys is complex, dangerous, and often requires
|
|
370
|
-
// dropping and recreating foreign keys.
|
|
371
406
|
if (current.isPrimaryKey && desired.isPrimaryKey) {
|
|
372
407
|
return false;
|
|
373
408
|
}
|
|
@@ -384,48 +419,12 @@ export class AbstractSchemaGenerator extends AbstractDialect {
|
|
|
384
419
|
return false;
|
|
385
420
|
}
|
|
386
421
|
/**
|
|
387
|
-
* Compare two column types for equality
|
|
422
|
+
* Compare two column types for equality using the canonical type system
|
|
388
423
|
*/
|
|
389
424
|
isTypeEqual(current, desired) {
|
|
390
|
-
const
|
|
391
|
-
const
|
|
392
|
-
return
|
|
393
|
-
}
|
|
394
|
-
/**
|
|
395
|
-
* Normalize a column type string for comparison
|
|
396
|
-
*/
|
|
397
|
-
normalizeType(column) {
|
|
398
|
-
let type = column.type.toLowerCase();
|
|
399
|
-
// Remove any extra spaces and standardize common aliases
|
|
400
|
-
type = type
|
|
401
|
-
.replace(/\s+/g, '')
|
|
402
|
-
.replace(/^integer$/, 'int')
|
|
403
|
-
.replace(/^boolean$/, 'tinyint(1)') // Common in MySQL
|
|
404
|
-
.replace(/^doubleprecision$/, 'double') // Postgres vs MySQL
|
|
405
|
-
.replace(/^float8$/, 'double')
|
|
406
|
-
.replace(/^float4$/, 'real')
|
|
407
|
-
.replace(/^characterany$/, 'varchar')
|
|
408
|
-
.replace(/^charactervarying$/, 'varchar')
|
|
409
|
-
.replace(/generated(always|bydefault)asidentity$/, '') // Ignore identity keywords
|
|
410
|
-
.replace(/unsigned$/, ''); // Ignore unsigned for type comparison
|
|
411
|
-
// Strip display width/length for integer types as they are often
|
|
412
|
-
// inconsistent between introspection and generation (e.g. bigint(20) vs bigint)
|
|
413
|
-
if (type.includes('int')) {
|
|
414
|
-
type = type.replace(/\(\d+\)/, '');
|
|
415
|
-
}
|
|
416
|
-
// Add length if it's not already in the type string
|
|
417
|
-
if (column.length && !type.includes('(')) {
|
|
418
|
-
type = `${type}(${column.length})`;
|
|
419
|
-
}
|
|
420
|
-
else if (column.precision !== undefined && !type.includes('(')) {
|
|
421
|
-
if (column.scale !== undefined) {
|
|
422
|
-
type = `${type}(${column.precision},${column.scale})`;
|
|
423
|
-
}
|
|
424
|
-
else {
|
|
425
|
-
type = `${type}(${column.precision})`;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
return type;
|
|
425
|
+
const typeA = sqlToCanonical(current.type);
|
|
426
|
+
const typeB = sqlToCanonical(desired.type);
|
|
427
|
+
return areTypesEqual(typeA, typeB);
|
|
429
428
|
}
|
|
430
429
|
/**
|
|
431
430
|
* Compare two default values for equality
|
|
@@ -439,9 +438,7 @@ export class AbstractSchemaGenerator extends AbstractDialect {
|
|
|
439
438
|
if (val === null)
|
|
440
439
|
return 'null';
|
|
441
440
|
if (typeof val === 'string') {
|
|
442
|
-
// Remove type casts first (common in Postgres like ::text, ::character varying, ::timestamp without time zone)
|
|
443
441
|
let s = val.replace(/::[a-z_]+(\s+[a-z_]+)*(\[\])?$/i, '');
|
|
444
|
-
// Remove surrounding quotes
|
|
445
442
|
s = s.replace(/^'(.*)'$/, '$1');
|
|
446
443
|
if (s.toLowerCase() === 'null')
|
|
447
444
|
return 'null';
|
|
@@ -451,5 +448,240 @@ export class AbstractSchemaGenerator extends AbstractDialect {
|
|
|
451
448
|
};
|
|
452
449
|
return normalize(current) === normalize(desired);
|
|
453
450
|
}
|
|
451
|
+
// ============================================================================
|
|
452
|
+
// SchemaAST Support Methods
|
|
453
|
+
// ============================================================================
|
|
454
|
+
/**
|
|
455
|
+
* Generate CREATE TABLE SQL from a TableNode.
|
|
456
|
+
*/
|
|
457
|
+
generateCreateTableFromNode(table, options = {}) {
|
|
458
|
+
const columns = [];
|
|
459
|
+
const constraints = [];
|
|
460
|
+
for (const col of table.columns.values()) {
|
|
461
|
+
const colDef = this.generateColumnFromNode(col);
|
|
462
|
+
columns.push(colDef);
|
|
463
|
+
}
|
|
464
|
+
if (table.primaryKey.length > 1) {
|
|
465
|
+
const pkCols = table.primaryKey.map((c) => this.escapeId(c.name)).join(', ');
|
|
466
|
+
constraints.push(`PRIMARY KEY (${pkCols})`);
|
|
467
|
+
}
|
|
468
|
+
// Add table-level foreign keys if any
|
|
469
|
+
for (const rel of table.outgoingRelations) {
|
|
470
|
+
if (rel.from.columns.length > 0) {
|
|
471
|
+
const fromCols = rel.from.columns.map((c) => this.escapeId(c.name)).join(', ');
|
|
472
|
+
const toCols = rel.to.columns.map((c) => this.escapeId(c.name)).join(', ');
|
|
473
|
+
const constraintName = rel.name ? `CONSTRAINT ${this.escapeId(rel.name)} ` : '';
|
|
474
|
+
constraints.push(`${constraintName}FOREIGN KEY (${fromCols}) REFERENCES ${this.escapeId(rel.to.table.name)} (${toCols})` +
|
|
475
|
+
` ON DELETE ${rel.onDelete ?? this.defaultForeignKeyAction} ON UPDATE ${rel.onUpdate ?? this.defaultForeignKeyAction}`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
const ifNotExists = options.ifNotExists && this.config.supportsIfNotExists ? 'IF NOT EXISTS ' : '';
|
|
479
|
+
let sql = `CREATE TABLE ${ifNotExists}${this.escapeId(table.name)} (\n`;
|
|
480
|
+
sql += columns.map((col) => ` ${col}`).join(',\n');
|
|
481
|
+
if (constraints.length > 0) {
|
|
482
|
+
sql += ',\n';
|
|
483
|
+
sql += constraints.map((c) => ` ${c}`).join(',\n');
|
|
484
|
+
}
|
|
485
|
+
sql += '\n)';
|
|
486
|
+
if (this.config.tableOptions) {
|
|
487
|
+
sql += ` ${this.config.tableOptions}`;
|
|
488
|
+
}
|
|
489
|
+
sql += ';';
|
|
490
|
+
// Generate indexes as separate statements
|
|
491
|
+
const indexStatements = table.indexes.map((idx) => this.generateCreateIndexFromNode(idx, options)).join('\n');
|
|
492
|
+
return indexStatements ? `${sql}\n${indexStatements}` : sql;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Generate a column definition from a ColumnNode.
|
|
496
|
+
*/
|
|
497
|
+
generateColumnFromNode(col) {
|
|
498
|
+
const colName = this.escapeId(col.name);
|
|
499
|
+
let sqlType = this.canonicalTypeToSql(col.type);
|
|
500
|
+
if (col.isPrimaryKey && col.isAutoIncrement) {
|
|
501
|
+
sqlType = this.serialPrimaryKeyType;
|
|
502
|
+
}
|
|
503
|
+
let def = `${colName} ${sqlType}`;
|
|
504
|
+
if (!col.nullable && !col.isPrimaryKey) {
|
|
505
|
+
def += ' NOT NULL';
|
|
506
|
+
}
|
|
507
|
+
if (col.isPrimaryKey && col.table.primaryKey.length === 1 && !sqlType.includes('PRIMARY KEY')) {
|
|
508
|
+
def += ' PRIMARY KEY';
|
|
509
|
+
}
|
|
510
|
+
if (col.isUnique && !col.isPrimaryKey) {
|
|
511
|
+
def += ' UNIQUE';
|
|
512
|
+
}
|
|
513
|
+
if (col.defaultValue !== undefined) {
|
|
514
|
+
def += ` DEFAULT ${this.formatDefaultValue(col.defaultValue)}`;
|
|
515
|
+
}
|
|
516
|
+
if (col.comment) {
|
|
517
|
+
def += this.generateColumnComment(col.name, col.comment);
|
|
518
|
+
}
|
|
519
|
+
return def;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Generate CREATE INDEX SQL from an IndexNode.
|
|
523
|
+
*/
|
|
524
|
+
generateCreateIndexFromNode(index, options = {}) {
|
|
525
|
+
const uniqueStr = index.unique ? 'UNIQUE ' : '';
|
|
526
|
+
const columns = index.columns.map((c) => this.escapeId(c.name)).join(', ');
|
|
527
|
+
const tableName = this.escapeId(index.table.name);
|
|
528
|
+
const ifNotExists = options.ifNotExists && this.config.supportsIfNotExists ? 'IF NOT EXISTS ' : '';
|
|
529
|
+
return `CREATE ${uniqueStr}INDEX ${ifNotExists}${this.escapeId(index.name)} ON ${tableName} (${columns});`;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Generate DROP TABLE SQL from a TableNode.
|
|
533
|
+
*/
|
|
534
|
+
generateDropTableFromNode(table, options = {}) {
|
|
535
|
+
const ifExists = options.ifExists ? 'IF EXISTS ' : '';
|
|
536
|
+
return `DROP TABLE ${ifExists}${this.escapeId(table.name)};`;
|
|
537
|
+
}
|
|
538
|
+
// ============================================================================
|
|
539
|
+
// Phase 3: Builder Operation Methods (Moved forward for unification)
|
|
540
|
+
// ============================================================================
|
|
541
|
+
generateCreateTableFromDefinition(table, options = {}) {
|
|
542
|
+
const tableNode = this.tableDefinitionToNode(table);
|
|
543
|
+
return this.generateCreateTableFromNode(tableNode, options);
|
|
544
|
+
}
|
|
545
|
+
generateDropTableSql(tableName, options) {
|
|
546
|
+
const ifExists = options?.ifExists ? 'IF EXISTS ' : '';
|
|
547
|
+
const cascade = options?.cascade ? ' CASCADE' : '';
|
|
548
|
+
return `DROP TABLE ${ifExists}${this.escapeId(tableName)}${cascade};`;
|
|
549
|
+
}
|
|
550
|
+
generateRenameTableSql(oldName, newName) {
|
|
551
|
+
if (this.dialect === 'mysql' || this.dialect === 'mariadb') {
|
|
552
|
+
return `RENAME TABLE ${this.escapeId(oldName)} TO ${this.escapeId(newName)};`;
|
|
553
|
+
}
|
|
554
|
+
return `ALTER TABLE ${this.escapeId(oldName)} RENAME TO ${this.escapeId(newName)};`;
|
|
555
|
+
}
|
|
556
|
+
generateAddColumnSql(tableName, column) {
|
|
557
|
+
const colSql = this.generateColumnFromNode(this.fullColumnDefinitionToNode(column, tableName));
|
|
558
|
+
return `ALTER TABLE ${this.escapeId(tableName)} ADD COLUMN ${colSql};`;
|
|
559
|
+
}
|
|
560
|
+
generateAlterColumnSql(tableName, columnName, column) {
|
|
561
|
+
const colSql = this.generateColumnFromNode(this.fullColumnDefinitionToNode(column, tableName));
|
|
562
|
+
return this.generateAlterColumnStatements(tableName, { name: columnName, type: '' }, colSql).join('\n');
|
|
563
|
+
}
|
|
564
|
+
generateDropColumnSql(tableName, columnName) {
|
|
565
|
+
return `ALTER TABLE ${this.escapeId(tableName)} DROP COLUMN ${this.escapeId(columnName)};`;
|
|
566
|
+
}
|
|
567
|
+
generateRenameColumnSql(tableName, oldName, newName) {
|
|
568
|
+
return `ALTER TABLE ${this.escapeId(tableName)} RENAME COLUMN ${this.escapeId(oldName)} TO ${this.escapeId(newName)};`;
|
|
569
|
+
}
|
|
570
|
+
generateCreateIndexSql(tableName, index) {
|
|
571
|
+
return this.generateCreateIndex(tableName, index);
|
|
572
|
+
}
|
|
573
|
+
generateDropIndexSql(tableName, indexName) {
|
|
574
|
+
return this.generateDropIndex(tableName, indexName);
|
|
575
|
+
}
|
|
576
|
+
generateAddForeignKeySql(tableName, foreignKey) {
|
|
577
|
+
const fkCols = foreignKey.columns.map((c) => this.escapeId(c)).join(', ');
|
|
578
|
+
const refCols = foreignKey.referencesColumns.map((c) => this.escapeId(c)).join(', ');
|
|
579
|
+
const constraintName = foreignKey.name
|
|
580
|
+
? this.escapeId(foreignKey.name)
|
|
581
|
+
: this.escapeId(`fk_${tableName}_${foreignKey.columns.join('_')}`);
|
|
582
|
+
return (`ALTER TABLE ${this.escapeId(tableName)} ADD CONSTRAINT ${constraintName} ` +
|
|
583
|
+
`FOREIGN KEY (${fkCols}) REFERENCES ${this.escapeId(foreignKey.referencesTable)} (${refCols}) ` +
|
|
584
|
+
`ON DELETE ${foreignKey.onDelete ?? this.defaultForeignKeyAction} ON UPDATE ${foreignKey.onUpdate ?? this.defaultForeignKeyAction};`);
|
|
585
|
+
}
|
|
586
|
+
generateDropForeignKeySql(tableName, constraintName) {
|
|
587
|
+
return `ALTER TABLE ${this.escapeId(tableName)} ${this.config.dropForeignKeySyntax} ${this.escapeId(constraintName)};`;
|
|
588
|
+
}
|
|
589
|
+
tableDefinitionToNode(def) {
|
|
590
|
+
const columns = new Map();
|
|
591
|
+
const pkNodes = [];
|
|
592
|
+
const table = {
|
|
593
|
+
name: def.name,
|
|
594
|
+
columns,
|
|
595
|
+
primaryKey: [], // placeholder
|
|
596
|
+
indexes: [],
|
|
597
|
+
schema: { tables: new Map(), relationships: [], indexes: [] },
|
|
598
|
+
incomingRelations: [],
|
|
599
|
+
outgoingRelations: [],
|
|
600
|
+
comment: def.comment,
|
|
601
|
+
};
|
|
602
|
+
for (const colDef of def.columns) {
|
|
603
|
+
const node = this.fullColumnDefinitionToNode(colDef, def.name);
|
|
604
|
+
node.table = table;
|
|
605
|
+
columns.set(node.name, node);
|
|
606
|
+
if (node.isPrimaryKey) {
|
|
607
|
+
pkNodes.push(node);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
const finalPrimaryKey = def.primaryKey
|
|
611
|
+
? def.primaryKey.map((name) => columns.get(name)).filter((c) => c !== undefined)
|
|
612
|
+
: pkNodes;
|
|
613
|
+
table.primaryKey = finalPrimaryKey;
|
|
614
|
+
for (const idxDef of def.indexes) {
|
|
615
|
+
const indexNode = {
|
|
616
|
+
name: idxDef.name,
|
|
617
|
+
table,
|
|
618
|
+
columns: idxDef.columns.map((name) => columns.get(name)).filter((c) => c !== undefined),
|
|
619
|
+
unique: idxDef.unique,
|
|
620
|
+
};
|
|
621
|
+
table.indexes.push(indexNode);
|
|
622
|
+
}
|
|
623
|
+
for (const fkDef of def.foreignKeys) {
|
|
624
|
+
const relNode = {
|
|
625
|
+
name: fkDef.name ?? `fk_${def.name}_${fkDef.columns.join('_')}`,
|
|
626
|
+
type: 'ManyToOne', // Builder default
|
|
627
|
+
from: {
|
|
628
|
+
table,
|
|
629
|
+
columns: fkDef.columns.map((name) => columns.get(name)).filter((c) => c !== undefined),
|
|
630
|
+
},
|
|
631
|
+
to: {
|
|
632
|
+
table: { name: fkDef.referencesTable },
|
|
633
|
+
columns: fkDef.referencesColumns.map((name) => ({ name })),
|
|
634
|
+
},
|
|
635
|
+
onDelete: fkDef.onDelete,
|
|
636
|
+
onUpdate: fkDef.onUpdate,
|
|
637
|
+
};
|
|
638
|
+
table.outgoingRelations.push(relNode);
|
|
639
|
+
}
|
|
640
|
+
return table;
|
|
641
|
+
}
|
|
642
|
+
fullColumnDefinitionToNode(col, tableName) {
|
|
643
|
+
return {
|
|
644
|
+
name: col.name,
|
|
645
|
+
type: col.type,
|
|
646
|
+
nullable: col.nullable,
|
|
647
|
+
defaultValue: col.defaultValue,
|
|
648
|
+
isPrimaryKey: col.primaryKey,
|
|
649
|
+
isAutoIncrement: col.autoIncrement,
|
|
650
|
+
isUnique: col.unique,
|
|
651
|
+
comment: col.comment,
|
|
652
|
+
table: { name: tableName },
|
|
653
|
+
referencedBy: [],
|
|
654
|
+
references: col.foreignKey
|
|
655
|
+
? {
|
|
656
|
+
name: `fk_${tableName}_${col.name}`,
|
|
657
|
+
type: 'ManyToOne',
|
|
658
|
+
from: { table: { name: tableName }, columns: [] },
|
|
659
|
+
to: {
|
|
660
|
+
table: { name: col.foreignKey.table },
|
|
661
|
+
columns: col.foreignKey.columns.map((name) => ({ name })),
|
|
662
|
+
},
|
|
663
|
+
onDelete: col.foreignKey.onDelete,
|
|
664
|
+
onUpdate: col.foreignKey.onUpdate,
|
|
665
|
+
}
|
|
666
|
+
: undefined,
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
import { MongoSchemaGenerator } from './generator/mongoSchemaGenerator.js';
|
|
671
|
+
export { MongoSchemaGenerator };
|
|
672
|
+
/**
|
|
673
|
+
* Factory function to create a SchemaGenerator for a specific dialect.
|
|
674
|
+
* Returns undefined for unsupported dialects.
|
|
675
|
+
*/
|
|
676
|
+
export function createSchemaGenerator(dialect, namingStrategy, defaultForeignKeyAction) {
|
|
677
|
+
if (dialect === 'mongodb') {
|
|
678
|
+
return new MongoSchemaGenerator(namingStrategy, defaultForeignKeyAction);
|
|
679
|
+
}
|
|
680
|
+
// Check if dialect is supported (has config)
|
|
681
|
+
const supportedDialects = ['postgres', 'mysql', 'mariadb', 'sqlite'];
|
|
682
|
+
if (!supportedDialects.includes(dialect)) {
|
|
683
|
+
return undefined;
|
|
684
|
+
}
|
|
685
|
+
return new SqlSchemaGenerator(dialect, namingStrategy, defaultForeignKeyAction);
|
|
454
686
|
}
|
|
455
687
|
//# sourceMappingURL=schemaGenerator.js.map
|