In the last time I get some strange reviews on the PlayStore.
Whyever after installing the update of my app, users complain all their app data has been wiped. I have absolutely no idea how this can happen. Database is only recreated if tables do not exist.
Code which is executed after opening database connection:
private void createTablesIfNotExist() {
//query tables
Cursor c = DatabaseManager.executeSelect("SELECT name FROM sqlite_master WHERE type = \"table\"");
//if tables already created, do nothing (+1 because of header table)
if (!((tableAmount + 1) == c.getCount())) {
//meta data table
this.database.execSQL("DROP TABLE IF EXISTS android_metadata;");
this.database.execSQL("CREATE TABLE android_metadata (locale TEXT);");
this.database.execSQL("INSERT INTO android_metadata VALUES('de_DE');");
}
//create event table
c = DatabaseManager.executeSelect("SELECT name FROM sqlite_master WHERE name = \"" + EVENTS + "\"");
c.moveToFirst();
if(c.isAfterLast()) {
this.database.execSQL("CREATE TABLE.....");
}
c = DatabaseManager.executeSelect("SELECT name FROM sqlite_master WHERE name = \"" + ASSIGNED_PRODUCTS + "\"");
c.moveToFirst();
if(c.isAfterLast()) {
this.database.execSQL("CREATE TABLE.......");
}
}
Has anyone an idea? Thanks :)
I think you don't "rewrite" your data from old database to new one.
Android: upgrading DB version and adding new table
Check this link and if it is your problem then if possible you can transfer data for each version to new one and everything will be fine. You have to remember to design database and dataflow to keep in mind that old users can lack some data, so some data in new database can be nullable
Related
On devices with modified android versions i get this error. For example on Xiaomi devices.
String query = "select * from dialogues where userId = ? and topChat = 0 order by updatedAtByClient desc";
Cursor dialogRes = db.rawQuery(query, new String[]{userId});
Here i get exception:
android.database.sqlite.SQLiteException: no such column: topChat (code 1):,
while compiling: select * from dialogues where userId = ? and topChat = 0
order by updatedAtByClient desc
I have written the exception message by hand, because the user has sent me it in a screenshot, so there might be typos.
How can this be fixed, and why does this happen?
UPD1:
the create table statement looks similar to this:
"CREATE TABLE IF NOT EXISTS dialogues(fieldName VARCHAR, camelCaseFieldName VARCHAR,
topChat INTEGER, createdAt DATE);";
And i have a correctly implemented update method for when im changing the DB structure, but this particular table and field name did not change for a long time.
UPD2:
i have made an apk for the user with problems, that logs that table columns, and i did see the problematic column in the log, and user says that this version works ok.
So seems that this error does not happen 100% of times. Very strange. Maybe there is a way to check the database for integrity after creating it, and recreate tables with errors?
I don't believe this would be a xiaomi issue. it rather seems be the result of an unfortunate migration, where a new column had not been added and subsequently, the user might still work with the previous version of the table. and there is no other logical explanation for an absent column), simply because either the CREATE TABLE statement works - or it doesn't.
one can still work around it with ALTER TABLE. eg. when that SQLiteException occurs, addColumnIfNotExists("dialogues", "topChat", "INTEGER DEFAULT 0"); ...in order not to cause data-loss by dropping the table, only because it lacks some column.
public void addColumnIfNotExists(String tableName, String columnName, String dataType) {
Cursor cursor = null;
try {
cursor = db.rawQuery("SELECT * FROM " + tableName, null);
if(! Arrays.asList(cursor.getColumnNames()).contains(columnName)) {
db.execSQL(String.format("ALTER TABLE %s ADD COLUMN %s %s", tableName, columnName, dataType));
}
} finally {
if(cursor != null) {
cursor.close();
}
}
}
In my project, I use SQL (SQLite since its android) to save my data.
I encountered some odd problem:
In my application, I have three tabs and in order to know which data belong to which tab I have in my chart a column for each tab. the number in the column (Integer) represent if and how many of that data suppose to be in this tab.
So, when the tab initialized, it reads from the chart, and using the relevant column, it can tell which data needs to be read and how many.
When I retrieve data from the server (the database of the server is not related to the problem at hand), I check if the data is new or that I already have a similar one in my SQL DB. If its new, I add it to the SQL chart and put 1 in the relevant column. If it already exists, it checks if the data in the SQL is updated (it update the data if necessary) and add 1 to the relevant column (the same column it puts 1 in it in case the data is not in the SQL DB).
now here's my problem:
when it reads from the SQL DB to see the number in the column, so it can add 1 to it and then update the chart, it always return 1 regardless the actual number that in the column in that moment (I know its not really 1 because when I read from the DB in the same column from other places in my app it does read the actual number).
Since in other places in my app it doesn't happen I tried to see what is the difference between the places but I did not find anything wrong (the only difference is that I used WritableDatabase instead of just ReadableDatabase in that time but that should not be an issue as far as I know, at least not in such case).
the code where the problem occurs (the problem is with COL_CART):
writable = db.getWritableDatabase();
Cursor cursor = writable.query(
ShopContract.ShopChart.TABLE_NAME,
new String[]{ShopContract.ShopChart.COL_NAME, ShopContract.ShopChart.COL_PRICE, ShopContract.ShopChart.COL_PIC, ShopContract.ShopChart.COL_CART},
ShopContract.ShopChart.COL_PROD_ID + " = '" + product.getProd_id() +"'",
null,
null,
null,
null
);
if(cursor.moveToFirst()){
//check if the data match, if not, replace.
if(!cursor.getString(cursor.getColumnIndex(ShopContract.ShopChart.COL_NAME)).equals(product.getName())){
ContentValues values = new ContentValues();
values.put(ShopContract.ShopChart.COL_NAME,product.getName());
update(values);
}
if(cursor.getDouble(cursor.getColumnIndex(ShopContract.ShopChart.COL_PRICE)) != product.getPrice()){
ContentValues values = new ContentValues();
values.put(ShopContract.ShopChart.COL_PRICE,product.getPrice());
update(values);
}
if(!cursor.getString(cursor.getColumnIndex(ShopContract.ShopChart.COL_PIC)).equals(product.getPicture())){
ContentValues values = new ContentValues();
values.put(ShopContract.ShopChart.COL_PIC,product.getPicture());
update(values);
}
//check how many there are already in cart (already in the cursor) and update it to be ++
Log.d(TAG, "run: the number in col_cart is: " + cursor.getInt(cursor.getColumnIndex(ShopContract.ShopChart.COL_CART)));
int inCart = cursor.getInt(cursor.getColumnIndex(ShopContract.ShopChart.COL_CART)) + 1; // because of the new product we just added
Log.d(TAG, "run: inCart = " + inCart);
ContentValues values = new ContentValues();
values.put(ShopContract.ShopChart.COL_CART,inCart);
Log.d(TAG, "run: change cart");
update(values);
edit:
here is the update method code (the writable is being initialized in the code above):
private SQLiteDatabase writable;
private void update(ContentValues values){
writable.update(ShopContract.ShopChart.TABLE_NAME, values, ShopContract.ShopChart.COL_PROD_ID + " = '" + product.getProd_id() +"'",null);
}
I am trying to create a simple android application which creates a database using SQLiteDatabase (not using SQLiteOpenHelper). So the database creation and the table creation sql executes successfully without any issues/exceptions.
Now the issue is when I reopen the same database, the earlier table created does not exist!!
I am checking the existence of the table using the following code :
Cursor cursor = db.rawQuery("select DISTINCT tbl_name from sqlite_master where tbl_name = '"+ sqlQueryString +"'", null);
if(cursor!=null) {
if(cursor.getCount() == 0) {
//error handling code here
}
cursor.close();
}
Obviously am ending in the if(cursor.getCount() == 0) condition.
I pulled the db file and also checked with a sqlite viewer on the pc, the table created earlier simply does not exist.
So my question is how do I verify that my create table query has created the table properly?
Adding requested information:
Create table ExampleTable ( ROWID integer primary key autoincrement , FIRSTNAME text , LASTNAME text ) ;
Adding the android code the execute the above query:
db.beginTransaction();
db.execSQL(sqlQueryString);
db.endTransaction();
You need to call db.setTransactionSuccessful() before you end the transaction, or else the operation is assumed to have failed and the transaction is rolled back.
Typical transaction usage is:
db.beginTransaction();
try {
// execute DB queries here
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
Credit to njzk2 for first mentioning the solution in a comment.
I am getting inconsistent results between two methods of reading the columns in an Android SQLite database.
First, this is part of a database upgrade routine as per the accepted answer here: Upgrade SQLite database from one version to another?
The technique involves moving the current table away with a temporary name, creating a new table with the new schema, and then copying relevant data from the old table into the new one before deleting the old temporary table.
The particular problem I have is when I remove a column from the schema. So, a particular column exists in the old version of the table, but not the new one.
That answer suggests using a method like this to list the columns in the table:
/**
* Returns a list of the table's column names.
*/
private List<String> getColumns(SQLiteDatabase db, final String tableName) {
List<String> ar = null;
Cursor c = null;
try {
c = db.rawQuery("SELECT * FROM " + tableName + " LIMIT 1", null);
if (c != null) {
ar = new ArrayList<String>(Arrays.asList(c.getColumnNames()));
}
} finally {
if (c != null)
c.close();
}
return ar;
}
That works fine on the old table, before I move it away with a temporary name and replace it. When I run the same query again later, on the newly-created empty table, it still lists the old table schema with the name of the column which no longer exists. It looks as if it's reusing stale cached results for that query.
If I read the columns a different way, using this instead, then it returns the new column list as expected:
private void listColumns(SQLiteDatabase db, final String tableName) {
final String query = "PRAGMA table_info(" + tableName + ");";
Cursor c = db.rawQuery(query, null);
while (c.moveToNext()) {
Log.v("MyApp", "Column: " + c.getString(1));
}
c.close();
}
The complete sequence is:
final String tempTableName = "temp_" + tableName;
table.addToDb(db); // ensure it exists to start with
// get column names of existing table
final List<String> columns = getColumns(db, tableName);
// backup table
db.execSQL("ALTER TABLE " + tableName + " RENAME TO " + tempTableName);
// create new table
table.addToDb(db);
// delete old columns which aren't in the new schema
columns.retainAll(getColumns(db, tableName));
// restore data from old into new table
String columnList = TextUtils.join(",", columns);
db.execSQL(String.format("INSERT INTO %s (%s) SELECT %s from %s", tableName, columnList, columnList,
tempTableName));
// remove backup
db.execSQL(DROP_TABLE + tempTableName);
What's the reason for the different results?
I assume you have done something similar to this:
ALTER TABLE "main"."mytable" RENAME TO "newtable";
CREATE TABLE "main"."mytable" ("key1" text PRIMARY KEY,"key2" text,"key3" text);
INSERT INTO "main"."mytable" SELECT "key1","key2","key3" FROM "main"."newtable";
DROP TABLE "main"."newtable";
If you have, please share the equivalent code, just to rule out any errors with this part.
I never got to the bottom of this. I just ended up using the second method I mentioned, which doesn't exhibit the problem.
I am attempting to make an android application with a pre-populated database. When learning about how to go about this, I came across this article http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/ , which basically takes an existing sqlite database and streams it into the correct location on the android device. The data I had was dealt with in ruby, so I grabbed the sqlite gem, and created a database like so.
db = SQLite3::Database.new( "cards.db" )
db.execute("CREATE TABLE android_metadata (locale TEXT DEFAULT \"en_US\")")
db.execute("INSERT INTO android_metadata VALUES (\"en_US\")")
db.execute("
CREATE TABLE #{##card_table_name} (
_id INTEGER PRIMARY KEY,
name TEXT UNIQUE
)")
cards.each do |card|
begin
db.execute("INSERT INTO #{#card_table_name} (_id, name) VALUES (?, ?)",
card.id, card.name)
rescue => e
puts "#{card.name} (#{card.id})"
puts e
end
end
When I go into the database, both the one made from the ruby script, and the one from using adb and examining the database on the emulator, I get this for the schema.
sqlite> .schema
CREATE TABLE Cards (
_id INTEGER PRIMARY KEY,
name TEXT UNIQUE
);
However, when I pull the data back out in my application, getString can't deal with the name, specifically, this block gets into the exception clause, and prints the name successfully within that block
Cursor cursor = myDataBase.query("Cards", new String[] {"_id", "name"}, null, null, null, null, null, "5");
while (cursor.moveToNext()) {
try {
cards.add(new Card(cursor.getInt(0), new String(cursor.getString(1))));
} catch (Exception e) {
byte[] blob = cursor.getBlob(1);
String translated = new String(blob);
Log.i(MagicApp.TAG, "DB retrival blew up on " + cursor.getInt(0) + ", " + blob + " : " + translated);
e.printStackTrace();
}
}
I can deal with that, but it seems like I shouldn't have to do that. Any one else encountered this, or know what I'm doing wrong?