sqlmorpheus 0.1.9 → 0.2.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/dist/index.d.mts +42 -18
- package/dist/index.d.ts +42 -18
- package/dist/index.js +131 -58
- package/dist/index.mjs +131 -58
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
interface DatabaseAccess {
|
|
2
|
-
createBackup?(
|
|
2
|
+
createBackup?(backupName: string): Promise<void>;
|
|
3
3
|
runGetStatement(query: string): Promise<unknown>;
|
|
4
4
|
runMultipleWriteStatements(query: string): Promise<void>;
|
|
5
5
|
}
|
|
@@ -22,14 +22,44 @@ interface AllowedMigrations {
|
|
|
22
22
|
continueWithoutRollback?: boolean;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
interface RenameData {
|
|
26
|
+
oldName: string;
|
|
27
|
+
newName: string;
|
|
28
|
+
}
|
|
25
29
|
interface MigrationInstructions {
|
|
26
|
-
|
|
30
|
+
tableRenaming?: RenameData;
|
|
27
31
|
recreate: boolean;
|
|
28
32
|
/**
|
|
29
|
-
* Stores changes to column
|
|
33
|
+
* Stores changes to column. One entry per column.
|
|
34
|
+
* Each entry is an array (0: name in the database, last: current name in code)
|
|
30
35
|
*/
|
|
31
|
-
renamedColumns:
|
|
36
|
+
renamedColumns: RenameData[];
|
|
32
37
|
allowedMigrations: AllowedMigrations;
|
|
38
|
+
usedMigrations: Record<string, boolean>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface ColumnInfo {
|
|
42
|
+
name: string;
|
|
43
|
+
type: string;
|
|
44
|
+
defaultValue: string;
|
|
45
|
+
isPrimaryKey: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type ForeignKeyActions = "SET NULL" | "SET DEFAULT" | "RESTRICT" | "NO ACTION" | "CASCADE";
|
|
49
|
+
interface ForeignKeyInfo {
|
|
50
|
+
fromTable: string;
|
|
51
|
+
fromColumn: string;
|
|
52
|
+
toTable: string;
|
|
53
|
+
toColumn: string;
|
|
54
|
+
onUpdate?: ForeignKeyActions;
|
|
55
|
+
onDelete?: ForeignKeyActions;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface TableStructure {
|
|
59
|
+
table: string;
|
|
60
|
+
primaryKey?: string;
|
|
61
|
+
columns: ColumnInfo[];
|
|
62
|
+
foreignKeys?: ForeignKeyInfo[];
|
|
33
63
|
}
|
|
34
64
|
|
|
35
65
|
declare class Migrations {
|
|
@@ -37,20 +67,24 @@ declare class Migrations {
|
|
|
37
67
|
private alwaysAllowed;
|
|
38
68
|
private fromVersion;
|
|
39
69
|
private migrationData;
|
|
70
|
+
private migrationDataForOldTableNames;
|
|
40
71
|
private notAllowedChanges;
|
|
41
72
|
reset(dbInstructions: DatabaseInstructions, fromVersion: number): void;
|
|
42
73
|
private getEntry;
|
|
43
74
|
private getTableName;
|
|
44
|
-
|
|
45
|
-
|
|
75
|
+
verifyRenamingTasks(newTables: Record<string, TableStructure>): Error | void;
|
|
76
|
+
verifyAllowedMigrations(): Error | void;
|
|
77
|
+
compareWithAllowedMigration(tableName: string, type: keyof AllowedMigrations): void;
|
|
46
78
|
allowMigration(version: number, table: string | Class<BackendTable>, ...allowedMigrations: (keyof AllowedMigrations)[]): void;
|
|
47
|
-
renameTable(version: number, oldTableName: string,
|
|
79
|
+
renameTable(version: number, oldTableName: string, newTableName: string | Class<BackendTable>): void;
|
|
48
80
|
renameColumn(version: number, table: string | Class<BackendTable>, oldColumn: string, newColumn: string): void;
|
|
49
81
|
recreateTableImp(table: Class<BackendTable> | string): void;
|
|
50
82
|
recreateTable(version: number, table: Class<BackendTable> | string): void;
|
|
51
83
|
getMigrationData(): Record<string, MigrationInstructions>;
|
|
52
84
|
loopRenamedColumns(tableName: string, callback: (oldColumnName: string, newColumnName: string) => void): void;
|
|
53
|
-
|
|
85
|
+
getNewestTableName(tableName: string): string;
|
|
86
|
+
getOldTableName(tableName: string): string;
|
|
87
|
+
getNewestColumnName(tableName: string, oldColumnName: string): string;
|
|
54
88
|
willBeRecreated(tableName: string): boolean;
|
|
55
89
|
}
|
|
56
90
|
type PublicMigrations = Pick<Migrations, "recreateTable" | "renameTable" | "renameColumn" | "allowMigration">;
|
|
@@ -60,16 +94,6 @@ interface SqlChanges {
|
|
|
60
94
|
down: string;
|
|
61
95
|
}
|
|
62
96
|
|
|
63
|
-
type ForeignKeyActions = "SET NULL" | "SET DEFAULT" | "RESTRICT" | "NO ACTION" | "CASCADE";
|
|
64
|
-
interface ForeignKeyInfo {
|
|
65
|
-
fromTable: string;
|
|
66
|
-
fromColumn: string;
|
|
67
|
-
toTable: string;
|
|
68
|
-
toColumn: string;
|
|
69
|
-
onUpdate?: ForeignKeyActions;
|
|
70
|
-
onDelete?: ForeignKeyActions;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
97
|
interface TableInfo {
|
|
74
98
|
primaryKey?: string;
|
|
75
99
|
foreignKeys?: ForeignKeyInfo[];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
interface DatabaseAccess {
|
|
2
|
-
createBackup?(
|
|
2
|
+
createBackup?(backupName: string): Promise<void>;
|
|
3
3
|
runGetStatement(query: string): Promise<unknown>;
|
|
4
4
|
runMultipleWriteStatements(query: string): Promise<void>;
|
|
5
5
|
}
|
|
@@ -22,14 +22,44 @@ interface AllowedMigrations {
|
|
|
22
22
|
continueWithoutRollback?: boolean;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
interface RenameData {
|
|
26
|
+
oldName: string;
|
|
27
|
+
newName: string;
|
|
28
|
+
}
|
|
25
29
|
interface MigrationInstructions {
|
|
26
|
-
|
|
30
|
+
tableRenaming?: RenameData;
|
|
27
31
|
recreate: boolean;
|
|
28
32
|
/**
|
|
29
|
-
* Stores changes to column
|
|
33
|
+
* Stores changes to column. One entry per column.
|
|
34
|
+
* Each entry is an array (0: name in the database, last: current name in code)
|
|
30
35
|
*/
|
|
31
|
-
renamedColumns:
|
|
36
|
+
renamedColumns: RenameData[];
|
|
32
37
|
allowedMigrations: AllowedMigrations;
|
|
38
|
+
usedMigrations: Record<string, boolean>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface ColumnInfo {
|
|
42
|
+
name: string;
|
|
43
|
+
type: string;
|
|
44
|
+
defaultValue: string;
|
|
45
|
+
isPrimaryKey: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type ForeignKeyActions = "SET NULL" | "SET DEFAULT" | "RESTRICT" | "NO ACTION" | "CASCADE";
|
|
49
|
+
interface ForeignKeyInfo {
|
|
50
|
+
fromTable: string;
|
|
51
|
+
fromColumn: string;
|
|
52
|
+
toTable: string;
|
|
53
|
+
toColumn: string;
|
|
54
|
+
onUpdate?: ForeignKeyActions;
|
|
55
|
+
onDelete?: ForeignKeyActions;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface TableStructure {
|
|
59
|
+
table: string;
|
|
60
|
+
primaryKey?: string;
|
|
61
|
+
columns: ColumnInfo[];
|
|
62
|
+
foreignKeys?: ForeignKeyInfo[];
|
|
33
63
|
}
|
|
34
64
|
|
|
35
65
|
declare class Migrations {
|
|
@@ -37,20 +67,24 @@ declare class Migrations {
|
|
|
37
67
|
private alwaysAllowed;
|
|
38
68
|
private fromVersion;
|
|
39
69
|
private migrationData;
|
|
70
|
+
private migrationDataForOldTableNames;
|
|
40
71
|
private notAllowedChanges;
|
|
41
72
|
reset(dbInstructions: DatabaseInstructions, fromVersion: number): void;
|
|
42
73
|
private getEntry;
|
|
43
74
|
private getTableName;
|
|
44
|
-
|
|
45
|
-
|
|
75
|
+
verifyRenamingTasks(newTables: Record<string, TableStructure>): Error | void;
|
|
76
|
+
verifyAllowedMigrations(): Error | void;
|
|
77
|
+
compareWithAllowedMigration(tableName: string, type: keyof AllowedMigrations): void;
|
|
46
78
|
allowMigration(version: number, table: string | Class<BackendTable>, ...allowedMigrations: (keyof AllowedMigrations)[]): void;
|
|
47
|
-
renameTable(version: number, oldTableName: string,
|
|
79
|
+
renameTable(version: number, oldTableName: string, newTableName: string | Class<BackendTable>): void;
|
|
48
80
|
renameColumn(version: number, table: string | Class<BackendTable>, oldColumn: string, newColumn: string): void;
|
|
49
81
|
recreateTableImp(table: Class<BackendTable> | string): void;
|
|
50
82
|
recreateTable(version: number, table: Class<BackendTable> | string): void;
|
|
51
83
|
getMigrationData(): Record<string, MigrationInstructions>;
|
|
52
84
|
loopRenamedColumns(tableName: string, callback: (oldColumnName: string, newColumnName: string) => void): void;
|
|
53
|
-
|
|
85
|
+
getNewestTableName(tableName: string): string;
|
|
86
|
+
getOldTableName(tableName: string): string;
|
|
87
|
+
getNewestColumnName(tableName: string, oldColumnName: string): string;
|
|
54
88
|
willBeRecreated(tableName: string): boolean;
|
|
55
89
|
}
|
|
56
90
|
type PublicMigrations = Pick<Migrations, "recreateTable" | "renameTable" | "renameColumn" | "allowMigration">;
|
|
@@ -60,16 +94,6 @@ interface SqlChanges {
|
|
|
60
94
|
down: string;
|
|
61
95
|
}
|
|
62
96
|
|
|
63
|
-
type ForeignKeyActions = "SET NULL" | "SET DEFAULT" | "RESTRICT" | "NO ACTION" | "CASCADE";
|
|
64
|
-
interface ForeignKeyInfo {
|
|
65
|
-
fromTable: string;
|
|
66
|
-
fromColumn: string;
|
|
67
|
-
toTable: string;
|
|
68
|
-
toColumn: string;
|
|
69
|
-
onUpdate?: ForeignKeyActions;
|
|
70
|
-
onDelete?: ForeignKeyActions;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
97
|
interface TableInfo {
|
|
74
98
|
primaryKey?: string;
|
|
75
99
|
foreignKeys?: ForeignKeyInfo[];
|
package/dist/index.js
CHANGED
|
@@ -146,10 +146,10 @@ var TableStructureGenerator = class {
|
|
|
146
146
|
// src/lib/exceptions/NotAllowedException.ts
|
|
147
147
|
var NotAllowedException = class extends Error {
|
|
148
148
|
constructor(entries) {
|
|
149
|
-
let message = "Some destructive changes need to be confirmed
|
|
149
|
+
let message = "Some destructive changes need to be confirmed.\n In preMigration() run:\n";
|
|
150
150
|
for (const entry of entries) {
|
|
151
|
-
message += `
|
|
152
|
-
|
|
151
|
+
message += ` migrations.allowMigration(${entry.version}, "${entry.tableName}", "${entry.type}");
|
|
152
|
+
`;
|
|
153
153
|
}
|
|
154
154
|
super(message);
|
|
155
155
|
}
|
|
@@ -161,6 +161,7 @@ var Migrations = class {
|
|
|
161
161
|
alwaysAllowed = {};
|
|
162
162
|
fromVersion = 0;
|
|
163
163
|
migrationData = {};
|
|
164
|
+
migrationDataForOldTableNames = {};
|
|
164
165
|
notAllowedChanges = [];
|
|
165
166
|
reset(dbInstructions, fromVersion) {
|
|
166
167
|
this.fromVersion = fromVersion;
|
|
@@ -175,7 +176,8 @@ var Migrations = class {
|
|
|
175
176
|
this.migrationData[tableName] = {
|
|
176
177
|
recreate: false,
|
|
177
178
|
renamedColumns: [],
|
|
178
|
-
allowedMigrations: {}
|
|
179
|
+
allowedMigrations: {},
|
|
180
|
+
usedMigrations: {}
|
|
179
181
|
};
|
|
180
182
|
}
|
|
181
183
|
return this.migrationData[tableName];
|
|
@@ -183,18 +185,47 @@ var Migrations = class {
|
|
|
183
185
|
getTableName(table) {
|
|
184
186
|
return typeof table == "string" ? table : table.name;
|
|
185
187
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
188
|
+
verifyRenamingTasks(newTables) {
|
|
189
|
+
for (const tableName in this.migrationData) {
|
|
190
|
+
const migrationEntry = this.migrationData[tableName];
|
|
191
|
+
if (migrationEntry.tableRenaming?.oldName == tableName)
|
|
192
|
+
return new Error(`You set table "${tableName}" to be renamed to itself!`);
|
|
193
|
+
if (migrationEntry.tableRenaming && migrationEntry.tableRenaming.newName != tableName)
|
|
194
|
+
return new Error(`You set table "${tableName}" to be renamed to a different name than the one in your structure (${migrationEntry.tableRenaming.newName})!`);
|
|
195
|
+
else if (!newTables[tableName])
|
|
196
|
+
return new Error(`You set table "${migrationEntry.tableRenaming}" migrations for "${tableName}". But "${tableName}" does not exist in your structure.`);
|
|
197
|
+
for (const renamingData of migrationEntry.renamedColumns) {
|
|
198
|
+
const oldColumnName = renamingData.oldName;
|
|
199
|
+
const newColumnName = renamingData.newName;
|
|
200
|
+
if (oldColumnName == newColumnName)
|
|
201
|
+
return new Error(`You set column ${tableName}.${oldColumnName} to be renamed to itself!`);
|
|
202
|
+
else if (!newTables[tableName]?.columns.find((column) => column.name == newColumnName))
|
|
203
|
+
return new Error(`You set column ${tableName}.${oldColumnName} to be renamed to ${tableName}.${newColumnName}. But ${tableName}.${newColumnName} does not exist in your structure.`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
189
206
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
207
|
+
verifyAllowedMigrations() {
|
|
208
|
+
if (this.notAllowedChanges.length)
|
|
209
|
+
return new NotAllowedException(this.notAllowedChanges);
|
|
210
|
+
let errorMsg = "";
|
|
211
|
+
for (const tableName in this.migrationData) {
|
|
212
|
+
const migration = this.migrationData[tableName];
|
|
213
|
+
for (const type in migration.allowedMigrations) {
|
|
214
|
+
if (!migration.usedMigrations[type])
|
|
215
|
+
errorMsg += `Migration "${type}" for ${tableName} was allowed but not needed.
|
|
216
|
+
`;
|
|
217
|
+
}
|
|
196
218
|
}
|
|
197
|
-
|
|
219
|
+
if (errorMsg)
|
|
220
|
+
return new Error(errorMsg);
|
|
221
|
+
}
|
|
222
|
+
compareWithAllowedMigration(tableName, type) {
|
|
223
|
+
const migrationEntry = this.migrationData[tableName];
|
|
224
|
+
if (!migrationEntry?.allowedMigrations[type]) {
|
|
225
|
+
if (!this.alwaysAllowed[type])
|
|
226
|
+
this.notAllowedChanges.push({ version: this.toVersion, tableName, type });
|
|
227
|
+
} else if (migrationEntry)
|
|
228
|
+
migrationEntry.usedMigrations[type] = true;
|
|
198
229
|
}
|
|
199
230
|
allowMigration(version, table, ...allowedMigrations) {
|
|
200
231
|
if (version < this.fromVersion || version > this.toVersion)
|
|
@@ -204,23 +235,30 @@ var Migrations = class {
|
|
|
204
235
|
entry.allowedMigrations[key] = true;
|
|
205
236
|
}
|
|
206
237
|
}
|
|
207
|
-
renameTable(version, oldTableName,
|
|
238
|
+
renameTable(version, oldTableName, newTableName) {
|
|
208
239
|
if (version < this.fromVersion || version > this.toVersion)
|
|
209
240
|
return;
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
241
|
+
const newTableNameString = this.getTableName(newTableName);
|
|
242
|
+
if (this.migrationData.hasOwnProperty(oldTableName)) {
|
|
243
|
+
this.migrationData[newTableNameString] = this.getEntry(oldTableName);
|
|
244
|
+
delete this.migrationData[oldTableName];
|
|
245
|
+
}
|
|
246
|
+
const entry = this.getEntry(newTableName);
|
|
247
|
+
this.migrationDataForOldTableNames[oldTableName] = entry;
|
|
248
|
+
if (!entry.tableRenaming)
|
|
249
|
+
entry.tableRenaming = { oldName: oldTableName, newName: newTableNameString };
|
|
250
|
+
else
|
|
251
|
+
entry.tableRenaming.newName = newTableNameString;
|
|
214
252
|
}
|
|
215
253
|
renameColumn(version, table, oldColumn, newColumn) {
|
|
216
254
|
if (version < this.fromVersion || version > this.toVersion)
|
|
217
255
|
return;
|
|
218
256
|
const entry = this.getEntry(table);
|
|
219
|
-
const existingColumnEntry = entry.renamedColumns.find((entry2) => entry2
|
|
257
|
+
const existingColumnEntry = entry.renamedColumns.find((entry2) => entry2.newName == oldColumn);
|
|
220
258
|
if (existingColumnEntry)
|
|
221
|
-
existingColumnEntry.
|
|
259
|
+
existingColumnEntry.newName = newColumn;
|
|
222
260
|
else
|
|
223
|
-
entry.renamedColumns.push(
|
|
261
|
+
entry.renamedColumns.push({ oldName: oldColumn, newName: newColumn });
|
|
224
262
|
}
|
|
225
263
|
recreateTableImp(table) {
|
|
226
264
|
const entry = this.getEntry(table);
|
|
@@ -236,17 +274,19 @@ var Migrations = class {
|
|
|
236
274
|
}
|
|
237
275
|
loopRenamedColumns(tableName, callback) {
|
|
238
276
|
const migrationEntry = this.migrationData[tableName];
|
|
239
|
-
for (const
|
|
240
|
-
|
|
241
|
-
continue;
|
|
242
|
-
const oldColumnName = renamingArray[0];
|
|
243
|
-
const newColumnName = renamingArray[renamingArray.length - 1];
|
|
244
|
-
callback(oldColumnName, newColumnName);
|
|
277
|
+
for (const renamingData of migrationEntry.renamedColumns) {
|
|
278
|
+
callback(renamingData.oldName, renamingData.newName);
|
|
245
279
|
}
|
|
246
280
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
281
|
+
getNewestTableName(tableName) {
|
|
282
|
+
return this.migrationDataForOldTableNames[tableName]?.tableRenaming?.newName ?? tableName;
|
|
283
|
+
}
|
|
284
|
+
getOldTableName(tableName) {
|
|
285
|
+
return this.migrationData[tableName]?.tableRenaming?.oldName ?? tableName;
|
|
286
|
+
}
|
|
287
|
+
getNewestColumnName(tableName, oldColumnName) {
|
|
288
|
+
const columnNames = this.migrationData[tableName]?.renamedColumns.find((columns) => columns.oldName == oldColumnName);
|
|
289
|
+
return columnNames ? columnNames.newName : oldColumnName;
|
|
250
290
|
}
|
|
251
291
|
willBeRecreated(tableName) {
|
|
252
292
|
return this.migrationData[tableName]?.recreate;
|
|
@@ -257,6 +297,10 @@ var Migrations = class {
|
|
|
257
297
|
var MigrationManager = class {
|
|
258
298
|
migrations = new Migrations();
|
|
259
299
|
newTables = {};
|
|
300
|
+
/**
|
|
301
|
+
* Table names that currently exist in the database
|
|
302
|
+
* Note: If tables are set to be renamed, then names in this array will be corrected to their future name.
|
|
303
|
+
*/
|
|
260
304
|
existingTables = [];
|
|
261
305
|
dialect;
|
|
262
306
|
constructor(dialect) {
|
|
@@ -314,8 +358,9 @@ ${preSQL.down}`;
|
|
|
314
358
|
changes.down += initialChanges.down;
|
|
315
359
|
} else {
|
|
316
360
|
Logger.log(`Creating migration SQL from version ${fromVersion} to ${toVersion}`);
|
|
317
|
-
this.existingTables = await this.dialect.getTableNames();
|
|
361
|
+
this.existingTables = (await this.dialect.getTableNames()).map((oldTableName) => this.migrations.getNewestTableName(oldTableName));
|
|
318
362
|
[
|
|
363
|
+
this.renameTables(),
|
|
319
364
|
await this.createAndDropTables(),
|
|
320
365
|
await this.migrateForeignKeys(),
|
|
321
366
|
await this.migrateColumns(),
|
|
@@ -344,8 +389,18 @@ ${foreignKeyOnQuery}`;
|
|
|
344
389
|
changes.down += `
|
|
345
390
|
|
|
346
391
|
${foreignKeyOnQuery}`;
|
|
347
|
-
|
|
348
|
-
|
|
392
|
+
const exception = this.migrations.verifyRenamingTasks(this.newTables) ?? this.migrations.verifyAllowedMigrations();
|
|
393
|
+
if (exception) {
|
|
394
|
+
Logger.debug(`
|
|
395
|
+
Canceled changes:
|
|
396
|
+
${changes.up}
|
|
397
|
+
`);
|
|
398
|
+
if (dbInstructions.throwIfNotAllowed)
|
|
399
|
+
throw exception;
|
|
400
|
+
else {
|
|
401
|
+
console.error(exception.message);
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
349
404
|
}
|
|
350
405
|
return {
|
|
351
406
|
fromVersion,
|
|
@@ -353,6 +408,22 @@ ${foreignKeyOnQuery}`;
|
|
|
353
408
|
changes
|
|
354
409
|
};
|
|
355
410
|
}
|
|
411
|
+
renameTables() {
|
|
412
|
+
const changes = {
|
|
413
|
+
up: "\n\n-- Rename tables\n",
|
|
414
|
+
down: "\n\n-- Rename tables\n"
|
|
415
|
+
};
|
|
416
|
+
const data = this.migrations.getMigrationData();
|
|
417
|
+
for (const tableName in data) {
|
|
418
|
+
const migrationEntry = data[tableName];
|
|
419
|
+
if (migrationEntry.tableRenaming) {
|
|
420
|
+
Logger.log(`Table "${migrationEntry.tableRenaming.oldName}" will be renamed to "${tableName}"!`);
|
|
421
|
+
changes.up += this.dialect.renameTable(migrationEntry.tableRenaming.oldName, tableName);
|
|
422
|
+
changes.down += this.dialect.renameTable(tableName, migrationEntry.tableRenaming.oldName);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return changes;
|
|
426
|
+
}
|
|
356
427
|
/**
|
|
357
428
|
* Generates SQL change statements for creating and dropping tables based on the current table structure
|
|
358
429
|
* and existing table configurations in the database. The `up` changes include SQL statements for creating
|
|
@@ -364,8 +435,8 @@ ${foreignKeyOnQuery}`;
|
|
|
364
435
|
*/
|
|
365
436
|
async createAndDropTables() {
|
|
366
437
|
let changes = {
|
|
367
|
-
up: "\n\n-- Create tables\n",
|
|
368
|
-
down: "\n\n-- Create tables\n"
|
|
438
|
+
up: "\n\n-- Create and drop tables\n",
|
|
439
|
+
down: "\n\n-- Create and drop tables\n"
|
|
369
440
|
};
|
|
370
441
|
for (const tableName in this.newTables) {
|
|
371
442
|
if (this.tableDoesNotExists(tableName)) {
|
|
@@ -378,12 +449,13 @@ ${foreignKeyOnQuery}`;
|
|
|
378
449
|
}
|
|
379
450
|
for (const tableName of this.existingTables) {
|
|
380
451
|
if (!this.newTables[tableName]) {
|
|
381
|
-
this.migrations.
|
|
452
|
+
this.migrations.compareWithAllowedMigration(tableName, "dropTable");
|
|
382
453
|
if (!this.dialect.canInspectForeignKeys || !this.dialect.canInspectPrimaryKey)
|
|
383
|
-
this.migrations.
|
|
454
|
+
this.migrations.compareWithAllowedMigration(tableName, "continueWithoutRollback");
|
|
455
|
+
const dbTableName = this.migrations.getOldTableName(tableName);
|
|
384
456
|
changes.up += `${this.dialect.dropTable(tableName)}
|
|
385
457
|
`;
|
|
386
|
-
changes.down += this.dialect.canInspectForeignKeys && this.dialect.canInspectPrimaryKey ? this.createTableSql(tableName, await this.dialect.getColumnInformation(
|
|
458
|
+
changes.down += this.dialect.canInspectForeignKeys && this.dialect.canInspectPrimaryKey ? this.createTableSql(tableName, await this.dialect.getColumnInformation(dbTableName), await this.dialect.getForeignKeys(dbTableName)) : "\n\n-- Cannot recreate foreign keys or load primary keys with this database! Table will not be recreated!\n";
|
|
387
459
|
}
|
|
388
460
|
}
|
|
389
461
|
return changes;
|
|
@@ -434,10 +506,11 @@ ${foreignKeyOnQuery}`;
|
|
|
434
506
|
const migrationEntry = migrationData[tableName];
|
|
435
507
|
if (!migrationEntry.recreate)
|
|
436
508
|
continue;
|
|
437
|
-
|
|
509
|
+
Logger.log(`Table ${tableName} will be recreated!`);
|
|
510
|
+
this.migrations.compareWithAllowedMigration(tableName, "recreateTable");
|
|
438
511
|
if (!this.dialect.canInspectForeignKeys || !this.dialect.canInspectPrimaryKey)
|
|
439
|
-
this.migrations.
|
|
440
|
-
const oldColumnList = await this.dialect.getColumnInformation(tableName);
|
|
512
|
+
this.migrations.compareWithAllowedMigration(tableName, "continueWithoutRollback");
|
|
513
|
+
const oldColumnList = await this.dialect.getColumnInformation(this.migrations.getOldTableName(tableName));
|
|
441
514
|
const newColumnList = this.newTables[tableName].columns;
|
|
442
515
|
const moveableColumns = newColumnList.filter((entry) => oldColumnList.find((oldEntry) => oldEntry.name == entry.name) != void 0).map((entry) => entry.name);
|
|
443
516
|
const backupTableName = `${tableName}__backup`;
|
|
@@ -447,8 +520,9 @@ ${foreignKeyOnQuery}`;
|
|
|
447
520
|
);
|
|
448
521
|
const moveDataQuery = insertQuery + "\n" + this.dialect.dropTable(tableName) + "\n" + this.dialect.renameTable(backupTableName, tableName);
|
|
449
522
|
const structure = this.newTables[tableName];
|
|
450
|
-
changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery;
|
|
451
|
-
|
|
523
|
+
changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery + "\n";
|
|
524
|
+
const dbTableName = this.migrations.getOldTableName(tableName);
|
|
525
|
+
changes.down += this.dialect.canInspectForeignKeys && this.dialect.canInspectPrimaryKey ? this.createTableSql(backupTableName, await this.dialect.getColumnInformation(dbTableName), await this.dialect.getForeignKeys(dbTableName)) + "\n" + moveDataQuery : "\n\n-- Cannot recreate foreign keys or load primary keys with this database! Table will not be recreated!\n";
|
|
452
526
|
}
|
|
453
527
|
return changes;
|
|
454
528
|
}
|
|
@@ -456,8 +530,6 @@ ${foreignKeyOnQuery}`;
|
|
|
456
530
|
* Migrates foreign key constraints for all defined tables.
|
|
457
531
|
*/
|
|
458
532
|
async migrateForeignKeys() {
|
|
459
|
-
if (!this.dialect.canAlterForeignKeys)
|
|
460
|
-
return { up: "", down: "" };
|
|
461
533
|
let changes = {
|
|
462
534
|
up: "\n\n-- Foreign keys\n",
|
|
463
535
|
down: "\n\n-- Foreign keys\n"
|
|
@@ -467,13 +539,13 @@ ${foreignKeyOnQuery}`;
|
|
|
467
539
|
continue;
|
|
468
540
|
const structure = this.newTables[tableName];
|
|
469
541
|
const newForeignKeys = structure.foreignKeys ?? [];
|
|
470
|
-
const oldForeignKeys = await this.dialect.getForeignKeys(tableName);
|
|
542
|
+
const oldForeignKeys = await this.dialect.getForeignKeys(this.migrations.getOldTableName(tableName));
|
|
471
543
|
let checkForNewForeignKeys = true;
|
|
472
544
|
for (const oldForeignKey of oldForeignKeys) {
|
|
473
545
|
const newForeignKey = newForeignKeys.find((entry) => entry.fromColumn == oldForeignKey.fromColumn);
|
|
474
546
|
if (!newForeignKey) {
|
|
475
|
-
Logger.log(`Foreign key ${oldForeignKey.
|
|
476
|
-
this.migrations.
|
|
547
|
+
Logger.log(`Foreign key ${oldForeignKey.fromTable}.${oldForeignKey.fromColumn} to ${oldForeignKey.toTable}.${oldForeignKey.toColumn} will be removed!`);
|
|
548
|
+
this.migrations.compareWithAllowedMigration(tableName, "removeForeignKey");
|
|
477
549
|
if (this.dialect.canAlterForeignKeys) {
|
|
478
550
|
changes.up += this.dialect.removeForeignKey(oldForeignKey.fromTable, oldForeignKey.fromColumn);
|
|
479
551
|
changes.down += this.dialect.addForeignKey(
|
|
@@ -486,8 +558,8 @@ ${foreignKeyOnQuery}`;
|
|
|
486
558
|
}
|
|
487
559
|
} else {
|
|
488
560
|
if (!newForeignKey || oldForeignKey.toColumn != newForeignKey.toColumn || (oldForeignKey.onUpdate ?? "NO ACTION") != (newForeignKey.onUpdate ?? "NO ACTION") || (oldForeignKey.onDelete ?? "NO ACTION") != (newForeignKey.onDelete ?? "NO ACTION")) {
|
|
489
|
-
Logger.log(`Foreign key ${oldForeignKey.
|
|
490
|
-
this.migrations.
|
|
561
|
+
Logger.log(`Foreign key ${oldForeignKey.fromTable}.${oldForeignKey.fromColumn} to ${oldForeignKey.toTable}.${oldForeignKey.toColumn} will be changed!`);
|
|
562
|
+
this.migrations.compareWithAllowedMigration(tableName, "alterForeignKey");
|
|
491
563
|
if (this.dialect.canAlterForeignKeys) {
|
|
492
564
|
changes.up += this.dialect.removeForeignKey(oldForeignKey.fromTable, oldForeignKey.fromColumn) + this.dialect.addForeignKey(
|
|
493
565
|
newForeignKey.fromTable,
|
|
@@ -509,7 +581,7 @@ ${foreignKeyOnQuery}`;
|
|
|
509
581
|
const oldForeignKey = oldForeignKeys.find((entry) => entry.fromColumn == newForeignKey.fromColumn);
|
|
510
582
|
if (oldForeignKey)
|
|
511
583
|
continue;
|
|
512
|
-
Logger.log(`Foreign key ${newForeignKey.
|
|
584
|
+
Logger.log(`Foreign key ${newForeignKey.fromTable}.${newForeignKey.fromColumn} to ${newForeignKey.toTable}.${newForeignKey.toColumn} will be added!`);
|
|
513
585
|
if (this.dialect.canAlterForeignKeys) {
|
|
514
586
|
changes.up += this.dialect.addForeignKey(
|
|
515
587
|
newForeignKey.toTable,
|
|
@@ -538,13 +610,13 @@ ${foreignKeyOnQuery}`;
|
|
|
538
610
|
if (this.migrations.willBeRecreated(tableName) || this.tableDoesNotExists(tableName))
|
|
539
611
|
continue;
|
|
540
612
|
const newTableDefinition = this.newTables[tableName];
|
|
541
|
-
const oldColumnList = await this.dialect.getColumnInformation(tableName);
|
|
613
|
+
const oldColumnList = await this.dialect.getColumnInformation(this.migrations.getOldTableName(tableName));
|
|
542
614
|
const oldPrimaryKey = this.dialect.canInspectPrimaryKey ? this.getPrimaryKeyColumn(oldColumnList) : false;
|
|
543
615
|
const newColumnList = newTableDefinition.columns;
|
|
544
616
|
const newPrimaryKey = newTableDefinition.primaryKey;
|
|
545
617
|
if (this.dialect.canInspectPrimaryKey && oldPrimaryKey != newPrimaryKey) {
|
|
546
|
-
Logger.log(`Primary key in ${tableName}
|
|
547
|
-
this.migrations.
|
|
618
|
+
Logger.log(`Primary key in ${tableName} will be changed from ${oldPrimaryKey} to ${newPrimaryKey}!`);
|
|
619
|
+
this.migrations.compareWithAllowedMigration(tableName, "alterPrimaryKey");
|
|
548
620
|
if (this.dialect.canAlterPrimaryKey) {
|
|
549
621
|
if (oldPrimaryKey) {
|
|
550
622
|
changes.up += this.dialect.removePrimaryKey(tableName, oldPrimaryKey);
|
|
@@ -560,7 +632,7 @@ ${foreignKeyOnQuery}`;
|
|
|
560
632
|
}
|
|
561
633
|
for (const newColumn of newColumnList) {
|
|
562
634
|
const oldColumn = oldColumnList.find(
|
|
563
|
-
(entry) => this.migrations.
|
|
635
|
+
(entry) => this.migrations.getNewestColumnName(tableName, entry.name) == newColumn.name
|
|
564
636
|
);
|
|
565
637
|
if (oldColumn == void 0) {
|
|
566
638
|
changes.up += this.dialect.createColumn(tableName, this.dialect.columnDefinition(newColumn.name, newColumn.type, newColumn.defaultValue, newColumn.isPrimaryKey)) + "\n";
|
|
@@ -571,10 +643,10 @@ ${foreignKeyOnQuery}`;
|
|
|
571
643
|
}
|
|
572
644
|
for (const oldColumn of oldColumnList) {
|
|
573
645
|
const newColumn = newColumnList.find(
|
|
574
|
-
(entry) => entry.name == this.migrations.
|
|
646
|
+
(entry) => entry.name == this.migrations.getNewestColumnName(tableName, oldColumn.name)
|
|
575
647
|
);
|
|
576
648
|
if (newColumn == void 0) {
|
|
577
|
-
this.migrations.
|
|
649
|
+
this.migrations.compareWithAllowedMigration(tableName, "dropColumn");
|
|
578
650
|
changes.up += this.dialect.dropColumn(tableName, oldColumn.name) + "\n";
|
|
579
651
|
changes.down += this.dialect.createColumn(tableName, this.dialect.columnDefinition(oldColumn.name, oldColumn.type, oldColumn.defaultValue, oldColumn.isPrimaryKey)) + "\n";
|
|
580
652
|
}
|
|
@@ -608,6 +680,7 @@ ${foreignKeyOnQuery}`;
|
|
|
608
680
|
if (migrationData[tableName].recreate)
|
|
609
681
|
continue;
|
|
610
682
|
this.migrations.loopRenamedColumns(tableName, (oldColumnName, newColumnName) => {
|
|
683
|
+
Logger.log(`Column ${tableName}.${oldColumnName} will be renamed to ${tableName}.${newColumnName}!`);
|
|
611
684
|
changes.up += this.dialect.renameColumn(tableName, oldColumnName, newColumnName);
|
|
612
685
|
changes.down += this.dialect.renameColumn(tableName, newColumnName, oldColumnName);
|
|
613
686
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -114,10 +114,10 @@ var TableStructureGenerator = class {
|
|
|
114
114
|
// src/lib/exceptions/NotAllowedException.ts
|
|
115
115
|
var NotAllowedException = class extends Error {
|
|
116
116
|
constructor(entries) {
|
|
117
|
-
let message = "Some destructive changes need to be confirmed
|
|
117
|
+
let message = "Some destructive changes need to be confirmed.\n In preMigration() run:\n";
|
|
118
118
|
for (const entry of entries) {
|
|
119
|
-
message += `
|
|
120
|
-
|
|
119
|
+
message += ` migrations.allowMigration(${entry.version}, "${entry.tableName}", "${entry.type}");
|
|
120
|
+
`;
|
|
121
121
|
}
|
|
122
122
|
super(message);
|
|
123
123
|
}
|
|
@@ -129,6 +129,7 @@ var Migrations = class {
|
|
|
129
129
|
alwaysAllowed = {};
|
|
130
130
|
fromVersion = 0;
|
|
131
131
|
migrationData = {};
|
|
132
|
+
migrationDataForOldTableNames = {};
|
|
132
133
|
notAllowedChanges = [];
|
|
133
134
|
reset(dbInstructions, fromVersion) {
|
|
134
135
|
this.fromVersion = fromVersion;
|
|
@@ -143,7 +144,8 @@ var Migrations = class {
|
|
|
143
144
|
this.migrationData[tableName] = {
|
|
144
145
|
recreate: false,
|
|
145
146
|
renamedColumns: [],
|
|
146
|
-
allowedMigrations: {}
|
|
147
|
+
allowedMigrations: {},
|
|
148
|
+
usedMigrations: {}
|
|
147
149
|
};
|
|
148
150
|
}
|
|
149
151
|
return this.migrationData[tableName];
|
|
@@ -151,18 +153,47 @@ var Migrations = class {
|
|
|
151
153
|
getTableName(table) {
|
|
152
154
|
return typeof table == "string" ? table : table.name;
|
|
153
155
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
156
|
+
verifyRenamingTasks(newTables) {
|
|
157
|
+
for (const tableName in this.migrationData) {
|
|
158
|
+
const migrationEntry = this.migrationData[tableName];
|
|
159
|
+
if (migrationEntry.tableRenaming?.oldName == tableName)
|
|
160
|
+
return new Error(`You set table "${tableName}" to be renamed to itself!`);
|
|
161
|
+
if (migrationEntry.tableRenaming && migrationEntry.tableRenaming.newName != tableName)
|
|
162
|
+
return new Error(`You set table "${tableName}" to be renamed to a different name than the one in your structure (${migrationEntry.tableRenaming.newName})!`);
|
|
163
|
+
else if (!newTables[tableName])
|
|
164
|
+
return new Error(`You set table "${migrationEntry.tableRenaming}" migrations for "${tableName}". But "${tableName}" does not exist in your structure.`);
|
|
165
|
+
for (const renamingData of migrationEntry.renamedColumns) {
|
|
166
|
+
const oldColumnName = renamingData.oldName;
|
|
167
|
+
const newColumnName = renamingData.newName;
|
|
168
|
+
if (oldColumnName == newColumnName)
|
|
169
|
+
return new Error(`You set column ${tableName}.${oldColumnName} to be renamed to itself!`);
|
|
170
|
+
else if (!newTables[tableName]?.columns.find((column) => column.name == newColumnName))
|
|
171
|
+
return new Error(`You set column ${tableName}.${oldColumnName} to be renamed to ${tableName}.${newColumnName}. But ${tableName}.${newColumnName} does not exist in your structure.`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
157
174
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
175
|
+
verifyAllowedMigrations() {
|
|
176
|
+
if (this.notAllowedChanges.length)
|
|
177
|
+
return new NotAllowedException(this.notAllowedChanges);
|
|
178
|
+
let errorMsg = "";
|
|
179
|
+
for (const tableName in this.migrationData) {
|
|
180
|
+
const migration = this.migrationData[tableName];
|
|
181
|
+
for (const type in migration.allowedMigrations) {
|
|
182
|
+
if (!migration.usedMigrations[type])
|
|
183
|
+
errorMsg += `Migration "${type}" for ${tableName} was allowed but not needed.
|
|
184
|
+
`;
|
|
185
|
+
}
|
|
164
186
|
}
|
|
165
|
-
|
|
187
|
+
if (errorMsg)
|
|
188
|
+
return new Error(errorMsg);
|
|
189
|
+
}
|
|
190
|
+
compareWithAllowedMigration(tableName, type) {
|
|
191
|
+
const migrationEntry = this.migrationData[tableName];
|
|
192
|
+
if (!migrationEntry?.allowedMigrations[type]) {
|
|
193
|
+
if (!this.alwaysAllowed[type])
|
|
194
|
+
this.notAllowedChanges.push({ version: this.toVersion, tableName, type });
|
|
195
|
+
} else if (migrationEntry)
|
|
196
|
+
migrationEntry.usedMigrations[type] = true;
|
|
166
197
|
}
|
|
167
198
|
allowMigration(version, table, ...allowedMigrations) {
|
|
168
199
|
if (version < this.fromVersion || version > this.toVersion)
|
|
@@ -172,23 +203,30 @@ var Migrations = class {
|
|
|
172
203
|
entry.allowedMigrations[key] = true;
|
|
173
204
|
}
|
|
174
205
|
}
|
|
175
|
-
renameTable(version, oldTableName,
|
|
206
|
+
renameTable(version, oldTableName, newTableName) {
|
|
176
207
|
if (version < this.fromVersion || version > this.toVersion)
|
|
177
208
|
return;
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
209
|
+
const newTableNameString = this.getTableName(newTableName);
|
|
210
|
+
if (this.migrationData.hasOwnProperty(oldTableName)) {
|
|
211
|
+
this.migrationData[newTableNameString] = this.getEntry(oldTableName);
|
|
212
|
+
delete this.migrationData[oldTableName];
|
|
213
|
+
}
|
|
214
|
+
const entry = this.getEntry(newTableName);
|
|
215
|
+
this.migrationDataForOldTableNames[oldTableName] = entry;
|
|
216
|
+
if (!entry.tableRenaming)
|
|
217
|
+
entry.tableRenaming = { oldName: oldTableName, newName: newTableNameString };
|
|
218
|
+
else
|
|
219
|
+
entry.tableRenaming.newName = newTableNameString;
|
|
182
220
|
}
|
|
183
221
|
renameColumn(version, table, oldColumn, newColumn) {
|
|
184
222
|
if (version < this.fromVersion || version > this.toVersion)
|
|
185
223
|
return;
|
|
186
224
|
const entry = this.getEntry(table);
|
|
187
|
-
const existingColumnEntry = entry.renamedColumns.find((entry2) => entry2
|
|
225
|
+
const existingColumnEntry = entry.renamedColumns.find((entry2) => entry2.newName == oldColumn);
|
|
188
226
|
if (existingColumnEntry)
|
|
189
|
-
existingColumnEntry.
|
|
227
|
+
existingColumnEntry.newName = newColumn;
|
|
190
228
|
else
|
|
191
|
-
entry.renamedColumns.push(
|
|
229
|
+
entry.renamedColumns.push({ oldName: oldColumn, newName: newColumn });
|
|
192
230
|
}
|
|
193
231
|
recreateTableImp(table) {
|
|
194
232
|
const entry = this.getEntry(table);
|
|
@@ -204,17 +242,19 @@ var Migrations = class {
|
|
|
204
242
|
}
|
|
205
243
|
loopRenamedColumns(tableName, callback) {
|
|
206
244
|
const migrationEntry = this.migrationData[tableName];
|
|
207
|
-
for (const
|
|
208
|
-
|
|
209
|
-
continue;
|
|
210
|
-
const oldColumnName = renamingArray[0];
|
|
211
|
-
const newColumnName = renamingArray[renamingArray.length - 1];
|
|
212
|
-
callback(oldColumnName, newColumnName);
|
|
245
|
+
for (const renamingData of migrationEntry.renamedColumns) {
|
|
246
|
+
callback(renamingData.oldName, renamingData.newName);
|
|
213
247
|
}
|
|
214
248
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
249
|
+
getNewestTableName(tableName) {
|
|
250
|
+
return this.migrationDataForOldTableNames[tableName]?.tableRenaming?.newName ?? tableName;
|
|
251
|
+
}
|
|
252
|
+
getOldTableName(tableName) {
|
|
253
|
+
return this.migrationData[tableName]?.tableRenaming?.oldName ?? tableName;
|
|
254
|
+
}
|
|
255
|
+
getNewestColumnName(tableName, oldColumnName) {
|
|
256
|
+
const columnNames = this.migrationData[tableName]?.renamedColumns.find((columns) => columns.oldName == oldColumnName);
|
|
257
|
+
return columnNames ? columnNames.newName : oldColumnName;
|
|
218
258
|
}
|
|
219
259
|
willBeRecreated(tableName) {
|
|
220
260
|
return this.migrationData[tableName]?.recreate;
|
|
@@ -225,6 +265,10 @@ var Migrations = class {
|
|
|
225
265
|
var MigrationManager = class {
|
|
226
266
|
migrations = new Migrations();
|
|
227
267
|
newTables = {};
|
|
268
|
+
/**
|
|
269
|
+
* Table names that currently exist in the database
|
|
270
|
+
* Note: If tables are set to be renamed, then names in this array will be corrected to their future name.
|
|
271
|
+
*/
|
|
228
272
|
existingTables = [];
|
|
229
273
|
dialect;
|
|
230
274
|
constructor(dialect) {
|
|
@@ -282,8 +326,9 @@ ${preSQL.down}`;
|
|
|
282
326
|
changes.down += initialChanges.down;
|
|
283
327
|
} else {
|
|
284
328
|
Logger.log(`Creating migration SQL from version ${fromVersion} to ${toVersion}`);
|
|
285
|
-
this.existingTables = await this.dialect.getTableNames();
|
|
329
|
+
this.existingTables = (await this.dialect.getTableNames()).map((oldTableName) => this.migrations.getNewestTableName(oldTableName));
|
|
286
330
|
[
|
|
331
|
+
this.renameTables(),
|
|
287
332
|
await this.createAndDropTables(),
|
|
288
333
|
await this.migrateForeignKeys(),
|
|
289
334
|
await this.migrateColumns(),
|
|
@@ -312,8 +357,18 @@ ${foreignKeyOnQuery}`;
|
|
|
312
357
|
changes.down += `
|
|
313
358
|
|
|
314
359
|
${foreignKeyOnQuery}`;
|
|
315
|
-
|
|
316
|
-
|
|
360
|
+
const exception = this.migrations.verifyRenamingTasks(this.newTables) ?? this.migrations.verifyAllowedMigrations();
|
|
361
|
+
if (exception) {
|
|
362
|
+
Logger.debug(`
|
|
363
|
+
Canceled changes:
|
|
364
|
+
${changes.up}
|
|
365
|
+
`);
|
|
366
|
+
if (dbInstructions.throwIfNotAllowed)
|
|
367
|
+
throw exception;
|
|
368
|
+
else {
|
|
369
|
+
console.error(exception.message);
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
317
372
|
}
|
|
318
373
|
return {
|
|
319
374
|
fromVersion,
|
|
@@ -321,6 +376,22 @@ ${foreignKeyOnQuery}`;
|
|
|
321
376
|
changes
|
|
322
377
|
};
|
|
323
378
|
}
|
|
379
|
+
renameTables() {
|
|
380
|
+
const changes = {
|
|
381
|
+
up: "\n\n-- Rename tables\n",
|
|
382
|
+
down: "\n\n-- Rename tables\n"
|
|
383
|
+
};
|
|
384
|
+
const data = this.migrations.getMigrationData();
|
|
385
|
+
for (const tableName in data) {
|
|
386
|
+
const migrationEntry = data[tableName];
|
|
387
|
+
if (migrationEntry.tableRenaming) {
|
|
388
|
+
Logger.log(`Table "${migrationEntry.tableRenaming.oldName}" will be renamed to "${tableName}"!`);
|
|
389
|
+
changes.up += this.dialect.renameTable(migrationEntry.tableRenaming.oldName, tableName);
|
|
390
|
+
changes.down += this.dialect.renameTable(tableName, migrationEntry.tableRenaming.oldName);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return changes;
|
|
394
|
+
}
|
|
324
395
|
/**
|
|
325
396
|
* Generates SQL change statements for creating and dropping tables based on the current table structure
|
|
326
397
|
* and existing table configurations in the database. The `up` changes include SQL statements for creating
|
|
@@ -332,8 +403,8 @@ ${foreignKeyOnQuery}`;
|
|
|
332
403
|
*/
|
|
333
404
|
async createAndDropTables() {
|
|
334
405
|
let changes = {
|
|
335
|
-
up: "\n\n-- Create tables\n",
|
|
336
|
-
down: "\n\n-- Create tables\n"
|
|
406
|
+
up: "\n\n-- Create and drop tables\n",
|
|
407
|
+
down: "\n\n-- Create and drop tables\n"
|
|
337
408
|
};
|
|
338
409
|
for (const tableName in this.newTables) {
|
|
339
410
|
if (this.tableDoesNotExists(tableName)) {
|
|
@@ -346,12 +417,13 @@ ${foreignKeyOnQuery}`;
|
|
|
346
417
|
}
|
|
347
418
|
for (const tableName of this.existingTables) {
|
|
348
419
|
if (!this.newTables[tableName]) {
|
|
349
|
-
this.migrations.
|
|
420
|
+
this.migrations.compareWithAllowedMigration(tableName, "dropTable");
|
|
350
421
|
if (!this.dialect.canInspectForeignKeys || !this.dialect.canInspectPrimaryKey)
|
|
351
|
-
this.migrations.
|
|
422
|
+
this.migrations.compareWithAllowedMigration(tableName, "continueWithoutRollback");
|
|
423
|
+
const dbTableName = this.migrations.getOldTableName(tableName);
|
|
352
424
|
changes.up += `${this.dialect.dropTable(tableName)}
|
|
353
425
|
`;
|
|
354
|
-
changes.down += this.dialect.canInspectForeignKeys && this.dialect.canInspectPrimaryKey ? this.createTableSql(tableName, await this.dialect.getColumnInformation(
|
|
426
|
+
changes.down += this.dialect.canInspectForeignKeys && this.dialect.canInspectPrimaryKey ? this.createTableSql(tableName, await this.dialect.getColumnInformation(dbTableName), await this.dialect.getForeignKeys(dbTableName)) : "\n\n-- Cannot recreate foreign keys or load primary keys with this database! Table will not be recreated!\n";
|
|
355
427
|
}
|
|
356
428
|
}
|
|
357
429
|
return changes;
|
|
@@ -402,10 +474,11 @@ ${foreignKeyOnQuery}`;
|
|
|
402
474
|
const migrationEntry = migrationData[tableName];
|
|
403
475
|
if (!migrationEntry.recreate)
|
|
404
476
|
continue;
|
|
405
|
-
|
|
477
|
+
Logger.log(`Table ${tableName} will be recreated!`);
|
|
478
|
+
this.migrations.compareWithAllowedMigration(tableName, "recreateTable");
|
|
406
479
|
if (!this.dialect.canInspectForeignKeys || !this.dialect.canInspectPrimaryKey)
|
|
407
|
-
this.migrations.
|
|
408
|
-
const oldColumnList = await this.dialect.getColumnInformation(tableName);
|
|
480
|
+
this.migrations.compareWithAllowedMigration(tableName, "continueWithoutRollback");
|
|
481
|
+
const oldColumnList = await this.dialect.getColumnInformation(this.migrations.getOldTableName(tableName));
|
|
409
482
|
const newColumnList = this.newTables[tableName].columns;
|
|
410
483
|
const moveableColumns = newColumnList.filter((entry) => oldColumnList.find((oldEntry) => oldEntry.name == entry.name) != void 0).map((entry) => entry.name);
|
|
411
484
|
const backupTableName = `${tableName}__backup`;
|
|
@@ -415,8 +488,9 @@ ${foreignKeyOnQuery}`;
|
|
|
415
488
|
);
|
|
416
489
|
const moveDataQuery = insertQuery + "\n" + this.dialect.dropTable(tableName) + "\n" + this.dialect.renameTable(backupTableName, tableName);
|
|
417
490
|
const structure = this.newTables[tableName];
|
|
418
|
-
changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery;
|
|
419
|
-
|
|
491
|
+
changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery + "\n";
|
|
492
|
+
const dbTableName = this.migrations.getOldTableName(tableName);
|
|
493
|
+
changes.down += this.dialect.canInspectForeignKeys && this.dialect.canInspectPrimaryKey ? this.createTableSql(backupTableName, await this.dialect.getColumnInformation(dbTableName), await this.dialect.getForeignKeys(dbTableName)) + "\n" + moveDataQuery : "\n\n-- Cannot recreate foreign keys or load primary keys with this database! Table will not be recreated!\n";
|
|
420
494
|
}
|
|
421
495
|
return changes;
|
|
422
496
|
}
|
|
@@ -424,8 +498,6 @@ ${foreignKeyOnQuery}`;
|
|
|
424
498
|
* Migrates foreign key constraints for all defined tables.
|
|
425
499
|
*/
|
|
426
500
|
async migrateForeignKeys() {
|
|
427
|
-
if (!this.dialect.canAlterForeignKeys)
|
|
428
|
-
return { up: "", down: "" };
|
|
429
501
|
let changes = {
|
|
430
502
|
up: "\n\n-- Foreign keys\n",
|
|
431
503
|
down: "\n\n-- Foreign keys\n"
|
|
@@ -435,13 +507,13 @@ ${foreignKeyOnQuery}`;
|
|
|
435
507
|
continue;
|
|
436
508
|
const structure = this.newTables[tableName];
|
|
437
509
|
const newForeignKeys = structure.foreignKeys ?? [];
|
|
438
|
-
const oldForeignKeys = await this.dialect.getForeignKeys(tableName);
|
|
510
|
+
const oldForeignKeys = await this.dialect.getForeignKeys(this.migrations.getOldTableName(tableName));
|
|
439
511
|
let checkForNewForeignKeys = true;
|
|
440
512
|
for (const oldForeignKey of oldForeignKeys) {
|
|
441
513
|
const newForeignKey = newForeignKeys.find((entry) => entry.fromColumn == oldForeignKey.fromColumn);
|
|
442
514
|
if (!newForeignKey) {
|
|
443
|
-
Logger.log(`Foreign key ${oldForeignKey.
|
|
444
|
-
this.migrations.
|
|
515
|
+
Logger.log(`Foreign key ${oldForeignKey.fromTable}.${oldForeignKey.fromColumn} to ${oldForeignKey.toTable}.${oldForeignKey.toColumn} will be removed!`);
|
|
516
|
+
this.migrations.compareWithAllowedMigration(tableName, "removeForeignKey");
|
|
445
517
|
if (this.dialect.canAlterForeignKeys) {
|
|
446
518
|
changes.up += this.dialect.removeForeignKey(oldForeignKey.fromTable, oldForeignKey.fromColumn);
|
|
447
519
|
changes.down += this.dialect.addForeignKey(
|
|
@@ -454,8 +526,8 @@ ${foreignKeyOnQuery}`;
|
|
|
454
526
|
}
|
|
455
527
|
} else {
|
|
456
528
|
if (!newForeignKey || oldForeignKey.toColumn != newForeignKey.toColumn || (oldForeignKey.onUpdate ?? "NO ACTION") != (newForeignKey.onUpdate ?? "NO ACTION") || (oldForeignKey.onDelete ?? "NO ACTION") != (newForeignKey.onDelete ?? "NO ACTION")) {
|
|
457
|
-
Logger.log(`Foreign key ${oldForeignKey.
|
|
458
|
-
this.migrations.
|
|
529
|
+
Logger.log(`Foreign key ${oldForeignKey.fromTable}.${oldForeignKey.fromColumn} to ${oldForeignKey.toTable}.${oldForeignKey.toColumn} will be changed!`);
|
|
530
|
+
this.migrations.compareWithAllowedMigration(tableName, "alterForeignKey");
|
|
459
531
|
if (this.dialect.canAlterForeignKeys) {
|
|
460
532
|
changes.up += this.dialect.removeForeignKey(oldForeignKey.fromTable, oldForeignKey.fromColumn) + this.dialect.addForeignKey(
|
|
461
533
|
newForeignKey.fromTable,
|
|
@@ -477,7 +549,7 @@ ${foreignKeyOnQuery}`;
|
|
|
477
549
|
const oldForeignKey = oldForeignKeys.find((entry) => entry.fromColumn == newForeignKey.fromColumn);
|
|
478
550
|
if (oldForeignKey)
|
|
479
551
|
continue;
|
|
480
|
-
Logger.log(`Foreign key ${newForeignKey.
|
|
552
|
+
Logger.log(`Foreign key ${newForeignKey.fromTable}.${newForeignKey.fromColumn} to ${newForeignKey.toTable}.${newForeignKey.toColumn} will be added!`);
|
|
481
553
|
if (this.dialect.canAlterForeignKeys) {
|
|
482
554
|
changes.up += this.dialect.addForeignKey(
|
|
483
555
|
newForeignKey.toTable,
|
|
@@ -506,13 +578,13 @@ ${foreignKeyOnQuery}`;
|
|
|
506
578
|
if (this.migrations.willBeRecreated(tableName) || this.tableDoesNotExists(tableName))
|
|
507
579
|
continue;
|
|
508
580
|
const newTableDefinition = this.newTables[tableName];
|
|
509
|
-
const oldColumnList = await this.dialect.getColumnInformation(tableName);
|
|
581
|
+
const oldColumnList = await this.dialect.getColumnInformation(this.migrations.getOldTableName(tableName));
|
|
510
582
|
const oldPrimaryKey = this.dialect.canInspectPrimaryKey ? this.getPrimaryKeyColumn(oldColumnList) : false;
|
|
511
583
|
const newColumnList = newTableDefinition.columns;
|
|
512
584
|
const newPrimaryKey = newTableDefinition.primaryKey;
|
|
513
585
|
if (this.dialect.canInspectPrimaryKey && oldPrimaryKey != newPrimaryKey) {
|
|
514
|
-
Logger.log(`Primary key in ${tableName}
|
|
515
|
-
this.migrations.
|
|
586
|
+
Logger.log(`Primary key in ${tableName} will be changed from ${oldPrimaryKey} to ${newPrimaryKey}!`);
|
|
587
|
+
this.migrations.compareWithAllowedMigration(tableName, "alterPrimaryKey");
|
|
516
588
|
if (this.dialect.canAlterPrimaryKey) {
|
|
517
589
|
if (oldPrimaryKey) {
|
|
518
590
|
changes.up += this.dialect.removePrimaryKey(tableName, oldPrimaryKey);
|
|
@@ -528,7 +600,7 @@ ${foreignKeyOnQuery}`;
|
|
|
528
600
|
}
|
|
529
601
|
for (const newColumn of newColumnList) {
|
|
530
602
|
const oldColumn = oldColumnList.find(
|
|
531
|
-
(entry) => this.migrations.
|
|
603
|
+
(entry) => this.migrations.getNewestColumnName(tableName, entry.name) == newColumn.name
|
|
532
604
|
);
|
|
533
605
|
if (oldColumn == void 0) {
|
|
534
606
|
changes.up += this.dialect.createColumn(tableName, this.dialect.columnDefinition(newColumn.name, newColumn.type, newColumn.defaultValue, newColumn.isPrimaryKey)) + "\n";
|
|
@@ -539,10 +611,10 @@ ${foreignKeyOnQuery}`;
|
|
|
539
611
|
}
|
|
540
612
|
for (const oldColumn of oldColumnList) {
|
|
541
613
|
const newColumn = newColumnList.find(
|
|
542
|
-
(entry) => entry.name == this.migrations.
|
|
614
|
+
(entry) => entry.name == this.migrations.getNewestColumnName(tableName, oldColumn.name)
|
|
543
615
|
);
|
|
544
616
|
if (newColumn == void 0) {
|
|
545
|
-
this.migrations.
|
|
617
|
+
this.migrations.compareWithAllowedMigration(tableName, "dropColumn");
|
|
546
618
|
changes.up += this.dialect.dropColumn(tableName, oldColumn.name) + "\n";
|
|
547
619
|
changes.down += this.dialect.createColumn(tableName, this.dialect.columnDefinition(oldColumn.name, oldColumn.type, oldColumn.defaultValue, oldColumn.isPrimaryKey)) + "\n";
|
|
548
620
|
}
|
|
@@ -576,6 +648,7 @@ ${foreignKeyOnQuery}`;
|
|
|
576
648
|
if (migrationData[tableName].recreate)
|
|
577
649
|
continue;
|
|
578
650
|
this.migrations.loopRenamedColumns(tableName, (oldColumnName, newColumnName) => {
|
|
651
|
+
Logger.log(`Column ${tableName}.${oldColumnName} will be renamed to ${tableName}.${newColumnName}!`);
|
|
579
652
|
changes.up += this.dialect.renameColumn(tableName, oldColumnName, newColumnName);
|
|
580
653
|
changes.down += this.dialect.renameColumn(tableName, newColumnName, oldColumnName);
|
|
581
654
|
});
|