@mikro-orm/migrations 7.1.0-dev.3 → 7.1.0-dev.31
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/MigrationRunner.d.ts +4 -1
- package/MigrationRunner.js +37 -3
- package/MigrationStorage.d.ts +5 -2
- package/MigrationStorage.js +37 -12
- package/Migrator.d.ts +3 -1
- package/Migrator.js +34 -13
- package/README.md +2 -1
- package/package.json +4 -4
package/MigrationRunner.d.ts
CHANGED
|
@@ -8,8 +8,11 @@ export declare class MigrationRunner {
|
|
|
8
8
|
protected readonly options: MigrationsOptions;
|
|
9
9
|
protected readonly config: Configuration;
|
|
10
10
|
constructor(driver: AbstractSqlDriver, options: MigrationsOptions, config: Configuration);
|
|
11
|
-
run(migration: Migration, method: 'up' | 'down'): Promise<void>;
|
|
11
|
+
run(migration: Migration, method: 'up' | 'down', afterRun?: (tx?: Transaction) => Promise<void>): Promise<void>;
|
|
12
|
+
private resetSessionSchema;
|
|
12
13
|
setMasterMigration(trx: Transaction): void;
|
|
13
14
|
unsetMasterMigration(): void;
|
|
15
|
+
setRunSchema(schema?: string): void;
|
|
16
|
+
unsetRunSchema(): void;
|
|
14
17
|
private getQueries;
|
|
15
18
|
}
|
package/MigrationRunner.js
CHANGED
|
@@ -7,6 +7,7 @@ export class MigrationRunner {
|
|
|
7
7
|
#connection;
|
|
8
8
|
#helper;
|
|
9
9
|
#masterTransaction;
|
|
10
|
+
#runSchema;
|
|
10
11
|
constructor(driver, options, config) {
|
|
11
12
|
this.driver = driver;
|
|
12
13
|
this.options = options;
|
|
@@ -14,31 +15,64 @@ export class MigrationRunner {
|
|
|
14
15
|
this.#connection = this.driver.getConnection();
|
|
15
16
|
this.#helper = this.driver.getPlatform().getSchemaHelper();
|
|
16
17
|
}
|
|
17
|
-
async run(migration, method) {
|
|
18
|
+
async run(migration, method, afterRun) {
|
|
18
19
|
migration.reset();
|
|
19
20
|
if (!this.options.transactional || !migration.isTransactional()) {
|
|
21
|
+
// Without a pinned transaction the set/reset statements may land on different pooled
|
|
22
|
+
// connections than the DDL, so refuse rather than silently running in the default schema.
|
|
23
|
+
if (this.#runSchema) {
|
|
24
|
+
throw new Error('Runtime schema (migrations.schema / migrator.up({ schema })) is only supported with transactional migrations');
|
|
25
|
+
}
|
|
20
26
|
const queries = await this.getQueries(migration, method);
|
|
21
27
|
await Utils.runSerial(queries, sql => this.driver.execute(sql));
|
|
28
|
+
await afterRun?.();
|
|
22
29
|
}
|
|
23
30
|
else {
|
|
24
31
|
await this.#connection.transactional(async (tx) => {
|
|
25
32
|
migration.setTransactionContext(tx);
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
try {
|
|
34
|
+
const queries = await this.getQueries(migration, method);
|
|
35
|
+
await Utils.runSerial(queries, sql => this.driver.execute(sql, undefined, 'all', tx));
|
|
36
|
+
await afterRun?.(tx);
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
await this.resetSessionSchema(tx);
|
|
40
|
+
}
|
|
28
41
|
}, { ctx: this.#masterTransaction });
|
|
29
42
|
}
|
|
30
43
|
}
|
|
44
|
+
async resetSessionSchema(ctx) {
|
|
45
|
+
if (!this.#runSchema) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const sql = this.#helper.getResetSchemaSQL(this.config.get('dbName'));
|
|
49
|
+
/* v8 ignore next 3 */
|
|
50
|
+
if (!sql) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// best-effort — surfacing a reset failure would mask the real migration error
|
|
54
|
+
await this.driver.execute(sql, undefined, 'all', ctx).catch(() => void 0);
|
|
55
|
+
}
|
|
31
56
|
setMasterMigration(trx) {
|
|
32
57
|
this.#masterTransaction = trx;
|
|
33
58
|
}
|
|
34
59
|
unsetMasterMigration() {
|
|
35
60
|
this.#masterTransaction = undefined;
|
|
36
61
|
}
|
|
62
|
+
setRunSchema(schema) {
|
|
63
|
+
this.#runSchema = this.#helper.resolveMigrationSchema(schema);
|
|
64
|
+
}
|
|
65
|
+
unsetRunSchema() {
|
|
66
|
+
this.#runSchema = undefined;
|
|
67
|
+
}
|
|
37
68
|
async getQueries(migration, method) {
|
|
38
69
|
await migration[method]();
|
|
39
70
|
const charset = this.config.get('charset');
|
|
40
71
|
let queries = migration.getQueries();
|
|
41
72
|
queries.unshift(...this.#helper.getSchemaBeginning(charset, this.options.disableForeignKeys).split('\n'));
|
|
73
|
+
if (this.#runSchema) {
|
|
74
|
+
queries.unshift(this.#helper.getSetSchemaSQL(this.#runSchema));
|
|
75
|
+
}
|
|
42
76
|
queries.push(...this.#helper.getSchemaEnd(this.options.disableForeignKeys).split('\n'));
|
|
43
77
|
queries = queries.filter(sql => typeof sql !== 'string' || sql.trim().length > 0);
|
|
44
78
|
return queries;
|
package/MigrationStorage.d.ts
CHANGED
|
@@ -10,14 +10,16 @@ export declare class MigrationStorage {
|
|
|
10
10
|
executed(): Promise<string[]>;
|
|
11
11
|
logMigration(params: {
|
|
12
12
|
name: string;
|
|
13
|
-
}): Promise<void>;
|
|
13
|
+
}, tx?: Transaction): Promise<void>;
|
|
14
14
|
unlogMigration(params: {
|
|
15
15
|
name: string;
|
|
16
|
-
}): Promise<void>;
|
|
16
|
+
}, tx?: Transaction): Promise<void>;
|
|
17
17
|
getExecutedMigrations(): Promise<MigrationRow[]>;
|
|
18
18
|
ensureTable(): Promise<void>;
|
|
19
19
|
setMasterMigration(trx: Transaction): void;
|
|
20
20
|
unsetMasterMigration(): void;
|
|
21
|
+
setRunSchema(schema?: string): void;
|
|
22
|
+
unsetRunSchema(): void;
|
|
21
23
|
/**
|
|
22
24
|
* @internal
|
|
23
25
|
*/
|
|
@@ -30,4 +32,5 @@ export declare class MigrationStorage {
|
|
|
30
32
|
schemaName: string;
|
|
31
33
|
entity: EntitySchema;
|
|
32
34
|
};
|
|
35
|
+
private resolveTableName;
|
|
33
36
|
}
|
package/MigrationStorage.js
CHANGED
|
@@ -7,7 +7,9 @@ export class MigrationStorage {
|
|
|
7
7
|
#connection;
|
|
8
8
|
#helper;
|
|
9
9
|
#masterTransaction;
|
|
10
|
+
#runSchema;
|
|
10
11
|
#platform;
|
|
12
|
+
#ensuredSchemas = new Set();
|
|
11
13
|
constructor(driver, options) {
|
|
12
14
|
this.driver = driver;
|
|
13
15
|
this.options = options;
|
|
@@ -19,18 +21,21 @@ export class MigrationStorage {
|
|
|
19
21
|
const migrations = await this.getExecutedMigrations();
|
|
20
22
|
return migrations.map(({ name }) => this.getMigrationName(name));
|
|
21
23
|
}
|
|
22
|
-
async logMigration(params) {
|
|
24
|
+
async logMigration(params, tx) {
|
|
25
|
+
await this.ensureTable();
|
|
23
26
|
const { entity } = this.getTableName();
|
|
24
27
|
const name = this.getMigrationName(params.name);
|
|
25
|
-
await this.driver.nativeInsert(entity, { name }, { ctx: this.#masterTransaction });
|
|
28
|
+
await this.driver.nativeInsert(entity, { name }, { ctx: tx ?? this.#masterTransaction });
|
|
26
29
|
}
|
|
27
|
-
async unlogMigration(params) {
|
|
30
|
+
async unlogMigration(params, tx) {
|
|
31
|
+
await this.ensureTable();
|
|
28
32
|
const { entity } = this.getTableName();
|
|
29
33
|
const withoutExt = this.getMigrationName(params.name);
|
|
30
34
|
const names = [withoutExt, withoutExt + '.js', withoutExt + '.ts'];
|
|
31
|
-
await this.driver.nativeDelete(entity, { name: { $in: [params.name, ...names] } }, { ctx: this.#masterTransaction });
|
|
35
|
+
await this.driver.nativeDelete(entity, { name: { $in: [params.name, ...names] } }, { ctx: tx ?? this.#masterTransaction });
|
|
32
36
|
}
|
|
33
37
|
async getExecutedMigrations() {
|
|
38
|
+
await this.ensureTable();
|
|
34
39
|
const { entity, schemaName } = this.getTableName();
|
|
35
40
|
const res = await this.driver
|
|
36
41
|
.createQueryBuilder(entity, this.#masterTransaction)
|
|
@@ -45,15 +50,20 @@ export class MigrationStorage {
|
|
|
45
50
|
});
|
|
46
51
|
}
|
|
47
52
|
async ensureTable() {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
const { tableName, schemaName } = this.resolveTableName();
|
|
54
|
+
// `\x00` can't appear in SQL identifiers — unambiguous pair encoding
|
|
55
|
+
const cacheKey = `${schemaName ?? ''}\x00${tableName}`;
|
|
56
|
+
if (this.#ensuredSchemas.has(cacheKey)) {
|
|
51
57
|
return;
|
|
52
58
|
}
|
|
53
|
-
|
|
59
|
+
if (await this.#helper.tableExists(this.#connection, tableName, schemaName, this.#masterTransaction)) {
|
|
60
|
+
this.#ensuredSchemas.add(cacheKey);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const schemas = await this.#helper.getNamespaces(this.#connection, this.#masterTransaction);
|
|
54
64
|
if (schemaName && !schemas.includes(schemaName)) {
|
|
55
65
|
const sql = this.#helper.getCreateNamespaceSQL(schemaName);
|
|
56
|
-
await this.#connection.execute(sql);
|
|
66
|
+
await this.#connection.execute(sql, [], 'run', this.#masterTransaction);
|
|
57
67
|
}
|
|
58
68
|
const table = new DatabaseTable(this.#platform, tableName, schemaName);
|
|
59
69
|
table.addColumn({
|
|
@@ -78,6 +88,7 @@ export class MigrationStorage {
|
|
|
78
88
|
});
|
|
79
89
|
const sql = this.#helper.createTable(table);
|
|
80
90
|
await this.#connection.execute(sql.join(';\n'), [], 'run', this.#masterTransaction);
|
|
91
|
+
this.#ensuredSchemas.add(cacheKey);
|
|
81
92
|
}
|
|
82
93
|
setMasterMigration(trx) {
|
|
83
94
|
this.#masterTransaction = trx;
|
|
@@ -85,6 +96,12 @@ export class MigrationStorage {
|
|
|
85
96
|
unsetMasterMigration() {
|
|
86
97
|
this.#masterTransaction = undefined;
|
|
87
98
|
}
|
|
99
|
+
setRunSchema(schema) {
|
|
100
|
+
this.#runSchema = this.#helper.resolveMigrationSchema(schema);
|
|
101
|
+
}
|
|
102
|
+
unsetRunSchema() {
|
|
103
|
+
this.#runSchema = undefined;
|
|
104
|
+
}
|
|
88
105
|
/**
|
|
89
106
|
* @internal
|
|
90
107
|
*/
|
|
@@ -95,9 +112,7 @@ export class MigrationStorage {
|
|
|
95
112
|
* @internal
|
|
96
113
|
*/
|
|
97
114
|
getTableName() {
|
|
98
|
-
const
|
|
99
|
-
const tableName = parts.length > 1 ? parts[1] : parts[0];
|
|
100
|
-
const schemaName = parts.length > 1 ? parts[0] : this.driver.config.get('schema', this.driver.getPlatform().getDefaultSchemaName());
|
|
115
|
+
const { tableName, schemaName } = this.resolveTableName();
|
|
101
116
|
const entity = defineEntity({
|
|
102
117
|
name: 'Migration',
|
|
103
118
|
tableName,
|
|
@@ -111,4 +126,14 @@ export class MigrationStorage {
|
|
|
111
126
|
entity.meta.sync();
|
|
112
127
|
return { tableName, schemaName, entity };
|
|
113
128
|
}
|
|
129
|
+
resolveTableName() {
|
|
130
|
+
const parts = this.options.tableName.split('.');
|
|
131
|
+
const tableName = parts.length > 1 ? parts[1] : parts[0];
|
|
132
|
+
const schemaName = this.#runSchema ??
|
|
133
|
+
this.options.schema ??
|
|
134
|
+
(parts.length > 1
|
|
135
|
+
? parts[0]
|
|
136
|
+
: this.driver.config.get('schema', this.driver.getPlatform().getDefaultSchemaName()));
|
|
137
|
+
return { tableName, schemaName };
|
|
138
|
+
}
|
|
114
139
|
}
|
package/Migrator.d.ts
CHANGED
|
@@ -17,7 +17,9 @@ export declare class Migrator extends AbstractMigrator<AbstractSqlDriver> {
|
|
|
17
17
|
* @inheritDoc
|
|
18
18
|
*/
|
|
19
19
|
create(path?: string, blank?: boolean, initial?: boolean, name?: string): Promise<MigrationResult>;
|
|
20
|
-
getPending(
|
|
20
|
+
getPending(options?: {
|
|
21
|
+
schema?: string;
|
|
22
|
+
}): Promise<MigrationInfo[]>;
|
|
21
23
|
private hasSnapshot;
|
|
22
24
|
checkSchema(): Promise<boolean>;
|
|
23
25
|
/**
|
package/Migrator.js
CHANGED
|
@@ -51,7 +51,14 @@ export class Migrator extends AbstractMigrator {
|
|
|
51
51
|
if (created) {
|
|
52
52
|
this.initServices();
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
// Defer tracking-table creation when wildcard-schema fan-out is enabled — pre-creating
|
|
55
|
+
// here would land in the default schema and leave a stray `mikro_orm_migrations` after
|
|
56
|
+
// `migrator.up({ schema })` against a virgin DB. For the standard flow, eager creation
|
|
57
|
+
// keeps the per-call query log clean (no `tableExists`/`getNamespaces`/`create` probe
|
|
58
|
+
// on first storage access).
|
|
59
|
+
if (!this.options.includeWildcardSchema) {
|
|
60
|
+
await this.storage.ensureTable();
|
|
61
|
+
}
|
|
55
62
|
}
|
|
56
63
|
/**
|
|
57
64
|
* @inheritDoc
|
|
@@ -74,23 +81,30 @@ export class Migrator extends AbstractMigrator {
|
|
|
74
81
|
diff,
|
|
75
82
|
};
|
|
76
83
|
}
|
|
77
|
-
async getPending() {
|
|
84
|
+
async getPending(options) {
|
|
78
85
|
if (!(await this.hasSnapshot())) {
|
|
79
|
-
return super.getPending();
|
|
86
|
+
return super.getPending(options);
|
|
80
87
|
}
|
|
81
88
|
await this.initPaths();
|
|
82
89
|
const all = await this.discoverMigrations();
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// from `storage.executed()` propagate so real bugs are not swallowed
|
|
90
|
+
const schema = options?.schema ?? this.options.schema;
|
|
91
|
+
this.storage.setRunSchema?.(schema);
|
|
86
92
|
try {
|
|
87
|
-
|
|
93
|
+
// probe the DB via `ensureTable` — if it fails, the DB is unreachable and
|
|
94
|
+
// we treat every discovered migration as pending; otherwise let errors
|
|
95
|
+
// from `storage.executed()` propagate so real bugs are not swallowed
|
|
96
|
+
try {
|
|
97
|
+
await this.storage.ensureTable();
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return all.map(m => ({ name: m.name, path: m.path }));
|
|
101
|
+
}
|
|
102
|
+
const executed = new Set(await this.storage.executed());
|
|
103
|
+
return all.filter(m => !executed.has(m.name)).map(m => ({ name: m.name, path: m.path }));
|
|
88
104
|
}
|
|
89
|
-
|
|
90
|
-
|
|
105
|
+
finally {
|
|
106
|
+
this.storage.unsetRunSchema?.();
|
|
91
107
|
}
|
|
92
|
-
const executed = new Set(await this.storage.executed());
|
|
93
|
-
return all.filter(m => !executed.has(m.name)).map(m => ({ name: m.name, path: m.path }));
|
|
94
108
|
}
|
|
95
109
|
async hasSnapshot() {
|
|
96
110
|
if (!this.options.snapshot) {
|
|
@@ -197,9 +211,12 @@ export class Migrator extends AbstractMigrator {
|
|
|
197
211
|
const data = fs.readJSONSync(snapshotPath);
|
|
198
212
|
const schema = new DatabaseSchema(this.driver.getPlatform(), this.config.get('schema'));
|
|
199
213
|
const { tables, namespaces, ...rest } = data;
|
|
214
|
+
// share schema-level native enums by reference; fall back to per-table copy on older snapshots
|
|
215
|
+
const sharedNativeEnums = rest.nativeEnums ?? {};
|
|
216
|
+
const hasSharedNativeEnums = Object.keys(sharedNativeEnums).length > 0;
|
|
200
217
|
const tableInstances = tables.map((tbl) => {
|
|
201
218
|
const table = new DatabaseTable(this.driver.getPlatform(), tbl.name, tbl.schema);
|
|
202
|
-
table.nativeEnums = tbl.nativeEnums ?? {};
|
|
219
|
+
table.nativeEnums = hasSharedNativeEnums ? sharedNativeEnums : (tbl.nativeEnums ?? {});
|
|
203
220
|
table.comment = tbl.comment;
|
|
204
221
|
if (tbl.indexes) {
|
|
205
222
|
table.setIndexes(tbl.indexes);
|
|
@@ -271,7 +288,10 @@ export class Migrator extends AbstractMigrator {
|
|
|
271
288
|
down.push('select 1');
|
|
272
289
|
}
|
|
273
290
|
else if (initial) {
|
|
274
|
-
const dump = await this.#schemaGenerator.getCreateSchemaSQL({
|
|
291
|
+
const dump = await this.#schemaGenerator.getCreateSchemaSQL({
|
|
292
|
+
wrap: false,
|
|
293
|
+
includeWildcardSchema: this.options.includeWildcardSchema,
|
|
294
|
+
});
|
|
275
295
|
up.push(...splitStatements(dump));
|
|
276
296
|
}
|
|
277
297
|
else {
|
|
@@ -280,6 +300,7 @@ export class Migrator extends AbstractMigrator {
|
|
|
280
300
|
safe: this.options.safe,
|
|
281
301
|
dropTables: this.options.dropTables,
|
|
282
302
|
fromSchema: await this.getSchemaFromSnapshot(),
|
|
303
|
+
includeWildcardSchema: this.options.includeWildcardSchema,
|
|
283
304
|
});
|
|
284
305
|
up.push(...splitStatements(diff.up));
|
|
285
306
|
down.push(...splitStatements(diff.down));
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<a href="https://mikro-orm.io"><img src="https://raw.githubusercontent.com/mikro-orm/mikro-orm/master/docs/static/img/logo-readme.svg?sanitize=true" alt="MikroORM" /></a>
|
|
3
3
|
</h1>
|
|
4
4
|
|
|
5
|
-
TypeScript ORM for Node.js based on Data Mapper, [Unit of Work](https://mikro-orm.io/docs/unit-of-work/) and [Identity Map](https://mikro-orm.io/docs/identity-map/) patterns. Supports MongoDB, MySQL, MariaDB, PostgreSQL, SQLite (including libSQL), MSSQL and Oracle databases.
|
|
5
|
+
TypeScript ORM for Node.js based on Data Mapper, [Unit of Work](https://mikro-orm.io/docs/unit-of-work/) and [Identity Map](https://mikro-orm.io/docs/identity-map/) patterns. Supports MongoDB, MySQL, MariaDB, PostgreSQL (including CockroachDB and PGlite), SQLite (including libSQL), MSSQL and Oracle databases.
|
|
6
6
|
|
|
7
7
|
> Heavily inspired by [Doctrine](https://www.doctrine-project.org/) and [Hibernate](https://hibernate.org/).
|
|
8
8
|
|
|
@@ -19,6 +19,7 @@ Install a driver package for your database:
|
|
|
19
19
|
|
|
20
20
|
```sh
|
|
21
21
|
npm install @mikro-orm/postgresql # PostgreSQL
|
|
22
|
+
npm install @mikro-orm/pglite # PGlite (embedded PostgreSQL in WASM)
|
|
22
23
|
npm install @mikro-orm/mysql # MySQL
|
|
23
24
|
npm install @mikro-orm/mariadb # MariaDB
|
|
24
25
|
npm install @mikro-orm/sqlite # SQLite
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/migrations",
|
|
3
|
-
"version": "7.1.0-dev.
|
|
3
|
+
"version": "7.1.0-dev.31",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
|
@@ -47,13 +47,13 @@
|
|
|
47
47
|
"copy": "node ../../scripts/copy.mjs"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@mikro-orm/sql": "7.1.0-dev.
|
|
50
|
+
"@mikro-orm/sql": "7.1.0-dev.31"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@mikro-orm/core": "^7.0.
|
|
53
|
+
"@mikro-orm/core": "^7.0.15"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@mikro-orm/core": "7.1.0-dev.
|
|
56
|
+
"@mikro-orm/core": "7.1.0-dev.31"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"node": ">= 22.17.0"
|