sqlmorpheus 0.2.1 → 0.2.3
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 +178 -17
- package/dist/index.d.ts +178 -17
- package/dist/index.js +471 -163
- package/dist/index.mjs +471 -163
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -21,42 +21,67 @@ var LoggerClass = class {
|
|
|
21
21
|
};
|
|
22
22
|
var Logger = new LoggerClass();
|
|
23
23
|
|
|
24
|
-
// src/lib/tableInfo/TableInfo.ts
|
|
25
|
-
var TABLE_INFO_PROPERTY_NAME = Symbol("tableInfo");
|
|
26
|
-
function getTableInfo(table) {
|
|
27
|
-
let metadata = table[Symbol.metadata] ?? table.prototype?.[Symbol.metadata];
|
|
28
|
-
if (!metadata) {
|
|
29
|
-
metadata = {};
|
|
30
|
-
table[Symbol.metadata] = metadata;
|
|
31
|
-
}
|
|
32
|
-
return getTableInfoFromMetadata(metadata);
|
|
33
|
-
}
|
|
34
|
-
function getTableInfoFromMetadata(metadata) {
|
|
35
|
-
if (!metadata[TABLE_INFO_PROPERTY_NAME])
|
|
36
|
-
metadata[TABLE_INFO_PROPERTY_NAME] = { primaryKey: "", foreignKeys: [] };
|
|
37
|
-
return metadata[TABLE_INFO_PROPERTY_NAME];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
24
|
// src/lib/tableInfo/TableObj.ts
|
|
41
25
|
var TableObj = class _TableObj {
|
|
42
26
|
IS_TABLE_OBJ = true;
|
|
43
27
|
tableName;
|
|
44
|
-
|
|
45
|
-
|
|
28
|
+
tableStructure = {
|
|
29
|
+
table: "",
|
|
46
30
|
primaryKey: void 0,
|
|
47
|
-
|
|
48
|
-
|
|
31
|
+
columns: {},
|
|
32
|
+
foreignKeys: []
|
|
49
33
|
};
|
|
50
|
-
|
|
34
|
+
static getEmptyColumnEntry(key) {
|
|
35
|
+
return {
|
|
36
|
+
name: key,
|
|
37
|
+
type: "",
|
|
38
|
+
defaultValue: "",
|
|
39
|
+
isPrimaryKey: false
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
static getColumnEntry(key, value) {
|
|
43
|
+
let dataType = typeof value;
|
|
44
|
+
switch (typeof value) {
|
|
45
|
+
case "function":
|
|
46
|
+
return null;
|
|
47
|
+
case "object":
|
|
48
|
+
if (value instanceof Date) {
|
|
49
|
+
dataType = "date";
|
|
50
|
+
break;
|
|
51
|
+
} else if (value !== null) {
|
|
52
|
+
Logger.warn(`${key} was skipped because its type (${typeof value}) is not supported`);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
dataType = "string";
|
|
56
|
+
break;
|
|
57
|
+
case "undefined":
|
|
58
|
+
case "symbol":
|
|
59
|
+
Logger.warn(`${key} was skipped because its type (${typeof value}) is not supported`);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
..._TableObj.getEmptyColumnEntry(key),
|
|
64
|
+
inputType: dataType,
|
|
65
|
+
inputDefaultValue: value
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
constructor(tableName, defaultValues) {
|
|
51
69
|
this.tableName = tableName;
|
|
52
|
-
|
|
70
|
+
for (const key in defaultValues) {
|
|
71
|
+
const value = defaultValues[key];
|
|
72
|
+
const columnEntry = _TableObj.getColumnEntry(key, value);
|
|
73
|
+
if (!columnEntry)
|
|
74
|
+
continue;
|
|
75
|
+
this.tableStructure.columns[key] = columnEntry;
|
|
76
|
+
}
|
|
53
77
|
}
|
|
54
78
|
primaryKey(key) {
|
|
55
|
-
this.
|
|
79
|
+
this.tableStructure.primaryKey = key.toString();
|
|
80
|
+
this.tableStructure.columns[key.toString()].isPrimaryKey = true;
|
|
56
81
|
return this;
|
|
57
82
|
}
|
|
58
83
|
foreignKey(fromColumn, toTable, toColumn, options) {
|
|
59
|
-
this.
|
|
84
|
+
this.tableStructure.foreignKeys.push({
|
|
60
85
|
fromTable: this.tableName,
|
|
61
86
|
fromColumn: fromColumn.toString(),
|
|
62
87
|
toTable: toTable.tableName,
|
|
@@ -67,17 +92,44 @@ var TableObj = class _TableObj {
|
|
|
67
92
|
return this;
|
|
68
93
|
}
|
|
69
94
|
dataType(column, type) {
|
|
70
|
-
this.
|
|
95
|
+
this.tableStructure.columns[column.toString()].inputType = type;
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
maxCharacterLength(column, max) {
|
|
99
|
+
this.tableStructure.columns[column.toString()].maxLength = max;
|
|
71
100
|
return this;
|
|
72
101
|
}
|
|
73
102
|
static create(tableName, columns) {
|
|
74
103
|
return new _TableObj(tableName, columns);
|
|
75
104
|
}
|
|
76
|
-
static
|
|
105
|
+
static isTableObj(obj) {
|
|
77
106
|
return !!obj?.IS_TABLE_OBJ;
|
|
78
107
|
}
|
|
79
108
|
};
|
|
80
109
|
|
|
110
|
+
// src/lib/tableInfo/getTableStructure.ts
|
|
111
|
+
var TABLE_INFO_PROPERTY_NAME = Symbol("tableInfo");
|
|
112
|
+
function getInputColumnInfo(table, context) {
|
|
113
|
+
const metadata = context?.metadata ? getTableStructureFromMetadata(context.metadata) : getTableStructure(table);
|
|
114
|
+
const key = context.name ?? context;
|
|
115
|
+
if (!metadata.columns.hasOwnProperty(key))
|
|
116
|
+
metadata.columns[key] = TableObj.getEmptyColumnEntry(key);
|
|
117
|
+
return metadata.columns[key];
|
|
118
|
+
}
|
|
119
|
+
function getTableStructure(table) {
|
|
120
|
+
let metadata = table[Symbol.metadata] ?? table.prototype?.[Symbol.metadata];
|
|
121
|
+
if (!metadata) {
|
|
122
|
+
metadata = {};
|
|
123
|
+
table[Symbol.metadata] = metadata;
|
|
124
|
+
}
|
|
125
|
+
return getTableStructureFromMetadata(metadata);
|
|
126
|
+
}
|
|
127
|
+
function getTableStructureFromMetadata(metadata) {
|
|
128
|
+
if (!metadata[TABLE_INFO_PROPERTY_NAME])
|
|
129
|
+
metadata[TABLE_INFO_PROPERTY_NAME] = { table: "", primaryKey: "", foreignKeys: [], columns: {} };
|
|
130
|
+
return metadata[TABLE_INFO_PROPERTY_NAME];
|
|
131
|
+
}
|
|
132
|
+
|
|
81
133
|
// src/lib/TableStructureGenerator.ts
|
|
82
134
|
var TableStructureGenerator = class {
|
|
83
135
|
dialect;
|
|
@@ -92,75 +144,31 @@ var TableStructureGenerator = class {
|
|
|
92
144
|
* @return An object where keys are table names, and values are their corresponding table structures.
|
|
93
145
|
*/
|
|
94
146
|
generateTableStructure() {
|
|
95
|
-
const
|
|
147
|
+
const tables = {};
|
|
96
148
|
for (const table of this.dbInstructions.tables) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
149
|
+
let structure;
|
|
150
|
+
if (TableObj.isTableObj(table)) {
|
|
151
|
+
structure = table.tableStructure;
|
|
152
|
+
tables[table.tableName] = table.tableStructure;
|
|
153
|
+
} else {
|
|
100
154
|
const classTable = table;
|
|
101
|
-
|
|
155
|
+
structure = getTableStructure(classTable);
|
|
156
|
+
tables[classTable.name] = getTableStructure(classTable);
|
|
102
157
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const tableInfo = obj.tableInfo;
|
|
108
|
-
tables[tableName] = {
|
|
109
|
-
table: tableName,
|
|
110
|
-
primaryKey: tableInfo?.primaryKey,
|
|
111
|
-
columns: this.getColumns(obj.columns, tableInfo),
|
|
112
|
-
foreignKeys: tableInfo?.foreignKeys
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
return tables;
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Retrieves the columns for a table
|
|
119
|
-
*
|
|
120
|
-
* @param obj - The table class interface object containing properties that represent the table's fields.
|
|
121
|
-
* @param tableInfo - Optional metadata about the table. Can be undefined.
|
|
122
|
-
* @return An array of {@link ColumnInfo} representing the detailed column information.
|
|
123
|
-
*/
|
|
124
|
-
getColumns(obj, tableInfo) {
|
|
125
|
-
const columns = [];
|
|
126
|
-
for (const property in obj) {
|
|
127
|
-
const propertyKey = property;
|
|
128
|
-
const value = obj[propertyKey];
|
|
129
|
-
const columnData = {
|
|
130
|
-
name: property,
|
|
131
|
-
isPrimaryKey: property == tableInfo?.primaryKey,
|
|
132
|
-
type: this.dialect.types.string,
|
|
133
|
-
defaultValue: this.dialect.types.null
|
|
134
|
-
};
|
|
135
|
-
let dataType = tableInfo?.dataTypes?.[property] ?? typeof value;
|
|
136
|
-
switch (dataType) {
|
|
137
|
-
case "function":
|
|
138
|
-
continue;
|
|
139
|
-
case "object":
|
|
140
|
-
if (value instanceof Date) {
|
|
141
|
-
dataType = "date";
|
|
142
|
-
break;
|
|
143
|
-
} else {
|
|
144
|
-
Logger.warn(`${property} was skipped because its type (${typeof value}) is not supported`);
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
case "undefined":
|
|
148
|
-
case "symbol":
|
|
149
|
-
Logger.warn(`${property} was skipped because its type (${typeof value}) is not supported`);
|
|
150
|
-
continue;
|
|
158
|
+
for (const key in structure.columns) {
|
|
159
|
+
const entry = structure.columns[key];
|
|
160
|
+
entry.defaultValue = entry.inputDefaultValue === null ? this.dialect.types.null : this.dialect.formatValueToSql(entry.inputDefaultValue, entry.inputType);
|
|
161
|
+
entry.type = this.dialect.getSqlType(entry.inputType, entry);
|
|
151
162
|
}
|
|
152
|
-
columnData.defaultValue = value === null ? this.dialect.types.null : this.dialect.formatValueToSql(value, dataType);
|
|
153
|
-
columnData.type = this.dialect.types[dataType];
|
|
154
|
-
columns.push(columnData);
|
|
155
163
|
}
|
|
156
|
-
return
|
|
164
|
+
return tables;
|
|
157
165
|
}
|
|
158
166
|
};
|
|
159
167
|
|
|
160
168
|
// src/lib/exceptions/NotAllowedException.ts
|
|
161
169
|
var NotAllowedException = class extends Error {
|
|
162
170
|
constructor(entries) {
|
|
163
|
-
let message = "Some destructive changes need to be confirmed.
|
|
171
|
+
let message = "Some destructive changes need to be confirmed. To confirm, add to preMigration():\n";
|
|
164
172
|
for (const entry of entries) {
|
|
165
173
|
message += ` migrations.allowMigration(${entry.version}, ${entry.tableName}, "${entry.type}");
|
|
166
174
|
`;
|
|
@@ -178,6 +186,13 @@ var Migrations = class {
|
|
|
178
186
|
migrationDataForOldTableNames = {};
|
|
179
187
|
alwaysAllowed = {};
|
|
180
188
|
notAllowedChanges = [];
|
|
189
|
+
/**
|
|
190
|
+
* Resets the migration state to its initial values.
|
|
191
|
+
* Only used internally.
|
|
192
|
+
*
|
|
193
|
+
* @param dbInstructions - The instructions containing database metadata and migration details.
|
|
194
|
+
* @param fromVersion - The version of the actual database.
|
|
195
|
+
*/
|
|
181
196
|
reset(dbInstructions, fromVersion) {
|
|
182
197
|
this.fromVersion = fromVersion;
|
|
183
198
|
this.toVersion = dbInstructions.version;
|
|
@@ -186,10 +201,23 @@ var Migrations = class {
|
|
|
186
201
|
this.alwaysAllowed = dbInstructions.alwaysAllowedMigrations ?? {};
|
|
187
202
|
this.notAllowedChanges = [];
|
|
188
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Retrieves the migration instructions for the specified table.
|
|
206
|
+
* @see getEntryFromTableName
|
|
207
|
+
*
|
|
208
|
+
* @param table - The {@link TableInput} object from which the table name will be retrieved.
|
|
209
|
+
* @return The migration instructions corresponding to the given table input.
|
|
210
|
+
*/
|
|
189
211
|
getEntry(table) {
|
|
190
212
|
const tableName = this.getTableName(table);
|
|
191
213
|
return this.getEntryFromTableName(tableName);
|
|
192
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Retrieves the migration instructions for the specified table.
|
|
217
|
+
*
|
|
218
|
+
* @param tableName - The name of the table for which the entry is to be retrieved or created.
|
|
219
|
+
* @return The migration instructions corresponding to the given table input.
|
|
220
|
+
*/
|
|
193
221
|
getEntryFromTableName(tableName) {
|
|
194
222
|
if (!this.migrationData.hasOwnProperty(tableName)) {
|
|
195
223
|
this.migrationData[tableName] = {
|
|
@@ -201,17 +229,39 @@ var Migrations = class {
|
|
|
201
229
|
}
|
|
202
230
|
return this.migrationData[tableName];
|
|
203
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Retrieves the table name from the given table input.
|
|
234
|
+
*
|
|
235
|
+
* @param table - The {@link TableInput} object.
|
|
236
|
+
* @return The name of the table derived from either {@link TableObj} or a class.
|
|
237
|
+
*/
|
|
204
238
|
getTableName(table) {
|
|
205
|
-
return TableObj.
|
|
239
|
+
return TableObj.isTableObj(table) ? table.tableName : table.name;
|
|
206
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Checks whether a provided version is relevant based on database version ({@link fromVersion}) and target version ({@link toVersion}).
|
|
243
|
+
* Also makes sure that version checks happen in order.
|
|
244
|
+
*
|
|
245
|
+
* @param version - The version to check for relevance.
|
|
246
|
+
* @return Returns true if the version is relevant, otherwise false.
|
|
247
|
+
* @throws Error if versions are not properly ordered
|
|
248
|
+
*/
|
|
207
249
|
versionIsRelevant(version) {
|
|
208
|
-
if (version
|
|
250
|
+
if (version <= this.fromVersion || version > this.toVersion)
|
|
209
251
|
return false;
|
|
210
252
|
if (version < this.lastUsedVersion)
|
|
211
253
|
throw new Error("Migrations in preMigration() have to be ordered by version (beginning with the lowest version).");
|
|
212
254
|
this.lastUsedVersion = version;
|
|
213
255
|
return true;
|
|
214
256
|
}
|
|
257
|
+
/**
|
|
258
|
+
* Verifies the renaming tasks specified in {@link DatabaseInstructions.preMigration} that were considered are valid.
|
|
259
|
+
* Called after all SQL queries have been generated.
|
|
260
|
+
* Only used internally.
|
|
261
|
+
*
|
|
262
|
+
* @param newTables - The table structures extracted from {@link DatabaseInstructions.tables}.
|
|
263
|
+
* @throws Error if a renaming task is invalid.
|
|
264
|
+
*/
|
|
215
265
|
verifyRenamingTasks(newTables) {
|
|
216
266
|
for (const tableName in this.migrationData) {
|
|
217
267
|
const migrationEntry = this.migrationData[tableName];
|
|
@@ -228,11 +278,18 @@ var Migrations = class {
|
|
|
228
278
|
const newColumnName = renamingData.newName;
|
|
229
279
|
if (oldColumnName == newColumnName)
|
|
230
280
|
return new Error(`You set column ${tableName}.${oldColumnName} to be renamed to itself!`);
|
|
231
|
-
else if (!newTables[tableName]?.columns
|
|
281
|
+
else if (!newTables[tableName]?.columns[newColumnName])
|
|
232
282
|
return new Error(`You set column ${tableName}.${oldColumnName} to be renamed to ${tableName}.${newColumnName}. But ${tableName}.${newColumnName} does not exist in your structure.`);
|
|
233
283
|
}
|
|
234
284
|
}
|
|
235
285
|
}
|
|
286
|
+
/**
|
|
287
|
+
* Making sure all considered calls to {@link allowMigration()} where necessary to prevent input mistakes.
|
|
288
|
+
* Called after all SQL queries have been generated.
|
|
289
|
+
* Only used internally.
|
|
290
|
+
*
|
|
291
|
+
* @throws Error if unnecessary calls were discovered. All discovered calls are collected and thrown as one Error.
|
|
292
|
+
*/
|
|
236
293
|
verifyAllowedMigrations() {
|
|
237
294
|
if (this.notAllowedChanges.length)
|
|
238
295
|
return new NotAllowedException(this.notAllowedChanges);
|
|
@@ -248,6 +305,14 @@ var Migrations = class {
|
|
|
248
305
|
if (errorMsg)
|
|
249
306
|
return new Error(errorMsg);
|
|
250
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* Called by {@link MigrationManager} to check if a specific destructive query is allowed.
|
|
310
|
+
* Only used internally.
|
|
311
|
+
*
|
|
312
|
+
* @see allowMigration()
|
|
313
|
+
* @param tableName - The name of the table to check the migration against.
|
|
314
|
+
* @param type - The type of destructive migration being checked.
|
|
315
|
+
*/
|
|
251
316
|
compareWithAllowedMigration(tableName, type) {
|
|
252
317
|
const migrationEntry = this.migrationData[tableName];
|
|
253
318
|
if (!migrationEntry?.allowedMigrations[type]) {
|
|
@@ -256,41 +321,114 @@ var Migrations = class {
|
|
|
256
321
|
} else if (migrationEntry)
|
|
257
322
|
migrationEntry.usedMigrations[type] = true;
|
|
258
323
|
}
|
|
324
|
+
/**
|
|
325
|
+
* Retrieves the migration data containing instructions for data migration.
|
|
326
|
+
* Only used internally.
|
|
327
|
+
*
|
|
328
|
+
* @return An object where each key corresponds to the table name,
|
|
329
|
+
* and the value is the associated migration instructions.
|
|
330
|
+
*/
|
|
259
331
|
getMigrationData() {
|
|
260
332
|
return this.migrationData;
|
|
261
333
|
}
|
|
334
|
+
/**
|
|
335
|
+
* Iterates over renamed columns for the specified table and executes a callback for each pair of old and new column names.
|
|
336
|
+
* Only used internally.
|
|
337
|
+
*
|
|
338
|
+
* @param tableName - Name of the table whose renamed columns should be processed.
|
|
339
|
+
* @param callback - A function to execute for each renamed column, receiving the old and new column names as arguments.
|
|
340
|
+
*/
|
|
262
341
|
loopRenamedColumns(tableName, callback) {
|
|
263
342
|
const migrationEntry = this.migrationData[tableName];
|
|
264
343
|
for (const renamingData of migrationEntry.renamedColumns) {
|
|
265
344
|
callback(renamingData.oldName, renamingData.newName);
|
|
266
345
|
}
|
|
267
346
|
}
|
|
347
|
+
/**
|
|
348
|
+
* Considers the renaming of a table and returns its (newest) name after the migration or
|
|
349
|
+
* the original name if no renaming is queued.
|
|
350
|
+
* Only used internally.
|
|
351
|
+
*
|
|
352
|
+
* @see getOldTableName()
|
|
353
|
+
* @param tableName - The current table name in the database.
|
|
354
|
+
* @return The newest table name if available, otherwise the current table name.
|
|
355
|
+
*/
|
|
268
356
|
getNewestTableName(tableName) {
|
|
269
357
|
return this.migrationDataForOldTableNames[tableName]?.tableRenaming?.newName ?? tableName;
|
|
270
358
|
}
|
|
359
|
+
/**
|
|
360
|
+
* Retrieves the current table name in the database.
|
|
361
|
+
* Only used internally.
|
|
362
|
+
*
|
|
363
|
+
* @see getNewestTableName()
|
|
364
|
+
* @param tableName - The name of the newest table name.
|
|
365
|
+
* @return The current table name in the database.
|
|
366
|
+
*/
|
|
271
367
|
getOldTableName(tableName) {
|
|
272
368
|
return this.migrationData[tableName]?.tableRenaming?.oldName ?? tableName;
|
|
273
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* Considers the renaming of a column in a table and returns its (newest) name after the migration or
|
|
372
|
+
* the original name if no renaming is queued.
|
|
373
|
+
* Only used internally.
|
|
374
|
+
*
|
|
375
|
+
* @param tableName - The name of the table containing the column.
|
|
376
|
+
* @param oldColumnName - The current name of the column in the database.
|
|
377
|
+
* @return The newest column name if available, otherwise the original column name.
|
|
378
|
+
*/
|
|
274
379
|
getNewestColumnName(tableName, oldColumnName) {
|
|
275
380
|
const columnNames = this.migrationData[tableName]?.renamedColumns.find((columns) => columns.oldName == oldColumnName);
|
|
276
381
|
return columnNames ? columnNames.newName : oldColumnName;
|
|
277
382
|
}
|
|
383
|
+
/**
|
|
384
|
+
* Determines if the specified table is marked to be recreated.
|
|
385
|
+
* Only used internally.
|
|
386
|
+
*
|
|
387
|
+
* @param tableName The name of the table to check.
|
|
388
|
+
* @return Returns true if the table will be recreated, false otherwise.
|
|
389
|
+
*/
|
|
278
390
|
willBeRecreated(tableName) {
|
|
279
391
|
return this.migrationData[tableName]?.recreate;
|
|
280
392
|
}
|
|
393
|
+
/**
|
|
394
|
+
* Marks a table to be recreated and logs the recreation reason for the user.
|
|
395
|
+
* Only used internally.
|
|
396
|
+
*
|
|
397
|
+
* @param tableName - The name of the table to be marked for recreation.
|
|
398
|
+
* @param reason - The reason why the table is being marked for recreation.
|
|
399
|
+
*/
|
|
281
400
|
internalRecreate(tableName, reason) {
|
|
282
401
|
const entry = this.getEntryFromTableName(tableName);
|
|
283
402
|
entry.recreate = true;
|
|
284
403
|
Logger.log(`Table ${tableName} will be recreated(${reason})!`);
|
|
285
404
|
}
|
|
405
|
+
/**
|
|
406
|
+
* SqlMorpheus only allows destructive statements if it was specifically allowed in {@link DatabaseInstructions.preMigration()}.
|
|
407
|
+
* Use this method to allow a destructive statement for a specific version.
|
|
408
|
+
* This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
|
|
409
|
+
*
|
|
410
|
+
* @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.
|
|
411
|
+
* @param table - Either a {@link TableObj} or a class decorated with @TableClass.
|
|
412
|
+
* @param allowedMigrations - The keys representing the migrations to be allowed. Multiple migrations can be provided
|
|
413
|
+
*/
|
|
286
414
|
allowMigration(version, table, ...allowedMigrations) {
|
|
287
|
-
if (
|
|
415
|
+
if (!this.versionIsRelevant(version))
|
|
288
416
|
return;
|
|
289
417
|
const entry = this.getEntry(table);
|
|
290
418
|
for (const key of allowedMigrations) {
|
|
291
419
|
entry.allowedMigrations[key] = true;
|
|
292
420
|
}
|
|
293
421
|
}
|
|
422
|
+
/**
|
|
423
|
+
* When you change the name of a table, SqlMorpheus will assume the original table was meant to be dropped
|
|
424
|
+
* and the new table was meant to be added, which would lead to data loss.
|
|
425
|
+
* Use this method to mark a table to be renamed instead.
|
|
426
|
+
* This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
|
|
427
|
+
*
|
|
428
|
+
* @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.
|
|
429
|
+
* @param oldTableName - The current name in the database at the time of the migration.
|
|
430
|
+
* @param newTableName - The new table name or its equivalent input format.
|
|
431
|
+
*/
|
|
294
432
|
renameTable(version, oldTableName, newTableName) {
|
|
295
433
|
if (!this.versionIsRelevant(version))
|
|
296
434
|
return;
|
|
@@ -302,6 +440,17 @@ var Migrations = class {
|
|
|
302
440
|
else
|
|
303
441
|
entry.tableRenaming.newName = newTableNameString;
|
|
304
442
|
}
|
|
443
|
+
/**
|
|
444
|
+
* When you change the name of a column, SqlMorpheus will assume the original column was meant to be removed
|
|
445
|
+
* and the new column was meant to be added, which would lead to data loss.
|
|
446
|
+
* Use this method to mark a column to be renamed instead.
|
|
447
|
+
* This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
|
|
448
|
+
*
|
|
449
|
+
* @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.
|
|
450
|
+
* @param table Either a {@link TableObj} or a class decorated with @TableClass containing the column to be renamed.
|
|
451
|
+
* @param oldColumn The current name in the database at the time of the migration.
|
|
452
|
+
* @param newColumn The new name to assign to the column.
|
|
453
|
+
*/
|
|
305
454
|
renameColumn(version, table, oldColumn, newColumn) {
|
|
306
455
|
if (!this.versionIsRelevant(version))
|
|
307
456
|
return;
|
|
@@ -312,6 +461,15 @@ var Migrations = class {
|
|
|
312
461
|
else
|
|
313
462
|
entry.renamedColumns.push({ oldName: oldColumn, newName: newColumn });
|
|
314
463
|
}
|
|
464
|
+
/**
|
|
465
|
+
* Should not be needed most of the time.
|
|
466
|
+
* Marks a table to be recreated
|
|
467
|
+
* (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).
|
|
468
|
+
* This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
|
|
469
|
+
*
|
|
470
|
+
* @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.
|
|
471
|
+
* @param table Either a {@link TableObj} or a class decorated with @TableClass.
|
|
472
|
+
*/
|
|
315
473
|
recreateTable(version, table) {
|
|
316
474
|
if (!this.versionIsRelevant(version))
|
|
317
475
|
return;
|
|
@@ -500,8 +658,9 @@ ${changes.up}
|
|
|
500
658
|
*/
|
|
501
659
|
createTableSql(tableName, columns, foreignKeys) {
|
|
502
660
|
const queryLines = [];
|
|
503
|
-
for (const
|
|
504
|
-
|
|
661
|
+
for (const key in columns) {
|
|
662
|
+
const columnInfo = columns[key];
|
|
663
|
+
queryLines.push(this.dialect.columnDefinition(columnInfo.name, columnInfo.type, columnInfo.isPrimaryKey, columnInfo.defaultValue));
|
|
505
664
|
}
|
|
506
665
|
if (foreignKeys) {
|
|
507
666
|
for (const foreignKeyInfo of foreignKeys) {
|
|
@@ -539,7 +698,11 @@ ${changes.up}
|
|
|
539
698
|
this.migrations.compareWithAllowedMigration(tableName, "continueWithoutRollback");
|
|
540
699
|
const oldColumnList = await this.dialect.getColumnInformation(this.migrations.getOldTableName(tableName));
|
|
541
700
|
const newColumnList = this.newTables[tableName].columns;
|
|
542
|
-
const moveableColumns =
|
|
701
|
+
const moveableColumns = [];
|
|
702
|
+
for (const key in newColumnList) {
|
|
703
|
+
if (oldColumnList.hasOwnProperty(key))
|
|
704
|
+
moveableColumns.push(newColumnList[key].name);
|
|
705
|
+
}
|
|
543
706
|
const backupTableName = `${tableName}__backup`;
|
|
544
707
|
const insertQuery = this.dialect.insert(
|
|
545
708
|
backupTableName,
|
|
@@ -547,7 +710,7 @@ ${changes.up}
|
|
|
547
710
|
);
|
|
548
711
|
const moveDataQuery = insertQuery + "\n" + this.dialect.dropTable(tableName) + "\n" + this.dialect.renameTable(backupTableName, tableName);
|
|
549
712
|
const structure = this.newTables[tableName];
|
|
550
|
-
changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery + "\n";
|
|
713
|
+
changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery + "\n\n";
|
|
551
714
|
const dbTableName = this.migrations.getOldTableName(tableName);
|
|
552
715
|
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";
|
|
553
716
|
}
|
|
@@ -639,6 +802,10 @@ ${changes.up}
|
|
|
639
802
|
const newTableDefinition = this.newTables[tableName];
|
|
640
803
|
const oldColumnList = await this.dialect.getColumnInformation(this.migrations.getOldTableName(tableName));
|
|
641
804
|
const oldPrimaryKey = this.dialect.canInspectPrimaryKey ? this.getPrimaryKeyColumn(oldColumnList) : false;
|
|
805
|
+
const oldColumnNewNameIndex = {};
|
|
806
|
+
for (const key in oldColumnList) {
|
|
807
|
+
oldColumnNewNameIndex[this.migrations.getNewestColumnName(tableName, key)] = oldColumnList[key];
|
|
808
|
+
}
|
|
642
809
|
const newColumnList = newTableDefinition.columns;
|
|
643
810
|
const newPrimaryKey = newTableDefinition.primaryKey;
|
|
644
811
|
if (this.dialect.canInspectPrimaryKey && oldPrimaryKey != newPrimaryKey) {
|
|
@@ -657,25 +824,23 @@ ${changes.up}
|
|
|
657
824
|
this.migrations.internalRecreate(newTableDefinition.table, "Dialect does not support altering primary keys");
|
|
658
825
|
continue;
|
|
659
826
|
}
|
|
660
|
-
for (const
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
);
|
|
827
|
+
for (const key in newColumnList) {
|
|
828
|
+
const newColumn = newColumnList[key];
|
|
829
|
+
const oldColumn = oldColumnNewNameIndex[newColumn.name];
|
|
664
830
|
if (oldColumn == void 0) {
|
|
665
|
-
changes.up += this.dialect.createColumn(tableName, this.dialect.columnDefinition(newColumn.name, newColumn.type, newColumn.
|
|
831
|
+
changes.up += this.dialect.createColumn(tableName, this.dialect.columnDefinition(newColumn.name, newColumn.type, newColumn.isPrimaryKey, newColumn.defaultValue)) + "\n";
|
|
666
832
|
changes.down += this.dialect.dropColumn(tableName, newColumn.name) + "\n";
|
|
667
833
|
} else if (newColumn.type != oldColumn.type || newColumn.defaultValue != oldColumn.defaultValue) {
|
|
668
834
|
this.migrations.internalRecreate(newTableDefinition.table, "Table structure was changed");
|
|
669
835
|
}
|
|
670
836
|
}
|
|
671
|
-
for (const
|
|
672
|
-
const
|
|
673
|
-
|
|
674
|
-
);
|
|
837
|
+
for (const key in oldColumnList) {
|
|
838
|
+
const oldColumn = oldColumnList[key];
|
|
839
|
+
const newColumn = newColumnList[this.migrations.getNewestColumnName(tableName, oldColumn.name)];
|
|
675
840
|
if (newColumn == void 0) {
|
|
676
841
|
this.migrations.compareWithAllowedMigration(tableName, "dropColumn");
|
|
677
842
|
changes.up += this.dialect.dropColumn(tableName, oldColumn.name) + "\n";
|
|
678
|
-
changes.down += this.dialect.createColumn(tableName, this.dialect.columnDefinition(oldColumn.name, oldColumn.type, oldColumn.
|
|
843
|
+
changes.down += this.dialect.createColumn(tableName, this.dialect.columnDefinition(oldColumn.name, oldColumn.type, oldColumn.isPrimaryKey, oldColumn.defaultValue)) + "\n";
|
|
679
844
|
}
|
|
680
845
|
}
|
|
681
846
|
}
|
|
@@ -684,11 +849,12 @@ ${changes.up}
|
|
|
684
849
|
/**
|
|
685
850
|
* Retrieves the name of the primary key column from a list of column information.
|
|
686
851
|
*
|
|
687
|
-
* @param
|
|
852
|
+
* @param columnInfoEntries - An array of ColumnInfo objects containing details about each column.
|
|
688
853
|
* @return The name of the primary key column if found; otherwise, undefined.
|
|
689
854
|
*/
|
|
690
|
-
getPrimaryKeyColumn(
|
|
691
|
-
for (const
|
|
855
|
+
getPrimaryKeyColumn(columnInfoEntries) {
|
|
856
|
+
for (const key in columnInfoEntries) {
|
|
857
|
+
const columnInfo = columnInfoEntries[key];
|
|
692
858
|
if (columnInfo.isPrimaryKey)
|
|
693
859
|
return columnInfo.name;
|
|
694
860
|
}
|
|
@@ -778,15 +944,27 @@ ${changes.down}`, { encoding: "utf-8" });
|
|
|
778
944
|
import "polyfill-symbol-metadata";
|
|
779
945
|
function TableClass(tableName, primaryKey) {
|
|
780
946
|
return (table, context) => {
|
|
781
|
-
const
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
947
|
+
const tableStructure = context?.metadata ? getTableStructureFromMetadata(context.metadata) : getTableStructure(table);
|
|
948
|
+
tableStructure.primaryKey = primaryKey?.toString();
|
|
949
|
+
tableStructure.table = tableName;
|
|
950
|
+
if (tableStructure.foreignKeys) {
|
|
951
|
+
for (const foreignKey of tableStructure.foreignKeys) {
|
|
785
952
|
foreignKey.fromTable = tableName;
|
|
786
953
|
if (foreignKey.toTable == table.name)
|
|
787
954
|
foreignKey.toTable = tableName;
|
|
788
955
|
}
|
|
789
956
|
}
|
|
957
|
+
for (const key in table) {
|
|
958
|
+
const value = table[key];
|
|
959
|
+
const existingEntry = tableStructure.columns[key];
|
|
960
|
+
const defaultEntry = TableObj.getColumnEntry(key, value);
|
|
961
|
+
if (!defaultEntry)
|
|
962
|
+
continue;
|
|
963
|
+
tableStructure.columns[key] = {
|
|
964
|
+
...defaultEntry,
|
|
965
|
+
...existingEntry
|
|
966
|
+
};
|
|
967
|
+
}
|
|
790
968
|
Object.defineProperty(table, "name", { value: tableName, writable: false });
|
|
791
969
|
};
|
|
792
970
|
}
|
|
@@ -795,10 +973,8 @@ function TableClass(tableName, primaryKey) {
|
|
|
795
973
|
import "polyfill-symbol-metadata";
|
|
796
974
|
function DataType(type) {
|
|
797
975
|
return (table, context) => {
|
|
798
|
-
const
|
|
799
|
-
|
|
800
|
-
metadata.dataTypes = {};
|
|
801
|
-
metadata.dataTypes[context.name ?? context] = type;
|
|
976
|
+
const entry = getInputColumnInfo(table, context);
|
|
977
|
+
entry.inputType = type;
|
|
802
978
|
};
|
|
803
979
|
}
|
|
804
980
|
|
|
@@ -806,7 +982,7 @@ function DataType(type) {
|
|
|
806
982
|
import "polyfill-symbol-metadata";
|
|
807
983
|
function ForeignKey(toTable, toColumn, options) {
|
|
808
984
|
return (table, context) => {
|
|
809
|
-
const metadata = context?.metadata ?
|
|
985
|
+
const metadata = context?.metadata ? getTableStructureFromMetadata(context.metadata) : getTableStructure(table);
|
|
810
986
|
if (!metadata.foreignKeys)
|
|
811
987
|
metadata.foreignKeys = [];
|
|
812
988
|
metadata.foreignKeys.push({
|
|
@@ -846,7 +1022,7 @@ var DefaultSql = class {
|
|
|
846
1022
|
* Formats a given value to a SQL-equivalent string based on the provided data type.
|
|
847
1023
|
*
|
|
848
1024
|
* @param value - The value to be formatted.
|
|
849
|
-
* @param type - The data type of the value. Either determined through typeof or {@link
|
|
1025
|
+
* @param type - The data type of the value. Either determined through typeof or {@link TableStructure} setting
|
|
850
1026
|
* @return A string representation of the value formatted for SQL.
|
|
851
1027
|
*/
|
|
852
1028
|
formatValueToSql(value, type) {
|
|
@@ -857,65 +1033,68 @@ var DefaultSql = class {
|
|
|
857
1033
|
case "string":
|
|
858
1034
|
return `"${value}"`;
|
|
859
1035
|
case "time":
|
|
860
|
-
return toDate(value).toISOString().slice(11, 19)
|
|
1036
|
+
return `"${toDate(value).toISOString().slice(11, 19)}"`;
|
|
861
1037
|
case "date":
|
|
862
|
-
return toDate(value).toISOString().slice(0, 10)
|
|
1038
|
+
return `"${toDate(value).toISOString().slice(0, 10)}"`;
|
|
863
1039
|
case "dateTime":
|
|
864
|
-
return toDate(value).toISOString().slice(0, 19).replace("T", " ")
|
|
1040
|
+
return `"${toDate(value).toISOString().slice(0, 19).replace("T", " ")}"`;
|
|
865
1041
|
default:
|
|
866
1042
|
return value.toString();
|
|
867
1043
|
}
|
|
868
1044
|
}
|
|
1045
|
+
getSqlType(dataType, _) {
|
|
1046
|
+
return this.types[dataType];
|
|
1047
|
+
}
|
|
869
1048
|
/**
|
|
870
|
-
*
|
|
1049
|
+
* Statement to enable / disable foreign keys in the database.
|
|
871
1050
|
* Is dialect specific.
|
|
872
1051
|
*
|
|
873
1052
|
* @param enabled - A boolean indicating whether to enable or disable foreign key constraints.
|
|
874
|
-
* @return The
|
|
1053
|
+
* @return The SQL statement as a string.
|
|
875
1054
|
*/
|
|
876
1055
|
changeForeignKeysState(enabled) {
|
|
877
1056
|
return "";
|
|
878
1057
|
}
|
|
879
1058
|
/**
|
|
880
|
-
*
|
|
1059
|
+
* Statement to add a foreign key to a specified table.
|
|
881
1060
|
* Is only used if {@link canAlterForeignKeys} is true.
|
|
882
1061
|
*
|
|
883
1062
|
* @param fromTableName - The name of the table to which the foreign key will be added.
|
|
884
1063
|
* @param foreignKey - The foreign key definition to be added, see {@link foreignKey()}.
|
|
885
|
-
* @return The SQL statement
|
|
1064
|
+
* @return The SQL statement as a string.
|
|
886
1065
|
*/
|
|
887
1066
|
addForeignKey(fromTableName, foreignKey) {
|
|
888
1067
|
throw new Error("Adding foreign keys is not supported!");
|
|
889
1068
|
}
|
|
890
1069
|
/**
|
|
891
|
-
*
|
|
1070
|
+
* Statement to remove a foreign key from a specified table.
|
|
892
1071
|
* Is only used if {@link canAlterForeignKeys} is true.
|
|
893
1072
|
*
|
|
894
1073
|
* @param tableName - The name of the table to which the foreign key will be added.
|
|
895
1074
|
* @param foreignKeyName - The name of the foreign key definition.
|
|
896
|
-
* @return The SQL statement
|
|
1075
|
+
* @return The SQL statement as a string.
|
|
897
1076
|
*/
|
|
898
1077
|
removeForeignKey(tableName, foreignKeyName) {
|
|
899
1078
|
throw new Error("Foreign key removal is not supported!");
|
|
900
1079
|
}
|
|
901
1080
|
/**
|
|
902
|
-
*
|
|
1081
|
+
* Statement to add a primary key to the specified table.
|
|
903
1082
|
* Is only used if {@link canAlterPrimaryKey} is true.
|
|
904
1083
|
*
|
|
905
1084
|
* @param tableName - The name of the table to which the primary key will be added.
|
|
906
1085
|
* @param columnName - The name of the column that will be set as the primary key.
|
|
907
|
-
* @return
|
|
1086
|
+
* @return The SQL statement as a string.
|
|
908
1087
|
*/
|
|
909
1088
|
addPrimaryKey(tableName, columnName) {
|
|
910
1089
|
throw new Error("Adding foreign keys is not supported!");
|
|
911
1090
|
}
|
|
912
1091
|
/**
|
|
913
|
-
*
|
|
1092
|
+
* Statement to remove a primary key from the specified table.
|
|
914
1093
|
* Is only used if {@link canAlterPrimaryKey} is true.
|
|
915
1094
|
*
|
|
916
1095
|
* @param tableName - The name of the table to which the primary key will be added.
|
|
917
1096
|
* @param columnName - The name of the column that will be set as the primary key.
|
|
918
|
-
* @return
|
|
1097
|
+
* @return The SQL statement as a string.
|
|
919
1098
|
*/
|
|
920
1099
|
removePrimaryKey(tableName, columnName) {
|
|
921
1100
|
throw new Error("Foreign key removal is not supported!");
|
|
@@ -939,64 +1118,164 @@ var DefaultSql = class {
|
|
|
939
1118
|
query += ` ON DELETE ${onDelete}`;
|
|
940
1119
|
return query;
|
|
941
1120
|
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Statement to create a table if it does not already exist.
|
|
1123
|
+
*
|
|
1124
|
+
* @param tableName - The name of the table to be created.
|
|
1125
|
+
* @param entries - An array containing the column definitions for the table.
|
|
1126
|
+
* @return The SQL statement as a string.
|
|
1127
|
+
*/
|
|
942
1128
|
createTable(tableName, entries) {
|
|
943
1129
|
return `CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
944
1130
|
${entries.join(",\n ")}
|
|
945
1131
|
);`;
|
|
946
1132
|
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Statement to rename a table.
|
|
1135
|
+
*
|
|
1136
|
+
* @param tableName - The name of the existing table to be renamed.
|
|
1137
|
+
* @param newTableName - The new name for the table.
|
|
1138
|
+
* @return The SQL statement as a string.
|
|
1139
|
+
*/
|
|
947
1140
|
renameTable(tableName, newTableName) {
|
|
948
1141
|
return `ALTER TABLE ${tableName} RENAME TO ${newTableName};`;
|
|
949
1142
|
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Statement to drop a table if it exists.
|
|
1145
|
+
*
|
|
1146
|
+
* @param tableName - The name of the table to be dropped.
|
|
1147
|
+
* @return The SQL statement as a string.
|
|
1148
|
+
*/
|
|
950
1149
|
dropTable(tableName) {
|
|
951
1150
|
return `DROP TABLE IF EXISTS ${tableName};`;
|
|
952
1151
|
}
|
|
953
|
-
|
|
954
|
-
|
|
1152
|
+
/**
|
|
1153
|
+
* Constructs a SQL column definition based on the provided parameters.
|
|
1154
|
+
*
|
|
1155
|
+
* @param columnName - The name of the column.
|
|
1156
|
+
* @param type - The data type of the column.
|
|
1157
|
+
* @param defaultValue - The default value for the column.
|
|
1158
|
+
* @param isPrimaryKey - Indicates whether the column is a primary key.
|
|
1159
|
+
* @return The constructed SQL column definition string.
|
|
1160
|
+
*/
|
|
1161
|
+
columnDefinition(columnName, type, isPrimaryKey, defaultValue) {
|
|
1162
|
+
const query = `${columnName} ${type}${defaultValue ? ` DEFAULT ${defaultValue}` : ""}`;
|
|
955
1163
|
return isPrimaryKey ? `${query} PRIMARY KEY` : query;
|
|
956
1164
|
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Statement to add a new column to a specified table.
|
|
1167
|
+
*
|
|
1168
|
+
* @param columnTable - The name of the table to which the column will be added.
|
|
1169
|
+
* @param entry - The column definition including the column name and its data type. See {@link columnDefinition()}
|
|
1170
|
+
* @return The SQL statement as a string.
|
|
1171
|
+
*/
|
|
957
1172
|
createColumn(columnTable, entry) {
|
|
958
1173
|
return `ALTER TABLE ${columnTable} ADD ${entry};`;
|
|
959
1174
|
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Statement to rename a column in an existing table.
|
|
1177
|
+
*
|
|
1178
|
+
* @param tableName - The name of the table containing the column to rename.
|
|
1179
|
+
* @param oldColumnName - The current name of the column to be renamed.
|
|
1180
|
+
* @param newColumnName - The new name for the column.
|
|
1181
|
+
* @return The SQL statement as a string.
|
|
1182
|
+
*/
|
|
960
1183
|
renameColumn(tableName, oldColumnName, newColumnName) {
|
|
961
1184
|
return `ALTER TABLE ${tableName} RENAME COLUMN ${oldColumnName} TO ${newColumnName};`;
|
|
962
1185
|
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1186
|
+
/**
|
|
1187
|
+
* Statement to drop a column from a specified table.
|
|
1188
|
+
*
|
|
1189
|
+
* @param tableName - The name of the table from which the column will be dropped.
|
|
1190
|
+
* @param columnName - The name of the column to be dropped.
|
|
1191
|
+
* @return The SQL statement as a string.
|
|
1192
|
+
*/
|
|
966
1193
|
dropColumn(tableName, columnName) {
|
|
967
1194
|
return `ALTER TABLE ${tableName} DROP COLUMN ${columnName};`;
|
|
968
1195
|
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Statement to return data from a table.
|
|
1198
|
+
*
|
|
1199
|
+
* @param tableName - The name of the database table to select data from.
|
|
1200
|
+
* @param select - An array of column names to include in the SELECT clause.
|
|
1201
|
+
* @param where - An optional condition.
|
|
1202
|
+
* @return The SQL statement as a string.
|
|
1203
|
+
*/
|
|
969
1204
|
select(tableName, select, where) {
|
|
970
1205
|
let query = `SELECT ${select.join(",")} FROM ${tableName}`;
|
|
971
1206
|
if (where)
|
|
972
1207
|
query += ` WHERE ${where}`;
|
|
973
1208
|
return `${query};`;
|
|
974
1209
|
}
|
|
1210
|
+
/**
|
|
1211
|
+
* Statement to insert data into a table.
|
|
1212
|
+
*
|
|
1213
|
+
* @param tableName - The name of the table where the data will be inserted.
|
|
1214
|
+
* @param content - The value query. See {@link insertValues()}.
|
|
1215
|
+
* @return The SQL statement as a string.
|
|
1216
|
+
*/
|
|
975
1217
|
insert(tableName, content) {
|
|
976
1218
|
return `INSERT INTO ${tableName} ${content};`;
|
|
977
1219
|
}
|
|
1220
|
+
/**
|
|
1221
|
+
* A SQL insert query string. See {@link insert()}.
|
|
1222
|
+
*
|
|
1223
|
+
* @param keys - An array of column names to be used in the query.
|
|
1224
|
+
* @param [valueString] - An optional preformatted value string to be included in the query.
|
|
1225
|
+
* If not provided, the method generates a default placeholder string with "?" for each key.
|
|
1226
|
+
* @return The SQL statement as a string.
|
|
1227
|
+
*/
|
|
978
1228
|
insertValues(keys, valueString) {
|
|
979
1229
|
return `(${keys}) ${valueString ?? `VALUES (${keys.map(() => "?").join(",")})`}`;
|
|
980
1230
|
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Returns a SQL query string to create a migration table.
|
|
1233
|
+
* The table is used to store migration data, specifically the version of the migration.
|
|
1234
|
+
*
|
|
1235
|
+
* @return The SQL query string for creating the migration table.
|
|
1236
|
+
*/
|
|
981
1237
|
migrationTableQuery() {
|
|
982
1238
|
return this.createTable(MIGRATION_DATA_TABLE_NAME, [
|
|
983
|
-
this.columnDefinition("version", this.types.number, "0"
|
|
1239
|
+
this.columnDefinition("version", this.types.number, false, "0")
|
|
984
1240
|
]);
|
|
985
1241
|
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Fetches the foreign keys for the specified table.
|
|
1244
|
+
* This method is only used if {@link canInspectForeignKeys} is true
|
|
1245
|
+
*
|
|
1246
|
+
* @param tableName - The name of the table for which to retrieve foreign key information.
|
|
1247
|
+
* @return {Promise<ForeignKeyInfo[]>} A promise that resolves to an array of {@link ForeignKeyInfo}.
|
|
1248
|
+
*/
|
|
986
1249
|
async getForeignKeys(tableName) {
|
|
987
1250
|
throw new Error("Inspecting foreign keys is not supported!");
|
|
988
1251
|
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Ensures that the migration table exists in the database.
|
|
1254
|
+
* If the table does not exist, it creates the table using the defined migration table query.
|
|
1255
|
+
*
|
|
1256
|
+
* @return A promise that resolves when the migration table is created successfully.
|
|
1257
|
+
*/
|
|
989
1258
|
async createMigrationTableIfNeeded() {
|
|
990
1259
|
await this.db.runMultipleWriteStatements(this.migrationTableQuery());
|
|
991
1260
|
}
|
|
992
|
-
|
|
1261
|
+
/**
|
|
1262
|
+
* Retrieves the current version from the migration data table.
|
|
1263
|
+
*
|
|
1264
|
+
* @return A promise that resolves to the current version number. Returns 0 if no version is found.
|
|
1265
|
+
*/
|
|
993
1266
|
async getVersion() {
|
|
994
1267
|
await this.createMigrationTableIfNeeded();
|
|
995
1268
|
const query = this.select(MIGRATION_DATA_TABLE_NAME, ["version"]);
|
|
996
1269
|
const data = await this.db.runGetStatement(query);
|
|
997
1270
|
return data.length ? data[0].version : 0;
|
|
998
1271
|
}
|
|
999
|
-
|
|
1272
|
+
/**
|
|
1273
|
+
* Updates the migration version in the database. If the version entry does not exist, it inserts a new one;
|
|
1274
|
+
* if it exists, it updates the version to the specified value.
|
|
1275
|
+
*
|
|
1276
|
+
* @param newVersion - The new migration version to set in the database.
|
|
1277
|
+
* @return A promise that resolves when the operation is complete.
|
|
1278
|
+
*/
|
|
1000
1279
|
async setVersion(newVersion) {
|
|
1001
1280
|
const query = `${this.migrationTableQuery()};
|
|
1002
1281
|
INSERT INTO ${MIGRATION_DATA_TABLE_NAME} (version) VALUES ${newVersion} WHERE NOT EXISTS (SELECT 1 FROM ${MIGRATION_DATA_TABLE_NAME})
|
|
@@ -1014,9 +1293,9 @@ var SqliteDialect = class extends DefaultSql {
|
|
|
1014
1293
|
number: "INTEGER",
|
|
1015
1294
|
bigint: "INTEGER",
|
|
1016
1295
|
boolean: "INTEGER",
|
|
1017
|
-
date: "
|
|
1018
|
-
time: "
|
|
1019
|
-
dateTime: "
|
|
1296
|
+
date: "INTEGER",
|
|
1297
|
+
time: "INTEGER",
|
|
1298
|
+
dateTime: "INTEGER",
|
|
1020
1299
|
null: "NULL"
|
|
1021
1300
|
};
|
|
1022
1301
|
canAlterForeignKeys = false;
|
|
@@ -1031,14 +1310,16 @@ var SqliteDialect = class extends DefaultSql {
|
|
|
1031
1310
|
}
|
|
1032
1311
|
async getColumnInformation(tableName) {
|
|
1033
1312
|
const data = await this.db.runGetStatement(`PRAGMA table_info(${tableName})`);
|
|
1034
|
-
|
|
1035
|
-
|
|
1313
|
+
const output = {};
|
|
1314
|
+
for (const entry of data) {
|
|
1315
|
+
output[entry["name"]] = {
|
|
1036
1316
|
name: entry["name"],
|
|
1037
1317
|
type: entry["type"],
|
|
1038
1318
|
defaultValue: entry["dflt_value"],
|
|
1039
1319
|
isPrimaryKey: entry["pk"] == "1"
|
|
1040
1320
|
};
|
|
1041
|
-
}
|
|
1321
|
+
}
|
|
1322
|
+
return output;
|
|
1042
1323
|
}
|
|
1043
1324
|
formatValueToSql(value, type) {
|
|
1044
1325
|
function toDate(value2) {
|
|
@@ -1063,8 +1344,8 @@ var SqliteDialect = class extends DefaultSql {
|
|
|
1063
1344
|
fromColumn: entry["from"],
|
|
1064
1345
|
toTable: entry["table"],
|
|
1065
1346
|
toColumn: entry["to"],
|
|
1066
|
-
|
|
1067
|
-
|
|
1347
|
+
onUpdate: entry["on_update"] ?? "NO ACTION",
|
|
1348
|
+
onDelete: entry["on_delete"] ?? "NO ACTION"
|
|
1068
1349
|
};
|
|
1069
1350
|
});
|
|
1070
1351
|
}
|
|
@@ -1097,14 +1378,17 @@ var PostgresDialect = class extends DefaultSql {
|
|
|
1097
1378
|
}
|
|
1098
1379
|
async getColumnInformation(tableName) {
|
|
1099
1380
|
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}';`);
|
|
1100
|
-
|
|
1101
|
-
|
|
1381
|
+
const output = {};
|
|
1382
|
+
for (const entry of data) {
|
|
1383
|
+
output[entry["name"]] = {
|
|
1102
1384
|
name: entry["column_name"],
|
|
1103
1385
|
type: entry["data_type"],
|
|
1386
|
+
maxLength: 0,
|
|
1104
1387
|
defaultValue: entry["column_default"],
|
|
1105
1388
|
isPrimaryKey: false
|
|
1106
1389
|
};
|
|
1107
|
-
}
|
|
1390
|
+
}
|
|
1391
|
+
return output;
|
|
1108
1392
|
}
|
|
1109
1393
|
addPrimaryKey(tableName, columnName) {
|
|
1110
1394
|
return `ALTER TABLE ${tableName} ADD PRIMARY KEY (${columnName});
|
|
@@ -1149,18 +1433,21 @@ var MsSqlDialect = class extends DefaultSql {
|
|
|
1149
1433
|
});
|
|
1150
1434
|
}
|
|
1151
1435
|
async getTableNames() {
|
|
1152
|
-
return await this.db.runGetStatement("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'");
|
|
1436
|
+
return (await this.db.runGetStatement("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'")).map((entry) => entry.name);
|
|
1153
1437
|
}
|
|
1154
1438
|
async getColumnInformation(tableName) {
|
|
1155
1439
|
const data = await this.db.runGetStatement(`SELECT COLUMN_NAME, DATA_TYPE, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = N'${tableName};'`);
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1440
|
+
const output = {};
|
|
1441
|
+
for (const entry of data) {
|
|
1442
|
+
output[entry["name"]] = {
|
|
1443
|
+
name: entry["name"],
|
|
1159
1444
|
type: entry["DATA_TYPE"],
|
|
1445
|
+
maxLength: 0,
|
|
1160
1446
|
defaultValue: entry["COLUMN_DEFAULT"],
|
|
1161
|
-
isPrimaryKey:
|
|
1447
|
+
isPrimaryKey: entry["pk"] == "1"
|
|
1162
1448
|
};
|
|
1163
|
-
}
|
|
1449
|
+
}
|
|
1450
|
+
return output;
|
|
1164
1451
|
}
|
|
1165
1452
|
};
|
|
1166
1453
|
|
|
@@ -1170,9 +1457,26 @@ var MySqlDialect = class extends DefaultSql {
|
|
|
1170
1457
|
canAlterPrimaryKey = true;
|
|
1171
1458
|
canInspectForeignKeys = true;
|
|
1172
1459
|
canInspectPrimaryKey = true;
|
|
1460
|
+
types = {
|
|
1461
|
+
string: "VARCHAR",
|
|
1462
|
+
number: "INT",
|
|
1463
|
+
bigint: "BIGINT",
|
|
1464
|
+
boolean: "BOOLEAN",
|
|
1465
|
+
date: "DATE",
|
|
1466
|
+
time: "TIME",
|
|
1467
|
+
dateTime: "DATETIME",
|
|
1468
|
+
null: "NULL"
|
|
1469
|
+
};
|
|
1470
|
+
getSqlType(dataType, columnInfo) {
|
|
1471
|
+
switch (dataType) {
|
|
1472
|
+
case "string":
|
|
1473
|
+
return this.types[dataType] + `(${columnInfo?.maxLength})`;
|
|
1474
|
+
default:
|
|
1475
|
+
return super.getSqlType(dataType, columnInfo);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1173
1478
|
changeForeignKeysState(enabled) {
|
|
1174
|
-
return `SET FOREIGN_KEY_CHECKS ${enabled ? "1" : "0"}
|
|
1175
|
-
;`;
|
|
1479
|
+
return `SET FOREIGN_KEY_CHECKS ${enabled ? "1" : "0"};`;
|
|
1176
1480
|
}
|
|
1177
1481
|
addForeignKey(fromTableName, foreignKey) {
|
|
1178
1482
|
return `ALTER TABLE ${fromTableName} ADD ${foreignKey};`;
|
|
@@ -1198,18 +1502,22 @@ var MySqlDialect = class extends DefaultSql {
|
|
|
1198
1502
|
});
|
|
1199
1503
|
}
|
|
1200
1504
|
async getTableNames() {
|
|
1201
|
-
|
|
1505
|
+
const t = await this.db.runGetStatement("SELECT table_name FROM information_schema.tables;");
|
|
1506
|
+
return (await this.db.runGetStatement("SELECT table_name FROM information_schema.tables;")).map((entry) => entry.name);
|
|
1202
1507
|
}
|
|
1203
1508
|
async getColumnInformation(tableName) {
|
|
1204
1509
|
const data = await this.db.runGetStatement(`SHOW COLUMNS FROM ${tableName};`);
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1510
|
+
const output = {};
|
|
1511
|
+
for (const entry of data) {
|
|
1512
|
+
output[entry["name"]] = {
|
|
1513
|
+
name: entry["name"],
|
|
1514
|
+
type: entry["type"],
|
|
1515
|
+
maxLength: 0,
|
|
1516
|
+
defaultValue: entry["dflt_value"],
|
|
1517
|
+
isPrimaryKey: entry["pk"] == "1"
|
|
1211
1518
|
};
|
|
1212
|
-
}
|
|
1519
|
+
}
|
|
1520
|
+
return output;
|
|
1213
1521
|
}
|
|
1214
1522
|
};
|
|
1215
1523
|
|