Skip to content

Commit cb4831b

Browse files
authored
fix: Cascade recreation of table to related tables. (serverpod#3195)
1 parent 4b0e55f commit cb4831b

File tree

2 files changed

+566
-10
lines changed

2 files changed

+566
-10
lines changed

tools/serverpod_cli/lib/src/database/migration.dart

+62-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import 'package:serverpod_shared/serverpod_shared.dart';
21
import 'package:serverpod_service_client/serverpod_service_client.dart';
2+
import 'package:serverpod_shared/serverpod_shared.dart';
3+
34
import 'extensions.dart';
45

56
DatabaseMigration generateDatabaseMigration({
@@ -9,17 +10,25 @@ DatabaseMigration generateDatabaseMigration({
910
var warnings = <DatabaseMigrationWarning>[];
1011
var actions = <DatabaseMigrationAction>[];
1112

12-
// Find deleted tables
13-
var deleteTables = <String>[];
14-
var sourceTables = databaseSource.tables.where((table) => table.isManaged);
15-
var targetTables = databaseTarget.tables.where((table) => table.isManaged);
13+
var sourceTables =
14+
databaseSource.tables.where((table) => table.isManaged).toList();
15+
var targetTables =
16+
databaseTarget.tables.where((table) => table.isManaged).toList();
17+
var deleteTables = <String>{};
1618

19+
// Mark tables which do not exist in the target schema anymore for deletion
1720
for (var srcTable in sourceTables) {
1821
if (!databaseTarget.containsTableNamed(srcTable.name)) {
19-
deleteTables.add(srcTable.name);
22+
deleteTables.addAll([
23+
srcTable.name,
24+
// For any table we delete, we also need to delete any other existing table that has and retains a foreign key pointing into this table
25+
..._findDependentTables(srcTable.name,
26+
sourceTables: sourceTables, targetTables: targetTables),
27+
]);
2028
}
2129
}
22-
for (var tableName in deleteTables.reversed) {
30+
31+
for (var tableName in deleteTables.toList().reversed) {
2332
actions.add(
2433
DatabaseMigrationAction(
2534
type: DatabaseMigrationActionType.deleteTable,
@@ -43,12 +52,13 @@ DatabaseMigration generateDatabaseMigration({
4352
(table) => table?.name == dstTable.name,
4453
orElse: () => null);
4554

46-
if (srcTable == null || srcTable.managed == false) {
55+
if (srcTable == null ||
56+
srcTable.managed == false ||
57+
deleteTables.contains(srcTable.name)) {
4758
// Added table
48-
4959
actions.add(
5060
DatabaseMigrationAction(
51-
type: srcTable == null
61+
type: srcTable == null || deleteTables.contains(srcTable.name)
5262
? DatabaseMigrationActionType.createTable
5363
: DatabaseMigrationActionType.createTableIfNotExists,
5464
createTable: dstTable,
@@ -92,6 +102,47 @@ DatabaseMigration generateDatabaseMigration({
92102
);
93103
}
94104

105+
/// Returns the set of table names for all tables which have any relation into the table mentioned by [tableName]
106+
Set<String> _findDependentTables(
107+
String tableName, {
108+
required List<TableDefinition> sourceTables,
109+
required List<TableDefinition> targetTables,
110+
Set<String>? dependentTables,
111+
}) {
112+
dependentTables ??= {};
113+
114+
/// Returns whether the [sourceTable] has a current and future relation to [tableName]
115+
bool hasCurrentAndFutureRelationToTable(TableDefinition sourceTable) {
116+
return sourceTable.foreignKeys.any((foreignKey) =>
117+
foreignKey.referenceTable == tableName &&
118+
// Check whether the reference will also be upheld in the target table.
119+
// otherwise the target table will already be modified and does not need to have be fully dropped
120+
targetTables.any((targetTable) =>
121+
targetTable.name == sourceTable.name &&
122+
targetTable.foreignKeys.any((targetForeignKey) =>
123+
targetForeignKey.constraintName == foreignKey.constraintName)));
124+
}
125+
126+
for (var sourceTable in sourceTables) {
127+
if (dependentTables.contains(sourceTable.name)) {
128+
continue;
129+
}
130+
131+
if (hasCurrentAndFutureRelationToTable(sourceTable)) {
132+
dependentTables.add(sourceTable.name);
133+
134+
_findDependentTables(
135+
sourceTable.name,
136+
sourceTables: sourceTables,
137+
targetTables: targetTables,
138+
dependentTables: dependentTables,
139+
);
140+
}
141+
}
142+
143+
return dependentTables;
144+
}
145+
95146
TableMigration? generateTableMigration(
96147
TableDefinition srcTable,
97148
TableDefinition dstTable,
@@ -247,6 +298,7 @@ TableMigration? generateTableMigration(
247298
}
248299
if (!srcKey.like(dstKey)) {
249300
deleteForeignKeys.add(srcKey.constraintName);
301+
250302
addForeignKeys.add(dstKey);
251303
}
252304
}

0 commit comments

Comments
 (0)