public void Import() { // Rename the old database file for migration: var destPath = Path.ChangeExtension(_path, Manager.DatabaseSuffixv1 + "-mgr"); if (!MoveSqliteFiles(_path, destPath)) { Log.W(TAG, "Upgrade failed: Cannot rename the old sqlite files"); MoveSqliteFiles(destPath, _path); throw new CouchbaseLiteException(StatusCode.InternalServerError); } int version = DatabaseUpgraderFactory.SchemaVersion(destPath); if (version < 0) { throw new CouchbaseLiteException("Cannot determine database schema version", StatusCode.CorruptError); } // Open source (SQLite) database: var err = raw.sqlite3_open_v2(destPath, out _sqlite, raw.SQLITE_OPEN_READWRITE, null); if (err > 0) { throw new CouchbaseLiteException(SqliteErrToStatus(err).Code); } raw.sqlite3_create_collation(_sqlite, "JSON", raw.SQLITE_UTF8, CollateRevIDs); sqlite3_stmt stmt = null; PrepareSQL(ref stmt, "SELECT name FROM sqlite_master WHERE type='table' AND name='maps'"); err = raw.sqlite3_step(stmt); if (err == raw.SQLITE_ROW) { sqlite3_stmt stmt2 = null; PrepareSQL(ref stmt2, "SELECT * FROM maps"); while ((err = raw.sqlite3_step(stmt2)) == raw.SQLITE_ROW) { int viewId = raw.sqlite3_column_int(stmt2, 0); sqlite3_stmt stmt3 = null; PrepareSQL(ref stmt3, "CREATE TABLE IF NOT EXISTS maps_" + viewId + " (sequence INTEGER NOT NULL REFERENCES revs(sequence) ON DELETE CASCADE," + "key TEXT NOT NULL COLLATE JSON," + "value TEXT," + "fulltext_id INTEGER, " + "bbox_id INTEGER, " + "geokey BLOB)"); raw.sqlite3_step(stmt3); raw.sqlite3_finalize(stmt3); stmt3 = null; var sequence = raw.sqlite3_column_int64(stmt2, 1); var key = raw.sqlite3_column_text(stmt2, 2); var value = raw.sqlite3_column_text(stmt2, 3); var insertSql = String.Format("INSERT INTO maps_{0} (sequence, key, value) VALUES (?, ?, ?)", viewId); PrepareSQL(ref stmt3, insertSql); raw.sqlite3_bind_int64(stmt3, 0, sequence); raw.sqlite3_bind_text(stmt3, 1, key); raw.sqlite3_bind_text(stmt3, 2, value); raw.sqlite3_step(stmt3); raw.sqlite3_finalize(stmt3); } raw.sqlite3_finalize(stmt2); stmt2 = null; PrepareSQL(ref stmt2, "DROP TABLE maps"); raw.sqlite3_step(stmt2); raw.sqlite3_finalize(stmt2); } raw.sqlite3_finalize(stmt); raw.sqlite3_close(_sqlite); if (err != raw.SQLITE_DONE) { throw new CouchbaseLiteException(SqliteErrToStatus(err).Code); } if (version >= 101) { _db.Delete(); MoveSqliteFiles(destPath, _path); var secondaryUpgrade = new v11_upgrader(_db, _path); secondaryUpgrade.Import(); Directory.Move(Path.Combine(Path.GetDirectoryName(_path), _db.Name + Manager.DatabaseSuffix), _db.DbDirectory); return; } Log.D(TAG, "Upgrading database v1.0 ({0}) to v1.1 at {1} ...", version, _path); err = raw.sqlite3_open_v2(destPath, out _sqlite, raw.SQLITE_OPEN_READONLY, null); if (err > 0) { throw new CouchbaseLiteException(SqliteErrToStatus(err).Code); } raw.sqlite3_create_collation(_sqlite, "REVID", raw.SQLITE_UTF8, CollateRevIDs); // Open destination database: try { _db.Open(); } catch(CouchbaseLiteException) { Log.W(TAG, "Upgrade failed: Couldn't open new db"); throw; } catch(Exception e) { throw new CouchbaseLiteException("Error during upgrade; couldn't open new db", e) { Code = StatusCode.Exception }; } try { MoveAttachmentsDir(); } catch(CouchbaseLiteException) { Log.W(TAG, "Failed to move attachments directory for database at '{0}'", _path); throw; } catch(Exception e) { throw new CouchbaseLiteException(String.Format( "Error moving attachments directory for database at '{0}'", _path), e) { Code = StatusCode.Exception }; } // Upgrade documents: // CREATE TABLE docs (doc_id INTEGER PRIMARY KEY, docid TEXT UNIQUE NOT NULL); sqlite3_stmt docQuery = null; PrepareSQL(ref docQuery, "SELECT doc_id, docid FROM docs"); _db.RunInTransaction(() => { int transactionErr; int count = 0; while(raw.SQLITE_ROW == (transactionErr = raw.sqlite3_step(docQuery))) { long docNumericID = raw.sqlite3_column_int64(docQuery, 0); string docID = raw.sqlite3_column_text(docQuery, 1); try { ImportDoc(docID, docNumericID); } catch(CouchbaseLiteException) { Log.W(TAG, "Failed to import document #{0} ({1})", docNumericID, docID); throw; } catch(Exception e) { throw new CouchbaseLiteException(String.Format("Error importing document #{0} ({1}", docNumericID, docID), e) { Code = StatusCode.Exception }; } if((++count % 1000) == 0) { Log.I(TAG, "Migrated {0} documents", count); } } return transactionErr == raw.SQLITE_DONE; }); raw.sqlite3_finalize(docQuery); try { ImportLocalDocs(); } catch(CouchbaseLiteException) { Log.W(TAG, "Failed to import local docs for database '{0}'", _path); throw; } catch(Exception e) { throw new CouchbaseLiteException(String.Format( "Error importing local docs for database '{0}'", _path), e) { Code = StatusCode.Exception }; } try { ImportInfo(); } catch(CouchbaseLiteException) { Log.W(TAG, "Failed to import info for database '{0}'", _path); throw; } catch(Exception e) { throw new CouchbaseLiteException(String.Format( "Error importing info for database '{0}'", _path), e) { Code = StatusCode.Exception }; } raw.sqlite3_close(_sqlite); _sqlite = null; File.Delete(destPath); File.Delete(destPath + "-wal"); File.Delete(destPath + "-shm"); }
public void Import() { // Rename the old database file for migration: var destPath = Path.ChangeExtension(_path, Manager.DatabaseSuffixv1 + "-mgr"); if (!MoveSqliteFiles(_path, destPath)) { MoveSqliteFiles(destPath, _path); throw Misc.CreateExceptionAndLog(Log.To.Upgrade, StatusCode.InternalServerError, TAG, "Upgrade failed: Cannot rename the old sqlite files"); } int version = DatabaseUpgraderFactory.SchemaVersion(destPath); if (version < 0) { throw Misc.CreateExceptionAndLog(Log.To.Upgrade, StatusCode.CorruptError, TAG, "Cannot determine database schema version"); } // Open source (SQLite) database: var err = raw.sqlite3_open_v2(destPath, out _sqlite, raw.SQLITE_OPEN_READWRITE, null); if (err > 0) { throw Misc.CreateExceptionAndLog(Log.To.Upgrade, SqliteErrToStatus(err).Code, TAG, "SQLite error while opening source database ({0})", err); } raw.sqlite3_create_collation(_sqlite, "JSON", raw.SQLITE_UTF8, CollateRevIDs); sqlite3_stmt stmt = null; PrepareSQL(ref stmt, "SELECT name FROM sqlite_master WHERE type='table' AND name='maps'"); err = raw.sqlite3_step(stmt); if (err == raw.SQLITE_ROW) { sqlite3_stmt stmt2 = null; PrepareSQL(ref stmt2, "SELECT * FROM maps"); while ((err = raw.sqlite3_step(stmt2)) == raw.SQLITE_ROW) { int viewId = raw.sqlite3_column_int(stmt2, 0); sqlite3_stmt stmt3 = null; PrepareSQL(ref stmt3, "CREATE TABLE IF NOT EXISTS maps_" + viewId + " (sequence INTEGER NOT NULL REFERENCES revs(sequence) ON DELETE CASCADE," + "key TEXT NOT NULL COLLATE JSON," + "value TEXT," + "fulltext_id INTEGER, " + "bbox_id INTEGER, " + "geokey BLOB)"); raw.sqlite3_step(stmt3); raw.sqlite3_finalize(stmt3); stmt3 = null; var sequence = raw.sqlite3_column_int64(stmt2, 1); var key = raw.sqlite3_column_text(stmt2, 2); var value = raw.sqlite3_column_text(stmt2, 3); var insertSql = String.Format("INSERT INTO maps_{0} (sequence, key, value) VALUES (?, ?, ?)", viewId); PrepareSQL(ref stmt3, insertSql); raw.sqlite3_bind_int64(stmt3, 0, sequence); raw.sqlite3_bind_text(stmt3, 1, key); raw.sqlite3_bind_text(stmt3, 2, value); raw.sqlite3_step(stmt3); raw.sqlite3_finalize(stmt3); } raw.sqlite3_finalize(stmt2); stmt2 = null; PrepareSQL(ref stmt2, "DROP TABLE maps"); raw.sqlite3_step(stmt2); raw.sqlite3_finalize(stmt2); } raw.sqlite3_finalize(stmt); raw.sqlite3_close(_sqlite); if (err != raw.SQLITE_DONE) { throw Misc.CreateExceptionAndLog(Log.To.Upgrade, SqliteErrToStatus(err).Code, TAG, "SQLite error during upgrade process ({0})", err); } if (version >= 101) { _db.Delete(); MoveSqliteFiles(destPath, _path); var secondaryUpgrade = new v11_upgrader(_db, _path); secondaryUpgrade.Import(); var newPath = Path.Combine(Path.GetDirectoryName(_path), _db.Name + Manager.DatabaseSuffix); if (newPath != _db.DbDirectory) { Directory.Move(Path.Combine(Path.GetDirectoryName(_path), _db.Name + Manager.DatabaseSuffix), _db.DbDirectory); } return; } Log.To.Upgrade.I(TAG, "Upgrading database v1.0 ({0}) to v1.1 at {1} ...", version, _path); err = raw.sqlite3_open_v2(destPath, out _sqlite, raw.SQLITE_OPEN_READONLY, null); if (err > 0) { throw Misc.CreateExceptionAndLog(Log.To.Upgrade, SqliteErrToStatus(err).Code, TAG, "Error opening destination SQLite file ({0})", err); } raw.sqlite3_create_collation(_sqlite, "REVID", raw.SQLITE_UTF8, CollateRevIDs); // Open destination database: try { _db.Open(); } catch(CouchbaseLiteException) { Log.To.Upgrade.E(TAG, "Upgrade failed: Couldn't open new db, rethrowing..."); throw; } catch(Exception e) { throw Misc.CreateExceptionAndLog(Log.To.Upgrade, e, TAG, "Error during upgrade; couldn't open new db"); } try { MoveAttachmentsDir(); } catch(CouchbaseLiteException) { Log.To.Upgrade.E(TAG, "Failed to move attachments directory for database at '{0}', rethrowing...", _path); throw; } catch(Exception e) { throw Misc.CreateExceptionAndLog(Log.To.Upgrade, e, TAG, "Error moving attachments directory for database at '{0}'", _path); } // Upgrade documents: // CREATE TABLE docs (doc_id INTEGER PRIMARY KEY, docid TEXT UNIQUE NOT NULL); sqlite3_stmt docQuery = null; PrepareSQL(ref docQuery, "SELECT doc_id, docid FROM docs"); _db.RunInTransaction(() => { int transactionErr; int count = 0; while(raw.SQLITE_ROW == (transactionErr = raw.sqlite3_step(docQuery))) { long docNumericID = raw.sqlite3_column_int64(docQuery, 0); string docID = raw.sqlite3_column_text(docQuery, 1); try { ImportDoc(docID, docNumericID); } catch(CouchbaseLiteException) { Log.To.Upgrade.E(TAG, "Failed to import document #{0} ({1}), rethrowing", docNumericID, new SecureLogString(docID, LogMessageSensitivity.PotentiallyInsecure)); throw; } catch(Exception e) { throw Misc.CreateExceptionAndLog(Log.To.Upgrade, e, TAG, "Error importing documents"); } if((++count % 1000) == 0) { Log.To.Upgrade.I(TAG, "Migrated {0} documents", count); } } return transactionErr == raw.SQLITE_DONE; }); raw.sqlite3_finalize(docQuery); try { ImportLocalDocs(); } catch(CouchbaseLiteException) { Log.To.Upgrade.E(TAG, "Failed to import local docs for database '{0}', rethrowing...", _path); throw; } catch(Exception e) { throw Misc.CreateExceptionAndLog(Log.To.Upgrade, e, TAG, "Error importing local docs for database '{0}'", _path); } try { ImportInfo(); } catch(CouchbaseLiteException) { Log.To.Upgrade.E(TAG, "Failed to import info for database '{0}', rethrowing...", _path); throw; } catch(Exception e) { throw Misc.CreateExceptionAndLog(Log.To.Upgrade, e, TAG, "Error importing info for database '{0}'", _path); } raw.sqlite3_close(_sqlite); _sqlite = null; File.Delete(destPath); File.Delete(destPath + "-wal"); File.Delete(destPath + "-shm"); }