private RevisionList GetAllRevisionsOfDocumentID(string docId, long docNumericID, bool onlyCurrent) { var sql = onlyCurrent ? "SELECT sequence, revid, deleted FROM revs " + "WHERE doc_id=? AND current ORDER BY sequence DESC" : "SELECT sequence, revid, deleted FROM revs " + "WHERE doc_id=? ORDER BY sequence DESC"; var args = new [] { Convert.ToString (docNumericID) }; var cursor = StorageEngine.RawQuery(sql, args); RevisionList result; try { cursor.MoveToNext(); result = new RevisionList(); while (!cursor.IsAfterLast()) { var rev = new RevisionInternal(docId, cursor.GetString(1), (cursor.GetInt(2) > 0), this); rev.SetSequence(cursor.GetLong(0)); result.AddItem(rev); cursor.MoveToNext(); } } catch (SQLException e) { Log.E(Tag, "Error getting all revisions of document", e); return null; } finally { if (cursor != null) { cursor.Close(); } } return result; }
public void TestRevTree() { var rev = new RevisionInternal("MyDocId", "4-abcd", false); var revProperties = new Dictionary<string, object>(); revProperties.Put("_id", rev.GetDocId()); revProperties.Put("_rev", rev.GetRevId()); revProperties["message"] = "hi"; rev.SetProperties(revProperties); var revHistory = new List<string>(); revHistory.AddItem(rev.GetRevId()); revHistory.AddItem("3-abcd"); revHistory.AddItem("2-abcd"); revHistory.AddItem("1-abcd"); database.ForceInsert(rev, revHistory, null); Assert.AreEqual(1, database.DocumentCount); VerifyHistory(database, rev, revHistory); var conflict = new RevisionInternal("MyDocId", "5-abcd", false); var conflictProperties = new Dictionary<string, object>(); conflictProperties.Put("_id", conflict.GetDocId()); conflictProperties.Put("_rev", conflict.GetRevId()); conflictProperties["message"] = "yo"; conflict.SetProperties(conflictProperties); var conflictHistory = new List<string>(); conflictHistory.AddItem(conflict.GetRevId()); conflictHistory.AddItem("4-bcde"); conflictHistory.AddItem("3-bcde"); conflictHistory.AddItem("2-abcd"); conflictHistory.AddItem("1-abcd"); database.ForceInsert(conflict, conflictHistory, null); Assert.AreEqual(1, database.DocumentCount); VerifyHistory(database, conflict, conflictHistory); // Add an unrelated document: var other = new RevisionInternal("AnotherDocID", "1-cdef", false); var otherProperties = new Dictionary<string, object>(); otherProperties["language"] = "jp"; other.SetProperties(otherProperties); var otherHistory = new List<string>(); otherHistory.AddItem(other.GetRevId()); database.ForceInsert(other, otherHistory, null); // Fetch one of those phantom revisions with no body: var rev2 = database.GetDocument(rev.GetDocId(), "2-abcd", true); Assert.IsNull(rev2); // Make sure no duplicate rows were inserted for the common revisions: Assert.IsTrue(database.LastSequenceNumber <= 8); // Make sure the revision with the higher revID wins the conflict: var current = database.GetDocument(rev.GetDocId(), null, true); Assert.AreEqual(conflict, current); // Get the _changes feed and verify only the winner is in it: var options = new ChangesOptions(); var changes = database.ChangesSince(0, options, null, null); var expectedChanges = new RevisionList(); expectedChanges.AddItem(conflict); expectedChanges.AddItem(other); Assert.AreEqual(expectedChanges, changes); options.IncludeConflicts = true; changes = database.ChangesSince(0, options, null, null); expectedChanges = new RevisionList(); expectedChanges.AddItem(rev); expectedChanges.AddItem(conflict); expectedChanges.AddItem(other); var expectedChangesAlt = new RevisionList(); expectedChangesAlt.AddItem(conflict); expectedChangesAlt.AddItem(rev); expectedChangesAlt.AddItem(other); Assert.IsTrue(expectedChanges.SequenceEqual(changes) || expectedChangesAlt.SequenceEqual(changes)); }
internal RevisionList ChangesSince(long lastSeq, ChangesOptions options, FilterDelegate filter) { // http://wiki.apache.org/couchdb/HTTP_database_API#Changes if (options == null) { options = new ChangesOptions(); } var includeDocs = options.IsIncludeDocs() || (filter != null); var additionalSelectColumns = string.Empty; if (includeDocs) { additionalSelectColumns = ", json"; } var sql = "SELECT sequence, revs.doc_id, docid, revid, deleted" + additionalSelectColumns + " FROM revs, docs " + "WHERE sequence > ? AND current=1 " + "AND revs.doc_id = docs.doc_id " + "ORDER BY revs.doc_id, revid DESC"; var args = lastSeq; Cursor cursor = null; RevisionList changes = null; try { cursor = StorageEngine.RawQuery(sql, CommandBehavior.SequentialAccess, args); cursor.MoveToNext(); changes = new RevisionList(); long lastDocId = 0; while (!cursor.IsAfterLast()) { if (!options.IsIncludeConflicts()) { // Only count the first rev for a given doc (the rest will be losing conflicts): var docNumericId = cursor.GetLong(1); if (docNumericId == lastDocId) { cursor.MoveToNext(); continue; } lastDocId = docNumericId; } var sequence = cursor.GetLong(0); var rev = new RevisionInternal(cursor.GetString(2), cursor.GetString(3), (cursor.GetInt(4) > 0), this); rev.SetSequence(sequence); if (includeDocs) { ExpandStoredJSONIntoRevisionWithAttachments(cursor.GetBlob(5), rev, options.GetContentOptions()); } IDictionary<string, object> paramsFixMe = null; // TODO: these should not be null if (RunFilter(filter, paramsFixMe, rev)) { changes.AddItem(rev); } cursor.MoveToNext(); } } catch (SQLException e) { Log.E(Tag, "Error looking for changes", e); } finally { if (cursor != null) { cursor.Close(); } } if (options.IsSortBySequence()) { changes.SortBySequence(); } changes.Limit(options.GetLimit()); return changes; }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> public virtual void TestRevTree() { RevisionInternal rev = new RevisionInternal("MyDocId", "4-foxy", false, database); IDictionary<string, object> revProperties = new Dictionary<string, object>(); revProperties.Put("_id", rev.GetDocId()); revProperties.Put("_rev", rev.GetRevId()); revProperties.Put("message", "hi"); rev.SetProperties(revProperties); IList<string> revHistory = new AList<string>(); revHistory.AddItem(rev.GetRevId()); revHistory.AddItem("3-thrice"); revHistory.AddItem("2-too"); revHistory.AddItem("1-won"); database.ForceInsert(rev, revHistory, null); NUnit.Framework.Assert.AreEqual(1, database.GetDocumentCount()); VerifyHistory(database, rev, revHistory); RevisionInternal conflict = new RevisionInternal("MyDocId", "5-epsilon", false, database ); IDictionary<string, object> conflictProperties = new Dictionary<string, object>(); conflictProperties.Put("_id", conflict.GetDocId()); conflictProperties.Put("_rev", conflict.GetRevId()); conflictProperties.Put("message", "yo"); conflict.SetProperties(conflictProperties); IList<string> conflictHistory = new AList<string>(); conflictHistory.AddItem(conflict.GetRevId()); conflictHistory.AddItem("4-delta"); conflictHistory.AddItem("3-gamma"); conflictHistory.AddItem("2-too"); conflictHistory.AddItem("1-won"); IList wasInConflict = new ArrayList(); Database.ChangeListener listener = new _ChangeListener_84(wasInConflict); database.AddChangeListener(listener); database.ForceInsert(conflict, conflictHistory, null); NUnit.Framework.Assert.IsTrue(wasInConflict.Count > 0); database.RemoveChangeListener(listener); NUnit.Framework.Assert.AreEqual(1, database.GetDocumentCount()); VerifyHistory(database, conflict, conflictHistory); // Add an unrelated document: RevisionInternal other = new RevisionInternal("AnotherDocID", "1-ichi", false, database ); IDictionary<string, object> otherProperties = new Dictionary<string, object>(); otherProperties.Put("language", "jp"); other.SetProperties(otherProperties); IList<string> otherHistory = new AList<string>(); otherHistory.AddItem(other.GetRevId()); database.ForceInsert(other, otherHistory, null); // Fetch one of those phantom revisions with no body: RevisionInternal rev2 = database.GetDocumentWithIDAndRev(rev.GetDocId(), "2-too", EnumSet.NoneOf<Database.TDContentOptions>()); NUnit.Framework.Assert.AreEqual(rev.GetDocId(), rev2.GetDocId()); NUnit.Framework.Assert.AreEqual("2-too", rev2.GetRevId()); //Assert.assertNull(rev2.getContent()); // Make sure no duplicate rows were inserted for the common revisions: NUnit.Framework.Assert.AreEqual(8, database.GetLastSequenceNumber()); // Make sure the revision with the higher revID wins the conflict: RevisionInternal current = database.GetDocumentWithIDAndRev(rev.GetDocId(), null, EnumSet.NoneOf<Database.TDContentOptions>()); NUnit.Framework.Assert.AreEqual(conflict, current); // Get the _changes feed and verify only the winner is in it: ChangesOptions options = new ChangesOptions(); RevisionList changes = database.ChangesSince(0, options, null); RevisionList expectedChanges = new RevisionList(); expectedChanges.AddItem(conflict); expectedChanges.AddItem(other); NUnit.Framework.Assert.AreEqual(changes, expectedChanges); options.SetIncludeConflicts(true); changes = database.ChangesSince(0, options, null); expectedChanges = new RevisionList(); expectedChanges.AddItem(rev); expectedChanges.AddItem(conflict); expectedChanges.AddItem(other); NUnit.Framework.Assert.AreEqual(changes, expectedChanges); }
public RevisionList GetAllRevisionsOfDocumentID(string docId, long docNumericID, bool onlyCurrent) { string sql = null; if (onlyCurrent) { sql = "SELECT sequence, revid, deleted FROM revs " + "WHERE doc_id=? AND current ORDER BY sequence DESC"; } else { sql = "SELECT sequence, revid, deleted FROM revs " + "WHERE doc_id=? ORDER BY sequence DESC"; } string[] args = new string[] { System.Convert.ToString(docNumericID) }; Cursor cursor = null; cursor = database.RawQuery(sql, args); RevisionList result; try { cursor.MoveToNext(); result = new RevisionList(); while (!cursor.IsAfterLast()) { RevisionInternal rev = new RevisionInternal(docId, cursor.GetString(1), (cursor.GetInt (2) > 0), this); rev.SetSequence(cursor.GetLong(0)); result.AddItem(rev); cursor.MoveToNext(); } } catch (SQLException e) { Log.E(Database.Tag, "Error getting all revisions of document", e); return null; } finally { if (cursor != null) { cursor.Close(); } } return result; }
public void TestRevTree() { var rev = new RevisionInternal("MyDocId", "4-foxy", false, database); var revProperties = new Dictionary<string, object>(); revProperties.Put("_id", rev.GetDocId()); revProperties.Put("_rev", rev.GetRevId()); revProperties["message"] = "hi"; rev.SetProperties(revProperties); var revHistory = new AList<string>(); revHistory.AddItem(rev.GetRevId()); revHistory.AddItem("3-thrice"); revHistory.AddItem("2-too"); revHistory.AddItem("1-won"); database.ForceInsert(rev, revHistory, null); Assert.AreEqual(1, database.DocumentCount); VerifyHistory(database, rev, revHistory); var conflict = new RevisionInternal("MyDocId", "5-epsilon", false, database); var conflictProperties = new Dictionary<string, object>(); conflictProperties.Put("_id", conflict.GetDocId()); conflictProperties.Put("_rev", conflict.GetRevId()); conflictProperties["message"] = "yo"; conflict.SetProperties(conflictProperties); var conflictHistory = new AList<string>(); conflictHistory.AddItem(conflict.GetRevId()); conflictHistory.AddItem("4-delta"); conflictHistory.AddItem("3-gamma"); conflictHistory.AddItem("2-too"); conflictHistory.AddItem("1-won"); database.ForceInsert(conflict, conflictHistory, null); Assert.AreEqual(1, database.DocumentCount); VerifyHistory(database, conflict, conflictHistory); // Add an unrelated document: var other = new RevisionInternal("AnotherDocID", "1-ichi", false, database); var otherProperties = new Dictionary<string, object>(); otherProperties["language"] = "jp"; other.SetProperties(otherProperties); var otherHistory = new AList<string>(); otherHistory.AddItem(other.GetRevId()); database.ForceInsert(other, otherHistory, null); // Fetch one of those phantom revisions with no body: var rev2 = database.GetDocumentWithIDAndRev(rev.GetDocId(), "2-too", DocumentContentOptions.None); Assert.AreEqual(rev.GetDocId(), rev2.GetDocId()); Assert.AreEqual("2-too", rev2.GetRevId()); // Make sure no duplicate rows were inserted for the common revisions: Assert.AreEqual(8, database.GetLastSequenceNumber()); // Make sure the revision with the higher revID wins the conflict: var current = database.GetDocumentWithIDAndRev(rev.GetDocId(), null, DocumentContentOptions.None); Assert.AreEqual(conflict, current); // Get the _changes feed and verify only the winner is in it: var options = new ChangesOptions(); var changes = database.ChangesSince(0, options, null); var expectedChanges = new RevisionList(); expectedChanges.AddItem(conflict); expectedChanges.AddItem(other); Assert.AreEqual(changes, expectedChanges); options.SetIncludeConflicts(true); changes = database.ChangesSince(0, options, null); expectedChanges = new RevisionList(); expectedChanges.AddItem(rev); expectedChanges.AddItem(conflict); expectedChanges.AddItem(other); Assert.AreEqual(changes, expectedChanges); }
public void OnCompletion(object response, Exception e) { try { Log.V(Log.TagSync, "%s: got /_revs_diff response"); IDictionary<string, object> results = (IDictionary<string, object>)response; if (e != null) { this._enclosing.SetError(e); this._enclosing.RevisionFailed(); } else { if (results.Count != 0) { // Go through the list of local changes again, selecting the ones the destination server // said were missing and mapping them to a JSON dictionary in the form _bulk_docs wants: IList<object> docsToSend = new AList<object>(); RevisionList revsToSend = new RevisionList(); foreach (RevisionInternal rev in changes) { // Is this revision in the server's 'missing' list? IDictionary<string, object> properties = null; IDictionary<string, object> revResults = (IDictionary<string, object>)results.Get (rev.GetDocId()); if (revResults == null) { continue; } IList<string> revs = (IList<string>)revResults.Get("missing"); if (revs == null || !revs.Contains(rev.GetRevId())) { this._enclosing.RemovePending(rev); continue; } // Get the revision's properties: EnumSet<Database.TDContentOptions> contentOptions = EnumSet.Of(Database.TDContentOptions .TDIncludeAttachments); if (!this._enclosing.dontSendMultipart && this._enclosing.revisionBodyTransformationBlock == null) { contentOptions.AddItem(Database.TDContentOptions.TDBigAttachmentsFollow); } RevisionInternal loadedRev; try { loadedRev = this._enclosing.db.LoadRevisionBody(rev, contentOptions); properties = new Dictionary<string, object>(rev.GetProperties()); } catch (CouchbaseLiteException) { Log.W(Log.TagSync, "%s Couldn't get local contents of %s", rev, this._enclosing); this._enclosing.RevisionFailed(); continue; } RevisionInternal populatedRev = this._enclosing.TransformRevision(loadedRev); IList<string> possibleAncestors = (IList<string>)revResults.Get("possible_ancestors" ); properties = new Dictionary<string, object>(populatedRev.GetProperties()); IDictionary<string, object> revisions = this._enclosing.db.GetRevisionHistoryDictStartingFromAnyAncestor (populatedRev, possibleAncestors); properties.Put("_revisions", revisions); populatedRev.SetProperties(properties); // Strip any attachments already known to the target db: if (properties.ContainsKey("_attachments")) { // Look for the latest common ancestor and stub out older attachments: int minRevPos = Couchbase.Lite.Replicator.Pusher.FindCommonAncestor(populatedRev, possibleAncestors); Database.StubOutAttachmentsInRevBeforeRevPos(populatedRev, minRevPos + 1, false); properties = populatedRev.GetProperties(); if (!this._enclosing.dontSendMultipart && this._enclosing.UploadMultipartRevision (populatedRev)) { continue; } } if (properties == null || !properties.ContainsKey("_id")) { throw new InvalidOperationException("properties must contain a document _id"); } revsToSend.AddItem(rev); docsToSend.AddItem(properties); } //TODO: port this code from iOS // Post the revisions to the destination: this._enclosing.UploadBulkDocs(docsToSend, revsToSend); } else { // None of the revisions are new to the remote foreach (RevisionInternal revisionInternal in changes) { this._enclosing.RemovePending(revisionInternal); } } } } finally { Log.V(Log.TagSync, "%s | %s: processInbox.sendAsyncRequest() calling asyncTaskFinished()" , this, Sharpen.Thread.CurrentThread()); this._enclosing.AsyncTaskFinished(1); } }
public void OnCompletion(object response, Exception e) { try { Log.V(Log.TagSync, "%s: got /_revs_diff response"); IDictionary <string, object> results = (IDictionary <string, object>)response; if (e != null) { this._enclosing.SetError(e); this._enclosing.RevisionFailed(); } else { if (results.Count != 0) { // Go through the list of local changes again, selecting the ones the destination server // said were missing and mapping them to a JSON dictionary in the form _bulk_docs wants: IList <object> docsToSend = new AList <object>(); RevisionList revsToSend = new RevisionList(); foreach (RevisionInternal rev in changes) { // Is this revision in the server's 'missing' list? IDictionary <string, object> properties = null; IDictionary <string, object> revResults = (IDictionary <string, object>)results.Get (rev.GetDocId()); if (revResults == null) { continue; } IList <string> revs = (IList <string>)revResults.Get("missing"); if (revs == null || !revs.Contains(rev.GetRevId())) { this._enclosing.RemovePending(rev); continue; } // Get the revision's properties: EnumSet <Database.TDContentOptions> contentOptions = EnumSet.Of(Database.TDContentOptions .TDIncludeAttachments); if (!this._enclosing.dontSendMultipart && this._enclosing.revisionBodyTransformationBlock == null) { contentOptions.AddItem(Database.TDContentOptions.TDBigAttachmentsFollow); } RevisionInternal loadedRev; try { loadedRev = this._enclosing.db.LoadRevisionBody(rev, contentOptions); properties = new Dictionary <string, object>(rev.GetProperties()); } catch (CouchbaseLiteException) { Log.W(Log.TagSync, "%s Couldn't get local contents of %s", rev, this._enclosing); this._enclosing.RevisionFailed(); continue; } RevisionInternal populatedRev = this._enclosing.TransformRevision(loadedRev); IList <string> possibleAncestors = (IList <string>)revResults.Get("possible_ancestors" ); properties = new Dictionary <string, object>(populatedRev.GetProperties()); IDictionary <string, object> revisions = this._enclosing.db.GetRevisionHistoryDictStartingFromAnyAncestor (populatedRev, possibleAncestors); properties.Put("_revisions", revisions); populatedRev.SetProperties(properties); // Strip any attachments already known to the target db: if (properties.ContainsKey("_attachments")) { // Look for the latest common ancestor and stub out older attachments: int minRevPos = Couchbase.Lite.Replicator.Pusher.FindCommonAncestor(populatedRev, possibleAncestors); Database.StubOutAttachmentsInRevBeforeRevPos(populatedRev, minRevPos + 1, false); properties = populatedRev.GetProperties(); if (!this._enclosing.dontSendMultipart && this._enclosing.UploadMultipartRevision (populatedRev)) { continue; } } if (properties == null || !properties.ContainsKey("_id")) { throw new InvalidOperationException("properties must contain a document _id"); } revsToSend.AddItem(rev); docsToSend.AddItem(properties); } //TODO: port this code from iOS // Post the revisions to the destination: this._enclosing.UploadBulkDocs(docsToSend, revsToSend); } else { // None of the revisions are new to the remote foreach (RevisionInternal revisionInternal in changes) { this._enclosing.RemovePending(revisionInternal); } } } } finally { Log.V(Log.TagSync, "%s | %s: processInbox.sendAsyncRequest() calling asyncTaskFinished()" , this, Sharpen.Thread.CurrentThread()); this._enclosing.AsyncTaskFinished(1); } }