plan method
Builds a plan from two schema snapshots.
Implementation
PostgresqlMigrationPlan plan({
required SchemaDocument from,
required SchemaDocument to,
}) {
from = from.withoutIgnored();
to = to.withoutIgnored();
final statements = <String>[];
final warnings = <String>[];
var requiresRebuild = false;
final matchedSourceEnumNames = <String>{};
final matchedEnumPairs = <String>{};
final fromEnumsByName = {
for (final definition in from.enums) definition.name: definition,
};
final fromEnumsByDatabaseName = {
for (final definition in from.enums) definition.databaseName: definition,
};
for (final targetEnum in to.enums) {
final sourceEnum =
fromEnumsByName.remove(targetEnum.name) ??
fromEnumsByDatabaseName.remove(targetEnum.databaseName) ??
_inferRenamedSourceEnum(
sourceSchema: from,
targetSchema: to,
targetEnum: targetEnum,
matchedSourceEnumNames: matchedSourceEnumNames,
);
if (sourceEnum == null) {
statements.add(schemaApplier.createEnumStatement(targetEnum));
continue;
}
matchedSourceEnumNames.add(sourceEnum.name);
matchedEnumPairs.add(_enumPairKey(sourceEnum, targetEnum));
fromEnumsByName.remove(sourceEnum.name);
fromEnumsByDatabaseName.remove(sourceEnum.databaseName);
// Handle DB-level type rename when @@map changes.
if (sourceEnum.databaseName != targetEnum.databaseName) {
final escapedOld = sourceEnum.databaseName.replaceAll('"', '""');
final escapedNew = targetEnum.databaseName.replaceAll('"', '""');
statements.add('ALTER TYPE "$escapedOld" RENAME TO "$escapedNew"');
}
if (_hasCompatibleEnumDefinition(sourceEnum, targetEnum)) {
continue;
}
final renamedValues = _renamedEnumValues(sourceEnum, targetEnum);
if (renamedValues != null) {
for (final rename in renamedValues) {
statements.add(
_createRenameEnumValueStatement(
targetEnum.databaseName,
rename.$1,
rename.$2,
),
);
}
continue;
}
final appendedValues = _appendedEnumValues(sourceEnum, targetEnum);
if (appendedValues != null) {
for (final statement in appendedValues) {
statements.add(statement);
}
continue;
}
final combinedValues = _combinedRenameInsertStatements(
sourceEnum,
targetEnum,
);
if (combinedValues != null) {
for (final statement in combinedValues) {
statements.add(statement);
}
continue;
}
requiresRebuild = true;
warnings.add(
'Altering enum ${targetEnum.databaseName} requires schema rebuild and data compatibility review.',
);
}
for (final removedEnum in fromEnumsByName.values) {
requiresRebuild = true;
warnings.add(
'Dropping enum ${removedEnum.databaseName} requires schema rebuild and data compatibility review.',
);
}
final fromModelsByName = {
for (final model in from.models) model.name: model,
};
final fromModelsByDatabaseName = {
for (final model in from.models) model.databaseName: model,
};
for (final targetModel in to.models) {
final sourceModel =
fromModelsByName.remove(targetModel.name) ??
fromModelsByDatabaseName.remove(targetModel.databaseName);
if (sourceModel == null) {
statements.add(
schemaApplier.createTableStatementForModel(targetModel, schema: to),
);
continue;
}
fromModelsByName.remove(sourceModel.name);
fromModelsByDatabaseName.remove(sourceModel.databaseName);
_planModelDiff(
sourceSchema: from,
targetSchema: to,
sourceModel: sourceModel,
targetModel: targetModel,
matchedEnumPairs: matchedEnumPairs,
statements: statements,
warnings: warnings,
);
}
for (final removedModel in from.models.where(
(model) =>
fromModelsByName.containsKey(model.name) ||
fromModelsByDatabaseName.containsKey(model.databaseName),
)) {
warnings.add(
'Dropping model ${removedModel.name} requires manual migration.',
);
}
_planImplicitManyToManyDiffs(
from: from,
to: to,
statements: statements,
warnings: warnings,
);
return PostgresqlMigrationPlan(
statements: List<String>.unmodifiable(statements),
warnings: List<String>.unmodifiable(warnings),
requiresRebuild: requiresRebuild,
);
}