Android createFromAsset迁移,但保留特定列

Android createFromAsset迁移,但保留特定列,android,sqlite,android-room,Android,Sqlite,Android Room,我有一个测验应用程序,我有一个数据库,表中有所有问题,每个问题都有一个列已解决,如果答案正确,我会更新该列,因此我可以使用SQLWHERE过滤,只显示未解决的问题。 现在,每隔一段时间我就要纠正问题中的拼写错误,或者可能想添加一些新的,所以 如何在保留已解决的列的同时,将资产中已更正的数据库(questions.db)应用到用户设备上已保存的数据库中 我想到并尝试了以下事情,但没有成功: 目前,我使用自行编制的解决方案替换设备上的数据库(破坏性),但在更新之间保留已解决的信息 将已解决的信息(

我有一个测验应用程序,我有一个数据库,表中有所有问题,每个问题都有一个列
已解决
,如果答案正确,我会更新该列,因此我可以使用SQL
WHERE
过滤,只显示未解决的问题。 现在,每隔一段时间我就要纠正问题中的拼写错误,或者可能想添加一些新的,所以

如何在保留
已解决的
列的同时,将资产中已更正的数据库(questions.db)应用到用户设备上已保存的数据库中

我想到并尝试了以下事情,但没有成功:

  • 目前,我使用自行编制的解决方案替换设备上的数据库(破坏性),但在更新之间保留已解决的信息

  • 将已解决的信息(问题id已解决y/n)放在一个单独的表中,然后
    左连接
    以筛选未解决的问题,这只会使问题变得复杂

  • 有一个额外的数据库来解决问题,似乎没有简单的方法来连接两个房间的数据库

因此,本质上,这可能是Room开发团队的灵感所在,我希望为createFromAsset提供一个适当的迁移策略,能够指定要保留的某些列/表。 感谢您迄今为止的出色工作,我非常喜欢使用Android Jetpack和Room!
另外,我很高兴能找到解决这个问题的方法:)

我相信下面的方法可以满足您的需要

@Database(version = DatabaseConstants.DBVERSION, entities = {Question.class})
public abstract class QuestionDatabase extends RoomDatabase {

    static final String DBNAME = DatabaseConstants.DBNAME;

    abstract QuestionDao questionsDao();

    public static QuestionDatabase getInstance(Context context) {
        copyFromAssets(context,false);
        if (getDBVersion(context,DatabaseConstants.DBNAME) < DatabaseConstants.DBVERSION) {
            copyFromAssets(context,true);
        }
        return Room.databaseBuilder(context,QuestionDatabase.class,DBNAME)
                .addCallback(callback)
                .allowMainThreadQueries()
                .addMigrations(Migration_1_2)
                .build();
    }

    private static RoomDatabase.Callback callback = new Callback() {
        @Override
        public void onCreate(@NonNull SupportSQLiteDatabase db) {
            super.onCreate(db);
        }

        @Override
        public void onOpen(@NonNull SupportSQLiteDatabase db) {
            super.onOpen(db);
        }

        @Override
        public void onDestructiveMigration(@NonNull SupportSQLiteDatabase db) {
            super.onDestructiveMigration(db);
        }
    };

