1

I'm trying to migrate a Java project to android with SQLite, when instantiate a new MapleDBHelper class, I got following exception:

attempt to write a readonly database (code 1032 SQLITE_READONLY_DBMOVED)

I searched on the Internet but got no clue, what cloud be the reason ?

Code:

try (MapleDBHelper mapledb = MapleDBHelper.getInstance(this.context);
     SQLiteDatabase db = mapledb.getWritableDatabase()) {
     // Code Modifying DB
} catch (SQLiteException sqle) {
    Log.e("Failed to run all startup-bound database tasks", sqle.toString());
    throw new IllegalStateException(sqle);
}

DBHelper Class:

public class MapleDBHelper extends SQLiteOpenHelper {
    public Context context;
    private static final String DATABASE_NAME = "cosmic";
    private static final int DATABASE_VERSION = 1;
    private static MapleDBHelper sInstance;
    private MapleDBHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        this.context = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        File databaseFile = context.getDatabasePath(DATABASE_NAME);
        if (databaseFile.exists()) {
            // Delete the existing database file
            SQLiteDatabase.deleteDatabase(databaseFile);
        }

        try {
            AssetManager assetManager = context.getAssets();
            InputStream db_database = assetManager.open("sql/1_db_database.sql");
            InputStream db_drops = assetManager.open("sql/2_db_drops.sql");
            InputStream db_shopupdate = assetManager.open("sql/3_db_shopupdate.sql");
            InputStream db_admin = assetManager.open("sql/4_db-admin.sql");
            db.execSQL(convertInputStreamToString(db_database));
            db.execSQL(convertInputStreamToString(db_drops));
            db.execSQL(convertInputStreamToString(db_shopupdate));
            db.execSQL(convertInputStreamToString(db_admin));

            db_database.close();
            db_drops.close();
            db_shopupdate.close();
            db_admin.close();
        } catch (FileNotFoundException e) {
            throw new RuntimeException("Could not read database file : " + e.getMessage());
        } catch (IOException e) {
            throw new RuntimeException("Could not successfully parse database file " + e.getMessage());
        }
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }

    public static synchronized MapleDBHelper getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new MapleDBHelper(context.getApplicationContext());
        }
        return sInstance;
    }
}
Stacktrace:
0 = {StackTraceElement@17117} "android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(Native Method)"
1 = {StackTraceElement@17118} "android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:890)"
2 = {StackTraceElement@17119} "android.database.sqlite.SQLiteSession.executeForChangedRowCount(SQLiteSession.java:756)"
3 = {StackTraceElement@17120} "android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:66)"
4 = {StackTraceElement@17121} "android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1920)"
5 = {StackTraceElement@17122} "android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1841)"
6 = {StackTraceElement@17123} "android.database.sqlite.SQLiteDatabase.setVersion(SQLiteDatabase.java:1102)"
7 = {StackTraceElement@17124} "android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:419)"
8 = {StackTraceElement@17125} "android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316)"
9 = {StackTraceElement@17126} "net.server.Server.init(Server.java:897)"
10 = {StackTraceElement@17127} "net.server.Server.main(Server.java:1017)"
11 = {StackTraceElement@17128} "com.mapleserver.MainActivity.startMapleServer(MainActivity.kt:34)"
12 = {StackTraceElement@17129} "com.mapleserver.MainActivity.onCreate(MainActivity.kt:28)"

1 Answer 1

1

You are deleting the database, you should not.

When the onCreate method is called(invoked), the actual database (empty apart from sqlite_master and android_metadata) has been created. So it will exist, so you are always deleting it and not creating it (which if you did would not be pointed to anyway as it would be another file handle).

If you look at onCreate's parameters, it has the database passed to it.

Remove the block of code:-

    if (databaseFile.exists()) {
        // Delete the existing database file
        SQLiteDatabase.deleteDatabase(databaseFile);
    }
  • The database will always exist
    • it will have no tables other than sqlite_master (the schema which will only have android_metedata) and android_metadata (this being an API generated table that stores the locale).
  • Deleting the database or the file (same thing really) will result in issues as the file handle (terminology may not be correct but means whatever points to the file) will no longer be valid.

Using your code, with the convertInputStreamToString method included in the Helper, and the suggested commenting out the of delete as per:-

public class MapleDBHelper extends SQLiteOpenHelper {
    public Context context;
    private static final String DATABASE_NAME = "cosmic";
    private static final int DATABASE_VERSION = 1;
    private static MapleDBHelper sInstance;
    private MapleDBHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        this.context = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        File databaseFile = context.getDatabasePath(DATABASE_NAME);
        if (databaseFile.exists()) {
            // Delete the existing database file
            //SQLiteDatabase.deleteDatabase(databaseFile);
        }

