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/dist/index.mjs CHANGED
@@ -21,19 +21,110 @@ var LoggerClass = class {
21
21
  };
22
22
  var Logger = new LoggerClass();
23
23
 
24
- // src/lib/tableInfo/TableInfo.ts
24
+ // src/lib/tableInfo/TableObj.ts
25
+ var TableObj = class _TableObj {
26
+ IS_TABLE_OBJ = true;
27
+ tableName;
28
+ tableStructure = {
29
+ table: "",
30
+ primaryKey: void 0,
31
+ columns: {},
32
+ foreignKeys: []
33
+ };
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 {
52
+ Logger.warn(`${key} was skipped because its type (${typeof value}) is not supported`);
53
+ return null;
54
+ }
55
+ case "undefined":
56
+ case "symbol":
57
+ Logger.warn(`${key} was skipped because its type (${typeof value}) is not supported`);
58
+ return null;
59
+ }
60
+ return {
61
+ ..._TableObj.getEmptyColumnEntry(key),
62
+ inputType: dataType,
63
+ inputDefaultValue: value
64
+ };
65
+ }
66
+ constructor(tableName, defaultValues) {
67
+ this.tableName = tableName;
68
+ for (const key in defaultValues) {
69
+ const value = defaultValues[key];
70
+ const columnEntry = _TableObj.getColumnEntry(key, value);
71
+ if (!columnEntry)
72
+ continue;
73
+ this.tableStructure.columns[key] = columnEntry;
74
+ }
75
+ }
76
+ primaryKey(key) {
77
+ this.tableStructure.primaryKey = key.toString();
78
+ this.tableStructure.columns[key.toString()].isPrimaryKey = true;
79
+ return this;
80
+ }
81
+ foreignKey(fromColumn, toTable, toColumn, options) {
82
+ this.tableStructure.foreignKeys.push({
83
+ fromTable: this.tableName,
84
+ fromColumn: fromColumn.toString(),
85
+ toTable: toTable.tableName,
86
+ toColumn: toColumn.toString(),
87
+ onUpdate: options?.onUpdate,
88
+ onDelete: options?.onDelete
89
+ });
90
+ return this;
91
+ }
92
+ dataType(column, type) {
93
+ this.tableStructure.columns[column.toString()].type = type;
94
+ return this;
95
+ }
96
+ maxCharacterLength(column, max) {
97
+ this.tableStructure.columns[column.toString()].maxLength = max;
98
+ return this;
99
+ }
100
+ static create(tableName, columns) {
101
+ return new _TableObj(tableName, columns);
102
+ }
103
+ static isTableObj(obj) {
104
+ return !!obj?.IS_TABLE_OBJ;
105
+ }
106
+ };
107
+
108
+ // src/lib/tableInfo/getTableStructure.ts
25
109
  var TABLE_INFO_PROPERTY_NAME = Symbol("tableInfo");
