tamimattafi/kabin

ForeignKey: Inconsistent behavior across different KMP targets, especially during migration

Closed this issue · 0 comments

Problem

Enabling and disabling foreign keys currently works through the API provided by sqldelight, however, this leads to inconsistent behavior between android and iOS.

Explanation

Inside NativeSqliteDriver, and exactly during the creation of a connection through DatabaseManager, PRAGMA foreign_keys = ON; is called before migrate.

migrate is executed inside a transaction, therefore, our PRAGMA foreign_keys = OFF; call inside KabinSqlSchema.migrate function has no effect, because according to the official docs, it should be called before BEGIN TRANSACTION or after COMMIT https://www.sqlite.org/pragma.html#pragma_foreign_keys

Even tho we have defer_foreign_keys calls inside our transactions, some errors triggered by foreign keys during migration are not handled, since they are not directly related to constraints, for example, having table not found errors when dropping related tables. This throws an error immediately and doesn't wait for COMMIT to recheck the integrity of the keys https://www.sqlite.org/pragma.html#pragma_defer_foreign_keys

On android, foreign keys are enabled inside the callback onOpen, here's the official documentation of the method:

Called when the database has been opened. The implementation should check SupportSQLiteDatabase.isReadOnly before updating the database.
This method is called after the database connection has been configured and after the database schema has been created, upgraded or downgraded as necessary. If the database connection must be configured in some way before the schema is created, upgraded, or downgraded, do it in onConfigure instead.
Params:
db - The database.

As we can see, onOpen is called after all the work with the scheme is done, including migration, which means during migration, foreign keys are disabled, and enabled only after everything is set.

Solution

Make a unified logic to enabled and disable foreign keys during driver creation, using raw SQL calls, rather than using platform specific config. This will help to increase the consistency and similarity.