uql-orm 0.21.0 → 0.22.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 +5 -4
- package/dist/bunSql/bunSqlCockroachDialect.d.ts +2 -2
- package/dist/bunSql/bunSqlCockroachDialect.js +4 -10
- package/dist/bunSql/bunSqlPostgresDialect.d.ts +2 -2
- package/dist/bunSql/bunSqlPostgresDialect.js +4 -10
- package/dist/cockroachdb/cockroachDialect.d.ts +20 -4
- package/dist/cockroachdb/cockroachDialect.js +33 -18
- package/dist/d1/d1Querier.d.ts +4 -3
- package/dist/d1/d1Querier.js +0 -3
- package/dist/d1/d1SqliteDialect.d.ts +7 -0
- package/dist/d1/d1SqliteDialect.js +9 -0
- package/dist/dialect/abstractDialect.d.ts +8 -3
- package/dist/dialect/abstractDialect.js +9 -4
- package/dist/dialect/abstractSqlDialect.d.ts +35 -7
- package/dist/dialect/abstractSqlDialect.js +76 -35
- package/dist/dialect/index.d.ts +1 -0
- package/dist/dialect/index.js +1 -0
- package/dist/dialect/indexSqlDialect.d.ts +57 -0
- package/dist/dialect/indexSqlDialect.js +101 -0
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +16 -1
- package/dist/dialect/mysqlLikeSqlDialect.js +32 -1
- package/dist/dialect/pgLikeSqlDialect.d.ts +33 -15
- package/dist/dialect/pgLikeSqlDialect.js +90 -56
- package/dist/dialect/vectorCast.d.ts +21 -0
- package/dist/dialect/vectorCast.js +28 -0
- package/dist/dialect/vectorSqlDialect.d.ts +10 -3
- package/dist/dialect/vectorSqlDialect.js +19 -8
- package/dist/entity/decorator/index-decorator.d.ts +2 -9
- package/dist/entity/metadata/definition.d.ts +5 -4
- package/dist/entity/metadata/definition.js +4 -3
- package/dist/libsql/libsqlDialect.d.ts +12 -2
- package/dist/libsql/libsqlDialect.js +14 -2
- package/dist/libsql/libsqlQuerier.d.ts +9 -22
- package/dist/libsql/libsqlQuerier.js +9 -85
- package/dist/libsql/libsqlQuerierPool.d.ts +10 -8
- package/dist/libsql/libsqlQuerierPool.js +17 -15
- package/dist/maria/mariaDialect.d.ts +26 -4
- package/dist/maria/mariaDialect.js +54 -15
- package/dist/migrate/builder/migrationBuilder.d.ts +9 -29
- package/dist/migrate/builder/migrationBuilder.js +58 -76
- package/dist/migrate/builder/splitSqlStatements.d.ts +0 -2
- package/dist/migrate/builder/splitSqlStatements.js +0 -2
- package/dist/migrate/builder/tableBuilder.d.ts +12 -2
- package/dist/migrate/builder/tableBuilder.js +71 -119
- package/dist/migrate/builder/types.d.ts +30 -41
- package/dist/migrate/cli.d.ts +3 -2
- package/dist/migrate/cli.js +21 -48
- package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -4
- package/dist/migrate/codegen/entityCodeGenerator.js +5 -63
- package/dist/migrate/codegen/fieldOptionsSource.d.ts +10 -0
- package/dist/migrate/codegen/fieldOptionsSource.js +55 -0
- package/dist/migrate/codegen/index.d.ts +0 -2
- package/dist/migrate/codegen/index.js +0 -4
- package/dist/migrate/generator/indexNodeToSchema.d.ts +9 -0
- package/dist/migrate/generator/indexNodeToSchema.js +14 -0
- package/dist/migrate/generator/mongoCommand.d.ts +57 -0
- package/dist/migrate/generator/mongoCommand.js +26 -0
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +9 -20
- package/dist/migrate/generator/mongoSchemaGenerator.js +40 -82
- package/dist/migrate/index.d.ts +0 -1
- package/dist/migrate/index.js +0 -1
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +25 -10
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +46 -32
- package/dist/migrate/introspection/baseSqlIntrospector.d.ts +0 -1
- package/dist/migrate/introspection/baseSqlIntrospector.js +3 -12
- package/dist/migrate/introspection/mongoIntrospector.js +2 -2
- package/dist/migrate/introspection/mysqlIntrospector.d.ts +5 -8
- package/dist/migrate/introspection/mysqlIntrospector.js +4 -10
- package/dist/migrate/introspection/postgresIntrospector.d.ts +5 -8
- package/dist/migrate/introspection/postgresIntrospector.js +4 -10
- package/dist/migrate/introspection/sqliteIntrospector.d.ts +6 -5
- package/dist/migrate/introspection/sqliteIntrospector.js +26 -18
- package/dist/migrate/migrator.d.ts +23 -3
- package/dist/migrate/migrator.js +57 -69
- package/dist/migrate/schemaGenerator.d.ts +26 -37
- package/dist/migrate/schemaGenerator.js +69 -174
- package/dist/migrate/schemaGeneratorAsync.d.ts +2 -2
- package/dist/migrate/schemaGeneratorAsync.js +3 -3
- package/dist/migrate/storage/databaseStorage.js +4 -4
- package/dist/mongo/mongoDialect.d.ts +2 -0
- package/dist/mongo/mongoDialect.js +14 -3
- package/dist/mysql/mysqlDialect.d.ts +8 -0
- package/dist/mysql/mysqlDialect.js +13 -0
- package/dist/schema/canonicalType.d.ts +14 -2
- package/dist/schema/canonicalType.js +41 -31
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/schemaASTBuilder.d.ts +8 -1
- package/dist/schema/schemaASTBuilder.js +56 -47
- package/dist/schema/types.d.ts +10 -37
- package/dist/sqlite/abstractSqliteQuerier.d.ts +39 -0
- package/dist/sqlite/abstractSqliteQuerier.js +44 -0
- package/dist/sqlite/bunSqliteAdapter.bun.d.ts +26 -0
- package/dist/sqlite/bunSqliteAdapter.bun.js +25 -0
- package/dist/sqlite/hranaQuerier.d.ts +54 -0
- package/dist/sqlite/hranaQuerier.js +90 -0
- package/dist/sqlite/hranaQuerierPool.d.ts +21 -0
- package/dist/sqlite/hranaQuerierPool.js +25 -0
- package/dist/sqlite/index.d.ts +2 -1
- package/dist/sqlite/index.js +2 -1
- package/dist/sqlite/sqliteDialect.d.ts +18 -4
- package/dist/sqlite/sqliteDialect.js +24 -24
- package/dist/sqlite/sqliteQuerier.d.ts +16 -9
- package/dist/sqlite/sqliteQuerier.js +4 -27
- package/dist/sqlite/sqliteQuerierPool.d.ts +14 -4
- package/dist/sqlite/sqliteQuerierPool.js +16 -5
- package/dist/turso/index.d.ts +3 -0
- package/dist/turso/index.js +3 -0
- package/dist/turso/local.d.ts +3 -0
- package/dist/turso/local.js +3 -0
- package/dist/turso/tursoDialect.d.ts +15 -0
- package/dist/turso/tursoDialect.js +18 -0
- package/dist/turso/tursoLocalQuerier.d.ts +25 -0
- package/dist/turso/tursoLocalQuerier.js +20 -0
- package/dist/turso/tursoLocalQuerierPool.d.ts +32 -0
- package/dist/turso/tursoLocalQuerierPool.js +39 -0
- package/dist/turso/tursoQuerier.d.ts +10 -0
- package/dist/turso/tursoQuerier.js +10 -0
- package/dist/turso/tursoQuerierPool.d.ts +39 -0
- package/dist/turso/tursoQuerierPool.js +38 -0
- package/dist/type/config.d.ts +0 -6
- package/dist/type/dialect.d.ts +12 -4
- package/dist/type/dialect.js +7 -1
- package/dist/type/entity.d.ts +66 -5
- package/dist/type/migration.d.ts +32 -23
- package/dist/type/vector.d.ts +5 -2
- package/dist/util/index.d.ts +1 -0
- package/dist/util/index.js +1 -0
- package/dist/util/indexColumn.util.d.ts +6 -0
- package/dist/util/indexColumn.util.js +26 -0
- package/package.json +16 -3
- package/dist/migrate/codegen/entityMerger.d.ts +0 -114
- package/dist/migrate/codegen/entityMerger.js +0 -293
- package/dist/migrate/codegen/migrationCodeGenerator.d.ts +0 -61
- package/dist/migrate/codegen/migrationCodeGenerator.js +0 -355
- package/dist/migrate/generator/index.d.ts +0 -2
- package/dist/migrate/generator/index.js +0 -2
- package/dist/migrate/sync/index.d.ts +0 -6
- package/dist/migrate/sync/index.js +0 -6
- package/dist/migrate/sync/schemaSync.d.ts +0 -131
- package/dist/migrate/sync/schemaSync.js +0 -259
- package/dist/sqlite/betterSqlite3Dialect.d.ts +0 -9
- package/dist/sqlite/betterSqlite3Dialect.js +0 -9
|
@@ -32,16 +32,16 @@ export class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
32
32
|
}
|
|
33
33
|
// SQLite uses PRAGMA which doesn't use parameterized queries in the same way
|
|
34
34
|
getColumnsQuery(tableName) {
|
|
35
|
-
return `PRAGMA table_info(${this.escapeId(tableName)})`;
|
|
35
|
+
return /*sql*/ `PRAGMA table_info(${this.escapeId(tableName)})`;
|
|
36
36
|
}
|
|
37
37
|
getIndexesQuery(tableName) {
|
|
38
|
-
return `PRAGMA index_list(${this.escapeId(tableName)})`;
|
|
38
|
+
return /*sql*/ `PRAGMA index_list(${this.escapeId(tableName)})`;
|
|
39
39
|
}
|
|
40
40
|
getForeignKeysQuery(tableName) {
|
|
41
|
-
return `PRAGMA foreign_key_list(${this.escapeId(tableName)})`;
|
|
41
|
+
return /*sql*/ `PRAGMA foreign_key_list(${this.escapeId(tableName)})`;
|
|
42
42
|
}
|
|
43
43
|
getPrimaryKeyQuery(tableName) {
|
|
44
|
-
return `PRAGMA table_info(${this.escapeId(tableName)})`;
|
|
44
|
+
return /*sql*/ `PRAGMA table_info(${this.escapeId(tableName)})`;
|
|
45
45
|
}
|
|
46
46
|
getColumnsParams(_tableName) {
|
|
47
47
|
return [];
|
|
@@ -61,9 +61,9 @@ export class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
61
61
|
mapTableNameRow(row) {
|
|
62
62
|
return row.name;
|
|
63
63
|
}
|
|
64
|
-
async mapColumnsResult(
|
|
64
|
+
async mapColumnsResult(read, tableName, results) {
|
|
65
65
|
// Get unique columns from indexes
|
|
66
|
-
const uniqueColumns = await this.getUniqueColumns(
|
|
66
|
+
const uniqueColumns = await this.getUniqueColumns(read, tableName);
|
|
67
67
|
return results.map((row) => ({
|
|
68
68
|
name: row.name,
|
|
69
69
|
type: this.normalizeType(row.type),
|
|
@@ -78,25 +78,29 @@ export class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
78
78
|
comment: undefined, // SQLite doesn't support column comments
|
|
79
79
|
}));
|
|
80
80
|
}
|
|
81
|
-
async mapIndexesResult(
|
|
81
|
+
async mapIndexesResult(read, _tableName, results) {
|
|
82
82
|
const indexSchemas = [];
|
|
83
83
|
for (const index of results) {
|
|
84
|
-
const columns = await
|
|
84
|
+
const columns = await this.getIndexColumns(read, index.name);
|
|
85
85
|
// Include user-created indexes ('c') and multi-column unique constraints ('u')
|
|
86
86
|
// Skip primary key indexes ('pk') and single-column unique constraints
|
|
87
87
|
const isUserCreated = index.origin === 'c';
|
|
88
88
|
const isCompositeUnique = index.origin === 'u' && columns.length > 1;
|
|
89
|
-
|
|
89
|
+
// `PRAGMA index_info` names an expression entry `null` (its `cid` is -2), and the expression text
|
|
90
|
+
// lives only in `sqlite_master.sql`. Reporting `{ column: null }` put a column literally named
|
|
91
|
+
// `null` into the diff, so an index UQL cannot describe is left out entirely instead.
|
|
92
|
+
const named = columns.filter((column) => column.name !== null);
|
|
93
|
+
if (named.length === columns.length && (isUserCreated || isCompositeUnique)) {
|
|
90
94
|
indexSchemas.push({
|
|
91
95
|
name: index.name,
|
|
92
|
-
columns:
|
|
96
|
+
columns: named.map((column) => ({ column: column.name })),
|
|
93
97
|
unique: Boolean(index.unique),
|
|
94
98
|
});
|
|
95
99
|
}
|
|
96
100
|
}
|
|
97
101
|
return indexSchemas;
|
|
98
102
|
}
|
|
99
|
-
async mapForeignKeysResult(
|
|
103
|
+
async mapForeignKeysResult(_read, tableName, results) {
|
|
100
104
|
// Group by id to handle composite foreign keys
|
|
101
105
|
const grouped = new Map();
|
|
102
106
|
for (const row of results) {
|
|
@@ -127,20 +131,24 @@ export class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
127
131
|
// ============================================================================
|
|
128
132
|
// SQLite-specific helpers
|
|
129
133
|
// ============================================================================
|
|
130
|
-
async getUniqueColumns(
|
|
131
|
-
const
|
|
134
|
+
async getUniqueColumns(read, tableName) {
|
|
135
|
+
const indexes = await read(this.getIndexesQuery(tableName));
|
|
132
136
|
const uniqueColumns = new Set();
|
|
133
|
-
for (const index of
|
|
137
|
+
for (const index of indexes) {
|
|
134
138
|
if (index.unique) {
|
|
135
|
-
const
|
|
136
|
-
// Only single-column unique constraints
|
|
137
|
-
|
|
138
|
-
|
|
139
|
+
const columns = await this.getIndexColumns(read, index.name);
|
|
140
|
+
// Only single-column unique constraints, and only over a real column (not an expression)
|
|
141
|
+
const [column] = columns;
|
|
142
|
+
if (columns.length === 1 && column.name !== null) {
|
|
143
|
+
uniqueColumns.add(column.name);
|
|
139
144
|
}
|
|
140
145
|
}
|
|
141
146
|
}
|
|
142
147
|
return uniqueColumns;
|
|
143
148
|
}
|
|
149
|
+
getIndexColumns(read, indexName) {
|
|
150
|
+
return read(/*sql*/ `PRAGMA index_info(${this.escapeId(indexName)})`);
|
|
151
|
+
}
|
|
144
152
|
normalizeType(type) {
|
|
145
153
|
// Extract base type without length/precision
|
|
146
154
|
const match = type.match(/^([A-Za-z]+)/);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DialectName, LoggingOptions, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorOptions, MongoQuerier,
|
|
1
|
+
import type { DialectName, LoggingOptions, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorOptions, MongoQuerier, Querier, QuerierPool, SchemaDiff, SchemaGenerator, SchemaIntrospector, Type } from '../type/index.js';
|
|
2
2
|
import { LoggerWrapper } from '../util/index.js';
|
|
3
3
|
import type { IMigrationBuilder } from './builder/types.js';
|
|
4
4
|
/**
|
|
@@ -16,7 +16,7 @@ export declare class Migrator {
|
|
|
16
16
|
readonly dialectName: DialectName;
|
|
17
17
|
schemaGenerator?: SchemaGenerator;
|
|
18
18
|
schemaIntrospector?: SchemaIntrospector;
|
|
19
|
-
private readonly
|
|
19
|
+
private readonly _defaultForeignKeyAction?;
|
|
20
20
|
private _mongoSchemaLoadPromise?;
|
|
21
21
|
constructor(pool: QuerierPool, options?: MigratorOptions);
|
|
22
22
|
/** Loads MongoDB schema generator on first use; SQL generators are set in the constructor (or via {@link setSchemaGenerator}). */
|
|
@@ -26,7 +26,7 @@ export declare class Migrator {
|
|
|
26
26
|
*/
|
|
27
27
|
setSchemaGenerator(generator: SchemaGenerator): void;
|
|
28
28
|
protected createIntrospector(): SchemaIntrospector | undefined;
|
|
29
|
-
protected createGenerator(
|
|
29
|
+
protected createGenerator(): SchemaGenerator | undefined;
|
|
30
30
|
/**
|
|
31
31
|
* Get all discovered migrations from the migrations directory
|
|
32
32
|
*/
|
|
@@ -88,6 +88,26 @@ export declare class Migrator {
|
|
|
88
88
|
drop?: boolean;
|
|
89
89
|
logging?: boolean;
|
|
90
90
|
}): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* The DDL {@link autoSync} would run, without running it. Separate so `--dry-run` shows the real
|
|
93
|
+
* statements rather than a summary of a second, differently-computed diff.
|
|
94
|
+
*/
|
|
95
|
+
planSync(options?: {
|
|
96
|
+
safe?: boolean;
|
|
97
|
+
drop?: boolean;
|
|
98
|
+
}): Promise<string[]>;
|
|
99
|
+
/**
|
|
100
|
+
* Each pending diff with the entity it came from, since resolving that is async and every caller
|
|
101
|
+
* needs it. What to emit stays with the caller: a sync narrows the forward direction to what the
|
|
102
|
+
* caller allows and never asks for the rollback, which on SQLite cannot even be expressed (no
|
|
103
|
+
* `ALTER COLUMN`), so computing it eagerly for everyone would throw there.
|
|
104
|
+
*/
|
|
105
|
+
private pendingDiffs;
|
|
106
|
+
/**
|
|
107
|
+
* The schema generator. A getter because MongoDB's loads lazily (see {@link ensureSchemaGenerator}),
|
|
108
|
+
* so every caller had to repeat the same assertion after awaiting it.
|
|
109
|
+
*/
|
|
110
|
+
private get generator();
|
|
91
111
|
protected filterDiff(diff: SchemaDiff, options: {
|
|
92
112
|
safe?: boolean;
|
|
93
113
|
drop?: boolean;
|
package/dist/migrate/migrator.js
CHANGED
|
@@ -7,6 +7,7 @@ import { isKnownMigratorDialect, isSqlQuerier } from '../type/index.js';
|
|
|
7
7
|
import { LoggerWrapper } from '../util/index.js';
|
|
8
8
|
import { acquireQuerierForMigrations } from './acquireQuerierForMigrations.js';
|
|
9
9
|
import { buildSqlQuerierMigrationModule, EMPTY_MANUAL_MIGRATION_DOWN_INNER, EMPTY_MANUAL_MIGRATION_UP_INNER, emitSqlRunCalls, } from './codegen/migrationFile.js';
|
|
10
|
+
import { runMongoCommand } from './generator/mongoCommand.js';
|
|
10
11
|
import { MongoSchemaIntrospector, MysqlSchemaIntrospector, PostgresSchemaIntrospector, SqliteSchemaIntrospector, } from './introspection/index.js';
|
|
11
12
|
import { createSchemaGenerator } from './schemaGenerator.js';
|
|
12
13
|
import { createSchemaGeneratorAsync } from './schemaGeneratorAsync.js';
|
|
@@ -32,12 +33,12 @@ export class Migrator {
|
|
|
32
33
|
dialectName;
|
|
33
34
|
schemaGenerator;
|
|
34
35
|
schemaIntrospector;
|
|
35
|
-
|
|
36
|
+
_defaultForeignKeyAction;
|
|
36
37
|
_mongoSchemaLoadPromise;
|
|
37
38
|
constructor(pool, options = {}) {
|
|
38
39
|
this.pool = pool;
|
|
39
40
|
this.dialectName = pool.dialect.dialectName ?? 'postgres';
|
|
40
|
-
this.
|
|
41
|
+
this._defaultForeignKeyAction = options.defaultForeignKeyAction;
|
|
41
42
|
this.storage =
|
|
42
43
|
options.storage ??
|
|
43
44
|
new DatabaseMigrationStorage(pool, {
|
|
@@ -48,8 +49,7 @@ export class Migrator {
|
|
|
48
49
|
this._entities = options.entities;
|
|
49
50
|
this.schemaIntrospector = this.createIntrospector();
|
|
50
51
|
this.schemaGenerator =
|
|
51
|
-
options.schemaGenerator ??
|
|
52
|
-
(this.dialectName === 'mongodb' ? undefined : this.createGenerator(options.namingStrategy));
|
|
52
|
+
options.schemaGenerator ?? (this.dialectName === 'mongodb' ? undefined : this.createGenerator());
|
|
53
53
|
}
|
|
54
54
|
/** Loads MongoDB schema generator on first use; SQL generators are set in the constructor (or via {@link setSchemaGenerator}). */
|
|
55
55
|
async ensureSchemaGenerator() {
|
|
@@ -57,7 +57,7 @@ export class Migrator {
|
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
59
59
|
if (!this._mongoSchemaLoadPromise) {
|
|
60
|
-
this._mongoSchemaLoadPromise = createSchemaGeneratorAsync(this.pool.dialect, this.
|
|
60
|
+
this._mongoSchemaLoadPromise = createSchemaGeneratorAsync(this.pool.dialect, this._defaultForeignKeyAction).then((gen) => {
|
|
61
61
|
if (gen) {
|
|
62
62
|
this.schemaGenerator = gen;
|
|
63
63
|
}
|
|
@@ -91,11 +91,11 @@ export class Migrator {
|
|
|
91
91
|
return undefined;
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
-
createGenerator(
|
|
94
|
+
createGenerator() {
|
|
95
95
|
if (!isKnownMigratorDialect(this.dialectName)) {
|
|
96
96
|
return undefined;
|
|
97
97
|
}
|
|
98
|
-
return createSchemaGenerator(this.pool.dialect,
|
|
98
|
+
return createSchemaGenerator(this.pool.dialect, this._defaultForeignKeyAction);
|
|
99
99
|
}
|
|
100
100
|
/**
|
|
101
101
|
* Get all discovered migrations from the migrations directory
|
|
@@ -251,26 +251,18 @@ export class Migrator {
|
|
|
251
251
|
* Generate a migration based on entity schema differences
|
|
252
252
|
*/
|
|
253
253
|
async generateFromEntities(name) {
|
|
254
|
-
await this.ensureSchemaGenerator();
|
|
255
|
-
if (!this.schemaGenerator) {
|
|
256
|
-
throw new Error('Schema generator not set. Call setSchemaGenerator() first.');
|
|
257
|
-
}
|
|
258
|
-
const diffs = await this.getDiffs();
|
|
259
254
|
const upStatements = [];
|
|
260
255
|
const downStatements = [];
|
|
261
|
-
for (const diff of
|
|
256
|
+
for (const { diff, entity } of await this.pendingDiffs()) {
|
|
262
257
|
if (diff.type === 'create') {
|
|
263
|
-
const entity = await this.findEntityForTable(diff.tableName);
|
|
264
258
|
if (entity) {
|
|
265
|
-
upStatements.push(...this.
|
|
266
|
-
downStatements.push(this.
|
|
259
|
+
upStatements.push(...this.generator.generateCreateTable(entity));
|
|
260
|
+
downStatements.push(this.generator.generateDropTable(diff.tableName, { ifExists: true }));
|
|
267
261
|
}
|
|
268
262
|
}
|
|
269
263
|
else if (diff.type === 'alter') {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const alterDownStatements = this.schemaGenerator.generateAlterTableDown(diff);
|
|
273
|
-
downStatements.push(...alterDownStatements);
|
|
264
|
+
upStatements.push(...this.generator.generateAlterTable(diff));
|
|
265
|
+
downStatements.push(...this.generator.generateAlterTableDown(diff));
|
|
274
266
|
}
|
|
275
267
|
}
|
|
276
268
|
if (upStatements.length === 0) {
|
|
@@ -342,9 +334,6 @@ export class Migrator {
|
|
|
342
334
|
*/
|
|
343
335
|
async syncForce() {
|
|
344
336
|
await this.ensureSchemaGenerator();
|
|
345
|
-
if (!this.schemaGenerator) {
|
|
346
|
-
throw new Error('Schema generator not set. Call setSchemaGenerator() first.');
|
|
347
|
-
}
|
|
348
337
|
const querier = await acquireQuerierForMigrations(this.pool);
|
|
349
338
|
if (!isSqlQuerier(querier)) {
|
|
350
339
|
await querier.release();
|
|
@@ -354,13 +343,14 @@ export class Migrator {
|
|
|
354
343
|
await querier.beginTransaction();
|
|
355
344
|
// Drop all tables first (in reverse order for foreign keys)
|
|
356
345
|
for (const entity of [...this.entities].reverse()) {
|
|
357
|
-
const
|
|
346
|
+
const tableName = this.generator.resolveTableName(entity, getMeta(entity));
|
|
347
|
+
const dropSql = this.generator.generateDropTable(tableName, { ifExists: true });
|
|
358
348
|
this.logger.logSchema(`Executing: ${dropSql}`);
|
|
359
349
|
await querier.run(dropSql);
|
|
360
350
|
}
|
|
361
351
|
// Create all tables
|
|
362
352
|
for (const entity of this.entities) {
|
|
363
|
-
const createStmts = this.
|
|
353
|
+
const createStmts = this.generator.generateCreateTable(entity);
|
|
364
354
|
for (const createSql of createStmts) {
|
|
365
355
|
this.logger.logSchema(`Executing: ${createSql}`);
|
|
366
356
|
await querier.run(createSql);
|
|
@@ -381,31 +371,53 @@ export class Migrator {
|
|
|
381
371
|
* Safely synchronizes the schema by only adding missing tables and columns.
|
|
382
372
|
*/
|
|
383
373
|
async autoSync(options = {}) {
|
|
384
|
-
await this.
|
|
385
|
-
if (
|
|
386
|
-
|
|
374
|
+
const statements = await this.planSync(options);
|
|
375
|
+
if (statements.length === 0) {
|
|
376
|
+
if (options.logging)
|
|
377
|
+
this.logger.logSchema('Schema is already in sync.');
|
|
378
|
+
return;
|
|
387
379
|
}
|
|
388
|
-
|
|
380
|
+
await this.executeSyncStatements(statements, options);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* The DDL {@link autoSync} would run, without running it. Separate so `--dry-run` shows the real
|
|
384
|
+
* statements rather than a summary of a second, differently-computed diff.
|
|
385
|
+
*/
|
|
386
|
+
async planSync(options = {}) {
|
|
389
387
|
const statements = [];
|
|
390
|
-
for (const diff of
|
|
388
|
+
for (const { diff, entity } of await this.pendingDiffs()) {
|
|
391
389
|
if (diff.type === 'create') {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
statements.push(...this.schemaGenerator.generateCreateTable(entity));
|
|
395
|
-
}
|
|
390
|
+
if (entity)
|
|
391
|
+
statements.push(...this.generator.generateCreateTable(entity));
|
|
396
392
|
}
|
|
397
393
|
else if (diff.type === 'alter') {
|
|
398
|
-
|
|
399
|
-
const alterStatements = this.schemaGenerator.generateAlterTable(filteredDiff);
|
|
400
|
-
statements.push(...alterStatements);
|
|
394
|
+
statements.push(...this.generator.generateAlterTable(this.filterDiff(diff, options)));
|
|
401
395
|
}
|
|
402
396
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
397
|
+
return statements;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Each pending diff with the entity it came from, since resolving that is async and every caller
|
|
401
|
+
* needs it. What to emit stays with the caller: a sync narrows the forward direction to what the
|
|
402
|
+
* caller allows and never asks for the rollback, which on SQLite cannot even be expressed (no
|
|
403
|
+
* `ALTER COLUMN`), so computing it eagerly for everyone would throw there.
|
|
404
|
+
*/
|
|
405
|
+
async pendingDiffs() {
|
|
406
|
+
const diffs = await this.getDiffs();
|
|
407
|
+
return Promise.all(diffs.map(async (diff) => ({
|
|
408
|
+
diff,
|
|
409
|
+
entity: diff.type === 'create' ? await this.findEntityForTable(diff.tableName) : undefined,
|
|
410
|
+
})));
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* The schema generator. A getter because MongoDB's loads lazily (see {@link ensureSchemaGenerator}),
|
|
414
|
+
* so every caller had to repeat the same assertion after awaiting it.
|
|
415
|
+
*/
|
|
416
|
+
get generator() {
|
|
417
|
+
if (!this.schemaGenerator) {
|
|
418
|
+
throw new Error('Schema generator not set. Call setSchemaGenerator() first.');
|
|
407
419
|
}
|
|
408
|
-
|
|
420
|
+
return this.schemaGenerator;
|
|
409
421
|
}
|
|
410
422
|
filterDiff(diff, options) {
|
|
411
423
|
const filteredDiff = { ...diff };
|
|
@@ -452,34 +464,10 @@ export class Migrator {
|
|
|
452
464
|
}
|
|
453
465
|
}
|
|
454
466
|
async executeMongoSyncStatements(statements, options, querier) {
|
|
455
|
-
const
|
|
456
|
-
for (const stmt of statements) {
|
|
457
|
-
const cmd = JSON.parse(stmt);
|
|
467
|
+
for (const statement of statements) {
|
|
458
468
|
if (options.logging)
|
|
459
|
-
this.logger.logSchema(`Executing MongoDB: ${
|
|
460
|
-
|
|
461
|
-
if (!collectionName) {
|
|
462
|
-
throw new Error(`MongoDB command missing collection name: ${stmt}`);
|
|
463
|
-
}
|
|
464
|
-
const collection = db.collection(collectionName);
|
|
465
|
-
if (cmd.action === 'createCollection') {
|
|
466
|
-
await db.createCollection(cmd.name);
|
|
467
|
-
if (cmd.indexes?.length) {
|
|
468
|
-
for (const idx of cmd.indexes) {
|
|
469
|
-
const key = Object.fromEntries(idx.columns.map((c) => [c, 1]));
|
|
470
|
-
await collection.createIndex(key, { unique: idx.unique, name: idx.name });
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
else if (cmd.action === 'dropCollection') {
|
|
475
|
-
await collection.drop();
|
|
476
|
-
}
|
|
477
|
-
else if (cmd.action === 'createIndex') {
|
|
478
|
-
await collection.createIndex(cmd.key, cmd.options);
|
|
479
|
-
}
|
|
480
|
-
else if (cmd.action === 'dropIndex') {
|
|
481
|
-
await collection.dropIndex(cmd.name);
|
|
482
|
-
}
|
|
469
|
+
this.logger.logSchema(`Executing MongoDB: ${statement}`);
|
|
470
|
+
await runMongoCommand(querier.db, statement);
|
|
483
471
|
}
|
|
484
472
|
}
|
|
485
473
|
async executeSqlSyncStatements(statements, options, querier) {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { type AbstractDialect, AbstractSqlDialect } from '../dialect/index.js';
|
|
2
2
|
import type { CanonicalType, ColumnNode, ForeignKeyAction, IndexNode, TableNode } from '../schema/types.js';
|
|
3
|
-
import type { ColumnSchema, DialectFeatures, EntityMeta, FieldOptions, IndexSchema, NamingStrategy, SchemaDiff,
|
|
3
|
+
import type { ColumnSchema, DialectFeatures, EntityMeta, FieldOptions, IndexSchema, NamingStrategy, SchemaDiff, SqlDdlGenerator, Type } from '../type/index.js';
|
|
4
4
|
import type { FullColumnDefinition, TableDefinition, TableForeignKeyDefinition } from './builder/types.js';
|
|
5
5
|
/**
|
|
6
6
|
* Unified SQL schema generator.
|
|
7
7
|
* Parameterized by dialect to handle Postgres, MySQL, MariaDB, and SQLite.
|
|
8
8
|
*/
|
|
9
|
-
export declare class SqlSchemaGenerator implements
|
|
9
|
+
export declare class SqlSchemaGenerator implements SqlDdlGenerator {
|
|
10
10
|
protected readonly dialect: AbstractSqlDialect;
|
|
11
11
|
protected readonly defaultForeignKeyAction: ForeignKeyAction;
|
|
12
12
|
constructor(dialect: AbstractSqlDialect, defaultForeignKeyAction?: ForeignKeyAction);
|
|
@@ -26,15 +26,14 @@ export declare class SqlSchemaGenerator implements SchemaGenerator {
|
|
|
26
26
|
* Convert FieldOptions to CanonicalType using the unified type system.
|
|
27
27
|
*/
|
|
28
28
|
protected getCanonicalType(field: FieldOptions, fieldType?: unknown): CanonicalType;
|
|
29
|
-
|
|
30
|
-
* Convert CanonicalType to SQL type string for this dialect.
|
|
31
|
-
* Also handles legacy string types for backward compatibility.
|
|
32
|
-
*/
|
|
33
|
-
protected canonicalTypeToSql(type: CanonicalType | string): string;
|
|
29
|
+
protected canonicalTypeToSql(type: CanonicalType): string;
|
|
34
30
|
generateCreateTable<E>(entity: Type<E>, options?: {
|
|
35
31
|
ifNotExists?: boolean;
|
|
36
32
|
}): string[];
|
|
37
|
-
generateDropTable
|
|
33
|
+
generateDropTable(tableName: string, options?: {
|
|
34
|
+
ifExists?: boolean;
|
|
35
|
+
cascade?: boolean;
|
|
36
|
+
}): string;
|
|
38
37
|
generateAlterTable(diff: SchemaDiff): string[];
|
|
39
38
|
generateAlterTableDown(diff: SchemaDiff): string[];
|
|
40
39
|
generateCreateIndex(tableName: string, index: IndexSchema, options?: {
|
|
@@ -42,25 +41,24 @@ export declare class SqlSchemaGenerator implements SchemaGenerator {
|
|
|
42
41
|
}): string;
|
|
43
42
|
generateDropIndex(tableName: string, indexName: string): string;
|
|
44
43
|
/**
|
|
45
|
-
* Generate column definition from a ColumnSchema
|
|
44
|
+
* Generate a column definition from a {@link ColumnSchema}, whose type is the engine's own spelling
|
|
45
|
+
* and may already carry its precision (or even `PRIMARY KEY`, for a serial).
|
|
46
46
|
*/
|
|
47
47
|
generateColumnDefinitionFromSchema(column: ColumnSchema, options?: {
|
|
48
48
|
includePrimaryKey?: boolean;
|
|
49
49
|
includeUnique?: boolean;
|
|
50
50
|
}): string;
|
|
51
|
-
getSqlType(field: FieldOptions, fieldType?: unknown): string;
|
|
52
51
|
/**
|
|
53
|
-
*
|
|
52
|
+
* The one place a column definition is spelled. Both callers reach it - the `ColumnSchema` path above
|
|
53
|
+
* and the `ColumnNode` path below - because the clause order and the "no NOT NULL on a primary key"
|
|
54
|
+
* rules are the same everywhere, and having them written twice is how the two paths drifted.
|
|
54
55
|
*/
|
|
55
|
-
|
|
56
|
+
private renderColumn;
|
|
57
|
+
getSqlType(field: FieldOptions, fieldType?: unknown): string;
|
|
56
58
|
/**
|
|
57
59
|
* Generate ALTER COLUMN statements (database-specific)
|
|
58
60
|
*/
|
|
59
61
|
generateAlterColumnStatements(tableName: string, column: ColumnSchema, newDefinition: string): string[];
|
|
60
|
-
/**
|
|
61
|
-
* Get table options (e.g., ENGINE for MySQL)
|
|
62
|
-
*/
|
|
63
|
-
getTableOptions<E>(_meta: EntityMeta<E>): string;
|
|
64
62
|
/**
|
|
65
63
|
* Generate column comment clause (if supported)
|
|
66
64
|
*/
|
|
@@ -75,7 +73,8 @@ export declare class SqlSchemaGenerator implements SchemaGenerator {
|
|
|
75
73
|
diffSchema<E>(entity: Type<E>, currentTable: TableNode | undefined): SchemaDiff | undefined;
|
|
76
74
|
private columnNodeToSchema;
|
|
77
75
|
/**
|
|
78
|
-
* Convert field options to ColumnSchema
|
|
76
|
+
* Convert field options to ColumnSchema. Both sides of a diff are the engine's SQL spelling: what it
|
|
77
|
+
* would create for this field, against what it reported for the existing column.
|
|
79
78
|
*/
|
|
80
79
|
protected fieldToColumnSchema<E>(fieldKey: string, field: FieldOptions, meta: EntityMeta<E>): ColumnSchema;
|
|
81
80
|
/**
|
|
@@ -83,7 +82,13 @@ export declare class SqlSchemaGenerator implements SchemaGenerator {
|
|
|
83
82
|
*/
|
|
84
83
|
protected columnsNeedAlteration(current: ColumnSchema, desired: ColumnSchema): boolean;
|
|
85
84
|
/**
|
|
86
|
-
*
|
|
85
|
+
* Whether two column types are the same *as this engine stores them*.
|
|
86
|
+
*
|
|
87
|
+
* Both sides are SQL spellings, parsed back to canonical so that `INT` and `INTEGER`, or `DATETIME`
|
|
88
|
+
* and `TIMESTAMP`, do not read as a change. Do not "simplify" this into comparing the entity's
|
|
89
|
+
* canonical type against the column's: several canonical types share one storage type per engine
|
|
90
|
+
* (`boolean` is `TINYINT(1)` on MySQL and `INTEGER` on SQLite), so that comparison reports an
|
|
91
|
+
* alteration for those columns on every single sync.
|
|
87
92
|
*/
|
|
88
93
|
protected isTypeEqual(current: ColumnSchema, desired: ColumnSchema): boolean;
|
|
89
94
|
/**
|
|
@@ -94,14 +99,10 @@ export declare class SqlSchemaGenerator implements SchemaGenerator {
|
|
|
94
99
|
ifNotExists?: boolean;
|
|
95
100
|
}): string[];
|
|
96
101
|
/**
|
|
97
|
-
* Generate a column definition from a ColumnNode.
|
|
102
|
+
* Generate a column definition from a ColumnNode. A composite key is declared as a table constraint,
|
|
103
|
+
* so only a lone primary key column carries `PRIMARY KEY` inline.
|
|
98
104
|
*/
|
|
99
105
|
protected generateColumnFromNode(col: ColumnNode): string;
|
|
100
|
-
/**
|
|
101
|
-
* Generate an inline VECTOR INDEX clause for use inside CREATE TABLE.
|
|
102
|
-
* Syntax: `VECTOR INDEX (col) M=n DISTANCE=metric`
|
|
103
|
-
*/
|
|
104
|
-
private generateInlineVectorIndex;
|
|
105
106
|
/**
|
|
106
107
|
* Generate CREATE INDEX SQL from an IndexNode.
|
|
107
108
|
* Delegates to `generateCreateIndex` for unified SQL assembly.
|
|
@@ -109,26 +110,14 @@ export declare class SqlSchemaGenerator implements SchemaGenerator {
|
|
|
109
110
|
generateCreateIndexFromNode(index: IndexNode, options?: {
|
|
110
111
|
ifNotExists: boolean;
|
|
111
112
|
}): string;
|
|
112
|
-
/**
|
|
113
|
-
* Generate DROP TABLE SQL from a TableNode.
|
|
114
|
-
*/
|
|
115
|
-
generateDropTableFromNode(table: TableNode, options?: {
|
|
116
|
-
ifExists?: boolean;
|
|
117
|
-
}): string;
|
|
118
113
|
generateCreateTableFromDefinition(table: TableDefinition, options?: {
|
|
119
114
|
ifNotExists?: boolean;
|
|
120
115
|
}): string[];
|
|
121
|
-
generateDropTableSql(tableName: string, options?: {
|
|
122
|
-
ifExists?: boolean;
|
|
123
|
-
cascade?: boolean;
|
|
124
|
-
}): string;
|
|
125
116
|
generateRenameTableSql(oldName: string, newName: string): string;
|
|
126
117
|
generateAddColumnSql(tableName: string, column: FullColumnDefinition): string;
|
|
127
118
|
generateAlterColumnSql(tableName: string, columnName: string, column: FullColumnDefinition): string;
|
|
128
119
|
generateDropColumnSql(tableName: string, columnName: string): string;
|
|
129
120
|
generateRenameColumnSql(tableName: string, oldName: string, newName: string): string;
|
|
130
|
-
generateCreateIndexSql(tableName: string, index: IndexSchema): string;
|
|
131
|
-
generateDropIndexSql(tableName: string, indexName: string): string;
|
|
132
121
|
generateAddForeignKeySql(tableName: string, foreignKey: TableForeignKeyDefinition): string;
|
|
133
122
|
generateDropForeignKeySql(tableName: string, constraintName: string): string;
|
|
134
123
|
private tableDefinitionToNode;
|
|
@@ -138,4 +127,4 @@ export declare class SqlSchemaGenerator implements SchemaGenerator {
|
|
|
138
127
|
* Synchronous factory for SQL schema generators only.
|
|
139
128
|
* For MongoDB, use `createSchemaGeneratorAsync` from `./schemaGeneratorAsync.js` so the optional `mongodb` peer is not loaded at import time.
|
|
140
129
|
*/
|
|
141
|
-
export declare function createSchemaGenerator(dialect: AbstractDialect,
|
|
130
|
+
export declare function createSchemaGenerator(dialect: AbstractDialect, defaultForeignKeyAction?: ForeignKeyAction): SqlSchemaGenerator | undefined;
|