Search code examples
fluttersqflite

sqflite error "cannot commit - no transaction is active" fires only in release mode on iOS


The error

flutter: error DatabaseException(Error Domain=SqfliteDarwinDatabase Code=1 "cannot commit - no transaction is active" UserInfo={NSLocalizedDescription=cannot commit - no transaction is active}) sql 'COMMIT' args [] during open, closing...
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: DatabaseException(Error Domain=SqfliteDarwinDatabase Code=1 "cannot commit - no transaction is active" UserInfo={NSLocalizedDescription=cannot commit - no transaction is active}) sql 'COMMIT' args []

reproducec only in release mode on iOS on app laucng. The code is:

Future<Database> getDBInstance({BuildContext? context}) async {
  debugPrint('getting db instance');
  if (database == null) {
    debugPrint('db is null');
    Database db;
    if (Platform.isWindows) {
      sqfliteFfiInit();
      var databaseFactory = databaseFactoryFfi;
      db = await databaseFactory.openDatabase("cashway.db",
          options: OpenDatabaseOptions(
              version: 1, onCreate: onCreate, onUpgrade: onUpgrade));
    } else {
      String databasesPath = await getDatabasesPath();
      String dbPath = join(databasesPath, 'cashway.db');
      //the last printed output
      debugPrint('opening db');
      db = await openDatabase(dbPath,
          version: 1, onCreate: onCreate, onUpgrade: onUpgrade);
    }
    database = db;
    return db;
  } else {
    Database db = database!;
    return db;
  }
}

void onCreate(Database database, int version) async {
  debugPrint('on create');
  for (MigrationCallback callback in migrations) {
    debugPrint('doin migration');
    await callback(database);
  }
  resetDatabase();
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await getDBInstance();

  AppProvider appProvider = await AppProvider.getInstance();

  runApp(MultiProvider(
      providers: [ChangeNotifierProvider(create: (_) => appProvider)],
      child: const App()));
}

The getDBInstance is called in different places but I believe it's not the root of the problem since it fires only in release mode on iOS. But probably I'm wrong. The app itself hangs on black screen but asks for permissions for camera.

UPD

Future<void> resetDatabase(database) async {
  debugPrint('resetting');
  database.delete("payments", where: "id>0");
  database.delete("accounts", where: "id>0");
  database.delete("categories", where: "id>0");
  debugPrint('removed all entries');

  await database.insert("accounts", {
    "name": "Наличные",
    "icon": Icons.wallet.codePoint,
    "color": Colors.teal.value,
    "isDefault": 1
  });

Solution

  • What you can try first:

    • Create the parent folder of the database before opening the database, it sometimes does not exist yet
    • Check that in your migration callbacks - or anything that could happen during onCreate and onUpdate -, all db calls are properly awaited
    • Show some sql logs to check that indeed everything happens in the proper order - quick way. databaseFactory = databaseFactory.debugQuickLoggerWrapper(); - see here

    One question to be sure

    • I'm not sure what resetDatabase do. I assume it does not access the database (edited: that was the issue!)