sqlmorpheus 0.2.4 → 0.2.6
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 +5 -8
- package/dist/index.d.ts +5 -8
- package/dist/index.js +162 -158
- package/dist/index.mjs +151 -154
- package/package.json +4 -1
package/dist/index.d.mts
CHANGED
|
@@ -39,7 +39,7 @@ interface MigrationInstructions {
|
|
|
39
39
|
|
|
40
40
|
interface ColumnInfo {
|
|
41
41
|
name: string;
|
|
42
|
-
|
|
42
|
+
sqlType: string;
|
|
43
43
|
maxLength?: number;
|
|
44
44
|
defaultValue?: string;
|
|
45
45
|
isPrimaryKey: boolean;
|
|
@@ -251,7 +251,7 @@ interface SqlChanges {
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
type NativeDataTypeOptions = "boolean" | "number" | "bigint" | "string";
|
|
254
|
-
type AddedDataTypeOptions = "date" | "time" | "dateTime";
|
|
254
|
+
type AddedDataTypeOptions = "date" | "time" | "dateTime" | "text";
|
|
255
255
|
type DataTypeOptions = NativeDataTypeOptions | AddedDataTypeOptions;
|
|
256
256
|
|
|
257
257
|
interface InputColumnInfo extends ColumnInfo {
|
|
@@ -347,10 +347,7 @@ declare function ForeignKey<TOther extends TableClassInterface>(toTable: Class<T
|
|
|
347
347
|
onUpdate?: ForeignKeyActions;
|
|
348
348
|
}): (table: any, context: any) => void;
|
|
349
349
|
|
|
350
|
-
declare function
|
|
351
|
-
declare function
|
|
352
|
-
declare function prepareAndRunMigration(db: DatabaseAccess, instructions: DatabaseInstructions, overwriteExisting?: boolean): Promise<void>;
|
|
353
|
-
declare function runMigrationWithoutHistory(db: DatabaseAccess, dbInstructions: DatabaseInstructions): Promise<void>;
|
|
354
|
-
declare function rollback(db: DatabaseAccess, dbInstructions: DatabaseInstructions, toVersion: number): Promise<void>;
|
|
350
|
+
declare function runMigration(db: DatabaseAccess, dbInstructions: DatabaseInstructions): Promise<void>;
|
|
351
|
+
declare function rollback(db: DatabaseAccess, dbInstructions: DatabaseInstructions, toVersion: number): Promise<number>;
|
|
355
352
|
|
|
356
|
-
export { DataType, type DatabaseAccess, type DatabaseInstructions, ForeignKey, type PublicMigrations, type SqlChanges, TableClass, TableObj,
|
|
353
|
+
export { DataType, type DatabaseAccess, type DatabaseInstructions, ForeignKey, type PublicMigrations, type SqlChanges, TableClass, TableObj, rollback, runMigration };
|
package/dist/index.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ interface MigrationInstructions {
|
|
|
39
39
|
|
|
40
40
|
interface ColumnInfo {
|
|
41
41
|
name: string;
|
|
42
|
-
|
|
42
|
+
sqlType: string;
|
|
43
43
|
maxLength?: number;
|
|
44
44
|
defaultValue?: string;
|
|
45
45
|
isPrimaryKey: boolean;
|
|
@@ -251,7 +251,7 @@ interface SqlChanges {
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
type NativeDataTypeOptions = "boolean" | "number" | "bigint" | "string";
|
|
254
|
-
type AddedDataTypeOptions = "date" | "time" | "dateTime";
|
|
254
|
+
type AddedDataTypeOptions = "date" | "time" | "dateTime" | "text";
|
|
255
255
|
type DataTypeOptions = NativeDataTypeOptions | AddedDataTypeOptions;
|
|
256
256
|
|
|
257
257
|
interface InputColumnInfo extends ColumnInfo {
|
|
@@ -347,10 +347,7 @@ declare function ForeignKey<TOther extends TableClassInterface>(toTable: Class<T
|
|
|
347
347
|
onUpdate?: ForeignKeyActions;
|
|
348
348
|
}): (table: any, context: any) => void;
|
|
349
349
|
|
|
350
|
-
declare function
|
|
351
|
-
declare function
|
|
352
|
-
declare function prepareAndRunMigration(db: DatabaseAccess, instructions: DatabaseInstructions, overwriteExisting?: boolean): Promise<void>;
|
|
353
|
-
declare function runMigrationWithoutHistory(db: DatabaseAccess, dbInstructions: DatabaseInstructions): Promise<void>;
|
|
354
|
-
declare function rollback(db: DatabaseAccess, dbInstructions: DatabaseInstructions, toVersion: number): Promise<void>;
|
|
350
|
+
declare function runMigration(db: DatabaseAccess, dbInstructions: DatabaseInstructions): Promise<void>;
|
|
351
|
+
declare function rollback(db: DatabaseAccess, dbInstructions: DatabaseInstructions, toVersion: number): Promise<number>;
|
|
355
352
|
|
|
356
|
-
export { DataType, type DatabaseAccess, type DatabaseInstructions, ForeignKey, type PublicMigrations, type SqlChanges, TableClass, TableObj,
|
|
353
|
+
export { DataType, type DatabaseAccess, type DatabaseInstructions, ForeignKey, type PublicMigrations, type SqlChanges, TableClass, TableObj, rollback, runMigration };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -24,11 +34,8 @@ __export(index_exports, {
|
|
|
24
34
|
ForeignKey: () => ForeignKey,
|
|
25
35
|
TableClass: () => TableClass,
|
|
26
36
|
TableObj: () => TableObj,
|
|
27
|
-
prepareAndRunMigration: () => prepareAndRunMigration,
|
|
28
|
-
prepareMigration: () => prepareMigration,
|
|
29
37
|
rollback: () => rollback,
|
|
30
|
-
|
|
31
|
-
runPreparedMigrations: () => runPreparedMigrations
|
|
38
|
+
runMigration: () => runMigration
|
|
32
39
|
});
|
|
33
40
|
module.exports = __toCommonJS(index_exports);
|
|
34
41
|
|
|
@@ -68,7 +75,7 @@ var TableObj = class _TableObj {
|
|
|
68
75
|
static getEmptyColumnEntry(key) {
|
|
69
76
|
return {
|
|
70
77
|
name: key,
|
|
71
|
-
|
|
78
|
+
sqlType: "",
|
|
72
79
|
defaultValue: "",
|
|
73
80
|
isPrimaryKey: false
|
|
74
81
|
};
|
|
@@ -101,6 +108,7 @@ var TableObj = class _TableObj {
|
|
|
101
108
|
}
|
|
102
109
|
constructor(tableName, defaultValues) {
|
|
103
110
|
this.tableName = tableName;
|
|
111
|
+
this.tableStructure.table = tableName;
|
|
104
112
|
for (const key in defaultValues) {
|
|
105
113
|
const value = defaultValues[key];
|
|
106
114
|
const columnEntry = _TableObj.getColumnEntry(key, value);
|
|
@@ -110,6 +118,9 @@ var TableObj = class _TableObj {
|
|
|
110
118
|
}
|
|
111
119
|
}
|
|
112
120
|
primaryKey(key) {
|
|
121
|
+
if (this.tableStructure.primaryKey) {
|
|
122
|
+
this.tableStructure.columns[this.tableStructure.primaryKey].isPrimaryKey = false;
|
|
123
|
+
}
|
|
113
124
|
this.tableStructure.primaryKey = key.toString();
|
|
114
125
|
this.tableStructure.columns[key.toString()].isPrimaryKey = true;
|
|
115
126
|
return this;
|
|
@@ -191,8 +202,8 @@ var TableStructureGenerator = class {
|
|
|
191
202
|
}
|
|
192
203
|
for (const key in structure.columns) {
|
|
193
204
|
const entry = structure.columns[key];
|
|
194
|
-
entry.defaultValue = entry.inputDefaultValue === null ? this.dialect.
|
|
195
|
-
entry.
|
|
205
|
+
entry.defaultValue = entry.inputDefaultValue === null ? this.dialect.nullType : this.dialect.formatValueToSql(entry.inputDefaultValue, entry.inputType);
|
|
206
|
+
entry.sqlType = this.dialect.getSqlType(entry.inputType, entry);
|
|
196
207
|
}
|
|
197
208
|
}
|
|
198
209
|
return tables;
|
|
@@ -510,6 +521,7 @@ var Migrations = class {
|
|
|
510
521
|
const entry = this.getEntry(table);
|
|
511
522
|
entry.recreate = true;
|
|
512
523
|
Logger.log(`Table ${this.getTableName(table)} will be recreated!`);
|
|
524
|
+
this.allowMigration(version, table, "recreateTable");
|
|
513
525
|
}
|
|
514
526
|
};
|
|
515
527
|
|
|
@@ -609,17 +621,19 @@ ${foreignKeyOnQuery}`;
|
|
|
609
621
|
changes.down += `
|
|
610
622
|
|
|
611
623
|
${foreignKeyOnQuery}`;
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
624
|
+
if (fromVersion != 0) {
|
|
625
|
+
const exception = this.migrations.verifyRenamingTasks(this.newTables) ?? this.migrations.verifyAllowedMigrations();
|
|
626
|
+
if (exception) {
|
|
627
|
+
Logger.debug(`
|
|
615
628
|
Canceled changes:
|
|
616
629
|
${changes.up}
|
|
617
630
|
`);
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
631
|
+
if (dbInstructions.throwIfNotAllowed)
|
|
632
|
+
throw exception;
|
|
633
|
+
else {
|
|
634
|
+
console.error(exception.message);
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
623
637
|
}
|
|
624
638
|
}
|
|
625
639
|
return {
|
|
@@ -694,7 +708,7 @@ ${changes.up}
|
|
|
694
708
|
const queryLines = [];
|
|
695
709
|
for (const key in columns) {
|
|
696
710
|
const columnInfo = columns[key];
|
|
697
|
-
queryLines.push(this.dialect.columnDefinition(columnInfo.name, columnInfo.
|
|
711
|
+
queryLines.push(this.dialect.columnDefinition(columnInfo.name, columnInfo.sqlType, columnInfo.isPrimaryKey, columnInfo.defaultValue));
|
|
698
712
|
}
|
|
699
713
|
if (foreignKeys) {
|
|
700
714
|
for (const foreignKeyInfo of foreignKeys) {
|
|
@@ -858,13 +872,17 @@ ${changes.up}
|
|
|
858
872
|
this.migrations.internalRecreate(newTableDefinition.table, "Dialect does not support altering primary keys");
|
|
859
873
|
continue;
|
|
860
874
|
}
|
|
875
|
+
const renamedColumns = this.migrations.getMigrationData()[tableName]?.renamedColumns;
|
|
861
876
|
for (const key in newColumnList) {
|
|
862
877
|
const newColumn = newColumnList[key];
|
|
863
878
|
const oldColumn = oldColumnNewNameIndex[newColumn.name];
|
|
879
|
+
const willBeRenamed = renamedColumns?.find((entry) => entry.newName);
|
|
880
|
+
if (willBeRenamed)
|
|
881
|
+
continue;
|
|
864
882
|
if (oldColumn == void 0) {
|
|
865
|
-
changes.up += this.dialect.createColumn(tableName, this.dialect.columnDefinition(newColumn.name, newColumn.
|
|
883
|
+
changes.up += this.dialect.createColumn(tableName, this.dialect.columnDefinition(newColumn.name, newColumn.sqlType, newColumn.isPrimaryKey, newColumn.defaultValue)) + "\n";
|
|
866
884
|
changes.down += this.dialect.dropColumn(tableName, newColumn.name) + "\n";
|
|
867
|
-
} else if (newColumn.
|
|
885
|
+
} else if (newColumn.sqlType != oldColumn.sqlType || newColumn.defaultValue != oldColumn.defaultValue) {
|
|
868
886
|
this.migrations.internalRecreate(newTableDefinition.table, "Table structure was changed");
|
|
869
887
|
}
|
|
870
888
|
}
|
|
@@ -874,7 +892,7 @@ ${changes.up}
|
|
|
874
892
|
if (newColumn == void 0) {
|
|
875
893
|
this.migrations.compareWithAllowedMigration(tableName, "dropColumn");
|
|
876
894
|
changes.up += this.dialect.dropColumn(tableName, oldColumn.name) + "\n";
|
|
877
|
-
changes.down += this.dialect.createColumn(tableName, this.dialect.columnDefinition(oldColumn.name, oldColumn.
|
|
895
|
+
changes.down += this.dialect.createColumn(tableName, this.dialect.columnDefinition(oldColumn.name, oldColumn.sqlType, oldColumn.isPrimaryKey, oldColumn.defaultValue)) + "\n";
|
|
878
896
|
}
|
|
879
897
|
}
|
|
880
898
|
}
|
|
@@ -916,64 +934,6 @@ ${changes.up}
|
|
|
916
934
|
}
|
|
917
935
|
};
|
|
918
936
|
|
|
919
|
-
// src/lib/MigrationHistoryManager.ts
|
|
920
|
-
var import_node_fs = require("fs");
|
|
921
|
-
var import_fs = require("fs");
|
|
922
|
-
var FILENAME_UP_PREFIX = "up_to_";
|
|
923
|
-
var FILENAME_DOWN_PREFIX = "down_to_";
|
|
924
|
-
var MigrationHistoryManager = class {
|
|
925
|
-
configPath;
|
|
926
|
-
migrationsPath;
|
|
927
|
-
constructor(configPath) {
|
|
928
|
-
this.configPath = configPath;
|
|
929
|
-
this.migrationsPath = `${configPath}/migrations`;
|
|
930
|
-
(0, import_node_fs.mkdirSync)(this.configPath, { recursive: true });
|
|
931
|
-
(0, import_node_fs.mkdirSync)(this.migrationsPath, { recursive: true });
|
|
932
|
-
}
|
|
933
|
-
/**
|
|
934
|
-
* Creates migration history files for upgrading and downgrading database schemas.
|
|
935
|
-
*
|
|
936
|
-
* @param fromVersion - The current version number of the database.
|
|
937
|
-
* @param toVersion - The target version number for the migration.
|
|
938
|
-
* @param changes - An object containing the SQL changes for upgrading (`changes.up`) and downgrading (`changes.down`).
|
|
939
|
-
* @param overwriteExisting - A flag that determines if existing migration files should be overwritten. Defaults to `false`.
|
|
940
|
-
*/
|
|
941
|
-
createMigrationHistory(fromVersion, toVersion, changes, overwriteExisting) {
|
|
942
|
-
const upPath = `${this.migrationsPath}/${FILENAME_UP_PREFIX}${toVersion}.sql`;
|
|
943
|
-
const downPath = `${this.migrationsPath}/${FILENAME_DOWN_PREFIX}${fromVersion}.sql`;
|
|
944
|
-
if ((0, import_node_fs.existsSync)(upPath) && !overwriteExisting)
|
|
945
|
-
throw new Error(`Migration ${upPath} already exists!`);
|
|
946
|
-
else if ((0, import_node_fs.existsSync)(downPath) && !overwriteExisting)
|
|
947
|
-
throw new Error(`Migration ${downPath} already exists!`);
|
|
948
|
-
Logger.log(`Save migrations to ${this.migrationsPath}`);
|
|
949
|
-
(0, import_node_fs.writeFileSync)(`${this.migrationsPath}/${FILENAME_UP_PREFIX}${toVersion}.sql`, `-- From: Version ${fromVersion}
|
|
950
|
-
-- To: Version ${toVersion}
|
|
951
|
-
${changes.up}`, { encoding: "utf-8" });
|
|
952
|
-
(0, import_node_fs.writeFileSync)(`${this.migrationsPath}/${FILENAME_DOWN_PREFIX}${fromVersion}.sql`, `-- From: Version ${toVersion}
|
|
953
|
-
-- To: Version ${fromVersion}
|
|
954
|
-
${changes.down}`, { encoding: "utf-8" });
|
|
955
|
-
}
|
|
956
|
-
/**
|
|
957
|
-
* Retrieves the SQL migration script for upgrading to the specified version.
|
|
958
|
-
* If no version is provided, it retrieves the migration script for the latest version in the history.
|
|
959
|
-
*
|
|
960
|
-
* @param toVersion - The version of the migration script to retrieve. If not specified, the latest version is used.
|
|
961
|
-
* @return The content of the SQL migration script as a string.
|
|
962
|
-
*/
|
|
963
|
-
getUpMigration(toVersion) {
|
|
964
|
-
return (0, import_fs.readFileSync)(`${this.migrationsPath}/${FILENAME_UP_PREFIX}${toVersion}.sql`, { encoding: "utf-8" });
|
|
965
|
-
}
|
|
966
|
-
/**
|
|
967
|
-
* Retrieves the SQL migration script for downgrading to the specified version.
|
|
968
|
-
*
|
|
969
|
-
* @param version - The version number of the migration to retrieve.
|
|
970
|
-
* @return The SQL script for the down migration.
|
|
971
|
-
*/
|
|
972
|
-
getDownMigration(version) {
|
|
973
|
-
return (0, import_fs.readFileSync)(`${this.migrationsPath}/${FILENAME_DOWN_PREFIX}${version}.sql`, { encoding: "utf-8" });
|
|
974
|
-
}
|
|
975
|
-
};
|
|
976
|
-
|
|
977
937
|
// src/lib/tableInfo/decorators/TableClass.ts
|
|
978
938
|
var import_polyfill_symbol_metadata = require("polyfill-symbol-metadata");
|
|
979
939
|
function TableClass(tableName, primaryKey) {
|
|
@@ -1035,6 +995,7 @@ function ForeignKey(toTable, toColumn, options) {
|
|
|
1035
995
|
}
|
|
1036
996
|
|
|
1037
997
|
// src/lib/dialects/DefaultSql.ts
|
|
998
|
+
var import_escape_sql_string = __toESM(require("escape-sql-string"));
|
|
1038
999
|
var MIGRATION_DATA_TABLE_NAME = "__sqlmorpheus_migrations";
|
|
1039
1000
|
var DefaultSql = class {
|
|
1040
1001
|
db;
|
|
@@ -1043,15 +1004,16 @@ var DefaultSql = class {
|
|
|
1043
1004
|
canInspectForeignKeys = false;
|
|
1044
1005
|
canInspectPrimaryKey = false;
|
|
1045
1006
|
types = {
|
|
1007
|
+
text: "TEXT",
|
|
1046
1008
|
string: "TEXT",
|
|
1047
1009
|
number: "INTEGER",
|
|
1048
1010
|
bigint: "BIGINT",
|
|
1049
1011
|
boolean: "BOOLEAN",
|
|
1050
1012
|
date: "DATE",
|
|
1051
1013
|
time: "TIME",
|
|
1052
|
-
dateTime: "DATETIME"
|
|
1053
|
-
null: "NULL"
|
|
1014
|
+
dateTime: "DATETIME"
|
|
1054
1015
|
};
|
|
1016
|
+
nullType = "NULL";
|
|
1055
1017
|
constructor(db) {
|
|
1056
1018
|
this.db = db;
|
|
1057
1019
|
}
|
|
@@ -1067,6 +1029,7 @@ var DefaultSql = class {
|
|
|
1067
1029
|
return value2 instanceof Date ? value2 : new Date(value2);
|
|
1068
1030
|
}
|
|
1069
1031
|
switch (type) {
|
|
1032
|
+
case "text":
|
|
1070
1033
|
case "string":
|
|
1071
1034
|
return `"${value}"`;
|
|
1072
1035
|
case "time":
|
|
@@ -1236,12 +1199,17 @@ var DefaultSql = class {
|
|
|
1236
1199
|
* @param tableName - The name of the database table to select data from.
|
|
1237
1200
|
* @param select - An array of column names to include in the SELECT clause.
|
|
1238
1201
|
* @param where - An optional condition.
|
|
1202
|
+
* @param orderBy - Optional. the column to sort by
|
|
1239
1203
|
* @return The SQL statement as a string.
|
|
1240
1204
|
*/
|
|
1241
|
-
select(tableName, select, where) {
|
|
1205
|
+
select(tableName, select, where, orderBy) {
|
|
1242
1206
|
let query = `SELECT ${select.join(",")} FROM ${tableName}`;
|
|
1243
|
-
if (where)
|
|
1207
|
+
if (where) {
|
|
1244
1208
|
query += ` WHERE ${where}`;
|
|
1209
|
+
}
|
|
1210
|
+
if (orderBy) {
|
|
1211
|
+
query += ` ORDER BY ${orderBy}`;
|
|
1212
|
+
}
|
|
1245
1213
|
return `${query};`;
|
|
1246
1214
|
}
|
|
1247
1215
|
/**
|
|
@@ -1273,7 +1241,10 @@ var DefaultSql = class {
|
|
|
1273
1241
|
*/
|
|
1274
1242
|
migrationTableQuery() {
|
|
1275
1243
|
return this.createTable(MIGRATION_DATA_TABLE_NAME, [
|
|
1276
|
-
this.columnDefinition("
|
|
1244
|
+
this.columnDefinition("fromVersion", this.getSqlType("number"), false, "0"),
|
|
1245
|
+
this.columnDefinition("toVersion", this.getSqlType("number"), true, "0"),
|
|
1246
|
+
this.columnDefinition("upChanges", this.getSqlType("text"), false),
|
|
1247
|
+
this.columnDefinition("downChanges", this.getSqlType("text"), false)
|
|
1277
1248
|
]);
|
|
1278
1249
|
}
|
|
1279
1250
|
/**
|
|
@@ -1302,23 +1273,44 @@ var DefaultSql = class {
|
|
|
1302
1273
|
*/
|
|
1303
1274
|
async getVersion() {
|
|
1304
1275
|
await this.createMigrationTableIfNeeded();
|
|
1305
|
-
const query = this.select(MIGRATION_DATA_TABLE_NAME, ["
|
|
1276
|
+
const query = this.select(MIGRATION_DATA_TABLE_NAME, ["toVersion"], void 0, "toVersion DESC");
|
|
1306
1277
|
const data = await this.db.runGetStatement(query);
|
|
1307
|
-
return data.length ? data[0].
|
|
1278
|
+
return data.length ? data[0].toVersion : 0;
|
|
1308
1279
|
}
|
|
1309
1280
|
/**
|
|
1310
1281
|
* Updates the migration version in the database. If the version entry does not exist, it inserts a new one;
|
|
1311
1282
|
* if it exists, it updates the version to the specified value.
|
|
1312
1283
|
*
|
|
1313
|
-
* @param
|
|
1284
|
+
* @param toVersion - The new migration version to set in the database.
|
|
1314
1285
|
* @return A promise that resolves when the operation is complete.
|
|
1315
1286
|
*/
|
|
1316
|
-
async
|
|
1317
|
-
const query = `${this.migrationTableQuery()};
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1287
|
+
async rollbackHistory(toVersion) {
|
|
1288
|
+
const query = `${this.migrationTableQuery()}; DELETE FROM ${MIGRATION_DATA_TABLE_NAME} WHERE toVersion > ${toVersion};`;
|
|
1289
|
+
await this.db.runMultipleWriteStatements(query);
|
|
1290
|
+
}
|
|
1291
|
+
async getChanges(version) {
|
|
1292
|
+
await this.createMigrationTableIfNeeded();
|
|
1293
|
+
const query = this.select(MIGRATION_DATA_TABLE_NAME, ["fromVersion", "upChanges", "downChanges"], `toVersion = ${version}`);
|
|
1294
|
+
const data = await this.db.runGetStatement(query);
|
|
1295
|
+
return data ? { up: data[0].upChanges, down: data[0].downChanges, fromVersion: data[0].fromVersion } : null;
|
|
1296
|
+
}
|
|
1297
|
+
async hasChanges(version) {
|
|
1298
|
+
await this.createMigrationTableIfNeeded();
|
|
1299
|
+
const query = this.select(MIGRATION_DATA_TABLE_NAME, ["toVersion"], `toVersion = ${version}`);
|
|
1300
|
+
const data = await this.db.runGetStatement(query);
|
|
1301
|
+
return !!data.length;
|
|
1302
|
+
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Saves the change history to the SQLMorpheus table in the database
|
|
1305
|
+
* Since we save SQL queries in the database, we need to make sure that they are properly escaped.
|
|
1306
|
+
* @param fromVersion
|
|
1307
|
+
* @param toVersion
|
|
1308
|
+
* @param changes
|
|
1309
|
+
*/
|
|
1310
|
+
async setChanges(fromVersion, toVersion, changes) {
|
|
1311
|
+
const downChanges = (0, import_escape_sql_string.default)(changes.down);
|
|
1312
|
+
const upChanges = (0, import_escape_sql_string.default)(changes.up);
|
|
1313
|
+
const query = `${this.migrationTableQuery()} ${await this.hasChanges(toVersion) ? `UPDATE ${MIGRATION_DATA_TABLE_NAME} SET fromVersion = '${fromVersion}', upChanges = ${upChanges}, downChanges = ${downChanges} WHERE toVersion = '${toVersion}';` : `INSERT INTO ${MIGRATION_DATA_TABLE_NAME} (fromVersion, toVersion, upChanges, downChanges) VALUES ('${fromVersion}', '${toVersion}', ${upChanges}, ${downChanges})`}`;
|
|
1322
1314
|
await this.db.runMultipleWriteStatements(query);
|
|
1323
1315
|
}
|
|
1324
1316
|
};
|
|
@@ -1326,14 +1318,14 @@ UPDATE ${MIGRATION_DATA_TABLE_NAME} SET version = ${newVersion} WHERE EXISTS (SE
|
|
|
1326
1318
|
// src/lib/dialects/SqliteDialect.ts
|
|
1327
1319
|
var SqliteDialect = class extends DefaultSql {
|
|
1328
1320
|
types = {
|
|
1321
|
+
text: "TEXT",
|
|
1329
1322
|
string: "TEXT",
|
|
1330
1323
|
number: "INTEGER",
|
|
1331
1324
|
bigint: "INTEGER",
|
|
1332
1325
|
boolean: "INTEGER",
|
|
1333
1326
|
date: "INTEGER",
|
|
1334
1327
|
time: "INTEGER",
|
|
1335
|
-
dateTime: "INTEGER"
|
|
1336
|
-
null: "NULL"
|
|
1328
|
+
dateTime: "INTEGER"
|
|
1337
1329
|
};
|
|
1338
1330
|
canAlterForeignKeys = false;
|
|
1339
1331
|
canAlterPrimaryKey = false;
|
|
@@ -1351,8 +1343,8 @@ var SqliteDialect = class extends DefaultSql {
|
|
|
1351
1343
|
for (const entry of data) {
|
|
1352
1344
|
output[entry["name"]] = {
|
|
1353
1345
|
name: entry["name"],
|
|
1354
|
-
|
|
1355
|
-
defaultValue: entry["dflt_value"],
|
|
1346
|
+
sqlType: entry["type"],
|
|
1347
|
+
defaultValue: entry["dflt_value"] ?? this.nullType,
|
|
1356
1348
|
isPrimaryKey: entry["pk"] == "1"
|
|
1357
1349
|
};
|
|
1358
1350
|
}
|
|
@@ -1386,12 +1378,17 @@ var SqliteDialect = class extends DefaultSql {
|
|
|
1386
1378
|
};
|
|
1387
1379
|
});
|
|
1388
1380
|
}
|
|
1381
|
+
async setChanges(fromVersion, toVersion, changes) {
|
|
1382
|
+
await this.db.runMultipleWriteStatements(`PRAGMA user_version = ${toVersion};`);
|
|
1383
|
+
await super.setChanges(fromVersion, toVersion, changes);
|
|
1384
|
+
}
|
|
1389
1385
|
async getVersion() {
|
|
1390
1386
|
const output = await this.db.runGetStatement(`PRAGMA user_version;`);
|
|
1391
1387
|
return output[0].user_version;
|
|
1392
1388
|
}
|
|
1393
|
-
async
|
|
1394
|
-
await this.db.runMultipleWriteStatements(`PRAGMA user_version = ${
|
|
1389
|
+
async rollbackHistory(toVersion) {
|
|
1390
|
+
await this.db.runMultipleWriteStatements(`PRAGMA user_version = ${toVersion};`);
|
|
1391
|
+
await super.rollbackHistory(toVersion);
|
|
1395
1392
|
}
|
|
1396
1393
|
};
|
|
1397
1394
|
|
|
@@ -1419,7 +1416,7 @@ var PostgresDialect = class extends DefaultSql {
|
|
|
1419
1416
|
for (const entry of data) {
|
|
1420
1417
|
output[entry["name"]] = {
|
|
1421
1418
|
name: entry["column_name"],
|
|
1422
|
-
|
|
1419
|
+
sqlType: entry["data_type"],
|
|
1423
1420
|
maxLength: 0,
|
|
1424
1421
|
defaultValue: entry["column_default"],
|
|
1425
1422
|
isPrimaryKey: false
|
|
@@ -1478,7 +1475,7 @@ var MsSqlDialect = class extends DefaultSql {
|
|
|
1478
1475
|
for (const entry of data) {
|
|
1479
1476
|
output[entry["name"]] = {
|
|
1480
1477
|
name: entry["name"],
|
|
1481
|
-
|
|
1478
|
+
sqlType: entry["DATA_TYPE"],
|
|
1482
1479
|
maxLength: 0,
|
|
1483
1480
|
defaultValue: entry["COLUMN_DEFAULT"],
|
|
1484
1481
|
isPrimaryKey: entry["pk"] == "1"
|
|
@@ -1495,19 +1492,27 @@ var MySqlDialect = class extends DefaultSql {
|
|
|
1495
1492
|
canInspectForeignKeys = true;
|
|
1496
1493
|
canInspectPrimaryKey = true;
|
|
1497
1494
|
types = {
|
|
1495
|
+
text: "TEXT",
|
|
1498
1496
|
string: "VARCHAR",
|
|
1499
1497
|
number: "INT",
|
|
1500
1498
|
bigint: "BIGINT",
|
|
1501
|
-
boolean: "
|
|
1499
|
+
boolean: "TINYINT(1)",
|
|
1502
1500
|
date: "DATE",
|
|
1503
1501
|
time: "TIME",
|
|
1504
|
-
dateTime: "DATETIME"
|
|
1505
|
-
null: "NULL"
|
|
1502
|
+
dateTime: "DATETIME"
|
|
1506
1503
|
};
|
|
1504
|
+
formatValueToSql(value, type) {
|
|
1505
|
+
switch (type) {
|
|
1506
|
+
case "boolean":
|
|
1507
|
+
return value ? "1" : "0";
|
|
1508
|
+
default:
|
|
1509
|
+
return super.formatValueToSql(value, type);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1507
1512
|
getSqlType(dataType, columnInfo) {
|
|
1508
1513
|
switch (dataType) {
|
|
1509
1514
|
case "string":
|
|
1510
|
-
return this.types[dataType] + `(${columnInfo?.maxLength})`;
|
|
1515
|
+
return this.types[dataType] + `(${columnInfo?.maxLength ?? 100})`;
|
|
1511
1516
|
default:
|
|
1512
1517
|
return super.getSqlType(dataType, columnInfo);
|
|
1513
1518
|
}
|
|
@@ -1528,31 +1533,55 @@ var MySqlDialect = class extends DefaultSql {
|
|
|
1528
1533
|
return `ALTER TABLE ${tableName} PRIMARY KEY;`;
|
|
1529
1534
|
}
|
|
1530
1535
|
async getForeignKeys(tableName) {
|
|
1531
|
-
const
|
|
1536
|
+
const data3 = await this.db.runGetStatement(
|
|
1537
|
+
`SELECT
|
|
1538
|
+
*
|
|
1539
|
+
FROM information_schema.KEY_COLUMN_USAGE AS info
|
|
1540
|
+
INNER JOIN information_schema.REFERENTIAL_CONSTRAINTS AS constr ON info.CONSTRAINT_NAME = constr.CONSTRAINT_NAME
|
|
1541
|
+
WHERE info.REFERENCED_TABLE_SCHEMA = (SELECT DATABASE()) AND info.TABLE_NAME = '${tableName}';`
|
|
1542
|
+
);
|
|
1543
|
+
const data = await this.db.runGetStatement(
|
|
1544
|
+
`SELECT
|
|
1545
|
+
info.TABLE_NAME, info.COLUMN_NAME, info.REFERENCED_TABLE_NAME, info.REFERENCED_COLUMN_NAME, constr.UPDATE_RULE, constr.DELETE_RULE
|
|
1546
|
+
FROM information_schema.KEY_COLUMN_USAGE AS info INNER JOIN information_schema.REFERENTIAL_CONSTRAINTS AS constr ON info.CONSTRAINT_NAME = constr.CONSTRAINT_NAME
|
|
1547
|
+
WHERE info.REFERENCED_TABLE_SCHEMA = (SELECT DATABASE()) AND info.TABLE_NAME = '${tableName}';`
|
|
1548
|
+
);
|
|
1532
1549
|
return data.map((entry) => {
|
|
1533
1550
|
return {
|
|
1534
1551
|
fromTable: tableName,
|
|
1535
1552
|
fromColumn: entry["COLUMN_NAME"],
|
|
1536
1553
|
toTable: entry["REFERENCED_TABLE_NAME"],
|
|
1537
|
-
toColumn: entry["REFERENCED_COLUMN_NAME"]
|
|
1554
|
+
toColumn: entry["REFERENCED_COLUMN_NAME"],
|
|
1555
|
+
onUpdate: entry["UPDATE_RULE"],
|
|
1556
|
+
onDelete: entry["DELETE_RULE"]
|
|
1538
1557
|
};
|
|
1539
1558
|
});
|
|
1540
1559
|
}
|
|
1541
1560
|
async getTableNames() {
|
|
1542
|
-
const
|
|
1543
|
-
return
|
|
1561
|
+
const entries = await this.db.runGetStatement("SHOW TABLES");
|
|
1562
|
+
return entries.map((entry) => Object.values(entry)[0]);
|
|
1544
1563
|
}
|
|
1545
1564
|
async getColumnInformation(tableName) {
|
|
1546
1565
|
const data = await this.db.runGetStatement(`SHOW COLUMNS FROM ${tableName};`);
|
|
1547
1566
|
const output = {};
|
|
1548
1567
|
for (const entry of data) {
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
isPrimaryKey: entry["pk"] == "1"
|
|
1568
|
+
const sqlType = entry["Type"].toUpperCase();
|
|
1569
|
+
const info = {
|
|
1570
|
+
name: entry["Field"],
|
|
1571
|
+
sqlType,
|
|
1572
|
+
isPrimaryKey: entry["Key"] == "PRI"
|
|
1555
1573
|
};
|
|
1574
|
+
switch (sqlType.substring(0, 4)) {
|
|
1575
|
+
case "TEXT":
|
|
1576
|
+
case "VARC":
|
|
1577
|
+
case "DATE":
|
|
1578
|
+
case "TIME":
|
|
1579
|
+
info.defaultValue = entry["Default"] ? `"${entry["Default"]}"` : this.nullType;
|
|
1580
|
+
break;
|
|
1581
|
+
default:
|
|
1582
|
+
info.defaultValue = entry["Default"] ?? this.nullType;
|
|
1583
|
+
}
|
|
1584
|
+
output[entry["Field"]] = info;
|
|
1556
1585
|
}
|
|
1557
1586
|
return output;
|
|
1558
1587
|
}
|
|
@@ -1575,45 +1604,13 @@ function getDialect(db, dbInstructions) {
|
|
|
1575
1604
|
}
|
|
1576
1605
|
|
|
1577
1606
|
// src/index.ts
|
|
1578
|
-
async function
|
|
1579
|
-
Logger.setMode(dbInstructions.loggerMode);
|
|
1580
|
-
const dialect = getDialect(db, dbInstructions);
|
|
1581
|
-
const mm = new MigrationManager(dialect);
|
|
1582
|
-
const migration = await mm.generateSqlChanges(dbInstructions);
|
|
1583
|
-
if (!migration)
|
|
1584
|
-
return;
|
|
1585
|
-
const migrationHistoryManager = new MigrationHistoryManager(dbInstructions.configPath);
|
|
1586
|
-
migrationHistoryManager.createMigrationHistory(migration.fromVersion, dbInstructions.version, migration.changes, overwriteExisting);
|
|
1587
|
-
}
|
|
1588
|
-
async function runPreparedMigrations(db, dbInstructions) {
|
|
1589
|
-
Logger.setMode(dbInstructions.loggerMode);
|
|
1590
|
-
const historyManager = new MigrationHistoryManager(dbInstructions.configPath);
|
|
1591
|
-
const dialect = getDialect(db, dbInstructions);
|
|
1592
|
-
const fromVersion = await dialect.getVersion();
|
|
1593
|
-
const toVersion = dbInstructions.version;
|
|
1594
|
-
if (fromVersion == toVersion)
|
|
1595
|
-
return;
|
|
1596
|
-
await db.createBackup?.(`from_${fromVersion}_to_${dbInstructions.version}`);
|
|
1597
|
-
Logger.log(`Running migrations from ${fromVersion} to ${toVersion}`);
|
|
1598
|
-
for (let i = fromVersion ? fromVersion + 1 : toVersion; i <= toVersion; ++i) {
|
|
1599
|
-
const upChanges = historyManager.getUpMigration(i);
|
|
1600
|
-
Logger.debug(upChanges);
|
|
1601
|
-
await db.runMultipleWriteStatements(upChanges);
|
|
1602
|
-
dbInstructions.version = i;
|
|
1603
|
-
}
|
|
1604
|
-
await dialect.setVersion(toVersion);
|
|
1605
|
-
}
|
|
1606
|
-
async function prepareAndRunMigration(db, instructions, overwriteExisting) {
|
|
1607
|
-
Logger.setMode(instructions.loggerMode);
|
|
1608
|
-
await prepareMigration(db, instructions, overwriteExisting);
|
|
1609
|
-
await runPreparedMigrations(db, instructions);
|
|
1610
|
-
}
|
|
1611
|
-
async function runMigrationWithoutHistory(db, dbInstructions) {
|
|
1607
|
+
async function runMigration(db, dbInstructions) {
|
|
1612
1608
|
Logger.setMode(dbInstructions.loggerMode);
|
|
1613
1609
|
const dialect = getDialect(db, dbInstructions);
|
|
1614
1610
|
const mm = new MigrationManager(dialect);
|
|
1615
1611
|
const migration = await mm.generateSqlChanges(dbInstructions);
|
|
1616
1612
|
if (migration) {
|
|
1613
|
+
await dialect.setChanges(migration.fromVersion, dbInstructions.version, migration.changes);
|
|
1617
1614
|
await db.createBackup?.(`from_${migration.fromVersion}_to_${dbInstructions.version}`);
|
|
1618
1615
|
await db.runMultipleWriteStatements(migration.changes.up);
|
|
1619
1616
|
}
|
|
@@ -1622,16 +1619,26 @@ async function rollback(db, dbInstructions, toVersion) {
|
|
|
1622
1619
|
Logger.setMode(dbInstructions.loggerMode);
|
|
1623
1620
|
const dialect = getDialect(db, dbInstructions);
|
|
1624
1621
|
const fromVersion = await dialect.getVersion();
|
|
1625
|
-
const historyManager = new MigrationHistoryManager(dbInstructions.configPath);
|
|
1626
1622
|
await db.createBackup?.(`from_${fromVersion}_to_${dbInstructions.version}`);
|
|
1627
1623
|
Logger.log(`Rolling back from ${fromVersion} to ${toVersion}`);
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1624
|
+
let version = fromVersion;
|
|
1625
|
+
while (version > toVersion) {
|
|
1626
|
+
const changes = await dialect.getChanges(version);
|
|
1627
|
+
if (!changes) {
|
|
1628
|
+
throw new Error(`No changes found for version ${version}`);
|
|
1629
|
+
} else if (changes.fromVersion == version) {
|
|
1630
|
+
throw new Error(`Saved changes do not change the database version (${version})`);
|
|
1631
|
+
}
|
|
1632
|
+
Logger.debug(changes.down);
|
|
1633
|
+
await db.runMultipleWriteStatements(changes.down);
|
|
1634
|
+
dbInstructions.version = changes.fromVersion;
|
|
1635
|
+
version = changes.fromVersion;
|
|
1636
|
+
}
|
|
1637
|
+
if (version < toVersion) {
|
|
1638
|
+
Logger.warn(`There was no entry for version ${toVersion}. Rolled back to version ${version}`);
|
|
1633
1639
|
}
|
|
1634
|
-
await dialect.
|
|
1640
|
+
await dialect.rollbackHistory(version);
|
|
1641
|
+
return version;
|
|
1635
1642
|
}
|
|
1636
1643
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1637
1644
|
0 && (module.exports = {
|
|
@@ -1639,9 +1646,6 @@ async function rollback(db, dbInstructions, toVersion) {
|
|
|
1639
1646
|
ForeignKey,
|
|
1640
1647
|
TableClass,
|
|
1641
1648
|
TableObj,
|
|
1642
|
-
prepareAndRunMigration,
|
|
1643
|
-
prepareMigration,
|
|
1644
1649
|
rollback,
|
|
1645
|
-
|
|
1646
|
-
runPreparedMigrations
|
|
1650
|
+
runMigration
|
|
1647
1651
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -34,7 +34,7 @@ var TableObj = class _TableObj {
|
|
|
34
34
|
static getEmptyColumnEntry(key) {
|
|
35
35
|
return {
|
|
36
36
|
name: key,
|
|
37
|
-
|
|
37
|
+
sqlType: "",
|
|
38
38
|
defaultValue: "",
|
|
39
39
|
isPrimaryKey: false
|
|
40
40
|
};
|
|
@@ -67,6 +67,7 @@ var TableObj = class _TableObj {
|
|
|
67
67
|
}
|
|
68
68
|
constructor(tableName, defaultValues) {
|
|
69
69
|
this.tableName = tableName;
|
|
70
|
+
this.tableStructure.table = tableName;
|
|
70
71
|
for (const key in defaultValues) {
|
|
71
72
|
const value = defaultValues[key];
|
|
72
73
|
const columnEntry = _TableObj.getColumnEntry(key, value);
|
|
@@ -76,6 +77,9 @@ var TableObj = class _TableObj {
|
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
primaryKey(key) {
|
|
80
|
+
if (this.tableStructure.primaryKey) {
|
|
81
|
+
this.tableStructure.columns[this.tableStructure.primaryKey].isPrimaryKey = false;
|
|
82
|
+
}
|
|
79
83
|
this.tableStructure.primaryKey = key.toString();
|
|
80
84
|
this.tableStructure.columns[key.toString()].isPrimaryKey = true;
|
|
81
85
|
return this;
|
|
@@ -157,8 +161,8 @@ var TableStructureGenerator = class {
|
|
|
157
161
|
}
|
|
158
162
|
for (const key in structure.columns) {
|
|
159
163
|
const entry = structure.columns[key];
|
|
160
|
-
entry.defaultValue = entry.inputDefaultValue === null ? this.dialect.
|
|
161
|
-
entry.
|
|
164
|
+
entry.defaultValue = entry.inputDefaultValue === null ? this.dialect.nullType : this.dialect.formatValueToSql(entry.inputDefaultValue, entry.inputType);
|
|
165
|
+
entry.sqlType = this.dialect.getSqlType(entry.inputType, entry);
|
|
162
166
|
}
|
|
163
167
|
}
|
|
164
168
|
return tables;
|
|
@@ -476,6 +480,7 @@ var Migrations = class {
|
|
|
476
480
|
const entry = this.getEntry(table);
|
|
477
481
|
entry.recreate = true;
|
|
478
482
|
Logger.log(`Table ${this.getTableName(table)} will be recreated!`);
|
|
483
|
+
this.allowMigration(version, table, "recreateTable");
|
|
479
484
|
}
|
|
480
485
|
};
|
|
481
486
|
|
|
@@ -575,17 +580,19 @@ ${foreignKeyOnQuery}`;
|
|
|
575
580
|
changes.down += `
|
|
576
581
|
|
|
577
582
|
${foreignKeyOnQuery}`;
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
583
|
+
if (fromVersion != 0) {
|
|
584
|
+
const exception = this.migrations.verifyRenamingTasks(this.newTables) ?? this.migrations.verifyAllowedMigrations();
|
|
585
|
+
if (exception) {
|
|
586
|
+
Logger.debug(`
|
|
581
587
|
Canceled changes:
|
|
582
588
|
${changes.up}
|
|
583
589
|
`);
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
590
|
+
if (dbInstructions.throwIfNotAllowed)
|
|
591
|
+
throw exception;
|
|
592
|
+
else {
|
|
593
|
+
console.error(exception.message);
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
589
596
|
}
|
|
590
597
|
}
|
|
591
598
|
return {
|
|
@@ -660,7 +667,7 @@ ${changes.up}
|
|
|
660
667
|
const queryLines = [];
|
|
661
668
|
for (const key in columns) {
|
|
662
669
|
const columnInfo = columns[key];
|
|
663
|
-
queryLines.push(this.dialect.columnDefinition(columnInfo.name, columnInfo.
|
|
670
|
+
queryLines.push(this.dialect.columnDefinition(columnInfo.name, columnInfo.sqlType, columnInfo.isPrimaryKey, columnInfo.defaultValue));
|
|
664
671
|
}
|
|
665
672
|
if (foreignKeys) {
|
|
666
673
|
for (const foreignKeyInfo of foreignKeys) {
|
|
@@ -824,13 +831,17 @@ ${changes.up}
|
|
|
824
831
|
this.migrations.internalRecreate(newTableDefinition.table, "Dialect does not support altering primary keys");
|
|
825
832
|
continue;
|
|
826
833
|
}
|
|
834
|
+
const renamedColumns = this.migrations.getMigrationData()[tableName]?.renamedColumns;
|
|
827
835
|
for (const key in newColumnList) {
|
|
828
836
|
const newColumn = newColumnList[key];
|
|
829
837
|
const oldColumn = oldColumnNewNameIndex[newColumn.name];
|
|
838
|
+
const willBeRenamed = renamedColumns?.find((entry) => entry.newName);
|
|
839
|
+
if (willBeRenamed)
|
|
840
|
+
continue;
|
|
830
841
|
if (oldColumn == void 0) {
|
|
831
|
-
changes.up += this.dialect.createColumn(tableName, this.dialect.columnDefinition(newColumn.name, newColumn.
|
|
842
|
+
changes.up += this.dialect.createColumn(tableName, this.dialect.columnDefinition(newColumn.name, newColumn.sqlType, newColumn.isPrimaryKey, newColumn.defaultValue)) + "\n";
|
|
832
843
|
changes.down += this.dialect.dropColumn(tableName, newColumn.name) + "\n";
|
|
833
|
-
} else if (newColumn.
|
|
844
|
+
} else if (newColumn.sqlType != oldColumn.sqlType || newColumn.defaultValue != oldColumn.defaultValue) {
|
|
834
845
|
this.migrations.internalRecreate(newTableDefinition.table, "Table structure was changed");
|
|
835
846
|
}
|
|
836
847
|
}
|
|
@@ -840,7 +851,7 @@ ${changes.up}
|
|
|
840
851
|
if (newColumn == void 0) {
|
|
841
852
|
this.migrations.compareWithAllowedMigration(tableName, "dropColumn");
|
|
842
853
|
changes.up += this.dialect.dropColumn(tableName, oldColumn.name) + "\n";
|
|
843
|
-
changes.down += this.dialect.createColumn(tableName, this.dialect.columnDefinition(oldColumn.name, oldColumn.
|
|
854
|
+
changes.down += this.dialect.createColumn(tableName, this.dialect.columnDefinition(oldColumn.name, oldColumn.sqlType, oldColumn.isPrimaryKey, oldColumn.defaultValue)) + "\n";
|
|
844
855
|
}
|
|
845
856
|
}
|
|
846
857
|
}
|
|
@@ -882,64 +893,6 @@ ${changes.up}
|
|
|
882
893
|
}
|
|
883
894
|
};
|
|
884
895
|
|
|
885
|
-
// src/lib/MigrationHistoryManager.ts
|
|
886
|
-
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
887
|
-
import { readFileSync } from "fs";
|
|
888
|
-
var FILENAME_UP_PREFIX = "up_to_";
|
|
889
|
-
var FILENAME_DOWN_PREFIX = "down_to_";
|
|
890
|
-
var MigrationHistoryManager = class {
|
|
891
|
-
configPath;
|
|
892
|
-
migrationsPath;
|
|
893
|
-
constructor(configPath) {
|
|
894
|
-
this.configPath = configPath;
|
|
895
|
-
this.migrationsPath = `${configPath}/migrations`;
|
|
896
|
-
mkdirSync(this.configPath, { recursive: true });
|
|
897
|
-
mkdirSync(this.migrationsPath, { recursive: true });
|
|
898
|
-
}
|
|
899
|
-
/**
|
|
900
|
-
* Creates migration history files for upgrading and downgrading database schemas.
|
|
901
|
-
*
|
|
902
|
-
* @param fromVersion - The current version number of the database.
|
|
903
|
-
* @param toVersion - The target version number for the migration.
|
|
904
|
-
* @param changes - An object containing the SQL changes for upgrading (`changes.up`) and downgrading (`changes.down`).
|
|
905
|
-
* @param overwriteExisting - A flag that determines if existing migration files should be overwritten. Defaults to `false`.
|
|
906
|
-
*/
|
|
907
|
-
createMigrationHistory(fromVersion, toVersion, changes, overwriteExisting) {
|
|
908
|
-
const upPath = `${this.migrationsPath}/${FILENAME_UP_PREFIX}${toVersion}.sql`;
|
|
909
|
-
const downPath = `${this.migrationsPath}/${FILENAME_DOWN_PREFIX}${fromVersion}.sql`;
|
|
910
|
-
if (existsSync(upPath) && !overwriteExisting)
|
|
911
|
-
throw new Error(`Migration ${upPath} already exists!`);
|
|
912
|
-
else if (existsSync(downPath) && !overwriteExisting)
|
|
913
|
-
throw new Error(`Migration ${downPath} already exists!`);
|
|
914
|
-
Logger.log(`Save migrations to ${this.migrationsPath}`);
|
|
915
|
-
writeFileSync(`${this.migrationsPath}/${FILENAME_UP_PREFIX}${toVersion}.sql`, `-- From: Version ${fromVersion}
|
|
916
|
-
-- To: Version ${toVersion}
|
|
917
|
-
${changes.up}`, { encoding: "utf-8" });
|
|
918
|
-
writeFileSync(`${this.migrationsPath}/${FILENAME_DOWN_PREFIX}${fromVersion}.sql`, `-- From: Version ${toVersion}
|
|
919
|
-
-- To: Version ${fromVersion}
|
|
920
|
-
${changes.down}`, { encoding: "utf-8" });
|
|
921
|
-
}
|
|
922
|
-
/**
|
|
923
|
-
* Retrieves the SQL migration script for upgrading to the specified version.
|
|
924
|
-
* If no version is provided, it retrieves the migration script for the latest version in the history.
|
|
925
|
-
*
|
|
926
|
-
* @param toVersion - The version of the migration script to retrieve. If not specified, the latest version is used.
|
|
927
|
-
* @return The content of the SQL migration script as a string.
|
|
928
|
-
*/
|
|
929
|
-
getUpMigration(toVersion) {
|
|
930
|
-
return readFileSync(`${this.migrationsPath}/${FILENAME_UP_PREFIX}${toVersion}.sql`, { encoding: "utf-8" });
|
|
931
|
-
}
|
|
932
|
-
/**
|
|
933
|
-
* Retrieves the SQL migration script for downgrading to the specified version.
|
|
934
|
-
*
|
|
935
|
-
* @param version - The version number of the migration to retrieve.
|
|
936
|
-
* @return The SQL script for the down migration.
|
|
937
|
-
*/
|
|
938
|
-
getDownMigration(version) {
|
|
939
|
-
return readFileSync(`${this.migrationsPath}/${FILENAME_DOWN_PREFIX}${version}.sql`, { encoding: "utf-8" });
|
|
940
|
-
}
|
|
941
|
-
};
|
|
942
|
-
|
|
943
896
|
// src/lib/tableInfo/decorators/TableClass.ts
|
|
944
897
|
import "polyfill-symbol-metadata";
|
|
945
898
|
function TableClass(tableName, primaryKey) {
|
|
@@ -1001,6 +954,7 @@ function ForeignKey(toTable, toColumn, options) {
|
|
|
1001
954
|
}
|
|
1002
955
|
|
|
1003
956
|
// src/lib/dialects/DefaultSql.ts
|
|
957
|
+
import escapeString from "escape-sql-string";
|
|
1004
958
|
var MIGRATION_DATA_TABLE_NAME = "__sqlmorpheus_migrations";
|
|
1005
959
|
var DefaultSql = class {
|
|
1006
960
|
db;
|
|
@@ -1009,15 +963,16 @@ var DefaultSql = class {
|
|
|
1009
963
|
canInspectForeignKeys = false;
|
|
1010
964
|
canInspectPrimaryKey = false;
|
|
1011
965
|
types = {
|
|
966
|
+
text: "TEXT",
|
|
1012
967
|
string: "TEXT",
|
|
1013
968
|
number: "INTEGER",
|
|
1014
969
|
bigint: "BIGINT",
|
|
1015
970
|
boolean: "BOOLEAN",
|
|
1016
971
|
date: "DATE",
|
|
1017
972
|
time: "TIME",
|
|
1018
|
-
dateTime: "DATETIME"
|
|
1019
|
-
null: "NULL"
|
|
973
|
+
dateTime: "DATETIME"
|
|
1020
974
|
};
|
|
975
|
+
nullType = "NULL";
|
|
1021
976
|
constructor(db) {
|
|
1022
977
|
this.db = db;
|
|
1023
978
|
}
|
|
@@ -1033,6 +988,7 @@ var DefaultSql = class {
|
|
|
1033
988
|
return value2 instanceof Date ? value2 : new Date(value2);
|
|
1034
989
|
}
|
|
1035
990
|
switch (type) {
|
|
991
|
+
case "text":
|
|
1036
992
|
case "string":
|
|
1037
993
|
return `"${value}"`;
|
|
1038
994
|
case "time":
|
|
@@ -1202,12 +1158,17 @@ var DefaultSql = class {
|
|
|
1202
1158
|
* @param tableName - The name of the database table to select data from.
|
|
1203
1159
|
* @param select - An array of column names to include in the SELECT clause.
|
|
1204
1160
|
* @param where - An optional condition.
|
|
1161
|
+
* @param orderBy - Optional. the column to sort by
|
|
1205
1162
|
* @return The SQL statement as a string.
|
|
1206
1163
|
*/
|
|
1207
|
-
select(tableName, select, where) {
|
|
1164
|
+
select(tableName, select, where, orderBy) {
|
|
1208
1165
|
let query = `SELECT ${select.join(",")} FROM ${tableName}`;
|
|
1209
|
-
if (where)
|
|
1166
|
+
if (where) {
|
|
1210
1167
|
query += ` WHERE ${where}`;
|
|
1168
|
+
}
|
|
1169
|
+
if (orderBy) {
|
|
1170
|
+
query += ` ORDER BY ${orderBy}`;
|
|
1171
|
+
}
|
|
1211
1172
|
return `${query};`;
|
|
1212
1173
|
}
|
|
1213
1174
|
/**
|
|
@@ -1239,7 +1200,10 @@ var DefaultSql = class {
|
|
|
1239
1200
|
*/
|
|
1240
1201
|
migrationTableQuery() {
|
|
1241
1202
|
return this.createTable(MIGRATION_DATA_TABLE_NAME, [
|
|
1242
|
-
this.columnDefinition("
|
|
1203
|
+
this.columnDefinition("fromVersion", this.getSqlType("number"), false, "0"),
|
|
1204
|
+
this.columnDefinition("toVersion", this.getSqlType("number"), true, "0"),
|
|
1205
|
+
this.columnDefinition("upChanges", this.getSqlType("text"), false),
|
|
1206
|
+
this.columnDefinition("downChanges", this.getSqlType("text"), false)
|
|
1243
1207
|
]);
|
|
1244
1208
|
}
|
|
1245
1209
|
/**
|
|
@@ -1268,23 +1232,44 @@ var DefaultSql = class {
|
|
|
1268
1232
|
*/
|
|
1269
1233
|
async getVersion() {
|
|
1270
1234
|
await this.createMigrationTableIfNeeded();
|
|
1271
|
-
const query = this.select(MIGRATION_DATA_TABLE_NAME, ["
|
|
1235
|
+
const query = this.select(MIGRATION_DATA_TABLE_NAME, ["toVersion"], void 0, "toVersion DESC");
|
|
1272
1236
|
const data = await this.db.runGetStatement(query);
|
|
1273
|
-
return data.length ? data[0].
|
|
1237
|
+
return data.length ? data[0].toVersion : 0;
|
|
1274
1238
|
}
|
|
1275
1239
|
/**
|
|
1276
1240
|
* Updates the migration version in the database. If the version entry does not exist, it inserts a new one;
|
|
1277
1241
|
* if it exists, it updates the version to the specified value.
|
|
1278
1242
|
*
|
|
1279
|
-
* @param
|
|
1243
|
+
* @param toVersion - The new migration version to set in the database.
|
|
1280
1244
|
* @return A promise that resolves when the operation is complete.
|
|
1281
1245
|
*/
|
|
1282
|
-
async
|
|
1283
|
-
const query = `${this.migrationTableQuery()};
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1246
|
+
async rollbackHistory(toVersion) {
|
|
1247
|
+
const query = `${this.migrationTableQuery()}; DELETE FROM ${MIGRATION_DATA_TABLE_NAME} WHERE toVersion > ${toVersion};`;
|
|
1248
|
+
await this.db.runMultipleWriteStatements(query);
|
|
1249
|
+
}
|
|
1250
|
+
async getChanges(version) {
|
|
1251
|
+
await this.createMigrationTableIfNeeded();
|
|
1252
|
+
const query = this.select(MIGRATION_DATA_TABLE_NAME, ["fromVersion", "upChanges", "downChanges"], `toVersion = ${version}`);
|
|
1253
|
+
const data = await this.db.runGetStatement(query);
|
|
1254
|
+
return data ? { up: data[0].upChanges, down: data[0].downChanges, fromVersion: data[0].fromVersion } : null;
|
|
1255
|
+
}
|
|
1256
|
+
async hasChanges(version) {
|
|
1257
|
+
await this.createMigrationTableIfNeeded();
|
|
1258
|
+
const query = this.select(MIGRATION_DATA_TABLE_NAME, ["toVersion"], `toVersion = ${version}`);
|
|
1259
|
+
const data = await this.db.runGetStatement(query);
|
|
1260
|
+
return !!data.length;
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* Saves the change history to the SQLMorpheus table in the database
|
|
1264
|
+
* Since we save SQL queries in the database, we need to make sure that they are properly escaped.
|
|
1265
|
+
* @param fromVersion
|
|
1266
|
+
* @param toVersion
|
|
1267
|
+
* @param changes
|
|
1268
|
+
*/
|
|
1269
|
+
async setChanges(fromVersion, toVersion, changes) {
|
|
1270
|
+
const downChanges = escapeString(changes.down);
|
|
1271
|
+
const upChanges = escapeString(changes.up);
|
|
1272
|
+
const query = `${this.migrationTableQuery()} ${await this.hasChanges(toVersion) ? `UPDATE ${MIGRATION_DATA_TABLE_NAME} SET fromVersion = '${fromVersion}', upChanges = ${upChanges}, downChanges = ${downChanges} WHERE toVersion = '${toVersion}';` : `INSERT INTO ${MIGRATION_DATA_TABLE_NAME} (fromVersion, toVersion, upChanges, downChanges) VALUES ('${fromVersion}', '${toVersion}', ${upChanges}, ${downChanges})`}`;
|
|
1288
1273
|
await this.db.runMultipleWriteStatements(query);
|
|
1289
1274
|
}
|
|
1290
1275
|
};
|
|
@@ -1292,14 +1277,14 @@ UPDATE ${MIGRATION_DATA_TABLE_NAME} SET version = ${newVersion} WHERE EXISTS (SE
|
|
|
1292
1277
|
// src/lib/dialects/SqliteDialect.ts
|
|
1293
1278
|
var SqliteDialect = class extends DefaultSql {
|
|
1294
1279
|
types = {
|
|
1280
|
+
text: "TEXT",
|
|
1295
1281
|
string: "TEXT",
|
|
1296
1282
|
number: "INTEGER",
|
|
1297
1283
|
bigint: "INTEGER",
|
|
1298
1284
|
boolean: "INTEGER",
|
|
1299
1285
|
date: "INTEGER",
|
|
1300
1286
|
time: "INTEGER",
|
|
1301
|
-
dateTime: "INTEGER"
|
|
1302
|
-
null: "NULL"
|
|
1287
|
+
dateTime: "INTEGER"
|
|
1303
1288
|
};
|
|
1304
1289
|
canAlterForeignKeys = false;
|
|
1305
1290
|
canAlterPrimaryKey = false;
|
|
@@ -1317,8 +1302,8 @@ var SqliteDialect = class extends DefaultSql {
|
|
|
1317
1302
|
for (const entry of data) {
|
|
1318
1303
|
output[entry["name"]] = {
|
|
1319
1304
|
name: entry["name"],
|
|
1320
|
-
|
|
1321
|
-
defaultValue: entry["dflt_value"],
|
|
1305
|
+
sqlType: entry["type"],
|
|
1306
|
+
defaultValue: entry["dflt_value"] ?? this.nullType,
|
|
1322
1307
|
isPrimaryKey: entry["pk"] == "1"
|
|
1323
1308
|
};
|
|
1324
1309
|
}
|
|
@@ -1352,12 +1337,17 @@ var SqliteDialect = class extends DefaultSql {
|
|
|
1352
1337
|
};
|
|
1353
1338
|
});
|
|
1354
1339
|
}
|
|
1340
|
+
async setChanges(fromVersion, toVersion, changes) {
|
|
1341
|
+
await this.db.runMultipleWriteStatements(`PRAGMA user_version = ${toVersion};`);
|
|
1342
|
+
await super.setChanges(fromVersion, toVersion, changes);
|
|
1343
|
+
}
|
|
1355
1344
|
async getVersion() {
|
|
1356
1345
|
const output = await this.db.runGetStatement(`PRAGMA user_version;`);
|
|
1357
1346
|
return output[0].user_version;
|
|
1358
1347
|
}
|
|
1359
|
-
async
|
|
1360
|
-
await this.db.runMultipleWriteStatements(`PRAGMA user_version = ${
|
|
1348
|
+
async rollbackHistory(toVersion) {
|
|
1349
|
+
await this.db.runMultipleWriteStatements(`PRAGMA user_version = ${toVersion};`);
|
|
1350
|
+
await super.rollbackHistory(toVersion);
|
|
1361
1351
|
}
|
|
1362
1352
|
};
|
|
1363
1353
|
|
|
@@ -1385,7 +1375,7 @@ var PostgresDialect = class extends DefaultSql {
|
|
|
1385
1375
|
for (const entry of data) {
|
|
1386
1376
|
output[entry["name"]] = {
|
|
1387
1377
|
name: entry["column_name"],
|
|
1388
|
-
|
|
1378
|
+
sqlType: entry["data_type"],
|
|
1389
1379
|
maxLength: 0,
|
|
1390
1380
|
defaultValue: entry["column_default"],
|
|
1391
1381
|
isPrimaryKey: false
|
|
@@ -1444,7 +1434,7 @@ var MsSqlDialect = class extends DefaultSql {
|
|
|
1444
1434
|
for (const entry of data) {
|
|
1445
1435
|
output[entry["name"]] = {
|
|
1446
1436
|
name: entry["name"],
|
|
1447
|
-
|
|
1437
|
+
sqlType: entry["DATA_TYPE"],
|
|
1448
1438
|
maxLength: 0,
|
|
1449
1439
|
defaultValue: entry["COLUMN_DEFAULT"],
|
|
1450
1440
|
isPrimaryKey: entry["pk"] == "1"
|
|
@@ -1461,19 +1451,27 @@ var MySqlDialect = class extends DefaultSql {
|
|
|
1461
1451
|
canInspectForeignKeys = true;
|
|
1462
1452
|
canInspectPrimaryKey = true;
|
|
1463
1453
|
types = {
|
|
1454
|
+
text: "TEXT",
|
|
1464
1455
|
string: "VARCHAR",
|
|
1465
1456
|
number: "INT",
|
|
1466
1457
|
bigint: "BIGINT",
|
|
1467
|
-
boolean: "
|
|
1458
|
+
boolean: "TINYINT(1)",
|
|
1468
1459
|
date: "DATE",
|
|
1469
1460
|
time: "TIME",
|
|
1470
|
-
dateTime: "DATETIME"
|
|
1471
|
-
null: "NULL"
|
|
1461
|
+
dateTime: "DATETIME"
|
|
1472
1462
|
};
|
|
1463
|
+
formatValueToSql(value, type) {
|
|
1464
|
+
switch (type) {
|
|
1465
|
+
case "boolean":
|
|
1466
|
+
return value ? "1" : "0";
|
|
1467
|
+
default:
|
|
1468
|
+
return super.formatValueToSql(value, type);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1473
1471
|
getSqlType(dataType, columnInfo) {
|
|
1474
1472
|
switch (dataType) {
|
|
1475
1473
|
case "string":
|
|
1476
|
-
return this.types[dataType] + `(${columnInfo?.maxLength})`;
|
|
1474
|
+
return this.types[dataType] + `(${columnInfo?.maxLength ?? 100})`;
|
|
1477
1475
|
default:
|
|
1478
1476
|
return super.getSqlType(dataType, columnInfo);
|
|
1479
1477
|
}
|
|
@@ -1494,31 +1492,55 @@ var MySqlDialect = class extends DefaultSql {
|
|
|
1494
1492
|
return `ALTER TABLE ${tableName} PRIMARY KEY;`;
|
|
1495
1493
|
}
|
|
1496
1494
|
async getForeignKeys(tableName) {
|
|
1497
|
-
const
|
|
1495
|
+
const data3 = await this.db.runGetStatement(
|
|
1496
|
+
`SELECT
|
|
1497
|
+
*
|
|
1498
|
+
FROM information_schema.KEY_COLUMN_USAGE AS info
|
|
1499
|
+
INNER JOIN information_schema.REFERENTIAL_CONSTRAINTS AS constr ON info.CONSTRAINT_NAME = constr.CONSTRAINT_NAME
|
|
1500
|
+
WHERE info.REFERENCED_TABLE_SCHEMA = (SELECT DATABASE()) AND info.TABLE_NAME = '${tableName}';`
|
|
1501
|
+
);
|
|
1502
|
+
const data = await this.db.runGetStatement(
|
|
1503
|
+
`SELECT
|
|
1504
|
+
info.TABLE_NAME, info.COLUMN_NAME, info.REFERENCED_TABLE_NAME, info.REFERENCED_COLUMN_NAME, constr.UPDATE_RULE, constr.DELETE_RULE
|
|
1505
|
+
FROM information_schema.KEY_COLUMN_USAGE AS info INNER JOIN information_schema.REFERENTIAL_CONSTRAINTS AS constr ON info.CONSTRAINT_NAME = constr.CONSTRAINT_NAME
|
|
1506
|
+
WHERE info.REFERENCED_TABLE_SCHEMA = (SELECT DATABASE()) AND info.TABLE_NAME = '${tableName}';`
|
|
1507
|
+
);
|
|
1498
1508
|
return data.map((entry) => {
|
|
1499
1509
|
return {
|
|
1500
1510
|
fromTable: tableName,
|
|
1501
1511
|
fromColumn: entry["COLUMN_NAME"],
|
|
1502
1512
|
toTable: entry["REFERENCED_TABLE_NAME"],
|
|
1503
|
-
toColumn: entry["REFERENCED_COLUMN_NAME"]
|
|
1513
|
+
toColumn: entry["REFERENCED_COLUMN_NAME"],
|
|
1514
|
+
onUpdate: entry["UPDATE_RULE"],
|
|
1515
|
+
onDelete: entry["DELETE_RULE"]
|
|
1504
1516
|
};
|
|
1505
1517
|
});
|
|
1506
1518
|
}
|
|
1507
1519
|
async getTableNames() {
|
|
1508
|
-
const
|
|
1509
|
-
return
|
|
1520
|
+
const entries = await this.db.runGetStatement("SHOW TABLES");
|
|
1521
|
+
return entries.map((entry) => Object.values(entry)[0]);
|
|
1510
1522
|
}
|
|
1511
1523
|
async getColumnInformation(tableName) {
|
|
1512
1524
|
const data = await this.db.runGetStatement(`SHOW COLUMNS FROM ${tableName};`);
|
|
1513
1525
|
const output = {};
|
|
1514
1526
|
for (const entry of data) {
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
isPrimaryKey: entry["pk"] == "1"
|
|
1527
|
+
const sqlType = entry["Type"].toUpperCase();
|
|
1528
|
+
const info = {
|
|
1529
|
+
name: entry["Field"],
|
|
1530
|
+
sqlType,
|
|
1531
|
+
isPrimaryKey: entry["Key"] == "PRI"
|
|
1521
1532
|
};
|
|
1533
|
+
switch (sqlType.substring(0, 4)) {
|
|
1534
|
+
case "TEXT":
|
|
1535
|
+
case "VARC":
|
|
1536
|
+
case "DATE":
|
|
1537
|
+
case "TIME":
|
|
1538
|
+
info.defaultValue = entry["Default"] ? `"${entry["Default"]}"` : this.nullType;
|
|
1539
|
+
break;
|
|
1540
|
+
default:
|
|
1541
|
+
info.defaultValue = entry["Default"] ?? this.nullType;
|
|
1542
|
+
}
|
|
1543
|
+
output[entry["Field"]] = info;
|
|
1522
1544
|
}
|
|
1523
1545
|
return output;
|
|
1524
1546
|
}
|
|
@@ -1541,45 +1563,13 @@ function getDialect(db, dbInstructions) {
|
|
|
1541
1563
|
}
|
|
1542
1564
|
|
|
1543
1565
|
// src/index.ts
|
|
1544
|
-
async function
|
|
1545
|
-
Logger.setMode(dbInstructions.loggerMode);
|
|
1546
|
-
const dialect = getDialect(db, dbInstructions);
|
|
1547
|
-
const mm = new MigrationManager(dialect);
|
|
1548
|
-
const migration = await mm.generateSqlChanges(dbInstructions);
|
|
1549
|
-
if (!migration)
|
|
1550
|
-
return;
|
|
1551
|
-
const migrationHistoryManager = new MigrationHistoryManager(dbInstructions.configPath);
|
|
1552
|
-
migrationHistoryManager.createMigrationHistory(migration.fromVersion, dbInstructions.version, migration.changes, overwriteExisting);
|
|
1553
|
-
}
|
|
1554
|
-
async function runPreparedMigrations(db, dbInstructions) {
|
|
1555
|
-
Logger.setMode(dbInstructions.loggerMode);
|
|
1556
|
-
const historyManager = new MigrationHistoryManager(dbInstructions.configPath);
|
|
1557
|
-
const dialect = getDialect(db, dbInstructions);
|
|
1558
|
-
const fromVersion = await dialect.getVersion();
|
|
1559
|
-
const toVersion = dbInstructions.version;
|
|
1560
|
-
if (fromVersion == toVersion)
|
|
1561
|
-
return;
|
|
1562
|
-
await db.createBackup?.(`from_${fromVersion}_to_${dbInstructions.version}`);
|
|
1563
|
-
Logger.log(`Running migrations from ${fromVersion} to ${toVersion}`);
|
|
1564
|
-
for (let i = fromVersion ? fromVersion + 1 : toVersion; i <= toVersion; ++i) {
|
|
1565
|
-
const upChanges = historyManager.getUpMigration(i);
|
|
1566
|
-
Logger.debug(upChanges);
|
|
1567
|
-
await db.runMultipleWriteStatements(upChanges);
|
|
1568
|
-
dbInstructions.version = i;
|
|
1569
|
-
}
|
|
1570
|
-
await dialect.setVersion(toVersion);
|
|
1571
|
-
}
|
|
1572
|
-
async function prepareAndRunMigration(db, instructions, overwriteExisting) {
|
|
1573
|
-
Logger.setMode(instructions.loggerMode);
|
|
1574
|
-
await prepareMigration(db, instructions, overwriteExisting);
|
|
1575
|
-
await runPreparedMigrations(db, instructions);
|
|
1576
|
-
}
|
|
1577
|
-
async function runMigrationWithoutHistory(db, dbInstructions) {
|
|
1566
|
+
async function runMigration(db, dbInstructions) {
|
|
1578
1567
|
Logger.setMode(dbInstructions.loggerMode);
|
|
1579
1568
|
const dialect = getDialect(db, dbInstructions);
|
|
1580
1569
|
const mm = new MigrationManager(dialect);
|
|
1581
1570
|
const migration = await mm.generateSqlChanges(dbInstructions);
|
|
1582
1571
|
if (migration) {
|
|
1572
|
+
await dialect.setChanges(migration.fromVersion, dbInstructions.version, migration.changes);
|
|
1583
1573
|
await db.createBackup?.(`from_${migration.fromVersion}_to_${dbInstructions.version}`);
|
|
1584
1574
|
await db.runMultipleWriteStatements(migration.changes.up);
|
|
1585
1575
|
}
|
|
@@ -1588,25 +1578,32 @@ async function rollback(db, dbInstructions, toVersion) {
|
|
|
1588
1578
|
Logger.setMode(dbInstructions.loggerMode);
|
|
1589
1579
|
const dialect = getDialect(db, dbInstructions);
|
|
1590
1580
|
const fromVersion = await dialect.getVersion();
|
|
1591
|
-
const historyManager = new MigrationHistoryManager(dbInstructions.configPath);
|
|
1592
1581
|
await db.createBackup?.(`from_${fromVersion}_to_${dbInstructions.version}`);
|
|
1593
1582
|
Logger.log(`Rolling back from ${fromVersion} to ${toVersion}`);
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1583
|
+
let version = fromVersion;
|
|
1584
|
+
while (version > toVersion) {
|
|
1585
|
+
const changes = await dialect.getChanges(version);
|
|
1586
|
+
if (!changes) {
|
|
1587
|
+
throw new Error(`No changes found for version ${version}`);
|
|
1588
|
+
} else if (changes.fromVersion == version) {
|
|
1589
|
+
throw new Error(`Saved changes do not change the database version (${version})`);
|
|
1590
|
+
}
|
|
1591
|
+
Logger.debug(changes.down);
|
|
1592
|
+
await db.runMultipleWriteStatements(changes.down);
|
|
1593
|
+
dbInstructions.version = changes.fromVersion;
|
|
1594
|
+
version = changes.fromVersion;
|
|
1595
|
+
}
|
|
1596
|
+
if (version < toVersion) {
|
|
1597
|
+
Logger.warn(`There was no entry for version ${toVersion}. Rolled back to version ${version}`);
|
|
1599
1598
|
}
|
|
1600
|
-
await dialect.
|
|
1599
|
+
await dialect.rollbackHistory(version);
|
|
1600
|
+
return version;
|
|
1601
1601
|
}
|
|
1602
1602
|
export {
|
|
1603
1603
|
DataType,
|
|
1604
1604
|
ForeignKey,
|
|
1605
1605
|
TableClass,
|
|
1606
1606
|
TableObj,
|
|
1607
|
-
prepareAndRunMigration,
|
|
1608
|
-
prepareMigration,
|
|
1609
1607
|
rollback,
|
|
1610
|
-
|
|
1611
|
-
runPreparedMigrations
|
|
1608
|
+
runMigration
|
|
1612
1609
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sqlmorpheus",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"author": "JodliDev",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,14 +26,17 @@
|
|
|
26
26
|
"build_and_publish": "npm run build && npm publish"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
+
"@testcontainers/mysql": "^11.7.1",
|
|
29
30
|
"@types/better-sqlite3": "^7.6.13",
|
|
30
31
|
"@types/node": "^24.0.13",
|
|
31
32
|
"better-sqlite3": "^12.2.0",
|
|
33
|
+
"mysql2": "^3.15.2",
|
|
32
34
|
"tsup": "^8.5.0",
|
|
33
35
|
"typescript": "^5.8.3",
|
|
34
36
|
"vitest": "^3.2.4"
|
|
35
37
|
},
|
|
36
38
|
"dependencies": {
|
|
39
|
+
"escape-sql-string": "^1.2.2",
|
|
37
40
|
"polyfill-symbol-metadata": "^0.0.2"
|
|
38
41
|
}
|
|
39
42
|
}
|