sqlmorpheus 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +251 -12
- package/dist/index.d.cts +52 -3
- package/dist/index.d.mts +52 -3
- package/dist/index.mjs +251 -13
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -134,6 +134,10 @@ function getTableStructureFromMetadata(metadata) {
|
|
|
134
134
|
//#endregion
|
|
135
135
|
//#region src/lib/TableStructureGenerator.ts
|
|
136
136
|
/**
|
|
137
|
+
* Postgres silently truncates longer identifiers, which would lead to differing index names in each migration
|
|
138
|
+
*/
|
|
139
|
+
const MAX_INDEX_NAME_LENGTH = 63;
|
|
140
|
+
/**
|
|
137
141
|
* Represents a generator for creating table structures based on specified database instructions
|
|
138
142
|
*/
|
|
139
143
|
var TableStructureGenerator = class {
|
|
@@ -150,24 +154,55 @@ var TableStructureGenerator = class {
|
|
|
150
154
|
*/
|
|
151
155
|
generateTableStructure() {
|
|
152
156
|
const tables = {};
|
|
157
|
+
const indexNames = {};
|
|
153
158
|
for (const table of this.dbInstructions.tables) {
|
|
154
159
|
let structure;
|
|
160
|
+
let tableName;
|
|
155
161
|
if (TableObjHelper.isTableObj(table)) {
|
|
156
162
|
structure = table.tableStructure;
|
|
157
|
-
|
|
163
|
+
tableName = table.tableName;
|
|
158
164
|
} else {
|
|
159
165
|
const classTable = table;
|
|
160
166
|
structure = getTableStructure(classTable);
|
|
161
|
-
|
|
167
|
+
tableName = classTable.name;
|
|
162
168
|
}
|
|
163
169
|
for (const key in structure.columns) {
|
|
164
170
|
const entry = structure.columns[key];
|
|
165
171
|
entry.defaultValue = entry.inputDefaultValue === null ? this.dialect.nullType : this.dialect.formatValueToSql(entry.inputDefaultValue, entry.inputType);
|
|
166
172
|
entry.sqlType = this.dialect.getSqlType(entry.inputType, entry);
|
|
167
173
|
}
|
|
174
|
+
const indexes = this.getIndexes(tableName, structure);
|
|
175
|
+
for (const index of indexes) {
|
|
176
|
+
if (indexNames[index.name]) throw new Error(`Index name "${index.name}" is used in ${indexNames[index.name]} and ${tableName}. Index names need to be unique.`);
|
|
177
|
+
indexNames[index.name] = tableName;
|
|
178
|
+
}
|
|
179
|
+
tables[tableName] = {
|
|
180
|
+
...structure,
|
|
181
|
+
indexes
|
|
182
|
+
};
|
|
168
183
|
}
|
|
169
184
|
return tables;
|
|
170
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Validates the index definitions of a table and generates names for indexes that have none.
|
|
188
|
+
*
|
|
189
|
+
* @param tableName - The name of the table the indexes belong to.
|
|
190
|
+
* @param structure - The table structure containing the index definitions.
|
|
191
|
+
* @return The index definitions including their names.
|
|
192
|
+
*/
|
|
193
|
+
getIndexes(tableName, structure) {
|
|
194
|
+
return (structure.indexes ?? []).map((index) => {
|
|
195
|
+
if (!index.columns.length) throw new Error(`An index in ${tableName} has no columns.`);
|
|
196
|
+
for (const column of index.columns) if (!structure.columns[column]) throw new Error(`Index in ${tableName} uses column "${column}", which does not exist.`);
|
|
197
|
+
const name = index.name ?? `${tableName}_${index.columns.join("_")}_idx`;
|
|
198
|
+
if (name.length > MAX_INDEX_NAME_LENGTH) throw new Error(`Index name "${name}" is longer than ${MAX_INDEX_NAME_LENGTH} characters. Please provide a shorter name.`);
|
|
199
|
+
return {
|
|
200
|
+
name,
|
|
201
|
+
columns: index.columns,
|
|
202
|
+
isUnique: index.isUnique
|
|
203
|
+
};
|
|
204
|
+
});
|
|
205
|
+
}
|
|
171
206
|
};
|
|
172
207
|
//#endregion
|
|
173
208
|
//#region src/lib/typings/AllowedMigrations.ts
|
|
@@ -290,6 +325,7 @@ var Migrations = class {
|
|
|
290
325
|
* @param tableName - The name of the table to check the migration against.
|
|
291
326
|
* @param type - The type of destructive migration being checked.
|
|
292
327
|
* @param column - The column name for which the migration is allowed. Only needed for "dropColumn", "removeForeignKey" and "alterForeignKey".
|
|
328
|
+
* For "dropIndex", the index name is used instead.
|
|
293
329
|
*/
|
|
294
330
|
compareWithAllowedMigration(tableName, type, column = NO_COLUMN) {
|
|
295
331
|
const migrationEntry = this.migrationData[tableName];
|
|
@@ -391,6 +427,7 @@ var Migrations = class {
|
|
|
391
427
|
* @param tableName - The current table name in the database at the time of the migration version.
|
|
392
428
|
* @param allowedMigration - The keys representing the migrations to be allowed.
|
|
393
429
|
* @param column - The column name for which the migration is allowed. Only needed for "dropColumn", "removeForeignKey" and "alterForeignKey".
|
|
430
|
+
* For "dropIndex", provide the index name instead.
|
|
394
431
|
*
|
|
395
432
|
* @throws Error if the provided column name is not valid for the specified migration.
|
|
396
433
|
*/
|
|
@@ -408,6 +445,7 @@ var Migrations = class {
|
|
|
408
445
|
case "dropColumn":
|
|
409
446
|
case "removeForeignKey":
|
|
410
447
|
case "alterForeignKey": throw new Error(`The ${allowedMigration} migration requires a column name. Please provide one.`);
|
|
448
|
+
case "dropIndex": throw new Error(`The ${allowedMigration} migration requires an index name. Please provide one.`);
|
|
411
449
|
}
|
|
412
450
|
}
|
|
413
451
|
/**
|
|
@@ -676,6 +714,26 @@ var DefaultSql = class {
|
|
|
676
714
|
return `ALTER TABLE ${this.formatIdentifier(tableName)} DROP COLUMN ${this.formatIdentifier(columnName)};`;
|
|
677
715
|
}
|
|
678
716
|
/**
|
|
717
|
+
* Statement to create an index on a specified table.
|
|
718
|
+
*
|
|
719
|
+
* @param tableName - The name of the table on which the index will be created.
|
|
720
|
+
* @param index - The index definition.
|
|
721
|
+
* @return The SQL statement as a string.
|
|
722
|
+
*/
|
|
723
|
+
createIndex(tableName, index) {
|
|
724
|
+
return `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX ${this.formatIdentifier(index.name)} ON ${this.formatIdentifier(tableName)} (${index.columns.map((column) => this.formatIdentifier(column)).join(", ")});`;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Statement to drop an index from a specified table.
|
|
728
|
+
*
|
|
729
|
+
* @param tableName - The name of the table the index belongs to.
|
|
730
|
+
* @param indexName - The name of the index to be dropped.
|
|
731
|
+
* @return The SQL statement as a string.
|
|
732
|
+
*/
|
|
733
|
+
dropIndex(tableName, indexName) {
|
|
734
|
+
return `DROP INDEX ${this.formatIdentifier(indexName)};`;
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
679
737
|
* Statement to return data from a table.
|
|
680
738
|
*
|
|
681
739
|
* @param tableName - The name of the database table to select data from.
|
|
@@ -726,6 +784,24 @@ var DefaultSql = class {
|
|
|
726
784
|
]);
|
|
727
785
|
}
|
|
728
786
|
/**
|
|
787
|
+
* Combines rows (one row per indexed column, ordered by index name and column position) into a list of {@link IndexInfo}.
|
|
788
|
+
*
|
|
789
|
+
* @param rows - Rows containing the index name, whether the index is unique and the column name.
|
|
790
|
+
* @return An array of {@link IndexInfo}.
|
|
791
|
+
*/
|
|
792
|
+
rowsToIndexInfo(rows) {
|
|
793
|
+
const indexes = {};
|
|
794
|
+
for (const row of rows) {
|
|
795
|
+
if (!indexes[row.index_name]) indexes[row.index_name] = {
|
|
796
|
+
name: row.index_name,
|
|
797
|
+
columns: [],
|
|
798
|
+
isUnique: !!Number(row.is_unique)
|
|
799
|
+
};
|
|
800
|
+
indexes[row.index_name].columns.push(row.column_name);
|
|
801
|
+
}
|
|
802
|
+
return Object.values(indexes);
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
729
805
|
* Ensures that the migration table exists in the database.
|
|
730
806
|
* If the table does not exist, it creates the table using the defined migration table query.
|
|
731
807
|
*
|
|
@@ -860,13 +936,22 @@ var MigrationManager = class {
|
|
|
860
936
|
} else {
|
|
861
937
|
Logger.log(`Creating migration SQL from version ${fromVersion} to ${toVersion}`);
|
|
862
938
|
this.existingTables = (await this.dialect.getTableNames()).filter((oldTableName) => oldTableName != MIGRATION_DATA_TABLE_NAME).map((oldTableName) => this.migrations.getNewestTableName(oldTableName));
|
|
939
|
+
const renameTablesChanges = this.renameTables();
|
|
940
|
+
const createAndDropTablesChanges = await this.createAndDropTables();
|
|
941
|
+
const foreignKeyChanges = await this.migrateForeignKeys();
|
|
942
|
+
const columnChanges = await this.migrateColumns();
|
|
943
|
+
const renameColumnsChanges = this.renameColumns();
|
|
944
|
+
const indexChanges = await this.migrateIndexes();
|
|
945
|
+
const recreateTablesChanges = await this.recreateTables();
|
|
863
946
|
[
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
947
|
+
renameTablesChanges,
|
|
948
|
+
createAndDropTablesChanges,
|
|
949
|
+
indexChanges.dropped,
|
|
950
|
+
foreignKeyChanges,
|
|
951
|
+
columnChanges,
|
|
952
|
+
renameColumnsChanges,
|
|
953
|
+
indexChanges.created,
|
|
954
|
+
recreateTablesChanges
|
|
870
955
|
].forEach((entry) => {
|
|
871
956
|
changes.up += entry.up;
|
|
872
957
|
changes.down += entry.down;
|
|
@@ -927,13 +1012,14 @@ var MigrationManager = class {
|
|
|
927
1012
|
for (const tableName in this.newTables) if (this.tableDoesNotExists(tableName)) {
|
|
928
1013
|
const structure = this.newTables[tableName];
|
|
929
1014
|
changes.up += `${this.createTableSql(tableName, structure.columns, structure.foreignKeys)}\n`;
|
|
1015
|
+
changes.up += this.createIndexesSql(tableName, structure.indexes);
|
|
930
1016
|
changes.down += `${this.dialect.dropTable(tableName)}\n`;
|
|
931
1017
|
}
|
|
932
1018
|
for (const tableName of this.existingTables) if (!this.newTables[tableName]) {
|
|
933
1019
|
this.migrations.compareWithAllowedMigration(tableName, "dropTable");
|
|
934
1020
|
const dbTableName = this.migrations.getOldTableName(tableName);
|
|
935
1021
|
changes.up += `${this.dialect.dropTable(tableName)}\n`;
|
|
936
|
-
changes.down += this.createTableSql(tableName, await this.dialect.getColumnInformation(dbTableName), await this.dialect.getForeignKeys(dbTableName));
|
|
1022
|
+
changes.down += this.createTableSql(tableName, await this.dialect.getColumnInformation(dbTableName), await this.dialect.getForeignKeys(dbTableName)) + "\n" + this.createIndexesSql(tableName, await this.dialect.getIndexes(dbTableName));
|
|
937
1023
|
}
|
|
938
1024
|
return changes;
|
|
939
1025
|
}
|
|
@@ -957,6 +1043,16 @@ var MigrationManager = class {
|
|
|
957
1043
|
return this.dialect.createTable(tableName, queryLines);
|
|
958
1044
|
}
|
|
959
1045
|
/**
|
|
1046
|
+
* Generates an SQL string to create all provided indexes on a table.
|
|
1047
|
+
*
|
|
1048
|
+
* @param tableName - The name of the table the indexes belong to.
|
|
1049
|
+
* @param indexes - The index definitions.
|
|
1050
|
+
* @return The SQL string to create the indexes (one statement per line).
|
|
1051
|
+
*/
|
|
1052
|
+
createIndexesSql(tableName, indexes) {
|
|
1053
|
+
return (indexes ?? []).map((index) => `${this.dialect.createIndex(tableName, index)}\n`).join("");
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
960
1056
|
* Checks if the specified table does not exist in the list of existing tables.
|
|
961
1057
|
*
|
|
962
1058
|
* @param tableName - The name of the table to check.
|
|
@@ -987,9 +1083,9 @@ var MigrationManager = class {
|
|
|
987
1083
|
const backupTableName = `${tableName}__backup`;
|
|
988
1084
|
const moveDataQuery = this.dialect.insert(backupTableName, this.dialect.insertValues(moveableColumns, this.dialect.select(tableName, moveableColumns))) + "\n" + this.dialect.dropTable(tableName) + "\n" + this.dialect.renameTable(backupTableName, tableName);
|
|
989
1085
|
const structure = this.newTables[tableName];
|
|
990
|
-
changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery + "\n\n";
|
|
1086
|
+
changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery + "\n" + this.createIndexesSql(tableName, structure.indexes) + "\n";
|
|
991
1087
|
const dbTableName = this.migrations.getOldTableName(tableName);
|
|
992
|
-
changes.down += this.createTableSql(backupTableName, await this.dialect.getColumnInformation(dbTableName), await this.dialect.getForeignKeys(dbTableName)) + "\n" + moveDataQuery;
|
|
1088
|
+
changes.down += this.createTableSql(backupTableName, await this.dialect.getColumnInformation(dbTableName), await this.dialect.getForeignKeys(dbTableName)) + "\n" + moveDataQuery + "\n" + this.createIndexesSql(tableName, await this.dialect.getIndexes(dbTableName));
|
|
993
1089
|
}
|
|
994
1090
|
return changes;
|
|
995
1091
|
}
|
|
@@ -1103,6 +1199,71 @@ var MigrationManager = class {
|
|
|
1103
1199
|
return changes;
|
|
1104
1200
|
}
|
|
1105
1201
|
/**
|
|
1202
|
+
* Compares the indexes of all existing tables with their new definitions.
|
|
1203
|
+
* Indexes that are removed or changed have to be allowed (using "dropIndex").
|
|
1204
|
+
* Indexes of tables that will be recreated are only checked, their SQL is generated in {@link recreateTables()}.
|
|
1205
|
+
*
|
|
1206
|
+
* @return SQL changes to drop indexes (needs to run before columns are migrated)
|
|
1207
|
+
* and to create indexes (needs to run after columns were migrated and renamed).
|
|
1208
|
+
*/
|
|
1209
|
+
async migrateIndexes() {
|
|
1210
|
+
const dropped = {
|
|
1211
|
+
up: "\n\n-- Drop indexes\n",
|
|
1212
|
+
down: "\n\n-- Drop indexes\n"
|
|
1213
|
+
};
|
|
1214
|
+
const created = {
|
|
1215
|
+
up: "\n\n-- Create indexes\n",
|
|
1216
|
+
down: "\n\n-- Create indexes\n"
|
|
1217
|
+
};
|
|
1218
|
+
for (const tableName in this.newTables) {
|
|
1219
|
+
if (this.tableDoesNotExists(tableName)) continue;
|
|
1220
|
+
const dbTableName = this.migrations.getOldTableName(tableName);
|
|
1221
|
+
const newIndexes = this.newTables[tableName].indexes ?? [];
|
|
1222
|
+
const oldIndexes = await this.dialect.getIndexes(dbTableName);
|
|
1223
|
+
const isSameIndex = (oldIndex, newIndex) => oldIndex.isUnique == newIndex.isUnique && oldIndex.columns.length == newIndex.columns.length && oldIndex.columns.every((column, i) => this.migrations.getNewestColumnName(tableName, column) == newIndex.columns[i]);
|
|
1224
|
+
const indexesToDrop = [];
|
|
1225
|
+
const indexesToCreate = [];
|
|
1226
|
+
const addedIndexes = newIndexes.filter((newIndex) => !oldIndexes.some((oldIndex) => oldIndex.name == newIndex.name));
|
|
1227
|
+
for (const oldIndex of oldIndexes) {
|
|
1228
|
+
const newIndex = newIndexes.find((entry) => entry.name == oldIndex.name);
|
|
1229
|
+
if (newIndex) {
|
|
1230
|
+
if (isSameIndex(oldIndex, newIndex)) continue;
|
|
1231
|
+
Logger.log(`Index ${oldIndex.name} in ${tableName} will be changed!`);
|
|
1232
|
+
this.migrations.compareWithAllowedMigration(tableName, "dropIndex", oldIndex.name);
|
|
1233
|
+
indexesToCreate.push(newIndex);
|
|
1234
|
+
} else {
|
|
1235
|
+
const renamedIndex = addedIndexes.find((entry) => isSameIndex(oldIndex, entry));
|
|
1236
|
+
if (renamedIndex) {
|
|
1237
|
+
Logger.log(`Index ${oldIndex.name} in ${tableName} will be renamed to ${renamedIndex.name}!`);
|
|
1238
|
+
addedIndexes.splice(addedIndexes.indexOf(renamedIndex), 1);
|
|
1239
|
+
indexesToCreate.push(renamedIndex);
|
|
1240
|
+
} else {
|
|
1241
|
+
Logger.log(`Index ${oldIndex.name} in ${tableName} will be removed!`);
|
|
1242
|
+
this.migrations.compareWithAllowedMigration(tableName, "dropIndex", oldIndex.name);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
indexesToDrop.push(oldIndex);
|
|
1246
|
+
}
|
|
1247
|
+
for (const newIndex of addedIndexes) {
|
|
1248
|
+
Logger.log(`Index ${newIndex.name} in ${tableName} will be added!`);
|
|
1249
|
+
indexesToCreate.push(newIndex);
|
|
1250
|
+
}
|
|
1251
|
+
if (this.migrations.willBeRecreated(tableName)) continue;
|
|
1252
|
+
for (const index of indexesToDrop) {
|
|
1253
|
+
dropped.up += `${this.dialect.dropIndex(tableName, index.name)}\n`;
|
|
1254
|
+
created.down += `${this.dialect.createIndex(dbTableName, index)}\n`;
|
|
1255
|
+
}
|
|
1256
|
+
for (const index of indexesToCreate) {
|
|
1257
|
+
created.up += `${this.dialect.createIndex(tableName, index)}\n`;
|
|
1258
|
+
dropped.down += `${this.dialect.dropIndex(dbTableName, index.name)}\n`;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
return {
|
|
1262
|
+
dropped,
|
|
1263
|
+
created
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1106
1267
|
* Retrieves the name of the primary key column from a list of column information.
|
|
1107
1268
|
*
|
|
1108
1269
|
* @param columnInfoEntries - An array of ColumnInfo objects containing details about each column.
|
|
@@ -1184,7 +1345,8 @@ var TableObj = class TableObj {
|
|
|
1184
1345
|
table: "",
|
|
1185
1346
|
primaryKey: void 0,
|
|
1186
1347
|
columns: {},
|
|
1187
|
-
foreignKeys: []
|
|
1348
|
+
foreignKeys: [],
|
|
1349
|
+
indexes: []
|
|
1188
1350
|
};
|
|
1189
1351
|
constructor(tableName, defaultValues) {
|
|
1190
1352
|
const addColumnEntry = (key, value) => {
|
|
@@ -1243,6 +1405,21 @@ var TableObj = class TableObj {
|
|
|
1243
1405
|
return this;
|
|
1244
1406
|
}
|
|
1245
1407
|
/**
|
|
1408
|
+
* Creates an index on one or more columns of the current table.
|
|
1409
|
+
*
|
|
1410
|
+
* @param columns - The columns that will be part of the index (in order).
|
|
1411
|
+
* @param options - Optional configurations for the index.
|
|
1412
|
+
* If no name is provided, the name is generated from the table and column names.
|
|
1413
|
+
*/
|
|
1414
|
+
index(columns, options) {
|
|
1415
|
+
this.tableStructure.indexes.push({
|
|
1416
|
+
name: options?.name,
|
|
1417
|
+
columns: columns.map((column) => column.toString()),
|
|
1418
|
+
isUnique: !!options?.unique
|
|
1419
|
+
});
|
|
1420
|
+
return this;
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1246
1423
|
* An optional helper function to deal with `TS2589: Type instantiation is excessively deep and possibly infinite. when used as method parameter`
|
|
1247
1424
|
* Does nothing in runtime
|
|
1248
1425
|
*/
|
|
@@ -1292,6 +1469,26 @@ function ForeignKey(toTable, toColumn, options) {
|
|
|
1292
1469
|
};
|
|
1293
1470
|
}
|
|
1294
1471
|
//#endregion
|
|
1472
|
+
//#region src/lib/tableInfo/decorators/Index.ts
|
|
1473
|
+
/**
|
|
1474
|
+
* Decorator function to create an index on one or more columns of a table.
|
|
1475
|
+
* This decorator must be used at the head of the class.
|
|
1476
|
+
*
|
|
1477
|
+
* @param columns - The columns that will be part of the index (in order).
|
|
1478
|
+
* @param options - Additional options. If no name is provided, the name is generated from the table and column names.
|
|
1479
|
+
*/
|
|
1480
|
+
function Index(columns, options) {
|
|
1481
|
+
return (table, context) => {
|
|
1482
|
+
const tableInfo = context?.metadata ? getTableStructureFromMetadata(context.metadata) : getTableStructure(table);
|
|
1483
|
+
if (!tableInfo.indexes) tableInfo.indexes = [];
|
|
1484
|
+
tableInfo.indexes.push({
|
|
1485
|
+
name: options?.name,
|
|
1486
|
+
columns: columns.map((column) => column.toString()),
|
|
1487
|
+
isUnique: !!options?.unique
|
|
1488
|
+
});
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
//#endregion
|
|
1295
1492
|
//#region src/lib/dialects/SqliteDialect.ts
|
|
1296
1493
|
/**
|
|
1297
1494
|
* SQLite-specific syntax for SQL queries
|
|
@@ -1360,6 +1557,13 @@ var SqliteDialect = class extends DefaultSql {
|
|
|
1360
1557
|
};
|
|
1361
1558
|
});
|
|
1362
1559
|
}
|
|
1560
|
+
async getIndexes(tableName) {
|
|
1561
|
+
const data = await this.db.runReadStatement(`SELECT il.name AS index_name, il."unique" AS is_unique, ii.name AS column_name
|
|
1562
|
+
FROM pragma_index_list('${tableName}') AS il JOIN pragma_index_info(il.name) AS ii
|
|
1563
|
+
WHERE il.origin = 'c'
|
|
1564
|
+
ORDER BY il.name, ii.seqno;`);
|
|
1565
|
+
return this.rowsToIndexInfo(data);
|
|
1566
|
+
}
|
|
1363
1567
|
async setChanges(fromVersion, toVersion, changes) {
|
|
1364
1568
|
await this.db.runTransaction(`PRAGMA user_version = ${toVersion};`);
|
|
1365
1569
|
await super.setChanges(fromVersion, toVersion, changes);
|
|
@@ -1446,6 +1650,18 @@ var PostgresDialect = class extends DefaultSql {
|
|
|
1446
1650
|
removePrimaryKey(tableName, columnName) {
|
|
1447
1651
|
return `ALTER TABLE ${tableName} DROP CONSTRAINT ${columnName};\n`;
|
|
1448
1652
|
}
|
|
1653
|
+
async getIndexes(tableName) {
|
|
1654
|
+
const data = await this.db.runReadStatement(`SELECT i.relname AS index_name, ix.indisunique AS is_unique, a.attname AS column_name
|
|
1655
|
+
FROM pg_index AS ix
|
|
1656
|
+
JOIN pg_class AS t ON t.oid = ix.indrelid
|
|
1657
|
+
JOIN pg_class AS i ON i.oid = ix.indexrelid
|
|
1658
|
+
JOIN pg_namespace AS n ON n.oid = t.relnamespace
|
|
1659
|
+
JOIN pg_attribute AS a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey)
|
|
1660
|
+
WHERE n.nspname = 'public' AND t.relname = '${tableName}' AND NOT ix.indisprimary
|
|
1661
|
+
AND NOT EXISTS (SELECT 1 FROM pg_constraint AS c WHERE c.conindid = ix.indexrelid)
|
|
1662
|
+
ORDER BY i.relname, array_position(ix.indkey::int2[], a.attnum);`);
|
|
1663
|
+
return this.rowsToIndexInfo(data);
|
|
1664
|
+
}
|
|
1449
1665
|
async getForeignKeys(tableName) {
|
|
1450
1666
|
return (await this.db.runReadStatement(`SELECT
|
|
1451
1667
|
tc.table_name AS child_table,
|
|
@@ -1538,6 +1754,21 @@ var MySqlDialect = class extends DefaultSql {
|
|
|
1538
1754
|
};
|
|
1539
1755
|
});
|
|
1540
1756
|
}
|
|
1757
|
+
dropIndex(tableName, indexName) {
|
|
1758
|
+
return `DROP INDEX ${this.formatIdentifier(indexName)} ON ${this.formatIdentifier(tableName)};`;
|
|
1759
|
+
}
|
|
1760
|
+
async getIndexes(tableName) {
|
|
1761
|
+
const data = await this.db.runReadStatement(`SELECT s.INDEX_NAME AS index_name, s.NON_UNIQUE = 0 AS is_unique, s.COLUMN_NAME AS column_name
|
|
1762
|
+
FROM information_schema.STATISTICS AS s
|
|
1763
|
+
WHERE s.TABLE_SCHEMA = (SELECT DATABASE()) AND s.TABLE_NAME = '${tableName}' AND s.INDEX_NAME <> 'PRIMARY'
|
|
1764
|
+
AND NOT EXISTS (
|
|
1765
|
+
SELECT 1 FROM information_schema.KEY_COLUMN_USAGE AS k
|
|
1766
|
+
WHERE k.TABLE_SCHEMA = s.TABLE_SCHEMA AND k.TABLE_NAME = s.TABLE_NAME AND k.REFERENCED_TABLE_NAME IS NOT NULL
|
|
1767
|
+
AND (k.CONSTRAINT_NAME = s.INDEX_NAME OR k.COLUMN_NAME = s.INDEX_NAME)
|
|
1768
|
+
)
|
|
1769
|
+
ORDER BY s.INDEX_NAME, s.SEQ_IN_INDEX;`);
|
|
1770
|
+
return this.rowsToIndexInfo(data);
|
|
1771
|
+
}
|
|
1541
1772
|
async getTableNames() {
|
|
1542
1773
|
return (await this.db.runReadStatement("SHOW TABLES")).map((entry) => Object.values(entry)[0]);
|
|
1543
1774
|
}
|
|
@@ -1577,6 +1808,13 @@ function getDialect(db, dialect) {
|
|
|
1577
1808
|
}
|
|
1578
1809
|
//#endregion
|
|
1579
1810
|
//#region src/index.ts
|
|
1811
|
+
/**
|
|
1812
|
+
* Executes a database migration based on the provided database connection and migration instructions.
|
|
1813
|
+
*
|
|
1814
|
+
* @param db - The database access object used to interact with the database.
|
|
1815
|
+
* @param dbInstructions - The migration instructions, including target version, logger mode, and dialect.
|
|
1816
|
+
* @return A promise that resolves when the migration process is successfully completed.
|
|
1817
|
+
*/
|
|
1580
1818
|
async function runMigration(db, dbInstructions) {
|
|
1581
1819
|
Logger.setMode(dbInstructions.loggerMode);
|
|
1582
1820
|
const dialect = getDialect(db, dbInstructions.dialect);
|
|
@@ -1616,6 +1854,7 @@ async function rollback(dbAccess, toVersion, dialect, loggerMode) {
|
|
|
1616
1854
|
//#endregion
|
|
1617
1855
|
exports.DataType = DataType;
|
|
1618
1856
|
exports.ForeignKey = ForeignKey;
|
|
1857
|
+
exports.Index = Index;
|
|
1619
1858
|
exports.TableClass = TableClass;
|
|
1620
1859
|
exports.TableObj = TableObj;
|
|
1621
1860
|
exports.rollback = rollback;
|
package/dist/index.d.cts
CHANGED
|
@@ -27,6 +27,7 @@ interface AllowedMigrations {
|
|
|
27
27
|
alterPrimaryKey?: Record<string, AllowedMigrationState>;
|
|
28
28
|
removeForeignKey?: Record<string, AllowedMigrationState>;
|
|
29
29
|
alterForeignKey?: Record<string, AllowedMigrationState>;
|
|
30
|
+
dropIndex?: Record<string, AllowedMigrationState>;
|
|
30
31
|
}
|
|
31
32
|
//#endregion
|
|
32
33
|
//#region src/lib/typings/MigrationInstructions.d.ts
|
|
@@ -64,12 +65,26 @@ interface ForeignKeyInfo {
|
|
|
64
65
|
onDelete?: ForeignKeyActions;
|
|
65
66
|
}
|
|
66
67
|
//#endregion
|
|
68
|
+
//#region src/lib/typings/IndexInfo.d.ts
|
|
69
|
+
interface IndexInfo {
|
|
70
|
+
name: string;
|
|
71
|
+
columns: string[];
|
|
72
|
+
isUnique: boolean;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Index definition as provided by the user. If no name is provided, a name is generated from the table and column names.
|
|
76
|
+
*/
|
|
77
|
+
interface InputIndexInfo extends Omit<IndexInfo, "name"> {
|
|
78
|
+
name?: string;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
67
81
|
//#region src/lib/typings/TableStructure.d.ts
|
|
68
82
|
interface TableStructure {
|
|
69
83
|
table: string;
|
|
70
84
|
primaryKey?: string;
|
|
71
85
|
columns: Record<string, ColumnInfo>;
|
|
72
86
|
foreignKeys?: ForeignKeyInfo[];
|
|
87
|
+
indexes?: IndexInfo[];
|
|
73
88
|
}
|
|
74
89
|
//#endregion
|
|
75
90
|
//#region src/lib/Migrations.d.ts
|
|
@@ -133,6 +148,7 @@ declare class Migrations {
|
|
|
133
148
|
* @param tableName - The name of the table to check the migration against.
|
|
134
149
|
* @param type - The type of destructive migration being checked.
|
|
135
150
|
* @param column - The column name for which the migration is allowed. Only needed for "dropColumn", "removeForeignKey" and "alterForeignKey".
|
|
151
|
+
* For "dropIndex", the index name is used instead.
|
|
136
152
|
*/
|
|
137
153
|
compareWithAllowedMigration(tableName: string, type: keyof AllowedMigrations, column?: string): void;
|
|
138
154
|
/**
|
|
@@ -205,6 +221,7 @@ declare class Migrations {
|
|
|
205
221
|
* @param tableName - The current table name in the database at the time of the migration version.
|
|
206
222
|
* @param allowedMigration - The keys representing the migrations to be allowed.
|
|
207
223
|
* @param column - The column name for which the migration is allowed. Only needed for "dropColumn", "removeForeignKey" and "alterForeignKey".
|
|
224
|
+
* For "dropIndex", provide the index name instead.
|
|
208
225
|
*
|
|
209
226
|
* @throws Error if the provided column name is not valid for the specified migration.
|
|
210
227
|
*/
|
|
@@ -263,8 +280,9 @@ interface InputColumnInfo extends ColumnInfo {
|
|
|
263
280
|
}
|
|
264
281
|
//#endregion
|
|
265
282
|
//#region src/lib/typings/InputTableStructure.d.ts
|
|
266
|
-
interface InputTableStructure extends TableStructure {
|
|
283
|
+
interface InputTableStructure extends Omit<TableStructure, "indexes"> {
|
|
267
284
|
columns: Record<string, InputColumnInfo>;
|
|
285
|
+
indexes?: InputIndexInfo[];
|
|
268
286
|
}
|
|
269
287
|
//#endregion
|
|
270
288
|
//#region src/lib/tableInfo/TableObjHelper.d.ts
|
|
@@ -278,7 +296,7 @@ interface ColumnOptions {
|
|
|
278
296
|
}
|
|
279
297
|
type DataFormat = Record<string, AllowedTypes | [AllowedTypes, ColumnOptions]>;
|
|
280
298
|
type GetColumns<T> = T extends PublicTableObj<infer Item> ? Item : never;
|
|
281
|
-
type PublicTableObj<T extends DataFormat> = Pick<TableObj<T>, "foreignKey" | "sealType">;
|
|
299
|
+
type PublicTableObj<T extends DataFormat> = Pick<TableObj<T>, "foreignKey" | "index" | "sealType">;
|
|
282
300
|
type TableObjInput = PublicTableObj<DataFormat>;
|
|
283
301
|
/**
|
|
284
302
|
* Use this class to define tables for DatabaseInstructions
|
|
@@ -303,6 +321,17 @@ declare class TableObj<T extends DataFormat> {
|
|
|
303
321
|
onUpdate?: ForeignKeyActions;
|
|
304
322
|
onDelete?: ForeignKeyActions;
|
|
305
323
|
}): PublicTableObj<T>;
|
|
324
|
+
/**
|
|
325
|
+
* Creates an index on one or more columns of the current table.
|
|
326
|
+
*
|
|
327
|
+
* @param columns - The columns that will be part of the index (in order).
|
|
328
|
+
* @param options - Optional configurations for the index.
|
|
329
|
+
* If no name is provided, the name is generated from the table and column names.
|
|
330
|
+
*/
|
|
331
|
+
index(columns: (keyof T)[], options?: {
|
|
332
|
+
name?: string;
|
|
333
|
+
unique?: boolean;
|
|
334
|
+
}): PublicTableObj<T>;
|
|
306
335
|
/**
|
|
307
336
|
* An optional helper function to deal with `TS2589: Type instantiation is excessively deep and possibly infinite. when used as method parameter`
|
|
308
337
|
* Does nothing in runtime
|
|
@@ -387,7 +416,27 @@ declare function ForeignKey<TOther extends TableClassInterface>(toTable: Class<T
|
|
|
387
416
|
onUpdate?: ForeignKeyActions;
|
|
388
417
|
}): (table: any, context: any) => void;
|
|
389
418
|
//#endregion
|
|
419
|
+
//#region src/lib/tableInfo/decorators/Index.d.ts
|
|
420
|
+
/**
|
|
421
|
+
* Decorator function to create an index on one or more columns of a table.
|
|
422
|
+
* This decorator must be used at the head of the class.
|
|
423
|
+
*
|
|
424
|
+
* @param columns - The columns that will be part of the index (in order).
|
|
425
|
+
* @param options - Additional options. If no name is provided, the name is generated from the table and column names.
|
|
426
|
+
*/
|
|
427
|
+
declare function Index<T extends TableClassInterface>(columns: (keyof T)[], options?: {
|
|
428
|
+
name?: string;
|
|
429
|
+
unique?: boolean;
|
|
430
|
+
}): (table: Class<T>, context?: any) => void;
|
|
431
|
+
//#endregion
|
|
390
432
|
//#region src/index.d.ts
|
|
433
|
+
/**
|
|
434
|
+
* Executes a database migration based on the provided database connection and migration instructions.
|
|
435
|
+
*
|
|
436
|
+
* @param db - The database access object used to interact with the database.
|
|
437
|
+
* @param dbInstructions - The migration instructions, including target version, logger mode, and dialect.
|
|
438
|
+
* @return A promise that resolves when the migration process is successfully completed.
|
|
439
|
+
*/
|
|
391
440
|
export declare function runMigration(db: DatabaseAccess, dbInstructions: DatabaseInstructions): Promise<void>;
|
|
392
441
|
/**
|
|
393
442
|
* Rolls back the database to the provided toVersion
|
|
@@ -398,4 +447,4 @@ export declare function runMigration(db: DatabaseAccess, dbInstructions: Databas
|
|
|
398
447
|
*/
|
|
399
448
|
export declare function rollback(dbAccess: DatabaseAccess, toVersion: number, dialect: DatabaseInstructions["dialect"], loggerMode?: DatabaseInstructions["loggerMode"]): Promise<number>;
|
|
400
449
|
//#endregion
|
|
401
|
-
export { DataType, type DatabaseAccess, type DatabaseInstructions, ForeignKey, type PublicMigrations, type SqlChanges, TableClass, TableObj };
|
|
450
|
+
export { DataType, type DatabaseAccess, type DatabaseInstructions, ForeignKey, Index, type PublicMigrations, type SqlChanges, TableClass, TableObj };
|
package/dist/index.d.mts
CHANGED
|
@@ -27,6 +27,7 @@ interface AllowedMigrations {
|
|
|
27
27
|
alterPrimaryKey?: Record<string, AllowedMigrationState>;
|
|
28
28
|
removeForeignKey?: Record<string, AllowedMigrationState>;
|
|
29
29
|
alterForeignKey?: Record<string, AllowedMigrationState>;
|
|
30
|
+
dropIndex?: Record<string, AllowedMigrationState>;
|
|
30
31
|
}
|
|
31
32
|
//#endregion
|
|
32
33
|
//#region src/lib/typings/MigrationInstructions.d.ts
|
|
@@ -64,12 +65,26 @@ interface ForeignKeyInfo {
|
|
|
64
65
|
onDelete?: ForeignKeyActions;
|
|
65
66
|
}
|
|
66
67
|
//#endregion
|
|
68
|
+
//#region src/lib/typings/IndexInfo.d.ts
|
|
69
|
+
interface IndexInfo {
|
|
70
|
+
name: string;
|
|
71
|
+
columns: string[];
|
|
72
|
+
isUnique: boolean;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Index definition as provided by the user. If no name is provided, a name is generated from the table and column names.
|
|
76
|
+
*/
|
|
77
|
+
interface InputIndexInfo extends Omit<IndexInfo, "name"> {
|
|
78
|
+
name?: string;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
67
81
|
//#region src/lib/typings/TableStructure.d.ts
|
|
68
82
|
interface TableStructure {
|
|
69
83
|
table: string;
|
|
70
84
|
primaryKey?: string;
|
|
71
85
|
columns: Record<string, ColumnInfo>;
|
|
72
86
|
foreignKeys?: ForeignKeyInfo[];
|
|
87
|
+
indexes?: IndexInfo[];
|
|
73
88
|
}
|
|
74
89
|
//#endregion
|
|
75
90
|
//#region src/lib/Migrations.d.ts
|
|
@@ -133,6 +148,7 @@ declare class Migrations {
|
|
|
133
148
|
* @param tableName - The name of the table to check the migration against.
|
|
134
149
|
* @param type - The type of destructive migration being checked.
|
|
135
150
|
* @param column - The column name for which the migration is allowed. Only needed for "dropColumn", "removeForeignKey" and "alterForeignKey".
|
|
151
|
+
* For "dropIndex", the index name is used instead.
|
|
136
152
|
*/
|
|
137
153
|
compareWithAllowedMigration(tableName: string, type: keyof AllowedMigrations, column?: string): void;
|
|
138
154
|
/**
|
|
@@ -205,6 +221,7 @@ declare class Migrations {
|
|
|
205
221
|
* @param tableName - The current table name in the database at the time of the migration version.
|
|
206
222
|
* @param allowedMigration - The keys representing the migrations to be allowed.
|
|
207
223
|
* @param column - The column name for which the migration is allowed. Only needed for "dropColumn", "removeForeignKey" and "alterForeignKey".
|
|
224
|
+
* For "dropIndex", provide the index name instead.
|
|
208
225
|
*
|
|
209
226
|
* @throws Error if the provided column name is not valid for the specified migration.
|
|
210
227
|
*/
|
|
@@ -263,8 +280,9 @@ interface InputColumnInfo extends ColumnInfo {
|
|
|
263
280
|
}
|
|
264
281
|
//#endregion
|
|
265
282
|
//#region src/lib/typings/InputTableStructure.d.ts
|
|
266
|
-
interface InputTableStructure extends TableStructure {
|
|
283
|
+
interface InputTableStructure extends Omit<TableStructure, "indexes"> {
|
|
267
284
|
columns: Record<string, InputColumnInfo>;
|
|
285
|
+
indexes?: InputIndexInfo[];
|
|
268
286
|
}
|
|
269
287
|
//#endregion
|
|
270
288
|
//#region src/lib/tableInfo/TableObjHelper.d.ts
|
|
@@ -278,7 +296,7 @@ interface ColumnOptions {
|
|
|
278
296
|
}
|
|
279
297
|
type DataFormat = Record<string, AllowedTypes | [AllowedTypes, ColumnOptions]>;
|
|
280
298
|
type GetColumns<T> = T extends PublicTableObj<infer Item> ? Item : never;
|
|
281
|
-
type PublicTableObj<T extends DataFormat> = Pick<TableObj<T>, "foreignKey" | "sealType">;
|
|
299
|
+
type PublicTableObj<T extends DataFormat> = Pick<TableObj<T>, "foreignKey" | "index" | "sealType">;
|
|
282
300
|
type TableObjInput = PublicTableObj<DataFormat>;
|
|
283
301
|
/**
|
|
284
302
|
* Use this class to define tables for DatabaseInstructions
|
|
@@ -303,6 +321,17 @@ declare class TableObj<T extends DataFormat> {
|
|
|
303
321
|
onUpdate?: ForeignKeyActions;
|
|
304
322
|
onDelete?: ForeignKeyActions;
|
|
305
323
|
}): PublicTableObj<T>;
|
|
324
|
+
/**
|
|
325
|
+
* Creates an index on one or more columns of the current table.
|
|
326
|
+
*
|
|
327
|
+
* @param columns - The columns that will be part of the index (in order).
|
|
328
|
+
* @param options - Optional configurations for the index.
|
|
329
|
+
* If no name is provided, the name is generated from the table and column names.
|
|
330
|
+
*/
|
|
331
|
+
index(columns: (keyof T)[], options?: {
|
|
332
|
+
name?: string;
|
|
333
|
+
unique?: boolean;
|
|
334
|
+
}): PublicTableObj<T>;
|
|
306
335
|
/**
|
|
307
336
|
* An optional helper function to deal with `TS2589: Type instantiation is excessively deep and possibly infinite. when used as method parameter`
|
|
308
337
|
* Does nothing in runtime
|
|
@@ -387,7 +416,27 @@ declare function ForeignKey<TOther extends TableClassInterface>(toTable: Class<T
|
|
|
387
416
|
onUpdate?: ForeignKeyActions;
|
|
388
417
|
}): (table: any, context: any) => void;
|
|
389
418
|
//#endregion
|
|
419
|
+
//#region src/lib/tableInfo/decorators/Index.d.ts
|
|
420
|
+
/**
|
|
421
|
+
* Decorator function to create an index on one or more columns of a table.
|
|
422
|
+
* This decorator must be used at the head of the class.
|
|
423
|
+
*
|
|
424
|
+
* @param columns - The columns that will be part of the index (in order).
|
|
425
|
+
* @param options - Additional options. If no name is provided, the name is generated from the table and column names.
|
|
426
|
+
*/
|
|
427
|
+
declare function Index<T extends TableClassInterface>(columns: (keyof T)[], options?: {
|
|
428
|
+
name?: string;
|
|
429
|
+
unique?: boolean;
|
|
430
|
+
}): (table: Class<T>, context?: any) => void;
|
|
431
|
+
//#endregion
|
|
390
432
|
//#region src/index.d.ts
|
|
433
|
+
/**
|
|
434
|
+
* Executes a database migration based on the provided database connection and migration instructions.
|
|
435
|
+
*
|
|
436
|
+
* @param db - The database access object used to interact with the database.
|
|
437
|
+
* @param dbInstructions - The migration instructions, including target version, logger mode, and dialect.
|
|
438
|
+
* @return A promise that resolves when the migration process is successfully completed.
|
|
439
|
+
*/
|
|
391
440
|
export declare function runMigration(db: DatabaseAccess, dbInstructions: DatabaseInstructions): Promise<void>;
|
|
392
441
|
/**
|
|
393
442
|
* Rolls back the database to the provided toVersion
|
|
@@ -398,4 +447,4 @@ export declare function runMigration(db: DatabaseAccess, dbInstructions: Databas
|
|
|
398
447
|
*/
|
|
399
448
|
export declare function rollback(dbAccess: DatabaseAccess, toVersion: number, dialect: DatabaseInstructions["dialect"], loggerMode?: DatabaseInstructions["loggerMode"]): Promise<number>;
|
|
400
449
|
//#endregion
|
|
401
|
-
export { DataType, type DatabaseAccess, type DatabaseInstructions, ForeignKey, type PublicMigrations, type SqlChanges, TableClass, TableObj };
|
|
450
|
+
export { DataType, type DatabaseAccess, type DatabaseInstructions, ForeignKey, Index, type PublicMigrations, type SqlChanges, TableClass, TableObj };
|
package/dist/index.mjs
CHANGED
|
@@ -110,6 +110,10 @@ function getTableStructureFromMetadata(metadata) {
|
|
|
110
110
|
//#endregion
|
|
111
111
|
//#region src/lib/TableStructureGenerator.ts
|
|
112
112
|
/**
|
|
113
|
+
* Postgres silently truncates longer identifiers, which would lead to differing index names in each migration
|
|
114
|
+
*/
|
|
115
|
+
const MAX_INDEX_NAME_LENGTH = 63;
|
|
116
|
+
/**
|
|
113
117
|
* Represents a generator for creating table structures based on specified database instructions
|
|
114
118
|
*/
|
|
115
119
|
var TableStructureGenerator = class {
|
|
@@ -126,24 +130,55 @@ var TableStructureGenerator = class {
|
|
|
126
130
|
*/
|
|
127
131
|
generateTableStructure() {
|
|
128
132
|
const tables = {};
|
|
133
|
+
const indexNames = {};
|
|
129
134
|
for (const table of this.dbInstructions.tables) {
|
|
130
135
|
let structure;
|
|
136
|
+
let tableName;
|
|
131
137
|
if (TableObjHelper.isTableObj(table)) {
|
|
132
138
|
structure = table.tableStructure;
|
|
133
|
-
|
|
139
|
+
tableName = table.tableName;
|
|
134
140
|
} else {
|
|
135
141
|
const classTable = table;
|
|
136
142
|
structure = getTableStructure(classTable);
|
|
137
|
-
|
|
143
|
+
tableName = classTable.name;
|
|
138
144
|
}
|
|
139
145
|
for (const key in structure.columns) {
|
|
140
146
|
const entry = structure.columns[key];
|
|
141
147
|
entry.defaultValue = entry.inputDefaultValue === null ? this.dialect.nullType : this.dialect.formatValueToSql(entry.inputDefaultValue, entry.inputType);
|
|
142
148
|
entry.sqlType = this.dialect.getSqlType(entry.inputType, entry);
|
|
143
149
|
}
|
|
150
|
+
const indexes = this.getIndexes(tableName, structure);
|
|
151
|
+
for (const index of indexes) {
|
|
152
|
+
if (indexNames[index.name]) throw new Error(`Index name "${index.name}" is used in ${indexNames[index.name]} and ${tableName}. Index names need to be unique.`);
|
|
153
|
+
indexNames[index.name] = tableName;
|
|
154
|
+
}
|
|
155
|
+
tables[tableName] = {
|
|
156
|
+
...structure,
|
|
157
|
+
indexes
|
|
158
|
+
};
|
|
144
159
|
}
|
|
145
160
|
return tables;
|
|
146
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Validates the index definitions of a table and generates names for indexes that have none.
|
|
164
|
+
*
|
|
165
|
+
* @param tableName - The name of the table the indexes belong to.
|
|
166
|
+
* @param structure - The table structure containing the index definitions.
|
|
167
|
+
* @return The index definitions including their names.
|
|
168
|
+
*/
|
|
169
|
+
getIndexes(tableName, structure) {
|
|
170
|
+
return (structure.indexes ?? []).map((index) => {
|
|
171
|
+
if (!index.columns.length) throw new Error(`An index in ${tableName} has no columns.`);
|
|
172
|
+
for (const column of index.columns) if (!structure.columns[column]) throw new Error(`Index in ${tableName} uses column "${column}", which does not exist.`);
|
|
173
|
+
const name = index.name ?? `${tableName}_${index.columns.join("_")}_idx`;
|
|
174
|
+
if (name.length > MAX_INDEX_NAME_LENGTH) throw new Error(`Index name "${name}" is longer than ${MAX_INDEX_NAME_LENGTH} characters. Please provide a shorter name.`);
|
|
175
|
+
return {
|
|
176
|
+
name,
|
|
177
|
+
columns: index.columns,
|
|
178
|
+
isUnique: index.isUnique
|
|
179
|
+
};
|
|
180
|
+
});
|
|
181
|
+
}
|
|
147
182
|
};
|
|
148
183
|
//#endregion
|
|
149
184
|
//#region src/lib/typings/AllowedMigrations.ts
|
|
@@ -266,6 +301,7 @@ var Migrations = class {
|
|
|
266
301
|
* @param tableName - The name of the table to check the migration against.
|
|
267
302
|
* @param type - The type of destructive migration being checked.
|
|
268
303
|
* @param column - The column name for which the migration is allowed. Only needed for "dropColumn", "removeForeignKey" and "alterForeignKey".
|
|
304
|
+
* For "dropIndex", the index name is used instead.
|
|
269
305
|
*/
|
|
270
306
|
compareWithAllowedMigration(tableName, type, column = NO_COLUMN) {
|
|
271
307
|
const migrationEntry = this.migrationData[tableName];
|
|
@@ -367,6 +403,7 @@ var Migrations = class {
|
|
|
367
403
|
* @param tableName - The current table name in the database at the time of the migration version.
|
|
368
404
|
* @param allowedMigration - The keys representing the migrations to be allowed.
|
|
369
405
|
* @param column - The column name for which the migration is allowed. Only needed for "dropColumn", "removeForeignKey" and "alterForeignKey".
|
|
406
|
+
* For "dropIndex", provide the index name instead.
|
|
370
407
|
*
|
|
371
408
|
* @throws Error if the provided column name is not valid for the specified migration.
|
|
372
409
|
*/
|
|
@@ -384,6 +421,7 @@ var Migrations = class {
|
|
|
384
421
|
case "dropColumn":
|
|
385
422
|
case "removeForeignKey":
|
|
386
423
|
case "alterForeignKey": throw new Error(`The ${allowedMigration} migration requires a column name. Please provide one.`);
|
|
424
|
+
case "dropIndex": throw new Error(`The ${allowedMigration} migration requires an index name. Please provide one.`);
|
|
387
425
|
}
|
|
388
426
|
}
|
|
389
427
|
/**
|
|
@@ -652,6 +690,26 @@ var DefaultSql = class {
|
|
|
652
690
|
return `ALTER TABLE ${this.formatIdentifier(tableName)} DROP COLUMN ${this.formatIdentifier(columnName)};`;
|
|
653
691
|
}
|
|
654
692
|
/**
|
|
693
|
+
* Statement to create an index on a specified table.
|
|
694
|
+
*
|
|
695
|
+
* @param tableName - The name of the table on which the index will be created.
|
|
696
|
+
* @param index - The index definition.
|
|
697
|
+
* @return The SQL statement as a string.
|
|
698
|
+
*/
|
|
699
|
+
createIndex(tableName, index) {
|
|
700
|
+
return `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX ${this.formatIdentifier(index.name)} ON ${this.formatIdentifier(tableName)} (${index.columns.map((column) => this.formatIdentifier(column)).join(", ")});`;
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Statement to drop an index from a specified table.
|
|
704
|
+
*
|
|
705
|
+
* @param tableName - The name of the table the index belongs to.
|
|
706
|
+
* @param indexName - The name of the index to be dropped.
|
|
707
|
+
* @return The SQL statement as a string.
|
|
708
|
+
*/
|
|
709
|
+
dropIndex(tableName, indexName) {
|
|
710
|
+
return `DROP INDEX ${this.formatIdentifier(indexName)};`;
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
655
713
|
* Statement to return data from a table.
|
|
656
714
|
*
|
|
657
715
|
* @param tableName - The name of the database table to select data from.
|
|
@@ -702,6 +760,24 @@ var DefaultSql = class {
|
|
|
702
760
|
]);
|
|
703
761
|
}
|
|
704
762
|
/**
|
|
763
|
+
* Combines rows (one row per indexed column, ordered by index name and column position) into a list of {@link IndexInfo}.
|
|
764
|
+
*
|
|
765
|
+
* @param rows - Rows containing the index name, whether the index is unique and the column name.
|
|
766
|
+
* @return An array of {@link IndexInfo}.
|
|
767
|
+
*/
|
|
768
|
+
rowsToIndexInfo(rows) {
|
|
769
|
+
const indexes = {};
|
|
770
|
+
for (const row of rows) {
|
|
771
|
+
if (!indexes[row.index_name]) indexes[row.index_name] = {
|
|
772
|
+
name: row.index_name,
|
|
773
|
+
columns: [],
|
|
774
|
+
isUnique: !!Number(row.is_unique)
|
|
775
|
+
};
|
|
776
|
+
indexes[row.index_name].columns.push(row.column_name);
|
|
777
|
+
}
|
|
778
|
+
return Object.values(indexes);
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
705
781
|
* Ensures that the migration table exists in the database.
|
|
706
782
|
* If the table does not exist, it creates the table using the defined migration table query.
|
|
707
783
|
*
|
|
@@ -836,13 +912,22 @@ var MigrationManager = class {
|
|
|
836
912
|
} else {
|
|
837
913
|
Logger.log(`Creating migration SQL from version ${fromVersion} to ${toVersion}`);
|
|
838
914
|
this.existingTables = (await this.dialect.getTableNames()).filter((oldTableName) => oldTableName != MIGRATION_DATA_TABLE_NAME).map((oldTableName) => this.migrations.getNewestTableName(oldTableName));
|
|
915
|
+
const renameTablesChanges = this.renameTables();
|
|
916
|
+
const createAndDropTablesChanges = await this.createAndDropTables();
|
|
917
|
+
const foreignKeyChanges = await this.migrateForeignKeys();
|
|
918
|
+
const columnChanges = await this.migrateColumns();
|
|
919
|
+
const renameColumnsChanges = this.renameColumns();
|
|
920
|
+
const indexChanges = await this.migrateIndexes();
|
|
921
|
+
const recreateTablesChanges = await this.recreateTables();
|
|
839
922
|
[
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
923
|
+
renameTablesChanges,
|
|
924
|
+
createAndDropTablesChanges,
|
|
925
|
+
indexChanges.dropped,
|
|
926
|
+
foreignKeyChanges,
|
|
927
|
+
columnChanges,
|
|
928
|
+
renameColumnsChanges,
|
|
929
|
+
indexChanges.created,
|
|
930
|
+
recreateTablesChanges
|
|
846
931
|
].forEach((entry) => {
|
|
847
932
|
changes.up += entry.up;
|
|
848
933
|
changes.down += entry.down;
|
|
@@ -903,13 +988,14 @@ var MigrationManager = class {
|
|
|
903
988
|
for (const tableName in this.newTables) if (this.tableDoesNotExists(tableName)) {
|
|
904
989
|
const structure = this.newTables[tableName];
|
|
905
990
|
changes.up += `${this.createTableSql(tableName, structure.columns, structure.foreignKeys)}\n`;
|
|
991
|
+
changes.up += this.createIndexesSql(tableName, structure.indexes);
|
|
906
992
|
changes.down += `${this.dialect.dropTable(tableName)}\n`;
|
|
907
993
|
}
|
|
908
994
|
for (const tableName of this.existingTables) if (!this.newTables[tableName]) {
|
|
909
995
|
this.migrations.compareWithAllowedMigration(tableName, "dropTable");
|
|
910
996
|
const dbTableName = this.migrations.getOldTableName(tableName);
|
|
911
997
|
changes.up += `${this.dialect.dropTable(tableName)}\n`;
|
|
912
|
-
changes.down += this.createTableSql(tableName, await this.dialect.getColumnInformation(dbTableName), await this.dialect.getForeignKeys(dbTableName));
|
|
998
|
+
changes.down += this.createTableSql(tableName, await this.dialect.getColumnInformation(dbTableName), await this.dialect.getForeignKeys(dbTableName)) + "\n" + this.createIndexesSql(tableName, await this.dialect.getIndexes(dbTableName));
|
|
913
999
|
}
|
|
914
1000
|
return changes;
|
|
915
1001
|
}
|
|
@@ -933,6 +1019,16 @@ var MigrationManager = class {
|
|
|
933
1019
|
return this.dialect.createTable(tableName, queryLines);
|
|
934
1020
|
}
|
|
935
1021
|
/**
|
|
1022
|
+
* Generates an SQL string to create all provided indexes on a table.
|
|
1023
|
+
*
|
|
1024
|
+
* @param tableName - The name of the table the indexes belong to.
|
|
1025
|
+
* @param indexes - The index definitions.
|
|
1026
|
+
* @return The SQL string to create the indexes (one statement per line).
|
|
1027
|
+
*/
|
|
1028
|
+
createIndexesSql(tableName, indexes) {
|
|
1029
|
+
return (indexes ?? []).map((index) => `${this.dialect.createIndex(tableName, index)}\n`).join("");
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
936
1032
|
* Checks if the specified table does not exist in the list of existing tables.
|
|
937
1033
|
*
|
|
938
1034
|
* @param tableName - The name of the table to check.
|
|
@@ -963,9 +1059,9 @@ var MigrationManager = class {
|
|
|
963
1059
|
const backupTableName = `${tableName}__backup`;
|
|
964
1060
|
const moveDataQuery = this.dialect.insert(backupTableName, this.dialect.insertValues(moveableColumns, this.dialect.select(tableName, moveableColumns))) + "\n" + this.dialect.dropTable(tableName) + "\n" + this.dialect.renameTable(backupTableName, tableName);
|
|
965
1061
|
const structure = this.newTables[tableName];
|
|
966
|
-
changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery + "\n\n";
|
|
1062
|
+
changes.up += this.createTableSql(backupTableName, structure.columns, structure.foreignKeys) + "\n" + moveDataQuery + "\n" + this.createIndexesSql(tableName, structure.indexes) + "\n";
|
|
967
1063
|
const dbTableName = this.migrations.getOldTableName(tableName);
|
|
968
|
-
changes.down += this.createTableSql(backupTableName, await this.dialect.getColumnInformation(dbTableName), await this.dialect.getForeignKeys(dbTableName)) + "\n" + moveDataQuery;
|
|
1064
|
+
changes.down += this.createTableSql(backupTableName, await this.dialect.getColumnInformation(dbTableName), await this.dialect.getForeignKeys(dbTableName)) + "\n" + moveDataQuery + "\n" + this.createIndexesSql(tableName, await this.dialect.getIndexes(dbTableName));
|
|
969
1065
|
}
|
|
970
1066
|
return changes;
|
|
971
1067
|
}
|
|
@@ -1079,6 +1175,71 @@ var MigrationManager = class {
|
|
|
1079
1175
|
return changes;
|
|
1080
1176
|
}
|
|
1081
1177
|
/**
|
|
1178
|
+
* Compares the indexes of all existing tables with their new definitions.
|
|
1179
|
+
* Indexes that are removed or changed have to be allowed (using "dropIndex").
|
|
1180
|
+
* Indexes of tables that will be recreated are only checked, their SQL is generated in {@link recreateTables()}.
|
|
1181
|
+
*
|
|
1182
|
+
* @return SQL changes to drop indexes (needs to run before columns are migrated)
|
|
1183
|
+
* and to create indexes (needs to run after columns were migrated and renamed).
|
|
1184
|
+
*/
|
|
1185
|
+
async migrateIndexes() {
|
|
1186
|
+
const dropped = {
|
|
1187
|
+
up: "\n\n-- Drop indexes\n",
|
|
1188
|
+
down: "\n\n-- Drop indexes\n"
|
|
1189
|
+
};
|
|
1190
|
+
const created = {
|
|
1191
|
+
up: "\n\n-- Create indexes\n",
|
|
1192
|
+
down: "\n\n-- Create indexes\n"
|
|
1193
|
+
};
|
|
1194
|
+
for (const tableName in this.newTables) {
|
|
1195
|
+
if (this.tableDoesNotExists(tableName)) continue;
|
|
1196
|
+
const dbTableName = this.migrations.getOldTableName(tableName);
|
|
1197
|
+
const newIndexes = this.newTables[tableName].indexes ?? [];
|
|
1198
|
+
const oldIndexes = await this.dialect.getIndexes(dbTableName);
|
|
1199
|
+
const isSameIndex = (oldIndex, newIndex) => oldIndex.isUnique == newIndex.isUnique && oldIndex.columns.length == newIndex.columns.length && oldIndex.columns.every((column, i) => this.migrations.getNewestColumnName(tableName, column) == newIndex.columns[i]);
|
|
1200
|
+
const indexesToDrop = [];
|
|
1201
|
+
const indexesToCreate = [];
|
|
1202
|
+
const addedIndexes = newIndexes.filter((newIndex) => !oldIndexes.some((oldIndex) => oldIndex.name == newIndex.name));
|
|
1203
|
+
for (const oldIndex of oldIndexes) {
|
|
1204
|
+
const newIndex = newIndexes.find((entry) => entry.name == oldIndex.name);
|
|
1205
|
+
if (newIndex) {
|
|
1206
|
+
if (isSameIndex(oldIndex, newIndex)) continue;
|
|
1207
|
+
Logger.log(`Index ${oldIndex.name} in ${tableName} will be changed!`);
|
|
1208
|
+
this.migrations.compareWithAllowedMigration(tableName, "dropIndex", oldIndex.name);
|
|
1209
|
+
indexesToCreate.push(newIndex);
|
|
1210
|
+
} else {
|
|
1211
|
+
const renamedIndex = addedIndexes.find((entry) => isSameIndex(oldIndex, entry));
|
|
1212
|
+
if (renamedIndex) {
|
|
1213
|
+
Logger.log(`Index ${oldIndex.name} in ${tableName} will be renamed to ${renamedIndex.name}!`);
|
|
1214
|
+
addedIndexes.splice(addedIndexes.indexOf(renamedIndex), 1);
|
|
1215
|
+
indexesToCreate.push(renamedIndex);
|
|
1216
|
+
} else {
|
|
1217
|
+
Logger.log(`Index ${oldIndex.name} in ${tableName} will be removed!`);
|
|
1218
|
+
this.migrations.compareWithAllowedMigration(tableName, "dropIndex", oldIndex.name);
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
indexesToDrop.push(oldIndex);
|
|
1222
|
+
}
|
|
1223
|
+
for (const newIndex of addedIndexes) {
|
|
1224
|
+
Logger.log(`Index ${newIndex.name} in ${tableName} will be added!`);
|
|
1225
|
+
indexesToCreate.push(newIndex);
|
|
1226
|
+
}
|
|
1227
|
+
if (this.migrations.willBeRecreated(tableName)) continue;
|
|
1228
|
+
for (const index of indexesToDrop) {
|
|
1229
|
+
dropped.up += `${this.dialect.dropIndex(tableName, index.name)}\n`;
|
|
1230
|
+
created.down += `${this.dialect.createIndex(dbTableName, index)}\n`;
|
|
1231
|
+
}
|
|
1232
|
+
for (const index of indexesToCreate) {
|
|
1233
|
+
created.up += `${this.dialect.createIndex(tableName, index)}\n`;
|
|
1234
|
+
dropped.down += `${this.dialect.dropIndex(dbTableName, index.name)}\n`;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
return {
|
|
1238
|
+
dropped,
|
|
1239
|
+
created
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1082
1243
|
* Retrieves the name of the primary key column from a list of column information.
|
|
1083
1244
|
*
|
|
1084
1245
|
* @param columnInfoEntries - An array of ColumnInfo objects containing details about each column.
|
|
@@ -1160,7 +1321,8 @@ var TableObj = class TableObj {
|
|
|
1160
1321
|
table: "",
|
|
1161
1322
|
primaryKey: void 0,
|
|
1162
1323
|
columns: {},
|
|
1163
|
-
foreignKeys: []
|
|
1324
|
+
foreignKeys: [],
|
|
1325
|
+
indexes: []
|
|
1164
1326
|
};
|
|
1165
1327
|
constructor(tableName, defaultValues) {
|
|
1166
1328
|
const addColumnEntry = (key, value) => {
|
|
@@ -1219,6 +1381,21 @@ var TableObj = class TableObj {
|
|
|
1219
1381
|
return this;
|
|
1220
1382
|
}
|
|
1221
1383
|
/**
|
|
1384
|
+
* Creates an index on one or more columns of the current table.
|
|
1385
|
+
*
|
|
1386
|
+
* @param columns - The columns that will be part of the index (in order).
|
|
1387
|
+
* @param options - Optional configurations for the index.
|
|
1388
|
+
* If no name is provided, the name is generated from the table and column names.
|
|
1389
|
+
*/
|
|
1390
|
+
index(columns, options) {
|
|
1391
|
+
this.tableStructure.indexes.push({
|
|
1392
|
+
name: options?.name,
|
|
1393
|
+
columns: columns.map((column) => column.toString()),
|
|
1394
|
+
isUnique: !!options?.unique
|
|
1395
|
+
});
|
|
1396
|
+
return this;
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1222
1399
|
* An optional helper function to deal with `TS2589: Type instantiation is excessively deep and possibly infinite. when used as method parameter`
|
|
1223
1400
|
* Does nothing in runtime
|
|
1224
1401
|
*/
|
|
@@ -1268,6 +1445,26 @@ function ForeignKey(toTable, toColumn, options) {
|
|
|
1268
1445
|
};
|
|
1269
1446
|
}
|
|
1270
1447
|
//#endregion
|
|
1448
|
+
//#region src/lib/tableInfo/decorators/Index.ts
|
|
1449
|
+
/**
|
|
1450
|
+
* Decorator function to create an index on one or more columns of a table.
|
|
1451
|
+
* This decorator must be used at the head of the class.
|
|
1452
|
+
*
|
|
1453
|
+
* @param columns - The columns that will be part of the index (in order).
|
|
1454
|
+
* @param options - Additional options. If no name is provided, the name is generated from the table and column names.
|
|
1455
|
+
*/
|
|
1456
|
+
function Index(columns, options) {
|
|
1457
|
+
return (table, context) => {
|
|
1458
|
+
const tableInfo = context?.metadata ? getTableStructureFromMetadata(context.metadata) : getTableStructure(table);
|
|
1459
|
+
if (!tableInfo.indexes) tableInfo.indexes = [];
|
|
1460
|
+
tableInfo.indexes.push({
|
|
1461
|
+
name: options?.name,
|
|
1462
|
+
columns: columns.map((column) => column.toString()),
|
|
1463
|
+
isUnique: !!options?.unique
|
|
1464
|
+
});
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
//#endregion
|
|
1271
1468
|
//#region src/lib/dialects/SqliteDialect.ts
|
|
1272
1469
|
/**
|
|
1273
1470
|
* SQLite-specific syntax for SQL queries
|
|
@@ -1336,6 +1533,13 @@ var SqliteDialect = class extends DefaultSql {
|
|
|
1336
1533
|
};
|
|
1337
1534
|
});
|
|
1338
1535
|
}
|
|
1536
|
+
async getIndexes(tableName) {
|
|
1537
|
+
const data = await this.db.runReadStatement(`SELECT il.name AS index_name, il."unique" AS is_unique, ii.name AS column_name
|
|
1538
|
+
FROM pragma_index_list('${tableName}') AS il JOIN pragma_index_info(il.name) AS ii
|
|
1539
|
+
WHERE il.origin = 'c'
|
|
1540
|
+
ORDER BY il.name, ii.seqno;`);
|
|
1541
|
+
return this.rowsToIndexInfo(data);
|
|
1542
|
+
}
|
|
1339
1543
|
async setChanges(fromVersion, toVersion, changes) {
|
|
1340
1544
|
await this.db.runTransaction(`PRAGMA user_version = ${toVersion};`);
|
|
1341
1545
|
await super.setChanges(fromVersion, toVersion, changes);
|
|
@@ -1422,6 +1626,18 @@ var PostgresDialect = class extends DefaultSql {
|
|
|
1422
1626
|
removePrimaryKey(tableName, columnName) {
|
|
1423
1627
|
return `ALTER TABLE ${tableName} DROP CONSTRAINT ${columnName};\n`;
|
|
1424
1628
|
}
|
|
1629
|
+
async getIndexes(tableName) {
|
|
1630
|
+
const data = await this.db.runReadStatement(`SELECT i.relname AS index_name, ix.indisunique AS is_unique, a.attname AS column_name
|
|
1631
|
+
FROM pg_index AS ix
|
|
1632
|
+
JOIN pg_class AS t ON t.oid = ix.indrelid
|
|
1633
|
+
JOIN pg_class AS i ON i.oid = ix.indexrelid
|
|
1634
|
+
JOIN pg_namespace AS n ON n.oid = t.relnamespace
|
|
1635
|
+
JOIN pg_attribute AS a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey)
|
|
1636
|
+
WHERE n.nspname = 'public' AND t.relname = '${tableName}' AND NOT ix.indisprimary
|
|
1637
|
+
AND NOT EXISTS (SELECT 1 FROM pg_constraint AS c WHERE c.conindid = ix.indexrelid)
|
|
1638
|
+
ORDER BY i.relname, array_position(ix.indkey::int2[], a.attnum);`);
|
|
1639
|
+
return this.rowsToIndexInfo(data);
|
|
1640
|
+
}
|
|
1425
1641
|
async getForeignKeys(tableName) {
|
|
1426
1642
|
return (await this.db.runReadStatement(`SELECT
|
|
1427
1643
|
tc.table_name AS child_table,
|
|
@@ -1514,6 +1730,21 @@ var MySqlDialect = class extends DefaultSql {
|
|
|
1514
1730
|
};
|
|
1515
1731
|
});
|
|
1516
1732
|
}
|
|
1733
|
+
dropIndex(tableName, indexName) {
|
|
1734
|
+
return `DROP INDEX ${this.formatIdentifier(indexName)} ON ${this.formatIdentifier(tableName)};`;
|
|
1735
|
+
}
|
|
1736
|
+
async getIndexes(tableName) {
|
|
1737
|
+
const data = await this.db.runReadStatement(`SELECT s.INDEX_NAME AS index_name, s.NON_UNIQUE = 0 AS is_unique, s.COLUMN_NAME AS column_name
|
|
1738
|
+
FROM information_schema.STATISTICS AS s
|
|
1739
|
+
WHERE s.TABLE_SCHEMA = (SELECT DATABASE()) AND s.TABLE_NAME = '${tableName}' AND s.INDEX_NAME <> 'PRIMARY'
|
|
1740
|
+
AND NOT EXISTS (
|
|
1741
|
+
SELECT 1 FROM information_schema.KEY_COLUMN_USAGE AS k
|
|
1742
|
+
WHERE k.TABLE_SCHEMA = s.TABLE_SCHEMA AND k.TABLE_NAME = s.TABLE_NAME AND k.REFERENCED_TABLE_NAME IS NOT NULL
|
|
1743
|
+
AND (k.CONSTRAINT_NAME = s.INDEX_NAME OR k.COLUMN_NAME = s.INDEX_NAME)
|
|
1744
|
+
)
|
|
1745
|
+
ORDER BY s.INDEX_NAME, s.SEQ_IN_INDEX;`);
|
|
1746
|
+
return this.rowsToIndexInfo(data);
|
|
1747
|
+
}
|
|
1517
1748
|
async getTableNames() {
|
|
1518
1749
|
return (await this.db.runReadStatement("SHOW TABLES")).map((entry) => Object.values(entry)[0]);
|
|
1519
1750
|
}
|
|
@@ -1553,6 +1784,13 @@ function getDialect(db, dialect) {
|
|
|
1553
1784
|
}
|
|
1554
1785
|
//#endregion
|
|
1555
1786
|
//#region src/index.ts
|
|
1787
|
+
/**
|
|
1788
|
+
* Executes a database migration based on the provided database connection and migration instructions.
|
|
1789
|
+
*
|
|
1790
|
+
* @param db - The database access object used to interact with the database.
|
|
1791
|
+
* @param dbInstructions - The migration instructions, including target version, logger mode, and dialect.
|
|
1792
|
+
* @return A promise that resolves when the migration process is successfully completed.
|
|
1793
|
+
*/
|
|
1556
1794
|
async function runMigration(db, dbInstructions) {
|
|
1557
1795
|
Logger.setMode(dbInstructions.loggerMode);
|
|
1558
1796
|
const dialect = getDialect(db, dbInstructions.dialect);
|
|
@@ -1590,4 +1828,4 @@ async function rollback(dbAccess, toVersion, dialect, loggerMode) {
|
|
|
1590
1828
|
return version;
|
|
1591
1829
|
}
|
|
1592
1830
|
//#endregion
|
|
1593
|
-
export { DataType, ForeignKey, TableClass, TableObj, rollback, runMigration };
|
|
1831
|
+
export { DataType, ForeignKey, Index, TableClass, TableObj, rollback, runMigration };
|