runMigrationSteps static method

Future<void> runMigrationSteps({
  1. required Migrator migrator,
  2. required int from,
  3. required int to,
  4. required MigrationStepWithVersion steps,
})

Helper method that runs a (subset of) stepByStepHelper by invoking the steps function for each intermediate schema version from from until to is reached.

This can be used to implement a custom OnUpgrade callback that runs additional checks before and after the migrations:

onUpgrade: (m, from, to) async {
 await customStatement('PRAGMA foreign_keys = OFF');

 await transaction(
   () => VersionedSchema.runMigrationSteps(
     migrator: m,
     from: from,
     to: to,
     steps: migrationSteps(
       from1To2: ...,
       ...
     ),
   ),
 );

 if (kDebugMode) {
   final wrongForeignKeys = await customSelect('PRAGMA foreign_key_check').get();
   assert(wrongForeignKeys.isEmpty, '${wrongForeignKeys.map((e) => e.data)}');
 }

 await customStatement('PRAGMA foreign_keys = ON;');
},

Implementation

static Future<void> runMigrationSteps({
  required Migrator migrator,
  required int from,
  required int to,
  required MigrationStepWithVersion steps,
}) async {
  final database = migrator.database;

  for (var target = from; target < to;) {
    final newVersion = await steps(target, database);
    assert(newVersion > target);

    // Saving the schema version after each step prevents the schema of the
    // database diverging from what's stored in `user_version` if a migration
    // fails halfway.
    // We can only reliably do this for sqlite3 at the moment since managing
    // schema versions happens at a lower layer and is not current exposed to
    // the query builder.
    if (database.executor.dialect == SqlDialect.sqlite) {
      await database.customStatement('pragma user_version = $newVersion');
    }

    target = newVersion;
  }
}