@@ -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,88 @@ 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 ,
825+ "Upgrade failed: Creating new db failed: %s" , tempName );
815826 return false ;
816827 }
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 ();
828+
829+ // upgradeDatabase() is called only if dbPath (old db version) exists. So presence of
830+ // temporary db indicates that previous upgrade crashed midway.
831+ if (tmpDB .exists ()) {
832+ Log .v (Log .TAG_DATABASE , "Previous upgrade probably crashed midway. dbPath: " + dbPath );
833+
834+ // rollback from temporary database to old database
835+ // NOTE: Deleting temporary db is not enough because `DatabaseUpgrade.importData()`
836+ // move attachment files instead of copy files.
837+ DatabaseUpgrade upgrader = new DatabaseUpgrade (this , tmpDB , dbPath );
838+ upgrader .backOut ();
839+
840+ // temporary db is deleted by previous operation, reopen the temporary db.
841+ tmpDB = getDatabase (tempName , false );
842+ if (tmpDB == null ) {
843+ Log .w (Log .TAG_DATABASE , "Upgrade failed: Creating new db failed: %s" , tempName );
822844 return false ;
823845 }
824846 }
825- if (close )
826- db .close ();
847+
848+ if (tmpDB .exists ()) {
849+ // the temporary db should not exist. Just double check
850+ Log .w (Log .TAG_DATABASE ,
851+ "Upgrade failed: Failed to delete already existing db: %s" , tempName );
852+ return false ;
853+ }
854+
855+ // Upgrade the old database into the new one:
856+ DatabaseUpgrade upgrader = new DatabaseUpgrade (this , tmpDB , dbPath );
857+ if (!upgrader .importData ()) {
858+ upgrader .backOut ();
859+ return false ;
860+ }
861+
862+ // close temporary database
863+ tmpDB .close ();
864+
865+ // rename temporary database name to new name
866+ File tmpPath = new File (pathForDatabaseNamed (tempName ));
867+ File newPath = new File (pathForDatabaseNamed (name ));
868+ if (!tmpPath .renameTo (newPath )) {
869+ Log .w (Log .TAG_DATABASE ,
870+ "Upgrade failed: Failed to rename db folder from temporary name: %s -> %s" ,
871+ tmpPath , newPath );
872+ upgrader = new DatabaseUpgrade (this , getDatabase (tempName , false ), dbPath );
873+ upgrader .backOut ();
874+ return false ;
875+ }
876+
877+ // reopen db with name
878+ db = getDatabase (name , false );
879+ if (!db .exists ()) {
880+ Log .w (Log .TAG_DATABASE ,
881+ "Upgrade failed: Failed to open the database after migrate: %s" , name );
882+ return false ;
883+ }
827884 }
828885
886+ // close db if necessary
887+ if (close )
888+ db .close ();
889+
829890 // Remove old database file and its SQLite side files:
830891 moveSQLiteDbFiles (dbPath , null );
831892
0 commit comments