Android Room Database Migrate closed Database - android

Room Database Migrate method
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE menuItems ADD COLUMN cnt INTEGER DEFAULT 0 NOT NULL");
}
database is throwing an error
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase:
I have not used database close method anywhere in the method.
I am not able to alter database table.
How to get rid of above exception.
I tried to open database in migrate method.

we migrated to room latest version and used automigrationspec to solve this issue

Related

Android Room, SQLite - TO expected, got 'COLUMN'

I'm trying to rename a column in my Room database. I foolishly used the column name index and want to change it to id, but this migration function is causing headaches:
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
#Override
public void migrate(#NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE items RENAME COLUMN index TO id;");
}
};
I followed this syntax diagram for the query:
Android Studio is giving me the error TO expected, got 'COLUMN', and I cannot use the database due to the RuntimeException:
Caused by: java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
The version number is correct, so I am assuming this issue is caused by the syntax problem above; I cannot find anything else wrong with my setup.
Android uses SQLite v3.19. That makes renaming a column using RENAME COLUMN not possible. The best approach is to recreate the table. – Leo Pelozo
Looks like I need to create function that drops the table so I can create a new one.
#Query("DELETE FROM items")
void dropTable();
... then create the table again, though I'm not sure how to go about this.
Update:
I was able (I think, we'll see...) to re-create the table by calling the above function, removing ALL migrations and setting the database version back to 1. Then I re-defined the database class itself with the proper names etc. and was able to insert data into it without any errors. Adding .fallbackToDestructiveMigration() to my database singleton class was also necessary.
Personally I think this is a little ridiculous just for simply re-naming a column; I was never able to simply rename the column and add a migration for the change, nor was I able to drop the table and re-create it with the proper column name and add that as a migration. But alas, this is Android after all.

"SQLiteException error: No such table" if I change the table's name in the same DB and run again the app [duplicate]

This question already has answers here:
"No Such Table" Error found in SQLite Android
(11 answers)
Closed 8 years ago.
I have created a fts database DATABASE_NAME and a table FTS_VIRTUAL_TABLE.
I can insert and select values.
If I change the name of the table in my code e.g. FTS_VIRTUAL_TABLE_NEW keeping the same database DATABASE_NAME and run the application I get a SQLiteException error: No such table: FTS_VIRTUAL_TABLE_NEW. I wonder, why, since in onCreate method the proper SQL statement exists. Shouldn't the FTS_VIRTUAL_TABLE_NEW have been created?
However if I change not only the name of the table but also the name of the database (i.e. new database) everything works. The app runs normally (of course with no values in the database initially).
I don't understand why I can't only change the name of the table keeping the same database.
If you change your database schema at all, you need to increment your database version used in your SQLiteOpenHelper constructor and handle upgrades (i.e. drop and recreate or modify tables) in the onUpgrade method.

Android SQLcipher PRAGMA problems

Hey guys I am having a some problems with SQLcipher db for android
The documentation is not too descriptive so I could not figure it out.
I am trying to modify the default number of iterations on sqlcipher for android,
I am editing the notecipher app provided as demo app with sqlcipher, and want to increase the kdf_iter to i.e. 5000
By overriding the getWritableDatabase() method in the database helper i enter the pragma value just after the file is open with the password.
I can open and initialize the database, but I cannot re-open the db if I do a database.close() call.
whenever I close the database on the next open() call I get a :
I/Database(807): sqlite returned: error code = 26, msg = file is encrypted or is not a database
E/Database(807): CREATE TABLE android_metadata failed
E/Database(807): Failed to setLocale() when constructing, closing the database
E/Database(807): info.guardianproject.database.sqlcipher.SQLiteException: file is encrypted or is not a database
You'll want to use a SQLiteDatabaseHook object to call the kdf_iter pragma. This will ensure that the pragma is called immediately after the database is opened, but before it is used.
SQLiteDatabaseHook hook = new SQLiteDatabaseHook(){
public void preKey(SQLiteDatabase database){
database.rawExecSQL("PRAGMA kdf_iter = 5000");
}
public void postKey(SQLiteDatabase database){}
}
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(databasePath, password, null, hook);
#Stephen answer is only partially correct, because according to the documentation:
PRAGMA kdf_iter must be called after PRAGMA key and before the first actual database operation or it will have no effect.
So the line:
database.rawExecSQL("PRAGMA kdf_iter = 5000");
Must be inserted in the postKey() method, and NOT in preKey().
This worked for me.

SQLite Delete Cascade not working

In Android 4.2, using SQLite 3.7.11, when I delete a row from the Quizzes table, who's schema is below, the corresponding rows in the QuizQuestions table are not deleted.
I can't figure out what's wrong. I have tried putting
db.execSQL("PRAGMA foreign_keys = ON;");
before and after the create table statements.
Create table statements:
CREATE TABLE quizzes(quiz_name TEXT PRIMARY KEY COLLATE NOCASE);
CREATE TABLE quizQuestions(quiz_name TEXT, question_id INTEGER,
PRIMARY KEY(quiz_name, question_id),
FOREIGN KEY(quiz_name) REFERENCES quizzes(quiz_name) ON DELETE CASCADE,
FOREIGN KEY(question_id) REFERENCES questions(question_id) ON DELETE CASCADE);
Your database should delete rows from quizQuestions in case someone is deleting from quizzes or from questions. It will ignore the entire foreign key constraint in case foreign key support is turned off and you have just regular columns that can contain any value.
SQLite defaults to PRAGMA foreign_keys = OFF every time you open the database. It's not a property of a table or of the schema.
In case you use SQLiteOpenHelper put it in onOpen. That is the place that is called every time the database is opened. onCreate only once when the database is created.
What SQLiteOpenHelper calls when you call getWriteableDatabase for the first time is
onConfigure every time, API Level >= 16 required
depending on the existence and version of the database file the following is called within an transaction
onCreate if there is no database file. Typically, this happens only once in the entire lifetime of the app.
onUpgrade if the database version (PRAGMA user_version - saved inside the database file) is less then the version supplied in SQLiteOpenHelper's constructor. Happens every time you bump the version in your code.
Nothing if file exists and version matches.
onOpen every time
If the same instance of SQLiteOpenHelper already has an open database it will just return it and nothing of above happens.
Try adding this right after opening database in your Android app:
db.execSQL("PRAGMA foreign_keys=ON");
This turns on support for foreign keys, which is necessary for ON DELETE CASCADE to work properly.
Sqlite disable foreign key constrain by default, so you need to enable it by simply override onOpen method in your DBhelper class like below
public class YourOwnDbHelper extends SQLiteOpenHelper {
#Override
public void onOpen(SQLiteDatabase db){
super.onOpen(db);
db.execSQL("PRAGMA foreign_keys=ON");
}
}
i had the same issue on visual basic!!! you have to write the command text like this:
cone.CommandText = "PRAGMA foreign_keys = ON; DELETE FROM employees
WHERE cod_emp=0;"
and you have to do it everytime you delete something

Android onUpgrade() fails because database is locked, what should I do differently?

I have a project with a set of classes that are responsible for their respective database tables.
Each table managing class contains CRUD methods that follow the pattern of get connection, run crud operation, close connection:
public class PersonManager {
SQLiteDatabase db;
DbAdapter dbAdapter; //This is a subclass of SQLiteOpenHelper
public void addPerson(Person person)
{
ContentValues contentValues = new ContentValues();
contentValues.put("email", person.email);
contentValues.put("first_name", person.firstName);
db = dbAdapter.getWritableDatabase();
db.insert("person", null, contentValues);
db.close();
}
...other crud/utility methods omitted...
}
Now that I am upgrading my database via onUpgrade(), I run into database locked issues.
The exact error message follows:
CREATE TABLE android_metadata failed
Failed to setLocale() when constructing, closing the database
android.database.sqlite.SQLiteException: database is locked
It appears that onUpgrade is either meant to:
1 run db.execSQL() calls or
2 use helper classes that use onUpgrade()'s SQLiteDatabase rather than their own
It would be much easier to use my table managing classes to migrate data in onUpgrade() than db.execSQL() statements, or rewrite all my CRUD methods to take onUpgrade()'s SQLiteDatabase.
Am I setting up my database access correctly? If the above code follows the correct pattern, what should I do to fix this issue?
Thanks!
Here's your problem:
db = dbAdapter.getWritableDatabase();
When you're in onUpgrade(), you have to use the SQLiteDatabase handle that onUpgrade() provides you. So your solution is to rewrite your addPerson function to take one more argument -- an SQLiteDatabase handle:
public void addPerson(Person person, SQLiteDatabase db) {...}
If you need to call addPerson() from elsewhere in your project, then keep your current addPerson(Person person) function, have it do that
db = dbAdapter.getWritableDatabase()
call, and pass db to your two-argument version of addPerson().
I didn't get any answers, so I asked on a dev hangout.
According to the Android Developer Hangout Team, onUpgrade is only meant for structure alterations, not really for data migration/manipulation.

Categories

Resources