26
- function getTableInfo(table) {
110
+ function getInputColumnInfo(table, context) {
111
+ const metadata = context?.metadata ? getTableStructureFromMetadata(context.metadata) : getTableStructure(table);
112
+ const key = context.name ?? context;
113
+ if (!metadata.columns.hasOwnProperty(key))
114
+ metadata.columns[key] = TableObj.getEmptyColumnEntry(key);
115
+ return metadata.columns[key];
116
+ }
117
+ function getTableStructure(table) {
27
118
  let metadata = table[Symbol.metadata] ?? table.prototype?.[Symbol.metadata];
28
119
  if (!metadata) {
29
120
  metadata = {};
30
121
  table[Symbol.metadata] = metadata;
31
122
  }
32
- return getTableInfoFromMetadata(metadata);
123
+ return getTableStructureFromMetadata(metadata);
33
124
  }
34
- function getTableInfoFromMetadata(metadata) {
125
+ function getTableStructureFromMetadata(metadata) {
35
126
  if (!metadata[TABLE_INFO_PROPERTY_NAME])
36
- metadata[TABLE_INFO_PROPERTY_NAME] = { primaryKey: "", foreignKeys: [] };
127
+ metadata[TABLE_INFO_PROPERTY_NAME] = { table: "", primaryKey: "", foreignKeys: [], columns: {} };
37
128
  return metadata[TABLE_INFO_PROPERTY_NAME];
38
129
  }
39
130
 
@@ -45,101 +136,85 @@ var TableStructureGenerator = class {
45
136
  this.dialect = dialect;
46
137
  this.dbInstructions = dbInstructions;
47
138
  }
139
+ /**
140
+ * Generates the structure of tables based on database instructions.
141
+ *
142
+ * @return An object where keys are table names, and values are their corresponding table structures.
143
+ */
48
144
  generateTableStructure() {
49
- if (this.dbInstructions.tables.length) {
50
- const tableObjects = {};
51
- for (const table of this.dbInstructions.tables) {
52
- tableObjects[table.name] = { columns: new table(), tableInfo: getTableInfo(table) };
53
- }
54
- return this.getExpectedStructure(tableObjects);
55
- } else {
56
- const tableObjects = this.dbInstructions.tables;
57
- return this.getExpectedStructure(tableObjects);
58
- }
59
- }
60
- getExpectedStructure(tableObjects) {
61
145
  const tables = {};
62
- for (const tableName in tableObjects) {
63
- const obj = tableObjects[tableName];
64
- const tableInfo = obj.tableInfo;
65
- tables[tableName] = {
66
- table: tableName,
67
- primaryKey: tableInfo?.primaryKey,
68
- columns: this.getColumns(obj.columns, tableInfo?.primaryKey),
69
- foreignKeys: tableInfo?.foreignKeys
70
- };
71
- }
72
- return tables;
73
- }
74
- getColumns(obj, primaryKey) {
75
- const columns = [];
76
- for (const property in obj) {
77
- const propertyKey = property;
78
- const value = obj[propertyKey];
79
- const columnData = {
80
- name: property,
81
- isPrimaryKey: property == primaryKey,
82
- type: "TEXT",
83
- defaultValue: this.dialect.typeNull
84
- };
85
- switch (typeof value) {
86
- case "string":
87
- columnData.type = this.dialect.typeString;
88
- columnData.defaultValue = value === null ? this.dialect.typeNull : `"${this.dialect.formatValueToSql(value)}"`;
89
- break;
90
- case "bigint":
91
- columnData.type = this.dialect.typeBigInt;
92
- columnData.defaultValue = value === null ? this.dialect.typeNull : this.dialect.formatValueToSql(value);
93
- break;
94
- case "number":
95
- columnData.type = this.dialect.typeInt;
96
- columnData.defaultValue = value === null ? this.dialect.typeNull : this.dialect.formatValueToSql(value);
97
- break;
98
- case "boolean":
99
- columnData.type = this.dialect.typeBoolean;
100
- columnData.defaultValue = value === null ? this.dialect.typeNull : this.dialect.formatValueToSql(value);
101
- break;
102
- case "function":
103
- continue;
104
- default:
105
- Logger.warn(`${obj.constructor.name}.${property} was skipped because its type is not supported (${typeof value})`);
106
- continue;
146
+ for (const table of this.dbInstructions.tables) {
147
+ if (TableObj.isTableObj(table)) {
148
+ tables[table.tableName] = table.tableStructure;
149
+ } else {
150
+ const classTable = table;
151
+ const structure = getTableStructure(classTable);
152
+ tables[classTable.name] = structure;
153
+ for (const key in structure.columns) {
154
+ const entry = structure.columns[key];
155
+ entry.defaultValue = entry.defaultValue === null ? this.dialect.types.null : this.dialect.formatValueToSql(entry.inputDefaultValue, entry.inputType);
156
+ entry.type = this.dialect.getSqlType(entry.inputType, entry);
157
+ }
107
158
  }
108
- columns.push(columnData);
109
159
  }
110
- return columns;
160
+ return tables;
111
161
  }
112
162
  };
113
163
 
114
164
  // src/lib/exceptions/NotAllowedException.ts
115
165
  var NotAllowedException = class extends Error {
116
166
  constructor(entries) {
117
- let message = "Some destructive changes need to be confirmed.\n In preMigration() run:\n";
167
+ let message = "Some destructive changes need to be confirmed. To confirm, add to preMigration():\n";
118
168
  for (const entry of entries) {
119
- message += ` migrations.allowMigration(${entry.version}, "${entry.tableName}", "${entry.type}");
169
+ message += ` migrations.allowMigration(${entry.version}, ${entry.tableName}, "${entry.type}");
120
170
  `;
121
171
  }
122
172
  super(message);
123
173
  }
124
174
  };
125
175
 
126
- // src/lib/typings/Migrations.ts
176
+ // src/lib/Migrations.ts
127
177
  var Migrations = class {
128
- toVersion = 0;
129
- alwaysAllowed = {};
130
178
  fromVersion = 0;
179
+ toVersion = 0;
180
+ lastUsedVersion = 0;
131
181
  migrationData = {};
132
182
  migrationDataForOldTableNames = {};
183
+ alwaysAllowed = {};
133
184
  notAllowedChanges = [];
185
+ /**
186
+ * Resets the migration state to its initial values.
187
+ * Only used internally.
188
+ *
189
+ * @param dbInstructions - The instructions containing database metadata and migration details.
190
+ * @param fromVersion - The version of the actual database.
191
+ */
134
192
  reset(dbInstructions, fromVersion) {
135
193
  this.fromVersion = fromVersion;
136
- this.migrationData = {};
137
194
  this.toVersion = dbInstructions.version;
195
+ this.migrationData = {};
196
+ this.migrationDataForOldTableNames = {};
138
197
  this.alwaysAllowed = dbInstructions.alwaysAllowedMigrations ?? {};
139
198
  this.notAllowedChanges = [];
140
199
  }
200
+ /**
201
+ * Retrieves the migration instructions for the specified table.
202
+ * @see getEntryFromTableName
203
+ *
204
+ * @param table - The {@link TableInput} object from which the table name will be retrieved.
205
+ * @return The migration instructions corresponding to the given table input.
206
+ */
141
207
  getEntry(table) {
142
208
  const tableName = this.getTableName(table);
209
+ return this.getEntryFromTableName(tableName);
210
+ }
211
+ /**
212
+ * Retrieves the migration instructions for the specified table.
213
+ *
214
+ * @param tableName - The name of the table for which the entry is to be retrieved or created.
215
+ * @return The migration instructions corresponding to the given table input.
216
+ */
217
+ getEntryFromTableName(tableName) {
143
218
  if (!this.migrationData.hasOwnProperty(tableName)) {
144
219
  this.migrationData[tableName] = {
145
220
  recreate: false,
@@ -150,28 +225,67 @@ var Migrations = class {
150
225
  }
151
226
  return this.migrationData[tableName];
152
227
  }
228
+ /**
229
+ * Retrieves the table name from the given table input.
230
+ *
231
+ * @param table - The {@link TableInput} object.
232
+ * @return The name of the table derived from either {@link TableObj} or a class.
233
+ */
153
234
  getTableName(table) {
154
- return typeof table == "string" ? table : table.name;
235
+ return TableObj.isTableObj(table) ? table.tableName : table.name;
236
+ }
237
+ /**
238
+ * Checks whether a provided version is relevant based on database version ({@link fromVersion}) and target version ({@link toVersion}).
239
+ * Also makes sure that version checks happen in order.
240
+ *
241
+ * @param version - The version to check for relevance.
242
+ * @return Returns true if the version is relevant, otherwise false.
243
+ * @throws Error if versions are not properly ordered
244
+ */
245
+ versionIsRelevant(version) {
246
+ if (version <= this.fromVersion || version > this.toVersion)
247
+ return false;
248
+ if (version < this.lastUsedVersion)
249
+ throw new Error("Migrations in preMigration() have to be ordered by version (beginning with the lowest version).");
250
+ this.lastUsedVersion = version;
251
+ return true;
155
252
  }
253
+ /**
254
+ * Verifies the renaming tasks specified in {@link DatabaseInstructions.preMigration} that were considered are valid.
255
+ * Called after all SQL queries have been generated.
256
+ * Only used internally.
257
+ *
258
+ * @param newTables - The table structures extracted from {@link DatabaseInstructions.tables}.
259
+ * @throws Error if a renaming task is invalid.
260
+ */
156
261
  verifyRenamingTasks(newTables) {
157
262
  for (const tableName in this.migrationData) {
158
263
  const migrationEntry = this.migrationData[tableName];
159
- if (migrationEntry.tableRenaming?.oldName == tableName)
160
- return new Error(`You set table "${tableName}" to be renamed to itself!`);
161
- if (migrationEntry.tableRenaming && migrationEntry.tableRenaming.newName != tableName)
162
- return new Error(`You set table "${tableName}" to be renamed to a different name than the one in your structure (${migrationEntry.tableRenaming.newName})!`);
163
- else if (!newTables[tableName])
164
- return new Error(`You set table "${migrationEntry.tableRenaming}" migrations for "${tableName}". But "${tableName}" does not exist in your structure.`);
264
+ if (migrationEntry.tableRenaming) {
265
+ if (migrationEntry.tableRenaming.oldName == tableName)
266
+ return new Error(`You set table "${tableName}" to be renamed to itself!`);
267
+ if (migrationEntry.tableRenaming.newName != tableName)
268
+ return new Error(`You set table "${tableName}" to be renamed to a different name than the one in your structure (${migrationEntry.tableRenaming.newName})!`);
269
+ else if (!newTables[tableName])
270
+ return new Error(`You set table "${migrationEntry.tableRenaming}" migrations for "${tableName}". But "${tableName}" does not exist in your structure.`);
271
+ }
165
272
  for (const renamingData of migrationEntry.renamedColumns) {
166
273
  const oldColumnName = renamingData.oldName;
167
274
  const newColumnName = renamingData.newName;
168
275
  if (oldColumnName == newColumnName)
169
276
  return new Error(`You set column ${tableName}.${oldColumnName} to be renamed to itself!`);
170
- else if (!newTables[tableName]?.columns.find((column) => column.name == newColumnName))
277
+ else if (!newTables[tableName]?.columns[newColumnName])
171
278
  return new Error(`You set column ${tableName}.${oldColumnName} to be renamed to ${tableName}.${newColumnName}. But ${tableName}.${newColumnName} does not exist in your structure.`);
172
279
  }
173
280
  }
174
281
  }
282
+ /**
283
+ * Making sure all considered calls to {@link allowMigration()} where necessary to prevent input mistakes.
284
+ * Called after all SQL queries have been generated.
285
+ * Only used internally.
286
+ *
287
+ * @throws Error if unnecessary calls were discovered. All discovered calls are collected and thrown as one Error.
288
+ */
175
289
  verifyAllowedMigrations() {
176
290
  if (this.notAllowedChanges.length)
177
291
  return new NotAllowedException(this.notAllowedChanges);
@@ -187,6 +301,14 @@ var Migrations = class {
187
301
  if (errorMsg)
188
302
  return new Error(errorMsg);
189
303
  }
304
+ /**
305
+ * Called by {@link MigrationManager} to check if a specific destructive query is allowed.
306
+ * Only used internally.
307
+ *
308
+ * @see allowMigration()
309
+ * @param tableName - The name of the table to check the migration against.
310
+ * @param type - The type of destructive migration being checked.
311
+ */
190
312
  compareWithAllowedMigration(tableName, type) {
191
313
  const migrationEntry = this.migrationData[tableName];
192
314
  if (!migrationEntry?.allowedMigrations[type]) {
@@ -195,22 +317,118 @@ var Migrations = class {
195
317
  } else if (migrationEntry)
196
318
  migrationEntry.usedMigrations[type] = true;
197
319
  }
320
+ /**
321
+ * Retrieves the migration data containing instructions for data migration.
322
+ * Only used internally.
323
+ *
324
+ * @return An object where each key corresponds to the table name,
325
+ * and the value is the associated migration instructions.
326
+ */
327
+ getMigrationData() {
328
+ return this.migrationData;
329
+ }
330
+ /**
331
+ * Iterates over renamed columns for the specified table and executes a callback for each pair of old and new column names.
332
+ * Only used internally.
333
+ *
334
+ * @param tableName - Name of the table whose renamed columns should be processed.
335
+ * @param callback - A function to execute for each renamed column, receiving the old and new column names as arguments.
336
+ */
337
+ loopRenamedColumns(tableName, callback) {
338
+ const migrationEntry = this.migrationData[tableName];
339
+ for (const renamingData of migrationEntry.renamedColumns) {
340
+ callback(renamingData.oldName, renamingData.newName);
341
+ }
342
+ }
343
+ /**
344
+ * Considers the renaming of a table and returns its (newest) name after the migration or
345
+ * the original name if no renaming is queued.
346
+ * Only used internally.
347
+ *
348
+ * @see getOldTableName()
349
+ * @param tableName - The current table name in the database.
350
+ * @return The newest table name if available, otherwise the current table name.
351
+ */
352
+ getNewestTableName(tableName) {
353
+ return this.migrationDataForOldTableNames[tableName]?.tableRenaming?.newName ?? tableName;
354
+ }
355
+ /**
356
+ * Retrieves the current table name in the database.
357
+ * Only used internally.
358
+ *
359
+ * @see getNewestTableName()
360
+ * @param tableName - The name of the newest table name.
361
+ * @return The current table name in the database.
362
+ */
363
+ getOldTableName(tableName) {
364
+ return this.migrationData[tableName]?.tableRenaming?.oldName ?? tableName;
365
+ }
366
+ /**
367
+ * Considers the renaming of a column in a table and returns its (newest) name after the migration or
368
+ * the original name if no renaming is queued.
369
+ * Only used internally.
370
+ *
371
+ * @param tableName - The name of the table containing the column.
372
+ * @param oldColumnName - The current name of the column in the database.
373
+ * @return The newest column name if available, otherwise the original column name.
374
+ */
375
+ getNewestColumnName(tableName, oldColumnName) {
376
+ const columnNames = this.migrationData[tableName]?.renamedColumns.find((columns) => columns.oldName == oldColumnName);
377
+ return columnNames ? columnNames.newName : oldColumnName;
378
+ }
379
+ /**
380
+ * Determines if the specified table is marked to be recreated.
381
+ * Only used internally.
382
+ *
383
+ * @param tableName The name of the table to check.
384
+ * @return Returns true if the table will be recreated, false otherwise.
385
+ */
386
+ willBeRecreated(tableName) {
387
+ return this.migrationData[tableName]?.recreate;
388
+ }
389
+ /**
390
+ * Marks a table to be recreated and logs the recreation reason for the user.
391
+ * Only used internally.
392
+ *
393
+ * @param tableName - The name of the table to be marked for recreation.
394
+ * @param reason - The reason why the table is being marked for recreation.
395
+ */
396
+ internalRecreate(tableName, reason) {
397
+ const entry = this.getEntryFromTableName(tableName);
398
+ entry.recreate = true;
399
+ Logger.log(`Table ${tableName} will be recreated(${reason})!`);
400
+ }
401
+ /**
402
+ * SqlMorpheus only allows destructive statements if it was specifically allowed in {@link DatabaseInstructions.preMigration()}.
403
+ * Use this method to allow a destructive statement for a specific version.
404
+ * This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
405
+ *
406
+ * @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.
407
+ * @param table - Either a {@link TableObj} or a class decorated with @TableClass.
408
+ * @param allowedMigrations - The keys representing the migrations to be allowed. Multiple migrations can be provided
409
+ */
198
410
  allowMigration(version, table, ...allowedMigrations) {
199
- if (version < this.fromVersion || version > this.toVersion)
411
+ if (!this.versionIsRelevant(version))
200
412
  return;
201
413
  const entry = this.getEntry(table);
202
414
  for (const key of allowedMigrations) {
203
415
  entry.allowedMigrations[key] = true;
204
416
  }
205
417
  }
418
+ /**
419
+ * When you change the name of a table, SqlMorpheus will assume the original table was meant to be dropped
420
+ * and the new table was meant to be added, which would lead to data loss.
421
+ * Use this method to mark a table to be renamed instead.
422
+ * This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
423
+ *
424
+ * @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.
425
+ * @param oldTableName - The current name in the database at the time of the migration.
426
+ * @param newTableName - The new table name or its equivalent input format.
427
+ */
206
428
  renameTable(version, oldTableName, newTableName) {
207
- if (version < this.fromVersion || version > this.toVersion)
429
+ if (!this.versionIsRelevant(version))
208
430
  return;
209
431
  const newTableNameString = this.getTableName(newTableName);
210
- if (this.migrationData.hasOwnProperty(oldTableName)) {
211
- this.migrationData[newTableNameString] = this.getEntry(oldTableName);
212
- delete this.migrationData[oldTableName];
213
- }
214
432
  const entry = this.getEntry(newTableName);
215
433
  this.migrationDataForOldTableNames[oldTableName] = entry;
216
434
  if (!entry.tableRenaming)
@@ -218,8 +436,19 @@ var Migrations = class {
218
436
  else
219
437
  entry.tableRenaming.newName = newTableNameString;
220
438
  }
439
+ /**
440
+ * When you change the name of a column, SqlMorpheus will assume the original column was meant to be removed
441
+ * and the new column was meant to be added, which would lead to data loss.
442
+ * Use this method to mark a column to be renamed instead.
443
+ * This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
444
+ *
445
+ * @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.
446
+ * @param table Either a {@link TableObj} or a class decorated with @TableClass containing the column to be renamed.
447
+ * @param oldColumn The current name in the database at the time of the migration.
448
+ * @param newColumn The new name to assign to the column.
449
+ */
221
450
  renameColumn(version, table, oldColumn, newColumn) {
222
- if (version < this.fromVersion || version > this.toVersion)
451
+ if (!this.versionIsRelevant(version))
223
452
  return;
224
453
  const entry = this.getEntry(table);
225
454
  const existingColumnEntry = entry.renamedColumns.find((entry2) => entry2.newName == oldColumn);
@@ -228,36 +457,21 @@ var Migrations = class {
228
457
  else
229
458
  entry.renamedColumns.push({ oldName: oldColumn, newName: newColumn });
230
459
  }
231
- recreateTableImp(table) {
232
- const entry = this.getEntry(table);
233
- entry.recreate = true;
234
- }
460
+ /**
461
+ * Should not be needed most of the time.
462
+ * Marks a table to be recreated
463
+ * (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).
464
+ * This method will be ignored if {@link version} is smaller than the current database version or higher than the target version of {@link DatabaseInstructions}.
465
+ *
466
+ * @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.
467
+ * @param table Either a {@link TableObj} or a class decorated with @TableClass.
468
+ */
235
469
  recreateTable(version, table) {
236
- if (version < this.fromVersion || version > this.toVersion)
470
+ if (!this.versionIsRelevant(version))
237
471
  return;
238
- this.recreateTableImp(table);
239
- }
240
- getMigrationData() {
241
- return this.migrationData;
242
- }
243
- loopRenamedColumns(tableName, callback) {
244
- const migrationEntry = this.migrationData[tableName];
245
- for (const renamingData of migrationEntry.renamedColumns) {
246
- callback(renamingData.oldName, renamingData.newName);
247
- }
248
- }
249
- getNewestTableName(tableName) {
250
- return this.migrationDataForOldTableNames[tableName]?.tableRenaming?.newName ?? tableName;
251
- }
252
- getOldTableName(tableName) {
253
- return this.migrationData[tableName]?.tableRenaming?.oldName ?? tableName;
254
- }
255
- getNewestColumnName(tableName, oldColumnName) {
256
- const columnNames = this.migrationData[tableName]?.renamedColumns.find((columns) => columns.oldName == oldColumnName);
257
- return columnNames ? columnNames.newName : oldColumnName;
258
- }
259
- willBeRecreated(tableName) {
260
- return this.migrationData[tableName]?.recreate;
472
+ const entry = this.getEntry(table);
473
+ entry.recreate = true;
474
+ Logger.log(`Table ${this.getTableName(table)} will be recreated!`);
261
475
  }
262
476
  };
263
477
 
@@ -440,8 +654,9 @@ ${changes.up}
440
654
  */
441
655
  createTableSql(tableName, columns, foreignKeys) {
442
656
  const queryLines = [];
443
- for (const columnInfo of columns) {
444
- queryLines.push(this.dialect.columnDefinition(columnInfo.name, columnInfo.type, columnInfo.defaultValue, columnInfo.isPrimaryKey));
657
+ for (const key in columns) {
658
+ const columnInfo = columns[key];
659
+ queryLines.push(this.dialect.columnDefinition(columnInfo.name, columnInfo.type, columnInfo.isPrimaryKey, columnInfo.defaultValue));
445
660
  }
446
661
  if (foreignKeys) {
447
662
  for (const foreignKeyInfo of foreignKeys) {
@@ -474,13 +689,16 @@ ${changes.up}
474
689
  const migrationEntry = migrationData[tableName];
475
690
  if (!migrationEntry.recreate)
476
691
  continue;
477
- Logger.log(`Table ${tableName} will be recreated!`);
478
692
  this.migrations.compareWithAllowedMigration(tableName, "recreateTable");
479
693
  if (!this.dialect.canInspectForeignKeys || !this.dialect.canInspectPrimaryKey)
480
694
  this.migrations.compareWithAllowedMigration(tableName, "continueWithoutRollback");
481
695
  const oldColumnList = await this.dialect.getColumnInformation(this.migrations.getOldTableName(tableName));
482
696
  const newColumnList = this.newTables[tableName].columns;
483
- const moveableColumns = newColumnList.filter((entry) => oldColumnList.find((oldEntry) => oldEntry.name == entry.name) != void 0).map((entry) => entry.name);
697
+ const moveableColumns = [];
698
+ for (const key in newColumnList) {
699
+ if (oldColumnList.hasOwnProperty(key))
700
+ moveableColumns.push(newColumnList[key].name);
701
+ }
484
702
  const backupTableName = `${tableName}__backup`;
485
703
  const insertQuery = this.dialect.insert(
486
704
  backupTableName,
@@ -488,7 +706,7 @@ ${changes.up}
488
706
  );
489
707
  const moveDataQuery = insertQuery + "\n" + this.dialect.dropTable(tableName) + "\n" + this.dialect.renameTable(backupTableName, tableName);
490
708
  const structure = this.newTables[tableName];
491
- changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery + "\n";
709
+ changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery + "\n\n";
492
710
  const dbTableName = this.migrations.getOldTableName(tableName);
493
711
  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";
494
712
  }
@@ -521,7 +739,7 @@ ${changes.up}
521
739
  this.dialect.foreignKey(oldForeignKey.fromColumn, oldForeignKey.toTable, oldForeignKey.toColumn, oldForeignKey.onUpdate, oldForeignKey.onDelete)
522
740
  );
523
741
  } else {
524
- this.migrations.recreateTableImp(structure.table);
742
+ this.migrations.internalRecreate(structure.table, "Dialect does not support altering foreign keys");
525
743
  checkForNewForeignKeys = false;
526
744
  }
527
745
  } else {
@@ -538,7 +756,7 @@ ${changes.up}
538
756
  this.dialect.foreignKey(oldForeignKey.fromColumn, oldForeignKey.toTable, oldForeignKey.toColumn, oldForeignKey.onUpdate, oldForeignKey.onDelete)
539
757
  ) + this.dialect.removeForeignKey(newForeignKey.fromTable, newForeignKey.fromColumn);
540
758
  } else {
541
- this.migrations.recreateTableImp(structure.table);
759
+ this.migrations.internalRecreate(structure.table, "Dialect does not support altering foreign keys");
542
760
  checkForNewForeignKeys = false;
543
761
  }
544
762
  }
@@ -557,7 +775,7 @@ ${changes.up}
557
775
  );
558
776
  changes.down += this.dialect.removeForeignKey(newForeignKey.fromTable, newForeignKey.fromColumn);
559
777
  } else {
560
- this.migrations.recreateTableImp(structure.table);
778
+ this.migrations.internalRecreate(structure.table, "Dialect does not support altering foreign keys");
561
779
  break;
562
780
  }
563
781
  }
@@ -580,6 +798,10 @@ ${changes.up}
580
798
  const newTableDefinition = this.newTables[tableName];
581
799
  const oldColumnList = await this.dialect.getColumnInformation(this.migrations.getOldTableName(tableName));
582
800
  const oldPrimaryKey = this.dialect.canInspectPrimaryKey ? this.getPrimaryKeyColumn(oldColumnList) : false;
801
+ const oldColumnNewNameIndex = {};
802
+ for (const key in oldColumnList) {
803
+ oldColumnNewNameIndex[this.migrations.getNewestColumnName(tableName, key)] = oldColumnList[key];
804
+ }
583
805
  const newColumnList = newTableDefinition.columns;
584
806
  const newPrimaryKey = newTableDefinition.primaryKey;
585
807
  if (this.dialect.canInspectPrimaryKey && oldPrimaryKey != newPrimaryKey) {
@@ -595,28 +817,26 @@ ${changes.up}
595
817
  changes.down += this.dialect.removePrimaryKey(tableName, newPrimaryKey.toString());
596
818
  }
597
819
  } else
598
- this.migrations.recreateTableImp(newTableDefinition.table);
820
+ this.migrations.internalRecreate(newTableDefinition.table, "Dialect does not support altering primary keys");
599
821
  continue;
600
822
  }
601
- for (const newColumn of newColumnList) {
602
- const oldColumn = oldColumnList.find(
603
- (entry) => this.migrations.getNewestColumnName(tableName, entry.name) == newColumn.name
604
- );
823
+ for (const key in newColumnList) {
824
+ const newColumn = newColumnList[key];
825
+ const oldColumn = oldColumnNewNameIndex[newColumn.name];
605
826
  if (oldColumn == void 0) {
606
- changes.up += this.dialect.createColumn(tableName, this.dialect.columnDefinition(newColumn.name, newColumn.type, newColumn.defaultValue, newColumn.isPrimaryKey)) + "\n";
827
+ changes.up += this.dialect.createColumn(tableName, this.dialect.columnDefinition(newColumn.name, newColumn.type, newColumn.isPrimaryKey, newColumn.defaultValue)) + "\n";
607
828
  changes.down += this.dialect.dropColumn(tableName, newColumn.name) + "\n";
608
829
  } else if (newColumn.type != oldColumn.type || newColumn.defaultValue != oldColumn.defaultValue) {
609
- this.migrations.recreateTableImp(newTableDefinition.table);
830
+ this.migrations.internalRecreate(newTableDefinition.table, "Table structure was changed");
610
831
  }
611
832
  }
612
- for (const oldColumn of oldColumnList) {
613
- const newColumn = newColumnList.find(
614
- (entry) => entry.name == this.migrations.getNewestColumnName(tableName, oldColumn.name)
615
- );
833
+ for (const key in oldColumnList) {
834
+ const oldColumn = oldColumnList[key];
835
+ const newColumn = newColumnList[this.migrations.getNewestColumnName(tableName, oldColumn.name)];
616
836
  if (newColumn == void 0) {
617
837
  this.migrations.compareWithAllowedMigration(tableName, "dropColumn");
618
838
  changes.up += this.dialect.dropColumn(tableName, oldColumn.name) + "\n";
619
- changes.down += this.dialect.createColumn(tableName, this.dialect.columnDefinition(oldColumn.name, oldColumn.type, oldColumn.defaultValue, oldColumn.isPrimaryKey)) + "\n";
839
+ changes.down += this.dialect.createColumn(tableName, this.dialect.columnDefinition(oldColumn.name, oldColumn.type, oldColumn.isPrimaryKey, oldColumn.defaultValue)) + "\n";
620
840
  }
621
841
  }
622
842
  }
@@ -625,11 +845,12 @@ ${changes.up}
625
845
  /**
626
846
  * Retrieves the name of the primary key column from a list of column information.
627
847
  *
628
- * @param columnInfoList - An array of ColumnInfo objects containing details about each column.
848
+ * @param columnInfoEntries - An array of ColumnInfo objects containing details about each column.
629
849
  * @return The name of the primary key column if found; otherwise, undefined.
630
850
  */
631
- getPrimaryKeyColumn(columnInfoList) {
632
- for (const columnInfo of columnInfoList) {
851
+ getPrimaryKeyColumn(columnInfoEntries) {
852
+ for (const key in columnInfoEntries) {
853
+ const columnInfo = columnInfoEntries[key];
633
854
  if (columnInfo.isPrimaryKey)
634
855
  return columnInfo.name;
635
856
  }
@@ -715,26 +936,49 @@ ${changes.down}`, { encoding: "utf-8" });
715
936
  }
716
937
  };
717
938
 
718
- // src/lib/tableInfo/decorators/DbTable.ts
939
+ // src/lib/tableInfo/decorators/TableClass.ts
719
940
  import "polyfill-symbol-metadata";
720
- function DbTable(tableName, primaryKey) {
941
+ function TableClass(tableName, primaryKey) {
721
942
  return (table, context) => {
722
- Object.defineProperty(table, "name", { value: tableName, writable: false });
723
- const tableInfo = context?.metadata ? getTableInfoFromMetadata(context.metadata) : getTableInfo(table);
724
- tableInfo.primaryKey = primaryKey?.toString();
725
- if (tableInfo.foreignKeys) {
726
- for (const foreignKey of tableInfo.foreignKeys) {
943
+ const tableStructure = context?.metadata ? getTableStructureFromMetadata(context.metadata) : getTableStructure(table);
944
+ tableStructure.primaryKey = primaryKey?.toString();
945
+ tableStructure.table = tableName;
946
+ if (tableStructure.foreignKeys) {
947
+ for (const foreignKey of tableStructure.foreignKeys) {
727
948
  foreignKey.fromTable = tableName;
949
+ if (foreignKey.toTable == table.name)
950
+ foreignKey.toTable = tableName;
728
951
  }
729
952
  }
953
+ for (const key in table) {
954
+ const value = table[key];
955
+ const existingEntry = tableStructure.columns[key];
956
+ const defaultEntry = TableObj.getColumnEntry(key, value);
957
+ if (!defaultEntry)
958
+ continue;
959
+ tableStructure.columns[key] = {
960
+ ...defaultEntry,
961
+ ...existingEntry
962
+ };
963
+ }
964
+ Object.defineProperty(table, "name", { value: tableName, writable: false });
965
+ };
966
+ }
967
+
968
+ // src/lib/tableInfo/decorators/DataType.ts
969
+ import "polyfill-symbol-metadata";
970
+ function DataType(type) {
971
+ return (table, context) => {
972
+ const entry = getInputColumnInfo(table, context);
973
+ entry.inputType = type;
730
974
  };
731
975
  }
732
976
 
733
977
  // src/lib/tableInfo/decorators/ForeignKey.ts
734
978
  import "polyfill-symbol-metadata";
735
- function ForeignKey(toTable, toColumn, onDelete, onUpdate) {
979
+ function ForeignKey(toTable, toColumn, options) {
736
980
  return (table, context) => {
737
- const metadata = context?.metadata ? getTableInfoFromMetadata(context.metadata) : getTableInfo(table);
981
+ const metadata = context?.metadata ? getTableStructureFromMetadata(context.metadata) : getTableStructure(table);
738
982
  if (!metadata.foreignKeys)
739
983
  metadata.foreignKeys = [];
740
984
  metadata.foreignKeys.push({
@@ -743,8 +987,8 @@ function ForeignKey(toTable, toColumn, onDelete, onUpdate) {
743
987
  //to stay compatible with old decorator structure (see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#decorator-metadata)
744
988
  toTable: toTable.name,
745
989
  toColumn: toColumn.toString(),
746
- onDelete,
747
- onUpdate
990
+ onDelete: options?.onDelete,
991
+ onUpdate: options?.onUpdate
748
992
  });
749
993
  };
750
994
  }
@@ -757,32 +1001,111 @@ var DefaultSql = class {
757
1001
  canAlterPrimaryKey = false;
758
1002
  canInspectForeignKeys = false;
759
1003
  canInspectPrimaryKey = false;
760
- typeString = "TEXT";
761
- typeInt = "INTEGER";
762
- typeBigInt = "BIGINT";
763
- typeBoolean = "BOOLEAN";
764
- typeNull = "NULL";
1004
+ types = {
1005
+ string: "TEXT",
1006
+ number: "INTEGER",
1007
+ bigint: "BIGINT",
1008
+ boolean: "BOOLEAN",
1009
+ date: "DATE",
1010
+ time: "TIME",
1011
+ dateTime: "DATETIME",
1012
+ null: "NULL"
1013
+ };
765
1014
  constructor(db) {
766
1015
  this.db = db;
767
1016
  }
768
- formatValueToSql(value) {
769
- return value.toString();
1017
+ /**
1018
+ * Formats a given value to a SQL-equivalent string based on the provided data type.
1019
+ *
1020
+ * @param value - The value to be formatted.
1021
+ * @param type - The data type of the value. Either determined through typeof or {@link TableStructure} setting
1022
+ * @return A string representation of the value formatted for SQL.
1023
+ */
1024
+ formatValueToSql(value, type) {
1025
+ function toDate(value2) {
1026
+ return value2 instanceof Date ? value2 : new Date(value2);
1027
+ }
1028
+ switch (type) {
1029
+ case "string":
1030
+ return `"${value}"`;
1031
+ case "time":
1032
+ return `"${toDate(value).toISOString().slice(11, 19)}"`;
1033
+ case "date":
1034
+ return `"${toDate(value).toISOString().slice(0, 10)}"`;
1035
+ case "dateTime":
1036
+ return `"${toDate(value).toISOString().slice(0, 19).replace("T", " ")}"`;
1037
+ default:
1038
+ return value.toString();
1039
+ }
1040
+ }
1041
+ getSqlType(dataType, _) {
1042
+ return this.types[dataType];
770
1043
  }
1044
+ /**
1045
+ * Statement to enable / disable foreign keys in the database.
1046
+ * Is dialect specific.
1047
+ *
1048
+ * @param enabled - A boolean indicating whether to enable or disable foreign key constraints.
1049
+ * @return The SQL statement as a string.
1050
+ */
771
1051
  changeForeignKeysState(enabled) {
772
1052
  return "";
773
1053
  }
1054
+ /**
1055
+ * Statement to add a foreign key to a specified table.
1056
+ * Is only used if {@link canAlterForeignKeys} is true.
1057
+ *
1058
+ * @param fromTableName - The name of the table to which the foreign key will be added.
1059
+ * @param foreignKey - The foreign key definition to be added, see {@link foreignKey()}.
1060
+ * @return The SQL statement as a string.
1061
+ */
774
1062
  addForeignKey(fromTableName, foreignKey) {
775
1063
  throw new Error("Adding foreign keys is not supported!");
776
1064
  }
1065
+ /**
1066
+ * Statement to remove a foreign key from a specified table.
1067
+ * Is only used if {@link canAlterForeignKeys} is true.
1068
+ *
1069
+ * @param tableName - The name of the table to which the foreign key will be added.
1070
+ * @param foreignKeyName - The name of the foreign key definition.
1071
+ * @return The SQL statement as a string.
1072
+ */
777
1073
  removeForeignKey(tableName, foreignKeyName) {
778
1074
  throw new Error("Foreign key removal is not supported!");
779
1075
  }
1076
+ /**
1077
+ * Statement to add a primary key to the specified table.
1078
+ * Is only used if {@link canAlterPrimaryKey} is true.
1079
+ *
1080
+ * @param tableName - The name of the table to which the primary key will be added.
1081
+ * @param columnName - The name of the column that will be set as the primary key.
1082
+ * @return The SQL statement as a string.
1083
+ */
780
1084
  addPrimaryKey(tableName, columnName) {
781
1085
  throw new Error("Adding foreign keys is not supported!");
782
1086
  }
1087
+ /**
1088
+ * Statement to remove a primary key from the specified table.
1089
+ * Is only used if {@link canAlterPrimaryKey} is true.
1090
+ *
1091
+ * @param tableName - The name of the table to which the primary key will be added.
1092
+ * @param columnName - The name of the column that will be set as the primary key.
1093
+ * @return The SQL statement as a string.
1094
+ */
783
1095
  removePrimaryKey(tableName, columnName) {
784
1096
  throw new Error("Foreign key removal is not supported!");
785
1097
  }
1098
+ /**
1099
+ * Constructs a SQL FOREIGN KEY constraint clause.
1100
+ * Used in combination with {@link addForeignKey} or {@link createTable}
1101
+ *
1102
+ * @param column - The name of the column in the current table to apply the foreign key constraint.
1103
+ * @param foreignTable - The name of the referenced table.
1104
+ * @param foreignColumn - The name of the referenced column in the foreign table.
1105
+ * @param onUpdate - Optional action to specify the behavior on update (e.g., CASCADE, SET NULL).
1106
+ * @param onDelete - Optional action to specify the behavior on delete (e.g., CASCADE, SET NULL).
1107
+ * @return The SQL string representing the foreign key constraint.
1108
+ */
786
1109
  foreignKey(column, foreignTable, foreignColumn, onUpdate, onDelete) {
787
1110
  let query = `FOREIGN KEY (${column}) REFERENCES ${foreignTable} (${foreignColumn})`;
788
1111
  if (onUpdate)
@@ -791,64 +1114,164 @@ var DefaultSql = class {
791
1114
  query += ` ON DELETE ${onDelete}`;
792
1115
  return query;
793
1116
  }
1117
+ /**
1118
+ * Statement to create a table if it does not already exist.
1119
+ *
1120
+ * @param tableName - The name of the table to be created.
1121
+ * @param entries - An array containing the column definitions for the table.
1122
+ * @return The SQL statement as a string.
1123
+ */
794
1124
  createTable(tableName, entries) {
795
1125
  return `CREATE TABLE IF NOT EXISTS ${tableName} (
796
1126
  ${entries.join(",\n ")}
797
1127
  );`;
798
1128
  }
1129
+ /**
1130
+ * Statement to rename a table.
1131
+ *
1132
+ * @param tableName - The name of the existing table to be renamed.
1133
+ * @param newTableName - The new name for the table.
1134
+ * @return The SQL statement as a string.
1135
+ */
799
1136
  renameTable(tableName, newTableName) {
800
1137
  return `ALTER TABLE ${tableName} RENAME TO ${newTableName};`;
801
1138
  }
1139
+ /**
1140
+ * Statement to drop a table if it exists.
1141
+ *
1142
+ * @param tableName - The name of the table to be dropped.
1143
+ * @return The SQL statement as a string.
1144
+ */
802
1145
  dropTable(tableName) {
803
1146
  return `DROP TABLE IF EXISTS ${tableName};`;
804
1147
  }
805
- columnDefinition(columnName, type, defaultValue, isPrimaryKey) {
806
- const query = `${columnName} ${type} DEFAULT ${defaultValue}`;
1148
+ /**
1149
+ * Constructs a SQL column definition based on the provided parameters.
1150
+ *
1151
+ * @param columnName - The name of the column.
1152
+ * @param type - The data type of the column.
1153
+ * @param defaultValue - The default value for the column.
1154
+ * @param isPrimaryKey - Indicates whether the column is a primary key.
1155
+ * @return The constructed SQL column definition string.
1156
+ */
1157
+ columnDefinition(columnName, type, isPrimaryKey, defaultValue) {
1158
+ const query = `${columnName} ${type}${defaultValue ? ` DEFAULT ${defaultValue}` : ""}`;
807
1159
  return isPrimaryKey ? `${query} PRIMARY KEY` : query;
808
1160
  }
1161
+ /**
1162
+ * Statement to add a new column to a specified table.
1163
+ *
1164
+ * @param columnTable - The name of the table to which the column will be added.
1165
+ * @param entry - The column definition including the column name and its data type. See {@link columnDefinition()}
1166
+ * @return The SQL statement as a string.
1167
+ */
809
1168
  createColumn(columnTable, entry) {
810
1169
  return `ALTER TABLE ${columnTable} ADD ${entry};`;
811
1170
  }
1171
+ /**
1172
+ * Statement to rename a column in an existing table.
1173
+ *
1174
+ * @param tableName - The name of the table containing the column to rename.
1175
+ * @param oldColumnName - The current name of the column to be renamed.
1176
+ * @param newColumnName - The new name for the column.
1177
+ * @return The SQL statement as a string.
1178
+ */
812
1179
  renameColumn(tableName, oldColumnName, newColumnName) {
813
1180
  return `ALTER TABLE ${tableName} RENAME COLUMN ${oldColumnName} TO ${newColumnName};`;
814
1181
  }
815
- copyColumn(tableName, oldColumnName, newColumnName) {
816
- return `UPDATE ${tableName} SET ${newColumnName} = ${oldColumnName};`;
817
- }
1182
+ /**
1183
+ * Statement to drop a column from a specified table.
1184
+ *
1185
+ * @param tableName - The name of the table from which the column will be dropped.
1186
+ * @param columnName - The name of the column to be dropped.
1187
+ * @return The SQL statement as a string.
1188
+ */
818
1189
  dropColumn(tableName, columnName) {
819
1190
  return `ALTER TABLE ${tableName} DROP COLUMN ${columnName};`;
820
1191
  }
1192
+ /**
1193
+ * Statement to return data from a table.
1194
+ *
1195
+ * @param tableName - The name of the database table to select data from.
1196
+ * @param select - An array of column names to include in the SELECT clause.
1197
+ * @param where - An optional condition.
1198
+ * @return The SQL statement as a string.
1199
+ */
821
1200
  select(tableName, select, where) {
822
1201
  let query = `SELECT ${select.join(",")} FROM ${tableName}`;
823
1202
  if (where)
824
1203
  query += ` WHERE ${where}`;
825
1204
  return `${query};`;
826
1205
  }
1206
+ /**
1207
+ * Statement to insert data into a table.
1208
+ *
1209
+ * @param tableName - The name of the table where the data will be inserted.
1210
+ * @param content - The value query. See {@link insertValues()}.
1211
+ * @return The SQL statement as a string.
1212
+ */
827
1213
  insert(tableName, content) {
828
1214
  return `INSERT INTO ${tableName} ${content};`;
829
1215
  }
1216
+ /**
1217
+ * A SQL insert query string. See {@link insert()}.
1218
+ *
1219
+ * @param keys - An array of column names to be used in the query.
1220
+ * @param [valueString] - An optional preformatted value string to be included in the query.
1221
+ * If not provided, the method generates a default placeholder string with "?" for each key.
1222
+ * @return The SQL statement as a string.
1223
+ */
830
1224
  insertValues(keys, valueString) {
831
1225
  return `(${keys}) ${valueString ?? `VALUES (${keys.map(() => "?").join(",")})`}`;
832
1226
  }
1227
+ /**
1228
+ * Returns a SQL query string to create a migration table.
1229
+ * The table is used to store migration data, specifically the version of the migration.
1230
+ *
1231
+ * @return The SQL query string for creating the migration table.
1232
+ */
833
1233
  migrationTableQuery() {
834
1234
  return this.createTable(MIGRATION_DATA_TABLE_NAME, [
835
- this.columnDefinition("version", this.typeInt, "0", false)
1235
+ this.columnDefinition("version", this.types.number, false, "0")
836
1236
  ]);
837
1237
  }
1238
+ /**
1239
+ * Fetches the foreign keys for the specified table.
1240
+ * This method is only used if {@link canInspectForeignKeys} is true
1241
+ *
1242
+ * @param tableName - The name of the table for which to retrieve foreign key information.
1243
+ * @return {Promise<ForeignKeyInfo[]>} A promise that resolves to an array of {@link ForeignKeyInfo}.
1244
+ */
838
1245
  async getForeignKeys(tableName) {
839
1246
  throw new Error("Inspecting foreign keys is not supported!");
840
1247
  }
1248
+ /**
1249
+ * Ensures that the migration table exists in the database.
1250
+ * If the table does not exist, it creates the table using the defined migration table query.
1251
+ *
1252
+ * @return A promise that resolves when the migration table is created successfully.
1253
+ */
841
1254
  async createMigrationTableIfNeeded() {
842
1255
  await this.db.runMultipleWriteStatements(this.migrationTableQuery());
843
1256
  }
844
- //TODO: untested
1257
+ /**
1258
+ * Retrieves the current version from the migration data table.
1259
+ *
1260
+ * @return A promise that resolves to the current version number. Returns 0 if no version is found.
1261
+ */
845
1262
  async getVersion() {
846
1263
  await this.createMigrationTableIfNeeded();
847
1264
  const query = this.select(MIGRATION_DATA_TABLE_NAME, ["version"]);
848
1265
  const data = await this.db.runGetStatement(query);
849
1266
  return data.length ? data[0].version : 0;
850
1267
  }
851
- //TODO: untested
1268
+ /**
1269
+ * Updates the migration version in the database. If the version entry does not exist, it inserts a new one;
1270
+ * if it exists, it updates the version to the specified value.
1271
+ *
1272
+ * @param newVersion - The new migration version to set in the database.
1273
+ * @return A promise that resolves when the operation is complete.
1274
+ */
852
1275
  async setVersion(newVersion) {
853
1276
  const query = `${this.migrationTableQuery()};
854
1277
  INSERT INTO ${MIGRATION_DATA_TABLE_NAME} (version) VALUES ${newVersion} WHERE NOT EXISTS (SELECT 1 FROM ${MIGRATION_DATA_TABLE_NAME})
@@ -861,8 +1284,16 @@ UPDATE ${MIGRATION_DATA_TABLE_NAME} SET version = ${newVersion} WHERE EXISTS (SE
861
1284
 
862
1285
  // src/lib/dialects/SqliteDialect.ts
863
1286
  var SqliteDialect = class extends DefaultSql {
864
- typeBoolean = "INTEGER";
865
- typeBigInt = "INTEGER";
1287
+ types = {
1288
+ string: "TEXT",
1289
+ number: "INTEGER",
1290
+ bigint: "INTEGER",
1291
+ boolean: "INTEGER",
1292
+ date: "INTEGER",
1293
+ time: "INTEGER",
1294
+ dateTime: "INTEGER",
1295
+ null: "NULL"
1296
+ };
866
1297
  canAlterForeignKeys = false;
867
1298
  canAlterPrimaryKey = false;
868
1299
  canInspectForeignKeys = true;
@@ -875,21 +1306,30 @@ var SqliteDialect = class extends DefaultSql {
875
1306
  }
876
1307
  async getColumnInformation(tableName) {
877
1308
  const data = await this.db.runGetStatement(`PRAGMA table_info(${tableName})`);
878
- return data.map((entry) => {
879
- return {
1309
+ const output = {};
1310
+ for (const entry of data) {
1311
+ output[entry["name"]] = {
880
1312
  name: entry["name"],
881
1313
  type: entry["type"],
882
1314
  defaultValue: entry["dflt_value"],
883
1315
  isPrimaryKey: entry["pk"] == "1"
884
1316
  };
885
- });
1317
+ }
1318
+ return output;
886
1319
  }
887
- formatValueToSql(value) {
888
- switch (typeof value) {
1320
+ formatValueToSql(value, type) {
1321
+ function toDate(value2) {
1322
+ return value2 instanceof Date ? value2 : new Date(value2);
1323
+ }
1324
+ switch (type) {
889
1325
  case "boolean":
890
1326
  return value ? "1" : "0";
1327
+ case "date":
1328
+ case "time":
1329
+ case "dateTime":
1330
+ return toDate(value).getTime().toString();
891
1331
  default:
892
- return value.toString();
1332
+ return super.formatValueToSql(value, type);
893
1333
  }
894
1334
  }
895
1335
  async getForeignKeys(tableName) {
@@ -900,8 +1340,8 @@ var SqliteDialect = class extends DefaultSql {
900
1340
  fromColumn: entry["from"],
901
1341
  toTable: entry["table"],
902
1342
  toColumn: entry["to"],
903
- on_update: entry["on_update"],
904
- on_delete: entry["on_delete"]
1343
+ onUpdate: entry["on_update"] ?? "NO ACTION",
1344
+ onDelete: entry["on_delete"] ?? "NO ACTION"
905
1345
  };
906
1346
  });
907
1347
  }
@@ -934,14 +1374,17 @@ var PostgresDialect = class extends DefaultSql {
934
1374
  }
935
1375
  async getColumnInformation(tableName) {
936
1376
  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}';`);
937
- return data.map((entry) => {
938
- return {
1377
+ const output = {};
1378
+ for (const entry of data) {
1379
+ output[entry["name"]] = {
939
1380
  name: entry["column_name"],
940
1381
  type: entry["data_type"],
1382
+ maxLength: 0,
941
1383
  defaultValue: entry["column_default"],
942
1384
  isPrimaryKey: false
943
1385
  };
944
- });
1386
+ }
1387
+ return output;
945
1388
  }
946
1389
  addPrimaryKey(tableName, columnName) {
947
1390
  return `ALTER TABLE ${tableName} ADD PRIMARY KEY (${columnName});
@@ -986,18 +1429,21 @@ var MsSqlDialect = class extends DefaultSql {
986
1429
  });
987
1430
  }
988
1431
  async getTableNames() {
989
- return await this.db.runGetStatement("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'");
1432
+ return (await this.db.runGetStatement("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'")).map((entry) => entry.name);
990
1433
  }
991
1434
  async getColumnInformation(tableName) {
992
1435
  const data = await this.db.runGetStatement(`SELECT COLUMN_NAME, DATA_TYPE, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = N'${tableName};'`);
993
- return data.map((entry) => {
994
- return {
995
- name: entry["COLUMN_NAME"],
1436
+ const output = {};
1437
+ for (const entry of data) {
1438
+ output[entry["name"]] = {
1439
+ name: entry["name"],
996
1440
  type: entry["DATA_TYPE"],
1441
+ maxLength: 0,
997
1442
  defaultValue: entry["COLUMN_DEFAULT"],
998
- isPrimaryKey: false
1443
+ isPrimaryKey: entry["pk"] == "1"
999
1444
  };
1000
- });
1445
+ }
1446
+ return output;
1001
1447
  }
1002
1448
  };
1003
1449
 
@@ -1007,9 +1453,26 @@ var MySqlDialect = class extends DefaultSql {
1007
1453
  canAlterPrimaryKey = true;
1008
1454
  canInspectForeignKeys = true;
1009
1455
  canInspectPrimaryKey = true;
1456
+ types = {
1457
+ string: "VARCHAR",
1458
+ number: "INT",
1459
+ bigint: "BIGINT",
1460
+ boolean: "BOOLEAN",
1461
+ date: "DATE",
1462
+ time: "TIME",
1463
+ dateTime: "DATETIME",
1464
+ null: "NULL"
1465
+ };
1466
+ getSqlType(dataType, columnInfo) {
1467
+ switch (dataType) {
1468
+ case "string":
1469
+ return this.types[dataType] + `(${columnInfo?.maxLength})`;
1470
+ default:
1471
+ return super.getSqlType(dataType, columnInfo);
1472
+ }
1473
+ }
1010
1474
  changeForeignKeysState(enabled) {
1011
- return `SET FOREIGN_KEY_CHECKS ${enabled ? "1" : "0"}
1012
- ;`;
1475
+ return `SET FOREIGN_KEY_CHECKS ${enabled ? "1" : "0"};`;
1013
1476
  }
1014
1477
  addForeignKey(fromTableName, foreignKey) {
1015
1478
  return `ALTER TABLE ${fromTableName} ADD ${foreignKey};`;
@@ -1035,18 +1498,22 @@ var MySqlDialect = class extends DefaultSql {
1035
1498
  });
1036
1499
  }
1037
1500
  async getTableNames() {
1038
- return await this.db.runGetStatement("SHOW TABLES;");
1501
+ const t = await this.db.runGetStatement("SELECT table_name FROM information_schema.tables;");
1502
+ return (await this.db.runGetStatement("SELECT table_name FROM information_schema.tables;")).map((entry) => entry.name);
1039
1503
  }
1040
1504
  async getColumnInformation(tableName) {
1041
1505
  const data = await this.db.runGetStatement(`SHOW COLUMNS FROM ${tableName};`);
1042
- return data.map((entry) => {
1043
- return {
1044
- name: entry["Field"],
1045
- type: entry["Type"],
1046
- defaultValue: entry["Default"],
1047
- isPrimaryKey: entry["Key"] == "PRI"
1506
+ const output = {};
1507
+ for (const entry of data) {
1508
+ output[entry["name"]] = {
1509
+ name: entry["name"],
1510
+ type: entry["type"],
1511
+ maxLength: 0,
1512
+ defaultValue: entry["dflt_value"],
1513
+ isPrimaryKey: entry["pk"] == "1"
1048
1514
  };
1049
- });
1515
+ }
1516
+ return output;
1050
1517
  }
1051
1518
  };
1052
1519
 
@@ -1126,8 +1593,10 @@ async function rollback(db, dbInstructions, toVersion) {
1126
1593
  await dialect.setVersion(toVersion);
1127
1594
  }
1128
1595
  export {
1129
- DbTable,
1596
+ DataType,
1130
1597
  ForeignKey,
1598
+ TableClass,
1599
+ TableObj,
1131
1600
  prepareAndRunMigration,
1132
1601
  prepareMigration,
1133
1602
  rollback,