@@ -795,7 +795,8 @@ public boolean accept(File file, String name) {
795795 String filename = file .getName ();
796796 String name = nameOfDatabaseAtPath (filename );
797797 String oldDbPath = new File (directory , filename ).getAbsolutePath ();
798- upgradeDatabase (name , oldDbPath , true );
798+ if (!upgradeDatabase (name , oldDbPath , true ))
799+ throw new RuntimeException ("Database upgrade failed for: " + name );
799800 }
800801 }
801802
@@ -804,28 +805,81 @@ public boolean accept(File file, String name) {
804805 * - (BOOL) upgradeDatabaseNamed: (NSString*)name
805806 * atPath: (NSString*)dbPath
806807 * error: (NSError**)outError
808+ *
809+ * NOTE: upgradeDatabase() method is called if the old database exists.
807810 */
808811 private boolean upgradeDatabase (String name , String dbPath , boolean close ) {
809- Log .v (Log .TAG_DATABASE , "CouchbaseLite: Upgrading database at %s ..." , dbPath );
810- if (!name .equals ("_replicator" )) {
811- // Create and open new CBLDatabase:
812- Database db = getDatabase (name , false );
813- if (db == null ) {
814- Log .w (Log .TAG_DATABASE , "Upgrade failed: Creating new db failed" );
812+ Log .v (Log .TAG_DATABASE , "CouchbaseLite: Upgrading database (%s) at %s ..." , name , dbPath );
813+
814+ // if db with name already exists, not need to migrate. Simply remove old database
815+ Database db = getDatabase (name , false );
816+ if (!db .exists () && !name .equals ("_replicator" )) {
817+
818+ // temporary database name for new db schema
819+ String tempName = name + ".tmp" ;
820+
821+ // Create and open new CBLDatabase with temporary name:
822+ Database tmpDB = getDatabase (tempName , false );
823+ if (tmpDB == null ) {
824+ Log .w (Log .TAG_DATABASE , "Upgrade failed: Creating new db failed: %s" , tempName );
815825 return false ;
816826 }
817- if (!db .exists ()) {
818- // Upgrade the old database into the new one:
819- DatabaseUpgrade upgrader = new DatabaseUpgrade (this , db , dbPath );
820- if (!upgrader .importData ()) {
821- upgrader .backOut ();
827+
828+ // upgradeDatabase() is called only if dbPath (old db version) exists. So presence of
829+ // temporary db indicates that previous upgrade crashed midway.
830+ if (tmpDB .exists ()) {
831+ Log .v (Log .TAG_DATABASE , "Previous upgrade probably crashed midway. dbPath: " + dbPath );
832+
833+ // rollback from temporary database to old database
834+ // NOTE: Deleting temporary db is not enough because `DatabaseUpgrade.importData()`
835+ // move attachment files instead of copy files.
836+ DatabaseUpgrade upgrader = new DatabaseUpgrade (this , tmpDB , dbPath );
837+ upgrader .backOut ();
838+
839+ // temporary db is deleted by previous operation, reopen the temporary db.
840+ tmpDB = getDatabase (tempName , false );
841+ if (tmpDB == null ) {
842+ Log .w (Log .TAG_DATABASE , "Upgrade failed: Creating new db failed: %s" , tempName );
822843 return false ;
823844 }
824845 }
825- if (close )
826- db .close ();
846+
847+ if (tmpDB .exists ()) {
848+ // the temporary db should not exist. Just double check
849+ Log .w (Log .TAG_DATABASE , "Upgrade failed: Failed to delete already existing db: %s" , tempName );
850+ return false ;
851+ }
852+
853+ // Upgrade the old database into the new one:
854+ DatabaseUpgrade upgrader = new DatabaseUpgrade (this , tmpDB , dbPath );
855+ if (!upgrader .importData ()) {
856+ upgrader .backOut ();
857+ return false ;
858+ }
859+
860+ // close temporary database
861+ tmpDB .close ();
862+
863+ // rename temporary database name to new name
864+ File tmpPath = new File (pathForDatabaseNamed (tempName ));
865+ File newPath = new File (pathForDatabaseNamed (name ));
866+ if (!tmpPath .renameTo (newPath )) {
867+ Log .w (Log .TAG_DATABASE , "Upgrade failed: Failed to rename db folder from temporary name: %s -> %s" , tmpPath , newPath );
868+ return false ;
869+ }
870+
871+ // reopen db with name
872+ db = getDatabase (name , false );
873+ if (!db .exists ()) {
874+ Log .w (Log .TAG_DATABASE , "Upgrade failed: Failed to open the database after migrate: %s" , name );
875+ return false ;
876+ }
827877 }
828878
879+ // close db if necessary
880+ if (close )
881+ db .close ();
882+
829883 // Remove old database file and its SQLite side files:
830884 moveSQLiteDbFiles (dbPath , null );
831885
0 commit comments