tekartik / sqflite

SQLite flutter plugin
BSD 2-Clause "Simplified" License
2.86k stars 521 forks source link

Any way to handle a case when user manually delete the db file. #617

Open nikhilbadyal opened 3 years ago

nikhilbadyal commented 3 years ago

Can i handle a case where user delete the database manually. Imagine a root user, who is in mood to mess thing up and he/she delete the .db file from the database folder. Now it's obvious that if I'll try to insert. I will be getting a long error. How i can handle this ? I thought

 await openDatabase(join(databasePath, 'notes_database.db'),
          onCreate: (database, version) {
            return database.execute(
              _query(),
            );
          }, version: 1);

this check whether the db exist. But i dont think its working , :(

Error I'm getting

E/SQLiteLog(27300): (1032) statement aborts at 12: [INSERT OR REPLACE INTO notes (title, content, creationDate, lastModify, color, state, imagePath) VALUES (?, ?, ?, ?, ?, ?, ?)] attempt to write a readonly database
E/flutter (27300): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: DatabaseException(attempt to write a readonly database (code 1032 SQLITE_READONLY_DBMOVED)) sql 'INSERT OR REPLACE INTO notes (title, content, creationDate, lastModify, color, state, imagePath) VALUES (?, ?, ?, ?, ?, ?, ?)' args [, rqerer, 1616134491013, 1616134493674, 4294967295, 0, ]}
E/flutter (27300): #0      wrapDatabaseException (package:sqflite/src/exception_impl.dart:11:7)
E/flutter (27300): <asynchronous suspension>
E/flutter (27300): #1      SqfliteDatabaseMixin.txnRawInsert.<anonymous closure> (package:sqflite_common/src/database_mixin.dart:394:14)
E/flutter (27300): <asynchronous suspension>
E/flutter (27300): #2      BasicLock.synchronized (package:synchronized/src/basic_lock.dart:33:16)
E/flutter (27300): <asynchronous suspension>
E/flutter (27300): #3      SqfliteDatabaseMixin.txnSynchronized (package:sqflite_common/src/database_mixin.dart:346:14)
E/flutter (27300): <asynchronous suspension>
E/flutter (27300): #4      DatabaseHelper.insertNote (package:notes/database/database_helper.dart:56:15)
alextekartik commented 3 years ago

You can check if a database exists using databaseExists.

nikhilbadyal commented 3 years ago

Already tried this

static Future<Database> get database async {
    final databasePath = await getDatabasesPath();
    final complPath = join(databasePath, 'notes_database.db');
    bool status = await  databaseExists(complPath);
    if(!status){
      _database =  await openDatabase(complPath,
          onCreate: (database, version) {
            return database.execute(
              _query(),
            );
          }, version: 1);
    }
    return _database;
  }

and this

static Future<Database> get database async {
    final databasePath = await getDatabasesPath();
    bool status = await  databaseExists(databasePath);
    if(!status){
      _database =  await openDatabase(join(databasePath, 'notes_database.db'),
          onCreate: (database, version) {
            return database.execute(
              _query(),
            );
          }, version: 1);
    }
    return _database;
  }

still of no help. Anything I'm missing ?

alextekartik commented 3 years ago

Sorry I'm not sure what you are trying to do. If the use deletes the database when the app is not running, onCreate will work properly. If a user deletes the database while the app is running, there is not much we can do...

nikhilbadyal commented 3 years ago

Thanks for this much help. onCreate works fine if the app is not running. It creates new db on new run if it doesn't exist. And as you also mentioned, it doesn't handle the case if the app is running and the user delete db at same time. So is it possible to let the dev know, that such thing happened. Like when sqflite insert in db , it can throw some exception like DB not found. So that we can catch that and force the user to restart/rerun the app.

davidmartos96 commented 3 years ago

@ProblematicDude What feature in particular are you implementing that creates that situation in the first place? I had a similar situation once, where the database file could be replaced at runtime, but the simplest thing to in that case is to restart the app programmatically.

alextekartik commented 3 years ago

This is very specific so I'm not sure there is a solution that will match everybody's need.

It should already throw an exception (like you have noticed in the description) and you should be able to try/catch and get the errorCode (1032 but I'm not sure there is a cross platform way to have the same exception).

nikhilbadyal commented 3 years ago

@ProblematicDude What feature in particular are you implementing that creates that situation in the first place? I had a similar situation once, where the database file could be replaced at runtime, but the simplest thing to in that case is to restart the app programmatically.

Nothing much. Just inserting some notes in the db. But I imagined a case where the db is deleted at runtime. Can you explain how you managed to replace the db at runtime.

and yes I also want the simple solution( restart one). But i can't just force to restart the app. I was looking whether there is any particular error code or particular throw statement which i can catch and do something to fix things/or restart the app. I can't just catch any exception and force restart the app

nikhilbadyal commented 3 years ago

This is very specific so I'm not sure there is a solution that will match everybody's need.

It should already throw an exception (like you have noticed in the description) and you should be able to try/catch and get the errorCode (1032 but I'm not sure there is a cross platform way to have the same exception).

This is what is was looking for Error Code 1032 . I thought this was just a generic error code. Thanks for the help

davidmartos96 commented 3 years ago

@ProblematicDude In case you were not aware, the folder where the databases are stored on Android and iOS is protected, so unless the user intentionally wants to delete the database (and make the app misbehave) that situation shouldn't occur. In any case you can always catch errors at the top level of your Flutter app and show a general dialog that something unexpected has happened. About replacing the database is as simple as removing it. You just need the location of the old and new files, and then use the dart:io module to operate with the files.

nikhilbadyal commented 3 years ago

@ProblematicDude In case you were not aware, the folder where the databases are stored on Android and iOS is protected, so unless the user intentionally wants to delete the database (and make the app misbehave) that situation shouldn't occur. In any case you can always catch errors at the top level of your Flutter app and show a general dialog that something unexpected has happened. About replacing the database is as simple as removing it. You just need the location of the old and new files, and then use the dart:io module to operate with the files.

I know they are protected , that's why i mentioned in very first line. If a ROOT USER...... . Btw thanks for help :-)

davidmartos96 commented 3 years ago

@ProblematicDude Oops, missed that part, sorry. 😅