flutterdartmoor

Flutter: in Moor how to change schemes and write the right onUpgrade method if there were 2 or more schemes before?


I have a database, here is how I create it:

@UseMoor(tables: [
  Users
  Images,
], daos: [
  UserDao,
  ImageDao
])
class AppDatabase extends _$AppDatabase {
  AppDatabase() : super(FlutterQueryExecutor.inDatabaseFolder(path: 'my-db.sql', logStatements: true));

  @override
  MigrationStrategy get migration => MigrationStrategy(
        beforeOpen: (details) async {
          await customStatement('PRAGMA foreign_keys = ON');
        },
        onUpgrade: (Migrator m, int from, int to) async {
          if (from == 1) {
            m.createTable(keps);
          }
        },
      );

  @override
  int get schemaVersion => 2;
}

As you can see first time I had only one table Users. Later added Images so I wrote this onUpgrade method:

onUpgrade: (Migrator m, int from, int to) async {
          if (from == 1) {
            m.createTable(images);
          }
        },

Now I want to add one colum to table Users but I don't know if I should write the onUpgrade like this:

onUpgrade: (Migrator m, int from, int to) async {
          if (from == 1) {
            m.createTable(images);
          }
          else if (from == 2) {
            await m.addColumn(users, users.keycloakName);
          }
        },
      );


  @override
  int get schemaVersion => 3;

In this option I expect the Moor to work like this: if the application on the phone is using a database that has schemaVersion == 2 then it is straightforward: it just adds the column.

But what if the schemaVersion on the phone is 1 and gets a new app version that is using schemaVersion == 3? Does the Moor realize this and first it runs the

          if (from == 1) {
            m.createTable(images);
          }

part, and after it done it runs the:

          else if (from == 2) {
            await m.addColumn(users, users.keycloakName);
          }

part, or I have to write the onUpgrade like this (repeat the code):

onUpgrade: (Migrator m, int from, int to) async {
          if (from == 1) {
            m.createTable(images);
          }
          else if (from == 2) {
            m.createTable(images);
            await m.addColumn(users, users.keycloakName);
          }
        },
      );


  @override
  int get schemaVersion => 3;

With this the problem that if the schemaVersion was already 2 on the phone, it tries to create the iamges table again...


Solution

  • The onUpgrade is only run once, so if you're 'upgrading' from 1 to 3, only the first (from == 1) upgrade will be run, in your code above. You could of course change your code a bit s.t. it will run all upgrades, like

    if (from == 1) {
        // upgrade from 1, all migrations from 1 to 2 here
        m.createTable(images);
    }
    if (from <= 2) {
        // upgrade from 2, all migrations from 2 to 3 here
        await m.addColumn(users, users.keycloakName);
    }
    if (from <= 3) {
        // upgrade from 3, all migrations from 3 to 4 here
        etc.etc()
    }
    

    But as I'm sure you'll understand you need to be very careful how you use this! I personally prefer my above approach to copy-pasting all migrations, but of course it depends on your schema and migrations.

    Final note

    There's actually a very recent discussion about this exact issue in this issue in the Moor repo, so be sure to check that out as well :)