Comments (4)
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.
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.
@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.
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)
- Can't compile to wasm HOT 1
- [web] TypeError: Failed to execute 'compile' on 'WebAssembly': Incorrect response MIME type. Expected 'application/wasm' HOT 1
- SqliteException(1): while preparing a statement, unrecognized token: "@" HOT 3
- Database can no longer be mocked with new manager API HOT 10
- [Documentation] Insufficient documentation and examples on complex migration HOT 1
- Ability to mark foreign key constraint as deferrable HOT 1
- Schema verifier fails in setting DatabaseConfig.doubleQuotedStringLiterals HOT 4
- Flutter not resolving
- database is locked error with WAL journal mode HOT 4
- Option to run a few queries sequentially HOT 3
- Code generation creates database table with wrong type HOT 9
- Drift generator doesn't strip custom row class from SQL HOT 1
- Primary Key not required in companion insert HOT 2
- Issue with generated code: database.g.dart file HOT 29
- Select int64 return type HOT 4
- Start a select with the Dart API without specifiying a table HOT 4
- Custom query generated type missing nullables HOT 4
- [WEB] DriftRemoteException: RangeError (start): Invalid value: Not in inclusive range 0..2322399232: -1972640192 HOT 1
- createdAt & updatedAt columns HOT 4
- SqliteException(5): while selecting from statement, database is locked, database is locked (code 5) HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from drift.