sqlmorpheus 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/index.d.mts +226 -28
- package/dist/index.d.ts +226 -28
- package/dist/index.js +665 -194
- package/dist/index.mjs +661 -192
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20,8 +20,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
|
|
23
|
+
DataType: () => DataType,
|
|
24
24
|
ForeignKey: () => ForeignKey,
|
|
25
|
+
TableClass: () => TableClass,
|
|
26
|
+
TableObj: () => TableObj,
|
|
25
27
|
prepareAndRunMigration: () => prepareAndRunMigration,
|
|
26
28
|
prepareMigration: () => prepareMigration,
|
|
27
29
|
rollback: () => rollback,
|
|
@@ -53,19 +55,110 @@ var LoggerClass = class {
|
|
|
53
55
|
};
|
|
54
56
|
var Logger = new LoggerClass();
|
|
55
57
|
|
|
56
|
-
// src/lib/tableInfo/
|
|
58
|
+
// src/lib/tableInfo/TableObj.ts
|
|
59
|
+
var TableObj = class _TableObj {
|
|
60
|
+
IS_TABLE_OBJ = true;
|
|
61
|
+
tableName;
|
|
62
|
+
tableStructure = {
|
|
63
|
+
table: "",
|
|
64
|
+
primaryKey: void 0,
|
|
65
|
+
columns: {},
|
|
66
|
+
foreignKeys: []
|
|
67
|
+
};
|
|
68
|
+
static getEmptyColumnEntry(key) {
|
|
69
|
+
return {
|
|
70
|
+
name: key,
|
|
71
|
+
type: "",
|
|
72
|
+
defaultValue: "",
|
|
73
|
+
isPrimaryKey: false
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
static getColumnEntry(key, value) {
|
|
77
|
+
let dataType = typeof value;
|
|
78
|
+
switch (typeof value) {
|
|
79
|
+
case "function":
|
|
80
|
+
return null;
|
|
81
|
+
case "object":
|
|
82
|
+
if (value instanceof Date) {
|
|
83
|
+
dataType = "date";
|
|
84
|
+
break;
|
|
85
|
+
} else {
|
|
86
|
+
Logger.warn(`${key} was skipped because its type (${typeof value}) is not supported`);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
case "undefined":
|
|
90
|
+
case "symbol":
|
|
91
|
+
Logger.warn(`${key} was skipped because its type (${typeof value}) is not supported`);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
..._TableObj.getEmptyColumnEntry(key),
|
|
96
|
+
inputType: dataType,
|
|
97
|
+
inputDefaultValue: value
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
constructor(tableName, defaultValues) {
|
|
101
|
+
this.tableName = tableName;
|
|
102
|
+
for (const key in defaultValues) {
|
|
103
|
+
const value = defaultValues[key];
|
|
104
|
+
const columnEntry = _TableObj.getColumnEntry(key, value);
|
|
105
|
+
if (!columnEntry)
|
|
106
|
+
continue;
|
|
107
|
+
this.tableStructure.columns[key] = columnEntry;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
primaryKey(key) {
|
|
111
|
+
this.tableStructure.primaryKey = key.toString();
|
|
112
|
+
this.tableStructure.columns[key.toString()].isPrimaryKey = true;
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
foreignKey(fromColumn, toTable, toColumn, options) {
|
|
116
|
+
this.tableStructure.foreignKeys.push({
|
|
117
|
+
fromTable: this.tableName,
|
|
118
|
+
fromColumn: fromColumn.toString(),
|
|
119
|
+
toTable: toTable.tableName,
|
|
120
|
+
toColumn: toColumn.toString(),
|
|
121
|
+
onUpdate: options?.onUpdate,
|
|
122
|
+
onDelete: options?.onDelete
|
|
123
|
+
});
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
dataType(column, type) {
|
|
127
|
+
this.tableStructure.columns[column.toString()].type = type;
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
maxCharacterLength(column, max) {
|
|
131
|
+
this.tableStructure.columns[column.toString()].maxLength = max;
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
static create(tableName, columns) {
|
|
135
|
+
return new _TableObj(tableName, columns);
|
|
136
|
+
}
|
|
137
|
+
static isTableObj(obj) {
|
|
138
|
+
return !!obj?.IS_TABLE_OBJ;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// src/lib/tableInfo/getTableStructure.ts
|
|
57
143
|
var TABLE_INFO_PROPERTY_NAME = Symbol("tableInfo");
|
|
58
|
-
function
|
|
144
|
+
function getInputColumnInfo(table, context) {
|
|
145
|
+
const metadata = context?.metadata ? getTableStructureFromMetadata(context.metadata) : getTableStructure(table);
|
|
146
|
+
const key = context.name ?? context;
|
|
147
|
+
if (!metadata.columns.hasOwnProperty(key))
|
|
148
|
+
metadata.columns[key] = TableObj.getEmptyColumnEntry(key);
|
|
149
|
+
return metadata.columns[key];
|
|
150
|
+
}
|
|
151
|
+
function getTableStructure(table) {
|
|
59
152
|
let metadata = table[Symbol.metadata] ?? table.prototype?.[Symbol.metadata];
|
|
60
153
|
if (!metadata) {
|
|
61
154
|
metadata = {};
|
|
62
155
|
table[Symbol.metadata] = metadata;
|
|
63
156
|
}
|
|
64
|
-
return
|
|
157
|
+
return getTableStructureFromMetadata(metadata);
|
|
65
158
|
}
|
|
66
|
-
function
|
|
159
|
+
function getTableStructureFromMetadata(metadata) {
|
|
67
160
|
if (!metadata[TABLE_INFO_PROPERTY_NAME])
|
|
68
|
-
metadata[TABLE_INFO_PROPERTY_NAME] = { primaryKey: "", foreignKeys: [] };
|
|
161
|
+
metadata[TABLE_INFO_PROPERTY_NAME] = { table: "", primaryKey: "", foreignKeys: [], columns: {} };
|
|
69
162
|
return metadata[TABLE_INFO_PROPERTY_NAME];
|
|
70
163
|
}
|
|
71
164
|
|
|
@@ -77,101 +170,85 @@ var TableStructureGenerator = class {
|
|
|
77
170
|
this.dialect = dialect;
|
|
78
171
|
this.dbInstructions = dbInstructions;
|
|
79
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Generates the structure of tables based on database instructions.
|
|
175
|
+
*
|
|
176
|
+
* @return An object where keys are table names, and values are their corresponding table structures.
|
|
177
|
+
*/
|
|
80
178
|
generateTableStructure() {
|
|
81
|
-
if (this.dbInstructions.tables.length) {
|
|
82
|
-
const tableObjects = {};
|
|
83
|
-
for (const table of this.dbInstructions.tables) {
|
|
84
|
-
tableObjects[table.name] = { columns: new table(), tableInfo: getTableInfo(table) };
|
|
85
|
-
}
|
|
86
|
-
return this.getExpectedStructure(tableObjects);
|
|
87
|
-
} else {
|
|
88
|
-
const tableObjects = this.dbInstructions.tables;
|
|
89
|
-
return this.getExpectedStructure(tableObjects);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
getExpectedStructure(tableObjects) {
|
|
93
179
|
const tables = {};
|
|
94
|
-
for (const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
table
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
getColumns(obj, primaryKey) {
|
|
107
|
-
const columns = [];
|
|
108
|
-
for (const property in obj) {
|
|
109
|
-
const propertyKey = property;
|
|
110
|
-
const value = obj[propertyKey];
|
|
111
|
-
const columnData = {
|
|
112
|
-
name: property,
|
|
113
|
-
isPrimaryKey: property == primaryKey,
|
|
114
|
-
type: "TEXT",
|
|
115
|
-
defaultValue: this.dialect.typeNull
|
|
116
|
-
};
|
|
117
|
-
switch (typeof value) {
|
|
118
|
-
case "string":
|
|
119
|
-
columnData.type = this.dialect.typeString;
|
|
120
|
-
columnData.defaultValue = value === null ? this.dialect.typeNull : `"${this.dialect.formatValueToSql(value)}"`;
|
|
121
|
-
break;
|
|
122
|
-
case "bigint":
|
|
123
|
-
columnData.type = this.dialect.typeBigInt;
|
|
124
|
-
columnData.defaultValue = value === null ? this.dialect.typeNull : this.dialect.formatValueToSql(value);
|
|
125
|
-
break;
|
|
126
|
-
case "number":
|
|
127
|
-
columnData.type = this.dialect.typeInt;
|
|
128
|
-
columnData.defaultValue = value === null ? this.dialect.typeNull : this.dialect.formatValueToSql(value);
|
|
129
|
-
break;
|
|
130
|
-
case "boolean":
|
|
131
|
-
columnData.type = this.dialect.typeBoolean;
|
|
132
|
-
columnData.defaultValue = value === null ? this.dialect.typeNull : this.dialect.formatValueToSql(value);
|
|
133
|
-
break;
|
|
134
|
-
case "function":
|
|
135
|
-
continue;
|
|
136
|
-
default:
|
|
137
|
-
Logger.warn(`${obj.constructor.name}.${property} was skipped because its type is not supported (${typeof value})`);
|
|
138
|
-
continue;
|
|
180
|
+
for (const table of this.dbInstructions.tables) {
|
|
181
|
+
if (TableObj.isTableObj(table)) {
|
|
182
|
+
tables[table.tableName] = table.tableStructure;
|
|
183
|
+
} else {
|
|
184
|
+
const classTable = table;
|
|
185
|
+
const structure = getTableStructure(classTable);
|
|
186
|
+
tables[classTable.name] = structure;
|
|
187
|
+
for (const key in structure.columns) {
|
|
188
|
+
const entry = structure.columns[key];
|
|
189
|
+
entry.defaultValue = entry.defaultValue === null ? this.dialect.types.null : this.dialect.formatValueToSql(entry.inputDefaultValue, entry.inputType);
|
|
190
|
+
entry.type = this.dialect.getSqlType(entry.inputType, entry);
|
|
191
|
+
}
|
|
139
192
|
}
|
|
140
|
-
columns.push(columnData);
|
|
141
193
|
}
|
|
142
|
-
return
|
|
194
|
+
return tables;
|
|
143
195
|
}
|
|
144
196
|
};
|
|
145
197
|
|
|
146
198
|
// src/lib/exceptions/NotAllowedException.ts
|
|
147
199
|
var NotAllowedException = class extends Error {
|
|
148
200
|
constructor(entries) {
|
|
149
|
-
let message = "Some destructive changes need to be confirmed
|
|
201
|
+
let message = "Some destructive changes need to be confirmed. To confirm, add to preMigration():\n";
|
|
150
202
|
for (const entry of entries) {
|
|
151
|
-
message += ` migrations.allowMigration(${entry.version},
|
|
203
|
+
message += ` migrations.allowMigration(${entry.version}, ${entry.tableName}, "${entry.type}");
|
|
152
204
|
`;
|
|
153
205
|
}
|
|
154
206
|
super(message);
|
|
155
207
|
}
|
|
156
208
|
};
|
|
157
209
|
|
|
158
|
-
// src/lib/
|
|
210
|
+
// src/lib/Migrations.ts
|
|
159
211
|
var Migrations = class {
|
|
160
|
-
toVersion = 0;
|
|
161
|
-
alwaysAllowed = {};
|
|
162
212
|
fromVersion = 0;
|
|
213
|
+
toVersion = 0;
|
|
214
|
+
lastUsedVersion = 0;
|
|
163
215
|
migrationData = {};
|
|
164
216
|
migrationDataForOldTableNames = {};
|
|
217
|
+
alwaysAllowed = {};
|
|
165
218
|
notAllowedChanges = [];
|
|
219
|
+
/**
|
|
220
|
+
* Resets the migration state to its initial values.
|
|
221
|
+
* Only used internally.
|
|
222
|
+
*
|
|
223
|
+
* @param dbInstructions - The instructions containing database metadata and migration details.
|
|
224
|
+
* @param fromVersion - The version of the actual database.
|
|
225
|
+
*/
|
|
166
226
|
reset(dbInstructions, fromVersion) {
|
|
167
227
|
this.fromVersion = fromVersion;
|
|
168
|
-
this.migrationData = {};
|
|
169
228
|
this.toVersion = dbInstructions.version;
|
|
229
|
+
this.migrationData = {};
|
|
230
|
+
this.migrationDataForOldTableNames = {};
|
|
170
231
|
this.alwaysAllowed = dbInstructions.alwaysAllowedMigrations ?? {};
|
|
171
232
|
this.notAllowedChanges = [];
|
|
172
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Retrieves the migration instructions for the specified table.
|
|
236
|
+
* @see getEntryFromTableName
|
|
237
|
+
*
|
|
238
|
+
* @param table - The {@link TableInput} object from which the table name will be retrieved.
|
|
239
|
+
* @return The migration instructions corresponding to the given table input.
|
|
240
|
+
*/
|
|
173
241
|
getEntry(table) {
|
|
174
242
|
const tableName = this.getTableName(table);
|
|
243
|
+
return this.getEntryFromTableName(tableName);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Retrieves the migration instructions for the specified table.
|
|
247
|
+
*
|
|
248
|
+
* @param tableName - The name of the table for which the entry is to be retrieved or created.
|
|
249
|
+
* @return The migration instructions corresponding to the given table input.
|
|
250
|
+
*/
|
|
251
|
+
getEntryFromTableName(tableName) {
|
|
175
252
|
if (!this.migrationData.hasOwnProperty(tableName)) {
|
|
176
253
|
this.migrationData[tableName] = {
|
|
177
254
|
recreate: false,
|
|
@@ -182,28 +259,67 @@ var Migrations = class {
|
|
|
182
259
|
}
|
|
183
260
|
return this.migrationData[tableName];
|
|
184
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Retrieves the table name from the given table input.
|
|
264
|
+
*
|
|
265
|
+
* @param table - The {@link TableInput} object.
|
|
266
|
+
* @return The name of the table derived from either {@link TableObj} or a class.
|
|
267
|
+
*/
|
|
185
268
|
getTableName(table) {
|
|
186
|
-
return
|
|
269
|
+
return TableObj.isTableObj(table) ? table.tableName : table.name;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Checks whether a provided version is relevant based on database version ({@link fromVersion}) and target version ({@link toVersion}).
|
|
273
|
+
* Also makes sure that version checks happen in order.
|
|
274
|
+
*
|
|
275
|
+
* @param version - The version to check for relevance.
|
|
276
|
+
* @return Returns true if the version is relevant, otherwise false.
|
|
277
|
+
* @throws Error if versions are not properly ordered
|
|
278
|
+
*/
|
|
279
|
+
versionIsRelevant(version) {
|
|
280
|
+
if (version <= this.fromVersion || version > this.toVersion)
|
|
281
|
+
return false;
|
|
282
|
+
if (version < this.lastUsedVersion)
|
|
283
|
+
throw new Error("Migrations in preMigration() have to be ordered by version (beginning with the lowest version).");
|
|
284
|
+
this.lastUsedVersion = version;
|
|
285
|
+
return true;
|
|
187
286
|
}
|
|
287
|
+
/**
|
|
288
|
+
* Verifies the renaming tasks specified in {@link DatabaseInstructions.preMigration} that were considered are valid.
|
|
289
|
+
* Called after all SQL queries have been generated.
|
|
290
|
+
* Only used internally.
|
|
291
|
+
*
|
|
292
|
+
* @param newTables - The table structures extracted from {@link DatabaseInstructions.tables}.
|
|
293
|
+
* @throws Error if a renaming task is invalid.
|
|
294
|
+
*/
|
|
188
295
|
verifyRenamingTasks(newTables) {
|
|
189
296
|
for (const tableName in this.migrationData) {
|
|
190
297
|
const migrationEntry = this.migrationData[tableName];
|
|
191
|
-
if (migrationEntry.tableRenaming
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
298
|
+
if (migrationEntry.tableRenaming) {
|
|
299
|
+
if (migrationEntry.tableRenaming.oldName == tableName)
|
|
300
|
+
return new Error(`You set table "${tableName}" to be renamed to itself!`);
|
|
301
|
+
if (migrationEntry.tableRenaming.newName != tableName)
|
|
302
|
+
return new Error(`You set table "${tableName}" to be renamed to a different name than the one in your structure (${migrationEntry.tableRenaming.newName})!`);
|
|
303
|
+
else if (!newTables[tableName])
|
|
304
|
+
return new Error(`You set table "${migrationEntry.tableRenaming}" migrations for "${tableName}". But "${tableName}" does not exist in your structure.`);
|
|
305
|
+
}
|
|
197
306
|
for (const renamingData of migrationEntry.renamedColumns) {
|
|
198
307
|
const oldColumnName = renamingData.oldName;
|
|
199
308
|
const newColumnName = renamingData.newName;
|
|
200
309
|
if (oldColumnName == newColumnName)
|
|
201
310
|
return new Error(`You set column ${tableName}.${oldColumnName} to be renamed to itself!`);
|
|
202
|
-
else if (!newTables[tableName]?.columns
|
|
311
|
+
else if (!newTables[tableName]?.columns[newColumnName])
|
|
203
312
|
return new Error(`You set column ${tableName}.${oldColumnName} to be renamed to ${tableName}.${newColumnName}. But ${tableName}.${newColumnName} does not exist in your structure.`);
|
|
204
313
|
}
|
|
205
314
|
}
|
|
206
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Making sure all considered calls to {@link allowMigration()} where necessary to prevent input mistakes.
|
|
318
|
+
* Called after all SQL queries have been generated.
|
|
319
|
+
* Only used internally.
|
|
320
|
+
*
|
|
321
|
+
* @throws Error if unnecessary calls were discovered. All discovered calls are collected and thrown as one Error.
|
|
322
|
+
*/
|
|
207
323
|
verifyAllowedMigrations() {
|
|
208
324
|
if (this.notAllowedChanges.length)
|
|
209
325
|
return new NotAllowedException(this.notAllowedChanges);
|
|
@@ -219,6 +335,14 @@ var Migrations = class {
|
|
|
219
335
|
if (errorMsg)
|
|
220
336
|
return new Error(errorMsg);
|
|
221
337
|
}
|
|
338
|
+
/**
|
|
339
|
+
* Called by {@link MigrationManager} to check if a specific destructive query is allowed.
|
|
340
|
+
* Only used internally.
|
|
341
|
+
*
|
|
342
|
+
* @see allowMigration()
|
|
343
|
+
* @param tableName - The name of the table to check the migration against.
|
|
344
|
+
* @param type - The type of destructive migration being checked.
|
|
345
|
+
*/
|
|
222
346
|
compareWithAllowedMigration(tableName, type) {
|
|
223
347
|
const migrationEntry = this.migrationData[tableName];
|
|
224
348
|
if (!migrationEntry?.allowedMigrations[type]) {
|
|
@@ -227,22 +351,118 @@ var Migrations = class {
|
|
|
227
351
|
} else if (migrationEntry)
|
|
228
352
|
migrationEntry.usedMigrations[type] = true;
|
|
229
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Retrieves the migration data containing instructions for data migration.
|
|
356
|
+
* Only used internally.
|
|
357
|
+
*
|
|
358
|
+
* @return An object where each key corresponds to the table name,
|
|
359
|
+
* and the value is the associated migration instructions.
|
|
360
|
+
*/
|
|
361
|
+
getMigrationData() {
|
|
362
|
+
return this.migrationData;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Iterates over renamed columns for the specified table and executes a callback for each pair of old and new column names.
|
|
366
|
+
* Only used internally.
|
|
367
|
+
*
|
|
368
|
+
* @param tableName - Name of the table whose renamed columns should be processed.
|
|
369
|
+
* @param callback - A function to execute for each renamed column, receiving the old and new column names as arguments.
|
|
370
|
+
*/
|
|
371
|
+
loopRenamedColumns(tableName, callback) {
|
|
372
|
+
const migrationEntry = this.migrationData[tableName];
|
|
373
|
+
for (const renamingData of migrationEntry.renamedColumns) {
|
|
374
|
+
callback(renamingData.oldName, renamingData.newName);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Considers the renaming of a table and returns its (newest) name after the migration or
|
|
379
|
+
* the original name if no renaming is queued.
|
|
380
|
+
* Only used internally.
|
|
381
|
+
*
|
|
382
|
+
* @see getOldTableName()
|
|
383
|
+
* @param tableName - The current table name in the database.
|
|
384
|
+
* @return The newest table name if available, otherwise the current table name.
|
|
385
|
+
*/
|
|
386
|
+
getNewestTableName(tableName) {
|
|
387
|
+
return this.migrationDataForOldTableNames[tableName]?.tableRenaming?.newName ?? tableName;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Retrieves the current table name in the database.
|
|
391
|
+
* Only used internally.
|
|
392
|
+
*
|
|
393
|
+
* @see getNewestTableName()
|
|
394
|
+
* @param tableName - The name of the newest table name.
|
|
395
|
+
* @return The current table name in the database.
|
|
396
|
+
*/
|
|
397
|
+
getOldTableName(tableName) {
|
|
398
|
+
return this.migrationData[tableName]?.tableRenaming?.oldName ?? tableName;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Considers the renaming of a column in a table and returns its (newest) name after the migration or
|
|
402
|
+
* the original name if no renaming is queued.
|
|
403
|
+
* Only used internally.
|
|
404
|
+
*
|
|
405
|
+
* @param tableName - The name of the table containing the column.
|
|
406
|
+
* @param oldColumnName - The current name of the column in the database.
|
|
407
|
+
* @return The newest column name if available, otherwise the original column name.
|
|
408
|
+
*/
|
|
409
|
+
getNewestColumnName(tableName, oldColumnName) {
|
|
410
|
+
const columnNames = this.migrationData[tableName]?.renamedColumns.find((columns) => columns.oldName == oldColumnName);
|
|
411
|
+
return columnNames ? columnNames.newName : oldColumnName;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Determines if the specified table is marked to be recreated.
|
|
415
|
+
* Only used internally.
|
|
416
|
+
*
|
|
417
|
+
* @param tableName The name of the table to check.
|
|
418
|
+
* @return Returns true if the table will be recreated, false otherwise.
|
|
419
|
+
*/
|
|
420
|
+
willBeRecreated(tableName) {
|
|
421
|
+
return this.migrationData[tableName]?.recreate;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Marks a table to be recreated and logs the recreation reason for the user.
|
|
425
|
+
* Only used internally.
|
|
426
|
+
*
|
|
427
|
+
* @param tableName - The name of the table to be marked for recreation.
|
|
428
|
+
* @param reason - The reason why the table is being marked for recreation.
|
|
429
|
+
*/
|
|
430
|
+
internalRecreate(tableName, reason) {
|
|
431
|
+
const entry = this.getEntryFromTableName(tableName);
|
|
432
|
+
entry.recreate = true;
|
|
433
|
+
Logger.log(`Table ${tableName} will be recreated(${reason})!`);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* SqlMorpheus only allows destructive statements if it was specifically allowed in {@link DatabaseInstructions.preMigration()}.
|
|
437
|
+
* Use this method to allow a destructive statement for a specific version.
|
|
438
|
+
* This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
|
|
439
|
+
*
|
|
440
|
+
* @param version - The target version for which the specific migration is allowed. Must be provided to make sure this method will only have an effect when specified.
|
|
441
|
+
* @param table - Either a {@link TableObj} or a class decorated with @TableClass.
|
|
442
|
+
* @param allowedMigrations - The keys representing the migrations to be allowed. Multiple migrations can be provided
|
|
443
|
+
*/
|
|
230
444
|
allowMigration(version, table, ...allowedMigrations) {
|
|
231
|
-
if (
|
|
445
|
+
if (!this.versionIsRelevant(version))
|
|
232
446
|
return;
|
|
233
447
|
const entry = this.getEntry(table);
|
|
234
448
|
for (const key of allowedMigrations) {
|
|
235
449
|
entry.allowedMigrations[key] = true;
|
|
236
450
|
}
|
|
237
451
|
}
|
|
452
|
+
/**
|
|
453
|
+
* When you change the name of a table, SqlMorpheus will assume the original table was meant to be dropped
|
|
454
|
+
* and the new table was meant to be added, which would lead to data loss.
|
|
455
|
+
* Use this method to mark a table to be renamed instead.
|
|
456
|
+
* This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
|
|
457
|
+
*
|
|
458
|
+
* @param version - The target version for which the table should be renamed. Must be provided to make sure this method will only have an effect when specified.
|
|
459
|
+
* @param oldTableName - The current name in the database at the time of the migration.
|
|
460
|
+
* @param newTableName - The new table name or its equivalent input format.
|
|
461
|
+
*/
|
|
238
462
|
renameTable(version, oldTableName, newTableName) {
|
|
239
|
-
if (
|
|
463
|
+
if (!this.versionIsRelevant(version))
|
|
240
464
|
return;
|
|
241
465
|
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
466
|
const entry = this.getEntry(newTableName);
|
|
247
467
|
this.migrationDataForOldTableNames[oldTableName] = entry;
|
|
248
468
|
if (!entry.tableRenaming)
|
|
@@ -250,8 +470,19 @@ var Migrations = class {
|
|
|
250
470
|
else
|
|
251
471
|
entry.tableRenaming.newName = newTableNameString;
|
|
252
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* When you change the name of a column, SqlMorpheus will assume the original column was meant to be removed
|
|
475
|
+
* and the new column was meant to be added, which would lead to data loss.
|
|
476
|
+
* Use this method to mark a column to be renamed instead.
|
|
477
|
+
* This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
|
|
478
|
+
*
|
|
479
|
+
* @param version - The target version for which the table should be renamed. Must be provided to make sure this method will only have an effect when specified.
|
|
480
|
+
* @param table Either a {@link TableObj} or a class decorated with @TableClass containing the column to be renamed.
|
|
481
|
+
* @param oldColumn The current name in the database at the time of the migration.
|
|
482
|
+
* @param newColumn The new name to assign to the column.
|
|
483
|
+
*/
|
|
253
484
|
renameColumn(version, table, oldColumn, newColumn) {
|
|
254
|
-
if (
|
|
485
|
+
if (!this.versionIsRelevant(version))
|
|
255
486
|
return;
|
|
256
487
|
const entry = this.getEntry(table);
|
|
257
488
|
const existingColumnEntry = entry.renamedColumns.find((entry2) => entry2.newName == oldColumn);
|
|
@@ -260,36 +491,21 @@ var Migrations = class {
|
|
|
260
491
|
else
|
|
261
492
|
entry.renamedColumns.push({ oldName: oldColumn, newName: newColumn });
|
|
262
493
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
494
|
+
/**
|
|
495
|
+
* Should not be needed most of the time.
|
|
496
|
+
* Marks a table to be recreated
|
|
497
|
+
* (a copy of the table will be created, all data will be moved over, the original table will be dropped and the new table will be renamed to the original name).
|
|
498
|
+
* This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
|
|
499
|
+
*
|
|
500
|
+
* @param version - The target version for which the table should be renamed. Must be provided to make sure this method will only have an effect when specified.
|
|
501
|
+
* @param table Either a {@link TableObj} or a class decorated with @TableClass.
|
|
502
|
+
*/
|
|
267
503
|
recreateTable(version, table) {
|
|
268
|
-
if (
|
|
504
|
+
if (!this.versionIsRelevant(version))
|
|
269
505
|
return;
|
|
270
|
-
this.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
return this.migrationData;
|
|
274
|
-
}
|
|
275
|
-
loopRenamedColumns(tableName, callback) {
|
|
276
|
-
const migrationEntry = this.migrationData[tableName];
|
|
277
|
-
for (const renamingData of migrationEntry.renamedColumns) {
|
|
278
|
-
callback(renamingData.oldName, renamingData.newName);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
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;
|
|
290
|
-
}
|
|
291
|
-
willBeRecreated(tableName) {
|
|
292
|
-
return this.migrationData[tableName]?.recreate;
|
|
506
|
+
const entry = this.getEntry(table);
|
|
507
|
+
entry.recreate = true;
|
|
508
|
+
Logger.log(`Table ${this.getTableName(table)} will be recreated!`);
|
|
293
509
|
}
|
|
294
510
|
};
|
|
295
511
|
|
|
@@ -472,8 +688,9 @@ ${changes.up}
|
|
|
472
688
|
*/
|
|
473
689
|
createTableSql(tableName, columns, foreignKeys) {
|
|
474
690
|
const queryLines = [];
|
|
475
|
-
for (const
|
|
476
|
-
|
|
691
|
+
for (const key in columns) {
|
|
692
|
+
const columnInfo = columns[key];
|
|
693
|
+
queryLines.push(this.dialect.columnDefinition(columnInfo.name, columnInfo.type, columnInfo.isPrimaryKey, columnInfo.defaultValue));
|
|
477
694
|
}
|
|
478
695
|
if (foreignKeys) {
|
|
479
696
|
for (const foreignKeyInfo of foreignKeys) {
|
|
@@ -506,13 +723,16 @@ ${changes.up}
|
|
|
506
723
|
const migrationEntry = migrationData[tableName];
|
|
507
724
|
if (!migrationEntry.recreate)
|
|
508
725
|
continue;
|
|
509
|
-
Logger.log(`Table ${tableName} will be recreated!`);
|
|
510
726
|
this.migrations.compareWithAllowedMigration(tableName, "recreateTable");
|
|
511
727
|
if (!this.dialect.canInspectForeignKeys || !this.dialect.canInspectPrimaryKey)
|
|
512
728
|
this.migrations.compareWithAllowedMigration(tableName, "continueWithoutRollback");
|
|
513
729
|
const oldColumnList = await this.dialect.getColumnInformation(this.migrations.getOldTableName(tableName));
|
|
514
730
|
const newColumnList = this.newTables[tableName].columns;
|
|
515
|
-
const moveableColumns =
|
|
731
|
+
const moveableColumns = [];
|
|
732
|
+
for (const key in newColumnList) {
|
|
733
|
+
if (oldColumnList.hasOwnProperty(key))
|
|
734
|
+
moveableColumns.push(newColumnList[key].name);
|
|
735
|
+
}
|
|
516
736
|
const backupTableName = `${tableName}__backup`;
|
|
517
737
|
const insertQuery = this.dialect.insert(
|
|
518
738
|
backupTableName,
|
|
@@ -520,7 +740,7 @@ ${changes.up}
|
|
|
520
740
|
);
|
|
521
741
|
const moveDataQuery = insertQuery + "\n" + this.dialect.dropTable(tableName) + "\n" + this.dialect.renameTable(backupTableName, tableName);
|
|
522
742
|
const structure = this.newTables[tableName];
|
|
523
|
-
changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery + "\n";
|
|
743
|
+
changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery + "\n\n";
|
|
524
744
|
const dbTableName = this.migrations.getOldTableName(tableName);
|
|
525
745
|
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";
|
|
526
746
|
}
|
|
@@ -553,7 +773,7 @@ ${changes.up}
|
|
|
553
773
|
this.dialect.foreignKey(oldForeignKey.fromColumn, oldForeignKey.toTable, oldForeignKey.toColumn, oldForeignKey.onUpdate, oldForeignKey.onDelete)
|
|
554
774
|
);
|
|
555
775
|
} else {
|
|
556
|
-
this.migrations.
|
|
776
|
+
this.migrations.internalRecreate(structure.table, "Dialect does not support altering foreign keys");
|
|
557
777
|
checkForNewForeignKeys = false;
|
|
558
778
|
}
|
|
559
779
|
} else {
|
|
@@ -570,7 +790,7 @@ ${changes.up}
|
|
|
570
790
|
this.dialect.foreignKey(oldForeignKey.fromColumn, oldForeignKey.toTable, oldForeignKey.toColumn, oldForeignKey.onUpdate, oldForeignKey.onDelete)
|
|
571
791
|
) + this.dialect.removeForeignKey(newForeignKey.fromTable, newForeignKey.fromColumn);
|
|
572
792
|
} else {
|
|
573
|
-
this.migrations.
|
|
793
|
+
this.migrations.internalRecreate(structure.table, "Dialect does not support altering foreign keys");
|
|
574
794
|
checkForNewForeignKeys = false;
|
|
575
795
|
}
|
|
576
796
|
}
|
|
@@ -589,7 +809,7 @@ ${changes.up}
|
|
|
589
809
|
);
|
|
590
810
|
changes.down += this.dialect.removeForeignKey(newForeignKey.fromTable, newForeignKey.fromColumn);
|
|
591
811
|
} else {
|
|
592
|
-
this.migrations.
|
|
812
|
+
this.migrations.internalRecreate(structure.table, "Dialect does not support altering foreign keys");
|
|
593
813
|
break;
|
|
594
814
|
}
|
|
595
815
|
}
|
|
@@ -612,6 +832,10 @@ ${changes.up}
|
|
|
612
832
|
const newTableDefinition = this.newTables[tableName];
|
|
613
833
|
const oldColumnList = await this.dialect.getColumnInformation(this.migrations.getOldTableName(tableName));
|
|
614
834
|
const oldPrimaryKey = this.dialect.canInspectPrimaryKey ? this.getPrimaryKeyColumn(oldColumnList) : false;
|
|
835
|
+
const oldColumnNewNameIndex = {};
|
|
836
|
+
for (const key in oldColumnList) {
|
|
837
|
+
oldColumnNewNameIndex[this.migrations.getNewestColumnName(tableName, key)] = oldColumnList[key];
|
|
838
|
+
}
|
|
615
839
|
const newColumnList = newTableDefinition.columns;
|
|
616
840
|
const newPrimaryKey = newTableDefinition.primaryKey;
|
|
617
841
|
if (this.dialect.canInspectPrimaryKey && oldPrimaryKey != newPrimaryKey) {
|
|
@@ -627,28 +851,26 @@ ${changes.up}
|
|
|
627
851
|
changes.down += this.dialect.removePrimaryKey(tableName, newPrimaryKey.toString());
|
|
628
852
|
}
|
|
629
853
|
} else
|
|
630
|
-
this.migrations.
|
|
854
|
+
this.migrations.internalRecreate(newTableDefinition.table, "Dialect does not support altering primary keys");
|
|
631
855
|
continue;
|
|
632
856
|
}
|
|
633
|
-
for (const
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
);
|
|
857
|
+
for (const key in newColumnList) {
|
|
858
|
+
const newColumn = newColumnList[key];
|
|
859
|
+
const oldColumn = oldColumnNewNameIndex[newColumn.name];
|
|
637
860
|
if (oldColumn == void 0) {
|
|
638
|
-
changes.up += this.dialect.createColumn(tableName, this.dialect.columnDefinition(newColumn.name, newColumn.type, newColumn.
|
|
861
|
+
changes.up += this.dialect.createColumn(tableName, this.dialect.columnDefinition(newColumn.name, newColumn.type, newColumn.isPrimaryKey, newColumn.defaultValue)) + "\n";
|
|
639
862
|
changes.down += this.dialect.dropColumn(tableName, newColumn.name) + "\n";
|
|
640
863
|
} else if (newColumn.type != oldColumn.type || newColumn.defaultValue != oldColumn.defaultValue) {
|
|
641
|
-
this.migrations.
|
|
864
|
+
this.migrations.internalRecreate(newTableDefinition.table, "Table structure was changed");
|
|
642
865
|
}
|
|
643
866
|
}
|
|
644
|
-
for (const
|
|
645
|
-
const
|
|
646
|
-
|
|
647
|
-
);
|
|
867
|
+
for (const key in oldColumnList) {
|
|
868
|
+
const oldColumn = oldColumnList[key];
|
|
869
|
+
const newColumn = newColumnList[this.migrations.getNewestColumnName(tableName, oldColumn.name)];
|
|
648
870
|
if (newColumn == void 0) {
|
|
649
871
|
this.migrations.compareWithAllowedMigration(tableName, "dropColumn");
|
|
650
872
|
changes.up += this.dialect.dropColumn(tableName, oldColumn.name) + "\n";
|
|
651
|
-
changes.down += this.dialect.createColumn(tableName, this.dialect.columnDefinition(oldColumn.name, oldColumn.type, oldColumn.
|
|
873
|
+
changes.down += this.dialect.createColumn(tableName, this.dialect.columnDefinition(oldColumn.name, oldColumn.type, oldColumn.isPrimaryKey, oldColumn.defaultValue)) + "\n";
|
|
652
874
|
}
|
|
653
875
|
}
|
|
654
876
|
}
|
|
@@ -657,11 +879,12 @@ ${changes.up}
|
|
|
657
879
|
/**
|
|
658
880
|
* Retrieves the name of the primary key column from a list of column information.
|
|
659
881
|
*
|
|
660
|
-
* @param
|
|
882
|
+
* @param columnInfoEntries - An array of ColumnInfo objects containing details about each column.
|
|
661
883
|
* @return The name of the primary key column if found; otherwise, undefined.
|
|
662
884
|
*/
|
|
663
|
-
getPrimaryKeyColumn(
|
|
664
|
-
for (const
|
|
885
|
+
getPrimaryKeyColumn(columnInfoEntries) {
|
|
886
|
+
for (const key in columnInfoEntries) {
|
|
887
|
+
const columnInfo = columnInfoEntries[key];
|
|
665
888
|
if (columnInfo.isPrimaryKey)
|
|
666
889
|
return columnInfo.name;
|
|
667
890
|
}
|
|
@@ -747,26 +970,49 @@ ${changes.down}`, { encoding: "utf-8" });
|
|
|
747
970
|
}
|
|
748
971
|
};
|
|
749
972
|
|
|
750
|
-
// src/lib/tableInfo/decorators/
|
|
973
|
+
// src/lib/tableInfo/decorators/TableClass.ts
|
|
751
974
|
var import_polyfill_symbol_metadata = require("polyfill-symbol-metadata");
|
|
752
|
-
function
|
|
975
|
+
function TableClass(tableName, primaryKey) {
|
|
753
976
|
return (table, context) => {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
if (
|
|
758
|
-
for (const foreignKey of
|
|
977
|
+
const tableStructure = context?.metadata ? getTableStructureFromMetadata(context.metadata) : getTableStructure(table);
|
|
978
|
+
tableStructure.primaryKey = primaryKey?.toString();
|
|
979
|
+
tableStructure.table = tableName;
|
|
980
|
+
if (tableStructure.foreignKeys) {
|
|
981
|
+
for (const foreignKey of tableStructure.foreignKeys) {
|
|
759
982
|
foreignKey.fromTable = tableName;
|
|
983
|
+
if (foreignKey.toTable == table.name)
|
|
984
|
+
foreignKey.toTable = tableName;
|
|
760
985
|
}
|
|
761
986
|
}
|
|
987
|
+
for (const key in table) {
|
|
988
|
+
const value = table[key];
|
|
989
|
+
const existingEntry = tableStructure.columns[key];
|
|
990
|
+
const defaultEntry = TableObj.getColumnEntry(key, value);
|
|
991
|
+
if (!defaultEntry)
|
|
992
|
+
continue;
|
|
993
|
+
tableStructure.columns[key] = {
|
|
994
|
+
...defaultEntry,
|
|
995
|
+
...existingEntry
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
Object.defineProperty(table, "name", { value: tableName, writable: false });
|
|
762
999
|
};
|
|
763
1000
|
}
|
|
764
1001
|
|
|
765
|
-
// src/lib/tableInfo/decorators/
|
|
1002
|
+
// src/lib/tableInfo/decorators/DataType.ts
|
|
766
1003
|
var import_polyfill_symbol_metadata2 = require("polyfill-symbol-metadata");
|
|
767
|
-
function
|
|
1004
|
+
function DataType(type) {
|
|
1005
|
+
return (table, context) => {
|
|
1006
|
+
const entry = getInputColumnInfo(table, context);
|
|
1007
|
+
entry.inputType = type;
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// src/lib/tableInfo/decorators/ForeignKey.ts
|
|
1012
|
+
var import_polyfill_symbol_metadata3 = require("polyfill-symbol-metadata");
|
|
1013
|
+
function ForeignKey(toTable, toColumn, options) {
|
|
768
1014
|
return (table, context) => {
|
|
769
|
-
const metadata = context?.metadata ?
|
|
1015
|
+
const metadata = context?.metadata ? getTableStructureFromMetadata(context.metadata) : getTableStructure(table);
|
|
770
1016
|
if (!metadata.foreignKeys)
|
|
771
1017
|
metadata.foreignKeys = [];
|
|
772
1018
|
metadata.foreignKeys.push({
|
|
@@ -775,8 +1021,8 @@ function ForeignKey(toTable, toColumn, onDelete, onUpdate) {
|
|
|
775
1021
|
//to stay compatible with old decorator structure (see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#decorator-metadata)
|
|
776
1022
|
toTable: toTable.name,
|
|
777
1023
|
toColumn: toColumn.toString(),
|
|
778
|
-
onDelete,
|
|
779
|
-
onUpdate
|
|
1024
|
+
onDelete: options?.onDelete,
|
|
1025
|
+
onUpdate: options?.onUpdate
|
|
780
1026
|
});
|
|
781
1027
|
};
|
|
782
1028
|
}
|
|
@@ -789,32 +1035,111 @@ var DefaultSql = class {
|
|
|
789
1035
|
canAlterPrimaryKey = false;
|
|
790
1036
|
canInspectForeignKeys = false;
|
|
791
1037
|
canInspectPrimaryKey = false;
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
1038
|
+
types = {
|
|
1039
|
+
string: "TEXT",
|
|
1040
|
+
number: "INTEGER",
|
|
1041
|
+
bigint: "BIGINT",
|
|
1042
|
+
boolean: "BOOLEAN",
|
|
1043
|
+
date: "DATE",
|
|
1044
|
+
time: "TIME",
|
|
1045
|
+
dateTime: "DATETIME",
|
|
1046
|
+
null: "NULL"
|
|
1047
|
+
};
|
|
797
1048
|
constructor(db) {
|
|
798
1049
|
this.db = db;
|
|
799
1050
|
}
|
|
800
|
-
|
|
801
|
-
|
|
1051
|
+
/**
|
|
1052
|
+
* Formats a given value to a SQL-equivalent string based on the provided data type.
|
|
1053
|
+
*
|
|
1054
|
+
* @param value - The value to be formatted.
|
|
1055
|
+
* @param type - The data type of the value. Either determined through typeof or {@link TableStructure} setting
|
|
1056
|
+
* @return A string representation of the value formatted for SQL.
|
|
1057
|
+
*/
|
|
1058
|
+
formatValueToSql(value, type) {
|
|
1059
|
+
function toDate(value2) {
|
|
1060
|
+
return value2 instanceof Date ? value2 : new Date(value2);
|
|
1061
|
+
}
|
|
1062
|
+
switch (type) {
|
|
1063
|
+
case "string":
|
|
1064
|
+
return `"${value}"`;
|
|
1065
|
+
case "time":
|
|
1066
|
+
return `"${toDate(value).toISOString().slice(11, 19)}"`;
|
|
1067
|
+
case "date":
|
|
1068
|
+
return `"${toDate(value).toISOString().slice(0, 10)}"`;
|
|
1069
|
+
case "dateTime":
|
|
1070
|
+
return `"${toDate(value).toISOString().slice(0, 19).replace("T", " ")}"`;
|
|
1071
|
+
default:
|
|
1072
|
+
return value.toString();
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
getSqlType(dataType, _) {
|
|
1076
|
+
return this.types[dataType];
|
|
802
1077
|
}
|
|
1078
|
+
/**
|
|
1079
|
+
* Statement to enable / disable foreign keys in the database.
|
|
1080
|
+
* Is dialect specific.
|
|
1081
|
+
*
|
|
1082
|
+
* @param enabled - A boolean indicating whether to enable or disable foreign key constraints.
|
|
1083
|
+
* @return The SQL statement as a string.
|
|
1084
|
+
*/
|
|
803
1085
|
changeForeignKeysState(enabled) {
|
|
804
1086
|
return "";
|
|
805
1087
|
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Statement to add a foreign key to a specified table.
|
|
1090
|
+
* Is only used if {@link canAlterForeignKeys} is true.
|
|
1091
|
+
*
|
|
1092
|
+
* @param fromTableName - The name of the table to which the foreign key will be added.
|
|
1093
|
+
* @param foreignKey - The foreign key definition to be added, see {@link foreignKey()}.
|
|
1094
|
+
* @return The SQL statement as a string.
|
|
1095
|
+
*/
|
|
806
1096
|
addForeignKey(fromTableName, foreignKey) {
|
|
807
1097
|
throw new Error("Adding foreign keys is not supported!");
|
|
808
1098
|
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Statement to remove a foreign key from a specified table.
|
|
1101
|
+
* Is only used if {@link canAlterForeignKeys} is true.
|
|
1102
|
+
*
|
|
1103
|
+
* @param tableName - The name of the table to which the foreign key will be added.
|
|
1104
|
+
* @param foreignKeyName - The name of the foreign key definition.
|
|
1105
|
+
* @return The SQL statement as a string.
|
|
1106
|
+
*/
|
|
809
1107
|
removeForeignKey(tableName, foreignKeyName) {
|
|
810
1108
|
throw new Error("Foreign key removal is not supported!");
|
|
811
1109
|
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Statement to add a primary key to the specified table.
|
|
1112
|
+
* Is only used if {@link canAlterPrimaryKey} is true.
|
|
1113
|
+
*
|
|
1114
|
+
* @param tableName - The name of the table to which the primary key will be added.
|
|
1115
|
+
* @param columnName - The name of the column that will be set as the primary key.
|
|
1116
|
+
* @return The SQL statement as a string.
|
|
1117
|
+
*/
|
|
812
1118
|
addPrimaryKey(tableName, columnName) {
|
|
813
1119
|
throw new Error("Adding foreign keys is not supported!");
|
|
814
1120
|
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Statement to remove a primary key from the specified table.
|
|
1123
|
+
* Is only used if {@link canAlterPrimaryKey} is true.
|
|
1124
|
+
*
|
|
1125
|
+
* @param tableName - The name of the table to which the primary key will be added.
|
|
1126
|
+
* @param columnName - The name of the column that will be set as the primary key.
|
|
1127
|
+
* @return The SQL statement as a string.
|
|
1128
|
+
*/
|
|
815
1129
|
removePrimaryKey(tableName, columnName) {
|
|
816
1130
|
throw new Error("Foreign key removal is not supported!");
|
|
817
1131
|
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Constructs a SQL FOREIGN KEY constraint clause.
|
|
1134
|
+
* Used in combination with {@link addForeignKey} or {@link createTable}
|
|
1135
|
+
*
|
|
1136
|
+
* @param column - The name of the column in the current table to apply the foreign key constraint.
|
|
1137
|
+
* @param foreignTable - The name of the referenced table.
|
|
1138
|
+
* @param foreignColumn - The name of the referenced column in the foreign table.
|
|
1139
|
+
* @param onUpdate - Optional action to specify the behavior on update (e.g., CASCADE, SET NULL).
|
|
1140
|
+
* @param onDelete - Optional action to specify the behavior on delete (e.g., CASCADE, SET NULL).
|
|
1141
|
+
* @return The SQL string representing the foreign key constraint.
|
|
1142
|
+
*/
|
|
818
1143
|
foreignKey(column, foreignTable, foreignColumn, onUpdate, onDelete) {
|
|
819
1144
|
let query = `FOREIGN KEY (${column}) REFERENCES ${foreignTable} (${foreignColumn})`;
|
|
820
1145
|
if (onUpdate)
|
|
@@ -823,64 +1148,164 @@ var DefaultSql = class {
|
|
|
823
1148
|
query += ` ON DELETE ${onDelete}`;
|
|
824
1149
|
return query;
|
|
825
1150
|
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Statement to create a table if it does not already exist.
|
|
1153
|
+
*
|
|
1154
|
+
* @param tableName - The name of the table to be created.
|
|
1155
|
+
* @param entries - An array containing the column definitions for the table.
|
|
1156
|
+
* @return The SQL statement as a string.
|
|
1157
|
+
*/
|
|
826
1158
|
createTable(tableName, entries) {
|
|
827
1159
|
return `CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
828
1160
|
${entries.join(",\n ")}
|
|
829
1161
|
);`;
|
|
830
1162
|
}
|
|
1163
|
+
/**
|
|
1164
|
+
* Statement to rename a table.
|
|
1165
|
+
*
|
|
1166
|
+
* @param tableName - The name of the existing table to be renamed.
|
|
1167
|
+
* @param newTableName - The new name for the table.
|
|
1168
|
+
* @return The SQL statement as a string.
|
|
1169
|
+
*/
|
|
831
1170
|
renameTable(tableName, newTableName) {
|
|
832
1171
|
return `ALTER TABLE ${tableName} RENAME TO ${newTableName};`;
|
|
833
1172
|
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Statement to drop a table if it exists.
|
|
1175
|
+
*
|
|
1176
|
+
* @param tableName - The name of the table to be dropped.
|
|
1177
|
+
* @return The SQL statement as a string.
|
|
1178
|
+
*/
|
|
834
1179
|
dropTable(tableName) {
|
|
835
1180
|
return `DROP TABLE IF EXISTS ${tableName};`;
|
|
836
1181
|
}
|
|
837
|
-
|
|
838
|
-
|
|
1182
|
+
/**
|
|
1183
|
+
* Constructs a SQL column definition based on the provided parameters.
|
|
1184
|
+
*
|
|
1185
|
+
* @param columnName - The name of the column.
|
|
1186
|
+
* @param type - The data type of the column.
|
|
1187
|
+
* @param defaultValue - The default value for the column.
|
|
1188
|
+
* @param isPrimaryKey - Indicates whether the column is a primary key.
|
|
1189
|
+
* @return The constructed SQL column definition string.
|
|
1190
|
+
*/
|
|
1191
|
+
columnDefinition(columnName, type, isPrimaryKey, defaultValue) {
|
|
1192
|
+
const query = `${columnName} ${type}${defaultValue ? ` DEFAULT ${defaultValue}` : ""}`;
|
|
839
1193
|
return isPrimaryKey ? `${query} PRIMARY KEY` : query;
|
|
840
1194
|
}
|
|
1195
|
+
/**
|
|
1196
|
+
* Statement to add a new column to a specified table.
|
|
1197
|
+
*
|
|
1198
|
+
* @param columnTable - The name of the table to which the column will be added.
|
|
1199
|
+
* @param entry - The column definition including the column name and its data type. See {@link columnDefinition()}
|
|
1200
|
+
* @return The SQL statement as a string.
|
|
1201
|
+
*/
|
|
841
1202
|
createColumn(columnTable, entry) {
|
|
842
1203
|
return `ALTER TABLE ${columnTable} ADD ${entry};`;
|
|
843
1204
|
}
|
|
1205
|
+
/**
|
|
1206
|
+
* Statement to rename a column in an existing table.
|
|
1207
|
+
*
|
|
1208
|
+
* @param tableName - The name of the table containing the column to rename.
|
|
1209
|
+
* @param oldColumnName - The current name of the column to be renamed.
|
|
1210
|
+
* @param newColumnName - The new name for the column.
|
|
1211
|
+
* @return The SQL statement as a string.
|
|
1212
|
+
*/
|
|
844
1213
|
renameColumn(tableName, oldColumnName, newColumnName) {
|
|
845
1214
|
return `ALTER TABLE ${tableName} RENAME COLUMN ${oldColumnName} TO ${newColumnName};`;
|
|
846
1215
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
1216
|
+
/**
|
|
1217
|
+
* Statement to drop a column from a specified table.
|
|
1218
|
+
*
|
|
1219
|
+
* @param tableName - The name of the table from which the column will be dropped.
|
|
1220
|
+
* @param columnName - The name of the column to be dropped.
|
|
1221
|
+
* @return The SQL statement as a string.
|
|
1222
|
+
*/
|
|
850
1223
|
dropColumn(tableName, columnName) {
|
|
851
1224
|
return `ALTER TABLE ${tableName} DROP COLUMN ${columnName};`;
|
|
852
1225
|
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Statement to return data from a table.
|
|
1228
|
+
*
|
|
1229
|
+
* @param tableName - The name of the database table to select data from.
|
|
1230
|
+
* @param select - An array of column names to include in the SELECT clause.
|
|
1231
|
+
* @param where - An optional condition.
|
|
1232
|
+
* @return The SQL statement as a string.
|
|
1233
|
+
*/
|
|
853
1234
|
select(tableName, select, where) {
|
|
854
1235
|
let query = `SELECT ${select.join(",")} FROM ${tableName}`;
|
|
855
1236
|
if (where)
|
|
856
1237
|
query += ` WHERE ${where}`;
|
|
857
1238
|
return `${query};`;
|
|
858
1239
|
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Statement to insert data into a table.
|
|
1242
|
+
*
|
|
1243
|
+
* @param tableName - The name of the table where the data will be inserted.
|
|
1244
|
+
* @param content - The value query. See {@link insertValues()}.
|
|
1245
|
+
* @return The SQL statement as a string.
|
|
1246
|
+
*/
|
|
859
1247
|
insert(tableName, content) {
|
|
860
1248
|
return `INSERT INTO ${tableName} ${content};`;
|
|
861
1249
|
}
|
|
1250
|
+
/**
|
|
1251
|
+
* A SQL insert query string. See {@link insert()}.
|
|
1252
|
+
*
|
|
1253
|
+
* @param keys - An array of column names to be used in the query.
|
|
1254
|
+
* @param [valueString] - An optional preformatted value string to be included in the query.
|
|
1255
|
+
* If not provided, the method generates a default placeholder string with "?" for each key.
|
|
1256
|
+
* @return The SQL statement as a string.
|
|
1257
|
+
*/
|
|
862
1258
|
insertValues(keys, valueString) {
|
|
863
1259
|
return `(${keys}) ${valueString ?? `VALUES (${keys.map(() => "?").join(",")})`}`;
|
|
864
1260
|
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Returns a SQL query string to create a migration table.
|
|
1263
|
+
* The table is used to store migration data, specifically the version of the migration.
|
|
1264
|
+
*
|
|
1265
|
+
* @return The SQL query string for creating the migration table.
|
|
1266
|
+
*/
|
|
865
1267
|
migrationTableQuery() {
|
|
866
1268
|
return this.createTable(MIGRATION_DATA_TABLE_NAME, [
|
|
867
|
-
this.columnDefinition("version", this.
|
|
1269
|
+
this.columnDefinition("version", this.types.number, false, "0")
|
|
868
1270
|
]);
|
|
869
1271
|
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Fetches the foreign keys for the specified table.
|
|
1274
|
+
* This method is only used if {@link canInspectForeignKeys} is true
|
|
1275
|
+
*
|
|
1276
|
+
* @param tableName - The name of the table for which to retrieve foreign key information.
|
|
1277
|
+
* @return {Promise<ForeignKeyInfo[]>} A promise that resolves to an array of {@link ForeignKeyInfo}.
|
|
1278
|
+
*/
|
|
870
1279
|
async getForeignKeys(tableName) {
|
|
871
1280
|
throw new Error("Inspecting foreign keys is not supported!");
|
|
872
1281
|
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Ensures that the migration table exists in the database.
|
|
1284
|
+
* If the table does not exist, it creates the table using the defined migration table query.
|
|
1285
|
+
*
|
|
1286
|
+
* @return A promise that resolves when the migration table is created successfully.
|
|
1287
|
+
*/
|
|
873
1288
|
async createMigrationTableIfNeeded() {
|
|
874
1289
|
await this.db.runMultipleWriteStatements(this.migrationTableQuery());
|
|
875
1290
|
}
|
|
876
|
-
|
|
1291
|
+
/**
|
|
1292
|
+
* Retrieves the current version from the migration data table.
|
|
1293
|
+
*
|
|
1294
|
+
* @return A promise that resolves to the current version number. Returns 0 if no version is found.
|
|
1295
|
+
*/
|
|
877
1296
|
async getVersion() {
|
|
878
1297
|
await this.createMigrationTableIfNeeded();
|
|
879
1298
|
const query = this.select(MIGRATION_DATA_TABLE_NAME, ["version"]);
|
|
880
1299
|
const data = await this.db.runGetStatement(query);
|
|
881
1300
|
return data.length ? data[0].version : 0;
|
|
882
1301
|
}
|
|
883
|
-
|
|
1302
|
+
/**
|
|
1303
|
+
* Updates the migration version in the database. If the version entry does not exist, it inserts a new one;
|
|
1304
|
+
* if it exists, it updates the version to the specified value.
|
|
1305
|
+
*
|
|
1306
|
+
* @param newVersion - The new migration version to set in the database.
|
|
1307
|
+
* @return A promise that resolves when the operation is complete.
|
|
1308
|
+
*/
|
|
884
1309
|
async setVersion(newVersion) {
|
|
885
1310
|
const query = `${this.migrationTableQuery()};
|
|
886
1311
|
INSERT INTO ${MIGRATION_DATA_TABLE_NAME} (version) VALUES ${newVersion} WHERE NOT EXISTS (SELECT 1 FROM ${MIGRATION_DATA_TABLE_NAME})
|
|
@@ -893,8 +1318,16 @@ UPDATE ${MIGRATION_DATA_TABLE_NAME} SET version = ${newVersion} WHERE EXISTS (SE
|
|
|
893
1318
|
|
|
894
1319
|
// src/lib/dialects/SqliteDialect.ts
|
|
895
1320
|
var SqliteDialect = class extends DefaultSql {
|
|
896
|
-
|
|
897
|
-
|
|
1321
|
+
types = {
|
|
1322
|
+
string: "TEXT",
|
|
1323
|
+
number: "INTEGER",
|
|
1324
|
+
bigint: "INTEGER",
|
|
1325
|
+
boolean: "INTEGER",
|
|
1326
|
+
date: "INTEGER",
|
|
1327
|
+
time: "INTEGER",
|
|
1328
|
+
dateTime: "INTEGER",
|
|
1329
|
+
null: "NULL"
|
|
1330
|
+
};
|
|
898
1331
|
canAlterForeignKeys = false;
|
|
899
1332
|
canAlterPrimaryKey = false;
|
|
900
1333
|
canInspectForeignKeys = true;
|
|
@@ -907,21 +1340,30 @@ var SqliteDialect = class extends DefaultSql {
|
|
|
907
1340
|
}
|
|
908
1341
|
async getColumnInformation(tableName) {
|
|
909
1342
|
const data = await this.db.runGetStatement(`PRAGMA table_info(${tableName})`);
|
|
910
|
-
|
|
911
|
-
|
|
1343
|
+
const output = {};
|
|
1344
|
+
for (const entry of data) {
|
|
1345
|
+
output[entry["name"]] = {
|
|
912
1346
|
name: entry["name"],
|
|
913
1347
|
type: entry["type"],
|
|
914
1348
|
defaultValue: entry["dflt_value"],
|
|
915
1349
|
isPrimaryKey: entry["pk"] == "1"
|
|
916
1350
|
};
|
|
917
|
-
}
|
|
1351
|
+
}
|
|
1352
|
+
return output;
|
|
918
1353
|
}
|
|
919
|
-
formatValueToSql(value) {
|
|
920
|
-
|
|
1354
|
+
formatValueToSql(value, type) {
|
|
1355
|
+
function toDate(value2) {
|
|
1356
|
+
return value2 instanceof Date ? value2 : new Date(value2);
|
|
1357
|
+
}
|
|
1358
|
+
switch (type) {
|
|
921
1359
|
case "boolean":
|
|
922
1360
|
return value ? "1" : "0";
|
|
1361
|
+
case "date":
|
|
1362
|
+
case "time":
|
|
1363
|
+
case "dateTime":
|
|
1364
|
+
return toDate(value).getTime().toString();
|
|
923
1365
|
default:
|
|
924
|
-
return
|
|
1366
|
+
return super.formatValueToSql(value, type);
|
|
925
1367
|
}
|
|
926
1368
|
}
|
|
927
1369
|
async getForeignKeys(tableName) {
|
|
@@ -932,8 +1374,8 @@ var SqliteDialect = class extends DefaultSql {
|
|
|
932
1374
|
fromColumn: entry["from"],
|
|
933
1375
|
toTable: entry["table"],
|
|
934
1376
|
toColumn: entry["to"],
|
|
935
|
-
|
|
936
|
-
|
|
1377
|
+
onUpdate: entry["on_update"] ?? "NO ACTION",
|
|
1378
|
+
onDelete: entry["on_delete"] ?? "NO ACTION"
|
|
937
1379
|
};
|
|
938
1380
|
});
|
|
939
1381
|
}
|
|
@@ -966,14 +1408,17 @@ var PostgresDialect = class extends DefaultSql {
|
|
|
966
1408
|
}
|
|
967
1409
|
async getColumnInformation(tableName) {
|
|
968
1410
|
const data = await this.db.runGetStatement(`SELECT column_name, data_type, column_default FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '${tableName}';`);
|
|
969
|
-
|
|
970
|
-
|
|
1411
|
+
const output = {};
|
|
1412
|
+
for (const entry of data) {
|
|
1413
|
+
output[entry["name"]] = {
|
|
971
1414
|
name: entry["column_name"],
|
|
972
1415
|
type: entry["data_type"],
|
|
1416
|
+
maxLength: 0,
|
|
973
1417
|
defaultValue: entry["column_default"],
|
|
974
1418
|
isPrimaryKey: false
|
|
975
1419
|
};
|
|
976
|
-
}
|
|
1420
|
+
}
|
|
1421
|
+
return output;
|
|
977
1422
|
}
|
|
978
1423
|
addPrimaryKey(tableName, columnName) {
|
|
979
1424
|
return `ALTER TABLE ${tableName} ADD PRIMARY KEY (${columnName});
|
|
@@ -1018,18 +1463,21 @@ var MsSqlDialect = class extends DefaultSql {
|
|
|
1018
1463
|
});
|
|
1019
1464
|
}
|
|
1020
1465
|
async getTableNames() {
|
|
1021
|
-
return await this.db.runGetStatement("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'");
|
|
1466
|
+
return (await this.db.runGetStatement("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'")).map((entry) => entry.name);
|
|
1022
1467
|
}
|
|
1023
1468
|
async getColumnInformation(tableName) {
|
|
1024
1469
|
const data = await this.db.runGetStatement(`SELECT COLUMN_NAME, DATA_TYPE, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = N'${tableName};'`);
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1470
|
+
const output = {};
|
|
1471
|
+
for (const entry of data) {
|
|
1472
|
+
output[entry["name"]] = {
|
|
1473
|
+
name: entry["name"],
|
|
1028
1474
|
type: entry["DATA_TYPE"],
|
|
1475
|
+
maxLength: 0,
|
|
1029
1476
|
defaultValue: entry["COLUMN_DEFAULT"],
|
|
1030
|
-
isPrimaryKey:
|
|
1477
|
+
isPrimaryKey: entry["pk"] == "1"
|
|
1031
1478
|
};
|
|
1032
|
-
}
|
|
1479
|
+
}
|
|
1480
|
+
return output;
|
|
1033
1481
|
}
|
|
1034
1482
|
};
|
|
1035
1483
|
|
|
@@ -1039,9 +1487,26 @@ var MySqlDialect = class extends DefaultSql {
|
|
|
1039
1487
|
canAlterPrimaryKey = true;
|
|
1040
1488
|
canInspectForeignKeys = true;
|
|
1041
1489
|
canInspectPrimaryKey = true;
|
|
1490
|
+
types = {
|
|
1491
|
+
string: "VARCHAR",
|
|
1492
|
+
number: "INT",
|
|
1493
|
+
bigint: "BIGINT",
|
|
1494
|
+
boolean: "BOOLEAN",
|
|
1495
|
+
date: "DATE",
|
|
1496
|
+
time: "TIME",
|
|
1497
|
+
dateTime: "DATETIME",
|
|
1498
|
+
null: "NULL"
|
|
1499
|
+
};
|
|
1500
|
+
getSqlType(dataType, columnInfo) {
|
|
1501
|
+
switch (dataType) {
|
|
1502
|
+
case "string":
|
|
1503
|
+
return this.types[dataType] + `(${columnInfo?.maxLength})`;
|
|
1504
|
+
default:
|
|
1505
|
+
return super.getSqlType(dataType, columnInfo);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1042
1508
|
changeForeignKeysState(enabled) {
|
|
1043
|
-
return `SET FOREIGN_KEY_CHECKS ${enabled ? "1" : "0"}
|
|
1044
|
-
;`;
|
|
1509
|
+
return `SET FOREIGN_KEY_CHECKS ${enabled ? "1" : "0"};`;
|
|
1045
1510
|
}
|
|
1046
1511
|
addForeignKey(fromTableName, foreignKey) {
|
|
1047
1512
|
return `ALTER TABLE ${fromTableName} ADD ${foreignKey};`;
|
|
@@ -1067,18 +1532,22 @@ var MySqlDialect = class extends DefaultSql {
|
|
|
1067
1532
|
});
|
|
1068
1533
|
}
|
|
1069
1534
|
async getTableNames() {
|
|
1070
|
-
|
|
1535
|
+
const t = await this.db.runGetStatement("SELECT table_name FROM information_schema.tables;");
|
|
1536
|
+
return (await this.db.runGetStatement("SELECT table_name FROM information_schema.tables;")).map((entry) => entry.name);
|
|
1071
1537
|
}
|
|
1072
1538
|
async getColumnInformation(tableName) {
|
|
1073
1539
|
const data = await this.db.runGetStatement(`SHOW COLUMNS FROM ${tableName};`);
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1540
|
+
const output = {};
|
|
1541
|
+
for (const entry of data) {
|
|
1542
|
+
output[entry["name"]] = {
|
|
1543
|
+
name: entry["name"],
|
|
1544
|
+
type: entry["type"],
|
|
1545
|
+
maxLength: 0,
|
|
1546
|
+
defaultValue: entry["dflt_value"],
|
|
1547
|
+
isPrimaryKey: entry["pk"] == "1"
|
|
1080
1548
|
};
|
|
1081
|
-
}
|
|
1549
|
+
}
|
|
1550
|
+
return output;
|
|
1082
1551
|
}
|
|
1083
1552
|
};
|
|
1084
1553
|
|
|
@@ -1159,8 +1628,10 @@ async function rollback(db, dbInstructions, toVersion) {
|
|
|
1159
1628
|
}
|
|
1160
1629
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1161
1630
|
0 && (module.exports = {
|
|
1162
|
-
|
|
1631
|
+
DataType,
|
|
1163
1632
|
ForeignKey,
|
|
1633
|
+
TableClass,
|
|
1634
|
+
TableObj,
|
|
1164
1635
|
prepareAndRunMigration,
|
|
1165
1636
|
prepareMigration,
|
|
1166
1637
|
rollback,
|