createMigration method

Future<MigrationVersionArtifacts?> createMigration({
  1. String? tag,
  2. String? precomputedVersion,
  3. required bool force,
  4. required GeneratorConfig config,
  5. MigrationGenerationContext? context,
  6. bool write = true,
})

Creates a new migration version. If tag is specified, the migration will be tagged with the given name. If force is true, the migration will be created even if there are warnings. If write is false, the migration will not be written to disk.

A precomputedVersion can be provided to skip the version name creation.

Returns the migration version, or null if no migration was created.

Throws MigrationVersionLoadException if the a migration version could not be loaded. Throws GenerateMigrationDatabaseDefinitionException if the database definition could not be created from project models. Throws MigrationVersionAlreadyExistsException if the migration version already exists.

Implementation

Future<MigrationVersionArtifacts?> createMigration({
  String? tag,
  String? precomputedVersion,
  required bool force,
  required GeneratorConfig config,
  MigrationGenerationContext? context,
  bool write = true,
}) async {
  var versions = await _artifactStore.listVersions();
  var latestVersion = versions.lastOrNull;

  var databaseDefinitionLatest = await _getSourceDatabaseDefinition(
    projectName,
    latestVersion,
  );

  var modelDefinitions =
      (context ?? await MigrationGenerationContext.load(config))
          .modelDefinitions;

  var databaseDefinitionProject = createDatabaseDefinitionFromModels(
    modelDefinitions,
    config.name,
    config.modulesAll,
    serverCode: serverCode,
  );

  var databaseDefinitions = await _loadModuleDatabaseDefinitions(
    config.modulesDependent,
    directory,
  );

  var versionName = precomputedVersion ?? createVersionName(tag);
  var nextMigrationVersion = DatabaseMigrationVersionModel(
    module: projectName,
    version: versionName,
  );

  var databaseDefinitionNext = _mergeDatabaseDefinitions(
    databaseDefinitionProject,
    databaseDefinitions,
    nextMigrationVersion,
  );

  var migration = generateDatabaseMigration(
    databaseSource: databaseDefinitionLatest,
    databaseTarget: databaseDefinitionNext,
  );

  var warnings = migration.warnings;
  _logWarnings(warnings);

  if (warnings.isNotEmpty && !force) {
    throw const MigrationAbortedException();
  }

  if (migration.isEmpty) {
    return null;
  }

  final dialect = serverCode
      ? config.databaseDialect
      : DatabaseDialect.sqlite;

  var sqlGenerator = SqlGenerator.forDialect(dialect);

  // Filter the elements here to keep the definition files complete. Only
  // the migration and definition SQL will be filtered by the dialect.
  var databaseDefinitionNextForDialect = databaseDefinitionNext.forDialect(
    dialect,
    logWarnings: log.warning,
  );

  var artifacts = MigrationVersionArtifacts(
    version: versionName,
    definitionSql: sqlGenerator.generateDatabaseDefinitionSql(
      databaseDefinitionNextForDialect,
      installedModules: databaseDefinitionNext.installedModules,
    ),
    migrationSql: sqlGenerator.generateDatabaseMigrationSql(
      migration,
      databaseDefinitionNextForDialect,
      installedModules: databaseDefinitionNext.installedModules,
      removedModules: _removedModulesDiff(
        databaseDefinitionLatest.installedModules,
        databaseDefinitionNext.installedModules,
      ),
    ),
    definition: databaseDefinitionNext,
    projectDefinition: databaseDefinitionProject,
    migration: migration,
  );

  if (write) {
    await _artifactStore.writeVersion(artifacts);
    versions.add(versionName);
    await _artifactStore.writeVersionRegistry(versions);
  }

  return artifacts;
}