Git Product home page Git Product logo

Comments (4)

simolus3 avatar simolus3 commented on June 21, 2024 1

Argh damn. We have been tricked by the horrible double-quoted strings misfeature. I should know better, but this still took me by surprise.
When you write SELECT "is_default" FROM accounts LIMIT 1;, some sqlite3 builds see that there is no is_default column which you've referenced and then think that you've obviously meant to select a string literal from the table instead of using the reference syntax to, well, reference a column.

The builds provided by sqlite3_flutter_libs don't do this, but migration tests are unit tests and they use the sqlite3 build from your system. You can fix this by applying a setup callback when creating the SchemaVerifier:

verifier = SchemaVerifier(schema.GeneratedHelper(),
    setup: (raw) => raw.config.doubleQuotedStringLiterals = false);

I will also apply this option by default since it's super confusing.

And one note that is unrelated but still good to be aware of: You won't get a SqliteException thrown when using NativeDatabase.createInBackground() - that would be a DriftRemoteException in that case. Something to be aware of if you intend to migrate to a background isolate.

Finally, ideally you shouldn't have to run schema checks at runtime since the migration isn't supposed to run when the column is already there.

from drift.

simolus3 avatar simolus3 commented on June 21, 2024

Thanks for the report - this sounds pretty bizarre though, I don't think we're catching those errors anywhere and I don't think sqlite3 will just accept those queries either.

Do you have a reduced example that reproduces this (e.g. the tables before and after, your migration and how the test looks like)?

from drift.

noxasch avatar noxasch commented on June 21, 2024

@simolus3 here some reduced example with the migration and test.

What I'm trying to do with the migration is since sqlite doesn't support IF NOT EXIST and trying not to read the schema since it will require me to write another model just for the migration.

My test is very simple, here are some example and the workaround that I'm currently use to make sure the test passed on CI.

model changes

// v1
class Accounts extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get name => text().withLength(max: 250)();
  DateTimeColumn get createdAt => dateTime().clientDefault(() => clock.now())();
  DateTimeColumn get updatedAt => dateTime().clientDefault(() => clock.now())();
}

// v2
class Accounts extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get name => text().withLength(max: 250)();
  DateTimeColumn get createdAt => dateTime().clientDefault(() => clock.now())();
  DateTimeColumn get updatedAt => dateTime().clientDefault(() => clock.now())();
  BoolColumn get isDefault => boolean().withDefault(const Constant(false))();
}

migration

MigrationStrategy get migration {
    return MigrationStrategy(
      onUpgrade: (Migrator m, int from, int to) async {
        await customStatement('PRAGMA foreign_keys = OFF');
      
        if (from == 1 && to == 2) {
          try {
            final rows = await customSelect(
              'SELECT "is_default" FROM accounts LIMIT 1;',
              readsFrom: {accounts},
            ).get();

            // WORKAROUND: no error is thrown in migration test
            final bool kTestMode =
                Platform.environment.containsKey('FLUTTER_TEST');
            if (kTestMode && rows.isEmpty) {
              await m.addColumn(accounts, accounts.isDefault);
              await customStatement('UPDATE accounts SET is_default = 0;');
            }
            // WORKAROUND END
          } on SqliteException catch (e) {
            if (e.message.contains('no such column')) {
              await m.addColumn(accounts, accounts.isDefault);
              await customStatement('UPDATE accounts SET is_default = 0;');
            }
          }
        }
      },
      beforeOpen: (OpeningDetails details) async {
        await customStatement('PRAGMA foreign_keys = ON');
      },
    );
  }

schema validation test

group('Schema Validation', () {
    late SchemaVerifier verifier;

   test('upgrade from v1 to v2', () async {
      final connection = await verifier.startAt(1);
      final db = AppDatabase.connect(connection);
      addTearDown(db.close);
      await verifier.migrateAndValidate(db, 2);
  });
}

from drift.

noxasch avatar noxasch commented on June 21, 2024

just to add after I encounter issue that depending on SqliteException is not that reliable in this case, I moved to following strategy instead.

if (from == 1 && to == 2) {
     final rows = await customSelect(
        'SELECT * FROM pragma_table_info(\'accounts\') WHERE name=\'is_default\';',
         readsFrom: {accounts},
      ).get();

     if (rows.isEmpty) {
        await m.addColumn(accounts, accounts.isDefault);
        await customStatement('UPDATE accounts SET is_default = 0;');
      }
}

from drift.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.