        try {
            AssetManager assetManager = context.getAssets();
            InputStream db_database = assetManager.open("sql/1_db_database.sql");
            InputStream db_drops = assetManager.open("sql/2_db_drops.sql");
            InputStream db_shopupdate = assetManager.open("sql/3_db_shopupdate.sql");
            InputStream db_admin = assetManager.open("sql/4_db-admin.sql");
            db.execSQL(convertInputStreamToString(db_database));
            db.execSQL(convertInputStreamToString(db_drops));
            db.execSQL(convertInputStreamToString(db_shopupdate));
            db.execSQL(convertInputStreamToString(db_admin));

            db_database.close();
            db_drops.close();
            db_shopupdate.close();
            db_admin.close();
        } catch (FileNotFoundException e) {
            throw new RuntimeException("Could not read database file : " + e.getMessage());
        } catch (IOException e) {
            throw new RuntimeException("Could not successfully parse database file " + e.getMessage());
        }
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }

    public static synchronized MapleDBHelper getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new MapleDBHelper(context.getApplicationContext());
        }
        return sInstance;
    }
    private String convertInputStreamToString(InputStream is) {
        String str = "";
        StringBuffer buf = new StringBuffer();
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            if (is != null) {
                while((str = br.readLine())!= null) {
                    buf.append(str).append("\n");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (Throwable ignore){}
        }
        return buf.toString();
    }
}

And 4 asset files each with a simple table create (all the same except for table1,2,3 and 4) e.g. CREATE TABLE IF NOT EXISTS table1 (id INTEGER PRIMARY KEY, c1 TEXT);

enter image description here

And the following Activity code:-

public class MainActivity extends AppCompatActivity {
    MapleDBHelper _MDB;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        _MDB = MapleDBHelper.getInstance(this);
        Cursor csr = _MDB.getWritableDatabase().query("sqlite_master",null,null,null,null,null,null,null);
        DatabaseUtils.dumpCursor(csr);
        csr.close();
    }
}

When run then the log shows the schema (sqlite_master) as:-

2023-10-09 19:02:15.585 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@d598771
2023-10-09 19:02:15.586 I/System.out: 0 {
2023-10-09 19:02:15.586 I/System.out:    type=table
2023-10-09 19:02:15.586 I/System.out:    name=android_metadata
2023-10-09 19:02:15.586 I/System.out:    tbl_name=android_metadata
2023-10-09 19:02:15.586 I/System.out:    rootpage=3
2023-10-09 19:02:15.586 I/System.out:    sql=CREATE TABLE android_metadata (locale TEXT)
2023-10-09 19:02:15.586 I/System.out: }
2023-10-09 19:02:15.586 I/System.out: 1 {
2023-10-09 19:02:15.586 I/System.out:    type=table
2023-10-09 19:02:15.586 I/System.out:    name=table1
2023-10-09 19:02:15.586 I/System.out:    tbl_name=table1
2023-10-09 19:02:15.587 I/System.out:    rootpage=4
2023-10-09 19:02:15.587 I/System.out:    sql=CREATE TABLE table1 (id INTEGER PRIMARY KEY, c1 TEXT)
2023-10-09 19:02:15.587 I/System.out: }
2023-10-09 19:02:15.587 I/System.out: 2 {
2023-10-09 19:02:15.587 I/System.out:    type=table
2023-10-09 19:02:15.587 I/System.out:    name=table2
2023-10-09 19:02:15.587 I/System.out:    tbl_name=table2
2023-10-09 19:02:15.587 I/System.out:    rootpage=5
2023-10-09 19:02:15.587 I/System.out:    sql=CREATE TABLE table2 (id INTEGER PRIMARY KEY, c1 TEXT)
2023-10-09 19:02:15.587 I/System.out: }
2023-10-09 19:02:15.587 I/System.out: 3 {
2023-10-09 19:02:15.587 I/System.out:    type=table
2023-10-09 19:02:15.588 I/System.out:    name=table3
2023-10-09 19:02:15.588 I/System.out:    tbl_name=table3
2023-10-09 19:02:15.588 I/System.out:    rootpage=6
2023-10-09 19:02:15.588 I/System.out:    sql=CREATE TABLE table3 (id INTEGER PRIMARY KEY, c1 TEXT)
2023-10-09 19:02:15.588 I/System.out: }
2023-10-09 19:02:15.588 I/System.out: 4 {
2023-10-09 19:02:15.588 I/System.out:    type=table
2023-10-09 19:02:15.588 I/System.out:    name=table4
2023-10-09 19:02:15.588 I/System.out:    tbl_name=table4
2023-10-09 19:02:15.588 I/System.out:    rootpage=7
2023-10-09 19:02:15.588 I/System.out:    sql=CREATE TABLE table4 (id INTEGER PRIMARY KEY, c1 TEXT)
2023-10-09 19:02:15.589 I/System.out: }
2023-10-09 19:02:15.589 I/System.out: <<<<<

i.e. android_metadata table and the 4 tables as created from the 4 assets.

And App Inspection showing:-

enter image description here

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the response, how do you connect to the sqlite database when debugging ?
@k0alaXing If the database hasn't been closed and the App is running, then [App Inspection] in Android Studio.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.