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;
        }
Example #4
0
		/// <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);
		}
Example #5
0
		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);
     }
 }
Example #8
0
 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);
     }
 }