Correct database migration and initialization logic (#236)
Reworks the `initDB` function to resolve critical failures. - Prevents a "no such table: configs" crash on fresh installs by deferring the compilation of CREATE TABLE statements until their dependencies are met. - Fixes a "duplicate column" error by correcting the initial schema and letting migration logic add new columns. - Replaces the fragile fall-through switch and nested transactions with a robust, sequential upgrade process inside a single atomic transaction.
This commit is contained in:
parent
6703b45350
commit
2666bdad2d
|
|
@ -137,16 +137,15 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final SQLiteStatement createModulesTable = db.compileStatement("CREATE TABLE IF NOT EXISTS modules (" +
|
private static final String CREATE_MODULES_TABLE = "CREATE TABLE IF NOT EXISTS modules (" +
|
||||||
"mid integer PRIMARY KEY AUTOINCREMENT," +
|
"mid integer PRIMARY KEY AUTOINCREMENT," +
|
||||||
"module_pkg_name text NOT NULL UNIQUE," +
|
"module_pkg_name text NOT NULL UNIQUE," +
|
||||||
"apk_path text NOT NULL, " +
|
"apk_path text NOT NULL, " +
|
||||||
"enabled BOOLEAN DEFAULT 0 " +
|
"enabled BOOLEAN DEFAULT 0 " +
|
||||||
"CHECK (enabled IN (0, 1))," +
|
"CHECK (enabled IN (0, 1))" +
|
||||||
"auto_include BOOLEAN DEFAULT 0 " +
|
");";
|
||||||
"CHECK (auto_include IN (0, 1))" +
|
|
||||||
");");
|
private static final String CREATE_SCOPE_TABLE = "CREATE TABLE IF NOT EXISTS scope (" +
|
||||||
private final SQLiteStatement createScopeTable = db.compileStatement("CREATE TABLE IF NOT EXISTS scope (" +
|
|
||||||
"mid integer," +
|
"mid integer," +
|
||||||
"app_pkg_name text NOT NULL," +
|
"app_pkg_name text NOT NULL," +
|
||||||
"user_id integer NOT NULL," +
|
"user_id integer NOT NULL," +
|
||||||
|
|
@ -155,8 +154,9 @@ public class ConfigManager {
|
||||||
" FOREIGN KEY (mid)" +
|
" FOREIGN KEY (mid)" +
|
||||||
" REFERENCES modules (mid)" +
|
" REFERENCES modules (mid)" +
|
||||||
" ON DELETE CASCADE" +
|
" ON DELETE CASCADE" +
|
||||||
");");
|
");";
|
||||||
private final SQLiteStatement createConfigTable = db.compileStatement("CREATE TABLE IF NOT EXISTS configs (" +
|
|
||||||
|
private static final String CREATE_CONFIG_TABLE = "CREATE TABLE IF NOT EXISTS configs (" +
|
||||||
"module_pkg_name text NOT NULL," +
|
"module_pkg_name text NOT NULL," +
|
||||||
"user_id integer NOT NULL," +
|
"user_id integer NOT NULL," +
|
||||||
"`group` text NOT NULL," +
|
"`group` text NOT NULL," +
|
||||||
|
|
@ -167,7 +167,7 @@ public class ConfigManager {
|
||||||
" FOREIGN KEY (module_pkg_name)" +
|
" FOREIGN KEY (module_pkg_name)" +
|
||||||
" REFERENCES modules (module_pkg_name)" +
|
" REFERENCES modules (module_pkg_name)" +
|
||||||
" ON DELETE CASCADE" +
|
" ON DELETE CASCADE" +
|
||||||
");");
|
");";
|
||||||
|
|
||||||
private final Map<ProcessScope, List<Module>> cachedScope = new ConcurrentHashMap<>();
|
private final Map<ProcessScope, List<Module>> cachedScope = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
|
@ -376,74 +376,73 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initDB() {
|
private void initDB() {
|
||||||
try {
|
|
||||||
db.setForeignKeyConstraintsEnabled(true);
|
db.setForeignKeyConstraintsEnabled(true);
|
||||||
switch (db.getVersion()) {
|
int oldVersion = db.getVersion();
|
||||||
case 0:
|
if (oldVersion >= 4) {
|
||||||
executeInTransaction(() -> {
|
// Database is already up to date.
|
||||||
createModulesTable.execute();
|
return;
|
||||||
createScopeTable.execute();
|
}
|
||||||
createConfigTable.execute();
|
|
||||||
|
Log.i(TAG, "Initializing/Upgrading database from version " + oldVersion + " to 4");
|
||||||
|
db.beginTransaction();
|
||||||
|
try {
|
||||||
|
if (oldVersion == 0) {
|
||||||
|
db.execSQL(CREATE_MODULES_TABLE);
|
||||||
|
db.execSQL(CREATE_SCOPE_TABLE);
|
||||||
|
db.execSQL(CREATE_CONFIG_TABLE);
|
||||||
|
|
||||||
var values = new ContentValues();
|
var values = new ContentValues();
|
||||||
values.put("module_pkg_name", "lspd");
|
values.put("module_pkg_name", "lspd");
|
||||||
values.put("apk_path", ConfigFileManager.managerApkPath.toString());
|
values.put("apk_path", ConfigFileManager.managerApkPath.toString());
|
||||||
// dummy module for config
|
|
||||||
db.insertWithOnConflict("modules", null, values, SQLiteDatabase.CONFLICT_IGNORE);
|
db.insertWithOnConflict("modules", null, values, SQLiteDatabase.CONFLICT_IGNORE);
|
||||||
db.setVersion(1);
|
oldVersion = 1;
|
||||||
});
|
}
|
||||||
case 1:
|
if (oldVersion < 2) {
|
||||||
executeInTransaction(() -> {
|
// Upgrade from 1 to 2: Recreate tables to enforce constraints and clean up.
|
||||||
db.compileStatement("DROP INDEX IF EXISTS configs_idx;").execute();
|
db.compileStatement("DROP INDEX IF EXISTS configs_idx;").execute();
|
||||||
db.compileStatement("DROP TABLE IF EXISTS config;").execute();
|
db.compileStatement("DROP TABLE IF EXISTS config;").execute();
|
||||||
db.compileStatement("ALTER TABLE scope RENAME TO old_scope;").execute();
|
db.compileStatement("ALTER TABLE scope RENAME TO old_scope;").execute();
|
||||||
db.compileStatement("ALTER TABLE configs RENAME TO old_configs;").execute();
|
db.compileStatement("ALTER TABLE configs RENAME TO old_configs;").execute();
|
||||||
createConfigTable.execute();
|
|
||||||
createScopeTable.execute();
|
db.execSQL(CREATE_SCOPE_TABLE);
|
||||||
db.compileStatement("CREATE INDEX IF NOT EXISTS configs_idx ON configs (module_pkg_name, user_id);").execute();
|
db.execSQL(CREATE_CONFIG_TABLE);
|
||||||
executeInTransaction(() -> {
|
|
||||||
try {
|
try {
|
||||||
db.compileStatement("INSERT INTO scope SELECT * FROM old_scope;").execute();
|
db.compileStatement("INSERT INTO scope SELECT * FROM old_scope;").execute();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Log.w(TAG, "migrate scope", e);
|
Log.w(TAG, "Failed to migrate scope data", e);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
executeInTransaction(() -> {
|
|
||||||
try {
|
try {
|
||||||
executeInTransaction(() -> db.compileStatement("INSERT INTO configs SELECT * FROM old_configs;").execute());
|
db.compileStatement("INSERT INTO configs SELECT * FROM old_configs;").execute();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Log.w(TAG, "migrate config", e);
|
Log.w(TAG, "Failed to migrate config data", e);
|
||||||
}
|
|
||||||
});
|
|
||||||
db.compileStatement("DROP TABLE old_scope;").execute();
|
|
||||||
db.compileStatement("DROP TABLE old_configs;").execute();
|
|
||||||
db.setVersion(2);
|
|
||||||
});
|
|
||||||
case 2:
|
|
||||||
executeInTransaction(() -> {
|
|
||||||
db.compileStatement("UPDATE scope SET app_pkg_name = 'system' WHERE app_pkg_name = 'android';").execute();
|
|
||||||
db.setVersion(3);
|
|
||||||
});
|
|
||||||
case 3:
|
|
||||||
try {
|
|
||||||
executeInTransaction(() -> {
|
|
||||||
db.compileStatement("ALTER TABLE modules ADD COLUMN auto_include BOOLEAN DEFAULT 0 CHECK (auto_include IN (0, 1));").execute();
|
|
||||||
db.setVersion(4);
|
|
||||||
});
|
|
||||||
} catch (SQLiteException ex) {
|
|
||||||
// Fix wrong init code for new column auto_include
|
|
||||||
if (ex.getMessage().startsWith("duplicate column name: auto_include")) {
|
|
||||||
db.setVersion(4);
|
|
||||||
} else {
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Log.e(TAG, "init db", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db.compileStatement("DROP TABLE old_scope;").execute();
|
||||||
|
db.compileStatement("DROP TABLE old_configs;").execute();
|
||||||
|
db.compileStatement("CREATE INDEX IF NOT EXISTS configs_idx ON configs (module_pkg_name, user_id);").execute();
|
||||||
|
}
|
||||||
|
if (oldVersion < 3) {
|
||||||
|
// Upgrade from 2 to 3: Rename 'android' scope to 'system'.
|
||||||
|
db.compileStatement("UPDATE scope SET app_pkg_name = 'system' WHERE app_pkg_name = 'android';").execute();
|
||||||
|
}
|
||||||
|
if (oldVersion < 4) {
|
||||||
|
// Upgrade from 3 to 4: Add the 'auto_include' column to the modules table.
|
||||||
|
try {
|
||||||
|
db.compileStatement("ALTER TABLE modules ADD COLUMN auto_include BOOLEAN DEFAULT 0 CHECK (auto_include IN (0, 1));").execute();
|
||||||
|
} catch (SQLiteException ex) {
|
||||||
|
// This might happen if the column already exists from a previous buggy run.
|
||||||
|
Log.w(TAG, "Could not add auto_include column, it may already exist.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db.setVersion(4);
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
Log.i(TAG, "Database upgrade to version 4 successful.");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e(TAG, "Failed to initialize or upgrade database, transaction rolled back.", e);
|
||||||
|
} finally {
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ProcessScope> getAssociatedProcesses(Application app) throws RemoteException {
|
private List<ProcessScope> getAssociatedProcesses(Application app) throws RemoteException {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue