5353import java .util .EnumSet ;
5454import java .util .HashMap ;
5555import java .util .HashSet ;
56+ import java .util .Iterator ;
5657import java .util .List ;
5758import java .util .Map ;
5859import java .util .Set ;
@@ -997,7 +998,7 @@ public synchronized boolean open() {
997998 int dbVersion = database .getVersion ();
998999
9991000 // Incompatible version changes increment the hundreds' place:
1000- if (dbVersion >= 100 ) {
1001+ if (dbVersion >= 200 ) {
10011002 Log .e (Database .TAG , "Database: Database version (%d) is newer than I know how to work with" , dbVersion );
10021003 database .close ();
10031004 return false ;
@@ -1210,6 +1211,110 @@ public synchronized boolean open() {
12101211 dbVersion = 18 ;
12111212 }
12121213
1214+ // NOTE: Following lines of code are for compatibility with Couchbase Lite iOS v1.1.0 database format.
1215+ // https://github.com/couchbase/couchbase-lite-java-core/issues/596
1216+ // CBL iOS v1.1.0 => 101
1217+ // 1. Creates attachments table if it does not exist.
1218+ // 2. Iterate revs table to populate attachments table.
1219+ if (dbVersion >= 101 ) {
1220+ // Check if attachments table exists. If not, create the table, and iterate revs
1221+ // to populate attachment table
1222+ boolean existsAttachments = false ;
1223+ Cursor cursor = null ;
1224+ try {
1225+ cursor = database .rawQuery ("SELECT name FROM sqlite_master WHERE type='table' AND name='attachments'" , null );
1226+ if (cursor .moveToNext ()) {
1227+ existsAttachments = true ;
1228+ }
1229+ } catch (SQLException e ) {
1230+ Log .e (Database .TAG , "Failed to check if attachments table exists" , e );
1231+ if (cursor != null ) {
1232+ cursor .close ();
1233+ }
1234+ database .close ();
1235+ return false ;
1236+ } finally {
1237+ if (cursor != null ) {
1238+ cursor .close ();
1239+ }
1240+ }
1241+
1242+ if (!existsAttachments ) {
1243+ // 1. create attachments table
1244+ String upgradeSql = "CREATE TABLE attachments ( " +
1245+ "sequence INTEGER NOT NULL REFERENCES revs(sequence) ON DELETE CASCADE, " +
1246+ "filename TEXT NOT NULL, " +
1247+ "key BLOB NOT NULL, " +
1248+ "type TEXT, " +
1249+ "length INTEGER NOT NULL, " +
1250+ "revpos INTEGER DEFAULT 0); " +
1251+ "CREATE INDEX attachments_by_sequence on attachments(sequence, filename); " +
1252+ "CREATE INDEX attachments_sequence ON attachments(sequence); " +
1253+ "PRAGMA user_version = 20" ;
1254+ if (!initialize (upgradeSql )) {
1255+ database .close ();
1256+ return false ;
1257+ }
1258+
1259+ // 2. iterate revs table, and populate attachment table
1260+ String sql = "SELECT sequence, json FROM revs WHERE no_attachments=0" ;
1261+ Cursor cursor2 = null ;
1262+ try {
1263+ cursor2 = database .rawQuery (sql , null );
1264+ while (cursor2 .moveToNext ()) {
1265+ if (!cursor2 .isNull (1 )) {
1266+ long sequence = cursor2 .getLong (0 );
1267+ byte [] json = cursor2 .getBlob (1 );
1268+ try {
1269+ Map <String , Object > docProperties = Manager .getObjectMapper ().readValue (json , Map .class );
1270+ Map <String , Object > attachments = (Map <String , Object >) docProperties .get ("_attachments" );
1271+ Iterator <String > itr = attachments .keySet ().iterator ();
1272+ while (itr .hasNext ()) {
1273+ String name = itr .next ();
1274+ Map <String , Object > attachment = (Map <String , Object >) attachments .get (name );
1275+ String contentType = (String ) attachment .get ("content_type" );
1276+ int revPos = (Integer ) attachment .get ("revpos" );
1277+ int length = (Integer ) attachment .get ("length" );
1278+ String digest = (String ) attachment .get ("digest" );
1279+ BlobKey key = new BlobKey (digest );
1280+ try {
1281+ insertAttachmentForSequenceWithNameAndType (sequence , name , contentType , revPos , key , length );
1282+ } catch (CouchbaseLiteException e ) {
1283+ Log .e (Log .TAG_DATABASE , "Attachment information inserstion error: " + name + "=" + attachment .toString (), e );
1284+ }
1285+ }
1286+ } catch (Exception e ) {
1287+ Log .e (Log .TAG_DATABASE , "JSON parsing error: " + new String (json ), e );
1288+ }
1289+ }
1290+ }
1291+ } catch (SQLException e ) {
1292+ Log .e (Database .TAG , "Failed to check if attachments table exists" , e );
1293+ if (cursor2 != null ) {
1294+ cursor2 .close ();
1295+ }
1296+ database .close ();
1297+ return false ;
1298+ } finally {
1299+ if (cursor2 != null ) {
1300+ cursor2 .close ();
1301+ }
1302+ }
1303+ }
1304+ dbVersion = 20 ;
1305+ }
1306+
1307+ // NOTE: CBL Android/Java v1.1.0 Set database version 20.
1308+ // 20 is higher than any previous release, but lower than CBL iOS v1.1.0 - 101
1309+ if (dbVersion < 20 ) {
1310+ String upgradeSql = "PRAGMA user_version = 20" ;
1311+ if (!initialize (upgradeSql )) {
1312+ database .close ();
1313+ return false ;
1314+ }
1315+ dbVersion = 20 ;
1316+ }
1317+
12131318 if (isNew ) {
12141319 optimizeSQLIndexes (); // runs ANALYZE query
12151320 }
@@ -2751,7 +2856,7 @@ public void insertAttachmentForSequenceWithNameAndType(long sequence, String nam
27512856 ContentValues args = new ContentValues ();
27522857 args .put ("sequence" , sequence );
27532858 args .put ("filename" , name );
2754- if (key != null ){
2859+ if (key != null ) {
27552860 args .put ("key" , key .getBytes ());
27562861 args .put ("length" , attachments .getSizeOfBlob (key ));
27572862 }
@@ -2763,13 +2868,32 @@ public void insertAttachmentForSequenceWithNameAndType(long sequence, String nam
27632868 Log .e (Database .TAG , msg );
27642869 throw new CouchbaseLiteException (msg , Status .INTERNAL_SERVER_ERROR );
27652870 }
2766-
27672871 } catch (SQLException e ) {
27682872 Log .e (Database .TAG , "Error inserting attachment" , e );
27692873 throw new CouchbaseLiteException (e , Status .INTERNAL_SERVER_ERROR );
27702874 }
27712875 }
27722876
2877+ private void insertAttachmentForSequenceWithNameAndType (long sequence , String name , String contentType , int revpos , BlobKey key , long length ) throws CouchbaseLiteException {
2878+ try {
2879+ ContentValues args = new ContentValues ();
2880+ args .put ("sequence" , sequence );
2881+ args .put ("filename" , name );
2882+ args .put ("key" , key .getBytes ());
2883+ args .put ("length" , length );
2884+ args .put ("type" , contentType );
2885+ args .put ("revpos" , revpos );
2886+ long result = database .insert ("attachments" , null , args );
2887+ if (result == -1 ) {
2888+ String msg = "Insert attachment failed (returned -1)" ;
2889+ Log .e (Database .TAG , msg );
2890+ throw new CouchbaseLiteException (msg , Status .INTERNAL_SERVER_ERROR );
2891+ }
2892+ } catch (SQLException e ) {
2893+ Log .e (Database .TAG , "Error inserting attachment" , e );
2894+ throw new CouchbaseLiteException (e , Status .INTERNAL_SERVER_ERROR );
2895+ }
2896+ }
27732897 /**
27742898 * @exclude
27752899 */
0 commit comments