    private static Migration Migration_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
        }
    };

    private static boolean doesDatabaseExist(Context context) {
        if (new File(context.getDatabasePath(DBNAME).getPath()).exists()) return true;
        if (!(new File(context.getDatabasePath(DBNAME).getPath()).getParentFile()).exists()) {
            new File(context.getDatabasePath(DBNAME).getPath()).getParentFile().mkdirs();
        }
        return false;
    }

    private static void copyFromAssets(Context context, boolean replaceExisting) {
        boolean dbExists = doesDatabaseExist(context);
        if (dbExists && !replaceExisting) return;
        //First Copy
        if (!replaceExisting) {
            copyAssetFile(context);
            return;
        }
        //Subsequent Copies

        File originalDBPath = new File(context.getDatabasePath(DBNAME).getPath());
        // Open and close the original DB so as to checkpoint the WAL file
        SQLiteDatabase originalDB = SQLiteDatabase.openDatabase(originalDBPath.getPath(),null,SQLiteDatabase.OPEN_READWRITE);
        originalDB.close();

        //1. Rename original database
        String preservedDBName = "preserved_" + DBNAME;
        File preservedDBPath = new File (originalDBPath.getParentFile().getPath() + preservedDBName);
        (new File(context.getDatabasePath(DBNAME).getPath()))
                .renameTo(preservedDBPath);

        //2. Copy the replacement database from the assets folder
        copyAssetFile(context);

        //3. Open the newly copied database
        SQLiteDatabase copiedDB = SQLiteDatabase.openDatabase(originalDBPath.getPath(),null,SQLiteDatabase.OPEN_READWRITE);
        SQLiteDatabase preservedDB = SQLiteDatabase.openDatabase(preservedDBPath.getPath(),null,SQLiteDatabase.OPEN_READONLY);

        //4. get the orignal data to be preserved
        Cursor csr = preservedDB.query(
                DatabaseConstants.QUESTION_TABLENAME,DatabaseConstants.EXTRACT_COLUMNS,
                null,null,null,null,null
        );

        //5. Apply preserved data to the newly copied data
        copiedDB.beginTransaction();
        ContentValues cv = new ContentValues();
        while (csr.moveToNext()) {
            cv.clear();
            for (String s: DatabaseConstants.PRESERVED_COLUMNS) {
                switch (csr.getType(csr.getColumnIndex(s))) {
                    case Cursor.FIELD_TYPE_INTEGER:
                        cv.put(s,csr.getLong(csr.getColumnIndex(s)));
                        break;
                    case Cursor.FIELD_TYPE_STRING:
                        cv.put(s,csr.getString(csr.getColumnIndex(s)));
                        break;
                    case Cursor.FIELD_TYPE_FLOAT:
                        cv.put(s,csr.getDouble(csr.getColumnIndex(s)));
                        break;
                    case Cursor.FIELD_TYPE_BLOB:
                        cv.put(s,csr.getBlob(csr.getColumnIndex(s)));
                        break;
                }
            }
            copiedDB.update(
                    DatabaseConstants.QUESTION_TABLENAME,
                    cv,
                    DatabaseConstants.QUESTION_ID_COLUMN + "=?",
                    new String[]{
                            String.valueOf(
                                    csr.getLong(
                                            csr.getColumnIndex(DatabaseConstants.QUESTION_ID_COLUMN
                                            )
                                    )
                            )
                    }
                    );
        }
        copiedDB.setTransactionSuccessful();
        copiedDB.endTransaction();
        csr.close();
        //6. Cleanup
        copiedDB.close();
        preservedDB.close();
        preservedDBPath.delete();
    }

    private static void copyAssetFile(Context context) {
        int buffer_size = 8192;
        byte[] buffer = new byte[buffer_size];
        int bytes_read = 0;
        try {
            InputStream fis = context.getAssets().open(DBNAME);
            OutputStream os = new FileOutputStream(new File(context.getDatabasePath(DBNAME).getPath()));
            while ((bytes_read = fis.read(buffer)) > 0) {
                os.write(buffer,0,bytes_read);
            }
            os.flush();
            os.close();
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("Unable to copy from assets");
        }
    }

    private static int getDBVersion(Context context, String databaseName) {
        SQLiteDatabase db = SQLiteDatabase.openDatabase( context.getDatabasePath(databaseName).getPath(),null,SQLiteDatabase.OPEN_READONLY);
        int rv = db.getVersion();
        db.close();
        return rv;
    }
}
因此,可以添加要保留的其他列(根据copyFromAssets方法中的5.的任何类型)。 还可以指定要提取的列,在上述情况下,ID列唯一地标识问题,以便在WHERE子句使用的已解决列之外提取问题

测试 已对上述各项进行了测试,以:-

起初的
  • 当DBVERSION为1时,从资产复制数据库的第一个版本

    • 请注意,本文件最初包含3个问题,根据

    • 代码的一部分(在调用活动中,检查所有已解决的值是否为0,如果是,则使用id为2更改问题的已解决状态)
  • 不复制数据库,但在子集运行中,当DBVERSION为1时,使用现有数据库。
    • 第二个问题仍未解决
新的
  • 将原始资源从重命名为以原始资源为前缀后,按如下方式编辑数据库,并将其复制到资源文件中:-

  • 在不更改DBVERSION(仍然为1)的情况下运行,并且原始数据库仍在使用中

  • 将DBVERSION更改为2后,运行将复制更改的资产文件并恢复/保留已解决状态

  • 对于后续运行,新数据的已解决状态保持不变

用于测试调用活动,包括:-

public class MainActivity extends AppCompatActivity {

    QuestionDatabase questionDatabase;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        questionDatabase = QuestionDatabase.getInstance(this);
        int solvedCount = 0;
        for (Question q: questionDatabase.questionsDao().getAll()) {
            if (q.isSolved()) solvedCount++;
            q.logQuestion();
        }
        if (solvedCount == 0) {
            questionDatabase.questionsDao().setSolved(true,2);
        }
        for (Question q: questionDatabase.questionsDao().getAll()) {
            q.logQuestion();
        }
    }
} 
对于每次运行,它将所有问题输出到日志两次。在第一个问题之后,如果没有已解决的问题,它将使用id 2解决该问题

上次运行的输出为:-

附加-改进版 这是一个经批准的版本,适用于多个表和列。为了满足表的需要,添加了一个类TablePreserve,该类允许表、要保留的列、要提取的列以及where子句的列。根据:-

public class TablePreserve {
    String tableName;
    String[] preserveColumns;
    String[] extractColumns;
    String[] whereColumns;

    public TablePreserve(String table, String[] preserveColumns, String[] extractColumns, String[] whereColumns) {
        this.tableName = table;
        this.preserveColumns = preserveColumns;
        this.extractColumns = extractColumns;
        this.whereColumns = whereColumns;
    }

    public String getTableName() {
        return tableName;
    }

    public String[] getPreserveColumns() {
        return preserveColumns;
    }

    public String[] getExtractColumns() {
        return extractColumns;
    }

    public String[] getWhereColumns() {
        return whereColumns;
    }
}
您创建一个TablePreserve对象数组,这些对象在其中循环

public final class DatabaseConstants {
    public static final String DBNAME = "question.db";
    public static final int DBVERSION = 2;
    public static final String QUESTION_TABLENAME = "question";
    public static final String QUESTION_ID_COLUMN = "id";
    public static final String QUESTION_QUESTION_COLUMN = QUESTION_TABLENAME;
    public static final String QUESTION_ANSWER1_COLUMN = "answer1";
    public static final String QUESTION_ANSWER2_COLUMN = "answer2";
    public static final String QUESTION_ANSWER3_COLUMN = "answer3";
    public static final String QUESTION_CORRECTANSWER_COLUMN = "correctAsnwer";
    public static final String QUESTION_SOLVED_COLUMN = "solved";

    public static final TablePreserve questionTablePreserve = new TablePreserve(
            QUESTION_TABLENAME,
            new String[]{QUESTION_SOLVED_COLUMN},
            new String[]{QUESTION_ID_COLUMN,QUESTION_SOLVED_COLUMN},
            new String[]{QUESTION_ID_COLUMN}
    );

    public static final TablePreserve[] TABLE_PRESERVELIST = new TablePreserve[] {
            questionTablePreserve
    };
}
然后问题数据库变成:-

@Database(version = DatabaseConstants.DBVERSION, entities = {Question.class})
public abstract class QuestionDatabase extends RoomDatabase {

    static final String DBNAME = DatabaseConstants.DBNAME;

    abstract QuestionDao questionsDao();

    public static QuestionDatabase getInstance(Context context) {
        if (!doesDatabaseExist(context)) {
            copyFromAssets(context,false);
        }
        if (getDBVersion(context, DatabaseConstants.DBNAME) < DatabaseConstants.DBVERSION) {
            copyFromAssets(context, true);
        }

        return Room.databaseBuilder(context,QuestionDatabase.class,DBNAME)
                .addCallback(callback)
                .allowMainThreadQueries()
                .addMigrations(Migration_1_2)
                .build();
    }

    private static RoomDatabase.Callback callback = new Callback() {
        @Override
        public void onCreate(@NonNull SupportSQLiteDatabase db) {
            super.onCreate(db);
        }

        @Override
        public void onOpen(@NonNull SupportSQLiteDatabase db) {
            super.onOpen(db);
        }

        @Override
        public void onDestructiveMigration(@NonNull SupportSQLiteDatabase db) {
            super.onDestructiveMigration(db);
        }
    };

    private static Migration Migration_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
        }
    };

    private static boolean doesDatabaseExist(Context context) {
        if (new File(context.getDatabasePath(DBNAME).getPath()).exists()) return true;
        if (!(new File(context.getDatabasePath(DBNAME).getPath()).getParentFile()).exists()) {
            new File(context.getDatabasePath(DBNAME).getPath()).getParentFile().mkdirs();
        }
        return false;
    }

    private static void copyFromAssets(Context context, boolean replaceExisting) {
        boolean dbExists = doesDatabaseExist(context);
        if (dbExists && !replaceExisting) return;
        //First Copy
        if (!replaceExisting) {
            copyAssetFile(context);
            setDBVersion(context,DBNAME,DatabaseConstants.DBVERSION);
            return;
        }
        //Subsequent Copies

        File originalDBPath = new File(context.getDatabasePath(DBNAME).getPath());
        // Open and close the original DB so as to checkpoint the WAL file
        SQLiteDatabase originalDB = SQLiteDatabase.openDatabase(originalDBPath.getPath(),null,SQLiteDatabase.OPEN_READWRITE);
        originalDB.close();

        //1. Rename original database
        String preservedDBName = "preserved_" + DBNAME;
        File preservedDBPath = new File (originalDBPath.getParentFile().getPath() + File.separator + preservedDBName);
        (new File(context.getDatabasePath(DBNAME).getPath()))
                .renameTo(preservedDBPath);

        //2. Copy the replacement database from the assets folder
        copyAssetFile(context);

        //3. Open the newly copied database
        SQLiteDatabase copiedDB = SQLiteDatabase.openDatabase(originalDBPath.getPath(),null,SQLiteDatabase.OPEN_READWRITE);
        SQLiteDatabase preservedDB = SQLiteDatabase.openDatabase(preservedDBPath.getPath(),null,SQLiteDatabase.OPEN_READONLY);

        //4. Apply preserved data to the newly copied data
        copiedDB.beginTransaction();
        for (TablePreserve tp: DatabaseConstants.TABLE_PRESERVELIST) {
            preserveTableColumns(
                    preservedDB,
                    copiedDB,
                    tp.getTableName(),
                    tp.getPreserveColumns(),
                    tp.getExtractColumns(),
                    tp.getWhereColumns(),
                    true
            );
        }
        copiedDB.setVersion(DatabaseConstants.DBVERSION);
        copiedDB.setTransactionSuccessful();
        copiedDB.endTransaction();
        //5. Cleanup
        copiedDB.close();
        preservedDB.close();
        preservedDBPath.delete();
    }

    private static void copyAssetFile(Context context) {
        int buffer_size = 8192;
        byte[] buffer = new byte[buffer_size];
        int bytes_read = 0;
        try {
            InputStream fis = context.getAssets().open(DBNAME);
            OutputStream os = new FileOutputStream(new File(context.getDatabasePath(DBNAME).getPath()));
            while ((bytes_read = fis.read(buffer)) > 0) {
                os.write(buffer,0,bytes_read);
            }
            os.flush();
            os.close();
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("Unable to copy from assets");
        }
    }

    private static int getDBVersion(Context context, String databaseName) {
        SQLiteDatabase db = SQLiteDatabase.openDatabase( context.getDatabasePath(databaseName).getPath(),null,SQLiteDatabase.OPEN_READONLY);
        int rv = db.getVersion();
        db.close();
        return rv;
    }
    private static void setDBVersion(Context context, String databaseName, int version) {
        SQLiteDatabase db = SQLiteDatabase.openDatabase( context.getDatabasePath(databaseName).getPath(),null,SQLiteDatabase.OPEN_READWRITE);
        db.setVersion(version);
        db.close();
    }

    private static boolean preserveTableColumns(
            SQLiteDatabase originalDatabase,
            SQLiteDatabase newDatabase,
            String tableName,
            String[] columnsToPreserve,
            String[] columnsToExtract,
            String[] whereClauseColumns,
            boolean failWithException) {

        StringBuilder sb = new StringBuilder();
        Cursor csr = originalDatabase.query("sqlite_master",new String[]{"name"},"name=? AND type=?",new String[]{tableName,"table"},null,null,null);
        if (!csr.moveToFirst()) {
            sb.append("\n\tTable ").append(tableName).append(" not found in database ").append(originalDatabase.getPath());
        }
        csr = newDatabase.query("sqlite_master",new String[]{"name"},"name=? AND type=?",new String[]{tableName,"table"},null,null,null);
        if (!csr.moveToFirst()) {
            sb.append("\n\tTable ").append(tableName).append(" not found in database ").append(originalDatabase.getPath());
        }
        if (sb.length() > 0) {
            if (failWithException) {
                throw new RuntimeException("Both databases are required to have a table named " + tableName + sb.toString());
            }
            return false;
        }
        for (String pc: columnsToPreserve) {
            boolean preserveColumnInExtractedColumn = false;
            for (String ec: columnsToExtract) {
                if (pc.equals(ec)) preserveColumnInExtractedColumn = true;
            }
            if (!preserveColumnInExtractedColumn) {
                if (failWithException) {
                    StringBuilder sbpc = new StringBuilder().append("Column in Columns to Preserve not found in Columns to Extract. Cannot continuue." +
                            "\n\tColumns to Preserve are :-");

                    }
                throw new RuntimeException("Column " + pc + " is not int the Columns to Extract.");
            }
            return false;
        }
        sb = new StringBuilder();
        for (String c: whereClauseColumns) {
            sb.append(c).append("=? ");
        }
        String[] whereargs = new String[whereClauseColumns.length];
        csr = originalDatabase.query(tableName,columnsToExtract,sb.toString(),whereClauseColumns,null,null,null);
        ContentValues cv = new ContentValues();
        while (csr.moveToNext()) {
            cv.clear();
            for (String pc: columnsToPreserve) {
                switch (csr.getType(csr.getColumnIndex(pc))) {
                    case Cursor.FIELD_TYPE_INTEGER:
                        cv.put(pc,csr.getLong(csr.getColumnIndex(pc)));
                        break;
                    case Cursor.FIELD_TYPE_STRING:
                        cv.put(pc,csr.getString(csr.getColumnIndex(pc)));
                        break;
                    case Cursor.FIELD_TYPE_FLOAT:
                        cv.put(pc,csr.getDouble(csr.getColumnIndex(pc)));
                        break;
                    case Cursor.FIELD_TYPE_BLOB:
                        cv.put(pc,csr.getBlob(csr.getColumnIndex(pc)));
                }
            }
            int waix = 0;
            for (String wa: whereClauseColumns) {
                whereargs[waix] = csr.getString(csr.getColumnIndex(wa));
            }
            newDatabase.update(tableName,cv,sb.toString(),whereargs);
        }
        csr.close();
        return true;
    }
}
@Database(version=DatabaseConstants.DBVERSION,entities={Question.class})
公共抽象类QuestionDatabase扩展了RoomDatabase{
静态最终字符串DBNAME=DatabaseConstants.DBNAME;
抽象问题dao questionsDao();
公共静态问题数据库getInstance(上下文){
如果(!doesDatabaseExist(上下文)){
copyFromAssets(上下文,false);
}
if(getDBVersion(context,DatabaseConstants.DBNAME)public final class DatabaseConstants {
    public static final String DBNAME = "question.db";
    public static final int DBVERSION = 2;
    public static final String QUESTION_TABLENAME = "question";
    public static final String QUESTION_ID_COLUMN = "id";
    public static final String QUESTION_QUESTION_COLUMN = QUESTION_TABLENAME;
    public static final String QUESTION_ANSWER1_COLUMN = "answer1";
    public static final String QUESTION_ANSWER2_COLUMN = "answer2";
    public static final String QUESTION_ANSWER3_COLUMN = "answer3";
    public static final String QUESTION_CORRECTANSWER_COLUMN = "correctAsnwer";
    public static final String QUESTION_SOLVED_COLUMN = "solved";

    public static final TablePreserve questionTablePreserve = new TablePreserve(
            QUESTION_TABLENAME,
            new String[]{QUESTION_SOLVED_COLUMN},
            new String[]{QUESTION_ID_COLUMN,QUESTION_SOLVED_COLUMN},
            new String[]{QUESTION_ID_COLUMN}
    );

    public static final TablePreserve[] TABLE_PRESERVELIST = new TablePreserve[] {
            questionTablePreserve
    };
}
@Database(version = DatabaseConstants.DBVERSION, entities = {Question.class})
public abstract class QuestionDatabase extends RoomDatabase {

    static final String DBNAME = DatabaseConstants.DBNAME;

    abstract QuestionDao questionsDao();

    public static QuestionDatabase getInstance(Context context) {
        if (!doesDatabaseExist(context)) {
            copyFromAssets(context,false);
        }
        if (getDBVersion(context, DatabaseConstants.DBNAME) < DatabaseConstants.DBVERSION) {
            copyFromAssets(context, true);
        }

        return Room.databaseBuilder(context,QuestionDatabase.class,DBNAME)
                .addCallback(callback)
                .allowMainThreadQueries()
                .addMigrations(Migration_1_2)
                .build();
    }

    private static RoomDatabase.Callback callback = new Callback() {
        @Override
        public void onCreate(@NonNull SupportSQLiteDatabase db) {
            super.onCreate(db);
        }

        @Override
        public void onOpen(@NonNull SupportSQLiteDatabase db) {
            super.onOpen(db);
        }

        @Override
        public void onDestructiveMigration(@NonNull SupportSQLiteDatabase db) {
            super.onDestructiveMigration(db);
        }
    };

    private static Migration Migration_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
        }
    };

    private static boolean doesDatabaseExist(Context context) {
        if (new File(context.getDatabasePath(DBNAME).getPath()).exists()) return true;
        if (!(new File(context.getDatabasePath(DBNAME).getPath()).getParentFile()).exists()) {
            new File(context.getDatabasePath(DBNAME).getPath()).getParentFile().mkdirs();
        }
        return false;
    }

    private static void copyFromAssets(Context context, boolean replaceExisting) {
        boolean dbExists = doesDatabaseExist(context);
        if (dbExists && !replaceExisting) return;
        //First Copy
        if (!replaceExisting) {
            copyAssetFile(context);
            setDBVersion(context,DBNAME,DatabaseConstants.DBVERSION);
            return;
        }
        //Subsequent Copies

        File originalDBPath = new File(context.getDatabasePath(DBNAME).getPath());
        // Open and close the original DB so as to checkpoint the WAL file
        SQLiteDatabase originalDB = SQLiteDatabase.openDatabase(originalDBPath.getPath(),null,SQLiteDatabase.OPEN_READWRITE);
        originalDB.close();

        //1. Rename original database
        String preservedDBName = "preserved_" + DBNAME;
        File preservedDBPath = new File (originalDBPath.getParentFile().getPath() + File.separator + preservedDBName);
        (new File(context.getDatabasePath(DBNAME).getPath()))
                .renameTo(preservedDBPath);

        //2. Copy the replacement database from the assets folder
        copyAssetFile(context);

        //3. Open the newly copied database
        SQLiteDatabase copiedDB = SQLiteDatabase.openDatabase(originalDBPath.getPath(),null,SQLiteDatabase.OPEN_READWRITE);
        SQLiteDatabase preservedDB = SQLiteDatabase.openDatabase(preservedDBPath.getPath(),null,SQLiteDatabase.OPEN_READONLY);

        //4. Apply preserved data to the newly copied data
        copiedDB.beginTransaction();
        for (TablePreserve tp: DatabaseConstants.TABLE_PRESERVELIST) {
            preserveTableColumns(
                    preservedDB,
                    copiedDB,
                    tp.getTableName(),
                    tp.getPreserveColumns(),
                    tp.getExtractColumns(),
                    tp.getWhereColumns(),
                    true
            );
        }
        copiedDB.setVersion(DatabaseConstants.DBVERSION);
        copiedDB.setTransactionSuccessful();
        copiedDB.endTransaction();
        //5. Cleanup
        copiedDB.close();
        preservedDB.close();
        preservedDBPath.delete();
    }

    private static void copyAssetFile(Context context) {
        int buffer_size = 8192;
        byte[] buffer = new byte[buffer_size];
        int bytes_read = 0;
        try {
            InputStream fis = context.getAssets().open(DBNAME);
            OutputStream os = new FileOutputStream(new File(context.getDatabasePath(DBNAME).getPath()));
            while ((bytes_read = fis.read(buffer)) > 0) {
                os.write(buffer,0,bytes_read);
            }
            os.flush();
            os.close();
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("Unable to copy from assets");
        }
    }

    private static int getDBVersion(Context context, String databaseName) {
        SQLiteDatabase db = SQLiteDatabase.openDatabase( context.getDatabasePath(databaseName).getPath(),null,SQLiteDatabase.OPEN_READONLY);
        int rv = db.getVersion();
        db.close();
        return rv;
    }
    private static void setDBVersion(Context context, String databaseName, int version) {
        SQLiteDatabase db = SQLiteDatabase.openDatabase( context.getDatabasePath(databaseName).getPath(),null,SQLiteDatabase.OPEN_READWRITE);
        db.setVersion(version);
        db.close();
    }

    private static boolean preserveTableColumns(
            SQLiteDatabase originalDatabase,
            SQLiteDatabase newDatabase,
            String tableName,
            String[] columnsToPreserve,
            String[] columnsToExtract,
            String[] whereClauseColumns,
            boolean failWithException) {

        StringBuilder sb = new StringBuilder();
        Cursor csr = originalDatabase.query("sqlite_master",new String[]{"name"},"name=? AND type=?",new String[]{tableName,"table"},null,null,null);
        if (!csr.moveToFirst()) {
            sb.append("\n\tTable ").append(tableName).append(" not found in database ").append(originalDatabase.getPath());
        }
        csr = newDatabase.query("sqlite_master",new String[]{"name"},"name=? AND type=?",new String[]{tableName,"table"},null,null,null);
        if (!csr.moveToFirst()) {
            sb.append("\n\tTable ").append(tableName).append(" not found in database ").append(originalDatabase.getPath());
        }
        if (sb.length() > 0) {
            if (failWithException) {
                throw new RuntimeException("Both databases are required to have a table named " + tableName + sb.toString());
            }
            return false;
        }
        for (String pc: columnsToPreserve) {
            boolean preserveColumnInExtractedColumn = false;
            for (String ec: columnsToExtract) {
                if (pc.equals(ec)) preserveColumnInExtractedColumn = true;
            }
            if (!preserveColumnInExtractedColumn) {
                if (failWithException) {
                    StringBuilder sbpc = new StringBuilder().append("Column in Columns to Preserve not found in Columns to Extract. Cannot continuue." +
                            "\n\tColumns to Preserve are :-");

                    }
                throw new RuntimeException("Column " + pc + " is not int the Columns to Extract.");
            }
            return false;
        }
        sb = new StringBuilder();
        for (String c: whereClauseColumns) {
            sb.append(c).append("=? ");
        }
        String[] whereargs = new String[whereClauseColumns.length];
        csr = originalDatabase.query(tableName,columnsToExtract,sb.toString(),whereClauseColumns,null,null,null);
        ContentValues cv = new ContentValues();
        while (csr.moveToNext()) {
            cv.clear();
            for (String pc: columnsToPreserve) {
                switch (csr.getType(csr.getColumnIndex(pc))) {
                    case Cursor.FIELD_TYPE_INTEGER:
                        cv.put(pc,csr.getLong(csr.getColumnIndex(pc)));
                        break;
                    case Cursor.FIELD_TYPE_STRING:
                        cv.put(pc,csr.getString(csr.getColumnIndex(pc)));
                        break;
                    case Cursor.FIELD_TYPE_FLOAT:
                        cv.put(pc,csr.getDouble(csr.getColumnIndex(pc)));
                        break;
                    case Cursor.FIELD_TYPE_BLOB:
                        cv.put(pc,csr.getBlob(csr.getColumnIndex(pc)));
                }
            }
            int waix = 0;
            for (String wa: whereClauseColumns) {
                whereargs[waix] = csr.getString(csr.getColumnIndex(wa));
            }
            newDatabase.update(tableName,cv,sb.toString(),whereargs);
        }
        csr.close();
        return true;
    }
}