1a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar/* 2a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * Copyright (C) 2017 The Android Open Source Project 3a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * 4a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 5a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * you may not use this file except in compliance with the License. 6a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * You may obtain a copy of the License at 7a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * 8a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 9a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * 10a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * Unless required by applicable law or agreed to in writing, software 11a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 12a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * See the License for the specific language governing permissions and 14a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * limitations under the License. 15a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar */ 16a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 17ba069d50913c3fb250bb60ec310439db36895337Alan Viverettepackage androidx.room; 18a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 19a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyarimport android.database.Cursor; 20ba069d50913c3fb250bb60ec310439db36895337Alan Viverette 21ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.annotation.NonNull; 22ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.annotation.Nullable; 23ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.annotation.RestrictTo; 24ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.migration.Migration; 25ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.sqlite.db.SimpleSQLiteQuery; 26ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.sqlite.db.SupportSQLiteDatabase; 27ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.sqlite.db.SupportSQLiteOpenHelper; 28a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 29a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyarimport java.util.List; 30a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 31a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar/** 32a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * An open helper that holds a reference to the configuration until the database is opened. 33a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * 34a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * @hide 35a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar */ 36a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar@SuppressWarnings("unused") 37a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 38a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyarpublic class RoomOpenHelper extends SupportSQLiteOpenHelper.Callback { 39a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar @Nullable 40a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar private DatabaseConfiguration mConfiguration; 41a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar @NonNull 42a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar private final Delegate mDelegate; 43a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar @NonNull 44a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar private final String mIdentityHash; 45e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar /** 46e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar * Room v1 had a bug where the hash was not consistent if fields are reordered. 47e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar * The new has fixes it but we still need to accept the legacy hash. 48e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar */ 49e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar @NonNull // b/64290754 50e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar private final String mLegacyHash; 51a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 52a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar public RoomOpenHelper(@NonNull DatabaseConfiguration configuration, @NonNull Delegate delegate, 53e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar @NonNull String identityHash, @NonNull String legacyHash) { 542b4a201ee53be9d5e3995e62c76f83c07a1ddfddYigit Boyar super(delegate.version); 55a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar mConfiguration = configuration; 56a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar mDelegate = delegate; 57a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar mIdentityHash = identityHash; 58e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar mLegacyHash = legacyHash; 59a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 60a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 610bf1c211603a37b9cbc2431999d58a8ea085ff5aSergey Vasilinets public RoomOpenHelper(@NonNull DatabaseConfiguration configuration, @NonNull Delegate delegate, 620bf1c211603a37b9cbc2431999d58a8ea085ff5aSergey Vasilinets @NonNull String legacyHash) { 630bf1c211603a37b9cbc2431999d58a8ea085ff5aSergey Vasilinets this(configuration, delegate, null, legacyHash); 640bf1c211603a37b9cbc2431999d58a8ea085ff5aSergey Vasilinets } 650bf1c211603a37b9cbc2431999d58a8ea085ff5aSergey Vasilinets 66a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar @Override 67a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar public void onConfigure(SupportSQLiteDatabase db) { 68a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar super.onConfigure(db); 69a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 70a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 71a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar @Override 72a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar public void onCreate(SupportSQLiteDatabase db) { 73a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar updateIdentity(db); 74a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar mDelegate.createAllTables(db); 75cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki mDelegate.onCreate(db); 76a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 77a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 78a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar @Override 79a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { 80a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar boolean migrated = false; 81a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar if (mConfiguration != null) { 82a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar List<Migration> migrations = mConfiguration.migrationContainer.findMigrationPath( 83a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar oldVersion, newVersion); 84a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar if (migrations != null) { 85a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar for (Migration migration : migrations) { 86a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar migration.migrate(db); 87a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 883a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar mDelegate.validateMigration(db); 893a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar updateIdentity(db); 90a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar migrated = true; 91a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 92a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 93a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar if (!migrated) { 94d2658c69a5eb56c5fa7f09bd361974aa2452b320shepshapard if (mConfiguration != null && !mConfiguration.isMigrationRequiredFrom(oldVersion)) { 95d2658c69a5eb56c5fa7f09bd361974aa2452b320shepshapard mDelegate.dropAllTables(db); 96d2658c69a5eb56c5fa7f09bd361974aa2452b320shepshapard mDelegate.createAllTables(db); 97d2658c69a5eb56c5fa7f09bd361974aa2452b320shepshapard } else { 98234f073e2227fcb62a9ed8285c79724de1f0fa92Yigit Boyar throw new IllegalStateException("A migration from " + oldVersion + " to " 99d2658c69a5eb56c5fa7f09bd361974aa2452b320shepshapard + newVersion + " was required but not found. Please provide the " 100d2658c69a5eb56c5fa7f09bd361974aa2452b320shepshapard + "necessary Migration path via " 101d2658c69a5eb56c5fa7f09bd361974aa2452b320shepshapard + "RoomDatabase.Builder.addMigration(Migration ...) or allow for " 102d2658c69a5eb56c5fa7f09bd361974aa2452b320shepshapard + "destructive migrations via one of the " 103d2658c69a5eb56c5fa7f09bd361974aa2452b320shepshapard + "RoomDatabase.Builder.fallbackToDestructiveMigration* methods."); 104234f073e2227fcb62a9ed8285c79724de1f0fa92Yigit Boyar } 105a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 106a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 107a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 108a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar @Override 109a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { 110a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar onUpgrade(db, oldVersion, newVersion); 111a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 112a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 113a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar @Override 114a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar public void onOpen(SupportSQLiteDatabase db) { 115a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar super.onOpen(db); 1167ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki checkIdentity(db); 117a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar mDelegate.onOpen(db); 118a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar // there might be too many configurations etc, just clear it. 119a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar mConfiguration = null; 120a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 121a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 1227ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki private void checkIdentity(SupportSQLiteDatabase db) { 1230bf1c211603a37b9cbc2431999d58a8ea085ff5aSergey Vasilinets String identityHash = null; 1247ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki if (hasRoomMasterTable(db)) { 1257ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki Cursor cursor = db.query(new SimpleSQLiteQuery(RoomMasterTable.READ_QUERY)); 1267ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki //noinspection TryFinallyCanBeTryWithResources 1272eb8c31eb44ee4bc4138136b8866847b6e49e5b2Yuichi Araki try { 1287ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki if (cursor.moveToFirst()) { 1297ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki identityHash = cursor.getString(0); 1307ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki } 1312eb8c31eb44ee4bc4138136b8866847b6e49e5b2Yuichi Araki } finally { 1327ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki cursor.close(); 133a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 134a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 135e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar if (!mIdentityHash.equals(identityHash) && !mLegacyHash.equals(identityHash)) { 136a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar throw new IllegalStateException("Room cannot verify the data integrity. Looks like" 137a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar + " you've changed schema but forgot to update the version number. You can" 138a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar + " simply fix this by increasing the version number."); 139a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 140a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 141a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 142a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar private void updateIdentity(SupportSQLiteDatabase db) { 143a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar createMasterTableIfNotExists(db); 144a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar db.execSQL(RoomMasterTable.createInsertQuery(mIdentityHash)); 145a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 146a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 147a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar private void createMasterTableIfNotExists(SupportSQLiteDatabase db) { 148a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar db.execSQL(RoomMasterTable.CREATE_QUERY); 149a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 150a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 1517ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki private static boolean hasRoomMasterTable(SupportSQLiteDatabase db) { 1527ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki Cursor cursor = db.query("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name='" 1537ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki + RoomMasterTable.TABLE_NAME + "'"); 1547ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki //noinspection TryFinallyCanBeTryWithResources 1557ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki try { 1567ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki return cursor.moveToFirst() && cursor.getInt(0) != 0; 1577ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki } finally { 1587ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki cursor.close(); 1597ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki } 1607ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki } 1617ce96deb4511a8106d475e7cf8dfa13d2b05a3b6Yuichi Araki 162a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar /** 163a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * @hide 164a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar */ 165a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 166a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar public abstract static class Delegate { 1672b4a201ee53be9d5e3995e62c76f83c07a1ddfddYigit Boyar public final int version; 1682b4a201ee53be9d5e3995e62c76f83c07a1ddfddYigit Boyar 1692b4a201ee53be9d5e3995e62c76f83c07a1ddfddYigit Boyar public Delegate(int version) { 1702b4a201ee53be9d5e3995e62c76f83c07a1ddfddYigit Boyar this.version = version; 1712b4a201ee53be9d5e3995e62c76f83c07a1ddfddYigit Boyar } 1722b4a201ee53be9d5e3995e62c76f83c07a1ddfddYigit Boyar 173a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar protected abstract void dropAllTables(SupportSQLiteDatabase database); 174a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 175a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar protected abstract void createAllTables(SupportSQLiteDatabase database); 176a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 177a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar protected abstract void onOpen(SupportSQLiteDatabase database); 178a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 179cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki protected abstract void onCreate(SupportSQLiteDatabase database); 180cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki 181a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar /** 182a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * Called after a migration run to validate database integrity. 183a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * 184a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar * @param db The SQLite database. 185a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar */ 1863a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar protected abstract void validateMigration(SupportSQLiteDatabase db); 187a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 188a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 189a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar} 190