Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sqflite malfunction upon restarting the application[Bug?] #598

Closed
IhabMks opened this issue Jul 18, 2023 · 11 comments
Closed

Sqflite malfunction upon restarting the application[Bug?] #598

IhabMks opened this issue Jul 18, 2023 · 11 comments
Labels
bug Something isn't working

Comments

@IhabMks
Copy link

IhabMks commented Jul 18, 2023

So i'm using drift_sqflite, which uses sqflite's SqfliteQueryExecutor, and the rest is pretty much similar as to using the drift library.
Starting the application for the first time, works just fine even when doing a hot-restart, but when doing a proper restart i got the below error, and i'm obliged to exit and do another flutter-tizen run.

Error:

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Bad state: databaseFactory not initialized
databaseFactory is only initialized when using sqflite. When using `sqflite_common_ffi`
You must call `databaseFactory = databaseFactoryFfi;` before using global openDatabase API
#0      databaseFactory.<anonymous closure> (package:sqflite_common/src/sqflite_database_factory.dart:29:7)
#1      databaseFactory (package:sqflite_common/src/sqflite_database_factory.dart:33:6)
#2      getDatabasesPath (package:sqflite_common/sqflite.dart:105:38)
#3      _SqfliteDelegate.open (package:drift_sqflite/drift_sqflite.dart:46:35)
#4      DelegatedDatabase.ensureOpen.<anonymous closure> (package:drift/src/runtime/executor/helpers/engines.dart:424:22)
<asynchronous suspension>
#5      Selectable.getSingleOrNull (package:drift/src/runtime/query_builder/statements/query.dart:240:18)
<asynchronous suspension>
#6      MyDatabase.getUpdatedAtValue (package:tizen_poke/database/database.dart:176:20)
<asynchronous suspension>
#7      CategoriesDataProvider._dataStillValid (package:tizen_poke/network/categories_data_provider.dart:47:33)
<asynchronous suspension>
#8      CategoriesDataProvider._fetchData (package:tizen_poke/network/categories_data_provider.dart:61:26)
<asynchronous suspension>
#9      BasicLock.synchronized (package:synchronized/src/basic_lock.dart:33:16)
<asynchronous suspension>
@IhabMks IhabMks changed the title Sqflite malfunction upon restarting the application[Bug] Sqflite malfunction upon restarting the application[Bug?] Jul 18, 2023
@swift-kim
Copy link
Member

Hmm. Does your app depend on the latest sqflite_tizen (0.1.2) which includes this patch: fba3848?

@swift-kim swift-kim added the bug Something isn't working label Jul 19, 2023
@IhabMks
Copy link
Author

IhabMks commented Jul 19, 2023

@swift-kim Yes it does, here's my pubspec:

  drift: ^2.8.0
  drift_sqflite: ^2.0.1
  sqflite_tizen: ^0.1.2

@swift-kim
Copy link
Member

Do you have sample code for reproduction?

@IhabMks
Copy link
Author

IhabMks commented Jul 20, 2023

Here's a code example:

@DataClassName('EntityA')
class TableA extends Table {
  TextColumn get name => text()();
  IntColumn get something => integer().nullable()();

  @override
  Set<Column<Object>>? get primaryKey => {name};
}

@DriftDatabase(
    tables: [TableA, TableB, TableC, ...])
class MyDatabase extends _$MyDatabase {
  MyDatabase() : super(_openConnection());

  // you should bump this number whenever you change or add a table definition.
  @override
  int get schemaVersion => 1;
  }

QueryExecutor _openConnection() {
  return SqfliteQueryExecutor.inDatabaseFolder(path: _dbName);
}


class MyProvider extends ChangeNotifier {
  final MyDatabase _database = MyDatabase();
  int? something = await _database.getSomething();
}

When doing a flutter-tizen run for the first time, it runs fine, but when you do for example changes that needs a reset instead of a hot-reload, the error pops up.

@swift-kim
Copy link
Member

swift-kim commented Jul 21, 2023

I modified the drift example app slightly to make it dependent on drift_sqflite (by replacing impl.connect() with a SqfliteQueryExecutor), but I'm getting a different error when trying to relaunch the app after installing.

flutter: DatabaseException(trigger todos_insert already exists (code 1))
flutter: #0      wrapDatabaseException (package:sqflite/src/exception_impl.dart:11:7)
flutter: <asynchronous suspension>
flutter: #1      BasicLock.synchronized (package:synchronized/src/basic_lock.dart:33:16)
flutter: <asynchronous suspension>
flutter: #2      SqfliteDatabaseMixin.txnSynchronized (package:sqflite_common/src/database_mixin.dart:490:14)
flutter: <asynchronous suspension>
flutter: #3      Migrator.create (package:drift/src/runtime/query_builder/migration.dart:86:7)
flutter: <asynchronous suspension>
flutter: #4      Migrator.createAll (package:drift/src/runtime/query_builder/migration.dart:76:7)
flutter: <asynchronous suspension>
flutter: #5      GeneratedDatabase.beforeOpen.<anonymous closure> (package:drift/src/runtime/api/db_base.dart:126:9)
flutter: <asynchronous suspension>
flutter: #6      DelegatedDatabase._runMigrations (package:drift/src/runtime/executor/helpers/engines.dart:458:5)
flutter: <asynchronous suspension>
flutter: #7      DelegatedDatabase.ensureOpen.<anonymous closure> (package:drift/src/runtime/executor/helpers/engines.dart:426:7)
flutter: <asynchronous suspension>
flutter: #8      NonNullableCancellationExtension.resultOrNullIfCancelled (package:drift/src/runtime/cancellation_zone.dart:62:24)
flutter: <asynchronous suspension>
flutter: #9      QueryStream.fetchAndEmitData (package:drift/src/runtime/executor/stream_queries.dart:313:20)
flutter: <asynchronous suspension>

I have no idea what is causing the error since I'm not familiar with the drift library, but anyway, according to the stack trace, the sqflite plugin was loaded correctly (no "databaseFactory not initialized" error). If you still get the "databaseFactory not initialized" error even after updating sqflite_tizen to 0.1.2, consider removing the drift_sqflite dependency itself from your app. As far as I understand (from its README), you don't need the dependency to use the drift package.

All you need to do is to add a path_provider_tizen dependency to your app

dependencies:
  path_provider_tizen: ^2.1.0

and add the following code to main() (before calling runApp()).

open.overrideFor(OperatingSystem.linux, () {
  return DynamicLibrary.open('/usr/share/dotnet.tizen/lib/libsqlite3.so');
});

Make sure not to use NativeDatabase.createBackgroundConnection() (which implicitly spawns an isolate) to open a database connection. Instead, use sqlite3.open().

// DON'T:
return NativeDatabase.createBackgroundConnection(await databaseFile);

// DO:
return DatabaseConnection(NativeDatabase.opened(sqlite3.open((await databaseFile).path)));

Reference: #277 (comment)

@IhabMks
Copy link
Author

IhabMks commented Jul 21, 2023

Hi, @swift-kim
I tried your solution, it works, but this time, upon restarting the app, i'm getting an error related to a specefic method : "getApplicationDocumentsDirectory", which i tried to avoid before.

Packages used:
path_provider: ^2.0.7
path_provider_tizen: ^2.1.0

Here's the error:

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: MissingPluginException(No implementation found for method getApplicationDocumentsDirectory on channel plugins.flutter.io/path_provider)
#0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:313:7)
<asynchronous suspension>
#1      getApplicationDocumentsDirectory (package:path_provider/path_provider.dart:120:24)
<asynchronous suspension>
#2      _openConnection.<anonymous closure> (package:tizen_poke/database/database.dart:197:22)
<asynchronous suspension>
#3      LazyDatabase._awaitOpened.<anonymous closure> (package:drift/src/utils/lazy_database.dart:47:32)
<asynchronous suspension>

@swift-kim
Copy link
Member

Could you try explicitly calling getApplicationDocumentsDirectory() from your app code to see if the path_provider plugin works?

Note that path_provider_tizen doesn't work in secondary (non-root) isolates by default so make sure all the code is run on the root isolate.

@IhabMks
Copy link
Author

IhabMks commented Jul 21, 2023

@swift-kim
I did like you asked:

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final dbFolder = await getApplicationDocumentsDirectory();
  open.overrideFor(OperatingSystem.linux, () {
    return DynamicLibrary.open('/usr/share/dotnet.tizen/lib/libsqlite3.so');
  });
  runApp(ChangeNotifierProvider(
      create: (context) => MyProvider(dbFolder),
      child: MaterialApp(...)

First application start works fine, but after a restart, still got the same error:

[E] [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: MissingPluginException(No implementation found for method getApplicationDocumentsDirectory on channel plugins.flutter.io/path_provider)
#0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:313:7)
<asynchronous suspension>
#1      getApplicationDocumentsDirectory (package:path_provider/path_provider.dart:120:24)
<asynchronous suspension>
#2      main (package:tizen_poke/main.dart:35:20)
<asynchronous suspension>

@swift-kim
Copy link
Member

Well, I can't reproduce the error, and I can't guess what has gone wrong. You should have the file tizen/flutter/generated_main.dart in your project, and it should have a plugin registrant entrypoint function which calls PathProviderPlugin.register(). You will get the error if this function is not called on app startup. The code is already compiled into your app package so it shouldn't matter if it's a first-time launch or a restart.

A simple workaround is to call the function directly in your app code (maybe in main()),

import 'package:path_provider_tizen/path_provider_tizen.dart';

PathProviderPlugin.register();

but, if possible, I'd like to know what the actual cause of the bug is so that we can fix it.

@IhabMks
Copy link
Author

IhabMks commented Jul 21, 2023

@swift-kim
I just tried calling it from the main method, and it works fine:

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  PathProviderPlugin.register();
  final dbFolder = await getApplicationDocumentsDirectory();
  open.overrideFor(OperatingSystem.linux, () {
    return DynamicLibrary.open('/usr/share/dotnet.tizen/lib/libsqlite3.so');
  });
...

But as you said, that part of the code is already compiled into the app package, so it wouldn't need to be called again after a restart as it already did the first time.

I'll keep trying different approaches, and it see what the actual cause is.

@swift-kim
Copy link
Member

Please let us know if the problem still persists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants